Source code

Revision control

Copy as Markdown

Other Tools

// |jit-test| test-also=--wasm-compiler=optimizing; error: TestComplete; skip-if: !wasmDebuggingEnabled()
// Tests that wasm module scripts raises onEnterFrame and onLeaveFrame events.
load(libdir + "asserts.js");
function runWasmWithDebugger(wast, lib, init, done) {
let g = newGlobal({newCompartment: true});
let dbg = new Debugger(g);
g.eval(`
var wasm = wasmTextToBinary('${wast}');
var lib = ${lib || 'undefined'};
var m = new WebAssembly.Instance(new WebAssembly.Module(wasm), lib);`);
init(dbg, g);
let result = undefined, error = undefined;
try {
result = g.eval("m.exports.test()");
} catch (ex) {
error = ex;
}
done(dbg, result, error, g);
}
// Checking if onEnterFrame is fired for wasm frames and verifying the content
// of the frame and environment properties.
var onEnterFrameCalled, onLeaveFrameCalled, onExceptionUnwindCalled, testComplete;
runWasmWithDebugger(
'(module (func (result i32) (i32.const 42)) (export "test" (func 0)))', undefined,
function (dbg) {
var wasmScript = dbg.findScripts().filter(s => s.format == 'wasm')[0];
assertEq(!!wasmScript, true);
onEnterFrameCalled = 0;
onLeaveFrameCalled = 0;
testComplete = false;
var evalFrame;
dbg.onEnterFrame = function (frame) {
if (frame.type !== 'wasmcall') {
if (frame.type === 'eval')
evalFrame = frame;
return;
}
onEnterFrameCalled++;
assertEq(frame.script, wasmScript);
assertEq(frame.older, evalFrame);
assertEq(frame.type, 'wasmcall');
let env = frame.environment;
assertEq(env instanceof Object, true);
assertEq(env.inspectable, true);
assertEq(env.parent !== null, true);
assertEq(env.type, 'declarative');
assertEq(env.calleeScript, null);
assertEq(Array.isArray(env.names()), true);
assertEq(env.names().length, 0);
frame.onPop = function() {
onLeaveFrameCalled++;
testComplete = true;
};
};
},
function (dbg, result, error) {
assertEq(testComplete, true);
assertEq(onEnterFrameCalled, 1);
assertEq(onLeaveFrameCalled, 1);
assertEq(result, 42);
assertEq(error, undefined);
}
);
// Checking the dbg.getNewestFrame() and frame.older.
runWasmWithDebugger(
'(module (import "env" "ex" (func $fn1)) (func $fn2 (call $fn1)) (export "test" (func $fn2)))',
'{env: { ex: () => { }}}',
function (dbg) {
onEnterFrameCalled = 0;
onLeaveFrameCalled = 0;
testComplete = false;
var evalFrame, wasmFrame;
dbg.onEnterFrame = function (frame) {
onEnterFrameCalled++;
assertEq(dbg.getNewestFrame(), frame);
switch (frame.type) {
case 'eval':
evalFrame = frame;
break;
case 'wasmcall':
wasmFrame = frame;
break;
case 'call':
assertEq(frame.older, wasmFrame);
assertEq(frame.older.older, evalFrame);
assertEq(frame.older.older.older, null);
testComplete = true;
break;
}
frame.onPop = function() {
onLeaveFrameCalled++;
};
};
},
function (dbg, result, error) {
assertEq(testComplete, true);
assertEq(onEnterFrameCalled, 3);
assertEq(onLeaveFrameCalled, 3);
assertEq(error, undefined);
}
);
// Checking if we can enumerate frames and find 'wasmcall' one.
runWasmWithDebugger(
'(module (import "env" "ex" (func $fn1)) (func $fn2 (call $fn1)) (export "test" (func $fn2)))',
'{env: { ex: () => { debugger; }}}',
function (dbg) {
testComplete = false;
dbg.onDebuggerStatement = function (frame) {
assertEq(frame.type, 'call');
assertEq(frame.older.type, 'wasmcall');
assertEq(frame.older.older.type, 'eval');
assertEq(frame.older.older.older, null);
testComplete = true;
}
},
function (dbg, result, error) {
assertEq(testComplete, true);
assertEq(error, undefined);
}
);
// Checking if onPop works without onEnterFrame handler.
runWasmWithDebugger(
'(module (import "env" "ex" (func $fn1)) (func $fn2 (call $fn1)) (export "test" (func $fn2)))',
'{env: { ex: () => { debugger; }}}',
function (dbg) {
onLeaveFrameCalled = 0;
dbg.onDebuggerStatement = function (frame) {
if (!frame.older || frame.older.type != 'wasmcall')
return;
frame.older.onPop = function () {
onLeaveFrameCalled++;
};
}
},
function (dbg, result, error) {
assertEq(onLeaveFrameCalled, 1);
assertEq(error, undefined);
}
);
// Checking if function return values are not changed.
runWasmWithDebugger(
'(module (func (result f64) (f64.const 0.42)) (export "test" (func 0)))', undefined,
function (dbg) {
dbg.onEnterFrame = function (frame) {
dbg.onPop = function () {};
};
},
function (dbg, result, error) {
assertEq(result, 0.42);
assertEq(error, undefined);
}
);
runWasmWithDebugger(
'(module (func (result f32) (f32.const 4.25)) (export "test" (func 0)))', undefined,
function (dbg) {
dbg.onEnterFrame = function (frame) {
dbg.onPop = function () {};
};
},
function (dbg, result, error) {
assertEq(result, 4.25);
assertEq(error, undefined);
}
);
// Checking if onEnterFrame/onExceptionUnwind work during exceptions --
// `unreachable` causes wasm to throw WebAssembly.RuntimeError exception.
runWasmWithDebugger(
'(module (func (unreachable)) (export "test" (func 0)))', undefined,
function (dbg) {
onEnterFrameCalled = 0;
onLeaveFrameCalled = 0;
onExceptionUnwindCalled = 0;
dbg.onEnterFrame = function (frame) {
if (frame.type !== "wasmcall") return;
onEnterFrameCalled++;
frame.onPop = function() {
onLeaveFrameCalled++;
};
};
dbg.onExceptionUnwind = function (frame) {
if (frame.type !== "wasmcall") return;
onExceptionUnwindCalled++;
};
},
function (dbg, result, error, g) {
assertEq(onEnterFrameCalled, 1);
assertEq(onLeaveFrameCalled, 1);
assertEq(onExceptionUnwindCalled, 1);
assertEq(error instanceof g.WebAssembly.RuntimeError, true);
}
);
// Checking if onEnterFrame/onExceptionUnwind work during exceptions
// originated in the JavaScript import call.
runWasmWithDebugger(
'(module (import "env" "ex" (func $fn1)) (func $fn2 (call $fn1)) (export "test" (func $fn2)))',
'{env: { ex: () => { throw new Error(); }}}',
function (dbg) {
onEnterFrameCalled = 0;
onLeaveFrameCalled = 0;
onExceptionUnwindCalled = 0;
dbg.onEnterFrame = function (frame) {
if (frame.type !== "wasmcall") return;
onEnterFrameCalled++;
frame.onPop = function() {
onLeaveFrameCalled++;
};
};
dbg.onExceptionUnwind = function (frame) {
if (frame.type !== "wasmcall") return;
onExceptionUnwindCalled++;
};
},
function (dbg, result, error, g) {
assertEq(onEnterFrameCalled, 1);
assertEq(onLeaveFrameCalled, 1);
assertEq(onExceptionUnwindCalled, 1);
assertEq(error instanceof g.Error, true);
}
);
// Checking throwing in the handler.
runWasmWithDebugger(
'(module (func (unreachable)) (export "test" (func 0)))', undefined,
function (dbg) {
dbg.uncaughtExceptionHook = function (value) {
assertEq(value instanceof Error, true);
return {throw: 'test'};
};
dbg.onEnterFrame = function (frame) {
if (frame.type !== "wasmcall") return;
throw new Error();
};
},
function (dbg, result, error) {
assertEq(error, 'test');
}
);
runWasmWithDebugger(
'(module (func (unreachable)) (export "test" (func 0)))', undefined,
function (dbg) {
dbg.uncaughtExceptionHook = function (value) {
assertEq(value instanceof Error, true);
return {throw: 'test'};
};
dbg.onEnterFrame = function (frame) {
if (frame.type !== "wasmcall") return;
frame.onPop = function () {
throw new Error();
}
};
},
function (dbg, result, error) {
assertEq(error, 'test');
}
);
// Checking resumption values for JS_THROW.
runWasmWithDebugger(
'(module (func (nop)) (export "test" (func 0)))', undefined,
function (dbg, g) {
dbg.onEnterFrame = function (frame) {
if (frame.type !== "wasmcall") return;
return {throw: 'test'};
};
},
function (dbg, result, error, g) {
assertEq(error, 'test');
}
);
runWasmWithDebugger(
'(module (func (nop)) (export "test" (func 0)))', undefined,
function (dbg, g) {
dbg.onEnterFrame = function (frame) {
if (frame.type !== "wasmcall") return;
frame.onPop = function () {
return {throw: 'test'};
}
};
},
function (dbg, result, error, g) {
assertEq(error, 'test');
}
);
// Checking resumption values for JS_RETURN (not implemented by wasm baseline).
runWasmWithDebugger(
'(module (func (unreachable)) (export "test" (func 0)))', undefined,
function (dbg) {
dbg.onEnterFrame = function (frame) {
if (frame.type !== "wasmcall") return;
return {return: 2};
};
},
function (dbg, result, error) {
assertEq(result, undefined, 'NYI: result == 2, if JS_RETURN is implemented');
assertEq(error != undefined, true, 'NYI: error == undefined, if JS_RETURN is implemented');
}
);
runWasmWithDebugger(
'(module (func (unreachable)) (export "test" (func 0)))', undefined,
function (dbg) {
dbg.onEnterFrame = function (frame) {
if (frame.type !== "wasmcall") return;
frame.onPop = function () {
return {return: 2};
}
};
},
function (dbg, result, error) {
assertEq(result, undefined, 'NYI: result == 2, if JS_RETURN is implemented');
assertEq(error != undefined, true, 'NYI: error == undefined, if JS_RETURN is implemented');
}
);
throw "TestComplete";