1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
// 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('Tests stepping through wasm scripts by byte offsets');
session.setupScriptMap();
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
var builder = new WasmModuleBuilder();
var func_a_idx =
builder.addFunction('wasm_A', kSig_v_i).addBody([kExprNop, kExprNop]).index;
// wasm_B calls wasm_A <param0> times.
var func_b = builder.addFunction('wasm_B', kSig_v_i)
.addBody([
// clang-format off
kExprLoop, kWasmStmt, // while
kExprLocalGet, 0, // -
kExprIf, kWasmStmt, // if <param0> != 0
kExprLocalGet, 0, // -
kExprI32Const, 1, // -
kExprI32Sub, // -
kExprLocalSet, 0, // decrease <param0>
...wasmI32Const(1024), // some longer i32 const (2 byte imm)
kExprCallFunction, func_a_idx, // -
kExprBr, 1, // continue
kExprEnd, // -
kExprEnd, // break
// clang-format on
])
.exportAs('main');
let fact = builder.addFunction('fact', kSig_i_i)
.addLocals({i32_count: 1})
.addBody([
// clang-format off
kExprLocalGet, 0,
kExprIf, kWasmI32, // if <param0> != 0
kExprLocalGet, 0,
kExprI32Const, 1,
kExprI32Sub,
kExprCallFunction, 2,
kExprLocalGet, 0,
kExprI32Mul, // return fact(<param0> - 1) * <param0>
kExprElse, // else
kExprI32Const, 1, // return 1
kExprEnd,
// clang-format on
])
.exportAs('fact');
var module_bytes = builder.toArray();
function instantiate(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;
}
var module = new WebAssembly.Module(buffer);
// Set global variable.
instance = new WebAssembly.Instance(module);
}
(async function test() {
for (const action of ['stepInto', 'stepOver', 'stepOut', 'resume'])
InspectorTest.logProtocolCommandCalls('Debugger.' + action);
await Protocol.Debugger.enable();
InspectorTest.log('Setting up global instance variable.');
Protocol.Runtime.evaluate({
expression: `var instance;` +
`(${instantiate.toString()})(${JSON.stringify(module_bytes)})`
});
const [, {params: wasmScript}] = await Protocol.Debugger.onceScriptParsed(2);
InspectorTest.log('Got wasm script: ' + wasmScript.url);
// Set the breakpoint on a non-breakable position. This should resolve to the
// next instruction.
var offset = func_b.body_offset + 15;
InspectorTest.log(
`Setting breakpoint on offset ` + offset + ` (should be propagated to ` +
(offset + 1) + `, the offset of the call), url ${wasmScript.url}`);
let bpmsg = await Protocol.Debugger.setBreakpoint({
location: {scriptId: wasmScript.scriptId, lineNumber: 0, columnNumber: offset}
});
InspectorTest.logMessage(bpmsg.result.actualLocation);
Protocol.Runtime.evaluate({ expression: 'instance.exports.main(4)' });
await waitForPauseAndStep('stepInto'); // into call to wasm_A
await waitForPauseAndStep('stepOver'); // over first nop
await waitForPauseAndStep('stepOut'); // out of wasm_A
await waitForPauseAndStep('stepOut'); // out of wasm_B, stop on breakpoint
await waitForPauseAndStep('stepOver'); // over call
await waitForPauseAndStep('stepInto'); // == stepOver br
await waitForPauseAndStep('resume'); // to next breakpoint (3rd iteration)
await waitForPauseAndStep('stepInto'); // into wasm_A
await waitForPauseAndStep('stepOut'); // out to wasm_B
// Now step 10 times, until we are in wasm_A again.
for (let i = 0; i < 10; ++i) await waitForPauseAndStep('stepInto');
// 3 more times, back to wasm_B.
for (let i = 0; i < 3; ++i) await waitForPauseAndStep('stepInto');
// Then just resume.
await waitForPauseAndStep('resume');
InspectorTest.log('exports.main returned!');
InspectorTest.log('Test stepping over a recursive call');
// Set a breakpoint at the recursive call and run.
offset = fact.body_offset + 9; // Offset of the recursive call instruction.
InspectorTest.log(
`Setting breakpoint on the recursive call instruction @+` + offset +
`, url ${wasmScript.url}`);
bpmsg = await Protocol.Debugger.setBreakpoint({
location: {scriptId: wasmScript.scriptId, lineNumber: 0, columnNumber: offset}
});
actualLocation = bpmsg.result.actualLocation;
InspectorTest.logMessage(actualLocation);
Protocol.Runtime.evaluate({ expression: 'instance.exports.fact(4)' });
await waitForPause();
// Remove the breakpoint before stepping over.
InspectorTest.log('Removing breakpoint');
let breakpointId = bpmsg.result.breakpointId;
await Protocol.Debugger.removeBreakpoint({breakpointId});
await Protocol.Debugger.stepOver();
await waitForPauseAndStep('resume');
InspectorTest.log('Finished!');
})().catch(reason => InspectorTest.log(`Failed: ${reason}`))
.finally(InspectorTest.completeTest);
async function waitForPauseAndStep(stepAction) {
await waitForPause();
Protocol.Debugger[stepAction]();
}
async function waitForPause() {
const {params: {callFrames}} = await Protocol.Debugger.oncePaused();
await session.logSourceLocation(callFrames[0].location);
}
|