v8/test/inspector/wasm-inspector-test.js
Matthias Liedtke ee9b0f9f02 [wasm-gc] Debugger: Provide type info for structs and arrays in tables
This change also modifies the way references are typed: Instead of
using the static type (which may be a generic type like anyref) the
actual type based on the referenced object is used.
While this is very useful for arrays and structs (and somewhat nice for
i31 not just being a number but also having some type information), it
means for non-null values that the reference type is "not nullable",
so it will show e.g. "ref $type0" although the static type  might be
"ref null $type0".

Bug: v8:7748
Change-Id: I00c3258b0da6f89ec5efffd2a963889b1f341c3a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3852485
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82753}
2022-08-26 14:51:20 +00:00

173 lines
6.2 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 || message.result.exceptionDetails) {
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;
let description = entry.value.description;
// For tables containing references, explore one level into the ref.
if (entry.value.objectId != null) {
let referencedObj = await Protocol.Runtime.getProperties(
{objectId: entry.value.objectId});
let value = referencedObj.result.result
.filter(prop => prop.name == "value")[0].value.description;
description = `${value} (${description})`;
}
functions.push(`${entry.name}: ${description}`);
}
const functions_str = functions.join(', ');
tables_str.push(` ${table.name}: ${functions_str}`);
}
return '\n' + tables_str.join('\n');
}