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