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:
4 * Copyright 2014 Mozilla Foundation
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "wasm/AsmJS.h"
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/Try.h" // MOZ_TRY*
28 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
29 #include "mozilla/Variant.h"
36 #include "frontend/BytecodeCompiler.h" // CompileStandaloneFunction
37 #include "frontend/FrontendContext.h" // js::FrontendContext
38 #include "frontend/FunctionSyntaxKind.h" // FunctionSyntaxKind
39 #include "frontend/ParseNode.h"
40 #include "frontend/Parser-macros.h" // MOZ_TRY_*
41 #include "frontend/Parser.h"
42 #include "frontend/ParserAtom.h" // ParserAtomsTable, TaggedParserAtomIndex
43 #include "frontend/SharedContext.h" // TopLevelFunction
44 #include "frontend/TaggedParserAtomIndexHasher.h" // TaggedParserAtomIndexHasher
46 #include "gc/Policy.h"
47 #include "jit/InlinableNatives.h"
48 #include "js/BuildId.h" // JS::BuildIdCharVector
49 #include "js/experimental/JitInfo.h"
50 #include "js/friend/ErrorMessages.h" // JSMSG_*
51 #include "js/MemoryMetrics.h"
52 #include "js/Printf.h"
53 #include "js/ScalarType.h" // js::Scalar::Type
54 #include "js/SourceText.h"
55 #include "js/StableStringChars.h"
56 #include "js/Wrapper.h"
57 #include "util/DifferentialTesting.h"
58 #include "util/StringBuffer.h"
59 #include "util/Text.h"
60 #include "vm/ErrorReporting.h"
61 #include "vm/FunctionFlags.h" // js::FunctionFlags
62 #include "vm/GeneratorAndAsyncKind.h" // js::GeneratorKind, js::FunctionAsyncKind
63 #include "vm/Interpreter.h"
64 #include "vm/SelfHosting.h"
66 #include "vm/TypedArrayObject.h"
67 #include "vm/Warnings.h" // js::WarnNumberASCII
68 #include "wasm/WasmCompile.h"
69 #include "wasm/WasmFeatures.h"
70 #include "wasm/WasmGenerator.h"
71 #include "wasm/WasmInstance.h"
72 #include "wasm/WasmIonCompile.h"
73 #include "wasm/WasmJS.h"
74 #include "wasm/WasmModuleTypes.h"
75 #include "wasm/WasmSerialize.h"
76 #include "wasm/WasmSignalHandlers.h"
77 #include "wasm/WasmValidate.h"
79 #include "frontend/SharedContext-inl.h"
80 #include "vm/ArrayBufferObject-inl.h"
81 #include "vm/JSObject-inl.h"
82 #include "wasm/WasmInstance-inl.h"
85 using namespace js::frontend
;
86 using namespace js::jit
;
87 using namespace js::wasm
;
89 using JS::AsmJSOption
;
90 using JS::AutoStableStringChars
;
92 using JS::SourceOwnership
;
95 using mozilla::AsVariant
;
96 using mozilla::CeilingLog2
;
97 using mozilla::HashGeneric
;
98 using mozilla::IsNegativeZero
;
99 using mozilla::IsPositiveZero
;
100 using mozilla::IsPowerOfTwo
;
101 using mozilla::Nothing
;
102 using mozilla::PodZero
;
103 using mozilla::PositiveInfinity
;
105 using mozilla::Utf8Unit
;
106 using mozilla::Compression::LZ4
;
108 using FunctionVector
= JS::GCVector
<JSFunction
*>;
110 /*****************************************************************************/
112 // A wasm module can either use no memory, a unshared memory (ArrayBuffer) or
113 // shared memory (SharedArrayBuffer).
115 enum class MemoryUsage
{ None
= false, Unshared
= 1, Shared
= 2 };
117 // The asm.js valid heap lengths are precisely the WASM valid heap lengths for
118 // ARM greater or equal to MinHeapLength
119 static const size_t MinHeapLength
= PageSize
;
120 // An asm.js heap can in principle be up to INT32_MAX bytes but requirements
121 // on the format restrict it further to the largest pseudo-ARM-immediate.
122 // See IsValidAsmJSHeapLength().
123 static const uint64_t MaxHeapLength
= 0x7f000000;
125 static uint64_t RoundUpToNextValidAsmJSHeapLength(uint64_t length
) {
126 if (length
<= MinHeapLength
) {
127 return MinHeapLength
;
130 return wasm::RoundUpToNextValidARMImmediate(length
);
133 static uint64_t DivideRoundingUp(uint64_t a
, uint64_t b
) {
134 return (a
+ (b
- 1)) / b
;
137 /*****************************************************************************/
138 // asm.js module object
140 // The asm.js spec recognizes this set of builtin Math functions.
141 enum AsmJSMathBuiltinFunction
{
142 AsmJSMathBuiltin_sin
,
143 AsmJSMathBuiltin_cos
,
144 AsmJSMathBuiltin_tan
,
145 AsmJSMathBuiltin_asin
,
146 AsmJSMathBuiltin_acos
,
147 AsmJSMathBuiltin_atan
,
148 AsmJSMathBuiltin_ceil
,
149 AsmJSMathBuiltin_floor
,
150 AsmJSMathBuiltin_exp
,
151 AsmJSMathBuiltin_log
,
152 AsmJSMathBuiltin_pow
,
153 AsmJSMathBuiltin_sqrt
,
154 AsmJSMathBuiltin_abs
,
155 AsmJSMathBuiltin_atan2
,
156 AsmJSMathBuiltin_imul
,
157 AsmJSMathBuiltin_fround
,
158 AsmJSMathBuiltin_min
,
159 AsmJSMathBuiltin_max
,
160 AsmJSMathBuiltin_clz32
163 // LitValPOD is a restricted version of LitVal suitable for asm.js that is
167 PackedTypeCode valType_
;
175 LitValPOD() = default;
177 explicit LitValPOD(uint32_t u32
) : valType_(ValType(ValType::I32
).packed()) {
180 explicit LitValPOD(uint64_t u64
) : valType_(ValType(ValType::I64
).packed()) {
184 explicit LitValPOD(float f32
) : valType_(ValType(ValType::F32
).packed()) {
187 explicit LitValPOD(double f64
) : valType_(ValType(ValType::F64
).packed()) {
191 LitVal
asLitVal() const {
192 switch (valType_
.typeCode()) {
194 return LitVal(u
.u32_
);
196 return LitVal(u
.u64_
);
198 return LitVal(u
.f32_
);
200 return LitVal(u
.f64_
);
202 MOZ_CRASH("Can't happen");
207 static_assert(std::is_pod_v
<LitValPOD
>,
208 "must be POD to be simply serialized/deserialized");
210 // An AsmJSGlobal represents a JS global variable in the asm.js module function.
221 enum VarInitKind
{ InitConstant
, InitImport
};
222 enum ConstantKind
{ GlobalConstant
, MathConstant
};
225 struct CacheablePod
{
229 VarInitKind initKind_
;
231 PackedTypeCode importValType_
;
236 Scalar::Type viewType_
;
237 AsmJSMathBuiltinFunction mathBuiltinFunc_
;
244 CacheableChars field_
;
246 friend class ModuleValidatorShared
;
247 template <typename Unit
>
248 friend class ModuleValidator
;
251 AsmJSGlobal() = default;
252 AsmJSGlobal(Which which
, UniqueChars field
) {
253 mozilla::PodZero(&pod
); // zero padding for Valgrind
255 field_
= std::move(field
);
257 const char* field() const { return field_
.get(); }
258 Which
which() const { return pod
.which_
; }
259 VarInitKind
varInitKind() const {
260 MOZ_ASSERT(pod
.which_
== Variable
);
261 return pod
.u
.var
.initKind_
;
263 LitValPOD
varInitVal() const {
264 MOZ_ASSERT(pod
.which_
== Variable
);
265 MOZ_ASSERT(pod
.u
.var
.initKind_
== InitConstant
);
266 return pod
.u
.var
.u
.val_
;
268 ValType
varInitImportType() const {
269 MOZ_ASSERT(pod
.which_
== Variable
);
270 MOZ_ASSERT(pod
.u
.var
.initKind_
== InitImport
);
271 return ValType(pod
.u
.var
.u
.importValType_
);
273 uint32_t ffiIndex() const {
274 MOZ_ASSERT(pod
.which_
== FFI
);
275 return pod
.u
.ffiIndex_
;
277 // When a view is created from an imported constructor:
278 // var I32 = stdlib.Int32Array;
279 // var i32 = new I32(buffer);
280 // the second import has nothing to validate and thus has a null field.
281 Scalar::Type
viewType() const {
282 MOZ_ASSERT(pod
.which_
== ArrayView
|| pod
.which_
== ArrayViewCtor
);
283 return pod
.u
.viewType_
;
285 AsmJSMathBuiltinFunction
mathBuiltinFunction() const {
286 MOZ_ASSERT(pod
.which_
== MathBuiltinFunction
);
287 return pod
.u
.mathBuiltinFunc_
;
289 ConstantKind
constantKind() const {
290 MOZ_ASSERT(pod
.which_
== Constant
);
291 return pod
.u
.constant
.kind_
;
293 double constantValue() const {
294 MOZ_ASSERT(pod
.which_
== Constant
);
295 return pod
.u
.constant
.value_
;
299 using AsmJSGlobalVector
= Vector
<AsmJSGlobal
, 0, SystemAllocPolicy
>;
301 // An AsmJSImport is slightly different than an asm.js FFI function: a single
302 // asm.js FFI function can be called with many different signatures. When
303 // compiled to wasm, each unique FFI function paired with signature generates a
309 AsmJSImport() = default;
310 explicit AsmJSImport(uint32_t ffiIndex
) : ffiIndex_(ffiIndex
) {}
311 uint32_t ffiIndex() const { return ffiIndex_
; }
314 using AsmJSImportVector
= Vector
<AsmJSImport
, 0, SystemAllocPolicy
>;
316 // An AsmJSExport logically extends Export with the extra information needed for
317 // an asm.js exported function, viz., the offsets in module's source chars in
318 // case the function is toString()ed.
320 uint32_t funcIndex_
= 0;
322 // All fields are treated as cacheable POD:
323 uint32_t startOffsetInModule_
= 0; // Store module-start-relative offsets
324 uint32_t endOffsetInModule_
= 0; // so preserved by serialization.
327 AsmJSExport() = default;
328 AsmJSExport(uint32_t funcIndex
, uint32_t startOffsetInModule
,
329 uint32_t endOffsetInModule
)
330 : funcIndex_(funcIndex
),
331 startOffsetInModule_(startOffsetInModule
),
332 endOffsetInModule_(endOffsetInModule
) {}
333 uint32_t funcIndex() const { return funcIndex_
; }
334 uint32_t startOffsetInModule() const { return startOffsetInModule_
; }
335 uint32_t endOffsetInModule() const { return endOffsetInModule_
; }
338 using AsmJSExportVector
= Vector
<AsmJSExport
, 0, SystemAllocPolicy
>;
340 // Holds the immutable guts of an AsmJSModule.
342 // AsmJSMetadata is built incrementally by ModuleValidator and then shared
343 // immutably between AsmJSModules.
345 struct AsmJSMetadataCacheablePod
{
346 uint32_t numFFIs
= 0;
347 uint32_t srcLength
= 0;
348 uint32_t srcLengthWithRightBrace
= 0;
350 AsmJSMetadataCacheablePod() = default;
353 struct js::AsmJSMetadata
: Metadata
, AsmJSMetadataCacheablePod
{
354 AsmJSGlobalVector asmJSGlobals
;
355 AsmJSImportVector asmJSImports
;
356 AsmJSExportVector asmJSExports
;
357 CacheableCharsVector asmJSFuncNames
;
358 CacheableChars globalArgumentName
;
359 CacheableChars importArgumentName
;
360 CacheableChars bufferArgumentName
;
362 // These values are not serialized since they are relative to the
363 // containing script which can be different between serialization and
364 // deserialization contexts. Thus, they must be set explicitly using the
365 // ambient Parser/ScriptSource after deserialization.
367 // srcStart refers to the offset in the ScriptSource to the beginning of
368 // the asm.js module function. If the function has been created with the
369 // Function constructor, this will be the first character in the function
370 // source. Otherwise, it will be the opening parenthesis of the arguments
372 uint32_t toStringStart
;
375 bool alwaysUseFdlibm
= false;
376 RefPtr
<ScriptSource
> source
;
378 uint32_t srcEndBeforeCurly() const { return srcStart
+ srcLength
; }
379 uint32_t srcEndAfterCurly() const {
380 return srcStart
+ srcLengthWithRightBrace
;
384 : Metadata(ModuleKind::AsmJS
),
388 ~AsmJSMetadata() override
= default;
390 const AsmJSExport
& lookupAsmJSExport(uint32_t funcIndex
) const {
391 // The AsmJSExportVector isn't stored in sorted order so do a linear
392 // search. This is for the super-cold and already-expensive toString()
393 // path and the number of exports is generally small.
394 for (const AsmJSExport
& exp
: asmJSExports
) {
395 if (exp
.funcIndex() == funcIndex
) {
399 MOZ_CRASH("missing asm.js func export");
402 bool mutedErrors() const override
{ return source
->mutedErrors(); }
403 const char16_t
* displayURL() const override
{
404 return source
->hasDisplayURL() ? source
->displayURL() : nullptr;
406 ScriptSource
* maybeScriptSource() const override
{ return source
.get(); }
407 bool getFuncName(NameContext ctx
, uint32_t funcIndex
,
408 UTF8Bytes
* name
) const override
{
409 const char* p
= asmJSFuncNames
[funcIndex
].get();
413 return name
->append(p
, strlen(p
));
416 AsmJSMetadataCacheablePod
& pod() { return *this; }
417 const AsmJSMetadataCacheablePod
& pod() const { return *this; }
420 using MutableAsmJSMetadata
= RefPtr
<AsmJSMetadata
>;
422 /*****************************************************************************/
423 // ParseNode utilities
425 static inline ParseNode
* NextNode(ParseNode
* pn
) { return pn
->pn_next
; }
427 static inline ParseNode
* UnaryKid(ParseNode
* pn
) {
428 return pn
->as
<UnaryNode
>().kid();
431 static inline ParseNode
* BinaryRight(ParseNode
* pn
) {
432 return pn
->as
<BinaryNode
>().right();
435 static inline ParseNode
* BinaryLeft(ParseNode
* pn
) {
436 return pn
->as
<BinaryNode
>().left();
439 static inline ParseNode
* ReturnExpr(ParseNode
* pn
) {
440 MOZ_ASSERT(pn
->isKind(ParseNodeKind::ReturnStmt
));
444 static inline ParseNode
* TernaryKid1(ParseNode
* pn
) {
445 return pn
->as
<TernaryNode
>().kid1();
448 static inline ParseNode
* TernaryKid2(ParseNode
* pn
) {
449 return pn
->as
<TernaryNode
>().kid2();
452 static inline ParseNode
* TernaryKid3(ParseNode
* pn
) {
453 return pn
->as
<TernaryNode
>().kid3();
456 static inline ParseNode
* ListHead(ParseNode
* pn
) {
457 return pn
->as
<ListNode
>().head();
460 static inline unsigned ListLength(ParseNode
* pn
) {
461 return pn
->as
<ListNode
>().count();
464 static inline ParseNode
* CallCallee(ParseNode
* pn
) {
465 MOZ_ASSERT(pn
->isKind(ParseNodeKind::CallExpr
));
466 return BinaryLeft(pn
);
469 static inline unsigned CallArgListLength(ParseNode
* pn
) {
470 MOZ_ASSERT(pn
->isKind(ParseNodeKind::CallExpr
));
471 return ListLength(BinaryRight(pn
));
474 static inline ParseNode
* CallArgList(ParseNode
* pn
) {
475 MOZ_ASSERT(pn
->isKind(ParseNodeKind::CallExpr
));
476 return ListHead(BinaryRight(pn
));
479 static inline ParseNode
* VarListHead(ParseNode
* pn
) {
480 MOZ_ASSERT(pn
->isKind(ParseNodeKind::VarStmt
) ||
481 pn
->isKind(ParseNodeKind::ConstDecl
));
485 static inline bool IsDefaultCase(ParseNode
* pn
) {
486 return pn
->as
<CaseClause
>().isDefault();
489 static inline ParseNode
* CaseExpr(ParseNode
* pn
) {
490 return pn
->as
<CaseClause
>().caseExpression();
493 static inline ParseNode
* CaseBody(ParseNode
* pn
) {
494 return pn
->as
<CaseClause
>().statementList();
497 static inline ParseNode
* BinaryOpLeft(ParseNode
* pn
) {
498 MOZ_ASSERT(pn
->isBinaryOperation());
499 MOZ_ASSERT(pn
->as
<ListNode
>().count() == 2);
503 static inline ParseNode
* BinaryOpRight(ParseNode
* pn
) {
504 MOZ_ASSERT(pn
->isBinaryOperation());
505 MOZ_ASSERT(pn
->as
<ListNode
>().count() == 2);
506 return NextNode(ListHead(pn
));
509 static inline ParseNode
* BitwiseLeft(ParseNode
* pn
) { return BinaryOpLeft(pn
); }
511 static inline ParseNode
* BitwiseRight(ParseNode
* pn
) {
512 return BinaryOpRight(pn
);
515 static inline ParseNode
* MultiplyLeft(ParseNode
* pn
) {
516 MOZ_ASSERT(pn
->isKind(ParseNodeKind::MulExpr
));
517 return BinaryOpLeft(pn
);
520 static inline ParseNode
* MultiplyRight(ParseNode
* pn
) {
521 MOZ_ASSERT(pn
->isKind(ParseNodeKind::MulExpr
));
522 return BinaryOpRight(pn
);
525 static inline ParseNode
* AddSubLeft(ParseNode
* pn
) {
526 MOZ_ASSERT(pn
->isKind(ParseNodeKind::AddExpr
) ||
527 pn
->isKind(ParseNodeKind::SubExpr
));
528 return BinaryOpLeft(pn
);
531 static inline ParseNode
* AddSubRight(ParseNode
* pn
) {
532 MOZ_ASSERT(pn
->isKind(ParseNodeKind::AddExpr
) ||
533 pn
->isKind(ParseNodeKind::SubExpr
));
534 return BinaryOpRight(pn
);
537 static inline ParseNode
* DivOrModLeft(ParseNode
* pn
) {
538 MOZ_ASSERT(pn
->isKind(ParseNodeKind::DivExpr
) ||
539 pn
->isKind(ParseNodeKind::ModExpr
));
540 return BinaryOpLeft(pn
);
543 static inline ParseNode
* DivOrModRight(ParseNode
* pn
) {
544 MOZ_ASSERT(pn
->isKind(ParseNodeKind::DivExpr
) ||
545 pn
->isKind(ParseNodeKind::ModExpr
));
546 return BinaryOpRight(pn
);
549 static inline ParseNode
* ComparisonLeft(ParseNode
* pn
) {
550 return BinaryOpLeft(pn
);
553 static inline ParseNode
* ComparisonRight(ParseNode
* pn
) {
554 return BinaryOpRight(pn
);
557 static inline bool IsExpressionStatement(ParseNode
* pn
) {
558 return pn
->isKind(ParseNodeKind::ExpressionStmt
);
561 static inline ParseNode
* ExpressionStatementExpr(ParseNode
* pn
) {
562 MOZ_ASSERT(pn
->isKind(ParseNodeKind::ExpressionStmt
));
566 static inline TaggedParserAtomIndex
LoopControlMaybeLabel(ParseNode
* pn
) {
567 MOZ_ASSERT(pn
->isKind(ParseNodeKind::BreakStmt
) ||
568 pn
->isKind(ParseNodeKind::ContinueStmt
));
569 return pn
->as
<LoopControlStatement
>().label();
572 static inline TaggedParserAtomIndex
LabeledStatementLabel(ParseNode
* pn
) {
573 return pn
->as
<LabeledStatement
>().label();
576 static inline ParseNode
* LabeledStatementStatement(ParseNode
* pn
) {
577 return pn
->as
<LabeledStatement
>().statement();
580 static double NumberNodeValue(ParseNode
* pn
) {
581 return pn
->as
<NumericLiteral
>().value();
584 static bool NumberNodeHasFrac(ParseNode
* pn
) {
585 return pn
->as
<NumericLiteral
>().decimalPoint() == HasDecimal
;
588 static ParseNode
* DotBase(ParseNode
* pn
) {
589 return &pn
->as
<PropertyAccess
>().expression();
592 static TaggedParserAtomIndex
DotMember(ParseNode
* pn
) {
593 return pn
->as
<PropertyAccess
>().name();
596 static ParseNode
* ElemBase(ParseNode
* pn
) {
597 return &pn
->as
<PropertyByValue
>().expression();
600 static ParseNode
* ElemIndex(ParseNode
* pn
) {
601 return &pn
->as
<PropertyByValue
>().key();
604 static inline TaggedParserAtomIndex
FunctionName(FunctionNode
* funNode
) {
605 if (auto name
= funNode
->funbox()->explicitName()) {
608 return TaggedParserAtomIndex::null();
611 static inline ParseNode
* FunctionFormalParametersList(FunctionNode
* fn
,
612 unsigned* numFormals
) {
613 ParamsBodyNode
* argsBody
= fn
->body();
615 // The number of formals is equal to the number of parameters (excluding the
616 // trailing lexical scope). There are no destructuring or rest parameters for
618 *numFormals
= argsBody
->count();
620 // If the function has been fully parsed, the trailing function body node is a
621 // lexical scope. If we've only parsed the function parameters, the last node
622 // is the last parameter.
623 if (*numFormals
> 0 && argsBody
->last()->is
<LexicalScopeNode
>()) {
624 MOZ_ASSERT(argsBody
->last()->as
<LexicalScopeNode
>().scopeBody()->isKind(
625 ParseNodeKind::StatementList
));
629 return argsBody
->head();
632 static inline ParseNode
* FunctionStatementList(FunctionNode
* funNode
) {
633 LexicalScopeNode
* last
= funNode
->body()->body();
634 MOZ_ASSERT(last
->isEmptyScope());
635 ParseNode
* body
= last
->scopeBody();
636 MOZ_ASSERT(body
->isKind(ParseNodeKind::StatementList
));
640 static inline bool IsNormalObjectField(ParseNode
* pn
) {
641 return pn
->isKind(ParseNodeKind::PropertyDefinition
) &&
642 pn
->as
<PropertyDefinition
>().accessorType() == AccessorType::None
&&
643 BinaryLeft(pn
)->isKind(ParseNodeKind::ObjectPropertyName
);
646 static inline TaggedParserAtomIndex
ObjectNormalFieldName(ParseNode
* pn
) {
647 MOZ_ASSERT(IsNormalObjectField(pn
));
648 MOZ_ASSERT(BinaryLeft(pn
)->isKind(ParseNodeKind::ObjectPropertyName
));
649 return BinaryLeft(pn
)->as
<NameNode
>().atom();
652 static inline ParseNode
* ObjectNormalFieldInitializer(ParseNode
* pn
) {
653 MOZ_ASSERT(IsNormalObjectField(pn
));
654 return BinaryRight(pn
);
657 static inline bool IsUseOfName(ParseNode
* pn
, TaggedParserAtomIndex name
) {
658 return pn
->isName(name
);
661 static inline bool IsIgnoredDirectiveName(TaggedParserAtomIndex atom
) {
662 return atom
!= TaggedParserAtomIndex::WellKnown::use_strict_();
665 static inline bool IsIgnoredDirective(ParseNode
* pn
) {
666 return pn
->isKind(ParseNodeKind::ExpressionStmt
) &&
667 UnaryKid(pn
)->isKind(ParseNodeKind::StringExpr
) &&
668 IsIgnoredDirectiveName(UnaryKid(pn
)->as
<NameNode
>().atom());
671 static inline bool IsEmptyStatement(ParseNode
* pn
) {
672 return pn
->isKind(ParseNodeKind::EmptyStmt
);
675 static inline ParseNode
* SkipEmptyStatements(ParseNode
* pn
) {
676 while (pn
&& IsEmptyStatement(pn
)) {
682 static inline ParseNode
* NextNonEmptyStatement(ParseNode
* pn
) {
683 return SkipEmptyStatements(pn
->pn_next
);
686 template <typename Unit
>
687 static bool GetToken(AsmJSParser
<Unit
>& parser
, TokenKind
* tkp
) {
688 auto& ts
= parser
.tokenStream
;
691 if (!ts
.getToken(&tk
, TokenStreamShared::SlashIsRegExp
)) {
694 if (tk
!= TokenKind::Semi
) {
702 template <typename Unit
>
703 static bool PeekToken(AsmJSParser
<Unit
>& parser
, TokenKind
* tkp
) {
704 auto& ts
= parser
.tokenStream
;
707 if (!ts
.peekToken(&tk
, TokenStream::SlashIsRegExp
)) {
710 if (tk
!= TokenKind::Semi
) {
713 ts
.consumeKnownToken(TokenKind::Semi
, TokenStreamShared::SlashIsRegExp
);
719 template <typename Unit
>
720 static bool ParseVarOrConstStatement(AsmJSParser
<Unit
>& parser
,
723 if (!PeekToken(parser
, &tk
)) {
726 if (tk
!= TokenKind::Var
&& tk
!= TokenKind::Const
) {
731 MOZ_TRY_VAR_OR_RETURN(*var
, parser
.statementListItem(YieldIsName
), false);
733 MOZ_ASSERT((*var
)->isKind(ParseNodeKind::VarStmt
) ||
734 (*var
)->isKind(ParseNodeKind::ConstDecl
));
738 /*****************************************************************************/
740 // Represents the type and value of an asm.js numeric literal.
742 // A literal is a double iff the literal contains a decimal point (even if the
743 // fractional part is 0). Otherwise, integers may be classified:
745 // negative int: [-2^31, 0)
746 // big unsigned: [2^31, 2^32)
747 // out of range: otherwise
748 // Lastly, a literal may be a float literal which is any double or integer
749 // literal coerced with Math.fround.
768 NumLit(Which w
, const Value
& v
) : which_(w
), value_(v
) {}
770 Which
which() const { return which_
; }
772 int32_t toInt32() const {
773 MOZ_ASSERT(which_
== Fixnum
|| which_
== NegativeInt
||
774 which_
== BigUnsigned
);
775 return value_
.toInt32();
778 uint32_t toUint32() const { return (uint32_t)toInt32(); }
780 double toDouble() const {
781 MOZ_ASSERT(which_
== Double
);
782 return value_
.toDouble();
785 float toFloat() const {
786 MOZ_ASSERT(which_
== Float
);
787 return float(value_
.toDouble());
790 Value
scalarValue() const {
791 MOZ_ASSERT(which_
!= OutOfRangeInt
);
795 bool valid() const { return which_
!= OutOfRangeInt
; }
797 bool isZeroBits() const {
801 case NumLit::NegativeInt
:
802 case NumLit::BigUnsigned
:
803 return toInt32() == 0;
805 return IsPositiveZero(toDouble());
807 return IsPositiveZero(toFloat());
808 case NumLit::OutOfRangeInt
:
809 MOZ_CRASH("can't be here because of valid() check above");
814 LitValPOD
value() const {
817 case NumLit::NegativeInt
:
818 case NumLit::BigUnsigned
:
819 return LitValPOD(toUint32());
821 return LitValPOD(toFloat());
823 return LitValPOD(toDouble());
824 case NumLit::OutOfRangeInt
:;
826 MOZ_CRASH("bad literal");
830 // Represents the type of a general asm.js expression.
832 // A canonical subset of types representing the coercion targets: Int, Float,
835 // Void is also part of the canonical subset.
840 Fixnum
= NumLit::Fixnum
,
841 Signed
= NumLit::NegativeInt
,
842 Unsigned
= NumLit::BigUnsigned
,
843 DoubleLit
= NumLit::Double
,
844 Float
= NumLit::Float
,
859 MOZ_IMPLICIT
Type(Which w
) : which_(w
) {}
861 // Map an already canonicalized Type to the return type of a function call.
862 static Type
ret(Type t
) {
863 MOZ_ASSERT(t
.isCanonical());
864 // The 32-bit external type is Signed, not Int.
865 return t
.isInt() ? Signed
: t
;
868 static Type
lit(const NumLit
& lit
) {
869 MOZ_ASSERT(lit
.valid());
870 Which which
= Type::Which(lit
.which());
871 MOZ_ASSERT(which
>= Fixnum
&& which
<= Float
);
877 // Map |t| to one of the canonical vartype representations of a
879 static Type
canonicalize(Type t
) {
901 // These types need some kind of coercion, they can't be mapped
905 MOZ_CRASH("Invalid vartype");
908 Which
which() const { return which_
; }
910 bool operator==(Type rhs
) const { return which_
== rhs
.which_
; }
911 bool operator!=(Type rhs
) const { return which_
!= rhs
.which_
; }
913 bool operator<=(Type rhs
) const {
914 switch (rhs
.which_
) {
920 return isDoubleLit();
926 return isMaybeDouble();
928 return isMaybeFloat();
940 MOZ_CRASH("unexpected rhs type");
943 bool isFixnum() const { return which_
== Fixnum
; }
945 bool isSigned() const { return which_
== Signed
|| which_
== Fixnum
; }
947 bool isUnsigned() const { return which_
== Unsigned
|| which_
== Fixnum
; }
949 bool isInt() const { return isSigned() || isUnsigned() || which_
== Int
; }
951 bool isIntish() const { return isInt() || which_
== Intish
; }
953 bool isDoubleLit() const { return which_
== DoubleLit
; }
955 bool isDouble() const { return isDoubleLit() || which_
== Double
; }
957 bool isMaybeDouble() const { return isDouble() || which_
== MaybeDouble
; }
959 bool isFloat() const { return which_
== Float
; }
961 bool isMaybeFloat() const { return isFloat() || which_
== MaybeFloat
; }
963 bool isFloatish() const { return isMaybeFloat() || which_
== Floatish
; }
965 bool isVoid() const { return which_
== Void
; }
967 bool isExtern() const { return isDouble() || isSigned(); }
969 // Check if this is one of the valid types for a function argument.
970 bool isArgType() const { return isInt() || isFloat() || isDouble(); }
972 // Check if this is one of the valid types for a function return value.
973 bool isReturnType() const {
974 return isSigned() || isFloat() || isDouble() || isVoid();
977 // Check if this is one of the valid types for a global variable.
978 bool isGlobalVarType() const { return isArgType(); }
980 // Check if this is one of the canonical vartype representations of a
981 // wasm::ValType, or is void. See Type::canonicalize().
982 bool isCanonical() const {
994 // Check if this is a canonical representation of a wasm::ValType.
995 bool isCanonicalValType() const { return !isVoid() && isCanonical(); }
997 // Convert this canonical type to a wasm::ValType.
998 ValType
canonicalToValType() const {
1001 return ValType::I32
;
1003 return ValType::F32
;
1005 return ValType::F64
;
1007 MOZ_CRASH("Need canonical type");
1011 Maybe
<ValType
> canonicalToReturnType() const {
1012 return isVoid() ? Nothing() : Some(canonicalToValType());
1015 // Convert this type to a wasm::TypeCode for use in a wasm
1016 // block signature. This works for all types, including non-canonical
1017 // ones. Consequently, the type isn't valid for subsequent asm.js
1018 // validation; it's only valid for use in producing wasm.
1019 TypeCode
toWasmBlockSignatureType() const {
1026 return TypeCode::I32
;
1031 return TypeCode::F32
;
1036 return TypeCode::F64
;
1039 return TypeCode::BlockVoid
;
1041 MOZ_CRASH("Invalid Type");
1044 const char* toChars() const {
1071 MOZ_CRASH("Invalid Type");
1075 static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE
= 4 * 1024;
1077 class MOZ_STACK_CLASS ModuleValidatorShared
{
1083 uint64_t minPages() const { return DivideRoundingUp(minLength
, PageSize
); }
1089 TaggedParserAtomIndex name_
;
1092 uint32_t funcDefIndex_
;
1096 // Available when defined:
1101 Uint32Vector callSiteLineNums_
;
1104 Func(TaggedParserAtomIndex name
, uint32_t sigIndex
, uint32_t firstUse
,
1105 uint32_t funcDefIndex
)
1107 sigIndex_(sigIndex
),
1108 firstUse_(firstUse
),
1109 funcDefIndex_(funcDefIndex
),
1115 TaggedParserAtomIndex
name() const { return name_
; }
1116 uint32_t sigIndex() const { return sigIndex_
; }
1117 uint32_t firstUse() const { return firstUse_
; }
1118 bool defined() const { return defined_
; }
1119 uint32_t funcDefIndex() const { return funcDefIndex_
; }
1121 void define(ParseNode
* fn
, uint32_t line
, Bytes
&& bytes
,
1122 Uint32Vector
&& callSiteLineNums
) {
1123 MOZ_ASSERT(!defined_
);
1125 srcBegin_
= fn
->pn_pos
.begin
;
1126 srcEnd_
= fn
->pn_pos
.end
;
1128 bytes_
= std::move(bytes
);
1129 callSiteLineNums_
= std::move(callSiteLineNums
);
1132 uint32_t srcBegin() const {
1133 MOZ_ASSERT(defined_
);
1136 uint32_t srcEnd() const {
1137 MOZ_ASSERT(defined_
);
1140 uint32_t line() const {
1141 MOZ_ASSERT(defined_
);
1144 const Bytes
& bytes() const {
1145 MOZ_ASSERT(defined_
);
1148 Uint32Vector
& callSiteLineNums() {
1149 MOZ_ASSERT(defined_
);
1150 return callSiteLineNums_
;
1154 using ConstFuncVector
= Vector
<const Func
*>;
1155 using FuncVector
= Vector
<Func
>;
1159 TaggedParserAtomIndex name_
;
1165 Table(uint32_t sigIndex
, TaggedParserAtomIndex name
, uint32_t firstUse
,
1167 : sigIndex_(sigIndex
),
1169 firstUse_(firstUse
),
1173 Table(Table
&& rhs
) = delete;
1175 uint32_t sigIndex() const { return sigIndex_
; }
1176 TaggedParserAtomIndex
name() const { return name_
; }
1177 uint32_t firstUse() const { return firstUse_
; }
1178 unsigned mask() const { return mask_
; }
1179 bool defined() const { return defined_
; }
1181 MOZ_ASSERT(!defined_
);
1186 using TableVector
= Vector
<Table
*>;
1208 NumLit literalValue_
;
1210 VarOrConst(unsigned index
, const NumLit
& lit
)
1211 : type_(Type::lit(lit
).which()),
1213 literalValue_(lit
) // copies |lit|
1216 VarOrConst(unsigned index
, Type::Which which
)
1217 : type_(which
), index_(index
) {
1218 // The |literalValue_| field remains unused and
1219 // uninitialized for non-constant variables.
1222 explicit VarOrConst(double constant
)
1223 : type_(Type::Double
),
1224 literalValue_(NumLit::Double
, DoubleValue(constant
)) {
1225 // The index_ field is unused and uninitialized for
1226 // constant doubles.
1229 uint32_t funcDefIndex_
;
1230 uint32_t tableIndex_
;
1232 Scalar::Type viewType_
;
1233 AsmJSMathBuiltinFunction mathBuiltinFunc_
;
1235 // |varOrConst|, through |varOrConst.literalValue_|, has a
1236 // non-trivial constructor and therefore MUST be placement-new'd
1238 MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
1239 U() : funcDefIndex_(0) {}
1240 MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
1243 friend class ModuleValidatorShared
;
1244 template <typename Unit
>
1245 friend class ModuleValidator
;
1246 friend class js::LifoAlloc
;
1248 explicit Global(Which which
) : which_(which
) {}
1251 Which
which() const { return which_
; }
1252 Type
varOrConstType() const {
1253 MOZ_ASSERT(which_
== Variable
|| which_
== ConstantLiteral
||
1254 which_
== ConstantImport
);
1255 return u
.varOrConst
.type_
;
1257 unsigned varOrConstIndex() const {
1258 MOZ_ASSERT(which_
== Variable
|| which_
== ConstantImport
);
1259 return u
.varOrConst
.index_
;
1261 bool isConst() const {
1262 return which_
== ConstantLiteral
|| which_
== ConstantImport
;
1264 NumLit
constLiteralValue() const {
1265 MOZ_ASSERT(which_
== ConstantLiteral
);
1266 return u
.varOrConst
.literalValue_
;
1268 uint32_t funcDefIndex() const {
1269 MOZ_ASSERT(which_
== Function
);
1270 return u
.funcDefIndex_
;
1272 uint32_t tableIndex() const {
1273 MOZ_ASSERT(which_
== Table
);
1274 return u
.tableIndex_
;
1276 unsigned ffiIndex() const {
1277 MOZ_ASSERT(which_
== FFI
);
1280 Scalar::Type
viewType() const {
1281 MOZ_ASSERT(which_
== ArrayView
|| which_
== ArrayViewCtor
);
1284 bool isMathFunction() const { return which_
== MathBuiltinFunction
; }
1285 AsmJSMathBuiltinFunction
mathBuiltinFunction() const {
1286 MOZ_ASSERT(which_
== MathBuiltinFunction
);
1287 return u
.mathBuiltinFunc_
;
1291 struct MathBuiltin
{
1292 enum Kind
{ Function
, Constant
};
1297 AsmJSMathBuiltinFunction func
;
1300 MathBuiltin() : kind(Kind(-1)), u
{} {}
1301 explicit MathBuiltin(double cst
) : kind(Constant
) { u
.cst
= cst
; }
1302 explicit MathBuiltin(AsmJSMathBuiltinFunction func
) : kind(Function
) {
1308 ArrayView(TaggedParserAtomIndex name
, Scalar::Type type
)
1309 : name(name
), type(type
) {}
1311 TaggedParserAtomIndex name
;
1318 const TypeContext
& types_
;
1321 HashableSig(uint32_t sigIndex
, const TypeContext
& types
)
1322 : sigIndex_(sigIndex
), types_(types
) {}
1323 uint32_t sigIndex() const { return sigIndex_
; }
1324 const FuncType
& funcType() const { return types_
[sigIndex_
].funcType(); }
1326 // Implement HashPolicy:
1327 using Lookup
= const FuncType
&;
1328 static HashNumber
hash(Lookup l
) { return l
.hash(nullptr); }
1329 static bool match(HashableSig lhs
, Lookup rhs
) {
1330 return FuncType::strictlyEquals(lhs
.funcType(), rhs
);
1334 class NamedSig
: public HashableSig
{
1335 TaggedParserAtomIndex name_
;
1338 NamedSig(TaggedParserAtomIndex name
, uint32_t sigIndex
,
1339 const TypeContext
& types
)
1340 : HashableSig(sigIndex
, types
), name_(name
) {}
1341 TaggedParserAtomIndex
name() const { return name_
; }
1343 // Implement HashPolicy:
1345 TaggedParserAtomIndex name
;
1346 const FuncType
& funcType
;
1347 Lookup(TaggedParserAtomIndex name
, const FuncType
& funcType
)
1348 : name(name
), funcType(funcType
) {}
1350 static HashNumber
hash(Lookup l
) {
1351 return HashGeneric(TaggedParserAtomIndexHasher::hash(l
.name
),
1352 l
.funcType
.hash(nullptr));
1354 static bool match(NamedSig lhs
, Lookup rhs
) {
1355 return lhs
.name() == rhs
.name
&&
1356 FuncType::strictlyEquals(lhs
.funcType(), rhs
.funcType
);
1360 using SigSet
= HashSet
<HashableSig
, HashableSig
>;
1361 using FuncImportMap
= HashMap
<NamedSig
, uint32_t, NamedSig
>;
1363 HashMap
<TaggedParserAtomIndex
, Global
*, TaggedParserAtomIndexHasher
>;
1365 HashMap
<TaggedParserAtomIndex
, MathBuiltin
, TaggedParserAtomIndexHasher
>;
1366 using ArrayViewVector
= Vector
<ArrayView
>;
1369 FrontendContext
* fc_
;
1370 ParserAtomsTable
& parserAtoms_
;
1371 FunctionNode
* moduleFunctionNode_
;
1372 TaggedParserAtomIndex moduleFunctionName_
;
1373 TaggedParserAtomIndex globalArgumentName_
;
1374 TaggedParserAtomIndex importArgumentName_
;
1375 TaggedParserAtomIndex bufferArgumentName_
;
1376 MathNameMap standardLibraryMathNames_
;
1378 // Validation-internal state:
1379 LifoAlloc validationLifo_
;
1381 FuncVector funcDefs_
;
1382 TableVector tables_
;
1383 GlobalMap globalMap_
;
1385 FuncImportMap funcImportMap_
;
1386 ArrayViewVector arrayViews_
;
1388 // State used to build the AsmJSModule in finish():
1389 CompilerEnvironment compilerEnv_
;
1390 ModuleEnvironment moduleEnv_
;
1391 MutableAsmJSMetadata asmJSMetadata_
;
1394 UniqueChars errorString_
= nullptr;
1395 uint32_t errorOffset_
= UINT32_MAX
;
1396 bool errorOverRecursed_
= false;
1399 ModuleValidatorShared(FrontendContext
* fc
, ParserAtomsTable
& parserAtoms
,
1400 FunctionNode
* moduleFunctionNode
)
1402 parserAtoms_(parserAtoms
),
1403 moduleFunctionNode_(moduleFunctionNode
),
1404 moduleFunctionName_(FunctionName(moduleFunctionNode
)),
1405 standardLibraryMathNames_(fc
),
1406 validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE
),
1413 compilerEnv_(CompileMode::Once
, Tier::Optimized
, DebugEnabled::False
),
1414 moduleEnv_(FeatureArgs(), ModuleKind::AsmJS
) {
1415 compilerEnv_
.computeParameters();
1416 memory_
.minLength
= RoundUpToNextValidAsmJSHeapLength(0);
1420 [[nodiscard
]] bool initModuleEnvironment() { return moduleEnv_
.init(); }
1422 [[nodiscard
]] bool addStandardLibraryMathInfo() {
1423 static constexpr struct {
1425 AsmJSMathBuiltinFunction func
;
1427 {"sin", AsmJSMathBuiltin_sin
}, {"cos", AsmJSMathBuiltin_cos
},
1428 {"tan", AsmJSMathBuiltin_tan
}, {"asin", AsmJSMathBuiltin_asin
},
1429 {"acos", AsmJSMathBuiltin_acos
}, {"atan", AsmJSMathBuiltin_atan
},
1430 {"ceil", AsmJSMathBuiltin_ceil
}, {"floor", AsmJSMathBuiltin_floor
},
1431 {"exp", AsmJSMathBuiltin_exp
}, {"log", AsmJSMathBuiltin_log
},
1432 {"pow", AsmJSMathBuiltin_pow
}, {"sqrt", AsmJSMathBuiltin_sqrt
},
1433 {"abs", AsmJSMathBuiltin_abs
}, {"atan2", AsmJSMathBuiltin_atan2
},
1434 {"imul", AsmJSMathBuiltin_imul
}, {"clz32", AsmJSMathBuiltin_clz32
},
1435 {"fround", AsmJSMathBuiltin_fround
}, {"min", AsmJSMathBuiltin_min
},
1436 {"max", AsmJSMathBuiltin_max
},
1439 auto AddMathFunction
= [this](const char* name
,
1440 AsmJSMathBuiltinFunction func
) {
1441 auto atom
= parserAtoms_
.internAscii(fc_
, name
, strlen(name
));
1445 MathBuiltin
builtin(func
);
1446 return this->standardLibraryMathNames_
.putNew(atom
, builtin
);
1449 for (const auto& info
: functions
) {
1450 if (!AddMathFunction(info
.name
, info
.func
)) {
1455 static constexpr struct {
1463 {"LOG10E", M_LOG10E
},
1465 {"SQRT1_2", M_SQRT1_2
},
1469 auto AddMathConstant
= [this](const char* name
, double cst
) {
1470 auto atom
= parserAtoms_
.internAscii(fc_
, name
, strlen(name
));
1474 MathBuiltin
builtin(cst
);
1475 return this->standardLibraryMathNames_
.putNew(atom
, builtin
);
1478 for (const auto& info
: constants
) {
1479 if (!AddMathConstant(info
.name
, info
.value
)) {
1488 FrontendContext
* fc() const { return fc_
; }
1489 TaggedParserAtomIndex
moduleFunctionName() const {
1490 return moduleFunctionName_
;
1492 TaggedParserAtomIndex
globalArgumentName() const {
1493 return globalArgumentName_
;
1495 TaggedParserAtomIndex
importArgumentName() const {
1496 return importArgumentName_
;
1498 TaggedParserAtomIndex
bufferArgumentName() const {
1499 return bufferArgumentName_
;
1501 const ModuleEnvironment
& env() { return moduleEnv_
; }
1503 void initModuleFunctionName(TaggedParserAtomIndex name
) {
1504 MOZ_ASSERT(!moduleFunctionName_
);
1505 moduleFunctionName_
= name
;
1507 [[nodiscard
]] bool initGlobalArgumentName(TaggedParserAtomIndex n
) {
1508 globalArgumentName_
= n
;
1510 asmJSMetadata_
->globalArgumentName
= parserAtoms_
.toNewUTF8CharsZ(fc_
, n
);
1511 if (!asmJSMetadata_
->globalArgumentName
) {
1517 [[nodiscard
]] bool initImportArgumentName(TaggedParserAtomIndex n
) {
1518 importArgumentName_
= n
;
1520 asmJSMetadata_
->importArgumentName
= parserAtoms_
.toNewUTF8CharsZ(fc_
, n
);
1521 if (!asmJSMetadata_
->importArgumentName
) {
1527 [[nodiscard
]] bool initBufferArgumentName(TaggedParserAtomIndex n
) {
1528 bufferArgumentName_
= n
;
1530 asmJSMetadata_
->bufferArgumentName
= parserAtoms_
.toNewUTF8CharsZ(fc_
, n
);
1531 if (!asmJSMetadata_
->bufferArgumentName
) {
1537 bool addGlobalVarInit(TaggedParserAtomIndex var
, const NumLit
& lit
, Type type
,
1539 MOZ_ASSERT(type
.isGlobalVarType());
1540 MOZ_ASSERT(type
== Type::canonicalize(Type::lit(lit
)));
1542 uint32_t index
= moduleEnv_
.globals
.length();
1543 if (!moduleEnv_
.globals
.emplaceBack(type
.canonicalToValType(), !isConst
,
1544 index
, ModuleKind::AsmJS
)) {
1548 Global::Which which
= isConst
? Global::ConstantLiteral
: Global::Variable
;
1549 Global
* global
= validationLifo_
.new_
<Global
>(which
);
1554 new (&global
->u
.varOrConst
) Global::U::VarOrConst(index
, lit
);
1556 new (&global
->u
.varOrConst
) Global::U::VarOrConst(index
, type
.which());
1558 if (!globalMap_
.putNew(var
, global
)) {
1562 AsmJSGlobal
g(AsmJSGlobal::Variable
, nullptr);
1563 g
.pod
.u
.var
.initKind_
= AsmJSGlobal::InitConstant
;
1564 g
.pod
.u
.var
.u
.val_
= lit
.value();
1565 return asmJSMetadata_
->asmJSGlobals
.append(std::move(g
));
1567 bool addGlobalVarImport(TaggedParserAtomIndex var
,
1568 TaggedParserAtomIndex field
, Type type
,
1570 MOZ_ASSERT(type
.isGlobalVarType());
1572 UniqueChars fieldChars
= parserAtoms_
.toNewUTF8CharsZ(fc_
, field
);
1577 uint32_t index
= moduleEnv_
.globals
.length();
1578 ValType valType
= type
.canonicalToValType();
1579 if (!moduleEnv_
.globals
.emplaceBack(valType
, !isConst
, index
,
1580 ModuleKind::AsmJS
)) {
1584 Global::Which which
= isConst
? Global::ConstantImport
: Global::Variable
;
1585 Global
* global
= validationLifo_
.new_
<Global
>(which
);
1589 new (&global
->u
.varOrConst
) Global::U::VarOrConst(index
, type
.which());
1590 if (!globalMap_
.putNew(var
, global
)) {
1594 AsmJSGlobal
g(AsmJSGlobal::Variable
, std::move(fieldChars
));
1595 g
.pod
.u
.var
.initKind_
= AsmJSGlobal::InitImport
;
1596 g
.pod
.u
.var
.u
.importValType_
= valType
.packed();
1597 return asmJSMetadata_
->asmJSGlobals
.append(std::move(g
));
1599 bool addArrayView(TaggedParserAtomIndex var
, Scalar::Type vt
,
1600 TaggedParserAtomIndex maybeField
) {
1601 UniqueChars fieldChars
;
1603 fieldChars
= parserAtoms_
.toNewUTF8CharsZ(fc_
, maybeField
);
1609 if (!arrayViews_
.append(ArrayView(var
, vt
))) {
1613 Global
* global
= validationLifo_
.new_
<Global
>(Global::ArrayView
);
1617 new (&global
->u
.viewType_
) Scalar::Type(vt
);
1618 if (!globalMap_
.putNew(var
, global
)) {
1622 AsmJSGlobal
g(AsmJSGlobal::ArrayView
, std::move(fieldChars
));
1623 g
.pod
.u
.viewType_
= vt
;
1624 return asmJSMetadata_
->asmJSGlobals
.append(std::move(g
));
1626 bool addMathBuiltinFunction(TaggedParserAtomIndex var
,
1627 AsmJSMathBuiltinFunction func
,
1628 TaggedParserAtomIndex field
) {
1629 UniqueChars fieldChars
= parserAtoms_
.toNewUTF8CharsZ(fc_
, field
);
1634 Global
* global
= validationLifo_
.new_
<Global
>(Global::MathBuiltinFunction
);
1638 new (&global
->u
.mathBuiltinFunc_
) AsmJSMathBuiltinFunction(func
);
1639 if (!globalMap_
.putNew(var
, global
)) {
1643 AsmJSGlobal
g(AsmJSGlobal::MathBuiltinFunction
, std::move(fieldChars
));
1644 g
.pod
.u
.mathBuiltinFunc_
= func
;
1645 return asmJSMetadata_
->asmJSGlobals
.append(std::move(g
));
1649 bool addGlobalDoubleConstant(TaggedParserAtomIndex var
, double constant
) {
1650 Global
* global
= validationLifo_
.new_
<Global
>(Global::ConstantLiteral
);
1654 new (&global
->u
.varOrConst
) Global::U::VarOrConst(constant
);
1655 return globalMap_
.putNew(var
, global
);
1659 bool addMathBuiltinConstant(TaggedParserAtomIndex var
, double constant
,
1660 TaggedParserAtomIndex field
) {
1661 UniqueChars fieldChars
= parserAtoms_
.toNewUTF8CharsZ(fc_
, field
);
1666 if (!addGlobalDoubleConstant(var
, constant
)) {
1670 AsmJSGlobal
g(AsmJSGlobal::Constant
, std::move(fieldChars
));
1671 g
.pod
.u
.constant
.value_
= constant
;
1672 g
.pod
.u
.constant
.kind_
= AsmJSGlobal::MathConstant
;
1673 return asmJSMetadata_
->asmJSGlobals
.append(std::move(g
));
1675 bool addGlobalConstant(TaggedParserAtomIndex var
, double constant
,
1676 TaggedParserAtomIndex field
) {
1677 UniqueChars fieldChars
= parserAtoms_
.toNewUTF8CharsZ(fc_
, field
);
1682 if (!addGlobalDoubleConstant(var
, constant
)) {
1686 AsmJSGlobal
g(AsmJSGlobal::Constant
, std::move(fieldChars
));
1687 g
.pod
.u
.constant
.value_
= constant
;
1688 g
.pod
.u
.constant
.kind_
= AsmJSGlobal::GlobalConstant
;
1689 return asmJSMetadata_
->asmJSGlobals
.append(std::move(g
));
1691 bool addArrayViewCtor(TaggedParserAtomIndex var
, Scalar::Type vt
,
1692 TaggedParserAtomIndex field
) {
1693 UniqueChars fieldChars
= parserAtoms_
.toNewUTF8CharsZ(fc_
, field
);
1698 Global
* global
= validationLifo_
.new_
<Global
>(Global::ArrayViewCtor
);
1702 new (&global
->u
.viewType_
) Scalar::Type(vt
);
1703 if (!globalMap_
.putNew(var
, global
)) {
1707 AsmJSGlobal
g(AsmJSGlobal::ArrayViewCtor
, std::move(fieldChars
));
1708 g
.pod
.u
.viewType_
= vt
;
1709 return asmJSMetadata_
->asmJSGlobals
.append(std::move(g
));
1711 bool addFFI(TaggedParserAtomIndex var
, TaggedParserAtomIndex field
) {
1712 UniqueChars fieldChars
= parserAtoms_
.toNewUTF8CharsZ(fc_
, field
);
1717 if (asmJSMetadata_
->numFFIs
== UINT32_MAX
) {
1720 uint32_t ffiIndex
= asmJSMetadata_
->numFFIs
++;
1722 Global
* global
= validationLifo_
.new_
<Global
>(Global::FFI
);
1726 new (&global
->u
.ffiIndex_
) uint32_t(ffiIndex
);
1727 if (!globalMap_
.putNew(var
, global
)) {
1731 AsmJSGlobal
g(AsmJSGlobal::FFI
, std::move(fieldChars
));
1732 g
.pod
.u
.ffiIndex_
= ffiIndex
;
1733 return asmJSMetadata_
->asmJSGlobals
.append(std::move(g
));
1735 bool addExportField(const Func
& func
, TaggedParserAtomIndex maybeField
) {
1736 // Record the field name of this export.
1737 CacheableName fieldName
;
1739 UniqueChars fieldChars
= parserAtoms_
.toNewUTF8CharsZ(fc_
, maybeField
);
1743 fieldName
= CacheableName::fromUTF8Chars(std::move(fieldChars
));
1746 // Declare which function is exported which gives us an index into the
1747 // module ExportVector.
1748 uint32_t funcIndex
= funcImportMap_
.count() + func
.funcDefIndex();
1749 if (!moduleEnv_
.exports
.emplaceBack(std::move(fieldName
), funcIndex
,
1750 DefinitionKind::Function
)) {
1754 // The exported function might have already been exported in which case
1755 // the index will refer into the range of AsmJSExports.
1756 return asmJSMetadata_
->asmJSExports
.emplaceBack(
1757 funcIndex
, func
.srcBegin() - asmJSMetadata_
->srcStart
,
1758 func
.srcEnd() - asmJSMetadata_
->srcStart
);
1761 bool defineFuncPtrTable(uint32_t tableIndex
, Uint32Vector
&& elems
) {
1762 Table
& table
= *tables_
[tableIndex
];
1763 if (table
.defined()) {
1769 for (uint32_t& index
: elems
) {
1770 index
+= funcImportMap_
.count();
1773 ModuleElemSegment seg
= ModuleElemSegment();
1774 seg
.elemType
= RefType::func();
1775 seg
.tableIndex
= tableIndex
;
1776 seg
.offsetIfActive
= Some(InitExpr(LitVal(uint32_t(0))));
1777 seg
.encoding
= ModuleElemSegment::Encoding::Indices
;
1778 seg
.elemIndices
= std::move(elems
);
1779 return moduleEnv_
.elemSegments
.append(std::move(seg
));
1782 bool tryConstantAccess(uint64_t start
, uint64_t width
) {
1783 MOZ_ASSERT(UINT64_MAX
- start
> width
);
1784 uint64_t len
= start
+ width
;
1785 if (len
> uint64_t(INT32_MAX
) + 1) {
1788 len
= RoundUpToNextValidAsmJSHeapLength(len
);
1789 if (len
> memory_
.minLength
) {
1790 memory_
.minLength
= len
;
1796 bool hasAlreadyFailed() const { return !!errorString_
; }
1798 bool failOffset(uint32_t offset
, const char* str
) {
1799 MOZ_ASSERT(!hasAlreadyFailed());
1800 MOZ_ASSERT(errorOffset_
== UINT32_MAX
);
1802 errorOffset_
= offset
;
1803 errorString_
= DuplicateString(str
);
1807 bool fail(ParseNode
* pn
, const char* str
) {
1808 return failOffset(pn
->pn_pos
.begin
, str
);
1811 bool failfVAOffset(uint32_t offset
, const char* fmt
, va_list ap
)
1812 MOZ_FORMAT_PRINTF(3, 0) {
1813 MOZ_ASSERT(!hasAlreadyFailed());
1814 MOZ_ASSERT(errorOffset_
== UINT32_MAX
);
1816 errorOffset_
= offset
;
1817 errorString_
= JS_vsmprintf(fmt
, ap
);
1821 bool failfOffset(uint32_t offset
, const char* fmt
, ...)
1822 MOZ_FORMAT_PRINTF(3, 4) {
1825 failfVAOffset(offset
, fmt
, ap
);
1830 bool failf(ParseNode
* pn
, const char* fmt
, ...) MOZ_FORMAT_PRINTF(3, 4) {
1833 failfVAOffset(pn
->pn_pos
.begin
, fmt
, ap
);
1838 bool failNameOffset(uint32_t offset
, const char* fmt
,
1839 TaggedParserAtomIndex name
) {
1840 // This function is invoked without the caller properly rooting its locals.
1841 if (UniqueChars bytes
= parserAtoms_
.toPrintableString(name
)) {
1842 failfOffset(offset
, fmt
, bytes
.get());
1844 ReportOutOfMemory(fc_
);
1849 bool failName(ParseNode
* pn
, const char* fmt
, TaggedParserAtomIndex name
) {
1850 return failNameOffset(pn
->pn_pos
.begin
, fmt
, name
);
1853 bool failOverRecursed() {
1854 errorOverRecursed_
= true;
1858 unsigned numArrayViews() const { return arrayViews_
.length(); }
1859 const ArrayView
& arrayView(unsigned i
) const { return arrayViews_
[i
]; }
1860 unsigned numFuncDefs() const { return funcDefs_
.length(); }
1861 const Func
& funcDef(unsigned i
) const { return funcDefs_
[i
]; }
1862 unsigned numFuncPtrTables() const { return tables_
.length(); }
1863 Table
& table(unsigned i
) const { return *tables_
[i
]; }
1865 const Global
* lookupGlobal(TaggedParserAtomIndex name
) const {
1866 if (GlobalMap::Ptr p
= globalMap_
.lookup(name
)) {
1872 Func
* lookupFuncDef(TaggedParserAtomIndex name
) {
1873 if (GlobalMap::Ptr p
= globalMap_
.lookup(name
)) {
1874 Global
* value
= p
->value();
1875 if (value
->which() == Global::Function
) {
1876 return &funcDefs_
[value
->funcDefIndex()];
1882 bool lookupStandardLibraryMathName(TaggedParserAtomIndex name
,
1883 MathBuiltin
* mathBuiltin
) const {
1884 if (MathNameMap::Ptr p
= standardLibraryMathNames_
.lookup(name
)) {
1885 *mathBuiltin
= p
->value();
1891 bool startFunctionBodies() {
1892 if (!arrayViews_
.empty()) {
1893 memory_
.usage
= MemoryUsage::Unshared
;
1895 memory_
.usage
= MemoryUsage::None
;
1901 // The ModuleValidator encapsulates the entire validation of an asm.js module.
1902 // Its lifetime goes from the validation of the top components of an asm.js
1903 // module (all the globals), the emission of bytecode for all the functions in
1904 // the module and the validation of function's pointer tables. It also finishes
1905 // the compilation of all the module's stubs.
1906 template <typename Unit
>
1907 class MOZ_STACK_CLASS ModuleValidator
: public ModuleValidatorShared
{
1909 AsmJSParser
<Unit
>& parser_
;
1912 ModuleValidator(FrontendContext
* fc
, ParserAtomsTable
& parserAtoms
,
1913 AsmJSParser
<Unit
>& parser
, FunctionNode
* moduleFunctionNode
)
1914 : ModuleValidatorShared(fc
, parserAtoms
, moduleFunctionNode
),
1917 ~ModuleValidator() {
1919 MOZ_ASSERT(errorOffset_
!= UINT32_MAX
);
1920 typeFailure(errorOffset_
, errorString_
.get());
1922 if (errorOverRecursed_
) {
1923 ReportOverRecursed(fc_
);
1929 bool newSig(FuncType
&& sig
, uint32_t* sigIndex
) {
1930 if (moduleEnv_
.types
->length() >= MaxTypes
) {
1931 return failCurrentOffset("too many signatures");
1934 *sigIndex
= moduleEnv_
.types
->length();
1935 return moduleEnv_
.types
->addType(std::move(sig
));
1937 bool declareSig(FuncType
&& sig
, uint32_t* sigIndex
) {
1938 SigSet::AddPtr p
= sigSet_
.lookupForAdd(sig
);
1940 *sigIndex
= p
->sigIndex();
1941 MOZ_ASSERT(FuncType::strictlyEquals(
1942 moduleEnv_
.types
->type(*sigIndex
).funcType(), sig
));
1946 return newSig(std::move(sig
), sigIndex
) &&
1947 sigSet_
.add(p
, HashableSig(*sigIndex
, *moduleEnv_
.types
));
1951 void typeFailure(uint32_t offset
, ...) {
1953 va_start(args
, offset
);
1955 auto& ts
= tokenStream();
1956 ErrorMetadata metadata
;
1957 if (ts
.computeErrorMetadata(&metadata
, AsVariant(offset
))) {
1958 if (ts
.anyCharsAccess().options().throwOnAsmJSValidationFailure()) {
1959 ReportCompileErrorLatin1VA(fc_
, std::move(metadata
), nullptr,
1960 JSMSG_USE_ASM_TYPE_FAIL
, &args
);
1962 // asm.js type failure is indicated by calling one of the fail*
1963 // functions below. These functions always return false to
1964 // halt asm.js parsing. Whether normal parsing is attempted as
1965 // fallback, depends whether an exception is also set.
1967 // If warning succeeds, no exception is set. If warning fails,
1968 // an exception is set and execution will halt. Thus it's safe
1969 // and correct to ignore the return value here.
1970 (void)ts
.compileWarning(std::move(metadata
), nullptr,
1971 JSMSG_USE_ASM_TYPE_FAIL
, &args
);
1980 asmJSMetadata_
= js_new
<AsmJSMetadata
>();
1981 if (!asmJSMetadata_
) {
1982 ReportOutOfMemory(fc_
);
1986 asmJSMetadata_
->toStringStart
=
1987 moduleFunctionNode_
->funbox()->extent().toStringStart
;
1988 asmJSMetadata_
->srcStart
= moduleFunctionNode_
->body()->pn_pos
.begin
;
1989 asmJSMetadata_
->strict
= parser_
.pc_
->sc()->strict() &&
1990 !parser_
.pc_
->sc()->hasExplicitUseStrict();
1991 asmJSMetadata_
->alwaysUseFdlibm
= parser_
.options().alwaysUseFdlibm();
1992 asmJSMetadata_
->source
= do_AddRef(parser_
.ss
);
1994 if (!initModuleEnvironment()) {
1997 return addStandardLibraryMathInfo();
2000 AsmJSParser
<Unit
>& parser() const { return parser_
; }
2002 auto& tokenStream() const { return parser_
.tokenStream
; }
2004 bool alwaysUseFdlibm() const { return asmJSMetadata_
->alwaysUseFdlibm
; }
2007 bool addFuncDef(TaggedParserAtomIndex name
, uint32_t firstUse
, FuncType
&& sig
,
2010 if (!declareSig(std::move(sig
), &sigIndex
)) {
2014 uint32_t funcDefIndex
= funcDefs_
.length();
2015 if (funcDefIndex
>= MaxFuncs
) {
2016 return failCurrentOffset("too many functions");
2019 Global
* global
= validationLifo_
.new_
<Global
>(Global::Function
);
2023 new (&global
->u
.funcDefIndex_
) uint32_t(funcDefIndex
);
2024 if (!globalMap_
.putNew(name
, global
)) {
2027 if (!funcDefs_
.emplaceBack(name
, sigIndex
, firstUse
, funcDefIndex
)) {
2030 *func
= &funcDefs_
.back();
2033 bool declareFuncPtrTable(FuncType
&& sig
, TaggedParserAtomIndex name
,
2034 uint32_t firstUse
, uint32_t mask
,
2035 uint32_t* tableIndex
) {
2036 if (mask
> MaxTableLength
) {
2037 return failCurrentOffset("function pointer table too big");
2040 MOZ_ASSERT(moduleEnv_
.tables
.length() == tables_
.length());
2041 *tableIndex
= moduleEnv_
.tables
.length();
2044 if (!newSig(std::move(sig
), &sigIndex
)) {
2048 MOZ_ASSERT(sigIndex
>= moduleEnv_
.asmJSSigToTableIndex
.length());
2049 if (!moduleEnv_
.asmJSSigToTableIndex
.resize(sigIndex
+ 1)) {
2053 moduleEnv_
.asmJSSigToTableIndex
[sigIndex
] = moduleEnv_
.tables
.length();
2054 if (!moduleEnv_
.tables
.emplaceBack(RefType::func(), mask
+ 1, Nothing(),
2055 /* initExpr */ Nothing(),
2056 /*isAsmJS*/ true)) {
2060 Global
* global
= validationLifo_
.new_
<Global
>(Global::Table
);
2065 new (&global
->u
.tableIndex_
) uint32_t(*tableIndex
);
2066 if (!globalMap_
.putNew(name
, global
)) {
2070 Table
* t
= validationLifo_
.new_
<Table
>(sigIndex
, name
, firstUse
, mask
);
2071 return t
&& tables_
.append(t
);
2073 bool declareImport(TaggedParserAtomIndex name
, FuncType
&& sig
,
2074 unsigned ffiIndex
, uint32_t* importIndex
) {
2075 FuncImportMap::AddPtr p
=
2076 funcImportMap_
.lookupForAdd(NamedSig::Lookup(name
, sig
));
2078 *importIndex
= p
->value();
2082 *importIndex
= funcImportMap_
.count();
2083 MOZ_ASSERT(*importIndex
== asmJSMetadata_
->asmJSImports
.length());
2085 if (*importIndex
>= MaxImports
) {
2086 return failCurrentOffset("too many imports");
2089 if (!asmJSMetadata_
->asmJSImports
.emplaceBack(ffiIndex
)) {
2094 if (!declareSig(std::move(sig
), &sigIndex
)) {
2098 return funcImportMap_
.add(p
, NamedSig(name
, sigIndex
, *moduleEnv_
.types
),
2103 bool failCurrentOffset(const char* str
) {
2104 return failOffset(tokenStream().anyCharsAccess().currentToken().pos
.begin
,
2108 SharedModule
finish() {
2109 MOZ_ASSERT(moduleEnv_
.numMemories() == 0);
2110 if (memory_
.usage
!= MemoryUsage::None
) {
2112 limits
.shared
= memory_
.usage
== MemoryUsage::Shared
? Shareable::True
2114 limits
.initial
= memory_
.minPages();
2115 limits
.maximum
= Nothing();
2116 limits
.indexType
= IndexType::I32
;
2117 if (!moduleEnv_
.memories
.append(MemoryDesc(limits
))) {
2121 MOZ_ASSERT(moduleEnv_
.funcs
.empty());
2122 if (!moduleEnv_
.funcs
.resize(funcImportMap_
.count() + funcDefs_
.length())) {
2125 for (FuncImportMap::Range r
= funcImportMap_
.all(); !r
.empty();
2127 uint32_t funcIndex
= r
.front().value();
2128 uint32_t funcTypeIndex
= r
.front().key().sigIndex();
2129 MOZ_ASSERT(!moduleEnv_
.funcs
[funcIndex
].type
);
2130 moduleEnv_
.funcs
[funcIndex
] = FuncDesc(
2131 &moduleEnv_
.types
->type(funcTypeIndex
).funcType(), funcTypeIndex
);
2133 for (const Func
& func
: funcDefs_
) {
2134 uint32_t funcIndex
= funcImportMap_
.count() + func
.funcDefIndex();
2135 uint32_t funcTypeIndex
= func
.sigIndex();
2136 MOZ_ASSERT(!moduleEnv_
.funcs
[funcIndex
].type
);
2137 moduleEnv_
.funcs
[funcIndex
] = FuncDesc(
2138 &moduleEnv_
.types
->type(funcTypeIndex
).funcType(), funcTypeIndex
);
2140 for (const Export
& exp
: moduleEnv_
.exports
) {
2141 if (exp
.kind() != DefinitionKind::Function
) {
2144 uint32_t funcIndex
= exp
.funcIndex();
2145 moduleEnv_
.declareFuncExported(funcIndex
, /* eager */ true,
2146 /* canRefFunc */ false);
2149 moduleEnv_
.numFuncImports
= funcImportMap_
.count();
2151 // All globals (inits and imports) are imports from Wasm point of view.
2152 moduleEnv_
.numGlobalImports
= moduleEnv_
.globals
.length();
2154 MOZ_ASSERT(asmJSMetadata_
->asmJSFuncNames
.empty());
2155 if (!asmJSMetadata_
->asmJSFuncNames
.resize(funcImportMap_
.count())) {
2158 for (const Func
& func
: funcDefs_
) {
2159 CacheableChars funcName
= parserAtoms_
.toNewUTF8CharsZ(fc_
, func
.name());
2161 !asmJSMetadata_
->asmJSFuncNames
.emplaceBack(std::move(funcName
))) {
2166 uint32_t endBeforeCurly
=
2167 tokenStream().anyCharsAccess().currentToken().pos
.end
;
2168 asmJSMetadata_
->srcLength
= endBeforeCurly
- asmJSMetadata_
->srcStart
;
2172 tokenStream().peekTokenPos(&pos
, TokenStreamShared::SlashIsRegExp
));
2173 uint32_t endAfterCurly
= pos
.end
;
2174 asmJSMetadata_
->srcLengthWithRightBrace
=
2175 endAfterCurly
- asmJSMetadata_
->srcStart
;
2177 ScriptedCaller scriptedCaller
;
2178 if (parser_
.ss
->filename()) {
2179 scriptedCaller
.line
= 0; // unused
2180 scriptedCaller
.filename
= DuplicateString(parser_
.ss
->filename());
2181 if (!scriptedCaller
.filename
) {
2186 // The default options are fine for asm.js
2187 SharedCompileArgs args
=
2188 CompileArgs::buildForAsmJS(std::move(scriptedCaller
));
2190 ReportOutOfMemory(fc_
);
2194 uint32_t codeSectionSize
= 0;
2195 for (const Func
& func
: funcDefs_
) {
2196 codeSectionSize
+= func
.bytes().length();
2199 moduleEnv_
.codeSection
.emplace();
2200 moduleEnv_
.codeSection
->start
= 0;
2201 moduleEnv_
.codeSection
->size
= codeSectionSize
;
2203 // asm.js does not have any wasm bytecode to save; view-source is
2204 // provided through the ScriptSource.
2205 SharedBytes bytes
= js_new
<ShareableBytes
>();
2207 ReportOutOfMemory(fc_
);
2211 ModuleGenerator
mg(*args
, &moduleEnv_
, &compilerEnv_
, nullptr, nullptr,
2213 if (!mg
.init(asmJSMetadata_
.get())) {
2217 for (Func
& func
: funcDefs_
) {
2218 if (!mg
.compileFuncDef(funcImportMap_
.count() + func
.funcDefIndex(),
2219 func
.line(), func
.bytes().begin(),
2221 std::move(func
.callSiteLineNums()))) {
2226 if (!mg
.finishFuncDefs()) {
2230 return mg
.finishModule(*bytes
);
2234 /*****************************************************************************/
2235 // Numeric literal utilities
2237 static bool IsNumericNonFloatLiteral(ParseNode
* pn
) {
2238 // Note: '-' is never rolled into the number; numbers are always positive
2239 // and negations must be applied manually.
2240 return pn
->isKind(ParseNodeKind::NumberExpr
) ||
2241 (pn
->isKind(ParseNodeKind::NegExpr
) &&
2242 UnaryKid(pn
)->isKind(ParseNodeKind::NumberExpr
));
2245 static bool IsCallToGlobal(ModuleValidatorShared
& m
, ParseNode
* pn
,
2246 const ModuleValidatorShared::Global
** global
) {
2247 if (!pn
->isKind(ParseNodeKind::CallExpr
)) {
2251 ParseNode
* callee
= CallCallee(pn
);
2252 if (!callee
->isKind(ParseNodeKind::Name
)) {
2256 *global
= m
.lookupGlobal(callee
->as
<NameNode
>().name());
2260 static bool IsCoercionCall(ModuleValidatorShared
& m
, ParseNode
* pn
,
2261 Type
* coerceTo
, ParseNode
** coercedExpr
) {
2262 const ModuleValidatorShared::Global
* global
;
2263 if (!IsCallToGlobal(m
, pn
, &global
)) {
2267 if (CallArgListLength(pn
) != 1) {
2272 *coercedExpr
= CallArgList(pn
);
2275 if (global
->isMathFunction() &&
2276 global
->mathBuiltinFunction() == AsmJSMathBuiltin_fround
) {
2277 *coerceTo
= Type::Float
;
2284 static bool IsFloatLiteral(ModuleValidatorShared
& m
, ParseNode
* pn
) {
2285 ParseNode
* coercedExpr
;
2287 if (!IsCoercionCall(m
, pn
, &coerceTo
, &coercedExpr
)) {
2290 // Don't fold into || to avoid clang/memcheck bug (bug 1077031).
2291 if (!coerceTo
.isFloat()) {
2294 return IsNumericNonFloatLiteral(coercedExpr
);
2297 static bool IsNumericLiteral(ModuleValidatorShared
& m
, ParseNode
* pn
) {
2298 return IsNumericNonFloatLiteral(pn
) || IsFloatLiteral(m
, pn
);
2301 // The JS grammar treats -42 as -(42) (i.e., with separate grammar
2302 // productions) for the unary - and literal 42). However, the asm.js spec
2303 // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
2304 // so fold the two potential parse nodes into a single double value.
2305 static double ExtractNumericNonFloatValue(ParseNode
* pn
,
2306 ParseNode
** out
= nullptr) {
2307 MOZ_ASSERT(IsNumericNonFloatLiteral(pn
));
2309 if (pn
->isKind(ParseNodeKind::NegExpr
)) {
2314 return -NumberNodeValue(pn
);
2317 return NumberNodeValue(pn
);
2320 static NumLit
ExtractNumericLiteral(ModuleValidatorShared
& m
, ParseNode
* pn
) {
2321 MOZ_ASSERT(IsNumericLiteral(m
, pn
));
2323 if (pn
->isKind(ParseNodeKind::CallExpr
)) {
2324 // Float literals are explicitly coerced and thus the coerced literal may be
2325 // any valid (non-float) numeric literal.
2326 MOZ_ASSERT(CallArgListLength(pn
) == 1);
2327 pn
= CallArgList(pn
);
2328 double d
= ExtractNumericNonFloatValue(pn
);
2329 return NumLit(NumLit::Float
, DoubleValue(d
));
2332 double d
= ExtractNumericNonFloatValue(pn
, &pn
);
2334 // The asm.js spec syntactically distinguishes any literal containing a
2335 // decimal point or the literal -0 as having double type.
2336 if (NumberNodeHasFrac(pn
) || IsNegativeZero(d
)) {
2337 return NumLit(NumLit::Double
, DoubleValue(d
));
2340 // The syntactic checks above rule out these double values.
2341 MOZ_ASSERT(!IsNegativeZero(d
));
2342 MOZ_ASSERT(!std::isnan(d
));
2344 // Although doubles can only *precisely* represent 53-bit integers, they
2345 // can *imprecisely* represent integers much bigger than an int64_t.
2346 // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t
2347 // is undefined, so test against the integer bounds using doubles.
2348 if (d
< double(INT32_MIN
) || d
> double(UINT32_MAX
)) {
2349 return NumLit(NumLit::OutOfRangeInt
, UndefinedValue());
2352 // With the above syntactic and range limitations, d is definitely an
2353 // integer in the range [INT32_MIN, UINT32_MAX] range.
2354 int64_t i64
= int64_t(d
);
2356 if (i64
<= INT32_MAX
) {
2357 return NumLit(NumLit::Fixnum
, Int32Value(i64
));
2359 MOZ_ASSERT(i64
<= UINT32_MAX
);
2360 return NumLit(NumLit::BigUnsigned
, Int32Value(uint32_t(i64
)));
2362 MOZ_ASSERT(i64
>= INT32_MIN
);
2363 return NumLit(NumLit::NegativeInt
, Int32Value(i64
));
2366 static inline bool IsLiteralInt(const NumLit
& lit
, uint32_t* u32
) {
2367 switch (lit
.which()) {
2368 case NumLit::Fixnum
:
2369 case NumLit::BigUnsigned
:
2370 case NumLit::NegativeInt
:
2371 *u32
= lit
.toUint32();
2373 case NumLit::Double
:
2375 case NumLit::OutOfRangeInt
:
2378 MOZ_CRASH("Bad literal type");
2381 static inline bool IsLiteralInt(ModuleValidatorShared
& m
, ParseNode
* pn
,
2383 return IsNumericLiteral(m
, pn
) &&
2384 IsLiteralInt(ExtractNumericLiteral(m
, pn
), u32
);
2387 /*****************************************************************************/
2391 using LabelVector
= Vector
<TaggedParserAtomIndex
, 4, SystemAllocPolicy
>;
2393 class MOZ_STACK_CLASS FunctionValidatorShared
{
2398 Local(Type t
, unsigned slot
) : type(t
), slot(slot
) {
2399 MOZ_ASSERT(type
.isCanonicalValType());
2405 HashMap
<TaggedParserAtomIndex
, Local
, TaggedParserAtomIndexHasher
>;
2407 HashMap
<TaggedParserAtomIndex
, uint32_t, TaggedParserAtomIndexHasher
>;
2409 // This is also a ModuleValidator<Unit>& after the appropriate static_cast<>.
2410 ModuleValidatorShared
& m_
;
2415 Uint32Vector callSiteLineNums_
;
2419 LabelMap breakLabels_
;
2420 LabelMap continueLabels_
;
2421 Uint32Vector breakableStack_
;
2422 Uint32Vector continuableStack_
;
2423 uint32_t blockDepth_
;
2425 bool hasAlreadyReturned_
;
2426 Maybe
<ValType
> ret_
;
2429 FunctionValidatorShared(ModuleValidatorShared
& m
, FunctionNode
* fn
,
2430 FrontendContext
* fc
)
2436 continueLabels_(fc
),
2438 hasAlreadyReturned_(false) {}
2441 template <typename Unit
>
2442 FunctionValidatorShared(ModuleValidator
<Unit
>& m
, FunctionNode
* fn
,
2443 FrontendContext
* fc
)
2444 : FunctionValidatorShared(static_cast<ModuleValidatorShared
&>(m
), fn
,
2448 ModuleValidatorShared
& m() const { return m_
; }
2450 FrontendContext
* fc() const { return m_
.fc(); }
2451 FunctionNode
* fn() const { return fn_
; }
2453 void define(ModuleValidatorShared::Func
* func
, unsigned line
) {
2454 MOZ_ASSERT(!blockDepth_
);
2455 MOZ_ASSERT(breakableStack_
.empty());
2456 MOZ_ASSERT(continuableStack_
.empty());
2457 MOZ_ASSERT(breakLabels_
.empty());
2458 MOZ_ASSERT(continueLabels_
.empty());
2459 func
->define(fn_
, line
, std::move(bytes_
), std::move(callSiteLineNums_
));
2462 bool fail(ParseNode
* pn
, const char* str
) { return m_
.fail(pn
, str
); }
2464 bool failf(ParseNode
* pn
, const char* fmt
, ...) MOZ_FORMAT_PRINTF(3, 4) {
2467 m_
.failfVAOffset(pn
->pn_pos
.begin
, fmt
, ap
);
2472 bool failName(ParseNode
* pn
, const char* fmt
, TaggedParserAtomIndex name
) {
2473 return m_
.failName(pn
, fmt
, name
);
2476 /***************************************************** Local scope setup */
2478 bool addLocal(ParseNode
* pn
, TaggedParserAtomIndex name
, Type type
) {
2479 LocalMap::AddPtr p
= locals_
.lookupForAdd(name
);
2481 return failName(pn
, "duplicate local name '%s' not allowed", name
);
2483 return locals_
.add(p
, name
, Local(type
, locals_
.count()));
2486 /****************************** For consistency of returns in a function */
2488 bool hasAlreadyReturned() const { return hasAlreadyReturned_
; }
2490 Maybe
<ValType
> returnedType() const { return ret_
; }
2492 void setReturnedType(const Maybe
<ValType
>& ret
) {
2493 MOZ_ASSERT(!hasAlreadyReturned_
);
2495 hasAlreadyReturned_
= true;
2498 /**************************************************************** Labels */
2500 bool writeBr(uint32_t absolute
, Op op
= Op::Br
) {
2501 MOZ_ASSERT(op
== Op::Br
|| op
== Op::BrIf
);
2502 MOZ_ASSERT(absolute
< blockDepth_
);
2503 return encoder().writeOp(op
) &&
2504 encoder().writeVarU32(blockDepth_
- 1 - absolute
);
2506 void removeLabel(TaggedParserAtomIndex label
, LabelMap
* map
) {
2507 LabelMap::Ptr p
= map
->lookup(label
);
2513 bool pushBreakableBlock() {
2514 return encoder().writeOp(Op::Block
) &&
2515 encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid
)) &&
2516 breakableStack_
.append(blockDepth_
++);
2518 bool popBreakableBlock() {
2519 MOZ_ALWAYS_TRUE(breakableStack_
.popCopy() == --blockDepth_
);
2520 return encoder().writeOp(Op::End
);
2523 bool pushUnbreakableBlock(const LabelVector
* labels
= nullptr) {
2525 for (TaggedParserAtomIndex label
: *labels
) {
2526 if (!breakLabels_
.putNew(label
, blockDepth_
)) {
2532 return encoder().writeOp(Op::Block
) &&
2533 encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid
));
2535 bool popUnbreakableBlock(const LabelVector
* labels
= nullptr) {
2537 for (TaggedParserAtomIndex label
: *labels
) {
2538 removeLabel(label
, &breakLabels_
);
2542 return encoder().writeOp(Op::End
);
2545 bool pushContinuableBlock() {
2546 return encoder().writeOp(Op::Block
) &&
2547 encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid
)) &&
2548 continuableStack_
.append(blockDepth_
++);
2550 bool popContinuableBlock() {
2551 MOZ_ALWAYS_TRUE(continuableStack_
.popCopy() == --blockDepth_
);
2552 return encoder().writeOp(Op::End
);
2556 return encoder().writeOp(Op::Block
) &&
2557 encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid
)) &&
2558 encoder().writeOp(Op::Loop
) &&
2559 encoder().writeFixedU8(uint8_t(TypeCode::BlockVoid
)) &&
2560 breakableStack_
.append(blockDepth_
++) &&
2561 continuableStack_
.append(blockDepth_
++);
2564 MOZ_ALWAYS_TRUE(continuableStack_
.popCopy() == --blockDepth_
);
2565 MOZ_ALWAYS_TRUE(breakableStack_
.popCopy() == --blockDepth_
);
2566 return encoder().writeOp(Op::End
) && encoder().writeOp(Op::End
);
2569 bool pushIf(size_t* typeAt
) {
2571 return encoder().writeOp(Op::If
) && encoder().writePatchableFixedU7(typeAt
);
2573 bool switchToElse() {
2574 MOZ_ASSERT(blockDepth_
> 0);
2575 return encoder().writeOp(Op::Else
);
2577 void setIfType(size_t typeAt
, TypeCode type
) {
2578 encoder().patchFixedU7(typeAt
, uint8_t(type
));
2581 MOZ_ASSERT(blockDepth_
> 0);
2583 return encoder().writeOp(Op::End
);
2585 bool popIf(size_t typeAt
, TypeCode type
) {
2586 MOZ_ASSERT(blockDepth_
> 0);
2588 if (!encoder().writeOp(Op::End
)) {
2592 setIfType(typeAt
, type
);
2596 bool writeBreakIf() { return writeBr(breakableStack_
.back(), Op::BrIf
); }
2597 bool writeContinueIf() { return writeBr(continuableStack_
.back(), Op::BrIf
); }
2598 bool writeUnlabeledBreakOrContinue(bool isBreak
) {
2599 return writeBr(isBreak
? breakableStack_
.back() : continuableStack_
.back());
2601 bool writeContinue() { return writeBr(continuableStack_
.back()); }
2603 bool addLabels(const LabelVector
& labels
, uint32_t relativeBreakDepth
,
2604 uint32_t relativeContinueDepth
) {
2605 for (TaggedParserAtomIndex label
: labels
) {
2606 if (!breakLabels_
.putNew(label
, blockDepth_
+ relativeBreakDepth
)) {
2609 if (!continueLabels_
.putNew(label
, blockDepth_
+ relativeContinueDepth
)) {
2615 void removeLabels(const LabelVector
& labels
) {
2616 for (TaggedParserAtomIndex label
: labels
) {
2617 removeLabel(label
, &breakLabels_
);
2618 removeLabel(label
, &continueLabels_
);
2621 bool writeLabeledBreakOrContinue(TaggedParserAtomIndex label
, bool isBreak
) {
2622 LabelMap
& map
= isBreak
? breakLabels_
: continueLabels_
;
2623 if (LabelMap::Ptr p
= map
.lookup(label
)) {
2624 return writeBr(p
->value());
2626 MOZ_CRASH("nonexistent label");
2629 /*************************************************** Read-only interface */
2631 const Local
* lookupLocal(TaggedParserAtomIndex name
) const {
2632 if (auto p
= locals_
.lookup(name
)) {
2638 const ModuleValidatorShared::Global
* lookupGlobal(
2639 TaggedParserAtomIndex name
) const {
2640 if (locals_
.has(name
)) {
2643 return m_
.lookupGlobal(name
);
2646 size_t numLocals() const { return locals_
.count(); }
2648 /**************************************************** Encoding interface */
2650 Encoder
& encoder() { return encoder_
; }
2652 [[nodiscard
]] bool writeInt32Lit(int32_t i32
) {
2653 return encoder().writeOp(Op::I32Const
) && encoder().writeVarS32(i32
);
2655 [[nodiscard
]] bool writeConstExpr(const NumLit
& lit
) {
2656 switch (lit
.which()) {
2657 case NumLit::Fixnum
:
2658 case NumLit::NegativeInt
:
2659 case NumLit::BigUnsigned
:
2660 return writeInt32Lit(lit
.toInt32());
2662 return encoder().writeOp(Op::F32Const
) &&
2663 encoder().writeFixedF32(lit
.toFloat());
2664 case NumLit::Double
:
2665 return encoder().writeOp(Op::F64Const
) &&
2666 encoder().writeFixedF64(lit
.toDouble());
2667 case NumLit::OutOfRangeInt
:
2670 MOZ_CRASH("unexpected literal type");
2674 // Encapsulates the building of an asm bytecode function from an asm.js function
2675 // source code, packing the asm.js code into the asm bytecode form that can
2676 // be decoded and compiled with a FunctionCompiler.
2677 template <typename Unit
>
2678 class MOZ_STACK_CLASS FunctionValidator
: public FunctionValidatorShared
{
2680 FunctionValidator(ModuleValidator
<Unit
>& m
, FunctionNode
* fn
)
2681 : FunctionValidatorShared(m
, fn
, m
.fc()) {}
2684 ModuleValidator
<Unit
>& m() const {
2685 return static_cast<ModuleValidator
<Unit
>&>(FunctionValidatorShared::m());
2688 [[nodiscard
]] bool writeCall(ParseNode
* pn
, Op op
) {
2689 MOZ_ASSERT(op
== Op::Call
);
2690 if (!encoder().writeOp(op
)) {
2694 return appendCallSiteLineNumber(pn
);
2696 [[nodiscard
]] bool writeCall(ParseNode
* pn
, MozOp op
) {
2697 MOZ_ASSERT(op
== MozOp::OldCallDirect
|| op
== MozOp::OldCallIndirect
);
2698 if (!encoder().writeOp(op
)) {
2702 return appendCallSiteLineNumber(pn
);
2704 [[nodiscard
]] bool prepareCall(ParseNode
* pn
) {
2705 return appendCallSiteLineNumber(pn
);
2709 [[nodiscard
]] bool appendCallSiteLineNumber(ParseNode
* node
) {
2710 const TokenStreamAnyChars
& anyChars
= m().tokenStream().anyCharsAccess();
2711 auto lineToken
= anyChars
.lineToken(node
->pn_pos
.begin
);
2712 uint32_t lineNumber
= anyChars
.lineNumber(lineToken
);
2713 if (lineNumber
> CallSiteDesc::MAX_LINE_OR_BYTECODE_VALUE
) {
2714 return fail(node
, "line number exceeding implementation limits");
2716 return callSiteLineNums_
.append(lineNumber
);
2720 } /* anonymous namespace */
2722 /*****************************************************************************/
2723 // asm.js type-checking and code-generation algorithm
2725 static bool CheckIdentifier(ModuleValidatorShared
& m
, ParseNode
* usepn
,
2726 TaggedParserAtomIndex name
) {
2727 if (name
== TaggedParserAtomIndex::WellKnown::arguments() ||
2728 name
== TaggedParserAtomIndex::WellKnown::eval()) {
2729 return m
.failName(usepn
, "'%s' is not an allowed identifier", name
);
2734 static bool CheckModuleLevelName(ModuleValidatorShared
& m
, ParseNode
* usepn
,
2735 TaggedParserAtomIndex name
) {
2736 if (!CheckIdentifier(m
, usepn
, name
)) {
2740 if (name
== m
.moduleFunctionName() || name
== m
.globalArgumentName() ||
2741 name
== m
.importArgumentName() || name
== m
.bufferArgumentName() ||
2742 m
.lookupGlobal(name
)) {
2743 return m
.failName(usepn
, "duplicate name '%s' not allowed", name
);
2749 static bool CheckFunctionHead(ModuleValidatorShared
& m
, FunctionNode
* funNode
) {
2750 FunctionBox
* funbox
= funNode
->funbox();
2751 MOZ_ASSERT(!funbox
->hasExprBody());
2753 if (funbox
->hasRest()) {
2754 return m
.fail(funNode
, "rest args not allowed");
2756 if (funbox
->hasDestructuringArgs
) {
2757 return m
.fail(funNode
, "destructuring args not allowed");
2762 static bool CheckArgument(ModuleValidatorShared
& m
, ParseNode
* arg
,
2763 TaggedParserAtomIndex
* name
) {
2764 *name
= TaggedParserAtomIndex::null();
2766 if (!arg
->isKind(ParseNodeKind::Name
)) {
2767 return m
.fail(arg
, "argument is not a plain name");
2770 TaggedParserAtomIndex argName
= arg
->as
<NameNode
>().name();
2771 if (!CheckIdentifier(m
, arg
, argName
)) {
2779 static bool CheckModuleArgument(ModuleValidatorShared
& m
, ParseNode
* arg
,
2780 TaggedParserAtomIndex
* name
) {
2781 if (!CheckArgument(m
, arg
, name
)) {
2785 return CheckModuleLevelName(m
, arg
, *name
);
2788 static bool CheckModuleArguments(ModuleValidatorShared
& m
,
2789 FunctionNode
* funNode
) {
2790 unsigned numFormals
;
2791 ParseNode
* arg1
= FunctionFormalParametersList(funNode
, &numFormals
);
2792 ParseNode
* arg2
= arg1
? NextNode(arg1
) : nullptr;
2793 ParseNode
* arg3
= arg2
? NextNode(arg2
) : nullptr;
2795 if (numFormals
> 3) {
2796 return m
.fail(funNode
, "asm.js modules takes at most 3 argument");
2799 TaggedParserAtomIndex arg1Name
;
2800 if (arg1
&& !CheckModuleArgument(m
, arg1
, &arg1Name
)) {
2803 if (!m
.initGlobalArgumentName(arg1Name
)) {
2807 TaggedParserAtomIndex arg2Name
;
2808 if (arg2
&& !CheckModuleArgument(m
, arg2
, &arg2Name
)) {
2811 if (!m
.initImportArgumentName(arg2Name
)) {
2815 TaggedParserAtomIndex arg3Name
;
2816 if (arg3
&& !CheckModuleArgument(m
, arg3
, &arg3Name
)) {
2819 return m
.initBufferArgumentName(arg3Name
);
2822 static bool CheckPrecedingStatements(ModuleValidatorShared
& m
,
2823 ParseNode
* stmtList
) {
2824 MOZ_ASSERT(stmtList
->isKind(ParseNodeKind::StatementList
));
2826 ParseNode
* stmt
= ListHead(stmtList
);
2827 for (unsigned i
= 0, n
= ListLength(stmtList
); i
< n
; i
++) {
2828 if (!IsIgnoredDirective(stmt
)) {
2829 return m
.fail(stmt
, "invalid asm.js statement");
2836 static bool CheckGlobalVariableInitConstant(ModuleValidatorShared
& m
,
2837 TaggedParserAtomIndex varName
,
2838 ParseNode
* initNode
, bool isConst
) {
2839 NumLit lit
= ExtractNumericLiteral(m
, initNode
);
2841 return m
.fail(initNode
,
2842 "global initializer is out of representable integer range");
2845 Type canonicalType
= Type::canonicalize(Type::lit(lit
));
2846 if (!canonicalType
.isGlobalVarType()) {
2847 return m
.fail(initNode
, "global variable type not allowed");
2850 return m
.addGlobalVarInit(varName
, lit
, canonicalType
, isConst
);
2853 static bool CheckTypeAnnotation(ModuleValidatorShared
& m
,
2854 ParseNode
* coercionNode
, Type
* coerceTo
,
2855 ParseNode
** coercedExpr
= nullptr) {
2856 switch (coercionNode
->getKind()) {
2857 case ParseNodeKind::BitOrExpr
: {
2858 ParseNode
* rhs
= BitwiseRight(coercionNode
);
2860 if (!IsLiteralInt(m
, rhs
, &i
) || i
!= 0) {
2861 return m
.fail(rhs
, "must use |0 for argument/return coercion");
2863 *coerceTo
= Type::Int
;
2865 *coercedExpr
= BitwiseLeft(coercionNode
);
2869 case ParseNodeKind::PosExpr
: {
2870 *coerceTo
= Type::Double
;
2872 *coercedExpr
= UnaryKid(coercionNode
);
2876 case ParseNodeKind::CallExpr
: {
2877 if (IsCoercionCall(m
, coercionNode
, coerceTo
, coercedExpr
)) {
2885 return m
.fail(coercionNode
, "must be of the form +x, x|0 or fround(x)");
2888 static bool CheckGlobalVariableInitImport(ModuleValidatorShared
& m
,
2889 TaggedParserAtomIndex varName
,
2890 ParseNode
* initNode
, bool isConst
) {
2892 ParseNode
* coercedExpr
;
2893 if (!CheckTypeAnnotation(m
, initNode
, &coerceTo
, &coercedExpr
)) {
2897 if (!coercedExpr
->isKind(ParseNodeKind::DotExpr
)) {
2898 return m
.failName(coercedExpr
, "invalid import expression for global '%s'",
2902 if (!coerceTo
.isGlobalVarType()) {
2903 return m
.fail(initNode
, "global variable type not allowed");
2906 ParseNode
* base
= DotBase(coercedExpr
);
2907 TaggedParserAtomIndex field
= DotMember(coercedExpr
);
2909 TaggedParserAtomIndex importName
= m
.importArgumentName();
2911 return m
.fail(coercedExpr
,
2912 "cannot import without an asm.js foreign parameter");
2914 if (!IsUseOfName(base
, importName
)) {
2915 return m
.failName(coercedExpr
, "base of import expression must be '%s'",
2919 return m
.addGlobalVarImport(varName
, field
, coerceTo
, isConst
);
2922 static bool IsArrayViewCtorName(ModuleValidatorShared
& m
,
2923 TaggedParserAtomIndex name
,
2924 Scalar::Type
* type
) {
2925 if (name
== TaggedParserAtomIndex::WellKnown::Int8Array()) {
2926 *type
= Scalar::Int8
;
2927 } else if (name
== TaggedParserAtomIndex::WellKnown::Uint8Array()) {
2928 *type
= Scalar::Uint8
;
2929 } else if (name
== TaggedParserAtomIndex::WellKnown::Int16Array()) {
2930 *type
= Scalar::Int16
;
2931 } else if (name
== TaggedParserAtomIndex::WellKnown::Uint16Array()) {
2932 *type
= Scalar::Uint16
;
2933 } else if (name
== TaggedParserAtomIndex::WellKnown::Int32Array()) {
2934 *type
= Scalar::Int32
;
2935 } else if (name
== TaggedParserAtomIndex::WellKnown::Uint32Array()) {
2936 *type
= Scalar::Uint32
;
2937 } else if (name
== TaggedParserAtomIndex::WellKnown::Float32Array()) {
2938 *type
= Scalar::Float32
;
2939 } else if (name
== TaggedParserAtomIndex::WellKnown::Float64Array()) {
2940 *type
= Scalar::Float64
;
2947 static bool CheckNewArrayViewArgs(ModuleValidatorShared
& m
, ParseNode
* newExpr
,
2948 TaggedParserAtomIndex bufferName
) {
2949 ParseNode
* ctorExpr
= BinaryLeft(newExpr
);
2950 ParseNode
* ctorArgs
= BinaryRight(newExpr
);
2951 ParseNode
* bufArg
= ListHead(ctorArgs
);
2952 if (!bufArg
|| NextNode(bufArg
) != nullptr) {
2953 return m
.fail(ctorExpr
,
2954 "array view constructor takes exactly one argument");
2957 if (!IsUseOfName(bufArg
, bufferName
)) {
2958 return m
.failName(bufArg
, "argument to array view constructor must be '%s'",
2965 static bool CheckNewArrayView(ModuleValidatorShared
& m
,
2966 TaggedParserAtomIndex varName
,
2967 ParseNode
* newExpr
) {
2968 TaggedParserAtomIndex globalName
= m
.globalArgumentName();
2971 newExpr
, "cannot create array view without an asm.js global parameter");
2974 TaggedParserAtomIndex bufferName
= m
.bufferArgumentName();
2976 return m
.fail(newExpr
,
2977 "cannot create array view without an asm.js heap parameter");
2980 ParseNode
* ctorExpr
= BinaryLeft(newExpr
);
2982 TaggedParserAtomIndex field
;
2984 if (ctorExpr
->isKind(ParseNodeKind::DotExpr
)) {
2985 ParseNode
* base
= DotBase(ctorExpr
);
2987 if (!IsUseOfName(base
, globalName
)) {
2988 return m
.failName(base
, "expecting '%s.*Array", globalName
);
2991 field
= DotMember(ctorExpr
);
2992 if (!IsArrayViewCtorName(m
, field
, &type
)) {
2993 return m
.fail(ctorExpr
, "could not match typed array name");
2996 if (!ctorExpr
->isKind(ParseNodeKind::Name
)) {
2997 return m
.fail(ctorExpr
,
2998 "expecting name of imported array view constructor");
3001 TaggedParserAtomIndex globalName
= ctorExpr
->as
<NameNode
>().name();
3002 const ModuleValidatorShared::Global
* global
= m
.lookupGlobal(globalName
);
3004 return m
.failName(ctorExpr
, "%s not found in module global scope",
3008 if (global
->which() != ModuleValidatorShared::Global::ArrayViewCtor
) {
3009 return m
.failName(ctorExpr
,
3010 "%s must be an imported array view constructor",
3014 type
= global
->viewType();
3017 if (!CheckNewArrayViewArgs(m
, newExpr
, bufferName
)) {
3021 return m
.addArrayView(varName
, type
, field
);
3024 static bool CheckGlobalMathImport(ModuleValidatorShared
& m
, ParseNode
* initNode
,
3025 TaggedParserAtomIndex varName
,
3026 TaggedParserAtomIndex field
) {
3027 // Math builtin, with the form glob.Math.[[builtin]]
3028 ModuleValidatorShared::MathBuiltin mathBuiltin
;
3029 if (!m
.lookupStandardLibraryMathName(field
, &mathBuiltin
)) {
3030 return m
.failName(initNode
, "'%s' is not a standard Math builtin", field
);
3033 switch (mathBuiltin
.kind
) {
3034 case ModuleValidatorShared::MathBuiltin::Function
:
3035 return m
.addMathBuiltinFunction(varName
, mathBuiltin
.u
.func
, field
);
3036 case ModuleValidatorShared::MathBuiltin::Constant
:
3037 return m
.addMathBuiltinConstant(varName
, mathBuiltin
.u
.cst
, field
);
3041 MOZ_CRASH("unexpected or uninitialized math builtin type");
3044 static bool CheckGlobalDotImport(ModuleValidatorShared
& m
,
3045 TaggedParserAtomIndex varName
,
3046 ParseNode
* initNode
) {
3047 ParseNode
* base
= DotBase(initNode
);
3048 TaggedParserAtomIndex field
= DotMember(initNode
);
3050 if (base
->isKind(ParseNodeKind::DotExpr
)) {
3051 ParseNode
* global
= DotBase(base
);
3052 TaggedParserAtomIndex math
= DotMember(base
);
3054 TaggedParserAtomIndex globalName
= m
.globalArgumentName();
3057 base
, "import statement requires the module have a stdlib parameter");
3060 if (!IsUseOfName(global
, globalName
)) {
3061 if (global
->isKind(ParseNodeKind::DotExpr
)) {
3062 return m
.failName(base
,
3063 "imports can have at most two dot accesses "
3064 "(e.g. %s.Math.sin)",
3067 return m
.failName(base
, "expecting %s.*", globalName
);
3070 if (math
== TaggedParserAtomIndex::WellKnown::Math()) {
3071 return CheckGlobalMathImport(m
, initNode
, varName
, field
);
3073 return m
.failName(base
, "expecting %s.Math", globalName
);
3076 if (!base
->isKind(ParseNodeKind::Name
)) {
3077 return m
.fail(base
, "expected name of variable or parameter");
3080 auto baseName
= base
->as
<NameNode
>().name();
3081 if (baseName
== m
.globalArgumentName()) {
3082 if (field
== TaggedParserAtomIndex::WellKnown::NaN()) {
3083 return m
.addGlobalConstant(varName
, GenericNaN(), field
);
3085 if (field
== TaggedParserAtomIndex::WellKnown::Infinity()) {
3086 return m
.addGlobalConstant(varName
, PositiveInfinity
<double>(), field
);
3090 if (IsArrayViewCtorName(m
, field
, &type
)) {
3091 return m
.addArrayViewCtor(varName
, type
, field
);
3095 initNode
, "'%s' is not a standard constant or typed array name", field
);
3098 if (baseName
!= m
.importArgumentName()) {
3099 return m
.fail(base
, "expected global or import name");
3102 return m
.addFFI(varName
, field
);
3105 static bool CheckModuleGlobal(ModuleValidatorShared
& m
, ParseNode
* decl
,
3107 if (!decl
->isKind(ParseNodeKind::AssignExpr
)) {
3108 return m
.fail(decl
, "module import needs initializer");
3110 AssignmentNode
* assignNode
= &decl
->as
<AssignmentNode
>();
3112 ParseNode
* var
= assignNode
->left();
3114 if (!var
->isKind(ParseNodeKind::Name
)) {
3115 return m
.fail(var
, "import variable is not a plain name");
3118 TaggedParserAtomIndex varName
= var
->as
<NameNode
>().name();
3119 if (!CheckModuleLevelName(m
, var
, varName
)) {
3123 ParseNode
* initNode
= assignNode
->right();
3125 if (IsNumericLiteral(m
, initNode
)) {
3126 return CheckGlobalVariableInitConstant(m
, varName
, initNode
, isConst
);
3129 if (initNode
->isKind(ParseNodeKind::BitOrExpr
) ||
3130 initNode
->isKind(ParseNodeKind::PosExpr
) ||
3131 initNode
->isKind(ParseNodeKind::CallExpr
)) {
3132 return CheckGlobalVariableInitImport(m
, varName
, initNode
, isConst
);
3135 if (initNode
->isKind(ParseNodeKind::NewExpr
)) {
3136 return CheckNewArrayView(m
, varName
, initNode
);
3139 if (initNode
->isKind(ParseNodeKind::DotExpr
)) {
3140 return CheckGlobalDotImport(m
, varName
, initNode
);
3143 return m
.fail(initNode
, "unsupported import expression");
3146 template <typename Unit
>
3147 static bool CheckModuleProcessingDirectives(ModuleValidator
<Unit
>& m
) {
3148 auto& ts
= m
.parser().tokenStream
;
3151 if (!ts
.matchToken(&matched
, TokenKind::String
,
3152 TokenStreamShared::SlashIsRegExp
)) {
3159 if (!IsIgnoredDirectiveName(ts
.anyCharsAccess().currentToken().atom())) {
3160 return m
.failCurrentOffset("unsupported processing directive");
3164 if (!ts
.getToken(&tt
)) {
3167 if (tt
!= TokenKind::Semi
) {
3168 return m
.failCurrentOffset("expected semicolon after string literal");
3173 template <typename Unit
>
3174 static bool CheckModuleGlobals(ModuleValidator
<Unit
>& m
) {
3177 if (!ParseVarOrConstStatement(m
.parser(), &varStmt
)) {
3183 for (ParseNode
* var
= VarListHead(varStmt
); var
; var
= NextNode(var
)) {
3184 if (!CheckModuleGlobal(m
, var
,
3185 varStmt
->isKind(ParseNodeKind::ConstDecl
))) {
3194 static bool ArgFail(FunctionValidatorShared
& f
, TaggedParserAtomIndex argName
,
3196 return f
.failName(stmt
,
3197 "expecting argument type declaration for '%s' of the "
3198 "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'",
3202 static bool CheckArgumentType(FunctionValidatorShared
& f
, ParseNode
* stmt
,
3203 TaggedParserAtomIndex name
, Type
* type
) {
3204 if (!stmt
|| !IsExpressionStatement(stmt
)) {
3205 return ArgFail(f
, name
, stmt
? stmt
: f
.fn());
3208 ParseNode
* initNode
= ExpressionStatementExpr(stmt
);
3209 if (!initNode
->isKind(ParseNodeKind::AssignExpr
)) {
3210 return ArgFail(f
, name
, stmt
);
3213 ParseNode
* argNode
= BinaryLeft(initNode
);
3214 ParseNode
* coercionNode
= BinaryRight(initNode
);
3216 if (!IsUseOfName(argNode
, name
)) {
3217 return ArgFail(f
, name
, stmt
);
3220 ParseNode
* coercedExpr
;
3221 if (!CheckTypeAnnotation(f
.m(), coercionNode
, type
, &coercedExpr
)) {
3225 if (!type
->isArgType()) {
3226 return f
.failName(stmt
, "invalid type for argument '%s'", name
);
3229 if (!IsUseOfName(coercedExpr
, name
)) {
3230 return ArgFail(f
, name
, stmt
);
3236 static bool CheckProcessingDirectives(ModuleValidatorShared
& m
,
3237 ParseNode
** stmtIter
) {
3238 ParseNode
* stmt
= *stmtIter
;
3240 while (stmt
&& IsIgnoredDirective(stmt
)) {
3241 stmt
= NextNode(stmt
);
3248 static bool CheckArguments(FunctionValidatorShared
& f
, ParseNode
** stmtIter
,
3249 ValTypeVector
* argTypes
) {
3250 ParseNode
* stmt
= *stmtIter
;
3252 unsigned numFormals
;
3253 ParseNode
* argpn
= FunctionFormalParametersList(f
.fn(), &numFormals
);
3255 for (unsigned i
= 0; i
< numFormals
;
3256 i
++, argpn
= NextNode(argpn
), stmt
= NextNode(stmt
)) {
3257 TaggedParserAtomIndex name
;
3258 if (!CheckArgument(f
.m(), argpn
, &name
)) {
3263 if (!CheckArgumentType(f
, stmt
, name
, &type
)) {
3267 if (!argTypes
->append(type
.canonicalToValType())) {
3271 if (!f
.addLocal(argpn
, name
, type
)) {
3280 static bool IsLiteralOrConst(FunctionValidatorShared
& f
, ParseNode
* pn
,
3282 if (pn
->isKind(ParseNodeKind::Name
)) {
3283 const ModuleValidatorShared::Global
* global
=
3284 f
.lookupGlobal(pn
->as
<NameNode
>().name());
3286 global
->which() != ModuleValidatorShared::Global::ConstantLiteral
) {
3290 *lit
= global
->constLiteralValue();
3294 if (!IsNumericLiteral(f
.m(), pn
)) {
3298 *lit
= ExtractNumericLiteral(f
.m(), pn
);
3302 static bool CheckFinalReturn(FunctionValidatorShared
& f
,
3303 ParseNode
* lastNonEmptyStmt
) {
3304 if (!f
.encoder().writeOp(Op::End
)) {
3308 if (!f
.hasAlreadyReturned()) {
3309 f
.setReturnedType(Nothing());
3313 if (!lastNonEmptyStmt
->isKind(ParseNodeKind::ReturnStmt
) &&
3315 return f
.fail(lastNonEmptyStmt
,
3316 "void incompatible with previous return type");
3322 static bool CheckVariable(FunctionValidatorShared
& f
, ParseNode
* decl
,
3323 ValTypeVector
* types
, Vector
<NumLit
>* inits
) {
3324 if (!decl
->isKind(ParseNodeKind::AssignExpr
)) {
3326 decl
, "var '%s' needs explicit type declaration via an initial value",
3327 decl
->as
<NameNode
>().name());
3329 AssignmentNode
* assignNode
= &decl
->as
<AssignmentNode
>();
3331 ParseNode
* var
= assignNode
->left();
3333 if (!var
->isKind(ParseNodeKind::Name
)) {
3334 return f
.fail(var
, "local variable is not a plain name");
3337 TaggedParserAtomIndex name
= var
->as
<NameNode
>().name();
3339 if (!CheckIdentifier(f
.m(), var
, name
)) {
3343 ParseNode
* initNode
= assignNode
->right();
3346 if (!IsLiteralOrConst(f
, initNode
, &lit
)) {
3348 var
, "var '%s' initializer must be literal or const literal", name
);
3352 return f
.failName(var
, "var '%s' initializer out of range", name
);
3355 Type type
= Type::canonicalize(Type::lit(lit
));
3357 return f
.addLocal(var
, name
, type
) &&
3358 types
->append(type
.canonicalToValType()) && inits
->append(lit
);
3361 static bool CheckVariables(FunctionValidatorShared
& f
, ParseNode
** stmtIter
) {
3362 ParseNode
* stmt
= *stmtIter
;
3364 uint32_t firstVar
= f
.numLocals();
3366 ValTypeVector types
;
3367 Vector
<NumLit
> inits(f
.fc());
3369 for (; stmt
&& stmt
->isKind(ParseNodeKind::VarStmt
);
3370 stmt
= NextNonEmptyStatement(stmt
)) {
3371 for (ParseNode
* var
= VarListHead(stmt
); var
; var
= NextNode(var
)) {
3372 if (!CheckVariable(f
, var
, &types
, &inits
)) {
3378 MOZ_ASSERT(f
.encoder().empty());
3380 if (!EncodeLocalEntries(f
.encoder(), types
)) {
3384 for (uint32_t i
= 0; i
< inits
.length(); i
++) {
3385 NumLit lit
= inits
[i
];
3386 if (lit
.isZeroBits()) {
3389 if (!f
.writeConstExpr(lit
)) {
3392 if (!f
.encoder().writeOp(Op::LocalSet
)) {
3395 if (!f
.encoder().writeVarU32(firstVar
+ i
)) {
3404 template <typename Unit
>
3405 static bool CheckExpr(FunctionValidator
<Unit
>& f
, ParseNode
* expr
, Type
* type
);
3407 template <typename Unit
>
3408 static bool CheckNumericLiteral(FunctionValidator
<Unit
>& f
, ParseNode
* num
,
3410 NumLit lit
= ExtractNumericLiteral(f
.m(), num
);
3412 return f
.fail(num
, "numeric literal out of representable integer range");
3414 *type
= Type::lit(lit
);
3415 return f
.writeConstExpr(lit
);
3418 static bool CheckVarRef(FunctionValidatorShared
& f
, ParseNode
* varRef
,
3420 TaggedParserAtomIndex name
= varRef
->as
<NameNode
>().name();
3422 if (const FunctionValidatorShared::Local
* local
= f
.lookupLocal(name
)) {
3423 if (!f
.encoder().writeOp(Op::LocalGet
)) {
3426 if (!f
.encoder().writeVarU32(local
->slot
)) {
3429 *type
= local
->type
;
3433 if (const ModuleValidatorShared::Global
* global
= f
.lookupGlobal(name
)) {
3434 switch (global
->which()) {
3435 case ModuleValidatorShared::Global::ConstantLiteral
:
3436 *type
= global
->varOrConstType();
3437 return f
.writeConstExpr(global
->constLiteralValue());
3438 case ModuleValidatorShared::Global::ConstantImport
:
3439 case ModuleValidatorShared::Global::Variable
: {
3440 *type
= global
->varOrConstType();
3441 return f
.encoder().writeOp(Op::GlobalGet
) &&
3442 f
.encoder().writeVarU32(global
->varOrConstIndex());
3444 case ModuleValidatorShared::Global::Function
:
3445 case ModuleValidatorShared::Global::FFI
:
3446 case ModuleValidatorShared::Global::MathBuiltinFunction
:
3447 case ModuleValidatorShared::Global::Table
:
3448 case ModuleValidatorShared::Global::ArrayView
:
3449 case ModuleValidatorShared::Global::ArrayViewCtor
:
3452 return f
.failName(varRef
,
3453 "'%s' may not be accessed by ordinary expressions", name
);
3456 return f
.failName(varRef
, "'%s' not found in local or asm.js module scope",
3460 static inline bool IsLiteralOrConstInt(FunctionValidatorShared
& f
,
3461 ParseNode
* pn
, uint32_t* u32
) {
3463 if (!IsLiteralOrConst(f
, pn
, &lit
)) {
3467 return IsLiteralInt(lit
, u32
);
3470 static const int32_t NoMask
= -1;
3472 template <typename Unit
>
3473 static bool CheckArrayAccess(FunctionValidator
<Unit
>& f
, ParseNode
* viewName
,
3474 ParseNode
* indexExpr
, Scalar::Type
* viewType
) {
3475 if (!viewName
->isKind(ParseNodeKind::Name
)) {
3476 return f
.fail(viewName
,
3477 "base of array access must be a typed array view name");
3480 const ModuleValidatorShared::Global
* global
=
3481 f
.lookupGlobal(viewName
->as
<NameNode
>().name());
3482 if (!global
|| global
->which() != ModuleValidatorShared::Global::ArrayView
) {
3483 return f
.fail(viewName
,
3484 "base of array access must be a typed array view name");
3487 *viewType
= global
->viewType();
3490 if (IsLiteralOrConstInt(f
, indexExpr
, &index
)) {
3491 uint64_t byteOffset
= uint64_t(index
) << TypedArrayShift(*viewType
);
3492 uint64_t width
= TypedArrayElemSize(*viewType
);
3493 if (!f
.m().tryConstantAccess(byteOffset
, width
)) {
3494 return f
.fail(indexExpr
, "constant index out of range");
3497 return f
.writeInt32Lit(byteOffset
);
3500 // Mask off the low bits to account for the clearing effect of a right shift
3501 // followed by the left shift implicit in the array access. E.g., H32[i>>2]
3502 // loses the low two bits.
3503 int32_t mask
= ~(TypedArrayElemSize(*viewType
) - 1);
3505 if (indexExpr
->isKind(ParseNodeKind::RshExpr
)) {
3506 ParseNode
* shiftAmountNode
= BitwiseRight(indexExpr
);
3509 if (!IsLiteralInt(f
.m(), shiftAmountNode
, &shift
)) {
3510 return f
.failf(shiftAmountNode
, "shift amount must be constant");
3513 unsigned requiredShift
= TypedArrayShift(*viewType
);
3514 if (shift
!= requiredShift
) {
3515 return f
.failf(shiftAmountNode
, "shift amount must be %u", requiredShift
);
3518 ParseNode
* pointerNode
= BitwiseLeft(indexExpr
);
3521 if (!CheckExpr(f
, pointerNode
, &pointerType
)) {
3525 if (!pointerType
.isIntish()) {
3526 return f
.failf(pointerNode
, "%s is not a subtype of int",
3527 pointerType
.toChars());
3530 // For legacy scalar access compatibility, accept Int8/Uint8 accesses
3532 if (TypedArrayShift(*viewType
) != 0) {
3535 "index expression isn't shifted; must be an Int8/Uint8 access");
3538 MOZ_ASSERT(mask
== NoMask
);
3540 ParseNode
* pointerNode
= indexExpr
;
3543 if (!CheckExpr(f
, pointerNode
, &pointerType
)) {
3546 if (!pointerType
.isInt()) {
3547 return f
.failf(pointerNode
, "%s is not a subtype of int",
3548 pointerType
.toChars());
3552 // Don't generate the mask op if there is no need for it which could happen
3553 // for a shift of zero.
3554 if (mask
!= NoMask
) {
3555 return f
.writeInt32Lit(mask
) && f
.encoder().writeOp(Op::I32And
);
3561 static bool WriteArrayAccessFlags(FunctionValidatorShared
& f
,
3562 Scalar::Type viewType
) {
3563 // asm.js only has naturally-aligned accesses.
3564 size_t align
= TypedArrayElemSize(viewType
);
3565 MOZ_ASSERT(IsPowerOfTwo(align
));
3566 if (!f
.encoder().writeFixedU8(CeilingLog2(align
))) {
3570 // asm.js doesn't have constant offsets, so just encode a 0.
3571 return f
.encoder().writeVarU32(0);
3574 template <typename Unit
>
3575 static bool CheckLoadArray(FunctionValidator
<Unit
>& f
, ParseNode
* elem
,
3577 Scalar::Type viewType
;
3579 if (!CheckArrayAccess(f
, ElemBase(elem
), ElemIndex(elem
), &viewType
)) {
3585 if (!f
.encoder().writeOp(Op::I32Load8S
)) return false;
3588 if (!f
.encoder().writeOp(Op::I32Load8U
)) return false;
3591 if (!f
.encoder().writeOp(Op::I32Load16S
)) return false;
3593 case Scalar::Uint16
:
3594 if (!f
.encoder().writeOp(Op::I32Load16U
)) return false;
3596 case Scalar::Uint32
:
3598 if (!f
.encoder().writeOp(Op::I32Load
)) return false;
3600 case Scalar::Float32
:
3601 if (!f
.encoder().writeOp(Op::F32Load
)) return false;
3603 case Scalar::Float64
:
3604 if (!f
.encoder().writeOp(Op::F64Load
)) return false;
3607 MOZ_CRASH("unexpected scalar type");
3615 case Scalar::Uint16
:
3616 case Scalar::Uint32
:
3617 *type
= Type::Intish
;
3619 case Scalar::Float32
:
3620 *type
= Type::MaybeFloat
;
3622 case Scalar::Float64
:
3623 *type
= Type::MaybeDouble
;
3626 MOZ_CRASH("Unexpected array type");
3629 return WriteArrayAccessFlags(f
, viewType
);
3632 template <typename Unit
>
3633 static bool CheckStoreArray(FunctionValidator
<Unit
>& f
, ParseNode
* lhs
,
3634 ParseNode
* rhs
, Type
* type
) {
3635 Scalar::Type viewType
;
3636 if (!CheckArrayAccess(f
, ElemBase(lhs
), ElemIndex(lhs
), &viewType
)) {
3641 if (!CheckExpr(f
, rhs
, &rhsType
)) {
3650 case Scalar::Uint16
:
3651 case Scalar::Uint32
:
3652 if (!rhsType
.isIntish()) {
3653 return f
.failf(lhs
, "%s is not a subtype of intish", rhsType
.toChars());
3656 case Scalar::Float32
:
3657 if (!rhsType
.isMaybeDouble() && !rhsType
.isFloatish()) {
3658 return f
.failf(lhs
, "%s is not a subtype of double? or floatish",
3662 case Scalar::Float64
:
3663 if (!rhsType
.isMaybeFloat() && !rhsType
.isMaybeDouble()) {
3664 return f
.failf(lhs
, "%s is not a subtype of float? or double?",
3669 MOZ_CRASH("Unexpected view type");
3675 if (!f
.encoder().writeOp(MozOp::I32TeeStore8
)) {
3680 case Scalar::Uint16
:
3681 if (!f
.encoder().writeOp(MozOp::I32TeeStore16
)) {
3686 case Scalar::Uint32
:
3687 if (!f
.encoder().writeOp(MozOp::I32TeeStore
)) {
3691 case Scalar::Float32
:
3692 if (rhsType
.isFloatish()) {
3693 if (!f
.encoder().writeOp(MozOp::F32TeeStore
)) {
3697 if (!f
.encoder().writeOp(MozOp::F64TeeStoreF32
)) {
3702 case Scalar::Float64
:
3703 if (rhsType
.isFloatish()) {
3704 if (!f
.encoder().writeOp(MozOp::F32TeeStoreF64
)) {
3708 if (!f
.encoder().writeOp(MozOp::F64TeeStore
)) {
3714 MOZ_CRASH("unexpected scalar type");
3717 if (!WriteArrayAccessFlags(f
, viewType
)) {
3725 template <typename Unit
>
3726 static bool CheckAssignName(FunctionValidator
<Unit
>& f
, ParseNode
* lhs
,
3727 ParseNode
* rhs
, Type
* type
) {
3728 TaggedParserAtomIndex name
= lhs
->as
<NameNode
>().name();
3730 if (const FunctionValidatorShared::Local
* lhsVar
= f
.lookupLocal(name
)) {
3732 if (!CheckExpr(f
, rhs
, &rhsType
)) {
3736 if (!f
.encoder().writeOp(Op::LocalTee
)) {
3739 if (!f
.encoder().writeVarU32(lhsVar
->slot
)) {
3743 if (!(rhsType
<= lhsVar
->type
)) {
3744 return f
.failf(lhs
, "%s is not a subtype of %s", rhsType
.toChars(),
3745 lhsVar
->type
.toChars());
3751 if (const ModuleValidatorShared::Global
* global
= f
.lookupGlobal(name
)) {
3752 if (global
->which() != ModuleValidatorShared::Global::Variable
) {
3753 return f
.failName(lhs
, "'%s' is not a mutable variable", name
);
3757 if (!CheckExpr(f
, rhs
, &rhsType
)) {
3761 Type globType
= global
->varOrConstType();
3762 if (!(rhsType
<= globType
)) {
3763 return f
.failf(lhs
, "%s is not a subtype of %s", rhsType
.toChars(),
3764 globType
.toChars());
3766 if (!f
.encoder().writeOp(MozOp::TeeGlobal
)) {
3769 if (!f
.encoder().writeVarU32(global
->varOrConstIndex())) {
3777 return f
.failName(lhs
, "'%s' not found in local or asm.js module scope",
3781 template <typename Unit
>
3782 static bool CheckAssign(FunctionValidator
<Unit
>& f
, ParseNode
* assign
,
3784 MOZ_ASSERT(assign
->isKind(ParseNodeKind::AssignExpr
));
3786 ParseNode
* lhs
= BinaryLeft(assign
);
3787 ParseNode
* rhs
= BinaryRight(assign
);
3789 if (lhs
->getKind() == ParseNodeKind::ElemExpr
) {
3790 return CheckStoreArray(f
, lhs
, rhs
, type
);
3793 if (lhs
->getKind() == ParseNodeKind::Name
) {
3794 return CheckAssignName(f
, lhs
, rhs
, type
);
3799 "left-hand side of assignment must be a variable or array access");
3802 template <typename Unit
>
3803 static bool CheckMathIMul(FunctionValidator
<Unit
>& f
, ParseNode
* call
,
3805 if (CallArgListLength(call
) != 2) {
3806 return f
.fail(call
, "Math.imul must be passed 2 arguments");
3809 ParseNode
* lhs
= CallArgList(call
);
3810 ParseNode
* rhs
= NextNode(lhs
);
3813 if (!CheckExpr(f
, lhs
, &lhsType
)) {
3818 if (!CheckExpr(f
, rhs
, &rhsType
)) {
3822 if (!lhsType
.isIntish()) {
3823 return f
.failf(lhs
, "%s is not a subtype of intish", lhsType
.toChars());
3825 if (!rhsType
.isIntish()) {
3826 return f
.failf(rhs
, "%s is not a subtype of intish", rhsType
.toChars());
3829 *type
= Type::Signed
;
3830 return f
.encoder().writeOp(Op::I32Mul
);
3833 template <typename Unit
>
3834 static bool CheckMathClz32(FunctionValidator
<Unit
>& f
, ParseNode
* call
,
3836 if (CallArgListLength(call
) != 1) {
3837 return f
.fail(call
, "Math.clz32 must be passed 1 argument");
3840 ParseNode
* arg
= CallArgList(call
);
3843 if (!CheckExpr(f
, arg
, &argType
)) {
3847 if (!argType
.isIntish()) {
3848 return f
.failf(arg
, "%s is not a subtype of intish", argType
.toChars());
3851 *type
= Type::Fixnum
;
3852 return f
.encoder().writeOp(Op::I32Clz
);
3855 template <typename Unit
>
3856 static bool CheckMathAbs(FunctionValidator
<Unit
>& f
, ParseNode
* call
,
3858 if (CallArgListLength(call
) != 1) {
3859 return f
.fail(call
, "Math.abs must be passed 1 argument");
3862 ParseNode
* arg
= CallArgList(call
);
3865 if (!CheckExpr(f
, arg
, &argType
)) {
3869 if (argType
.isSigned()) {
3870 *type
= Type::Unsigned
;
3871 return f
.encoder().writeOp(MozOp::I32Abs
);
3874 if (argType
.isMaybeDouble()) {
3875 *type
= Type::Double
;
3876 return f
.encoder().writeOp(Op::F64Abs
);
3879 if (argType
.isMaybeFloat()) {
3880 *type
= Type::Floatish
;
3881 return f
.encoder().writeOp(Op::F32Abs
);
3884 return f
.failf(call
, "%s is not a subtype of signed, float? or double?",
3888 template <typename Unit
>
3889 static bool CheckMathSqrt(FunctionValidator
<Unit
>& f
, ParseNode
* call
,
3891 if (CallArgListLength(call
) != 1) {
3892 return f
.fail(call
, "Math.sqrt must be passed 1 argument");
3895 ParseNode
* arg
= CallArgList(call
);
3898 if (!CheckExpr(f
, arg
, &argType
)) {
3902 if (argType
.isMaybeDouble()) {
3903 *type
= Type::Double
;
3904 return f
.encoder().writeOp(Op::F64Sqrt
);
3907 if (argType
.isMaybeFloat()) {
3908 *type
= Type::Floatish
;
3909 return f
.encoder().writeOp(Op::F32Sqrt
);
3912 return f
.failf(call
, "%s is neither a subtype of double? nor float?",
3916 template <typename Unit
>
3917 static bool CheckMathMinMax(FunctionValidator
<Unit
>& f
, ParseNode
* callNode
,
3918 bool isMax
, Type
* type
) {
3919 if (CallArgListLength(callNode
) < 2) {
3920 return f
.fail(callNode
, "Math.min/max must be passed at least 2 arguments");
3923 ParseNode
* firstArg
= CallArgList(callNode
);
3925 if (!CheckExpr(f
, firstArg
, &firstType
)) {
3930 MozOp mozOp
= MozOp::Limit
;
3931 if (firstType
.isMaybeDouble()) {
3932 *type
= Type::Double
;
3933 firstType
= Type::MaybeDouble
;
3934 op
= isMax
? Op::F64Max
: Op::F64Min
;
3935 } else if (firstType
.isMaybeFloat()) {
3936 *type
= Type::Float
;
3937 firstType
= Type::MaybeFloat
;
3938 op
= isMax
? Op::F32Max
: Op::F32Min
;
3939 } else if (firstType
.isSigned()) {
3940 *type
= Type::Signed
;
3941 firstType
= Type::Signed
;
3942 mozOp
= isMax
? MozOp::I32Max
: MozOp::I32Min
;
3944 return f
.failf(firstArg
, "%s is not a subtype of double?, float? or signed",
3945 firstType
.toChars());
3948 unsigned numArgs
= CallArgListLength(callNode
);
3949 ParseNode
* nextArg
= NextNode(firstArg
);
3950 for (unsigned i
= 1; i
< numArgs
; i
++, nextArg
= NextNode(nextArg
)) {
3952 if (!CheckExpr(f
, nextArg
, &nextType
)) {
3955 if (!(nextType
<= firstType
)) {
3956 return f
.failf(nextArg
, "%s is not a subtype of %s", nextType
.toChars(),
3957 firstType
.toChars());
3960 if (op
!= Op::Limit
) {
3961 if (!f
.encoder().writeOp(op
)) {
3965 if (!f
.encoder().writeOp(mozOp
)) {
3974 using CheckArgType
= bool (*)(FunctionValidatorShared
& f
, ParseNode
* argNode
,
3977 template <CheckArgType checkArg
, typename Unit
>
3978 static bool CheckCallArgs(FunctionValidator
<Unit
>& f
, ParseNode
* callNode
,
3979 ValTypeVector
* args
) {
3980 ParseNode
* argNode
= CallArgList(callNode
);
3981 for (unsigned i
= 0; i
< CallArgListLength(callNode
);
3982 i
++, argNode
= NextNode(argNode
)) {
3984 if (!CheckExpr(f
, argNode
, &type
)) {
3988 if (!checkArg(f
, argNode
, type
)) {
3992 if (!args
->append(Type::canonicalize(type
).canonicalToValType())) {
3999 static bool CheckSignatureAgainstExisting(ModuleValidatorShared
& m
,
4000 ParseNode
* usepn
, const FuncType
& sig
,
4001 const FuncType
& existing
) {
4002 if (!FuncType::strictlyEquals(sig
, existing
)) {
4003 return m
.failf(usepn
, "incompatible argument types to function");
4008 template <typename Unit
>
4009 static bool CheckFunctionSignature(ModuleValidator
<Unit
>& m
, ParseNode
* usepn
,
4010 FuncType
&& sig
, TaggedParserAtomIndex name
,
4011 ModuleValidatorShared::Func
** func
) {
4012 if (sig
.args().length() > MaxParams
) {
4013 return m
.failf(usepn
, "too many parameters");
4016 ModuleValidatorShared::Func
* existing
= m
.lookupFuncDef(name
);
4018 if (!CheckModuleLevelName(m
, usepn
, name
)) {
4021 return m
.addFuncDef(name
, usepn
->pn_pos
.begin
, std::move(sig
), func
);
4024 const FuncType
& existingSig
=
4025 m
.env().types
->type(existing
->sigIndex()).funcType();
4027 if (!CheckSignatureAgainstExisting(m
, usepn
, sig
, existingSig
)) {
4035 static bool CheckIsArgType(FunctionValidatorShared
& f
, ParseNode
* argNode
,
4037 if (!type
.isArgType()) {
4038 return f
.failf(argNode
, "%s is not a subtype of int, float, or double",
4044 template <typename Unit
>
4045 static bool CheckInternalCall(FunctionValidator
<Unit
>& f
, ParseNode
* callNode
,
4046 TaggedParserAtomIndex calleeName
, Type ret
,
4048 MOZ_ASSERT(ret
.isCanonical());
4051 if (!CheckCallArgs
<CheckIsArgType
>(f
, callNode
, &args
)) {
4055 ValTypeVector results
;
4056 Maybe
<ValType
> retType
= ret
.canonicalToReturnType();
4057 if (retType
&& !results
.append(retType
.ref())) {
4061 FuncType
sig(std::move(args
), std::move(results
));
4063 ModuleValidatorShared::Func
* callee
;
4064 if (!CheckFunctionSignature(f
.m(), callNode
, std::move(sig
), calleeName
,
4069 if (!f
.writeCall(callNode
, MozOp::OldCallDirect
)) {
4073 if (!f
.encoder().writeVarU32(callee
->funcDefIndex())) {
4077 *type
= Type::ret(ret
);
4081 template <typename Unit
>
4082 static bool CheckFuncPtrTableAgainstExisting(ModuleValidator
<Unit
>& m
,
4084 TaggedParserAtomIndex name
,
4085 FuncType
&& sig
, unsigned mask
,
4086 uint32_t* tableIndex
) {
4087 if (const ModuleValidatorShared::Global
* existing
= m
.lookupGlobal(name
)) {
4088 if (existing
->which() != ModuleValidatorShared::Global::Table
) {
4089 return m
.failName(usepn
, "'%s' is not a function-pointer table", name
);
4092 ModuleValidatorShared::Table
& table
= m
.table(existing
->tableIndex());
4093 if (mask
!= table
.mask()) {
4094 return m
.failf(usepn
, "mask does not match previous value (%u)",
4098 if (!CheckSignatureAgainstExisting(
4099 m
, usepn
, sig
, m
.env().types
->type(table
.sigIndex()).funcType())) {
4103 *tableIndex
= existing
->tableIndex();
4107 if (!CheckModuleLevelName(m
, usepn
, name
)) {
4111 return m
.declareFuncPtrTable(std::move(sig
), name
, usepn
->pn_pos
.begin
, mask
,
4115 template <typename Unit
>
4116 static bool CheckFuncPtrCall(FunctionValidator
<Unit
>& f
, ParseNode
* callNode
,
4117 Type ret
, Type
* type
) {
4118 MOZ_ASSERT(ret
.isCanonical());
4120 ParseNode
* callee
= CallCallee(callNode
);
4121 ParseNode
* tableNode
= ElemBase(callee
);
4122 ParseNode
* indexExpr
= ElemIndex(callee
);
4124 if (!tableNode
->isKind(ParseNodeKind::Name
)) {
4125 return f
.fail(tableNode
, "expecting name of function-pointer array");
4128 TaggedParserAtomIndex name
= tableNode
->as
<NameNode
>().name();
4129 if (const ModuleValidatorShared::Global
* existing
= f
.lookupGlobal(name
)) {
4130 if (existing
->which() != ModuleValidatorShared::Global::Table
) {
4132 tableNode
, "'%s' is not the name of a function-pointer array", name
);
4136 if (!indexExpr
->isKind(ParseNodeKind::BitAndExpr
)) {
4137 return f
.fail(indexExpr
,
4138 "function-pointer table index expression needs & mask");
4141 ParseNode
* indexNode
= BitwiseLeft(indexExpr
);
4142 ParseNode
* maskNode
= BitwiseRight(indexExpr
);
4145 if (!IsLiteralInt(f
.m(), maskNode
, &mask
) || mask
== UINT32_MAX
||
4146 !IsPowerOfTwo(mask
+ 1)) {
4147 return f
.fail(maskNode
,
4148 "function-pointer table index mask value must be a power of "
4153 if (!CheckExpr(f
, indexNode
, &indexType
)) {
4157 if (!indexType
.isIntish()) {
4158 return f
.failf(indexNode
, "%s is not a subtype of intish",
4159 indexType
.toChars());
4163 if (!CheckCallArgs
<CheckIsArgType
>(f
, callNode
, &args
)) {
4167 ValTypeVector results
;
4168 Maybe
<ValType
> retType
= ret
.canonicalToReturnType();
4169 if (retType
&& !results
.append(retType
.ref())) {
4173 FuncType
sig(std::move(args
), std::move(results
));
4175 uint32_t tableIndex
;
4176 if (!CheckFuncPtrTableAgainstExisting(f
.m(), tableNode
, name
, std::move(sig
),
4177 mask
, &tableIndex
)) {
4181 if (!f
.writeCall(callNode
, MozOp::OldCallIndirect
)) {
4186 if (!f
.encoder().writeVarU32(f
.m().table(tableIndex
).sigIndex())) {
4190 *type
= Type::ret(ret
);
4194 static bool CheckIsExternType(FunctionValidatorShared
& f
, ParseNode
* argNode
,
4196 if (!type
.isExtern()) {
4197 return f
.failf(argNode
, "%s is not a subtype of extern", type
.toChars());
4202 template <typename Unit
>
4203 static bool CheckFFICall(FunctionValidator
<Unit
>& f
, ParseNode
* callNode
,
4204 unsigned ffiIndex
, Type ret
, Type
* type
) {
4205 MOZ_ASSERT(ret
.isCanonical());
4207 TaggedParserAtomIndex calleeName
=
4208 CallCallee(callNode
)->as
<NameNode
>().name();
4210 if (ret
.isFloat()) {
4211 return f
.fail(callNode
, "FFI calls can't return float");
4215 if (!CheckCallArgs
<CheckIsExternType
>(f
, callNode
, &args
)) {
4219 ValTypeVector results
;
4220 Maybe
<ValType
> retType
= ret
.canonicalToReturnType();
4221 if (retType
&& !results
.append(retType
.ref())) {
4225 FuncType
sig(std::move(args
), std::move(results
));
4227 uint32_t importIndex
;
4228 if (!f
.m().declareImport(calleeName
, std::move(sig
), ffiIndex
,
4233 if (!f
.writeCall(callNode
, Op::Call
)) {
4237 if (!f
.encoder().writeVarU32(importIndex
)) {
4241 *type
= Type::ret(ret
);
4245 static bool CheckFloatCoercionArg(FunctionValidatorShared
& f
,
4246 ParseNode
* inputNode
, Type inputType
) {
4247 if (inputType
.isMaybeDouble()) {
4248 return f
.encoder().writeOp(Op::F32DemoteF64
);
4250 if (inputType
.isSigned()) {
4251 return f
.encoder().writeOp(Op::F32ConvertI32S
);
4253 if (inputType
.isUnsigned()) {
4254 return f
.encoder().writeOp(Op::F32ConvertI32U
);
4256 if (inputType
.isFloatish()) {
4260 return f
.failf(inputNode
,
4261 "%s is not a subtype of signed, unsigned, double? or floatish",
4262 inputType
.toChars());
4265 template <typename Unit
>
4266 static bool CheckCoercedCall(FunctionValidator
<Unit
>& f
, ParseNode
* call
,
4267 Type ret
, Type
* type
);
4269 template <typename Unit
>
4270 static bool CheckCoercionArg(FunctionValidator
<Unit
>& f
, ParseNode
* arg
,
4271 Type expected
, Type
* type
) {
4272 MOZ_ASSERT(expected
.isCanonicalValType());
4274 if (arg
->isKind(ParseNodeKind::CallExpr
)) {
4275 return CheckCoercedCall(f
, arg
, expected
, type
);
4279 if (!CheckExpr(f
, arg
, &argType
)) {
4283 if (expected
.isFloat()) {
4284 if (!CheckFloatCoercionArg(f
, arg
, argType
)) {
4288 MOZ_CRASH("not call coercions");
4291 *type
= Type::ret(expected
);
4295 template <typename Unit
>
4296 static bool CheckMathFRound(FunctionValidator
<Unit
>& f
, ParseNode
* callNode
,
4298 if (CallArgListLength(callNode
) != 1) {
4299 return f
.fail(callNode
, "Math.fround must be passed 1 argument");
4302 ParseNode
* argNode
= CallArgList(callNode
);
4304 if (!CheckCoercionArg(f
, argNode
, Type::Float
, &argType
)) {
4308 MOZ_ASSERT(argType
== Type::Float
);
4309 *type
= Type::Float
;
4313 template <typename Unit
>
4314 static bool CheckMathBuiltinCall(FunctionValidator
<Unit
>& f
,
4315 ParseNode
* callNode
,
4316 AsmJSMathBuiltinFunction func
, Type
* type
) {
4320 MozOp mozf64
= MozOp::Limit
;
4322 case AsmJSMathBuiltin_imul
:
4323 return CheckMathIMul(f
, callNode
, type
);
4324 case AsmJSMathBuiltin_clz32
:
4325 return CheckMathClz32(f
, callNode
, type
);
4326 case AsmJSMathBuiltin_abs
:
4327 return CheckMathAbs(f
, callNode
, type
);
4328 case AsmJSMathBuiltin_sqrt
:
4329 return CheckMathSqrt(f
, callNode
, type
);
4330 case AsmJSMathBuiltin_fround
:
4331 return CheckMathFRound(f
, callNode
, type
);
4332 case AsmJSMathBuiltin_min
:
4333 return CheckMathMinMax(f
, callNode
, /* isMax = */ false, type
);
4334 case AsmJSMathBuiltin_max
:
4335 return CheckMathMinMax(f
, callNode
, /* isMax = */ true, type
);
4336 case AsmJSMathBuiltin_ceil
:
4341 case AsmJSMathBuiltin_floor
:
4346 case AsmJSMathBuiltin_sin
:
4348 if (!f
.m().alwaysUseFdlibm()) {
4349 mozf64
= MozOp::F64SinNative
;
4351 mozf64
= MozOp::F64SinFdlibm
;
4353 f32
= Op::Unreachable
;
4355 case AsmJSMathBuiltin_cos
:
4357 if (!f
.m().alwaysUseFdlibm()) {
4358 mozf64
= MozOp::F64CosNative
;
4360 mozf64
= MozOp::F64CosFdlibm
;
4362 f32
= Op::Unreachable
;
4364 case AsmJSMathBuiltin_tan
:
4366 if (!f
.m().alwaysUseFdlibm()) {
4367 mozf64
= MozOp::F64TanNative
;
4369 mozf64
= MozOp::F64TanFdlibm
;
4371 f32
= Op::Unreachable
;
4373 case AsmJSMathBuiltin_asin
:
4375 mozf64
= MozOp::F64Asin
;
4376 f32
= Op::Unreachable
;
4378 case AsmJSMathBuiltin_acos
:
4380 mozf64
= MozOp::F64Acos
;
4381 f32
= Op::Unreachable
;
4383 case AsmJSMathBuiltin_atan
:
4385 mozf64
= MozOp::F64Atan
;
4386 f32
= Op::Unreachable
;
4388 case AsmJSMathBuiltin_exp
:
4390 mozf64
= MozOp::F64Exp
;
4391 f32
= Op::Unreachable
;
4393 case AsmJSMathBuiltin_log
:
4395 mozf64
= MozOp::F64Log
;
4396 f32
= Op::Unreachable
;
4398 case AsmJSMathBuiltin_pow
:
4400 mozf64
= MozOp::F64Pow
;
4401 f32
= Op::Unreachable
;
4403 case AsmJSMathBuiltin_atan2
:
4405 mozf64
= MozOp::F64Atan2
;
4406 f32
= Op::Unreachable
;
4409 MOZ_CRASH("unexpected mathBuiltin function");
4412 unsigned actualArity
= CallArgListLength(callNode
);
4413 if (actualArity
!= arity
) {
4414 return f
.failf(callNode
, "call passed %u arguments, expected %u",
4415 actualArity
, arity
);
4418 if (!f
.prepareCall(callNode
)) {
4423 ParseNode
* argNode
= CallArgList(callNode
);
4424 if (!CheckExpr(f
, argNode
, &firstType
)) {
4428 if (!firstType
.isMaybeFloat() && !firstType
.isMaybeDouble()) {
4431 "arguments to math call should be a subtype of double? or float?");
4434 bool opIsDouble
= firstType
.isMaybeDouble();
4435 if (!opIsDouble
&& f32
== Op::Unreachable
) {
4436 return f
.fail(callNode
, "math builtin cannot be used as float");
4441 argNode
= NextNode(argNode
);
4442 if (!CheckExpr(f
, argNode
, &secondType
)) {
4446 if (firstType
.isMaybeDouble() && !secondType
.isMaybeDouble()) {
4449 "both arguments to math builtin call should be the same type");
4451 if (firstType
.isMaybeFloat() && !secondType
.isMaybeFloat()) {
4454 "both arguments to math builtin call should be the same type");
4459 if (f64
!= Op::Limit
) {
4460 if (!f
.encoder().writeOp(f64
)) {
4464 if (!f
.encoder().writeOp(mozf64
)) {
4469 if (!f
.encoder().writeOp(f32
)) {
4474 *type
= opIsDouble
? Type::Double
: Type::Floatish
;
4478 template <typename Unit
>
4479 static bool CheckUncoercedCall(FunctionValidator
<Unit
>& f
, ParseNode
* expr
,
4481 MOZ_ASSERT(expr
->isKind(ParseNodeKind::CallExpr
));
4483 const ModuleValidatorShared::Global
* global
;
4484 if (IsCallToGlobal(f
.m(), expr
, &global
) && global
->isMathFunction()) {
4485 return CheckMathBuiltinCall(f
, expr
, global
->mathBuiltinFunction(), type
);
4490 "all function calls must be calls to standard lib math functions,"
4491 " ignored (via f(); or comma-expression), coerced to signed (via f()|0),"
4492 " coerced to float (via fround(f())), or coerced to double (via +f())");
4495 static bool CoerceResult(FunctionValidatorShared
& f
, ParseNode
* expr
,
4496 Type expected
, Type actual
, Type
* type
) {
4497 MOZ_ASSERT(expected
.isCanonical());
4499 // At this point, the bytecode resembles this:
4500 // | the thing we wanted to coerce | current position |>
4501 switch (expected
.which()) {
4503 if (!actual
.isVoid()) {
4504 if (!f
.encoder().writeOp(Op::Drop
)) {
4510 if (!actual
.isIntish()) {
4511 return f
.failf(expr
, "%s is not a subtype of intish", actual
.toChars());
4515 if (!CheckFloatCoercionArg(f
, expr
, actual
)) {
4520 if (actual
.isMaybeDouble()) {
4521 // No conversion necessary.
4522 } else if (actual
.isMaybeFloat()) {
4523 if (!f
.encoder().writeOp(Op::F64PromoteF32
)) {
4526 } else if (actual
.isSigned()) {
4527 if (!f
.encoder().writeOp(Op::F64ConvertI32S
)) {
4530 } else if (actual
.isUnsigned()) {
4531 if (!f
.encoder().writeOp(Op::F64ConvertI32U
)) {
4536 expr
, "%s is not a subtype of double?, float?, signed or unsigned",
4541 MOZ_CRASH("unexpected uncoerced result type");
4544 *type
= Type::ret(expected
);
4548 template <typename Unit
>
4549 static bool CheckCoercedMathBuiltinCall(FunctionValidator
<Unit
>& f
,
4550 ParseNode
* callNode
,
4551 AsmJSMathBuiltinFunction func
, Type ret
,
4554 if (!CheckMathBuiltinCall(f
, callNode
, func
, &actual
)) {
4557 return CoerceResult(f
, callNode
, ret
, actual
, type
);
4560 template <typename Unit
>
4561 static bool CheckCoercedCall(FunctionValidator
<Unit
>& f
, ParseNode
* call
,
4562 Type ret
, Type
* type
) {
4563 MOZ_ASSERT(ret
.isCanonical());
4565 AutoCheckRecursionLimit
recursion(f
.fc());
4566 if (!recursion
.checkDontReport(f
.fc())) {
4567 return f
.m().failOverRecursed();
4570 if (IsNumericLiteral(f
.m(), call
)) {
4571 NumLit lit
= ExtractNumericLiteral(f
.m(), call
);
4572 if (!f
.writeConstExpr(lit
)) {
4575 return CoerceResult(f
, call
, ret
, Type::lit(lit
), type
);
4578 ParseNode
* callee
= CallCallee(call
);
4580 if (callee
->isKind(ParseNodeKind::ElemExpr
)) {
4581 return CheckFuncPtrCall(f
, call
, ret
, type
);
4584 if (!callee
->isKind(ParseNodeKind::Name
)) {
4585 return f
.fail(callee
, "unexpected callee expression type");
4588 TaggedParserAtomIndex calleeName
= callee
->as
<NameNode
>().name();
4590 if (const ModuleValidatorShared::Global
* global
=
4591 f
.lookupGlobal(calleeName
)) {
4592 switch (global
->which()) {
4593 case ModuleValidatorShared::Global::FFI
:
4594 return CheckFFICall(f
, call
, global
->ffiIndex(), ret
, type
);
4595 case ModuleValidatorShared::Global::MathBuiltinFunction
:
4596 return CheckCoercedMathBuiltinCall(
4597 f
, call
, global
->mathBuiltinFunction(), ret
, type
);
4598 case ModuleValidatorShared::Global::ConstantLiteral
:
4599 case ModuleValidatorShared::Global::ConstantImport
:
4600 case ModuleValidatorShared::Global::Variable
:
4601 case ModuleValidatorShared::Global::Table
:
4602 case ModuleValidatorShared::Global::ArrayView
:
4603 case ModuleValidatorShared::Global::ArrayViewCtor
:
4604 return f
.failName(callee
, "'%s' is not callable function", calleeName
);
4605 case ModuleValidatorShared::Global::Function
:
4610 return CheckInternalCall(f
, call
, calleeName
, ret
, type
);
4613 template <typename Unit
>
4614 static bool CheckPos(FunctionValidator
<Unit
>& f
, ParseNode
* pos
, Type
* type
) {
4615 MOZ_ASSERT(pos
->isKind(ParseNodeKind::PosExpr
));
4616 ParseNode
* operand
= UnaryKid(pos
);
4618 if (operand
->isKind(ParseNodeKind::CallExpr
)) {
4619 return CheckCoercedCall(f
, operand
, Type::Double
, type
);
4623 if (!CheckExpr(f
, operand
, &actual
)) {
4627 return CoerceResult(f
, operand
, Type::Double
, actual
, type
);
4630 template <typename Unit
>
4631 static bool CheckNot(FunctionValidator
<Unit
>& f
, ParseNode
* expr
, Type
* type
) {
4632 MOZ_ASSERT(expr
->isKind(ParseNodeKind::NotExpr
));
4633 ParseNode
* operand
= UnaryKid(expr
);
4636 if (!CheckExpr(f
, operand
, &operandType
)) {
4640 if (!operandType
.isInt()) {
4641 return f
.failf(operand
, "%s is not a subtype of int",
4642 operandType
.toChars());
4646 return f
.encoder().writeOp(Op::I32Eqz
);
4649 template <typename Unit
>
4650 static bool CheckNeg(FunctionValidator
<Unit
>& f
, ParseNode
* expr
, Type
* type
) {
4651 MOZ_ASSERT(expr
->isKind(ParseNodeKind::NegExpr
));
4652 ParseNode
* operand
= UnaryKid(expr
);
4655 if (!CheckExpr(f
, operand
, &operandType
)) {
4659 if (operandType
.isInt()) {
4660 *type
= Type::Intish
;
4661 return f
.encoder().writeOp(MozOp::I32Neg
);
4664 if (operandType
.isMaybeDouble()) {
4665 *type
= Type::Double
;
4666 return f
.encoder().writeOp(Op::F64Neg
);
4669 if (operandType
.isMaybeFloat()) {
4670 *type
= Type::Floatish
;
4671 return f
.encoder().writeOp(Op::F32Neg
);
4674 return f
.failf(operand
, "%s is not a subtype of int, float? or double?",
4675 operandType
.toChars());
4678 template <typename Unit
>
4679 static bool CheckCoerceToInt(FunctionValidator
<Unit
>& f
, ParseNode
* expr
,
4681 MOZ_ASSERT(expr
->isKind(ParseNodeKind::BitNotExpr
));
4682 ParseNode
* operand
= UnaryKid(expr
);
4685 if (!CheckExpr(f
, operand
, &operandType
)) {
4689 if (operandType
.isMaybeDouble() || operandType
.isMaybeFloat()) {
4690 *type
= Type::Signed
;
4692 operandType
.isMaybeDouble() ? Op::I32TruncF64S
: Op::I32TruncF32S
;
4693 return f
.encoder().writeOp(opcode
);
4696 if (!operandType
.isIntish()) {
4697 return f
.failf(operand
, "%s is not a subtype of double?, float? or intish",
4698 operandType
.toChars());
4701 *type
= Type::Signed
;
4705 template <typename Unit
>
4706 static bool CheckBitNot(FunctionValidator
<Unit
>& f
, ParseNode
* neg
,
4708 MOZ_ASSERT(neg
->isKind(ParseNodeKind::BitNotExpr
));
4709 ParseNode
* operand
= UnaryKid(neg
);
4711 if (operand
->isKind(ParseNodeKind::BitNotExpr
)) {
4712 return CheckCoerceToInt(f
, operand
, type
);
4716 if (!CheckExpr(f
, operand
, &operandType
)) {
4720 if (!operandType
.isIntish()) {
4721 return f
.failf(operand
, "%s is not a subtype of intish",
4722 operandType
.toChars());
4725 if (!f
.encoder().writeOp(MozOp::I32BitNot
)) {
4729 *type
= Type::Signed
;
4733 template <typename Unit
>
4734 static bool CheckAsExprStatement(FunctionValidator
<Unit
>& f
,
4735 ParseNode
* exprStmt
);
4737 template <typename Unit
>
4738 static bool CheckComma(FunctionValidator
<Unit
>& f
, ParseNode
* comma
,
4740 MOZ_ASSERT(comma
->isKind(ParseNodeKind::CommaExpr
));
4741 ParseNode
* operands
= ListHead(comma
);
4743 // The block depth isn't taken into account here, because a comma list can't
4744 // contain breaks and continues and nested control flow structures.
4745 if (!f
.encoder().writeOp(Op::Block
)) {
4750 if (!f
.encoder().writePatchableFixedU7(&typeAt
)) {
4754 ParseNode
* pn
= operands
;
4755 for (; NextNode(pn
); pn
= NextNode(pn
)) {
4756 if (!CheckAsExprStatement(f
, pn
)) {
4761 if (!CheckExpr(f
, pn
, type
)) {
4765 f
.encoder().patchFixedU7(typeAt
, uint8_t(type
->toWasmBlockSignatureType()));
4767 return f
.encoder().writeOp(Op::End
);
4770 template <typename Unit
>
4771 static bool CheckConditional(FunctionValidator
<Unit
>& f
, ParseNode
* ternary
,
4773 MOZ_ASSERT(ternary
->isKind(ParseNodeKind::ConditionalExpr
));
4775 ParseNode
* cond
= TernaryKid1(ternary
);
4776 ParseNode
* thenExpr
= TernaryKid2(ternary
);
4777 ParseNode
* elseExpr
= TernaryKid3(ternary
);
4780 if (!CheckExpr(f
, cond
, &condType
)) {
4784 if (!condType
.isInt()) {
4785 return f
.failf(cond
, "%s is not a subtype of int", condType
.toChars());
4789 if (!f
.pushIf(&typeAt
)) {
4794 if (!CheckExpr(f
, thenExpr
, &thenType
)) {
4798 if (!f
.switchToElse()) {
4803 if (!CheckExpr(f
, elseExpr
, &elseType
)) {
4807 if (thenType
.isInt() && elseType
.isInt()) {
4809 } else if (thenType
.isDouble() && elseType
.isDouble()) {
4810 *type
= Type::Double
;
4811 } else if (thenType
.isFloat() && elseType
.isFloat()) {
4812 *type
= Type::Float
;
4816 "then/else branches of conditional must both produce int, float, "
4817 "double, current types are %s and %s",
4818 thenType
.toChars(), elseType
.toChars());
4821 return f
.popIf(typeAt
, type
->toWasmBlockSignatureType());
4824 template <typename Unit
>
4825 static bool IsValidIntMultiplyConstant(ModuleValidator
<Unit
>& m
,
4827 if (!IsNumericLiteral(m
, expr
)) {
4831 NumLit lit
= ExtractNumericLiteral(m
, expr
);
4832 switch (lit
.which()) {
4833 case NumLit::Fixnum
:
4834 case NumLit::NegativeInt
:
4835 if (Abs(lit
.toInt32()) < (uint32_t(1) << 20)) {
4839 case NumLit::BigUnsigned
:
4840 case NumLit::Double
:
4842 case NumLit::OutOfRangeInt
:
4846 MOZ_CRASH("Bad literal");
4849 template <typename Unit
>
4850 static bool CheckMultiply(FunctionValidator
<Unit
>& f
, ParseNode
* star
,
4852 MOZ_ASSERT(star
->isKind(ParseNodeKind::MulExpr
));
4853 ParseNode
* lhs
= MultiplyLeft(star
);
4854 ParseNode
* rhs
= MultiplyRight(star
);
4857 if (!CheckExpr(f
, lhs
, &lhsType
)) {
4862 if (!CheckExpr(f
, rhs
, &rhsType
)) {
4866 if (lhsType
.isInt() && rhsType
.isInt()) {
4867 if (!IsValidIntMultiplyConstant(f
.m(), lhs
) &&
4868 !IsValidIntMultiplyConstant(f
.m(), rhs
)) {
4871 "one arg to int multiply must be a small (-2^20, 2^20) int literal");
4873 *type
= Type::Intish
;
4874 return f
.encoder().writeOp(Op::I32Mul
);
4877 if (lhsType
.isMaybeDouble() && rhsType
.isMaybeDouble()) {
4878 *type
= Type::Double
;
4879 return f
.encoder().writeOp(Op::F64Mul
);
4882 if (lhsType
.isMaybeFloat() && rhsType
.isMaybeFloat()) {
4883 *type
= Type::Floatish
;
4884 return f
.encoder().writeOp(Op::F32Mul
);
4888 star
, "multiply operands must be both int, both double? or both float?");
4891 template <typename Unit
>
4892 static bool CheckAddOrSub(FunctionValidator
<Unit
>& f
, ParseNode
* expr
,
4893 Type
* type
, unsigned* numAddOrSubOut
= nullptr) {
4894 AutoCheckRecursionLimit
recursion(f
.fc());
4895 if (!recursion
.checkDontReport(f
.fc())) {
4896 return f
.m().failOverRecursed();
4899 MOZ_ASSERT(expr
->isKind(ParseNodeKind::AddExpr
) ||
4900 expr
->isKind(ParseNodeKind::SubExpr
));
4901 ParseNode
* lhs
= AddSubLeft(expr
);
4902 ParseNode
* rhs
= AddSubRight(expr
);
4904 Type lhsType
, rhsType
;
4905 unsigned lhsNumAddOrSub
, rhsNumAddOrSub
;
4907 if (lhs
->isKind(ParseNodeKind::AddExpr
) ||
4908 lhs
->isKind(ParseNodeKind::SubExpr
)) {
4909 if (!CheckAddOrSub(f
, lhs
, &lhsType
, &lhsNumAddOrSub
)) {
4912 if (lhsType
== Type::Intish
) {
4913 lhsType
= Type::Int
;
4916 if (!CheckExpr(f
, lhs
, &lhsType
)) {
4922 if (rhs
->isKind(ParseNodeKind::AddExpr
) ||
4923 rhs
->isKind(ParseNodeKind::SubExpr
)) {
4924 if (!CheckAddOrSub(f
, rhs
, &rhsType
, &rhsNumAddOrSub
)) {
4927 if (rhsType
== Type::Intish
) {
4928 rhsType
= Type::Int
;
4931 if (!CheckExpr(f
, rhs
, &rhsType
)) {
4937 unsigned numAddOrSub
= lhsNumAddOrSub
+ rhsNumAddOrSub
+ 1;
4938 if (numAddOrSub
> (1 << 20)) {
4939 return f
.fail(expr
, "too many + or - without intervening coercion");
4942 if (lhsType
.isInt() && rhsType
.isInt()) {
4943 if (!f
.encoder().writeOp(
4944 expr
->isKind(ParseNodeKind::AddExpr
) ? Op::I32Add
: Op::I32Sub
)) {
4947 *type
= Type::Intish
;
4948 } else if (lhsType
.isMaybeDouble() && rhsType
.isMaybeDouble()) {
4949 if (!f
.encoder().writeOp(
4950 expr
->isKind(ParseNodeKind::AddExpr
) ? Op::F64Add
: Op::F64Sub
)) {
4953 *type
= Type::Double
;
4954 } else if (lhsType
.isMaybeFloat() && rhsType
.isMaybeFloat()) {
4955 if (!f
.encoder().writeOp(
4956 expr
->isKind(ParseNodeKind::AddExpr
) ? Op::F32Add
: Op::F32Sub
)) {
4959 *type
= Type::Floatish
;
4963 "operands to + or - must both be int, float? or double?, got %s and %s",
4964 lhsType
.toChars(), rhsType
.toChars());
4967 if (numAddOrSubOut
) {
4968 *numAddOrSubOut
= numAddOrSub
;
4973 template <typename Unit
>
4974 static bool CheckDivOrMod(FunctionValidator
<Unit
>& f
, ParseNode
* expr
,
4976 MOZ_ASSERT(expr
->isKind(ParseNodeKind::DivExpr
) ||
4977 expr
->isKind(ParseNodeKind::ModExpr
));
4979 ParseNode
* lhs
= DivOrModLeft(expr
);
4980 ParseNode
* rhs
= DivOrModRight(expr
);
4982 Type lhsType
, rhsType
;
4983 if (!CheckExpr(f
, lhs
, &lhsType
)) {
4986 if (!CheckExpr(f
, rhs
, &rhsType
)) {
4990 if (lhsType
.isMaybeDouble() && rhsType
.isMaybeDouble()) {
4991 *type
= Type::Double
;
4992 if (expr
->isKind(ParseNodeKind::DivExpr
)) {
4993 return f
.encoder().writeOp(Op::F64Div
);
4995 return f
.encoder().writeOp(MozOp::F64Mod
);
4998 if (lhsType
.isMaybeFloat() && rhsType
.isMaybeFloat()) {
4999 *type
= Type::Floatish
;
5000 if (expr
->isKind(ParseNodeKind::DivExpr
)) {
5001 return f
.encoder().writeOp(Op::F32Div
);
5003 return f
.fail(expr
, "modulo cannot receive float arguments");
5006 if (lhsType
.isSigned() && rhsType
.isSigned()) {
5007 *type
= Type::Intish
;
5008 return f
.encoder().writeOp(
5009 expr
->isKind(ParseNodeKind::DivExpr
) ? Op::I32DivS
: Op::I32RemS
);
5012 if (lhsType
.isUnsigned() && rhsType
.isUnsigned()) {
5013 *type
= Type::Intish
;
5014 return f
.encoder().writeOp(
5015 expr
->isKind(ParseNodeKind::DivExpr
) ? Op::I32DivU
: Op::I32RemU
);
5020 "arguments to / or %% must both be double?, float?, signed, or unsigned; "
5021 "%s and %s are given",
5022 lhsType
.toChars(), rhsType
.toChars());
5025 template <typename Unit
>
5026 static bool CheckComparison(FunctionValidator
<Unit
>& f
, ParseNode
* comp
,
5028 MOZ_ASSERT(comp
->isKind(ParseNodeKind::LtExpr
) ||
5029 comp
->isKind(ParseNodeKind::LeExpr
) ||
5030 comp
->isKind(ParseNodeKind::GtExpr
) ||
5031 comp
->isKind(ParseNodeKind::GeExpr
) ||
5032 comp
->isKind(ParseNodeKind::EqExpr
) ||
5033 comp
->isKind(ParseNodeKind::NeExpr
));
5035 ParseNode
* lhs
= ComparisonLeft(comp
);
5036 ParseNode
* rhs
= ComparisonRight(comp
);
5038 Type lhsType
, rhsType
;
5039 if (!CheckExpr(f
, lhs
, &lhsType
)) {
5042 if (!CheckExpr(f
, rhs
, &rhsType
)) {
5046 if (!(lhsType
.isSigned() && rhsType
.isSigned()) &&
5047 !(lhsType
.isUnsigned() && rhsType
.isUnsigned()) &&
5048 !(lhsType
.isDouble() && rhsType
.isDouble()) &&
5049 !(lhsType
.isFloat() && rhsType
.isFloat())) {
5050 return f
.failf(comp
,
5051 "arguments to a comparison must both be signed, unsigned, "
5052 "floats or doubles; "
5053 "%s and %s are given",
5054 lhsType
.toChars(), rhsType
.toChars());
5058 if (lhsType
.isSigned() && rhsType
.isSigned()) {
5059 switch (comp
->getKind()) {
5060 case ParseNodeKind::EqExpr
:
5063 case ParseNodeKind::NeExpr
:
5066 case ParseNodeKind::LtExpr
:
5069 case ParseNodeKind::LeExpr
:
5072 case ParseNodeKind::GtExpr
:
5075 case ParseNodeKind::GeExpr
:
5079 MOZ_CRASH("unexpected comparison op");
5081 } else if (lhsType
.isUnsigned() && rhsType
.isUnsigned()) {
5082 switch (comp
->getKind()) {
5083 case ParseNodeKind::EqExpr
:
5086 case ParseNodeKind::NeExpr
:
5089 case ParseNodeKind::LtExpr
:
5092 case ParseNodeKind::LeExpr
:
5095 case ParseNodeKind::GtExpr
:
5098 case ParseNodeKind::GeExpr
:
5102 MOZ_CRASH("unexpected comparison op");
5104 } else if (lhsType
.isDouble()) {
5105 switch (comp
->getKind()) {
5106 case ParseNodeKind::EqExpr
:
5109 case ParseNodeKind::NeExpr
:
5112 case ParseNodeKind::LtExpr
:
5115 case ParseNodeKind::LeExpr
:
5118 case ParseNodeKind::GtExpr
:
5121 case ParseNodeKind::GeExpr
:
5125 MOZ_CRASH("unexpected comparison op");
5127 } else if (lhsType
.isFloat()) {
5128 switch (comp
->getKind()) {
5129 case ParseNodeKind::EqExpr
:
5132 case ParseNodeKind::NeExpr
:
5135 case ParseNodeKind::LtExpr
:
5138 case ParseNodeKind::LeExpr
:
5141 case ParseNodeKind::GtExpr
:
5144 case ParseNodeKind::GeExpr
:
5148 MOZ_CRASH("unexpected comparison op");
5151 MOZ_CRASH("unexpected type");
5155 return f
.encoder().writeOp(stmt
);
5158 template <typename Unit
>
5159 static bool CheckBitwise(FunctionValidator
<Unit
>& f
, ParseNode
* bitwise
,
5161 ParseNode
* lhs
= BitwiseLeft(bitwise
);
5162 ParseNode
* rhs
= BitwiseRight(bitwise
);
5164 int32_t identityElement
;
5166 switch (bitwise
->getKind()) {
5167 case ParseNodeKind::BitOrExpr
:
5168 identityElement
= 0;
5169 onlyOnRight
= false;
5170 *type
= Type::Signed
;
5172 case ParseNodeKind::BitAndExpr
:
5173 identityElement
= -1;
5174 onlyOnRight
= false;
5175 *type
= Type::Signed
;
5177 case ParseNodeKind::BitXorExpr
:
5178 identityElement
= 0;
5179 onlyOnRight
= false;
5180 *type
= Type::Signed
;
5182 case ParseNodeKind::LshExpr
:
5183 identityElement
= 0;
5185 *type
= Type::Signed
;
5187 case ParseNodeKind::RshExpr
:
5188 identityElement
= 0;
5190 *type
= Type::Signed
;
5192 case ParseNodeKind::UrshExpr
:
5193 identityElement
= 0;
5195 *type
= Type::Unsigned
;
5198 MOZ_CRASH("not a bitwise op");
5202 if (!onlyOnRight
&& IsLiteralInt(f
.m(), lhs
, &i
) &&
5203 i
== uint32_t(identityElement
)) {
5205 if (!CheckExpr(f
, rhs
, &rhsType
)) {
5208 if (!rhsType
.isIntish()) {
5209 return f
.failf(bitwise
, "%s is not a subtype of intish",
5215 if (IsLiteralInt(f
.m(), rhs
, &i
) && i
== uint32_t(identityElement
)) {
5216 if (bitwise
->isKind(ParseNodeKind::BitOrExpr
) &&
5217 lhs
->isKind(ParseNodeKind::CallExpr
)) {
5218 return CheckCoercedCall(f
, lhs
, Type::Int
, type
);
5222 if (!CheckExpr(f
, lhs
, &lhsType
)) {
5225 if (!lhsType
.isIntish()) {
5226 return f
.failf(bitwise
, "%s is not a subtype of intish",
5233 if (!CheckExpr(f
, lhs
, &lhsType
)) {
5238 if (!CheckExpr(f
, rhs
, &rhsType
)) {
5242 if (!lhsType
.isIntish()) {
5243 return f
.failf(lhs
, "%s is not a subtype of intish", lhsType
.toChars());
5245 if (!rhsType
.isIntish()) {
5246 return f
.failf(rhs
, "%s is not a subtype of intish", rhsType
.toChars());
5249 switch (bitwise
->getKind()) {
5250 case ParseNodeKind::BitOrExpr
:
5251 if (!f
.encoder().writeOp(Op::I32Or
)) return false;
5253 case ParseNodeKind::BitAndExpr
:
5254 if (!f
.encoder().writeOp(Op::I32And
)) return false;
5256 case ParseNodeKind::BitXorExpr
:
5257 if (!f
.encoder().writeOp(Op::I32Xor
)) return false;
5259 case ParseNodeKind::LshExpr
:
5260 if (!f
.encoder().writeOp(Op::I32Shl
)) return false;
5262 case ParseNodeKind::RshExpr
:
5263 if (!f
.encoder().writeOp(Op::I32ShrS
)) return false;
5265 case ParseNodeKind::UrshExpr
:
5266 if (!f
.encoder().writeOp(Op::I32ShrU
)) return false;
5269 MOZ_CRASH("not a bitwise op");
5275 template <typename Unit
>
5276 static bool CheckExpr(FunctionValidator
<Unit
>& f
, ParseNode
* expr
, Type
* type
) {
5277 AutoCheckRecursionLimit
recursion(f
.fc());
5278 if (!recursion
.checkDontReport(f
.fc())) {
5279 return f
.m().failOverRecursed();
5282 if (IsNumericLiteral(f
.m(), expr
)) {
5283 return CheckNumericLiteral(f
, expr
, type
);
5286 switch (expr
->getKind()) {
5287 case ParseNodeKind::Name
:
5288 return CheckVarRef(f
, expr
, type
);
5289 case ParseNodeKind::ElemExpr
:
5290 return CheckLoadArray(f
, expr
, type
);
5291 case ParseNodeKind::AssignExpr
:
5292 return CheckAssign(f
, expr
, type
);
5293 case ParseNodeKind::PosExpr
:
5294 return CheckPos(f
, expr
, type
);
5295 case ParseNodeKind::NotExpr
:
5296 return CheckNot(f
, expr
, type
);
5297 case ParseNodeKind::NegExpr
:
5298 return CheckNeg(f
, expr
, type
);
5299 case ParseNodeKind::BitNotExpr
:
5300 return CheckBitNot(f
, expr
, type
);
5301 case ParseNodeKind::CommaExpr
:
5302 return CheckComma(f
, expr
, type
);
5303 case ParseNodeKind::ConditionalExpr
:
5304 return CheckConditional(f
, expr
, type
);
5305 case ParseNodeKind::MulExpr
:
5306 return CheckMultiply(f
, expr
, type
);
5307 case ParseNodeKind::CallExpr
:
5308 return CheckUncoercedCall(f
, expr
, type
);
5310 case ParseNodeKind::AddExpr
:
5311 case ParseNodeKind::SubExpr
:
5312 return CheckAddOrSub(f
, expr
, type
);
5314 case ParseNodeKind::DivExpr
:
5315 case ParseNodeKind::ModExpr
:
5316 return CheckDivOrMod(f
, expr
, type
);
5318 case ParseNodeKind::LtExpr
:
5319 case ParseNodeKind::LeExpr
:
5320 case ParseNodeKind::GtExpr
:
5321 case ParseNodeKind::GeExpr
:
5322 case ParseNodeKind::EqExpr
:
5323 case ParseNodeKind::NeExpr
:
5324 return CheckComparison(f
, expr
, type
);
5326 case ParseNodeKind::BitOrExpr
:
5327 case ParseNodeKind::BitAndExpr
:
5328 case ParseNodeKind::BitXorExpr
:
5329 case ParseNodeKind::LshExpr
:
5330 case ParseNodeKind::RshExpr
:
5331 case ParseNodeKind::UrshExpr
:
5332 return CheckBitwise(f
, expr
, type
);
5337 return f
.fail(expr
, "unsupported expression");
5340 template <typename Unit
>
5341 static bool CheckStatement(FunctionValidator
<Unit
>& f
, ParseNode
* stmt
);
5343 template <typename Unit
>
5344 static bool CheckAsExprStatement(FunctionValidator
<Unit
>& f
, ParseNode
* expr
) {
5345 if (expr
->isKind(ParseNodeKind::CallExpr
)) {
5347 return CheckCoercedCall(f
, expr
, Type::Void
, &ignored
);
5351 if (!CheckExpr(f
, expr
, &resultType
)) {
5355 if (!resultType
.isVoid()) {
5356 if (!f
.encoder().writeOp(Op::Drop
)) {
5364 template <typename Unit
>
5365 static bool CheckExprStatement(FunctionValidator
<Unit
>& f
,
5366 ParseNode
* exprStmt
) {
5367 MOZ_ASSERT(exprStmt
->isKind(ParseNodeKind::ExpressionStmt
));
5368 return CheckAsExprStatement(f
, UnaryKid(exprStmt
));
5371 template <typename Unit
>
5372 static bool CheckLoopConditionOnEntry(FunctionValidator
<Unit
>& f
,
5375 if (IsLiteralInt(f
.m(), cond
, &maybeLit
) && maybeLit
) {
5380 if (!CheckExpr(f
, cond
, &condType
)) {
5383 if (!condType
.isInt()) {
5384 return f
.failf(cond
, "%s is not a subtype of int", condType
.toChars());
5387 if (!f
.encoder().writeOp(Op::I32Eqz
)) {
5391 // brIf (i32.eqz $f) $out
5392 return f
.writeBreakIf();
5395 template <typename Unit
>
5396 static bool CheckWhile(FunctionValidator
<Unit
>& f
, ParseNode
* whileStmt
,
5397 const LabelVector
* labels
= nullptr) {
5398 MOZ_ASSERT(whileStmt
->isKind(ParseNodeKind::WhileStmt
));
5399 ParseNode
* cond
= BinaryLeft(whileStmt
);
5400 ParseNode
* body
= BinaryRight(whileStmt
);
5402 // A while loop `while(#cond) #body` is equivalent to:
5403 // (block $after_loop
5405 // (brIf $after_loop (i32.eq 0 #cond))
5410 if (labels
&& !f
.addLabels(*labels
, 0, 1)) {
5414 if (!f
.pushLoop()) {
5418 if (!CheckLoopConditionOnEntry(f
, cond
)) {
5421 if (!CheckStatement(f
, body
)) {
5424 if (!f
.writeContinue()) {
5432 f
.removeLabels(*labels
);
5437 template <typename Unit
>
5438 static bool CheckFor(FunctionValidator
<Unit
>& f
, ParseNode
* forStmt
,
5439 const LabelVector
* labels
= nullptr) {
5440 MOZ_ASSERT(forStmt
->isKind(ParseNodeKind::ForStmt
));
5441 ParseNode
* forHead
= BinaryLeft(forStmt
);
5442 ParseNode
* body
= BinaryRight(forStmt
);
5444 if (!forHead
->isKind(ParseNodeKind::ForHead
)) {
5445 return f
.fail(forHead
, "unsupported for-loop statement");
5448 ParseNode
* maybeInit
= TernaryKid1(forHead
);
5449 ParseNode
* maybeCond
= TernaryKid2(forHead
);
5450 ParseNode
* maybeInc
= TernaryKid3(forHead
);
5452 // A for-loop `for (#init; #cond; #inc) #body` is equivalent to:
5453 // (block // depth X
5455 // (block $after_loop // depth X+1 (block)
5456 // (loop $loop_top // depth X+2 (loop)
5457 // (brIf $after (eq 0 #cond))
5458 // (block $after_body #body) // depth X+3
5464 // A break in the body should break out to $after_loop, i.e. depth + 1.
5465 // A continue in the body should break out to $after_body, i.e. depth + 3.
5466 if (labels
&& !f
.addLabels(*labels
, 1, 3)) {
5470 if (!f
.pushUnbreakableBlock()) {
5474 if (maybeInit
&& !CheckAsExprStatement(f
, maybeInit
)) {
5479 if (!f
.pushLoop()) {
5483 if (maybeCond
&& !CheckLoopConditionOnEntry(f
, maybeCond
)) {
5488 // Continuing in the body should just break out to the increment.
5489 if (!f
.pushContinuableBlock()) {
5492 if (!CheckStatement(f
, body
)) {
5495 if (!f
.popContinuableBlock()) {
5500 if (maybeInc
&& !CheckAsExprStatement(f
, maybeInc
)) {
5504 if (!f
.writeContinue()) {
5512 if (!f
.popUnbreakableBlock()) {
5517 f
.removeLabels(*labels
);
5523 template <typename Unit
>
5524 static bool CheckDoWhile(FunctionValidator
<Unit
>& f
, ParseNode
* whileStmt
,
5525 const LabelVector
* labels
= nullptr) {
5526 MOZ_ASSERT(whileStmt
->isKind(ParseNodeKind::DoWhileStmt
));
5527 ParseNode
* body
= BinaryLeft(whileStmt
);
5528 ParseNode
* cond
= BinaryRight(whileStmt
);
5530 // A do-while loop `do { #body } while (#cond)` is equivalent to:
5531 // (block $after_loop // depth X
5532 // (loop $top // depth X+1
5533 // (block #body) // depth X+2
5534 // (brIf #cond $top)
5537 // A break should break out of the entire loop, i.e. at depth 0.
5538 // A continue should break out to the condition, i.e. at depth 2.
5539 if (labels
&& !f
.addLabels(*labels
, 0, 2)) {
5543 if (!f
.pushLoop()) {
5548 // An unlabeled continue in the body should break out to the condition.
5549 if (!f
.pushContinuableBlock()) {
5552 if (!CheckStatement(f
, body
)) {
5555 if (!f
.popContinuableBlock()) {
5561 if (!CheckExpr(f
, cond
, &condType
)) {
5564 if (!condType
.isInt()) {
5565 return f
.failf(cond
, "%s is not a subtype of int", condType
.toChars());
5568 if (!f
.writeContinueIf()) {
5576 f
.removeLabels(*labels
);
5581 template <typename Unit
>
5582 static bool CheckStatementList(FunctionValidator
<Unit
>& f
, ParseNode
*,
5583 const LabelVector
* = nullptr);
5585 template <typename Unit
>
5586 static bool CheckLabel(FunctionValidator
<Unit
>& f
, ParseNode
* labeledStmt
) {
5587 MOZ_ASSERT(labeledStmt
->isKind(ParseNodeKind::LabelStmt
));
5590 ParseNode
* innermost
= labeledStmt
;
5592 if (!labels
.append(LabeledStatementLabel(innermost
))) {
5595 innermost
= LabeledStatementStatement(innermost
);
5596 } while (innermost
->getKind() == ParseNodeKind::LabelStmt
);
5598 switch (innermost
->getKind()) {
5599 case ParseNodeKind::ForStmt
:
5600 return CheckFor(f
, innermost
, &labels
);
5601 case ParseNodeKind::DoWhileStmt
:
5602 return CheckDoWhile(f
, innermost
, &labels
);
5603 case ParseNodeKind::WhileStmt
:
5604 return CheckWhile(f
, innermost
, &labels
);
5605 case ParseNodeKind::StatementList
:
5606 return CheckStatementList(f
, innermost
, &labels
);
5611 if (!f
.pushUnbreakableBlock(&labels
)) {
5615 if (!CheckStatement(f
, innermost
)) {
5619 return f
.popUnbreakableBlock(&labels
);
5622 template <typename Unit
>
5623 static bool CheckIf(FunctionValidator
<Unit
>& f
, ParseNode
* ifStmt
) {
5624 uint32_t numIfEnd
= 1;
5627 MOZ_ASSERT(ifStmt
->isKind(ParseNodeKind::IfStmt
));
5628 ParseNode
* cond
= TernaryKid1(ifStmt
);
5629 ParseNode
* thenStmt
= TernaryKid2(ifStmt
);
5630 ParseNode
* elseStmt
= TernaryKid3(ifStmt
);
5633 if (!CheckExpr(f
, cond
, &condType
)) {
5636 if (!condType
.isInt()) {
5637 return f
.failf(cond
, "%s is not a subtype of int", condType
.toChars());
5641 if (!f
.pushIf(&typeAt
)) {
5645 f
.setIfType(typeAt
, TypeCode::BlockVoid
);
5647 if (!CheckStatement(f
, thenStmt
)) {
5652 if (!f
.switchToElse()) {
5656 if (elseStmt
->isKind(ParseNodeKind::IfStmt
)) {
5658 if (numIfEnd
++ == UINT32_MAX
) {
5664 if (!CheckStatement(f
, elseStmt
)) {
5669 for (uint32_t i
= 0; i
!= numIfEnd
; ++i
) {
5678 static bool CheckCaseExpr(FunctionValidatorShared
& f
, ParseNode
* caseExpr
,
5680 if (!IsNumericLiteral(f
.m(), caseExpr
)) {
5681 return f
.fail(caseExpr
,
5682 "switch case expression must be an integer literal");
5685 NumLit lit
= ExtractNumericLiteral(f
.m(), caseExpr
);
5686 switch (lit
.which()) {
5687 case NumLit::Fixnum
:
5688 case NumLit::NegativeInt
:
5689 *value
= lit
.toInt32();
5691 case NumLit::OutOfRangeInt
:
5692 case NumLit::BigUnsigned
:
5693 return f
.fail(caseExpr
, "switch case expression out of integer range");
5694 case NumLit::Double
:
5696 return f
.fail(caseExpr
,
5697 "switch case expression must be an integer literal");
5703 static bool CheckDefaultAtEnd(FunctionValidatorShared
& f
, ParseNode
* stmt
) {
5704 for (; stmt
; stmt
= NextNode(stmt
)) {
5705 if (IsDefaultCase(stmt
) && NextNode(stmt
) != nullptr) {
5706 return f
.fail(stmt
, "default label must be at the end");
5713 static bool CheckSwitchRange(FunctionValidatorShared
& f
, ParseNode
* stmt
,
5714 int32_t* low
, int32_t* high
,
5715 uint32_t* tableLength
) {
5716 if (IsDefaultCase(stmt
)) {
5724 if (!CheckCaseExpr(f
, CaseExpr(stmt
), &i
)) {
5730 ParseNode
* initialStmt
= stmt
;
5731 for (stmt
= NextNode(stmt
); stmt
&& !IsDefaultCase(stmt
);
5732 stmt
= NextNode(stmt
)) {
5734 if (!CheckCaseExpr(f
, CaseExpr(stmt
), &i
)) {
5738 *low
= std::min(*low
, i
);
5739 *high
= std::max(*high
, i
);
5742 int64_t i64
= (int64_t(*high
) - int64_t(*low
)) + 1;
5743 if (i64
> MaxBrTableElems
) {
5746 "all switch statements generate tables; this table would be too big");
5749 *tableLength
= uint32_t(i64
);
5753 template <typename Unit
>
5754 static bool CheckSwitchExpr(FunctionValidator
<Unit
>& f
, ParseNode
* switchExpr
) {
5756 if (!CheckExpr(f
, switchExpr
, &exprType
)) {
5759 if (!exprType
.isSigned()) {
5760 return f
.failf(switchExpr
, "%s is not a subtype of signed",
5761 exprType
.toChars());
5766 // A switch will be constructed as:
5767 // - the default block wrapping all the other blocks, to be able to break
5768 // out of the switch with an unlabeled break statement. It has two statements
5769 // (an inner block and the default expr). asm.js rules require default to be at
5770 // the end, so the default block always encloses all the cases blocks.
5771 // - one block per case between low and high; undefined cases just jump to the
5772 // default case. Each of these blocks contain two statements: the next case's
5773 // block and the possibly empty statement list comprising the case body. The
5774 // last block pushed is the first case so the (relative) branch target therefore
5775 // matches the sequential order of cases.
5776 // - one block for the br_table, so that the first break goes to the first
5778 template <typename Unit
>
5779 static bool CheckSwitch(FunctionValidator
<Unit
>& f
, ParseNode
* switchStmt
) {
5780 MOZ_ASSERT(switchStmt
->isKind(ParseNodeKind::SwitchStmt
));
5782 ParseNode
* switchExpr
= BinaryLeft(switchStmt
);
5783 ParseNode
* switchBody
= BinaryRight(switchStmt
);
5785 if (switchBody
->is
<LexicalScopeNode
>()) {
5786 LexicalScopeNode
* scope
= &switchBody
->as
<LexicalScopeNode
>();
5787 if (!scope
->isEmptyScope()) {
5788 return f
.fail(scope
, "switch body may not contain lexical declarations");
5790 switchBody
= scope
->scopeBody();
5793 ParseNode
* stmt
= ListHead(switchBody
);
5795 if (!CheckSwitchExpr(f
, switchExpr
)) {
5798 return f
.encoder().writeOp(Op::Drop
);
5801 if (!CheckDefaultAtEnd(f
, stmt
)) {
5805 int32_t low
= 0, high
= 0;
5806 uint32_t tableLength
= 0;
5807 if (!CheckSwitchRange(f
, stmt
, &low
, &high
, &tableLength
)) {
5811 static const uint32_t CASE_NOT_DEFINED
= UINT32_MAX
;
5813 Uint32Vector caseDepths
;
5814 if (!caseDepths
.appendN(CASE_NOT_DEFINED
, tableLength
)) {
5818 uint32_t numCases
= 0;
5819 for (ParseNode
* s
= stmt
; s
&& !IsDefaultCase(s
); s
= NextNode(s
)) {
5820 int32_t caseValue
= ExtractNumericLiteral(f
.m(), CaseExpr(s
)).toInt32();
5822 MOZ_ASSERT(caseValue
>= low
);
5823 unsigned i
= caseValue
- low
;
5824 if (caseDepths
[i
] != CASE_NOT_DEFINED
) {
5825 return f
.fail(s
, "no duplicate case labels");
5828 MOZ_ASSERT(numCases
!= CASE_NOT_DEFINED
);
5829 caseDepths
[i
] = numCases
++;
5832 // Open the wrapping breakable default block.
5833 if (!f
.pushBreakableBlock()) {
5837 // Open all the case blocks.
5838 for (uint32_t i
= 0; i
< numCases
; i
++) {
5839 if (!f
.pushUnbreakableBlock()) {
5844 // Open the br_table block.
5845 if (!f
.pushUnbreakableBlock()) {
5849 // The default block is the last one.
5850 uint32_t defaultDepth
= numCases
;
5852 // Subtract lowest case value, so that all the cases start from 0.
5854 if (!CheckSwitchExpr(f
, switchExpr
)) {
5857 if (!f
.writeInt32Lit(low
)) {
5860 if (!f
.encoder().writeOp(Op::I32Sub
)) {
5864 if (!CheckSwitchExpr(f
, switchExpr
)) {
5869 // Start the br_table block.
5870 if (!f
.encoder().writeOp(Op::BrTable
)) {
5874 // Write the number of cases (tableLength - 1 + 1 (default)).
5875 // Write the number of cases (tableLength - 1 + 1 (default)).
5876 if (!f
.encoder().writeVarU32(tableLength
)) {
5880 // Each case value describes the relative depth to the actual block. When
5881 // a case is not explicitly defined, it goes to the default.
5882 for (size_t i
= 0; i
< tableLength
; i
++) {
5884 caseDepths
[i
] == CASE_NOT_DEFINED
? defaultDepth
: caseDepths
[i
];
5885 if (!f
.encoder().writeVarU32(target
)) {
5890 // Write the default depth.
5891 if (!f
.encoder().writeVarU32(defaultDepth
)) {
5895 // Our br_table is done. Close its block, write the cases down in order.
5896 if (!f
.popUnbreakableBlock()) {
5900 for (; stmt
&& !IsDefaultCase(stmt
); stmt
= NextNode(stmt
)) {
5901 if (!CheckStatement(f
, CaseBody(stmt
))) {
5904 if (!f
.popUnbreakableBlock()) {
5909 // Write the default block.
5910 if (stmt
&& IsDefaultCase(stmt
)) {
5911 if (!CheckStatement(f
, CaseBody(stmt
))) {
5916 // Close the wrapping block.
5917 return f
.popBreakableBlock();
5920 static bool CheckReturnType(FunctionValidatorShared
& f
, ParseNode
* usepn
,
5922 Maybe
<ValType
> type
= ret
.canonicalToReturnType();
5924 if (!f
.hasAlreadyReturned()) {
5925 f
.setReturnedType(type
);
5929 if (f
.returnedType() != type
) {
5930 return f
.failf(usepn
, "%s incompatible with previous return of type %s",
5931 ToString(type
, nullptr).get(),
5932 ToString(f
.returnedType(), nullptr).get());
5938 template <typename Unit
>
5939 static bool CheckReturn(FunctionValidator
<Unit
>& f
, ParseNode
* returnStmt
) {
5940 ParseNode
* expr
= ReturnExpr(returnStmt
);
5943 if (!CheckReturnType(f
, returnStmt
, Type::Void
)) {
5948 if (!CheckExpr(f
, expr
, &type
)) {
5952 if (!type
.isReturnType()) {
5953 return f
.failf(expr
, "%s is not a valid return type", type
.toChars());
5956 if (!CheckReturnType(f
, expr
, Type::canonicalize(type
))) {
5961 return f
.encoder().writeOp(Op::Return
);
5964 template <typename Unit
>
5965 static bool CheckStatementList(FunctionValidator
<Unit
>& f
, ParseNode
* stmtList
,
5966 const LabelVector
* labels
/*= nullptr */) {
5967 MOZ_ASSERT(stmtList
->isKind(ParseNodeKind::StatementList
));
5969 if (!f
.pushUnbreakableBlock(labels
)) {
5973 for (ParseNode
* stmt
= ListHead(stmtList
); stmt
; stmt
= NextNode(stmt
)) {
5974 if (!CheckStatement(f
, stmt
)) {
5979 return f
.popUnbreakableBlock(labels
);
5982 template <typename Unit
>
5983 static bool CheckLexicalScope(FunctionValidator
<Unit
>& f
, ParseNode
* node
) {
5984 LexicalScopeNode
* lexicalScope
= &node
->as
<LexicalScopeNode
>();
5985 if (!lexicalScope
->isEmptyScope()) {
5986 return f
.fail(lexicalScope
, "cannot have 'let' or 'const' declarations");
5989 return CheckStatement(f
, lexicalScope
->scopeBody());
5992 static bool CheckBreakOrContinue(FunctionValidatorShared
& f
, bool isBreak
,
5994 if (TaggedParserAtomIndex maybeLabel
= LoopControlMaybeLabel(stmt
)) {
5995 return f
.writeLabeledBreakOrContinue(maybeLabel
, isBreak
);
5997 return f
.writeUnlabeledBreakOrContinue(isBreak
);
6000 template <typename Unit
>
6001 static bool CheckStatement(FunctionValidator
<Unit
>& f
, ParseNode
* stmt
) {
6002 AutoCheckRecursionLimit
recursion(f
.fc());
6003 if (!recursion
.checkDontReport(f
.fc())) {
6004 return f
.m().failOverRecursed();
6007 switch (stmt
->getKind()) {
6008 case ParseNodeKind::EmptyStmt
:
6010 case ParseNodeKind::ExpressionStmt
:
6011 return CheckExprStatement(f
, stmt
);
6012 case ParseNodeKind::WhileStmt
:
6013 return CheckWhile(f
, stmt
);
6014 case ParseNodeKind::ForStmt
:
6015 return CheckFor(f
, stmt
);
6016 case ParseNodeKind::DoWhileStmt
:
6017 return CheckDoWhile(f
, stmt
);
6018 case ParseNodeKind::LabelStmt
:
6019 return CheckLabel(f
, stmt
);
6020 case ParseNodeKind::IfStmt
:
6021 return CheckIf(f
, stmt
);
6022 case ParseNodeKind::SwitchStmt
:
6023 return CheckSwitch(f
, stmt
);
6024 case ParseNodeKind::ReturnStmt
:
6025 return CheckReturn(f
, stmt
);
6026 case ParseNodeKind::StatementList
:
6027 return CheckStatementList(f
, stmt
);
6028 case ParseNodeKind::BreakStmt
:
6029 return CheckBreakOrContinue(f
, true, stmt
);
6030 case ParseNodeKind::ContinueStmt
:
6031 return CheckBreakOrContinue(f
, false, stmt
);
6032 case ParseNodeKind::LexicalScope
:
6033 return CheckLexicalScope(f
, stmt
);
6037 return f
.fail(stmt
, "unexpected statement kind");
6040 template <typename Unit
>
6041 static bool ParseFunction(ModuleValidator
<Unit
>& m
, FunctionNode
** funNodeOut
,
6043 auto& tokenStream
= m
.tokenStream();
6045 tokenStream
.consumeKnownToken(TokenKind::Function
,
6046 TokenStreamShared::SlashIsRegExp
);
6048 auto& anyChars
= tokenStream
.anyCharsAccess();
6049 uint32_t toStringStart
= anyChars
.currentToken().pos
.begin
;
6050 *line
= anyChars
.lineNumber(anyChars
.lineToken(toStringStart
));
6053 if (!tokenStream
.getToken(&tk
, TokenStreamShared::SlashIsRegExp
)) {
6056 if (tk
== TokenKind::Mul
) {
6057 return m
.failCurrentOffset("unexpected generator function");
6059 if (!TokenKindIsPossibleIdentifier(tk
)) {
6060 return false; // The regular parser will throw a SyntaxError, no need to
6064 TaggedParserAtomIndex name
= m
.parser().bindingIdentifier(YieldIsName
);
6069 FunctionNode
* funNode
;
6070 MOZ_TRY_VAR_OR_RETURN(funNode
,
6071 m
.parser().handler_
.newFunction(
6072 FunctionSyntaxKind::Statement
, m
.parser().pos()),
6075 ParseContext
* outerpc
= m
.parser().pc_
;
6076 Directives
directives(outerpc
);
6077 FunctionFlags
flags(FunctionFlags::INTERPRETED_NORMAL
);
6078 FunctionBox
* funbox
= m
.parser().newFunctionBox(
6079 funNode
, name
, flags
, toStringStart
, directives
,
6080 GeneratorKind::NotGenerator
, FunctionAsyncKind::SyncFunction
);
6084 funbox
->initWithEnclosingParseContext(outerpc
, FunctionSyntaxKind::Statement
);
6086 Directives newDirectives
= directives
;
6087 SourceParseContext
funpc(&m
.parser(), funbox
, &newDirectives
);
6088 if (!funpc
.init()) {
6092 if (!m
.parser().functionFormalParametersAndBody(
6093 InAllowed
, YieldIsName
, &funNode
, FunctionSyntaxKind::Statement
)) {
6094 if (anyChars
.hadError() || directives
== newDirectives
) {
6098 return m
.fail(funNode
, "encountered new directive in function");
6101 MOZ_ASSERT(!anyChars
.hadError());
6102 MOZ_ASSERT(directives
== newDirectives
);
6104 *funNodeOut
= funNode
;
6108 template <typename Unit
>
6109 static bool CheckFunction(ModuleValidator
<Unit
>& m
) {
6110 // asm.js modules can be quite large when represented as parse trees so pop
6111 // the backing LifoAlloc after parsing/compiling each function. Release the
6112 // parser's lifo memory after the last use of a parse node.
6113 frontend::ParserBase::Mark mark
= m
.parser().mark();
6115 mozilla::MakeScopeExit([&m
, &mark
] { m
.parser().release(mark
); });
6117 FunctionNode
* funNode
= nullptr;
6119 if (!ParseFunction(m
, &funNode
, &line
)) {
6123 if (!CheckFunctionHead(m
, funNode
)) {
6127 FunctionValidator
<Unit
> f(m
, funNode
);
6129 ParseNode
* stmtIter
= ListHead(FunctionStatementList(funNode
));
6131 if (!CheckProcessingDirectives(m
, &stmtIter
)) {
6136 if (!CheckArguments(f
, &stmtIter
, &args
)) {
6140 if (!CheckVariables(f
, &stmtIter
)) {
6144 ParseNode
* lastNonEmptyStmt
= nullptr;
6145 for (; stmtIter
; stmtIter
= NextNonEmptyStatement(stmtIter
)) {
6146 lastNonEmptyStmt
= stmtIter
;
6147 if (!CheckStatement(f
, stmtIter
)) {
6152 if (!CheckFinalReturn(f
, lastNonEmptyStmt
)) {
6156 ValTypeVector results
;
6157 if (f
.returnedType()) {
6158 if (!results
.append(f
.returnedType().ref())) {
6163 FuncType
sig(std::move(args
), std::move(results
));
6165 ModuleValidatorShared::Func
* func
= nullptr;
6166 if (!CheckFunctionSignature(m
, funNode
, std::move(sig
), FunctionName(funNode
),
6171 if (func
->defined()) {
6172 return m
.failName(funNode
, "function '%s' already defined",
6173 FunctionName(funNode
));
6176 f
.define(func
, line
);
6181 static bool CheckAllFunctionsDefined(ModuleValidatorShared
& m
) {
6182 for (unsigned i
= 0; i
< m
.numFuncDefs(); i
++) {
6183 const ModuleValidatorShared::Func
& f
= m
.funcDef(i
);
6185 return m
.failNameOffset(f
.firstUse(), "missing definition of function %s",
6193 template <typename Unit
>
6194 static bool CheckFunctions(ModuleValidator
<Unit
>& m
) {
6197 if (!PeekToken(m
.parser(), &tk
)) {
6201 if (tk
!= TokenKind::Function
) {
6205 if (!CheckFunction(m
)) {
6210 return CheckAllFunctionsDefined(m
);
6213 template <typename Unit
>
6214 static bool CheckFuncPtrTable(ModuleValidator
<Unit
>& m
, ParseNode
* decl
) {
6215 if (!decl
->isKind(ParseNodeKind::AssignExpr
)) {
6216 return m
.fail(decl
, "function-pointer table must have initializer");
6218 AssignmentNode
* assignNode
= &decl
->as
<AssignmentNode
>();
6220 ParseNode
* var
= assignNode
->left();
6222 if (!var
->isKind(ParseNodeKind::Name
)) {
6223 return m
.fail(var
, "function-pointer table name is not a plain name");
6226 ParseNode
* arrayLiteral
= assignNode
->right();
6228 if (!arrayLiteral
->isKind(ParseNodeKind::ArrayExpr
)) {
6230 var
, "function-pointer table's initializer must be an array literal");
6233 unsigned length
= ListLength(arrayLiteral
);
6235 if (!IsPowerOfTwo(length
)) {
6236 return m
.failf(arrayLiteral
,
6237 "function-pointer table length must be a power of 2 (is %u)",
6241 unsigned mask
= length
- 1;
6243 Uint32Vector elemFuncDefIndices
;
6244 const FuncType
* sig
= nullptr;
6245 for (ParseNode
* elem
= ListHead(arrayLiteral
); elem
; elem
= NextNode(elem
)) {
6246 if (!elem
->isKind(ParseNodeKind::Name
)) {
6248 elem
, "function-pointer table's elements must be names of functions");
6251 TaggedParserAtomIndex funcName
= elem
->as
<NameNode
>().name();
6252 const ModuleValidatorShared::Func
* func
= m
.lookupFuncDef(funcName
);
6255 elem
, "function-pointer table's elements must be names of functions");
6258 const FuncType
& funcSig
= m
.env().types
->type(func
->sigIndex()).funcType();
6260 if (!FuncType::strictlyEquals(*sig
, funcSig
)) {
6261 return m
.fail(elem
, "all functions in table must have same signature");
6267 if (!elemFuncDefIndices
.append(func
->funcDefIndex())) {
6273 if (!copy
.clone(*sig
)) {
6277 uint32_t tableIndex
;
6278 if (!CheckFuncPtrTableAgainstExisting(m
, var
, var
->as
<NameNode
>().name(),
6279 std::move(copy
), mask
, &tableIndex
)) {
6283 if (!m
.defineFuncPtrTable(tableIndex
, std::move(elemFuncDefIndices
))) {
6284 return m
.fail(var
, "duplicate function-pointer definition");
6290 template <typename Unit
>
6291 static bool CheckFuncPtrTables(ModuleValidator
<Unit
>& m
) {
6294 if (!ParseVarOrConstStatement(m
.parser(), &varStmt
)) {
6300 for (ParseNode
* var
= VarListHead(varStmt
); var
; var
= NextNode(var
)) {
6301 if (!CheckFuncPtrTable(m
, var
)) {
6307 for (unsigned i
= 0; i
< m
.numFuncPtrTables(); i
++) {
6308 ModuleValidatorShared::Table
& table
= m
.table(i
);
6309 if (!table
.defined()) {
6310 return m
.failNameOffset(table
.firstUse(),
6311 "function-pointer table %s wasn't defined",
6319 static bool CheckModuleExportFunction(
6320 ModuleValidatorShared
& m
, ParseNode
* pn
,
6321 TaggedParserAtomIndex maybeFieldName
= TaggedParserAtomIndex::null()) {
6322 if (!pn
->isKind(ParseNodeKind::Name
)) {
6323 return m
.fail(pn
, "expected name of exported function");
6326 TaggedParserAtomIndex funcName
= pn
->as
<NameNode
>().name();
6327 const ModuleValidatorShared::Func
* func
= m
.lookupFuncDef(funcName
);
6329 return m
.failName(pn
, "function '%s' not found", funcName
);
6332 return m
.addExportField(*func
, maybeFieldName
);
6335 static bool CheckModuleExportObject(ModuleValidatorShared
& m
,
6336 ParseNode
* object
) {
6337 MOZ_ASSERT(object
->isKind(ParseNodeKind::ObjectExpr
));
6339 for (ParseNode
* pn
= ListHead(object
); pn
; pn
= NextNode(pn
)) {
6340 if (!IsNormalObjectField(pn
)) {
6342 "only normal object properties may be used in the export "
6346 TaggedParserAtomIndex fieldName
= ObjectNormalFieldName(pn
);
6348 ParseNode
* initNode
= ObjectNormalFieldInitializer(pn
);
6349 if (!initNode
->isKind(ParseNodeKind::Name
)) {
6352 "initializer of exported object literal must be name of function");
6355 if (!CheckModuleExportFunction(m
, initNode
, fieldName
)) {
6363 template <typename Unit
>
6364 static bool CheckModuleReturn(ModuleValidator
<Unit
>& m
) {
6366 if (!GetToken(m
.parser(), &tk
)) {
6369 auto& ts
= m
.parser().tokenStream
;
6370 if (tk
!= TokenKind::Return
) {
6371 return m
.failCurrentOffset(
6372 (tk
== TokenKind::RightCurly
|| tk
== TokenKind::Eof
)
6373 ? "expecting return statement"
6374 : "invalid asm.js. statement");
6376 ts
.anyCharsAccess().ungetToken();
6378 ParseNode
* returnStmt
;
6379 MOZ_TRY_VAR_OR_RETURN(returnStmt
, m
.parser().statementListItem(YieldIsName
),
6382 ParseNode
* returnExpr
= ReturnExpr(returnStmt
);
6384 return m
.fail(returnStmt
, "export statement must return something");
6387 if (returnExpr
->isKind(ParseNodeKind::ObjectExpr
)) {
6388 if (!CheckModuleExportObject(m
, returnExpr
)) {
6392 if (!CheckModuleExportFunction(m
, returnExpr
)) {
6400 template <typename Unit
>
6401 static bool CheckModuleEnd(ModuleValidator
<Unit
>& m
) {
6403 if (!GetToken(m
.parser(), &tk
)) {
6407 if (tk
!= TokenKind::Eof
&& tk
!= TokenKind::RightCurly
) {
6408 return m
.failCurrentOffset(
6409 "top-level export (return) must be the last statement");
6412 m
.parser().tokenStream
.anyCharsAccess().ungetToken();
6416 template <typename Unit
>
6417 static SharedModule
CheckModule(FrontendContext
* fc
,
6418 ParserAtomsTable
& parserAtoms
,
6419 AsmJSParser
<Unit
>& parser
, ParseNode
* stmtList
,
6421 int64_t before
= PRMJ_Now();
6423 FunctionNode
* moduleFunctionNode
= parser
.pc_
->functionBox()->functionNode
;
6425 ModuleValidator
<Unit
> m(fc
, parserAtoms
, parser
, moduleFunctionNode
);
6430 if (!CheckFunctionHead(m
, moduleFunctionNode
)) {
6434 if (!CheckModuleArguments(m
, moduleFunctionNode
)) {
6438 if (!CheckPrecedingStatements(m
, stmtList
)) {
6442 if (!CheckModuleProcessingDirectives(m
)) {
6446 if (!CheckModuleGlobals(m
)) {
6450 if (!m
.startFunctionBodies()) {
6454 if (!CheckFunctions(m
)) {
6458 if (!CheckFuncPtrTables(m
)) {
6462 if (!CheckModuleReturn(m
)) {
6466 if (!CheckModuleEnd(m
)) {
6470 SharedModule module
= m
.finish();
6475 *time
= (PRMJ_Now() - before
) / PRMJ_USEC_PER_MSEC
;
6479 /*****************************************************************************/
6480 // Link-time validation
6482 static bool LinkFail(JSContext
* cx
, const char* str
) {
6483 WarnNumberASCII(cx
, JSMSG_USE_ASM_LINK_FAIL
, str
);
6487 static bool IsMaybeWrappedScriptedProxy(JSObject
* obj
) {
6488 JSObject
* unwrapped
= UncheckedUnwrap(obj
);
6489 return unwrapped
&& IsScriptedProxy(unwrapped
);
6492 static bool GetDataProperty(JSContext
* cx
, HandleValue objVal
,
6493 Handle
<JSAtom
*> field
, MutableHandleValue v
) {
6494 if (!objVal
.isObject()) {
6495 return LinkFail(cx
, "accessing property of non-object");
6498 RootedObject
obj(cx
, &objVal
.toObject());
6499 if (IsMaybeWrappedScriptedProxy(obj
)) {
6500 return LinkFail(cx
, "accessing property of a Proxy");
6503 RootedId
id(cx
, AtomToId(field
));
6504 Rooted
<mozilla::Maybe
<PropertyDescriptor
>> desc(cx
);
6505 RootedObject
holder(cx
);
6506 if (!GetPropertyDescriptor(cx
, obj
, id
, &desc
, &holder
)) {
6510 if (!desc
.isSome()) {
6511 return LinkFail(cx
, "property not present on object");
6514 if (!desc
->isDataDescriptor()) {
6515 return LinkFail(cx
, "property is not a data property");
6518 v
.set(desc
->value());
6522 static bool GetDataProperty(JSContext
* cx
, HandleValue objVal
,
6523 const char* fieldChars
, MutableHandleValue v
) {
6524 Rooted
<JSAtom
*> field(cx
,
6525 AtomizeUTF8Chars(cx
, fieldChars
, strlen(fieldChars
)));
6530 return GetDataProperty(cx
, objVal
, field
, v
);
6533 static bool GetDataProperty(JSContext
* cx
, HandleValue objVal
,
6534 const ImmutableTenuredPtr
<PropertyName
*>& field
,
6535 MutableHandleValue v
) {
6536 Handle
<PropertyName
*> fieldHandle
= field
;
6537 return GetDataProperty(cx
, objVal
, fieldHandle
, v
);
6540 static bool HasObjectValueOfMethodPure(JSObject
* obj
, JSContext
* cx
) {
6542 if (!GetPropertyPure(cx
, obj
, NameToId(cx
->names().valueOf
), &v
)) {
6547 if (!IsFunctionObject(v
, &fun
)) {
6551 return IsSelfHostedFunctionWithName(fun
, cx
->names().Object_valueOf
);
6554 static bool HasPureCoercion(JSContext
* cx
, HandleValue v
) {
6555 // Ideally, we'd reject all non-primitives, but Emscripten has a bug that
6556 // generates code that passes functions for some imports. To avoid breaking
6557 // all the code that contains this bug, we make an exception for functions
6558 // that don't have user-defined valueOf or toString, for their coercions
6559 // are not observable and coercion via ToNumber/ToInt32 definitely produces
6560 // NaN/0. We should remove this special case later once most apps have been
6561 // built with newer Emscripten.
6562 return v
.toObject().is
<JSFunction
>() &&
6563 HasNoToPrimitiveMethodPure(&v
.toObject(), cx
) &&
6564 HasObjectValueOfMethodPure(&v
.toObject(), cx
) &&
6565 HasNativeMethodPure(&v
.toObject(), cx
->names().toString
, fun_toString
,
6569 static bool ValidateGlobalVariable(JSContext
* cx
, const AsmJSGlobal
& global
,
6570 HandleValue importVal
,
6571 Maybe
<LitValPOD
>* val
) {
6572 switch (global
.varInitKind()) {
6573 case AsmJSGlobal::InitConstant
:
6574 val
->emplace(global
.varInitVal());
6577 case AsmJSGlobal::InitImport
: {
6579 if (!GetDataProperty(cx
, importVal
, global
.field(), &v
)) {
6583 if (!v
.isPrimitive() && !HasPureCoercion(cx
, v
)) {
6584 return LinkFail(cx
, "Imported values must be primitives");
6587 switch (global
.varInitImportType().kind()) {
6588 case ValType::I32
: {
6590 if (!ToInt32(cx
, v
, &i32
)) {
6593 val
->emplace(uint32_t(i32
));
6600 case ValType::F32
: {
6602 if (!RoundFloat32(cx
, v
, &f
)) {
6608 case ValType::F64
: {
6610 if (!ToNumber(cx
, v
, &d
)) {
6616 case ValType::Ref
: {
6617 MOZ_CRASH("not available in asm.js");
6623 MOZ_CRASH("unreachable");
6626 static bool ValidateFFI(JSContext
* cx
, const AsmJSGlobal
& global
,
6627 HandleValue importVal
,
6628 MutableHandle
<FunctionVector
> ffis
) {
6630 if (!GetDataProperty(cx
, importVal
, global
.field(), &v
)) {
6634 if (!IsFunctionObject(v
)) {
6635 return LinkFail(cx
, "FFI imports must be functions");
6638 ffis
[global
.ffiIndex()].set(&v
.toObject().as
<JSFunction
>());
6642 static bool ValidateArrayView(JSContext
* cx
, const AsmJSGlobal
& global
,
6643 HandleValue globalVal
) {
6644 if (!global
.field()) {
6648 if (Scalar::isBigIntType(global
.viewType())) {
6649 return LinkFail(cx
, "bad typed array constructor");
6653 if (!GetDataProperty(cx
, globalVal
, global
.field(), &v
)) {
6657 bool tac
= IsTypedArrayConstructor(v
, global
.viewType());
6659 return LinkFail(cx
, "bad typed array constructor");
6665 static InlinableNative
ToInlinableNative(AsmJSMathBuiltinFunction func
) {
6667 case AsmJSMathBuiltin_sin
:
6668 return InlinableNative::MathSin
;
6669 case AsmJSMathBuiltin_cos
:
6670 return InlinableNative::MathCos
;
6671 case AsmJSMathBuiltin_tan
:
6672 return InlinableNative::MathTan
;
6673 case AsmJSMathBuiltin_asin
:
6674 return InlinableNative::MathASin
;
6675 case AsmJSMathBuiltin_acos
:
6676 return InlinableNative::MathACos
;
6677 case AsmJSMathBuiltin_atan
:
6678 return InlinableNative::MathATan
;
6679 case AsmJSMathBuiltin_ceil
:
6680 return InlinableNative::MathCeil
;
6681 case AsmJSMathBuiltin_floor
:
6682 return InlinableNative::MathFloor
;
6683 case AsmJSMathBuiltin_exp
:
6684 return InlinableNative::MathExp
;
6685 case AsmJSMathBuiltin_log
:
6686 return InlinableNative::MathLog
;
6687 case AsmJSMathBuiltin_pow
:
6688 return InlinableNative::MathPow
;
6689 case AsmJSMathBuiltin_sqrt
:
6690 return InlinableNative::MathSqrt
;
6691 case AsmJSMathBuiltin_abs
:
6692 return InlinableNative::MathAbs
;
6693 case AsmJSMathBuiltin_atan2
:
6694 return InlinableNative::MathATan2
;
6695 case AsmJSMathBuiltin_imul
:
6696 return InlinableNative::MathImul
;
6697 case AsmJSMathBuiltin_fround
:
6698 return InlinableNative::MathFRound
;
6699 case AsmJSMathBuiltin_min
:
6700 return InlinableNative::MathMin
;
6701 case AsmJSMathBuiltin_max
:
6702 return InlinableNative::MathMax
;
6703 case AsmJSMathBuiltin_clz32
:
6704 return InlinableNative::MathClz32
;
6706 MOZ_CRASH("Invalid asm.js math builtin function");
6709 static bool ValidateMathBuiltinFunction(JSContext
* cx
,
6710 const AsmJSMetadata
& metadata
,
6711 const AsmJSGlobal
& global
,
6712 HandleValue globalVal
) {
6714 if (!GetDataProperty(cx
, globalVal
, cx
->names().Math
, &v
)) {
6718 if (!GetDataProperty(cx
, v
, global
.field(), &v
)) {
6722 InlinableNative native
= ToInlinableNative(global
.mathBuiltinFunction());
6725 if (!IsFunctionObject(v
, &fun
) || !fun
->hasJitInfo() ||
6726 fun
->jitInfo()->type() != JSJitInfo::InlinableNative
||
6727 fun
->jitInfo()->inlinableNative
!= native
) {
6728 return LinkFail(cx
, "bad Math.* builtin function");
6730 if (fun
->realm()->creationOptions().alwaysUseFdlibm() !=
6731 metadata
.alwaysUseFdlibm
) {
6733 "Math.* builtin function and asm.js use different native"
6734 " math implementations.");
6740 static bool ValidateConstant(JSContext
* cx
, const AsmJSGlobal
& global
,
6741 HandleValue globalVal
) {
6742 RootedValue
v(cx
, globalVal
);
6744 if (global
.constantKind() == AsmJSGlobal::MathConstant
) {
6745 if (!GetDataProperty(cx
, v
, cx
->names().Math
, &v
)) {
6750 if (!GetDataProperty(cx
, v
, global
.field(), &v
)) {
6754 if (!v
.isNumber()) {
6755 return LinkFail(cx
, "math / global constant value needs to be a number");
6759 if (std::isnan(global
.constantValue())) {
6760 if (!std::isnan(v
.toNumber())) {
6761 return LinkFail(cx
, "global constant value needs to be NaN");
6764 if (v
.toNumber() != global
.constantValue()) {
6765 return LinkFail(cx
, "global constant value mismatch");
6772 static bool CheckBuffer(JSContext
* cx
, const AsmJSMetadata
& metadata
,
6773 HandleValue bufferVal
,
6774 MutableHandle
<ArrayBufferObject
*> buffer
) {
6775 if (!bufferVal
.isObject()) {
6776 return LinkFail(cx
, "buffer must be an object");
6778 JSObject
* bufferObj
= &bufferVal
.toObject();
6780 if (metadata
.memories
[0].isShared()) {
6781 if (!bufferObj
->is
<SharedArrayBufferObject
>()) {
6783 cx
, "shared views can only be constructed onto SharedArrayBuffer");
6785 return LinkFail(cx
, "Unable to prepare SharedArrayBuffer for asm.js use");
6788 if (!bufferObj
->is
<ArrayBufferObject
>()) {
6790 "unshared views can only be constructed onto ArrayBuffer");
6793 buffer
.set(&bufferObj
->as
<ArrayBufferObject
>());
6795 size_t memoryLength
= buffer
->byteLength();
6797 if (!IsValidAsmJSHeapLength(memoryLength
)) {
6799 if (memoryLength
> MaxHeapLength
) {
6800 msg
= JS_smprintf("ArrayBuffer byteLength 0x%" PRIx64
6801 " is not a valid heap length - it is too long."
6802 " The longest valid length is 0x%" PRIx64
,
6803 uint64_t(memoryLength
), MaxHeapLength
);
6805 msg
= JS_smprintf("ArrayBuffer byteLength 0x%" PRIx64
6806 " is not a valid heap length. The next "
6807 "valid length is 0x%" PRIx64
,
6808 uint64_t(memoryLength
),
6809 RoundUpToNextValidAsmJSHeapLength(memoryLength
));
6814 return LinkFail(cx
, msg
.get());
6817 // This check is sufficient without considering the size of the loaded datum
6818 // because heap loads and stores start on an aligned boundary and the heap
6819 // byteLength has larger alignment.
6820 uint64_t minMemoryLength
= metadata
.memories
.length() != 0
6821 ? metadata
.memories
[0].initialLength32()
6823 MOZ_ASSERT((minMemoryLength
- 1) <= INT32_MAX
);
6824 if (memoryLength
< minMemoryLength
) {
6825 UniqueChars
msg(JS_smprintf("ArrayBuffer byteLength of 0x%" PRIx64
6826 " is less than 0x%" PRIx64
" (the "
6828 "by const heap accesses).",
6829 uint64_t(memoryLength
), minMemoryLength
));
6833 return LinkFail(cx
, msg
.get());
6836 // ArrayBuffer lengths in SpiderMonkey used to be restricted to <= INT32_MAX,
6837 // but that has since been relaxed for the benefit of wasm. We keep the old
6838 // limit for asm.js so as to avoid having to worry about whether the asm.js
6839 // implementation is safe for larger heaps.
6840 if (memoryLength
>= INT32_MAX
) {
6842 JS_smprintf("ArrayBuffer byteLength 0x%" PRIx64
6843 " is too large for asm.js (implementation limit).",
6844 uint64_t(memoryLength
)));
6848 return LinkFail(cx
, msg
.get());
6851 if (buffer
->isResizable()) {
6853 "Unable to prepare resizable ArrayBuffer for asm.js use");
6856 if (!buffer
->prepareForAsmJS()) {
6857 return LinkFail(cx
, "Unable to prepare ArrayBuffer for asm.js use");
6860 MOZ_ASSERT(buffer
->isPreparedForAsmJS());
6864 static bool GetImports(JSContext
* cx
, const AsmJSMetadata
& metadata
,
6865 HandleValue globalVal
, HandleValue importVal
,
6866 ImportValues
* imports
) {
6867 Rooted
<FunctionVector
> ffis(cx
, FunctionVector(cx
));
6868 if (!ffis
.resize(metadata
.numFFIs
)) {
6872 for (const AsmJSGlobal
& global
: metadata
.asmJSGlobals
) {
6873 switch (global
.which()) {
6874 case AsmJSGlobal::Variable
: {
6875 Maybe
<LitValPOD
> litVal
;
6876 if (!ValidateGlobalVariable(cx
, global
, importVal
, &litVal
)) {
6879 if (!imports
->globalValues
.append(Val(litVal
->asLitVal()))) {
6884 case AsmJSGlobal::FFI
:
6885 if (!ValidateFFI(cx
, global
, importVal
, &ffis
)) {
6889 case AsmJSGlobal::ArrayView
:
6890 case AsmJSGlobal::ArrayViewCtor
:
6891 if (!ValidateArrayView(cx
, global
, globalVal
)) {
6895 case AsmJSGlobal::MathBuiltinFunction
:
6896 if (!ValidateMathBuiltinFunction(cx
, metadata
, global
, globalVal
)) {
6900 case AsmJSGlobal::Constant
:
6901 if (!ValidateConstant(cx
, global
, globalVal
)) {
6908 for (const AsmJSImport
& import
: metadata
.asmJSImports
) {
6909 if (!imports
->funcs
.append(ffis
[import
.ffiIndex()])) {
6917 static bool TryInstantiate(JSContext
* cx
, CallArgs args
, const Module
& module
,
6918 const AsmJSMetadata
& metadata
,
6919 MutableHandle
<WasmInstanceObject
*> instanceObj
,
6920 MutableHandleObject exportObj
) {
6921 HandleValue globalVal
= args
.get(0);
6922 HandleValue importVal
= args
.get(1);
6923 HandleValue bufferVal
= args
.get(2);
6925 MOZ_RELEASE_ASSERT(HasPlatformSupport());
6927 if (!wasm::EnsureFullSignalHandlers(cx
)) {
6928 return LinkFail(cx
, "failed to install signal handlers");
6931 Rooted
<ImportValues
> imports(cx
);
6933 if (module
.metadata().memories
.length() != 0) {
6934 MOZ_ASSERT(module
.metadata().memories
.length() == 1);
6935 Rooted
<ArrayBufferObject
*> buffer(cx
);
6936 if (!CheckBuffer(cx
, metadata
, bufferVal
, &buffer
)) {
6940 Rooted
<WasmMemoryObject
*> memory(
6941 cx
, WasmMemoryObject::create(cx
, buffer
, /* isHuge= */ false, nullptr));
6942 if (!memory
|| !imports
.get().memories
.append(memory
)) {
6947 if (!GetImports(cx
, metadata
, globalVal
, importVal
, imports
.address())) {
6951 if (!module
.instantiate(cx
, imports
.get(), nullptr, instanceObj
)) {
6955 exportObj
.set(&instanceObj
->exportsObj());
6959 static bool HandleInstantiationFailure(JSContext
* cx
, CallArgs args
,
6960 const AsmJSMetadata
& metadata
) {
6961 using js::frontend::FunctionSyntaxKind
;
6963 Rooted
<JSAtom
*> name(cx
, args
.callee().as
<JSFunction
>().fullExplicitName());
6965 if (cx
->isExceptionPending()) {
6969 ScriptSource
* source
= metadata
.maybeScriptSource();
6971 // Source discarding is allowed to affect JS semantics because it is never
6972 // enabled for normal JS content.
6974 if (!ScriptSource::loadSource(cx
, source
, &haveSource
)) {
6978 JS_ReportErrorASCII(cx
,
6979 "asm.js link failure with source discarding enabled");
6983 uint32_t begin
= metadata
.toStringStart
;
6984 uint32_t end
= metadata
.srcEndAfterCurly();
6985 Rooted
<JSLinearString
*> src(cx
, source
->substringDontDeflate(cx
, begin
, end
));
6990 JS::CompileOptions
options(cx
);
6991 options
.setMutedErrors(source
->mutedErrors())
6992 .setFile(source
->filename())
6993 .setNoScriptRval(false);
6994 options
.setAsmJSOption(AsmJSOption::DisabledByLinker
);
6996 // The exported function inherits an implicit strict context if the module
6997 // also inherited it somehow.
6998 if (metadata
.strict
) {
6999 options
.setForceStrictMode();
7002 AutoStableStringChars
linearChars(cx
);
7003 if (!linearChars
.initTwoByte(cx
, src
)) {
7007 SourceText
<char16_t
> srcBuf
;
7008 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
7012 FunctionSyntaxKind syntaxKind
= FunctionSyntaxKind::Statement
;
7014 RootedFunction
fun(cx
, frontend::CompileStandaloneFunction(
7015 cx
, options
, srcBuf
, Nothing(), syntaxKind
));
7020 fun
->initEnvironment(&cx
->global()->lexicalEnvironment());
7022 // Call the function we just recompiled.
7023 args
.setCallee(ObjectValue(*fun
));
7024 return InternalCallOrConstruct(
7025 cx
, args
, args
.isConstructing() ? CONSTRUCT
: NO_CONSTRUCT
);
7028 static const Module
& AsmJSModuleFunctionToModule(JSFunction
* fun
) {
7029 MOZ_ASSERT(IsAsmJSModule(fun
));
7030 const Value
& v
= fun
->getExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT
);
7031 return v
.toObject().as
<WasmModuleObject
>().module();
7034 // Implements the semantics of an asm.js module function that has been
7035 // successfully validated.
7036 bool js::InstantiateAsmJS(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
7037 CallArgs args
= CallArgsFromVp(argc
, vp
);
7039 JSFunction
* callee
= &args
.callee().as
<JSFunction
>();
7040 const Module
& module
= AsmJSModuleFunctionToModule(callee
);
7041 const AsmJSMetadata
& metadata
= module
.metadata().asAsmJS();
7043 Rooted
<WasmInstanceObject
*> instanceObj(cx
);
7044 RootedObject
exportObj(cx
);
7045 if (!TryInstantiate(cx
, args
, module
, metadata
, &instanceObj
, &exportObj
)) {
7046 // Link-time validation checks failed, so reparse the entire asm.js
7047 // module from scratch to get normal interpreted bytecode which we can
7048 // simply Invoke. Very slow.
7049 return HandleInstantiationFailure(cx
, args
, metadata
);
7052 args
.rval().set(ObjectValue(*exportObj
));
7056 /*****************************************************************************/
7057 // Top-level js::CompileAsmJS
7059 static bool NoExceptionPending(FrontendContext
* fc
) { return !fc
->hadErrors(); }
7061 static bool SuccessfulValidation(frontend::ParserBase
& parser
,
7062 unsigned compilationTime
) {
7063 unsigned errNum
= js::SupportDifferentialTesting()
7064 ? JSMSG_USE_ASM_TYPE_OK_NO_TIME
7065 : JSMSG_USE_ASM_TYPE_OK
;
7068 SprintfLiteral(timeChars
, "%u", compilationTime
);
7070 return parser
.warningNoOffset(errNum
, timeChars
);
7073 static bool TypeFailureWarning(frontend::ParserBase
& parser
, const char* str
) {
7074 if (parser
.options().throwOnAsmJSValidationFailure()) {
7075 parser
.errorNoOffset(JSMSG_USE_ASM_TYPE_FAIL
, str
? str
: "");
7079 // Per the asm.js standard convention, whether failure sets a pending
7080 // exception determines whether to attempt non-asm.js reparsing, so ignore
7081 // the return value below.
7082 (void)parser
.warningNoOffset(JSMSG_USE_ASM_TYPE_FAIL
, str
? str
: "");
7086 // asm.js requires Ion to be available on the current hardware/OS and to be
7087 // enabled for wasm, since asm.js compilation goes via wasm.
7088 static bool IsAsmJSCompilerAvailable(JSContext
* cx
) {
7089 return HasPlatformSupport() && WasmCompilerForAsmJSAvailable(cx
);
7092 static bool EstablishPreconditions(frontend::ParserBase
& parser
) {
7093 switch (parser
.options().asmJSOption()) {
7094 case AsmJSOption::DisabledByAsmJSPref
:
7095 return TypeFailureWarning(
7096 parser
, "Asm.js optimizer disabled by 'asmjs' runtime option");
7097 case AsmJSOption::DisabledByLinker
:
7098 return TypeFailureWarning(
7100 "Asm.js optimizer disabled by linker (instantiation failure)");
7101 case AsmJSOption::DisabledByNoWasmCompiler
:
7102 return TypeFailureWarning(parser
,
7103 "Asm.js optimizer disabled because no suitable "
7104 "wasm compiler is available");
7105 case AsmJSOption::DisabledByDebugger
:
7106 return TypeFailureWarning(
7107 parser
, "Asm.js optimizer disabled because debugger is active");
7108 case AsmJSOption::Enabled
:
7112 if (parser
.pc_
->isGenerator()) {
7113 return TypeFailureWarning(parser
,
7114 "Asm.js optimizer disabled in generator context");
7117 if (parser
.pc_
->isAsync()) {
7118 return TypeFailureWarning(parser
,
7119 "Asm.js optimizer disabled in async context");
7122 if (parser
.pc_
->isArrowFunction()) {
7123 return TypeFailureWarning(
7124 parser
, "Asm.js optimizer disabled in arrow function context");
7127 // Class constructors are also methods
7128 if (parser
.pc_
->isMethod() || parser
.pc_
->isGetterOrSetter()) {
7129 return TypeFailureWarning(
7131 "Asm.js optimizer disabled in class constructor or method context");
7137 template <typename Unit
>
7138 static bool DoCompileAsmJS(FrontendContext
* fc
, ParserAtomsTable
& parserAtoms
,
7139 AsmJSParser
<Unit
>& parser
, ParseNode
* stmtList
,
7143 // Various conditions disable asm.js optimizations.
7144 if (!EstablishPreconditions(parser
)) {
7145 return NoExceptionPending(fc
);
7148 // "Checking" parses, validates and compiles, producing a fully compiled
7149 // WasmModuleObject as result.
7151 SharedModule module
= CheckModule(fc
, parserAtoms
, parser
, stmtList
, &time
);
7153 return NoExceptionPending(fc
);
7156 // Finished! Save the ref-counted module on the FunctionBox. When JSFunctions
7157 // are eventually allocated we will create an asm.js constructor for it.
7158 FunctionBox
* funbox
= parser
.pc_
->functionBox();
7159 MOZ_ASSERT(funbox
->isInterpreted());
7160 if (!funbox
->setAsmJSModule(module
)) {
7161 return NoExceptionPending(fc
);
7164 // Success! Write to the console with a "warning" message indicating
7165 // total compilation time.
7167 SuccessfulValidation(parser
, time
);
7168 return NoExceptionPending(fc
);
7171 bool js::CompileAsmJS(FrontendContext
* fc
, ParserAtomsTable
& parserAtoms
,
7172 AsmJSParser
<char16_t
>& parser
, ParseNode
* stmtList
,
7174 return DoCompileAsmJS(fc
, parserAtoms
, parser
, stmtList
, validated
);
7177 bool js::CompileAsmJS(FrontendContext
* fc
, ParserAtomsTable
& parserAtoms
,
7178 AsmJSParser
<Utf8Unit
>& parser
, ParseNode
* stmtList
,
7180 return DoCompileAsmJS(fc
, parserAtoms
, parser
, stmtList
, validated
);
7183 /*****************************************************************************/
7184 // asm.js testing functions
7186 bool js::IsAsmJSModuleNative(Native native
) {
7187 return native
== InstantiateAsmJS
;
7190 bool js::IsAsmJSModule(JSFunction
* fun
) {
7191 return fun
->maybeNative() == InstantiateAsmJS
;
7194 bool js::IsAsmJSFunction(JSFunction
* fun
) {
7195 return fun
->kind() == FunctionFlags::AsmJS
;
7198 bool js::IsAsmJSStrictModeModuleOrFunction(JSFunction
* fun
) {
7199 if (IsAsmJSModule(fun
)) {
7200 return AsmJSModuleFunctionToModule(fun
).metadata().asAsmJS().strict
;
7203 if (IsAsmJSFunction(fun
)) {
7204 return ExportedFunctionToInstance(fun
).metadata().asAsmJS().strict
;
7210 bool js::IsAsmJSCompilationAvailable(JSContext
* cx
) {
7211 return cx
->options().asmJS() && IsAsmJSCompilerAvailable(cx
);
7214 bool js::IsAsmJSCompilationAvailable(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7215 CallArgs args
= CallArgsFromVp(argc
, vp
);
7216 bool available
= IsAsmJSCompilationAvailable(cx
);
7217 args
.rval().set(BooleanValue(available
));
7221 static JSFunction
* MaybeWrappedNativeFunction(const Value
& v
) {
7222 if (!v
.isObject()) {
7226 return v
.toObject().maybeUnwrapIf
<JSFunction
>();
7229 bool js::IsAsmJSModule(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7230 CallArgs args
= CallArgsFromVp(argc
, vp
);
7233 if (JSFunction
* fun
= MaybeWrappedNativeFunction(args
.get(0))) {
7234 rval
= IsAsmJSModule(fun
);
7237 args
.rval().set(BooleanValue(rval
));
7241 bool js::IsAsmJSFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7242 CallArgs args
= CallArgsFromVp(argc
, vp
);
7245 if (JSFunction
* fun
= MaybeWrappedNativeFunction(args
.get(0))) {
7246 rval
= IsAsmJSFunction(fun
);
7249 args
.rval().set(BooleanValue(rval
));
7253 /*****************************************************************************/
7254 // asm.js toString/toSource support
7256 JSString
* js::AsmJSModuleToString(JSContext
* cx
, HandleFunction fun
,
7258 MOZ_ASSERT(IsAsmJSModule(fun
));
7260 const AsmJSMetadata
& metadata
=
7261 AsmJSModuleFunctionToModule(fun
).metadata().asAsmJS();
7262 uint32_t begin
= metadata
.toStringStart
;
7263 uint32_t end
= metadata
.srcEndAfterCurly();
7264 ScriptSource
* source
= metadata
.maybeScriptSource();
7266 JSStringBuilder
out(cx
);
7268 if (isToSource
&& fun
->isLambda() && !out
.append("(")) {
7273 if (!ScriptSource::loadSource(cx
, source
, &haveSource
)) {
7278 if (!out
.append("function ")) {
7281 if (fun
->fullExplicitName() && !out
.append(fun
->fullExplicitName())) {
7284 if (!out
.append("() {\n [native code]\n}")) {
7288 Rooted
<JSLinearString
*> src(cx
, source
->substring(cx
, begin
, end
));
7293 if (!out
.append(src
)) {
7298 if (isToSource
&& fun
->isLambda() && !out
.append(")")) {
7302 return out
.finishString();
7305 JSString
* js::AsmJSFunctionToString(JSContext
* cx
, HandleFunction fun
) {
7306 MOZ_ASSERT(IsAsmJSFunction(fun
));
7308 const AsmJSMetadata
& metadata
=
7309 ExportedFunctionToInstance(fun
).metadata().asAsmJS();
7310 const AsmJSExport
& f
=
7311 metadata
.lookupAsmJSExport(ExportedFunctionToFuncIndex(fun
));
7313 uint32_t begin
= metadata
.srcStart
+ f
.startOffsetInModule();
7314 uint32_t end
= metadata
.srcStart
+ f
.endOffsetInModule();
7316 ScriptSource
* source
= metadata
.maybeScriptSource();
7317 JSStringBuilder
out(cx
);
7319 if (!out
.append("function ")) {
7324 if (!ScriptSource::loadSource(cx
, source
, &haveSource
)) {
7329 // asm.js functions can't be anonymous
7330 MOZ_ASSERT(fun
->fullExplicitName());
7331 if (!out
.append(fun
->fullExplicitName())) {
7334 if (!out
.append("() {\n [native code]\n}")) {
7338 Rooted
<JSLinearString
*> src(cx
, source
->substring(cx
, begin
, end
));
7342 if (!out
.append(src
)) {
7347 return out
.finishString();
7350 bool js::IsValidAsmJSHeapLength(size_t length
) {
7351 if (length
< MinHeapLength
) {
7355 // The heap length is limited by what a wasm memory32 can handle.
7356 if (length
> MaxMemoryBytes(IndexType::I32
)) {
7360 return wasm::IsValidARMImmediate(length
);