[wasm] Implement new JS Promise Integration API
Implement the WebAssembly.Function-based API. With the old API, wrapping an import and export with JS Promise Integration looked like: WebAssembly.returnPromiseOnSuspend(<wasm_export>); WebAssembly.suspendOnReturnedPromise( new WebAssembly.Function(<sig>, <js_import>)); With the new API: new WebAssembly.Function(<sig>, <wasm_export>, {promising: 'first'}) new WebAssembly.Function(<sig>, <js_import>, {suspending: 'first'}) For details, see https://github.com/WebAssembly/js-promise-integration/pull/8/files R=ahaas@chromium.org Bug: v8:12191 Change-Id: Iaefaac5304a038fc39283db165b637af7e48b009 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3804669 Reviewed-by: Andreas Haas <ahaas@chromium.org> Commit-Queue: Thibaud Michaud <thibaudm@chromium.org> Cr-Commit-Position: refs/heads/main@{#82183}
This commit is contained in:
parent
f62fadc614
commit
29db563159
@ -7828,8 +7828,7 @@ WasmImportData ResolveWasmImportCall(
|
||||
if (WasmJSFunction::IsWasmJSFunction(*callable)) {
|
||||
auto js_function = Handle<WasmJSFunction>::cast(callable);
|
||||
suspend = js_function->GetSuspend();
|
||||
if ((suspend && !js_function->MatchesSignatureForSuspend(expected_sig)) ||
|
||||
(!suspend && !js_function->MatchesSignature(expected_sig))) {
|
||||
if (!js_function->MatchesSignature(expected_sig)) {
|
||||
return {WasmImportCallKind::kLinkError, callable, wasm::kNoSuspend};
|
||||
}
|
||||
// Resolve the short-cut to the underlying callable and continue.
|
||||
|
@ -2033,6 +2033,7 @@ void WasmInstanceObject::WasmInstanceObjectPrint(std::ostream& os) {
|
||||
void WasmFunctionData::WasmFunctionDataPrint(std::ostream& os) {
|
||||
os << "\n - internal: " << Brief(internal());
|
||||
os << "\n - wrapper_code: " << Brief(TorqueGeneratedClass::wrapper_code());
|
||||
os << "\n - js_promise_flags: " << js_promise_flags();
|
||||
}
|
||||
|
||||
void WasmExportedFunctionData::WasmExportedFunctionDataPrint(std::ostream& os) {
|
||||
@ -2042,7 +2043,6 @@ void WasmExportedFunctionData::WasmExportedFunctionDataPrint(std::ostream& os) {
|
||||
os << "\n - function_index: " << function_index();
|
||||
os << "\n - signature: " << Brief(signature());
|
||||
os << "\n - wrapper_budget: " << wrapper_budget();
|
||||
os << "\n - suspend: " << suspend();
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
|
@ -1671,7 +1671,8 @@ Handle<WasmInternalFunction> Factory::NewWasmInternalFunction(
|
||||
Handle<WasmJSFunctionData> Factory::NewWasmJSFunctionData(
|
||||
Address opt_call_target, Handle<JSReceiver> callable, int return_count,
|
||||
int parameter_count, Handle<PodArray<wasm::ValueType>> serialized_sig,
|
||||
Handle<CodeT> wrapper_code, Handle<Map> rtt, wasm::Suspend suspend) {
|
||||
Handle<CodeT> wrapper_code, Handle<Map> rtt, wasm::Suspend suspend,
|
||||
wasm::Promise promise) {
|
||||
Handle<WasmApiFunctionRef> ref =
|
||||
NewWasmApiFunctionRef(callable, suspend, Handle<WasmInstanceObject>());
|
||||
Handle<WasmInternalFunction> internal =
|
||||
@ -1686,7 +1687,8 @@ Handle<WasmJSFunctionData> Factory::NewWasmJSFunctionData(
|
||||
result.set_serialized_return_count(return_count);
|
||||
result.set_serialized_parameter_count(parameter_count);
|
||||
result.set_serialized_signature(*serialized_sig);
|
||||
result.set_suspend(suspend);
|
||||
result.set_js_promise_flags(WasmFunctionData::SuspendField::encode(suspend) |
|
||||
WasmFunctionData::PromiseField::encode(promise));
|
||||
return handle(result, isolate());
|
||||
}
|
||||
|
||||
@ -1705,7 +1707,7 @@ Handle<WasmExportedFunctionData> Factory::NewWasmExportedFunctionData(
|
||||
Handle<CodeT> export_wrapper, Handle<WasmInstanceObject> instance,
|
||||
Address call_target, Handle<Object> ref, int func_index,
|
||||
Address sig_address, int wrapper_budget, Handle<Map> rtt,
|
||||
wasm::Suspend suspend) {
|
||||
wasm::Promise promise) {
|
||||
Handle<Foreign> sig_foreign = NewForeign(sig_address);
|
||||
Handle<WasmInternalFunction> internal =
|
||||
NewWasmInternalFunction(call_target, Handle<HeapObject>::cast(ref), rtt);
|
||||
@ -1728,7 +1730,9 @@ Handle<WasmExportedFunctionData> Factory::NewWasmExportedFunctionData(
|
||||
*BUILTIN_CODE(isolate(), Illegal),
|
||||
V8_EXTERNAL_CODE_SPACE_BOOL ? UPDATE_WRITE_BARRIER : SKIP_WRITE_BARRIER);
|
||||
result.set_packed_args_size(0);
|
||||
result.set_suspend(suspend);
|
||||
result.set_js_promise_flags(
|
||||
WasmFunctionData::SuspendField::encode(wasm::kNoSuspend) |
|
||||
WasmFunctionData::PromiseField::encode(promise));
|
||||
return handle(result, isolate());
|
||||
}
|
||||
|
||||
@ -1749,6 +1753,9 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData(
|
||||
result.set_wrapper_code(*wrapper_code);
|
||||
result.set_embedder_data(*embedder_data);
|
||||
result.set_serialized_signature(*serialized_sig);
|
||||
result.set_js_promise_flags(
|
||||
WasmFunctionData::SuspendField::encode(wasm::kNoSuspend) |
|
||||
WasmFunctionData::PromiseField::encode(wasm::kNoPromise));
|
||||
return handle(result, isolate());
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,7 @@ struct WasmElemSegment;
|
||||
class WasmValue;
|
||||
enum class OnResume : int;
|
||||
enum Suspend : uint8_t;
|
||||
enum Promise : uint8_t;
|
||||
} // namespace wasm
|
||||
#endif
|
||||
|
||||
@ -643,7 +644,7 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
|
||||
Handle<CodeT> export_wrapper, Handle<WasmInstanceObject> instance,
|
||||
Address call_target, Handle<Object> ref, int func_index,
|
||||
Address sig_address, int wrapper_budget, Handle<Map> rtt,
|
||||
wasm::Suspend suspend);
|
||||
wasm::Promise promise);
|
||||
Handle<WasmApiFunctionRef> NewWasmApiFunctionRef(
|
||||
Handle<JSReceiver> callable, wasm::Suspend suspend,
|
||||
Handle<WasmInstanceObject> instance);
|
||||
@ -652,7 +653,8 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
|
||||
Handle<WasmJSFunctionData> NewWasmJSFunctionData(
|
||||
Address opt_call_target, Handle<JSReceiver> callable, int return_count,
|
||||
int parameter_count, Handle<PodArray<wasm::ValueType>> serialized_sig,
|
||||
Handle<CodeT> wrapper_code, Handle<Map> rtt, wasm::Suspend suspend);
|
||||
Handle<CodeT> wrapper_code, Handle<Map> rtt, wasm::Suspend suspend,
|
||||
wasm::Promise promise);
|
||||
Handle<WasmResumeData> NewWasmResumeData(
|
||||
Handle<WasmSuspenderObject> suspender, wasm::OnResume on_resume);
|
||||
Handle<WasmStruct> NewWasmStruct(const wasm::StructType* type,
|
||||
|
@ -743,8 +743,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
|
||||
// function. Use CWasmEntry instead.
|
||||
start_function_ = WasmExportedFunction::New(
|
||||
isolate_, instance, start_index,
|
||||
static_cast<int>(function.sig->parameter_count()), wrapper_code,
|
||||
kNoSuspend);
|
||||
static_cast<int>(function.sig->parameter_count()), wrapper_code);
|
||||
|
||||
if (function.imported) {
|
||||
ImportedFunctionEntry entry(instance, module_->start_function_index);
|
||||
|
@ -244,16 +244,6 @@ i::wasm::ModuleWireBytes GetFirstArgumentAsBytes(
|
||||
return i::wasm::ModuleWireBytes(start, start + length);
|
||||
}
|
||||
|
||||
i::MaybeHandle<i::JSFunction> GetFirstArgumentAsJSFunction(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args, ErrorThrower* thrower) {
|
||||
i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
|
||||
if (!arg0->IsJSFunction()) {
|
||||
thrower->TypeError("Argument 0 must be a function");
|
||||
return {};
|
||||
}
|
||||
return i::Handle<i::JSFunction>::cast(arg0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
i::MaybeHandle<i::JSReceiver> ImportsAsMaybeReceiver(Local<Value> ffi) {
|
||||
if (ffi->IsUndefined()) return {};
|
||||
@ -1889,6 +1879,76 @@ void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Utils::ToLocal(i::Handle<i::Object>::cast(runtime_exception)));
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool HasJSPromiseIntegrationFlag(Isolate* isolate, Local<Object> usage_obj,
|
||||
ErrorThrower* thrower, const char* flag_name) {
|
||||
Local<Context> context = isolate->GetCurrentContext();
|
||||
Local<String> flag_str = v8_str(isolate, flag_name);
|
||||
Local<String> first_str = v8_str(isolate, "first");
|
||||
Local<String> last_str = v8_str(isolate, "last");
|
||||
Local<String> none_str = v8_str(isolate, "none");
|
||||
v8::MaybeLocal<v8::Value> maybe_flag = usage_obj->Get(context, flag_str);
|
||||
v8::Local<Value> flag_value;
|
||||
v8::Local<String> flag_value_str;
|
||||
if (maybe_flag.ToLocal(&flag_value) && !flag_value->IsUndefined() &&
|
||||
flag_value->ToString(context).ToLocal(&flag_value_str)) {
|
||||
if (!flag_value_str->StringEquals(first_str) &&
|
||||
!flag_value_str->StringEquals(last_str) &&
|
||||
!flag_value_str->StringEquals(none_str)) {
|
||||
thrower->TypeError(
|
||||
"JS Promise Integration: Expected suspender "
|
||||
"position to be \"first\", \"last\" or \"none\"");
|
||||
return false;
|
||||
} else if (flag_value_str->StringEquals(last_str)) {
|
||||
// TODO(thibaudm): Support the "last" position.
|
||||
UNIMPLEMENTED();
|
||||
} else if (flag_value_str->StringEquals(first_str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Given {inner_sig}: [ti*] -> [to*]
|
||||
// {outer_sig} must be: [externref ti*] -> [to*]
|
||||
bool IsSuspendingSignature(const i::wasm::FunctionSig* inner_sig,
|
||||
const i::wasm::FunctionSig* outer_sig) {
|
||||
if (inner_sig->parameter_count() + 1 != outer_sig->parameter_count()) {
|
||||
return false;
|
||||
}
|
||||
if (inner_sig->return_count() != outer_sig->return_count()) {
|
||||
return false;
|
||||
}
|
||||
if (outer_sig->GetParam(0) != i::wasm::kWasmExternRef) return false;
|
||||
for (size_t i = 1; i < outer_sig->parameter_count(); ++i) {
|
||||
if (outer_sig->GetParam(i) != inner_sig->GetParam(i - 1)) return false;
|
||||
}
|
||||
for (size_t i = 0; i < outer_sig->return_count(); ++i) {
|
||||
if (outer_sig->GetReturn(i) != inner_sig->GetReturn(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Given {inner_sig}: externref ti* -> to
|
||||
// {outer_sig} must be: ti* -> externref
|
||||
bool IsPromisingSignature(const i::wasm::FunctionSig* inner_sig,
|
||||
const i::wasm::FunctionSig* outer_sig) {
|
||||
if (inner_sig->parameter_count() != outer_sig->parameter_count() + 1) {
|
||||
return false;
|
||||
}
|
||||
if (inner_sig->return_count() != 1 || outer_sig->return_count() != 1) {
|
||||
return false;
|
||||
}
|
||||
if (inner_sig->GetParam(0) != i::wasm::kWasmExternRef) return false;
|
||||
for (size_t i = 0; i < outer_sig->parameter_count(); ++i) {
|
||||
if (outer_sig->GetParam(i) != inner_sig->GetParam(i + 1)) return false;
|
||||
}
|
||||
if (outer_sig->GetReturn(0) != i::wasm::kWasmExternRef) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// WebAssembly.Function
|
||||
void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
@ -1983,7 +2043,35 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
|
||||
i::Handle<i::JSReceiver> callable =
|
||||
Utils::OpenHandle(*args[1].As<Function>());
|
||||
if (i::WasmExportedFunction::IsWasmExportedFunction(*callable)) {
|
||||
|
||||
i::wasm::Suspend suspend = i::wasm::kNoSuspend;
|
||||
i::wasm::Promise promise = i::wasm::kNoPromise;
|
||||
if (i::FLAG_experimental_wasm_stack_switching) {
|
||||
// Optional third argument for JS Promise Integration.
|
||||
if (!args[2]->IsNullOrUndefined() && !args[2]->IsObject()) {
|
||||
thrower.TypeError(
|
||||
"Expected argument 3 to be an object with a "
|
||||
"'suspending' or 'promising' property");
|
||||
return;
|
||||
}
|
||||
if (args[2]->IsObject()) {
|
||||
Local<Object> usage_obj = Local<Object>::Cast(args[2]);
|
||||
if (HasJSPromiseIntegrationFlag(isolate, usage_obj, &thrower,
|
||||
"suspending")) {
|
||||
suspend = i::wasm::kSuspend;
|
||||
}
|
||||
if (HasJSPromiseIntegrationFlag(isolate, usage_obj, &thrower,
|
||||
"promising")) {
|
||||
promise = i::wasm::kPromise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_wasm_exported_function =
|
||||
i::WasmExportedFunction::IsWasmExportedFunction(*callable);
|
||||
bool is_wasm_js_function = i::WasmJSFunction::IsWasmJSFunction(*callable);
|
||||
|
||||
if (is_wasm_exported_function && !suspend && !promise) {
|
||||
if (*i::Handle<i::WasmExportedFunction>::cast(callable)->sig() == *sig) {
|
||||
args.GetReturnValue().Set(Utils::ToLocal(callable));
|
||||
return;
|
||||
@ -1995,7 +2083,7 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (i::WasmJSFunction::IsWasmJSFunction(*callable)) {
|
||||
if (is_wasm_js_function && !suspend && !promise) {
|
||||
if (i::Handle<i::WasmJSFunction>::cast(callable)->MatchesSignature(sig)) {
|
||||
args.GetReturnValue().Set(Utils::ToLocal(callable));
|
||||
return;
|
||||
@ -2007,8 +2095,49 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_wasm_exported_function && suspend) {
|
||||
// TODO(thibaudm): Support wasm-to-wasm calls with suspending behavior, and
|
||||
// also with combined promising+suspending behavior.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
if (is_wasm_exported_function && promise) {
|
||||
auto wasm_exported_function = i::WasmExportedFunction::cast(*callable);
|
||||
i::SharedFunctionInfo sfi = wasm_exported_function.shared();
|
||||
i::WasmExportedFunctionData data = sfi.wasm_exported_function_data();
|
||||
if (!IsPromisingSignature(data.sig(), sig)) {
|
||||
thrower.TypeError("Incompatible signature for promising function");
|
||||
return;
|
||||
}
|
||||
i::Handle<i::WasmInstanceObject> instance(
|
||||
i::WasmInstanceObject::cast(data.internal().ref()), i_isolate);
|
||||
int func_index = data.function_index();
|
||||
i::Handle<i::CodeT> wrapper =
|
||||
BUILTIN_CODE(i_isolate, WasmReturnPromiseOnSuspend);
|
||||
i::Handle<i::JSFunction> result = i::WasmExportedFunction::New(
|
||||
i_isolate, instance, func_index,
|
||||
static_cast<int>(data.sig()->parameter_count()), wrapper);
|
||||
args.GetReturnValue().Set(Utils::ToLocal(result));
|
||||
return;
|
||||
}
|
||||
if (is_wasm_js_function && promise) {
|
||||
// TODO(thibaudm): This case has no practical use. The generated suspender
|
||||
// would be unusable since the stack would always contain at least one JS
|
||||
// frame. But for now the spec would require us to add specific JS-to-JS and
|
||||
// wasm-to-JS wrappers to support this case. Leave this unimplemented for
|
||||
// now.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
if (is_wasm_js_function && suspend) {
|
||||
auto wasm_js_function = i::WasmJSFunction::cast(*callable);
|
||||
const i::wasm::FunctionSig* inner_sig =
|
||||
wasm_js_function.GetSignature(&zone);
|
||||
if (!IsSuspendingSignature(inner_sig, sig)) {
|
||||
thrower.TypeError("Incompatible signature for suspending function");
|
||||
return;
|
||||
}
|
||||
}
|
||||
i::Handle<i::JSFunction> result =
|
||||
i::WasmJSFunction::New(i_isolate, sig, callable, i::wasm::kNoSuspend);
|
||||
i::WasmJSFunction::New(i_isolate, sig, callable, suspend);
|
||||
args.GetReturnValue().Set(Utils::ToLocal(result));
|
||||
}
|
||||
|
||||
@ -2029,8 +2158,8 @@ void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
i::Handle<i::WasmExportedFunctionData> data =
|
||||
handle(sfi->wasm_exported_function_data(), i_isolate);
|
||||
sig = wasm_exported_function->sig();
|
||||
if (data->suspend()) {
|
||||
// If this export is suspendable, the first parameter of the original
|
||||
if (i::WasmFunctionData::PromiseField::decode(data->js_promise_flags())) {
|
||||
// If this export is "promising", the first parameter of the original
|
||||
// function is an externref (suspender) which does not appear in the
|
||||
// wrapper function's signature. The wrapper function also returns a
|
||||
// promise as an externref instead of the original return type.
|
||||
@ -2046,22 +2175,6 @@ void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
}
|
||||
} else if (i::WasmJSFunction::IsWasmJSFunction(*arg0)) {
|
||||
sig = i::Handle<i::WasmJSFunction>::cast(arg0)->GetSignature(&zone);
|
||||
auto wasm_js_function = i::Handle<i::WasmJSFunction>::cast(arg0);
|
||||
if (wasm_js_function->shared().wasm_js_function_data().suspend()) {
|
||||
// If this function is the result of calling
|
||||
// WebAssembly.suspendOnReturnedPromise(), it takes an extra suspender
|
||||
// parameter which will be consumed by the wasm-to-JS wrapper.
|
||||
size_t param_count = sig->parameter_count();
|
||||
i::wasm::FunctionSig::Builder builder(&zone, 1, param_count + 1);
|
||||
builder.AddParam(internal::wasm::kWasmExternRef);
|
||||
for (size_t i = 0; i < param_count; ++i) {
|
||||
builder.AddParam(sig->GetParam(i));
|
||||
}
|
||||
DCHECK_EQ(1, sig->return_count());
|
||||
DCHECK_EQ(i::wasm::kWasmExternRef, sig->GetReturn(0));
|
||||
builder.AddReturn(i::wasm::kWasmExternRef);
|
||||
sig = builder.Build();
|
||||
}
|
||||
} else {
|
||||
thrower.TypeError("Argument 0 must be a WebAssembly.Function");
|
||||
return;
|
||||
@ -2744,88 +2857,6 @@ void WebAssemblyGlobalType(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
args.GetReturnValue().Set(Utils::ToLocal(type));
|
||||
}
|
||||
|
||||
// WebAssembly.returnPromiseOnSuspend(WebAssembly.Function) ->
|
||||
// WebAssembly.Function
|
||||
void WebAssemblyReturnPromiseOnSuspend(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
HandleScope scope(isolate);
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
ScheduledErrorThrower thrower(i_isolate,
|
||||
"WebAssembly.returnPromiseOnSuspend()");
|
||||
if (args.Length() == 0) {
|
||||
thrower.TypeError("Argument 0 is required");
|
||||
return;
|
||||
}
|
||||
auto maybe_function = GetFirstArgumentAsJSFunction(args, &thrower);
|
||||
if (thrower.error()) return;
|
||||
i::Handle<i::JSFunction> function = maybe_function.ToHandleChecked();
|
||||
i::SharedFunctionInfo sfi = function->shared();
|
||||
if (!sfi.HasWasmExportedFunctionData()) {
|
||||
thrower.TypeError("Argument 0 must be a wasm function");
|
||||
}
|
||||
i::WasmExportedFunctionData data = sfi.wasm_exported_function_data();
|
||||
if (data.sig()->return_count() != 1) {
|
||||
thrower.TypeError(
|
||||
"Expected a WebAssembly.Function with exactly one return type");
|
||||
}
|
||||
if (data.sig()->parameter_count() == 0 ||
|
||||
data.sig()->GetParam(0) != internal::wasm::kWasmExternRef) {
|
||||
thrower.TypeError("Expected at least one parameter of type %s",
|
||||
i::wasm::kWasmExternRef.name().c_str());
|
||||
}
|
||||
if (thrower.error()) return;
|
||||
int index = data.function_index();
|
||||
i::Handle<i::WasmInstanceObject> instance(
|
||||
i::WasmInstanceObject::cast(data.internal().ref()), i_isolate);
|
||||
i::Handle<i::CodeT> wrapper =
|
||||
BUILTIN_CODE(i_isolate, WasmReturnPromiseOnSuspend);
|
||||
// Upcast to JSFunction to re-use the existing ToLocal helper below.
|
||||
i::Handle<i::JSFunction> result =
|
||||
i::Handle<i::WasmExternalFunction>::cast(i::WasmExportedFunction::New(
|
||||
i_isolate, instance, index,
|
||||
static_cast<int>(data.sig()->parameter_count()), wrapper,
|
||||
internal::wasm::kSuspend));
|
||||
args.GetReturnValue().Set(Utils::ToLocal(result));
|
||||
}
|
||||
|
||||
// WebAssembly.suspendOnReturnedPromise(Function) -> Function
|
||||
void WebAssemblySuspendOnReturnedPromise(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
HandleScope scope(isolate);
|
||||
ScheduledErrorThrower thrower(i_isolate,
|
||||
"WebAssembly.suspendOnReturnedPromise()");
|
||||
if (!args[0]->IsObject()) {
|
||||
thrower.TypeError("Argument 0 must be a WebAssembly.Function");
|
||||
return;
|
||||
}
|
||||
i::Zone zone(i_isolate->allocator(), ZONE_NAME);
|
||||
const i::wasm::FunctionSig* sig;
|
||||
i::Handle<i::Object> arg0 = Utils::OpenHandle(*args[0]);
|
||||
|
||||
if (i::WasmExportedFunction::IsWasmExportedFunction(*arg0)) {
|
||||
// TODO(thibaudm): Suspend on wrapped wasm-to-wasm calls too.
|
||||
UNIMPLEMENTED();
|
||||
} else if (!i::WasmJSFunction::IsWasmJSFunction(*arg0)) {
|
||||
thrower.TypeError("Argument 0 must be a WebAssembly.Function");
|
||||
return;
|
||||
}
|
||||
sig = i::Handle<i::WasmJSFunction>::cast(arg0)->GetSignature(&zone);
|
||||
if (sig->return_count() != 1 ||
|
||||
sig->GetReturn(0) != i::wasm::kWasmExternRef) {
|
||||
thrower.TypeError("Expected a WebAssembly.Function with return type %s",
|
||||
i::wasm::kWasmExternRef.name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto callable = handle(
|
||||
i::Handle<i::WasmJSFunction>::cast(arg0)->GetCallable(), i_isolate);
|
||||
i::Handle<i::JSFunction> result =
|
||||
i::WasmJSFunction::New(i_isolate, sig, callable, i::wasm::kSuspend);
|
||||
args.GetReturnValue().Set(Utils::ToLocal(result));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// TODO(titzer): we use the API to create the function template because the
|
||||
@ -3123,10 +3154,6 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) {
|
||||
SetupConstructor(isolate, suspender_constructor,
|
||||
i::WASM_SUSPENDER_OBJECT_TYPE,
|
||||
WasmSuspenderObject::kHeaderSize, "WebAssembly.Suspender");
|
||||
InstallFunc(isolate, webassembly, "returnPromiseOnSuspend",
|
||||
WebAssemblyReturnPromiseOnSuspend, 1);
|
||||
InstallFunc(isolate, webassembly, "suspendOnReturnedPromise",
|
||||
WebAssemblySuspendOnReturnedPromise, 1);
|
||||
}
|
||||
|
||||
// Setup Function
|
||||
|
@ -1431,8 +1431,7 @@ WasmInstanceObject::GetOrCreateWasmInternalFunction(
|
||||
}
|
||||
auto external = Handle<WasmExternalFunction>::cast(WasmExportedFunction::New(
|
||||
isolate, instance, function_index,
|
||||
static_cast<int>(function.sig->parameter_count()), wrapper,
|
||||
wasm::kNoSuspend));
|
||||
static_cast<int>(function.sig->parameter_count()), wrapper));
|
||||
result =
|
||||
WasmInternalFunction::FromExternal(external, isolate).ToHandleChecked();
|
||||
|
||||
@ -1972,7 +1971,7 @@ int WasmExportedFunction::function_index() {
|
||||
|
||||
Handle<WasmExportedFunction> WasmExportedFunction::New(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index,
|
||||
int arity, Handle<CodeT> export_wrapper, wasm::Suspend suspend) {
|
||||
int arity, Handle<CodeT> export_wrapper) {
|
||||
DCHECK(
|
||||
CodeKind::JS_TO_WASM_FUNCTION == export_wrapper->kind() ||
|
||||
(export_wrapper->is_builtin() &&
|
||||
@ -1998,11 +1997,15 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
|
||||
} else {
|
||||
rtt = factory->wasm_internal_function_map();
|
||||
}
|
||||
wasm::Promise promise =
|
||||
export_wrapper->builtin_id() == Builtin::kWasmReturnPromiseOnSuspend
|
||||
? wasm::kPromise
|
||||
: wasm::kNoPromise;
|
||||
Handle<WasmExportedFunctionData> function_data =
|
||||
factory->NewWasmExportedFunctionData(
|
||||
export_wrapper, instance, call_target, ref, func_index,
|
||||
reinterpret_cast<Address>(sig), wasm::kGenericWrapperBudget, rtt,
|
||||
suspend);
|
||||
promise);
|
||||
|
||||
MaybeHandle<String> maybe_name;
|
||||
bool is_asm_js_module = instance->module_object().is_asm_js();
|
||||
@ -2130,7 +2133,7 @@ Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate,
|
||||
Handle<Map> rtt = factory->wasm_internal_function_map();
|
||||
Handle<WasmJSFunctionData> function_data = factory->NewWasmJSFunctionData(
|
||||
call_target, callable, return_count, parameter_count, serialized_sig,
|
||||
wrapper_code, rtt, suspend);
|
||||
wrapper_code, rtt, suspend, wasm::kNoPromise);
|
||||
|
||||
if (wasm::WasmFeatures::FromIsolate(isolate).has_typed_funcref()) {
|
||||
using CK = compiler::WasmImportCallKind;
|
||||
@ -2197,28 +2200,6 @@ const wasm::FunctionSig* WasmJSFunction::GetSignature(Zone* zone) {
|
||||
return zone->New<wasm::FunctionSig>(return_count, parameter_count, types);
|
||||
}
|
||||
|
||||
bool WasmJSFunction::MatchesSignatureForSuspend(const wasm::FunctionSig* sig) {
|
||||
DCHECK_LE(sig->all().size(), kMaxInt);
|
||||
int sig_size = static_cast<int>(sig->all().size());
|
||||
int parameter_count = static_cast<int>(sig->parameter_count());
|
||||
DisallowHeapAllocation no_alloc;
|
||||
WasmJSFunctionData function_data = shared().wasm_js_function_data();
|
||||
// The suspender parameter is not forwarded to the JS function so the
|
||||
// parameter count should differ by one.
|
||||
if (parameter_count != function_data.serialized_parameter_count() + 1) {
|
||||
return false;
|
||||
}
|
||||
if (sig_size == 0) return true; // Prevent undefined behavior.
|
||||
// This function is only called for functions wrapped by
|
||||
// WebAssembly.suspendOnReturnedPromise, so the return type has to be
|
||||
// externref.
|
||||
CHECK_EQ(function_data.serialized_return_count(), 1);
|
||||
CHECK_EQ(function_data.serialized_signature().get(0), wasm::kWasmExternRef);
|
||||
const wasm::ValueType* expected = sig->parameters().begin() + 1;
|
||||
return function_data.serialized_signature().matches(1, expected,
|
||||
parameter_count - 1);
|
||||
}
|
||||
|
||||
// TODO(9495): Update this if function type variance is introduced.
|
||||
bool WasmJSFunction::MatchesSignature(const wasm::FunctionSig* sig) {
|
||||
DCHECK_LE(sig->all().size(), kMaxInt);
|
||||
|
@ -2,6 +2,7 @@
|
||||
// this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/base/bit-field.h"
|
||||
#if !V8_ENABLE_WEBASSEMBLY
|
||||
#error This header should only be included if WebAssembly is enabled.
|
||||
#endif // !V8_ENABLE_WEBASSEMBLY
|
||||
@ -79,6 +80,7 @@ namespace wasm {
|
||||
// https://github.com/llvm/llvm-project/issues/56560. See also
|
||||
// crbug.com/1344641.
|
||||
enum Suspend : uint8_t { kSuspend = 1, kNoSuspend = 0 };
|
||||
enum Promise : uint8_t { kPromise = 1, kNoPromise = 0 };
|
||||
enum class OnResume : int { kContinue, kThrow };
|
||||
} // namespace wasm
|
||||
|
||||
@ -610,7 +612,7 @@ class WasmExportedFunction : public JSFunction {
|
||||
|
||||
V8_EXPORT_PRIVATE static Handle<WasmExportedFunction> New(
|
||||
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index,
|
||||
int arity, Handle<CodeT> export_wrapper, wasm::Suspend suspend);
|
||||
int arity, Handle<CodeT> export_wrapper);
|
||||
|
||||
Address GetWasmCallTarget();
|
||||
|
||||
@ -644,8 +646,6 @@ class WasmJSFunction : public JSFunction {
|
||||
// that lifetime of the signature is hence directly coupled to the zone.
|
||||
const wasm::FunctionSig* GetSignature(Zone* zone);
|
||||
bool MatchesSignature(const wasm::FunctionSig* sig);
|
||||
// Special typing rule for imports wrapped by a Suspender.
|
||||
bool MatchesSignatureForSuspend(const wasm::FunctionSig* sig);
|
||||
|
||||
DECL_CAST(WasmJSFunction)
|
||||
OBJECT_CONSTRUCTORS(WasmJSFunction, JSFunction);
|
||||
@ -715,6 +715,9 @@ class WasmFunctionData
|
||||
|
||||
using BodyDescriptor = FlexibleBodyDescriptor<kStartOfStrongFieldsOffset>;
|
||||
|
||||
using SuspendField = base::BitField<wasm::Suspend, 0, 1>;
|
||||
using PromiseField = base::BitField<wasm::Promise, 1, 1>;
|
||||
|
||||
TQ_OBJECT_CONSTRUCTORS(WasmFunctionData)
|
||||
};
|
||||
|
||||
|
@ -55,6 +55,8 @@ extern class WasmFunctionData extends HeapObject {
|
||||
// Used for calling this function from JavaScript.
|
||||
@if(V8_EXTERNAL_CODE_SPACE) wrapper_code: CodeDataContainer;
|
||||
@ifnot(V8_EXTERNAL_CODE_SPACE) wrapper_code: Code;
|
||||
// Encode the {promising} and {suspending} flags in a single smi.
|
||||
js_promise_flags: Smi;
|
||||
}
|
||||
|
||||
extern class WasmExportedFunctionData extends WasmFunctionData {
|
||||
@ -70,18 +72,12 @@ extern class WasmExportedFunctionData extends WasmFunctionData {
|
||||
@if(V8_EXTERNAL_CODE_SPACE) c_wrapper_code: CodeDataContainer;
|
||||
@ifnot(V8_EXTERNAL_CODE_SPACE) c_wrapper_code: Code;
|
||||
packed_args_size: Smi;
|
||||
// Functions returned by suspender.returnPromiseOnSuspend() have this field
|
||||
// set to the host suspender object.
|
||||
suspend: Smi; // Boolean.
|
||||
}
|
||||
|
||||
extern class WasmJSFunctionData extends WasmFunctionData {
|
||||
serialized_return_count: Smi;
|
||||
serialized_parameter_count: Smi;
|
||||
serialized_signature: PodArrayOfWasmValueType;
|
||||
// Whether this function is the result of wrapping another function with
|
||||
// WebAssembly.suspendOnReturnedPromise.
|
||||
suspend: Smi;
|
||||
}
|
||||
|
||||
extern class WasmCapiFunctionData extends WasmFunctionData {
|
||||
|
@ -12,6 +12,20 @@
|
||||
|
||||
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
function ToPromising(wasm_export) {
|
||||
let sig = WebAssembly.Function.type(wasm_export);
|
||||
assertTrue(sig.parameters.length > 0);
|
||||
assertEquals('externref', sig.parameters[0]);
|
||||
assertEquals(1, sig.results.length);
|
||||
let wrapper_sig = {
|
||||
parameters: sig.parameters.slice(1),
|
||||
results: ['externref']
|
||||
};
|
||||
return new WebAssembly.Function(
|
||||
wrapper_sig, wasm_export, {promising: 'first'});
|
||||
|
||||
}
|
||||
|
||||
(function testGenericWrapper0Param() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
@ -30,7 +44,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main());
|
||||
assertEquals(20, x);
|
||||
})();
|
||||
@ -46,7 +60,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
.exportFunc();
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertTraps(kTrapUnreachable, main);
|
||||
})();
|
||||
|
||||
@ -62,7 +76,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
.exportFunc();
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertTraps(kTrapUnreachable, main);
|
||||
})();
|
||||
|
||||
@ -88,7 +102,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(5));
|
||||
assertEquals(17, x);
|
||||
})();
|
||||
@ -116,7 +130,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
|
||||
let y = { valueOf: () => { print("Hello!"); gc(); return 24; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(y));
|
||||
assertEquals(36, x);
|
||||
})();
|
||||
@ -149,7 +163,7 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
let param2 = { valueOf: () => { gc(); return 6; } };
|
||||
let param3 = { valueOf: () => { gc(); return 3; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(9, param2, param3, 0));
|
||||
assertEquals(60, x);
|
||||
// Now we test if the evaluation order of the parameters is correct.
|
||||
@ -203,7 +217,7 @@ let kSig_r_riiiiiiii = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI32,
|
||||
let param6 = { valueOf: () => { gc(); return 10; } };
|
||||
let param8 = { valueOf: () => { gc(); return 12; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(param1, 6, 7, param4, 9, param6, 11, param8));
|
||||
assertEquals(360, x);
|
||||
})();
|
||||
@ -236,7 +250,7 @@ let kSig_r_riiiiiiii = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI32,
|
||||
|
||||
let param2 = { valueOf: () => { gc(); return 3; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(5, param2));
|
||||
assertEquals(20, x);
|
||||
})();
|
||||
@ -270,7 +284,7 @@ let kSig_r_riiiiiiii = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI32,
|
||||
let param2 = { valueOf: () => { gc(); return 3; } };
|
||||
let param3 = { valueOf: () => { gc(); return 6; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(5, param2, param3, 7, 200, 300, 400));
|
||||
assertEquals(33, x);
|
||||
})();
|
||||
@ -297,7 +311,7 @@ let kSig_r_riiiiiiii = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI32,
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(17, main(5));
|
||||
})();
|
||||
|
||||
@ -324,7 +338,7 @@ let kSig_r_riiiiiiii = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI32,
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(2147483645, main(5));
|
||||
})();
|
||||
|
||||
@ -357,7 +371,7 @@ let kSig_i_rlili = makeSig([kWasmExternRef, kWasmI64, kWasmI32, kWasmI64, kWasmI
|
||||
let param2 = { valueOf: () => { gc(); return 6; } };
|
||||
let param3 = { valueOf: () => { gc(); return 3n; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(60, main(9n, param2, param3, 0));
|
||||
})();
|
||||
|
||||
@ -391,7 +405,7 @@ let kSig_r_riiili = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI32, kWasm
|
||||
let param2 = { valueOf: () => { gc(); return 6; } };
|
||||
let param3 = { valueOf: () => { gc(); return 3; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(9, param2, param3, 0n, 2));
|
||||
assertEquals(72, x);
|
||||
})();
|
||||
@ -428,7 +442,7 @@ let kSig_r_riiilii = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI32,
|
||||
let param2 = { valueOf: () => { gc(); return 6; } };
|
||||
let param3 = { valueOf: () => { gc(); return 3; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(9, param2, param3, 0n, 2, 3));
|
||||
assertEquals(93, x);
|
||||
})();
|
||||
@ -471,7 +485,7 @@ let kSig_r_rliilliiil = makeSig([kWasmExternRef, kWasmI64, kWasmI32, kWasmI32,
|
||||
let param6 = { valueOf: () => { gc(); return 10; } };
|
||||
let param8 = { valueOf: () => { gc(); return 12; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(360, main(param1, 6, 7, param4, 9n, param6, 11, param8, 0n));
|
||||
})();
|
||||
|
||||
@ -497,7 +511,7 @@ let kSig_r_rliilliiil = makeSig([kWasmExternRef, kWasmI64, kWasmI32, kWasmI32,
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertThrows(() => { main(17) }, TypeError);
|
||||
})();
|
||||
|
||||
@ -521,7 +535,7 @@ let kSig_r_rliilliiil = makeSig([kWasmExternRef, kWasmI64, kWasmI32, kWasmI32,
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(10000000000n, main());
|
||||
})();
|
||||
|
||||
@ -545,7 +559,7 @@ let kSig_r_rliilliiil = makeSig([kWasmExternRef, kWasmI64, kWasmI32, kWasmI32,
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(0.5, main());
|
||||
})();
|
||||
|
||||
@ -569,7 +583,7 @@ let kSig_r_rliilliiil = makeSig([kWasmExternRef, kWasmI64, kWasmI32, kWasmI32,
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(0.25, main());
|
||||
})();
|
||||
|
||||
@ -595,7 +609,7 @@ let kSig_r_rliilliiil = makeSig([kWasmExternRef, kWasmI64, kWasmI32, kWasmI32,
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(12.5));
|
||||
assertEquals(25, x);
|
||||
})();
|
||||
@ -622,7 +636,7 @@ let kSig_r_rliilliiil = makeSig([kWasmExternRef, kWasmI64, kWasmI32, kWasmI32,
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(12.5));
|
||||
assertEquals(25, x);
|
||||
})();
|
||||
@ -664,7 +678,7 @@ let kSig_r_rffddddff = makeSig([kWasmExternRef, kWasmF32, kWasmF32, kWasmF64,
|
||||
let param6 = { valueOf: () => { gc(); return 6.5; } };
|
||||
let param8 = { valueOf: () => { gc(); return 8.5; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(param1, 2.5, 3.5, param4, 5.5, param6, 7.5,
|
||||
param8));
|
||||
assertEquals(234, x);
|
||||
@ -712,7 +726,7 @@ let kSig_r_riiliffddlfdff = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI6
|
||||
}
|
||||
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(5, 6, 7n, 8, 1.5, 2.5, 3.5, 4.5, 11n, 5.5, 6.5,
|
||||
7.5, 8.5));
|
||||
assertEquals(137, x);
|
||||
@ -763,7 +777,7 @@ let kSig_r_riiliiiffddli = makeSig([kWasmExternRef, kWasmI32, kWasmI32, kWasmI64
|
||||
let param6 = { valueOf: () => { gc(); return 10; } };
|
||||
let param8 = { valueOf: () => { gc(); return 12; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(undefined, main(param1, 6, 7n, param4, 9, param6,
|
||||
1.5, 2.5, 3.6, 4.4, 11n, param8));
|
||||
assertEquals(360, x);
|
||||
@ -826,7 +840,7 @@ let kSig_f_riiliiiffddlifffdi = makeSig([kWasmExternRef, kWasmI32, kWasmI32,
|
||||
let paramf3 = { valueOf: () => { gc(); return 5.5; } };
|
||||
let param9 = { valueOf: () => { gc(); return 0; } };
|
||||
let instance = builder.instantiate({ mod: { func: import_func } });
|
||||
let main = WebAssembly.returnPromiseOnSuspend(instance.exports.main);
|
||||
let main = ToPromising(instance.exports.main);
|
||||
assertEquals(223, main(param1, 6, 7n, param4, 9, param6, 1.5, 2.5, paramd1,
|
||||
4.5, 11n, param8, paramf3, 6.5, 7.5, 8.5, param9));
|
||||
assertEquals(360, x);
|
||||
@ -841,7 +855,7 @@ let kSig_f_riiliiiffddlifffdi = makeSig([kWasmExternRef, kWasmI32, kWasmI32,
|
||||
|
||||
instance = builder.instantiate();
|
||||
function js_caller() {
|
||||
return WebAssembly.returnPromiseOnSuspend(instance.exports.wasm_fn)();
|
||||
return ToPromising(instance.exports.wasm_fn);
|
||||
}
|
||||
%PrepareFunctionForOptimization(js_caller);
|
||||
js_caller();
|
||||
@ -862,7 +876,7 @@ let kSig_f_riiliiiffddlifffdi = makeSig([kWasmExternRef, kWasmI32, kWasmI32,
|
||||
.exportFunc();
|
||||
|
||||
let instance = builder.instantiate();
|
||||
let f1 = WebAssembly.returnPromiseOnSuspend(instance.exports.f1);
|
||||
let f1 = ToPromising(instance.exports.f1);
|
||||
assertEquals(15, f1());
|
||||
})();
|
||||
|
||||
@ -884,8 +898,8 @@ let kSig_f_riiliiiffddlifffdi = makeSig([kWasmExternRef, kWasmI32, kWasmI32,
|
||||
%DeoptimizeFunction(caller);
|
||||
}
|
||||
|
||||
let main = WebAssembly.returnPromiseOnSuspend(
|
||||
builder.instantiate({q: {func: deopt}}).exports.main);
|
||||
let instance = builder.instantiate({q: {func: deopt}});
|
||||
let main = ToPromising(instance.exports.main);
|
||||
function caller() {
|
||||
main(1, 2, 3, 4, 5);
|
||||
main(1, 2, 3, 4);
|
||||
@ -915,7 +929,7 @@ let kSig_f_riiliiiffddlifffdi = makeSig([kWasmExternRef, kWasmI32, kWasmI32,
|
||||
|
||||
let module = new WebAssembly.Module(builder.toBuffer());
|
||||
let instance = new WebAssembly.Instance(module);
|
||||
let func0 = WebAssembly.returnPromiseOnSuspend(instance.exports.func0);
|
||||
let func0 = ToPromising(instance.exports.func0);
|
||||
let res = func0(1, 2, "3", "4", "5", "6", "7", "8", 9, 10, 11, 12, 13);
|
||||
assertEquals("8", res);
|
||||
})();
|
||||
|
@ -15,6 +15,20 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
/WebAssembly.Suspender must be invoked with 'new'/);
|
||||
})();
|
||||
|
||||
function ToPromising(wasm_export) {
|
||||
let sig = WebAssembly.Function.type(wasm_export);
|
||||
assertTrue(sig.parameters.length > 0);
|
||||
assertEquals('externref', sig.parameters[0]);
|
||||
assertEquals(1, sig.results.length);
|
||||
let wrapper_sig = {
|
||||
parameters: sig.parameters.slice(1),
|
||||
results: ['externref']
|
||||
};
|
||||
return new WebAssembly.Function(
|
||||
wrapper_sig, wasm_export, {promising: 'first'});
|
||||
|
||||
}
|
||||
|
||||
(function TestSuspenderTypes() {
|
||||
print(arguments.callee.name);
|
||||
let builder = new WasmModuleBuilder();
|
||||
@ -36,34 +50,44 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
}
|
||||
|
||||
// Wrap the import, instantiate the module, and wrap the export.
|
||||
let wasm_js_import = new WebAssembly.Function(
|
||||
{parameters: ['i32'], results: ['externref']}, js_import);
|
||||
let import_wrapper = WebAssembly.suspendOnReturnedPromise(wasm_js_import);
|
||||
let import_wrapper = new WebAssembly.Function(
|
||||
{parameters: ['externref', 'i32'], results: []},
|
||||
js_import,
|
||||
{suspending: 'first'});
|
||||
let instance = builder.instantiate({'m': {'import': import_wrapper}});
|
||||
let export_wrapper =
|
||||
WebAssembly.returnPromiseOnSuspend(instance.exports.export);
|
||||
let export_wrapper = ToPromising(instance.exports.export);
|
||||
|
||||
// Check type errors.
|
||||
wasm_js_import = new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: ['i32']}, js_import);
|
||||
assertThrows(() => WebAssembly.suspendOnReturnedPromise(wasm_js_import),
|
||||
TypeError, /Expected a WebAssembly.Function with return type externref/);
|
||||
assertThrows(() => WebAssembly.returnPromiseOnSuspend(instance.exports.wrong1),
|
||||
assertThrows(() => new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: ['externref']},
|
||||
js_import,
|
||||
{suspending: 'foo'}),
|
||||
TypeError,
|
||||
/Expected a WebAssembly.Function with exactly one return type/);
|
||||
assertThrows(() => WebAssembly.returnPromiseOnSuspend(instance.exports.wrong2),
|
||||
/JS Promise Integration: Expected suspender position to be "first", "last" or "none"/);
|
||||
// Bad inner signature (promising)
|
||||
for (const f of [instance.exports.wrong1, instance.exports.wrong2, instance.exports.wrong3]) {
|
||||
assertThrows(() => new WebAssembly.Function(
|
||||
{parameters: ['i32'], results: ['externref']},
|
||||
f,
|
||||
{promising: 'first'}),
|
||||
TypeError,
|
||||
/Incompatible signature for promising function/);
|
||||
}
|
||||
// Signature mismatch (suspending)
|
||||
assertThrows(() => new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: []},
|
||||
new WebAssembly.Function(
|
||||
{parameters: [], results: ['i32']}, js_import),
|
||||
{suspending: 'first'}),
|
||||
TypeError,
|
||||
/Expected a WebAssembly.Function with exactly one return type/);
|
||||
assertThrows(() => WebAssembly.returnPromiseOnSuspend(instance.exports.wrong3),
|
||||
/Incompatible signature for suspending function/);
|
||||
// Signature mismatch (promising)
|
||||
assertThrows(() => new WebAssembly.Function(
|
||||
{parameters: ['externref', 'i32'], results: ['i32']},
|
||||
instance.exports.export,
|
||||
{promising: 'first'}),
|
||||
TypeError,
|
||||
/Expected at least one parameter of type externref/);
|
||||
// Signature mismatch (link error).
|
||||
let wrong_import = new WebAssembly.Function(
|
||||
{parameters: ['externref', 'f32'], results: ['externref']}, () => {});
|
||||
wrong_import = WebAssembly.suspendOnReturnedPromise(wrong_import);
|
||||
assertThrows(() => builder.instantiate({'m': {'import': wrong_import}}),
|
||||
WebAssembly.LinkError,
|
||||
/imported function does not match the expected type/);
|
||||
/Incompatible signature for promising function/);
|
||||
|
||||
// Check the wrapped export's signature.
|
||||
let export_sig = WebAssembly.Function.type(export_wrapper);
|
||||
@ -73,7 +97,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
// Check the wrapped import's signature.
|
||||
let import_sig = WebAssembly.Function.type(import_wrapper);
|
||||
assertEquals(['externref', 'i32'], import_sig.parameters);
|
||||
assertEquals(['externref'], import_sig.results);
|
||||
assertEquals([], import_sig.results);
|
||||
})();
|
||||
|
||||
(function TestStackSwitchSuspenderType() {
|
||||
@ -83,7 +107,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
.addBody([kExprI32Const, 0]).exportFunc();
|
||||
let instance = builder.instantiate();
|
||||
let suspender = new WebAssembly.Suspender();
|
||||
let wrapper = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let wrapper = ToPromising(instance.exports.test);
|
||||
})();
|
||||
|
||||
(function TestStackSwitchNoSuspend() {
|
||||
@ -96,7 +120,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
kExprGlobalSet, 0,
|
||||
kExprI32Const, 0]).exportFunc();
|
||||
let instance = builder.instantiate();
|
||||
let wrapper = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let wrapper = ToPromising(instance.exports.test);
|
||||
wrapper();
|
||||
assertEquals(42, instance.exports.g.value);
|
||||
})();
|
||||
@ -110,34 +134,34 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
kExprLocalGet, 0,
|
||||
kExprCallFunction, import_index, // suspend
|
||||
]).exportFunc();
|
||||
let js_import = WebAssembly.suspendOnReturnedPromise(
|
||||
new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']},
|
||||
() => Promise.resolve(42)));
|
||||
let js_import = new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
() => Promise.resolve(42),
|
||||
{suspending: 'first'});
|
||||
let instance = builder.instantiate({m: {import: js_import}});
|
||||
let wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let wrapped_export = ToPromising(instance.exports.test);
|
||||
let combined_promise = wrapped_export();
|
||||
combined_promise.then(v => assertEquals(42, v));
|
||||
assertPromiseResult(combined_promise, v => assertEquals(42, v));
|
||||
|
||||
// Also try with a JS function with a mismatching arity.
|
||||
js_import = WebAssembly.suspendOnReturnedPromise(
|
||||
new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']},
|
||||
(unused) => Promise.resolve(42)));
|
||||
js_import = new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
(unused) => Promise.resolve(42),
|
||||
{suspending: 'first'});
|
||||
instance = builder.instantiate({m: {import: js_import}});
|
||||
wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
wrapped_export = ToPromising(instance.exports.test);
|
||||
combined_promise = wrapped_export();
|
||||
combined_promise.then(v => assertEquals(42, v));
|
||||
assertPromiseResult(combined_promise, v => assertEquals(42, v));
|
||||
|
||||
// Also try with a proxy.
|
||||
js_import = WebAssembly.suspendOnReturnedPromise(
|
||||
new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']},
|
||||
new Proxy(() => Promise.resolve(42), {})));
|
||||
js_import = new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
new Proxy(() => Promise.resolve(42), {}),
|
||||
{suspending: "first"});
|
||||
instance = builder.instantiate({m: {import: js_import}});
|
||||
wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
wrapped_export = ToPromising(instance.exports.test);
|
||||
combined_promise = wrapped_export();
|
||||
combined_promise.then(v => assertEquals(42, v));
|
||||
assertPromiseResult(combined_promise, v => assertEquals(42, v));
|
||||
})();
|
||||
|
||||
// Check that we can suspend back out of a resumed computation.
|
||||
@ -175,14 +199,14 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
return Promise.resolve(++i);
|
||||
};
|
||||
let wasm_js_import = new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']}, js_import);
|
||||
let suspending_wasm_js_import =
|
||||
WebAssembly.suspendOnReturnedPromise(wasm_js_import);
|
||||
let instance = builder.instantiate({m: {import: suspending_wasm_js_import}});
|
||||
let wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
js_import,
|
||||
{suspending: 'first'});
|
||||
let instance = builder.instantiate({m: {import: wasm_js_import}});
|
||||
let wrapped_export = ToPromising(instance.exports.test);
|
||||
let chained_promise = wrapped_export();
|
||||
assertEquals(0, instance.exports.g.value);
|
||||
chained_promise.then(_ => assertEquals(15, instance.exports.g.value));
|
||||
assertPromiseResult(chained_promise, _ => assertEquals(15, instance.exports.g.value));
|
||||
})();
|
||||
|
||||
// Call the GC in the import call.
|
||||
@ -196,12 +220,12 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
kExprCallFunction, gc_index,
|
||||
kExprI32Const, 0
|
||||
]).exportFunc();
|
||||
let js_import = WebAssembly.suspendOnReturnedPromise(
|
||||
new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']},
|
||||
gc));
|
||||
let js_import = new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: []},
|
||||
gc,
|
||||
{suspending: 'first'});
|
||||
let instance = builder.instantiate({'m': {'gc': js_import}});
|
||||
let wrapper = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let wrapper = ToPromising(instance.exports.test);
|
||||
wrapper();
|
||||
})();
|
||||
|
||||
@ -217,14 +241,14 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
kExprLocalGet, 1,
|
||||
kExprCallFunction, import_index,
|
||||
]).exportFunc();
|
||||
let js_import = WebAssembly.suspendOnReturnedPromise(
|
||||
new WebAssembly.Function(
|
||||
{parameters: ['i32'], results: ['externref']},
|
||||
(v) => { return Promise.resolve(v) }));
|
||||
let js_import = new WebAssembly.Function(
|
||||
{parameters: ['externref', 'i32'], results: ['i32']},
|
||||
(v) => { return Promise.resolve(v) },
|
||||
{suspending: 'first'});
|
||||
let instance = builder.instantiate({'m': {'import': js_import}});
|
||||
let wrapper = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let wrapper = ToPromising(instance.exports.test);
|
||||
let arg = { valueOf: () => { gc(); return 24; } };
|
||||
wrapper(arg).then((v) => assertEquals(arg.valueOf(), v));
|
||||
assertPromiseResult(wrapper(arg), v => assertEquals(arg.valueOf(), v));
|
||||
})();
|
||||
|
||||
// Check that the suspender does not suspend if the import's
|
||||
@ -244,10 +268,12 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
function js_import() {
|
||||
return 42
|
||||
};
|
||||
let wasm_js_import = new WebAssembly.Function({parameters: [], results: ['externref']}, js_import);
|
||||
let suspending_wasm_js_import = WebAssembly.suspendOnReturnedPromise(wasm_js_import);
|
||||
let instance = builder.instantiate({m: {import: suspending_wasm_js_import}});
|
||||
let wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let wasm_js_import = new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
js_import,
|
||||
{suspending: 'first'});
|
||||
let instance = builder.instantiate({m: {import: wasm_js_import}});
|
||||
let wrapped_export = ToPromising(instance.exports.test);
|
||||
let result = wrapped_export();
|
||||
assertEquals(42, instance.exports.g.value);
|
||||
})();
|
||||
@ -276,17 +302,17 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
return Promise.resolve(reduce(Array.from(arguments)));
|
||||
};
|
||||
let wasm_js_import = new WebAssembly.Function(
|
||||
{parameters: ['i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'f32', 'f32',
|
||||
'f32', 'f32', 'f32', 'f32', 'f32'], results: ['externref']}, js_import);
|
||||
let suspending_wasm_js_import =
|
||||
WebAssembly.suspendOnReturnedPromise(wasm_js_import);
|
||||
{parameters: ['externref', 'i32', 'i32', 'i32', 'i32', 'i32', 'i32', 'f32',
|
||||
'f32', 'f32', 'f32', 'f32', 'f32', 'f32'], results: ['i32']},
|
||||
js_import,
|
||||
{suspending: 'first'});
|
||||
|
||||
let instance = builder.instantiate({m: {import: suspending_wasm_js_import}});
|
||||
let wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let instance = builder.instantiate({m: {import: wasm_js_import}});
|
||||
let wrapped_export = ToPromising(instance.exports.test);
|
||||
let args = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
||||
let combined_promise =
|
||||
wrapped_export.apply(null, args);
|
||||
combined_promise.then(v => assertEquals(reduce(args), v));
|
||||
assertPromiseResult(combined_promise, v => assertEquals(reduce(args), v));
|
||||
})();
|
||||
|
||||
(function TestStackSwitchReturnFloat() {
|
||||
@ -303,14 +329,14 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
return Promise.resolve(0.5);
|
||||
};
|
||||
let wasm_js_import = new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']}, js_import);
|
||||
let suspending_wasm_js_import =
|
||||
WebAssembly.suspendOnReturnedPromise(wasm_js_import);
|
||||
{parameters: ['externref'], results: ['f32']},
|
||||
js_import,
|
||||
{suspending: 'first'});
|
||||
|
||||
let instance = builder.instantiate({m: {import: suspending_wasm_js_import}});
|
||||
let wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let instance = builder.instantiate({m: {import: wasm_js_import}});
|
||||
let wrapped_export = ToPromising(instance.exports.test);
|
||||
let combined_promise = wrapped_export();
|
||||
combined_promise.then(v => assertEquals(0.5, v));
|
||||
assertPromiseResult(combined_promise, v => assertEquals(0.5, v));
|
||||
})();
|
||||
|
||||
// Throw an exception after the initial prompt.
|
||||
@ -321,7 +347,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
builder.addFunction("throw", kSig_i_r)
|
||||
.addBody([kExprThrow, tag]).exportFunc();
|
||||
let instance = builder.instantiate();
|
||||
let wrapper = WebAssembly.returnPromiseOnSuspend(instance.exports.throw);
|
||||
let wrapper = ToPromising(instance.exports.throw);
|
||||
try {
|
||||
wrapper();
|
||||
assertUnreachable();
|
||||
@ -348,12 +374,12 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
return Promise.resolve(42);
|
||||
};
|
||||
let wasm_js_import = new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']}, js_import);
|
||||
let suspending_wasm_js_import =
|
||||
WebAssembly.suspendOnReturnedPromise(wasm_js_import);
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
js_import,
|
||||
{suspending: 'first'});
|
||||
|
||||
let instance = builder.instantiate({m: {import: suspending_wasm_js_import, tag: tag}});
|
||||
let wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let instance = builder.instantiate({m: {import: wasm_js_import, tag: tag}});
|
||||
let wrapped_export = ToPromising(instance.exports.test);
|
||||
let combined_promise = wrapped_export();
|
||||
assertThrowsAsync(combined_promise, WebAssembly.Exception);
|
||||
})();
|
||||
@ -376,12 +402,12 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
|
||||
return Promise.reject(new WebAssembly.Exception(tag, [42]));
|
||||
};
|
||||
let wasm_js_import = new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']}, js_import);
|
||||
let suspending_wasm_js_import =
|
||||
WebAssembly.suspendOnReturnedPromise(wasm_js_import);
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
js_import,
|
||||
{suspending: 'first'});
|
||||
|
||||
let instance = builder.instantiate({m: {import: suspending_wasm_js_import, tag: tag}});
|
||||
let wrapped_export = WebAssembly.returnPromiseOnSuspend(instance.exports.test);
|
||||
let instance = builder.instantiate({m: {import: wasm_js_import, tag: tag}});
|
||||
let wrapped_export = ToPromising(instance.exports.test);
|
||||
let combined_promise = wrapped_export();
|
||||
assertPromiseResult(combined_promise, v => assertEquals(v, 42));
|
||||
})();
|
||||
@ -410,20 +436,20 @@ function TestNestedSuspenders(suspend) {
|
||||
kExprCallFunction, inner_index
|
||||
]).exportFunc();
|
||||
|
||||
let inner = WebAssembly.suspendOnReturnedPromise(
|
||||
new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']},
|
||||
() => suspend ? Promise.resolve(42) : 42));
|
||||
let inner = new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
() => suspend ? Promise.resolve(42) : 42,
|
||||
{suspending: 'first'});
|
||||
|
||||
let export_inner;
|
||||
let outer = WebAssembly.suspendOnReturnedPromise(
|
||||
new WebAssembly.Function(
|
||||
{parameters: [], results: ['externref']},
|
||||
() => export_inner()));
|
||||
let outer = new WebAssembly.Function(
|
||||
{parameters: ['externref'], results: ['i32']},
|
||||
() => export_inner(),
|
||||
{suspending: 'first'});
|
||||
|
||||
let instance = builder.instantiate({m: {inner, outer}});
|
||||
export_inner = WebAssembly.returnPromiseOnSuspend(instance.exports.inner);
|
||||
let export_outer = WebAssembly.returnPromiseOnSuspend(instance.exports.outer);
|
||||
export_inner = ToPromising(instance.exports.inner);
|
||||
let export_outer = ToPromising(instance.exports.outer);
|
||||
if (suspend) {
|
||||
assertPromiseResult(export_outer(), v => assertEquals(42, v));
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user