Commit 29db5631 authored by Thibaud Michaud's avatar Thibaud Michaud Committed by V8 LUCI CQ

[wasm] Implement new JS Promise Integration API

Implement the WebAssembly.Function-based API.
With the old API, wrapping an import and export with JS Promise
Integration looked like:

  WebAssembly.returnPromiseOnSuspend(<wasm_export>);
  WebAssembly.suspendOnReturnedPromise(
    new WebAssembly.Function(<sig>, <js_import>));

With the new API:

  new WebAssembly.Function(<sig>, <wasm_export>, {promising: 'first'})
  new WebAssembly.Function(<sig>, <js_import>, {suspending: 'first'})

For details, see
https://github.com/WebAssembly/js-promise-integration/pull/8/files

R=ahaas@chromium.org

Bug: v8:12191
Change-Id: Iaefaac5304a038fc39283db165b637af7e48b009
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3804669Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82183}
parent f62fadc6
......@@ -7828,8 +7828,7 @@ WasmImportData ResolveWasmImportCall(
if (WasmJSFunction::IsWasmJSFunction(*callable)) {
auto js_function = Handle<WasmJSFunction>::cast(callable);
suspend = js_function->GetSuspend();
if ((suspend && !js_function->MatchesSignatureForSuspend(expected_sig)) ||
(!suspend && !js_function->MatchesSignature(expected_sig))) {
if (!js_function->MatchesSignature(expected_sig)) {
return {WasmImportCallKind::kLinkError, callable, wasm::kNoSuspend};
}
// Resolve the short-cut to the underlying callable and continue.
......
......@@ -2033,6 +2033,7 @@ void WasmInstanceObject::WasmInstanceObjectPrint(std::ostream& os) {
void WasmFunctionData::WasmFunctionDataPrint(std::ostream& os) {
os << "\n - internal: " << Brief(internal());
os << "\n - wrapper_code: " << Brief(TorqueGeneratedClass::wrapper_code());
os << "\n - js_promise_flags: " << js_promise_flags();
}
void WasmExportedFunctionData::WasmExportedFunctionDataPrint(std::ostream& os) {
......@@ -2042,7 +2043,6 @@ void WasmExportedFunctionData::WasmExportedFunctionDataPrint(std::ostream& os) {
os << "\n - function_index: " << function_index();
os << "\n - signature: " << Brief(signature());
os << "\n - wrapper_budget: " << wrapper_budget();
os << "\n - suspend: " << suspend();
os << "\n";
}
......
......@@ -1671,7 +1671,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, wasm::Suspend suspend) {
Handle<CodeT> wrapper_code, Handle<Map> rtt, wasm::Suspend suspend,
wasm::Promise promise) {
Handle<WasmApiFunctionRef> ref =
NewWasmApiFunctionRef(callable, suspend, Handle<WasmInstanceObject>());
Handle<WasmInternalFunction> internal =
......@@ -1686,7 +1687,8 @@ Handle<WasmJSFunctionData> Factory::NewWasmJSFunctionData(
result.set_serialized_return_count(return_count);
result.set_serialized_parameter_count(parameter_count);
result.set_serialized_signature(*serialized_sig);
result.set_suspend(suspend);
result.set_js_promise_flags(WasmFunctionData::SuspendField::encode(suspend) |
WasmFunctionData::PromiseField::encode(promise));
return handle(result, isolate());
}
......@@ -1705,7 +1707,7 @@ Handle<WasmExportedFunctionData> Factory::NewWasmExportedFunctionData(
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,
wasm::Suspend suspend) {
wasm::Promise promise) {
Handle<Foreign> sig_foreign = NewForeign(sig_address);
Handle<WasmInternalFunction> internal =
NewWasmInternalFunction(call_target, Handle<HeapObject>::cast(ref), rtt);
......@@ -1728,7 +1730,9 @@ Handle<WasmExportedFunctionData> Factory::NewWasmExportedFunctionData(
*BUILTIN_CODE(isolate(), Illegal),
V8_EXTERNAL_CODE_SPACE_BOOL ? UPDATE_WRITE_BARRIER : SKIP_WRITE_BARRIER);
result.set_packed_args_size(0);
result.set_suspend(suspend);
result.set_js_promise_flags(
WasmFunctionData::SuspendField::encode(wasm::kNoSuspend) |
WasmFunctionData::PromiseField::encode(promise));
return handle(result, isolate());
}
......@@ -1749,6 +1753,9 @@ Handle<WasmCapiFunctionData> Factory::NewWasmCapiFunctionData(
result.set_wrapper_code(*wrapper_code);
result.set_embedder_data(*embedder_data);
result.set_serialized_signature(*serialized_sig);
result.set_js_promise_flags(
WasmFunctionData::SuspendField::encode(wasm::kNoSuspend) |
WasmFunctionData::PromiseField::encode(wasm::kNoPromise));
return handle(result, isolate());
}
......
......@@ -84,6 +84,7 @@ struct WasmElemSegment;
class WasmValue;
enum class OnResume : int;
enum Suspend : uint8_t;
enum Promise : uint8_t;
} // namespace wasm
#endif
......@@ -643,7 +644,7 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<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,
wasm::Suspend suspend);
wasm::Promise promise);
Handle<WasmApiFunctionRef> NewWasmApiFunctionRef(
Handle<JSReceiver> callable, wasm::Suspend suspend,
Handle<WasmInstanceObject> instance);
......@@ -652,7 +653,8 @@ class V8_EXPORT_PRIVATE Factory : public FactoryBase<Factory> {
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, wasm::Suspend suspend);
Handle<CodeT> wrapper_code, Handle<Map> rtt, wasm::Suspend suspend,
wasm::Promise promise);
Handle<WasmResumeData> NewWasmResumeData(
Handle<WasmSuspenderObject> suspender, wasm::OnResume on_resume);
Handle<WasmStruct> NewWasmStruct(const wasm::StructType* type,
......
......@@ -743,8 +743,7 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
// function. Use CWasmEntry instead.
start_function_ = WasmExportedFunction::New(
isolate_, instance, start_index,
static_cast<int>(function.sig->parameter_count()), wrapper_code,
kNoSuspend);
static_cast<int>(function.sig->parameter_count()), wrapper_code);
if (function.imported) {
ImportedFunctionEntry entry(instance, module_->start_function_index);
......
This diff is collapsed.
......@@ -1431,8 +1431,7 @@ WasmInstanceObject::GetOrCreateWasmInternalFunction(
}
auto external = Handle<WasmExternalFunction>::cast(WasmExportedFunction::New(
isolate, instance, function_index,
static_cast<int>(function.sig->parameter_count()), wrapper,
wasm::kNoSuspend));
static_cast<int>(function.sig->parameter_count()), wrapper));
result =
WasmInternalFunction::FromExternal(external, isolate).ToHandleChecked();
......@@ -1972,7 +1971,7 @@ int WasmExportedFunction::function_index() {
Handle<WasmExportedFunction> WasmExportedFunction::New(
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index,
int arity, Handle<CodeT> export_wrapper, wasm::Suspend suspend) {
int arity, Handle<CodeT> export_wrapper) {
DCHECK(
CodeKind::JS_TO_WASM_FUNCTION == export_wrapper->kind() ||
(export_wrapper->is_builtin() &&
......@@ -1998,11 +1997,15 @@ Handle<WasmExportedFunction> WasmExportedFunction::New(
} else {
rtt = factory->wasm_internal_function_map();
}
wasm::Promise promise =
export_wrapper->builtin_id() == Builtin::kWasmReturnPromiseOnSuspend
? wasm::kPromise
: wasm::kNoPromise;
Handle<WasmExportedFunctionData> function_data =
factory->NewWasmExportedFunctionData(
export_wrapper, instance, call_target, ref, func_index,
reinterpret_cast<Address>(sig), wasm::kGenericWrapperBudget, rtt,
suspend);
promise);
MaybeHandle<String> maybe_name;
bool is_asm_js_module = instance->module_object().is_asm_js();
......@@ -2130,7 +2133,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, suspend);
wrapper_code, rtt, suspend, wasm::kNoPromise);
if (wasm::WasmFeatures::FromIsolate(isolate).has_typed_funcref()) {
using CK = compiler::WasmImportCallKind;
......@@ -2197,28 +2200,6 @@ const wasm::FunctionSig* WasmJSFunction::GetSignature(Zone* zone) {
return zone->New<wasm::FunctionSig>(return_count, parameter_count, types);
}
bool WasmJSFunction::MatchesSignatureForSuspend(const wasm::FunctionSig* sig) {
DCHECK_LE(sig->all().size(), kMaxInt);
int sig_size = static_cast<int>(sig->all().size());
int parameter_count = static_cast<int>(sig->parameter_count());
DisallowHeapAllocation no_alloc;
WasmJSFunctionData function_data = shared().wasm_js_function_data();
// The suspender parameter is not forwarded to the JS function so the
// parameter count should differ by one.
if (parameter_count != function_data.serialized_parameter_count() + 1) {
return false;
}
if (sig_size == 0) return true; // Prevent undefined behavior.
// This function is only called for functions wrapped by
// WebAssembly.suspendOnReturnedPromise, so the return type has to be
// externref.
CHECK_EQ(function_data.serialized_return_count(), 1);
CHECK_EQ(function_data.serialized_signature().get(0), wasm::kWasmExternRef);
const wasm::ValueType* expected = sig->parameters().begin() + 1;
return function_data.serialized_signature().matches(1, expected,
parameter_count - 1);
}
// TODO(9495): Update this if function type variance is introduced.
bool WasmJSFunction::MatchesSignature(const wasm::FunctionSig* sig) {
DCHECK_LE(sig->all().size(), kMaxInt);
......
......@@ -2,6 +2,7 @@
// this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/base/bit-field.h"
#if !V8_ENABLE_WEBASSEMBLY
#error This header should only be included if WebAssembly is enabled.
#endif // !V8_ENABLE_WEBASSEMBLY
......@@ -79,6 +80,7 @@ namespace wasm {
// https://github.com/llvm/llvm-project/issues/56560. See also
// crbug.com/1344641.
enum Suspend : uint8_t { kSuspend = 1, kNoSuspend = 0 };
enum Promise : uint8_t { kPromise = 1, kNoPromise = 0 };
enum class OnResume : int { kContinue, kThrow };
} // namespace wasm
......@@ -610,7 +612,7 @@ class WasmExportedFunction : public JSFunction {
V8_EXPORT_PRIVATE static Handle<WasmExportedFunction> New(
Isolate* isolate, Handle<WasmInstanceObject> instance, int func_index,
int arity, Handle<CodeT> export_wrapper, wasm::Suspend suspend);
int arity, Handle<CodeT> export_wrapper);
Address GetWasmCallTarget();
......@@ -644,8 +646,6 @@ class WasmJSFunction : public JSFunction {
// that lifetime of the signature is hence directly coupled to the zone.
const wasm::FunctionSig* GetSignature(Zone* zone);
bool MatchesSignature(const wasm::FunctionSig* sig);
// Special typing rule for imports wrapped by a Suspender.
bool MatchesSignatureForSuspend(const wasm::FunctionSig* sig);
DECL_CAST(WasmJSFunction)
OBJECT_CONSTRUCTORS(WasmJSFunction, JSFunction);
......@@ -715,6 +715,9 @@ class WasmFunctionData
using BodyDescriptor = FlexibleBodyDescriptor<kStartOfStrongFieldsOffset>;
using SuspendField = base::BitField<wasm::Suspend, 0, 1>;
using PromiseField = base::BitField<wasm::Promise, 1, 1>;
TQ_OBJECT_CONSTRUCTORS(WasmFunctionData)
};
......
......@@ -55,6 +55,8 @@ extern class WasmFunctionData extends HeapObject {
// Used for calling this function from JavaScript.
@if(V8_EXTERNAL_CODE_SPACE) wrapper_code: CodeDataContainer;
@ifnot(V8_EXTERNAL_CODE_SPACE) wrapper_code: Code;
// Encode the {promising} and {suspending} flags in a single smi.
js_promise_flags: Smi;
}
extern class WasmExportedFunctionData extends WasmFunctionData {
......@@ -70,18 +72,12 @@ extern class WasmExportedFunctionData extends WasmFunctionData {
@if(V8_EXTERNAL_CODE_SPACE) c_wrapper_code: CodeDataContainer;
@ifnot(V8_EXTERNAL_CODE_SPACE) c_wrapper_code: Code;
packed_args_size: Smi;
// Functions returned by suspender.returnPromiseOnSuspend() have this field
// set to the host suspender object.
suspend: Smi; // Boolean.
}
extern class WasmJSFunctionData extends WasmFunctionData {
serialized_return_count: Smi;
serialized_parameter_count: Smi;
serialized_signature: PodArrayOfWasmValueType;
// Whether this function is the result of wrapping another function with
// WebAssembly.suspendOnReturnedPromise.
suspend: Smi;
}
extern class WasmCapiFunctionData extends WasmFunctionData {
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment