This interface is partly implemented in Firefox Nightly builds. Some parts are not implemented, some APIs may yet change, and anything marked "(future plan)" is a long way off. You can [https://github.com/jimblandy/DebuggerDocs fork this specification on github] to draft and discuss revisions.
The Debugger
constructor makes objects with methods for debugging code running in global objects in other compartments. Given a Debugger
instance, you can:
- add debuggee global objects to its purview using its
addDebuggee
method; - request notification of basic debugging events like stack frame entry and
debugger
statement execution by providing appropriate handler functions; - set breakpoints in scripts;
- set watchpoints on objects and their properties;
- examine the debuggee's stack frames and lexical enviroments;
- inspect and manipulate its objects;
Each global object may be debugged by any number of Debugger
instances simultaneously.
Your event handling methods run in the same thread as the debuggee, on the same JavaScript stack: when the event occurs, the debuggee pauses while your handler methods run, and resumes (unless you say otherwise) when your methods return. Your event handling methods run in the compartment to which they belong, typically the debugger's compartment. The compartment system mediates the debugger's and debuggee's access to each other's objects.
The Debugger
object provides various sorts of objects representing the debuggee's state, which the debugger code can examine and manipulate:
Debugger.Object
instances represent objects in the debuggee.Debugger.Frame
instances represent debuggee stack frames.Debugger.Script
instances represent the debuggee's code, whether it is a function body, code passed toeval
, or a top-level script.Debugger.Environment
instances represent the variable environments in effect at different points in the debuggee's code.
A Debugger
instance can only debug code running in other compartments. You may not create cycles of debugger/debuggee compartments.
The Debugger
interface does not itself support cross-thread or multi-threaded debugging. As a general rule, only one thread may use a compartment at a time. When a debugger in one compartment is debugging globals in another, many kinds of events in the debuggees may cause the debugger to call its handler methods. Conversely, a call to almost any method in this interface could cause the debugger to try to interact with a debuggee in some way. Thus, as a general rule, you should never permit different threads to run in the debugger and debuggee compartments simultaneously. Tools should instead use the [[Remote Debugging Protocol]] for all inter-thread communication.
== General Conventions ==
=== Properties ===
Properties of objects that comprise the Debugger
interface, and those that the interface creates, follow some general conventions:
- Instances and prototypes are extensible; you can add your own properties and methods to them.
- Properties 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.)
- Method properties are writable.
- We prefer inherited accessor properties to own data properties. Both are read using the same syntax, but inherited accessors seem like a more accurate reflection of what's going on. Unless otherwise noted, these properties have getters but no setters, as they cannot meaningfully be assigned to.
=== Debuggee Values ===
The Debugger
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 Debugger.Object
instances, which provide reflection-oriented methods for inspecting their referents; see Debugger.Object
, below.
Of the debugger's objects, only Debugger.Object
instances may be passed to the debuggee: when this occurs, the debuggee receives the Debugger.Object
's referent, not the Debugger.Object
instance itself.
In the descriptions below, the term "debuggee value" means either a primitive value or a Debugger.Object
instance; it is a value that might be received from the debuggee, or that could be passed to the debuggee.
=== Debuggee Code ===
Each Debugger
instance maintains a set of global objects that, taken together, comprise the debuggee. Code evaluated in the scope of a debuggee global object, directly or indirectly, is considered debuggee code. Similarly:
- a debuggee frame is a frame running debuggee code;
- a debuggee function is a function that closes over a debuggee global object (and thus the function's code is debuggee code); and
- a debuggee script is a script containing debuggee code.
=== Completion Values ===
When a debuggee stack frame completes its execution, or when some sort of debuggee call initiated by the debugger finishes, the Debugger
interface provides a value describing how the code completed; these are called completion values. A completion value has one of the following forms:
- { return: value }
- The code completed normally, returning value. Value is a debuggee value.
- { yield: value }
- The running code is a generator frame which has yielded value. Value is a debuggee value.
- { throw: value }
- The code threw value as an exception. Value is a debuggee value.
- null
- The code was terminated, as if by the "slow script" dialog box.
If control reaches the end of a generator frame, the completion value is {throw: stop}
where ''stop'' is a Debugger.Object
object representing the StopIteration
object being thrown.
=== Resumption Values ===
As the debuggee runs, the Debugger
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 resumption values. A resumption value has one of the following forms:
- undefined
- The debuggee should continue execution normally.
- { return: value }
- Return value immediately as the current value of the function. Value must be a debuggee value. (Most handler functions support this, except those whose descriptions say otherwise.) If the function was called as a constructor (that is, via a
new
expression), then value serves as the value returned by the function's body, not that produced by thenew
expression: if the value is not an object, thenew
expression returns the frame'sthis
value. - { yield: value }
- Yield value immediately as the next value of the current frame, which must be a generator frame. Value is a debuggee value. The current frame must be a generator frame that has not yet completed in some other way. You may use
yield
resumption values to substitute a new value or one already yielded by a generator, or to make a generator yield additional values. - { throw: value }
- Throw value as an execption from the current bytecode instruction. Value must be a debuggee value.
- null
- Terminate the debuggee, as if it had been cancelled by the "slow script" dialog box.
If a function that would normally return a resumption value to indicate how the debuggee should continue instead throws an exception, we never propagate such an exception to the debuggee; instead, we call the associated Debugger
instance's uncaughtExceptionHook
property, as described below.
=== The Debugger.DebuggeeWouldRun Exception ===
Some debugger operations that appear to simply inspect the debuggee's state may actually cause debuggee code to run. For example, reading a variable might run a getter function on the global or on a with
expression's operand; and getting an object's property descriptor will run a handler trap if the object is a proxy. To protect the debugger's integrity, only methods whose stated purpose is to run debuggee code can do so. These methods are called [[#Invocation_Functions_and_.22debugger.22_Frames|invocation functions]], and they follow certain common conventions to report the debuggee's behavior safely. For other methods, if their normal operation would cause debuggee code to run, they throw an instance of the Debugger.DebuggeeWouldRun
exception.
A Debugger.DebuggeeWouldRun
exception may have a cause
property, providing more detailed information on why the debuggee would have run. The cause
property's value is one of the following strings:
{| frame="box" rules="all" cellpadding="8" ! cause value
! meaning |
---|
"proxy" |
Carrying out the operation would have caused a proxy handler to run. |
- |
"getter" |
Carrying out the operation would have caused an object property getter to run. |
- |
"setter" |
Carrying out the operation would have caused an object property setter to run. |
} |
If the system can't determine why control attempted to enter the debuggee, it will leave the exception's cause
property undefined.
== The Debugger Object ==
When called as a constructor, the Debugger
object creates a new Debugger
instance.
- new Debugger([global, ...])
- Create a debugger object, and apply its
addDebuggee
method to each of the given global objects to add them as the initial debuggees.
=== Accessor Properties of the Debugger Prototype Object ===
A Debugger
instance inherits the following accessor properties from its prototype:
- enabled
- A boolean value indicating whether this
Debugger
instance's handlers, breakpoints, watchpoints, and the like are currently enabled. It is an accessor property with a getter and setter: assigning to it enables or disables thisDebugger
instance; reading it produces true if the instance is enabled, or false otherwise. This property is initiallytrue
in a freshly createdDebugger
instance.This property gives debugger code a single point of control for disentangling itself from the debuggee, regardless of what sort of events or handlers or "points" we add to the interface.
- uncaughtExceptionHook
- Either
null
or a function that SpiderMonkey calls when a call to a debug event handler, breakpoint handler, watchpoint handler, or similar function throws some exception, debugger-exception. Exceptions thrown in the debugger are not propagated to debuggee code; instead, SpiderMonkey calls this function, passing debugger-exception as its sole argument and theDebugger
instance as thethis
value. This function should return a [[#Resumption_Values|resumption value]], which determines how the debuggee should continue.If the uncaught exception hook itself throws an exception, uncaught-hook-exception, SpiderMonkey throws a new error object, confess-to-debuggee-exception, to the debuggee whose message blames the debugger, and includes textual descriptions of uncaught-hook-exception and the original debugger-exception.
If
uncaughtExceptionHook
's value isnull
, SpiderMonkey throws an exception to the debuggee whose message blames the debugger, and includes a textual description of debugger-exception.Assigning anything other than a callable value or
null
to this property throws aTypeError
exception.(This is not an ideal way to handle debugger bugs, but the hope here is that some sort of backstop, even if imperfect, will make life easier for debugger developers. For example, an uncaught exception hook may have access to browser-level features like the
alert
function, which this API's implementation does not, making it possible to present debugger errors to the developer in a way suited to the context.)
=== Debugger Handler Functions ===
Each Debugger
instance inherits accessor properties with which you can store handler functions for SpiderMonkey to call when given events occur in debuggee code.
When one of the events described below occurs in debuggee code, the engine pauses the debuggee and calls the corresponding debugging handler on each Debugger
instance that is observing the debuggee. The handler functions receive the Debugger
instance as their this
value. Most handler functions can return a [[#Resumption_Values|resumption value]] indicating how the debuggee's execution should proceed.
On a new Debugger
instance, each of these properties is initially undefined
. Any value assigned to a debugging handler must be either a function or undefined; otherwise a TypeError
is thrown.
Handler functions run in the same thread in which the event occurred. They run in the compartment to which they belong, not in a debuggee compartment.
- onNewScript(script, global)
- New code, represented by the
Debugger.Script
instance script, has been loaded in the scope of the debuggee global object global. global is aDebugger.Object
instance whose referent is the global object.This method's return value is ignored.
- onDebuggerStatement(frame)
- Debuggee code has executed a debugger statement in frame. This method should return a [[#Resumption_Values|resumption value]] specifying how the debuggee's execution should proceed.
- onEnterFrame(frame)
- The stack frame frame is about to begin executing code. (Naturally, frame is currently the youngest [[#Visible_Frames|visible frame]].) This method should return a [[#Resumption_Values|resumption value]] specifying how the debuggee's execution should proceed.
SpiderMonkey only calls
onEnterFrame
to report [[#Visible_Frames|visible]], non-"debugger"
frames. - onThrow(frame, value) (future plan)
- The exception value is being thrown by frame, which is running debuggee code. This method should return a [[#Resumption_Values|resumption value]] specifying how the debuggee's execution should proceed. If it returns
undefined
, the exception is thrown as normal.A call to the
onThrow
handler is typically followed by one or more calls to theonExceptionUnwind
handler.''(pending discussion)'' If the debuggee executes
try { throw 0; } finally { f(); }
andf()
executes without error, theonThrow
handler is called only once. The debugger is not notified when the exception is set aside in order to execute thefinally
block, nor when it is restored after thefinally
block completes normally.''(An alternative design here would be: onException(status, frame, value) where status is one of the strings "throw", "unwind", "catch", "finally", "rethrow". JS_SaveExceptionState would trigger a "finally" event, JS_RestoreExceptionState would trigger a "rethrow", JS_ClearPendingException would trigger a "catch"; not sure what JS_DropExceptionState or a return/throw from a finally block should do.)''
- onExceptionUnwind(frame, value)
- The exception value has been thrown, and has propagated to frame; frame is the youngest remaining stack frame, and is a debuggee frame. This method should return a [[#Resumption_Values|resumption value]] specifying how the debuggee's execution should proceed. If it returns
undefined
, the exception continues to propagate as normal: if control inframe
is in atry
block, control jumps to the correspondingcatch
orfinally
block; otherwise, frame is popped, and the exception propagates to frame's caller.When an exception's propagation causes control to enter a
finally
block, the exception is temporarily set aside. If thefinally
block finishes normally, the exception resumes propagation, and the debugger'sonExceptionUnwind
handler is called again, in the same frame. (The other possibility is for thefinally
block to exit due to areturn
,continue
, orbreak
statement, or a new exception. In those cases the old exception does not continue to propagate; it is discarded.) - sourceHandler(ASuffusionOfYellow)
- This method is never called. If it is ever called, a contradiction has been proven, and the debugger is free to assume that everything is true.
- onError(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.
This method's return value is ignored.
- onNewGlobalObject(global)
- A new global object, global, has been created. The application embedding the JavaScript implementation may provide details about what kind of global it is via
global.hostAnnotations
.This handler method should return a [[#Resumption_Values|resumption value]] specifying how the debuggee's execution should proceed. However, note that a
{ return: value }
resumption value is treated likeundefined
("continue normally"); value is ignored. (Allowing the handler to substitute its own value for the new global object doesn't seem useful.)This handler method is only available to debuggers running in privileged code ("chrome", in Firefox). Most functions provided by this
Debugger
API observe activity in only those globals that are reachable by the API's user, thus imposing capability-based restrictions on aDebugger
's reach. However, theonNewGlobalObject
method allows the API user to monitor all global object creation that occurs anywhere within the JavaScript system (the "JSRuntime", in SpiderMonkey terms), thereby escaping the capability-based limits. For this reason,onNewGlobalObject
is only available to privileged code.
=== Function Properties of the Debugger Prototype Object ===
The functions described below may only be called with a this
value referring to a Debugger
instance; they may not be used as methods of other kinds of objects.
- addDebuggee(global)
- Add the global object designated by global to the set of global objects this
Debugger
instance is debugging. Return thisDebugger
'sDebugger.Object
instance referring to global's referent.The value global may be any of the following:
- A global object.
- An HTML5
WindowProxy
object (an "outer window", in Firefox terminology), which is treated as if theWindow
object of the browsing context's active document (the "inner window") were passed. - A cross-compartment wrapper of an object; we apply the prior rules to the wrapped object.
- A
Debugger.Object
instance belonging to thisDebugger
instance; we apply the prior rules to the referent.
TypeError
. (Note that each rule is only applied once in the process of resolving a given global argument. Thus, for example, aDebugger.Object
referring to a secondDebugger.Object
which refers to a global does not designate that global for the purposes of this function.)The global designated by global must be in a different compartment than this
Debugger
instance itself. If adding the designated global's compartment would create a cycle of debugger and debuggee compartments, this method throws an error.This method returns the
Debugger.Object
instance whose referent is the designated global object.The
Debugger
instance does not hold a strong reference to its debuggee globals: if a debuggee global is not otherwise reachable, then it is dropped from theDebugger
's set of debuggees. (Naturally, theDebugger.Object
instance this method returns does hold a strong reference to the added global.) - removeDebuggee(global)
- Remove the global object designated by global from this
Debugger
instance's set of debuggees. Returnundefined
.This method interprets global using the same rules that
addDebuggee
does. - hasDebuggee(global)
- Return
true
if the global object designated by global is a debuggee of thisDebugger
instance.This method interprets global using the same rules that
addDebuggee
does. - getDebuggees()
- Return an array of distinct
Debugger.Object
instances whose referents are all the global objects thisDebugger
instance is debugging.Since
Debugger
instances don't hold strong references to their debuggee globals, if a debuggee global is otherwise unreachable, it may be dropped at any moment from the array this method returns. - getNewestFrame()
- Return a
Debugger.Frame
instance referring to the youngest [[#Visible_Frames|visible frame]] currently on the calling thread's stack, ornull
if there are no visible frames on the stack. - findScriptURLs([query])
- Return an array containing the values of the
url
properties of all debuggee scripts matching query. Each distinct value appears only once in the array. Query is an object whose properties restrict which scripts are returned; a script must meet all the criteria given by query to be returned. If query is omitted, we return theurl
values of all debuggee scripts.Query may have the following property:
- global
- The script must be in the scope of the given global object. If this property's value is a
Debugger.Object
instance belonging to thisDebugger
instance, then its referent is used. If the object is not a global object, then the global in whose scope it was allocated is used.
Note that the result may include values for scripts that can no longer ever be used by the debuggee, say, those for eval code that has finished running, or unreachable functions. Whether such script's
url
values appear can be affected by the garbage collector's behavior, so this function's behavior is not entirely deterministic. - findScripts([query])
- Return an array of
Debugger.Script
instances for all debuggee scripts matching query. Each instance appears only once in the array. Query is an object whose properties restrict which scripts are returned; a script must meet all the criteria given by query to be returned. If query is omitted, we return theDebugger.Script
instances for all debuggee scripts.Query may have the following properties:
- url
- The script's
url
property must be equal to this value. - line
- The script must at least partially cover the given source line. If this property is present, the
url
property must be present as well. - column
- The script must include given column on the line given by the
line
property. If this property is present, theurl
andline
properties must both be present as well. - innermost
- If this property is present and true, the script must be the innermost script covering the given source location; scripts of enclosing code are omitted.
- global
- The script must be in the scope of the given global object. If this property's value is a
Debugger.Object
instance belonging to thisDebugger
instance, then its referent is used. If the object is not a global object, then the global in whose scope it was allocated is used.
Note that the result may include
Debugger.Script
instances for scripts that can no longer ever be used by the debuggee, say, those for eval code that has finished running, or unreachable functions. Whether such scripts appear can be affected by the garbage collector's behavior, so this function's behavior is not entirely deterministic. - clearBreakpoint(handler)
- Remove all breakpoints set in this
Debugger
instance that use handler as their handler. Note that, if breakpoints using other handler objects are set at the same location(s) as handler, they remain in place. - clearAllBreakpoints()
- Remove all breakpoints set using this
Debugger
instance. - clearAllWatchpoints() (future plan)
- Clear all watchpoints owned by this
Debugger
instance. - findAllGlobals()
- Return an array of
Debugger.Object
instances referring to all the global objects present in this JavaScript instance. The application may provide details about what kind of globals they are via theDebugger.Object
instances'hostAnnotations
accessors.The results of this call can be affected in non-deterministic ways by the details of the JavaScript implementation. The array may include
Debugger.Object
instances referring to global objects that are not actually reachable by the debuggee or any other code in the system. (Naturally, once the function has returned, the array'sDebugger.Object
instances strongly reference the globals they refer to.)This handler method is only available to debuggers running in privileged code ("chrome", in Firefox). Most functions provided by this
Debugger
API observe activity in only those globals that are reachable by the API's user, thus imposing capability-based restrictions on aDebugger
's reach. However,findAllGlobals
allows the API user to find all global objects anywhere within the JavaScript system (the "JSRuntime", in SpiderMonkey terms), thereby escaping the capability-based limits. For this reason,findAllGlobals
is only available to privileged code.
== Debugger.Frame ==
A Debugger.Frame
instance represents a [[#Visible_Frames|visible stack frame]]. Given a Debugger.Frame
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.
For a given Debugger
instance, SpiderMonkey creates only one Debugger.Frame
instance for a given visible frame. Every handler method called while the debuggee is running in a given frame is given the same frame object. Similarly, 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 ==
to decide whether two expressions refer to the same frame, and so on.
(If more than one Debugger
instance is debugging the same code, each Debugger
gets a separate Debugger.Frame
instance for a given frame. This allows the code using each Debugger
instance to place whatever properties it likes on its Debugger.Frame
instances, without worrying about interfering with other debuggers.)
When the debuggee pops a stack frame (say, because a function call has returned or an exception has been thrown from it), the Debugger.Frame
instance referring to that frame becomes inactive: its live
property becomes false
, and accessing its other properties or calling its methods throws an exception. Note that frames only become inactive at times that are predictable for the debugger: when the debuggee runs, or when the debugger removes frames from the stack itself.
Stack frames that represent the control state of generator-iterator objects behave in a special way, described in [[#Generator_Frames|Generator Frames]] below.
=== Visible Frames ===
When inspecting the call stack, Debugger
does not reveal all the frames that are actually present on the stack: while it does reveal all frames running debuggee code, it omits frames running the debugger's own code, and omits most frames running non-debuggee code. We call those stack frames a Debugger
does reveal visible frames.
A frame is a visible frame if any of the following are true:
- it is running [[#debuggee_code|debuggee code]];
- its immediate caller is a frame running debuggee code; or
- it is a [[#Invocation_Functions_and_.22debugger.22_Frames|
"debugger"
frame]], representing the continuation of debuggee code invoked by the debugger.
The "immediate caller" rule means that, when debuggee code calls a non-debuggee function, it looks like a call to a primitive: you see a frame for the non-debuggee function that was accessible to the debuggee, but any further calls that function makes are treated as internal details, and omitted from the stack trace. If the non-debuggee function eventually calls back into debuggee code, then those frames are visible.
(Note that the debuggee is not considered an "immediate caller" of handler methods it triggers. Even though the debuggee and debugger share the same JavaScript stack, frames pushed for SpiderMonkey's calls to handler methods to report events in the debuggee are never considered visible frames.)
=== Invocation Functions and "debugger" Frames ===
An invocation function is any function in this interface that allows the debugger to invoke code in the debuggee: Debugger.Object.prototype.call
, Debugger.Frame.prototype.eval
, and so on.
While invocation functions differ in the code to be run and how to pass values to it, they all follow this general procedure:
- Let older be the youngest visible frame on the stack, or
null
if there is no such frame. (This is never one of the the debugger's own frames; those never appear asDebugger.Frame
instances.) - Push a
"debugger"
frame on the stack, with older as itsolder
property. - Invoke the debuggee code as appropriate for the given invocation function, with the
"debugger"
frame as its continuation. For example,Debugger.Frame.prototype.eval
pushes an"eval"
frame for code it runs, whereasDebugger.Object.prototype.call
pushes a"call"
frame. - When the debuggee code completes, whether by returning, throwing an exception or being terminated, pop the
"debugger"
frame, and return an appropriate [[#Completion_Values|completion value]] from the invocation function to the debugger.
When a debugger calls an invocation function to run debuggee code, that code's continuation is the debugger, not the next debuggee code frame. Pushing a "debugger"
frame makes this continuation explicit, and makes it easier to find the extent of the stack created for the invocation.
=== Accessor Properties of the Debugger.Frame Prototype Object ===
A Debugger.Frame
instance inherits the following accessor properties from its prototype:
- type
- A string describing what sort of frame this is:
"call"
: a frame running a function call. (We may not be able to obtain frames for calls to host functions.)"eval"
: a frame running code passed toeval
."global"
: a frame running global code (JavaScript that is neither of the above)."debugger"
: a frame for a call to user code invoked by the debugger (see theeval
method below).
- this
- The value of
this
for this frame (a debuggee value). - older
- The next-older visible frame, in which control will resume when this frame completes. If there is no older frame, this is
null
. (On a suspended generator frame, the value of this property isnull
; see [[#Generator_Frames|Generator Frames]].) - depth
- The depth of this frame, counting from oldest to youngest; the oldest frame has a depth of zero.
- live
- True if the frame this
Debugger.Frame
instance refers to is still on the stack (or, in the case of generator-iterator objects, has not yet finished its iteration); false if it has completed execution or been popped in some other way. - script
- The script being executed in this frame (a
Debugger.Script
instance), ornull
on frames that do not represent calls to debuggee code. On frames whosecallee
property is not null, this is equal tocallee.script
. - offset
- The offset of the bytecode instruction currently being executed in
script
, orundefined
if the frame'sscript
property isnull
. - environment
- The lexical environment within which evaluation is taking place (a
Debugger.Environment
instance), ornull
on frames that do not represent the evaluation of debuggee code, like calls non-debuggee functions, host functions or"debugger"
frames. - callee
- The function whose application created this frame, as a debuggee value, or
null
if this is not a"call"
frame. - generator
- True if this frame is a generator frame, false otherwise.
- constructing
- True if this frame is for a function called as a constructor, false otherwise.
- arguments
- The arguments passed to the current frame, or
null
if this is not a"call"
frame. When non-null
, this is an object, allocated in the same global as the debugger, withArray.prototype
on its prototype chain, a non-writablelength
property, and properties whose names are array indices. Each property is a read-only accessor property whose getter returns the current value of the corresponding parameter. When the referent frame is popped, the argument value's properties' getters throw an error.
=== Handler Methods of Debugger.Frame Instances ===
Each Debugger.Frame
instance inherits accessor properties holding handler functions for SpiderMonkey to call when given events occur in the frame.
Calls to frames' handler methods are cross-compartment, intra-thread calls: the call takes place in the thread to which the frame belongs, and runs in the compartment to which the handler method belongs.
Debugger.Frame
instances inherit the following handler method properties:
- onStep
- This property must be either
undefined
or a function. If it is a function, SpiderMonkey calls it when execution in this frame makes a small amount of progress, passing no arguments and providing thisDebugger.Frame
instance as thethis
value. The function should return a [[#Resumption_Values|resumption value]] specifying how the debuggee's execution should proceed.What constitutes "a small amount of progress" varies depending on the implementation, but it is fine-grained enough to implement useful "step" and "next" behavior.
If multiple
Debugger
instances each haveDebugger.Frame
instances for a given stack frame withonStep
handlers set, their handlers are run in an unspecified order. If anyonStep
handler forces the frame to return early (by returning a resumption value other thanundefined
), any remaining debuggers'onStep
handlers do not run.This property is ignored on frames that are not executing debuggee code, like
"call"
frames for calls to host functions and"debugger"
frames. - onPop
- This property must be either
undefined
or a function. If it is a function, SpiderMonkey calls it just before this frame is popped, passing a [[#Completion_Values|completion value]] indicating how this frame's execution completed, and providing thisDebugger.Frame
instance as thethis
value. The function should return a [[#Resumption_Values|resumption value]] indicating how execution should proceed. On newly created frames, this property's value isundefined
.When an
onPop
call reports the completion of a construction call (that is, a function called via thenew
operator), the completion value passed to the handler describes the value returned by the function body. If this value is not an object, it may be different from the value produced by thenew
expression, which will be the value of the frame'sthis
property. (In ECMAScript terms, theonPop
handler receives the value returned by the[[Call]]
method, not the value returned by the[[Construct]]
method.)When a debugger handler function forces a frame to complete early, by returning a
{ return:... }
,{ throw:... }
, ornull
resumption value, SpiderMonkey calls the frame'sonPop
handler, if any. The completion value passed in this case reflects the resumption value that caused the frame to complete.When SpiderMonkey calls an
onPop
handler for a frame that is throwing an exception or being terminated, and the handler returnsundefined
, then SpiderMonkey proceeds with the exception or termination. That is, anundefined
resumption value leaves the frame's throwing and termination process undisturbed.When a generator frame yields a value, SpiderMonkey calls its
Debugger.Frame
instance'sonPop
handler method, if present, passing ayield
resumption value; however, theDebugger.Frame
instance remains live.If multiple
Debugger
instances each haveDebugger.Frame
instances for a given stack frame withonPop
handlers set, their handlers are run in an unspecified order. The resumption value each handler returns establishes the completion value reported to the next handler.This property is ignored on
"debugger"
frames. - onResume(value)
- This property must be either
undefined
or a function. If it is a function, SpiderMonkey calls it if the current frame is a generator frame whose execution has just been resumed. The function should return a [[#Resumption_Values|resumption value]] indicating how execution should proceed. On newly created frames, this property's value isundefined
.If the program resumed the generator by calling its
send
method and passing a value, then value is that value. Otherwise, value isundefined
.
=== Function Properties of the Debugger.Frame Prototype Object ===
The functions described below may only be called with a this
value referring to a Debugger.Frame
instance; they may not be used as methods of other kinds of objects.
- eval(code)
- Evaluate code in the execution context of this frame, and return a [[#Completion_Values|completion value]] describing how it completed. Code is a string. If this frame's
environment
property isnull
, throw aTypeError
. All extant handler methods, breakpoints, watchpoints, and so on remain active during the call. This function follows the [[#Invocation_Functions_and_.22debugger.22_Frames|invocation function conventions]].Code is interpreted as strict mode code when it contains a Use Strict Directive, or the code executing in this frame is strict mode code.
If code is not strict mode code, then variable declarations in code affect the environment of this frame. (In the terms used by the ECMAScript specification, the
VariableEnvironment
of the execution context for the eval code is theVariableEnvironment
of the execution context that this frame represents.) If implementation restrictions prevent SpiderMonkey from extending this frame's environment as requested, this call throws an Error exception. - evalWithBindings(code, bindings)
- Like
eval
, but evaluate code in the environment of this frame, extended with bindings from the object bindings. For each own enumerable property of bindings named name whose value is value, include a variable in the environment in which code is evaluated named name, whose value is value. Each value must be a debuggee value. (This is not like awith
statement: code may access, assign to, and delete the introduced bindings without having any effect on the bindings object.)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, and do so without mutating any existing debuggee environment.
Note that, like
eval
, declarations in the code passed toevalWithBindings
affect the environment of this frame, even as that environment is extended by bindings visible within code. (In the terms used by the ECMAScript specification, theVariableEnvironment
of the execution context for the eval code is theVariableEnvironment
of the execution context that this frame represents, and the bindings appear in a new declarative environment, which is the eval code'sLexicalEnvironment
.) If implementation restrictions prevent SpiderMonkey from extending this frame's environment as requested, this call throws anError
exception. - pop(completion) (future plan)
- Pop this frame (and any younger frames) from the stack as if this frame had completed as specified by the completion value completion.
Note that this does not 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 debuggee will only resume execution when you return from the handler method that brought control to the debugger originally.
This cannot remove any
"call"
frames for calls to host functions from the stack. (We might be able to make this work eventually, but it will take some cleverness.) - replaceCall(function, this, arguments) (future plan)
- Pop any younger frames from the stack, and then change this frame into a frame for a call to function, with the given this value and arguments. This should be a debuggee value, or
{ asConstructor: true }
to invoke function as a constructor, in which case SpiderMonkey provides an appropriatethis
value itself. Arguments should be an array of debuggee values. This frame must be a"call"
frame.This can be used as a primitive in implementing some forms of a "patch and continue" debugger feature.
Note that this does not 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 debuggee will only resume execution when you return from the handler method that brought control to the debugger originally.
Like
pop
, this cannot remove"call"
frames for calls to host functions 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 like (i*i for each (i in [1,2,3]))
.
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.
A Debugger.Frame
instance representing a generator frame differs from an ordinary stack frame as follows:
- A generator frame's
generator
property is true. - A generator frame disappears from the stack each time the generator yields a value and is suspended, and reappears atop the stack when it is resumed to produce the generator's next value. The same
Debugger.Frame
instance refers to the generator frame until it returns, throws an exception, or is terminated. - A generator frame's
older
property refers to the frame's continuation frame while the generator is running, and isnull
while the generator is suspended. - A generator frame's
depth
property reflects the frame's position on the stack when the generator is resumed, and isnull
while the generator is suspended. - A generator frame's
live
property remains true until the frame returns, throws an exception, or is terminated. Thus, generator frames can be live while not present on the stack.
The other Debugger.Frame
methods and accessor properties work as described on generator frames, even when the generator frame is suspended. You may examine a suspended generator frame's variables, and use its script
and offset
members to see which yield
it is suspended at.
A Debugger.Frame
instance referring to a generator-iterator frame has a strong reference to the generator-iterator object; the frame (and its object) will live as long as the Debugger.Frame
instance does. However, when the generator function returns, throws an exception, or is terminated, thus ending the iteration, the Debugger.Frame
instance becomes inactive and its live
property becomes false
, just as would occur for any other sort of frame that is popped. A non-live Debugger.Frame
instance no longer holds a strong reference to the generator-iterator object.
== Debugger.Script ==
A Debugger.Script
instance refers to a sequence of bytecode in the debuggee; it is the JavaScript-level presentation of a JSAPI JSScript
object. Each of the following is represented by single JSScript object:
- The body of a function—that is, all the code in the function that is not contained within some nested function.
- The code passed to a single call to
eval
, excluding the bodies of any functions that code defines. - The contents of a
<script>
element. - A DOM event handler, whether embedded in HTML or attached to the element by other JavaScript code.
- Code appearing in a
javascript:
URL.
The Debugger
interface constructs Debugger.Script
objects as scripts of debuggee code are uncovered by the debugger: via the onNewScript
handler method; via Debugger.Frame
's script
properties; via the functionScript
method of Debugger.Object
instances; and so on. For a given Debugger
instance, SpiderMonkey constructs exactly one Debugger.Script
instance for each underlying script object; debugger code can add its own properties to a script object and expect to find them later, use ==
to decide whether two expressions refer to the same script, and so on.
(If more than one Debugger
instance is debugging the same code, each Debugger
gets a separate Debugger.Script
instance for a given script. This allows the code using each Debugger
instance to place whatever properties it likes on its Debugger.Script
instances, without worrying about interfering with other debuggers.)
A Debugger.Script
instance is a strong reference to a JSScript object; it protects the script it refers to from being garbage collected.
Note that SpiderMonkey may use the same Debugger.Script
instances for equivalent functions or evaluated code—that is, scripts representing the same source code, at the same position in the same source file, evaluated in the same lexical environment.
=== Accessor Properties of the Debugger.Script Prototype Object ===
A Debugger.Script
instance inherits the following accessor properties from its prototype:
- url
- The filename or URL from which this script's code was loaded.
- startLine
- The number of the line at which this script's code starts, within the file or document named by
url
. - lineCount
- The number of lines this script's code occupies, within the file or document named by
url
. - staticLevel
- The number of function bodies enclosing this script's code.
Global code is at level zero; bodies of functions defined at the top level in global code are at level one; bodies of functions nested within those are at level two; and so on.
A script for code passed to direct
eval
is at a static level one greater than that of the script containing the call toeval
, because direct eval code runs within the caller's scope. However, a script for code passed to an indirecteval
call is at static level zero, since it is evaluated in the global scope.Note that a generator's expressions are considered to be part of the body of a synthetic function, produced by the compiler.
Scripts' static level be useful in deciding where to set breakpoints. For example, a breakpoint set on line 3 in this code:
function f() { x = function g() { // line 2 // line 3; no code here ...; } }
should be set in
g
's script, not inf
's, even though neither script contains code at that line. In such a case, the most deeply nested script—the one with the highest static level—should receive the breakpoint. - strictMode
- This is
true
if this script's code is ECMAScript strict mode code, andfalse
otherwise.
=== Function Properties of the Debugger.Script Prototype Object ===
The functions described below may only be called with a this
value referring to a Debugger.Script
instance; they may not be used as methods of other kinds of objects.
- decompile([pretty])
- Return a string containing JavaScript source code equivalent to this script in its effect and result. If pretty is present and true, produce indented code with line breaks.
(Note that
Debugger.Object
instances referring to functions also have adecompile
method, whose result includes the function header and parameter names, so it is probably better to writef.decompile()
than to writef.getFunctionScript().decompile()
.) - getAllOffsets()
- Return an array L describing the relationship between bytecode instruction offsets and source code positions in this script. L is sparse, and indexed by source line number. If a source line number line has no code, then L has no line property. If there is code for line, then
L[line]
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
getAllOffsets()
on that code might yield an array like this: [[0], [5, 20], , [10]]This array indicates that:
- the first line's code starts at offset 0 in the script;
- the
for
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); - the third line has no code;
- and the fourth line begins at offset 10.
- getLineOffsets(line)
- Return an array of bytecode instruction offsets representing the entry points to source line line. If the script contains no executable code at that line, the array returned is empty.
- getOffsetLine(offset)
- Return the source code line responsible for the bytecode at offset in this script.
- getChildScripts()
- Return a new array whose elements are Debugger.Script objects for each function and each generator expression in this script. Only direct children are included; nested children can be reached by walking the tree.
- setBreakpoint(offset, handler)
- Set a breakpoint at the bytecode instruction at offset in this script, reporting hits to the
hit
method of handler. If offset is not a valid offset in this script, throw an error.When execution reaches the given instruction, SpiderMonkey calls the
hit
method of handler, passing aDebugger.Frame
instance representing the currently executing stack frame. Thehit
method's return value should be a [[#Resumption_Values|resumption value]], determining how execution should continue.Any number of breakpoints may be set at a single location; when control reaches that point, SpiderMonkey calls their handlers in an unspecified order.
Any number of breakpoints may use the same handler object.
Breakpoint handler method calls are cross-compartment, intra-thread calls: the call takes place in the same thread that hit the breakpoint, and in the compartment containing the handler function (typically the debugger's compartment).
The new breakpoint belongs to the
Debugger
instance to which this script belongs; disabling theDebugger
instance disables this breakpoint. - getBreakpoints([offset])
- Return an array containing the handler objects for all the breakpoints set at offset in this script. If offset is omitted, return the handlers of all breakpoints set anywhere in this script. If offset is present, but not a valid offset in this script, throw an error.
- clearBreakpoints(handler, [offset])
- Remove all breakpoints set in this
Debugger
instance that use handler as their handler. If offset is given, remove only those breakpoints set at offset that use handler; if offset is not a valid offset in this script, throw an error.Note that, if breakpoints using other handler objects are set at the same location(s) as handler, they remain in place.
- clearAllBreakpoints([offset])
- Remove all breakpoints set in this script. If offset is present, remove all breakpoints set at that offset in this script; if offset is not a valid bytecode offset in this script, throw an error.
== Debugger.Object ==
A Debugger.Object
instance represents an object in the debuggee, providing reflection-oriented methods to inspect and modify its referent. The referent's properties do not appear directly as properties of the Debugger.Object
instance; the debugger can access them only through methods like Debugger.Object.prototype.getOwnPropertyDescriptor
and Debugger.Object.prototype.defineProperty
, ensuring that the debugger will not inadvertently invoke the referent's getters and setters.
SpiderMonkey creates exactly one Debugger.Object
instance for each debuggee object it presents to a given Debugger
instance: if the debugger encounters the same object through two different routes (perhaps two functions are called on the same object), SpiderMonkey presents the same Debugger.Object
instance to the debugger each time. This means that the debugger can use the ==
operator to recognize when two Debugger.Object
instances refer to the same debuggee object, and place its own properties on a Debugger.Object
instance to store metadata about particular debuggee objects.
JavaScript code in different compartments can have different views of the same object. For example, in Firefox, code in privileged compartments sees content DOM element objects without redefinitions or extensions made to that object's properties by content code. (In Firefox terminology, privileged code sees the element through an "xray wrapper".) To ensure that debugger code sees each object just as the debuggee would, each Debugger.Object
instance presents its referent as it would be seen from a particular compartment. This "viewing compartment" is chosen to match the way the debugger came across the referent. As a consequence, a single Debugger
instance may actually have several Debugger.Object
instances: one for each compartment from which the referent is viewed.
If more than one Debugger
instance is debugging the same code, each Debugger
gets a separate Debugger.Object
instance for a given object. This allows the code using each Debugger
instance to place whatever properties it likes on its own Debugger.Object
instances, without worrying about interfering with other debuggers.
While most Debugger.Object
instances are created by SpiderMonkey in the process of exposing debuggee's behavior and state to the debugger, the debugger can use Debugger.Object.prototype.makeDebuggeeValue
to create Debugger.Object
instances for given debuggee objects, or use Debugger.Object.prototype.copy
and Debugger.Object.prototype.create
to create new objects in debuggee compartments, allocated as if by particular debuggee globals.
Debugger.Object
instances protect their referents from the garbage collector; as long as the Debugger.Object
instance is live, the referent remains live. This means that garbage collection has no visible effect on Debugger.Object
instances.
=== Accessor Properties of the Debugger.Object prototype ===
A Debugger.Object
instance inherits the following accessor properties from its prototype:
- proto
- The referent's prototype (as a new
Debugger.Object
instance), ornull
if it has no prototype. - class
- A string naming the ECMAScript
[[Class]]
of the referent. - callable
true
if the referent is a callable object (such as a function or a function proxy); false otherwise.- name
- The name of the referent, if it is a named function. If the referent is an anonymous function, or not a function at all, this is
undefined
.This accessor returns whatever name appeared after the
function
keyword in the source code, regardless of whether the function is the result of instantiating a function declaration (which binds the function to its name in the enclosing scope) or evaluating a function expression (which binds the function to its name only within the function's body). - displayName
- The referent's display name, if the referent is a function with a display name. If the referent is not a function, or if it has no display name, this is
undefined
.If a function has a given name, its display name is the same as its given name. In this case, the
displayName
andname
properties are equal.If a function has no name, SpiderMonkey attempts to infer an appropriate name for it given its context. For example: function f() {} // display name: f (the given name) var g = function () {}; // display name: g o.p = function () {}; // display name: o.p var q = { r: function () {} // display name: q.r };
Note that the display name may not be a proper JavaScript identifier, or even a proper expression: we attempt to find helpful names even when the function is not immediately assigned as the value of some variable or property. Thus, we use
a/b
to refer to the b defined within a, anda<
to refer to a function that occurs somewhere within an expression that is assigned to a. For example: function h() { var i = function() {}; // display name: h/i f(function () {}); // display name: h/< } var s = f(function () {}); // display name: s< - parameterNames
- If the referent is a function, the names of the its parameters, as an array of strings. If the referent is not a function at all, this is
undefined
.If the referent is a host function for which parameter names are not available, return an array with one element per parameter, each of which is
undefined
.If the referent is a function proxy, return an empty array.
If the referent uses destructuring parameters, then the array's elements reflect the structure of the parameters. For example, if the referent is a function declared in this way:
function f(a, [b, c], {d, e:f}) { ... }
then this
Debugger.Object
instance'sparameterNames
property would have the value:["a", ["b", "c"], {d:"d", e:"f"}]
- script
- If the referent is a function that is debuggee code, this is that function's script, as a
Debugger.Script
instance. If the referent is a function proxy or not debuggee code, this isundefined
. - environment
- If the referent is a function that is debuggee code, a
Debugger.Environment
instance representing the lexical environment enclosing the function when it was created. If the referent is a function proxy or not debuggee code, this isundefined
. - proxyHandler
- If the referent is a proxy whose handler object was allocated by debuggee code, this is its handler object—the object whose methods are invoked to implement accesses of the proxy's properties. If the referent is not a proxy whose handler object was allocated by debuggee code, this is
null
. - proxyCallTrap
- If the referent is a function proxy whose handler object was allocated by debuggee code, this is its call trap function—the function called when the function proxy is called. If the referent is not a function proxy whose handler object was allocated by debuggee code, this is
null
. - proxyConstructTrap
- If the referent is a function proxy whose handler object was allocated by debuggee code, its construction trap function—the function called when the function proxy is called via a
new
expression. If the referent is not a function proxy whose handler object was allocated by debuggee code, this isnull
. - global
- A
Debugger.Object
instance referring to the global object in whose scope the referent was allocated. This does not unwrap cross-compartment wrappers: if the referent is a wrapper, the result refers to the wrapper's global, not the wrapped object's global. The result refers to the global directly, not via a wrapper. - hostAnnotations
- A JavaScript object providing further metadata about the referent, or
null
if none is available. The metadata object is in the same compartment as thisDebugger.Object
instance. The same metadata object is returned each time for a givenDebugger.Object
instance.A typical JavaScript embedding provides "host objects" to expose application-specific functionality to scripts. The
hostAnnotations
accessor consults the embedding for additional information about the referent that might be of interest to the debugger. The returned object's properties' meanings are up to the embedding. For example, a web browser might provide host annotations for global objects to distinguish top-level windows, iframes, and internal JavaScript scopes.By convention, host annotation objects have a string-valued
"type"
property that, taken together with the object's class, indicate what sort of thing the referent is. The host annotation object's other properties provide further details, as appropriate for the type. For example, in Firefox, a metadata object for a JavaScript Module's global object might look like this:{ "type":"jsm", "uri":"resource:://gre/modules/XPCOMUtils.jsm" }
Firefox provides [DebuggerHostAnnotationsForFirefox annotations] for its host objects.
=== Function Properties of the Debugger.Object prototype ===
The functions described below may only be called with a this
value referring to a Debugger.Object
instance; they may not be used as methods of other kinds of objects. The descriptions use "referent" to mean "the referent of this Debugger.Object
instance".
Unless otherwise specified, these methods are not [[#Invocation_Functions_and_.22debugger.22_Frames|invocation functions]]; if a call would cause debuggee code to run (say, because it gets or sets an accessor property whose handler is debuggee code, or because the referent is a proxy whose traps are debuggee code), the call throws a [[#The_Debugger.DebuggeeWouldRun_Exception|Debugger.DebuggeeWouldRun
]] exception.
- getProperty(name)
- Return the value of the referent's property named name, or
undefined
if it has no such property. Name must be a string. The result is a debuggee value. - setProperty(name, value)
- Store value as the value of the referent's property named name, creating the property if it does not exist. Name must be a string; value must be a debuggee value.
- getOwnPropertyDescriptor(name)
- Return a property descriptor for the property named name of the referent. If the referent has no such property, return
undefined
. (This function behaves like the standardObject.getOwnPropertyDescriptor
function, except that the object being inspected is implicit; the property descriptor returned is allocated as if by code scoped to the debugger's global object (and is thus in the debugger's compartment); and itsvalue
,get
, andset
properties, if present, are debuggee values.) - getOwnPropertyNames()
- Return an array of strings naming all the referent's own properties, as if
Object.getOwnPropertyNames(referent)
had been called in the debuggee, and the result copied in the scope of the debugger's global object. - defineProperty(name, attributes)
- Define a property on the referent named name, as described by the property descriptor descriptor. Any
value
,get
, andset
properties of attributes must be debuggee values. (This function behaves likeObject.defineProperty
, except that the target object is implicit, and in a different compartment from the function and descriptor.) - defineProperties(properties)
- Add the properties given by properties to the referent. (This function behaves like
Object.defineProperties
, except that the target object is implicit, and in a different compartment from the properties argument.) - deleteProperty(name)
- Remove the referent's property named name. Return true if the property was successfully removed, or if the referent has no such property. Return false if the property is non-configurable.
- seal()
- Prevent properties from being added to or deleted from the referent. Return this
Debugger.Object
instance. (This function behaves like the standardObject.seal
function, except that the object to be sealed is implicit and in a different compartment from the caller.) - freeze()
- Prevent properties from being added to or deleted from the referent, and mark each property as non-writable. Return this
Debugger.Object
instance. (This function behaves like the standardObject.freeze
function, except that the object to be sealed is implicit and in a different compartment from the caller.) - preventExtensions()
- Prevent properties from being added to the referent. (This function behaves like the standard
Object.preventExtensions
function, except that the object to operate on is implicit and in a different compartment from the caller.) - isSealed()
- Return true if the referent is sealed—that is, if it is not extensible, and all its properties have been marked as non-configurable. (This function behaves like the standard
Object.isSealed
function, except that the object inspected is implicit and in a different compartment from the caller.) - isFrozen()
- Return true if the referent is frozen—that is, if it is not extensible, and all its properties have been marked as non-configurable and read-only. (This function behaves like the standard
Object.isFrozen
function, except that the object inspected is implicit and in a different compartment from the caller.) - isExtensible()
- Return true if the referent is extensible—that is, if it can have new properties defined on it. (This function behaves like the standard
Object.isExtensible
function, except that the object inspected is implicit and in a different compartment from the caller.) - copy(value)
- Apply the HTML5 "structured cloning" algorithm to create a copy of value in the referent's global object (and thus in the referent's compartment), and return a
Debugger.Object
instance referring to the copy.Note that this returns primitive values unchanged. This means you can use
Debugger.Object.prototype.copy
as a generic "debugger value to debuggee value" conversion function—within the limitations of the "structured cloning" algorithm. - create(prototype, [properties])
- Create a new object in the referent's global (and thus in the referent's compartment), and return a
Debugger.Object
referring to it. The new object's prototype is prototype, which must be anDebugger.Object
instance. The new object's properties are as given by properties, as if properties were passed toDebugger.Object.prototype.defineProperties
, with the newDebugger.Object
instance as thethis
value. - makeDebuggeeValue(value)
- Return the debuggee value that represents value in the debuggee. If value is a primitive, we return it unchanged; if value is an object, we return the
Debugger.Object
instance representing that object, wrapped appropriately for use in thisDebugger.Object
's referent's compartment.Note that, if value is an object, it need not be one allocated in a debuggee global, nor even a debuggee compartment; it can be any object the debugger wishes to use as a debuggee value.
As [[#Debugger_Object|described above]], each
Debugger.Object
instance presents its referent as viewed from a particular compartment. Given aDebugger.Object
instance d and an object o, the calld.makeDebuggeeValue(o)
returns aDebugger.Object
instance that presents o as it would be seen by code in d's compartment. - decompile([pretty])
- If the referent is a function that is debuggee code, return the JavaScript source code for a function definition equivalent to the referent function in its effect and result, as a string. If pretty is present and true, produce indented code with line breaks. If the referent is not a function that is debuggee code, return
undefined
. - call(this, argument, ...)
- If the referent is callable, call it with the given this value and argument values, and return a [[#Completion_Values|completion value]] describing how the call completed. This should be a debuggee value, or
{ asConstructor: true }
to invoke the referent as a constructor, in which case SpiderMonkey provides an appropriatethis
value itself. Each argument must be a debuggee value. All extant handler methods, breakpoints, watchpoints, and so on remain active during the call. Details of how the call is carried out are given in the description of [[#Debugger.Frame.Debugger|Debugger.Frame.Debugger frames]]. If the referent is not callable, throw aTypeError
. This function follows the [[#Invocation_Functions_and_.22debugger.22_Frames|invocation function conventions]]. - apply(this, arguments)
- If the referent is callable, call it with the given this value and the argument values in arguments, and return a [[#Completion_Values|completion value]] describing how the call completed. This should be a debuggee value, or
{ asConstructor: true }
to invoke function as a constructor, in which case SpiderMonkey provides an appropriatethis
value itself. Arguments must either be an array (in the debugger) of debuggee values, ornull
orundefined
, which are treated as an empty array. All extant handler methods, breakpoints, watchpoints, and so on remain active during the call. Details of how the call is carried out are given in the description of [[#Debugger.Frame.Debugger|Debugger.Frame.Debugger frames]]. If the referent is not callable, throw aTypeError
. This function follows the [[#Invocation_Functions_and_.22debugger.22_Frames|invocation function conventions]]. - evalInGlobal(code)
- If the referent is a global object, evaluate code in that global environment, and return a [[#Completion_Values|completion value]] describing how it completed. Code is a string. All extant handler methods, breakpoints, watchpoints, and so on remain active during the call. This function follows the [[#Invocation_Functions_and_.22debugger.22_Frames|invocation function conventions]]. If the referent is not a global object, throw a
TypeError
exception.Code is interpreted as strict mode code when it contains a Use Strict Directive.
If code is not strict mode code, then variable declarations in code affect the referent global object. (In the terms used by the ECMAScript specification, the
VariableEnvironment
of the execution context for the eval code is the referent.) - evalInGlobalWithBindings(code, bindings)
- Like
evalInGlobal
, but evaluate code using the referent as the variable object, but with a lexical environment extended with bindings from the object bindings. For each own enumerable property of bindings named name whose value is value, include a variable in the lexical environment in which code is evaluated named name, whose value is value. Each value must be a debuggee value. (This is not like awith
statement: code may access, assign to, and delete the introduced bindings without having any effect on the bindings object.)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, and do so without mutating any existing debuggee environment.
Note that, like
evalInGlobal
, if the code passed toevalInGlobalWithBindings
is not strict mode code, then any declarations it contains affect the referent global object, even as code is evaluated in an environment extended according to bindings. (In the terms used by the ECMAScript specification, theVariableEnvironment
of the execution context for non-strict eval code is the referent, and the bindings appear in a new declarative environment, which is the eval code'sLexicalEnvironment
.) - asEnvironment()
- If the referent is a global object, return the
Debugger.Environment
instance representing the referent as a variable environment for evaluating code. If the referent is not a global object, throw aTypeError
. - setObjectWatchpoint(handler) (future plan)
- Set a watchpoint on all the referent's own properties, reporting events by calling handler's methods. Any previous watchpoint handler on this
Debugger.Object
instance is replaced. If handler is null, the referent is no longer watched. Handler may have the following methods, called under the given circumstances:- add(frame, name, descriptor)
- A property named name has been added to the referent. Descriptor is a property descriptor of the sort accepted by
Debugger.Object.prototype.defineProperty
, giving the newly added property's attributes. - delete(frame, name)
- The property named name is about to be deleted from the referent.
- change(frame, name, oldDescriptor, newDescriptor)
- The existing property named name on the referent is being changed from those given by oldDescriptor to those given by newDescriptor. This handler method is only called when attributes of the property other than its value are being changed; if only the value is changing, SpiderMonkey calls the handler's
set
method. - set(frame, oldValue, newValue)
- The data property named name of the referent is about to have its value changed from oldValue to newValue.
SpiderMonkey only calls this method on assignments to data properties that will succeed; assignments to un-writable data properties fail without notifying the debugger.
- extensionsPrevented(frame)
- The referent has been made non-extensible, as if by a call to
Object.preventExtensions
.
For all watchpoint handler methods:
- Handler calls receive the handler object itself as the
this
value. - The frame argument is the current stack frame, whose code is about to perform the operation on the object being reported.
- If the method returns
undefined
, then SpiderMonkey makes the announced change to the object, and continues execution normally. If the method returns an object:- If the object has a
superseded
property whose value is a true value, then SpiderMonkey does not make the announced change. - If the object has a
resume
property, its value is taken as a [[#Resumption_Values|resumption value]], indicating how execution should proceed. (However,return
resumption values are not supported.)
- If the object has a
- If a given method is absent from handler, then events of that sort are ignored. The watchpoint consults handler's properties each time an event occurs, so adding methods to or removing methods from handler after setting the watchpoint enables or disables reporting of the corresponding events.
- Values passed to handler's methods are debuggee values. Descriptors passed to handler's methods are ordinary objects in the debugger's compartment, except for
value
,get
, andset
properties in descriptors, which are debuggee values; they are the sort of value expected byDebugger.Object.prototype.defineProperty
. - Watchpoint handler calls are cross-compartment, intra-thread calls: the call takes place in the same thread that changed the property, and in handler's method's compartment (typically the same as the debugger's compartment).
The new watchpoint belongs to the
Debugger
instance to which thisDebugger.Object
instance belongs; disabling theDebugger
instance disables this watchpoint. - clearObjectWatchpoint() (future plan)
- Remove any object watchpoint set on the referent.
- setPropertyWatchpoint(name, handler) (future plan)
- Set a watchpoint on the referent's property named name, reporting events by calling handler's methods. Any previous watchpoint handler on this property for this
Debugger.Object
instance is replaced. If handler is null, the property is no longer watched. Handler is as described forDebugger.Object.prototype.setObjectWatchpoint
, except that it does not receiveextensionsPrevented
events. - clearPropertyWatchpoint(name) (future plan)
- Remove any watchpoint set on the referent's property named name.
- unwrap()
- If the referent is a wrapper that this
Debugger.Object
's compartment is permitted to unwrap, return aDebugger.Object
instance referring to the wrapped object. If we are not permitted to unwrap the referent, returnnull
. If the referent is not a wrapper, return thisDebugger.Object
instance unchanged. - unsafeDereference()
- Return the referent of this
Debugger.Object
instance.This method pierces the membrane of
Debugger.Object
instances meant to protect debugger code from debuggee code, and allows debugger code to access debuggee objects through the standard cross-compartment wrappers, rather than viaDebugger.Object
's reflection-oriented interfaces. This method makes it easier to gradually adapt large code bases to this Debugger API: adapted portions of the code can useDebugger.Object
instances, but use this method to pass direct object references to code that has not yet been updated.
== Debugger.Environment ==
A Debugger.Environment
instance represents a lexical environment, associating names with variables. Each Debugger.Frame
instance representing a debuggee frame has an associated environment object describing the variables in scope in that frame; and each Debugger.Object
instance representing a debuggee function has an environment object representing the environment the function has closed over.
ECMAScript environments form a tree, in which each local environment is parented by its enclosing environment (in ECMAScript terms, its 'outer' environment). We say an environment binds an identifier if that environment itself associates the identifier with a variable, independently of its outer environments. We say an identifier is in scope in an environment if the identifier is bound in that environment or any enclosing environment.
SpiderMonkey creates Debugger.Environment
instances as needed as the debugger inspects stack frames and function objects; calling Debugger.Environment
as a function or constructor raises a TypeError
exception.
SpiderMonkey creates exactly one Debugger.Environment
instance for each environment it presents via a given Debugger
instance: if the debugger encounters the same environment through two different routes (perhaps two functions have closed over the same environment), SpiderMonkey presents the same Debugger.Environment
instance to the debugger each time. This means that the debugger can use the ==
operator to recognize when two Debugger.Environment
instances refer to the same environment in the debuggee, and place its own properties on a Debugger.Environment
instance to store metadata about particular environments.
(If more than one Debugger
instance is debugging the same code, each Debugger
gets a separate Debugger.Environment
instance for a given environment. This allows the code using each Debugger
instance to place whatever properties it likes on its own Debugger.Object
instances, without worrying about interfering with other debuggers.)
Debugger.Environment
instances protect their referents from the garbage collector; as long as the Debugger.Environment
instance is live, the referent remains live. Garbage collection has no visible effect on Debugger.Environment
instances.
=== Accessor Properties of the Debugger.Environment Prototype Object ===
A Debugger.Environment
instance inherits the following accessor properties from its prototype:
- type
- The type of this environment object, one of the following values:
- "declarative", indicating that the environment is a declarative environment record. Function calls, calls to
eval
,let
blocks,catch
blocks, and the like create declarative environment records. - "object", indicating that the environment's bindings are the properties of an object. The global object and DOM elements appear in the chain of environments via object environments. (Note that
with
statements have their own environment type.) - "with", indicating that the environment was introduced by a
with
statement.
- "declarative", indicating that the environment is a declarative environment record. Function calls, calls to
- parent
- The environment that encloses this one (the "outer" environment, in ECMAScript terminology), or
null
if this is the outermost environment. - object
- A
Debugger.Object
instance referring to the object whose properties this environment reflects. If this is a declarative environment record, this accessor throws aTypeError
(since declarative environment records have no such object). Both"object"
and"with"
environments haveobject
properties that provide the object whose properties they reflect as variable bindings. - callee
- If this environment represents the variable environment (the top-level environment within the function, which receives
var
definitions) for a call to a function f, then this property's value is aDebugger.Object
instance referring to f. Otherwise, this property's value isnull
.
=== Function Properties of the Debugger.Environment Prototype Object ===
The methods described below may only be called with a this
value referring to a Debugger.Environment
instance; they may not be used as methods of other kinds of objects.
- names()
- Return an array of strings giving the names of the identifiers bound by this environment. The result does not include the names of identifiers bound by enclosing environments.
- getVariable(name)
- Return the value of the variable bound to name in this environment, or
undefined
if this environment does not bind name. Name must be a string that is a valid ECMAScript identifier name. The result is a debuggee value.This is not an [[#Invocation_Functions_and_.22debugger.22_Frames|invocation function]]; if this call would cause debuggee code to run (say, because the environment is a
"with"
environment, and name refers to an accessor property of thewith
statement's operand), this call throws a [[#The_Debugger.DebuggeeWouldRun_Exception|Debugger.DebuggeeWouldRun
]] exception. - setVariable(name, value)
- Store value as the value of the variable bound to name in this environment. Name must be a string that is a valid ECMAScript identifier name; value must be a debuggee value.
If this environment binds no variable named name, throw a
ReferenceError
.This is not an [[#Invocation_Functions_and_.22debugger.22_Frames|invocation function]]; if this call would cause debuggee code to run, this call throws a [[#The_Debugger.DebuggeeWouldRun_Exception|
Debugger.DebuggeeWouldRun
]] exception. - getVariableDescriptor(name)
- Return an property descriptor describing the variable bound to name in this environment, of the sort returned by
Debugger.Object.prototype.getOwnPropertyDescriptor
. Name must be a string whose value is a valid ECMAScript identifier name.If this is an
"object"
or"with"
environment record, this simply returns the descriptor for the given property of the environment's object. If this is a declarative environment record, this returns a descriptor reflecting the binding's mutability as the descriptor'swritable
property, and its deletability as the descriptor'sconfigurable
property. All declarative environment record bindings are marked asenumerable
. (This isn't great; the semantics of variables in declarative enviroments don't really match those of properties, so writing code that operates properly on descriptors for either kind may be difficult.)If this environment binds no variable named name, throw a
ReferenceError
. - defineVariable(name, descriptor)
- Create or reconfigure the variable bound to name in this environment according to descriptor. Descriptor is the sort of value returned by
getVariableDescriptor
. On success, returnundefined
; on failure, throw an appropriate exception. Name must be a string whose value is a valid ECMAScript identifier name.If implementation restrictions prevent SpiderMonkey from creating or reconfiguring the variable as requested, this call throws an
Error
exception. - deleteVariable(name)
- Delete this environment's binding for name.
If this environment binds no variable named name, throw a
ReferenceError
.If implementation restrictions prevent SpiderMonkey from deleting the variable as requested, this call throws an
Error
exception. - find(name)
- Return a reference to the innermost environment, starting with this environment, that binds name. If name is not in scope in this environment, return
null
. Name must be a string whose value is a valid ECMAScript identifier name. - eval(code) (future plan)
- Evaluate code in this environment, and return a [[#Completion_Values|completion value]] describing how it completed. Code is a string. All extant handler methods, breakpoints, watchpoints, and so on remain active during the call. This function follows the [[#Invocation_Functions_and_.22debugger.22_Frames|invocation function conventions]].
Code is interpreted as strict mode code when it contains a Use Strict Directive.
If code is not strict mode code, then variable declarations in code affect this environment. (In the terms used by the ECMAScript specification, the
VariableEnvironment
of the execution context for the eval code is theVariableEnvironment
thisDebugger.Environment
instance represents.) If implementation restrictions prevent SpiderMonkey from extending this environment as requested, this call throws anError
exception. - evalWithBindings(code, bindings) (future plan)
- Like
eval
, but evaluate code in this environment, extended with bindings from the object bindings. For each own enumerable property of bindings named name whose value is value, include a variable in the environment in which code is evaluated named name, whose value is value. Each value must be a debuggee value. (This is not like awith
statement: code may access, assign to, and delete the introduced bindings without having any effect on the bindings object.)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, and do so without mutating any existing debuggee environment.
Note that, like
eval
, declarations in the code passed toevalWithBindings
affect this environment, even as code is evaluated with bindings visible. (In the terms used by the ECMAScript specification, theVariableEnvironment
of the execution context for the eval code is theVariableEnvironment
this environment represents, and the bindings appear in a new declarative environment, which is the eval code'sLexicalEnvironment
.) If implementation restrictions prevent SpiderMonkey from extending this environment as requested, this call throws anError
exception.
== Future Work ==
- Since common optimizations (say, the "null closure" closure representation) make environments that one would expect to be present, given the source code, unavailable at run time,
Debugger.Environment
should provide ways to reflect what is and is not available. - It may be possible for
Debugger.Frame.prototype.environment
to return more complete environment chains thanDebugger.Object.prototype.environment
. This possibility should be documented, along with its effects on environment identity. - The interface should provide clean, predictable ways to observe the effects of garbage collection. For example, perhaps it should provide an interface like [[http://www.cs.indiana.edu/~dyb/pubs/guardians-abstract.html R. Kent Dybvig's guardians]] for observing when objects and scripts become unreachable.