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