Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : *
4 : * Copyright 2014 Mozilla Foundation
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "wasm/AsmJS.h"
20 :
21 : #include "mozilla/Attributes.h"
22 : #include "mozilla/Compression.h"
23 : #include "mozilla/MathAlgorithms.h"
24 : #include "mozilla/Maybe.h"
25 : #include "mozilla/Unused.h"
26 :
27 : #include "jsmath.h"
28 : #include "jsprf.h"
29 : #include "jsstr.h"
30 : #include "jsutil.h"
31 :
32 : #include "jswrapper.h"
33 :
34 : #include "builtin/SIMD.h"
35 : #include "frontend/Parser.h"
36 : #include "gc/Policy.h"
37 : #include "jit/AtomicOperations.h"
38 : #include "js/MemoryMetrics.h"
39 : #include "vm/ErrorReporting.h"
40 : #include "vm/SelfHosting.h"
41 : #include "vm/StringBuffer.h"
42 : #include "vm/Time.h"
43 : #include "vm/TypedArrayObject.h"
44 : #include "wasm/WasmCompile.h"
45 : #include "wasm/WasmGenerator.h"
46 : #include "wasm/WasmInstance.h"
47 : #include "wasm/WasmJS.h"
48 : #include "wasm/WasmSerialize.h"
49 : #include "wasm/WasmValidate.h"
50 :
51 : #include "jsobjinlines.h"
52 :
53 : #include "frontend/ParseNode-inl.h"
54 : #include "vm/ArrayBufferObject-inl.h"
55 :
56 : using namespace js;
57 : using namespace js::frontend;
58 : using namespace js::jit;
59 : using namespace js::wasm;
60 :
61 : using mozilla::CeilingLog2;
62 : using mozilla::Compression::LZ4;
63 : using mozilla::HashGeneric;
64 : using mozilla::IsNaN;
65 : using mozilla::IsNegativeZero;
66 : using mozilla::IsPositiveZero;
67 : using mozilla::IsPowerOfTwo;
68 : using mozilla::Maybe;
69 : using mozilla::Move;
70 : using mozilla::PodCopy;
71 : using mozilla::PodEqual;
72 : using mozilla::PodZero;
73 : using mozilla::PositiveInfinity;
74 : using mozilla::Unused;
75 : using JS::AsmJSOption;
76 : using JS::GenericNaN;
77 :
78 : /*****************************************************************************/
79 :
80 : // The asm.js valid heap lengths are precisely the WASM valid heap lengths for ARM
81 : // greater or equal to MinHeapLength
82 : static const size_t MinHeapLength = PageSize;
83 :
84 : static uint32_t
85 0 : RoundUpToNextValidAsmJSHeapLength(uint32_t length)
86 : {
87 0 : if (length <= MinHeapLength)
88 0 : return MinHeapLength;
89 :
90 0 : return wasm::RoundUpToNextValidARMImmediate(length);
91 : }
92 :
93 :
94 : /*****************************************************************************/
95 : // asm.js module object
96 :
97 : // The asm.js spec recognizes this set of builtin Math functions.
98 : enum AsmJSMathBuiltinFunction
99 : {
100 : AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan,
101 : AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan,
102 : AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp,
103 : AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt,
104 : AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul,
105 : AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max,
106 : AsmJSMathBuiltin_clz32
107 : };
108 :
109 : // The asm.js spec will recognize this set of builtin Atomics functions.
110 : enum AsmJSAtomicsBuiltinFunction
111 : {
112 : AsmJSAtomicsBuiltin_compareExchange,
113 : AsmJSAtomicsBuiltin_exchange,
114 : AsmJSAtomicsBuiltin_load,
115 : AsmJSAtomicsBuiltin_store,
116 : AsmJSAtomicsBuiltin_add,
117 : AsmJSAtomicsBuiltin_sub,
118 : AsmJSAtomicsBuiltin_and,
119 : AsmJSAtomicsBuiltin_or,
120 : AsmJSAtomicsBuiltin_xor,
121 : AsmJSAtomicsBuiltin_isLockFree
122 : };
123 :
124 :
125 : // An AsmJSGlobal represents a JS global variable in the asm.js module function.
126 0 : class AsmJSGlobal
127 : {
128 : public:
129 : enum Which { Variable, FFI, ArrayView, ArrayViewCtor, MathBuiltinFunction,
130 : AtomicsBuiltinFunction, Constant, SimdCtor, SimdOp };
131 : enum VarInitKind { InitConstant, InitImport };
132 : enum ConstantKind { GlobalConstant, MathConstant };
133 :
134 : private:
135 0 : struct CacheablePod {
136 : Which which_;
137 : union V {
138 : struct {
139 : VarInitKind initKind_;
140 : union U {
141 : ValType importType_;
142 : Val val_;
143 : U() {}
144 : } u;
145 : } var;
146 : uint32_t ffiIndex_;
147 : Scalar::Type viewType_;
148 : AsmJSMathBuiltinFunction mathBuiltinFunc_;
149 : AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
150 : SimdType simdCtorType_;
151 : struct {
152 : SimdType type_;
153 : SimdOperation which_;
154 : } simdOp;
155 : struct {
156 : ConstantKind kind_;
157 : double value_;
158 : } constant;
159 0 : V() {}
160 : } u;
161 : } pod;
162 : CacheableChars field_;
163 :
164 : friend class ModuleValidator;
165 :
166 : public:
167 0 : AsmJSGlobal() = default;
168 0 : AsmJSGlobal(Which which, UniqueChars field) {
169 0 : mozilla::PodZero(&pod); // zero padding for Valgrind
170 0 : pod.which_ = which;
171 0 : field_ = Move(field);
172 0 : }
173 0 : const char* field() const {
174 0 : return field_.get();
175 : }
176 0 : Which which() const {
177 0 : return pod.which_;
178 : }
179 0 : VarInitKind varInitKind() const {
180 0 : MOZ_ASSERT(pod.which_ == Variable);
181 0 : return pod.u.var.initKind_;
182 : }
183 0 : Val varInitVal() const {
184 0 : MOZ_ASSERT(pod.which_ == Variable);
185 0 : MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
186 0 : return pod.u.var.u.val_;
187 : }
188 0 : ValType varInitImportType() const {
189 0 : MOZ_ASSERT(pod.which_ == Variable);
190 0 : MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
191 0 : return pod.u.var.u.importType_;
192 : }
193 0 : uint32_t ffiIndex() const {
194 0 : MOZ_ASSERT(pod.which_ == FFI);
195 0 : return pod.u.ffiIndex_;
196 : }
197 : // When a view is created from an imported constructor:
198 : // var I32 = stdlib.Int32Array;
199 : // var i32 = new I32(buffer);
200 : // the second import has nothing to validate and thus has a null field.
201 0 : Scalar::Type viewType() const {
202 0 : MOZ_ASSERT(pod.which_ == ArrayView || pod.which_ == ArrayViewCtor);
203 0 : return pod.u.viewType_;
204 : }
205 0 : AsmJSMathBuiltinFunction mathBuiltinFunction() const {
206 0 : MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
207 0 : return pod.u.mathBuiltinFunc_;
208 : }
209 0 : AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
210 0 : MOZ_ASSERT(pod.which_ == AtomicsBuiltinFunction);
211 0 : return pod.u.atomicsBuiltinFunc_;
212 : }
213 0 : SimdType simdCtorType() const {
214 0 : MOZ_ASSERT(pod.which_ == SimdCtor);
215 0 : return pod.u.simdCtorType_;
216 : }
217 0 : SimdOperation simdOperation() const {
218 0 : MOZ_ASSERT(pod.which_ == SimdOp);
219 0 : return pod.u.simdOp.which_;
220 : }
221 0 : SimdType simdOperationType() const {
222 0 : MOZ_ASSERT(pod.which_ == SimdOp);
223 0 : return pod.u.simdOp.type_;
224 : }
225 0 : ConstantKind constantKind() const {
226 0 : MOZ_ASSERT(pod.which_ == Constant);
227 0 : return pod.u.constant.kind_;
228 : }
229 0 : double constantValue() const {
230 0 : MOZ_ASSERT(pod.which_ == Constant);
231 0 : return pod.u.constant.value_;
232 : }
233 :
234 : WASM_DECLARE_SERIALIZABLE(AsmJSGlobal);
235 : };
236 :
237 : typedef Vector<AsmJSGlobal, 0, SystemAllocPolicy> AsmJSGlobalVector;
238 :
239 : // An AsmJSImport is slightly different than an asm.js FFI function: a single
240 : // asm.js FFI function can be called with many different signatures. When
241 : // compiled to wasm, each unique FFI function paired with signature generates a
242 : // wasm import.
243 : class AsmJSImport
244 : {
245 : uint32_t ffiIndex_;
246 : public:
247 : AsmJSImport() = default;
248 0 : explicit AsmJSImport(uint32_t ffiIndex) : ffiIndex_(ffiIndex) {}
249 0 : uint32_t ffiIndex() const { return ffiIndex_; }
250 : };
251 :
252 : typedef Vector<AsmJSImport, 0, SystemAllocPolicy> AsmJSImportVector;
253 :
254 : // An AsmJSExport logically extends Export with the extra information needed for
255 : // an asm.js exported function, viz., the offsets in module's source chars in
256 : // case the function is toString()ed.
257 : class AsmJSExport
258 : {
259 : uint32_t funcIndex_;
260 :
261 : // All fields are treated as cacheable POD:
262 : uint32_t startOffsetInModule_; // Store module-start-relative offsets
263 : uint32_t endOffsetInModule_; // so preserved by serialization.
264 :
265 : public:
266 : AsmJSExport() { PodZero(this); }
267 0 : AsmJSExport(uint32_t funcIndex, uint32_t startOffsetInModule, uint32_t endOffsetInModule)
268 0 : : funcIndex_(funcIndex),
269 : startOffsetInModule_(startOffsetInModule),
270 0 : endOffsetInModule_(endOffsetInModule)
271 0 : {}
272 0 : uint32_t funcIndex() const {
273 0 : return funcIndex_;
274 : }
275 0 : uint32_t startOffsetInModule() const {
276 0 : return startOffsetInModule_;
277 : }
278 0 : uint32_t endOffsetInModule() const {
279 0 : return endOffsetInModule_;
280 : }
281 : };
282 :
283 : typedef Vector<AsmJSExport, 0, SystemAllocPolicy> AsmJSExportVector;
284 :
285 : enum class CacheResult
286 : {
287 : Hit,
288 : Miss
289 : };
290 :
291 : // Holds the immutable guts of an AsmJSModule.
292 : //
293 : // AsmJSMetadata is built incrementally by ModuleValidator and then shared
294 : // immutably between AsmJSModules.
295 :
296 : struct AsmJSMetadataCacheablePod
297 : {
298 : uint32_t numFFIs;
299 : uint32_t srcLength;
300 : uint32_t srcLengthWithRightBrace;
301 : bool usesSimd;
302 :
303 0 : AsmJSMetadataCacheablePod() { PodZero(this); }
304 : };
305 :
306 : struct js::AsmJSMetadata : Metadata, AsmJSMetadataCacheablePod
307 : {
308 : AsmJSGlobalVector asmJSGlobals;
309 : AsmJSImportVector asmJSImports;
310 : AsmJSExportVector asmJSExports;
311 : CacheableCharsVector asmJSFuncNames;
312 : CacheableChars globalArgumentName;
313 : CacheableChars importArgumentName;
314 : CacheableChars bufferArgumentName;
315 :
316 : CacheResult cacheResult;
317 :
318 : // These values are not serialized since they are relative to the
319 : // containing script which can be different between serialization and
320 : // deserialization contexts. Thus, they must be set explicitly using the
321 : // ambient Parser/ScriptSource after deserialization.
322 : //
323 : // srcStart refers to the offset in the ScriptSource to the beginning of
324 : // the asm.js module function. If the function has been created with the
325 : // Function constructor, this will be the first character in the function
326 : // source. Otherwise, it will be the opening parenthesis of the arguments
327 : // list.
328 : uint32_t toStringStart;
329 : uint32_t srcStart;
330 : uint32_t srcBodyStart;
331 : bool strict;
332 : ScriptSourceHolder scriptSource;
333 :
334 0 : uint32_t srcEndBeforeCurly() const {
335 0 : return srcStart + srcLength;
336 : }
337 0 : uint32_t srcEndAfterCurly() const {
338 0 : return srcStart + srcLengthWithRightBrace;
339 : }
340 :
341 0 : explicit AsmJSMetadata(UniqueMetadataTier tier)
342 0 : : Metadata(Move(tier), ModuleKind::AsmJS),
343 : cacheResult(CacheResult::Miss),
344 : srcStart(0),
345 : srcBodyStart(0),
346 0 : strict(false)
347 0 : {}
348 0 : ~AsmJSMetadata() override {}
349 :
350 0 : const AsmJSExport& lookupAsmJSExport(uint32_t funcIndex) const {
351 : // The AsmJSExportVector isn't stored in sorted order so do a linear
352 : // search. This is for the super-cold and already-expensive toString()
353 : // path and the number of exports is generally small.
354 0 : for (const AsmJSExport& exp : asmJSExports) {
355 0 : if (exp.funcIndex() == funcIndex)
356 0 : return exp;
357 : }
358 0 : MOZ_CRASH("missing asm.js func export");
359 : }
360 :
361 0 : bool mutedErrors() const override {
362 0 : return scriptSource.get()->mutedErrors();
363 : }
364 0 : const char16_t* displayURL() const override {
365 0 : return scriptSource.get()->hasDisplayURL() ? scriptSource.get()->displayURL() : nullptr;
366 : }
367 0 : ScriptSource* maybeScriptSource() const override {
368 0 : return scriptSource.get();
369 : }
370 0 : bool getFuncName(const Bytes* maybeBytecode, uint32_t funcIndex, UTF8Bytes* name) const override {
371 : // asm.js doesn't allow exporting imports or putting imports in tables
372 0 : MOZ_ASSERT(funcIndex >= AsmJSFirstDefFuncIndex);
373 0 : const char* p = asmJSFuncNames[funcIndex - AsmJSFirstDefFuncIndex].get();
374 0 : return name->append(p, strlen(p));
375 : }
376 :
377 0 : AsmJSMetadataCacheablePod& pod() { return *this; }
378 0 : const AsmJSMetadataCacheablePod& pod() const { return *this; }
379 :
380 : WASM_DECLARE_SERIALIZABLE_OVERRIDE(AsmJSMetadata)
381 : };
382 :
383 : typedef RefPtr<AsmJSMetadata> MutableAsmJSMetadata;
384 :
385 : /*****************************************************************************/
386 : // ParseNode utilities
387 :
388 : static inline ParseNode*
389 0 : NextNode(ParseNode* pn)
390 : {
391 0 : return pn->pn_next;
392 : }
393 :
394 : static inline ParseNode*
395 0 : UnaryKid(ParseNode* pn)
396 : {
397 0 : MOZ_ASSERT(pn->isArity(PN_UNARY));
398 0 : return pn->pn_kid;
399 : }
400 :
401 : static inline ParseNode*
402 0 : BinaryRight(ParseNode* pn)
403 : {
404 0 : MOZ_ASSERT(pn->isArity(PN_BINARY));
405 0 : return pn->pn_right;
406 : }
407 :
408 : static inline ParseNode*
409 0 : BinaryLeft(ParseNode* pn)
410 : {
411 0 : MOZ_ASSERT(pn->isArity(PN_BINARY));
412 0 : return pn->pn_left;
413 : }
414 :
415 : static inline ParseNode*
416 0 : ReturnExpr(ParseNode* pn)
417 : {
418 0 : MOZ_ASSERT(pn->isKind(PNK_RETURN));
419 0 : return UnaryKid(pn);
420 : }
421 :
422 : static inline ParseNode*
423 0 : TernaryKid1(ParseNode* pn)
424 : {
425 0 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
426 0 : return pn->pn_kid1;
427 : }
428 :
429 : static inline ParseNode*
430 0 : TernaryKid2(ParseNode* pn)
431 : {
432 0 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
433 0 : return pn->pn_kid2;
434 : }
435 :
436 : static inline ParseNode*
437 0 : TernaryKid3(ParseNode* pn)
438 : {
439 0 : MOZ_ASSERT(pn->isArity(PN_TERNARY));
440 0 : return pn->pn_kid3;
441 : }
442 :
443 : static inline ParseNode*
444 0 : ListHead(ParseNode* pn)
445 : {
446 0 : MOZ_ASSERT(pn->isArity(PN_LIST));
447 0 : return pn->pn_head;
448 : }
449 :
450 : static inline unsigned
451 0 : ListLength(ParseNode* pn)
452 : {
453 0 : MOZ_ASSERT(pn->isArity(PN_LIST));
454 0 : return pn->pn_count;
455 : }
456 :
457 : static inline ParseNode*
458 0 : CallCallee(ParseNode* pn)
459 : {
460 0 : MOZ_ASSERT(pn->isKind(PNK_CALL));
461 0 : return ListHead(pn);
462 : }
463 :
464 : static inline unsigned
465 0 : CallArgListLength(ParseNode* pn)
466 : {
467 0 : MOZ_ASSERT(pn->isKind(PNK_CALL));
468 0 : MOZ_ASSERT(ListLength(pn) >= 1);
469 0 : return ListLength(pn) - 1;
470 : }
471 :
472 : static inline ParseNode*
473 0 : CallArgList(ParseNode* pn)
474 : {
475 0 : MOZ_ASSERT(pn->isKind(PNK_CALL));
476 0 : return NextNode(ListHead(pn));
477 : }
478 :
479 : static inline ParseNode*
480 0 : VarListHead(ParseNode* pn)
481 : {
482 0 : MOZ_ASSERT(pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST));
483 0 : return ListHead(pn);
484 : }
485 :
486 : static inline bool
487 0 : IsDefaultCase(ParseNode* pn)
488 : {
489 0 : return pn->as<CaseClause>().isDefault();
490 : }
491 :
492 : static inline ParseNode*
493 0 : CaseExpr(ParseNode* pn)
494 : {
495 0 : return pn->as<CaseClause>().caseExpression();
496 : }
497 :
498 : static inline ParseNode*
499 0 : CaseBody(ParseNode* pn)
500 : {
501 0 : return pn->as<CaseClause>().statementList();
502 : }
503 :
504 : static inline ParseNode*
505 0 : BinaryOpLeft(ParseNode* pn)
506 : {
507 0 : MOZ_ASSERT(pn->isBinaryOperation());
508 0 : MOZ_ASSERT(pn->isArity(PN_LIST));
509 0 : MOZ_ASSERT(pn->pn_count == 2);
510 0 : return ListHead(pn);
511 : }
512 :
513 : static inline ParseNode*
514 0 : BinaryOpRight(ParseNode* pn)
515 : {
516 0 : MOZ_ASSERT(pn->isBinaryOperation());
517 0 : MOZ_ASSERT(pn->isArity(PN_LIST));
518 0 : MOZ_ASSERT(pn->pn_count == 2);
519 0 : return NextNode(ListHead(pn));
520 : }
521 :
522 : static inline ParseNode*
523 0 : BitwiseLeft(ParseNode* pn)
524 : {
525 0 : return BinaryOpLeft(pn);
526 : }
527 :
528 : static inline ParseNode*
529 0 : BitwiseRight(ParseNode* pn)
530 : {
531 0 : return BinaryOpRight(pn);
532 : }
533 :
534 : static inline ParseNode*
535 0 : MultiplyLeft(ParseNode* pn)
536 : {
537 0 : MOZ_ASSERT(pn->isKind(PNK_STAR));
538 0 : return BinaryOpLeft(pn);
539 : }
540 :
541 : static inline ParseNode*
542 0 : MultiplyRight(ParseNode* pn)
543 : {
544 0 : MOZ_ASSERT(pn->isKind(PNK_STAR));
545 0 : return BinaryOpRight(pn);
546 : }
547 :
548 : static inline ParseNode*
549 0 : AddSubLeft(ParseNode* pn)
550 : {
551 0 : MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
552 0 : return BinaryOpLeft(pn);
553 : }
554 :
555 : static inline ParseNode*
556 0 : AddSubRight(ParseNode* pn)
557 : {
558 0 : MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
559 0 : return BinaryOpRight(pn);
560 : }
561 :
562 : static inline ParseNode*
563 0 : DivOrModLeft(ParseNode* pn)
564 : {
565 0 : MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
566 0 : return BinaryOpLeft(pn);
567 : }
568 :
569 : static inline ParseNode*
570 0 : DivOrModRight(ParseNode* pn)
571 : {
572 0 : MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
573 0 : return BinaryOpRight(pn);
574 : }
575 :
576 : static inline ParseNode*
577 0 : ComparisonLeft(ParseNode* pn)
578 : {
579 0 : return BinaryOpLeft(pn);
580 : }
581 :
582 : static inline ParseNode*
583 0 : ComparisonRight(ParseNode* pn)
584 : {
585 0 : return BinaryOpRight(pn);
586 : }
587 :
588 : static inline bool
589 0 : IsExpressionStatement(ParseNode* pn)
590 : {
591 0 : return pn->isKind(PNK_SEMI);
592 : }
593 :
594 : static inline ParseNode*
595 0 : ExpressionStatementExpr(ParseNode* pn)
596 : {
597 0 : MOZ_ASSERT(pn->isKind(PNK_SEMI));
598 0 : return UnaryKid(pn);
599 : }
600 :
601 : static inline PropertyName*
602 0 : LoopControlMaybeLabel(ParseNode* pn)
603 : {
604 0 : MOZ_ASSERT(pn->isKind(PNK_BREAK) || pn->isKind(PNK_CONTINUE));
605 0 : MOZ_ASSERT(pn->isArity(PN_NULLARY));
606 0 : return pn->as<LoopControlStatement>().label();
607 : }
608 :
609 : static inline PropertyName*
610 0 : LabeledStatementLabel(ParseNode* pn)
611 : {
612 0 : return pn->as<LabeledStatement>().label();
613 : }
614 :
615 : static inline ParseNode*
616 0 : LabeledStatementStatement(ParseNode* pn)
617 : {
618 0 : return pn->as<LabeledStatement>().statement();
619 : }
620 :
621 : static double
622 0 : NumberNodeValue(ParseNode* pn)
623 : {
624 0 : MOZ_ASSERT(pn->isKind(PNK_NUMBER));
625 0 : return pn->pn_dval;
626 : }
627 :
628 : static bool
629 0 : NumberNodeHasFrac(ParseNode* pn)
630 : {
631 0 : MOZ_ASSERT(pn->isKind(PNK_NUMBER));
632 0 : return pn->pn_u.number.decimalPoint == HasDecimal;
633 : }
634 :
635 : static ParseNode*
636 0 : DotBase(ParseNode* pn)
637 : {
638 0 : MOZ_ASSERT(pn->isKind(PNK_DOT));
639 0 : MOZ_ASSERT(pn->isArity(PN_NAME));
640 0 : return pn->expr();
641 : }
642 :
643 : static PropertyName*
644 0 : DotMember(ParseNode* pn)
645 : {
646 0 : MOZ_ASSERT(pn->isKind(PNK_DOT));
647 0 : MOZ_ASSERT(pn->isArity(PN_NAME));
648 0 : return pn->pn_atom->asPropertyName();
649 : }
650 :
651 : static ParseNode*
652 0 : ElemBase(ParseNode* pn)
653 : {
654 0 : MOZ_ASSERT(pn->isKind(PNK_ELEM));
655 0 : return BinaryLeft(pn);
656 : }
657 :
658 : static ParseNode*
659 0 : ElemIndex(ParseNode* pn)
660 : {
661 0 : MOZ_ASSERT(pn->isKind(PNK_ELEM));
662 0 : return BinaryRight(pn);
663 : }
664 :
665 : static inline JSFunction*
666 0 : FunctionObject(ParseNode* fn)
667 : {
668 0 : MOZ_ASSERT(fn->isKind(PNK_FUNCTION));
669 0 : MOZ_ASSERT(fn->isArity(PN_CODE));
670 0 : return fn->pn_funbox->function();
671 : }
672 :
673 : static inline PropertyName*
674 0 : FunctionName(ParseNode* fn)
675 : {
676 0 : if (JSAtom* name = FunctionObject(fn)->explicitName())
677 0 : return name->asPropertyName();
678 0 : return nullptr;
679 : }
680 :
681 : static inline ParseNode*
682 0 : FunctionStatementList(ParseNode* fn)
683 : {
684 0 : MOZ_ASSERT(fn->pn_body->isKind(PNK_PARAMSBODY));
685 0 : ParseNode* last = fn->pn_body->last();
686 0 : MOZ_ASSERT(last->isKind(PNK_LEXICALSCOPE));
687 0 : MOZ_ASSERT(last->isEmptyScope());
688 0 : ParseNode* body = last->scopeBody();
689 0 : MOZ_ASSERT(body->isKind(PNK_STATEMENTLIST));
690 0 : return body;
691 : }
692 :
693 : static inline bool
694 0 : IsNormalObjectField(JSContext* cx, ParseNode* pn)
695 : {
696 0 : return pn->isKind(PNK_COLON) &&
697 0 : pn->getOp() == JSOP_INITPROP &&
698 0 : BinaryLeft(pn)->isKind(PNK_OBJECT_PROPERTY_NAME);
699 : }
700 :
701 : static inline PropertyName*
702 0 : ObjectNormalFieldName(JSContext* cx, ParseNode* pn)
703 : {
704 0 : MOZ_ASSERT(IsNormalObjectField(cx, pn));
705 0 : MOZ_ASSERT(BinaryLeft(pn)->isKind(PNK_OBJECT_PROPERTY_NAME));
706 0 : return BinaryLeft(pn)->pn_atom->asPropertyName();
707 : }
708 :
709 : static inline ParseNode*
710 0 : ObjectNormalFieldInitializer(JSContext* cx, ParseNode* pn)
711 : {
712 0 : MOZ_ASSERT(IsNormalObjectField(cx, pn));
713 0 : return BinaryRight(pn);
714 : }
715 :
716 : static inline ParseNode*
717 0 : MaybeInitializer(ParseNode* pn)
718 : {
719 0 : return pn->expr();
720 : }
721 :
722 : static inline bool
723 0 : IsUseOfName(ParseNode* pn, PropertyName* name)
724 : {
725 0 : return pn->isKind(PNK_NAME) && pn->name() == name;
726 : }
727 :
728 : static inline bool
729 0 : IsIgnoredDirectiveName(JSContext* cx, JSAtom* atom)
730 : {
731 0 : return atom != cx->names().useStrict;
732 : }
733 :
734 : static inline bool
735 0 : IsIgnoredDirective(JSContext* cx, ParseNode* pn)
736 : {
737 0 : return pn->isKind(PNK_SEMI) &&
738 0 : UnaryKid(pn) &&
739 0 : UnaryKid(pn)->isKind(PNK_STRING) &&
740 0 : IsIgnoredDirectiveName(cx, UnaryKid(pn)->pn_atom);
741 : }
742 :
743 : static inline bool
744 0 : IsEmptyStatement(ParseNode* pn)
745 : {
746 0 : return pn->isKind(PNK_SEMI) && !UnaryKid(pn);
747 : }
748 :
749 : static inline ParseNode*
750 0 : SkipEmptyStatements(ParseNode* pn)
751 : {
752 0 : while (pn && IsEmptyStatement(pn))
753 0 : pn = pn->pn_next;
754 0 : return pn;
755 : }
756 :
757 : static inline ParseNode*
758 0 : NextNonEmptyStatement(ParseNode* pn)
759 : {
760 0 : return SkipEmptyStatements(pn->pn_next);
761 : }
762 :
763 : static bool
764 0 : GetToken(AsmJSParser& parser, TokenKind* tkp)
765 : {
766 0 : TokenStream& ts = parser.tokenStream;
767 : TokenKind tk;
768 : while (true) {
769 0 : if (!ts.getToken(&tk, TokenStream::Operand))
770 0 : return false;
771 0 : if (tk != TOK_SEMI)
772 0 : break;
773 : }
774 0 : *tkp = tk;
775 0 : return true;
776 : }
777 :
778 : static bool
779 0 : PeekToken(AsmJSParser& parser, TokenKind* tkp)
780 : {
781 0 : TokenStream& ts = parser.tokenStream;
782 : TokenKind tk;
783 : while (true) {
784 0 : if (!ts.peekToken(&tk, TokenStream::Operand))
785 0 : return false;
786 0 : if (tk != TOK_SEMI)
787 0 : break;
788 0 : ts.consumeKnownToken(TOK_SEMI, TokenStream::Operand);
789 : }
790 0 : *tkp = tk;
791 0 : return true;
792 : }
793 :
794 : static bool
795 0 : ParseVarOrConstStatement(AsmJSParser& parser, ParseNode** var)
796 : {
797 : TokenKind tk;
798 0 : if (!PeekToken(parser, &tk))
799 0 : return false;
800 0 : if (tk != TOK_VAR && tk != TOK_CONST) {
801 0 : *var = nullptr;
802 0 : return true;
803 : }
804 :
805 0 : *var = parser.statementListItem(YieldIsName);
806 0 : if (!*var)
807 0 : return false;
808 :
809 0 : MOZ_ASSERT((*var)->isKind(PNK_VAR) || (*var)->isKind(PNK_CONST));
810 0 : return true;
811 : }
812 :
813 : /*****************************************************************************/
814 :
815 : // Represents the type and value of an asm.js numeric literal.
816 : //
817 : // A literal is a double iff the literal contains a decimal point (even if the
818 : // fractional part is 0). Otherwise, integers may be classified:
819 : // fixnum: [0, 2^31)
820 : // negative int: [-2^31, 0)
821 : // big unsigned: [2^31, 2^32)
822 : // out of range: otherwise
823 : // Lastly, a literal may be a float literal which is any double or integer
824 : // literal coerced with Math.fround.
825 : //
826 : // This class distinguishes between signed and unsigned integer SIMD types like
827 : // Int32x4 and Uint32x4, and so does Type below. The wasm ValType and ExprType
828 : // enums, and the wasm::Val class do not.
829 : class NumLit
830 : {
831 : public:
832 : enum Which {
833 : Fixnum,
834 : NegativeInt,
835 : BigUnsigned,
836 : Double,
837 : Float,
838 : Int8x16,
839 : Int16x8,
840 : Int32x4,
841 : Uint8x16,
842 : Uint16x8,
843 : Uint32x4,
844 : Float32x4,
845 : Bool8x16,
846 : Bool16x8,
847 : Bool32x4,
848 : OutOfRangeInt = -1
849 : };
850 :
851 : private:
852 : Which which_;
853 0 : union {
854 : Value scalar_;
855 : SimdConstant simd_;
856 : } u;
857 :
858 : public:
859 0 : NumLit() = default;
860 :
861 0 : NumLit(Which w, const Value& v) : which_(w) {
862 0 : u.scalar_ = v;
863 0 : MOZ_ASSERT(!isSimd());
864 0 : }
865 :
866 0 : NumLit(Which w, SimdConstant c) : which_(w) {
867 0 : u.simd_ = c;
868 0 : MOZ_ASSERT(isSimd());
869 0 : }
870 :
871 0 : Which which() const {
872 0 : return which_;
873 : }
874 :
875 0 : int32_t toInt32() const {
876 0 : MOZ_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned);
877 0 : return u.scalar_.toInt32();
878 : }
879 :
880 0 : uint32_t toUint32() const {
881 0 : return (uint32_t)toInt32();
882 : }
883 :
884 0 : double toDouble() const {
885 0 : MOZ_ASSERT(which_ == Double);
886 0 : return u.scalar_.toDouble();
887 : }
888 :
889 0 : float toFloat() const {
890 0 : MOZ_ASSERT(which_ == Float);
891 0 : return float(u.scalar_.toDouble());
892 : }
893 :
894 : Value scalarValue() const {
895 : MOZ_ASSERT(which_ != OutOfRangeInt);
896 : return u.scalar_;
897 : }
898 :
899 0 : bool isSimd() const
900 : {
901 0 : return which_ == Int8x16 || which_ == Uint8x16 || which_ == Int16x8 ||
902 0 : which_ == Uint16x8 || which_ == Int32x4 || which_ == Uint32x4 ||
903 0 : which_ == Float32x4 || which_ == Bool8x16 || which_ == Bool16x8 ||
904 0 : which_ == Bool32x4;
905 : }
906 :
907 0 : const SimdConstant& simdValue() const {
908 0 : MOZ_ASSERT(isSimd());
909 0 : return u.simd_;
910 : }
911 :
912 0 : bool valid() const {
913 0 : return which_ != OutOfRangeInt;
914 : }
915 :
916 0 : bool isZeroBits() const {
917 0 : MOZ_ASSERT(valid());
918 0 : switch (which()) {
919 : case NumLit::Fixnum:
920 : case NumLit::NegativeInt:
921 : case NumLit::BigUnsigned:
922 0 : return toInt32() == 0;
923 : case NumLit::Double:
924 0 : return IsPositiveZero(toDouble());
925 : case NumLit::Float:
926 0 : return IsPositiveZero(toFloat());
927 : case NumLit::Int8x16:
928 : case NumLit::Uint8x16:
929 : case NumLit::Bool8x16:
930 0 : return simdValue() == SimdConstant::SplatX16(0);
931 : case NumLit::Int16x8:
932 : case NumLit::Uint16x8:
933 : case NumLit::Bool16x8:
934 0 : return simdValue() == SimdConstant::SplatX8(0);
935 : case NumLit::Int32x4:
936 : case NumLit::Uint32x4:
937 : case NumLit::Bool32x4:
938 0 : return simdValue() == SimdConstant::SplatX4(0);
939 : case NumLit::Float32x4:
940 0 : return simdValue() == SimdConstant::SplatX4(0.f);
941 : case NumLit::OutOfRangeInt:
942 0 : MOZ_CRASH("can't be here because of valid() check above");
943 : }
944 0 : return false;
945 : }
946 :
947 0 : Val value() const {
948 0 : switch (which_) {
949 : case NumLit::Fixnum:
950 : case NumLit::NegativeInt:
951 : case NumLit::BigUnsigned:
952 0 : return Val(toUint32());
953 : case NumLit::Float:
954 0 : return Val(toFloat());
955 : case NumLit::Double:
956 0 : return Val(toDouble());
957 : case NumLit::Int8x16:
958 : case NumLit::Uint8x16:
959 0 : return Val(simdValue().asInt8x16());
960 : case NumLit::Int16x8:
961 : case NumLit::Uint16x8:
962 0 : return Val(simdValue().asInt16x8());
963 : case NumLit::Int32x4:
964 : case NumLit::Uint32x4:
965 0 : return Val(simdValue().asInt32x4());
966 : case NumLit::Float32x4:
967 0 : return Val(simdValue().asFloat32x4());
968 : case NumLit::Bool8x16:
969 0 : return Val(simdValue().asInt8x16(), ValType::B8x16);
970 : case NumLit::Bool16x8:
971 0 : return Val(simdValue().asInt16x8(), ValType::B16x8);
972 : case NumLit::Bool32x4:
973 0 : return Val(simdValue().asInt32x4(), ValType::B32x4);
974 : case NumLit::OutOfRangeInt:;
975 : }
976 0 : MOZ_CRASH("bad literal");
977 : }
978 : };
979 :
980 : // Represents the type of a general asm.js expression.
981 : //
982 : // A canonical subset of types representing the coercion targets: Int, Float,
983 : // Double, and the SIMD types. This is almost equivalent to wasm::ValType,
984 : // except the integer SIMD types have signed/unsigned variants.
985 : //
986 : // Void is also part of the canonical subset which then maps to wasm::ExprType.
987 : //
988 : // Note that while the canonical subset distinguishes signed and unsigned SIMD
989 : // types, it only uses |Int| to represent signed and unsigned 32-bit integers.
990 : // This is because the scalar coersions x|0 and x>>>0 work with any kind of
991 : // integer input, while the SIMD check functions throw a TypeError if the passed
992 : // type doesn't match.
993 : //
994 : class Type
995 : {
996 : public:
997 : enum Which {
998 : Fixnum = NumLit::Fixnum,
999 : Signed = NumLit::NegativeInt,
1000 : Unsigned = NumLit::BigUnsigned,
1001 : DoubleLit = NumLit::Double,
1002 : Float = NumLit::Float,
1003 : Int8x16 = NumLit::Int8x16,
1004 : Int16x8 = NumLit::Int16x8,
1005 : Int32x4 = NumLit::Int32x4,
1006 : Uint8x16 = NumLit::Uint8x16,
1007 : Uint16x8 = NumLit::Uint16x8,
1008 : Uint32x4 = NumLit::Uint32x4,
1009 : Float32x4 = NumLit::Float32x4,
1010 : Bool8x16 = NumLit::Bool8x16,
1011 : Bool16x8 = NumLit::Bool16x8,
1012 : Bool32x4 = NumLit::Bool32x4,
1013 : Double,
1014 : MaybeDouble,
1015 : MaybeFloat,
1016 : Floatish,
1017 : Int,
1018 : Intish,
1019 : Void
1020 : };
1021 :
1022 : private:
1023 : Which which_;
1024 :
1025 : public:
1026 0 : Type() = default;
1027 0 : MOZ_IMPLICIT Type(Which w) : which_(w) {}
1028 0 : MOZ_IMPLICIT Type(SimdType type) {
1029 0 : switch (type) {
1030 0 : case SimdType::Int8x16: which_ = Int8x16; return;
1031 0 : case SimdType::Int16x8: which_ = Int16x8; return;
1032 0 : case SimdType::Int32x4: which_ = Int32x4; return;
1033 0 : case SimdType::Uint8x16: which_ = Uint8x16; return;
1034 0 : case SimdType::Uint16x8: which_ = Uint16x8; return;
1035 0 : case SimdType::Uint32x4: which_ = Uint32x4; return;
1036 0 : case SimdType::Float32x4: which_ = Float32x4; return;
1037 0 : case SimdType::Bool8x16: which_ = Bool8x16; return;
1038 0 : case SimdType::Bool16x8: which_ = Bool16x8; return;
1039 0 : case SimdType::Bool32x4: which_ = Bool32x4; return;
1040 0 : default: break;
1041 : }
1042 0 : MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("bad SimdType");
1043 : }
1044 :
1045 : // Map an already canonicalized Type to the return type of a function call.
1046 0 : static Type ret(Type t) {
1047 0 : MOZ_ASSERT(t.isCanonical());
1048 : // The 32-bit external type is Signed, not Int.
1049 0 : return t.isInt() ? Signed: t;
1050 : }
1051 :
1052 0 : static Type lit(const NumLit& lit) {
1053 0 : MOZ_ASSERT(lit.valid());
1054 0 : Which which = Type::Which(lit.which());
1055 0 : MOZ_ASSERT(which >= Fixnum && which <= Bool32x4);
1056 0 : Type t;
1057 0 : t.which_ = which;
1058 0 : return t;
1059 : }
1060 :
1061 : // Map |t| to one of the canonical vartype representations of a
1062 : // wasm::ExprType.
1063 0 : static Type canonicalize(Type t) {
1064 0 : switch(t.which()) {
1065 : case Fixnum:
1066 : case Signed:
1067 : case Unsigned:
1068 : case Int:
1069 0 : return Int;
1070 :
1071 : case Float:
1072 0 : return Float;
1073 :
1074 : case DoubleLit:
1075 : case Double:
1076 0 : return Double;
1077 :
1078 : case Void:
1079 0 : return Void;
1080 :
1081 : case Int8x16:
1082 : case Int16x8:
1083 : case Int32x4:
1084 : case Uint8x16:
1085 : case Uint16x8:
1086 : case Uint32x4:
1087 : case Float32x4:
1088 : case Bool8x16:
1089 : case Bool16x8:
1090 : case Bool32x4:
1091 0 : return t;
1092 :
1093 : case MaybeDouble:
1094 : case MaybeFloat:
1095 : case Floatish:
1096 : case Intish:
1097 : // These types need some kind of coercion, they can't be mapped
1098 : // to an ExprType.
1099 0 : break;
1100 : }
1101 0 : MOZ_CRASH("Invalid vartype");
1102 : }
1103 :
1104 0 : Which which() const { return which_; }
1105 :
1106 0 : bool operator==(Type rhs) const { return which_ == rhs.which_; }
1107 0 : bool operator!=(Type rhs) const { return which_ != rhs.which_; }
1108 :
1109 0 : bool operator<=(Type rhs) const {
1110 0 : switch (rhs.which_) {
1111 0 : case Signed: return isSigned();
1112 0 : case Unsigned: return isUnsigned();
1113 0 : case DoubleLit: return isDoubleLit();
1114 0 : case Double: return isDouble();
1115 0 : case Float: return isFloat();
1116 0 : case Int8x16: return isInt8x16();
1117 0 : case Int16x8: return isInt16x8();
1118 0 : case Int32x4: return isInt32x4();
1119 0 : case Uint8x16: return isUint8x16();
1120 0 : case Uint16x8: return isUint16x8();
1121 0 : case Uint32x4: return isUint32x4();
1122 0 : case Float32x4: return isFloat32x4();
1123 0 : case Bool8x16: return isBool8x16();
1124 0 : case Bool16x8: return isBool16x8();
1125 0 : case Bool32x4: return isBool32x4();
1126 0 : case MaybeDouble: return isMaybeDouble();
1127 0 : case MaybeFloat: return isMaybeFloat();
1128 0 : case Floatish: return isFloatish();
1129 0 : case Int: return isInt();
1130 0 : case Intish: return isIntish();
1131 0 : case Fixnum: return isFixnum();
1132 0 : case Void: return isVoid();
1133 : }
1134 0 : MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected rhs type");
1135 : }
1136 :
1137 0 : bool isFixnum() const {
1138 0 : return which_ == Fixnum;
1139 : }
1140 :
1141 0 : bool isSigned() const {
1142 0 : return which_ == Signed || which_ == Fixnum;
1143 : }
1144 :
1145 0 : bool isUnsigned() const {
1146 0 : return which_ == Unsigned || which_ == Fixnum;
1147 : }
1148 :
1149 0 : bool isInt() const {
1150 0 : return isSigned() || isUnsigned() || which_ == Int;
1151 : }
1152 :
1153 0 : bool isIntish() const {
1154 0 : return isInt() || which_ == Intish;
1155 : }
1156 :
1157 0 : bool isDoubleLit() const {
1158 0 : return which_ == DoubleLit;
1159 : }
1160 :
1161 0 : bool isDouble() const {
1162 0 : return isDoubleLit() || which_ == Double;
1163 : }
1164 :
1165 0 : bool isMaybeDouble() const {
1166 0 : return isDouble() || which_ == MaybeDouble;
1167 : }
1168 :
1169 0 : bool isFloat() const {
1170 0 : return which_ == Float;
1171 : }
1172 :
1173 0 : bool isMaybeFloat() const {
1174 0 : return isFloat() || which_ == MaybeFloat;
1175 : }
1176 :
1177 0 : bool isFloatish() const {
1178 0 : return isMaybeFloat() || which_ == Floatish;
1179 : }
1180 :
1181 0 : bool isVoid() const {
1182 0 : return which_ == Void;
1183 : }
1184 :
1185 0 : bool isExtern() const {
1186 0 : return isDouble() || isSigned();
1187 : }
1188 :
1189 0 : bool isInt8x16() const {
1190 0 : return which_ == Int8x16;
1191 : }
1192 :
1193 0 : bool isInt16x8() const {
1194 0 : return which_ == Int16x8;
1195 : }
1196 :
1197 0 : bool isInt32x4() const {
1198 0 : return which_ == Int32x4;
1199 : }
1200 :
1201 0 : bool isUint8x16() const {
1202 0 : return which_ == Uint8x16;
1203 : }
1204 :
1205 0 : bool isUint16x8() const {
1206 0 : return which_ == Uint16x8;
1207 : }
1208 :
1209 0 : bool isUint32x4() const {
1210 0 : return which_ == Uint32x4;
1211 : }
1212 :
1213 0 : bool isFloat32x4() const {
1214 0 : return which_ == Float32x4;
1215 : }
1216 :
1217 0 : bool isBool8x16() const {
1218 0 : return which_ == Bool8x16;
1219 : }
1220 :
1221 0 : bool isBool16x8() const {
1222 0 : return which_ == Bool16x8;
1223 : }
1224 :
1225 0 : bool isBool32x4() const {
1226 0 : return which_ == Bool32x4;
1227 : }
1228 :
1229 0 : bool isSimd() const {
1230 0 : return isInt8x16() || isInt16x8() || isInt32x4() || isUint8x16() || isUint16x8() ||
1231 0 : isUint32x4() || isFloat32x4() || isBool8x16() || isBool16x8() || isBool32x4();
1232 : }
1233 :
1234 0 : bool isUnsignedSimd() const {
1235 0 : return isUint8x16() || isUint16x8() || isUint32x4();
1236 : }
1237 :
1238 : // Check if this is one of the valid types for a function argument.
1239 0 : bool isArgType() const {
1240 0 : return isInt() || isFloat() || isDouble() || (isSimd() && !isUnsignedSimd());
1241 : }
1242 :
1243 : // Check if this is one of the valid types for a function return value.
1244 0 : bool isReturnType() const {
1245 0 : return isSigned() || isFloat() || isDouble() || (isSimd() && !isUnsignedSimd()) ||
1246 0 : isVoid();
1247 : }
1248 :
1249 : // Check if this is one of the valid types for a global variable.
1250 0 : bool isGlobalVarType() const {
1251 0 : return isArgType();
1252 : }
1253 :
1254 : // Check if this is one of the canonical vartype representations of a
1255 : // wasm::ExprType. See Type::canonicalize().
1256 0 : bool isCanonical() const {
1257 0 : switch (which()) {
1258 : case Int:
1259 : case Float:
1260 : case Double:
1261 : case Void:
1262 0 : return true;
1263 : default:
1264 0 : return isSimd();
1265 : }
1266 : }
1267 :
1268 : // Check if this is a canonical representation of a wasm::ValType.
1269 0 : bool isCanonicalValType() const {
1270 0 : return !isVoid() && isCanonical();
1271 : }
1272 :
1273 : // Convert this canonical type to a wasm::ExprType.
1274 0 : ExprType canonicalToExprType() const {
1275 0 : switch (which()) {
1276 0 : case Int: return ExprType::I32;
1277 0 : case Float: return ExprType::F32;
1278 0 : case Double: return ExprType::F64;
1279 0 : case Void: return ExprType::Void;
1280 : case Uint8x16:
1281 0 : case Int8x16: return ExprType::I8x16;
1282 : case Uint16x8:
1283 0 : case Int16x8: return ExprType::I16x8;
1284 : case Uint32x4:
1285 0 : case Int32x4: return ExprType::I32x4;
1286 0 : case Float32x4: return ExprType::F32x4;
1287 0 : case Bool8x16: return ExprType::B8x16;
1288 0 : case Bool16x8: return ExprType::B16x8;
1289 0 : case Bool32x4: return ExprType::B32x4;
1290 0 : default: MOZ_CRASH("Need canonical type");
1291 : }
1292 : }
1293 :
1294 : // Convert this canonical type to a wasm::ValType.
1295 0 : ValType canonicalToValType() const {
1296 0 : return NonVoidToValType(canonicalToExprType());
1297 : }
1298 :
1299 : // Convert this type to a wasm::ExprType for use in a wasm
1300 : // block signature. This works for all types, including non-canonical
1301 : // ones. Consequently, the type isn't valid for subsequent asm.js
1302 : // validation; it's only valid for use in producing wasm.
1303 0 : ExprType toWasmBlockSignatureType() const {
1304 0 : switch (which()) {
1305 : case Fixnum:
1306 : case Signed:
1307 : case Unsigned:
1308 : case Int:
1309 : case Intish:
1310 0 : return ExprType::I32;
1311 :
1312 : case Float:
1313 : case MaybeFloat:
1314 : case Floatish:
1315 0 : return ExprType::F32;
1316 :
1317 : case DoubleLit:
1318 : case Double:
1319 : case MaybeDouble:
1320 0 : return ExprType::F64;
1321 :
1322 : case Void:
1323 0 : return ExprType::Void;
1324 :
1325 : case Uint8x16:
1326 0 : case Int8x16: return ExprType::I8x16;
1327 : case Uint16x8:
1328 0 : case Int16x8: return ExprType::I16x8;
1329 : case Uint32x4:
1330 0 : case Int32x4: return ExprType::I32x4;
1331 0 : case Float32x4: return ExprType::F32x4;
1332 0 : case Bool8x16: return ExprType::B8x16;
1333 0 : case Bool16x8: return ExprType::B16x8;
1334 0 : case Bool32x4: return ExprType::B32x4;
1335 : }
1336 0 : MOZ_CRASH("Invalid Type");
1337 : }
1338 :
1339 0 : const char* toChars() const {
1340 0 : switch (which_) {
1341 0 : case Double: return "double";
1342 0 : case DoubleLit: return "doublelit";
1343 0 : case MaybeDouble: return "double?";
1344 0 : case Float: return "float";
1345 0 : case Floatish: return "floatish";
1346 0 : case MaybeFloat: return "float?";
1347 0 : case Fixnum: return "fixnum";
1348 0 : case Int: return "int";
1349 0 : case Signed: return "signed";
1350 0 : case Unsigned: return "unsigned";
1351 0 : case Intish: return "intish";
1352 0 : case Int8x16: return "int8x16";
1353 0 : case Int16x8: return "int16x8";
1354 0 : case Int32x4: return "int32x4";
1355 0 : case Uint8x16: return "uint8x16";
1356 0 : case Uint16x8: return "uint16x8";
1357 0 : case Uint32x4: return "uint32x4";
1358 0 : case Float32x4: return "float32x4";
1359 0 : case Bool8x16: return "bool8x16";
1360 0 : case Bool16x8: return "bool16x8";
1361 0 : case Bool32x4: return "bool32x4";
1362 0 : case Void: return "void";
1363 : }
1364 0 : MOZ_CRASH("Invalid Type");
1365 : }
1366 : };
1367 :
1368 : static const unsigned VALIDATION_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
1369 :
1370 : // The ModuleValidator encapsulates the entire validation of an asm.js module.
1371 : // Its lifetime goes from the validation of the top components of an asm.js
1372 : // module (all the globals), the emission of bytecode for all the functions in
1373 : // the module and the validation of function's pointer tables. It also finishes
1374 : // the compilation of all the module's stubs.
1375 : //
1376 : // Rooting note: ModuleValidator is a stack class that contains unrooted
1377 : // PropertyName (JSAtom) pointers. This is safe because it cannot be
1378 : // constructed without a TokenStream reference. TokenStream is itself a stack
1379 : // class that cannot be constructed without an AutoKeepAtoms being live on the
1380 : // stack, which prevents collection of atoms.
1381 : //
1382 : // ModuleValidator is marked as rooted in the rooting analysis. Don't add
1383 : // non-JSAtom pointers, or this will break!
1384 : class MOZ_STACK_CLASS ModuleValidator
1385 : {
1386 : public:
1387 : class Func
1388 : {
1389 : PropertyName* name_;
1390 : uint32_t firstUse_;
1391 : uint32_t index_;
1392 : uint32_t srcBegin_;
1393 : uint32_t srcEnd_;
1394 : bool defined_;
1395 :
1396 : public:
1397 0 : Func(PropertyName* name, uint32_t firstUse, uint32_t index)
1398 0 : : name_(name), firstUse_(firstUse), index_(index),
1399 0 : srcBegin_(0), srcEnd_(0), defined_(false)
1400 0 : {}
1401 :
1402 0 : PropertyName* name() const { return name_; }
1403 0 : uint32_t firstUse() const { return firstUse_; }
1404 0 : bool defined() const { return defined_; }
1405 0 : uint32_t index() const { return index_; }
1406 :
1407 0 : void define(ParseNode* fn) {
1408 0 : MOZ_ASSERT(!defined_);
1409 0 : defined_ = true;
1410 0 : srcBegin_ = fn->pn_pos.begin;
1411 0 : srcEnd_ = fn->pn_pos.end;
1412 0 : }
1413 :
1414 0 : uint32_t srcBegin() const { MOZ_ASSERT(defined_); return srcBegin_; }
1415 0 : uint32_t srcEnd() const { MOZ_ASSERT(defined_); return srcEnd_; }
1416 : };
1417 :
1418 : typedef Vector<const Func*> ConstFuncVector;
1419 : typedef Vector<Func*> FuncVector;
1420 :
1421 : class FuncPtrTable
1422 : {
1423 : uint32_t sigIndex_;
1424 : PropertyName* name_;
1425 : uint32_t firstUse_;
1426 : uint32_t mask_;
1427 : bool defined_;
1428 :
1429 : FuncPtrTable(FuncPtrTable&& rhs) = delete;
1430 :
1431 : public:
1432 0 : FuncPtrTable(uint32_t sigIndex, PropertyName* name, uint32_t firstUse, uint32_t mask)
1433 0 : : sigIndex_(sigIndex), name_(name), firstUse_(firstUse), mask_(mask), defined_(false)
1434 0 : {}
1435 :
1436 0 : uint32_t sigIndex() const { return sigIndex_; }
1437 0 : PropertyName* name() const { return name_; }
1438 0 : uint32_t firstUse() const { return firstUse_; }
1439 0 : unsigned mask() const { return mask_; }
1440 0 : bool defined() const { return defined_; }
1441 0 : void define() { MOZ_ASSERT(!defined_); defined_ = true; }
1442 : };
1443 :
1444 : typedef Vector<FuncPtrTable*> FuncPtrTableVector;
1445 :
1446 : class Global
1447 : {
1448 : public:
1449 : enum Which {
1450 : Variable,
1451 : ConstantLiteral,
1452 : ConstantImport,
1453 : Function,
1454 : FuncPtrTable,
1455 : FFI,
1456 : ArrayView,
1457 : ArrayViewCtor,
1458 : MathBuiltinFunction,
1459 : AtomicsBuiltinFunction,
1460 : SimdCtor,
1461 : SimdOp
1462 : };
1463 :
1464 : private:
1465 : Which which_;
1466 0 : union {
1467 : struct {
1468 : Type::Which type_;
1469 : unsigned index_;
1470 : NumLit literalValue_;
1471 : } varOrConst;
1472 : uint32_t funcIndex_;
1473 : uint32_t funcPtrTableIndex_;
1474 : uint32_t ffiIndex_;
1475 : struct {
1476 : Scalar::Type viewType_;
1477 : } viewInfo;
1478 : AsmJSMathBuiltinFunction mathBuiltinFunc_;
1479 : AsmJSAtomicsBuiltinFunction atomicsBuiltinFunc_;
1480 : SimdType simdCtorType_;
1481 : struct {
1482 : SimdType type_;
1483 : SimdOperation which_;
1484 : } simdOp;
1485 : } u;
1486 :
1487 : friend class ModuleValidator;
1488 : friend class js::LifoAlloc;
1489 :
1490 0 : explicit Global(Which which) : which_(which) {}
1491 :
1492 : public:
1493 0 : Which which() const {
1494 0 : return which_;
1495 : }
1496 0 : Type varOrConstType() const {
1497 0 : MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport);
1498 0 : return u.varOrConst.type_;
1499 : }
1500 0 : unsigned varOrConstIndex() const {
1501 0 : MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
1502 0 : return u.varOrConst.index_;
1503 : }
1504 : bool isConst() const {
1505 : return which_ == ConstantLiteral || which_ == ConstantImport;
1506 : }
1507 0 : NumLit constLiteralValue() const {
1508 0 : MOZ_ASSERT(which_ == ConstantLiteral);
1509 0 : return u.varOrConst.literalValue_;
1510 : }
1511 0 : uint32_t funcIndex() const {
1512 0 : MOZ_ASSERT(which_ == Function);
1513 0 : return u.funcIndex_;
1514 : }
1515 0 : uint32_t funcPtrTableIndex() const {
1516 0 : MOZ_ASSERT(which_ == FuncPtrTable);
1517 0 : return u.funcPtrTableIndex_;
1518 : }
1519 0 : unsigned ffiIndex() const {
1520 0 : MOZ_ASSERT(which_ == FFI);
1521 0 : return u.ffiIndex_;
1522 : }
1523 0 : bool isAnyArrayView() const {
1524 0 : return which_ == ArrayView || which_ == ArrayViewCtor;
1525 : }
1526 0 : Scalar::Type viewType() const {
1527 0 : MOZ_ASSERT(isAnyArrayView());
1528 0 : return u.viewInfo.viewType_;
1529 : }
1530 0 : bool isMathFunction() const {
1531 0 : return which_ == MathBuiltinFunction;
1532 : }
1533 0 : AsmJSMathBuiltinFunction mathBuiltinFunction() const {
1534 0 : MOZ_ASSERT(which_ == MathBuiltinFunction);
1535 0 : return u.mathBuiltinFunc_;
1536 : }
1537 0 : bool isAtomicsFunction() const {
1538 0 : return which_ == AtomicsBuiltinFunction;
1539 : }
1540 0 : AsmJSAtomicsBuiltinFunction atomicsBuiltinFunction() const {
1541 0 : MOZ_ASSERT(which_ == AtomicsBuiltinFunction);
1542 0 : return u.atomicsBuiltinFunc_;
1543 : }
1544 0 : bool isSimdCtor() const {
1545 0 : return which_ == SimdCtor;
1546 : }
1547 0 : SimdType simdCtorType() const {
1548 0 : MOZ_ASSERT(which_ == SimdCtor);
1549 0 : return u.simdCtorType_;
1550 : }
1551 0 : bool isSimdOperation() const {
1552 0 : return which_ == SimdOp;
1553 : }
1554 0 : SimdOperation simdOperation() const {
1555 0 : MOZ_ASSERT(which_ == SimdOp);
1556 0 : return u.simdOp.which_;
1557 : }
1558 0 : SimdType simdOperationType() const {
1559 0 : MOZ_ASSERT(which_ == SimdOp);
1560 0 : return u.simdOp.type_;
1561 : }
1562 : };
1563 :
1564 : struct MathBuiltin
1565 : {
1566 : enum Kind { Function, Constant };
1567 : Kind kind;
1568 :
1569 : union {
1570 : double cst;
1571 : AsmJSMathBuiltinFunction func;
1572 : } u;
1573 :
1574 0 : MathBuiltin() : kind(Kind(-1)) {}
1575 0 : explicit MathBuiltin(double cst) : kind(Constant) {
1576 0 : u.cst = cst;
1577 0 : }
1578 0 : explicit MathBuiltin(AsmJSMathBuiltinFunction func) : kind(Function) {
1579 0 : u.func = func;
1580 0 : }
1581 : };
1582 :
1583 : struct ArrayView
1584 : {
1585 0 : ArrayView(PropertyName* name, Scalar::Type type)
1586 0 : : name(name), type(type)
1587 0 : {}
1588 :
1589 : PropertyName* name;
1590 : Scalar::Type type;
1591 : };
1592 :
1593 : private:
1594 : class NamedSig
1595 : {
1596 : PropertyName* name_;
1597 : const SigWithId* sig_;
1598 :
1599 : public:
1600 0 : NamedSig(PropertyName* name, const SigWithId& sig)
1601 0 : : name_(name), sig_(&sig)
1602 0 : {}
1603 : PropertyName* name() const {
1604 : return name_;
1605 : }
1606 : const Sig& sig() const {
1607 : return *sig_;
1608 : }
1609 :
1610 : // Implement HashPolicy:
1611 : struct Lookup {
1612 : PropertyName* name;
1613 : const Sig& sig;
1614 0 : Lookup(PropertyName* name, const Sig& sig) : name(name), sig(sig) {}
1615 : };
1616 0 : static HashNumber hash(Lookup l) {
1617 0 : return HashGeneric(l.name, l.sig.hash());
1618 : }
1619 0 : static bool match(NamedSig lhs, Lookup rhs) {
1620 0 : return lhs.name_ == rhs.name && *lhs.sig_ == rhs.sig;
1621 : }
1622 : };
1623 : typedef HashMap<NamedSig, uint32_t, NamedSig> ImportMap;
1624 : typedef HashMap<const SigWithId*, uint32_t, SigHashPolicy> SigMap;
1625 : typedef HashMap<PropertyName*, Global*> GlobalMap;
1626 : typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
1627 : typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
1628 : typedef HashMap<PropertyName*, SimdOperation> SimdOperationNameMap;
1629 : typedef Vector<ArrayView> ArrayViewVector;
1630 :
1631 : JSContext* cx_;
1632 : AsmJSParser& parser_;
1633 : ParseNode* moduleFunctionNode_;
1634 : PropertyName* moduleFunctionName_;
1635 : PropertyName* globalArgumentName_;
1636 : PropertyName* importArgumentName_;
1637 : PropertyName* bufferArgumentName_;
1638 : MathNameMap standardLibraryMathNames_;
1639 : AtomicsNameMap standardLibraryAtomicsNames_;
1640 : SimdOperationNameMap standardLibrarySimdOpNames_;
1641 : RootedFunction dummyFunction_;
1642 :
1643 : // Validation-internal state:
1644 : LifoAlloc validationLifo_;
1645 : FuncVector functions_;
1646 : FuncPtrTableVector funcPtrTables_;
1647 : GlobalMap globalMap_;
1648 : SigMap sigMap_;
1649 : ImportMap importMap_;
1650 : ArrayViewVector arrayViews_;
1651 : bool atomicsPresent_;
1652 : bool simdPresent_;
1653 :
1654 : // State used to build the AsmJSModule in finish():
1655 : ModuleGenerator mg_;
1656 : MutableAsmJSMetadata asmJSMetadata_;
1657 :
1658 : // Error reporting:
1659 : UniqueChars errorString_;
1660 : uint32_t errorOffset_;
1661 : bool errorOverRecursed_;
1662 :
1663 : // Helpers:
1664 0 : bool addStandardLibraryMathName(const char* name, AsmJSMathBuiltinFunction func) {
1665 0 : JSAtom* atom = Atomize(cx_, name, strlen(name));
1666 0 : if (!atom)
1667 0 : return false;
1668 0 : MathBuiltin builtin(func);
1669 0 : return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
1670 : }
1671 0 : bool addStandardLibraryMathName(const char* name, double cst) {
1672 0 : JSAtom* atom = Atomize(cx_, name, strlen(name));
1673 0 : if (!atom)
1674 0 : return false;
1675 0 : MathBuiltin builtin(cst);
1676 0 : return standardLibraryMathNames_.putNew(atom->asPropertyName(), builtin);
1677 : }
1678 0 : bool addStandardLibraryAtomicsName(const char* name, AsmJSAtomicsBuiltinFunction func) {
1679 0 : JSAtom* atom = Atomize(cx_, name, strlen(name));
1680 0 : if (!atom)
1681 0 : return false;
1682 0 : return standardLibraryAtomicsNames_.putNew(atom->asPropertyName(), func);
1683 : }
1684 0 : bool addStandardLibrarySimdOpName(const char* name, SimdOperation op) {
1685 0 : JSAtom* atom = Atomize(cx_, name, strlen(name));
1686 0 : if (!atom)
1687 0 : return false;
1688 0 : return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
1689 : }
1690 0 : bool newSig(Sig&& sig, uint32_t* sigIndex) {
1691 0 : *sigIndex = 0;
1692 0 : if (mg_.numSigs() >= AsmJSMaxTypes)
1693 0 : return failCurrentOffset("too many signatures");
1694 :
1695 0 : *sigIndex = mg_.numSigs();
1696 0 : mg_.initSig(*sigIndex, Move(sig));
1697 0 : return true;
1698 : }
1699 0 : bool declareSig(Sig&& sig, uint32_t* sigIndex) {
1700 0 : SigMap::AddPtr p = sigMap_.lookupForAdd(sig);
1701 0 : if (p) {
1702 0 : *sigIndex = p->value();
1703 0 : MOZ_ASSERT(mg_.sig(*sigIndex) == sig);
1704 0 : return true;
1705 : }
1706 :
1707 0 : return newSig(Move(sig), sigIndex) &&
1708 0 : sigMap_.add(p, &mg_.sig(*sigIndex), *sigIndex);
1709 : }
1710 :
1711 : public:
1712 0 : ModuleValidator(JSContext* cx, AsmJSParser& parser, ParseNode* moduleFunctionNode)
1713 0 : : cx_(cx),
1714 : parser_(parser),
1715 : moduleFunctionNode_(moduleFunctionNode),
1716 0 : moduleFunctionName_(FunctionName(moduleFunctionNode)),
1717 : globalArgumentName_(nullptr),
1718 : importArgumentName_(nullptr),
1719 : bufferArgumentName_(nullptr),
1720 : standardLibraryMathNames_(cx),
1721 : standardLibraryAtomicsNames_(cx),
1722 : standardLibrarySimdOpNames_(cx),
1723 : dummyFunction_(cx),
1724 : validationLifo_(VALIDATION_LIFO_DEFAULT_CHUNK_SIZE),
1725 : functions_(cx),
1726 : funcPtrTables_(cx),
1727 : globalMap_(cx),
1728 : sigMap_(cx),
1729 : importMap_(cx),
1730 : arrayViews_(cx),
1731 : atomicsPresent_(false),
1732 : simdPresent_(false),
1733 : mg_(nullptr),
1734 : errorString_(nullptr),
1735 : errorOffset_(UINT32_MAX),
1736 0 : errorOverRecursed_(false)
1737 0 : {}
1738 :
1739 0 : ~ModuleValidator() {
1740 0 : if (errorString_) {
1741 0 : MOZ_ASSERT(errorOffset_ != UINT32_MAX);
1742 0 : typeFailure(errorOffset_, errorString_.get());
1743 : }
1744 0 : if (errorOverRecursed_)
1745 0 : ReportOverRecursed(cx_);
1746 0 : }
1747 :
1748 : private:
1749 0 : void typeFailure(uint32_t offset, ...) {
1750 : va_list args;
1751 0 : va_start(args, offset);
1752 :
1753 0 : TokenStream& ts = tokenStream();
1754 0 : ErrorMetadata metadata;
1755 0 : if (ts.computeErrorMetadata(&metadata, offset)) {
1756 0 : if (ts.options().throwOnAsmJSValidationFailureOption) {
1757 0 : ReportCompileError(cx_, Move(metadata), nullptr, JSREPORT_ERROR,
1758 0 : JSMSG_USE_ASM_TYPE_FAIL, args);
1759 : } else {
1760 : // asm.js type failure is indicated by calling one of the fail*
1761 : // functions below. These functions always return false to
1762 : // halt asm.js parsing. Whether normal parsing is attempted as
1763 : // fallback, depends whether an exception is also set.
1764 : //
1765 : // If warning succeeds, no exception is set. If warning fails,
1766 : // an exception is set and execution will halt. Thus it's safe
1767 : // and correct to ignore the return value here.
1768 0 : Unused << ts.compileWarning(Move(metadata), nullptr, JSREPORT_WARNING,
1769 : JSMSG_USE_ASM_TYPE_FAIL, args);
1770 : }
1771 : }
1772 :
1773 0 : va_end(args);
1774 0 : }
1775 :
1776 : public:
1777 0 : bool init() {
1778 0 : auto tierMetadata = js::MakeUnique<MetadataTier>(Tier::Ion);
1779 0 : if (!tierMetadata)
1780 0 : return false;
1781 :
1782 0 : asmJSMetadata_ = cx_->new_<AsmJSMetadata>(Move(tierMetadata));
1783 0 : if (!asmJSMetadata_)
1784 0 : return false;
1785 :
1786 0 : asmJSMetadata_->toStringStart = moduleFunctionNode_->pn_funbox->toStringStart;
1787 0 : asmJSMetadata_->srcStart = moduleFunctionNode_->pn_body->pn_pos.begin;
1788 0 : asmJSMetadata_->srcBodyStart = parser_.tokenStream.currentToken().pos.end;
1789 0 : asmJSMetadata_->strict = parser_.pc->sc()->strict() &&
1790 0 : !parser_.pc->sc()->hasExplicitUseStrict();
1791 0 : asmJSMetadata_->scriptSource.reset(parser_.ss);
1792 :
1793 0 : if (!globalMap_.init() || !sigMap_.init() || !importMap_.init())
1794 0 : return false;
1795 :
1796 0 : if (!standardLibraryMathNames_.init() ||
1797 0 : !addStandardLibraryMathName("sin", AsmJSMathBuiltin_sin) ||
1798 0 : !addStandardLibraryMathName("cos", AsmJSMathBuiltin_cos) ||
1799 0 : !addStandardLibraryMathName("tan", AsmJSMathBuiltin_tan) ||
1800 0 : !addStandardLibraryMathName("asin", AsmJSMathBuiltin_asin) ||
1801 0 : !addStandardLibraryMathName("acos", AsmJSMathBuiltin_acos) ||
1802 0 : !addStandardLibraryMathName("atan", AsmJSMathBuiltin_atan) ||
1803 0 : !addStandardLibraryMathName("ceil", AsmJSMathBuiltin_ceil) ||
1804 0 : !addStandardLibraryMathName("floor", AsmJSMathBuiltin_floor) ||
1805 0 : !addStandardLibraryMathName("exp", AsmJSMathBuiltin_exp) ||
1806 0 : !addStandardLibraryMathName("log", AsmJSMathBuiltin_log) ||
1807 0 : !addStandardLibraryMathName("pow", AsmJSMathBuiltin_pow) ||
1808 0 : !addStandardLibraryMathName("sqrt", AsmJSMathBuiltin_sqrt) ||
1809 0 : !addStandardLibraryMathName("abs", AsmJSMathBuiltin_abs) ||
1810 0 : !addStandardLibraryMathName("atan2", AsmJSMathBuiltin_atan2) ||
1811 0 : !addStandardLibraryMathName("imul", AsmJSMathBuiltin_imul) ||
1812 0 : !addStandardLibraryMathName("clz32", AsmJSMathBuiltin_clz32) ||
1813 0 : !addStandardLibraryMathName("fround", AsmJSMathBuiltin_fround) ||
1814 0 : !addStandardLibraryMathName("min", AsmJSMathBuiltin_min) ||
1815 0 : !addStandardLibraryMathName("max", AsmJSMathBuiltin_max) ||
1816 0 : !addStandardLibraryMathName("E", M_E) ||
1817 0 : !addStandardLibraryMathName("LN10", M_LN10) ||
1818 0 : !addStandardLibraryMathName("LN2", M_LN2) ||
1819 0 : !addStandardLibraryMathName("LOG2E", M_LOG2E) ||
1820 0 : !addStandardLibraryMathName("LOG10E", M_LOG10E) ||
1821 0 : !addStandardLibraryMathName("PI", M_PI) ||
1822 0 : !addStandardLibraryMathName("SQRT1_2", M_SQRT1_2) ||
1823 0 : !addStandardLibraryMathName("SQRT2", M_SQRT2))
1824 : {
1825 0 : return false;
1826 : }
1827 :
1828 0 : if (!standardLibraryAtomicsNames_.init() ||
1829 0 : !addStandardLibraryAtomicsName("compareExchange", AsmJSAtomicsBuiltin_compareExchange) ||
1830 0 : !addStandardLibraryAtomicsName("exchange", AsmJSAtomicsBuiltin_exchange) ||
1831 0 : !addStandardLibraryAtomicsName("load", AsmJSAtomicsBuiltin_load) ||
1832 0 : !addStandardLibraryAtomicsName("store", AsmJSAtomicsBuiltin_store) ||
1833 0 : !addStandardLibraryAtomicsName("add", AsmJSAtomicsBuiltin_add) ||
1834 0 : !addStandardLibraryAtomicsName("sub", AsmJSAtomicsBuiltin_sub) ||
1835 0 : !addStandardLibraryAtomicsName("and", AsmJSAtomicsBuiltin_and) ||
1836 0 : !addStandardLibraryAtomicsName("or", AsmJSAtomicsBuiltin_or) ||
1837 0 : !addStandardLibraryAtomicsName("xor", AsmJSAtomicsBuiltin_xor) ||
1838 0 : !addStandardLibraryAtomicsName("isLockFree", AsmJSAtomicsBuiltin_isLockFree))
1839 : {
1840 0 : return false;
1841 : }
1842 :
1843 : #define ADDSTDLIBSIMDOPNAME(op) || !addStandardLibrarySimdOpName(#op, SimdOperation::Fn_##op)
1844 0 : if (!standardLibrarySimdOpNames_.init()
1845 0 : FORALL_SIMD_ASMJS_OP(ADDSTDLIBSIMDOPNAME))
1846 : {
1847 0 : return false;
1848 : }
1849 : #undef ADDSTDLIBSIMDOPNAME
1850 :
1851 : // This flows into FunctionBox, so must be tenured.
1852 0 : dummyFunction_ = NewScriptedFunction(cx_, 0, JSFunction::INTERPRETED, nullptr,
1853 : /* proto = */ nullptr, gc::AllocKind::FUNCTION,
1854 0 : TenuredObject);
1855 0 : if (!dummyFunction_)
1856 0 : return false;
1857 :
1858 0 : ScriptedCaller scriptedCaller;
1859 0 : if (parser_.ss->filename()) {
1860 0 : scriptedCaller.line = scriptedCaller.column = 0; // unused
1861 0 : scriptedCaller.filename = DuplicateString(parser_.ss->filename());
1862 0 : if (!scriptedCaller.filename)
1863 0 : return false;
1864 : }
1865 :
1866 0 : CompileArgs args;
1867 0 : if (!args.initFromContext(cx_, Move(scriptedCaller)))
1868 0 : return false;
1869 :
1870 0 : auto env = MakeUnique<ModuleEnvironment>(ModuleKind::AsmJS);
1871 0 : if (!env ||
1872 0 : !env->sigs.resize(AsmJSMaxTypes) ||
1873 0 : !env->funcSigs.resize(AsmJSMaxFuncs) ||
1874 0 : !env->funcImportGlobalDataOffsets.resize(AsmJSMaxImports) ||
1875 0 : !env->tables.resize(AsmJSMaxTables) ||
1876 0 : !env->asmJSSigToTableIndex.resize(AsmJSMaxTypes))
1877 : {
1878 0 : return false;
1879 : }
1880 :
1881 0 : env->minMemoryLength = RoundUpToNextValidAsmJSHeapLength(0);
1882 :
1883 0 : if (!mg_.init(Move(env), args, asmJSMetadata_.get()))
1884 0 : return false;
1885 :
1886 0 : return true;
1887 : }
1888 :
1889 0 : JSContext* cx() const { return cx_; }
1890 0 : PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
1891 0 : PropertyName* globalArgumentName() const { return globalArgumentName_; }
1892 0 : PropertyName* importArgumentName() const { return importArgumentName_; }
1893 0 : PropertyName* bufferArgumentName() const { return bufferArgumentName_; }
1894 0 : ModuleGenerator& mg() { return mg_; }
1895 0 : AsmJSParser& parser() const { return parser_; }
1896 0 : TokenStream& tokenStream() const { return parser_.tokenStream; }
1897 0 : RootedFunction& dummyFunction() { return dummyFunction_; }
1898 0 : bool supportsSimd() const { return cx_->jitSupportsSimd(); }
1899 0 : bool atomicsPresent() const { return atomicsPresent_; }
1900 : uint32_t minMemoryLength() const { return mg_.minMemoryLength(); }
1901 :
1902 : void initModuleFunctionName(PropertyName* name) {
1903 : MOZ_ASSERT(!moduleFunctionName_);
1904 : moduleFunctionName_ = name;
1905 : }
1906 0 : MOZ_MUST_USE bool initGlobalArgumentName(PropertyName* n) {
1907 0 : MOZ_ASSERT(n->isTenured());
1908 0 : globalArgumentName_ = n;
1909 0 : if (n) {
1910 0 : asmJSMetadata_->globalArgumentName = StringToNewUTF8CharsZ(cx_, *n);
1911 0 : if (!asmJSMetadata_->globalArgumentName)
1912 0 : return false;
1913 : }
1914 0 : return true;
1915 : }
1916 0 : MOZ_MUST_USE bool initImportArgumentName(PropertyName* n) {
1917 0 : MOZ_ASSERT(n->isTenured());
1918 0 : importArgumentName_ = n;
1919 0 : if (n) {
1920 0 : asmJSMetadata_->importArgumentName = StringToNewUTF8CharsZ(cx_, *n);
1921 0 : if (!asmJSMetadata_->importArgumentName)
1922 0 : return false;
1923 : }
1924 0 : return true;
1925 : }
1926 0 : MOZ_MUST_USE bool initBufferArgumentName(PropertyName* n) {
1927 0 : MOZ_ASSERT(n->isTenured());
1928 0 : bufferArgumentName_ = n;
1929 0 : if (n) {
1930 0 : asmJSMetadata_->bufferArgumentName = StringToNewUTF8CharsZ(cx_, *n);
1931 0 : if (!asmJSMetadata_->bufferArgumentName)
1932 0 : return false;
1933 : }
1934 0 : return true;
1935 : }
1936 0 : bool addGlobalVarInit(PropertyName* var, const NumLit& lit, Type type, bool isConst) {
1937 0 : MOZ_ASSERT(type.isGlobalVarType());
1938 0 : MOZ_ASSERT(type == Type::canonicalize(Type::lit(lit)));
1939 :
1940 : uint32_t index;
1941 0 : if (!mg_.addGlobal(type.canonicalToValType(), isConst, &index))
1942 0 : return false;
1943 :
1944 0 : Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
1945 0 : Global* global = validationLifo_.new_<Global>(which);
1946 0 : if (!global)
1947 0 : return false;
1948 0 : global->u.varOrConst.index_ = index;
1949 0 : global->u.varOrConst.type_ = (isConst ? Type::lit(lit) : type).which();
1950 0 : if (isConst)
1951 0 : global->u.varOrConst.literalValue_ = lit;
1952 0 : if (!globalMap_.putNew(var, global))
1953 0 : return false;
1954 :
1955 0 : AsmJSGlobal g(AsmJSGlobal::Variable, nullptr);
1956 0 : g.pod.u.var.initKind_ = AsmJSGlobal::InitConstant;
1957 0 : g.pod.u.var.u.val_ = lit.value();
1958 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
1959 : }
1960 0 : bool addGlobalVarImport(PropertyName* var, PropertyName* field, Type type, bool isConst) {
1961 0 : MOZ_ASSERT(type.isGlobalVarType());
1962 :
1963 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
1964 0 : if (!fieldChars)
1965 0 : return false;
1966 :
1967 : uint32_t index;
1968 0 : ValType valType = type.canonicalToValType();
1969 0 : if (!mg_.addGlobal(valType, isConst, &index))
1970 0 : return false;
1971 :
1972 0 : Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
1973 0 : Global* global = validationLifo_.new_<Global>(which);
1974 0 : if (!global)
1975 0 : return false;
1976 0 : global->u.varOrConst.index_ = index;
1977 0 : global->u.varOrConst.type_ = type.which();
1978 0 : if (!globalMap_.putNew(var, global))
1979 0 : return false;
1980 :
1981 0 : AsmJSGlobal g(AsmJSGlobal::Variable, Move(fieldChars));
1982 0 : g.pod.u.var.initKind_ = AsmJSGlobal::InitImport;
1983 0 : g.pod.u.var.u.importType_ = valType;
1984 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
1985 : }
1986 0 : bool addArrayView(PropertyName* var, Scalar::Type vt, PropertyName* maybeField) {
1987 0 : UniqueChars fieldChars;
1988 0 : if (maybeField) {
1989 0 : fieldChars = StringToNewUTF8CharsZ(cx_, *maybeField);
1990 0 : if (!fieldChars)
1991 0 : return false;
1992 : }
1993 :
1994 0 : if (!arrayViews_.append(ArrayView(var, vt)))
1995 0 : return false;
1996 :
1997 0 : Global* global = validationLifo_.new_<Global>(Global::ArrayView);
1998 0 : if (!global)
1999 0 : return false;
2000 0 : global->u.viewInfo.viewType_ = vt;
2001 0 : if (!globalMap_.putNew(var, global))
2002 0 : return false;
2003 :
2004 0 : AsmJSGlobal g(AsmJSGlobal::ArrayView, Move(fieldChars));
2005 0 : g.pod.u.viewType_ = vt;
2006 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2007 : }
2008 0 : bool addMathBuiltinFunction(PropertyName* var, AsmJSMathBuiltinFunction func,
2009 : PropertyName* field)
2010 : {
2011 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
2012 0 : if (!fieldChars)
2013 0 : return false;
2014 :
2015 0 : Global* global = validationLifo_.new_<Global>(Global::MathBuiltinFunction);
2016 0 : if (!global)
2017 0 : return false;
2018 0 : global->u.mathBuiltinFunc_ = func;
2019 0 : if (!globalMap_.putNew(var, global))
2020 0 : return false;
2021 :
2022 0 : AsmJSGlobal g(AsmJSGlobal::MathBuiltinFunction, Move(fieldChars));
2023 0 : g.pod.u.mathBuiltinFunc_ = func;
2024 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2025 : }
2026 : private:
2027 0 : bool addGlobalDoubleConstant(PropertyName* var, double constant) {
2028 0 : Global* global = validationLifo_.new_<Global>(Global::ConstantLiteral);
2029 0 : if (!global)
2030 0 : return false;
2031 0 : global->u.varOrConst.type_ = Type::Double;
2032 0 : global->u.varOrConst.literalValue_ = NumLit(NumLit::Double, DoubleValue(constant));
2033 0 : return globalMap_.putNew(var, global);
2034 : }
2035 : public:
2036 0 : bool addMathBuiltinConstant(PropertyName* var, double constant, PropertyName* field) {
2037 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
2038 0 : if (!fieldChars)
2039 0 : return false;
2040 :
2041 0 : if (!addGlobalDoubleConstant(var, constant))
2042 0 : return false;
2043 :
2044 0 : AsmJSGlobal g(AsmJSGlobal::Constant, Move(fieldChars));
2045 0 : g.pod.u.constant.value_ = constant;
2046 0 : g.pod.u.constant.kind_ = AsmJSGlobal::MathConstant;
2047 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2048 : }
2049 0 : bool addGlobalConstant(PropertyName* var, double constant, PropertyName* field) {
2050 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
2051 0 : if (!fieldChars)
2052 0 : return false;
2053 :
2054 0 : if (!addGlobalDoubleConstant(var, constant))
2055 0 : return false;
2056 :
2057 0 : AsmJSGlobal g(AsmJSGlobal::Constant, Move(fieldChars));
2058 0 : g.pod.u.constant.value_ = constant;
2059 0 : g.pod.u.constant.kind_ = AsmJSGlobal::GlobalConstant;
2060 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2061 : }
2062 0 : bool addAtomicsBuiltinFunction(PropertyName* var, AsmJSAtomicsBuiltinFunction func,
2063 : PropertyName* field)
2064 : {
2065 0 : if (!JitOptions.asmJSAtomicsEnable)
2066 0 : return failCurrentOffset("asm.js Atomics only enabled in wasm test mode");
2067 :
2068 0 : atomicsPresent_ = true;
2069 :
2070 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
2071 0 : if (!fieldChars)
2072 0 : return false;
2073 :
2074 0 : Global* global = validationLifo_.new_<Global>(Global::AtomicsBuiltinFunction);
2075 0 : if (!global)
2076 0 : return false;
2077 0 : global->u.atomicsBuiltinFunc_ = func;
2078 0 : if (!globalMap_.putNew(var, global))
2079 0 : return false;
2080 :
2081 0 : AsmJSGlobal g(AsmJSGlobal::AtomicsBuiltinFunction, Move(fieldChars));
2082 0 : g.pod.u.atomicsBuiltinFunc_ = func;
2083 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2084 : }
2085 0 : bool addSimdCtor(PropertyName* var, SimdType type, PropertyName* field) {
2086 0 : simdPresent_ = true;
2087 :
2088 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
2089 0 : if (!fieldChars)
2090 0 : return false;
2091 :
2092 0 : Global* global = validationLifo_.new_<Global>(Global::SimdCtor);
2093 0 : if (!global)
2094 0 : return false;
2095 0 : global->u.simdCtorType_ = type;
2096 0 : if (!globalMap_.putNew(var, global))
2097 0 : return false;
2098 :
2099 0 : AsmJSGlobal g(AsmJSGlobal::SimdCtor, Move(fieldChars));
2100 0 : g.pod.u.simdCtorType_ = type;
2101 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2102 : }
2103 0 : bool addSimdOperation(PropertyName* var, SimdType type, SimdOperation op, PropertyName* field) {
2104 0 : simdPresent_ = true;
2105 :
2106 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
2107 0 : if (!fieldChars)
2108 0 : return false;
2109 :
2110 0 : Global* global = validationLifo_.new_<Global>(Global::SimdOp);
2111 0 : if (!global)
2112 0 : return false;
2113 0 : global->u.simdOp.type_ = type;
2114 0 : global->u.simdOp.which_ = op;
2115 0 : if (!globalMap_.putNew(var, global))
2116 0 : return false;
2117 :
2118 0 : AsmJSGlobal g(AsmJSGlobal::SimdOp, Move(fieldChars));
2119 0 : g.pod.u.simdOp.type_ = type;
2120 0 : g.pod.u.simdOp.which_ = op;
2121 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2122 : }
2123 0 : bool addArrayViewCtor(PropertyName* var, Scalar::Type vt, PropertyName* field) {
2124 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
2125 0 : if (!fieldChars)
2126 0 : return false;
2127 :
2128 0 : Global* global = validationLifo_.new_<Global>(Global::ArrayViewCtor);
2129 0 : if (!global)
2130 0 : return false;
2131 0 : global->u.viewInfo.viewType_ = vt;
2132 0 : if (!globalMap_.putNew(var, global))
2133 0 : return false;
2134 :
2135 0 : AsmJSGlobal g(AsmJSGlobal::ArrayViewCtor, Move(fieldChars));
2136 0 : g.pod.u.viewType_ = vt;
2137 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2138 : }
2139 0 : bool addFFI(PropertyName* var, PropertyName* field) {
2140 0 : UniqueChars fieldChars = StringToNewUTF8CharsZ(cx_, *field);
2141 0 : if (!fieldChars)
2142 0 : return false;
2143 :
2144 0 : if (asmJSMetadata_->numFFIs == UINT32_MAX)
2145 0 : return false;
2146 0 : uint32_t ffiIndex = asmJSMetadata_->numFFIs++;
2147 :
2148 0 : Global* global = validationLifo_.new_<Global>(Global::FFI);
2149 0 : if (!global)
2150 0 : return false;
2151 0 : global->u.ffiIndex_ = ffiIndex;
2152 0 : if (!globalMap_.putNew(var, global))
2153 0 : return false;
2154 :
2155 0 : AsmJSGlobal g(AsmJSGlobal::FFI, Move(fieldChars));
2156 0 : g.pod.u.ffiIndex_ = ffiIndex;
2157 0 : return asmJSMetadata_->asmJSGlobals.append(Move(g));
2158 : }
2159 0 : bool addExportField(ParseNode* pn, const Func& func, PropertyName* maybeField) {
2160 : // Record the field name of this export.
2161 0 : CacheableChars fieldChars;
2162 0 : if (maybeField)
2163 0 : fieldChars = StringToNewUTF8CharsZ(cx_, *maybeField);
2164 : else
2165 0 : fieldChars = DuplicateString("");
2166 0 : if (!fieldChars)
2167 0 : return false;
2168 :
2169 : // Declare which function is exported which gives us an index into the
2170 : // module ExportVector.
2171 0 : if (!mg_.addExport(Move(fieldChars), func.index()))
2172 0 : return false;
2173 :
2174 : // The exported function might have already been exported in which case
2175 : // the index will refer into the range of AsmJSExports.
2176 0 : return asmJSMetadata_->asmJSExports.emplaceBack(func.index(),
2177 0 : func.srcBegin() - asmJSMetadata_->srcStart,
2178 0 : func.srcEnd() - asmJSMetadata_->srcStart);
2179 : }
2180 0 : bool addFunction(PropertyName* name, uint32_t firstUse, Sig&& sig, Func** func) {
2181 : uint32_t sigIndex;
2182 0 : if (!declareSig(Move(sig), &sigIndex))
2183 0 : return false;
2184 0 : uint32_t funcIndex = AsmJSFirstDefFuncIndex + numFunctions();
2185 0 : if (funcIndex >= AsmJSMaxFuncs)
2186 0 : return failCurrentOffset("too many functions");
2187 0 : mg_.initFuncSig(funcIndex, sigIndex);
2188 0 : Global* global = validationLifo_.new_<Global>(Global::Function);
2189 0 : if (!global)
2190 0 : return false;
2191 0 : global->u.funcIndex_ = funcIndex;
2192 0 : if (!globalMap_.putNew(name, global))
2193 0 : return false;
2194 0 : *func = validationLifo_.new_<Func>(name, firstUse, funcIndex);
2195 0 : return *func && functions_.append(*func);
2196 : }
2197 0 : bool declareFuncPtrTable(Sig&& sig, PropertyName* name, uint32_t firstUse, uint32_t mask,
2198 : uint32_t* index)
2199 : {
2200 0 : if (mask > MaxTableInitialLength)
2201 0 : return failCurrentOffset("function pointer table too big");
2202 : uint32_t sigIndex;
2203 0 : if (!newSig(Move(sig), &sigIndex))
2204 0 : return false;
2205 0 : if (!mg_.initSigTableLength(sigIndex, mask + 1))
2206 0 : return false;
2207 0 : Global* global = validationLifo_.new_<Global>(Global::FuncPtrTable);
2208 0 : if (!global)
2209 0 : return false;
2210 0 : global->u.funcPtrTableIndex_ = *index = funcPtrTables_.length();
2211 0 : if (!globalMap_.putNew(name, global))
2212 0 : return false;
2213 0 : FuncPtrTable* t = validationLifo_.new_<FuncPtrTable>(sigIndex, name, firstUse, mask);
2214 0 : return t && funcPtrTables_.append(t);
2215 : }
2216 0 : bool defineFuncPtrTable(uint32_t funcPtrTableIndex, Uint32Vector&& elems) {
2217 0 : FuncPtrTable& table = *funcPtrTables_[funcPtrTableIndex];
2218 0 : if (table.defined())
2219 0 : return false;
2220 0 : table.define();
2221 0 : return mg_.initSigTableElems(table.sigIndex(), Move(elems));
2222 : }
2223 0 : bool declareImport(PropertyName* name, Sig&& sig, unsigned ffiIndex, uint32_t* funcIndex) {
2224 0 : ImportMap::AddPtr p = importMap_.lookupForAdd(NamedSig::Lookup(name, sig));
2225 0 : if (p) {
2226 0 : *funcIndex = p->value();
2227 0 : return true;
2228 : }
2229 0 : *funcIndex = asmJSMetadata_->asmJSImports.length();
2230 0 : if (*funcIndex > AsmJSMaxImports)
2231 0 : return failCurrentOffset("too many imports");
2232 0 : if (!asmJSMetadata_->asmJSImports.emplaceBack(ffiIndex))
2233 0 : return false;
2234 : uint32_t sigIndex;
2235 0 : if (!declareSig(Move(sig), &sigIndex))
2236 0 : return false;
2237 0 : if (!mg_.initImport(*funcIndex, sigIndex))
2238 0 : return false;
2239 0 : return importMap_.add(p, NamedSig(name, mg_.sig(sigIndex)), *funcIndex);
2240 : }
2241 :
2242 0 : bool tryConstantAccess(uint64_t start, uint64_t width) {
2243 0 : MOZ_ASSERT(UINT64_MAX - start > width);
2244 0 : uint64_t len = start + width;
2245 0 : if (len > uint64_t(INT32_MAX) + 1)
2246 0 : return false;
2247 0 : len = RoundUpToNextValidAsmJSHeapLength(len);
2248 0 : if (len > mg_.minMemoryLength())
2249 0 : mg_.bumpMinMemoryLength(len);
2250 0 : return true;
2251 : }
2252 :
2253 : // Error handling.
2254 0 : bool hasAlreadyFailed() const {
2255 0 : return !!errorString_;
2256 : }
2257 :
2258 0 : bool failOffset(uint32_t offset, const char* str) {
2259 0 : MOZ_ASSERT(!hasAlreadyFailed());
2260 0 : MOZ_ASSERT(errorOffset_ == UINT32_MAX);
2261 0 : MOZ_ASSERT(str);
2262 0 : errorOffset_ = offset;
2263 0 : errorString_ = DuplicateString(str);
2264 0 : return false;
2265 : }
2266 :
2267 0 : bool failCurrentOffset(const char* str) {
2268 0 : return failOffset(tokenStream().currentToken().pos.begin, str);
2269 : }
2270 :
2271 0 : bool fail(ParseNode* pn, const char* str) {
2272 0 : return failOffset(pn->pn_pos.begin, str);
2273 : }
2274 :
2275 0 : bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap) MOZ_FORMAT_PRINTF(3, 0) {
2276 0 : MOZ_ASSERT(!hasAlreadyFailed());
2277 0 : MOZ_ASSERT(errorOffset_ == UINT32_MAX);
2278 0 : MOZ_ASSERT(fmt);
2279 0 : errorOffset_ = offset;
2280 0 : errorString_ = JS_vsmprintf(fmt, ap);
2281 0 : return false;
2282 : }
2283 :
2284 0 : bool failfOffset(uint32_t offset, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
2285 : va_list ap;
2286 0 : va_start(ap, fmt);
2287 0 : failfVAOffset(offset, fmt, ap);
2288 0 : va_end(ap);
2289 0 : return false;
2290 : }
2291 :
2292 0 : bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
2293 : va_list ap;
2294 0 : va_start(ap, fmt);
2295 0 : failfVAOffset(pn->pn_pos.begin, fmt, ap);
2296 0 : va_end(ap);
2297 0 : return false;
2298 : }
2299 :
2300 0 : bool failNameOffset(uint32_t offset, const char* fmt, PropertyName* name) {
2301 : // This function is invoked without the caller properly rooting its locals.
2302 0 : gc::AutoSuppressGC suppress(cx_);
2303 0 : JSAutoByteString bytes;
2304 0 : if (AtomToPrintableString(cx_, name, &bytes))
2305 0 : failfOffset(offset, fmt, bytes.ptr());
2306 0 : return false;
2307 : }
2308 :
2309 0 : bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
2310 0 : return failNameOffset(pn->pn_pos.begin, fmt, name);
2311 : }
2312 :
2313 0 : bool failOverRecursed() {
2314 0 : errorOverRecursed_ = true;
2315 0 : return false;
2316 : }
2317 :
2318 : unsigned numArrayViews() const {
2319 : return arrayViews_.length();
2320 : }
2321 : const ArrayView& arrayView(unsigned i) const {
2322 : return arrayViews_[i];
2323 : }
2324 0 : unsigned numFunctions() const {
2325 0 : return functions_.length();
2326 : }
2327 0 : Func& function(unsigned i) const {
2328 0 : return *functions_[i];
2329 : }
2330 0 : unsigned numFuncPtrTables() const {
2331 0 : return funcPtrTables_.length();
2332 : }
2333 0 : FuncPtrTable& funcPtrTable(unsigned i) const {
2334 0 : return *funcPtrTables_[i];
2335 : }
2336 :
2337 0 : const Global* lookupGlobal(PropertyName* name) const {
2338 0 : if (GlobalMap::Ptr p = globalMap_.lookup(name))
2339 0 : return p->value();
2340 0 : return nullptr;
2341 : }
2342 :
2343 0 : Func* lookupFunction(PropertyName* name) {
2344 0 : if (GlobalMap::Ptr p = globalMap_.lookup(name)) {
2345 0 : Global* value = p->value();
2346 0 : if (value->which() == Global::Function) {
2347 0 : MOZ_ASSERT(value->funcIndex() >= AsmJSFirstDefFuncIndex);
2348 0 : return functions_[value->funcIndex() - AsmJSFirstDefFuncIndex];
2349 : }
2350 : }
2351 0 : return nullptr;
2352 : }
2353 :
2354 0 : bool lookupStandardLibraryMathName(PropertyName* name, MathBuiltin* mathBuiltin) const {
2355 0 : if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
2356 0 : *mathBuiltin = p->value();
2357 0 : return true;
2358 : }
2359 0 : return false;
2360 : }
2361 0 : bool lookupStandardLibraryAtomicsName(PropertyName* name, AsmJSAtomicsBuiltinFunction* atomicsBuiltin) const {
2362 0 : if (AtomicsNameMap::Ptr p = standardLibraryAtomicsNames_.lookup(name)) {
2363 0 : *atomicsBuiltin = p->value();
2364 0 : return true;
2365 : }
2366 0 : return false;
2367 : }
2368 0 : bool lookupStandardSimdOpName(PropertyName* name, SimdOperation* op) const {
2369 0 : if (SimdOperationNameMap::Ptr p = standardLibrarySimdOpNames_.lookup(name)) {
2370 0 : *op = p->value();
2371 0 : return true;
2372 : }
2373 0 : return false;
2374 : }
2375 :
2376 0 : bool startFunctionBodies() {
2377 0 : if (!arrayViews_.empty())
2378 0 : mg_.initMemoryUsage(atomicsPresent_ ? MemoryUsage::Shared : MemoryUsage::Unshared);
2379 :
2380 0 : return mg_.startFuncDefs();
2381 : }
2382 0 : bool finishFunctionBodies() {
2383 0 : return mg_.finishFuncDefs();
2384 : }
2385 0 : SharedModule finish() {
2386 0 : asmJSMetadata_->usesSimd = simdPresent_;
2387 :
2388 0 : MOZ_ASSERT(asmJSMetadata_->asmJSFuncNames.empty());
2389 0 : for (const Func* func : functions_) {
2390 0 : CacheableChars funcName = StringToNewUTF8CharsZ(cx_, *func->name());
2391 0 : if (!funcName || !asmJSMetadata_->asmJSFuncNames.emplaceBack(Move(funcName)))
2392 0 : return nullptr;
2393 : }
2394 :
2395 0 : uint32_t endBeforeCurly = tokenStream().currentToken().pos.end;
2396 0 : asmJSMetadata_->srcLength = endBeforeCurly - asmJSMetadata_->srcStart;
2397 :
2398 0 : TokenPos pos;
2399 0 : JS_ALWAYS_TRUE(tokenStream().peekTokenPos(&pos, TokenStream::Operand));
2400 0 : uint32_t endAfterCurly = pos.end;
2401 0 : asmJSMetadata_->srcLengthWithRightBrace = endAfterCurly - asmJSMetadata_->srcStart;
2402 :
2403 : // asm.js does not have any wasm bytecode to save; view-source is
2404 : // provided through the ScriptSource.
2405 0 : SharedBytes bytes = js_new<ShareableBytes>();
2406 0 : if (!bytes)
2407 0 : return nullptr;
2408 :
2409 0 : return mg_.finish(*bytes);
2410 : }
2411 : };
2412 :
2413 : /*****************************************************************************/
2414 : // Numeric literal utilities
2415 :
2416 : static bool
2417 0 : IsNumericNonFloatLiteral(ParseNode* pn)
2418 : {
2419 : // Note: '-' is never rolled into the number; numbers are always positive
2420 : // and negations must be applied manually.
2421 0 : return pn->isKind(PNK_NUMBER) ||
2422 0 : (pn->isKind(PNK_NEG) && UnaryKid(pn)->isKind(PNK_NUMBER));
2423 : }
2424 :
2425 : static bool
2426 0 : IsCallToGlobal(ModuleValidator& m, ParseNode* pn, const ModuleValidator::Global** global)
2427 : {
2428 0 : if (!pn->isKind(PNK_CALL))
2429 0 : return false;
2430 :
2431 0 : ParseNode* callee = CallCallee(pn);
2432 0 : if (!callee->isKind(PNK_NAME))
2433 0 : return false;
2434 :
2435 0 : *global = m.lookupGlobal(callee->name());
2436 0 : return !!*global;
2437 : }
2438 :
2439 : static bool
2440 0 : IsCoercionCall(ModuleValidator& m, ParseNode* pn, Type* coerceTo, ParseNode** coercedExpr)
2441 : {
2442 : const ModuleValidator::Global* global;
2443 0 : if (!IsCallToGlobal(m, pn, &global))
2444 0 : return false;
2445 :
2446 0 : if (CallArgListLength(pn) != 1)
2447 0 : return false;
2448 :
2449 0 : if (coercedExpr)
2450 0 : *coercedExpr = CallArgList(pn);
2451 :
2452 0 : if (global->isMathFunction() && global->mathBuiltinFunction() == AsmJSMathBuiltin_fround) {
2453 0 : *coerceTo = Type::Float;
2454 0 : return true;
2455 : }
2456 :
2457 0 : if (global->isSimdOperation() && global->simdOperation() == SimdOperation::Fn_check) {
2458 0 : *coerceTo = global->simdOperationType();
2459 0 : return true;
2460 : }
2461 :
2462 0 : return false;
2463 : }
2464 :
2465 : static bool
2466 0 : IsFloatLiteral(ModuleValidator& m, ParseNode* pn)
2467 : {
2468 : ParseNode* coercedExpr;
2469 0 : Type coerceTo;
2470 0 : if (!IsCoercionCall(m, pn, &coerceTo, &coercedExpr))
2471 0 : return false;
2472 : // Don't fold into || to avoid clang/memcheck bug (bug 1077031).
2473 0 : if (!coerceTo.isFloat())
2474 0 : return false;
2475 0 : return IsNumericNonFloatLiteral(coercedExpr);
2476 : }
2477 :
2478 : static bool
2479 0 : IsSimdTuple(ModuleValidator& m, ParseNode* pn, SimdType* type)
2480 : {
2481 : const ModuleValidator::Global* global;
2482 0 : if (!IsCallToGlobal(m, pn, &global))
2483 0 : return false;
2484 :
2485 0 : if (!global->isSimdCtor())
2486 0 : return false;
2487 :
2488 0 : if (CallArgListLength(pn) != GetSimdLanes(global->simdCtorType()))
2489 0 : return false;
2490 :
2491 0 : *type = global->simdCtorType();
2492 0 : return true;
2493 : }
2494 :
2495 : static bool
2496 : IsNumericLiteral(ModuleValidator& m, ParseNode* pn, bool* isSimd = nullptr);
2497 :
2498 : static NumLit
2499 : ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn);
2500 :
2501 : static inline bool
2502 : IsLiteralInt(ModuleValidator& m, ParseNode* pn, uint32_t* u32);
2503 :
2504 : static bool
2505 0 : IsSimdLiteral(ModuleValidator& m, ParseNode* pn)
2506 : {
2507 : SimdType type;
2508 0 : if (!IsSimdTuple(m, pn, &type))
2509 0 : return false;
2510 :
2511 0 : ParseNode* arg = CallArgList(pn);
2512 0 : unsigned length = GetSimdLanes(type);
2513 0 : for (unsigned i = 0; i < length; i++) {
2514 0 : if (!IsNumericLiteral(m, arg))
2515 0 : return false;
2516 :
2517 : uint32_t _;
2518 0 : switch (type) {
2519 : case SimdType::Int8x16:
2520 : case SimdType::Int16x8:
2521 : case SimdType::Int32x4:
2522 : case SimdType::Uint8x16:
2523 : case SimdType::Uint16x8:
2524 : case SimdType::Uint32x4:
2525 : case SimdType::Bool8x16:
2526 : case SimdType::Bool16x8:
2527 : case SimdType::Bool32x4:
2528 0 : if (!IsLiteralInt(m, arg, &_))
2529 0 : return false;
2530 0 : break;
2531 : case SimdType::Float32x4:
2532 0 : if (!IsNumericNonFloatLiteral(arg))
2533 0 : return false;
2534 0 : break;
2535 : default:
2536 0 : MOZ_CRASH("unhandled simd type");
2537 : }
2538 :
2539 0 : arg = NextNode(arg);
2540 : }
2541 :
2542 0 : MOZ_ASSERT(arg == nullptr);
2543 0 : return true;
2544 : }
2545 :
2546 : static bool
2547 0 : IsNumericLiteral(ModuleValidator& m, ParseNode* pn, bool* isSimd)
2548 : {
2549 0 : if (IsNumericNonFloatLiteral(pn) || IsFloatLiteral(m, pn))
2550 0 : return true;
2551 0 : if (IsSimdLiteral(m, pn)) {
2552 0 : if (isSimd)
2553 0 : *isSimd = true;
2554 0 : return true;
2555 : }
2556 0 : return false;
2557 : }
2558 :
2559 : // The JS grammar treats -42 as -(42) (i.e., with separate grammar
2560 : // productions) for the unary - and literal 42). However, the asm.js spec
2561 : // recognizes -42 (modulo parens, so -(42) and -((42))) as a single literal
2562 : // so fold the two potential parse nodes into a single double value.
2563 : static double
2564 0 : ExtractNumericNonFloatValue(ParseNode* pn, ParseNode** out = nullptr)
2565 : {
2566 0 : MOZ_ASSERT(IsNumericNonFloatLiteral(pn));
2567 :
2568 0 : if (pn->isKind(PNK_NEG)) {
2569 0 : pn = UnaryKid(pn);
2570 0 : if (out)
2571 0 : *out = pn;
2572 0 : return -NumberNodeValue(pn);
2573 : }
2574 :
2575 0 : return NumberNodeValue(pn);
2576 : }
2577 :
2578 : static NumLit
2579 0 : ExtractSimdValue(ModuleValidator& m, ParseNode* pn)
2580 : {
2581 0 : MOZ_ASSERT(IsSimdLiteral(m, pn));
2582 :
2583 0 : SimdType type = SimdType::Count;
2584 0 : JS_ALWAYS_TRUE(IsSimdTuple(m, pn, &type));
2585 0 : MOZ_ASSERT(CallArgListLength(pn) == GetSimdLanes(type));
2586 :
2587 0 : ParseNode* arg = CallArgList(pn);
2588 0 : switch (type) {
2589 : case SimdType::Int8x16:
2590 : case SimdType::Uint8x16: {
2591 0 : MOZ_ASSERT(GetSimdLanes(type) == 16);
2592 : int8_t val[16];
2593 0 : for (size_t i = 0; i < 16; i++, arg = NextNode(arg)) {
2594 : uint32_t u32;
2595 0 : JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
2596 0 : val[i] = int8_t(u32);
2597 : }
2598 0 : MOZ_ASSERT(arg == nullptr);
2599 0 : NumLit::Which w = type == SimdType::Uint8x16 ? NumLit::Uint8x16 : NumLit::Int8x16;
2600 0 : return NumLit(w, SimdConstant::CreateX16(val));
2601 : }
2602 : case SimdType::Int16x8:
2603 : case SimdType::Uint16x8: {
2604 0 : MOZ_ASSERT(GetSimdLanes(type) == 8);
2605 : int16_t val[8];
2606 0 : for (size_t i = 0; i < 8; i++, arg = NextNode(arg)) {
2607 : uint32_t u32;
2608 0 : JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
2609 0 : val[i] = int16_t(u32);
2610 : }
2611 0 : MOZ_ASSERT(arg == nullptr);
2612 0 : NumLit::Which w = type == SimdType::Uint16x8 ? NumLit::Uint16x8 : NumLit::Int16x8;
2613 0 : return NumLit(w, SimdConstant::CreateX8(val));
2614 : }
2615 : case SimdType::Int32x4:
2616 : case SimdType::Uint32x4: {
2617 0 : MOZ_ASSERT(GetSimdLanes(type) == 4);
2618 : int32_t val[4];
2619 0 : for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
2620 : uint32_t u32;
2621 0 : JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
2622 0 : val[i] = int32_t(u32);
2623 : }
2624 0 : MOZ_ASSERT(arg == nullptr);
2625 0 : NumLit::Which w = type == SimdType::Uint32x4 ? NumLit::Uint32x4 : NumLit::Int32x4;
2626 0 : return NumLit(w, SimdConstant::CreateX4(val));
2627 : }
2628 : case SimdType::Float32x4: {
2629 0 : MOZ_ASSERT(GetSimdLanes(type) == 4);
2630 : float val[4];
2631 0 : for (size_t i = 0; i < 4; i++, arg = NextNode(arg))
2632 0 : val[i] = float(ExtractNumericNonFloatValue(arg));
2633 0 : MOZ_ASSERT(arg == nullptr);
2634 0 : return NumLit(NumLit::Float32x4, SimdConstant::CreateX4(val));
2635 : }
2636 : case SimdType::Bool8x16: {
2637 0 : MOZ_ASSERT(GetSimdLanes(type) == 16);
2638 : int8_t val[16];
2639 0 : for (size_t i = 0; i < 16; i++, arg = NextNode(arg)) {
2640 : uint32_t u32;
2641 0 : JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
2642 0 : val[i] = u32 ? -1 : 0;
2643 : }
2644 0 : MOZ_ASSERT(arg == nullptr);
2645 0 : return NumLit(NumLit::Bool8x16, SimdConstant::CreateX16(val));
2646 : }
2647 : case SimdType::Bool16x8: {
2648 0 : MOZ_ASSERT(GetSimdLanes(type) == 8);
2649 : int16_t val[8];
2650 0 : for (size_t i = 0; i < 8; i++, arg = NextNode(arg)) {
2651 : uint32_t u32;
2652 0 : JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
2653 0 : val[i] = u32 ? -1 : 0;
2654 : }
2655 0 : MOZ_ASSERT(arg == nullptr);
2656 0 : return NumLit(NumLit::Bool16x8, SimdConstant::CreateX8(val));
2657 : }
2658 : case SimdType::Bool32x4: {
2659 0 : MOZ_ASSERT(GetSimdLanes(type) == 4);
2660 : int32_t val[4];
2661 0 : for (size_t i = 0; i < 4; i++, arg = NextNode(arg)) {
2662 : uint32_t u32;
2663 0 : JS_ALWAYS_TRUE(IsLiteralInt(m, arg, &u32));
2664 0 : val[i] = u32 ? -1 : 0;
2665 : }
2666 0 : MOZ_ASSERT(arg == nullptr);
2667 0 : return NumLit(NumLit::Bool32x4, SimdConstant::CreateX4(val));
2668 : }
2669 : default:
2670 0 : break;
2671 : }
2672 :
2673 0 : MOZ_CRASH("Unexpected SIMD type.");
2674 : }
2675 :
2676 : static NumLit
2677 0 : ExtractNumericLiteral(ModuleValidator& m, ParseNode* pn)
2678 : {
2679 0 : MOZ_ASSERT(IsNumericLiteral(m, pn));
2680 :
2681 0 : if (pn->isKind(PNK_CALL)) {
2682 : // Float literals are explicitly coerced and thus the coerced literal may be
2683 : // any valid (non-float) numeric literal.
2684 0 : if (CallArgListLength(pn) == 1) {
2685 0 : pn = CallArgList(pn);
2686 0 : double d = ExtractNumericNonFloatValue(pn);
2687 0 : return NumLit(NumLit::Float, DoubleValue(d));
2688 : }
2689 :
2690 0 : return ExtractSimdValue(m, pn);
2691 : }
2692 :
2693 0 : double d = ExtractNumericNonFloatValue(pn, &pn);
2694 :
2695 : // The asm.js spec syntactically distinguishes any literal containing a
2696 : // decimal point or the literal -0 as having double type.
2697 0 : if (NumberNodeHasFrac(pn) || IsNegativeZero(d))
2698 0 : return NumLit(NumLit::Double, DoubleValue(d));
2699 :
2700 : // The syntactic checks above rule out these double values.
2701 0 : MOZ_ASSERT(!IsNegativeZero(d));
2702 0 : MOZ_ASSERT(!IsNaN(d));
2703 :
2704 : // Although doubles can only *precisely* represent 53-bit integers, they
2705 : // can *imprecisely* represent integers much bigger than an int64_t.
2706 : // Furthermore, d may be inf or -inf. In both cases, casting to an int64_t
2707 : // is undefined, so test against the integer bounds using doubles.
2708 0 : if (d < double(INT32_MIN) || d > double(UINT32_MAX))
2709 0 : return NumLit(NumLit::OutOfRangeInt, UndefinedValue());
2710 :
2711 : // With the above syntactic and range limitations, d is definitely an
2712 : // integer in the range [INT32_MIN, UINT32_MAX] range.
2713 0 : int64_t i64 = int64_t(d);
2714 0 : if (i64 >= 0) {
2715 0 : if (i64 <= INT32_MAX)
2716 0 : return NumLit(NumLit::Fixnum, Int32Value(i64));
2717 0 : MOZ_ASSERT(i64 <= UINT32_MAX);
2718 0 : return NumLit(NumLit::BigUnsigned, Int32Value(uint32_t(i64)));
2719 : }
2720 0 : MOZ_ASSERT(i64 >= INT32_MIN);
2721 0 : return NumLit(NumLit::NegativeInt, Int32Value(i64));
2722 : }
2723 :
2724 : static inline bool
2725 0 : IsLiteralInt(const NumLit& lit, uint32_t* u32)
2726 : {
2727 0 : switch (lit.which()) {
2728 : case NumLit::Fixnum:
2729 : case NumLit::BigUnsigned:
2730 : case NumLit::NegativeInt:
2731 0 : *u32 = lit.toUint32();
2732 0 : return true;
2733 : case NumLit::Double:
2734 : case NumLit::Float:
2735 : case NumLit::OutOfRangeInt:
2736 : case NumLit::Int8x16:
2737 : case NumLit::Uint8x16:
2738 : case NumLit::Int16x8:
2739 : case NumLit::Uint16x8:
2740 : case NumLit::Int32x4:
2741 : case NumLit::Uint32x4:
2742 : case NumLit::Float32x4:
2743 : case NumLit::Bool8x16:
2744 : case NumLit::Bool16x8:
2745 : case NumLit::Bool32x4:
2746 0 : return false;
2747 : }
2748 0 : MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal type");
2749 : }
2750 :
2751 : static inline bool
2752 0 : IsLiteralInt(ModuleValidator& m, ParseNode* pn, uint32_t* u32)
2753 : {
2754 0 : return IsNumericLiteral(m, pn) &&
2755 0 : IsLiteralInt(ExtractNumericLiteral(m, pn), u32);
2756 : }
2757 :
2758 : /*****************************************************************************/
2759 :
2760 : namespace {
2761 :
2762 : #define CASE(TYPE, OP) case SimdOperation::Fn_##OP: return MozOp::TYPE##OP;
2763 : #define I8x16CASE(OP) CASE(I8x16, OP)
2764 : #define I16x8CASE(OP) CASE(I16x8, OP)
2765 : #define I32x4CASE(OP) CASE(I32x4, OP)
2766 : #define F32x4CASE(OP) CASE(F32x4, OP)
2767 : #define B8x16CASE(OP) CASE(B8x16, OP)
2768 : #define B16x8CASE(OP) CASE(B16x8, OP)
2769 : #define B32x4CASE(OP) CASE(B32x4, OP)
2770 : #define ENUMERATE(TYPE, FOR_ALL, DO) \
2771 : switch(op) { \
2772 : case SimdOperation::Constructor: return MozOp::TYPE##Constructor;\
2773 : FOR_ALL(DO) \
2774 : default: break; \
2775 : }
2776 :
2777 : static inline MozOp
2778 0 : SimdToOp(SimdType type, SimdOperation op)
2779 : {
2780 0 : switch (type) {
2781 : case SimdType::Uint8x16:
2782 : // Handle the special unsigned opcodes, then fall through to Int8x16.
2783 0 : switch (op) {
2784 0 : case SimdOperation::Fn_addSaturate: return MozOp::I8x16addSaturateU;
2785 0 : case SimdOperation::Fn_subSaturate: return MozOp::I8x16subSaturateU;
2786 0 : case SimdOperation::Fn_extractLane: return MozOp::I8x16extractLaneU;
2787 0 : case SimdOperation::Fn_shiftRightByScalar: return MozOp::I8x16shiftRightByScalarU;
2788 0 : case SimdOperation::Fn_lessThan: return MozOp::I8x16lessThanU;
2789 0 : case SimdOperation::Fn_lessThanOrEqual: return MozOp::I8x16lessThanOrEqualU;
2790 0 : case SimdOperation::Fn_greaterThan: return MozOp::I8x16greaterThanU;
2791 0 : case SimdOperation::Fn_greaterThanOrEqual: return MozOp::I8x16greaterThanOrEqualU;
2792 0 : case SimdOperation::Fn_fromInt8x16Bits: return MozOp::Limit;
2793 0 : default: break;
2794 : }
2795 : MOZ_FALLTHROUGH;
2796 : case SimdType::Int8x16:
2797 : // Bitcasts Uint8x16 <--> Int8x16 become noops.
2798 0 : switch (op) {
2799 0 : case SimdOperation::Fn_fromUint8x16Bits: return MozOp::Limit;
2800 0 : case SimdOperation::Fn_fromUint16x8Bits: return MozOp::I8x16fromInt16x8Bits;
2801 0 : case SimdOperation::Fn_fromUint32x4Bits: return MozOp::I8x16fromInt32x4Bits;
2802 0 : default: break;
2803 : }
2804 0 : ENUMERATE(I8x16, FORALL_INT8X16_ASMJS_OP, I8x16CASE)
2805 0 : break;
2806 :
2807 : case SimdType::Uint16x8:
2808 : // Handle the special unsigned opcodes, then fall through to Int16x8.
2809 0 : switch(op) {
2810 0 : case SimdOperation::Fn_addSaturate: return MozOp::I16x8addSaturateU;
2811 0 : case SimdOperation::Fn_subSaturate: return MozOp::I16x8subSaturateU;
2812 0 : case SimdOperation::Fn_extractLane: return MozOp::I16x8extractLaneU;
2813 0 : case SimdOperation::Fn_shiftRightByScalar: return MozOp::I16x8shiftRightByScalarU;
2814 0 : case SimdOperation::Fn_lessThan: return MozOp::I16x8lessThanU;
2815 0 : case SimdOperation::Fn_lessThanOrEqual: return MozOp::I16x8lessThanOrEqualU;
2816 0 : case SimdOperation::Fn_greaterThan: return MozOp::I16x8greaterThanU;
2817 0 : case SimdOperation::Fn_greaterThanOrEqual: return MozOp::I16x8greaterThanOrEqualU;
2818 0 : case SimdOperation::Fn_fromInt16x8Bits: return MozOp::Limit;
2819 0 : default: break;
2820 : }
2821 : MOZ_FALLTHROUGH;
2822 : case SimdType::Int16x8:
2823 : // Bitcasts Uint16x8 <--> Int16x8 become noops.
2824 0 : switch (op) {
2825 0 : case SimdOperation::Fn_fromUint8x16Bits: return MozOp::I16x8fromInt8x16Bits;
2826 0 : case SimdOperation::Fn_fromUint16x8Bits: return MozOp::Limit;
2827 0 : case SimdOperation::Fn_fromUint32x4Bits: return MozOp::I16x8fromInt32x4Bits;
2828 0 : default: break;
2829 : }
2830 0 : ENUMERATE(I16x8, FORALL_INT16X8_ASMJS_OP, I16x8CASE)
2831 0 : break;
2832 :
2833 : case SimdType::Uint32x4:
2834 : // Handle the special unsigned opcodes, then fall through to Int32x4.
2835 0 : switch(op) {
2836 0 : case SimdOperation::Fn_shiftRightByScalar: return MozOp::I32x4shiftRightByScalarU;
2837 0 : case SimdOperation::Fn_lessThan: return MozOp::I32x4lessThanU;
2838 0 : case SimdOperation::Fn_lessThanOrEqual: return MozOp::I32x4lessThanOrEqualU;
2839 0 : case SimdOperation::Fn_greaterThan: return MozOp::I32x4greaterThanU;
2840 0 : case SimdOperation::Fn_greaterThanOrEqual: return MozOp::I32x4greaterThanOrEqualU;
2841 0 : case SimdOperation::Fn_fromFloat32x4: return MozOp::I32x4fromFloat32x4U;
2842 0 : case SimdOperation::Fn_fromInt32x4Bits: return MozOp::Limit;
2843 0 : default: break;
2844 : }
2845 : MOZ_FALLTHROUGH;
2846 : case SimdType::Int32x4:
2847 : // Bitcasts Uint32x4 <--> Int32x4 become noops.
2848 0 : switch (op) {
2849 0 : case SimdOperation::Fn_fromUint8x16Bits: return MozOp::I32x4fromInt8x16Bits;
2850 0 : case SimdOperation::Fn_fromUint16x8Bits: return MozOp::I32x4fromInt16x8Bits;
2851 0 : case SimdOperation::Fn_fromUint32x4Bits: return MozOp::Limit;
2852 0 : default: break;
2853 : }
2854 0 : ENUMERATE(I32x4, FORALL_INT32X4_ASMJS_OP, I32x4CASE)
2855 0 : break;
2856 :
2857 : case SimdType::Float32x4:
2858 0 : switch (op) {
2859 0 : case SimdOperation::Fn_fromUint8x16Bits: return MozOp::F32x4fromInt8x16Bits;
2860 0 : case SimdOperation::Fn_fromUint16x8Bits: return MozOp::F32x4fromInt16x8Bits;
2861 0 : case SimdOperation::Fn_fromUint32x4Bits: return MozOp::F32x4fromInt32x4Bits;
2862 0 : default: break;
2863 : }
2864 0 : ENUMERATE(F32x4, FORALL_FLOAT32X4_ASMJS_OP, F32x4CASE)
2865 0 : break;
2866 :
2867 : case SimdType::Bool8x16:
2868 0 : ENUMERATE(B8x16, FORALL_BOOL_SIMD_OP, B8x16CASE)
2869 0 : break;
2870 :
2871 : case SimdType::Bool16x8:
2872 0 : ENUMERATE(B16x8, FORALL_BOOL_SIMD_OP, B16x8CASE)
2873 0 : break;
2874 :
2875 : case SimdType::Bool32x4:
2876 0 : ENUMERATE(B32x4, FORALL_BOOL_SIMD_OP, B32x4CASE)
2877 0 : break;
2878 :
2879 0 : default: break;
2880 : }
2881 0 : MOZ_CRASH("unexpected SIMD (type, operator) combination");
2882 : }
2883 :
2884 : #undef CASE
2885 : #undef I8x16CASE
2886 : #undef I16x8CASE
2887 : #undef I32x4CASE
2888 : #undef F32x4CASE
2889 : #undef B8x16CASE
2890 : #undef B16x8CASE
2891 : #undef B32x4CASE
2892 : #undef ENUMERATE
2893 :
2894 : typedef Vector<PropertyName*, 4, SystemAllocPolicy> NameVector;
2895 :
2896 : // Encapsulates the building of an asm bytecode function from an asm.js function
2897 : // source code, packing the asm.js code into the asm bytecode form that can
2898 : // be decoded and compiled with a FunctionCompiler.
2899 0 : class MOZ_STACK_CLASS FunctionValidator
2900 : {
2901 : public:
2902 : struct Local
2903 : {
2904 : Type type;
2905 : unsigned slot;
2906 0 : Local(Type t, unsigned slot) : type(t), slot(slot) {
2907 0 : MOZ_ASSERT(type.isCanonicalValType());
2908 0 : }
2909 : };
2910 :
2911 : private:
2912 : typedef HashMap<PropertyName*, Local> LocalMap;
2913 : typedef HashMap<PropertyName*, uint32_t> LabelMap;
2914 :
2915 : ModuleValidator& m_;
2916 : ParseNode* fn_;
2917 :
2918 : FunctionGenerator fg_;
2919 : Maybe<Encoder> encoder_;
2920 :
2921 : LocalMap locals_;
2922 :
2923 : // Labels
2924 : LabelMap breakLabels_;
2925 : LabelMap continueLabels_;
2926 : Uint32Vector breakableStack_;
2927 : Uint32Vector continuableStack_;
2928 : uint32_t blockDepth_;
2929 :
2930 : bool hasAlreadyReturned_;
2931 : ExprType ret_;
2932 :
2933 : public:
2934 0 : FunctionValidator(ModuleValidator& m, ParseNode* fn)
2935 0 : : m_(m),
2936 : fn_(fn),
2937 : locals_(m.cx()),
2938 : breakLabels_(m.cx()),
2939 : continueLabels_(m.cx()),
2940 : blockDepth_(0),
2941 : hasAlreadyReturned_(false),
2942 0 : ret_(ExprType::Limit)
2943 0 : {}
2944 :
2945 0 : ModuleValidator& m() const { return m_; }
2946 0 : JSContext* cx() const { return m_.cx(); }
2947 0 : ParseNode* fn() const { return fn_; }
2948 :
2949 0 : bool init(PropertyName* name, unsigned line) {
2950 0 : if (!locals_.init() || !breakLabels_.init() || !continueLabels_.init())
2951 0 : return false;
2952 :
2953 0 : if (!m_.mg().startFuncDef(line, &fg_))
2954 0 : return false;
2955 :
2956 0 : encoder_.emplace(fg_.bytes());
2957 0 : return true;
2958 : }
2959 :
2960 0 : bool finish(uint32_t funcIndex) {
2961 0 : MOZ_ASSERT(!blockDepth_);
2962 0 : MOZ_ASSERT(breakableStack_.empty());
2963 0 : MOZ_ASSERT(continuableStack_.empty());
2964 0 : MOZ_ASSERT(breakLabels_.empty());
2965 0 : MOZ_ASSERT(continueLabels_.empty());
2966 0 : for (auto iter = locals_.all(); !iter.empty(); iter.popFront()) {
2967 0 : if (iter.front().value().type.isSimd()) {
2968 0 : setUsesSimd();
2969 0 : break;
2970 : }
2971 : }
2972 :
2973 0 : return m_.mg().finishFuncDef(funcIndex, &fg_);
2974 : }
2975 :
2976 0 : bool fail(ParseNode* pn, const char* str) {
2977 0 : return m_.fail(pn, str);
2978 : }
2979 :
2980 0 : bool failf(ParseNode* pn, const char* fmt, ...) MOZ_FORMAT_PRINTF(3, 4) {
2981 : va_list ap;
2982 0 : va_start(ap, fmt);
2983 0 : m_.failfVAOffset(pn->pn_pos.begin, fmt, ap);
2984 0 : va_end(ap);
2985 0 : return false;
2986 : }
2987 :
2988 0 : bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
2989 0 : return m_.failName(pn, fmt, name);
2990 : }
2991 :
2992 : /***************************************************** Attributes */
2993 :
2994 0 : void setUsesSimd() {
2995 0 : fg_.setUsesSimd();
2996 0 : }
2997 :
2998 0 : void setUsesAtomics() {
2999 0 : fg_.setUsesAtomics();
3000 0 : }
3001 :
3002 : /***************************************************** Local scope setup */
3003 :
3004 0 : bool addLocal(ParseNode* pn, PropertyName* name, Type type) {
3005 0 : LocalMap::AddPtr p = locals_.lookupForAdd(name);
3006 0 : if (p)
3007 0 : return failName(pn, "duplicate local name '%s' not allowed", name);
3008 0 : return locals_.add(p, name, Local(type, locals_.count()));
3009 : }
3010 :
3011 : /****************************** For consistency of returns in a function */
3012 :
3013 0 : bool hasAlreadyReturned() const {
3014 0 : return hasAlreadyReturned_;
3015 : }
3016 :
3017 0 : ExprType returnedType() const {
3018 0 : return ret_;
3019 : }
3020 :
3021 0 : void setReturnedType(ExprType ret) {
3022 0 : ret_ = ret;
3023 0 : hasAlreadyReturned_ = true;
3024 0 : }
3025 :
3026 : /**************************************************************** Labels */
3027 : private:
3028 0 : bool writeBr(uint32_t absolute, Op op = Op::Br) {
3029 0 : MOZ_ASSERT(op == Op::Br || op == Op::BrIf);
3030 0 : MOZ_ASSERT(absolute < blockDepth_);
3031 0 : return encoder().writeOp(op) &&
3032 0 : encoder().writeVarU32(blockDepth_ - 1 - absolute);
3033 : }
3034 0 : void removeLabel(PropertyName* label, LabelMap* map) {
3035 0 : LabelMap::Ptr p = map->lookup(label);
3036 0 : MOZ_ASSERT(p);
3037 0 : map->remove(p);
3038 0 : }
3039 :
3040 : public:
3041 0 : bool pushBreakableBlock() {
3042 0 : return encoder().writeOp(Op::Block) &&
3043 0 : encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
3044 0 : breakableStack_.append(blockDepth_++);
3045 : }
3046 0 : bool popBreakableBlock() {
3047 0 : JS_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
3048 0 : return encoder().writeOp(Op::End);
3049 : }
3050 :
3051 0 : bool pushUnbreakableBlock(const NameVector* labels = nullptr) {
3052 0 : if (labels) {
3053 0 : for (PropertyName* label : *labels) {
3054 0 : if (!breakLabels_.putNew(label, blockDepth_))
3055 0 : return false;
3056 : }
3057 : }
3058 0 : blockDepth_++;
3059 0 : return encoder().writeOp(Op::Block) &&
3060 0 : encoder().writeFixedU8(uint8_t(ExprType::Void));
3061 : }
3062 0 : bool popUnbreakableBlock(const NameVector* labels = nullptr) {
3063 0 : if (labels) {
3064 0 : for (PropertyName* label : *labels)
3065 0 : removeLabel(label, &breakLabels_);
3066 : }
3067 0 : --blockDepth_;
3068 0 : return encoder().writeOp(Op::End);
3069 : }
3070 :
3071 0 : bool pushContinuableBlock() {
3072 0 : return encoder().writeOp(Op::Block) &&
3073 0 : encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
3074 0 : continuableStack_.append(blockDepth_++);
3075 : }
3076 0 : bool popContinuableBlock() {
3077 0 : JS_ALWAYS_TRUE(continuableStack_.popCopy() == --blockDepth_);
3078 0 : return encoder().writeOp(Op::End);
3079 : }
3080 :
3081 0 : bool pushLoop() {
3082 0 : return encoder().writeOp(Op::Block) &&
3083 0 : encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
3084 0 : encoder().writeOp(Op::Loop) &&
3085 0 : encoder().writeFixedU8(uint8_t(ExprType::Void)) &&
3086 0 : breakableStack_.append(blockDepth_++) &&
3087 0 : continuableStack_.append(blockDepth_++);
3088 : }
3089 0 : bool popLoop() {
3090 0 : JS_ALWAYS_TRUE(continuableStack_.popCopy() == --blockDepth_);
3091 0 : JS_ALWAYS_TRUE(breakableStack_.popCopy() == --blockDepth_);
3092 0 : return encoder().writeOp(Op::End) &&
3093 0 : encoder().writeOp(Op::End);
3094 : }
3095 :
3096 0 : bool pushIf(size_t* typeAt) {
3097 0 : ++blockDepth_;
3098 0 : return encoder().writeOp(Op::If) &&
3099 0 : encoder().writePatchableFixedU7(typeAt);
3100 : }
3101 0 : bool switchToElse() {
3102 0 : MOZ_ASSERT(blockDepth_ > 0);
3103 0 : return encoder().writeOp(Op::Else);
3104 : }
3105 0 : void setIfType(size_t typeAt, ExprType type) {
3106 0 : encoder().patchFixedU7(typeAt, uint8_t(type));
3107 0 : }
3108 0 : bool popIf() {
3109 0 : MOZ_ASSERT(blockDepth_ > 0);
3110 0 : --blockDepth_;
3111 0 : return encoder().writeOp(Op::End);
3112 : }
3113 0 : bool popIf(size_t typeAt, ExprType type) {
3114 0 : MOZ_ASSERT(blockDepth_ > 0);
3115 0 : --blockDepth_;
3116 0 : if (!encoder().writeOp(Op::End))
3117 0 : return false;
3118 :
3119 0 : setIfType(typeAt, type);
3120 0 : return true;
3121 : }
3122 :
3123 0 : bool writeBreakIf() {
3124 0 : return writeBr(breakableStack_.back(), Op::BrIf);
3125 : }
3126 0 : bool writeContinueIf() {
3127 0 : return writeBr(continuableStack_.back(), Op::BrIf);
3128 : }
3129 0 : bool writeUnlabeledBreakOrContinue(bool isBreak) {
3130 0 : return writeBr(isBreak? breakableStack_.back() : continuableStack_.back());
3131 : }
3132 0 : bool writeContinue() {
3133 0 : return writeBr(continuableStack_.back());
3134 : }
3135 :
3136 0 : bool addLabels(const NameVector& labels, uint32_t relativeBreakDepth,
3137 : uint32_t relativeContinueDepth)
3138 : {
3139 0 : for (PropertyName* label : labels) {
3140 0 : if (!breakLabels_.putNew(label, blockDepth_ + relativeBreakDepth))
3141 0 : return false;
3142 0 : if (!continueLabels_.putNew(label, blockDepth_ + relativeContinueDepth))
3143 0 : return false;
3144 : }
3145 0 : return true;
3146 : }
3147 0 : void removeLabels(const NameVector& labels) {
3148 0 : for (PropertyName* label : labels) {
3149 0 : removeLabel(label, &breakLabels_);
3150 0 : removeLabel(label, &continueLabels_);
3151 : }
3152 0 : }
3153 0 : bool writeLabeledBreakOrContinue(PropertyName* label, bool isBreak) {
3154 0 : LabelMap& map = isBreak ? breakLabels_ : continueLabels_;
3155 0 : if (LabelMap::Ptr p = map.lookup(label))
3156 0 : return writeBr(p->value());
3157 0 : MOZ_CRASH("nonexistent label");
3158 : }
3159 :
3160 : /*************************************************** Read-only interface */
3161 :
3162 0 : const Local* lookupLocal(PropertyName* name) const {
3163 0 : if (auto p = locals_.lookup(name))
3164 0 : return &p->value();
3165 0 : return nullptr;
3166 : }
3167 :
3168 0 : const ModuleValidator::Global* lookupGlobal(PropertyName* name) const {
3169 0 : if (locals_.has(name))
3170 0 : return nullptr;
3171 0 : return m_.lookupGlobal(name);
3172 : }
3173 :
3174 0 : size_t numLocals() const { return locals_.count(); }
3175 :
3176 : /**************************************************** Encoding interface */
3177 :
3178 0 : Encoder& encoder() { return *encoder_; }
3179 :
3180 0 : MOZ_MUST_USE bool writeInt32Lit(int32_t i32) {
3181 0 : return encoder().writeOp(Op::I32Const) &&
3182 0 : encoder().writeVarS32(i32);
3183 : }
3184 0 : MOZ_MUST_USE bool writeConstExpr(const NumLit& lit) {
3185 0 : switch (lit.which()) {
3186 : case NumLit::Fixnum:
3187 : case NumLit::NegativeInt:
3188 : case NumLit::BigUnsigned:
3189 0 : return writeInt32Lit(lit.toInt32());
3190 : case NumLit::Float:
3191 0 : return encoder().writeOp(Op::F32Const) &&
3192 0 : encoder().writeFixedF32(lit.toFloat());
3193 : case NumLit::Double:
3194 0 : return encoder().writeOp(Op::F64Const) &&
3195 0 : encoder().writeFixedF64(lit.toDouble());
3196 : case NumLit::Int8x16:
3197 : case NumLit::Uint8x16:
3198 0 : return encoder().writeOp(MozOp::I8x16Const) &&
3199 0 : encoder().writeFixedI8x16(lit.simdValue().asInt8x16());
3200 : case NumLit::Int16x8:
3201 : case NumLit::Uint16x8:
3202 0 : return encoder().writeOp(MozOp::I16x8Const) &&
3203 0 : encoder().writeFixedI16x8(lit.simdValue().asInt16x8());
3204 : case NumLit::Int32x4:
3205 : case NumLit::Uint32x4:
3206 0 : return encoder().writeOp(MozOp::I32x4Const) &&
3207 0 : encoder().writeFixedI32x4(lit.simdValue().asInt32x4());
3208 : case NumLit::Float32x4:
3209 0 : return encoder().writeOp(MozOp::F32x4Const) &&
3210 0 : encoder().writeFixedF32x4(lit.simdValue().asFloat32x4());
3211 : case NumLit::Bool8x16:
3212 : // Boolean vectors use the Int8x16 memory representation.
3213 0 : return encoder().writeOp(MozOp::B8x16Const) &&
3214 0 : encoder().writeFixedI8x16(lit.simdValue().asInt8x16());
3215 : case NumLit::Bool16x8:
3216 : // Boolean vectors use the Int16x8 memory representation.
3217 0 : return encoder().writeOp(MozOp::B16x8Const) &&
3218 0 : encoder().writeFixedI16x8(lit.simdValue().asInt16x8());
3219 : case NumLit::Bool32x4:
3220 : // Boolean vectors use the Int32x4 memory representation.
3221 0 : return encoder().writeOp(MozOp::B32x4Const) &&
3222 0 : encoder().writeFixedI32x4(lit.simdValue().asInt32x4());
3223 : case NumLit::OutOfRangeInt:
3224 0 : break;
3225 : }
3226 0 : MOZ_CRASH("unexpected literal type");
3227 : }
3228 0 : MOZ_MUST_USE bool writeCall(ParseNode* pn, Op op) {
3229 0 : return encoder().writeOp(op) &&
3230 0 : fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
3231 : }
3232 0 : MOZ_MUST_USE bool writeCall(ParseNode* pn, MozOp op) {
3233 0 : return encoder().writeOp(op) &&
3234 0 : fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
3235 : }
3236 0 : MOZ_MUST_USE bool prepareCall(ParseNode* pn) {
3237 0 : return fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
3238 : }
3239 0 : MOZ_MUST_USE bool writeSimdOp(SimdType simdType, SimdOperation simdOp) {
3240 0 : MozOp op = SimdToOp(simdType, simdOp);
3241 0 : if (op == MozOp::Limit)
3242 0 : return true;
3243 0 : return encoder().writeOp(op);
3244 : }
3245 : };
3246 :
3247 : } /* anonymous namespace */
3248 :
3249 : /*****************************************************************************/
3250 : // asm.js type-checking and code-generation algorithm
3251 :
3252 : static bool
3253 0 : CheckIdentifier(ModuleValidator& m, ParseNode* usepn, PropertyName* name)
3254 : {
3255 0 : if (name == m.cx()->names().arguments || name == m.cx()->names().eval)
3256 0 : return m.failName(usepn, "'%s' is not an allowed identifier", name);
3257 0 : return true;
3258 : }
3259 :
3260 : static bool
3261 0 : CheckModuleLevelName(ModuleValidator& m, ParseNode* usepn, PropertyName* name)
3262 : {
3263 0 : if (!CheckIdentifier(m, usepn, name))
3264 0 : return false;
3265 :
3266 0 : if (name == m.moduleFunctionName() ||
3267 0 : name == m.globalArgumentName() ||
3268 0 : name == m.importArgumentName() ||
3269 0 : name == m.bufferArgumentName() ||
3270 0 : m.lookupGlobal(name))
3271 : {
3272 0 : return m.failName(usepn, "duplicate name '%s' not allowed", name);
3273 : }
3274 :
3275 0 : return true;
3276 : }
3277 :
3278 : static bool
3279 0 : CheckFunctionHead(ModuleValidator& m, ParseNode* fn)
3280 : {
3281 0 : if (fn->pn_funbox->hasRest())
3282 0 : return m.fail(fn, "rest args not allowed");
3283 0 : if (fn->pn_funbox->isExprBody())
3284 0 : return m.fail(fn, "expression closures not allowed");
3285 0 : if (fn->pn_funbox->hasDestructuringArgs)
3286 0 : return m.fail(fn, "destructuring args not allowed");
3287 0 : return true;
3288 : }
3289 :
3290 : static bool
3291 0 : CheckArgument(ModuleValidator& m, ParseNode* arg, PropertyName** name)
3292 : {
3293 0 : *name = nullptr;
3294 :
3295 0 : if (!arg->isKind(PNK_NAME))
3296 0 : return m.fail(arg, "argument is not a plain name");
3297 :
3298 0 : if (!CheckIdentifier(m, arg, arg->name()))
3299 0 : return false;
3300 :
3301 0 : *name = arg->name();
3302 0 : return true;
3303 : }
3304 :
3305 : static bool
3306 0 : CheckModuleArgument(ModuleValidator& m, ParseNode* arg, PropertyName** name)
3307 : {
3308 0 : if (!CheckArgument(m, arg, name))
3309 0 : return false;
3310 :
3311 0 : if (!CheckModuleLevelName(m, arg, *name))
3312 0 : return false;
3313 :
3314 0 : return true;
3315 : }
3316 :
3317 : static bool
3318 0 : CheckModuleArguments(ModuleValidator& m, ParseNode* fn)
3319 : {
3320 : unsigned numFormals;
3321 0 : ParseNode* arg1 = FunctionFormalParametersList(fn, &numFormals);
3322 0 : ParseNode* arg2 = arg1 ? NextNode(arg1) : nullptr;
3323 0 : ParseNode* arg3 = arg2 ? NextNode(arg2) : nullptr;
3324 :
3325 0 : if (numFormals > 3)
3326 0 : return m.fail(fn, "asm.js modules takes at most 3 argument");
3327 :
3328 0 : PropertyName* arg1Name = nullptr;
3329 0 : if (arg1 && !CheckModuleArgument(m, arg1, &arg1Name))
3330 0 : return false;
3331 0 : if (!m.initGlobalArgumentName(arg1Name))
3332 0 : return false;
3333 :
3334 0 : PropertyName* arg2Name = nullptr;
3335 0 : if (arg2 && !CheckModuleArgument(m, arg2, &arg2Name))
3336 0 : return false;
3337 0 : if (!m.initImportArgumentName(arg2Name))
3338 0 : return false;
3339 :
3340 0 : PropertyName* arg3Name = nullptr;
3341 0 : if (arg3 && !CheckModuleArgument(m, arg3, &arg3Name))
3342 0 : return false;
3343 0 : if (!m.initBufferArgumentName(arg3Name))
3344 0 : return false;
3345 :
3346 0 : return true;
3347 : }
3348 :
3349 : static bool
3350 0 : CheckPrecedingStatements(ModuleValidator& m, ParseNode* stmtList)
3351 : {
3352 0 : MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
3353 :
3354 0 : ParseNode* stmt = ListHead(stmtList);
3355 0 : for (unsigned i = 0, n = ListLength(stmtList); i < n; i++) {
3356 0 : if (!IsIgnoredDirective(m.cx(), stmt))
3357 0 : return m.fail(stmt, "invalid asm.js statement");
3358 : }
3359 :
3360 0 : return true;
3361 : }
3362 :
3363 : static bool
3364 0 : CheckGlobalVariableInitConstant(ModuleValidator& m, PropertyName* varName, ParseNode* initNode,
3365 : bool isConst)
3366 : {
3367 0 : NumLit lit = ExtractNumericLiteral(m, initNode);
3368 0 : if (!lit.valid())
3369 0 : return m.fail(initNode, "global initializer is out of representable integer range");
3370 :
3371 0 : Type canonicalType = Type::canonicalize(Type::lit(lit));
3372 0 : if (!canonicalType.isGlobalVarType())
3373 0 : return m.fail(initNode, "global variable type not allowed");
3374 :
3375 0 : return m.addGlobalVarInit(varName, lit, canonicalType, isConst);
3376 : }
3377 :
3378 : static bool
3379 0 : CheckTypeAnnotation(ModuleValidator& m, ParseNode* coercionNode, Type* coerceTo,
3380 : ParseNode** coercedExpr = nullptr)
3381 : {
3382 0 : switch (coercionNode->getKind()) {
3383 : case PNK_BITOR: {
3384 0 : ParseNode* rhs = BitwiseRight(coercionNode);
3385 : uint32_t i;
3386 0 : if (!IsLiteralInt(m, rhs, &i) || i != 0)
3387 0 : return m.fail(rhs, "must use |0 for argument/return coercion");
3388 0 : *coerceTo = Type::Int;
3389 0 : if (coercedExpr)
3390 0 : *coercedExpr = BitwiseLeft(coercionNode);
3391 0 : return true;
3392 : }
3393 : case PNK_POS: {
3394 0 : *coerceTo = Type::Double;
3395 0 : if (coercedExpr)
3396 0 : *coercedExpr = UnaryKid(coercionNode);
3397 0 : return true;
3398 : }
3399 : case PNK_CALL: {
3400 0 : if (IsCoercionCall(m, coercionNode, coerceTo, coercedExpr))
3401 0 : return true;
3402 0 : break;
3403 : }
3404 : default:;
3405 : }
3406 :
3407 0 : return m.fail(coercionNode, "must be of the form +x, x|0, fround(x), or a SIMD check(x)");
3408 : }
3409 :
3410 : static bool
3411 0 : CheckGlobalVariableInitImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode,
3412 : bool isConst)
3413 : {
3414 0 : Type coerceTo;
3415 : ParseNode* coercedExpr;
3416 0 : if (!CheckTypeAnnotation(m, initNode, &coerceTo, &coercedExpr))
3417 0 : return false;
3418 :
3419 0 : if (!coercedExpr->isKind(PNK_DOT))
3420 0 : return m.failName(coercedExpr, "invalid import expression for global '%s'", varName);
3421 :
3422 0 : if (!coerceTo.isGlobalVarType())
3423 0 : return m.fail(initNode, "global variable type not allowed");
3424 :
3425 0 : ParseNode* base = DotBase(coercedExpr);
3426 0 : PropertyName* field = DotMember(coercedExpr);
3427 :
3428 0 : PropertyName* importName = m.importArgumentName();
3429 0 : if (!importName)
3430 0 : return m.fail(coercedExpr, "cannot import without an asm.js foreign parameter");
3431 0 : if (!IsUseOfName(base, importName))
3432 0 : return m.failName(coercedExpr, "base of import expression must be '%s'", importName);
3433 :
3434 0 : return m.addGlobalVarImport(varName, field, coerceTo, isConst);
3435 : }
3436 :
3437 : static bool
3438 0 : IsArrayViewCtorName(ModuleValidator& m, PropertyName* name, Scalar::Type* type)
3439 : {
3440 0 : JSAtomState& names = m.cx()->names();
3441 0 : if (name == names.Int8Array) {
3442 0 : *type = Scalar::Int8;
3443 0 : } else if (name == names.Uint8Array) {
3444 0 : *type = Scalar::Uint8;
3445 0 : } else if (name == names.Int16Array) {
3446 0 : *type = Scalar::Int16;
3447 0 : } else if (name == names.Uint16Array) {
3448 0 : *type = Scalar::Uint16;
3449 0 : } else if (name == names.Int32Array) {
3450 0 : *type = Scalar::Int32;
3451 0 : } else if (name == names.Uint32Array) {
3452 0 : *type = Scalar::Uint32;
3453 0 : } else if (name == names.Float32Array) {
3454 0 : *type = Scalar::Float32;
3455 0 : } else if (name == names.Float64Array) {
3456 0 : *type = Scalar::Float64;
3457 : } else {
3458 0 : return false;
3459 : }
3460 0 : return true;
3461 : }
3462 :
3463 : static bool
3464 0 : CheckNewArrayViewArgs(ModuleValidator& m, ParseNode* ctorExpr, PropertyName* bufferName)
3465 : {
3466 0 : ParseNode* bufArg = NextNode(ctorExpr);
3467 0 : if (!bufArg || NextNode(bufArg) != nullptr)
3468 0 : return m.fail(ctorExpr, "array view constructor takes exactly one argument");
3469 :
3470 0 : if (!IsUseOfName(bufArg, bufferName))
3471 0 : return m.failName(bufArg, "argument to array view constructor must be '%s'", bufferName);
3472 :
3473 0 : return true;
3474 : }
3475 :
3476 : static bool
3477 0 : CheckNewArrayView(ModuleValidator& m, PropertyName* varName, ParseNode* newExpr)
3478 : {
3479 0 : PropertyName* globalName = m.globalArgumentName();
3480 0 : if (!globalName)
3481 0 : return m.fail(newExpr, "cannot create array view without an asm.js global parameter");
3482 :
3483 0 : PropertyName* bufferName = m.bufferArgumentName();
3484 0 : if (!bufferName)
3485 0 : return m.fail(newExpr, "cannot create array view without an asm.js heap parameter");
3486 :
3487 0 : ParseNode* ctorExpr = ListHead(newExpr);
3488 :
3489 : PropertyName* field;
3490 : Scalar::Type type;
3491 0 : if (ctorExpr->isKind(PNK_DOT)) {
3492 0 : ParseNode* base = DotBase(ctorExpr);
3493 :
3494 0 : if (!IsUseOfName(base, globalName))
3495 0 : return m.failName(base, "expecting '%s.*Array", globalName);
3496 :
3497 0 : field = DotMember(ctorExpr);
3498 0 : if (!IsArrayViewCtorName(m, field, &type))
3499 0 : return m.fail(ctorExpr, "could not match typed array name");
3500 : } else {
3501 0 : if (!ctorExpr->isKind(PNK_NAME))
3502 0 : return m.fail(ctorExpr, "expecting name of imported array view constructor");
3503 :
3504 0 : PropertyName* globalName = ctorExpr->name();
3505 0 : const ModuleValidator::Global* global = m.lookupGlobal(globalName);
3506 0 : if (!global)
3507 0 : return m.failName(ctorExpr, "%s not found in module global scope", globalName);
3508 :
3509 0 : if (global->which() != ModuleValidator::Global::ArrayViewCtor)
3510 0 : return m.failName(ctorExpr, "%s must be an imported array view constructor", globalName);
3511 :
3512 0 : field = nullptr;
3513 0 : type = global->viewType();
3514 : }
3515 :
3516 0 : if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName))
3517 0 : return false;
3518 :
3519 0 : return m.addArrayView(varName, type, field);
3520 : }
3521 :
3522 : static bool
3523 0 : IsSimdValidOperationType(SimdType type, SimdOperation op)
3524 : {
3525 : #define CASE(op) case SimdOperation::Fn_##op:
3526 0 : switch(type) {
3527 : case SimdType::Int8x16:
3528 0 : switch (op) {
3529 : case SimdOperation::Constructor:
3530 : case SimdOperation::Fn_fromUint8x16Bits:
3531 : case SimdOperation::Fn_fromUint16x8Bits:
3532 : case SimdOperation::Fn_fromUint32x4Bits:
3533 0 : FORALL_INT8X16_ASMJS_OP(CASE) return true;
3534 0 : default: return false;
3535 : }
3536 : break;
3537 : case SimdType::Int16x8:
3538 0 : switch (op) {
3539 : case SimdOperation::Constructor:
3540 : case SimdOperation::Fn_fromUint8x16Bits:
3541 : case SimdOperation::Fn_fromUint16x8Bits:
3542 : case SimdOperation::Fn_fromUint32x4Bits:
3543 0 : FORALL_INT16X8_ASMJS_OP(CASE) return true;
3544 0 : default: return false;
3545 : }
3546 : break;
3547 : case SimdType::Int32x4:
3548 0 : switch (op) {
3549 : case SimdOperation::Constructor:
3550 : case SimdOperation::Fn_fromUint8x16Bits:
3551 : case SimdOperation::Fn_fromUint16x8Bits:
3552 : case SimdOperation::Fn_fromUint32x4Bits:
3553 0 : FORALL_INT32X4_ASMJS_OP(CASE) return true;
3554 0 : default: return false;
3555 : }
3556 : break;
3557 : case SimdType::Uint8x16:
3558 0 : switch (op) {
3559 : case SimdOperation::Constructor:
3560 : case SimdOperation::Fn_fromInt8x16Bits:
3561 : case SimdOperation::Fn_fromUint16x8Bits:
3562 : case SimdOperation::Fn_fromUint32x4Bits:
3563 0 : FORALL_INT8X16_ASMJS_OP(CASE) return true;
3564 0 : default: return false;
3565 : }
3566 : break;
3567 : case SimdType::Uint16x8:
3568 0 : switch (op) {
3569 : case SimdOperation::Constructor:
3570 : case SimdOperation::Fn_fromUint8x16Bits:
3571 : case SimdOperation::Fn_fromInt16x8Bits:
3572 : case SimdOperation::Fn_fromUint32x4Bits:
3573 0 : FORALL_INT16X8_ASMJS_OP(CASE) return true;
3574 0 : default: return false;
3575 : }
3576 : break;
3577 : case SimdType::Uint32x4:
3578 0 : switch (op) {
3579 : case SimdOperation::Constructor:
3580 : case SimdOperation::Fn_fromUint8x16Bits:
3581 : case SimdOperation::Fn_fromUint16x8Bits:
3582 : case SimdOperation::Fn_fromInt32x4Bits:
3583 0 : FORALL_INT32X4_ASMJS_OP(CASE) return true;
3584 0 : default: return false;
3585 : }
3586 : break;
3587 : case SimdType::Float32x4:
3588 0 : switch (op) {
3589 : case SimdOperation::Constructor:
3590 : case SimdOperation::Fn_fromUint8x16Bits:
3591 : case SimdOperation::Fn_fromUint16x8Bits:
3592 : case SimdOperation::Fn_fromUint32x4Bits:
3593 0 : FORALL_FLOAT32X4_ASMJS_OP(CASE) return true;
3594 0 : default: return false;
3595 : }
3596 : break;
3597 : case SimdType::Bool8x16:
3598 : case SimdType::Bool16x8:
3599 : case SimdType::Bool32x4:
3600 0 : switch (op) {
3601 : case SimdOperation::Constructor:
3602 0 : FORALL_BOOL_SIMD_OP(CASE) return true;
3603 0 : default: return false;
3604 : }
3605 : break;
3606 : default:
3607 : // Unimplemented SIMD type.
3608 0 : return false;
3609 : }
3610 : #undef CASE
3611 : }
3612 :
3613 : static bool
3614 0 : CheckGlobalMathImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName,
3615 : PropertyName* field)
3616 : {
3617 : // Math builtin, with the form glob.Math.[[builtin]]
3618 0 : ModuleValidator::MathBuiltin mathBuiltin;
3619 0 : if (!m.lookupStandardLibraryMathName(field, &mathBuiltin))
3620 0 : return m.failName(initNode, "'%s' is not a standard Math builtin", field);
3621 :
3622 0 : switch (mathBuiltin.kind) {
3623 : case ModuleValidator::MathBuiltin::Function:
3624 0 : return m.addMathBuiltinFunction(varName, mathBuiltin.u.func, field);
3625 : case ModuleValidator::MathBuiltin::Constant:
3626 0 : return m.addMathBuiltinConstant(varName, mathBuiltin.u.cst, field);
3627 : default:
3628 0 : break;
3629 : }
3630 0 : MOZ_CRASH("unexpected or uninitialized math builtin type");
3631 : }
3632 :
3633 : static bool
3634 0 : CheckGlobalAtomicsImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName,
3635 : PropertyName* field)
3636 : {
3637 : // Atomics builtin, with the form glob.Atomics.[[builtin]]
3638 : AsmJSAtomicsBuiltinFunction func;
3639 0 : if (!m.lookupStandardLibraryAtomicsName(field, &func))
3640 0 : return m.failName(initNode, "'%s' is not a standard Atomics builtin", field);
3641 :
3642 0 : return m.addAtomicsBuiltinFunction(varName, func, field);
3643 : }
3644 :
3645 : static bool
3646 0 : CheckGlobalSimdImport(ModuleValidator& m, ParseNode* initNode, PropertyName* varName,
3647 : PropertyName* field)
3648 : {
3649 0 : if (!m.supportsSimd())
3650 0 : return m.fail(initNode, "SIMD is not supported on this platform");
3651 :
3652 : // SIMD constructor, with the form glob.SIMD.[[type]]
3653 : SimdType simdType;
3654 0 : if (!IsSimdTypeName(m.cx()->names(), field, &simdType))
3655 0 : return m.failName(initNode, "'%s' is not a standard SIMD type", field);
3656 :
3657 : // IsSimdTypeName will return true for any SIMD type supported by the VM.
3658 : //
3659 : // Since we may not support all of those SIMD types in asm.js, use the
3660 : // asm.js-specific IsSimdValidOperationType() to check if this specific
3661 : // constructor is supported in asm.js.
3662 0 : if (!IsSimdValidOperationType(simdType, SimdOperation::Constructor))
3663 0 : return m.failName(initNode, "'%s' is not a supported SIMD type", field);
3664 :
3665 0 : return m.addSimdCtor(varName, simdType, field);
3666 : }
3667 :
3668 : static bool
3669 0 : CheckGlobalSimdOperationImport(ModuleValidator& m, const ModuleValidator::Global* global,
3670 : ParseNode* initNode, PropertyName* varName, PropertyName* opName)
3671 : {
3672 0 : SimdType simdType = global->simdCtorType();
3673 : SimdOperation simdOp;
3674 0 : if (!m.lookupStandardSimdOpName(opName, &simdOp))
3675 0 : return m.failName(initNode, "'%s' is not a standard SIMD operation", opName);
3676 0 : if (!IsSimdValidOperationType(simdType, simdOp))
3677 0 : return m.failName(initNode, "'%s' is not an operation supported by the SIMD type", opName);
3678 0 : return m.addSimdOperation(varName, simdType, simdOp, opName);
3679 : }
3680 :
3681 : static bool
3682 0 : CheckGlobalDotImport(ModuleValidator& m, PropertyName* varName, ParseNode* initNode)
3683 : {
3684 0 : ParseNode* base = DotBase(initNode);
3685 0 : PropertyName* field = DotMember(initNode);
3686 :
3687 0 : if (base->isKind(PNK_DOT)) {
3688 0 : ParseNode* global = DotBase(base);
3689 0 : PropertyName* mathOrAtomicsOrSimd = DotMember(base);
3690 :
3691 0 : PropertyName* globalName = m.globalArgumentName();
3692 0 : if (!globalName)
3693 0 : return m.fail(base, "import statement requires the module have a stdlib parameter");
3694 :
3695 0 : if (!IsUseOfName(global, globalName)) {
3696 0 : if (global->isKind(PNK_DOT)) {
3697 : return m.failName(base, "imports can have at most two dot accesses "
3698 0 : "(e.g. %s.Math.sin)", globalName);
3699 : }
3700 0 : return m.failName(base, "expecting %s.*", globalName);
3701 : }
3702 :
3703 0 : if (mathOrAtomicsOrSimd == m.cx()->names().Math)
3704 0 : return CheckGlobalMathImport(m, initNode, varName, field);
3705 0 : if (mathOrAtomicsOrSimd == m.cx()->names().Atomics)
3706 0 : return CheckGlobalAtomicsImport(m, initNode, varName, field);
3707 0 : if (mathOrAtomicsOrSimd == m.cx()->names().SIMD)
3708 0 : return CheckGlobalSimdImport(m, initNode, varName, field);
3709 0 : return m.failName(base, "expecting %s.{Math|SIMD}", globalName);
3710 : }
3711 :
3712 0 : if (!base->isKind(PNK_NAME))
3713 0 : return m.fail(base, "expected name of variable or parameter");
3714 :
3715 0 : if (base->name() == m.globalArgumentName()) {
3716 0 : if (field == m.cx()->names().NaN)
3717 0 : return m.addGlobalConstant(varName, GenericNaN(), field);
3718 0 : if (field == m.cx()->names().Infinity)
3719 0 : return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
3720 :
3721 : Scalar::Type type;
3722 0 : if (IsArrayViewCtorName(m, field, &type))
3723 0 : return m.addArrayViewCtor(varName, type, field);
3724 :
3725 0 : return m.failName(initNode, "'%s' is not a standard constant or typed array name", field);
3726 : }
3727 :
3728 0 : if (base->name() == m.importArgumentName())
3729 0 : return m.addFFI(varName, field);
3730 :
3731 0 : const ModuleValidator::Global* global = m.lookupGlobal(base->name());
3732 0 : if (!global)
3733 0 : return m.failName(initNode, "%s not found in module global scope", base->name());
3734 :
3735 0 : if (!global->isSimdCtor())
3736 0 : return m.failName(base, "expecting SIMD constructor name, got %s", field);
3737 :
3738 0 : return CheckGlobalSimdOperationImport(m, global, initNode, varName, field);
3739 : }
3740 :
3741 : static bool
3742 0 : CheckModuleGlobal(ModuleValidator& m, ParseNode* var, bool isConst)
3743 : {
3744 0 : if (!var->isKind(PNK_NAME))
3745 0 : return m.fail(var, "import variable is not a plain name");
3746 :
3747 0 : if (!CheckModuleLevelName(m, var, var->name()))
3748 0 : return false;
3749 :
3750 0 : ParseNode* initNode = MaybeInitializer(var);
3751 0 : if (!initNode)
3752 0 : return m.fail(var, "module import needs initializer");
3753 :
3754 0 : if (IsNumericLiteral(m, initNode))
3755 0 : return CheckGlobalVariableInitConstant(m, var->name(), initNode, isConst);
3756 :
3757 0 : if (initNode->isKind(PNK_BITOR) || initNode->isKind(PNK_POS) || initNode->isKind(PNK_CALL))
3758 0 : return CheckGlobalVariableInitImport(m, var->name(), initNode, isConst);
3759 :
3760 0 : if (initNode->isKind(PNK_NEW))
3761 0 : return CheckNewArrayView(m, var->name(), initNode);
3762 :
3763 0 : if (initNode->isKind(PNK_DOT))
3764 0 : return CheckGlobalDotImport(m, var->name(), initNode);
3765 :
3766 0 : return m.fail(initNode, "unsupported import expression");
3767 : }
3768 :
3769 : static bool
3770 0 : CheckModuleProcessingDirectives(ModuleValidator& m)
3771 : {
3772 0 : TokenStream& ts = m.parser().tokenStream;
3773 : while (true) {
3774 : bool matched;
3775 0 : if (!ts.matchToken(&matched, TOK_STRING, TokenStream::Operand))
3776 0 : return false;
3777 0 : if (!matched)
3778 0 : return true;
3779 :
3780 0 : if (!IsIgnoredDirectiveName(m.cx(), ts.currentToken().atom()))
3781 0 : return m.failCurrentOffset("unsupported processing directive");
3782 :
3783 : TokenKind tt;
3784 0 : if (!ts.getToken(&tt))
3785 0 : return false;
3786 0 : if (tt != TOK_SEMI)
3787 0 : return m.failCurrentOffset("expected semicolon after string literal");
3788 0 : }
3789 : }
3790 :
3791 : static bool
3792 0 : CheckModuleGlobals(ModuleValidator& m)
3793 : {
3794 : while (true) {
3795 : ParseNode* varStmt;
3796 0 : if (!ParseVarOrConstStatement(m.parser(), &varStmt))
3797 0 : return false;
3798 0 : if (!varStmt)
3799 0 : break;
3800 0 : for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
3801 0 : if (!CheckModuleGlobal(m, var, varStmt->isKind(PNK_CONST)))
3802 0 : return false;
3803 : }
3804 0 : }
3805 :
3806 0 : return true;
3807 : }
3808 :
3809 : static bool
3810 0 : ArgFail(FunctionValidator& f, PropertyName* argName, ParseNode* stmt)
3811 : {
3812 : return f.failName(stmt, "expecting argument type declaration for '%s' of the "
3813 0 : "form 'arg = arg|0' or 'arg = +arg' or 'arg = fround(arg)'", argName);
3814 : }
3815 :
3816 : static bool
3817 0 : CheckArgumentType(FunctionValidator& f, ParseNode* stmt, PropertyName* name, Type* type)
3818 : {
3819 0 : if (!stmt || !IsExpressionStatement(stmt))
3820 0 : return ArgFail(f, name, stmt ? stmt : f.fn());
3821 :
3822 0 : ParseNode* initNode = ExpressionStatementExpr(stmt);
3823 0 : if (!initNode || !initNode->isKind(PNK_ASSIGN))
3824 0 : return ArgFail(f, name, stmt);
3825 :
3826 0 : ParseNode* argNode = BinaryLeft(initNode);
3827 0 : ParseNode* coercionNode = BinaryRight(initNode);
3828 :
3829 0 : if (!IsUseOfName(argNode, name))
3830 0 : return ArgFail(f, name, stmt);
3831 :
3832 : ParseNode* coercedExpr;
3833 0 : if (!CheckTypeAnnotation(f.m(), coercionNode, type, &coercedExpr))
3834 0 : return false;
3835 :
3836 0 : if (!type->isArgType())
3837 0 : return f.failName(stmt, "invalid type for argument '%s'", name);
3838 :
3839 0 : if (!IsUseOfName(coercedExpr, name))
3840 0 : return ArgFail(f, name, stmt);
3841 :
3842 0 : return true;
3843 : }
3844 :
3845 : static bool
3846 0 : CheckProcessingDirectives(ModuleValidator& m, ParseNode** stmtIter)
3847 : {
3848 0 : ParseNode* stmt = *stmtIter;
3849 :
3850 0 : while (stmt && IsIgnoredDirective(m.cx(), stmt))
3851 0 : stmt = NextNode(stmt);
3852 :
3853 0 : *stmtIter = stmt;
3854 0 : return true;
3855 : }
3856 :
3857 : static bool
3858 0 : CheckArguments(FunctionValidator& f, ParseNode** stmtIter, ValTypeVector* argTypes)
3859 : {
3860 0 : ParseNode* stmt = *stmtIter;
3861 :
3862 : unsigned numFormals;
3863 0 : ParseNode* argpn = FunctionFormalParametersList(f.fn(), &numFormals);
3864 :
3865 0 : for (unsigned i = 0; i < numFormals; i++, argpn = NextNode(argpn), stmt = NextNode(stmt)) {
3866 : PropertyName* name;
3867 0 : if (!CheckArgument(f.m(), argpn, &name))
3868 0 : return false;
3869 :
3870 0 : Type type;
3871 0 : if (!CheckArgumentType(f, stmt, name, &type))
3872 0 : return false;
3873 :
3874 0 : if (!argTypes->append(type.canonicalToValType()))
3875 0 : return false;
3876 :
3877 0 : if (!f.addLocal(argpn, name, type))
3878 0 : return false;
3879 : }
3880 :
3881 0 : *stmtIter = stmt;
3882 0 : return true;
3883 : }
3884 :
3885 : static bool
3886 0 : IsLiteralOrConst(FunctionValidator& f, ParseNode* pn, NumLit* lit)
3887 : {
3888 0 : if (pn->isKind(PNK_NAME)) {
3889 0 : const ModuleValidator::Global* global = f.lookupGlobal(pn->name());
3890 0 : if (!global || global->which() != ModuleValidator::Global::ConstantLiteral)
3891 0 : return false;
3892 :
3893 0 : *lit = global->constLiteralValue();
3894 0 : return true;
3895 : }
3896 :
3897 0 : bool isSimd = false;
3898 0 : if (!IsNumericLiteral(f.m(), pn, &isSimd))
3899 0 : return false;
3900 :
3901 0 : if (isSimd)
3902 0 : f.setUsesSimd();
3903 :
3904 0 : *lit = ExtractNumericLiteral(f.m(), pn);
3905 0 : return true;
3906 : }
3907 :
3908 : static bool
3909 0 : CheckFinalReturn(FunctionValidator& f, ParseNode* lastNonEmptyStmt)
3910 : {
3911 0 : if (!f.encoder().writeOp(Op::End))
3912 0 : return false;
3913 :
3914 0 : if (!f.hasAlreadyReturned()) {
3915 0 : f.setReturnedType(ExprType::Void);
3916 0 : return true;
3917 : }
3918 :
3919 0 : if (!lastNonEmptyStmt->isKind(PNK_RETURN) && !IsVoid(f.returnedType()))
3920 0 : return f.fail(lastNonEmptyStmt, "void incompatible with previous return type");
3921 :
3922 0 : return true;
3923 : }
3924 :
3925 : static bool
3926 0 : CheckVariable(FunctionValidator& f, ParseNode* var, ValTypeVector* types, Vector<NumLit>* inits)
3927 : {
3928 0 : if (!var->isKind(PNK_NAME))
3929 0 : return f.fail(var, "local variable is not a plain name");
3930 :
3931 0 : PropertyName* name = var->name();
3932 :
3933 0 : if (!CheckIdentifier(f.m(), var, name))
3934 0 : return false;
3935 :
3936 0 : ParseNode* initNode = MaybeInitializer(var);
3937 0 : if (!initNode)
3938 0 : return f.failName(var, "var '%s' needs explicit type declaration via an initial value", name);
3939 :
3940 0 : NumLit lit;
3941 0 : if (!IsLiteralOrConst(f, initNode, &lit))
3942 0 : return f.failName(var, "var '%s' initializer must be literal or const literal", name);
3943 :
3944 0 : if (!lit.valid())
3945 0 : return f.failName(var, "var '%s' initializer out of range", name);
3946 :
3947 0 : Type type = Type::canonicalize(Type::lit(lit));
3948 :
3949 0 : return f.addLocal(var, name, type) &&
3950 0 : types->append(type.canonicalToValType()) &&
3951 0 : inits->append(lit);
3952 : }
3953 :
3954 : static bool
3955 0 : CheckVariables(FunctionValidator& f, ParseNode** stmtIter)
3956 : {
3957 0 : ParseNode* stmt = *stmtIter;
3958 :
3959 0 : uint32_t firstVar = f.numLocals();
3960 :
3961 0 : ValTypeVector types;
3962 0 : Vector<NumLit> inits(f.cx());
3963 :
3964 0 : for (; stmt && stmt->isKind(PNK_VAR); stmt = NextNonEmptyStatement(stmt)) {
3965 0 : for (ParseNode* var = VarListHead(stmt); var; var = NextNode(var)) {
3966 0 : if (!CheckVariable(f, var, &types, &inits))
3967 0 : return false;
3968 : }
3969 : }
3970 :
3971 0 : MOZ_ASSERT(f.encoder().empty());
3972 :
3973 0 : if (!EncodeLocalEntries(f.encoder(), types))
3974 0 : return false;
3975 :
3976 0 : for (uint32_t i = 0; i < inits.length(); i++) {
3977 0 : NumLit lit = inits[i];
3978 0 : if (lit.isZeroBits())
3979 0 : continue;
3980 0 : if (!f.writeConstExpr(lit))
3981 0 : return false;
3982 0 : if (!f.encoder().writeOp(Op::SetLocal))
3983 0 : return false;
3984 0 : if (!f.encoder().writeVarU32(firstVar + i))
3985 0 : return false;
3986 : }
3987 :
3988 0 : *stmtIter = stmt;
3989 0 : return true;
3990 : }
3991 :
3992 : static bool
3993 : CheckExpr(FunctionValidator& f, ParseNode* op, Type* type);
3994 :
3995 : static bool
3996 0 : CheckNumericLiteral(FunctionValidator& f, ParseNode* num, Type* type)
3997 : {
3998 0 : NumLit lit = ExtractNumericLiteral(f.m(), num);
3999 0 : if (!lit.valid())
4000 0 : return f.fail(num, "numeric literal out of representable integer range");
4001 0 : *type = Type::lit(lit);
4002 0 : return f.writeConstExpr(lit);
4003 : }
4004 :
4005 : static bool
4006 0 : CheckVarRef(FunctionValidator& f, ParseNode* varRef, Type* type)
4007 : {
4008 0 : PropertyName* name = varRef->name();
4009 :
4010 0 : if (const FunctionValidator::Local* local = f.lookupLocal(name)) {
4011 0 : if (!f.encoder().writeOp(Op::GetLocal))
4012 0 : return false;
4013 0 : if (!f.encoder().writeVarU32(local->slot))
4014 0 : return false;
4015 0 : *type = local->type;
4016 0 : return true;
4017 : }
4018 :
4019 0 : if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
4020 0 : switch (global->which()) {
4021 : case ModuleValidator::Global::ConstantLiteral:
4022 0 : *type = global->varOrConstType();
4023 0 : return f.writeConstExpr(global->constLiteralValue());
4024 : case ModuleValidator::Global::ConstantImport:
4025 : case ModuleValidator::Global::Variable: {
4026 0 : *type = global->varOrConstType();
4027 0 : return f.encoder().writeOp(Op::GetGlobal) &&
4028 0 : f.encoder().writeVarU32(global->varOrConstIndex());
4029 : }
4030 : case ModuleValidator::Global::Function:
4031 : case ModuleValidator::Global::FFI:
4032 : case ModuleValidator::Global::MathBuiltinFunction:
4033 : case ModuleValidator::Global::AtomicsBuiltinFunction:
4034 : case ModuleValidator::Global::FuncPtrTable:
4035 : case ModuleValidator::Global::ArrayView:
4036 : case ModuleValidator::Global::ArrayViewCtor:
4037 : case ModuleValidator::Global::SimdCtor:
4038 : case ModuleValidator::Global::SimdOp:
4039 0 : break;
4040 : }
4041 0 : return f.failName(varRef, "'%s' may not be accessed by ordinary expressions", name);
4042 : }
4043 :
4044 0 : return f.failName(varRef, "'%s' not found in local or asm.js module scope", name);
4045 : }
4046 :
4047 : static inline bool
4048 0 : IsLiteralOrConstInt(FunctionValidator& f, ParseNode* pn, uint32_t* u32)
4049 : {
4050 0 : NumLit lit;
4051 0 : if (!IsLiteralOrConst(f, pn, &lit))
4052 0 : return false;
4053 :
4054 0 : return IsLiteralInt(lit, u32);
4055 : }
4056 :
4057 : static const int32_t NoMask = -1;
4058 : static const bool YesSimd = true;
4059 : static const bool NoSimd = false;
4060 :
4061 : static bool
4062 0 : CheckArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
4063 : bool isSimd, Scalar::Type* viewType)
4064 : {
4065 0 : if (!viewName->isKind(PNK_NAME))
4066 0 : return f.fail(viewName, "base of array access must be a typed array view name");
4067 :
4068 0 : const ModuleValidator::Global* global = f.lookupGlobal(viewName->name());
4069 0 : if (!global || !global->isAnyArrayView())
4070 0 : return f.fail(viewName, "base of array access must be a typed array view name");
4071 :
4072 0 : *viewType = global->viewType();
4073 :
4074 : uint32_t index;
4075 0 : if (IsLiteralOrConstInt(f, indexExpr, &index)) {
4076 0 : uint64_t byteOffset = uint64_t(index) << TypedArrayShift(*viewType);
4077 0 : uint64_t width = isSimd ? Simd128DataSize : TypedArrayElemSize(*viewType);
4078 0 : if (!f.m().tryConstantAccess(byteOffset, width))
4079 0 : return f.fail(indexExpr, "constant index out of range");
4080 :
4081 0 : return f.writeInt32Lit(byteOffset);
4082 : }
4083 :
4084 : // Mask off the low bits to account for the clearing effect of a right shift
4085 : // followed by the left shift implicit in the array access. E.g., H32[i>>2]
4086 : // loses the low two bits.
4087 0 : int32_t mask = ~(TypedArrayElemSize(*viewType) - 1);
4088 :
4089 0 : if (indexExpr->isKind(PNK_RSH)) {
4090 0 : ParseNode* shiftAmountNode = BitwiseRight(indexExpr);
4091 :
4092 : uint32_t shift;
4093 0 : if (!IsLiteralInt(f.m(), shiftAmountNode, &shift))
4094 0 : return f.failf(shiftAmountNode, "shift amount must be constant");
4095 :
4096 0 : unsigned requiredShift = TypedArrayShift(*viewType);
4097 0 : if (shift != requiredShift)
4098 0 : return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift);
4099 :
4100 0 : ParseNode* pointerNode = BitwiseLeft(indexExpr);
4101 :
4102 0 : Type pointerType;
4103 0 : if (!CheckExpr(f, pointerNode, &pointerType))
4104 0 : return false;
4105 :
4106 0 : if (!pointerType.isIntish())
4107 0 : return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
4108 : } else {
4109 : // For SIMD access, and legacy scalar access compatibility, accept
4110 : // Int8/Uint8 accesses with no shift.
4111 0 : if (TypedArrayShift(*viewType) != 0)
4112 0 : return f.fail(indexExpr, "index expression isn't shifted; must be an Int8/Uint8 access");
4113 :
4114 0 : MOZ_ASSERT(mask == NoMask);
4115 :
4116 0 : ParseNode* pointerNode = indexExpr;
4117 :
4118 0 : Type pointerType;
4119 0 : if (!CheckExpr(f, pointerNode, &pointerType))
4120 0 : return false;
4121 :
4122 0 : if (isSimd) {
4123 0 : if (!pointerType.isIntish())
4124 0 : return f.failf(pointerNode, "%s is not a subtype of intish", pointerType.toChars());
4125 : } else {
4126 0 : if (!pointerType.isInt())
4127 0 : return f.failf(pointerNode, "%s is not a subtype of int", pointerType.toChars());
4128 : }
4129 : }
4130 :
4131 : // Don't generate the mask op if there is no need for it which could happen for
4132 : // a shift of zero or a SIMD access.
4133 0 : if (mask != NoMask) {
4134 0 : return f.writeInt32Lit(mask) &&
4135 0 : f.encoder().writeOp(Op::I32And);
4136 : }
4137 :
4138 0 : return true;
4139 : }
4140 :
4141 : static bool
4142 0 : CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
4143 : bool isSimd, Scalar::Type* viewType)
4144 : {
4145 0 : return CheckArrayAccess(f, viewName, indexExpr, isSimd, viewType);
4146 : }
4147 :
4148 : static bool
4149 0 : WriteArrayAccessFlags(FunctionValidator& f, Scalar::Type viewType)
4150 : {
4151 : // asm.js only has naturally-aligned accesses.
4152 0 : size_t align = TypedArrayElemSize(viewType);
4153 0 : MOZ_ASSERT(IsPowerOfTwo(align));
4154 0 : if (!f.encoder().writeFixedU8(CeilingLog2(align)))
4155 0 : return false;
4156 :
4157 : // asm.js doesn't have constant offsets, so just encode a 0.
4158 0 : if (!f.encoder().writeVarU32(0))
4159 0 : return false;
4160 :
4161 0 : return true;
4162 : }
4163 :
4164 : static bool
4165 0 : CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type)
4166 : {
4167 : Scalar::Type viewType;
4168 :
4169 0 : if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), NoSimd, &viewType))
4170 0 : return false;
4171 :
4172 0 : switch (viewType) {
4173 0 : case Scalar::Int8: if (!f.encoder().writeOp(Op::I32Load8S)) return false; break;
4174 0 : case Scalar::Uint8: if (!f.encoder().writeOp(Op::I32Load8U)) return false; break;
4175 0 : case Scalar::Int16: if (!f.encoder().writeOp(Op::I32Load16S)) return false; break;
4176 0 : case Scalar::Uint16: if (!f.encoder().writeOp(Op::I32Load16U)) return false; break;
4177 : case Scalar::Uint32:
4178 0 : case Scalar::Int32: if (!f.encoder().writeOp(Op::I32Load)) return false; break;
4179 0 : case Scalar::Float32: if (!f.encoder().writeOp(Op::F32Load)) return false; break;
4180 0 : case Scalar::Float64: if (!f.encoder().writeOp(Op::F64Load)) return false; break;
4181 0 : default: MOZ_CRASH("unexpected scalar type");
4182 : }
4183 :
4184 0 : switch (viewType) {
4185 : case Scalar::Int8:
4186 : case Scalar::Int16:
4187 : case Scalar::Int32:
4188 : case Scalar::Uint8:
4189 : case Scalar::Uint16:
4190 : case Scalar::Uint32:
4191 0 : *type = Type::Intish;
4192 0 : break;
4193 : case Scalar::Float32:
4194 0 : *type = Type::MaybeFloat;
4195 0 : break;
4196 : case Scalar::Float64:
4197 0 : *type = Type::MaybeDouble;
4198 0 : break;
4199 0 : default: MOZ_CRASH("Unexpected array type");
4200 : }
4201 :
4202 0 : if (!WriteArrayAccessFlags(f, viewType))
4203 0 : return false;
4204 :
4205 0 : return true;
4206 : }
4207 :
4208 : static bool
4209 0 : CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type)
4210 : {
4211 : Scalar::Type viewType;
4212 0 : if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), NoSimd, &viewType))
4213 0 : return false;
4214 :
4215 0 : Type rhsType;
4216 0 : if (!CheckExpr(f, rhs, &rhsType))
4217 0 : return false;
4218 :
4219 0 : switch (viewType) {
4220 : case Scalar::Int8:
4221 : case Scalar::Int16:
4222 : case Scalar::Int32:
4223 : case Scalar::Uint8:
4224 : case Scalar::Uint16:
4225 : case Scalar::Uint32:
4226 0 : if (!rhsType.isIntish())
4227 0 : return f.failf(lhs, "%s is not a subtype of intish", rhsType.toChars());
4228 0 : break;
4229 : case Scalar::Float32:
4230 0 : if (!rhsType.isMaybeDouble() && !rhsType.isFloatish())
4231 0 : return f.failf(lhs, "%s is not a subtype of double? or floatish", rhsType.toChars());
4232 0 : break;
4233 : case Scalar::Float64:
4234 0 : if (!rhsType.isMaybeFloat() && !rhsType.isMaybeDouble())
4235 0 : return f.failf(lhs, "%s is not a subtype of float? or double?", rhsType.toChars());
4236 0 : break;
4237 : default:
4238 0 : MOZ_CRASH("Unexpected view type");
4239 : }
4240 :
4241 0 : switch (viewType) {
4242 : case Scalar::Int8:
4243 : case Scalar::Uint8:
4244 0 : if (!f.encoder().writeOp(MozOp::I32TeeStore8))
4245 0 : return false;
4246 0 : break;
4247 : case Scalar::Int16:
4248 : case Scalar::Uint16:
4249 0 : if (!f.encoder().writeOp(MozOp::I32TeeStore16))
4250 0 : return false;
4251 0 : break;
4252 : case Scalar::Int32:
4253 : case Scalar::Uint32:
4254 0 : if (!f.encoder().writeOp(MozOp::I32TeeStore))
4255 0 : return false;
4256 0 : break;
4257 : case Scalar::Float32:
4258 0 : if (rhsType.isFloatish()) {
4259 0 : if (!f.encoder().writeOp(MozOp::F32TeeStore))
4260 0 : return false;
4261 : } else {
4262 0 : if (!f.encoder().writeOp(MozOp::F64TeeStoreF32))
4263 0 : return false;
4264 : }
4265 0 : break;
4266 : case Scalar::Float64:
4267 0 : if (rhsType.isFloatish()) {
4268 0 : if (!f.encoder().writeOp(MozOp::F32TeeStoreF64))
4269 0 : return false;
4270 : } else {
4271 0 : if (!f.encoder().writeOp(MozOp::F64TeeStore))
4272 0 : return false;
4273 : }
4274 0 : break;
4275 0 : default: MOZ_CRASH("unexpected scalar type");
4276 : }
4277 :
4278 0 : if (!WriteArrayAccessFlags(f, viewType))
4279 0 : return false;
4280 :
4281 0 : *type = rhsType;
4282 0 : return true;
4283 : }
4284 :
4285 : static bool
4286 0 : CheckAssignName(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type)
4287 : {
4288 0 : RootedPropertyName name(f.cx(), lhs->name());
4289 :
4290 0 : if (const FunctionValidator::Local* lhsVar = f.lookupLocal(name)) {
4291 0 : Type rhsType;
4292 0 : if (!CheckExpr(f, rhs, &rhsType))
4293 0 : return false;
4294 :
4295 0 : if (!f.encoder().writeOp(Op::TeeLocal))
4296 0 : return false;
4297 0 : if (!f.encoder().writeVarU32(lhsVar->slot))
4298 0 : return false;
4299 :
4300 0 : if (!(rhsType <= lhsVar->type)) {
4301 0 : return f.failf(lhs, "%s is not a subtype of %s",
4302 0 : rhsType.toChars(), lhsVar->type.toChars());
4303 : }
4304 0 : *type = rhsType;
4305 0 : return true;
4306 : }
4307 :
4308 0 : if (const ModuleValidator::Global* global = f.lookupGlobal(name)) {
4309 0 : if (global->which() != ModuleValidator::Global::Variable)
4310 0 : return f.failName(lhs, "'%s' is not a mutable variable", name);
4311 :
4312 0 : Type rhsType;
4313 0 : if (!CheckExpr(f, rhs, &rhsType))
4314 0 : return false;
4315 :
4316 0 : Type globType = global->varOrConstType();
4317 0 : if (!(rhsType <= globType))
4318 0 : return f.failf(lhs, "%s is not a subtype of %s", rhsType.toChars(), globType.toChars());
4319 0 : if (!f.encoder().writeOp(MozOp::TeeGlobal))
4320 0 : return false;
4321 0 : if (!f.encoder().writeVarU32(global->varOrConstIndex()))
4322 0 : return false;
4323 :
4324 0 : *type = rhsType;
4325 0 : return true;
4326 : }
4327 :
4328 0 : return f.failName(lhs, "'%s' not found in local or asm.js module scope", name);
4329 : }
4330 :
4331 : static bool
4332 0 : CheckAssign(FunctionValidator& f, ParseNode* assign, Type* type)
4333 : {
4334 0 : MOZ_ASSERT(assign->isKind(PNK_ASSIGN));
4335 :
4336 0 : ParseNode* lhs = BinaryLeft(assign);
4337 0 : ParseNode* rhs = BinaryRight(assign);
4338 :
4339 0 : if (lhs->getKind() == PNK_ELEM)
4340 0 : return CheckStoreArray(f, lhs, rhs, type);
4341 :
4342 0 : if (lhs->getKind() == PNK_NAME)
4343 0 : return CheckAssignName(f, lhs, rhs, type);
4344 :
4345 0 : return f.fail(assign, "left-hand side of assignment must be a variable or array access");
4346 : }
4347 :
4348 : static bool
4349 0 : CheckMathIMul(FunctionValidator& f, ParseNode* call, Type* type)
4350 : {
4351 0 : if (CallArgListLength(call) != 2)
4352 0 : return f.fail(call, "Math.imul must be passed 2 arguments");
4353 :
4354 0 : ParseNode* lhs = CallArgList(call);
4355 0 : ParseNode* rhs = NextNode(lhs);
4356 :
4357 0 : Type lhsType;
4358 0 : if (!CheckExpr(f, lhs, &lhsType))
4359 0 : return false;
4360 :
4361 0 : Type rhsType;
4362 0 : if (!CheckExpr(f, rhs, &rhsType))
4363 0 : return false;
4364 :
4365 0 : if (!lhsType.isIntish())
4366 0 : return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
4367 0 : if (!rhsType.isIntish())
4368 0 : return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
4369 :
4370 0 : *type = Type::Signed;
4371 0 : return f.encoder().writeOp(Op::I32Mul);
4372 : }
4373 :
4374 : static bool
4375 0 : CheckMathClz32(FunctionValidator& f, ParseNode* call, Type* type)
4376 : {
4377 0 : if (CallArgListLength(call) != 1)
4378 0 : return f.fail(call, "Math.clz32 must be passed 1 argument");
4379 :
4380 0 : ParseNode* arg = CallArgList(call);
4381 :
4382 0 : Type argType;
4383 0 : if (!CheckExpr(f, arg, &argType))
4384 0 : return false;
4385 :
4386 0 : if (!argType.isIntish())
4387 0 : return f.failf(arg, "%s is not a subtype of intish", argType.toChars());
4388 :
4389 0 : *type = Type::Fixnum;
4390 0 : return f.encoder().writeOp(Op::I32Clz);
4391 : }
4392 :
4393 : static bool
4394 0 : CheckMathAbs(FunctionValidator& f, ParseNode* call, Type* type)
4395 : {
4396 0 : if (CallArgListLength(call) != 1)
4397 0 : return f.fail(call, "Math.abs must be passed 1 argument");
4398 :
4399 0 : ParseNode* arg = CallArgList(call);
4400 :
4401 0 : Type argType;
4402 0 : if (!CheckExpr(f, arg, &argType))
4403 0 : return false;
4404 :
4405 0 : if (argType.isSigned()) {
4406 0 : *type = Type::Unsigned;
4407 0 : return f.encoder().writeOp(MozOp::I32Abs);
4408 : }
4409 :
4410 0 : if (argType.isMaybeDouble()) {
4411 0 : *type = Type::Double;
4412 0 : return f.encoder().writeOp(Op::F64Abs);
4413 : }
4414 :
4415 0 : if (argType.isMaybeFloat()) {
4416 0 : *type = Type::Floatish;
4417 0 : return f.encoder().writeOp(Op::F32Abs);
4418 : }
4419 :
4420 0 : return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars());
4421 : }
4422 :
4423 : static bool
4424 0 : CheckMathSqrt(FunctionValidator& f, ParseNode* call, Type* type)
4425 : {
4426 0 : if (CallArgListLength(call) != 1)
4427 0 : return f.fail(call, "Math.sqrt must be passed 1 argument");
4428 :
4429 0 : ParseNode* arg = CallArgList(call);
4430 :
4431 0 : Type argType;
4432 0 : if (!CheckExpr(f, arg, &argType))
4433 0 : return false;
4434 :
4435 0 : if (argType.isMaybeDouble()) {
4436 0 : *type = Type::Double;
4437 0 : return f.encoder().writeOp(Op::F64Sqrt);
4438 : }
4439 :
4440 0 : if (argType.isMaybeFloat()) {
4441 0 : *type = Type::Floatish;
4442 0 : return f.encoder().writeOp(Op::F32Sqrt);
4443 : }
4444 :
4445 0 : return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
4446 : }
4447 :
4448 : static bool
4449 0 : CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* type)
4450 : {
4451 0 : if (CallArgListLength(callNode) < 2)
4452 0 : return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
4453 :
4454 0 : ParseNode* firstArg = CallArgList(callNode);
4455 0 : Type firstType;
4456 0 : if (!CheckExpr(f, firstArg, &firstType))
4457 0 : return false;
4458 :
4459 0 : Op op = Op::Limit;
4460 0 : MozOp mozOp = MozOp::Limit;
4461 0 : if (firstType.isMaybeDouble()) {
4462 0 : *type = Type::Double;
4463 0 : firstType = Type::MaybeDouble;
4464 0 : op = isMax ? Op::F64Max : Op::F64Min;
4465 0 : } else if (firstType.isMaybeFloat()) {
4466 0 : *type = Type::Float;
4467 0 : firstType = Type::MaybeFloat;
4468 0 : op = isMax ? Op::F32Max : Op::F32Min;
4469 0 : } else if (firstType.isSigned()) {
4470 0 : *type = Type::Signed;
4471 0 : firstType = Type::Signed;
4472 0 : mozOp = isMax ? MozOp::I32Max : MozOp::I32Min;
4473 : } else {
4474 0 : return f.failf(firstArg, "%s is not a subtype of double?, float? or signed",
4475 0 : firstType.toChars());
4476 : }
4477 :
4478 0 : unsigned numArgs = CallArgListLength(callNode);
4479 0 : ParseNode* nextArg = NextNode(firstArg);
4480 0 : for (unsigned i = 1; i < numArgs; i++, nextArg = NextNode(nextArg)) {
4481 0 : Type nextType;
4482 0 : if (!CheckExpr(f, nextArg, &nextType))
4483 0 : return false;
4484 0 : if (!(nextType <= firstType))
4485 0 : return f.failf(nextArg, "%s is not a subtype of %s", nextType.toChars(), firstType.toChars());
4486 :
4487 0 : if (op != Op::Limit) {
4488 0 : if (!f.encoder().writeOp(op))
4489 0 : return false;
4490 : } else {
4491 0 : if (!f.encoder().writeOp(mozOp))
4492 0 : return false;
4493 : }
4494 : }
4495 :
4496 0 : return true;
4497 : }
4498 :
4499 : static bool
4500 0 : CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
4501 : Scalar::Type* viewType)
4502 : {
4503 0 : if (!CheckAndPrepareArrayAccess(f, viewName, indexExpr, NoSimd, viewType))
4504 0 : return false;
4505 :
4506 : // The global will be sane, CheckArrayAccess checks it.
4507 0 : const ModuleValidator::Global* global = f.lookupGlobal(viewName->name());
4508 0 : if (global->which() != ModuleValidator::Global::ArrayView)
4509 0 : return f.fail(viewName, "base of array access must be a typed array view");
4510 :
4511 0 : MOZ_ASSERT(f.m().atomicsPresent());
4512 :
4513 0 : switch (*viewType) {
4514 : case Scalar::Int8:
4515 : case Scalar::Int16:
4516 : case Scalar::Int32:
4517 : case Scalar::Uint8:
4518 : case Scalar::Uint16:
4519 : case Scalar::Uint32:
4520 0 : return true;
4521 : default:
4522 0 : return f.failf(viewName, "not an integer array");
4523 : }
4524 :
4525 : return true;
4526 : }
4527 :
4528 : static bool
4529 0 : WriteAtomicOperator(FunctionValidator& f, MozOp opcode, Scalar::Type viewType)
4530 : {
4531 0 : return f.encoder().writeOp(opcode) &&
4532 0 : f.encoder().writeFixedU8(viewType);
4533 : }
4534 :
4535 : static bool
4536 0 : CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type)
4537 : {
4538 0 : if (CallArgListLength(call) != 2)
4539 0 : return f.fail(call, "Atomics.load must be passed 2 arguments");
4540 :
4541 0 : ParseNode* arrayArg = CallArgList(call);
4542 0 : ParseNode* indexArg = NextNode(arrayArg);
4543 :
4544 : Scalar::Type viewType;
4545 0 : if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
4546 0 : return false;
4547 :
4548 0 : if (!WriteAtomicOperator(f, MozOp::I32AtomicsLoad, viewType))
4549 0 : return false;
4550 :
4551 0 : if (!WriteArrayAccessFlags(f, viewType))
4552 0 : return false;
4553 :
4554 0 : *type = Type::Int;
4555 0 : return true;
4556 : }
4557 :
4558 : static bool
4559 0 : CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type)
4560 : {
4561 0 : if (CallArgListLength(call) != 3)
4562 0 : return f.fail(call, "Atomics.store must be passed 3 arguments");
4563 :
4564 0 : ParseNode* arrayArg = CallArgList(call);
4565 0 : ParseNode* indexArg = NextNode(arrayArg);
4566 0 : ParseNode* valueArg = NextNode(indexArg);
4567 :
4568 0 : Type rhsType;
4569 0 : if (!CheckExpr(f, valueArg, &rhsType))
4570 0 : return false;
4571 :
4572 0 : if (!rhsType.isIntish())
4573 0 : return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars());
4574 :
4575 : Scalar::Type viewType;
4576 0 : if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
4577 0 : return false;
4578 :
4579 0 : if (!WriteAtomicOperator(f, MozOp::I32AtomicsStore, viewType))
4580 0 : return false;
4581 :
4582 0 : if (!WriteArrayAccessFlags(f, viewType))
4583 0 : return false;
4584 :
4585 0 : *type = rhsType;
4586 0 : return true;
4587 : }
4588 :
4589 : static bool
4590 0 : CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op)
4591 : {
4592 0 : if (CallArgListLength(call) != 3)
4593 0 : return f.fail(call, "Atomics binary operator must be passed 3 arguments");
4594 :
4595 0 : ParseNode* arrayArg = CallArgList(call);
4596 0 : ParseNode* indexArg = NextNode(arrayArg);
4597 0 : ParseNode* valueArg = NextNode(indexArg);
4598 :
4599 0 : Type valueArgType;
4600 0 : if (!CheckExpr(f, valueArg, &valueArgType))
4601 0 : return false;
4602 :
4603 0 : if (!valueArgType.isIntish())
4604 0 : return f.failf(valueArg, "%s is not a subtype of intish", valueArgType.toChars());
4605 :
4606 : Scalar::Type viewType;
4607 0 : if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
4608 0 : return false;
4609 :
4610 0 : if (!WriteAtomicOperator(f, MozOp::I32AtomicsBinOp, viewType))
4611 0 : return false;
4612 0 : if (!f.encoder().writeFixedU8(uint8_t(op)))
4613 0 : return false;
4614 :
4615 0 : if (!WriteArrayAccessFlags(f, viewType))
4616 0 : return false;
4617 :
4618 0 : *type = Type::Int;
4619 0 : return true;
4620 : }
4621 :
4622 : static bool
4623 0 : CheckAtomicsIsLockFree(FunctionValidator& f, ParseNode* call, Type* type)
4624 : {
4625 0 : if (CallArgListLength(call) != 1)
4626 0 : return f.fail(call, "Atomics.isLockFree must be passed 1 argument");
4627 :
4628 0 : ParseNode* sizeArg = CallArgList(call);
4629 :
4630 : uint32_t size;
4631 0 : if (!IsLiteralInt(f.m(), sizeArg, &size))
4632 0 : return f.fail(sizeArg, "Atomics.isLockFree requires an integer literal argument");
4633 :
4634 0 : *type = Type::Int;
4635 0 : return f.writeInt32Lit(AtomicOperations::isLockfree(size));
4636 : }
4637 :
4638 : static bool
4639 0 : CheckAtomicsCompareExchange(FunctionValidator& f, ParseNode* call, Type* type)
4640 : {
4641 0 : if (CallArgListLength(call) != 4)
4642 0 : return f.fail(call, "Atomics.compareExchange must be passed 4 arguments");
4643 :
4644 0 : ParseNode* arrayArg = CallArgList(call);
4645 0 : ParseNode* indexArg = NextNode(arrayArg);
4646 0 : ParseNode* oldValueArg = NextNode(indexArg);
4647 0 : ParseNode* newValueArg = NextNode(oldValueArg);
4648 :
4649 0 : Type oldValueArgType;
4650 0 : if (!CheckExpr(f, oldValueArg, &oldValueArgType))
4651 0 : return false;
4652 :
4653 0 : Type newValueArgType;
4654 0 : if (!CheckExpr(f, newValueArg, &newValueArgType))
4655 0 : return false;
4656 :
4657 0 : if (!oldValueArgType.isIntish())
4658 0 : return f.failf(oldValueArg, "%s is not a subtype of intish", oldValueArgType.toChars());
4659 :
4660 0 : if (!newValueArgType.isIntish())
4661 0 : return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars());
4662 :
4663 : Scalar::Type viewType;
4664 0 : if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
4665 0 : return false;
4666 :
4667 0 : if (!WriteAtomicOperator(f, MozOp::I32AtomicsCompareExchange, viewType))
4668 0 : return false;
4669 :
4670 0 : if (!WriteArrayAccessFlags(f, viewType))
4671 0 : return false;
4672 :
4673 0 : *type = Type::Int;
4674 0 : return true;
4675 : }
4676 :
4677 : static bool
4678 0 : CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type)
4679 : {
4680 0 : if (CallArgListLength(call) != 3)
4681 0 : return f.fail(call, "Atomics.exchange must be passed 3 arguments");
4682 :
4683 0 : ParseNode* arrayArg = CallArgList(call);
4684 0 : ParseNode* indexArg = NextNode(arrayArg);
4685 0 : ParseNode* valueArg = NextNode(indexArg);
4686 :
4687 0 : Type valueArgType;
4688 0 : if (!CheckExpr(f, valueArg, &valueArgType))
4689 0 : return false;
4690 :
4691 0 : if (!valueArgType.isIntish())
4692 0 : return f.failf(arrayArg, "%s is not a subtype of intish", valueArgType.toChars());
4693 :
4694 : Scalar::Type viewType;
4695 0 : if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
4696 0 : return false;
4697 :
4698 0 : if (!WriteAtomicOperator(f, MozOp::I32AtomicsExchange, viewType))
4699 0 : return false;
4700 :
4701 0 : if (!WriteArrayAccessFlags(f, viewType))
4702 0 : return false;
4703 :
4704 0 : *type = Type::Int;
4705 0 : return true;
4706 : }
4707 :
4708 : static bool
4709 0 : CheckAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsBuiltinFunction func,
4710 : Type* type)
4711 : {
4712 0 : f.setUsesAtomics();
4713 :
4714 0 : switch (func) {
4715 : case AsmJSAtomicsBuiltin_compareExchange:
4716 0 : return CheckAtomicsCompareExchange(f, callNode, type);
4717 : case AsmJSAtomicsBuiltin_exchange:
4718 0 : return CheckAtomicsExchange(f, callNode, type);
4719 : case AsmJSAtomicsBuiltin_load:
4720 0 : return CheckAtomicsLoad(f, callNode, type);
4721 : case AsmJSAtomicsBuiltin_store:
4722 0 : return CheckAtomicsStore(f, callNode, type);
4723 : case AsmJSAtomicsBuiltin_add:
4724 0 : return CheckAtomicsBinop(f, callNode, type, AtomicFetchAddOp);
4725 : case AsmJSAtomicsBuiltin_sub:
4726 0 : return CheckAtomicsBinop(f, callNode, type, AtomicFetchSubOp);
4727 : case AsmJSAtomicsBuiltin_and:
4728 0 : return CheckAtomicsBinop(f, callNode, type, AtomicFetchAndOp);
4729 : case AsmJSAtomicsBuiltin_or:
4730 0 : return CheckAtomicsBinop(f, callNode, type, AtomicFetchOrOp);
4731 : case AsmJSAtomicsBuiltin_xor:
4732 0 : return CheckAtomicsBinop(f, callNode, type, AtomicFetchXorOp);
4733 : case AsmJSAtomicsBuiltin_isLockFree:
4734 0 : return CheckAtomicsIsLockFree(f, callNode, type);
4735 : default:
4736 0 : MOZ_CRASH("unexpected atomicsBuiltin function");
4737 : }
4738 : }
4739 :
4740 : typedef bool (*CheckArgType)(FunctionValidator& f, ParseNode* argNode, Type type);
4741 :
4742 : template <CheckArgType checkArg>
4743 : static bool
4744 0 : CheckCallArgs(FunctionValidator& f, ParseNode* callNode, ValTypeVector* args)
4745 : {
4746 0 : ParseNode* argNode = CallArgList(callNode);
4747 0 : for (unsigned i = 0; i < CallArgListLength(callNode); i++, argNode = NextNode(argNode)) {
4748 0 : Type type;
4749 0 : if (!CheckExpr(f, argNode, &type))
4750 0 : return false;
4751 :
4752 0 : if (!checkArg(f, argNode, type))
4753 0 : return false;
4754 :
4755 0 : if (!args->append(Type::canonicalize(type).canonicalToValType()))
4756 0 : return false;
4757 : }
4758 0 : return true;
4759 : }
4760 :
4761 : static bool
4762 0 : CheckSignatureAgainstExisting(ModuleValidator& m, ParseNode* usepn, const Sig& sig, const Sig& existing)
4763 : {
4764 0 : if (sig.args().length() != existing.args().length()) {
4765 0 : return m.failf(usepn, "incompatible number of arguments (%" PRIuSIZE
4766 : " here vs. %" PRIuSIZE " before)",
4767 0 : sig.args().length(), existing.args().length());
4768 : }
4769 :
4770 0 : for (unsigned i = 0; i < sig.args().length(); i++) {
4771 0 : if (sig.arg(i) != existing.arg(i)) {
4772 0 : return m.failf(usepn, "incompatible type for argument %u: (%s here vs. %s before)", i,
4773 0 : ToCString(sig.arg(i)), ToCString(existing.arg(i)));
4774 : }
4775 : }
4776 :
4777 0 : if (sig.ret() != existing.ret()) {
4778 0 : return m.failf(usepn, "%s incompatible with previous return of type %s",
4779 0 : ToCString(sig.ret()), ToCString(existing.ret()));
4780 : }
4781 :
4782 0 : MOZ_ASSERT(sig == existing);
4783 0 : return true;
4784 : }
4785 :
4786 : static bool
4787 0 : CheckFunctionSignature(ModuleValidator& m, ParseNode* usepn, Sig&& sig, PropertyName* name,
4788 : ModuleValidator::Func** func)
4789 : {
4790 0 : ModuleValidator::Func* existing = m.lookupFunction(name);
4791 0 : if (!existing) {
4792 0 : if (!CheckModuleLevelName(m, usepn, name))
4793 0 : return false;
4794 0 : return m.addFunction(name, usepn->pn_pos.begin, Move(sig), func);
4795 : }
4796 :
4797 0 : if (!CheckSignatureAgainstExisting(m, usepn, sig, m.mg().funcSig(existing->index())))
4798 0 : return false;
4799 :
4800 0 : *func = existing;
4801 0 : return true;
4802 : }
4803 :
4804 : static bool
4805 0 : CheckIsArgType(FunctionValidator& f, ParseNode* argNode, Type type)
4806 : {
4807 0 : if (!type.isArgType())
4808 0 : return f.failf(argNode,
4809 : "%s is not a subtype of int, float, double, or an allowed SIMD type",
4810 0 : type.toChars());
4811 :
4812 0 : return true;
4813 : }
4814 :
4815 : static bool
4816 0 : CheckInternalCall(FunctionValidator& f, ParseNode* callNode, PropertyName* calleeName,
4817 : Type ret, Type* type)
4818 : {
4819 0 : MOZ_ASSERT(ret.isCanonical());
4820 :
4821 0 : ValTypeVector args;
4822 0 : if (!CheckCallArgs<CheckIsArgType>(f, callNode, &args))
4823 0 : return false;
4824 :
4825 0 : Sig sig(Move(args), ret.canonicalToExprType());
4826 :
4827 : ModuleValidator::Func* callee;
4828 0 : if (!CheckFunctionSignature(f.m(), callNode, Move(sig), calleeName, &callee))
4829 0 : return false;
4830 :
4831 0 : if (!f.writeCall(callNode, Op::Call))
4832 0 : return false;
4833 :
4834 0 : if (!f.encoder().writeVarU32(callee->index()))
4835 0 : return false;
4836 :
4837 0 : *type = Type::ret(ret);
4838 0 : return true;
4839 : }
4840 :
4841 : static bool
4842 0 : CheckFuncPtrTableAgainstExisting(ModuleValidator& m, ParseNode* usepn, PropertyName* name,
4843 : Sig&& sig, unsigned mask, uint32_t* funcPtrTableIndex)
4844 : {
4845 0 : if (const ModuleValidator::Global* existing = m.lookupGlobal(name)) {
4846 0 : if (existing->which() != ModuleValidator::Global::FuncPtrTable)
4847 0 : return m.failName(usepn, "'%s' is not a function-pointer table", name);
4848 :
4849 0 : ModuleValidator::FuncPtrTable& table = m.funcPtrTable(existing->funcPtrTableIndex());
4850 0 : if (mask != table.mask())
4851 0 : return m.failf(usepn, "mask does not match previous value (%u)", table.mask());
4852 :
4853 0 : if (!CheckSignatureAgainstExisting(m, usepn, sig, m.mg().sig(table.sigIndex())))
4854 0 : return false;
4855 :
4856 0 : *funcPtrTableIndex = existing->funcPtrTableIndex();
4857 0 : return true;
4858 : }
4859 :
4860 0 : if (!CheckModuleLevelName(m, usepn, name))
4861 0 : return false;
4862 :
4863 0 : if (!m.declareFuncPtrTable(Move(sig), name, usepn->pn_pos.begin, mask, funcPtrTableIndex))
4864 0 : return false;
4865 :
4866 0 : return true;
4867 : }
4868 :
4869 : static bool
4870 0 : CheckFuncPtrCall(FunctionValidator& f, ParseNode* callNode, Type ret, Type* type)
4871 : {
4872 0 : MOZ_ASSERT(ret.isCanonical());
4873 :
4874 0 : ParseNode* callee = CallCallee(callNode);
4875 0 : ParseNode* tableNode = ElemBase(callee);
4876 0 : ParseNode* indexExpr = ElemIndex(callee);
4877 :
4878 0 : if (!tableNode->isKind(PNK_NAME))
4879 0 : return f.fail(tableNode, "expecting name of function-pointer array");
4880 :
4881 0 : PropertyName* name = tableNode->name();
4882 0 : if (const ModuleValidator::Global* existing = f.lookupGlobal(name)) {
4883 0 : if (existing->which() != ModuleValidator::Global::FuncPtrTable)
4884 0 : return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name);
4885 : }
4886 :
4887 0 : if (!indexExpr->isKind(PNK_BITAND))
4888 0 : return f.fail(indexExpr, "function-pointer table index expression needs & mask");
4889 :
4890 0 : ParseNode* indexNode = BitwiseLeft(indexExpr);
4891 0 : ParseNode* maskNode = BitwiseRight(indexExpr);
4892 :
4893 : uint32_t mask;
4894 0 : if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
4895 0 : return f.fail(maskNode, "function-pointer table index mask value must be a power of two minus 1");
4896 :
4897 0 : Type indexType;
4898 0 : if (!CheckExpr(f, indexNode, &indexType))
4899 0 : return false;
4900 :
4901 0 : if (!indexType.isIntish())
4902 0 : return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars());
4903 :
4904 0 : ValTypeVector args;
4905 0 : if (!CheckCallArgs<CheckIsArgType>(f, callNode, &args))
4906 0 : return false;
4907 :
4908 0 : Sig sig(Move(args), ret.canonicalToExprType());
4909 :
4910 : uint32_t tableIndex;
4911 0 : if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, Move(sig), mask, &tableIndex))
4912 0 : return false;
4913 :
4914 0 : if (!f.writeCall(callNode, MozOp::OldCallIndirect))
4915 0 : return false;
4916 :
4917 : // Call signature
4918 0 : if (!f.encoder().writeVarU32(f.m().funcPtrTable(tableIndex).sigIndex()))
4919 0 : return false;
4920 :
4921 0 : *type = Type::ret(ret);
4922 0 : return true;
4923 : }
4924 :
4925 : static bool
4926 0 : CheckIsExternType(FunctionValidator& f, ParseNode* argNode, Type type)
4927 : {
4928 0 : if (!type.isExtern())
4929 0 : return f.failf(argNode, "%s is not a subtype of extern", type.toChars());
4930 0 : return true;
4931 : }
4932 :
4933 : static bool
4934 0 : CheckFFICall(FunctionValidator& f, ParseNode* callNode, unsigned ffiIndex, Type ret, Type* type)
4935 : {
4936 0 : MOZ_ASSERT(ret.isCanonical());
4937 :
4938 0 : PropertyName* calleeName = CallCallee(callNode)->name();
4939 :
4940 0 : if (ret.isFloat())
4941 0 : return f.fail(callNode, "FFI calls can't return float");
4942 0 : if (ret.isSimd())
4943 0 : return f.fail(callNode, "FFI calls can't return SIMD values");
4944 :
4945 0 : ValTypeVector args;
4946 0 : if (!CheckCallArgs<CheckIsExternType>(f, callNode, &args))
4947 0 : return false;
4948 :
4949 0 : Sig sig(Move(args), ret.canonicalToExprType());
4950 :
4951 : uint32_t funcIndex;
4952 0 : if (!f.m().declareImport(calleeName, Move(sig), ffiIndex, &funcIndex))
4953 0 : return false;
4954 :
4955 0 : if (!f.writeCall(callNode, Op::Call))
4956 0 : return false;
4957 :
4958 0 : if (!f.encoder().writeVarU32(funcIndex))
4959 0 : return false;
4960 :
4961 0 : *type = Type::ret(ret);
4962 0 : return true;
4963 : }
4964 :
4965 : static bool
4966 0 : CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType)
4967 : {
4968 0 : if (inputType.isMaybeDouble())
4969 0 : return f.encoder().writeOp(Op::F32DemoteF64);
4970 0 : if (inputType.isSigned())
4971 0 : return f.encoder().writeOp(Op::F32ConvertSI32);
4972 0 : if (inputType.isUnsigned())
4973 0 : return f.encoder().writeOp(Op::F32ConvertUI32);
4974 0 : if (inputType.isFloatish())
4975 0 : return true;
4976 :
4977 0 : return f.failf(inputNode, "%s is not a subtype of signed, unsigned, double? or floatish",
4978 0 : inputType.toChars());
4979 : }
4980 :
4981 : static bool
4982 : CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type);
4983 :
4984 : static bool
4985 0 : CheckCoercionArg(FunctionValidator& f, ParseNode* arg, Type expected, Type* type)
4986 : {
4987 0 : MOZ_ASSERT(expected.isCanonicalValType());
4988 :
4989 0 : if (arg->isKind(PNK_CALL))
4990 0 : return CheckCoercedCall(f, arg, expected, type);
4991 :
4992 0 : Type argType;
4993 0 : if (!CheckExpr(f, arg, &argType))
4994 0 : return false;
4995 :
4996 0 : if (expected.isFloat()) {
4997 0 : if (!CheckFloatCoercionArg(f, arg, argType))
4998 0 : return false;
4999 0 : } else if (expected.isSimd()) {
5000 0 : if (!(argType <= expected))
5001 0 : return f.fail(arg, "argument to SIMD coercion isn't from the correct SIMD type");
5002 : } else {
5003 0 : MOZ_CRASH("not call coercions");
5004 : }
5005 :
5006 0 : *type = Type::ret(expected);
5007 0 : return true;
5008 : }
5009 :
5010 : static bool
5011 0 : CheckMathFRound(FunctionValidator& f, ParseNode* callNode, Type* type)
5012 : {
5013 0 : if (CallArgListLength(callNode) != 1)
5014 0 : return f.fail(callNode, "Math.fround must be passed 1 argument");
5015 :
5016 0 : ParseNode* argNode = CallArgList(callNode);
5017 0 : Type argType;
5018 0 : if (!CheckCoercionArg(f, argNode, Type::Float, &argType))
5019 0 : return false;
5020 :
5021 0 : MOZ_ASSERT(argType == Type::Float);
5022 0 : *type = Type::Float;
5023 0 : return true;
5024 : }
5025 :
5026 : static bool
5027 0 : CheckMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func,
5028 : Type* type)
5029 : {
5030 0 : unsigned arity = 0;
5031 0 : Op f32 = Op::Limit;
5032 0 : Op f64 = Op::Limit;
5033 0 : MozOp mozf64 = MozOp::Limit;
5034 0 : switch (func) {
5035 0 : case AsmJSMathBuiltin_imul: return CheckMathIMul(f, callNode, type);
5036 0 : case AsmJSMathBuiltin_clz32: return CheckMathClz32(f, callNode, type);
5037 0 : case AsmJSMathBuiltin_abs: return CheckMathAbs(f, callNode, type);
5038 0 : case AsmJSMathBuiltin_sqrt: return CheckMathSqrt(f, callNode, type);
5039 0 : case AsmJSMathBuiltin_fround: return CheckMathFRound(f, callNode, type);
5040 0 : case AsmJSMathBuiltin_min: return CheckMathMinMax(f, callNode, /* isMax = */ false, type);
5041 0 : case AsmJSMathBuiltin_max: return CheckMathMinMax(f, callNode, /* isMax = */ true, type);
5042 0 : case AsmJSMathBuiltin_ceil: arity = 1; f64 = Op::F64Ceil; f32 = Op::F32Ceil; break;
5043 0 : case AsmJSMathBuiltin_floor: arity = 1; f64 = Op::F64Floor; f32 = Op::F32Floor; break;
5044 0 : case AsmJSMathBuiltin_sin: arity = 1; mozf64 = MozOp::F64Sin; f32 = Op::Unreachable; break;
5045 0 : case AsmJSMathBuiltin_cos: arity = 1; mozf64 = MozOp::F64Cos; f32 = Op::Unreachable; break;
5046 0 : case AsmJSMathBuiltin_tan: arity = 1; mozf64 = MozOp::F64Tan; f32 = Op::Unreachable; break;
5047 0 : case AsmJSMathBuiltin_asin: arity = 1; mozf64 = MozOp::F64Asin; f32 = Op::Unreachable; break;
5048 0 : case AsmJSMathBuiltin_acos: arity = 1; mozf64 = MozOp::F64Acos; f32 = Op::Unreachable; break;
5049 0 : case AsmJSMathBuiltin_atan: arity = 1; mozf64 = MozOp::F64Atan; f32 = Op::Unreachable; break;
5050 0 : case AsmJSMathBuiltin_exp: arity = 1; mozf64 = MozOp::F64Exp; f32 = Op::Unreachable; break;
5051 0 : case AsmJSMathBuiltin_log: arity = 1; mozf64 = MozOp::F64Log; f32 = Op::Unreachable; break;
5052 0 : case AsmJSMathBuiltin_pow: arity = 2; mozf64 = MozOp::F64Pow; f32 = Op::Unreachable; break;
5053 0 : case AsmJSMathBuiltin_atan2: arity = 2; mozf64 = MozOp::F64Atan2; f32 = Op::Unreachable; break;
5054 0 : default: MOZ_CRASH("unexpected mathBuiltin function");
5055 : }
5056 :
5057 0 : unsigned actualArity = CallArgListLength(callNode);
5058 0 : if (actualArity != arity)
5059 0 : return f.failf(callNode, "call passed %u arguments, expected %u", actualArity, arity);
5060 :
5061 0 : if (!f.prepareCall(callNode))
5062 0 : return false;
5063 :
5064 0 : Type firstType;
5065 0 : ParseNode* argNode = CallArgList(callNode);
5066 0 : if (!CheckExpr(f, argNode, &firstType))
5067 0 : return false;
5068 :
5069 0 : if (!firstType.isMaybeFloat() && !firstType.isMaybeDouble())
5070 0 : return f.fail(argNode, "arguments to math call should be a subtype of double? or float?");
5071 :
5072 0 : bool opIsDouble = firstType.isMaybeDouble();
5073 0 : if (!opIsDouble && f32 == Op::Unreachable)
5074 0 : return f.fail(callNode, "math builtin cannot be used as float");
5075 :
5076 0 : if (arity == 2) {
5077 0 : Type secondType;
5078 0 : argNode = NextNode(argNode);
5079 0 : if (!CheckExpr(f, argNode, &secondType))
5080 0 : return false;
5081 :
5082 0 : if (firstType.isMaybeDouble() && !secondType.isMaybeDouble())
5083 0 : return f.fail(argNode, "both arguments to math builtin call should be the same type");
5084 0 : if (firstType.isMaybeFloat() && !secondType.isMaybeFloat())
5085 0 : return f.fail(argNode, "both arguments to math builtin call should be the same type");
5086 : }
5087 :
5088 0 : if (opIsDouble) {
5089 0 : if (f64 != Op::Limit) {
5090 0 : if (!f.encoder().writeOp(f64))
5091 0 : return false;
5092 : } else {
5093 0 : if (!f.encoder().writeOp(mozf64))
5094 0 : return false;
5095 : }
5096 : } else {
5097 0 : if (!f.encoder().writeOp(f32))
5098 0 : return false;
5099 : }
5100 :
5101 0 : *type = opIsDouble ? Type::Double : Type::Floatish;
5102 0 : return true;
5103 : }
5104 :
5105 : namespace {
5106 : // Include CheckSimdCallArgs in unnamed namespace to avoid MSVC name lookup bug.
5107 :
5108 : template<class CheckArgOp>
5109 : static bool
5110 0 : CheckSimdCallArgs(FunctionValidator& f, ParseNode* call, unsigned expectedArity,
5111 : const CheckArgOp& checkArg)
5112 : {
5113 0 : unsigned numArgs = CallArgListLength(call);
5114 0 : if (numArgs != expectedArity)
5115 0 : return f.failf(call, "expected %u arguments to SIMD call, got %u", expectedArity, numArgs);
5116 :
5117 0 : ParseNode* arg = CallArgList(call);
5118 0 : for (size_t i = 0; i < numArgs; i++, arg = NextNode(arg)) {
5119 0 : MOZ_ASSERT(!!arg);
5120 0 : Type argType;
5121 0 : if (!CheckExpr(f, arg, &argType))
5122 0 : return false;
5123 0 : if (!checkArg(f, arg, i, argType))
5124 0 : return false;
5125 : }
5126 :
5127 0 : return true;
5128 : }
5129 :
5130 :
5131 : class CheckArgIsSubtypeOf
5132 : {
5133 : Type formalType_;
5134 :
5135 : public:
5136 0 : explicit CheckArgIsSubtypeOf(SimdType t) : formalType_(t) {}
5137 :
5138 0 : bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const
5139 : {
5140 0 : if (!(actualType <= formalType_)) {
5141 0 : return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
5142 0 : formalType_.toChars());
5143 : }
5144 0 : return true;
5145 : }
5146 : };
5147 :
5148 : static inline Type
5149 0 : SimdToCoercedScalarType(SimdType t)
5150 : {
5151 0 : switch (t) {
5152 : case SimdType::Int8x16:
5153 : case SimdType::Int16x8:
5154 : case SimdType::Int32x4:
5155 : case SimdType::Uint8x16:
5156 : case SimdType::Uint16x8:
5157 : case SimdType::Uint32x4:
5158 : case SimdType::Bool8x16:
5159 : case SimdType::Bool16x8:
5160 : case SimdType::Bool32x4:
5161 0 : return Type::Intish;
5162 : case SimdType::Float32x4:
5163 0 : return Type::Floatish;
5164 : default:
5165 0 : break;
5166 : }
5167 0 : MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected SIMD type");
5168 : }
5169 :
5170 : class CheckSimdScalarArgs
5171 : {
5172 : SimdType simdType_;
5173 : Type formalType_;
5174 :
5175 : public:
5176 0 : explicit CheckSimdScalarArgs(SimdType simdType)
5177 0 : : simdType_(simdType), formalType_(SimdToCoercedScalarType(simdType))
5178 0 : {}
5179 :
5180 0 : bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const
5181 : {
5182 0 : if (!(actualType <= formalType_)) {
5183 : // As a special case, accept doublelit arguments to float32x4 ops by
5184 : // re-emitting them as float32 constants.
5185 0 : if (simdType_ != SimdType::Float32x4 || !actualType.isDoubleLit()) {
5186 0 : return f.failf(arg, "%s is not a subtype of %s%s",
5187 : actualType.toChars(), formalType_.toChars(),
5188 0 : simdType_ == SimdType::Float32x4 ? " or doublelit" : "");
5189 : }
5190 :
5191 : // We emitted a double literal and actually want a float32.
5192 0 : return f.encoder().writeOp(Op::F32DemoteF64);
5193 : }
5194 :
5195 0 : return true;
5196 : }
5197 : };
5198 :
5199 : class CheckSimdSelectArgs
5200 : {
5201 : Type formalType_;
5202 : Type maskType_;
5203 :
5204 : public:
5205 0 : explicit CheckSimdSelectArgs(SimdType t) : formalType_(t), maskType_(GetBooleanSimdType(t)) {}
5206 :
5207 0 : bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const
5208 : {
5209 : // The first argument is the boolean selector, the next two are the
5210 : // values to choose from.
5211 0 : Type wantedType = argIndex == 0 ? maskType_ : formalType_;
5212 :
5213 0 : if (!(actualType <= wantedType)) {
5214 0 : return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
5215 0 : wantedType.toChars());
5216 : }
5217 0 : return true;
5218 : }
5219 : };
5220 :
5221 : class CheckSimdVectorScalarArgs
5222 : {
5223 : SimdType formalSimdType_;
5224 :
5225 : public:
5226 0 : explicit CheckSimdVectorScalarArgs(SimdType t) : formalSimdType_(t) {}
5227 :
5228 0 : bool operator()(FunctionValidator& f, ParseNode* arg, unsigned argIndex, Type actualType) const
5229 : {
5230 0 : MOZ_ASSERT(argIndex < 2);
5231 0 : if (argIndex == 0) {
5232 : // First argument is the vector
5233 0 : if (!(actualType <= Type(formalSimdType_))) {
5234 0 : return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
5235 0 : Type(formalSimdType_).toChars());
5236 : }
5237 :
5238 0 : return true;
5239 : }
5240 :
5241 : // Second argument is the scalar
5242 0 : return CheckSimdScalarArgs(formalSimdType_)(f, arg, argIndex, actualType);
5243 : }
5244 : };
5245 :
5246 : } // namespace
5247 :
5248 : static bool
5249 0 : CheckSimdUnary(FunctionValidator& f, ParseNode* call, SimdType opType, SimdOperation op,
5250 : Type* type)
5251 : {
5252 0 : if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType)))
5253 0 : return false;
5254 0 : if (!f.writeSimdOp(opType, op))
5255 0 : return false;
5256 0 : *type = opType;
5257 0 : return true;
5258 : }
5259 :
5260 : static bool
5261 0 : CheckSimdBinaryShift(FunctionValidator& f, ParseNode* call, SimdType opType, SimdOperation op,
5262 : Type *type)
5263 : {
5264 0 : if (!CheckSimdCallArgs(f, call, 2, CheckSimdVectorScalarArgs(opType)))
5265 0 : return false;
5266 0 : if (!f.writeSimdOp(opType, op))
5267 0 : return false;
5268 0 : *type = opType;
5269 0 : return true;
5270 : }
5271 :
5272 : static bool
5273 0 : CheckSimdBinaryComp(FunctionValidator& f, ParseNode* call, SimdType opType, SimdOperation op,
5274 : Type *type)
5275 : {
5276 0 : if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType)))
5277 0 : return false;
5278 0 : if (!f.writeSimdOp(opType, op))
5279 0 : return false;
5280 0 : *type = GetBooleanSimdType(opType);
5281 0 : return true;
5282 : }
5283 :
5284 : static bool
5285 0 : CheckSimdBinary(FunctionValidator& f, ParseNode* call, SimdType opType, SimdOperation op,
5286 : Type* type)
5287 : {
5288 0 : if (!CheckSimdCallArgs(f, call, 2, CheckArgIsSubtypeOf(opType)))
5289 0 : return false;
5290 0 : if (!f.writeSimdOp(opType, op))
5291 0 : return false;
5292 0 : *type = opType;
5293 0 : return true;
5294 : }
5295 :
5296 : static bool
5297 0 : CheckSimdExtractLane(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5298 : {
5299 0 : switch (opType) {
5300 : case SimdType::Int8x16:
5301 : case SimdType::Int16x8:
5302 0 : case SimdType::Int32x4: *type = Type::Signed; break;
5303 : case SimdType::Uint8x16:
5304 : case SimdType::Uint16x8:
5305 0 : case SimdType::Uint32x4: *type = Type::Unsigned; break;
5306 0 : case SimdType::Float32x4: *type = Type::Float; break;
5307 : case SimdType::Bool8x16:
5308 : case SimdType::Bool16x8:
5309 0 : case SimdType::Bool32x4: *type = Type::Int; break;
5310 0 : default: MOZ_CRASH("unhandled simd type");
5311 : }
5312 :
5313 0 : unsigned numArgs = CallArgListLength(call);
5314 0 : if (numArgs != 2)
5315 0 : return f.failf(call, "expected 2 arguments to SIMD extract, got %u", numArgs);
5316 :
5317 0 : ParseNode* arg = CallArgList(call);
5318 :
5319 : // First argument is the vector
5320 0 : Type vecType;
5321 0 : if (!CheckExpr(f, arg, &vecType))
5322 0 : return false;
5323 0 : if (!(vecType <= Type(opType))) {
5324 0 : return f.failf(arg, "%s is not a subtype of %s", vecType.toChars(),
5325 0 : Type(opType).toChars());
5326 : }
5327 :
5328 0 : arg = NextNode(arg);
5329 :
5330 : // Second argument is the lane < vector length
5331 : uint32_t lane;
5332 0 : if (!IsLiteralOrConstInt(f, arg, &lane))
5333 0 : return f.failf(arg, "lane selector should be a constant integer literal");
5334 0 : if (lane >= GetSimdLanes(opType))
5335 0 : return f.failf(arg, "lane selector should be in bounds");
5336 :
5337 0 : if (!f.writeSimdOp(opType, SimdOperation::Fn_extractLane))
5338 0 : return false;
5339 0 : if (!f.encoder().writeVarU32(lane))
5340 0 : return false;
5341 0 : return true;
5342 : }
5343 :
5344 : static bool
5345 0 : CheckSimdReplaceLane(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5346 : {
5347 0 : unsigned numArgs = CallArgListLength(call);
5348 0 : if (numArgs != 3)
5349 0 : return f.failf(call, "expected 2 arguments to SIMD replace, got %u", numArgs);
5350 :
5351 0 : ParseNode* arg = CallArgList(call);
5352 :
5353 : // First argument is the vector
5354 0 : Type vecType;
5355 0 : if (!CheckExpr(f, arg, &vecType))
5356 0 : return false;
5357 0 : if (!(vecType <= Type(opType))) {
5358 0 : return f.failf(arg, "%s is not a subtype of %s", vecType.toChars(),
5359 0 : Type(opType).toChars());
5360 : }
5361 :
5362 0 : arg = NextNode(arg);
5363 :
5364 : // Second argument is the lane < vector length
5365 : uint32_t lane;
5366 0 : if (!IsLiteralOrConstInt(f, arg, &lane))
5367 0 : return f.failf(arg, "lane selector should be a constant integer literal");
5368 0 : if (lane >= GetSimdLanes(opType))
5369 0 : return f.failf(arg, "lane selector should be in bounds");
5370 :
5371 0 : arg = NextNode(arg);
5372 :
5373 : // Third argument is the scalar
5374 0 : Type scalarType;
5375 0 : if (!CheckExpr(f, arg, &scalarType))
5376 0 : return false;
5377 0 : if (!(scalarType <= SimdToCoercedScalarType(opType))) {
5378 0 : if (opType == SimdType::Float32x4 && scalarType.isDoubleLit()) {
5379 0 : if (!f.encoder().writeOp(Op::F32DemoteF64))
5380 0 : return false;
5381 : } else {
5382 0 : return f.failf(arg, "%s is not the correct type to replace an element of %s",
5383 0 : scalarType.toChars(), vecType.toChars());
5384 : }
5385 : }
5386 :
5387 0 : if (!f.writeSimdOp(opType, SimdOperation::Fn_replaceLane))
5388 0 : return false;
5389 0 : if (!f.encoder().writeVarU32(lane))
5390 0 : return false;
5391 0 : *type = opType;
5392 0 : return true;
5393 : }
5394 :
5395 : typedef bool Bitcast;
5396 :
5397 : namespace {
5398 : // Include CheckSimdCast in unnamed namespace to avoid MSVC name lookup bug (due to the use of Type).
5399 :
5400 : static bool
5401 0 : CheckSimdCast(FunctionValidator& f, ParseNode* call, SimdType fromType, SimdType toType,
5402 : SimdOperation op, Type* type)
5403 : {
5404 0 : if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(fromType)))
5405 0 : return false;
5406 0 : if (!f.writeSimdOp(toType, op))
5407 0 : return false;
5408 0 : *type = toType;
5409 0 : return true;
5410 : }
5411 :
5412 : } // namespace
5413 :
5414 : static bool
5415 0 : CheckSimdShuffleSelectors(FunctionValidator& f, ParseNode* lane,
5416 : mozilla::Array<uint8_t, 16>& lanes, unsigned numLanes, unsigned maxLane)
5417 : {
5418 0 : for (unsigned i = 0; i < numLanes; i++, lane = NextNode(lane)) {
5419 : uint32_t u32;
5420 0 : if (!IsLiteralInt(f.m(), lane, &u32))
5421 0 : return f.failf(lane, "lane selector should be a constant integer literal");
5422 0 : if (u32 >= maxLane)
5423 0 : return f.failf(lane, "lane selector should be less than %u", maxLane);
5424 0 : lanes[i] = uint8_t(u32);
5425 : }
5426 0 : return true;
5427 : }
5428 :
5429 : static bool
5430 0 : CheckSimdSwizzle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5431 : {
5432 0 : const unsigned numLanes = GetSimdLanes(opType);
5433 0 : unsigned numArgs = CallArgListLength(call);
5434 0 : if (numArgs != 1 + numLanes)
5435 0 : return f.failf(call, "expected %u arguments to SIMD swizzle, got %u", 1 + numLanes,
5436 0 : numArgs);
5437 :
5438 0 : Type retType = opType;
5439 0 : ParseNode* vec = CallArgList(call);
5440 0 : Type vecType;
5441 0 : if (!CheckExpr(f, vec, &vecType))
5442 0 : return false;
5443 0 : if (!(vecType <= retType))
5444 0 : return f.failf(vec, "%s is not a subtype of %s", vecType.toChars(), retType.toChars());
5445 :
5446 0 : if (!f.writeSimdOp(opType, SimdOperation::Fn_swizzle))
5447 0 : return false;
5448 :
5449 0 : mozilla::Array<uint8_t, 16> lanes;
5450 0 : if (!CheckSimdShuffleSelectors(f, NextNode(vec), lanes, numLanes, numLanes))
5451 0 : return false;
5452 :
5453 0 : for (unsigned i = 0; i < numLanes; i++) {
5454 0 : if (!f.encoder().writeFixedU8(lanes[i]))
5455 0 : return false;
5456 : }
5457 :
5458 0 : *type = retType;
5459 0 : return true;
5460 : }
5461 :
5462 : static bool
5463 0 : CheckSimdShuffle(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5464 : {
5465 0 : const unsigned numLanes = GetSimdLanes(opType);
5466 0 : unsigned numArgs = CallArgListLength(call);
5467 0 : if (numArgs != 2 + numLanes)
5468 0 : return f.failf(call, "expected %u arguments to SIMD shuffle, got %u", 2 + numLanes,
5469 0 : numArgs);
5470 :
5471 0 : Type retType = opType;
5472 0 : ParseNode* arg = CallArgList(call);
5473 0 : for (unsigned i = 0; i < 2; i++, arg = NextNode(arg)) {
5474 0 : Type type;
5475 0 : if (!CheckExpr(f, arg, &type))
5476 0 : return false;
5477 0 : if (!(type <= retType))
5478 0 : return f.failf(arg, "%s is not a subtype of %s", type.toChars(), retType.toChars());
5479 : }
5480 :
5481 0 : if (!f.writeSimdOp(opType, SimdOperation::Fn_shuffle))
5482 0 : return false;
5483 :
5484 0 : mozilla::Array<uint8_t, 16> lanes;
5485 0 : if (!CheckSimdShuffleSelectors(f, arg, lanes, numLanes, 2 * numLanes))
5486 0 : return false;
5487 :
5488 0 : for (unsigned i = 0; i < numLanes; i++) {
5489 0 : if (!f.encoder().writeFixedU8(uint8_t(lanes[i])))
5490 0 : return false;
5491 : }
5492 :
5493 0 : *type = retType;
5494 0 : return true;
5495 : }
5496 :
5497 : static bool
5498 0 : CheckSimdLoadStoreArgs(FunctionValidator& f, ParseNode* call, Scalar::Type* viewType)
5499 : {
5500 0 : ParseNode* view = CallArgList(call);
5501 0 : if (!view->isKind(PNK_NAME))
5502 0 : return f.fail(view, "expected Uint8Array view as SIMD.*.load/store first argument");
5503 :
5504 0 : ParseNode* indexExpr = NextNode(view);
5505 :
5506 0 : if (!CheckAndPrepareArrayAccess(f, view, indexExpr, YesSimd, viewType))
5507 0 : return false;
5508 :
5509 0 : if (*viewType != Scalar::Uint8)
5510 0 : return f.fail(view, "expected Uint8Array view as SIMD.*.load/store first argument");
5511 :
5512 0 : return true;
5513 : }
5514 :
5515 : static bool
5516 0 : CheckSimdLoad(FunctionValidator& f, ParseNode* call, SimdType opType, SimdOperation op,
5517 : Type* type)
5518 : {
5519 0 : unsigned numArgs = CallArgListLength(call);
5520 0 : if (numArgs != 2)
5521 0 : return f.failf(call, "expected 2 arguments to SIMD load, got %u", numArgs);
5522 :
5523 : Scalar::Type viewType;
5524 0 : if (!CheckSimdLoadStoreArgs(f, call, &viewType))
5525 0 : return false;
5526 :
5527 0 : if (!f.writeSimdOp(opType, op))
5528 0 : return false;
5529 :
5530 0 : if (!WriteArrayAccessFlags(f, viewType))
5531 0 : return false;
5532 :
5533 0 : *type = opType;
5534 0 : return true;
5535 : }
5536 :
5537 : static bool
5538 0 : CheckSimdStore(FunctionValidator& f, ParseNode* call, SimdType opType, SimdOperation op,
5539 : Type* type)
5540 : {
5541 0 : unsigned numArgs = CallArgListLength(call);
5542 0 : if (numArgs != 3)
5543 0 : return f.failf(call, "expected 3 arguments to SIMD store, got %u", numArgs);
5544 :
5545 : Scalar::Type viewType;
5546 0 : if (!CheckSimdLoadStoreArgs(f, call, &viewType))
5547 0 : return false;
5548 :
5549 0 : Type retType = opType;
5550 0 : ParseNode* vecExpr = NextNode(NextNode(CallArgList(call)));
5551 0 : Type vecType;
5552 0 : if (!CheckExpr(f, vecExpr, &vecType))
5553 0 : return false;
5554 :
5555 0 : if (!f.writeSimdOp(opType, op))
5556 0 : return false;
5557 :
5558 0 : if (!WriteArrayAccessFlags(f, viewType))
5559 0 : return false;
5560 :
5561 0 : if (!(vecType <= retType))
5562 0 : return f.failf(vecExpr, "%s is not a subtype of %s", vecType.toChars(), retType.toChars());
5563 :
5564 0 : *type = vecType;
5565 0 : return true;
5566 : }
5567 :
5568 : static bool
5569 0 : CheckSimdSelect(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5570 : {
5571 0 : if (!CheckSimdCallArgs(f, call, 3, CheckSimdSelectArgs(opType)))
5572 0 : return false;
5573 0 : if (!f.writeSimdOp(opType, SimdOperation::Fn_select))
5574 0 : return false;
5575 0 : *type = opType;
5576 0 : return true;
5577 : }
5578 :
5579 : static bool
5580 0 : CheckSimdAllTrue(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5581 : {
5582 0 : if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType)))
5583 0 : return false;
5584 0 : if (!f.writeSimdOp(opType, SimdOperation::Fn_allTrue))
5585 0 : return false;
5586 0 : *type = Type::Int;
5587 0 : return true;
5588 : }
5589 :
5590 : static bool
5591 0 : CheckSimdAnyTrue(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5592 : {
5593 0 : if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(opType)))
5594 0 : return false;
5595 0 : if (!f.writeSimdOp(opType, SimdOperation::Fn_anyTrue))
5596 0 : return false;
5597 0 : *type = Type::Int;
5598 0 : return true;
5599 : }
5600 :
5601 : static bool
5602 0 : CheckSimdCheck(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5603 : {
5604 0 : Type coerceTo;
5605 : ParseNode* argNode;
5606 0 : if (!IsCoercionCall(f.m(), call, &coerceTo, &argNode))
5607 0 : return f.failf(call, "expected 1 argument in call to check");
5608 0 : return CheckCoercionArg(f, argNode, coerceTo, type);
5609 : }
5610 :
5611 : static bool
5612 0 : CheckSimdSplat(FunctionValidator& f, ParseNode* call, SimdType opType, Type* type)
5613 : {
5614 0 : if (!CheckSimdCallArgs(f, call, 1, CheckSimdScalarArgs(opType)))
5615 0 : return false;
5616 0 : if (!f.writeSimdOp(opType, SimdOperation::Fn_splat))
5617 0 : return false;
5618 0 : *type = opType;
5619 0 : return true;
5620 : }
5621 :
5622 : static bool
5623 0 : CheckSimdOperationCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
5624 : Type* type)
5625 : {
5626 0 : f.setUsesSimd();
5627 :
5628 0 : MOZ_ASSERT(global->isSimdOperation());
5629 :
5630 0 : SimdType opType = global->simdOperationType();
5631 :
5632 0 : switch (SimdOperation op = global->simdOperation()) {
5633 : case SimdOperation::Fn_check:
5634 0 : return CheckSimdCheck(f, call, opType, type);
5635 :
5636 : #define _CASE(OP) case SimdOperation::Fn_##OP:
5637 : FOREACH_SHIFT_SIMD_OP(_CASE)
5638 0 : return CheckSimdBinaryShift(f, call, opType, op, type);
5639 :
5640 : FOREACH_COMP_SIMD_OP(_CASE)
5641 0 : return CheckSimdBinaryComp(f, call, opType, op, type);
5642 :
5643 : FOREACH_NUMERIC_SIMD_BINOP(_CASE)
5644 : FOREACH_FLOAT_SIMD_BINOP(_CASE)
5645 : FOREACH_BITWISE_SIMD_BINOP(_CASE)
5646 : FOREACH_SMINT_SIMD_BINOP(_CASE)
5647 0 : return CheckSimdBinary(f, call, opType, op, type);
5648 : #undef _CASE
5649 :
5650 : case SimdOperation::Fn_extractLane:
5651 0 : return CheckSimdExtractLane(f, call, opType, type);
5652 : case SimdOperation::Fn_replaceLane:
5653 0 : return CheckSimdReplaceLane(f, call, opType, type);
5654 :
5655 : case SimdOperation::Fn_fromInt8x16Bits:
5656 0 : return CheckSimdCast(f, call, SimdType::Int8x16, opType, op, type);
5657 : case SimdOperation::Fn_fromUint8x16Bits:
5658 0 : return CheckSimdCast(f, call, SimdType::Uint8x16, opType, op, type);
5659 : case SimdOperation::Fn_fromInt16x8Bits:
5660 0 : return CheckSimdCast(f, call, SimdType::Int16x8, opType, op, type);
5661 : case SimdOperation::Fn_fromUint16x8Bits:
5662 0 : return CheckSimdCast(f, call, SimdType::Uint16x8, opType, op, type);
5663 : case SimdOperation::Fn_fromInt32x4:
5664 : case SimdOperation::Fn_fromInt32x4Bits:
5665 0 : return CheckSimdCast(f, call, SimdType::Int32x4, opType, op, type);
5666 : case SimdOperation::Fn_fromUint32x4:
5667 : case SimdOperation::Fn_fromUint32x4Bits:
5668 0 : return CheckSimdCast(f, call, SimdType::Uint32x4, opType, op, type);
5669 : case SimdOperation::Fn_fromFloat32x4:
5670 : case SimdOperation::Fn_fromFloat32x4Bits:
5671 0 : return CheckSimdCast(f, call, SimdType::Float32x4, opType, op, type);
5672 :
5673 : case SimdOperation::Fn_abs:
5674 : case SimdOperation::Fn_neg:
5675 : case SimdOperation::Fn_not:
5676 : case SimdOperation::Fn_sqrt:
5677 : case SimdOperation::Fn_reciprocalApproximation:
5678 : case SimdOperation::Fn_reciprocalSqrtApproximation:
5679 0 : return CheckSimdUnary(f, call, opType, op, type);
5680 :
5681 : case SimdOperation::Fn_swizzle:
5682 0 : return CheckSimdSwizzle(f, call, opType, type);
5683 : case SimdOperation::Fn_shuffle:
5684 0 : return CheckSimdShuffle(f, call, opType, type);
5685 :
5686 : case SimdOperation::Fn_load:
5687 : case SimdOperation::Fn_load1:
5688 : case SimdOperation::Fn_load2:
5689 0 : return CheckSimdLoad(f, call, opType, op, type);
5690 : case SimdOperation::Fn_store:
5691 : case SimdOperation::Fn_store1:
5692 : case SimdOperation::Fn_store2:
5693 0 : return CheckSimdStore(f, call, opType, op, type);
5694 :
5695 : case SimdOperation::Fn_select:
5696 0 : return CheckSimdSelect(f, call, opType, type);
5697 :
5698 : case SimdOperation::Fn_splat:
5699 0 : return CheckSimdSplat(f, call, opType, type);
5700 :
5701 : case SimdOperation::Fn_allTrue:
5702 0 : return CheckSimdAllTrue(f, call, opType, type);
5703 : case SimdOperation::Fn_anyTrue:
5704 0 : return CheckSimdAnyTrue(f, call, opType, type);
5705 :
5706 : case SimdOperation::Fn_load3:
5707 : case SimdOperation::Fn_store3:
5708 0 : return f.fail(call, "asm.js does not support 3-element SIMD loads or stores");
5709 :
5710 : case SimdOperation::Constructor:
5711 0 : MOZ_CRASH("constructors are handled in CheckSimdCtorCall");
5712 : case SimdOperation::Fn_fromFloat64x2Bits:
5713 0 : MOZ_CRASH("NYI");
5714 : }
5715 0 : MOZ_CRASH("unexpected simd operation in CheckSimdOperationCall");
5716 : }
5717 :
5718 : static bool
5719 0 : CheckSimdCtorCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
5720 : Type* type)
5721 : {
5722 0 : f.setUsesSimd();
5723 :
5724 0 : MOZ_ASSERT(call->isKind(PNK_CALL));
5725 :
5726 0 : SimdType simdType = global->simdCtorType();
5727 0 : unsigned length = GetSimdLanes(simdType);
5728 0 : if (!CheckSimdCallArgs(f, call, length, CheckSimdScalarArgs(simdType)))
5729 0 : return false;
5730 :
5731 0 : if (!f.writeSimdOp(simdType, SimdOperation::Constructor))
5732 0 : return false;
5733 :
5734 0 : *type = simdType;
5735 0 : return true;
5736 : }
5737 :
5738 : static bool
5739 0 : CheckUncoercedCall(FunctionValidator& f, ParseNode* expr, Type* type)
5740 : {
5741 0 : MOZ_ASSERT(expr->isKind(PNK_CALL));
5742 :
5743 : const ModuleValidator::Global* global;
5744 0 : if (IsCallToGlobal(f.m(), expr, &global)) {
5745 0 : if (global->isMathFunction())
5746 0 : return CheckMathBuiltinCall(f, expr, global->mathBuiltinFunction(), type);
5747 0 : if (global->isAtomicsFunction())
5748 0 : return CheckAtomicsBuiltinCall(f, expr, global->atomicsBuiltinFunction(), type);
5749 0 : if (global->isSimdCtor())
5750 0 : return CheckSimdCtorCall(f, expr, global, type);
5751 0 : if (global->isSimdOperation())
5752 0 : return CheckSimdOperationCall(f, expr, global, type);
5753 : }
5754 :
5755 : return f.fail(expr, "all function calls must either be calls to standard lib math functions, "
5756 : "standard atomic functions, standard SIMD constructors or operations, "
5757 : "ignored (via f(); or comma-expression), coerced to signed (via f()|0), "
5758 0 : "coerced to float (via fround(f())) or coerced to double (via +f())");
5759 : }
5760 :
5761 : static bool
5762 0 : CoerceResult(FunctionValidator& f, ParseNode* expr, Type expected, Type actual,
5763 : Type* type)
5764 : {
5765 0 : MOZ_ASSERT(expected.isCanonical());
5766 :
5767 : // At this point, the bytecode resembles this:
5768 : // | the thing we wanted to coerce | current position |>
5769 0 : switch (expected.which()) {
5770 : case Type::Void:
5771 0 : if (!actual.isVoid()) {
5772 0 : if (!f.encoder().writeOp(Op::Drop))
5773 0 : return false;
5774 : }
5775 0 : break;
5776 : case Type::Int:
5777 0 : if (!actual.isIntish())
5778 0 : return f.failf(expr, "%s is not a subtype of intish", actual.toChars());
5779 0 : break;
5780 : case Type::Float:
5781 0 : if (!CheckFloatCoercionArg(f, expr, actual))
5782 0 : return false;
5783 0 : break;
5784 : case Type::Double:
5785 0 : if (actual.isMaybeDouble()) {
5786 : // No conversion necessary.
5787 0 : } else if (actual.isMaybeFloat()) {
5788 0 : if (!f.encoder().writeOp(Op::F64PromoteF32))
5789 0 : return false;
5790 0 : } else if (actual.isSigned()) {
5791 0 : if (!f.encoder().writeOp(Op::F64ConvertSI32))
5792 0 : return false;
5793 0 : } else if (actual.isUnsigned()) {
5794 0 : if (!f.encoder().writeOp(Op::F64ConvertUI32))
5795 0 : return false;
5796 : } else {
5797 0 : return f.failf(expr, "%s is not a subtype of double?, float?, signed or unsigned", actual.toChars());
5798 : }
5799 0 : break;
5800 : default:
5801 0 : MOZ_ASSERT(expected.isSimd(), "Incomplete switch");
5802 0 : if (actual != expected)
5803 0 : return f.failf(expr, "got type %s, expected %s", actual.toChars(), expected.toChars());
5804 0 : break;
5805 : }
5806 :
5807 0 : *type = Type::ret(expected);
5808 0 : return true;
5809 : }
5810 :
5811 : static bool
5812 0 : CheckCoercedMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func,
5813 : Type ret, Type* type)
5814 : {
5815 0 : Type actual;
5816 0 : if (!CheckMathBuiltinCall(f, callNode, func, &actual))
5817 0 : return false;
5818 0 : return CoerceResult(f, callNode, ret, actual, type);
5819 : }
5820 :
5821 : static bool
5822 0 : CheckCoercedSimdCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
5823 : Type ret, Type* type)
5824 : {
5825 0 : MOZ_ASSERT(ret.isCanonical());
5826 :
5827 0 : Type actual;
5828 0 : if (global->isSimdCtor()) {
5829 0 : if (!CheckSimdCtorCall(f, call, global, &actual))
5830 0 : return false;
5831 0 : MOZ_ASSERT(actual.isSimd());
5832 : } else {
5833 0 : MOZ_ASSERT(global->isSimdOperation());
5834 0 : if (!CheckSimdOperationCall(f, call, global, &actual))
5835 0 : return false;
5836 : }
5837 :
5838 0 : return CoerceResult(f, call, ret, actual, type);
5839 : }
5840 :
5841 : static bool
5842 0 : CheckCoercedAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode,
5843 : AsmJSAtomicsBuiltinFunction func, Type ret, Type* type)
5844 : {
5845 0 : MOZ_ASSERT(ret.isCanonical());
5846 :
5847 0 : Type actual;
5848 0 : if (!CheckAtomicsBuiltinCall(f, callNode, func, &actual))
5849 0 : return false;
5850 0 : return CoerceResult(f, callNode, ret, actual, type);
5851 : }
5852 :
5853 : static bool
5854 0 : CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type)
5855 : {
5856 0 : MOZ_ASSERT(ret.isCanonical());
5857 :
5858 0 : if (!CheckRecursionLimitDontReport(f.cx()))
5859 0 : return f.m().failOverRecursed();
5860 :
5861 0 : bool isSimd = false;
5862 0 : if (IsNumericLiteral(f.m(), call, &isSimd)) {
5863 0 : if (isSimd)
5864 0 : f.setUsesSimd();
5865 0 : NumLit lit = ExtractNumericLiteral(f.m(), call);
5866 0 : if (!f.writeConstExpr(lit))
5867 0 : return false;
5868 0 : return CoerceResult(f, call, ret, Type::lit(lit), type);
5869 : }
5870 :
5871 0 : ParseNode* callee = CallCallee(call);
5872 :
5873 0 : if (callee->isKind(PNK_ELEM))
5874 0 : return CheckFuncPtrCall(f, call, ret, type);
5875 :
5876 0 : if (!callee->isKind(PNK_NAME))
5877 0 : return f.fail(callee, "unexpected callee expression type");
5878 :
5879 0 : PropertyName* calleeName = callee->name();
5880 :
5881 0 : if (const ModuleValidator::Global* global = f.lookupGlobal(calleeName)) {
5882 0 : switch (global->which()) {
5883 : case ModuleValidator::Global::FFI:
5884 0 : return CheckFFICall(f, call, global->ffiIndex(), ret, type);
5885 : case ModuleValidator::Global::MathBuiltinFunction:
5886 0 : return CheckCoercedMathBuiltinCall(f, call, global->mathBuiltinFunction(), ret, type);
5887 : case ModuleValidator::Global::AtomicsBuiltinFunction:
5888 0 : return CheckCoercedAtomicsBuiltinCall(f, call, global->atomicsBuiltinFunction(), ret, type);
5889 : case ModuleValidator::Global::ConstantLiteral:
5890 : case ModuleValidator::Global::ConstantImport:
5891 : case ModuleValidator::Global::Variable:
5892 : case ModuleValidator::Global::FuncPtrTable:
5893 : case ModuleValidator::Global::ArrayView:
5894 : case ModuleValidator::Global::ArrayViewCtor:
5895 0 : return f.failName(callee, "'%s' is not callable function", callee->name());
5896 : case ModuleValidator::Global::SimdCtor:
5897 : case ModuleValidator::Global::SimdOp:
5898 0 : return CheckCoercedSimdCall(f, call, global, ret, type);
5899 : case ModuleValidator::Global::Function:
5900 0 : break;
5901 : }
5902 : }
5903 :
5904 0 : return CheckInternalCall(f, call, calleeName, ret, type);
5905 : }
5906 :
5907 : static bool
5908 0 : CheckPos(FunctionValidator& f, ParseNode* pos, Type* type)
5909 : {
5910 0 : MOZ_ASSERT(pos->isKind(PNK_POS));
5911 0 : ParseNode* operand = UnaryKid(pos);
5912 :
5913 0 : if (operand->isKind(PNK_CALL))
5914 0 : return CheckCoercedCall(f, operand, Type::Double, type);
5915 :
5916 0 : Type actual;
5917 0 : if (!CheckExpr(f, operand, &actual))
5918 0 : return false;
5919 :
5920 0 : return CoerceResult(f, operand, Type::Double, actual, type);
5921 : }
5922 :
5923 : static bool
5924 0 : CheckNot(FunctionValidator& f, ParseNode* expr, Type* type)
5925 : {
5926 0 : MOZ_ASSERT(expr->isKind(PNK_NOT));
5927 0 : ParseNode* operand = UnaryKid(expr);
5928 :
5929 0 : Type operandType;
5930 0 : if (!CheckExpr(f, operand, &operandType))
5931 0 : return false;
5932 :
5933 0 : if (!operandType.isInt())
5934 0 : return f.failf(operand, "%s is not a subtype of int", operandType.toChars());
5935 :
5936 0 : *type = Type::Int;
5937 0 : return f.encoder().writeOp(Op::I32Eqz);
5938 : }
5939 :
5940 : static bool
5941 0 : CheckNeg(FunctionValidator& f, ParseNode* expr, Type* type)
5942 : {
5943 0 : MOZ_ASSERT(expr->isKind(PNK_NEG));
5944 0 : ParseNode* operand = UnaryKid(expr);
5945 :
5946 0 : Type operandType;
5947 0 : if (!CheckExpr(f, operand, &operandType))
5948 0 : return false;
5949 :
5950 0 : if (operandType.isInt()) {
5951 0 : *type = Type::Intish;
5952 0 : return f.encoder().writeOp(MozOp::I32Neg);
5953 : }
5954 :
5955 0 : if (operandType.isMaybeDouble()) {
5956 0 : *type = Type::Double;
5957 0 : return f.encoder().writeOp(Op::F64Neg);
5958 : }
5959 :
5960 0 : if (operandType.isMaybeFloat()) {
5961 0 : *type = Type::Floatish;
5962 0 : return f.encoder().writeOp(Op::F32Neg);
5963 : }
5964 :
5965 0 : return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars());
5966 : }
5967 :
5968 : static bool
5969 0 : CheckCoerceToInt(FunctionValidator& f, ParseNode* expr, Type* type)
5970 : {
5971 0 : MOZ_ASSERT(expr->isKind(PNK_BITNOT));
5972 0 : ParseNode* operand = UnaryKid(expr);
5973 :
5974 0 : Type operandType;
5975 0 : if (!CheckExpr(f, operand, &operandType))
5976 0 : return false;
5977 :
5978 0 : if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) {
5979 0 : *type = Type::Signed;
5980 0 : Op opcode = operandType.isMaybeDouble() ? Op::I32TruncSF64 : Op::I32TruncSF32;
5981 0 : return f.encoder().writeOp(opcode);
5982 : }
5983 :
5984 0 : if (!operandType.isIntish())
5985 0 : return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars());
5986 :
5987 0 : *type = Type::Signed;
5988 0 : return true;
5989 : }
5990 :
5991 : static bool
5992 0 : CheckBitNot(FunctionValidator& f, ParseNode* neg, Type* type)
5993 : {
5994 0 : MOZ_ASSERT(neg->isKind(PNK_BITNOT));
5995 0 : ParseNode* operand = UnaryKid(neg);
5996 :
5997 0 : if (operand->isKind(PNK_BITNOT))
5998 0 : return CheckCoerceToInt(f, operand, type);
5999 :
6000 0 : Type operandType;
6001 0 : if (!CheckExpr(f, operand, &operandType))
6002 0 : return false;
6003 :
6004 0 : if (!operandType.isIntish())
6005 0 : return f.failf(operand, "%s is not a subtype of intish", operandType.toChars());
6006 :
6007 0 : if (!f.encoder().writeOp(MozOp::I32BitNot))
6008 0 : return false;
6009 :
6010 0 : *type = Type::Signed;
6011 0 : return true;
6012 : }
6013 :
6014 : static bool
6015 : CheckAsExprStatement(FunctionValidator& f, ParseNode* exprStmt);
6016 :
6017 : static bool
6018 0 : CheckComma(FunctionValidator& f, ParseNode* comma, Type* type)
6019 : {
6020 0 : MOZ_ASSERT(comma->isKind(PNK_COMMA));
6021 0 : ParseNode* operands = ListHead(comma);
6022 :
6023 : // The block depth isn't taken into account here, because a comma list can't
6024 : // contain breaks and continues and nested control flow structures.
6025 0 : if (!f.encoder().writeOp(Op::Block))
6026 0 : return false;
6027 :
6028 : size_t typeAt;
6029 0 : if (!f.encoder().writePatchableFixedU7(&typeAt))
6030 0 : return false;
6031 :
6032 0 : ParseNode* pn = operands;
6033 0 : for (; NextNode(pn); pn = NextNode(pn)) {
6034 0 : if (!CheckAsExprStatement(f, pn))
6035 0 : return false;
6036 : }
6037 :
6038 0 : if (!CheckExpr(f, pn, type))
6039 0 : return false;
6040 :
6041 0 : f.encoder().patchFixedU7(typeAt, uint8_t(type->toWasmBlockSignatureType()));
6042 :
6043 0 : return f.encoder().writeOp(Op::End);
6044 : }
6045 :
6046 : static bool
6047 0 : CheckConditional(FunctionValidator& f, ParseNode* ternary, Type* type)
6048 : {
6049 0 : MOZ_ASSERT(ternary->isKind(PNK_CONDITIONAL));
6050 :
6051 0 : ParseNode* cond = TernaryKid1(ternary);
6052 0 : ParseNode* thenExpr = TernaryKid2(ternary);
6053 0 : ParseNode* elseExpr = TernaryKid3(ternary);
6054 :
6055 0 : Type condType;
6056 0 : if (!CheckExpr(f, cond, &condType))
6057 0 : return false;
6058 :
6059 0 : if (!condType.isInt())
6060 0 : return f.failf(cond, "%s is not a subtype of int", condType.toChars());
6061 :
6062 : size_t typeAt;
6063 0 : if (!f.pushIf(&typeAt))
6064 0 : return false;
6065 :
6066 0 : Type thenType;
6067 0 : if (!CheckExpr(f, thenExpr, &thenType))
6068 0 : return false;
6069 :
6070 0 : if (!f.switchToElse())
6071 0 : return false;
6072 :
6073 0 : Type elseType;
6074 0 : if (!CheckExpr(f, elseExpr, &elseType))
6075 0 : return false;
6076 :
6077 0 : if (thenType.isInt() && elseType.isInt()) {
6078 0 : *type = Type::Int;
6079 0 : } else if (thenType.isDouble() && elseType.isDouble()) {
6080 0 : *type = Type::Double;
6081 0 : } else if (thenType.isFloat() && elseType.isFloat()) {
6082 0 : *type = Type::Float;
6083 0 : } else if (thenType.isSimd() && elseType == thenType) {
6084 0 : *type = thenType;
6085 : } else {
6086 0 : return f.failf(ternary, "then/else branches of conditional must both produce int, float, "
6087 : "double or SIMD types, current types are %s and %s",
6088 0 : thenType.toChars(), elseType.toChars());
6089 : }
6090 :
6091 0 : if (!f.popIf(typeAt, type->toWasmBlockSignatureType()))
6092 0 : return false;
6093 :
6094 0 : return true;
6095 : }
6096 :
6097 : static bool
6098 0 : IsValidIntMultiplyConstant(ModuleValidator& m, ParseNode* expr)
6099 : {
6100 0 : if (!IsNumericLiteral(m, expr))
6101 0 : return false;
6102 :
6103 0 : NumLit lit = ExtractNumericLiteral(m, expr);
6104 0 : switch (lit.which()) {
6105 : case NumLit::Fixnum:
6106 : case NumLit::NegativeInt:
6107 0 : if (abs(lit.toInt32()) < (1<<20))
6108 0 : return true;
6109 0 : return false;
6110 : case NumLit::BigUnsigned:
6111 : case NumLit::Double:
6112 : case NumLit::Float:
6113 : case NumLit::OutOfRangeInt:
6114 : case NumLit::Int8x16:
6115 : case NumLit::Uint8x16:
6116 : case NumLit::Int16x8:
6117 : case NumLit::Uint16x8:
6118 : case NumLit::Int32x4:
6119 : case NumLit::Uint32x4:
6120 : case NumLit::Float32x4:
6121 : case NumLit::Bool8x16:
6122 : case NumLit::Bool16x8:
6123 : case NumLit::Bool32x4:
6124 0 : return false;
6125 : }
6126 :
6127 0 : MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal");
6128 : }
6129 :
6130 : static bool
6131 0 : CheckMultiply(FunctionValidator& f, ParseNode* star, Type* type)
6132 : {
6133 0 : MOZ_ASSERT(star->isKind(PNK_STAR));
6134 0 : ParseNode* lhs = MultiplyLeft(star);
6135 0 : ParseNode* rhs = MultiplyRight(star);
6136 :
6137 0 : Type lhsType;
6138 0 : if (!CheckExpr(f, lhs, &lhsType))
6139 0 : return false;
6140 :
6141 0 : Type rhsType;
6142 0 : if (!CheckExpr(f, rhs, &rhsType))
6143 0 : return false;
6144 :
6145 0 : if (lhsType.isInt() && rhsType.isInt()) {
6146 0 : if (!IsValidIntMultiplyConstant(f.m(), lhs) && !IsValidIntMultiplyConstant(f.m(), rhs))
6147 0 : return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal");
6148 0 : *type = Type::Intish;
6149 0 : return f.encoder().writeOp(Op::I32Mul);
6150 : }
6151 :
6152 0 : if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
6153 0 : *type = Type::Double;
6154 0 : return f.encoder().writeOp(Op::F64Mul);
6155 : }
6156 :
6157 0 : if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
6158 0 : *type = Type::Floatish;
6159 0 : return f.encoder().writeOp(Op::F32Mul);
6160 : }
6161 :
6162 0 : return f.fail(star, "multiply operands must be both int, both double? or both float?");
6163 : }
6164 :
6165 : static bool
6166 0 : CheckAddOrSub(FunctionValidator& f, ParseNode* expr, Type* type, unsigned* numAddOrSubOut = nullptr)
6167 : {
6168 0 : if (!CheckRecursionLimitDontReport(f.cx()))
6169 0 : return f.m().failOverRecursed();
6170 :
6171 0 : MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
6172 0 : ParseNode* lhs = AddSubLeft(expr);
6173 0 : ParseNode* rhs = AddSubRight(expr);
6174 :
6175 0 : Type lhsType, rhsType;
6176 : unsigned lhsNumAddOrSub, rhsNumAddOrSub;
6177 :
6178 0 : if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) {
6179 0 : if (!CheckAddOrSub(f, lhs, &lhsType, &lhsNumAddOrSub))
6180 0 : return false;
6181 0 : if (lhsType == Type::Intish)
6182 0 : lhsType = Type::Int;
6183 : } else {
6184 0 : if (!CheckExpr(f, lhs, &lhsType))
6185 0 : return false;
6186 0 : lhsNumAddOrSub = 0;
6187 : }
6188 :
6189 0 : if (rhs->isKind(PNK_ADD) || rhs->isKind(PNK_SUB)) {
6190 0 : if (!CheckAddOrSub(f, rhs, &rhsType, &rhsNumAddOrSub))
6191 0 : return false;
6192 0 : if (rhsType == Type::Intish)
6193 0 : rhsType = Type::Int;
6194 : } else {
6195 0 : if (!CheckExpr(f, rhs, &rhsType))
6196 0 : return false;
6197 0 : rhsNumAddOrSub = 0;
6198 : }
6199 :
6200 0 : unsigned numAddOrSub = lhsNumAddOrSub + rhsNumAddOrSub + 1;
6201 0 : if (numAddOrSub > (1<<20))
6202 0 : return f.fail(expr, "too many + or - without intervening coercion");
6203 :
6204 0 : if (lhsType.isInt() && rhsType.isInt()) {
6205 0 : if (!f.encoder().writeOp(expr->isKind(PNK_ADD) ? Op::I32Add : Op::I32Sub))
6206 0 : return false;
6207 0 : *type = Type::Intish;
6208 0 : } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
6209 0 : if (!f.encoder().writeOp(expr->isKind(PNK_ADD) ? Op::F64Add : Op::F64Sub))
6210 0 : return false;
6211 0 : *type = Type::Double;
6212 0 : } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
6213 0 : if (!f.encoder().writeOp(expr->isKind(PNK_ADD) ? Op::F32Add : Op::F32Sub))
6214 0 : return false;
6215 0 : *type = Type::Floatish;
6216 : } else {
6217 0 : return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s",
6218 0 : lhsType.toChars(), rhsType.toChars());
6219 : }
6220 :
6221 0 : if (numAddOrSubOut)
6222 0 : *numAddOrSubOut = numAddOrSub;
6223 0 : return true;
6224 : }
6225 :
6226 : static bool
6227 0 : CheckDivOrMod(FunctionValidator& f, ParseNode* expr, Type* type)
6228 : {
6229 0 : MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD));
6230 :
6231 0 : ParseNode* lhs = DivOrModLeft(expr);
6232 0 : ParseNode* rhs = DivOrModRight(expr);
6233 :
6234 0 : Type lhsType, rhsType;
6235 0 : if (!CheckExpr(f, lhs, &lhsType))
6236 0 : return false;
6237 0 : if (!CheckExpr(f, rhs, &rhsType))
6238 0 : return false;
6239 :
6240 0 : if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
6241 0 : *type = Type::Double;
6242 0 : if (expr->isKind(PNK_DIV))
6243 0 : return f.encoder().writeOp(Op::F64Div);
6244 0 : return f.encoder().writeOp(MozOp::F64Mod);
6245 : }
6246 :
6247 0 : if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
6248 0 : *type = Type::Floatish;
6249 0 : if (expr->isKind(PNK_DIV))
6250 0 : return f.encoder().writeOp(Op::F32Div);
6251 : else
6252 0 : return f.fail(expr, "modulo cannot receive float arguments");
6253 : }
6254 :
6255 0 : if (lhsType.isSigned() && rhsType.isSigned()) {
6256 0 : *type = Type::Intish;
6257 0 : return f.encoder().writeOp(expr->isKind(PNK_DIV) ? Op::I32DivS : Op::I32RemS);
6258 : }
6259 :
6260 0 : if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
6261 0 : *type = Type::Intish;
6262 0 : return f.encoder().writeOp(expr->isKind(PNK_DIV) ? Op::I32DivU : Op::I32RemU);
6263 : }
6264 :
6265 0 : return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; "
6266 0 : "%s and %s are given", lhsType.toChars(), rhsType.toChars());
6267 : }
6268 :
6269 : static bool
6270 0 : CheckComparison(FunctionValidator& f, ParseNode* comp, Type* type)
6271 : {
6272 0 : MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
6273 : comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
6274 :
6275 0 : ParseNode* lhs = ComparisonLeft(comp);
6276 0 : ParseNode* rhs = ComparisonRight(comp);
6277 :
6278 0 : Type lhsType, rhsType;
6279 0 : if (!CheckExpr(f, lhs, &lhsType))
6280 0 : return false;
6281 0 : if (!CheckExpr(f, rhs, &rhsType))
6282 0 : return false;
6283 :
6284 0 : if (!(lhsType.isSigned() && rhsType.isSigned()) &&
6285 0 : !(lhsType.isUnsigned() && rhsType.isUnsigned()) &&
6286 0 : !(lhsType.isDouble() && rhsType.isDouble()) &&
6287 0 : !(lhsType.isFloat() && rhsType.isFloat()))
6288 : {
6289 0 : return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
6290 0 : "%s and %s are given", lhsType.toChars(), rhsType.toChars());
6291 : }
6292 :
6293 : Op stmt;
6294 0 : if (lhsType.isSigned() && rhsType.isSigned()) {
6295 0 : switch (comp->getOp()) {
6296 0 : case JSOP_EQ: stmt = Op::I32Eq; break;
6297 0 : case JSOP_NE: stmt = Op::I32Ne; break;
6298 0 : case JSOP_LT: stmt = Op::I32LtS; break;
6299 0 : case JSOP_LE: stmt = Op::I32LeS; break;
6300 0 : case JSOP_GT: stmt = Op::I32GtS; break;
6301 0 : case JSOP_GE: stmt = Op::I32GeS; break;
6302 0 : default: MOZ_CRASH("unexpected comparison op");
6303 : }
6304 0 : } else if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
6305 0 : switch (comp->getOp()) {
6306 0 : case JSOP_EQ: stmt = Op::I32Eq; break;
6307 0 : case JSOP_NE: stmt = Op::I32Ne; break;
6308 0 : case JSOP_LT: stmt = Op::I32LtU; break;
6309 0 : case JSOP_LE: stmt = Op::I32LeU; break;
6310 0 : case JSOP_GT: stmt = Op::I32GtU; break;
6311 0 : case JSOP_GE: stmt = Op::I32GeU; break;
6312 0 : default: MOZ_CRASH("unexpected comparison op");
6313 : }
6314 0 : } else if (lhsType.isDouble()) {
6315 0 : switch (comp->getOp()) {
6316 0 : case JSOP_EQ: stmt = Op::F64Eq; break;
6317 0 : case JSOP_NE: stmt = Op::F64Ne; break;
6318 0 : case JSOP_LT: stmt = Op::F64Lt; break;
6319 0 : case JSOP_LE: stmt = Op::F64Le; break;
6320 0 : case JSOP_GT: stmt = Op::F64Gt; break;
6321 0 : case JSOP_GE: stmt = Op::F64Ge; break;
6322 0 : default: MOZ_CRASH("unexpected comparison op");
6323 : }
6324 0 : } else if (lhsType.isFloat()) {
6325 0 : switch (comp->getOp()) {
6326 0 : case JSOP_EQ: stmt = Op::F32Eq; break;
6327 0 : case JSOP_NE: stmt = Op::F32Ne; break;
6328 0 : case JSOP_LT: stmt = Op::F32Lt; break;
6329 0 : case JSOP_LE: stmt = Op::F32Le; break;
6330 0 : case JSOP_GT: stmt = Op::F32Gt; break;
6331 0 : case JSOP_GE: stmt = Op::F32Ge; break;
6332 0 : default: MOZ_CRASH("unexpected comparison op");
6333 : }
6334 : } else {
6335 0 : MOZ_CRASH("unexpected type");
6336 : }
6337 :
6338 0 : *type = Type::Int;
6339 0 : return f.encoder().writeOp(stmt);
6340 : }
6341 :
6342 : static bool
6343 0 : CheckBitwise(FunctionValidator& f, ParseNode* bitwise, Type* type)
6344 : {
6345 0 : ParseNode* lhs = BitwiseLeft(bitwise);
6346 0 : ParseNode* rhs = BitwiseRight(bitwise);
6347 :
6348 : int32_t identityElement;
6349 : bool onlyOnRight;
6350 0 : switch (bitwise->getKind()) {
6351 0 : case PNK_BITOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break;
6352 0 : case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed; break;
6353 0 : case PNK_BITXOR: identityElement = 0; onlyOnRight = false; *type = Type::Signed; break;
6354 0 : case PNK_LSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break;
6355 0 : case PNK_RSH: identityElement = 0; onlyOnRight = true; *type = Type::Signed; break;
6356 0 : case PNK_URSH: identityElement = 0; onlyOnRight = true; *type = Type::Unsigned; break;
6357 0 : default: MOZ_CRASH("not a bitwise op");
6358 : }
6359 :
6360 : uint32_t i;
6361 0 : if (!onlyOnRight && IsLiteralInt(f.m(), lhs, &i) && i == uint32_t(identityElement)) {
6362 0 : Type rhsType;
6363 0 : if (!CheckExpr(f, rhs, &rhsType))
6364 0 : return false;
6365 0 : if (!rhsType.isIntish())
6366 0 : return f.failf(bitwise, "%s is not a subtype of intish", rhsType.toChars());
6367 0 : return true;
6368 : }
6369 :
6370 0 : if (IsLiteralInt(f.m(), rhs, &i) && i == uint32_t(identityElement)) {
6371 0 : if (bitwise->isKind(PNK_BITOR) && lhs->isKind(PNK_CALL))
6372 0 : return CheckCoercedCall(f, lhs, Type::Int, type);
6373 :
6374 0 : Type lhsType;
6375 0 : if (!CheckExpr(f, lhs, &lhsType))
6376 0 : return false;
6377 0 : if (!lhsType.isIntish())
6378 0 : return f.failf(bitwise, "%s is not a subtype of intish", lhsType.toChars());
6379 0 : return true;
6380 : }
6381 :
6382 0 : Type lhsType;
6383 0 : if (!CheckExpr(f, lhs, &lhsType))
6384 0 : return false;
6385 :
6386 0 : Type rhsType;
6387 0 : if (!CheckExpr(f, rhs, &rhsType))
6388 0 : return false;
6389 :
6390 0 : if (!lhsType.isIntish())
6391 0 : return f.failf(lhs, "%s is not a subtype of intish", lhsType.toChars());
6392 0 : if (!rhsType.isIntish())
6393 0 : return f.failf(rhs, "%s is not a subtype of intish", rhsType.toChars());
6394 :
6395 0 : switch (bitwise->getKind()) {
6396 0 : case PNK_BITOR: if (!f.encoder().writeOp(Op::I32Or)) return false; break;
6397 0 : case PNK_BITAND: if (!f.encoder().writeOp(Op::I32And)) return false; break;
6398 0 : case PNK_BITXOR: if (!f.encoder().writeOp(Op::I32Xor)) return false; break;
6399 0 : case PNK_LSH: if (!f.encoder().writeOp(Op::I32Shl)) return false; break;
6400 0 : case PNK_RSH: if (!f.encoder().writeOp(Op::I32ShrS)) return false; break;
6401 0 : case PNK_URSH: if (!f.encoder().writeOp(Op::I32ShrU)) return false; break;
6402 0 : default: MOZ_CRASH("not a bitwise op");
6403 : }
6404 :
6405 0 : return true;
6406 : }
6407 :
6408 : static bool
6409 0 : CheckExpr(FunctionValidator& f, ParseNode* expr, Type* type)
6410 : {
6411 0 : if (!CheckRecursionLimitDontReport(f.cx()))
6412 0 : return f.m().failOverRecursed();
6413 :
6414 0 : bool isSimd = false;
6415 0 : if (IsNumericLiteral(f.m(), expr, &isSimd)) {
6416 0 : if (isSimd)
6417 0 : f.setUsesSimd();
6418 0 : return CheckNumericLiteral(f, expr, type);
6419 : }
6420 :
6421 0 : switch (expr->getKind()) {
6422 0 : case PNK_NAME: return CheckVarRef(f, expr, type);
6423 0 : case PNK_ELEM: return CheckLoadArray(f, expr, type);
6424 0 : case PNK_ASSIGN: return CheckAssign(f, expr, type);
6425 0 : case PNK_POS: return CheckPos(f, expr, type);
6426 0 : case PNK_NOT: return CheckNot(f, expr, type);
6427 0 : case PNK_NEG: return CheckNeg(f, expr, type);
6428 0 : case PNK_BITNOT: return CheckBitNot(f, expr, type);
6429 0 : case PNK_COMMA: return CheckComma(f, expr, type);
6430 0 : case PNK_CONDITIONAL: return CheckConditional(f, expr, type);
6431 0 : case PNK_STAR: return CheckMultiply(f, expr, type);
6432 0 : case PNK_CALL: return CheckUncoercedCall(f, expr, type);
6433 :
6434 : case PNK_ADD:
6435 0 : case PNK_SUB: return CheckAddOrSub(f, expr, type);
6436 :
6437 : case PNK_DIV:
6438 0 : case PNK_MOD: return CheckDivOrMod(f, expr, type);
6439 :
6440 : case PNK_LT:
6441 : case PNK_LE:
6442 : case PNK_GT:
6443 : case PNK_GE:
6444 : case PNK_EQ:
6445 0 : case PNK_NE: return CheckComparison(f, expr, type);
6446 :
6447 : case PNK_BITOR:
6448 : case PNK_BITAND:
6449 : case PNK_BITXOR:
6450 : case PNK_LSH:
6451 : case PNK_RSH:
6452 0 : case PNK_URSH: return CheckBitwise(f, expr, type);
6453 :
6454 : default:;
6455 : }
6456 :
6457 0 : return f.fail(expr, "unsupported expression");
6458 : }
6459 :
6460 : static bool
6461 : CheckStatement(FunctionValidator& f, ParseNode* stmt);
6462 :
6463 : static bool
6464 0 : CheckAsExprStatement(FunctionValidator& f, ParseNode* expr)
6465 : {
6466 0 : if (expr->isKind(PNK_CALL)) {
6467 0 : Type ignored;
6468 0 : return CheckCoercedCall(f, expr, Type::Void, &ignored);
6469 : }
6470 :
6471 0 : Type resultType;
6472 0 : if (!CheckExpr(f, expr, &resultType))
6473 0 : return false;
6474 :
6475 0 : if (!resultType.isVoid()) {
6476 0 : if (!f.encoder().writeOp(Op::Drop))
6477 0 : return false;
6478 : }
6479 :
6480 0 : return true;
6481 : }
6482 :
6483 : static bool
6484 0 : CheckExprStatement(FunctionValidator& f, ParseNode* exprStmt)
6485 : {
6486 0 : MOZ_ASSERT(exprStmt->isKind(PNK_SEMI));
6487 0 : ParseNode* expr = UnaryKid(exprStmt);
6488 0 : if (!expr)
6489 0 : return true;
6490 0 : return CheckAsExprStatement(f, expr);
6491 : }
6492 :
6493 : static bool
6494 0 : CheckLoopConditionOnEntry(FunctionValidator& f, ParseNode* cond)
6495 : {
6496 : uint32_t maybeLit;
6497 0 : if (IsLiteralInt(f.m(), cond, &maybeLit) && maybeLit)
6498 0 : return true;
6499 :
6500 0 : Type condType;
6501 0 : if (!CheckExpr(f, cond, &condType))
6502 0 : return false;
6503 0 : if (!condType.isInt())
6504 0 : return f.failf(cond, "%s is not a subtype of int", condType.toChars());
6505 :
6506 : // TODO change this to i32.eqz
6507 : // i32.eq 0 $f
6508 0 : if (!f.writeInt32Lit(0))
6509 0 : return false;
6510 0 : if (!f.encoder().writeOp(Op::I32Eq))
6511 0 : return false;
6512 :
6513 : // brIf (i32.eq 0 $f) $out
6514 0 : if (!f.writeBreakIf())
6515 0 : return false;
6516 :
6517 0 : return true;
6518 : }
6519 :
6520 : static bool
6521 0 : CheckWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
6522 : {
6523 0 : MOZ_ASSERT(whileStmt->isKind(PNK_WHILE));
6524 0 : ParseNode* cond = BinaryLeft(whileStmt);
6525 0 : ParseNode* body = BinaryRight(whileStmt);
6526 :
6527 : // A while loop `while(#cond) #body` is equivalent to:
6528 : // (block $after_loop
6529 : // (loop $top
6530 : // (brIf $after_loop (i32.eq 0 #cond))
6531 : // #body
6532 : // (br $top)
6533 : // )
6534 : // )
6535 0 : if (labels && !f.addLabels(*labels, 0, 1))
6536 0 : return false;
6537 :
6538 0 : if (!f.pushLoop())
6539 0 : return false;
6540 :
6541 0 : if (!CheckLoopConditionOnEntry(f, cond))
6542 0 : return false;
6543 0 : if (!CheckStatement(f, body))
6544 0 : return false;
6545 0 : if (!f.writeContinue())
6546 0 : return false;
6547 :
6548 0 : if (!f.popLoop())
6549 0 : return false;
6550 0 : if (labels)
6551 0 : f.removeLabels(*labels);
6552 0 : return true;
6553 : }
6554 :
6555 : static bool
6556 0 : CheckFor(FunctionValidator& f, ParseNode* forStmt, const NameVector* labels = nullptr)
6557 : {
6558 0 : MOZ_ASSERT(forStmt->isKind(PNK_FOR));
6559 0 : ParseNode* forHead = BinaryLeft(forStmt);
6560 0 : ParseNode* body = BinaryRight(forStmt);
6561 :
6562 0 : if (!forHead->isKind(PNK_FORHEAD))
6563 0 : return f.fail(forHead, "unsupported for-loop statement");
6564 :
6565 0 : ParseNode* maybeInit = TernaryKid1(forHead);
6566 0 : ParseNode* maybeCond = TernaryKid2(forHead);
6567 0 : ParseNode* maybeInc = TernaryKid3(forHead);
6568 :
6569 : // A for-loop `for (#init; #cond; #inc) #body` is equivalent to:
6570 : // (block // depth X
6571 : // (#init)
6572 : // (block $after_loop // depth X+1 (block)
6573 : // (loop $loop_top // depth X+2 (loop)
6574 : // (brIf $after (eq 0 #cond))
6575 : // (block $after_body #body) // depth X+3
6576 : // #inc
6577 : // (br $loop_top)
6578 : // )
6579 : // )
6580 : // )
6581 : // A break in the body should break out to $after_loop, i.e. depth + 1.
6582 : // A continue in the body should break out to $after_body, i.e. depth + 3.
6583 0 : if (labels && !f.addLabels(*labels, 1, 3))
6584 0 : return false;
6585 :
6586 0 : if (!f.pushUnbreakableBlock())
6587 0 : return false;
6588 :
6589 0 : if (maybeInit && !CheckAsExprStatement(f, maybeInit))
6590 0 : return false;
6591 :
6592 : {
6593 0 : if (!f.pushLoop())
6594 0 : return false;
6595 :
6596 0 : if (maybeCond && !CheckLoopConditionOnEntry(f, maybeCond))
6597 0 : return false;
6598 :
6599 : {
6600 : // Continuing in the body should just break out to the increment.
6601 0 : if (!f.pushContinuableBlock())
6602 0 : return false;
6603 0 : if (!CheckStatement(f, body))
6604 0 : return false;
6605 0 : if (!f.popContinuableBlock())
6606 0 : return false;
6607 : }
6608 :
6609 0 : if (maybeInc && !CheckAsExprStatement(f, maybeInc))
6610 0 : return false;
6611 :
6612 0 : if (!f.writeContinue())
6613 0 : return false;
6614 0 : if (!f.popLoop())
6615 0 : return false;
6616 : }
6617 :
6618 0 : if (!f.popUnbreakableBlock())
6619 0 : return false;
6620 :
6621 0 : if (labels)
6622 0 : f.removeLabels(*labels);
6623 :
6624 0 : return true;
6625 : }
6626 :
6627 : static bool
6628 0 : CheckDoWhile(FunctionValidator& f, ParseNode* whileStmt, const NameVector* labels = nullptr)
6629 : {
6630 0 : MOZ_ASSERT(whileStmt->isKind(PNK_DOWHILE));
6631 0 : ParseNode* body = BinaryLeft(whileStmt);
6632 0 : ParseNode* cond = BinaryRight(whileStmt);
6633 :
6634 : // A do-while loop `do { #body } while (#cond)` is equivalent to:
6635 : // (block $after_loop // depth X
6636 : // (loop $top // depth X+1
6637 : // (block #body) // depth X+2
6638 : // (brIf #cond $top)
6639 : // )
6640 : // )
6641 : // A break should break out of the entire loop, i.e. at depth 0.
6642 : // A continue should break out to the condition, i.e. at depth 2.
6643 0 : if (labels && !f.addLabels(*labels, 0, 2))
6644 0 : return false;
6645 :
6646 0 : if (!f.pushLoop())
6647 0 : return false;
6648 :
6649 : {
6650 : // An unlabeled continue in the body should break out to the condition.
6651 0 : if (!f.pushContinuableBlock())
6652 0 : return false;
6653 0 : if (!CheckStatement(f, body))
6654 0 : return false;
6655 0 : if (!f.popContinuableBlock())
6656 0 : return false;
6657 : }
6658 :
6659 0 : Type condType;
6660 0 : if (!CheckExpr(f, cond, &condType))
6661 0 : return false;
6662 0 : if (!condType.isInt())
6663 0 : return f.failf(cond, "%s is not a subtype of int", condType.toChars());
6664 :
6665 0 : if (!f.writeContinueIf())
6666 0 : return false;
6667 :
6668 0 : if (!f.popLoop())
6669 0 : return false;
6670 0 : if (labels)
6671 0 : f.removeLabels(*labels);
6672 0 : return true;
6673 : }
6674 :
6675 : static bool CheckStatementList(FunctionValidator& f, ParseNode*, const NameVector* = nullptr);
6676 :
6677 : static bool
6678 0 : CheckLabel(FunctionValidator& f, ParseNode* labeledStmt)
6679 : {
6680 0 : MOZ_ASSERT(labeledStmt->isKind(PNK_LABEL));
6681 :
6682 0 : NameVector labels;
6683 0 : ParseNode* innermost = labeledStmt;
6684 0 : do {
6685 0 : if (!labels.append(LabeledStatementLabel(innermost)))
6686 0 : return false;
6687 0 : innermost = LabeledStatementStatement(innermost);
6688 0 : } while (innermost->getKind() == PNK_LABEL);
6689 :
6690 0 : switch (innermost->getKind()) {
6691 : case PNK_FOR:
6692 0 : return CheckFor(f, innermost, &labels);
6693 : case PNK_DOWHILE:
6694 0 : return CheckDoWhile(f, innermost, &labels);
6695 : case PNK_WHILE:
6696 0 : return CheckWhile(f, innermost, &labels);
6697 : case PNK_STATEMENTLIST:
6698 0 : return CheckStatementList(f, innermost, &labels);
6699 : default:
6700 0 : break;
6701 : }
6702 :
6703 0 : if (!f.pushUnbreakableBlock(&labels))
6704 0 : return false;
6705 :
6706 0 : if (!CheckStatement(f, innermost))
6707 0 : return false;
6708 :
6709 0 : if (!f.popUnbreakableBlock(&labels))
6710 0 : return false;
6711 0 : return true;
6712 : }
6713 :
6714 : static bool
6715 0 : CheckIf(FunctionValidator& f, ParseNode* ifStmt)
6716 : {
6717 0 : uint32_t numIfEnd = 1;
6718 :
6719 : recurse:
6720 0 : MOZ_ASSERT(ifStmt->isKind(PNK_IF));
6721 0 : ParseNode* cond = TernaryKid1(ifStmt);
6722 0 : ParseNode* thenStmt = TernaryKid2(ifStmt);
6723 0 : ParseNode* elseStmt = TernaryKid3(ifStmt);
6724 :
6725 0 : Type condType;
6726 0 : if (!CheckExpr(f, cond, &condType))
6727 0 : return false;
6728 0 : if (!condType.isInt())
6729 0 : return f.failf(cond, "%s is not a subtype of int", condType.toChars());
6730 :
6731 : size_t typeAt;
6732 0 : if (!f.pushIf(&typeAt))
6733 0 : return false;
6734 :
6735 0 : f.setIfType(typeAt, ExprType::Void);
6736 :
6737 0 : if (!CheckStatement(f, thenStmt))
6738 0 : return false;
6739 :
6740 0 : if (elseStmt) {
6741 0 : if (!f.switchToElse())
6742 0 : return false;
6743 :
6744 0 : if (elseStmt->isKind(PNK_IF)) {
6745 0 : ifStmt = elseStmt;
6746 0 : if (numIfEnd++ == UINT32_MAX)
6747 0 : return false;
6748 0 : goto recurse;
6749 : }
6750 :
6751 0 : if (!CheckStatement(f, elseStmt))
6752 0 : return false;
6753 : }
6754 :
6755 0 : for (uint32_t i = 0; i != numIfEnd; ++i) {
6756 0 : if (!f.popIf())
6757 0 : return false;
6758 : }
6759 :
6760 0 : return true;
6761 : }
6762 :
6763 : static bool
6764 0 : CheckCaseExpr(FunctionValidator& f, ParseNode* caseExpr, int32_t* value)
6765 : {
6766 0 : if (!IsNumericLiteral(f.m(), caseExpr))
6767 0 : return f.fail(caseExpr, "switch case expression must be an integer literal");
6768 :
6769 0 : NumLit lit = ExtractNumericLiteral(f.m(), caseExpr);
6770 0 : switch (lit.which()) {
6771 : case NumLit::Fixnum:
6772 : case NumLit::NegativeInt:
6773 0 : *value = lit.toInt32();
6774 0 : break;
6775 : case NumLit::OutOfRangeInt:
6776 : case NumLit::BigUnsigned:
6777 0 : return f.fail(caseExpr, "switch case expression out of integer range");
6778 : case NumLit::Double:
6779 : case NumLit::Float:
6780 : case NumLit::Int8x16:
6781 : case NumLit::Uint8x16:
6782 : case NumLit::Int16x8:
6783 : case NumLit::Uint16x8:
6784 : case NumLit::Int32x4:
6785 : case NumLit::Uint32x4:
6786 : case NumLit::Float32x4:
6787 : case NumLit::Bool8x16:
6788 : case NumLit::Bool16x8:
6789 : case NumLit::Bool32x4:
6790 0 : return f.fail(caseExpr, "switch case expression must be an integer literal");
6791 : }
6792 :
6793 0 : return true;
6794 : }
6795 :
6796 : static bool
6797 0 : CheckDefaultAtEnd(FunctionValidator& f, ParseNode* stmt)
6798 : {
6799 0 : for (; stmt; stmt = NextNode(stmt)) {
6800 0 : if (IsDefaultCase(stmt) && NextNode(stmt) != nullptr)
6801 0 : return f.fail(stmt, "default label must be at the end");
6802 : }
6803 :
6804 0 : return true;
6805 : }
6806 :
6807 : static bool
6808 0 : CheckSwitchRange(FunctionValidator& f, ParseNode* stmt, int32_t* low, int32_t* high,
6809 : uint32_t* tableLength)
6810 : {
6811 0 : if (IsDefaultCase(stmt)) {
6812 0 : *low = 0;
6813 0 : *high = -1;
6814 0 : *tableLength = 0;
6815 0 : return true;
6816 : }
6817 :
6818 0 : int32_t i = 0;
6819 0 : if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
6820 0 : return false;
6821 :
6822 0 : *low = *high = i;
6823 :
6824 0 : ParseNode* initialStmt = stmt;
6825 0 : for (stmt = NextNode(stmt); stmt && !IsDefaultCase(stmt); stmt = NextNode(stmt)) {
6826 0 : int32_t i = 0;
6827 0 : if (!CheckCaseExpr(f, CaseExpr(stmt), &i))
6828 0 : return false;
6829 :
6830 0 : *low = Min(*low, i);
6831 0 : *high = Max(*high, i);
6832 : }
6833 :
6834 0 : int64_t i64 = (int64_t(*high) - int64_t(*low)) + 1;
6835 0 : if (i64 > MaxBrTableElems)
6836 0 : return f.fail(initialStmt, "all switch statements generate tables; this table would be too big");
6837 :
6838 0 : *tableLength = uint32_t(i64);
6839 0 : return true;
6840 : }
6841 :
6842 : static bool
6843 0 : CheckSwitchExpr(FunctionValidator& f, ParseNode* switchExpr)
6844 : {
6845 0 : Type exprType;
6846 0 : if (!CheckExpr(f, switchExpr, &exprType))
6847 0 : return false;
6848 0 : if (!exprType.isSigned())
6849 0 : return f.failf(switchExpr, "%s is not a subtype of signed", exprType.toChars());
6850 0 : return true;
6851 : }
6852 :
6853 : // A switch will be constructed as:
6854 : // - the default block wrapping all the other blocks, to be able to break
6855 : // out of the switch with an unlabeled break statement. It has two statements
6856 : // (an inner block and the default expr). asm.js rules require default to be at
6857 : // the end, so the default block always encloses all the cases blocks.
6858 : // - one block per case between low and high; undefined cases just jump to the
6859 : // default case. Each of these blocks contain two statements: the next case's
6860 : // block and the possibly empty statement list comprising the case body. The
6861 : // last block pushed is the first case so the (relative) branch target therefore
6862 : // matches the sequential order of cases.
6863 : // - one block for the br_table, so that the first break goes to the first
6864 : // case's block.
6865 : static bool
6866 0 : CheckSwitch(FunctionValidator& f, ParseNode* switchStmt)
6867 : {
6868 0 : MOZ_ASSERT(switchStmt->isKind(PNK_SWITCH));
6869 :
6870 0 : ParseNode* switchExpr = BinaryLeft(switchStmt);
6871 0 : ParseNode* switchBody = BinaryRight(switchStmt);
6872 :
6873 0 : if (switchBody->isKind(PNK_LEXICALSCOPE)) {
6874 0 : if (!switchBody->isEmptyScope())
6875 0 : return f.fail(switchBody, "switch body may not contain lexical declarations");
6876 0 : switchBody = switchBody->scopeBody();
6877 : }
6878 :
6879 0 : ParseNode* stmt = ListHead(switchBody);
6880 0 : if (!stmt) {
6881 0 : if (!CheckSwitchExpr(f, switchExpr))
6882 0 : return false;
6883 0 : if (!f.encoder().writeOp(Op::Drop))
6884 0 : return false;
6885 0 : return true;
6886 : }
6887 :
6888 0 : if (!CheckDefaultAtEnd(f, stmt))
6889 0 : return false;
6890 :
6891 0 : int32_t low = 0, high = 0;
6892 0 : uint32_t tableLength = 0;
6893 0 : if (!CheckSwitchRange(f, stmt, &low, &high, &tableLength))
6894 0 : return false;
6895 :
6896 : static const uint32_t CASE_NOT_DEFINED = UINT32_MAX;
6897 :
6898 0 : Uint32Vector caseDepths;
6899 0 : if (!caseDepths.appendN(CASE_NOT_DEFINED, tableLength))
6900 0 : return false;
6901 :
6902 0 : uint32_t numCases = 0;
6903 0 : for (ParseNode* s = stmt; s && !IsDefaultCase(s); s = NextNode(s)) {
6904 0 : int32_t caseValue = ExtractNumericLiteral(f.m(), CaseExpr(s)).toInt32();
6905 :
6906 0 : MOZ_ASSERT(caseValue >= low);
6907 0 : unsigned i = caseValue - low;
6908 0 : if (caseDepths[i] != CASE_NOT_DEFINED)
6909 0 : return f.fail(s, "no duplicate case labels");
6910 :
6911 0 : MOZ_ASSERT(numCases != CASE_NOT_DEFINED);
6912 0 : caseDepths[i] = numCases++;
6913 : }
6914 :
6915 : // Open the wrapping breakable default block.
6916 0 : if (!f.pushBreakableBlock())
6917 0 : return false;
6918 :
6919 : // Open all the case blocks.
6920 0 : for (uint32_t i = 0; i < numCases; i++) {
6921 0 : if (!f.pushUnbreakableBlock())
6922 0 : return false;
6923 : }
6924 :
6925 : // Open the br_table block.
6926 0 : if (!f.pushUnbreakableBlock())
6927 0 : return false;
6928 :
6929 : // The default block is the last one.
6930 0 : uint32_t defaultDepth = numCases;
6931 :
6932 : // Subtract lowest case value, so that all the cases start from 0.
6933 0 : if (low) {
6934 0 : if (!CheckSwitchExpr(f, switchExpr))
6935 0 : return false;
6936 0 : if (!f.writeInt32Lit(low))
6937 0 : return false;
6938 0 : if (!f.encoder().writeOp(Op::I32Sub))
6939 0 : return false;
6940 : } else {
6941 0 : if (!CheckSwitchExpr(f, switchExpr))
6942 0 : return false;
6943 : }
6944 :
6945 : // Start the br_table block.
6946 0 : if (!f.encoder().writeOp(Op::BrTable))
6947 0 : return false;
6948 :
6949 : // Write the number of cases (tableLength - 1 + 1 (default)).
6950 : // Write the number of cases (tableLength - 1 + 1 (default)).
6951 0 : if (!f.encoder().writeVarU32(tableLength))
6952 0 : return false;
6953 :
6954 : // Each case value describes the relative depth to the actual block. When
6955 : // a case is not explicitly defined, it goes to the default.
6956 0 : for (size_t i = 0; i < tableLength; i++) {
6957 0 : uint32_t target = caseDepths[i] == CASE_NOT_DEFINED ? defaultDepth : caseDepths[i];
6958 0 : if (!f.encoder().writeVarU32(target))
6959 0 : return false;
6960 : }
6961 :
6962 : // Write the default depth.
6963 0 : if (!f.encoder().writeVarU32(defaultDepth))
6964 0 : return false;
6965 :
6966 : // Our br_table is done. Close its block, write the cases down in order.
6967 0 : if (!f.popUnbreakableBlock())
6968 0 : return false;
6969 :
6970 0 : for (; stmt && !IsDefaultCase(stmt); stmt = NextNode(stmt)) {
6971 0 : if (!CheckStatement(f, CaseBody(stmt)))
6972 0 : return false;
6973 0 : if (!f.popUnbreakableBlock())
6974 0 : return false;
6975 : }
6976 :
6977 : // Write the default block.
6978 0 : if (stmt && IsDefaultCase(stmt)) {
6979 0 : if (!CheckStatement(f, CaseBody(stmt)))
6980 0 : return false;
6981 : }
6982 :
6983 : // Close the wrapping block.
6984 0 : if (!f.popBreakableBlock())
6985 0 : return false;
6986 0 : return true;
6987 : }
6988 :
6989 : static bool
6990 0 : CheckReturnType(FunctionValidator& f, ParseNode* usepn, Type ret)
6991 : {
6992 0 : if (!f.hasAlreadyReturned()) {
6993 0 : f.setReturnedType(ret.canonicalToExprType());
6994 0 : return true;
6995 : }
6996 :
6997 0 : if (f.returnedType() != ret.canonicalToExprType()) {
6998 0 : return f.failf(usepn, "%s incompatible with previous return of type %s",
6999 0 : Type::ret(ret).toChars(), ToCString(f.returnedType()));
7000 : }
7001 :
7002 0 : return true;
7003 : }
7004 :
7005 : static bool
7006 0 : CheckReturn(FunctionValidator& f, ParseNode* returnStmt)
7007 : {
7008 0 : ParseNode* expr = ReturnExpr(returnStmt);
7009 :
7010 0 : if (!expr) {
7011 0 : if (!CheckReturnType(f, returnStmt, Type::Void))
7012 0 : return false;
7013 : } else {
7014 0 : Type type;
7015 0 : if (!CheckExpr(f, expr, &type))
7016 0 : return false;
7017 :
7018 0 : if (!type.isReturnType())
7019 0 : return f.failf(expr, "%s is not a valid return type", type.toChars());
7020 :
7021 0 : if (!CheckReturnType(f, expr, Type::canonicalize(type)))
7022 0 : return false;
7023 : }
7024 :
7025 0 : if (!f.encoder().writeOp(Op::Return))
7026 0 : return false;
7027 :
7028 0 : return true;
7029 : }
7030 :
7031 : static bool
7032 0 : CheckStatementList(FunctionValidator& f, ParseNode* stmtList, const NameVector* labels /*= nullptr */)
7033 : {
7034 0 : MOZ_ASSERT(stmtList->isKind(PNK_STATEMENTLIST));
7035 :
7036 0 : if (!f.pushUnbreakableBlock(labels))
7037 0 : return false;
7038 :
7039 0 : for (ParseNode* stmt = ListHead(stmtList); stmt; stmt = NextNode(stmt)) {
7040 0 : if (!CheckStatement(f, stmt))
7041 0 : return false;
7042 : }
7043 :
7044 0 : if (!f.popUnbreakableBlock(labels))
7045 0 : return false;
7046 0 : return true;
7047 : }
7048 :
7049 : static bool
7050 0 : CheckLexicalScope(FunctionValidator& f, ParseNode* lexicalScope)
7051 : {
7052 0 : MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE));
7053 :
7054 0 : if (!lexicalScope->isEmptyScope())
7055 0 : return f.fail(lexicalScope, "cannot have 'let' or 'const' declarations");
7056 :
7057 0 : return CheckStatement(f, lexicalScope->scopeBody());
7058 : }
7059 :
7060 : static bool
7061 0 : CheckBreakOrContinue(FunctionValidator& f, bool isBreak, ParseNode* stmt)
7062 : {
7063 0 : if (PropertyName* maybeLabel = LoopControlMaybeLabel(stmt))
7064 0 : return f.writeLabeledBreakOrContinue(maybeLabel, isBreak);
7065 0 : return f.writeUnlabeledBreakOrContinue(isBreak);
7066 : }
7067 :
7068 : static bool
7069 0 : CheckStatement(FunctionValidator& f, ParseNode* stmt)
7070 : {
7071 0 : if (!CheckRecursionLimitDontReport(f.cx()))
7072 0 : return f.m().failOverRecursed();
7073 :
7074 0 : switch (stmt->getKind()) {
7075 0 : case PNK_SEMI: return CheckExprStatement(f, stmt);
7076 0 : case PNK_WHILE: return CheckWhile(f, stmt);
7077 0 : case PNK_FOR: return CheckFor(f, stmt);
7078 0 : case PNK_DOWHILE: return CheckDoWhile(f, stmt);
7079 0 : case PNK_LABEL: return CheckLabel(f, stmt);
7080 0 : case PNK_IF: return CheckIf(f, stmt);
7081 0 : case PNK_SWITCH: return CheckSwitch(f, stmt);
7082 0 : case PNK_RETURN: return CheckReturn(f, stmt);
7083 0 : case PNK_STATEMENTLIST: return CheckStatementList(f, stmt);
7084 0 : case PNK_BREAK: return CheckBreakOrContinue(f, true, stmt);
7085 0 : case PNK_CONTINUE: return CheckBreakOrContinue(f, false, stmt);
7086 0 : case PNK_LEXICALSCOPE: return CheckLexicalScope(f, stmt);
7087 : default:;
7088 : }
7089 :
7090 0 : return f.fail(stmt, "unexpected statement kind");
7091 : }
7092 :
7093 : static bool
7094 0 : ParseFunction(ModuleValidator& m, ParseNode** fnOut, unsigned* line)
7095 : {
7096 0 : TokenStream& tokenStream = m.tokenStream();
7097 :
7098 0 : tokenStream.consumeKnownToken(TOK_FUNCTION, TokenStream::Operand);
7099 0 : uint32_t toStringStart = tokenStream.currentToken().pos.begin;
7100 0 : *line = tokenStream.srcCoords.lineNum(tokenStream.currentToken().pos.end);
7101 :
7102 : TokenKind tk;
7103 0 : if (!tokenStream.getToken(&tk, TokenStream::Operand))
7104 0 : return false;
7105 0 : if (!TokenKindIsPossibleIdentifier(tk))
7106 0 : return false; // The regular parser will throw a SyntaxError, no need to m.fail.
7107 :
7108 0 : RootedPropertyName name(m.cx(), m.parser().bindingIdentifier(YieldIsName));
7109 0 : if (!name)
7110 0 : return false;
7111 :
7112 0 : ParseNode* fn = m.parser().handler.newFunctionStatement(m.parser().pos());
7113 0 : if (!fn)
7114 0 : return false;
7115 :
7116 0 : RootedFunction& fun = m.dummyFunction();
7117 0 : fun->setAtom(name);
7118 0 : fun->setArgCount(0);
7119 :
7120 0 : ParseContext* outerpc = m.parser().pc;
7121 0 : Directives directives(outerpc);
7122 0 : FunctionBox* funbox = m.parser().newFunctionBox(fn, fun, toStringStart, directives,
7123 0 : NotGenerator, SyncFunction);
7124 0 : if (!funbox)
7125 0 : return false;
7126 0 : funbox->initWithEnclosingParseContext(outerpc, frontend::Statement);
7127 :
7128 0 : Directives newDirectives = directives;
7129 0 : ParseContext funpc(&m.parser(), funbox, &newDirectives);
7130 0 : if (!funpc.init())
7131 0 : return false;
7132 :
7133 0 : if (!m.parser().functionFormalParametersAndBody(InAllowed, YieldIsName, fn, Statement)) {
7134 0 : if (tokenStream.hadError() || directives == newDirectives)
7135 0 : return false;
7136 :
7137 0 : return m.fail(fn, "encountered new directive in function");
7138 : }
7139 :
7140 0 : MOZ_ASSERT(!tokenStream.hadError());
7141 0 : MOZ_ASSERT(directives == newDirectives);
7142 :
7143 0 : *fnOut = fn;
7144 0 : return true;
7145 : }
7146 :
7147 : static bool
7148 0 : CheckFunction(ModuleValidator& m)
7149 : {
7150 : // asm.js modules can be quite large when represented as parse trees so pop
7151 : // the backing LifoAlloc after parsing/compiling each function.
7152 0 : AsmJSParser::Mark mark = m.parser().mark();
7153 :
7154 0 : ParseNode* fn = nullptr;
7155 0 : unsigned line = 0;
7156 0 : if (!ParseFunction(m, &fn, &line))
7157 0 : return false;
7158 :
7159 0 : if (!CheckFunctionHead(m, fn))
7160 0 : return false;
7161 :
7162 0 : FunctionValidator f(m, fn);
7163 0 : if (!f.init(FunctionName(fn), line))
7164 0 : return m.fail(fn, "internal compiler failure (probably out of memory)");
7165 :
7166 0 : ParseNode* stmtIter = ListHead(FunctionStatementList(fn));
7167 :
7168 0 : if (!CheckProcessingDirectives(m, &stmtIter))
7169 0 : return false;
7170 :
7171 0 : ValTypeVector args;
7172 0 : if (!CheckArguments(f, &stmtIter, &args))
7173 0 : return false;
7174 :
7175 0 : if (!CheckVariables(f, &stmtIter))
7176 0 : return false;
7177 :
7178 0 : ParseNode* lastNonEmptyStmt = nullptr;
7179 0 : for (; stmtIter; stmtIter = NextNonEmptyStatement(stmtIter)) {
7180 0 : lastNonEmptyStmt = stmtIter;
7181 0 : if (!CheckStatement(f, stmtIter))
7182 0 : return false;
7183 : }
7184 :
7185 0 : if (!CheckFinalReturn(f, lastNonEmptyStmt))
7186 0 : return false;
7187 :
7188 0 : ModuleValidator::Func* func = nullptr;
7189 0 : if (!CheckFunctionSignature(m, fn, Sig(Move(args), f.returnedType()), FunctionName(fn), &func))
7190 0 : return false;
7191 :
7192 0 : if (func->defined())
7193 0 : return m.failName(fn, "function '%s' already defined", FunctionName(fn));
7194 :
7195 0 : func->define(fn);
7196 :
7197 0 : if (!f.finish(func->index()))
7198 0 : return m.fail(fn, "internal compiler failure (probably out of memory)");
7199 :
7200 : // Release the parser's lifo memory only after the last use of a parse node.
7201 0 : m.parser().release(mark);
7202 0 : return true;
7203 : }
7204 :
7205 : static bool
7206 0 : CheckAllFunctionsDefined(ModuleValidator& m)
7207 : {
7208 0 : for (unsigned i = 0; i < m.numFunctions(); i++) {
7209 0 : ModuleValidator::Func& f = m.function(i);
7210 0 : if (!f.defined())
7211 0 : return m.failNameOffset(f.firstUse(), "missing definition of function %s", f.name());
7212 : }
7213 :
7214 0 : return true;
7215 : }
7216 :
7217 : static bool
7218 0 : CheckFunctions(ModuleValidator& m)
7219 : {
7220 : while (true) {
7221 : TokenKind tk;
7222 0 : if (!PeekToken(m.parser(), &tk))
7223 0 : return false;
7224 :
7225 0 : if (tk != TOK_FUNCTION)
7226 0 : break;
7227 :
7228 0 : if (!CheckFunction(m))
7229 0 : return false;
7230 0 : }
7231 :
7232 0 : return CheckAllFunctionsDefined(m);
7233 : }
7234 :
7235 : static bool
7236 0 : CheckFuncPtrTable(ModuleValidator& m, ParseNode* var)
7237 : {
7238 0 : if (!var->isKind(PNK_NAME))
7239 0 : return m.fail(var, "function-pointer table name is not a plain name");
7240 :
7241 0 : ParseNode* arrayLiteral = MaybeInitializer(var);
7242 0 : if (!arrayLiteral || !arrayLiteral->isKind(PNK_ARRAY))
7243 0 : return m.fail(var, "function-pointer table's initializer must be an array literal");
7244 :
7245 0 : unsigned length = ListLength(arrayLiteral);
7246 :
7247 0 : if (!IsPowerOfTwo(length))
7248 0 : return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length);
7249 :
7250 0 : unsigned mask = length - 1;
7251 :
7252 0 : Uint32Vector elemFuncIndices;
7253 0 : const Sig* sig = nullptr;
7254 0 : for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
7255 0 : if (!elem->isKind(PNK_NAME))
7256 0 : return m.fail(elem, "function-pointer table's elements must be names of functions");
7257 :
7258 0 : PropertyName* funcName = elem->name();
7259 0 : const ModuleValidator::Func* func = m.lookupFunction(funcName);
7260 0 : if (!func)
7261 0 : return m.fail(elem, "function-pointer table's elements must be names of functions");
7262 :
7263 0 : const Sig& funcSig = m.mg().funcSig(func->index());
7264 0 : if (sig) {
7265 0 : if (*sig != funcSig)
7266 0 : return m.fail(elem, "all functions in table must have same signature");
7267 : } else {
7268 0 : sig = &funcSig;
7269 : }
7270 :
7271 0 : if (!elemFuncIndices.append(func->index()))
7272 0 : return false;
7273 : }
7274 :
7275 0 : Sig copy;
7276 0 : if (!copy.clone(*sig))
7277 0 : return false;
7278 :
7279 : uint32_t tableIndex;
7280 0 : if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), Move(copy), mask, &tableIndex))
7281 0 : return false;
7282 :
7283 0 : if (!m.defineFuncPtrTable(tableIndex, Move(elemFuncIndices)))
7284 0 : return m.fail(var, "duplicate function-pointer definition");
7285 :
7286 0 : return true;
7287 : }
7288 :
7289 : static bool
7290 0 : CheckFuncPtrTables(ModuleValidator& m)
7291 : {
7292 : while (true) {
7293 : ParseNode* varStmt;
7294 0 : if (!ParseVarOrConstStatement(m.parser(), &varStmt))
7295 0 : return false;
7296 0 : if (!varStmt)
7297 0 : break;
7298 0 : for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
7299 0 : if (!CheckFuncPtrTable(m, var))
7300 0 : return false;
7301 : }
7302 0 : }
7303 :
7304 0 : for (unsigned i = 0; i < m.numFuncPtrTables(); i++) {
7305 0 : ModuleValidator::FuncPtrTable& funcPtrTable = m.funcPtrTable(i);
7306 0 : if (!funcPtrTable.defined()) {
7307 0 : return m.failNameOffset(funcPtrTable.firstUse(),
7308 : "function-pointer table %s wasn't defined",
7309 0 : funcPtrTable.name());
7310 : }
7311 : }
7312 :
7313 0 : return true;
7314 : }
7315 :
7316 : static bool
7317 0 : CheckModuleExportFunction(ModuleValidator& m, ParseNode* pn, PropertyName* maybeFieldName = nullptr)
7318 : {
7319 0 : if (!pn->isKind(PNK_NAME))
7320 0 : return m.fail(pn, "expected name of exported function");
7321 :
7322 0 : PropertyName* funcName = pn->name();
7323 0 : const ModuleValidator::Func* func = m.lookupFunction(funcName);
7324 0 : if (!func)
7325 0 : return m.failName(pn, "function '%s' not found", funcName);
7326 :
7327 0 : return m.addExportField(pn, *func, maybeFieldName);
7328 : }
7329 :
7330 : static bool
7331 0 : CheckModuleExportObject(ModuleValidator& m, ParseNode* object)
7332 : {
7333 0 : MOZ_ASSERT(object->isKind(PNK_OBJECT));
7334 :
7335 0 : for (ParseNode* pn = ListHead(object); pn; pn = NextNode(pn)) {
7336 0 : if (!IsNormalObjectField(m.cx(), pn))
7337 0 : return m.fail(pn, "only normal object properties may be used in the export object literal");
7338 :
7339 0 : PropertyName* fieldName = ObjectNormalFieldName(m.cx(), pn);
7340 :
7341 0 : ParseNode* initNode = ObjectNormalFieldInitializer(m.cx(), pn);
7342 0 : if (!initNode->isKind(PNK_NAME))
7343 0 : return m.fail(initNode, "initializer of exported object literal must be name of function");
7344 :
7345 0 : if (!CheckModuleExportFunction(m, initNode, fieldName))
7346 0 : return false;
7347 : }
7348 :
7349 0 : return true;
7350 : }
7351 :
7352 : static bool
7353 0 : CheckModuleReturn(ModuleValidator& m)
7354 : {
7355 : TokenKind tk;
7356 0 : if (!GetToken(m.parser(), &tk))
7357 0 : return false;
7358 0 : TokenStream& ts = m.parser().tokenStream;
7359 0 : if (tk != TOK_RETURN) {
7360 0 : return m.failCurrentOffset((tk == TOK_RC || tk == TOK_EOF)
7361 : ? "expecting return statement"
7362 0 : : "invalid asm.js. statement");
7363 : }
7364 0 : ts.ungetToken();
7365 :
7366 0 : ParseNode* returnStmt = m.parser().statementListItem(YieldIsName);
7367 0 : if (!returnStmt)
7368 0 : return false;
7369 :
7370 0 : ParseNode* returnExpr = ReturnExpr(returnStmt);
7371 0 : if (!returnExpr)
7372 0 : return m.fail(returnStmt, "export statement must return something");
7373 :
7374 0 : if (returnExpr->isKind(PNK_OBJECT)) {
7375 0 : if (!CheckModuleExportObject(m, returnExpr))
7376 0 : return false;
7377 : } else {
7378 0 : if (!CheckModuleExportFunction(m, returnExpr))
7379 0 : return false;
7380 : }
7381 :
7382 0 : return true;
7383 : }
7384 :
7385 : static bool
7386 0 : CheckModuleEnd(ModuleValidator &m)
7387 : {
7388 : TokenKind tk;
7389 0 : if (!GetToken(m.parser(), &tk))
7390 0 : return false;
7391 :
7392 0 : if (tk != TOK_EOF && tk != TOK_RC)
7393 0 : return m.failCurrentOffset("top-level export (return) must be the last statement");
7394 :
7395 0 : m.parser().tokenStream.ungetToken();
7396 0 : return true;
7397 : }
7398 :
7399 : static SharedModule
7400 0 : CheckModule(JSContext* cx, AsmJSParser& parser, ParseNode* stmtList, unsigned* time)
7401 : {
7402 0 : int64_t before = PRMJ_Now();
7403 :
7404 0 : ParseNode* moduleFunctionNode = parser.pc->functionBox()->functionNode;
7405 0 : MOZ_ASSERT(moduleFunctionNode);
7406 :
7407 0 : ModuleValidator m(cx, parser, moduleFunctionNode);
7408 0 : if (!m.init())
7409 0 : return nullptr;
7410 :
7411 0 : if (!CheckFunctionHead(m, moduleFunctionNode))
7412 0 : return nullptr;
7413 :
7414 0 : if (!CheckModuleArguments(m, moduleFunctionNode))
7415 0 : return nullptr;
7416 :
7417 0 : if (!CheckPrecedingStatements(m, stmtList))
7418 0 : return nullptr;
7419 :
7420 0 : if (!CheckModuleProcessingDirectives(m))
7421 0 : return nullptr;
7422 :
7423 0 : if (!CheckModuleGlobals(m))
7424 0 : return nullptr;
7425 :
7426 0 : if (!m.startFunctionBodies())
7427 0 : return nullptr;
7428 :
7429 0 : if (!CheckFunctions(m))
7430 0 : return nullptr;
7431 :
7432 0 : if (!m.finishFunctionBodies())
7433 0 : return nullptr;
7434 :
7435 0 : if (!CheckFuncPtrTables(m))
7436 0 : return nullptr;
7437 :
7438 0 : if (!CheckModuleReturn(m))
7439 0 : return nullptr;
7440 :
7441 0 : if (!CheckModuleEnd(m))
7442 0 : return nullptr;
7443 :
7444 0 : SharedModule module = m.finish();
7445 0 : if (!module)
7446 0 : return nullptr;
7447 :
7448 0 : *time = (PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC;
7449 0 : return module;
7450 : }
7451 :
7452 : /*****************************************************************************/
7453 : // Link-time validation
7454 :
7455 : static bool
7456 0 : LinkFail(JSContext* cx, const char* str)
7457 : {
7458 : JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
7459 0 : JSMSG_USE_ASM_LINK_FAIL, str);
7460 0 : return false;
7461 : }
7462 :
7463 : static bool
7464 0 : IsMaybeWrappedScriptedProxy(JSObject* obj)
7465 : {
7466 0 : JSObject* unwrapped = UncheckedUnwrap(obj);
7467 0 : return unwrapped && IsScriptedProxy(unwrapped);
7468 : }
7469 :
7470 : static bool
7471 0 : GetDataProperty(JSContext* cx, HandleValue objVal, HandleAtom field, MutableHandleValue v)
7472 : {
7473 0 : if (!objVal.isObject())
7474 0 : return LinkFail(cx, "accessing property of non-object");
7475 :
7476 0 : RootedObject obj(cx, &objVal.toObject());
7477 0 : if (IsMaybeWrappedScriptedProxy(obj))
7478 0 : return LinkFail(cx, "accessing property of a Proxy");
7479 :
7480 0 : Rooted<PropertyDescriptor> desc(cx);
7481 0 : RootedId id(cx, AtomToId(field));
7482 0 : if (!GetPropertyDescriptor(cx, obj, id, &desc))
7483 0 : return false;
7484 :
7485 0 : if (!desc.object())
7486 0 : return LinkFail(cx, "property not present on object");
7487 :
7488 0 : if (!desc.isDataDescriptor())
7489 0 : return LinkFail(cx, "property is not a data property");
7490 :
7491 0 : v.set(desc.value());
7492 0 : return true;
7493 : }
7494 :
7495 : static bool
7496 0 : GetDataProperty(JSContext* cx, HandleValue objVal, const char* fieldChars, MutableHandleValue v)
7497 : {
7498 0 : RootedAtom field(cx, AtomizeUTF8Chars(cx, fieldChars, strlen(fieldChars)));
7499 0 : if (!field)
7500 0 : return false;
7501 :
7502 0 : return GetDataProperty(cx, objVal, field, v);
7503 : }
7504 :
7505 : static bool
7506 0 : GetDataProperty(JSContext* cx, HandleValue objVal, ImmutablePropertyNamePtr field, MutableHandleValue v)
7507 : {
7508 : // Help the conversion along for all the cx->names().* users.
7509 0 : HandlePropertyName fieldHandle = field;
7510 0 : return GetDataProperty(cx, objVal, fieldHandle, v);
7511 : }
7512 :
7513 : static bool
7514 0 : HasObjectValueOfMethodPure(JSObject* obj, JSContext* cx)
7515 : {
7516 0 : Value v;
7517 0 : if (!GetPropertyPure(cx, obj, NameToId(cx->names().valueOf), &v))
7518 0 : return false;
7519 :
7520 : JSFunction* fun;
7521 0 : if (!IsFunctionObject(v, &fun))
7522 0 : return false;
7523 :
7524 0 : return IsSelfHostedFunctionWithName(fun, cx->names().Object_valueOf);
7525 : }
7526 :
7527 : static bool
7528 0 : HasPureCoercion(JSContext* cx, HandleValue v)
7529 : {
7530 : // Unsigned SIMD types are not allowed in function signatures.
7531 0 : if (IsVectorObject<Int32x4>(v) || IsVectorObject<Int16x8>(v) || IsVectorObject<Int8x16>(v) ||
7532 0 : IsVectorObject<Bool32x4>(v) || IsVectorObject<Bool16x8>(v) ||
7533 0 : IsVectorObject<Bool8x16>(v) || IsVectorObject<Float32x4>(v)) {
7534 0 : return true;
7535 : }
7536 :
7537 : // Ideally, we'd reject all non-SIMD non-primitives, but Emscripten has a
7538 : // bug that generates code that passes functions for some imports. To avoid
7539 : // breaking all the code that contains this bug, we make an exception for
7540 : // functions that don't have user-defined valueOf or toString, for their
7541 : // coercions are not observable and coercion via ToNumber/ToInt32
7542 : // definitely produces NaN/0. We should remove this special case later once
7543 : // most apps have been built with newer Emscripten.
7544 0 : if (v.toObject().is<JSFunction>() &&
7545 0 : HasNoToPrimitiveMethodPure(&v.toObject(), cx) &&
7546 0 : HasObjectValueOfMethodPure(&v.toObject(), cx) &&
7547 0 : HasNativeMethodPure(&v.toObject(), cx->names().toString, fun_toString, cx))
7548 : {
7549 0 : return true;
7550 : }
7551 :
7552 0 : return false;
7553 : }
7554 :
7555 : static bool
7556 0 : ValidateGlobalVariable(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal, Val* val)
7557 : {
7558 0 : switch (global.varInitKind()) {
7559 : case AsmJSGlobal::InitConstant:
7560 0 : *val = global.varInitVal();
7561 0 : return true;
7562 :
7563 : case AsmJSGlobal::InitImport: {
7564 0 : RootedValue v(cx);
7565 0 : if (!GetDataProperty(cx, importVal, global.field(), &v))
7566 0 : return false;
7567 :
7568 0 : if (!v.isPrimitive() && !HasPureCoercion(cx, v))
7569 0 : return LinkFail(cx, "Imported values must be primitives");
7570 :
7571 0 : switch (global.varInitImportType()) {
7572 : case ValType::I32: {
7573 : int32_t i32;
7574 0 : if (!ToInt32(cx, v, &i32))
7575 0 : return false;
7576 0 : *val = Val(uint32_t(i32));
7577 0 : return true;
7578 : }
7579 : case ValType::I64:
7580 0 : MOZ_CRASH("int64");
7581 : case ValType::F32: {
7582 : float f;
7583 0 : if (!RoundFloat32(cx, v, &f))
7584 0 : return false;
7585 0 : *val = Val(f);
7586 0 : return true;
7587 : }
7588 : case ValType::F64: {
7589 : double d;
7590 0 : if (!ToNumber(cx, v, &d))
7591 0 : return false;
7592 0 : *val = Val(d);
7593 0 : return true;
7594 : }
7595 : case ValType::I8x16: {
7596 : SimdConstant simdConstant;
7597 0 : if (!ToSimdConstant<Int8x16>(cx, v, &simdConstant))
7598 0 : return false;
7599 0 : *val = Val(simdConstant.asInt8x16());
7600 0 : return true;
7601 : }
7602 : case ValType::I16x8: {
7603 : SimdConstant simdConstant;
7604 0 : if (!ToSimdConstant<Int16x8>(cx, v, &simdConstant))
7605 0 : return false;
7606 0 : *val = Val(simdConstant.asInt16x8());
7607 0 : return true;
7608 : }
7609 : case ValType::I32x4: {
7610 : SimdConstant simdConstant;
7611 0 : if (!ToSimdConstant<Int32x4>(cx, v, &simdConstant))
7612 0 : return false;
7613 0 : *val = Val(simdConstant.asInt32x4());
7614 0 : return true;
7615 : }
7616 : case ValType::F32x4: {
7617 : SimdConstant simdConstant;
7618 0 : if (!ToSimdConstant<Float32x4>(cx, v, &simdConstant))
7619 0 : return false;
7620 0 : *val = Val(simdConstant.asFloat32x4());
7621 0 : return true;
7622 : }
7623 : case ValType::B8x16: {
7624 : SimdConstant simdConstant;
7625 0 : if (!ToSimdConstant<Bool8x16>(cx, v, &simdConstant))
7626 0 : return false;
7627 : // Bool8x16 uses the same data layout as Int8x16.
7628 0 : *val = Val(simdConstant.asInt8x16());
7629 0 : return true;
7630 : }
7631 : case ValType::B16x8: {
7632 : SimdConstant simdConstant;
7633 0 : if (!ToSimdConstant<Bool16x8>(cx, v, &simdConstant))
7634 0 : return false;
7635 : // Bool16x8 uses the same data layout as Int16x8.
7636 0 : *val = Val(simdConstant.asInt16x8());
7637 0 : return true;
7638 : }
7639 : case ValType::B32x4: {
7640 : SimdConstant simdConstant;
7641 0 : if (!ToSimdConstant<Bool32x4>(cx, v, &simdConstant))
7642 0 : return false;
7643 : // Bool32x4 uses the same data layout as Int32x4.
7644 0 : *val = Val(simdConstant.asInt32x4());
7645 0 : return true;
7646 : }
7647 : }
7648 : }
7649 : }
7650 :
7651 0 : MOZ_CRASH("unreachable");
7652 : }
7653 :
7654 : static bool
7655 0 : ValidateFFI(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal,
7656 : MutableHandle<FunctionVector> ffis)
7657 : {
7658 0 : RootedValue v(cx);
7659 0 : if (!GetDataProperty(cx, importVal, global.field(), &v))
7660 0 : return false;
7661 :
7662 0 : if (!IsFunctionObject(v))
7663 0 : return LinkFail(cx, "FFI imports must be functions");
7664 :
7665 0 : ffis[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
7666 0 : return true;
7667 : }
7668 :
7669 : static bool
7670 0 : ValidateArrayView(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
7671 : {
7672 0 : if (!global.field())
7673 0 : return true;
7674 :
7675 0 : RootedValue v(cx);
7676 0 : if (!GetDataProperty(cx, globalVal, global.field(), &v))
7677 0 : return false;
7678 :
7679 0 : bool tac = IsTypedArrayConstructor(v, global.viewType());
7680 0 : if (!tac)
7681 0 : return LinkFail(cx, "bad typed array constructor");
7682 :
7683 0 : return true;
7684 : }
7685 :
7686 : static bool
7687 0 : ValidateMathBuiltinFunction(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
7688 : {
7689 0 : RootedValue v(cx);
7690 0 : if (!GetDataProperty(cx, globalVal, cx->names().Math, &v))
7691 0 : return false;
7692 :
7693 0 : if (!GetDataProperty(cx, v, global.field(), &v))
7694 0 : return false;
7695 :
7696 0 : Native native = nullptr;
7697 0 : switch (global.mathBuiltinFunction()) {
7698 0 : case AsmJSMathBuiltin_sin: native = math_sin; break;
7699 0 : case AsmJSMathBuiltin_cos: native = math_cos; break;
7700 0 : case AsmJSMathBuiltin_tan: native = math_tan; break;
7701 0 : case AsmJSMathBuiltin_asin: native = math_asin; break;
7702 0 : case AsmJSMathBuiltin_acos: native = math_acos; break;
7703 0 : case AsmJSMathBuiltin_atan: native = math_atan; break;
7704 0 : case AsmJSMathBuiltin_ceil: native = math_ceil; break;
7705 0 : case AsmJSMathBuiltin_floor: native = math_floor; break;
7706 0 : case AsmJSMathBuiltin_exp: native = math_exp; break;
7707 0 : case AsmJSMathBuiltin_log: native = math_log; break;
7708 0 : case AsmJSMathBuiltin_pow: native = math_pow; break;
7709 0 : case AsmJSMathBuiltin_sqrt: native = math_sqrt; break;
7710 0 : case AsmJSMathBuiltin_min: native = math_min; break;
7711 0 : case AsmJSMathBuiltin_max: native = math_max; break;
7712 0 : case AsmJSMathBuiltin_abs: native = math_abs; break;
7713 0 : case AsmJSMathBuiltin_atan2: native = math_atan2; break;
7714 0 : case AsmJSMathBuiltin_imul: native = math_imul; break;
7715 0 : case AsmJSMathBuiltin_clz32: native = math_clz32; break;
7716 0 : case AsmJSMathBuiltin_fround: native = math_fround; break;
7717 : }
7718 :
7719 0 : if (!IsNativeFunction(v, native))
7720 0 : return LinkFail(cx, "bad Math.* builtin function");
7721 :
7722 0 : return true;
7723 : }
7724 :
7725 : static bool
7726 0 : ValidateSimdType(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal,
7727 : MutableHandleValue out)
7728 : {
7729 0 : RootedValue v(cx);
7730 0 : if (!GetDataProperty(cx, globalVal, cx->names().SIMD, &v))
7731 0 : return false;
7732 :
7733 : SimdType type;
7734 0 : if (global.which() == AsmJSGlobal::SimdCtor)
7735 0 : type = global.simdCtorType();
7736 : else
7737 0 : type = global.simdOperationType();
7738 :
7739 0 : RootedPropertyName simdTypeName(cx, SimdTypeToName(cx->names(), type));
7740 0 : if (!GetDataProperty(cx, v, simdTypeName, &v))
7741 0 : return false;
7742 :
7743 0 : if (!v.isObject())
7744 0 : return LinkFail(cx, "bad SIMD type");
7745 :
7746 0 : RootedObject simdDesc(cx, &v.toObject());
7747 0 : if (!simdDesc->is<SimdTypeDescr>())
7748 0 : return LinkFail(cx, "bad SIMD type");
7749 :
7750 0 : if (type != simdDesc->as<SimdTypeDescr>().type())
7751 0 : return LinkFail(cx, "bad SIMD type");
7752 :
7753 0 : out.set(v);
7754 0 : return true;
7755 : }
7756 :
7757 : static bool
7758 0 : ValidateSimdType(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
7759 : {
7760 0 : RootedValue _(cx);
7761 0 : return ValidateSimdType(cx, global, globalVal, &_);
7762 : }
7763 :
7764 : static bool
7765 0 : ValidateSimdOperation(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
7766 : {
7767 0 : RootedValue v(cx);
7768 0 : JS_ALWAYS_TRUE(ValidateSimdType(cx, global, globalVal, &v));
7769 :
7770 0 : if (!GetDataProperty(cx, v, global.field(), &v))
7771 0 : return false;
7772 :
7773 0 : Native native = nullptr;
7774 0 : switch (global.simdOperationType()) {
7775 : #define SET_NATIVE_INT8X16(op) case SimdOperation::Fn_##op: native = simd_int8x16_##op; break;
7776 : #define SET_NATIVE_INT16X8(op) case SimdOperation::Fn_##op: native = simd_int16x8_##op; break;
7777 : #define SET_NATIVE_INT32X4(op) case SimdOperation::Fn_##op: native = simd_int32x4_##op; break;
7778 : #define SET_NATIVE_UINT8X16(op) case SimdOperation::Fn_##op: native = simd_uint8x16_##op; break;
7779 : #define SET_NATIVE_UINT16X8(op) case SimdOperation::Fn_##op: native = simd_uint16x8_##op; break;
7780 : #define SET_NATIVE_UINT32X4(op) case SimdOperation::Fn_##op: native = simd_uint32x4_##op; break;
7781 : #define SET_NATIVE_FLOAT32X4(op) case SimdOperation::Fn_##op: native = simd_float32x4_##op; break;
7782 : #define SET_NATIVE_BOOL8X16(op) case SimdOperation::Fn_##op: native = simd_bool8x16_##op; break;
7783 : #define SET_NATIVE_BOOL16X8(op) case SimdOperation::Fn_##op: native = simd_bool16x8_##op; break;
7784 : #define SET_NATIVE_BOOL32X4(op) case SimdOperation::Fn_##op: native = simd_bool32x4_##op; break;
7785 : #define FALLTHROUGH(op) case SimdOperation::Fn_##op:
7786 : case SimdType::Int8x16:
7787 0 : switch (global.simdOperation()) {
7788 0 : FORALL_INT8X16_ASMJS_OP(SET_NATIVE_INT8X16)
7789 0 : SET_NATIVE_INT8X16(fromUint8x16Bits)
7790 0 : SET_NATIVE_INT8X16(fromUint16x8Bits)
7791 0 : SET_NATIVE_INT8X16(fromUint32x4Bits)
7792 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7793 : }
7794 0 : break;
7795 : case SimdType::Int16x8:
7796 0 : switch (global.simdOperation()) {
7797 0 : FORALL_INT16X8_ASMJS_OP(SET_NATIVE_INT16X8)
7798 0 : SET_NATIVE_INT16X8(fromUint8x16Bits)
7799 0 : SET_NATIVE_INT16X8(fromUint16x8Bits)
7800 0 : SET_NATIVE_INT16X8(fromUint32x4Bits)
7801 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7802 : }
7803 0 : break;
7804 : case SimdType::Int32x4:
7805 0 : switch (global.simdOperation()) {
7806 0 : FORALL_INT32X4_ASMJS_OP(SET_NATIVE_INT32X4)
7807 0 : SET_NATIVE_INT32X4(fromUint8x16Bits)
7808 0 : SET_NATIVE_INT32X4(fromUint16x8Bits)
7809 0 : SET_NATIVE_INT32X4(fromUint32x4Bits)
7810 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7811 : }
7812 0 : break;
7813 : case SimdType::Uint8x16:
7814 0 : switch (global.simdOperation()) {
7815 0 : FORALL_INT8X16_ASMJS_OP(SET_NATIVE_UINT8X16)
7816 0 : SET_NATIVE_UINT8X16(fromInt8x16Bits)
7817 0 : SET_NATIVE_UINT8X16(fromUint16x8Bits)
7818 0 : SET_NATIVE_UINT8X16(fromUint32x4Bits)
7819 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7820 : }
7821 0 : break;
7822 : case SimdType::Uint16x8:
7823 0 : switch (global.simdOperation()) {
7824 0 : FORALL_INT16X8_ASMJS_OP(SET_NATIVE_UINT16X8)
7825 0 : SET_NATIVE_UINT16X8(fromUint8x16Bits)
7826 0 : SET_NATIVE_UINT16X8(fromInt16x8Bits)
7827 0 : SET_NATIVE_UINT16X8(fromUint32x4Bits)
7828 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7829 : }
7830 0 : break;
7831 : case SimdType::Uint32x4:
7832 0 : switch (global.simdOperation()) {
7833 0 : FORALL_INT32X4_ASMJS_OP(SET_NATIVE_UINT32X4)
7834 0 : SET_NATIVE_UINT32X4(fromUint8x16Bits)
7835 0 : SET_NATIVE_UINT32X4(fromUint16x8Bits)
7836 0 : SET_NATIVE_UINT32X4(fromInt32x4Bits)
7837 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7838 : }
7839 0 : break;
7840 : case SimdType::Float32x4:
7841 0 : switch (global.simdOperation()) {
7842 0 : FORALL_FLOAT32X4_ASMJS_OP(SET_NATIVE_FLOAT32X4)
7843 0 : SET_NATIVE_FLOAT32X4(fromUint8x16Bits)
7844 0 : SET_NATIVE_FLOAT32X4(fromUint16x8Bits)
7845 0 : SET_NATIVE_FLOAT32X4(fromUint32x4Bits)
7846 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7847 : }
7848 0 : break;
7849 : case SimdType::Bool8x16:
7850 0 : switch (global.simdOperation()) {
7851 0 : FORALL_BOOL_SIMD_OP(SET_NATIVE_BOOL8X16)
7852 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7853 : }
7854 0 : break;
7855 : case SimdType::Bool16x8:
7856 0 : switch (global.simdOperation()) {
7857 0 : FORALL_BOOL_SIMD_OP(SET_NATIVE_BOOL16X8)
7858 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7859 : }
7860 0 : break;
7861 : case SimdType::Bool32x4:
7862 0 : switch (global.simdOperation()) {
7863 0 : FORALL_BOOL_SIMD_OP(SET_NATIVE_BOOL32X4)
7864 0 : default: MOZ_CRASH("shouldn't have been validated in the first place");
7865 : }
7866 0 : break;
7867 0 : default: MOZ_CRASH("unhandled simd type");
7868 : #undef FALLTHROUGH
7869 : #undef SET_NATIVE_INT8X16
7870 : #undef SET_NATIVE_INT16X8
7871 : #undef SET_NATIVE_INT32X4
7872 : #undef SET_NATIVE_UINT8X16
7873 : #undef SET_NATIVE_UINT16X8
7874 : #undef SET_NATIVE_UINT32X4
7875 : #undef SET_NATIVE_FLOAT32X4
7876 : #undef SET_NATIVE_BOOL8X16
7877 : #undef SET_NATIVE_BOOL16X8
7878 : #undef SET_NATIVE_BOOL32X4
7879 : #undef SET_NATIVE
7880 : }
7881 0 : if (!native || !IsNativeFunction(v, native))
7882 0 : return LinkFail(cx, "bad SIMD.type.* operation");
7883 0 : return true;
7884 : }
7885 :
7886 : static bool
7887 0 : ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
7888 : {
7889 0 : RootedValue v(cx);
7890 0 : if (!GetDataProperty(cx, globalVal, cx->names().Atomics, &v))
7891 0 : return false;
7892 :
7893 0 : if (!GetDataProperty(cx, v, global.field(), &v))
7894 0 : return false;
7895 :
7896 0 : Native native = nullptr;
7897 0 : switch (global.atomicsBuiltinFunction()) {
7898 0 : case AsmJSAtomicsBuiltin_compareExchange: native = atomics_compareExchange; break;
7899 0 : case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break;
7900 0 : case AsmJSAtomicsBuiltin_load: native = atomics_load; break;
7901 0 : case AsmJSAtomicsBuiltin_store: native = atomics_store; break;
7902 0 : case AsmJSAtomicsBuiltin_add: native = atomics_add; break;
7903 0 : case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break;
7904 0 : case AsmJSAtomicsBuiltin_and: native = atomics_and; break;
7905 0 : case AsmJSAtomicsBuiltin_or: native = atomics_or; break;
7906 0 : case AsmJSAtomicsBuiltin_xor: native = atomics_xor; break;
7907 0 : case AsmJSAtomicsBuiltin_isLockFree: native = atomics_isLockFree; break;
7908 : }
7909 :
7910 0 : if (!IsNativeFunction(v, native))
7911 0 : return LinkFail(cx, "bad Atomics.* builtin function");
7912 :
7913 0 : return true;
7914 : }
7915 :
7916 : static bool
7917 0 : ValidateConstant(JSContext* cx, const AsmJSGlobal& global, HandleValue globalVal)
7918 : {
7919 0 : RootedValue v(cx, globalVal);
7920 :
7921 0 : if (global.constantKind() == AsmJSGlobal::MathConstant) {
7922 0 : if (!GetDataProperty(cx, v, cx->names().Math, &v))
7923 0 : return false;
7924 : }
7925 :
7926 0 : if (!GetDataProperty(cx, v, global.field(), &v))
7927 0 : return false;
7928 :
7929 0 : if (!v.isNumber())
7930 0 : return LinkFail(cx, "math / global constant value needs to be a number");
7931 :
7932 : // NaN != NaN
7933 0 : if (IsNaN(global.constantValue())) {
7934 0 : if (!IsNaN(v.toNumber()))
7935 0 : return LinkFail(cx, "global constant value needs to be NaN");
7936 : } else {
7937 0 : if (v.toNumber() != global.constantValue())
7938 0 : return LinkFail(cx, "global constant value mismatch");
7939 : }
7940 :
7941 0 : return true;
7942 : }
7943 :
7944 : static bool
7945 0 : CheckBuffer(JSContext* cx, const AsmJSMetadata& metadata, HandleValue bufferVal,
7946 : MutableHandle<ArrayBufferObjectMaybeShared*> buffer)
7947 : {
7948 0 : if (metadata.memoryUsage == MemoryUsage::Shared) {
7949 0 : if (!IsSharedArrayBuffer(bufferVal))
7950 0 : return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer");
7951 : } else {
7952 0 : if (!IsArrayBuffer(bufferVal))
7953 0 : return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer");
7954 : }
7955 :
7956 0 : buffer.set(&AsAnyArrayBuffer(bufferVal));
7957 0 : uint32_t memoryLength = buffer->byteLength();
7958 :
7959 0 : if (!IsValidAsmJSHeapLength(memoryLength)) {
7960 : UniqueChars msg(
7961 : JS_smprintf("ArrayBuffer byteLength 0x%x is not a valid heap length. The next "
7962 : "valid length is 0x%x",
7963 : memoryLength,
7964 0 : RoundUpToNextValidAsmJSHeapLength(memoryLength)));
7965 0 : if (!msg)
7966 0 : return false;
7967 0 : return LinkFail(cx, msg.get());
7968 : }
7969 :
7970 : // This check is sufficient without considering the size of the loaded datum because heap
7971 : // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
7972 0 : MOZ_ASSERT((metadata.minMemoryLength - 1) <= INT32_MAX);
7973 0 : if (memoryLength < metadata.minMemoryLength) {
7974 : UniqueChars msg(
7975 : JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
7976 : "by const heap accesses).",
7977 : memoryLength,
7978 0 : metadata.minMemoryLength));
7979 0 : if (!msg)
7980 0 : return false;
7981 0 : return LinkFail(cx, msg.get());
7982 : }
7983 :
7984 0 : if (buffer->is<ArrayBufferObject>()) {
7985 : // On 64-bit, bounds checks are statically removed so the huge guard
7986 : // region is always necessary. On 32-bit, allocating a guard page
7987 : // requires reallocating the incoming ArrayBuffer which could trigger
7988 : // OOM. Thus, only ask for a guard page when SIMD is used since SIMD
7989 : // allows unaligned memory access (see MaxMemoryAccessSize comment);
7990 : #ifdef WASM_HUGE_MEMORY
7991 0 : bool needGuard = true;
7992 : #else
7993 : bool needGuard = metadata.usesSimd;
7994 : #endif
7995 0 : Rooted<ArrayBufferObject*> arrayBuffer(cx, &buffer->as<ArrayBufferObject>());
7996 0 : if (!ArrayBufferObject::prepareForAsmJS(cx, arrayBuffer, needGuard))
7997 0 : return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use");
7998 : } else {
7999 0 : if (!buffer->as<SharedArrayBufferObject>().isPreparedForAsmJS())
8000 0 : return LinkFail(cx, "SharedArrayBuffer must be created with wasm test mode enabled");
8001 : }
8002 :
8003 0 : MOZ_ASSERT(buffer->isPreparedForAsmJS());
8004 0 : return true;
8005 : }
8006 :
8007 : static bool
8008 0 : GetImports(JSContext* cx, const AsmJSMetadata& metadata, HandleValue globalVal,
8009 : HandleValue importVal, MutableHandle<FunctionVector> funcImports, ValVector* valImports)
8010 : {
8011 0 : Rooted<FunctionVector> ffis(cx, FunctionVector(cx));
8012 0 : if (!ffis.resize(metadata.numFFIs))
8013 0 : return false;
8014 :
8015 0 : for (const AsmJSGlobal& global : metadata.asmJSGlobals) {
8016 0 : switch (global.which()) {
8017 : case AsmJSGlobal::Variable: {
8018 0 : Val val;
8019 0 : if (!ValidateGlobalVariable(cx, global, importVal, &val))
8020 0 : return false;
8021 0 : if (!valImports->append(val))
8022 0 : return false;
8023 0 : break;
8024 : }
8025 : case AsmJSGlobal::FFI:
8026 0 : if (!ValidateFFI(cx, global, importVal, &ffis))
8027 0 : return false;
8028 0 : break;
8029 : case AsmJSGlobal::ArrayView:
8030 : case AsmJSGlobal::ArrayViewCtor:
8031 0 : if (!ValidateArrayView(cx, global, globalVal))
8032 0 : return false;
8033 0 : break;
8034 : case AsmJSGlobal::MathBuiltinFunction:
8035 0 : if (!ValidateMathBuiltinFunction(cx, global, globalVal))
8036 0 : return false;
8037 0 : break;
8038 : case AsmJSGlobal::AtomicsBuiltinFunction:
8039 0 : if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal))
8040 0 : return false;
8041 0 : break;
8042 : case AsmJSGlobal::Constant:
8043 0 : if (!ValidateConstant(cx, global, globalVal))
8044 0 : return false;
8045 0 : break;
8046 : case AsmJSGlobal::SimdCtor:
8047 0 : if (!ValidateSimdType(cx, global, globalVal))
8048 0 : return false;
8049 0 : break;
8050 : case AsmJSGlobal::SimdOp:
8051 0 : if (!ValidateSimdOperation(cx, global, globalVal))
8052 0 : return false;
8053 0 : break;
8054 : }
8055 : }
8056 :
8057 0 : for (const AsmJSImport& import : metadata.asmJSImports) {
8058 0 : if (!funcImports.append(ffis[import.ffiIndex()]))
8059 0 : return false;
8060 : }
8061 :
8062 0 : return true;
8063 : }
8064 :
8065 : static bool
8066 0 : TryInstantiate(JSContext* cx, CallArgs args, Module& module, const AsmJSMetadata& metadata,
8067 : MutableHandleWasmInstanceObject instanceObj, MutableHandleObject exportObj)
8068 : {
8069 0 : HandleValue globalVal = args.get(0);
8070 0 : HandleValue importVal = args.get(1);
8071 0 : HandleValue bufferVal = args.get(2);
8072 :
8073 0 : RootedArrayBufferObjectMaybeShared buffer(cx);
8074 0 : RootedWasmMemoryObject memory(cx);
8075 0 : if (module.metadata().usesMemory()) {
8076 0 : if (!CheckBuffer(cx, metadata, bufferVal, &buffer))
8077 0 : return false;
8078 :
8079 0 : memory = WasmMemoryObject::create(cx, buffer, nullptr);
8080 0 : if (!memory)
8081 0 : return false;
8082 : }
8083 :
8084 0 : ValVector valImports;
8085 0 : Rooted<FunctionVector> funcs(cx, FunctionVector(cx));
8086 0 : if (!GetImports(cx, metadata, globalVal, importVal, &funcs, &valImports))
8087 0 : return false;
8088 :
8089 0 : RootedWasmTableObject table(cx);
8090 0 : if (!module.instantiate(cx, funcs, table, memory, valImports, nullptr, instanceObj))
8091 0 : return false;
8092 :
8093 0 : exportObj.set(&instanceObj->exportsObj());
8094 0 : return true;
8095 : }
8096 :
8097 : static bool
8098 0 : HandleInstantiationFailure(JSContext* cx, CallArgs args, const AsmJSMetadata& metadata)
8099 : {
8100 0 : RootedAtom name(cx, args.callee().as<JSFunction>().explicitName());
8101 :
8102 0 : if (cx->isExceptionPending())
8103 0 : return false;
8104 :
8105 0 : ScriptSource* source = metadata.scriptSource.get();
8106 :
8107 : // Source discarding is allowed to affect JS semantics because it is never
8108 : // enabled for normal JS content.
8109 0 : bool haveSource = source->hasSourceData();
8110 0 : if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
8111 0 : return false;
8112 0 : if (!haveSource) {
8113 0 : JS_ReportErrorASCII(cx, "asm.js link failure with source discarding enabled");
8114 0 : return false;
8115 : }
8116 :
8117 0 : uint32_t begin = metadata.toStringStart;
8118 0 : uint32_t end = metadata.srcEndAfterCurly();
8119 0 : Rooted<JSFlatString*> src(cx, source->substringDontDeflate(cx, begin, end));
8120 0 : if (!src)
8121 0 : return false;
8122 :
8123 0 : RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL,
8124 : name, /* proto = */ nullptr, gc::AllocKind::FUNCTION,
8125 0 : TenuredObject));
8126 0 : if (!fun)
8127 0 : return false;
8128 :
8129 0 : CompileOptions options(cx);
8130 0 : options.setMutedErrors(source->mutedErrors())
8131 0 : .setFile(source->filename())
8132 0 : .setNoScriptRval(false);
8133 0 : options.asmJSOption = AsmJSOption::Disabled;
8134 :
8135 : // The exported function inherits an implicit strict context if the module
8136 : // also inherited it somehow.
8137 0 : if (metadata.strict)
8138 0 : options.strictOption = true;
8139 :
8140 0 : AutoStableStringChars stableChars(cx);
8141 0 : if (!stableChars.initTwoByte(cx, src))
8142 0 : return false;
8143 :
8144 0 : const char16_t* chars = stableChars.twoByteRange().begin().get();
8145 0 : SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
8146 0 : ? SourceBufferHolder::GiveOwnership
8147 0 : : SourceBufferHolder::NoOwnership;
8148 0 : SourceBufferHolder srcBuf(chars, end - begin, ownership);
8149 0 : if (!frontend::CompileStandaloneFunction(cx, &fun, options, srcBuf, Nothing()))
8150 0 : return false;
8151 :
8152 : // Call the function we just recompiled.
8153 0 : args.setCallee(ObjectValue(*fun));
8154 0 : return InternalCallOrConstruct(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT);
8155 : }
8156 :
8157 : static Module&
8158 0 : AsmJSModuleFunctionToModule(JSFunction* fun)
8159 : {
8160 0 : MOZ_ASSERT(IsAsmJSModule(fun));
8161 0 : const Value& v = fun->getExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT);
8162 0 : return v.toObject().as<WasmModuleObject>().module();
8163 : }
8164 :
8165 : // Implements the semantics of an asm.js module function that has been successfully validated.
8166 : static bool
8167 0 : InstantiateAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
8168 : {
8169 0 : CallArgs args = CallArgsFromVp(argc, vp);
8170 :
8171 0 : JSFunction* callee = &args.callee().as<JSFunction>();
8172 0 : Module& module = AsmJSModuleFunctionToModule(callee);
8173 0 : const AsmJSMetadata& metadata = module.metadata().asAsmJS();
8174 :
8175 0 : RootedWasmInstanceObject instanceObj(cx);
8176 0 : RootedObject exportObj(cx);
8177 0 : if (!TryInstantiate(cx, args, module, metadata, &instanceObj, &exportObj)) {
8178 : // Link-time validation checks failed, so reparse the entire asm.js
8179 : // module from scratch to get normal interpreted bytecode which we can
8180 : // simply Invoke. Very slow.
8181 0 : return HandleInstantiationFailure(cx, args, metadata);
8182 : }
8183 :
8184 0 : args.rval().set(ObjectValue(*exportObj));
8185 0 : return true;
8186 : }
8187 :
8188 : static JSFunction*
8189 0 : NewAsmJSModuleFunction(JSContext* cx, JSFunction* origFun, HandleObject moduleObj)
8190 : {
8191 0 : RootedAtom name(cx, origFun->explicitName());
8192 :
8193 0 : JSFunction::Flags flags = origFun->isLambda() ? JSFunction::ASMJS_LAMBDA_CTOR
8194 0 : : JSFunction::ASMJS_CTOR;
8195 : JSFunction* moduleFun =
8196 0 : NewNativeConstructor(cx, InstantiateAsmJS, origFun->nargs(), name,
8197 : gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
8198 0 : flags);
8199 0 : if (!moduleFun)
8200 0 : return nullptr;
8201 :
8202 0 : moduleFun->setExtendedSlot(FunctionExtended::ASMJS_MODULE_SLOT, ObjectValue(*moduleObj));
8203 :
8204 0 : MOZ_ASSERT(IsAsmJSModule(moduleFun));
8205 0 : return moduleFun;
8206 : }
8207 :
8208 : /*****************************************************************************/
8209 : // Caching and cloning
8210 :
8211 : size_t
8212 0 : AsmJSGlobal::serializedSize() const
8213 : {
8214 : return sizeof(pod) +
8215 0 : field_.serializedSize();
8216 : }
8217 :
8218 : uint8_t*
8219 0 : AsmJSGlobal::serialize(uint8_t* cursor) const
8220 : {
8221 0 : cursor = WriteBytes(cursor, &pod, sizeof(pod));
8222 0 : cursor = field_.serialize(cursor);
8223 0 : return cursor;
8224 : }
8225 :
8226 : const uint8_t*
8227 0 : AsmJSGlobal::deserialize(const uint8_t* cursor)
8228 : {
8229 0 : (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
8230 0 : (cursor = field_.deserialize(cursor));
8231 0 : return cursor;
8232 : }
8233 :
8234 : size_t
8235 0 : AsmJSGlobal::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
8236 : {
8237 0 : return field_.sizeOfExcludingThis(mallocSizeOf);
8238 : }
8239 :
8240 : size_t
8241 0 : AsmJSMetadata::serializedSize() const
8242 : {
8243 0 : return Metadata::serializedSize() +
8244 0 : sizeof(pod()) +
8245 0 : SerializedVectorSize(asmJSGlobals) +
8246 0 : SerializedPodVectorSize(asmJSImports) +
8247 0 : SerializedPodVectorSize(asmJSExports) +
8248 0 : SerializedVectorSize(asmJSFuncNames) +
8249 0 : globalArgumentName.serializedSize() +
8250 0 : importArgumentName.serializedSize() +
8251 0 : bufferArgumentName.serializedSize();
8252 : }
8253 :
8254 : uint8_t*
8255 0 : AsmJSMetadata::serialize(uint8_t* cursor) const
8256 : {
8257 0 : cursor = Metadata::serialize(cursor);
8258 0 : cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
8259 0 : cursor = SerializeVector(cursor, asmJSGlobals);
8260 0 : cursor = SerializePodVector(cursor, asmJSImports);
8261 0 : cursor = SerializePodVector(cursor, asmJSExports);
8262 0 : cursor = SerializeVector(cursor, asmJSFuncNames);
8263 0 : cursor = globalArgumentName.serialize(cursor);
8264 0 : cursor = importArgumentName.serialize(cursor);
8265 0 : cursor = bufferArgumentName.serialize(cursor);
8266 0 : return cursor;
8267 : }
8268 :
8269 : const uint8_t*
8270 0 : AsmJSMetadata::deserialize(const uint8_t* cursor)
8271 : {
8272 0 : (cursor = Metadata::deserialize(cursor)) &&
8273 0 : (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
8274 0 : (cursor = DeserializeVector(cursor, &asmJSGlobals)) &&
8275 0 : (cursor = DeserializePodVector(cursor, &asmJSImports)) &&
8276 0 : (cursor = DeserializePodVector(cursor, &asmJSExports)) &&
8277 0 : (cursor = DeserializeVector(cursor, &asmJSFuncNames)) &&
8278 0 : (cursor = globalArgumentName.deserialize(cursor)) &&
8279 0 : (cursor = importArgumentName.deserialize(cursor)) &&
8280 0 : (cursor = bufferArgumentName.deserialize(cursor));
8281 0 : cacheResult = CacheResult::Hit;
8282 0 : return cursor;
8283 : }
8284 :
8285 : size_t
8286 0 : AsmJSMetadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
8287 : {
8288 0 : return Metadata::sizeOfExcludingThis(mallocSizeOf) +
8289 0 : SizeOfVectorExcludingThis(asmJSGlobals, mallocSizeOf) +
8290 0 : asmJSImports.sizeOfExcludingThis(mallocSizeOf) +
8291 0 : asmJSExports.sizeOfExcludingThis(mallocSizeOf) +
8292 0 : SizeOfVectorExcludingThis(asmJSFuncNames, mallocSizeOf) +
8293 0 : globalArgumentName.sizeOfExcludingThis(mallocSizeOf) +
8294 0 : importArgumentName.sizeOfExcludingThis(mallocSizeOf) +
8295 0 : bufferArgumentName.sizeOfExcludingThis(mallocSizeOf);
8296 : }
8297 :
8298 : namespace {
8299 :
8300 0 : class ModuleChars
8301 : {
8302 : protected:
8303 : uint32_t isFunCtor_;
8304 : Vector<CacheableChars, 0, SystemAllocPolicy> funCtorArgs_;
8305 :
8306 : public:
8307 0 : static uint32_t beginOffset(AsmJSParser& parser) {
8308 0 : return parser.pc->functionBox()->functionNode->pn_pos.begin;
8309 : }
8310 :
8311 0 : static uint32_t endOffset(AsmJSParser& parser) {
8312 0 : TokenPos pos(0, 0); // initialize to silence GCC warning
8313 0 : MOZ_ALWAYS_TRUE(parser.tokenStream.peekTokenPos(&pos, TokenStream::Operand));
8314 0 : return pos.end;
8315 : }
8316 : };
8317 :
8318 0 : class ModuleCharsForStore : ModuleChars
8319 : {
8320 : uint32_t uncompressedSize_;
8321 : uint32_t compressedSize_;
8322 : Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
8323 :
8324 : public:
8325 0 : bool init(AsmJSParser& parser) {
8326 0 : MOZ_ASSERT(beginOffset(parser) < endOffset(parser));
8327 :
8328 0 : uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(char16_t);
8329 0 : size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
8330 0 : if (maxCompressedSize < uncompressedSize_)
8331 0 : return false;
8332 :
8333 0 : if (!compressedBuffer_.resize(maxCompressedSize))
8334 0 : return false;
8335 :
8336 0 : const char16_t* chars = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
8337 0 : const char* source = reinterpret_cast<const char*>(chars);
8338 0 : size_t compressedSize = LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin());
8339 0 : if (!compressedSize || compressedSize > UINT32_MAX)
8340 0 : return false;
8341 :
8342 0 : compressedSize_ = compressedSize;
8343 :
8344 : // For a function statement or named function expression:
8345 : // function f(x,y,z) { abc }
8346 : // the range [beginOffset, endOffset) captures the source:
8347 : // f(x,y,z) { abc }
8348 : // An unnamed function expression captures the same thing, sans 'f'.
8349 : // Since asm.js modules do not contain any free variables, equality of
8350 : // [beginOffset, endOffset) is sufficient to guarantee identical code
8351 : // generation, modulo Assumptions.
8352 : //
8353 : // For functions created with 'new Function', function arguments are
8354 : // not present in the source so we must manually explicitly serialize
8355 : // and match the formals as a Vector of PropertyName.
8356 0 : isFunCtor_ = parser.pc->isStandaloneFunctionBody();
8357 0 : if (isFunCtor_) {
8358 : unsigned numArgs;
8359 0 : ParseNode* functionNode = parser.pc->functionBox()->functionNode;
8360 0 : ParseNode* arg = FunctionFormalParametersList(functionNode, &numArgs);
8361 0 : for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
8362 0 : UniqueChars name = StringToNewUTF8CharsZ(nullptr, *arg->name());
8363 0 : if (!name || !funCtorArgs_.append(Move(name)))
8364 0 : return false;
8365 : }
8366 : }
8367 :
8368 0 : return true;
8369 : }
8370 :
8371 0 : size_t serializedSize() const {
8372 : return sizeof(uint32_t) +
8373 : sizeof(uint32_t) +
8374 0 : compressedSize_ +
8375 0 : sizeof(uint32_t) +
8376 0 : (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0);
8377 : }
8378 :
8379 0 : uint8_t* serialize(uint8_t* cursor) const {
8380 0 : cursor = WriteScalar<uint32_t>(cursor, uncompressedSize_);
8381 0 : cursor = WriteScalar<uint32_t>(cursor, compressedSize_);
8382 0 : cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_);
8383 0 : cursor = WriteScalar<uint32_t>(cursor, isFunCtor_);
8384 0 : if (isFunCtor_)
8385 0 : cursor = SerializeVector(cursor, funCtorArgs_);
8386 0 : return cursor;
8387 : }
8388 : };
8389 :
8390 0 : class ModuleCharsForLookup : ModuleChars
8391 : {
8392 : Vector<char16_t, 0, SystemAllocPolicy> chars_;
8393 :
8394 : public:
8395 0 : const uint8_t* deserialize(const uint8_t* cursor) {
8396 : uint32_t uncompressedSize;
8397 0 : cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
8398 :
8399 : uint32_t compressedSize;
8400 0 : cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
8401 :
8402 0 : if (!chars_.resize(uncompressedSize / sizeof(char16_t)))
8403 0 : return nullptr;
8404 :
8405 0 : const char* source = reinterpret_cast<const char*>(cursor);
8406 0 : char* dest = reinterpret_cast<char*>(chars_.begin());
8407 0 : if (!LZ4::decompress(source, dest, uncompressedSize))
8408 0 : return nullptr;
8409 :
8410 0 : cursor += compressedSize;
8411 :
8412 0 : cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
8413 0 : if (isFunCtor_)
8414 0 : cursor = DeserializeVector(cursor, &funCtorArgs_);
8415 :
8416 0 : return cursor;
8417 : }
8418 :
8419 0 : bool match(AsmJSParser& parser) const {
8420 0 : const char16_t* parseBegin = parser.tokenStream.rawCharPtrAt(beginOffset(parser));
8421 0 : const char16_t* parseLimit = parser.tokenStream.rawLimit();
8422 0 : MOZ_ASSERT(parseLimit >= parseBegin);
8423 0 : if (uint32_t(parseLimit - parseBegin) < chars_.length())
8424 0 : return false;
8425 0 : if (!PodEqual(chars_.begin(), parseBegin, chars_.length()))
8426 0 : return false;
8427 0 : if (isFunCtor_ != parser.pc->isStandaloneFunctionBody())
8428 0 : return false;
8429 0 : if (isFunCtor_) {
8430 : // For function statements, the closing } is included as the last
8431 : // character of the matched source. For Function constructor,
8432 : // parsing terminates with EOF which we must explicitly check. This
8433 : // prevents
8434 : // new Function('"use asm"; function f() {} return f')
8435 : // from incorrectly matching
8436 : // new Function('"use asm"; function f() {} return ff')
8437 0 : if (parseBegin + chars_.length() != parseLimit)
8438 0 : return false;
8439 : unsigned numArgs;
8440 0 : ParseNode* functionNode = parser.pc->functionBox()->functionNode;
8441 0 : ParseNode* arg = FunctionFormalParametersList(functionNode, &numArgs);
8442 0 : if (funCtorArgs_.length() != numArgs)
8443 0 : return false;
8444 0 : for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
8445 0 : UniqueChars name = StringToNewUTF8CharsZ(nullptr, *arg->name());
8446 0 : if (!name || strcmp(funCtorArgs_[i].get(), name.get()))
8447 0 : return false;
8448 : }
8449 : }
8450 0 : return true;
8451 : }
8452 : };
8453 :
8454 : struct ScopedCacheEntryOpenedForWrite
8455 : {
8456 : JSContext* cx;
8457 : const size_t serializedSize;
8458 : uint8_t* memory;
8459 : intptr_t handle;
8460 :
8461 0 : ScopedCacheEntryOpenedForWrite(JSContext* cx, size_t serializedSize)
8462 0 : : cx(cx), serializedSize(serializedSize), memory(nullptr), handle(-1)
8463 0 : {}
8464 :
8465 0 : ~ScopedCacheEntryOpenedForWrite() {
8466 0 : if (memory)
8467 0 : cx->asmJSCacheOps().closeEntryForWrite(serializedSize, memory, handle);
8468 0 : }
8469 : };
8470 :
8471 : struct ScopedCacheEntryOpenedForRead
8472 : {
8473 : JSContext* cx;
8474 : size_t serializedSize;
8475 : const uint8_t* memory;
8476 : intptr_t handle;
8477 :
8478 0 : explicit ScopedCacheEntryOpenedForRead(JSContext* cx)
8479 0 : : cx(cx), serializedSize(0), memory(nullptr), handle(0)
8480 0 : {}
8481 :
8482 0 : ~ScopedCacheEntryOpenedForRead() {
8483 0 : if (memory)
8484 0 : cx->asmJSCacheOps().closeEntryForRead(serializedSize, memory, handle);
8485 0 : }
8486 : };
8487 :
8488 : } // unnamed namespace
8489 :
8490 : static JS::AsmJSCacheResult
8491 0 : StoreAsmJSModuleInCache(AsmJSParser& parser, Module& module, JSContext* cx)
8492 : {
8493 0 : ModuleCharsForStore moduleChars;
8494 0 : if (!moduleChars.init(parser))
8495 0 : return JS::AsmJSCache_InternalError;
8496 :
8497 : size_t bytecodeSize, compiledSize;
8498 0 : module.serializedSize(&bytecodeSize, &compiledSize);
8499 0 : MOZ_RELEASE_ASSERT(bytecodeSize == 0);
8500 0 : MOZ_RELEASE_ASSERT(compiledSize <= UINT32_MAX);
8501 :
8502 : size_t serializedSize = sizeof(uint32_t) +
8503 0 : compiledSize +
8504 0 : moduleChars.serializedSize();
8505 :
8506 0 : JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
8507 0 : if (!open)
8508 0 : return JS::AsmJSCache_Disabled_Internal;
8509 :
8510 0 : const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
8511 0 : const char16_t* end = parser.tokenStream.rawCharPtrAt(ModuleChars::endOffset(parser));
8512 :
8513 0 : ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
8514 : JS::AsmJSCacheResult openResult =
8515 0 : open(cx->global(), begin, end, serializedSize, &entry.memory, &entry.handle);
8516 0 : if (openResult != JS::AsmJSCache_Success)
8517 0 : return openResult;
8518 :
8519 0 : uint8_t* cursor = entry.memory;
8520 :
8521 : // Everything serialized before the Module must not change incompatibly
8522 : // between any two builds (regardless of platform, architecture, ...).
8523 : // (The Module::assumptionsMatch() guard everything in the Module and
8524 : // afterwards.)
8525 0 : cursor = WriteScalar<uint32_t>(cursor, compiledSize);
8526 :
8527 0 : module.serialize(/* bytecodeBegin = */ nullptr, /* bytecodeSize = */ 0, cursor, compiledSize);
8528 0 : cursor += compiledSize;
8529 :
8530 0 : cursor = moduleChars.serialize(cursor);
8531 :
8532 0 : MOZ_RELEASE_ASSERT(cursor == entry.memory + serializedSize);
8533 :
8534 0 : return JS::AsmJSCache_Success;
8535 : }
8536 :
8537 : static bool
8538 0 : LookupAsmJSModuleInCache(JSContext* cx, AsmJSParser& parser, bool* loadedFromCache,
8539 : SharedModule* module, UniqueChars* compilationTimeReport)
8540 : {
8541 0 : int64_t before = PRMJ_Now();
8542 :
8543 0 : *loadedFromCache = false;
8544 :
8545 0 : JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead;
8546 0 : if (!open)
8547 0 : return true;
8548 :
8549 0 : const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
8550 0 : const char16_t* limit = parser.tokenStream.rawLimit();
8551 :
8552 0 : ScopedCacheEntryOpenedForRead entry(cx);
8553 0 : if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
8554 0 : return true;
8555 :
8556 0 : size_t remain = entry.serializedSize;
8557 0 : const uint8_t* cursor = entry.memory;
8558 :
8559 : uint32_t compiledSize;
8560 0 : cursor = ReadScalarChecked<uint32_t>(cursor, &remain, &compiledSize);
8561 0 : if (!cursor)
8562 0 : return true;
8563 :
8564 0 : Assumptions assumptions;
8565 0 : if (!assumptions.initBuildIdFromContext(cx))
8566 0 : return false;
8567 :
8568 0 : if (!Module::assumptionsMatch(assumptions, cursor, remain))
8569 0 : return true;
8570 :
8571 0 : auto tierMetadata = js::MakeUnique<MetadataTier>(Tier::Ion);
8572 0 : if (!tierMetadata)
8573 0 : return false;
8574 :
8575 0 : MutableAsmJSMetadata asmJSMetadata = cx->new_<AsmJSMetadata>(Move(tierMetadata));
8576 0 : if (!asmJSMetadata)
8577 0 : return false;
8578 :
8579 0 : *module = Module::deserialize(/* bytecodeBegin = */ nullptr, /* bytecodeSize = */ 0,
8580 0 : cursor, compiledSize, asmJSMetadata.get());
8581 0 : if (!*module) {
8582 0 : ReportOutOfMemory(cx);
8583 0 : return false;
8584 : }
8585 0 : cursor += compiledSize;
8586 :
8587 : // Due to the hash comparison made by openEntryForRead, this should succeed
8588 : // with high probability.
8589 0 : ModuleCharsForLookup moduleChars;
8590 0 : cursor = moduleChars.deserialize(cursor);
8591 0 : if (!moduleChars.match(parser))
8592 0 : return true;
8593 :
8594 : // Don't punish release users by crashing if there is a programmer error
8595 : // here, just gracefully return with a cache miss.
8596 : #ifdef NIGHTLY_BUILD
8597 0 : MOZ_RELEASE_ASSERT(cursor == entry.memory + entry.serializedSize);
8598 : #endif
8599 0 : if (cursor != entry.memory + entry.serializedSize)
8600 0 : return true;
8601 :
8602 : // See AsmJSMetadata comment as well as ModuleValidator::init().
8603 0 : asmJSMetadata->toStringStart = parser.pc->functionBox()->toStringStart;
8604 0 : asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin;
8605 0 : asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end;
8606 0 : asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
8607 0 : asmJSMetadata->scriptSource.reset(parser.ss);
8608 :
8609 0 : if (!parser.tokenStream.advance(asmJSMetadata->srcEndBeforeCurly()))
8610 0 : return false;
8611 :
8612 0 : int64_t after = PRMJ_Now();
8613 0 : int ms = (after - before) / PRMJ_USEC_PER_MSEC;
8614 0 : *compilationTimeReport = JS_smprintf("loaded from cache in %dms", ms);
8615 0 : if (!*compilationTimeReport)
8616 0 : return false;
8617 :
8618 0 : *loadedFromCache = true;
8619 0 : return true;
8620 : }
8621 :
8622 : /*****************************************************************************/
8623 : // Top-level js::CompileAsmJS
8624 :
8625 : static bool
8626 0 : NoExceptionPending(JSContext* cx)
8627 : {
8628 0 : return cx->helperThread() || !cx->isExceptionPending();
8629 : }
8630 :
8631 : static bool
8632 0 : Warn(AsmJSParser& parser, int errorNumber, const char* str)
8633 : {
8634 0 : ParseReportKind reportKind = parser.options().throwOnAsmJSValidationFailureOption &&
8635 : errorNumber == JSMSG_USE_ASM_TYPE_FAIL
8636 0 : ? ParseError
8637 0 : : ParseWarning;
8638 0 : parser.reportNoOffset(reportKind, /* strict = */ false, errorNumber, str ? str : "");
8639 0 : return false;
8640 : }
8641 :
8642 : static bool
8643 0 : EstablishPreconditions(JSContext* cx, AsmJSParser& parser)
8644 : {
8645 0 : if (!HasCompilerSupport(cx))
8646 0 : return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by lack of compiler support");
8647 :
8648 0 : switch (parser.options().asmJSOption) {
8649 : case AsmJSOption::Disabled:
8650 0 : return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by 'asmjs' runtime option");
8651 : case AsmJSOption::DisabledByDebugger:
8652 0 : return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by debugger");
8653 : case AsmJSOption::Enabled:
8654 0 : break;
8655 : }
8656 :
8657 0 : if (parser.pc->isStarGenerator() || parser.pc->isLegacyGenerator())
8658 0 : return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by generator context");
8659 :
8660 0 : if (parser.pc->isAsync())
8661 0 : return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by async context");
8662 :
8663 0 : if (parser.pc->isArrowFunction())
8664 0 : return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by arrow function context");
8665 :
8666 : // Class constructors are also methods
8667 0 : if (parser.pc->isMethod())
8668 0 : return Warn(parser, JSMSG_USE_ASM_TYPE_FAIL, "Disabled by class constructor or method context");
8669 :
8670 0 : return true;
8671 : }
8672 :
8673 : static UniqueChars
8674 0 : BuildConsoleMessage(JSContext* cx, unsigned time, JS::AsmJSCacheResult cacheResult)
8675 : {
8676 : #ifndef JS_MORE_DETERMINISTIC
8677 0 : const char* cacheString = "";
8678 0 : switch (cacheResult) {
8679 : case JS::AsmJSCache_Success:
8680 0 : cacheString = "stored in cache";
8681 0 : break;
8682 : case JS::AsmJSCache_ModuleTooSmall:
8683 0 : cacheString = "not stored in cache (too small to benefit)";
8684 0 : break;
8685 : case JS::AsmJSCache_SynchronousScript:
8686 0 : cacheString = "unable to cache asm.js in synchronous scripts; try loading "
8687 : "asm.js via <script async> or createElement('script')";
8688 0 : break;
8689 : case JS::AsmJSCache_QuotaExceeded:
8690 0 : cacheString = "not enough temporary storage quota to store in cache";
8691 0 : break;
8692 : case JS::AsmJSCache_StorageInitFailure:
8693 0 : cacheString = "storage initialization failed (consider filing a bug)";
8694 0 : break;
8695 : case JS::AsmJSCache_Disabled_Internal:
8696 0 : cacheString = "caching disabled by internal configuration (consider filing a bug)";
8697 0 : break;
8698 : case JS::AsmJSCache_Disabled_ShellFlags:
8699 0 : cacheString = "caching disabled by missing command-line arguments";
8700 0 : break;
8701 : case JS::AsmJSCache_Disabled_JitInspector:
8702 0 : cacheString = "caching disabled by active JIT inspector";
8703 0 : break;
8704 : case JS::AsmJSCache_InternalError:
8705 0 : cacheString = "unable to store in cache due to internal error (consider filing a bug)";
8706 0 : break;
8707 : case JS::AsmJSCache_Disabled_PrivateBrowsing:
8708 0 : cacheString = "caching disabled by private browsing mode";
8709 0 : break;
8710 : case JS::AsmJSCache_LIMIT:
8711 0 : MOZ_CRASH("bad AsmJSCacheResult");
8712 : break;
8713 : }
8714 :
8715 0 : return JS_smprintf("total compilation time %dms; %s", time, cacheString);
8716 : #else
8717 : return DuplicateString("");
8718 : #endif
8719 : }
8720 :
8721 : bool
8722 0 : js::CompileAsmJS(JSContext* cx, AsmJSParser& parser, ParseNode* stmtList, bool* validated)
8723 : {
8724 0 : *validated = false;
8725 :
8726 : // Various conditions disable asm.js optimizations.
8727 0 : if (!EstablishPreconditions(cx, parser))
8728 0 : return NoExceptionPending(cx);
8729 :
8730 : // Before spending any time parsing the module, try to look it up in the
8731 : // embedding's cache using the chars about to be parsed as the key.
8732 : bool loadedFromCache;
8733 0 : SharedModule module;
8734 0 : UniqueChars message;
8735 0 : if (!LookupAsmJSModuleInCache(cx, parser, &loadedFromCache, &module, &message))
8736 0 : return false;
8737 :
8738 : // If not present in the cache, parse, validate and generate code in a
8739 : // single linear pass over the chars of the asm.js module.
8740 0 : if (!loadedFromCache) {
8741 : // "Checking" parses, validates and compiles, producing a fully compiled
8742 : // WasmModuleObject as result.
8743 : unsigned time;
8744 0 : module = CheckModule(cx, parser, stmtList, &time);
8745 0 : if (!module)
8746 0 : return NoExceptionPending(cx);
8747 :
8748 : // Try to store the AsmJSModule in the embedding's cache. The
8749 : // AsmJSModule must be stored before static linking since static linking
8750 : // specializes the AsmJSModule to the current process's address space
8751 : // and therefore must be executed after a cache hit.
8752 0 : JS::AsmJSCacheResult cacheResult = StoreAsmJSModuleInCache(parser, *module, cx);
8753 :
8754 : // Build the string message to display in the developer console.
8755 0 : message = BuildConsoleMessage(cx, time, cacheResult);
8756 0 : if (!message)
8757 0 : return NoExceptionPending(cx);
8758 : }
8759 :
8760 : // Hand over ownership to a GC object wrapper which can then be referenced
8761 : // from the module function.
8762 0 : Rooted<WasmModuleObject*> moduleObj(cx, WasmModuleObject::create(cx, *module));
8763 0 : if (!moduleObj)
8764 0 : return false;
8765 :
8766 : // The module function dynamically links the AsmJSModule when called and
8767 : // generates a set of functions wrapping all the exports.
8768 0 : FunctionBox* funbox = parser.pc->functionBox();
8769 0 : RootedFunction moduleFun(cx, NewAsmJSModuleFunction(cx, funbox->function(), moduleObj));
8770 0 : if (!moduleFun)
8771 0 : return false;
8772 :
8773 : // Finished! Clobber the default function created by the parser with the new
8774 : // asm.js module function. Special cases in the bytecode emitter avoid
8775 : // generating bytecode for asm.js functions, allowing this asm.js module
8776 : // function to be the finished result.
8777 0 : MOZ_ASSERT(funbox->function()->isInterpreted());
8778 0 : funbox->object = moduleFun;
8779 :
8780 : // Success! Write to the console with a "warning" message.
8781 0 : *validated = true;
8782 0 : Warn(parser, JSMSG_USE_ASM_TYPE_OK, message.get());
8783 0 : return NoExceptionPending(cx);
8784 : }
8785 :
8786 : /*****************************************************************************/
8787 : // asm.js testing functions
8788 :
8789 : bool
8790 0 : js::IsAsmJSModuleNative(Native native)
8791 : {
8792 0 : return native == InstantiateAsmJS;
8793 : }
8794 :
8795 : bool
8796 958 : js::IsAsmJSModule(JSFunction* fun)
8797 : {
8798 958 : return fun->maybeNative() == InstantiateAsmJS;
8799 : }
8800 :
8801 : bool
8802 4 : js::IsAsmJSFunction(JSFunction* fun)
8803 : {
8804 4 : if (IsExportedFunction(fun))
8805 0 : return ExportedFunctionToInstance(fun).metadata().isAsmJS();
8806 4 : return false;
8807 : }
8808 :
8809 : bool
8810 0 : js::IsAsmJSStrictModeModuleOrFunction(JSFunction* fun)
8811 : {
8812 0 : if (IsAsmJSModule(fun))
8813 0 : return AsmJSModuleFunctionToModule(fun).metadata().asAsmJS().strict;
8814 :
8815 0 : if (IsAsmJSFunction(fun))
8816 0 : return ExportedFunctionToInstance(fun).metadata().asAsmJS().strict;
8817 :
8818 0 : return false;
8819 : }
8820 :
8821 : bool
8822 0 : js::IsAsmJSCompilationAvailable(JSContext* cx, unsigned argc, Value* vp)
8823 : {
8824 0 : CallArgs args = CallArgsFromVp(argc, vp);
8825 :
8826 : // See EstablishPreconditions.
8827 0 : bool available = HasCompilerSupport(cx) && cx->options().asmJS();
8828 :
8829 0 : args.rval().set(BooleanValue(available));
8830 0 : return true;
8831 : }
8832 :
8833 : static JSFunction*
8834 0 : MaybeWrappedNativeFunction(const Value& v)
8835 : {
8836 0 : if (!v.isObject())
8837 0 : return nullptr;
8838 :
8839 0 : JSObject* obj = CheckedUnwrap(&v.toObject());
8840 0 : if (!obj)
8841 0 : return nullptr;
8842 :
8843 0 : if (!obj->is<JSFunction>())
8844 0 : return nullptr;
8845 :
8846 0 : return &obj->as<JSFunction>();
8847 : }
8848 :
8849 : bool
8850 0 : js::IsAsmJSModule(JSContext* cx, unsigned argc, Value* vp)
8851 : {
8852 0 : CallArgs args = CallArgsFromVp(argc, vp);
8853 :
8854 0 : bool rval = false;
8855 0 : if (JSFunction* fun = MaybeWrappedNativeFunction(args.get(0)))
8856 0 : rval = IsAsmJSModule(fun);
8857 :
8858 0 : args.rval().set(BooleanValue(rval));
8859 0 : return true;
8860 : }
8861 :
8862 : bool
8863 0 : js::IsAsmJSFunction(JSContext* cx, unsigned argc, Value* vp)
8864 : {
8865 0 : CallArgs args = CallArgsFromVp(argc, vp);
8866 :
8867 0 : bool rval = false;
8868 0 : if (JSFunction* fun = MaybeWrappedNativeFunction(args.get(0)))
8869 0 : rval = IsAsmJSFunction(fun);
8870 :
8871 0 : args.rval().set(BooleanValue(rval));
8872 0 : return true;
8873 : }
8874 :
8875 : bool
8876 0 : js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp)
8877 : {
8878 0 : CallArgs args = CallArgsFromVp(argc, vp);
8879 :
8880 0 : JSFunction* fun = MaybeWrappedNativeFunction(args.get(0));
8881 0 : if (!fun || !IsAsmJSModule(fun)) {
8882 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_USE_ASM_TYPE_FAIL,
8883 : "argument passed to isAsmJSModuleLoadedFromCache is not a "
8884 0 : "validated asm.js module");
8885 0 : return false;
8886 : }
8887 :
8888 : bool loadedFromCache =
8889 0 : AsmJSModuleFunctionToModule(fun).metadata().asAsmJS().cacheResult == CacheResult::Hit;
8890 :
8891 0 : args.rval().set(BooleanValue(loadedFromCache));
8892 0 : return true;
8893 : }
8894 :
8895 : /*****************************************************************************/
8896 : // asm.js toString/toSource support
8897 :
8898 : JSString*
8899 0 : js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda)
8900 : {
8901 0 : MOZ_ASSERT(IsAsmJSModule(fun));
8902 :
8903 0 : const AsmJSMetadata& metadata = AsmJSModuleFunctionToModule(fun).metadata().asAsmJS();
8904 0 : uint32_t begin = metadata.toStringStart;
8905 0 : uint32_t end = metadata.srcEndAfterCurly();
8906 0 : ScriptSource* source = metadata.scriptSource.get();
8907 :
8908 0 : StringBuffer out(cx);
8909 :
8910 0 : if (addParenToLambda && fun->isLambda() && !out.append("("))
8911 0 : return nullptr;
8912 :
8913 0 : bool haveSource = source->hasSourceData();
8914 0 : if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
8915 0 : return nullptr;
8916 :
8917 0 : if (!haveSource) {
8918 0 : if (!out.append("function "))
8919 0 : return nullptr;
8920 0 : if (fun->explicitName() && !out.append(fun->explicitName()))
8921 0 : return nullptr;
8922 0 : if (!out.append("() {\n [sourceless code]\n}"))
8923 0 : return nullptr;
8924 : } else {
8925 0 : Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
8926 0 : if (!src)
8927 0 : return nullptr;
8928 :
8929 0 : if (!out.append(src))
8930 0 : return nullptr;
8931 : }
8932 :
8933 0 : if (addParenToLambda && fun->isLambda() && !out.append(")"))
8934 0 : return nullptr;
8935 :
8936 0 : return out.finishString();
8937 : }
8938 :
8939 : JSString*
8940 0 : js::AsmJSFunctionToString(JSContext* cx, HandleFunction fun)
8941 : {
8942 0 : MOZ_ASSERT(IsAsmJSFunction(fun));
8943 :
8944 0 : const AsmJSMetadata& metadata = ExportedFunctionToInstance(fun).metadata().asAsmJS();
8945 0 : const AsmJSExport& f = metadata.lookupAsmJSExport(ExportedFunctionToFuncIndex(fun));
8946 :
8947 0 : uint32_t begin = metadata.srcStart + f.startOffsetInModule();
8948 0 : uint32_t end = metadata.srcStart + f.endOffsetInModule();
8949 :
8950 0 : ScriptSource* source = metadata.scriptSource.get();
8951 0 : StringBuffer out(cx);
8952 :
8953 0 : if (!out.append("function "))
8954 0 : return nullptr;
8955 :
8956 0 : bool haveSource = source->hasSourceData();
8957 0 : if (!haveSource && !JSScript::loadSource(cx, source, &haveSource))
8958 0 : return nullptr;
8959 :
8960 0 : if (!haveSource) {
8961 : // asm.js functions can't be anonymous
8962 0 : MOZ_ASSERT(fun->explicitName());
8963 0 : if (!out.append(fun->explicitName()))
8964 0 : return nullptr;
8965 0 : if (!out.append("() {\n [sourceless code]\n}"))
8966 0 : return nullptr;
8967 : } else {
8968 0 : Rooted<JSFlatString*> src(cx, source->substring(cx, begin, end));
8969 0 : if (!src)
8970 0 : return nullptr;
8971 0 : if (!out.append(src))
8972 0 : return nullptr;
8973 : }
8974 :
8975 0 : return out.finishString();
8976 : }
8977 :
8978 : bool
8979 0 : js::IsValidAsmJSHeapLength(uint32_t length)
8980 : {
8981 0 : if (length < MinHeapLength)
8982 0 : return false;
8983 :
8984 0 : return wasm::IsValidARMImmediate(length);
8985 : }
|