[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:
parent
a60707f598
commit
9c9167124b
@ -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{{}};
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
const char* label = "global%d";
|
||||
Handle<String> name = PrintFToOneByteString<true>(isolate, label, 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";
|
||||
name = PrintFToOneByteString<true>(isolate, label, i);
|
||||
}
|
||||
WasmValue value =
|
||||
WasmInstanceObject::GetGlobalValue(instance, globals[i]);
|
||||
Handle<Object> value_obj = WasmValueToValueObject(isolate, value);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
7
test/inspector/debugger/wasm-global-names-expected.txt
Normal file
7
test/inspector/debugger/wasm-global-names-expected.txt
Normal 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.
|
82
test/inspector/debugger/wasm-global-names.js
Normal file
82
test/inspector/debugger/wasm-global-names.js
Normal 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();
|
||||
}
|
||||
})();
|
Loading…
Reference in New Issue
Block a user