Debugger tests

From MozillaWiki
Jump to: navigation, search

Here is a simple Debugger test:

var g = newGlobal('new-compartment');
var dbg = new Debugger(g);
var c;
dbg.onDebuggerStatement = function (frame) {
    c = frame.eval("2 + 2");
};
g.eval("debugger;");
assertEq(c.return, 4);

Let’s step through this example line by line and see how it works.

First, we need something to debug. The Debugger object won’t let you debug yourself. You must debug a global object in another compartment. So the first step is to make such an object:

var g = newGlobal('new-compartment');

Now we can create a Debugger to debug it.

var dbg = new Debugger(g);

This automatically turns on debug mode for the target compartment, so the -d option is unnecessary. Hooray!

Debugger statements are extremely useful for testing the debugger. This is actually a standard part of the ECMAScript language: a debugger statement looks like this: debugger; and when it executes, the behavior is implementation-defined. In our case, it fires an onDebuggerStatement event. So we set a handler for that event:

var c;
dbg.onDebuggerStatement = function (frame) {
    c = frame.eval("2 + 2");
};

Warning: By design, exceptions are never automatically propagated from debugger code to the debuggee.

  • A few debugger callbacks, like onNewScript, squelch all exceptions!! It’s useless to call assertEq from such callbacks, because even if the assertion fails, the exception will be silently discarded, and the test will pass anyway. Instead, store the information you need and check it later.
  • Throwing from any other debugger callbacks, including onDebuggerStatement, will terminate the debuggee with an uncatchable error. So it is OK to call assertEq from onDebuggerStatement. Both jit_test.py and jstests.py will treat that as a test failure.

Any non-trivial Debugger test has to run code in the debuggee. We do that using its eval function:

g.eval("debugger;");

If you understand cross-compartment wrappers, you can see how this works. g is a wrapper for a global object in another compartment, so g.eval is a wrapper for that global’s eval function. When we call g.eval, this happens:

  • we enter g’s compartment, rewrapping the argument(s);
  • we call eval in that compartment;
  • we return to the caller’s compartment, rewrapping the return value or exception.

So this line of code should execute a debugger statement in g’s compartment, which should fire the onDebuggerStatement event and execute our callback code. The call to evalInFrame should return the completion value {return: 4}.

Lastly, we check the result:

assertEq(c.return, 4);

When jsdbg2 lands, the existing debugger tests will go under js/src/jit-test/tests/debug.