/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 *
 * Copyright 2016 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef wasm_validate_h
#define wasm_validate_h

#include "mozilla/TypeTraits.h"

#include "wasm/WasmCode.h"
#include "wasm/WasmTypes.h"

namespace js {
namespace wasm {

// This struct captures the bytecode offset of a section's payload (so not
// including the header) and the size of the payload.

struct SectionRange
{
    uint32_t start;
    uint32_t size;

    uint32_t end() const {
        return start + size;
    }
    bool operator==(const SectionRange& rhs) const {
        return start == rhs.start && size == rhs.size;
    }
};

typedef Maybe<SectionRange> MaybeSectionRange;

// CompilerEnvironment holds any values that will be needed to compute
// compilation parameters once the module's feature opt-in sections have been
// parsed.
//
// Subsequent to construction a computeParameters() call will compute the final
// compilation parameters, and the object can then be queried for their values.

struct CompileArgs;
class Decoder;

struct CompilerEnvironment
{
    // The object starts in one of two "initial" states; computeParameters moves
    // it into the "computed" state.
    enum State
    {
        InitialWithArgs,
        InitialWithModeTierDebug,
        Computed
    };

    State state_;
    union {
        // Value if the state_ == InitialWithArgs.
        const CompileArgs* args_;

        // Value in the other two states.
        struct {
            CompileMode      mode_;
            Tier             tier_;
            OptimizedBackend optimizedBackend_;
            DebugEnabled     debug_;
            HasGcTypes       gcTypes_;
        };
    };

  public:
    // Retain a reference to the CompileArgs.  A subsequent computeParameters()
    // will compute all parameters from the CompileArgs and additional values.
    explicit CompilerEnvironment(const CompileArgs& args);

    // Save the provided values for mode, tier, and debug, and the initial value
    // for gcTypes.  A subsequent computeParameters() will compute the final
    // value of gcTypes.
    CompilerEnvironment(CompileMode mode,
                        Tier tier,
                        OptimizedBackend optimizedBackend,
                        DebugEnabled debugEnabled,
                        HasGcTypes gcTypesConfigured);

    // Compute any remaining compilation parameters.
    void computeParameters(Decoder& d, HasGcTypes gcFeatureOptIn);

    // Compute any remaining compilation parameters.  Only use this method if
    // the CompilerEnvironment was created with values for mode, tier, and
    // debug.
    void computeParameters(HasGcTypes gcFeatureOptIn);

    bool isComputed() const {
        return state_ == Computed;
    }
    CompileMode mode() const {
        MOZ_ASSERT(isComputed());
        return mode_;
    }
    Tier tier() const {
        MOZ_ASSERT(isComputed());
        return tier_;
    }
    OptimizedBackend optimizedBackend() const {
        MOZ_ASSERT(isComputed());
        return optimizedBackend_;
    }
    DebugEnabled debug() const {
        MOZ_ASSERT(isComputed());
        return debug_;
    }
    HasGcTypes gcTypes() const {
        MOZ_ASSERT(isComputed());
        return gcTypes_;
    }
};

// ModuleEnvironment contains all the state necessary to process or render
// functions, and all of the state necessary to validate aspects of the
// functions that do not require looking forwards in the bytecode stream.
// The remaining validation state is accumulated in DeferredValidationState
// and is checked at the end of a module's bytecode.
//
// A ModuleEnvironment is created by decoding all the sections before the wasm
// code section and then used immutably during. When compiling a module using a
// ModuleGenerator, the ModuleEnvironment holds state shared between the
// ModuleGenerator thread and background compile threads. All the threads
// are given a read-only view of the ModuleEnvironment, thus preventing race
// conditions.

struct ModuleEnvironment
{
    // Constant parameters for the entire compilation:
    const ModuleKind           kind;
    const Shareable            sharedMemoryEnabled;
    // `gcTypesConfigured` reflects the value of the flags --wasm-gc and
    // javascript.options.wasm_gc.  These flags will disappear eventually, thus
    // allowing the removal of this variable and its replacement everywhere by
    // the value HasGcTypes::True.
    //
    // For now, the value is used to control whether we emit code to suppress GC
    // while wasm activations are on the stack.
    const HasGcTypes           gcTypesConfigured;
    CompilerEnvironment* const compilerEnv;

