v8/test/inspector/wasm-inspector-test.js
Benedikt Meurer a23adbbc5a [inspector][wasm] Improve Scope view and instance preview.
This adds the following internal properties to `WasmInstanceObject`
values in DevTools:

 - `[[Module]]` pointing to the `WasmModuleObject`, allowing the
   developer to find the module to an instance no matter where in
   DevTools front-end the instance is inspected.
 - `[[Functions]]`, `[[Globals]]`, `[[Memories]]`, and `[[Tables]]`
   are shown (when they aren't empty), allowing developers to inspect
   the entities within an instance no matter where in DevTools front-end
   it's inspected.

This also updates the _Module_ scope for Wasm frames to show the entity
containers (`functions`, `globals`, `memories` and `tables`) in addition
to the `instance` and `module` to make it easier accessible (fewer
clicks to get there), but also to align it better with the _Add property
path to Watch_ and _Copy property path_ features (since exactly the same
names are exposed via Debug Evaluate on Wasm frames).

```
> Stack
> Locals
v Module
  > module
  > instance
  > functions
  > globals
  > memories
  > tables
```

Drive-by-fix: Move GetWasmModuleObjectInternalProperties() logic into
debug-wasm-support.cc

Screenshot: https://imgur.com/ksEHG2I.png
Doc: http://bit.ly/devtools-wasm-entities
Fixed: chromium:1165294
Bug: chromium:1071432, chromium:1164241, chromium:1165304
Change-Id: Ia88fb2705287c79988ff2b432e4a33ac34e098f5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2622912
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72042}
2021-01-12 15:13:49 +00:00

105 lines
3.6 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 = (code, url) =>
Protocol.Runtime
.evaluate({'expression': code + '\n//# sourceURL=v8://test/' + url})
.then(printIfFailure);
WasmInspectorTest.instantiateFromBuffer = function(bytes, imports) {
var buffer = new ArrayBuffer(bytes.length);
var view = new Uint8Array(buffer);
for (var i = 0; i < bytes.length; ++i) {
view[i] = bytes[i] | 0;
}
const module = new WebAssembly.Module(buffer);
return new WebAssembly.Instance(module, imports);
}
WasmInspectorTest.instantiate = async function(bytes, instance_name = 'instance') {
const instantiate_code = `var ${instance_name} = (${WasmInspectorTest.instantiateFromBuffer})(${JSON.stringify(bytes)});`;
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 = value => {
return value.unserializableValue ?? value.value;
}
function printIfFailure(message) {
if (!message.result) {
InspectorTest.logMessage(message);
}
return message;
}
async function getScopeValues(name, value) {
if (value.type === 'object') {
if (value.subtype === 'typedarray' || value.subtype == 'webassemblymemory') return value.description;
if (name === 'instance') return dumpInstanceProperties(value);
if (name === 'module') return value.description;
let msg = await Protocol.Runtime.getProperties({objectId: value.objectId});
printIfFailure(msg);
const printProperty = function({name, value}) {
if ('className' in value) {
return `"${name}" (${value.className})`;
}
return `"${name}": ${WasmInspectorTest.getWasmValue(value)} (${value.subtype ?? value.type})`;
}
return msg.result.result.map(printProperty).join(', ');
}
return `${WasmInspectorTest.getWasmValue(value)} (${value.subtype ?? value.type})`;
}
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);
const printExports = function(value) {
return `"${value.name}" (${value.value.className})`;
}
const formattedExports = exports.result.result.map(printExports).join(', ');
return `${exportsName}: ${formattedExports}`
}