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