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