v8/test/inspector/debugger/wasm-evaluate-on-call-frame.js
Benedikt Meurer 987a7f4ae4 [inspector] Send type as description for WasmValueObject.
Also block sending "type" as part of the ObjectPreview, but only send
the "value" property. The front-end will be updated to display
WasmValueObject's similar to what we do for wrapper objects (i.e.
StringWrapper and the like). The matching front-end change is still
pending.

Also refactor the WasmValueObject to have dedicated constructors for
the individual types (i32, i64, f32, f64, externref and v128). This
way we can just reuse the existing logic in descriptionForObject()
and we also don't need to store the "type" on the object itself (not
really performance sensitive, but fewer moving parts / things that
can go wrong).

This also addresses the crash in https://crbug.com/1166077#c16 since
the WasmValueObject instances now have a proper JSFunction in their
maps' constructor_or_backpointer slot and are thus able to locate
their creation context. Note that this doesn't generally address
https://crbug.com/1166077 itself, but only the WasmValueObject case.

Screenshot: https://imgur.com/kbd3bix.png
Bug: chromium:1170282, chromium:1071432
Bug: chromium:1159402, chromium:1166077
Change-Id: Iae649cad155efd774cfb1f4eea8cf406e413c03a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2692574
Reviewed-by: Philip Pfaffe <pfaffe@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#72736}
2021-02-15 11:41:50 +00:00

351 lines
14 KiB
JavaScript

