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

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: I5f20ea57261f6433b8d86f55054216bf96b41382
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1760826
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63273}
This commit is contained in:
Michael Starzinger 2019-08-20 14:00:52 +02:00 committed by Commit Bot
parent 9460101cdb
commit 017a68c0ee
4 changed files with 124 additions and 25 deletions

View File

@ -1497,6 +1497,8 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
// 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) {
@ -1981,24 +1983,9 @@ void WebAssemblyGlobalType(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto maybe_global = GetFirstArgumentAsGlobal(args, &thrower);
if (thrower.error()) return;
i::Handle<i::WasmGlobalObject> global = maybe_global.ToHandleChecked();
v8::Local<v8::Object> ret = v8::Object::New(isolate);
if (!ret->CreateDataProperty(isolate->GetCurrentContext(),
v8_str(isolate, "mutable"),
v8::Boolean::New(isolate, global->is_mutable()))
.IsJust()) {
return;
}
Local<String> type = ToValueTypeString(isolate, global->type());
if (!ret->CreateDataProperty(isolate->GetCurrentContext(),
v8_str(isolate, "value"), type)
.IsJust()) {
return;
}
v8::ReturnValue<v8::Value> return_value = args.GetReturnValue();
return_value.Set(ret);
auto type = i::wasm::GetTypeForGlobal(i_isolate, global->is_mutable(),
global->type());
args.GetReturnValue().Set(Utils::ToLocal(type));
}
} // namespace

View File

