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"
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.
98 using namespace js::jit
;
99 using namespace js::wasm
;
101 using mozilla::CheckedInt
;
102 using mozilla::Nothing
;
103 using mozilla::RangedPtr
;
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
);
114 // ============================================================================
117 static bool ThrowBadImportArg(JSContext
* cx
) {
118 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
119 JSMSG_WASM_BAD_IMPORT_ARG
);
123 static bool ThrowBadImportType(JSContext
* cx
, const CacheableName
& field
,
125 UniqueChars fieldQuoted
= field
.toQuotedString(cx
);
126 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
127 JSMSG_WASM_BAD_IMPORT_TYPE
, fieldQuoted
.get(), str
);
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
);
161 MutableHandle
<JSObject
*> builtinInstance
=
162 builtinInstances
[*builtinModule
];
163 if (!builtinInstance
&& !wasm::InstantiateBuiltinModule(
164 cx
, *builtinModule
, builtinInstance
)) {
167 importModuleObject
= builtinInstance
;
169 RootedId
moduleName(cx
);
170 if (!import
.module
.toPropertyKey(cx
, &moduleName
)) {
174 if (!GetProperty(cx
, importObj
, importObj
, moduleName
,
175 &importModuleValue
)) {
179 if (!importModuleValue
.isObject()) {
180 UniqueChars moduleQuoted
= import
.module
.toQuotedString(cx
);
181 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
182 JSMSG_WASM_BAD_IMPORT_FIELD
,
187 importModuleObject
= &importModuleValue
.toObject();
190 RootedId
fieldName(cx
);
191 if (!import
.field
.toPropertyKey(cx
, &fieldName
)) {
195 if (!GetProperty(cx
, importModuleObject
, importModuleObject
, fieldName
,
196 &importFieldValue
)) {
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())) {
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
);
227 if (!imports
->tables
.append(obj
)) {
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
>())) {
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(),
265 if (!imports
->tagObjs
.append(obj
)) {
266 ReportOutOfMemory(cx
);
271 case DefinitionKind::Global
: {
272 const uint32_t index
= globalIndex
++;
273 const GlobalDesc
& global
= globals
[index
];
274 MOZ_ASSERT(global
.importIndex() == index
);
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
);
288 bool matches
= global
.isMutable()
289 ? obj
->type() == global
.type()
290 : ValType::isSubTypeOf(obj
->type(), global
.type());
292 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
293 JSMSG_WASM_BAD_GLOB_TYPE_LINK
);
297 if (imports
->globalObjs
.length() <= index
&&
298 !imports
->globalObjs
.resize(index
+ 1)) {
299 ReportOutOfMemory(cx
);
302 imports
->globalObjs
[index
] = obj
;
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
);
320 if (!Val::fromJSValue(cx
, global
.type(), importFieldValue
, &val
)) {
325 if (!imports
->globalValues
.append(val
)) {
334 MOZ_ASSERT(globalIndex
== globals
.length() ||
335 !globals
[globalIndex
].isImport());
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.
347 if (JS::DescribeScriptedCaller(cx
, &af
, &caller
->line
)) {
349 FormatIntroducedFilename(af
.get(), caller
->line
, introducer
);
350 if (!caller
->filename
) {
351 ReportOutOfMemory(cx
);
359 static SharedCompileArgs
InitCompileArgs(JSContext
* cx
, FeatureOptions options
,
360 const char* introducer
) {
361 ScriptedCaller scriptedCaller
;
362 if (!DescribeScriptedCaller(cx
, &scriptedCaller
, introducer
)) {
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
)) {
379 MutableBytes bytecode
= cx
->new_
<ShareableBytes
>();
384 if (!bytecode
->append((uint8_t*)code
->dataPointerEither().unwrap(),
385 code
->byteLength().valueOr(0))) {
386 ReportOutOfMemory(cx
);
390 FeatureOptions options
;
391 SharedCompileArgs compileArgs
= InitCompileArgs(cx
, options
, "wasm_eval");
397 UniqueCharsVector warnings
;
398 SharedModule module
=
399 CompileBuffer(*compileArgs
, *bytecode
, &error
, &warnings
, nullptr);
402 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
403 JSMSG_WASM_COMPILE_ERROR
, error
.get());
406 return ThrowCompileOutOfMemory(cx
);
409 Rooted
<ImportValues
> imports(cx
);
410 if (!GetImports(cx
, *module
, importObj
, imports
.address())) {
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;
424 explicit SerializeListener(Bytes
* serialized
) : serialized(serialized
) {}
426 void storeOptimizedEncoding(const uint8_t* bytes
, size_t length
) override
{
429 if (serialized
->resizeUninitialized(length
)) {
430 memcpy(serialized
->begin(), bytes
, length
);
435 bool wasm::CompileAndSerialize(JSContext
* cx
, const ShareableBytes
& bytecode
,
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());
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
462 compileArgs
->features
= FeatureArgs::build(cx
, FeatureOptions());
464 SerializeListener
listener(serialized
);
467 UniqueCharsVector warnings
;
468 SharedModule module
=
469 CompileBuffer(*compileArgs
, bytecode
, &error
, &warnings
, &listener
);
471 fprintf(stderr
, "Compilation error: %s\n", error
? error
.get() : "oom");
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());
485 ReportOutOfMemory(cx
);
489 moduleObj
.set(module
->createObject(cx
));
493 // ============================================================================
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
) {
507 if (!ToNumber(cx
, v
, &x
)) {
512 if (mozilla::IsNegativeZero(x
)) {
517 if (!std::isfinite(x
)) {
518 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
519 JSMSG_WASM_BAD_ENFORCE_RANGE
, kind
, noun
);
524 x
= JS::ToInteger(x
);
527 if (x
< 0 || x
> double(max
)) {
528 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
529 JSMSG_WASM_BAD_ENFORCE_RANGE
, kind
, noun
);
534 MOZ_ASSERT(double(*val
) == x
);
538 static bool EnforceRangeU32(JSContext
* cx
, HandleValue v
, const char* kind
,
539 const char* noun
, uint32_t* u32
) {
541 if (!EnforceRange(cx
, v
, kind
, noun
, uint64_t(UINT32_MAX
), &u64
)) {
544 *u32
= uint32_t(u64
);
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
));
561 RootedId
id(cx
, AtomToId(atom
));
564 if (!GetProperty(cx
, obj
, obj
, id
, &val
)) {
568 if (val
.isUndefined()) {
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
,
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
) {
592 RootedId
indexTypeId(cx
, AtomToId(indexTypeAtom
));
594 RootedValue
indexTypeVal(cx
);
595 if (!GetProperty(cx
, obj
, obj
, indexTypeId
, &indexTypeVal
)) {
599 // The index type has a default value
600 if (!indexTypeVal
.isUndefined()) {
601 if (!ToIndexType(cx
, indexTypeVal
, &limits
->indexType
)) {
605 if (limits
->indexType
== IndexType::I64
&& !Memory64Available(cx
)) {
606 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
607 JSMSG_WASM_NO_MEM64_LINK
);
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;
620 bool haveInitial
= false;
621 if (!GetLimit(cx
, obj
, "initial", noun
, "initial size", range
, &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
,
636 limits
->initial
= limit
;
640 if (!(haveInitial
|| haveMinimum
)) {
641 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
642 JSMSG_WASM_MISSING_REQUIRED
, "initial");
645 if (haveInitial
&& haveMinimum
) {
646 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
647 JSMSG_WASM_SUPPLY_ONLY_ONE
, "minimum", "initial");
651 bool haveMaximum
= false;
652 if (!GetLimit(cx
, obj
, "maximum", noun
, "maximum size", range
, &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"));
669 RootedId
sharedId(cx
, AtomToId(sharedAtom
));
671 RootedValue
sharedVal(cx
);
672 if (!GetProperty(cx
, obj
, obj
, sharedId
, &sharedVal
)) {
676 // shared's default value is false, which is already the value set above.
677 if (!sharedVal
.isUndefined()) {
679 ToBoolean(sharedVal
) ? Shareable::True
: Shareable::False
;
681 if (limits
->shared
== Shareable::True
) {
683 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
684 JSMSG_WASM_MISSING_MAXIMUM
, noun
);
690 .getSharedMemoryAndAtomicsEnabled()) {
691 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
692 JSMSG_WASM_NO_SHMEM_LINK
);
702 static bool CheckLimits(JSContext
* cx
, uint64_t maximumField
, LimitsKind kind
,
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");
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");
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
)));
728 return NewNativeConstructor(cx
, Class::construct
, 1, className
);
731 static JSObject
* GetWasmConstructorPrototype(JSContext
* cx
,
732 const CallArgs
& callArgs
,
734 RootedObject
proto(cx
);
735 if (!GetPrototypeFromBuiltinConstructor(cx
, callArgs
, key
, &proto
)) {
739 proto
= GlobalObject::getOrCreatePrototype(cx
, key
);
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
)) {
752 RootedValue
nextParam(cx
);
755 if (!iterator
.next(&nextParam
, &done
)) {
763 if (!ToValType(cx
, nextParam
, &valType
) || !dest
.append(valType
)) {
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
));
781 for (ValType valType
: valTypes
) {
782 RootedString
type(cx
,
783 UTF8CharsToString(cx
, ToString(valType
, nullptr).get()));
787 if (!NewbornArrayPush(cx
, arrayObj
, StringValue(type
))) {
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
);
805 RootedObject
resultsObj(cx
, ValTypesToArray(cx
, type
.results()));
806 if (!resultsObj
|| !props
.append(IdValuePair(NameToId(cx
->names().results
),
807 ObjectValue(*resultsObj
)))) {
808 ReportOutOfMemory(cx
);
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
);
827 if (maximum
.isSome()) {
828 if (!props
.append(IdValuePair(NameToId(cx
->names().maximum
),
829 NumberValue(maximum
.value())))) {
830 ReportOutOfMemory(cx
);
836 IdValuePair(NameToId(cx
->names().minimum
), NumberValue(initial
)))) {
837 ReportOutOfMemory(cx
);
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
));
851 if (indexType
== IndexType::I32
) {
852 maxPagesNum
= double(mozilla::AssertedCast
<uint32_t>(maxPages
->value()));
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
);
865 if (indexType
== IndexType::I32
) {
866 minPagesNum
= double(mozilla::AssertedCast
<uint32_t>(minPages
.value()));
868 minPagesNum
= double(minPages
.value());
870 if (!props
.append(IdValuePair(NameToId(cx
->names().minimum
),
871 NumberValue(minPagesNum
)))) {
872 ReportOutOfMemory(cx
);
876 # ifdef ENABLE_WASM_MEMORY64
878 cx
, JS_NewStringCopyZ(cx
, indexType
== IndexType::I32
? "i32" : "i64"));
883 IdValuePair(NameToId(cx
->names().index
), StringValue(it
)))) {
884 ReportOutOfMemory(cx
);
890 IdValuePair(NameToId(cx
->names().shared
), BooleanValue(shared
)))) {
891 ReportOutOfMemory(cx
);
895 return NewPlainObjectWithUniqueNames(cx
, props
.begin(), props
.length());
898 static JSObject
* GlobalTypeToObject(JSContext
* cx
, ValType type
,
900 Rooted
<IdValueVector
> props(cx
, IdValueVector(cx
));
902 if (!props
.append(IdValuePair(NameToId(cx
->names().mutable_
),
903 BooleanValue(isMutable
)))) {
904 ReportOutOfMemory(cx
);
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
);
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
);
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
944 nullptr, // mayResolve
945 WasmModuleObject::finalize
, // finalize
947 nullptr, // construct
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
,
969 WasmModuleObject::methods
,
970 WasmModuleObject::properties
,
972 ClassSpec::DontDefineConstructor
};
974 const JSPropertySpec
WasmModuleObject::properties
[] = {
975 JS_STRING_SYM_PS(toStringTag
, "WebAssembly.Module", JSPROP_READONLY
),
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,
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
>();
1001 *module
= &mobj
->module();
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
)) {
1011 if (!args
[0].isObject() || !IsModuleObject(&args
[0].toObject(), module
)) {
1012 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1013 JSMSG_WASM_BAD_MOD_ARG
);
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"));
1036 names
->kind
= kind
->asPropertyName();
1038 JSAtom
* table
= Atomize(cx
, "table", strlen("table"));
1042 names
->table
= table
->asPropertyName();
1044 JSAtom
* memory
= Atomize(cx
, "memory", strlen("memory"));
1048 names
->memory
= memory
->asPropertyName();
1050 JSAtom
* tag
= Atomize(cx
, "tag", strlen("tag"));
1054 names
->tag
= tag
->asPropertyName();
1056 JSAtom
* type
= Atomize(cx
, "type", strlen("type"));
1060 names
->type
= type
->asPropertyName();
1065 static JSString
* KindToString(JSContext
* cx
, const KindNames
& names
,
1066 DefinitionKind kind
) {
1068 case DefinitionKind::Function
:
1069 return cx
->names().function
;
1070 case DefinitionKind::Table
:
1072 case DefinitionKind::Memory
:
1073 return names
.memory
;
1074 case DefinitionKind::Global
:
1075 return cx
->names().global
;
1076 case DefinitionKind::Tag
:
1080 MOZ_CRASH("invalid kind");
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
)) {
1092 KindNames
names(cx
);
1093 if (!InitKindNames(cx
, &names
)) {
1097 RootedValueVector
elems(cx
);
1098 if (!elems
.reserve(module
->imports().length())) {
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)) {
1120 JSString
* moduleStr
= import
.module
.toAtom(cx
);
1124 props
.infallibleAppend(
1125 IdValuePair(NameToId(cx
->names().module
), StringValue(moduleStr
)));
1127 JSString
* nameStr
= import
.field
.toAtom(cx
);
1131 props
.infallibleAppend(
1132 IdValuePair(NameToId(cx
->names().name
), StringValue(nameStr
)));
1134 JSString
* kindStr
= KindToString(cx
, names
, import
.kind
);
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
);
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
);
1158 case DefinitionKind::Memory
: {
1159 size_t memoryIndex
= numMemoryImport
++;
1160 const MemoryDesc
& memory
= metadata
.memories
[memoryIndex
];
1162 MemoryTypeToObject(cx
, memory
.isShared(), memory
.indexType(),
1163 memory
.initialPages(), memory
.maximumPages());
1166 case DefinitionKind::Global
: {
1167 size_t globalIndex
= numGlobalImport
++;
1168 const GlobalDesc
& global
= metadata
.globals
[globalIndex
];
1169 typeObj
= GlobalTypeToObject(cx
, global
.type(), global
.isMutable());
1172 case DefinitionKind::Tag
: {
1173 size_t tagIndex
= numTagImport
++;
1174 const TagDesc
& tag
= metadata
.tags
[tagIndex
];
1175 typeObj
= TagTypeToObject(cx
, tag
.type
->argTypes());
1180 if (!typeObj
|| !props
.append(IdValuePair(NameToId(names
.type
),
1181 ObjectValue(*typeObj
)))) {
1182 ReportOutOfMemory(cx
);
1185 #endif // ENABLE_WASM_TYPE_REFLECTIONS
1188 NewPlainObjectWithUniqueNames(cx
, props
.begin(), props
.length());
1193 elems
.infallibleAppend(ObjectValue(*obj
));
1196 JSObject
* arr
= NewDenseCopiedArray(cx
, elems
.length(), elems
.begin());
1201 args
.rval().setObject(*arr
);
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
)) {
1214 KindNames
names(cx
);
1215 if (!InitKindNames(cx
, &names
)) {
1219 RootedValueVector
elems(cx
);
1220 if (!elems
.reserve(module
->exports().length())) {
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)) {
1236 JSString
* nameStr
= exp
.fieldName().toAtom(cx
);
1240 props
.infallibleAppend(
1241 IdValuePair(NameToId(cx
->names().name
), StringValue(nameStr
)));
1243 JSString
* kindStr
= KindToString(cx
, names
, exp
.kind());
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
);
1259 case DefinitionKind::Table
: {
1260 const TableDesc
& table
= metadata
.tables
[exp
.tableIndex()];
1261 typeObj
= TableTypeToObject(cx
, table
.elemType
, table
.initialLength
,
1262 table
.maximumLength
);
1265 case DefinitionKind::Memory
: {
1266 const MemoryDesc
& memory
= metadata
.memories
[exp
.memoryIndex()];
1268 MemoryTypeToObject(cx
, memory
.isShared(), memory
.indexType(),
1269 memory
.initialPages(), memory
.maximumPages());
1272 case DefinitionKind::Global
: {
1273 const GlobalDesc
& global
= metadata
.globals
[exp
.globalIndex()];
1274 typeObj
= GlobalTypeToObject(cx
, global
.type(), global
.isMutable());
1277 case DefinitionKind::Tag
: {
1278 const TagDesc
& tag
= metadata
.tags
[exp
.tagIndex()];
1279 typeObj
= TagTypeToObject(cx
, tag
.type
->argTypes());
1284 if (!typeObj
|| !props
.append(IdValuePair(NameToId(names
.type
),
1285 ObjectValue(*typeObj
)))) {
1286 ReportOutOfMemory(cx
);
1289 #endif // ENABLE_WASM_TYPE_REFLECTIONS
1292 NewPlainObjectWithUniqueNames(cx
, props
.begin(), props
.length());
1297 elems
.infallibleAppend(ObjectValue(*obj
));
1300 JSObject
* arr
= NewDenseCopiedArray(cx
, elems
.length(), elems
.begin());
1305 args
.rval().setObject(*arr
);
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",
1319 Vector
<char, 8> name(cx
);
1321 RootedString
str(cx
, ToString(cx
, args
.get(1)));
1326 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
1331 if (!name
.initLengthUninitialized(
1332 JS::GetDeflatedUTF8StringLength(linear
))) {
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()) {
1346 if (memcmp(name
.begin(), cs
.name
.begin(), name
.length()) != 0) {
1350 buf
= ArrayBufferObject::createZeroed(cx
, cs
.payload
->length());
1355 memcpy(buf
->dataPointer(), cs
.payload
->begin(), cs
.payload
->length());
1356 if (!elems
.append(ObjectValue(*buf
))) {
1361 JSObject
* arr
= NewDenseCopiedArray(cx
, elems
.length(), elems
.begin());
1366 args
.rval().setObject(*arr
);
1371 WasmModuleObject
* WasmModuleObject::create(JSContext
* cx
, const Module
& module
,
1372 HandleObject proto
) {
1373 AutoSetNewObjectMetadata
metadata(cx
);
1374 auto* obj
= NewObjectWithGivenProto
<WasmModuleObject
>(cx
, proto
);
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
);
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()));
1403 static bool GetBufferSource(JSContext
* cx
, JSObject
* obj
, unsigned errorNumber
,
1404 MutableBytes
* bytecode
) {
1405 *bytecode
= cx
->new_
<ShareableBytes
>();
1410 JSObject
* unwrapped
= CheckedUnwrapStatic(obj
);
1412 SharedMem
<uint8_t*> dataPointer
;
1414 if (!unwrapped
|| !IsBufferSource(unwrapped
, &dataPointer
, &byteLength
)) {
1415 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, errorNumber
);
1419 if (!(*bytecode
)->append(dataPointer
.unwrap(), byteLength
)) {
1420 ReportOutOfMemory(cx
);
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())) {
1438 if (warnings
.length() > numWarnings
) {
1439 if (!WarnNumberASCII(cx
, JSMSG_WASM_COMPILE_WARNING
,
1440 "other warnings suppressed")) {
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")) {
1458 if (!cx
->isRuntimeCodeGenEnabled(JS::RuntimeCode::WASM
, nullptr)) {
1459 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1460 JSMSG_CSP_BLOCKED_WASM
, "WebAssembly.Module");
1464 if (!callArgs
.requireAtLeast(cx
, "WebAssembly.Module", 1)) {
1468 if (!callArgs
[0].isObject()) {
1469 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1470 JSMSG_WASM_BAD_BUF_ARG
);
1474 MutableBytes bytecode
;
1475 if (!GetBufferSource(cx
, &callArgs
[0].toObject(), JSMSG_WASM_BAD_BUF_ARG
,
1480 FeatureOptions options
;
1481 if (!options
.init(cx
, callArgs
.get(1))) {
1485 SharedCompileArgs compileArgs
=
1486 InitCompileArgs(cx
, options
, "WebAssembly.Module");
1492 UniqueCharsVector warnings
;
1493 SharedModule module
=
1494 CompileBuffer(*compileArgs
, *bytecode
, &error
, &warnings
, nullptr);
1496 if (!ReportCompileWarnings(cx
, warnings
)) {
1501 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
1502 JSMSG_WASM_COMPILE_ERROR
, error
.get());
1505 return ThrowCompileOutOfMemory(cx
);
1509 cx
, GetWasmConstructorPrototype(cx
, callArgs
, JSProto_WasmModule
));
1511 ReportOutOfMemory(cx
);
1515 RootedObject
moduleObj(cx
, WasmModuleObject::create(cx
, *module
, proto
));
1520 Log(cx
, "sync new Module() succeded");
1522 callArgs
.rval().setObject(*moduleObj
);
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
1540 nullptr, // mayResolve
1541 WasmInstanceObject::finalize
, // finalize
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
,
1565 WasmInstanceObject::methods
,
1566 WasmInstanceObject::properties
,
1568 ClassSpec::DontDefineConstructor
};
1570 static bool IsInstance(HandleValue v
) {
1571 return v
.isObject() && v
.toObject().is
<WasmInstanceObject
>();
1575 bool WasmInstanceObject::exportsGetterImpl(JSContext
* cx
,
1576 const CallArgs
& args
) {
1577 args
.rval().setObject(
1578 args
.thisv().toObject().as
<WasmInstanceObject
>().exportsObj());
1583 bool WasmInstanceObject::exportsGetter(JSContext
* cx
, unsigned argc
,
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
),
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
{
1614 WasmFunctionScopeMap
& asWasmFunctionScopeMap() {
1615 return *(WasmFunctionScopeMap
*)this;
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
);
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
);
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()));
1661 ReportOutOfMemory(cx
);
1665 UniquePtr
<WasmFunctionScopeMap
> scopes
=
1666 js::MakeUnique
<WasmFunctionScopeMap
>(cx
->zone(), cx
->zone());
1668 ReportOutOfMemory(cx
);
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()) {
1682 Rooted
<UniquePtr
<GlobalObjectVector
>> indirectGlobalObjs(
1683 cx
, js::MakeUnique
<GlobalObjectVector
>(cx
->zone()));
1684 if (!indirectGlobalObjs
|| !indirectGlobalObjs
->resize(indirectGlobals
)) {
1685 ReportOutOfMemory(cx
);
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
);
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
));
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
)) {
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());
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")) {
1775 if (!args
.requireAtLeast(cx
, "WebAssembly.Instance", 1)) {
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
);
1786 RootedObject
importObj(cx
);
1787 if (!GetImportArg(cx
, args
.get(1), &importObj
)) {
1792 cx
, GetWasmConstructorPrototype(cx
, args
, JSProto_WasmInstance
));
1794 ReportOutOfMemory(cx
);
1798 Rooted
<ImportValues
> imports(cx
);
1799 if (!GetImports(cx
, *module
, importObj
, imports
.address())) {
1803 Rooted
<WasmInstanceObject
*> instanceObj(cx
);
1804 if (!module
->instantiate(cx
, imports
.get(), proto
, &instanceObj
)) {
1808 Log(cx
, "sync new Instance() succeeded");
1810 args
.rval().setObject(*instanceObj
);
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()
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
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
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
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
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
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
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
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
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.
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.
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
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.
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());
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
));
2113 fun
.set(NewNativeConstructor(cx
, WasmCall
, numArgs
, name
,
2114 gc::AllocKind::FUNCTION_EXTENDED
,
2115 TenuredObject
, FunctionFlags::ASMJS_CTOR
));
2120 // asm.js does not support jit entries.
2121 fun
->setWasmFuncIndex(funcIndex
);
2123 Rooted
<JSAtom
*> name(cx
, NumberToAtom(cx
, funcIndex
));
2127 RootedObject
proto(cx
);
2128 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
2129 proto
= GlobalObject::getOrCreatePrototype(cx
, JSProto_WasmFunction
);
2134 fun
.set(NewFunctionWithProto(
2135 cx
, WasmCall
, numArgs
, FunctionFlags::WASM
, nullptr, name
, proto
,
2136 gc::AllocKind::FUNCTION_EXTENDED
, TenuredObject
));
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()) {
2151 void* provisionalLazyJitEntryStub
= ProvisionalLazyJitEntryStub();
2152 MOZ_ASSERT(provisionalLazyJitEntryStub
);
2153 instance
.code().setJitEntryIfNull(funcIndex
,
2154 provisionalLazyJitEntryStub
);
2156 fun
->setWasmJitEntry(instance
.code().getAddressOfJitEntry(funcIndex
));
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
);
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
));
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
)
2199 Rooted
<WasmInstanceScope
*> instanceScope(
2200 cx
, WasmInstanceScope::create(cx
, instanceObj
));
2201 if (!instanceScope
) {
2205 instanceObj
->setReservedSlot(INSTANCE_SCOPE_SLOT
,
2206 PrivateGCThingValue(instanceScope
));
2208 return instanceScope
;
2212 WasmFunctionScope
* WasmInstanceObject::getFunctionScope(
2213 JSContext
* cx
, Handle
<WasmInstanceObject
*> instanceObj
,
2214 uint32_t funcIndex
) {
2216 instanceObj
->scopes().asWasmFunctionScopeMap().lookup(funcIndex
)) {
2220 Rooted
<WasmInstanceScope
*> instanceScope(
2221 cx
, WasmInstanceObject::getScope(cx
, instanceObj
));
2222 if (!instanceScope
) {
2226 Rooted
<WasmFunctionScope
*> funcScope(
2227 cx
, WasmFunctionScope::create(cx
, instanceScope
, funcIndex
));
2232 if (!instanceObj
->scopes().asWasmFunctionScopeMap().putNew(funcIndex
,
2234 ReportOutOfMemory(cx
);
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
2266 nullptr, // mayResolve
2267 WasmMemoryObject::finalize
, // finalize
2269 nullptr, // construct
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_
));
2290 if (MemoryControlAvailable(cx
)) {
2291 if (!JS_DefineFunctions(cx
, proto
,
2292 WasmMemoryObject::memoryControlMethods
)) {
2299 const ClassSpec
WasmMemoryObject::classSpec_
= {
2300 CreateWasmConstructor
<WasmMemoryObject
, WasmMemoryName
>,
2301 CreateWasmMemoryPrototype
,
2302 WasmMemoryObject::static_methods
,
2304 WasmMemoryObject::methods
,
2305 WasmMemoryObject::properties
,
2307 ClassSpec::DontDefineConstructor
};
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
);
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
);
2327 obj
->initReservedSlot(BUFFER_SLOT
, ObjectValue(*buffer
));
2328 obj
->initReservedSlot(ISHUGE_SLOT
, BooleanValue(isHuge
));
2329 MOZ_ASSERT(!obj
->hasObservers());
2335 bool WasmMemoryObject::construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2336 CallArgs args
= CallArgsFromVp(argc
, vp
);
2338 if (!ThrowIfNotConstructing(cx
, args
, "Memory")) {
2342 if (!args
.requireAtLeast(cx
, "WebAssembly.Memory", 1)) {
2346 if (!args
.get(0).isObject()) {
2347 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2348 JSMSG_WASM_BAD_DESC_ARG
, "memory");
2352 RootedObject
obj(cx
, &args
[0].toObject());
2354 if (!GetLimits(cx
, obj
, LimitsKind::Memory
, &limits
) ||
2355 !CheckLimits(cx
, MaxMemoryLimitField(limits
.indexType
),
2356 LimitsKind::Memory
, &limits
)) {
2360 if (Pages(limits
.initial
) > MaxMemoryPages(limits
.indexType
)) {
2361 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2362 JSMSG_WASM_MEM_IMP_LIMIT
);
2365 MemoryDesc
memory(limits
);
2367 Rooted
<ArrayBufferObjectMaybeShared
*> buffer(cx
,
2368 CreateWasmBuffer(cx
, memory
));
2373 RootedObject
proto(cx
,
2374 GetWasmConstructorPrototype(cx
, args
, JSProto_WasmMemory
));
2376 ReportOutOfMemory(cx
);
2380 Rooted
<WasmMemoryObject
*> memoryObj(
2381 cx
, WasmMemoryObject::create(
2382 cx
, buffer
, IsHugeMemoryEnabled(limits
.indexType
), proto
));
2387 args
.rval().setObject(*memoryObj
);
2391 static bool IsMemory(HandleValue v
) {
2392 return v
.isObject() && v
.toObject().is
<WasmMemoryObject
>();
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
));
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
);
2420 memoryObj
->setReservedSlot(BUFFER_SLOT
, ObjectValue(*newBuffer
));
2424 args
.rval().setObject(*buffer
);
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
),
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)) {
2449 if (!EnforceRangeU32(cx
, args
.get(0), "Memory", "grow delta", &delta
)) {
2453 uint32_t ret
= grow(memory
, delta
, cx
);
2455 if (ret
== uint32_t(-1)) {
2456 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_WASM_BAD_GROW
,
2461 args
.rval().setInt32(int32_t(ret
));
2466 bool WasmMemoryObject::grow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2467 CallArgs args
= CallArgsFromVp(argc
, vp
);
2468 return CallNonGenericMethod
<IsMemory
, growImpl
>(cx
, args
);
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)) {
2480 uint64_t byteOffset
;
2481 if (!EnforceRangeU64(cx
, args
.get(0), "Memory", "byte offset", &byteOffset
)) {
2486 if (!EnforceRangeU64(cx
, args
.get(1), "Memory", "length", &byteLen
)) {
2490 if (byteOffset
% wasm::PageSize
!= 0 || byteLen
% wasm::PageSize
!= 0) {
2491 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2492 JSMSG_WASM_UNALIGNED_ACCESS
);
2496 if (!wasm::MemoryBoundsCheck(byteOffset
, byteLen
,
2497 memory
->volatileMemoryLength())) {
2498 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2499 JSMSG_WASM_OUT_OF_BOUNDS
);
2503 discard(memory
, byteOffset
, byteLen
, cx
);
2505 args
.rval().setUndefined();
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
),
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
),
2525 const JSFunctionSpec
WasmMemoryObject::static_methods
[] = {JS_FS_END
};
2527 ArrayBufferObjectMaybeShared
& WasmMemoryObject::buffer() const {
2528 return getReservedSlot(BUFFER_SLOT
)
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()));
2549 args
.rval().setObject(*typeObj
);
2553 bool WasmMemoryObject::type(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2554 CallArgs args
= CallArgsFromVp(argc
, vp
);
2555 return CallNonGenericMethod
<IsMemory
, typeImpl
>(cx
, args
);
2559 size_t WasmMemoryObject::volatileMemoryLength() const {
2561 return sharedArrayRawBuffer()->volatileByteLength();
2563 return buffer().byteLength();
2566 wasm::Pages
WasmMemoryObject::volatilePages() const {
2568 return sharedArrayRawBuffer()->volatileWasmPages();
2570 return buffer().wasmPages();
2573 wasm::Pages
WasmMemoryObject::clampedMaxPages() const {
2575 return sharedArrayRawBuffer()->wasmClampedMaxPages();
2577 return buffer().wasmClampedMaxPages();
2580 Maybe
<wasm::Pages
> WasmMemoryObject::sourceMaxPages() const {
2582 return Some(sharedArrayRawBuffer()->wasmSourceMaxPages());
2584 return buffer().wasmSourceMaxPages();
2587 wasm::IndexType
WasmMemoryObject::indexType() const {
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(
2610 if (!hasObservers()) {
2611 auto observers
= MakeUnique
<InstanceSet
>(cx
->zone(), cx
->zone());
2613 ReportOutOfMemory(cx
);
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
2642 MOZ_ASSERT(mappedSize
< UINT32_MAX
);
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()));
2652 bool WasmMemoryObject::addMovingGrowObserver(JSContext
* cx
,
2653 WasmInstanceObject
* instance
) {
2654 MOZ_ASSERT(movingGrowable());
2656 InstanceSet
* observers
= getOrCreateObservers(cx
);
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
);
2672 uint64_t WasmMemoryObject::growShared(Handle
<WasmMemoryObject
*> memory
,
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();
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");
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
);
2721 newBuf
= ArrayBufferObject::wasmGrowToPagesInPlace(memory
->indexType(),
2722 newPages
, oldBuf
, cx
);
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();
2735 r
.front()->instance().onMovingGrowMemory(memory
);
2739 return oldNumPages
.value();
2743 void WasmMemoryObject::discard(Handle
<WasmMemoryObject
*> memory
,
2744 uint64_t byteOffset
, uint64_t byteLen
,
2746 if (memory
->isShared()) {
2747 Rooted
<SharedArrayBufferObject
*> buf(
2748 cx
, &memory
->buffer().as
<SharedArrayBufferObject
>());
2749 SharedArrayBufferObject::wasmDiscard(buf
, byteOffset
, byteLen
);
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
2771 nullptr, // mayResolve
2772 WasmTableObject::finalize
, // finalize
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
,
2794 WasmTableObject::methods
,
2795 WasmTableObject::properties
,
2797 ClassSpec::DontDefineConstructor
};
2799 bool WasmTableObject::isNewborn() const {
2800 MOZ_ASSERT(is
<WasmTableObject
>());
2801 return getReservedSlot(TABLE_SLOT
).isUndefined();
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
);
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].
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
);
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
));
2850 MOZ_ASSERT(obj
->isNewborn());
2852 TableDesc
td(tableType
, initialLength
, maximumLength
, Nothing(),
2854 /*isImported=*/true, /*isExported=*/true);
2856 SharedTable table
= Table::create(cx
, td
, obj
);
2861 size_t size
= table
->gcMallocBytes();
2862 InitReservedSlot(obj
, TABLE_SLOT
, table
.forget().take(), size
,
2863 MemoryUse::WasmTableTable
);
2865 MOZ_ASSERT(!obj
->isNewborn());
2870 bool WasmTableObject::construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2871 CallArgs args
= CallArgsFromVp(argc
, vp
);
2873 if (!ThrowIfNotConstructing(cx
, args
, "Table")) {
2877 if (!args
.requireAtLeast(cx
, "WebAssembly.Table", 1)) {
2881 if (!args
.get(0).isObject()) {
2882 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2883 JSMSG_WASM_BAD_DESC_ARG
, "table");
2887 RootedObject
obj(cx
, &args
[0].toObject());
2889 JSAtom
* elementAtom
= Atomize(cx
, "element", strlen("element"));
2893 RootedId
elementId(cx
, AtomToId(elementAtom
));
2895 RootedValue
elementVal(cx
);
2896 if (!GetProperty(cx
, obj
, obj
, elementId
, &elementVal
)) {
2901 if (!ToRefType(cx
, elementVal
, &tableType
)) {
2906 if (!GetLimits(cx
, obj
, LimitsKind::Table
, &limits
) ||
2907 !CheckLimits(cx
, MaxTableLimitField
, LimitsKind::Table
, &limits
)) {
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
);
2920 RootedObject
proto(cx
,
2921 GetWasmConstructorPrototype(cx
, args
, JSProto_WasmTable
));
2923 ReportOutOfMemory(cx
);
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
,
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
)) {
2949 // Skip initializing the table if the fill value is null, as that is the
2951 if (!initValue
.isNull() &&
2952 !table
->fillRange(cx
, 0, initialLength
, initValue
)) {
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
);
2965 args
.rval().setObject(*table
);
2969 static bool IsTable(HandleValue v
) {
2970 return v
.isObject() && v
.toObject().is
<WasmTableObject
>();
2974 bool WasmTableObject::lengthGetterImpl(JSContext
* cx
, const CallArgs
& args
) {
2975 args
.rval().setNumber(
2976 args
.thisv().toObject().as
<WasmTableObject
>().table().length());
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
),
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
)) {
2997 if (*index
>= table
.length()) {
2998 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2999 JSMSG_WASM_BAD_RANGE
, "Table", noun
);
3006 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
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()));
3015 args
.rval().setObject(*typeObj
);
3020 bool WasmTableObject::type(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3021 CallArgs args
= CallArgsFromVp(argc
, vp
);
3022 return CallNonGenericMethod
<IsTable
, typeImpl
>(cx
, args
);
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)) {
3037 if (!ToTableIndex(cx
, args
.get(0), table
, "get index", &index
)) {
3041 return table
.getValue(cx
, index
, args
.rval());
3045 bool WasmTableObject::get(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3046 CallArgs args
= CallArgsFromVp(argc
, vp
);
3047 return CallNonGenericMethod
<IsTable
, getImpl
>(cx
, args
);
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)) {
3061 if (!ToTableIndex(cx
, args
.get(0), table
, "set index", &index
)) {
3065 RootedValue
fillValue(
3066 cx
, args
.length() < 2 ? RefTypeDefautValue(table
.elemType()) : args
[1]);
3067 if (!tableObj
->fillRange(cx
, index
, 1, fillValue
)) {
3071 args
.rval().setUndefined();
3076 bool WasmTableObject::set(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3077 CallArgs args
= CallArgsFromVp(argc
, vp
);
3078 return CallNonGenericMethod
<IsTable
, setImpl
>(cx
, args
);
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)) {
3092 if (!EnforceRangeU32(cx
, args
.get(0), "Table", "grow delta", &delta
)) {
3096 RootedValue
fillValue(
3097 cx
, args
.length() < 2 ? RefTypeDefautValue(table
.elemType()) : args
[1]);
3098 if (!CheckRefTypeValue(cx
, table
.elemType(), fillValue
)) {
3102 uint32_t oldLength
= table
.grow(delta
);
3104 if (oldLength
== uint32_t(-1)) {
3105 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_WASM_BAD_GROW
,
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
)) {
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
);
3126 args
.rval().setInt32(int32_t(oldLength
));
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
),
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
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
)) {
3163 switch (tab
.repr()) {
3164 case TableRepr::Func
:
3165 MOZ_RELEASE_ASSERT(!tab
.isAsmJS());
3166 tab
.fillFuncRef(index
, length
, FuncRef::fromJSFunction(fun
), cx
);
3168 case TableRepr::Ref
:
3169 tab
.fillAnyRef(index
, length
, any
);
3175 // ============================================================================
3176 // WebAssembly.global class and methods
3178 const JSClassOps
WasmGlobalObject::classOps_
= {
3179 nullptr, // addProperty
3180 nullptr, // delProperty
3181 nullptr, // enumerate
3182 nullptr, // newEnumerate
3184 nullptr, // mayResolve
3185 WasmGlobalObject::finalize
, // finalize
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
,
3206 WasmGlobalObject::methods
,
3207 WasmGlobalObject::properties
,
3209 ClassSpec::DontDefineConstructor
};
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.
3220 global
->val().get().trace(trc
);
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
3229 global
->type().Release();
3230 gcx
->delete_(obj
, &global
->val(), MemoryUse::WasmGlobalCell
);
3235 WasmGlobalObject
* WasmGlobalObject::create(JSContext
* cx
, HandleVal value
,
3236 bool isMutable
, HandleObject proto
) {
3237 Rooted
<WasmGlobalObject
*> obj(
3238 cx
, NewObjectWithGivenProto
<WasmGlobalObject
>(cx
, proto
));
3243 MOZ_ASSERT(obj
->isNewborn());
3244 MOZ_ASSERT(obj
->isTenured(), "assumed by global.set post barriers");
3246 GCPtrVal
* val
= js_new
<GCPtrVal
>(Val());
3248 ReportOutOfMemory(cx
);
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
3259 obj
->type().AddRef();
3261 MOZ_ASSERT(!obj
->isNewborn());
3267 bool WasmGlobalObject::construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3268 CallArgs args
= CallArgsFromVp(argc
, vp
);
3270 if (!ThrowIfNotConstructing(cx
, args
, "Global")) {
3274 if (!args
.requireAtLeast(cx
, "WebAssembly.Global", 1)) {
3278 if (!args
.get(0).isObject()) {
3279 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3280 JSMSG_WASM_BAD_DESC_ARG
, "global");
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
)) {
3293 RootedValue
typeVal(cx
);
3294 if (!JS_GetProperty(cx
, obj
, "value", &typeVal
)) {
3299 if (!ToValType(cx
, typeVal
, &globalType
)) {
3303 if (!globalType
.isExposable()) {
3304 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3305 JSMSG_WASM_BAD_VAL_TYPE
);
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())
3319 if (!Val::fromJSValue(cx
, globalType
, valueVal
, &globalVal
)) {
3323 valueVal
.set(args
.get(1));
3324 if (!valueVal
.isUndefined() &&
3325 !Val::fromJSValue(cx
, globalType
, valueVal
, &globalVal
)) {
3330 RootedObject
proto(cx
,
3331 GetWasmConstructorPrototype(cx
, args
, JSProto_WasmGlobal
));
3333 ReportOutOfMemory(cx
);
3337 WasmGlobalObject
* global
=
3338 WasmGlobalObject::create(cx
, globalVal
, isMutable
, proto
);
3343 args
.rval().setObject(*global
);
3347 static bool IsGlobal(HandleValue v
) {
3348 return v
.isObject() && v
.toObject().is
<WasmGlobalObject
>();
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
);
3360 return globalObj
.val().get().toJSValue(cx
, args
.rval());
3364 bool WasmGlobalObject::valueGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3365 CallArgs args
= CallArgsFromVp(argc
, vp
);
3366 return CallNonGenericMethod
<IsGlobal
, valueGetterImpl
>(cx
, args
);
3370 bool WasmGlobalObject::valueSetterImpl(JSContext
* cx
, const CallArgs
& args
) {
3371 if (!args
.requireAtLeast(cx
, "WebAssembly.Global setter", 1)) {
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
);
3384 if (!Val::fromJSValue(cx
, global
->type(), args
.get(0), &val
)) {
3387 global
->val() = val
.get();
3389 args
.rval().setUndefined();
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
),
3405 const JSFunctionSpec
WasmGlobalObject::methods
[] = {
3406 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3407 JS_FN("type", WasmGlobalObject::type
, 0, JSPROP_ENUMERATE
),
3409 JS_FN("valueOf", WasmGlobalObject::valueGetter
, 0, JSPROP_ENUMERATE
),
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
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()));
3434 args
.rval().setObject(*typeObj
);
3439 bool WasmGlobalObject::type(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3440 CallArgs args
= CallArgsFromVp(argc
, vp
);
3441 return CallNonGenericMethod
<IsGlobal
, typeImpl
>(cx
, args
);
3445 // ============================================================================
3446 // WebAssembly.Tag class and methods
3448 const JSClassOps
WasmTagObject::classOps_
= {
3449 nullptr, // addProperty
3450 nullptr, // delProperty
3451 nullptr, // enumerate
3452 nullptr, // newEnumerate
3454 nullptr, // mayResolve
3455 WasmTagObject::finalize
, // finalize
3457 nullptr, // construct
3461 const JSClass
WasmTagObject::class_
= {
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
,
3476 WasmTagObject::methods
,
3477 WasmTagObject::properties
,
3479 ClassSpec::DontDefineConstructor
};
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")) {
3498 if (!args
.requireAtLeast(cx
, "WebAssembly.Tag", 1)) {
3502 if (!args
.get(0).isObject()) {
3503 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3504 JSMSG_WASM_BAD_DESC_ARG
, "tag");
3508 RootedObject
obj(cx
, &args
[0].toObject());
3509 RootedValue
paramsVal(cx
);
3510 if (!JS_GetProperty(cx
, obj
, "parameters", ¶msVal
)) {
3514 ValTypeVector params
;
3515 if (!ParseValTypes(cx
, paramsVal
, params
)) {
3518 wasm::MutableTagType tagType
= js_new
<wasm::TagType
>();
3519 if (!tagType
|| !tagType
->initialize(std::move(params
))) {
3523 RootedObject
proto(cx
,
3524 GetWasmConstructorPrototype(cx
, args
, JSProto_WasmTag
));
3526 ReportOutOfMemory(cx
);
3530 Rooted
<WasmTagObject
*> tagObj(cx
, WasmTagObject::create(cx
, tagType
, proto
));
3535 args
.rval().setObject(*tagObj
);
3540 WasmTagObject
* WasmTagObject::create(JSContext
* cx
,
3541 const wasm::SharedTagType
& tagType
,
3542 HandleObject proto
) {
3543 Rooted
<WasmTagObject
*> obj(cx
,
3544 NewObjectWithGivenProto
<WasmTagObject
>(cx
, proto
));
3549 tagType
.get()->AddRef();
3550 obj
->initReservedSlot(TYPE_SLOT
, PrivateValue((void*)tagType
.get()));
3555 const JSPropertySpec
WasmTagObject::properties
[] = {
3556 JS_STRING_SYM_PS(toStringTag
, "WebAssembly.Tag", JSPROP_READONLY
),
3559 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
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()));
3567 args
.rval().setObject(*typeObj
);
3572 bool WasmTagObject::type(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3573 CallArgs args
= CallArgsFromVp(argc
, vp
);
3574 return CallNonGenericMethod
<IsTag
, typeImpl
>(cx
, args
);
3578 const JSFunctionSpec
WasmTagObject::methods
[] = {
3579 #ifdef ENABLE_WASM_TYPE_REFLECTIONS
3580 JS_FN("type", WasmTagObject::type
, 0, JSPROP_ENUMERATE
),
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
3607 nullptr, // mayResolve
3608 WasmExceptionObject::finalize
, // finalize
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
,
3629 WasmExceptionObject::methods
,
3630 WasmExceptionObject::properties
,
3632 ClassSpec::DontDefineConstructor
};
3635 void WasmExceptionObject::finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
3636 WasmExceptionObject
& exnObj
= obj
->as
<WasmExceptionObject
>();
3637 if (exnObj
.isNewborn()) {
3640 gcx
->free_(obj
, exnObj
.typedMem(), exnObj
.tagType()->tagSize(),
3641 MemoryUse::WasmExceptionData
);
3642 exnObj
.tagType()->Release();
3646 void WasmExceptionObject::trace(JSTracer
* trc
, JSObject
* obj
) {
3647 WasmExceptionObject
& exnObj
= obj
->as
<WasmExceptionObject
>();
3648 if (exnObj
.isNewborn()) {
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
{
3673 ExceptionOptions() : traceStack(false) {}
3675 [[nodiscard
]] bool init(JSContext
* cx
, HandleValue val
);
3678 bool ExceptionOptions::init(JSContext
* cx
, HandleValue val
) {
3679 if (val
.isNullOrUndefined()) {
3683 if (!val
.isObject()) {
3684 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3685 JSMSG_WASM_BAD_EXN_OPTIONS
);
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
)) {
3695 traceStack
= ToBoolean(traceStackVal
);
3700 bool WasmExceptionObject::construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3701 CallArgs args
= CallArgsFromVp(argc
, vp
);
3703 if (!ThrowIfNotConstructing(cx
, args
, "Exception")) {
3707 if (!args
.requireAtLeast(cx
, "WebAssembly.Exception", 2)) {
3711 if (!IsTag(args
[0])) {
3712 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3713 JSMSG_WASM_BAD_EXN_ARG
);
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
);
3724 JS::ForOfIterator
iterator(cx
);
3725 if (!iterator
.init(args
.get(1), JS::ForOfIterator::ThrowOnNonIterable
)) {
3729 // Get the optional 'options' parameter
3730 ExceptionOptions options
;
3731 if (!options
.init(cx
, args
.get(2))) {
3735 // Trace the stack if requested
3736 RootedObject
stack(cx
);
3737 if (options
.traceStack
&& !CaptureStack(cx
, &stack
)) {
3742 cx
, GetWasmConstructorPrototype(cx
, args
, JSProto_WasmException
));
3744 ReportOutOfMemory(cx
);
3748 Rooted
<WasmExceptionObject
*> exnObj(
3749 cx
, WasmExceptionObject::create(cx
, exnTag
, stack
, proto
));
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
++) {
3761 if (!iterator
.next(&nextArg
, &done
)) {
3765 UniqueChars
expected(JS_smprintf("%zu", params
.length()));
3766 UniqueChars
got(JS_smprintf("%zu", i
));
3767 if (!expected
|| !got
) {
3768 ReportOutOfMemory(cx
);
3772 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3773 JSMSG_WASM_BAD_EXN_PAYLOAD_LEN
, expected
.get(),
3778 if (!exnObj
->initArg(cx
, offsets
[i
], params
[i
], nextArg
)) {
3783 args
.rval().setObject(*exnObj
);
3788 WasmExceptionObject
* WasmExceptionObject::create(JSContext
* cx
,
3789 Handle
<WasmTagObject
*> tag
,
3791 HandleObject proto
) {
3792 Rooted
<WasmExceptionObject
*> obj(
3793 cx
, NewObjectWithGivenProto
<WasmExceptionObject
>(cx
, proto
));
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());
3803 ReportOutOfMemory(cx
);
3807 MOZ_ASSERT(obj
->isNewborn());
3808 obj
->initFixedSlot(TAG_SLOT
, ObjectValue(*tag
));
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());
3820 WasmExceptionObject
* WasmExceptionObject::wrapJSValue(JSContext
* cx
,
3821 HandleValue value
) {
3822 Rooted
<WasmNamespaceObject
*> wasm(cx
, WasmNamespaceObject::getOrCreate(cx
));
3827 Rooted
<AnyRef
> valueAnyRef(cx
);
3828 if (!AnyRef::fromJSValue(cx
, value
, &valueAnyRef
)) {
3832 Rooted
<WasmTagObject
*> wrappedJSValueTag(cx
, wasm
->wrappedJSValueTag());
3833 WasmExceptionObject
* exn
=
3834 WasmExceptionObject::create(cx
, wrappedJSValueTag
, nullptr, nullptr);
3838 MOZ_ASSERT(exn
->isWrappedJSValue());
3840 exn
->initRefArg(WrappedJSValueTagType_ValueOffset
, valueAnyRef
);
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
),
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)) {
3872 if (!IsTag(args
[0])) {
3873 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3874 JSMSG_WASM_BAD_EXN_ARG
);
3878 Rooted
<WasmTagObject
*> exnTag(cx
,
3879 &args
.get(0).toObject().as
<WasmTagObject
>());
3880 args
.rval().setBoolean(exnTag
.get() == &exnObj
->tag());
3886 bool WasmExceptionObject::isMethod(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3887 CallArgs args
= CallArgsFromVp(argc
, vp
);
3888 return CallNonGenericMethod
<IsException
, isImpl
>(cx
, args
);
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)) {
3900 if (!IsTag(args
[0])) {
3901 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3902 JSMSG_WASM_BAD_EXN_ARG
);
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
);
3915 if (!EnforceRangeU32(cx
, args
.get(1), "Exception", "getArg index", &index
)) {
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");
3926 uint32_t offset
= exnTag
->tagType()->argOffsets()[index
];
3927 RootedValue
result(cx
);
3928 if (!exnObj
->loadArg(cx
, offset
, params
[index
], &result
)) {
3931 args
.rval().set(result
);
3936 bool WasmExceptionObject::getArg(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3937 CallArgs args
= CallArgsFromVp(argc
, vp
);
3938 return CallNonGenericMethod
<IsException
, getArgImpl
>(cx
, args
);
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();
3950 JSPrincipals
* principals
= exnObj
->realm()->principals();
3951 RootedString
stackString(cx
);
3952 if (!BuildStackString(cx
, principals
, savedFrameObj
, &stackString
)) {
3955 args
.rval().setString(stackString
);
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
,
3975 MutableHandleValue vp
) const {
3976 if (!type
.isExposable()) {
3977 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
3978 JSMSG_WASM_BAD_VAL_TYPE
);
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
);
3992 // Avoid rooting hazard of `this` being live across `fromJSValue`
3994 uint8_t* dest
= typedMem() + offset
;
3996 if (!Val::fromJSValue(cx
, type
, value
, &val
)) {
3999 val
.get().writeToHeapLocation(dest
);
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
),
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_
,
4038 [[nodiscard
]] static bool IsWasmFunction(HandleValue v
) {
4039 if (!v
.isObject()) {
4042 if (!v
.toObject().is
<JSFunction
>()) {
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
));
4061 args
.rval().setObject(*typeObj
);
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
);
4090 ModuleEnvironment
moduleEnv(compileArgs
->features
);
4091 CompilerEnvironment
compilerEnv(CompileMode::Once
, Tier::Optimized
,
4092 DebugEnabled::False
);
4093 compilerEnv
.computeParameters();
4095 if (!moduleEnv
.init()) {
4099 FuncType funcType
= FuncType(std::move(params
), std::move(results
));
4100 if (!moduleEnv
.types
->addType(std::move(funcType
))) {
4104 // Add an (import (func ...))
4105 FuncDesc funcDesc
= FuncDesc(&(*moduleEnv
.types
)[0].funcType(), 0);
4106 if (!moduleEnv
.funcs
.append(funcDesc
)) {
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
)) {
4122 ModuleGenerator
mg(*compileArgs
, &moduleEnv
, &compilerEnv
, nullptr, nullptr,
4124 if (!mg
.init(nullptr)) {
4127 // We're not compiling any function definitions.
4128 if (!mg
.finishFuncDefs()) {
4131 SharedBytes shareableBytes
= js_new
<ShareableBytes
>();
4132 if (!shareableBytes
) {
4135 SharedModule module
= mg
.finishModule(*shareableBytes
);
4140 // Instantiate the module.
4141 Rooted
<ImportValues
> imports(cx
);
4142 if (!imports
.get().funcs
.append(func
)) {
4145 Rooted
<WasmInstanceObject
*> instance(cx
);
4146 if (!module
->instantiate(cx
, imports
.get(), nullptr, &instance
)) {
4147 MOZ_ASSERT(cx
->isThrowingOutOfMemory());
4151 // Get the exported function which wraps the JS function to return.
4152 RootedFunction
wasmFunc(cx
);
4153 if (!instance
->getExportedFunction(cx
, instance
, 0, &wasmFunc
)) {
4159 bool WasmFunctionConstruct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4160 CallArgs args
= CallArgsFromVp(argc
, vp
);
4162 if (!ThrowIfNotConstructing(cx
, args
, "WebAssembly.Function")) {
4166 if (!args
.requireAtLeast(cx
, "WebAssembly.Function", 2)) {
4170 if (!args
[0].isObject()) {
4171 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
4172 JSMSG_WASM_BAD_DESC_ARG
, "function");
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", ¶metersVal
)) {
4184 ValTypeVector params
;
4185 if (!ParseValTypes(cx
, parametersVal
, params
)) {
4189 RootedValue
resultsVal(cx
);
4190 if (!JS_GetProperty(cx
, typeObj
, "results", &resultsVal
)) {
4194 ValTypeVector results
;
4195 if (!ParseValTypes(cx
, resultsVal
, results
)) {
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
);
4206 RootedObject
func(cx
, &args
[1].toObject());
4209 cx
, GetWasmConstructorPrototype(cx
, args
, JSProto_WasmFunction
));
4211 ReportOutOfMemory(cx
);
4215 RootedFunction
wasmFunc(cx
, WasmFunctionCreate(cx
, func
, std::move(params
),
4216 std::move(results
), proto
));
4218 ReportOutOfMemory(cx
);
4221 args
.rval().setObject(*wasmFunc
);
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
)));
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
,
4248 WasmFunctionMethods
,
4251 ClassSpec::DontDefineConstructor
};
4253 const JSClass
js::WasmFunctionClass
= {
4254 "WebAssembly.Function", 0, JS_NULL_CLASS_OPS
, &WasmFunctionClassSpec
};
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
);
4267 static bool RejectWithPendingException(JSContext
* cx
,
4268 Handle
<PromiseObject
*> promise
) {
4269 if (!cx
->isExceptionPending()) {
4273 RootedValue
rejectionValue(cx
);
4274 if (!GetAndClearException(cx
, &rejectionValue
)) {
4278 return PromiseObject::reject(cx
, promise
, rejectionValue
);
4281 static bool Reject(JSContext
* cx
, const CompileArgs
& args
,
4282 Handle
<PromiseObject
*> promise
, const UniqueChars
& 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()) {
4292 JS_NewStringCopyUTF8N(cx
, JS::UTF8Chars(filename
, strlen(filename
)));
4294 fileName
= JS_GetEmptyString(cx
);
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
4305 UniqueChars
str(JS_smprintf("wasm validation error: %s", error
.get()));
4310 size_t len
= strlen(str
.get());
4311 RootedString
message(cx
, NewStringCopyN
<CanGC
>(cx
, str
.get(), len
));
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,
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_
;
4345 AsyncInstantiateTask(JSContext
* cx
, const Module
& module
, Ret ret
,
4346 Handle
<PromiseObject
*> promise
)
4347 : OffThreadPromiseTask(cx
, promise
),
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
,
4361 return RejectWithPendingException(cx
, promise
);
4364 RootedValue
resolutionValue(cx
);
4365 if (ret_
== Ret::Instance
) {
4366 resolutionValue
= ObjectValue(*instanceObj
);
4368 RootedObject
resultObj(cx
, JS_NewPlainObject(cx
));
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
));
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_
);
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
)) {
4412 if (!GetImports(cx
, module
, importObj
, &task
->imports())) {
4413 return RejectWithPendingException(cx
, promise
);
4416 task
.release()->dispatchResolveAndDestroy();
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
));
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
);
4437 struct CompileBufferTask
: PromiseHelperTask
{
4438 MutableBytes bytecode
;
4439 SharedCompileArgs compileArgs
;
4441 UniqueCharsVector warnings
;
4442 SharedModule module
;
4444 PersistentRootedObject importObj
;
4446 CompileBufferTask(JSContext
* cx
, Handle
<PromiseObject
*> promise
,
4447 HandleObject importObj
)
4448 : PromiseHelperTask(cx
, promise
),
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
);
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
)) {
4472 return Reject(cx
, *compileArgs
, promise
, error
);
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
)) {
4488 callArgs
.rval().setObject(*promise
);
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.");
4501 static bool GetBufferSource(JSContext
* cx
, CallArgs callArgs
, const char* name
,
4502 MutableBytes
* bytecode
) {
4503 if (!callArgs
.requireAtLeast(cx
, name
, 1)) {
4507 if (!callArgs
[0].isObject()) {
4508 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
4509 JSMSG_WASM_BAD_BUF_ARG
);
4513 return GetBufferSource(cx
, &callArgs
[0].toObject(), JSMSG_WASM_BAD_BUF_ARG
,
4517 static bool WebAssembly_compile(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4518 if (!EnsurePromiseSupport(cx
)) {
4522 Log(cx
, "async compile() started");
4524 Rooted
<PromiseObject
*> promise(cx
, PromiseObject::createSkippingExecutor(cx
));
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
);
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))) {
4551 if (!task
->init(cx
, options
, "WebAssembly.compile")) {
4555 if (!StartOffThreadPromiseHelperTask(cx
, std::move(task
))) {
4559 callArgs
.rval().setObject(*promise
);
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)) {
4571 if (!callArgs
[0].isObject()) {
4572 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
4573 JSMSG_WASM_BAD_BUF_MOD_ARG
);
4577 firstArg
.set(&callArgs
[0].toObject());
4579 if (!GetImportArg(cx
, callArgs
.get(1), importObj
)) {
4583 featureOptions
.set(callArgs
.get(2));
4587 static bool WebAssembly_instantiate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4588 if (!EnsurePromiseSupport(cx
)) {
4592 Log(cx
, "async instantiate() started");
4594 Rooted
<PromiseObject
*> promise(cx
, PromiseObject::createSkippingExecutor(cx
));
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
,
4606 return RejectWithPendingException(cx
, promise
, callArgs
);
4609 const Module
* module
;
4610 if (IsModuleObject(firstArg
, &module
)) {
4611 if (!AsyncInstantiate(cx
, *module
, importObj
, Ret::Instance
, promise
)) {
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
)) {
4627 auto task
= cx
->make_unique
<CompileBufferTask
>(cx
, promise
, importObj
);
4628 if (!task
|| !task
->init(cx
, options
, "WebAssembly.instantiate")) {
4632 if (!GetBufferSource(cx
, firstArg
, JSMSG_WASM_BAD_BUF_MOD_ARG
,
4634 return RejectWithPendingException(cx
, promise
, callArgs
);
4637 if (!StartOffThreadPromiseHelperTask(cx
, std::move(task
))) {
4642 callArgs
.rval().setObject(*promise
);
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
)) {
4654 FeatureOptions options
;
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
4661 if (!validated
&& !error
) {
4662 ReportOutOfMemory(cx
);
4667 MOZ_ASSERT(!validated
);
4668 Log(cx
, "validate() failed with: %s", error
.get());
4671 callArgs
.rval().setBoolean(validated
);
4675 static bool EnsureStreamSupport(JSContext
* cx
) {
4676 // This should match wasm::StreamingCompilationAvailable().
4678 if (!EnsurePromiseSupport(cx
)) {
4682 if (!CanUseExtraThreads()) {
4683 JS_ReportErrorASCII(
4684 cx
, "WebAssembly.compileStreaming not supported with --no-threads");
4688 if (!cx
->runtime()->consumeStreamCallback
) {
4689 JS_ReportErrorASCII(cx
,
4690 "WebAssembly streaming not supported in this runtime");
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
);
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_
;
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:
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
4733 uint8_t* codeBytesEnd_
;
4734 ExclusiveBytesPtr exclusiveCodeBytesEnd_
;
4736 // Immutable after Tail state:
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
{
4753 compileArgs_
->scriptedCaller
.filename
= DuplicateString(url
);
4754 compileArgs_
->scriptedCaller
.filenameIsURL
= true;
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();
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();
4806 bool consumeChunk(const uint8_t* begin
, size_t length
) override
{
4807 switch (streamState_
.lock().get()) {
4809 if (!envBytes_
.append(begin
, length
)) {
4810 return rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode
);
4813 if (!StartsCodeSection(envBytes_
.begin(), envBytes_
.end(),
4818 uint32_t extraBytes
= envBytes_
.length() - codeSection_
.start
;
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
;
4844 return consumeChunk(begin
+ length
- extraBytes
, extraBytes
);
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()) {
4865 streamState_
.lock().get() = Tail
;
4867 if (uint32_t extraBytes
= length
- copyLength
) {
4868 return consumeChunk(begin
+ copyLength
, extraBytes
);
4874 if (!tailBytes_
.append(begin
, length
)) {
4875 return rejectAndDestroyAfterHelperThreadStarted(StreamOOMCode
);
4881 MOZ_CRASH("consumeChunk() in Closed state");
4883 MOZ_CRASH("unreachable");
4886 void streamEnd(JS::OptimizedEncodingListener
* tier2Listener
) override
{
4887 switch (streamState_
.lock().get()) {
4889 SharedBytes bytecode
= js_new
<ShareableBytes
>(std::move(envBytes_
));
4891 rejectAndDestroyBeforeHelperThreadStarted(StreamOOMCode
);
4894 module_
= CompileBuffer(*compileArgs_
, *bytecode
, &compileError_
,
4895 &warnings_
, nullptr);
4896 setClosedAndDestroyBeforeHelperThreadStarted();
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();
4913 MOZ_CRASH("streamEnd() in Closed state");
4917 void streamError(size_t errorCode
) override
{
4918 MOZ_ASSERT(errorCode
!= StreamOOMCode
);
4919 switch (streamState_
.lock().get()) {
4921 rejectAndDestroyBeforeHelperThreadStarted(errorCode
);
4925 rejectAndDestroyAfterHelperThreadStarted(errorCode
);
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_
)) {
4965 MOZ_ASSERT(!streamFailed_
&& !streamError_
&& !compileError_
);
4967 return AsyncInstantiate(cx
, *module_
, importObj_
, Ret::Pair
, promise
);
4969 return ResolveCompile(cx
, *module_
, promise
);
4973 return RejectWithStreamErrorNumber(cx
, *streamError_
, promise
);
4976 return Reject(cx
, *compileArgs_
, promise
, compileError_
);
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
),
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
);
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);
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
));
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
5057 nullptr, // mayResolve
5058 ResolveResponseClosure::finalize
, // finalize
5060 nullptr, // construct
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()
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
,
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
)) {
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
,
5110 return RejectWithPendingException(cx
, promise
);
5113 (void)task
.release();
5115 callArgs
.rval().setUndefined();
5119 static bool ResolveResponse_OnRejected(JSContext
* cx
, unsigned argc
,
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))) {
5130 args
.rval().setUndefined();
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
)) {
5149 SharedCompileArgs compileArgs
= InitCompileArgs(cx
, options
, introducer
);
5154 RootedObject
closure(
5155 cx
, ResolveResponseClosure::create(cx
, *compileArgs
, resultPromise
,
5156 instantiate
, importObj
));
5161 RootedFunction
onResolved(
5162 cx
, NewNativeFunction(cx
, ResolveResponse_OnFulfilled
, 1, nullptr,
5163 gc::AllocKind::FUNCTION_EXTENDED
, GenericObject
));
5168 RootedFunction
onRejected(
5169 cx
, NewNativeFunction(cx
, ResolveResponse_OnRejected
, 1, nullptr,
5170 gc::AllocKind::FUNCTION_EXTENDED
, GenericObject
));
5175 onResolved
->setExtendedSlot(0, ObjectValue(*closure
));
5176 onRejected
->setExtendedSlot(0, ObjectValue(*closure
));
5178 RootedObject
resolve(cx
,
5179 PromiseObject::unforgeableResolve(cx
, responsePromise
));
5184 return JS::AddPromiseReactions(cx
, resolve
, onResolved
, onRejected
);
5187 static bool WebAssembly_compileStreaming(JSContext
* cx
, unsigned argc
,
5189 if (!EnsureStreamSupport(cx
)) {
5193 Log(cx
, "async compileStreaming() started");
5195 Rooted
<PromiseObject
*> resultPromise(
5196 cx
, PromiseObject::createSkippingExecutor(cx
));
5197 if (!resultPromise
) {
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
);
5220 static bool WebAssembly_instantiateStreaming(JSContext
* cx
, unsigned argc
,
5222 if (!EnsureStreamSupport(cx
)) {
5226 Log(cx
, "async instantiateStreaming() started");
5228 Rooted
<PromiseObject
*> resultPromise(
5229 cx
, PromiseObject::createSkippingExecutor(cx
));
5230 if (!resultPromise
) {
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
,
5248 return RejectWithPendingException(cx
, resultPromise
, callArgs
);
5250 Rooted
<Value
> responsePromise(cx
, ObjectValue(*firstArg
.get()));
5252 if (!ResolveResponse(cx
, responsePromise
, featureOptions
, resultPromise
, true,
5254 return RejectWithPendingException(cx
, resultPromise
, callArgs
);
5257 callArgs
.rval().setObject(*resultPromise
);
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
,
5269 ReportOutOfMemory(cx
);
5272 args
.rval().set(ObjectValue(*module
.get()));
5276 static const JSFunctionSpec WebAssembly_mozIntGemm_methods
[] = {
5277 JS_FN("mozIntGemm", WebAssembly_mozIntGemm
, 0, JSPROP_ENUMERATE
),
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,
5289 JS_FN("instantiateStreaming", WebAssembly_instantiateStreaming
, 1,
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_
,
5304 struct NameAndProtoKey
{
5305 const char* const name
;
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
);
5318 ctorValue
.setObject(*ctor
);
5320 JSAtom
* className
= Atomize(cx
, entry
.name
, strlen(entry
.name
));
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
},
5346 RootedValue
ctorValue(cx
);
5348 for (const auto& entry
: entries
) {
5349 if (!WebAssemblyDefineConstructor(cx
, wasm
, entry
, &ctorValue
, &id
)) {
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
)) {
5366 RootedObject
tagProto(
5367 cx
, GlobalObject::getOrCreatePrototype(cx
, JSProto_WasmTag
));
5369 ReportOutOfMemory(cx
);
5373 SharedTagType
wrappedJSValueTagType(sWrappedJSValueTagType
);
5374 WasmTagObject
* wrappedJSValueTagObject
=
5375 WasmTagObject::create(cx
, wrappedJSValueTagType
, tagProto
);
5376 if (!wrappedJSValueTagObject
) {
5380 wasm
->setWrappedJSValueTag(wrappedJSValueTagObject
);
5382 #ifdef ENABLE_WASM_MOZ_INTGEMM
5383 if (MozIntGemmAvailable(cx
) &&
5384 !JS_DefineFunctions(cx
, wasm
, WebAssembly_mozIntGemm_methods
)) {
5392 WasmNamespaceObject
* WasmNamespaceObject::getOrCreate(JSContext
* cx
) {
5394 GlobalObject::getOrCreateConstructor(cx
, JSProto_WebAssembly
);
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_
= {
5408 JSCLASS_HAS_CACHED_PROTO(JSProto_WebAssembly
) |
5409 JSCLASS_HAS_RESERVED_SLOTS(WasmNamespaceObject::RESERVED_SLOTS
),
5410 JS_NULL_CLASS_OPS
, &WebAssemblyClassSpec
};