// Slot that is used in WasmPromisingFunction const size_t WRAPPED_FN_SLOT = 0;
// Slots that are used in the WasmPromiseReaction const size_t CONT_SLOT = 0; const size_t REACTION_SLOT = 1; const size_t PROMISING_PROMISE_SLOT = 2;
// Suspending
// Builds a wasm module with following structure: // (module // (type $results (struct (field ..)*))) // (import "" "tag" (tag $on-suspend)) // (import "" "wrapped" (func $suspending.wrappedfn ..)) // (func $suspending.exported .. ) // (export "" (func $suspending.exported)) // ) // class SuspendingFunctionModuleFactory { public: // Type indices, relative to baseTypeIndex_. enum TypeIdx {
ResultsTypeIndex,
TagFuncTypeIndex,
Count,
};
enum TagIdx {
OnSuspendTagIndex,
};
enum FnIdx {
WrappedFnIndex,
ExportedFnIndex,
};
uint32_t baseTypeIndex_ = 0;
private: // Builds function that will be imported to wasm module: // (func $suspending.exported // (param ..)* (result ..)* // // (local $promise externref) // (local $results (ref $results)) // // ;; ensure that there is a WebAssembly.promising function on the stack // guard-suspending // // ;; call the wrapped fn // (local.get $param)* // call $suspending.wrappedfn // // ;; call Promise.resolve on the result of wrapped fn // call $builtin.promise-resolve // ;; save the promise for when we resume // local.tee $promise // suspend $on-suspend // // ;; get the results from the promise as a struct // local.get $promise // call $builtin.get-promise-results // ref.cast $results // local.set $results // // ;; unbox the struct and return the results // (struct.get $results i local.get $results)* // return // ) bool encodeExportedFunction(CodeMetadata& codeMeta, uint32_t paramsSize,
uint32_t resultSize, uint32_t paramsOffset,
RefType resultType, Bytes& bytecode) {
Encoder encoder(bytecode, *codeMeta.types);
// If the function we're wrapping is WebAssembly, treat it as if it was JS. // This is required by the specification so that type mismatches don't // surface as link errors. It also ensures that we don't need a suspend // barrier instruction when calling the wrapped function.
codeMeta->funcImportsAreJS = true;
// Copy all RecGroups from the foreign module so that ValTypes in params // and results that reference concrete type defs resolve correctly. if (!codeMeta->types->clone(*foreignTypes)) {
ReportOutOfMemory(cx); return nullptr;
}
baseTypeIndex_ = codeMeta->types->length();
// If we don't have room to add our types with the wrapped function's // module's types, then fail and treat this as an OOM. This is extremely // unlikely. if (codeMeta->types->length() > MaxTypes - TypeIdx::Count) {
ReportOutOfMemory(cx); return nullptr;
}
// Pull the params and results from the import's declared type. const FuncType& importFuncType =
codeMeta->types->type(funcTypeIndex).funcType();
ValTypeVector params, results; if (!params.append(importFuncType.args().begin(),
importFuncType.args().end()) ||
!results.append(importFuncType.results().begin(),
importFuncType.results().end())) {
ReportOutOfMemory(cx); return nullptr;
}
// Func 1: $suspending.exported (defined, exported) - params -> results // // Give the wrapper the import's actual declared type so that it has the // correct identity, subtyping, and final attributes.
MOZ_ASSERT(codeMeta->funcs.length() == ExportedFnIndex);
MOZ_ASSERT(funcTypeIndex < baseTypeIndex_);
MOZ_ASSERT((*codeMeta->types)[funcTypeIndex].isFuncType()); if (!moduleMeta->addDefinedFuncWithType(funcTypeIndex, /*declareForRef = */ true,
mozilla::Some(CacheableName()))) {
ReportOutOfMemory(cx); return nullptr;
}
if (!moduleMeta->prepareForCompile(compilerEnv.mode())) {
ReportOutOfMemory(cx); return nullptr;
}
// Instantiate the module.
Rooted<ImportValues> imports(cx);
// Add $suspending.wrappedfn to imports. if (!imports.get().funcs.append(func)) {
ReportOutOfMemory(cx); return nullptr;
}
// Add $on-suspend to imports.
Rooted<WasmNamespaceObject*> wasmNamespace(
cx, WasmNamespaceObject::getOrCreate(cx)); if (!wasmNamespace) {
ReportOutOfMemory(cx); return nullptr;
} if (!imports.get().tagObjs.append(wasmNamespace->jsPromiseTag())) {
ReportOutOfMemory(cx); return nullptr;
}
Rooted<WasmInstanceObject*> instance(cx); if (!module->instantiate(cx, imports.get(), nullptr, &instance)) { // Can also trap on invalid input function. return nullptr;
}
// Returns the $suspending.exported function.
RootedFunction wasmFunc(cx); if (!WasmInstanceObject::getExportedFunction(
cx, instance, SuspendingFunctionModuleFactory::ExportedFnIndex,
&wasmFunc)) { return nullptr;
} return wasmFunc;
}
// Promising
// Builds a wasm module with following structure: // (module // (type $params (struct (field ..)*)) // (type $results (struct (field ..)*)) // (type $cont (cont)) // (import "" "wrapped" (func $promising.wrappedfn ..)) // (import "" "tag" (tag $on-suspend)) // ;; globals for passing values from exported to trampoline. // ;; works around work-in-progress stack switching implementation. // (global $promisingPromise (mut externref)) // (global $params (mut (ref null $params))) // (func $promising.exported .. ) // (func $promising.trampoline ..) // (func $promising.reaction ..) // (export "" (func $promising.exported)) // ) // // The module provides logic for the Invoke Promising Import state transition // via $promising.exported and $promising.trampoline (see the SMDOC). // class PromisingFunctionModuleFactory {
uint32_t baseTypeIndex_ = 0;
public: // Type indices, relative to baseTypeIndex_. Types added by addDefinedFunc // are interleaved. enum TypeIdx {
ParamsTypeIndex = 0,
ResultsTypeIndex = 1, // Type 2: exported fn func type (added by addDefinedFunc for Exported)
TrampolineFuncTypeIndex = 3, // Type 4: trampoline fn func type (added by addDefinedFunc, same as 3)
ContTypeIndex = 5,
TagFuncTypeIndex = 6,
SuspendBlockTypeIndex = 7, // Type 8: reaction fn func type (added by addDefinedFunc for Reaction)
Count = 9,
};
ValTypeVector locals; if (!locals.emplaceBack(RefType::extern_())) { returnfalse;
} if (!EncodeLocalEntries(encoder, locals)) { returnfalse;
}
# ifdef DEBUG // Trap if the global promise is non-null. This would only happen if we // messed up our global state clearing or had unexpected re-entrance. if (!encoder.writeOp(Op::GlobalGet) ||
!encoder.writeVarU32(PromisingPromiseGlobalIndex)) { returnfalse;
} if (!encoder.writeOp(Op::RefIsNull) || !encoder.writeOp(Op::I32Eqz)) { returnfalse;
} if (!encoder.writeOp(Op::If) ||
!encoder.writeFixedU8((uint8_t)TypeCode::BlockVoid) ||
!encoder.writeOp(Op::Unreachable) || !encoder.writeOp(Op::End)) { returnfalse;
} # endif
// call $builtin.create-promise // local.set $promisingPromise if (!encoder.writeOp(MozOp::CallBuiltinModuleFunc) ||
!encoder.writeVarU32((uint32_t)BuiltinModuleFuncId::CreatePromise)) { returnfalse;
} if (!encoder.writeOp(Op::LocalSet) ||
!encoder.writeVarU32(promisingPromiseIndex)) { returnfalse;
}
// Copy all RecGroups from the wrapped function's module so that ValTypes // referencing concrete type defs are valid in this module's TypeContext. const SharedTypeContext& foreignTypes = fn->wasmInstance().codeMeta().types; if (!codeMeta->types->clone(*foreignTypes)) {
ReportOutOfMemory(cx); return nullptr;
}
baseTypeIndex_ = codeMeta->types->length();
// If we don't have room to add our types with the wrapped function's // module's types, then fail and treat this as an OOM. This is extremely // unlikely. if (codeMeta->types->length() > MaxTypes - TypeIdx::Count) {
ReportOutOfMemory(cx); return nullptr;
}
// Type baseTypeIndex_ + 0: $params struct
StructType boxedParamsStruct; if (!StructType::createImmutable(fnType.args(), &boxedParamsStruct)) {
ReportOutOfMemory(cx); return nullptr;
}
MOZ_ASSERT(codeMeta->types->length() == baseTypeIndex_ + ParamsTypeIndex); if (!codeMeta->types->addType(std::move(boxedParamsStruct))) {
ReportOutOfMemory(cx); return nullptr;
}
// Type baseTypeIndex_ + 1: $results struct
StructType boxedResultType; if (!StructType::createImmutable(fnType.results(), &boxedResultType)) {
ReportOutOfMemory(cx); return nullptr;
}
MOZ_ASSERT(codeMeta->types->length() == baseTypeIndex_ + ResultsTypeIndex); if (!codeMeta->types->addType(std::move(boxedResultType))) {
ReportOutOfMemory(cx); return nullptr;
}
// Func 0 (imported): $promising.wrappedfn // Use the wrapped function's actual type index from the cloned type context // so that there is no type mismatch during instantiation.
MOZ_ASSERT(funcTypeIndex < baseTypeIndex_);
MOZ_ASSERT((*codeMeta->types)[funcTypeIndex].isFuncType());
MOZ_ASSERT(codeMeta->funcs.length() == WrappedFnIndex); if (!moduleMeta->addDefinedFuncWithType(funcTypeIndex)) {
ReportOutOfMemory(cx); return nullptr;
}
// Type baseTypeIndex_ + 3: trampoline func type () -> () // This is the func type the cont type will reference.
MOZ_ASSERT(codeMeta->types->length() ==
baseTypeIndex_ + TrampolineFuncTypeIndex); if (!codeMeta->types->addType(FuncType(ValTypeVector(), ValTypeVector()))) {
ReportOutOfMemory(cx); return nullptr;
}
if (!mg.finishFuncDefs()) {
ReportOutOfMemory(cx); return nullptr;
}
SharedModule m = mg.finishModule(BytecodeBufferOrSource(), *moduleMeta, /*maybeCompleteTier2Listener=*/nullptr); if (!m) {
ReportOutOfMemory(cx); return nullptr;
} return m;
}
};
// Wraps `$promising.exported` function so that it's not a host function as // required by the spec. staticbool WasmPromisingFunction(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
Rooted<JSFunction*> callee(cx, &args.callee().as<JSFunction>());
RootedFunction fn(
cx,
&callee->getExtendedSlot(WRAPPED_FN_SLOT).toObject().as<JSFunction>());
if (Call(cx, UndefinedHandleValue, fn, args, args.rval())) { returntrue;
}
// The stack was unwound during exception. There should be no active // continuation.
MOZ_RELEASE_ASSERT(!cx->wasm().currentStack());
// Any errors from invoking the wasm function need to be converted to a // rejected promise.
JSObject* newPromise = NewPromiseObject(cx, nullptr); if (!newPromise) { returnfalse;
}
Rooted<PromiseObject*> promiseObject(cx, &newPromise->as<PromiseObject>());
args.rval().setObject(*promiseObject); return RejectPromiseWithPendingError(cx, promiseObject);
}
// Wrap $promising.exported function for exceptions/traps handling.
RootedFunction wasmFunc(cx); if (!WasmInstanceObject::getExportedFunction(
cx, instance, PromisingFunctionModuleFactory::ExportedFnIndex,
&wasmFunc)) { return nullptr;
}
if (Call(cx, UndefinedHandleValue, reactionFunc, argv, &rval)) { returntrue;
}
// The stack was unwound during exception.
MOZ_RELEASE_ASSERT(!cx->wasm().currentStack());
// Any errors from invoking the wasm function need to be converted to a // rejected promise. return RejectPromiseWithPendingError(cx, promisingPromiseObject);
}
// Creates a promise. // // Seen as $builtin.create-promise to wasm. void* CreatePromise(Instance* instance) {
MOZ_ASSERT(SASigCreatePromise.failureMode == FailureMode::FailOnNullPtr);
JSContext* cx = instance->cx();
JSObject* promise = NewPromiseObject(cx, nullptr); if (!promise) {
MOZ_ASSERT(cx->isExceptionPending()); return nullptr;
} return AnyRef::fromJSObject(*promise).forCompiledCode();
}
// Converts promise results into actual function result, or exception/trap // if rejected. // // Seen as $builtin.get-promise-results to wasm. void* GetPromiseResults(Instance* instance, void* promiseRef,
uint32_t typeIndex) {
MOZ_ASSERT(SASigGetPromiseResults.failureMode == FailureMode::FailOnNullPtr);
JSContext* cx = instance->cx();
if (fields.length() > 0) { // The struct object is constructed based on returns of exported function. // It is the only way we can get ValType for Val::fromJSValue call. const wasm::FuncType& sig = instance->codeMeta().getFuncType(
SuspendingFunctionModuleFactory::ExportedFnIndex);
if (fields.length() == 1) {
RootedVal val(cx);
MOZ_ASSERT(sig.result(0).storageType() == fields[0].type); if (!Val::fromJSValue(cx, sig.result(0), jsValue, &val)) { return nullptr;
}
results->storeVal(val, 0);
} else { // The multi-value result is wrapped into ArrayObject/Iterable.
Rooted<ArrayObject*> array(cx, IterableToArray(cx, jsValue)); if (!array) { return nullptr;
} if (fields.length() != array->length()) {
UniqueChars expected(JS_smprintf("%zu", fields.length()));
UniqueChars got(JS_smprintf("%u", array->length())); if (!expected || !got) {
ReportOutOfMemory(cx); return nullptr;
}
// Collects returned suspending promising, and registers callbacks to // react on it using WasmPromiseReaction. // // Seen as $builtin.add-promise-reactions to wasm.
int32_t AddPromiseReactions(Instance* instance, void* promiseRef, void* contRef, void* reactionRef, void* promisingPromiseRef) {
MOZ_ASSERT(SASigAddPromiseReactions.failureMode == FailureMode::FailOnNegI32);
JSContext* cx = instance->cx();
RootedObject promiseObject(
cx, &AnyRef::fromCompiledCode(promiseRef).toJSObject()); if (IsProxy(promiseObject) &&
JS_IsDeadWrapper(UncheckedUnwrap(promiseObject))) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT); return -1;
}
Rooted<ContObject*> contObject(
cx, &AnyRef::fromCompiledCode(contRef).toJSObject().as<ContObject>());
RootedFunction reactionFunc(
cx, &AnyRef::fromCompiledCode(reactionRef).toJSObject().as<JSFunction>());
Rooted<PromiseObject*> promisingPromise(
cx, &AnyRef::fromCompiledCode(promisingPromiseRef)
.toJSObject()
.as<PromiseObject>());
// Build a `then` function
RootedFunction then_(
cx, NewNativeFunction(cx, WasmPromiseReaction, 1, nullptr,
gc::AllocKind::FUNCTION_EXTENDED, GenericObject)); if (!then_) { return -1;
}
then_->initExtendedSlot(CONT_SLOT, ObjectValue(*contObject));
then_->initExtendedSlot(REACTION_SLOT, ObjectValue(*reactionFunc));
then_->initExtendedSlot(PROMISING_PROMISE_SLOT,
ObjectValue(*promisingPromise));
// Add the `then` function as a promise reaction if (!JS::AddPromiseReactions(cx, promiseObject, then_, then_)) {
MOZ_ASSERT(cx->isExceptionPending()); return -1;
} return0;
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.39Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-30)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.