[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::move(debug_name), WasmAssemblerOptions());
} }
std::pair<WasmImportCallKind, Handle<JSReceiver>> ResolveWasmImportCall( WasmImportData ResolveWasmImportCall(
Handle<JSReceiver> callable, const wasm::FunctionSig* expected_sig, Handle<JSReceiver> callable, const wasm::FunctionSig* expected_sig,
const wasm::WasmModule* module, const wasm::WasmModule* module,
const wasm::WasmFeatures& enabled_features) { const wasm::WasmFeatures& enabled_features) {
Isolate* isolate = callable->GetIsolate();
Handle<HeapObject> no_suspender;
if (WasmExportedFunction::IsWasmExportedFunction(*callable)) { if (WasmExportedFunction::IsWasmExportedFunction(*callable)) {
auto imported_function = Handle<WasmExportedFunction>::cast(callable); auto imported_function = Handle<WasmExportedFunction>::cast(callable);
if (!imported_function->MatchesSignature(module, expected_sig)) { if (!imported_function->MatchesSignature(module, expected_sig)) {
return std::make_pair(WasmImportCallKind::kLinkError, callable); return {WasmImportCallKind::kLinkError, callable, no_suspender};
} }
uint32_t func_index = uint32_t func_index =
static_cast<uint32_t>(imported_function->function_index()); static_cast<uint32_t>(imported_function->function_index());
if (func_index >= if (func_index >=
imported_function->instance().module()->num_imported_functions) { 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. // Resolve the shortcut to the underlying callable and continue.
Handle<WasmInstanceObject> instance(imported_function->instance(), isolate); Handle<WasmInstanceObject> instance(imported_function->instance(), isolate);
ImportedFunctionEntry entry(instance, func_index); ImportedFunctionEntry entry(instance, func_index);
callable = handle(entry.callable(), isolate); callable = handle(entry.callable(), isolate);
} }
Handle<HeapObject> suspender = isolate->factory()->undefined_value();
if (WasmJSFunction::IsWasmJSFunction(*callable)) { if (WasmJSFunction::IsWasmJSFunction(*callable)) {
auto js_function = Handle<WasmJSFunction>::cast(callable); auto js_function = Handle<WasmJSFunction>::cast(callable);
if (!js_function->MatchesSignature(expected_sig)) { 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. // Resolve the short-cut to the underlying callable and continue.
callable = handle(js_function->GetCallable(), isolate); callable = handle(js_function->GetCallable(), isolate);
} }
if (WasmCapiFunction::IsWasmCapiFunction(*callable)) { if (WasmCapiFunction::IsWasmCapiFunction(*callable)) {
auto capi_function = Handle<WasmCapiFunction>::cast(callable); auto capi_function = Handle<WasmCapiFunction>::cast(callable);
if (!capi_function->MatchesSignature(expected_sig)) { 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. // Assuming we are calling to JS, check whether this would be a runtime error.
if (!wasm::IsJSCompatibleSignature(expected_sig, module, enabled_features)) { 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. // For JavaScript calls, determine whether the target has an arity match.
if (callable->IsJSFunction()) { if (callable->IsJSFunction()) {
@ -7634,7 +7636,7 @@ std::pair<WasmImportCallKind, Handle<JSReceiver>> ResolveWasmImportCall(
if (!sig) sig = wasm::WasmOpcodes::AsmjsSignature(wasm::kExpr##name); \ if (!sig) sig = wasm::WasmOpcodes::AsmjsSignature(wasm::kExpr##name); \
DCHECK_NOT_NULL(sig); \ DCHECK_NOT_NULL(sig); \
if (*expected_sig == *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) \ #define COMPARE_SIG_FOR_BUILTIN_F64(name) \
@ -7679,13 +7681,12 @@ std::pair<WasmImportCallKind, Handle<JSReceiver>> ResolveWasmImportCall(
if (IsClassConstructor(shared->kind())) { if (IsClassConstructor(shared->kind())) {
// Class constructor will throw anyway. // 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() == if (shared->internal_formal_parameter_count_without_receiver() ==
expected_sig->parameter_count()) { expected_sig->parameter_count()) {
return std::make_pair(WasmImportCallKind::kJSFunctionArityMatch, return {WasmImportCallKind::kJSFunctionArityMatch, callable, suspender};
callable);
} }
// If function isn't compiled, compile it now. // If function isn't compiled, compile it now.
@ -7696,11 +7697,10 @@ std::pair<WasmImportCallKind, Handle<JSReceiver>> ResolveWasmImportCall(
&is_compiled_scope); &is_compiled_scope);
} }
return std::make_pair(WasmImportCallKind::kJSFunctionArityMismatch, return {WasmImportCallKind::kJSFunctionArityMismatch, callable, suspender};
callable);
} }
// Unknown case. Use the call builtin. // Unknown case. Use the call builtin.
return std::make_pair(WasmImportCallKind::kUseCallBuiltin, callable); return {WasmImportCallKind::kUseCallBuiltin, callable, suspender};
} }
namespace { namespace {

View File

@ -107,14 +107,19 @@ enum class WasmImportCallKind : uint8_t {
constexpr WasmImportCallKind kDefaultImportCallKind = constexpr WasmImportCallKind kDefaultImportCallKind =
WasmImportCallKind::kJSFunctionArityMatch; WasmImportCallKind::kJSFunctionArityMatch;
struct WasmImportData {
WasmImportCallKind kind;
Handle<JSReceiver> callable;
Handle<HeapObject> suspender;
};
// Resolves which import call wrapper is required for the given JS callable. // 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 // Returns the kind of wrapper needed, the ultimate target callable, and the
// some callables (e.g. a {WasmExportedFunction} or {WasmJSFunction}) just wrap // suspender object if applicable. Note that some callables (e.g. a
// another target, which is why the ultimate target is returned as well. // {WasmExportedFunction} or {WasmJSFunction}) just wrap another target, which
V8_EXPORT_PRIVATE std::pair<WasmImportCallKind, Handle<JSReceiver>> // is why the ultimate target is returned as well.
ResolveWasmImportCall(Handle<JSReceiver> callable, const wasm::FunctionSig* sig, V8_EXPORT_PRIVATE WasmImportData ResolveWasmImportCall(
const wasm::WasmModule* module, Handle<JSReceiver> callable, const wasm::FunctionSig* sig,
const wasm::WasmFeatures& enabled_features); const wasm::WasmModule* module, const wasm::WasmFeatures& enabled_features);
// Compiles an import call wrapper, which allows Wasm to call imports. // Compiles an import call wrapper, which allows Wasm to call imports.
V8_EXPORT_PRIVATE wasm::WasmCompilationResult CompileWasmImportCallWrapper( 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 - isolate_root: " << reinterpret_cast<void*>(isolate_root());
os << "\n - native_context: " << Brief(native_context()); os << "\n - native_context: " << Brief(native_context());
os << "\n - callable: " << Brief(callable()); os << "\n - callable: " << Brief(callable());
os << "\n - suspender: " << Brief(suspender());
os << "\n"; os << "\n";
} }

View File

@ -1523,7 +1523,7 @@ Handle<WasmTypeInfo> Factory::NewWasmTypeInfo(
} }
Handle<WasmApiFunctionRef> Factory::NewWasmApiFunctionRef( Handle<WasmApiFunctionRef> Factory::NewWasmApiFunctionRef(
Handle<JSReceiver> callable) { Handle<JSReceiver> callable, Handle<HeapObject> suspender) {
Map map = *wasm_api_function_ref_map(); Map map = *wasm_api_function_ref_map();
auto result = WasmApiFunctionRef::cast(AllocateRawWithImmortalMap( auto result = WasmApiFunctionRef::cast(AllocateRawWithImmortalMap(
map.instance_size(), AllocationType::kOld, map)); map.instance_size(), AllocationType::kOld, map));
@ -1535,6 +1535,11 @@ Handle<WasmApiFunctionRef> Factory::NewWasmApiFunctionRef(
} else { } else {
result.set_callable(*undefined_value()); result.set_callable(*undefined_value());
} }
if (!suspender.is_null()) {
result.set_suspender(*suspender);
} else {
result.set_suspender(*undefined_value());
}
return handle(result, isolate()); return handle(result, isolate());
} }
@ -1556,8 +1561,8 @@ Handle<WasmInternalFunction> Factory::NewWasmInternalFunction(
Handle<WasmJSFunctionData> Factory::NewWasmJSFunctionData( Handle<WasmJSFunctionData> Factory::NewWasmJSFunctionData(
Address opt_call_target, Handle<JSReceiver> callable, int return_count, Address opt_call_target, Handle<JSReceiver> callable, int return_count,
int parameter_count, Handle<PodArray<wasm::ValueType>> serialized_sig, 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<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(callable); Handle<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(callable, suspender);
Handle<WasmInternalFunction> internal = Handle<WasmInternalFunction> internal =
NewWasmInternalFunction(opt_call_target, ref, rtt); NewWasmInternalFunction(opt_call_target, ref, rtt);
Map map = *wasm_js_function_data_map(); Map map = *wasm_js_function_data_map();
@ -1602,7 +1607,8 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData(
Address call_target, Handle<Foreign> embedder_data, Address call_target, Handle<Foreign> embedder_data,
Handle<CodeT> wrapper_code, Handle<Map> rtt, Handle<CodeT> wrapper_code, Handle<Map> rtt,
Handle<PodArray<wasm::ValueType>> serialized_sig) { Handle<PodArray<wasm::ValueType>> serialized_sig) {
Handle<WasmApiFunctionRef> ref = NewWasmApiFunctionRef(Handle<JSReceiver>()); Handle<WasmApiFunctionRef> ref =
NewWasmApiFunctionRef(Handle<JSReceiver>(), Handle<HeapObject>());
Handle<WasmInternalFunction> internal = Handle<WasmInternalFunction> internal =
NewWasmInternalFunction(call_target, ref, rtt); NewWasmInternalFunction(call_target, ref, rtt);
Map map = *wasm_capi_function_data_map(); 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, Handle<CodeT> export_wrapper, Handle<WasmInstanceObject> instance,
Address call_target, Handle<Object> ref, int func_index, Address call_target, Handle<Object> ref, int func_index,
Address sig_address, int wrapper_budget, Handle<Map> rtt); 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 // {opt_call_target} is kNullAddress for JavaScript functions, and
// non-null for exported Wasm functions. // non-null for exported Wasm functions.
Handle<WasmJSFunctionData> NewWasmJSFunctionData( Handle<WasmJSFunctionData> NewWasmJSFunctionData(
Address opt_call_target, Handle<JSReceiver> callable, int return_count, Address opt_call_target, Handle<JSReceiver> callable, int return_count,
int parameter_count, Handle<PodArray<wasm::ValueType>> serialized_sig, 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, Handle<WasmStruct> NewWasmStruct(const wasm::StructType* type,
wasm::WasmValue* args, Handle<Map> map); wasm::WasmValue* args, Handle<Map> map);
Handle<WasmArray> NewWasmArray(const wasm::ArrayType* type, 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; const FunctionSig* expected_sig = module_->functions[func_index].sig;
auto resolved = compiler::ResolveWasmImportCall(js_receiver, expected_sig, auto resolved = compiler::ResolveWasmImportCall(js_receiver, expected_sig,
module_, enabled_); module_, enabled_);
compiler::WasmImportCallKind kind = resolved.first; compiler::WasmImportCallKind kind = resolved.kind;
js_receiver = resolved.second; js_receiver = resolved.callable;
switch (kind) { switch (kind) {
case compiler::WasmImportCallKind::kLinkError: case compiler::WasmImportCallKind::kLinkError:
ReportLinkError("imported function does not match the expected type", ReportLinkError("imported function does not match the expected type",
@ -1224,7 +1224,8 @@ bool InstanceBuilder::ProcessImportedFunction(
ImportedFunctionEntry entry(instance, func_index); ImportedFunctionEntry entry(instance, func_index);
// We re-use the SetWasmToJs infrastructure because it passes the // We re-use the SetWasmToJs infrastructure because it passes the
// callable to the wrapper, which we need to get the function data. // 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; break;
} }
default: { default: {
@ -1245,7 +1246,7 @@ bool InstanceBuilder::ProcessImportedFunction(
ImportedFunctionEntry entry(instance, func_index); ImportedFunctionEntry entry(instance, func_index);
if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) { if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) {
// Wasm to JS wrappers are treated specially in the import table. // 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 { } else {
// Wasm math intrinsics are compiled as regular Wasm functions. // Wasm math intrinsics are compiled as regular Wasm functions.
DCHECK(kind >= compiler::WasmImportCallKind::kFirstMathIntrinsic && DCHECK(kind >= compiler::WasmImportCallKind::kFirstMathIntrinsic &&
@ -1634,7 +1635,7 @@ void InstanceBuilder::CompileImportWrappers(
const FunctionSig* sig = module_->functions[func_index].sig; const FunctionSig* sig = module_->functions[func_index].sig;
auto resolved = auto resolved =
compiler::ResolveWasmImportCall(js_receiver, sig, module_, enabled_); compiler::ResolveWasmImportCall(js_receiver, sig, module_, enabled_);
compiler::WasmImportCallKind kind = resolved.first; compiler::WasmImportCallKind kind = resolved.kind;
if (kind == compiler::WasmImportCallKind::kWasmToWasm || if (kind == compiler::WasmImportCallKind::kWasmToWasm ||
kind == compiler::WasmImportCallKind::kLinkError || kind == compiler::WasmImportCallKind::kLinkError ||
kind == compiler::WasmImportCallKind::kWasmToCapi) { kind == compiler::WasmImportCallKind::kWasmToCapi) {
@ -1642,9 +1643,9 @@ void InstanceBuilder::CompileImportWrappers(
} }
int expected_arity = static_cast<int>(sig->parameter_count()); int expected_arity = static_cast<int>(sig->parameter_count());
if (resolved.first == if (resolved.kind ==
compiler::WasmImportCallKind::kJSFunctionArityMismatch) { compiler::WasmImportCallKind::kJSFunctionArityMismatch) {
Handle<JSFunction> function = Handle<JSFunction>::cast(resolved.second); Handle<JSFunction> function = Handle<JSFunction>::cast(resolved.callable);
SharedFunctionInfo shared = function->shared(); SharedFunctionInfo shared = function->shared();
expected_arity = expected_arity =
shared.internal_formal_parameter_count_without_receiver(); shared.internal_formal_parameter_count_without_receiver();

View File

@ -25,9 +25,11 @@
#include "src/init/v8.h" #include "src/init/v8.h"
#include "src/objects/fixed-array.h" #include "src/objects/fixed-array.h"
#include "src/objects/instance-type.h" #include "src/objects/instance-type.h"
#include "src/objects/js-function.h"
#include "src/objects/js-promise-inl.h" #include "src/objects/js-promise-inl.h"
#include "src/objects/managed-inl.h" #include "src/objects/managed-inl.h"
#include "src/objects/objects-inl.h" #include "src/objects/objects-inl.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/templates.h" #include "src/objects/templates.h"
#include "src/parsing/parse-info.h" #include "src/parsing/parse-info.h"
#include "src/tasks/task-utils.h" #include "src/tasks/task-utils.h"
@ -1894,8 +1896,10 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
return; return;
} }
i::Handle<i::HeapObject> suspender =
handle(i::ReadOnlyRoots(i_isolate).undefined_value(), i_isolate);
i::Handle<i::JSFunction> result = 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)); 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_WasmGlobalObject = "WebAssembly.Global";
constexpr const char* kName_WasmMemoryObject = "WebAssembly.Memory"; constexpr const char* kName_WasmMemoryObject = "WebAssembly.Memory";
constexpr const char* kName_WasmInstanceObject = "WebAssembly.Instance"; 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_WasmTableObject = "WebAssembly.Table";
constexpr const char* kName_WasmTagObject = "WebAssembly.Tag"; constexpr const char* kName_WasmTagObject = "WebAssembly.Tag";
constexpr const char* kName_WasmExceptionPackage = "WebAssembly.Exception"; constexpr const char* kName_WasmExceptionPackage = "WebAssembly.Exception";
@ -2580,6 +2585,38 @@ void WebAssemblySuspenderReturnPromiseOnSuspend(
args.GetReturnValue().Set(Utils::ToLocal(result)); 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 } // namespace
// TODO(titzer): we use the API to create the function template because the // 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"); WasmSuspenderObject::kHeaderSize, "WebAssembly.Suspender");
InstallFunc(isolate, suspender_proto, "returnPromiseOnSuspend", InstallFunc(isolate, suspender_proto, "returnPromiseOnSuspend",
WebAssemblySuspenderReturnPromiseOnSuspend, 1); WebAssemblySuspenderReturnPromiseOnSuspend, 1);
InstallFunc(isolate, suspender_proto, "suspendOnReturnedPromise",
WebAssemblySuspenderSuspendOnReturnedPromise, 1);
} }
// Setup Function // Setup Function

View File

@ -1076,7 +1076,7 @@ FunctionTargetAndRef::FunctionTargetAndRef(
void ImportedFunctionEntry::SetWasmToJs( void ImportedFunctionEntry::SetWasmToJs(
Isolate* isolate, Handle<JSReceiver> callable, 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 TRACE_IFT("Import callable 0x%" PRIxPTR "[%d] = {callable=0x%" PRIxPTR
", target=%p}\n", ", target=%p}\n",
instance_->ptr(), index_, callable->ptr(), instance_->ptr(), index_, callable->ptr(),
@ -1084,7 +1084,7 @@ void ImportedFunctionEntry::SetWasmToJs(
DCHECK(wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToJsWrapper || DCHECK(wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToJsWrapper ||
wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToCapiWrapper); wasm_to_js_wrapper->kind() == wasm::WasmCode::kWasmToCapiWrapper);
Handle<WasmApiFunctionRef> ref = Handle<WasmApiFunctionRef> ref =
isolate->factory()->NewWasmApiFunctionRef(callable); isolate->factory()->NewWasmApiFunctionRef(callable, suspender);
instance_->imported_function_refs().set(index_, *ref); instance_->imported_function_refs().set(index_, *ref);
instance_->imported_function_targets()[index_] = instance_->imported_function_targets()[index_] =
wasm_to_js_wrapper->instruction_start(); wasm_to_js_wrapper->instruction_start();
@ -1448,8 +1448,8 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
const wasm::WasmFeatures enabled = native_module->enabled_features(); const wasm::WasmFeatures enabled = native_module->enabled_features();
auto resolved = compiler::ResolveWasmImportCall( auto resolved = compiler::ResolveWasmImportCall(
callable, sig, instance->module(), enabled); callable, sig, instance->module(), enabled);
compiler::WasmImportCallKind kind = resolved.first; compiler::WasmImportCallKind kind = resolved.kind;
callable = resolved.second; // Update to ultimate target. callable = resolved.callable; // Update to ultimate target.
DCHECK_NE(compiler::WasmImportCallKind::kLinkError, kind); DCHECK_NE(compiler::WasmImportCallKind::kLinkError, kind);
wasm::CompilationEnv env = native_module->CreateCompilationEnv(); wasm::CompilationEnv env = native_module->CreateCompilationEnv();
// {expected_arity} should only be used if kind != kJSFunctionArityMismatch. // {expected_arity} should only be used if kind != kJSFunctionArityMismatch.
@ -1479,8 +1479,9 @@ void WasmInstanceObject::ImportWasmJSFunctionIntoTable(
} }
// Update the dispatch table. // Update the dispatch table.
Handle<HeapObject> suspender = handle(js_function->GetSuspender(), isolate);
Handle<WasmApiFunctionRef> ref = Handle<WasmApiFunctionRef> ref =
isolate->factory()->NewWasmApiFunctionRef(callable); isolate->factory()->NewWasmApiFunctionRef(callable, suspender);
WasmIndirectFunctionTable::cast( WasmIndirectFunctionTable::cast(
instance->indirect_function_tables().get(table_index)) instance->indirect_function_tables().get(table_index))
.Set(entry_index, sig_id, call_target, *ref); .Set(entry_index, sig_id, call_target, *ref);
@ -2031,7 +2032,8 @@ bool WasmJSFunction::IsWasmJSFunction(Object object) {
Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate, Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate,
const wasm::FunctionSig* sig, const wasm::FunctionSig* sig,
Handle<JSReceiver> callable) { Handle<JSReceiver> callable,
Handle<HeapObject> suspender) {
DCHECK_LE(sig->all().size(), kMaxInt); DCHECK_LE(sig->all().size(), kMaxInt);
int sig_size = static_cast<int>(sig->all().size()); int sig_size = static_cast<int>(sig->all().size());
int return_count = static_cast<int>(sig->return_count()); 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<Map> rtt = factory->wasm_internal_function_map();
Handle<WasmJSFunctionData> function_data = factory->NewWasmJSFunctionData( Handle<WasmJSFunctionData> function_data = factory->NewWasmJSFunctionData(
call_target, callable, return_count, parameter_count, serialized_sig, call_target, callable, return_count, parameter_count, serialized_sig,
wrapper_code, rtt); wrapper_code, rtt, suspender);
if (wasm::WasmFeatures::FromIsolate(isolate).has_typed_funcref()) { if (wasm::WasmFeatures::FromIsolate(isolate).has_typed_funcref()) {
using CK = compiler::WasmImportCallKind; using CK = compiler::WasmImportCallKind;
@ -2108,6 +2110,12 @@ JSReceiver WasmJSFunction::GetCallable() const {
.callable()); .callable());
} }
HeapObject WasmJSFunction::GetSuspender() const {
return WasmApiFunctionRef::cast(
shared().wasm_js_function_data().internal().ref())
.suspender();
}
const wasm::FunctionSig* WasmJSFunction::GetSignature(Zone* zone) { const wasm::FunctionSig* WasmJSFunction::GetSignature(Zone* zone) {
WasmJSFunctionData function_data = shared().wasm_js_function_data(); WasmJSFunctionData function_data = shared().wasm_js_function_data();
int sig_size = function_data.serialized_signature().length(); 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 // Initialize this entry as a Wasm to JS call. This accepts the isolate as a
// parameter, since it must allocate a tuple. // parameter, since it must allocate a tuple.
V8_EXPORT_PRIVATE void SetWasmToJs(Isolate*, Handle<JSReceiver> callable, 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. // Initialize this entry as a Wasm to Wasm call.
void SetWasmToWasm(WasmInstanceObject target_instance, Address call_target); void SetWasmToWasm(WasmInstanceObject target_instance, Address call_target);
@ -622,9 +623,11 @@ class WasmJSFunction : public JSFunction {
static Handle<WasmJSFunction> New(Isolate* isolate, static Handle<WasmJSFunction> New(Isolate* isolate,
const wasm::FunctionSig* sig, const wasm::FunctionSig* sig,
Handle<JSReceiver> callable); Handle<JSReceiver> callable,
Handle<HeapObject> suspender);
JSReceiver GetCallable() const; JSReceiver GetCallable() const;
HeapObject GetSuspender() const;
// Deserializes the signature of this function using the provided zone. Note // Deserializes the signature of this function using the provided zone. Note
// that lifetime of the signature is hence directly coupled to the zone. // that lifetime of the signature is hence directly coupled to the zone.
const wasm::FunctionSig* GetSignature(Zone* zone); const wasm::FunctionSig* GetSignature(Zone* zone);

View File

@ -21,6 +21,7 @@ extern class WasmApiFunctionRef extends HeapObject {
isolate_root: RawPtr; isolate_root: RawPtr;
native_context: NativeContext; native_context: NativeContext;
callable: JSReceiver|Undefined; callable: JSReceiver|Undefined;
suspender: WasmSuspenderObject|Undefined;
} }
// This is the representation that is used internally by wasm to represent // This is the representation that is used internally by wasm to represent

View File

@ -73,8 +73,8 @@ TestingModuleBuilder::TestingModuleBuilder(
auto resolved = compiler::ResolveWasmImportCall( auto resolved = compiler::ResolveWasmImportCall(
maybe_import->js_function, maybe_import->sig, maybe_import->js_function, maybe_import->sig,
instance_object_->module(), enabled_features_); instance_object_->module(), enabled_features_);
compiler::WasmImportCallKind kind = resolved.first; compiler::WasmImportCallKind kind = resolved.kind;
Handle<JSReceiver> callable = resolved.second; Handle<JSReceiver> callable = resolved.callable;
WasmImportWrapperCache::ModificationScope cache_scope( WasmImportWrapperCache::ModificationScope cache_scope(
native_module_->import_wrapper_cache()); native_module_->import_wrapper_cache());
WasmImportWrapperCache::CacheKey key( WasmImportWrapperCache::CacheKey key(
@ -89,7 +89,7 @@ TestingModuleBuilder::TestingModuleBuilder(
} }
ImportedFunctionEntry(instance_object_, maybe_import_index) ImportedFunctionEntry(instance_object_, maybe_import_index)
.SetWasmToJs(isolate_, callable, import_wrapper); .SetWasmToJs(isolate_, callable, import_wrapper, resolved.suspender);
} }
if (tier == TestExecutionTier::kInterpreter) { 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 // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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"); 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); 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() { (function TestStackSwitchGC() {
print(arguments.callee.name); print(arguments.callee.name);
let builder = new WasmModuleBuilder(); let builder = new WasmModuleBuilder();