diff options
Diffstat (limited to 'js/src/doc/Debugger')
-rw-r--r-- | js/src/doc/Debugger/Conventions.md | 168 | ||||
-rw-r--r-- | js/src/doc/Debugger/Debugger-API.md | 126 | ||||
-rw-r--r-- | js/src/doc/Debugger/Debugger.Environment.md | 153 | ||||
-rw-r--r-- | js/src/doc/Debugger/Debugger.Frame.md | 328 | ||||
-rw-r--r-- | js/src/doc/Debugger/Debugger.Memory.md | 549 | ||||
-rw-r--r-- | js/src/doc/Debugger/Debugger.Object.md | 559 | ||||
-rw-r--r-- | js/src/doc/Debugger/Debugger.Script.md | 379 | ||||
-rw-r--r-- | js/src/doc/Debugger/Debugger.Source.md | 220 | ||||
-rw-r--r-- | js/src/doc/Debugger/Debugger.md | 496 | ||||
-rw-r--r-- | js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md | 222 | ||||
-rw-r--r-- | js/src/doc/Debugger/Tutorial-Debugger-Statement.md | 82 | ||||
-rw-r--r-- | js/src/doc/Debugger/alloc-plot-console.png | bin | 0 -> 82359 bytes | |||
-rw-r--r-- | js/src/doc/Debugger/config.sh | 66 | ||||
-rw-r--r-- | js/src/doc/Debugger/debugger-alert.png | bin | 0 -> 27770 bytes | |||
-rw-r--r-- | js/src/doc/Debugger/enable-chrome-devtools.png | bin | 0 -> 28465 bytes | |||
-rw-r--r-- | js/src/doc/Debugger/scratchpad-browser-environment.png | bin | 0 -> 30443 bytes | |||
-rw-r--r-- | js/src/doc/Debugger/shadows.svg | 997 |
17 files changed, 4345 insertions, 0 deletions
diff --git a/js/src/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md new file mode 100644 index 000000000..e8bd3ee43 --- /dev/null +++ b/js/src/doc/Debugger/Conventions.md @@ -0,0 +1,168 @@ +# General Conventions + +This page describes general conventions used in the [`Debugger`][debugger] API, +and defines some terminology used throughout the specification. + + +## 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); + +- a *debuggee environment* is an environment whose outermost + enclosing environment is a debuggee global object; 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: + +<code>{ return: <i>value</i> }</code> +: The code completed normally, returning <i>value</i>. <i>Value</i> is a + debuggee value. + +<code>{ throw: <i>value</i> }</code> +: The code threw <i>value</i> as an exception. <i>Value</i> 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 +<code>{ throw: <i>stop</i> }</code> where <i>stop</i> 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. + +<code>{ return: <i>value</i> }</code> +: 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 descriptions say otherwise.) If the function + was called as a constructor (that is, via a `new` expression), then + <i>value</i> serves as the value returned by the function's body, not + that produced by the `new` expression: if the value is not an object, + the `new` expression returns the frame's `this` value. Similarly, if + the function is the constructor for a subclass, then a non-object + value may result in a TypeError. + If the frame is a generator or async function, then <i>value</i> must + conform to the iterator protocol: it must be a non-proxy object of the form + <code>{ done: <i>boolean</i>, value: <i>v</i> }</code>, where + both `done` and `value` are ordinary properties. + +<code>{ throw: <i>value</i> }</code> +: Throw <i>value</i> as an exception from the current bytecode + instruction. <i>Value</i> 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. + + +## Timestamps + +Timestamps are expressed in units of milliseconds since an arbitrary, +but fixed, epoch. The resolution of timestamps is generally greater +than milliseconds, though no specific resolution is guaranteed. + + +## 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][inv fr], 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. + +If there are debugger frames on stack from multiple Debugger instances, the +thrown exception is an instance of the topmost locking debugger's global's +`Debugger.DebuggeeWouldRun`. + +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: + + <i>cause</i> 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. diff --git a/js/src/doc/Debugger/Debugger-API.md b/js/src/doc/Debugger/Debugger-API.md new file mode 100644 index 000000000..10f6ebf89 --- /dev/null +++ b/js/src/doc/Debugger/Debugger-API.md @@ -0,0 +1,126 @@ +# The `Debugger` Interface + +Mozilla's JavaScript engine, SpiderMonkey, provides a debugging interface +named `Debugger` which lets JavaScript code observe and manipulate the +execution of other JavaScript code. Both Firefox's built-in developer tools +and the Firebug add-on use `Debugger` to implement their JavaScript +debuggers. However, `Debugger` is quite general, and can be used to +implement other kinds of tools like tracers, coverage analysis, +patch-and-continue, and so on. + +`Debugger` has three essential qualities: + +- It is a *source level* interface: it operates in terms of the JavaScript + language, not machine language. It operates on JavaScript objects, stack + frames, environments, and code, and presents a consistent interface + regardless of whether the debuggee is interpreted, compiled, or + optimized. If you have a strong command of the JavaScript language, you + should have all the background you need to use `Debugger` successfully, + even if you have never looked into the language's implementation. + +- It is for use *by JavaScript code*. JavaScript is both the debuggee + language and the tool implementation language, so the qualities that make + JavaScript effective on the web can be brought to bear in crafting tools + for developers. As is expected of JavaScript APIs, `Debugger` is a + *sound* interface: using (or even misusing) `Debugger` should never cause + Gecko to crash. Errors throw proper JavaScript exceptions. + +- It is an *intra-thread* debugging API. Both the debuggee and the code + using `Debugger` to observe it must run in the same thread. Cross-thread, + cross-process, and cross-device tools must use `Debugger` to observe the + debuggee from within the same thread, and then handle any needed + communication themselves. (Firefox's builtin tools have a + [protocol][protocol] defined for this purpose.) + +In Gecko, the `Debugger` API is available to chrome code only. By design, +it ought not to introduce security holes, so in principle it could be made +available to content as well; but it is hard to justify the security risks +of the additional attack surface. + +The `Debugger` API cannot currently observe self-hosted JavaScript. This is not +inherent in the API's design, but simply that the self-hosting infrastructure +isn't prepared for the kind of invasions the `Debugger` API can perform. + + +## Debugger Instances and Shadow Objects + +`Debugger` reflects every aspect of the debuggee's state as a JavaScript +value---not just actual JavaScript values like objects and primitives, +but also stack frames, environments, scripts, and compilation units, which +are not normally accessible as objects in their own right. + +Here is a JavaScript program in the process of running a timer callback function: + +![A running JavaScript program and its Debugger shadows][img-shadows] + +This diagram shows the various types of shadow objects that make up the +Debugger API (which all follow some [general conventions][conventions]): + +- A [`Debugger.Object`][object] represents a debuggee object, offering a + reflection-oriented API that protects the debugger from accidentally + invoking getters, setters, proxy traps, and so on. + +- A [`Debugger.Script`][script] represents a block of JavaScript + code---either a function body or a top-level script. Given a + `Debugger.Script`, one can set breakpoints, translate between source + positions and bytecode offsets (a deviation from the "source level" + design principle), and find other static characteristics of the code. + +- A [`Debugger.Frame`][frame] represents a running stack frame. You can use + these to walk the stack and find each frame's script and environment. You + can also set `onStep` and `onPop` handlers on frames. + +- A [`Debugger.Environment`][environment] represents an environment, + associating variable names with storage locations. Environments may + belong to a running stack frame, captured by a function closure, or + reflect some global object's properties as variables. + +The [`Debugger`][debugger-object] instance itself is not really a shadow of +anything in the debuggee; rather, it maintains the set of global objects +which are to be considered debuggees. A `Debugger` observes only execution +taking place in the scope of these global objects. You can set functions to +be called when new stack frames are pushed; when new code is loaded; and so +on. + +Omitted from this picture are [`Debugger.Source`][source] instances, which +represent JavaScript compilation units. A `Debugger.Source` can furnish a +full copy of its source code, and explain how the code entered the system, +whether via a call to `eval`, a `<script>` element, or otherwise. A +`Debugger.Script` points to the `Debugger.Source` from which it is derived. + +Also omitted is the `Debugger`'s [`Debugger.Memory`][memory] instance, which +holds methods and accessors for observing the debuggee's memory use. + +All these types follow some [general conventions][conventions], which you +should look through before drilling down into any particular type's +specification. + +All shadow objects are unique per `Debugger` and per referent. For a given +`Debugger`, there is exactly one `Debugger.Object` that refers to a +particular debuggee object; exactly one `Debugger.Frame` for a particular +stack frame; and so on. Thus, a tool can store metadata about a shadow's +referent as a property on the shadow itself, and count on finding that +metadata again if it comes across the same referent. And since shadows are +per-`Debugger`, tools can do so without worrying about interfering with +other tools that use their own `Debugger` instances. + + +## Examples + +Here are some things you can try out yourself that show off some of `Debugger`'s +features: + +- [Evaluating an expression in a web page's stack frame when it executes a `debugger;` statement.][tut debugger] + +- [Showing how many objects different call paths allocate.][tut alloc log] + + +## Gecko-specific features + +While the `Debugger` core API deals only with concepts common to any +JavaScript implementation, it also includes some Gecko-specific features: + +- [Global tracking][global] supports debugging all the code running in a + Gecko instance at once---the 'chrome debugging' model. +- [Object wrapper][wrapper] functions help manipulate object references + that cross privilege boundaries. diff --git a/js/src/doc/Debugger/Debugger.Environment.md b/js/src/doc/Debugger/Debugger.Environment.md new file mode 100644 index 000000000..e970f0265 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Environment.md @@ -0,0 +1,153 @@ +# Debugger.Environment + +A `Debugger.Environment` instance represents a lexical environment, +associating names with variables. Each [`Debugger.Frame`][frame] instance +representing a debuggee frame has an associated environment object +describing the variables in scope in that frame; and each +[`Debugger.Object`][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 <i>binds</i> an identifier if that +environment itself associates the identifier with a variable, independently +of its outer environments. We say an identifier is <i>in scope</i> 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`][debugger-object] 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`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Environment` instance for a given environment. This allows the +code using each [`Debugger`][debugger-object] instance to place whatever +properties it likes on its own [`Debugger.Object`][object] instances, +without worrying about interfering with other debuggers.) + +If a `Debugger.Environment` instance's referent is not a debuggee +environment, then attempting to access its properties (other than +`inspectable`) or call any its methods throws an instance of `Error`. + +`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: + +`inspectable` +: True if this environment is a debuggee environment, and can therefore + be inspected. False otherwise. All other properties and methods of + `Debugger.Environment` instances throw if applied to a non-inspectable + environment. + +`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. + +`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`][object] instance referring to the object whose + properties this environment reflects. If this is a declarative + environment record, this accessor throws a `TypeError` (since + declarative environment records have no such object). Both `"object"` + and `"with"` environments have `object` 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 <i>f</i>, then this property's value is a + [`Debugger.Object`][object] instance referring to <i>f</i>. Otherwise, + this property's value is `null`. + +`optimizedOut` +: True if this environment is optimized out. False otherwise. For example, + functions whose locals are never aliased may present optimized-out + environments. When true, `getVariable` returns an ordinary JavaScript + object whose `optimizedOut` property is true on all bindings, and + `setVariable` throws a `ReferenceError`. + + +## 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. + +<code>getVariable(<i>name</i>)</code> +: Return the value of the variable bound to <i>name</i> in this + environment, or `undefined` if this environment does not bind + <i>name</i>. <i>Name</i> must be a string that is a valid ECMAScript + identifier name. The result is a debuggee value. + + JavaScript engines often omit variables from environments, to save space + and reduce execution time. If the given variable should be in scope, but + `getVariable` is unable to produce its value, it returns an ordinary + JavaScript object (not a [`Debugger.Object`][object] instance) whose + `optimizedOut` property is `true`. + + This is not an [invocation function][inv fr]; + if this call would cause debuggee code to run (say, because the + environment is a `"with"` environment, and <i>name</i> refers to an + accessor property of the `with` statement's operand), this call throws a + [`Debugger.DebuggeeWouldRun`][wouldrun] + exception. + +<code>setVariable(<i>name</i>, <i>value</i>)</code> +: Store <i>value</i> as the value of the variable bound to <i>name</i> in + this environment. <i>Name</i> must be a string that is a valid + ECMAScript identifier name; <i>value</i> must be a debuggee value. + + If this environment binds no variable named <i>name</i>, throw a + `ReferenceError`. + + This is not an [invocation function][inv fr]; + if this call would cause debuggee code to run, this call throws a + [`Debugger.DebuggeeWouldRun`][wouldrun] + exception. + +<code>find(<i>name</i>)</code> +: Return a reference to the innermost environment, starting with this + environment, that binds <i>name</i>. If <i>name</i> is not in scope in + this environment, return `null`. <i>Name</i> must be a string whose + value is a valid ECMAScript identifier name. + diff --git a/js/src/doc/Debugger/Debugger.Frame.md b/js/src/doc/Debugger/Debugger.Frame.md new file mode 100644 index 000000000..68daaae6c --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Frame.md @@ -0,0 +1,328 @@ +# Debugger.Frame + +A `Debugger.Frame` instance represents a [visible stack frame][vf]. 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`][debugger-object] 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`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Frame` instance for a given frame. This allows the code using +each [`Debugger`][debugger-object] 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. + + +## Visible Frames + +When inspecting the call stack, [`Debugger`][debugger-object] 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`][debugger-object] does reveal +<i>visible frames</i>. + +A frame is a visible frame if any of the following are true: + +* it is running [debuggee code][dbg code]; + +* its immediate caller is a frame running debuggee code; or + +* it is a [`"debugger"` frame][inv fr], + 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.) + + +## <span id='invf'>Invocation</span> Functions and "debugger" Frames + +An <i>invocation function</i> 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: + +1. Let <i>older</i> 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 as `Debugger.Frame` instances.) + +2. Push a `"debugger"` frame on the stack, with <i>older</i> as its + `older` property. + +3. 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, whereas `Debugger.Object.prototype.call` pushes a `"call"` frame. + +4. When the debuggee code completes, whether by returning, throwing an + exception or being terminated, pop the `"debugger"` frame, and return an + appropriate [completion value][cv] 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 to `eval`. + + * `"global"`: a frame running global code (JavaScript that is neither of + the above). + + * `"module"`: a frame running code at the top level of a module. + + * `"debugger"`: a frame for a call to user code invoked by the debugger + (see the `eval` method below). + +`implementation` +: A string describing which tier of the JavaScript engine this frame is + executing in: + + * `"interpreter"`: a frame running in the interpreter. + + * `"baseline"`: a frame running in the unoptimizing, baseline JIT. + + * `"ion"`: a frame running in the optimizing JIT. + +`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`. + +`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; false if it has completed execution or been + popped in some other way. + +`script` +: The script being executed in this frame (a [`Debugger.Script`][script] + instance), or `null` on frames that do not represent calls to debuggee + code. On frames whose `callee` property is not null, this is equal to + `callee.script`. + +`offset` +: The offset of the bytecode instruction currently being executed in + `script`, or `undefined` if the frame's `script` property is `null`. + +`environment` +: The lexical environment within which evaluation is taking place (a + [`Debugger.Environment`][environment] instance), or `null` 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, with `Array.prototype` on its prototype + chain, a non-writable `length` 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 this + `Debugger.Frame` instance as the `this`value. The function should + return a [resumption value][rv] 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`][debugger-object] instances each have + `Debugger.Frame` instances for a given stack frame with `onStep` + handlers set, their handlers are run in an unspecified order. If any + `onStep` handler forces the frame to return early (by returning a + resumption value other than `undefined`), 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 value][cv] indicating how this frame's execution + completed, and providing this `Debugger.Frame` instance as the `this` + value. The function should return a [resumption value][rv] indicating + how execution should proceed. On newly created frames, this property's + value is `undefined`. + + When this handler is called, this frame's current execution location, as + reflected in its `offset` and `environment` properties, is the operation + which caused it to be unwound. In frames returning or throwing an + exception, the location is often a return or a throw statement. In frames + propagating exceptions, the location is a call. + + When an `onPop` call reports the completion of a construction call + (that is, a function called via the `new` 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 the `new` expression, which will be the value of + the frame's `this` property. (In ECMAScript terms, the `onPop` 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:... }`, or `null` resumption + value, SpiderMonkey calls the frame's `onPop` 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 returns `undefined`, + then SpiderMonkey proceeds with the exception or termination. That is, + an `undefined` resumption value leaves the frame's throwing and + termination process undisturbed. + + If multiple [`Debugger`][debugger-object] instances each have + `Debugger.Frame` instances for a given stack frame with `onPop` + 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 handler is not called on `"debugger"` frames. It is also not called + when unwinding a frame due to an over-recursion or out-of-memory + exception. + + +## 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. + +<code id="eval">eval(<i>code</i>, [<i>options</i>])</code> +: Evaluate <i>code</i> in the execution context of this frame, and return + a [completion value][cv] describing how it completed. <i>Code</i> is a + string. If this frame's `environment` property is `null`, throw a + `TypeError`. All extant handler methods, breakpoints, and + so on remain active during the call. This function follows the + [invocation function conventions][inv fr]. + + <i>Code</i> 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 <i>code</i> is not strict mode code, then variable declarations in + <i>code</i> 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 the `VariableEnvironment` 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. + + If given, <i>options</i> should be an object whose properties specify + details of how the evaluation should occur. The `eval` method + recognizes the following properties: + + <code>url</code> + : The filename or URL to which we should attribute <i>code</i>. If this + property is omitted, the URL defaults to `"debugger eval code"`. + + <code>lineNumber</code> + : The line number at which the evaluated code should be claimed to begin + within <i>url</i>. + +<code>evalWithBindings(<i>code</i>, <i>bindings</i>, [<i>options</i>])</code> +: Like `eval`, but evaluate <i>code</i> in the environment of this frame, + extended with bindings from the object <i>bindings</i>. For each own + enumerable property of <i>bindings</i> named <i>name</i> whose value is + <i>value</i>, include a variable in the environment in which + <i>code</i> is evaluated named <i>name</i>, whose value is + <i>value</i>. Each <i>value</i> must be a debuggee value. (This is not + like a `with` statement: <i>code</i> may access, assign to, and delete + the introduced bindings without having any effect on the + <i>bindings</i> 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 <i>code</i> passed to + `evalWithBindings` affect the environment of this frame, even as that + environment is extended by bindings visible within <i>code</i>. (In the + terms used by the ECMAScript specification, the `VariableEnvironment` + of the execution context for the eval code is the `VariableEnvironment` + of the execution context that this frame represents, and the + <i>bindings</i> appear in a new declarative environment, which is the + eval code's `LexicalEnvironment`.) If implementation restrictions + prevent SpiderMonkey from extending this frame's environment as + requested, this call throws an `Error` exception. + + The <i>options</i> argument is as for + [`Debugger.Frame.prototype.eval`][fr eval], described above. diff --git a/js/src/doc/Debugger/Debugger.Memory.md b/js/src/doc/Debugger/Debugger.Memory.md new file mode 100644 index 000000000..a5ccd93cd --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Memory.md @@ -0,0 +1,549 @@ +Debugger.Memory +=============== + +The [`Debugger API`][debugger] can help tools observe the debuggee's memory use +in various ways: + +- It can mark each new object with the JavaScript call stack at which it was + allocated. + +- It can log all object allocations, yielding a stream of JavaScript call stacks + at which allocations have occurred. + +- It can compute a *census* of items belonging to the debuggee, categorizing + items in various ways, and yielding item counts. + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, then the methods and +accessor properties of <code><i>dbg</i>.memory</code> control how <i>dbg</i> +observes its debuggees' memory use. The <code><i>dbg</i>.memory</code> object is +an instance of `Debugger.Memory`; its inherited accesors and methods are +described below. + + +### Allocation Site Tracking + +The JavaScript engine marks each new object with the call stack at which it was +allocated, if: + +- the object is allocated in the scope of a global object that is a debuggee of + some [`Debugger`][debugger-object] instance <i>dbg</i>; and + +- <code><i>dbg</i>.memory.[trackingAllocationSites][tracking-allocs]</code> is + set to `true`. + +- A [Bernoulli trial][bernoulli-trial] succeeds, with probability equal to the + maximum of + [`d.memory.allocationSamplingProbability`][alloc-sampling-probability] of all + `Debugger` instances `d` that are observing the global that this object is + allocated within the scope of. + +Given a [`Debugger.Object`][object] instance <i>dobj</i> referring to some +object, <code><i>dobj</i>.[allocationSite][allocation-site]</code> returns a +[saved call stack][saved-frame] indicating where <i>dobj</i>'s referent was +allocated. + + +### Allocation Logging + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, and +<code><i>dbg</i>.memory.[trackingAllocationSites][tracking-allocs]</code> is set +to `true`, then the JavaScript engine logs each object allocated by <i>dbg</i>'s +debuggee code. You can retrieve the current log by calling +<code><i>dbg</i>.memory.[drainAllocationsLog][drain-alloc-log]</code>. You can +control the limit on the log's size by setting +<code><i>dbg</i>.memory.[maxAllocationsLogLength][max-alloc-log]</code>. + + +### Censuses + +A *census* is a complete traversal of the graph of all reachable memory items +belonging to a particular `Debugger`'s debuggees. It produces a count of those +items, broken down by various criteria. If <i>dbg</i> is a +[`Debugger`][debugger-object] instance, you can call +<code><i>dbg</i>.memory.[takeCensus][take-census]</code> to conduct a census of +its debuggees' possessions. + + +Accessor Properties of the `Debugger.Memory.prototype` Object +------------------------------------------------------------- + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, then +`<i>dbg</i>.memory` is a `Debugger.Memory` instance, which inherits the +following accessor properties from its prototype: + +<code id='trackingallocationsites'>trackingAllocationSites</code> +: A boolean value indicating whether this `Debugger.Memory` instance is + capturing the JavaScript execution stack when each Object is allocated. This + accessor property has both a getter and setter: assigning to it enables or + disables the allocation site tracking. Reading the accessor produces `true` + if the Debugger is capturing stacks for Object allocations, and `false` + otherwise. Allocation site tracking is initially disabled in a new Debugger. + + Assignment is fallible: if the Debugger cannot track allocation sites, it + throws an `Error` instance. + + You can retrieve the allocation site for a given object with the + [`Debugger.Object.prototype.allocationSite`][allocation-site] accessor + property. + +<code id='alloc-sampling-probability'>allocationSamplingProbability</code> +: A number between 0 and 1 that indicates the probability with which each new + allocation should be entered into the allocations log. 0 is equivalent to + "never", 1 is "always", and .05 would be "one out of twenty". + + The default is 1, or logging every allocation. + + Note that in the presence of multiple <code>Debugger</code> instances + observing the same allocations within a global's scope, the maximum + <code>allocationSamplingProbability</code> of all the + <code>Debugger</code>s is used. + +<code id='max-alloc-log'>maxAllocationsLogLength</code> +: The maximum number of allocation sites to accumulate in the allocations log + at a time. This accessor can be both fetched and stored to. Its default + value is `5000`. + +<code id='allocationsLogOverflowed'>allocationsLogOverflowed</code> +: Returns `true` if there have been more than + [`maxAllocationsLogLength`][#max-alloc-log] allocations since the last time + [`drainAllocationsLog`][#drain-alloc-log] was called and some data has been + lost. Returns `false` otherwise. + +Debugger.Memory Handler Functions +--------------------------------- + +Similar to [`Debugger`'s handler functions][debugger], `Debugger.Memory` +inherits accessor properties that store handler functions for SpiderMonkey to +call when given events occur in debuggee code. + +Unlike `Debugger`'s hooks, `Debugger.Memory`'s handlers' return values are not +significant, and are ignored. The handler functions receive the +`Debugger.Memory`'s owning `Debugger` instance as their `this` value. The owning +`Debugger`'s `uncaughtExceptionHandler` is still fired for errors thrown in +`Debugger.Memory` hooks. + +On a new `Debugger.Memory` 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. + +<code>onGarbageCollection(<i>statistics</i>)</code> +: A garbage collection cycle spanning one or more debuggees has just been + completed. + + The *statistics* parameter is an object containing information about the GC + cycle. It has the following properties: + + `collections` + : The `collections` property's value is an array. Because SpiderMonkey's + collector is incremental, a full collection cycle may consist of + multiple discrete collection slices with the JS mutator running + interleaved. For each collection slice that occurred, there is an entry + in the `collections` array with the following form: + + <pre class='language-js'><code> + { + "startTimestamp": <i>timestamp</i>, + "endTimestamp": <i>timestamp</i>, + } + </code></pre> + + Here the *timestamp* values are [timestamps][] of the GC slice's start + and end events. + + `reason` + : A very short string describing the reason why the collection was + triggered. Known values include the following: + + * "API" + * "EAGER_ALLOC_TRIGGER" + * "DESTROY_RUNTIME" + * "LAST_DITCH" + * "TOO_MUCH_MALLOC" + * "ALLOC_TRIGGER" + * "DEBUG_GC" + * "COMPARTMENT_REVIVED" + * "RESET" + * "OUT_OF_NURSERY" + * "EVICT_NURSERY" + * "FULL_STORE_BUFFER" + * "SHARED_MEMORY_LIMIT" + * "PERIODIC_FULL_GC" + * "INCREMENTAL_TOO_SLOW" + * "DOM_WINDOW_UTILS" + * "COMPONENT_UTILS" + * "MEM_PRESSURE" + * "CC_WAITING" + * "CC_FORCED" + * "LOAD_END" + * "PAGE_HIDE" + * "NSJSCONTEXT_DESTROY" + * "SET_NEW_DOCUMENT" + * "SET_DOC_SHELL" + * "DOM_UTILS" + * "DOM_IPC" + * "DOM_WORKER" + * "INTER_SLICE_GC" + * "REFRESH_FRAME" + * "FULL_GC_TIMER" + * "SHUTDOWN_CC" + * "FINISH_LARGE_EVALUATE" + * "USER_INACTIVE" + + `nonincrementalReason` + : If SpiderMonkey's collector determined it could not incrementally + collect garbage, and had to do a full GC all at once, this is a short + string describing the reason it determined the full GC was necessary. + Otherwise, `null` is returned. Known values include the following: + + * "GC mode" + * "malloc bytes trigger" + * "allocation trigger" + * "requested" + + `gcCycleNumber` + : The GC cycle's "number". Does not correspond to the number + of GC cycles that have run, but is guaranteed to be monotonically + increasing. + +Function Properties of the `Debugger.Memory.prototype` Object +------------------------------------------------------------- + +<code id='drain-alloc-log'>drainAllocationsLog()</code> +: When `trackingAllocationSites` is `true`, this method returns an array of + recent `Object` allocations within the set of debuggees. *Recent* is + defined as the `maxAllocationsLogLength` most recent `Object` allocations + since the last call to `drainAllocationsLog`. Therefore, calling this + method effectively clears the log. + + Objects in the array are of the form: + + <pre class='language-js'><code> + { + "timestamp": <i>timestamp</i>, + "frame": <i>allocationSite</i>, + "class": <i>className</i>, + "constructor": <i>constructorName</i>, + "size": <i>byteSize</i>, + "inNursery": <i>inNursery</i>, + } + </code></pre> + + Where + + * *timestamp* is the [timestamp][timestamps] of the allocation event. + + * *allocationSite* is an allocation site (as a + [captured stack][saved-frame]). Note that this property can be null if the + object was allocated with no JavaScript frames on the stack. + + * *className* is the string name of the allocated object's internal + `[[Class]]` property, for example "Array", "Date", "RegExp", or (most + commonly) "Object". + + * *constructorName* is the constructor function's display name for objects + created by `new Ctor`. If that data is not available, or the object was + not created with a `new` expression, this property is `null`. + + * *byteSize* is the size of the object in bytes. + + * *inNursery* is true if the allocation happened inside the nursery. False + if the allocation skipped the nursery and started in the tenured heap. + + When `trackingAllocationSites` is `false`, `drainAllocationsLog()` throws an + `Error`. + +<code id='take-census'>takeCensus(<i>options</i>)</code> +: Carry out a census of the debuggee compartments' contents. A *census* is a + complete traversal of the graph of all reachable memory items belonging to a + particular `Debugger`'s debuggees. The census produces a count of those + items, broken down by various criteria. + + The <i>options</i> argument is an object whose properties specify how the + census should be carried out. + + If <i>options</i> has a `breakdown` property, that determines how the census + categorizes the items it finds, and what data it collects about them. For + example, if `dbg` is a `Debugger` instance, the following performs a simple + count of debuggee items: + + dbg.memory.takeCensus({ breakdown: { by: 'count' } }) + + That might produce a result like: + + { "count": 1616, "bytes": 93240 } + + Here is a breakdown that groups JavaScript objects by their class name, and + non-string, non-script items by their C++ type name: + + { + by: "coarseType", + objects: { by: "objectClass" }, + other: { by: "internalType" } + } + + which produces a result like this: + + { + "objects": { + "Function": { "count": 404, "bytes": 37328 }, + "Object": { "count": 11, "bytes": 1264 }, + "Debugger": { "count": 1, "bytes": 416 }, + "ScriptSource": { "count": 1, "bytes": 64 }, + // ... omitted for brevity... + }, + "scripts": { "count": 1, "bytes": 0 }, + "strings": { "count": 701, "bytes": 49080 }, + "other": { + "js::Shape": { "count": 450, "bytes": 0 }, + "js::BaseShape": { "count": 21, "bytes": 0 }, + "js::ObjectGroup": { "count": 17, "bytes": 0 } + } + } + + In general, a `breakdown` value has one of the following forms: + + <code>{ by: "count", count:<i>count<i>, bytes:<i>bytes</i> }</code> + : The trivial categorization: none whatsoever. Simply tally up the items + visited. If <i>count</i> is true, count the number of items visited; if + <i>bytes</i> is true, total the number of bytes the items use directly. + Both <i>count</i> and <i>bytes</i> are optional; if omitted, they + default to `true`. In the result of the census, this breakdown produces + a value of the form: + + { "count":<i>n</b>, "bytes":<i>b</i> } + + where the `count` and `bytes` properties are present as directed by the + <i>count</i> and <i>bytes</i> properties on the breakdown. + + Note that the census can produce byte sizes only for the most common + types. When the census cannot find the byte size for a given type, it + returns zero. + + <code>{ by: "bucket" }</code> + : Do not do any filtering or categorizing. Instead, accumulate a bucket of + each node's ID for every node that matches. The resulting report is an + array of the IDs. + + For example, to find the ID of all nodes whose internal object + `[[class]]` property is named "RegExp", you could use the following code: + + const report = dbg.memory.takeCensus({ + breakdown: { + by: "objectClass", + then: { by: "bucket" } + } + }); + doStuffWithRegExpIDs(report.RegExp); + + <code>{ by: "allocationStack", then:<i>breakdown</i>, noStack:<i>noStackBreakdown</i> }</code> + : Group items by the full JavaScript stack trace at which they were + allocated. + + Further categorize all the items allocated at each distinct stack using + <i>breakdown</i>. + + In the result of the census, this breakdown produces a JavaScript `Map` + value whose keys are `SavedFrame` values, and whose values are whatever + sort of result <i>breakdown</i> produces. Objects allocated on an empty + JavaScript stack appear under the key `null`. + + SpiderMonkey only tracks allocation sites for items if requested via the + [`trackingAllocationSites`][tracking-allocs] flag; even then, it does + not record allocation sites for every kind of item that appears in the + heap. Items that lack allocation site information are counted using + <i>noStackBreakdown</i>. These appear in the result `Map` under the key + string `"noStack"`. + + <code>{ by: "objectClass", then:<i>breakdown</i>, other:<i>otherBreakdown</i> }</code> + : Group JavaScript objects by their ECMAScript `[[Class]]` internal property values. + + Further categorize JavaScript objects in each class using + <i>breakdown</i>. Further categorize items that are not JavaScript + objects using <i>otherBreakdown</i>. + + In the result of the census, this breakdown produces a JavaScript object + with no prototype whose own property names are strings naming classes, + and whose values are whatever sort of result <i>breakdown</i> produces. + The results for non-object items appear as the value of the property + named `"other"`. + + <code>{ by: "coarseType", objects:<i>objects</i>, scripts:<i>scripts</i>, strings:<i>strings</i>, other:<i>other</i> }</code> + : Group items by their coarse type. + + Use the breakdown value <i>objects</i> for items that are JavaScript + objects. + + Use the breakdown value <i>scripts</i> for items that are + representations of JavaScript code. This includes bytecode, compiled + machine code, and saved source code. + + Use the breakdown value <i>strings</i> for JavaScript strings. + + Use the breakdown value <i>other</i> for items that don't fit into any of + the above categories. + + In the result of the census, this breakdown produces a JavaScript object + of the form: + + <pre class='language-js'><code> + { + "objects": <i>result</i>, + "scripts": <i>result</i>, + "strings": <i>result</i>, + "other": <i>result</i> + } + </code></pre> + + where each <i>result</i> is a value of whatever sort the corresponding + breakdown value produces. All breakdown values are optional, and default + to `{ type: "count" }`. + + <code>{ by: "internalType", then:<i>breakdown</i> }</code> + : Group items by the names given their types internally by SpiderMonkey. + These names are not meaningful to web developers, but this type of + breakdown does serve as a catch-all that can be useful to Firefox tool + developers. + + For example, a census of a pristine debuggee global broken down by + internal type name typically looks like this: + + { + "JSString": { "count": 701, "bytes": 49080 }, + "js::Shape": { "count": 450, "bytes": 0 }, + "JSObject": { "count": 426, "bytes": 44160 }, + "js::BaseShape": { "count": 21, "bytes": 0 }, + "js::ObjectGroup": { "count": 17, "bytes": 0 }, + "JSScript": { "count": 1, "bytes": 0 } + } + + In the result of the census, this breakdown produces a JavaScript object + with no prototype whose own property names are strings naming types, + and whose values are whatever sort of result <i>breakdown</i> produces. + + <code>[ <i>breakdown</i>, ... ]</code> + : Group each item using all the given breakdown values. In the result of + the census, this breakdown produces an array of values of the sort + produced by each listed breakdown. + + To simplify breakdown values, all `then` and `other` properties are optional. + If omitted, they are treated as if they were `{ type: "count" }`. + + If the `options` argument has no `breakdown` property, `takeCensus` defaults + to the following: + + <pre class='language-js'><code> + { + by: "coarseType", + objects: { by: "objectClass" }, + other: { by: "internalType" } + } + </code></pre> + + which produces results of the form: + + <pre class='language-js'><code> + { + objects: { <i>class</i>: <i>count</i>, ... }, + scripts: <i>count</i>, + strings: <i>count</i>, + other: { <i>type name</i>: <i>count</i>, ... } + } + </code></pre> + + where each <i>count</i> has the form: + + <pre class='language-js'><code> + { "count": <i>count</i>, bytes:<i>bytes</i> } + </code></pre> + + Because performing a census requires traversing the entire graph of objects + in debuggee compartments, it is an expensive operation. On developer + hardware in 2014, traversing a memory graph containing roughly 130,000 nodes + and 410,000 edges took roughly 100ms. The traversal itself temporarily + allocates one hash table entry per node (roughly two address-sized words) in + addition to the per-category counts, whose size depends on the number of + categories. + + +Memory Use Analysis Exposes Implementation Details +-------------------------------------------------- + +Memory analysis may yield surprising results, because browser implementation +details that are transparent to content JavaScript often have visible effects on +memory consumption. Web developers need to know their pages' actual memory +consumption on real browsers, so it is correct for the tool to expose these +behaviors, as long as it is done in a way that helps developers make decisions +about their own code. + +This section covers some areas where Firefox's actual behavior deviates from +what one might expect from the specified behavior of the web platform. + + +### Objects + +SpiderMonkey objects usually use less memory than the naïve "table of properties +with attributes" model would suggest. For example, it is typical for many +objects to have identical sets of properties, with only the properties' values +varying from one object to the next. To take advantage of this regularity, +SpiderMonkey objects with identical sets of properties may share their property +metadata; only property values are stored directly in the object. + +Array objects may also be optimized, if the set of live indices is dense. + + +### Strings + +SpiderMonkey has three representations of strings: + +- Normal: the string's text is counted in its size. + +- Substring: the string is a substring of some other string, and points to that + string for its storage. This representation may result in a small string + retaining a very large string. However, the memory consumed by the string + itself is a small constant independent of its size, since it is simply a + reference to the larger string, a start position, and a length. + +- Concatenations: When asked to concatenate two strings, SpiderMonkey may elect + to delay copying the strings' data, and represent the result simply as a + pointer to the two original strings. Again, such a string retains other + strings, but the memory consumed by the string itself is a small constant + independent of its size, since it is simply a pair of pointers. + +SpiderMonkey converts strings from the more complex representations to the +simpler ones when it pleases. Such conversions usually increase memory +consumption. + +SpiderMonkey shares some strings amongst all web pages and browser JS. These +shared strings, called *atoms*, are not included in censuses' string counts. + + +### Scripts + +SpiderMonkey has a complex, hybrid representation of JavaScript code. There +are four representations kept in memory: + +- _Source code_. SpiderMonkey retains a copy of most JavaScript source code. + +- _Compressed source code_. SpiderMonkey compresses JavaScript source code, + and de-compresses it on demand. Heuristics determine how long to retain the + uncompressed code. + +- _Bytecode_. This is SpiderMonkey's parsed representation of JavaScript. + Bytecode can be interpreted directly, or used as input to a just-in-time + compiler. Source is parsed into bytecode on demand; functions that are never + called are never parsed. + +- _Machine code_. SpiderMonkey includes several just-in-time compilers, each of + which translates JavaScript source or bytecode to machine code. Heuristics + determine which code to compile, and which compiler to use. Machine code may + be dropped in response to memory pressure, and regenerated as needed. + +Furthermore, SpiderMonkey tracks which types of values have appeared in +variables and object properties. This type information can be large. + +In a census, all the various forms of JavaScript code are placed in the +`"script"` category. Type information is accounted to the `"types"` category. diff --git a/js/src/doc/Debugger/Debugger.Object.md b/js/src/doc/Debugger/Debugger.Object.md new file mode 100644 index 000000000..ee1e588f4 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Object.md @@ -0,0 +1,559 @@ +# 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`][debugger-object] +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`][debugger-object] instance may actually have several +`Debugger.Object` instances: one for each compartment from which the +referent is viewed. + +If more than one [`Debugger`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Object` instance for a given object. This allows the code using +each [`Debugger`][debugger-object] 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), or + `null` 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` and `name` properties are + equal. + + If a function has no name, SpiderMonkey attempts to infer an appropriate + name for it given its context. For example: + + ```language-js + 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 <code><i>a</i>/<i>b</i></code> to refer to + the <i>b</i> defined within <i>a</i>, and <code><i>a</i><</code> to + refer to a function that occurs somewhere within an expression that is + assigned to <i>a</i>. For example: + + ```language-js + 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 debuggee function, the names of the its parameters, + as an array of strings. If the referent is not a debuggee function, or + 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: + + ```language-js + function f(a, [b, c], {d, e:f}) { ... } + ``` + + then this `Debugger.Object` instance's `parameterNames` property would + have the value: + + ```language-js + ["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`][script] instance. If the + referent is a function proxy or not debuggee code, this is `undefined`. + +`environment` +: If the referent is a function that is debuggee code, a + [`Debugger.Environment`][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 is `undefined`. + +`errorMessageName` +: If the referent is an error created with an engine internal message template + this is a string which is the name of the template; `undefined` otherwise. + +`errorLineNumber` +: If the referent is an Error object, this is the line number at which the + referent was created; `undefined` otherwise. + +`errorColumnNumber` +: If the referent is an Error object, this is the column number at which the + referent was created; `undefined` otherwise. + +`isBoundFunction` +: If the referent is a debuggee function, returns `true` if the referent is a + bound function; `false` otherwise. If the referent is not a debuggee + function, or not a function at all, returns `undefined` instead. + +`isArrowFunction` +: If the referent is a debuggee function, returns `true` if the referent is an + arrow function; `false` otherwise. If the referent is not a debuggee + function, or not a function at all, returns `undefined` instead. + +`isPromise` +: `true` if the referent is a Promise; `false` otherwise. + +`boundTargetFunction` +: If the referent is a bound debuggee function, this is its target function— + the function that was bound to a particular `this` object. If the referent + is either not a bound function, not a debuggee function, or not a function + at all, this is `undefined`. + +`boundThis` +: If the referent is a bound debuggee function, this is the `this` value it + was bound to. If the referent is either not a bound function, not a debuggee + function, or not a function at all, this is `undefined`. + +`boundArguments` +: If the referent is a bound debuggee function, this is an array (in the + Debugger object's compartment) that contains the debuggee values of the + `arguments` object it was bound to. If the referent is either not a bound + function, not a debuggee function, or not a function at all, this is + `undefined`. + +`isProxy` +: If the referent is a (scripted) proxy, either revoked or not, return `true`. + If the referent is not a (scripted) proxy, return `false`. + +`proxyTarget` +: If the referent is a non-revoked (scripted) proxy, return a `Debugger.Object` + instance referring to the ECMAScript `[[ProxyTarget]]` of the referent. + If the referent is a revoked (scripted) proxy, return `null`. + If the referent is not a (scripted) proxy, return `undefined`. + +`proxyHandler` +: If the referent is a non-revoked (scripted) proxy, return a `Debugger.Object` + instance referring to the ECMAScript `[[ProxyHandler]]` of the referent. + If the referent is a revoked (scripted) proxy, return `null`. + If the referent is not a (scripted) proxy, return `undefined`. + +`promiseState` +: If the referent is a [`Promise`][promise], return a string indicating + whether the [`Promise`][promise] is pending, or has been fulfilled or + rejected. This string takes one of the following values: + + * `"pending"`, if the [`Promise`][promise] is pending. + + * `"fulfilled"`, if the [`Promise`][promise] has been fulfilled. + + * `"rejected"`, if the [`Promise`][promise] has been rejected. + + If the referent is not a [`Promise`][promise], throw a `TypeError`. + +`promiseValue` +: Return a debuggee value representing the value the [`Promise`][promise] has + been fulfilled with. + + If the referent is not a [`Promise`][promise], or the [`Promise`][promise] + has not been fulfilled, throw a `TypeError`. + +`promiseReason` +: Return a debuggee value representing the value the [`Promise`][promise] has + been rejected with. + + If the referent is not a [`Promise`][promise], or the [`Promise`][promise] + has not been rejected, throw a `TypeError`. + +`promiseAllocationSite` +: If the referent is a [`Promise`][promise], this is the + [JavaScript execution stack][saved-frame] captured at the time of the + promise's allocation. This can return null if the promise was not + created from script. If the referent is not a [`Promise`][promise], throw + a `TypeError` exception. + +`promiseResolutionSite` +: If the referent is a [`Promise`][promise], this is the + [JavaScript execution stack][saved-frame] captured at the time of the + promise's resolution. This can return null if the promise was not + resolved by calling its `resolve` or `reject` resolving functions from + script. If the referent is not a [`Promise`][promise], throw a `TypeError` + exception. + +`promiseID` +: If the referent is a [`Promise`][promise], this is a process-unique + identifier for the [`Promise`][promise]. With e10s, the same id can + potentially be assigned to multiple [`Promise`][promise] instances, if + those instances were created in different processes. If the referent is + not a [`Promise`][promise], throw a `TypeError` exception. + +`promiseDependentPromises` +: If the referent is a [`Promise`][promise], this is an `Array` of + `Debugger.Objects` referring to the promises directly depending on the + referent [`Promise`][promise]. These are: + + 1) Return values of `then()` calls on the promise. + 2) Return values of `Promise.all()` if the referent [`Promise`][promise] + was passed in as one of the arguments. + 3) Return values of `Promise.race()` if the referent [`Promise`][promise] + was passed in as one of the arguments. + + Once a [`Promise`][promise] is settled, it will generally notify its + dependent promises and forget about them, so this is most useful on + *pending* promises. + + Note that the `Array` only contains the promises that directly depend on + the referent [`Promise`][promise]. It does not contain promises that depend + on promises that depend on the referent [`Promise`][promise]. + + If the referent is not a [`Promise`][promise], throw a `TypeError` + exception. + +`promiseLifetime` +: If the referent is a [`Promise`][promise], this is the number of + milliseconds elapsed since the [`Promise`][promise] was created. If the + referent is not a [`Promise`][promise], throw a `TypeError` exception. + +`promiseTimeToResolution` +: If the referent is a [`Promise`][promise], this is the number of + milliseconds elapsed between when the [`Promise`][promise] was created and + when it was resolved. If the referent hasn't been resolved or is not a + [`Promise`][promise], throw a `TypeError` exception. + +`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. + +<code id="allocationsite">allocationSite</code> +: If [object allocation site tracking][tracking-allocs] was enabled when this + `Debugger.Object`'s referent was allocated, return the + [JavaScript execution stack][saved-frame] captured at the time of the + allocation. Otherwise, return `null`. + + +## 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][inv fr]; 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 [`Debugger.DebuggeeWouldRun`][wouldrun] exception. + +<code>getProperty(<i>name</i>)</code> +: Return the value of the referent's property named <i>name</i>, or + `undefined` if it has no such property. <i>Name</i> must be a string. + The result is a debuggee value. + +<code>setProperty(<i>name</i>, <i>value</i>)</code> +: Store <i>value</i> as the value of the referent's property named + <i>name</i>, creating the property if it does not exist. <i>Name</i> + must be a string; <i>value</i> must be a debuggee value. + +<code>getOwnPropertyDescriptor(<i>name</i>)</code> +: Return a property descriptor for the property named <i>name</i> of the + referent. If the referent has no such property, return `undefined`. + (This function behaves like the standard + `Object.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 its `value`, `get`, and `set` properties, + if present, are debuggee values.) + +`getOwnPropertyNames()` +: Return an array of strings naming all the referent's own properties, as + if <code>Object.getOwnPropertyNames(<i>referent</i>)</code> had been + called in the debuggee, and the result copied in the scope of the + debugger's global object. + +`getOwnPropertySymbols()` +: Return an array of strings naming all the referent's own symbols, as + if <code>Object.getOwnPropertySymbols(<i>referent</i>)</code> had been + called in the debuggee, and the result copied in the scope of the + debugger's global object. + +<code>defineProperty(<i>name</i>, <i>attributes</i>)</code> +: Define a property on the referent named <i>name</i>, as described by + the property descriptor <i>descriptor</i>. Any `value`, `get`, and + `set` properties of <i>attributes</i> must be debuggee values. (This + function behaves like `Object.defineProperty`, except that the target + object is implicit, and in a different compartment from the function + and descriptor.) + +<code>defineProperties(<i>properties</i>)</code> +: Add the properties given by <i>properties</i> to the referent. (This + function behaves like `Object.defineProperties`, except that the target + object is implicit, and in a different compartment from the + <i>properties</i> argument.) + +<code>deleteProperty(<i>name</i>)</code> +: Remove the referent's property named <i>name</i>. 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 + standard `Object.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 standard `Object.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.) + +<code>copy(<i>value</i>)</code> +: Apply the HTML5 "structured cloning" algorithm to create a copy of + <i>value</i> 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. + +<code>create(<i>prototype</i>, [<i>properties</i>])</code> +: 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 <i>prototype</i>, which must be an + `Debugger.Object` instance. The new object's properties are as given by + <i>properties</i>, as if <i>properties</i> were passed to + `Debugger.Object.prototype.defineProperties`, with the new + `Debugger.Object` instance as the `this` value. + +<code>makeDebuggeeValue(<i>value</i>)</code> +: Return the debuggee value that represents <i>value</i> in the debuggee. + If <i>value</i> is a primitive, we return it unchanged; if <i>value</i> + is an object, we return the `Debugger.Object` instance representing + that object, wrapped appropriately for use in this `Debugger.Object`'s + referent's compartment. + + Note that, if <i>value</i> 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 described above, each `Debugger.Object` instance presents its + referent as viewed from a particular compartment. Given a + `Debugger.Object` instance <i>d</i> and an object <i>o</i>, the call + <code><i>d</i>.makeDebuggeeValue(<i>o</i>)</code> returns a + `Debugger.Object` instance that presents <i>o</i> as it would be seen + by code in <i>d</i>'s compartment. + +<code>decompile([<i>pretty</i>])</code> +: 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 + <i>pretty</i> is present and true, produce indented code with line + breaks. If the referent is not a function that is debuggee code, return + `undefined`. + +<code>call(<i>this</i>, <i>argument</i>, ...)</code> +: If the referent is callable, call it with the given <i>this</i> value + and <i>argument</i> values, and return a [completion value][cv] + describing how the call completed. <i>This</i> should be a debuggee + value, or `{ asConstructor: true }` to invoke the referent as a + constructor, in which case SpiderMonkey provides an appropriate `this` + value itself. Each <i>argument</i> must be a debuggee value. All extant + handler methods, breakpoints, and so on remain active + during the call. If the referent is not callable, throw a `TypeError`. + This function follows the [invocation function conventions][inv fr]. + +<code>apply(<i>this</i>, <i>arguments</i>)</code> +: If the referent is callable, call it with the given <i>this</i> value + and the argument values in <i>arguments</i>, and return a + [completion value][cv] describing how the call completed. <i>This</i> + should be a debuggee value, or `{ asConstructor: true }` to invoke + <i>function</i> as a constructor, in which case SpiderMonkey provides + an appropriate `this` value itself. <i>Arguments</i> must either be an + array (in the debugger) of debuggee values, or `null` or `undefined`, + which are treated as an empty array. All extant handler methods, + breakpoints, and so on remain active during the call. If + the referent is not callable, throw a `TypeError`. This function + follows the [invocation function conventions][inv fr]. + +<code>executeInGlobal(<i>code</i>, [<i>options</i>])</code> +: If the referent is a global object, evaluate <i>code</i> in that global + environment, and return a [completion value][cv] describing how it completed. + <i>Code</i> is a string. All extant handler methods, breakpoints, + and so on remain active during the call. This function + follows the [invocation function conventions][inv fr]. + If the referent is not a global object, throw a `TypeError` exception. + + <i>Code</i> is interpreted as strict mode code when it contains a Use + Strict Directive. + + This evaluation is semantically equivalent to executing statements at the + global level, not an indirect eval. Regardless of <i>code</i> being strict + mode code, variable declarations in <i>code</i> affect the referent global + object. + + The <i>options</i> argument is as for [`Debugger.Frame.prototype.eval`][fr eval]. + +<code>executeInGlobalWithBindings(<i>code</i>, <i>bindings</i>, [<i>options</i>])</code> +: Like `executeInGlobal`, but evaluate <i>code</i> using the referent as the + variable object, but with a lexical environment extended with bindings + from the object <i>bindings</i>. For each own enumerable property of + <i>bindings</i> named <i>name</i> whose value is <i>value</i>, include a + variable in the lexical environment in which <i>code</i> is evaluated + named <i>name</i>, whose value is <i>value</i>. Each <i>value</i> must + be a debuggee value. (This is not like a `with` statement: <i>code</i> + may access, assign to, and delete the introduced bindings without having + any effect on the <i>bindings</i> 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 `executeInGlobal`, any declarations it contains affect the + referent global object, even as <i>code</i> is evaluated in an environment + extended according to <i>bindings</i>. (In the terms used by the ECMAScript + specification, the `VariableEnvironment` of the execution context for + <i>code</i> is the referent, and the <i>bindings</i> appear in a new + declarative environment, which is the eval code's `LexicalEnvironment`.) + + The <i>options</i> argument is as for [`Debugger.Frame.prototype.eval`][fr eval]. + +`asEnvironment()` +: If the referent is a global object, return the [`Debugger.Environment`][environment] + instance representing the referent's global lexical scope. The global + lexical scope's enclosing scope is the global object. If the referent is + not a global object, throw a `TypeError`. + +`unwrap()` +: If the referent is a wrapper that this `Debugger.Object`'s compartment + is permitted to unwrap, return a `Debugger.Object` instance referring to + the wrapped object. If we are not permitted to unwrap the referent, + return `null`. If the referent is not a wrapper, return this + `Debugger.Object` instance unchanged. + +`unsafeDereference()` +: Return the referent of this `Debugger.Object` instance. + + If the referent is an inner object (say, an HTML5 `Window` object), + return the corresponding outer object (say, the HTML5 `WindowProxy` + object). This makes `unsafeDereference` more useful in producing values + appropriate for direct use by debuggee code, without using [invocation functions][inv fr]. + + 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 via `Debugger.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 use `Debugger.Object` + instances, but use this method to pass direct object references to code + that has not yet been updated. + +<code>forceLexicalInitializationByName(<i>binding</i>)</code> +: If <i>binding</i> is in an uninitialized state initialize it to undefined + and return true, otherwise do nothing and return false. diff --git a/js/src/doc/Debugger/Debugger.Script.md b/js/src/doc/Debugger/Debugger.Script.md new file mode 100644 index 000000000..65eac9026 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Script.md @@ -0,0 +1,379 @@ +# Debugger.Script + +A `Debugger.Script` instance may refer to a sequence of bytecode in the +debuggee or to a block of WebAssembly code. For the former, it is the +[`Debugger`][debugger-object] API's presentation of a JSAPI `JSScript` +object. The two cases are distinguished by their `format` property being +`"js"` or `"wasm"`. + +## Debugger.Script for JSScripts + +For `Debugger.Script` instances referring to a `JSScript`, they are +distinguished by their `format` property being `"js"`. + +Each of the following is represented by a 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`][debugger-object] interface constructs `Debugger.Script` objects as scripts +of debuggee code are uncovered by the debugger: via the `onNewScript` +handler method; via [`Debugger.Frame`][frame]'s `script` properties; via the +`functionScript` method of [`Debugger.Object`][object] instances; and so on. For a +given [`Debugger`][debugger-object] 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`][debugger-object] instance is debugging the same code, each +[`Debugger`][debugger-object] gets a separate `Debugger.Script` instance for a given +script. This allows the code using each [`Debugger`][debugger-object] 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. + +## Debugger.Script for WebAssembly + +For `Debugger.Script` instances referring to a block of WebAssembly code, they +are distinguished by their `format` property being `"wasm"`. + +Currently only entire modules evaluated via `new WebAssembly.Module` are +represented. + +`Debugger.Script` objects for WebAssembly are uncovered via `onNewScript` when +a new WebAssembly module is instantiated and via the `findScripts` method on +[`Debugger`][debugger-object] instances. SpiderMonkey constructs exactly one +`Debugger.Script` for each underlying WebAssembly module per +[`Debugger`][debugger-object] instance. + +A `Debugger.Script` instance is a strong reference to the underlying +WebAssembly module; it protects the module it refers to from being garbage +collected. + +Please note at the time of this writing, support for WebAssembly is +very preliminary. Many properties and methods below throw. + +## Convention + +For descriptions of properties and methods below, if the behavior of the +property or method differs between the instance referring to a `JSScript` or +to a block of WebAssembly code, the text will be split into two sections, +headed by "**if the instance refers to a `JSScript`**" and "**if the instance +refers to WebAssembly code**", respectively. If the behavior does not differ, +no such emphasized headings will appear. + +## Accessor Properties of the Debugger.Script Prototype Object + +A `Debugger.Script` instance inherits the following accessor properties +from its prototype: + +`displayName` +: **If the instance refers to a `JSScript`**, this is the script's display + name, if it has one. If the script has no display name — for example, + if it is a top-level `eval` script — this is `undefined`. + + If the script's function has a given name, its display name is the same as + its function's given name. + + If the script's function has no name, SpiderMonkey attempts to infer an + appropriate name for it given its context. For example: + + ```language-js + 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 <code><i>a</i>/<i>b</i></code> to refer to + the <i>b</i> defined within <i>a</i>, and <code><i>a</i><</code> to + refer to a function that occurs somewhere within an expression that is + assigned to <i>a</i>. For example: + + ```language-js + function h() { + var i = function() {}; // display name: h/i + f(function () {}); // display name: h/< + } + var s = f(function () {}); // display name: s< + ``` + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`url` +: **If the instance refers to a `JSScript`**, the filename or URL from which + this script's code was loaded. If the `source` property is non-`null`, + then this is equal to `source.url`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`startLine` +: **If the instance refers to a `JSScript`**, the number of the line at + which this script's code starts, within the file or document named by + `url`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`lineCount` +: **If the instance refers to a `JSScript`**, the number of lines this + script's code occupies, within the file or document named by `url`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`source` +: **If the instance refers to a `JSScript`**, the + [`Debugger.Source`][source] instance representing the source code from + which this script was produced. This is `null` if the source code was not + retained. + + **If the instance refers to WebAssembly code**, the + [`Debugger.Source`][source] instance representing the serialized text + format of the WebAssembly code. + +`sourceStart` +: **If the instance refers to a `JSScript`**, the character within the + [`Debugger.Source`][source] instance given by `source` at which this + script's code starts; zero-based. If this is a function's script, this is + the index of the start of the `function` token in the source code. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`sourceLength` +: **If the instance refers to a `JSScript`**, the length, in characters, of + this script's code within the [`Debugger.Source`][source] instance given + by `source`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`global` +: **If the instance refers to a `JSScript`**, a [`Debugger.Object`][object] + instance referring to the global object in whose scope this script + runs. The result refers to the global directly, not via a wrapper or a + `WindowProxy` ("outer window", in Firefox). + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`format` +: **If the instance refers to a `JSScript`**, `"js"`. + + **If the instance refers to WebAssembly code**, `"wasm"`. + +## 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. + +`getAllOffsets()` +: **If the instance refers to a `JSScript`**, return an array <i>L</i> + describing the relationship between bytecode instruction offsets and + source code positions in this script. <i>L</i> is sparse, and indexed by + source line number. If a source line number <i>line</i> has no code, then + <i>L</i> has no <i>line</i> property. If there is code for <i>line</i>, + then <code><i>L</i>[<i>line</i>]</code> is an array of offsets of byte + code instructions that are entry points to that line. + + For example, suppose we have a script for the following source code: + + ```language-js + 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: + + ```language-js + [[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. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`getAllColumnOffsets()`: +: **If the instance refers to a `JSScript`**, return an array describing the + relationship between bytecode instruction offsets and source code + positions in this script. Unlike getAllOffsets(), which returns all + offsets that are entry points for each line, getAllColumnOffsets() returns + all offsets that are entry points for each (line, column) pair. + + The elements of the array are objects, each of which describes a single + entry point, and contains the following properties: + + * lineNumber: the line number for which offset is an entry point + + * columnNumber: the column number for which offset is an entry point + + * offset: the bytecode instruction offset of the entry point + + For example, suppose we have a script for the following source code: + + ```language-js + a=[] + for (i=1; i < 10; i++) + // It's hip to be square. + a[i] = i*i; + ``` + + Calling `getAllColumnOffsets()` on that code might yield an array like this: + + ```language-js + [{ lineNumber: 0, columnNumber: 0, offset: 0 }, + { lineNumber: 1, columnNumber: 5, offset: 5 }, + { lineNumber: 1, columnNumber: 10, offset: 20 }, + { lineNumber: 3, columnNumber: 4, offset: 10 }] + ``` + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>getLineOffsets(<i>line</i>)</code> +: **If the instance refers to a `JSScript`**, return an array of bytecode + instruction offsets representing the entry points to source line + <i>line</i>. If the script contains no executable code at that line, the + array returned is empty. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>getOffsetLocation(<i>offset</i>)</code> +: **If the instance refers to a `JSScript`**, return an object describing the + source code location responsible for the bytecode at <i>offset</i> in this + script. The object has the following properties: + + * lineNumber: the line number for which offset is an entry point + + * columnNumber: the column number for which offset is an entry point + + * isEntryPoint: true if the offset is a column entry point, as + would be reported by getAllColumnOffsets(); otherwise false. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`getOffsetsCoverage()`: +: **If the instance refers to a `JSScript`**, return `null` or an array which + contains informations about the coverage of all opcodes. The elements of + the array are objects, each of which describes a single opcode, and + contains the following properties: + + * lineNumber: the line number of the current opcode. + + * columnNumber: the column number of the current opcode. + + * offset: the bytecode instruction offset of the current opcode. + + * count: the number of times the current opcode got executed. + + If this script has no coverage, or if it is not instrumented, then this + function will return `null`. To ensure that the debuggee is instrumented, + the flag `Debugger.collectCoverageInfo` should be set to `true`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`getChildScripts()` +: **If the instance refers to a `JSScript`**, return a new array whose + elements are Debugger.Script objects for each function + in this script. Only direct children are included; nested + children can be reached by walking the tree. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>setBreakpoint(<i>offset</i>, <i>handler</i>)</code> +: **If the instance refers to a `JSScript`**, set a breakpoint at the + bytecode instruction at <i>offset</i> in this script, reporting hits to + the `hit` method of <i>handler</i>. If <i>offset</i> is not a valid offset + in this script, throw an error. + + When execution reaches the given instruction, SpiderMonkey calls the + `hit` method of <i>handler</i>, passing a [`Debugger.Frame`][frame] + instance representing the currently executing stack frame. The `hit` + method's return value should be a [resumption value][rv], 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 <i>handler</i> 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`][debugger-object] instance to + which this script belongs. Disabling the [`Debugger`][debugger-object] + instance disables this breakpoint; and removing a global from the + [`Debugger`][debugger-object] instance's set of debuggees clears all the + breakpoints belonging to that [`Debugger`][debugger-object] instance in that + global's scripts. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>getBreakpoints([<i>offset</i>])</code> +: **If the instance refers to a `JSScript`**, return an array containing the + handler objects for all the breakpoints set at <i>offset</i> in this + script. If <i>offset</i> is omitted, return the handlers of all + breakpoints set anywhere in this script. If <i>offset</i> is present, but + not a valid offset in this script, throw an error. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>clearBreakpoints(handler, [<i>offset</i>])</code> +: **If the instance refers to a `JSScript`**, remove all breakpoints set in + this [`Debugger`][debugger-object] instance that use <i>handler</i> as + their handler. If <i>offset</i> is given, remove only those breakpoints + set at <i>offset</i> that use <i>handler</i>; if <i>offset</i> 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 <i>handler</i>, they remain in place. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>clearAllBreakpoints([<i>offset</i>])</code> +: **If the instance refers to a `JSScript`**, remove all breakpoints set in + this script. If <i>offset</i> is present, remove all breakpoints set at + that offset in this script; if <i>offset</i> is not a valid bytecode + offset in this script, throw an error. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>isInCatchScope([<i>offset</i>])</code> +: **If the instance refers to a `JSScript`**, this is `true` if this offset + falls within the scope of a try block, and `false` otherwise. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. diff --git a/js/src/doc/Debugger/Debugger.Source.md b/js/src/doc/Debugger/Debugger.Source.md new file mode 100644 index 000000000..6a5535b74 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Source.md @@ -0,0 +1,220 @@ +# Debugger.Source + +A `Debugger.Source` instance represents either a piece of JavaScript source +code or the serialized text of a block of WebAssembly code. The two cases are +distinguished by the latter having its `introductionType` property always +being `"wasm"` and the former having its `introductionType` property never +being `"wasm"`. + +Each [`Debugger`][debugger-object] instance has a separate collection of +`Debugger.Source` instances representing the source code that has been +presented to the system. + +A debugger may place its own properties on `Debugger.Source` instances, +to store metadata about particular pieces of source code. + +## Debugger.Source for JavaScript + +For a `Debugger.Source` instance representing a piece of JavaScript source +code, its properties provide the source code itself as a string, and describe +where it came from. Each [`Debugger.Script`][script] instance refers to the +`Debugger.Source` instance holding the source code from which it was produced. + +If a single piece of source code contains both top-level code and +function definitions, perhaps with nested functions, then the +[`Debugger.Script`][script] instances for those all refer to the same +`Debugger.Source` instance. Each script indicates the substring of the +overall source to which it corresponds. + +A `Debugger.Source` instance may represent only a portion of a larger +source document. For example, an HTML document can contain JavaScript in +multiple `<script>` elements and event handler content attributes. +In this case, there may be either a single `Debugger.Source` instance +for the entire HTML document, with each [`Debugger.Script`][script] referring to +its substring of the document; or there may be a separate +`Debugger.Source` instance for each `<script>` element and +attribute. The choice is left up to the implementation. + +If a given piece of source code is presented to the JavaScript +implementation more than once, with the same origin metadata, the +JavaScript implementation may generate a fresh `Debugger.Source` +instance to represent each presentation, or it may use a single +`Debugger.Source` instance to represent them all. + +## Debugger.Source for WebAssembly + +For a `Debugger.Source` instance representing the serialized text of a block +of WebAssembly code, its properties provide the serialized text as a string. + +Currently only entire modules evaluated via `new WebAssembly.Module` are +represented. SpiderMonkey constructs exactly one `Debugger.Source` for each +underlying WebAssembly module per [`Debugger`][debugger-object] instance. + +Please note at the time of this writing, support for WebAssembly is very +preliminary. Many properties below return placeholder values. + +## Convention + +For descriptions of properties and methods below, if the behavior of the +property or method differs between the instance referring to JavaScript source +or to a block of WebAssembly code, the text will be split into two sections, +headed by "**if the instance refers to JavaScript source**" and "**if the +instance refers to WebAssembly code**", respectively. If the behavior does not +differ, no such emphasized headings will appear. + +## Accessor Properties of the Debugger.Source Prototype Object + +A `Debugger.Source` instance inherits the following accessor properties +from its prototype: + +`text` +: **If the instance refers to JavaScript source**, the JavaScript source + code, as a string. The value satisfies the `Program`, + `FunctionDeclaration`, or `FunctionExpression` productions in the + ECMAScript standard. + + **If the instance refers to WebAssembly code**, the serialized text + representation. The format is yet to be specified in the WebAssembly + standard. Currently, the text is an s-expression based syntax. + +`url` +: **If the instance refers to JavaScript source**, the URL from which this + source was loaded, if this source was loaded from a URL. Otherwise, this + is `undefined`. Source may be loaded from a URL in the following ways: + + * The URL may appear as the `src` attribute of a `<script>` element + in markup text. + + * The URL may be passed to the `Worker` web worker constructor, or the web + worker `importScripts` function. + + * The URL may be the name of a XPCOM JavaScript module or subscript. + + (Note that code passed to `eval`, the `Function` constructor, or a + similar function is <i>not</i> considered to be loaded from a URL; the + `url` accessor on `Debugger.Source` instances for such sources should + return `undefined`.) + + **If the instance refers to WebAssembly code**, the URL of the script that + called `new WebAssembly.Module` with the string `"> wasm"` appended. + +`sourceMapURL` +: **If the instance refers to JavaScript source**, if this source was + produced by a minimizer or translated from some other language, and we + know the URL of a <b>source map</b> document relating the source positions + in this source to the corresponding source positions in the original + source, then this property's value is that URL. Otherwise, this is `null`. + + (On the web, the translator may provide the source map URL in a + specially formatted comment in the JavaScript source code, or via a + header in the HTTP reply that carried the generated JavaScript.) + + This property is writable, so you can change the source map URL by + setting it. All Debugger.Source objects referencing the same + source will see the change. Setting an empty string has no affect + and will not change existing value. + + **If the instance refers to WebAssembly code**, `null`. Attempts to write + to this property throw a `TypeError`. + +`element` +: The [`Debugger.Object`][object] instance referring to the DOM element to which + this source code belongs, if any, or `undefined` if it belongs to no DOM + element. Source belongs to a DOM element in the following cases: + + * Source belongs to a `<script>` element if it is the element's text + content (that is, it is written out as the body of the `<script>` + element in the markup text), or is the source document referenced by its + `src` attribute. + + * Source belongs to a DOM element if it is an event handler content + attribute (that is, if it is written out in the markup text as an + attribute value). + + * Source belongs to a DOM element if it was assigned to one of the + element's event handler IDL attributes as a string. (Note that one may + assign both strings and functions to DOM elements' event handler IDL + attributes. If one assigns a function, that function's script's source + does <i>not</i> belong to the DOM element; the function's definition + must appear elsewhere.) + + (If the sources attached to a DOM element change, the `Debugger.Source` + instances representing superceded code still refer to the DOM element; + this accessor only reflects origins, not current relationships.) + +`elementAttributeName` +: If this source belongs to a DOM element because it is an event handler + content attribute or an event handler IDL attribute, this is the name of + that attribute, a string. Otherwise, this is `undefined`. + +`introductionType` +: **If the instance refers to JavaScript source**, a string indicating how + this source code was introduced into the system. This accessor returns + one of the following values: + + * `"eval"`, for code passed to `eval`. + + * `"Function"`, for code passed to the `Function` constructor. + + * `"Worker"`, for code loaded by calling the Web worker constructor—the + worker's main script. + + * `"importScripts"`, for code by calling `importScripts` in a web worker. + + * `"eventHandler"`, for code assigned to DOM elements' event handler IDL + attributes as a string. + + * `"scriptElement"`, for code belonging to `<script>` elements. + + * `"javascriptURL"`, for code presented in `javascript:` URLs. + + * `"setTimeout"`, for code passed to `setTimeout` as a string. + + * `"setInterval"`, for code passed to `setInterval` as a string. + + * `undefined`, if the implementation doesn't know how the code was + introduced. + + **If the instance refers to WebAssembly code**, `"wasm"`. + +`introductionScript`, `introductionOffset` +: **If the instance refers to JavaScript source**, and if this source was + introduced by calling a function from debuggee code, then + `introductionScript` is the [`Debugger.Script`][script] instance referring + to the script containing that call, and `introductionOffset` is the call's + bytecode offset within that script. Otherwise, these are both `undefined`. + Taken together, these properties indicate the location of the introducing + call. + + For the purposes of these accessors, assignments to accessor properties are + treated as function calls. Thus, setting a DOM element's event handler IDL + attribute by assigning to the corresponding JavaScript property creates a + source whose `introductionScript` and `introductionOffset` refer to the + property assignment. + + Since a `<script>` element parsed from a web page's original HTML was not + introduced by any scripted call, its source's `introductionScript` and + `introductionOffset` accessors both return `undefined`. + + If a `<script>` element was dynamically inserted into a document, then these + accessors refer to the call that actually caused the script to run—usually + the call that made the element part of the document. Thus, they do + <i>not</i> refer to the call that created the element; stored the source as + the element's text child; made the element a child of some uninserted parent + node that was later inserted; or the like. + + Although the main script of a worker thread is introduced by a call to + `Worker` or `SharedWorker`, these accessors always return `undefined` on + such script's sources. A worker's main script source and the call that + created the worker are always in separate threads, but + [`Debugger`][debugger-object] is an inherently single-threaded facility: its + debuggees must all run in the same thread. Since the global that created the + worker is in a different thread, it is guaranteed not to be a debuggee of + the [`Debugger`][debugger-object] instance that owns this source; and thus + the creating call is never "in debuggee code". Relating a worker to its + creator, and other multi-threaded debugging concerns, are out of scope for + [`Debugger`][debugger-object]. + + **If the instance refers to WebAssembly code**, `introductionScript` is + the [`Debugger.Script`][script] instance referring to the same underlying + WebAssembly module. `introductionOffset` is `undefined`. diff --git a/js/src/doc/Debugger/Debugger.md b/js/src/doc/Debugger/Debugger.md new file mode 100644 index 000000000..ebb75d789 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.md @@ -0,0 +1,496 @@ +# The Debugger Object + +When called as a constructor, the `Debugger` object creates a new +`Debugger` instance. + +<code>new Debugger([<i>global</i>, ...])</code> +: Create a debugger object, and apply its [`addDebuggee`][add] method to + each of the given <i>global</i> 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, and the like are currently enabled. It is an + accessor property with a getter and setter: assigning to it enables or + disables this `Debugger` instance; reading it produces true if the + instance is enabled, or false otherwise. This property is initially + `true` in a freshly created `Debugger` 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. + +`allowUnobservedAsmJS` +: A boolean value indicating whether asm.js code running inside this + `Debugger` instance's debuggee globals is invisible to Debugger API + handlers and breakpoints. Setting this to `false` inhibits the + ahead-of-time asm.js compiler and forces asm.js code to run as normal + JavaScript. This is an accessor property with a getter and setter. It is + initially `false` in a freshly created `Debugger` instance. + + Setting this flag to `true` is intended for uses of subsystems of the + Debugger API (e.g, [`Debugger.Source`][source]) for purposes other than + step debugging a target JavaScript program. + +`collectCoverageInfo` +: A boolean value indicating whether code coverage should be enabled inside + each debuggee of this `Debugger` instance. Changing this flag value will + recompile all JIT code to add or remove code coverage + instrumentation. Changing this flag when any frame of the debuggee is + currently active on the stack will produce an exception. + + Setting this to `true` enables code coverage instrumentation, which can be + accessed via the [`Debugger.Script`][script] `getOffsetsCoverage` + function. In some cases, the code coverage might expose information which + pre-date the modification of this flag. Code coverage reports are monotone, + thus one can take a snapshot when the Debugger is enabled, and output the + difference. + + Setting this to `false` prevents this `Debugger` instance from requiring any + code coverage instrumentation, but it does not guarantee that the + instrumentation is not present. + +`uncaughtExceptionHook` +: Either `null` or a function that SpiderMonkey calls when a call to a + debug event handler, breakpoint handler, or similar + function throws some exception, which we refer to as + <i>debugger-exception</i> here. Exceptions thrown in the debugger are + not propagated to debuggee code; instead, SpiderMonkey calls this + function, passing <i>debugger-exception</i> as its sole argument and + the `Debugger` instance as the `this` value. This function should + return a [resumption value][rv], which determines how the debuggee + should continue. + + If the uncaught exception hook itself throws an exception, + <i>uncaught-hook-exception</i>, SpiderMonkey throws a new error object, + <i>confess-to-debuggee-exception</i>, to the debuggee whose message + blames the debugger, and includes textual descriptions of + <i>uncaught-hook-exception</i> and the original + <i>debugger-exception</i>. + + If `uncaughtExceptionHook`'s value is `null`, SpiderMonkey throws an + exception to the debuggee whose message blames the debugger, and + includes a textual description of <i>debugger-exception</i>. + + Assigning anything other than a callable value or `null` to this + property throws a `TypeError` 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 value][rv] 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. + +<code>onNewScript(<i>script</i>, <i>global</i>)</code> +: New code, represented by the [`Debugger.Script`][script] instance + <i>script</i>, has been loaded in the scope of the debuggees. + + This method's return value is ignored. + +<code>onNewPromise(<i>promise</i>)</code> +: A new Promise object, referenced by the [`Debugger.Object`][object] instance + *promise*, has been allocated in the scope of the debuggees. The Promise's + allocation stack can be obtained using the *promiseAllocationStack* + accessor property of the [`Debugger.Object`][object] instance *promise*. + + This handler method should return a [resumption value][rv] specifying how + the debuggee's execution should proceed. However, note that a <code>{ + return: <i>value</i> }</code> resumption value is treated like `undefined` + ("continue normally"); <i>value</i> is ignored. + +<code>onPromiseSettled(<i>promise</i>)</code> +: A Promise object, referenced by the [`Debugger.Object`][object] instance + *promise* that was allocated within a debuggee scope, has settled (either + fulfilled or rejected). The Promise's state, fulfillment or rejection + value, and the allocation and resolution stacks can be obtained using the + Promise-related accessor properties of the [`Debugger.Object`][object] + instance *promise*. + + This handler method should return a [resumption value][rv] specifying how + the debuggee's execution should proceed. However, note that a <code>{ + return: <i>value</i> }</code> resumption value is treated like `undefined` + ("continue normally"); <i>value</i> is ignored. + +<code>onDebuggerStatement(<i>frame</i>)</code> +: Debuggee code has executed a <i>debugger</i> statement in <i>frame</i>. + This method should return a [resumption value][rv] specifying how the + debuggee's execution should proceed. + +<code>onEnterFrame(<i>frame</i>)</code> +: The stack frame <i>frame</i> is about to begin executing code. + (Naturally, <i>frame</i> is currently the youngest + [visible frame][vf].) This method should return + a [resumption value][rv] specifying how the debuggee's execution should + proceed. + + SpiderMonkey only calls `onEnterFrame` to report + [visible][vf], non-`"debugger"` frames. + +<code>onExceptionUnwind(<i>frame</i>, <i>value</i>)</code> +: The exception <i>value</i> has been thrown, and has propagated to + <i>frame</i>; <i>frame</i> is the youngest remaining stack frame, and is a + debuggee frame. This method should return a [resumption value][rv] + specifying how the debuggee's execution should proceed. If it returns + `undefined`, the exception continues to propagate as normal: if control in + `frame` is in a `try` block, control jumps to the corresponding `catch` or + `finally` block; otherwise, <i>frame</i> is popped, and the exception + propagates to <i>frame</i>'s caller. + + When an exception's propagation causes control to enter a `finally` + block, the exception is temporarily set aside. If the `finally` block + finishes normally, the exception resumes propagation, and the debugger's + `onExceptionUnwind` handler is called again, in the same frame. (The + other possibility is for the `finally` block to exit due to a `return`, + `continue`, or `break` statement, or a new exception. In those cases the + old exception does not continue to propagate; it is discarded.) + + This handler is not called when unwinding a frame due to an over-recursion + or out-of-memory exception. + +<code>sourceHandler(<i>ASuffusionOfYellow</i>)</code> +: 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. + +<code>onError(<i>frame</i>, <i>report</i>)</code> +: SpiderMonkey is about to report an error in <i>frame</i>. <i>Report</i> + 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 <i>line</i> 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, <i>global</i>, has been created. + + This handler method should return a [resumption value][rv] specifying how + the debuggee's execution should proceed. However, note that a <code>{ return: + <i>value</i> }</code> resumption value is treated like `undefined` ("continue + normally"); <i>value</i> 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 a + `Debugger`'s reach. However, the `onNewGlobalObject` 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. + +<code id="addDebuggee">addDebuggee(<i>global</i>)</code> +: Add the global object designated by <i>global</i> to the set of global + objects this `Debugger` instance is debugging. If the designated global + is already a debuggee, this has no effect. Return this `Debugger`'s + [`Debugger.Object`][object] instance referring to the designated global. + + The value <i>global</i> may be any of the following: + + * A global object. + + * An HTML5 `WindowProxy` object (an "outer window", in Firefox + terminology), which is treated as if the `Window` 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`][object] instance belonging to this `Debugger` instance; + we apply the prior rules to the referent. + + * Any other sort of value is treated as a `TypeError`. (Note that each + rule is only applied once in the process of resolving a given + <i>global</i> argument. Thus, for example, a [`Debugger.Object`][object] + referring to a second [`Debugger.Object`][object] which refers to a global does + not designate that global for the purposes of this function.) + + The global designated by <i>global</i> 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`][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 the `Debugger`'s set of debuggees. (Naturally, the + [`Debugger.Object`][object] instance this method returns does hold a strong + reference to the added global.) + + If this debugger is [tracking allocation sites][tracking-allocs] and cannot + track allocation sites for <i>global</i>, this method throws an `Error`. + +`addAllGlobalsAsDebuggees()` +: This method is like [`addDebuggee`][add], but adds all the global + objects from all compartments to this `Debugger` instance's set of + debuggees. Note that it skips this debugger's compartment. + + If this debugger is [tracking allocation sites][tracking-allocs] and cannot + track allocation sites for some global, this method throws an `Error`. + Otherwise this method returns `undefined`. + + This 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 a + `Debugger`'s reach. However, the `addAllGlobalsAsDebuggees` 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, `addAllGlobalsAsDebuggees` is only + available to privileged code. + +<code>removeDebuggee(<i>global</i>)</code> +: Remove the global object designated by <i>global</i> from this + `Debugger` instance's set of debuggees. Return `undefined`. + + This method interprets <i>global</i> using the same rules that + [`addDebuggee`][add] does. + + Removing a global as a debuggee from this `Debugger` clears all breakpoints + that belong to that `Debugger` in that global. + +`removeAllDebuggees()` +: Remove all the global objects from this `Debugger` instance's set + of debuggees. Return `undefined`. + +<code>hasDebuggee(<i>global</i>)</code> +: Return `true` if the global object designated by <i>global</i> is a + debuggee of this `Debugger` instance. + + This method interprets <i>global</i> using the same rules that + [`addDebuggee`][add] does. + +`getDebuggees()` +: Return an array of distinct [`Debugger.Object`][object] instances whose referents + are all the global objects this `Debugger` 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`][frame] instance referring to the youngest + [visible frame][vf] currently on the calling thread's stack, or `null` + if there are no visible frames on the stack. + +<code>findSources([<i>query</i>]) <i>(not yet implemented)</i></code> +: Return an array of all [`Debugger.Source`][source] instances matching + <i>query</i>. Each source appears only once in the array. <i>Query</i> + is an object whose properties restrict which sources are returned; a + source must meet all the criteria given by <i>query</i> to be returned. + If <i>query</i> is omitted, we return all sources of all debuggee + scripts. + + <i>Query</i> may have the following properties: + + `url` + : The source's `url` property must be equal to this value. + + `global` + : The source must have been evaluated in the scope of the given global + object. If this property's value is a [`Debugger.Object`][object] instance + belonging to this `Debugger` 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 sources that can no longer ever be + used by the debuggee: say, eval code that has finished running, or + source for unreachable functions. Whether such sources appear can be + affected by the garbage collector's behavior, so this function's result + is not entirely deterministic. + +<code>findScripts([<i>query</i>])</code> +: Return an array of [`Debugger.Script`][script] instances for all debuggee scripts + matching <i>query</i>. Each instance appears only once in the array. + <i>Query</i> is an object whose properties restrict which scripts are + returned; a script must meet all the criteria given by <i>query</i> to + be returned. If <i>query</i> is omitted, we return the [`Debugger.Script`][script] + instances for all debuggee scripts. + + <i>Query</i> may have the following properties: + + `url` + : The script's `url` property must be equal to this value. + + `source` + : The script's `source` 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, the `url` and `line` 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`][object] instance belonging to this + `Debugger` 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. + + All properties of <i>query</i> are optional. Passing an empty object + returns all debuggee code scripts. + + Note that the result may include [`Debugger.Script`][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. + +<code>findObjects([<i>query</i>])</code> +: Return an array of [`Debugger.Object`][object] instances referring to each + live object allocated in the scope of the debuggee globals that matches + *query*. Each instance appears only once in the array. *Query* is an object + whose properties restrict which objects are returned; an object must meet + all the criteria given by *query* to be returned. If *query* is omitted, we + return the [`Debugger.Object`][object] instances for all objects allocated + in the scope of debuggee globals. + + The *query* object may have the following properties: + + `class` + : If present, only return objects whose internal `[[Class]]`'s name + matches the given string. Note that in some cases, the prototype object + for a given constructor has the same `[[Class]]` as the instances that + refer to it, but cannot itself be used as a valid instance of the + class. Code gathering objects by class name may need to examine them + further before trying to use them. + + All properties of *query* are optional. Passing an empty object returns all + objects in debuggee globals. + + Unlike `findScripts`, this function is deterministic and will never return + [`Debugger.Object`s][object] referring to previously unreachable objects + that had not been collected yet. + +<code>clearBreakpoint(<i>handler</i>)</code> +: Remove all breakpoints set in this `Debugger` instance that use + <i>handler</i> as their handler. Note that, if breakpoints using other + handler objects are set at the same location(s) as <i>handler</i>, they + remain in place. + +`clearAllBreakpoints()` +: Remove all breakpoints set using this `Debugger` instance. + +`findAllGlobals()` +: Return an array of [`Debugger.Object`][object] instances referring to all the + global objects present in this JavaScript instance. + + 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`][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's + [`Debugger.Object`][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 a + `Debugger`'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. + +<code>makeGlobalObjectReference(<i>global</i>)</code> +: Return the [`Debugger.Object`][object] whose referent is the global object + designated by <i>global</i>, without adding the designated global as a + debuggee. If <i>global</i> does not designate a global object, throw a + `TypeError`. Determine which global is designated by <i>global</i> + using the same rules as [`Debugger.prototype.addDebuggee`][add]. + +<code>adoptDebuggeeValue(<i>value</i>)</code> +: Given a debuggee value `value` owned by an arbitrary `Debugger`, return an + equivalent debuggee value owned by this `Debugger`. + + If `value` is a primitive value, return it unchanged. If `value` is a + `Debugger.Object` owned by an arbitrary `Debugger`, return an equivalent + `Debugger.Object` owned by this `Debugger`. Otherwise, if `value` is some + other kind of object, and hence not a proper debuggee value, throw a + TypeError instead. + +## Static methods of the Debugger Object + +The functions described below are not called with a `this` value. + +<code id="isCompilableUnit">isCompilableUnit(<i>source</i>)</code> +: Given a string of source code, designated by <i>source</i>, return false if + the string might become a valid JavaScript statement with the addition of + more lines. Otherwise return true. The intent is to support interactive + compilation - accumulate lines in a buffer until isCompilableUnit is true, + then pass it to the compiler. diff --git a/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md b/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md new file mode 100644 index 000000000..d9a92769a --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md @@ -0,0 +1,222 @@ +Tutorial: Show Allocations Per Call Path +======================================== + +{{ gecko_minversion_header(\'34\') }} + +This page shows how to use the [`Debugger` API][debugger] to show how many +objects a web page allocates, sorted by the function call path that allocated +them. + +1) Visit the URL `about:config`, and set the `devtools.chrome.enabled` + preference to `true`: + + ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] + +2) Open a developer Scratchpad (Menu button > Developer > Scratchpad), and + select "Browser" from the "Environment" menu. (This menu will not be + present unless you have changed the preference as explained above.) + + ![Selecting the 'browser' context in the Scratchpad][img-scratchpad-browser] + +3) Enter the following code in the Scratchpad: + + ```language-js + // This simply defines the 'Debugger' constructor in this + // Scratchpad; it doesn't actually start debugging anything. + Components.utils.import('resource://gre/modules/jsdebugger.jsm'); + addDebuggerToGlobal(window); + + (function () { + // The debugger we'll use to observe a tab's allocation. + var dbg; + + // Start measuring the selected tab's main window's memory + // consumption. This function is available in the browser + // console. + window.demoTrackAllocations = function() { + dbg = new Debugger; + + // This makes hacking on the demo *much* more + // pleasant. + dbg.uncaughtExceptionHook = handleUncaughtException; + + // Find the current tab's main content window. + var w = gBrowser.selectedBrowser.contentWindow; + console.log("Tracking allocations in page: " + + w.location.href); + + // Make that window a debuggee of our Debugger. + dbg.addDebuggee(w.wrappedJSObject); + + // Enable allocation tracking in dbg's debuggees. + dbg.memory.trackingAllocationSites = true; + } + + window.demoPlotAllocations = function() { + // Grab the allocation log. + var log = dbg.memory.drainAllocationsLog(); + + // Neutralize the Debugger, and drop it on the floor + // for the GC to collect. + console.log("Stopping allocation tracking."); + dbg.removeAllDebuggees(); + dbg = undefined; + + // Analyze and display the allocation log. + plot(log); + } + + function handleUncaughtException(ex) { + console.log('Debugger hook threw:'); + console.log(ex.toString()); + console.log('Stack:'); + console.log(ex.stack); + }; + + function plot(log) { + // Given the log, compute a map from allocation sites to + // allocation counts. Note that stack entries are '===' if + // they represent the same site with the same callers. + var counts = new Map; + for (let site of log) { + // This is a kludge, necessary for now. The saved stacks + // are new, and Firefox doesn't yet understand that they + // are safe for chrome code to use, so we must tell it + // so explicitly. + site = Components.utils.waiveXrays(site.frame); + + if (!counts.has(site)) + counts.set(site, 0); + counts.set(site, counts.get(site) + 1); + } + + // Walk from each site that allocated something up to the + // root, computing allocation totals that include + // children. Remember that 'null' is a valid site, + // representing the root. + var totals = new Map; + for (let [site, count] of counts) { + for(;;) { + if (!totals.has(site)) + totals.set(site, 0); + totals.set(site, totals.get(site) + count); + if (!site) + break; + site = site.parent; + } + } + + // Compute parent-to-child links, since saved stack frames + // have only parent links. + var rootChildren = new Map; + function childMapFor(site) { + if (!site) + return rootChildren; + + let parentMap = childMapFor(site.parent); + if (parentMap.has(site)) + return parentMap.get(site); + + var m = new Map; + parentMap.set(site, m); + return m; + } + for (let [site, total] of totals) { + childMapFor(site); + } + + // Print the allocation count for |site|. Print + // |children|'s entries as |site|'s child nodes. Indent + // the whole thing by |indent|. + function walk(site, children, indent) { + var name, place; + if (site) { + name = site.functionDisplayName; + place = ' ' + site.source + ':' + site.line + ':' + site.column; + } else { + name = '(root)'; + place = ''; + } + console.log(indent + totals.get(site) + ': ' + name + place); + for (let [child, grandchildren] of children) + walk(child, grandchildren, indent + ' '); + } + walk(null, rootChildren, ''); + } + })(); + ``` + +4) In the Scratchpad, ensure that no text is selected, and press the "Run" + button. (If you get an error complaining that `Components.utils` is not + defined, be sure you've selected `Browser` from the scratchpad's + `Environment` menu, as described in step 2.) + +5) Save the following HTML text to a file, and visit the file in your browser. + Make sure the current browser tab is displaying this page. + + ```language-html + <div onclick="doDivsAndSpans()"> + Click here to make the page do some allocations. + </div> + + <script> + function makeFactory(type) { + return function factory(content) { + var elt = document.createElement(type); + elt.textContent = content; + return elt; + }; + } + + var divFactory = makeFactory('div'); + var spanFactory = makeFactory('span'); + + function divsAndSpans() { + for (i = 0; i < 10; i++) { + var div = divFactory('div #' + i); + div.appendChild(spanFactory('span #' + i)); + document.body.appendChild(div); + } + } + + function doDivsAndSpans() { divsAndSpans(); } + </script> + ``` + +6) Open the browser console (Menu Button > Developer > Browser Console), and + then evaluate the expression `demoTrackAllocations()` in the browser + console. This begins logging allocations in the current browser tab. + +7) In the browser tab, click on the text that says "Click here...". The event + handler should add some text to the end of the page. + +8) Back in the browser console, evaluate the expression + `demoPlotAllocations()`. This stops logging allocations, and displays a tree + of allocations: + + ![An allocation plot, displayed in the console][img-alloc-plot] + + The numbers at the left edge of each line show the total number of objects + allocated at that site or at sites called from there. After the count, we + see the function name, and the source code location of the call site or + allocation. + + The `(root)` node's count includes objects allocated in the content page by + the web browser, like DOM events. Indeed, this display shows that + `popup.xml` and `content.js`, which are internal components of Firefox, + allocated more objects in the page's compartment than the page itself. (We + will probably revise the allocation log to present such allocations in a way + that is more informative, and that exposes less of Firefox's internal + structure.) + + As expected, the `onclick` handler is responsible for all allocation done by + the page's own code. (The line number for the onclick handler is `1`, + indicating that the allocating call is located on line one of the handler + text itself. We will probably change this to be the line number within + `page.html`, not the line number within the handler code.) + + The `onclick` handler calls `doDivsAndSpans`, which calls `divsAndSpans`, + which invokes closures of `factory` to do all the actual allocation. (It is + unclear why `spanFactory` allocated thirteen objects, despite being called + only ten times.) + diff --git a/js/src/doc/Debugger/Tutorial-Debugger-Statement.md b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md new file mode 100644 index 000000000..58360ff6f --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md @@ -0,0 +1,82 @@ +Tutorial: Evaluate an Expression When a debugger; Statement Is Executed +======================================================================= + +This page shows how you can try out the [`Debugger` API][debugger] yourself +using Firefox's Scratchpad. We use the API to evaluate an expression in the web +page whenever it executes a JavaScript `debugger;` statement. + +1) Visit the URL `about:config`, and set the `devtools.chrome.enabled` + preference to `true`: + + ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] + +2) Save the following HTML text to a file, and visit the file in your + browser: + + ```language-html + <div onclick="var x = 'snoo'; debugger;">Click me!</div> + ``` + +3) Open a developer Scratchpad (Menu button > Developer > Scratchpad), and + select "Browser" from the "Environment" menu. (This menu will not be + present unless you have changed the preference as explained above.) + + ![Selecting the 'browser' context in the Scratchpad][img-scratchpad-browser] + +4) Enter the following code in the Scratchpad: + + ```language-js + // This simply defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + Components.utils.import("resource://gre/modules/jsdebugger.jsm"); + addDebuggerToGlobal(window); + + // Create a 'Debugger' instance. + var dbg = new Debugger; + + // Get the current tab's content window, and make it a debuggee. + var w = gBrowser.selectedBrowser.contentWindow.wrappedJSObject; + dbg.addDebuggee(w); + + // When the debuggee executes a 'debugger' statement, evaluate + // the expression 'x' in that stack frame, and show its value. + dbg.onDebuggerStatement = function (frame) { + alert('hit debugger statement; x = ' + frame.eval('x').return); + } + ``` + +5) In the Scratchpad, ensure that no text is selected, and press the "Run" + button. + +6) Now, click on the text that says "Click me!" in the web page. This runs + the `div` element's `onclick` handler. When control reaches the + `debugger;` statement, `Debugger` calls your callback function, passing + a `Debugger.Frame` instance. Your callback function evaluates the + expression `x` in the given stack frame, and displays the alert: + + ![The Debugger callback displaying an alert][img-example-alert] + +7) Press "Run" in the Scratchpad again. Now, clicking on the "Click me!" + text causes *two* alerts to show---one for each `Debugger` + instance. + + Multiple `Debugger` instances can observe the same debuggee. Re-running + the code in the Scratchpad created a fresh `Debugger` instance, added + the same web page as its debuggee, and then registered a fresh + `debugger;` statement handler with the new instance. When you clicked + on the `div` element, both of them ran. This shows how any number of + `Debugger`-based tools can observe a single web page + simultaneously---although, since the order in which their handlers + run is not specified, such tools should probably only observe, and not + influence, the debuggee's behavior. + +8) Close the web page and the Scratchpad. + + Since both the Scratchpad's global object and the debuggee window are + now gone, the `Debugger` instances will be garbage collected, since + they can no longer have any visible effect on Firefox's behavior. The + `Debugger` API tries to interact with garbage collection as + transparently as possible; for example, if both a `Debugger.Object` + instance and its referent are not reachable, they will both be + collected, even while the `Debugger` instance to which the shadow + belonged continues to exist. diff --git a/js/src/doc/Debugger/alloc-plot-console.png b/js/src/doc/Debugger/alloc-plot-console.png Binary files differnew file mode 100644 index 000000000..541172472 --- /dev/null +++ b/js/src/doc/Debugger/alloc-plot-console.png diff --git a/js/src/doc/Debugger/config.sh b/js/src/doc/Debugger/config.sh new file mode 100644 index 000000000..eda4ed8d7 --- /dev/null +++ b/js/src/doc/Debugger/config.sh @@ -0,0 +1,66 @@ +### Description of Debugger docs: how to format, where to install. +### See js/src/doc/README.md for a description. + +base-url https://developer.mozilla.org/en-US/docs/Tools/ + +markdown Debugger-API.md Debugger-API + label 'debugger' "The Debugger API" + +markdown Conventions.md Debugger-API/Conventions + label 'conventions' "Debugger API: General Conventions" + label 'dbg code' '#debuggee-code' "Debugger API: General Conventions: Debuggee Code" + label 'cv' '#completion-values' "Debugger API: General Conventions: Completion Values" + label 'rv' '#resumption-values' "Debugger API: General Conventions: Resumption Values" + label 'timestamps' '#timestamps' "Debugger API: General Conventions: Timestamps" + label 'wouldrun' '#the-debugger.debuggeewouldrun-exception' "Debugger API: DebuggeeWouldRun" + +markdown Debugger.md Debugger-API/Debugger + label 'debugger-object' "The Debugger object" + label 'add' '#addDebuggee' "The Debugger object: addDebuggee" + +markdown Debugger.Environment.md Debugger-API/Debugger.Environment + label 'environment' "Debugger.Environment" + +markdown Debugger.Frame.md Debugger-API/Debugger.Frame + label 'frame' "Debugger.Frame" + label 'vf' '#visible-frames' "Debugger.Frame: Visible Frames" + label 'inv fr' '#invf' "Debugger.Frame: Invocation Frames" + label 'fr eval' '#eval' "Debugger.Frame: Eval" + +markdown Debugger.Object.md Debugger-API/Debugger.Object + label 'object' "Debugger.Object" + label 'allocation-site' '#allocationsite' "Debugger.Object: allocationSite" + +markdown Debugger.Script.md Debugger-API/Debugger.Script + label 'script' "Debugger.Script" + +markdown Debugger.Source.md Debugger-API/Debugger.Source + label 'source' "Debugger.Source" + +markdown Debugger.Memory.md Debugger-API/Debugger.Memory + label 'memory' "Debugger.Memory" + label 'tracking-allocs' '#trackingallocationsites' "Debugger.Memory: trackingAllocationSites" + label 'drain-alloc-log' '#drain-alloc-log' "Debugger.Memory: drainAllocationsLog" + label 'max-alloc-log' '#max-alloc-log' "Debugger.Memory: maxAllocationsLogLength" + label 'alloc-sampling-probability' '#alloc-sampling-probability' "Debugger.Memory: allocationSamplingProbability" + label 'take-census' '#take-census' "Debugger.Memory: takeCensus" + +markdown Tutorial-Debugger-Statement.md Debugger-API/Tutorial-Debugger-Statement + label 'tut debugger' "Tutorial: the debugger; statement" + +markdown Tutorial-Alloc-Log-Tree.md Debugger-API/Tutorial-Allocation-Log-Tree + label 'tut alloc log' "Tutorial: the allocation log" + +# Images: +RBASE=https://mdn.mozillademos.org/files +resource 'img-shadows' shadows.svg $RBASE/7225/shadows.svg +resource 'img-chrome-pref' enable-chrome-devtools.png $RBASE/7233/enable-chrome-devtools.png +resource 'img-scratchpad-browser' scratchpad-browser-environment.png $RBASE/7229/scratchpad-browser-environment.png +resource 'img-example-alert' debugger-alert.png $RBASE/7231/debugger-alert.png +resource 'img-alloc-plot' alloc-plot-console.png $RBASE/8461/alloc-plot-console.png + +# External links: +absolute-label 'protocol' https://wiki.mozilla.org/Remote_Debugging_Protocol "Remote Debugging Protocol" +absolute-label 'saved-frame' https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/SavedFrame "SavedFrame" +absolute-label 'bernoulli-trial' https://en.wikipedia.org/wiki/Bernoulli_trial "Bernoulli Trial" +absolute-label 'promise' https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise" diff --git a/js/src/doc/Debugger/debugger-alert.png b/js/src/doc/Debugger/debugger-alert.png Binary files differnew file mode 100644 index 000000000..2bf936224 --- /dev/null +++ b/js/src/doc/Debugger/debugger-alert.png diff --git a/js/src/doc/Debugger/enable-chrome-devtools.png b/js/src/doc/Debugger/enable-chrome-devtools.png Binary files differnew file mode 100644 index 000000000..033468991 --- /dev/null +++ b/js/src/doc/Debugger/enable-chrome-devtools.png diff --git a/js/src/doc/Debugger/scratchpad-browser-environment.png b/js/src/doc/Debugger/scratchpad-browser-environment.png Binary files differnew file mode 100644 index 000000000..534d0f950 --- /dev/null +++ b/js/src/doc/Debugger/scratchpad-browser-environment.png diff --git a/js/src/doc/Debugger/shadows.svg b/js/src/doc/Debugger/shadows.svg new file mode 100644 index 000000000..5774fc78f --- /dev/null +++ b/js/src/doc/Debugger/shadows.svg @@ -0,0 +1,997 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="650" + height="400" + id="svg10302" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="shadows.svg" + viewBox="0 0 650 400"> + <defs + id="defs10304"> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-0" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-3" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-2" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-6" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-6" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-3" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-9" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-4" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-4" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-79" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-8" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-11" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-9" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-0" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-0" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-96" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-4" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-72" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-1" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-8" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-67" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-5" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-78" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-89" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-6" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-5" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-3" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-5" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-18" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-35" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-2" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.6369231" + inkscape:cx="338.70355" + inkscape:cy="200" + inkscape:document-units="px" + inkscape:current-layer="alertLater" + showgrid="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1920" + inkscape:window-height="1021" + inkscape:window-x="0" + inkscape:window-y="867" + inkscape:window-maximized="1" /> + <metadata + id="metadata10307"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="debuggeeset" + inkscape:label="debuggeeset" + style="display:inline" + class="flip"> + <rect + style="fill:#c8c0c0;fill-opacity:1;stroke:#000000;stroke-width:2.18226266;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.36452564, 4.36452564;stroke-dashoffset:0;display:inline" + id="rect3755-3-0-6-1-8-6" + width="171.31711" + height="150.68491" + x="0.10979491" + y="248.55035" + ry="4.814189" + rx="4.0350175" /> + <text + xml:space="preserve" + style="font-size:14.11999989px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="9.8749857" + y="268.84415" + id="text22690" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan22692" + x="9.8749857" + y="268.84415">Debugger</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="shadows" + inkscape:label="shadows" + style="display:inline" + class="flip"> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-68" + width="119.42149" + height="42.397526" + x="25.236485" + y="160.0201" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-8" + width="177.65483" + height="46.614746" + x="209.36084" + y="336.41965" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-43" + width="149.12187" + height="46.614746" + x="237.8938" + y="287.1636" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-6-1" + width="119.42149" + height="42.397526" + x="192.17702" + y="161.09923" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-2-4" + width="136.4054" + height="46.614731" + x="26.006214" + y="336.41965" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-1-5-9" + width="242.77243" + height="75.739441" + x="183.31783" + y="47.05508" + ry="6.1249413" + rx="6.1249394" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:#ffffff;stroke-width:2.92859435;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + id="rect3755-1-5-1-7-2" + width="186.27605" + height="18.674528" + x="285.59784" + y="66.98394" + ry="6.1249413" + rx="6.1249399" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-0" + width="194.03011" + height="46.614746" + x="413.20236" + y="336.41989" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-4-6" + width="90.035484" + height="46.614746" + x="296.98013" + y="237.91023" + ry="4.0393105" + rx="4.0393105" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="33.257587" + y="378.20139" + id="text4037" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4039" + x="33.257587" + y="378.20139">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="218.72287" + y="377.52206" + id="text4041" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4043" + x="218.72287" + y="377.52206">Debugger.Environment</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="421.84915" + y="377.52206" + id="text4045" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4047" + x="421.84915" + y="377.52206">Debugger.Frame</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="29.181461" + y="196.81334" + id="text4049" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4051" + x="29.181461" + y="196.81334">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="199.2216" + y="197.89247" + id="text4053" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4055" + x="199.2216" + y="197.89247">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="190.1888" + y="118.00808" + id="text4057" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4059" + x="190.1888" + y="118.00808">Debugger.Script</tspan></text> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-5-8" + width="194.03011" + height="46.614746" + x="413.20236" + y="287.1636" + ry="4.0393105" + rx="4.0393105" /> + </g> + <g + inkscape:groupmode="layer" + id="global" + inkscape:label="global" + transform="translate(0,-344.09448)" + class="flip" + style="display:inline"> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1" + width="177.9265" + height="46.614887" + x="201.82011" + y="664.15881" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="210.6344" + y="691.88666" + id="text10970" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan10972" + x="210.6344" + y="691.88666">global environment</tspan></text> + <rect + style="fill:#83d4ff;fill-opacity:1;stroke:none" + id="rect3755-5-2" + width="136.61398" + height="46.614872" + x="18.810663" + y="664.15881" + ry="11.065418" + rx="11.065418" /> + <path + style="fill:none;stroke:#000000;stroke-width:2.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8)" + d="m 204.0527,687.46628 -46.26685,0" + id="path11163-2" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="3.020442" + y="659.44391" + id="text15699" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan15701" + x="3.020442" + y="659.44391">global object:</tspan></text> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="87.051254" + y="698.41443" + id="text15703" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + x="87.051254" + y="698.41443" + id="tspan15707">Date; Math; ...</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="scripts" + inkscape:label="scripts" + style="display:inline" + class="flip"> + <rect + transform="translate(0,-652.36218)" + style="fill:#83ff9a;fill-opacity:1;stroke:none;display:inline" + id="rect3755-1-5" + width="243.14369" + height="75.73967" + x="176.36284" + y="683.0611" + ry="6.1249409" + rx="6.1249394" + class="nonvalue" /> + <rect + transform="translate(0,-652.36218)" + style="fill:#83ff9a;fill-opacity:1;stroke:#e6e4dd;stroke-width:2.92859435000000001;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + id="rect3755-1-5-1-7" + width="186.56091" + height="18.674585" + x="278.79929" + y="702.98999" + ry="6.1249409" + rx="6.1249399" + class="nonvalue" /> + </g> + <g + inkscape:groupmode="layer" + id="code" + inkscape:label="code" + class="flip" + style="display:inline"> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="182.99957" + y="699.52692" + id="text10314" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923777,0,-652.36218)"><tspan + sodipodi:role="line" + x="182.99957" + y="699.52692" + id="tspan4518">function alertLater(msg, delay) {</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="717.1795" + id="tspan4527"> setTimeout( function () { alert(msg); },</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="734.83215" + id="tspan4529"> delay);</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="752.48474" + id="tspan4531">}</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="alertLater" + inkscape:label="alertLater" + style="display:inline" + class="flip"> + <rect + transform="translate(0,-652.36218)" + style="fill:#83d4ff;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5" + width="119.60412" + height="42.397655" + x="18.039759" + y="796.02643" + ry="11.065418" + rx="11.065418" /> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="98.427589" + y="811.1579" + id="text3761-6" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan3763-0" + x="98.427589" + y="811.1579">[[Code]]:</tspan></text> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="98.427589" + y="831.64917" + id="text3765-0" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan3767-2" + x="98.427589" + y="831.64917">[[Scope]]:</tspan></text> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#e6e4dd;stroke-width:2.95684338000000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + d="m 18.039736,817.89404 119.601824,0" + id="path3773-4-3" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="2.4066608" + y="791.50812" + id="text10404" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan10406" + x="2.4066608" + y="791.50812">alertLater:</tspan></text> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 113.60688,806.63271 c 0,-45.02374 23.466,-83.54273 59.2075,-83.54273" + id="path3868-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 113.60688,827.22105 c 51.73315,0 90.50566,99.84829 90.50566,141.32477" + id="path3868-4-0" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="87.051254" + y="991.61841" + id="text15703-4" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + x="89.299202" + y="991.61841" + id="tspan15707-0">alertLater; </tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="call1env" + inkscape:label="call1env" + style="display:inline" + class="flip"> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3" + width="149.34991" + height="46.614887" + x="229.7888" + y="270.80811" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <g + style="display:inline" + id="g3550" + transform="matrix(0.58886915,0,0,0.58797179,4.5262952,-176.54364)"> + <text + transform="scale(1.0009935,0.9990075)" + sodipodi:linespacing="125%" + id="text13153" + y="795.71594" + x="499.133" + style="font-size:22px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="795.71594" + x="499.133" + id="tspan13155" + sodipodi:role="line">msg:</tspan><tspan + y="823.21594" + x="499.133" + sodipodi:role="line" + id="tspan3538">delay:</tspan></text> + <text + sodipodi:linespacing="125%" + id="text3540" + y="794.92615" + x="507" + style="font-size:22px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="794.92615" + x="507" + id="tspan3542" + sodipodi:role="line">'xlerb'</tspan><tspan + id="tspan3544" + y="822.42615" + x="507" + sodipodi:role="line">1000</tspan></text> + </g> + </g> + <g + inkscape:groupmode="layer" + id="anon" + inkscape:label="anon" + style="display:inline" + class="flip"> + <rect + style="fill:#83d4ff;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-6" + width="119.60412" + height="42.397655" + x="185.84294" + y="143.66425" + ry="11.065418" + rx="11.065418" /> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="266.10284" + y="158.2981" + id="text3761-6-0" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan3763-0-7" + x="266.10284" + y="158.2981">[[Code]]:</tspan></text> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="266.10284" + y="178.78937" + id="text3765-0-7" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan3767-2-6" + x="266.10284" + y="178.78937">[[Scope]]:</tspan></text> + <path + style="fill:none;stroke:#e6e4dd;stroke-width:2.95684338000000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + d="m 185.84293,165.53187 119.60182,0" + id="path3773-4-3-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 285.44496,176.59077 c 0,31.25836 -38.08897,59.76616 -38.08897,91.05227" + id="path3868-4-0-4-7" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 285.95088,154.47813 c 165.68371,0 170.77148,-32.93057 158.20494,-82.18402" + id="path3868-4-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> + <g + inkscape:groupmode="layer" + id="fire" + inkscape:label="fire" + style="display:inline" + class="flip"> + <rect + style="fill:#ff9457;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-4" + width="194.03011" + height="46.614746" + x="406.38477" + y="320.06448" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="434.10962" + y="346.93411" + id="text10970-7-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + x="434.10962" + y="346.93411" + id="tspan3534-4"><tspan + style="font-style:italic;-inkscape-font-specification:Sans Italic" + id="tspan5754">anonymous</tspan>()</tspan></text> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-4" + width="90.035484" + height="46.614746" + x="289.10324" + y="221.55482" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:italic;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans;-inkscape-font-specification:Sans Italic" + x="312.99686" + y="248.05814" + id="text5774" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5776" + x="312.99686" + y="248.05814">empty</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8);display:inline" + d="m 415.85979,342.35006 c -51.09645,0 11.10573,-95.59507 -39.0289,-95.59507" + id="path11163-1" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8);display:inline" + d="m 588.68444,343.33325 c 66.19678,0 27.60009,-283.14206 -117.22439,-283.14206" + id="path11163-1-6" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> + <g + inkscape:groupmode="layer" + id="call2" + inkscape:label="call2" + class="flip" + style="display:inline"> + <rect + style="fill:#ff9457;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-5-4" + width="194.03011" + height="46.614746" + x="406.38477" + y="270.80817" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="433.86328" + y="298.87103" + id="text6389" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan6391" + x="433.86328" + y="298.87103">alert('xlerb')</tspan></text> + </g> +</svg> |