tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WasmBinary.h (28064B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 *
      4 * Copyright 2021 Mozilla Foundation
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *     http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 #ifndef wasm_binary_h
     20 #define wasm_binary_h
     21 
     22 #include "mozilla/DebugOnly.h"
     23 #include "mozilla/Maybe.h"
     24 
     25 #include <type_traits>
     26 
     27 #include "js/WasmFeatures.h"
     28 
     29 #include "wasm/WasmBinaryTypes.h"
     30 #include "wasm/WasmCompile.h"
     31 #include "wasm/WasmCompileArgs.h"
     32 #include "wasm/WasmConstants.h"
     33 #include "wasm/WasmTypeDecls.h"
     34 #include "wasm/WasmTypeDef.h"
     35 #include "wasm/WasmValType.h"
     36 
     37 namespace js {
     38 namespace wasm {
     39 
     40 // The Opcode compactly and safely represents the primary opcode plus any
     41 // extension, with convenient predicates and accessors.
     42 
     43 class Opcode {
     44  uint32_t bits_;
     45 
     46 public:
     47  MOZ_IMPLICIT Opcode(Op op) : bits_(uint32_t(op)) {
     48    static_assert(size_t(Op::Limit) == 256, "fits");
     49    MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
     50  }
     51  MOZ_IMPLICIT Opcode(MiscOp op)
     52      : bits_((uint32_t(op) << 8) | uint32_t(Op::MiscPrefix)) {
     53    static_assert(size_t(MiscOp::Limit) <= 0xFFFFFF, "fits");
     54    MOZ_ASSERT(size_t(op) < size_t(MiscOp::Limit));
     55  }
     56  MOZ_IMPLICIT Opcode(ThreadOp op)
     57      : bits_((uint32_t(op) << 8) | uint32_t(Op::ThreadPrefix)) {
     58    static_assert(size_t(ThreadOp::Limit) <= 0xFFFFFF, "fits");
     59    MOZ_ASSERT(size_t(op) < size_t(ThreadOp::Limit));
     60  }
     61  MOZ_IMPLICIT Opcode(MozOp op)
     62      : bits_((uint32_t(op) << 8) | uint32_t(Op::MozPrefix)) {
     63    static_assert(size_t(MozOp::Limit) <= 0xFFFFFF, "fits");
     64    MOZ_ASSERT(size_t(op) < size_t(MozOp::Limit));
     65  }
     66  MOZ_IMPLICIT Opcode(SimdOp op)
     67      : bits_((uint32_t(op) << 8) | uint32_t(Op::SimdPrefix)) {
     68    static_assert(size_t(SimdOp::Limit) <= 0xFFFFFF, "fits");
     69    MOZ_ASSERT(size_t(op) < size_t(SimdOp::Limit));
     70  }
     71  MOZ_IMPLICIT Opcode(GcOp op)
     72      : bits_((uint32_t(op) << 8) | uint32_t(Op::GcPrefix)) {
     73    static_assert(size_t(SimdOp::Limit) <= 0xFFFFFF, "fits");
     74    MOZ_ASSERT(size_t(op) < size_t(SimdOp::Limit));
     75  }
     76 
     77  bool isOp() const { return bits_ < uint32_t(Op::FirstPrefix); }
     78  bool isMisc() const { return (bits_ & 255) == uint32_t(Op::MiscPrefix); }
     79  bool isThread() const { return (bits_ & 255) == uint32_t(Op::ThreadPrefix); }
     80  bool isMoz() const { return (bits_ & 255) == uint32_t(Op::MozPrefix); }
     81  bool isSimd() const { return (bits_ & 255) == uint32_t(Op::SimdPrefix); }
     82  bool isGc() const { return (bits_ & 255) == uint32_t(Op::GcPrefix); }
     83 
     84  Op asOp() const {
     85    MOZ_ASSERT(isOp());
     86    return Op(bits_);
     87  }
     88  MiscOp asMisc() const {
     89    MOZ_ASSERT(isMisc());
     90    return MiscOp(bits_ >> 8);
     91  }
     92  ThreadOp asThread() const {
     93    MOZ_ASSERT(isThread());
     94    return ThreadOp(bits_ >> 8);
     95  }
     96  MozOp asMoz() const {
     97    MOZ_ASSERT(isMoz());
     98    return MozOp(bits_ >> 8);
     99  }
    100  SimdOp asSimd() const {
    101    MOZ_ASSERT(isSimd());
    102    return SimdOp(bits_ >> 8);
    103  }
    104  GcOp asGc() const {
    105    MOZ_ASSERT(isGc());
    106    return GcOp(bits_ >> 8);
    107  }
    108 
    109  uint32_t bits() const { return bits_; }
    110 
    111  bool operator==(const Opcode& that) const { return bits_ == that.bits_; }
    112  bool operator!=(const Opcode& that) const { return bits_ != that.bits_; }
    113 };
    114 
    115 // The Encoder class appends bytes to the Bytes object it is given during
    116 // construction. The client is responsible for the Bytes's lifetime and must
    117 // keep the Bytes alive as long as the Encoder is used.
    118 
    119 class Encoder {
    120  Bytes& bytes_;
    121  const TypeContext* types_;
    122 
    123  template <class T>
    124  [[nodiscard]] bool write(const T& v) {
    125    return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
    126  }
    127 
    128  template <typename UInt>
    129  [[nodiscard]] bool writeVarU(UInt i) {
    130    do {
    131      uint8_t byte = i & 0x7f;
    132      i >>= 7;
    133      if (i != 0) {
    134        byte |= 0x80;
    135      }
    136      if (!bytes_.append(byte)) {
    137        return false;
    138      }
    139    } while (i != 0);
    140    return true;
    141  }
    142 
    143  template <typename SInt>
    144  [[nodiscard]] bool writeVarS(SInt i) {
    145    bool done;
    146    do {
    147      uint8_t byte = i & 0x7f;
    148      i >>= 7;
    149      done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
    150      if (!done) {
    151        byte |= 0x80;
    152      }
    153      if (!bytes_.append(byte)) {
    154        return false;
    155      }
    156    } while (!done);
    157    return true;
    158  }
    159 
    160  void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
    161    do {
    162      uint8_t assertByte = assertBits & 0x7f;
    163      uint8_t patchByte = patchBits & 0x7f;
    164      assertBits >>= 7;
    165      patchBits >>= 7;
    166      if (assertBits != 0) {
    167        assertByte |= 0x80;
    168        patchByte |= 0x80;
    169      }
    170      MOZ_ASSERT(assertByte == bytes_[offset]);
    171      (void)assertByte;
    172      bytes_[offset] = patchByte;
    173      offset++;
    174    } while (assertBits != 0);
    175  }
    176 
    177  void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
    178    MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
    179    patchFixedU8(offset, patchBits, assertBits);
    180  }
    181 
    182  void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
    183    MOZ_ASSERT(bytes_[offset] == assertBits);
    184    bytes_[offset] = patchBits;
    185  }
    186 
    187  uint32_t varU32ByteLength(size_t offset) const {
    188    size_t start = offset;
    189    while (bytes_[offset] & 0x80) {
    190      offset++;
    191    }
    192    return offset - start + 1;
    193  }
    194 
    195 public:
    196  explicit Encoder(Bytes& bytes) : bytes_(bytes), types_(nullptr) {
    197    MOZ_ASSERT(empty());
    198  }
    199  explicit Encoder(Bytes& bytes, const TypeContext& types)
    200      : bytes_(bytes), types_(&types) {
    201    MOZ_ASSERT(empty());
    202  }
    203 
    204  size_t currentOffset() const { return bytes_.length(); }
    205  bool empty() const { return currentOffset() == 0; }
    206 
    207  // Fixed-size encoding operations simply copy the literal bytes (without
    208  // attempting to align).
    209 
    210  [[nodiscard]] bool writeFixedU7(uint8_t i) {
    211    MOZ_ASSERT(i <= uint8_t(INT8_MAX));
    212    return writeFixedU8(i);
    213  }
    214  [[nodiscard]] bool writeFixedU8(uint8_t i) { return write<uint8_t>(i); }
    215  [[nodiscard]] bool writeFixedU32(uint32_t i) { return write<uint32_t>(i); }
    216  [[nodiscard]] bool writeFixedF32(float f) { return write<float>(f); }
    217  [[nodiscard]] bool writeFixedF64(double d) { return write<double>(d); }
    218 
    219  // Variable-length encodings that all use LEB128.
    220 
    221  [[nodiscard]] bool writeVarU32(uint32_t i) { return writeVarU<uint32_t>(i); }
    222  [[nodiscard]] bool writeVarS32(int32_t i) { return writeVarS<int32_t>(i); }
    223  [[nodiscard]] bool writeVarU64(uint64_t i) { return writeVarU<uint64_t>(i); }
    224  [[nodiscard]] bool writeVarS64(int64_t i) { return writeVarS<int64_t>(i); }
    225  [[nodiscard]] bool writeValType(ValType type) {
    226    static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
    227    if (type.isTypeRef()) {
    228      MOZ_RELEASE_ASSERT(types_,
    229                         "writeValType is used, but types were not specified.");
    230      if (!writeFixedU8(uint8_t(type.isNullable() ? TypeCode::NullableRef
    231                                                  : TypeCode::Ref))) {
    232        return false;
    233      }
    234      uint32_t typeIndex = types_->indexOf(*type.typeDef());
    235      // Encode positive LEB S33 as S64.
    236      return writeVarS64(typeIndex);
    237    }
    238    TypeCode tc = type.packed().typeCode();
    239    MOZ_ASSERT(size_t(tc) < size_t(TypeCode::Limit));
    240    return writeFixedU8(uint8_t(tc));
    241  }
    242  [[nodiscard]] bool writeOp(Opcode opcode) {
    243    // The Opcode constructor has asserted that `opcode` is meaningful, so no
    244    // further correctness checking is necessary here.
    245    uint32_t bits = opcode.bits();
    246    if (!writeFixedU8(bits & 255)) {
    247      return false;
    248    }
    249    if (opcode.isOp()) {
    250      return true;
    251    }
    252    return writeVarU32(bits >> 8);
    253  }
    254 
    255  // Fixed-length encodings that allow back-patching.
    256 
    257  [[nodiscard]] bool writePatchableFixedU7(size_t* offset) {
    258    *offset = bytes_.length();
    259    return writeFixedU8(UINT8_MAX);
    260  }
    261  void patchFixedU7(size_t offset, uint8_t patchBits) {
    262    return patchFixedU7(offset, patchBits, UINT8_MAX);
    263  }
    264 
    265  // Variable-length encodings that allow back-patching.
    266 
    267  [[nodiscard]] bool writePatchableVarU32(size_t* offset) {
    268    *offset = bytes_.length();
    269    return writeVarU32(UINT32_MAX);
    270  }
    271  void patchVarU32(size_t offset, uint32_t patchBits) {
    272    return patchVarU32(offset, patchBits, UINT32_MAX);
    273  }
    274 
    275  // Byte ranges start with an LEB128 length followed by an arbitrary sequence
    276  // of bytes. When used for strings, bytes are to be interpreted as utf8.
    277 
    278  [[nodiscard]] bool writeBytes(const void* bytes, uint32_t numBytes) {
    279    return writeVarU32(numBytes) &&
    280           bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
    281  }
    282 
    283  // A "section" is a contiguous range of bytes that stores its own size so
    284  // that it may be trivially skipped without examining the payload. Sections
    285  // require backpatching since the size of the section is only known at the
    286  // end while the size's varU32 must be stored at the beginning. Immediately
    287  // after the section length is the string id of the section.
    288 
    289  [[nodiscard]] bool startSection(SectionId id, size_t* offset) {
    290    MOZ_ASSERT(uint32_t(id) < 128);
    291    return writeVarU32(uint32_t(id)) && writePatchableVarU32(offset);
    292  }
    293  void finishSection(size_t offset) {
    294    return patchVarU32(offset,
    295                       bytes_.length() - offset - varU32ByteLength(offset));
    296  }
    297 };
    298 
    299 // The Decoder class decodes the bytes in the range it is given during
    300 // construction. The client is responsible for keeping the byte range alive as
    301 // long as the Decoder is used.
    302 
    303 class Decoder {
    304  const uint8_t* const beg_;
    305  const uint8_t* const end_;
    306  const uint8_t* cur_;
    307  const size_t offsetInModule_;
    308  UniqueChars* error_;
    309  UniqueCharsVector* warnings_;
    310 
    311  template <class T>
    312  [[nodiscard]] bool read(T* out) {
    313    if (bytesRemain() < sizeof(T)) {
    314      return false;
    315    }
    316    memcpy((void*)out, cur_, sizeof(T));
    317    cur_ += sizeof(T);
    318    return true;
    319  }
    320 
    321  template <class T>
    322  T uncheckedRead() {
    323    MOZ_ASSERT(bytesRemain() >= sizeof(T));
    324    T ret;
    325    memcpy(&ret, cur_, sizeof(T));
    326    cur_ += sizeof(T);
    327    return ret;
    328  }
    329 
    330  template <class T>
    331  void uncheckedRead(T* ret) {
    332    MOZ_ASSERT(bytesRemain() >= sizeof(T));
    333    memcpy(ret, cur_, sizeof(T));
    334    cur_ += sizeof(T);
    335  }
    336 
    337  template <typename UInt>
    338  [[nodiscard]] bool readVarU(UInt* out) {
    339    mozilla::DebugOnly<const uint8_t*> before = cur_;
    340    const unsigned numBits = sizeof(UInt) * CHAR_BIT;
    341    const unsigned remainderBits = numBits % 7;
    342    const unsigned numBitsInSevens = numBits - remainderBits;
    343    UInt u = 0;
    344    uint8_t byte;
    345    UInt shift = 0;
    346    do {
    347      if (!readFixedU8(&byte)) {
    348        return false;
    349      }
    350      if (!(byte & 0x80)) {
    351        *out = u | UInt(byte) << shift;
    352        return true;
    353      }
    354      u |= UInt(byte & 0x7F) << shift;
    355      shift += 7;
    356    } while (shift != numBitsInSevens);
    357    if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits))) {
    358      return false;
    359    }
    360    *out = u | (UInt(byte) << numBitsInSevens);
    361    MOZ_ASSERT_IF(sizeof(UInt) == 4,
    362                  unsigned(cur_ - before) <= MaxVarU32DecodedBytes);
    363    return true;
    364  }
    365 
    366  template <typename SInt>
    367  [[nodiscard]] bool readVarS(SInt* out) {
    368    using UInt = std::make_unsigned_t<SInt>;
    369    const unsigned numBits = sizeof(SInt) * CHAR_BIT;
    370    const unsigned remainderBits = numBits % 7;
    371    const unsigned numBitsInSevens = numBits - remainderBits;
    372    SInt s = 0;
    373    uint8_t byte;
    374    unsigned shift = 0;
    375    do {
    376      if (!readFixedU8(&byte)) {
    377        return false;
    378      }
    379      s |= SInt(byte & 0x7f) << shift;
    380      shift += 7;
    381      if (!(byte & 0x80)) {
    382        if (byte & 0x40) {
    383          s |= UInt(-1) << shift;
    384        }
    385        *out = s;
    386        return true;
    387      }
    388    } while (shift < numBitsInSevens);
    389    if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80)) {
    390      return false;
    391    }
    392    uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
    393    if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0)) {
    394      return false;
    395    }
    396    *out = s | UInt(byte) << shift;
    397    return true;
    398  }
    399 
    400 public:
    401  Decoder(const uint8_t* begin, const uint8_t* end, size_t offsetInModule,
    402          UniqueChars* error, UniqueCharsVector* warnings = nullptr)
    403      : beg_(begin),
    404        end_(end),
    405        cur_(begin),
    406        offsetInModule_(offsetInModule),
    407        error_(error),
    408        warnings_(warnings) {
    409    MOZ_ASSERT(begin <= end);
    410  }
    411  explicit Decoder(BytecodeSpan span, size_t offsetInModule = 0,
    412                   UniqueChars* error = nullptr,
    413                   UniqueCharsVector* warnings = nullptr)
    414      : beg_(span.data()),
    415        end_(span.data() + span.size()),
    416        cur_(span.data()),
    417        offsetInModule_(offsetInModule),
    418        error_(error),
    419        warnings_(warnings) {}
    420 
    421  // These convenience functions use currentOffset() as the errorOffset.
    422  bool fail(const char* msg) { return fail(currentOffset(), msg); }
    423  bool failf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
    424  void warnf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
    425 
    426  // Report an error at the given offset (relative to the whole module).
    427  bool fail(size_t errorOffset, const char* msg);
    428 
    429  UniqueChars* error() { return error_; }
    430 
    431  void clearError() {
    432    if (error_) {
    433      error_->reset();
    434    }
    435  }
    436 
    437  bool done() const {
    438    MOZ_ASSERT(cur_ <= end_);
    439    return cur_ == end_;
    440  }
    441 
    442  size_t bytesRemain() const {
    443    MOZ_ASSERT(end_ >= cur_);
    444    return size_t(end_ - cur_);
    445  }
    446  // pos must be a value previously returned from currentPosition.
    447  void rollbackPosition(const uint8_t* pos) { cur_ = pos; }
    448  const uint8_t* currentPosition() const { return cur_; }
    449  size_t beginOffset() const { return offsetInModule_; }
    450  size_t currentOffset() const { return offsetInModule_ + (cur_ - beg_); }
    451  const uint8_t* begin() const { return beg_; }
    452  const uint8_t* end() const { return end_; }
    453 
    454  // Peek at the next byte, if it exists, without advancing the position.
    455 
    456  bool peekByte(uint8_t* byte) {
    457    if (done()) {
    458      return false;
    459    }
    460    *byte = *cur_;
    461    return true;
    462  }
    463 
    464  // Fixed-size encoding operations simply copy the literal bytes (without
    465  // attempting to align).
    466 
    467  [[nodiscard]] bool readFixedU8(uint8_t* i) { return read<uint8_t>(i); }
    468  [[nodiscard]] bool readFixedU32(uint32_t* u) { return read<uint32_t>(u); }
    469  [[nodiscard]] bool readFixedF32(float* f) { return read<float>(f); }
    470  [[nodiscard]] bool readFixedF64(double* d) { return read<double>(d); }
    471 #ifdef ENABLE_WASM_SIMD
    472  [[nodiscard]] bool readFixedV128(V128* d) {
    473    for (unsigned i = 0; i < 16; i++) {
    474      if (!read<uint8_t>(d->bytes + i)) {
    475        return false;
    476      }
    477    }
    478    return true;
    479  }
    480 #endif
    481 
    482  // Variable-length encodings that all use LEB128.
    483 
    484  [[nodiscard]] bool readVarU32(uint32_t* out) {
    485    return readVarU<uint32_t>(out);
    486  }
    487  [[nodiscard]] bool readVarS32(int32_t* out) { return readVarS<int32_t>(out); }
    488  [[nodiscard]] bool readVarU64(uint64_t* out) {
    489    return readVarU<uint64_t>(out);
    490  }
    491  [[nodiscard]] bool readVarS64(int64_t* out) { return readVarS<int64_t>(out); }
    492 
    493  // Value and reference types
    494 
    495  [[nodiscard]] ValType uncheckedReadValType(const TypeContext& types);
    496 
    497  template <class T>
    498  [[nodiscard]] bool readPackedType(const TypeContext& types,
    499                                    const FeatureArgs& features, T* type);
    500 
    501  [[nodiscard]] bool readValType(const TypeContext& types,
    502                                 const FeatureArgs& features, ValType* type);
    503 
    504  [[nodiscard]] bool readStorageType(const TypeContext& types,
    505                                     const FeatureArgs& features,
    506                                     StorageType* type);
    507 
    508  [[nodiscard]] bool readHeapType(const TypeContext& types,
    509                                  const FeatureArgs& features, bool nullable,
    510                                  RefType* type);
    511 
    512  [[nodiscard]] bool readRefType(const TypeContext& types,
    513                                 const FeatureArgs& features, RefType* type);
    514 
    515  // Instruction opcode
    516 
    517  [[nodiscard]] bool readOp(OpBytes* op);
    518 
    519  // Instruction immediates for constant instructions
    520 
    521  [[nodiscard]] bool readBinary() { return true; }
    522  [[nodiscard]] bool readTypeIndex(uint32_t* typeIndex);
    523  [[nodiscard]] bool readGlobalIndex(uint32_t* globalIndex);
    524  [[nodiscard]] bool readFuncIndex(uint32_t* funcIndex);
    525  [[nodiscard]] bool readI32Const(int32_t* i32);
    526  [[nodiscard]] bool readI64Const(int64_t* i64);
    527  [[nodiscard]] bool readF32Const(float* f32);
    528  [[nodiscard]] bool readF64Const(double* f64);
    529 #ifdef ENABLE_WASM_SIMD
    530  [[nodiscard]] bool readV128Const(V128* value);
    531 #endif
    532  [[nodiscard]] bool readRefNull(const TypeContext& types,
    533                                 const FeatureArgs& features, RefType* type);
    534 
    535  // See writeBytes comment.
    536 
    537  [[nodiscard]] bool readBytes(uint32_t numBytes,
    538                               const uint8_t** bytes = nullptr) {
    539    if (bytes) {
    540      *bytes = cur_;
    541    }
    542    if (bytesRemain() < numBytes) {
    543      return false;
    544    }
    545    cur_ += numBytes;
    546    return true;
    547  }
    548 
    549  // See "section" description in Encoder.
    550 
    551  [[nodiscard]] bool readSectionHeader(uint8_t* id, BytecodeRange* range);
    552 
    553  [[nodiscard]] bool startSection(SectionId id, CodeMetadata* codeMeta,
    554                                  MaybeBytecodeRange* range,
    555                                  const char* sectionName);
    556  [[nodiscard]] bool finishSection(const BytecodeRange& range,
    557                                   const char* sectionName);
    558 
    559  // Custom sections do not cause validation errors unless the error is in
    560  // the section header itself.
    561 
    562  [[nodiscard]] bool startCustomSection(const char* expected,
    563                                        size_t expectedLength,
    564                                        CodeMetadata* codeMeta,
    565                                        MaybeBytecodeRange* range);
    566 
    567  template <size_t NameSizeWith0>
    568  [[nodiscard]] bool startCustomSection(const char (&name)[NameSizeWith0],
    569                                        CodeMetadata* codeMeta,
    570                                        MaybeBytecodeRange* range) {
    571    MOZ_ASSERT(name[NameSizeWith0 - 1] == '\0');
    572    return startCustomSection(name, NameSizeWith0 - 1, codeMeta, range);
    573  }
    574 
    575  [[nodiscard]] bool finishCustomSection(const char* name,
    576                                         const BytecodeRange& range);
    577  void skipAndFinishCustomSection(const BytecodeRange& range);
    578 
    579  [[nodiscard]] bool skipCustomSection(CodeMetadata* codeMeta);
    580 
    581  // The Name section has its own optional subsections.
    582 
    583  [[nodiscard]] bool startNameSubsection(NameType nameType,
    584                                         mozilla::Maybe<uint32_t>* endOffset);
    585  [[nodiscard]] bool finishNameSubsection(uint32_t endOffset);
    586  [[nodiscard]] bool skipNameSubsection();
    587 
    588  // The infallible "unchecked" decoding functions can be used when we are
    589  // sure that the bytes are well-formed (by construction or due to previous
    590  // validation).
    591 
    592  uint8_t uncheckedReadFixedU8() { return uncheckedRead<uint8_t>(); }
    593  uint32_t uncheckedReadFixedU32() { return uncheckedRead<uint32_t>(); }
    594  void uncheckedReadFixedF32(float* out) { uncheckedRead<float>(out); }
    595  void uncheckedReadFixedF64(double* out) { uncheckedRead<double>(out); }
    596  template <typename UInt>
    597  UInt uncheckedReadVarU() {
    598    static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
    599    static const unsigned remainderBits = numBits % 7;
    600    static const unsigned numBitsInSevens = numBits - remainderBits;
    601    UInt decoded = 0;
    602    uint32_t shift = 0;
    603    do {
    604      uint8_t byte = *cur_++;
    605      if (!(byte & 0x80)) {
    606        return decoded | (UInt(byte) << shift);
    607      }
    608      decoded |= UInt(byte & 0x7f) << shift;
    609      shift += 7;
    610    } while (shift != numBitsInSevens);
    611    uint8_t byte = *cur_++;
    612    MOZ_ASSERT(!(byte & 0xf0));
    613    return decoded | (UInt(byte) << numBitsInSevens);
    614  }
    615  uint32_t uncheckedReadVarU32() { return uncheckedReadVarU<uint32_t>(); }
    616  int32_t uncheckedReadVarS32() {
    617    int32_t i32 = 0;
    618    MOZ_ALWAYS_TRUE(readVarS32(&i32));
    619    return i32;
    620  }
    621  uint64_t uncheckedReadVarU64() { return uncheckedReadVarU<uint64_t>(); }
    622  int64_t uncheckedReadVarS64() {
    623    int64_t i64 = 0;
    624    MOZ_ALWAYS_TRUE(readVarS64(&i64));
    625    return i64;
    626  }
    627  Op uncheckedReadOp() {
    628    static_assert(size_t(Op::Limit) == 256, "fits");
    629    uint8_t u8 = uncheckedReadFixedU8();
    630    return u8 != UINT8_MAX ? Op(u8) : Op(uncheckedReadFixedU8() + UINT8_MAX);
    631  }
    632 };
    633 
    634 // Value and reference types
    635 
    636 inline ValType Decoder::uncheckedReadValType(const TypeContext& types) {
    637  uint8_t code = uncheckedReadFixedU8();
    638  switch (code) {
    639    case uint8_t(TypeCode::AnyRef):
    640    case uint8_t(TypeCode::EqRef):
    641    case uint8_t(TypeCode::I31Ref):
    642    case uint8_t(TypeCode::StructRef):
    643    case uint8_t(TypeCode::ArrayRef):
    644    case uint8_t(TypeCode::NullAnyRef):
    645    case uint8_t(TypeCode::FuncRef):
    646    case uint8_t(TypeCode::NullFuncRef):
    647    case uint8_t(TypeCode::ExternRef):
    648    case uint8_t(TypeCode::NullExternRef):
    649    case uint8_t(TypeCode::ExnRef):
    650    case uint8_t(TypeCode::NullExnRef):
    651      return RefType::fromTypeCode(TypeCode(code), true);
    652    case uint8_t(TypeCode::Ref):
    653    case uint8_t(TypeCode::NullableRef): {
    654      bool nullable = code == uint8_t(TypeCode::NullableRef);
    655 
    656      uint8_t nextByte;
    657      peekByte(&nextByte);
    658 
    659      if ((nextByte & SLEB128SignMask) == SLEB128SignBit) {
    660        uint8_t code = uncheckedReadFixedU8();
    661        return RefType::fromTypeCode(TypeCode(code), nullable);
    662      }
    663 
    664      int32_t x = uncheckedReadVarS32();
    665      const TypeDef* typeDef = &types.type(x);
    666      return RefType::fromTypeDef(typeDef, nullable);
    667    }
    668    default:
    669      return ValType::fromNonRefTypeCode(TypeCode(code));
    670  }
    671 }
    672 
    673 template <class T>
    674 inline bool Decoder::readPackedType(const TypeContext& types,
    675                                    const FeatureArgs& features, T* type) {
    676  static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
    677  uint8_t code;
    678  if (!readFixedU8(&code)) {
    679    return fail("expected type code");
    680  }
    681  switch (code) {
    682    case uint8_t(TypeCode::V128): {
    683 #ifdef ENABLE_WASM_SIMD
    684      if (!features.simd) {
    685        return fail("v128 not enabled");
    686      }
    687      *type = T::fromNonRefTypeCode(TypeCode(code));
    688      return true;
    689 #else
    690      break;
    691 #endif
    692    }
    693    case uint8_t(TypeCode::FuncRef):
    694    case uint8_t(TypeCode::ExternRef): {
    695      *type = RefType::fromTypeCode(TypeCode(code), true);
    696      return true;
    697    }
    698    case uint8_t(TypeCode::ExnRef):
    699    case uint8_t(TypeCode::NullExnRef): {
    700      *type = RefType::fromTypeCode(TypeCode(code), true);
    701      return true;
    702    }
    703    case uint8_t(TypeCode::Ref):
    704    case uint8_t(TypeCode::NullableRef): {
    705      bool nullable = code == uint8_t(TypeCode::NullableRef);
    706      RefType refType;
    707      if (!readHeapType(types, features, nullable, &refType)) {
    708        return false;
    709      }
    710      *type = refType;
    711      return true;
    712    }
    713    case uint8_t(TypeCode::AnyRef):
    714    case uint8_t(TypeCode::I31Ref):
    715    case uint8_t(TypeCode::EqRef):
    716    case uint8_t(TypeCode::StructRef):
    717    case uint8_t(TypeCode::ArrayRef):
    718    case uint8_t(TypeCode::NullFuncRef):
    719    case uint8_t(TypeCode::NullExternRef):
    720    case uint8_t(TypeCode::NullAnyRef): {
    721      *type = RefType::fromTypeCode(TypeCode(code), true);
    722      return true;
    723    }
    724    default: {
    725      if (!T::isValidTypeCode(TypeCode(code))) {
    726        break;
    727      }
    728      *type = T::fromNonRefTypeCode(TypeCode(code));
    729      return true;
    730    }
    731  }
    732  return fail("bad type");
    733 }
    734 
    735 inline bool Decoder::readValType(const TypeContext& types,
    736                                 const FeatureArgs& features, ValType* type) {
    737  return readPackedType<ValType>(types, features, type);
    738 }
    739 
    740 inline bool Decoder::readStorageType(const TypeContext& types,
    741                                     const FeatureArgs& features,
    742                                     StorageType* type) {
    743  return readPackedType<StorageType>(types, features, type);
    744 }
    745 
    746 inline bool Decoder::readHeapType(const TypeContext& types,
    747                                  const FeatureArgs& features, bool nullable,
    748                                  RefType* type) {
    749  uint8_t nextByte;
    750  if (!peekByte(&nextByte)) {
    751    return fail("expected heap type code");
    752  }
    753 
    754  if ((nextByte & SLEB128SignMask) == SLEB128SignBit) {
    755    uint8_t code;
    756    if (!readFixedU8(&code)) {
    757      return false;
    758    }
    759 
    760    switch (code) {
    761      case uint8_t(TypeCode::FuncRef):
    762      case uint8_t(TypeCode::ExternRef):
    763        *type = RefType::fromTypeCode(TypeCode(code), nullable);
    764        return true;
    765      case uint8_t(TypeCode::ExnRef):
    766      case uint8_t(TypeCode::NullExnRef): {
    767        *type = RefType::fromTypeCode(TypeCode(code), nullable);
    768        return true;
    769      }
    770      case uint8_t(TypeCode::AnyRef):
    771      case uint8_t(TypeCode::I31Ref):
    772      case uint8_t(TypeCode::EqRef):
    773      case uint8_t(TypeCode::StructRef):
    774      case uint8_t(TypeCode::ArrayRef):
    775      case uint8_t(TypeCode::NullFuncRef):
    776      case uint8_t(TypeCode::NullExternRef):
    777      case uint8_t(TypeCode::NullAnyRef):
    778        *type = RefType::fromTypeCode(TypeCode(code), nullable);
    779        return true;
    780      default:
    781        return fail("invalid heap type");
    782    }
    783  }
    784 
    785  int32_t x;
    786  if (!readVarS32(&x) || x < 0 || uint32_t(x) >= types.length()) {
    787    return fail("invalid heap type index");
    788  }
    789  const TypeDef* typeDef = &types.type(x);
    790  *type = RefType::fromTypeDef(typeDef, nullable);
    791  return true;
    792 }
    793 
    794 inline bool Decoder::readRefType(const TypeContext& types,
    795                                 const FeatureArgs& features, RefType* type) {
    796  ValType valType;
    797  if (!readValType(types, features, &valType)) {
    798    return false;
    799  }
    800  if (!valType.isRefType()) {
    801    return fail("bad type");
    802  }
    803  *type = valType.refType();
    804  return true;
    805 }
    806 
    807 // Instruction opcode
    808 
    809 inline bool Decoder::readOp(OpBytes* op) {
    810  static_assert(size_t(Op::Limit) == 256, "fits");
    811  uint8_t u8;
    812  if (!readFixedU8(&u8)) {
    813    return false;
    814  }
    815  op->b0 = u8;
    816  if (MOZ_LIKELY(!IsPrefixByte(u8))) {
    817    return true;
    818  }
    819  return readVarU32(&op->b1);
    820 }
    821 
    822 // Instruction immediates for constant instructions
    823 
    824 inline bool Decoder::readTypeIndex(uint32_t* typeIndex) {
    825  if (!readVarU32(typeIndex)) {
    826    return fail("unable to read type index");
    827  }
    828  return true;
    829 }
    830 
    831 inline bool Decoder::readGlobalIndex(uint32_t* globalIndex) {
    832  if (!readVarU32(globalIndex)) {
    833    return fail("unable to read global index");
    834  }
    835  return true;
    836 }
    837 
    838 inline bool Decoder::readFuncIndex(uint32_t* funcIndex) {
    839  if (!readVarU32(funcIndex)) {
    840    return fail("unable to read function index");
    841  }
    842  return true;
    843 }
    844 
    845 inline bool Decoder::readI32Const(int32_t* i32) {
    846  if (!readVarS32(i32)) {
    847    return fail("failed to read I32 constant");
    848  }
    849  return true;
    850 }
    851 
    852 inline bool Decoder::readI64Const(int64_t* i64) {
    853  if (!readVarS64(i64)) {
    854    return fail("failed to read I64 constant");
    855  }
    856  return true;
    857 }
    858 
    859 inline bool Decoder::readF32Const(float* f32) {
    860  if (!readFixedF32(f32)) {
    861    return fail("failed to read F32 constant");
    862  }
    863  return true;
    864 }
    865 
    866 inline bool Decoder::readF64Const(double* f64) {
    867  if (!readFixedF64(f64)) {
    868    return fail("failed to read F64 constant");
    869  }
    870  return true;
    871 }
    872 
    873 #ifdef ENABLE_WASM_SIMD
    874 inline bool Decoder::readV128Const(V128* value) {
    875  if (!readFixedV128(value)) {
    876    return fail("unable to read V128 constant");
    877  }
    878  return true;
    879 }
    880 #endif
    881 
    882 inline bool Decoder::readRefNull(const TypeContext& types,
    883                                 const FeatureArgs& features, RefType* type) {
    884  return readHeapType(types, features, true, type);
    885 }
    886 
    887 }  // namespace wasm
    888 }  // namespace js
    889 
    890 #endif  // namespace wasm_binary_h