WasmJS.cpp (178281B)
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 2016 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/WasmJS.h" 20 21 #include "mozilla/Maybe.h" 22 23 #include <algorithm> 24 #include <cstdint> 25 26 #include "jsapi.h" 27 #include "jsexn.h" 28 29 #include "ds/IdValuePair.h" // js::IdValuePair 30 #include "frontend/FrontendContext.h" // AutoReportFrontendContext 31 #include "gc/GCContext.h" 32 #include "jit/AtomicOperations.h" 33 #include "jit/FlushICache.h" 34 #include "jit/JitContext.h" 35 #include "jit/JitOptions.h" 36 #include "jit/Simulator.h" 37 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin 38 #include "js/ForOfIterator.h" 39 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* 40 #include "js/Printf.h" 41 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty 42 #include "js/PropertySpec.h" // JS_{PS,FN}{,_END} 43 #include "js/Stack.h" // BuildStackString 44 #include "js/StreamConsumer.h" 45 #include "util/StringBuilder.h" 46 #include "util/Text.h" 47 #include "vm/ErrorObject.h" 48 #include "vm/FunctionFlags.h" // js::FunctionFlags 49 #include "vm/GlobalObject.h" // js::GlobalObject 50 #include "vm/HelperThreadState.h" // js::PromiseHelperTask 51 #include "vm/Interpreter.h" 52 #include "vm/JSFunction.h" 53 #include "vm/PlainObject.h" // js::PlainObject 54 #include "vm/PromiseObject.h" // js::PromiseObject 55 #include "vm/SharedArrayObject.h" 56 #include "vm/StringType.h" 57 #include "vm/Warnings.h" // js::WarnNumberASCII 58 #include "wasm/WasmBaselineCompile.h" 59 #include "wasm/WasmBuiltinModule.h" 60 #include "wasm/WasmBuiltins.h" 61 #include "wasm/WasmCompile.h" 62 #include "wasm/WasmDebug.h" 63 #include "wasm/WasmFeatures.h" 64 #include "wasm/WasmInstance.h" 65 #include "wasm/WasmIonCompile.h" 66 #include "wasm/WasmMemory.h" 67 #include "wasm/WasmModule.h" 68 #include "wasm/WasmPI.h" 69 #include "wasm/WasmProcess.h" 70 #include "wasm/WasmSignalHandlers.h" 71 #include "wasm/WasmStubs.h" 72 #include "wasm/WasmValidate.h" 73 74 #include "gc/GCContext-inl.h" 75 #include "gc/StableCellHasher-inl.h" 76 #include "vm/ArrayBufferObject-inl.h" 77 #include "vm/JSObject-inl.h" 78 #include "vm/NativeObject-inl.h" 79 #include "wasm/WasmInstance-inl.h" 80 81 /* 82 * [SMDOC] WebAssembly code rules (evolving) 83 * 84 * TlsContext.get() is only to be invoked from functions that have been invoked 85 * _directly_ by generated code as cold(!) Builtin calls, from code that is 86 * only used by signal handlers, or from helper functions that have been 87 * called _directly_ from a simulator. All other code shall pass in a 88 * JSContext* to functions that need it, or an Instance* or Instance* since 89 * the context is available through them. 90 * 91 * Code that uses TlsContext.get() shall annotate each such call with the 92 * reason why the call is OK. 93 */ 94 95 using namespace js; 96 using namespace js::jit; 97 using namespace js::wasm; 98 99 using mozilla::Maybe; 100 using mozilla::Nothing; 101 using mozilla::Some; 102 using mozilla::Span; 103 104 static bool ThrowCompileOutOfMemory(JSContext* cx) { 105 // Most OOMs during compilation are due to large contiguous allocations, 106 // and future allocations are likely to succeed. Throwing a proper error 107 // object is nicer for users in these circumstances. 108 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY); 109 return false; 110 } 111 112 // ============================================================================ 113 // Imports 114 115 static bool ThrowBadImportArg(JSContext* cx) { 116 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 117 JSMSG_WASM_BAD_IMPORT_ARG); 118 return false; 119 } 120 121 static bool ThrowBadImportType(JSContext* cx, const CacheableName& field, 122 const char* str) { 123 UniqueChars fieldQuoted = field.toQuotedString(cx); 124 if (!fieldQuoted) { 125 ReportOutOfMemory(cx); 126 return false; 127 } 128 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 129 JSMSG_WASM_BAD_IMPORT_TYPE, fieldQuoted.get(), str); 130 return false; 131 } 132 133 // For now reject cross-compartment wrappers. These have more complicated realm 134 // semantics (we use nonCCWRealm in a few places) and may require unwrapping to 135 // test for specific function types. 136 static bool IsCallableNonCCW(const Value& v) { 137 return IsCallable(v) && !IsCrossCompartmentWrapper(&v.toObject()); 138 } 139 140 static bool IsWasmSuspendingWrapper(const Value& v) { 141 return v.isObject() && js::IsWasmSuspendingObject(&v.toObject()); 142 } 143 144 bool js::wasm::GetImports(JSContext* cx, const Module& module, 145 HandleObject importObj, ImportValues* imports) { 146 const ModuleMetadata& moduleMeta = module.moduleMeta(); 147 const CodeMetadata& codeMeta = module.codeMeta(); 148 const BuiltinModuleIds& builtinModules = codeMeta.features().builtinModules; 149 150 if (!moduleMeta.imports.empty() && !importObj) { 151 return ThrowBadImportArg(cx); 152 } 153 154 BuiltinModuleInstances builtinInstances(cx); 155 RootedValue importModuleValue(cx); 156 RootedObject importModuleObject(cx); 157 bool isImportedStringModule = false; 158 RootedValue importFieldValue(cx); 159 160 uint32_t tagIndex = 0; 161 const TagDescVector& tags = codeMeta.tags; 162 uint32_t globalIndex = 0; 163 const GlobalDescVector& globals = codeMeta.globals; 164 uint32_t tableIndex = 0; 165 const TableDescVector& tables = codeMeta.tables; 166 for (const Import& import : moduleMeta.imports) { 167 Maybe<BuiltinModuleId> builtinModule = 168 ImportMatchesBuiltinModule(import.module.utf8Bytes(), builtinModules); 169 if (builtinModule) { 170 if (*builtinModule == BuiltinModuleId::JSStringConstants) { 171 isImportedStringModule = true; 172 importModuleObject = nullptr; 173 } else { 174 MutableHandle<JSObject*> builtinInstance = 175 builtinInstances[*builtinModule]; 176 177 // If this module has not been instantiated yet, do so now. 178 if (!builtinInstance) { 179 // Use the first imported memory, if it exists, when compiling the 180 // builtin module. 181 const Import* firstMemoryImport = 182 (codeMeta.memories.empty() || 183 codeMeta.memories[0].importIndex.isNothing()) 184 ? nullptr 185 : &moduleMeta.imports[*codeMeta.memories[0].importIndex]; 186 187 // Compile and instantiate the builtin module. This uses our module's 188 // importObj so that it can read the memory import that we provided 189 // above. 190 if (!wasm::InstantiateBuiltinModule(cx, *builtinModule, 191 firstMemoryImport, importObj, 192 builtinInstance)) { 193 return false; 194 } 195 } 196 isImportedStringModule = false; 197 importModuleObject = builtinInstance; 198 } 199 } else { 200 RootedId moduleName(cx); 201 if (!import.module.toPropertyKey(cx, &moduleName)) { 202 return false; 203 } 204 205 if (!GetProperty(cx, importObj, importObj, moduleName, 206 &importModuleValue)) { 207 return false; 208 } 209 210 if (!importModuleValue.isObject()) { 211 UniqueChars moduleQuoted = import.module.toQuotedString(cx); 212 if (!moduleQuoted) { 213 ReportOutOfMemory(cx); 214 return false; 215 } 216 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 217 JSMSG_WASM_BAD_IMPORT_FIELD, 218 moduleQuoted.get()); 219 return false; 220 } 221 222 isImportedStringModule = false; 223 importModuleObject = &importModuleValue.toObject(); 224 } 225 MOZ_RELEASE_ASSERT(!isImportedStringModule || 226 import.kind == DefinitionKind::Global); 227 228 if (isImportedStringModule) { 229 RootedString stringConstant(cx, import.field.toJSString(cx)); 230 if (!stringConstant) { 231 ReportOutOfMemory(cx); 232 return false; 233 } 234 importFieldValue = StringValue(stringConstant); 235 } else { 236 RootedId fieldName(cx); 237 if (!import.field.toPropertyKey(cx, &fieldName)) { 238 return false; 239 } 240 if (!GetProperty(cx, importModuleObject, importModuleObject, fieldName, 241 &importFieldValue)) { 242 return false; 243 } 244 } 245 246 switch (import.kind) { 247 case DefinitionKind::Function: { 248 if (!IsCallableNonCCW(importFieldValue) && 249 !IsWasmSuspendingWrapper(importFieldValue)) { 250 return ThrowBadImportType(cx, import.field, "Function"); 251 } 252 253 if (!imports->funcs.append(&importFieldValue.toObject())) { 254 ReportOutOfMemory(cx); 255 return false; 256 } 257 258 break; 259 } 260 case DefinitionKind::Table: { 261 const uint32_t index = tableIndex++; 262 if (!importFieldValue.isObject() || 263 !importFieldValue.toObject().is<WasmTableObject>()) { 264 return ThrowBadImportType(cx, import.field, "Table"); 265 } 266 267 Rooted<WasmTableObject*> obj( 268 cx, &importFieldValue.toObject().as<WasmTableObject>()); 269 if (obj->table().elemType() != tables[index].elemType) { 270 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 271 JSMSG_WASM_BAD_TBL_TYPE_LINK); 272 return false; 273 } 274 275 if (!imports->tables.append(obj)) { 276 ReportOutOfMemory(cx); 277 return false; 278 } 279 break; 280 } 281 case DefinitionKind::Memory: { 282 if (!importFieldValue.isObject() || 283 !importFieldValue.toObject().is<WasmMemoryObject>()) { 284 return ThrowBadImportType(cx, import.field, "Memory"); 285 } 286 287 if (!imports->memories.append( 288 &importFieldValue.toObject().as<WasmMemoryObject>())) { 289 ReportOutOfMemory(cx); 290 return false; 291 } 292 break; 293 } 294 case DefinitionKind::Tag: { 295 const uint32_t index = tagIndex++; 296 if (!importFieldValue.isObject() || 297 !importFieldValue.toObject().is<WasmTagObject>()) { 298 return ThrowBadImportType(cx, import.field, "Tag"); 299 } 300 301 Rooted<WasmTagObject*> obj( 302 cx, &importFieldValue.toObject().as<WasmTagObject>()); 303 304 // Checks whether the signature of the imported exception object matches 305 // the signature declared in the exception import's TagDesc. 306 if (!TagType::matches(*obj->tagType(), *tags[index].type)) { 307 UniqueChars fieldQuoted = import.field.toQuotedString(cx); 308 UniqueChars moduleQuoted = import.module.toQuotedString(cx); 309 if (!fieldQuoted || !moduleQuoted) { 310 ReportOutOfMemory(cx); 311 return false; 312 } 313 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 314 JSMSG_WASM_BAD_TAG_SIG, moduleQuoted.get(), 315 fieldQuoted.get()); 316 return false; 317 } 318 319 if (!imports->tagObjs.append(obj)) { 320 ReportOutOfMemory(cx); 321 return false; 322 } 323 break; 324 } 325 case DefinitionKind::Global: { 326 const uint32_t index = globalIndex++; 327 const GlobalDesc& global = globals[index]; 328 MOZ_ASSERT(global.importIndex() == index); 329 330 RootedVal val(cx); 331 if (importFieldValue.isObject() && 332 importFieldValue.toObject().is<WasmGlobalObject>()) { 333 Rooted<WasmGlobalObject*> obj( 334 cx, &importFieldValue.toObject().as<WasmGlobalObject>()); 335 336 if (obj->isMutable() != global.isMutable()) { 337 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 338 JSMSG_WASM_BAD_GLOB_MUT_LINK); 339 return false; 340 } 341 342 bool matches = global.isMutable() 343 ? obj->type() == global.type() 344 : ValType::isSubTypeOf(obj->type(), global.type()); 345 if (!matches) { 346 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 347 JSMSG_WASM_BAD_GLOB_TYPE_LINK); 348 return false; 349 } 350 351 if (imports->globalObjs.length() <= index && 352 !imports->globalObjs.resize(index + 1)) { 353 ReportOutOfMemory(cx); 354 return false; 355 } 356 imports->globalObjs[index] = obj; 357 val = obj->val(); 358 } else { 359 if (!global.type().isRefType()) { 360 if (global.type() == ValType::I64 && !importFieldValue.isBigInt()) { 361 return ThrowBadImportType(cx, import.field, "BigInt"); 362 } 363 if (global.type() != ValType::I64 && !importFieldValue.isNumber()) { 364 return ThrowBadImportType(cx, import.field, "Number"); 365 } 366 } 367 368 if (global.isMutable()) { 369 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 370 JSMSG_WASM_BAD_GLOB_MUT_LINK); 371 return false; 372 } 373 374 if (!Val::fromJSValue(cx, global.type(), importFieldValue, &val)) { 375 return false; 376 } 377 } 378 379 if (!imports->globalValues.append(val)) { 380 ReportOutOfMemory(cx); 381 return false; 382 } 383 384 break; 385 } 386 } 387 } 388 389 MOZ_ASSERT(globalIndex == globals.length() || 390 !globals[globalIndex].isImport()); 391 392 return true; 393 } 394 395 static bool DescribeScriptedCaller(JSContext* cx, ScriptedCaller* caller, 396 const char* introducer) { 397 // Note: JS::DescribeScriptedCaller returns whether a scripted caller was 398 // found, not whether an error was thrown. This wrapper function converts 399 // back to the more ordinary false-if-error form. 400 401 JS::AutoFilename af; 402 if (JS::DescribeScriptedCaller(&af, cx, &caller->line)) { 403 caller->filename = 404 FormatIntroducedFilename(af.get(), caller->line, introducer); 405 if (!caller->filename) { 406 ReportOutOfMemory(cx); 407 return false; 408 } 409 } 410 411 return true; 412 } 413 414 static SharedCompileArgs InitCompileArgs(JSContext* cx, 415 const FeatureOptions& options, 416 const char* introducer) { 417 ScriptedCaller scriptedCaller; 418 if (!DescribeScriptedCaller(cx, &scriptedCaller, introducer)) { 419 return nullptr; 420 } 421 422 return CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options); 423 } 424 425 // ============================================================================ 426 // Testing / Fuzzing support 427 428 bool wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code, 429 HandleObject importObj, 430 MutableHandle<WasmInstanceObject*> instanceObj) { 431 if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) { 432 return false; 433 } 434 435 FeatureOptions options; 436 SharedCompileArgs compileArgs = InitCompileArgs(cx, options, "wasm_eval"); 437 if (!compileArgs) { 438 return false; 439 } 440 441 BytecodeSource source((uint8_t*)code->dataPointerEither().unwrap(), 442 code->byteLength().valueOr(0)); 443 UniqueChars error; 444 UniqueCharsVector warnings; 445 SharedModule module = CompileBuffer( 446 *compileArgs, BytecodeBufferOrSource(source), &error, &warnings, nullptr); 447 if (!module) { 448 if (error) { 449 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 450 JSMSG_WASM_COMPILE_ERROR, error.get()); 451 return false; 452 } 453 return ThrowCompileOutOfMemory(cx); 454 } 455 456 Rooted<ImportValues> imports(cx); 457 if (!GetImports(cx, *module, importObj, imports.address())) { 458 return false; 459 } 460 461 return module->instantiate(cx, imports.get(), nullptr, instanceObj); 462 } 463 464 struct MOZ_STACK_CLASS SerializeListener : JS::OptimizedEncodingListener { 465 // MOZ_STACK_CLASS means these can be nops. 466 MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override { return 0; } 467 MozExternalRefCountType MOZ_XPCOM_ABI Release() override { return 0; } 468 469 mozilla::DebugOnly<bool> called = false; 470 Bytes* serialized; 471 explicit SerializeListener(Bytes* serialized) : serialized(serialized) {} 472 473 void storeOptimizedEncoding(const uint8_t* bytes, size_t length) override { 474 MOZ_ASSERT(!called); 475 called = true; 476 if (serialized->resizeUninitialized(length)) { 477 memcpy(serialized->begin(), bytes, length); 478 } 479 } 480 }; 481 482 bool wasm::CompileAndSerialize(JSContext* cx, 483 const BytecodeSource& bytecodeSource, 484 Bytes* serialized) { 485 // The caller must check that code caching is available 486 MOZ_ASSERT(CodeCachingAvailable(cx)); 487 488 // Create and manually fill in compile args for code caching 489 MutableCompileArgs compileArgs = js_new<CompileArgs>(); 490 if (!compileArgs) { 491 return false; 492 } 493 494 // The caller has ensured CodeCachingAvailable(). Moreover, we want to ensure 495 // we go straight to tier-2 so that we synchronously call 496 // JS::OptimizedEncodingListener::storeOptimizedEncoding(). 497 compileArgs->baselineEnabled = false; 498 compileArgs->forceTiering = false; 499 500 // We always pick Ion here, and we depend on CodeCachingAvailable() having 501 // determined that Ion is available, see comments at CodeCachingAvailable(). 502 // To do better, we need to pass information about which compiler that should 503 // be used into CompileAndSerialize(). 504 compileArgs->ionEnabled = true; 505 506 // Select features that are enabled. This is guaranteed to be consistent with 507 // our compiler selection, as code caching is only available if ion is 508 // available, and ion is only available if it's not disabled by enabled 509 // features. 510 compileArgs->features = FeatureArgs::build(cx, FeatureOptions()); 511 512 SerializeListener listener(serialized); 513 514 UniqueChars error; 515 UniqueCharsVector warnings; 516 SharedModule module = 517 CompileBuffer(*compileArgs, BytecodeBufferOrSource(bytecodeSource), 518 &error, &warnings, &listener); 519 if (!module) { 520 fprintf(stderr, "Compilation error: %s\n", error ? error.get() : "oom"); 521 return false; 522 } 523 524 MOZ_ASSERT(module->code().hasCompleteTier(Tier::Serialized)); 525 MOZ_ASSERT(listener.called); 526 return !listener.serialized->empty(); 527 } 528 529 bool wasm::DeserializeModule(JSContext* cx, const Bytes& serialized, 530 MutableHandleObject moduleObj) { 531 MutableModule module = 532 Module::deserialize(serialized.begin(), serialized.length()); 533 if (!module) { 534 ReportOutOfMemory(cx); 535 return false; 536 } 537 538 moduleObj.set(module->createObject(cx)); 539 return !!moduleObj; 540 } 541 542 // ============================================================================ 543 // Common functions 544 545 // '[EnforceRange] unsigned long' types are coerced with 546 // ConvertToInt(v, 32, 'unsigned') 547 // defined in Web IDL Section 3.2.4.9. 548 // 549 // This just generalizes that to an arbitrary limit that is representable as an 550 // integer in double form. 551 552 static bool EnforceRange(JSContext* cx, HandleValue v, const char* kind, 553 const char* noun, uint64_t max, uint64_t* val) { 554 // Step 4. 555 double x; 556 if (!ToNumber(cx, v, &x)) { 557 return false; 558 } 559 560 // Step 5. 561 if (mozilla::IsNegativeZero(x)) { 562 x = 0.0; 563 } 564 565 // Step 6.1. 566 if (!std::isfinite(x)) { 567 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 568 JSMSG_WASM_BAD_ENFORCE_RANGE, kind, noun); 569 return false; 570 } 571 572 // Step 6.2. 573 x = JS::ToInteger(x); 574 575 // Step 6.3. 576 if (x < 0 || x > double(max)) { 577 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 578 JSMSG_WASM_BAD_ENFORCE_RANGE, kind, noun); 579 return false; 580 } 581 582 *val = uint64_t(x); 583 MOZ_ASSERT(double(*val) == x); 584 return true; 585 } 586 587 static bool EnforceRangeU32(JSContext* cx, HandleValue v, const char* kind, 588 const char* noun, uint32_t* u32) { 589 uint64_t u64 = 0; 590 if (!EnforceRange(cx, v, kind, noun, uint64_t(UINT32_MAX), &u64)) { 591 return false; 592 } 593 *u32 = uint32_t(u64); 594 return true; 595 } 596 597 static bool EnforceRangeU64(JSContext* cx, HandleValue v, const char* kind, 598 const char* noun, uint64_t* u64) { 599 // The max is Number.MAX_SAFE_INTEGER 600 return EnforceRange(cx, v, kind, noun, (1LL << 53) - 1, u64); 601 } 602 603 static bool EnforceRangeBigInt64(JSContext* cx, HandleValue v, const char* kind, 604 const char* noun, uint64_t* u64) { 605 RootedBigInt bi(cx, ToBigInt(cx, v)); 606 if (!bi) { 607 return false; 608 } 609 if (!BigInt::isUint64(bi, u64)) { 610 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 611 JSMSG_WASM_BAD_ENFORCE_RANGE, kind, noun); 612 return false; 613 } 614 return true; 615 } 616 617 static bool EnforceAddressValue(JSContext* cx, HandleValue v, 618 AddressType addressType, const char* kind, 619 const char* noun, uint64_t* result) { 620 switch (addressType) { 621 case AddressType::I32: { 622 uint32_t result32; 623 if (!EnforceRangeU32(cx, v, kind, noun, &result32)) { 624 return false; 625 } 626 *result = uint64_t(result32); 627 return true; 628 } 629 case AddressType::I64: 630 return EnforceRangeBigInt64(cx, v, kind, noun, result); 631 default: 632 MOZ_CRASH("unknown address type"); 633 } 634 } 635 636 // The AddressValue typedef, a union of number and bigint, is used in the JS API 637 // spec for memory and table arguments, where number is used for memory32 and 638 // bigint is used for memory64. 639 [[nodiscard]] static bool CreateAddressValue(JSContext* cx, uint64_t value, 640 AddressType addressType, 641 MutableHandleValue addressValue) { 642 switch (addressType) { 643 case AddressType::I32: 644 MOZ_ASSERT(value <= UINT32_MAX); 645 addressValue.set(NumberValue(value)); 646 return true; 647 case AddressType::I64: { 648 BigInt* bi = BigInt::createFromUint64(cx, value); 649 if (!bi) { 650 return false; 651 } 652 addressValue.set(BigIntValue(bi)); 653 return true; 654 } 655 default: 656 MOZ_CRASH("unknown address type"); 657 } 658 } 659 660 // Gets an AddressValue property ("initial" or "maximum") from a 661 // MemoryDescriptor or TableDescriptor. The values returned by this should be 662 // run through CheckLimits to enforce the validation limits prescribed by the 663 // spec. 664 static bool GetDescriptorAddressValue(JSContext* cx, HandleObject obj, 665 const char* name, const char* noun, 666 const char* msg, AddressType addressType, 667 bool* found, uint64_t* value) { 668 JSAtom* atom = Atomize(cx, name, strlen(name)); 669 if (!atom) { 670 return false; 671 } 672 RootedId id(cx, AtomToId(atom)); 673 674 RootedValue val(cx); 675 if (!GetProperty(cx, obj, obj, id, &val)) { 676 return false; 677 } 678 679 if (val.isUndefined()) { 680 *found = false; 681 return true; 682 } 683 *found = true; 684 685 return EnforceAddressValue(cx, val, addressType, noun, msg, value); 686 } 687 688 static bool GetLimits(JSContext* cx, HandleObject obj, LimitsKind kind, 689 Limits* limits) { 690 limits->addressType = AddressType::I32; 691 692 // Limits may specify an alternate address type, and we need this to check the 693 // ranges for initial and maximum, so look for the address type first. 694 // Get the address type field 695 JSAtom* addressTypeAtom = Atomize(cx, "address", strlen("address")); 696 if (!addressTypeAtom) { 697 return false; 698 } 699 RootedId addressTypeId(cx, AtomToId(addressTypeAtom)); 700 RootedValue addressTypeVal(cx); 701 if (!GetProperty(cx, obj, obj, addressTypeId, &addressTypeVal)) { 702 return false; 703 } 704 705 // The address type has a default value 706 if (!addressTypeVal.isUndefined()) { 707 if (!ToAddressType(cx, addressTypeVal, &limits->addressType)) { 708 return false; 709 } 710 } 711 712 const char* noun = ToString(kind); 713 uint64_t limit = 0; 714 715 bool haveInitial = false; 716 if (!GetDescriptorAddressValue(cx, obj, "initial", noun, "initial size", 717 limits->addressType, &haveInitial, &limit)) { 718 return false; 719 } 720 if (haveInitial) { 721 limits->initial = limit; 722 } 723 724 bool haveMinimum = false; 725 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 726 if (!GetDescriptorAddressValue(cx, obj, "minimum", noun, "initial size", 727 limits->addressType, &haveMinimum, &limit)) { 728 return false; 729 } 730 if (haveMinimum) { 731 limits->initial = limit; 732 } 733 #endif 734 735 if (!(haveInitial || haveMinimum)) { 736 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 737 JSMSG_WASM_MISSING_REQUIRED, "initial"); 738 return false; 739 } 740 if (haveInitial && haveMinimum) { 741 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 742 JSMSG_WASM_SUPPLY_ONLY_ONE, "minimum", "initial"); 743 return false; 744 } 745 746 bool haveMaximum = false; 747 if (!GetDescriptorAddressValue(cx, obj, "maximum", noun, "maximum size", 748 limits->addressType, &haveMaximum, &limit)) { 749 return false; 750 } 751 if (haveMaximum) { 752 limits->maximum = Some(limit); 753 } 754 755 limits->shared = Shareable::False; 756 757 // Memory limits may be shared. 758 if (kind == LimitsKind::Memory) { 759 // Get the shared field 760 JSAtom* sharedAtom = Atomize(cx, "shared", strlen("shared")); 761 if (!sharedAtom) { 762 return false; 763 } 764 RootedId sharedId(cx, AtomToId(sharedAtom)); 765 766 RootedValue sharedVal(cx); 767 if (!GetProperty(cx, obj, obj, sharedId, &sharedVal)) { 768 return false; 769 } 770 771 // shared's default value is false, which is already the value set above. 772 if (!sharedVal.isUndefined()) { 773 limits->shared = 774 ToBoolean(sharedVal) ? Shareable::True : Shareable::False; 775 776 if (limits->shared == Shareable::True) { 777 if (!haveMaximum) { 778 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 779 JSMSG_WASM_MISSING_MAXIMUM, noun); 780 return false; 781 } 782 783 if (!cx->realm() 784 ->creationOptions() 785 .getSharedMemoryAndAtomicsEnabled()) { 786 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 787 JSMSG_WASM_NO_SHMEM_LINK); 788 return false; 789 } 790 } 791 } 792 793 // TODO: Should be updated when the JS API for custom page sizes 794 // is finalized, see https://bugzilla.mozilla.org/show_bug.cgi?id=1985679 795 limits->pageSize = PageSize::Standard; 796 } 797 798 return true; 799 } 800 801 static bool CheckLimits(JSContext* cx, uint64_t validationMax, LimitsKind kind, 802 Limits* limits) { 803 const char* noun = ToString(kind); 804 805 // There are several layers of validation and error-throwing here, including 806 // one which is currently not defined by the JS API spec: 807 // 808 // - [EnforceRange] on parameters (must be TypeError) 809 // - A check that initial <= maximum (must be RangeError) 810 // - Either a mem_alloc or table_alloc operation, which has two components: 811 // - A pre-condition that the given memory or table type is valid 812 // (not specified, RangeError in practice) 813 // - The actual allocation (should report OOM if it fails) 814 // 815 // There are two questions currently left open by the spec: when is the memory 816 // or table type validated, and if it is invalid, what type of exception does 817 // it throw? In practice, all browsers throw RangeError, and by the time you 818 // read this the spec will hopefully have been updated to reflect this. See 819 // the following issue: https://github.com/WebAssembly/spec/issues/1792 820 821 // Check that initial <= maximum 822 if (limits->maximum.isSome() && *limits->maximum < limits->initial) { 823 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 824 JSMSG_WASM_MAX_LT_INITIAL, noun); 825 return false; 826 } 827 828 // Check wasm validation limits 829 if (limits->initial > validationMax) { 830 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE, 831 noun, "initial size"); 832 return false; 833 } 834 if (limits->maximum.isSome() && *limits->maximum > validationMax) { 835 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE, 836 noun, "maximum size"); 837 return false; 838 } 839 840 return true; 841 } 842 843 template <class Class, const char* name> 844 static JSObject* CreateWasmConstructor(JSContext* cx, JSProtoKey key) { 845 Rooted<JSAtom*> className(cx, Atomize(cx, name, strlen(name))); 846 if (!className) { 847 return nullptr; 848 } 849 850 return NewNativeConstructor(cx, Class::construct, 1, className); 851 } 852 853 static JSObject* GetWasmConstructorPrototype(JSContext* cx, 854 const CallArgs& callArgs, 855 JSProtoKey key) { 856 RootedObject proto(cx); 857 if (!GetPrototypeFromBuiltinConstructor(cx, callArgs, key, &proto)) { 858 return nullptr; 859 } 860 if (!proto) { 861 proto = GlobalObject::getOrCreatePrototype(cx, key); 862 } 863 return proto; 864 } 865 866 [[nodiscard]] static bool ParseValTypes(JSContext* cx, HandleValue src, 867 ValTypeVector& dest) { 868 JS::ForOfIterator iterator(cx); 869 870 if (!iterator.init(src, JS::ForOfIterator::ThrowOnNonIterable)) { 871 return false; 872 } 873 874 RootedValue nextParam(cx); 875 while (true) { 876 bool done; 877 if (!iterator.next(&nextParam, &done)) { 878 return false; 879 } 880 if (done) { 881 break; 882 } 883 884 ValType valType; 885 if (!ToValType(cx, nextParam, &valType) || !dest.append(valType)) { 886 return false; 887 } 888 } 889 return true; 890 } 891 892 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 893 template <typename T> 894 static JSString* TypeToString(JSContext* cx, T type) { 895 UniqueChars chars = ToString(type, nullptr); 896 if (!chars) { 897 return nullptr; 898 } 899 return NewStringCopyUTF8Z( 900 cx, JS::ConstUTF8CharsZ(chars.get(), strlen(chars.get()))); 901 } 902 903 static JSString* AddressTypeToString(JSContext* cx, AddressType type) { 904 return JS_NewStringCopyZ(cx, ToString(type)); 905 } 906 907 [[nodiscard]] static JSObject* ValTypesToArray(JSContext* cx, 908 const ValTypeVector& valTypes) { 909 Rooted<ArrayObject*> arrayObj(cx, NewDenseEmptyArray(cx)); 910 if (!arrayObj) { 911 return nullptr; 912 } 913 for (ValType valType : valTypes) { 914 RootedString type(cx, TypeToString(cx, valType)); 915 if (!type) { 916 return nullptr; 917 } 918 if (!NewbornArrayPush(cx, arrayObj, StringValue(type))) { 919 return nullptr; 920 } 921 } 922 return arrayObj; 923 } 924 925 static JSObject* FuncTypeToObject(JSContext* cx, const FuncType& type) { 926 Rooted<IdValueVector> props(cx, IdValueVector(cx)); 927 928 RootedObject parametersObj(cx, ValTypesToArray(cx, type.args())); 929 if (!parametersObj || 930 !props.append(IdValuePair(NameToId(cx->names().parameters), 931 ObjectValue(*parametersObj)))) { 932 ReportOutOfMemory(cx); 933 return nullptr; 934 } 935 936 RootedObject resultsObj(cx, ValTypesToArray(cx, type.results())); 937 if (!resultsObj || !props.append(IdValuePair(NameToId(cx->names().results), 938 ObjectValue(*resultsObj)))) { 939 ReportOutOfMemory(cx); 940 return nullptr; 941 } 942 943 return NewPlainObjectWithUniqueNames(cx, props); 944 } 945 946 static JSObject* TableTypeToObject(JSContext* cx, AddressType addressType, 947 RefType type, uint64_t initial, 948 Maybe<uint64_t> maximum) { 949 Rooted<IdValueVector> props(cx, IdValueVector(cx)); 950 951 RootedString elementType(cx, TypeToString(cx, type)); 952 if (!elementType || !props.append(IdValuePair(NameToId(cx->names().element), 953 StringValue(elementType)))) { 954 ReportOutOfMemory(cx); 955 return nullptr; 956 } 957 958 if (maximum.isSome()) { 959 RootedId maximumId(cx, NameToId(cx->names().maximum)); 960 RootedValue maximumValue(cx); 961 if (!CreateAddressValue(cx, maximum.value(), addressType, &maximumValue)) { 962 ReportOutOfMemory(cx); 963 return nullptr; 964 } 965 if (!props.append(IdValuePair(maximumId, maximumValue))) { 966 ReportOutOfMemory(cx); 967 return nullptr; 968 } 969 } 970 971 RootedId minimumId(cx, NameToId(cx->names().minimum)); 972 RootedValue minimumValue(cx); 973 if (!CreateAddressValue(cx, initial, addressType, &minimumValue)) { 974 ReportOutOfMemory(cx); 975 return nullptr; 976 } 977 if (!props.append(IdValuePair(minimumId, minimumValue))) { 978 ReportOutOfMemory(cx); 979 return nullptr; 980 } 981 982 RootedString at(cx, AddressTypeToString(cx, addressType)); 983 if (!at) { 984 return nullptr; 985 } 986 if (!props.append( 987 IdValuePair(NameToId(cx->names().address), StringValue(at)))) { 988 ReportOutOfMemory(cx); 989 return nullptr; 990 } 991 992 return NewPlainObjectWithUniqueNames(cx, props); 993 } 994 995 static JSObject* MemoryTypeToObject(JSContext* cx, bool shared, 996 wasm::AddressType addressType, 997 wasm::Pages minPages, 998 Maybe<wasm::Pages> maxPages) { 999 Rooted<IdValueVector> props(cx, IdValueVector(cx)); 1000 if (maxPages) { 1001 RootedId maximumId(cx, NameToId(cx->names().maximum)); 1002 RootedValue maximumValue(cx); 1003 if (!CreateAddressValue(cx, maxPages.value().pageCount(), addressType, 1004 &maximumValue)) { 1005 ReportOutOfMemory(cx); 1006 return nullptr; 1007 } 1008 if (!props.append(IdValuePair(maximumId, maximumValue))) { 1009 ReportOutOfMemory(cx); 1010 return nullptr; 1011 } 1012 } 1013 1014 RootedId minimumId(cx, NameToId(cx->names().minimum)); 1015 RootedValue minimumValue(cx); 1016 if (!CreateAddressValue(cx, minPages.pageCount(), addressType, 1017 &minimumValue)) { 1018 ReportOutOfMemory(cx); 1019 return nullptr; 1020 } 1021 if (!props.append(IdValuePair(minimumId, minimumValue))) { 1022 ReportOutOfMemory(cx); 1023 return nullptr; 1024 } 1025 1026 RootedString at(cx, AddressTypeToString(cx, addressType)); 1027 if (!at) { 1028 return nullptr; 1029 } 1030 if (!props.append( 1031 IdValuePair(NameToId(cx->names().address), StringValue(at)))) { 1032 ReportOutOfMemory(cx); 1033 return nullptr; 1034 } 1035 1036 if (!props.append( 1037 IdValuePair(NameToId(cx->names().shared), BooleanValue(shared)))) { 1038 ReportOutOfMemory(cx); 1039 return nullptr; 1040 } 1041 1042 return NewPlainObjectWithUniqueNames(cx, props); 1043 } 1044 1045 static JSObject* GlobalTypeToObject(JSContext* cx, ValType type, 1046 bool isMutable) { 1047 Rooted<IdValueVector> props(cx, IdValueVector(cx)); 1048 1049 if (!props.append(IdValuePair(NameToId(cx->names().mutable_), 1050 BooleanValue(isMutable)))) { 1051 ReportOutOfMemory(cx); 1052 return nullptr; 1053 } 1054 1055 RootedString valueType(cx, TypeToString(cx, type)); 1056 if (!valueType || !props.append(IdValuePair(NameToId(cx->names().value), 1057 StringValue(valueType)))) { 1058 ReportOutOfMemory(cx); 1059 return nullptr; 1060 } 1061 1062 return NewPlainObjectWithUniqueNames(cx, props); 1063 } 1064 1065 static JSObject* TagTypeToObject(JSContext* cx, 1066 const wasm::ValTypeVector& params) { 1067 Rooted<IdValueVector> props(cx, IdValueVector(cx)); 1068 1069 RootedObject parametersObj(cx, ValTypesToArray(cx, params)); 1070 if (!parametersObj || 1071 !props.append(IdValuePair(NameToId(cx->names().parameters), 1072 ObjectValue(*parametersObj)))) { 1073 ReportOutOfMemory(cx); 1074 return nullptr; 1075 } 1076 1077 return NewPlainObjectWithUniqueNames(cx, props); 1078 } 1079 #endif // ENABLE_WASM_TYPE_REFLECTIONS 1080 1081 // ============================================================================ 1082 // WebAssembly.Module class and methods 1083 1084 const JSClassOps WasmModuleObject::classOps_ = { 1085 nullptr, // addProperty 1086 nullptr, // delProperty 1087 nullptr, // enumerate 1088 nullptr, // newEnumerate 1089 nullptr, // resolve 1090 nullptr, // mayResolve 1091 WasmModuleObject::finalize, // finalize 1092 nullptr, // call 1093 nullptr, // construct 1094 nullptr, // trace 1095 }; 1096 1097 const JSClass WasmModuleObject::class_ = { 1098 "WebAssembly.Module", 1099 JSCLASS_DELAY_METADATA_BUILDER | 1100 JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) | 1101 JSCLASS_FOREGROUND_FINALIZE, 1102 &WasmModuleObject::classOps_, 1103 &WasmModuleObject::classSpec_, 1104 }; 1105 1106 const JSClass& WasmModuleObject::protoClass_ = PlainObject::class_; 1107 1108 static constexpr char WasmModuleName[] = "Module"; 1109 1110 const ClassSpec WasmModuleObject::classSpec_ = { 1111 CreateWasmConstructor<WasmModuleObject, WasmModuleName>, 1112 GenericCreatePrototype<WasmModuleObject>, 1113 WasmModuleObject::static_methods, 1114 nullptr, 1115 WasmModuleObject::methods, 1116 WasmModuleObject::properties, 1117 nullptr, 1118 ClassSpec::DontDefineConstructor, 1119 }; 1120 1121 const JSPropertySpec WasmModuleObject::properties[] = { 1122 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Module", JSPROP_READONLY), 1123 JS_PS_END, 1124 }; 1125 1126 const JSFunctionSpec WasmModuleObject::methods[] = { 1127 JS_FS_END, 1128 }; 1129 1130 const JSFunctionSpec WasmModuleObject::static_methods[] = { 1131 JS_FN("imports", WasmModuleObject::imports, 1, JSPROP_ENUMERATE), 1132 JS_FN("exports", WasmModuleObject::exports, 1, JSPROP_ENUMERATE), 1133 JS_FN("customSections", WasmModuleObject::customSections, 2, 1134 JSPROP_ENUMERATE), 1135 JS_FS_END, 1136 }; 1137 1138 /* static */ 1139 void WasmModuleObject::finalize(JS::GCContext* gcx, JSObject* obj) { 1140 const Module& module = obj->as<WasmModuleObject>().module(); 1141 size_t codeMemory = module.tier1CodeMemoryUsed(); 1142 if (codeMemory) { 1143 obj->zone()->decJitMemory(codeMemory); 1144 } 1145 gcx->release(obj, &module, module.gcMallocBytesExcludingCode(), 1146 MemoryUse::WasmModule); 1147 } 1148 1149 static bool IsModuleObject(JSObject* obj, const Module** module) { 1150 WasmModuleObject* mobj = obj->maybeUnwrapIf<WasmModuleObject>(); 1151 if (!mobj) { 1152 return false; 1153 } 1154 1155 *module = &mobj->module(); 1156 return true; 1157 } 1158 1159 static bool GetModuleArg(JSContext* cx, const CallArgs& args, 1160 uint32_t numRequired, const char* name, 1161 const Module** module) { 1162 if (!args.requireAtLeast(cx, name, numRequired)) { 1163 return false; 1164 } 1165 1166 if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) { 1167 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 1168 JSMSG_WASM_BAD_MOD_ARG); 1169 return false; 1170 } 1171 1172 return true; 1173 } 1174 1175 struct KindNames { 1176 Rooted<PropertyName*> kind; 1177 Rooted<PropertyName*> table; 1178 Rooted<PropertyName*> memory; 1179 Rooted<PropertyName*> tag; 1180 Rooted<PropertyName*> type; 1181 1182 explicit KindNames(JSContext* cx) 1183 : kind(cx), table(cx), memory(cx), tag(cx), type(cx) {} 1184 }; 1185 1186 static bool InitKindNames(JSContext* cx, KindNames* names) { 1187 JSAtom* kind = Atomize(cx, "kind", strlen("kind")); 1188 if (!kind) { 1189 return false; 1190 } 1191 names->kind = kind->asPropertyName(); 1192 1193 JSAtom* table = Atomize(cx, "table", strlen("table")); 1194 if (!table) { 1195 return false; 1196 } 1197 names->table = table->asPropertyName(); 1198 1199 JSAtom* memory = Atomize(cx, "memory", strlen("memory")); 1200 if (!memory) { 1201 return false; 1202 } 1203 names->memory = memory->asPropertyName(); 1204 1205 JSAtom* tag = Atomize(cx, "tag", strlen("tag")); 1206 if (!tag) { 1207 return false; 1208 } 1209 names->tag = tag->asPropertyName(); 1210 1211 JSAtom* type = Atomize(cx, "type", strlen("type")); 1212 if (!type) { 1213 return false; 1214 } 1215 names->type = type->asPropertyName(); 1216 1217 return true; 1218 } 1219 1220 static JSString* KindToString(JSContext* cx, const KindNames& names, 1221 DefinitionKind kind) { 1222 switch (kind) { 1223 case DefinitionKind::Function: 1224 return cx->names().function; 1225 case DefinitionKind::Table: 1226 return names.table; 1227 case DefinitionKind::Memory: 1228 return names.memory; 1229 case DefinitionKind::Global: 1230 return cx->names().global; 1231 case DefinitionKind::Tag: 1232 return names.tag; 1233 } 1234 1235 MOZ_CRASH("invalid kind"); 1236 } 1237 1238 /* static */ 1239 bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) { 1240 CallArgs args = CallArgsFromVp(argc, vp); 1241 1242 const Module* module; 1243 if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.imports", &module)) { 1244 return false; 1245 } 1246 1247 KindNames names(cx); 1248 if (!InitKindNames(cx, &names)) { 1249 return false; 1250 } 1251 1252 const ModuleMetadata& moduleMeta = module->moduleMeta(); 1253 1254 RootedValueVector elems(cx); 1255 if (!elems.reserve(moduleMeta.imports.length())) { 1256 return false; 1257 } 1258 1259 const CodeMetadata& codeMeta = module->codeMeta(); 1260 #if defined(ENABLE_WASM_TYPE_REFLECTIONS) 1261 size_t numFuncImport = 0; 1262 size_t numMemoryImport = 0; 1263 size_t numGlobalImport = 0; 1264 size_t numTableImport = 0; 1265 size_t numTagImport = 0; 1266 #endif // ENABLE_WASM_TYPE_REFLECTIONS 1267 1268 for (const Import& import : moduleMeta.imports) { 1269 Maybe<BuiltinModuleId> builtinModule = ImportMatchesBuiltinModule( 1270 import.module.utf8Bytes(), codeMeta.features().builtinModules); 1271 if (builtinModule) { 1272 continue; 1273 } 1274 1275 Rooted<IdValueVector> props(cx, IdValueVector(cx)); 1276 if (!props.reserve(3)) { 1277 return false; 1278 } 1279 1280 JSString* moduleStr = import.module.toAtom(cx); 1281 if (!moduleStr) { 1282 return false; 1283 } 1284 props.infallibleAppend( 1285 IdValuePair(NameToId(cx->names().module), StringValue(moduleStr))); 1286 1287 JSString* nameStr = import.field.toAtom(cx); 1288 if (!nameStr) { 1289 return false; 1290 } 1291 props.infallibleAppend( 1292 IdValuePair(NameToId(cx->names().name), StringValue(nameStr))); 1293 1294 JSString* kindStr = KindToString(cx, names, import.kind); 1295 if (!kindStr) { 1296 return false; 1297 } 1298 props.infallibleAppend( 1299 IdValuePair(NameToId(names.kind), StringValue(kindStr))); 1300 1301 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 1302 RootedObject typeObj(cx); 1303 switch (import.kind) { 1304 case DefinitionKind::Function: { 1305 size_t funcIndex = numFuncImport++; 1306 const FuncType& funcType = codeMeta.getFuncType(funcIndex); 1307 typeObj = FuncTypeToObject(cx, funcType); 1308 break; 1309 } 1310 case DefinitionKind::Table: { 1311 size_t tableIndex = numTableImport++; 1312 const TableDesc& table = codeMeta.tables[tableIndex]; 1313 typeObj = 1314 TableTypeToObject(cx, table.addressType(), table.elemType, 1315 table.initialLength(), table.maximumLength()); 1316 break; 1317 } 1318 case DefinitionKind::Memory: { 1319 size_t memoryIndex = numMemoryImport++; 1320 const MemoryDesc& memory = codeMeta.memories[memoryIndex]; 1321 typeObj = 1322 MemoryTypeToObject(cx, memory.isShared(), memory.addressType(), 1323 memory.initialPages(), memory.maximumPages()); 1324 break; 1325 } 1326 case DefinitionKind::Global: { 1327 size_t globalIndex = numGlobalImport++; 1328 const GlobalDesc& global = codeMeta.globals[globalIndex]; 1329 typeObj = GlobalTypeToObject(cx, global.type(), global.isMutable()); 1330 break; 1331 } 1332 case DefinitionKind::Tag: { 1333 size_t tagIndex = numTagImport++; 1334 const TagDesc& tag = codeMeta.tags[tagIndex]; 1335 typeObj = TagTypeToObject(cx, tag.type->argTypes()); 1336 break; 1337 } 1338 } 1339 1340 if (!typeObj || !props.append(IdValuePair(NameToId(names.type), 1341 ObjectValue(*typeObj)))) { 1342 ReportOutOfMemory(cx); 1343 return false; 1344 } 1345 #endif // ENABLE_WASM_TYPE_REFLECTIONS 1346 1347 JSObject* obj = NewPlainObjectWithUniqueNames(cx, props); 1348 if (!obj) { 1349 return false; 1350 } 1351 1352 elems.infallibleAppend(ObjectValue(*obj)); 1353 } 1354 1355 JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin()); 1356 if (!arr) { 1357 return false; 1358 } 1359 1360 args.rval().setObject(*arr); 1361 return true; 1362 } 1363 1364 /* static */ 1365 bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) { 1366 CallArgs args = CallArgsFromVp(argc, vp); 1367 1368 const Module* module; 1369 if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.exports", &module)) { 1370 return false; 1371 } 1372 1373 KindNames names(cx); 1374 if (!InitKindNames(cx, &names)) { 1375 return false; 1376 } 1377 1378 const ModuleMetadata& moduleMeta = module->moduleMeta(); 1379 1380 RootedValueVector elems(cx); 1381 if (!elems.reserve(moduleMeta.exports.length())) { 1382 return false; 1383 } 1384 1385 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 1386 const CodeMetadata& codeMeta = module->codeMeta(); 1387 #endif // ENABLE_WASM_TYPE_REFLECTIONS 1388 1389 for (const Export& exp : moduleMeta.exports) { 1390 Rooted<IdValueVector> props(cx, IdValueVector(cx)); 1391 if (!props.reserve(2)) { 1392 return false; 1393 } 1394 1395 JSString* nameStr = exp.fieldName().toAtom(cx); 1396 if (!nameStr) { 1397 return false; 1398 } 1399 props.infallibleAppend( 1400 IdValuePair(NameToId(cx->names().name), StringValue(nameStr))); 1401 1402 JSString* kindStr = KindToString(cx, names, exp.kind()); 1403 if (!kindStr) { 1404 return false; 1405 } 1406 props.infallibleAppend( 1407 IdValuePair(NameToId(names.kind), StringValue(kindStr))); 1408 1409 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 1410 RootedObject typeObj(cx); 1411 switch (exp.kind()) { 1412 case DefinitionKind::Function: { 1413 const FuncType& funcType = codeMeta.getFuncType(exp.funcIndex()); 1414 typeObj = FuncTypeToObject(cx, funcType); 1415 break; 1416 } 1417 case DefinitionKind::Table: { 1418 const TableDesc& table = codeMeta.tables[exp.tableIndex()]; 1419 typeObj = 1420 TableTypeToObject(cx, table.addressType(), table.elemType, 1421 table.initialLength(), table.maximumLength()); 1422 break; 1423 } 1424 case DefinitionKind::Memory: { 1425 const MemoryDesc& memory = codeMeta.memories[exp.memoryIndex()]; 1426 typeObj = 1427 MemoryTypeToObject(cx, memory.isShared(), memory.addressType(), 1428 memory.initialPages(), memory.maximumPages()); 1429 break; 1430 } 1431 case DefinitionKind::Global: { 1432 const GlobalDesc& global = codeMeta.globals[exp.globalIndex()]; 1433 typeObj = GlobalTypeToObject(cx, global.type(), global.isMutable()); 1434 break; 1435 } 1436 case DefinitionKind::Tag: { 1437 const TagDesc& tag = codeMeta.tags[exp.tagIndex()]; 1438 typeObj = TagTypeToObject(cx, tag.type->argTypes()); 1439 break; 1440 } 1441 } 1442 1443 if (!typeObj || !props.append(IdValuePair(NameToId(names.type), 1444 ObjectValue(*typeObj)))) { 1445 ReportOutOfMemory(cx); 1446 return false; 1447 } 1448 #endif // ENABLE_WASM_TYPE_REFLECTIONS 1449 1450 JSObject* obj = NewPlainObjectWithUniqueNames(cx, props); 1451 if (!obj) { 1452 return false; 1453 } 1454 1455 elems.infallibleAppend(ObjectValue(*obj)); 1456 } 1457 1458 JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin()); 1459 if (!arr) { 1460 return false; 1461 } 1462 1463 args.rval().setObject(*arr); 1464 return true; 1465 } 1466 1467 /* static */ 1468 bool WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp) { 1469 CallArgs args = CallArgsFromVp(argc, vp); 1470 1471 const Module* module; 1472 if (!GetModuleArg(cx, args, 2, "WebAssembly.Module.customSections", 1473 &module)) { 1474 return false; 1475 } 1476 1477 Vector<char, 8> name(cx); 1478 { 1479 RootedString str(cx, ToString(cx, args.get(1))); 1480 if (!str) { 1481 return false; 1482 } 1483 1484 Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx)); 1485 if (!linear) { 1486 return false; 1487 } 1488 1489 if (!name.initLengthUninitialized( 1490 JS::GetDeflatedUTF8StringLength(linear))) { 1491 return false; 1492 } 1493 1494 (void)JS::DeflateStringToUTF8Buffer(linear, 1495 Span(name.begin(), name.length())); 1496 } 1497 1498 RootedValueVector elems(cx); 1499 Rooted<ArrayBufferObject*> buf(cx); 1500 for (const CustomSection& cs : module->moduleMeta().customSections) { 1501 if (name.length() != cs.name.length()) { 1502 continue; 1503 } 1504 if (memcmp(name.begin(), cs.name.begin(), name.length()) != 0) { 1505 continue; 1506 } 1507 1508 buf = ArrayBufferObject::createZeroed(cx, cs.payload->length()); 1509 if (!buf) { 1510 return false; 1511 } 1512 1513 memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length()); 1514 if (!elems.append(ObjectValue(*buf))) { 1515 return false; 1516 } 1517 } 1518 1519 JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin()); 1520 if (!arr) { 1521 return false; 1522 } 1523 1524 args.rval().setObject(*arr); 1525 return true; 1526 } 1527 1528 /* static */ 1529 WasmModuleObject* WasmModuleObject::create(JSContext* cx, const Module& module, 1530 HandleObject proto) { 1531 AutoSetNewObjectMetadata metadata(cx); 1532 auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto); 1533 if (!obj) { 1534 return nullptr; 1535 } 1536 1537 // The pipeline state on some architectures may retain stale instructions 1538 // even after we invalidate the instruction cache. There is no generally 1539 // available method to broadcast this pipeline flush to all threads after 1540 // we've compiled new code, so conservatively perform one here when we're 1541 // receiving a module that may have been compiled from another thread. 1542 // 1543 // The cost of this flush is expected to minimal enough to not be worth 1544 // optimizing away in the case the module was compiled on this thread. 1545 jit::FlushExecutionContext(); 1546 1547 // This accounts for module allocation size (excluding code which is handled 1548 // separately - see below). This assumes that the size of associated data 1549 // doesn't change for the life of the WasmModuleObject. The size is counted 1550 // once per WasmModuleObject referencing a Module. 1551 InitReservedSlot(obj, MODULE_SLOT, const_cast<Module*>(&module), 1552 module.gcMallocBytesExcludingCode(), MemoryUse::WasmModule); 1553 module.AddRef(); 1554 1555 // Bug 1569888: We account for the first tier here; the second tier, if 1556 // different, also needs to be accounted for. 1557 size_t codeMemory = module.tier1CodeMemoryUsed(); 1558 if (codeMemory) { 1559 cx->zone()->incJitMemory(codeMemory); 1560 } 1561 return obj; 1562 } 1563 1564 struct MOZ_STACK_CLASS AutoPinBufferSourceLength { 1565 explicit AutoPinBufferSourceLength(JSContext* cx, JSObject* bufferSource) 1566 : bufferSource_(cx, bufferSource), 1567 wasPinned_(!JS::PinArrayBufferOrViewLength(bufferSource_, true)) {} 1568 ~AutoPinBufferSourceLength() { 1569 if (!wasPinned_) { 1570 JS::PinArrayBufferOrViewLength(bufferSource_, false); 1571 } 1572 } 1573 1574 private: 1575 Rooted<JSObject*> bufferSource_; 1576 bool wasPinned_; 1577 }; 1578 1579 static bool GetBytecodeSource(JSContext* cx, Handle<JSObject*> obj, 1580 unsigned errorNumber, BytecodeSource* bytecode) { 1581 JSObject* unwrapped = CheckedUnwrapStatic(obj); 1582 1583 SharedMem<uint8_t*> dataPointer; 1584 size_t byteLength; 1585 if (!unwrapped || 1586 !IsBufferSource(cx, unwrapped, /*allowShared*/ false, 1587 /*allowResizable*/ false, &dataPointer, &byteLength)) { 1588 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber); 1589 return false; 1590 } 1591 1592 *bytecode = BytecodeSource(dataPointer.unwrap(), byteLength); 1593 return true; 1594 } 1595 1596 static bool GetBytecodeBuffer(JSContext* cx, Handle<JSObject*> obj, 1597 unsigned errorNumber, BytecodeBuffer* bytecode) { 1598 BytecodeSource source; 1599 if (!GetBytecodeSource(cx, obj, errorNumber, &source)) { 1600 return false; 1601 } 1602 AutoPinBufferSourceLength pin(cx, obj); 1603 if (!BytecodeBuffer::fromSource(source, bytecode)) { 1604 ReportOutOfMemory(cx); 1605 return false; 1606 } 1607 return true; 1608 } 1609 1610 static bool ReportCompileWarnings(JSContext* cx, 1611 const UniqueCharsVector& warnings) { 1612 // Avoid spamming the console. 1613 size_t numWarnings = std::min<size_t>(warnings.length(), 3); 1614 1615 for (size_t i = 0; i < numWarnings; i++) { 1616 if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING, warnings[i].get())) { 1617 return false; 1618 } 1619 } 1620 1621 if (warnings.length() > numWarnings) { 1622 if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING, 1623 "other warnings suppressed")) { 1624 return false; 1625 } 1626 } 1627 1628 return true; 1629 } 1630 1631 /* static */ 1632 bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) { 1633 CallArgs callArgs = CallArgsFromVp(argc, vp); 1634 1635 Log(cx, "sync new Module() started"); 1636 1637 if (!ThrowIfNotConstructing(cx, callArgs, "Module")) { 1638 return false; 1639 } 1640 1641 JS::RootedVector<JSString*> parameterStrings(cx); 1642 JS::RootedVector<Value> parameterArgs(cx); 1643 bool canCompileStrings = false; 1644 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, 1645 JS::CompilationType::Undefined, 1646 parameterStrings, nullptr, parameterArgs, 1647 NullHandleValue, &canCompileStrings)) { 1648 return false; 1649 } 1650 if (!canCompileStrings) { 1651 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 1652 JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module"); 1653 return false; 1654 } 1655 1656 if (!callArgs.requireAtLeast(cx, "WebAssembly.Module", 1)) { 1657 return false; 1658 } 1659 1660 if (!callArgs[0].isObject()) { 1661 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 1662 JSMSG_WASM_BAD_BUF_ARG); 1663 return false; 1664 } 1665 1666 FeatureOptions options; 1667 if (!options.init(cx, callArgs.get(1))) { 1668 return false; 1669 } 1670 1671 SharedCompileArgs compileArgs = 1672 InitCompileArgs(cx, options, "WebAssembly.Module"); 1673 if (!compileArgs) { 1674 return false; 1675 } 1676 1677 BytecodeSource source; 1678 Rooted<JSObject*> sourceObj(cx, &callArgs[0].toObject()); 1679 if (!GetBytecodeSource(cx, sourceObj, JSMSG_WASM_BAD_BUF_ARG, &source)) { 1680 return false; 1681 } 1682 1683 UniqueChars error; 1684 UniqueCharsVector warnings; 1685 SharedModule module; 1686 { 1687 AutoPinBufferSourceLength pin(cx, sourceObj.get()); 1688 module = CompileBuffer(*compileArgs, BytecodeBufferOrSource(source), &error, 1689 &warnings, nullptr); 1690 } 1691 1692 if (!ReportCompileWarnings(cx, warnings)) { 1693 return false; 1694 } 1695 if (!module) { 1696 if (error) { 1697 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 1698 JSMSG_WASM_COMPILE_ERROR, error.get()); 1699 return false; 1700 } 1701 return ThrowCompileOutOfMemory(cx); 1702 } 1703 1704 RootedObject proto( 1705 cx, GetWasmConstructorPrototype(cx, callArgs, JSProto_WasmModule)); 1706 if (!proto) { 1707 ReportOutOfMemory(cx); 1708 return false; 1709 } 1710 1711 RootedObject moduleObj(cx, WasmModuleObject::create(cx, *module, proto)); 1712 if (!moduleObj) { 1713 return false; 1714 } 1715 1716 Log(cx, "sync new Module() succeded"); 1717 1718 callArgs.rval().setObject(*moduleObj); 1719 return true; 1720 } 1721 1722 const Module& WasmModuleObject::module() const { 1723 MOZ_ASSERT(is<WasmModuleObject>()); 1724 return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate(); 1725 } 1726 1727 // ============================================================================ 1728 // WebAssembly.Instance class and methods 1729 1730 const JSClassOps WasmInstanceObject::classOps_ = { 1731 nullptr, // addProperty 1732 nullptr, // delProperty 1733 nullptr, // enumerate 1734 nullptr, // newEnumerate 1735 nullptr, // resolve 1736 nullptr, // mayResolve 1737 WasmInstanceObject::finalize, // finalize 1738 nullptr, // call 1739 nullptr, // construct 1740 WasmInstanceObject::trace, // trace 1741 }; 1742 1743 const JSClass WasmInstanceObject::class_ = { 1744 "WebAssembly.Instance", 1745 JSCLASS_DELAY_METADATA_BUILDER | 1746 JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) | 1747 JSCLASS_FOREGROUND_FINALIZE, 1748 &WasmInstanceObject::classOps_, 1749 &WasmInstanceObject::classSpec_, 1750 }; 1751 1752 const JSClass& WasmInstanceObject::protoClass_ = PlainObject::class_; 1753 1754 static constexpr char WasmInstanceName[] = "Instance"; 1755 1756 const ClassSpec WasmInstanceObject::classSpec_ = { 1757 CreateWasmConstructor<WasmInstanceObject, WasmInstanceName>, 1758 GenericCreatePrototype<WasmInstanceObject>, 1759 WasmInstanceObject::static_methods, 1760 nullptr, 1761 WasmInstanceObject::methods, 1762 WasmInstanceObject::properties, 1763 nullptr, 1764 ClassSpec::DontDefineConstructor, 1765 }; 1766 1767 static bool IsInstance(HandleValue v) { 1768 return v.isObject() && v.toObject().is<WasmInstanceObject>(); 1769 } 1770 1771 /* static */ 1772 bool WasmInstanceObject::exportsGetterImpl(JSContext* cx, 1773 const CallArgs& args) { 1774 args.rval().setObject( 1775 args.thisv().toObject().as<WasmInstanceObject>().exportsObj()); 1776 return true; 1777 } 1778 1779 /* static */ 1780 bool WasmInstanceObject::exportsGetter(JSContext* cx, unsigned argc, 1781 Value* vp) { 1782 CallArgs args = CallArgsFromVp(argc, vp); 1783 return CallNonGenericMethod<IsInstance, exportsGetterImpl>(cx, args); 1784 } 1785 1786 const JSPropertySpec WasmInstanceObject::properties[] = { 1787 JS_PSG("exports", WasmInstanceObject::exportsGetter, JSPROP_ENUMERATE), 1788 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Instance", JSPROP_READONLY), 1789 JS_PS_END, 1790 }; 1791 1792 const JSFunctionSpec WasmInstanceObject::methods[] = { 1793 JS_FS_END, 1794 }; 1795 1796 const JSFunctionSpec WasmInstanceObject::static_methods[] = { 1797 JS_FS_END, 1798 }; 1799 1800 bool WasmInstanceObject::isNewborn() const { 1801 MOZ_ASSERT(is<WasmInstanceObject>()); 1802 return getReservedSlot(INSTANCE_SLOT).isUndefined(); 1803 } 1804 1805 // WeakScopeMap maps from function index to js::Scope. This maps is weak 1806 // to avoid holding scope objects alive. The scopes are normally created 1807 // during debugging. 1808 // 1809 // This is defined here in order to avoid recursive dependency between 1810 // WasmJS.h and Scope.h. 1811 using WasmFunctionScopeMap = 1812 JS::WeakCache<GCHashMap<uint32_t, WeakHeapPtr<WasmFunctionScope*>, 1813 DefaultHasher<uint32_t>, CellAllocPolicy>>; 1814 class WasmInstanceObject::UnspecifiedScopeMap { 1815 public: 1816 WasmFunctionScopeMap& asWasmFunctionScopeMap() { 1817 return *(WasmFunctionScopeMap*)this; 1818 } 1819 }; 1820 1821 /* static */ 1822 void WasmInstanceObject::finalize(JS::GCContext* gcx, JSObject* obj) { 1823 WasmInstanceObject& instance = obj->as<WasmInstanceObject>(); 1824 gcx->delete_(obj, &instance.scopes().asWasmFunctionScopeMap(), 1825 MemoryUse::WasmInstanceScopes); 1826 gcx->delete_(obj, &instance.indirectGlobals(), 1827 MemoryUse::WasmInstanceGlobals); 1828 if (!instance.isNewborn()) { 1829 if (instance.instance().debugEnabled()) { 1830 instance.instance().debug().finalize(gcx); 1831 } 1832 Instance::destroy(&instance.instance()); 1833 gcx->removeCellMemory(obj, sizeof(Instance), 1834 MemoryUse::WasmInstanceInstance); 1835 } 1836 } 1837 1838 /* static */ 1839 void WasmInstanceObject::trace(JSTracer* trc, JSObject* obj) { 1840 WasmInstanceObject& instanceObj = obj->as<WasmInstanceObject>(); 1841 instanceObj.indirectGlobals().trace(trc); 1842 if (!instanceObj.isNewborn()) { 1843 instanceObj.instance().tracePrivate(trc); 1844 } 1845 } 1846 1847 /* static */ 1848 WasmInstanceObject* WasmInstanceObject::create( 1849 JSContext* cx, const SharedCode& code, 1850 const DataSegmentVector& dataSegments, 1851 const ModuleElemSegmentVector& elemSegments, uint32_t instanceDataLength, 1852 Handle<WasmMemoryObjectVector> memories, SharedTableVector&& tables, 1853 const JSObjectVector& funcImports, const GlobalDescVector& globals, 1854 const ValVector& globalImportValues, 1855 const WasmGlobalObjectVector& globalObjs, 1856 const WasmTagObjectVector& tagObjs, HandleObject proto, 1857 UniqueDebugState maybeDebug) { 1858 UniquePtr<WasmFunctionScopeMap> scopes = 1859 js::MakeUnique<WasmFunctionScopeMap>(cx->zone(), cx->zone()); 1860 if (!scopes) { 1861 ReportOutOfMemory(cx); 1862 return nullptr; 1863 } 1864 // Note that `scopes` is a WeakCache, auto-linked into a sweep list on the 1865 // Zone, and so does not require rooting. 1866 1867 uint32_t indirectGlobals = 0; 1868 1869 for (uint32_t i = 0; i < globalObjs.length(); i++) { 1870 if (globalObjs[i] && globals[i].isIndirect()) { 1871 indirectGlobals++; 1872 } 1873 } 1874 1875 Rooted<UniquePtr<GlobalObjectVector>> indirectGlobalObjs( 1876 cx, js::MakeUnique<GlobalObjectVector>(cx->zone())); 1877 if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals)) { 1878 ReportOutOfMemory(cx); 1879 return nullptr; 1880 } 1881 1882 { 1883 uint32_t next = 0; 1884 for (uint32_t i = 0; i < globalObjs.length(); i++) { 1885 if (globalObjs[i] && globals[i].isIndirect()) { 1886 (*indirectGlobalObjs)[next++] = globalObjs[i]; 1887 } 1888 } 1889 } 1890 1891 Instance* instance = nullptr; 1892 Rooted<WasmInstanceObject*> obj(cx); 1893 1894 { 1895 // We must delay creating metadata for this object until after all its 1896 // slots have been initialized. We must also create the metadata before 1897 // calling Instance::init as that may allocate new objects. 1898 AutoSetNewObjectMetadata metadata(cx); 1899 obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, proto); 1900 if (!obj) { 1901 return nullptr; 1902 } 1903 1904 MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers"); 1905 1906 InitReservedSlot(obj, SCOPES_SLOT, scopes.release(), 1907 MemoryUse::WasmInstanceScopes); 1908 1909 InitReservedSlot(obj, GLOBALS_SLOT, indirectGlobalObjs.release(), 1910 MemoryUse::WasmInstanceGlobals); 1911 1912 obj->initReservedSlot(INSTANCE_SCOPE_SLOT, UndefinedValue()); 1913 1914 // The INSTANCE_SLOT may not be initialized if Instance allocation fails, 1915 // leading to an observable "newborn" state in tracing/finalization. 1916 MOZ_ASSERT(obj->isNewborn()); 1917 1918 // Create this just before constructing Instance to avoid rooting hazards. 1919 instance = Instance::create(cx, obj, code, instanceDataLength, 1920 std::move(tables), std::move(maybeDebug)); 1921 if (!instance) { 1922 return nullptr; 1923 } 1924 1925 InitReservedSlot(obj, INSTANCE_SLOT, instance, 1926 MemoryUse::WasmInstanceInstance); 1927 MOZ_ASSERT(!obj->isNewborn()); 1928 } 1929 1930 if (!instance->init(cx, funcImports, globalImportValues, memories, globalObjs, 1931 tagObjs, dataSegments, elemSegments)) { 1932 return nullptr; 1933 } 1934 1935 return obj; 1936 } 1937 1938 void WasmInstanceObject::initExportsObj(JSObject& exportsObj) { 1939 MOZ_ASSERT(getReservedSlot(EXPORTS_OBJ_SLOT).isUndefined()); 1940 setReservedSlot(EXPORTS_OBJ_SLOT, ObjectValue(exportsObj)); 1941 } 1942 1943 static bool GetImportArg(JSContext* cx, HandleValue importArg, 1944 MutableHandleObject importObj) { 1945 if (!importArg.isUndefined()) { 1946 if (!importArg.isObject()) { 1947 return ThrowBadImportArg(cx); 1948 } 1949 importObj.set(&importArg.toObject()); 1950 } 1951 return true; 1952 } 1953 1954 /* static */ 1955 bool WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp) { 1956 CallArgs args = CallArgsFromVp(argc, vp); 1957 1958 Log(cx, "sync new Instance() started"); 1959 1960 if (!ThrowIfNotConstructing(cx, args, "Instance")) { 1961 return false; 1962 } 1963 1964 if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1)) { 1965 return false; 1966 } 1967 1968 const Module* module; 1969 if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) { 1970 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 1971 JSMSG_WASM_BAD_MOD_ARG); 1972 return false; 1973 } 1974 1975 RootedObject importObj(cx); 1976 if (!GetImportArg(cx, args.get(1), &importObj)) { 1977 return false; 1978 } 1979 1980 RootedObject proto( 1981 cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmInstance)); 1982 if (!proto) { 1983 ReportOutOfMemory(cx); 1984 return false; 1985 } 1986 1987 Rooted<ImportValues> imports(cx); 1988 if (!GetImports(cx, *module, importObj, imports.address())) { 1989 return false; 1990 } 1991 1992 Rooted<WasmInstanceObject*> instanceObj(cx); 1993 if (!module->instantiate(cx, imports.get(), proto, &instanceObj)) { 1994 return false; 1995 } 1996 1997 Log(cx, "sync new Instance() succeeded"); 1998 1999 args.rval().setObject(*instanceObj); 2000 return true; 2001 } 2002 2003 Instance& WasmInstanceObject::instance() const { 2004 MOZ_ASSERT(!isNewborn()); 2005 return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate(); 2006 } 2007 2008 JSObject& WasmInstanceObject::exportsObj() const { 2009 return getReservedSlot(EXPORTS_OBJ_SLOT).toObject(); 2010 } 2011 2012 WasmInstanceObject::UnspecifiedScopeMap& WasmInstanceObject::scopes() const { 2013 return *(UnspecifiedScopeMap*)(getReservedSlot(SCOPES_SLOT).toPrivate()); 2014 } 2015 2016 WasmInstanceObject::GlobalObjectVector& WasmInstanceObject::indirectGlobals() 2017 const { 2018 return *(GlobalObjectVector*)getReservedSlot(GLOBALS_SLOT).toPrivate(); 2019 } 2020 2021 /* static */ 2022 bool WasmInstanceObject::getExportedFunction( 2023 JSContext* cx, Handle<WasmInstanceObject*> instanceObj, uint32_t funcIndex, 2024 MutableHandleFunction fun) { 2025 Instance& instance = instanceObj->instance(); 2026 return instance.getExportedFunction(cx, funcIndex, fun); 2027 } 2028 2029 /* static */ 2030 WasmInstanceScope* WasmInstanceObject::getScope( 2031 JSContext* cx, Handle<WasmInstanceObject*> instanceObj) { 2032 if (!instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT).isUndefined()) { 2033 return (WasmInstanceScope*)instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT) 2034 .toGCThing(); 2035 } 2036 2037 Rooted<WasmInstanceScope*> instanceScope( 2038 cx, WasmInstanceScope::create(cx, instanceObj)); 2039 if (!instanceScope) { 2040 return nullptr; 2041 } 2042 2043 instanceObj->setReservedSlot(INSTANCE_SCOPE_SLOT, 2044 PrivateGCThingValue(instanceScope)); 2045 2046 return instanceScope; 2047 } 2048 2049 /* static */ 2050 WasmFunctionScope* WasmInstanceObject::getFunctionScope( 2051 JSContext* cx, Handle<WasmInstanceObject*> instanceObj, 2052 uint32_t funcIndex) { 2053 if (auto p = 2054 instanceObj->scopes().asWasmFunctionScopeMap().lookup(funcIndex)) { 2055 return p->value(); 2056 } 2057 2058 Rooted<WasmInstanceScope*> instanceScope( 2059 cx, WasmInstanceObject::getScope(cx, instanceObj)); 2060 if (!instanceScope) { 2061 return nullptr; 2062 } 2063 2064 Rooted<WasmFunctionScope*> funcScope( 2065 cx, WasmFunctionScope::create(cx, instanceScope, funcIndex)); 2066 if (!funcScope) { 2067 return nullptr; 2068 } 2069 2070 if (!instanceObj->scopes().asWasmFunctionScopeMap().putNew(funcIndex, 2071 funcScope)) { 2072 ReportOutOfMemory(cx); 2073 return nullptr; 2074 } 2075 2076 return funcScope; 2077 } 2078 2079 // ============================================================================ 2080 // WebAssembly.Memory class and methods 2081 2082 const JSClassOps WasmMemoryObject::classOps_ = { 2083 nullptr, // addProperty 2084 nullptr, // delProperty 2085 nullptr, // enumerate 2086 nullptr, // newEnumerate 2087 nullptr, // resolve 2088 nullptr, // mayResolve 2089 WasmMemoryObject::finalize, // finalize 2090 nullptr, // call 2091 nullptr, // construct 2092 nullptr, // trace 2093 }; 2094 2095 const JSClass WasmMemoryObject::class_ = { 2096 "WebAssembly.Memory", 2097 JSCLASS_DELAY_METADATA_BUILDER | 2098 JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) | 2099 JSCLASS_FOREGROUND_FINALIZE, 2100 &WasmMemoryObject::classOps_, 2101 &WasmMemoryObject::classSpec_, 2102 }; 2103 2104 const JSClass& WasmMemoryObject::protoClass_ = PlainObject::class_; 2105 2106 static constexpr char WasmMemoryName[] = "Memory"; 2107 2108 static JSObject* CreateWasmMemoryPrototype(JSContext* cx, JSProtoKey key) { 2109 RootedObject proto(cx, GlobalObject::createBlankPrototype( 2110 cx, cx->global(), &WasmMemoryObject::protoClass_)); 2111 if (!proto) { 2112 return nullptr; 2113 } 2114 if (MemoryControlAvailable(cx)) { 2115 if (!JS_DefineFunctions(cx, proto, 2116 WasmMemoryObject::memoryControlMethods)) { 2117 return nullptr; 2118 } 2119 } 2120 return proto; 2121 } 2122 2123 const ClassSpec WasmMemoryObject::classSpec_ = { 2124 CreateWasmConstructor<WasmMemoryObject, WasmMemoryName>, 2125 CreateWasmMemoryPrototype, 2126 WasmMemoryObject::static_methods, 2127 nullptr, 2128 WasmMemoryObject::methods, 2129 WasmMemoryObject::properties, 2130 nullptr, 2131 ClassSpec::DontDefineConstructor, 2132 }; 2133 2134 /* static */ 2135 void WasmMemoryObject::finalize(JS::GCContext* gcx, JSObject* obj) { 2136 WasmMemoryObject& memory = obj->as<WasmMemoryObject>(); 2137 if (memory.hasObservers()) { 2138 gcx->delete_(obj, &memory.observers(), MemoryUse::WasmMemoryObservers); 2139 } 2140 } 2141 2142 /* static */ 2143 WasmMemoryObject* WasmMemoryObject::create( 2144 JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, bool isHuge, 2145 HandleObject proto) { 2146 AutoSetNewObjectMetadata metadata(cx); 2147 auto* obj = NewObjectWithGivenProto<WasmMemoryObject>(cx, proto); 2148 if (!obj) { 2149 return nullptr; 2150 } 2151 2152 obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer)); 2153 obj->initReservedSlot(ISHUGE_SLOT, BooleanValue(isHuge)); 2154 MOZ_ASSERT(!obj->hasObservers()); 2155 2156 return obj; 2157 } 2158 2159 /* static */ 2160 bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) { 2161 CallArgs args = CallArgsFromVp(argc, vp); 2162 2163 if (!ThrowIfNotConstructing(cx, args, "Memory")) { 2164 return false; 2165 } 2166 2167 if (!args.requireAtLeast(cx, "WebAssembly.Memory", 1)) { 2168 return false; 2169 } 2170 2171 if (!args.get(0).isObject()) { 2172 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2173 JSMSG_WASM_BAD_DESC_ARG, "memory"); 2174 return false; 2175 } 2176 2177 RootedObject obj(cx, &args[0].toObject()); 2178 Limits limits; 2179 if (!GetLimits(cx, obj, LimitsKind::Memory, &limits) || 2180 !CheckLimits( 2181 cx, MaxMemoryPagesValidation(limits.addressType, limits.pageSize), 2182 LimitsKind::Memory, &limits)) { 2183 return false; 2184 } 2185 2186 if (Pages::fromPageCount(limits.initial, limits.pageSize) > 2187 MaxMemoryPages(limits.addressType, limits.pageSize)) { 2188 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2189 JSMSG_WASM_MEM_IMP_LIMIT); 2190 return false; 2191 } 2192 MemoryDesc memory(limits); 2193 2194 Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, 2195 CreateWasmBuffer(cx, memory)); 2196 if (!buffer) { 2197 return false; 2198 } 2199 2200 RootedObject proto(cx, 2201 GetWasmConstructorPrototype(cx, args, JSProto_WasmMemory)); 2202 if (!proto) { 2203 ReportOutOfMemory(cx); 2204 return false; 2205 } 2206 2207 Rooted<WasmMemoryObject*> memoryObj( 2208 cx, WasmMemoryObject::create( 2209 cx, buffer, 2210 IsHugeMemoryEnabled(limits.addressType, limits.pageSize), proto)); 2211 if (!memoryObj) { 2212 return false; 2213 } 2214 2215 args.rval().setObject(*memoryObj); 2216 return true; 2217 } 2218 2219 static bool IsMemory(HandleValue v) { 2220 return v.isObject() && v.toObject().is<WasmMemoryObject>(); 2221 } 2222 2223 /* static */ 2224 ArrayBufferObjectMaybeShared* WasmMemoryObject::refreshBuffer( 2225 JSContext* cx, Handle<WasmMemoryObject*> memoryObj, 2226 Handle<ArrayBufferObjectMaybeShared*> buffer) { 2227 if (memoryObj->isShared()) { 2228 size_t memoryLength = memoryObj->volatileMemoryLength(); 2229 MOZ_ASSERT_IF(!buffer->is<GrowableSharedArrayBufferObject>(), 2230 memoryLength >= buffer->byteLength()); 2231 2232 // The `length` field on a fixed length SAB cannot change even if 2233 // the underlying memory has grown. The spec therefore requires that 2234 // accessing the buffer property will create a new fixed length SAB 2235 // with the current length if the underlying raw buffer's length has 2236 // changed. We don't need to do this for growable SAB. 2237 if (!buffer->is<GrowableSharedArrayBufferObject>() && 2238 memoryLength > buffer->byteLength()) { 2239 Rooted<SharedArrayBufferObject*> newBuffer( 2240 cx, SharedArrayBufferObject::New( 2241 cx, memoryObj->sharedArrayRawBuffer(), memoryLength)); 2242 if (!newBuffer) { 2243 return nullptr; 2244 } 2245 MOZ_ASSERT(newBuffer->is<FixedLengthSharedArrayBufferObject>()); 2246 // OK to addReference after we try to allocate because the memoryObj 2247 // keeps the rawBuffer alive. 2248 if (!memoryObj->sharedArrayRawBuffer()->addReference()) { 2249 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2250 JSMSG_SC_SAB_REFCNT_OFLO); 2251 return nullptr; 2252 } 2253 memoryObj->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuffer)); 2254 return newBuffer; 2255 } 2256 } 2257 return buffer; 2258 } 2259 2260 /* static */ 2261 bool WasmMemoryObject::bufferGetterImpl(JSContext* cx, const CallArgs& args) { 2262 Rooted<WasmMemoryObject*> memoryObj( 2263 cx, &args.thisv().toObject().as<WasmMemoryObject>()); 2264 2265 Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &memoryObj->buffer()); 2266 MOZ_RELEASE_ASSERT(buffer->isWasm() && !buffer->isPreparedForAsmJS()); 2267 2268 ArrayBufferObjectMaybeShared* refreshedBuffer = 2269 WasmMemoryObject::refreshBuffer(cx, memoryObj, buffer); 2270 if (!refreshedBuffer) { 2271 return false; 2272 } 2273 2274 args.rval().setObject(*refreshedBuffer); 2275 return true; 2276 } 2277 2278 /* static */ 2279 bool WasmMemoryObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp) { 2280 CallArgs args = CallArgsFromVp(argc, vp); 2281 return CallNonGenericMethod<IsMemory, bufferGetterImpl>(cx, args); 2282 } 2283 2284 const JSPropertySpec WasmMemoryObject::properties[] = { 2285 JS_PSG("buffer", WasmMemoryObject::bufferGetter, JSPROP_ENUMERATE), 2286 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Memory", JSPROP_READONLY), 2287 JS_PS_END, 2288 }; 2289 2290 /* static */ 2291 bool WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args) { 2292 Rooted<WasmMemoryObject*> memory( 2293 cx, &args.thisv().toObject().as<WasmMemoryObject>()); 2294 2295 if (!args.requireAtLeast(cx, "WebAssembly.Memory.grow", 1)) { 2296 return false; 2297 } 2298 2299 uint64_t delta; 2300 if (!EnforceAddressValue(cx, args.get(0), memory->addressType(), "Memory", 2301 "grow delta", &delta)) { 2302 return false; 2303 } 2304 2305 uint32_t ret = grow(memory, delta, cx); 2306 2307 if (ret == uint32_t(-1)) { 2308 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, 2309 "memory"); 2310 return false; 2311 } 2312 2313 RootedValue result(cx); 2314 if (!CreateAddressValue(cx, ret, memory->addressType(), &result)) { 2315 ReportOutOfMemory(cx); 2316 return false; 2317 } 2318 args.rval().set(result); 2319 return true; 2320 } 2321 2322 /* static */ 2323 bool WasmMemoryObject::grow(JSContext* cx, unsigned argc, Value* vp) { 2324 CallArgs args = CallArgsFromVp(argc, vp); 2325 return CallNonGenericMethod<IsMemory, growImpl>(cx, args); 2326 } 2327 2328 /* static */ 2329 bool WasmMemoryObject::discardImpl(JSContext* cx, const CallArgs& args) { 2330 Rooted<WasmMemoryObject*> memory( 2331 cx, &args.thisv().toObject().as<WasmMemoryObject>()); 2332 2333 if (!args.requireAtLeast(cx, "WebAssembly.Memory.discard", 2)) { 2334 return false; 2335 } 2336 2337 uint64_t byteOffset; 2338 if (!EnforceRangeU64(cx, args.get(0), "Memory", "byte offset", &byteOffset)) { 2339 return false; 2340 } 2341 2342 uint64_t byteLen; 2343 if (!EnforceRangeU64(cx, args.get(1), "Memory", "length", &byteLen)) { 2344 return false; 2345 } 2346 2347 if (byteOffset % wasm::StandardPageSizeBytes != 0 || 2348 byteLen % wasm::StandardPageSizeBytes != 0) { 2349 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2350 JSMSG_WASM_UNALIGNED_ACCESS); 2351 return false; 2352 } 2353 2354 if (!wasm::MemoryBoundsCheck(byteOffset, byteLen, 2355 memory->volatileMemoryLength())) { 2356 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2357 JSMSG_WASM_OUT_OF_BOUNDS); 2358 return false; 2359 } 2360 2361 discard(memory, byteOffset, byteLen, cx); 2362 2363 args.rval().setUndefined(); 2364 return true; 2365 } 2366 2367 /* static */ 2368 bool WasmMemoryObject::discard(JSContext* cx, unsigned argc, Value* vp) { 2369 CallArgs args = CallArgsFromVp(argc, vp); 2370 return CallNonGenericMethod<IsMemory, discardImpl>(cx, args); 2371 } 2372 2373 #ifdef ENABLE_WASM_RESIZABLE_ARRAYBUFFER 2374 /* static */ 2375 bool WasmMemoryObject::toFixedLengthBufferImpl(JSContext* cx, 2376 const CallArgs& args) { 2377 Rooted<WasmMemoryObject*> memory( 2378 cx, &args.thisv().toObject().as<WasmMemoryObject>()); 2379 2380 Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &memory->buffer()); 2381 MOZ_RELEASE_ASSERT(buffer->isWasm() && !buffer->isPreparedForAsmJS()); 2382 // If IsFixedLengthArrayBuffer(buffer) is true, return buffer. 2383 if (!buffer->isResizable()) { 2384 ArrayBufferObjectMaybeShared* refreshedBuffer = 2385 refreshBuffer(cx, memory, buffer); 2386 if (!refreshedBuffer) { 2387 return false; 2388 } 2389 args.rval().set(ObjectValue(*refreshedBuffer)); 2390 return true; 2391 } 2392 2393 Rooted<ArrayBufferObjectMaybeShared*> fixedBuffer(cx); 2394 if (memory->isShared()) { 2395 Rooted<SharedArrayBufferObject*> oldBuffer( 2396 cx, &buffer->as<SharedArrayBufferObject>()); 2397 fixedBuffer.set(SharedArrayBufferObject::createFromWasmObject< 2398 FixedLengthSharedArrayBufferObject>(cx, oldBuffer)); 2399 } else { 2400 Rooted<ArrayBufferObject*> oldBuffer(cx, &buffer->as<ArrayBufferObject>()); 2401 fixedBuffer.set( 2402 ArrayBufferObject::createFromWasmObject<FixedLengthArrayBufferObject>( 2403 cx, oldBuffer)); 2404 } 2405 2406 if (!fixedBuffer) { 2407 return false; 2408 } 2409 memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*fixedBuffer)); 2410 args.rval().set(ObjectValue(*fixedBuffer)); 2411 return true; 2412 } 2413 2414 /* static */ 2415 bool WasmMemoryObject::toFixedLengthBuffer(JSContext* cx, unsigned argc, 2416 Value* vp) { 2417 CallArgs args = CallArgsFromVp(argc, vp); 2418 return CallNonGenericMethod<IsMemory, toFixedLengthBufferImpl>(cx, args); 2419 } 2420 2421 /* static */ 2422 bool WasmMemoryObject::toResizableBufferImpl(JSContext* cx, 2423 const CallArgs& args) { 2424 Rooted<WasmMemoryObject*> memory( 2425 cx, &args.thisv().toObject().as<WasmMemoryObject>()); 2426 2427 Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &memory->buffer()); 2428 // If IsFixedLengthArrayBuffer(buffer) is false, return buffer. 2429 if (buffer->isResizable()) { 2430 args.rval().set(ObjectValue(*buffer)); 2431 return true; 2432 } 2433 2434 if (buffer->wasmSourceMaxPages().isNothing()) { 2435 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2436 JSMSG_WASM_MEMORY_NOT_RESIZABLE); 2437 return false; 2438 } 2439 2440 Rooted<ArrayBufferObjectMaybeShared*> resizableBuffer(cx); 2441 if (memory->isShared()) { 2442 Rooted<SharedArrayBufferObject*> oldBuffer( 2443 cx, &buffer->as<SharedArrayBufferObject>()); 2444 resizableBuffer.set(SharedArrayBufferObject::createFromWasmObject< 2445 GrowableSharedArrayBufferObject>(cx, oldBuffer)); 2446 } else { 2447 Rooted<ArrayBufferObject*> oldBuffer(cx, &buffer->as<ArrayBufferObject>()); 2448 resizableBuffer.set( 2449 ArrayBufferObject::createFromWasmObject<ResizableArrayBufferObject>( 2450 cx, oldBuffer)); 2451 } 2452 2453 if (!resizableBuffer) { 2454 return false; 2455 } 2456 memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*resizableBuffer)); 2457 args.rval().set(ObjectValue(*resizableBuffer)); 2458 return true; 2459 } 2460 2461 /* static */ 2462 bool WasmMemoryObject::toResizableBuffer(JSContext* cx, unsigned argc, 2463 Value* vp) { 2464 CallArgs args = CallArgsFromVp(argc, vp); 2465 return CallNonGenericMethod<IsMemory, toResizableBufferImpl>(cx, args); 2466 } 2467 #endif // ENABLE_WASM_RESIZABLE_ARRAYBUFFER 2468 2469 const JSFunctionSpec WasmMemoryObject::methods[] = { 2470 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 2471 JS_FN("type", WasmMemoryObject::type, 0, JSPROP_ENUMERATE), 2472 #endif 2473 JS_FN("grow", WasmMemoryObject::grow, 1, JSPROP_ENUMERATE), 2474 #ifdef ENABLE_WASM_RESIZABLE_ARRAYBUFFER 2475 JS_FN("toFixedLengthBuffer", WasmMemoryObject::toFixedLengthBuffer, 0, 2476 JSPROP_ENUMERATE), 2477 JS_FN("toResizableBuffer", WasmMemoryObject::toResizableBuffer, 0, 2478 JSPROP_ENUMERATE), 2479 #endif 2480 JS_FS_END, 2481 }; 2482 2483 const JSFunctionSpec WasmMemoryObject::memoryControlMethods[] = { 2484 JS_FN("discard", WasmMemoryObject::discard, 2, JSPROP_ENUMERATE), 2485 JS_FS_END, 2486 }; 2487 2488 const JSFunctionSpec WasmMemoryObject::static_methods[] = { 2489 JS_FS_END, 2490 }; 2491 2492 ArrayBufferObjectMaybeShared& WasmMemoryObject::buffer() const { 2493 return getReservedSlot(BUFFER_SLOT) 2494 .toObject() 2495 .as<ArrayBufferObjectMaybeShared>(); 2496 } 2497 2498 WasmSharedArrayRawBuffer* WasmMemoryObject::sharedArrayRawBuffer() const { 2499 MOZ_ASSERT(isShared()); 2500 return buffer().as<SharedArrayBufferObject>().rawWasmBufferObject(); 2501 } 2502 2503 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 2504 bool WasmMemoryObject::typeImpl(JSContext* cx, const CallArgs& args) { 2505 Rooted<WasmMemoryObject*> memoryObj( 2506 cx, &args.thisv().toObject().as<WasmMemoryObject>()); 2507 RootedObject typeObj(cx, MemoryTypeToObject(cx, memoryObj->isShared(), 2508 memoryObj->addressType(), 2509 memoryObj->volatilePages(), 2510 memoryObj->sourceMaxPages())); 2511 if (!typeObj) { 2512 return false; 2513 } 2514 args.rval().setObject(*typeObj); 2515 return true; 2516 } 2517 2518 bool WasmMemoryObject::type(JSContext* cx, unsigned argc, Value* vp) { 2519 CallArgs args = CallArgsFromVp(argc, vp); 2520 return CallNonGenericMethod<IsMemory, typeImpl>(cx, args); 2521 } 2522 #endif 2523 2524 size_t WasmMemoryObject::volatileMemoryLength() const { 2525 if (isShared()) { 2526 return sharedArrayRawBuffer()->volatileByteLength(); 2527 } 2528 return buffer().byteLength(); 2529 } 2530 2531 wasm::Pages WasmMemoryObject::volatilePages() const { 2532 if (isShared()) { 2533 return sharedArrayRawBuffer()->volatileWasmPages(); 2534 } 2535 return buffer().wasmPages(); 2536 } 2537 2538 wasm::Pages WasmMemoryObject::clampedMaxPages() const { 2539 if (isShared()) { 2540 return sharedArrayRawBuffer()->wasmClampedMaxPages(); 2541 } 2542 return buffer().wasmClampedMaxPages(); 2543 } 2544 2545 Maybe<wasm::Pages> WasmMemoryObject::sourceMaxPages() const { 2546 if (isShared()) { 2547 return Some(sharedArrayRawBuffer()->wasmSourceMaxPages()); 2548 } 2549 return buffer().wasmSourceMaxPages(); 2550 } 2551 2552 wasm::AddressType WasmMemoryObject::addressType() const { 2553 if (isShared()) { 2554 return sharedArrayRawBuffer()->wasmAddressType(); 2555 } 2556 return buffer().wasmAddressType(); 2557 } 2558 2559 bool WasmMemoryObject::isShared() const { 2560 return buffer().is<SharedArrayBufferObject>(); 2561 } 2562 2563 bool WasmMemoryObject::hasObservers() const { 2564 return !getReservedSlot(OBSERVERS_SLOT).isUndefined(); 2565 } 2566 2567 WasmMemoryObject::InstanceSet& WasmMemoryObject::observers() const { 2568 MOZ_ASSERT(hasObservers()); 2569 return *reinterpret_cast<InstanceSet*>( 2570 getReservedSlot(OBSERVERS_SLOT).toPrivate()); 2571 } 2572 2573 WasmMemoryObject::InstanceSet* WasmMemoryObject::getOrCreateObservers( 2574 JSContext* cx) { 2575 if (!hasObservers()) { 2576 auto observers = MakeUnique<InstanceSet>(cx->zone(), cx->zone()); 2577 if (!observers) { 2578 ReportOutOfMemory(cx); 2579 return nullptr; 2580 } 2581 2582 InitReservedSlot(this, OBSERVERS_SLOT, observers.release(), 2583 MemoryUse::WasmMemoryObservers); 2584 } 2585 2586 return &observers(); 2587 } 2588 2589 bool WasmMemoryObject::isHuge() const { 2590 return getReservedSlot(ISHUGE_SLOT).toBoolean(); 2591 } 2592 2593 bool WasmMemoryObject::movingGrowable() const { 2594 return !isHuge() && !buffer().wasmSourceMaxPages(); 2595 } 2596 2597 size_t WasmMemoryObject::boundsCheckLimit() const { 2598 if (!buffer().isWasm() || isHuge()) { 2599 return buffer().byteLength(); 2600 } 2601 #ifdef ENABLE_WASM_CUSTOM_PAGE_SIZES 2602 // For tiny page sizes, we need to use the actual byte length as the bounds 2603 // check as we cannot rely on virtual memory for accesses between the byte 2604 // length and the mapped size. 2605 if (buffer().wasmPageSize() == wasm::PageSize::Tiny) { 2606 size_t limit = buffer().byteLength(); 2607 MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(addressType(), 2608 buffer().wasmPageSize())); 2609 return limit; 2610 } 2611 #endif 2612 size_t mappedSize = buffer().wasmMappedSize(); 2613 #if !defined(JS_64BIT) 2614 // See clamping performed in CreateSpecificWasmBuffer(). On 32-bit systems 2615 // we do not want to overflow a uint32_t. For the other 64-bit compilers, 2616 // all constraints are implied by the largest accepted value for a memory's 2617 // max field. 2618 MOZ_ASSERT(mappedSize < UINT32_MAX); 2619 #endif 2620 MOZ_ASSERT(buffer().wasmPageSize() == wasm::PageSize::Standard); 2621 MOZ_ASSERT(mappedSize % wasm::StandardPageSizeBytes == 0); 2622 MOZ_ASSERT(mappedSize >= wasm::GuardSize); 2623 size_t limit = mappedSize - wasm::GuardSize; 2624 MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(addressType(), 2625 wasm::PageSize::Standard)); 2626 return limit; 2627 } 2628 2629 wasm::PageSize WasmMemoryObject::pageSize() const { 2630 if (isShared()) { 2631 return sharedArrayRawBuffer()->wasmPageSize(); 2632 } 2633 return buffer().wasmPageSize(); 2634 } 2635 2636 bool WasmMemoryObject::addMovingGrowObserver(JSContext* cx, 2637 WasmInstanceObject* instance) { 2638 MOZ_ASSERT(movingGrowable()); 2639 2640 InstanceSet* observers = getOrCreateObservers(cx); 2641 if (!observers) { 2642 return false; 2643 } 2644 2645 // A memory can be imported multiple times into an instance, but we only 2646 // register the instance as an observer once. 2647 if (!observers->put(instance)) { 2648 ReportOutOfMemory(cx); 2649 return false; 2650 } 2651 2652 return true; 2653 } 2654 2655 /* static */ 2656 uint64_t WasmMemoryObject::growShared(Handle<WasmMemoryObject*> memory, 2657 uint64_t delta) { 2658 WasmSharedArrayRawBuffer* rawBuf = memory->sharedArrayRawBuffer(); 2659 WasmSharedArrayRawBuffer::Lock lock(rawBuf); 2660 2661 Pages oldNumPages = rawBuf->volatileWasmPages(); 2662 Pages newPages = oldNumPages; 2663 if (!newPages.checkedIncrement(delta)) { 2664 return uint64_t(int64_t(-1)); 2665 } 2666 2667 if (!rawBuf->wasmGrowToPagesInPlace(lock, memory->addressType(), newPages)) { 2668 return uint64_t(int64_t(-1)); 2669 } 2670 // New buffer objects will be created lazily in all agents (including in 2671 // this agent) by bufferGetterImpl, above, so no more work to do here. 2672 2673 return oldNumPages.pageCount(); 2674 } 2675 2676 /* static */ 2677 uint64_t WasmMemoryObject::grow(Handle<WasmMemoryObject*> memory, 2678 uint64_t delta, JSContext* cx) { 2679 if (memory->isShared()) { 2680 return growShared(memory, delta); 2681 } 2682 2683 Rooted<ArrayBufferObject*> oldBuf(cx, 2684 &memory->buffer().as<ArrayBufferObject>()); 2685 2686 #if !defined(JS_64BIT) 2687 // TODO (large ArrayBuffer): See more information at the definition of 2688 // MaxMemoryBytes(). 2689 MOZ_ASSERT( 2690 MaxMemoryBytes(memory->addressType(), memory->pageSize()) <= UINT32_MAX, 2691 "Avoid 32-bit overflows"); 2692 #endif 2693 2694 Pages oldNumPages = oldBuf->wasmPages(); 2695 Pages newPages = oldNumPages; 2696 if (!newPages.checkedIncrement(delta)) { 2697 return uint64_t(int64_t(-1)); 2698 } 2699 2700 ArrayBufferObject* newBuf; 2701 if (memory->movingGrowable()) { 2702 MOZ_ASSERT(!memory->isHuge()); 2703 newBuf = ArrayBufferObject::wasmMovingGrowToPages(memory->addressType(), 2704 newPages, oldBuf, cx); 2705 } else { 2706 newBuf = ArrayBufferObject::wasmGrowToPagesInPlace(memory->addressType(), 2707 newPages, oldBuf, cx); 2708 } 2709 if (!newBuf) { 2710 return uint64_t(int64_t(-1)); 2711 } 2712 2713 memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf)); 2714 2715 // Only notify moving-grow-observers after the BUFFER_SLOT has been updated 2716 // since observers will call buffer(). 2717 if (memory->hasObservers()) { 2718 for (InstanceSet::Range r = memory->observers().all(); !r.empty(); 2719 r.popFront()) { 2720 r.front()->instance().onMovingGrowMemory(memory); 2721 } 2722 } 2723 2724 return oldNumPages.pageCount(); 2725 } 2726 2727 /* static */ 2728 void WasmMemoryObject::discard(Handle<WasmMemoryObject*> memory, 2729 uint64_t byteOffset, uint64_t byteLen, 2730 JSContext* cx) { 2731 if (memory->isShared()) { 2732 Rooted<SharedArrayBufferObject*> buf( 2733 cx, &memory->buffer().as<SharedArrayBufferObject>()); 2734 SharedArrayBufferObject::wasmDiscard(buf, byteOffset, byteLen); 2735 } else { 2736 Rooted<ArrayBufferObject*> buf(cx, 2737 &memory->buffer().as<ArrayBufferObject>()); 2738 ArrayBufferObject::wasmDiscard(buf, byteOffset, byteLen); 2739 } 2740 } 2741 2742 bool js::wasm::IsSharedWasmMemoryObject(JSObject* obj) { 2743 WasmMemoryObject* mobj = obj->maybeUnwrapIf<WasmMemoryObject>(); 2744 return mobj && mobj->isShared(); 2745 } 2746 2747 // ============================================================================ 2748 // WebAssembly.Table class and methods 2749 2750 const JSClassOps WasmTableObject::classOps_ = { 2751 nullptr, // addProperty 2752 nullptr, // delProperty 2753 nullptr, // enumerate 2754 nullptr, // newEnumerate 2755 nullptr, // resolve 2756 nullptr, // mayResolve 2757 WasmTableObject::finalize, // finalize 2758 nullptr, // call 2759 nullptr, // construct 2760 WasmTableObject::trace, // trace 2761 }; 2762 2763 const JSClass WasmTableObject::class_ = { 2764 "WebAssembly.Table", 2765 JSCLASS_DELAY_METADATA_BUILDER | 2766 JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS) | 2767 JSCLASS_FOREGROUND_FINALIZE, 2768 &WasmTableObject::classOps_, 2769 &WasmTableObject::classSpec_, 2770 }; 2771 2772 const JSClass& WasmTableObject::protoClass_ = PlainObject::class_; 2773 2774 static constexpr char WasmTableName[] = "Table"; 2775 2776 const ClassSpec WasmTableObject::classSpec_ = { 2777 CreateWasmConstructor<WasmTableObject, WasmTableName>, 2778 GenericCreatePrototype<WasmTableObject>, 2779 WasmTableObject::static_methods, 2780 nullptr, 2781 WasmTableObject::methods, 2782 WasmTableObject::properties, 2783 nullptr, 2784 ClassSpec::DontDefineConstructor, 2785 }; 2786 2787 bool WasmTableObject::isNewborn() const { 2788 MOZ_ASSERT(is<WasmTableObject>()); 2789 return getReservedSlot(TABLE_SLOT).isUndefined(); 2790 } 2791 2792 /* static */ 2793 void WasmTableObject::finalize(JS::GCContext* gcx, JSObject* obj) { 2794 WasmTableObject& tableObj = obj->as<WasmTableObject>(); 2795 if (!tableObj.isNewborn()) { 2796 auto& table = tableObj.table(); 2797 gcx->release(obj, &table, table.gcMallocBytes(), MemoryUse::WasmTableTable); 2798 } 2799 } 2800 2801 /* static */ 2802 void WasmTableObject::trace(JSTracer* trc, JSObject* obj) { 2803 WasmTableObject& tableObj = obj->as<WasmTableObject>(); 2804 if (!tableObj.isNewborn()) { 2805 tableObj.table().tracePrivate(trc); 2806 } 2807 } 2808 2809 // Return the JS value to use when a parameter to a function requiring a table 2810 // value is omitted. An implementation of [1]. 2811 // 2812 // [1] 2813 // https://webassembly.github.io/spec/js-api/#defaultvalue 2814 static Value RefTypeDefaultValue(wasm::RefType tableType) { 2815 return tableType.isExtern() ? UndefinedValue() : NullValue(); 2816 } 2817 2818 /* static */ 2819 WasmTableObject* WasmTableObject::create(JSContext* cx, Limits limits, 2820 wasm::RefType tableType, 2821 HandleObject proto) { 2822 AutoSetNewObjectMetadata metadata(cx); 2823 Rooted<WasmTableObject*> obj( 2824 cx, NewObjectWithGivenProto<WasmTableObject>(cx, proto)); 2825 if (!obj) { 2826 return nullptr; 2827 } 2828 2829 MOZ_ASSERT(obj->isNewborn()); 2830 2831 TableDesc td(limits, tableType, Nothing(), 2832 /*isAsmJS*/ false, 2833 /*isImported=*/true, /*isExported=*/true); 2834 2835 SharedTable table = Table::create(cx, td, obj); 2836 if (!table) { 2837 return nullptr; 2838 } 2839 2840 size_t size = table->gcMallocBytes(); 2841 InitReservedSlot(obj, TABLE_SLOT, table.forget().take(), size, 2842 MemoryUse::WasmTableTable); 2843 2844 MOZ_ASSERT(!obj->isNewborn()); 2845 return obj; 2846 } 2847 2848 /* static */ 2849 bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) { 2850 CallArgs args = CallArgsFromVp(argc, vp); 2851 2852 if (!ThrowIfNotConstructing(cx, args, "Table")) { 2853 return false; 2854 } 2855 2856 if (!args.requireAtLeast(cx, "WebAssembly.Table", 1)) { 2857 return false; 2858 } 2859 2860 if (!args.get(0).isObject()) { 2861 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2862 JSMSG_WASM_BAD_DESC_ARG, "table"); 2863 return false; 2864 } 2865 2866 RootedObject obj(cx, &args[0].toObject()); 2867 2868 JSAtom* elementAtom = Atomize(cx, "element", strlen("element")); 2869 if (!elementAtom) { 2870 return false; 2871 } 2872 RootedId elementId(cx, AtomToId(elementAtom)); 2873 2874 RootedValue elementVal(cx); 2875 if (!GetProperty(cx, obj, obj, elementId, &elementVal)) { 2876 return false; 2877 } 2878 2879 RefType tableType; 2880 if (!ToRefType(cx, elementVal, &tableType)) { 2881 return false; 2882 } 2883 2884 Limits limits; 2885 if (!GetLimits(cx, obj, LimitsKind::Table, &limits) || 2886 !CheckLimits(cx, MaxTableElemsValidation(limits.addressType), 2887 LimitsKind::Table, &limits)) { 2888 return false; 2889 } 2890 2891 if (limits.initial > MaxTableElemsRuntime) { 2892 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 2893 JSMSG_WASM_TABLE_IMP_LIMIT); 2894 return false; 2895 } 2896 2897 RootedObject proto(cx, 2898 GetWasmConstructorPrototype(cx, args, JSProto_WasmTable)); 2899 if (!proto) { 2900 ReportOutOfMemory(cx); 2901 return false; 2902 } 2903 2904 Rooted<WasmTableObject*> table( 2905 cx, WasmTableObject::create(cx, limits, tableType, proto)); 2906 if (!table) { 2907 return false; 2908 } 2909 2910 // Initialize the table to a default value 2911 RootedValue initValue( 2912 cx, args.length() < 2 ? RefTypeDefaultValue(tableType) : args[1]); 2913 if (!CheckRefType(cx, tableType, initValue)) { 2914 return false; 2915 } 2916 2917 // Skip initializing the table if the fill value is null, as that is the 2918 // default value. 2919 if (!initValue.isNull() && 2920 !table->fillRange(cx, 0, limits.initial, initValue)) { 2921 return false; 2922 } 2923 #ifdef DEBUG 2924 // Assert that null is the default value of a new table. 2925 if (initValue.isNull()) { 2926 table->table().assertRangeNull(0, limits.initial); 2927 } 2928 if (!tableType.isNullable()) { 2929 table->table().assertRangeNotNull(0, limits.initial); 2930 } 2931 #endif 2932 2933 args.rval().setObject(*table); 2934 return true; 2935 } 2936 2937 static bool IsTable(HandleValue v) { 2938 return v.isObject() && v.toObject().is<WasmTableObject>(); 2939 } 2940 2941 /* static */ 2942 bool WasmTableObject::lengthGetterImpl(JSContext* cx, const CallArgs& args) { 2943 const WasmTableObject& tableObj = 2944 args.thisv().toObject().as<WasmTableObject>(); 2945 RootedValue length(cx); 2946 if (!CreateAddressValue(cx, tableObj.table().length(), 2947 tableObj.table().addressType(), &length)) { 2948 ReportOutOfMemory(cx); 2949 return false; 2950 } 2951 args.rval().set(length); 2952 return true; 2953 } 2954 2955 /* static */ 2956 bool WasmTableObject::lengthGetter(JSContext* cx, unsigned argc, Value* vp) { 2957 CallArgs args = CallArgsFromVp(argc, vp); 2958 return CallNonGenericMethod<IsTable, lengthGetterImpl>(cx, args); 2959 } 2960 2961 const JSPropertySpec WasmTableObject::properties[] = { 2962 JS_PSG("length", WasmTableObject::lengthGetter, JSPROP_ENUMERATE), 2963 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Table", JSPROP_READONLY), 2964 JS_PS_END, 2965 }; 2966 2967 // Gets an AddressValue parameter for a table. This differs from our general 2968 // EnforceAddressValue because our table implementation still uses 32-bit sizes 2969 // internally, and this function therefore returns a uint32_t. Values outside 2970 // the 32-bit range will be clamped to UINT32_MAX, which will always trigger 2971 // bounds checks for all Table uses of AddressValue. See 2972 // MacroAssembler::wasmClampTable64Address and its uses. 2973 // 2974 // isAddress should be true if the value is an actual address, and false if it 2975 // is a different quantity (e.g. a grow delta). 2976 static bool EnforceTableAddressValue(JSContext* cx, HandleValue v, 2977 const Table& table, const char* noun, 2978 uint32_t* result, bool isAddress) { 2979 uint64_t result64; 2980 if (!EnforceAddressValue(cx, v, table.addressType(), "Table", noun, 2981 &result64)) { 2982 return false; 2983 } 2984 2985 static_assert(MaxTableElemsRuntime < UINT32_MAX); 2986 *result = result64 > UINT32_MAX ? UINT32_MAX : uint32_t(result64); 2987 2988 if (isAddress && *result >= table.length()) { 2989 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2990 JSMSG_WASM_BAD_RANGE, "Table", noun); 2991 return false; 2992 } 2993 2994 return true; 2995 } 2996 2997 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 2998 /* static */ 2999 bool WasmTableObject::typeImpl(JSContext* cx, const CallArgs& args) { 3000 Table& table = args.thisv().toObject().as<WasmTableObject>().table(); 3001 RootedObject typeObj( 3002 cx, TableTypeToObject(cx, table.addressType(), table.elemType(), 3003 table.length(), table.maximum())); 3004 if (!typeObj) { 3005 return false; 3006 } 3007 args.rval().setObject(*typeObj); 3008 return true; 3009 } 3010 3011 /* static */ 3012 bool WasmTableObject::type(JSContext* cx, unsigned argc, Value* vp) { 3013 CallArgs args = CallArgsFromVp(argc, vp); 3014 return CallNonGenericMethod<IsTable, typeImpl>(cx, args); 3015 } 3016 #endif 3017 3018 /* static */ 3019 bool WasmTableObject::getImpl(JSContext* cx, const CallArgs& args) { 3020 Rooted<WasmTableObject*> tableObj( 3021 cx, &args.thisv().toObject().as<WasmTableObject>()); 3022 const Table& table = tableObj->table(); 3023 3024 if (!args.requireAtLeast(cx, "WebAssembly.Table.get", 1)) { 3025 return false; 3026 } 3027 3028 uint32_t address; 3029 if (!EnforceTableAddressValue(cx, args.get(0), table, "get address", &address, 3030 /*isAddress=*/true)) { 3031 return false; 3032 } 3033 3034 return table.getValue(cx, address, args.rval()); 3035 } 3036 3037 /* static */ 3038 bool WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp) { 3039 CallArgs args = CallArgsFromVp(argc, vp); 3040 return CallNonGenericMethod<IsTable, getImpl>(cx, args); 3041 } 3042 3043 /* static */ 3044 bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) { 3045 Rooted<WasmTableObject*> tableObj( 3046 cx, &args.thisv().toObject().as<WasmTableObject>()); 3047 Table& table = tableObj->table(); 3048 3049 if (!args.requireAtLeast(cx, "WebAssembly.Table.set", 1)) { 3050 return false; 3051 } 3052 3053 uint32_t address; 3054 if (!EnforceTableAddressValue(cx, args.get(0), table, "set address", &address, 3055 /*isAddress=*/true)) { 3056 return false; 3057 } 3058 3059 RootedValue fillValue( 3060 cx, args.length() < 2 ? RefTypeDefaultValue(table.elemType()) : args[1]); 3061 if (!tableObj->fillRange(cx, address, 1, fillValue)) { 3062 return false; 3063 } 3064 3065 args.rval().setUndefined(); 3066 return true; 3067 } 3068 3069 /* static */ 3070 bool WasmTableObject::set(JSContext* cx, unsigned argc, Value* vp) { 3071 CallArgs args = CallArgsFromVp(argc, vp); 3072 return CallNonGenericMethod<IsTable, setImpl>(cx, args); 3073 } 3074 3075 /* static */ 3076 bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) { 3077 Rooted<WasmTableObject*> tableObj( 3078 cx, &args.thisv().toObject().as<WasmTableObject>()); 3079 Table& table = tableObj->table(); 3080 3081 if (!args.requireAtLeast(cx, "WebAssembly.Table.grow", 1)) { 3082 return false; 3083 } 3084 3085 uint32_t delta; 3086 if (!EnforceTableAddressValue(cx, args.get(0), table, "grow delta", &delta, 3087 /*isAddress=*/false)) { 3088 return false; 3089 } 3090 3091 RootedValue fillValue( 3092 cx, args.length() < 2 ? RefTypeDefaultValue(table.elemType()) : args[1]); 3093 if (!CheckRefType(cx, table.elemType(), fillValue)) { 3094 return false; 3095 } 3096 3097 uint32_t oldLength = table.grow(delta); 3098 3099 if (oldLength == uint32_t(-1)) { 3100 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW, 3101 "table"); 3102 return false; 3103 } 3104 3105 // Skip filling the grown range of the table if the fill value is null, as 3106 // that is the default value. 3107 if (!fillValue.isNull() && 3108 !tableObj->fillRange(cx, oldLength, delta, fillValue)) { 3109 return false; 3110 } 3111 #ifdef DEBUG 3112 // Assert that null is the default value of the grown range. 3113 if (fillValue.isNull()) { 3114 table.assertRangeNull(oldLength, delta); 3115 } 3116 if (!table.elemType().isNullable()) { 3117 table.assertRangeNotNull(oldLength, delta); 3118 } 3119 #endif 3120 3121 RootedValue result(cx); 3122 if (!CreateAddressValue(cx, oldLength, table.addressType(), &result)) { 3123 ReportOutOfMemory(cx); 3124 return false; 3125 } 3126 args.rval().set(result); 3127 return true; 3128 } 3129 3130 /* static */ 3131 bool WasmTableObject::grow(JSContext* cx, unsigned argc, Value* vp) { 3132 CallArgs args = CallArgsFromVp(argc, vp); 3133 return CallNonGenericMethod<IsTable, growImpl>(cx, args); 3134 } 3135 3136 const JSFunctionSpec WasmTableObject::methods[] = { 3137 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 3138 JS_FN("type", WasmTableObject::type, 0, JSPROP_ENUMERATE), 3139 #endif 3140 JS_FN("get", WasmTableObject::get, 1, JSPROP_ENUMERATE), 3141 JS_FN("set", WasmTableObject::set, 2, JSPROP_ENUMERATE), 3142 JS_FN("grow", WasmTableObject::grow, 1, JSPROP_ENUMERATE), 3143 JS_FS_END, 3144 }; 3145 3146 const JSFunctionSpec WasmTableObject::static_methods[] = { 3147 JS_FS_END, 3148 }; 3149 3150 Table& WasmTableObject::table() const { 3151 return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate(); 3152 } 3153 3154 bool WasmTableObject::fillRange(JSContext* cx, uint32_t index, uint32_t length, 3155 HandleValue value) const { 3156 Table& tab = table(); 3157 3158 // All consumers are required to either bounds check or statically be in 3159 // bounds 3160 MOZ_ASSERT(uint64_t(index) + uint64_t(length) <= tab.length()); 3161 3162 RootedAnyRef any(cx, AnyRef::null()); 3163 if (!wasm::CheckRefType(cx, tab.elemType(), value, &any)) { 3164 return false; 3165 } 3166 switch (tab.repr()) { 3167 case TableRepr::Func: 3168 MOZ_RELEASE_ASSERT(!tab.isAsmJS()); 3169 tab.fillFuncRef(index, length, FuncRef::fromAnyRefUnchecked(any.get()), 3170 cx); 3171 break; 3172 case TableRepr::Ref: 3173 tab.fillAnyRef(index, length, any); 3174 break; 3175 } 3176 return true; 3177 } 3178 3179 // ============================================================================ 3180 // WebAssembly.global class and methods 3181 3182 const JSClassOps WasmGlobalObject::classOps_ = { 3183 nullptr, // addProperty 3184 nullptr, // delProperty 3185 nullptr, // enumerate 3186 nullptr, // newEnumerate 3187 nullptr, // resolve 3188 nullptr, // mayResolve 3189 WasmGlobalObject::finalize, // finalize 3190 nullptr, // call 3191 nullptr, // construct 3192 WasmGlobalObject::trace, // trace 3193 }; 3194 3195 const JSClass WasmGlobalObject::class_ = { 3196 "WebAssembly.Global", 3197 JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS) | 3198 JSCLASS_BACKGROUND_FINALIZE, 3199 &WasmGlobalObject::classOps_, 3200 &WasmGlobalObject::classSpec_, 3201 }; 3202 3203 const JSClass& WasmGlobalObject::protoClass_ = PlainObject::class_; 3204 3205 static constexpr char WasmGlobalName[] = "Global"; 3206 3207 const ClassSpec WasmGlobalObject::classSpec_ = { 3208 CreateWasmConstructor<WasmGlobalObject, WasmGlobalName>, 3209 GenericCreatePrototype<WasmGlobalObject>, 3210 WasmGlobalObject::static_methods, 3211 nullptr, 3212 WasmGlobalObject::methods, 3213 WasmGlobalObject::properties, 3214 nullptr, 3215 ClassSpec::DontDefineConstructor, 3216 }; 3217 3218 /* static */ 3219 void WasmGlobalObject::trace(JSTracer* trc, JSObject* obj) { 3220 WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj); 3221 if (global->isNewborn()) { 3222 // This can happen while we're allocating the object, in which case 3223 // every single slot of the object is not defined yet. In particular, 3224 // there's nothing to trace yet. 3225 return; 3226 } 3227 global->val().get().trace(trc); 3228 } 3229 3230 /* static */ 3231 void WasmGlobalObject::finalize(JS::GCContext* gcx, JSObject* obj) { 3232 WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj); 3233 if (!global->isNewborn()) { 3234 // Release the strong reference to the type definitions this global could 3235 // be referencing. 3236 global->type().Release(); 3237 gcx->delete_(obj, &global->mutableVal(), MemoryUse::WasmGlobalCell); 3238 } 3239 } 3240 3241 /* static */ 3242 WasmGlobalObject* WasmGlobalObject::create(JSContext* cx, HandleVal value, 3243 bool isMutable, HandleObject proto) { 3244 Rooted<WasmGlobalObject*> obj( 3245 cx, NewObjectWithGivenProto<WasmGlobalObject>(cx, proto)); 3246 if (!obj) { 3247 return nullptr; 3248 } 3249 3250 MOZ_ASSERT(obj->isNewborn()); 3251 MOZ_ASSERT(obj->isTenured(), "assumed by global.set post barriers"); 3252 3253 GCPtrVal* val = js_new<GCPtrVal>(Val()); 3254 if (!val) { 3255 ReportOutOfMemory(cx); 3256 return nullptr; 3257 } 3258 obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable)); 3259 InitReservedSlot(obj, VAL_SLOT, val, MemoryUse::WasmGlobalCell); 3260 3261 // It's simpler to initialize the cell after the object has been created, 3262 // to avoid needing to root the cell before the object creation. 3263 // We don't use `setVal` here because the assumes the cell has already 3264 // been initialized. 3265 obj->mutableVal() = value.get(); 3266 // Acquire a strong reference to a type definition this global could 3267 // be referencing. 3268 obj->type().AddRef(); 3269 3270 MOZ_ASSERT(!obj->isNewborn()); 3271 3272 return obj; 3273 } 3274 3275 /* static */ 3276 bool WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) { 3277 CallArgs args = CallArgsFromVp(argc, vp); 3278 3279 if (!ThrowIfNotConstructing(cx, args, "Global")) { 3280 return false; 3281 } 3282 3283 if (!args.requireAtLeast(cx, "WebAssembly.Global", 1)) { 3284 return false; 3285 } 3286 3287 if (!args.get(0).isObject()) { 3288 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3289 JSMSG_WASM_BAD_DESC_ARG, "global"); 3290 return false; 3291 } 3292 3293 RootedObject obj(cx, &args[0].toObject()); 3294 3295 // Extract properties in lexicographic order per spec. 3296 3297 RootedValue mutableVal(cx); 3298 if (!JS_GetProperty(cx, obj, "mutable", &mutableVal)) { 3299 return false; 3300 } 3301 3302 RootedValue typeVal(cx); 3303 if (!JS_GetProperty(cx, obj, "value", &typeVal)) { 3304 return false; 3305 } 3306 3307 ValType globalType; 3308 if (!ToValType(cx, typeVal, &globalType)) { 3309 return false; 3310 } 3311 3312 if (!globalType.isExposable()) { 3313 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3314 JSMSG_WASM_BAD_VAL_TYPE); 3315 return false; 3316 } 3317 3318 bool isMutable = ToBoolean(mutableVal); 3319 3320 // Extract the initial value, or provide a suitable default. 3321 RootedVal globalVal(cx, globalType); 3322 3323 // Override with non-undefined value, if provided. 3324 RootedValue valueVal(cx); 3325 if (globalType.isRefType()) { 3326 valueVal.set(args.length() < 2 ? RefTypeDefaultValue(globalType.refType()) 3327 : args[1]); 3328 if (!Val::fromJSValue(cx, globalType, valueVal, &globalVal)) { 3329 return false; 3330 } 3331 } else { 3332 valueVal.set(args.get(1)); 3333 if (!valueVal.isUndefined() && 3334 !Val::fromJSValue(cx, globalType, valueVal, &globalVal)) { 3335 return false; 3336 } 3337 } 3338 3339 RootedObject proto(cx, 3340 GetWasmConstructorPrototype(cx, args, JSProto_WasmGlobal)); 3341 if (!proto) { 3342 ReportOutOfMemory(cx); 3343 return false; 3344 } 3345 3346 WasmGlobalObject* global = 3347 WasmGlobalObject::create(cx, globalVal, isMutable, proto); 3348 if (!global) { 3349 return false; 3350 } 3351 3352 args.rval().setObject(*global); 3353 return true; 3354 } 3355 3356 static bool IsGlobal(HandleValue v) { 3357 return v.isObject() && v.toObject().is<WasmGlobalObject>(); 3358 } 3359 3360 /* static */ 3361 bool WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args) { 3362 const WasmGlobalObject& globalObj = 3363 args.thisv().toObject().as<WasmGlobalObject>(); 3364 if (!globalObj.type().isExposable()) { 3365 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3366 JSMSG_WASM_BAD_VAL_TYPE); 3367 return false; 3368 } 3369 return globalObj.val().get().toJSValue(cx, args.rval()); 3370 } 3371 3372 /* static */ 3373 bool WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp) { 3374 CallArgs args = CallArgsFromVp(argc, vp); 3375 return CallNonGenericMethod<IsGlobal, valueGetterImpl>(cx, args); 3376 } 3377 3378 /* static */ 3379 bool WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args) { 3380 if (!args.requireAtLeast(cx, "WebAssembly.Global setter", 1)) { 3381 return false; 3382 } 3383 3384 Rooted<WasmGlobalObject*> global( 3385 cx, &args.thisv().toObject().as<WasmGlobalObject>()); 3386 if (!global->isMutable()) { 3387 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3388 JSMSG_WASM_GLOBAL_IMMUTABLE); 3389 return false; 3390 } 3391 3392 RootedVal val(cx); 3393 if (!Val::fromJSValue(cx, global->type(), args.get(0), &val)) { 3394 return false; 3395 } 3396 global->setVal(val); 3397 3398 args.rval().setUndefined(); 3399 return true; 3400 } 3401 3402 /* static */ 3403 bool WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp) { 3404 CallArgs args = CallArgsFromVp(argc, vp); 3405 return CallNonGenericMethod<IsGlobal, valueSetterImpl>(cx, args); 3406 } 3407 3408 const JSPropertySpec WasmGlobalObject::properties[] = { 3409 JS_PSGS("value", WasmGlobalObject::valueGetter, 3410 WasmGlobalObject::valueSetter, JSPROP_ENUMERATE), 3411 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Global", JSPROP_READONLY), 3412 JS_PS_END, 3413 }; 3414 3415 const JSFunctionSpec WasmGlobalObject::methods[] = { 3416 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 3417 JS_FN("type", WasmGlobalObject::type, 0, JSPROP_ENUMERATE), 3418 #endif 3419 JS_FN("valueOf", WasmGlobalObject::valueGetter, 0, JSPROP_ENUMERATE), 3420 JS_FS_END, 3421 }; 3422 3423 const JSFunctionSpec WasmGlobalObject::static_methods[] = { 3424 JS_FS_END, 3425 }; 3426 3427 bool WasmGlobalObject::isMutable() const { 3428 return getReservedSlot(MUTABLE_SLOT).toBoolean(); 3429 } 3430 3431 ValType WasmGlobalObject::type() const { return val().get().type(); } 3432 3433 GCPtrVal& WasmGlobalObject::mutableVal() { 3434 return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate()); 3435 } 3436 3437 const GCPtrVal& WasmGlobalObject::val() const { 3438 return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate()); 3439 } 3440 3441 void WasmGlobalObject::setVal(wasm::HandleVal value) { 3442 MOZ_ASSERT(type() == value.get().type()); 3443 mutableVal() = value; 3444 } 3445 3446 void* WasmGlobalObject::addressOfCell() const { 3447 return (void*)&val().get().cell(); 3448 } 3449 3450 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 3451 /* static */ 3452 bool WasmGlobalObject::typeImpl(JSContext* cx, const CallArgs& args) { 3453 Rooted<WasmGlobalObject*> global( 3454 cx, &args.thisv().toObject().as<WasmGlobalObject>()); 3455 RootedObject typeObj( 3456 cx, GlobalTypeToObject(cx, global->type(), global->isMutable())); 3457 if (!typeObj) { 3458 return false; 3459 } 3460 args.rval().setObject(*typeObj); 3461 return true; 3462 } 3463 3464 /* static */ 3465 bool WasmGlobalObject::type(JSContext* cx, unsigned argc, Value* vp) { 3466 CallArgs args = CallArgsFromVp(argc, vp); 3467 return CallNonGenericMethod<IsGlobal, typeImpl>(cx, args); 3468 } 3469 #endif 3470 3471 // ============================================================================ 3472 // WebAssembly.Tag class and methods 3473 3474 const JSClassOps WasmTagObject::classOps_ = { 3475 nullptr, // addProperty 3476 nullptr, // delProperty 3477 nullptr, // enumerate 3478 nullptr, // newEnumerate 3479 nullptr, // resolve 3480 nullptr, // mayResolve 3481 WasmTagObject::finalize, // finalize 3482 nullptr, // call 3483 nullptr, // construct 3484 nullptr, // trace 3485 }; 3486 3487 const JSClass WasmTagObject::class_ = { 3488 "WebAssembly.Tag", 3489 JSCLASS_HAS_RESERVED_SLOTS(WasmTagObject::RESERVED_SLOTS) | 3490 JSCLASS_FOREGROUND_FINALIZE, 3491 &WasmTagObject::classOps_, 3492 &WasmTagObject::classSpec_, 3493 }; 3494 3495 const JSClass& WasmTagObject::protoClass_ = PlainObject::class_; 3496 3497 static constexpr char WasmTagName[] = "Tag"; 3498 3499 const ClassSpec WasmTagObject::classSpec_ = { 3500 CreateWasmConstructor<WasmTagObject, WasmTagName>, 3501 GenericCreatePrototype<WasmTagObject>, 3502 WasmTagObject::static_methods, 3503 nullptr, 3504 WasmTagObject::methods, 3505 WasmTagObject::properties, 3506 nullptr, 3507 ClassSpec::DontDefineConstructor, 3508 }; 3509 3510 /* static */ 3511 void WasmTagObject::finalize(JS::GCContext* gcx, JSObject* obj) { 3512 WasmTagObject& tagObj = obj->as<WasmTagObject>(); 3513 tagObj.tagType()->Release(); 3514 } 3515 3516 static bool IsTag(HandleValue v) { 3517 return v.isObject() && v.toObject().is<WasmTagObject>(); 3518 } 3519 3520 bool WasmTagObject::construct(JSContext* cx, unsigned argc, Value* vp) { 3521 CallArgs args = CallArgsFromVp(argc, vp); 3522 3523 if (!ThrowIfNotConstructing(cx, args, "Tag")) { 3524 return false; 3525 } 3526 3527 if (!args.requireAtLeast(cx, "WebAssembly.Tag", 1)) { 3528 return false; 3529 } 3530 3531 if (!args.get(0).isObject()) { 3532 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3533 JSMSG_WASM_BAD_DESC_ARG, "tag"); 3534 return false; 3535 } 3536 3537 RootedObject obj(cx, &args[0].toObject()); 3538 RootedValue paramsVal(cx); 3539 if (!JS_GetProperty(cx, obj, "parameters", ¶msVal)) { 3540 return false; 3541 } 3542 3543 ValTypeVector params; 3544 if (!ParseValTypes(cx, paramsVal, params)) { 3545 return false; 3546 } 3547 if (params.length() > MaxParams) { 3548 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3549 JSMSG_WASM_BAD_EXN_TAG_PARAMS); 3550 return false; 3551 } 3552 3553 RefPtr<TypeContext> types = js_new<TypeContext>(); 3554 if (!types) { 3555 ReportOutOfMemory(cx); 3556 return false; 3557 } 3558 const TypeDef* tagTypeDef = 3559 types->addType(FuncType(std::move(params), ValTypeVector())); 3560 if (!tagTypeDef) { 3561 ReportOutOfMemory(cx); 3562 return false; 3563 } 3564 3565 wasm::MutableTagType tagType = js_new<wasm::TagType>(); 3566 if (!tagType || !tagType->initialize(tagTypeDef)) { 3567 ReportOutOfMemory(cx); 3568 return false; 3569 } 3570 3571 RootedObject proto(cx, 3572 GetWasmConstructorPrototype(cx, args, JSProto_WasmTag)); 3573 if (!proto) { 3574 ReportOutOfMemory(cx); 3575 return false; 3576 } 3577 3578 Rooted<WasmTagObject*> tagObj(cx, WasmTagObject::create(cx, tagType, proto)); 3579 if (!tagObj) { 3580 return false; 3581 } 3582 3583 args.rval().setObject(*tagObj); 3584 return true; 3585 } 3586 3587 /* static */ 3588 WasmTagObject* WasmTagObject::create(JSContext* cx, 3589 const wasm::SharedTagType& tagType, 3590 HandleObject proto) { 3591 Rooted<WasmTagObject*> obj(cx, 3592 NewObjectWithGivenProto<WasmTagObject>(cx, proto)); 3593 if (!obj) { 3594 return nullptr; 3595 } 3596 3597 tagType.get()->AddRef(); 3598 obj->initReservedSlot(TYPE_SLOT, PrivateValue((void*)tagType.get())); 3599 3600 return obj; 3601 } 3602 3603 const JSPropertySpec WasmTagObject::properties[] = { 3604 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Tag", JSPROP_READONLY), 3605 JS_PS_END, 3606 }; 3607 3608 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 3609 /* static */ 3610 bool WasmTagObject::typeImpl(JSContext* cx, const CallArgs& args) { 3611 Rooted<WasmTagObject*> tag(cx, &args.thisv().toObject().as<WasmTagObject>()); 3612 RootedObject typeObj(cx, TagTypeToObject(cx, tag->valueTypes())); 3613 if (!typeObj) { 3614 return false; 3615 } 3616 args.rval().setObject(*typeObj); 3617 return true; 3618 } 3619 3620 /* static */ 3621 bool WasmTagObject::type(JSContext* cx, unsigned argc, Value* vp) { 3622 CallArgs args = CallArgsFromVp(argc, vp); 3623 return CallNonGenericMethod<IsTag, typeImpl>(cx, args); 3624 } 3625 #endif 3626 3627 const JSFunctionSpec WasmTagObject::methods[] = { 3628 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 3629 JS_FN("type", WasmTagObject::type, 0, JSPROP_ENUMERATE), 3630 #endif 3631 JS_FS_END, 3632 }; 3633 3634 const JSFunctionSpec WasmTagObject::static_methods[] = { 3635 JS_FS_END, 3636 }; 3637 3638 const TagType* WasmTagObject::tagType() const { 3639 return (const TagType*)getFixedSlot(TYPE_SLOT).toPrivate(); 3640 }; 3641 3642 const wasm::ValTypeVector& WasmTagObject::valueTypes() const { 3643 return tagType()->argTypes(); 3644 }; 3645 3646 // ============================================================================ 3647 // WebAssembly.Exception class and methods 3648 3649 const JSClassOps WasmExceptionObject::classOps_ = { 3650 nullptr, // addProperty 3651 nullptr, // delProperty 3652 nullptr, // enumerate 3653 nullptr, // newEnumerate 3654 nullptr, // resolve 3655 nullptr, // mayResolve 3656 WasmExceptionObject::finalize, // finalize 3657 nullptr, // call 3658 nullptr, // construct 3659 WasmExceptionObject::trace, // trace 3660 }; 3661 3662 const JSClass WasmExceptionObject::class_ = { 3663 "WebAssembly.Exception", 3664 JSCLASS_HAS_RESERVED_SLOTS(WasmExceptionObject::RESERVED_SLOTS) | 3665 JSCLASS_FOREGROUND_FINALIZE, 3666 &WasmExceptionObject::classOps_, 3667 &WasmExceptionObject::classSpec_, 3668 }; 3669 3670 const JSClass& WasmExceptionObject::protoClass_ = PlainObject::class_; 3671 3672 static constexpr char WasmExceptionName[] = "Exception"; 3673 3674 const ClassSpec WasmExceptionObject::classSpec_ = { 3675 CreateWasmConstructor<WasmExceptionObject, WasmExceptionName>, 3676 GenericCreatePrototype<WasmExceptionObject>, 3677 WasmExceptionObject::static_methods, 3678 nullptr, 3679 WasmExceptionObject::methods, 3680 WasmExceptionObject::properties, 3681 nullptr, 3682 ClassSpec::DontDefineConstructor, 3683 }; 3684 3685 /* static */ 3686 void WasmExceptionObject::finalize(JS::GCContext* gcx, JSObject* obj) { 3687 WasmExceptionObject& exnObj = obj->as<WasmExceptionObject>(); 3688 if (exnObj.isNewborn()) { 3689 return; 3690 } 3691 gcx->free_(obj, exnObj.typedMem(), exnObj.tagType()->tagSize(), 3692 MemoryUse::WasmExceptionData); 3693 exnObj.tagType()->Release(); 3694 } 3695 3696 /* static */ 3697 void WasmExceptionObject::trace(JSTracer* trc, JSObject* obj) { 3698 WasmExceptionObject& exnObj = obj->as<WasmExceptionObject>(); 3699 if (exnObj.isNewborn()) { 3700 return; 3701 } 3702 3703 wasm::SharedTagType tag = exnObj.tagType(); 3704 const wasm::ValTypeVector& params = tag->argTypes(); 3705 const wasm::TagOffsetVector& offsets = tag->argOffsets(); 3706 uint8_t* typedMem = exnObj.typedMem(); 3707 for (size_t i = 0; i < params.length(); i++) { 3708 ValType paramType = params[i]; 3709 if (paramType.isRefRepr()) { 3710 GCPtr<wasm::AnyRef>* paramPtr = 3711 reinterpret_cast<GCPtr<AnyRef>*>(typedMem + offsets[i]); 3712 TraceNullableEdge(trc, paramPtr, "wasm exception param"); 3713 } 3714 } 3715 } 3716 3717 static bool IsException(HandleValue v) { 3718 return v.isObject() && v.toObject().is<WasmExceptionObject>(); 3719 } 3720 3721 struct ExceptionOptions { 3722 bool traceStack; 3723 3724 ExceptionOptions() : traceStack(false) {} 3725 3726 [[nodiscard]] bool init(JSContext* cx, HandleValue val); 3727 }; 3728 3729 bool ExceptionOptions::init(JSContext* cx, HandleValue val) { 3730 if (val.isNullOrUndefined()) { 3731 return true; 3732 } 3733 3734 if (!val.isObject()) { 3735 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3736 JSMSG_WASM_BAD_EXN_OPTIONS); 3737 return false; 3738 } 3739 RootedObject obj(cx, &val.toObject()); 3740 3741 // Get `traceStack` and coerce to boolean 3742 RootedValue traceStackVal(cx); 3743 if (!JS_GetProperty(cx, obj, "traceStack", &traceStackVal)) { 3744 return false; 3745 } 3746 traceStack = ToBoolean(traceStackVal); 3747 3748 return true; 3749 } 3750 3751 bool WasmExceptionObject::construct(JSContext* cx, unsigned argc, Value* vp) { 3752 CallArgs args = CallArgsFromVp(argc, vp); 3753 3754 if (!ThrowIfNotConstructing(cx, args, "Exception")) { 3755 return false; 3756 } 3757 3758 if (!args.requireAtLeast(cx, "WebAssembly.Exception", 2)) { 3759 return false; 3760 } 3761 3762 if (!IsTag(args[0])) { 3763 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3764 JSMSG_WASM_BAD_EXN_ARG); 3765 return false; 3766 } 3767 Rooted<WasmTagObject*> exnTag(cx, &args[0].toObject().as<WasmTagObject>()); 3768 3769 if (!args.get(1).isObject()) { 3770 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3771 JSMSG_WASM_BAD_EXN_PAYLOAD); 3772 return false; 3773 } 3774 3775 JS::ForOfIterator iterator(cx); 3776 if (!iterator.init(args.get(1), JS::ForOfIterator::ThrowOnNonIterable)) { 3777 return false; 3778 } 3779 3780 // Get the optional 'options' parameter 3781 ExceptionOptions options; 3782 if (!options.init(cx, args.get(2))) { 3783 return false; 3784 } 3785 3786 // Trace the stack if requested 3787 RootedObject stack(cx); 3788 bool captureStack = 3789 options.traceStack || JS::Prefs::wasm_exception_force_stack_trace(); 3790 if (captureStack && !CaptureStack(cx, &stack)) { 3791 ReportOutOfMemory(cx); 3792 return false; 3793 } 3794 3795 RootedObject proto( 3796 cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmException)); 3797 if (!proto) { 3798 ReportOutOfMemory(cx); 3799 return false; 3800 } 3801 3802 Rooted<WasmExceptionObject*> exnObj( 3803 cx, WasmExceptionObject::create(cx, exnTag, stack, proto)); 3804 if (!exnObj) { 3805 return false; 3806 } 3807 3808 wasm::SharedTagType tagType = exnObj->tagType(); 3809 const wasm::ValTypeVector& params = tagType->argTypes(); 3810 const wasm::TagOffsetVector& offsets = tagType->argOffsets(); 3811 3812 RootedValue nextArg(cx); 3813 for (size_t i = 0; i < params.length(); i++) { 3814 bool done; 3815 if (!iterator.next(&nextArg, &done)) { 3816 return false; 3817 } 3818 if (done) { 3819 UniqueChars expected(JS_smprintf("%zu", params.length())); 3820 UniqueChars got(JS_smprintf("%zu", i)); 3821 if (!expected || !got) { 3822 ReportOutOfMemory(cx); 3823 return false; 3824 } 3825 3826 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3827 JSMSG_WASM_BAD_EXN_PAYLOAD_LEN, expected.get(), 3828 got.get()); 3829 return false; 3830 } 3831 3832 if (!exnObj->initArg(cx, offsets[i], params[i], nextArg)) { 3833 return false; 3834 } 3835 } 3836 3837 args.rval().setObject(*exnObj); 3838 return true; 3839 } 3840 3841 /* static */ 3842 WasmExceptionObject* WasmExceptionObject::create(JSContext* cx, 3843 Handle<WasmTagObject*> tag, 3844 HandleObject stack, 3845 HandleObject proto) { 3846 Rooted<WasmExceptionObject*> obj( 3847 cx, NewObjectWithGivenProto<WasmExceptionObject>(cx, proto)); 3848 if (!obj) { 3849 return nullptr; 3850 } 3851 const TagType* tagType = tag->tagType(); 3852 3853 // Allocate the data buffer before initializing the object so that an OOM 3854 // does not result in a partially constructed object. 3855 uint8_t* data = (uint8_t*)js_calloc(tagType->tagSize()); 3856 if (!data) { 3857 ReportOutOfMemory(cx); 3858 return nullptr; 3859 } 3860 3861 MOZ_ASSERT(obj->isNewborn()); 3862 obj->initFixedSlot(TAG_SLOT, ObjectValue(*tag)); 3863 tagType->AddRef(); 3864 obj->initFixedSlot(TYPE_SLOT, PrivateValue((void*)tagType)); 3865 InitReservedSlot(obj, DATA_SLOT, data, tagType->tagSize(), 3866 MemoryUse::WasmExceptionData); 3867 obj->initFixedSlot(STACK_SLOT, ObjectOrNullValue(stack)); 3868 3869 MOZ_ASSERT(!obj->isNewborn()); 3870 3871 return obj; 3872 } 3873 3874 WasmExceptionObject* WasmExceptionObject::wrapJSValue(JSContext* cx, 3875 HandleValue value) { 3876 Rooted<WasmNamespaceObject*> wasm(cx, WasmNamespaceObject::getOrCreate(cx)); 3877 if (!wasm) { 3878 return nullptr; 3879 } 3880 3881 Rooted<AnyRef> valueAnyRef(cx); 3882 if (!AnyRef::fromJSValue(cx, value, &valueAnyRef)) { 3883 return nullptr; 3884 } 3885 3886 Rooted<WasmTagObject*> wrappedJSValueTag(cx, wasm->wrappedJSValueTag()); 3887 WasmExceptionObject* exn = 3888 WasmExceptionObject::create(cx, wrappedJSValueTag, nullptr, nullptr); 3889 if (!exn) { 3890 return nullptr; 3891 } 3892 MOZ_ASSERT(exn->isWrappedJSValue()); 3893 3894 exn->initRefArg(WrappedJSValueTagType_ValueOffset, valueAnyRef); 3895 return exn; 3896 } 3897 3898 bool WasmExceptionObject::isNewborn() const { 3899 MOZ_ASSERT(is<WasmExceptionObject>()); 3900 return getReservedSlot(DATA_SLOT).isUndefined(); 3901 } 3902 3903 bool WasmExceptionObject::isWrappedJSValue() const { 3904 return tagType() == sWrappedJSValueTagType; 3905 } 3906 3907 Value WasmExceptionObject::wrappedJSValue() const { 3908 MOZ_ASSERT(isWrappedJSValue()); 3909 return loadRefArg(WrappedJSValueTagType_ValueOffset).toJSValue(); 3910 } 3911 3912 const JSPropertySpec WasmExceptionObject::properties[] = { 3913 JS_PSG("stack", WasmExceptionObject::getStack, 0), 3914 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Exception", JSPROP_READONLY), 3915 JS_PS_END, 3916 }; 3917 3918 /* static */ 3919 bool WasmExceptionObject::isImpl(JSContext* cx, const CallArgs& args) { 3920 Rooted<WasmExceptionObject*> exnObj( 3921 cx, &args.thisv().toObject().as<WasmExceptionObject>()); 3922 3923 if (!args.requireAtLeast(cx, "WebAssembly.Exception.is", 1)) { 3924 return false; 3925 } 3926 3927 if (!IsTag(args[0])) { 3928 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3929 JSMSG_WASM_BAD_EXN_ARG); 3930 return false; 3931 } 3932 3933 Rooted<WasmTagObject*> exnTag(cx, 3934 &args.get(0).toObject().as<WasmTagObject>()); 3935 args.rval().setBoolean(exnTag.get() == &exnObj->tag()); 3936 3937 return true; 3938 } 3939 3940 /* static */ 3941 bool WasmExceptionObject::isMethod(JSContext* cx, unsigned argc, Value* vp) { 3942 CallArgs args = CallArgsFromVp(argc, vp); 3943 return CallNonGenericMethod<IsException, isImpl>(cx, args); 3944 } 3945 3946 /* static */ 3947 bool WasmExceptionObject::getArgImpl(JSContext* cx, const CallArgs& args) { 3948 Rooted<WasmExceptionObject*> exnObj( 3949 cx, &args.thisv().toObject().as<WasmExceptionObject>()); 3950 3951 if (!args.requireAtLeast(cx, "WebAssembly.Exception.getArg", 2)) { 3952 return false; 3953 } 3954 3955 if (!IsTag(args[0])) { 3956 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3957 JSMSG_WASM_BAD_EXN_ARG); 3958 return false; 3959 } 3960 3961 Rooted<WasmTagObject*> exnTag(cx, 3962 &args.get(0).toObject().as<WasmTagObject>()); 3963 if (exnTag.get() != &exnObj->tag()) { 3964 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 3965 JSMSG_WASM_BAD_EXN_TAG); 3966 return false; 3967 } 3968 3969 uint32_t index; 3970 if (!EnforceRangeU32(cx, args.get(1), "Exception", "getArg index", &index)) { 3971 return false; 3972 } 3973 3974 const wasm::ValTypeVector& params = exnTag->valueTypes(); 3975 if (index >= params.length()) { 3976 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE, 3977 "Exception", "getArg index"); 3978 return false; 3979 } 3980 3981 uint32_t offset = exnTag->tagType()->argOffsets()[index]; 3982 RootedValue result(cx); 3983 if (!exnObj->loadArg(cx, offset, params[index], &result)) { 3984 return false; 3985 } 3986 args.rval().set(result); 3987 return true; 3988 } 3989 3990 /* static */ 3991 bool WasmExceptionObject::getArg(JSContext* cx, unsigned argc, Value* vp) { 3992 CallArgs args = CallArgsFromVp(argc, vp); 3993 return CallNonGenericMethod<IsException, getArgImpl>(cx, args); 3994 } 3995 3996 /* static */ 3997 bool WasmExceptionObject::getStack_impl(JSContext* cx, const CallArgs& args) { 3998 Rooted<WasmExceptionObject*> exnObj( 3999 cx, &args.thisv().toObject().as<WasmExceptionObject>()); 4000 RootedObject savedFrameObj(cx, exnObj->stack()); 4001 if (!savedFrameObj) { 4002 args.rval().setUndefined(); 4003 return true; 4004 } 4005 JSPrincipals* principals = exnObj->realm()->principals(); 4006 RootedString stackString(cx); 4007 if (!BuildStackString(cx, principals, savedFrameObj, &stackString)) { 4008 return false; 4009 } 4010 args.rval().setString(stackString); 4011 return true; 4012 } 4013 4014 /* static */ 4015 bool WasmExceptionObject::getStack(JSContext* cx, unsigned argc, Value* vp) { 4016 CallArgs args = CallArgsFromVp(argc, vp); 4017 return CallNonGenericMethod<IsException, getStack_impl>(cx, args); 4018 } 4019 4020 JSObject* WasmExceptionObject::stack() const { 4021 return getReservedSlot(STACK_SLOT).toObjectOrNull(); 4022 } 4023 4024 uint8_t* WasmExceptionObject::typedMem() const { 4025 return (uint8_t*)getReservedSlot(DATA_SLOT).toPrivate(); 4026 } 4027 4028 bool WasmExceptionObject::loadArg(JSContext* cx, size_t offset, 4029 wasm::ValType type, 4030 MutableHandleValue vp) const { 4031 if (!type.isExposable()) { 4032 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4033 JSMSG_WASM_BAD_VAL_TYPE); 4034 return false; 4035 } 4036 return ToJSValue(cx, typedMem() + offset, type, vp); 4037 } 4038 4039 bool WasmExceptionObject::initArg(JSContext* cx, size_t offset, 4040 wasm::ValType type, HandleValue value) { 4041 if (!type.isExposable()) { 4042 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4043 JSMSG_WASM_BAD_VAL_TYPE); 4044 return false; 4045 } 4046 4047 // Avoid rooting hazard of `this` being live across `fromJSValue` 4048 // which may GC. 4049 uint8_t* dest = typedMem() + offset; 4050 RootedVal val(cx); 4051 if (!Val::fromJSValue(cx, type, value, &val)) { 4052 return false; 4053 } 4054 val.get().writeToHeapLocation(dest); 4055 return true; 4056 } 4057 4058 void WasmExceptionObject::initRefArg(size_t offset, wasm::AnyRef ref) { 4059 uint8_t* dest = typedMem() + offset; 4060 *((GCPtr<AnyRef>*)dest) = ref; 4061 } 4062 4063 wasm::AnyRef WasmExceptionObject::loadRefArg(size_t offset) const { 4064 uint8_t* src = typedMem() + offset; 4065 return *((GCPtr<AnyRef>*)src); 4066 } 4067 4068 const JSFunctionSpec WasmExceptionObject::methods[] = { 4069 JS_FN("is", WasmExceptionObject::isMethod, 1, JSPROP_ENUMERATE), 4070 JS_FN("getArg", WasmExceptionObject::getArg, 2, JSPROP_ENUMERATE), 4071 JS_FS_END, 4072 }; 4073 4074 const JSFunctionSpec WasmExceptionObject::static_methods[] = { 4075 JS_FS_END, 4076 }; 4077 4078 const TagType* WasmExceptionObject::tagType() const { 4079 return (const TagType*)getReservedSlot(TYPE_SLOT).toPrivate(); 4080 } 4081 4082 WasmTagObject& WasmExceptionObject::tag() const { 4083 return getReservedSlot(TAG_SLOT).toObject().as<WasmTagObject>(); 4084 } 4085 4086 // ============================================================================ 4087 // WebAssembly.Function and methods 4088 #if defined(ENABLE_WASM_TYPE_REFLECTIONS) || defined(ENABLE_WASM_JSPI) 4089 [[nodiscard]] static bool IsWasmFunction(HandleValue v) { 4090 if (!v.isObject()) { 4091 return false; 4092 } 4093 if (!v.toObject().is<JSFunction>()) { 4094 return false; 4095 } 4096 return v.toObject().as<JSFunction>().isWasm(); 4097 } 4098 #endif // ENABLE_WASM_TYPE_REFLECTIONS || ENABLE_WASM_JSPI 4099 4100 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 4101 static JSObject* CreateWasmFunctionPrototype(JSContext* cx, JSProtoKey key) { 4102 // WasmFunction's prototype should inherit from JSFunction's prototype. 4103 RootedObject jsProto(cx, &cx->global()->getFunctionPrototype()); 4104 return GlobalObject::createBlankPrototypeInheriting(cx, &PlainObject::class_, 4105 jsProto); 4106 } 4107 4108 bool WasmFunctionTypeImpl(JSContext* cx, const CallArgs& args) { 4109 RootedFunction function(cx, &args.thisv().toObject().as<JSFunction>()); 4110 const FuncType& funcType = function->wasmTypeDef()->funcType(); 4111 RootedObject typeObj(cx, FuncTypeToObject(cx, funcType)); 4112 if (!typeObj) { 4113 return false; 4114 } 4115 args.rval().setObject(*typeObj); 4116 return true; 4117 } 4118 4119 bool WasmFunctionType(JSContext* cx, unsigned argc, Value* vp) { 4120 CallArgs args = CallArgsFromVp(argc, vp); 4121 return CallNonGenericMethod<IsWasmFunction, WasmFunctionTypeImpl>(cx, args); 4122 } 4123 4124 static JSFunction* WasmFunctionCreate(JSContext* cx, HandleObject func, 4125 wasm::ValTypeVector&& params, 4126 wasm::ValTypeVector&& results, 4127 HandleObject proto) { 4128 MOZ_ASSERT(IsCallableNonCCW(ObjectValue(*func))); 4129 4130 // We want to import the function to a wasm module and then export it again so 4131 // that it behaves exactly like a normal wasm function and can be used like 4132 // one in wasm tables. We synthesize such a module below, instantiate it, and 4133 // then return the exported function as the result. 4134 FeatureOptions options; 4135 ScriptedCaller scriptedCaller; 4136 SharedCompileArgs compileArgs = 4137 CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options); 4138 if (!compileArgs) { 4139 return nullptr; 4140 } 4141 4142 MutableModuleMetadata moduleMeta = js_new<ModuleMetadata>(); 4143 if (!moduleMeta || !moduleMeta->init(*compileArgs)) { 4144 return nullptr; 4145 } 4146 MutableCodeMetadata codeMeta = moduleMeta->codeMeta; 4147 CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized, 4148 DebugEnabled::False); 4149 compilerEnv.computeParameters(); 4150 4151 FuncType funcType = FuncType(std::move(params), std::move(results)); 4152 if (!codeMeta->types->addType(std::move(funcType))) { 4153 return nullptr; 4154 } 4155 4156 // Add an (import (func ...)) 4157 FuncDesc funcDesc = FuncDesc(0); 4158 if (!codeMeta->funcs.append(funcDesc)) { 4159 return nullptr; 4160 } 4161 codeMeta->numFuncImports = 1; 4162 codeMeta->funcImportsAreJS = true; 4163 4164 // Add an (export (func 0)) 4165 codeMeta->funcs[0].declareFuncExported(/* eager */ true, 4166 /* canRefFunc */ true); 4167 4168 // We will be looking up and using the function in the future by index so the 4169 // name doesn't matter. 4170 CacheableName fieldName; 4171 if (!moduleMeta->exports.emplaceBack(std::move(fieldName), 0, 4172 DefinitionKind::Function)) { 4173 return nullptr; 4174 } 4175 4176 if (!moduleMeta->prepareForCompile(compilerEnv.mode())) { 4177 return nullptr; 4178 } 4179 4180 ModuleGenerator mg(*codeMeta, compilerEnv, compilerEnv.initialState(), 4181 nullptr, nullptr, nullptr); 4182 if (!mg.initializeCompleteTier()) { 4183 return nullptr; 4184 } 4185 // We're not compiling any function definitions. 4186 if (!mg.finishFuncDefs()) { 4187 return nullptr; 4188 } 4189 SharedModule module = mg.finishModule(BytecodeBufferOrSource(), *moduleMeta, 4190 /*maybeCompleteTier2Listener=*/nullptr); 4191 if (!module) { 4192 return nullptr; 4193 } 4194 4195 // Instantiate the module. 4196 Rooted<ImportValues> imports(cx); 4197 if (!imports.get().funcs.append(func)) { 4198 return nullptr; 4199 } 4200 Rooted<WasmInstanceObject*> instance(cx); 4201 if (!module->instantiate(cx, imports.get(), nullptr, &instance)) { 4202 MOZ_ASSERT(cx->isThrowingOutOfMemory()); 4203 return nullptr; 4204 } 4205 4206 // Get the exported function which wraps the JS function to return. 4207 RootedFunction wasmFunc(cx); 4208 if (!instance->getExportedFunction(cx, instance, 0, &wasmFunc)) { 4209 return nullptr; 4210 } 4211 return wasmFunc; 4212 } 4213 4214 bool WasmFunctionConstruct(JSContext* cx, unsigned argc, Value* vp) { 4215 CallArgs args = CallArgsFromVp(argc, vp); 4216 4217 if (!ThrowIfNotConstructing(cx, args, "WebAssembly.Function")) { 4218 return false; 4219 } 4220 4221 if (!args.requireAtLeast(cx, "WebAssembly.Function", 2)) { 4222 return false; 4223 } 4224 4225 if (!args[0].isObject()) { 4226 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4227 JSMSG_WASM_BAD_DESC_ARG, "function"); 4228 return false; 4229 } 4230 RootedObject typeObj(cx, &args[0].toObject()); 4231 4232 // Extract properties in lexicographic order per spec. 4233 4234 RootedValue parametersVal(cx); 4235 if (!JS_GetProperty(cx, typeObj, "parameters", ¶metersVal)) { 4236 return false; 4237 } 4238 4239 ValTypeVector params; 4240 if (!ParseValTypes(cx, parametersVal, params)) { 4241 return false; 4242 } 4243 if (params.length() > MaxParams) { 4244 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4245 JSMSG_WASM_BAD_FUNCTION_TYPE, "parameters"); 4246 return false; 4247 } 4248 4249 RootedValue resultsVal(cx); 4250 if (!JS_GetProperty(cx, typeObj, "results", &resultsVal)) { 4251 return false; 4252 } 4253 4254 ValTypeVector results; 4255 if (!ParseValTypes(cx, resultsVal, results)) { 4256 return false; 4257 } 4258 if (results.length() > MaxResults) { 4259 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4260 JSMSG_WASM_BAD_FUNCTION_TYPE, "results"); 4261 return false; 4262 } 4263 4264 // Get the target function 4265 4266 if (!IsCallableNonCCW(args[1])) { 4267 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4268 JSMSG_WASM_BAD_FUNCTION_VALUE); 4269 return false; 4270 } 4271 RootedObject func(cx, &args[1].toObject()); 4272 4273 RootedObject proto( 4274 cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmFunction)); 4275 if (!proto) { 4276 ReportOutOfMemory(cx); 4277 return false; 4278 } 4279 4280 RootedFunction wasmFunc(cx, WasmFunctionCreate(cx, func, std::move(params), 4281 std::move(results), proto)); 4282 if (!wasmFunc) { 4283 ReportOutOfMemory(cx); 4284 return false; 4285 } 4286 args.rval().setObject(*wasmFunc); 4287 4288 return true; 4289 } 4290 4291 static constexpr char WasmFunctionName[] = "Function"; 4292 4293 static JSObject* CreateWasmFunctionConstructor(JSContext* cx, JSProtoKey key) { 4294 RootedObject proto(cx, &cx->global()->getFunctionConstructor()); 4295 4296 Rooted<JSAtom*> className( 4297 cx, Atomize(cx, WasmFunctionName, strlen(WasmFunctionName))); 4298 if (!className) { 4299 return nullptr; 4300 } 4301 return NewFunctionWithProto(cx, WasmFunctionConstruct, 1, 4302 FunctionFlags::NATIVE_CTOR, nullptr, className, 4303 proto, gc::AllocKind::FUNCTION, TenuredObject); 4304 } 4305 4306 const JSFunctionSpec WasmFunctionMethods[] = { 4307 JS_FN("type", WasmFunctionType, 0, 0), 4308 JS_FS_END, 4309 }; 4310 4311 const ClassSpec WasmFunctionClassSpec = { 4312 CreateWasmFunctionConstructor, 4313 CreateWasmFunctionPrototype, 4314 nullptr, 4315 nullptr, 4316 WasmFunctionMethods, 4317 nullptr, 4318 nullptr, 4319 ClassSpec::DontDefineConstructor, 4320 }; 4321 4322 const JSClass js::WasmFunctionClass = { 4323 "WebAssembly.Function", 4324 0, 4325 JS_NULL_CLASS_OPS, 4326 &WasmFunctionClassSpec, 4327 }; 4328 4329 #endif 4330 4331 // ============================================================================ 4332 // WebAssembly class and static methods 4333 4334 static bool WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp) { 4335 CallArgs args = CallArgsFromVp(argc, vp); 4336 args.rval().setString(cx->names().WebAssembly); 4337 return true; 4338 } 4339 4340 static bool RejectWithPendingException(JSContext* cx, 4341 Handle<PromiseObject*> promise) { 4342 if (!cx->isExceptionPending()) { 4343 return false; 4344 } 4345 4346 RootedValue rejectionValue(cx); 4347 if (!GetAndClearException(cx, &rejectionValue)) { 4348 return false; 4349 } 4350 4351 return PromiseObject::reject(cx, promise, rejectionValue); 4352 } 4353 4354 static bool Reject(JSContext* cx, const CompileArgs& args, 4355 Handle<PromiseObject*> promise, const UniqueChars& error) { 4356 if (!error) { 4357 ThrowCompileOutOfMemory(cx); 4358 return RejectWithPendingException(cx, promise); 4359 } 4360 4361 RootedObject stack(cx, promise->allocationSite()); 4362 RootedString fileName(cx); 4363 if (const char* filename = args.scriptedCaller.filename.get()) { 4364 fileName = 4365 JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(filename, strlen(filename))); 4366 } else { 4367 fileName = JS_GetEmptyString(cx); 4368 } 4369 if (!fileName) { 4370 return false; 4371 } 4372 4373 uint32_t line = args.scriptedCaller.line; 4374 4375 // Ideally we'd report a JSMSG_WASM_COMPILE_ERROR here, but there's no easy 4376 // way to create an ErrorObject for an arbitrary error code with multiple 4377 // replacements. 4378 UniqueChars str(JS_smprintf("wasm validation error: %s", error.get())); 4379 if (!str) { 4380 return false; 4381 } 4382 4383 size_t len = strlen(str.get()); 4384 RootedString message(cx, NewStringCopyN<CanGC>(cx, str.get(), len)); 4385 if (!message) { 4386 return false; 4387 } 4388 4389 // There's no error |cause| available here. 4390 auto cause = JS::NothingHandleValue; 4391 4392 RootedObject errorObj( 4393 cx, ErrorObject::create(cx, JSEXN_WASMCOMPILEERROR, stack, fileName, 0, 4394 line, JS::ColumnNumberOneOrigin(), nullptr, 4395 message, cause)); 4396 if (!errorObj) { 4397 return false; 4398 } 4399 4400 RootedValue rejectionValue(cx, ObjectValue(*errorObj)); 4401 return PromiseObject::reject(cx, promise, rejectionValue); 4402 } 4403 4404 static bool RejectWithOutOfMemory(JSContext* cx, 4405 Handle<PromiseObject*> promise) { 4406 ReportOutOfMemory(cx); 4407 return RejectWithPendingException(cx, promise); 4408 } 4409 4410 static void LogAsync(JSContext* cx, const char* funcName, 4411 const Module& module) { 4412 Log(cx, "async %s succeeded%s", funcName, 4413 module.loggingDeserialized() ? " (loaded from cache)" : ""); 4414 } 4415 4416 enum class Ret { Pair, Instance }; 4417 4418 class AsyncInstantiateTask : public OffThreadPromiseTask { 4419 SharedModule module_; 4420 PersistentRooted<ImportValues> imports_; 4421 Ret ret_; 4422 4423 public: 4424 AsyncInstantiateTask(JSContext* cx, const Module& module, Ret ret, 4425 Handle<PromiseObject*> promise) 4426 : OffThreadPromiseTask(cx, promise), 4427 module_(&module), 4428 imports_(cx), 4429 ret_(ret) {} 4430 4431 ImportValues& imports() { return imports_.get(); } 4432 4433 bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override { 4434 RootedObject instanceProto( 4435 cx, &cx->global()->getPrototype(JSProto_WasmInstance)); 4436 4437 Rooted<WasmInstanceObject*> instanceObj(cx); 4438 if (!module_->instantiate(cx, imports_.get(), instanceProto, 4439 &instanceObj)) { 4440 return RejectWithPendingException(cx, promise); 4441 } 4442 4443 RootedValue resolutionValue(cx); 4444 if (ret_ == Ret::Instance) { 4445 resolutionValue = ObjectValue(*instanceObj); 4446 } else { 4447 RootedObject resultObj(cx, JS_NewPlainObject(cx)); 4448 if (!resultObj) { 4449 return RejectWithPendingException(cx, promise); 4450 } 4451 4452 RootedObject moduleProto(cx, 4453 &cx->global()->getPrototype(JSProto_WasmModule)); 4454 RootedObject moduleObj( 4455 cx, WasmModuleObject::create(cx, *module_, moduleProto)); 4456 if (!moduleObj) { 4457 return RejectWithPendingException(cx, promise); 4458 } 4459 4460 RootedValue val(cx, ObjectValue(*moduleObj)); 4461 if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE)) { 4462 return RejectWithPendingException(cx, promise); 4463 } 4464 4465 val = ObjectValue(*instanceObj); 4466 if (!JS_DefineProperty(cx, resultObj, "instance", val, 4467 JSPROP_ENUMERATE)) { 4468 return RejectWithPendingException(cx, promise); 4469 } 4470 4471 resolutionValue = ObjectValue(*resultObj); 4472 } 4473 4474 if (!PromiseObject::resolve(cx, promise, resolutionValue)) { 4475 return RejectWithPendingException(cx, promise); 4476 } 4477 4478 LogAsync(cx, "instantiate", *module_); 4479 return true; 4480 } 4481 }; 4482 4483 static bool AsyncInstantiate(JSContext* cx, const Module& module, 4484 HandleObject importObj, Ret ret, 4485 Handle<PromiseObject*> promise) { 4486 auto task = js::MakeUnique<AsyncInstantiateTask>(cx, module, ret, promise); 4487 if (!task || !task->init(cx)) { 4488 return RejectWithOutOfMemory(cx, promise); 4489 } 4490 4491 if (!GetImports(cx, module, importObj, &task->imports())) { 4492 return RejectWithPendingException(cx, promise); 4493 } 4494 4495 OffThreadPromiseTask::DispatchResolveAndDestroy(std::move(task)); 4496 return true; 4497 } 4498 4499 static bool ResolveCompile(JSContext* cx, const Module& module, 4500 Handle<PromiseObject*> promise) { 4501 RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule)); 4502 RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto)); 4503 if (!moduleObj) { 4504 return RejectWithPendingException(cx, promise); 4505 } 4506 4507 RootedValue resolutionValue(cx, ObjectValue(*moduleObj)); 4508 if (!PromiseObject::resolve(cx, promise, resolutionValue)) { 4509 return RejectWithPendingException(cx, promise); 4510 } 4511 4512 LogAsync(cx, "compile", module); 4513 return true; 4514 } 4515 4516 struct CompileBufferTask : PromiseHelperTask { 4517 BytecodeBuffer bytecode; 4518 SharedCompileArgs compileArgs; 4519 UniqueChars error; 4520 UniqueCharsVector warnings; 4521 SharedModule module; 4522 bool instantiate; 4523 PersistentRootedObject importObj; 4524 4525 CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise, 4526 HandleObject importObj) 4527 : PromiseHelperTask(cx, promise), 4528 instantiate(true), 4529 importObj(cx, importObj) {} 4530 4531 CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise) 4532 : PromiseHelperTask(cx, promise), instantiate(false) {} 4533 4534 bool init(JSContext* cx, FeatureOptions options, const char* introducer) { 4535 compileArgs = InitCompileArgs(cx, options, introducer); 4536 if (!compileArgs) { 4537 return false; 4538 } 4539 return PromiseHelperTask::init(cx); 4540 } 4541 4542 void execute() override { 4543 module = CompileBuffer(*compileArgs, BytecodeBufferOrSource(bytecode), 4544 &error, &warnings, nullptr); 4545 } 4546 4547 bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override { 4548 if (!ReportCompileWarnings(cx, warnings)) { 4549 return false; 4550 } 4551 if (!module) { 4552 return Reject(cx, *compileArgs, promise, error); 4553 } 4554 if (instantiate) { 4555 return AsyncInstantiate(cx, *module, importObj, Ret::Pair, promise); 4556 } 4557 return ResolveCompile(cx, *module, promise); 4558 } 4559 }; 4560 4561 static bool RejectWithPendingException(JSContext* cx, 4562 Handle<PromiseObject*> promise, 4563 CallArgs& callArgs) { 4564 if (!RejectWithPendingException(cx, promise)) { 4565 return false; 4566 } 4567 4568 callArgs.rval().setObject(*promise); 4569 return true; 4570 } 4571 4572 static bool EnsurePromiseSupport(JSContext* cx) { 4573 if (!cx->runtime()->offThreadPromiseState.ref().initialized()) { 4574 JS_ReportErrorASCII( 4575 cx, "WebAssembly Promise APIs not supported in this runtime."); 4576 return false; 4577 } 4578 return true; 4579 } 4580 4581 static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) { 4582 if (!EnsurePromiseSupport(cx)) { 4583 return false; 4584 } 4585 4586 Log(cx, "async compile() started"); 4587 4588 Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx)); 4589 if (!promise) { 4590 return false; 4591 } 4592 4593 CallArgs callArgs = CallArgsFromVp(argc, vp); 4594 4595 JS::RootedVector<JSString*> parameterStrings(cx); 4596 JS::RootedVector<Value> parameterArgs(cx); 4597 bool canCompileStrings = false; 4598 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, 4599 JS::CompilationType::Undefined, 4600 parameterStrings, nullptr, parameterArgs, 4601 NullHandleValue, &canCompileStrings)) { 4602 return RejectWithPendingException(cx, promise, callArgs); 4603 } 4604 if (!canCompileStrings) { 4605 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 4606 JSMSG_CSP_BLOCKED_WASM, "WebAssembly.compile"); 4607 return RejectWithPendingException(cx, promise, callArgs); 4608 } 4609 4610 auto task = cx->make_unique<CompileBufferTask>(cx, promise); 4611 if (!task) { 4612 return false; 4613 } 4614 4615 if (!callArgs.requireAtLeast(cx, "WebAssembly.compile", 1)) { 4616 return RejectWithPendingException(cx, promise, callArgs); 4617 } 4618 4619 FeatureOptions options; 4620 if (!options.init(cx, callArgs.get(1))) { 4621 return RejectWithPendingException(cx, promise, callArgs); 4622 } 4623 4624 if (!callArgs[0].isObject()) { 4625 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4626 JSMSG_WASM_BAD_BUF_ARG); 4627 return RejectWithPendingException(cx, promise, callArgs); 4628 } 4629 4630 Rooted<JSObject*> sourceObj(cx, &callArgs[0].toObject()); 4631 if (!GetBytecodeBuffer(cx, sourceObj, JSMSG_WASM_BAD_BUF_ARG, 4632 &task->bytecode)) { 4633 return RejectWithPendingException(cx, promise, callArgs); 4634 } 4635 4636 if (!task->init(cx, options, "WebAssembly.compile")) { 4637 return false; 4638 } 4639 4640 if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) { 4641 return false; 4642 } 4643 4644 callArgs.rval().setObject(*promise); 4645 return true; 4646 } 4647 4648 static bool GetInstantiateArgs(JSContext* cx, const CallArgs& callArgs, 4649 MutableHandleObject firstArg, 4650 MutableHandleObject importObj, 4651 MutableHandleValue featureOptions) { 4652 if (!callArgs.requireAtLeast(cx, "WebAssembly.instantiate", 1)) { 4653 return false; 4654 } 4655 4656 if (!callArgs[0].isObject()) { 4657 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4658 JSMSG_WASM_BAD_BUF_MOD_ARG); 4659 return false; 4660 } 4661 4662 firstArg.set(&callArgs[0].toObject()); 4663 4664 if (!GetImportArg(cx, callArgs.get(1), importObj)) { 4665 return false; 4666 } 4667 4668 featureOptions.set(callArgs.get(2)); 4669 return true; 4670 } 4671 4672 static bool WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) { 4673 if (!EnsurePromiseSupport(cx)) { 4674 return false; 4675 } 4676 4677 Log(cx, "async instantiate() started"); 4678 4679 Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx)); 4680 if (!promise) { 4681 return false; 4682 } 4683 4684 CallArgs callArgs = CallArgsFromVp(argc, vp); 4685 4686 RootedObject firstArg(cx); 4687 RootedObject importObj(cx); 4688 RootedValue featureOptions(cx); 4689 if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj, 4690 &featureOptions)) { 4691 return RejectWithPendingException(cx, promise, callArgs); 4692 } 4693 4694 const Module* module; 4695 if (IsModuleObject(firstArg, &module)) { 4696 if (!AsyncInstantiate(cx, *module, importObj, Ret::Instance, promise)) { 4697 return false; 4698 } 4699 } else { 4700 JS::RootedVector<JSString*> parameterStrings(cx); 4701 JS::RootedVector<Value> parameterArgs(cx); 4702 bool canCompileStrings = false; 4703 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, 4704 JS::CompilationType::Undefined, 4705 parameterStrings, nullptr, parameterArgs, 4706 NullHandleValue, &canCompileStrings)) { 4707 return RejectWithPendingException(cx, promise, callArgs); 4708 } 4709 if (!canCompileStrings) { 4710 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 4711 JSMSG_CSP_BLOCKED_WASM, 4712 "WebAssembly.instantiate"); 4713 return RejectWithPendingException(cx, promise, callArgs); 4714 } 4715 4716 FeatureOptions options; 4717 if (!options.init(cx, featureOptions)) { 4718 return false; 4719 } 4720 4721 auto task = cx->make_unique<CompileBufferTask>(cx, promise, importObj); 4722 if (!task || !task->init(cx, options, "WebAssembly.instantiate")) { 4723 return false; 4724 } 4725 4726 if (!GetBytecodeBuffer(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG, 4727 &task->bytecode)) { 4728 return RejectWithPendingException(cx, promise, callArgs); 4729 } 4730 4731 if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) { 4732 return false; 4733 } 4734 } 4735 4736 callArgs.rval().setObject(*promise); 4737 return true; 4738 } 4739 4740 static bool WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp) { 4741 CallArgs callArgs = CallArgsFromVp(argc, vp); 4742 4743 if (!callArgs.requireAtLeast(cx, "WebAssembly.validate", 1)) { 4744 return false; 4745 } 4746 4747 FeatureOptions options; 4748 if (!options.init(cx, callArgs.get(1))) { 4749 return false; 4750 } 4751 4752 if (!callArgs[0].isObject()) { 4753 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 4754 JSMSG_WASM_BAD_BUF_ARG); 4755 return false; 4756 } 4757 4758 BytecodeSource source; 4759 Rooted<JSObject*> sourceObj(cx, &callArgs[0].toObject()); 4760 if (!GetBytecodeSource(cx, sourceObj, JSMSG_WASM_BAD_BUF_ARG, &source)) { 4761 return false; 4762 } 4763 4764 UniqueChars error; 4765 bool validated; 4766 { 4767 AutoPinBufferSourceLength pin(cx, sourceObj.get()); 4768 validated = Validate(cx, source, options, &error); 4769 } 4770 4771 // If the reason for validation failure was OOM (signalled by null error 4772 // message), report out-of-memory so that validate's return is always 4773 // correct. 4774 if (!validated && !error) { 4775 ReportOutOfMemory(cx); 4776 return false; 4777 } 4778 4779 if (error) { 4780 MOZ_ASSERT(!validated); 4781 Log(cx, "validate() failed with: %s", error.get()); 4782 } 4783 4784 callArgs.rval().setBoolean(validated); 4785 return true; 4786 } 4787 4788 static bool EnsureStreamSupport(JSContext* cx) { 4789 // This should match wasm::StreamingCompilationAvailable(). 4790 4791 if (!EnsurePromiseSupport(cx)) { 4792 return false; 4793 } 4794 4795 if (!CanUseExtraThreads()) { 4796 JS_ReportErrorASCII( 4797 cx, "WebAssembly.compileStreaming not supported with --no-threads"); 4798 return false; 4799 } 4800 4801 if (!cx->runtime()->consumeStreamCallback) { 4802 JS_ReportErrorASCII(cx, 4803 "WebAssembly streaming not supported in this runtime"); 4804 return false; 4805 } 4806 4807 return true; 4808 } 4809 4810 // This value is chosen and asserted to be disjoint from any host error code. 4811 static const size_t StreamOOMCode = 0; 4812 4813 static bool RejectWithStreamErrorNumber(JSContext* cx, size_t errorCode, 4814 Handle<PromiseObject*> promise) { 4815 if (errorCode == StreamOOMCode) { 4816 ReportOutOfMemory(cx); 4817 return false; 4818 } 4819 4820 cx->runtime()->reportStreamErrorCallback(cx, errorCode); 4821 return RejectWithPendingException(cx, promise); 4822 } 4823 4824 class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer { 4825 // The stream progresses monotonically through these states; the helper 4826 // thread wait()s for streamState_ to reach Closed. 4827 enum StreamState { Env, Code, Tail, Closed }; 4828 ExclusiveWaitableData<StreamState> streamState_; 4829 4830 // Immutable: 4831 const bool instantiate_; 4832 const PersistentRootedObject importObj_; 4833 4834 // Immutable after noteResponseURLs() which is called at most once before 4835 // first call on stream thread: 4836 const MutableCompileArgs compileArgs_; 4837 4838 // Immutable after Env state: 4839 MutableBytes envBytes_; 4840 BytecodeRange codeSection_; 4841 4842 // The code section vector is resized once during the Env state and filled 4843 // in chunk by chunk during the Code state, updating the end-pointer after 4844 // each chunk: 4845 MutableBytes codeBytes_; 4846 uint8_t* codeBytesEnd_; 4847 ExclusiveBytesPtr exclusiveCodeBytesEnd_; 4848 4849 // Immutable after Tail state: 4850 MutableBytes tailBytes_; 4851 ExclusiveStreamEndData exclusiveStreamEnd_; 4852 4853 // Written once before Closed state and read in Closed state on main thread: 4854 SharedModule module_; 4855 Maybe<size_t> streamError_; 4856 UniqueChars compileError_; 4857 UniqueCharsVector warnings_; 4858 4859 // Set on stream thread and read racily on helper thread to abort compilation: 4860 mozilla::Atomic<bool> streamFailed_; 4861 4862 // Called on some thread before consumeChunk(), streamEnd(), streamError()): 4863 4864 void noteResponseURLs(const char* url, const char* sourceMapUrl) override { 4865 if (url) { 4866 compileArgs_->scriptedCaller.filename = DuplicateString(url); 4867 compileArgs_->scriptedCaller.filenameIsURL = true; 4868 } 4869 if (sourceMapUrl) { 4870 compileArgs_->sourceMapURL = DuplicateString(sourceMapUrl); 4871 } 4872 } 4873 4874 // Called on a stream thread: 4875 4876 // Until StartOffThreadPromiseHelperTask succeeds, we are responsible for 4877 // dispatching ourselves back to the JS thread. 4878 // 4879 // Warning: After this function returns, 'this' can be deleted at any time, so 4880 // the caller must immediately return from the stream callback. 4881 void setClosedAndDestroyBeforeHelperThreadStarted() { 4882 streamState_.lock().get() = Closed; 4883 dispatchResolveAndDestroy(); 4884 } 4885 4886 // See setClosedAndDestroyBeforeHelperThreadStarted() comment. 4887 bool rejectAndDestroyBeforeHelperThreadStarted(size_t errorNumber) { 4888 MOZ_ASSERT(streamState_.lock() == Env); 4889 MOZ_ASSERT(!streamError_); 4890 streamError_ = Some(errorNumber); 4891 setClosedAndDestroyBeforeHelperThreadStarted(); 4892 return false; 4893 } 4894 4895 // Once StartOffThreadPromiseHelperTask succeeds, the helper thread will 4896 // dispatchResolveAndDestroy() after execute() returns, but execute() 4897 // wait()s for state to be Closed. 4898 // 4899 // Warning: After this function returns, 'this' can be deleted at any time, so 4900 // the caller must immediately return from the stream callback. 4901 void setClosedAndDestroyAfterHelperThreadStarted() { 4902 auto streamState = streamState_.lock(); 4903 MOZ_ASSERT(streamState != Closed); 4904 streamState.get() = Closed; 4905 streamState.notify_one(/* stream closed */); 4906 } 4907 4908 // See setClosedAndDestroyAfterHelperThreadStarted() comment. 4909 bool rejectAndDestroyAfterHelperThreadStarted(size_t errorNumber) { 4910 MOZ_ASSERT(!streamError_); 4911 streamError_ = Some(errorNumber); 4912 streamFailed_ = true; 4913 exclusiveCodeBytesEnd_.lock().notify_one(); 4914 exclusiveStreamEnd_.lock().notify_one(); 4915 setClosedAndDestroyAfterHelperThreadStarted(); 4916 return false; 4917 } 4918 4919 bool consumeChunk(const uint8_t* begin, size_t length) override { 4920 switch (streamState_.lock().get()) { 4921 case Env: { 4922 if (!envBytes_->append(begin, length)) { 4923 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode); 4924 } 4925 4926 if (!StartsCodeSection(envBytes_->begin(), envBytes_->end(), 4927 &codeSection_)) { 4928 return true; 4929 } 4930 4931 uint32_t extraBytes = envBytes_->length() - codeSection_.start; 4932 if (extraBytes) { 4933 envBytes_->shrinkTo(codeSection_.start); 4934 } 4935 4936 if (codeSection_.size() > MaxCodeSectionBytes) { 4937 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode); 4938 } 4939 4940 if (!codeBytes_->vector.resize(codeSection_.size())) { 4941 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode); 4942 } 4943 4944 codeBytesEnd_ = codeBytes_->begin(); 4945 exclusiveCodeBytesEnd_.lock().get() = codeBytesEnd_; 4946 4947 if (!StartOffThreadPromiseHelperTask(this)) { 4948 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode); 4949 } 4950 4951 // Set the state to Code iff StartOffThreadPromiseHelperTask() 4952 // succeeds so that the state tells us whether we are before or 4953 // after the helper thread started. 4954 streamState_.lock().get() = Code; 4955 4956 if (extraBytes) { 4957 return consumeChunk(begin + length - extraBytes, extraBytes); 4958 } 4959 4960 return true; 4961 } 4962 case Code: { 4963 size_t copyLength = 4964 std::min<size_t>(length, codeBytes_->end() - codeBytesEnd_); 4965 memcpy(codeBytesEnd_, begin, copyLength); 4966 codeBytesEnd_ += copyLength; 4967 4968 { 4969 auto codeStreamEnd = exclusiveCodeBytesEnd_.lock(); 4970 codeStreamEnd.get() = codeBytesEnd_; 4971 codeStreamEnd.notify_one(); 4972 } 4973 4974 if (codeBytesEnd_ != codeBytes_->end()) { 4975 return true; 4976 } 4977 4978 streamState_.lock().get() = Tail; 4979 4980 if (uint32_t extraBytes = length - copyLength) { 4981 return consumeChunk(begin + copyLength, extraBytes); 4982 } 4983 4984 return true; 4985 } 4986 case Tail: { 4987 if (!tailBytes_->append(begin, length)) { 4988 return rejectAndDestroyAfterHelperThreadStarted(StreamOOMCode); 4989 } 4990 4991 return true; 4992 } 4993 case Closed: 4994 MOZ_CRASH("consumeChunk() in Closed state"); 4995 } 4996 MOZ_CRASH("unreachable"); 4997 } 4998 4999 void streamEnd( 5000 JS::OptimizedEncodingListener* completeTier2Listener) override { 5001 switch (streamState_.lock().get()) { 5002 case Env: { 5003 BytecodeBuffer bytecode(envBytes_, nullptr, nullptr); 5004 module_ = CompileBuffer(*compileArgs_, BytecodeBufferOrSource(bytecode), 5005 &compileError_, &warnings_, nullptr); 5006 setClosedAndDestroyBeforeHelperThreadStarted(); 5007 return; 5008 } 5009 case Code: 5010 case Tail: 5011 // Unlock exclusiveStreamEnd_ before locking streamState_. 5012 { 5013 auto streamEnd = exclusiveStreamEnd_.lock(); 5014 MOZ_ASSERT(!streamEnd->reached); 5015 streamEnd->reached = true; 5016 streamEnd->tailBytes = tailBytes_; 5017 streamEnd->completeTier2Listener = completeTier2Listener; 5018 streamEnd.notify_one(); 5019 } 5020 setClosedAndDestroyAfterHelperThreadStarted(); 5021 return; 5022 case Closed: 5023 MOZ_CRASH("streamEnd() in Closed state"); 5024 } 5025 } 5026 5027 void streamError(size_t errorCode) override { 5028 MOZ_ASSERT(errorCode != StreamOOMCode); 5029 switch (streamState_.lock().get()) { 5030 case Env: 5031 rejectAndDestroyBeforeHelperThreadStarted(errorCode); 5032 return; 5033 case Tail: 5034 case Code: 5035 rejectAndDestroyAfterHelperThreadStarted(errorCode); 5036 return; 5037 case Closed: 5038 MOZ_CRASH("streamError() in Closed state"); 5039 } 5040 } 5041 5042 void consumeOptimizedEncoding(const uint8_t* begin, size_t length) override { 5043 module_ = Module::deserialize(begin, length); 5044 5045 MOZ_ASSERT(streamState_.lock().get() == Env); 5046 setClosedAndDestroyBeforeHelperThreadStarted(); 5047 } 5048 5049 // Called on a helper thread: 5050 5051 void execute() override { 5052 module_ = CompileStreaming(*compileArgs_, *envBytes_, *codeBytes_, 5053 exclusiveCodeBytesEnd_, exclusiveStreamEnd_, 5054 streamFailed_, &compileError_, &warnings_); 5055 5056 // When execute() returns, the CompileStreamTask will be dispatched 5057 // back to its JS thread to call resolve() and then be destroyed. We 5058 // can't let this happen until the stream has been closed lest 5059 // consumeChunk() or streamEnd() be called on a dead object. 5060 auto streamState = streamState_.lock(); 5061 while (streamState != Closed) { 5062 streamState.wait(/* stream closed */); 5063 } 5064 } 5065 5066 // Called on a JS thread after streaming compilation completes/errors: 5067 5068 bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override { 5069 MOZ_ASSERT(streamState_.lock() == Closed); 5070 5071 if (!ReportCompileWarnings(cx, warnings_)) { 5072 return false; 5073 } 5074 if (module_) { 5075 MOZ_ASSERT(!streamFailed_ && !streamError_ && !compileError_); 5076 if (instantiate_) { 5077 return AsyncInstantiate(cx, *module_, importObj_, Ret::Pair, promise); 5078 } 5079 return ResolveCompile(cx, *module_, promise); 5080 } 5081 5082 if (streamError_) { 5083 return RejectWithStreamErrorNumber(cx, *streamError_, promise); 5084 } 5085 5086 return Reject(cx, *compileArgs_, promise, compileError_); 5087 } 5088 5089 public: 5090 CompileStreamTask(JSContext* cx, Handle<PromiseObject*> promise, 5091 CompileArgs& compileArgs, bool instantiate, 5092 HandleObject importObj) 5093 : PromiseHelperTask(cx, promise), 5094 streamState_(mutexid::WasmStreamStatus, Env), 5095 instantiate_(instantiate), 5096 importObj_(cx, importObj), 5097 compileArgs_(&compileArgs), 5098 codeSection_{}, 5099 codeBytesEnd_(nullptr), 5100 exclusiveCodeBytesEnd_(mutexid::WasmCodeBytesEnd, nullptr), 5101 exclusiveStreamEnd_(mutexid::WasmStreamEnd), 5102 streamFailed_(false) { 5103 MOZ_ASSERT_IF(importObj_, instantiate_); 5104 } 5105 5106 [[nodiscard]] bool init(JSContext* cx) { 5107 envBytes_ = cx->new_<ShareableBytes>(); 5108 if (!envBytes_) { 5109 return false; 5110 } 5111 5112 codeBytes_ = js_new<ShareableBytes>(); 5113 if (!codeBytes_) { 5114 return false; 5115 } 5116 5117 tailBytes_ = js_new<ShareableBytes>(); 5118 if (!tailBytes_) { 5119 return false; 5120 } 5121 5122 return PromiseHelperTask::init(cx); 5123 } 5124 }; 5125 5126 // A short-lived object that captures the arguments of a 5127 // WebAssembly.{compileStreaming,instantiateStreaming} while waiting for 5128 // the Promise<Response> to resolve to a (hopefully) Promise. 5129 class ResolveResponseClosure : public NativeObject { 5130 static const unsigned COMPILE_ARGS_SLOT = 0; 5131 static const unsigned PROMISE_OBJ_SLOT = 1; 5132 static const unsigned INSTANTIATE_SLOT = 2; 5133 static const unsigned IMPORT_OBJ_SLOT = 3; 5134 static const JSClassOps classOps_; 5135 5136 static void finalize(JS::GCContext* gcx, JSObject* obj) { 5137 auto& closure = obj->as<ResolveResponseClosure>(); 5138 gcx->release(obj, &closure.compileArgs(), 5139 MemoryUse::WasmResolveResponseClosure); 5140 } 5141 5142 public: 5143 static const unsigned RESERVED_SLOTS = 4; 5144 static const JSClass class_; 5145 5146 static ResolveResponseClosure* create(JSContext* cx, const CompileArgs& args, 5147 HandleObject promise, bool instantiate, 5148 HandleObject importObj) { 5149 MOZ_ASSERT_IF(importObj, instantiate); 5150 5151 AutoSetNewObjectMetadata metadata(cx); 5152 auto* obj = NewObjectWithGivenProto<ResolveResponseClosure>(cx, nullptr); 5153 if (!obj) { 5154 return nullptr; 5155 } 5156 5157 args.AddRef(); 5158 InitReservedSlot(obj, COMPILE_ARGS_SLOT, const_cast<CompileArgs*>(&args), 5159 MemoryUse::WasmResolveResponseClosure); 5160 obj->setReservedSlot(PROMISE_OBJ_SLOT, ObjectValue(*promise)); 5161 obj->setReservedSlot(INSTANTIATE_SLOT, BooleanValue(instantiate)); 5162 obj->setReservedSlot(IMPORT_OBJ_SLOT, ObjectOrNullValue(importObj)); 5163 return obj; 5164 } 5165 5166 CompileArgs& compileArgs() const { 5167 return *(CompileArgs*)getReservedSlot(COMPILE_ARGS_SLOT).toPrivate(); 5168 } 5169 PromiseObject& promise() const { 5170 return getReservedSlot(PROMISE_OBJ_SLOT).toObject().as<PromiseObject>(); 5171 } 5172 bool instantiate() const { 5173 return getReservedSlot(INSTANTIATE_SLOT).toBoolean(); 5174 } 5175 JSObject* importObj() const { 5176 return getReservedSlot(IMPORT_OBJ_SLOT).toObjectOrNull(); 5177 } 5178 }; 5179 5180 const JSClassOps ResolveResponseClosure::classOps_ = { 5181 nullptr, // addProperty 5182 nullptr, // delProperty 5183 nullptr, // enumerate 5184 nullptr, // newEnumerate 5185 nullptr, // resolve 5186 nullptr, // mayResolve 5187 ResolveResponseClosure::finalize, // finalize 5188 nullptr, // call 5189 nullptr, // construct 5190 nullptr, // trace 5191 }; 5192 5193 const JSClass ResolveResponseClosure::class_ = { 5194 "WebAssembly ResolveResponseClosure", 5195 JSCLASS_DELAY_METADATA_BUILDER | 5196 JSCLASS_HAS_RESERVED_SLOTS(ResolveResponseClosure::RESERVED_SLOTS) | 5197 JSCLASS_FOREGROUND_FINALIZE, 5198 &ResolveResponseClosure::classOps_, 5199 }; 5200 5201 static ResolveResponseClosure* ToResolveResponseClosure(const CallArgs& args) { 5202 return &args.callee() 5203 .as<JSFunction>() 5204 .getExtendedSlot(0) 5205 .toObject() 5206 .as<ResolveResponseClosure>(); 5207 } 5208 5209 static bool RejectWithErrorNumber(JSContext* cx, uint32_t errorNumber, 5210 Handle<PromiseObject*> promise) { 5211 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber); 5212 return RejectWithPendingException(cx, promise); 5213 } 5214 5215 static bool ResolveResponse_OnFulfilled(JSContext* cx, unsigned argc, 5216 Value* vp) { 5217 CallArgs callArgs = CallArgsFromVp(argc, vp); 5218 5219 Rooted<ResolveResponseClosure*> closure(cx, 5220 ToResolveResponseClosure(callArgs)); 5221 Rooted<PromiseObject*> promise(cx, &closure->promise()); 5222 CompileArgs& compileArgs = closure->compileArgs(); 5223 bool instantiate = closure->instantiate(); 5224 Rooted<JSObject*> importObj(cx, closure->importObj()); 5225 5226 auto task = cx->make_unique<CompileStreamTask>(cx, promise, compileArgs, 5227 instantiate, importObj); 5228 if (!task || !task->init(cx)) { 5229 return RejectWithOutOfMemory(cx, promise); 5230 } 5231 5232 if (!callArgs.get(0).isObject()) { 5233 return RejectWithErrorNumber(cx, JSMSG_WASM_BAD_RESPONSE_VALUE, promise); 5234 } 5235 5236 RootedObject response(cx, &callArgs.get(0).toObject()); 5237 if (!cx->runtime()->consumeStreamCallback(cx, response, JS::MimeType::Wasm, 5238 task.get())) { 5239 return RejectWithPendingException(cx, promise); 5240 } 5241 5242 (void)task.release(); 5243 5244 callArgs.rval().setUndefined(); 5245 return true; 5246 } 5247 5248 static bool ResolveResponse_OnRejected(JSContext* cx, unsigned argc, 5249 Value* vp) { 5250 CallArgs args = CallArgsFromVp(argc, vp); 5251 5252 Rooted<ResolveResponseClosure*> closure(cx, ToResolveResponseClosure(args)); 5253 Rooted<PromiseObject*> promise(cx, &closure->promise()); 5254 5255 if (!PromiseObject::reject(cx, promise, args.get(0))) { 5256 return false; 5257 } 5258 5259 args.rval().setUndefined(); 5260 return true; 5261 } 5262 5263 static bool ResolveResponse(JSContext* cx, Handle<Value> responsePromise, 5264 Handle<Value> featureOptions, 5265 Handle<PromiseObject*> resultPromise, 5266 bool instantiate = false, 5267 HandleObject importObj = nullptr) { 5268 MOZ_ASSERT_IF(importObj, instantiate); 5269 5270 const char* introducer = instantiate ? "WebAssembly.instantiateStreaming" 5271 : "WebAssembly.compileStreaming"; 5272 5273 FeatureOptions options; 5274 if (!options.init(cx, featureOptions)) { 5275 return false; 5276 } 5277 5278 SharedCompileArgs compileArgs = InitCompileArgs(cx, options, introducer); 5279 if (!compileArgs) { 5280 return false; 5281 } 5282 5283 RootedObject closure( 5284 cx, ResolveResponseClosure::create(cx, *compileArgs, resultPromise, 5285 instantiate, importObj)); 5286 if (!closure) { 5287 return false; 5288 } 5289 5290 RootedFunction onResolved( 5291 cx, NewNativeFunction(cx, ResolveResponse_OnFulfilled, 1, nullptr, 5292 gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); 5293 if (!onResolved) { 5294 return false; 5295 } 5296 5297 RootedFunction onRejected( 5298 cx, NewNativeFunction(cx, ResolveResponse_OnRejected, 1, nullptr, 5299 gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); 5300 if (!onRejected) { 5301 return false; 5302 } 5303 5304 onResolved->setExtendedSlot(0, ObjectValue(*closure)); 5305 onRejected->setExtendedSlot(0, ObjectValue(*closure)); 5306 5307 RootedObject resolve(cx, 5308 PromiseObject::unforgeableResolve(cx, responsePromise)); 5309 if (!resolve) { 5310 return false; 5311 } 5312 5313 return JS::AddPromiseReactions(cx, resolve, onResolved, onRejected); 5314 } 5315 5316 static bool WebAssembly_compileStreaming(JSContext* cx, unsigned argc, 5317 Value* vp) { 5318 if (!EnsureStreamSupport(cx)) { 5319 return false; 5320 } 5321 5322 Log(cx, "async compileStreaming() started"); 5323 5324 Rooted<PromiseObject*> resultPromise( 5325 cx, PromiseObject::createSkippingExecutor(cx)); 5326 if (!resultPromise) { 5327 return false; 5328 } 5329 5330 CallArgs callArgs = CallArgsFromVp(argc, vp); 5331 5332 JS::RootedVector<JSString*> parameterStrings(cx); 5333 JS::RootedVector<Value> parameterArgs(cx); 5334 bool canCompileStrings = false; 5335 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, 5336 JS::CompilationType::Undefined, 5337 parameterStrings, nullptr, parameterArgs, 5338 NullHandleValue, &canCompileStrings)) { 5339 return RejectWithPendingException(cx, resultPromise, callArgs); 5340 } 5341 if (!canCompileStrings) { 5342 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5343 JSMSG_CSP_BLOCKED_WASM, 5344 "WebAssembly.compileStreaming"); 5345 return RejectWithPendingException(cx, resultPromise, callArgs); 5346 } 5347 5348 Rooted<Value> responsePromise(cx, callArgs.get(0)); 5349 Rooted<Value> featureOptions(cx, callArgs.get(1)); 5350 if (!ResolveResponse(cx, responsePromise, featureOptions, resultPromise)) { 5351 return RejectWithPendingException(cx, resultPromise, callArgs); 5352 } 5353 5354 callArgs.rval().setObject(*resultPromise); 5355 return true; 5356 } 5357 5358 static bool WebAssembly_instantiateStreaming(JSContext* cx, unsigned argc, 5359 Value* vp) { 5360 if (!EnsureStreamSupport(cx)) { 5361 return false; 5362 } 5363 5364 Log(cx, "async instantiateStreaming() started"); 5365 5366 Rooted<PromiseObject*> resultPromise( 5367 cx, PromiseObject::createSkippingExecutor(cx)); 5368 if (!resultPromise) { 5369 return false; 5370 } 5371 5372 CallArgs callArgs = CallArgsFromVp(argc, vp); 5373 5374 JS::RootedVector<JSString*> parameterStrings(cx); 5375 JS::RootedVector<Value> parameterArgs(cx); 5376 bool canCompileStrings = false; 5377 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr, 5378 JS::CompilationType::Undefined, 5379 parameterStrings, nullptr, parameterArgs, 5380 NullHandleValue, &canCompileStrings)) { 5381 return RejectWithPendingException(cx, resultPromise, callArgs); 5382 } 5383 if (!canCompileStrings) { 5384 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 5385 JSMSG_CSP_BLOCKED_WASM, 5386 "WebAssembly.instantiateStreaming"); 5387 return RejectWithPendingException(cx, resultPromise, callArgs); 5388 } 5389 5390 Rooted<JSObject*> firstArg(cx); 5391 Rooted<JSObject*> importObj(cx); 5392 Rooted<Value> featureOptions(cx); 5393 if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj, 5394 &featureOptions)) { 5395 return RejectWithPendingException(cx, resultPromise, callArgs); 5396 } 5397 Rooted<Value> responsePromise(cx, ObjectValue(*firstArg.get())); 5398 5399 if (!ResolveResponse(cx, responsePromise, featureOptions, resultPromise, true, 5400 importObj)) { 5401 return RejectWithPendingException(cx, resultPromise, callArgs); 5402 } 5403 5404 callArgs.rval().setObject(*resultPromise); 5405 return true; 5406 } 5407 5408 #ifdef ENABLE_WASM_JSPI 5409 const ClassSpec WasmSuspendingObject::classSpec_ = { 5410 GenericCreateConstructor<construct, 1, gc::AllocKind::FUNCTION>, 5411 GenericCreatePrototype<WasmSuspendingObject>, 5412 nullptr, 5413 nullptr, 5414 nullptr, 5415 nullptr, 5416 nullptr, 5417 ClassSpec::DontDefineConstructor, 5418 }; 5419 5420 const JSClass WasmSuspendingObject::class_ = { 5421 "Suspending", 5422 JSCLASS_HAS_RESERVED_SLOTS(WasmSuspendingObject::RESERVED_SLOTS), 5423 JS_NULL_CLASS_OPS, 5424 &classSpec_, 5425 }; 5426 5427 const JSClass& WasmSuspendingObject::protoClass_ = PlainObject::class_; 5428 5429 /* static */ 5430 bool WasmSuspendingObject::construct(JSContext* cx, unsigned argc, Value* vp) { 5431 CallArgs args = CallArgsFromVp(argc, vp); 5432 5433 if (!ThrowIfNotConstructing(cx, args, "WebAssembly.Suspending")) { 5434 return false; 5435 } 5436 5437 if (!args.requireAtLeast(cx, "WebAssembly.Suspending", 1)) { 5438 return false; 5439 } 5440 5441 if (!IsCallableNonCCW(args[0])) { 5442 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 5443 JSMSG_WASM_BAD_FUNCTION_VALUE); 5444 return false; 5445 } 5446 5447 RootedObject callable(cx, &args[0].toObject()); 5448 Rooted<WasmSuspendingObject*> suspending( 5449 cx, NewBuiltinClassInstance<WasmSuspendingObject>(cx)); 5450 if (!suspending) { 5451 return false; 5452 } 5453 suspending->setWrappedFunction(callable); 5454 args.rval().setObject(*suspending); 5455 return true; 5456 } 5457 5458 static bool WebAssembly_promising(JSContext* cx, unsigned argc, Value* vp) { 5459 CallArgs args = CallArgsFromVp(argc, vp); 5460 5461 if (!args.requireAtLeast(cx, "WebAssembly.promising", 1)) { 5462 return false; 5463 } 5464 5465 if (!IsWasmFunction(args[0])) { 5466 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 5467 JSMSG_WASM_BAD_FUNCTION_VALUE); 5468 return false; 5469 } 5470 5471 RootedObject func(cx, &args[0].toObject()); 5472 RootedFunction promise( 5473 cx, WasmPromisingFunctionCreate(cx, func, wasm::ValTypeVector(), 5474 wasm::ValTypeVector())); 5475 if (!promise) { 5476 return false; 5477 } 5478 args.rval().setObject(*promise); 5479 return true; 5480 } 5481 5482 static const JSFunctionSpec WebAssembly_jspi_methods[] = { 5483 JS_FN("promising", WebAssembly_promising, 1, JSPROP_ENUMERATE), 5484 JS_FS_END, 5485 }; 5486 5487 bool js::IsWasmSuspendingObject(JSObject* obj) { 5488 return obj->is<WasmSuspendingObject>(); 5489 } 5490 5491 JSObject* js::MaybeUnwrapSuspendingObject(JSObject* wrapper) { 5492 if (!wrapper->is<WasmSuspendingObject>()) { 5493 return nullptr; 5494 } 5495 return wrapper->as<WasmSuspendingObject>().wrappedFunction(); 5496 } 5497 #else 5498 bool js::IsWasmSuspendingObject(JSObject* obj) { return false; } 5499 #endif // ENABLE_WASM_JSPI 5500 5501 #ifdef ENABLE_WASM_MOZ_INTGEMM 5502 5503 static bool WebAssembly_mozIntGemm(JSContext* cx, unsigned argc, Value* vp) { 5504 CallArgs args = CallArgsFromVp(argc, vp); 5505 5506 Rooted<WasmModuleObject*> module(cx); 5507 if (!wasm::CompileBuiltinModule(cx, wasm::BuiltinModuleId::IntGemm, nullptr, 5508 &module)) { 5509 ReportOutOfMemory(cx); 5510 return false; 5511 } 5512 args.rval().set(ObjectValue(*module.get())); 5513 return true; 5514 } 5515 5516 static const JSFunctionSpec WebAssembly_mozIntGemm_methods[] = { 5517 JS_FN("mozIntGemm", WebAssembly_mozIntGemm, 0, JSPROP_ENUMERATE), 5518 JS_FS_END, 5519 }; 5520 5521 #endif // ENABLE_WASM_MOZ_INTGEMM 5522 5523 static const JSFunctionSpec WebAssembly_static_methods[] = { 5524 JS_FN("toSource", WebAssembly_toSource, 0, 0), 5525 JS_FN("compile", WebAssembly_compile, 1, JSPROP_ENUMERATE), 5526 JS_FN("instantiate", WebAssembly_instantiate, 1, JSPROP_ENUMERATE), 5527 JS_FN("validate", WebAssembly_validate, 1, JSPROP_ENUMERATE), 5528 JS_FN("compileStreaming", WebAssembly_compileStreaming, 1, 5529 JSPROP_ENUMERATE), 5530 JS_FN("instantiateStreaming", WebAssembly_instantiateStreaming, 1, 5531 JSPROP_ENUMERATE), 5532 JS_FS_END, 5533 }; 5534 5535 static const JSPropertySpec WebAssembly_static_properties[] = { 5536 JS_STRING_SYM_PS(toStringTag, "WebAssembly", JSPROP_READONLY), 5537 JS_PS_END, 5538 }; 5539 5540 static JSObject* CreateWebAssemblyObject(JSContext* cx, JSProtoKey key) { 5541 MOZ_RELEASE_ASSERT(HasSupport(cx)); 5542 5543 RootedObject proto(cx, &cx->global()->getObjectPrototype()); 5544 return NewTenuredObjectWithGivenProto(cx, &WasmNamespaceObject::class_, 5545 proto); 5546 } 5547 5548 struct NameAndProtoKey { 5549 const char* const name; 5550 JSProtoKey key; 5551 }; 5552 5553 static bool WebAssemblyDefineConstructor(JSContext* cx, 5554 Handle<WasmNamespaceObject*> wasm, 5555 NameAndProtoKey entry, 5556 MutableHandleValue ctorValue, 5557 MutableHandleId id) { 5558 JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, entry.key); 5559 if (!ctor) { 5560 return false; 5561 } 5562 ctorValue.setObject(*ctor); 5563 5564 JSAtom* className = Atomize(cx, entry.name, strlen(entry.name)); 5565 if (!className) { 5566 return false; 5567 } 5568 id.set(AtomToId(className)); 5569 5570 return DefineDataProperty(cx, wasm, id, ctorValue, 0); 5571 } 5572 5573 static bool WebAssemblyClassFinish(JSContext* cx, HandleObject object, 5574 HandleObject proto) { 5575 Handle<WasmNamespaceObject*> wasm = object.as<WasmNamespaceObject>(); 5576 5577 constexpr NameAndProtoKey entries[] = { 5578 {"Module", JSProto_WasmModule}, 5579 {"Instance", JSProto_WasmInstance}, 5580 {"Memory", JSProto_WasmMemory}, 5581 {"Table", JSProto_WasmTable}, 5582 {"Global", JSProto_WasmGlobal}, 5583 {"CompileError", GetExceptionProtoKey(JSEXN_WASMCOMPILEERROR)}, 5584 {"LinkError", GetExceptionProtoKey(JSEXN_WASMLINKERROR)}, 5585 {"RuntimeError", GetExceptionProtoKey(JSEXN_WASMRUNTIMEERROR)}, 5586 #ifdef ENABLE_WASM_TYPE_REFLECTIONS 5587 {"Function", JSProto_WasmFunction}, 5588 #endif 5589 }; 5590 RootedValue ctorValue(cx); 5591 RootedId id(cx); 5592 for (const auto& entry : entries) { 5593 if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) { 5594 return false; 5595 } 5596 } 5597 5598 constexpr NameAndProtoKey exceptionEntries[] = { 5599 {"Tag", JSProto_WasmTag}, 5600 {"Exception", JSProto_WasmException}, 5601 }; 5602 for (const auto& entry : exceptionEntries) { 5603 if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) { 5604 return false; 5605 } 5606 } 5607 5608 RootedObject tagProto( 5609 cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmTag)); 5610 if (!tagProto) { 5611 ReportOutOfMemory(cx); 5612 return false; 5613 } 5614 5615 SharedTagType wrappedJSValueTagType(sWrappedJSValueTagType); 5616 WasmTagObject* wrappedJSValueTagObject = 5617 WasmTagObject::create(cx, wrappedJSValueTagType, tagProto); 5618 if (!wrappedJSValueTagObject) { 5619 return false; 5620 } 5621 5622 wasm->setWrappedJSValueTag(wrappedJSValueTagObject); 5623 5624 RootedId jsTagName(cx, NameToId(cx->names().jsTag)); 5625 RootedValue jsTagValue(cx, ObjectValue(*wrappedJSValueTagObject)); 5626 if (!DefineDataProperty(cx, wasm, jsTagName, jsTagValue, 5627 JSPROP_READONLY | JSPROP_ENUMERATE)) { 5628 return false; 5629 } 5630 5631 #ifdef ENABLE_WASM_JSPI 5632 constexpr NameAndProtoKey jspiEntries[] = { 5633 {"Suspending", JSProto_WasmSuspending}, 5634 {"SuspendError", GetExceptionProtoKey(JSEXN_WASMSUSPENDERROR)}, 5635 }; 5636 if (JSPromiseIntegrationAvailable(cx)) { 5637 if (!JS_DefineFunctions(cx, wasm, WebAssembly_jspi_methods)) { 5638 return false; 5639 } 5640 for (const auto& entry : jspiEntries) { 5641 if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) { 5642 return false; 5643 } 5644 } 5645 } 5646 #endif 5647 5648 #ifdef ENABLE_WASM_MOZ_INTGEMM 5649 if (MozIntGemmAvailable(cx) && 5650 !JS_DefineFunctions(cx, wasm, WebAssembly_mozIntGemm_methods)) { 5651 return false; 5652 } 5653 #endif 5654 5655 return true; 5656 } 5657 5658 WasmNamespaceObject* WasmNamespaceObject::getOrCreate(JSContext* cx) { 5659 JSObject* wasm = 5660 GlobalObject::getOrCreateConstructor(cx, JSProto_WebAssembly); 5661 if (!wasm) { 5662 return nullptr; 5663 } 5664 return &wasm->as<WasmNamespaceObject>(); 5665 } 5666 5667 static const ClassSpec WebAssemblyClassSpec = { 5668 CreateWebAssemblyObject, nullptr, WebAssembly_static_methods, 5669 WebAssembly_static_properties, nullptr, nullptr, 5670 WebAssemblyClassFinish, 5671 }; 5672 5673 const JSClass js::WasmNamespaceObject::class_ = { 5674 "WebAssembly", 5675 JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly) | 5676 JSCLASS_HAS_RESERVED_SLOTS(WasmNamespaceObject::RESERVED_SLOTS), 5677 JS_NULL_CLASS_OPS, 5678 &WebAssemblyClassSpec, 5679 }; 5680 5681 // Sundry