From 017a68c0ee43fd22f2a03f7a960ba2627b3c086b Mon Sep 17 00:00:00 2001 From: Michael Starzinger Date: Tue, 20 Aug 2019 14:00:52 +0200 Subject: [PATCH] [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 Reviewed-by: Andreas Haas Cr-Commit-Position: refs/heads/master@{#63273} --- src/wasm/wasm-js.cc | 23 ++------- src/wasm/wasm-module.cc | 76 ++++++++++++++++++++++++++++ src/wasm/wasm-module.h | 14 ++--- test/mjsunit/wasm/type-reflection.js | 36 +++++++++++++ 4 files changed, 124 insertions(+), 25 deletions(-) diff --git a/src/wasm/wasm-js.cc b/src/wasm/wasm-js.cc index 1900db0640..10b5aef6d3 100644 --- a/src/wasm/wasm-js.cc +++ b/src/wasm/wasm-js.cc @@ -1497,6 +1497,8 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo& 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 ToValueTypeString(Isolate* isolate, i::wasm::ValueType type) { Local string; switch (type) { @@ -1981,24 +1983,9 @@ void WebAssemblyGlobalType(const v8::FunctionCallbackInfo& args) { auto maybe_global = GetFirstArgumentAsGlobal(args, &thrower); if (thrower.error()) return; i::Handle global = maybe_global.ToHandleChecked(); - v8::Local ret = v8::Object::New(isolate); - - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "mutable"), - v8::Boolean::New(isolate, global->is_mutable())) - .IsJust()) { - return; - } - - Local type = ToValueTypeString(isolate, global->type()); - if (!ret->CreateDataProperty(isolate->GetCurrentContext(), - v8_str(isolate, "value"), type) - .IsJust()) { - return; - } - - v8::ReturnValue 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 diff --git a/src/wasm/wasm-module.cc b/src/wasm/wasm-module.cc index 05057301ed..92b37aeaeb 100644 --- a/src/wasm/wasm-module.cc +++ b/src/wasm/wasm-module.cc @@ -113,13 +113,69 @@ bool IsWasmCodegenAllowed(Isolate* isolate, Handle 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 ToValueTypeString(Isolate* isolate, ValueType type) { + Factory* factory = isolate->factory(); + Handle 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 GetTypeForGlobal(Isolate* isolate, bool is_mutable, + ValueType type) { + Factory* factory = isolate->factory(); + + Handle mutable_string = factory->InternalizeUtf8String("mutable"); + Handle value_string = factory->InternalizeUtf8String("value"); + + Handle object_function = isolate->object_function(); + Handle 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 GetImports(Isolate* isolate, Handle module_object) { + auto enabled_features = i::wasm::WasmFeaturesFromIsolate(isolate); Factory* factory = isolate->factory(); Handle module_string = factory->InternalizeUtf8String("module"); Handle name_string = factory->InternalizeUtf8String("name"); Handle kind_string = factory->InternalizeUtf8String("kind"); + Handle type_string = factory->InternalizeUtf8String("type"); Handle function_string = factory->InternalizeUtf8String("function"); Handle table_string = factory->InternalizeUtf8String("table"); @@ -145,6 +201,7 @@ Handle GetImports(Isolate* isolate, Handle entry = factory->NewJSObject(object_function); Handle import_kind; + Handle type_value; switch (import.kind) { case kExternalFunction: import_kind = function_string; @@ -156,6 +213,11 @@ Handle 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 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 GetImports(Isolate* isolate, Handle GetExports(Isolate* isolate, Handle module_object) { + auto enabled_features = i::wasm::WasmFeaturesFromIsolate(isolate); Factory* factory = isolate->factory(); Handle name_string = factory->InternalizeUtf8String("name"); Handle kind_string = factory->InternalizeUtf8String("kind"); + Handle type_string = factory->InternalizeUtf8String("type"); Handle function_string = factory->InternalizeUtf8String("function"); Handle table_string = factory->InternalizeUtf8String("table"); @@ -214,6 +281,7 @@ Handle GetExports(Isolate* isolate, const WasmExport& exp = module->export_table[index]; Handle export_kind; + Handle type_value; switch (exp.kind) { case kExternalFunction: export_kind = function_string; @@ -225,6 +293,11 @@ Handle 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 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); } diff --git a/src/wasm/wasm-module.h b/src/wasm/wasm-module.h index 7dea208d8e..397451f13a 100644 --- a/src/wasm/wasm-module.h +++ b/src/wasm/wasm-module.h @@ -301,13 +301,13 @@ V8_EXPORT_PRIVATE MaybeHandle CreateModuleObjectFromBytes( V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate, Handle context); -V8_EXPORT_PRIVATE Handle GetImports(Isolate* isolate, - Handle module); -V8_EXPORT_PRIVATE Handle GetExports(Isolate* isolate, - Handle module); -V8_EXPORT_PRIVATE Handle GetCustomSections( - Isolate* isolate, Handle module, Handle name, - ErrorThrower* thrower); +Handle GetTypeForGlobal(Isolate* isolate, bool is_mutable, + ValueType type); +Handle GetImports(Isolate* isolate, Handle module); +Handle GetExports(Isolate* isolate, Handle module); +Handle GetCustomSections(Isolate* isolate, + Handle module, + Handle name, ErrorThrower* thrower); // Decode local variable names from the names section. Return FixedArray of // FixedArray of . The outer fixed array is indexed by the diff --git a/test/mjsunit/wasm/type-reflection.js b/test/mjsunit/wasm/type-reflection.js index a5bc5f41f5..800a65f9d6 100644 --- a/test/mjsunit/wasm/type-reflection.js +++ b/test/mjsunit/wasm/type-reflection.js @@ -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);