diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index aa471a175f..720fffc1ba 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -7579,46 +7579,48 @@ std::unique_ptr NewJSToWasmCompilationJob( std::move(debug_name), WasmAssemblerOptions()); } -std::pair> ResolveWasmImportCall( +WasmImportData ResolveWasmImportCall( Handle callable, const wasm::FunctionSig* expected_sig, const wasm::WasmModule* module, const wasm::WasmFeatures& enabled_features) { + Isolate* isolate = callable->GetIsolate(); + Handle no_suspender; if (WasmExportedFunction::IsWasmExportedFunction(*callable)) { auto imported_function = Handle::cast(callable); if (!imported_function->MatchesSignature(module, expected_sig)) { - return std::make_pair(WasmImportCallKind::kLinkError, callable); + return {WasmImportCallKind::kLinkError, callable, no_suspender}; } uint32_t func_index = static_cast(imported_function->function_index()); if (func_index >= imported_function->instance().module()->num_imported_functions) { - return std::make_pair(WasmImportCallKind::kWasmToWasm, callable); + return {WasmImportCallKind::kWasmToWasm, callable, no_suspender}; } - Isolate* isolate = callable->GetIsolate(); // Resolve the shortcut to the underlying callable and continue. Handle instance(imported_function->instance(), isolate); ImportedFunctionEntry entry(instance, func_index); callable = handle(entry.callable(), isolate); } + Handle suspender = isolate->factory()->undefined_value(); if (WasmJSFunction::IsWasmJSFunction(*callable)) { auto js_function = Handle::cast(callable); if (!js_function->MatchesSignature(expected_sig)) { - return std::make_pair(WasmImportCallKind::kLinkError, callable); + return {WasmImportCallKind::kLinkError, callable, no_suspender}; } - Isolate* isolate = callable->GetIsolate(); + suspender = handle(js_function->GetSuspender(), isolate); // Resolve the short-cut to the underlying callable and continue. callable = handle(js_function->GetCallable(), isolate); } if (WasmCapiFunction::IsWasmCapiFunction(*callable)) { auto capi_function = Handle::cast(callable); if (!capi_function->MatchesSignature(expected_sig)) { - return std::make_pair(WasmImportCallKind::kLinkError, callable); + return {WasmImportCallKind::kLinkError, callable, no_suspender}; } - return std::make_pair(WasmImportCallKind::kWasmToCapi, callable); + return {WasmImportCallKind::kWasmToCapi, callable, no_suspender}; } // Assuming we are calling to JS, check whether this would be a runtime error. if (!wasm::IsJSCompatibleSignature(expected_sig, module, enabled_features)) { - return std::make_pair(WasmImportCallKind::kRuntimeTypeError, callable); + return {WasmImportCallKind::kRuntimeTypeError, callable, no_suspender}; } // For JavaScript calls, determine whether the target has an arity match. if (callable->IsJSFunction()) { @@ -7634,7 +7636,7 @@ std::pair> ResolveWasmImportCall( if (!sig) sig = wasm::WasmOpcodes::AsmjsSignature(wasm::kExpr##name); \ DCHECK_NOT_NULL(sig); \ if (*expected_sig == *sig) { \ - return std::make_pair(WasmImportCallKind::k##name, callable); \ + return {WasmImportCallKind::k##name, callable, no_suspender}; \ } \ } #define COMPARE_SIG_FOR_BUILTIN_F64(name) \ @@ -7679,13 +7681,12 @@ std::pair> ResolveWasmImportCall( if (IsClassConstructor(shared->kind())) { // Class constructor will throw anyway. - return std::make_pair(WasmImportCallKind::kUseCallBuiltin, callable); + return {WasmImportCallKind::kUseCallBuiltin, callable, suspender}; } if (shared->internal_formal_parameter_count_without_receiver() == expected_sig->parameter_count()) { - return std::make_pair(WasmImportCallKind::kJSFunctionArityMatch, - callable); + return {WasmImportCallKind::kJSFunctionArityMatch, callable, suspender}; } // If function isn't compiled, compile it now. @@ -7696,11 +7697,10 @@ std::pair> ResolveWasmImportCall( &is_compiled_scope); } - return std::make_pair(WasmImportCallKind::kJSFunctionArityMismatch, - callable); + return {WasmImportCallKind::kJSFunctionArityMismatch, callable, suspender}; } // Unknown case. Use the call builtin. - return std::make_pair(WasmImportCallKind::kUseCallBuiltin, callable); + return {WasmImportCallKind::kUseCallBuiltin, callable, suspender}; } namespace { diff --git a/src/compiler/wasm-compiler.h b/src/compiler/wasm-compiler.h index a4bcb10bb9..022461f253 100644 --- a/src/compiler/wasm-compiler.h +++ b/src/compiler/wasm-compiler.h @@ -107,14 +107,19 @@ enum class WasmImportCallKind : uint8_t { constexpr WasmImportCallKind kDefaultImportCallKind = WasmImportCallKind::kJSFunctionArityMatch; +struct WasmImportData { + WasmImportCallKind kind; + Handle callable; + Handle suspender; +}; // Resolves which import call wrapper is required for the given JS callable. -// Returns the kind of wrapper need and the ultimate target callable. Note that -// some callables (e.g. a {WasmExportedFunction} or {WasmJSFunction}) just wrap -// another target, which is why the ultimate target is returned as well. -V8_EXPORT_PRIVATE std::pair> -ResolveWasmImportCall(Handle callable, const wasm::FunctionSig* sig, - const wasm::WasmModule* module, - const wasm::WasmFeatures& enabled_features); +// Returns the kind of wrapper needed, the ultimate target callable, and the +// suspender object if applicable. Note that some callables (e.g. a +// {WasmExportedFunction} or {WasmJSFunction}) just wrap another target, which +// is why the ultimate target is returned as well. +V8_EXPORT_PRIVATE WasmImportData ResolveWasmImportCall( + Handle callable, const wasm::FunctionSig* sig, + const wasm::WasmModule* module, const wasm::WasmFeatures& enabled_features); // Compiles an import call wrapper, which allows Wasm to call imports. V8_EXPORT_PRIVATE wasm::WasmCompilationResult CompileWasmImportCallWrapper( diff --git a/src/diagnostics/objects-printer.cc b/src/diagnostics/objects-printer.cc index 1bfc47fd5e..a97cb74e9e 100644 --- a/src/diagnostics/objects-printer.cc +++ b/src/diagnostics/objects-printer.cc @@ -1946,6 +1946,7 @@ void WasmApiFunctionRef::WasmApiFunctionRefPrint(std::ostream& os) { os << "\n - isolate_root: " << reinterpret_cast(isolate_root()); os << "\n - native_context: " << Brief(native_context()); os << "\n - callable: " << Brief(callable()); + os << "\n - suspender: " << Brief(suspender()); os << "\n"; } diff --git a/src/heap/factory.cc b/src/heap/factory.cc index 53162863ec..b739fac21c 100644 --- a/src/heap/factory.cc +++ b/src/heap/factory.cc @@ -1523,7 +1523,7 @@ Handle Factory::NewWasmTypeInfo( } Handle Factory::NewWasmApiFunctionRef( - Handle callable) { + Handle callable, Handle suspender) { Map map = *wasm_api_function_ref_map(); auto result = WasmApiFunctionRef::cast(AllocateRawWithImmortalMap( map.instance_size(), AllocationType::kOld, map)); @@ -1535,6 +1535,11 @@ Handle Factory::NewWasmApiFunctionRef( } else { result.set_callable(*undefined_value()); } + if (!suspender.is_null()) { + result.set_suspender(*suspender); + } else { + result.set_suspender(*undefined_value()); + } return handle(result, isolate()); } @@ -1556,8 +1561,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) { - Handle ref = NewWasmApiFunctionRef(callable); + Handle wrapper_code, Handle rtt, Handle suspender) { + Handle ref = NewWasmApiFunctionRef(callable, suspender); Handle internal = NewWasmInternalFunction(opt_call_target, ref, rtt); Map map = *wasm_js_function_data_map(); @@ -1602,7 +1607,8 @@ Handle Factory::NewWasmCapiFunctionData( Address call_target, Handle embedder_data, Handle wrapper_code, Handle rtt, Handle> serialized_sig) { - Handle ref = NewWasmApiFunctionRef(Handle()); + Handle ref = + NewWasmApiFunctionRef(Handle(), Handle()); Handle internal = NewWasmInternalFunction(call_target, ref, rtt); Map map = *wasm_capi_function_data_map(); diff --git a/src/heap/factory.h b/src/heap/factory.h index ec1e180cf8..aabca7d5f6 100644 --- a/src/heap/factory.h +++ b/src/heap/factory.h @@ -604,13 +604,15 @@ 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); - Handle NewWasmApiFunctionRef(Handle callable); + Handle NewWasmApiFunctionRef( + Handle callable, Handle suspender); // {opt_call_target} is kNullAddress for JavaScript functions, and // non-null for exported Wasm functions. Handle NewWasmJSFunctionData( Address opt_call_target, Handle callable, int return_count, int parameter_count, Handle> serialized_sig, - Handle wrapper_code, Handle rtt); + Handle wrapper_code, Handle rtt, + Handle suspender); Handle NewWasmStruct(const wasm::StructType* type, wasm::WasmValue* args, Handle map); Handle NewWasmArray(const wasm::ArrayType* type, diff --git a/src/wasm/module-instantiate.cc b/src/wasm/module-instantiate.cc index 6b12d2a54f..01dde898cc 100644 --- a/src/wasm/module-instantiate.cc +++ b/src/wasm/module-instantiate.cc @@ -1181,8 +1181,8 @@ bool InstanceBuilder::ProcessImportedFunction( const FunctionSig* expected_sig = module_->functions[func_index].sig; auto resolved = compiler::ResolveWasmImportCall(js_receiver, expected_sig, module_, enabled_); - compiler::WasmImportCallKind kind = resolved.first; - js_receiver = resolved.second; + compiler::WasmImportCallKind kind = resolved.kind; + js_receiver = resolved.callable; switch (kind) { case compiler::WasmImportCallKind::kLinkError: ReportLinkError("imported function does not match the expected type", @@ -1224,7 +1224,8 @@ bool InstanceBuilder::ProcessImportedFunction( ImportedFunctionEntry entry(instance, func_index); // We re-use the SetWasmToJs infrastructure because it passes the // callable to the wrapper, which we need to get the function data. - entry.SetWasmToJs(isolate_, js_receiver, wasm_code); + entry.SetWasmToJs(isolate_, js_receiver, wasm_code, + isolate_->factory()->undefined_value()); break; } default: { @@ -1245,7 +1246,7 @@ bool InstanceBuilder::ProcessImportedFunction( ImportedFunctionEntry entry(instance, func_index); if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) { // Wasm to JS wrappers are treated specially in the import table. - entry.SetWasmToJs(isolate_, js_receiver, wasm_code); + entry.SetWasmToJs(isolate_, js_receiver, wasm_code, resolved.suspender); } else { // Wasm math intrinsics are compiled as regular Wasm functions. DCHECK(kind >= compiler::WasmImportCallKind::kFirstMathIntrinsic && @@ -1634,7 +1635,7 @@ void InstanceBuilder::CompileImportWrappers( const FunctionSig* sig = module_->functions[func_index].sig; auto resolved = compiler::ResolveWasmImportCall(js_receiver, sig, module_, enabled_); - compiler::WasmImportCallKind kind = resolved.first; + compiler::WasmImportCallKind kind = resolved.kind; if (kind == compiler::WasmImportCallKind::kWasmToWasm || kind == compiler::WasmImportCallKind::kLinkError || kind == compiler::WasmImportCallKind::kWasmToCapi) { @@ -1642,9 +1643,9 @@ void InstanceBuilder::CompileImportWrappers( } int expected_arity = static_cast(sig->parameter_count()); - if (resolved.first == + if (resolved.kind == compiler::WasmImportCallKind::kJSFunctionArityMismatch) { - Handle function = Handle::cast(resolved.second); + Handle function = Handle::cast(resolved.callable); SharedFunctionInfo shared = function->shared(); expected_arity = shared.internal_formal_parameter_count_without_receiver(); diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index 25213436c4..08b6ab5c90 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -25,9 +25,11 @@ #include "src/init/v8.h" #include "src/objects/fixed-array.h" #include "src/objects/instance-type.h" +#include "src/objects/js-function.h" #include "src/objects/js-promise-inl.h" #include "src/objects/managed-inl.h" #include "src/objects/objects-inl.h" +#include "src/objects/shared-function-info.h" #include "src/objects/templates.h" #include "src/parsing/parse-info.h" #include "src/tasks/task-utils.h" @@ -1894,8 +1896,10 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo& args) { return; } + i::Handle suspender = + handle(i::ReadOnlyRoots(i_isolate).undefined_value(), i_isolate); i::Handle result = - i::WasmJSFunction::New(i_isolate, sig, callable); + i::WasmJSFunction::New(i_isolate, sig, callable, suspender); args.GetReturnValue().Set(Utils::ToLocal(result)); } @@ -1925,6 +1929,7 @@ void WebAssemblyFunctionType(const v8::FunctionCallbackInfo& args) { constexpr const char* kName_WasmGlobalObject = "WebAssembly.Global"; constexpr const char* kName_WasmMemoryObject = "WebAssembly.Memory"; constexpr const char* kName_WasmInstanceObject = "WebAssembly.Instance"; +constexpr const char* kName_WasmSuspenderObject = "WebAssembly.Suspender"; constexpr const char* kName_WasmTableObject = "WebAssembly.Table"; constexpr const char* kName_WasmTagObject = "WebAssembly.Tag"; constexpr const char* kName_WasmExceptionPackage = "WebAssembly.Exception"; @@ -2580,6 +2585,38 @@ void WebAssemblySuspenderReturnPromiseOnSuspend( args.GetReturnValue().Set(Utils::ToLocal(result)); } +// WebAssembly.Suspender.suspendOnReturnedPromise(Function) -> Function +void WebAssemblySuspenderSuspendOnReturnedPromise( + const v8::FunctionCallbackInfo& args) { + v8::Isolate* isolate = args.GetIsolate(); + i::Isolate* i_isolate = reinterpret_cast(isolate); + HandleScope scope(isolate); + ScheduledErrorThrower thrower( + i_isolate, "WebAssembly.Suspender.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); + + auto callable = handle( + i::Handle::cast(arg0)->GetCallable(), i_isolate); + EXTRACT_THIS(suspender, WasmSuspenderObject); + i::Handle result = + i::WasmJSFunction::New(i_isolate, sig, callable, suspender); + args.GetReturnValue().Set(Utils::ToLocal(result)); +} } // namespace // TODO(titzer): we use the API to create the function template because the @@ -2876,6 +2913,8 @@ void WasmJs::Install(Isolate* isolate, bool exposed_on_global_object) { WasmSuspenderObject::kHeaderSize, "WebAssembly.Suspender"); InstallFunc(isolate, suspender_proto, "returnPromiseOnSuspend", WebAssemblySuspenderReturnPromiseOnSuspend, 1); + InstallFunc(isolate, suspender_proto, "suspendOnReturnedPromise", + WebAssemblySuspenderSuspendOnReturnedPromise, 1); } // Setup Function diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc index ea36cfc5b6..641bf601c8 100644 --- a/src/wasm/wasm-objects.cc +++ b/src/wasm/wasm-objects.cc @@ -1076,7 +1076,7 @@ FunctionTargetAndRef::FunctionTargetAndRef( void ImportedFunctionEntry::SetWasmToJs( Isolate* isolate, Handle callable, - const wasm::WasmCode* wasm_to_js_wrapper) { + const wasm::WasmCode* wasm_to_js_wrapper, Handle suspender) { TRACE_IFT("Import callable 0x%" PRIxPTR "[%d] = {callable=0x%" PRIxPTR ", target=%p}\n", instance_->ptr(), index_, callable->ptr(), @@ -1084,7 +1084,7 @@ void ImportedFunctionEntry::SetWasmToJs( DCHECK(wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToJsWrapper || wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToCapiWrapper); Handle ref = - isolate->factory()->NewWasmApiFunctionRef(callable); + isolate->factory()->NewWasmApiFunctionRef(callable, suspender); instance_->imported_function_refs().set(index_, *ref); instance_->imported_function_targets()[index_] = wasm_to_js_wrapper->instruction_start(); @@ -1448,8 +1448,8 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable( const wasm::WasmFeatures enabled = native_module->enabled_features(); auto resolved = compiler::ResolveWasmImportCall( callable, sig, instance->module(), enabled); - compiler::WasmImportCallKind kind = resolved.first; - callable = resolved.second; // Update to ultimate target. + compiler::WasmImportCallKind kind = resolved.kind; + callable = resolved.callable; // Update to ultimate target. DCHECK_NE(compiler::WasmImportCallKind::kLinkError, kind); wasm::CompilationEnv env = native_module->CreateCompilationEnv(); // {expected_arity} should only be used if kind != kJSFunctionArityMismatch. @@ -1479,8 +1479,9 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable( } // Update the dispatch table. + Handle suspender = handle(js_function->GetSuspender(), isolate); Handle ref = - isolate->factory()->NewWasmApiFunctionRef(callable); + isolate->factory()->NewWasmApiFunctionRef(callable, suspender); WasmIndirectFunctionTable::cast( instance->indirect_function_tables().get(table_index)) .Set(entry_index, sig_id, call_target, *ref); @@ -2031,7 +2032,8 @@ bool WasmJSFunction::IsWasmJSFunction(Object object) { Handle WasmJSFunction::New(Isolate* isolate, const wasm::FunctionSig* sig, - Handle callable) { + Handle callable, + Handle suspender) { DCHECK_LE(sig->all().size(), kMaxInt); int sig_size = static_cast(sig->all().size()); int return_count = static_cast(sig->return_count()); @@ -2061,7 +2063,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); + wrapper_code, rtt, suspender); if (wasm::WasmFeatures::FromIsolate(isolate).has_typed_funcref()) { using CK = compiler::WasmImportCallKind; @@ -2108,6 +2110,12 @@ JSReceiver WasmJSFunction::GetCallable() const { .callable()); } +HeapObject WasmJSFunction::GetSuspender() const { + return WasmApiFunctionRef::cast( + shared().wasm_js_function_data().internal().ref()) + .suspender(); +} + const wasm::FunctionSig* WasmJSFunction::GetSignature(Zone* zone) { WasmJSFunctionData function_data = shared().wasm_js_function_data(); int sig_size = function_data.serialized_signature().length(); diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h index 74af9f1753..5076ae0fe9 100644 --- a/src/wasm/wasm-objects.h +++ b/src/wasm/wasm-objects.h @@ -91,7 +91,8 @@ class ImportedFunctionEntry { // Initialize this entry as a Wasm to JS call. This accepts the isolate as a // parameter, since it must allocate a tuple. V8_EXPORT_PRIVATE void SetWasmToJs(Isolate*, Handle callable, - const wasm::WasmCode* wasm_to_js_wrapper); + const wasm::WasmCode* wasm_to_js_wrapper, + Handle suspender); // Initialize this entry as a Wasm to Wasm call. void SetWasmToWasm(WasmInstanceObject target_instance, Address call_target); @@ -622,9 +623,11 @@ class WasmJSFunction : public JSFunction { static Handle New(Isolate* isolate, const wasm::FunctionSig* sig, - Handle callable); + Handle callable, + Handle suspender); JSReceiver GetCallable() const; + HeapObject GetSuspender() const; // Deserializes the signature of this function using the provided zone. Note // that lifetime of the signature is hence directly coupled to the zone. const wasm::FunctionSig* GetSignature(Zone* zone); diff --git a/src/wasm/wasm-objects.tq b/src/wasm/wasm-objects.tq index c3837b5510..4b4998165a 100644 --- a/src/wasm/wasm-objects.tq +++ b/src/wasm/wasm-objects.tq @@ -21,6 +21,7 @@ extern class WasmApiFunctionRef extends HeapObject { isolate_root: RawPtr; native_context: NativeContext; callable: JSReceiver|Undefined; + suspender: WasmSuspenderObject|Undefined; } // This is the representation that is used internally by wasm to represent diff --git a/test/cctest/wasm/wasm-run-utils.cc b/test/cctest/wasm/wasm-run-utils.cc index 7b17a4d88a..e638d37a58 100644 --- a/test/cctest/wasm/wasm-run-utils.cc +++ b/test/cctest/wasm/wasm-run-utils.cc @@ -73,8 +73,8 @@ TestingModuleBuilder::TestingModuleBuilder( auto resolved = compiler::ResolveWasmImportCall( maybe_import->js_function, maybe_import->sig, instance_object_->module(), enabled_features_); - compiler::WasmImportCallKind kind = resolved.first; - Handle callable = resolved.second; + compiler::WasmImportCallKind kind = resolved.kind; + Handle callable = resolved.callable; WasmImportWrapperCache::ModificationScope cache_scope( native_module_->import_wrapper_cache()); WasmImportWrapperCache::CacheKey key( @@ -89,7 +89,7 @@ TestingModuleBuilder::TestingModuleBuilder( } ImportedFunctionEntry(instance_object_, maybe_import_index) - .SetWasmToJs(isolate_, callable, import_wrapper); + .SetWasmToJs(isolate_, callable, import_wrapper, resolved.suspender); } if (tier == TestExecutionTier::kInterpreter) { diff --git a/test/mjsunit/wasm/stack-switching.js b/test/mjsunit/wasm/stack-switching.js index 045c016bae..ece02af1a7 100644 --- a/test/mjsunit/wasm/stack-switching.js +++ b/test/mjsunit/wasm/stack-switching.js @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Flags: --allow-natives-syntax --experimental-wasm-stack-switching --expose-gc +// Flags: --allow-natives-syntax --experimental-wasm-stack-switching +// Flags: --experimental-wasm-type-reflection --expose-gc load("test/mjsunit/wasm/wasm-module-builder.js"); @@ -27,6 +28,31 @@ load("test/mjsunit/wasm/wasm-module-builder.js"); assertEquals(42, instance.exports.g.value); })(); +(function TestStackSwitchSuspend() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + builder.addGlobal(kWasmI32, true).exportAs('g'); + import_index = builder.addImport('m', 'import', kSig_i_v); + builder.addFunction("test", kSig_v_v) + .addBody([ + kExprCallFunction, import_index, // suspend + kExprGlobalSet, 0 // resume + ]).exportFunc(); + let suspender = new WebAssembly.Suspender(); + function js_import() { + // TODO(thibaudm): Return the value as a promise. + return 42; + }; + let wasm_js_import = new WebAssembly.Function({parameters: [], results: ['i32']}, js_import); + let suspending_wasm_js_import = suspender.suspendOnReturnedPromise(wasm_js_import); + let instance = builder.instantiate({m: {import: suspending_wasm_js_import}}); + let wrapped_export = suspender.returnPromiseOnSuspend(instance.exports.test); + let combined_promise = wrapped_export(); + // TODO(thibaudm): Once we generate the actual wrapper, we expect 0 here + // The global will only be set after the promise resolves. + assertEquals(42, instance.exports.g.value); +})(); + (function TestStackSwitchGC() { print(arguments.callee.name); let builder = new WasmModuleBuilder();