[wasm] WAT-compatible naming for imported and exported globals

For globals, we would use name as follows.
1. If import: use <import_module>.<field_name> from WasmImport.
2. If export: use <field_name> from WasmExport.
3. Use global<index>.

Add DecodeGlobalNames similar to DecodeFunctionNames.

Doc: https://docs.google.com/document/d/1XoXWONLBgZWQ9dhtoMpQPvD0fnnWA50OorsuSXfME3g
Bug: v8:10242
Change-Id: I11131528fc6ae6ca50727e9b428f76e76b0000e5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2122523
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Kim-Anh Tran <kimanh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66910}
This commit is contained in:
Z Nguyen-Huu 2020-03-30 09:38:52 -07:00 committed by Commit Bot
parent a60707f598
commit 9c9167124b
10 changed files with 196 additions and 4 deletions

View File

@ -2257,6 +2257,35 @@ void DecodeFunctionNames(const byte* module_start, const byte* module_end,
}
}
void DecodeGlobalNames(
const Vector<const WasmImport> import_table,
const Vector<const WasmExport> export_table,
std::unordered_map<uint32_t, std::pair<WireBytesRef, WireBytesRef>>*
names) {
DCHECK_NOT_NULL(names);
DCHECK(names->empty());
// Extract from import table.
for (const WasmImport& imp : import_table) {
if (imp.kind != kExternalGlobal) continue;
if (!imp.module_name.is_set() || !imp.field_name.is_set()) continue;
if (names->count(imp.index) == 0) {
names->insert(std::make_pair(
imp.index, std::make_pair(imp.module_name, imp.field_name)));
}
}
// Extract from export table.
for (const WasmExport& exp : export_table) {
if (exp.kind != kExternalGlobal) continue;
if (!exp.name.is_set()) continue;
if (names->count(exp.index) == 0) {
names->insert(
std::make_pair(exp.index, std::make_pair(WireBytesRef(), exp.name)));
}
}
}
LocalNames DecodeLocalNames(Vector<const uint8_t> module_bytes) {
Decoder decoder(module_bytes);
if (!FindNameSection(&decoder)) return LocalNames{{}};

View File

@ -168,6 +168,13 @@ void DecodeFunctionNames(const byte* module_start, const byte* module_end,
std::unordered_map<uint32_t, WireBytesRef>* names,
const Vector<const WasmExport> export_table);
// Decode the global names from import table and export table. Returns the
// result as an unordered map.
void DecodeGlobalNames(
const Vector<const WasmImport> import_table,
const Vector<const WasmExport> export_table,
std::unordered_map<uint32_t, std::pair<WireBytesRef, WireBytesRef>>* names);
// Decode the local names assignment from the name section.
// The result will be empty if no name section is present. On encountering an
// error in the name section, returns all information decoded up to the first

View File

@ -552,9 +552,13 @@ Handle<JSObject> GetGlobalScopeObject(Handle<WasmInstanceObject> instance) {
JSObject::AddProperty(isolate, global_scope_object, globals_name,
globals_obj, NONE);
for (size_t i = 0; i < globals.size(); ++i) {
for (uint32_t i = 0; i < globals.size(); ++i) {
Handle<String> name;
if (!WasmInstanceObject::GetGlobalNameOrNull(isolate, instance, i)
.ToHandle(&name)) {
const char* label = "global%d";
Handle<String> name = PrintFToOneByteString<true>(isolate, label, i);
name = PrintFToOneByteString<true>(isolate, label, i);
}
WasmValue value =
WasmInstanceObject::GetGlobalValue(instance, globals[i]);
Handle<Object> value_obj = WasmValueToValueObject(isolate, value);

View File

@ -44,6 +44,21 @@ WireBytesRef DecodedFunctionNames::Lookup(
return it->second;
}
std::pair<WireBytesRef, WireBytesRef> DecodedGlobalNames::Lookup(
uint32_t global_index, Vector<const WasmImport> import_table,
Vector<const WasmExport> export_table) const {
base::MutexGuard lock(&mutex_);
if (!global_names_) {
global_names_.reset(
new std::unordered_map<uint32_t,
std::pair<WireBytesRef, WireBytesRef>>());
DecodeGlobalNames(import_table, export_table, global_names_.get());
}
auto it = global_names_->find(global_index);
if (it == global_names_->end()) return {};
return it->second;
}
// static
int MaxNumExportWrappers(const WasmModule* module) {
// For each signature there may exist a wrapper, both for imported and

View File

@ -206,6 +206,21 @@ class V8_EXPORT_PRIVATE DecodedFunctionNames {
function_names_;
};
class V8_EXPORT_PRIVATE DecodedGlobalNames {
public:
std::pair<WireBytesRef, WireBytesRef> Lookup(
uint32_t global_index, Vector<const WasmImport> import_table,
Vector<const WasmExport> export_table) const;
private:
// {global_names_} is populated lazily after decoding, and therefore needs a
// mutex to protect concurrent modifications from multiple {WasmModuleObject}.
mutable base::Mutex mutex_;
mutable std::unique_ptr<
std::unordered_map<uint32_t, std::pair<WireBytesRef, WireBytesRef>>>
global_names_;
};
class V8_EXPORT_PRIVATE AsmJsOffsetInformation {
public:
explicit AsmJsOffsetInformation(Vector<const byte> encoded_offsets);
@ -272,6 +287,7 @@ struct V8_EXPORT_PRIVATE WasmModule {
ModuleOrigin origin = kWasmOrigin; // origin of the module
DecodedFunctionNames function_names;
DecodedGlobalNames global_names;
std::string source_map_url;
// Asm.js source position information. Only available for modules compiled

View File

@ -1519,6 +1519,33 @@ WasmInstanceObject::GetGlobalBufferAndIndex(Handle<WasmInstanceObject> instance,
return {handle(instance->tagged_globals_buffer(), isolate), global.offset};
}
// static
MaybeHandle<String> WasmInstanceObject::GetGlobalNameOrNull(
Isolate* isolate, Handle<WasmInstanceObject> instance,
uint32_t global_index) {
wasm::ModuleWireBytes wire_bytes(
instance->module_object().native_module()->wire_bytes());
// This is pair of <module_name, field_name>.
// If field_name is not set then we don't generate a name. Else if module_name
// is set then it is imported global. Otherwise it is exported global.
std::pair<wasm::WireBytesRef, wasm::WireBytesRef> name_ref =
instance->module()->global_names.Lookup(
global_index, VectorOf(instance->module()->import_table),
VectorOf(instance->module()->export_table));
if (!name_ref.second.is_set()) return {};
Vector<const char> field_name = wire_bytes.GetNameOrNull(name_ref.second);
if (!name_ref.first.is_set()) {
return isolate->factory()->NewStringFromUtf8(VectorOf(field_name));
}
Vector<const char> module_name = wire_bytes.GetNameOrNull(name_ref.first);
std::string global_name;
global_name.append(module_name.begin(), module_name.end());
global_name.append(".");
global_name.append(field_name.begin(), field_name.end());
return isolate->factory()->NewStringFromUtf8(VectorOf(global_name));
}
// static
wasm::WasmValue WasmInstanceObject::GetGlobalValue(
Handle<WasmInstanceObject> instance, const wasm::WasmGlobal& global) {

View File

@ -567,6 +567,11 @@ class WasmInstanceObject : public JSObject {
static wasm::WasmValue GetGlobalValue(Handle<WasmInstanceObject>,
const wasm::WasmGlobal&);
// Get the name of a global in the given instance by index.
static MaybeHandle<String> GetGlobalNameOrNull(Isolate*,
Handle<WasmInstanceObject>,
uint32_t global_index);
OBJECT_CONSTRUCTORS(WasmInstanceObject, JSObject);
private:

View File

@ -3,5 +3,5 @@ Waiting for wasm script to be parsed.
Setting breakpoint in wasm.
Running main.
Paused in debugger.
globals: {"global0": hello, world}
globals: {"m.global": hello, world}
Finished.

View File

@ -0,0 +1,7 @@
Test wasm global names
Waiting for wasm script to be parsed.
Setting breakpoint in wasm.
Running main.
Paused in debugger.
globals: {"module_name.imported_global", "exported_global", "global2"}
Finished.

View File

@ -0,0 +1,82 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
let {session, contextGroup, Protocol} =
InspectorTest.start('Test wasm global names');
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
let builder = new WasmModuleBuilder();
builder.addImportedGlobal('module_name', 'imported_global', kWasmI32, false);
let func = builder.addFunction('func', kSig_v_i)
.addBody([
kExprGlobalGet, 0, //
kExprDrop, //
])
.exportAs('main');
var o = builder.addGlobal(kWasmI32, func).exportAs('exported_global');
builder.addGlobal(kWasmI32); // global2
let moduleBytes = JSON.stringify(builder.toArray());
function test(moduleBytes) {
let module = new WebAssembly.Module((new Uint8Array(moduleBytes)).buffer);
let imported_global_value = 123;
instance = new WebAssembly.Instance(
module, {module_name: {imported_global: imported_global_value}});
}
(async function() {
try {
Protocol.Debugger.enable();
Protocol.Runtime.evaluate({
expression: `
let instance;
${test.toString()}
test(${moduleBytes});`
});
InspectorTest.log('Waiting for wasm script to be parsed.');
let scriptId;
while (true) {
let msg = await Protocol.Debugger.onceScriptParsed();
if (msg.params.url.startsWith('wasm://')) {
scriptId = msg.params.scriptId;
break;
}
}
InspectorTest.log('Setting breakpoint in wasm.');
await Protocol.Debugger.setBreakpoint(
{location: {scriptId, lineNumber: 0, columnNumber: func.body_offset}});
InspectorTest.log('Running main.');
Protocol.Runtime.evaluate({expression: 'instance.exports.main()'});
let msg = await Protocol.Debugger.oncePaused();
let callFrames = msg.params.callFrames;
InspectorTest.log('Paused in debugger.');
let scopeChain = callFrames[0].scopeChain;
for (let scope of scopeChain) {
if (scope.type != 'global') continue;
let globalObjectProps = (await Protocol.Runtime.getProperties({
'objectId': scope.object.objectId
})).result.result;
for (let prop of globalObjectProps) {
let subProps = (await Protocol.Runtime.getProperties({
objectId: prop.value.objectId
})).result.result;
let values = subProps.map((value) => `"${value.name}"`).join(', ');
InspectorTest.log(` ${prop.name}: {${values}}`);
}
}
InspectorTest.log('Finished.');
} catch (exc) {
InspectorTest.log(`Failed with exception: ${exc}.`);
} finally {
InspectorTest.completeTest();
}
})();