Debugger: Difference between revisions
(→Debugging hooks: Describe "slow script"-style termination. Explain what non-function code is.) |
(→Debug.Frame: First cut.) |
||
| Line 104: | Line 104: | ||
==Debug.Frame== | ==Debug.Frame== | ||
A <code>Debug.Frame</code> instance represents a debuggee stack frame. It provides operations for finding the script the frame is executing, the frame that invoked it, and the lexical scope in which the execution is taking place. | |||
A <code>Debug.Frame</code> instance is a weak reference to the frame; once the debuggee destroys the frame (perhaps by returning from a function or completing an <code>eval</code> call), further operations on the <code>Debug.Frame</code> instance will raise an error. | |||
SpiderMonkey creates instances of <code>Debug.Frame</code> as needed in two situations: when calling a hook function that expects a frame as an argument; and when an existing frame's <code>older</code> property is accessed. SpiderMonkey creates only one <code>Debug.Frame</code> instance for a given debuggee frame; every hook function called while the debuggee is running in a given frame receives the same frame object. Thus, debugger code can place its own properties on a frame object and expect to find them later, use <code>==</code> to decide whether two expressions refer to the same frame, and so on. | |||
A <code>Debug.Frame</code> instance has the following properties, which are all non-writable and non-configurable: | |||
<dl> | |||
<dt>type | |||
<dd>A string describing what sort of frame this is: | |||
<ul> | |||
<li><code>"call"</code>: a frame running a function that was called. | |||
<li><code>"eval"</code>: a frame running code passed to <code>eval</code>. | |||
<li><code>"global"</code>: a frame running global code (JavaScript that is neither of the above) | |||
<li><code>"host"</code>: a frame for a call to a host function (I'm not sure if we can obtain these) | |||
<li><code>"debugger"</code>: a frame for a call to user code invoked by the debugger | |||
<li><code>"dummy"</code>: a frame pushed for stupid people (rather—I don't know what this is) | |||
</ul> | |||
</dl> | |||
(Note that a <code>"debugger"</code> frame represents the debugger continuation expecting the expression's value; the expression itself has its own <code>"eval"</code> frame.) | |||
<dt>older | |||
<dd>The frame in which control will resume when this frame completes. | |||
<dt>depth | |||
<dd>The depth of this frame, counting the oldest frame as frame zero. | |||
<dt>callee | |||
<dd>The function whose application created this frame. Present only on <code>"call"</code> and <code>"host"</code> frames. | |||
<dt>generator | |||
<dd>True if this frame is a generator frame; false otherwise. Present only on frames whose type is <code>"call"</code>. | |||
<dt>constructing | |||
<dd>True if this frame is for a function called as a constructor. Present on <code>"call"</code> and <code>"host"</code> frames. | |||
<dt>script | |||
<dd>The script being executed in this frame (a <code>Debug.Script</code> instance). Present on <code>"call"</code>, <code>"eval"</code>, and <code>"global"</code> frames. On <code>"call"</code> frames, this is equal to <code>callee.script</code>. | |||
<dt>offset | |||
<dd>The offset of the bytecode instruction currently being executed in <code>script</code>. Present when <code>script</code> is. | |||
<dt>environment | |||
<dd>The lexical environment within which evaluation is taking place (a debuggee value). Present on <code>"call"</code>, <code>"eval"</code>, and <code>"global"</code> frames. | |||
<dt>this | |||
<dd>The value of <code>this</code> for the current frame&mdash (a debuggee value). Present on <code>"call"</code>, <code>"eval"</code>, and <code>"host"</code> frames. | |||
<dt>arguments | |||
<dd>The arguments passed to the current frame, as an array of debuggee values. (The array itself is an ordinary array in the debugger compartment.) Present on <code>"call"</code>, <code>"eval"</code>, and <code>"host"</code> frames. | |||
</dl> | |||
A <code>Debug.Frame</code> instance has the following methods: | |||
<dl> | |||
<dt>eval(<i>code</i>) | |||
<dd>Begin evaluating <i>code</i> in the scope of this frame. <i>Code</i> is a string. This pushes a <code>"debugger"</code> frame on the debuggee's stack, evaluates <i>code</i> with all extant hook functions active, and returns a value of the sort passed to an <code>enterFrame</code> completion function describing how the code completed. | |||
<dt>finish(<i>result</i>) <i>(future plan)</i> | |||
<dd>Pop this frame (and any younger frames) from the stack as if this frame had completed. <i>Result</i> is a value of the sort that the <code>interrupt</code> hook might return, indicating how execution should be prepared to continue. Note that this does <emph>not</emph> resume the debuggee's execution; it merely adjusts the debuggee's state to what it would be if this frame's execution had completed. You must return <code>true</code> from the hook function to resume execution in that state. | |||
This cannot remove any <code>"host"</code> frames (calls through C++) from the stack. (With some clever hacking we might be able to make this work, but it would be ugly.) | |||
<dt>restart(<i>function</i>, <i>this</i>, <i>arguments</i>) <i>(future plan)</i> | |||
<dd>Pop any younger frames from the stack, and then turn this frame into a frame about to begin executing <i>function</i>, with the given <i>this</i> value and <i>arguments</i>. <i>This</i> should be a debuggee value, or <code>{ asConstructor: true }</code> to invoke <i>function</i> as a constructor. <i>Arguments</i> should be an array of debuggee values. This frame must be a <code>"call"</code> frame. Note that this does <emph>not</emph> resume the debuggee's execution; it merely adjusts the debuggee's state to what it would be if this frame were about to make this call. You must return <code>true</code> from the hook function to resume execution in that state. | |||
Like <code>finish</code>, this cannot remove <code>"host"</code> frames from the stack. | |||
</dl> | |||
===Generator Frames=== | |||
SpiderMonkey supports generator-iterator objects, which produce a series of values by repeatedly suspending the execution of a function or expression. For example, calling a function that uses <code>yield</code> produces a generator-iterator object, as does evaluating a generator expression. | |||
A generator-iterator object refers to a stack frame with no fixed continuation frame. While the generator's code is running, its continuation is whatever frame called its <code>next</code> method; while the generator is suspended, it has no particular continuation frame; and when it resumes again, the continuation frame for that resumption could be different from that of the previous resumption. | |||
When you use the <code>Debug</code> object to inspect a program that is running a generator frame, that frame appears on the stack like any other call frame, except that its <code>generator</code> property is <code>true</code>. Such a frame will disappear from the stack when it is suspended, and reappear (possibly with a different <code>older</code> frame) each time it is resumed. | |||
==Debug.Script== | ==Debug.Script== | ||
Revision as of 07:45, 4 March 2011
The Debug object provides functions for debugging code running in a separate compartment. You can provide functions for SpiderMonkey to call when events like steps, calls, and breakpoint hits occur in the debuggee, examine the debuggee's stack frames, and inspect and manipulate the debuggee's objects.
Debug object event hook functions run in the same thread as the debuggee, on the same stack: when the event occurs, the debuggee pauses while your hook functions run, and resumes (unless you say otherwise) when your functions return.
The debugger and debuggee must be in separate compartments. Your hook functions run in the debugger's compartment. SpiderMonkey mediates their access to the debuggee's objects, and prevents the debuggee from accessing the debugger's objects at all.
The Debug object provides objects representing the debuggee's stack frames, scripts, and other internal interpreter structures, for your hook functions to examine and manipulate.
Debugger access to debuggee values
The Debug object follows certain conventions to help debuggers safely inspect and modify the debuggee's objects and values. Primitive values are passed freely between debugger and debuggee; copying or wrapping is handled transparently, as appropriate. Objects (including host objects like DOM nodes) received from the debuggee are fronted in the debugger by Debug.Object instances (described in detail below), which provide reflection-oriented methods for inspecting the referent object's properties and other characteristics. Of the debugger's objects, only Debug.Object instances may be passed to the debuggee: when this occurs, the debuggee receives the Debug.Object's referent, not the Debug.Object instance itself.
In the descriptions below, the term "debuggee value" means either a primitive value or a Debug.Object instance; it is a value that might be received from the debuggee, or that could be passed to the debuggee.
Beginning to Debug
To begin debugging another compartment's code, you create a Debug object for the debuggee compartment, and install your hook functions.
- Debug(object)
- Create a debugger object debugging object's compartment. Object is typically a global object, but can be any JavaScript object from the debuggee's compartment. The object must be in a different compartment than the calling code, and debugger/debuggee compartments may not form a cycle. Object's compartment must not be in use by another thread while this call runs.
- D.setHooks(hooks)
- Use the functions in hooks to handle events occurring in D's debuggee. Hooks should be an object; each property should be named after a debugging event, and its value should be a function SpiderMonkey should call when the named event occurs. See below for descriptions of specific debugging hooks.
This removes all previously registered hooks; after the call, only the hooks mentioned in hooks are in force. Thus, a call like
D.setHooks({})removes all debugging hooks. Hook function calls are cross-compartment, same-thread calls. Hook functions run in the thread in which the event occurred, not in the thread that registered the hooks. (It is your responsibility to ensure that two threads don't try to run in the same compartment). Hook functions run in the compartment to which they belong, not in the debuggee's compartment. - D.getHooks()
- Return an object holding all the event hooks currently in force. The returned object is suitable for use with
D.setHooks.
Debugging hooks
For each debugging hook, we give the name of the hook and the arguments passed to its handler function, and describe the circumstances under which SpiderMonkey calls it.
- interrupt(frame)
- A bytecode instruction is about to execute in the stack frame represented by frame, a
Debug.Frameinstance. Naturally, frame is the youngest debuggee frame. This hook function's return value determines how execution should continue:- If it returns true, execution continues normally.
- If it returns an object of the form
{ throw: value }, then value is thrown as an exception from the current bytecode instruction. value must be a debuggee value. - If it returns an object of the form
{ return: value }, then value is immediately returned as the current value of the function. value must be a debuggee value. - If it returns null, the calling code is terminated, as if it had been cancelled by the "slow script" dialog box.
- If the hook throws an exception, ... well, we're in trouble. That's an error in the debugger which should be reported somehow, but certainly not handled by the debuggee.
- newScript(script, [function])
- New code, represented by the
Debug.Scriptinstance script, has been loaded into the debuggee's compartment. If the new code is part of a function, function is a Debug.Object reference to the function object. (Not all code is part of a function; for example, the code appearing in a<script>tag that is outside of any functions defined in that tag would be passed tonewScriptwithout an accompanying function argument.) Note that script may be a temporary script, created for a call to eval and destroyed when its execution is complete. - destroyScript(script)
- SpiderMonkey has determined that script will no longer be needed, and is about to throw it away. The garbage collector may have found that the script is no longer in use, or perhaps eval has finished executing the script, and is about to destroy it. In any case, operations on script after this hook function returns will throw an error.
- debuggerHandler(frame)
- The debuggee has executed a debugger statement in frame. This hook function's return value determines how execution proceeds, as for the interrupt hook function.
- sourceHandler(ASuffusionOfYellow)
- This hook function is never called. If it is ever called, a contradiction has been proven, and the debugger is free to assume that everything is true.
- enterFrame(frame, call)
- The stack frame frame is about to begin executing code. (Naturally, frame is currently the youngest debuggee frame.) If call is true, it is a function call; if call is false, it is global or eval code.
If this hook function returns a function f, SpiderMonkey will call f when execution of frame completes, passing one argument indicating how it completed.
- If the argument is of the form
{ return: value }, then the code completed normally, yielding value. Value is a debuggee value. - If the argument is of the form
{ throw: value }, then the code threw value as an exception. Value is a debuggee value. - If the argument is
null, then the code was terminated, as if by the "slow script" dialog box.
- If the argument is of the form
- throw(frame, value)
- The code running in frame is about to throw value as an exception. The value this hook function returns determines how execution proceeds, as for interrupt.
- error(frame, report)
- SpiderMonkey is about to report an error in frame. Report is an object describing the error, with the following properties:
message- The fully formatted error message.
file- If present, the source file name, URL, etc. (If this property is present, the line property will be too, and vice versa.)
line- If present, the source line number at which the error occurred.
lineText- If present, this is the source code of the offending line.
offset- The index of the character within lineText at which the error occurred.
warning- Present and true if this is a warning; absent otherwise.
strict- Present and true if this error or warning is due to the strict option (not to be confused with ES strict mode)
exception- Present and true if an exception will be thrown; absent otherwise.
arguments- An array of strings, representing the arguments substituted into the error message.
Debug.Frame
A Debug.Frame instance represents a debuggee stack frame. It provides operations for finding the script the frame is executing, the frame that invoked it, and the lexical scope in which the execution is taking place.
A Debug.Frame instance is a weak reference to the frame; once the debuggee destroys the frame (perhaps by returning from a function or completing an eval call), further operations on the Debug.Frame instance will raise an error.
SpiderMonkey creates instances of Debug.Frame as needed in two situations: when calling a hook function that expects a frame as an argument; and when an existing frame's older property is accessed. SpiderMonkey creates only one Debug.Frame instance for a given debuggee frame; every hook function called while the debuggee is running in a given frame receives the same frame object. Thus, debugger code can place its own properties on a frame object and expect to find them later, use == to decide whether two expressions refer to the same frame, and so on.
A Debug.Frame instance has the following properties, which are all non-writable and non-configurable:
- type
- A string describing what sort of frame this is:
"call": a frame running a function that was called."eval": a frame running code passed toeval."global": a frame running global code (JavaScript that is neither of the above)"host": a frame for a call to a host function (I'm not sure if we can obtain these)"debugger": a frame for a call to user code invoked by the debugger"dummy": a frame pushed for stupid people (rather—I don't know what this is)
(Note that a "debugger" frame represents the debugger continuation expecting the expression's value; the expression itself has its own "eval" frame.)
"call" and "host" frames.
"call".
"call" and "host" frames.
Debug.Script instance). Present on "call", "eval", and "global" frames. On "call" frames, this is equal to callee.script.
script. Present when script is.
"call", "eval", and "global" frames.
this for the current frame&mdash (a debuggee value). Present on "call", "eval", and "host" frames.
"call", "eval", and "host" frames.
A Debug.Frame instance has the following methods:
- eval(code)
- Begin evaluating code in the scope of this frame. Code is a string. This pushes a
"debugger"frame on the debuggee's stack, evaluates code with all extant hook functions active, and returns a value of the sort passed to anenterFramecompletion function describing how the code completed. - finish(result) (future plan)
- Pop this frame (and any younger frames) from the stack as if this frame had completed. Result is a value of the sort that the
interrupthook might return, indicating how execution should be prepared to continue. Note that this does <emph>not</emph> resume the debuggee's execution; it merely adjusts the debuggee's state to what it would be if this frame's execution had completed. You must returntruefrom the hook function to resume execution in that state. This cannot remove any"host"frames (calls through C++) from the stack. (With some clever hacking we might be able to make this work, but it would be ugly.) - restart(function, this, arguments) (future plan)
- Pop any younger frames from the stack, and then turn this frame into a frame about to begin executing function, with the given this value and arguments. This should be a debuggee value, or
{ asConstructor: true }to invoke function as a constructor. Arguments should be an array of debuggee values. This frame must be a"call"frame. Note that this does <emph>not</emph> resume the debuggee's execution; it merely adjusts the debuggee's state to what it would be if this frame were about to make this call. You must returntruefrom the hook function to resume execution in that state. Likefinish, this cannot remove"host"frames from the stack.
Generator Frames
SpiderMonkey supports generator-iterator objects, which produce a series of values by repeatedly suspending the execution of a function or expression. For example, calling a function that uses yield produces a generator-iterator object, as does evaluating a generator expression.
A generator-iterator object refers to a stack frame with no fixed continuation frame. While the generator's code is running, its continuation is whatever frame called its next method; while the generator is suspended, it has no particular continuation frame; and when it resumes again, the continuation frame for that resumption could be different from that of the previous resumption.
When you use the Debug object to inspect a program that is running a generator frame, that frame appears on the stack like any other call frame, except that its generator property is true. Such a frame will disappear from the stack when it is suspended, and reappear (possibly with a different older frame) each time it is resumed.
Debug.Script
weak reference
Debug.Object
strong reference