[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:
parent
d82b4b6699
commit
5c829be17c
@ -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 {
|
||||||
|
@ -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(
|
||||||
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user