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