diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/doc/Debugger | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
19 files changed, 5123 insertions, 0 deletions
diff --git a/js/src/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md new file mode 100644 index 0000000000..5d89eb7343 --- /dev/null +++ b/js/src/doc/Debugger/Conventions.md @@ -0,0 +1,254 @@ +# 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 + +The `Debugger` API often needs to convey the result of running some JS code. For example, suppose you get a `frame.onPop` callback telling you that a method in the debuggee just finished. Did it return successfully? Did it throw? What did it return? The debugger passes the `onPop` handler a *completion value* that tells what happened. + +A completion value is one of these: + +* `{ return: value }` + + The code completed normally, returning <i>value</i>. <i>Value</i> is a + debuggee value. + +* `{ throw: value, stack: stack }` + + The code threw <i>value</i> as an exception. <i>Value</i> is a debuggee + value. <i>stack</i> is a `SavedFrame` representing the location from which + the value was thrown, and may be missing. + +* `null` + + The code was terminated, as if by the "slow script" ribbon. + +Generators and async functions add a wrinkle: they can suspend themselves (with `yield` or `await`), which removes their frame from the stack. Later, the generator or async frame might be returned to the stack and continue running where it left off. Does it count as "completion" when a generator suspends itself? + +The `Debugger` API says yes. `yield` and `await` do trigger the `frame.onPop` handler, passing a completion value that explains why the frame is being suspended. The completion value gets an extra `.yield` or `.await` property, to distinguish this kind of completion from a normal `return`. + +```js +{ return: value, yield: true } +``` + +where *value* is a debuggee value for the iterator result object, like `{ value: 1, done: false }`, for the yield. + +When a generator function is called, it first evaluates any default argument +expressions and destructures its arguments. Then its frame is suspended, and the +new generator object is returned to the caller. This initial suspension is reported +to any `onPop` handlers as a completion value of the form: + +```js +{ return: generatorObject, yield: true, initial: true } +``` + +where *generatorObject* is a debuggee value for the generator object being +returned to the caller. + +When an async function awaits a promise, its suspension is reported to any +`onPop` handlers as a completion value of the form: + +```js +{ return: promise, await: true } +``` + +where *promise* is a debuggee value for the promise being returned to the +caller. + +The first time a call to an async function awaits, returns, or throws, a promise +of its result is returned to the caller. Subsequent resumptions of the async +call, if any, are initiated directly from the job queue's event loop, with no +calling frame on the stack. Thus, if needed, an `onPop` handler can distinguish +an async call's initial suspension, which returns the promise, from any +subsequent suspensions by checking the `Debugger.Frame`'s `older` property: if +that is `null`, the call was resumed directly from the event loop. + +Async generators are a combination of async functions and generators that can +use both `yield` and `await` expressions. Suspensions of async generator frames +are reported using any combination of the completion values above. + + +## Resumption Values + +As the debuggee runs, the `Debugger` interface calls various +debugger-provided handler functions to report the debuggee's behavior. +Some of these calls can return a value indicating how the debuggee's +execution should continue; these are called *resumption values*. A +resumption value has one of the following forms: + +* `undefined` + + The debuggee should continue execution normally. + +* `{ return: value }` + + Force the top frame of the debuggee to return <i>value</i> immediately, + as if by executing a `return` statement. <i>Value</i> must be a debuggee + value. (Most handler functions support this, except those whose + descriptions say otherwise.) See the list of special cases below. + +* `{ throw: value }` + + Throw <i>value</i> as an exception from the current bytecode + instruction. <i>Value</i> must be a debuggee value. Note that unlike + completion values, resumption values do not specify a stack. When + initiating an exceptional return from a handler, the current debuggee stack + will be used. If a handler wants to avoid modifying the stack of an + already-thrown exception, it should return `undefined`. + +* `null` + + Terminate the debuggee, as if it had been cancelled by the "slow script" + dialog box. + +In some places, the JS language treats `return` statements specially or +doesn't allow them at all. So there are a few special cases. + +* An arrow function without curly braces can't contain a return + statement, but `{ return: value }` works anyway, + returning the specified value. + + Likewise, if the top frame of the debuggee is not in a function at + all—that is, it's running toplevel code in a `script` tag, or `eval` + code—then <i>value</i> is returned even though `return` statements + aren't legal in that kind of code. (In the case of a `script` tag, + the browser discards the return value.) + +* If the debuggee is in a function that 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`. + +* Returning from a generator simulates a `return`, not a `yield`; + there is no way to force a debuggee generator to `yield`. + + The way generators execute is rather odd. When a generator-function + is first called, it is put onto the stack and runs just a few + bytecode instructions (or more, if the generator-function has any + default argument values to compute), then performs the "initial + suspend". At that point, a new generator object is created and + returned to the caller. Thereafter, the caller may cause execution + of the generator to resume at any time, by calling `genObj.next()`, + and the generator may pause itself again using `yield`. + + JS generators normally can't return before the "initial + suspend"—there’s no place to put a `return` statement—but + `{ return: value }` there works anyway, replacing + the generator object that the initial suspend would normally create + and return. + + Returning from a generator that's been resumed via `genobj.next()` + (or one of the other methods) closes the generator, and the + `genobj.next()` or other method returns a new object of the form + `{ done: true, value: value }`. + +If a debugger hook function throws an exception, rather than returning a +resumption value, 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: + +* `"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. + + +[debugger]: Debugger-API.md +[inv fr]: Debugger.Frame.md#invocation-functions-and-debugger-frames diff --git a/js/src/doc/Debugger/Debugger-API.md b/js/src/doc/Debugger/Debugger-API.md new file mode 100644 index 0000000000..79f36c0e22 --- /dev/null +++ b/js/src/doc/Debugger/Debugger-API.md @@ -0,0 +1,129 @@ +# 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: + +- [Setting a breakpoint][tut breakpoint] in a page, running a handler function + when it is hit that evaluates an expression in the page's context. + +- [Showing how many objects different call paths allocate.][tut alloc log] + +[object]: Debugger.Object.md +[protocol]: https://wiki.mozilla.org/Remote_Debugging_Protocol +[img-shadows]: shadows.svg +[script]: Debugger.Script.md +[frame]: Debugger.Frame.md +[environment]: Debugger.Environment.md +[debugger-object]: Debugger.md +[source]: Debugger.Source.md +[memory]: Debugger.Memory.md +[conventions]: Conventions.md +[tut breakpoint]: Tutorial-Breakpoint.md +[tut alloc log]: Tutorial-Alloc-Log-Tree.md diff --git a/js/src/doc/Debugger/Debugger.Environment.md b/js/src/doc/Debugger/Debugger.Environment.md new file mode 100644 index 0000000000..eef4d35818 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Environment.md @@ -0,0 +1,182 @@ +# 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. + +### `scopeKind` +If this is a declarative environment, a string describing the kind of scope +which this environment is associated with, or `null` for other types of +environments. There is an assortment of possible scope kinds which can be +generated, with a selection of possible values below. Unlike the type +accessor, the categorization this performs is specific to SpiderMonkey's +implementation, and not derived from distinctions made in the ECMAScript +language specification. + +* "function", indicating the top level body scope of a function for +arguments and 'var' variables. + +* "function lexical", indicating the top level lexical scope in a function. + +### `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. + +### `calleeScript` +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.Script`][script] instance referring to <i>f</i>'s script. 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. + +### `getVariable(name)` + +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, in most cases. + +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`. + +Aside from the above case, this method can return something that is not a +debuggee value in two other cases. If a function argument is missing, then it +returns an ordinary JavaScript object whose `missingArgument` property is +`true`. Finally, if a variable name is bound in the environment but not yet +initialized (for example, if the debuggee is paused in the middle of an +initializer expression) then it returns an ordinary JavaScript object whose +`uninitialized` 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. + +### `setVariable(name, value)` +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. + +### `find(name)` +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. + + +[frame]: Debugger.Frame.md +[object]: Debugger.Object.md +[debugger-object]: Debugger.md +[inv fr]: Debugger.Frame.html#invocation-functions-and-debugger-frames +[wouldrun]: Conventions.html#the-debugger-debuggeewouldrun-exception diff --git a/js/src/doc/Debugger/Debugger.Frame.md b/js/src/doc/Debugger/Debugger.Frame.md new file mode 100644 index 0000000000..32ae5027de --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Frame.md @@ -0,0 +1,489 @@ +# 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 `onStack` property +becomes `false`, and accessing its many 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.) + + +## Invocation 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. + + +## Suspended Frames + +Some frames can be *suspended*. + +When a generator `yield`s a value, or when an async function `await`s a +value, the current frame is suspended and removed from the stack, and +other JS code has a chance to run. Later (if the `await`ed promise +becomes resolved, for example), SpiderMonkey will *resume* the frame. It +will be put back onto the stack, and execution will continue where it +left off. Only generator and async function call frames can be suspended +and resumed. + +Currently, a frame's `onStack` property is `false` while it's suspended +([bug 1448880](https://bugzilla.mozilla.org/show_bug.cgi?id=1448880)). + +SpiderMonkey uses the same `Debugger.Frame` object each time a generator +or async function call is put back onto the stack. This means that the +`onStep` handler can be used to step over `yield` and `await`. + +The `frame.onPop` handler is called each time a frame is suspended, and +the `Debugger.onEnterFrame` handler is called each time a frame is +resumed. (This means these events can fire multiple times for the same +`Frame` object, which is odd, but accurately conveys what's happening.) + +The [completion value][cv] passed to the `frame.onPop` handler for a suspension +contains additional properties to clarify what's going on. See the documentation +for completion values for details. + + +## Stepping Into Generators: The "Initial Yield" + +When a debuggee generator is called, something weird happens. The +`.onEnterFrame` hook fires, as though we're stepping into the generator. +But the code inside the generator doesn't run. Instead it immediately +returns. Then we sometimes get *another* `.onEnterFrame` event for the +same generator. What's going on? + +To explain this, we first have to describe how generator calls work, +according to the ECMAScript language specification. Note that except for +step 3, it's exactly like a regular function call. + +1. An "execution context" (what we call a `Frame`) is pushed to the stack. +2. An environment is created (for arguments and local variables). + Argument-default-value-expressions, if any, are evaluated. +3. A generator object is created, initially suspended at the start of the generator body. +4. The stack frame is popped, and the generator object is returned to the caller. + +The JavaScript engine actually carries out these steps, in this order. +So when a debuggee generator is called, here's what you'll observe: + +1. The `debugger.onEnterFrame` hook fires. +2. The debugger can step through the argument-default-value code, if any. +3. The body of the generator does not run yet. Instead, a generator object + is created and suspended (which does not fire any debugger events). +4. The `frame.onPop` hook fires, with a completion value of + `{return:` *(the new generator object)* `}`. + +In SpiderMonkey, this process of suspending and returning a new +generator object is called the "initial yield". + +If the caller then uses the generator's `.next()` method, which may or +may not happen right away depending on the debuggee code, the suspended +generator will be resumed, firing `.onEnterFrame` again. + +## 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. + +* `"wasmcall"`: a frame running a WebAssembly function call. + +* `"debugger"`: a frame for a call to user code invoked by the debugger + (see the `eval` method below). + +Accessing this property will throw if `.terminated == true`. + +### `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. + +* `"wasm"`: a frame running in WebAssembly baseline JIT. + +Accessing this property will throw if `.onStack == false`. + +### `this` +The value of `this` for this frame (a debuggee value). For a `wasmcall` +frame, this property throws a `TypeError`. + +Accessing this property will throw if `.terminated == true`. + +### `older` +The `Debugger.Frame` for the next-older visible frame, in which control will +resume when this frame completes. If there is no older frame, this is `null`. +If there an explicitly inserted asynchronous stack trace above this frame, +this is `null`, since the explicit saved frame takes priority. +If this frame is a suspended generator or async call, this will also be `null`. + +Accessing this property will throw if `.terminated == true`. + +### `olderSavedFrame` + +If this frame has no `older` frame, this field may hold a [`SavedFrame`][saved-frame] +object representing the saved asynchronous stack that triggered execution of +this `Debugger.Frame` instance. + +Accessing this property will throw if `.terminated == true`. + +### `onStack` +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. +Note that this property may be accessed regardless of what state the frame is +in, so it can be used to verify whether it is safe to access other properties +that require an on-stack frame. + +### `terminated` +True if the frame this `Debugger.Frame` instance refers to will never run +again; false if it is on-stack or is a suspended generator/async call that +may be resumed later. Note that this property may be accessed regardless of +what state the frame is in, so it can be used to verify whether it is safe to +access other properties that require a non-terminated frame. + +### `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`. + +Accessing this property will throw if `.terminated == true`. + +### `offset` +The offset of the bytecode instruction currently being executed in +`script`, or `undefined` if the frame's `script` property is `null`. +For a `wasmcall` frame, this property throws a `TypeError`. + +If this is used on a suspended function frame, the offset will reference +the offset where the frame will be resumed. + +Accessing this property will throw if `.terminated == true`. + +### `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. + +Accessing this property will throw if `.terminated == true`. + +### `callee` +The function whose application created this frame, as a debuggee value, +or `null` if this is not a `"call"` frame. + +Accessing this property will throw if `.terminated == true`. + +### `constructing` +True if this frame is for a function called as a constructor, false +otherwise. + +Accessing this property will throw if `.terminated == true`. + +### `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. + +Accessing this property will throw if `.onStack == false`. + +### `asyncPromise` + +If the frame is not an async (generator) function, this will be `undefined`. + +For async functions, this will be a [`Debugger.Object`][object] whose referent +is the promise for async function call's return value. Note that this +property will be `null` if the value is accessed during `onEnterFrame`, +since the promise doesn't exist yet at that point. + +For async generator functions, this will be a [`Debugger.Object`][object] +whose referent is the promise for the current iteration's "value"+"done" object, +which will be resolved when the generator next throws/yields/returns. +Note that this will be `null` if the value is accessed during the initial +generator `onEnterFrame`/`onPop` (before the first `.next` call), +since there is no promise yet at that point. + +Accessing this property will throw if `.terminated == true`. + + +## 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. + +Accessing and reassigning this property is allowed independent of whether +or not the frame is currently on-stack/suspended/terminated. + +### `onPop` +This property must be either `undefined` or a function. If it is a function, +SpiderMonkey calls it just before this frame is popped or suspended, passing +a [completion value][cv] indicating the reason, 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. In generator or async function frames, +the location may be a `yield` or `await` expression. + +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. + +The `onPop` handler is typically called only once for a given +`Debugger.Frame`, after which the frame becomes inactive. However, in the +case of [generators and async functions](#suspended-frames), `onPop` fires +each time the frame is suspended. + +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. + +Accessing and reassigning this property is allowed independent of whether +or not the frame is currently on-stack/suspended/terminated. + + +## Function Properties of the Debugger.Frame Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger.Frame` instance; they may not be used as +methods of other kinds of objects. + +### `eval(code, [options])` +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` or `type` property +is `wasmcall`, 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: + +* `url` + + The filename or URL to which we should attribute <i>code</i>. If this + property is omitted, the URL defaults to `"debugger eval code"`. + +* `lineNumber` + + The line number at which the evaluated code should be claimed to begin + within <i>url</i>. + +Accessing this property will throw if `.onStack == false`. + +### `evalWithBindings(code, bindings, [options])` +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. +Also like `eval`, if this frame's `environment` property is `null` or +`type` property is `wasmcall`, throw a `TypeError`. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +Accessing this property will throw if `.onStack == false`. + + +[vf]: #visible-frames +[debugger-object]: Debugger.md +[object]: Debugger.Object.md +[dbg code]: Conventions.md#debuggee-code +[inv fr]: #invocation-functions-and-debugger-frames +[cv]: Conventions.md#completion-values +[script]: Debugger.Script.md +[environment]: Debugger.Environment.md +[rv]: Conventions.md#resumption-values +[fr eval]: #eval-code-options +[saved-frame]: ../SavedFrame/index diff --git a/js/src/doc/Debugger/Debugger.Memory.md b/js/src/doc/Debugger/Debugger.Memory.md new file mode 100644 index 0000000000..c20354ef3c --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Memory.md @@ -0,0 +1,575 @@ +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 `dbg.memory` control how <i>dbg</i> +observes its debuggees' memory use. The `dbg.memory` object is +an instance of `Debugger.Memory`; its inherited accessors 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: + +## `trackingAllocationSites` +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. + +## `allocationSamplingProbability` +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. + +## `maxAllocationsLogLength` +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`. + +## `allocationsLogOverflowed` +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. + +## `onGarbageCollection(statistics)` +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: + +``` +{ + "startTimestamp": timestamp, + "endTimestamp": timestamp, +} +``` + +Here the `timestamp` values are [timestamps][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_FINISHED"` +* `"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"` +* `"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 +------------------------------------------------------------- + +## `drainAllocationsLog()` +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: + +``` +{ + "timestamp": timestamp, + "frame": allocationSite, + "class": className, + "size": byteSize, + "inNursery": inNursery, +} +``` + +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". + +* `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`. + +## `takeCensus(options)` +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, +non-string, non-script items by their C++ type name, and DOM nodes with +their node name: + + { + by: "coarseType", + objects: { by: "objectClass" }, + other: { by: "internalType" }, + domNode: { by: "descriptiveType" } + } + +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 } + }, + "domNode": { + "#text": { "count": 1, "bytes": 12 } + } + } + +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": n, "bytes": b } + + 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>, domNode:<i>domNode</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>domNode</i> for DOM nodes. + + 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: + + ``` + { + "objects": result, + "scripts": result, + "strings": result, + "domNode:" result, + "other": result, + } + ``` + + 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" }`. + +* `{ by: "internalType", then: breakdown }` + + 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: + +```js +{ + by: "coarseType", + objects: { by: "objectClass" }, + domNode: { by: "descriptiveType" }, + other: { by: "internalType" } +} +``` + +which produces results of the form: + +``` +{ + objects: { class: count, ... }, + scripts: count, + strings: count, + domNode: { node name:count, ... }, + other: { type name:count, ... } +} +``` + +where each `count` has the form: + +```js +{ "count": count, bytes: bytes } +``` + +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's just-in-time compilers generate inline caches for +type specialization. This information is dropped periodically to reduce memory +usage. + +In a census, all the various forms of JavaScript code are placed in the +`"scripts"` category. + + +[debugger]: Debugger-API.md +[debugger-object]: Debugger.md +[tracking-allocs]: #trackingallocationsites +[bernoulli-trial]: https://en.wikipedia.org/wiki/Bernoulli_trial +[alloc-sampling-probability]: #allocsamplingprobability +[object]: Debugger.Object.md +[allocation-site]: Debugger.Object.md#allocationsite +[saved-frame]: ../SavedFrame/index +[drain-alloc-log]: #drainAllocationsLog +[max-alloc-log]: #maxAllocationsLogLength +[take-census]: #takecensus-options +[timestamps]: ./Conventions.md#timestamps diff --git a/js/src/doc/Debugger/Debugger.Object.md b/js/src/doc/Debugger/Debugger.Object.md new file mode 100644 index 0000000000..abde860e34 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Object.md @@ -0,0 +1,676 @@ +# 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. This accessor may throw if the referent is a scripted +proxy or some other sort of exotic object (an opaque wrapper, for example). + +### `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: + +```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: + +```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 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 function uses destructuring parameters, the corresponding array elements +are `undefined`. For example, if the referent is a function declared in this +way: + +```js +function f(a, [b, c], {d, e:f}) { ... } +``` + +then this `Debugger.Object` instance's `parameterNames` property would +have the value: + +```js +["a", undefined, undefined] +``` + +### `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`. + +### `isError` +`true` if the referent is any potentially wrapped Error; `false` otherwise. + +### `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. + +### `isGeneratorFunction` +If the referent is a debuggee function, returns `true` if the referent was +created with a `function*` expression or statement, or false if it is some +other sort of function. If the referent is not a debuggee function, or not a +function at all, this is `undefined`. (This is always equal to +`obj.script.isGeneratorFunction`, assuming `obj.script` is a +`Debugger.Script`.) + +### `isAsyncFunction` +If the referent is a debuggee function, returns `true` if the referent is an +async function, defined with an `async function` expression or statement, or +false if it is some other sort of function. If the referent is not a +debuggee function, or not a function at all, this is `undefined`. (This is +always equal to `obj.script.isAsyncFunction`, assuming `obj.script` is a +`Debugger.Script`.) + +### `isClassConstructor` +If the referent is a debuggee function, returns `true` if the referent is a class, +or false if it is some other sort of function. If the referent is not a debuggee +function, or not a function at all, this is `undefined`. (This is always equal to +`obj.script.isClassConstructor`, assuming `obj.script` is a `Debugger.Script`.) + +### `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. + +### `allocationSite` +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. + +These methods may throw if the referent is not a native object. Even simple +accessors like `isExtensible` may throw if the referent is a proxy or some sort +of exotic object like an opaque wrapper. + +### `getProperty(key, [receiver])` +Return a [completion value][cv] with "return" being the value of the +referent's property named <i>key</i>, or `undefined` if it has no such +property. <i>key</i> must be a string or symbol; <i>receiver</i> must be +a debuggee value. If the property is a getter, it will be evaluated with +<i>receiver</i> as the receiver, defaulting to the `Debugger.Object` +if omitted. All extant handler methods, breakpoints, and so on remain +active during the call. + +### `setProperty(key, value<, [receiver])` +Store <i>value</i> as the value of the referent's property named +<i>key</i>, creating the property if it does not exist. <i>key</i> +must be a string or symbol; <i>value</i> and <i>receiver</i> must be +debuggee values. If the property is a setter, it will be evaluated with +<i>receiver</i> as the receiver, defaulting to the `Debugger.Object` +if omitted. Return a [completion value][cv] with "return" being a +success/failure boolean, or else "throw" being the exception thrown +during property assignment. All extant handler methods, breakpoints, +and so on remain active during the call. + +### `getOwnPropertyDescriptor(key)` +Return a property descriptor for the property named <i>key</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. + +### `getOwnPropertyNamesLength()` +Return the number of the referent's own properties. + +### `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. + +### `defineProperty(key, attributes)` +Define a property on the referent named <i>key</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.) + +### `defineProperties(properties)` +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.) + +### `deleteProperty(key)` +Remove the referent's property named <i>key</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.) + +### `makeDebuggeeValue(value)` +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. + +### `makeDebuggeeNativeFunction(value)` +If <i>value</i> is a native function in the debugger's compartment, create +an equivalent function for the same native in the debuggee's realm, and +return a `Debugger.Object` instance for the new function. The new function +can be accessed by code in the debuggee without going through a cross +compartment wrapper. + +### `isSameNative(value)` +If <i>value</i> is a native function in the debugger's compartment, return +whether the referent is a native function for the same C++ native. + +### `isNativeGetterWithJitInfo()` +Return whether the referent is a native getter function with JSJitInfo. + +### `decompile([pretty])` +If the referent is a function that is debuggee code, return the +JavaScript source code for a function definition equivalent to the +referent function in its effect and result, as a string. If +<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`. + +### `call(this, argument, ...)` +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]. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +### `apply(this, arguments)` +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]. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +### `executeInGlobal(code, [options])` +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 (pending note below). 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]. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +### `executeInGlobalWithBindings(code, bindings, [options])` +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]. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +### `createSource(options)` +If the referent is a global object, return a new JavaScript source in the +global's realm which has its properties filled in according to the `options` +object. If the referent is not a global object, throw a `TypeError` +exception. The `options` object can have the following properties: + * `text`: String contents of the JavaScript in the source. + * `url`: URL the resulting source should be associated with. + * `startLine`: Starting line of the source. + * `startColumn`: Starting column of the source. + * `sourceMapURL`: Optional URL specifying the source's source map URL. + If not specified, the source map URL can be filled in if specified by + the source's text. + * `isScriptElement`: Optional boolean which will set the source's + `introductionType` to `"inlineScript"` if specified. Otherwise, the + source's `introductionType` will be `undefined`. + +### `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. + +### `forceLexicalInitializationByName(binding)` +If <i>binding</i> is in an uninitialized state initialize it to undefined +and return true, otherwise do nothing and return false. + +### `getPromiseReactions` + +If the referent is a [`Promise`][promise] or a cross-compartment wrapper of one, +this returns an array of objects describing the reaction records added to the +promise. There are several different sorts of reaction records: + +- The array entry for a reaction record added with `then` or `catch` has the + form `{ resolve: F, reject: F, result: P }`, where each `F` is a `Debugger.Object` + referring to a function object, and `P` is the promise that will be resolved + with the result of calling them. + + The `resolve` and `reject` properties may be absent in some cases. A call to + `then` can omit the rejection handler, and a call to `catch` omits the + resolution handler. Furthermore, various promise facilities create records + like this as internal implementation details, creating handlers that are not + representable as JavaScript functions. + +- When a promise `P1` is resolved to another promise `P2` (such that resolving + `P2` resolves `P1` in the same way) that adds a reaction record to `P2`. The + array entry for that reaction record is simply the `Debugger.Object` + representing `P1`. + + Note that, if `P1` and `P2` are in different compartments, resolving `P1` to + `P2` creates the same sort of reaction record as a call to `then` or + `catch`, with `P1` stored only in a private slot of the `resolve` and + `reject` functions, and not directly available from the reaction record. + +- An `await` expression calls `PromiseResolve` on its operand to obtain a + promise `P`, and then adds a reaction record to `P` that resumes the + suspended call appropriately. The array entry for that reaction record is a + `Debugger.Frame` representing the suspended call. + + If the `await`'s operand `A` is a native promise with the standard + constructor, then `PromiseResolve` simply returns `A` unchanged, and the + reaction record for resuming the suspended call is added to `A`'s list. But + if `A` is some other sort of 'thenable', then `PromiseResolve` creates a new + promise and enqueues a job to call `A`'s `then` method; this may produce + more indirect chains from awaitees to awaiters. + +- `JS::AddPromiseReactions` and + `JS::AddPromiseReactionsIgnoringUnhandledRejection` create a reaction record + whose promise field is `null`. + +[debugger-object]: Debugger.md +[script]: Debugger.Script.md +[environment]: Debugger.Environment.md +[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise +[saved-frame]: ../SavedFrame/index + +[tracking-allocs]: Debugger.Memory.md#trackingallocationsites +[inv fr]: Debugger.Frame.md#invocation-functions-and-debugger-frames +[wouldrun]: Conventions.md#the-debugger-debuggeewouldrun-exception +[cv]: Conventions.md#completion-values +[fr eval]: Debugger.Frame.md#eval-code-options diff --git a/js/src/doc/Debugger/Debugger.Script.md b/js/src/doc/Debugger/Debugger.Script.md new file mode 100644 index 0000000000..1788e5230d --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Script.md @@ -0,0 +1,505 @@ +# 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: + +### `isGeneratorFunction` +True if this instance refers to a `JSScript` for a function defined with a +`function*` expression or statement. False otherwise. + +### `isAsyncFunction` +True if this instance refers to a `JSScript` for an async function, defined +with an `async function` expression or statement. False otherwise. + +### `isFunction` +True if this instance refers to a `JSScript` for a function. False +otherwise. + +### `isModule` +True if this instance refers to a `JSScript` that was parsed and loaded +as an ECMAScript module. False otherwise. + +### `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: + +```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: + +```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`. + +### `parameterNames` +**If the instance refers to a `JSScript`**, the names of its parameters, +as an array of strings. If the script is not a function script this is +`undefined`. + +If the function uses destructuring parameters, the corresponding array elements +are `undefined`. For example, if the referent is a function script declared in this +way: + +```js +function f(a, [b, c], {d, e:f}) { ... } +``` + +then this `Debugger.Script` instance's `parameterNames` property would +have the value: + +```js +["a", undefined, undefined] +``` + +**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. For scripts created by `eval` or the +`Function` constructor, this may be a synthesized filename, starting with a +valid URL and followed by information tracking how the code was introduced +into the system; the entire string is not a valid URL. For +`Function.prototype`'s script, this is `null`. If this `Debugger.Script`'s +`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`. + +### `startColumn` +**If the instance refers to a `JSScript`**, the zero-indexed number of the +column at which this script's code starts, within the file or document +named by `url`. For functions, this is the start of the function's +arguments: +```js +function f() { ... } +// ^ start (column 10) +let g = x => x*x; +// ^ start (column 8) +let h = (x) => x*x; +// ^ start (column 8) +``` +For default class constructors, it is the start of the `class` keyword: +```js +let MyClass = class { }; +// ^ start (column 14) +``` +For scripts from other sources, such as `eval` or the `Function` +constructor, it is typically 0: +```js +let f = new Function(" console.log('hello world');"); +// ^ start (column 0, from the string's perspective) +``` + +### `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`. + +### `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`. + +### `mainOffset` +**If the instance refers to a `JSScript`**, the offset of the main +entry point of the script, excluding any prologue. + +**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. + +### `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`. + +### `getPossibleBreakpoints(query)` +Query for the recommended breakpoint locations available in SpiderMonkey. +Returns a result array of objects with the following properties: + * `offset: number` - The offset the breakpoint. + * `lineNumber: number` - The line number of the breakpoint. + * `columnNumber: number` - The column number of the breakpoint. + * `isStepStart: boolean` - True if SpiderMonkey recommends that the + breakpoint be treated as a step location when users of debuggers + step to the next item. This _roughly_ translates to the start of + each statement, though not entirely. + +The `query` argument can be used to filter the set of breakpoints. +The `query` object can contain the following properties: + + * `minOffset: number` - The inclusive lower bound of `offset` values to include. + * `maxOffset: number` - The exclusive upper bound of `offset` values to include. + * `line: number` - Limit to breakpoints on the given line. + * `minLine: number` - The inclusive lower bound of lines to include. + * `minColumn: number` - The inclusive lower bound of the line/minLine column to include. + * `maxLine: number` - The exclusive upper bound of lines to include. + * `maxColumn: number` - The exclusive upper bound of the line/maxLine column to include. + +### `getPossibleBreakpointOffsets(query)` +Query for the recommended breakpoint locations available in SpiderMonkey. +Identical to getPossibleBreakpoints except this returns an array of `offset` +values instead of offset metadata objects. + +### `getOffsetMetadata(offset)` +Get metadata about a given bytecode offset. +Returns an object with the following properties: + * `lineNumber: number` - The line number of the breakpoint. + * `columnNumber: number` - The column number of the breakpoint. + * `isBreakpoint: boolean` - True if this offset qualifies as a breakpoint, + defined using the same semantics used for `getPossibleBreakpoints()`. + * `isStepStart: boolean` - True if SpiderMonkey recommends that the + breakpoint be treated as a step location when users of debuggers + step to the next item. This _roughly_ translates to the start of + each statement, though not entirely. + +### `setBreakpoint(offset, handler)` +**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. Also, even if <i>offset</i> is valid offset +in this script, some instructions for engine-internal operation (e.g. +SetAliasedVar in the generator function initialization) don't allow setting +breakpoints, and in that case, this also throws 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. 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. + +### `getBreakpoints([offset])` +**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`. + +### `clearBreakpoint(handler, [offset])` +**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. + +### `clearAllBreakpoints([offset])` +**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. + +### `getEffectfulOffsets()` +**If the instance refers to a `JSScript`**, return an array +containing the offsets of all bytecodes in the script which can have direct +side effects that are visible outside the currently executing frame. This +includes, for example, operations that set properties or elements on +objects, or that may set names in environments created outside the frame. + +This doesn't include some instructions for engine-internal operation (e.g. +SetAliasedVar in the generator function initialization). Those instructions +can be effectful in term of engine-internal, but that's not user-visible and +can be treated as not-effectful here. + +### `getOffsetsCoverage()`: +**If the instance refers to a `JSScript`**, return `null` or an array which +contains information 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`. + +### `isInCatchScope([offset])` +**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`. + +### Deprecated Debugger.Script Prototype Functions + +The following functions have all been deprecated in favor of `getOffsetMetadata`, +`getPossibleBreakpoints`, and `getPossibleBreakpointOffsets`. These functions +all have an under-defined concept of what offsets are and are not included +in their results. + +#### `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: + +```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: + +```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: + +```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: + +```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`. + +#### `getLineOffsets(line)` +**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. + +#### `getOffsetLocation(offset)` +**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. + + +[debugger-object]: Debugger.md +[source]: Debugger.Source.md +[object]: Debugger.Object.md +[frame]: Debugger.Frame.md +[rv]: Conventions.md#resumption-values diff --git a/js/src/doc/Debugger/Debugger.Source.md b/js/src/doc/Debugger/Debugger.Source.md new file mode 100644 index 0000000000..a808211c0e --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Source.md @@ -0,0 +1,285 @@ +# 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 `"[wasm]"` value will +be returned. + +### `binary` +**If the instance refers to WebAssembly code**, a Uint8Array that contains +the WebAssembly bytecode. + +### `url` +**If the instance refers to JavaScript source**, the filename or URL from +which this script's code was loaded. For scripts created by `eval` or the +`Function` constructor, this may be a synthesized filename, starting with a +valid URL and followed by information tracking how the code was introduced +into the system; the entire string is not a valid URL. For +`Function.prototype`'s script, this is `null`. 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. + +### `startLine` +**If the instance refers to JavaScript source**, the start line of the +source within the file or URL it was loaded from. This is normally `1`, but +may have another value if the source is part of an HTML document. + +### `startColumn` +**If the instance refers to JavaScript source**, the start column of the +source within the file or URL it was loaded from. This is normally `1`, but +may have another value if the source is part of an HTML document. + +### `id` +**If the instance refers to JavaScript source**, an int32 counter that identifies +the source within the current process. This ID is used in other places in Gecko +that weakly refer to sources, such as nsIScriptError. + +### `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 effect +and will not change existing value. + +**If the instance refers to WebAssembly code**, `null`. Attempts to write +to this property throw a `TypeError`. + +### `displayURL` +If the script had a special `//# sourceURL` comment, as described in +the source maps specification, then this property's value holds +the string that was given. Otherwise, this is `null`. + +### `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 superseded 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`. + +* `"debugger eval"`, for code evaluated by debugger. + +* `"Function"`, for code passed to the `Function` constructor. + +* `"GeneratorFunction"`, for code passed to the generator constructor. + +* `"AsyncFunction"`, for code passed to the async function constructor. + +* `"AsyncGenerator"`, for code passed to the async generator constructor. + +* `"Worklet"`, for code loaded by worklet. + +* `"importScripts"`, for code by calling `importScripts` in a web worker. + +* `"eventHandler"`, for code assigned to DOM elements' event handler IDL + attributes as a string. + +* `"srcScript"`, for code belonging to `<script src="file.js">` elements. + +* `"inlineScript"`, for code belonging to `<script>code;</script>` elements. + +* `"injectedScript"`, for code belonging to scripts that _would_ be + `"inlineScript"` except that they were not part of the initial file itself. + + For example, scripts created via: + + * `document.write("<script>code;</script>")` + * `var s = document.createElement("script"); s.text = "code";` + +* `"importedModule"`, for code that was loaded indirectly by being imported + by another script using ESM static or dynamic imports. + +* `"javascriptURL"`, for code presented in `javascript:` URLs. + +* `"domTimer"`, for code passed to `setTimeout`/`setInterval` as a string. + +* `"self-hosted"`, for internal self-hosted JS code. + +* `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`. + +## Function Properties of the Debugger.Source Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger.Source` instance; they may not be used as +methods of other kinds of objects. + +### `reparse()` + +**If the instance refers to JavaScript source**, and if the source has text +available, return a `Debugger.Script` instance referring to a new script +created by reparsing this source's text. Debugger `onNewScript` hooks will +not be invoked for the new script. Otherwise, throw an `Error`. + + +[debugger-object]: Debugger.md +[script]: Debugger.Script.md +[object]: Debugger.Object.md diff --git a/js/src/doc/Debugger/Debugger.md b/js/src/doc/Debugger/Debugger.md new file mode 100644 index 0000000000..e720eda259 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.md @@ -0,0 +1,555 @@ +# The Debugger Object + +When called as a constructor, the `Debugger` object creates a new +`Debugger` instance. + +## `new Debugger([global, ...])` +Create a debugger object, and apply its [`addDebuggee`][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: + +### `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 coverage first 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. + +### `onNewScript(script, global)` +New code, represented by the [`Debugger.Script`][script] instance +<i>script</i>, has been loaded in the scope of the debuggees. + +Since each function has its own [`Debugger.Script`][script], separate from +the top-level code or function that encloses it, loading JavaScript code +typically introduces not just a single script, but a tree of scripts +representing the top-level code and any functions it includes. The +`onNewScript` hook reports only the root script of such a tree. If +necessary, the handler function can use the scripts' `getChildScripts` +method to walk the tree and obtain all the newly introduced scripts. + +This method's return value is ignored. + +### `onNewPromise(promise)` +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. + +### `onPromiseSettled(promise)` +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. + +### `onDebuggerStatement(frame)` +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. + +### `onEnterFrame(frame)` +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. + +### `onNativeCall(callee, reason)` +A call to a native function is being made from a debuggee realm. +<i>callee</i> is a [`Debugger.Object`] for the function being called, and +<i>reason</i> is a string describing the reason the call was made, and +has one of the following values: + +`get`: The native is the getter for a property which is being accessed. +`set`: The native is the setter for a property being written to. +`call`: Any call not fitting into the above categories. + +This method should return a [resumption value][rv] specifying how the debuggee's +execution should proceed. If a return value is overridden for a constructor +call, it must be an object. + +SpiderMonkey only calls `onNativeCall` hooks when execution is inside a +debugger evaluation associated with the debugger that has the `onNativeCall` +hook. Such evaluation methods include `Debugger.Object.apply`, `Debugger.Object.call`, +`Debugger.Object.executeInGlobal`, `Debugger.Frame.eval`, and associated methods. + +Separately, any Debugger hooks triggered during calls to `Debugger.Object.apply`, `Debugger.Object.call`, +`Debugger.Object.executeInGlobal`, `Debugger.Frame.eval`, and associated methods +will only be triggered on Debugger objects owned by the Debugger performing +the evaluation. + +### `onExceptionUnwind(frame, value)` +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. + +### `sourceHandler(ASuffusionOfYellow)` +This method is never called. If it is ever called, a contradiction has +been proven, and the debugger is free to assume that everything is true. + +### `onError(frame, report)` +SpiderMonkey is about to report an error in <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. + +Note that, even though the presence of a `Debugger`'s `onNewGlobalObject` +hook can have arbitrary side effects, the garbage collector does not +consider the presence of the hook sufficient reason to keep the `Debugger` +alive. Thus, the behavior of code that uses `onNewGlobalObject` on unrooted +`Debugger`s may be affected by the garbage collector's activity, and +is not entirely deterministic. + + +## Function Properties of the Debugger Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger` instance; they may not be used as methods of +other kinds of objects. + +### `addDebuggee(global)` +Add the global object designated by <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. + +### `removeDebuggee(global)` +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`. + +### `hasDebuggee(global)` +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. + +### `findSources()` +Return an array of all [`Debugger.Source`][source] instances of all debuggee +scripts. + +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. + +### `findScripts([query])` +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. + +### `findSourceURLs()` + +Return an array of strings containing the URLs of all known sources that +have been created in any debuggee realm. The array will have one entry for +each source, so may have duplicates. The URLs for the realms are +occasionally purged and the returned array might not be complete. + +### `findObjects([query])` +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. + +### `clearBreakpoint(handler)` +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. + +### `makeGlobalObjectReference(global)` +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]. + +### `adoptDebuggeeValue(value)` +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. + +### `adoptFrame(frame)` +Given `frame` of type `Debugger.Frame` which is owned by an arbitrary +`Debugger`, return an equivalent `Debugger.Frame` owned by this `Debugger`. +If the `frame` is associated with a debuggee that is _not_ a debuggee of +the adopting debugger, this method will throw. + +### `adoptSource(source)` +Given `source` of type `Debugger.Source` which is owned by an arbitrary +`Debugger`, return an equivalent `Debugger.Source` owned by this `Debugger`. + +### `enableAsyncStack(global)` +Enable async stack capturing for the realm for the global object designated by +<i>global</i>. + +### `disableAsyncStack(global)` +Disable async stack capturing for the realm for the global object designated by +<i>global</i>. + +### `enableUnlimitedStacksCapturing(global)` +Allow to capture more than 50 stacktraces for the realm for the global object +designated by <i>global</i>, even if it is not a debuggee. + +### `disableUnlimitedStacksCapturing(global)` +Disallow to capture more than 50 stacktraces for the realm for the global object +designated by <i>global</i>, unless it is a debuggee. + +## Static methods of the Debugger Object + +The functions described below are not called with a `this` value. + +### `isCompilableUnit(source)` +: 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. + +[add]: #adddebuggee-global +[source]: Debugger.Source.md +[script]: Debugger.Script.md +[rv]: Conventions.md#resumption-values +[object]: Debugger.Object.md +[vf]: Debugger.Frame.md#visible-frames +[tracking-allocs]: Debugger.Memory.md#trackingallocationsites +[frame]: Debugger.Frame.md 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 0000000000..635b8eca1b --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md @@ -0,0 +1,226 @@ +# Tutorial: Show Allocations Per Call Path + +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: + + ```js + // This simply defines the 'Debugger' constructor in this + // Scratchpad; it doesn't actually start debugging anything. + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + 'resource://gre/modules/jsdebugger.sys.mjs' + ); + 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. + + ```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.) + + +[debugger]: Debugger-API.md +[img-chrome-pref]: enable-chrome-devtools.png +[img-scratchpad-browser]: scratchpad-browser-environment.png +[img-alloc-plot]: alloc-plot-console.png diff --git a/js/src/doc/Debugger/Tutorial-Breakpoint.md b/js/src/doc/Debugger/Tutorial-Breakpoint.md new file mode 100644 index 0000000000..55b8681857 --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Breakpoint.md @@ -0,0 +1,133 @@ +Tutorial: Set a breakpoint using `Debugger` +=========================================== + +This page shows how you can try out the [`Debugger` API][debugger] yourself +using Firefox's Scratchpad. We use `Debugger` to set a breakpoint in a function, +and then evaluate an expression whenever it is hit. + +This tutorial was tested against Firefox 58 Beta and Nightly. It does not work in Firefox 57. + +1. Since the `Debugger` API is only available to privileged JavaScript code, + you'll need to use the Browser Content Toolbox to try it out. To do this, + open the Firefox developer tools, click on the options gear at the upper + right of the toolbox, and make sure that both “Enable browser chrome and + add-on debugging toolboxes” and “Enable remote debugging” are checked. These + are located at the bottom right of the options panel; you may need to scroll + to see them. Once they're checked, you can close the developer tools. + + +2. Save the following text to an HTML file: + + ```html + <div onclick="report('the best div');">Click me!</div> + <div onclick="report('another great div');">Or me!</div> + <script> + function report(what) { + console.log('clicked: ' + what); + } + </script> + ``` + +3. Visit the HTML file in your browser, and open the Browser Content Toolbox by + opening the Firefox menu, choosing “Browser Tools”, and then “Browser + Content Toolbox”. If that item doesn't appear in the “Browser Tools” menu, + make sure you checked both boxes to enable the Browser Content Toolbox as + explained in Step 1. + +4. Our example code is long enough that the best way to run it is to use the + Scratchpad panel, which is not enabled by default. To enable it, click on + the options gear at the upper right of the Browser Content Toolbox, and make + sure the “Scratchpad” box in the “Default Developer Tools” section the left + is checked. The Scratchpad panel should appear at the top of the Toolbox + alongside the Console, Debugger, and Memory panels. + +5. Click on the Scratchpad panel and enter the following code: + + ```js + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + "resource://gre/modules/jsdebugger.sys.mjs" + ); + const { console } = ChromeUtils.importESModule( + "resource://gre/modules/Console.sys.mjs" + ); + + // This simply defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + addDebuggerToGlobal(globalThis); + + // Create a 'Debugger' instance. + var dbg = new Debugger; + + // Make the tab's top window a debuggee, and get a + // Debugger.Object referring to the window. + var windowDO = dbg.addDebuggee(tabs[0].content); + + // Get a Debugger.Object referring to the window's `report` + // function. + var reportDO = windowDO.getOwnPropertyDescriptor('report').value; + + // Set a breakpoint at the entry point of `report`. + reportDO.script.setBreakpoint(0, { + hit: function (frame) { + console.log('hit breakpoint in ' + frame.callee.name); + console.log('what = ' + frame.eval('what').return); + } + }); + + console.log('Finished setting breakpoint!'); + ``` + +6. In the Scratchpad, ensure that no text is selected, and press the "Run" + button. + + 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 start of the `report` function, + `Debugger` calls the breakpoint handler's `hit` method, + passing a `Debugger.Frame` instance. + The `hit` method logs the breakpoint hit to the browser content toolbox's console. + Then it evaluates the expression `what` in the given stack frame, and logs its result. + The toolbox's console now looks like this: + + ![The breakpoint handler's console output][img-example-console] + <!-- HTML comment prevents caption, which MDN styles poorly --> + + You can also click on the text that says “Or me!”, to see `report` called from a + different handler. + + If `Debugger` is unable to find the `report` function, or the console output + does not appear, evaluate the expression `tabs[0].content.document.location` + in the console to make sure that `tabs[0]` indeed refers to the HTML file you + visited. If you have more than one tab visiting a `file:` URL, they all share + a single content process, so you may need to use a different element of the + array as the debuggee. + +7. Press "Run" in the Scratchpad again. Now, clicking on "Click me!" causes the + breakpoint hit to be logged twice---one for each `Debugger` instance. + + Multiple `Debugger` instances can observe the same debuggee. Re-running the code + in the Scratchpad creates a fresh `Debugger` instance, adds the same web page as + its debuggee, and then sets a new breakpoint. When you click on the `div` + element, both `Debugger`s' breakpoints are hit, and both handlers run. + + This shows how any number of `Debugger`-based tools can observe a single web + page simultaneously. In fact, you can use the Browser Content Toolbox's Debugger + panel to set its own breakpoint in `report`, and it will trigger along with the + first two. Keep in mind, however, that when multiple Debuggers share a debuggee, + the order in which their handlers run is not specified. If more than one tool + tries to influence the debuggee's behavior, their combined behavior could be + unpredictable. + +8. Close the web page and the Browser Content Toolbox. + + 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. + +[debugger]: Debugger-API.md +[img-example-console]: console.png 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 0000000000..ebac35e445 --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md @@ -0,0 +1,94 @@ +Tutorial: Evaluate an Expression When a debugger; Statement Is Executed +======================================================================= + +**NOTE: This tutorial no longer works in current versions of Firefox.** +Instead, please try the updated and expanded [breakpoint tutorial][tut breakpoint]. + +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: + + ```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: + + ```js + // This simply defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + "resource://gre/modules/jsdebugger.sys.mjs" + ); + 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. + +[tut breakpoint]: Tutorial-Breakpoint.md +[debugger]: Debugger-API.md + +[img-chrome-pref]: enable-chrome-devtools.png +[img-scratchpad-browser]: scratchpad-browser-environment.png +[img-example-alert]: debugger-alert.png 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 0000000000..5411724724 --- /dev/null +++ b/js/src/doc/Debugger/alloc-plot-console.png diff --git a/js/src/doc/Debugger/console.png b/js/src/doc/Debugger/console.png Binary files differnew file mode 100644 index 0000000000..9e9ce4ae74 --- /dev/null +++ b/js/src/doc/Debugger/console.png 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 0000000000..2bf9362243 --- /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 0000000000..033468991f --- /dev/null +++ b/js/src/doc/Debugger/enable-chrome-devtools.png diff --git a/js/src/doc/Debugger/index.rst b/js/src/doc/Debugger/index.rst new file mode 100644 index 0000000000..35ed8fa10f --- /dev/null +++ b/js/src/doc/Debugger/index.rst @@ -0,0 +1,20 @@ +===================== +Debugger API +===================== + +.. toctree:: + :maxdepth: 4 + + Debugger-API.md + Debugger.md + Debugger.Object.md + Debugger.Script.md + Debugger.Source.md + Debugger.Environment.md + Debugger.Frame.md + Debugger.Memory.md + Conventions.md + + Tutorial-Alloc-Log-Tree.md + Tutorial-Breakpoint.md + Tutorial-Debugger-Statement.md 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 0000000000..534d0f9500 --- /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 0000000000..44bceddb4c --- /dev/null +++ b/js/src/doc/Debugger/shadows.svg @@ -0,0 +1,1000 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<!-- 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> |