no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / js / src / wasm / WasmJS.cpp
blob6cc952841524117797d620e6c9545916c732323d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
4 * Copyright 2016 Mozilla Foundation
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 #include "wasm/WasmJS.h"
21 #include "mozilla/CheckedInt.h"
22 #include "mozilla/EndianUtils.h"
23 #include "mozilla/Maybe.h"
24 #include "mozilla/RangedPtr.h"
26 #include <algorithm>
27 #include <cstdint>
29 #include "jsapi.h"
30 #include "jsexn.h"
32 #include "ds/IdValuePair.h" // js::IdValuePair
33 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
34 #include "gc/GCContext.h"
35 #include "jit/AtomicOperations.h"
36 #include "jit/FlushICache.h"
37 #include "jit/JitContext.h"
38 #include "jit/JitOptions.h"
39 #include "jit/Simulator.h"
40 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
41 #include "js/ForOfIterator.h"
42 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
43 #include "js/Printf.h"
44 #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_GetProperty
45 #include "js/PropertySpec.h" // JS_{PS,FN}{,_END}
46 #include "js/Stack.h" // BuildStackString
47 #include "js/StreamConsumer.h"
48 #include "util/StringBuffer.h"
49 #include "util/Text.h"
50 #include "vm/ErrorObject.h"
51 #include "vm/FunctionFlags.h" // js::FunctionFlags
52 #include "vm/GlobalObject.h" // js::GlobalObject
53 #include "vm/HelperThreadState.h" // js::PromiseHelperTask
54 #include "vm/Interpreter.h"
55 #include "vm/JSFunction.h"
56 #include "vm/PlainObject.h" // js::PlainObject
57 #include "vm/PromiseObject.h" // js::PromiseObject
58 #include "vm/SharedArrayObject.h"
59 #include "vm/StringType.h"
60 #include "vm/Warnings.h" // js::WarnNumberASCII
61 #include "wasm/WasmBaselineCompile.h"
62 #include "wasm/WasmBuiltinModule.h"
63 #include "wasm/WasmBuiltins.h"
64 #include "wasm/WasmCompile.h"
65 #include "wasm/WasmDebug.h"
66 #include "wasm/WasmFeatures.h"
67 #include "wasm/WasmInstance.h"
68 #include "wasm/WasmIonCompile.h"
69 #include "wasm/WasmMemory.h"
70 #include "wasm/WasmModule.h"
71 #include "wasm/WasmProcess.h"
72 #include "wasm/WasmSignalHandlers.h"
73 #include "wasm/WasmStubs.h"
74 #include "wasm/WasmValidate.h"
76 #include "gc/GCContext-inl.h"
77 #include "gc/StableCellHasher-inl.h"
78 #include "vm/ArrayBufferObject-inl.h"
79 #include "vm/JSObject-inl.h"
80 #include "vm/NativeObject-inl.h"
81 #include "wasm/WasmInstance-inl.h"
84 * [SMDOC] WebAssembly code rules (evolving)
86 * TlsContext.get() is only to be invoked from functions that have been invoked
87 * _directly_ by generated code as cold(!) Builtin calls, from code that is
88 * only used by signal handlers, or from helper functions that have been
89 * called _directly_ from a simulator. All other code shall pass in a
90 * JSContext* to functions that need it, or an Instance* or Instance* since
91 * the context is available through them.
93 * Code that uses TlsContext.get() shall annotate each such call with the
94 * reason why the call is OK.
97 using namespace js;
98 using namespace js::jit;
99 using namespace js::wasm;
101 using mozilla::CheckedInt;
102 using mozilla::Nothing;
103 using mozilla::RangedPtr;
104 using mozilla::Span;
106 static bool ThrowCompileOutOfMemory(JSContext* cx) {
107 // Most OOMs during compilation are due to large contiguous allocations,
108 // and future allocations are likely to succeed. Throwing a proper error
109 // object is nicer for users in these circumstances.
110 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
111 return false;
114 // ============================================================================
115 // Imports
117 static bool ThrowBadImportArg(JSContext* cx) {
118 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
119 JSMSG_WASM_BAD_IMPORT_ARG);
120 return false;
123 static bool ThrowBadImportType(JSContext* cx, const CacheableName& field,
124 const char* str) {
125 UniqueChars fieldQuoted = field.toQuotedString(cx);
126 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
127 JSMSG_WASM_BAD_IMPORT_TYPE, fieldQuoted.get(), str);
128 return false;
131 // For now reject cross-compartment wrappers. These have more complicated realm
132 // semantics (we use nonCCWRealm in a few places) and may require unwrapping to
133 // test for specific function types.
134 static bool IsCallableNonCCW(const Value& v) {
135 return IsCallable(v) && !IsCrossCompartmentWrapper(&v.toObject());
138 bool js::wasm::GetImports(JSContext* cx, const Module& module,
139 HandleObject importObj, ImportValues* imports) {
140 if (!module.imports().empty() && !importObj) {
141 return ThrowBadImportArg(cx);
144 const Metadata& metadata = module.metadata();
146 BuiltinModuleInstances builtinInstances(cx);
147 RootedValue importModuleValue(cx);
148 RootedObject importModuleObject(cx);
149 RootedValue importFieldValue(cx);
151 uint32_t tagIndex = 0;
152 const TagDescVector& tags = metadata.tags;
153 uint32_t globalIndex = 0;
154 const GlobalDescVector& globals = metadata.globals;
155 uint32_t tableIndex = 0;
156 const TableDescVector& tables = metadata.tables;
157 for (const Import& import : module.imports()) {
158 Maybe<BuiltinModuleId> builtinModule = ImportMatchesBuiltinModule(
159 import.module.utf8Bytes(), metadata.builtinModules);
160 if (builtinModule) {
161 MutableHandle<JSObject*> builtinInstance =
162 builtinInstances[*builtinModule];
163 if (!builtinInstance && !wasm::InstantiateBuiltinModule(
164 cx, *builtinModule, builtinInstance)) {
165 return false;
167 importModuleObject = builtinInstance;
168 } else {
169 RootedId moduleName(cx);
170 if (!import.module.toPropertyKey(cx, &moduleName)) {
171 return false;
174 if (!GetProperty(cx, importObj, importObj, moduleName,
175 &importModuleValue)) {
176 return false;
179 if (!importModuleValue.isObject()) {
180 UniqueChars moduleQuoted = import.module.toQuotedString(cx);
181 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
182 JSMSG_WASM_BAD_IMPORT_FIELD,
183 moduleQuoted.get());
184 return false;
187 importModuleObject = &importModuleValue.toObject();
190 RootedId fieldName(cx);
191 if (!import.field.toPropertyKey(cx, &fieldName)) {
192 return false;
195 if (!GetProperty(cx, importModuleObject, importModuleObject, fieldName,
196 &importFieldValue)) {
197 return false;
200 switch (import.kind) {
201 case DefinitionKind::Function: {
202 if (!IsCallableNonCCW(importFieldValue)) {
203 return ThrowBadImportType(cx, import.field, "Function");
206 if (!imports->funcs.append(&importFieldValue.toObject())) {
207 return false;
210 break;
212 case DefinitionKind::Table: {
213 const uint32_t index = tableIndex++;
214 if (!importFieldValue.isObject() ||
215 !importFieldValue.toObject().is<WasmTableObject>()) {
216 return ThrowBadImportType(cx, import.field, "Table");
219 Rooted<WasmTableObject*> obj(
220 cx, &importFieldValue.toObject().as<WasmTableObject>());
221 if (obj->table().elemType() != tables[index].elemType) {
222 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
223 JSMSG_WASM_BAD_TBL_TYPE_LINK);
224 return false;
227 if (!imports->tables.append(obj)) {
228 return false;
230 break;
232 case DefinitionKind::Memory: {
233 if (!importFieldValue.isObject() ||
234 !importFieldValue.toObject().is<WasmMemoryObject>()) {
235 return ThrowBadImportType(cx, import.field, "Memory");
238 if (!imports->memories.append(
239 &importFieldValue.toObject().as<WasmMemoryObject>())) {
240 return false;
242 break;
244 case DefinitionKind::Tag: {
245 const uint32_t index = tagIndex++;
246 if (!importFieldValue.isObject() ||
247 !importFieldValue.toObject().is<WasmTagObject>()) {
248 return ThrowBadImportType(cx, import.field, "Tag");
251 Rooted<WasmTagObject*> obj(
252 cx, &importFieldValue.toObject().as<WasmTagObject>());
254 // Checks whether the signature of the imported exception object matches
255 // the signature declared in the exception import's TagDesc.
256 if (obj->resultType() != tags[index].type->resultType()) {
257 UniqueChars fieldQuoted = import.field.toQuotedString(cx);
258 UniqueChars moduleQuoted = import.module.toQuotedString(cx);
259 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
260 JSMSG_WASM_BAD_TAG_SIG, moduleQuoted.get(),
261 fieldQuoted.get());
262 return false;
265 if (!imports->tagObjs.append(obj)) {
266 ReportOutOfMemory(cx);
267 return false;
269 break;
271 case DefinitionKind::Global: {
272 const uint32_t index = globalIndex++;
273 const GlobalDesc& global = globals[index];
274 MOZ_ASSERT(global.importIndex() == index);
276 RootedVal val(cx);
277 if (importFieldValue.isObject() &&
278 importFieldValue.toObject().is<WasmGlobalObject>()) {
279 Rooted<WasmGlobalObject*> obj(
280 cx, &importFieldValue.toObject().as<WasmGlobalObject>());
282 if (obj->isMutable() != global.isMutable()) {
283 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
284 JSMSG_WASM_BAD_GLOB_MUT_LINK);
285 return false;
288 bool matches = global.isMutable()
289 ? obj->type() == global.type()
290 : ValType::isSubTypeOf(obj->type(), global.type());
291 if (!matches) {
292 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
293 JSMSG_WASM_BAD_GLOB_TYPE_LINK);
294 return false;
297 if (imports->globalObjs.length() <= index &&
298 !imports->globalObjs.resize(index + 1)) {
299 ReportOutOfMemory(cx);
300 return false;
302 imports->globalObjs[index] = obj;
303 val = obj->val();
304 } else {
305 if (!global.type().isRefType()) {
306 if (global.type() == ValType::I64 && !importFieldValue.isBigInt()) {
307 return ThrowBadImportType(cx, import.field, "BigInt");
309 if (global.type() != ValType::I64 && !importFieldValue.isNumber()) {
310 return ThrowBadImportType(cx, import.field, "Number");
314 if (global.isMutable()) {
315 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
316 JSMSG_WASM_BAD_GLOB_MUT_LINK);
317 return false;
320 if (!Val::fromJSValue(cx, global.type(), importFieldValue, &val)) {
321 return false;
325 if (!imports->globalValues.append(val)) {
326 return false;
329 break;
334 MOZ_ASSERT(globalIndex == globals.length() ||
335 !globals[globalIndex].isImport());
337 return true;
340 static bool DescribeScriptedCaller(JSContext* cx, ScriptedCaller* caller,
341 const char* introducer) {
342 // Note: JS::DescribeScriptedCaller returns whether a scripted caller was
343 // found, not whether an error was thrown. This wrapper function converts
344 // back to the more ordinary false-if-error form.
346 JS::AutoFilename af;
347 if (JS::DescribeScriptedCaller(cx, &af, &caller->line)) {
348 caller->filename =
349 FormatIntroducedFilename(af.get(), caller->line, introducer);
350 if (!caller->filename) {
351 ReportOutOfMemory(cx);
352 return false;
356 return true;
359 static SharedCompileArgs InitCompileArgs(JSContext* cx, FeatureOptions options,
360 const char* introducer) {
361 ScriptedCaller scriptedCaller;
362 if (!DescribeScriptedCaller(cx, &scriptedCaller, introducer)) {
363 return nullptr;
366 return CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options);
369 // ============================================================================
370 // Testing / Fuzzing support
372 bool wasm::Eval(JSContext* cx, Handle<TypedArrayObject*> code,
373 HandleObject importObj,
374 MutableHandle<WasmInstanceObject*> instanceObj) {
375 if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_WebAssembly)) {
376 return false;
379 MutableBytes bytecode = cx->new_<ShareableBytes>();
380 if (!bytecode) {
381 return false;
384 if (!bytecode->append((uint8_t*)code->dataPointerEither().unwrap(),
385 code->byteLength().valueOr(0))) {
386 ReportOutOfMemory(cx);
387 return false;
390 FeatureOptions options;
391 SharedCompileArgs compileArgs = InitCompileArgs(cx, options, "wasm_eval");
392 if (!compileArgs) {
393 return false;
396 UniqueChars error;
397 UniqueCharsVector warnings;
398 SharedModule module =
399 CompileBuffer(*compileArgs, *bytecode, &error, &warnings, nullptr);
400 if (!module) {
401 if (error) {
402 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
403 JSMSG_WASM_COMPILE_ERROR, error.get());
404 return false;
406 return ThrowCompileOutOfMemory(cx);
409 Rooted<ImportValues> imports(cx);
410 if (!GetImports(cx, *module, importObj, imports.address())) {
411 return false;
414 return module->instantiate(cx, imports.get(), nullptr, instanceObj);
417 struct MOZ_STACK_CLASS SerializeListener : JS::OptimizedEncodingListener {
418 // MOZ_STACK_CLASS means these can be nops.
419 MozExternalRefCountType MOZ_XPCOM_ABI AddRef() override { return 0; }
420 MozExternalRefCountType MOZ_XPCOM_ABI Release() override { return 0; }
422 DebugOnly<bool> called = false;
423 Bytes* serialized;
424 explicit SerializeListener(Bytes* serialized) : serialized(serialized) {}
426 void storeOptimizedEncoding(const uint8_t* bytes, size_t length) override {
427 MOZ_ASSERT(!called);
428 called = true;
429 if (serialized->resizeUninitialized(length)) {
430 memcpy(serialized->begin(), bytes, length);
435 bool wasm::CompileAndSerialize(JSContext* cx, const ShareableBytes& bytecode,
436 Bytes* serialized) {
437 // The caller must check that code caching is available
438 MOZ_ASSERT(CodeCachingAvailable(cx));
440 // Create and manually fill in compile args for code caching
441 MutableCompileArgs compileArgs = js_new<CompileArgs>(ScriptedCaller());
442 if (!compileArgs) {
443 return false;
446 // The caller has ensured CodeCachingAvailable(). Moreover, we want to ensure
447 // we go straight to tier-2 so that we synchronously call
448 // JS::OptimizedEncodingListener::storeOptimizedEncoding().
449 compileArgs->baselineEnabled = false;
450 compileArgs->forceTiering = false;
452 // We always pick Ion here, and we depend on CodeCachingAvailable() having
453 // determined that Ion is available, see comments at CodeCachingAvailable().
454 // To do better, we need to pass information about which compiler that should
455 // be used into CompileAndSerialize().
456 compileArgs->ionEnabled = true;
458 // Select features that are enabled. This is guaranteed to be consistent with
459 // our compiler selection, as code caching is only available if ion is
460 // available, and ion is only available if it's not disabled by enabled
461 // features.
462 compileArgs->features = FeatureArgs::build(cx, FeatureOptions());
464 SerializeListener listener(serialized);
466 UniqueChars error;
467 UniqueCharsVector warnings;
468 SharedModule module =
469 CompileBuffer(*compileArgs, bytecode, &error, &warnings, &listener);
470 if (!module) {
471 fprintf(stderr, "Compilation error: %s\n", error ? error.get() : "oom");
472 return false;
475 MOZ_ASSERT(module->code().hasTier(Tier::Serialized));
476 MOZ_ASSERT(listener.called);
477 return !listener.serialized->empty();
480 bool wasm::DeserializeModule(JSContext* cx, const Bytes& serialized,
481 MutableHandleObject moduleObj) {
482 MutableModule module =
483 Module::deserialize(serialized.begin(), serialized.length());
484 if (!module) {
485 ReportOutOfMemory(cx);
486 return false;
489 moduleObj.set(module->createObject(cx));
490 return !!moduleObj;
493 // ============================================================================
494 // Common functions
496 // '[EnforceRange] unsigned long' types are coerced with
497 // ConvertToInt(v, 32, 'unsigned')
498 // defined in Web IDL Section 3.2.4.9.
500 // This just generalizes that to an arbitrary limit that is representable as an
501 // integer in double form.
503 static bool EnforceRange(JSContext* cx, HandleValue v, const char* kind,
504 const char* noun, uint64_t max, uint64_t* val) {
505 // Step 4.
506 double x;
507 if (!ToNumber(cx, v, &x)) {
508 return false;
511 // Step 5.
512 if (mozilla::IsNegativeZero(x)) {
513 x = 0.0;
516 // Step 6.1.
517 if (!std::isfinite(x)) {
518 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
519 JSMSG_WASM_BAD_ENFORCE_RANGE, kind, noun);
520 return false;
523 // Step 6.2.
524 x = JS::ToInteger(x);
526 // Step 6.3.
527 if (x < 0 || x > double(max)) {
528 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
529 JSMSG_WASM_BAD_ENFORCE_RANGE, kind, noun);
530 return false;
533 *val = uint64_t(x);
534 MOZ_ASSERT(double(*val) == x);
535 return true;
538 static bool EnforceRangeU32(JSContext* cx, HandleValue v, const char* kind,
539 const char* noun, uint32_t* u32) {
540 uint64_t u64 = 0;
541 if (!EnforceRange(cx, v, kind, noun, uint64_t(UINT32_MAX), &u64)) {
542 return false;
544 *u32 = uint32_t(u64);
545 return true;
548 static bool EnforceRangeU64(JSContext* cx, HandleValue v, const char* kind,
549 const char* noun, uint64_t* u64) {
550 // The max is Number.MAX_SAFE_INTEGER
551 return EnforceRange(cx, v, kind, noun, (1LL << 53) - 1, u64);
554 static bool GetLimit(JSContext* cx, HandleObject obj, const char* name,
555 const char* noun, const char* msg, uint32_t range,
556 bool* found, uint64_t* value) {
557 JSAtom* atom = Atomize(cx, name, strlen(name));
558 if (!atom) {
559 return false;
561 RootedId id(cx, AtomToId(atom));
563 RootedValue val(cx);
564 if (!GetProperty(cx, obj, obj, id, &val)) {
565 return false;
568 if (val.isUndefined()) {
569 *found = false;
570 return true;
572 *found = true;
573 // The range can be greater than 53, but then the logic in EnforceRange has to
574 // change to avoid precision loss.
575 MOZ_ASSERT(range < 54);
576 return EnforceRange(cx, val, noun, msg, (uint64_t(1) << range) - 1, value);
579 static bool GetLimits(JSContext* cx, HandleObject obj, LimitsKind kind,
580 Limits* limits) {
581 limits->indexType = IndexType::I32;
583 // Memory limits may specify an alternate index type, and we need this to
584 // check the ranges for initial and maximum, so look for the index type first.
585 if (kind == LimitsKind::Memory) {
586 #ifdef ENABLE_WASM_MEMORY64
587 // Get the index type field
588 JSAtom* indexTypeAtom = Atomize(cx, "index", strlen("index"));
589 if (!indexTypeAtom) {
590 return false;
592 RootedId indexTypeId(cx, AtomToId(indexTypeAtom));
594 RootedValue indexTypeVal(cx);
595 if (!GetProperty(cx, obj, obj, indexTypeId, &indexTypeVal)) {
596 return false;
599 // The index type has a default value
600 if (!indexTypeVal.isUndefined()) {
601 if (!ToIndexType(cx, indexTypeVal, &limits->indexType)) {
602 return false;
605 if (limits->indexType == IndexType::I64 && !Memory64Available(cx)) {
606 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
607 JSMSG_WASM_NO_MEM64_LINK);
608 return false;
611 #endif
614 const char* noun = (kind == LimitsKind::Memory ? "Memory" : "Table");
615 // 2^48 is a valid value, so the range goes to 49 bits. Values above 2^48 are
616 // filtered later, just as values above 2^16 are filtered for mem32.
617 const uint32_t range = limits->indexType == IndexType::I32 ? 32 : 49;
618 uint64_t limit = 0;
620 bool haveInitial = false;
621 if (!GetLimit(cx, obj, "initial", noun, "initial size", range, &haveInitial,
622 &limit)) {
623 return false;
625 if (haveInitial) {
626 limits->initial = limit;
629 bool haveMinimum = false;
630 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
631 if (!GetLimit(cx, obj, "minimum", noun, "initial size", range, &haveMinimum,
632 &limit)) {
633 return false;
635 if (haveMinimum) {
636 limits->initial = limit;
638 #endif
640 if (!(haveInitial || haveMinimum)) {
641 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
642 JSMSG_WASM_MISSING_REQUIRED, "initial");
643 return false;
645 if (haveInitial && haveMinimum) {
646 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
647 JSMSG_WASM_SUPPLY_ONLY_ONE, "minimum", "initial");
648 return false;
651 bool haveMaximum = false;
652 if (!GetLimit(cx, obj, "maximum", noun, "maximum size", range, &haveMaximum,
653 &limit)) {
654 return false;
656 if (haveMaximum) {
657 limits->maximum = Some(limit);
660 limits->shared = Shareable::False;
662 // Memory limits may be shared.
663 if (kind == LimitsKind::Memory) {
664 // Get the shared field
665 JSAtom* sharedAtom = Atomize(cx, "shared", strlen("shared"));
666 if (!sharedAtom) {
667 return false;
669 RootedId sharedId(cx, AtomToId(sharedAtom));
671 RootedValue sharedVal(cx);
672 if (!GetProperty(cx, obj, obj, sharedId, &sharedVal)) {
673 return false;
676 // shared's default value is false, which is already the value set above.
677 if (!sharedVal.isUndefined()) {
678 limits->shared =
679 ToBoolean(sharedVal) ? Shareable::True : Shareable::False;
681 if (limits->shared == Shareable::True) {
682 if (!haveMaximum) {
683 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
684 JSMSG_WASM_MISSING_MAXIMUM, noun);
685 return false;
688 if (!cx->realm()
689 ->creationOptions()
690 .getSharedMemoryAndAtomicsEnabled()) {
691 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
692 JSMSG_WASM_NO_SHMEM_LINK);
693 return false;
699 return true;
702 static bool CheckLimits(JSContext* cx, uint64_t maximumField, LimitsKind kind,
703 Limits* limits) {
704 const char* noun = (kind == LimitsKind::Memory ? "Memory" : "Table");
706 if (limits->initial > maximumField) {
707 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
708 noun, "initial size");
709 return false;
712 if (limits->maximum.isSome() &&
713 (*limits->maximum > maximumField || limits->initial > *limits->maximum)) {
714 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
715 noun, "maximum size");
716 return false;
718 return true;
721 template <class Class, const char* name>
722 static JSObject* CreateWasmConstructor(JSContext* cx, JSProtoKey key) {
723 Rooted<JSAtom*> className(cx, Atomize(cx, name, strlen(name)));
724 if (!className) {
725 return nullptr;
728 return NewNativeConstructor(cx, Class::construct, 1, className);
731 static JSObject* GetWasmConstructorPrototype(JSContext* cx,
732 const CallArgs& callArgs,
733 JSProtoKey key) {
734 RootedObject proto(cx);
735 if (!GetPrototypeFromBuiltinConstructor(cx, callArgs, key, &proto)) {
736 return nullptr;
738 if (!proto) {
739 proto = GlobalObject::getOrCreatePrototype(cx, key);
741 return proto;
744 [[nodiscard]] static bool ParseValTypes(JSContext* cx, HandleValue src,
745 ValTypeVector& dest) {
746 JS::ForOfIterator iterator(cx);
748 if (!iterator.init(src, JS::ForOfIterator::ThrowOnNonIterable)) {
749 return false;
752 RootedValue nextParam(cx);
753 while (true) {
754 bool done;
755 if (!iterator.next(&nextParam, &done)) {
756 return false;
758 if (done) {
759 break;
762 ValType valType;
763 if (!ToValType(cx, nextParam, &valType) || !dest.append(valType)) {
764 return false;
767 return true;
770 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
771 static JSString* UTF8CharsToString(JSContext* cx, const char* chars) {
772 return NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(chars, strlen(chars)));
775 [[nodiscard]] static JSObject* ValTypesToArray(JSContext* cx,
776 const ValTypeVector& valTypes) {
777 Rooted<ArrayObject*> arrayObj(cx, NewDenseEmptyArray(cx));
778 if (!arrayObj) {
779 return nullptr;
781 for (ValType valType : valTypes) {
782 RootedString type(cx,
783 UTF8CharsToString(cx, ToString(valType, nullptr).get()));
784 if (!type) {
785 return nullptr;
787 if (!NewbornArrayPush(cx, arrayObj, StringValue(type))) {
788 return nullptr;
791 return arrayObj;
794 static JSObject* FuncTypeToObject(JSContext* cx, const FuncType& type) {
795 Rooted<IdValueVector> props(cx, IdValueVector(cx));
797 RootedObject parametersObj(cx, ValTypesToArray(cx, type.args()));
798 if (!parametersObj ||
799 !props.append(IdValuePair(NameToId(cx->names().parameters),
800 ObjectValue(*parametersObj)))) {
801 ReportOutOfMemory(cx);
802 return nullptr;
805 RootedObject resultsObj(cx, ValTypesToArray(cx, type.results()));
806 if (!resultsObj || !props.append(IdValuePair(NameToId(cx->names().results),
807 ObjectValue(*resultsObj)))) {
808 ReportOutOfMemory(cx);
809 return nullptr;
812 return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length());
815 static JSObject* TableTypeToObject(JSContext* cx, RefType type,
816 uint32_t initial, Maybe<uint32_t> maximum) {
817 Rooted<IdValueVector> props(cx, IdValueVector(cx));
819 RootedString elementType(
820 cx, UTF8CharsToString(cx, ToString(type, nullptr).get()));
821 if (!elementType || !props.append(IdValuePair(NameToId(cx->names().element),
822 StringValue(elementType)))) {
823 ReportOutOfMemory(cx);
824 return nullptr;
827 if (maximum.isSome()) {
828 if (!props.append(IdValuePair(NameToId(cx->names().maximum),
829 NumberValue(maximum.value())))) {
830 ReportOutOfMemory(cx);
831 return nullptr;
835 if (!props.append(
836 IdValuePair(NameToId(cx->names().minimum), NumberValue(initial)))) {
837 ReportOutOfMemory(cx);
838 return nullptr;
841 return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length());
844 static JSObject* MemoryTypeToObject(JSContext* cx, bool shared,
845 wasm::IndexType indexType,
846 wasm::Pages minPages,
847 Maybe<wasm::Pages> maxPages) {
848 Rooted<IdValueVector> props(cx, IdValueVector(cx));
849 if (maxPages) {
850 double maxPagesNum;
851 if (indexType == IndexType::I32) {
852 maxPagesNum = double(mozilla::AssertedCast<uint32_t>(maxPages->value()));
853 } else {
854 // The maximum number of pages is 2^48.
855 maxPagesNum = double(maxPages->value());
857 if (!props.append(IdValuePair(NameToId(cx->names().maximum),
858 NumberValue(maxPagesNum)))) {
859 ReportOutOfMemory(cx);
860 return nullptr;
864 double minPagesNum;
865 if (indexType == IndexType::I32) {
866 minPagesNum = double(mozilla::AssertedCast<uint32_t>(minPages.value()));
867 } else {
868 minPagesNum = double(minPages.value());
870 if (!props.append(IdValuePair(NameToId(cx->names().minimum),
871 NumberValue(minPagesNum)))) {
872 ReportOutOfMemory(cx);
873 return nullptr;
876 # ifdef ENABLE_WASM_MEMORY64
877 RootedString it(
878 cx, JS_NewStringCopyZ(cx, indexType == IndexType::I32 ? "i32" : "i64"));
879 if (!it) {
880 return nullptr;
882 if (!props.append(
883 IdValuePair(NameToId(cx->names().index), StringValue(it)))) {
884 ReportOutOfMemory(cx);
885 return nullptr;
887 # endif
889 if (!props.append(
890 IdValuePair(NameToId(cx->names().shared), BooleanValue(shared)))) {
891 ReportOutOfMemory(cx);
892 return nullptr;
895 return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length());
898 static JSObject* GlobalTypeToObject(JSContext* cx, ValType type,
899 bool isMutable) {
900 Rooted<IdValueVector> props(cx, IdValueVector(cx));
902 if (!props.append(IdValuePair(NameToId(cx->names().mutable_),
903 BooleanValue(isMutable)))) {
904 ReportOutOfMemory(cx);
905 return nullptr;
908 RootedString valueType(cx,
909 UTF8CharsToString(cx, ToString(type, nullptr).get()));
910 if (!valueType || !props.append(IdValuePair(NameToId(cx->names().value),
911 StringValue(valueType)))) {
912 ReportOutOfMemory(cx);
913 return nullptr;
916 return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length());
919 static JSObject* TagTypeToObject(JSContext* cx,
920 const wasm::ValTypeVector& params) {
921 Rooted<IdValueVector> props(cx, IdValueVector(cx));
923 RootedObject parametersObj(cx, ValTypesToArray(cx, params));
924 if (!parametersObj ||
925 !props.append(IdValuePair(NameToId(cx->names().parameters),
926 ObjectValue(*parametersObj)))) {
927 ReportOutOfMemory(cx);
928 return nullptr;
931 return NewPlainObjectWithUniqueNames(cx, props.begin(), props.length());
933 #endif // ENABLE_WASM_TYPE_REFLECTIONS
935 // ============================================================================
936 // WebAssembly.Module class and methods
938 const JSClassOps WasmModuleObject::classOps_ = {
939 nullptr, // addProperty
940 nullptr, // delProperty
941 nullptr, // enumerate
942 nullptr, // newEnumerate
943 nullptr, // resolve
944 nullptr, // mayResolve
945 WasmModuleObject::finalize, // finalize
946 nullptr, // call
947 nullptr, // construct
948 nullptr, // trace
951 const JSClass WasmModuleObject::class_ = {
952 "WebAssembly.Module",
953 JSCLASS_DELAY_METADATA_BUILDER |
954 JSCLASS_HAS_RESERVED_SLOTS(WasmModuleObject::RESERVED_SLOTS) |
955 JSCLASS_FOREGROUND_FINALIZE,
956 &WasmModuleObject::classOps_,
957 &WasmModuleObject::classSpec_,
960 const JSClass& WasmModuleObject::protoClass_ = PlainObject::class_;
962 static constexpr char WasmModuleName[] = "Module";
964 const ClassSpec WasmModuleObject::classSpec_ = {
965 CreateWasmConstructor<WasmModuleObject, WasmModuleName>,
966 GenericCreatePrototype<WasmModuleObject>,
967 WasmModuleObject::static_methods,
968 nullptr,
969 WasmModuleObject::methods,
970 WasmModuleObject::properties,
971 nullptr,
972 ClassSpec::DontDefineConstructor};
974 const JSPropertySpec WasmModuleObject::properties[] = {
975 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Module", JSPROP_READONLY),
976 JS_PS_END};
978 const JSFunctionSpec WasmModuleObject::methods[] = {JS_FS_END};
980 const JSFunctionSpec WasmModuleObject::static_methods[] = {
981 JS_FN("imports", WasmModuleObject::imports, 1, JSPROP_ENUMERATE),
982 JS_FN("exports", WasmModuleObject::exports, 1, JSPROP_ENUMERATE),
983 JS_FN("customSections", WasmModuleObject::customSections, 2,
984 JSPROP_ENUMERATE),
985 JS_FS_END};
987 /* static */
988 void WasmModuleObject::finalize(JS::GCContext* gcx, JSObject* obj) {
989 const Module& module = obj->as<WasmModuleObject>().module();
990 obj->zone()->decJitMemory(module.codeLength(module.code().stableTier()));
991 gcx->release(obj, &module, module.gcMallocBytesExcludingCode(),
992 MemoryUse::WasmModule);
995 static bool IsModuleObject(JSObject* obj, const Module** module) {
996 WasmModuleObject* mobj = obj->maybeUnwrapIf<WasmModuleObject>();
997 if (!mobj) {
998 return false;
1001 *module = &mobj->module();
1002 return true;
1005 static bool GetModuleArg(JSContext* cx, CallArgs args, uint32_t numRequired,
1006 const char* name, const Module** module) {
1007 if (!args.requireAtLeast(cx, name, numRequired)) {
1008 return false;
1011 if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), module)) {
1012 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1013 JSMSG_WASM_BAD_MOD_ARG);
1014 return false;
1017 return true;
1020 struct KindNames {
1021 Rooted<PropertyName*> kind;
1022 Rooted<PropertyName*> table;
1023 Rooted<PropertyName*> memory;
1024 Rooted<PropertyName*> tag;
1025 Rooted<PropertyName*> type;
1027 explicit KindNames(JSContext* cx)
1028 : kind(cx), table(cx), memory(cx), tag(cx), type(cx) {}
1031 static bool InitKindNames(JSContext* cx, KindNames* names) {
1032 JSAtom* kind = Atomize(cx, "kind", strlen("kind"));
1033 if (!kind) {
1034 return false;
1036 names->kind = kind->asPropertyName();
1038 JSAtom* table = Atomize(cx, "table", strlen("table"));
1039 if (!table) {
1040 return false;
1042 names->table = table->asPropertyName();
1044 JSAtom* memory = Atomize(cx, "memory", strlen("memory"));
1045 if (!memory) {
1046 return false;
1048 names->memory = memory->asPropertyName();
1050 JSAtom* tag = Atomize(cx, "tag", strlen("tag"));
1051 if (!tag) {
1052 return false;
1054 names->tag = tag->asPropertyName();
1056 JSAtom* type = Atomize(cx, "type", strlen("type"));
1057 if (!type) {
1058 return false;
1060 names->type = type->asPropertyName();
1062 return true;
1065 static JSString* KindToString(JSContext* cx, const KindNames& names,
1066 DefinitionKind kind) {
1067 switch (kind) {
1068 case DefinitionKind::Function:
1069 return cx->names().function;
1070 case DefinitionKind::Table:
1071 return names.table;
1072 case DefinitionKind::Memory:
1073 return names.memory;
1074 case DefinitionKind::Global:
1075 return cx->names().global;
1076 case DefinitionKind::Tag:
1077 return names.tag;
1080 MOZ_CRASH("invalid kind");
1083 /* static */
1084 bool WasmModuleObject::imports(JSContext* cx, unsigned argc, Value* vp) {
1085 CallArgs args = CallArgsFromVp(argc, vp);
1087 const Module* module;
1088 if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.imports", &module)) {
1089 return false;
1092 KindNames names(cx);
1093 if (!InitKindNames(cx, &names)) {
1094 return false;
1097 RootedValueVector elems(cx);
1098 if (!elems.reserve(module->imports().length())) {
1099 return false;
1102 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
1103 const Metadata& metadata = module->metadata();
1104 const MetadataTier& metadataTier =
1105 module->metadata(module->code().stableTier());
1107 size_t numFuncImport = 0;
1108 size_t numMemoryImport = 0;
1109 size_t numGlobalImport = 0;
1110 size_t numTableImport = 0;
1111 size_t numTagImport = 0;
1112 #endif // ENABLE_WASM_TYPE_REFLECTIONS
1114 for (const Import& import : module->imports()) {
1115 Rooted<IdValueVector> props(cx, IdValueVector(cx));
1116 if (!props.reserve(3)) {
1117 return false;
1120 JSString* moduleStr = import.module.toAtom(cx);
1121 if (!moduleStr) {
1122 return false;
1124 props.infallibleAppend(
1125 IdValuePair(NameToId(cx->names().module), StringValue(moduleStr)));
1127 JSString* nameStr = import.field.toAtom(cx);
1128 if (!nameStr) {
1129 return false;
1131 props.infallibleAppend(
1132 IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
1134 JSString* kindStr = KindToString(cx, names, import.kind);
1135 if (!kindStr) {
1136 return false;
1138 props.infallibleAppend(
1139 IdValuePair(NameToId(names.kind), StringValue(kindStr)));
1141 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
1142 RootedObject typeObj(cx);
1143 switch (import.kind) {
1144 case DefinitionKind::Function: {
1145 size_t funcIndex = numFuncImport++;
1146 const FuncType& funcType =
1147 metadata.getFuncImportType(metadataTier.funcImports[funcIndex]);
1148 typeObj = FuncTypeToObject(cx, funcType);
1149 break;
1151 case DefinitionKind::Table: {
1152 size_t tableIndex = numTableImport++;
1153 const TableDesc& table = metadata.tables[tableIndex];
1154 typeObj = TableTypeToObject(cx, table.elemType, table.initialLength,
1155 table.maximumLength);
1156 break;
1158 case DefinitionKind::Memory: {
1159 size_t memoryIndex = numMemoryImport++;
1160 const MemoryDesc& memory = metadata.memories[memoryIndex];
1161 typeObj =
1162 MemoryTypeToObject(cx, memory.isShared(), memory.indexType(),
1163 memory.initialPages(), memory.maximumPages());
1164 break;
1166 case DefinitionKind::Global: {
1167 size_t globalIndex = numGlobalImport++;
1168 const GlobalDesc& global = metadata.globals[globalIndex];
1169 typeObj = GlobalTypeToObject(cx, global.type(), global.isMutable());
1170 break;
1172 case DefinitionKind::Tag: {
1173 size_t tagIndex = numTagImport++;
1174 const TagDesc& tag = metadata.tags[tagIndex];
1175 typeObj = TagTypeToObject(cx, tag.type->argTypes());
1176 break;
1180 if (!typeObj || !props.append(IdValuePair(NameToId(names.type),
1181 ObjectValue(*typeObj)))) {
1182 ReportOutOfMemory(cx);
1183 return false;
1185 #endif // ENABLE_WASM_TYPE_REFLECTIONS
1187 JSObject* obj =
1188 NewPlainObjectWithUniqueNames(cx, props.begin(), props.length());
1189 if (!obj) {
1190 return false;
1193 elems.infallibleAppend(ObjectValue(*obj));
1196 JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1197 if (!arr) {
1198 return false;
1201 args.rval().setObject(*arr);
1202 return true;
1205 /* static */
1206 bool WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp) {
1207 CallArgs args = CallArgsFromVp(argc, vp);
1209 const Module* module;
1210 if (!GetModuleArg(cx, args, 1, "WebAssembly.Module.exports", &module)) {
1211 return false;
1214 KindNames names(cx);
1215 if (!InitKindNames(cx, &names)) {
1216 return false;
1219 RootedValueVector elems(cx);
1220 if (!elems.reserve(module->exports().length())) {
1221 return false;
1224 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
1225 const Metadata& metadata = module->metadata();
1226 const MetadataTier& metadataTier =
1227 module->metadata(module->code().stableTier());
1228 #endif // ENABLE_WASM_TYPE_REFLECTIONS
1230 for (const Export& exp : module->exports()) {
1231 Rooted<IdValueVector> props(cx, IdValueVector(cx));
1232 if (!props.reserve(2)) {
1233 return false;
1236 JSString* nameStr = exp.fieldName().toAtom(cx);
1237 if (!nameStr) {
1238 return false;
1240 props.infallibleAppend(
1241 IdValuePair(NameToId(cx->names().name), StringValue(nameStr)));
1243 JSString* kindStr = KindToString(cx, names, exp.kind());
1244 if (!kindStr) {
1245 return false;
1247 props.infallibleAppend(
1248 IdValuePair(NameToId(names.kind), StringValue(kindStr)));
1250 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
1251 RootedObject typeObj(cx);
1252 switch (exp.kind()) {
1253 case DefinitionKind::Function: {
1254 const FuncExport& fe = metadataTier.lookupFuncExport(exp.funcIndex());
1255 const FuncType& funcType = metadata.getFuncExportType(fe);
1256 typeObj = FuncTypeToObject(cx, funcType);
1257 break;
1259 case DefinitionKind::Table: {
1260 const TableDesc& table = metadata.tables[exp.tableIndex()];
1261 typeObj = TableTypeToObject(cx, table.elemType, table.initialLength,
1262 table.maximumLength);
1263 break;
1265 case DefinitionKind::Memory: {
1266 const MemoryDesc& memory = metadata.memories[exp.memoryIndex()];
1267 typeObj =
1268 MemoryTypeToObject(cx, memory.isShared(), memory.indexType(),
1269 memory.initialPages(), memory.maximumPages());
1270 break;
1272 case DefinitionKind::Global: {
1273 const GlobalDesc& global = metadata.globals[exp.globalIndex()];
1274 typeObj = GlobalTypeToObject(cx, global.type(), global.isMutable());
1275 break;
1277 case DefinitionKind::Tag: {
1278 const TagDesc& tag = metadata.tags[exp.tagIndex()];
1279 typeObj = TagTypeToObject(cx, tag.type->argTypes());
1280 break;
1284 if (!typeObj || !props.append(IdValuePair(NameToId(names.type),
1285 ObjectValue(*typeObj)))) {
1286 ReportOutOfMemory(cx);
1287 return false;
1289 #endif // ENABLE_WASM_TYPE_REFLECTIONS
1291 JSObject* obj =
1292 NewPlainObjectWithUniqueNames(cx, props.begin(), props.length());
1293 if (!obj) {
1294 return false;
1297 elems.infallibleAppend(ObjectValue(*obj));
1300 JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1301 if (!arr) {
1302 return false;
1305 args.rval().setObject(*arr);
1306 return true;
1309 /* static */
1310 bool WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp) {
1311 CallArgs args = CallArgsFromVp(argc, vp);
1313 const Module* module;
1314 if (!GetModuleArg(cx, args, 2, "WebAssembly.Module.customSections",
1315 &module)) {
1316 return false;
1319 Vector<char, 8> name(cx);
1321 RootedString str(cx, ToString(cx, args.get(1)));
1322 if (!str) {
1323 return false;
1326 Rooted<JSLinearString*> linear(cx, str->ensureLinear(cx));
1327 if (!linear) {
1328 return false;
1331 if (!name.initLengthUninitialized(
1332 JS::GetDeflatedUTF8StringLength(linear))) {
1333 return false;
1336 (void)JS::DeflateStringToUTF8Buffer(linear,
1337 Span(name.begin(), name.length()));
1340 RootedValueVector elems(cx);
1341 Rooted<ArrayBufferObject*> buf(cx);
1342 for (const CustomSection& cs : module->customSections()) {
1343 if (name.length() != cs.name.length()) {
1344 continue;
1346 if (memcmp(name.begin(), cs.name.begin(), name.length()) != 0) {
1347 continue;
1350 buf = ArrayBufferObject::createZeroed(cx, cs.payload->length());
1351 if (!buf) {
1352 return false;
1355 memcpy(buf->dataPointer(), cs.payload->begin(), cs.payload->length());
1356 if (!elems.append(ObjectValue(*buf))) {
1357 return false;
1361 JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
1362 if (!arr) {
1363 return false;
1366 args.rval().setObject(*arr);
1367 return true;
1370 /* static */
1371 WasmModuleObject* WasmModuleObject::create(JSContext* cx, const Module& module,
1372 HandleObject proto) {
1373 AutoSetNewObjectMetadata metadata(cx);
1374 auto* obj = NewObjectWithGivenProto<WasmModuleObject>(cx, proto);
1375 if (!obj) {
1376 return nullptr;
1379 // The pipeline state on some architectures may retain stale instructions
1380 // even after we invalidate the instruction cache. There is no generally
1381 // available method to broadcast this pipeline flush to all threads after
1382 // we've compiled new code, so conservatively perform one here when we're
1383 // receiving a module that may have been compiled from another thread.
1385 // The cost of this flush is expected to minimal enough to not be worth
1386 // optimizing away in the case the module was compiled on this thread.
1387 jit::FlushExecutionContext();
1389 // This accounts for module allocation size (excluding code which is handled
1390 // separately - see below). This assumes that the size of associated data
1391 // doesn't change for the life of the WasmModuleObject. The size is counted
1392 // once per WasmModuleObject referencing a Module.
1393 InitReservedSlot(obj, MODULE_SLOT, const_cast<Module*>(&module),
1394 module.gcMallocBytesExcludingCode(), MemoryUse::WasmModule);
1395 module.AddRef();
1397 // Bug 1569888: We account for the first tier here; the second tier, if
1398 // different, also needs to be accounted for.
1399 cx->zone()->incJitMemory(module.codeLength(module.code().stableTier()));
1400 return obj;
1403 static bool GetBufferSource(JSContext* cx, JSObject* obj, unsigned errorNumber,
1404 MutableBytes* bytecode) {
1405 *bytecode = cx->new_<ShareableBytes>();
1406 if (!*bytecode) {
1407 return false;
1410 JSObject* unwrapped = CheckedUnwrapStatic(obj);
1412 SharedMem<uint8_t*> dataPointer;
1413 size_t byteLength;
1414 if (!unwrapped || !IsBufferSource(unwrapped, &dataPointer, &byteLength)) {
1415 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
1416 return false;
1419 if (!(*bytecode)->append(dataPointer.unwrap(), byteLength)) {
1420 ReportOutOfMemory(cx);
1421 return false;
1424 return true;
1427 static bool ReportCompileWarnings(JSContext* cx,
1428 const UniqueCharsVector& warnings) {
1429 // Avoid spamming the console.
1430 size_t numWarnings = std::min<size_t>(warnings.length(), 3);
1432 for (size_t i = 0; i < numWarnings; i++) {
1433 if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING, warnings[i].get())) {
1434 return false;
1438 if (warnings.length() > numWarnings) {
1439 if (!WarnNumberASCII(cx, JSMSG_WASM_COMPILE_WARNING,
1440 "other warnings suppressed")) {
1441 return false;
1445 return true;
1448 /* static */
1449 bool WasmModuleObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1450 CallArgs callArgs = CallArgsFromVp(argc, vp);
1452 Log(cx, "sync new Module() started");
1454 if (!ThrowIfNotConstructing(cx, callArgs, "Module")) {
1455 return false;
1458 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
1459 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1460 JSMSG_CSP_BLOCKED_WASM, "WebAssembly.Module");
1461 return false;
1464 if (!callArgs.requireAtLeast(cx, "WebAssembly.Module", 1)) {
1465 return false;
1468 if (!callArgs[0].isObject()) {
1469 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1470 JSMSG_WASM_BAD_BUF_ARG);
1471 return false;
1474 MutableBytes bytecode;
1475 if (!GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG,
1476 &bytecode)) {
1477 return false;
1480 FeatureOptions options;
1481 if (!options.init(cx, callArgs.get(1))) {
1482 return false;
1485 SharedCompileArgs compileArgs =
1486 InitCompileArgs(cx, options, "WebAssembly.Module");
1487 if (!compileArgs) {
1488 return false;
1491 UniqueChars error;
1492 UniqueCharsVector warnings;
1493 SharedModule module =
1494 CompileBuffer(*compileArgs, *bytecode, &error, &warnings, nullptr);
1496 if (!ReportCompileWarnings(cx, warnings)) {
1497 return false;
1499 if (!module) {
1500 if (error) {
1501 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1502 JSMSG_WASM_COMPILE_ERROR, error.get());
1503 return false;
1505 return ThrowCompileOutOfMemory(cx);
1508 RootedObject proto(
1509 cx, GetWasmConstructorPrototype(cx, callArgs, JSProto_WasmModule));
1510 if (!proto) {
1511 ReportOutOfMemory(cx);
1512 return false;
1515 RootedObject moduleObj(cx, WasmModuleObject::create(cx, *module, proto));
1516 if (!moduleObj) {
1517 return false;
1520 Log(cx, "sync new Module() succeded");
1522 callArgs.rval().setObject(*moduleObj);
1523 return true;
1526 const Module& WasmModuleObject::module() const {
1527 MOZ_ASSERT(is<WasmModuleObject>());
1528 return *(const Module*)getReservedSlot(MODULE_SLOT).toPrivate();
1531 // ============================================================================
1532 // WebAssembly.Instance class and methods
1534 const JSClassOps WasmInstanceObject::classOps_ = {
1535 nullptr, // addProperty
1536 nullptr, // delProperty
1537 nullptr, // enumerate
1538 nullptr, // newEnumerate
1539 nullptr, // resolve
1540 nullptr, // mayResolve
1541 WasmInstanceObject::finalize, // finalize
1542 nullptr, // call
1543 nullptr, // construct
1544 WasmInstanceObject::trace, // trace
1547 const JSClass WasmInstanceObject::class_ = {
1548 "WebAssembly.Instance",
1549 JSCLASS_DELAY_METADATA_BUILDER |
1550 JSCLASS_HAS_RESERVED_SLOTS(WasmInstanceObject::RESERVED_SLOTS) |
1551 JSCLASS_FOREGROUND_FINALIZE,
1552 &WasmInstanceObject::classOps_,
1553 &WasmInstanceObject::classSpec_,
1556 const JSClass& WasmInstanceObject::protoClass_ = PlainObject::class_;
1558 static constexpr char WasmInstanceName[] = "Instance";
1560 const ClassSpec WasmInstanceObject::classSpec_ = {
1561 CreateWasmConstructor<WasmInstanceObject, WasmInstanceName>,
1562 GenericCreatePrototype<WasmInstanceObject>,
1563 WasmInstanceObject::static_methods,
1564 nullptr,
1565 WasmInstanceObject::methods,
1566 WasmInstanceObject::properties,
1567 nullptr,
1568 ClassSpec::DontDefineConstructor};
1570 static bool IsInstance(HandleValue v) {
1571 return v.isObject() && v.toObject().is<WasmInstanceObject>();
1574 /* static */
1575 bool WasmInstanceObject::exportsGetterImpl(JSContext* cx,
1576 const CallArgs& args) {
1577 args.rval().setObject(
1578 args.thisv().toObject().as<WasmInstanceObject>().exportsObj());
1579 return true;
1582 /* static */
1583 bool WasmInstanceObject::exportsGetter(JSContext* cx, unsigned argc,
1584 Value* vp) {
1585 CallArgs args = CallArgsFromVp(argc, vp);
1586 return CallNonGenericMethod<IsInstance, exportsGetterImpl>(cx, args);
1589 const JSPropertySpec WasmInstanceObject::properties[] = {
1590 JS_PSG("exports", WasmInstanceObject::exportsGetter, JSPROP_ENUMERATE),
1591 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Instance", JSPROP_READONLY),
1592 JS_PS_END};
1594 const JSFunctionSpec WasmInstanceObject::methods[] = {JS_FS_END};
1596 const JSFunctionSpec WasmInstanceObject::static_methods[] = {JS_FS_END};
1598 bool WasmInstanceObject::isNewborn() const {
1599 MOZ_ASSERT(is<WasmInstanceObject>());
1600 return getReservedSlot(INSTANCE_SLOT).isUndefined();
1603 // WeakScopeMap maps from function index to js::Scope. This maps is weak
1604 // to avoid holding scope objects alive. The scopes are normally created
1605 // during debugging.
1607 // This is defined here in order to avoid recursive dependency between
1608 // WasmJS.h and Scope.h.
1609 using WasmFunctionScopeMap =
1610 WeakCache<GCHashMap<uint32_t, WeakHeapPtr<WasmFunctionScope*>,
1611 DefaultHasher<uint32_t>, CellAllocPolicy>>;
1612 class WasmInstanceObject::UnspecifiedScopeMap {
1613 public:
1614 WasmFunctionScopeMap& asWasmFunctionScopeMap() {
1615 return *(WasmFunctionScopeMap*)this;
1619 /* static */
1620 void WasmInstanceObject::finalize(JS::GCContext* gcx, JSObject* obj) {
1621 WasmInstanceObject& instance = obj->as<WasmInstanceObject>();
1622 gcx->delete_(obj, &instance.exports(), MemoryUse::WasmInstanceExports);
1623 gcx->delete_(obj, &instance.scopes().asWasmFunctionScopeMap(),
1624 MemoryUse::WasmInstanceScopes);
1625 gcx->delete_(obj, &instance.indirectGlobals(),
1626 MemoryUse::WasmInstanceGlobals);
1627 if (!instance.isNewborn()) {
1628 if (instance.instance().debugEnabled()) {
1629 instance.instance().debug().finalize(gcx);
1631 Instance::destroy(&instance.instance());
1632 gcx->removeCellMemory(obj, sizeof(Instance),
1633 MemoryUse::WasmInstanceInstance);
1637 /* static */
1638 void WasmInstanceObject::trace(JSTracer* trc, JSObject* obj) {
1639 WasmInstanceObject& instanceObj = obj->as<WasmInstanceObject>();
1640 instanceObj.exports().trace(trc);
1641 instanceObj.indirectGlobals().trace(trc);
1642 if (!instanceObj.isNewborn()) {
1643 instanceObj.instance().tracePrivate(trc);
1647 /* static */
1648 WasmInstanceObject* WasmInstanceObject::create(
1649 JSContext* cx, const SharedCode& code,
1650 const DataSegmentVector& dataSegments,
1651 const ModuleElemSegmentVector& elemSegments, uint32_t instanceDataLength,
1652 Handle<WasmMemoryObjectVector> memories, SharedTableVector&& tables,
1653 const JSObjectVector& funcImports, const GlobalDescVector& globals,
1654 const ValVector& globalImportValues,
1655 const WasmGlobalObjectVector& globalObjs,
1656 const WasmTagObjectVector& tagObjs, HandleObject proto,
1657 UniqueDebugState maybeDebug) {
1658 Rooted<UniquePtr<ExportMap>> exports(cx,
1659 js::MakeUnique<ExportMap>(cx->zone()));
1660 if (!exports) {
1661 ReportOutOfMemory(cx);
1662 return nullptr;
1665 UniquePtr<WasmFunctionScopeMap> scopes =
1666 js::MakeUnique<WasmFunctionScopeMap>(cx->zone(), cx->zone());
1667 if (!scopes) {
1668 ReportOutOfMemory(cx);
1669 return nullptr;
1671 // Note that `scopes` is a WeakCache, auto-linked into a sweep list on the
1672 // Zone, and so does not require rooting.
1674 uint32_t indirectGlobals = 0;
1676 for (uint32_t i = 0; i < globalObjs.length(); i++) {
1677 if (globalObjs[i] && globals[i].isIndirect()) {
1678 indirectGlobals++;
1682 Rooted<UniquePtr<GlobalObjectVector>> indirectGlobalObjs(
1683 cx, js::MakeUnique<GlobalObjectVector>(cx->zone()));
1684 if (!indirectGlobalObjs || !indirectGlobalObjs->resize(indirectGlobals)) {
1685 ReportOutOfMemory(cx);
1686 return nullptr;
1690 uint32_t next = 0;
1691 for (uint32_t i = 0; i < globalObjs.length(); i++) {
1692 if (globalObjs[i] && globals[i].isIndirect()) {
1693 (*indirectGlobalObjs)[next++] = globalObjs[i];
1698 Instance* instance = nullptr;
1699 Rooted<WasmInstanceObject*> obj(cx);
1702 // We must delay creating metadata for this object until after all its
1703 // slots have been initialized. We must also create the metadata before
1704 // calling Instance::init as that may allocate new objects.
1705 AutoSetNewObjectMetadata metadata(cx);
1706 obj = NewObjectWithGivenProto<WasmInstanceObject>(cx, proto);
1707 if (!obj) {
1708 return nullptr;
1711 MOZ_ASSERT(obj->isTenured(), "assumed by WasmTableObject write barriers");
1713 // Finalization assumes these slots are always initialized:
1714 InitReservedSlot(obj, EXPORTS_SLOT, exports.release(),
1715 MemoryUse::WasmInstanceExports);
1717 InitReservedSlot(obj, SCOPES_SLOT, scopes.release(),
1718 MemoryUse::WasmInstanceScopes);
1720 InitReservedSlot(obj, GLOBALS_SLOT, indirectGlobalObjs.release(),
1721 MemoryUse::WasmInstanceGlobals);
1723 obj->initReservedSlot(INSTANCE_SCOPE_SLOT, UndefinedValue());
1725 // The INSTANCE_SLOT may not be initialized if Instance allocation fails,
1726 // leading to an observable "newborn" state in tracing/finalization.
1727 MOZ_ASSERT(obj->isNewborn());
1729 // Create this just before constructing Instance to avoid rooting hazards.
1730 instance = Instance::create(cx, obj, code, instanceDataLength,
1731 std::move(tables), std::move(maybeDebug));
1732 if (!instance) {
1733 return nullptr;
1736 InitReservedSlot(obj, INSTANCE_SLOT, instance,
1737 MemoryUse::WasmInstanceInstance);
1738 MOZ_ASSERT(!obj->isNewborn());
1741 if (!instance->init(cx, funcImports, globalImportValues, memories, globalObjs,
1742 tagObjs, dataSegments, elemSegments)) {
1743 return nullptr;
1746 return obj;
1749 void WasmInstanceObject::initExportsObj(JSObject& exportsObj) {
1750 MOZ_ASSERT(getReservedSlot(EXPORTS_OBJ_SLOT).isUndefined());
1751 setReservedSlot(EXPORTS_OBJ_SLOT, ObjectValue(exportsObj));
1754 static bool GetImportArg(JSContext* cx, HandleValue importArg,
1755 MutableHandleObject importObj) {
1756 if (!importArg.isUndefined()) {
1757 if (!importArg.isObject()) {
1758 return ThrowBadImportArg(cx);
1760 importObj.set(&importArg.toObject());
1762 return true;
1765 /* static */
1766 bool WasmInstanceObject::construct(JSContext* cx, unsigned argc, Value* vp) {
1767 CallArgs args = CallArgsFromVp(argc, vp);
1769 Log(cx, "sync new Instance() started");
1771 if (!ThrowIfNotConstructing(cx, args, "Instance")) {
1772 return false;
1775 if (!args.requireAtLeast(cx, "WebAssembly.Instance", 1)) {
1776 return false;
1779 const Module* module;
1780 if (!args[0].isObject() || !IsModuleObject(&args[0].toObject(), &module)) {
1781 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
1782 JSMSG_WASM_BAD_MOD_ARG);
1783 return false;
1786 RootedObject importObj(cx);
1787 if (!GetImportArg(cx, args.get(1), &importObj)) {
1788 return false;
1791 RootedObject proto(
1792 cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmInstance));
1793 if (!proto) {
1794 ReportOutOfMemory(cx);
1795 return false;
1798 Rooted<ImportValues> imports(cx);
1799 if (!GetImports(cx, *module, importObj, imports.address())) {
1800 return false;
1803 Rooted<WasmInstanceObject*> instanceObj(cx);
1804 if (!module->instantiate(cx, imports.get(), proto, &instanceObj)) {
1805 return false;
1808 Log(cx, "sync new Instance() succeeded");
1810 args.rval().setObject(*instanceObj);
1811 return true;
1814 Instance& WasmInstanceObject::instance() const {
1815 MOZ_ASSERT(!isNewborn());
1816 return *(Instance*)getReservedSlot(INSTANCE_SLOT).toPrivate();
1819 JSObject& WasmInstanceObject::exportsObj() const {
1820 return getReservedSlot(EXPORTS_OBJ_SLOT).toObject();
1823 WasmInstanceObject::ExportMap& WasmInstanceObject::exports() const {
1824 return *(ExportMap*)getReservedSlot(EXPORTS_SLOT).toPrivate();
1827 WasmInstanceObject::UnspecifiedScopeMap& WasmInstanceObject::scopes() const {
1828 return *(UnspecifiedScopeMap*)(getReservedSlot(SCOPES_SLOT).toPrivate());
1831 WasmInstanceObject::GlobalObjectVector& WasmInstanceObject::indirectGlobals()
1832 const {
1833 return *(GlobalObjectVector*)getReservedSlot(GLOBALS_SLOT).toPrivate();
1836 static bool WasmCall(JSContext* cx, unsigned argc, Value* vp) {
1837 CallArgs args = CallArgsFromVp(argc, vp);
1838 RootedFunction callee(cx, &args.callee().as<JSFunction>());
1840 Instance& instance = ExportedFunctionToInstance(callee);
1841 uint32_t funcIndex = ExportedFunctionToFuncIndex(callee);
1842 return instance.callExport(cx, funcIndex, args);
1846 * [SMDOC] Exported wasm functions and the jit-entry stubs
1848 * ## The kinds of exported functions
1850 * There are several kinds of exported wasm functions. /Explicitly/ exported
1851 * functions are:
1853 * - any wasm function exported via the export section
1854 * - any asm.js export
1855 * - the module start function
1857 * There are also /implicitly/ exported functions, these are the functions whose
1858 * indices in the module are referenced outside the code segment, eg, in element
1859 * segments and in global initializers.
1861 * ## Wasm functions as JSFunctions
1863 * Any exported function can be manipulated by JS and wasm code, and to both the
1864 * exported function is represented as a JSFunction. To JS, that means that the
1865 * function can be called in the same way as any other JSFunction. To Wasm, it
1866 * means that the function is a reference with the same representation as
1867 * externref.
1869 * However, the JSFunction object is created only when the function value is
1870 * actually exposed to JS the first time. The creation is performed by
1871 * getExportedFunction(), below, as follows:
1873 * - A function exported via the export section (or from asm.js) is created
1874 * when the export object is created, which happens at instantiation time.
1876 * - A function implicitly exported via a table is created when the table
1877 * element is read (by JS or wasm) and a function value is needed to
1878 * represent that value. Functions stored in tables by initializers have a
1879 * special representation that does not require the function object to be
1880 * created, as long as the initializing element segment uses the more
1881 * efficient index encoding instead of the more general expression encoding.
1883 * - A function implicitly exported via a global initializer is created when
1884 * the global is initialized.
1886 * - A function referenced from a ref.func instruction in code is created when
1887 * that instruction is executed the first time.
1889 * The JSFunction representing a wasm function never changes: every reference to
1890 * the wasm function that exposes the JSFunction gets the same JSFunction. In
1891 * particular, imported functions already have a JSFunction representation (from
1892 * JS or from their home module), and will be exposed using that representation.
1894 * The mapping from a wasm function to its JSFunction is instance-specific, and
1895 * held in a hashmap in the instance. If a module is shared across multiple
1896 * instances, possibly in multiple threads, each instance will have its own
1897 * JSFunction representing the wasm function.
1899 * ## Stubs -- interpreter, eager, lazy, provisional, and absent
1901 * While a Wasm exported function is just a JSFunction, the internal wasm ABI is
1902 * neither the C++ ABI nor the JS JIT ABI, so there needs to be an extra step
1903 * when C++ or JS JIT code calls wasm code. For this, execution passes through
1904 * a stub that is adapted to both the JS caller and the wasm callee.
1906 * ### Interpreter stubs and jit-entry stubs
1908 * When JS interpreted code calls a wasm function, we end up in
1909 * Instance::callExport() to execute the call. This function must enter wasm,
1910 * and to do this it uses a stub that is specific to the wasm function (see
1911 * GenerateInterpEntry) that is callable with the C++ interpreter ABI and which
1912 * will convert arguments as necessary and enter compiled wasm code.
1914 * The interpreter stub is created eagerly, when the module is compiled.
1916 * However, the interpreter call path is slow, and when JS jitted code calls
1917 * wasm we want to do better. In this case, there is a different, optimized
1918 * stub that is to be invoked, and it uses the JIT ABI. This is the jit-entry
1919 * stub for the function. Jitted code will call a wasm function's jit-entry
1920 * stub to invoke the function with the JIT ABI. The stub will adapt the call
1921 * to the wasm ABI.
1923 * Some jit-entry stubs are created eagerly and some are created lazily.
1925 * ### Eager jit-entry stubs
1927 * The explicitly exported functions have stubs created for them eagerly. Eager
1928 * stubs are created with their tier when the module is compiled, see
1929 * ModuleGenerator::finishCodeTier(), which calls wasm::GenerateStubs(), which
1930 * generates stubs for functions with eager stubs.
1932 * An eager stub for tier-1 is upgraded to tier-2 if the module tiers up, see
1933 * below.
1935 * ### Lazy jit-entry stubs
1937 * Stubs are created lazily for all implicitly exported functions. These
1938 * functions may flow out to JS, but will only need a stub if they are ever
1939 * called from jitted code. (That's true for explicitly exported functions too,
1940 * but for them the presumption is that they will be called.)
1942 * Lazy stubs are created only when they are needed, and they are /doubly/ lazy,
1943 * see getExportedFunction(), below: A function implicitly exported via a table
1944 * or global may be manipulated eagerly by host code without actually being
1945 * called (maybe ever), so we do not generate a lazy stub when the function
1946 * object escapes to JS, but instead delay stub generation until the function is
1947 * actually called.
1949 * ### The provisional lazy jit-entry stub
1951 * However, JS baseline compilation needs to have a stub to start with in order
1952 * to allow it to attach CacheIR data to the call (or it deoptimizes the call as
1953 * a C++ call). Thus when the JSFunction for the wasm export is retrieved by JS
1954 * code, a /provisional/ lazy jit-entry stub is associated with the function.
1955 * The stub will invoke the wasm function on the slow interpreter path via
1956 * callExport - if the function is ever called - and will cause a fast jit-entry
1957 * stub to be created at the time of the call. The provisional lazy stub is
1958 * shared globally, it contains no function-specific or context-specific data.
1960 * Thus, the final lazy jit-entry stubs are eventually created by
1961 * Instance::callExport, when a call is routed through it on the slow path for
1962 * any of the reasons given above.
1964 * ### Absent jit-entry stubs
1966 * Some functions never get jit-entry stubs. The predicate canHaveJitEntry()
1967 * determines if a wasm function gets a stub, and it will deny this if the
1968 * function's signature exposes non-JS-compatible types (such as v128) or if
1969 * stub optimization has been disabled by a jit option. Calls to these
1970 * functions will continue to go via callExport and use the slow interpreter
1971 * stub.
1973 * ## The jit-entry jump table
1975 * The mapping from the exported function to its jit-entry stub is implemented
1976 * by the jit-entry jump table in the JumpTables object (see WasmCode.h). The
1977 * jit-entry jump table entry for a function holds a stub that the jit can call
1978 * to perform fast calls.
1980 * While there is a single contiguous jump table, it has two logical sections:
1981 * one for eager stubs, and one for lazy stubs. These sections are initialized
1982 * and updated separately, using logic that is specific to each section.
1984 * The value of the table element for an eager stub is a pointer to the stub
1985 * code in the current tier. The pointer is installed just after the creation
1986 * of the stub, before any code in the module is executed. If the module later
1987 * tiers up, the eager jit-entry stub for tier-1 code is replaced by one for
1988 * tier-2 code, see the next section.
1990 * Initially the value of the jump table element for a lazy stub is null.
1992 * If the function is retrieved by JS (by getExportedFunction()) and is not
1993 * barred from having a jit-entry, then the stub is upgraded to the shared
1994 * provisional lazy jit-entry stub. This upgrade happens to be racy if the
1995 * module is shared, and so the update is atomic and only happens if the entry
1996 * is already null. Since the provisional lazy stub is shared, this is fine; if
1997 * several threads try to upgrade at the same time, it is to the same shared
1998 * value.
2000 * If the retrieved function is later invoked (via callExport()), the stub is
2001 * upgraded to an actual jit-entry stub for the current code tier, again if the
2002 * function is allowed to have a jit-entry. This is not racy -- though multiple
2003 * threads can be trying to create a jit-entry stub at the same time, they do so
2004 * under a lock and only the first to take the lock will be allowed to create a
2005 * stub, the others will reuse the first-installed stub.
2007 * If the module later tiers up, the lazy jit-entry stub for tier-1 code (if it
2008 * exists) is replaced by one for tier-2 code, see the next section.
2010 * (Note, the InterpEntry stub is never stored in the jit-entry table, as it
2011 * uses the C++ ABI, not the JIT ABI. It is accessible through the
2012 * FunctionEntry.)
2014 * ### Interaction of the jit-entry jump table and tiering
2016 * (For general info about tiering, see the comment in WasmCompile.cpp.)
2018 * The jit-entry stub, whether eager or lazy, is specific to a code tier - a
2019 * stub will invoke the code for its function for the tier. When we tier up,
2020 * new jit-entry stubs must be created that reference tier-2 code, and must then
2021 * be patched into the jit-entry table. The complication here is that, since
2022 * the jump table is shared with its code between instances on multiple threads,
2023 * tier-1 code is running on other threads and new tier-1 specific jit-entry
2024 * stubs may be created concurrently with trying to create the tier-2 stubs on
2025 * the thread that performs the tiering-up. Indeed, there may also be
2026 * concurrent attempts to upgrade null jit-entries to the provisional lazy stub.
2028 * Eager stubs:
2030 * - Eager stubs for tier-2 code are patched in racily by Module::finishTier2()
2031 * along with code pointers for tiering; nothing conflicts with these writes.
2033 * Lazy stubs:
2035 * - An upgrade from a null entry to a lazy provisional stub is atomic and can
2036 * only happen if the entry is null, and it only happens in
2037 * getExportedFunction(). No lazy provisional stub will be installed if
2038 * there's another stub present.
2040 * - The lazy tier-appropriate stub is installed by callExport() (really by
2041 * EnsureEntryStubs()) during the first invocation of the exported function
2042 * that reaches callExport(). That invocation must be from within JS, and so
2043 * the jit-entry element can't be null, because a prior getExportedFunction()
2044 * will have ensured that it is not: the lazy provisional stub will have been
2045 * installed. Hence the installing of the lazy tier-appropriate stub does
2046 * not race with the installing of the lazy provisional stub.
2048 * - A lazy tier-1 stub is upgraded to a lazy tier-2 stub by
2049 * Module::finishTier2(). The upgrade needs to ensure that all tier-1 stubs
2050 * are upgraded, and that once the upgrade is finished, callExport() will
2051 * only create tier-2 lazy stubs. (This upgrading does not upgrade lazy
2052 * provisional stubs or absent stubs.)
2054 * The locking protocol ensuring that all stubs are upgraded properly and
2055 * that the system switches to creating tier-2 stubs is implemented in
2056 * Module::finishTier2() and EnsureEntryStubs():
2058 * There are two locks, one per code tier.
2060 * EnsureEntryStubs() is attempting to create a tier-appropriate lazy stub,
2061 * so it takes the lock for the current best tier, checks to see if there is
2062 * a stub, and exits if there is. If the tier changed racily it takes the
2063 * other lock too, since that is now the lock for the best tier. Then it
2064 * creates the stub, installs it, and releases the locks. Thus at most one
2065 * stub per tier can be created at a time.
2067 * Module::finishTier2() takes both locks (tier-1 before tier-2), thus
2068 * preventing EnsureEntryStubs() from creating stubs while stub upgrading is
2069 * going on, and itself waiting until EnsureEntryStubs() is not active. Once
2070 * it has both locks, it upgrades all lazy stubs and makes tier-2 the new
2071 * best tier. Should EnsureEntryStubs subsequently enter, it will find that
2072 * a stub already exists at tier-2 and will exit early.
2074 * (It would seem that the locking protocol could be simplified a little by
2075 * having only one lock, hanging off the Code object, or by unconditionally
2076 * taking both locks in EnsureEntryStubs(). However, in some cases where we
2077 * acquire a lock the Code object is not readily available, so plumbing would
2078 * have to be added, and in EnsureEntryStubs(), there are sometimes not two code
2079 * tiers.)
2081 * ## Stub lifetimes and serialization
2083 * Eager jit-entry stub code, along with stub code for import functions, is
2084 * serialized along with the tier-2 code for the module.
2086 * Lazy stub code and thunks for builtin functions (including the provisional
2087 * lazy jit-entry stub) are never serialized.
2090 /* static */
2091 bool WasmInstanceObject::getExportedFunction(
2092 JSContext* cx, Handle<WasmInstanceObject*> instanceObj, uint32_t funcIndex,
2093 MutableHandleFunction fun) {
2094 if (ExportMap::Ptr p = instanceObj->exports().lookup(funcIndex)) {
2095 fun.set(p->value());
2096 return true;
2099 const Instance& instance = instanceObj->instance();
2100 const FuncExport& funcExport =
2101 instance.metadata(instance.code().bestTier()).lookupFuncExport(funcIndex);
2102 const TypeDef& funcTypeDef =
2103 instance.metadata().getFuncExportTypeDef(funcExport);
2104 unsigned numArgs = funcTypeDef.funcType().args().length();
2106 if (instance.isAsmJS()) {
2107 // asm.js needs to act like a normal JS function which means having the
2108 // name from the original source and being callable as a constructor.
2109 Rooted<JSAtom*> name(cx, instance.getFuncDisplayAtom(cx, funcIndex));
2110 if (!name) {
2111 return false;
2113 fun.set(NewNativeConstructor(cx, WasmCall, numArgs, name,
2114 gc::AllocKind::FUNCTION_EXTENDED,
2115 TenuredObject, FunctionFlags::ASMJS_CTOR));
2116 if (!fun) {
2117 return false;
2120 // asm.js does not support jit entries.
2121 fun->setWasmFuncIndex(funcIndex);
2122 } else {
2123 Rooted<JSAtom*> name(cx, NumberToAtom(cx, funcIndex));
2124 if (!name) {
2125 return false;
2127 RootedObject proto(cx);
2128 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
2129 proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmFunction);
2130 if (!proto) {
2131 return false;
2133 #endif
2134 fun.set(NewFunctionWithProto(
2135 cx, WasmCall, numArgs, FunctionFlags::WASM, nullptr, name, proto,
2136 gc::AllocKind::FUNCTION_EXTENDED, TenuredObject));
2137 if (!fun) {
2138 return false;
2141 // Some applications eagerly access all table elements which currently
2142 // triggers worst-case behavior for lazy stubs, since each will allocate a
2143 // separate 4kb code page. Most eagerly-accessed functions are not called,
2144 // so use a shared, provisional (and slow) lazy stub as JitEntry and wait
2145 // until Instance::callExport() to create the fast entry stubs.
2146 if (funcTypeDef.funcType().canHaveJitEntry()) {
2147 if (!funcExport.hasEagerStubs()) {
2148 if (!EnsureBuiltinThunksInitialized()) {
2149 return false;
2151 void* provisionalLazyJitEntryStub = ProvisionalLazyJitEntryStub();
2152 MOZ_ASSERT(provisionalLazyJitEntryStub);
2153 instance.code().setJitEntryIfNull(funcIndex,
2154 provisionalLazyJitEntryStub);
2156 fun->setWasmJitEntry(instance.code().getAddressOfJitEntry(funcIndex));
2157 } else {
2158 fun->setWasmFuncIndex(funcIndex);
2162 fun->setExtendedSlot(FunctionExtended::WASM_INSTANCE_SLOT,
2163 PrivateValue(const_cast<Instance*>(&instance)));
2164 fun->setExtendedSlot(FunctionExtended::WASM_STV_SLOT,
2165 PrivateValue((void*)funcTypeDef.superTypeVector()));
2167 const CodeTier& codeTier =
2168 instance.code().codeTier(instance.code().bestTier());
2169 const CodeRange& codeRange = codeTier.metadata().codeRange(funcExport);
2171 fun->setExtendedSlot(FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT,
2172 PrivateValue(codeTier.segment().base() +
2173 codeRange.funcUncheckedCallEntry()));
2175 if (!instanceObj->exports().putNew(funcIndex, fun)) {
2176 ReportOutOfMemory(cx);
2177 return false;
2180 return true;
2183 const CodeRange& WasmInstanceObject::getExportedFunctionCodeRange(
2184 JSFunction* fun, Tier tier) {
2185 uint32_t funcIndex = ExportedFunctionToFuncIndex(fun);
2186 MOZ_ASSERT(exports().lookup(funcIndex)->value() == fun);
2187 const MetadataTier& metadata = instance().metadata(tier);
2188 return metadata.codeRange(metadata.lookupFuncExport(funcIndex));
2191 /* static */
2192 WasmInstanceScope* WasmInstanceObject::getScope(
2193 JSContext* cx, Handle<WasmInstanceObject*> instanceObj) {
2194 if (!instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT).isUndefined()) {
2195 return (WasmInstanceScope*)instanceObj->getReservedSlot(INSTANCE_SCOPE_SLOT)
2196 .toGCThing();
2199 Rooted<WasmInstanceScope*> instanceScope(
2200 cx, WasmInstanceScope::create(cx, instanceObj));
2201 if (!instanceScope) {
2202 return nullptr;
2205 instanceObj->setReservedSlot(INSTANCE_SCOPE_SLOT,
2206 PrivateGCThingValue(instanceScope));
2208 return instanceScope;
2211 /* static */
2212 WasmFunctionScope* WasmInstanceObject::getFunctionScope(
2213 JSContext* cx, Handle<WasmInstanceObject*> instanceObj,
2214 uint32_t funcIndex) {
2215 if (auto p =
2216 instanceObj->scopes().asWasmFunctionScopeMap().lookup(funcIndex)) {
2217 return p->value();
2220 Rooted<WasmInstanceScope*> instanceScope(
2221 cx, WasmInstanceObject::getScope(cx, instanceObj));
2222 if (!instanceScope) {
2223 return nullptr;
2226 Rooted<WasmFunctionScope*> funcScope(
2227 cx, WasmFunctionScope::create(cx, instanceScope, funcIndex));
2228 if (!funcScope) {
2229 return nullptr;
2232 if (!instanceObj->scopes().asWasmFunctionScopeMap().putNew(funcIndex,
2233 funcScope)) {
2234 ReportOutOfMemory(cx);
2235 return nullptr;
2238 return funcScope;
2241 bool wasm::IsWasmExportedFunction(JSFunction* fun) {
2242 return fun->kind() == FunctionFlags::Wasm;
2245 Instance& wasm::ExportedFunctionToInstance(JSFunction* fun) {
2246 return fun->wasmInstance();
2249 WasmInstanceObject* wasm::ExportedFunctionToInstanceObject(JSFunction* fun) {
2250 return fun->wasmInstance().object();
2253 uint32_t wasm::ExportedFunctionToFuncIndex(JSFunction* fun) {
2254 return fun->wasmInstance().code().getFuncIndex(fun);
2257 // ============================================================================
2258 // WebAssembly.Memory class and methods
2260 const JSClassOps WasmMemoryObject::classOps_ = {
2261 nullptr, // addProperty
2262 nullptr, // delProperty
2263 nullptr, // enumerate
2264 nullptr, // newEnumerate
2265 nullptr, // resolve
2266 nullptr, // mayResolve
2267 WasmMemoryObject::finalize, // finalize
2268 nullptr, // call
2269 nullptr, // construct
2270 nullptr, // trace
2273 const JSClass WasmMemoryObject::class_ = {
2274 "WebAssembly.Memory",
2275 JSCLASS_DELAY_METADATA_BUILDER |
2276 JSCLASS_HAS_RESERVED_SLOTS(WasmMemoryObject::RESERVED_SLOTS) |
2277 JSCLASS_FOREGROUND_FINALIZE,
2278 &WasmMemoryObject::classOps_, &WasmMemoryObject::classSpec_};
2280 const JSClass& WasmMemoryObject::protoClass_ = PlainObject::class_;
2282 static constexpr char WasmMemoryName[] = "Memory";
2284 static JSObject* CreateWasmMemoryPrototype(JSContext* cx, JSProtoKey key) {
2285 RootedObject proto(cx, GlobalObject::createBlankPrototype(
2286 cx, cx->global(), &WasmMemoryObject::protoClass_));
2287 if (!proto) {
2288 return nullptr;
2290 if (MemoryControlAvailable(cx)) {
2291 if (!JS_DefineFunctions(cx, proto,
2292 WasmMemoryObject::memoryControlMethods)) {
2293 return nullptr;
2296 return proto;
2299 const ClassSpec WasmMemoryObject::classSpec_ = {
2300 CreateWasmConstructor<WasmMemoryObject, WasmMemoryName>,
2301 CreateWasmMemoryPrototype,
2302 WasmMemoryObject::static_methods,
2303 nullptr,
2304 WasmMemoryObject::methods,
2305 WasmMemoryObject::properties,
2306 nullptr,
2307 ClassSpec::DontDefineConstructor};
2309 /* static */
2310 void WasmMemoryObject::finalize(JS::GCContext* gcx, JSObject* obj) {
2311 WasmMemoryObject& memory = obj->as<WasmMemoryObject>();
2312 if (memory.hasObservers()) {
2313 gcx->delete_(obj, &memory.observers(), MemoryUse::WasmMemoryObservers);
2317 /* static */
2318 WasmMemoryObject* WasmMemoryObject::create(
2319 JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, bool isHuge,
2320 HandleObject proto) {
2321 AutoSetNewObjectMetadata metadata(cx);
2322 auto* obj = NewObjectWithGivenProto<WasmMemoryObject>(cx, proto);
2323 if (!obj) {
2324 return nullptr;
2327 obj->initReservedSlot(BUFFER_SLOT, ObjectValue(*buffer));
2328 obj->initReservedSlot(ISHUGE_SLOT, BooleanValue(isHuge));
2329 MOZ_ASSERT(!obj->hasObservers());
2331 return obj;
2334 /* static */
2335 bool WasmMemoryObject::construct(JSContext* cx, unsigned argc, Value* vp) {
2336 CallArgs args = CallArgsFromVp(argc, vp);
2338 if (!ThrowIfNotConstructing(cx, args, "Memory")) {
2339 return false;
2342 if (!args.requireAtLeast(cx, "WebAssembly.Memory", 1)) {
2343 return false;
2346 if (!args.get(0).isObject()) {
2347 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2348 JSMSG_WASM_BAD_DESC_ARG, "memory");
2349 return false;
2352 RootedObject obj(cx, &args[0].toObject());
2353 Limits limits;
2354 if (!GetLimits(cx, obj, LimitsKind::Memory, &limits) ||
2355 !CheckLimits(cx, MaxMemoryLimitField(limits.indexType),
2356 LimitsKind::Memory, &limits)) {
2357 return false;
2360 if (Pages(limits.initial) > MaxMemoryPages(limits.indexType)) {
2361 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2362 JSMSG_WASM_MEM_IMP_LIMIT);
2363 return false;
2365 MemoryDesc memory(limits);
2367 Rooted<ArrayBufferObjectMaybeShared*> buffer(cx,
2368 CreateWasmBuffer(cx, memory));
2369 if (!buffer) {
2370 return false;
2373 RootedObject proto(cx,
2374 GetWasmConstructorPrototype(cx, args, JSProto_WasmMemory));
2375 if (!proto) {
2376 ReportOutOfMemory(cx);
2377 return false;
2380 Rooted<WasmMemoryObject*> memoryObj(
2381 cx, WasmMemoryObject::create(
2382 cx, buffer, IsHugeMemoryEnabled(limits.indexType), proto));
2383 if (!memoryObj) {
2384 return false;
2387 args.rval().setObject(*memoryObj);
2388 return true;
2391 static bool IsMemory(HandleValue v) {
2392 return v.isObject() && v.toObject().is<WasmMemoryObject>();
2395 /* static */
2396 bool WasmMemoryObject::bufferGetterImpl(JSContext* cx, const CallArgs& args) {
2397 Rooted<WasmMemoryObject*> memoryObj(
2398 cx, &args.thisv().toObject().as<WasmMemoryObject>());
2399 Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &memoryObj->buffer());
2401 if (memoryObj->isShared()) {
2402 size_t memoryLength = memoryObj->volatileMemoryLength();
2403 MOZ_ASSERT(memoryLength >= buffer->byteLength());
2405 if (memoryLength > buffer->byteLength()) {
2406 Rooted<SharedArrayBufferObject*> newBuffer(
2407 cx, SharedArrayBufferObject::New(
2408 cx, memoryObj->sharedArrayRawBuffer(), memoryLength));
2409 if (!newBuffer) {
2410 return false;
2412 // OK to addReference after we try to allocate because the memoryObj
2413 // keeps the rawBuffer alive.
2414 if (!memoryObj->sharedArrayRawBuffer()->addReference()) {
2415 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2416 JSMSG_SC_SAB_REFCNT_OFLO);
2417 return false;
2419 buffer = newBuffer;
2420 memoryObj->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuffer));
2424 args.rval().setObject(*buffer);
2425 return true;
2428 /* static */
2429 bool WasmMemoryObject::bufferGetter(JSContext* cx, unsigned argc, Value* vp) {
2430 CallArgs args = CallArgsFromVp(argc, vp);
2431 return CallNonGenericMethod<IsMemory, bufferGetterImpl>(cx, args);
2434 const JSPropertySpec WasmMemoryObject::properties[] = {
2435 JS_PSG("buffer", WasmMemoryObject::bufferGetter, JSPROP_ENUMERATE),
2436 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Memory", JSPROP_READONLY),
2437 JS_PS_END};
2439 /* static */
2440 bool WasmMemoryObject::growImpl(JSContext* cx, const CallArgs& args) {
2441 Rooted<WasmMemoryObject*> memory(
2442 cx, &args.thisv().toObject().as<WasmMemoryObject>());
2444 if (!args.requireAtLeast(cx, "WebAssembly.Memory.grow", 1)) {
2445 return false;
2448 uint32_t delta;
2449 if (!EnforceRangeU32(cx, args.get(0), "Memory", "grow delta", &delta)) {
2450 return false;
2453 uint32_t ret = grow(memory, delta, cx);
2455 if (ret == uint32_t(-1)) {
2456 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
2457 "memory");
2458 return false;
2461 args.rval().setInt32(int32_t(ret));
2462 return true;
2465 /* static */
2466 bool WasmMemoryObject::grow(JSContext* cx, unsigned argc, Value* vp) {
2467 CallArgs args = CallArgsFromVp(argc, vp);
2468 return CallNonGenericMethod<IsMemory, growImpl>(cx, args);
2471 /* static */
2472 bool WasmMemoryObject::discardImpl(JSContext* cx, const CallArgs& args) {
2473 Rooted<WasmMemoryObject*> memory(
2474 cx, &args.thisv().toObject().as<WasmMemoryObject>());
2476 if (!args.requireAtLeast(cx, "WebAssembly.Memory.discard", 2)) {
2477 return false;
2480 uint64_t byteOffset;
2481 if (!EnforceRangeU64(cx, args.get(0), "Memory", "byte offset", &byteOffset)) {
2482 return false;
2485 uint64_t byteLen;
2486 if (!EnforceRangeU64(cx, args.get(1), "Memory", "length", &byteLen)) {
2487 return false;
2490 if (byteOffset % wasm::PageSize != 0 || byteLen % wasm::PageSize != 0) {
2491 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2492 JSMSG_WASM_UNALIGNED_ACCESS);
2493 return false;
2496 if (!wasm::MemoryBoundsCheck(byteOffset, byteLen,
2497 memory->volatileMemoryLength())) {
2498 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2499 JSMSG_WASM_OUT_OF_BOUNDS);
2500 return false;
2503 discard(memory, byteOffset, byteLen, cx);
2505 args.rval().setUndefined();
2506 return true;
2509 /* static */
2510 bool WasmMemoryObject::discard(JSContext* cx, unsigned argc, Value* vp) {
2511 CallArgs args = CallArgsFromVp(argc, vp);
2512 return CallNonGenericMethod<IsMemory, discardImpl>(cx, args);
2515 const JSFunctionSpec WasmMemoryObject::methods[] = {
2516 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
2517 JS_FN("type", WasmMemoryObject::type, 0, JSPROP_ENUMERATE),
2518 #endif
2519 JS_FN("grow", WasmMemoryObject::grow, 1, JSPROP_ENUMERATE), JS_FS_END};
2521 const JSFunctionSpec WasmMemoryObject::memoryControlMethods[] = {
2522 JS_FN("discard", WasmMemoryObject::discard, 2, JSPROP_ENUMERATE),
2523 JS_FS_END};
2525 const JSFunctionSpec WasmMemoryObject::static_methods[] = {JS_FS_END};
2527 ArrayBufferObjectMaybeShared& WasmMemoryObject::buffer() const {
2528 return getReservedSlot(BUFFER_SLOT)
2529 .toObject()
2530 .as<ArrayBufferObjectMaybeShared>();
2533 WasmSharedArrayRawBuffer* WasmMemoryObject::sharedArrayRawBuffer() const {
2534 MOZ_ASSERT(isShared());
2535 return buffer().as<SharedArrayBufferObject>().rawWasmBufferObject();
2538 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
2539 bool WasmMemoryObject::typeImpl(JSContext* cx, const CallArgs& args) {
2540 Rooted<WasmMemoryObject*> memoryObj(
2541 cx, &args.thisv().toObject().as<WasmMemoryObject>());
2542 RootedObject typeObj(
2543 cx, MemoryTypeToObject(cx, memoryObj->isShared(), memoryObj->indexType(),
2544 memoryObj->volatilePages(),
2545 memoryObj->sourceMaxPages()));
2546 if (!typeObj) {
2547 return false;
2549 args.rval().setObject(*typeObj);
2550 return true;
2553 bool WasmMemoryObject::type(JSContext* cx, unsigned argc, Value* vp) {
2554 CallArgs args = CallArgsFromVp(argc, vp);
2555 return CallNonGenericMethod<IsMemory, typeImpl>(cx, args);
2557 #endif
2559 size_t WasmMemoryObject::volatileMemoryLength() const {
2560 if (isShared()) {
2561 return sharedArrayRawBuffer()->volatileByteLength();
2563 return buffer().byteLength();
2566 wasm::Pages WasmMemoryObject::volatilePages() const {
2567 if (isShared()) {
2568 return sharedArrayRawBuffer()->volatileWasmPages();
2570 return buffer().wasmPages();
2573 wasm::Pages WasmMemoryObject::clampedMaxPages() const {
2574 if (isShared()) {
2575 return sharedArrayRawBuffer()->wasmClampedMaxPages();
2577 return buffer().wasmClampedMaxPages();
2580 Maybe<wasm::Pages> WasmMemoryObject::sourceMaxPages() const {
2581 if (isShared()) {
2582 return Some(sharedArrayRawBuffer()->wasmSourceMaxPages());
2584 return buffer().wasmSourceMaxPages();
2587 wasm::IndexType WasmMemoryObject::indexType() const {
2588 if (isShared()) {
2589 return sharedArrayRawBuffer()->wasmIndexType();
2591 return buffer().wasmIndexType();
2594 bool WasmMemoryObject::isShared() const {
2595 return buffer().is<SharedArrayBufferObject>();
2598 bool WasmMemoryObject::hasObservers() const {
2599 return !getReservedSlot(OBSERVERS_SLOT).isUndefined();
2602 WasmMemoryObject::InstanceSet& WasmMemoryObject::observers() const {
2603 MOZ_ASSERT(hasObservers());
2604 return *reinterpret_cast<InstanceSet*>(
2605 getReservedSlot(OBSERVERS_SLOT).toPrivate());
2608 WasmMemoryObject::InstanceSet* WasmMemoryObject::getOrCreateObservers(
2609 JSContext* cx) {
2610 if (!hasObservers()) {
2611 auto observers = MakeUnique<InstanceSet>(cx->zone(), cx->zone());
2612 if (!observers) {
2613 ReportOutOfMemory(cx);
2614 return nullptr;
2617 InitReservedSlot(this, OBSERVERS_SLOT, observers.release(),
2618 MemoryUse::WasmMemoryObservers);
2621 return &observers();
2624 bool WasmMemoryObject::isHuge() const {
2625 return getReservedSlot(ISHUGE_SLOT).toBoolean();
2628 bool WasmMemoryObject::movingGrowable() const {
2629 return !isHuge() && !buffer().wasmSourceMaxPages();
2632 size_t WasmMemoryObject::boundsCheckLimit() const {
2633 if (!buffer().isWasm() || isHuge()) {
2634 return buffer().byteLength();
2636 size_t mappedSize = buffer().wasmMappedSize();
2637 #if !defined(JS_64BIT)
2638 // See clamping performed in CreateSpecificWasmBuffer(). On 32-bit systems
2639 // we do not want to overflow a uint32_t. For the other 64-bit compilers,
2640 // all constraints are implied by the largest accepted value for a memory's
2641 // max field.
2642 MOZ_ASSERT(mappedSize < UINT32_MAX);
2643 #endif
2644 MOZ_ASSERT(mappedSize % wasm::PageSize == 0);
2645 MOZ_ASSERT(mappedSize >= wasm::GuardSize);
2646 MOZ_ASSERT(wasm::IsValidBoundsCheckImmediate(mappedSize - wasm::GuardSize));
2647 size_t limit = mappedSize - wasm::GuardSize;
2648 MOZ_ASSERT(limit <= MaxMemoryBoundsCheckLimit(indexType()));
2649 return limit;
2652 bool WasmMemoryObject::addMovingGrowObserver(JSContext* cx,
2653 WasmInstanceObject* instance) {
2654 MOZ_ASSERT(movingGrowable());
2656 InstanceSet* observers = getOrCreateObservers(cx);
2657 if (!observers) {
2658 return false;
2661 // A memory can be imported multiple times into an instance, but we only
2662 // register the instance as an observer once.
2663 if (!observers->put(instance)) {
2664 ReportOutOfMemory(cx);
2665 return false;
2668 return true;
2671 /* static */
2672 uint64_t WasmMemoryObject::growShared(Handle<WasmMemoryObject*> memory,
2673 uint64_t delta) {
2674 WasmSharedArrayRawBuffer* rawBuf = memory->sharedArrayRawBuffer();
2675 WasmSharedArrayRawBuffer::Lock lock(rawBuf);
2677 Pages oldNumPages = rawBuf->volatileWasmPages();
2678 Pages newPages = oldNumPages;
2679 if (!newPages.checkedIncrement(Pages(delta))) {
2680 return uint64_t(int64_t(-1));
2683 if (!rawBuf->wasmGrowToPagesInPlace(lock, memory->indexType(), newPages)) {
2684 return uint64_t(int64_t(-1));
2686 // New buffer objects will be created lazily in all agents (including in
2687 // this agent) by bufferGetterImpl, above, so no more work to do here.
2689 return oldNumPages.value();
2692 /* static */
2693 uint64_t WasmMemoryObject::grow(Handle<WasmMemoryObject*> memory,
2694 uint64_t delta, JSContext* cx) {
2695 if (memory->isShared()) {
2696 return growShared(memory, delta);
2699 Rooted<ArrayBufferObject*> oldBuf(cx,
2700 &memory->buffer().as<ArrayBufferObject>());
2702 #if !defined(JS_64BIT)
2703 // TODO (large ArrayBuffer): See more information at the definition of
2704 // MaxMemoryBytes().
2705 MOZ_ASSERT(MaxMemoryBytes(memory->indexType()) <= UINT32_MAX,
2706 "Avoid 32-bit overflows");
2707 #endif
2709 Pages oldNumPages = oldBuf->wasmPages();
2710 Pages newPages = oldNumPages;
2711 if (!newPages.checkedIncrement(Pages(delta))) {
2712 return uint64_t(int64_t(-1));
2715 ArrayBufferObject* newBuf;
2716 if (memory->movingGrowable()) {
2717 MOZ_ASSERT(!memory->isHuge());
2718 newBuf = ArrayBufferObject::wasmMovingGrowToPages(memory->indexType(),
2719 newPages, oldBuf, cx);
2720 } else {
2721 newBuf = ArrayBufferObject::wasmGrowToPagesInPlace(memory->indexType(),
2722 newPages, oldBuf, cx);
2724 if (!newBuf) {
2725 return uint64_t(int64_t(-1));
2728 memory->setReservedSlot(BUFFER_SLOT, ObjectValue(*newBuf));
2730 // Only notify moving-grow-observers after the BUFFER_SLOT has been updated
2731 // since observers will call buffer().
2732 if (memory->hasObservers()) {
2733 for (InstanceSet::Range r = memory->observers().all(); !r.empty();
2734 r.popFront()) {
2735 r.front()->instance().onMovingGrowMemory(memory);
2739 return oldNumPages.value();
2742 /* static */
2743 void WasmMemoryObject::discard(Handle<WasmMemoryObject*> memory,
2744 uint64_t byteOffset, uint64_t byteLen,
2745 JSContext* cx) {
2746 if (memory->isShared()) {
2747 Rooted<SharedArrayBufferObject*> buf(
2748 cx, &memory->buffer().as<SharedArrayBufferObject>());
2749 SharedArrayBufferObject::wasmDiscard(buf, byteOffset, byteLen);
2750 } else {
2751 Rooted<ArrayBufferObject*> buf(cx,
2752 &memory->buffer().as<ArrayBufferObject>());
2753 ArrayBufferObject::wasmDiscard(buf, byteOffset, byteLen);
2757 bool js::wasm::IsSharedWasmMemoryObject(JSObject* obj) {
2758 WasmMemoryObject* mobj = obj->maybeUnwrapIf<WasmMemoryObject>();
2759 return mobj && mobj->isShared();
2762 // ============================================================================
2763 // WebAssembly.Table class and methods
2765 const JSClassOps WasmTableObject::classOps_ = {
2766 nullptr, // addProperty
2767 nullptr, // delProperty
2768 nullptr, // enumerate
2769 nullptr, // newEnumerate
2770 nullptr, // resolve
2771 nullptr, // mayResolve
2772 WasmTableObject::finalize, // finalize
2773 nullptr, // call
2774 nullptr, // construct
2775 WasmTableObject::trace, // trace
2778 const JSClass WasmTableObject::class_ = {
2779 "WebAssembly.Table",
2780 JSCLASS_DELAY_METADATA_BUILDER |
2781 JSCLASS_HAS_RESERVED_SLOTS(WasmTableObject::RESERVED_SLOTS) |
2782 JSCLASS_FOREGROUND_FINALIZE,
2783 &WasmTableObject::classOps_, &WasmTableObject::classSpec_};
2785 const JSClass& WasmTableObject::protoClass_ = PlainObject::class_;
2787 static constexpr char WasmTableName[] = "Table";
2789 const ClassSpec WasmTableObject::classSpec_ = {
2790 CreateWasmConstructor<WasmTableObject, WasmTableName>,
2791 GenericCreatePrototype<WasmTableObject>,
2792 WasmTableObject::static_methods,
2793 nullptr,
2794 WasmTableObject::methods,
2795 WasmTableObject::properties,
2796 nullptr,
2797 ClassSpec::DontDefineConstructor};
2799 bool WasmTableObject::isNewborn() const {
2800 MOZ_ASSERT(is<WasmTableObject>());
2801 return getReservedSlot(TABLE_SLOT).isUndefined();
2804 /* static */
2805 void WasmTableObject::finalize(JS::GCContext* gcx, JSObject* obj) {
2806 WasmTableObject& tableObj = obj->as<WasmTableObject>();
2807 if (!tableObj.isNewborn()) {
2808 auto& table = tableObj.table();
2809 gcx->release(obj, &table, table.gcMallocBytes(), MemoryUse::WasmTableTable);
2813 /* static */
2814 void WasmTableObject::trace(JSTracer* trc, JSObject* obj) {
2815 WasmTableObject& tableObj = obj->as<WasmTableObject>();
2816 if (!tableObj.isNewborn()) {
2817 tableObj.table().tracePrivate(trc);
2821 // Return the JS value to use when a parameter to a function requiring a table
2822 // value is omitted. An implementation of [1].
2824 // [1]
2825 // https://webassembly.github.io/reference-types/js-api/index.html#defaultvalue
2826 static Value RefTypeDefautValue(wasm::RefType tableType) {
2827 return tableType.isExtern() ? UndefinedValue() : NullValue();
2830 static bool CheckRefTypeValue(JSContext* cx, wasm::RefType type,
2831 HandleValue value) {
2832 RootedFunction fun(cx);
2833 RootedAnyRef any(cx, AnyRef::null());
2835 return CheckRefType(cx, type, value, &fun, &any);
2838 /* static */
2839 WasmTableObject* WasmTableObject::create(JSContext* cx, uint32_t initialLength,
2840 Maybe<uint32_t> maximumLength,
2841 wasm::RefType tableType,
2842 HandleObject proto) {
2843 AutoSetNewObjectMetadata metadata(cx);
2844 Rooted<WasmTableObject*> obj(
2845 cx, NewObjectWithGivenProto<WasmTableObject>(cx, proto));
2846 if (!obj) {
2847 return nullptr;
2850 MOZ_ASSERT(obj->isNewborn());
2852 TableDesc td(tableType, initialLength, maximumLength, Nothing(),
2853 /*isAsmJS*/ false,
2854 /*isImported=*/true, /*isExported=*/true);
2856 SharedTable table = Table::create(cx, td, obj);
2857 if (!table) {
2858 return nullptr;
2861 size_t size = table->gcMallocBytes();
2862 InitReservedSlot(obj, TABLE_SLOT, table.forget().take(), size,
2863 MemoryUse::WasmTableTable);
2865 MOZ_ASSERT(!obj->isNewborn());
2866 return obj;
2869 /* static */
2870 bool WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp) {
2871 CallArgs args = CallArgsFromVp(argc, vp);
2873 if (!ThrowIfNotConstructing(cx, args, "Table")) {
2874 return false;
2877 if (!args.requireAtLeast(cx, "WebAssembly.Table", 1)) {
2878 return false;
2881 if (!args.get(0).isObject()) {
2882 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2883 JSMSG_WASM_BAD_DESC_ARG, "table");
2884 return false;
2887 RootedObject obj(cx, &args[0].toObject());
2889 JSAtom* elementAtom = Atomize(cx, "element", strlen("element"));
2890 if (!elementAtom) {
2891 return false;
2893 RootedId elementId(cx, AtomToId(elementAtom));
2895 RootedValue elementVal(cx);
2896 if (!GetProperty(cx, obj, obj, elementId, &elementVal)) {
2897 return false;
2900 RefType tableType;
2901 if (!ToRefType(cx, elementVal, &tableType)) {
2902 return false;
2905 Limits limits;
2906 if (!GetLimits(cx, obj, LimitsKind::Table, &limits) ||
2907 !CheckLimits(cx, MaxTableLimitField, LimitsKind::Table, &limits)) {
2908 return false;
2911 // Converting limits for a table only supports i32
2912 MOZ_ASSERT(limits.indexType == IndexType::I32);
2914 if (limits.initial > MaxTableLength) {
2915 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
2916 JSMSG_WASM_TABLE_IMP_LIMIT);
2917 return false;
2920 RootedObject proto(cx,
2921 GetWasmConstructorPrototype(cx, args, JSProto_WasmTable));
2922 if (!proto) {
2923 ReportOutOfMemory(cx);
2924 return false;
2927 // The rest of the runtime expects table limits to be within a 32-bit range.
2928 static_assert(MaxTableLimitField <= UINT32_MAX, "invariant");
2929 uint32_t initialLength = uint32_t(limits.initial);
2930 Maybe<uint32_t> maximumLength;
2931 if (limits.maximum) {
2932 maximumLength = Some(uint32_t(*limits.maximum));
2935 Rooted<WasmTableObject*> table(
2936 cx, WasmTableObject::create(cx, initialLength, maximumLength, tableType,
2937 proto));
2938 if (!table) {
2939 return false;
2942 // Initialize the table to a default value
2943 RootedValue initValue(
2944 cx, args.length() < 2 ? RefTypeDefautValue(tableType) : args[1]);
2945 if (!CheckRefTypeValue(cx, tableType, initValue)) {
2946 return false;
2949 // Skip initializing the table if the fill value is null, as that is the
2950 // default value.
2951 if (!initValue.isNull() &&
2952 !table->fillRange(cx, 0, initialLength, initValue)) {
2953 return false;
2955 #ifdef DEBUG
2956 // Assert that null is the default value of a new table.
2957 if (initValue.isNull()) {
2958 table->table().assertRangeNull(0, initialLength);
2960 if (!tableType.isNullable()) {
2961 table->table().assertRangeNotNull(0, initialLength);
2963 #endif
2965 args.rval().setObject(*table);
2966 return true;
2969 static bool IsTable(HandleValue v) {
2970 return v.isObject() && v.toObject().is<WasmTableObject>();
2973 /* static */
2974 bool WasmTableObject::lengthGetterImpl(JSContext* cx, const CallArgs& args) {
2975 args.rval().setNumber(
2976 args.thisv().toObject().as<WasmTableObject>().table().length());
2977 return true;
2980 /* static */
2981 bool WasmTableObject::lengthGetter(JSContext* cx, unsigned argc, Value* vp) {
2982 CallArgs args = CallArgsFromVp(argc, vp);
2983 return CallNonGenericMethod<IsTable, lengthGetterImpl>(cx, args);
2986 const JSPropertySpec WasmTableObject::properties[] = {
2987 JS_PSG("length", WasmTableObject::lengthGetter, JSPROP_ENUMERATE),
2988 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Table", JSPROP_READONLY),
2989 JS_PS_END};
2991 static bool ToTableIndex(JSContext* cx, HandleValue v, const Table& table,
2992 const char* noun, uint32_t* index) {
2993 if (!EnforceRangeU32(cx, v, "Table", noun, index)) {
2994 return false;
2997 if (*index >= table.length()) {
2998 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2999 JSMSG_WASM_BAD_RANGE, "Table", noun);
3000 return false;
3003 return true;
3006 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3007 /* static */
3008 bool WasmTableObject::typeImpl(JSContext* cx, const CallArgs& args) {
3009 Table& table = args.thisv().toObject().as<WasmTableObject>().table();
3010 RootedObject typeObj(cx, TableTypeToObject(cx, table.elemType(),
3011 table.length(), table.maximum()));
3012 if (!typeObj) {
3013 return false;
3015 args.rval().setObject(*typeObj);
3016 return true;
3019 /* static */
3020 bool WasmTableObject::type(JSContext* cx, unsigned argc, Value* vp) {
3021 CallArgs args = CallArgsFromVp(argc, vp);
3022 return CallNonGenericMethod<IsTable, typeImpl>(cx, args);
3024 #endif
3026 /* static */
3027 bool WasmTableObject::getImpl(JSContext* cx, const CallArgs& args) {
3028 Rooted<WasmTableObject*> tableObj(
3029 cx, &args.thisv().toObject().as<WasmTableObject>());
3030 const Table& table = tableObj->table();
3032 if (!args.requireAtLeast(cx, "WebAssembly.Table.get", 1)) {
3033 return false;
3036 uint32_t index;
3037 if (!ToTableIndex(cx, args.get(0), table, "get index", &index)) {
3038 return false;
3041 return table.getValue(cx, index, args.rval());
3044 /* static */
3045 bool WasmTableObject::get(JSContext* cx, unsigned argc, Value* vp) {
3046 CallArgs args = CallArgsFromVp(argc, vp);
3047 return CallNonGenericMethod<IsTable, getImpl>(cx, args);
3050 /* static */
3051 bool WasmTableObject::setImpl(JSContext* cx, const CallArgs& args) {
3052 Rooted<WasmTableObject*> tableObj(
3053 cx, &args.thisv().toObject().as<WasmTableObject>());
3054 Table& table = tableObj->table();
3056 if (!args.requireAtLeast(cx, "WebAssembly.Table.set", 1)) {
3057 return false;
3060 uint32_t index;
3061 if (!ToTableIndex(cx, args.get(0), table, "set index", &index)) {
3062 return false;
3065 RootedValue fillValue(
3066 cx, args.length() < 2 ? RefTypeDefautValue(table.elemType()) : args[1]);
3067 if (!tableObj->fillRange(cx, index, 1, fillValue)) {
3068 return false;
3071 args.rval().setUndefined();
3072 return true;
3075 /* static */
3076 bool WasmTableObject::set(JSContext* cx, unsigned argc, Value* vp) {
3077 CallArgs args = CallArgsFromVp(argc, vp);
3078 return CallNonGenericMethod<IsTable, setImpl>(cx, args);
3081 /* static */
3082 bool WasmTableObject::growImpl(JSContext* cx, const CallArgs& args) {
3083 Rooted<WasmTableObject*> tableObj(
3084 cx, &args.thisv().toObject().as<WasmTableObject>());
3085 Table& table = tableObj->table();
3087 if (!args.requireAtLeast(cx, "WebAssembly.Table.grow", 1)) {
3088 return false;
3091 uint32_t delta;
3092 if (!EnforceRangeU32(cx, args.get(0), "Table", "grow delta", &delta)) {
3093 return false;
3096 RootedValue fillValue(
3097 cx, args.length() < 2 ? RefTypeDefautValue(table.elemType()) : args[1]);
3098 if (!CheckRefTypeValue(cx, table.elemType(), fillValue)) {
3099 return false;
3102 uint32_t oldLength = table.grow(delta);
3104 if (oldLength == uint32_t(-1)) {
3105 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_GROW,
3106 "table");
3107 return false;
3110 // Skip filling the grown range of the table if the fill value is null, as
3111 // that is the default value.
3112 if (!fillValue.isNull() &&
3113 !tableObj->fillRange(cx, oldLength, delta, fillValue)) {
3114 return false;
3116 #ifdef DEBUG
3117 // Assert that null is the default value of the grown range.
3118 if (fillValue.isNull()) {
3119 table.assertRangeNull(oldLength, delta);
3121 if (!table.elemType().isNullable()) {
3122 table.assertRangeNotNull(oldLength, delta);
3124 #endif
3126 args.rval().setInt32(int32_t(oldLength));
3127 return true;
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);
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), JS_FS_END};
3144 const JSFunctionSpec WasmTableObject::static_methods[] = {JS_FS_END};
3146 Table& WasmTableObject::table() const {
3147 return *(Table*)getReservedSlot(TABLE_SLOT).toPrivate();
3150 bool WasmTableObject::fillRange(JSContext* cx, uint32_t index, uint32_t length,
3151 HandleValue value) const {
3152 Table& tab = table();
3154 // All consumers are required to either bounds check or statically be in
3155 // bounds
3156 MOZ_ASSERT(uint64_t(index) + uint64_t(length) <= tab.length());
3158 RootedFunction fun(cx);
3159 RootedAnyRef any(cx, AnyRef::null());
3160 if (!CheckRefType(cx, tab.elemType(), value, &fun, &any)) {
3161 return false;
3163 switch (tab.repr()) {
3164 case TableRepr::Func:
3165 MOZ_RELEASE_ASSERT(!tab.isAsmJS());
3166 tab.fillFuncRef(index, length, FuncRef::fromJSFunction(fun), cx);
3167 break;
3168 case TableRepr::Ref:
3169 tab.fillAnyRef(index, length, any);
3170 break;
3172 return true;
3175 // ============================================================================
3176 // WebAssembly.global class and methods
3178 const JSClassOps WasmGlobalObject::classOps_ = {
3179 nullptr, // addProperty
3180 nullptr, // delProperty
3181 nullptr, // enumerate
3182 nullptr, // newEnumerate
3183 nullptr, // resolve
3184 nullptr, // mayResolve
3185 WasmGlobalObject::finalize, // finalize
3186 nullptr, // call
3187 nullptr, // construct
3188 WasmGlobalObject::trace, // trace
3191 const JSClass WasmGlobalObject::class_ = {
3192 "WebAssembly.Global",
3193 JSCLASS_HAS_RESERVED_SLOTS(WasmGlobalObject::RESERVED_SLOTS) |
3194 JSCLASS_BACKGROUND_FINALIZE,
3195 &WasmGlobalObject::classOps_, &WasmGlobalObject::classSpec_};
3197 const JSClass& WasmGlobalObject::protoClass_ = PlainObject::class_;
3199 static constexpr char WasmGlobalName[] = "Global";
3201 const ClassSpec WasmGlobalObject::classSpec_ = {
3202 CreateWasmConstructor<WasmGlobalObject, WasmGlobalName>,
3203 GenericCreatePrototype<WasmGlobalObject>,
3204 WasmGlobalObject::static_methods,
3205 nullptr,
3206 WasmGlobalObject::methods,
3207 WasmGlobalObject::properties,
3208 nullptr,
3209 ClassSpec::DontDefineConstructor};
3211 /* static */
3212 void WasmGlobalObject::trace(JSTracer* trc, JSObject* obj) {
3213 WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
3214 if (global->isNewborn()) {
3215 // This can happen while we're allocating the object, in which case
3216 // every single slot of the object is not defined yet. In particular,
3217 // there's nothing to trace yet.
3218 return;
3220 global->val().get().trace(trc);
3223 /* static */
3224 void WasmGlobalObject::finalize(JS::GCContext* gcx, JSObject* obj) {
3225 WasmGlobalObject* global = reinterpret_cast<WasmGlobalObject*>(obj);
3226 if (!global->isNewborn()) {
3227 // Release the strong reference to the type definitions this global could
3228 // be referencing.
3229 global->type().Release();
3230 gcx->delete_(obj, &global->val(), MemoryUse::WasmGlobalCell);
3234 /* static */
3235 WasmGlobalObject* WasmGlobalObject::create(JSContext* cx, HandleVal value,
3236 bool isMutable, HandleObject proto) {
3237 Rooted<WasmGlobalObject*> obj(
3238 cx, NewObjectWithGivenProto<WasmGlobalObject>(cx, proto));
3239 if (!obj) {
3240 return nullptr;
3243 MOZ_ASSERT(obj->isNewborn());
3244 MOZ_ASSERT(obj->isTenured(), "assumed by global.set post barriers");
3246 GCPtrVal* val = js_new<GCPtrVal>(Val());
3247 if (!val) {
3248 ReportOutOfMemory(cx);
3249 return nullptr;
3251 obj->initReservedSlot(MUTABLE_SLOT, JS::BooleanValue(isMutable));
3252 InitReservedSlot(obj, VAL_SLOT, val, MemoryUse::WasmGlobalCell);
3254 // It's simpler to initialize the cell after the object has been created,
3255 // to avoid needing to root the cell before the object creation.
3256 obj->val() = value.get();
3257 // Acquire a strong reference to a type definition this global could
3258 // be referencing.
3259 obj->type().AddRef();
3261 MOZ_ASSERT(!obj->isNewborn());
3263 return obj;
3266 /* static */
3267 bool WasmGlobalObject::construct(JSContext* cx, unsigned argc, Value* vp) {
3268 CallArgs args = CallArgsFromVp(argc, vp);
3270 if (!ThrowIfNotConstructing(cx, args, "Global")) {
3271 return false;
3274 if (!args.requireAtLeast(cx, "WebAssembly.Global", 1)) {
3275 return false;
3278 if (!args.get(0).isObject()) {
3279 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3280 JSMSG_WASM_BAD_DESC_ARG, "global");
3281 return false;
3284 RootedObject obj(cx, &args[0].toObject());
3286 // Extract properties in lexicographic order per spec.
3288 RootedValue mutableVal(cx);
3289 if (!JS_GetProperty(cx, obj, "mutable", &mutableVal)) {
3290 return false;
3293 RootedValue typeVal(cx);
3294 if (!JS_GetProperty(cx, obj, "value", &typeVal)) {
3295 return false;
3298 ValType globalType;
3299 if (!ToValType(cx, typeVal, &globalType)) {
3300 return false;
3303 if (!globalType.isExposable()) {
3304 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3305 JSMSG_WASM_BAD_VAL_TYPE);
3306 return false;
3309 bool isMutable = ToBoolean(mutableVal);
3311 // Extract the initial value, or provide a suitable default.
3312 RootedVal globalVal(cx, globalType);
3314 // Override with non-undefined value, if provided.
3315 RootedValue valueVal(cx);
3316 if (globalType.isRefType()) {
3317 valueVal.set(args.length() < 2 ? RefTypeDefautValue(globalType.refType())
3318 : args[1]);
3319 if (!Val::fromJSValue(cx, globalType, valueVal, &globalVal)) {
3320 return false;
3322 } else {
3323 valueVal.set(args.get(1));
3324 if (!valueVal.isUndefined() &&
3325 !Val::fromJSValue(cx, globalType, valueVal, &globalVal)) {
3326 return false;
3330 RootedObject proto(cx,
3331 GetWasmConstructorPrototype(cx, args, JSProto_WasmGlobal));
3332 if (!proto) {
3333 ReportOutOfMemory(cx);
3334 return false;
3337 WasmGlobalObject* global =
3338 WasmGlobalObject::create(cx, globalVal, isMutable, proto);
3339 if (!global) {
3340 return false;
3343 args.rval().setObject(*global);
3344 return true;
3347 static bool IsGlobal(HandleValue v) {
3348 return v.isObject() && v.toObject().is<WasmGlobalObject>();
3351 /* static */
3352 bool WasmGlobalObject::valueGetterImpl(JSContext* cx, const CallArgs& args) {
3353 const WasmGlobalObject& globalObj =
3354 args.thisv().toObject().as<WasmGlobalObject>();
3355 if (!globalObj.type().isExposable()) {
3356 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3357 JSMSG_WASM_BAD_VAL_TYPE);
3358 return false;
3360 return globalObj.val().get().toJSValue(cx, args.rval());
3363 /* static */
3364 bool WasmGlobalObject::valueGetter(JSContext* cx, unsigned argc, Value* vp) {
3365 CallArgs args = CallArgsFromVp(argc, vp);
3366 return CallNonGenericMethod<IsGlobal, valueGetterImpl>(cx, args);
3369 /* static */
3370 bool WasmGlobalObject::valueSetterImpl(JSContext* cx, const CallArgs& args) {
3371 if (!args.requireAtLeast(cx, "WebAssembly.Global setter", 1)) {
3372 return false;
3375 Rooted<WasmGlobalObject*> global(
3376 cx, &args.thisv().toObject().as<WasmGlobalObject>());
3377 if (!global->isMutable()) {
3378 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3379 JSMSG_WASM_GLOBAL_IMMUTABLE);
3380 return false;
3383 RootedVal val(cx);
3384 if (!Val::fromJSValue(cx, global->type(), args.get(0), &val)) {
3385 return false;
3387 global->val() = val.get();
3389 args.rval().setUndefined();
3390 return true;
3393 /* static */
3394 bool WasmGlobalObject::valueSetter(JSContext* cx, unsigned argc, Value* vp) {
3395 CallArgs args = CallArgsFromVp(argc, vp);
3396 return CallNonGenericMethod<IsGlobal, valueSetterImpl>(cx, args);
3399 const JSPropertySpec WasmGlobalObject::properties[] = {
3400 JS_PSGS("value", WasmGlobalObject::valueGetter,
3401 WasmGlobalObject::valueSetter, JSPROP_ENUMERATE),
3402 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Global", JSPROP_READONLY),
3403 JS_PS_END};
3405 const JSFunctionSpec WasmGlobalObject::methods[] = {
3406 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3407 JS_FN("type", WasmGlobalObject::type, 0, JSPROP_ENUMERATE),
3408 #endif
3409 JS_FN("valueOf", WasmGlobalObject::valueGetter, 0, JSPROP_ENUMERATE),
3410 JS_FS_END};
3412 const JSFunctionSpec WasmGlobalObject::static_methods[] = {JS_FS_END};
3414 bool WasmGlobalObject::isMutable() const {
3415 return getReservedSlot(MUTABLE_SLOT).toBoolean();
3418 ValType WasmGlobalObject::type() const { return val().get().type(); }
3420 GCPtrVal& WasmGlobalObject::val() const {
3421 return *reinterpret_cast<GCPtrVal*>(getReservedSlot(VAL_SLOT).toPrivate());
3424 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3425 /* static */
3426 bool WasmGlobalObject::typeImpl(JSContext* cx, const CallArgs& args) {
3427 Rooted<WasmGlobalObject*> global(
3428 cx, &args.thisv().toObject().as<WasmGlobalObject>());
3429 RootedObject typeObj(
3430 cx, GlobalTypeToObject(cx, global->type(), global->isMutable()));
3431 if (!typeObj) {
3432 return false;
3434 args.rval().setObject(*typeObj);
3435 return true;
3438 /* static */
3439 bool WasmGlobalObject::type(JSContext* cx, unsigned argc, Value* vp) {
3440 CallArgs args = CallArgsFromVp(argc, vp);
3441 return CallNonGenericMethod<IsGlobal, typeImpl>(cx, args);
3443 #endif
3445 // ============================================================================
3446 // WebAssembly.Tag class and methods
3448 const JSClassOps WasmTagObject::classOps_ = {
3449 nullptr, // addProperty
3450 nullptr, // delProperty
3451 nullptr, // enumerate
3452 nullptr, // newEnumerate
3453 nullptr, // resolve
3454 nullptr, // mayResolve
3455 WasmTagObject::finalize, // finalize
3456 nullptr, // call
3457 nullptr, // construct
3458 nullptr, // trace
3461 const JSClass WasmTagObject::class_ = {
3462 "WebAssembly.Tag",
3463 JSCLASS_HAS_RESERVED_SLOTS(WasmTagObject::RESERVED_SLOTS) |
3464 JSCLASS_FOREGROUND_FINALIZE,
3465 &WasmTagObject::classOps_, &WasmTagObject::classSpec_};
3467 const JSClass& WasmTagObject::protoClass_ = PlainObject::class_;
3469 static constexpr char WasmTagName[] = "Tag";
3471 const ClassSpec WasmTagObject::classSpec_ = {
3472 CreateWasmConstructor<WasmTagObject, WasmTagName>,
3473 GenericCreatePrototype<WasmTagObject>,
3474 WasmTagObject::static_methods,
3475 nullptr,
3476 WasmTagObject::methods,
3477 WasmTagObject::properties,
3478 nullptr,
3479 ClassSpec::DontDefineConstructor};
3481 /* static */
3482 void WasmTagObject::finalize(JS::GCContext* gcx, JSObject* obj) {
3483 WasmTagObject& tagObj = obj->as<WasmTagObject>();
3484 tagObj.tagType()->Release();
3487 static bool IsTag(HandleValue v) {
3488 return v.isObject() && v.toObject().is<WasmTagObject>();
3491 bool WasmTagObject::construct(JSContext* cx, unsigned argc, Value* vp) {
3492 CallArgs args = CallArgsFromVp(argc, vp);
3494 if (!ThrowIfNotConstructing(cx, args, "Tag")) {
3495 return false;
3498 if (!args.requireAtLeast(cx, "WebAssembly.Tag", 1)) {
3499 return false;
3502 if (!args.get(0).isObject()) {
3503 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3504 JSMSG_WASM_BAD_DESC_ARG, "tag");
3505 return false;
3508 RootedObject obj(cx, &args[0].toObject());
3509 RootedValue paramsVal(cx);
3510 if (!JS_GetProperty(cx, obj, "parameters", &paramsVal)) {
3511 return false;
3514 ValTypeVector params;
3515 if (!ParseValTypes(cx, paramsVal, params)) {
3516 return false;
3518 wasm::MutableTagType tagType = js_new<wasm::TagType>();
3519 if (!tagType || !tagType->initialize(std::move(params))) {
3520 return false;
3523 RootedObject proto(cx,
3524 GetWasmConstructorPrototype(cx, args, JSProto_WasmTag));
3525 if (!proto) {
3526 ReportOutOfMemory(cx);
3527 return false;
3530 Rooted<WasmTagObject*> tagObj(cx, WasmTagObject::create(cx, tagType, proto));
3531 if (!tagObj) {
3532 return false;
3535 args.rval().setObject(*tagObj);
3536 return true;
3539 /* static */
3540 WasmTagObject* WasmTagObject::create(JSContext* cx,
3541 const wasm::SharedTagType& tagType,
3542 HandleObject proto) {
3543 Rooted<WasmTagObject*> obj(cx,
3544 NewObjectWithGivenProto<WasmTagObject>(cx, proto));
3545 if (!obj) {
3546 return nullptr;
3549 tagType.get()->AddRef();
3550 obj->initReservedSlot(TYPE_SLOT, PrivateValue((void*)tagType.get()));
3552 return obj;
3555 const JSPropertySpec WasmTagObject::properties[] = {
3556 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Tag", JSPROP_READONLY),
3557 JS_PS_END};
3559 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3560 /* static */
3561 bool WasmTagObject::typeImpl(JSContext* cx, const CallArgs& args) {
3562 Rooted<WasmTagObject*> tag(cx, &args.thisv().toObject().as<WasmTagObject>());
3563 RootedObject typeObj(cx, TagTypeToObject(cx, tag->valueTypes()));
3564 if (!typeObj) {
3565 return false;
3567 args.rval().setObject(*typeObj);
3568 return true;
3571 /* static */
3572 bool WasmTagObject::type(JSContext* cx, unsigned argc, Value* vp) {
3573 CallArgs args = CallArgsFromVp(argc, vp);
3574 return CallNonGenericMethod<IsTag, typeImpl>(cx, args);
3576 #endif
3578 const JSFunctionSpec WasmTagObject::methods[] = {
3579 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3580 JS_FN("type", WasmTagObject::type, 0, JSPROP_ENUMERATE),
3581 #endif
3582 JS_FS_END};
3584 const JSFunctionSpec WasmTagObject::static_methods[] = {JS_FS_END};
3586 const TagType* WasmTagObject::tagType() const {
3587 return (const TagType*)getFixedSlot(TYPE_SLOT).toPrivate();
3590 const wasm::ValTypeVector& WasmTagObject::valueTypes() const {
3591 return tagType()->argTypes();
3594 wasm::ResultType WasmTagObject::resultType() const {
3595 return wasm::ResultType::Vector(valueTypes());
3598 // ============================================================================
3599 // WebAssembly.Exception class and methods
3601 const JSClassOps WasmExceptionObject::classOps_ = {
3602 nullptr, // addProperty
3603 nullptr, // delProperty
3604 nullptr, // enumerate
3605 nullptr, // newEnumerate
3606 nullptr, // resolve
3607 nullptr, // mayResolve
3608 WasmExceptionObject::finalize, // finalize
3609 nullptr, // call
3610 nullptr, // construct
3611 WasmExceptionObject::trace, // trace
3614 const JSClass WasmExceptionObject::class_ = {
3615 "WebAssembly.Exception",
3616 JSCLASS_HAS_RESERVED_SLOTS(WasmExceptionObject::RESERVED_SLOTS) |
3617 JSCLASS_FOREGROUND_FINALIZE,
3618 &WasmExceptionObject::classOps_, &WasmExceptionObject::classSpec_};
3620 const JSClass& WasmExceptionObject::protoClass_ = PlainObject::class_;
3622 static constexpr char WasmExceptionName[] = "Exception";
3624 const ClassSpec WasmExceptionObject::classSpec_ = {
3625 CreateWasmConstructor<WasmExceptionObject, WasmExceptionName>,
3626 GenericCreatePrototype<WasmExceptionObject>,
3627 WasmExceptionObject::static_methods,
3628 nullptr,
3629 WasmExceptionObject::methods,
3630 WasmExceptionObject::properties,
3631 nullptr,
3632 ClassSpec::DontDefineConstructor};
3634 /* static */
3635 void WasmExceptionObject::finalize(JS::GCContext* gcx, JSObject* obj) {
3636 WasmExceptionObject& exnObj = obj->as<WasmExceptionObject>();
3637 if (exnObj.isNewborn()) {
3638 return;
3640 gcx->free_(obj, exnObj.typedMem(), exnObj.tagType()->tagSize(),
3641 MemoryUse::WasmExceptionData);
3642 exnObj.tagType()->Release();
3645 /* static */
3646 void WasmExceptionObject::trace(JSTracer* trc, JSObject* obj) {
3647 WasmExceptionObject& exnObj = obj->as<WasmExceptionObject>();
3648 if (exnObj.isNewborn()) {
3649 return;
3652 wasm::SharedTagType tag = exnObj.tagType();
3653 const wasm::ValTypeVector& params = tag->argTypes();
3654 const wasm::TagOffsetVector& offsets = tag->argOffsets();
3655 uint8_t* typedMem = exnObj.typedMem();
3656 for (size_t i = 0; i < params.length(); i++) {
3657 ValType paramType = params[i];
3658 if (paramType.isRefRepr()) {
3659 GCPtr<wasm::AnyRef>* paramPtr =
3660 reinterpret_cast<GCPtr<AnyRef>*>(typedMem + offsets[i]);
3661 TraceNullableEdge(trc, paramPtr, "wasm exception param");
3666 static bool IsException(HandleValue v) {
3667 return v.isObject() && v.toObject().is<WasmExceptionObject>();
3670 struct ExceptionOptions {
3671 bool traceStack;
3673 ExceptionOptions() : traceStack(false) {}
3675 [[nodiscard]] bool init(JSContext* cx, HandleValue val);
3678 bool ExceptionOptions::init(JSContext* cx, HandleValue val) {
3679 if (val.isNullOrUndefined()) {
3680 return true;
3683 if (!val.isObject()) {
3684 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3685 JSMSG_WASM_BAD_EXN_OPTIONS);
3686 return false;
3688 RootedObject obj(cx, &val.toObject());
3690 // Get `traceStack` and coerce to boolean
3691 RootedValue traceStackVal(cx);
3692 if (!JS_GetProperty(cx, obj, "traceStack", &traceStackVal)) {
3693 return false;
3695 traceStack = ToBoolean(traceStackVal);
3697 return true;
3700 bool WasmExceptionObject::construct(JSContext* cx, unsigned argc, Value* vp) {
3701 CallArgs args = CallArgsFromVp(argc, vp);
3703 if (!ThrowIfNotConstructing(cx, args, "Exception")) {
3704 return false;
3707 if (!args.requireAtLeast(cx, "WebAssembly.Exception", 2)) {
3708 return false;
3711 if (!IsTag(args[0])) {
3712 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3713 JSMSG_WASM_BAD_EXN_ARG);
3714 return false;
3716 Rooted<WasmTagObject*> exnTag(cx, &args[0].toObject().as<WasmTagObject>());
3718 if (!args.get(1).isObject()) {
3719 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3720 JSMSG_WASM_BAD_EXN_PAYLOAD);
3721 return false;
3724 JS::ForOfIterator iterator(cx);
3725 if (!iterator.init(args.get(1), JS::ForOfIterator::ThrowOnNonIterable)) {
3726 return false;
3729 // Get the optional 'options' parameter
3730 ExceptionOptions options;
3731 if (!options.init(cx, args.get(2))) {
3732 return false;
3735 // Trace the stack if requested
3736 RootedObject stack(cx);
3737 if (options.traceStack && !CaptureStack(cx, &stack)) {
3738 return false;
3741 RootedObject proto(
3742 cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmException));
3743 if (!proto) {
3744 ReportOutOfMemory(cx);
3745 return false;
3748 Rooted<WasmExceptionObject*> exnObj(
3749 cx, WasmExceptionObject::create(cx, exnTag, stack, proto));
3750 if (!exnObj) {
3751 return false;
3754 wasm::SharedTagType tagType = exnObj->tagType();
3755 const wasm::ValTypeVector& params = tagType->argTypes();
3756 const wasm::TagOffsetVector& offsets = tagType->argOffsets();
3758 RootedValue nextArg(cx);
3759 for (size_t i = 0; i < params.length(); i++) {
3760 bool done;
3761 if (!iterator.next(&nextArg, &done)) {
3762 return false;
3764 if (done) {
3765 UniqueChars expected(JS_smprintf("%zu", params.length()));
3766 UniqueChars got(JS_smprintf("%zu", i));
3767 if (!expected || !got) {
3768 ReportOutOfMemory(cx);
3769 return false;
3772 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3773 JSMSG_WASM_BAD_EXN_PAYLOAD_LEN, expected.get(),
3774 got.get());
3775 return false;
3778 if (!exnObj->initArg(cx, offsets[i], params[i], nextArg)) {
3779 return false;
3783 args.rval().setObject(*exnObj);
3784 return true;
3787 /* static */
3788 WasmExceptionObject* WasmExceptionObject::create(JSContext* cx,
3789 Handle<WasmTagObject*> tag,
3790 HandleObject stack,
3791 HandleObject proto) {
3792 Rooted<WasmExceptionObject*> obj(
3793 cx, NewObjectWithGivenProto<WasmExceptionObject>(cx, proto));
3794 if (!obj) {
3795 return nullptr;
3797 const TagType* tagType = tag->tagType();
3799 // Allocate the data buffer before initializing the object so that an OOM
3800 // does not result in a partially constructed object.
3801 uint8_t* data = (uint8_t*)js_calloc(tagType->tagSize());
3802 if (!data) {
3803 ReportOutOfMemory(cx);
3804 return nullptr;
3807 MOZ_ASSERT(obj->isNewborn());
3808 obj->initFixedSlot(TAG_SLOT, ObjectValue(*tag));
3809 tagType->AddRef();
3810 obj->initFixedSlot(TYPE_SLOT, PrivateValue((void*)tagType));
3811 InitReservedSlot(obj, DATA_SLOT, data, tagType->tagSize(),
3812 MemoryUse::WasmExceptionData);
3813 obj->initFixedSlot(STACK_SLOT, ObjectOrNullValue(stack));
3815 MOZ_ASSERT(!obj->isNewborn());
3817 return obj;
3820 WasmExceptionObject* WasmExceptionObject::wrapJSValue(JSContext* cx,
3821 HandleValue value) {
3822 Rooted<WasmNamespaceObject*> wasm(cx, WasmNamespaceObject::getOrCreate(cx));
3823 if (!wasm) {
3824 return nullptr;
3827 Rooted<AnyRef> valueAnyRef(cx);
3828 if (!AnyRef::fromJSValue(cx, value, &valueAnyRef)) {
3829 return nullptr;
3832 Rooted<WasmTagObject*> wrappedJSValueTag(cx, wasm->wrappedJSValueTag());
3833 WasmExceptionObject* exn =
3834 WasmExceptionObject::create(cx, wrappedJSValueTag, nullptr, nullptr);
3835 if (!exn) {
3836 return nullptr;
3838 MOZ_ASSERT(exn->isWrappedJSValue());
3840 exn->initRefArg(WrappedJSValueTagType_ValueOffset, valueAnyRef);
3841 return exn;
3844 bool WasmExceptionObject::isNewborn() const {
3845 MOZ_ASSERT(is<WasmExceptionObject>());
3846 return getReservedSlot(DATA_SLOT).isUndefined();
3849 bool WasmExceptionObject::isWrappedJSValue() const {
3850 return tagType() == sWrappedJSValueTagType;
3853 Value WasmExceptionObject::wrappedJSValue() const {
3854 MOZ_ASSERT(isWrappedJSValue());
3855 return loadRefArg(WrappedJSValueTagType_ValueOffset).toJSValue();
3858 const JSPropertySpec WasmExceptionObject::properties[] = {
3859 JS_PSG("stack", WasmExceptionObject::getStack, 0),
3860 JS_STRING_SYM_PS(toStringTag, "WebAssembly.Exception", JSPROP_READONLY),
3861 JS_PS_END};
3863 /* static */
3864 bool WasmExceptionObject::isImpl(JSContext* cx, const CallArgs& args) {
3865 Rooted<WasmExceptionObject*> exnObj(
3866 cx, &args.thisv().toObject().as<WasmExceptionObject>());
3868 if (!args.requireAtLeast(cx, "WebAssembly.Exception.is", 1)) {
3869 return false;
3872 if (!IsTag(args[0])) {
3873 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3874 JSMSG_WASM_BAD_EXN_ARG);
3875 return false;
3878 Rooted<WasmTagObject*> exnTag(cx,
3879 &args.get(0).toObject().as<WasmTagObject>());
3880 args.rval().setBoolean(exnTag.get() == &exnObj->tag());
3882 return true;
3885 /* static */
3886 bool WasmExceptionObject::isMethod(JSContext* cx, unsigned argc, Value* vp) {
3887 CallArgs args = CallArgsFromVp(argc, vp);
3888 return CallNonGenericMethod<IsException, isImpl>(cx, args);
3891 /* static */
3892 bool WasmExceptionObject::getArgImpl(JSContext* cx, const CallArgs& args) {
3893 Rooted<WasmExceptionObject*> exnObj(
3894 cx, &args.thisv().toObject().as<WasmExceptionObject>());
3896 if (!args.requireAtLeast(cx, "WebAssembly.Exception.getArg", 2)) {
3897 return false;
3900 if (!IsTag(args[0])) {
3901 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3902 JSMSG_WASM_BAD_EXN_ARG);
3903 return false;
3906 Rooted<WasmTagObject*> exnTag(cx,
3907 &args.get(0).toObject().as<WasmTagObject>());
3908 if (exnTag.get() != &exnObj->tag()) {
3909 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3910 JSMSG_WASM_BAD_EXN_TAG);
3911 return false;
3914 uint32_t index;
3915 if (!EnforceRangeU32(cx, args.get(1), "Exception", "getArg index", &index)) {
3916 return false;
3919 const wasm::ValTypeVector& params = exnTag->valueTypes();
3920 if (index >= params.length()) {
3921 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_RANGE,
3922 "Exception", "getArg index");
3923 return false;
3926 uint32_t offset = exnTag->tagType()->argOffsets()[index];
3927 RootedValue result(cx);
3928 if (!exnObj->loadArg(cx, offset, params[index], &result)) {
3929 return false;
3931 args.rval().set(result);
3932 return true;
3935 /* static */
3936 bool WasmExceptionObject::getArg(JSContext* cx, unsigned argc, Value* vp) {
3937 CallArgs args = CallArgsFromVp(argc, vp);
3938 return CallNonGenericMethod<IsException, getArgImpl>(cx, args);
3941 /* static */
3942 bool WasmExceptionObject::getStack_impl(JSContext* cx, const CallArgs& args) {
3943 Rooted<WasmExceptionObject*> exnObj(
3944 cx, &args.thisv().toObject().as<WasmExceptionObject>());
3945 RootedObject savedFrameObj(cx, exnObj->stack());
3946 if (!savedFrameObj) {
3947 args.rval().setUndefined();
3948 return true;
3950 JSPrincipals* principals = exnObj->realm()->principals();
3951 RootedString stackString(cx);
3952 if (!BuildStackString(cx, principals, savedFrameObj, &stackString)) {
3953 return false;
3955 args.rval().setString(stackString);
3956 return true;
3959 /* static */
3960 bool WasmExceptionObject::getStack(JSContext* cx, unsigned argc, Value* vp) {
3961 CallArgs args = CallArgsFromVp(argc, vp);
3962 return CallNonGenericMethod<IsException, getStack_impl>(cx, args);
3965 JSObject* WasmExceptionObject::stack() const {
3966 return getReservedSlot(STACK_SLOT).toObjectOrNull();
3969 uint8_t* WasmExceptionObject::typedMem() const {
3970 return (uint8_t*)getReservedSlot(DATA_SLOT).toPrivate();
3973 bool WasmExceptionObject::loadArg(JSContext* cx, size_t offset,
3974 wasm::ValType type,
3975 MutableHandleValue vp) const {
3976 if (!type.isExposable()) {
3977 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3978 JSMSG_WASM_BAD_VAL_TYPE);
3979 return false;
3981 return ToJSValue(cx, typedMem() + offset, type, vp);
3984 bool WasmExceptionObject::initArg(JSContext* cx, size_t offset,
3985 wasm::ValType type, HandleValue value) {
3986 if (!type.isExposable()) {
3987 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
3988 JSMSG_WASM_BAD_VAL_TYPE);
3989 return false;
3992 // Avoid rooting hazard of `this` being live across `fromJSValue`
3993 // which may GC.
3994 uint8_t* dest = typedMem() + offset;
3995 RootedVal val(cx);
3996 if (!Val::fromJSValue(cx, type, value, &val)) {
3997 return false;
3999 val.get().writeToHeapLocation(dest);
4000 return true;
4003 void WasmExceptionObject::initRefArg(size_t offset, wasm::AnyRef ref) {
4004 uint8_t* dest = typedMem() + offset;
4005 *((GCPtr<AnyRef>*)dest) = ref;
4008 wasm::AnyRef WasmExceptionObject::loadRefArg(size_t offset) const {
4009 uint8_t* src = typedMem() + offset;
4010 return *((GCPtr<AnyRef>*)src);
4013 const JSFunctionSpec WasmExceptionObject::methods[] = {
4014 JS_FN("is", WasmExceptionObject::isMethod, 1, JSPROP_ENUMERATE),
4015 JS_FN("getArg", WasmExceptionObject::getArg, 2, JSPROP_ENUMERATE),
4016 JS_FS_END};
4018 const JSFunctionSpec WasmExceptionObject::static_methods[] = {JS_FS_END};
4020 const TagType* WasmExceptionObject::tagType() const {
4021 return (const TagType*)getReservedSlot(TYPE_SLOT).toPrivate();
4024 WasmTagObject& WasmExceptionObject::tag() const {
4025 return getReservedSlot(TAG_SLOT).toObject().as<WasmTagObject>();
4028 // ============================================================================
4029 // WebAssembly.Function and methods
4030 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
4031 static JSObject* CreateWasmFunctionPrototype(JSContext* cx, JSProtoKey key) {
4032 // WasmFunction's prototype should inherit from JSFunction's prototype.
4033 RootedObject jsProto(cx, &cx->global()->getFunctionPrototype());
4034 return GlobalObject::createBlankPrototypeInheriting(cx, &PlainObject::class_,
4035 jsProto);
4038 [[nodiscard]] static bool IsWasmFunction(HandleValue v) {
4039 if (!v.isObject()) {
4040 return false;
4042 if (!v.toObject().is<JSFunction>()) {
4043 return false;
4045 return v.toObject().as<JSFunction>().isWasm();
4048 bool WasmFunctionTypeImpl(JSContext* cx, const CallArgs& args) {
4049 RootedFunction function(cx, &args.thisv().toObject().as<JSFunction>());
4050 Rooted<WasmInstanceObject*> instanceObj(
4051 cx, ExportedFunctionToInstanceObject(function));
4052 uint32_t funcIndex = ExportedFunctionToFuncIndex(function);
4053 Instance& instance = instanceObj->instance();
4054 const FuncExport& fe =
4055 instance.metadata(instance.code().bestTier()).lookupFuncExport(funcIndex);
4056 const FuncType& funcType = instance.metadata().getFuncExportType(fe);
4057 RootedObject typeObj(cx, FuncTypeToObject(cx, funcType));
4058 if (!typeObj) {
4059 return false;
4061 args.rval().setObject(*typeObj);
4062 return true;
4065 bool WasmFunctionType(JSContext* cx, unsigned argc, Value* vp) {
4066 CallArgs args = CallArgsFromVp(argc, vp);
4067 return CallNonGenericMethod<IsWasmFunction, WasmFunctionTypeImpl>(cx, args);
4070 static JSFunction* WasmFunctionCreate(JSContext* cx, HandleObject func,
4071 wasm::ValTypeVector&& params,
4072 wasm::ValTypeVector&& results,
4073 HandleObject proto) {
4074 MOZ_ASSERT(IsCallableNonCCW(ObjectValue(*func)));
4075 MOZ_RELEASE_ASSERT(!func->is<JSFunction>() ||
4076 !IsWasmExportedFunction(&func->as<JSFunction>()));
4078 // We want to import the function to a wasm module and then export it again so
4079 // that it behaves exactly like a normal wasm function and can be used like
4080 // one in wasm tables. We synthesize such a module below, instantiate it, and
4081 // then return the exported function as the result.
4082 FeatureOptions options;
4083 ScriptedCaller scriptedCaller;
4084 SharedCompileArgs compileArgs =
4085 CompileArgs::buildAndReport(cx, std::move(scriptedCaller), options);
4086 if (!compileArgs) {
4087 return nullptr;
4090 ModuleEnvironment moduleEnv(compileArgs->features);
4091 CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
4092 DebugEnabled::False);
4093 compilerEnv.computeParameters();
4095 if (!moduleEnv.init()) {
4096 return nullptr;
4099 FuncType funcType = FuncType(std::move(params), std::move(results));
4100 if (!moduleEnv.types->addType(std::move(funcType))) {
4101 return nullptr;
4104 // Add an (import (func ...))
4105 FuncDesc funcDesc = FuncDesc(&(*moduleEnv.types)[0].funcType(), 0);
4106 if (!moduleEnv.funcs.append(funcDesc)) {
4107 return nullptr;
4109 moduleEnv.numFuncImports = 1;
4111 // Add an (export (func 0))
4112 moduleEnv.declareFuncExported(0, /* eager */ true, /* canRefFunc */ true);
4114 // We will be looking up and using the function in the future by index so the
4115 // name doesn't matter.
4116 CacheableName fieldName;
4117 if (!moduleEnv.exports.emplaceBack(std::move(fieldName), 0,
4118 DefinitionKind::Function)) {
4119 return nullptr;
4122 ModuleGenerator mg(*compileArgs, &moduleEnv, &compilerEnv, nullptr, nullptr,
4123 nullptr);
4124 if (!mg.init(nullptr)) {
4125 return nullptr;
4127 // We're not compiling any function definitions.
4128 if (!mg.finishFuncDefs()) {
4129 return nullptr;
4131 SharedBytes shareableBytes = js_new<ShareableBytes>();
4132 if (!shareableBytes) {
4133 return nullptr;
4135 SharedModule module = mg.finishModule(*shareableBytes);
4136 if (!module) {
4137 return nullptr;
4140 // Instantiate the module.
4141 Rooted<ImportValues> imports(cx);
4142 if (!imports.get().funcs.append(func)) {
4143 return nullptr;
4145 Rooted<WasmInstanceObject*> instance(cx);
4146 if (!module->instantiate(cx, imports.get(), nullptr, &instance)) {
4147 MOZ_ASSERT(cx->isThrowingOutOfMemory());
4148 return nullptr;
4151 // Get the exported function which wraps the JS function to return.
4152 RootedFunction wasmFunc(cx);
4153 if (!instance->getExportedFunction(cx, instance, 0, &wasmFunc)) {
4154 return nullptr;
4156 return wasmFunc;
4159 bool WasmFunctionConstruct(JSContext* cx, unsigned argc, Value* vp) {
4160 CallArgs args = CallArgsFromVp(argc, vp);
4162 if (!ThrowIfNotConstructing(cx, args, "WebAssembly.Function")) {
4163 return false;
4166 if (!args.requireAtLeast(cx, "WebAssembly.Function", 2)) {
4167 return false;
4170 if (!args[0].isObject()) {
4171 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
4172 JSMSG_WASM_BAD_DESC_ARG, "function");
4173 return false;
4175 RootedObject typeObj(cx, &args[0].toObject());
4177 // Extract properties in lexicographic order per spec.
4179 RootedValue parametersVal(cx);
4180 if (!JS_GetProperty(cx, typeObj, "parameters", &parametersVal)) {
4181 return false;
4184 ValTypeVector params;
4185 if (!ParseValTypes(cx, parametersVal, params)) {
4186 return false;
4189 RootedValue resultsVal(cx);
4190 if (!JS_GetProperty(cx, typeObj, "results", &resultsVal)) {
4191 return false;
4194 ValTypeVector results;
4195 if (!ParseValTypes(cx, resultsVal, results)) {
4196 return false;
4199 // Get the target function
4201 if (!IsCallableNonCCW(args[1]) || IsWasmFunction(args[1])) {
4202 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
4203 JSMSG_WASM_BAD_FUNCTION_VALUE);
4204 return false;
4206 RootedObject func(cx, &args[1].toObject());
4208 RootedObject proto(
4209 cx, GetWasmConstructorPrototype(cx, args, JSProto_WasmFunction));
4210 if (!proto) {
4211 ReportOutOfMemory(cx);
4212 return false;
4215 RootedFunction wasmFunc(cx, WasmFunctionCreate(cx, func, std::move(params),
4216 std::move(results), proto));
4217 if (!wasmFunc) {
4218 ReportOutOfMemory(cx);
4219 return false;
4221 args.rval().setObject(*wasmFunc);
4223 return true;
4226 static constexpr char WasmFunctionName[] = "Function";
4228 static JSObject* CreateWasmFunctionConstructor(JSContext* cx, JSProtoKey key) {
4229 RootedObject proto(cx, &cx->global()->getFunctionConstructor());
4231 Rooted<JSAtom*> className(
4232 cx, Atomize(cx, WasmFunctionName, strlen(WasmFunctionName)));
4233 if (!className) {
4234 return nullptr;
4236 return NewFunctionWithProto(cx, WasmFunctionConstruct, 1,
4237 FunctionFlags::NATIVE_CTOR, nullptr, className,
4238 proto, gc::AllocKind::FUNCTION, TenuredObject);
4241 const JSFunctionSpec WasmFunctionMethods[] = {
4242 JS_FN("type", WasmFunctionType, 0, 0), JS_FS_END};
4244 const ClassSpec WasmFunctionClassSpec = {CreateWasmFunctionConstructor,
4245 CreateWasmFunctionPrototype,
4246 nullptr,
4247 nullptr,
4248 WasmFunctionMethods,
4249 nullptr,
4250 nullptr,
4251 ClassSpec::DontDefineConstructor};
4253 const JSClass js::WasmFunctionClass = {
4254 "WebAssembly.Function", 0, JS_NULL_CLASS_OPS, &WasmFunctionClassSpec};
4256 #endif
4258 // ============================================================================
4259 // WebAssembly class and static methods
4261 static bool WebAssembly_toSource(JSContext* cx, unsigned argc, Value* vp) {
4262 CallArgs args = CallArgsFromVp(argc, vp);
4263 args.rval().setString(cx->names().WebAssembly);
4264 return true;
4267 static bool RejectWithPendingException(JSContext* cx,
4268 Handle<PromiseObject*> promise) {
4269 if (!cx->isExceptionPending()) {
4270 return false;
4273 RootedValue rejectionValue(cx);
4274 if (!GetAndClearException(cx, &rejectionValue)) {
4275 return false;
4278 return PromiseObject::reject(cx, promise, rejectionValue);
4281 static bool Reject(JSContext* cx, const CompileArgs& args,
4282 Handle<PromiseObject*> promise, const UniqueChars& error) {
4283 if (!error) {
4284 ThrowCompileOutOfMemory(cx);
4285 return RejectWithPendingException(cx, promise);
4288 RootedObject stack(cx, promise->allocationSite());
4289 RootedString fileName(cx);
4290 if (const char* filename = args.scriptedCaller.filename.get()) {
4291 fileName =
4292 JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(filename, strlen(filename)));
4293 } else {
4294 fileName = JS_GetEmptyString(cx);
4296 if (!fileName) {
4297 return false;
4300 uint32_t line = args.scriptedCaller.line;
4302 // Ideally we'd report a JSMSG_WASM_COMPILE_ERROR here, but there's no easy
4303 // way to create an ErrorObject for an arbitrary error code with multiple
4304 // replacements.
4305 UniqueChars str(JS_smprintf("wasm validation error: %s", error.get()));
4306 if (!str) {
4307 return false;
4310 size_t len = strlen(str.get());
4311 RootedString message(cx, NewStringCopyN<CanGC>(cx, str.get(), len));
4312 if (!message) {
4313 return false;
4316 // There's no error |cause| available here.
4317 auto cause = JS::NothingHandleValue;
4319 RootedObject errorObj(
4320 cx, ErrorObject::create(cx, JSEXN_WASMCOMPILEERROR, stack, fileName, 0,
4321 line, JS::ColumnNumberOneOrigin(), nullptr,
4322 message, cause));
4323 if (!errorObj) {
4324 return false;
4327 RootedValue rejectionValue(cx, ObjectValue(*errorObj));
4328 return PromiseObject::reject(cx, promise, rejectionValue);
4331 static void LogAsync(JSContext* cx, const char* funcName,
4332 const Module& module) {
4333 Log(cx, "async %s succeeded%s", funcName,
4334 module.loggingDeserialized() ? " (loaded from cache)" : "");
4337 enum class Ret { Pair, Instance };
4339 class AsyncInstantiateTask : public OffThreadPromiseTask {
4340 SharedModule module_;
4341 PersistentRooted<ImportValues> imports_;
4342 Ret ret_;
4344 public:
4345 AsyncInstantiateTask(JSContext* cx, const Module& module, Ret ret,
4346 Handle<PromiseObject*> promise)
4347 : OffThreadPromiseTask(cx, promise),
4348 module_(&module),
4349 imports_(cx),
4350 ret_(ret) {}
4352 ImportValues& imports() { return imports_.get(); }
4354 bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
4355 RootedObject instanceProto(
4356 cx, &cx->global()->getPrototype(JSProto_WasmInstance));
4358 Rooted<WasmInstanceObject*> instanceObj(cx);
4359 if (!module_->instantiate(cx, imports_.get(), instanceProto,
4360 &instanceObj)) {
4361 return RejectWithPendingException(cx, promise);
4364 RootedValue resolutionValue(cx);
4365 if (ret_ == Ret::Instance) {
4366 resolutionValue = ObjectValue(*instanceObj);
4367 } else {
4368 RootedObject resultObj(cx, JS_NewPlainObject(cx));
4369 if (!resultObj) {
4370 return RejectWithPendingException(cx, promise);
4373 RootedObject moduleProto(cx,
4374 &cx->global()->getPrototype(JSProto_WasmModule));
4375 RootedObject moduleObj(
4376 cx, WasmModuleObject::create(cx, *module_, moduleProto));
4377 if (!moduleObj) {
4378 return RejectWithPendingException(cx, promise);
4381 RootedValue val(cx, ObjectValue(*moduleObj));
4382 if (!JS_DefineProperty(cx, resultObj, "module", val, JSPROP_ENUMERATE)) {
4383 return RejectWithPendingException(cx, promise);
4386 val = ObjectValue(*instanceObj);
4387 if (!JS_DefineProperty(cx, resultObj, "instance", val,
4388 JSPROP_ENUMERATE)) {
4389 return RejectWithPendingException(cx, promise);
4392 resolutionValue = ObjectValue(*resultObj);
4395 if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
4396 return RejectWithPendingException(cx, promise);
4399 LogAsync(cx, "instantiate", *module_);
4400 return true;
4404 static bool AsyncInstantiate(JSContext* cx, const Module& module,
4405 HandleObject importObj, Ret ret,
4406 Handle<PromiseObject*> promise) {
4407 auto task = js::MakeUnique<AsyncInstantiateTask>(cx, module, ret, promise);
4408 if (!task || !task->init(cx)) {
4409 return false;
4412 if (!GetImports(cx, module, importObj, &task->imports())) {
4413 return RejectWithPendingException(cx, promise);
4416 task.release()->dispatchResolveAndDestroy();
4417 return true;
4420 static bool ResolveCompile(JSContext* cx, const Module& module,
4421 Handle<PromiseObject*> promise) {
4422 RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmModule));
4423 RootedObject moduleObj(cx, WasmModuleObject::create(cx, module, proto));
4424 if (!moduleObj) {
4425 return RejectWithPendingException(cx, promise);
4428 RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
4429 if (!PromiseObject::resolve(cx, promise, resolutionValue)) {
4430 return RejectWithPendingException(cx, promise);
4433 LogAsync(cx, "compile", module);
4434 return true;
4437 struct CompileBufferTask : PromiseHelperTask {
4438 MutableBytes bytecode;
4439 SharedCompileArgs compileArgs;
4440 UniqueChars error;
4441 UniqueCharsVector warnings;
4442 SharedModule module;
4443 bool instantiate;
4444 PersistentRootedObject importObj;
4446 CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise,
4447 HandleObject importObj)
4448 : PromiseHelperTask(cx, promise),
4449 instantiate(true),
4450 importObj(cx, importObj) {}
4452 CompileBufferTask(JSContext* cx, Handle<PromiseObject*> promise)
4453 : PromiseHelperTask(cx, promise), instantiate(false) {}
4455 bool init(JSContext* cx, FeatureOptions options, const char* introducer) {
4456 compileArgs = InitCompileArgs(cx, options, introducer);
4457 if (!compileArgs) {
4458 return false;
4460 return PromiseHelperTask::init(cx);
4463 void execute() override {
4464 module = CompileBuffer(*compileArgs, *bytecode, &error, &warnings, nullptr);
4467 bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
4468 if (!ReportCompileWarnings(cx, warnings)) {
4469 return false;
4471 if (!module) {
4472 return Reject(cx, *compileArgs, promise, error);
4474 if (instantiate) {
4475 return AsyncInstantiate(cx, *module, importObj, Ret::Pair, promise);
4477 return ResolveCompile(cx, *module, promise);
4481 static bool RejectWithPendingException(JSContext* cx,
4482 Handle<PromiseObject*> promise,
4483 CallArgs& callArgs) {
4484 if (!RejectWithPendingException(cx, promise)) {
4485 return false;
4488 callArgs.rval().setObject(*promise);
4489 return true;
4492 static bool EnsurePromiseSupport(JSContext* cx) {
4493 if (!cx->runtime()->offThreadPromiseState.ref().initialized()) {
4494 JS_ReportErrorASCII(
4495 cx, "WebAssembly Promise APIs not supported in this runtime.");
4496 return false;
4498 return true;
4501 static bool GetBufferSource(JSContext* cx, CallArgs callArgs, const char* name,
4502 MutableBytes* bytecode) {
4503 if (!callArgs.requireAtLeast(cx, name, 1)) {
4504 return false;
4507 if (!callArgs[0].isObject()) {
4508 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
4509 JSMSG_WASM_BAD_BUF_ARG);
4510 return false;
4513 return GetBufferSource(cx, &callArgs[0].toObject(), JSMSG_WASM_BAD_BUF_ARG,
4514 bytecode);
4517 static bool WebAssembly_compile(JSContext* cx, unsigned argc, Value* vp) {
4518 if (!EnsurePromiseSupport(cx)) {
4519 return false;
4522 Log(cx, "async compile() started");
4524 Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
4525 if (!promise) {
4526 return false;
4529 CallArgs callArgs = CallArgsFromVp(argc, vp);
4531 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
4532 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4533 JSMSG_CSP_BLOCKED_WASM, "WebAssembly.compile");
4534 return RejectWithPendingException(cx, promise, callArgs);
4537 auto task = cx->make_unique<CompileBufferTask>(cx, promise);
4538 if (!task) {
4539 return false;
4542 if (!GetBufferSource(cx, callArgs, "WebAssembly.compile", &task->bytecode)) {
4543 return RejectWithPendingException(cx, promise, callArgs);
4546 FeatureOptions options;
4547 if (!options.init(cx, callArgs.get(1))) {
4548 return false;
4551 if (!task->init(cx, options, "WebAssembly.compile")) {
4552 return false;
4555 if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) {
4556 return false;
4559 callArgs.rval().setObject(*promise);
4560 return true;
4563 static bool GetInstantiateArgs(JSContext* cx, CallArgs callArgs,
4564 MutableHandleObject firstArg,
4565 MutableHandleObject importObj,
4566 MutableHandleValue featureOptions) {
4567 if (!callArgs.requireAtLeast(cx, "WebAssembly.instantiate", 1)) {
4568 return false;
4571 if (!callArgs[0].isObject()) {
4572 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
4573 JSMSG_WASM_BAD_BUF_MOD_ARG);
4574 return false;
4577 firstArg.set(&callArgs[0].toObject());
4579 if (!GetImportArg(cx, callArgs.get(1), importObj)) {
4580 return false;
4583 featureOptions.set(callArgs.get(2));
4584 return true;
4587 static bool WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp) {
4588 if (!EnsurePromiseSupport(cx)) {
4589 return false;
4592 Log(cx, "async instantiate() started");
4594 Rooted<PromiseObject*> promise(cx, PromiseObject::createSkippingExecutor(cx));
4595 if (!promise) {
4596 return false;
4599 CallArgs callArgs = CallArgsFromVp(argc, vp);
4601 RootedObject firstArg(cx);
4602 RootedObject importObj(cx);
4603 RootedValue featureOptions(cx);
4604 if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj,
4605 &featureOptions)) {
4606 return RejectWithPendingException(cx, promise, callArgs);
4609 const Module* module;
4610 if (IsModuleObject(firstArg, &module)) {
4611 if (!AsyncInstantiate(cx, *module, importObj, Ret::Instance, promise)) {
4612 return false;
4614 } else {
4615 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
4616 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4617 JSMSG_CSP_BLOCKED_WASM,
4618 "WebAssembly.instantiate");
4619 return RejectWithPendingException(cx, promise, callArgs);
4622 FeatureOptions options;
4623 if (!options.init(cx, featureOptions)) {
4624 return false;
4627 auto task = cx->make_unique<CompileBufferTask>(cx, promise, importObj);
4628 if (!task || !task->init(cx, options, "WebAssembly.instantiate")) {
4629 return false;
4632 if (!GetBufferSource(cx, firstArg, JSMSG_WASM_BAD_BUF_MOD_ARG,
4633 &task->bytecode)) {
4634 return RejectWithPendingException(cx, promise, callArgs);
4637 if (!StartOffThreadPromiseHelperTask(cx, std::move(task))) {
4638 return false;
4642 callArgs.rval().setObject(*promise);
4643 return true;
4646 static bool WebAssembly_validate(JSContext* cx, unsigned argc, Value* vp) {
4647 CallArgs callArgs = CallArgsFromVp(argc, vp);
4649 MutableBytes bytecode;
4650 if (!GetBufferSource(cx, callArgs, "WebAssembly.validate", &bytecode)) {
4651 return false;
4654 FeatureOptions options;
4655 UniqueChars error;
4656 bool validated = Validate(cx, *bytecode, options, &error);
4658 // If the reason for validation failure was OOM (signalled by null error
4659 // message), report out-of-memory so that validate's return is always
4660 // correct.
4661 if (!validated && !error) {
4662 ReportOutOfMemory(cx);
4663 return false;
4666 if (error) {
4667 MOZ_ASSERT(!validated);
4668 Log(cx, "validate() failed with: %s", error.get());
4671 callArgs.rval().setBoolean(validated);
4672 return true;
4675 static bool EnsureStreamSupport(JSContext* cx) {
4676 // This should match wasm::StreamingCompilationAvailable().
4678 if (!EnsurePromiseSupport(cx)) {
4679 return false;
4682 if (!CanUseExtraThreads()) {
4683 JS_ReportErrorASCII(
4684 cx, "WebAssembly.compileStreaming not supported with --no-threads");
4685 return false;
4688 if (!cx->runtime()->consumeStreamCallback) {
4689 JS_ReportErrorASCII(cx,
4690 "WebAssembly streaming not supported in this runtime");
4691 return false;
4694 return true;
4697 // This value is chosen and asserted to be disjoint from any host error code.
4698 static const size_t StreamOOMCode = 0;
4700 static bool RejectWithStreamErrorNumber(JSContext* cx, size_t errorCode,
4701 Handle<PromiseObject*> promise) {
4702 if (errorCode == StreamOOMCode) {
4703 ReportOutOfMemory(cx);
4704 return false;
4707 cx->runtime()->reportStreamErrorCallback(cx, errorCode);
4708 return RejectWithPendingException(cx, promise);
4711 class CompileStreamTask : public PromiseHelperTask, public JS::StreamConsumer {
4712 // The stream progresses monotonically through these states; the helper
4713 // thread wait()s for streamState_ to reach Closed.
4714 enum StreamState { Env, Code, Tail, Closed };
4715 ExclusiveWaitableData<StreamState> streamState_;
4717 // Immutable:
4718 const bool instantiate_;
4719 const PersistentRootedObject importObj_;
4721 // Immutable after noteResponseURLs() which is called at most once before
4722 // first call on stream thread:
4723 const MutableCompileArgs compileArgs_;
4725 // Immutable after Env state:
4726 Bytes envBytes_;
4727 SectionRange codeSection_;
4729 // The code section vector is resized once during the Env state and filled
4730 // in chunk by chunk during the Code state, updating the end-pointer after
4731 // each chunk:
4732 Bytes codeBytes_;
4733 uint8_t* codeBytesEnd_;
4734 ExclusiveBytesPtr exclusiveCodeBytesEnd_;
4736 // Immutable after Tail state:
4737 Bytes tailBytes_;
4738 ExclusiveStreamEndData exclusiveStreamEnd_;
4740 // Written once before Closed state and read in Closed state on main thread:
4741 SharedModule module_;
4742 Maybe<size_t> streamError_;
4743 UniqueChars compileError_;
4744 UniqueCharsVector warnings_;
4746 // Set on stream thread and read racily on helper thread to abort compilation:
4747 Atomic<bool> streamFailed_;
4749 // Called on some thread before consumeChunk(), streamEnd(), streamError()):
4751 void noteResponseURLs(const char* url, const char* sourceMapUrl) override {
4752 if (url) {
4753 compileArgs_->scriptedCaller.filename = DuplicateString(url);
4754 compileArgs_->scriptedCaller.filenameIsURL = true;
4756 if (sourceMapUrl) {
4757 compileArgs_->sourceMapURL = DuplicateString(sourceMapUrl);
4761 // Called on a stream thread:
4763 // Until StartOffThreadPromiseHelperTask succeeds, we are responsible for
4764 // dispatching ourselves back to the JS thread.
4766 // Warning: After this function returns, 'this' can be deleted at any time, so
4767 // the caller must immediately return from the stream callback.
4768 void setClosedAndDestroyBeforeHelperThreadStarted() {
4769 streamState_.lock().get() = Closed;
4770 dispatchResolveAndDestroy();
4773 // See setClosedAndDestroyBeforeHelperThreadStarted() comment.
4774 bool rejectAndDestroyBeforeHelperThreadStarted(size_t errorNumber) {
4775 MOZ_ASSERT(streamState_.lock() == Env);
4776 MOZ_ASSERT(!streamError_);
4777 streamError_ = Some(errorNumber);
4778 setClosedAndDestroyBeforeHelperThreadStarted();
4779 return false;
4782 // Once StartOffThreadPromiseHelperTask succeeds, the helper thread will
4783 // dispatchResolveAndDestroy() after execute() returns, but execute()
4784 // wait()s for state to be Closed.
4786 // Warning: After this function returns, 'this' can be deleted at any time, so
4787 // the caller must immediately return from the stream callback.
4788 void setClosedAndDestroyAfterHelperThreadStarted() {
4789 auto streamState = streamState_.lock();
4790 MOZ_ASSERT(streamState != Closed);
4791 streamState.get() = Closed;
4792 streamState.notify_one(/* stream closed */);
4795 // See setClosedAndDestroyAfterHelperThreadStarted() comment.
4796 bool rejectAndDestroyAfterHelperThreadStarted(size_t errorNumber) {
4797 MOZ_ASSERT(!streamError_);
4798 streamError_ = Some(errorNumber);
4799 streamFailed_ = true;
4800 exclusiveCodeBytesEnd_.lock().notify_one();
4801 exclusiveStreamEnd_.lock().notify_one();
4802 setClosedAndDestroyAfterHelperThreadStarted();
4803 return false;
4806 bool consumeChunk(const uint8_t* begin, size_t length) override {
4807 switch (streamState_.lock().get()) {
4808 case Env: {
4809 if (!envBytes_.append(begin, length)) {
4810 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4813 if (!StartsCodeSection(envBytes_.begin(), envBytes_.end(),
4814 &codeSection_)) {
4815 return true;
4818 uint32_t extraBytes = envBytes_.length() - codeSection_.start;
4819 if (extraBytes) {
4820 envBytes_.shrinkTo(codeSection_.start);
4823 if (codeSection_.size > MaxCodeSectionBytes) {
4824 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4827 if (!codeBytes_.resize(codeSection_.size)) {
4828 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4831 codeBytesEnd_ = codeBytes_.begin();
4832 exclusiveCodeBytesEnd_.lock().get() = codeBytesEnd_;
4834 if (!StartOffThreadPromiseHelperTask(this)) {
4835 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4838 // Set the state to Code iff StartOffThreadPromiseHelperTask()
4839 // succeeds so that the state tells us whether we are before or
4840 // after the helper thread started.
4841 streamState_.lock().get() = Code;
4843 if (extraBytes) {
4844 return consumeChunk(begin + length - extraBytes, extraBytes);
4847 return true;
4849 case Code: {
4850 size_t copyLength =
4851 std::min<size_t>(length, codeBytes_.end() - codeBytesEnd_);
4852 memcpy(codeBytesEnd_, begin, copyLength);
4853 codeBytesEnd_ += copyLength;
4856 auto codeStreamEnd = exclusiveCodeBytesEnd_.lock();
4857 codeStreamEnd.get() = codeBytesEnd_;
4858 codeStreamEnd.notify_one();
4861 if (codeBytesEnd_ != codeBytes_.end()) {
4862 return true;
4865 streamState_.lock().get() = Tail;
4867 if (uint32_t extraBytes = length - copyLength) {
4868 return consumeChunk(begin + copyLength, extraBytes);
4871 return true;
4873 case Tail: {
4874 if (!tailBytes_.append(begin, length)) {
4875 return rejectAndDestroyAfterHelperThreadStarted(StreamOOMCode);
4878 return true;
4880 case Closed:
4881 MOZ_CRASH("consumeChunk() in Closed state");
4883 MOZ_CRASH("unreachable");
4886 void streamEnd(JS::OptimizedEncodingListener* tier2Listener) override {
4887 switch (streamState_.lock().get()) {
4888 case Env: {
4889 SharedBytes bytecode = js_new<ShareableBytes>(std::move(envBytes_));
4890 if (!bytecode) {
4891 rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode);
4892 return;
4894 module_ = CompileBuffer(*compileArgs_, *bytecode, &compileError_,
4895 &warnings_, nullptr);
4896 setClosedAndDestroyBeforeHelperThreadStarted();
4897 return;
4899 case Code:
4900 case Tail:
4901 // Unlock exclusiveStreamEnd_ before locking streamState_.
4903 auto streamEnd = exclusiveStreamEnd_.lock();
4904 MOZ_ASSERT(!streamEnd->reached);
4905 streamEnd->reached = true;
4906 streamEnd->tailBytes = &tailBytes_;
4907 streamEnd->tier2Listener = tier2Listener;
4908 streamEnd.notify_one();
4910 setClosedAndDestroyAfterHelperThreadStarted();
4911 return;
4912 case Closed:
4913 MOZ_CRASH("streamEnd() in Closed state");
4917 void streamError(size_t errorCode) override {
4918 MOZ_ASSERT(errorCode != StreamOOMCode);
4919 switch (streamState_.lock().get()) {
4920 case Env:
4921 rejectAndDestroyBeforeHelperThreadStarted(errorCode);
4922 return;
4923 case Tail:
4924 case Code:
4925 rejectAndDestroyAfterHelperThreadStarted(errorCode);
4926 return;
4927 case Closed:
4928 MOZ_CRASH("streamError() in Closed state");
4932 void consumeOptimizedEncoding(const uint8_t* begin, size_t length) override {
4933 module_ = Module::deserialize(begin, length);
4935 MOZ_ASSERT(streamState_.lock().get() == Env);
4936 setClosedAndDestroyBeforeHelperThreadStarted();
4939 // Called on a helper thread:
4941 void execute() override {
4942 module_ = CompileStreaming(*compileArgs_, envBytes_, codeBytes_,
4943 exclusiveCodeBytesEnd_, exclusiveStreamEnd_,
4944 streamFailed_, &compileError_, &warnings_);
4946 // When execute() returns, the CompileStreamTask will be dispatched
4947 // back to its JS thread to call resolve() and then be destroyed. We
4948 // can't let this happen until the stream has been closed lest
4949 // consumeChunk() or streamEnd() be called on a dead object.
4950 auto streamState = streamState_.lock();
4951 while (streamState != Closed) {
4952 streamState.wait(/* stream closed */);
4956 // Called on a JS thread after streaming compilation completes/errors:
4958 bool resolve(JSContext* cx, Handle<PromiseObject*> promise) override {
4959 MOZ_ASSERT(streamState_.lock() == Closed);
4961 if (!ReportCompileWarnings(cx, warnings_)) {
4962 return false;
4964 if (module_) {
4965 MOZ_ASSERT(!streamFailed_ && !streamError_ && !compileError_);
4966 if (instantiate_) {
4967 return AsyncInstantiate(cx, *module_, importObj_, Ret::Pair, promise);
4969 return ResolveCompile(cx, *module_, promise);
4972 if (streamError_) {
4973 return RejectWithStreamErrorNumber(cx, *streamError_, promise);
4976 return Reject(cx, *compileArgs_, promise, compileError_);
4979 public:
4980 CompileStreamTask(JSContext* cx, Handle<PromiseObject*> promise,
4981 CompileArgs& compileArgs, bool instantiate,
4982 HandleObject importObj)
4983 : PromiseHelperTask(cx, promise),
4984 streamState_(mutexid::WasmStreamStatus, Env),
4985 instantiate_(instantiate),
4986 importObj_(cx, importObj),
4987 compileArgs_(&compileArgs),
4988 codeSection_{},
4989 codeBytesEnd_(nullptr),
4990 exclusiveCodeBytesEnd_(mutexid::WasmCodeBytesEnd, nullptr),
4991 exclusiveStreamEnd_(mutexid::WasmStreamEnd),
4992 streamFailed_(false) {
4993 MOZ_ASSERT_IF(importObj_, instantiate_);
4997 // A short-lived object that captures the arguments of a
4998 // WebAssembly.{compileStreaming,instantiateStreaming} while waiting for
4999 // the Promise<Response> to resolve to a (hopefully) Promise.
5000 class ResolveResponseClosure : public NativeObject {
5001 static const unsigned COMPILE_ARGS_SLOT = 0;
5002 static const unsigned PROMISE_OBJ_SLOT = 1;
5003 static const unsigned INSTANTIATE_SLOT = 2;
5004 static const unsigned IMPORT_OBJ_SLOT = 3;
5005 static const JSClassOps classOps_;
5007 static void finalize(JS::GCContext* gcx, JSObject* obj) {
5008 auto& closure = obj->as<ResolveResponseClosure>();
5009 gcx->release(obj, &closure.compileArgs(),
5010 MemoryUse::WasmResolveResponseClosure);
5013 public:
5014 static const unsigned RESERVED_SLOTS = 4;
5015 static const JSClass class_;
5017 static ResolveResponseClosure* create(JSContext* cx, const CompileArgs& args,
5018 HandleObject promise, bool instantiate,
5019 HandleObject importObj) {
5020 MOZ_ASSERT_IF(importObj, instantiate);
5022 AutoSetNewObjectMetadata metadata(cx);
5023 auto* obj = NewObjectWithGivenProto<ResolveResponseClosure>(cx, nullptr);
5024 if (!obj) {
5025 return nullptr;
5028 args.AddRef();
5029 InitReservedSlot(obj, COMPILE_ARGS_SLOT, const_cast<CompileArgs*>(&args),
5030 MemoryUse::WasmResolveResponseClosure);
5031 obj->setReservedSlot(PROMISE_OBJ_SLOT, ObjectValue(*promise));
5032 obj->setReservedSlot(INSTANTIATE_SLOT, BooleanValue(instantiate));
5033 obj->setReservedSlot(IMPORT_OBJ_SLOT, ObjectOrNullValue(importObj));
5034 return obj;
5037 CompileArgs& compileArgs() const {
5038 return *(CompileArgs*)getReservedSlot(COMPILE_ARGS_SLOT).toPrivate();
5040 PromiseObject& promise() const {
5041 return getReservedSlot(PROMISE_OBJ_SLOT).toObject().as<PromiseObject>();
5043 bool instantiate() const {
5044 return getReservedSlot(INSTANTIATE_SLOT).toBoolean();
5046 JSObject* importObj() const {
5047 return getReservedSlot(IMPORT_OBJ_SLOT).toObjectOrNull();
5051 const JSClassOps ResolveResponseClosure::classOps_ = {
5052 nullptr, // addProperty
5053 nullptr, // delProperty
5054 nullptr, // enumerate
5055 nullptr, // newEnumerate
5056 nullptr, // resolve
5057 nullptr, // mayResolve
5058 ResolveResponseClosure::finalize, // finalize
5059 nullptr, // call
5060 nullptr, // construct
5061 nullptr, // trace
5064 const JSClass ResolveResponseClosure::class_ = {
5065 "WebAssembly ResolveResponseClosure",
5066 JSCLASS_DELAY_METADATA_BUILDER |
5067 JSCLASS_HAS_RESERVED_SLOTS(ResolveResponseClosure::RESERVED_SLOTS) |
5068 JSCLASS_FOREGROUND_FINALIZE,
5069 &ResolveResponseClosure::classOps_,
5072 static ResolveResponseClosure* ToResolveResponseClosure(CallArgs args) {
5073 return &args.callee()
5074 .as<JSFunction>()
5075 .getExtendedSlot(0)
5076 .toObject()
5077 .as<ResolveResponseClosure>();
5080 static bool RejectWithErrorNumber(JSContext* cx, uint32_t errorNumber,
5081 Handle<PromiseObject*> promise) {
5082 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, errorNumber);
5083 return RejectWithPendingException(cx, promise);
5086 static bool ResolveResponse_OnFulfilled(JSContext* cx, unsigned argc,
5087 Value* vp) {
5088 CallArgs callArgs = CallArgsFromVp(argc, vp);
5090 Rooted<ResolveResponseClosure*> closure(cx,
5091 ToResolveResponseClosure(callArgs));
5092 Rooted<PromiseObject*> promise(cx, &closure->promise());
5093 CompileArgs& compileArgs = closure->compileArgs();
5094 bool instantiate = closure->instantiate();
5095 Rooted<JSObject*> importObj(cx, closure->importObj());
5097 auto task = cx->make_unique<CompileStreamTask>(cx, promise, compileArgs,
5098 instantiate, importObj);
5099 if (!task || !task->init(cx)) {
5100 return false;
5103 if (!callArgs.get(0).isObject()) {
5104 return RejectWithErrorNumber(cx, JSMSG_WASM_BAD_RESPONSE_VALUE, promise);
5107 RootedObject response(cx, &callArgs.get(0).toObject());
5108 if (!cx->runtime()->consumeStreamCallback(cx, response, JS::MimeType::Wasm,
5109 task.get())) {
5110 return RejectWithPendingException(cx, promise);
5113 (void)task.release();
5115 callArgs.rval().setUndefined();
5116 return true;
5119 static bool ResolveResponse_OnRejected(JSContext* cx, unsigned argc,
5120 Value* vp) {
5121 CallArgs args = CallArgsFromVp(argc, vp);
5123 Rooted<ResolveResponseClosure*> closure(cx, ToResolveResponseClosure(args));
5124 Rooted<PromiseObject*> promise(cx, &closure->promise());
5126 if (!PromiseObject::reject(cx, promise, args.get(0))) {
5127 return false;
5130 args.rval().setUndefined();
5131 return true;
5134 static bool ResolveResponse(JSContext* cx, Handle<Value> responsePromise,
5135 Handle<Value> featureOptions,
5136 Handle<PromiseObject*> resultPromise,
5137 bool instantiate = false,
5138 HandleObject importObj = nullptr) {
5139 MOZ_ASSERT_IF(importObj, instantiate);
5141 const char* introducer = instantiate ? "WebAssembly.instantiateStreaming"
5142 : "WebAssembly.compileStreaming";
5144 FeatureOptions options;
5145 if (!options.init(cx, featureOptions)) {
5146 return false;
5149 SharedCompileArgs compileArgs = InitCompileArgs(cx, options, introducer);
5150 if (!compileArgs) {
5151 return false;
5154 RootedObject closure(
5155 cx, ResolveResponseClosure::create(cx, *compileArgs, resultPromise,
5156 instantiate, importObj));
5157 if (!closure) {
5158 return false;
5161 RootedFunction onResolved(
5162 cx, NewNativeFunction(cx, ResolveResponse_OnFulfilled, 1, nullptr,
5163 gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
5164 if (!onResolved) {
5165 return false;
5168 RootedFunction onRejected(
5169 cx, NewNativeFunction(cx, ResolveResponse_OnRejected, 1, nullptr,
5170 gc::AllocKind::FUNCTION_EXTENDED, GenericObject));
5171 if (!onRejected) {
5172 return false;
5175 onResolved->setExtendedSlot(0, ObjectValue(*closure));
5176 onRejected->setExtendedSlot(0, ObjectValue(*closure));
5178 RootedObject resolve(cx,
5179 PromiseObject::unforgeableResolve(cx, responsePromise));
5180 if (!resolve) {
5181 return false;
5184 return JS::AddPromiseReactions(cx, resolve, onResolved, onRejected);
5187 static bool WebAssembly_compileStreaming(JSContext* cx, unsigned argc,
5188 Value* vp) {
5189 if (!EnsureStreamSupport(cx)) {
5190 return false;
5193 Log(cx, "async compileStreaming() started");
5195 Rooted<PromiseObject*> resultPromise(
5196 cx, PromiseObject::createSkippingExecutor(cx));
5197 if (!resultPromise) {
5198 return false;
5201 CallArgs callArgs = CallArgsFromVp(argc, vp);
5203 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
5204 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5205 JSMSG_CSP_BLOCKED_WASM,
5206 "WebAssembly.compileStreaming");
5207 return RejectWithPendingException(cx, resultPromise, callArgs);
5210 Rooted<Value> responsePromise(cx, callArgs.get(0));
5211 Rooted<Value> featureOptions(cx, callArgs.get(1));
5212 if (!ResolveResponse(cx, responsePromise, featureOptions, resultPromise)) {
5213 return RejectWithPendingException(cx, resultPromise, callArgs);
5216 callArgs.rval().setObject(*resultPromise);
5217 return true;
5220 static bool WebAssembly_instantiateStreaming(JSContext* cx, unsigned argc,
5221 Value* vp) {
5222 if (!EnsureStreamSupport(cx)) {
5223 return false;
5226 Log(cx, "async instantiateStreaming() started");
5228 Rooted<PromiseObject*> resultPromise(
5229 cx, PromiseObject::createSkippingExecutor(cx));
5230 if (!resultPromise) {
5231 return false;
5234 CallArgs callArgs = CallArgsFromVp(argc, vp);
5236 if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM, nullptr)) {
5237 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5238 JSMSG_CSP_BLOCKED_WASM,
5239 "WebAssembly.instantiateStreaming");
5240 return RejectWithPendingException(cx, resultPromise, callArgs);
5243 Rooted<JSObject*> firstArg(cx);
5244 Rooted<JSObject*> importObj(cx);
5245 Rooted<Value> featureOptions(cx);
5246 if (!GetInstantiateArgs(cx, callArgs, &firstArg, &importObj,
5247 &featureOptions)) {
5248 return RejectWithPendingException(cx, resultPromise, callArgs);
5250 Rooted<Value> responsePromise(cx, ObjectValue(*firstArg.get()));
5252 if (!ResolveResponse(cx, responsePromise, featureOptions, resultPromise, true,
5253 importObj)) {
5254 return RejectWithPendingException(cx, resultPromise, callArgs);
5257 callArgs.rval().setObject(*resultPromise);
5258 return true;
5261 #ifdef ENABLE_WASM_MOZ_INTGEMM
5263 static bool WebAssembly_mozIntGemm(JSContext* cx, unsigned argc, Value* vp) {
5264 CallArgs args = CallArgsFromVp(argc, vp);
5266 Rooted<WasmModuleObject*> module(cx);
5267 if (!wasm::CompileBuiltinModule(cx, wasm::BuiltinModuleId::IntGemm,
5268 &module)) {
5269 ReportOutOfMemory(cx);
5270 return false;
5272 args.rval().set(ObjectValue(*module.get()));
5273 return true;
5276 static const JSFunctionSpec WebAssembly_mozIntGemm_methods[] = {
5277 JS_FN("mozIntGemm", WebAssembly_mozIntGemm, 0, JSPROP_ENUMERATE),
5278 JS_FS_END};
5280 #endif // ENABLE_WASM_MOZ_INTGEMM
5282 static const JSFunctionSpec WebAssembly_static_methods[] = {
5283 JS_FN("toSource", WebAssembly_toSource, 0, 0),
5284 JS_FN("compile", WebAssembly_compile, 1, JSPROP_ENUMERATE),
5285 JS_FN("instantiate", WebAssembly_instantiate, 1, JSPROP_ENUMERATE),
5286 JS_FN("validate", WebAssembly_validate, 1, JSPROP_ENUMERATE),
5287 JS_FN("compileStreaming", WebAssembly_compileStreaming, 1,
5288 JSPROP_ENUMERATE),
5289 JS_FN("instantiateStreaming", WebAssembly_instantiateStreaming, 1,
5290 JSPROP_ENUMERATE),
5291 JS_FS_END};
5293 static const JSPropertySpec WebAssembly_static_properties[] = {
5294 JS_STRING_SYM_PS(toStringTag, "WebAssembly", JSPROP_READONLY), JS_PS_END};
5296 static JSObject* CreateWebAssemblyObject(JSContext* cx, JSProtoKey key) {
5297 MOZ_RELEASE_ASSERT(HasSupport(cx));
5299 RootedObject proto(cx, &cx->global()->getObjectPrototype());
5300 return NewTenuredObjectWithGivenProto(cx, &WasmNamespaceObject::class_,
5301 proto);
5304 struct NameAndProtoKey {
5305 const char* const name;
5306 JSProtoKey key;
5309 static bool WebAssemblyDefineConstructor(JSContext* cx,
5310 Handle<WasmNamespaceObject*> wasm,
5311 NameAndProtoKey entry,
5312 MutableHandleValue ctorValue,
5313 MutableHandleId id) {
5314 JSObject* ctor = GlobalObject::getOrCreateConstructor(cx, entry.key);
5315 if (!ctor) {
5316 return false;
5318 ctorValue.setObject(*ctor);
5320 JSAtom* className = Atomize(cx, entry.name, strlen(entry.name));
5321 if (!className) {
5322 return false;
5324 id.set(AtomToId(className));
5326 return DefineDataProperty(cx, wasm, id, ctorValue, 0);
5329 static bool WebAssemblyClassFinish(JSContext* cx, HandleObject object,
5330 HandleObject proto) {
5331 Handle<WasmNamespaceObject*> wasm = object.as<WasmNamespaceObject>();
5333 constexpr NameAndProtoKey entries[] = {
5334 {"Module", JSProto_WasmModule},
5335 {"Instance", JSProto_WasmInstance},
5336 {"Memory", JSProto_WasmMemory},
5337 {"Table", JSProto_WasmTable},
5338 {"Global", JSProto_WasmGlobal},
5339 {"CompileError", GetExceptionProtoKey(JSEXN_WASMCOMPILEERROR)},
5340 {"LinkError", GetExceptionProtoKey(JSEXN_WASMLINKERROR)},
5341 {"RuntimeError", GetExceptionProtoKey(JSEXN_WASMRUNTIMEERROR)},
5342 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
5343 {"Function", JSProto_WasmFunction},
5344 #endif
5346 RootedValue ctorValue(cx);
5347 RootedId id(cx);
5348 for (const auto& entry : entries) {
5349 if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) {
5350 return false;
5354 if (ExceptionsAvailable(cx)) {
5355 constexpr NameAndProtoKey exceptionEntries[] = {
5356 {"Tag", JSProto_WasmTag},
5357 {"Exception", JSProto_WasmException},
5359 for (const auto& entry : exceptionEntries) {
5360 if (!WebAssemblyDefineConstructor(cx, wasm, entry, &ctorValue, &id)) {
5361 return false;
5366 RootedObject tagProto(
5367 cx, GlobalObject::getOrCreatePrototype(cx, JSProto_WasmTag));
5368 if (!tagProto) {
5369 ReportOutOfMemory(cx);
5370 return false;
5373 SharedTagType wrappedJSValueTagType(sWrappedJSValueTagType);
5374 WasmTagObject* wrappedJSValueTagObject =
5375 WasmTagObject::create(cx, wrappedJSValueTagType, tagProto);
5376 if (!wrappedJSValueTagObject) {
5377 return false;
5380 wasm->setWrappedJSValueTag(wrappedJSValueTagObject);
5382 #ifdef ENABLE_WASM_MOZ_INTGEMM
5383 if (MozIntGemmAvailable(cx) &&
5384 !JS_DefineFunctions(cx, wasm, WebAssembly_mozIntGemm_methods)) {
5385 return false;
5387 #endif
5389 return true;
5392 WasmNamespaceObject* WasmNamespaceObject::getOrCreate(JSContext* cx) {
5393 JSObject* wasm =
5394 GlobalObject::getOrCreateConstructor(cx, JSProto_WebAssembly);
5395 if (!wasm) {
5396 return nullptr;
5398 return &wasm->as<WasmNamespaceObject>();
5401 static const ClassSpec WebAssemblyClassSpec = {
5402 CreateWebAssemblyObject, nullptr, WebAssembly_static_methods,
5403 WebAssembly_static_properties, nullptr, nullptr,
5404 WebAssemblyClassFinish};
5406 const JSClass js::WasmNamespaceObject::class_ = {
5407 "WebAssembly",
5408 JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly) |
5409 JSCLASS_HAS_RESERVED_SLOTS(WasmNamespaceObject::RESERVED_SLOTS),
5410 JS_NULL_CLASS_OPS, &WebAssemblyClassSpec};
5412 // Sundry