    // Module fields decoded from the module environment (or initialized while
    // validating an asm.js module) and immutable during compilation:
#ifdef ENABLE_WASM_GC
    // `gcFeatureOptIn` reflects the presence in a module of a GcFeatureOptIn
    // section.  This variable will be removed eventually, allowing it to be
    // replaced everywhere by the value HasGcTypes::True.
    //
    // The flag is used in the value of gcTypesEnabled(), which controls whether
    // ref types and struct types and associated instructions are accepted
    // during validation.
    HasGcTypes                gcFeatureOptIn;
#endif
    MemoryUsage               memoryUsage;
    uint32_t                  minMemoryLength;
    Maybe<uint32_t>           maxMemoryLength;
    uint32_t                  numStructTypes;
    TypeDefVector             types;
    FuncTypeWithIdPtrVector   funcTypes;
    Uint32Vector              funcImportGlobalDataOffsets;
    GlobalDescVector          globals;
    TableDescVector           tables;
    Uint32Vector              asmJSSigToTableIndex;
    ImportVector              imports;
    ExportVector              exports;
    Maybe<uint32_t>           startFuncIndex;
    ElemSegmentVector         elemSegments;
    MaybeSectionRange         codeSection;

    // Fields decoded as part of the wasm module tail:
    DataSegmentEnvVector      dataSegments;
    CustomSectionEnvVector    customSections;
    Maybe<uint32_t>           nameCustomSectionIndex;
    Maybe<Name>               moduleName;
    NameVector                funcNames;

    explicit ModuleEnvironment(HasGcTypes gcTypesConfigured,
                               CompilerEnvironment* compilerEnv,
                               Shareable sharedMemoryEnabled,
                               ModuleKind kind = ModuleKind::Wasm)
      : kind(kind),
        sharedMemoryEnabled(sharedMemoryEnabled),
        gcTypesConfigured(gcTypesConfigured),
        compilerEnv(compilerEnv),
#ifdef ENABLE_WASM_GC
        gcFeatureOptIn(HasGcTypes::False),
#endif
        memoryUsage(MemoryUsage::None),
        minMemoryLength(0),
        numStructTypes(0)
    {}

    Tier tier() const {
        return compilerEnv->tier();
    }
    OptimizedBackend optimizedBackend() const {
        return compilerEnv->optimizedBackend();
    }
    CompileMode mode() const {
        return compilerEnv->mode();
    }
    DebugEnabled debug() const {
        return compilerEnv->debug();
    }
    size_t numTables() const {
        return tables.length();
    }
    size_t numTypes() const {
        return types.length();
    }
    size_t numFuncs() const {
        return funcTypes.length();
    }
    size_t numFuncImports() const {
        return funcImportGlobalDataOffsets.length();
    }
    size_t numFuncDefs() const {
        return funcTypes.length() - funcImportGlobalDataOffsets.length();
    }
    HasGcTypes gcTypesEnabled() const {
        return compilerEnv->gcTypes();
    }
    bool usesMemory() const {
        return memoryUsage != MemoryUsage::None;
    }
    bool usesSharedMemory() const {
        return memoryUsage == MemoryUsage::Shared;
    }
    bool isAsmJS() const {
        return kind == ModuleKind::AsmJS;
    }
    bool debugEnabled() const {
        return compilerEnv->debug() == DebugEnabled::True;
    }
    bool funcIsImport(uint32_t funcIndex) const {
        return funcIndex < funcImportGlobalDataOffsets.length();
    }
};

// The Encoder class appends bytes to the Bytes object it is given during
// construction. The client is responsible for the Bytes's lifetime and must
// keep the Bytes alive as long as the Encoder is used.

class Encoder
{
    Bytes& bytes_;

    template <class T>
    MOZ_MUST_USE bool write(const T& v) {
        return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
    }

    template <typename UInt>
    MOZ_MUST_USE bool writeVarU(UInt i) {
        do {
            uint8_t byte = i & 0x7f;
            i >>= 7;
            if (i != 0) {
                byte |= 0x80;
            }
            if (!bytes_.append(byte)) {
                return false;
            }
        } while (i != 0);
        return true;
    }

