Confirmed users
497
edits
m (spelling) |
(Many revisions throughout.) |
||
| Line 9: | Line 9: | ||
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. | 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. | ||
== | == Debuggee Values == | ||
The <code>Debug</code> 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 | The <code>Debug</code> 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. Objects received from the debuggee (including host objects like DOM elements) are fronted in the debugger by <code>Debug.Object</code> instances, which provide reflection-oriented methods for inspecting their referents; see <code>Debug.Object</code>, below. | ||
Of the debugger's objects, only <code>Debug.Object</code> instances may be passed to the debuggee: when this occurs, the debuggee receives the <code>Debug.Object</code>'s referent, not the Debug.Object instance itself. | |||
In the descriptions below, the term "debuggee value" means either a primitive value or a <code>Debug.Object</code> instance; it is a value that might be received from the debuggee, or that could be passed to the debuggee. | In the descriptions below, the term "debuggee value" means either a primitive value or a <code>Debug.Object</code> instance; it is a value that might be received from the debuggee, or that could be passed to the debuggee. | ||
==Beginning to Debug== | == Property Conventions == | ||
The <code>Debug</code> interface creates various sorts of objects to present the debuggee's state to the debugger. These objects' properties follow some conventions: | |||
<ul> | |||
<li>Properties described here are non-configurable. This applies to both "own" and prototype properties, and to data properties and methods. | |||
<li>Non-method properties are generally non-writable value properties. | |||
<li>Instances and prototypes are extensible; you can add your own properties and methods to them. | |||
</ul> | |||
== Beginning to Debug == | |||
To begin debugging another compartment's code, | To begin debugging another compartment's code, create a Debug object for the debuggee compartment. You can use this object to install hook functions, set breakpoints, and so on. | ||
<dl> | <dl> | ||
<dt>Debug(<i>object</i>) | <dt>Debug(<i>object</i>) | ||
<dd>Create a debugger object debugging <i>object</i>'s compartment. <i>Object</i> is typically a global object, but can be any JavaScript object from the debuggee's compartment. | <dd>Create a debugger object debugging <i>object</i>'s compartment. <i>Object</i> is typically a global object, but can be any JavaScript object from the debuggee's compartment. This debugger object is initially enabled; see the <code>enabled</code> property, below. | ||
The <i>object</i> must be in a different compartment than the calling code, and debugger/debuggee compartments may not form a cycle. <i>Object</i>'s compartment must not be in use by another thread while this call runs. | The <i>object</i> must be in a different compartment than the calling code, and debugger/debuggee compartments may not form a cycle. <i>Object</i>'s compartment must not be in use by another thread while this call runs. | ||
</dl> | </dl> | ||
=== Properties of Debug instances === | |||
<dl> | |||
<dt>enabled | |||
<dd>A boolean value indicating whether this <code>Debug</code> instance's hooks and breakpoints are currently enabled. It is an accessor property with a getter and setter: assigning to it enables or disables this <code>Debug</code> instance; reading it produces true if the instance is enabled, or false otherwise. | |||
This property gives debugger code a single point of control for disentangling itself from the debuggee, even as the debugging interface evolves. (For example, when we add a watchpoint interface, disabling the debugger object will disable all its watchpoints as well.) | |||
</dl> | |||
=== Properties of the Debug Prototype Object === | |||
The functions described below may only be called with a <code>this</code> value referring to a <code>Debug</code> instance; they may not be used as methods of other kinds of objects. | |||
<dl> | <dl> | ||
| Line 35: | Line 57: | ||
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. | 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. | ||
Rather than keeping a reference to the <i>hooks</i> object and consulting it when events occur, this function copies the hook functions from the <i>hooks</i> object, and stores them internally. Once the call to <code>setHooks</code> has returned, changes to the <i>hooks</i> object have no effect on the debugger hooks in force. | |||
Note that if the debugger object is disabled, hook functions are not called; see the <code>enabled</code> property. | |||
<dt>getHooks() | <dt>getHooks() | ||
<dd>Return an object holding all the event hooks currently in force. The returned object is suitable for use with <code>setHooks</code>. | <dd>Return an object holding all the event hooks currently in force. The returned object is suitable for use with <code>setHooks</code>. | ||
<dt>setBreakpoint(<i>script</i>, <i>offset</i>, <i>handler</i>) | |||
<dd>Set a breakpoint at the bytecode instruction at <i>offset</i> in <i>script</i>. When execution reaches this point, SpiderMonkey calls the function <i>handler</i> in the debugger's compartment, passing no arguments. Replace any existing trap at the given instruction. | |||
The new breakpoint belongs to this <code>Debug</code> instance; disabling the <code>Debug</code> instance disables this breakpoint. | |||
(While a typical design would have debugger objects that represent breakpoints and handler functions whose behavior is driven by those objects, it seems practical in JavaScript to have the handler function play both roles, since it is itself an object. Other code can access function objects' properties, and the function's own code can find them by referring to the its own name, as in <code>function f() { ... f.x ... }</code>.) | |||
<dt>clearBreakpoint(<i>script</i>, <i>offset</i>) | |||
<dd>Remove any breakpoint set at <i>offset</i> in <i>script</i>. | |||
<dt>clearAllBreakpoints([<i>script</i>]) | |||
<dd>Remove all breakpoints set using this <code>Debug</code> instance. If <i>script</i> is present and refers to a <code>Debug.Script</code> instance, remove all breakpoints set in that script. | |||
</dl> | </dl> | ||
==Debugging hooks== | === 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. | 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. | ||
| Line 55: | Line 95: | ||
<li>If it returns null, the calling code is terminated, as if it had been cancelled by the "slow script" dialog box. | <li>If it returns null, the calling code is terminated, as if it had been cancelled by the "slow script" dialog box. | ||
<li>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. | <li>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. | ||
</ul> | |||
Some notes about the interrupt hook: | |||
<ul> | |||
<li>If a script object has not had its <code>singleStepMode</code> flag set, SpiderMonkey may not call the <code>interrupt</code> hook function when running the script. | |||
<li>Depending on whether the code has been compiled or is being run by the bytecode interpreter, SpiderMonkey may not call the <code>interrupt</code> on every bytecode instruction. However, it will call the <code>interrupt</code> at least every time the source code line and JavaScript statement associated with the current bytecode changes. (That is, multi-line statements may only receive a single interrupt, not one interrupt for each line the statement occupies.) | |||
</ul> | </ul> | ||
| Line 109: | Line 155: | ||
</dl> | </dl> | ||
==Debug.Frame== | == Debug.Frame == | ||
A <code>Debug.Frame</code> instance represents a debuggee stack frame. Given a <code>Debug.Frame</code> instance, you can find the script the frame is executing, walk the stack to older frames, find the lexical environment in which the execution is taking place, and so on. | A <code>Debug.Frame</code> instance represents a debuggee stack frame. Given a <code>Debug.Frame</code> instance, you can find the script the frame is executing, walk the stack to older frames, find the lexical environment in which the execution is taking place, and so on. | ||
| Line 115: | Line 161: | ||
SpiderMonkey creates instances of <code>Debug.Frame</code> as needed in two situations: when it calls a hook function that expects a frame as an argument, and when the debugger reads an existing frame's <code>older</code> property. 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; and walking the stack back to a previously accessed frame yields the same frame object as before. Debugger code can add its own properties to 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. | SpiderMonkey creates instances of <code>Debug.Frame</code> as needed in two situations: when it calls a hook function that expects a frame as an argument, and when the debugger reads an existing frame's <code>older</code> property. 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; and walking the stack back to a previously accessed frame yields the same frame object as before. Debugger code can add its own properties to 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 is a weak reference to the frame; once the debuggee destroys the frame ( | A <code>Debug.Frame</code> instance is a weak reference to the frame; once the debuggee destroys the frame (by returning from the function, completing the <code>eval</code> call, freeing the generator-iterator object, or taking some similar action), the <code>Debug.Frame</code> instance becomes inactive: its <code>live</code> property becomes <code>false</code>, its properties become undefined, and calls to its methods throw exceptions. (Note that this means that debugger code can be affected by the garbage collector, since debugger code can notice when <code>Debug.Frame</code> instances die.) | ||
A <code>Debug.Frame</code> instance has the following properties | === Properties of Debug.Frame instances === | ||
A <code>Debug.Frame</code> instance has the following properties: | |||
<dl> | <dl> | ||
| Line 150: | Line 198: | ||
<dt>offset | <dt>offset | ||
<dd>The offset of the bytecode instruction currently being executed in <code>script</code>. Present when <code>script</code> is. | <dd>The offset of the bytecode instruction currently being executed in <code>script</code>. Present when <code>script</code> is. This is an accessor property with a getter, but no setter. | ||
<dt>environment | <dt>environment | ||
| Line 156: | Line 204: | ||
<dt>this | <dt>this | ||
<dd>The value of <code>this</code> for the current frame (a debuggee value). Present on <code>"call"</code>, <code>"eval"</code>, and <code>"host"</code> frames. | <dd>The value of <code>this</code> for the current frame (a debuggee value). Present on <code>"call"</code>, <code>"eval"</code>, and <code>"host"</code> frames. This is an accessor property with a getter, but no setter. | ||
<dt>arguments | <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. | <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. | ||
<dt>live | |||
<dd>True if the frame this <code>Debug.Frame</code> instance refers to still exists in the debuggee; false if it has been destroyed. | |||
</dl> | </dl> | ||
=== Properties of the Debug.Frame Prototype Object === | |||
The functions described below may only be called with a <code>this</code> value referring to a <code>Debug.Frame</code> instance; they may not be used as methods of other kinds of objects. | |||
<dl> | <dl> | ||
| Line 208: | Line 261: | ||
The Debug interface constructs <code>Debug.Script</code> objects as script objects are uncovered by the debugger: via the <code>newScript</code> hook; via <code>Debug.Frame</code>'s <code>script</code> properties; via the <code>functionScript</code> method of <code>Debug.Object</code> instances; and so on. It constructs exactly one <code>Debug.Script</code> instance for each underlying script object; debugger code can add its own properties to a script object and expect to find them later, use <code>==</code> to decide whether two expressions refer to the same script, and so on. | The Debug interface constructs <code>Debug.Script</code> objects as script objects are uncovered by the debugger: via the <code>newScript</code> hook; via <code>Debug.Frame</code>'s <code>script</code> properties; via the <code>functionScript</code> method of <code>Debug.Object</code> instances; and so on. It constructs exactly one <code>Debug.Script</code> instance for each underlying script object; debugger code can add its own properties to a script object and expect to find them later, use <code>==</code> to decide whether two expressions refer to the same script, and so on. | ||
A <code>Debug.Script</code> instance is a weak reference to a JSScript object; it does not protect the script it refers to from being freed or garbage collected. When SpiderMonkey decides to free the script, it calls the <code>destroyScript</code> debugger hook function to let the debugger know. Once that call has returned, the <code>Debug.Script</code> object | A <code>Debug.Script</code> instance is a weak reference to a JSScript object; it does not protect the script it refers to from being freed or garbage collected. When SpiderMonkey decides to free the script, it calls the <code>destroyScript</code> debugger hook function to let the debugger know. Once that call has returned, the <code>Debug.Script</code> object's <code>live</code> property becomes false, its properties become undefined, and calls to its methods throw exceptions. (Note that this means that debugger code can be affected by the garbage collector, since debugger code can notice when <code>Debug.Script</code> instances die.) | ||
The lifetime of a script depends on its origin. A function script is shared by all closures produced for that function, and lives as long as there are live closures referring to it. An <code>eval</code> script | The lifetime of a script depends on its origin. A function script is shared by all closures produced for that function, and lives as long as there are live closures referring to it. An <code>eval</code> script lives until the call to <code>eval</code> completes. Debuggers should avoid depending on scripts' lifetimes after the stack frames, functions, and so on that own them have been destroyed. | ||
=== Properties of | === Properties of Debug.Script instances === | ||
<dl> | <dl> | ||
<dt> | <dt>url | ||
<dd> | <dd>The filename or URL from which this script's code was loaded. | ||
<dt> | <dt>startLine | ||
<dd> | <dd>The number of the line at which this script's code starts, within the file or document named by <code>url</code>. | ||
<dt>length | |||
<dd>The number of lines this script's code occupies, within the file or document named by <code>url</code>. | |||
<dt> | <dt>singleStepMode | ||
< | <dd>True if executing this script will produce calls to the <code>Debug</code> instance's <code>interrupt</code> hook for each source line. This is an accessor property with a getter and setter; assigning a true value to this property enables single stepping mode for this script. The property reads true if single step mode mode is set, or false otherwise. | ||
<dt> | <dt>live | ||
<dd> | <dd>True if the script this <code>Debug.Script</code> instance refers to still exists in the debuggee; false if it has been destroyed. | ||
</dl> | |||
=== Properties of the Debug.Script Prototype Object === | |||
The functions described below may only be called with a <code>this</code> value referring to a <code>Debug.Script</code> instance; they may not be used as methods of other kinds of objects. | |||
<dt> | <dl> | ||
<dd>Return | <dt>decompile([<i>pretty</i>]) | ||
<dd>Return a string containing JavaScript source code equivalent to this script in its effect and result. If <i>pretty</i> is present and true, produce indented code with line breaks. | |||
<dt> | <dt>getAllOffsets() | ||
< | <dd>Return an array <i>L</i> describing the relationship between bytecode instruction offsets and source code positions in this script. <i>L</i> is sparse, and indexed by source line number. If a source line number <i>line</i> has no code, then <i>L</i> has no <i>line</i> property. If there is code for <i>line</i>, then <code><i>L</i>[<i>line</i>]</code> is an array of offsets of byte code instructions that are entry points to that line. | ||
</ | |||
== | For example, suppose we have a script for the following source code: | ||
a=[] | |||
for (i=1; i < 10; i++) | |||
// It's hip to be square. | |||
a[i] = i*i; | |||
< | Calling <code>getAllOffsets()</code> on that code might yield an array like this: | ||
< | [[0], [5, 20], , [10]] | ||
< | This array indicates that: | ||
< | <ul> | ||
<li>the first line's code starts at offset 0 in the script; | |||
<li>the <code>for</code> statement head has two entry points at offsets 5 and 20 (for the initialization, which is performed only once, and the loop test, which is performed at the start of each iteration); | |||
<li>the third line has no code; | |||
<li>and the fourth line begins at offset 10. | |||
</ul> | |||
<dt> | <dt>getLineOffsets(<i>line</i>) | ||
<dd> | <dd>Return an array of bytecode instruction offsets representing the entry points to source line <i>line</i>. If the script contains no executable code at that line, the array returned is empty. | ||
<dt> | <dt>getOffsetLine(<i>offset</i>) | ||
<dd> | <dd>Return the source code line responsible for the bytecode at <i>offset</i> in this script. | ||
</dl> | </dl> | ||
== Debug.Object== | == Debug.Object == | ||
A <code>Debug.Object</code> instance represents an object in the debuggee. Debugger code never accesses debuggee objects directly; instead, it operates on <code>Debug.Object</code> instances that refer to the debuggee objects. SpiderMonkey's compartment system ensures that this separation is respected. | A <code>Debug.Object</code> instance represents an object in the debuggee. Debugger code never accesses debuggee objects directly; instead, it operates on <code>Debug.Object</code> instances that refer to the debuggee objects. SpiderMonkey's compartment system ensures that this separation is respected. | ||
| Line 269: | Line 329: | ||
While most <code>Debug.Object</code> instances are created by SpiderMonkey in the process of exposing debuggee's behavior and state to the debugger, the debugger can apply the <code>Debug.Object</code> constructor to its own objects, to copy them into the debuggee; see the description of the <code>Debug.Object</code> constructor below. | While most <code>Debug.Object</code> instances are created by SpiderMonkey in the process of exposing debuggee's behavior and state to the debugger, the debugger can apply the <code>Debug.Object</code> constructor to its own objects, to copy them into the debuggee; see the description of the <code>Debug.Object</code> constructor below. | ||
<code>Debug.Object</code> instances protect their referents from the garbage collector; as long as the <code>Debug.Object</code> instance is live, the referent remains live. Garbage collection has no debugger-visible effect. | <code>Debug.Object</code> instances protect their referents from the garbage collector; as long as the <code>Debug.Object</code> instance is live, the referent remains live. Garbage collection has no debugger-visible effect on <code>Debug.Object</code> instances. | ||
=== The | === The Debug.Object constructor === | ||
When called via a <code>new</code> expression, the <code>Debug.Object</code> constructor takes one argument, an object in the debugger's compartment, and applies the HTML5 "structured cloning" algorithm to copy the object into the debuggee's compartment. It then returns a <code>Debug.Object</code> instance referring to the copy. It is an error to apply <code>Debug.Object</code> to a primitive value via a <code>new</code> expression. | When called via a <code>new</code> expression, the <code>Debug.Object</code> constructor takes one argument, an object in the debugger's compartment, and applies the HTML5 "structured cloning" algorithm to copy the object into the debuggee's compartment. It then returns a <code>Debug.Object</code> instance referring to the copy. It is an error to apply <code>Debug.Object</code> to a primitive value via a <code>new</code> expression. | ||
| Line 277: | Line 337: | ||
When applied as a function, <code>Debug.Object</code> behaves as above, except that primitive values are returned unchanged (although possibly wrapped, in an ordinary cross-compartment wrapper). This allows the debugger to use <code>Debug.Object</code> as a generic "debugger value to debuggee value" conversion function. | When applied as a function, <code>Debug.Object</code> behaves as above, except that primitive values are returned unchanged (although possibly wrapped, in an ordinary cross-compartment wrapper). This allows the debugger to use <code>Debug.Object</code> as a generic "debugger value to debuggee value" conversion function. | ||
=== Properties of the | === Properties of the Debug.Object constructor === | ||
<dl> | <dl> | ||
| Line 284: | Line 344: | ||
</dl> | </dl> | ||
=== Properties of the | === Properties of the Debug.Object prototype === | ||
The functions described below may only be called with a <code>this</code> value referring to a <code>Debug.Object</code> instance; they may not be used as methods of other kinds of objects. The descriptions use "referent" to mean "the referent of this <code>Debug.Object</code> instance". | The functions described below may only be called with a <code>this</code> value referring to a <code>Debug.Object</code> instance; they may not be used as methods of other kinds of objects. The descriptions use "referent" to mean "the referent of this <code>Debug.Object</code> instance". | ||
| Line 334: | Line 394: | ||
<dd>Return true if the referent has a <code><nowiki>[[Call]]</nowiki></code> internal method. | <dd>Return true if the referent has a <code><nowiki>[[Call]]</nowiki></code> internal method. | ||
<dt> | <dt>getFunctionName() | ||
<dd>If the referent is a named function, return its name. If the referent is an anonymous function, return <code>null</code>. If the referent is not a function, | <dd>If the referent is a named function, return its name. If the referent is an anonymous function, return <code>null</code>. If the referent is not a function, throw a <code>TypeError</code>. | ||
<dt> | <dt>getFunctionParameterNames() | ||
<dd>If the referent is a function, return the names of its parameters as an array of strings. If the referent is not a function, | <dd>If the referent is a function, return the names of its parameters as an array of strings. If the referent is not a function, throw a <code>TypeError</code>. | ||
<dt> | <dt>getFunctionScript() | ||
<dd>If the referent is a function, return its script, as a <code>Debug.Script</code> instance. If the referent is not a function, | <dd>If the referent is a function, return its script, as a <code>Debug.Script</code> instance. If the referent is not a function, throw a <code>TypeError</code>. | ||
<dt> | <dt>getFunctionScope() | ||
<dd>If the referent is a function, return a <code>Debug.Object</code> instance referring to the lexical scope that enclosed the function when it was created. If the referent is not a function, | <dd>If the referent is a function, return a <code>Debug.Object</code> instance referring to the lexical scope that enclosed the function when it was created. If the referent is not a function, throw a <code>TypeError</code>. | ||
<dt> | <dt>decompile([<i>pretty</i>]) | ||
<dd>If the referent is a function, return | <dd>If the referent is a function, return the source code for a JavaScript function definition equivalent to this function in its effect and result, as a string. If the referent is not a function, throw a <code>TypeError</code>. If <i>pretty</i> is present and true, produce indented code with line breaks. | ||
<dt>parent() | <dt>parent() | ||
<dd>If the referent is part of a chain of lexical scopes, return a <code>Debug.Object</code> instance referring to its enclosing lexical scope, or <code>null</code> if it is the outermost scope. If the referent is not part of such a chain, | <dd>If the referent is part of a chain of lexical scopes, return a <code>Debug.Object</code> instance referring to its enclosing lexical scope, or <code>null</code> if it is the outermost scope. If the referent is not part of such a chain, throw a <code>TypeError</code>. | ||
<dt>referentToString() | <dt>referentToString() | ||
<dd>Return a string representing the referent, showing its class and any other useful information, without invoking its <code>toString</code> or <code>toSource</code> | <dd>Return a string representing the referent, showing its class and any other useful information, without invoking its <code>toString</code> or <code>toSource</code> methods, or running any other debuggee code. The specific string returned is unspecified. (It is better to add functions to <code>Debug.Object.prototype</code> that retrieve the information you need about the object than to depend on details of <code>safeToString</code>'s behavior.) | ||
(Note that simply calling the <code>toString</code> method of a <code>Debug.Object</code> instance applies to instance itself, not its referent, and thus returns something like <code>"[Object Debug.Object]"</code>.) | (Note that simply calling the <code>toString</code> method of a <code>Debug.Object</code> instance applies to instance itself, not its referent, and thus returns something like <code>"[Object Debug.Object]"</code>.) | ||
</dl> | </dl> | ||