@ -113,13 +113,69 @@ bool IsWasmCodegenAllowed(Isolate* isolate, Handle<Context> context) {
v8::Utils::ToLocal(isolate->factory()->empty_string()));
}
namespace {
// Converts the given {type} into a string representation that can be used in
// reflective functions. Should be kept in sync with the {GetValueType} helper.
Handle<String> ToValueTypeString(Isolate* isolate, ValueType type) {
Factory* factory = isolate->factory();
Handle<String> string;
switch (type) {
case i::wasm::kWasmI32: {
string = factory->InternalizeUtf8String("i32");
break;
}
case i::wasm::kWasmI64: {
string = factory->InternalizeUtf8String("i64");
break;
}
case i::wasm::kWasmF32: {
string = factory->InternalizeUtf8String("f32");
break;
}
case i::wasm::kWasmF64: {
string = factory->InternalizeUtf8String("f64");
break;
}
// TODO(mstarzinger): Add support and tests for exnref and funcref.
case i::wasm::kWasmAnyRef: {
string = factory->InternalizeUtf8String("anyref");
break;
}
default:
UNREACHABLE();
}
return string;
}
} // namespace
Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
ValueType type) {
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<JSObject> object = factory->NewJSObject(object_function);
JSObject::AddProperty(isolate, object, mutable_string,
factory->ToBoolean(is_mutable), NONE);
JSObject::AddProperty(isolate, object, value_string,
ToValueTypeString(isolate, type), NONE);
return object;
}
Handle<JSArray> GetImports(Isolate* isolate,
Handle<WasmModuleObject> module_object) {
auto enabled_features = i::wasm::WasmFeaturesFromIsolate(isolate);
Factory* factory = isolate->factory();
Handle<String> module_string = factory->InternalizeUtf8String("module");
Handle<String> name_string = factory->InternalizeUtf8String("name");
Handle<String> kind_string = factory->InternalizeUtf8String("kind");
Handle<String> type_string = factory->InternalizeUtf8String("type");
Handle<String> function_string = factory->InternalizeUtf8String("function");
Handle<String> table_string = factory->InternalizeUtf8String("table");
@ -145,6 +201,7 @@ Handle<JSArray> GetImports(Isolate* isolate,
Handle<JSObject> entry = factory->NewJSObject(object_function);
Handle<String> import_kind;
Handle<JSObject> type_value;
switch (import.kind) {
case kExternalFunction:
import_kind = function_string;
@ -156,6 +213,11 @@ Handle<JSArray> GetImports(Isolate* isolate,
import_kind = memory_string;
break;
case kExternalGlobal:
if (enabled_features.type_reflection) {
auto& global = module->globals[import.index];
type_value =
GetTypeForGlobal(isolate, global.mutability, global.type);
}
import_kind = global_string;
break;
case kExternalException:
@ -178,6 +240,9 @@ Handle<JSArray> GetImports(Isolate* isolate,
JSObject::AddProperty(isolate, entry, name_string,
import_name.ToHandleChecked(), NONE);
JSObject::AddProperty(isolate, entry, kind_string, import_kind, NONE);
if (!type_value.is_null()) {
JSObject::AddProperty(isolate, entry, type_string, type_value, NONE);
}
storage->set(index, *entry);
}
@ -187,10 +252,12 @@ Handle<JSArray> GetImports(Isolate* isolate,
Handle<JSArray> GetExports(Isolate* isolate,
Handle<WasmModuleObject> module_object) {
auto enabled_features = i::wasm::WasmFeaturesFromIsolate(isolate);
Factory* factory = isolate->factory();
Handle<String> name_string = factory->InternalizeUtf8String("name");
Handle<String> kind_string = factory->InternalizeUtf8String("kind");
Handle<String> type_string = factory->InternalizeUtf8String("type");
Handle<String> function_string = factory->InternalizeUtf8String("function");
Handle<String> table_string = factory->InternalizeUtf8String("table");
@ -214,6 +281,7 @@ Handle<JSArray> GetExports(Isolate* isolate,
const WasmExport& exp = module->export_table[index];
Handle<String> export_kind;
Handle<JSObject> type_value;
switch (exp.kind) {
case kExternalFunction:
export_kind = function_string;
@ -225,6 +293,11 @@ Handle<JSArray> GetExports(Isolate* isolate,
export_kind = memory_string;
break;
case kExternalGlobal:
if (enabled_features.type_reflection) {
auto& global = module->globals[exp.index];
type_value =
GetTypeForGlobal(isolate, global.mutability, global.type);
}
export_kind = global_string;
break;
case kExternalException:
@ -243,6 +316,9 @@ Handle<JSArray> GetExports(Isolate* isolate,
JSObject::AddProperty(isolate, entry, name_string,
export_name.ToHandleChecked(), NONE);
JSObject::AddProperty(isolate, entry, kind_string, export_kind, NONE);
if (!type_value.is_null()) {
JSObject::AddProperty(isolate, entry, type_string, type_value, NONE);
}
storage->set(index, *entry);
}

View File

@ -301,13 +301,13 @@ V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes(
V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
Handle<Context> context);
V8_EXPORT_PRIVATE Handle<JSArray> GetImports(Isolate* isolate,
Handle<WasmModuleObject> module);
V8_EXPORT_PRIVATE Handle<JSArray> GetExports(Isolate* isolate,
Handle<WasmModuleObject> module);
V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections(
Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name,
ErrorThrower* thrower);
Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable,
ValueType type);
Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module);
Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module);
Handle<JSArray> GetCustomSections(Isolate* isolate,
Handle<WasmModuleObject> module,
Handle<String> name, ErrorThrower* thrower);
// Decode local variable names from the names section. Return FixedArray of
// FixedArray of <undefined|String>. The outer fixed array is indexed by the

View File

@ -105,6 +105,42 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
assertEquals(2, Object.getOwnPropertyNames(type).length);
})();
(function TestGlobalExports() {
let builder = new WasmModuleBuilder();
builder.addGlobal(kWasmI32).exportAs("a");
builder.addGlobal(kWasmF64, true).exportAs("b");
let module = new WebAssembly.Module(builder.toBuffer());
let exports = WebAssembly.Module.exports(module);
assertEquals("a", exports[0].name);
assertTrue("type" in exports[0]);
assertEquals("i32", exports[0].type.value);
assertEquals(false, exports[0].type.mutable);
assertEquals("b", exports[1].name);
assertTrue("type" in exports[1]);
assertEquals("f64", exports[1].type.value);
assertEquals(true, exports[1].type.mutable);
})();
(function TestGlobalImports() {
let builder = new WasmModuleBuilder();
builder.addImportedGlobal("m", "a", kWasmI32);
builder.addImportedGlobal("m", "b", kWasmF64, true);
let module = new WebAssembly.Module(builder.toBuffer());
let imports = WebAssembly.Module.imports(module);
assertEquals("a", imports[0].name);
assertTrue("type" in imports[0]);
assertEquals("i32", imports[0].type.value);
assertEquals(false, imports[0].type.mutable);
assertEquals("b", imports[1].name);
assertTrue("type" in imports[1]);
assertEquals("f64", imports[1].type.value);
assertEquals(true, imports[1].type.mutable);
})();
(function TestMemoryConstructorWithMinimum() {
let mem = new WebAssembly.Memory({minimum: 1});
assertTrue(mem instanceof WebAssembly.Memory);