    template <typename SInt>
    MOZ_MUST_USE bool writeVarS(SInt i) {
        bool done;
        do {
            uint8_t byte = i & 0x7f;
            i >>= 7;
            done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
            if (!done) {
                byte |= 0x80;
            }
            if (!bytes_.append(byte)) {
                return false;
            }
        } while (!done);
        return true;
    }

    void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
        do {
            uint8_t assertByte = assertBits & 0x7f;
            uint8_t patchByte = patchBits & 0x7f;
            assertBits >>= 7;
            patchBits >>= 7;
            if (assertBits != 0) {
                assertByte |= 0x80;
                patchByte |= 0x80;
            }
            MOZ_ASSERT(assertByte == bytes_[offset]);
            bytes_[offset] = patchByte;
            offset++;
        } while(assertBits != 0);
    }

    void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
        MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
        patchFixedU8(offset, patchBits, assertBits);
    }

    void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
        MOZ_ASSERT(bytes_[offset] == assertBits);
        bytes_[offset] = patchBits;
    }

    uint32_t varU32ByteLength(size_t offset) const {
        size_t start = offset;
        while (bytes_[offset] & 0x80) {
            offset++;
        }
        return offset - start + 1;
    }

  public:
    explicit Encoder(Bytes& bytes)
      : bytes_(bytes)
    {
        MOZ_ASSERT(empty());
    }

    size_t currentOffset() const { return bytes_.length(); }
    bool empty() const { return currentOffset() == 0; }

    // Fixed-size encoding operations simply copy the literal bytes (without
    // attempting to align).

    MOZ_MUST_USE bool writeFixedU7(uint8_t i) {
        MOZ_ASSERT(i <= uint8_t(INT8_MAX));
        return writeFixedU8(i);
    }
    MOZ_MUST_USE bool writeFixedU8(uint8_t i) {
        return write<uint8_t>(i);
    }
    MOZ_MUST_USE bool writeFixedU32(uint32_t i) {
        return write<uint32_t>(i);
    }
    MOZ_MUST_USE bool writeFixedF32(float f) {
        return write<float>(f);
    }
    MOZ_MUST_USE bool writeFixedF64(double d) {
        return write<double>(d);
    }

    // Variable-length encodings that all use LEB128.

    MOZ_MUST_USE bool writeVarU32(uint32_t i) {
        return writeVarU<uint32_t>(i);
    }
    MOZ_MUST_USE bool writeVarS32(int32_t i) {
        return writeVarS<int32_t>(i);
    }
    MOZ_MUST_USE bool writeVarU64(uint64_t i) {
        return writeVarU<uint64_t>(i);
    }
    MOZ_MUST_USE bool writeVarS64(int64_t i) {
        return writeVarS<int64_t>(i);
    }
    MOZ_MUST_USE bool writeValType(ValType type) {
        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
        MOZ_ASSERT(size_t(type.code()) < size_t(TypeCode::Limit));
        if (type.isRef()) {
            return writeFixedU8(uint8_t(TypeCode::Ref)) &&
                   writeVarU32(type.refTypeIndex());
        }
        return writeFixedU8(uint8_t(type.code()));
    }
    MOZ_MUST_USE bool writeBlockType(ExprType type) {
        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
        MOZ_ASSERT(size_t(type.code()) < size_t(TypeCode::Limit));
        if (type.isRef()) {
            return writeFixedU8(uint8_t(ExprType::Ref)) &&
                   writeVarU32(type.refTypeIndex());
        }
        return writeFixedU8(uint8_t(type.code()));
    }
    MOZ_MUST_USE bool writeOp(Op op) {
        static_assert(size_t(Op::Limit) == 256, "fits");
        MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
        return writeFixedU8(uint8_t(op));
    }
    MOZ_MUST_USE bool writeOp(MiscOp op) {
        static_assert(size_t(MiscOp::Limit) <= 256, "fits");
        MOZ_ASSERT(size_t(op) < size_t(MiscOp::Limit));
        return writeFixedU8(uint8_t(Op::MiscPrefix)) &&
               writeFixedU8(uint8_t(op));
    }
    MOZ_MUST_USE bool writeOp(ThreadOp op) {
        static_assert(size_t(ThreadOp::Limit) <= 256, "fits");
        MOZ_ASSERT(size_t(op) < size_t(ThreadOp::Limit));
        return writeFixedU8(uint8_t(Op::ThreadPrefix)) &&
               writeFixedU8(uint8_t(op));
    }
    MOZ_MUST_USE bool writeOp(MozOp op) {
        static_assert(size_t(MozOp::Limit) <= 256, "fits");
        MOZ_ASSERT(size_t(op) < size_t(MozOp::Limit));
        return writeFixedU8(uint8_t(Op::MozPrefix)) &&
               writeFixedU8(uint8_t(op));
    }

    // Fixed-length encodings that allow back-patching.

    MOZ_MUST_USE bool writePatchableFixedU7(size_t* offset) {
        *offset = bytes_.length();
        return writeFixedU8(UINT8_MAX);
    }
    void patchFixedU7(size_t offset, uint8_t patchBits) {
        return patchFixedU7(offset, patchBits, UINT8_MAX);
    }

    // Variable-length encodings that allow back-patching.

    MOZ_MUST_USE bool writePatchableVarU32(size_t* offset) {
        *offset = bytes_.length();
        return writeVarU32(UINT32_MAX);
    }
    void patchVarU32(size_t offset, uint32_t patchBits) {
        return patchVarU32(offset, patchBits, UINT32_MAX);
    }

    // Byte ranges start with an LEB128 length followed by an arbitrary sequence
    // of bytes. When used for strings, bytes are to be interpreted as utf8.

    MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
        return writeVarU32(numBytes) &&
               bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
    }

    // A "section" is a contiguous range of bytes that stores its own size so
    // that it may be trivially skipped without examining the payload. Sections
    // require backpatching since the size of the section is only known at the
    // end while the size's varU32 must be stored at the beginning. Immediately
    // after the section length is the string id of the section.

    MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
        MOZ_ASSERT(uint32_t(id) < 128);
        return writeVarU32(uint32_t(id)) &&
               writePatchableVarU32(offset);
    }
    void finishSection(size_t offset) {
        return patchVarU32(offset, bytes_.length() - offset - varU32ByteLength(offset));
    }
};