// Copyright 2021 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/inspector/wasm-inspector-test.js');
let {session, contextGroup, Protocol} =
InspectorTest.start('Test wasm debug evaluate');
Protocol.Debugger.enable();
Protocol.Runtime.enable();
async function compileModule(builder) {
const moduleBytes = JSON.stringify(builder.toArray());
const expression = `new WebAssembly.Module(new Uint8Array(${moduleBytes}).buffer);`;
const {result: {scriptId}} = await Protocol.Runtime.compileScript({expression, sourceURL: 'v8://test/compileModule', persistScript: true});
const [{params}, {result}] = await Promise.all([
Protocol.Debugger.onceScriptParsed(),
Protocol.Runtime.runScript({scriptId})
]);
return [result.result, params.scriptId];
}
async function instantiateModule({objectId}, importObject) {
const {result: {result}} = await Protocol.Runtime.callFunctionOn({
arguments: importObject ? [importObject] : [],
functionDeclaration: 'function(importObject) { return new WebAssembly.Instance(this, importObject); }',
objectId
});
return result;
}
async function dumpOnCallFrame(callFrameId, expression) {
const {result: {result: object}} = await Protocol.Debugger.evaluateOnCallFrame({
callFrameId, expression
});
if (object.type === 'object' && object.subtype === 'wasmvalue') {
const {result: {result: properties}} = await Protocol.Runtime.getProperties({objectId: object.objectId, ownProperties: true})
const valueProperty = properties.find(p => p.name === 'value');
InspectorTest.log(`> ${expression} = ${object.description} {${valueProperty.value.description}}`);
} else if ('description' in object) {
InspectorTest.log(`> ${expression} = ${object.description}`);
} else {
InspectorTest.log(`> ${expression} = ${JSON.stringify(object.value)}`);
}
}
async function dumpKeysOnCallFrame(callFrameId, object, keys) {
for (const key of keys) {
await dumpOnCallFrame(callFrameId, `${object}[${JSON.stringify(key)}]`);
if (typeof key === 'string' && key.indexOf('.') < 0) {
await dumpOnCallFrame(callFrameId, `${key}`);
}
}
}
async function waitForDebuggerPaused() {
const {params: {callFrames: [{callFrameId, functionName}]}} = await Protocol.Debugger.oncePaused();
InspectorTest.log(`Debugger paused in ${functionName}.`);
return callFrameId;
}
InspectorTest.runAsyncTestSuite([
async function testInstanceAndModule() {
const builder = new WasmModuleBuilder();
const main = builder.addFunction('main', kSig_v_v)
.addBody([]).exportFunc();
InspectorTest.log('Compile module.');
const [module, scriptId] = await compileModule(builder);
InspectorTest.log('Set breakpoint in main.');
await Protocol.Debugger.setBreakpoint({
location: {scriptId, lineNumber: 0, columnNumber: main.body_offset}
});
InspectorTest.log('Instantiate module.');
const instance = await instantiateModule(module);
InspectorTest.log('Call main.');
const callMainPromise = Protocol.Runtime.callFunctionOn({
functionDeclaration: `function() { return this.exports.main(); }`,
objectId: instance.objectId
});
const callFrameId = await waitForDebuggerPaused();
await dumpOnCallFrame(callFrameId, `instance`);
await dumpOnCallFrame(callFrameId, `module`);
await Protocol.Debugger.resume();
await callMainPromise;
},
async function testGlobals() {
const builder = new WasmModuleBuilder();
const global0 = builder.addGlobal(kWasmI32, true); // global0
const global1 = builder.addGlobal(kWasmI32, true).exportAs('global0');
const global2 = builder.addGlobal(kWasmI64, true).exportAs('global3');
const global3 = builder.addGlobal(kWasmI64, true); // global3
const main = builder.addFunction('main', kSig_v_v)
.addBody([
kExprI64Const, 42,
kExprGlobalSet, global3.index,
]).exportFunc();
const start = builder.addFunction('start', kSig_v_v)
.addBody([
kExprNop,
kExprI32Const, 0,
kExprGlobalSet, global0.index,
kExprI32Const, 1,
kExprGlobalSet, global1.index,
kExprI64Const, 2,
kExprGlobalSet, global2.index,
kExprI64Const, 3,
kExprGlobalSet, global3.index,
]);
builder.addStart(start.index);
const KEYS = [0, 1, 2, 3, '$global0', '$global3'];
InspectorTest.log('Compile module.');
const [module, scriptId] = await compileModule(builder);
InspectorTest.log('Set breakpoint in main.');
await Protocol.Debugger.setBreakpoint({
location: {scriptId, lineNumber: 0, columnNumber: main.body_offset}
});
InspectorTest.log('Instantiate module.');
const instance = await instantiateModule(module);
InspectorTest.log('Call main.');
const callMainPromise = Protocol.Runtime.callFunctionOn({
functionDeclaration: `function() { return this.exports.main(); }`,
objectId: instance.objectId
});
let callFrameId = await waitForDebuggerPaused();
await dumpOnCallFrame(callFrameId, `globals`);
await dumpOnCallFrame(callFrameId, `typeof globals`);
await dumpOnCallFrame(callFrameId, `Object.keys(globals)`);
await dumpKeysOnCallFrame(callFrameId, "globals", KEYS);
InspectorTest.log(`Stepping twice in main.`)
await Protocol.Debugger.stepOver(); // i32.const 42
await Protocol.Debugger.stepOver(); // global.set $global3
callFrameId = await waitForDebuggerPaused();
await dumpKeysOnCallFrame(callFrameId, "globals", KEYS);
InspectorTest.log('Changing global from JavaScript.')
await Protocol.Debugger.evaluateOnCallFrame({
callFrameId,
expression: `instance.exports.global0.value = 21`
});
await dumpKeysOnCallFrame(callFrameId, "globals", KEYS);
await Protocol.Debugger.resume();
await callMainPromise;
},
async function testFunctions() {
const builder = new WasmModuleBuilder();
builder.addImport('foo', 'bar', kSig_v_v);
const main = builder.addFunction('main', kSig_i_v)
.addBody([
kExprI32Const, 0,
]).exportFunc();
builder.addFunction('func2', kSig_i_v)
.addBody([
kExprI32Const, 1,
]);
builder.addFunction(undefined, kSig_i_v)
.addBody([
kExprI32Const, 2,
]).exportAs('func2');
builder.addFunction(undefined, kSig_i_v)
.addBody([
kExprI32Const, 3,
]);
const KEYS = [0, 1, 2, 3, 4, '$foo.bar', '$main', '$func2', '$func4'];
InspectorTest.log('Compile module.');
const [module, scriptId] = await compileModule(builder);
InspectorTest.log('Set breakpoint in main.');
await Protocol.Debugger.setBreakpoint({
location: {scriptId, lineNumber: 0, columnNumber: main.body_offset}
});
InspectorTest.log('Instantiate module.');
const {result: { result: importObject }} = await Protocol.Runtime.evaluate({
expression: `({foo: {bar() { }}})`
});
const instance = await instantiateModule(module, importObject);
InspectorTest.log('Call main.');
const callMainPromise = Protocol.Runtime.callFunctionOn({
functionDeclaration: `function() { return this.exports.main(); }`,
objectId: instance.objectId
});
let callFrameId = await waitForDebuggerPaused();
await dumpOnCallFrame(callFrameId, `functions`);
await dumpOnCallFrame(callFrameId, `typeof functions`);
await dumpOnCallFrame(callFrameId, `Object.keys(functions)`);
await dumpKeysOnCallFrame(callFrameId, "functions", KEYS);
await Protocol.Debugger.resume();
await callMainPromise;
},
async function testLocals() {
const builder = new WasmModuleBuilder();
const main = builder.addFunction('main', kSig_v_ii, ['x', 'x'])
.addLocals(kWasmI32, 1)
.addBody([
kExprI32Const, 42,
kExprLocalSet, 2
]).exportFunc();
const KEYS = [0, 1, 2, '$x', '$var2'];
InspectorTest.log('Compile module.');
const [module, scriptId] = await compileModule(builder);
InspectorTest.log('Set breakpoint in main.');
await Protocol.Debugger.setBreakpoint({
location: {scriptId, lineNumber: 0, columnNumber: main.body_offset}
});
InspectorTest.log('Instantiate module.');
const instance = await instantiateModule(module);
InspectorTest.log('Call main.');
const callMainPromise = Protocol.Runtime.callFunctionOn({
functionDeclaration: `function() { return this.exports.main(3, 6); }`,
objectId: instance.objectId
});
let callFrameId = await waitForDebuggerPaused();
await dumpOnCallFrame(callFrameId, `locals`);
await dumpOnCallFrame(callFrameId, `typeof locals`);
await dumpOnCallFrame(callFrameId, `Object.keys(locals)`);
await dumpKeysOnCallFrame(callFrameId, "locals", KEYS);
InspectorTest.log(`Stepping twice in main.`)
await Protocol.Debugger.stepOver(); // i32.const 42
await Protocol.Debugger.stepOver(); // local.set $var2
callFrameId = await waitForDebuggerPaused();
await dumpKeysOnCallFrame(callFrameId, "locals", KEYS);
await Protocol.Debugger.resume();
await callMainPromise;
},
async function testMemories() {
const builder = new WasmModuleBuilder();
builder.addMemory(1, 1).exportMemoryAs('foo');
const main = builder.addFunction('main', kSig_v_v)
.addBody([kExprNop]).exportFunc();
const KEYS = [0, '$foo'];
InspectorTest.log('Compile module.');
const [module, scriptId] = await compileModule(builder);
InspectorTest.log('Set breakpoint in main.');
await Protocol.Debugger.setBreakpoint({
location: {scriptId, lineNumber: 0, columnNumber: main.body_offset}
});
InspectorTest.log('Instantiate module.');
const instance = await instantiateModule(module);
InspectorTest.log('Call main.');
const callMainPromise = Protocol.Runtime.callFunctionOn({
functionDeclaration: `function() { return this.exports.main(); }`,
objectId: instance.objectId
});
let callFrameId = await waitForDebuggerPaused();
await dumpOnCallFrame(callFrameId, `memories`);
await dumpOnCallFrame(callFrameId, `typeof memories`);
await dumpOnCallFrame(callFrameId, `Object.keys(memories)`);
await dumpKeysOnCallFrame(callFrameId, "memories", KEYS);
await Protocol.Debugger.resume();
await callMainPromise;
},
async function testTables() {
const builder = new WasmModuleBuilder();
builder.addTable(kWasmFuncRef, 2).exportAs('bar');
const main = builder.addFunction('main', kSig_v_v)
.addBody([kExprNop]).exportFunc();
const KEYS = [0, '$bar'];
InspectorTest.log('Compile module.');
const [module, scriptId] = await compileModule(builder);
InspectorTest.log('Set breakpoint in main.');
await Protocol.Debugger.setBreakpoint({
location: {scriptId, lineNumber: 0, columnNumber: main.body_offset}
});
InspectorTest.log('Instantiate module.');
const instance = await instantiateModule(module);
InspectorTest.log('Call main.');
const callMainPromise = Protocol.Runtime.callFunctionOn({
functionDeclaration: `function() { return this.exports.main(); }`,
objectId: instance.objectId
});
let callFrameId = await waitForDebuggerPaused();
await dumpOnCallFrame(callFrameId, `tables`);
await dumpOnCallFrame(callFrameId, `typeof tables`);
await dumpOnCallFrame(callFrameId, `Object.keys(tables)`);
await dumpKeysOnCallFrame(callFrameId, "tables", KEYS);
await Protocol.Debugger.resume();
await callMainPromise;
},
async function testStack() {
const builder = new WasmModuleBuilder();
const main = builder.addFunction('main', kSig_i_i)
.addBody([
kExprLocalGet, 0,
kExprI32Const, 42,
kExprI32Add,
]).exportFunc();
InspectorTest.log('Compile module.');
const [module, scriptId] = await compileModule(builder);
InspectorTest.log('Set breakpoint in main.');
await Protocol.Debugger.setBreakpoint({
location: {scriptId, lineNumber: 0, columnNumber: main.body_offset}
});
InspectorTest.log('Instantiate module.');
const instance = await instantiateModule(module);
InspectorTest.log('Call main.');
const callMainPromise = Protocol.Runtime.callFunctionOn({
functionDeclaration: `function() { return this.exports.main(5); }`,
objectId: instance.objectId
});
let callFrameId = await waitForDebuggerPaused();
await dumpOnCallFrame(callFrameId, `stack`);
await dumpOnCallFrame(callFrameId, `typeof stack`);
await dumpOnCallFrame(callFrameId, `Object.keys(stack)`);
InspectorTest.log(`Stepping twice in main.`)
await Protocol.Debugger.stepOver(); // local.get $var0
await Protocol.Debugger.stepOver(); // i32.const 42
callFrameId = await waitForDebuggerPaused();
await dumpOnCallFrame(callFrameId, `stack`);
await dumpOnCallFrame(callFrameId, `Object.keys(stack)`);
await dumpKeysOnCallFrame(callFrameId, "stack", [0, 1]);
await Protocol.Debugger.resume();
await callMainPromise;
}
]);