Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
* vim: set ts=8 sts=2 et sw=2 tw=80:
3
*
4
* Copyright 2014 Mozilla Foundation
5
*
6
* Licensed under the Apache License, Version 2.0 (the "License");
7
* you may not use this file except in compliance with the License.
8
* You may obtain a copy of the License at
9
*
11
*
12
* Unless required by applicable law or agreed to in writing, software
13
* distributed under the License is distributed on an "AS IS" BASIS,
14
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
* See the License for the specific language governing permissions and
16
* limitations under the License.
17
*/
18
19
#include "wasm/AsmJS.h"
20
21
#include "mozilla/ArrayUtils.h"
22
#include "mozilla/Attributes.h"
23
#include "mozilla/Compression.h"
24
#include "mozilla/MathAlgorithms.h"
25
#include "mozilla/ScopeExit.h"
26
#include "mozilla/Sprintf.h" // SprintfLiteral
27
#include "mozilla/Unused.h"
28
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
29
#include "mozilla/Variant.h"
30
31
#include <algorithm>
32
#include <new>
33
34
#include "jsmath.h"
35
36
#include "frontend/ParseNode.h"
37
#include "frontend/Parser.h"
38
#include "gc/Policy.h"
39
#include "js/BuildId.h" // JS::BuildIdCharVector
40
#include "js/MemoryMetrics.h"
41
#include "js/Printf.h"
42
#include "js/SourceText.h"
43
#include "js/StableStringChars.h"
44
#include "js/Wrapper.h"
45
#include "util/StringBuffer.h"
46
#include "util/Text.h"
47
#include "vm/ErrorReporting.h"
48
#include "vm/SelfHosting.h"
49
#include "vm/Time.h"
50
#include "vm/TypedArrayObject.h"
51
#include "wasm/WasmCompile.h"
52
#include "wasm/WasmGenerator.h"
53
#include "wasm/WasmInstance.h"
54
#include "wasm/WasmIonCompile.h"
55
#include "wasm/WasmJS.h"
56
#include "wasm/WasmSerialize.h"
57
#include "wasm/WasmValidate.h"
58
59
#include "frontend/SharedContext-inl.h"
60
#include "vm/ArrayBufferObject-inl.h"
61
#include "vm/JSObject-inl.h"
62
63
using namespace js;
64
using namespace js::frontend;
65
using namespace js::jit;
66
using namespace js::wasm;
67
68
using JS::AsmJSOption;
69
using JS::AutoStableStringChars;
70
using JS::GenericNaN;
71
using JS::SourceOwnership;
72
using JS::SourceText;
73
using mozilla::Abs;
74
using mozilla::ArrayEqual;
75
using mozilla::AsVariant;
76
using mozilla::CeilingLog2;
77
using mozilla::HashGeneric;
78
using mozilla::IsNaN;
79
using mozilla::IsNegativeZero;
80
using mozilla::IsPositiveZero;
81
using mozilla::IsPowerOfTwo;
82
using mozilla::PodZero;
83
using mozilla::PositiveInfinity;
84
using mozilla::Unused;
85
using mozilla::Utf8Unit;
86
using mozilla::Compression::LZ4;
87
88
/*****************************************************************************/
89
90
// The asm.js valid heap lengths are precisely the WASM valid heap lengths for
91
// ARM greater or equal to MinHeapLength
92
static const size_t MinHeapLength = PageSize;
93
94
static uint32_t RoundUpToNextValidAsmJSHeapLength(uint32_t length) {
95
if (length <= MinHeapLength) {
96
return MinHeapLength;
97
}
98
99
return wasm::RoundUpToNextValidARMImmediate(length);
100
}
101
102
/*****************************************************************************/
103
// asm.js module object
104
105
// The asm.js spec recognizes this set of builtin Math functions.
106
enum AsmJSMathBuiltinFunction {
107
AsmJSMathBuiltin_sin,
108
AsmJSMathBuiltin_cos,
109
AsmJSMathBuiltin_tan,
110
AsmJSMathBuiltin_asin,
111
AsmJSMathBuiltin_acos,
112
AsmJSMathBuiltin_atan,
113
AsmJSMathBuiltin_ceil,
114
AsmJSMathBuiltin_floor,
115
AsmJSMathBuiltin_exp,
116
AsmJSMathBuiltin_log,
117
AsmJSMathBuiltin_pow,
118
AsmJSMathBuiltin_sqrt,
119
AsmJSMathBuiltin_abs,
120
AsmJSMathBuiltin_atan2,
121
AsmJSMathBuiltin_imul,
122
AsmJSMathBuiltin_fround,
123
AsmJSMathBuiltin_min,
124
AsmJSMathBuiltin_max,
125
AsmJSMathBuiltin_clz32
126
};
127
128
// LitValPOD is a restricted version of LitVal suitable for asm.js that is
129
// always POD.
130
131
struct LitValPOD {
132
PackedTypeCode valType_;
133
union U {
134
uint32_t u32_;
135
uint64_t u64_;
136
float f32_;
137
double f64_;
138
} u;
139
140
LitValPOD() = default;
141
142
explicit LitValPOD(uint32_t u32) : valType_(ValType(ValType::I32).packed()) {
143
u.u32_ = u32;
144
}
145
explicit LitValPOD(uint64_t u64) : valType_(ValType(ValType::I64).packed()) {
146
u.u64_ = u64;
147
}
148
149
explicit LitValPOD(float f32) : valType_(ValType(ValType::F32).packed()) {
150
u.f32_ = f32;
151
}
152
explicit LitValPOD(double f64) : valType_(ValType(ValType::F64).packed()) {
153
u.f64_ = f64;
154
}
155
156
LitVal asLitVal() const {
157
switch (UnpackTypeCodeType(valType_)) {
158
case TypeCode::I32:
159
return LitVal(u.u32_);
160
case TypeCode::I64:
161
return LitVal(u.u64_);
162
case TypeCode::F32:
163
return LitVal(u.f32_);
164
case TypeCode::F64:
165
return LitVal(u.f64_);
166
default:
167
MOZ_CRASH("Can't happen");
168
}
169
}
170
};
171
172
static_assert(std::is_pod<LitValPOD>::value,
173
"must be POD to be simply serialized/deserialized");
174
175
// An AsmJSGlobal represents a JS global variable in the asm.js module function.
176
class AsmJSGlobal {
177
public:
178
enum Which {
179
Variable,
180
FFI,
181
ArrayView,
182
ArrayViewCtor,
183
MathBuiltinFunction,
184
Constant
185
};
186
enum VarInitKind { InitConstant, InitImport };
187
enum ConstantKind { GlobalConstant, MathConstant };
188
189
private:
190
struct CacheablePod {
191
Which which_;
192
union V {
193
struct {
194
VarInitKind initKind_;
195
union U {
196
PackedTypeCode importValType_;
197
LitValPOD val_;
198
} u;
199
} var;
200
uint32_t ffiIndex_;
201
Scalar::Type viewType_;
202
AsmJSMathBuiltinFunction mathBuiltinFunc_;
203
struct {
204
ConstantKind kind_;
205
double value_;
206
} constant;
207
} u;
208
} pod;
209
CacheableChars field_;
210
211
friend class ModuleValidatorShared;
212
template <typename Unit>
213
friend class ModuleValidator;
214
215
public:
216
AsmJSGlobal() = default;
217
AsmJSGlobal(Which which, UniqueChars field) {
218
mozilla::PodZero(&pod); // zero padding for Valgrind
219
pod.which_ = which;
220
field_ = std::move(field);
221
}
222
const char* field() const { return field_.get(); }
223
Which which() const { return pod.which_; }
224
VarInitKind varInitKind() const {
225
MOZ_ASSERT(pod.which_ == Variable);
226
return pod.u.var.initKind_;
227
}
228
LitValPOD varInitVal() const {
229
MOZ_ASSERT(pod.which_ == Variable);
230
MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
231
return pod.u.var.u.val_;
232
}
233
ValType varInitImportType() const {
234
MOZ_ASSERT(pod.which_ == Variable);
235
MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
236
return ValType(pod.u.var.u.importValType_);
237
}
238
uint32_t ffiIndex() const {
239
MOZ_ASSERT(pod.which_ == FFI);
240
return pod.u.ffiIndex_;
241
}
242
// When a view is created from an imported constructor:
243
// var I32 = stdlib.Int32Array;
244
// var i32 = new I32(buffer);
245
// the second import has nothing to validate and thus has a null field.
246
Scalar::Type viewType() const {
247
MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
248
return pod.u.viewType_;
249
}
250
AsmJSMathBuiltinFunction mathBuiltinFunction() const {
251
MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
252
return pod.u.mathBuiltinFunc_;
253
}
254
ConstantKind constantKind() const {
255
MOZ_ASSERT(pod.which_ == Constant);
256
return pod.u.constant.kind_;
257
}
258
double constantValue() const {
259
MOZ_ASSERT(pod.which_ == Constant);
260
return pod.u.constant.value_;
261
}
262
};
263
264
typedef Vector<AsmJSGlobal, 0, SystemAllocPolicy> AsmJSGlobalVector;
265
266
// An AsmJSImport is slightly different than an asm.js FFI function: a single
267
// asm.js FFI function can be called with many different signatures. When
268
// compiled to wasm, each unique FFI function paired with signature generates a
269
// wasm import.
270
class AsmJSImport {
271
uint32_t ffiIndex_;
272
273
public:
274
AsmJSImport() = default;
275
explicit AsmJSImport(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
276
uint32_t ffiIndex() const { return ffiIndex_; }
277
};
278
279
typedef Vector<AsmJSImport, 0, SystemAllocPolicy> AsmJSImportVector;
280
281
// An AsmJSExport logically extends Export with the extra information needed for
282
// an asm.js exported function, viz., the offsets in module's source chars in
283
// case the function is toString()ed.
284
class AsmJSExport {
285
uint32_t funcIndex_ = 0;
286
287
// All fields are treated as cacheable POD:
288
uint32_t startOffsetInModule_ = 0; // Store module-start-relative offsets
289
uint32_t endOffsetInModule_ = 0; // so preserved by serialization.
290
291
public:
292
AsmJSExport() = default;
293
AsmJSExport(uint32_t funcIndex, uint32_t startOffsetInModule,
294
uint32_t endOffsetInModule)
295
: funcIndex_(funcIndex),
296
startOffsetInModule_(startOffsetInModule),
297
endOffsetInModule_(endOffsetInModule) {}
298
uint32_t funcIndex() const { return funcIndex_; }
299
uint32_t startOffsetInModule() const { return startOffsetInModule_; }
300
uint32_t endOffsetInModule() const { return endOffsetInModule_; }
301
};
302
303
typedef Vector<AsmJSExport, 0, SystemAllocPolicy> AsmJSExportVector;
304
305
// Holds the immutable guts of an AsmJSModule.
306
//
307
// AsmJSMetadata is built incrementally by ModuleValidator and then shared
308
// immutably between AsmJSModules.
309
310
struct AsmJSMetadataCacheablePod {
311
uint32_t numFFIs = 0;
312
uint32_t srcLength = 0;
313
uint32_t srcLengthWithRightBrace = 0;
314
315
AsmJSMetadataCacheablePod() = default;
316
};
317
318
struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod {
319
AsmJSGlobalVector asmJSGlobals;
320
AsmJSImportVector asmJSImports;
321
AsmJSExportVector asmJSExports;
322
CacheableCharsVector asmJSFuncNames;
323
CacheableChars globalArgumentName;
324
CacheableChars importArgumentName;
325
CacheableChars bufferArgumentName;
326
327
// These values are not serialized since they are relative to the
328
// containing script which can be different between serialization and
329
// deserialization contexts. Thus, they must be set explicitly using the
330
// ambient Parser/ScriptSource after deserialization.
331
//
332
// srcStart refers to the offset in the ScriptSource to the beginning of
333
// the asm.js module function. If the function has been created with the
334
// Function constructor, this will be the first character in the function
335
// source. Otherwise, it will be the opening parenthesis of the arguments
336
// list.
337
uint32_t toStringStart;
338
uint32_t srcStart;
339
bool strict;
340
ScriptSourceHolder scriptSource;
341
342
uint32_t srcEndBeforeCurly() const { return srcStart + srcLength; }
343
uint32_t srcEndAfterCurly() const {
344
return srcStart + srcLengthWithRightBrace;
345
}
346
347
AsmJSMetadata()
348
: Metadata(ModuleKind::AsmJS),
349
toStringStart(0),
350
srcStart(0),
351
strict(false) {}
352
~AsmJSMetadata() override {}
353
354
const AsmJSExport& lookupAsmJSExport(uint32_t funcIndex) const {
355
// The AsmJSExportVector isn't stored in sorted order so do a linear
356
// search. This is for the super-cold and already-expensive toString()
357
// path and the number of exports is generally small.
358
for (const AsmJSExport& exp : asmJSExports) {
359
if (exp.funcIndex() == funcIndex) {
360
return exp;
361
}
362
}
363
MOZ_CRASH("missing asm.js func export");
364
}
365
366
bool mutedErrors() const override {
367
return scriptSource.get()->mutedErrors();
368
}
369
const char16_t* displayURL() const override {
370
return scriptSource.get()->hasDisplayURL()
371
? scriptSource.get()->displayURL()
372
: nullptr;
373
}
374
ScriptSource* maybeScriptSource() const override {
375
return scriptSource.get();
376
}
377
bool getFuncName(NameContext ctx, uint32_t funcIndex,
378
UTF8Bytes* name) const override {
379
const char* p = asmJSFuncNames[funcIndex].get();
380
if (!p) {
381
return true;
382
}
383
return name->append(p, strlen(p));
384
}
385
386
AsmJSMetadataCacheablePod& pod() { return *this; }
387
const AsmJSMetadataCacheablePod& pod() const { return *this; }
388
};
389
390
typedef RefPtr<AsmJSMetadata> MutableAsmJSMetadata;
391
392
/*****************************************************************************/
393
// ParseNode utilities
394
395
static inline ParseNode* NextNode(ParseNode* pn) { return pn->pn_next; }
396
397
static inline ParseNode* UnaryKid(ParseNode* pn) {
398
return pn->as<UnaryNode>().kid();
399
}
400
401
static inline ParseNode* BinaryRight(ParseNode* pn) {
402
return pn->as<BinaryNode>().right();
403
}
404
405
static inline ParseNode* BinaryLeft(ParseNode* pn) {
406
return pn->as<BinaryNode>().left();
407
}
408
409
static inline ParseNode* ReturnExpr(ParseNode* pn) {
410
MOZ_ASSERT(pn->isKind(ParseNodeKind::ReturnStmt));
411
return UnaryKid(pn);
412
}
413
414
static inline ParseNode* TernaryKid1(ParseNode* pn) {
415
return pn->as<TernaryNode>().kid1();
416
}
417
418
static inline ParseNode* TernaryKid2(ParseNode* pn) {
419
return pn->as<TernaryNode>().kid2();
420
}
421
422
static inline ParseNode* TernaryKid3(ParseNode* pn) {
423
return pn->as<TernaryNode>().kid3();
424
}
425
426
static inline ParseNode* ListHead(ParseNode* pn) {
427
return pn->as<ListNode>().head();
428
}
429
430
static inline unsigned ListLength(ParseNode* pn) {
431
return pn->as<ListNode>().count();
432
}
433
434
static inline ParseNode* CallCallee(ParseNode* pn) {
435
MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
436
return BinaryLeft(pn);
437
}
438
439
static inline unsigned CallArgListLength(ParseNode* pn) {
440
MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
441
return ListLength(BinaryRight(pn));
442
}
443
444
static inline ParseNode* CallArgList(ParseNode* pn) {
445
MOZ_ASSERT(pn->isKind(ParseNodeKind::CallExpr));
446
return ListHead(BinaryRight(pn));
447
}
448
449
static inline ParseNode* VarListHead(ParseNode* pn) {
450
MOZ_ASSERT(pn->isKind(ParseNodeKind::VarStmt) ||
451
pn->isKind(ParseNodeKind::ConstDecl));
452
return ListHead(pn);
453
}
454
455
static inline bool IsDefaultCase(ParseNode* pn) {
456
return pn->as<CaseClause>().isDefault();
457
}
458
459
static inline ParseNode* CaseExpr(ParseNode* pn) {
460
return pn->as<CaseClause>().caseExpression();
461
}
462
463
static inline ParseNode* CaseBody(ParseNode* pn) {
464
return pn->as<CaseClause>().statementList();
465
}
466
467
static inline ParseNode* BinaryOpLeft(ParseNode* pn) {
468
MOZ_ASSERT(pn->isBinaryOperation());
469
MOZ_ASSERT(pn->as<ListNode>().count() == 2);
470
return ListHead(pn);
471
}
472
473
static inline ParseNode* BinaryOpRight(ParseNode* pn) {
474
MOZ_ASSERT(pn->isBinaryOperation());
475
MOZ_ASSERT(pn->as<ListNode>().count() == 2);
476
return NextNode(ListHead(pn));
477
}
478
479
static inline ParseNode* BitwiseLeft(ParseNode* pn) { return BinaryOpLeft(pn); }
480
481
static inline ParseNode* BitwiseRight(ParseNode* pn) {
482
return BinaryOpRight(pn);
483
}
484
485
static inline ParseNode* MultiplyLeft(ParseNode* pn) {
486
MOZ_ASSERT(pn->isKind(ParseNodeKind::MulExpr));
487
return BinaryOpLeft(pn);
488
}
489
490
static inline ParseNode* MultiplyRight(ParseNode* pn) {
491
MOZ_ASSERT(pn->isKind(ParseNodeKind::MulExpr));
492
return BinaryOpRight(pn);
493
}
494
495
static inline ParseNode* AddSubLeft(ParseNode* pn) {
496
MOZ_ASSERT(pn->isKind(ParseNodeKind::AddExpr) ||
497
pn->isKind(ParseNodeKind::SubExpr));
498
return BinaryOpLeft(pn);
499
}
500
501
static inline ParseNode* AddSubRight(ParseNode* pn) {
502
MOZ_ASSERT(pn->isKind(ParseNodeKind::AddExpr) ||
503
pn->isKind(ParseNodeKind::SubExpr));
504
return BinaryOpRight(pn);
505
}
506
507
static inline ParseNode* DivOrModLeft(ParseNode* pn) {
508
MOZ_ASSERT(pn->isKind(ParseNodeKind::DivExpr) ||
509
pn->isKind(ParseNodeKind::ModExpr));
510
return BinaryOpLeft(pn);
511
}
512
513
static inline ParseNode* DivOrModRight(ParseNode* pn) {
514
MOZ_ASSERT(pn->isKind(ParseNodeKind::DivExpr) ||
515
pn->isKind(ParseNodeKind::ModExpr));
516
return BinaryOpRight(pn);
517
}
518
519
static inline ParseNode* ComparisonLeft(ParseNode* pn) {
520
return BinaryOpLeft(pn);
521
}
522
523
static inline ParseNode* ComparisonRight(ParseNode* pn) {
524
return BinaryOpRight(pn);
525
}
526
527
static inline bool IsExpressionStatement(ParseNode* pn) {
528
return pn->isKind(ParseNodeKind::ExpressionStmt);
529
}
530
531
static inline ParseNode* ExpressionStatementExpr(ParseNode* pn) {
532
MOZ_ASSERT(pn->isKind(ParseNodeKind::ExpressionStmt));
533
return UnaryKid(pn);
534
}
535
536
static inline PropertyName* LoopControlMaybeLabel(ParseNode* pn) {
537
MOZ_ASSERT(pn->isKind(ParseNodeKind::BreakStmt) ||
538
pn->isKind(ParseNodeKind::ContinueStmt));
539
return pn->as<LoopControlStatement>().label();
540
}
541
542
static inline PropertyName* LabeledStatementLabel(ParseNode* pn) {
543
return pn->as<LabeledStatement>().label();
544
}
545
546
static inline ParseNode* LabeledStatementStatement(ParseNode* pn) {
547
return pn->as<LabeledStatement>().statement();
548
}
549
550
static double NumberNodeValue(ParseNode* pn) {
551
return pn->as<NumericLiteral>().value();
552
}
553
554
static bool NumberNodeHasFrac(ParseNode* pn) {
555
return pn->as<NumericLiteral>().decimalPoint() == HasDecimal;
556
}
557
558
static ParseNode* DotBase(ParseNode* pn) {
559
return &pn->as<PropertyAccess>().expression();
560
}
561
562
static PropertyName* DotMember(ParseNode* pn) {
563
return &pn->as<PropertyAccess>().name();
564
}
565
566
static ParseNode* ElemBase(ParseNode* pn) {
567
return &pn->as<PropertyByValue>().expression();
568
}
569
570
static ParseNode* ElemIndex(ParseNode* pn) {
571
return &pn->as<PropertyByValue>().key();
572
}
573
574
static inline PropertyName* FunctionName(FunctionNode* funNode) {
575
if (JSAtom* name = funNode->funbox()->explicitName()) {
576
return name->asPropertyName();
577
}
578
return nullptr;
579
}
580
581
static inline ParseNode* FunctionStatementList(FunctionNode* funNode) {
582
MOZ_ASSERT(funNode->body()->isKind(ParseNodeKind::ParamsBody));
583
LexicalScopeNode* last =
584
&funNode->body()->as<ListNode>().last()->as<LexicalScopeNode>();
585
MOZ_ASSERT(last->isEmptyScope());
586
ParseNode* body = last->scopeBody();
587
MOZ_ASSERT(body->isKind(ParseNodeKind::StatementList));
588
return body;
589
}
590
591
static inline bool IsNormalObjectField(ParseNode* pn) {
592
return pn->isKind(ParseNodeKind::PropertyDefinition) &&
593
pn->as<PropertyDefinition>().accessorType() == AccessorType::None &&
594
BinaryLeft(pn)->isKind(ParseNodeKind::ObjectPropertyName);
595
}
596
597
static inline PropertyName* ObjectNormalFieldName(ParseNode* pn) {
598
MOZ_ASSERT(IsNormalObjectField(pn));
599
MOZ_ASSERT(BinaryLeft(pn)->isKind(ParseNodeKind::ObjectPropertyName));
600
return BinaryLeft(pn)->as<NameNode>().atom()->asPropertyName();
601
}
602
603
static inline ParseNode* ObjectNormalFieldInitializer(ParseNode* pn) {
604
MOZ_ASSERT(IsNormalObjectField(pn));
605
return BinaryRight(pn);
606
}
607
608
static inline bool IsUseOfName(ParseNode* pn, PropertyName* name) {
609
return pn->isName(name);
610
}
611
612
static inline bool IsIgnoredDirectiveName(JSContext* cx, JSAtom* atom) {
613
return atom != cx->names().useStrict;
614
}
615
616
static inline bool IsIgnoredDirective(JSContext* cx, ParseNode* pn) {
617
return pn->isKind(ParseNodeKind::ExpressionStmt) &&
618
UnaryKid(pn)->isKind(ParseNodeKind::StringExpr) &&
619
IsIgnoredDirectiveName(cx, UnaryKid(pn)->as<NameNode>().atom());
620
}
621
622
static inline bool IsEmptyStatement(ParseNode* pn) {
623
return pn->isKind(ParseNodeKind::EmptyStmt);
624
}
625
626
static inline ParseNode* SkipEmptyStatements(ParseNode* pn) {
627
while (pn && IsEmptyStatement(pn)) {
628
pn = pn->pn_next;
629
}
630
return pn;
631
}
632
633
static inline ParseNode* NextNonEmptyStatement(ParseNode* pn) {
634
return SkipEmptyStatements(pn->pn_next);
635
}
636
637
template <typename Unit>
638
static bool GetToken(AsmJSParser<Unit>& parser, TokenKind* tkp) {
639
auto& ts = parser.tokenStream;
640
TokenKind tk;
641
while (true) {
642
if (!ts.getToken(&tk, TokenStreamShared::SlashIsRegExp)) {
643
return false;
644
}
645
if (tk != TokenKind::Semi) {
646
break;
647
}
648
}
649
*tkp = tk;
650
return true;
651
}
652
653
template <typename Unit>
654
static bool PeekToken(AsmJSParser<Unit>& parser, TokenKind* tkp) {
655
auto& ts = parser.tokenStream;
656
TokenKind tk;
657
while (true) {
658
if (!ts.peekToken(&tk, TokenStream::SlashIsRegExp)) {
659
return false;
660
}
661
if (tk != TokenKind::Semi) {
662
break;
663
}
664
ts.consumeKnownToken(TokenKind::Semi, TokenStreamShared::SlashIsRegExp);
665
}
666
*tkp = tk;
667
return true;
668
}
669
670
template <typename Unit>
671
static bool ParseVarOrConstStatement(AsmJSParser<Unit>& parser,
672
ParseNode** var) {
673
TokenKind tk;
674
if (!PeekToken(parser, &tk)) {
675
return false;
676
}
677
if (tk != TokenKind::Var && tk != TokenKind::Const) {
678
*var = nullptr;
679
return true;
680
}
681
682
*var = parser.statementListItem(YieldIsName);
683
if (!*var) {
684
return false;
685
}
686
687
MOZ_ASSERT((*var)->isKind(ParseNodeKind::VarStmt) ||
688
(*var)->isKind(ParseNodeKind::ConstDecl));
689
return true;
690
}
691
692
/*****************************************************************************/
693
694
// Represents the type and value of an asm.js numeric literal.
695
//
696
// A literal is a double iff the literal contains a decimal point (even if the
697
// fractional part is 0). Otherwise, integers may be classified:
698
// fixnum: [0, 2^31)
699
// negative int: [-2^31, 0)
700
// big unsigned: [2^31, 2^32)
701
// out of range: otherwise
702
// Lastly, a literal may be a float literal which is any double or integer
703
// literal coerced with Math.fround.
704
class NumLit {
705
public:
706
enum Which {
707
Fixnum,
708
NegativeInt,
709
BigUnsigned,
710
Double,
711
Float,
712
OutOfRangeInt = -1
713
};
714
715
private:
716
Which which_;
717
JS::Value value_;
718
719
public:
720
NumLit() = default;
721
722
NumLit(Which w, const Value& v) : which_(w), value_(v) {}
723
724
Which which() const { return which_; }
725
726
int32_t toInt32() const {
727
MOZ_ASSERT(which_ == Fixnum || which_ == NegativeInt ||
728
which_ == BigUnsigned);
729
return value_.toInt32();
730
}
731
732
uint32_t toUint32() const { return (uint32_t)toInt32(); }
733
734
double toDouble() const {
735
MOZ_ASSERT(which_ == Double);
736
return value_.toDouble();
737
}
738
739
float toFloat() const {
740
MOZ_ASSERT(which_ == Float);
741
return float(value_.toDouble());
742
}
743
744
Value scalarValue() const {
745
MOZ_ASSERT(which_ != OutOfRangeInt);
746
return value_;
747
}
748
749
bool valid() const { return which_ != OutOfRangeInt; }
750
751
bool isZeroBits() const {
752
MOZ_ASSERT(valid());
753
switch (which()) {
754
case NumLit::Fixnum:
755
case NumLit::NegativeInt:
756
case NumLit::BigUnsigned:
757
return toInt32() == 0;
758
case NumLit::Double:
759
return IsPositiveZero(toDouble());
760
case NumLit::Float:
761
return IsPositiveZero(toFloat());
762
case NumLit::OutOfRangeInt:
763
MOZ_CRASH("can't be here because of valid() check above");
764
}
765
return false;
766
}
767
768
LitValPOD value() const {
769
switch (which_) {
770
case NumLit::Fixnum:
771
case NumLit::NegativeInt:
772
case NumLit::BigUnsigned:
773
return LitValPOD(toUint32());
774
case NumLit::Float:
775
return LitValPOD(toFloat());
776
case NumLit::Double:
777
return LitValPOD(toDouble());
778
case NumLit::OutOfRangeInt:;
779
}
780
MOZ_CRASH("bad literal");
781
}
782
};
783
784
// Represents the type of a general asm.js expression.
785
//
786
// A canonical subset of types representing the coercion targets: Int, Float,
787
// Double.
788
//
789
// Void is also part of the canonical subset.
790
791
class Type {
792
public:
793
enum Which {
794
Fixnum = NumLit::Fixnum,
795
Signed = NumLit::NegativeInt,
796
Unsigned = NumLit::BigUnsigned,
797
DoubleLit = NumLit::Double,
798
Float = NumLit::Float,
799
Double,
800
MaybeDouble,
801
MaybeFloat,
802
Floatish,
803
Int,
804
Intish,
805
Void
806
};
807
808
private:
809
Which which_;
810
811
public:
812
Type() = default;
813
MOZ_IMPLICIT Type(Which w) : which_(w) {}
814
815
// Map an already canonicalized Type to the return type of a function call.
816
static Type ret(Type t) {
817
MOZ_ASSERT(t.isCanonical());
818
// The 32-bit external type is Signed, not Int.
819
return t.isInt() ? Signed : t;
820
}
821
822
static Type lit(const NumLit& lit) {
823
MOZ_ASSERT(lit.valid());
824
Which which = Type::Which(lit.which());
825
MOZ_ASSERT(which >= Fixnum && which <= Float);
826
Type t;
827
t.which_ = which;
828
return t;
829
}
830
831
// Map |t| to one of the canonical vartype representations of a
832
// wasm::ValType.
833
static Type canonicalize(Type t) {
834
switch (t.which()) {
835
case Fixnum:
836
case Signed:
837
case Unsigned:
838
case Int:
839
return Int;
840
841
case Float:
842
return Float;
843
844
case DoubleLit:
845
case Double:
846
return Double;
847
848
case Void:
849
return Void;
850
851
case MaybeDouble:
852
case MaybeFloat:
853
case Floatish:
854
case Intish:
855
// These types need some kind of coercion, they can't be mapped
856
// to an VarType.
857
break;
858
}
859
MOZ_CRASH("Invalid vartype");
860
}
861
862
Which which() const { return which_; }
863
864
bool operator==(Type rhs) const { return which_ == rhs.which_; }
865
bool operator!=(Type rhs) const { return which_ != rhs.which_; }
866
867
bool operator<=(Type rhs) const {
868
switch (rhs.which_) {
869
case Signed:
870
return isSigned();
871
case Unsigned:
872
return isUnsigned();
873
case DoubleLit:
874
return isDoubleLit();
875
case Double:
876
return isDouble();
877
case Float:
878
return isFloat();
879
case MaybeDouble:
880
return isMaybeDouble();
881
case MaybeFloat:
882
return isMaybeFloat();
883
case Floatish:
884
return isFloatish();
885
case Int:
886
return isInt();
887
case Intish:
888
return isIntish();
889
case Fixnum:
890
return isFixnum();
891
case Void:
892
return isVoid();
893
}
894
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected rhs type");
895
}
896
897
bool isFixnum() const { return which_ == Fixnum; }
898
899
bool isSigned() const { return which_ == Signed || which_ == Fixnum; }
900
901
bool isUnsigned() const { return which_ == Unsigned || which_ == Fixnum; }
902
903
bool isInt() const { return isSigned() || isUnsigned() || which_ == Int; }
904
905
bool isIntish() const { return isInt() || which_ == Intish; }
906
907
bool isDoubleLit() const { return which_ == DoubleLit; }
908
909
bool isDouble() const { return isDoubleLit() || which_ == Double; }
910
911
bool isMaybeDouble() const { return isDouble() || which_ == MaybeDouble; }
912
913
bool isFloat() const { return which_ == Float; }
914
915
bool isMaybeFloat() const { return isFloat() || which_ == MaybeFloat; }
916
917
bool isFloatish() const { return isMaybeFloat() || which_ == Floatish; }
918
919
bool isVoid() const { return which_ == Void; }
920
921
bool isExtern() const { return isDouble() || isSigned(); }
922
923
// Check if this is one of the valid types for a function argument.
924
bool isArgType() const { return isInt() || isFloat() || isDouble(); }
925
926
// Check if this is one of the valid types for a function return value.
927
bool isReturnType() const {
928
return isSigned() || isFloat() || isDouble() || isVoid();
929
}
930
931
// Check if this is one of the valid types for a global variable.
932
bool isGlobalVarType() const { return isArgType(); }
933
934
// Check if this is one of the canonical vartype representations of a
935
// wasm::ValType, or is void. See Type::canonicalize().
936
bool isCanonical() const {
937
switch (which()) {
938
case Int:
939
case Float:
940
case Double:
941
case Void:
942
return true;
943
default:
944
return false;
945
}
946
}
947
948
// Check if this is a canonical representation of a wasm::ValType.
949
bool isCanonicalValType() const { return !isVoid() && isCanonical(); }
950
951
// Convert this canonical type to a wasm::ValType.
952
ValType canonicalToValType() const {
953
switch (which()) {
954
case Int:
955
return ValType::I32;
956
case Float:
957
return ValType::F32;
958
case Double:
959
return ValType::F64;
960
default:
961
MOZ_CRASH("Need canonical type");
962
}
963
}
964
965
Maybe<ValType> canonicalToReturnType() const {
966
return isVoid() ? Nothing() : Some(canonicalToValType());
967
}
968
969
// Convert this type to a wasm::TypeCode for use in a wasm
970
// block signature. This works for all types, including non-canonical
971
// ones. Consequently, the type isn't valid for subsequent asm.js
972
// validation; it's only valid for use in producing wasm.
973
TypeCode toWasmBlockSignatureType() const {
974
switch (which()) {
975
case Fixnum:
976
case Signed:
977
case Unsigned:
978
case Int:
979
case Intish:
980
return TypeCode::I32;
981
982
case Float:
983
case MaybeFloat:
984
case Floatish:
985
return TypeCode::F32;
986
987
case DoubleLit:
988
case Double:
989
case MaybeDouble:
990
return TypeCode::F64;
991
992
case Void:
993
return TypeCode::BlockVoid;
994
}
995
MOZ_CRASH("Invalid Type");
996
}
997
998
const char* toChars() const {
999
switch (which_) {
1000
case Double:
1001
return "double";
1002
case DoubleLit:
1003
return "doublelit";
1004
case MaybeDouble:
1005
return "double?";
1006
case Float:
1007
return "float";
1008
case Floatish:
1009
return "floatish";
1010
case MaybeFloat:
1011
return "float?";
1012
case Fixnum:
1013
return "fixnum";
1014
case Int:
1015
return "int";
1016
case Signed:
1017
return "signed";
1018
case Unsigned:
1019
return "unsigned";
1020
case Intish:
1021
return "intish";
1022
case Void:
1023
return "void";
1024
}
1025
MOZ_CRASH("Invalid Type");
1026
}
1027
};
1028
1029
static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
1030
1031
class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidatorShared {
1032
public:
1033
class Func {
1034
PropertyName* name_;
1035
uint32_t sigIndex_;
1036
uint32_t firstUse_;
1037
uint32_t funcDefIndex_;
1038
1039
bool defined_;
1040
1041
// Available when defined:
1042
uint32_t srcBegin_;
1043
uint32_t srcEnd_;
1044
uint32_t line_;
1045
Bytes bytes_;
1046
Uint32Vector callSiteLineNums_;
1047
1048
public:
1049
Func(PropertyName* name, uint32_t sigIndex, uint32_t firstUse,
1050
uint32_t funcDefIndex)
1051
: name_(name),
1052
sigIndex_(sigIndex),
1053
firstUse_(firstUse),
1054
funcDefIndex_(funcDefIndex),
1055
defined_(false),
1056
srcBegin_(0),
1057
srcEnd_(0),
1058
line_(0) {}
1059
1060
PropertyName* name() const { return name_; }
1061
uint32_t sigIndex() const { return sigIndex_; }
1062
uint32_t firstUse() const { return firstUse_; }
1063
bool defined() const { return defined_; }
1064
uint32_t funcDefIndex() const { return funcDefIndex_; }
1065
1066
void define(ParseNode* fn, uint32_t line, Bytes&& bytes,
1067
Uint32Vector&& callSiteLineNums) {
1068
MOZ_ASSERT(!defined_);
1069
defined_ = true;
1070
srcBegin_ = fn->pn_pos.begin;
1071
srcEnd_ = fn->pn_pos.end;
1072
line_ = line;
1073
bytes_ = std::move(bytes);
1074
callSiteLineNums_ = std::move(callSiteLineNums);
1075
}
1076
1077
uint32_t srcBegin() const {
1078
MOZ_ASSERT(defined_);
1079
return srcBegin_;
1080
}
1081
uint32_t srcEnd() const {
1082
MOZ_ASSERT(defined_);
1083
return srcEnd_;
1084
}
1085
uint32_t line() const {
1086
MOZ_ASSERT(defined_);
1087
return line_;
1088
}
1089
const Bytes& bytes() const {
1090
MOZ_ASSERT(defined_);
1091
return bytes_;
1092
}
1093
Uint32Vector& callSiteLineNums() {
1094
MOZ_ASSERT(defined_);
1095
return callSiteLineNums_;
1096
}
1097
};
1098
1099
using ConstFuncVector = Vector<const Func*>;
1100
using FuncVector = Vector<Func>;
1101
1102
class Table {
1103
uint32_t sigIndex_;
1104
PropertyName* name_;
1105
uint32_t firstUse_;
1106
uint32_t mask_;
1107
bool defined_;
1108
1109
Table(Table&& rhs) = delete;
1110
1111
public:
1112
Table(uint32_t sigIndex, PropertyName* name, uint32_t firstUse,
1113
uint32_t mask)
1114
: sigIndex_(sigIndex),
1115
name_(name),
1116
firstUse_(firstUse),
1117
mask_(mask),
1118
defined_(false) {}
1119
1120
uint32_t sigIndex() const { return sigIndex_; }
1121
PropertyName* name() const { return name_; }
1122
uint32_t firstUse() const { return firstUse_; }
1123
unsigned mask() const { return mask_; }
1124
bool defined() const { return defined_; }
1125
void define() {
1126
MOZ_ASSERT(!defined_);
1127
defined_ = true;
1128
}
1129
};
1130
1131
using TableVector = Vector<Table*>;
1132
1133
class Global {
1134
public:
1135
enum Which {
1136
Variable,
1137
ConstantLiteral,
1138
ConstantImport,
1139
Function,
1140
Table,
1141
FFI,
1142
ArrayView,
1143
ArrayViewCtor,
1144
MathBuiltinFunction
1145
};
1146
1147
private:
1148
Which which_;
1149
union U {
1150
struct VarOrConst {
1151
Type::Which type_;
1152
unsigned index_;
1153
NumLit literalValue_;
1154
1155
VarOrConst(unsigned index, const NumLit& lit)
1156
: type_(Type::lit(lit).which()),
1157
index_(index),
1158
literalValue_(lit) // copies |lit|
1159
{}
1160
1161
VarOrConst(unsigned index, Type::Which which)
1162
: type_(which), index_(index) {
1163
// The |literalValue_| field remains unused and
1164
// uninitialized for non-constant variables.
1165
}
1166
1167
explicit VarOrConst(double constant)
1168
: type_(Type::Double),
1169
literalValue_(NumLit::Double, DoubleValue(constant)) {
1170
// The index_ field is unused and uninitialized for
1171
// constant doubles.
1172
}
1173
} varOrConst;
1174
uint32_t funcDefIndex_;
1175
uint32_t tableIndex_;
1176
uint32_t ffiIndex_;
1177
Scalar::Type viewType_;
1178
AsmJSMathBuiltinFunction mathBuiltinFunc_;
1179
1180
// |varOrConst|, through |varOrConst.literalValue_|, has a
1181
// non-trivial constructor and therefore MUST be placement-new'd
1182
// into existence.
1183
MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
1184
U() : funcDefIndex_(0) {}
1185
MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
1186
} u;
1187
1188
friend class ModuleValidatorShared;
1189
template <typename Unit>
1190
friend class ModuleValidator;
1191
friend class js::LifoAlloc;
1192
1193
explicit Global(Which which) : which_(which) {}
1194
1195
public:
1196
Which which() const { return which_; }
1197
Type varOrConstType() const {
1198
MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral ||
1199
which_ == ConstantImport);
1200
return u.varOrConst.type_;
1201
}
1202
unsigned varOrConstIndex() const {
1203
MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
1204
return u.varOrConst.index_;
1205
}
1206
bool isConst() const {
1207
return which_ == ConstantLiteral || which_ == ConstantImport;
1208
}
1209
NumLit constLiteralValue() const {
1210
MOZ_ASSERT(which_ == ConstantLiteral);
1211
return u.varOrConst.literalValue_;
1212
}
1213
uint32_t funcDefIndex() const {
1214
MOZ_ASSERT(which_ == Function);
1215
return u.funcDefIndex_;
1216
}
1217
uint32_t tableIndex() const {
1218
MOZ_ASSERT(which_ == Table);
1219
return u.tableIndex_;
1220
}
1221
unsigned ffiIndex() const {
1222
MOZ_ASSERT(which_ == FFI);
1223
return u.ffiIndex_;
1224
}
1225
bool isAnyArrayView() const {
1226
return which_ == ArrayView || which_ == ArrayViewCtor;
1227
}
1228
Scalar::Type viewType() const {
1229
MOZ_ASSERT(isAnyArrayView());
1230
return u.viewType_;
1231
}
1232
bool isMathFunction() const { return which_ == MathBuiltinFunction; }
1233
AsmJSMathBuiltinFunction mathBuiltinFunction() const {
1234
MOZ_ASSERT(which_ == MathBuiltinFunction);
1235
return u.mathBuiltinFunc_;
1236
}
1237
};
1238
1239
struct MathBuiltin {
1240
enum Kind { Function, Constant };
1241
Kind kind;
1242
1243
union {
1244
double cst;
1245
AsmJSMathBuiltinFunction func;
1246
} u;
1247
1248
MathBuiltin() : kind(Kind(-1)), u{} {}
1249
explicit MathBuiltin(double cst) : kind(Constant) { u.cst = cst; }
1250
explicit MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
1251
u.func = func;
1252
}
1253
};
1254
1255
struct ArrayView {
1256
ArrayView(PropertyName* name, Scalar::Type type) : name(name), type(type) {}
1257
1258
PropertyName* name;
1259
Scalar::Type type;
1260
};
1261
1262
protected:
1263
class HashableSig {
1264
uint32_t sigIndex_;
1265
const TypeDefVector& types_;
1266
1267
public:
1268
HashableSig(uint32_t sigIndex, const TypeDefVector& types)
1269
: sigIndex_(sigIndex), types_(types) {}
1270
uint32_t sigIndex() const { return sigIndex_; }
1271
const FuncType& funcType() const { return types_[sigIndex_].funcType(); }
1272
1273
// Implement HashPolicy:
1274
using Lookup = const FuncType&;
1275
static HashNumber hash(Lookup l) { return l.hash(); }
1276
static bool match(HashableSig lhs, Lookup rhs) {
1277
return lhs.funcType() == rhs;
1278
}
1279
};
1280
1281
class NamedSig : public HashableSig {
1282
PropertyName* name_;
1283
1284
public:
1285
NamedSig(PropertyName* name, uint32_t sigIndex, const TypeDefVector& types)
1286
: HashableSig(sigIndex, types), name_(name) {}
1287
PropertyName* name() const { return name_; }
1288
1289
// Implement HashPolicy:
1290
struct Lookup {
1291
PropertyName* name;
1292
const FuncType& funcType;
1293
Lookup(PropertyName* name, const FuncType& funcType)
1294
: name(name), funcType(funcType) {}
1295
};
1296
static HashNumber hash(Lookup l) {
1297
return HashGeneric(l.name, l.funcType.hash());
1298
}
1299
static bool match(NamedSig lhs, Lookup rhs) {
1300
return lhs.name() == rhs.name && lhs.funcType() == rhs.funcType;
1301
}
1302
};
1303
1304
using SigSet = HashSet<HashableSig, HashableSig>;
1305
using FuncImportMap = HashMap<NamedSig, uint32_t, NamedSig>;
1306
using GlobalMap = HashMap<PropertyName*, Global*>;
1307
using MathNameMap = HashMap<PropertyName*, MathBuiltin>;
1308
using ArrayViewVector = Vector<ArrayView>;
1309
1310
protected:
1311
JSContext* cx_;
1312
FunctionNode* moduleFunctionNode_;
1313
PropertyName* moduleFunctionName_;
1314
PropertyName* globalArgumentName_ = nullptr;
1315
PropertyName* importArgumentName_ = nullptr;
1316
PropertyName* bufferArgumentName_ = nullptr;
1317
MathNameMap standardLibraryMathNames_;
1318
RootedFunction dummyFunction_;
1319
1320
// Validation-internal state:
1321
LifoAlloc validationLifo_;
1322
FuncVector funcDefs_;
1323
TableVector tables_;
1324
GlobalMap globalMap_;
1325
SigSet sigSet_;
1326
FuncImportMap funcImportMap_;
1327
ArrayViewVector arrayViews_;
1328
1329
// State used to build the AsmJSModule in finish():
1330
CompilerEnvironment compilerEnv_;
1331
ModuleEnvironment env_;
1332
MutableAsmJSMetadata asmJSMetadata_;
1333
1334
// Error reporting:
1335
UniqueChars errorString_ = nullptr;
1336
uint32_t errorOffset_ = UINT32_MAX;
1337
bool errorOverRecursed_ = false;
1338
1339
protected:
1340
ModuleValidatorShared(JSContext* cx, FunctionNode* moduleFunctionNode)
1341
: cx_(cx),
1342
moduleFunctionNode_(moduleFunctionNode),
1343
moduleFunctionName_(FunctionName(moduleFunctionNode)),
1344
standardLibraryMathNames_(cx),
1345
dummyFunction_(cx),
1346
validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
1347
funcDefs_(cx),
1348
tables_(cx),
1349
globalMap_(cx),
1350
sigSet_(cx),
1351
funcImportMap_(cx),
1352
arrayViews_(cx),
1353
compilerEnv_(CompileMode::Once, Tier::Optimized, OptimizedBackend::Ion,
1354
DebugEnabled::False, /* multi value */ false,
1355
/* ref types */ false, /* gc types */ false,
1356
/* huge memory */ false, /* bigint */ false),
1357
env_(&compilerEnv_, Shareable::False, ModuleKind::AsmJS) {
1358
compilerEnv_.computeParameters(/* gc types */ false);
1359
env_.minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
1360
}
1361
1362
protected:
1363
MOZ_MUST_USE bool addStandardLibraryMathInfo() {
1364
static constexpr struct {
1365
const char* name;
1366
AsmJSMathBuiltinFunction func;
1367
} functions[] = {
1368
{"sin", AsmJSMathBuiltin_sin}, {"cos", AsmJSMathBuiltin_cos},
1369
{"tan", AsmJSMathBuiltin_tan}, {"asin", AsmJSMathBuiltin_asin},
1370
{"acos", AsmJSMathBuiltin_acos}, {"atan", AsmJSMathBuiltin_atan},
1371
{"ceil", AsmJSMathBuiltin_ceil}, {"floor", AsmJSMathBuiltin_floor},
1372
{"exp", AsmJSMathBuiltin_exp}, {"log", AsmJSMathBuiltin_log},
1373
{"pow", AsmJSMathBuiltin_pow}, {"sqrt", AsmJSMathBuiltin_sqrt},
1374
{"abs", AsmJSMathBuiltin_abs}, {"atan2", AsmJSMathBuiltin_atan2},
1375
{"imul", AsmJSMathBuiltin_imul}, {"clz32", AsmJSMathBuiltin_clz32},
1376
{"fround", AsmJSMathBuiltin_fround}, {"min", AsmJSMathBuiltin_min},
1377
{"max", AsmJSMathBuiltin_max},
1378
};
1379
1380
auto AddMathFunction = [this](const char* name,
1381
AsmJSMathBuiltinFunction func) {
1382
JSAtom* atom = Atomize(cx_, name, strlen(name));
1383
if (!atom) {
1384
return false;
1385
}
1386
MathBuiltin builtin(func);
1387
return this->standardLibraryMathNames_.putNew(atom->asPropertyName(),
1388
builtin);
1389
};
1390
1391
for (const auto& info : functions) {
1392
if (!AddMathFunction(info.name, info.func)) {
1393
return false;
1394
}
1395
}
1396
1397
static constexpr struct {
1398
const char* name;
1399
double value;
1400
} constants[] = {
1401
{"E", M_E},
1402
{"LN10", M_LN10},
1403
{"LN2", M_LN2},
1404
{"LOG2E", M_LOG2E},
1405
{"LOG10E", M_LOG10E},
1406
{"PI", M_PI},
1407
{"SQRT1_2", M_SQRT1_2},
1408
{"SQRT2", M_SQRT2},
1409
};
1410
1411
auto AddMathConstant = [this](const char* name, double cst) {
1412
JSAtom* atom = Atomize(cx_, name, strlen(name));
1413
if (!atom) {
1414
return false;
1415
}
1416
MathBuiltin builtin(cst);
1417
return this->standardLibraryMathNames_.putNew(atom->asPropertyName(),
1418
builtin);
1419
};
1420
1421
for (const auto& info : constants) {
1422
if (!AddMathConstant(info.name, info.value)) {
1423
return false;
1424
}
1425
}
1426
1427
return true;
1428
}
1429
1430
MOZ_MUST_USE bool initDummyFunction() {
1431
// This flows into FunctionBox, so must be tenured.
1432
dummyFunction_ = NewScriptedFunction(
1433
cx_, 0, FunctionFlags::INTERPRETED, nullptr,
1434
/* proto = */ nullptr, gc::AllocKind::FUNCTION, TenuredObject);
1435
if (!dummyFunction_) {
1436
return false;
1437
}
1438
1439
return true;
1440
}
1441
1442
public:
1443
JSContext* cx() const { return cx_; }
1444
PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
1445
PropertyName* globalArgumentName() const { return globalArgumentName_; }
1446
PropertyName* importArgumentName() const { return importArgumentName_; }
1447
PropertyName* bufferArgumentName() const { return bufferArgumentName_; }
1448
const ModuleEnvironment& env() { return env_; }
1449
1450
RootedFunction& dummyFunction() { return dummyFunction_; }
1451
uint32_t minMemoryLength() const { return env_.minMemoryLength; }
1452
1453
void initModuleFunctionName(PropertyName* name) {
1454
MOZ_ASSERT(!moduleFunctionName_);
1455
moduleFunctionName_ = name;
1456
}
1457
MOZ_MUST_USE bool initGlobalArgumentName(PropertyName* n) {
1458
globalArgumentName_ = n;
1459
if (n) {
1460
MOZ_ASSERT(n->isTenured());
1461
asmJSMetadata_->globalArgumentName = StringToNewUTF8CharsZ(cx_, *n);
1462
if (!asmJSMetadata_->globalArgumentName) {
1463
return false;
1464
}
1465
}
1466
return true;
1467
}
1468
MOZ_MUST_USE bool initImportArgumentName(PropertyName* n) {
1469
importArgumentName_ = n;
1470
if (n) {
1471
MOZ_ASSERT(n->isTenured());
1472
asmJSMetadata_->importArgumentName = StringToNewUTF8CharsZ(cx_, *n);
1473
if (!asmJSMetadata_->importArgumentName) {
1474
return false;
1475
}
1476
}
1477
return true;
1478
}
1479
MOZ_MUST_USE bool initBufferArgumentName(PropertyName* n) {
1480
bufferArgumentName_ = n;
1481
if (n) {
1482
MOZ_ASSERT(n->isTenured());
1483
asmJSMetadata_->bufferArgumentName = StringToNewUTF8CharsZ(cx_, *n);
1484
if (!asmJSMetadata_->bufferArgumentName) {
1485
return false;
1486
}
1487
}
1488
return true;
1489
}
1490
bool addGlobalVarInit(PropertyName* var, const NumLit& lit, Type type,
1491
bool isConst) {
1492
MOZ_ASSERT(type.isGlobalVarType());
1493
MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
1494
1495
uint32_t index = env_.globals.length();
1496
if (!env_.globals.emplaceBack(type.canonicalToValType(), !isConst, index,
1497
ModuleKind::AsmJS)) {
1498
return false;
1499
}
1500
1501
Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
1502
Global* global = validationLifo_.new_<Global>(which);
1503
if (!global) {
1504
return false;
1505
}
1506
if (isConst) {
1507
new (&global->u.varOrConst) Global::U::VarOrConst(index, lit);
1508
} else {
1509
new (&global->u.varOrConst) Global::U::VarOrConst(index, type.which());
1510
}
1511
if (!globalMap_.putNew(var, global)) {
1512
return false;
1513
}
1514
1515
AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
1516
g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
1517
g.pod.u.var.u.val_ = lit.value();
1518
return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1519
}
1520
bool addGlobalVarImport(PropertyName* var, PropertyName* field, Type type,
1521
bool isConst) {
1522
MOZ_ASSERT(type.isGlobalVarType());
1523
1524
UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
1525
if (!fieldChars) {
1526
return false;
1527
}
1528
1529
uint32_t index = env_.globals.length();
1530
ValType valType = type.canonicalToValType();
1531
if (!env_.globals.emplaceBack(valType, !isConst, index,
1532
ModuleKind::AsmJS)) {
1533
return false;
1534
}
1535
1536
Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
1537
Global* global = validationLifo_.new_<Global>(which);
1538
if (!global) {
1539
return false;
1540
}
1541
new (&global->u.varOrConst) Global::U::VarOrConst(index, type.which());
1542
if (!globalMap_.putNew(var, global)) {
1543
return false;
1544
}
1545
1546
AsmJSGlobal g(AsmJSGlobal::Variable, std::move(fieldChars));
1547
g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
1548
g.pod.u.var.u.importValType_ = valType.packed();
1549
return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1550
}
1551
bool addArrayView(PropertyName* var, Scalar::Type vt,
1552
PropertyName* maybeField) {
1553
UniqueChars fieldChars;
1554
if (maybeField) {
1555
fieldChars = StringToNewUTF8CharsZ(cx_, *maybeField);
1556
if (!fieldChars) {
1557
return false;
1558
}
1559
}
1560
1561
if (!arrayViews_.append(ArrayView(var, vt))) {
1562
return false;
1563
}
1564
1565
Global* global = validationLifo_.new_<Global>(Global::ArrayView);
1566
if (!global) {
1567
return false;
1568
}
1569
new (&global->u.viewType_) Scalar::Type(vt);
1570
if (!globalMap_.putNew(var, global)) {
1571
return false;
1572
}
1573
1574
AsmJSGlobal g(AsmJSGlobal::ArrayView, std::move(fieldChars));
1575
g.pod.u.viewType_ = vt;
1576
return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1577
}
1578
bool addMathBuiltinFunction(PropertyName* var, AsmJSMathBuiltinFunction func,
1579
PropertyName* field) {
1580
UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
1581
if (!fieldChars) {
1582
return false;
1583
}
1584
1585
Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction);
1586
if (!global) {
1587
return false;
1588
}
1589
new (&global->u.mathBuiltinFunc_) AsmJSMathBuiltinFunction(func);
1590
if (!globalMap_.putNew(var, global)) {
1591
return false;
1592
}
1593
1594
AsmJSGlobal g(AsmJSGlobal::MathBuiltinFunction, std::move(fieldChars));
1595
g.pod.u.mathBuiltinFunc_ = func;
1596
return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1597
}
1598
1599
private:
1600
bool addGlobalDoubleConstant(PropertyName* var, double constant) {
1601
Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral);
1602
if (!global) {
1603
return false;
1604
}
1605
new (&global->u.varOrConst) Global::U::VarOrConst(constant);
1606
return globalMap_.putNew(var, global);
1607
}
1608
1609
public:
1610
bool addMathBuiltinConstant(PropertyName* var, double constant,
1611
PropertyName* field) {
1612
UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
1613
if (!fieldChars) {
1614
return false;
1615
}
1616
1617
if (!addGlobalDoubleConstant(var, constant)) {
1618
return false;
1619
}
1620
1621
AsmJSGlobal g(AsmJSGlobal::Constant, std::move(fieldChars));
1622
g.pod.u.constant.value_ = constant;
1623
g.pod.u.constant.kind_ = AsmJSGlobal::MathConstant;
1624
return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1625
}
1626
bool addGlobalConstant(PropertyName* var, double constant,
1627
PropertyName* field) {
1628
UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
1629
if (!fieldChars) {
1630
return false;
1631
}
1632
1633
if (!addGlobalDoubleConstant(var, constant)) {
1634
return false;
1635
}
1636
1637
AsmJSGlobal g(AsmJSGlobal::Constant, std::move(fieldChars));
1638
g.pod.u.constant.value_ = constant;
1639
g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant;
1640
return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1641
}
1642
bool addArrayViewCtor(PropertyName* var, Scalar::Type vt,
1643
PropertyName* field) {
1644
UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
1645
if (!fieldChars) {
1646
return false;
1647
}
1648
1649
Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
1650
if (!global) {
1651
return false;
1652
}
1653
new (&global->u.viewType_) Scalar::Type(vt);
1654
if (!globalMap_.putNew(var, global)) {
1655
return false;
1656
}
1657
1658
AsmJSGlobal g(AsmJSGlobal::ArrayViewCtor, std::move(fieldChars));
1659
g.pod.u.viewType_ = vt;
1660
return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1661
}
1662
bool addFFI(PropertyName* var, PropertyName* field) {
1663
UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
1664
if (!fieldChars) {
1665
return false;
1666
}
1667
1668
if (asmJSMetadata_->numFFIs == UINT32_MAX) {
1669
return false;
1670
}
1671
uint32_t ffiIndex = asmJSMetadata_->numFFIs++;
1672
1673
Global* global = validationLifo_.new_<Global>(Global::FFI);
1674
if (!global) {
1675
return false;
1676
}
1677
new (&global->u.ffiIndex_) uint32_t(ffiIndex);
1678
if (!globalMap_.putNew(var, global)) {
1679
return false;
1680
}
1681
1682
AsmJSGlobal g(AsmJSGlobal::FFI, std::move(fieldChars));
1683
g.pod.u.ffiIndex_ = ffiIndex;
1684
return asmJSMetadata_->asmJSGlobals.append(std::move(g));
1685
}
1686
bool addExportField(const Func& func, PropertyName* maybeField) {
1687
// Record the field name of this export.