// DeferredValidationState holds mutable state shared between threads that
// compile a module.  The state accumulates information needed to complete
// validation at the end of compilation of a module.

struct DeferredValidationState {
    // These three fields keep track of the highest data segment index
    // mentioned in the code section, if any, and the associated section
    // offset, so as to facilitate error message creation.  The use of
    // |haveHighestDataSegIndex| avoids the difficulty of having to
    // special-case one of the |highestDataSegIndex| values to mean "we
    // haven't seen any data segments (yet)."

    bool     haveHighestDataSegIndex;
    uint32_t highestDataSegIndex;
    size_t   highestDataSegIndexOffset;

    DeferredValidationState() {
        init();
    }

    void init() {
        haveHighestDataSegIndex = false;
        highestDataSegIndex = 0;
        highestDataSegIndexOffset = 0;
    }

    // Call here to notify the use of the data segment index with value
    // |segIndex| at module offset |offsetInModule| whilst iterating through
    // the code segment.
    void notifyDataSegmentIndex(uint32_t segIndex, size_t offsetInModule);

    // Call here to perform all final validation actions once the module tail
    // has been processed.  Returns |true| if there are no errors.
    bool performDeferredValidation(const ModuleEnvironment& env,
                                   UniqueChars* error);
};

typedef ExclusiveData<DeferredValidationState> ExclusiveDeferredValidationState;

// The Decoder class decodes the bytes in the range it is given during
// construction. The client is responsible for keeping the byte range alive as
// long as the Decoder is used.

class Decoder
{
    const uint8_t* const beg_;
    const uint8_t* const end_;
    const uint8_t* cur_;
    const size_t offsetInModule_;
    UniqueChars* error_;
    UniqueCharsVector* warnings_;
    bool resilientMode_;

    template <class T>
    MOZ_MUST_USE bool read(T* out) {
        if (bytesRemain() < sizeof(T)) {
            return false;
        }
        memcpy((void*)out, cur_, sizeof(T));
        cur_ += sizeof(T);
        return true;
    }

    template <class T>
    T uncheckedRead() {
        MOZ_ASSERT(bytesRemain() >= sizeof(T));
        T ret;
        memcpy(&ret, cur_, sizeof(T));
        cur_ += sizeof(T);
        return ret;
    }

