Confirmed users
497
edits
(→Properties of the Debug.Object prototype: Add 'call' and 'apply' methods.) |
(Pull out descriptions of completion values and resumption values into their own sections.) |
||
| 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. | ||
== | == General Conventions == | ||
The <code>Debug</code> | === Properties === | ||
The objects comprising the <code>Debug</code> interface and the objects that interface creates to present the debuggee's state to the debugger follow some conventions: | |||
<ul> | |||
<li>Properties described here are configurable. This applies to both "own" and prototype properties, and to both methods and data properties. (Leaving these properties open to redefinition will hopefully make it easier for JavaScript debugger code to cope with bugs, bug fixes, and changes in the interface over time.) | |||
<li>Properties, both data and methods, are non-writable, unless stated otherwise. Since they are configurable, they can be made writable, but assigning to them has no effect on the debuggee unless stated otherwise. | |||
<li>Instances and prototypes are extensible; you can add your own properties and methods to them. | |||
</ul> | |||
=== Debuggee Values === | |||
The <code>Debug</code> interface follows some 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. | 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. | ||
| Line 17: | Line 28: | ||
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. | ||
== | === Completion Values === | ||
The <code>Debug</code> interface | When a debuggee stack frame completes its execution, or when some sort of debuggee call initiated by the debugger finishes, the <code>Debug</code> interface provides a value describing how the code completed; these are called <i>completion values</i>. A completion value has one of the following forms: | ||
< | <dl> | ||
< | <dt>{ return: <i>value</i> } | ||
< | <dd>The code completed normally, yielding <i>value</i>. <i>Value</i> is a debuggee value. | ||
< | <dt>{ throw: <i>value</i> } | ||
</ | <dd>The code threw <i>value</i> as an exception. <i>Value</i> is a debuggee value. | ||
<dt>null | |||
<dd>The code was terminated, as if by the "slow script" dialog box. | |||
</dl> | |||
=== Resumption Values === | |||
As the debuggee runs, the <code>Debug</code> interface calls various debugger-provided handler functions to report the debuggee's behavior. Some of these calls can return a value indicating how the debuggee's execution should continue; these are called <i>resumption values</i>. A resumption value has one of the following forms: | |||
<dl> | |||
<dt>true | |||
<dd>The debuggee should continue execution normally. | |||
<dt>{ return: <i>value</i> } | |||
<dd>Return <i>value</i> immediately as the current value of the function. <i>Value</i> must be a debuggee value. (Most handler functions support this, except those whose description says otherwise.) | |||
<dt>{ throw: <i>value</i> } | |||
<dd>Throw <i>value</i> as an execption from the current bytecode instruction. <i>Value</i> must be a debuggee value. | |||
<dt>null | |||
<dd>Terminate the debuggee, as if it had been cancelled by the "slow script" dialog box. | |||
</dl> | |||
== Beginning to Debug == | == Beginning to Debug == | ||
| Line 68: | Line 96: | ||
<dd>Set a breakpoint at the bytecode instruction at <i>offset</i> in <i>script</i>, reporting hits to the function <i>handler</i>. This call replaces any existing breakpoint at the given location. | <dd>Set a breakpoint at the bytecode instruction at <i>offset</i> in <i>script</i>, reporting hits to the function <i>handler</i>. This call replaces any existing breakpoint at the given location. | ||
When execution reaches the given instruction, SpiderMonkey calls <i>handler</i>, passing a <code>Debug.Frame</code> instance representing the currently executing stack frame. The handler method's return value | When execution reaches the given instruction, SpiderMonkey calls <i>handler</i>, passing a <code>Debug.Frame</code> instance representing the currently executing stack frame. The handler method's return value should be a [[#Resumption Values|resumption value]], determining how execution should continue. | ||
Breakpoint handler calls are cross-compartment, intra-thread calls: <i>handler</i> must be a function in the debugger's compartment (and thus calls to it take place in the debugger's compartment), and the call takes place in the same thread that hit the breakpoint. | Breakpoint handler calls are cross-compartment, intra-thread calls: <i>handler</i> must be a function in the debugger's compartment (and thus calls to it take place in the debugger's compartment), and the call takes place in the same thread that hit the breakpoint. | ||
| Line 88: | Line 111: | ||
<dt>setPropertyWatchpoint(<i>object</i>, <i>name</i>, <i>handler</i>) <i>(future plan)</i> | <dt>setPropertyWatchpoint(<i>object</i>, <i>name</i>, <i>handler</i>) <i>(future plan)</i> | ||
<dd>Set a watchpoint on the own property named <i>name</i> of the referent of the <code>Debug.Object</code> instance <i>object</i>, reporting events by calling <i>handler</i>'s methods. <i>Handler</i> may have the following methods, called under the given circumstances: | <dd>Set a watchpoint on the own property named <i>name</i> of the referent of the <code>Debug.Object</code> instance <i>object</i>, reporting events by calling <i>handler</i>'s methods. <i>Handler</i> may have the following methods, called under the given circumstances: | ||
<dl> | <dl> | ||
| Line 99: | Line 122: | ||
<dt>set(<i>frame</i>, <i>value</i>) | <dt>set(<i>frame</i>, <i>value</i>) | ||
<dd>The property named <i>name</i> is about to have <i>value</i> assigned to it. The assignment has not yet taken place, so the debugger can use <i>getOwnPropertyDescriptor</i> to find the property's pre-assignment value (assuming it is not an accessor property). This call takes place whether the property is a data or accessor property, even if the property is not writable or lacks a setter. This call takes place even if the property is a value property and <i>value</i> is identical to its current value. | <dd>The property named <i>name</i> is about to have <i>value</i> assigned to it. The assignment has not yet taken place, so the debugger can use <i>getOwnPropertyDescriptor</i> to find the property's pre-assignment value (assuming it is not an accessor property). This call takes place whether the property is a data or accessor property, even if the property is not writable or lacks a setter. This call takes place even if the property is a value property and <i>value</i> is identical to its current value. | ||
If this call includes a <code>value</code> property, whose value is a debuggee value <i>newValue</i>, in the [[#Resumption Values|resumption value]] it returns, the assignment stores <i>newValue</i> instead of <i>value</i>. | |||
<dt>get(<i>frame</i>) | <dt>get(<i>frame</i>) | ||
<dd>The property named <i>name</i> is about to be read. This call takes place whether the property is a data or accessor property, even if the property lacks a getter. | <dd>The property named <i>name</i> is about to be read. This call takes place whether the property is a data or accessor property, even if the property lacks a getter. | ||
</dl> | </dl> | ||
In all cases, the method's <i>frame</i> argument is the current stack frame, whose code is about to perform the operation on the object being reported. | In all cases, the method's <i>frame</i> argument is the current stack frame, whose code is about to perform the operation on the object being reported. Methods should return [[#Resumption Values|resumption values]]; however, watchpoint handler methods may not return <code>return</code> resumption values. | ||
If a given method is absent from <i>handler</i>, then events of the given sort are ignored. The watchpoint retains a reference to the <i>handler</i> object itself, and consults its properties each time an event occurs, so adding methods to or removing methods from <i>handler</i> after setting the watchpoint enables or disables reporting of the corresponding events. | If a given method is absent from <i>handler</i>, then events of the given sort are ignored. The watchpoint retains a reference to the <i>handler</i> object itself, and consults its properties each time an event occurs, so adding methods to or removing methods from <i>handler</i> after setting the watchpoint enables or disables reporting of the corresponding events. | ||
| Line 129: | Line 152: | ||
<dl> | <dl> | ||
<dt>interrupt(<i>frame</i>) | <dt>interrupt(<i>frame</i>) | ||
<dd>A bytecode instruction is about to execute in the stack frame represented by <i>frame</i>, a <code>Debug.Frame</code> instance. Naturally, <i>frame</i> is the youngest debuggee frame. | <dd>A bytecode instruction is about to execute in the stack frame represented by <i>frame</i>, a <code>Debug.Frame</code> instance. Naturally, <i>frame</i> is the youngest debuggee frame. This hook function should return a [[#Resumption Values|resumption value]] specifying how the debuggee's execution should proceed. | ||
This hook function | |||
Some notes about the interrupt hook: | Some notes about the interrupt hook: | ||
| Line 150: | Line 164: | ||
Note that <i>script</i> may be a temporary script, created for a call to <i>eval</i> and destroyed when its execution is complete. | Note that <i>script</i> may be a temporary script, created for a call to <i>eval</i> and destroyed when its execution is complete. | ||
This hook's return value is ignored. | |||
<dt>destroyScript(<i>script</i>) | <dt>destroyScript(<i>script</i>) | ||
<dd>SpiderMonkey has determined that <i>script</i> 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 <i>eval</i> has finished executing the script, and is about to destroy it. In any case, operations on <i>script</i> after this hook function returns will throw an error. | <dd>SpiderMonkey has determined that <i>script</i> 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 <i>eval</i> has finished executing the script, and is about to destroy it. In any case, operations on <i>script</i> after this hook function returns will throw an error. | ||
This hook's return value is ignored. | |||
<dt>debuggerHandler(<i>frame</i>) | <dt>debuggerHandler(<i>frame</i>) | ||
<dd>The debuggee has executed a <i>debugger</i> statement in <i>frame</i>. This hook function | <dd>The debuggee has executed a <i>debugger</i> statement in <i>frame</i>. This hook function should return a [[#Resumption Values|resumption value]] specifying how the debuggee's execution should proceed. | ||
<dt>sourceHandler(<i>ASuffusionOfYellow</i>) | <dt>sourceHandler(<i>ASuffusionOfYellow</i>) | ||
| Line 163: | Line 181: | ||
<dd>The stack frame <i>frame</i> is about to begin executing code. (Naturally, <i>frame</i> is currently the youngest debuggee frame.) If <i>call</i> is true, it is a function call; if <i>call</i> is false, it is global or eval code. | <dd>The stack frame <i>frame</i> is about to begin executing code. (Naturally, <i>frame</i> is currently the youngest debuggee frame.) If <i>call</i> is true, it is a function call; if <i>call</i> is false, it is global or eval code. | ||
This hook function should return either <code>null</code> or a function. If it returns a function <i>f</i>, SpiderMonkey will call <i>f</i> when execution of <i>frame</i> completes, passing a [[#Completion Values|completion value]] as its argument. This call takes place just before the frame is popped from the stack. | |||
<dt>throw(<i>frame</i>, <i>value</i>) | <dt>throw(<i>frame</i>, <i>value</i>) | ||
<dd>The code running in <i>frame</i> is about to throw <i>value</i> as an exception. | <dd>The code running in <i>frame</i> is about to throw <i>value</i> as an exception. This hook function should return a [[#Resumption Values|resumption value]] specifying how the debuggee's execution should proceed. | ||
<dt>error(<i>frame</i>, <i>report</i>) | <dt>error(<i>frame</i>, <i>report</i>) | ||
| Line 197: | Line 210: | ||
</dl> | </dl> | ||
</dl> | </dl> | ||
This hook's return value is ignored. | |||
== Debug.Frame == | == Debug.Frame == | ||
| Line 262: | Line 277: | ||
<dl> | <dl> | ||
<dt>eval(<i>code</i>) | <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>, and returns a value | <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>, and returns a [[#Completion Values|completion value]]. All extant hook functions, breakpoints, watchpoints, and so on remain active during the call. | ||
Note that, although this method mixes the debugger's own stack frames with the debuggee's, walking the stack only shows the debuggee's frames; the continuation of the debugger's call to this method, up to the debugging hook function call, is represented by a single <code>"debugger"</code> frame. The next younger frame is an <code>"eval"</code> frame running <i>code</i> itself. | Note that, although this method mixes the debugger's own stack frames with the debuggee's, walking the stack only shows the debuggee's frames; the continuation of the debugger's call to this method, up to the debugging hook function call, is represented by a single <code>"debugger"</code> frame. The next younger frame is an <code>"eval"</code> frame running <i>code</i> itself. | ||
| Line 271: | Line 286: | ||
This method allows debugger code to introduce temporary bindings that are visible to the given debuggee code and which refer to debugger-held debuggee values, without mutating any existing debuggee scope. | This method allows debugger code to introduce temporary bindings that are visible to the given debuggee code and which refer to debugger-held debuggee values, without mutating any existing debuggee scope. | ||
<dt>finish( | <dt>finish() <i>(future plan)</i> | ||
<dd>Pop this frame (and any younger frames) from the stack as if this frame had completed | <dd>Pop this frame (and any younger frames) from the stack as if this frame had completed. | ||
Note that this does <i>not</i> resume the debuggee's execution; it merely adjusts the debuggee's state to what it would be if this frame's execution had completed. | Note that this does <i>not</i> resume the debuggee's execution; it merely adjusts the debuggee's state to what it would be if this frame's execution had completed. The [[#Resumption Values|resumption value]] you return from the hook function determines how execution proceeds. | ||
This cannot remove any <code>"host"</code> frames (calls through C++) from the stack. (We might be able to make this work eventually, but it will take some cleverness.) | This cannot remove any <code>"host"</code> frames (calls through C++) from the stack. (We might be able to make this work eventually, but it will take some cleverness.) | ||
| Line 283: | Line 298: | ||
This can be used as a primitive in implementing some forms of a "patch and continue" debugger feature. | This can be used as a primitive in implementing some forms of a "patch and continue" debugger feature. | ||
Note that this does <i>not</i> 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. | Note that this does <i>not</i> 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. The [[#Resumption Values|resumption value]] you return from the hook function determines how execution proceeds. | ||
Like <code>finish</code>, this cannot remove <code>"host"</code> frames from the stack. | Like <code>finish</code>, this cannot remove <code>"host"</code> frames from the stack. | ||
| Line 338: | Line 353: | ||
<dl> | <dl> | ||
<dt>decompile([<i>pretty</i>]) | <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. | <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. (Note that <code>Debug.Object</code> instances referring to functions also have a <code>decompile</code> method, whose result includes the function header and parameter names, so it is probably better to write <code>f.decompile()</code> than to write <code>f.getFunctionScript().decompile()</code>.) | ||
<dt>getAllOffsets() | <dt>getAllOffsets() | ||
| Line 458: | Line 473: | ||
<dt>call(<i>this</i>, <i>argument</i>, ...) | <dt>call(<i>this</i>, <i>argument</i>, ...) | ||
<dd>If the referent is a function, push a <code>"debugger"</code> frame on the debuggee's stack, call the referent with the given <i>this</i> value and <i>argument</i> values, pop the <code>"debugger"</code> frame, and return a value | <dd>If the referent is a function, | ||
<ul> | |||
<li>push a <code>"debugger"</code> frame on the debuggee's stack, | |||
<li>call the referent with the given <i>this</i> value and <i>argument</i> values, | |||
<li>pop the <code>"debugger"</code> frame, and | |||
<li>return a [[#Completion Values|completion value]] describing how the call completed. | |||
</ul> | |||
<i>This</i> and <i>argument</i> must be debuggee values. All extant hook functions, breakpoints, watchpoints, and so on remain active during the call. If the referent is not a function, throw a <code>TypeError</code>. | |||
<dt>apply(<i>function</i>, <i>this</i>, <i>arguments</i>) | <dt>apply(<i>function</i>, <i>this</i>, <i>arguments</i>) | ||
<dd>If the referent is a function, push a <code>"debugger"</code> frame on the debuggee's stack, call the referent with the given <i>this</i> and argument values in <i>arguments</i>, pop the <code>"debugger"</code> frame, and return a value | <dd>If the referent is a function, | ||
<ul> | |||
<li>push a <code>"debugger"</code> frame on the debuggee's stack, | |||
<li>call the referent with the given <i>this</i> value and argument values in <i>arguments</i>, | |||
<li>pop the <code>"debugger"</code> frame, and | |||
<li>return a [[#Completion Values|completion value]] describing how the call completed. | |||
</ul> | |||
<i>This</i> must be a debuggee value, and <i>arguments</i> must be an array (in the debugger) of debuggee values. All extant hook functions, breakpoints, watchpoints, and so on remain active during the call. If the referent is not a function, throw a <code>TypeError</code>. | |||
<dt>outerEnvironment() | <dt>outerEnvironment() | ||