diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 8bd5bc15d4..c6970e9677 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -7828,8 +7828,7 @@ WasmImportData ResolveWasmImportCall( if (WasmJSFunction::IsWasmJSFunction(*callable)) { auto js_function = Handle::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. diff --git a/src/diagnostics/objects-printer.cc b/src/diagnostics/objects-printer.cc index 9ff50baefa..e3548d4b6c 100644 --- a/src/diagnostics/objects-printer.cc +++ b/src/diagnostics/objects-printer.cc @@ -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"; } diff --git a/src/heap/factory.cc b/src/heap/factory.cc index 661c4e0b34..c0cd9187fd 100644 --- a/src/heap/factory.cc +++ b/src/heap/factory.cc @@ -1671,7 +1671,8 @@ Handle Factory::NewWasmInternalFunction( Handle Factory::NewWasmJSFunctionData( Address opt_call_target, Handle callable, int return_count, int parameter_count, Handle> serialized_sig, - Handle wrapper_code, Handle rtt, wasm::Suspend suspend) { + Handle wrapper_code, Handle rtt, wasm::Suspend suspend, + wasm::Promise promise) { Handle ref = NewWasmApiFunctionRef(callable, suspend, Handle()); Handle internal = @@ -1686,7 +1687,8 @@ Handle 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 Factory::NewWasmExportedFunctionData( Handle export_wrapper, Handle instance, Address call_target, Handle ref, int func_index, Address sig_address, int wrapper_budget, Handle rtt, - wasm::Suspend suspend) { + wasm::Promise promise) { Handle sig_foreign = NewForeign(sig_address); Handle internal = NewWasmInternalFunction(call_target, Handle::cast(ref), rtt); @@ -1728,7 +1730,9 @@ Handle 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 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()); } diff --git a/src/heap/factory.h b/src/heap/factory.h index 5b2656e229..01ddb7a60b 100644 --- a/src/heap/factory.h +++ b/src/heap/factory.h @@ -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 { Handle export_wrapper, Handle instance, Address call_target, Handle ref, int func_index, Address sig_address, int wrapper_budget, Handle rtt, - wasm::Suspend suspend); + wasm::Promise promise); Handle NewWasmApiFunctionRef( Handle callable, wasm::Suspend suspend, Handle instance); @@ -652,7 +653,8 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase { Handle NewWasmJSFunctionData( Address opt_call_target, Handle callable, int return_count, int parameter_count, Handle> serialized_sig, - Handle wrapper_code, Handle rtt, wasm::Suspend suspend); + Handle wrapper_code, Handle rtt, wasm::Suspend suspend, + wasm::Promise promise); Handle NewWasmResumeData( Handle suspender, wasm::OnResume on_resume); Handle NewWasmStruct(const wasm::StructType* type, diff --git a/src/wasm/module-instantiate.cc b/src/wasm/module-instantiate.cc index 07815909cd..e59d4292f0 100644 --- a/src/wasm/module-instantiate.cc +++ b/src/wasm/module-instantiate.cc @@ -743,8 +743,7 @@ MaybeHandle InstanceBuilder::Build() { // function. Use CWasmEntry instead. start_function_ = WasmExportedFunction::New( isolate_, instance, start_index, - static_cast(function.sig->parameter_count()), wrapper_code, - kNoSuspend); + static_cast(function.sig->parameter_count()), wrapper_code); if (function.imported) { ImportedFunctionEntry entry(instance, module_->start_function_index); diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index bfffa8dce6..c91d3b5f25 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -244,16 +244,6 @@ i::wasm::ModuleWireBytes GetFirstArgumentAsBytes( return i::wasm::ModuleWireBytes(start, start + length); } -i::MaybeHandle GetFirstArgumentAsJSFunction( - const v8::FunctionCallbackInfo& args, ErrorThrower* thrower) { - i::Handle arg0 = Utils::OpenHandle(*args[0]); - if (!arg0->IsJSFunction()) { - thrower->TypeError("Argument 0 must be a function"); - return {}; - } - return i::Handle::cast(arg0); -} - namespace { i::MaybeHandle ImportsAsMaybeReceiver(Local ffi) { if (ffi->IsUndefined()) return {}; @@ -1889,6 +1879,76 @@ void WebAssemblyException(const v8::FunctionCallbackInfo& args) { Utils::ToLocal(i::Handle::cast(runtime_exception))); } +namespace { +bool HasJSPromiseIntegrationFlag(Isolate* isolate, Local usage_obj, + ErrorThrower* thrower, const char* flag_name) { + Local context = isolate->GetCurrentContext(); + Local flag_str = v8_str(isolate, flag_name); + Local first_str = v8_str(isolate, "first"); + Local last_str = v8_str(isolate, "last"); + Local none_str = v8_str(isolate, "none"); + v8::MaybeLocal maybe_flag = usage_obj->Get(context, flag_str); + v8::Local flag_value; + v8::Local 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& args) { v8::Isolate* isolate = args.GetIsolate(); @@ -1983,7 +2043,35 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo& args) { i::Handle callable = Utils::OpenHandle(*args[1].As()); - 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 usage_obj = Local::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::cast(callable)->sig() == *sig) { args.GetReturnValue().Set(Utils::ToLocal(callable)); return; @@ -1995,7 +2083,7 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo& args) { return; } - if (i::WasmJSFunction::IsWasmJSFunction(*callable)) { + if (is_wasm_js_function && !suspend && !promise) { if (i::Handle::cast(callable)->MatchesSignature(sig)) { args.GetReturnValue().Set(Utils::ToLocal(callable)); return; @@ -2007,8 +2095,49 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo& 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 instance( + i::WasmInstanceObject::cast(data.internal().ref()), i_isolate); + int func_index = data.function_index(); + i::Handle wrapper = + BUILTIN_CODE(i_isolate, WasmReturnPromiseOnSuspend); + i::Handle result = i::WasmExportedFunction::New( + i_isolate, instance, func_index, + static_cast(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 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& args) { i::Handle 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& args) { } } else if (i::WasmJSFunction::IsWasmJSFunction(*arg0)) { sig = i::Handle::cast(arg0)->GetSignature(&zone); - auto wasm_js_function = i::Handle::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& args) { args.GetReturnValue().Set(Utils::ToLocal(type)); } -// WebAssembly.returnPromiseOnSuspend(WebAssembly.Function) -> -// WebAssembly.Function -void WebAssemblyReturnPromiseOnSuspend( - const v8::FunctionCallbackInfo& args) { - Isolate* isolate = args.GetIsolate(); - HandleScope scope(isolate); - i::Isolate* i_isolate = reinterpret_cast(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 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 instance( - i::WasmInstanceObject::cast(data.internal().ref()), i_isolate); - i::Handle wrapper = - BUILTIN_CODE(i_isolate, WasmReturnPromiseOnSuspend); - // Upcast to JSFunction to re-use the existing ToLocal helper below. - i::Handle result = - i::Handle::cast(i::WasmExportedFunction::New( - i_isolate, instance, index, - static_cast(data.sig()->parameter_count()), wrapper, - internal::wasm::kSuspend)); - args.GetReturnValue().Set(Utils::ToLocal(result)); -} - -// WebAssembly.suspendOnReturnedPromise(Function) -> Function -void WebAssemblySuspendOnReturnedPromise( - const v8::FunctionCallbackInfo& args) { - v8::Isolate* isolate = args.GetIsolate(); - i::Isolate* i_isolate = reinterpret_cast(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 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::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::cast(arg0)->GetCallable(), i_isolate); - i::Handle 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 diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc index 6f3f03498f..0b0a36e3c9 100644 --- a/src/wasm/wasm-objects.cc +++ b/src/wasm/wasm-objects.cc @@ -1431,8 +1431,7 @@ WasmInstanceObject::GetOrCreateWasmInternalFunction( } auto external = Handle::cast(WasmExportedFunction::New( isolate, instance, function_index, - static_cast(function.sig->parameter_count()), wrapper, - wasm::kNoSuspend)); + static_cast(function.sig->parameter_count()), wrapper)); result = WasmInternalFunction::FromExternal(external, isolate).ToHandleChecked(); @@ -1972,7 +1971,7 @@ int WasmExportedFunction::function_index() { Handle WasmExportedFunction::New( Isolate* isolate, Handle instance, int func_index, - int arity, Handle export_wrapper, wasm::Suspend suspend) { + int arity, Handle export_wrapper) { DCHECK( CodeKind::JS_TO_WASM_FUNCTION == export_wrapper->kind() || (export_wrapper->is_builtin() && @@ -1998,11 +1997,15 @@ Handle WasmExportedFunction::New( } else { rtt = factory->wasm_internal_function_map(); } + wasm::Promise promise = + export_wrapper->builtin_id() == Builtin::kWasmReturnPromiseOnSuspend + ? wasm::kPromise + : wasm::kNoPromise; Handle function_data = factory->NewWasmExportedFunctionData( export_wrapper, instance, call_target, ref, func_index, reinterpret_cast
(sig), wasm::kGenericWrapperBudget, rtt, - suspend); + promise); MaybeHandle maybe_name; bool is_asm_js_module = instance->module_object().is_asm_js(); @@ -2130,7 +2133,7 @@ Handle WasmJSFunction::New(Isolate* isolate, Handle rtt = factory->wasm_internal_function_map(); Handle 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(return_count, parameter_count, types); } -bool WasmJSFunction::MatchesSignatureForSuspend(const wasm::FunctionSig* sig) { - DCHECK_LE(sig->all().size(), kMaxInt); - int sig_size = static_cast(sig->all().size()); - int parameter_count = static_cast(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); diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h index 334d14b540..be04e10bda 100644 --- a/src/wasm/wasm-objects.h +++ b/src/wasm/wasm-objects.h @@ -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 New( Isolate* isolate, Handle instance, int func_index, - int arity, Handle export_wrapper, wasm::Suspend suspend); + int arity, Handle 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; + using SuspendField = base::BitField; + using PromiseField = base::BitField; + TQ_OBJECT_CONSTRUCTORS(WasmFunctionData) }; diff --git a/src/wasm/wasm-objects.tq b/src/wasm/wasm-objects.tq index 90c57e0b44..67ae65a0d8 100644 --- a/src/wasm/wasm-objects.tq +++ b/src/wasm/wasm-objects.tq @@ -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 { diff --git a/test/mjsunit/wasm/stack-switching-export.js b/test/mjsunit/wasm/stack-switching-export.js index f0879c2c10..3fbe979ab6 100644 --- a/test/mjsunit/wasm/stack-switching-export.js +++ b/test/mjsunit/wasm/stack-switching-export.js @@ -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); })(); diff --git a/test/mjsunit/wasm/stack-switching.js b/test/mjsunit/wasm/stack-switching.js index f72bf1d146..bc78b3c708 100644 --- a/test/mjsunit/wasm/stack-switching.js +++ b/test/mjsunit/wasm/stack-switching.js @@ -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 {