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 2016 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_op_iter_h
20 #define wasm_op_iter_h
21
22 #include "mozilla/CompactPair.h"
23 #include "mozilla/Poison.h"
24
25 #include <type_traits>
26
27 #include "js/Printf.h"
28 #include "wasm/WasmIntrinsic.h"
29 #include "wasm/WasmUtility.h"
30 #include "wasm/WasmValidate.h"
31
32 namespace js {
33 namespace wasm {
34
35 // The kind of a control-flow stack item.
36 enum class LabelKind : uint8_t {
37 Body,
38 Block,
39 Loop,
40 Then,
41 Else,
42 #ifdef ENABLE_WASM_EXCEPTIONS
43 Try,
44 Catch,
45 CatchAll,
46 #endif
47 };
48
49 // The type of values on the operand stack during validation. This is either a
50 // ValType or the special type "Bottom".
51
52 class StackType {
53 PackedTypeCode tc_;
54
StackType(PackedTypeCode tc)55 explicit StackType(PackedTypeCode tc) : tc_(tc) {}
56
57 public:
StackType()58 StackType() : tc_(PackedTypeCode::invalid()) {}
59
StackType(const ValType & t)60 explicit StackType(const ValType& t) : tc_(t.packed()) {
61 MOZ_ASSERT(tc_.isValid());
62 MOZ_ASSERT(!isBottom());
63 }
64
bottom()65 static StackType bottom() {
66 return StackType(PackedTypeCode::pack(TypeCode::Limit));
67 }
68
isBottom()69 bool isBottom() const {
70 MOZ_ASSERT(tc_.isValid());
71 return tc_.typeCode() == TypeCode::Limit;
72 }
73
valType()74 ValType valType() const {
75 MOZ_ASSERT(tc_.isValid());
76 MOZ_ASSERT(!isBottom());
77 return ValType(tc_);
78 }
79
asNonNullable()80 ValType asNonNullable() const {
81 MOZ_ASSERT(tc_.isValid());
82 MOZ_ASSERT(!isBottom());
83 return ValType(tc_.asNonNullable());
84 }
85
isValidForUntypedSelect()86 bool isValidForUntypedSelect() const {
87 MOZ_ASSERT(tc_.isValid());
88 if (isBottom()) {
89 return true;
90 }
91 switch (valType().kind()) {
92 case ValType::I32:
93 case ValType::F32:
94 case ValType::I64:
95 case ValType::F64:
96 #ifdef ENABLE_WASM_SIMD
97 case ValType::V128:
98 #endif
99 return true;
100 default:
101 return false;
102 }
103 }
104
105 bool operator==(const StackType& that) const {
106 MOZ_ASSERT(tc_.isValid() && that.tc_.isValid());
107 return tc_ == that.tc_;
108 }
109
110 bool operator!=(const StackType& that) const {
111 MOZ_ASSERT(tc_.isValid() && that.tc_.isValid());
112 return tc_ != that.tc_;
113 }
114 };
115
116 #ifdef DEBUG
117 // Families of opcodes that share a signature and validation logic.
118 enum class OpKind {
119 Block,
120 Loop,
121 Unreachable,
122 Drop,
123 I32,
124 I64,
125 F32,
126 F64,
127 V128,
128 Br,
129 BrIf,
130 BrTable,
131 Nop,
132 Unary,
133 Binary,
134 Ternary,
135 Comparison,
136 Conversion,
137 Load,
138 Store,
139 TeeStore,
140 MemorySize,
141 MemoryGrow,
142 Select,
143 GetLocal,
144 SetLocal,
145 TeeLocal,
146 GetGlobal,
147 SetGlobal,
148 TeeGlobal,
149 Call,
150 CallIndirect,
151 OldCallDirect,
152 OldCallIndirect,
153 Return,
154 If,
155 Else,
156 End,
157 Wait,
158 Wake,
159 Fence,
160 AtomicLoad,
161 AtomicStore,
162 AtomicBinOp,
163 AtomicCompareExchange,
164 OldAtomicLoad,
165 OldAtomicStore,
166 OldAtomicBinOp,
167 OldAtomicCompareExchange,
168 OldAtomicExchange,
169 MemOrTableCopy,
170 DataOrElemDrop,
171 MemFill,
172 MemOrTableInit,
173 TableFill,
174 TableGet,
175 TableGrow,
176 TableSet,
177 TableSize,
178 RefNull,
179 RefFunc,
180 RefAsNonNull,
181 BrOnNull,
182 StructNewWithRtt,
183 StructNewDefaultWithRtt,
184 StructGet,
185 StructSet,
186 ArrayNewWithRtt,
187 ArrayNewDefaultWithRtt,
188 ArrayGet,
189 ArraySet,
190 ArrayLen,
191 RttCanon,
192 RttSub,
193 RefTest,
194 RefCast,
195 BrOnCast,
196 # ifdef ENABLE_WASM_SIMD
197 ExtractLane,
198 ReplaceLane,
199 LoadLane,
200 StoreLane,
201 VectorShift,
202 VectorShuffle,
203 # endif
204 # ifdef ENABLE_WASM_EXCEPTIONS
205 Catch,
206 CatchAll,
207 Delegate,
208 Throw,
209 Rethrow,
210 Try,
211 # endif
212 Intrinsic,
213 };
214
215 // Return the OpKind for a given Op. This is used for sanity-checking that
216 // API users use the correct read function for a given Op.
217 OpKind Classify(OpBytes op);
218 #endif
219
220 // Common fields for linear memory access.
221 template <typename Value>
222 struct LinearMemoryAddress {
223 Value base;
224 uint64_t offset;
225 uint32_t align;
226
LinearMemoryAddressLinearMemoryAddress227 LinearMemoryAddress() : offset(0), align(0) {}
LinearMemoryAddressLinearMemoryAddress228 LinearMemoryAddress(Value base, uint64_t offset, uint32_t align)
229 : base(base), offset(offset), align(align) {}
230 };
231
232 template <typename ControlItem>
233 class ControlStackEntry {
234 // Use a pair to optimize away empty ControlItem.
235 mozilla::CompactPair<BlockType, ControlItem> typeAndItem_;
236
237 // The "base" of a control stack entry is valueStack_.length() minus
238 // type().params().length(), i.e., the size of the value stack "below"
239 // this block.
240 uint32_t valueStackBase_;
241 bool polymorphicBase_;
242
243 LabelKind kind_;
244
245 public:
ControlStackEntry(LabelKind kind,BlockType type,uint32_t valueStackBase)246 ControlStackEntry(LabelKind kind, BlockType type, uint32_t valueStackBase)
247 : typeAndItem_(type, ControlItem()),
248 valueStackBase_(valueStackBase),
249 polymorphicBase_(false),
250 kind_(kind) {
251 MOZ_ASSERT(type != BlockType());
252 }
253
kind()254 LabelKind kind() const { return kind_; }
type()255 BlockType type() const { return typeAndItem_.first(); }
resultType()256 ResultType resultType() const { return type().results(); }
branchTargetType()257 ResultType branchTargetType() const {
258 return kind_ == LabelKind::Loop ? type().params() : type().results();
259 }
valueStackBase()260 uint32_t valueStackBase() const { return valueStackBase_; }
controlItem()261 ControlItem& controlItem() { return typeAndItem_.second(); }
setPolymorphicBase()262 void setPolymorphicBase() { polymorphicBase_ = true; }
polymorphicBase()263 bool polymorphicBase() const { return polymorphicBase_; }
264
switchToElse()265 void switchToElse() {
266 MOZ_ASSERT(kind() == LabelKind::Then);
267 kind_ = LabelKind::Else;
268 polymorphicBase_ = false;
269 }
270
271 #ifdef ENABLE_WASM_EXCEPTIONS
switchToCatch()272 void switchToCatch() {
273 MOZ_ASSERT(kind() == LabelKind::Try);
274 kind_ = LabelKind::Catch;
275 polymorphicBase_ = false;
276 }
277
switchToCatchAll()278 void switchToCatchAll() {
279 MOZ_ASSERT(kind() == LabelKind::Try || kind() == LabelKind::Catch);
280 kind_ = LabelKind::CatchAll;
281 polymorphicBase_ = false;
282 }
283 #endif
284 };
285
286 template <typename Value>
287 class TypeAndValueT {
288 // Use a Pair to optimize away empty Value.
289 mozilla::CompactPair<StackType, Value> tv_;
290
291 public:
TypeAndValueT()292 TypeAndValueT() : tv_(StackType::bottom(), Value()) {}
TypeAndValueT(StackType type)293 explicit TypeAndValueT(StackType type) : tv_(type, Value()) {}
TypeAndValueT(ValType type)294 explicit TypeAndValueT(ValType type) : tv_(StackType(type), Value()) {}
TypeAndValueT(StackType type,Value value)295 TypeAndValueT(StackType type, Value value) : tv_(type, value) {}
TypeAndValueT(ValType type,Value value)296 TypeAndValueT(ValType type, Value value) : tv_(StackType(type), value) {}
type()297 StackType type() const { return tv_.first(); }
typeRef()298 StackType& typeRef() { return tv_.first(); }
value()299 Value value() const { return tv_.second(); }
setValue(Value value)300 void setValue(Value value) { tv_.second() = value; }
301 };
302
303 // An iterator over the bytes of a function body. It performs validation
304 // and unpacks the data into a usable form.
305 //
306 // The MOZ_STACK_CLASS attribute here is because of the use of DebugOnly.
307 // There's otherwise nothing inherent in this class which would require
308 // it to be used on the stack.
309 template <typename Policy>
310 class MOZ_STACK_CLASS OpIter : private Policy {
311 public:
312 using Value = typename Policy::Value;
313 using ValueVector = typename Policy::ValueVector;
314 using TypeAndValue = TypeAndValueT<Value>;
315 using TypeAndValueStack = Vector<TypeAndValue, 8, SystemAllocPolicy>;
316 using ControlItem = typename Policy::ControlItem;
317 using Control = ControlStackEntry<ControlItem>;
318 using ControlStack = Vector<Control, 8, SystemAllocPolicy>;
319
320 enum Kind {
321 Func,
322 InitExpr,
323 };
324
325 private:
326 Kind kind_;
327 Decoder& d_;
328 const ModuleEnvironment& env_;
329 TypeCache cache_;
330
331 TypeAndValueStack valueStack_;
332 TypeAndValueStack elseParamStack_;
333 ControlStack controlStack_;
334
335 #ifdef DEBUG
336 OpBytes op_;
337 #endif
338 size_t offsetOfLastReadOp_;
339
readFixedU8(uint8_t * out)340 [[nodiscard]] bool readFixedU8(uint8_t* out) { return d_.readFixedU8(out); }
readFixedU32(uint32_t * out)341 [[nodiscard]] bool readFixedU32(uint32_t* out) {
342 return d_.readFixedU32(out);
343 }
readVarS32(int32_t * out)344 [[nodiscard]] bool readVarS32(int32_t* out) { return d_.readVarS32(out); }
readVarU32(uint32_t * out)345 [[nodiscard]] bool readVarU32(uint32_t* out) { return d_.readVarU32(out); }
readVarS64(int64_t * out)346 [[nodiscard]] bool readVarS64(int64_t* out) { return d_.readVarS64(out); }
readVarU64(uint64_t * out)347 [[nodiscard]] bool readVarU64(uint64_t* out) { return d_.readVarU64(out); }
readFixedF32(float * out)348 [[nodiscard]] bool readFixedF32(float* out) { return d_.readFixedF32(out); }
readFixedF64(double * out)349 [[nodiscard]] bool readFixedF64(double* out) { return d_.readFixedF64(out); }
350
351 [[nodiscard]] bool readMemOrTableIndex(bool isMem, uint32_t* index);
352 [[nodiscard]] bool readLinearMemoryAddress(uint32_t byteSize,
353 LinearMemoryAddress<Value>* addr);
354 [[nodiscard]] bool readLinearMemoryAddressAligned(
355 uint32_t byteSize, LinearMemoryAddress<Value>* addr);
356 [[nodiscard]] bool readBlockType(BlockType* type);
357 [[nodiscard]] bool readGcTypeIndex(uint32_t* typeIndex);
358 [[nodiscard]] bool readStructTypeIndex(uint32_t* typeIndex);
359 [[nodiscard]] bool readArrayTypeIndex(uint32_t* typeIndex);
360 [[nodiscard]] bool readFieldIndex(uint32_t* fieldIndex,
361 const StructType& structType);
362
363 [[nodiscard]] bool popCallArgs(const ValTypeVector& expectedTypes,
364 ValueVector* values);
365
366 [[nodiscard]] bool failEmptyStack();
367 [[nodiscard]] bool popStackType(StackType* type, Value* value);
368 [[nodiscard]] bool popWithType(ValType expected, Value* value);
369 [[nodiscard]] bool popWithType(ResultType expected, ValueVector* values);
370 template <typename ValTypeSpanT>
371 [[nodiscard]] bool popWithTypes(ValTypeSpanT expected, ValueVector* values);
372 [[nodiscard]] bool popWithRefType(Value* value, StackType* type);
373 [[nodiscard]] bool popWithRttType(Value* rtt, uint32_t* rttTypeIndex,
374 uint32_t* rttDepth);
375 [[nodiscard]] bool popWithRttType(Value* rtt, uint32_t rttTypeIndex,
376 uint32_t* rttDepth);
377 [[nodiscard]] bool popThenPushType(ResultType expected, ValueVector* values);
378 [[nodiscard]] bool topWithTypeAndPush(ResultType expected,
379 ValueVector* values);
380
381 [[nodiscard]] bool pushControl(LabelKind kind, BlockType type);
382 [[nodiscard]] bool checkStackAtEndOfBlock(ResultType* type,
383 ValueVector* values);
384 [[nodiscard]] bool getControl(uint32_t relativeDepth, Control** controlEntry);
385 [[nodiscard]] bool checkBranchValueAndPush(uint32_t relativeDepth,
386 ResultType* type,
387 ValueVector* values);
388 [[nodiscard]] bool checkCastedBranchValueAndPush(uint32_t relativeDepth,
389 ValType castedFromType,
390 ValType castedToType,
391 ResultType* branchTargetType,
392 ValueVector* values);
393 [[nodiscard]] bool checkBrTableEntryAndPush(uint32_t* relativeDepth,
394 ResultType prevBranchType,
395 ResultType* branchType,
396 ValueVector* branchValues);
397
push(StackType t)398 [[nodiscard]] bool push(StackType t) { return valueStack_.emplaceBack(t); }
push(ValType t)399 [[nodiscard]] bool push(ValType t) { return valueStack_.emplaceBack(t); }
push(TypeAndValue tv)400 [[nodiscard]] bool push(TypeAndValue tv) { return valueStack_.append(tv); }
push(ResultType t)401 [[nodiscard]] bool push(ResultType t) {
402 for (size_t i = 0; i < t.length(); i++) {
403 if (!push(t[i])) {
404 return false;
405 }
406 }
407 return true;
408 }
infalliblePush(StackType t)409 void infalliblePush(StackType t) { valueStack_.infallibleEmplaceBack(t); }
infalliblePush(ValType t)410 void infalliblePush(ValType t) {
411 valueStack_.infallibleEmplaceBack(StackType(t));
412 }
infalliblePush(TypeAndValue tv)413 void infalliblePush(TypeAndValue tv) { valueStack_.infallibleAppend(tv); }
414
afterUnconditionalBranch()415 void afterUnconditionalBranch() {
416 valueStack_.shrinkTo(controlStack_.back().valueStackBase());
417 controlStack_.back().setPolymorphicBase();
418 }
419
420 inline bool checkIsSubtypeOf(ValType actual, ValType expected);
421 inline bool checkIsSubtypeOf(ResultType params, ResultType results);
422
423 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
424 inline bool checkIsSubtypeOf(uint32_t actualTypeIndex,
425 uint32_t expectedTypeIndex);
426 #endif
427
428 public:
429 #ifdef DEBUG
430 explicit OpIter(const ModuleEnvironment& env, Decoder& decoder,
431 Kind kind = OpIter::Func)
kind_(kind)432 : kind_(kind),
433 d_(decoder),
434 env_(env),
435 op_(OpBytes(Op::Limit)),
436 offsetOfLastReadOp_(0) {}
437 #else
438 explicit OpIter(const ModuleEnvironment& env, Decoder& decoder,
439 Kind kind = OpIter::Func)
440 : kind_(kind), d_(decoder), env_(env), offsetOfLastReadOp_(0) {}
441 #endif
442
443 // Return the decoding byte offset.
currentOffset()444 uint32_t currentOffset() const { return d_.currentOffset(); }
445
446 // Return the offset within the entire module of the last-read op.
lastOpcodeOffset()447 size_t lastOpcodeOffset() const {
448 return offsetOfLastReadOp_ ? offsetOfLastReadOp_ : d_.currentOffset();
449 }
450
451 // Return a BytecodeOffset describing where the current op should be reported
452 // to trap/call.
bytecodeOffset()453 BytecodeOffset bytecodeOffset() const {
454 return BytecodeOffset(lastOpcodeOffset());
455 }
456
457 // Test whether the iterator has reached the end of the buffer.
done()458 bool done() const { return d_.done(); }
459
460 // Return a pointer to the end of the buffer being decoded by this iterator.
end()461 const uint8_t* end() const { return d_.end(); }
462
463 // Report a general failure.
464 [[nodiscard]] bool fail(const char* msg) MOZ_COLD;
465
466 // Report a general failure with a context
467 [[nodiscard]] bool fail_ctx(const char* fmt, const char* context) MOZ_COLD;
468
469 // Report an unrecognized opcode.
470 [[nodiscard]] bool unrecognizedOpcode(const OpBytes* expr) MOZ_COLD;
471
472 // Return whether the innermost block has a polymorphic base of its stack.
473 // Ideally this accessor would be removed; consider using something else.
currentBlockHasPolymorphicBase()474 bool currentBlockHasPolymorphicBase() const {
475 return !controlStack_.empty() && controlStack_.back().polymorphicBase();
476 }
477
478 // ------------------------------------------------------------------------
479 // Decoding and validation interface.
480
481 // Initialization and termination
482
483 [[nodiscard]] bool startFunction(uint32_t funcIndex);
484 [[nodiscard]] bool endFunction(const uint8_t* bodyEnd);
485
486 [[nodiscard]] bool startInitExpr(ValType expected);
487 [[nodiscard]] bool endInitExpr();
488
489 // Value and reference types
490
491 [[nodiscard]] bool readValType(ValType* type);
492 [[nodiscard]] bool readHeapType(bool nullable, RefType* type);
493
494 // Instructions
495
496 [[nodiscard]] bool readOp(OpBytes* op);
497 [[nodiscard]] bool readReturn(ValueVector* values);
498 [[nodiscard]] bool readBlock(ResultType* paramType);
499 [[nodiscard]] bool readLoop(ResultType* paramType);
500 [[nodiscard]] bool readIf(ResultType* paramType, Value* condition);
501 [[nodiscard]] bool readElse(ResultType* paramType, ResultType* resultType,
502 ValueVector* thenResults);
503 [[nodiscard]] bool readEnd(LabelKind* kind, ResultType* type,
504 ValueVector* results,
505 ValueVector* resultsForEmptyElse);
506 void popEnd();
507 [[nodiscard]] bool readBr(uint32_t* relativeDepth, ResultType* type,
508 ValueVector* values);
509 [[nodiscard]] bool readBrIf(uint32_t* relativeDepth, ResultType* type,
510 ValueVector* values, Value* condition);
511 [[nodiscard]] bool readBrTable(Uint32Vector* depths, uint32_t* defaultDepth,
512 ResultType* defaultBranchType,
513 ValueVector* branchValues, Value* index);
514 #ifdef ENABLE_WASM_EXCEPTIONS
515 [[nodiscard]] bool readTry(ResultType* type);
516 [[nodiscard]] bool readCatch(LabelKind* kind, uint32_t* tagIndex,
517 ResultType* paramType, ResultType* resultType,
518 ValueVector* tryResults);
519 [[nodiscard]] bool readCatchAll(LabelKind* kind, ResultType* paramType,
520 ResultType* resultType,
521 ValueVector* tryResults);
522 [[nodiscard]] bool readDelegate(uint32_t* relativeDepth,
523 ResultType* resultType,
524 ValueVector* tryResults);
525 void popDelegate();
526 [[nodiscard]] bool readThrow(uint32_t* tagIndex, ValueVector* argValues);
527 [[nodiscard]] bool readRethrow(uint32_t* relativeDepth);
528 #endif
529 [[nodiscard]] bool readUnreachable();
530 [[nodiscard]] bool readDrop();
531 [[nodiscard]] bool readUnary(ValType operandType, Value* input);
532 [[nodiscard]] bool readConversion(ValType operandType, ValType resultType,
533 Value* input);
534 [[nodiscard]] bool readBinary(ValType operandType, Value* lhs, Value* rhs);
535 [[nodiscard]] bool readComparison(ValType operandType, Value* lhs,
536 Value* rhs);
537 [[nodiscard]] bool readTernary(ValType operandType, Value* v0, Value* v1,
538 Value* v2);
539 [[nodiscard]] bool readLoad(ValType resultType, uint32_t byteSize,
540 LinearMemoryAddress<Value>* addr);
541 [[nodiscard]] bool readStore(ValType resultType, uint32_t byteSize,
542 LinearMemoryAddress<Value>* addr, Value* value);
543 [[nodiscard]] bool readTeeStore(ValType resultType, uint32_t byteSize,
544 LinearMemoryAddress<Value>* addr,
545 Value* value);
546 [[nodiscard]] bool readNop();
547 [[nodiscard]] bool readMemorySize();
548 [[nodiscard]] bool readMemoryGrow(Value* input);
549 [[nodiscard]] bool readSelect(bool typed, StackType* type, Value* trueValue,
550 Value* falseValue, Value* condition);
551 [[nodiscard]] bool readGetLocal(const ValTypeVector& locals, uint32_t* id);
552 [[nodiscard]] bool readSetLocal(const ValTypeVector& locals, uint32_t* id,
553 Value* value);
554 [[nodiscard]] bool readTeeLocal(const ValTypeVector& locals, uint32_t* id,
555 Value* value);
556 [[nodiscard]] bool readGetGlobal(uint32_t* id);
557 [[nodiscard]] bool readSetGlobal(uint32_t* id, Value* value);
558 [[nodiscard]] bool readTeeGlobal(uint32_t* id, Value* value);
559 [[nodiscard]] bool readI32Const(int32_t* i32);
560 [[nodiscard]] bool readI64Const(int64_t* i64);
561 [[nodiscard]] bool readF32Const(float* f32);
562 [[nodiscard]] bool readF64Const(double* f64);
563 [[nodiscard]] bool readRefFunc(uint32_t* funcIndex);
564 [[nodiscard]] bool readRefNull(RefType* type);
565 [[nodiscard]] bool readRefIsNull(Value* input);
566 [[nodiscard]] bool readRefAsNonNull(Value* input);
567 [[nodiscard]] bool readBrOnNull(uint32_t* relativeDepth, ResultType* type,
568 ValueVector* values, Value* condition);
569 [[nodiscard]] bool readCall(uint32_t* funcTypeIndex, ValueVector* argValues);
570 [[nodiscard]] bool readCallIndirect(uint32_t* funcTypeIndex,
571 uint32_t* tableIndex, Value* callee,
572 ValueVector* argValues);
573 [[nodiscard]] bool readOldCallDirect(uint32_t numFuncImports,
574 uint32_t* funcTypeIndex,
575 ValueVector* argValues);
576 [[nodiscard]] bool readOldCallIndirect(uint32_t* funcTypeIndex, Value* callee,
577 ValueVector* argValues);
578 [[nodiscard]] bool readWake(LinearMemoryAddress<Value>* addr, Value* count);
579 [[nodiscard]] bool readWait(LinearMemoryAddress<Value>* addr,
580 ValType valueType, uint32_t byteSize,
581 Value* value, Value* timeout);
582 [[nodiscard]] bool readFence();
583 [[nodiscard]] bool readAtomicLoad(LinearMemoryAddress<Value>* addr,
584 ValType resultType, uint32_t byteSize);
585 [[nodiscard]] bool readAtomicStore(LinearMemoryAddress<Value>* addr,
586 ValType resultType, uint32_t byteSize,
587 Value* value);
588 [[nodiscard]] bool readAtomicRMW(LinearMemoryAddress<Value>* addr,
589 ValType resultType, uint32_t byteSize,
590 Value* value);
591 [[nodiscard]] bool readAtomicCmpXchg(LinearMemoryAddress<Value>* addr,
592 ValType resultType, uint32_t byteSize,
593 Value* oldValue, Value* newValue);
594 [[nodiscard]] bool readMemOrTableCopy(bool isMem,
595 uint32_t* dstMemOrTableIndex,
596 Value* dst,
597 uint32_t* srcMemOrTableIndex,
598 Value* src, Value* len);
599 [[nodiscard]] bool readDataOrElemDrop(bool isData, uint32_t* segIndex);
600 [[nodiscard]] bool readMemFill(Value* start, Value* val, Value* len);
601 [[nodiscard]] bool readMemOrTableInit(bool isMem, uint32_t* segIndex,
602 uint32_t* dstTableIndex, Value* dst,
603 Value* src, Value* len);
604 [[nodiscard]] bool readTableFill(uint32_t* tableIndex, Value* start,
605 Value* val, Value* len);
606 [[nodiscard]] bool readTableGet(uint32_t* tableIndex, Value* index);
607 [[nodiscard]] bool readTableGrow(uint32_t* tableIndex, Value* initValue,
608 Value* delta);
609 [[nodiscard]] bool readTableSet(uint32_t* tableIndex, Value* index,
610 Value* value);
611
612 [[nodiscard]] bool readTableSize(uint32_t* tableIndex);
613
614 #ifdef ENABLE_WASM_GC
615 [[nodiscard]] bool readStructNewWithRtt(uint32_t* typeIndex, Value* rtt,
616 ValueVector* argValues);
617 [[nodiscard]] bool readStructNewDefaultWithRtt(uint32_t* typeIndex,
618 Value* rtt);
619 [[nodiscard]] bool readStructGet(uint32_t* typeIndex, uint32_t* fieldIndex,
620 FieldExtension extension, Value* ptr);
621 [[nodiscard]] bool readStructSet(uint32_t* typeIndex, uint32_t* fieldIndex,
622 Value* ptr, Value* val);
623 [[nodiscard]] bool readArrayNewWithRtt(uint32_t* typeIndex, Value* rtt,
624 Value* length, Value* argValue);
625 [[nodiscard]] bool readArrayNewDefaultWithRtt(uint32_t* typeIndex, Value* rtt,
626 Value* length);
627 [[nodiscard]] bool readArrayGet(uint32_t* typeIndex, FieldExtension extension,
628 Value* index, Value* ptr);
629 [[nodiscard]] bool readArraySet(uint32_t* typeIndex, Value* val, Value* index,
630 Value* ptr);
631 [[nodiscard]] bool readArrayLen(uint32_t* typeIndex, Value* ptr);
632 [[nodiscard]] bool readRttCanon(ValType* rttType);
633 [[nodiscard]] bool readRttSub(Value* parentRtt, uint32_t* rttSubTypeIndex);
634 [[nodiscard]] bool readRefTest(Value* rtt, uint32_t* rttTypeIndex,
635 uint32_t* rttDepth, Value* ref);
636 [[nodiscard]] bool readRefCast(Value* rtt, uint32_t* rttTypeIndex,
637 uint32_t* rttDepth, Value* ref);
638 [[nodiscard]] bool readBrOnCast(uint32_t* relativeDepth, Value* rtt,
639 uint32_t* rttTypeIndex, uint32_t* rttDepth,
640 ResultType* branchTargetType,
641 ValueVector* values);
642 #endif
643
644 #ifdef ENABLE_WASM_SIMD
645 [[nodiscard]] bool readLaneIndex(uint32_t inputLanes, uint32_t* laneIndex);
646 [[nodiscard]] bool readExtractLane(ValType resultType, uint32_t inputLanes,
647 uint32_t* laneIndex, Value* input);
648 [[nodiscard]] bool readReplaceLane(ValType operandType, uint32_t inputLanes,
649 uint32_t* laneIndex, Value* baseValue,
650 Value* operand);
651 [[nodiscard]] bool readVectorShift(Value* baseValue, Value* shift);
652 [[nodiscard]] bool readVectorShuffle(Value* v1, Value* v2, V128* selectMask);
653 [[nodiscard]] bool readV128Const(V128* value);
654 [[nodiscard]] bool readLoadSplat(uint32_t byteSize,
655 LinearMemoryAddress<Value>* addr);
656 [[nodiscard]] bool readLoadExtend(LinearMemoryAddress<Value>* addr);
657 [[nodiscard]] bool readLoadLane(uint32_t byteSize,
658 LinearMemoryAddress<Value>* addr,
659 uint32_t* laneIndex, Value* input);
660 [[nodiscard]] bool readStoreLane(uint32_t byteSize,
661 LinearMemoryAddress<Value>* addr,
662 uint32_t* laneIndex, Value* input);
663 #endif
664
665 [[nodiscard]] bool readIntrinsic(const Intrinsic** intrinsic,
666 ValueVector* params);
667
668 // At a location where readOp is allowed, peek at the next opcode
669 // without consuming it or updating any internal state.
670 // Never fails: returns uint16_t(Op::Limit) in op->b0 if it can't read.
671 void peekOp(OpBytes* op);
672
673 // ------------------------------------------------------------------------
674 // Stack management.
675
676 // Set the top N result values.
setResults(size_t count,const ValueVector & values)677 void setResults(size_t count, const ValueVector& values) {
678 MOZ_ASSERT(valueStack_.length() >= count);
679 size_t base = valueStack_.length() - count;
680 for (size_t i = 0; i < count; i++) {
681 valueStack_[base + i].setValue(values[i]);
682 }
683 }
684
getResults(size_t count,ValueVector * values)685 bool getResults(size_t count, ValueVector* values) {
686 MOZ_ASSERT(valueStack_.length() >= count);
687 if (!values->resize(count)) {
688 return false;
689 }
690 size_t base = valueStack_.length() - count;
691 for (size_t i = 0; i < count; i++) {
692 (*values)[i] = valueStack_[base + i].value();
693 }
694 return true;
695 }
696
697 // Set the result value of the current top-of-value-stack expression.
setResult(Value value)698 void setResult(Value value) { valueStack_.back().setValue(value); }
699
700 // Return the result value of the current top-of-value-stack expression.
getResult()701 Value getResult() { return valueStack_.back().value(); }
702
703 // Return a reference to the top of the control stack.
controlItem()704 ControlItem& controlItem() { return controlStack_.back().controlItem(); }
705
706 // Return a reference to an element in the control stack.
controlItem(uint32_t relativeDepth)707 ControlItem& controlItem(uint32_t relativeDepth) {
708 return controlStack_[controlStack_.length() - 1 - relativeDepth]
709 .controlItem();
710 }
711
712 // Return the LabelKind of an element in the control stack.
controlKind(uint32_t relativeDepth)713 LabelKind controlKind(uint32_t relativeDepth) {
714 return controlStack_[controlStack_.length() - 1 - relativeDepth].kind();
715 }
716
717 // Return a reference to the outermost element on the control stack.
controlOutermost()718 ControlItem& controlOutermost() { return controlStack_[0].controlItem(); }
719
720 // Test whether the control-stack is empty, meaning we've consumed the final
721 // end of the function body.
controlStackEmpty()722 bool controlStackEmpty() const { return controlStack_.empty(); }
723
724 // Return the depth of the control stack.
controlStackDepth()725 size_t controlStackDepth() const { return controlStack_.length(); }
726
727 // Find the innermost control item of a specific kind, starting to search from
728 // a certain relative depth, and returning true if such innermost control item
729 // is found. The relative depth of the found item is returned via a parameter.
controlFindInnermostFrom(LabelKind kind,uint32_t fromRelativeDepth,uint32_t * foundRelativeDepth)730 bool controlFindInnermostFrom(LabelKind kind, uint32_t fromRelativeDepth,
731 uint32_t* foundRelativeDepth) {
732 int32_t fromAbsoluteDepth = controlStack_.length() - fromRelativeDepth - 1;
733 for (int32_t i = fromAbsoluteDepth; i >= 0; i--) {
734 if (controlStack_[i].kind() == kind) {
735 *foundRelativeDepth = controlStack_.length() - 1 - i;
736 return true;
737 }
738 }
739 return false;
740 }
741
controlFindInnermost(LabelKind kind,uint32_t * foundRelativeDepth)742 bool controlFindInnermost(LabelKind kind, uint32_t* foundRelativeDepth) {
743 return controlFindInnermostFrom(kind, 0, foundRelativeDepth);
744 }
745 };
746
747 template <typename Policy>
checkIsSubtypeOf(ValType actual,ValType expected)748 inline bool OpIter<Policy>::checkIsSubtypeOf(ValType actual, ValType expected) {
749 return CheckIsSubtypeOf(d_, env_, lastOpcodeOffset(), actual, expected,
750 &cache_);
751 }
752
753 template <typename Policy>
checkIsSubtypeOf(ResultType params,ResultType results)754 inline bool OpIter<Policy>::checkIsSubtypeOf(ResultType params,
755 ResultType results) {
756 if (params.length() != results.length()) {
757 UniqueChars error(
758 JS_smprintf("type mismatch: expected %zu values, got %zu values",
759 results.length(), params.length()));
760 if (!error) {
761 return false;
762 }
763 return fail(error.get());
764 }
765 for (uint32_t i = 0; i < params.length(); i++) {
766 ValType param = params[i];
767 ValType result = results[i];
768 if (!checkIsSubtypeOf(param, result)) {
769 return false;
770 }
771 }
772 return true;
773 }
774
775 #ifdef ENABLE_WASM_FUNCTION_REFERENCES
776 template <typename Policy>
checkIsSubtypeOf(uint32_t actualTypeIndex,uint32_t expectedTypeIndex)777 inline bool OpIter<Policy>::checkIsSubtypeOf(uint32_t actualTypeIndex,
778 uint32_t expectedTypeIndex) {
779 return CheckIsSubtypeOf(
780 d_, env_, lastOpcodeOffset(),
781 ValType(RefType::fromTypeIndex(actualTypeIndex, true)),
782 ValType(RefType::fromTypeIndex(expectedTypeIndex, true)), &cache_);
783 }
784 #endif
785
786 template <typename Policy>
unrecognizedOpcode(const OpBytes * expr)787 inline bool OpIter<Policy>::unrecognizedOpcode(const OpBytes* expr) {
788 UniqueChars error(JS_smprintf("unrecognized opcode: %x %x", expr->b0,
789 IsPrefixByte(expr->b0) ? expr->b1 : 0));
790 if (!error) {
791 return false;
792 }
793
794 return fail(error.get());
795 }
796
797 template <typename Policy>
fail(const char * msg)798 inline bool OpIter<Policy>::fail(const char* msg) {
799 return d_.fail(lastOpcodeOffset(), msg);
800 }
801
802 template <typename Policy>
fail_ctx(const char * fmt,const char * context)803 inline bool OpIter<Policy>::fail_ctx(const char* fmt, const char* context) {
804 UniqueChars error(JS_smprintf(fmt, context));
805 if (!error) {
806 return false;
807 }
808 return fail(error.get());
809 }
810
811 template <typename Policy>
failEmptyStack()812 inline bool OpIter<Policy>::failEmptyStack() {
813 return valueStack_.empty() ? fail("popping value from empty stack")
814 : fail("popping value from outside block");
815 }
816
817 // This function pops exactly one value from the stack, yielding Bottom types in
818 // various cases and therefore making it the caller's responsibility to do the
819 // right thing for StackType::Bottom. Prefer (pop|top)WithType. This is an
820 // optimization for the super-common case where the caller is statically
821 // expecting the resulttype `[valtype]`.
822 template <typename Policy>
popStackType(StackType * type,Value * value)823 inline bool OpIter<Policy>::popStackType(StackType* type, Value* value) {
824 Control& block = controlStack_.back();
825
826 MOZ_ASSERT(valueStack_.length() >= block.valueStackBase());
827 if (MOZ_UNLIKELY(valueStack_.length() == block.valueStackBase())) {
828 // If the base of this block's stack is polymorphic, then we can pop a
829 // dummy value of the bottom type; it won't be used since we're in
830 // unreachable code.
831 if (block.polymorphicBase()) {
832 *type = StackType::bottom();
833 *value = Value();
834
835 // Maintain the invariant that, after a pop, there is always memory
836 // reserved to push a value infallibly.
837 return valueStack_.reserve(valueStack_.length() + 1);
838 }
839
840 return failEmptyStack();
841 }
842
843 TypeAndValue& tv = valueStack_.back();
844 *type = tv.type();
845 *value = tv.value();
846 valueStack_.popBack();
847 return true;
848 }
849
850 // This function pops exactly one value from the stack, checking that it has the
851 // expected type which can either be a specific value type or a type variable.
852 template <typename Policy>
popWithType(ValType expectedType,Value * value)853 inline bool OpIter<Policy>::popWithType(ValType expectedType, Value* value) {
854 StackType stackType;
855 if (!popStackType(&stackType, value)) {
856 return false;
857 }
858
859 return stackType.isBottom() ||
860 checkIsSubtypeOf(stackType.valType(), expectedType);
861 }
862
863 template <typename Policy>
popWithType(ResultType expected,ValueVector * values)864 inline bool OpIter<Policy>::popWithType(ResultType expected,
865 ValueVector* values) {
866 return popWithTypes(expected, values);
867 }
868
869 // Pops each of the given expected types (in reverse, because it's a stack).
870 template <typename Policy>
871 template <typename ValTypeSpanT>
popWithTypes(ValTypeSpanT expected,ValueVector * values)872 inline bool OpIter<Policy>::popWithTypes(ValTypeSpanT expected,
873 ValueVector* values) {
874 size_t expectedLength = expected.size();
875 if (!values->resize(expectedLength)) {
876 return false;
877 }
878 for (size_t i = 0; i < expectedLength; i++) {
879 size_t reverseIndex = expectedLength - i - 1;
880 ValType expectedType = expected[reverseIndex];
881 Value* value = &(*values)[reverseIndex];
882 if (!popWithType(expectedType, value)) {
883 return false;
884 }
885 }
886 return true;
887 }
888
889 // This function pops exactly one value from the stack, checking that it is a
890 // reference type.
891 template <typename Policy>
popWithRefType(Value * value,StackType * type)892 inline bool OpIter<Policy>::popWithRefType(Value* value, StackType* type) {
893 if (!popStackType(type, value)) {
894 return false;
895 }
896
897 if (type->isBottom() || type->valType().isRefType()) {
898 return true;
899 }
900
901 UniqueChars actualText = ToString(type->valType());
902 if (!actualText) {
903 return false;
904 }
905
906 UniqueChars error(JS_smprintf(
907 "type mismatch: expression has type %s but expected a reference type",
908 actualText.get()));
909 if (!error) {
910 return false;
911 }
912
913 return fail(error.get());
914 }
915
916 // This function pops exactly one value from the stack, checking that it is an
917 // rtt type with any type index or depth value.
918 template <typename Policy>
popWithRttType(Value * rtt,uint32_t * rttTypeIndex,uint32_t * rttDepth)919 inline bool OpIter<Policy>::popWithRttType(Value* rtt, uint32_t* rttTypeIndex,
920 uint32_t* rttDepth) {
921 StackType type;
922 if (!popStackType(&type, rtt)) {
923 return false;
924 }
925
926 if (type.isBottom()) {
927 return fail("gc instruction temporarily not allowed in dead code");
928 }
929
930 if (type.valType().isRtt()) {
931 *rttTypeIndex = type.valType().typeIndex();
932 *rttDepth = type.valType().rttDepth();
933 return true;
934 }
935
936 UniqueChars actualText = ToString(type.valType());
937 if (!actualText) {
938 return false;
939 }
940
941 UniqueChars error(
942 JS_smprintf("type mismatch: expression has type %s but expected (rtt _)",
943 actualText.get()));
944 if (!error) {
945 return false;
946 }
947
948 return fail(error.get());
949 }
950
951 // This function is an optimization of the sequence:
952 // popWithType(ResultType, tmp)
953 // push(ResultType, tmp)
954 template <typename Policy>
popThenPushType(ResultType expected,ValueVector * values)955 inline bool OpIter<Policy>::popThenPushType(ResultType expected,
956 ValueVector* values) {
957 if (expected.empty()) {
958 return true;
959 }
960
961 Control& block = controlStack_.back();
962
963 size_t expectedLength = expected.length();
964 if (values && !values->resize(expectedLength)) {
965 return false;
966 }
967
968 for (size_t i = 0; i != expectedLength; i++) {
969 // We're iterating as-if we were popping each expected/actual type one by
970 // one, which means iterating the array of expected results backwards.
971 // The "current" value stack length refers to what the value stack length
972 // would have been if we were popping it.
973 size_t reverseIndex = expectedLength - i - 1;
974 ValType expectedType = expected[reverseIndex];
975 auto collectValue = [&](const Value& v) {
976 if (values) {
977 (*values)[reverseIndex] = v;
978 }
979 };
980
981 size_t currentValueStackLength = valueStack_.length() - i;
982
983 MOZ_ASSERT(currentValueStackLength >= block.valueStackBase());
984 if (currentValueStackLength == block.valueStackBase()) {
985 if (!block.polymorphicBase()) {
986 return failEmptyStack();
987 }
988
989 // If the base of this block's stack is polymorphic, then we can just
990 // pull out as many fake values as we need to validate; they won't be used
991 // since we're in unreachable code. We must however push these types on
992 // the operand stack since they are now fixed by this constraint.
993 if (!valueStack_.insert(valueStack_.begin() + currentValueStackLength,
994 TypeAndValue(expectedType))) {
995 return false;
996 }
997
998 collectValue(Value());
999 } else {
1000 TypeAndValue& observed = valueStack_[currentValueStackLength - 1];
1001
1002 if (observed.type().isBottom()) {
1003 observed.typeRef() = StackType(expectedType);
1004 collectValue(Value());
1005 } else {
1006 if (!checkIsSubtypeOf(observed.type().valType(), expectedType)) {
1007 return false;
1008 }
1009
1010 collectValue(observed.value());
1011 }
1012 }
1013 }
1014 return true;
1015 }
1016
1017 template <typename Policy>
topWithTypeAndPush(ResultType expected,ValueVector * values)1018 inline bool OpIter<Policy>::topWithTypeAndPush(ResultType expected,
1019 ValueVector* values) {
1020 if (expected.empty()) {
1021 return true;
1022 }
1023
1024 Control& block = controlStack_.back();
1025
1026 size_t expectedLength = expected.length();
1027 if (values && !values->resize(expectedLength)) {
1028 return false;
1029 }
1030
1031 for (size_t i = 0; i != expectedLength; i++) {
1032 // We're iterating as-if we were popping each expected/actual type one by
1033 // one, which means iterating the array of expected results backwards.
1034 // The "current" value stack length refers to what the value stack length
1035 // would have been if we were popping it.
1036 size_t reverseIndex = expectedLength - i - 1;
1037 ValType expectedType = expected[reverseIndex];
1038 auto collectValue = [&](const Value& v) {
1039 if (values) {
1040 (*values)[reverseIndex] = v;
1041 }
1042 };
1043
1044 size_t currentValueStackLength = valueStack_.length() - i;
1045
1046 MOZ_ASSERT(currentValueStackLength >= block.valueStackBase());
1047 if (currentValueStackLength == block.valueStackBase()) {
1048 if (!block.polymorphicBase()) {
1049 return failEmptyStack();
1050 }
1051
1052 // If the base of this block's stack is polymorphic, then we can just
1053 // pull out as many fake values as we need to validate; they won't be used
1054 // since we're in unreachable code.
1055 if (!valueStack_.insert(valueStack_.begin() + currentValueStackLength,
1056 TypeAndValue())) {
1057 return false;
1058 }
1059
1060 collectValue(Value());
1061 } else {
1062 TypeAndValue& observed = valueStack_[currentValueStackLength - 1];
1063
1064 if (observed.type().isBottom()) {
1065 collectValue(Value());
1066 } else {
1067 if (!checkIsSubtypeOf(observed.type().valType(), expectedType)) {
1068 return false;
1069 }
1070
1071 collectValue(observed.value());
1072 }
1073 }
1074 }
1075 return true;
1076 }
1077
1078 template <typename Policy>
pushControl(LabelKind kind,BlockType type)1079 inline bool OpIter<Policy>::pushControl(LabelKind kind, BlockType type) {
1080 ResultType paramType = type.params();
1081
1082 ValueVector values;
1083 if (!popThenPushType(paramType, &values)) {
1084 return false;
1085 }
1086 MOZ_ASSERT(valueStack_.length() >= paramType.length());
1087 uint32_t valueStackBase = valueStack_.length() - paramType.length();
1088 return controlStack_.emplaceBack(kind, type, valueStackBase);
1089 }
1090
1091 template <typename Policy>
checkStackAtEndOfBlock(ResultType * expectedType,ValueVector * values)1092 inline bool OpIter<Policy>::checkStackAtEndOfBlock(ResultType* expectedType,
1093 ValueVector* values) {
1094 Control& block = controlStack_.back();
1095 *expectedType = block.type().results();
1096
1097 MOZ_ASSERT(valueStack_.length() >= block.valueStackBase());
1098 if (expectedType->length() < valueStack_.length() - block.valueStackBase()) {
1099 return fail("unused values not explicitly dropped by end of block");
1100 }
1101
1102 return popThenPushType(*expectedType, values);
1103 }
1104
1105 template <typename Policy>
getControl(uint32_t relativeDepth,Control ** controlEntry)1106 inline bool OpIter<Policy>::getControl(uint32_t relativeDepth,
1107 Control** controlEntry) {
1108 if (relativeDepth >= controlStack_.length()) {
1109 return fail("branch depth exceeds current nesting level");
1110 }
1111
1112 *controlEntry = &controlStack_[controlStack_.length() - 1 - relativeDepth];
1113 return true;
1114 }
1115
1116 template <typename Policy>
readBlockType(BlockType * type)1117 inline bool OpIter<Policy>::readBlockType(BlockType* type) {
1118 uint8_t nextByte;
1119 if (!d_.peekByte(&nextByte)) {
1120 return fail("unable to read block type");
1121 }
1122
1123 if (nextByte == uint8_t(TypeCode::BlockVoid)) {
1124 d_.uncheckedReadFixedU8();
1125 *type = BlockType::VoidToVoid();
1126 return true;
1127 }
1128
1129 if ((nextByte & SLEB128SignMask) == SLEB128SignBit) {
1130 ValType v;
1131 if (!readValType(&v)) {
1132 return false;
1133 }
1134 *type = BlockType::VoidToSingle(v);
1135 return true;
1136 }
1137
1138 int32_t x;
1139 if (!d_.readVarS32(&x) || x < 0 || uint32_t(x) >= env_.types->length()) {
1140 return fail("invalid block type type index");
1141 }
1142
1143 if (!env_.types->isFuncType(x)) {
1144 return fail("block type type index must be func type");
1145 }
1146
1147 *type = BlockType::Func(env_.types->funcType(x));
1148
1149 return true;
1150 }
1151
1152 template <typename Policy>
readOp(OpBytes * op)1153 inline bool OpIter<Policy>::readOp(OpBytes* op) {
1154 MOZ_ASSERT(!controlStack_.empty());
1155
1156 offsetOfLastReadOp_ = d_.currentOffset();
1157
1158 if (MOZ_UNLIKELY(!d_.readOp(op))) {
1159 return fail("unable to read opcode");
1160 }
1161
1162 #ifdef DEBUG
1163 op_ = *op;
1164 #endif
1165
1166 return true;
1167 }
1168
1169 template <typename Policy>
peekOp(OpBytes * op)1170 inline void OpIter<Policy>::peekOp(OpBytes* op) {
1171 const uint8_t* pos = d_.currentPosition();
1172
1173 if (MOZ_UNLIKELY(!d_.readOp(op))) {
1174 op->b0 = uint16_t(Op::Limit);
1175 }
1176
1177 d_.rollbackPosition(pos);
1178 }
1179
1180 template <typename Policy>
startFunction(uint32_t funcIndex)1181 inline bool OpIter<Policy>::startFunction(uint32_t funcIndex) {
1182 MOZ_ASSERT(kind_ == OpIter::Func);
1183 MOZ_ASSERT(elseParamStack_.empty());
1184 MOZ_ASSERT(valueStack_.empty());
1185 MOZ_ASSERT(controlStack_.empty());
1186 MOZ_ASSERT(op_.b0 == uint16_t(Op::Limit));
1187 BlockType type = BlockType::FuncResults(*env_.funcs[funcIndex].type);
1188 return pushControl(LabelKind::Body, type);
1189 }
1190
1191 template <typename Policy>
endFunction(const uint8_t * bodyEnd)1192 inline bool OpIter<Policy>::endFunction(const uint8_t* bodyEnd) {
1193 if (d_.currentPosition() != bodyEnd) {
1194 return fail("function body length mismatch");
1195 }
1196
1197 if (!controlStack_.empty()) {
1198 return fail("unbalanced function body control flow");
1199 }
1200 MOZ_ASSERT(elseParamStack_.empty());
1201
1202 #ifdef DEBUG
1203 op_ = OpBytes(Op::Limit);
1204 #endif
1205 valueStack_.clear();
1206 return true;
1207 }
1208
1209 template <typename Policy>
startInitExpr(ValType expected)1210 inline bool OpIter<Policy>::startInitExpr(ValType expected) {
1211 MOZ_ASSERT(kind_ == OpIter::InitExpr);
1212 MOZ_ASSERT(elseParamStack_.empty());
1213 MOZ_ASSERT(valueStack_.empty());
1214 MOZ_ASSERT(controlStack_.empty());
1215 MOZ_ASSERT(op_.b0 == uint16_t(Op::Limit));
1216 BlockType type = BlockType::VoidToSingle(expected);
1217 return pushControl(LabelKind::Body, type);
1218 }
1219
1220 template <typename Policy>
endInitExpr()1221 inline bool OpIter<Policy>::endInitExpr() {
1222 MOZ_ASSERT(controlStack_.empty());
1223 MOZ_ASSERT(elseParamStack_.empty());
1224
1225 #ifdef DEBUG
1226 op_ = OpBytes(Op::Limit);
1227 #endif
1228 valueStack_.clear();
1229 return true;
1230 }
1231
1232 template <typename Policy>
readValType(ValType * type)1233 inline bool OpIter<Policy>::readValType(ValType* type) {
1234 return d_.readValType(*env_.types, env_.features, type);
1235 }
1236
1237 template <typename Policy>
readHeapType(bool nullable,RefType * type)1238 inline bool OpIter<Policy>::readHeapType(bool nullable, RefType* type) {
1239 return d_.readHeapType(*env_.types, env_.features, nullable, type);
1240 }
1241
1242 template <typename Policy>
readReturn(ValueVector * values)1243 inline bool OpIter<Policy>::readReturn(ValueVector* values) {
1244 MOZ_ASSERT(Classify(op_) == OpKind::Return);
1245
1246 Control& body = controlStack_[0];
1247 MOZ_ASSERT(body.kind() == LabelKind::Body);
1248
1249 if (!popWithType(body.resultType(), values)) {
1250 return false;
1251 }
1252
1253 afterUnconditionalBranch();
1254 return true;
1255 }
1256
1257 template <typename Policy>
readBlock(ResultType * paramType)1258 inline bool OpIter<Policy>::readBlock(ResultType* paramType) {
1259 MOZ_ASSERT(Classify(op_) == OpKind::Block);
1260
1261 BlockType type;
1262 if (!readBlockType(&type)) {
1263 return false;
1264 }
1265
1266 *paramType = type.params();
1267 return pushControl(LabelKind::Block, type);
1268 }
1269
1270 template <typename Policy>
readLoop(ResultType * paramType)1271 inline bool OpIter<Policy>::readLoop(ResultType* paramType) {
1272 MOZ_ASSERT(Classify(op_) == OpKind::Loop);
1273
1274 BlockType type;
1275 if (!readBlockType(&type)) {
1276 return false;
1277 }
1278
1279 *paramType = type.params();
1280 return pushControl(LabelKind::Loop, type);
1281 }
1282
1283 template <typename Policy>
readIf(ResultType * paramType,Value * condition)1284 inline bool OpIter<Policy>::readIf(ResultType* paramType, Value* condition) {
1285 MOZ_ASSERT(Classify(op_) == OpKind::If);
1286
1287 BlockType type;
1288 if (!readBlockType(&type)) {
1289 return false;
1290 }
1291
1292 if (!popWithType(ValType::I32, condition)) {
1293 return false;
1294 }
1295
1296 if (!pushControl(LabelKind::Then, type)) {
1297 return false;
1298 }
1299
1300 *paramType = type.params();
1301 size_t paramsLength = type.params().length();
1302 return elseParamStack_.append(valueStack_.end() - paramsLength, paramsLength);
1303 }
1304
1305 template <typename Policy>
readElse(ResultType * paramType,ResultType * resultType,ValueVector * thenResults)1306 inline bool OpIter<Policy>::readElse(ResultType* paramType,
1307 ResultType* resultType,
1308 ValueVector* thenResults) {
1309 MOZ_ASSERT(Classify(op_) == OpKind::Else);
1310
1311 Control& block = controlStack_.back();
1312 if (block.kind() != LabelKind::Then) {
1313 return fail("else can only be used within an if");
1314 }
1315
1316 *paramType = block.type().params();
1317 if (!checkStackAtEndOfBlock(resultType, thenResults)) {
1318 return false;
1319 }
1320
1321 valueStack_.shrinkTo(block.valueStackBase());
1322
1323 size_t nparams = block.type().params().length();
1324 MOZ_ASSERT(elseParamStack_.length() >= nparams);
1325 valueStack_.infallibleAppend(elseParamStack_.end() - nparams, nparams);
1326 elseParamStack_.shrinkBy(nparams);
1327
1328 block.switchToElse();
1329 return true;
1330 }
1331
1332 template <typename Policy>
readEnd(LabelKind * kind,ResultType * type,ValueVector * results,ValueVector * resultsForEmptyElse)1333 inline bool OpIter<Policy>::readEnd(LabelKind* kind, ResultType* type,
1334 ValueVector* results,
1335 ValueVector* resultsForEmptyElse) {
1336 MOZ_ASSERT(Classify(op_) == OpKind::End);
1337
1338 Control& block = controlStack_.back();
1339
1340 if (!checkStackAtEndOfBlock(type, results)) {
1341 return false;
1342 }
1343
1344 if (block.kind() == LabelKind::Then) {
1345 ResultType params = block.type().params();
1346 // If an `if` block ends with `end` instead of `else`, then the `else` block
1347 // implicitly passes the `if` parameters as the `else` results. In that
1348 // case, assert that the `if`'s param type matches the result type.
1349 if (params != block.type().results()) {
1350 return fail("if without else with a result value");
1351 }
1352
1353 size_t nparams = params.length();
1354 MOZ_ASSERT(elseParamStack_.length() >= nparams);
1355 if (!resultsForEmptyElse->resize(nparams)) {
1356 return false;
1357 }
1358 const TypeAndValue* elseParams = elseParamStack_.end() - nparams;
1359 for (size_t i = 0; i < nparams; i++) {
1360 (*resultsForEmptyElse)[i] = elseParams[i].value();
1361 }
1362 elseParamStack_.shrinkBy(nparams);
1363 }
1364
1365 *kind = block.kind();
1366 return true;
1367 }
1368
1369 template <typename Policy>
popEnd()1370 inline void OpIter<Policy>::popEnd() {
1371 MOZ_ASSERT(Classify(op_) == OpKind::End);
1372
1373 controlStack_.popBack();
1374 }
1375
1376 template <typename Policy>
checkBranchValueAndPush(uint32_t relativeDepth,ResultType * type,ValueVector * values)1377 inline bool OpIter<Policy>::checkBranchValueAndPush(uint32_t relativeDepth,
1378 ResultType* type,
1379 ValueVector* values) {
1380 Control* block = nullptr;
1381 if (!getControl(relativeDepth, &block)) {
1382 return false;
1383 }
1384
1385 *type = block->branchTargetType();
1386 return topWithTypeAndPush(*type, values);
1387 }
1388
1389 // Check the typing of a branch instruction which casts an input type to
1390 // an output type, branching on success to a target which takes the output
1391 // type along with extra values from the stack. On casting failure, the
1392 // original input type and extra values are left on the stack.
1393 template <typename Policy>
checkCastedBranchValueAndPush(uint32_t relativeDepth,ValType castedFromType,ValType castedToType,ResultType * branchTargetType,ValueVector * values)1394 inline bool OpIter<Policy>::checkCastedBranchValueAndPush(
1395 uint32_t relativeDepth, ValType castedFromType, ValType castedToType,
1396 ResultType* branchTargetType, ValueVector* values) {
1397 // Get the branch target type, which will determine the type of extra values
1398 // that are passed along with the casted type.
1399 Control* block = nullptr;
1400 if (!getControl(relativeDepth, &block)) {
1401 return false;
1402 }
1403 *branchTargetType = block->branchTargetType();
1404
1405 // Check we at least have one type in the branch target type, which will take
1406 // the casted type.
1407 if (branchTargetType->length() < 1) {
1408 UniqueChars expectedText = ToString(castedToType);
1409 if (!expectedText) {
1410 return false;
1411 }
1412
1413 UniqueChars error(JS_smprintf("type mismatch: expected [_, %s], got []",
1414 expectedText.get()));
1415 if (!error) {
1416 return false;
1417 }
1418 return fail(error.get());
1419 }
1420
1421 // The top of the stack is the type that is being cast. This is the last type
1422 // in the branch target type. This is guaranteed to exist by the above check.
1423 const size_t castTypeIndex = branchTargetType->length() - 1;
1424
1425 // Check that the branch target type can accept the castedToType. The branch
1426 // target may specify a super type of the castedToType, and this is okay.
1427 if (!checkIsSubtypeOf(castedToType, (*branchTargetType)[castTypeIndex])) {
1428 return false;
1429 }
1430
1431 // Create a copy of the branch target type, with the castTypeIndex replaced
1432 // with the castedFromType. Use this to check that the stack has the proper
1433 // types to branch to the target type.
1434 //
1435 // TODO: We could avoid a potential allocation here by handwriting a custom
1436 // topWithTypeAndPush that handles this case.
1437 ValTypeVector stackTargetType;
1438 if (!branchTargetType->cloneToVector(&stackTargetType)) {
1439 return false;
1440 }
1441 stackTargetType[castTypeIndex] = castedFromType;
1442
1443 return topWithTypeAndPush(ResultType::Vector(stackTargetType), values);
1444 }
1445
1446 template <typename Policy>
readBr(uint32_t * relativeDepth,ResultType * type,ValueVector * values)1447 inline bool OpIter<Policy>::readBr(uint32_t* relativeDepth, ResultType* type,
1448 ValueVector* values) {
1449 MOZ_ASSERT(Classify(op_) == OpKind::Br);
1450
1451 if (!readVarU32(relativeDepth)) {
1452 return fail("unable to read br depth");
1453 }
1454
1455 if (!checkBranchValueAndPush(*relativeDepth, type, values)) {
1456 return false;
1457 }
1458
1459 afterUnconditionalBranch();
1460 return true;
1461 }
1462
1463 template <typename Policy>
readBrIf(uint32_t * relativeDepth,ResultType * type,ValueVector * values,Value * condition)1464 inline bool OpIter<Policy>::readBrIf(uint32_t* relativeDepth, ResultType* type,
1465 ValueVector* values, Value* condition) {
1466 MOZ_ASSERT(Classify(op_) == OpKind::BrIf);
1467
1468 if (!readVarU32(relativeDepth)) {
1469 return fail("unable to read br_if depth");
1470 }
1471
1472 if (!popWithType(ValType::I32, condition)) {
1473 return false;
1474 }
1475
1476 return checkBranchValueAndPush(*relativeDepth, type, values);
1477 }
1478
1479 #define UNKNOWN_ARITY UINT32_MAX
1480
1481 template <typename Policy>
checkBrTableEntryAndPush(uint32_t * relativeDepth,ResultType prevBranchType,ResultType * type,ValueVector * branchValues)1482 inline bool OpIter<Policy>::checkBrTableEntryAndPush(
1483 uint32_t* relativeDepth, ResultType prevBranchType, ResultType* type,
1484 ValueVector* branchValues) {
1485 if (!readVarU32(relativeDepth)) {
1486 return fail("unable to read br_table depth");
1487 }
1488
1489 Control* block = nullptr;
1490 if (!getControl(*relativeDepth, &block)) {
1491 return false;
1492 }
1493
1494 *type = block->branchTargetType();
1495
1496 if (prevBranchType != ResultType()) {
1497 if (prevBranchType.length() != type->length()) {
1498 return fail("br_table targets must all have the same arity");
1499 }
1500
1501 // Avoid re-collecting the same values for subsequent branch targets.
1502 branchValues = nullptr;
1503 }
1504
1505 return topWithTypeAndPush(*type, branchValues);
1506 }
1507
1508 template <typename Policy>
readBrTable(Uint32Vector * depths,uint32_t * defaultDepth,ResultType * defaultBranchType,ValueVector * branchValues,Value * index)1509 inline bool OpIter<Policy>::readBrTable(Uint32Vector* depths,
1510 uint32_t* defaultDepth,
1511 ResultType* defaultBranchType,
1512 ValueVector* branchValues,
1513 Value* index) {
1514 MOZ_ASSERT(Classify(op_) == OpKind::BrTable);
1515
1516 uint32_t tableLength;
1517 if (!readVarU32(&tableLength)) {
1518 return fail("unable to read br_table table length");
1519 }
1520
1521 if (tableLength > MaxBrTableElems) {
1522 return fail("br_table too big");
1523 }
1524
1525 if (!popWithType(ValType::I32, index)) {
1526 return false;
1527 }
1528
1529 if (!depths->resize(tableLength)) {
1530 return false;
1531 }
1532
1533 ResultType prevBranchType;
1534 for (uint32_t i = 0; i < tableLength; i++) {
1535 ResultType branchType;
1536 if (!checkBrTableEntryAndPush(&(*depths)[i], prevBranchType, &branchType,
1537 branchValues)) {
1538 return false;
1539 }
1540 prevBranchType = branchType;
1541 }
1542
1543 if (!checkBrTableEntryAndPush(defaultDepth, prevBranchType, defaultBranchType,
1544 branchValues)) {
1545 return false;
1546 }
1547
1548 MOZ_ASSERT(*defaultBranchType != ResultType());
1549
1550 afterUnconditionalBranch();
1551 return true;
1552 }
1553
1554 #undef UNKNOWN_ARITY
1555
1556 #ifdef ENABLE_WASM_EXCEPTIONS
1557 template <typename Policy>
readTry(ResultType * paramType)1558 inline bool OpIter<Policy>::readTry(ResultType* paramType) {
1559 MOZ_ASSERT(Classify(op_) == OpKind::Try);
1560
1561 BlockType type;
1562 if (!readBlockType(&type)) {
1563 return false;
1564 }
1565
1566 *paramType = type.params();
1567 return pushControl(LabelKind::Try, type);
1568 }
1569
1570 template <typename Policy>
readCatch(LabelKind * kind,uint32_t * tagIndex,ResultType * paramType,ResultType * resultType,ValueVector * tryResults)1571 inline bool OpIter<Policy>::readCatch(LabelKind* kind, uint32_t* tagIndex,
1572 ResultType* paramType,
1573 ResultType* resultType,
1574 ValueVector* tryResults) {
1575 MOZ_ASSERT(Classify(op_) == OpKind::Catch);
1576
1577 if (!readVarU32(tagIndex)) {
1578 return fail("expected tag index");
1579 }
1580 if (*tagIndex >= env_.tags.length()) {
1581 return fail("tag index out of range");
1582 }
1583
1584 Control& block = controlStack_.back();
1585 if (block.kind() == LabelKind::CatchAll) {
1586 return fail("catch cannot follow a catch_all");
1587 }
1588 if (block.kind() != LabelKind::Try && block.kind() != LabelKind::Catch) {
1589 return fail("catch can only be used within a try-catch");
1590 }
1591 *kind = block.kind();
1592 *paramType = block.type().params();
1593
1594 if (!checkStackAtEndOfBlock(resultType, tryResults)) {
1595 return false;
1596 }
1597
1598 valueStack_.shrinkTo(block.valueStackBase());
1599 if (block.kind() == LabelKind::Try) {
1600 block.switchToCatch();
1601 }
1602
1603 return push(env_.tags[*tagIndex].type->resultType());
1604 }
1605
1606 template <typename Policy>
readCatchAll(LabelKind * kind,ResultType * paramType,ResultType * resultType,ValueVector * tryResults)1607 inline bool OpIter<Policy>::readCatchAll(LabelKind* kind, ResultType* paramType,
1608 ResultType* resultType,
1609 ValueVector* tryResults) {
1610 MOZ_ASSERT(Classify(op_) == OpKind::CatchAll);
1611
1612 Control& block = controlStack_.back();
1613 if (block.kind() != LabelKind::Try && block.kind() != LabelKind::Catch) {
1614 return fail("catch_all can only be used within a try-catch");
1615 }
1616 *kind = block.kind();
1617 *paramType = block.type().params();
1618
1619 if (!checkStackAtEndOfBlock(resultType, tryResults)) {
1620 return false;
1621 }
1622
1623 valueStack_.shrinkTo(block.valueStackBase());
1624 block.switchToCatchAll();
1625
1626 return true;
1627 }
1628
1629 template <typename Policy>
readDelegate(uint32_t * relativeDepth,ResultType * resultType,ValueVector * tryResults)1630 inline bool OpIter<Policy>::readDelegate(uint32_t* relativeDepth,
1631 ResultType* resultType,
1632 ValueVector* tryResults) {
1633 MOZ_ASSERT(Classify(op_) == OpKind::Delegate);
1634
1635 uint32_t originalDepth;
1636 if (!readVarU32(&originalDepth)) {
1637 return fail("unable to read delegate depth");
1638 }
1639
1640 Control& block = controlStack_.back();
1641 if (block.kind() != LabelKind::Try) {
1642 return fail("delegate can only be used within a try");
1643 }
1644
1645 // Depths for delegate start counting in the surrounding block.
1646 *relativeDepth = originalDepth + 1;
1647 if (*relativeDepth >= controlStack_.length()) {
1648 return fail("delegate depth exceeds current nesting level");
1649 }
1650
1651 // Because `delegate` acts like `end` and ends the block, we will check
1652 // the stack here.
1653 return checkStackAtEndOfBlock(resultType, tryResults);
1654 }
1655
1656 // We need popDelegate because readDelegate cannot pop the control stack
1657 // itself, as its caller may need to use the control item for delegate.
1658 template <typename Policy>
popDelegate()1659 inline void OpIter<Policy>::popDelegate() {
1660 MOZ_ASSERT(Classify(op_) == OpKind::Delegate);
1661
1662 controlStack_.popBack();
1663 }
1664
1665 template <typename Policy>
readThrow(uint32_t * tagIndex,ValueVector * argValues)1666 inline bool OpIter<Policy>::readThrow(uint32_t* tagIndex,
1667 ValueVector* argValues) {
1668 MOZ_ASSERT(Classify(op_) == OpKind::Throw);
1669
1670 if (!readVarU32(tagIndex)) {
1671 return fail("expected tag index");
1672 }
1673 if (*tagIndex >= env_.tags.length()) {
1674 return fail("tag index out of range");
1675 }
1676
1677 if (!popWithType(env_.tags[*tagIndex].type->resultType(), argValues)) {
1678 return false;
1679 }
1680
1681 afterUnconditionalBranch();
1682 return true;
1683 }
1684
1685 template <typename Policy>
readRethrow(uint32_t * relativeDepth)1686 inline bool OpIter<Policy>::readRethrow(uint32_t* relativeDepth) {
1687 MOZ_ASSERT(Classify(op_) == OpKind::Rethrow);
1688
1689 if (!readVarU32(relativeDepth)) {
1690 return fail("unable to read rethrow depth");
1691 }
1692
1693 if (*relativeDepth >= controlStack_.length()) {
1694 return fail("rethrow depth exceeds current nesting level");
1695 }
1696 LabelKind kind = controlKind(*relativeDepth);
1697 if (kind != LabelKind::Catch && kind != LabelKind::CatchAll) {
1698 return fail("rethrow target was not a catch block");
1699 }
1700
1701 afterUnconditionalBranch();
1702 return true;
1703 }
1704 #endif
1705
1706 template <typename Policy>
readUnreachable()1707 inline bool OpIter<Policy>::readUnreachable() {
1708 MOZ_ASSERT(Classify(op_) == OpKind::Unreachable);
1709
1710 afterUnconditionalBranch();
1711 return true;
1712 }
1713
1714 template <typename Policy>
readDrop()1715 inline bool OpIter<Policy>::readDrop() {
1716 MOZ_ASSERT(Classify(op_) == OpKind::Drop);
1717 StackType type;
1718 Value value;
1719 return popStackType(&type, &value);
1720 }
1721
1722 template <typename Policy>
readUnary(ValType operandType,Value * input)1723 inline bool OpIter<Policy>::readUnary(ValType operandType, Value* input) {
1724 MOZ_ASSERT(Classify(op_) == OpKind::Unary);
1725
1726 if (!popWithType(operandType, input)) {
1727 return false;
1728 }
1729
1730 infalliblePush(operandType);
1731
1732 return true;
1733 }
1734
1735 template <typename Policy>
readConversion(ValType operandType,ValType resultType,Value * input)1736 inline bool OpIter<Policy>::readConversion(ValType operandType,
1737 ValType resultType, Value* input) {
1738 MOZ_ASSERT(Classify(op_) == OpKind::Conversion);
1739
1740 if (!popWithType(operandType, input)) {
1741 return false;
1742 }
1743
1744 infalliblePush(resultType);
1745
1746 return true;
1747 }
1748
1749 template <typename Policy>
readBinary(ValType operandType,Value * lhs,Value * rhs)1750 inline bool OpIter<Policy>::readBinary(ValType operandType, Value* lhs,
1751 Value* rhs) {
1752 MOZ_ASSERT(Classify(op_) == OpKind::Binary);
1753
1754 if (!popWithType(operandType, rhs)) {
1755 return false;
1756 }
1757
1758 if (!popWithType(operandType, lhs)) {
1759 return false;
1760 }
1761
1762 infalliblePush(operandType);
1763
1764 return true;
1765 }
1766
1767 template <typename Policy>
readComparison(ValType operandType,Value * lhs,Value * rhs)1768 inline bool OpIter<Policy>::readComparison(ValType operandType, Value* lhs,
1769 Value* rhs) {
1770 MOZ_ASSERT(Classify(op_) == OpKind::Comparison);
1771
1772 if (!popWithType(operandType, rhs)) {
1773 return false;
1774 }
1775
1776 if (!popWithType(operandType, lhs)) {
1777 return false;
1778 }
1779
1780 infalliblePush(ValType::I32);
1781
1782 return true;
1783 }
1784
1785 template <typename Policy>
readTernary(ValType operandType,Value * v0,Value * v1,Value * v2)1786 inline bool OpIter<Policy>::readTernary(ValType operandType, Value* v0,
1787 Value* v1, Value* v2) {
1788 MOZ_ASSERT(Classify(op_) == OpKind::Ternary);
1789
1790 if (!popWithType(operandType, v2)) {
1791 return false;
1792 }
1793
1794 if (!popWithType(operandType, v1)) {
1795 return false;
1796 }
1797
1798 if (!popWithType(operandType, v0)) {
1799 return false;
1800 }
1801
1802 infalliblePush(operandType);
1803
1804 return true;
1805 }
1806
1807 // For memories, the index is currently always a placeholder zero byte.
1808 //
1809 // For tables, the index is a placeholder zero byte until we get multi-table
1810 // with the reftypes proposal.
1811 //
1812 // The zero-ness of the value must be checked by the caller.
1813 template <typename Policy>
readMemOrTableIndex(bool isMem,uint32_t * index)1814 inline bool OpIter<Policy>::readMemOrTableIndex(bool isMem, uint32_t* index) {
1815 bool readByte = isMem;
1816 if (readByte) {
1817 uint8_t indexTmp;
1818 if (!readFixedU8(&indexTmp)) {
1819 return fail("unable to read memory or table index");
1820 }
1821 *index = indexTmp;
1822 } else {
1823 if (!readVarU32(index)) {
1824 return fail("unable to read memory or table index");
1825 }
1826 }
1827 return true;
1828 }
1829
1830 template <typename Policy>
readLinearMemoryAddress(uint32_t byteSize,LinearMemoryAddress<Value> * addr)1831 inline bool OpIter<Policy>::readLinearMemoryAddress(
1832 uint32_t byteSize, LinearMemoryAddress<Value>* addr) {
1833 if (!env_.usesMemory()) {
1834 return fail("can't touch memory without memory");
1835 }
1836
1837 IndexType it = env_.memory->indexType();
1838
1839 uint8_t alignLog2;
1840 if (!readFixedU8(&alignLog2)) {
1841 return fail("unable to read load alignment");
1842 }
1843
1844 if (!readVarU64(&addr->offset)) {
1845 return fail("unable to read load offset");
1846 }
1847
1848 if (it == IndexType::I32 && addr->offset > UINT32_MAX) {
1849 return fail("offset too large for memory type");
1850 }
1851
1852 if (alignLog2 >= 32 || (uint32_t(1) << alignLog2) > byteSize) {
1853 return fail("greater than natural alignment");
1854 }
1855
1856 if (!popWithType(ToValType(it), &addr->base)) {
1857 return false;
1858 }
1859
1860 addr->align = uint32_t(1) << alignLog2;
1861 return true;
1862 }
1863
1864 template <typename Policy>
readLinearMemoryAddressAligned(uint32_t byteSize,LinearMemoryAddress<Value> * addr)1865 inline bool OpIter<Policy>::readLinearMemoryAddressAligned(
1866 uint32_t byteSize, LinearMemoryAddress<Value>* addr) {
1867 if (!readLinearMemoryAddress(byteSize, addr)) {
1868 return false;
1869 }
1870
1871 if (addr->align != byteSize) {
1872 return fail("not natural alignment");
1873 }
1874
1875 return true;
1876 }
1877
1878 template <typename Policy>
readLoad(ValType resultType,uint32_t byteSize,LinearMemoryAddress<Value> * addr)1879 inline bool OpIter<Policy>::readLoad(ValType resultType, uint32_t byteSize,
1880 LinearMemoryAddress<Value>* addr) {
1881 MOZ_ASSERT(Classify(op_) == OpKind::Load);
1882
1883 if (!readLinearMemoryAddress(byteSize, addr)) {
1884 return false;
1885 }
1886
1887 infalliblePush(resultType);
1888
1889 return true;
1890 }
1891
1892 template <typename Policy>
readStore(ValType resultType,uint32_t byteSize,LinearMemoryAddress<Value> * addr,Value * value)1893 inline bool OpIter<Policy>::readStore(ValType resultType, uint32_t byteSize,
1894 LinearMemoryAddress<Value>* addr,
1895 Value* value) {
1896 MOZ_ASSERT(Classify(op_) == OpKind::Store);
1897
1898 if (!popWithType(resultType, value)) {
1899 return false;
1900 }
1901
1902 if (!readLinearMemoryAddress(byteSize, addr)) {
1903 return false;
1904 }
1905
1906 return true;
1907 }
1908
1909 template <typename Policy>
readTeeStore(ValType resultType,uint32_t byteSize,LinearMemoryAddress<Value> * addr,Value * value)1910 inline bool OpIter<Policy>::readTeeStore(ValType resultType, uint32_t byteSize,
1911 LinearMemoryAddress<Value>* addr,
1912 Value* value) {
1913 MOZ_ASSERT(Classify(op_) == OpKind::TeeStore);
1914
1915 if (!popWithType(resultType, value)) {
1916 return false;
1917 }
1918
1919 if (!readLinearMemoryAddress(byteSize, addr)) {
1920 return false;
1921 }
1922
1923 infalliblePush(TypeAndValue(resultType, *value));
1924 return true;
1925 }
1926
1927 template <typename Policy>
readNop()1928 inline bool OpIter<Policy>::readNop() {
1929 MOZ_ASSERT(Classify(op_) == OpKind::Nop);
1930
1931 return true;
1932 }
1933
1934 template <typename Policy>
readMemorySize()1935 inline bool OpIter<Policy>::readMemorySize() {
1936 MOZ_ASSERT(Classify(op_) == OpKind::MemorySize);
1937
1938 if (!env_.usesMemory()) {
1939 return fail("can't touch memory without memory");
1940 }
1941
1942 uint8_t flags;
1943 if (!readFixedU8(&flags)) {
1944 return fail("failed to read memory flags");
1945 }
1946
1947 if (flags != uint8_t(0)) {
1948 return fail("unexpected flags");
1949 }
1950
1951 ValType ptrType = ToValType(env_.memory->indexType());
1952 return push(ptrType);
1953 }
1954
1955 template <typename Policy>
readMemoryGrow(Value * input)1956 inline bool OpIter<Policy>::readMemoryGrow(Value* input) {
1957 MOZ_ASSERT(Classify(op_) == OpKind::MemoryGrow);
1958
1959 if (!env_.usesMemory()) {
1960 return fail("can't touch memory without memory");
1961 }
1962
1963 uint8_t flags;
1964 if (!readFixedU8(&flags)) {
1965 return fail("failed to read memory flags");
1966 }
1967
1968 if (flags != uint8_t(0)) {
1969 return fail("unexpected flags");
1970 }
1971
1972 ValType ptrType = ToValType(env_.memory->indexType());
1973 if (!popWithType(ptrType, input)) {
1974 return false;
1975 }
1976
1977 infalliblePush(ptrType);
1978
1979 return true;
1980 }
1981
1982 template <typename Policy>
readSelect(bool typed,StackType * type,Value * trueValue,Value * falseValue,Value * condition)1983 inline bool OpIter<Policy>::readSelect(bool typed, StackType* type,
1984 Value* trueValue, Value* falseValue,
1985 Value* condition) {
1986 MOZ_ASSERT(Classify(op_) == OpKind::Select);
1987
1988 if (typed) {
1989 uint32_t length;
1990 if (!readVarU32(&length)) {
1991 return fail("unable to read select result length");
1992 }
1993 if (length != 1) {
1994 return fail("bad number of results");
1995 }
1996 ValType result;
1997 if (!readValType(&result)) {
1998 return fail("invalid result type for select");
1999 }
2000
2001 if (!popWithType(ValType::I32, condition)) {
2002 return false;
2003 }
2004 if (!popWithType(result, falseValue)) {
2005 return false;
2006 }
2007 if (!popWithType(result, trueValue)) {
2008 return false;
2009 }
2010
2011 *type = StackType(result);
2012 infalliblePush(*type);
2013 return true;
2014 }
2015
2016 if (!popWithType(ValType::I32, condition)) {
2017 return false;
2018 }
2019
2020 StackType falseType;
2021 if (!popStackType(&falseType, falseValue)) {
2022 return false;
2023 }
2024
2025 StackType trueType;
2026 if (!popStackType(&trueType, trueValue)) {
2027 return false;
2028 }
2029
2030 if (!falseType.isValidForUntypedSelect() ||
2031 !trueType.isValidForUntypedSelect()) {
2032 return fail("invalid types for untyped select");
2033 }
2034
2035 if (falseType.isBottom()) {
2036 *type = trueType;
2037 } else if (trueType.isBottom() || falseType == trueType) {
2038 *type = falseType;
2039 } else {
2040 return fail("select operand types must match");
2041 }
2042
2043 infalliblePush(*type);
2044 return true;
2045 }
2046
2047 template <typename Policy>
readGetLocal(const ValTypeVector & locals,uint32_t * id)2048 inline bool OpIter<Policy>::readGetLocal(const ValTypeVector& locals,
2049 uint32_t* id) {
2050 MOZ_ASSERT(Classify(op_) == OpKind::GetLocal);
2051
2052 if (!readVarU32(id)) {
2053 return fail("unable to read local index");
2054 }
2055
2056 if (*id >= locals.length()) {
2057 return fail("local.get index out of range");
2058 }
2059
2060 return push(locals[*id]);
2061 }
2062
2063 template <typename Policy>
readSetLocal(const ValTypeVector & locals,uint32_t * id,Value * value)2064 inline bool OpIter<Policy>::readSetLocal(const ValTypeVector& locals,
2065 uint32_t* id, Value* value) {
2066 MOZ_ASSERT(Classify(op_) == OpKind::SetLocal);
2067
2068 if (!readVarU32(id)) {
2069 return fail("unable to read local index");
2070 }
2071
2072 if (*id >= locals.length()) {
2073 return fail("local.set index out of range");
2074 }
2075
2076 return popWithType(locals[*id], value);
2077 }
2078
2079 template <typename Policy>
readTeeLocal(const ValTypeVector & locals,uint32_t * id,Value * value)2080 inline bool OpIter<Policy>::readTeeLocal(const ValTypeVector& locals,
2081 uint32_t* id, Value* value) {
2082 MOZ_ASSERT(Classify(op_) == OpKind::TeeLocal);
2083
2084 if (!readVarU32(id)) {
2085 return fail("unable to read local index");
2086 }
2087
2088 if (*id >= locals.length()) {
2089 return fail("local.set index out of range");
2090 }
2091
2092 ValueVector single;
2093 if (!popThenPushType(ResultType::Single(locals[*id]), &single)) {
2094 return false;
2095 }
2096
2097 *value = single[0];
2098 return true;
2099 }
2100
2101 template <typename Policy>
readGetGlobal(uint32_t * id)2102 inline bool OpIter<Policy>::readGetGlobal(uint32_t* id) {
2103 MOZ_ASSERT(Classify(op_) == OpKind::GetGlobal);
2104
2105 if (!d_.readGlobalIndex(id)) {
2106 return false;
2107 }
2108
2109 if (*id >= env_.globals.length()) {
2110 return fail("global.get index out of range");
2111 }
2112
2113 if (kind_ == OpIter::InitExpr &&
2114 (!env_.globals[*id].isImport() || env_.globals[*id].isMutable())) {
2115 return fail(
2116 "global.get in initializer expression must reference a global "
2117 "immutable import");
2118 }
2119
2120 return push(env_.globals[*id].type());
2121 }
2122
2123 template <typename Policy>
readSetGlobal(uint32_t * id,Value * value)2124 inline bool OpIter<Policy>::readSetGlobal(uint32_t* id, Value* value) {
2125 MOZ_ASSERT(Classify(op_) == OpKind::SetGlobal);
2126
2127 if (!d_.readGlobalIndex(id)) {
2128 return false;
2129 }
2130
2131 if (*id >= env_.globals.length()) {
2132 return fail("global.set index out of range");
2133 }
2134
2135 if (!env_.globals[*id].isMutable()) {
2136 return fail("can't write an immutable global");
2137 }
2138
2139 return popWithType(env_.globals[*id].type(), value);
2140 }
2141
2142 template <typename Policy>
readTeeGlobal(uint32_t * id,Value * value)2143 inline bool OpIter<Policy>::readTeeGlobal(uint32_t* id, Value* value) {
2144 MOZ_ASSERT(Classify(op_) == OpKind::TeeGlobal);
2145
2146 if (!d_.readGlobalIndex(id)) {
2147 return false;
2148 }
2149
2150 if (*id >= env_.globals.length()) {
2151 return fail("global.set index out of range");
2152 }
2153
2154 if (!env_.globals[*id].isMutable()) {
2155 return fail("can't write an immutable global");
2156 }
2157
2158 ValueVector single;
2159 if (!popThenPushType(ResultType::Single(env_.globals[*id].type()), &single)) {
2160 return false;
2161 }
2162
2163 MOZ_ASSERT(single.length() == 1);
2164 *value = single[0];
2165 return true;
2166 }
2167
2168 template <typename Policy>
readI32Const(int32_t * i32)2169 inline bool OpIter<Policy>::readI32Const(int32_t* i32) {
2170 MOZ_ASSERT(Classify(op_) == OpKind::I32);
2171
2172 if (!d_.readI32Const(i32)) {
2173 return false;
2174 }
2175
2176 return push(ValType::I32);
2177 }
2178
2179 template <typename Policy>
readI64Const(int64_t * i64)2180 inline bool OpIter<Policy>::readI64Const(int64_t* i64) {
2181 MOZ_ASSERT(Classify(op_) == OpKind::I64);
2182
2183 if (!d_.readI64Const(i64)) {
2184 return false;
2185 }
2186
2187 return push(ValType::I64);
2188 }
2189
2190 template <typename Policy>
readF32Const(float * f32)2191 inline bool OpIter<Policy>::readF32Const(float* f32) {
2192 MOZ_ASSERT(Classify(op_) == OpKind::F32);
2193
2194 if (!d_.readF32Const(f32)) {
2195 return false;
2196 }
2197
2198 return push(ValType::F32);
2199 }
2200
2201 template <typename Policy>
readF64Const(double * f64)2202 inline bool OpIter<Policy>::readF64Const(double* f64) {
2203 MOZ_ASSERT(Classify(op_) == OpKind::F64);
2204
2205 if (!d_.readF64Const(f64)) {
2206 return false;
2207 }
2208
2209 return push(ValType::F64);
2210 }
2211
2212 template <typename Policy>
readRefFunc(uint32_t * funcIndex)2213 inline bool OpIter<Policy>::readRefFunc(uint32_t* funcIndex) {
2214 MOZ_ASSERT(Classify(op_) == OpKind::RefFunc);
2215
2216 if (!d_.readFuncIndex(funcIndex)) {
2217 return false;
2218 }
2219 if (*funcIndex >= env_.funcs.length()) {
2220 return fail("function index out of range");
2221 }
2222 if (kind_ == OpIter::Func && !env_.funcs[*funcIndex].canRefFunc()) {
2223 return fail(
2224 "function index is not declared in a section before the code section");
2225 }
2226 return push(RefType::func());
2227 }
2228
2229 template <typename Policy>
readRefNull(RefType * type)2230 inline bool OpIter<Policy>::readRefNull(RefType* type) {
2231 MOZ_ASSERT(Classify(op_) == OpKind::RefNull);
2232
2233 if (!d_.readRefNull(*env_.types, env_.features, type)) {
2234 return false;
2235 }
2236 return push(*type);
2237 }
2238
2239 template <typename Policy>
readRefIsNull(Value * input)2240 inline bool OpIter<Policy>::readRefIsNull(Value* input) {
2241 MOZ_ASSERT(Classify(op_) == OpKind::Conversion);
2242
2243 StackType type;
2244 if (!popWithRefType(input, &type)) {
2245 return false;
2246 }
2247 return push(ValType::I32);
2248 }
2249
2250 template <typename Policy>
readRefAsNonNull(Value * input)2251 inline bool OpIter<Policy>::readRefAsNonNull(Value* input) {
2252 MOZ_ASSERT(Classify(op_) == OpKind::RefAsNonNull);
2253
2254 StackType type;
2255 if (!popWithRefType(input, &type)) {
2256 return false;
2257 }
2258
2259 if (type.isBottom()) {
2260 infalliblePush(type);
2261 } else {
2262 infalliblePush(type.asNonNullable());
2263 }
2264 return true;
2265 }
2266
2267 template <typename Policy>
readBrOnNull(uint32_t * relativeDepth,ResultType * type,ValueVector * values,Value * condition)2268 inline bool OpIter<Policy>::readBrOnNull(uint32_t* relativeDepth,
2269 ResultType* type, ValueVector* values,
2270 Value* condition) {
2271 MOZ_ASSERT(Classify(op_) == OpKind::BrOnNull);
2272
2273 if (!readVarU32(relativeDepth)) {
2274 return fail("unable to read br_on_null depth");
2275 }
2276
2277 StackType refType;
2278 if (!popWithRefType(condition, &refType)) {
2279 return false;
2280 }
2281
2282 if (!checkBranchValueAndPush(*relativeDepth, type, values)) {
2283 return false;
2284 }
2285
2286 if (refType.isBottom()) {
2287 return push(refType);
2288 }
2289 return push(refType.asNonNullable());
2290 }
2291
2292 template <typename Policy>
popCallArgs(const ValTypeVector & expectedTypes,ValueVector * values)2293 inline bool OpIter<Policy>::popCallArgs(const ValTypeVector& expectedTypes,
2294 ValueVector* values) {
2295 // Iterate through the argument types backward so that pops occur in the
2296 // right order.
2297
2298 if (!values->resize(expectedTypes.length())) {
2299 return false;
2300 }
2301
2302 for (int32_t i = expectedTypes.length() - 1; i >= 0; i--) {
2303 if (!popWithType(expectedTypes[i], &(*values)[i])) {
2304 return false;
2305 }
2306 }
2307
2308 return true;
2309 }
2310
2311 template <typename Policy>
readCall(uint32_t * funcTypeIndex,ValueVector * argValues)2312 inline bool OpIter<Policy>::readCall(uint32_t* funcTypeIndex,
2313 ValueVector* argValues) {
2314 MOZ_ASSERT(Classify(op_) == OpKind::Call);
2315
2316 if (!readVarU32(funcTypeIndex)) {
2317 return fail("unable to read call function index");
2318 }
2319
2320 if (*funcTypeIndex >= env_.funcs.length()) {
2321 return fail("callee index out of range");
2322 }
2323
2324 const FuncType& funcType = *env_.funcs[*funcTypeIndex].type;
2325
2326 if (!popCallArgs(funcType.args(), argValues)) {
2327 return false;
2328 }
2329
2330 return push(ResultType::Vector(funcType.results()));
2331 }
2332
2333 template <typename Policy>
readCallIndirect(uint32_t * funcTypeIndex,uint32_t * tableIndex,Value * callee,ValueVector * argValues)2334 inline bool OpIter<Policy>::readCallIndirect(uint32_t* funcTypeIndex,
2335 uint32_t* tableIndex,
2336 Value* callee,
2337 ValueVector* argValues) {
2338 MOZ_ASSERT(Classify(op_) == OpKind::CallIndirect);
2339 MOZ_ASSERT(funcTypeIndex != tableIndex);
2340
2341 if (!readVarU32(funcTypeIndex)) {
2342 return fail("unable to read call_indirect signature index");
2343 }
2344
2345 if (*funcTypeIndex >= env_.numTypes()) {
2346 return fail("signature index out of range");
2347 }
2348
2349 if (!readVarU32(tableIndex)) {
2350 return fail("unable to read call_indirect table index");
2351 }
2352 if (*tableIndex >= env_.tables.length()) {
2353 // Special case this for improved user experience.
2354 if (!env_.tables.length()) {
2355 return fail("can't call_indirect without a table");
2356 }
2357 return fail("table index out of range for call_indirect");
2358 }
2359 if (!env_.tables[*tableIndex].elemType.isFunc()) {
2360 return fail("indirect calls must go through a table of 'funcref'");
2361 }
2362
2363 if (!popWithType(ValType::I32, callee)) {
2364 return false;
2365 }
2366
2367 if (!env_.types->isFuncType(*funcTypeIndex)) {
2368 return fail("expected signature type");
2369 }
2370
2371 const FuncType& funcType = env_.types->funcType(*funcTypeIndex);
2372
2373 #ifdef WASM_PRIVATE_REFTYPES
2374 if (env_.tables[*tableIndex].isImportedOrExported &&
2375 funcType.exposesTypeIndex()) {
2376 return fail("cannot expose indexed reference type");
2377 }
2378 #endif
2379
2380 if (!popCallArgs(funcType.args(), argValues)) {
2381 return false;
2382 }
2383
2384 return push(ResultType::Vector(funcType.results()));
2385 }
2386
2387 template <typename Policy>
readOldCallDirect(uint32_t numFuncImports,uint32_t * funcTypeIndex,ValueVector * argValues)2388 inline bool OpIter<Policy>::readOldCallDirect(uint32_t numFuncImports,
2389 uint32_t* funcTypeIndex,
2390 ValueVector* argValues) {
2391 MOZ_ASSERT(Classify(op_) == OpKind::OldCallDirect);
2392
2393 uint32_t funcDefIndex;
2394 if (!readVarU32(&funcDefIndex)) {
2395 return fail("unable to read call function index");
2396 }
2397
2398 if (UINT32_MAX - funcDefIndex < numFuncImports) {
2399 return fail("callee index out of range");
2400 }
2401
2402 *funcTypeIndex = numFuncImports + funcDefIndex;
2403
2404 if (*funcTypeIndex >= env_.funcs.length()) {
2405 return fail("callee index out of range");
2406 }
2407
2408 const FuncType& funcType = *env_.funcs[*funcTypeIndex].type;
2409
2410 if (!popCallArgs(funcType.args(), argValues)) {
2411 return false;
2412 }
2413
2414 return push(ResultType::Vector(funcType.results()));
2415 }
2416
2417 template <typename Policy>
readOldCallIndirect(uint32_t * funcTypeIndex,Value * callee,ValueVector * argValues)2418 inline bool OpIter<Policy>::readOldCallIndirect(uint32_t* funcTypeIndex,
2419 Value* callee,
2420 ValueVector* argValues) {
2421 MOZ_ASSERT(Classify(op_) == OpKind::OldCallIndirect);
2422
2423 if (!readVarU32(funcTypeIndex)) {
2424 return fail("unable to read call_indirect signature index");
2425 }
2426
2427 if (*funcTypeIndex >= env_.numTypes()) {
2428 return fail("signature index out of range");
2429 }
2430
2431 if (!env_.types->isFuncType(*funcTypeIndex)) {
2432 return fail("expected signature type");
2433 }
2434
2435 const FuncType& funcType = env_.types->funcType(*funcTypeIndex);
2436
2437 if (!popCallArgs(funcType.args(), argValues)) {
2438 return false;
2439 }
2440
2441 if (!popWithType(ValType::I32, callee)) {
2442 return false;
2443 }
2444
2445 return push(ResultType::Vector(funcType.results()));
2446 }
2447
2448 template <typename Policy>
readWake(LinearMemoryAddress<Value> * addr,Value * count)2449 inline bool OpIter<Policy>::readWake(LinearMemoryAddress<Value>* addr,
2450 Value* count) {
2451 MOZ_ASSERT(Classify(op_) == OpKind::Wake);
2452
2453 if (!popWithType(ValType::I32, count)) {
2454 return false;
2455 }
2456
2457 uint32_t byteSize = 4; // Per spec; smallest WAIT is i32.
2458
2459 if (!readLinearMemoryAddressAligned(byteSize, addr)) {
2460 return false;
2461 }
2462
2463 infalliblePush(ValType::I32);
2464 return true;
2465 }
2466
2467 template <typename Policy>
readWait(LinearMemoryAddress<Value> * addr,ValType valueType,uint32_t byteSize,Value * value,Value * timeout)2468 inline bool OpIter<Policy>::readWait(LinearMemoryAddress<Value>* addr,
2469 ValType valueType, uint32_t byteSize,
2470 Value* value, Value* timeout) {
2471 MOZ_ASSERT(Classify(op_) == OpKind::Wait);
2472
2473 if (!popWithType(ValType::I64, timeout)) {
2474 return false;
2475 }
2476
2477 if (!popWithType(valueType, value)) {
2478 return false;
2479 }
2480
2481 if (!readLinearMemoryAddressAligned(byteSize, addr)) {
2482 return false;
2483 }
2484
2485 infalliblePush(ValType::I32);
2486 return true;
2487 }
2488
2489 template <typename Policy>
readFence()2490 inline bool OpIter<Policy>::readFence() {
2491 MOZ_ASSERT(Classify(op_) == OpKind::Fence);
2492 uint8_t flags;
2493 if (!readFixedU8(&flags)) {
2494 return fail("expected memory order after fence");
2495 }
2496 if (flags != 0) {
2497 return fail("non-zero memory order not supported yet");
2498 }
2499 return true;
2500 }
2501
2502 template <typename Policy>
readAtomicLoad(LinearMemoryAddress<Value> * addr,ValType resultType,uint32_t byteSize)2503 inline bool OpIter<Policy>::readAtomicLoad(LinearMemoryAddress<Value>* addr,
2504 ValType resultType,
2505 uint32_t byteSize) {
2506 MOZ_ASSERT(Classify(op_) == OpKind::AtomicLoad);
2507
2508 if (!readLinearMemoryAddressAligned(byteSize, addr)) {
2509 return false;
2510 }
2511
2512 infalliblePush(resultType);
2513 return true;
2514 }
2515
2516 template <typename Policy>
readAtomicStore(LinearMemoryAddress<Value> * addr,ValType resultType,uint32_t byteSize,Value * value)2517 inline bool OpIter<Policy>::readAtomicStore(LinearMemoryAddress<Value>* addr,
2518 ValType resultType,
2519 uint32_t byteSize, Value* value) {
2520 MOZ_ASSERT(Classify(op_) == OpKind::AtomicStore);
2521
2522 if (!popWithType(resultType, value)) {
2523 return false;
2524 }
2525
2526 if (!readLinearMemoryAddressAligned(byteSize, addr)) {
2527 return false;
2528 }
2529
2530 return true;
2531 }
2532
2533 template <typename Policy>
readAtomicRMW(LinearMemoryAddress<Value> * addr,ValType resultType,uint32_t byteSize,Value * value)2534 inline bool OpIter<Policy>::readAtomicRMW(LinearMemoryAddress<Value>* addr,
2535 ValType resultType, uint32_t byteSize,
2536 Value* value) {
2537 MOZ_ASSERT(Classify(op_) == OpKind::AtomicBinOp);
2538
2539 if (!popWithType(resultType, value)) {
2540 return false;
2541 }
2542
2543 if (!readLinearMemoryAddressAligned(byteSize, addr)) {
2544 return false;
2545 }
2546
2547 infalliblePush(resultType);
2548 return true;
2549 }
2550
2551 template <typename Policy>
readAtomicCmpXchg(LinearMemoryAddress<Value> * addr,ValType resultType,uint32_t byteSize,Value * oldValue,Value * newValue)2552 inline bool OpIter<Policy>::readAtomicCmpXchg(LinearMemoryAddress<Value>* addr,
2553 ValType resultType,
2554 uint32_t byteSize,
2555 Value* oldValue,
2556 Value* newValue) {
2557 MOZ_ASSERT(Classify(op_) == OpKind::AtomicCompareExchange);
2558
2559 if (!popWithType(resultType, newValue)) {
2560 return false;
2561 }
2562
2563 if (!popWithType(resultType, oldValue)) {
2564 return false;
2565 }
2566
2567 if (!readLinearMemoryAddressAligned(byteSize, addr)) {
2568 return false;
2569 }
2570
2571 infalliblePush(resultType);
2572 return true;
2573 }
2574
2575 template <typename Policy>
readMemOrTableCopy(bool isMem,uint32_t * dstMemOrTableIndex,Value * dst,uint32_t * srcMemOrTableIndex,Value * src,Value * len)2576 inline bool OpIter<Policy>::readMemOrTableCopy(bool isMem,
2577 uint32_t* dstMemOrTableIndex,
2578 Value* dst,
2579 uint32_t* srcMemOrTableIndex,
2580 Value* src, Value* len) {
2581 MOZ_ASSERT(Classify(op_) == OpKind::MemOrTableCopy);
2582 MOZ_ASSERT(dstMemOrTableIndex != srcMemOrTableIndex);
2583
2584 // Spec requires (dest, src) as of 2019-10-04.
2585 if (!readMemOrTableIndex(isMem, dstMemOrTableIndex)) {
2586 return false;
2587 }
2588 if (!readMemOrTableIndex(isMem, srcMemOrTableIndex)) {
2589 return false;
2590 }
2591
2592 if (isMem) {
2593 if (!env_.usesMemory()) {
2594 return fail("can't touch memory without memory");
2595 }
2596 if (*srcMemOrTableIndex != 0 || *dstMemOrTableIndex != 0) {
2597 return fail("memory index out of range for memory.copy");
2598 }
2599 } else {
2600 if (*dstMemOrTableIndex >= env_.tables.length() ||
2601 *srcMemOrTableIndex >= env_.tables.length()) {
2602 return fail("table index out of range for table.copy");
2603 }
2604 ValType dstElemType = env_.tables[*dstMemOrTableIndex].elemType;
2605 ValType srcElemType = env_.tables[*srcMemOrTableIndex].elemType;
2606 if (!checkIsSubtypeOf(srcElemType, dstElemType)) {
2607 return false;
2608 }
2609 }
2610
2611 ValType ptrType = isMem ? ToValType(env_.memory->indexType()) : ValType::I32;
2612
2613 if (!popWithType(ptrType, len)) {
2614 return false;
2615 }
2616
2617 if (!popWithType(ptrType, src)) {
2618 return false;
2619 }
2620
2621 if (!popWithType(ptrType, dst)) {
2622 return false;
2623 }
2624
2625 return true;
2626 }
2627
2628 template <typename Policy>
readDataOrElemDrop(bool isData,uint32_t * segIndex)2629 inline bool OpIter<Policy>::readDataOrElemDrop(bool isData,
2630 uint32_t* segIndex) {
2631 MOZ_ASSERT(Classify(op_) == OpKind::DataOrElemDrop);
2632
2633 if (!readVarU32(segIndex)) {
2634 return fail("unable to read segment index");
2635 }
2636
2637 if (isData) {
2638 if (env_.dataCount.isNothing()) {
2639 return fail("data.drop requires a DataCount section");
2640 }
2641 if (*segIndex >= *env_.dataCount) {
2642 return fail("data.drop segment index out of range");
2643 }
2644 } else {
2645 if (*segIndex >= env_.elemSegments.length()) {
2646 return fail("element segment index out of range for elem.drop");
2647 }
2648 }
2649
2650 return true;
2651 }
2652
2653 template <typename Policy>
readMemFill(Value * start,Value * val,Value * len)2654 inline bool OpIter<Policy>::readMemFill(Value* start, Value* val, Value* len) {
2655 MOZ_ASSERT(Classify(op_) == OpKind::MemFill);
2656
2657 if (!env_.usesMemory()) {
2658 return fail("can't touch memory without memory");
2659 }
2660
2661 uint8_t memoryIndex;
2662 if (!readFixedU8(&memoryIndex)) {
2663 return fail("failed to read memory index");
2664 }
2665 if (!env_.usesMemory()) {
2666 return fail("can't touch memory without memory");
2667 }
2668 if (memoryIndex != 0) {
2669 return fail("memory index must be zero");
2670 }
2671
2672 ValType ptrType = ToValType(env_.memory->indexType());
2673
2674 if (!popWithType(ptrType, len)) {
2675 return false;
2676 }
2677
2678 if (!popWithType(ValType::I32, val)) {
2679 return false;
2680 }
2681
2682 if (!popWithType(ptrType, start)) {
2683 return false;
2684 }
2685
2686 return true;
2687 }
2688
2689 template <typename Policy>
readMemOrTableInit(bool isMem,uint32_t * segIndex,uint32_t * dstTableIndex,Value * dst,Value * src,Value * len)2690 inline bool OpIter<Policy>::readMemOrTableInit(bool isMem, uint32_t* segIndex,
2691 uint32_t* dstTableIndex,
2692 Value* dst, Value* src,
2693 Value* len) {
2694 MOZ_ASSERT(Classify(op_) == OpKind::MemOrTableInit);
2695 MOZ_ASSERT(segIndex != dstTableIndex);
2696
2697 if (!readVarU32(segIndex)) {
2698 return fail("unable to read segment index");
2699 }
2700
2701 uint32_t memOrTableIndex = 0;
2702 if (!readMemOrTableIndex(isMem, &memOrTableIndex)) {
2703 return false;
2704 }
2705
2706 if (isMem) {
2707 if (!env_.usesMemory()) {
2708 return fail("can't touch memory without memory");
2709 }
2710 if (memOrTableIndex != 0) {
2711 return fail("memory index must be zero");
2712 }
2713 if (env_.dataCount.isNothing()) {
2714 return fail("memory.init requires a DataCount section");
2715 }
2716 if (*segIndex >= *env_.dataCount) {
2717 return fail("memory.init segment index out of range");
2718 }
2719 } else {
2720 if (memOrTableIndex >= env_.tables.length()) {
2721 return fail("table index out of range for table.init");
2722 }
2723 *dstTableIndex = memOrTableIndex;
2724
2725 if (*segIndex >= env_.elemSegments.length()) {
2726 return fail("table.init segment index out of range");
2727 }
2728 if (!checkIsSubtypeOf(env_.elemSegments[*segIndex]->elemType,
2729 env_.tables[*dstTableIndex].elemType)) {
2730 return false;
2731 }
2732 }
2733
2734 if (!popWithType(ValType::I32, len)) {
2735 return false;
2736 }
2737
2738 if (!popWithType(ValType::I32, src)) {
2739 return false;
2740 }
2741
2742 ValType ptrType = isMem ? ToValType(env_.memory->indexType()) : ValType::I32;
2743
2744 if (!popWithType(ptrType, dst)) {
2745 return false;
2746 }
2747
2748 return true;
2749 }
2750
2751 template <typename Policy>
readTableFill(uint32_t * tableIndex,Value * start,Value * val,Value * len)2752 inline bool OpIter<Policy>::readTableFill(uint32_t* tableIndex, Value* start,
2753 Value* val, Value* len) {
2754 MOZ_ASSERT(Classify(op_) == OpKind::TableFill);
2755
2756 if (!readVarU32(tableIndex)) {
2757 return fail("unable to read table index");
2758 }
2759 if (*tableIndex >= env_.tables.length()) {
2760 return fail("table index out of range for table.fill");
2761 }
2762
2763 if (!popWithType(ValType::I32, len)) {
2764 return false;
2765 }
2766 if (!popWithType(env_.tables[*tableIndex].elemType, val)) {
2767 return false;
2768 }
2769 if (!popWithType(ValType::I32, start)) {
2770 return false;
2771 }
2772
2773 return true;
2774 }
2775
2776 template <typename Policy>
readTableGet(uint32_t * tableIndex,Value * index)2777 inline bool OpIter<Policy>::readTableGet(uint32_t* tableIndex, Value* index) {
2778 MOZ_ASSERT(Classify(op_) == OpKind::TableGet);
2779
2780 if (!readVarU32(tableIndex)) {
2781 return fail("unable to read table index");
2782 }
2783 if (*tableIndex >= env_.tables.length()) {
2784 return fail("table index out of range for table.get");
2785 }
2786
2787 if (!popWithType(ValType::I32, index)) {
2788 return false;
2789 }
2790
2791 infalliblePush(env_.tables[*tableIndex].elemType);
2792 return true;
2793 }
2794
2795 template <typename Policy>
readTableGrow(uint32_t * tableIndex,Value * initValue,Value * delta)2796 inline bool OpIter<Policy>::readTableGrow(uint32_t* tableIndex,
2797 Value* initValue, Value* delta) {
2798 MOZ_ASSERT(Classify(op_) == OpKind::TableGrow);
2799
2800 if (!readVarU32(tableIndex)) {
2801 return fail("unable to read table index");
2802 }
2803 if (*tableIndex >= env_.tables.length()) {
2804 return fail("table index out of range for table.grow");
2805 }
2806
2807 if (!popWithType(ValType::I32, delta)) {
2808 return false;
2809 }
2810 if (!popWithType(env_.tables[*tableIndex].elemType, initValue)) {
2811 return false;
2812 }
2813
2814 infalliblePush(ValType::I32);
2815 return true;
2816 }
2817
2818 template <typename Policy>
readTableSet(uint32_t * tableIndex,Value * index,Value * value)2819 inline bool OpIter<Policy>::readTableSet(uint32_t* tableIndex, Value* index,
2820 Value* value) {
2821 MOZ_ASSERT(Classify(op_) == OpKind::TableSet);
2822
2823 if (!readVarU32(tableIndex)) {
2824 return fail("unable to read table index");
2825 }
2826 if (*tableIndex >= env_.tables.length()) {
2827 return fail("table index out of range for table.set");
2828 }
2829
2830 if (!popWithType(env_.tables[*tableIndex].elemType, value)) {
2831 return false;
2832 }
2833 if (!popWithType(ValType::I32, index)) {
2834 return false;
2835 }
2836
2837 return true;
2838 }
2839
2840 template <typename Policy>
readTableSize(uint32_t * tableIndex)2841 inline bool OpIter<Policy>::readTableSize(uint32_t* tableIndex) {
2842 MOZ_ASSERT(Classify(op_) == OpKind::TableSize);
2843
2844 *tableIndex = 0;
2845
2846 if (!readVarU32(tableIndex)) {
2847 return fail("unable to read table index");
2848 }
2849 if (*tableIndex >= env_.tables.length()) {
2850 return fail("table index out of range for table.size");
2851 }
2852
2853 return push(ValType::I32);
2854 }
2855
2856 template <typename Policy>
readGcTypeIndex(uint32_t * typeIndex)2857 inline bool OpIter<Policy>::readGcTypeIndex(uint32_t* typeIndex) {
2858 if (!d_.readTypeIndex(typeIndex)) {
2859 return false;
2860 }
2861
2862 if (*typeIndex >= env_.types->length()) {
2863 return fail("type index out of range");
2864 }
2865
2866 if (!env_.types->isStructType(*typeIndex) &&
2867 !env_.types->isArrayType(*typeIndex)) {
2868 return fail("not a gc type");
2869 }
2870
2871 return true;
2872 }
2873
2874 template <typename Policy>
readStructTypeIndex(uint32_t * typeIndex)2875 inline bool OpIter<Policy>::readStructTypeIndex(uint32_t* typeIndex) {
2876 if (!readVarU32(typeIndex)) {
2877 return fail("unable to read type index");
2878 }
2879
2880 if (*typeIndex >= env_.types->length()) {
2881 return fail("type index out of range");
2882 }
2883
2884 if (!env_.types->isStructType(*typeIndex)) {
2885 return fail("not a struct type");
2886 }
2887
2888 return true;
2889 }
2890
2891 template <typename Policy>
readArrayTypeIndex(uint32_t * typeIndex)2892 inline bool OpIter<Policy>::readArrayTypeIndex(uint32_t* typeIndex) {
2893 if (!readVarU32(typeIndex)) {
2894 return fail("unable to read type index");
2895 }
2896
2897 if (*typeIndex >= env_.types->length()) {
2898 return fail("type index out of range");
2899 }
2900
2901 if (!env_.types->isArrayType(*typeIndex)) {
2902 return fail("not an array type");
2903 }
2904
2905 return true;
2906 }
2907
2908 template <typename Policy>
readFieldIndex(uint32_t * fieldIndex,const StructType & structType)2909 inline bool OpIter<Policy>::readFieldIndex(uint32_t* fieldIndex,
2910 const StructType& structType) {
2911 if (!readVarU32(fieldIndex)) {
2912 return fail("unable to read field index");
2913 }
2914
2915 if (structType.fields_.length() <= *fieldIndex) {
2916 return fail("field index out of range");
2917 }
2918
2919 return true;
2920 }
2921
2922 #ifdef ENABLE_WASM_GC
2923
2924 template <typename Policy>
readStructNewWithRtt(uint32_t * typeIndex,Value * rtt,ValueVector * argValues)2925 inline bool OpIter<Policy>::readStructNewWithRtt(uint32_t* typeIndex,
2926 Value* rtt,
2927 ValueVector* argValues) {
2928 MOZ_ASSERT(Classify(op_) == OpKind::StructNewWithRtt);
2929
2930 if (!readStructTypeIndex(typeIndex)) {
2931 return false;
2932 }
2933
2934 const StructType& str = env_.types->structType(*typeIndex);
2935 const ValType rttType = ValType::fromRtt(*typeIndex, RttDepthNone);
2936
2937 if (!popWithType(rttType, rtt)) {
2938 return false;
2939 }
2940
2941 if (!argValues->resize(str.fields_.length())) {
2942 return false;
2943 }
2944
2945 static_assert(MaxStructFields <= INT32_MAX, "Or we iloop below");
2946
2947 for (int32_t i = str.fields_.length() - 1; i >= 0; i--) {
2948 if (!popWithType(str.fields_[i].type.widenToValType(), &(*argValues)[i])) {
2949 return false;
2950 }
2951 }
2952
2953 return push(RefType::fromTypeIndex(*typeIndex, false));
2954 }
2955
2956 template <typename Policy>
readStructNewDefaultWithRtt(uint32_t * typeIndex,Value * rtt)2957 inline bool OpIter<Policy>::readStructNewDefaultWithRtt(uint32_t* typeIndex,
2958 Value* rtt) {
2959 MOZ_ASSERT(Classify(op_) == OpKind::StructNewDefaultWithRtt);
2960
2961 if (!readStructTypeIndex(typeIndex)) {
2962 return false;
2963 }
2964
2965 const StructType& str = env_.types->structType(*typeIndex);
2966 const ValType rttType = ValType::fromRtt(*typeIndex, RttDepthNone);
2967
2968 if (!popWithType(rttType, rtt)) {
2969 return false;
2970 }
2971
2972 if (!str.isDefaultable()) {
2973 return fail("struct must be defaultable");
2974 }
2975
2976 return push(RefType::fromTypeIndex(*typeIndex, false));
2977 }
2978
2979 template <typename Policy>
readStructGet(uint32_t * typeIndex,uint32_t * fieldIndex,FieldExtension fieldExtension,Value * ptr)2980 inline bool OpIter<Policy>::readStructGet(uint32_t* typeIndex,
2981 uint32_t* fieldIndex,
2982 FieldExtension fieldExtension,
2983 Value* ptr) {
2984 MOZ_ASSERT(typeIndex != fieldIndex);
2985 MOZ_ASSERT(Classify(op_) == OpKind::StructGet);
2986
2987 if (!readStructTypeIndex(typeIndex)) {
2988 return false;
2989 }
2990
2991 const StructType& structType = env_.types->structType(*typeIndex);
2992
2993 if (!readFieldIndex(fieldIndex, structType)) {
2994 return false;
2995 }
2996
2997 if (!popWithType(RefType::fromTypeIndex(*typeIndex, true), ptr)) {
2998 return false;
2999 }
3000
3001 FieldType fieldType = structType.fields_[*fieldIndex].type;
3002
3003 if (fieldType.isValType() && fieldExtension != FieldExtension::None) {
3004 return fail("must not specify signedness for unpacked field type");
3005 }
3006
3007 if (!fieldType.isValType() && fieldExtension == FieldExtension::None) {
3008 return fail("must specify signedness for packed field type");
3009 }
3010
3011 return push(fieldType.widenToValType());
3012 }
3013
3014 template <typename Policy>
readStructSet(uint32_t * typeIndex,uint32_t * fieldIndex,Value * ptr,Value * val)3015 inline bool OpIter<Policy>::readStructSet(uint32_t* typeIndex,
3016 uint32_t* fieldIndex, Value* ptr,
3017 Value* val) {
3018 MOZ_ASSERT(typeIndex != fieldIndex);
3019 MOZ_ASSERT(Classify(op_) == OpKind::StructSet);
3020
3021 if (!readStructTypeIndex(typeIndex)) {
3022 return false;
3023 }
3024
3025 const StructType& structType = env_.types->structType(*typeIndex);
3026
3027 if (!readFieldIndex(fieldIndex, structType)) {
3028 return false;
3029 }
3030
3031 if (!popWithType(structType.fields_[*fieldIndex].type.widenToValType(),
3032 val)) {
3033 return false;
3034 }
3035
3036 if (!structType.fields_[*fieldIndex].isMutable) {
3037 return fail("field is not mutable");
3038 }
3039
3040 if (!popWithType(RefType::fromTypeIndex(*typeIndex, true), ptr)) {
3041 return false;
3042 }
3043
3044 return true;
3045 }
3046
3047 template <typename Policy>
readArrayNewWithRtt(uint32_t * typeIndex,Value * rtt,Value * length,Value * argValue)3048 inline bool OpIter<Policy>::readArrayNewWithRtt(uint32_t* typeIndex, Value* rtt,
3049 Value* length,
3050 Value* argValue) {
3051 MOZ_ASSERT(Classify(op_) == OpKind::ArrayNewWithRtt);
3052
3053 if (!readArrayTypeIndex(typeIndex)) {
3054 return false;
3055 }
3056
3057 const ArrayType& arr = env_.types->arrayType(*typeIndex);
3058 const ValType rttType = ValType::fromRtt(*typeIndex, RttDepthNone);
3059
3060 if (!popWithType(rttType, rtt)) {
3061 return false;
3062 }
3063
3064 if (!popWithType(ValType::I32, length)) {
3065 return false;
3066 }
3067
3068 if (!popWithType(arr.elementType_.widenToValType(), argValue)) {
3069 return false;
3070 }
3071
3072 return push(RefType::fromTypeIndex(*typeIndex, false));
3073 }
3074
3075 template <typename Policy>
readArrayNewDefaultWithRtt(uint32_t * typeIndex,Value * rtt,Value * length)3076 inline bool OpIter<Policy>::readArrayNewDefaultWithRtt(uint32_t* typeIndex,
3077 Value* rtt,
3078 Value* length) {
3079 MOZ_ASSERT(Classify(op_) == OpKind::ArrayNewDefaultWithRtt);
3080
3081 if (!readArrayTypeIndex(typeIndex)) {
3082 return false;
3083 }
3084
3085 const ArrayType& arr = env_.types->arrayType(*typeIndex);
3086 const ValType rttType = ValType::fromRtt(*typeIndex, RttDepthNone);
3087
3088 if (!popWithType(rttType, rtt)) {
3089 return false;
3090 }
3091
3092 if (!popWithType(ValType::I32, length)) {
3093 return false;
3094 }
3095
3096 if (!arr.elementType_.isDefaultable()) {
3097 return fail("array must be defaultable");
3098 }
3099
3100 return push(RefType::fromTypeIndex(*typeIndex, false));
3101 }
3102
3103 template <typename Policy>
readArrayGet(uint32_t * typeIndex,FieldExtension extension,Value * index,Value * ptr)3104 inline bool OpIter<Policy>::readArrayGet(uint32_t* typeIndex,
3105 FieldExtension extension, Value* index,
3106 Value* ptr) {
3107 MOZ_ASSERT(Classify(op_) == OpKind::ArrayGet);
3108
3109 if (!readArrayTypeIndex(typeIndex)) {
3110 return false;
3111 }
3112
3113 const ArrayType& arrayType = env_.types->arrayType(*typeIndex);
3114
3115 if (!popWithType(ValType::I32, index)) {
3116 return false;
3117 }
3118
3119 if (!popWithType(RefType::fromTypeIndex(*typeIndex, true), ptr)) {
3120 return false;
3121 }
3122
3123 FieldType fieldType = arrayType.elementType_;
3124
3125 if (fieldType.isValType() && extension != FieldExtension::None) {
3126 return fail("must not specify signedness for unpacked element type");
3127 }
3128
3129 if (!fieldType.isValType() && extension == FieldExtension::None) {
3130 return fail("must specify signedness for packed element type");
3131 }
3132
3133 return push(fieldType.widenToValType());
3134 }
3135
3136 template <typename Policy>
readArraySet(uint32_t * typeIndex,Value * val,Value * index,Value * ptr)3137 inline bool OpIter<Policy>::readArraySet(uint32_t* typeIndex, Value* val,
3138 Value* index, Value* ptr) {
3139 MOZ_ASSERT(Classify(op_) == OpKind::ArraySet);
3140
3141 if (!readArrayTypeIndex(typeIndex)) {
3142 return false;
3143 }
3144
3145 const ArrayType& arrayType = env_.types->arrayType(*typeIndex);
3146
3147 if (!arrayType.isMutable_) {
3148 return fail("array is not mutable");
3149 }
3150
3151 if (!popWithType(arrayType.elementType_.widenToValType(), val)) {
3152 return false;
3153 }
3154
3155 if (!popWithType(ValType::I32, index)) {
3156 return false;
3157 }
3158
3159 if (!popWithType(RefType::fromTypeIndex(*typeIndex, true), ptr)) {
3160 return false;
3161 }
3162
3163 return true;
3164 }
3165
3166 template <typename Policy>
readArrayLen(uint32_t * typeIndex,Value * ptr)3167 inline bool OpIter<Policy>::readArrayLen(uint32_t* typeIndex, Value* ptr) {
3168 MOZ_ASSERT(Classify(op_) == OpKind::ArrayLen);
3169
3170 if (!readArrayTypeIndex(typeIndex)) {
3171 return false;
3172 }
3173
3174 if (!popWithType(RefType::fromTypeIndex(*typeIndex, true), ptr)) {
3175 return false;
3176 }
3177
3178 return push(ValType::I32);
3179 }
3180
3181 template <typename Policy>
readRttCanon(ValType * rttType)3182 inline bool OpIter<Policy>::readRttCanon(ValType* rttType) {
3183 MOZ_ASSERT(Classify(op_) == OpKind::RttCanon);
3184
3185 uint32_t typeIndex;
3186 if (!readGcTypeIndex(&typeIndex)) {
3187 return false;
3188 }
3189
3190 *rttType = ValType::fromRtt(typeIndex, 0);
3191 return push(*rttType);
3192 }
3193
3194 template <typename Policy>
readRttSub(Value * parentRtt,uint32_t * rttSubTypeIndex)3195 inline bool OpIter<Policy>::readRttSub(Value* parentRtt,
3196 uint32_t* rttSubTypeIndex) {
3197 MOZ_ASSERT(Classify(op_) == OpKind::RttSub);
3198
3199 if (!readGcTypeIndex(rttSubTypeIndex)) {
3200 return false;
3201 }
3202
3203 uint32_t rttParentTypeIndex;
3204 uint32_t rttParentDepth;
3205 if (!popWithRttType(parentRtt, &rttParentTypeIndex, &rttParentDepth)) {
3206 return false;
3207 }
3208
3209 if (!checkIsSubtypeOf(*rttSubTypeIndex, rttParentTypeIndex)) {
3210 return false;
3211 }
3212
3213 uint32_t subRttDepth;
3214 if (rttParentDepth == RttDepthNone) {
3215 subRttDepth = RttDepthNone;
3216 } else {
3217 if (rttParentDepth >= MaxRttDepth) {
3218 return fail("rtt depth is too deep");
3219 }
3220 subRttDepth = rttParentDepth + 1;
3221 }
3222 return push(ValType::fromRtt(*rttSubTypeIndex, subRttDepth));
3223 }
3224
3225 template <typename Policy>
readRefTest(Value * rtt,uint32_t * rttTypeIndex,uint32_t * rttDepth,Value * ref)3226 inline bool OpIter<Policy>::readRefTest(Value* rtt, uint32_t* rttTypeIndex,
3227 uint32_t* rttDepth, Value* ref) {
3228 MOZ_ASSERT(Classify(op_) == OpKind::RefTest);
3229
3230 if (!popWithRttType(rtt, rttTypeIndex, rttDepth)) {
3231 return false;
3232 }
3233 if (!popWithType(RefType::eq(), ref)) {
3234 return false;
3235 }
3236 return push(ValType(ValType::I32));
3237 }
3238
3239 template <typename Policy>
readRefCast(Value * rtt,uint32_t * rttTypeIndex,uint32_t * rttDepth,Value * ref)3240 inline bool OpIter<Policy>::readRefCast(Value* rtt, uint32_t* rttTypeIndex,
3241 uint32_t* rttDepth, Value* ref) {
3242 MOZ_ASSERT(Classify(op_) == OpKind::RefCast);
3243
3244 if (!popWithRttType(rtt, rttTypeIndex, rttDepth)) {
3245 return false;
3246 }
3247 if (!popWithType(RefType::eq(), ref)) {
3248 return false;
3249 }
3250 return push(RefType::fromTypeIndex(*rttTypeIndex, false));
3251 }
3252
3253 template <typename Policy>
readBrOnCast(uint32_t * relativeDepth,Value * rtt,uint32_t * rttTypeIndex,uint32_t * rttDepth,ResultType * branchTargetType,ValueVector * values)3254 inline bool OpIter<Policy>::readBrOnCast(uint32_t* relativeDepth, Value* rtt,
3255 uint32_t* rttTypeIndex,
3256 uint32_t* rttDepth,
3257 ResultType* branchTargetType,
3258 ValueVector* values) {
3259 MOZ_ASSERT(Classify(op_) == OpKind::BrOnCast);
3260
3261 if (!readVarU32(relativeDepth)) {
3262 return fail("unable to read br_on_cast depth");
3263 }
3264
3265 if (!popWithRttType(rtt, rttTypeIndex, rttDepth)) {
3266 return false;
3267 }
3268
3269 // The casted from type is any subtype of eqref
3270 ValType castedFromType(RefType::eq());
3271
3272 // The casted to type is a non-nullable reference to the type index specified
3273 // by the input rtt on the stack
3274 ValType castedToType(RefType::fromTypeIndex(*rttTypeIndex, false));
3275
3276 return checkCastedBranchValueAndPush(*relativeDepth, castedFromType,
3277 castedToType, branchTargetType, values);
3278 }
3279
3280 #endif // ENABLE_WASM_GC
3281
3282 #ifdef ENABLE_WASM_SIMD
3283
3284 template <typename Policy>
readLaneIndex(uint32_t inputLanes,uint32_t * laneIndex)3285 inline bool OpIter<Policy>::readLaneIndex(uint32_t inputLanes,
3286 uint32_t* laneIndex) {
3287 uint8_t tmp;
3288 if (!readFixedU8(&tmp)) {
3289 return false; // Caller signals error
3290 }
3291 if (tmp >= inputLanes) {
3292 return false;
3293 }
3294 *laneIndex = tmp;
3295 return true;
3296 }
3297
3298 template <typename Policy>
readExtractLane(ValType resultType,uint32_t inputLanes,uint32_t * laneIndex,Value * input)3299 inline bool OpIter<Policy>::readExtractLane(ValType resultType,
3300 uint32_t inputLanes,
3301 uint32_t* laneIndex, Value* input) {
3302 MOZ_ASSERT(Classify(op_) == OpKind::ExtractLane);
3303
3304 if (!readLaneIndex(inputLanes, laneIndex)) {
3305 return fail("missing or invalid extract_lane lane index");
3306 }
3307
3308 if (!popWithType(ValType::V128, input)) {
3309 return false;
3310 }
3311
3312 infalliblePush(resultType);
3313
3314 return true;
3315 }
3316
3317 template <typename Policy>
readReplaceLane(ValType operandType,uint32_t inputLanes,uint32_t * laneIndex,Value * baseValue,Value * operand)3318 inline bool OpIter<Policy>::readReplaceLane(ValType operandType,
3319 uint32_t inputLanes,
3320 uint32_t* laneIndex,
3321 Value* baseValue, Value* operand) {
3322 MOZ_ASSERT(Classify(op_) == OpKind::ReplaceLane);
3323
3324 if (!readLaneIndex(inputLanes, laneIndex)) {
3325 return fail("missing or invalid replace_lane lane index");
3326 }
3327
3328 if (!popWithType(operandType, operand)) {
3329 return false;
3330 }
3331
3332 if (!popWithType(ValType::V128, baseValue)) {
3333 return false;
3334 }
3335
3336 infalliblePush(ValType::V128);
3337
3338 return true;
3339 }
3340
3341 template <typename Policy>
readVectorShift(Value * baseValue,Value * shift)3342 inline bool OpIter<Policy>::readVectorShift(Value* baseValue, Value* shift) {
3343 MOZ_ASSERT(Classify(op_) == OpKind::VectorShift);
3344
3345 if (!popWithType(ValType::I32, shift)) {
3346 return false;
3347 }
3348
3349 if (!popWithType(ValType::V128, baseValue)) {
3350 return false;
3351 }
3352
3353 infalliblePush(ValType::V128);
3354
3355 return true;
3356 }
3357
3358 template <typename Policy>
readVectorShuffle(Value * v1,Value * v2,V128 * selectMask)3359 inline bool OpIter<Policy>::readVectorShuffle(Value* v1, Value* v2,
3360 V128* selectMask) {
3361 MOZ_ASSERT(Classify(op_) == OpKind::VectorShuffle);
3362
3363 for (unsigned char& byte : selectMask->bytes) {
3364 uint8_t tmp;
3365 if (!readFixedU8(&tmp)) {
3366 return fail("unable to read shuffle index");
3367 }
3368 if (tmp > 31) {
3369 return fail("shuffle index out of range");
3370 }
3371 byte = tmp;
3372 }
3373
3374 if (!popWithType(ValType::V128, v2)) {
3375 return false;
3376 }
3377
3378 if (!popWithType(ValType::V128, v1)) {
3379 return false;
3380 }
3381
3382 infalliblePush(ValType::V128);
3383
3384 return true;
3385 }
3386
3387 template <typename Policy>
readV128Const(V128 * value)3388 inline bool OpIter<Policy>::readV128Const(V128* value) {
3389 MOZ_ASSERT(Classify(op_) == OpKind::V128);
3390
3391 if (!d_.readV128Const(value)) {
3392 return false;
3393 }
3394
3395 return push(ValType::V128);
3396 }
3397
3398 template <typename Policy>
readLoadSplat(uint32_t byteSize,LinearMemoryAddress<Value> * addr)3399 inline bool OpIter<Policy>::readLoadSplat(uint32_t byteSize,
3400 LinearMemoryAddress<Value>* addr) {
3401 MOZ_ASSERT(Classify(op_) == OpKind::Load);
3402
3403 if (!readLinearMemoryAddress(byteSize, addr)) {
3404 return false;
3405 }
3406
3407 infalliblePush(ValType::V128);
3408
3409 return true;
3410 }
3411
3412 template <typename Policy>
readLoadExtend(LinearMemoryAddress<Value> * addr)3413 inline bool OpIter<Policy>::readLoadExtend(LinearMemoryAddress<Value>* addr) {
3414 MOZ_ASSERT(Classify(op_) == OpKind::Load);
3415
3416 if (!readLinearMemoryAddress(/*byteSize=*/8, addr)) {
3417 return false;
3418 }
3419
3420 infalliblePush(ValType::V128);
3421
3422 return true;
3423 }
3424
3425 template <typename Policy>
readLoadLane(uint32_t byteSize,LinearMemoryAddress<Value> * addr,uint32_t * laneIndex,Value * input)3426 inline bool OpIter<Policy>::readLoadLane(uint32_t byteSize,
3427 LinearMemoryAddress<Value>* addr,
3428 uint32_t* laneIndex, Value* input) {
3429 MOZ_ASSERT(Classify(op_) == OpKind::LoadLane);
3430
3431 if (!popWithType(ValType::V128, input)) {
3432 return false;
3433 }
3434
3435 if (!readLinearMemoryAddress(byteSize, addr)) {
3436 return false;
3437 }
3438
3439 uint32_t inputLanes = 16 / byteSize;
3440 if (!readLaneIndex(inputLanes, laneIndex)) {
3441 return fail("missing or invalid load_lane lane index");
3442 }
3443
3444 infalliblePush(ValType::V128);
3445
3446 return true;
3447 }
3448
3449 template <typename Policy>
readStoreLane(uint32_t byteSize,LinearMemoryAddress<Value> * addr,uint32_t * laneIndex,Value * input)3450 inline bool OpIter<Policy>::readStoreLane(uint32_t byteSize,
3451 LinearMemoryAddress<Value>* addr,
3452 uint32_t* laneIndex, Value* input) {
3453 MOZ_ASSERT(Classify(op_) == OpKind::StoreLane);
3454
3455 if (!popWithType(ValType::V128, input)) {
3456 return false;
3457 }
3458
3459 if (!readLinearMemoryAddress(byteSize, addr)) {
3460 return false;
3461 }
3462
3463 uint32_t inputLanes = 16 / byteSize;
3464 if (!readLaneIndex(inputLanes, laneIndex)) {
3465 return fail("missing or invalid store_lane lane index");
3466 }
3467
3468 return true;
3469 }
3470
3471 #endif // ENABLE_WASM_SIMD
3472
3473 template <typename Policy>
readIntrinsic(const Intrinsic ** intrinsic,ValueVector * params)3474 inline bool OpIter<Policy>::readIntrinsic(const Intrinsic** intrinsic,
3475 ValueVector* params) {
3476 MOZ_ASSERT(Classify(op_) == OpKind::Intrinsic);
3477
3478 uint32_t id;
3479 if (!d_.readVarU32(&id)) {
3480 return false;
3481 }
3482
3483 if (id >= uint32_t(IntrinsicId::Limit)) {
3484 return fail("intrinsic index out of range");
3485 }
3486
3487 *intrinsic = &Intrinsic::getFromId(IntrinsicId(id));
3488
3489 if (!env_.usesMemory()) {
3490 return fail("can't touch memory without memory");
3491 }
3492 return popWithTypes((*intrinsic)->params, params);
3493 }
3494
3495 } // namespace wasm
3496 } // namespace js
3497
3498 namespace mozilla {
3499
3500 // Specialize IsPod for the Nothing specializations.
3501 template <>
3502 struct IsPod<js::wasm::TypeAndValueT<Nothing>> : std::true_type {};
3503 template <>
3504 struct IsPod<js::wasm::ControlStackEntry<Nothing>> : std::true_type {};
3505
3506 } // namespace mozilla
3507
3508 #endif // wasm_op_iter_h
3509