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

[wasm] Add WebAssembly.Module type reflection of functions.

This adds type reflection support to the {WebAssembly.Module.exports} as
well as {WebAssembly.Module.imports} method. It also refactors existing
reflective code to use the internal instead of the public embedder API,
which is slightly more efficient anyways.

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

Change-Id: I168741d382373ec47ebe0517ce7803732cbb3b24
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1762011
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63276}
parent b4859582
...@@ -1495,39 +1495,6 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1495,39 +1495,6 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(Utils::ToLocal(result)); args.GetReturnValue().Set(Utils::ToLocal(result));
} }
// Converts the given {type} into a string representation that can be used in
// reflective functions. Should be kept in sync with the {GetValueType} helper.
// TODO(mstarzinger): Remove once {WebAssemblyFunctionType} has been ported to
// use the internal API instead.
Local<String> ToValueTypeString(Isolate* isolate, i::wasm::ValueType type) {
Local<String> string;
switch (type) {
case i::wasm::kWasmI32: {
string = v8_str(isolate, "i32");
break;
}
case i::wasm::kWasmI64: {
string = v8_str(isolate, "i64");
break;
}
case i::wasm::kWasmF32: {
string = v8_str(isolate, "f32");
break;
}
case i::wasm::kWasmF64: {
string = v8_str(isolate, "f64");
break;
}
case i::wasm::kWasmAnyRef: {
string = v8_str(isolate, "anyref");
break;
}
default:
UNREACHABLE();
}
return string;
}
// WebAssembly.Function.type(WebAssembly.Function) -> FunctionType // WebAssembly.Function.type(WebAssembly.Function) -> FunctionType
void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) { void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate(); v8::Isolate* isolate = args.GetIsolate();
...@@ -1547,36 +1514,8 @@ void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -1547,36 +1514,8 @@ void WebAssemblyFunctionType(const v8::FunctionCallbackInfo<v8::Value>& args) {
return; return;
} }
// Extract values for the {ValueType[]} arrays. auto type = i::wasm::GetTypeForFunction(i_isolate, sig);
size_t param_index = 0; args.GetReturnValue().Set(Utils::ToLocal(type));
i::ScopedVector<Local<Value>> param_values(sig->parameter_count());
for (i::wasm::ValueType type : sig->parameters()) {
param_values[param_index++] = ToValueTypeString(isolate, type);
}
size_t result_index = 0;
i::ScopedVector<Local<Value>> result_values(sig->return_count());
for (i::wasm::ValueType type : sig->returns()) {
result_values[result_index++] = ToValueTypeString(isolate, type);
}
// Create the resulting {FunctionType} object.
Local<Object> ret = v8::Object::New(isolate);
Local<Context> context = isolate->GetCurrentContext();
Local<Array> params =
v8::Array::New(isolate, param_values.begin(), param_values.size());
if (!ret->CreateDataProperty(context, v8_str(isolate, "parameters"), params)
.IsJust()) {
return;
}
Local<Array> results =
v8::Array::New(isolate, result_values.begin(), result_values.size());
if (!ret->CreateDataProperty(context, v8_str(isolate, "results"), results)
.IsJust()) {
return;
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(ret);
} }
constexpr const char* kName_WasmGlobalObject = "WebAssembly.Global"; constexpr const char* kName_WasmGlobalObject = "WebAssembly.Global";
......
...@@ -150,15 +150,46 @@ Handle<String> ToValueTypeString(Isolate* isolate, ValueType type) { ...@@ -150,15 +150,46 @@ Handle<String> ToValueTypeString(Isolate* isolate, ValueType type) {
} // namespace } // namespace
Handle<JSObject> GetTypeForFunction(Isolate* isolate, FunctionSig* sig) {
Factory* factory = isolate->factory();
// Extract values for the {ValueType[]} arrays.
int param_index = 0;
int param_count = static_cast<int>(sig->parameter_count());
Handle<FixedArray> param_values = factory->NewFixedArray(param_count);
for (ValueType type : sig->parameters()) {
Handle<String> type_value = ToValueTypeString(isolate, type);
param_values->set(param_index++, *type_value);
}
int result_index = 0;
int result_count = static_cast<int>(sig->return_count());
Handle<FixedArray> result_values = factory->NewFixedArray(result_count);
for (ValueType type : sig->returns()) {
Handle<String> type_value = ToValueTypeString(isolate, type);
result_values->set(result_index++, *type_value);
}
// Create the resulting {FunctionType} object.
Handle<JSFunction> object_function = isolate->object_function();
Handle<JSObject> object = factory->NewJSObject(object_function);
Handle<JSArray> params = factory->NewJSArrayWithElements(param_values);
Handle<JSArray> results = factory->NewJSArrayWithElements(result_values);
Handle<String> params_string = factory->InternalizeUtf8String("parameters");
Handle<String> results_string = factory->InternalizeUtf8String("results");
JSObject::AddProperty(isolate, object, params_string, params, NONE);
JSObject::AddProperty(isolate, object, results_string, results, NONE);
return object;
}
Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable, Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
ValueType type) { ValueType type) {
Factory* factory = isolate->factory(); Factory* factory = isolate->factory();
Handle<String> mutable_string = factory->InternalizeUtf8String("mutable");
Handle<String> value_string = factory->InternalizeUtf8String("value");
Handle<JSFunction> object_function = isolate->object_function(); Handle<JSFunction> object_function = isolate->object_function();
Handle<JSObject> object = factory->NewJSObject(object_function); Handle<JSObject> object = factory->NewJSObject(object_function);
Handle<String> mutable_string = factory->InternalizeUtf8String("mutable");
Handle<String> value_string = factory->InternalizeUtf8String("value");
JSObject::AddProperty(isolate, object, mutable_string, JSObject::AddProperty(isolate, object, mutable_string,
factory->ToBoolean(is_mutable), NONE); factory->ToBoolean(is_mutable), NONE);
JSObject::AddProperty(isolate, object, value_string, JSObject::AddProperty(isolate, object, value_string,
...@@ -204,6 +235,10 @@ Handle<JSArray> GetImports(Isolate* isolate, ...@@ -204,6 +235,10 @@ Handle<JSArray> GetImports(Isolate* isolate,
Handle<JSObject> type_value; Handle<JSObject> type_value;
switch (import.kind) { switch (import.kind) {
case kExternalFunction: case kExternalFunction:
if (enabled_features.type_reflection) {
auto& func = module->functions[import.index];
type_value = GetTypeForFunction(isolate, func.sig);
}
import_kind = function_string; import_kind = function_string;
break; break;
case kExternalTable: case kExternalTable:
...@@ -284,6 +319,10 @@ Handle<JSArray> GetExports(Isolate* isolate, ...@@ -284,6 +319,10 @@ Handle<JSArray> GetExports(Isolate* isolate,
Handle<JSObject> type_value; Handle<JSObject> type_value;
switch (exp.kind) { switch (exp.kind) {
case kExternalFunction: case kExternalFunction:
if (enabled_features.type_reflection) {
auto& func = module->functions[exp.index];
type_value = GetTypeForFunction(isolate, func.sig);
}
export_kind = function_string; export_kind = function_string;
break; break;
case kExternalTable: case kExternalTable:
......
...@@ -301,6 +301,7 @@ V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes( ...@@ -301,6 +301,7 @@ V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes(
V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate, V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
Handle<Context> context); Handle<Context> context);
Handle<JSObject> GetTypeForFunction(Isolate* isolate, FunctionSig* sig);
Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable, Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
ValueType type); ValueType type);
Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module); Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module);
......
...@@ -131,11 +131,13 @@ load('test/mjsunit/wasm/wasm-module-builder.js'); ...@@ -131,11 +131,13 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
let imports = WebAssembly.Module.imports(module); let imports = WebAssembly.Module.imports(module);
assertEquals("a", imports[0].name); assertEquals("a", imports[0].name);
assertEquals("m", imports[0].module);
assertTrue("type" in imports[0]); assertTrue("type" in imports[0]);
assertEquals("i32", imports[0].type.value); assertEquals("i32", imports[0].type.value);
assertEquals(false, imports[0].type.mutable); assertEquals(false, imports[0].type.mutable);
assertEquals("b", imports[1].name); assertEquals("b", imports[1].name);
assertEquals("m", imports[1].module);
assertTrue("type" in imports[1]); assertTrue("type" in imports[1]);
assertEquals("f64", imports[1].type.value); assertEquals("f64", imports[1].type.value);
assertEquals(true, imports[1].type.mutable); assertEquals(true, imports[1].type.mutable);
...@@ -342,6 +344,45 @@ load('test/mjsunit/wasm/wasm-module-builder.js'); ...@@ -342,6 +344,45 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
}); });
})(); })();
(function TestFunctionExports() {
let testcases = [
[kSig_v_v, {parameters:[], results:[]}],
[kSig_v_i, {parameters:["i32"], results:[]}],
[kSig_i_l, {parameters:["i64"], results:["i32"]}],
[kSig_v_ddi, {parameters:["f64", "f64", "i32"], results:[]}],
[kSig_f_f, {parameters:["f32"], results:["f32"]}],
];
testcases.forEach(function([sig, expected]) {
let builder = new WasmModuleBuilder();
builder.addFunction("fun", sig).addBody([kExprUnreachable]).exportFunc();
let module = new WebAssembly.Module(builder.toBuffer());
let exports = WebAssembly.Module.exports(module);
assertEquals("fun", exports[0].name);
assertTrue("type" in exports[0]);
assertEquals(expected, exports[0].type);
});
})();
(function TestFunctionImports() {
let testcases = [
[kSig_v_v, {parameters:[], results:[]}],
[kSig_v_i, {parameters:["i32"], results:[]}],
[kSig_i_l, {parameters:["i64"], results:["i32"]}],
[kSig_v_ddi, {parameters:["f64", "f64", "i32"], results:[]}],
[kSig_f_f, {parameters:["f32"], results:["f32"]}],
];
testcases.forEach(function([sig, expected]) {
let builder = new WasmModuleBuilder();
builder.addImport("m", "fun", sig);
let module = new WebAssembly.Module(builder.toBuffer());
let imports = WebAssembly.Module.imports(module);
assertEquals("fun", imports[0].name);
assertEquals("m", imports[0].module);
assertTrue("type" in imports[0]);
assertEquals(expected, imports[0].type);
});
})();
(function TestFunctionConstructedCoercions() { (function TestFunctionConstructedCoercions() {
let obj1 = { valueOf: _ => 123.45 }; let obj1 = { valueOf: _ => 123.45 };
let obj2 = { toString: _ => "456" }; let obj2 = { toString: _ => "456" };
......
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