    template <class T>
    void uncheckedRead(T* ret) {
        MOZ_ASSERT(bytesRemain() >= sizeof(T));
        memcpy(ret, cur_, sizeof(T));
        cur_ += sizeof(T);
    }

    template <typename UInt>
    MOZ_MUST_USE bool readVarU(UInt* out) {
        DebugOnly<const uint8_t*> before = cur_;
        const unsigned numBits = sizeof(UInt) * CHAR_BIT;
        const unsigned remainderBits = numBits % 7;
        const unsigned numBitsInSevens = numBits - remainderBits;
        UInt u = 0;
        uint8_t byte;
        UInt shift = 0;
        do {
            if (!readFixedU8(&byte)) {
                return false;
            }
            if (!(byte & 0x80)) {
                *out = u | UInt(byte) << shift;
                return true;
            }
            u |= UInt(byte & 0x7F) << shift;
            shift += 7;
        } while (shift != numBitsInSevens);
        if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits))) {
            return false;
        }
        *out = u | (UInt(byte) << numBitsInSevens);
        MOZ_ASSERT_IF(sizeof(UInt) == 4, unsigned(cur_ - before) <= MaxVarU32DecodedBytes);
        return true;
    }

    template <typename SInt>
    MOZ_MUST_USE bool readVarS(SInt* out) {
        using UInt = typename mozilla::MakeUnsigned<SInt>::Type;
        const unsigned numBits = sizeof(SInt) * CHAR_BIT;
        const unsigned remainderBits = numBits % 7;
        const unsigned numBitsInSevens = numBits - remainderBits;
        SInt s = 0;
        uint8_t byte;
        unsigned shift = 0;
        do {
            if (!readFixedU8(&byte)) {
                return false;
            }
            s |= SInt(byte & 0x7f) << shift;
            shift += 7;
            if (!(byte & 0x80)) {
                if (byte & 0x40) {
                    s |= UInt(-1) << shift;
                }
                *out = s;
                return true;
            }
        } while (shift < numBitsInSevens);
        if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80)) {
            return false;
        }
        uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
        if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0)) {
            return false;
        }
        *out = s | UInt(byte) << shift;
        return true;
    }

  public:
    Decoder(const uint8_t* begin, const uint8_t* end, size_t offsetInModule, UniqueChars* error,
            UniqueCharsVector* warnings = nullptr, bool resilientMode = false)
      : beg_(begin),
        end_(end),
        cur_(begin),
        offsetInModule_(offsetInModule),
        error_(error),
        warnings_(warnings),
        resilientMode_(resilientMode)
    {
        MOZ_ASSERT(begin <= end);
    }
    explicit Decoder(const Bytes& bytes, size_t offsetInModule = 0, UniqueChars* error = nullptr,
                     UniqueCharsVector* warnings = nullptr)
      : beg_(bytes.begin()),
        end_(bytes.end()),
        cur_(bytes.begin()),
        offsetInModule_(offsetInModule),
        error_(error),
        warnings_(warnings),
        resilientMode_(false)
    {}

    // These convenience functions use currentOffset() as the errorOffset.
    bool fail(const char* msg) { return fail(currentOffset(), msg); }
    bool failf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
    void warnf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);

    // Report an error at the given offset (relative to the whole module).
    bool fail(size_t errorOffset, const char* msg);

    UniqueChars* error() { return error_; }

    void clearError() {
        if (error_) {
            error_->reset();
        }
    }

    bool done() const {
        MOZ_ASSERT(cur_ <= end_);
        return cur_ == end_;
    }
    bool resilientMode() const {
        return resilientMode_;
    }

    size_t bytesRemain() const {
        MOZ_ASSERT(end_ >= cur_);
        return size_t(end_ - cur_);
    }
    // pos must be a value previously returned from currentPosition.
    void rollbackPosition(const uint8_t* pos) {
        cur_ = pos;
    }
    const uint8_t* currentPosition() const {
        return cur_;
    }
    size_t currentOffset() const {
        return offsetInModule_ + (cur_ - beg_);
    }
    const uint8_t* begin() const {
        return beg_;
    }
    const uint8_t* end() const {
        return end_;
    }

    // Fixed-size encoding operations simply copy the literal bytes (without
    // attempting to align).

    MOZ_MUST_USE bool readFixedU8(uint8_t* i) {
        return read<uint8_t>(i);
    }
    MOZ_MUST_USE bool readFixedU32(uint32_t* u) {
        return read<uint32_t>(u);
    }
    MOZ_MUST_USE bool readFixedF32(float* f) {
        return read<float>(f);
    }
    MOZ_MUST_USE bool readFixedF64(double* d) {
        return read<double>(d);
    }

    // Variable-length encodings that all use LEB128.

    MOZ_MUST_USE bool readVarU32(uint32_t* out) {
        return readVarU<uint32_t>(out);
    }
    MOZ_MUST_USE bool readVarS32(int32_t* out) {
        return readVarS<int32_t>(out);
    }
    MOZ_MUST_USE bool readVarU64(uint64_t* out) {
        return readVarU<uint64_t>(out);
    }
    MOZ_MUST_USE bool readVarS64(int64_t* out) {
        return readVarS<int64_t>(out);
    }
    MOZ_MUST_USE bool readValType(uint8_t* code, uint32_t* refTypeIndex) {
        static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
        if (!readFixedU8(code)) {
            return false;
        }
        if (*code == uint8_t(TypeCode::Ref)) {
            if (!readVarU32(refTypeIndex)) {
                return false;
            }
            if (*refTypeIndex > MaxTypes) {
                return false;
            }
        } else {
            *refTypeIndex = NoRefTypeIndex;
        }
        return true;
    }
    MOZ_MUST_USE bool readBlockType(uint8_t* code, uint32_t* refTypeIndex) {
        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
        if (!readFixedU8(code)) {
            return false;
        }
        if (*code == uint8_t(TypeCode::Ref)) {
            if (!readVarU32(refTypeIndex)) {
                return false;
            }
        } else {
            *refTypeIndex = NoRefTypeIndex;
        }
        return true;
    }
    MOZ_MUST_USE bool readOp(OpBytes* op) {
        static_assert(size_t(Op::Limit) == 256, "fits");
        uint8_t u8;
        if (!readFixedU8(&u8)) {
            return false;
        }
        op->b0 = u8;
        if (MOZ_LIKELY(!IsPrefixByte(u8))) {
            return true;
        }
        if (!readFixedU8(&u8)) {
            op->b1 = 0;         // Make it sane
            return false;
        }
        op->b1 = u8;
        return true;
    }

    // See writeBytes comment.

    MOZ_MUST_USE bool readBytes(uint32_t numBytes, const uint8_t** bytes = nullptr) {
        if (bytes) {
            *bytes = cur_;
        }
        if (bytesRemain() < numBytes) {
            return false;
        }
        cur_ += numBytes;
        return true;
    }

    // See "section" description in Encoder.

    MOZ_MUST_USE bool readSectionHeader(uint8_t* id, SectionRange* range);

    MOZ_MUST_USE bool startSection(SectionId id,
                                   ModuleEnvironment* env,
                                   MaybeSectionRange* range,
                                   const char* sectionName);
    MOZ_MUST_USE bool finishSection(const SectionRange& range,
                                    const char* sectionName);

    // Custom sections do not cause validation errors unless the error is in
    // the section header itself.

    MOZ_MUST_USE bool startCustomSection(const char* expected,
                                         size_t expectedLength,
                                         ModuleEnvironment* env,
                                         MaybeSectionRange* range);

    template <size_t NameSizeWith0>
    MOZ_MUST_USE bool startCustomSection(const char (&name)[NameSizeWith0],
                                         ModuleEnvironment* env,
                                         MaybeSectionRange* range)
    {
        MOZ_ASSERT(name[NameSizeWith0 - 1] == '\0');
        return startCustomSection(name, NameSizeWith0 - 1, env, range);
    }

    void finishCustomSection(const char* name, const SectionRange& range);
    void skipAndFinishCustomSection(const SectionRange& range);

    MOZ_MUST_USE bool skipCustomSection(ModuleEnvironment* env);

    // The Name section has its own optional subsections.

    MOZ_MUST_USE bool startNameSubsection(NameType nameType, Maybe<uint32_t>* endOffset);
    MOZ_MUST_USE bool finishNameSubsection(uint32_t endOffset);
    MOZ_MUST_USE bool skipNameSubsection();

    // The infallible "unchecked" decoding functions can be used when we are
    // sure that the bytes are well-formed (by construction or due to previous
    // validation).

    uint8_t uncheckedReadFixedU8() {
        return uncheckedRead<uint8_t>();
    }
    uint32_t uncheckedReadFixedU32() {
        return uncheckedRead<uint32_t>();
    }
    void uncheckedReadFixedF32(float* out) {
        uncheckedRead<float>(out);
    }
    void uncheckedReadFixedF64(double* out) {
        uncheckedRead<double>(out);
    }
    template <typename UInt>
    UInt uncheckedReadVarU() {
        static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
        static const unsigned remainderBits = numBits % 7;
        static const unsigned numBitsInSevens = numBits - remainderBits;
        UInt decoded = 0;
        uint32_t shift = 0;
        do {
            uint8_t byte = *cur_++;
            if (!(byte & 0x80)) {
                return decoded | (UInt(byte) << shift);
            }
            decoded |= UInt(byte & 0x7f) << shift;
            shift += 7;
        } while (shift != numBitsInSevens);
        uint8_t byte = *cur_++;
        MOZ_ASSERT(!(byte & 0xf0));
        return decoded | (UInt(byte) << numBitsInSevens);
    }
    uint32_t uncheckedReadVarU32() {
        return uncheckedReadVarU<uint32_t>();
    }
    int32_t uncheckedReadVarS32() {
        int32_t i32 = 0;
        MOZ_ALWAYS_TRUE(readVarS32(&i32));
        return i32;
    }
    uint64_t uncheckedReadVarU64() {
        return uncheckedReadVarU<uint64_t>();
    }
    int64_t uncheckedReadVarS64() {
        int64_t i64 = 0;
        MOZ_ALWAYS_TRUE(readVarS64(&i64));
        return i64;
    }
    Op uncheckedReadOp() {
        static_assert(size_t(Op::Limit) == 256, "fits");
        uint8_t u8 = uncheckedReadFixedU8();
        return u8 != UINT8_MAX
               ? Op(u8)
               : Op(uncheckedReadFixedU8() + UINT8_MAX);
    }
};

