Bug 1447591: Stub out a few Debugger APIs for wasm; r=yury

--HG--
extra : rebase_source : b8c8b2afeac3a3bdefa8f412354579f601a3af86
This commit is contained in:
Benjamin Bouvier 2018-06-20 17:56:19 +02:00
Родитель 7549087a70
Коммит 1c9f5a8af9
16 изменённых файлов: 174 добавлений и 490 удалений

Просмотреть файл

@ -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