Backed out 2 changesets (bug 1447591) for mochitest failures at dom/base/test/test_postMessages.html

Backed out changeset 0800fdb509d2 (bug 1447591)
Backed out changeset 9b5347dee1f2 (bug 1447591)
This commit is contained in:
Coroiu Cristina 2018-06-27 13:09:31 +03:00
Родитель 0004f1c6ae
Коммит e555bb0ae4
25 изменённых файлов: 5689 добавлений и 179 удалений

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

@ -792,10 +792,8 @@ 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 &&
!isWasm) {
!noSliding) {
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,10 +26,7 @@ function run_test() {
gClient, "test-stack",
function(response, tabClient, threadClient) {
gThreadClient = threadClient;
gThreadClient.reconfigure({
observeAsmJS: true,
wasmBinarySource: true
}, function(response) {
gThreadClient.reconfigure({ observeAsmJS: true }, function(response) {
Assert.equal(!!response.error, false);
test_pause_frame();
});

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

@ -63,6 +63,7 @@
#include "vm/StringType.h"
#include "vm/TraceLogging.h"
#include "wasm/AsmJS.h"
#include "wasm/WasmBinaryToText.h"
#include "wasm/WasmJS.h"
#include "wasm/WasmModule.h"
#include "wasm/WasmSignalHandlers.h"
@ -658,61 +659,90 @@ 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].isBoolean()) {
ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a boolean");
if (!args[1].isString()) {
ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be a String");
return false;
}
withOffsets = ToBoolean(args[1]);
}
uintptr_t stackLimit = GetNativeStackLimit(cx);
wasm::Bytes bytes;
UniqueChars error;
wasm::Uint32Vector offsets;
if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes, &offsets, &error)) {
if (!wasm::TextToBinary(twoByteChars.twoByteChars(), stackLimit, &bytes, &error)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_TEXT_FAIL,
error.get() ? error.get() : "out of memory");
return false;
}
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));
RootedObject obj(cx, JS_NewUint8Array(cx, bytes.length()));
if (!obj)
return false;
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;
memcpy(obj->as<TypedArrayObject>().viewDataUnshared(), bytes.begin(), bytes.length());
args.rval().setObject(*obj);
return true;
}
static bool
WasmBinaryToText(JSContext* cx, unsigned argc, Value* vp)
{
if (!cx->options().wasm()) {
JS_ReportErrorASCII(cx, "wasm support unavailable");
return false;
}
CallArgs args = CallArgsFromVp(argc, vp);
if (!args.get(0).isObject() || !args.get(0).toObject().is<TypedArrayObject>()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
return false;
}
Rooted<TypedArrayObject*> code(cx, &args[0].toObject().as<TypedArrayObject>());
if (!TypedArrayObject::ensureHasBuffer(cx, code))
return false;
if (code->isSharedMemory()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_BUF_ARG);
return false;
}
const uint8_t* bufferStart = code->bufferUnshared()->dataPointer();
const uint8_t* bytes = bufferStart + code->byteOffset();
uint32_t length = code->byteLength();
Vector<uint8_t> copy(cx);
if (code->bufferUnshared()->hasInlineData()) {
if (!copy.append(bytes, length))
return false;
bytes = copy.begin();
}
if (args.length() > 1) {
JS_ReportErrorASCII(cx, "wasm text format selection is not supported");
return false;
}
StringBuffer buffer(cx);
bool ok = wasm::BinaryToText(cx, bytes, length, buffer);
if (!ok) {
if (!cx->isExceptionPending())
JS_ReportErrorASCII(cx, "wasm binary to text print error");
return false;
}
JSString* result = buffer.finishString();
if (!result)
return false;
args.rval().setString(result);
return true;
}
static bool
WasmExtractCode(JSContext* cx, unsigned argc, Value* vp)
{
@ -5648,6 +5678,10 @@ gc::ZealModeHelpText),
"wasmTextToBinary(str)",
" Translates the given text wasm module into its binary encoding."),
JS_FN_HELP("wasmBinaryToText", WasmBinaryToText, 1, 0,
"wasmBinaryToText(bin)",
" Translates binary encoding to text format"),
JS_FN_HELP("wasmExtractCode", WasmExtractCode, 1, 0,
"wasmExtractCode(module[, tier])",
" Extracts generated machine code from WebAssembly.Module. The tier is a string,\n"

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

@ -116,6 +116,14 @@ 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.
@ -126,15 +134,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'};
@ -152,6 +160,19 @@ 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() {

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

@ -0,0 +1,115 @@
// |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,9 +15,7 @@ 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);
@ -26,16 +24,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 never generated with the native Debugger API.
assertEq(source.text.includes('module'), false);
// The text is generated if wasm binary sources are disabled.
assertEq(source.text.includes('module'), true);
assertThrowsInstanceOf(() => source.binary, Error);
// Enable binary sources.
@ -31,7 +31,7 @@ assertEq(s.format, "wasm");
var source2 = s.source;
// The text is predefined if wasm binary sources are enabled.
// The text is '[wasm]' 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,55 +6,21 @@ load(libdir + "wasm.js");
if (!wasmDebuggingIsSupported())
quit();
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;
// Checking if we can stop at specified breakpoint.
runTest(
var onBreakpointCalled;
wasmRunWithDebugger(
'(module (func (nop) (nop)) (export "test" 0))',
function ({wasmScript, breakpoints}) {
undefined,
function ({wasmScript}) {
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
assertEq(breakpoints.length, 3);
assertEq(breakpoints[0] > 0, true);
assertEq(breakpoints[0].offset > 0, true);
// Checking if breakpoints offsets are in ascending order.
assertEq(breakpoints[0] < breakpoints[1], true);
assertEq(breakpoints[1] < breakpoints[2], true);
assertEq(breakpoints[0].offset < breakpoints[1].offset, true);
assertEq(breakpoints[1].offset < breakpoints[2].offset, true);
onBreakpointCalled = 0;
breakpoints.forEach(function (offset) {
breakpoints.forEach(function (bp) {
var offset = bp.offset;
wasmScript.setBreakpoint(offset, {
hit: (frame) => {
assertEq(frame.offset, offset);
@ -70,15 +36,18 @@ runTest(
);
// Checking if we can remove breakpoint one by one.
runTest(
wasmRunWithDebugger(
'(module (func (nop) (nop)) (export "test" 0))',
function ({wasmScript, breakpoints}) {
undefined,
function ({wasmScript}) {
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
onBreakpointCalled = 0;
var handlers = [];
breakpoints.forEach(function (offset, i) {
breakpoints.forEach(function (bp, i) {
var offset = bp.offset;
wasmScript.setBreakpoint(offset, handlers[i] = {
hit: (frame) => {
assertEq(frame.offset, breakpoints[0]);
assertEq(frame.offset, breakpoints[0].offset);
onBreakpointCalled++;
// Removing all handlers.
handlers.forEach(h => wasmScript.clearBreakpoint(h));
@ -93,15 +62,18 @@ runTest(
);
// Checking if we can remove breakpoint one by one from a breakpoint handler.
runTest(
wasmRunWithDebugger(
'(module (func (nop) (nop)) (export "test" 0))',
function ({wasmScript, breakpoints}) {
undefined,
function ({wasmScript}) {
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
onBreakpointCalled = 0;
var handlers = [];
breakpoints.forEach(function (offset, i) {
breakpoints.forEach(function (bp, i) {
var offset = bp.offset;
wasmScript.setBreakpoint(offset, handlers[i] = {
hit: (frame) => {
assertEq(frame.offset, breakpoints[0]);
assertEq(frame.offset, breakpoints[0].offset);
onBreakpointCalled++;
// Removing all handlers.
handlers.forEach(h => wasmScript.clearBreakpoint(h));
@ -118,13 +90,16 @@ runTest(
// Checking if we can remove breakpoint one by one from onEnterFrame,
// but onStep will still work.
var onStepCalled;
runTest(
wasmRunWithDebugger(
'(module (func (nop) (nop)) (export "test" 0))',
function ({dbg, wasmScript, breakpoints}) {
undefined,
function ({dbg, wasmScript}) {
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
onBreakpointCalled = 0;
onStepCalled = [];
var handlers = [];
breakpoints.forEach(function (offset, i) {
breakpoints.forEach(function (bp, i) {
var offset = bp.offset;
wasmScript.setBreakpoint(offset, handlers[i] = {
hit: (frame) => {
assertEq(false, true);
@ -150,14 +125,17 @@ runTest(
);
// Checking if we can remove all breakpoints.
runTest(
wasmRunWithDebugger(
'(module (func (nop) (nop)) (export "test" 0))',
function ({wasmScript, breakpoints}) {
undefined,
function ({wasmScript}) {
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
onBreakpointCalled = 0;
breakpoints.forEach(function (offset, i) {
breakpoints.forEach(function (bp, i) {
var offset = bp.offset;
wasmScript.setBreakpoint(offset, {
hit: (frame) => {
assertEq(frame.offset, breakpoints[0]);
assertEq(frame.offset, breakpoints[0].offset);
onBreakpointCalled++;
// Removing all handlers.
wasmScript.clearAllBreakpoints();
@ -172,11 +150,14 @@ runTest(
);
// Checking if breakpoints are removed after debugger has been detached.
runTest(
wasmRunWithDebugger(
'(module (func (nop) (nop)) (export "test" 0))',
function ({dbg, wasmScript, g, breakpoints}) {
undefined,
function ({dbg, wasmScript, g}) {
var breakpoints = wasmGetScriptBreakpoints(wasmScript);
onBreakpointCalled = 0;
breakpoints.forEach(function (offset, i) {
breakpoints.forEach(function (bp, i) {
var offset = bp.offset;
wasmScript.setBreakpoint(offset, {
hit: (frame) => {
onBreakpointCalled++;

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

@ -5,15 +5,15 @@
load(libdir + "asserts.js");
if (!wasmDebuggingIsSupported())
quit();
quit();
// Checking if experimental format generates internal source map to binary file
// by querying debugger scripts getAllColumnOffsets.
// (Notice that the source map will not be produced by wasmBinaryToText)
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,10 +28,8 @@ 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);

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

@ -84,10 +84,79 @@ for (let align of [1, 2, 4, 8]) {
assertEq(valText(text), align == 4);
}
// Check that the text output is sane.
for (let [type,view] of [['i32','8_u'],['i32','16_u'],['i32',''],['i64','8_u'],['i64','16_u'],['i64','32_u'],['i64','']]) {
let addr = "i32.const 48";
let value = `${type}.const 1`;
let value2 = `${type}.const 2`;
for (let op of ["add", "and", "or", "xor", "xchg"]) {
let operator = `${type}.atomic.rmw${view}.${op}`;
let text = `(module (memory 1 1 shared)
(func (result ${type}) (${operator} (${addr}) (${value})))
(export "" 0))`;
checkRoundTrip(text, [addr, value, operator]);
}
{
let operator = `${type}.atomic.rmw${view}.cmpxchg`;
let text = `(module (memory 1 1 shared)
(func (result ${type}) (${operator} (${addr}) (${value}) (${value2})))
(export "" 0))`;
checkRoundTrip(text, [addr, value, value2, operator]);
}
{
let operator = `${type}.atomic.load${view}`;
let text = `(module (memory 1 1 shared)
(func (result ${type}) (${operator} (${addr})))
(export "" 0))`;
checkRoundTrip(text, [addr, operator]);
}
{
let operator = `${type}.atomic.store${view}`;
let text = `(module (memory 1 1 shared)
(func (${operator} (${addr}) (${value})))
(export "" 0))`;
checkRoundTrip(text, [addr, value, operator]);
}
}
for (let type of ['i32', 'i64']) {
let addr = "i32.const 48";
let operator = `${type}.atomic.wait`
let value = `${type}.const 1`;
let timeout = "i64.const 314159";
let text = `(module (memory 1 1 shared)
(func (result i32) (${operator} (${addr}) (${value}) (${timeout})))
(export "" 0))`;
checkRoundTrip(text, [addr, value, timeout, operator]);
}
{
let addr = "i32.const 48";
let operator = "atomic.wake"
let count = "i32.const 1";
let text = `(module (memory 1 1 shared)
(func (result i32) (${operator} (${addr}) (${count})))
(export "" 0))`;
checkRoundTrip(text, [addr, count, operator]);
}
function valText(text) {
return WebAssembly.validate(wasmTextToBinary(text));
}
function checkRoundTrip(text, ss) {
let input = wasmTextToBinary(text);
let output = wasmBinaryToText(input).split("\n").map(String.trim);
for (let s of output) {
if (ss.length == 0)
break;
if (s.match(ss[0]))
ss.shift();
}
assertEq(ss.length, 0);
}
// Test that atomic operations work.
function I64(hi, lo) {

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

@ -69,6 +69,15 @@ assertEq(ins.hello(4.0, 1), 16.0)
assertEq(ins.x1(12), 36)
assertEq(ins.x2(8), Math.PI)
// Crude but at least checks that we have *something*.
var txt = wasmBinaryToText(bin);
var re = /\(type\s+\$[a-z0-9]+\s+\(struct/gm;
assertEq(Array.isArray(re.exec(txt)), true);
assertEq(Array.isArray(re.exec(txt)), true);
assertEq(Array.isArray(re.exec(txt)), true);
assertEq(Array.isArray(re.exec(txt)), false);
// The field name is optional, so this should work.
wasmEvalText(`

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

@ -0,0 +1,293 @@
var caught = false;
try {
wasmBinaryToText(new Int8Array(1));
} catch (e) {
caught = true;
}
assertEq(caught, true);
assertErrorMessage(() => wasmBinaryToText(wasmTextToBinary(`(module (func (result i32) (f32.const 13.37)))`)), WebAssembly.CompileError, /type mismatch/);
function runTest(code) {
var expected = wasmTextToBinary(code);
var s = wasmBinaryToText(expected);
print("TEXT: " + s);
var roundtrip = wasmTextToBinary(s);
assertDeepEq(expected, roundtrip);
}
// Smoke test
runTest(`
(module
(func (param i32) (result f64)
(local $l f32)
(block
(set_local $l (f32.const 0.0))
(loop $exit $cont
(br_if $exit (get_local 0))
(br 2)
)
(drop (if f64 (i32.const 1)
(f64.min (f64.neg (f64.const 1)) (f64.const 0))
(f64.add (f64.const 0.5) (f64.load offset=0 (i32.const 0)) )
))
)
(i32.store16 (i32.const 8) (i32.const 128))
(return (f64.const 0))
)
(export "test" 0)
(memory 1 10)
)`);
// Constants, stores and loads
runTest(`
(module (func
(local i32) (local f32) (local f64)
(drop (i32.const 0))
(drop (i32.const 100002))
(drop (f32.const 0.0))
(drop (f32.const 1.5))
(drop (f64.const 0.0))
(drop (f64.const -10.25))
(i32.store (i32.const 0) (i32.load (i32.const 0)))
(i32.store8 (i32.const 1) (i32.load8_s (i32.const 2)))
(i32.store8 (i32.const 3) (i32.load8_u (i32.const 4)))
(i32.store16 (i32.const 2) (i32.load16_s (i32.const 0)))
(i32.store16 (i32.const 1) (i32.load16_u (i32.const 0)))
(f32.store (i32.const 5) (f32.load (i32.const 6)))
(f64.store (i32.const 5) (f64.load (i32.const 6)))
(set_local 0 (get_local 0))
(set_local 2 (get_local 2))
)(memory 100))`);
// Branching
runTest(`
(module
(func
(block (block (block (nop))))
(block (loop ))
(if (i32.const 0) (block $label (nop)))
(if (i32.const 1) (nop) (loop $exit $cont (block )))
(block $l (br $l))
(block $m (block (block (br $m))))
(block $k (br_if 0 (i32.const 0)) (return))
(block $n (block (block (br_if 2 (i32.const 1)) (nop))))
(block $1 (block $2 (block $3 (br_table $2 $3 $1 (i32.const 1)) )) (nop))
(loop $exit $cont (br_if $cont (i32.const 0)) (nop))
(return)
)
(func (result f32) (return (f32.const -0.5)))
(memory 0)
)`);
// i32, f32 and f64 operations
runTest(`
(module
(func $iadd (param $x i32) (param $y i32) (result i32) (i32.add (get_local $x) (get_local $y)))
(func $isub (param $x i32) (param $y i32) (result i32) (i32.sub (get_local $x) (get_local $y)))
(func $imul (param $x i32) (param $y i32) (result i32) (i32.mul (get_local $x) (get_local $y)))
(func $idiv_s (param $x i32) (param $y i32) (result i32) (i32.div_s (get_local $x) (get_local $y)))
(func $idiv_u (param $x i32) (param $y i32) (result i32) (i32.div_u (get_local $x) (get_local $y)))
(func $irem_s (param $x i32) (param $y i32) (result i32) (i32.rem_s (get_local $x) (get_local $y)))
(func $irem_u (param $x i32) (param $y i32) (result i32) (i32.rem_u (get_local $x) (get_local $y)))
(func $iand (param $x i32) (param $y i32) (result i32) (i32.and (get_local $x) (get_local $y)))
(func $ior (param $x i32) (param $y i32) (result i32) (i32.or (get_local $x) (get_local $y)))
(func $ixor (param $x i32) (param $y i32) (result i32) (i32.xor (get_local $x) (get_local $y)))
(func $ishl (param $x i32) (param $y i32) (result i32) (i32.shl (get_local $x) (get_local $y)))
(func $ishr_s (param $x i32) (param $y i32) (result i32) (i32.shr_s (get_local $x) (get_local $y)))
(func $ishr_u (param $x i32) (param $y i32) (result i32) (i32.shr_u (get_local $x) (get_local $y)))
(func $iclz (param $x i32) (result i32) (i32.clz (get_local $x)))
(func $ictz (param $x i32) (result i32) (i32.ctz (get_local $x)))
(func $ipopcnt (param $x i32) (result i32) (i32.popcnt (get_local $x)))
(func $ieq (param $x i32) (param $y i32) (result i32) (i32.eq (get_local $x) (get_local $y)))
(func $ine (param $x i32) (param $y i32) (result i32) (i32.ne (get_local $x) (get_local $y)))
(func $ilt_s (param $x i32) (param $y i32) (result i32) (i32.lt_s (get_local $x) (get_local $y)))
(func $ilt_u (param $x i32) (param $y i32) (result i32) (i32.lt_u (get_local $x) (get_local $y)))
(func $ile_s (param $x i32) (param $y i32) (result i32) (i32.le_s (get_local $x) (get_local $y)))
(func $ile_u (param $x i32) (param $y i32) (result i32) (i32.le_u (get_local $x) (get_local $y)))
(func $igt_s (param $x i32) (param $y i32) (result i32) (i32.gt_s (get_local $x) (get_local $y)))
(func $igt_u (param $x i32) (param $y i32) (result i32) (i32.gt_u (get_local $x) (get_local $y)))
(func $ige_s (param $x i32) (param $y i32) (result i32) (i32.ge_s (get_local $x) (get_local $y)))
(func $ige_u (param $x i32) (param $y i32) (result i32) (i32.ge_u (get_local $x) (get_local $y)))
(func $fadd (param $x f32) (param $y f32) (result f32) (f32.add (get_local $x) (get_local $y)))
(func $fsub (param $x f32) (param $y f32) (result f32) (f32.sub (get_local $x) (get_local $y)))
(func $fmul (param $x f32) (param $y f32) (result f32) (f32.mul (get_local $x) (get_local $y)))
(func $fdiv (param $x f32) (param $y f32) (result f32) (f32.div (get_local $x) (get_local $y)))
(func $fsqrt (param $x f32) (result f32) (f32.sqrt (get_local $x)))
(func $fmin (param $x f32) (param $y f32) (result f32) (f32.min (get_local $x) (get_local $y)))
(func $fmax (param $x f32) (param $y f32) (result f32) (f32.max (get_local $x) (get_local $y)))
(func $fceil (param $x f32) (result f32) (f32.ceil (get_local $x)))
(func $ffloor (param $x f32) (result f32) (f32.floor (get_local $x)))
(func $fabs (param $x f32) (result f32) (f32.abs (get_local $x)))
(func $fneg (param $x f32) (result f32) (f32.neg (get_local $x)))
(func $dadd (param $x f64) (param $y f64) (result f64) (f64.add (get_local $x) (get_local $y)))
(func $dsub (param $x f64) (param $y f64) (result f64) (f64.sub (get_local $x) (get_local $y)))
(func $dmul (param $x f64) (param $y f64) (result f64) (f64.mul (get_local $x) (get_local $y)))
(func $ddiv (param $x f64) (param $y f64) (result f64) (f64.div (get_local $x) (get_local $y)))
(func $dceil (param $x f64) (result f64) (f64.ceil (get_local $x)))
(func $dfloor (param $x f64) (result f64) (f64.floor (get_local $x)))
(func $dabs (param $x f64) (result f64) (f64.abs (get_local $x)))
(func $dneg (param $x f64) (result f64) (f64.neg (get_local $x)))
(memory 0))`);
// conversions
runTest(`
(module
(func $itrunc_s_f32 (param $x f32) (result i32) (i32.trunc_s/f32 (get_local $x)))
(func $itrunc_u_f32 (param $x f32) (result i32) (i32.trunc_u/f32 (get_local $x)))
(func $itrunc_s_f64 (param $x f64) (result i32) (i32.trunc_s/f64 (get_local $x)))
(func $itrunc_u_f64 (param $x f64) (result i32) (i32.trunc_u/f64 (get_local $x)))
(func $fconvert_s_i32 (param $x i32) (result f32) (f32.convert_s/i32 (get_local $x)))
(func $dconvert_s_i32 (param $x i32) (result f64) (f64.convert_s/i32 (get_local $x)))
(func $fconvert_u_i32 (param $x i32) (result f32) (f32.convert_u/i32 (get_local $x)))
(func $dconvert_u_i32 (param $x i32) (result f64) (f64.convert_u/i32 (get_local $x)))
(func $dpromote_f32 (param $x f32) (result f64) (f64.promote/f32 (get_local $x)))
(func $fdemote_f64 (param $x f64) (result f32) (f32.demote/f64 (get_local $x)))
(memory 0))`);
// function calls
runTest(`
(module
(type $type1 (func (param i32) (result i32)))
(import $import1 "mod" "test" (param f32) (result f32))
(table anyfunc (elem $func1 $func2))
(func $func1 (param i32) (param f32) (nop))
(func $func2 (param i32) (result i32) (get_local 0))
(func $test
(call $func1
(call_indirect $type1 (i32.const 2) (i32.const 1))
(call $import1 (f32.const 1.0))
)
)
(export "test" $test)
(memory 1)
)`);
// default memory export from binaryen
runTest(`(module (func (nop)) (memory 0 65535))`);
// stack-machine code that isn't directly representable as an AST
runTest(`
(module
(func (result i32)
(local $x i32)
i32.const 100
set_local $x
i32.const 200
set_local $x
i32.const 400
set_local $x
i32.const 2
i32.const 16
nop
set_local $x
i32.const 3
i32.const 17
set_local $x
i32.const 18
set_local $x
i32.lt_s
if i32
i32.const 101
set_local $x
i32.const 8
i32.const 102
set_local $x
else
i32.const 103
set_local $x
i32.const 900
i32.const 104
set_local $x
i32.const 105
set_local $x
end
i32.const 107
set_local $x
get_local $x
i32.add
i32.const 106
set_local $x
)
(export "" 0)
)`);
// more stack-machine code that isn't directly representable as an AST
runTest(`
(module
(func $return_void)
(func (result i32)
(local $x i32)
i32.const 0
block
i32.const 1
set_local $x
end
i32.const 2
set_local $x
i32.const 3
loop
i32.const 4
set_local $x
end
i32.const 5
set_local $x
i32.add
call $return_void
)
(export "" 0)
)`);
runTest(`
(module
(func $func
block $block
i32.const 0
if
i32.const 0
if
end
else
end
end
)
(export "" 0)
)`);
// Branch table.
runTest(`(module
(func (export "run") (param $p i32) (local $n i32)
i32.const 0
set_local $n
loop $outer
block $c block $b block $a
loop $inner
get_local $p
br_table $b $a $c $inner $outer
end $inner
end $a
get_local $n
i32.const 1
i32.add
set_local $n
end $b
block
get_local $n
i32.const 2
i32.add
set_local $n
end
end $c
end $outer
)
)`);
// Import as a start function.
runTest(`(module
(import "env" "test" (func))
(start 0)
)`);

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

@ -401,6 +401,8 @@ UNIFIED_SOURCES += [
'vm/Xdr.cpp',
'wasm/AsmJS.cpp',
'wasm/WasmBaselineCompile.cpp',
'wasm/WasmBinaryToAST.cpp',
'wasm/WasmBinaryToText.cpp',
'wasm/WasmBuiltins.cpp',
'wasm/WasmCode.cpp',
'wasm/WasmCompile.cpp',
@ -418,6 +420,7 @@ UNIFIED_SOURCES += [
'wasm/WasmStubs.cpp',
'wasm/WasmTable.cpp',
'wasm/WasmTextToBinary.cpp',
'wasm/WasmTextUtils.cpp',
'wasm/WasmTypes.cpp',
'wasm/WasmValidate.cpp'
]

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

@ -126,17 +126,10 @@ struct AstBase
}
};
struct AstNode
{
void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() {
return astLifo.alloc(numBytes);
}
};
class AstFuncType;
class AstStructType;
class AstTypeDef : public AstNode
class AstTypeDef : public AstBase
{
protected:
enum class Which { IsFuncType, IsStructType };
@ -265,6 +258,19 @@ AstTypeDef::asStructType() const
return *static_cast<const AstStructType*>(this);
}
const uint32_t AstNodeUnknownOffset = 0;
class AstNode : public AstBase
{
uint32_t offset_; // if applicable, offset in the binary format file
public:
AstNode() : offset_(AstNodeUnknownOffset) {}
uint32_t offset() const { return offset_; }
void setOffset(uint32_t offset) { offset_ = offset; }
};
enum class AstExprKind
{
AtomicCmpXchg,
@ -856,6 +862,7 @@ class AstFunc : public AstNode
AstValTypeVector vars_;
AstNameVector localNames_;
AstExprVector body_;
uint32_t endOffset_; // if applicable, offset in the binary format file
public:
AstFunc(AstName name, AstRef ft, AstValTypeVector&& vars,
@ -864,13 +871,16 @@ class AstFunc : public AstNode
funcType_(ft),
vars_(std::move(vars)),
localNames_(std::move(locals)),
body_(std::move(body))
body_(std::move(body)),
endOffset_(AstNodeUnknownOffset)
{}
AstRef& funcType() { return funcType_; }
const AstValTypeVector& vars() const { return vars_; }
const AstNameVector& locals() const { return localNames_; }
const AstExprVector& body() const { return body_; }
AstName name() const { return name_; }
uint32_t endOffset() const { return endOffset_; }
void setEndOffset(uint32_t offset) { endOffset_ = offset; }
};
class AstGlobal : public AstNode

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,37 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef wasmbinarytoast_h
#define wasmbinarytoast_h
#include "ds/LifoAlloc.h"
#include "wasm/WasmAST.h"
#include "wasm/WasmTypes.h"
namespace js {
namespace wasm {
bool
BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAlloc& lifo,
AstModule** module);
} // end wasm namespace
} // end js namespace
#endif // namespace wasmbinarytoast_h

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,45 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2015 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef wasm_binary_to_text_h
#define wasm_binary_to_text_h
#include "NamespaceImports.h"
#include "gc/Rooting.h"
#include "js/Class.h"
#include "wasm/WasmCode.h"
namespace js {
class StringBuffer;
namespace wasm {
// Translate the given binary representation of a wasm module into the module's textual
// representation.
MOZ_MUST_USE bool
BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer,
GeneratedSourceMap* sourceMap = nullptr);
} // namespace wasm
} // namespace js
#endif // namespace wasm_binary_to_text_h

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

@ -27,6 +27,7 @@
#include "util/StringBuffer.h"
#include "util/Text.h"
#include "vm/Debugger.h"
#include "wasm/WasmBinaryToText.h"
#include "wasm/WasmInstance.h"
#include "wasm/WasmValidate.h"
@ -36,6 +37,56 @@ 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)
@ -48,14 +99,17 @@ 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 noBinarySource[] =
"Configure the debugger to display WebAssembly bytecode.";
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 notGeneratedMessage[] =
"WebAssembly text generation was disabled.";
static const unsigned TooBig = 1000000;
static const uint32_t DefaultBinarySourceColumnNumber = 1;
static const CallSite*
@ -75,26 +129,98 @@ DebugState::createText(JSContext* cx)
if (!maybeBytecode_) {
if (!buffer.append(enabledMessage))
return nullptr;
MOZ_ASSERT(!maybeSourceMap_);
} else if (binarySource_) {
if (!buffer.append(notGeneratedMessage))
return nullptr;
} else {
if (!buffer.append(noBinarySource))
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);
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_)
if (binarySource_) {
const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno);
if (callsite && !offsets->append(lineno))
return false;
return true;
const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno);
if (callsite && !offsets->append(lineno))
}
if (!ensureSourceMap(cx))
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;
}
@ -103,16 +229,25 @@ DebugState::getAllColumnOffsets(JSContext* cx, Vector<ExprLoc>* offsets)
{
if (!metadata().debugEnabled)
return true;
if (!binarySource_)
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;
}
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;
}
return true;
if (!ensureSourceMap(cx))
return false;
if (!maybeSourceMap_)
return true; // no source text available, keep offsets empty.
return offsets->appendAll(maybeSourceMap_->exprlocs());
}
bool
@ -121,13 +256,30 @@ DebugState::getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_
*found = false;
if (!debugEnabled())
return true;
if (!binarySource_)
if (binarySource_) {
if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset))
return true; // offset was not found
*found = true;
*lineno = offset;
*column = DefaultBinarySourceColumnNumber;
return true;
if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset))
return true; // offset was not found
}
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];
*found = true;
*lineno = offset;
*column = DefaultBinarySourceColumnNumber;
*lineno = loc.lineno;
*column = loc.column;
return true;
}
@ -137,10 +289,18 @@ DebugState::totalSourceLines(JSContext* cx, uint32_t* count)
*count = 0;
if (!debugEnabled())
return true;
if (!binarySource_)
if (binarySource_) {
if (maybeBytecode_)
*count = maybeBytecode_->length();
return true;
if (maybeBytecode_)
*count = maybeBytecode_->length();
}
if (!ensureSourceMap(cx))
return false;
if (maybeSourceMap_)
*count = maybeSourceMap_->totalLines();
return true;
}
@ -548,6 +708,8 @@ 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,14 +48,39 @@ 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
@ -67,6 +92,7 @@ class DebugState
StepModeCounters stepModeCounters_;
void toggleDebugTrap(uint32_t offset, bool enabled);
bool ensureSourceMap(JSContext* cx);
public:
DebugState(SharedCode code,
@ -76,6 +102,10 @@ 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, Uint32Vector* offsets, AstFunc& func)
EncodeFunctionBody(Encoder& e, AstFunc& func)
{
size_t bodySizeAt;
if (!e.writePatchableVarU32(&bodySizeAt))
@ -5559,14 +5559,10 @@ EncodeFunctionBody(Encoder& e, Uint32Vector* offsets, 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;
@ -5592,7 +5588,7 @@ EncodeStartSection(Encoder& e, AstModule& module)
}
static bool
EncodeCodeSection(Encoder& e, Uint32Vector* offsets, AstModule& module)
EncodeCodeSection(Encoder& e, AstModule& module)
{
if (module.funcs().empty())
return true;
@ -5605,7 +5601,7 @@ EncodeCodeSection(Encoder& e, Uint32Vector* offsets, AstModule& module)
return false;
for (AstFunc* func : module.funcs()) {
if (!EncodeFunctionBody(e, offsets, *func))
if (!EncodeFunctionBody(e, *func))
return false;
}
@ -5712,7 +5708,7 @@ EncodeElemSection(Encoder& e, AstModule& module)
}
static bool
EncodeModule(AstModule& module, Uint32Vector* offsets, Bytes* bytes)
EncodeModule(AstModule& module, Bytes* bytes)
{
Encoder e(*bytes);
@ -5749,7 +5745,7 @@ EncodeModule(AstModule& module, Uint32Vector* offsets, Bytes* bytes)
if (!EncodeElemSection(e, module))
return false;
if (!EncodeCodeSection(e, offsets, module))
if (!EncodeCodeSection(e, module))
return false;
if (!EncodeDataSection(e, module))
@ -5783,8 +5779,7 @@ EncodeBinaryModule(const AstModule& module, Bytes* bytes)
/*****************************************************************************/
bool
wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uint32Vector* offsets,
UniqueChars* error)
wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, UniqueChars* error)
{
LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
@ -5799,5 +5794,5 @@ wasm::TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, Uin
if (!ResolveModule(lifo, module, error))
return false;
return EncodeModule(*module, offsets, bytes);
return EncodeModule(*module, bytes);
}

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

@ -29,8 +29,7 @@ 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, Uint32Vector* offsets,
UniqueChars* error);
TextToBinary(const char16_t* text, uintptr_t stackLimit, Bytes* bytes, UniqueChars* error);
} // namespace wasm
} // namespace js

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

@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2016 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wasm/WasmTextUtils.h"
#include "util/StringBuffer.h"
#include "wasm/WasmTypes.h"
using namespace js;
using namespace jit;
using namespace wasm;
using mozilla::IsNaN;
template<size_t base>
bool
wasm::RenderInBase(StringBuffer& sb, uint64_t num)
{
uint64_t n = num;
uint64_t pow = 1;
while (n) {
pow *= base;
n /= base;
}
pow /= base;
n = num;
while (pow) {
if (!sb.append("0123456789abcdef"[n / pow]))
return false;
n -= (n / pow) * pow;
pow /= base;
}
return true;
}
template bool wasm::RenderInBase<10>(StringBuffer& sb, uint64_t num);
template<class T>
bool
wasm::RenderNaN(StringBuffer& sb, T num)
{
typedef typename mozilla::SelectTrait<T> Traits;
typedef typename Traits::Bits Bits;
MOZ_ASSERT(IsNaN(num));
Bits bits = mozilla::BitwiseCast<Bits>(num);
if ((bits & Traits::kSignBit) && !sb.append("-"))
return false;
if (!sb.append("nan"))
return false;
Bits payload = bits & Traits::kSignificandBits;
// Only render the payload if it's not the spec's default NaN.
if (payload == ((Traits::kSignificandBits + 1) >> 1))
return true;
return sb.append(":0x") &&
RenderInBase<16>(sb, payload);
}
template MOZ_MUST_USE bool wasm::RenderNaN(StringBuffer& b, float num);
template MOZ_MUST_USE bool wasm::RenderNaN(StringBuffer& b, double num);

105
js/src/wasm/WasmTextUtils.h Normal file
Просмотреть файл

@ -0,0 +1,105 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
*
* Copyright 2016 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef wasm_text_utils
#define wasm_text_utils
#include "NamespaceImports.h"
#include "util/StringBuffer.h"
namespace js {
namespace wasm {
template<size_t base>
MOZ_MUST_USE bool
RenderInBase(StringBuffer& sb, uint64_t num);
template<class T>
MOZ_MUST_USE bool
RenderNaN(StringBuffer& sb, T num);
// Helper class, StringBuffer wrapper, to track the position (line and column)
// within the generated source.
class WasmPrintBuffer
{
StringBuffer& stringBuffer_;
uint32_t lineno_;
uint32_t column_;
public:
explicit WasmPrintBuffer(StringBuffer& stringBuffer)
: stringBuffer_(stringBuffer),
lineno_(1),
column_(1)
{}
inline char processChar(char ch) {
if (ch == '\n') {
lineno_++; column_ = 1;
} else
column_++;
return ch;
}
inline char16_t processChar(char16_t ch) {
if (ch == '\n') {
lineno_++; column_ = 1;
} else
column_++;
return ch;
}
bool append(const char ch) {
return stringBuffer_.append(processChar(ch));
}
bool append(const char16_t ch) {
return stringBuffer_.append(processChar(ch));
}
bool append(const char* str, size_t length) {
for (size_t i = 0; i < length; i++)
processChar(str[i]);
return stringBuffer_.append(str, length);
}
bool append(const char16_t* begin, const char16_t* end) {
for (const char16_t* p = begin; p != end; p++)
processChar(*p);
return stringBuffer_.append(begin, end);
}
bool append(const char16_t* str, size_t length) {
return append(str, str + length);
}
template <size_t ArrayLength>
bool append(const char (&array)[ArrayLength]) {
static_assert(ArrayLength > 0, "null-terminated");
MOZ_ASSERT(array[ArrayLength - 1] == '\0');
return append(array, ArrayLength - 1);
}
char16_t getChar(size_t index) {
return stringBuffer_.getChar(index);
}
size_t length() {
return stringBuffer_.length();
}
StringBuffer& stringBuffer() { return stringBuffer_; }
uint32_t lineno() { return lineno_; }
uint32_t column() { return column_; }
};
} // namespace wasm
} // namespace js
#endif // namespace wasm_text_utils

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

@ -132,6 +132,9 @@ struct ModuleEnvironment
bool funcIsImport(uint32_t funcIndex) const {
return funcIndex < funcImportGlobalDataOffsets.length();
}
uint32_t funcIndexToFuncTypeIndex(uint32_t funcIndex) const {
return TypeDef::fromFuncTypeWithIdPtr(funcTypes[funcIndex]) - types.begin();
}
};
// The Encoder class appends bytes to the Bytes object it is given during