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 2014 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 #include "wasm/AsmJS.h"
20 
21 #include "mozilla/Attributes.h"
22 #include "mozilla/Compression.h"
23 #include "mozilla/MathAlgorithms.h"
24 #include "mozilla/Maybe.h"
25 #include "mozilla/ScopeExit.h"
26 #include "mozilla/Sprintf.h"  // SprintfLiteral
27 #include "mozilla/Utf8.h"     // mozilla::Utf8Unit
28 #include "mozilla/Variant.h"
29 
30 #include <algorithm>
31 #include <new>
32 
33 #include "jsmath.h"
34 
35 #include "frontend/FunctionSyntaxKind.h"  // FunctionSyntaxKind
36 #include "frontend/ParseNode.h"
37 #include "frontend/Parser.h"
38 #include "frontend/ParserAtom.h"     // ParserAtomsTable, TaggedParserAtomIndex
39 #include "frontend/SharedContext.h"  // TopLevelFunction
40 #include "frontend/TaggedParserAtomIndexHasher.h"  // TaggedParserAtomIndexHasher
41 #include "gc/Policy.h"
42 #include "js/BuildId.h"               // JS::BuildIdCharVector
43 #include "js/friend/ErrorMessages.h"  // JSMSG_*
44 #include "js/MemoryMetrics.h"
45 #include "js/Printf.h"
46 #include "js/ScalarType.h"  // js::Scalar::Type
47 #include "js/SourceText.h"
48 #include "js/StableStringChars.h"
49 #include "js/Wrapper.h"
50 #include "util/DifferentialTesting.h"
51 #include "util/StringBuffer.h"
52 #include "util/Text.h"
53 #include "vm/ErrorReporting.h"
54 #include "vm/FunctionFlags.h"          // js::FunctionFlags
55 #include "vm/GeneratorAndAsyncKind.h"  // js::GeneratorKind, js::FunctionAsyncKind
56 #include "vm/SelfHosting.h"
57 #include "vm/Time.h"
58 #include "vm/TypedArrayObject.h"
59 #include "vm/Warnings.h"  // js::WarnNumberASCII
60 #include "wasm/WasmCompile.h"
61 #include "wasm/WasmGenerator.h"
62 #include "wasm/WasmInstance.h"
63 #include "wasm/WasmIonCompile.h"
64 #include "wasm/WasmJS.h"
65 #include "wasm/WasmSerialize.h"
66 #include "wasm/WasmValidate.h"
67 
68 #include "frontend/SharedContext-inl.h"
69 #include "vm/ArrayBufferObject-inl.h"
70 #include "vm/JSObject-inl.h"
71 
72 using namespace js;
73 using namespace js::frontend;
74 using namespace js::jit;
75 using namespace js::wasm;
76 
77 using JS::AsmJSOption;
78 using JS::AutoStableStringChars;
79 using JS::GenericNaN;
80 using JS::SourceOwnership;
81 using JS::SourceText;
82 using mozilla::Abs;
83 using mozilla::AsVariant;
84 using mozilla::CeilingLog2;
85 using mozilla::HashGeneric;
86 using mozilla::IsNaN;
87 using mozilla::IsNegativeZero;
88 using mozilla::IsPositiveZero;
89 using mozilla::IsPowerOfTwo;
90 using mozilla::Nothing;
91 using mozilla::PodZero;
92 using mozilla::PositiveInfinity;
93 using mozilla::Some;
94 using mozilla::Utf8Unit;
95 using mozilla::Compression::LZ4;
96 
97 /*****************************************************************************/
98 
99 // A wasm module can either use no memory, a unshared memory (ArrayBuffer) or
100 // shared memory (SharedArrayBuffer).
101 
102 enum class MemoryUsage { None = false, Unshared = 1, Shared = 2 };
103 
104 // The asm.js valid heap lengths are precisely the WASM valid heap lengths for
105 // ARM greater or equal to MinHeapLength
106 static const size_t MinHeapLength = PageSize;
107 // An asm.js heap can in principle be up to INT32_MAX bytes but requirements
108 // on the format restrict it further to the largest pseudo-ARM-immediate.
109 // See IsValidAsmJSHeapLength().
110 static const uint64_t MaxHeapLength = 0x7f000000;
111 
RoundUpToNextValidAsmJSHeapLength(uint64_t length)112 static uint64_t RoundUpToNextValidAsmJSHeapLength(uint64_t length) {
113   if (length <= MinHeapLength) {
114     return MinHeapLength;
115   }
116 
117   return wasm::RoundUpToNextValidARMImmediate(length);
118 }
119 
DivideRoundingUp(uint64_t a,uint64_t b)120 static uint64_t DivideRoundingUp(uint64_t a, uint64_t b) {
121   return (a + (b - 1)) / b;
122 }
123 
124 /*****************************************************************************/
125 // asm.js module object
126 
127 // The asm.js spec recognizes this set of builtin Math functions.
128 enum AsmJSMathBuiltinFunction {
129   AsmJSMathBuiltin_sin,
130   AsmJSMathBuiltin_cos,
131   AsmJSMathBuiltin_tan,
132   AsmJSMathBuiltin_asin,
133   AsmJSMathBuiltin_acos,
134   AsmJSMathBuiltin_atan,
135   AsmJSMathBuiltin_ceil,
136   AsmJSMathBuiltin_floor,
137   AsmJSMathBuiltin_exp,
138   AsmJSMathBuiltin_log,
139   AsmJSMathBuiltin_pow,
140   AsmJSMathBuiltin_sqrt,
141   AsmJSMathBuiltin_abs,
142   AsmJSMathBuiltin_atan2,
143   AsmJSMathBuiltin_imul,
144   AsmJSMathBuiltin_fround,
145   AsmJSMathBuiltin_min,
146   AsmJSMathBuiltin_max,
147   AsmJSMathBuiltin_clz32
148 };
149 
150 // LitValPOD is a restricted version of LitVal suitable for asm.js that is
151 // always POD.
152 
153 struct LitValPOD {
154   PackedTypeCode valType_;
155   union U {
156     uint32_t u32_;
157     uint64_t u64_;
158     float f32_;
159     double f64_;
160   } u;
161 
162   LitValPOD() = default;
163 
LitValPODLitValPOD164   explicit LitValPOD(uint32_t u32) : valType_(ValType(ValType::I32).packed()) {
165     u.u32_ = u32;
166   }
LitValPODLitValPOD167   explicit LitValPOD(uint64_t u64) : valType_(ValType(ValType::I64).packed()) {
168     u.u64_ = u64;
169   }
170 
LitValPODLitValPOD171   explicit LitValPOD(float f32) : valType_(ValType(ValType::F32).packed()) {
172     u.f32_ = f32;
173   }
LitValPODLitValPOD174   explicit LitValPOD(double f64) : valType_(ValType(ValType::F64).packed()) {
175     u.f64_ = f64;
176   }
177 
asLitValLitValPOD178   LitVal asLitVal() const {
179     switch (valType_.typeCode()) {
180       case TypeCode::I32:
181         return LitVal(u.u32_);
182       case TypeCode::I64:
183         return LitVal(u.u64_);
184       case TypeCode::F32:
185         return LitVal(u.f32_);
186       case TypeCode::F64:
187         return LitVal(u.f64_);
188       default:
189         MOZ_CRASH("Can't happen");
190     }
191   }
192 };
193 
194 static_assert(std::is_pod_v<LitValPOD>,
195               "must be POD to be simply serialized/deserialized");
196 
197 // An AsmJSGlobal represents a JS global variable in the asm.js module function.
198 class AsmJSGlobal {
199  public:
200   enum Which {
201     Variable,
202     FFI,
203     ArrayView,
204     ArrayViewCtor,
205     MathBuiltinFunction,
206     Constant
207   };
208   enum VarInitKind { InitConstant, InitImport };
209   enum ConstantKind { GlobalConstant, MathConstant };
210 
211  private:
212   struct CacheablePod {
213     Which which_;
214     union V {
215       struct {
216         VarInitKind initKind_;
217         union U {
218           PackedTypeCode importValType_;
219           LitValPOD val_;
220         } u;
221       } var;
222       uint32_t ffiIndex_;
223       Scalar::Type viewType_;
224       AsmJSMathBuiltinFunction mathBuiltinFunc_;
225       struct {
226         ConstantKind kind_;
227         double value_;
228       } constant;
229     } u;
230   } pod;
231   CacheableChars field_;
232 
233   friend class ModuleValidatorShared;
234   template <typename Unit>
235   friend class ModuleValidator;
236 
237  public:
238   AsmJSGlobal() = default;
AsmJSGlobal(Which which,UniqueChars field)239   AsmJSGlobal(Which which, UniqueChars field) {
240     mozilla::PodZero(&pod);  // zero padding for Valgrind
241     pod.which_ = which;
242     field_ = std::move(field);
243   }
field() const244   const char* field() const { return field_.get(); }
which() const245   Which which() const { return pod.which_; }
varInitKind() const246   VarInitKind varInitKind() const {
247     MOZ_ASSERT(pod.which_ == Variable);
248     return pod.u.var.initKind_;
249   }
varInitVal() const250   LitValPOD varInitVal() const {
251     MOZ_ASSERT(pod.which_ == Variable);
252     MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
253     return pod.u.var.u.val_;
254   }
varInitImportType() const255   ValType varInitImportType() const {
256     MOZ_ASSERT(pod.which_ == Variable);
257     MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
258     return ValType(pod.u.var.u.importValType_);
259   }
ffiIndex() const260   uint32_t ffiIndex() const {
261     MOZ_ASSERT(pod.which_ == FFI);
262     return pod.u.ffiIndex_;
263   }
264   // When a view is created from an imported constructor:
265   //   var I32 = stdlib.Int32Array;
266   //   var i32 = new I32(buffer);
267   // the second import has nothing to validate and thus has a null field.
viewType() const268   Scalar::Type viewType() const {
269     MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
270     return pod.u.viewType_;
271   }
mathBuiltinFunction() const272   AsmJSMathBuiltinFunction mathBuiltinFunction() const {
273     MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
274     return pod.u.mathBuiltinFunc_;
275   }
constantKind() const276   ConstantKind constantKind() const {
277     MOZ_ASSERT(pod.which_ == Constant);
278     return pod.u.constant.kind_;
279   }
constantValue() const280   double constantValue() const {
281     MOZ_ASSERT(pod.which_ == Constant);
282     return pod.u.constant.value_;
283   }
284 };
285 
286 using AsmJSGlobalVector = Vector<AsmJSGlobal, 0, SystemAllocPolicy>;
287 
288 // An AsmJSImport is slightly different than an asm.js FFI function: a single
289 // asm.js FFI function can be called with many different signatures. When
290 // compiled to wasm, each unique FFI function paired with signature generates a
291 // wasm import.
292 class AsmJSImport {
293   uint32_t ffiIndex_;
294 
295  public:
296   AsmJSImport() = default;
AsmJSImport(uint32_t ffiIndex)297   explicit AsmJSImport(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
ffiIndex() const298   uint32_t ffiIndex() const { return ffiIndex_; }
299 };
300 
301 using AsmJSImportVector = Vector<AsmJSImport, 0, SystemAllocPolicy>;
302 
303 // An AsmJSExport logically extends Export with the extra information needed for
304 // an asm.js exported function, viz., the offsets in module's source chars in
305 // case the function is toString()ed.
306 class AsmJSExport {
307   uint32_t funcIndex_ = 0;
308 
309   // All fields are treated as cacheable POD:
310   uint32_t startOffsetInModule_ = 0;  // Store module-start-relative offsets
311   uint32_t endOffsetInModule_ = 0;    // so preserved by serialization.
312 
313  public:
314   AsmJSExport() = default;
AsmJSExport(uint32_t funcIndex,uint32_t startOffsetInModule,uint32_t endOffsetInModule)315   AsmJSExport(uint32_t funcIndex, uint32_t startOffsetInModule,
316               uint32_t endOffsetInModule)
317       : funcIndex_(funcIndex),
318         startOffsetInModule_(startOffsetInModule),
319         endOffsetInModule_(endOffsetInModule) {}
funcIndex() const320   uint32_t funcIndex() const { return funcIndex_; }
startOffsetInModule() const321   uint32_t startOffsetInModule() const { return startOffsetInModule_; }
endOffsetInModule() const322   uint32_t endOffsetInModule() const { return endOffsetInModule_; }
323 };
324 
325 using AsmJSExportVector = Vector<AsmJSExport, 0, SystemAllocPolicy>;
326 
327 // Holds the immutable guts of an AsmJSModule.
328 //
329 // AsmJSMetadata is built incrementally by ModuleValidator and then shared
330 // immutably between AsmJSModules.
331 
332 struct AsmJSMetadataCacheablePod {
333   uint32_t numFFIs = 0;
334   uint32_t srcLength = 0;
335   uint32_t srcLengthWithRightBrace = 0;
336 
337   AsmJSMetadataCacheablePod() = default;
338 };
339 
340 struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod {
341   AsmJSGlobalVector asmJSGlobals;
342   AsmJSImportVector asmJSImports;
343   AsmJSExportVector asmJSExports;
344   CacheableCharsVector asmJSFuncNames;
345   CacheableChars globalArgumentName;
346   CacheableChars importArgumentName;
347   CacheableChars bufferArgumentName;
348 
349   // These values are not serialized since they are relative to the
350   // containing script which can be different between serialization and
351   // deserialization contexts. Thus, they must be set explicitly using the
352   // ambient Parser/ScriptSource after deserialization.
353   //
354   // srcStart refers to the offset in the ScriptSource to the beginning of
355   // the asm.js module function. If the function has been created with the
356   // Function constructor, this will be the first character in the function
357   // source. Otherwise, it will be the opening parenthesis of the arguments
358   // list.
359   uint32_t toStringStart;
360   uint32_t srcStart;
361   bool strict;
362   RefPtr<ScriptSource> source;
363 
srcEndBeforeCurlyjs::AsmJSMetadata364   uint32_t srcEndBeforeCurly() const { return srcStart + srcLength; }
srcEndAfterCurlyjs::AsmJSMetadata365   uint32_t srcEndAfterCurly() const {
366     return srcStart + srcLengthWithRightBrace;
367   }
368 
AsmJSMetadatajs::AsmJSMetadata369   AsmJSMetadata()
370       : Metadata(ModuleKind::AsmJS),
371         toStringStart(0),
372         srcStart(0),
373         strict(false) {}
374   ~AsmJSMetadata() override = default;
375 
lookupAsmJSExportjs::AsmJSMetadata376   const AsmJSExport& lookupAsmJSExport(uint32_t funcIndex) const {
377     // The AsmJSExportVector isn't stored in sorted order so do a linear
378     // search. This is for the super-cold and already-expensive toString()
379     // path and the number of exports is generally small.
380     for (const AsmJSExport& exp : asmJSExports) {
381       if (exp.funcIndex() == funcIndex) {
382         return exp;
383       }
384     }
385     MOZ_CRASH("missing asm.js func export");
386   }
387 
mutedErrorsjs::AsmJSMetadata388   bool mutedErrors() const override { return source->mutedErrors(); }
displayURLjs::AsmJSMetadata389   const char16_t* displayURL() const override {
390     return source->hasDisplayURL() ? source->displayURL() : nullptr;
391   }
maybeScriptSourcejs::AsmJSMetadata392   ScriptSource* maybeScriptSource() const override { return source.get(); }
getFuncNamejs::AsmJSMetadata393   bool getFuncName(NameContext ctx, uint32_t funcIndex,
394                    UTF8Bytes* name) const override {
395     const char* p = asmJSFuncNames[funcIndex].get();
396     if (!p) {
397       return true;
398     }
399     return name->append(p, strlen(p));
400   }
401 
podjs::AsmJSMetadata402   AsmJSMetadataCacheablePod& pod() { return *this; }
podjs::AsmJSMetadata403   const AsmJSMetadataCacheablePod& pod() const { return *this; }
404 };
405 
406 using MutableAsmJSMetadata = RefPtr<AsmJSMetadata>;
407 
408 /*****************************************************************************/
409 // ParseNode utilities
410 
NextNode(ParseNode * pn)411 static inline ParseNode* NextNode(ParseNode* pn) { return pn->pn_next; }
412 
UnaryKid(ParseNode * pn)413 static inline ParseNode* UnaryKid(ParseNode* pn) {
414   return pn->as<UnaryNode>().kid();
415 }
416 
BinaryRight(ParseNode * pn)417 static inline ParseNode* BinaryRight(ParseNode* pn) {
418   return pn->as<BinaryNode>().right();
419 }
420 
BinaryLeft(ParseNode * pn)421 static inline ParseNode* BinaryLeft(ParseNode* pn) {
422   return pn->as<BinaryNode>().left();
423 }
424 
ReturnExpr(ParseNode * pn)425 static inline ParseNode* ReturnExpr(ParseNode* pn) {
426   MOZ_ASSERT(pn->isKind(ParseNodeKind::ReturnStmt));
427   return UnaryKid(pn);
428 }
429 
TernaryKid1(ParseNode * pn)430 static inline ParseNode* TernaryKid1(ParseNode* pn) {
431   return pn->as<TernaryNode>().kid1();
432 }
433 
TernaryKid2(ParseNode * pn)434 static inline ParseNode* TernaryKid2(ParseNode* pn) {
435   return pn->as<TernaryNode>().kid2();
436 }
437 
TernaryKid3(ParseNode * pn)438 static inline ParseNode* TernaryKid3(ParseNode* pn) {
439   return pn->as<TernaryNode>().kid3();
440 }
441 
ListHead(ParseNode * pn)442 static inline ParseNode* ListHead(ParseNode* pn) {
443   return pn->as<ListNode>().head();
444 }
445 
ListLength(ParseNode * pn)446 static inline unsigned ListLength(ParseNode* pn) {
447   return pn->as<ListNode>().count();
448 }
449 
CallCallee(ParseNode * pn)450 static inline ParseNode* CallCallee(ParseNode* pn) {
451   MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
452   return BinaryLeft(pn);
453 }
454 
CallArgListLength(ParseNode * pn)455 static inline unsigned CallArgListLength(ParseNode* pn) {
456   MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
457   return ListLength(BinaryRight(pn));
458 }
459 
CallArgList(ParseNode * pn)460 static inline ParseNode* CallArgList(ParseNode* pn) {
461   MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
462   return ListHead(BinaryRight(pn));
463 }
464 
VarListHead(ParseNode * pn)465 static inline ParseNode* VarListHead(ParseNode* pn) {
466   MOZ_ASSERT(pn->isKind(ParseNodeKind::VarStmt) ||
467              pn->isKind(ParseNodeKind::ConstDecl));
468   return ListHead(pn);
469 }
470 
IsDefaultCase(ParseNode * pn)471 static inline bool IsDefaultCase(ParseNode* pn) {
472   return pn->as<CaseClause>().isDefault();
473 }
474 
CaseExpr(ParseNode * pn)475 static inline ParseNode* CaseExpr(ParseNode* pn) {
476   return pn->as<CaseClause>().caseExpression();
477 }
478 
CaseBody(ParseNode * pn)479 static inline ParseNode* CaseBody(ParseNode* pn) {
480   return pn->as<CaseClause>().statementList();
481 }
482 
BinaryOpLeft(ParseNode * pn)483 static inline ParseNode* BinaryOpLeft(ParseNode* pn) {
484   MOZ_ASSERT(pn->isBinaryOperation());
485   MOZ_ASSERT(pn->as<ListNode>().count() == 2);
486   return ListHead(pn);
487 }
488 
BinaryOpRight(ParseNode * pn)489 static inline ParseNode* BinaryOpRight(ParseNode* pn) {
490   MOZ_ASSERT(pn->isBinaryOperation());
491   MOZ_ASSERT(pn->as<ListNode>().count() == 2);
492   return NextNode(ListHead(pn));
493 }
494 
BitwiseLeft(ParseNode * pn)495 static inline ParseNode* BitwiseLeft(ParseNode* pn) { return BinaryOpLeft(pn); }
496 
BitwiseRight(ParseNode * pn)497 static inline ParseNode* BitwiseRight(ParseNode* pn) {
498   return BinaryOpRight(pn);
499 }
500 
MultiplyLeft(ParseNode * pn)501 static inline ParseNode* MultiplyLeft(ParseNode* pn) {
502   MOZ_ASSERT(pn->isKind(ParseNodeKind::MulExpr));
503   return BinaryOpLeft(pn);
504 }
505 
MultiplyRight(ParseNode * pn)506 static inline ParseNode* MultiplyRight(ParseNode* pn) {
507   MOZ_ASSERT(pn->isKind(ParseNodeKind::MulExpr));
508   return BinaryOpRight(pn);
509 }
510 
AddSubLeft(ParseNode * pn)511 static inline ParseNode* AddSubLeft(ParseNode* pn) {
512   MOZ_ASSERT(pn->isKind(ParseNodeKind::AddExpr) ||
513              pn->isKind(ParseNodeKind::SubExpr));
514   return BinaryOpLeft(pn);
515 }
516 
AddSubRight(ParseNode * pn)517 static inline ParseNode* AddSubRight(ParseNode* pn) {
518   MOZ_ASSERT(pn->isKind(ParseNodeKind::AddExpr) ||
519              pn->isKind(ParseNodeKind::SubExpr));
520   return BinaryOpRight(pn);
521 }
522 
DivOrModLeft(ParseNode * pn)523 static inline ParseNode* DivOrModLeft(ParseNode* pn) {
524   MOZ_ASSERT(pn->isKind(ParseNodeKind::DivExpr) ||
525              pn->isKind(ParseNodeKind::ModExpr));
526   return BinaryOpLeft(pn);
527 }
528 
DivOrModRight(ParseNode * pn)529 static inline ParseNode* DivOrModRight(ParseNode* pn) {
530   MOZ_ASSERT(pn->isKind(ParseNodeKind::DivExpr) ||
531              pn->isKind(ParseNodeKind::ModExpr));
532   return BinaryOpRight(pn);
533 }
534 
ComparisonLeft(ParseNode * pn)535 static inline ParseNode* ComparisonLeft(ParseNode* pn) {
536   return BinaryOpLeft(pn);
537 }
538 
ComparisonRight(ParseNode * pn)539 static inline ParseNode* ComparisonRight(ParseNode* pn) {
540   return BinaryOpRight(pn);
541 }
542 
IsExpressionStatement(ParseNode * pn)543 static inline bool IsExpressionStatement(ParseNode* pn) {
544   return pn->isKind(ParseNodeKind::ExpressionStmt);
545 }
546 
ExpressionStatementExpr(ParseNode * pn)547 static inline ParseNode* ExpressionStatementExpr(ParseNode* pn) {
548   MOZ_ASSERT(pn->isKind(ParseNodeKind::ExpressionStmt));
549   return UnaryKid(pn);
550 }
551 
LoopControlMaybeLabel(ParseNode * pn)552 static inline TaggedParserAtomIndex LoopControlMaybeLabel(ParseNode* pn) {
553   MOZ_ASSERT(pn->isKind(ParseNodeKind::BreakStmt) ||
554              pn->isKind(ParseNodeKind::ContinueStmt));
555   return pn->as<LoopControlStatement>().label();
556 }
557 
LabeledStatementLabel(ParseNode * pn)558 static inline TaggedParserAtomIndex LabeledStatementLabel(ParseNode* pn) {
559   return pn->as<LabeledStatement>().label();
560 }
561 
LabeledStatementStatement(ParseNode * pn)562 static inline ParseNode* LabeledStatementStatement(ParseNode* pn) {
563   return pn->as<LabeledStatement>().statement();
564 }
565 
NumberNodeValue(ParseNode * pn)566 static double NumberNodeValue(ParseNode* pn) {
567   return pn->as<NumericLiteral>().value();
568 }
569 
NumberNodeHasFrac(ParseNode * pn)570 static bool NumberNodeHasFrac(ParseNode* pn) {
571   return pn->as<NumericLiteral>().decimalPoint() == HasDecimal;
572 }
573 
DotBase(ParseNode * pn)574 static ParseNode* DotBase(ParseNode* pn) {
575   return &pn->as<PropertyAccess>().expression();
576 }
577 
DotMember(ParseNode * pn)578 static TaggedParserAtomIndex DotMember(ParseNode* pn) {
579   return pn->as<PropertyAccess>().name();
580 }
581 
ElemBase(ParseNode * pn)582 static ParseNode* ElemBase(ParseNode* pn) {
583   return &pn->as<PropertyByValue>().expression();
584 }
585 
ElemIndex(ParseNode * pn)586 static ParseNode* ElemIndex(ParseNode* pn) {
587   return &pn->as<PropertyByValue>().key();
588 }
589 
FunctionName(FunctionNode * funNode)590 static inline TaggedParserAtomIndex FunctionName(FunctionNode* funNode) {
591   if (auto name = funNode->funbox()->explicitName()) {
592     return name;
593   }
594   return TaggedParserAtomIndex::null();
595 }
596 
FunctionStatementList(FunctionNode * funNode)597 static inline ParseNode* FunctionStatementList(FunctionNode* funNode) {
598   MOZ_ASSERT(funNode->body()->isKind(ParseNodeKind::ParamsBody));
599   LexicalScopeNode* last =
600       &funNode->body()->as<ListNode>().last()->as<LexicalScopeNode>();
601   MOZ_ASSERT(last->isEmptyScope());
602   ParseNode* body = last->scopeBody();
603   MOZ_ASSERT(body->isKind(ParseNodeKind::StatementList));
604   return body;
605 }
606 
IsNormalObjectField(ParseNode * pn)607 static inline bool IsNormalObjectField(ParseNode* pn) {
608   return pn->isKind(ParseNodeKind::PropertyDefinition) &&
609          pn->as<PropertyDefinition>().accessorType() == AccessorType::None &&
610          BinaryLeft(pn)->isKind(ParseNodeKind::ObjectPropertyName);
611 }
612 
ObjectNormalFieldName(ParseNode * pn)613 static inline TaggedParserAtomIndex ObjectNormalFieldName(ParseNode* pn) {
614   MOZ_ASSERT(IsNormalObjectField(pn));
615   MOZ_ASSERT(BinaryLeft(pn)->isKind(ParseNodeKind::ObjectPropertyName));
616   return BinaryLeft(pn)->as<NameNode>().atom();
617 }
618 
ObjectNormalFieldInitializer(ParseNode * pn)619 static inline ParseNode* ObjectNormalFieldInitializer(ParseNode* pn) {
620   MOZ_ASSERT(IsNormalObjectField(pn));
621   return BinaryRight(pn);
622 }
623 
IsUseOfName(ParseNode * pn,TaggedParserAtomIndex name)624 static inline bool IsUseOfName(ParseNode* pn, TaggedParserAtomIndex name) {
625   return pn->isName(name);
626 }
627 
IsIgnoredDirectiveName(JSContext * cx,TaggedParserAtomIndex atom)628 static inline bool IsIgnoredDirectiveName(JSContext* cx,
629                                           TaggedParserAtomIndex atom) {
630   return atom != TaggedParserAtomIndex::WellKnown::useStrict();
631 }
632 
IsIgnoredDirective(JSContext * cx,ParseNode * pn)633 static inline bool IsIgnoredDirective(JSContext* cx, ParseNode* pn) {
634   return pn->isKind(ParseNodeKind::ExpressionStmt) &&
635          UnaryKid(pn)->isKind(ParseNodeKind::StringExpr) &&
636          IsIgnoredDirectiveName(cx, UnaryKid(pn)->as<NameNode>().atom());
637 }
638 
IsEmptyStatement(ParseNode * pn)639 static inline bool IsEmptyStatement(ParseNode* pn) {
640   return pn->isKind(ParseNodeKind::EmptyStmt);
641 }
642 
SkipEmptyStatements(ParseNode * pn)643 static inline ParseNode* SkipEmptyStatements(ParseNode* pn) {
644   while (pn && IsEmptyStatement(pn)) {
645     pn = pn->pn_next;
646   }
647   return pn;
648 }
649 
NextNonEmptyStatement(ParseNode * pn)650 static inline ParseNode* NextNonEmptyStatement(ParseNode* pn) {
651   return SkipEmptyStatements(pn->pn_next);
652 }
653 
654 template <typename Unit>
GetToken(AsmJSParser<Unit> & parser,TokenKind * tkp)655 static bool GetToken(AsmJSParser<Unit>& parser, TokenKind* tkp) {
656   auto& ts = parser.tokenStream;
657   TokenKind tk;
658   while (true) {
659     if (!ts.getToken(&tk, TokenStreamShared::SlashIsRegExp)) {
660       return false;
661     }
662     if (tk != TokenKind::Semi) {
663       break;
664     }
665   }
666   *tkp = tk;
667   return true;
668 }
669 
670 template <typename Unit>
PeekToken(AsmJSParser<Unit> & parser,TokenKind * tkp)671 static bool PeekToken(AsmJSParser<Unit>& parser, TokenKind* tkp) {
672   auto& ts = parser.tokenStream;
673   TokenKind tk;
674   while (true) {
675     if (!ts.peekToken(&tk, TokenStream::SlashIsRegExp)) {
676       return false;
677     }
678     if (tk != TokenKind::Semi) {
679       break;
680     }
681     ts.consumeKnownToken(TokenKind::Semi, TokenStreamShared::SlashIsRegExp);
682   }
683   *tkp = tk;
684   return true;
685 }
686 
687 template <typename Unit>
ParseVarOrConstStatement(AsmJSParser<Unit> & parser,ParseNode ** var)688 static bool ParseVarOrConstStatement(AsmJSParser<Unit>& parser,
689                                      ParseNode** var) {
690   TokenKind tk;
691   if (!PeekToken(parser, &tk)) {
692     return false;
693   }
694   if (tk != TokenKind::Var && tk != TokenKind::Const) {
695     *var = nullptr;
696     return true;
697   }
698 
699   *var = parser.statementListItem(YieldIsName);
700   if (!*var) {
701     return false;
702   }
703 
704   MOZ_ASSERT((*var)->isKind(ParseNodeKind::VarStmt) ||
705              (*var)->isKind(ParseNodeKind::ConstDecl));
706   return true;
707 }
708 
709 /*****************************************************************************/
710 
711 // Represents the type and value of an asm.js numeric literal.
712 //
713 // A literal is a double iff the literal contains a decimal point (even if the
714 // fractional part is 0). Otherwise, integers may be classified:
715 //  fixnum: [0, 2^31)
716 //  negative int: [-2^31, 0)
717 //  big unsigned: [2^31, 2^32)
718 //  out of range: otherwise
719 // Lastly, a literal may be a float literal which is any double or integer
720 // literal coerced with Math.fround.
721 class NumLit {
722  public:
723   enum Which {
724     Fixnum,
725     NegativeInt,
726     BigUnsigned,
727     Double,
728     Float,
729     OutOfRangeInt = -1
730   };
731 
732  private:
733   Which which_;
734   JS::Value value_;
735 
736  public:
737   NumLit() = default;
738 
NumLit(Which w,const Value & v)739   NumLit(Which w, const Value& v) : which_(w), value_(v) {}
740 
which() const741   Which which() const { return which_; }
742 
toInt32() const743   int32_t toInt32() const {
744     MOZ_ASSERT(which_ == Fixnum || which_ == NegativeInt ||
745                which_ == BigUnsigned);
746     return value_.toInt32();
747   }
748 
toUint32() const749   uint32_t toUint32() const { return (uint32_t)toInt32(); }
750 
toDouble() const751   double toDouble() const {
752     MOZ_ASSERT(which_ == Double);
753     return value_.toDouble();
754   }
755 
toFloat() const756   float toFloat() const {
757     MOZ_ASSERT(which_ == Float);
758     return float(value_.toDouble());
759   }
760 
scalarValue() const761   Value scalarValue() const {
762     MOZ_ASSERT(which_ != OutOfRangeInt);
763     return value_;
764   }
765 
valid() const766   bool valid() const { return which_ != OutOfRangeInt; }
767 
isZeroBits() const768   bool isZeroBits() const {
769     MOZ_ASSERT(valid());
770     switch (which()) {
771       case NumLit::Fixnum:
772       case NumLit::NegativeInt:
773       case NumLit::BigUnsigned:
774         return toInt32() == 0;
775       case NumLit::Double:
776         return IsPositiveZero(toDouble());
777       case NumLit::Float:
778         return IsPositiveZero(toFloat());
779       case NumLit::OutOfRangeInt:
780         MOZ_CRASH("can't be here because of valid() check above");
781     }
782     return false;
783   }
784 
value() const785   LitValPOD value() const {
786     switch (which_) {
787       case NumLit::Fixnum:
788       case NumLit::NegativeInt:
789       case NumLit::BigUnsigned:
790         return LitValPOD(toUint32());
791       case NumLit::Float:
792         return LitValPOD(toFloat());
793       case NumLit::Double:
794         return LitValPOD(toDouble());
795       case NumLit::OutOfRangeInt:;
796     }
797     MOZ_CRASH("bad literal");
798   }
799 };
800 
801 // Represents the type of a general asm.js expression.
802 //
803 // A canonical subset of types representing the coercion targets: Int, Float,
804 // Double.
805 //
806 // Void is also part of the canonical subset.
807 
808 class Type {
809  public:
810   enum Which {
811     Fixnum = NumLit::Fixnum,
812     Signed = NumLit::NegativeInt,
813     Unsigned = NumLit::BigUnsigned,
814     DoubleLit = NumLit::Double,
815     Float = NumLit::Float,
816     Double,
817     MaybeDouble,
818     MaybeFloat,
819     Floatish,
820     Int,
821     Intish,
822     Void
823   };
824 
825  private:
826   Which which_;
827 
828  public:
829   Type() = default;
Type(Which w)830   MOZ_IMPLICIT Type(Which w) : which_(w) {}
831 
832   // Map an already canonicalized Type to the return type of a function call.
ret(Type t)833   static Type ret(Type t) {
834     MOZ_ASSERT(t.isCanonical());
835     // The 32-bit external type is Signed, not Int.
836     return t.isInt() ? Signed : t;
837   }
838 
lit(const NumLit & lit)839   static Type lit(const NumLit& lit) {
840     MOZ_ASSERT(lit.valid());
841     Which which = Type::Which(lit.which());
842     MOZ_ASSERT(which >= Fixnum && which <= Float);
843     Type t;
844     t.which_ = which;
845     return t;
846   }
847 
848   // Map |t| to one of the canonical vartype representations of a
849   // wasm::ValType.
canonicalize(Type t)850   static Type canonicalize(Type t) {
851     switch (t.which()) {
852       case Fixnum:
853       case Signed:
854       case Unsigned:
855       case Int:
856         return Int;
857 
858       case Float:
859         return Float;
860 
861       case DoubleLit:
862       case Double:
863         return Double;
864 
865       case Void:
866         return Void;
867 
868       case MaybeDouble:
869       case MaybeFloat:
870       case Floatish:
871       case Intish:
872         // These types need some kind of coercion, they can't be mapped
873         // to an VarType.
874         break;
875     }
876     MOZ_CRASH("Invalid vartype");
877   }
878 
which() const879   Which which() const { return which_; }
880 
operator ==(Type rhs) const881   bool operator==(Type rhs) const { return which_ == rhs.which_; }
operator !=(Type rhs) const882   bool operator!=(Type rhs) const { return which_ != rhs.which_; }
883 
operator <=(Type rhs) const884   bool operator<=(Type rhs) const {
885     switch (rhs.which_) {
886       case Signed:
887         return isSigned();
888       case Unsigned:
889         return isUnsigned();
890       case DoubleLit:
891         return isDoubleLit();
892       case Double:
893         return isDouble();
894       case Float:
895         return isFloat();
896       case MaybeDouble:
897         return isMaybeDouble();
898       case MaybeFloat:
899         return isMaybeFloat();
900       case Floatish:
901         return isFloatish();
902       case Int:
903         return isInt();
904       case Intish:
905         return isIntish();
906       case Fixnum:
907         return isFixnum();
908       case Void:
909         return isVoid();
910     }
911     MOZ_CRASH("unexpected rhs type");
912   }
913 
isFixnum() const914   bool isFixnum() const { return which_ == Fixnum; }
915 
isSigned() const916   bool isSigned() const { return which_ == Signed || which_ == Fixnum; }
917 
isUnsigned() const918   bool isUnsigned() const { return which_ == Unsigned || which_ == Fixnum; }
919 
isInt() const920   bool isInt() const { return isSigned() || isUnsigned() || which_ == Int; }
921 
isIntish() const922   bool isIntish() const { return isInt() || which_ == Intish; }
923 
isDoubleLit() const924   bool isDoubleLit() const { return which_ == DoubleLit; }
925 
isDouble() const926   bool isDouble() const { return isDoubleLit() || which_ == Double; }
927 
isMaybeDouble() const928   bool isMaybeDouble() const { return isDouble() || which_ == MaybeDouble; }
929 
isFloat() const930   bool isFloat() const { return which_ == Float; }
931 
isMaybeFloat() const932   bool isMaybeFloat() const { return isFloat() || which_ == MaybeFloat; }
933 
isFloatish() const934   bool isFloatish() const { return isMaybeFloat() || which_ == Floatish; }
935 
isVoid() const936   bool isVoid() const { return which_ == Void; }
937 
isExtern() const938   bool isExtern() const { return isDouble() || isSigned(); }
939 
940   // Check if this is one of the valid types for a function argument.
isArgType() const941   bool isArgType() const { return isInt() || isFloat() || isDouble(); }
942 
943   // Check if this is one of the valid types for a function return value.
isReturnType() const944   bool isReturnType() const {
945     return isSigned() || isFloat() || isDouble() || isVoid();
946   }
947 
948   // Check if this is one of the valid types for a global variable.
isGlobalVarType() const949   bool isGlobalVarType() const { return isArgType(); }
950 
951   // Check if this is one of the canonical vartype representations of a
952   // wasm::ValType, or is void. See Type::canonicalize().
isCanonical() const953   bool isCanonical() const {
954     switch (which()) {
955       case Int:
956       case Float:
957       case Double:
958       case Void:
959         return true;
960       default:
961         return false;
962     }
963   }
964 
965   // Check if this is a canonical representation of a wasm::ValType.
isCanonicalValType() const966   bool isCanonicalValType() const { return !isVoid() && isCanonical(); }
967 
968   // Convert this canonical type to a wasm::ValType.
canonicalToValType() const969   ValType canonicalToValType() const {
970     switch (which()) {
971       case Int:
972         return ValType::I32;
973       case Float:
974         return ValType::F32;
975       case Double:
976         return ValType::F64;
977       default:
978         MOZ_CRASH("Need canonical type");
979     }
980   }
981 
canonicalToReturnType() const982   Maybe<ValType> canonicalToReturnType() const {
983     return isVoid() ? Nothing() : Some(canonicalToValType());
984   }
985 
986   // Convert this type to a wasm::TypeCode for use in a wasm
987   // block signature. This works for all types, including non-canonical
988   // ones. Consequently, the type isn't valid for subsequent asm.js
989   // validation; it's only valid for use in producing wasm.
toWasmBlockSignatureType() const990   TypeCode toWasmBlockSignatureType() const {
991     switch (which()) {
992       case Fixnum:
993       case Signed:
994       case Unsigned:
995       case Int:
996       case Intish:
997         return TypeCode::I32;
998 
999       case Float:
1000       case MaybeFloat:
1001       case Floatish:
1002         return TypeCode::F32;
1003 
1004       case DoubleLit:
1005       case Double:
1006       case MaybeDouble:
1007         return TypeCode::F64;
1008 
1009       case Void:
1010         return TypeCode::BlockVoid;
1011     }
1012     MOZ_CRASH("Invalid Type");
1013   }
1014 
toChars() const1015   const char* toChars() const {
1016     switch (which_) {
1017       case Double:
1018         return "double";
1019       case DoubleLit:
1020         return "doublelit";
1021       case MaybeDouble:
1022         return "double?";
1023       case Float:
1024         return "float";
1025       case Floatish:
1026         return "floatish";
1027       case MaybeFloat:
1028         return "float?";
1029       case Fixnum:
1030         return "fixnum";
1031       case Int:
1032         return "int";
1033       case Signed:
1034         return "signed";
1035       case Unsigned:
1036         return "unsigned";
1037       case Intish:
1038         return "intish";
1039       case Void:
1040         return "void";
1041     }
1042     MOZ_CRASH("Invalid Type");
1043   }
1044 };
1045 
1046 static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
1047 
1048 class MOZ_STACK_CLASS ModuleValidatorShared {
1049  public:
1050   struct Memory {
1051     MemoryUsage usage;
1052     uint64_t minLength;
1053 
minPagesModuleValidatorShared::Memory1054     uint64_t minPages() const { return DivideRoundingUp(minLength, PageSize); }
1055 
1056     Memory() = default;
1057   };
1058 
1059   class Func {
1060     TaggedParserAtomIndex name_;
1061     uint32_t sigIndex_;
1062     uint32_t firstUse_;
1063     uint32_t funcDefIndex_;
1064 
1065     bool defined_;
1066 
1067     // Available when defined:
1068     uint32_t srcBegin_;
1069     uint32_t srcEnd_;
1070     uint32_t line_;
1071     Bytes bytes_;
1072     Uint32Vector callSiteLineNums_;
1073 
1074    public:
Func(TaggedParserAtomIndex name,uint32_t sigIndex,uint32_t firstUse,uint32_t funcDefIndex)1075     Func(TaggedParserAtomIndex name, uint32_t sigIndex, uint32_t firstUse,
1076          uint32_t funcDefIndex)
1077         : name_(name),
1078           sigIndex_(sigIndex),
1079           firstUse_(firstUse),
1080           funcDefIndex_(funcDefIndex),
1081           defined_(false),
1082           srcBegin_(0),
1083           srcEnd_(0),
1084           line_(0) {}
1085 
name() const1086     TaggedParserAtomIndex name() const { return name_; }
sigIndex() const1087     uint32_t sigIndex() const { return sigIndex_; }
firstUse() const1088     uint32_t firstUse() const { return firstUse_; }
defined() const1089     bool defined() const { return defined_; }
funcDefIndex() const1090     uint32_t funcDefIndex() const { return funcDefIndex_; }
1091 
define(ParseNode * fn,uint32_t line,Bytes && bytes,Uint32Vector && callSiteLineNums)1092     void define(ParseNode* fn, uint32_t line, Bytes&& bytes,
1093                 Uint32Vector&& callSiteLineNums) {
1094       MOZ_ASSERT(!defined_);
1095       defined_ = true;
1096       srcBegin_ = fn->pn_pos.begin;
1097       srcEnd_ = fn->pn_pos.end;
1098       line_ = line;
1099       bytes_ = std::move(bytes);
1100       callSiteLineNums_ = std::move(callSiteLineNums);
1101     }
1102 
srcBegin() const1103     uint32_t srcBegin() const {
1104       MOZ_ASSERT(defined_);
1105       return srcBegin_;
1106     }
srcEnd() const1107     uint32_t srcEnd() const {
1108       MOZ_ASSERT(defined_);
1109       return srcEnd_;
1110     }
line() const1111     uint32_t line() const {
1112       MOZ_ASSERT(defined_);
1113       return line_;
1114     }
bytes() const1115     const Bytes& bytes() const {
1116       MOZ_ASSERT(defined_);
1117       return bytes_;
1118     }
callSiteLineNums()1119     Uint32Vector& callSiteLineNums() {
1120       MOZ_ASSERT(defined_);
1121       return callSiteLineNums_;
1122     }
1123   };
1124 
1125   using ConstFuncVector = Vector<const Func*>;
1126   using FuncVector = Vector<Func>;
1127 
1128   class Table {
1129     uint32_t sigIndex_;
1130     TaggedParserAtomIndex name_;
1131     uint32_t firstUse_;
1132     uint32_t mask_;
1133     bool defined_;
1134 
1135     Table(Table&& rhs) = delete;
1136 
1137    public:
Table(uint32_t sigIndex,TaggedParserAtomIndex name,uint32_t firstUse,uint32_t mask)1138     Table(uint32_t sigIndex, TaggedParserAtomIndex name, uint32_t firstUse,
1139           uint32_t mask)
1140         : sigIndex_(sigIndex),
1141           name_(name),
1142           firstUse_(firstUse),
1143           mask_(mask),
1144           defined_(false) {}
1145 
sigIndex() const1146     uint32_t sigIndex() const { return sigIndex_; }
name() const1147     TaggedParserAtomIndex name() const { return name_; }
firstUse() const1148     uint32_t firstUse() const { return firstUse_; }
mask() const1149     unsigned mask() const { return mask_; }
defined() const1150     bool defined() const { return defined_; }
define()1151     void define() {
1152       MOZ_ASSERT(!defined_);
1153       defined_ = true;
1154     }
1155   };
1156 
1157   using TableVector = Vector<Table*>;
1158 
1159   class Global {
1160    public:
1161     enum Which {
1162       Variable,
1163       ConstantLiteral,
1164       ConstantImport,
1165       Function,
1166       Table,
1167       FFI,
1168       ArrayView,
1169       ArrayViewCtor,
1170       MathBuiltinFunction
1171     };
1172 
1173    private:
1174     Which which_;
1175     union U {
1176       struct VarOrConst {
1177         Type::Which type_;
1178         unsigned index_;
1179         NumLit literalValue_;
1180 
VarOrConstModuleValidatorShared::Global::U::VarOrConst1181         VarOrConst(unsigned index, const NumLit& lit)
1182             : type_(Type::lit(lit).which()),
1183               index_(index),
1184               literalValue_(lit)  // copies |lit|
1185         {}
1186 
VarOrConstModuleValidatorShared::Global::U::VarOrConst1187         VarOrConst(unsigned index, Type::Which which)
1188             : type_(which), index_(index) {
1189           // The |literalValue_| field remains unused and
1190           // uninitialized for non-constant variables.
1191         }
1192 
VarOrConstModuleValidatorShared::Global::U::VarOrConst1193         explicit VarOrConst(double constant)
1194             : type_(Type::Double),
1195               literalValue_(NumLit::Double, DoubleValue(constant)) {
1196           // The index_ field is unused and uninitialized for
1197           // constant doubles.
1198         }
1199       } varOrConst;
1200       uint32_t funcDefIndex_;
1201       uint32_t tableIndex_;
1202       uint32_t ffiIndex_;
1203       Scalar::Type viewType_;
1204       AsmJSMathBuiltinFunction mathBuiltinFunc_;
1205 
1206       // |varOrConst|, through |varOrConst.literalValue_|, has a
1207       // non-trivial constructor and therefore MUST be placement-new'd
1208       // into existence.
1209       MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
U()1210       U() : funcDefIndex_(0) {}
1211       MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
1212     } u;
1213 
1214     friend class ModuleValidatorShared;
1215     template <typename Unit>
1216     friend class ModuleValidator;
1217     friend class js::LifoAlloc;
1218 
Global(Which which)1219     explicit Global(Which which) : which_(which) {}
1220 
1221    public:
which() const1222     Which which() const { return which_; }
varOrConstType() const1223     Type varOrConstType() const {
1224       MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral ||
1225                  which_ == ConstantImport);
1226       return u.varOrConst.type_;
1227     }
varOrConstIndex() const1228     unsigned varOrConstIndex() const {
1229       MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
1230       return u.varOrConst.index_;
1231     }
isConst() const1232     bool isConst() const {
1233       return which_ == ConstantLiteral || which_ == ConstantImport;
1234     }
constLiteralValue() const1235     NumLit constLiteralValue() const {
1236       MOZ_ASSERT(which_ == ConstantLiteral);
1237       return u.varOrConst.literalValue_;
1238     }
funcDefIndex() const1239     uint32_t funcDefIndex() const {
1240       MOZ_ASSERT(which_ == Function);
1241       return u.funcDefIndex_;
1242     }
tableIndex() const1243     uint32_t tableIndex() const {
1244       MOZ_ASSERT(which_ == Table);
1245       return u.tableIndex_;
1246     }
ffiIndex() const1247     unsigned ffiIndex() const {
1248       MOZ_ASSERT(which_ == FFI);
1249       return u.ffiIndex_;
1250     }
viewType() const1251     Scalar::Type viewType() const {
1252       MOZ_ASSERT(which_ == ArrayView || which_ == ArrayViewCtor);
1253       return u.viewType_;
1254     }
isMathFunction() const1255     bool isMathFunction() const { return which_ == MathBuiltinFunction; }
mathBuiltinFunction() const1256     AsmJSMathBuiltinFunction mathBuiltinFunction() const {
1257       MOZ_ASSERT(which_ == MathBuiltinFunction);
1258       return u.mathBuiltinFunc_;
1259     }
1260   };
1261 
1262   struct MathBuiltin {
1263     enum Kind { Function, Constant };
1264     Kind kind;
1265 
1266     union {
1267       double cst;
1268       AsmJSMathBuiltinFunction func;
1269     } u;
1270 
MathBuiltinModuleValidatorShared::MathBuiltin1271     MathBuiltin() : kind(Kind(-1)), u{} {}
MathBuiltinModuleValidatorShared::MathBuiltin1272     explicit MathBuiltin(double cst) : kind(Constant) { u.cst = cst; }
MathBuiltinModuleValidatorShared::MathBuiltin1273     explicit MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
1274       u.func = func;
1275     }
1276   };
1277 
1278   struct ArrayView {
ArrayViewModuleValidatorShared::ArrayView1279     ArrayView(TaggedParserAtomIndex name, Scalar::Type type)
1280         : name(name), type(type) {}
1281 
1282     TaggedParserAtomIndex name;
1283     Scalar::Type type;
1284   };
1285 
1286  protected:
1287   class HashableSig {
1288     uint32_t sigIndex_;
1289     const TypeContext& types_;
1290 
1291    public:
HashableSig(uint32_t sigIndex,const TypeContext & types)1292     HashableSig(uint32_t sigIndex, const TypeContext& types)
1293         : sigIndex_(sigIndex), types_(types) {}
sigIndex() const1294     uint32_t sigIndex() const { return sigIndex_; }
funcType() const1295     const FuncType& funcType() const { return types_[sigIndex_].funcType(); }
1296 
1297     // Implement HashPolicy:
1298     using Lookup = const FuncType&;
hash(Lookup l)1299     static HashNumber hash(Lookup l) { return l.hash(); }
match(HashableSig lhs,Lookup rhs)1300     static bool match(HashableSig lhs, Lookup rhs) {
1301       return lhs.funcType() == rhs;
1302     }
1303   };
1304 
1305   class NamedSig : public HashableSig {
1306     TaggedParserAtomIndex name_;
1307 
1308    public:
NamedSig(TaggedParserAtomIndex name,uint32_t sigIndex,const TypeContext & types)1309     NamedSig(TaggedParserAtomIndex name, uint32_t sigIndex,
1310              const TypeContext& types)
1311         : HashableSig(sigIndex, types), name_(name) {}
name() const1312     TaggedParserAtomIndex name() const { return name_; }
1313 
1314     // Implement HashPolicy:
1315     struct Lookup {
1316       TaggedParserAtomIndex name;
1317       const FuncType& funcType;
LookupModuleValidatorShared::NamedSig::Lookup1318       Lookup(TaggedParserAtomIndex name, const FuncType& funcType)
1319           : name(name), funcType(funcType) {}
1320     };
hash(Lookup l)1321     static HashNumber hash(Lookup l) {
1322       return HashGeneric(TaggedParserAtomIndexHasher::hash(l.name),
1323                          l.funcType.hash());
1324     }
match(NamedSig lhs,Lookup rhs)1325     static bool match(NamedSig lhs, Lookup rhs) {
1326       return lhs.name() == rhs.name && lhs.funcType() == rhs.funcType;
1327     }
1328   };
1329 
1330   using SigSet = HashSet<HashableSig, HashableSig>;
1331   using FuncImportMap = HashMap<NamedSig, uint32_t, NamedSig>;
1332   using GlobalMap =
1333       HashMap<TaggedParserAtomIndex, Global*, TaggedParserAtomIndexHasher>;
1334   using MathNameMap =
1335       HashMap<TaggedParserAtomIndex, MathBuiltin, TaggedParserAtomIndexHasher>;
1336   using ArrayViewVector = Vector<ArrayView>;
1337 
1338  protected:
1339   JSContext* cx_;
1340   ParserAtomsTable& parserAtoms_;
1341   FunctionNode* moduleFunctionNode_;
1342   TaggedParserAtomIndex moduleFunctionName_;
1343   TaggedParserAtomIndex globalArgumentName_;
1344   TaggedParserAtomIndex importArgumentName_;
1345   TaggedParserAtomIndex bufferArgumentName_;
1346   MathNameMap standardLibraryMathNames_;
1347 
1348   // Validation-internal state:
1349   LifoAlloc validationLifo_;
1350   Memory memory_;
1351   FuncVector funcDefs_;
1352   TableVector tables_;
1353   GlobalMap globalMap_;
1354   SigSet sigSet_;
1355   FuncImportMap funcImportMap_;
1356   ArrayViewVector arrayViews_;
1357 
1358   // State used to build the AsmJSModule in finish():
1359   CompilerEnvironment compilerEnv_;
1360   ModuleEnvironment moduleEnv_;
1361   MutableAsmJSMetadata asmJSMetadata_;
1362 
1363   // Error reporting:
1364   UniqueChars errorString_ = nullptr;
1365   uint32_t errorOffset_ = UINT32_MAX;
1366   bool errorOverRecursed_ = false;
1367 
1368  protected:
ModuleValidatorShared(JSContext * cx,ParserAtomsTable & parserAtoms,FunctionNode * moduleFunctionNode)1369   ModuleValidatorShared(JSContext* cx, ParserAtomsTable& parserAtoms,
1370                         FunctionNode* moduleFunctionNode)
1371       : cx_(cx),
1372         parserAtoms_(parserAtoms),
1373         moduleFunctionNode_(moduleFunctionNode),
1374         moduleFunctionName_(FunctionName(moduleFunctionNode)),
1375         standardLibraryMathNames_(cx),
1376         validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
1377         funcDefs_(cx),
1378         tables_(cx),
1379         globalMap_(cx),
1380         sigSet_(cx),
1381         funcImportMap_(cx),
1382         arrayViews_(cx),
1383         compilerEnv_(CompileMode::Once, Tier::Optimized, OptimizedBackend::Ion,
1384                      DebugEnabled::False),
1385         moduleEnv_(FeatureArgs(), ModuleKind::AsmJS) {
1386     compilerEnv_.computeParameters();
1387     memory_.minLength = RoundUpToNextValidAsmJSHeapLength(0);
1388   }
1389 
1390  protected:
initModuleEnvironment()1391   [[nodiscard]] bool initModuleEnvironment() { return moduleEnv_.initTypes(0); }
1392 
addStandardLibraryMathInfo()1393   [[nodiscard]] bool addStandardLibraryMathInfo() {
1394     static constexpr struct {
1395       const char* name;
1396       AsmJSMathBuiltinFunction func;
1397     } functions[] = {
1398         {"sin", AsmJSMathBuiltin_sin},       {"cos", AsmJSMathBuiltin_cos},
1399         {"tan", AsmJSMathBuiltin_tan},       {"asin", AsmJSMathBuiltin_asin},
1400         {"acos", AsmJSMathBuiltin_acos},     {"atan", AsmJSMathBuiltin_atan},
1401         {"ceil", AsmJSMathBuiltin_ceil},     {"floor", AsmJSMathBuiltin_floor},
1402         {"exp", AsmJSMathBuiltin_exp},       {"log", AsmJSMathBuiltin_log},
1403         {"pow", AsmJSMathBuiltin_pow},       {"sqrt", AsmJSMathBuiltin_sqrt},
1404         {"abs", AsmJSMathBuiltin_abs},       {"atan2", AsmJSMathBuiltin_atan2},
1405         {"imul", AsmJSMathBuiltin_imul},     {"clz32", AsmJSMathBuiltin_clz32},
1406         {"fround", AsmJSMathBuiltin_fround}, {"min", AsmJSMathBuiltin_min},
1407         {"max", AsmJSMathBuiltin_max},
1408     };
1409 
1410     auto AddMathFunction = [this](const char* name,
1411                                   AsmJSMathBuiltinFunction func) {
1412       auto atom = parserAtoms_.internAscii(cx_, name, strlen(name));
1413       if (!atom) {
1414         return false;
1415       }
1416       MathBuiltin builtin(func);
1417       return this->standardLibraryMathNames_.putNew(atom, builtin);
1418     };
1419 
1420     for (const auto& info : functions) {
1421       if (!AddMathFunction(info.name, info.func)) {
1422         return false;
1423       }
1424     }
1425 
1426     static constexpr struct {
1427       const char* name;
1428       double value;
1429     } constants[] = {
1430         {"E", M_E},
1431         {"LN10", M_LN10},
1432         {"LN2", M_LN2},
1433         {"LOG2E", M_LOG2E},
1434         {"LOG10E", M_LOG10E},
1435         {"PI", M_PI},
1436         {"SQRT1_2", M_SQRT1_2},
1437         {"SQRT2", M_SQRT2},
1438     };
1439 
1440     auto AddMathConstant = [this](const char* name, double cst) {
1441       auto atom = parserAtoms_.internAscii(cx_, name, strlen(name));
1442       if (!atom) {
1443         return false;
1444       }
1445       MathBuiltin builtin(cst);
1446       return this->standardLibraryMathNames_.putNew(atom, builtin);
1447     };
1448 
1449     for (const auto& info : constants) {
1450       if (!AddMathConstant(info.name, info.value)) {
1451         return false;
1452       }
1453     }
1454 
1455     return true;
1456   }
1457 
1458  public:
cx() const1459   JSContext* cx() const { return cx_; }
moduleFunctionName() const1460   TaggedParserAtomIndex moduleFunctionName() const {
1461     return moduleFunctionName_;
1462   }
globalArgumentName() const1463   TaggedParserAtomIndex globalArgumentName() const {
1464     return globalArgumentName_;
1465   }
importArgumentName() const1466   TaggedParserAtomIndex importArgumentName() const {
1467     return importArgumentName_;
1468   }
bufferArgumentName() const1469   TaggedParserAtomIndex bufferArgumentName() const {
1470     return bufferArgumentName_;
1471   }
env()1472   const ModuleEnvironment& env() { return moduleEnv_; }
1473 
initModuleFunctionName(TaggedParserAtomIndex name)1474   void initModuleFunctionName(TaggedParserAtomIndex name) {
1475     MOZ_ASSERT(!moduleFunctionName_);
1476     moduleFunctionName_ = name;
1477   }
initGlobalArgumentName(TaggedParserAtomIndex n)1478   [[nodiscard]] bool initGlobalArgumentName(TaggedParserAtomIndex n) {
1479     globalArgumentName_ = n;
1480     if (n) {
1481       asmJSMetadata_->globalArgumentName = parserAtoms_.toNewUTF8CharsZ(cx_, n);
1482       if (!asmJSMetadata_->globalArgumentName) {
1483         return false;
1484       }
1485     }
1486     return true;
1487   }
initImportArgumentName(TaggedParserAtomIndex n)1488   [[nodiscard]] bool initImportArgumentName(TaggedParserAtomIndex n) {
1489     importArgumentName_ = n;
1490     if (n) {
1491       asmJSMetadata_->importArgumentName = parserAtoms_.toNewUTF8CharsZ(cx_, n);
1492       if (!asmJSMetadata_->importArgumentName) {
1493         return false;
1494       }
1495     }
1496     return true;
1497   }
initBufferArgumentName(TaggedParserAtomIndex n)1498   [[nodiscard]] bool initBufferArgumentName(TaggedParserAtomIndex n) {
1499     bufferArgumentName_ = n;
1500     if (n) {
1501       asmJSMetadata_->bufferArgumentName = parserAtoms_.toNewUTF8CharsZ(cx_, n);
1502       if (!asmJSMetadata_->bufferArgumentName) {
1503         return false;
1504       }
1505     }
1506     return true;
1507   }
addGlobalVarInit(TaggedParserAtomIndex var,const NumLit & lit,Type type,bool isConst)1508   bool addGlobalVarInit(TaggedParserAtomIndex var, const NumLit& lit, Type type,
1509                         bool isConst) {
1510     MOZ_ASSERT(type.isGlobalVarType());
1511     MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
1512 
1513     uint32_t index = moduleEnv_.globals.length();
1514     if (!moduleEnv_.globals.emplaceBack(type.canonicalToValType(), !isConst,
1515                                         index, ModuleKind::AsmJS)) {
1516       return false;
1517     }
1518 
1519     Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
1520     Global* global = validationLifo_.new_<Global>(which);
1521     if (!global) {
1522       return false;
1523     }
1524     if (isConst) {
1525       new (&global->u.varOrConst) Global::U::VarOrConst(index, lit);
1526     } else {
1527       new (&global->u.varOrConst) Global::U::VarOrConst(index, type.which());
1528     }
1529     if (!globalMap_.putNew(var, global)) {
1530       return false;
1531     }
1532 
1533     AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
1534     g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
1535     g.pod.u.var.u.val_ = lit.value();
1536     return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1537   }
addGlobalVarImport(TaggedParserAtomIndex var,TaggedParserAtomIndex field,Type type,bool isConst)1538   bool addGlobalVarImport(TaggedParserAtomIndex var,
1539                           TaggedParserAtomIndex field, Type type,
1540                           bool isConst) {
1541     MOZ_ASSERT(type.isGlobalVarType());
1542 
1543     UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(cx_, field);
1544     if (!fieldChars) {
1545       return false;
1546     }
1547 
1548     uint32_t index = moduleEnv_.globals.length();
1549     ValType valType = type.canonicalToValType();
1550     if (!moduleEnv_.globals.emplaceBack(valType, !isConst, index,
1551                                         ModuleKind::AsmJS)) {
1552       return false;
1553     }
1554 
1555     Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
1556     Global* global = validationLifo_.new_<Global>(which);
1557     if (!global) {
1558       return false;
1559     }
1560     new (&global->u.varOrConst) Global::U::VarOrConst(index, type.which());
1561     if (!globalMap_.putNew(var, global)) {
1562       return false;
1563     }
1564 
1565     AsmJSGlobal g(AsmJSGlobal::Variable, std::move(fieldChars));
1566     g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
1567     g.pod.u.var.u.importValType_ = valType.packed();
1568     return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1569   }
addArrayView(TaggedParserAtomIndex var,Scalar::Type vt,TaggedParserAtomIndex maybeField)1570   bool addArrayView(TaggedParserAtomIndex var, Scalar::Type vt,
1571                     TaggedParserAtomIndex maybeField) {
1572     UniqueChars fieldChars;
1573     if (maybeField) {
1574       fieldChars = parserAtoms_.toNewUTF8CharsZ(cx_, maybeField);
1575       if (!fieldChars) {
1576         return false;
1577       }
1578     }
1579 
1580     if (!arrayViews_.append(ArrayView(var, vt))) {
1581       return false;
1582     }
1583 
1584     Global* global = validationLifo_.new_<Global>(Global::ArrayView);
1585     if (!global) {
1586       return false;
1587     }
1588     new (&global->u.viewType_) Scalar::Type(vt);
1589     if (!globalMap_.putNew(var, global)) {
1590       return false;
1591     }
1592 
1593     AsmJSGlobal g(AsmJSGlobal::ArrayView, std::move(fieldChars));
1594     g.pod.u.viewType_ = vt;
1595     return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1596   }
addMathBuiltinFunction(TaggedParserAtomIndex var,AsmJSMathBuiltinFunction func,TaggedParserAtomIndex field)1597   bool addMathBuiltinFunction(TaggedParserAtomIndex var,
1598                               AsmJSMathBuiltinFunction func,
1599                               TaggedParserAtomIndex field) {
1600     UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(cx_, field);
1601     if (!fieldChars) {
1602       return false;
1603     }
1604 
1605     Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction);
1606     if (!global) {
1607       return false;
1608     }
1609     new (&global->u.mathBuiltinFunc_) AsmJSMathBuiltinFunction(func);
1610     if (!globalMap_.putNew(var, global)) {
1611       return false;
1612     }
1613 
1614     AsmJSGlobal g(AsmJSGlobal::MathBuiltinFunction, std::move(fieldChars));
1615     g.pod.u.mathBuiltinFunc_ = func;
1616     return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1617   }
1618 
1619  private:
addGlobalDoubleConstant(TaggedParserAtomIndex var,double constant)1620   bool addGlobalDoubleConstant(TaggedParserAtomIndex var, double constant) {
1621     Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral);
1622     if (!global) {
1623       return false;
1624     }
1625     new (&global->u.varOrConst) Global::U::VarOrConst(constant);
1626     return globalMap_.putNew(var, global);
1627   }
1628 
1629  public:
addMathBuiltinConstant(TaggedParserAtomIndex var,double constant,TaggedParserAtomIndex field)1630   bool addMathBuiltinConstant(TaggedParserAtomIndex var, double constant,
1631                               TaggedParserAtomIndex field) {
1632     UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(cx_, field);
1633     if (!fieldChars) {
1634       return false;
1635     }
1636 
1637     if (!addGlobalDoubleConstant(var, constant)) {
1638       return false;
1639     }
1640 
1641     AsmJSGlobal g(AsmJSGlobal::Constant, std::move(fieldChars));
1642     g.pod.u.constant.value_ = constant;
1643     g.pod.u.constant.kind_ = AsmJSGlobal::MathConstant;
1644     return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1645   }
addGlobalConstant(TaggedParserAtomIndex var,double constant,TaggedParserAtomIndex field)1646   bool addGlobalConstant(TaggedParserAtomIndex var, double constant,
1647                          TaggedParserAtomIndex field) {
1648     UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(cx_, field);
1649     if (!fieldChars) {
1650       return false;
1651     }
1652 
1653     if (!addGlobalDoubleConstant(var, constant)) {
1654       return false;
1655     }
1656 
1657     AsmJSGlobal g(AsmJSGlobal::Constant, std::move(fieldChars));
1658     g.pod.u.constant.value_ = constant;
1659     g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant;
1660     return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1661   }
addArrayViewCtor(TaggedParserAtomIndex var,Scalar::Type vt,TaggedParserAtomIndex field)1662   bool addArrayViewCtor(TaggedParserAtomIndex var, Scalar::Type vt,
1663                         TaggedParserAtomIndex field) {
1664     UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(cx_, field);
1665     if (!fieldChars) {
1666       return false;
1667     }
1668 
1669     Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
1670     if (!global) {
1671       return false;
1672     }
1673     new (&global->u.viewType_) Scalar::Type(vt);
1674     if (!globalMap_.putNew(var, global)) {
1675       return false;
1676     }
1677 
1678     AsmJSGlobal g(AsmJSGlobal::ArrayViewCtor, std::move(fieldChars));
1679     g.pod.u.viewType_ = vt;
1680     return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1681   }
addFFI(TaggedParserAtomIndex var,TaggedParserAtomIndex field)1682   bool addFFI(TaggedParserAtomIndex var, TaggedParserAtomIndex field) {
1683     UniqueChars fieldChars = parserAtoms_.toNewUTF8CharsZ(cx_, field);
1684     if (!fieldChars) {
1685       return false;
1686     }
1687 
1688     if (asmJSMetadata_->numFFIs == UINT32_MAX) {
1689       return false;
1690     }
1691     uint32_t ffiIndex = asmJSMetadata_->numFFIs++;
1692 
1693     Global* global = validationLifo_.new_<Global>(Global::FFI);
1694     if (!global) {
1695       return false;
1696     }
1697     new (&global->u.ffiIndex_) uint32_t(ffiIndex);
1698     if (!globalMap_.putNew(var, global)) {
1699       return false;
1700     }
1701 
1702     AsmJSGlobal g(AsmJSGlobal::FFI, std::move(fieldChars));
1703     g.pod.u.ffiIndex_ = ffiIndex;
1704     return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1705   }
addExportField(const Func & func,TaggedParserAtomIndex maybeField)1706   bool addExportField(const Func& func, TaggedParserAtomIndex maybeField) {
1707     // Record the field name of this export.
1708     CacheableChars fieldChars;
1709     if (maybeField) {
1710       fieldChars = parserAtoms_.toNewUTF8CharsZ(cx_, maybeField);
1711     } else {
1712       fieldChars = DuplicateString("");
1713     }
1714     if (!fieldChars) {
1715       return false;
1716     }
1717 
1718     // Declare which function is exported which gives us an index into the
1719     // module ExportVector.
1720     uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex();
1721     if (!moduleEnv_.exports.emplaceBack(std::move(fieldChars), funcIndex,
1722                                         DefinitionKind::Function)) {
1723       return false;
1724     }
1725 
1726     // The exported function might have already been exported in which case
1727     // the index will refer into the range of AsmJSExports.
1728     return asmJSMetadata_->asmJSExports.emplaceBack(
1729         funcIndex, func.srcBegin() - asmJSMetadata_->srcStart,
1730         func.srcEnd() - asmJSMetadata_->srcStart);
1731   }
1732 
defineFuncPtrTable(uint32_t tableIndex,Uint32Vector && elems)1733   bool defineFuncPtrTable(uint32_t tableIndex, Uint32Vector&& elems) {
1734     Table& table = *tables_[tableIndex];
1735     if (table.defined()) {
1736       return false;
1737     }
1738 
1739     table.define();
1740 
1741     for (uint32_t& index : elems) {
1742       index += funcImportMap_.count();
1743     }
1744 
1745     MutableElemSegment seg = js_new<ElemSegment>();
1746     if (!seg) {
1747       return false;
1748     }
1749     seg->elemType = RefType::func();
1750     seg->tableIndex = tableIndex;
1751     seg->offsetIfActive = Some(InitExpr(LitVal(uint32_t(0))));
1752     seg->elemFuncIndices = std::move(elems);
1753     return moduleEnv_.elemSegments.append(std::move(seg));
1754   }
1755 
tryConstantAccess(uint64_t start,uint64_t width)1756   bool tryConstantAccess(uint64_t start, uint64_t width) {
1757     MOZ_ASSERT(UINT64_MAX - start > width);
1758     uint64_t len = start + width;
1759     if (len > uint64_t(INT32_MAX) + 1) {
1760       return false;
1761     }
1762     len = RoundUpToNextValidAsmJSHeapLength(len);
1763     if (len > memory_.minLength) {
1764       memory_.minLength = len;
1765     }
1766     return true;
1767   }
1768 
1769   // Error handling.
hasAlreadyFailed() const1770   bool hasAlreadyFailed() const { return !!errorString_; }
1771 
failOffset(uint32_t offset,const char * str)1772   bool failOffset(uint32_t offset, const char* str) {
1773     MOZ_ASSERT(!hasAlreadyFailed());
1774     MOZ_ASSERT(errorOffset_ == UINT32_MAX);
1775     MOZ_ASSERT(str);
1776     errorOffset_ = offset;
1777     errorString_ = DuplicateString(str);
1778     return false;
1779   }
1780 
fail(ParseNode * pn,const char * str)1781   bool fail(ParseNode* pn, const char* str) {
1782     return failOffset(pn->pn_pos.begin, str);
1783   }
1784 
failfVAOffset(uint32_t offset,const char * fmt,va_list ap)1785   bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap)
1786       MOZ_FORMAT_PRINTF(3, 0) {
1787     MOZ_ASSERT(!hasAlreadyFailed());
1788     MOZ_ASSERT(errorOffset_ == UINT32_MAX);
1789     MOZ_ASSERT(fmt);
1790     errorOffset_ = offset;
1791     errorString_ = JS_vsmprintf(fmt, ap);
1792     return false;
1793   }
1794 
failfOffset(uint32_t offset,const char * fmt,...)1795   bool failfOffset(uint32_t offset, const char* fmt, ...)
1796       MOZ_FORMAT_PRINTF(3, 4) {
1797     va_list ap;
1798     va_start(ap, fmt);
1799     failfVAOffset(offset, fmt, ap);
1800     va_end(ap);
1801     return false;
1802   }
1803 
failf(ParseNode * pn,const char * fmt,...)1804   bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
1805     va_list ap;
1806     va_start(ap, fmt);
1807     failfVAOffset(pn->pn_pos.begin, fmt, ap);
1808     va_end(ap);
1809     return false;
1810   }
1811 
failNameOffset(uint32_t offset,const char * fmt,TaggedParserAtomIndex name)1812   bool failNameOffset(uint32_t offset, const char* fmt,
1813                       TaggedParserAtomIndex name) {
1814     // This function is invoked without the caller properly rooting its locals.
1815     gc::AutoSuppressGC suppress(cx_);
1816     if (UniqueChars bytes = parserAtoms_.toPrintableString(cx_, name)) {
1817       failfOffset(offset, fmt, bytes.get());
1818     }
1819     return false;
1820   }
1821 
failName(ParseNode * pn,const char * fmt,TaggedParserAtomIndex name)1822   bool failName(ParseNode* pn, const char* fmt, TaggedParserAtomIndex name) {
1823     return failNameOffset(pn->pn_pos.begin, fmt, name);
1824   }
1825 
failOverRecursed()1826   bool failOverRecursed() {
1827     errorOverRecursed_ = true;
1828     return false;
1829   }
1830 
numArrayViews() const1831   unsigned numArrayViews() const { return arrayViews_.length(); }
arrayView(unsigned i) const1832   const ArrayView& arrayView(unsigned i) const { return arrayViews_[i]; }
numFuncDefs() const1833   unsigned numFuncDefs() const { return funcDefs_.length(); }
funcDef(unsigned i) const1834   const Func& funcDef(unsigned i) const { return funcDefs_[i]; }
numFuncPtrTables() const1835   unsigned numFuncPtrTables() const { return tables_.length(); }
table(unsigned i) const1836   Table& table(unsigned i) const { return *tables_[i]; }
1837 
lookupGlobal(TaggedParserAtomIndex name) const1838   const Global* lookupGlobal(TaggedParserAtomIndex name) const {
1839     if (GlobalMap::Ptr p = globalMap_.lookup(name)) {
1840       return p->value();
1841     }
1842     return nullptr;
1843   }
1844 
lookupFuncDef(TaggedParserAtomIndex name)1845   Func* lookupFuncDef(TaggedParserAtomIndex name) {
1846     if (GlobalMap::Ptr p = globalMap_.lookup(name)) {
1847       Global* value = p->value();
1848       if (value->which() == Global::Function) {
1849         return &funcDefs_[value->funcDefIndex()];
1850       }
1851     }
1852     return nullptr;
1853   }
1854 
lookupStandardLibraryMathName(TaggedParserAtomIndex name,MathBuiltin * mathBuiltin) const1855   bool lookupStandardLibraryMathName(TaggedParserAtomIndex name,
1856                                      MathBuiltin* mathBuiltin) const {
1857     if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
1858       *mathBuiltin = p->value();
1859       return true;
1860     }
1861     return false;
1862   }
1863 
startFunctionBodies()1864   bool startFunctionBodies() {
1865     if (!arrayViews_.empty()) {
1866       memory_.usage = MemoryUsage::Unshared;
1867     } else {
1868       memory_.usage = MemoryUsage::None;
1869     }
1870     return true;
1871   }
1872 };
1873 
1874 // The ModuleValidator encapsulates the entire validation of an asm.js module.
1875 // Its lifetime goes from the validation of the top components of an asm.js
1876 // module (all the globals), the emission of bytecode for all the functions in
1877 // the module and the validation of function's pointer tables. It also finishes
1878 // the compilation of all the module's stubs.
1879 template <typename Unit>
1880 class MOZ_STACK_CLASS ModuleValidator : public ModuleValidatorShared {
1881  private:
1882   AsmJSParser<Unit>& parser_;
1883 
1884  public:
ModuleValidator(JSContext * cx,ParserAtomsTable & parserAtoms,AsmJSParser<Unit> & parser,FunctionNode * moduleFunctionNode)1885   ModuleValidator(JSContext* cx, ParserAtomsTable& parserAtoms,
1886                   AsmJSParser<Unit>& parser, FunctionNode* moduleFunctionNode)
1887       : ModuleValidatorShared(cx, parserAtoms, moduleFunctionNode),
1888         parser_(parser) {}
1889 
~ModuleValidator()1890   ~ModuleValidator() {
1891     if (errorString_) {
1892       MOZ_ASSERT(errorOffset_ != UINT32_MAX);
1893       typeFailure(errorOffset_, errorString_.get());
1894     }
1895     if (errorOverRecursed_) {
1896       ReportOverRecursed(cx_);
1897     }
1898   }
1899 
1900  private:
1901   // Helpers:
newSig(FuncType && sig,uint32_t * sigIndex)1902   bool newSig(FuncType&& sig, uint32_t* sigIndex) {
1903     if (moduleEnv_.types->length() >= MaxTypes) {
1904       return failCurrentOffset("too many signatures");
1905     }
1906 
1907     *sigIndex = moduleEnv_.types->length();
1908     return moduleEnv_.types->append(std::move(sig)) &&
1909            moduleEnv_.typeIds.append(TypeIdDesc());
1910   }
declareSig(FuncType && sig,uint32_t * sigIndex)1911   bool declareSig(FuncType&& sig, uint32_t* sigIndex) {
1912     SigSet::AddPtr p = sigSet_.lookupForAdd(sig);
1913     if (p) {
1914       *sigIndex = p->sigIndex();
1915       MOZ_ASSERT(moduleEnv_.types->funcType(*sigIndex) == sig);
1916       return true;
1917     }
1918 
1919     return newSig(std::move(sig), sigIndex) &&
1920            sigSet_.add(p, HashableSig(*sigIndex, *moduleEnv_.types));
1921   }
1922 
1923  private:
typeFailure(uint32_t offset,...)1924   void typeFailure(uint32_t offset, ...) {
1925     va_list args;
1926     va_start(args, offset);
1927 
1928     auto& ts = tokenStream();
1929     ErrorMetadata metadata;
1930     if (ts.computeErrorMetadata(&metadata, AsVariant(offset))) {
1931       if (ts.anyCharsAccess().options().throwOnAsmJSValidationFailureOption) {
1932         ReportCompileErrorLatin1(cx_, std::move(metadata), nullptr,
1933                                  JSMSG_USE_ASM_TYPE_FAIL, &args);
1934       } else {
1935         // asm.js type failure is indicated by calling one of the fail*
1936         // functions below.  These functions always return false to
1937         // halt asm.js parsing.  Whether normal parsing is attempted as
1938         // fallback, depends whether an exception is also set.
1939         //
1940         // If warning succeeds, no exception is set.  If warning fails,
1941         // an exception is set and execution will halt.  Thus it's safe
1942         // and correct to ignore the return value here.
1943         (void)ts.compileWarning(std::move(metadata), nullptr,
1944                                 JSMSG_USE_ASM_TYPE_FAIL, &args);
1945       }
1946     }
1947 
1948     va_end(args);
1949   }
1950 
1951  public:
init()1952   bool init() {
1953     asmJSMetadata_ = cx_->new_<AsmJSMetadata>();
1954     if (!asmJSMetadata_) {
1955       return false;
1956     }
1957 
1958     asmJSMetadata_->toStringStart =
1959         moduleFunctionNode_->funbox()->extent().toStringStart;
1960     asmJSMetadata_->srcStart = moduleFunctionNode_->body()->pn_pos.begin;
1961     asmJSMetadata_->strict = parser_.pc_->sc()->strict() &&
1962                              !parser_.pc_->sc()->hasExplicitUseStrict();
1963     asmJSMetadata_->source = do_AddRef(parser_.ss);
1964 
1965     if (!initModuleEnvironment()) {
1966       return false;
1967     }
1968     return addStandardLibraryMathInfo();
1969   }
1970 
parser() const1971   AsmJSParser<Unit>& parser() const { return parser_; }
1972 
tokenStream() const1973   auto& tokenStream() const { return parser_.tokenStream; }
1974 
1975  public:
addFuncDef(TaggedParserAtomIndex name,uint32_t firstUse,FuncType && sig,Func ** func)1976   bool addFuncDef(TaggedParserAtomIndex name, uint32_t firstUse, FuncType&& sig,
1977                   Func** func) {
1978     uint32_t sigIndex;
1979     if (!declareSig(std::move(sig), &sigIndex)) {
1980       return false;
1981     }
1982 
1983     uint32_t funcDefIndex = funcDefs_.length();
1984     if (funcDefIndex >= MaxFuncs) {
1985       return failCurrentOffset("too many functions");
1986     }
1987 
1988     Global* global = validationLifo_.new_<Global>(Global::Function);
1989     if (!global) {
1990       return false;
1991     }
1992     new (&global->u.funcDefIndex_) uint32_t(funcDefIndex);
1993     if (!globalMap_.putNew(name, global)) {
1994       return false;
1995     }
1996     if (!funcDefs_.emplaceBack(name, sigIndex, firstUse, funcDefIndex)) {
1997       return false;
1998     }
1999     *func = &funcDefs_.back();
2000     return true;
2001   }
declareFuncPtrTable(FuncType && sig,TaggedParserAtomIndex name,uint32_t firstUse,uint32_t mask,uint32_t * tableIndex)2002   bool declareFuncPtrTable(FuncType&& sig, TaggedParserAtomIndex name,
2003                            uint32_t firstUse, uint32_t mask,
2004                            uint32_t* tableIndex) {
2005     if (mask > MaxTableLength) {
2006       return failCurrentOffset("function pointer table too big");
2007     }
2008 
2009     MOZ_ASSERT(moduleEnv_.tables.length() == tables_.length());
2010     *tableIndex = moduleEnv_.tables.length();
2011 
2012     uint32_t sigIndex;
2013     if (!newSig(std::move(sig), &sigIndex)) {
2014       return false;
2015     }
2016 
2017     MOZ_ASSERT(sigIndex >= moduleEnv_.asmJSSigToTableIndex.length());
2018     if (!moduleEnv_.asmJSSigToTableIndex.resize(sigIndex + 1)) {
2019       return false;
2020     }
2021 
2022     moduleEnv_.asmJSSigToTableIndex[sigIndex] = moduleEnv_.tables.length();
2023     if (!moduleEnv_.tables.emplaceBack(RefType::func(), mask + 1, Nothing(),
2024                                        /*isAsmJS*/ true)) {
2025       return false;
2026     }
2027 
2028     Global* global = validationLifo_.new_<Global>(Global::Table);
2029     if (!global) {
2030       return false;
2031     }
2032 
2033     new (&global->u.tableIndex_) uint32_t(*tableIndex);
2034     if (!globalMap_.putNew(name, global)) {
2035       return false;
2036     }
2037 
2038     Table* t = validationLifo_.new_<Table>(sigIndex, name, firstUse, mask);
2039     return t && tables_.append(t);
2040   }
declareImport(TaggedParserAtomIndex name,FuncType && sig,unsigned ffiIndex,uint32_t * importIndex)2041   bool declareImport(TaggedParserAtomIndex name, FuncType&& sig,
2042                      unsigned ffiIndex, uint32_t* importIndex) {
2043     FuncImportMap::AddPtr p =
2044         funcImportMap_.lookupForAdd(NamedSig::Lookup(name, sig));
2045     if (p) {
2046       *importIndex = p->value();
2047       return true;
2048     }
2049 
2050     *importIndex = funcImportMap_.count();
2051     MOZ_ASSERT(*importIndex == asmJSMetadata_->asmJSImports.length());
2052 
2053     if (*importIndex >= MaxImports) {
2054       return failCurrentOffset("too many imports");
2055     }
2056 
2057     if (!asmJSMetadata_->asmJSImports.emplaceBack(ffiIndex)) {
2058       return false;
2059     }
2060 
2061     uint32_t sigIndex;
2062     if (!declareSig(std::move(sig), &sigIndex)) {
2063       return false;
2064     }
2065 
2066     return funcImportMap_.add(p, NamedSig(name, sigIndex, *moduleEnv_.types),
2067                               *importIndex);
2068   }
2069 
2070   // Error handling.
failCurrentOffset(const char * str)2071   bool failCurrentOffset(const char* str) {
2072     return failOffset(tokenStream().anyCharsAccess().currentToken().pos.begin,
2073                       str);
2074   }
2075 
finish()2076   SharedModule finish() {
2077     MOZ_ASSERT(!moduleEnv_.usesMemory());
2078     if (memory_.usage != MemoryUsage::None) {
2079       Limits limits;
2080       limits.shared = memory_.usage == MemoryUsage::Shared ? Shareable::True
2081                                                            : Shareable::False;
2082       limits.initial = memory_.minPages();
2083       limits.maximum = Nothing();
2084       limits.indexType = IndexType::I32;
2085       moduleEnv_.memory = Some(MemoryDesc(limits));
2086     }
2087     MOZ_ASSERT(moduleEnv_.funcs.empty());
2088     if (!moduleEnv_.funcs.resize(funcImportMap_.count() + funcDefs_.length())) {
2089       return nullptr;
2090     }
2091     for (FuncImportMap::Range r = funcImportMap_.all(); !r.empty();
2092          r.popFront()) {
2093       uint32_t funcIndex = r.front().value();
2094       uint32_t funcTypeIndex = r.front().key().sigIndex();
2095       MOZ_ASSERT(!moduleEnv_.funcs[funcIndex].type);
2096       moduleEnv_.funcs[funcIndex] =
2097           FuncDesc(&moduleEnv_.types->funcType(funcTypeIndex),
2098                    &moduleEnv_.typeIds[funcTypeIndex], funcTypeIndex);
2099     }
2100     for (const Func& func : funcDefs_) {
2101       uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex();
2102       uint32_t funcTypeIndex = func.sigIndex();
2103       MOZ_ASSERT(!moduleEnv_.funcs[funcIndex].type);
2104       moduleEnv_.funcs[funcIndex] =
2105           FuncDesc(&moduleEnv_.types->funcType(funcTypeIndex),
2106                    &moduleEnv_.typeIds[funcTypeIndex], funcTypeIndex);
2107     }
2108     for (const Export& exp : moduleEnv_.exports) {
2109       if (exp.kind() != DefinitionKind::Function) {
2110         continue;
2111       }
2112       uint32_t funcIndex = exp.funcIndex();
2113       moduleEnv_.declareFuncExported(funcIndex, /* eager */ true,
2114                                      /* canRefFunc */ false);
2115     }
2116 
2117     if (!moduleEnv_.funcImportGlobalDataOffsets.resize(
2118             funcImportMap_.count())) {
2119       return nullptr;
2120     }
2121 
2122     MOZ_ASSERT(asmJSMetadata_->asmJSFuncNames.empty());
2123     if (!asmJSMetadata_->asmJSFuncNames.resize(funcImportMap_.count())) {
2124       return nullptr;
2125     }
2126     for (const Func& func : funcDefs_) {
2127       CacheableChars funcName = parserAtoms_.toNewUTF8CharsZ(cx_, func.name());
2128       if (!funcName ||
2129           !asmJSMetadata_->asmJSFuncNames.emplaceBack(std::move(funcName))) {
2130         return nullptr;
2131       }
2132     }
2133 
2134     uint32_t endBeforeCurly =
2135         tokenStream().anyCharsAccess().currentToken().pos.end;
2136     asmJSMetadata_->srcLength = endBeforeCurly - asmJSMetadata_->srcStart;
2137 
2138     TokenPos pos;
2139     MOZ_ALWAYS_TRUE(
2140         tokenStream().peekTokenPos(&pos, TokenStreamShared::SlashIsRegExp));
2141     uint32_t endAfterCurly = pos.end;
2142     asmJSMetadata_->srcLengthWithRightBrace =
2143         endAfterCurly - asmJSMetadata_->srcStart;
2144 
2145     ScriptedCaller scriptedCaller;
2146     if (parser_.ss->filename()) {
2147       scriptedCaller.line = 0;  // unused
2148       scriptedCaller.filename = DuplicateString(parser_.ss->filename());
2149       if (!scriptedCaller.filename) {
2150         return nullptr;
2151       }
2152     }
2153 
2154     // The default options are fine for asm.js
2155     FeatureOptions options;
2156     CompileArgsError error;
2157     SharedCompileArgs args =
2158         CompileArgs::build(cx_, std::move(scriptedCaller), options, &error);
2159     if (!args) {
2160       // EstablishPreconditions will ensure that a compiler is available by
2161       // this point
2162       MOZ_RELEASE_ASSERT(error == CompileArgsError::OutOfMemory);
2163       return nullptr;
2164     }
2165 
2166     uint32_t codeSectionSize = 0;
2167     for (const Func& func : funcDefs_) {
2168       codeSectionSize += func.bytes().length();
2169     }
2170 
2171     moduleEnv_.codeSection.emplace();
2172     moduleEnv_.codeSection->start = 0;
2173     moduleEnv_.codeSection->size = codeSectionSize;
2174 
2175     // asm.js does not have any wasm bytecode to save; view-source is
2176     // provided through the ScriptSource.
2177     SharedBytes bytes = cx_->new_<ShareableBytes>();
2178     if (!bytes) {
2179       return nullptr;
2180     }
2181 
2182     ModuleGenerator mg(*args, &moduleEnv_, &compilerEnv_, nullptr, nullptr,
2183                        nullptr);
2184     if (!mg.init(asmJSMetadata_.get())) {
2185       return nullptr;
2186     }
2187 
2188     for (Func& func : funcDefs_) {
2189       if (!mg.compileFuncDef(funcImportMap_.count() + func.funcDefIndex(),
2190                              func.line(), func.bytes().begin(),
2191                              func.bytes().end(),
2192                              std::move(func.callSiteLineNums()))) {
2193         return nullptr;
2194       }
2195     }
2196 
2197     if (!mg.finishFuncDefs()) {
2198       return nullptr;
2199     }
2200 
2201     return mg.finishModule(*bytes);
2202   }
2203 };
2204 
2205 /*****************************************************************************/
2206 // Numeric literal utilities
2207 
IsNumericNonFloatLiteral(ParseNode * pn)2208 static bool IsNumericNonFloatLiteral(ParseNode* pn) {
2209   // Note: '-' is never rolled into the number; numbers are always positive
2210   // and negations must be applied manually.
2211   return pn->isKind(ParseNodeKind::NumberExpr) ||
2212          (pn->isKind(ParseNodeKind::NegExpr) &&
2213           UnaryKid(pn)->isKind(ParseNodeKind::NumberExpr));
2214 }
2215 
IsCallToGlobal(ModuleValidatorShared & m,ParseNode * pn,const ModuleValidatorShared::Global ** global)2216 static bool IsCallToGlobal(ModuleValidatorShared& m, ParseNode* pn,
2217                            const ModuleValidatorShared::Global** global) {
2218   if (!pn->isKind(ParseNodeKind::CallExpr)) {
2219     return false;
2220   }
2221 
2222   ParseNode* callee = CallCallee(pn);
2223   if (!callee->isKind(ParseNodeKind::Name)) {
2224     return false;
2225   }
2226 
2227   *global = m.lookupGlobal(callee->as<NameNode>().name());
2228   return !!*global;
2229 }
2230 
IsCoercionCall(ModuleValidatorShared & m,ParseNode * pn,Type * coerceTo,ParseNode ** coercedExpr)2231 static bool IsCoercionCall(ModuleValidatorShared& m, ParseNode* pn,
2232                            Type* coerceTo, ParseNode** coercedExpr) {
2233   const ModuleValidatorShared::Global* global;
2234   if (!IsCallToGlobal(m, pn, &global)) {
2235     return false;
2236   }
2237 
2238   if (CallArgListLength(pn) != 1) {
2239     return false;
2240   }
2241 
2242   if (coercedExpr) {
2243     *coercedExpr = CallArgList(pn);
2244   }
2245 
2246   if (global->isMathFunction() &&
2247       global->mathBuiltinFunction() == AsmJSMathBuiltin_fround) {
2248     *coerceTo = Type::Float;
2249     return true;
2250   }
2251 
2252   return false;
2253 }
2254 
IsFloatLiteral(ModuleValidatorShared & m,ParseNode * pn)2255 static bool IsFloatLiteral(ModuleValidatorShared& m, ParseNode* pn) {
2256   ParseNode* coercedExpr;
2257   Type coerceTo;
2258   if (!IsCoercionCall(m, pn, &coerceTo, &coercedExpr)) {
2259     return false;
2260   }
2261   // Don't fold into || to avoid clang/memcheck bug (bug 1077031).
2262   if (!coerceTo.isFloat()) {
2263     return false;
2264   }
2265   return IsNumericNonFloatLiteral(coercedExpr);
2266 }
2267 
IsNumericLiteral(ModuleValidatorShared & m,ParseNode * pn)2268 static bool IsNumericLiteral(ModuleValidatorShared& m, ParseNode* pn) {
2269   return IsNumericNonFloatLiteral(pn) || IsFloatLiteral(m, pn);
2270 }
2271 
2272 // The JS grammar treats -42 as -(42) (i.e., with separate grammar
2273 // productions) for the unary - and literal 42). However, the asm.js spec
2274 // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
2275 // so fold the two potential parse nodes into a single double value.
ExtractNumericNonFloatValue(ParseNode * pn,ParseNode ** out=nullptr)2276 static double ExtractNumericNonFloatValue(ParseNode* pn,
2277                                           ParseNode** out = nullptr) {
2278   MOZ_ASSERT(IsNumericNonFloatLiteral(pn));
2279 
2280   if (pn->isKind(ParseNodeKind::NegExpr)) {
2281     pn = UnaryKid(pn);
2282     if (out) {
2283       *out = pn;
2284     }
2285     return -NumberNodeValue(pn);
2286   }
2287 
2288   return NumberNodeValue(pn);
2289 }
2290 
ExtractNumericLiteral(ModuleValidatorShared & m,ParseNode * pn)2291 static NumLit ExtractNumericLiteral(ModuleValidatorShared& m, ParseNode* pn) {
2292   MOZ_ASSERT(IsNumericLiteral(m, pn));
2293 
2294   if (pn->isKind(ParseNodeKind::CallExpr)) {
2295     // Float literals are explicitly coerced and thus the coerced literal may be
2296     // any valid (non-float) numeric literal.
2297     MOZ_ASSERT(CallArgListLength(pn) == 1);
2298     pn = CallArgList(pn);
2299     double d = ExtractNumericNonFloatValue(pn);
2300     return NumLit(NumLit::Float, DoubleValue(d));
2301   }
2302 
2303   double d = ExtractNumericNonFloatValue(pn, &pn);
2304 
2305   // The asm.js spec syntactically distinguishes any literal containing a
2306   // decimal point or the literal -0 as having double type.
2307   if (NumberNodeHasFrac(pn) || IsNegativeZero(d)) {
2308     return NumLit(NumLit::Double, DoubleValue(d));
2309   }
2310 
2311   // The syntactic checks above rule out these double values.
2312   MOZ_ASSERT(!IsNegativeZero(d));
2313   MOZ_ASSERT(!IsNaN(d));
2314 
2315   // Although doubles can only *precisely* represent 53-bit integers, they
2316   // can *imprecisely* represent integers much bigger than an int64_t.
2317   // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t
2318   // is undefined, so test against the integer bounds using doubles.
2319   if (d < double(INT32_MIN) || d > double(UINT32_MAX)) {
2320     return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
2321   }
2322 
2323   // With the above syntactic and range limitations, d is definitely an
2324   // integer in the range [INT32_MIN, UINT32_MAX] range.
2325   int64_t i64 = int64_t(d);
2326   if (i64 >= 0) {
2327     if (i64 <= INT32_MAX) {
2328       return NumLit(NumLit::Fixnum, Int32Value(i64));
2329     }
2330     MOZ_ASSERT(i64 <= UINT32_MAX);
2331     return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
2332   }
2333   MOZ_ASSERT(i64 >= INT32_MIN);
2334   return NumLit(NumLit::NegativeInt, Int32Value(i64));
2335 }
2336 
IsLiteralInt(const NumLit & lit,uint32_t * u32)2337 static inline bool IsLiteralInt(const NumLit& lit, uint32_t* u32) {
2338   switch (lit.which()) {
2339     case NumLit::Fixnum:
2340     case NumLit::BigUnsigned:
2341     case NumLit::NegativeInt:
2342       *u32 = lit.toUint32();
2343       return true;
2344     case NumLit::Double:
2345     case NumLit::Float:
2346     case NumLit::OutOfRangeInt:
2347       return false;
2348   }
2349   MOZ_CRASH("Bad literal type");
2350 }
2351 
IsLiteralInt(ModuleValidatorShared & m,ParseNode * pn,uint32_t * u32)2352 static inline bool IsLiteralInt(ModuleValidatorShared& m, ParseNode* pn,
2353                                 uint32_t* u32) {
2354   return IsNumericLiteral(m, pn) &&
2355          IsLiteralInt(ExtractNumericLiteral(m, pn), u32);
2356 }
2357 
2358 /*****************************************************************************/
2359 
2360 namespace {
2361 
2362 using LabelVector = Vector<TaggedParserAtomIndex, 4, SystemAllocPolicy>;
2363 
2364 class MOZ_STACK_CLASS FunctionValidatorShared {
2365  public:
2366   struct Local {
2367     Type type;
2368     unsigned slot;
Local__anon6f1b47180811::FunctionValidatorShared::Local2369     Local(Type t, unsigned slot) : type(t), slot(slot) {
2370       MOZ_ASSERT(type.isCanonicalValType());
2371     }
2372   };
2373 
2374  protected:
2375   using LocalMap =
2376       HashMap<TaggedParserAtomIndex, Local, TaggedParserAtomIndexHasher>;
2377   using LabelMap =
2378       HashMap<TaggedParserAtomIndex, uint32_t, TaggedParserAtomIndexHasher>;
2379 
2380   // This is also a ModuleValidator<Unit>& after the appropriate static_cast<>.
2381   ModuleValidatorShared& m_;
2382 
2383   ParseNode* fn_;
2384   Bytes bytes_;
2385   Encoder encoder_;
2386   Uint32Vector callSiteLineNums_;
2387   LocalMap locals_;
2388 
2389   // Labels
2390   LabelMap breakLabels_;
2391   LabelMap continueLabels_;
2392   Uint32Vector breakableStack_;
2393   Uint32Vector continuableStack_;
2394   uint32_t blockDepth_;
2395 
2396   bool hasAlreadyReturned_;
2397   Maybe<ValType> ret_;
2398 
2399  private:
FunctionValidatorShared(ModuleValidatorShared & m,ParseNode * fn,JSContext * cx)2400   FunctionValidatorShared(ModuleValidatorShared& m, ParseNode* fn,
2401                           JSContext* cx)
2402       : m_(m),
2403         fn_(fn),
2404         encoder_(bytes_),
2405         locals_(cx),
2406         breakLabels_(cx),
2407         continueLabels_(cx),
2408         blockDepth_(0),
2409         hasAlreadyReturned_(false) {}
2410 
2411  protected:
2412   template <typename Unit>
FunctionValidatorShared(ModuleValidator<Unit> & m,ParseNode * fn,JSContext * cx)2413   FunctionValidatorShared(ModuleValidator<Unit>& m, ParseNode* fn,
2414                           JSContext* cx)
2415       : FunctionValidatorShared(static_cast<ModuleValidatorShared&>(m), fn,
2416                                 cx) {}
2417 
2418  public:
m() const2419   ModuleValidatorShared& m() const { return m_; }
2420 
cx() const2421   JSContext* cx() const { return m_.cx(); }
fn() const2422   ParseNode* fn() const { return fn_; }
2423 
define(ModuleValidatorShared::Func * func,unsigned line)2424   void define(ModuleValidatorShared::Func* func, unsigned line) {
2425     MOZ_ASSERT(!blockDepth_);
2426     MOZ_ASSERT(breakableStack_.empty());
2427     MOZ_ASSERT(continuableStack_.empty());
2428     MOZ_ASSERT(breakLabels_.empty());
2429     MOZ_ASSERT(continueLabels_.empty());
2430     func->define(fn_, line, std::move(bytes_), std::move(callSiteLineNums_));
2431   }
2432 
fail(ParseNode * pn,const char * str)2433   bool fail(ParseNode* pn, const char* str) { return m_.fail(pn, str); }
2434 
failf(ParseNode * pn,const char * fmt,...)2435   bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
2436     va_list ap;
2437     va_start(ap, fmt);
2438     m_.failfVAOffset(pn->pn_pos.begin, fmt, ap);
2439     va_end(ap);
2440     return false;
2441   }
2442 
failName(ParseNode * pn,const char * fmt,TaggedParserAtomIndex name)2443   bool failName(ParseNode* pn, const char* fmt, TaggedParserAtomIndex name) {
2444     return m_.failName(pn, fmt, name);
2445   }
2446 
2447   /***************************************************** Local scope setup */
2448 
addLocal(ParseNode * pn,TaggedParserAtomIndex name,Type type)2449   bool addLocal(ParseNode* pn, TaggedParserAtomIndex name, Type type) {
2450     LocalMap::AddPtr p = locals_.lookupForAdd(name);
2451     if (p) {
2452       return failName(pn, "duplicate local name '%s' not allowed", name);
2453     }
2454     return locals_.add(p, name, Local(type, locals_.count()));
2455   }
2456 
2457   /****************************** For consistency of returns in a function */
2458 
hasAlreadyReturned() const2459   bool hasAlreadyReturned() const { return hasAlreadyReturned_; }
2460 
returnedType() const2461   Maybe<ValType> returnedType() const { return ret_; }
2462 
setReturnedType(const Maybe<ValType> & ret)2463   void setReturnedType(const Maybe<ValType>& ret) {
2464     MOZ_ASSERT(!hasAlreadyReturned_);
2465     ret_ = ret;
2466     hasAlreadyReturned_ = true;
2467   }
2468 
2469   /**************************************************************** Labels */
2470  private:
writeBr(uint32_t absolute,Op op=Op::Br)2471   bool writeBr(uint32_t absolute, Op op = Op::Br) {
2472     MOZ_ASSERT(op == Op::Br || op == Op::BrIf);
2473     MOZ_ASSERT(absolute < blockDepth_);
2474     return encoder().writeOp(op) &&
2475            encoder().writeVarU32(blockDepth_ - 1 - absolute);
2476   }
removeLabel(TaggedParserAtomIndex label,LabelMap * map)2477   void removeLabel(TaggedParserAtomIndex label, LabelMap* map) {
2478     LabelMap::Ptr p = map->lookup(label);
2479     MOZ_ASSERT(p);
2480     map->remove(p);
2481   }
2482 
2483  public:
pushBreakableBlock()2484   bool pushBreakableBlock() {
2485     return encoder().writeOp(Op::Block) &&
2486            encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid)) &&
2487            breakableStack_.append(blockDepth_++);
2488   }
popBreakableBlock()2489   bool popBreakableBlock() {
2490     MOZ_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
2491     return encoder().writeOp(Op::End);
2492   }
2493 
pushUnbreakableBlock(const LabelVector * labels=nullptr)2494   bool pushUnbreakableBlock(const LabelVector* labels = nullptr) {
2495     if (labels) {
2496       for (TaggedParserAtomIndex label : *labels) {
2497         if (!breakLabels_.putNew(label, blockDepth_)) {
2498           return false;
2499         }
2500       }
2501     }
2502     blockDepth_++;
2503     return encoder().writeOp(Op::Block) &&
2504            encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid));
2505   }
popUnbreakableBlock(const LabelVector * labels=nullptr)2506   bool popUnbreakableBlock(const LabelVector* labels = nullptr) {
2507     if (labels) {
2508       for (TaggedParserAtomIndex label : *labels) {
2509         removeLabel(label, &breakLabels_);
2510       }
2511     }
2512     --blockDepth_;
2513     return encoder().writeOp(Op::End);
2514   }
2515 
pushContinuableBlock()2516   bool pushContinuableBlock() {
2517     return encoder().writeOp(Op::Block) &&
2518            encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid)) &&
2519            continuableStack_.append(blockDepth_++);
2520   }
popContinuableBlock()2521   bool popContinuableBlock() {
2522     MOZ_ALWAYS_TRUE(continuableStack_.popCopy() == --blockDepth_);
2523     return encoder().writeOp(Op::End);
2524   }
2525 
pushLoop()2526   bool pushLoop() {
2527     return encoder().writeOp(Op::Block) &&
2528            encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid)) &&
2529            encoder().writeOp(Op::Loop) &&
2530            encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid)) &&
2531            breakableStack_.append(blockDepth_++) &&
2532            continuableStack_.append(blockDepth_++);
2533   }
popLoop()2534   bool popLoop() {
2535     MOZ_ALWAYS_TRUE(continuableStack_.popCopy() == --blockDepth_);
2536     MOZ_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
2537     return encoder().writeOp(Op::End) && encoder().writeOp(Op::End);
2538   }
2539 
pushIf(size_t * typeAt)2540   bool pushIf(size_t* typeAt) {
2541     ++blockDepth_;
2542     return encoder().writeOp(Op::If) && encoder().writePatchableFixedU7(typeAt);
2543   }
switchToElse()2544   bool switchToElse() {
2545     MOZ_ASSERT(blockDepth_ > 0);
2546     return encoder().writeOp(Op::Else);
2547   }
setIfType(size_t typeAt,TypeCode type)2548   void setIfType(size_t typeAt, TypeCode type) {
2549     encoder().patchFixedU7(typeAt, uint8_t(type));
2550   }
popIf()2551   bool popIf() {
2552     MOZ_ASSERT(blockDepth_ > 0);
2553     --blockDepth_;
2554     return encoder().writeOp(Op::End);
2555   }
popIf(size_t typeAt,TypeCode type)2556   bool popIf(size_t typeAt, TypeCode type) {
2557     MOZ_ASSERT(blockDepth_ > 0);
2558     --blockDepth_;
2559     if (!encoder().writeOp(Op::End)) {
2560       return false;
2561     }
2562 
2563     setIfType(typeAt, type);
2564     return true;
2565   }
2566 
writeBreakIf()2567   bool writeBreakIf() { return writeBr(breakableStack_.back(), Op::BrIf); }
writeContinueIf()2568   bool writeContinueIf() { return writeBr(continuableStack_.back(), Op::BrIf); }
writeUnlabeledBreakOrContinue(bool isBreak)2569   bool writeUnlabeledBreakOrContinue(bool isBreak) {
2570     return writeBr(isBreak ? breakableStack_.back() : continuableStack_.back());
2571   }
writeContinue()2572   bool writeContinue() { return writeBr(continuableStack_.back()); }
2573 
addLabels(const LabelVector & labels,uint32_t relativeBreakDepth,uint32_t relativeContinueDepth)2574   bool addLabels(const LabelVector& labels, uint32_t relativeBreakDepth,
2575                  uint32_t relativeContinueDepth) {
2576     for (TaggedParserAtomIndex label : labels) {
2577       if (!breakLabels_.putNew(label, blockDepth_ + relativeBreakDepth)) {
2578         return false;
2579       }
2580       if (!continueLabels_.putNew(label, blockDepth_ + relativeContinueDepth)) {
2581         return false;
2582       }
2583     }
2584     return true;
2585   }
removeLabels(const LabelVector & labels)2586   void removeLabels(const LabelVector& labels) {
2587     for (TaggedParserAtomIndex label : labels) {
2588       removeLabel(label, &breakLabels_);
2589       removeLabel(label, &continueLabels_);
2590     }
2591   }
writeLabeledBreakOrContinue(TaggedParserAtomIndex label,bool isBreak)2592   bool writeLabeledBreakOrContinue(TaggedParserAtomIndex label, bool isBreak) {
2593     LabelMap& map = isBreak ? breakLabels_ : continueLabels_;
2594     if (LabelMap::Ptr p = map.lookup(label)) {
2595       return writeBr(p->value());
2596     }
2597     MOZ_CRASH("nonexistent label");
2598   }
2599 
2600   /*************************************************** Read-only interface */
2601 
lookupLocal(TaggedParserAtomIndex name) const2602   const Local* lookupLocal(TaggedParserAtomIndex name) const {
2603     if (auto p = locals_.lookup(name)) {
2604       return &p->value();
2605     }
2606     return nullptr;
2607   }
2608 
lookupGlobal(TaggedParserAtomIndex name) const2609   const ModuleValidatorShared::Global* lookupGlobal(
2610       TaggedParserAtomIndex name) const {
2611     if (locals_.has(name)) {
2612       return nullptr;
2613     }
2614     return m_.lookupGlobal(name);
2615   }
2616 
numLocals() const2617   size_t numLocals() const { return locals_.count(); }
2618 
2619   /**************************************************** Encoding interface */
2620 
encoder()2621   Encoder& encoder() { return encoder_; }
2622 
writeInt32Lit(int32_t i32)2623   [[nodiscard]] bool writeInt32Lit(int32_t i32) {
2624     return encoder().writeOp(Op::I32Const) && encoder().writeVarS32(i32);
2625   }
writeConstExpr(const NumLit & lit)2626   [[nodiscard]] bool writeConstExpr(const NumLit& lit) {
2627     switch (lit.which()) {
2628       case NumLit::Fixnum:
2629       case NumLit::NegativeInt:
2630       case NumLit::BigUnsigned:
2631         return writeInt32Lit(lit.toInt32());
2632       case NumLit::Float:
2633         return encoder().writeOp(Op::F32Const) &&
2634                encoder().writeFixedF32(lit.toFloat());
2635       case NumLit::Double:
2636         return encoder().writeOp(Op::F64Const) &&
2637                encoder().writeFixedF64(lit.toDouble());
2638       case NumLit::OutOfRangeInt:
2639         break;
2640     }
2641     MOZ_CRASH("unexpected literal type");
2642   }
2643 };
2644 
2645 // Encapsulates the building of an asm bytecode function from an asm.js function
2646 // source code, packing the asm.js code into the asm bytecode form that can
2647 // be decoded and compiled with a FunctionCompiler.
2648 template <typename Unit>
2649 class MOZ_STACK_CLASS FunctionValidator : public FunctionValidatorShared {
2650  public:
FunctionValidator(ModuleValidator<Unit> & m,ParseNode * fn)2651   FunctionValidator(ModuleValidator<Unit>& m, ParseNode* fn)
2652       : FunctionValidatorShared(m, fn, m.cx()) {}
2653 
2654  public:
m() const2655   ModuleValidator<Unit>& m() const {
2656     return static_cast<ModuleValidator<Unit>&>(FunctionValidatorShared::m());
2657   }
2658 
writeCall(ParseNode * pn,Op op)2659   [[nodiscard]] bool writeCall(ParseNode* pn, Op op) {
2660     if (!encoder().writeOp(op)) {
2661       return false;
2662     }
2663 
2664     return appendCallSiteLineNumber(pn);
2665   }
writeCall(ParseNode * pn,MozOp op)2666   [[nodiscard]] bool writeCall(ParseNode* pn, MozOp op) {
2667     if (!encoder().writeOp(op)) {
2668       return false;
2669     }
2670 
2671     return appendCallSiteLineNumber(pn);
2672   }
prepareCall(ParseNode * pn)2673   [[nodiscard]] bool prepareCall(ParseNode* pn) {
2674     return appendCallSiteLineNumber(pn);
2675   }
2676 
2677  private:
appendCallSiteLineNumber(ParseNode * node)2678   [[nodiscard]] bool appendCallSiteLineNumber(ParseNode* node) {
2679     const TokenStreamAnyChars& anyChars = m().tokenStream().anyCharsAccess();
2680     auto lineToken = anyChars.lineToken(node->pn_pos.begin);
2681     uint32_t lineNumber = anyChars.lineNumber(lineToken);
2682     if (lineNumber > CallSiteDesc::MAX_LINE_OR_BYTECODE_VALUE) {
2683       return fail(node, "line number exceeding implementation limits");
2684     }
2685     return callSiteLineNums_.append(lineNumber);
2686   }
2687 };
2688 
2689 } /* anonymous namespace */
2690 
2691 /*****************************************************************************/
2692 // asm.js type-checking and code-generation algorithm
2693 
CheckIdentifier(ModuleValidatorShared & m,ParseNode * usepn,TaggedParserAtomIndex name)2694 static bool CheckIdentifier(ModuleValidatorShared& m, ParseNode* usepn,
2695                             TaggedParserAtomIndex name) {
2696   if (name == TaggedParserAtomIndex::WellKnown::arguments() ||
2697       name == TaggedParserAtomIndex::WellKnown::eval()) {
2698     return m.failName(usepn, "'%s' is not an allowed identifier", name);
2699   }
2700   return true;
2701 }
2702 
CheckModuleLevelName(ModuleValidatorShared & m,ParseNode * usepn,TaggedParserAtomIndex name)2703 static bool CheckModuleLevelName(ModuleValidatorShared& m, ParseNode* usepn,
2704                                  TaggedParserAtomIndex name) {
2705   if (!CheckIdentifier(m, usepn, name)) {
2706     return false;
2707   }
2708 
2709   if (name == m.moduleFunctionName() || name == m.globalArgumentName() ||
2710       name == m.importArgumentName() || name == m.bufferArgumentName() ||
2711       m.lookupGlobal(name)) {
2712     return m.failName(usepn, "duplicate name '%s' not allowed", name);
2713   }
2714 
2715   return true;
2716 }
2717 
CheckFunctionHead(ModuleValidatorShared & m,FunctionNode * funNode)2718 static bool CheckFunctionHead(ModuleValidatorShared& m, FunctionNode* funNode) {
2719   FunctionBox* funbox = funNode->funbox();
2720   MOZ_ASSERT(!funbox->hasExprBody());
2721 
2722   if (funbox->hasRest()) {
2723     return m.fail(funNode, "rest args not allowed");
2724   }
2725   if (funbox->hasDestructuringArgs) {
2726     return m.fail(funNode, "destructuring args not allowed");
2727   }
2728   return true;
2729 }
2730 
CheckArgument(ModuleValidatorShared & m,ParseNode * arg,TaggedParserAtomIndex * name)2731 static bool CheckArgument(ModuleValidatorShared& m, ParseNode* arg,
2732                           TaggedParserAtomIndex* name) {
2733   *name = TaggedParserAtomIndex::null();
2734 
2735   if (!arg->isKind(ParseNodeKind::Name)) {
2736     return m.fail(arg, "argument is not a plain name");
2737   }
2738 
2739   TaggedParserAtomIndex argName = arg->as<NameNode>().name();
2740   if (!CheckIdentifier(m, arg, argName)) {
2741     return false;
2742   }
2743 
2744   *name = argName;
2745   return true;
2746 }
2747 
CheckModuleArgument(ModuleValidatorShared & m,ParseNode * arg,TaggedParserAtomIndex * name)2748 static bool CheckModuleArgument(ModuleValidatorShared& m, ParseNode* arg,
2749                                 TaggedParserAtomIndex* name) {
2750   if (!CheckArgument(m, arg, name)) {
2751     return false;
2752   }
2753 
2754   if (!CheckModuleLevelName(m, arg, *name)) {
2755     return false;
2756   }
2757 
2758   return true;
2759 }
2760 
CheckModuleArguments(ModuleValidatorShared & m,FunctionNode * funNode)2761 static bool CheckModuleArguments(ModuleValidatorShared& m,
2762                                  FunctionNode* funNode) {
2763   unsigned numFormals;
2764   ParseNode* arg1 = FunctionFormalParametersList(funNode, &numFormals);
2765   ParseNode* arg2 = arg1 ? NextNode(arg1) : nullptr;
2766   ParseNode* arg3 = arg2 ? NextNode(arg2) : nullptr;
2767 
2768   if (numFormals > 3) {
2769     return m.fail(funNode, "asm.js modules takes at most 3 argument");
2770   }
2771 
2772   TaggedParserAtomIndex arg1Name;
2773   if (arg1 && !CheckModuleArgument(m, arg1, &arg1Name)) {
2774     return false;
2775   }
2776   if (!m.initGlobalArgumentName(arg1Name)) {
2777     return false;
2778   }
2779 
2780   TaggedParserAtomIndex arg2Name;
2781   if (arg2 && !CheckModuleArgument(m, arg2, &arg2Name)) {
2782     return false;
2783   }
2784   if (!m.initImportArgumentName(arg2Name)) {
2785     return false;
2786   }
2787 
2788   TaggedParserAtomIndex arg3Name;
2789   if (arg3 && !CheckModuleArgument(m, arg3, &arg3Name)) {
2790     return false;
2791   }
2792   if (!m.initBufferArgumentName(arg3Name)) {
2793     return false;
2794   }
2795 
2796   return true;
2797 }
2798 
CheckPrecedingStatements(ModuleValidatorShared & m,ParseNode * stmtList)2799 static bool CheckPrecedingStatements(ModuleValidatorShared& m,
2800                                      ParseNode* stmtList) {
2801   MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
2802 
2803   ParseNode* stmt = ListHead(stmtList);
2804   for (unsigned i = 0, n = ListLength(stmtList); i < n; i++) {
2805     if (!IsIgnoredDirective(m.cx(), stmt)) {
2806       return m.fail(stmt, "invalid asm.js statement");
2807     }
2808   }
2809 
2810   return true;
2811 }
2812 
CheckGlobalVariableInitConstant(ModuleValidatorShared & m,TaggedParserAtomIndex varName,ParseNode * initNode,bool isConst)2813 static bool CheckGlobalVariableInitConstant(ModuleValidatorShared& m,
2814                                             TaggedParserAtomIndex varName,
2815                                             ParseNode* initNode, bool isConst) {
2816   NumLit lit = ExtractNumericLiteral(m, initNode);
2817   if (!lit.valid()) {
2818     return m.fail(initNode,
2819                   "global initializer is out of representable integer range");
2820   }
2821 
2822   Type canonicalType = Type::canonicalize(Type::lit(lit));
2823   if (!canonicalType.isGlobalVarType()) {
2824     return m.fail(initNode, "global variable type not allowed");
2825   }
2826 
2827   return m.addGlobalVarInit(varName, lit, canonicalType, isConst);
2828 }
2829 
CheckTypeAnnotation(ModuleValidatorShared & m,ParseNode * coercionNode,Type * coerceTo,ParseNode ** coercedExpr=nullptr)2830 static bool CheckTypeAnnotation(ModuleValidatorShared& m,
2831                                 ParseNode* coercionNode, Type* coerceTo,
2832                                 ParseNode** coercedExpr = nullptr) {
2833   switch (coercionNode->getKind()) {
2834     case ParseNodeKind::BitOrExpr: {
2835       ParseNode* rhs = BitwiseRight(coercionNode);
2836       uint32_t i;
2837       if (!IsLiteralInt(m, rhs, &i) || i != 0) {
2838         return m.fail(rhs, "must use |0 for argument/return coercion");
2839       }
2840       *coerceTo = Type::Int;
2841       if (coercedExpr) {
2842         *coercedExpr = BitwiseLeft(coercionNode);
2843       }
2844       return true;
2845     }
2846     case ParseNodeKind::PosExpr: {
2847       *coerceTo = Type::Double;
2848       if (coercedExpr) {
2849         *coercedExpr = UnaryKid(coercionNode);
2850       }
2851       return true;
2852     }
2853     case ParseNodeKind::CallExpr: {
2854       if (IsCoercionCall(m, coercionNode, coerceTo, coercedExpr)) {
2855         return true;
2856       }
2857       break;
2858     }
2859     default:;
2860   }
2861 
2862   return m.fail(coercionNode, "must be of the form +x, x|0 or fround(x)");
2863 }
2864 
CheckGlobalVariableInitImport(ModuleValidatorShared & m,TaggedParserAtomIndex varName,ParseNode * initNode,bool isConst)2865 static bool CheckGlobalVariableInitImport(ModuleValidatorShared& m,
2866                                           TaggedParserAtomIndex varName,
2867                                           ParseNode* initNode, bool isConst) {
2868   Type coerceTo;
2869   ParseNode* coercedExpr;
2870   if (!CheckTypeAnnotation(m, initNode, &coerceTo, &coercedExpr)) {
2871     return false;
2872   }
2873 
2874   if (!coercedExpr->isKind(ParseNodeKind::DotExpr)) {
2875     return m.failName(coercedExpr, "invalid import expression for global '%s'",
2876                       varName);
2877   }
2878 
2879   if (!coerceTo.isGlobalVarType()) {
2880     return m.fail(initNode, "global variable type not allowed");
2881   }
2882 
2883   ParseNode* base = DotBase(coercedExpr);
2884   TaggedParserAtomIndex field = DotMember(coercedExpr);
2885 
2886   TaggedParserAtomIndex importName = m.importArgumentName();
2887   if (!importName) {
2888     return m.fail(coercedExpr,
2889                   "cannot import without an asm.js foreign parameter");
2890   }
2891   if (!IsUseOfName(base, importName)) {
2892     return m.failName(coercedExpr, "base of import expression must be '%s'",
2893                       importName);
2894   }
2895 
2896   return m.addGlobalVarImport(varName, field, coerceTo, isConst);
2897 }
2898 
IsArrayViewCtorName(ModuleValidatorShared & m,TaggedParserAtomIndex name,Scalar::Type * type)2899 static bool IsArrayViewCtorName(ModuleValidatorShared& m,
2900                                 TaggedParserAtomIndex name,
2901                                 Scalar::Type* type) {
2902   if (name == TaggedParserAtomIndex::WellKnown::Int8Array()) {
2903     *type = Scalar::Int8;
2904   } else if (name == TaggedParserAtomIndex::WellKnown::Uint8Array()) {
2905     *type = Scalar::Uint8;
2906   } else if (name == TaggedParserAtomIndex::WellKnown::Int16Array()) {
2907     *type = Scalar::Int16;
2908   } else if (name == TaggedParserAtomIndex::WellKnown::Uint16Array()) {
2909     *type = Scalar::Uint16;
2910   } else if (name == TaggedParserAtomIndex::WellKnown::Int32Array()) {
2911     *type = Scalar::Int32;
2912   } else if (name == TaggedParserAtomIndex::WellKnown::Uint32Array()) {
2913     *type = Scalar::Uint32;
2914   } else if (name == TaggedParserAtomIndex::WellKnown::Float32Array()) {
2915     *type = Scalar::Float32;
2916   } else if (name == TaggedParserAtomIndex::WellKnown::Float64Array()) {
2917     *type = Scalar::Float64;
2918   } else {
2919     return false;
2920   }
2921   return true;
2922 }
2923 
CheckNewArrayViewArgs(ModuleValidatorShared & m,ParseNode * newExpr,TaggedParserAtomIndex bufferName)2924 static bool CheckNewArrayViewArgs(ModuleValidatorShared& m, ParseNode* newExpr,
2925                                   TaggedParserAtomIndex bufferName) {
2926   ParseNode* ctorExpr = BinaryLeft(newExpr);
2927   ParseNode* ctorArgs = BinaryRight(newExpr);
2928   ParseNode* bufArg = ListHead(ctorArgs);
2929   if (!bufArg || NextNode(bufArg) != nullptr) {
2930     return m.fail(ctorExpr,
2931                   "array view constructor takes exactly one argument");
2932   }
2933 
2934   if (!IsUseOfName(bufArg, bufferName)) {
2935     return m.failName(bufArg, "argument to array view constructor must be '%s'",
2936                       bufferName);
2937   }
2938 
2939   return true;
2940 }
2941 
CheckNewArrayView(ModuleValidatorShared & m,TaggedParserAtomIndex varName,ParseNode * newExpr)2942 static bool CheckNewArrayView(ModuleValidatorShared& m,
2943                               TaggedParserAtomIndex varName,
2944                               ParseNode* newExpr) {
2945   TaggedParserAtomIndex globalName = m.globalArgumentName();
2946   if (!globalName) {
2947     return m.fail(
2948         newExpr, "cannot create array view without an asm.js global parameter");
2949   }
2950 
2951   TaggedParserAtomIndex bufferName = m.bufferArgumentName();
2952   if (!bufferName) {
2953     return m.fail(newExpr,
2954                   "cannot create array view without an asm.js heap parameter");
2955   }
2956 
2957   ParseNode* ctorExpr = BinaryLeft(newExpr);
2958 
2959   TaggedParserAtomIndex field;
2960   Scalar::Type type;
2961   if (ctorExpr->isKind(ParseNodeKind::DotExpr)) {
2962     ParseNode* base = DotBase(ctorExpr);
2963 
2964     if (!IsUseOfName(base, globalName)) {
2965       return m.failName(base, "expecting '%s.*Array", globalName);
2966     }
2967 
2968     field = DotMember(ctorExpr);
2969     if (!IsArrayViewCtorName(m, field, &type)) {
2970       return m.fail(ctorExpr, "could not match typed array name");
2971     }
2972   } else {
2973     if (!ctorExpr->isKind(ParseNodeKind::Name)) {
2974       return m.fail(ctorExpr,
2975                     "expecting name of imported array view constructor");
2976     }
2977 
2978     TaggedParserAtomIndex globalName = ctorExpr->as<NameNode>().name();
2979     const ModuleValidatorShared::Global* global = m.lookupGlobal(globalName);
2980     if (!global) {
2981       return m.failName(ctorExpr, "%s not found in module global scope",
2982                         globalName);
2983     }
2984 
2985     if (global->which() != ModuleValidatorShared::Global::ArrayViewCtor) {
2986       return m.failName(ctorExpr,
2987                         "%s must be an imported array view constructor",
2988                         globalName);
2989     }
2990 
2991     type = global->viewType();
2992   }
2993 
2994   if (!CheckNewArrayViewArgs(m, newExpr, bufferName)) {
2995     return false;
2996   }
2997 
2998   return m.addArrayView(varName, type, field);
2999 }
3000 
CheckGlobalMathImport(ModuleValidatorShared & m,ParseNode * initNode,TaggedParserAtomIndex varName,TaggedParserAtomIndex field)3001 static bool CheckGlobalMathImport(ModuleValidatorShared& m, ParseNode* initNode,
3002                                   TaggedParserAtomIndex varName,
3003                                   TaggedParserAtomIndex field) {
3004   // Math builtin, with the form glob.Math.[[builtin]]
3005   ModuleValidatorShared::MathBuiltin mathBuiltin;
3006   if (!m.lookupStandardLibraryMathName(field, &mathBuiltin)) {
3007     return m.failName(initNode, "'%s' is not a standard Math builtin", field);
3008   }
3009 
3010   switch (mathBuiltin.kind) {
3011     case ModuleValidatorShared::MathBuiltin::Function:
3012       return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
3013     case ModuleValidatorShared::MathBuiltin::Constant:
3014       return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
3015     default:
3016       break;
3017   }
3018   MOZ_CRASH("unexpected or uninitialized math builtin type");
3019 }
3020 
CheckGlobalDotImport(ModuleValidatorShared & m,TaggedParserAtomIndex varName,ParseNode * initNode)3021 static bool CheckGlobalDotImport(ModuleValidatorShared& m,
3022                                  TaggedParserAtomIndex varName,
3023                                  ParseNode* initNode) {
3024   ParseNode* base = DotBase(initNode);
3025   TaggedParserAtomIndex field = DotMember(initNode);
3026 
3027   if (base->isKind(ParseNodeKind::DotExpr)) {
3028     ParseNode* global = DotBase(base);
3029     TaggedParserAtomIndex math = DotMember(base);
3030 
3031     TaggedParserAtomIndex globalName = m.globalArgumentName();
3032     if (!globalName) {
3033       return m.fail(
3034           base, "import statement requires the module have a stdlib parameter");
3035     }
3036 
3037     if (!IsUseOfName(global, globalName)) {
3038       if (global->isKind(ParseNodeKind::DotExpr)) {
3039         return m.failName(base,
3040                           "imports can have at most two dot accesses "
3041                           "(e.g. %s.Math.sin)",
3042                           globalName);
3043       }
3044       return m.failName(base, "expecting %s.*", globalName);
3045     }
3046 
3047     if (math == TaggedParserAtomIndex::WellKnown::Math()) {
3048       return CheckGlobalMathImport(m, initNode, varName, field);
3049     }
3050     return m.failName(base, "expecting %s.Math", globalName);
3051   }
3052 
3053   if (!base->isKind(ParseNodeKind::Name)) {
3054     return m.fail(base, "expected name of variable or parameter");
3055   }
3056 
3057   auto baseName = base->as<NameNode>().name();
3058   if (baseName == m.globalArgumentName()) {
3059     if (field == TaggedParserAtomIndex::WellKnown::NaN()) {
3060       return m.addGlobalConstant(varName, GenericNaN(), field);
3061     }
3062     if (field == TaggedParserAtomIndex::WellKnown::Infinity()) {
3063       return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
3064     }
3065 
3066     Scalar::Type type;
3067     if (IsArrayViewCtorName(m, field, &type)) {
3068       return m.addArrayViewCtor(varName, type, field);
3069     }
3070 
3071     return m.failName(
3072         initNode, "'%s' is not a standard constant or typed array name", field);
3073   }
3074 
3075   if (baseName != m.importArgumentName()) {
3076     return m.fail(base, "expected global or import name");
3077   }
3078 
3079   return m.addFFI(varName, field);
3080 }
3081 
CheckModuleGlobal(ModuleValidatorShared & m,ParseNode * decl,bool isConst)3082 static bool CheckModuleGlobal(ModuleValidatorShared& m, ParseNode* decl,
3083                               bool isConst) {
3084   if (!decl->isKind(ParseNodeKind::AssignExpr)) {
3085     return m.fail(decl, "module import needs initializer");
3086   }
3087   AssignmentNode* assignNode = &decl->as<AssignmentNode>();
3088 
3089   ParseNode* var = assignNode->left();
3090 
3091   if (!var->isKind(ParseNodeKind::Name)) {
3092     return m.fail(var, "import variable is not a plain name");
3093   }
3094 
3095   TaggedParserAtomIndex varName = var->as<NameNode>().name();
3096   if (!CheckModuleLevelName(m, var, varName)) {
3097     return false;
3098   }
3099 
3100   ParseNode* initNode = assignNode->right();
3101 
3102   if (IsNumericLiteral(m, initNode)) {
3103     return CheckGlobalVariableInitConstant(m, varName, initNode, isConst);
3104   }
3105 
3106   if (initNode->isKind(ParseNodeKind::BitOrExpr) ||
3107       initNode->isKind(ParseNodeKind::PosExpr) ||
3108       initNode->isKind(ParseNodeKind::CallExpr)) {
3109     return CheckGlobalVariableInitImport(m, varName, initNode, isConst);
3110   }
3111 
3112   if (initNode->isKind(ParseNodeKind::NewExpr)) {
3113     return CheckNewArrayView(m, varName, initNode);
3114   }
3115 
3116   if (initNode->isKind(ParseNodeKind::DotExpr)) {
3117     return CheckGlobalDotImport(m, varName, initNode);
3118   }
3119 
3120   return m.fail(initNode, "unsupported import expression");
3121 }
3122 
3123 template <typename Unit>
CheckModuleProcessingDirectives(ModuleValidator<Unit> & m)3124 static bool CheckModuleProcessingDirectives(ModuleValidator<Unit>& m) {
3125   auto& ts = m.parser().tokenStream;
3126   while (true) {
3127     bool matched;
3128     if (!ts.matchToken(&matched, TokenKind::String,
3129                        TokenStreamShared::SlashIsRegExp)) {
3130       return false;
3131     }
3132     if (!matched) {
3133       return true;
3134     }
3135 
3136     if (!IsIgnoredDirectiveName(m.cx(),
3137                                 ts.anyCharsAccess().currentToken().atom())) {
3138       return m.failCurrentOffset("unsupported processing directive");
3139     }
3140 
3141     TokenKind tt;
3142     if (!ts.getToken(&tt)) {
3143       return false;
3144     }
3145     if (tt != TokenKind::Semi) {
3146       return m.failCurrentOffset("expected semicolon after string literal");
3147     }
3148   }
3149 }
3150 
3151 template <typename Unit>
CheckModuleGlobals(ModuleValidator<Unit> & m)3152 static bool CheckModuleGlobals(ModuleValidator<Unit>& m) {
3153   while (true) {
3154     ParseNode* varStmt;
3155     if (!ParseVarOrConstStatement(m.parser(), &varStmt)) {
3156       return false;
3157     }
3158     if (!varStmt) {
3159       break;
3160     }
3161     for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
3162       if (!CheckModuleGlobal(m, var,
3163                              varStmt->isKind(ParseNodeKind::ConstDecl))) {
3164         return false;
3165       }
3166     }
3167   }
3168 
3169   return true;
3170 }
3171 
ArgFail(FunctionValidatorShared & f,TaggedParserAtomIndex argName,ParseNode * stmt)3172 static bool ArgFail(FunctionValidatorShared& f, TaggedParserAtomIndex argName,
3173                     ParseNode* stmt) {
3174   return f.failName(stmt,
3175                     "expecting argument type declaration for '%s' of the "
3176                     "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'",
3177                     argName);
3178 }
3179 
CheckArgumentType(FunctionValidatorShared & f,ParseNode * stmt,TaggedParserAtomIndex name,Type * type)3180 static bool CheckArgumentType(FunctionValidatorShared& f, ParseNode* stmt,
3181                               TaggedParserAtomIndex name, Type* type) {
3182   if (!stmt || !IsExpressionStatement(stmt)) {
3183     return ArgFail(f, name, stmt ? stmt : f.fn());
3184   }
3185 
3186   ParseNode* initNode = ExpressionStatementExpr(stmt);
3187   if (!initNode->isKind(ParseNodeKind::AssignExpr)) {
3188     return ArgFail(f, name, stmt);
3189   }
3190 
3191   ParseNode* argNode = BinaryLeft(initNode);
3192   ParseNode* coercionNode = BinaryRight(initNode);
3193 
3194   if (!IsUseOfName(argNode, name)) {
3195     return ArgFail(f, name, stmt);
3196   }
3197 
3198   ParseNode* coercedExpr;
3199   if (!CheckTypeAnnotation(f.m(), coercionNode, type, &coercedExpr)) {
3200     return false;
3201   }
3202 
3203   if (!type->isArgType()) {
3204     return f.failName(stmt, "invalid type for argument '%s'", name);
3205   }
3206 
3207   if (!IsUseOfName(coercedExpr, name)) {
3208     return ArgFail(f, name, stmt);
3209   }
3210 
3211   return true;
3212 }
3213 
CheckProcessingDirectives(ModuleValidatorShared & m,ParseNode ** stmtIter)3214 static bool CheckProcessingDirectives(ModuleValidatorShared& m,
3215                                       ParseNode** stmtIter) {
3216   ParseNode* stmt = *stmtIter;
3217 
3218   while (stmt && IsIgnoredDirective(m.cx(), stmt)) {
3219     stmt = NextNode(stmt);
3220   }
3221 
3222   *stmtIter = stmt;
3223   return true;
3224 }
3225 
CheckArguments(FunctionValidatorShared & f,ParseNode ** stmtIter,ValTypeVector * argTypes)3226 static bool CheckArguments(FunctionValidatorShared& f, ParseNode** stmtIter,
3227                            ValTypeVector* argTypes) {
3228   ParseNode* stmt = *stmtIter;
3229 
3230   unsigned numFormals;
3231   ParseNode* argpn = FunctionFormalParametersList(f.fn(), &numFormals);
3232 
3233   for (unsigned i = 0; i < numFormals;
3234        i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) {
3235     TaggedParserAtomIndex name;
3236     if (!CheckArgument(f.m(), argpn, &name)) {
3237       return false;
3238     }
3239 
3240     Type type;
3241     if (!CheckArgumentType(f, stmt, name, &type)) {
3242       return false;
3243     }
3244 
3245     if (!argTypes->append(type.canonicalToValType())) {
3246       return false;
3247     }
3248 
3249     if (!f.addLocal(argpn, name, type)) {
3250       return false;
3251     }
3252   }
3253 
3254   *stmtIter = stmt;
3255   return true;
3256 }
3257 
IsLiteralOrConst(FunctionValidatorShared & f,ParseNode * pn,NumLit * lit)3258 static bool IsLiteralOrConst(FunctionValidatorShared& f, ParseNode* pn,
3259                              NumLit* lit) {
3260   if (pn->isKind(ParseNodeKind::Name)) {
3261     const ModuleValidatorShared::Global* global =
3262         f.lookupGlobal(pn->as<NameNode>().name());
3263     if (!global ||
3264         global->which() != ModuleValidatorShared::Global::ConstantLiteral) {
3265       return false;
3266     }
3267 
3268     *lit = global->constLiteralValue();
3269     return true;
3270   }
3271 
3272   if (!IsNumericLiteral(f.m(), pn)) {
3273     return false;
3274   }
3275 
3276   *lit = ExtractNumericLiteral(f.m(), pn);
3277   return true;
3278 }
3279 
CheckFinalReturn(FunctionValidatorShared & f,ParseNode * lastNonEmptyStmt)3280 static bool CheckFinalReturn(FunctionValidatorShared& f,
3281                              ParseNode* lastNonEmptyStmt) {
3282   if (!f.encoder().writeOp(Op::End)) {
3283     return false;
3284   }
3285 
3286   if (!f.hasAlreadyReturned()) {
3287     f.setReturnedType(Nothing());
3288     return true;
3289   }
3290 
3291   if (!lastNonEmptyStmt->isKind(ParseNodeKind::ReturnStmt) &&
3292       f.returnedType()) {
3293     return f.fail(lastNonEmptyStmt,
3294                   "void incompatible with previous return type");
3295   }
3296 
3297   return true;
3298 }
3299 
CheckVariable(FunctionValidatorShared & f,ParseNode * decl,ValTypeVector * types,Vector<NumLit> * inits)3300 static bool CheckVariable(FunctionValidatorShared& f, ParseNode* decl,
3301                           ValTypeVector* types, Vector<NumLit>* inits) {
3302   if (!decl->isKind(ParseNodeKind::AssignExpr)) {
3303     return f.failName(
3304         decl, "var '%s' needs explicit type declaration via an initial value",
3305         decl->as<NameNode>().name());
3306   }
3307   AssignmentNode* assignNode = &decl->as<AssignmentNode>();
3308 
3309   ParseNode* var = assignNode->left();
3310 
3311   if (!var->isKind(ParseNodeKind::Name)) {
3312     return f.fail(var, "local variable is not a plain name");
3313   }
3314 
3315   TaggedParserAtomIndex name = var->as<NameNode>().name();
3316 
3317   if (!CheckIdentifier(f.m(), var, name)) {
3318     return false;
3319   }
3320 
3321   ParseNode* initNode = assignNode->right();
3322 
3323   NumLit lit;
3324   if (!IsLiteralOrConst(f, initNode, &lit)) {
3325     return f.failName(
3326         var, "var '%s' initializer must be literal or const literal", name);
3327   }
3328 
3329   if (!lit.valid()) {
3330     return f.failName(var, "var '%s' initializer out of range", name);
3331   }
3332 
3333   Type type = Type::canonicalize(Type::lit(lit));
3334 
3335   return f.addLocal(var, name, type) &&
3336          types->append(type.canonicalToValType()) && inits->append(lit);
3337 }
3338 
CheckVariables(FunctionValidatorShared & f,ParseNode ** stmtIter)3339 static bool CheckVariables(FunctionValidatorShared& f, ParseNode** stmtIter) {
3340   ParseNode* stmt = *stmtIter;
3341 
3342   uint32_t firstVar = f.numLocals();
3343 
3344   ValTypeVector types;
3345   Vector<NumLit> inits(f.cx());
3346 
3347   for (; stmt && stmt->isKind(ParseNodeKind::VarStmt);
3348        stmt = NextNonEmptyStatement(stmt)) {
3349     for (ParseNode* var = VarListHead(stmt); var; var = NextNode(var)) {
3350       if (!CheckVariable(f, var, &types, &inits)) {
3351         return false;
3352       }
3353     }
3354   }
3355 
3356   MOZ_ASSERT(f.encoder().empty());
3357 
3358   if (!EncodeLocalEntries(f.encoder(), types)) {
3359     return false;
3360   }
3361 
3362   for (uint32_t i = 0; i < inits.length(); i++) {
3363     NumLit lit = inits[i];
3364     if (lit.isZeroBits()) {
3365       continue;
3366     }
3367     if (!f.writeConstExpr(lit)) {
3368       return false;
3369     }
3370     if (!f.encoder().writeOp(Op::LocalSet)) {
3371       return false;
3372     }
3373     if (!f.encoder().writeVarU32(firstVar + i)) {
3374       return false;
3375     }
3376   }
3377 
3378   *stmtIter = stmt;
3379   return true;
3380 }
3381 
3382 template <typename Unit>
3383 static bool CheckExpr(FunctionValidator<Unit>& f, ParseNode* expr, Type* type);
3384 
3385 template <typename Unit>
CheckNumericLiteral(FunctionValidator<Unit> & f,ParseNode * num,Type * type)3386 static bool CheckNumericLiteral(FunctionValidator<Unit>& f, ParseNode* num,
3387                                 Type* type) {
3388   NumLit lit = ExtractNumericLiteral(f.m(), num);
3389   if (!lit.valid()) {
3390     return f.fail(num, "numeric literal out of representable integer range");
3391   }
3392   *type = Type::lit(lit);
3393   return f.writeConstExpr(lit);
3394 }
3395 
CheckVarRef(FunctionValidatorShared & f,ParseNode * varRef,Type * type)3396 static bool CheckVarRef(FunctionValidatorShared& f, ParseNode* varRef,
3397                         Type* type) {
3398   TaggedParserAtomIndex name = varRef->as<NameNode>().name();
3399 
3400   if (const FunctionValidatorShared::Local* local = f.lookupLocal(name)) {
3401     if (!f.encoder().writeOp(Op::LocalGet)) {
3402       return false;
3403     }
3404     if (!f.encoder().writeVarU32(local->slot)) {
3405       return false;
3406     }
3407     *type = local->type;
3408     return true;
3409   }
3410 
3411   if (const ModuleValidatorShared::Global* global = f.lookupGlobal(name)) {
3412     switch (global->which()) {
3413       case ModuleValidatorShared::Global::ConstantLiteral:
3414         *type = global->varOrConstType();
3415         return f.writeConstExpr(global->constLiteralValue());
3416       case ModuleValidatorShared::Global::ConstantImport:
3417       case ModuleValidatorShared::Global::Variable: {
3418         *type = global->varOrConstType();
3419         return f.encoder().writeOp(Op::GlobalGet) &&
3420                f.encoder().writeVarU32(global->varOrConstIndex());
3421       }
3422       case ModuleValidatorShared::Global::Function:
3423       case ModuleValidatorShared::Global::FFI:
3424       case ModuleValidatorShared::Global::MathBuiltinFunction:
3425       case ModuleValidatorShared::Global::Table:
3426       case ModuleValidatorShared::Global::ArrayView:
3427       case ModuleValidatorShared::Global::ArrayViewCtor:
3428         break;
3429     }
3430     return f.failName(varRef,
3431                       "'%s' may not be accessed by ordinary expressions", name);
3432   }
3433 
3434   return f.failName(varRef, "'%s' not found in local or asm.js module scope",
3435                     name);
3436 }
3437 
IsLiteralOrConstInt(FunctionValidatorShared & f,ParseNode * pn,uint32_t * u32)3438 static inline bool IsLiteralOrConstInt(FunctionValidatorShared& f,
3439                                        ParseNode* pn, uint32_t* u32) {
3440   NumLit lit;
3441   if (!IsLiteralOrConst(f, pn, &lit)) {
3442     return false;
3443   }
3444 
3445   return IsLiteralInt(lit, u32);
3446 }
3447 
3448 static const int32_t NoMask = -1;
3449 
3450 template <typename Unit>
CheckArrayAccess(FunctionValidator<Unit> & f,ParseNode * viewName,ParseNode * indexExpr,Scalar::Type * viewType)3451 static bool CheckArrayAccess(FunctionValidator<Unit>& f, ParseNode* viewName,
3452                              ParseNode* indexExpr, Scalar::Type* viewType) {
3453   if (!viewName->isKind(ParseNodeKind::Name)) {
3454     return f.fail(viewName,
3455                   "base of array access must be a typed array view name");
3456   }
3457 
3458   const ModuleValidatorShared::Global* global =
3459       f.lookupGlobal(viewName->as<NameNode>().name());
3460   if (!global || global->which() != ModuleValidatorShared::Global::ArrayView) {
3461     return f.fail(viewName,
3462                   "base of array access must be a typed array view name");
3463   }
3464 
3465   *viewType = global->viewType();
3466 
3467   uint32_t index;
3468   if (IsLiteralOrConstInt(f, indexExpr, &index)) {
3469     uint64_t byteOffset = uint64_t(index) << TypedArrayShift(*viewType);
3470     uint64_t width = TypedArrayElemSize(*viewType);
3471     if (!f.m().tryConstantAccess(byteOffset, width)) {
3472       return f.fail(indexExpr, "constant index out of range");
3473     }
3474 
3475     return f.writeInt32Lit(byteOffset);
3476   }
3477 
3478   // Mask off the low bits to account for the clearing effect of a right shift
3479   // followed by the left shift implicit in the array access. E.g., H32[i>>2]
3480   // loses the low two bits.
3481   int32_t mask = ~(TypedArrayElemSize(*viewType) - 1);
3482 
3483   if (indexExpr->isKind(ParseNodeKind::RshExpr)) {
3484     ParseNode* shiftAmountNode = BitwiseRight(indexExpr);
3485 
3486     uint32_t shift;
3487     if (!IsLiteralInt(f.m(), shiftAmountNode, &shift)) {
3488       return f.failf(shiftAmountNode, "shift amount must be constant");
3489     }
3490 
3491     unsigned requiredShift = TypedArrayShift(*viewType);
3492     if (shift != requiredShift) {
3493       return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift);
3494     }
3495 
3496     ParseNode* pointerNode = BitwiseLeft(indexExpr);
3497 
3498     Type pointerType;
3499     if (!CheckExpr(f, pointerNode, &pointerType)) {
3500       return false;
3501     }
3502 
3503     if (!pointerType.isIntish()) {
3504       return f.failf(pointerNode, "%s is not a subtype of int",
3505                      pointerType.toChars());
3506     }
3507   } else {
3508     // For legacy scalar access compatibility, accept Int8/Uint8 accesses
3509     // with no shift.
3510     if (TypedArrayShift(*viewType) != 0) {
3511       return f.fail(
3512           indexExpr,
3513           "index expression isn't shifted; must be an Int8/Uint8 access");
3514     }
3515 
3516     MOZ_ASSERT(mask == NoMask);
3517 
3518     ParseNode* pointerNode = indexExpr;
3519 
3520     Type pointerType;
3521     if (!CheckExpr(f, pointerNode, &pointerType)) {
3522       return false;
3523     }
3524     if (!pointerType.isInt()) {
3525       return f.failf(pointerNode, "%s is not a subtype of int",
3526                      pointerType.toChars());
3527     }
3528   }
3529 
3530   // Don't generate the mask op if there is no need for it which could happen
3531   // for a shift of zero.
3532   if (mask != NoMask) {
3533     return f.writeInt32Lit(mask) && f.encoder().writeOp(Op::I32And);
3534   }
3535 
3536   return true;
3537 }
3538 
WriteArrayAccessFlags(FunctionValidatorShared & f,Scalar::Type viewType)3539 static bool WriteArrayAccessFlags(FunctionValidatorShared& f,
3540                                   Scalar::Type viewType) {
3541   // asm.js only has naturally-aligned accesses.
3542   size_t align = TypedArrayElemSize(viewType);
3543   MOZ_ASSERT(IsPowerOfTwo(align));
3544   if (!f.encoder().writeFixedU8(CeilingLog2(align))) {
3545     return false;
3546   }
3547 
3548   // asm.js doesn't have constant offsets, so just encode a 0.
3549   if (!f.encoder().writeVarU32(0)) {
3550     return false;
3551   }
3552 
3553   return true;
3554 }
3555 
3556 template <typename Unit>
CheckLoadArray(FunctionValidator<Unit> & f,ParseNode * elem,Type * type)3557 static bool CheckLoadArray(FunctionValidator<Unit>& f, ParseNode* elem,
3558                            Type* type) {
3559   Scalar::Type viewType;
3560 
3561   if (!CheckArrayAccess(f, ElemBase(elem), ElemIndex(elem), &viewType)) {
3562     return false;
3563   }
3564 
3565   switch (viewType) {
3566     case Scalar::Int8:
3567       if (!f.encoder().writeOp(Op::I32Load8S)) return false;
3568       break;
3569     case Scalar::Uint8:
3570       if (!f.encoder().writeOp(Op::I32Load8U)) return false;
3571       break;
3572     case Scalar::Int16:
3573       if (!f.encoder().writeOp(Op::I32Load16S)) return false;
3574       break;
3575     case Scalar::Uint16:
3576       if (!f.encoder().writeOp(Op::I32Load16U)) return false;
3577       break;
3578     case Scalar::Uint32:
3579     case Scalar::Int32:
3580       if (!f.encoder().writeOp(Op::I32Load)) return false;
3581       break;
3582     case Scalar::Float32:
3583       if (!f.encoder().writeOp(Op::F32Load)) return false;
3584       break;
3585     case Scalar::Float64:
3586       if (!f.encoder().writeOp(Op::F64Load)) return false;
3587       break;
3588     default:
3589       MOZ_CRASH("unexpected scalar type");
3590   }
3591 
3592   switch (viewType) {
3593     case Scalar::Int8:
3594     case Scalar::Int16:
3595     case Scalar::Int32:
3596     case Scalar::Uint8:
3597     case Scalar::Uint16:
3598     case Scalar::Uint32:
3599       *type = Type::Intish;
3600       break;
3601     case Scalar::Float32:
3602       *type = Type::MaybeFloat;
3603       break;
3604     case Scalar::Float64:
3605       *type = Type::MaybeDouble;
3606       break;
3607     default:
3608       MOZ_CRASH("Unexpected array type");
3609   }
3610 
3611   return WriteArrayAccessFlags(f, viewType);
3612 }
3613 
3614 template <typename Unit>
CheckStoreArray(FunctionValidator<Unit> & f,ParseNode * lhs,ParseNode * rhs,Type * type)3615 static bool CheckStoreArray(FunctionValidator<Unit>& f, ParseNode* lhs,
3616                             ParseNode* rhs, Type* type) {
3617   Scalar::Type viewType;
3618   if (!CheckArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), &viewType)) {
3619     return false;
3620   }
3621 
3622   Type rhsType;
3623   if (!CheckExpr(f, rhs, &rhsType)) {
3624     return false;
3625   }
3626 
3627   switch (viewType) {
3628     case Scalar::Int8:
3629     case Scalar::Int16:
3630     case Scalar::Int32:
3631     case Scalar::Uint8:
3632     case Scalar::Uint16:
3633     case Scalar::Uint32:
3634       if (!rhsType.isIntish()) {
3635         return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
3636       }
3637       break;
3638     case Scalar::Float32:
3639       if (!rhsType.isMaybeDouble() && !rhsType.isFloatish()) {
3640         return f.failf(lhs, "%s is not a subtype of double? or floatish",
3641                        rhsType.toChars());
3642       }
3643       break;
3644     case Scalar::Float64:
3645       if (!rhsType.isMaybeFloat() && !rhsType.isMaybeDouble()) {
3646         return f.failf(lhs, "%s is not a subtype of float? or double?",
3647                        rhsType.toChars());
3648       }
3649       break;
3650     default:
3651       MOZ_CRASH("Unexpected view type");
3652   }
3653 
3654   switch (viewType) {
3655     case Scalar::Int8:
3656     case Scalar::Uint8:
3657       if (!f.encoder().writeOp(MozOp::I32TeeStore8)) {
3658         return false;
3659       }
3660       break;
3661     case Scalar::Int16:
3662     case Scalar::Uint16:
3663       if (!f.encoder().writeOp(MozOp::I32TeeStore16)) {
3664         return false;
3665       }
3666       break;
3667     case Scalar::Int32:
3668     case Scalar::Uint32:
3669       if (!f.encoder().writeOp(MozOp::I32TeeStore)) {
3670         return false;
3671       }
3672       break;
3673     case Scalar::Float32:
3674       if (rhsType.isFloatish()) {
3675         if (!f.encoder().writeOp(MozOp::F32TeeStore)) {
3676           return false;
3677         }
3678       } else {
3679         if (!f.encoder().writeOp(MozOp::F64TeeStoreF32)) {
3680           return false;
3681         }
3682       }
3683       break;
3684     case Scalar::Float64:
3685       if (rhsType.isFloatish()) {
3686         if (!f.encoder().writeOp(MozOp::F32TeeStoreF64)) {
3687           return false;
3688         }
3689       } else {
3690         if (!f.encoder().writeOp(MozOp::F64TeeStore)) {
3691           return false;
3692         }
3693       }
3694       break;
3695     default:
3696       MOZ_CRASH("unexpected scalar type");
3697   }
3698 
3699   if (!WriteArrayAccessFlags(f, viewType)) {
3700     return false;
3701   }
3702 
3703   *type = rhsType;
3704   return true;
3705 }
3706 
3707 template <typename Unit>
CheckAssignName(FunctionValidator<Unit> & f,ParseNode * lhs,ParseNode * rhs,Type * type)3708 static bool CheckAssignName(FunctionValidator<Unit>& f, ParseNode* lhs,
3709                             ParseNode* rhs, Type* type) {
3710   TaggedParserAtomIndex name = lhs->as<NameNode>().name();
3711 
3712   if (const FunctionValidatorShared::Local* lhsVar = f.lookupLocal(name)) {
3713     Type rhsType;
3714     if (!CheckExpr(f, rhs, &rhsType)) {
3715       return false;
3716     }
3717 
3718     if (!f.encoder().writeOp(Op::LocalTee)) {
3719       return false;
3720     }
3721     if (!f.encoder().writeVarU32(lhsVar->slot)) {
3722       return false;
3723     }
3724 
3725     if (!(rhsType <= lhsVar->type)) {
3726       return f.failf(lhs, "%s is not a subtype of %s", rhsType.toChars(),
3727                      lhsVar->type.toChars());
3728     }
3729     *type = rhsType;
3730     return true;
3731   }
3732 
3733   if (const ModuleValidatorShared::Global* global = f.lookupGlobal(name)) {
3734     if (global->which() != ModuleValidatorShared::Global::Variable) {
3735       return f.failName(lhs, "'%s' is not a mutable variable", name);
3736     }
3737 
3738     Type rhsType;
3739     if (!CheckExpr(f, rhs, &rhsType)) {
3740       return false;
3741     }
3742 
3743     Type globType = global->varOrConstType();
3744     if (!(rhsType <= globType)) {
3745       return f.failf(lhs, "%s is not a subtype of %s", rhsType.toChars(),
3746                      globType.toChars());
3747     }
3748     if (!f.encoder().writeOp(MozOp::TeeGlobal)) {
3749       return false;
3750     }
3751     if (!f.encoder().writeVarU32(global->varOrConstIndex())) {
3752       return false;
3753     }
3754 
3755     *type = rhsType;
3756     return true;
3757   }
3758 
3759   return f.failName(lhs, "'%s' not found in local or asm.js module scope",
3760                     name);
3761 }
3762 
3763 template <typename Unit>
CheckAssign(FunctionValidator<Unit> & f,ParseNode * assign,Type * type)3764 static bool CheckAssign(FunctionValidator<Unit>& f, ParseNode* assign,
3765                         Type* type) {
3766   MOZ_ASSERT(assign->isKind(ParseNodeKind::AssignExpr));
3767 
3768   ParseNode* lhs = BinaryLeft(assign);
3769   ParseNode* rhs = BinaryRight(assign);
3770 
3771   if (lhs->getKind() == ParseNodeKind::ElemExpr) {
3772     return CheckStoreArray(f, lhs, rhs, type);
3773   }
3774 
3775   if (lhs->getKind() == ParseNodeKind::Name) {
3776     return CheckAssignName(f, lhs, rhs, type);
3777   }
3778 
3779   return f.fail(
3780       assign,
3781       "left-hand side of assignment must be a variable or array access");
3782 }
3783 
3784 template <typename Unit>
CheckMathIMul(FunctionValidator<Unit> & f,ParseNode * call,Type * type)3785 static bool CheckMathIMul(FunctionValidator<Unit>& f, ParseNode* call,
3786                           Type* type) {
3787   if (CallArgListLength(call) != 2) {
3788     return f.fail(call, "Math.imul must be passed 2 arguments");
3789   }
3790 
3791   ParseNode* lhs = CallArgList(call);
3792   ParseNode* rhs = NextNode(lhs);
3793 
3794   Type lhsType;
3795   if (!CheckExpr(f, lhs, &lhsType)) {
3796     return false;
3797   }
3798 
3799   Type rhsType;
3800   if (!CheckExpr(f, rhs, &rhsType)) {
3801     return false;
3802   }
3803 
3804   if (!lhsType.isIntish()) {
3805     return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
3806   }
3807   if (!rhsType.isIntish()) {
3808     return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
3809   }
3810 
3811   *type = Type::Signed;
3812   return f.encoder().writeOp(Op::I32Mul);
3813 }
3814 
3815 template <typename Unit>
CheckMathClz32(FunctionValidator<Unit> & f,ParseNode * call,Type * type)3816 static bool CheckMathClz32(FunctionValidator<Unit>& f, ParseNode* call,
3817                            Type* type) {
3818   if (CallArgListLength(call) != 1) {
3819     return f.fail(call, "Math.clz32 must be passed 1 argument");
3820   }
3821 
3822   ParseNode* arg = CallArgList(call);
3823 
3824   Type argType;
3825   if (!CheckExpr(f, arg, &argType)) {
3826     return false;
3827   }
3828 
3829   if (!argType.isIntish()) {
3830     return f.failf(arg, "%s is not a subtype of intish", argType.toChars());
3831   }
3832 
3833   *type = Type::Fixnum;
3834   return f.encoder().writeOp(Op::I32Clz);
3835 }
3836 
3837 template <typename Unit>
CheckMathAbs(FunctionValidator<Unit> & f,ParseNode * call,Type * type)3838 static bool CheckMathAbs(FunctionValidator<Unit>& f, ParseNode* call,
3839                          Type* type) {
3840   if (CallArgListLength(call) != 1) {
3841     return f.fail(call, "Math.abs must be passed 1 argument");
3842   }
3843 
3844   ParseNode* arg = CallArgList(call);
3845 
3846   Type argType;
3847   if (!CheckExpr(f, arg, &argType)) {
3848     return false;
3849   }
3850 
3851   if (argType.isSigned()) {
3852     *type = Type::Unsigned;
3853     return f.encoder().writeOp(MozOp::I32Abs);
3854   }
3855 
3856   if (argType.isMaybeDouble()) {
3857     *type = Type::Double;
3858     return f.encoder().writeOp(Op::F64Abs);
3859   }
3860 
3861   if (argType.isMaybeFloat()) {
3862     *type = Type::Floatish;
3863     return f.encoder().writeOp(Op::F32Abs);
3864   }
3865 
3866   return f.failf(call, "%s is not a subtype of signed, float? or double?",
3867                  argType.toChars());
3868 }
3869 
3870 template <typename Unit>
CheckMathSqrt(FunctionValidator<Unit> & f,ParseNode * call,Type * type)3871 static bool CheckMathSqrt(FunctionValidator<Unit>& f, ParseNode* call,
3872                           Type* type) {
3873   if (CallArgListLength(call) != 1) {
3874     return f.fail(call, "Math.sqrt must be passed 1 argument");
3875   }
3876 
3877   ParseNode* arg = CallArgList(call);
3878 
3879   Type argType;
3880   if (!CheckExpr(f, arg, &argType)) {
3881     return false;
3882   }
3883 
3884   if (argType.isMaybeDouble()) {
3885     *type = Type::Double;
3886     return f.encoder().writeOp(Op::F64Sqrt);
3887   }
3888 
3889   if (argType.isMaybeFloat()) {
3890     *type = Type::Floatish;
3891     return f.encoder().writeOp(Op::F32Sqrt);
3892   }
3893 
3894   return f.failf(call, "%s is neither a subtype of double? nor float?",
3895                  argType.toChars());
3896 }
3897 
3898 template <typename Unit>
CheckMathMinMax(FunctionValidator<Unit> & f,ParseNode * callNode,bool isMax,Type * type)3899 static bool CheckMathMinMax(FunctionValidator<Unit>& f, ParseNode* callNode,
3900                             bool isMax, Type* type) {
3901   if (CallArgListLength(callNode) < 2) {
3902     return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
3903   }
3904 
3905   ParseNode* firstArg = CallArgList(callNode);
3906   Type firstType;
3907   if (!CheckExpr(f, firstArg, &firstType)) {
3908     return false;
3909   }
3910 
3911   Op op = Op::Limit;
3912   MozOp mozOp = MozOp::Limit;
3913   if (firstType.isMaybeDouble()) {
3914     *type = Type::Double;
3915     firstType = Type::MaybeDouble;
3916     op = isMax ? Op::F64Max : Op::F64Min;
3917   } else if (firstType.isMaybeFloat()) {
3918     *type = Type::Float;
3919     firstType = Type::MaybeFloat;
3920     op = isMax ? Op::F32Max : Op::F32Min;
3921   } else if (firstType.isSigned()) {
3922     *type = Type::Signed;
3923     firstType = Type::Signed;
3924     mozOp = isMax ? MozOp::I32Max : MozOp::I32Min;
3925   } else {
3926     return f.failf(firstArg, "%s is not a subtype of double?, float? or signed",
3927                    firstType.toChars());
3928   }
3929 
3930   unsigned numArgs = CallArgListLength(callNode);
3931   ParseNode* nextArg = NextNode(firstArg);
3932   for (unsigned i = 1; i < numArgs; i++, nextArg = NextNode(nextArg)) {
3933     Type nextType;
3934     if (!CheckExpr(f, nextArg, &nextType)) {
3935       return false;
3936     }
3937     if (!(nextType <= firstType)) {
3938       return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(),
3939                      firstType.toChars());
3940     }
3941 
3942     if (op != Op::Limit) {
3943       if (!f.encoder().writeOp(op)) {
3944         return false;
3945       }
3946     } else {
3947       if (!f.encoder().writeOp(mozOp)) {
3948         return false;
3949       }
3950     }
3951   }
3952 
3953   return true;
3954 }
3955 
3956 using CheckArgType = bool (*)(FunctionValidatorShared& f, ParseNode* argNode,
3957                               Type type);
3958 
3959 template <CheckArgType checkArg, typename Unit>
CheckCallArgs(FunctionValidator<Unit> & f,ParseNode * callNode,ValTypeVector * args)3960 static bool CheckCallArgs(FunctionValidator<Unit>& f, ParseNode* callNode,
3961                           ValTypeVector* args) {
3962   ParseNode* argNode = CallArgList(callNode);
3963   for (unsigned i = 0; i < CallArgListLength(callNode);
3964        i++, argNode = NextNode(argNode)) {
3965     Type type;
3966     if (!CheckExpr(f, argNode, &type)) {
3967       return false;
3968     }
3969 
3970     if (!checkArg(f, argNode, type)) {
3971       return false;
3972     }
3973 
3974     if (!args->append(Type::canonicalize(type).canonicalToValType())) {
3975       return false;
3976     }
3977   }
3978   return true;
3979 }
3980 
CheckSignatureAgainstExisting(ModuleValidatorShared & m,ParseNode * usepn,const FuncType & sig,const FuncType & existing)3981 static bool CheckSignatureAgainstExisting(ModuleValidatorShared& m,
3982                                           ParseNode* usepn, const FuncType& sig,
3983                                           const FuncType& existing) {
3984   if (sig != existing) {
3985     return m.failf(usepn, "incompatible argument types to function");
3986   }
3987   return true;
3988 }
3989 
3990 template <typename Unit>
CheckFunctionSignature(ModuleValidator<Unit> & m,ParseNode * usepn,FuncType && sig,TaggedParserAtomIndex name,ModuleValidatorShared::Func ** func)3991 static bool CheckFunctionSignature(ModuleValidator<Unit>& m, ParseNode* usepn,
3992                                    FuncType&& sig, TaggedParserAtomIndex name,
3993                                    ModuleValidatorShared::Func** func) {
3994   if (sig.args().length() > MaxParams) {
3995     return m.failf(usepn, "too many parameters");
3996   }
3997 
3998   ModuleValidatorShared::Func* existing = m.lookupFuncDef(name);
3999   if (!existing) {
4000     if (!CheckModuleLevelName(m, usepn, name)) {
4001       return false;
4002     }
4003     return m.addFuncDef(name, usepn->pn_pos.begin, std::move(sig), func);
4004   }
4005 
4006   const FuncType& existingSig = m.env().types->funcType(existing->sigIndex());
4007 
4008   if (!CheckSignatureAgainstExisting(m, usepn, sig, existingSig)) {
4009     return false;
4010   }
4011 
4012   *func = existing;
4013   return true;
4014 }
4015 
CheckIsArgType(FunctionValidatorShared & f,ParseNode * argNode,Type type)4016 static bool CheckIsArgType(FunctionValidatorShared& f, ParseNode* argNode,
4017                            Type type) {
4018   if (!type.isArgType()) {
4019     return f.failf(argNode, "%s is not a subtype of int, float, or double",
4020                    type.toChars());
4021   }
4022   return true;
4023 }
4024 
4025 template <typename Unit>
CheckInternalCall(FunctionValidator<Unit> & f,ParseNode * callNode,TaggedParserAtomIndex calleeName,Type ret,Type * type)4026 static bool CheckInternalCall(FunctionValidator<Unit>& f, ParseNode* callNode,
4027                               TaggedParserAtomIndex calleeName, Type ret,
4028                               Type* type) {
4029   MOZ_ASSERT(ret.isCanonical());
4030 
4031   ValTypeVector args;
4032   if (!CheckCallArgs<CheckIsArgType>(f, callNode, &args)) {
4033     return false;
4034   }
4035 
4036   ValTypeVector results;
4037   Maybe<ValType> retType = ret.canonicalToReturnType();
4038   if (retType && !results.append(retType.ref())) {
4039     return false;
4040   }
4041 
4042   FuncType sig(std::move(args), std::move(results));
4043 
4044   ModuleValidatorShared::Func* callee;
4045   if (!CheckFunctionSignature(f.m(), callNode, std::move(sig), calleeName,
4046                               &callee)) {
4047     return false;
4048   }
4049 
4050   if (!f.writeCall(callNode, MozOp::OldCallDirect)) {
4051     return false;
4052   }
4053 
4054   if (!f.encoder().writeVarU32(callee->funcDefIndex())) {
4055     return false;
4056   }
4057 
4058   *type = Type::ret(ret);
4059   return true;
4060 }
4061 
4062 template <typename Unit>
CheckFuncPtrTableAgainstExisting(ModuleValidator<Unit> & m,ParseNode * usepn,TaggedParserAtomIndex name,FuncType && sig,unsigned mask,uint32_t * tableIndex)4063 static bool CheckFuncPtrTableAgainstExisting(ModuleValidator<Unit>& m,
4064                                              ParseNode* usepn,
4065                                              TaggedParserAtomIndex name,
4066                                              FuncType&& sig, unsigned mask,
4067                                              uint32_t* tableIndex) {
4068   if (const ModuleValidatorShared::Global* existing = m.lookupGlobal(name)) {
4069     if (existing->which() != ModuleValidatorShared::Global::Table) {
4070       return m.failName(usepn, "'%s' is not a function-pointer table", name);
4071     }
4072 
4073     ModuleValidatorShared::Table& table = m.table(existing->tableIndex());
4074     if (mask != table.mask()) {
4075       return m.failf(usepn, "mask does not match previous value (%u)",
4076                      table.mask());
4077     }
4078 
4079     if (!CheckSignatureAgainstExisting(
4080             m, usepn, sig, m.env().types->funcType(table.sigIndex()))) {
4081       return false;
4082     }
4083 
4084     *tableIndex = existing->tableIndex();
4085     return true;
4086   }
4087 
4088   if (!CheckModuleLevelName(m, usepn, name)) {
4089     return false;
4090   }
4091 
4092   if (!m.declareFuncPtrTable(std::move(sig), name, usepn->pn_pos.begin, mask,
4093                              tableIndex)) {
4094     return false;
4095   }
4096 
4097   return true;
4098 }
4099 
4100 template <typename Unit>
CheckFuncPtrCall(FunctionValidator<Unit> & f,ParseNode * callNode,Type ret,Type * type)4101 static bool CheckFuncPtrCall(FunctionValidator<Unit>& f, ParseNode* callNode,
4102                              Type ret, Type* type) {
4103   MOZ_ASSERT(ret.isCanonical());
4104 
4105   ParseNode* callee = CallCallee(callNode);
4106   ParseNode* tableNode = ElemBase(callee);
4107   ParseNode* indexExpr = ElemIndex(callee);
4108 
4109   if (!tableNode->isKind(ParseNodeKind::Name)) {
4110     return f.fail(tableNode, "expecting name of function-pointer array");
4111   }
4112 
4113   TaggedParserAtomIndex name = tableNode->as<NameNode>().name();
4114   if (const ModuleValidatorShared::Global* existing = f.lookupGlobal(name)) {
4115     if (existing->which() != ModuleValidatorShared::Global::Table) {
4116       return f.failName(
4117           tableNode, "'%s' is not the name of a function-pointer array", name);
4118     }
4119   }
4120 
4121   if (!indexExpr->isKind(ParseNodeKind::BitAndExpr)) {
4122     return f.fail(indexExpr,
4123                   "function-pointer table index expression needs & mask");
4124   }
4125 
4126   ParseNode* indexNode = BitwiseLeft(indexExpr);
4127   ParseNode* maskNode = BitwiseRight(indexExpr);
4128 
4129   uint32_t mask;
4130   if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX ||
4131       !IsPowerOfTwo(mask + 1)) {
4132     return f.fail(maskNode,
4133                   "function-pointer table index mask value must be a power of "
4134                   "two minus 1");
4135   }
4136 
4137   Type indexType;
4138   if (!CheckExpr(f, indexNode, &indexType)) {
4139     return false;
4140   }
4141 
4142   if (!indexType.isIntish()) {
4143     return f.failf(indexNode, "%s is not a subtype of intish",
4144                    indexType.toChars());
4145   }
4146 
4147   ValTypeVector args;
4148   if (!CheckCallArgs<CheckIsArgType>(f, callNode, &args)) {
4149     return false;
4150   }
4151 
4152   ValTypeVector results;
4153   Maybe<ValType> retType = ret.canonicalToReturnType();
4154   if (retType && !results.append(retType.ref())) {
4155     return false;
4156   }
4157 
4158   FuncType sig(std::move(args), std::move(results));
4159 
4160   uint32_t tableIndex;
4161   if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, std::move(sig),
4162                                         mask, &tableIndex)) {
4163     return false;
4164   }
4165 
4166   if (!f.writeCall(callNode, MozOp::OldCallIndirect)) {
4167     return false;
4168   }
4169 
4170   // Call signature
4171   if (!f.encoder().writeVarU32(f.m().table(tableIndex).sigIndex())) {
4172     return false;
4173   }
4174 
4175   *type = Type::ret(ret);
4176   return true;
4177 }
4178 
CheckIsExternType(FunctionValidatorShared & f,ParseNode * argNode,Type type)4179 static bool CheckIsExternType(FunctionValidatorShared& f, ParseNode* argNode,
4180                               Type type) {
4181   if (!type.isExtern()) {
4182     return f.failf(argNode, "%s is not a subtype of extern", type.toChars());
4183   }
4184   return true;
4185 }
4186 
4187 template <typename Unit>
CheckFFICall(FunctionValidator<Unit> & f,ParseNode * callNode,unsigned ffiIndex,Type ret,Type * type)4188 static bool CheckFFICall(FunctionValidator<Unit>& f, ParseNode* callNode,
4189                          unsigned ffiIndex, Type ret, Type* type) {
4190   MOZ_ASSERT(ret.isCanonical());
4191 
4192   TaggedParserAtomIndex calleeName =
4193       CallCallee(callNode)->as<NameNode>().name();
4194 
4195   if (ret.isFloat()) {
4196     return f.fail(callNode, "FFI calls can't return float");
4197   }
4198 
4199   ValTypeVector args;
4200   if (!CheckCallArgs<CheckIsExternType>(f, callNode, &args)) {
4201     return false;
4202   }
4203 
4204   ValTypeVector results;
4205   Maybe<ValType> retType = ret.canonicalToReturnType();
4206   if (retType && !results.append(retType.ref())) {
4207     return false;
4208   }
4209 
4210   FuncType sig(std::move(args), std::move(results));
4211 
4212   uint32_t importIndex;
4213   if (!f.m().declareImport(calleeName, std::move(sig), ffiIndex,
4214                            &importIndex)) {
4215     return false;
4216   }
4217 
4218   if (!f.writeCall(callNode, Op::Call)) {
4219     return false;
4220   }
4221 
4222   if (!f.encoder().writeVarU32(importIndex)) {
4223     return false;
4224   }
4225 
4226   *type = Type::ret(ret);
4227   return true;
4228 }
4229 
CheckFloatCoercionArg(FunctionValidatorShared & f,ParseNode * inputNode,Type inputType)4230 static bool CheckFloatCoercionArg(FunctionValidatorShared& f,
4231                                   ParseNode* inputNode, Type inputType) {
4232   if (inputType.isMaybeDouble()) {
4233     return f.encoder().writeOp(Op::F32DemoteF64);
4234   }
4235   if (inputType.isSigned()) {
4236     return f.encoder().writeOp(Op::F32ConvertI32S);
4237   }
4238   if (inputType.isUnsigned()) {
4239     return f.encoder().writeOp(Op::F32ConvertI32U);
4240   }
4241   if (inputType.isFloatish()) {
4242     return true;
4243   }
4244 
4245   return f.failf(inputNode,
4246                  "%s is not a subtype of signed, unsigned, double? or floatish",
4247                  inputType.toChars());
4248 }
4249 
4250 template <typename Unit>
4251 static bool CheckCoercedCall(FunctionValidator<Unit>& f, ParseNode* call,
4252                              Type ret, Type* type);
4253 
4254 template <typename Unit>
CheckCoercionArg(FunctionValidator<Unit> & f,ParseNode * arg,Type expected,Type * type)4255 static bool CheckCoercionArg(FunctionValidator<Unit>& f, ParseNode* arg,
4256                              Type expected, Type* type) {
4257   MOZ_ASSERT(expected.isCanonicalValType());
4258 
4259   if (arg->isKind(ParseNodeKind::CallExpr)) {
4260     return CheckCoercedCall(f, arg, expected, type);
4261   }
4262 
4263   Type argType;
4264   if (!CheckExpr(f, arg, &argType)) {
4265     return false;
4266   }
4267 
4268   if (expected.isFloat()) {
4269     if (!CheckFloatCoercionArg(f, arg, argType)) {
4270       return false;
4271     }
4272   } else {
4273     MOZ_CRASH("not call coercions");
4274   }
4275 
4276   *type = Type::ret(expected);
4277   return true;
4278 }
4279 
4280 template <typename Unit>
CheckMathFRound(FunctionValidator<Unit> & f,ParseNode * callNode,Type * type)4281 static bool CheckMathFRound(FunctionValidator<Unit>& f, ParseNode* callNode,
4282                             Type* type) {
4283   if (CallArgListLength(callNode) != 1) {
4284     return f.fail(callNode, "Math.fround must be passed 1 argument");
4285   }
4286 
4287   ParseNode* argNode = CallArgList(callNode);
4288   Type argType;
4289   if (!CheckCoercionArg(f, argNode, Type::Float, &argType)) {
4290     return false;
4291   }
4292 
4293   MOZ_ASSERT(argType == Type::Float);
4294   *type = Type::Float;
4295   return true;
4296 }
4297 
4298 template <typename Unit>
CheckMathBuiltinCall(FunctionValidator<Unit> & f,ParseNode * callNode,AsmJSMathBuiltinFunction func,Type * type)4299 static bool CheckMathBuiltinCall(FunctionValidator<Unit>& f,
4300                                  ParseNode* callNode,
4301                                  AsmJSMathBuiltinFunction func, Type* type) {
4302   unsigned arity = 0;
4303   Op f32 = Op::Limit;
4304   Op f64 = Op::Limit;
4305   MozOp mozf64 = MozOp::Limit;
4306   switch (func) {
4307     case AsmJSMathBuiltin_imul:
4308       return CheckMathIMul(f, callNode, type);
4309     case AsmJSMathBuiltin_clz32:
4310       return CheckMathClz32(f, callNode, type);
4311     case AsmJSMathBuiltin_abs:
4312       return CheckMathAbs(f, callNode, type);
4313     case AsmJSMathBuiltin_sqrt:
4314       return CheckMathSqrt(f, callNode, type);
4315     case AsmJSMathBuiltin_fround:
4316       return CheckMathFRound(f, callNode, type);
4317     case AsmJSMathBuiltin_min:
4318       return CheckMathMinMax(f, callNode, /* isMax = */ false, type);
4319     case AsmJSMathBuiltin_max:
4320       return CheckMathMinMax(f, callNode, /* isMax = */ true, type);
4321     case AsmJSMathBuiltin_ceil:
4322       arity = 1;
4323       f64 = Op::F64Ceil;
4324       f32 = Op::F32Ceil;
4325       break;
4326     case AsmJSMathBuiltin_floor:
4327       arity = 1;
4328       f64 = Op::F64Floor;
4329       f32 = Op::F32Floor;
4330       break;
4331     case AsmJSMathBuiltin_sin:
4332       arity = 1;
4333       mozf64 = MozOp::F64Sin;
4334       f32 = Op::Unreachable;
4335       break;
4336     case AsmJSMathBuiltin_cos:
4337       arity = 1;
4338       mozf64 = MozOp::F64Cos;
4339       f32 = Op::Unreachable;
4340       break;
4341     case AsmJSMathBuiltin_tan:
4342       arity = 1;
4343       mozf64 = MozOp::F64Tan;
4344       f32 = Op::Unreachable;
4345       break;
4346     case AsmJSMathBuiltin_asin:
4347       arity = 1;
4348       mozf64 = MozOp::F64Asin;
4349       f32 = Op::Unreachable;
4350       break;
4351     case AsmJSMathBuiltin_acos:
4352       arity = 1;
4353       mozf64 = MozOp::F64Acos;
4354       f32 = Op::Unreachable;
4355       break;
4356     case AsmJSMathBuiltin_atan:
4357       arity = 1;
4358       mozf64 = MozOp::F64Atan;
4359       f32 = Op::Unreachable;
4360       break;
4361     case AsmJSMathBuiltin_exp:
4362       arity = 1;
4363       mozf64 = MozOp::F64Exp;
4364       f32 = Op::Unreachable;
4365       break;
4366     case AsmJSMathBuiltin_log:
4367       arity = 1;
4368       mozf64 = MozOp::F64Log;
4369       f32 = Op::Unreachable;
4370       break;
4371     case AsmJSMathBuiltin_pow:
4372       arity = 2;
4373       mozf64 = MozOp::F64Pow;
4374       f32 = Op::Unreachable;
4375       break;
4376     case AsmJSMathBuiltin_atan2:
4377       arity = 2;
4378       mozf64 = MozOp::F64Atan2;
4379       f32 = Op::Unreachable;
4380       break;
4381     default:
4382       MOZ_CRASH("unexpected mathBuiltin function");
4383   }
4384 
4385   unsigned actualArity = CallArgListLength(callNode);
4386   if (actualArity != arity) {
4387     return f.failf(callNode, "call passed %u arguments, expected %u",
4388                    actualArity, arity);
4389   }
4390 
4391   if (!f.prepareCall(callNode)) {
4392     return false;
4393   }
4394 
4395   Type firstType;
4396   ParseNode* argNode = CallArgList(callNode);
4397   if (!CheckExpr(f, argNode, &firstType)) {
4398     return false;
4399   }
4400 
4401   if (!firstType.isMaybeFloat() && !firstType.isMaybeDouble()) {
4402     return f.fail(
4403         argNode,
4404         "arguments to math call should be a subtype of double? or float?");
4405   }
4406 
4407   bool opIsDouble = firstType.isMaybeDouble();
4408   if (!opIsDouble && f32 == Op::Unreachable) {
4409     return f.fail(callNode, "math builtin cannot be used as float");
4410   }
4411 
4412   if (arity == 2) {
4413     Type secondType;
4414     argNode = NextNode(argNode);
4415     if (!CheckExpr(f, argNode, &secondType)) {
4416       return false;
4417     }
4418 
4419     if (firstType.isMaybeDouble() && !secondType.isMaybeDouble()) {
4420       return f.fail(
4421           argNode,
4422           "both arguments to math builtin call should be the same type");
4423     }
4424     if (firstType.isMaybeFloat() && !secondType.isMaybeFloat()) {
4425       return f.fail(
4426           argNode,
4427           "both arguments to math builtin call should be the same type");
4428     }
4429   }
4430 
4431   if (opIsDouble) {
4432     if (f64 != Op::Limit) {
4433       if (!f.encoder().writeOp(f64)) {
4434         return false;
4435       }
4436     } else {
4437       if (!f.encoder().writeOp(mozf64)) {
4438         return false;
4439       }
4440     }
4441   } else {
4442     if (!f.encoder().writeOp(f32)) {
4443       return false;
4444     }
4445   }
4446 
4447   *type = opIsDouble ? Type::Double : Type::Floatish;
4448   return true;
4449 }
4450 
4451 template <typename Unit>
CheckUncoercedCall(FunctionValidator<Unit> & f,ParseNode * expr,Type * type)4452 static bool CheckUncoercedCall(FunctionValidator<Unit>& f, ParseNode* expr,
4453                                Type* type) {
4454   MOZ_ASSERT(expr->isKind(ParseNodeKind::CallExpr));
4455 
4456   const ModuleValidatorShared::Global* global;
4457   if (IsCallToGlobal(f.m(), expr, &global) && global->isMathFunction()) {
4458     return CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), type);
4459   }
4460 
4461   return f.fail(
4462       expr,
4463       "all function calls must be calls to standard lib math functions,"
4464       " ignored (via f(); or comma-expression), coerced to signed (via f()|0),"
4465       " coerced to float (via fround(f())), or coerced to double (via +f())");
4466 }
4467 
CoerceResult(FunctionValidatorShared & f,ParseNode * expr,Type expected,Type actual,Type * type)4468 static bool CoerceResult(FunctionValidatorShared& f, ParseNode* expr,
4469                          Type expected, Type actual, Type* type) {
4470   MOZ_ASSERT(expected.isCanonical());
4471 
4472   // At this point, the bytecode resembles this:
4473   //      | the thing we wanted to coerce | current position |>
4474   switch (expected.which()) {
4475     case Type::Void:
4476       if (!actual.isVoid()) {
4477         if (!f.encoder().writeOp(Op::Drop)) {
4478           return false;
4479         }
4480       }
4481       break;
4482     case Type::Int:
4483       if (!actual.isIntish()) {
4484         return f.failf(expr, "%s is not a subtype of intish", actual.toChars());
4485       }
4486       break;
4487     case Type::Float:
4488       if (!CheckFloatCoercionArg(f, expr, actual)) {
4489         return false;
4490       }
4491       break;
4492     case Type::Double:
4493       if (actual.isMaybeDouble()) {
4494         // No conversion necessary.
4495       } else if (actual.isMaybeFloat()) {
4496         if (!f.encoder().writeOp(Op::F64PromoteF32)) {
4497           return false;
4498         }
4499       } else if (actual.isSigned()) {
4500         if (!f.encoder().writeOp(Op::F64ConvertI32S)) {
4501           return false;
4502         }
4503       } else if (actual.isUnsigned()) {
4504         if (!f.encoder().writeOp(Op::F64ConvertI32U)) {
4505           return false;
4506         }
4507       } else {
4508         return f.failf(
4509             expr, "%s is not a subtype of double?, float?, signed or unsigned",
4510             actual.toChars());
4511       }
4512       break;
4513     default:
4514       MOZ_CRASH("unexpected uncoerced result type");
4515   }
4516 
4517   *type = Type::ret(expected);
4518   return true;
4519 }
4520 
4521 template <typename Unit>
CheckCoercedMathBuiltinCall(FunctionValidator<Unit> & f,ParseNode * callNode,AsmJSMathBuiltinFunction func,Type ret,Type * type)4522 static bool CheckCoercedMathBuiltinCall(FunctionValidator<Unit>& f,
4523                                         ParseNode* callNode,
4524                                         AsmJSMathBuiltinFunction func, Type ret,
4525                                         Type* type) {
4526   Type actual;
4527   if (!CheckMathBuiltinCall(f, callNode, func, &actual)) {
4528     return false;
4529   }
4530   return CoerceResult(f, callNode, ret, actual, type);
4531 }
4532 
4533 template <typename Unit>
CheckCoercedCall(FunctionValidator<Unit> & f,ParseNode * call,Type ret,Type * type)4534 static bool CheckCoercedCall(FunctionValidator<Unit>& f, ParseNode* call,
4535                              Type ret, Type* type) {
4536   MOZ_ASSERT(ret.isCanonical());
4537 
4538   AutoCheckRecursionLimit recursion(f.cx());
4539   if (!recursion.checkDontReport(f.cx())) {
4540     return f.m().failOverRecursed();
4541   }
4542 
4543   if (IsNumericLiteral(f.m(), call)) {
4544     NumLit lit = ExtractNumericLiteral(f.m(), call);
4545     if (!f.writeConstExpr(lit)) {
4546       return false;
4547     }
4548     return CoerceResult(f, call, ret, Type::lit(lit), type);
4549   }
4550 
4551   ParseNode* callee = CallCallee(call);
4552 
4553   if (callee->isKind(ParseNodeKind::ElemExpr)) {
4554     return CheckFuncPtrCall(f, call, ret, type);
4555   }
4556 
4557   if (!callee->isKind(ParseNodeKind::Name)) {
4558     return f.fail(callee, "unexpected callee expression type");
4559   }
4560 
4561   TaggedParserAtomIndex calleeName = callee->as<NameNode>().name();
4562 
4563   if (const ModuleValidatorShared::Global* global =
4564           f.lookupGlobal(calleeName)) {
4565     switch (global->which()) {
4566       case ModuleValidatorShared::Global::FFI:
4567         return CheckFFICall(f, call, global->ffiIndex(), ret, type);
4568       case ModuleValidatorShared::Global::MathBuiltinFunction:
4569         return CheckCoercedMathBuiltinCall(
4570             f, call, global->mathBuiltinFunction(), ret, type);
4571       case ModuleValidatorShared::Global::ConstantLiteral:
4572       case ModuleValidatorShared::Global::ConstantImport:
4573       case ModuleValidatorShared::Global::Variable:
4574       case ModuleValidatorShared::Global::Table:
4575       case ModuleValidatorShared::Global::ArrayView:
4576       case ModuleValidatorShared::Global::ArrayViewCtor:
4577         return f.failName(callee, "'%s' is not callable function", calleeName);
4578       case ModuleValidatorShared::Global::Function:
4579         break;
4580     }
4581   }
4582 
4583   return CheckInternalCall(f, call, calleeName, ret, type);
4584 }
4585 
4586 template <typename Unit>
CheckPos(FunctionValidator<Unit> & f,ParseNode * pos,Type * type)4587 static bool CheckPos(FunctionValidator<Unit>& f, ParseNode* pos, Type* type) {
4588   MOZ_ASSERT(pos->isKind(ParseNodeKind::PosExpr));
4589   ParseNode* operand = UnaryKid(pos);
4590 
4591   if (operand->isKind(ParseNodeKind::CallExpr)) {
4592     return CheckCoercedCall(f, operand, Type::Double, type);
4593   }
4594 
4595   Type actual;
4596   if (!CheckExpr(f, operand, &actual)) {
4597     return false;
4598   }
4599 
4600   return CoerceResult(f, operand, Type::Double, actual, type);
4601 }
4602 
4603 template <typename Unit>
CheckNot(FunctionValidator<Unit> & f,ParseNode * expr,Type * type)4604 static bool CheckNot(FunctionValidator<Unit>& f, ParseNode* expr, Type* type) {
4605   MOZ_ASSERT(expr->isKind(ParseNodeKind::NotExpr));
4606   ParseNode* operand = UnaryKid(expr);
4607 
4608   Type operandType;
4609   if (!CheckExpr(f, operand, &operandType)) {
4610     return false;
4611   }
4612 
4613   if (!operandType.isInt()) {
4614     return f.failf(operand, "%s is not a subtype of int",
4615                    operandType.toChars());
4616   }
4617 
4618   *type = Type::Int;
4619   return f.encoder().writeOp(Op::I32Eqz);
4620 }
4621 
4622 template <typename Unit>
CheckNeg(FunctionValidator<Unit> & f,ParseNode * expr,Type * type)4623 static bool CheckNeg(FunctionValidator<Unit>& f, ParseNode* expr, Type* type) {
4624   MOZ_ASSERT(expr->isKind(ParseNodeKind::NegExpr));
4625   ParseNode* operand = UnaryKid(expr);
4626 
4627   Type operandType;
4628   if (!CheckExpr(f, operand, &operandType)) {
4629     return false;
4630   }
4631 
4632   if (operandType.isInt()) {
4633     *type = Type::Intish;
4634     return f.encoder().writeOp(MozOp::I32Neg);
4635   }
4636 
4637   if (operandType.isMaybeDouble()) {
4638     *type = Type::Double;
4639     return f.encoder().writeOp(Op::F64Neg);
4640   }
4641 
4642   if (operandType.isMaybeFloat()) {
4643     *type = Type::Floatish;
4644     return f.encoder().writeOp(Op::F32Neg);
4645   }
4646 
4647   return f.failf(operand, "%s is not a subtype of int, float? or double?",
4648                  operandType.toChars());
4649 }
4650 
4651 template <typename Unit>
CheckCoerceToInt(FunctionValidator<Unit> & f,ParseNode * expr,Type * type)4652 static bool CheckCoerceToInt(FunctionValidator<Unit>& f, ParseNode* expr,
4653                              Type* type) {
4654   MOZ_ASSERT(expr->isKind(ParseNodeKind::BitNotExpr));
4655   ParseNode* operand = UnaryKid(expr);
4656 
4657   Type operandType;
4658   if (!CheckExpr(f, operand, &operandType)) {
4659     return false;
4660   }
4661 
4662   if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) {
4663     *type = Type::Signed;
4664     Op opcode =
4665         operandType.isMaybeDouble() ? Op::I32TruncF64S : Op::I32TruncF32S;
4666     return f.encoder().writeOp(opcode);
4667   }
4668 
4669   if (!operandType.isIntish()) {
4670     return f.failf(operand, "%s is not a subtype of double?, float? or intish",
4671                    operandType.toChars());
4672   }
4673 
4674   *type = Type::Signed;
4675   return true;
4676 }
4677 
4678 template <typename Unit>
CheckBitNot(FunctionValidator<Unit> & f,ParseNode * neg,Type * type)4679 static bool CheckBitNot(FunctionValidator<Unit>& f, ParseNode* neg,
4680                         Type* type) {
4681   MOZ_ASSERT(neg->isKind(ParseNodeKind::BitNotExpr));
4682   ParseNode* operand = UnaryKid(neg);
4683 
4684   if (operand->isKind(ParseNodeKind::BitNotExpr)) {
4685     return CheckCoerceToInt(f, operand, type);
4686   }
4687 
4688   Type operandType;
4689   if (!CheckExpr(f, operand, &operandType)) {
4690     return false;
4691   }
4692 
4693   if (!operandType.isIntish()) {
4694     return f.failf(operand, "%s is not a subtype of intish",
4695                    operandType.toChars());
4696   }
4697 
4698   if (!f.encoder().writeOp(MozOp::I32BitNot)) {
4699     return false;
4700   }
4701 
4702   *type = Type::Signed;
4703   return true;
4704 }
4705 
4706 template <typename Unit>
4707 static bool CheckAsExprStatement(FunctionValidator<Unit>& f,
4708                                  ParseNode* exprStmt);
4709 
4710 template <typename Unit>
CheckComma(FunctionValidator<Unit> & f,ParseNode * comma,Type * type)4711 static bool CheckComma(FunctionValidator<Unit>& f, ParseNode* comma,
4712                        Type* type) {
4713   MOZ_ASSERT(comma->isKind(ParseNodeKind::CommaExpr));
4714   ParseNode* operands = ListHead(comma);
4715 
4716   // The block depth isn't taken into account here, because a comma list can't
4717   // contain breaks and continues and nested control flow structures.
4718   if (!f.encoder().writeOp(Op::Block)) {
4719     return false;
4720   }
4721 
4722   size_t typeAt;
4723   if (!f.encoder().writePatchableFixedU7(&typeAt)) {
4724     return false;
4725   }
4726 
4727   ParseNode* pn = operands;
4728   for (; NextNode(pn); pn = NextNode(pn)) {
4729     if (!CheckAsExprStatement(f, pn)) {
4730       return false;
4731     }
4732   }
4733 
4734   if (!CheckExpr(f, pn, type)) {
4735     return false;
4736   }
4737 
4738   f.encoder().patchFixedU7(typeAt, uint8_t(type->toWasmBlockSignatureType()));
4739 
4740   return f.encoder().writeOp(Op::End);
4741 }
4742 
4743 template <typename Unit>
CheckConditional(FunctionValidator<Unit> & f,ParseNode * ternary,Type * type)4744 static bool CheckConditional(FunctionValidator<Unit>& f, ParseNode* ternary,
4745                              Type* type) {
4746   MOZ_ASSERT(ternary->isKind(ParseNodeKind::ConditionalExpr));
4747 
4748   ParseNode* cond = TernaryKid1(ternary);
4749   ParseNode* thenExpr = TernaryKid2(ternary);
4750   ParseNode* elseExpr = TernaryKid3(ternary);
4751 
4752   Type condType;
4753   if (!CheckExpr(f, cond, &condType)) {
4754     return false;
4755   }
4756 
4757   if (!condType.isInt()) {
4758     return f.failf(cond, "%s is not a subtype of int", condType.toChars());
4759   }
4760 
4761   size_t typeAt;
4762   if (!f.pushIf(&typeAt)) {
4763     return false;
4764   }
4765 
4766   Type thenType;
4767   if (!CheckExpr(f, thenExpr, &thenType)) {
4768     return false;
4769   }
4770 
4771   if (!f.switchToElse()) {
4772     return false;
4773   }
4774 
4775   Type elseType;
4776   if (!CheckExpr(f, elseExpr, &elseType)) {
4777     return false;
4778   }
4779 
4780   if (thenType.isInt() && elseType.isInt()) {
4781     *type = Type::Int;
4782   } else if (thenType.isDouble() && elseType.isDouble()) {
4783     *type = Type::Double;
4784   } else if (thenType.isFloat() && elseType.isFloat()) {
4785     *type = Type::Float;
4786   } else {
4787     return f.failf(
4788         ternary,
4789         "then/else branches of conditional must both produce int, float, "
4790         "double, current types are %s and %s",
4791         thenType.toChars(), elseType.toChars());
4792   }
4793 
4794   if (!f.popIf(typeAt, type->toWasmBlockSignatureType())) {
4795     return false;
4796   }
4797 
4798   return true;
4799 }
4800 
4801 template <typename Unit>
IsValidIntMultiplyConstant(ModuleValidator<Unit> & m,ParseNode * expr)4802 static bool IsValidIntMultiplyConstant(ModuleValidator<Unit>& m,
4803                                        ParseNode* expr) {
4804   if (!IsNumericLiteral(m, expr)) {
4805     return false;
4806   }
4807 
4808   NumLit lit = ExtractNumericLiteral(m, expr);
4809   switch (lit.which()) {
4810     case NumLit::Fixnum:
4811     case NumLit::NegativeInt:
4812       if (Abs(lit.toInt32()) < (uint32_t(1) << 20)) {
4813         return true;
4814       }
4815       return false;
4816     case NumLit::BigUnsigned:
4817     case NumLit::Double:
4818     case NumLit::Float:
4819     case NumLit::OutOfRangeInt:
4820       return false;
4821   }
4822 
4823   MOZ_CRASH("Bad literal");
4824 }
4825 
4826 template <typename Unit>
CheckMultiply(FunctionValidator<Unit> & f,ParseNode * star,Type * type)4827 static bool CheckMultiply(FunctionValidator<Unit>& f, ParseNode* star,
4828                           Type* type) {
4829   MOZ_ASSERT(star->isKind(ParseNodeKind::MulExpr));
4830   ParseNode* lhs = MultiplyLeft(star);
4831   ParseNode* rhs = MultiplyRight(star);
4832 
4833   Type lhsType;
4834   if (!CheckExpr(f, lhs, &lhsType)) {
4835     return false;
4836   }
4837 
4838   Type rhsType;
4839   if (!CheckExpr(f, rhs, &rhsType)) {
4840     return false;
4841   }
4842 
4843   if (lhsType.isInt() && rhsType.isInt()) {
4844     if (!IsValidIntMultiplyConstant(f.m(), lhs) &&
4845         !IsValidIntMultiplyConstant(f.m(), rhs)) {
4846       return f.fail(
4847           star,
4848           "one arg to int multiply must be a small (-2^20, 2^20) int literal");
4849     }
4850     *type = Type::Intish;
4851     return f.encoder().writeOp(Op::I32Mul);
4852   }
4853 
4854   if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
4855     *type = Type::Double;
4856     return f.encoder().writeOp(Op::F64Mul);
4857   }
4858 
4859   if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
4860     *type = Type::Floatish;
4861     return f.encoder().writeOp(Op::F32Mul);
4862   }
4863 
4864   return f.fail(
4865       star, "multiply operands must be both int, both double? or both float?");
4866 }
4867 
4868 template <typename Unit>
CheckAddOrSub(FunctionValidator<Unit> & f,ParseNode * expr,Type * type,unsigned * numAddOrSubOut=nullptr)4869 static bool CheckAddOrSub(FunctionValidator<Unit>& f, ParseNode* expr,
4870                           Type* type, unsigned* numAddOrSubOut = nullptr) {
4871   AutoCheckRecursionLimit recursion(f.cx());
4872   if (!recursion.checkDontReport(f.cx())) {
4873     return f.m().failOverRecursed();
4874   }
4875 
4876   MOZ_ASSERT(expr->isKind(ParseNodeKind::AddExpr) ||
4877              expr->isKind(ParseNodeKind::SubExpr));
4878   ParseNode* lhs = AddSubLeft(expr);
4879   ParseNode* rhs = AddSubRight(expr);
4880 
4881   Type lhsType, rhsType;
4882   unsigned lhsNumAddOrSub, rhsNumAddOrSub;
4883 
4884   if (lhs->isKind(ParseNodeKind::AddExpr) ||
4885       lhs->isKind(ParseNodeKind::SubExpr)) {
4886     if (!CheckAddOrSub(f, lhs, &lhsType, &lhsNumAddOrSub)) {
4887       return false;
4888     }
4889     if (lhsType == Type::Intish) {
4890       lhsType = Type::Int;
4891     }
4892   } else {
4893     if (!CheckExpr(f, lhs, &lhsType)) {
4894       return false;
4895     }
4896     lhsNumAddOrSub = 0;
4897   }
4898 
4899   if (rhs->isKind(ParseNodeKind::AddExpr) ||
4900       rhs->isKind(ParseNodeKind::SubExpr)) {
4901     if (!CheckAddOrSub(f, rhs, &rhsType, &rhsNumAddOrSub)) {
4902       return false;
4903     }
4904     if (rhsType == Type::Intish) {
4905       rhsType = Type::Int;
4906     }
4907   } else {
4908     if (!CheckExpr(f, rhs, &rhsType)) {
4909       return false;
4910     }
4911     rhsNumAddOrSub = 0;
4912   }
4913 
4914   unsigned numAddOrSub = lhsNumAddOrSub + rhsNumAddOrSub + 1;
4915   if (numAddOrSub > (1 << 20)) {
4916     return f.fail(expr, "too many + or - without intervening coercion");
4917   }
4918 
4919   if (lhsType.isInt() && rhsType.isInt()) {
4920     if (!f.encoder().writeOp(
4921             expr->isKind(ParseNodeKind::AddExpr) ? Op::I32Add : Op::I32Sub)) {
4922       return false;
4923     }
4924     *type = Type::Intish;
4925   } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
4926     if (!f.encoder().writeOp(
4927             expr->isKind(ParseNodeKind::AddExpr) ? Op::F64Add : Op::F64Sub)) {
4928       return false;
4929     }
4930     *type = Type::Double;
4931   } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
4932     if (!f.encoder().writeOp(
4933             expr->isKind(ParseNodeKind::AddExpr) ? Op::F32Add : Op::F32Sub)) {
4934       return false;
4935     }
4936     *type = Type::Floatish;
4937   } else {
4938     return f.failf(
4939         expr,
4940         "operands to + or - must both be int, float? or double?, got %s and %s",
4941         lhsType.toChars(), rhsType.toChars());
4942   }
4943 
4944   if (numAddOrSubOut) {
4945     *numAddOrSubOut = numAddOrSub;
4946   }
4947   return true;
4948 }
4949 
4950 template <typename Unit>
CheckDivOrMod(FunctionValidator<Unit> & f,ParseNode * expr,Type * type)4951 static bool CheckDivOrMod(FunctionValidator<Unit>& f, ParseNode* expr,
4952                           Type* type) {
4953   MOZ_ASSERT(expr->isKind(ParseNodeKind::DivExpr) ||
4954              expr->isKind(ParseNodeKind::ModExpr));
4955 
4956   ParseNode* lhs = DivOrModLeft(expr);
4957   ParseNode* rhs = DivOrModRight(expr);
4958 
4959   Type lhsType, rhsType;
4960   if (!CheckExpr(f, lhs, &lhsType)) {
4961     return false;
4962   }
4963   if (!CheckExpr(f, rhs, &rhsType)) {
4964     return false;
4965   }
4966 
4967   if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
4968     *type = Type::Double;
4969     if (expr->isKind(ParseNodeKind::DivExpr)) {
4970       return f.encoder().writeOp(Op::F64Div);
4971     }
4972     return f.encoder().writeOp(MozOp::F64Mod);
4973   }
4974 
4975   if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
4976     *type = Type::Floatish;
4977     if (expr->isKind(ParseNodeKind::DivExpr)) {
4978       return f.encoder().writeOp(Op::F32Div);
4979     }
4980     return f.fail(expr, "modulo cannot receive float arguments");
4981   }
4982 
4983   if (lhsType.isSigned() && rhsType.isSigned()) {
4984     *type = Type::Intish;
4985     return f.encoder().writeOp(
4986         expr->isKind(ParseNodeKind::DivExpr) ? Op::I32DivS : Op::I32RemS);
4987   }
4988 
4989   if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
4990     *type = Type::Intish;
4991     return f.encoder().writeOp(
4992         expr->isKind(ParseNodeKind::DivExpr) ? Op::I32DivU : Op::I32RemU);
4993   }
4994 
4995   return f.failf(
4996       expr,
4997       "arguments to / or %% must both be double?, float?, signed, or unsigned; "
4998       "%s and %s are given",
4999       lhsType.toChars(), rhsType.toChars());
5000 }
5001 
5002 template <typename Unit>
CheckComparison(FunctionValidator<Unit> & f,ParseNode * comp,Type * type)5003 static bool CheckComparison(FunctionValidator<Unit>& f, ParseNode* comp,
5004                             Type* type) {
5005   MOZ_ASSERT(comp->isKind(ParseNodeKind::LtExpr) ||
5006              comp->isKind(ParseNodeKind::LeExpr) ||
5007              comp->isKind(ParseNodeKind::GtExpr) ||
5008              comp->isKind(ParseNodeKind::GeExpr) ||
5009              comp->isKind(ParseNodeKind::EqExpr) ||
5010              comp->isKind(ParseNodeKind::NeExpr));
5011 
5012   ParseNode* lhs = ComparisonLeft(comp);
5013   ParseNode* rhs = ComparisonRight(comp);
5014 
5015   Type lhsType, rhsType;
5016   if (!CheckExpr(f, lhs, &lhsType)) {
5017     return false;
5018   }
5019   if (!CheckExpr(f, rhs, &rhsType)) {
5020     return false;
5021   }
5022 
5023   if (!(lhsType.isSigned() && rhsType.isSigned()) &&
5024       !(lhsType.isUnsigned() && rhsType.isUnsigned()) &&
5025       !(lhsType.isDouble() && rhsType.isDouble()) &&
5026       !(lhsType.isFloat() && rhsType.isFloat())) {
5027     return f.failf(comp,
5028                    "arguments to a comparison must both be signed, unsigned, "
5029                    "floats or doubles; "
5030                    "%s and %s are given",
5031                    lhsType.toChars(), rhsType.toChars());
5032   }
5033 
5034   Op stmt;
5035   if (lhsType.isSigned() && rhsType.isSigned()) {
5036     switch (comp->getKind()) {
5037       case ParseNodeKind::EqExpr:
5038         stmt = Op::I32Eq;
5039         break;
5040       case ParseNodeKind::NeExpr:
5041         stmt = Op::I32Ne;
5042         break;
5043       case ParseNodeKind::LtExpr:
5044         stmt = Op::I32LtS;
5045         break;
5046       case ParseNodeKind::LeExpr:
5047         stmt = Op::I32LeS;
5048         break;
5049       case ParseNodeKind::GtExpr:
5050         stmt = Op::I32GtS;
5051         break;
5052       case ParseNodeKind::GeExpr:
5053         stmt = Op::I32GeS;
5054         break;
5055       default:
5056         MOZ_CRASH("unexpected comparison op");
5057     }
5058   } else if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
5059     switch (comp->getKind()) {
5060       case ParseNodeKind::EqExpr:
5061         stmt = Op::I32Eq;
5062         break;
5063       case ParseNodeKind::NeExpr:
5064         stmt = Op::I32Ne;
5065         break;
5066       case ParseNodeKind::LtExpr:
5067         stmt = Op::I32LtU;
5068         break;
5069       case ParseNodeKind::LeExpr:
5070         stmt = Op::I32LeU;
5071         break;
5072       case ParseNodeKind::GtExpr:
5073         stmt = Op::I32GtU;
5074         break;
5075       case ParseNodeKind::GeExpr:
5076         stmt = Op::I32GeU;
5077         break;
5078       default:
5079         MOZ_CRASH("unexpected comparison op");
5080     }
5081   } else if (lhsType.isDouble()) {
5082     switch (comp->getKind()) {
5083       case ParseNodeKind::EqExpr:
5084         stmt = Op::F64Eq;
5085         break;
5086       case ParseNodeKind::NeExpr:
5087         stmt = Op::F64Ne;
5088         break;
5089       case ParseNodeKind::LtExpr:
5090         stmt = Op::F64Lt;
5091         break;
5092       case ParseNodeKind::LeExpr:
5093         stmt = Op::F64Le;
5094         break;
5095       case ParseNodeKind::GtExpr:
5096         stmt = Op::F64Gt;
5097         break;
5098       case ParseNodeKind::GeExpr:
5099         stmt = Op::F64Ge;
5100         break;
5101       default:
5102         MOZ_CRASH("unexpected comparison op");
5103     }
5104   } else if (lhsType.isFloat()) {
5105     switch (comp->getKind()) {
5106       case ParseNodeKind::EqExpr:
5107         stmt = Op::F32Eq;
5108         break;
5109       case ParseNodeKind::NeExpr:
5110         stmt = Op::F32Ne;
5111         break;
5112       case ParseNodeKind::LtExpr:
5113         stmt = Op::F32Lt;
5114         break;
5115       case ParseNodeKind::LeExpr:
5116         stmt = Op::F32Le;
5117         break;
5118       case ParseNodeKind::GtExpr:
5119         stmt = Op::F32Gt;
5120         break;
5121       case ParseNodeKind::GeExpr:
5122         stmt = Op::F32Ge;
5123         break;
5124       default:
5125         MOZ_CRASH("unexpected comparison op");
5126     }
5127   } else {
5128     MOZ_CRASH("unexpected type");
5129   }
5130 
5131   *type = Type::Int;
5132   return f.encoder().writeOp(stmt);
5133 }
5134 
5135 template <typename Unit>
CheckBitwise(FunctionValidator<Unit> & f,ParseNode * bitwise,Type * type)5136 static bool CheckBitwise(FunctionValidator<Unit>& f, ParseNode* bitwise,
5137                          Type* type) {
5138   ParseNode* lhs = BitwiseLeft(bitwise);
5139   ParseNode* rhs = BitwiseRight(bitwise);
5140 
5141   int32_t identityElement;
5142   bool onlyOnRight;
5143   switch (bitwise->getKind()) {
5144     case ParseNodeKind::BitOrExpr:
5145       identityElement = 0;
5146       onlyOnRight = false;
5147       *type = Type::Signed;
5148       break;
5149     case ParseNodeKind::BitAndExpr:
5150       identityElement = -1;
5151       onlyOnRight = false;
5152       *type = Type::Signed;
5153       break;
5154     case ParseNodeKind::BitXorExpr:
5155       identityElement = 0;
5156       onlyOnRight = false;
5157       *type = Type::Signed;
5158       break;
5159     case ParseNodeKind::LshExpr:
5160       identityElement = 0;
5161       onlyOnRight = true;
5162       *type = Type::Signed;
5163       break;
5164     case ParseNodeKind::RshExpr:
5165       identityElement = 0;
5166       onlyOnRight = true;
5167       *type = Type::Signed;
5168       break;
5169     case ParseNodeKind::UrshExpr:
5170       identityElement = 0;
5171       onlyOnRight = true;
5172       *type = Type::Unsigned;
5173       break;
5174     default:
5175       MOZ_CRASH("not a bitwise op");
5176   }
5177 
5178   uint32_t i;
5179   if (!onlyOnRight && IsLiteralInt(f.m(), lhs, &i) &&
5180       i == uint32_t(identityElement)) {
5181     Type rhsType;
5182     if (!CheckExpr(f, rhs, &rhsType)) {
5183       return false;
5184     }
5185     if (!rhsType.isIntish()) {
5186       return f.failf(bitwise, "%s is not a subtype of intish",
5187                      rhsType.toChars());
5188     }
5189     return true;
5190   }
5191 
5192   if (IsLiteralInt(f.m(), rhs, &i) && i == uint32_t(identityElement)) {
5193     if (bitwise->isKind(ParseNodeKind::BitOrExpr) &&
5194         lhs->isKind(ParseNodeKind::CallExpr)) {
5195       return CheckCoercedCall(f, lhs, Type::Int, type);
5196     }
5197 
5198     Type lhsType;
5199     if (!CheckExpr(f, lhs, &lhsType)) {
5200       return false;
5201     }
5202     if (!lhsType.isIntish()) {
5203       return f.failf(bitwise, "%s is not a subtype of intish",
5204                      lhsType.toChars());
5205     }
5206     return true;
5207   }
5208 
5209   Type lhsType;
5210   if (!CheckExpr(f, lhs, &lhsType)) {
5211     return false;
5212   }
5213 
5214   Type rhsType;
5215   if (!CheckExpr(f, rhs, &rhsType)) {
5216     return false;
5217   }
5218 
5219   if (!lhsType.isIntish()) {
5220     return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
5221   }
5222   if (!rhsType.isIntish()) {
5223     return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
5224   }
5225 
5226   switch (bitwise->getKind()) {
5227     case ParseNodeKind::BitOrExpr:
5228       if (!f.encoder().writeOp(Op::I32Or)) return false;
5229       break;
5230     case ParseNodeKind::BitAndExpr:
5231       if (!f.encoder().writeOp(Op::I32And)) return false;
5232       break;
5233     case ParseNodeKind::BitXorExpr:
5234       if (!f.encoder().writeOp(Op::I32Xor)) return false;
5235       break;
5236     case ParseNodeKind::LshExpr:
5237       if (!f.encoder().writeOp(Op::I32Shl)) return false;
5238       break;
5239     case ParseNodeKind::RshExpr:
5240       if (!f.encoder().writeOp(Op::I32ShrS)) return false;
5241       break;
5242     case ParseNodeKind::UrshExpr:
5243       if (!f.encoder().writeOp(Op::I32ShrU)) return false;
5244       break;
5245     default:
5246       MOZ_CRASH("not a bitwise op");
5247   }
5248 
5249   return true;
5250 }
5251 
5252 template <typename Unit>
CheckExpr(FunctionValidator<Unit> & f,ParseNode * expr,Type * type)5253 static bool CheckExpr(FunctionValidator<Unit>& f, ParseNode* expr, Type* type) {
5254   AutoCheckRecursionLimit recursion(f.cx());
5255   if (!recursion.checkDontReport(f.cx())) {
5256     return f.m().failOverRecursed();
5257   }
5258 
5259   if (IsNumericLiteral(f.m(), expr)) {
5260     return CheckNumericLiteral(f, expr, type);
5261   }
5262 
5263   switch (expr->getKind()) {
5264     case ParseNodeKind::Name:
5265       return CheckVarRef(f, expr, type);
5266     case ParseNodeKind::ElemExpr:
5267       return CheckLoadArray(f, expr, type);
5268     case ParseNodeKind::AssignExpr:
5269       return CheckAssign(f, expr, type);
5270     case ParseNodeKind::PosExpr:
5271       return CheckPos(f, expr, type);
5272     case ParseNodeKind::NotExpr:
5273       return CheckNot(f, expr, type);
5274     case ParseNodeKind::NegExpr:
5275       return CheckNeg(f, expr, type);
5276     case ParseNodeKind::BitNotExpr:
5277       return CheckBitNot(f, expr, type);
5278     case ParseNodeKind::CommaExpr:
5279       return CheckComma(f, expr, type);
5280     case ParseNodeKind::ConditionalExpr:
5281       return CheckConditional(f, expr, type);
5282     case ParseNodeKind::MulExpr:
5283       return CheckMultiply(f, expr, type);
5284     case ParseNodeKind::CallExpr:
5285       return CheckUncoercedCall(f, expr, type);
5286 
5287     case ParseNodeKind::AddExpr:
5288     case ParseNodeKind::SubExpr:
5289       return CheckAddOrSub(f, expr, type);
5290 
5291     case ParseNodeKind::DivExpr:
5292     case ParseNodeKind::ModExpr:
5293       return CheckDivOrMod(f, expr, type);
5294 
5295     case ParseNodeKind::LtExpr:
5296     case ParseNodeKind::LeExpr:
5297     case ParseNodeKind::GtExpr:
5298     case ParseNodeKind::GeExpr:
5299     case ParseNodeKind::EqExpr:
5300     case ParseNodeKind::NeExpr:
5301       return CheckComparison(f, expr, type);
5302 
5303     case ParseNodeKind::BitOrExpr:
5304     case ParseNodeKind::BitAndExpr:
5305     case ParseNodeKind::BitXorExpr:
5306     case ParseNodeKind::LshExpr:
5307     case ParseNodeKind::RshExpr:
5308     case ParseNodeKind::UrshExpr:
5309       return CheckBitwise(f, expr, type);
5310 
5311     default:;
5312   }
5313 
5314   return f.fail(expr, "unsupported expression");
5315 }
5316 
5317 template <typename Unit>
5318 static bool CheckStatement(FunctionValidator<Unit>& f, ParseNode* stmt);
5319 
5320 template <typename Unit>
CheckAsExprStatement(FunctionValidator<Unit> & f,ParseNode * expr)5321 static bool CheckAsExprStatement(FunctionValidator<Unit>& f, ParseNode* expr) {
5322   if (expr->isKind(ParseNodeKind::CallExpr)) {
5323     Type ignored;
5324     return CheckCoercedCall(f, expr, Type::Void, &ignored);
5325   }
5326 
5327   Type resultType;
5328   if (!CheckExpr(f, expr, &resultType)) {
5329     return false;
5330   }
5331 
5332   if (!resultType.isVoid()) {
5333     if (!f.encoder().writeOp(Op::Drop)) {
5334       return false;
5335     }
5336   }
5337 
5338   return true;
5339 }
5340 
5341 template <typename Unit>
CheckExprStatement(FunctionValidator<Unit> & f,ParseNode * exprStmt)5342 static bool CheckExprStatement(FunctionValidator<Unit>& f,
5343                                ParseNode* exprStmt) {
5344   MOZ_ASSERT(exprStmt->isKind(ParseNodeKind::ExpressionStmt));
5345   return CheckAsExprStatement(f, UnaryKid(exprStmt));
5346 }
5347 
5348 template <typename Unit>
CheckLoopConditionOnEntry(FunctionValidator<Unit> & f,ParseNode * cond)5349 static bool CheckLoopConditionOnEntry(FunctionValidator<Unit>& f,
5350                                       ParseNode* cond) {
5351   uint32_t maybeLit;
5352   if (IsLiteralInt(f.m(), cond, &maybeLit) && maybeLit) {
5353     return true;
5354   }
5355 
5356   Type condType;
5357   if (!CheckExpr(f, cond, &condType)) {
5358     return false;
5359   }
5360   if (!condType.isInt()) {
5361     return f.failf(cond, "%s is not a subtype of int", condType.toChars());
5362   }
5363 
5364   if (!f.encoder().writeOp(Op::I32Eqz)) {
5365     return false;
5366   }
5367 
5368   // brIf (i32.eqz $f) $out
5369   if (!f.writeBreakIf()) {
5370     return false;
5371   }
5372 
5373   return true;
5374 }
5375 
5376 template <typename Unit>
CheckWhile(FunctionValidator<Unit> & f,ParseNode * whileStmt,const LabelVector * labels=nullptr)5377 static bool CheckWhile(FunctionValidator<Unit>& f, ParseNode* whileStmt,
5378                        const LabelVector* labels = nullptr) {
5379   MOZ_ASSERT(whileStmt->isKind(ParseNodeKind::WhileStmt));
5380   ParseNode* cond = BinaryLeft(whileStmt);
5381   ParseNode* body = BinaryRight(whileStmt);
5382 
5383   // A while loop `while(#cond) #body` is equivalent to:
5384   // (block $after_loop
5385   //    (loop $top
5386   //       (brIf $after_loop (i32.eq 0 #cond))
5387   //       #body
5388   //       (br $top)
5389   //    )
5390   // )
5391   if (labels && !f.addLabels(*labels, 0, 1)) {
5392     return false;
5393   }
5394 
5395   if (!f.pushLoop()) {
5396     return false;
5397   }
5398 
5399   if (!CheckLoopConditionOnEntry(f, cond)) {
5400     return false;
5401   }
5402   if (!CheckStatement(f, body)) {
5403     return false;
5404   }
5405   if (!f.writeContinue()) {
5406     return false;
5407   }
5408 
5409   if (!f.popLoop()) {
5410     return false;
5411   }
5412   if (labels) {
5413     f.removeLabels(*labels);
5414   }
5415   return true;
5416 }
5417 
5418 template <typename Unit>
CheckFor(FunctionValidator<Unit> & f,ParseNode * forStmt,const LabelVector * labels=nullptr)5419 static bool CheckFor(FunctionValidator<Unit>& f, ParseNode* forStmt,
5420                      const LabelVector* labels = nullptr) {
5421   MOZ_ASSERT(forStmt->isKind(ParseNodeKind::ForStmt));
5422   ParseNode* forHead = BinaryLeft(forStmt);
5423   ParseNode* body = BinaryRight(forStmt);
5424 
5425   if (!forHead->isKind(ParseNodeKind::ForHead)) {
5426     return f.fail(forHead, "unsupported for-loop statement");
5427   }
5428 
5429   ParseNode* maybeInit = TernaryKid1(forHead);
5430   ParseNode* maybeCond = TernaryKid2(forHead);
5431   ParseNode* maybeInc = TernaryKid3(forHead);
5432 
5433   // A for-loop `for (#init; #cond; #inc) #body` is equivalent to:
5434   // (block                                               // depth X
5435   //   (#init)
5436   //   (block $after_loop                                 // depth X+1 (block)
5437   //     (loop $loop_top                                  // depth X+2 (loop)
5438   //       (brIf $after (eq 0 #cond))
5439   //       (block $after_body #body)                      // depth X+3
5440   //       #inc
5441   //       (br $loop_top)
5442   //     )
5443   //   )
5444   // )
5445   // A break in the body should break out to $after_loop, i.e. depth + 1.
5446   // A continue in the body should break out to $after_body, i.e. depth + 3.
5447   if (labels && !f.addLabels(*labels, 1, 3)) {
5448     return false;
5449   }
5450 
5451   if (!f.pushUnbreakableBlock()) {
5452     return false;
5453   }
5454 
5455   if (maybeInit && !CheckAsExprStatement(f, maybeInit)) {
5456     return false;
5457   }
5458 
5459   {
5460     if (!f.pushLoop()) {
5461       return false;
5462     }
5463 
5464     if (maybeCond && !CheckLoopConditionOnEntry(f, maybeCond)) {
5465       return false;
5466     }
5467 
5468     {
5469       // Continuing in the body should just break out to the increment.
5470       if (!f.pushContinuableBlock()) {
5471         return false;
5472       }
5473       if (!CheckStatement(f, body)) {
5474         return false;
5475       }
5476       if (!f.popContinuableBlock()) {
5477         return false;
5478       }
5479     }
5480 
5481     if (maybeInc && !CheckAsExprStatement(f, maybeInc)) {
5482       return false;
5483     }
5484 
5485     if (!f.writeContinue()) {
5486       return false;
5487     }
5488     if (!f.popLoop()) {
5489       return false;
5490     }
5491   }
5492 
5493   if (!f.popUnbreakableBlock()) {
5494     return false;
5495   }
5496 
5497   if (labels) {
5498     f.removeLabels(*labels);
5499   }
5500 
5501   return true;
5502 }
5503 
5504 template <typename Unit>
CheckDoWhile(FunctionValidator<Unit> & f,ParseNode * whileStmt,const LabelVector * labels=nullptr)5505 static bool CheckDoWhile(FunctionValidator<Unit>& f, ParseNode* whileStmt,
5506                          const LabelVector* labels = nullptr) {
5507   MOZ_ASSERT(whileStmt->isKind(ParseNodeKind::DoWhileStmt));
5508   ParseNode* body = BinaryLeft(whileStmt);
5509   ParseNode* cond = BinaryRight(whileStmt);
5510 
5511   // A do-while loop `do { #body } while (#cond)` is equivalent to:
5512   // (block $after_loop           // depth X
5513   //   (loop $top                 // depth X+1
5514   //     (block #body)            // depth X+2
5515   //     (brIf #cond $top)
5516   //   )
5517   // )
5518   // A break should break out of the entire loop, i.e. at depth 0.
5519   // A continue should break out to the condition, i.e. at depth 2.
5520   if (labels && !f.addLabels(*labels, 0, 2)) {
5521     return false;
5522   }
5523 
5524   if (!f.pushLoop()) {
5525     return false;
5526   }
5527 
5528   {
5529     // An unlabeled continue in the body should break out to the condition.
5530     if (!f.pushContinuableBlock()) {
5531       return false;
5532     }
5533     if (!CheckStatement(f, body)) {
5534       return false;
5535     }
5536     if (!f.popContinuableBlock()) {
5537       return false;
5538     }
5539   }
5540 
5541   Type condType;
5542   if (!CheckExpr(f, cond, &condType)) {
5543     return false;
5544   }
5545   if (!condType.isInt()) {
5546     return f.failf(cond, "%s is not a subtype of int", condType.toChars());
5547   }
5548 
5549   if (!f.writeContinueIf()) {
5550     return false;
5551   }
5552 
5553   if (!f.popLoop()) {
5554     return false;
5555   }
5556   if (labels) {
5557     f.removeLabels(*labels);
5558   }
5559   return true;
5560 }
5561 
5562 template <typename Unit>
5563 static bool CheckStatementList(FunctionValidator<Unit>& f, ParseNode*,
5564                                const LabelVector* = nullptr);
5565 
5566 template <typename Unit>
CheckLabel(FunctionValidator<Unit> & f,ParseNode * labeledStmt)5567 static bool CheckLabel(FunctionValidator<Unit>& f, ParseNode* labeledStmt) {
5568   MOZ_ASSERT(labeledStmt->isKind(ParseNodeKind::LabelStmt));
5569 
5570   LabelVector labels;
5571   ParseNode* innermost = labeledStmt;
5572   do {
5573     if (!labels.append(LabeledStatementLabel(innermost))) {
5574       return false;
5575     }
5576     innermost = LabeledStatementStatement(innermost);
5577   } while (innermost->getKind() == ParseNodeKind::LabelStmt);
5578 
5579   switch (innermost->getKind()) {
5580     case ParseNodeKind::ForStmt:
5581       return CheckFor(f, innermost, &labels);
5582     case ParseNodeKind::DoWhileStmt:
5583       return CheckDoWhile(f, innermost, &labels);
5584     case ParseNodeKind::WhileStmt:
5585       return CheckWhile(f, innermost, &labels);
5586     case ParseNodeKind::StatementList:
5587       return CheckStatementList(f, innermost, &labels);
5588     default:
5589       break;
5590   }
5591 
5592   if (!f.pushUnbreakableBlock(&labels)) {
5593     return false;
5594   }
5595 
5596   if (!CheckStatement(f, innermost)) {
5597     return false;
5598   }
5599 
5600   if (!f.popUnbreakableBlock(&labels)) {
5601     return false;
5602   }
5603   return true;
5604 }
5605 
5606 template <typename Unit>
CheckIf(FunctionValidator<Unit> & f,ParseNode * ifStmt)5607 static bool CheckIf(FunctionValidator<Unit>& f, ParseNode* ifStmt) {
5608   uint32_t numIfEnd = 1;
5609 
5610 recurse:
5611   MOZ_ASSERT(ifStmt->isKind(ParseNodeKind::IfStmt));
5612   ParseNode* cond = TernaryKid1(ifStmt);
5613   ParseNode* thenStmt = TernaryKid2(ifStmt);
5614   ParseNode* elseStmt = TernaryKid3(ifStmt);
5615 
5616   Type condType;
5617   if (!CheckExpr(f, cond, &condType)) {
5618     return false;
5619   }
5620   if (!condType.isInt()) {
5621     return f.failf(cond, "%s is not a subtype of int", condType.toChars());
5622   }
5623 
5624   size_t typeAt;
5625   if (!f.pushIf(&typeAt)) {
5626     return false;
5627   }
5628 
5629   f.setIfType(typeAt, TypeCode::BlockVoid);
5630 
5631   if (!CheckStatement(f, thenStmt)) {
5632     return false;
5633   }
5634 
5635   if (elseStmt) {
5636     if (!f.switchToElse()) {
5637       return false;
5638     }
5639 
5640     if (elseStmt->isKind(ParseNodeKind::IfStmt)) {
5641       ifStmt = elseStmt;
5642       if (numIfEnd++ == UINT32_MAX) {
5643         return false;
5644       }
5645       goto recurse;
5646     }
5647 
5648     if (!CheckStatement(f, elseStmt)) {
5649       return false;
5650     }
5651   }
5652 
5653   for (uint32_t i = 0; i != numIfEnd; ++i) {
5654     if (!f.popIf()) {
5655       return false;
5656     }
5657   }
5658 
5659   return true;
5660 }
5661 
CheckCaseExpr(FunctionValidatorShared & f,ParseNode * caseExpr,int32_t * value)5662 static bool CheckCaseExpr(FunctionValidatorShared& f, ParseNode* caseExpr,
5663                           int32_t* value) {
5664   if (!IsNumericLiteral(f.m(), caseExpr)) {
5665     return f.fail(caseExpr,
5666                   "switch case expression must be an integer literal");
5667   }
5668 
5669   NumLit lit = ExtractNumericLiteral(f.m(), caseExpr);
5670   switch (lit.which()) {
5671     case NumLit::Fixnum:
5672     case NumLit::NegativeInt:
5673       *value = lit.toInt32();
5674       break;
5675     case NumLit::OutOfRangeInt:
5676     case NumLit::BigUnsigned:
5677       return f.fail(caseExpr, "switch case expression out of integer range");
5678     case NumLit::Double:
5679     case NumLit::Float:
5680       return f.fail(caseExpr,
5681                     "switch case expression must be an integer literal");
5682   }
5683 
5684   return true;
5685 }
5686 
CheckDefaultAtEnd(FunctionValidatorShared & f,ParseNode * stmt)5687 static bool CheckDefaultAtEnd(FunctionValidatorShared& f, ParseNode* stmt) {
5688   for (; stmt; stmt = NextNode(stmt)) {
5689     if (IsDefaultCase(stmt) && NextNode(stmt) != nullptr) {
5690       return f.fail(stmt, "default label must be at the end");
5691     }
5692   }
5693 
5694   return true;
5695 }
5696 
CheckSwitchRange(FunctionValidatorShared & f,ParseNode * stmt,int32_t * low,int32_t * high,uint32_t * tableLength)5697 static bool CheckSwitchRange(FunctionValidatorShared& f, ParseNode* stmt,
5698                              int32_t* low, int32_t* high,
5699                              uint32_t* tableLength) {
5700   if (IsDefaultCase(stmt)) {
5701     *low = 0;
5702     *high = -1;
5703     *tableLength = 0;
5704     return true;
5705   }
5706 
5707   int32_t i = 0;
5708   if (!CheckCaseExpr(f, CaseExpr(stmt), &i)) {
5709     return false;
5710   }
5711 
5712   *low = *high = i;
5713 
5714   ParseNode* initialStmt = stmt;
5715   for (stmt = NextNode(stmt); stmt && !IsDefaultCase(stmt);
5716        stmt = NextNode(stmt)) {
5717     int32_t i = 0;
5718     if (!CheckCaseExpr(f, CaseExpr(stmt), &i)) {
5719       return false;
5720     }
5721 
5722     *low = std::min(*low, i);
5723     *high = std::max(*high, i);
5724   }
5725 
5726   int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1;
5727   if (i64 > MaxBrTableElems) {
5728     return f.fail(
5729         initialStmt,
5730         "all switch statements generate tables; this table would be too big");
5731   }
5732 
5733   *tableLength = uint32_t(i64);
5734   return true;
5735 }
5736 
5737 template <typename Unit>
CheckSwitchExpr(FunctionValidator<Unit> & f,ParseNode * switchExpr)5738 static bool CheckSwitchExpr(FunctionValidator<Unit>& f, ParseNode* switchExpr) {
5739   Type exprType;
5740   if (!CheckExpr(f, switchExpr, &exprType)) {
5741     return false;
5742   }
5743   if (!exprType.isSigned()) {
5744     return f.failf(switchExpr, "%s is not a subtype of signed",
5745                    exprType.toChars());
5746   }
5747   return true;
5748 }
5749 
5750 // A switch will be constructed as:
5751 // - the default block wrapping all the other blocks, to be able to break
5752 // out of the switch with an unlabeled break statement. It has two statements
5753 // (an inner block and the default expr). asm.js rules require default to be at
5754 // the end, so the default block always encloses all the cases blocks.
5755 // - one block per case between low and high; undefined cases just jump to the
5756 // default case. Each of these blocks contain two statements: the next case's
5757 // block and the possibly empty statement list comprising the case body. The
5758 // last block pushed is the first case so the (relative) branch target therefore
5759 // matches the sequential order of cases.
5760 // - one block for the br_table, so that the first break goes to the first
5761 // case's block.
5762 template <typename Unit>
CheckSwitch(FunctionValidator<Unit> & f,ParseNode * switchStmt)5763 static bool CheckSwitch(FunctionValidator<Unit>& f, ParseNode* switchStmt) {
5764   MOZ_ASSERT(switchStmt->isKind(ParseNodeKind::SwitchStmt));
5765 
5766   ParseNode* switchExpr = BinaryLeft(switchStmt);
5767   ParseNode* switchBody = BinaryRight(switchStmt);
5768 
5769   if (switchBody->is<LexicalScopeNode>()) {
5770     LexicalScopeNode* scope = &switchBody->as<LexicalScopeNode>();
5771     if (!scope->isEmptyScope()) {
5772       return f.fail(scope, "switch body may not contain lexical declarations");
5773     }
5774     switchBody = scope->scopeBody();
5775   }
5776 
5777   ParseNode* stmt = ListHead(switchBody);
5778   if (!stmt) {
5779     if (!CheckSwitchExpr(f, switchExpr)) {
5780       return false;
5781     }
5782     if (!f.encoder().writeOp(Op::Drop)) {
5783       return false;
5784     }
5785     return true;
5786   }
5787 
5788   if (!CheckDefaultAtEnd(f, stmt)) {
5789     return false;
5790   }
5791 
5792   int32_t low = 0, high = 0;
5793   uint32_t tableLength = 0;
5794   if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength)) {
5795     return false;
5796   }
5797 
5798   static const uint32_t CASE_NOT_DEFINED = UINT32_MAX;
5799 
5800   Uint32Vector caseDepths;
5801   if (!caseDepths.appendN(CASE_NOT_DEFINED, tableLength)) {
5802     return false;
5803   }
5804 
5805   uint32_t numCases = 0;
5806   for (ParseNode* s = stmt; s && !IsDefaultCase(s); s = NextNode(s)) {
5807     int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(s)).toInt32();
5808 
5809     MOZ_ASSERT(caseValue >= low);
5810     unsigned i = caseValue - low;
5811     if (caseDepths[i] != CASE_NOT_DEFINED) {
5812       return f.fail(s, "no duplicate case labels");
5813     }
5814 
5815     MOZ_ASSERT(numCases != CASE_NOT_DEFINED);
5816     caseDepths[i] = numCases++;
5817   }
5818 
5819   // Open the wrapping breakable default block.
5820   if (!f.pushBreakableBlock()) {
5821     return false;
5822   }
5823 
5824   // Open all the case blocks.
5825   for (uint32_t i = 0; i < numCases; i++) {
5826     if (!f.pushUnbreakableBlock()) {
5827       return false;
5828     }
5829   }
5830 
5831   // Open the br_table block.
5832   if (!f.pushUnbreakableBlock()) {
5833     return false;
5834   }
5835 
5836   // The default block is the last one.
5837   uint32_t defaultDepth = numCases;
5838 
5839   // Subtract lowest case value, so that all the cases start from 0.
5840   if (low) {
5841     if (!CheckSwitchExpr(f, switchExpr)) {
5842       return false;
5843     }
5844     if (!f.writeInt32Lit(low)) {
5845       return false;
5846     }
5847     if (!f.encoder().writeOp(Op::I32Sub)) {
5848       return false;
5849     }
5850   } else {
5851     if (!CheckSwitchExpr(f, switchExpr)) {
5852       return false;
5853     }
5854   }
5855 
5856   // Start the br_table block.
5857   if (!f.encoder().writeOp(Op::BrTable)) {
5858     return false;
5859   }
5860 
5861   // Write the number of cases (tableLength - 1 + 1 (default)).
5862   // Write the number of cases (tableLength - 1 + 1 (default)).
5863   if (!f.encoder().writeVarU32(tableLength)) {
5864     return false;
5865   }
5866 
5867   // Each case value describes the relative depth to the actual block. When
5868   // a case is not explicitly defined, it goes to the default.
5869   for (size_t i = 0; i < tableLength; i++) {
5870     uint32_t target =
5871         caseDepths[i] == CASE_NOT_DEFINED ? defaultDepth : caseDepths[i];
5872     if (!f.encoder().writeVarU32(target)) {
5873       return false;
5874     }
5875   }
5876 
5877   // Write the default depth.
5878   if (!f.encoder().writeVarU32(defaultDepth)) {
5879     return false;
5880   }
5881 
5882   // Our br_table is done. Close its block, write the cases down in order.
5883   if (!f.popUnbreakableBlock()) {
5884     return false;
5885   }
5886 
5887   for (; stmt && !IsDefaultCase(stmt); stmt = NextNode(stmt)) {
5888     if (!CheckStatement(f, CaseBody(stmt))) {
5889       return false;
5890     }
5891     if (!f.popUnbreakableBlock()) {
5892       return false;
5893     }
5894   }
5895 
5896   // Write the default block.
5897   if (stmt && IsDefaultCase(stmt)) {
5898     if (!CheckStatement(f, CaseBody(stmt))) {
5899       return false;
5900     }
5901   }
5902 
5903   // Close the wrapping block.
5904   return f.popBreakableBlock();
5905 }
5906 
CheckReturnType(FunctionValidatorShared & f,ParseNode * usepn,Type ret)5907 static bool CheckReturnType(FunctionValidatorShared& f, ParseNode* usepn,
5908                             Type ret) {
5909   Maybe<ValType> type = ret.canonicalToReturnType();
5910 
5911   if (!f.hasAlreadyReturned()) {
5912     f.setReturnedType(type);
5913     return true;
5914   }
5915 
5916   if (f.returnedType() != type) {
5917     return f.failf(usepn, "%s incompatible with previous return of type %s",
5918                    ToString(type).get(), ToString(f.returnedType()).get());
5919   }
5920 
5921   return true;
5922 }
5923 
5924 template <typename Unit>
CheckReturn(FunctionValidator<Unit> & f,ParseNode * returnStmt)5925 static bool CheckReturn(FunctionValidator<Unit>& f, ParseNode* returnStmt) {
5926   ParseNode* expr = ReturnExpr(returnStmt);
5927 
5928   if (!expr) {
5929     if (!CheckReturnType(f, returnStmt, Type::Void)) {
5930       return false;
5931     }
5932   } else {
5933     Type type;
5934     if (!CheckExpr(f, expr, &type)) {
5935       return false;
5936     }
5937 
5938     if (!type.isReturnType()) {
5939       return f.failf(expr, "%s is not a valid return type", type.toChars());
5940     }
5941 
5942     if (!CheckReturnType(f, expr, Type::canonicalize(type))) {
5943       return false;
5944     }
5945   }
5946 
5947   return f.encoder().writeOp(Op::Return);
5948 }
5949 
5950 template <typename Unit>
CheckStatementList(FunctionValidator<Unit> & f,ParseNode * stmtList,const LabelVector * labels)5951 static bool CheckStatementList(FunctionValidator<Unit>& f, ParseNode* stmtList,
5952                                const LabelVector* labels /*= nullptr */) {
5953   MOZ_ASSERT(stmtList->isKind(ParseNodeKind::StatementList));
5954 
5955   if (!f.pushUnbreakableBlock(labels)) {
5956     return false;
5957   }
5958 
5959   for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) {
5960     if (!CheckStatement(f, stmt)) {
5961       return false;
5962     }
5963   }
5964 
5965   return f.popUnbreakableBlock(labels);
5966 }
5967 
5968 template <typename Unit>
CheckLexicalScope(FunctionValidator<Unit> & f,ParseNode * node)5969 static bool CheckLexicalScope(FunctionValidator<Unit>& f, ParseNode* node) {
5970   LexicalScopeNode* lexicalScope = &node->as<LexicalScopeNode>();
5971   if (!lexicalScope->isEmptyScope()) {
5972     return f.fail(lexicalScope, "cannot have 'let' or 'const' declarations");
5973   }
5974 
5975   return CheckStatement(f, lexicalScope->scopeBody());
5976 }
5977 
CheckBreakOrContinue(FunctionValidatorShared & f,bool isBreak,ParseNode * stmt)5978 static bool CheckBreakOrContinue(FunctionValidatorShared& f, bool isBreak,
5979                                  ParseNode* stmt) {
5980   if (TaggedParserAtomIndex maybeLabel = LoopControlMaybeLabel(stmt)) {
5981     return f.writeLabeledBreakOrContinue(maybeLabel, isBreak);
5982   }
5983   return f.writeUnlabeledBreakOrContinue(isBreak);
5984 }
5985 
5986 template <typename Unit>
CheckStatement(FunctionValidator<Unit> & f,ParseNode * stmt)5987 static bool CheckStatement(FunctionValidator<Unit>& f, ParseNode* stmt) {
5988   AutoCheckRecursionLimit recursion(f.cx());
5989   if (!recursion.checkDontReport(f.cx())) {
5990     return f.m().failOverRecursed();
5991   }
5992 
5993   switch (stmt->getKind()) {
5994     case ParseNodeKind::EmptyStmt:
5995       return true;
5996     case ParseNodeKind::ExpressionStmt:
5997       return CheckExprStatement(f, stmt);
5998     case ParseNodeKind::WhileStmt:
5999       return CheckWhile(f, stmt);
6000     case ParseNodeKind::ForStmt:
6001       return CheckFor(f, stmt);
6002     case ParseNodeKind::DoWhileStmt:
6003       return CheckDoWhile(f, stmt);
6004     case ParseNodeKind::LabelStmt:
6005       return CheckLabel(f, stmt);
6006     case ParseNodeKind::IfStmt:
6007       return CheckIf(f, stmt);
6008     case ParseNodeKind::SwitchStmt:
6009       return CheckSwitch(f, stmt);
6010     case ParseNodeKind::ReturnStmt:
6011       return CheckReturn(f, stmt);
6012     case ParseNodeKind::StatementList:
6013       return CheckStatementList(f, stmt);
6014     case ParseNodeKind::BreakStmt:
6015       return CheckBreakOrContinue(f, true, stmt);
6016     case ParseNodeKind::ContinueStmt:
6017       return CheckBreakOrContinue(f, false, stmt);
6018     case ParseNodeKind::LexicalScope:
6019       return CheckLexicalScope(f, stmt);
6020     default:;
6021   }
6022 
6023   return f.fail(stmt, "unexpected statement kind");
6024 }
6025 
6026 template <typename Unit>
ParseFunction(ModuleValidator<Unit> & m,FunctionNode ** funNodeOut,unsigned * line)6027 static bool ParseFunction(ModuleValidator<Unit>& m, FunctionNode** funNodeOut,
6028                           unsigned* line) {
6029   auto& tokenStream = m.tokenStream();
6030 
6031   tokenStream.consumeKnownToken(TokenKind::Function,
6032                                 TokenStreamShared::SlashIsRegExp);
6033 
6034   auto& anyChars = tokenStream.anyCharsAccess();
6035   uint32_t toStringStart = anyChars.currentToken().pos.begin;
6036   *line = anyChars.lineNumber(anyChars.lineToken(toStringStart));
6037 
6038   TokenKind tk;
6039   if (!tokenStream.getToken(&tk, TokenStreamShared::SlashIsRegExp)) {
6040     return false;
6041   }
6042   if (tk == TokenKind::Mul) {
6043     return m.failCurrentOffset("unexpected generator function");
6044   }
6045   if (!TokenKindIsPossibleIdentifier(tk)) {
6046     return false;  // The regular parser will throw a SyntaxError, no need to
6047                    // m.fail.
6048   }
6049 
6050   TaggedParserAtomIndex name = m.parser().bindingIdentifier(YieldIsName);
6051   if (!name) {
6052     return false;
6053   }
6054 
6055   FunctionNode* funNode = m.parser().handler_.newFunction(
6056       FunctionSyntaxKind::Statement, m.parser().pos());
6057   if (!funNode) {
6058     return false;
6059   }
6060 
6061   ParseContext* outerpc = m.parser().pc_;
6062   Directives directives(outerpc);
6063   FunctionFlags flags(FunctionFlags::INTERPRETED_NORMAL);
6064   FunctionBox* funbox = m.parser().newFunctionBox(
6065       funNode, name, flags, toStringStart, directives,
6066       GeneratorKind::NotGenerator, FunctionAsyncKind::SyncFunction);
6067   if (!funbox) {
6068     return false;
6069   }
6070   funbox->initWithEnclosingParseContext(outerpc, flags,
6071                                         FunctionSyntaxKind::Statement);
6072 
6073   Directives newDirectives = directives;
6074   SourceParseContext funpc(&m.parser(), funbox, &newDirectives);
6075   if (!funpc.init()) {
6076     return false;
6077   }
6078 
6079   if (!m.parser().functionFormalParametersAndBody(
6080           InAllowed, YieldIsName, &funNode, FunctionSyntaxKind::Statement)) {
6081     if (anyChars.hadError() || directives == newDirectives) {
6082       return false;
6083     }
6084 
6085     return m.fail(funNode, "encountered new directive in function");
6086   }
6087 
6088   MOZ_ASSERT(!anyChars.hadError());
6089   MOZ_ASSERT(directives == newDirectives);
6090 
6091   *funNodeOut = funNode;
6092   return true;
6093 }
6094 
6095 template <typename Unit>
CheckFunction(ModuleValidator<Unit> & m)6096 static bool CheckFunction(ModuleValidator<Unit>& m) {
6097   // asm.js modules can be quite large when represented as parse trees so pop
6098   // the backing LifoAlloc after parsing/compiling each function. Release the
6099   // parser's lifo memory after the last use of a parse node.
6100   frontend::ParserBase::Mark mark = m.parser().mark();
6101   auto releaseMark =
6102       mozilla::MakeScopeExit([&m, &mark] { m.parser().release(mark); });
6103 
6104   FunctionNode* funNode = nullptr;
6105   unsigned line = 0;
6106   if (!ParseFunction(m, &funNode, &line)) {
6107     return false;
6108   }
6109 
6110   if (!CheckFunctionHead(m, funNode)) {
6111     return false;
6112   }
6113 
6114   FunctionValidator<Unit> f(m, funNode);
6115 
6116   ParseNode* stmtIter = ListHead(FunctionStatementList(funNode));
6117 
6118   if (!CheckProcessingDirectives(m, &stmtIter)) {
6119     return false;
6120   }
6121 
6122   ValTypeVector args;
6123   if (!CheckArguments(f, &stmtIter, &args)) {
6124     return false;
6125   }
6126 
6127   if (!CheckVariables(f, &stmtIter)) {
6128     return false;
6129   }
6130 
6131   ParseNode* lastNonEmptyStmt = nullptr;
6132   for (; stmtIter; stmtIter = NextNonEmptyStatement(stmtIter)) {
6133     lastNonEmptyStmt = stmtIter;
6134     if (!CheckStatement(f, stmtIter)) {
6135       return false;
6136     }
6137   }
6138 
6139   if (!CheckFinalReturn(f, lastNonEmptyStmt)) {
6140     return false;
6141   }
6142 
6143   ValTypeVector results;
6144   if (f.returnedType()) {
6145     if (!results.append(f.returnedType().ref())) {
6146       return false;
6147     }
6148   }
6149 
6150   ModuleValidatorShared::Func* func = nullptr;
6151   if (!CheckFunctionSignature(m, funNode,
6152                               FuncType(std::move(args), std::move(results)),
6153                               FunctionName(funNode), &func)) {
6154     return false;
6155   }
6156 
6157   if (func->defined()) {
6158     return m.failName(funNode, "function '%s' already defined",
6159                       FunctionName(funNode));
6160   }
6161 
6162   f.define(func, line);
6163 
6164   return true;
6165 }
6166 
CheckAllFunctionsDefined(ModuleValidatorShared & m)6167 static bool CheckAllFunctionsDefined(ModuleValidatorShared& m) {
6168   for (unsigned i = 0; i < m.numFuncDefs(); i++) {
6169     const ModuleValidatorShared::Func& f = m.funcDef(i);
6170     if (!f.defined()) {
6171       return m.failNameOffset(f.firstUse(), "missing definition of function %s",
6172                               f.name());
6173     }
6174   }
6175 
6176   return true;
6177 }
6178 
6179 template <typename Unit>
CheckFunctions(ModuleValidator<Unit> & m)6180 static bool CheckFunctions(ModuleValidator<Unit>& m) {
6181   while (true) {
6182     TokenKind tk;
6183     if (!PeekToken(m.parser(), &tk)) {
6184       return false;
6185     }
6186 
6187     if (tk != TokenKind::Function) {
6188       break;
6189     }
6190 
6191     if (!CheckFunction(m)) {
6192       return false;
6193     }
6194   }
6195 
6196   return CheckAllFunctionsDefined(m);
6197 }
6198 
6199 template <typename Unit>
CheckFuncPtrTable(ModuleValidator<Unit> & m,ParseNode * decl)6200 static bool CheckFuncPtrTable(ModuleValidator<Unit>& m, ParseNode* decl) {
6201   if (!decl->isKind(ParseNodeKind::AssignExpr)) {
6202     return m.fail(decl, "function-pointer table must have initializer");
6203   }
6204   AssignmentNode* assignNode = &decl->as<AssignmentNode>();
6205 
6206   ParseNode* var = assignNode->left();
6207 
6208   if (!var->isKind(ParseNodeKind::Name)) {
6209     return m.fail(var, "function-pointer table name is not a plain name");
6210   }
6211 
6212   ParseNode* arrayLiteral = assignNode->right();
6213 
6214   if (!arrayLiteral->isKind(ParseNodeKind::ArrayExpr)) {
6215     return m.fail(
6216         var, "function-pointer table's initializer must be an array literal");
6217   }
6218 
6219   unsigned length = ListLength(arrayLiteral);
6220 
6221   if (!IsPowerOfTwo(length)) {
6222     return m.failf(arrayLiteral,
6223                    "function-pointer table length must be a power of 2 (is %u)",
6224                    length);
6225   }
6226 
6227   unsigned mask = length - 1;
6228 
6229   Uint32Vector elemFuncDefIndices;
6230   const FuncType* sig = nullptr;
6231   for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
6232     if (!elem->isKind(ParseNodeKind::Name)) {
6233       return m.fail(
6234           elem, "function-pointer table's elements must be names of functions");
6235     }
6236 
6237     TaggedParserAtomIndex funcName = elem->as<NameNode>().name();
6238     const ModuleValidatorShared::Func* func = m.lookupFuncDef(funcName);
6239     if (!func) {
6240       return m.fail(
6241           elem, "function-pointer table's elements must be names of functions");
6242     }
6243 
6244     const FuncType& funcSig = m.env().types->funcType(func->sigIndex());
6245     if (sig) {
6246       if (*sig != funcSig) {
6247         return m.fail(elem, "all functions in table must have same signature");
6248       }
6249     } else {
6250       sig = &funcSig;
6251     }
6252 
6253     if (!elemFuncDefIndices.append(func->funcDefIndex())) {
6254       return false;
6255     }
6256   }
6257 
6258   FuncType copy;
6259   if (!copy.clone(*sig)) {
6260     return false;
6261   }
6262 
6263   uint32_t tableIndex;
6264   if (!CheckFuncPtrTableAgainstExisting(m, var, var->as<NameNode>().name(),
6265                                         std::move(copy), mask, &tableIndex)) {
6266     return false;
6267   }
6268 
6269   if (!m.defineFuncPtrTable(tableIndex, std::move(elemFuncDefIndices))) {
6270     return m.fail(var, "duplicate function-pointer definition");
6271   }
6272 
6273   return true;
6274 }
6275 
6276 template <typename Unit>
CheckFuncPtrTables(ModuleValidator<Unit> & m)6277 static bool CheckFuncPtrTables(ModuleValidator<Unit>& m) {
6278   while (true) {
6279     ParseNode* varStmt;
6280     if (!ParseVarOrConstStatement(m.parser(), &varStmt)) {
6281       return false;
6282     }
6283     if (!varStmt) {
6284       break;
6285     }
6286     for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
6287       if (!CheckFuncPtrTable(m, var)) {
6288         return false;
6289       }
6290     }
6291   }
6292 
6293   for (unsigned i = 0; i < m.numFuncPtrTables(); i++) {
6294     ModuleValidatorShared::Table& table = m.table(i);
6295     if (!table.defined()) {
6296       return m.failNameOffset(table.firstUse(),
6297                               "function-pointer table %s wasn't defined",
6298                               table.name());
6299     }
6300   }
6301 
6302   return true;
6303 }
6304 
CheckModuleExportFunction(ModuleValidatorShared & m,ParseNode * pn,TaggedParserAtomIndex maybeFieldName=TaggedParserAtomIndex::null ())6305 static bool CheckModuleExportFunction(
6306     ModuleValidatorShared& m, ParseNode* pn,
6307     TaggedParserAtomIndex maybeFieldName = TaggedParserAtomIndex::null()) {
6308   if (!pn->isKind(ParseNodeKind::Name)) {
6309     return m.fail(pn, "expected name of exported function");
6310   }
6311 
6312   TaggedParserAtomIndex funcName = pn->as<NameNode>().name();
6313   const ModuleValidatorShared::Func* func = m.lookupFuncDef(funcName);
6314   if (!func) {
6315     return m.failName(pn, "function '%s' not found", funcName);
6316   }
6317 
6318   return m.addExportField(*func, maybeFieldName);
6319 }
6320 
CheckModuleExportObject(ModuleValidatorShared & m,ParseNode * object)6321 static bool CheckModuleExportObject(ModuleValidatorShared& m,
6322                                     ParseNode* object) {
6323   MOZ_ASSERT(object->isKind(ParseNodeKind::ObjectExpr));
6324 
6325   for (ParseNode* pn = ListHead(object); pn; pn = NextNode(pn)) {
6326     if (!IsNormalObjectField(pn)) {
6327       return m.fail(pn,
6328                     "only normal object properties may be used in the export "
6329                     "object literal");
6330     }
6331 
6332     TaggedParserAtomIndex fieldName = ObjectNormalFieldName(pn);
6333 
6334     ParseNode* initNode = ObjectNormalFieldInitializer(pn);
6335     if (!initNode->isKind(ParseNodeKind::Name)) {
6336       return m.fail(
6337           initNode,
6338           "initializer of exported object literal must be name of function");
6339     }
6340 
6341     if (!CheckModuleExportFunction(m, initNode, fieldName)) {
6342       return false;
6343     }
6344   }
6345 
6346   return true;
6347 }
6348 
6349 template <typename Unit>
CheckModuleReturn(ModuleValidator<Unit> & m)6350 static bool CheckModuleReturn(ModuleValidator<Unit>& m) {
6351   TokenKind tk;
6352   if (!GetToken(m.parser(), &tk)) {
6353     return false;
6354   }
6355   auto& ts = m.parser().tokenStream;
6356   if (tk != TokenKind::Return) {
6357     return m.failCurrentOffset(
6358         (tk == TokenKind::RightCurly || tk == TokenKind::Eof)
6359             ? "expecting return statement"
6360             : "invalid asm.js. statement");
6361   }
6362   ts.anyCharsAccess().ungetToken();
6363 
6364   ParseNode* returnStmt = m.parser().statementListItem(YieldIsName);
6365   if (!returnStmt) {
6366     return false;
6367   }
6368 
6369   ParseNode* returnExpr = ReturnExpr(returnStmt);
6370   if (!returnExpr) {
6371     return m.fail(returnStmt, "export statement must return something");
6372   }
6373 
6374   if (returnExpr->isKind(ParseNodeKind::ObjectExpr)) {
6375     if (!CheckModuleExportObject(m, returnExpr)) {
6376       return false;
6377     }
6378   } else {
6379     if (!CheckModuleExportFunction(m, returnExpr)) {
6380       return false;
6381     }
6382   }
6383 
6384   return true;
6385 }
6386 
6387 template <typename Unit>
CheckModuleEnd(ModuleValidator<Unit> & m)6388 static bool CheckModuleEnd(ModuleValidator<Unit>& m) {
6389   TokenKind tk;
6390   if (!GetToken(m.parser(), &tk)) {
6391     return false;
6392   }
6393 
6394   if (tk != TokenKind::Eof && tk != TokenKind::RightCurly) {
6395     return m.failCurrentOffset(
6396         "top-level export (return) must be the last statement");
6397   }
6398 
6399   m.parser().tokenStream.anyCharsAccess().ungetToken();
6400   return true;
6401 }
6402 
6403 template <typename Unit>
CheckModule(JSContext * cx,ParserAtomsTable & parserAtoms,AsmJSParser<Unit> & parser,ParseNode * stmtList,unsigned * time)6404 static SharedModule CheckModule(JSContext* cx, ParserAtomsTable& parserAtoms,
6405                                 AsmJSParser<Unit>& parser, ParseNode* stmtList,
6406                                 unsigned* time) {
6407   int64_t before = PRMJ_Now();
6408 
6409   FunctionNode* moduleFunctionNode = parser.pc_->functionBox()->functionNode;
6410 
6411   ModuleValidator<Unit> m(cx, parserAtoms, parser, moduleFunctionNode);
6412   if (!m.init()) {
6413     return nullptr;
6414   }
6415 
6416   if (!CheckFunctionHead(m, moduleFunctionNode)) {
6417     return nullptr;
6418   }
6419 
6420   if (!CheckModuleArguments(m, moduleFunctionNode)) {
6421     return nullptr;
6422   }
6423 
6424   if (!CheckPrecedingStatements(m, stmtList)) {
6425     return nullptr;
6426   }
6427 
6428   if (!CheckModuleProcessingDirectives(m)) {
6429     return nullptr;
6430   }
6431 
6432   if (!CheckModuleGlobals(m)) {
6433     return nullptr;
6434   }
6435 
6436   if (!m.startFunctionBodies()) {
6437     return nullptr;
6438   }
6439 
6440   if (!CheckFunctions(m)) {
6441     return nullptr;
6442   }
6443 
6444   if (!CheckFuncPtrTables(m)) {
6445     return nullptr;
6446   }
6447 
6448   if (!CheckModuleReturn(m)) {
6449     return nullptr;
6450   }
6451 
6452   if (!CheckModuleEnd(m)) {
6453     return nullptr;
6454   }
6455 
6456   SharedModule module = m.finish();
6457   if (!module) {
6458     return nullptr;
6459   }
6460 
6461   *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
6462   return module;
6463 }
6464 
6465 /*****************************************************************************/
6466 // Link-time validation
6467 
LinkFail(JSContext * cx,const char * str)6468 static bool LinkFail(JSContext* cx, const char* str) {
6469   WarnNumberASCII(cx, JSMSG_USE_ASM_LINK_FAIL, str);
6470   return false;
6471 }
6472 
IsMaybeWrappedScriptedProxy(JSObject * obj)6473 static bool IsMaybeWrappedScriptedProxy(JSObject* obj) {
6474   JSObject* unwrapped = UncheckedUnwrap(obj);
6475   return unwrapped && IsScriptedProxy(unwrapped);
6476 }
6477 
GetDataProperty(JSContext * cx,HandleValue objVal,HandleAtom field,MutableHandleValue v)6478 static bool GetDataProperty(JSContext* cx, HandleValue objVal, HandleAtom field,
6479                             MutableHandleValue v) {
6480   if (!objVal.isObject()) {
6481     return LinkFail(cx, "accessing property of non-object");
6482   }
6483 
6484   RootedObject obj(cx, &objVal.toObject());
6485   if (IsMaybeWrappedScriptedProxy(obj)) {
6486     return LinkFail(cx, "accessing property of a Proxy");
6487   }
6488 
6489   RootedId id(cx, AtomToId(field));
6490   Rooted<mozilla::Maybe<PropertyDescriptor>> desc(cx);
6491   RootedObject holder(cx);
6492   if (!GetPropertyDescriptor(cx, obj, id, &desc, &holder)) {
6493     return false;
6494   }
6495 
6496   if (!desc.isSome()) {
6497     return LinkFail(cx, "property not present on object");
6498   }
6499 
6500   if (!desc->isDataDescriptor()) {
6501     return LinkFail(cx, "property is not a data property");
6502   }
6503 
6504   v.set(desc->value());
6505   return true;
6506 }
6507 
GetDataProperty(JSContext * cx,HandleValue objVal,const char * fieldChars,MutableHandleValue v)6508 static bool GetDataProperty(JSContext* cx, HandleValue objVal,
6509                             const char* fieldChars, MutableHandleValue v) {
6510   RootedAtom field(cx, AtomizeUTF8Chars(cx, fieldChars, strlen(fieldChars)));
6511   if (!field) {
6512     return false;
6513   }
6514 
6515   return GetDataProperty(cx, objVal, field, v);
6516 }
6517 
GetDataProperty(JSContext * cx,HandleValue objVal,const ImmutablePropertyNamePtr & field,MutableHandleValue v)6518 static bool GetDataProperty(JSContext* cx, HandleValue objVal,
6519                             const ImmutablePropertyNamePtr& field,
6520                             MutableHandleValue v) {
6521   HandlePropertyName fieldHandle = field;
6522   return GetDataProperty(cx, objVal, fieldHandle, v);
6523 }
6524 
HasObjectValueOfMethodPure(JSObject * obj,JSContext * cx)6525 static bool HasObjectValueOfMethodPure(JSObject* obj, JSContext* cx) {
6526   Value v;
6527   if (!GetPropertyPure(cx, obj, NameToId(cx->names().valueOf), &v)) {
6528     return false;
6529   }
6530 
6531   JSFunction* fun;
6532   if (!IsFunctionObject(v, &fun)) {
6533     return false;
6534   }
6535 
6536   return IsSelfHostedFunctionWithName(fun, cx->names().Object_valueOf);
6537 }
6538 
HasPureCoercion(JSContext * cx,HandleValue v)6539 static bool HasPureCoercion(JSContext* cx, HandleValue v) {
6540   // Ideally, we'd reject all non-primitives, but Emscripten has a bug that
6541   // generates code that passes functions for some imports. To avoid breaking
6542   // all the code that contains this bug, we make an exception for functions
6543   // that don't have user-defined valueOf or toString, for their coercions
6544   // are not observable and coercion via ToNumber/ToInt32 definitely produces
6545   // NaN/0. We should remove this special case later once most apps have been
6546   // built with newer Emscripten.
6547   return v.toObject().is<JSFunction>() &&
6548          HasNoToPrimitiveMethodPure(&v.toObject(), cx) &&
6549          HasObjectValueOfMethodPure(&v.toObject(), cx) &&
6550          HasNativeMethodPure(&v.toObject(), cx->names().toString, fun_toString,
6551                              cx);
6552 }
6553 
ValidateGlobalVariable(JSContext * cx,const AsmJSGlobal & global,HandleValue importVal,Maybe<LitValPOD> * val)6554 static bool ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global,
6555                                    HandleValue importVal,
6556                                    Maybe<LitValPOD>* val) {
6557   switch (global.varInitKind()) {
6558     case AsmJSGlobal::InitConstant:
6559       val->emplace(global.varInitVal());
6560       return true;
6561 
6562     case AsmJSGlobal::InitImport: {
6563       RootedValue v(cx);
6564       if (!GetDataProperty(cx, importVal, global.field(), &v)) {
6565         return false;
6566       }
6567 
6568       if (!v.isPrimitive() && !HasPureCoercion(cx, v)) {
6569         return LinkFail(cx, "Imported values must be primitives");
6570       }
6571 
6572       switch (global.varInitImportType().kind()) {
6573         case ValType::I32: {
6574           int32_t i32;
6575           if (!ToInt32(cx, v, &i32)) {
6576             return false;
6577           }
6578           val->emplace(uint32_t(i32));
6579           return true;
6580         }
6581         case ValType::I64:
6582           MOZ_CRASH("int64");
6583         case ValType::V128:
6584           MOZ_CRASH("v128");
6585         case ValType::Rtt:
6586           MOZ_CRASH("rtt");
6587         case ValType::F32: {
6588           float f;
6589           if (!RoundFloat32(cx, v, &f)) {
6590             return false;
6591           }
6592           val->emplace(f);
6593           return true;
6594         }
6595         case ValType::F64: {
6596           double d;
6597           if (!ToNumber(cx, v, &d)) {
6598             return false;
6599           }
6600           val->emplace(d);
6601           return true;
6602         }
6603         case ValType::Ref: {
6604           MOZ_CRASH("not available in asm.js");
6605         }
6606       }
6607     }
6608   }
6609 
6610   MOZ_CRASH("unreachable");
6611 }
6612 
ValidateFFI(JSContext * cx,const AsmJSGlobal & global,HandleValue importVal,MutableHandle<FunctionVector> ffis)6613 static bool ValidateFFI(JSContext* cx, const AsmJSGlobal& global,
6614                         HandleValue importVal,
6615                         MutableHandle<FunctionVector> ffis) {
6616   RootedValue v(cx);
6617   if (!GetDataProperty(cx, importVal, global.field(), &v)) {
6618     return false;
6619   }
6620 
6621   if (!IsFunctionObject(v)) {
6622     return LinkFail(cx, "FFI imports must be functions");
6623   }
6624 
6625   ffis[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
6626   return true;
6627 }
6628 
ValidateArrayView(JSContext * cx,const AsmJSGlobal & global,HandleValue globalVal)6629 static bool ValidateArrayView(JSContext* cx, const AsmJSGlobal& global,
6630                               HandleValue globalVal) {
6631   if (!global.field()) {
6632     return true;
6633   }
6634 
6635   if (Scalar::isBigIntType(global.viewType())) {
6636     return LinkFail(cx, "bad typed array constructor");
6637   }
6638 
6639   RootedValue v(cx);
6640   if (!GetDataProperty(cx, globalVal, global.field(), &v)) {
6641     return false;
6642   }
6643 
6644   bool tac = IsTypedArrayConstructor(v, global.viewType());
6645   if (!tac) {
6646     return LinkFail(cx, "bad typed array constructor");
6647   }
6648 
6649   return true;
6650 }
6651 
ValidateMathBuiltinFunction(JSContext * cx,const AsmJSGlobal & global,HandleValue globalVal)6652 static bool ValidateMathBuiltinFunction(JSContext* cx,
6653                                         const AsmJSGlobal& global,
6654                                         HandleValue globalVal) {
6655   RootedValue v(cx);
6656   if (!GetDataProperty(cx, globalVal, cx->names().Math, &v)) {
6657     return false;
6658   }
6659 
6660   if (!GetDataProperty(cx, v, global.field(), &v)) {
6661     return false;
6662   }
6663 
6664   Native native = nullptr;
6665   switch (global.mathBuiltinFunction()) {
6666     case AsmJSMathBuiltin_sin:
6667       native = math_sin;
6668       break;
6669     case AsmJSMathBuiltin_cos:
6670       native = math_cos;
6671       break;
6672     case AsmJSMathBuiltin_tan:
6673       native = math_tan;
6674       break;
6675     case AsmJSMathBuiltin_asin:
6676       native = math_asin;
6677       break;
6678     case AsmJSMathBuiltin_acos:
6679       native = math_acos;
6680       break;
6681     case AsmJSMathBuiltin_atan:
6682       native = math_atan;
6683       break;
6684     case AsmJSMathBuiltin_ceil:
6685       native = math_ceil;
6686       break;
6687     case AsmJSMathBuiltin_floor:
6688       native = math_floor;
6689       break;
6690     case AsmJSMathBuiltin_exp:
6691       native = math_exp;
6692       break;
6693     case AsmJSMathBuiltin_log:
6694       native = math_log;
6695       break;
6696     case AsmJSMathBuiltin_pow:
6697       native = math_pow;
6698       break;
6699     case AsmJSMathBuiltin_sqrt:
6700       native = math_sqrt;
6701       break;
6702     case AsmJSMathBuiltin_min:
6703       native = math_min;
6704       break;
6705     case AsmJSMathBuiltin_max:
6706       native = math_max;
6707       break;
6708     case AsmJSMathBuiltin_abs:
6709       native = math_abs;
6710       break;
6711     case AsmJSMathBuiltin_atan2:
6712       native = math_atan2;
6713       break;
6714     case AsmJSMathBuiltin_imul:
6715       native = math_imul;
6716       break;
6717     case AsmJSMathBuiltin_clz32:
6718       native = math_clz32;
6719       break;
6720     case AsmJSMathBuiltin_fround:
6721       native = math_fround;
6722       break;
6723   }
6724 
6725   if (!IsNativeFunction(v, native)) {
6726     return LinkFail(cx, "bad Math.* builtin function");
6727   }
6728 
6729   return true;
6730 }
6731 
ValidateConstant(JSContext * cx,const AsmJSGlobal & global,HandleValue globalVal)6732 static bool ValidateConstant(JSContext* cx, const AsmJSGlobal& global,
6733                              HandleValue globalVal) {
6734   RootedValue v(cx, globalVal);
6735 
6736   if (global.constantKind() == AsmJSGlobal::MathConstant) {
6737     if (!GetDataProperty(cx, v, cx->names().Math, &v)) {
6738       return false;
6739     }
6740   }
6741 
6742   if (!GetDataProperty(cx, v, global.field(), &v)) {
6743     return false;
6744   }
6745 
6746   if (!v.isNumber()) {
6747     return LinkFail(cx, "math / global constant value needs to be a number");
6748   }
6749 
6750   // NaN != NaN
6751   if (IsNaN(global.constantValue())) {
6752     if (!IsNaN(v.toNumber())) {
6753       return LinkFail(cx, "global constant value needs to be NaN");
6754     }
6755   } else {
6756     if (v.toNumber() != global.constantValue()) {
6757       return LinkFail(cx, "global constant value mismatch");
6758     }
6759   }
6760 
6761   return true;
6762 }
6763 
CheckBuffer(JSContext * cx,const AsmJSMetadata & metadata,HandleValue bufferVal,MutableHandle<ArrayBufferObject * > buffer)6764 static bool CheckBuffer(JSContext* cx, const AsmJSMetadata& metadata,
6765                         HandleValue bufferVal,
6766                         MutableHandle<ArrayBufferObject*> buffer) {
6767   if (!bufferVal.isObject()) {
6768     return LinkFail(cx, "buffer must be an object");
6769   }
6770   JSObject* bufferObj = &bufferVal.toObject();
6771 
6772   if (metadata.usesSharedMemory()) {
6773     if (!bufferObj->is<SharedArrayBufferObject>()) {
6774       return LinkFail(
6775           cx, "shared views can only be constructed onto SharedArrayBuffer");
6776     }
6777     return LinkFail(cx, "Unable to prepare SharedArrayBuffer for asm.js use");
6778   }
6779 
6780   if (!bufferObj->is<ArrayBufferObject>()) {
6781     return LinkFail(cx,
6782                     "unshared views can only be constructed onto ArrayBuffer");
6783   }
6784 
6785   buffer.set(&bufferObj->as<ArrayBufferObject>());
6786 
6787   size_t memoryLength = buffer->byteLength();
6788 
6789   if (!IsValidAsmJSHeapLength(memoryLength)) {
6790     UniqueChars msg;
6791     if (memoryLength > MaxHeapLength) {
6792       msg = JS_smprintf("ArrayBuffer byteLength 0x%" PRIx64
6793                         " is not a valid heap length - it is too long."
6794                         " The longest valid length is 0x%" PRIx64,
6795                         uint64_t(memoryLength), MaxHeapLength);
6796     } else {
6797       msg = JS_smprintf("ArrayBuffer byteLength 0x%" PRIx64
6798                         " is not a valid heap length. The next "
6799                         "valid length is 0x%" PRIx64,
6800                         uint64_t(memoryLength),
6801                         RoundUpToNextValidAsmJSHeapLength(memoryLength));
6802     }
6803     if (!msg) {
6804       return false;
6805     }
6806     return LinkFail(cx, msg.get());
6807   }
6808 
6809   // This check is sufficient without considering the size of the loaded datum
6810   // because heap loads and stores start on an aligned boundary and the heap
6811   // byteLength has larger alignment.
6812   uint64_t minMemoryLength =
6813       metadata.usesMemory() ? metadata.memory->initialLength32() : 0;
6814   MOZ_ASSERT((minMemoryLength - 1) <= INT32_MAX);
6815   if (memoryLength < minMemoryLength) {
6816     UniqueChars msg(JS_smprintf("ArrayBuffer byteLength of 0x%" PRIx64
6817                                 " is less than 0x%" PRIx64 " (the "
6818                                 "size implied "
6819                                 "by const heap accesses).",
6820                                 uint64_t(memoryLength), minMemoryLength));
6821     if (!msg) {
6822       return false;
6823     }
6824     return LinkFail(cx, msg.get());
6825   }
6826 
6827   // ArrayBuffer lengths in SpiderMonkey used to be restricted to <= INT32_MAX,
6828   // but that has since been relaxed for the benefit of wasm.  We keep the old
6829   // limit for asm.js so as to avoid having to worry about whether the asm.js
6830   // implementation is safe for larger heaps.
6831   if (memoryLength >= INT32_MAX) {
6832     UniqueChars msg(
6833         JS_smprintf("ArrayBuffer byteLength 0x%" PRIx64
6834                     " is too large for asm.js (implementation limit).",
6835                     uint64_t(memoryLength)));
6836     if (!msg) {
6837       return false;
6838     }
6839     return LinkFail(cx, msg.get());
6840   }
6841 
6842   if (!buffer->prepareForAsmJS()) {
6843     return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
6844   }
6845 
6846   MOZ_ASSERT(buffer->isPreparedForAsmJS());
6847   return true;
6848 }
6849 
GetImports(JSContext * cx,const AsmJSMetadata & metadata,HandleValue globalVal,HandleValue importVal,ImportValues * imports)6850 static bool GetImports(JSContext* cx, const AsmJSMetadata& metadata,
6851                        HandleValue globalVal, HandleValue importVal,
6852                        ImportValues* imports) {
6853   Rooted<FunctionVector> ffis(cx, FunctionVector(cx));
6854   if (!ffis.resize(metadata.numFFIs)) {
6855     return false;
6856   }
6857 
6858   for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
6859     switch (global.which()) {
6860       case AsmJSGlobal::Variable: {
6861         Maybe<LitValPOD> litVal;
6862         if (!ValidateGlobalVariable(cx, global, importVal, &litVal)) {
6863           return false;
6864         }
6865         if (!imports->globalValues.append(Val(litVal->asLitVal()))) {
6866           return false;
6867         }
6868         break;
6869       }
6870       case AsmJSGlobal::FFI:
6871         if (!ValidateFFI(cx, global, importVal, &ffis)) {
6872           return false;
6873         }
6874         break;
6875       case AsmJSGlobal::ArrayView:
6876       case AsmJSGlobal::ArrayViewCtor:
6877         if (!ValidateArrayView(cx, global, globalVal)) {
6878           return false;
6879         }
6880         break;
6881       case AsmJSGlobal::MathBuiltinFunction:
6882         if (!ValidateMathBuiltinFunction(cx, global, globalVal)) {
6883           return false;
6884         }
6885         break;
6886       case AsmJSGlobal::Constant:
6887         if (!ValidateConstant(cx, global, globalVal)) {
6888           return false;
6889         }
6890         break;
6891     }
6892   }
6893 
6894   for (const AsmJSImport& import : metadata.asmJSImports) {
6895     if (!imports->funcs.append(ffis[import.ffiIndex()])) {
6896       return false;
6897     }
6898   }
6899 
6900   return true;
6901 }
6902 
TryInstantiate(JSContext * cx,CallArgs args,const Module & module,const AsmJSMetadata & metadata,MutableHandleWasmInstanceObject instanceObj,MutableHandleObject exportObj)6903 static bool TryInstantiate(JSContext* cx, CallArgs args, const Module& module,
6904                            const AsmJSMetadata& metadata,
6905                            MutableHandleWasmInstanceObject instanceObj,
6906                            MutableHandleObject exportObj) {
6907   HandleValue globalVal = args.get(0);
6908   HandleValue importVal = args.get(1);
6909   HandleValue bufferVal = args.get(2);
6910 
6911   // Re-check HasPlatformSupport(cx) since this varies per-thread and
6912   // 'module' may have been produced on a parser thread.
6913   if (!HasPlatformSupport(cx)) {
6914     return LinkFail(cx, "no platform support");
6915   }
6916 
6917   Rooted<ImportValues> imports(cx);
6918 
6919   if (module.metadata().usesMemory()) {
6920     RootedArrayBufferObject buffer(cx);
6921     if (!CheckBuffer(cx, metadata, bufferVal, &buffer)) {
6922       return false;
6923     }
6924 
6925     imports.get().memory =
6926         WasmMemoryObject::create(cx, buffer, /* hugeMemory= */ false, nullptr);
6927     if (!imports.get().memory) {
6928       return false;
6929     }
6930   }
6931 
6932   if (!GetImports(cx, metadata, globalVal, importVal, imports.address())) {
6933     return false;
6934   }
6935 
6936   if (!module.instantiate(cx, imports.get(), nullptr, instanceObj)) {
6937     return false;
6938   }
6939 
6940   exportObj.set(&instanceObj->exportsObj());
6941   return true;
6942 }
6943 
HandleInstantiationFailure(JSContext * cx,CallArgs args,const AsmJSMetadata & metadata)6944 static bool HandleInstantiationFailure(JSContext* cx, CallArgs args,
6945                                        const AsmJSMetadata& metadata) {
6946   using js::frontend::FunctionSyntaxKind;
6947 
6948   RootedAtom name(cx, args.callee().as<JSFunction>().explicitName());
6949 
6950   if (cx->isExceptionPending()) {
6951     return false;
6952   }
6953 
6954   ScriptSource* source = metadata.maybeScriptSource();
6955 
6956   // Source discarding is allowed to affect JS semantics because it is never
6957   // enabled for normal JS content.
6958   bool haveSource;
6959   if (!ScriptSource::loadSource(cx, source, &haveSource)) {
6960     return false;
6961   }
6962   if (!haveSource) {
6963     JS_ReportErrorASCII(cx,
6964                         "asm.js link failure with source discarding enabled");
6965     return false;
6966   }
6967 
6968   uint32_t begin = metadata.toStringStart;
6969   uint32_t end = metadata.srcEndAfterCurly();
6970   Rooted<JSLinearString*> src(cx, source->substringDontDeflate(cx, begin, end));
6971   if (!src) {
6972     return false;
6973   }
6974 
6975   JS::CompileOptions options(cx);
6976   options.setMutedErrors(source->mutedErrors())
6977       .setFile(source->filename())
6978       .setNoScriptRval(false);
6979   options.asmJSOption = AsmJSOption::DisabledByLinker;
6980 
6981   // The exported function inherits an implicit strict context if the module
6982   // also inherited it somehow.
6983   if (metadata.strict) {
6984     options.setForceStrictMode();
6985   }
6986 
6987   AutoStableStringChars stableChars(cx);
6988   if (!stableChars.initTwoByte(cx, src)) {
6989     return false;
6990   }
6991 
6992   SourceText<char16_t> srcBuf;
6993 
6994   const char16_t* chars = stableChars.twoByteRange().begin().get();
6995   SourceOwnership ownership = stableChars.maybeGiveOwnershipToCaller()
6996                                   ? SourceOwnership::TakeOwnership
6997                                   : SourceOwnership::Borrowed;
6998   if (!srcBuf.init(cx, chars, end - begin, ownership)) {
6999     return false;
7000   }
7001 
7002   FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
7003 
7004   RootedFunction fun(cx, frontend::CompileStandaloneFunction(
7005                              cx, options, srcBuf, Nothing(), syntaxKind));
7006   if (!fun) {
7007     return false;
7008   }
7009 
7010   fun->initEnvironment(&cx->global()->lexicalEnvironment());
7011 
7012   // Call the function we just recompiled.
7013   args.setCallee(ObjectValue(*fun));
7014   return InternalCallOrConstruct(
7015       cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
7016 }
7017 
AsmJSModuleFunctionToModule(JSFunction * fun)7018 static const Module& AsmJSModuleFunctionToModule(JSFunction* fun) {
7019   MOZ_ASSERT(IsAsmJSModule(fun));
7020   const Value& v = fun->getExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT);
7021   return v.toObject().as<WasmModuleObject>().module();
7022 }
7023 
7024 // Implements the semantics of an asm.js module function that has been
7025 // successfully validated.
InstantiateAsmJS(JSContext * cx,unsigned argc,JS::Value * vp)7026 bool js::InstantiateAsmJS(JSContext* cx, unsigned argc, JS::Value* vp) {
7027   CallArgs args = CallArgsFromVp(argc, vp);
7028 
7029   JSFunction* callee = &args.callee().as<JSFunction>();
7030   const Module& module = AsmJSModuleFunctionToModule(callee);
7031   const AsmJSMetadata& metadata = module.metadata().asAsmJS();
7032 
7033   RootedWasmInstanceObject instanceObj(cx);
7034   RootedObject exportObj(cx);
7035   if (!TryInstantiate(cx, args, module, metadata, &instanceObj, &exportObj)) {
7036     // Link-time validation checks failed, so reparse the entire asm.js
7037     // module from scratch to get normal interpreted bytecode which we can
7038     // simply Invoke. Very slow.
7039     return HandleInstantiationFailure(cx, args, metadata);
7040   }
7041 
7042   args.rval().set(ObjectValue(*exportObj));
7043   return true;
7044 }
7045 
7046 /*****************************************************************************/
7047 // Top-level js::CompileAsmJS
7048 
NoExceptionPending(JSContext * cx)7049 static bool NoExceptionPending(JSContext* cx) {
7050   return cx->isHelperThreadContext() || !cx->isExceptionPending();
7051 }
7052 
SuccessfulValidation(frontend::ParserBase & parser,unsigned compilationTime)7053 static bool SuccessfulValidation(frontend::ParserBase& parser,
7054                                  unsigned compilationTime) {
7055   unsigned errNum = js::SupportDifferentialTesting()
7056                         ? JSMSG_USE_ASM_TYPE_OK_NO_TIME
7057                         : JSMSG_USE_ASM_TYPE_OK;
7058 
7059   char timeChars[20];
7060   SprintfLiteral(timeChars, "%u", compilationTime);
7061 
7062   return parser.warningNoOffset(errNum, timeChars);
7063 }
7064 
TypeFailureWarning(frontend::ParserBase & parser,const char * str)7065 static bool TypeFailureWarning(frontend::ParserBase& parser, const char* str) {
7066   if (parser.options().throwOnAsmJSValidationFailureOption) {
7067     parser.errorNoOffset(JSMSG_USE_ASM_TYPE_FAIL, str ? str : "");
7068     return false;
7069   }
7070 
7071   // Per the asm.js standard convention, whether failure sets a pending
7072   // exception determines whether to attempt non-asm.js reparsing, so ignore
7073   // the return value below.
7074   (void)parser.warningNoOffset(JSMSG_USE_ASM_TYPE_FAIL, str ? str : "");
7075   return false;
7076 }
7077 
7078 // asm.js requires Ion to be available on the current hardware/OS and to be
7079 // enabled for wasm, since asm.js compilation goes via wasm.
IsAsmJSCompilerAvailable(JSContext * cx)7080 static bool IsAsmJSCompilerAvailable(JSContext* cx) {
7081   return HasPlatformSupport(cx) && WasmCompilerForAsmJSAvailable(cx);
7082 }
7083 
EstablishPreconditions(JSContext * cx,frontend::ParserBase & parser)7084 static bool EstablishPreconditions(JSContext* cx,
7085                                    frontend::ParserBase& parser) {
7086   switch (parser.options().asmJSOption) {
7087     case AsmJSOption::DisabledByAsmJSPref:
7088       return TypeFailureWarning(
7089           parser, "Asm.js optimizer disabled by 'asmjs' runtime option");
7090     case AsmJSOption::DisabledByLinker:
7091       return TypeFailureWarning(
7092           parser,
7093           "Asm.js optimizer disabled by linker (instantiation failure)");
7094     case AsmJSOption::DisabledByNoWasmCompiler:
7095       return TypeFailureWarning(parser,
7096                                 "Asm.js optimizer disabled because no suitable "
7097                                 "wasm compiler is available");
7098     case AsmJSOption::DisabledByDebugger:
7099       return TypeFailureWarning(
7100           parser, "Asm.js optimizer disabled because debugger is active");
7101     case AsmJSOption::Enabled:
7102       break;
7103   }
7104 
7105   if (parser.pc_->isGenerator()) {
7106     return TypeFailureWarning(parser,
7107                               "Asm.js optimizer disabled in generator context");
7108   }
7109 
7110   if (parser.pc_->isAsync()) {
7111     return TypeFailureWarning(parser,
7112                               "Asm.js optimizer disabled in async context");
7113   }
7114 
7115   if (parser.pc_->isArrowFunction()) {
7116     return TypeFailureWarning(
7117         parser, "Asm.js optimizer disabled in arrow function context");
7118   }
7119 
7120   // Class constructors are also methods
7121   if (parser.pc_->isMethod() || parser.pc_->isGetterOrSetter()) {
7122     return TypeFailureWarning(
7123         parser,
7124         "Asm.js optimizer disabled in class constructor or method context");
7125   }
7126 
7127   return true;
7128 }
7129 
7130 template <typename Unit>
DoCompileAsmJS(JSContext * cx,ParserAtomsTable & parserAtoms,AsmJSParser<Unit> & parser,ParseNode * stmtList,bool * validated)7131 static bool DoCompileAsmJS(JSContext* cx, ParserAtomsTable& parserAtoms,
7132                            AsmJSParser<Unit>& parser, ParseNode* stmtList,
7133                            bool* validated) {
7134   *validated = false;
7135 
7136   // Various conditions disable asm.js optimizations.
7137   if (!EstablishPreconditions(cx, parser)) {
7138     return NoExceptionPending(cx);
7139   }
7140 
7141   // "Checking" parses, validates and compiles, producing a fully compiled
7142   // WasmModuleObject as result.
7143   unsigned time;
7144   SharedModule module = CheckModule(cx, parserAtoms, parser, stmtList, &time);
7145   if (!module) {
7146     return NoExceptionPending(cx);
7147   }
7148 
7149   // Finished! Save the ref-counted module on the FunctionBox. When JSFunctions
7150   // are eventually allocated we will create an asm.js constructor for it.
7151   FunctionBox* funbox = parser.pc_->functionBox();
7152   MOZ_ASSERT(funbox->isInterpreted());
7153   if (!funbox->setAsmJSModule(module)) {
7154     return NoExceptionPending(cx);
7155   }
7156 
7157   // Success! Write to the console with a "warning" message indicating
7158   // total compilation time.
7159   *validated = true;
7160   SuccessfulValidation(parser, time);
7161   return NoExceptionPending(cx);
7162 }
7163 
CompileAsmJS(JSContext * cx,ParserAtomsTable & parserAtoms,AsmJSParser<char16_t> & parser,ParseNode * stmtList,bool * validated)7164 bool js::CompileAsmJS(JSContext* cx, ParserAtomsTable& parserAtoms,
7165                       AsmJSParser<char16_t>& parser, ParseNode* stmtList,
7166                       bool* validated) {
7167   return DoCompileAsmJS(cx, parserAtoms, parser, stmtList, validated);
7168 }
7169 
CompileAsmJS(JSContext * cx,ParserAtomsTable & parserAtoms,AsmJSParser<Utf8Unit> & parser,ParseNode * stmtList,bool * validated)7170 bool js::CompileAsmJS(JSContext* cx, ParserAtomsTable& parserAtoms,
7171                       AsmJSParser<Utf8Unit>& parser, ParseNode* stmtList,
7172                       bool* validated) {
7173   return DoCompileAsmJS(cx, parserAtoms, parser, stmtList, validated);
7174 }
7175 
7176 /*****************************************************************************/
7177 // asm.js testing functions
7178 
IsAsmJSModuleNative(Native native)7179 bool js::IsAsmJSModuleNative(Native native) {
7180   return native == InstantiateAsmJS;
7181 }
7182 
IsAsmJSModule(JSFunction * fun)7183 bool js::IsAsmJSModule(JSFunction* fun) {
7184   return fun->maybeNative() == InstantiateAsmJS;
7185 }
7186 
IsAsmJSFunction(JSFunction * fun)7187 bool js::IsAsmJSFunction(JSFunction* fun) {
7188   return fun->kind() == FunctionFlags::AsmJS;
7189 }
7190 
IsAsmJSStrictModeModuleOrFunction(JSFunction * fun)7191 bool js::IsAsmJSStrictModeModuleOrFunction(JSFunction* fun) {
7192   if (IsAsmJSModule(fun)) {
7193     return AsmJSModuleFunctionToModule(fun).metadata().asAsmJS().strict;
7194   }
7195 
7196   if (IsAsmJSFunction(fun)) {
7197     return ExportedFunctionToInstance(fun).metadata().asAsmJS().strict;
7198   }
7199 
7200   return false;
7201 }
7202 
IsAsmJSCompilationAvailable(JSContext * cx)7203 bool js::IsAsmJSCompilationAvailable(JSContext* cx) {
7204   return cx->options().asmJS() && IsAsmJSCompilerAvailable(cx);
7205 }
7206 
IsAsmJSCompilationAvailable(JSContext * cx,unsigned argc,Value * vp)7207 bool js::IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, Value* vp) {
7208   CallArgs args = CallArgsFromVp(argc, vp);
7209   bool available = IsAsmJSCompilationAvailable(cx);
7210   args.rval().set(BooleanValue(available));
7211   return true;
7212 }
7213 
MaybeWrappedNativeFunction(const Value & v)7214 static JSFunction* MaybeWrappedNativeFunction(const Value& v) {
7215   if (!v.isObject()) {
7216     return nullptr;
7217   }
7218 
7219   return v.toObject().maybeUnwrapIf<JSFunction>();
7220 }
7221 
IsAsmJSModule(JSContext * cx,unsigned argc,Value * vp)7222 bool js::IsAsmJSModule(JSContext* cx, unsigned argc, Value* vp) {
7223   CallArgs args = CallArgsFromVp(argc, vp);
7224 
7225   bool rval = false;
7226   if (JSFunction* fun = MaybeWrappedNativeFunction(args.get(0))) {
7227     rval = IsAsmJSModule(fun);
7228   }
7229 
7230   args.rval().set(BooleanValue(rval));
7231   return true;
7232 }
7233 
IsAsmJSFunction(JSContext * cx,unsigned argc,Value * vp)7234 bool js::IsAsmJSFunction(JSContext* cx, unsigned argc, Value* vp) {
7235   CallArgs args = CallArgsFromVp(argc, vp);
7236 
7237   bool rval = false;
7238   if (JSFunction* fun = MaybeWrappedNativeFunction(args.get(0))) {
7239     rval = IsAsmJSFunction(fun);
7240   }
7241 
7242   args.rval().set(BooleanValue(rval));
7243   return true;
7244 }
7245 
7246 /*****************************************************************************/
7247 // asm.js toString/toSource support
7248 
AsmJSModuleToString(JSContext * cx,HandleFunction fun,bool isToSource)7249 JSString* js::AsmJSModuleToString(JSContext* cx, HandleFunction fun,
7250                                   bool isToSource) {
7251   MOZ_ASSERT(IsAsmJSModule(fun));
7252 
7253   const AsmJSMetadata& metadata =
7254       AsmJSModuleFunctionToModule(fun).metadata().asAsmJS();
7255   uint32_t begin = metadata.toStringStart;
7256   uint32_t end = metadata.srcEndAfterCurly();
7257   ScriptSource* source = metadata.maybeScriptSource();
7258 
7259   JSStringBuilder out(cx);
7260 
7261   if (isToSource && fun->isLambda() && !out.append("(")) {
7262     return nullptr;
7263   }
7264 
7265   bool haveSource;
7266   if (!ScriptSource::loadSource(cx, source, &haveSource)) {
7267     return nullptr;
7268   }
7269 
7270   if (!haveSource) {
7271     if (!out.append("function ")) {
7272       return nullptr;
7273     }
7274     if (fun->explicitName() && !out.append(fun->explicitName())) {
7275       return nullptr;
7276     }
7277     if (!out.append("() {\n    [native code]\n}")) {
7278       return nullptr;
7279     }
7280   } else {
7281     Rooted<JSLinearString*> src(cx, source->substring(cx, begin, end));
7282     if (!src) {
7283       return nullptr;
7284     }
7285 
7286     if (!out.append(src)) {
7287       return nullptr;
7288     }
7289   }
7290 
7291   if (isToSource && fun->isLambda() && !out.append(")")) {
7292     return nullptr;
7293   }
7294 
7295   return out.finishString();
7296 }
7297 
AsmJSFunctionToString(JSContext * cx,HandleFunction fun)7298 JSString* js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun) {
7299   MOZ_ASSERT(IsAsmJSFunction(fun));
7300 
7301   const AsmJSMetadata& metadata =
7302       ExportedFunctionToInstance(fun).metadata().asAsmJS();
7303   const AsmJSExport& f =
7304       metadata.lookupAsmJSExport(ExportedFunctionToFuncIndex(fun));
7305 
7306   uint32_t begin = metadata.srcStart + f.startOffsetInModule();
7307   uint32_t end = metadata.srcStart + f.endOffsetInModule();
7308 
7309   ScriptSource* source = metadata.maybeScriptSource();
7310   JSStringBuilder out(cx);
7311 
7312   if (!out.append("function ")) {
7313     return nullptr;
7314   }
7315 
7316   bool haveSource;
7317   if (!ScriptSource::loadSource(cx, source, &haveSource)) {
7318     return nullptr;
7319   }
7320 
7321   if (!haveSource) {
7322     // asm.js functions can't be anonymous
7323     MOZ_ASSERT(fun->explicitName());
7324     if (!out.append(fun->explicitName())) {
7325       return nullptr;
7326     }
7327     if (!out.append("() {\n    [native code]\n}")) {
7328       return nullptr;
7329     }
7330   } else {
7331     Rooted<JSLinearString*> src(cx, source->substring(cx, begin, end));
7332     if (!src) {
7333       return nullptr;
7334     }
7335     if (!out.append(src)) {
7336       return nullptr;
7337     }
7338   }
7339 
7340   return out.finishString();
7341 }
7342 
IsValidAsmJSHeapLength(size_t length)7343 bool js::IsValidAsmJSHeapLength(size_t length) {
7344   if (length < MinHeapLength) {
7345     return false;
7346   }
7347 
7348   // The heap length is limited by what a wasm memory32 can handle.
7349   if (length > MaxMemoryBytes(IndexType::I32)) {
7350     return false;
7351   }
7352 
7353   return wasm::IsValidARMImmediate(length);
7354 }
7355