[wasm] Add Suspender.suspendOnReturnedPromise

- Add Suspender.suspendOnReturnedPromise method
- Extend the WasmApiFunctionRef data with the suspender
- Detect wrapped WasmJSFunctions when we resolve the import

For now the generated wrapper is still a regular wasm-to-js wrapper, but
this sets the ground for generating specific wrappers for functions
wrapped by suspendOnReturnedPromise, and to access the suspender from
the wrapper code.

R=ahaas@chromium.org
CC=​fgm@chromium.org

Bug: v8:12191
Change-Id: I81cbec6b023507e47e6e1463b5f9b912f807da6a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3345000
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78560}
This commit is contained in:
Thibaud Michaud 2022-01-05 17:22:05 +01:00 committed by V8 LUCI CQ
parent d82b4b6699
commit 5c829be17c
12 changed files with 142 additions and 50 deletions

View File

@ -7579,46 +7579,48 @@ std::unique_ptr<OptimizedCompilationJob> NewJSToWasmCompilationJob(
std::move(debug_name), WasmAssemblerOptions());
}
std::pair<WasmImportCallKind, Handle<JSReceiver>> ResolveWasmImportCall(
WasmImportData ResolveWasmImportCall(
Handle<JSReceiver> callable, const wasm::FunctionSig* expected_sig,
const wasm::WasmModule* module,
const wasm::WasmFeatures& enabled_features) {
Isolate* isolate = callable->GetIsolate();
Handle<HeapObject> no_suspender;
if (WasmExportedFunction::IsWasmExportedFunction(*callable)) {
auto imported_function = Handle<WasmExportedFunction>::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<uint32_t>(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<WasmInstanceObject> instance(imported_function->instance(), isolate);
ImportedFunctionEntry entry(instance, func_index);
callable = handle(entry.callable(), isolate);
}
Handle<HeapObject> suspender = isolate->factory()->undefined_value();
if (WasmJSFunction::IsWasmJSFunction(*callable)) {
auto js_function = Handle<WasmJSFunction>::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<WasmCapiFunction>::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<WasmImportCallKind, Handle<JSReceiver>> 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<WasmImportCallKind, Handle<JSReceiver>> 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<WasmImportCallKind, Handle<JSReceiver>> 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 {

View File

@ -107,14 +107,19 @@ enum class WasmImportCallKind : uint8_t {
constexpr WasmImportCallKind kDefaultImportCallKind =
WasmImportCallKind::kJSFunctionArityMatch;
struct WasmImportData {
WasmImportCallKind kind;
Handle<JSReceiver> callable;
Handle<HeapObject> 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<WasmImportCallKind, Handle<JSReceiver>>
ResolveWasmImportCall(Handle<JSReceiver> 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<JSReceiver> 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(

View File

@ -1946,6 +1946,7 @@ void WasmApiFunctionRef::WasmApiFunctionRefPrint(std::ostream& os) {
os << "\n - isolate_root: " << reinterpret_cast<void*>(isolate_root());
os << "\n - native_context: " << Brief(native_context());
os << "\n - callable: " << Brief(callable());
os << "\n - suspender: " << Brief(suspender());
os << "\n";
}

View File

@ -1523,7 +1523,7 @@ Handle<WasmTypeInfo> Factory::NewWasmTypeInfo(
}
Handle<WasmApiFunctionRef> Factory::NewWasmApiFunctionRef(
Handle<JSReceiver> callable) {
Handle<JSReceiver> callable, Handle<HeapObject> suspender) {
Map map = *wasm_api_function_ref_map();
auto result = WasmApiFunctionRef::cast(AllocateRawWithImmortalMap(
map.instance_size(), AllocationType::kOld, map));
@ -1535,6 +1535,11 @@ Handle<WasmApiFunctionRef> 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<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) {
Handle<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(callable);
Handle<CodeT> wrapper_code, Handle<Map> rtt, Handle<HeapObject> suspender) {
Handle<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(callable, suspender);
Handle<WasmInternalFunction> internal =
NewWasmInternalFunction(opt_call_target, ref, rtt);
Map map = *wasm_js_function_data_map();
@ -1602,7 +1607,8 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData(
Address call_target, Handle<Foreign> embedder_data,
Handle<CodeT> wrapper_code, Handle<Map> rtt,
Handle<PodArray<wasm::ValueType>> serialized_sig) {
Handle<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(Handle<JSReceiver>());
Handle<WasmApiFunctionRef> ref =
NewWasmApiFunctionRef(Handle<JSReceiver>(), Handle<HeapObject>());
Handle<WasmInternalFunction> internal =
NewWasmInternalFunction(call_target, ref, rtt);
Map map = *wasm_capi_function_data_map();

View File

@ -604,13 +604,15 @@ 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);
Handle<WasmApiFunctionRef> NewWasmApiFunctionRef(Handle<JSReceiver> callable);
Handle<WasmApiFunctionRef> NewWasmApiFunctionRef(
Handle<JSReceiver> callable, Handle<HeapObject> suspender);
// {opt_call_target} is kNullAddress for JavaScript functions, and
// non-null for exported Wasm functions.
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);
Handle<CodeT> wrapper_code, Handle<Map> rtt,
Handle<HeapObject> suspender);
Handle<WasmStruct> NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args, Handle<Map> map);
Handle<WasmArray> NewWasmArray(const wasm::ArrayType* type,

View File

@ -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<int>(sig->parameter_count());
if (resolved.first ==
if (resolved.kind ==
compiler::WasmImportCallKind::kJSFunctionArityMismatch) {
Handle<JSFunction> function = Handle<JSFunction>::cast(resolved.second);
Handle<JSFunction> function = Handle<JSFunction>::cast(resolved.callable);
SharedFunctionInfo shared = function->shared();
expected_arity =
shared.internal_formal_parameter_count_without_receiver();

View File

@ -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<v8::Value>& args) {
return;
}
i::Handle<i::HeapObject> suspender =
handle(i::ReadOnlyRoots(i_isolate).undefined_value(), i_isolate);
i::Handle<i::JSFunction> 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<v8::Value>& 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<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.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<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);
auto callable = handle(
i::Handle<i::WasmJSFunction>::cast(arg0)->GetCallable(), i_isolate);
EXTRACT_THIS(suspender, WasmSuspenderObject);
i::Handle<i::JSFunction> 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

View File

@ -1076,7 +1076,7 @@ FunctionTargetAndRef::FunctionTargetAndRef(
void ImportedFunctionEntry::SetWasmToJs(
Isolate* isolate, Handle<JSReceiver> callable,
const wasm::WasmCode* wasm_to_js_wrapper) {
const wasm::WasmCode* wasm_to_js_wrapper, Handle<HeapObject> 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<WasmApiFunctionRef> 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<HeapObject> suspender = handle(js_function->GetSuspender(), isolate);
Handle<WasmApiFunctionRef> 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> WasmJSFunction::New(Isolate* isolate,
const wasm::FunctionSig* sig,
Handle<JSReceiver> callable) {
Handle<JSReceiver> callable,
Handle<HeapObject> suspender) {
DCHECK_LE(sig->all().size(), kMaxInt);
int sig_size = static_cast<int>(sig->all().size());
int return_count = static_cast<int>(sig->return_count());
@ -2061,7 +2063,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);
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();

View File

@ -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<JSReceiver> callable,
const wasm::WasmCode* wasm_to_js_wrapper);
const wasm::WasmCode* wasm_to_js_wrapper,
Handle<HeapObject> 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<WasmJSFunction> New(Isolate* isolate,
const wasm::FunctionSig* sig,
Handle<JSReceiver> callable);
Handle<JSReceiver> callable,
Handle<HeapObject> 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);

View File

@ -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

View File

@ -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<JSReceiver> callable = resolved.second;
compiler::WasmImportCallKind kind = resolved.kind;
Handle<JSReceiver> 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) {

View File

@ -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();