Commit ba77172b authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Make constructed {WebAssembly.Function} callable.

This makes function objects constructed via the {WebAssembly.Function}
constructor callable directly from JavaScript (not just from within
WebAssembly modules). Semantics are as if the function performed the
transition JS-to-Wasm and then Wasm-to-JS in sequence.

R=clemensh@chromium.org
TEST=mjsunit/wasm/type-reflection
BUG=v8:7742

Change-Id: Ic7dcf36ccfda1b473f2541e49419f4d2ee38bc2c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1720809
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62953}
parent 695c40f0
This diff is collapsed.
......@@ -140,6 +140,12 @@ V8_EXPORT_PRIVATE wasm::WasmCompilationResult CompileWasmInterpreterEntry(
wasm::WasmEngine*, const wasm::WasmFeatures& enabled_features,
uint32_t func_index, wasm::FunctionSig*);
// Compiles a stub with JS linkage that serves as an adapter for function
// objects constructed via {WebAssembly.Function}. It performs a round-trip
// simulating a JS-to-Wasm-to-JS coercion of parameter and return values.
MaybeHandle<Code> CompileJSToJSWrapper(Isolate* isolate,
wasm::FunctionSig* sig);
enum CWasmEntryParameters {
kCodeEntry,
kObjectRef,
......@@ -444,6 +450,7 @@ class WasmGraphBuilder {
SetOncePointer<Node> globals_start_;
SetOncePointer<Node> imported_mutable_globals_;
SetOncePointer<Node> stack_check_code_node_;
SetOncePointer<Node> isolate_root_node_;
SetOncePointer<const Operator> stack_check_call_operator_;
Node** cur_buffer_;
......@@ -461,6 +468,8 @@ class WasmGraphBuilder {
Node* NoContextConstant();
Node* BuildLoadIsolateRoot();
Node* MemBuffer(uint32_t offset);
// BoundsCheckMem receives a uint32 {index} node and returns a ptrsize index.
Node* BoundsCheckMem(uint8_t access_size, Node* index, uint32_t offset,
......
......@@ -579,6 +579,8 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
return OPTIMIZED;
case Code::JS_TO_WASM_FUNCTION:
return JS_TO_WASM;
case Code::JS_TO_JS_FUNCTION:
return STUB;
case Code::C_WASM_ENTRY:
return C_WASM_ENTRY;
case Code::WASM_FUNCTION:
......
......@@ -1978,6 +1978,10 @@ void ExistingCodeLogger::LogCodeObject(Object object) {
description = "A JavaScript to Wasm adapter";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::JS_TO_JS_FUNCTION:
description = "A WebAssembly.Function adapter";
tag = CodeEventListener::STUB_TAG;
break;
case AbstractCode::WASM_TO_CAPI_FUNCTION:
description = "A Wasm to C-API adapter";
tag = CodeEventListener::STUB_TAG;
......
......@@ -45,6 +45,7 @@ class Code : public HeapObject {
V(WASM_TO_CAPI_FUNCTION) \
V(WASM_TO_JS_FUNCTION) \
V(JS_TO_WASM_FUNCTION) \
V(JS_TO_JS_FUNCTION) \
V(WASM_INTERPRETER_ENTRY) \
V(C_WASM_ENTRY)
......
......@@ -2298,6 +2298,10 @@ Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate,
if (sig_size > 0) {
serialized_sig->copy_in(0, sig->all().begin(), sig_size);
}
// TODO(mstarzinger): Think about caching and sharing the JS-to-JS wrappers
// per signature instead of compiling a new one for every instantiation.
Handle<Code> wrapper_code =
compiler::CompileJSToJSWrapper(isolate, sig).ToHandleChecked();
Handle<WasmJSFunctionData> function_data =
Handle<WasmJSFunctionData>::cast(isolate->factory()->NewStruct(
WASM_JS_FUNCTION_DATA_TYPE, AllocationType::kOld));
......@@ -2305,9 +2309,7 @@ Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate,
function_data->set_serialized_parameter_count(parameter_count);
function_data->set_serialized_signature(*serialized_sig);
function_data->set_callable(*callable);
// TODO(7742): Make this callable by using a proper wrapper code.
function_data->set_wrapper_code(
isolate->builtins()->builtin(Builtins::kIllegal));
function_data->set_wrapper_code(*wrapper_code);
Handle<String> name = isolate->factory()->Function_string();
if (callable->IsJSFunction()) {
name = JSFunction::GetName(Handle<JSFunction>::cast(callable));
......@@ -2316,6 +2318,7 @@ Handle<WasmJSFunction> WasmJSFunction::New(Isolate* isolate,
NewFunctionArgs args =
NewFunctionArgs::ForWasm(name, function_data, function_map);
Handle<JSFunction> js_function = isolate->factory()->NewFunction(args);
js_function->shared().set_internal_formal_parameter_count(parameter_count);
return Handle<WasmJSFunction>::cast(js_function);
}
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-type-reflection
// Flags: --experimental-wasm-type-reflection --expose-gc
load('test/mjsunit/wasm/wasm-module-builder.js');
......@@ -219,8 +219,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
assertSame(fun.__proto__.__proto__.__proto__, Object.prototype);
assertSame(fun.constructor, WebAssembly.Function);
assertEquals(typeof fun, 'function');
// TODO(7742): Enable once it is callable.
// assertDoesNotThrow(() => fun());
assertDoesNotThrow(() => fun());
})();
(function TestFunctionExportedFunction() {
......@@ -271,6 +270,49 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
});
})();
(function TestFunctionConstructedCoercions() {
let obj1 = { valueOf: _ => 123.45 };
let obj2 = { toString: _ => "456" };
let gcer = { valueOf: _ => gc() };
let testcases = [
{ params: { sig: ["i32"],
val: [23.5],
exp: [23], },
result: { sig: ["i32"],
val: 42.7,
exp: 42, },
},
{ params: { sig: ["i32", "f32", "f64"],
val: [obj1, obj2, "789"],
exp: [123, 456, 789], },
result: { sig: [],
val: undefined,
exp: undefined, },
},
{ params: { sig: ["i32", "f32", "f64"],
val: [gcer, {}, "xyz"],
exp: [0, NaN, NaN], },
result: { sig: ["f64"],
val: gcer,
exp: NaN, },
},
];
testcases.forEach(function({params, result}) {
let p = params.sig; let r = result.sig; var params_after;
function testFun() { params_after = arguments; return result.val; }
let fun = new WebAssembly.Function({parameters:p, results:r}, testFun);
let result_after = fun.apply(undefined, params.val);
assertArrayEquals(params.exp, params_after);
assertEquals(result.exp, result_after);
});
})();
(function TestFunctionConstructedIncompatibleSig() {
let fun = new WebAssembly.Function({parameters:["i64"], results:[]}, _ => 0);
assertThrows(() => fun(), TypeError,
/wasm function signature contains illegal type/);
})();
(function TestFunctionTableSetAndCall() {
let builder = new WasmModuleBuilder();
let fun1 = new WebAssembly.Function({parameters:[], results:["i32"]}, _ => 7);
......
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