v8/test/inspector/wasm-inspector-test.js
Andreas Haas 6d08c94cb5 [wasm][debug] Provide WebAssembly Table entries to DevTools
With this CL, V8 adds an internal field to the WebAssembly Table object
that is sent to DevTools to also show table entries. As WebAssembly
Tables get initialized lazily, V8 only shows the name of the function
instead of the function object itself.

Screenshot before change: https://imgur.com/a/XFvy3lA
Screenshot after change: https://imgur.com/kT84kst
R=kimanh@chromium.org

Change-Id: I56a0b07785ff3484f1447419fe39ae5ec3f93247
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2897097
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Kim-Anh Tran <kimanh@chromium.org>
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74690}
2021-05-20 09:59:09 +00:00

164 lines
5.7 KiB
JavaScript

// 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.
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
WasmInspectorTest = {}
InspectorTest.getWasmOpcodeName = getOpcodeName;
WasmInspectorTest.evalWithUrl = async function(expression, url) {
const sourceURL = `v8://test/${url}`;
const {result: {scriptId}} = await Protocol.Runtime.compileScript({
expression, sourceURL, persistScript: true}).then(printIfFailure);
return await Protocol.Runtime
.runScript({scriptId})
.then(printIfFailure);
};
WasmInspectorTest.compileFromBuffer = (function(bytes) {
var buffer = new ArrayBuffer(bytes.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < bytes.length; ++i) {
view[i] = bytes[i] | 0;
}
return new WebAssembly.Module(buffer);
}).toString();
WasmInspectorTest.instantiateFromBuffer =
(function(bytes, imports) {
return new WebAssembly.Instance(compileFromBuffer(bytes), imports);
})
.toString()
.replace('compileFromBuffer', WasmInspectorTest.compileFromBuffer);
WasmInspectorTest.compile = async function(bytes, module_name = 'module') {
const compile_code = `var ${module_name} = (${
WasmInspectorTest.compileFromBuffer})(${JSON.stringify(bytes)});`;
await WasmInspectorTest.evalWithUrl(compile_code, 'compile_module');
};
WasmInspectorTest.instantiate =
async function(bytes, instance_name = 'instance', imports) {
const instantiate_code = `var ${instance_name} = (${
WasmInspectorTest.instantiateFromBuffer})(${JSON.stringify(bytes)},
${imports});`;
await WasmInspectorTest.evalWithUrl(instantiate_code, 'instantiate');
};
WasmInspectorTest.dumpScopeProperties = async function(message) {
printIfFailure(message);
for (var value of message.result.result) {
var value_str = await getScopeValues(value.name, value.value);
InspectorTest.log(' ' + value.name + ': ' + value_str);
}
};
WasmInspectorTest.getWasmValue = async function(value) {
let msg = await Protocol.Runtime.getProperties({ objectId: value.objectId });
printIfFailure(msg);
const value_type = msg.result.result.find(({name}) => name === 'type');
const value_value = msg.result.result.find(({name}) => name === 'value');
return `${
value_value.value.unserializableValue ??
value_value.value.description ??
value_value.value.value} (${value_type.value.value})`;
};
function printIfFailure(message) {
if (!message.result) {
InspectorTest.logMessage(message);
}
return message;
}
async function getScopeValues(name, value) {
async function printValue(value) {
if (value.type === 'object' && value.subtype === 'wasmvalue') {
return await WasmInspectorTest.getWasmValue(value);
} else if ('className' in value) {
return `(${value.className})`;
}
return `${value.unserializableValue ?? value.value} (${
value.subtype ?? value.type})`;
}
if (value.type === 'object' && value.subtype !== 'wasmvalue') {
if (value.subtype === 'typedarray' || value.subtype == 'webassemblymemory')
return value.description;
if (name === 'instance') return dumpInstanceProperties(value);
if (name === 'module') return value.description;
if (name === 'tables') return dumpTables(value);
let msg = await Protocol.Runtime.getProperties({objectId: value.objectId});
printIfFailure(msg);
async function printProperty({name, value}) {
return `"${name}": ${await printValue(value)}`;
}
return (await Promise.all(msg.result.result.map(printProperty))).join(', ');
}
return await printValue(value);
}
function recursiveGetPropertiesWrapper(value, depth) {
return recursiveGetProperties({result: {result: [value]}}, depth);
}
async function recursiveGetProperties(value, depth) {
if (depth > 0) {
const properties = await Promise.all(value.result.result.map(x => {
return Protocol.Runtime.getProperties({objectId: x.value.objectId});
}));
const recursiveProperties = await Promise.all(properties.map(x => {
return recursiveGetProperties(x, depth - 1);
}));
return recursiveProperties.flat();
}
return value;
}
async function dumpInstanceProperties(instanceObj) {
function invokeGetter(property) {
return this[JSON.parse(property)];
}
const exportsName = 'exports';
let exportsObj = await Protocol.Runtime.callFunctionOn({
objectId: instanceObj.objectId,
functionDeclaration: invokeGetter.toString(),
arguments: [{value: JSON.stringify(exportsName)}]
});
printIfFailure(exportsObj);
let exports = await Protocol.Runtime.getProperties(
{objectId: exportsObj.result.result.objectId});
printIfFailure(exports);
function printExports(value) {
return `"${value.name}" (${value.value.className})`;
}
const formattedExports = exports.result.result.map(printExports).join(', ');
return `${exportsName}: ${formattedExports}`
}
async function dumpTables(tablesObj) {
let msg =
await Protocol.Runtime.getProperties({objectId: tablesObj.objectId});
let tables_str = [];
for (let table of msg.result.result) {
let table_content =
await Protocol.Runtime.getProperties({objectId: table.value.objectId});
let entries_object = table_content.result.internalProperties.filter(
p => p.name === '[[Entries]]')[0];
entries = await Protocol.Runtime.getProperties(
{objectId: entries_object.value.objectId});
let functions = [];
for (let entry of entries.result.result) {
if (entry.name === 'length') continue;
functions.push(`${entry.name}: ${entry.value.description}`);
}
const functions_str = functions.join(', ');
tables_str.push(` ${table.name}: ${functions_str}`);
}
return '\n' + tables_str.join('\n');
}