// The local entries are part of function bodies and thus serialized by both
// wasm and asm.js and decoded as part of both validation and compilation.

MOZ_MUST_USE bool
EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);

// This performs no validation; the local entries must already have been
// validated by an earlier pass.

MOZ_MUST_USE bool
DecodeValidatedLocalEntries(Decoder& d, ValTypeVector* locals);

// This validates the entries.

MOZ_MUST_USE bool
DecodeLocalEntries(Decoder& d, ModuleKind kind, const TypeDefVector& types,
                   HasGcTypes gcTypesEnabled, ValTypeVector* locals);

// Returns whether the given [begin, end) prefix of a module's bytecode starts a
// code section and, if so, returns the SectionRange of that code section.
// Note that, even if this function returns 'false', [begin, end) may actually
// be a valid module in the special case when there are no function defs and the
// code section is not present. Such modules can be valid so the caller must
// handle this special case.

MOZ_MUST_USE bool
StartsCodeSection(const uint8_t* begin, const uint8_t* end, SectionRange* range);

// Calling DecodeModuleEnvironment decodes all sections up to the code section
// and performs full validation of all those sections. The client must then
// decode the code section itself, reusing ValidateFunctionBody if necessary,
// and finally call DecodeModuleTail to decode all remaining sections after the
// code section (again, performing full validation).

MOZ_MUST_USE bool
DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env);

MOZ_MUST_USE bool
ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, uint32_t bodySize,
                     Decoder& d, ExclusiveDeferredValidationState& dvs);

MOZ_MUST_USE bool
DecodeModuleTail(Decoder& d, ModuleEnvironment* env, ExclusiveDeferredValidationState& dvs);

void
ConvertMemoryPagesToBytes(Limits* memory);

// Validate an entire module, returning true if the module was validated
// successfully. If Validate returns false:
//  - if *error is null, the caller should report out-of-memory
//  - otherwise, there was a legitimate error described by *error

MOZ_MUST_USE bool
Validate(JSContext* cx, const ShareableBytes& bytecode, UniqueChars* error);

// Same, but don't require a JSContext.
MOZ_MUST_USE bool
ValidateForCranelift(const ShareableBytes& bytecode, UniqueChars* error);


}  // namespace wasm
}  // namespace js

#endif // namespace wasm_validate_h
