зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1447591: Stub out a few Debugger APIs for wasm; r=yury
--HG-- extra : rebase_source : b8c8b2afeac3a3bdefa8f412354579f601a3af86
This commit is contained in:
Родитель
7549087a70
Коммит
1c9f5a8af9
|
@ -792,8 +792,10 @@ const SourceActor = ActorClassWithSpec(sourceSpec, {
|
|||
|
||||
if (!this.isSourceMapped) {
|
||||
const generatedLocation = GeneratedLocation.fromOriginalLocation(originalLocation);
|
||||
const isWasm = this.source && this.source.introductionType === "wasm";
|
||||
if (!this._setBreakpointAtGeneratedLocation(actor, generatedLocation) &&
|
||||
!noSliding) {
|
||||
!noSliding &&
|
||||
!isWasm) {
|
||||
const query = { line: originalLine };
|
||||
// For most cases, we have a real source to query for. The
|
||||
// only time we don't is for HTML pages. In that case we want
|
||||
|
|
|
@ -26,7 +26,10 @@ function run_test() {
|
|||
gClient, "test-stack",
|
||||
function(response, tabClient, threadClient) {
|
||||
gThreadClient = threadClient;
|
||||
gThreadClient.reconfigure({ observeAsmJS: true }, function(response) {
|
||||
gThreadClient.reconfigure({
|
||||
observeAsmJS: true,
|
||||
wasmBinarySource: true
|
||||
}, function(response) {
|
||||
Assert.equal(!!response.error, false);
|
||||
test_pause_frame();
|
||||
});
|
||||
|
|
|
@ -155,7 +155,7 @@ function create_wasmModule() {
|
|||
const fooModuleCode = wasmTextToBinary(`(module
|
||||
(func $foo (result i32) (i32.const 42))
|
||||
(export "foo" $foo)
|
||||
)`, 'new-format');
|
||||
)`);
|
||||
|
||||
WebAssembly.compile(fooModuleCode).then((m) => {
|
||||
ok(m instanceof WebAssembly.Module, "The WasmModule has been compiled.");
|
||||
|
|
|
@ -659,28 +659,56 @@ WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
|
|||
if (!twoByteChars.initTwoByte(cx, args[0].toString()))
|
||||
return false;
|
||||
|
||||
bool withOffsets = false;
|
||||
if (args.hasDefined(1)) {
|
||||
if (!args[1].isString()) {
|
||||
ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a String");
|
||||
if (!args[1].isBoolean()) {
|
||||
ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a boolean");
|
||||
return false;
|
||||
}
|
||||
withOffsets = ToBoolean(args[1]);
|
||||
}
|
||||
|
||||
uintptr_t stackLimit = GetNativeStackLimit(cx);
|
||||
|
||||
wasm::Bytes bytes;
|
||||
UniqueChars error;
|
||||
if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes, &error)) {
|
||||
wasm::Uint32Vector offsets;
|
||||
if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes, &offsets, &error)) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
|
||||
error.get() ? error.get() : "out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject obj(cx, JS_NewUint8Array(cx, bytes.length()));
|
||||
RootedObject binary(cx, JS_NewUint8Array(cx, bytes.length()));
|
||||
if (!binary)
|
||||
return false;
|
||||
|
||||
memcpy(binary->as<TypedArrayObject>().viewDataUnshared(), bytes.begin(), bytes.length());
|
||||
|
||||
if (!withOffsets) {
|
||||
args.rval().setObject(*binary);
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject obj(cx, JS_NewPlainObject(cx));
|
||||
if (!obj)
|
||||
return false;
|
||||
|
||||
memcpy(obj->as<TypedArrayObject>().viewDataUnshared(), bytes.begin(), bytes.length());
|
||||
constexpr unsigned propAttrs = JSPROP_ENUMERATE;
|
||||
if (!JS_DefineProperty(cx, obj, "binary", binary, propAttrs))
|
||||
return false;
|
||||
|
||||
RootedObject jsOffsets(cx, JS_NewArrayObject(cx, offsets.length()));
|
||||
if (!jsOffsets)
|
||||
return false;
|
||||
for (size_t i = 0; i < offsets.length(); i++) {
|
||||
uint32_t offset = offsets[i];
|
||||
RootedValue offsetVal(cx, NumberValue(offset));
|
||||
if (!JS_SetElement(cx, jsOffsets, i, offsetVal))
|
||||
return false;
|
||||
}
|
||||
if (!JS_DefineProperty(cx, obj, "offsets", jsOffsets, propAttrs))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*obj);
|
||||
return true;
|
||||
|
|
|
@ -116,14 +116,6 @@ function wasmFullPass(text, expected, maybeImports, ...args) {
|
|||
let instance = new WebAssembly.Instance(module, maybeImports);
|
||||
assertEq(typeof instance.exports.run, 'function', "A 'run' function must be exported.");
|
||||
assertEq(instance.exports.run(...args), expected, "Initial module must return the expected result.");
|
||||
|
||||
let retext = wasmBinaryToText(binary);
|
||||
let rebinary = wasmTextToBinary(retext);
|
||||
|
||||
assertEq(WebAssembly.validate(rebinary), true, "Recreated binary must validate.");
|
||||
let remodule = new WebAssembly.Module(rebinary);
|
||||
let reinstance = new WebAssembly.Instance(remodule, maybeImports);
|
||||
assertEq(reinstance.exports.run(...args), expected, "Reformed module must return the expected result");
|
||||
}
|
||||
|
||||
// Ditto, but expects a function named '$run' instead of exported with this name.
|
||||
|
@ -134,15 +126,15 @@ function wasmFullPassI64(text, expected, maybeImports, ...args) {
|
|||
let augmentedSrc = _augmentSrc(text, [ { type: 'i64', func: '$run', args, expected } ]);
|
||||
let augmentedBinary = wasmTextToBinary(augmentedSrc);
|
||||
new WebAssembly.Instance(new WebAssembly.Module(augmentedBinary), maybeImports).exports.assert_0();
|
||||
|
||||
let retext = wasmBinaryToText(augmentedBinary);
|
||||
new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(retext)), maybeImports).exports.assert_0();
|
||||
}
|
||||
|
||||
function wasmRunWithDebugger(wast, lib, init, done) {
|
||||
let g = newGlobal('');
|
||||
let dbg = new Debugger(g);
|
||||
|
||||
// Enable binary source mode.
|
||||
dbg.allowWasmBinarySource = true;
|
||||
|
||||
g.eval(`
|
||||
var wasm = wasmTextToBinary('${wast}');
|
||||
var lib = ${lib || 'undefined'};
|
||||
|
@ -160,19 +152,6 @@ var m = new WebAssembly.Instance(new WebAssembly.Module(wasm), lib);`);
|
|||
done({dbg, result, error, wasmScript, g,});
|
||||
}
|
||||
|
||||
function wasmGetScriptBreakpoints(wasmScript) {
|
||||
var result = [];
|
||||
var sourceText = wasmScript.source.text;
|
||||
sourceText.split('\n').forEach(function (line, i) {
|
||||
var lineOffsets = wasmScript.getLineOffsets(i + 1);
|
||||
if (lineOffsets.length === 0)
|
||||
return;
|
||||
assertEq(lineOffsets.length, 1);
|
||||
result.push({str: line.trim(), line: i + 1, offset: lineOffsets[0]});
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
const WasmHelpers = {};
|
||||
|
||||
(function() {
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
// |jit-test| test-also-no-wasm-baseline
|
||||
// Tests that wasm module scripts have text line to bytecode offset information
|
||||
// when source text is generated.
|
||||
|
||||
load(libdir + "asserts.js");
|
||||
|
||||
if (!wasmDebuggingIsSupported())
|
||||
quit();
|
||||
|
||||
// Checking if experimental format generates internal source map to binary file
|
||||
// by querying debugger scripts getLineOffsets.
|
||||
// (Notice that the source map will not be produced by wasmBinaryToText)
|
||||
function getAllOffsets(wast) {
|
||||
var sandbox = newGlobal('');
|
||||
var dbg = new Debugger();
|
||||
dbg.addDebuggee(sandbox);
|
||||
sandbox.eval(`
|
||||
var wasm = wasmTextToBinary('${wast}');
|
||||
var m = new WebAssembly.Instance(new WebAssembly.Module(wasm));
|
||||
`);
|
||||
var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0];
|
||||
var lines = wasmScript.source.text.split('\n');
|
||||
return lines.map((l, n) => { return { str: l, offsets: wasmScript.getLineOffsets(n + 1) }; });
|
||||
}
|
||||
|
||||
var result1 = getAllOffsets('(module \
|
||||
(func (nop)) \
|
||||
(func (drop (f32.sqrt (f32.add (f32.const 1.0) (f32.const 2.0))))) \
|
||||
)');
|
||||
|
||||
var nopLine = result1.filter(i => i.str.indexOf('nop') >= 0);
|
||||
assertEq(nopLine.length, 1);
|
||||
// The nopLine shall have single offset.
|
||||
assertEq(nopLine[0].offsets.length, 1);
|
||||
assertEq(nopLine[0].offsets[0] > 0, true);
|
||||
|
||||
var singleOffsetLines = result1.filter(i => i.offsets.length === 1);
|
||||
// There shall be total 8 lines with single offset.
|
||||
assertEq(singleOffsetLines.length, 8);
|
||||
|
||||
// Checking if all reported offsets can be resolved back to the corresponding
|
||||
// line number.
|
||||
function checkOffsetLineNumberMapping(wast, offsetsExpected) {
|
||||
var sandbox = newGlobal('');
|
||||
var dbg = new Debugger();
|
||||
dbg.addDebuggee(sandbox);
|
||||
sandbox.eval(`
|
||||
var wasm = wasmTextToBinary('${wast}');
|
||||
var module = new WebAssembly.Module(wasm);
|
||||
imports = {}
|
||||
for (let descriptor of WebAssembly.Module.imports(module)) {
|
||||
imports[descriptor.module] = {}
|
||||
switch(descriptor.kind) {
|
||||
case "function": imports[descriptor.module][descriptor.name] = new Function(''); break;
|
||||
}
|
||||
}
|
||||
var instance = new WebAssembly.Instance(module, imports);
|
||||
`);
|
||||
var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0];
|
||||
assertEq(wasmScript.startLine, 1);
|
||||
assertEq(wasmScript.lineCount >= 0, true);
|
||||
var lines = wasmScript.source.text.split('\n');
|
||||
var offsetsFound = 0;
|
||||
lines.forEach(function (l, n) {
|
||||
var offsets = wasmScript.getLineOffsets(n + 1);
|
||||
if (offsets.length < 1) return;
|
||||
assertEq(offsets.length, 1);
|
||||
assertEq(offsets[0] > 0, true);
|
||||
offsetsFound++;
|
||||
var loc = wasmScript.getOffsetLocation(offsets[0]);
|
||||
assertEq(loc instanceof Object, true);
|
||||
assertEq(loc.isEntryPoint, true);
|
||||
assertEq(loc.lineNumber, n + 1);
|
||||
assertEq(loc.columnNumber > 0, true);
|
||||
});
|
||||
assertEq(offsetsFound, offsetsExpected);
|
||||
}
|
||||
|
||||
checkOffsetLineNumberMapping('(module (func))', 1);
|
||||
checkOffsetLineNumberMapping('(module (func (nop)))', 2);
|
||||
checkOffsetLineNumberMapping('(module (import "a" "b"))', 0);
|
||||
checkOffsetLineNumberMapping('(module \
|
||||
(func (nop)) \
|
||||
(func (drop (f32.sqrt (f32.add (f32.const 1.0) (f32.const 2.0))))) \
|
||||
)', 8);
|
||||
checkOffsetLineNumberMapping('(module \
|
||||
(func (local i32) i32.const 0 i32.const 1 set_local 0 get_local 0 call 0 i32.add nop drop) \
|
||||
)', 9);
|
||||
|
||||
// Checking that there are no offsets are present in a wasm instance script for
|
||||
// which debug mode was not enabled.
|
||||
function getWasmScriptAfterDebuggerAttached(wast) {
|
||||
var sandbox = newGlobal('');
|
||||
var dbg = new Debugger();
|
||||
sandbox.eval(`
|
||||
var wasm = wasmTextToBinary('${wast}');
|
||||
var m = new WebAssembly.Instance(new WebAssembly.Module(wasm));
|
||||
`);
|
||||
// Attaching after wasm instance is created.
|
||||
dbg.addDebuggee(sandbox);
|
||||
var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0];
|
||||
return wasmScript;
|
||||
}
|
||||
|
||||
var wasmScript1 = getWasmScriptAfterDebuggerAttached('(module (func (nop)))');
|
||||
var wasmLines1 = wasmScript1.source.text.split('\n');
|
||||
assertEq(wasmScript1.startLine, 1);
|
||||
assertEq(wasmScript1.lineCount, 0);
|
||||
assertEq(wasmLines1.every((l, n) => wasmScript1.getLineOffsets(n + 1).length == 0), true);
|
||||
|
||||
// Checking that we must not resolve any location for any offset in a wasm
|
||||
// instance which debug mode was not enabled.
|
||||
var wasmScript2 = getWasmScriptAfterDebuggerAttached('(module (func (nop)))');
|
||||
for (var i = wasmTextToBinary('(module (func (nop)))').length - 1; i >= 0; i--)
|
||||
assertThrowsInstanceOf(() => wasmScript2.getOffsetLocation(i), Error);
|
|
@ -15,7 +15,9 @@ wasmRunWithDebugger(
|
|||
function ({dbg}) {
|
||||
offsets = [];
|
||||
dbg.onEnterFrame = function (frame) {
|
||||
if (frame.type != 'wasmcall') return;
|
||||
if (frame.type != 'wasmcall') {
|
||||
return;
|
||||
}
|
||||
offsets.push(frame.offset);
|
||||
frame.onStep = function () {
|
||||
offsets.push(frame.offset);
|
||||
|
@ -24,16 +26,16 @@ wasmRunWithDebugger(
|
|||
offsets.push(frame.offset);
|
||||
};
|
||||
};
|
||||
},
|
||||
function ({wasmScript, error}) {
|
||||
assertEq(error, undefined);
|
||||
assertEq(offsets.length, 5);
|
||||
offsets.forEach(offset => {
|
||||
var loc = wasmScript.getOffsetLocation(offset);
|
||||
assertEq(loc.isEntryPoint, true);
|
||||
assertEq(loc.lineNumber > 0, true);
|
||||
assertEq(loc.columnNumber > 0, true);
|
||||
assertEq(wasmScript.getLineOffsets(loc.lineNumber).length, 1);
|
||||
});
|
||||
}
|
||||
},
|
||||
function ({wasmScript, error}) {
|
||||
assertEq(error, undefined);
|
||||
assertEq(offsets.length, 5);
|
||||
offsets.forEach(offset => {
|
||||
var loc = wasmScript.getOffsetLocation(offset);
|
||||
assertEq(loc.isEntryPoint, true);
|
||||
assertEq(loc.lineNumber > 0, true);
|
||||
assertEq(loc.columnNumber > 0, true);
|
||||
assertEq(wasmScript.getLineOffsets(loc.lineNumber).length, 1);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -19,8 +19,8 @@ assertEq(s.format, "wasm");
|
|||
|
||||
var source = s.source;
|
||||
|
||||
// The text is generated if wasm binary sources are disabled.
|
||||
assertEq(source.text.includes('module'), true);
|
||||
// The text is never generated with the native Debugger API.
|
||||
assertEq(source.text.includes('module'), false);
|
||||
assertThrowsInstanceOf(() => source.binary, Error);
|
||||
|
||||
// Enable binary sources.
|
||||
|
@ -31,7 +31,7 @@ assertEq(s.format, "wasm");
|
|||
|
||||
var source2 = s.source;
|
||||
|
||||
// The text is '[wasm]' if wasm binary sources are enabled.
|
||||
// The text is predefined if wasm binary sources are enabled.
|
||||
assertEq(source2.text, '[wasm]');
|
||||
// The binary contains Uint8Array which is equal to wasm bytecode;
|
||||
arraysEqual(source2.binary, wasmTextToBinary('(module (func) (export "" 0))'));
|
||||
|
|
|
@ -6,21 +6,55 @@ load(libdir + "wasm.js");
|
|||
if (!wasmDebuggingIsSupported())
|
||||
quit();
|
||||
|
||||
// Checking if we can stop at specified breakpoint.
|
||||
function runTest(wast, initFunc, doneFunc) {
|
||||
let g = newGlobal('');
|
||||
let dbg = new Debugger(g);
|
||||
|
||||
g.eval(`
|
||||
var { binary, offsets } = wasmTextToBinary('${wast}', /* offsets */ true);
|
||||
var m = new WebAssembly.Instance(new WebAssembly.Module(binary));`);
|
||||
|
||||
var { offsets } = g;
|
||||
|
||||
var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0];
|
||||
|
||||
initFunc({
|
||||
dbg,
|
||||
wasmScript,
|
||||
g,
|
||||
breakpoints: offsets
|
||||
});
|
||||
|
||||
let result, error;
|
||||
try {
|
||||
result = g.eval("m.exports.test()");
|
||||
} catch (ex) {
|
||||
error = ex;
|
||||
}
|
||||
|
||||
doneFunc({
|
||||
dbg,
|
||||
result,
|
||||
error,
|
||||
wasmScript,
|
||||
g
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var onBreakpointCalled;
|
||||
wasmRunWithDebugger(
|
||||
|
||||
// Checking if we can stop at specified breakpoint.
|
||||
runTest(
|
||||
'(module (func (nop) (nop)) (export "test" 0))',
|
||||
undefined,
|
||||
function ({wasmScript}) {
|
||||
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
|
||||
function ({wasmScript, breakpoints}) {
|
||||
assertEq(breakpoints.length, 3);
|
||||
assertEq(breakpoints[0].offset > 0, true);
|
||||
assertEq(breakpoints[0] > 0, true);
|
||||
// Checking if breakpoints offsets are in ascending order.
|
||||
assertEq(breakpoints[0].offset < breakpoints[1].offset, true);
|
||||
assertEq(breakpoints[1].offset < breakpoints[2].offset, true);
|
||||
assertEq(breakpoints[0] < breakpoints[1], true);
|
||||
assertEq(breakpoints[1] < breakpoints[2], true);
|
||||
onBreakpointCalled = 0;
|
||||
breakpoints.forEach(function (bp) {
|
||||
var offset = bp.offset;
|
||||
breakpoints.forEach(function (offset) {
|
||||
wasmScript.setBreakpoint(offset, {
|
||||
hit: (frame) => {
|
||||
assertEq(frame.offset, offset);
|
||||
|
@ -36,18 +70,15 @@ wasmRunWithDebugger(
|
|||
);
|
||||
|
||||
// Checking if we can remove breakpoint one by one.
|
||||
wasmRunWithDebugger(
|
||||
runTest(
|
||||
'(module (func (nop) (nop)) (export "test" 0))',
|
||||
undefined,
|
||||
function ({wasmScript}) {
|
||||
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
|
||||
function ({wasmScript, breakpoints}) {
|
||||
onBreakpointCalled = 0;
|
||||
var handlers = [];
|
||||
breakpoints.forEach(function (bp, i) {
|
||||
var offset = bp.offset;
|
||||
breakpoints.forEach(function (offset, i) {
|
||||
wasmScript.setBreakpoint(offset, handlers[i] = {
|
||||
hit: (frame) => {
|
||||
assertEq(frame.offset, breakpoints[0].offset);
|
||||
assertEq(frame.offset, breakpoints[0]);
|
||||
onBreakpointCalled++;
|
||||
// Removing all handlers.
|
||||
handlers.forEach(h => wasmScript.clearBreakpoint(h));
|
||||
|
@ -62,18 +93,15 @@ wasmRunWithDebugger(
|
|||
);
|
||||
|
||||
// Checking if we can remove breakpoint one by one from a breakpoint handler.
|
||||
wasmRunWithDebugger(
|
||||
runTest(
|
||||
'(module (func (nop) (nop)) (export "test" 0))',
|
||||
undefined,
|
||||
function ({wasmScript}) {
|
||||
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
|
||||
function ({wasmScript, breakpoints}) {
|
||||
onBreakpointCalled = 0;
|
||||
var handlers = [];
|
||||
breakpoints.forEach(function (bp, i) {
|
||||
var offset = bp.offset;
|
||||
breakpoints.forEach(function (offset, i) {
|
||||
wasmScript.setBreakpoint(offset, handlers[i] = {
|
||||
hit: (frame) => {
|
||||
assertEq(frame.offset, breakpoints[0].offset);
|
||||
assertEq(frame.offset, breakpoints[0]);
|
||||
onBreakpointCalled++;
|
||||
// Removing all handlers.
|
||||
handlers.forEach(h => wasmScript.clearBreakpoint(h));
|
||||
|
@ -90,16 +118,13 @@ wasmRunWithDebugger(
|
|||
// Checking if we can remove breakpoint one by one from onEnterFrame,
|
||||
// but onStep will still work.
|
||||
var onStepCalled;
|
||||
wasmRunWithDebugger(
|
||||
runTest(
|
||||
'(module (func (nop) (nop)) (export "test" 0))',
|
||||
undefined,
|
||||
function ({dbg, wasmScript}) {
|
||||
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
|
||||
function ({dbg, wasmScript, breakpoints}) {
|
||||
onBreakpointCalled = 0;
|
||||
onStepCalled = [];
|
||||
var handlers = [];
|
||||
breakpoints.forEach(function (bp, i) {
|
||||
var offset = bp.offset;
|
||||
breakpoints.forEach(function (offset, i) {
|
||||
wasmScript.setBreakpoint(offset, handlers[i] = {
|
||||
hit: (frame) => {
|
||||
assertEq(false, true);
|
||||
|
@ -125,17 +150,14 @@ wasmRunWithDebugger(
|
|||
);
|
||||
|
||||
// Checking if we can remove all breakpoints.
|
||||
wasmRunWithDebugger(
|
||||
runTest(
|
||||
'(module (func (nop) (nop)) (export "test" 0))',
|
||||
undefined,
|
||||
function ({wasmScript}) {
|
||||
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
|
||||
function ({wasmScript, breakpoints}) {
|
||||
onBreakpointCalled = 0;
|
||||
breakpoints.forEach(function (bp, i) {
|
||||
var offset = bp.offset;
|
||||
breakpoints.forEach(function (offset, i) {
|
||||
wasmScript.setBreakpoint(offset, {
|
||||
hit: (frame) => {
|
||||
assertEq(frame.offset, breakpoints[0].offset);
|
||||
assertEq(frame.offset, breakpoints[0]);
|
||||
onBreakpointCalled++;
|
||||
// Removing all handlers.
|
||||
wasmScript.clearAllBreakpoints();
|
||||
|
@ -150,14 +172,11 @@ wasmRunWithDebugger(
|
|||
);
|
||||
|
||||
// Checking if breakpoints are removed after debugger has been detached.
|
||||
wasmRunWithDebugger(
|
||||
runTest(
|
||||
'(module (func (nop) (nop)) (export "test" 0))',
|
||||
undefined,
|
||||
function ({dbg, wasmScript, g}) {
|
||||
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
|
||||
function ({dbg, wasmScript, g, breakpoints}) {
|
||||
onBreakpointCalled = 0;
|
||||
breakpoints.forEach(function (bp, i) {
|
||||
var offset = bp.offset;
|
||||
breakpoints.forEach(function (offset, i) {
|
||||
wasmScript.setBreakpoint(offset, {
|
||||
hit: (frame) => {
|
||||
onBreakpointCalled++;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
load(libdir + "asserts.js");
|
||||
|
||||
if (!wasmDebuggingIsSupported())
|
||||
quit();
|
||||
quit();
|
||||
|
||||
// Checking if experimental format generates internal source map to binary file
|
||||
// by querying debugger scripts getAllColumnOffsets.
|
||||
|
@ -14,6 +14,7 @@ function getAllOffsets(wast) {
|
|||
var sandbox = newGlobal('');
|
||||
var dbg = new Debugger();
|
||||
dbg.addDebuggee(sandbox);
|
||||
dbg.allowWasmBinarySource = true;
|
||||
sandbox.eval(`
|
||||
var wasm = wasmTextToBinary('${wast}');
|
||||
var m = new WebAssembly.Instance(new WebAssembly.Module(wasm));
|
||||
|
@ -28,8 +29,10 @@ var offsets1 = getAllOffsets('(module \
|
|||
)');
|
||||
|
||||
// There shall be total 8 lines with single and unique offset per line.
|
||||
var usedOffsets = Object.create(null), usedLines = Object.create(null);
|
||||
var usedOffsets = Object.create(null),
|
||||
usedLines = Object.create(null);
|
||||
assertEq(offsets1.length, 8);
|
||||
|
||||
offsets1.forEach(({offset, lineNumber, columnNumber}) => {
|
||||
assertEq(offset > 0, true);
|
||||
assertEq(lineNumber > 0, true);
|
||||
|
|
|
@ -40,16 +40,13 @@ struct WasmRenderContext
|
|||
JSContext* cx;
|
||||
AstModule* module;
|
||||
WasmPrintBuffer& buffer;
|
||||
GeneratedSourceMap* maybeSourceMap;
|
||||
uint32_t indent;
|
||||
uint32_t currentFuncIndex;
|
||||
|
||||
WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer,
|
||||
GeneratedSourceMap* sourceMap)
|
||||
WasmRenderContext(JSContext* cx, AstModule* module, WasmPrintBuffer& buffer)
|
||||
: cx(cx),
|
||||
module(module),
|
||||
buffer(buffer),
|
||||
maybeSourceMap(sourceMap),
|
||||
indent(0),
|
||||
currentFuncIndex(0)
|
||||
{}
|
||||
|
@ -250,14 +247,6 @@ RenderBlockNameAndSignature(WasmRenderContext& c, const AstName& name, ExprType
|
|||
static bool
|
||||
RenderExpr(WasmRenderContext& c, AstExpr& expr, bool newLine = true);
|
||||
|
||||
#define MAP_AST_EXPR(c, expr) \
|
||||
if (c.maybeSourceMap) { \
|
||||
uint32_t lineno = c.buffer.lineno(); \
|
||||
uint32_t column = c.buffer.column(); \
|
||||
if (!c.maybeSourceMap->exprlocs().emplaceBack(lineno, column, expr.offset())) \
|
||||
return false; \
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// binary format parsing and rendering
|
||||
|
||||
|
@ -266,7 +255,6 @@ RenderNop(WasmRenderContext& c, AstNop& nop)
|
|||
{
|
||||
if (!RenderIndent(c))
|
||||
return false;
|
||||
MAP_AST_EXPR(c, nop);
|
||||
return c.buffer.append("nop");
|
||||
}
|
||||
|
||||
|
@ -278,7 +266,6 @@ RenderDrop(WasmRenderContext& c, AstDrop& drop)
|
|||
|
||||
if (!RenderIndent(c))
|
||||
return false;
|
||||
MAP_AST_EXPR(c, drop);
|
||||
return c.buffer.append("drop");
|
||||
}
|
||||
|
||||
|
@ -287,7 +274,6 @@ RenderUnreachable(WasmRenderContext& c, AstUnreachable& unreachable)
|
|||
{
|
||||
if (!RenderIndent(c))
|
||||
return false;
|
||||
MAP_AST_EXPR(c, unreachable);
|
||||
return c.buffer.append("unreachable");
|
||||
}
|
||||
|
||||
|
@ -311,7 +297,6 @@ RenderCall(WasmRenderContext& c, AstCall& call)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, call);
|
||||
if (call.op() == Op::Call) {
|
||||
if (!c.buffer.append("call "))
|
||||
return false;
|
||||
|
@ -334,7 +319,6 @@ RenderCallIndirect(WasmRenderContext& c, AstCallIndirect& call)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, call);
|
||||
if (!c.buffer.append("call_indirect "))
|
||||
return false;
|
||||
return RenderRef(c, call.funcType());
|
||||
|
@ -346,7 +330,6 @@ RenderConst(WasmRenderContext& c, AstConst& cst)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, cst);
|
||||
if (!RenderValType(c, cst.val().type()))
|
||||
return false;
|
||||
if (!c.buffer.append(".const "))
|
||||
|
@ -374,7 +357,6 @@ RenderGetLocal(WasmRenderContext& c, AstGetLocal& gl)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, gl);
|
||||
if (!c.buffer.append("get_local "))
|
||||
return false;
|
||||
return RenderRef(c, gl.local());
|
||||
|
@ -389,7 +371,6 @@ RenderSetLocal(WasmRenderContext& c, AstSetLocal& sl)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, sl);
|
||||
if (!c.buffer.append("set_local "))
|
||||
return false;
|
||||
return RenderRef(c, sl.local());
|
||||
|
@ -404,7 +385,6 @@ RenderTeeLocal(WasmRenderContext& c, AstTeeLocal& tl)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, tl);
|
||||
if (!c.buffer.append("tee_local "))
|
||||
return false;
|
||||
return RenderRef(c, tl.local());
|
||||
|
@ -416,7 +396,6 @@ RenderGetGlobal(WasmRenderContext& c, AstGetGlobal& gg)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, gg);
|
||||
if (!c.buffer.append("get_global "))
|
||||
return false;
|
||||
return RenderRef(c, gg.global());
|
||||
|
@ -431,7 +410,6 @@ RenderSetGlobal(WasmRenderContext& c, AstSetGlobal& sg)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, sg);
|
||||
if (!c.buffer.append("set_global "))
|
||||
return false;
|
||||
return RenderRef(c, sg.global());
|
||||
|
@ -453,7 +431,6 @@ RenderBlock(WasmRenderContext& c, AstBlock& block, bool isInline = false)
|
|||
if (!isInline && !RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, block);
|
||||
if (block.op() == Op::Block) {
|
||||
if (!c.buffer.append("block"))
|
||||
return false;
|
||||
|
@ -523,7 +500,6 @@ RenderGrowMemory(WasmRenderContext& c, AstGrowMemory& gm)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, gm);
|
||||
return c.buffer.append("grow_memory\n");
|
||||
}
|
||||
|
||||
|
@ -536,7 +512,6 @@ RenderUnaryOperator(WasmRenderContext& c, AstUnaryOperator& unary)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, unary);
|
||||
const char* opStr;
|
||||
switch (unary.op()) {
|
||||
case Op::I32Eqz: opStr = "i32.eqz"; break;
|
||||
|
@ -577,7 +552,6 @@ RenderBinaryOperator(WasmRenderContext& c, AstBinaryOperator& binary)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, binary);
|
||||
const char* opStr;
|
||||
switch (binary.op()) {
|
||||
case Op::I32Add: opStr = "i32.add"; break;
|
||||
|
@ -643,7 +617,6 @@ RenderTernaryOperator(WasmRenderContext& c, AstTernaryOperator& ternary)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, ternary);
|
||||
const char* opStr;
|
||||
switch (ternary.op()) {
|
||||
case Op::Select: opStr = "select"; break;
|
||||
|
@ -664,7 +637,6 @@ RenderComparisonOperator(WasmRenderContext& c, AstComparisonOperator& comp)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, comp);
|
||||
const char* opStr;
|
||||
switch (comp.op()) {
|
||||
case Op::I32Eq: opStr = "i32.eq"; break;
|
||||
|
@ -714,7 +686,6 @@ RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& conv)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, conv);
|
||||
const char* opStr;
|
||||
switch (conv.op()) {
|
||||
case Op::I32WrapI64: opStr = "i32.wrap/i64"; break;
|
||||
|
@ -764,7 +735,6 @@ RenderExtraConversionOperator(WasmRenderContext& c, AstExtraConversionOperator&
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, conv);
|
||||
const char* opStr;
|
||||
switch (conv.op()) {
|
||||
case MiscOp::I32TruncSSatF32: opStr = "i32.trunc_s:sat/f32"; break;
|
||||
|
@ -790,7 +760,6 @@ RenderIf(WasmRenderContext& c, AstIf& if_)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, if_);
|
||||
if (!c.buffer.append("if"))
|
||||
return false;
|
||||
if (!RenderBlockNameAndSignature(c, if_.name(), if_.type()))
|
||||
|
@ -858,7 +827,6 @@ RenderLoad(WasmRenderContext& c, AstLoad& load)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, load);
|
||||
uint32_t defaultAlignLog2;
|
||||
switch (load.op()) {
|
||||
case Op::I32Load8S:
|
||||
|
@ -950,7 +918,6 @@ RenderStore(WasmRenderContext& c, AstStore& store)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, store);
|
||||
uint32_t defaultAlignLog2;
|
||||
switch (store.op()) {
|
||||
case Op::I32Store8:
|
||||
|
@ -1024,7 +991,6 @@ RenderBranch(WasmRenderContext& c, AstBranch& branch)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, branch);
|
||||
if (op == Op::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br "))
|
||||
return false;
|
||||
|
||||
|
@ -1046,7 +1012,6 @@ RenderBrTable(WasmRenderContext& c, AstBranchTable& table)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, table);
|
||||
if (!c.buffer.append("br_table "))
|
||||
return false;
|
||||
|
||||
|
@ -1073,7 +1038,6 @@ RenderReturn(WasmRenderContext& c, AstReturn& ret)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, ret);
|
||||
return c.buffer.append("return");
|
||||
}
|
||||
|
||||
|
@ -1091,7 +1055,6 @@ RenderAtomicCmpXchg(WasmRenderContext& c, AstAtomicCmpXchg& cmpxchg)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, cmpxchg);
|
||||
const char* opname;
|
||||
switch (cmpxchg.op()) {
|
||||
case ThreadOp::I32AtomicCmpXchg8U: opname = "i32.atomic.rmw8_u.cmpxchg"; break;
|
||||
|
@ -1119,7 +1082,6 @@ RenderAtomicLoad(WasmRenderContext& c, AstAtomicLoad& load)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, load);
|
||||
const char* opname;
|
||||
switch (load.op()) {
|
||||
case ThreadOp::I32AtomicLoad8U: opname = "i32.atomic.load8_u"; break;
|
||||
|
@ -1150,7 +1112,6 @@ RenderAtomicRMW(WasmRenderContext& c, AstAtomicRMW& rmw)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, rmw);
|
||||
const char* opname;
|
||||
switch (rmw.op()) {
|
||||
case ThreadOp::I32AtomicAdd: opname = "i32.atomic.rmw.add"; break;
|
||||
|
@ -1216,7 +1177,6 @@ RenderAtomicStore(WasmRenderContext& c, AstAtomicStore& store)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, store);
|
||||
const char* opname;
|
||||
switch (store.op()) {
|
||||
case ThreadOp::I32AtomicStore8U: opname = "i32.atomic.store8_u"; break;
|
||||
|
@ -1250,7 +1210,6 @@ RenderWait(WasmRenderContext& c, AstWait& wait)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, wait);
|
||||
const char* opname;
|
||||
switch (wait.op()) {
|
||||
case ThreadOp::I32Wait: opname = "i32.atomic.wait"; break;
|
||||
|
@ -1296,7 +1255,6 @@ RenderMemCopy(WasmRenderContext& c, AstMemCopy& mc)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, mc);
|
||||
const char* opStr = "memory.copy";
|
||||
|
||||
return c.buffer.append(opStr, strlen(opStr));
|
||||
|
@ -1315,7 +1273,6 @@ RenderMemFill(WasmRenderContext& c, AstMemFill& mf)
|
|||
if (!RenderIndent(c))
|
||||
return false;
|
||||
|
||||
MAP_AST_EXPR(c, mf);
|
||||
const char* opStr = "memory.fill";
|
||||
|
||||
return c.buffer.append(opStr, strlen(opStr));
|
||||
|
@ -1940,11 +1897,6 @@ RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::TypeDef
|
|||
return false;
|
||||
}
|
||||
|
||||
if (c.maybeSourceMap) {
|
||||
if (!c.maybeSourceMap->exprlocs().emplaceBack(c.buffer.lineno(), c.buffer.column(), func.endOffset()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2110,14 +2062,11 @@ RenderModule(WasmRenderContext& c, AstModule& module)
|
|||
return true;
|
||||
}
|
||||
|
||||
#undef MAP_AST_EXPR
|
||||
|
||||
/*****************************************************************************/
|
||||
// Top-level functions
|
||||
|
||||
bool
|
||||
wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer,
|
||||
GeneratedSourceMap* sourceMap /* = nullptr */)
|
||||
wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer)
|
||||
{
|
||||
LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
|
||||
|
||||
|
@ -2126,7 +2075,7 @@ wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuf
|
|||
return false;
|
||||
|
||||
WasmPrintBuffer buf(buffer);
|
||||
WasmRenderContext c(cx, module, buf, sourceMap);
|
||||
WasmRenderContext c(cx, module, buf);
|
||||
|
||||
if (!RenderModule(c, *module)) {
|
||||
if (!cx->isExceptionPending())
|
||||
|
|
|
@ -35,8 +35,7 @@ namespace wasm {
|
|||
// representation.
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer,
|
||||
GeneratedSourceMap* sourceMap = nullptr);
|
||||
BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer);
|
||||
|
||||
} // namespace wasm
|
||||
|
||||
|
|
|
@ -37,56 +37,6 @@ using namespace js::wasm;
|
|||
|
||||
using mozilla::BinarySearchIf;
|
||||
|
||||
bool
|
||||
GeneratedSourceMap::searchLineByOffset(JSContext* cx, uint32_t offset, size_t* exprlocIndex)
|
||||
{
|
||||
MOZ_ASSERT(!exprlocs_.empty());
|
||||
size_t exprlocsLength = exprlocs_.length();
|
||||
|
||||
// Lazily build sorted array for fast log(n) lookup.
|
||||
if (!sortedByOffsetExprLocIndices_) {
|
||||
ExprLocIndexVector scratch;
|
||||
auto indices = MakeUnique<ExprLocIndexVector>();
|
||||
if (!indices || !indices->resize(exprlocsLength) || !scratch.resize(exprlocsLength)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
sortedByOffsetExprLocIndices_ = std::move(indices);
|
||||
|
||||
for (size_t i = 0; i < exprlocsLength; i++)
|
||||
(*sortedByOffsetExprLocIndices_)[i] = i;
|
||||
|
||||
auto compareExprLocViaIndex = [&](uint32_t i, uint32_t j, bool* lessOrEqualp) -> bool {
|
||||
*lessOrEqualp = exprlocs_[i].offset <= exprlocs_[j].offset;
|
||||
return true;
|
||||
};
|
||||
MOZ_ALWAYS_TRUE(MergeSort(sortedByOffsetExprLocIndices_->begin(), exprlocsLength,
|
||||
scratch.begin(), compareExprLocViaIndex));
|
||||
}
|
||||
|
||||
// Allowing non-exact search and if BinarySearchIf returns out-of-bound
|
||||
// index, moving the index to the last index.
|
||||
auto lookupFn = [&](uint32_t i) -> int {
|
||||
const ExprLoc& loc = exprlocs_[i];
|
||||
return offset == loc.offset ? 0 : offset < loc.offset ? -1 : 1;
|
||||
};
|
||||
size_t match;
|
||||
Unused << BinarySearchIf(sortedByOffsetExprLocIndices_->begin(), 0, exprlocsLength, lookupFn, &match);
|
||||
if (match >= exprlocsLength)
|
||||
match = exprlocsLength - 1;
|
||||
*exprlocIndex = (*sortedByOffsetExprLocIndices_)[match];
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
GeneratedSourceMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
size_t size = exprlocs_.sizeOfExcludingThis(mallocSizeOf);
|
||||
if (sortedByOffsetExprLocIndices_)
|
||||
size += sortedByOffsetExprLocIndices_->sizeOfIncludingThis(mallocSizeOf);
|
||||
return size;
|
||||
}
|
||||
|
||||
DebugState::DebugState(SharedCode code,
|
||||
const ShareableBytes* maybeBytecode,
|
||||
bool binarySource)
|
||||
|
@ -99,17 +49,14 @@ DebugState::DebugState(SharedCode code,
|
|||
}
|
||||
|
||||
const char enabledMessage[] =
|
||||
"Restart with developer tools open to view WebAssembly source";
|
||||
"Restart with developer tools open to view WebAssembly source.";
|
||||
|
||||
const char tooBigMessage[] =
|
||||
"Unfortunately, this WebAssembly module is too big to view as text.\n"
|
||||
"We are working hard to remove this limitation.";
|
||||
const char noBinarySource[] =
|
||||
"Configure the debugger to display WebAssembly bytecode.";
|
||||
|
||||
const char notGeneratedMessage[] =
|
||||
"WebAssembly text generation was disabled.";
|
||||
|
||||
static const unsigned TooBig = 1000000;
|
||||
|
||||
static const uint32_t DefaultBinarySourceColumnNumber = 1;
|
||||
|
||||
static const CallSite*
|
||||
|
@ -129,98 +76,26 @@ DebugState::createText(JSContext* cx)
|
|||
if (!maybeBytecode_) {
|
||||
if (!buffer.append(enabledMessage))
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(!maybeSourceMap_);
|
||||
} else if (binarySource_) {
|
||||
if (!buffer.append(notGeneratedMessage))
|
||||
return nullptr;
|
||||
return buffer.finishString();
|
||||
} else if (maybeBytecode_->bytes.length() > TooBig) {
|
||||
if (!buffer.append(tooBigMessage))
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(!maybeSourceMap_);
|
||||
} else {
|
||||
const Bytes& bytes = maybeBytecode_->bytes;
|
||||
auto sourceMap = MakeUnique<GeneratedSourceMap>();
|
||||
if (!sourceMap) {
|
||||
ReportOutOfMemory(cx);
|
||||
if (!buffer.append(noBinarySource))
|
||||
return nullptr;
|
||||
}
|
||||
maybeSourceMap_ = std::move(sourceMap);
|
||||
|
||||
if (!BinaryToText(cx, bytes.begin(), bytes.length(), buffer, maybeSourceMap_.get()))
|
||||
return nullptr;
|
||||
|
||||
#if DEBUG
|
||||
// Check that expression locations are sorted by line number.
|
||||
uint32_t lastLineno = 0;
|
||||
for (const ExprLoc& loc : maybeSourceMap_->exprlocs()) {
|
||||
MOZ_ASSERT(lastLineno <= loc.lineno);
|
||||
lastLineno = loc.lineno;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return buffer.finishString();
|
||||
}
|
||||
|
||||
bool
|
||||
DebugState::ensureSourceMap(JSContext* cx)
|
||||
{
|
||||
if (maybeSourceMap_ || !maybeBytecode_)
|
||||
return true;
|
||||
|
||||
// We just need to cache maybeSourceMap_, ignoring the text result.
|
||||
return createText(cx);
|
||||
}
|
||||
|
||||
struct LineComparator
|
||||
{
|
||||
const uint32_t lineno;
|
||||
explicit LineComparator(uint32_t lineno) : lineno(lineno) {}
|
||||
|
||||
int operator()(const ExprLoc& loc) const {
|
||||
return lineno == loc.lineno ? 0 : lineno < loc.lineno ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
bool
|
||||
DebugState::getLineOffsets(JSContext* cx, size_t lineno, Vector<uint32_t>* offsets)
|
||||
{
|
||||
if (!debugEnabled())
|
||||
return true;
|
||||
|
||||
if (binarySource_) {
|
||||
const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno);
|
||||
if (callsite && !offsets->append(lineno))
|
||||
return false;
|
||||
if (!binarySource_)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ensureSourceMap(cx))
|
||||
const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno);
|
||||
if (callsite && !offsets->append(lineno))
|
||||
return false;
|
||||
|
||||
if (!maybeSourceMap_)
|
||||
return true; // no source text available, keep offsets empty.
|
||||
|
||||
ExprLocVector& exprlocs = maybeSourceMap_->exprlocs();
|
||||
|
||||
// Binary search for the expression with the specified line number and
|
||||
// rewind to the first expression, if more than one expression on the same line.
|
||||
size_t match;
|
||||
if (!BinarySearchIf(exprlocs, 0, exprlocs.length(), LineComparator(lineno), &match))
|
||||
return true;
|
||||
|
||||
while (match > 0 && exprlocs[match - 1].lineno == lineno)
|
||||
match--;
|
||||
|
||||
// Return all expression offsets that were printed on the specified line.
|
||||
for (size_t i = match; i < exprlocs.length() && exprlocs[i].lineno == lineno; i++) {
|
||||
if (!offsets->append(exprlocs[i].offset))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -229,25 +104,16 @@ DebugState::getAllColumnOffsets(JSContext* cx, Vector<ExprLoc>* offsets)
|
|||
{
|
||||
if (!metadata().debugEnabled)
|
||||
return true;
|
||||
|
||||
if (binarySource_) {
|
||||
for (const CallSite& callSite : metadata(Tier::Debug).callSites) {
|
||||
if (callSite.kind() != CallSite::Breakpoint)
|
||||
continue;
|
||||
uint32_t offset = callSite.lineOrBytecode();
|
||||
if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset))
|
||||
return false;
|
||||
}
|
||||
if (!binarySource_)
|
||||
return true;
|
||||
for (const CallSite& callSite : metadata(Tier::Debug).callSites) {
|
||||
if (callSite.kind() != CallSite::Breakpoint)
|
||||
continue;
|
||||
uint32_t offset = callSite.lineOrBytecode();
|
||||
if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ensureSourceMap(cx))
|
||||
return false;
|
||||
|
||||
if (!maybeSourceMap_)
|
||||
return true; // no source text available, keep offsets empty.
|
||||
|
||||
return offsets->appendAll(maybeSourceMap_->exprlocs());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -256,30 +122,13 @@ DebugState::getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_
|
|||
*found = false;
|
||||
if (!debugEnabled())
|
||||
return true;
|
||||
|
||||
if (binarySource_) {
|
||||
if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset))
|
||||
return true; // offset was not found
|
||||
*found = true;
|
||||
*lineno = offset;
|
||||
*column = DefaultBinarySourceColumnNumber;
|
||||
if (!binarySource_)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ensureSourceMap(cx))
|
||||
return false;
|
||||
|
||||
if (!maybeSourceMap_ || maybeSourceMap_->exprlocs().empty())
|
||||
return true; // no source text available
|
||||
|
||||
size_t foundAt;
|
||||
if (!maybeSourceMap_->searchLineByOffset(cx, offset, &foundAt))
|
||||
return false;
|
||||
|
||||
const ExprLoc& loc = maybeSourceMap_->exprlocs()[foundAt];
|
||||
if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset))
|
||||
return true; // offset was not found
|
||||
*found = true;
|
||||
*lineno = loc.lineno;
|
||||
*column = loc.column;
|
||||
*lineno = offset;
|
||||
*column = DefaultBinarySourceColumnNumber;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -289,18 +138,10 @@ DebugState::totalSourceLines(JSContext* cx, uint32_t* count)
|
|||
*count = 0;
|
||||
if (!debugEnabled())
|
||||
return true;
|
||||
|
||||
if (binarySource_) {
|
||||
if (maybeBytecode_)
|
||||
*count = maybeBytecode_->length();
|
||||
if (!binarySource_)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ensureSourceMap(cx))
|
||||
return false;
|
||||
|
||||
if (maybeSourceMap_)
|
||||
*count = maybeSourceMap_->totalLines();
|
||||
if (maybeBytecode_)
|
||||
*count = maybeBytecode_->length();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -708,8 +549,6 @@ DebugState::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
|||
size_t* data) const
|
||||
{
|
||||
code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
|
||||
if (maybeSourceMap_)
|
||||
*data += maybeSourceMap_->sizeOfExcludingThis(mallocSizeOf);
|
||||
if (maybeBytecode_)
|
||||
*data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ namespace wasm {
|
|||
|
||||
struct MetadataTier;
|
||||
|
||||
// The generated source location for the AST node/expression. The offset field refers
|
||||
// an offset in an binary format file.
|
||||
// The generated source location for the AST node/expression. The offset field
|
||||
// refers an offset in an binary format file.
|
||||
|
||||
struct ExprLoc
|
||||
{
|
||||
|
@ -48,39 +48,14 @@ struct ExprLoc
|
|||
{}
|
||||
};
|
||||
|
||||
typedef Vector<ExprLoc, 0, SystemAllocPolicy> ExprLocVector;
|
||||
typedef Vector<uint32_t, 0, SystemAllocPolicy> ExprLocIndexVector;
|
||||
|
||||
// The generated source map for WebAssembly binary file. This map is generated during
|
||||
// building the text buffer (see BinaryToExperimentalText).
|
||||
|
||||
class GeneratedSourceMap
|
||||
{
|
||||
ExprLocVector exprlocs_;
|
||||
UniquePtr<ExprLocIndexVector> sortedByOffsetExprLocIndices_;
|
||||
uint32_t totalLines_;
|
||||
|
||||
public:
|
||||
explicit GeneratedSourceMap() : totalLines_(0) {}
|
||||
ExprLocVector& exprlocs() { return exprlocs_; }
|
||||
|
||||
uint32_t totalLines() { return totalLines_; }
|
||||
void setTotalLines(uint32_t val) { totalLines_ = val; }
|
||||
|
||||
bool searchLineByOffset(JSContext* cx, uint32_t offset, size_t* exprlocIndex);
|
||||
|
||||
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||
};
|
||||
|
||||
typedef UniquePtr<GeneratedSourceMap> UniqueGeneratedSourceMap;
|
||||
typedef HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy> StepModeCounters;
|
||||
typedef HashMap<uint32_t, WasmBreakpointSite*, DefaultHasher<uint32_t>, SystemAllocPolicy> WasmBreakpointSiteMap;
|
||||
typedef HashMap<uint32_t, WasmBreakpointSite*, DefaultHasher<uint32_t>, SystemAllocPolicy>
|
||||
WasmBreakpointSiteMap;
|
||||
|
||||
class DebugState
|
||||
{
|
||||
const SharedCode code_;
|
||||
const SharedBytes maybeBytecode_;
|
||||
UniqueGeneratedSourceMap maybeSourceMap_;
|
||||
bool binarySource_;
|
||||
|
||||
// State maintained when debugging is enabled. In this case, the Code is
|
||||
|
@ -92,7 +67,6 @@ class DebugState
|
|||
StepModeCounters stepModeCounters_;
|
||||
|
||||
void toggleDebugTrap(uint32_t offset, bool enabled);
|
||||
bool ensureSourceMap(JSContext* cx);
|
||||
|
||||
public:
|
||||
DebugState(SharedCode code,
|
||||
|
@ -102,10 +76,6 @@ class DebugState
|
|||
const Bytes* maybeBytecode() const { return maybeBytecode_ ? &maybeBytecode_->bytes : nullptr; }
|
||||
bool binarySource() const { return binarySource_; }
|
||||
|
||||
// If the source bytecode was saved when this Code was constructed, this
|
||||
// method will render the binary as text. Otherwise, a diagnostic string
|
||||
// will be returned.
|
||||
|
||||
JSString* createText(JSContext* cx);
|
||||
bool getLineOffsets(JSContext* cx, size_t lineno, Vector<uint32_t>* offsets);
|
||||
bool getAllColumnOffsets(JSContext* cx, Vector<ExprLoc>* offsets);
|
||||
|
|
|
@ -5544,7 +5544,7 @@ EncodeTableSection(Encoder& e, AstModule& module)
|
|||
}
|
||||
|
||||
static bool
|
||||
EncodeFunctionBody(Encoder& e, AstFunc& func)
|
||||
EncodeFunctionBody(Encoder& e, Uint32Vector* offsets, AstFunc& func)
|
||||
{
|
||||
size_t bodySizeAt;
|
||||
if (!e.writePatchableVarU32(&bodySizeAt))
|
||||
|
@ -5559,10 +5559,14 @@ EncodeFunctionBody(Encoder& e, AstFunc& func)
|
|||
return false;
|
||||
|
||||
for (AstExpr* expr : func.body()) {
|
||||
if (!offsets->append(e.currentOffset()))
|
||||
return false;
|
||||
if (!EncodeExpr(e, *expr))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!offsets->append(e.currentOffset()))
|
||||
return false;
|
||||
if (!e.writeOp(Op::End))
|
||||
return false;
|
||||
|
||||
|
@ -5588,7 +5592,7 @@ EncodeStartSection(Encoder& e, AstModule& module)
|
|||
}
|
||||
|
||||
static bool
|
||||
EncodeCodeSection(Encoder& e, AstModule& module)
|
||||
EncodeCodeSection(Encoder& e, Uint32Vector* offsets, AstModule& module)
|
||||
{
|
||||
if (module.funcs().empty())
|
||||
return true;
|
||||
|
@ -5601,7 +5605,7 @@ EncodeCodeSection(Encoder& e, AstModule& module)
|
|||
return false;
|
||||
|
||||
for (AstFunc* func : module.funcs()) {
|
||||
if (!EncodeFunctionBody(e, *func))
|
||||
if (!EncodeFunctionBody(e, offsets, *func))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -5708,7 +5712,7 @@ EncodeElemSection(Encoder& e, AstModule& module)
|
|||
}
|
||||
|
||||
static bool
|
||||
EncodeModule(AstModule& module, Bytes* bytes)
|
||||
EncodeModule(AstModule& module, Uint32Vector* offsets, Bytes* bytes)
|
||||
{
|
||||
Encoder e(*bytes);
|
||||
|
||||
|
@ -5745,7 +5749,7 @@ EncodeModule(AstModule& module, Bytes* bytes)
|
|||
if (!EncodeElemSection(e, module))
|
||||
return false;
|
||||
|
||||
if (!EncodeCodeSection(e, module))
|
||||
if (!EncodeCodeSection(e, offsets, module))
|
||||
return false;
|
||||
|
||||
if (!EncodeDataSection(e, module))
|
||||
|
@ -5779,7 +5783,8 @@ EncodeBinaryModule(const AstModule& module, Bytes* bytes)
|
|||
/*****************************************************************************/
|
||||
|
||||
bool
|
||||
wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, UniqueChars* error)
|
||||
wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uint32Vector* offsets,
|
||||
UniqueChars* error)
|
||||
{
|
||||
LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
|
||||
|
||||
|
@ -5794,5 +5799,5 @@ wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uni
|
|||
if (!ResolveModule(lifo, module, error))
|
||||
return false;
|
||||
|
||||
return EncodeModule(*module, bytes);
|
||||
return EncodeModule(*module, offsets, bytes);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,8 @@ namespace wasm {
|
|||
// other than out-of-memory an error message string will be stored in 'error'.
|
||||
|
||||
extern MOZ_MUST_USE bool
|
||||
TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, UniqueChars* error);
|
||||
TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uint32Vector* offsets,
|
||||
UniqueChars* error);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
|
Загрузка…
Ссылка в новой задаче