Bug 1867190 - Add prefs for PHC probablities r=glandium
[gecko.git] / js / src / wasm / WasmBinary.h
blob2d415281571c746d9327a31279e365707a595d95
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:
4 * Copyright 2021 Mozilla Foundation
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #ifndef wasm_binary_h
20 #define wasm_binary_h
22 #include "mozilla/DebugOnly.h"
23 #include "mozilla/Maybe.h"
25 #include <type_traits>
27 #include "js/WasmFeatures.h"
29 #include "wasm/WasmCompile.h"
30 #include "wasm/WasmCompileArgs.h"
31 #include "wasm/WasmConstants.h"
32 #include "wasm/WasmTypeDecls.h"
33 #include "wasm/WasmTypeDef.h"
34 #include "wasm/WasmValType.h"
36 namespace js {
37 namespace wasm {
39 using mozilla::DebugOnly;
40 using mozilla::Maybe;
42 struct ModuleEnvironment;
44 // The Opcode compactly and safely represents the primary opcode plus any
45 // extension, with convenient predicates and accessors.
47 class Opcode {
48 uint32_t bits_;
50 public:
51 MOZ_IMPLICIT Opcode(Op op) : bits_(uint32_t(op)) {
52 static_assert(size_t(Op::Limit) == 256, "fits");
53 MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
55 MOZ_IMPLICIT Opcode(MiscOp op)
56 : bits_((uint32_t(op) << 8) | uint32_t(Op::MiscPrefix)) {
57 static_assert(size_t(MiscOp::Limit) <= 0xFFFFFF, "fits");
58 MOZ_ASSERT(size_t(op) < size_t(MiscOp::Limit));
60 MOZ_IMPLICIT Opcode(ThreadOp op)
61 : bits_((uint32_t(op) << 8) | uint32_t(Op::ThreadPrefix)) {
62 static_assert(size_t(ThreadOp::Limit) <= 0xFFFFFF, "fits");
63 MOZ_ASSERT(size_t(op) < size_t(ThreadOp::Limit));
65 MOZ_IMPLICIT Opcode(MozOp op)
66 : bits_((uint32_t(op) << 8) | uint32_t(Op::MozPrefix)) {
67 static_assert(size_t(MozOp::Limit) <= 0xFFFFFF, "fits");
68 MOZ_ASSERT(size_t(op) < size_t(MozOp::Limit));
70 MOZ_IMPLICIT Opcode(SimdOp op)
71 : bits_((uint32_t(op) << 8) | uint32_t(Op::SimdPrefix)) {
72 static_assert(size_t(SimdOp::Limit) <= 0xFFFFFF, "fits");
73 MOZ_ASSERT(size_t(op) < size_t(SimdOp::Limit));
76 bool isOp() const { return bits_ < uint32_t(Op::FirstPrefix); }
77 bool isMisc() const { return (bits_ & 255) == uint32_t(Op::MiscPrefix); }
78 bool isThread() const { return (bits_ & 255) == uint32_t(Op::ThreadPrefix); }
79 bool isMoz() const { return (bits_ & 255) == uint32_t(Op::MozPrefix); }
80 bool isSimd() const { return (bits_ & 255) == uint32_t(Op::SimdPrefix); }
82 Op asOp() const {
83 MOZ_ASSERT(isOp());
84 return Op(bits_);
86 MiscOp asMisc() const {
87 MOZ_ASSERT(isMisc());
88 return MiscOp(bits_ >> 8);
90 ThreadOp asThread() const {
91 MOZ_ASSERT(isThread());
92 return ThreadOp(bits_ >> 8);
94 MozOp asMoz() const {
95 MOZ_ASSERT(isMoz());
96 return MozOp(bits_ >> 8);
98 SimdOp asSimd() const {
99 MOZ_ASSERT(isSimd());
100 return SimdOp(bits_ >> 8);
103 uint32_t bits() const { return bits_; }
105 bool operator==(const Opcode& that) const { return bits_ == that.bits_; }
106 bool operator!=(const Opcode& that) const { return bits_ != that.bits_; }
109 // This struct captures the bytecode offset of a section's payload (so not
110 // including the header) and the size of the payload.
112 struct SectionRange {
113 uint32_t start;
114 uint32_t size;
116 uint32_t end() const { return start + size; }
117 bool operator==(const SectionRange& rhs) const {
118 return start == rhs.start && size == rhs.size;
122 using MaybeSectionRange = Maybe<SectionRange>;
124 // The Encoder class appends bytes to the Bytes object it is given during
125 // construction. The client is responsible for the Bytes's lifetime and must
126 // keep the Bytes alive as long as the Encoder is used.
128 class Encoder {
129 Bytes& bytes_;
131 template <class T>
132 [[nodiscard]] bool write(const T& v) {
133 return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
136 template <typename UInt>
137 [[nodiscard]] bool writeVarU(UInt i) {
138 do {
139 uint8_t byte = i & 0x7f;
140 i >>= 7;
141 if (i != 0) {
142 byte |= 0x80;
144 if (!bytes_.append(byte)) {
145 return false;
147 } while (i != 0);
148 return true;
151 template <typename SInt>
152 [[nodiscard]] bool writeVarS(SInt i) {
153 bool done;
154 do {
155 uint8_t byte = i & 0x7f;
156 i >>= 7;
157 done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
158 if (!done) {
159 byte |= 0x80;
161 if (!bytes_.append(byte)) {
162 return false;
164 } while (!done);
165 return true;
168 void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
169 do {
170 uint8_t assertByte = assertBits & 0x7f;
171 uint8_t patchByte = patchBits & 0x7f;
172 assertBits >>= 7;
173 patchBits >>= 7;
174 if (assertBits != 0) {
175 assertByte |= 0x80;
176 patchByte |= 0x80;
178 MOZ_ASSERT(assertByte == bytes_[offset]);
179 (void)assertByte;
180 bytes_[offset] = patchByte;
181 offset++;
182 } while (assertBits != 0);
185 void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
186 MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
187 patchFixedU8(offset, patchBits, assertBits);
190 void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
191 MOZ_ASSERT(bytes_[offset] == assertBits);
192 bytes_[offset] = patchBits;
195 uint32_t varU32ByteLength(size_t offset) const {
196 size_t start = offset;
197 while (bytes_[offset] & 0x80) {
198 offset++;
200 return offset - start + 1;
203 public:
204 explicit Encoder(Bytes& bytes) : bytes_(bytes) { MOZ_ASSERT(empty()); }
206 size_t currentOffset() const { return bytes_.length(); }
207 bool empty() const { return currentOffset() == 0; }
209 // Fixed-size encoding operations simply copy the literal bytes (without
210 // attempting to align).
212 [[nodiscard]] bool writeFixedU7(uint8_t i) {
213 MOZ_ASSERT(i <= uint8_t(INT8_MAX));
214 return writeFixedU8(i);
216 [[nodiscard]] bool writeFixedU8(uint8_t i) { return write<uint8_t>(i); }
217 [[nodiscard]] bool writeFixedU32(uint32_t i) { return write<uint32_t>(i); }
218 [[nodiscard]] bool writeFixedF32(float f) { return write<float>(f); }
219 [[nodiscard]] bool writeFixedF64(double d) { return write<double>(d); }
221 // Variable-length encodings that all use LEB128.
223 [[nodiscard]] bool writeVarU32(uint32_t i) { return writeVarU<uint32_t>(i); }
224 [[nodiscard]] bool writeVarS32(int32_t i) { return writeVarS<int32_t>(i); }
225 [[nodiscard]] bool writeVarU64(uint64_t i) { return writeVarU<uint64_t>(i); }
226 [[nodiscard]] bool writeVarS64(int64_t i) { return writeVarS<int64_t>(i); }
227 [[nodiscard]] bool writeValType(ValType type) {
228 static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
229 // writeValType is only used by asm.js, which doesn't use type
230 // references
231 MOZ_RELEASE_ASSERT(!type.isTypeRef(), "NYI");
232 TypeCode tc = type.packed().typeCode();
233 MOZ_ASSERT(size_t(tc) < size_t(TypeCode::Limit));
234 return writeFixedU8(uint8_t(tc));
236 [[nodiscard]] bool writeOp(Opcode opcode) {
237 // The Opcode constructor has asserted that `opcode` is meaningful, so no
238 // further correctness checking is necessary here.
239 uint32_t bits = opcode.bits();
240 if (!writeFixedU8(bits & 255)) {
241 return false;
243 if (opcode.isOp()) {
244 return true;
246 return writeVarU32(bits >> 8);
249 // Fixed-length encodings that allow back-patching.
251 [[nodiscard]] bool writePatchableFixedU7(size_t* offset) {
252 *offset = bytes_.length();
253 return writeFixedU8(UINT8_MAX);
255 void patchFixedU7(size_t offset, uint8_t patchBits) {
256 return patchFixedU7(offset, patchBits, UINT8_MAX);
259 // Variable-length encodings that allow back-patching.
261 [[nodiscard]] bool writePatchableVarU32(size_t* offset) {
262 *offset = bytes_.length();
263 return writeVarU32(UINT32_MAX);
265 void patchVarU32(size_t offset, uint32_t patchBits) {
266 return patchVarU32(offset, patchBits, UINT32_MAX);
269 // Byte ranges start with an LEB128 length followed by an arbitrary sequence
270 // of bytes. When used for strings, bytes are to be interpreted as utf8.
272 [[nodiscard]] bool writeBytes(const void* bytes, uint32_t numBytes) {
273 return writeVarU32(numBytes) &&
274 bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
277 // A "section" is a contiguous range of bytes that stores its own size so
278 // that it may be trivially skipped without examining the payload. Sections
279 // require backpatching since the size of the section is only known at the
280 // end while the size's varU32 must be stored at the beginning. Immediately
281 // after the section length is the string id of the section.
283 [[nodiscard]] bool startSection(SectionId id, size_t* offset) {
284 MOZ_ASSERT(uint32_t(id) < 128);
285 return writeVarU32(uint32_t(id)) && writePatchableVarU32(offset);
287 void finishSection(size_t offset) {
288 return patchVarU32(offset,
289 bytes_.length() - offset - varU32ByteLength(offset));
293 // The Decoder class decodes the bytes in the range it is given during
294 // construction. The client is responsible for keeping the byte range alive as
295 // long as the Decoder is used.
297 class Decoder {
298 const uint8_t* const beg_;
299 const uint8_t* const end_;
300 const uint8_t* cur_;
301 const size_t offsetInModule_;
302 UniqueChars* error_;
303 UniqueCharsVector* warnings_;
304 bool resilientMode_;
306 template <class T>
307 [[nodiscard]] bool read(T* out) {
308 if (bytesRemain() < sizeof(T)) {
309 return false;
311 memcpy((void*)out, cur_, sizeof(T));
312 cur_ += sizeof(T);
313 return true;
316 template <class T>
317 T uncheckedRead() {
318 MOZ_ASSERT(bytesRemain() >= sizeof(T));
319 T ret;
320 memcpy(&ret, cur_, sizeof(T));
321 cur_ += sizeof(T);
322 return ret;
325 template <class T>
326 void uncheckedRead(T* ret) {
327 MOZ_ASSERT(bytesRemain() >= sizeof(T));
328 memcpy(ret, cur_, sizeof(T));
329 cur_ += sizeof(T);
332 template <typename UInt>
333 [[nodiscard]] bool readVarU(UInt* out) {
334 DebugOnly<const uint8_t*> before = cur_;
335 const unsigned numBits = sizeof(UInt) * CHAR_BIT;
336 const unsigned remainderBits = numBits % 7;
337 const unsigned numBitsInSevens = numBits - remainderBits;
338 UInt u = 0;
339 uint8_t byte;
340 UInt shift = 0;
341 do {
342 if (!readFixedU8(&byte)) {
343 return false;
345 if (!(byte & 0x80)) {
346 *out = u | UInt(byte) << shift;
347 return true;
349 u |= UInt(byte & 0x7F) << shift;
350 shift += 7;
351 } while (shift != numBitsInSevens);
352 if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits))) {
353 return false;
355 *out = u | (UInt(byte) << numBitsInSevens);
356 MOZ_ASSERT_IF(sizeof(UInt) == 4,
357 unsigned(cur_ - before) <= MaxVarU32DecodedBytes);
358 return true;
361 template <typename SInt>
362 [[nodiscard]] bool readVarS(SInt* out) {
363 using UInt = std::make_unsigned_t<SInt>;
364 const unsigned numBits = sizeof(SInt) * CHAR_BIT;
365 const unsigned remainderBits = numBits % 7;
366 const unsigned numBitsInSevens = numBits - remainderBits;
367 SInt s = 0;
368 uint8_t byte;
369 unsigned shift = 0;
370 do {
371 if (!readFixedU8(&byte)) {
372 return false;
374 s |= SInt(byte & 0x7f) << shift;
375 shift += 7;
376 if (!(byte & 0x80)) {
377 if (byte & 0x40) {
378 s |= UInt(-1) << shift;
380 *out = s;
381 return true;
383 } while (shift < numBitsInSevens);
384 if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80)) {
385 return false;
387 uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
388 if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0)) {
389 return false;
391 *out = s | UInt(byte) << shift;
392 return true;
395 public:
396 Decoder(const uint8_t* begin, const uint8_t* end, size_t offsetInModule,
397 UniqueChars* error, UniqueCharsVector* warnings = nullptr,
398 bool resilientMode = false)
399 : beg_(begin),
400 end_(end),
401 cur_(begin),
402 offsetInModule_(offsetInModule),
403 error_(error),
404 warnings_(warnings),
405 resilientMode_(resilientMode) {
406 MOZ_ASSERT(begin <= end);
408 explicit Decoder(const Bytes& bytes, size_t offsetInModule = 0,
409 UniqueChars* error = nullptr,
410 UniqueCharsVector* warnings = nullptr)
411 : beg_(bytes.begin()),
412 end_(bytes.end()),
413 cur_(bytes.begin()),
414 offsetInModule_(offsetInModule),
415 error_(error),
416 warnings_(warnings),
417 resilientMode_(false) {}
419 // These convenience functions use currentOffset() as the errorOffset.
420 bool fail(const char* msg) { return fail(currentOffset(), msg); }
421 bool failf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
422 void warnf(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
424 // Report an error at the given offset (relative to the whole module).
425 bool fail(size_t errorOffset, const char* msg);
427 UniqueChars* error() { return error_; }
429 void clearError() {
430 if (error_) {
431 error_->reset();
435 bool done() const {
436 MOZ_ASSERT(cur_ <= end_);
437 return cur_ == end_;
439 bool resilientMode() const { return resilientMode_; }
441 size_t bytesRemain() const {
442 MOZ_ASSERT(end_ >= cur_);
443 return size_t(end_ - cur_);
445 // pos must be a value previously returned from currentPosition.
446 void rollbackPosition(const uint8_t* pos) { cur_ = pos; }
447 const uint8_t* currentPosition() const { return cur_; }
448 size_t beginOffset() const { return offsetInModule_; }
449 size_t currentOffset() const { return offsetInModule_ + (cur_ - beg_); }
450 const uint8_t* begin() const { return beg_; }
451 const uint8_t* end() const { return end_; }
453 // Peek at the next byte, if it exists, without advancing the position.
455 bool peekByte(uint8_t* byte) {
456 if (done()) {
457 return false;
459 *byte = *cur_;
460 return true;
463 // Fixed-size encoding operations simply copy the literal bytes (without
464 // attempting to align).
466 [[nodiscard]] bool readFixedU8(uint8_t* i) { return read<uint8_t>(i); }
467 [[nodiscard]] bool readFixedU32(uint32_t* u) { return read<uint32_t>(u); }
468 [[nodiscard]] bool readFixedF32(float* f) { return read<float>(f); }
469 [[nodiscard]] bool readFixedF64(double* d) { return read<double>(d); }
470 #ifdef ENABLE_WASM_SIMD
471 [[nodiscard]] bool readFixedV128(V128* d) {
472 for (unsigned i = 0; i < 16; i++) {
473 if (!read<uint8_t>(d->bytes + i)) {
474 return false;
477 return true;
479 #endif
481 // Variable-length encodings that all use LEB128.
483 [[nodiscard]] bool readVarU32(uint32_t* out) {
484 return readVarU<uint32_t>(out);
486 [[nodiscard]] bool readVarS32(int32_t* out) { return readVarS<int32_t>(out); }
487 [[nodiscard]] bool readVarU64(uint64_t* out) {
488 return readVarU<uint64_t>(out);
490 [[nodiscard]] bool readVarS64(int64_t* out) { return readVarS<int64_t>(out); }
492 // Value and reference types
494 [[nodiscard]] ValType uncheckedReadValType(const TypeContext& types);
496 template <class T>
497 [[nodiscard]] bool readPackedType(const TypeContext& types,
498 const FeatureArgs& features, T* type);
500 [[nodiscard]] bool readValType(const TypeContext& types,
501 const FeatureArgs& features, ValType* type);
503 [[nodiscard]] bool readStorageType(const TypeContext& types,
504 const FeatureArgs& features,
505 StorageType* type);
507 [[nodiscard]] bool readHeapType(const TypeContext& types,
508 const FeatureArgs& features, bool nullable,
509 RefType* type);
511 [[nodiscard]] bool readRefType(const TypeContext& types,
512 const FeatureArgs& features, RefType* type);
514 // Instruction opcode
516 [[nodiscard]] bool readOp(OpBytes* op);
518 // Instruction immediates for constant instructions
520 [[nodiscard]] bool readBinary() { return true; }
521 [[nodiscard]] bool readTypeIndex(uint32_t* typeIndex);
522 [[nodiscard]] bool readGlobalIndex(uint32_t* globalIndex);
523 [[nodiscard]] bool readFuncIndex(uint32_t* funcIndex);
524 [[nodiscard]] bool readI32Const(int32_t* i32);
525 [[nodiscard]] bool readI64Const(int64_t* i64);
526 [[nodiscard]] bool readF32Const(float* f32);
527 [[nodiscard]] bool readF64Const(double* f64);
528 #ifdef ENABLE_WASM_SIMD
529 [[nodiscard]] bool readV128Const(V128* value);
530 #endif
531 [[nodiscard]] bool readRefNull(const TypeContext& types,
532 const FeatureArgs& features, RefType* type);
534 // See writeBytes comment.
536 [[nodiscard]] bool readBytes(uint32_t numBytes,
537 const uint8_t** bytes = nullptr) {
538 if (bytes) {
539 *bytes = cur_;
541 if (bytesRemain() < numBytes) {
542 return false;
544 cur_ += numBytes;
545 return true;
548 // See "section" description in Encoder.
550 [[nodiscard]] bool readSectionHeader(uint8_t* id, SectionRange* range);
552 [[nodiscard]] bool startSection(SectionId id, ModuleEnvironment* env,
553 MaybeSectionRange* range,
554 const char* sectionName);
555 [[nodiscard]] bool finishSection(const SectionRange& range,
556 const char* sectionName);
558 // Custom sections do not cause validation errors unless the error is in
559 // the section header itself.
561 [[nodiscard]] bool startCustomSection(const char* expected,
562 size_t expectedLength,
563 ModuleEnvironment* env,
564 MaybeSectionRange* range);
566 template <size_t NameSizeWith0>
567 [[nodiscard]] bool startCustomSection(const char (&name)[NameSizeWith0],
568 ModuleEnvironment* env,
569 MaybeSectionRange* range) {
570 MOZ_ASSERT(name[NameSizeWith0 - 1] == '\0');
571 return startCustomSection(name, NameSizeWith0 - 1, env, range);
574 void finishCustomSection(const char* name, const SectionRange& range);
575 void skipAndFinishCustomSection(const SectionRange& range);
577 [[nodiscard]] bool skipCustomSection(ModuleEnvironment* env);
579 // The Name section has its own optional subsections.
581 [[nodiscard]] bool startNameSubsection(NameType nameType,
582 Maybe<uint32_t>* endOffset);
583 [[nodiscard]] bool finishNameSubsection(uint32_t endOffset);
584 [[nodiscard]] bool skipNameSubsection();
586 // The infallible "unchecked" decoding functions can be used when we are
587 // sure that the bytes are well-formed (by construction or due to previous
588 // validation).
590 uint8_t uncheckedReadFixedU8() { return uncheckedRead<uint8_t>(); }
591 uint32_t uncheckedReadFixedU32() { return uncheckedRead<uint32_t>(); }
592 void uncheckedReadFixedF32(float* out) { uncheckedRead<float>(out); }
593 void uncheckedReadFixedF64(double* out) { uncheckedRead<double>(out); }
594 template <typename UInt>
595 UInt uncheckedReadVarU() {
596 static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
597 static const unsigned remainderBits = numBits % 7;
598 static const unsigned numBitsInSevens = numBits - remainderBits;
599 UInt decoded = 0;
600 uint32_t shift = 0;
601 do {
602 uint8_t byte = *cur_++;
603 if (!(byte & 0x80)) {
604 return decoded | (UInt(byte) << shift);
606 decoded |= UInt(byte & 0x7f) << shift;
607 shift += 7;
608 } while (shift != numBitsInSevens);
609 uint8_t byte = *cur_++;
610 MOZ_ASSERT(!(byte & 0xf0));
611 return decoded | (UInt(byte) << numBitsInSevens);
613 uint32_t uncheckedReadVarU32() { return uncheckedReadVarU<uint32_t>(); }
614 int32_t uncheckedReadVarS32() {
615 int32_t i32 = 0;
616 MOZ_ALWAYS_TRUE(readVarS32(&i32));
617 return i32;
619 uint64_t uncheckedReadVarU64() { return uncheckedReadVarU<uint64_t>(); }
620 int64_t uncheckedReadVarS64() {
621 int64_t i64 = 0;
622 MOZ_ALWAYS_TRUE(readVarS64(&i64));
623 return i64;
625 Op uncheckedReadOp() {
626 static_assert(size_t(Op::Limit) == 256, "fits");
627 uint8_t u8 = uncheckedReadFixedU8();
628 return u8 != UINT8_MAX ? Op(u8) : Op(uncheckedReadFixedU8() + UINT8_MAX);
632 // Value and reference types
634 inline ValType Decoder::uncheckedReadValType(const TypeContext& types) {
635 uint8_t code = uncheckedReadFixedU8();
636 switch (code) {
637 case uint8_t(TypeCode::FuncRef):
638 case uint8_t(TypeCode::ExternRef):
639 case uint8_t(TypeCode::ExnRef):
640 return RefType::fromTypeCode(TypeCode(code), true);
641 case uint8_t(TypeCode::Ref):
642 case uint8_t(TypeCode::NullableRef): {
643 bool nullable = code == uint8_t(TypeCode::NullableRef);
645 uint8_t nextByte;
646 peekByte(&nextByte);
648 if ((nextByte & SLEB128SignMask) == SLEB128SignBit) {
649 uint8_t code = uncheckedReadFixedU8();
650 return RefType::fromTypeCode(TypeCode(code), nullable);
653 int32_t x = uncheckedReadVarS32();
654 const TypeDef* typeDef = &types.type(x);
655 return RefType::fromTypeDef(typeDef, nullable);
657 default:
658 return ValType::fromNonRefTypeCode(TypeCode(code));
662 template <class T>
663 inline bool Decoder::readPackedType(const TypeContext& types,
664 const FeatureArgs& features, T* type) {
665 static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
666 uint8_t code;
667 if (!readFixedU8(&code)) {
668 return fail("expected type code");
670 switch (code) {
671 case uint8_t(TypeCode::V128): {
672 #ifdef ENABLE_WASM_SIMD
673 if (!features.simd) {
674 return fail("v128 not enabled");
676 *type = T::fromNonRefTypeCode(TypeCode(code));
677 return true;
678 #else
679 break;
680 #endif
682 case uint8_t(TypeCode::FuncRef):
683 case uint8_t(TypeCode::ExternRef): {
684 *type = RefType::fromTypeCode(TypeCode(code), true);
685 return true;
687 case uint8_t(TypeCode::ExnRef): {
688 if (!features.exnref) {
689 return fail("exnref not enabled");
691 *type = RefType::fromTypeCode(TypeCode(code), true);
692 return true;
694 case uint8_t(TypeCode::Ref):
695 case uint8_t(TypeCode::NullableRef): {
696 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
697 if (!features.functionReferences) {
698 return fail("(ref T) types not enabled");
700 bool nullable = code == uint8_t(TypeCode::NullableRef);
701 RefType refType;
702 if (!readHeapType(types, features, nullable, &refType)) {
703 return false;
705 *type = refType;
706 return true;
707 #else
708 break;
709 #endif
711 case uint8_t(TypeCode::AnyRef):
712 case uint8_t(TypeCode::I31Ref):
713 case uint8_t(TypeCode::EqRef):
714 case uint8_t(TypeCode::StructRef):
715 case uint8_t(TypeCode::ArrayRef):
716 case uint8_t(TypeCode::NullFuncRef):
717 case uint8_t(TypeCode::NullExternRef):
718 case uint8_t(TypeCode::NullAnyRef): {
719 #ifdef ENABLE_WASM_GC
720 if (!features.gc) {
721 return fail("gc types not enabled");
723 *type = RefType::fromTypeCode(TypeCode(code), true);
724 return true;
725 #else
726 break;
727 #endif
729 default: {
730 if (!T::isValidTypeCode(TypeCode(code))) {
731 break;
733 *type = T::fromNonRefTypeCode(TypeCode(code));
734 return true;
737 return fail("bad type");
740 inline bool Decoder::readValType(const TypeContext& types,
741 const FeatureArgs& features, ValType* type) {
742 return readPackedType<ValType>(types, features, type);
745 inline bool Decoder::readStorageType(const TypeContext& types,
746 const FeatureArgs& features,
747 StorageType* type) {
748 return readPackedType<StorageType>(types, features, type);
751 inline bool Decoder::readHeapType(const TypeContext& types,
752 const FeatureArgs& features, bool nullable,
753 RefType* type) {
754 uint8_t nextByte;
755 if (!peekByte(&nextByte)) {
756 return fail("expected heap type code");
759 if ((nextByte & SLEB128SignMask) == SLEB128SignBit) {
760 uint8_t code;
761 if (!readFixedU8(&code)) {
762 return false;
765 switch (code) {
766 case uint8_t(TypeCode::FuncRef):
767 case uint8_t(TypeCode::ExternRef):
768 *type = RefType::fromTypeCode(TypeCode(code), nullable);
769 return true;
770 case uint8_t(TypeCode::ExnRef): {
771 if (!features.exnref) {
772 return fail("exnref not enabled");
774 *type = RefType::fromTypeCode(TypeCode(code), nullable);
775 return true;
777 #ifdef ENABLE_WASM_GC
778 case uint8_t(TypeCode::AnyRef):
779 case uint8_t(TypeCode::I31Ref):
780 case uint8_t(TypeCode::EqRef):
781 case uint8_t(TypeCode::StructRef):
782 case uint8_t(TypeCode::ArrayRef):
783 case uint8_t(TypeCode::NullFuncRef):
784 case uint8_t(TypeCode::NullExternRef):
785 case uint8_t(TypeCode::NullAnyRef):
786 if (!features.gc) {
787 return fail("gc types not enabled");
789 *type = RefType::fromTypeCode(TypeCode(code), nullable);
790 return true;
791 #endif
792 default:
793 return fail("invalid heap type");
797 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
798 if (features.functionReferences) {
799 int32_t x;
800 if (!readVarS32(&x) || x < 0 || uint32_t(x) >= types.length()) {
801 return fail("invalid heap type index");
803 const TypeDef* typeDef = &types.type(x);
804 *type = RefType::fromTypeDef(typeDef, nullable);
805 return true;
807 #endif
808 return fail("invalid heap type");
811 inline bool Decoder::readRefType(const TypeContext& types,
812 const FeatureArgs& features, RefType* type) {
813 ValType valType;
814 if (!readValType(types, features, &valType)) {
815 return false;
817 if (!valType.isRefType()) {
818 return fail("bad type");
820 *type = valType.refType();
821 return true;
824 // Instruction opcode
826 inline bool Decoder::readOp(OpBytes* op) {
827 static_assert(size_t(Op::Limit) == 256, "fits");
828 uint8_t u8;
829 if (!readFixedU8(&u8)) {
830 return false;
832 op->b0 = u8;
833 if (MOZ_LIKELY(!IsPrefixByte(u8))) {
834 return true;
836 return readVarU32(&op->b1);
839 // Instruction immediates for constant instructions
841 inline bool Decoder::readTypeIndex(uint32_t* typeIndex) {
842 if (!readVarU32(typeIndex)) {
843 return fail("unable to read type index");
845 return true;
848 inline bool Decoder::readGlobalIndex(uint32_t* globalIndex) {
849 if (!readVarU32(globalIndex)) {
850 return fail("unable to read global index");
852 return true;
855 inline bool Decoder::readFuncIndex(uint32_t* funcIndex) {
856 if (!readVarU32(funcIndex)) {
857 return fail("unable to read function index");
859 return true;
862 inline bool Decoder::readI32Const(int32_t* i32) {
863 if (!readVarS32(i32)) {
864 return fail("failed to read I32 constant");
866 return true;
869 inline bool Decoder::readI64Const(int64_t* i64) {
870 if (!readVarS64(i64)) {
871 return fail("failed to read I64 constant");
873 return true;
876 inline bool Decoder::readF32Const(float* f32) {
877 if (!readFixedF32(f32)) {
878 return fail("failed to read F32 constant");
880 return true;
883 inline bool Decoder::readF64Const(double* f64) {
884 if (!readFixedF64(f64)) {
885 return fail("failed to read F64 constant");
887 return true;
890 #ifdef ENABLE_WASM_SIMD
891 inline bool Decoder::readV128Const(V128* value) {
892 if (!readFixedV128(value)) {
893 return fail("unable to read V128 constant");
895 return true;
897 #endif
899 inline bool Decoder::readRefNull(const TypeContext& types,
900 const FeatureArgs& features, RefType* type) {
901 return readHeapType(types, features, true, type);
904 } // namespace wasm
905 } // namespace js
907 #endif // namespace wasm_binary_h