diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | ad18d877ddd2a44d98fa12ccd3dbbcf4d0ac4299 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/doc | |
parent | 15477ed9af4859dacb069040b5d4de600803d3bc (diff) | |
download | uxp-ad18d877ddd2a44d98fa12ccd3dbbcf4d0ac4299.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/doc')
30 files changed, 6143 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..e8bd3ee430 --- /dev/null +++ b/js/src/doc/Debugger/Conventions.md @@ -0,0 +1,168 @@ +# General Conventions + +This page describes general conventions used in the [`Debugger`][debugger] API, +and defines some terminology used throughout the specification. + + +## Properties + +Properties of objects that comprise the `Debugger` interface, and those +that the interface creates, follow some general conventions: + +- Instances and prototypes are extensible; you can add your own properties + and methods to them. + +- Properties are configurable. This applies to both "own" and prototype + properties, and to both methods and data properties. (Leaving these + properties open to redefinition will hopefully make it easier for + JavaScript debugger code to cope with bugs, bug fixes, and changes in the + interface over time.) + +- Method properties are writable. + +- We prefer inherited accessor properties to own data properties. Both are + read using the same syntax, but inherited accessors seem like a more + accurate reflection of what's going on. Unless otherwise noted, these + properties have getters but no setters, as they cannot meaningfully be + assigned to. + + +## Debuggee Values + +The `Debugger` interface follows some conventions to help debuggers safely +inspect and modify the debuggee's objects and values. Primitive values are +passed freely between debugger and debuggee; copying or wrapping is handled +transparently. Objects received from the debuggee (including host objects +like DOM elements) are fronted in the debugger by `Debugger.Object` +instances, which provide reflection-oriented methods for inspecting their +referents; see `Debugger.Object`, below. + +Of the debugger's objects, only `Debugger.Object` instances may be passed +to the debuggee: when this occurs, the debuggee receives the +`Debugger.Object`'s referent, not the `Debugger.Object` instance itself. + +In the descriptions below, the term "debuggee value" means either a +primitive value or a `Debugger.Object` instance; it is a value that might +be received from the debuggee, or that could be passed to the debuggee. + + +## Debuggee Code + +Each `Debugger` instance maintains a set of global objects that, taken +together, comprise the debuggee. Code evaluated in the scope of a debuggee +global object, directly or indirectly, is considered *debuggee code*. +Similarly: + +- a *debuggee frame* is a frame running debuggee code; + +- a *debuggee function* is a function that closes over a debuggee + global object (and thus the function's code is debuggee code); + +- a *debuggee environment* is an environment whose outermost + enclosing environment is a debuggee global object; and + +- a *debuggee script* is a script containing debuggee code. + + +## Completion Values + +When a debuggee stack frame completes its execution, or when some sort +of debuggee call initiated by the debugger finishes, the `Debugger` +interface provides a value describing how the code completed; these are +called *completion values*. A completion value has one of the +following forms: + +<code>{ return: <i>value</i> }</code> +: The code completed normally, returning <i>value</i>. <i>Value</i> is a + debuggee value. + +<code>{ throw: <i>value</i> }</code> +: The code threw <i>value</i> as an exception. <i>Value</i> is a debuggee + value. + +`null` +: The code was terminated, as if by the "slow script" dialog box. + +If control reaches the end of a generator frame, the completion value is +<code>{ throw: <i>stop</i> }</code> where <i>stop</i> is a +`Debugger.Object` object representing the `StopIteration` object being +thrown. + + +## Resumption Values + +As the debuggee runs, the `Debugger` interface calls various +debugger-provided handler functions to report the debuggee's behavior. +Some of these calls can return a value indicating how the debuggee's +execution should continue; these are called *resumption values*. A +resumption value has one of the following forms: + +`undefined` +: The debuggee should continue execution normally. + +<code>{ return: <i>value</i> }</code> +: Return <i>value</i> immediately as the current value of the function. + <i>Value</i> must be a debuggee value. (Most handler functions support + this, except those whose descriptions say otherwise.) If the function + was called as a constructor (that is, via a `new` expression), then + <i>value</i> serves as the value returned by the function's body, not + that produced by the `new` expression: if the value is not an object, + the `new` expression returns the frame's `this` value. Similarly, if + the function is the constructor for a subclass, then a non-object + value may result in a TypeError. + If the frame is a generator or async function, then <i>value</i> must + conform to the iterator protocol: it must be a non-proxy object of the form + <code>{ done: <i>boolean</i>, value: <i>v</i> }</code>, where + both `done` and `value` are ordinary properties. + +<code>{ throw: <i>value</i> }</code> +: Throw <i>value</i> as an exception from the current bytecode + instruction. <i>Value</i> must be a debuggee value. + +`null` +: Terminate the debuggee, as if it had been cancelled by the "slow script" + dialog box. + +If a function that would normally return a resumption value to indicate +how the debuggee should continue instead throws an exception, we never +propagate such an exception to the debuggee; instead, we call the +associated `Debugger` instance's `uncaughtExceptionHook` property, as +described below. + + +## Timestamps + +Timestamps are expressed in units of milliseconds since an arbitrary, +but fixed, epoch. The resolution of timestamps is generally greater +than milliseconds, though no specific resolution is guaranteed. + + +## The `Debugger.DebuggeeWouldRun` Exception + +Some debugger operations that appear to simply inspect the debuggee's state +may actually cause debuggee code to run. For example, reading a variable +might run a getter function on the global or on a `with` expression's +operand; and getting an object's property descriptor will run a handler +trap if the object is a proxy. To protect the debugger's integrity, only +methods whose stated purpose is to run debuggee code can do so. These +methods are called [invocation functions][inv fr], and they follow certain +common conventions to report the debuggee's behavior safely. For other +methods, if their normal operation would cause debuggee code to run, they +throw an instance of the `Debugger.DebuggeeWouldRun` exception. + +If there are debugger frames on stack from multiple Debugger instances, the +thrown exception is an instance of the topmost locking debugger's global's +`Debugger.DebuggeeWouldRun`. + +A `Debugger.DebuggeeWouldRun` exception may have a `cause` property, +providing more detailed information on why the debuggee would have run. The +`cause` property's value is one of the following strings: + + <i>cause</i> value meaning + -------------------- -------------------------------------------------------------------------------- + "proxy" Carrying out the operation would have caused a proxy handler to run. + "getter" Carrying out the operation would have caused an object property getter to run. + "setter" Carrying out the operation would have caused an object property setter to run. + +If the system can't determine why control attempted to enter the debuggee, +it will leave the exception's `cause` property undefined. diff --git a/js/src/doc/Debugger/Debugger-API.md b/js/src/doc/Debugger/Debugger-API.md new file mode 100644 index 0000000000..10f6ebf895 --- /dev/null +++ b/js/src/doc/Debugger/Debugger-API.md @@ -0,0 +1,126 @@ +# The `Debugger` Interface + +Mozilla's JavaScript engine, SpiderMonkey, provides a debugging interface +named `Debugger` which lets JavaScript code observe and manipulate the +execution of other JavaScript code. Both Firefox's built-in developer tools +and the Firebug add-on use `Debugger` to implement their JavaScript +debuggers. However, `Debugger` is quite general, and can be used to +implement other kinds of tools like tracers, coverage analysis, +patch-and-continue, and so on. + +`Debugger` has three essential qualities: + +- It is a *source level* interface: it operates in terms of the JavaScript + language, not machine language. It operates on JavaScript objects, stack + frames, environments, and code, and presents a consistent interface + regardless of whether the debuggee is interpreted, compiled, or + optimized. If you have a strong command of the JavaScript language, you + should have all the background you need to use `Debugger` successfully, + even if you have never looked into the language's implementation. + +- It is for use *by JavaScript code*. JavaScript is both the debuggee + language and the tool implementation language, so the qualities that make + JavaScript effective on the web can be brought to bear in crafting tools + for developers. As is expected of JavaScript APIs, `Debugger` is a + *sound* interface: using (or even misusing) `Debugger` should never cause + Gecko to crash. Errors throw proper JavaScript exceptions. + +- It is an *intra-thread* debugging API. Both the debuggee and the code + using `Debugger` to observe it must run in the same thread. Cross-thread, + cross-process, and cross-device tools must use `Debugger` to observe the + debuggee from within the same thread, and then handle any needed + communication themselves. (Firefox's builtin tools have a + [protocol][protocol] defined for this purpose.) + +In Gecko, the `Debugger` API is available to chrome code only. By design, +it ought not to introduce security holes, so in principle it could be made +available to content as well; but it is hard to justify the security risks +of the additional attack surface. + +The `Debugger` API cannot currently observe self-hosted JavaScript. This is not +inherent in the API's design, but simply that the self-hosting infrastructure +isn't prepared for the kind of invasions the `Debugger` API can perform. + + +## Debugger Instances and Shadow Objects + +`Debugger` reflects every aspect of the debuggee's state as a JavaScript +value---not just actual JavaScript values like objects and primitives, +but also stack frames, environments, scripts, and compilation units, which +are not normally accessible as objects in their own right. + +Here is a JavaScript program in the process of running a timer callback function: + +![A running JavaScript program and its Debugger shadows][img-shadows] + +This diagram shows the various types of shadow objects that make up the +Debugger API (which all follow some [general conventions][conventions]): + +- A [`Debugger.Object`][object] represents a debuggee object, offering a + reflection-oriented API that protects the debugger from accidentally + invoking getters, setters, proxy traps, and so on. + +- A [`Debugger.Script`][script] represents a block of JavaScript + code---either a function body or a top-level script. Given a + `Debugger.Script`, one can set breakpoints, translate between source + positions and bytecode offsets (a deviation from the "source level" + design principle), and find other static characteristics of the code. + +- A [`Debugger.Frame`][frame] represents a running stack frame. You can use + these to walk the stack and find each frame's script and environment. You + can also set `onStep` and `onPop` handlers on frames. + +- A [`Debugger.Environment`][environment] represents an environment, + associating variable names with storage locations. Environments may + belong to a running stack frame, captured by a function closure, or + reflect some global object's properties as variables. + +The [`Debugger`][debugger-object] instance itself is not really a shadow of +anything in the debuggee; rather, it maintains the set of global objects +which are to be considered debuggees. A `Debugger` observes only execution +taking place in the scope of these global objects. You can set functions to +be called when new stack frames are pushed; when new code is loaded; and so +on. + +Omitted from this picture are [`Debugger.Source`][source] instances, which +represent JavaScript compilation units. A `Debugger.Source` can furnish a +full copy of its source code, and explain how the code entered the system, +whether via a call to `eval`, a `<script>` element, or otherwise. A +`Debugger.Script` points to the `Debugger.Source` from which it is derived. + +Also omitted is the `Debugger`'s [`Debugger.Memory`][memory] instance, which +holds methods and accessors for observing the debuggee's memory use. + +All these types follow some [general conventions][conventions], which you +should look through before drilling down into any particular type's +specification. + +All shadow objects are unique per `Debugger` and per referent. For a given +`Debugger`, there is exactly one `Debugger.Object` that refers to a +particular debuggee object; exactly one `Debugger.Frame` for a particular +stack frame; and so on. Thus, a tool can store metadata about a shadow's +referent as a property on the shadow itself, and count on finding that +metadata again if it comes across the same referent. And since shadows are +per-`Debugger`, tools can do so without worrying about interfering with +other tools that use their own `Debugger` instances. + + +## Examples + +Here are some things you can try out yourself that show off some of `Debugger`'s +features: + +- [Evaluating an expression in a web page's stack frame when it executes a `debugger;` statement.][tut debugger] + +- [Showing how many objects different call paths allocate.][tut alloc log] + + +## Gecko-specific features + +While the `Debugger` core API deals only with concepts common to any +JavaScript implementation, it also includes some Gecko-specific features: + +- [Global tracking][global] supports debugging all the code running in a + Gecko instance at once---the 'chrome debugging' model. +- [Object wrapper][wrapper] functions help manipulate object references + that cross privilege boundaries. diff --git a/js/src/doc/Debugger/Debugger.Environment.md b/js/src/doc/Debugger/Debugger.Environment.md new file mode 100644 index 0000000000..e970f02653 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Environment.md @@ -0,0 +1,153 @@ +# Debugger.Environment + +A `Debugger.Environment` instance represents a lexical environment, +associating names with variables. Each [`Debugger.Frame`][frame] instance +representing a debuggee frame has an associated environment object +describing the variables in scope in that frame; and each +[`Debugger.Object`][object] instance representing a debuggee function has an +environment object representing the environment the function has closed +over. + +ECMAScript environments form a tree, in which each local environment is +parented by its enclosing environment (in ECMAScript terms, its 'outer' +environment). We say an environment <i>binds</i> an identifier if that +environment itself associates the identifier with a variable, independently +of its outer environments. We say an identifier is <i>in scope</i> in an +environment if the identifier is bound in that environment or any enclosing +environment. + +SpiderMonkey creates `Debugger.Environment` instances as needed as the +debugger inspects stack frames and function objects; calling +`Debugger.Environment` as a function or constructor raises a `TypeError` +exception. + +SpiderMonkey creates exactly one `Debugger.Environment` instance for each +environment it presents via a given [`Debugger`][debugger-object] instance: +if the debugger encounters the same environment through two different +routes (perhaps two functions have closed over the same environment), +SpiderMonkey presents the same `Debugger.Environment` instance to the +debugger each time. This means that the debugger can use the `==` operator +to recognize when two `Debugger.Environment` instances refer to the same +environment in the debuggee, and place its own properties on a +`Debugger.Environment` instance to store metadata about particular +environments. + +(If more than one [`Debugger`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Environment` instance for a given environment. This allows the +code using each [`Debugger`][debugger-object] instance to place whatever +properties it likes on its own [`Debugger.Object`][object] instances, +without worrying about interfering with other debuggers.) + +If a `Debugger.Environment` instance's referent is not a debuggee +environment, then attempting to access its properties (other than +`inspectable`) or call any its methods throws an instance of `Error`. + +`Debugger.Environment` instances protect their referents from the +garbage collector; as long as the `Debugger.Environment` instance is +live, the referent remains live. Garbage collection has no visible +effect on `Debugger.Environment` instances. + + +## Accessor Properties of the Debugger.Environment Prototype Object + +A `Debugger.Environment` instance inherits the following accessor +properties from its prototype: + +`inspectable` +: True if this environment is a debuggee environment, and can therefore + be inspected. False otherwise. All other properties and methods of + `Debugger.Environment` instances throw if applied to a non-inspectable + environment. + +`type` +: The type of this environment object, one of the following values: + + * "declarative", indicating that the environment is a declarative + environment record. Function calls, calls to `eval`, `let` blocks, + `catch` blocks, and the like create declarative environment records. + + * "object", indicating that the environment's bindings are the + properties of an object. The global object and DOM elements appear in + the chain of environments via object environments. (Note that `with` + statements have their own environment type.) + + * "with", indicating that the environment was introduced by a `with` + statement. + +`parent` +: The environment that encloses this one (the "outer" environment, in + ECMAScript terminology), or `null` if this is the outermost environment. + +`object` +: A [`Debugger.Object`][object] instance referring to the object whose + properties this environment reflects. If this is a declarative + environment record, this accessor throws a `TypeError` (since + declarative environment records have no such object). Both `"object"` + and `"with"` environments have `object` properties that provide the + object whose properties they reflect as variable bindings. + +`callee` +: If this environment represents the variable environment (the top-level + environment within the function, which receives `var` definitions) for + a call to a function <i>f</i>, then this property's value is a + [`Debugger.Object`][object] instance referring to <i>f</i>. Otherwise, + this property's value is `null`. + +`optimizedOut` +: True if this environment is optimized out. False otherwise. For example, + functions whose locals are never aliased may present optimized-out + environments. When true, `getVariable` returns an ordinary JavaScript + object whose `optimizedOut` property is true on all bindings, and + `setVariable` throws a `ReferenceError`. + + +## Function Properties of the Debugger.Environment Prototype Object + +The methods described below may only be called with a `this` value +referring to a `Debugger.Environment` instance; they may not be used as +methods of other kinds of objects. + +`names()` +: Return an array of strings giving the names of the identifiers bound by + this environment. The result does not include the names of identifiers + bound by enclosing environments. + +<code>getVariable(<i>name</i>)</code> +: Return the value of the variable bound to <i>name</i> in this + environment, or `undefined` if this environment does not bind + <i>name</i>. <i>Name</i> must be a string that is a valid ECMAScript + identifier name. The result is a debuggee value. + + JavaScript engines often omit variables from environments, to save space + and reduce execution time. If the given variable should be in scope, but + `getVariable` is unable to produce its value, it returns an ordinary + JavaScript object (not a [`Debugger.Object`][object] instance) whose + `optimizedOut` property is `true`. + + This is not an [invocation function][inv fr]; + if this call would cause debuggee code to run (say, because the + environment is a `"with"` environment, and <i>name</i> refers to an + accessor property of the `with` statement's operand), this call throws a + [`Debugger.DebuggeeWouldRun`][wouldrun] + exception. + +<code>setVariable(<i>name</i>, <i>value</i>)</code> +: Store <i>value</i> as the value of the variable bound to <i>name</i> in + this environment. <i>Name</i> must be a string that is a valid + ECMAScript identifier name; <i>value</i> must be a debuggee value. + + If this environment binds no variable named <i>name</i>, throw a + `ReferenceError`. + + This is not an [invocation function][inv fr]; + if this call would cause debuggee code to run, this call throws a + [`Debugger.DebuggeeWouldRun`][wouldrun] + exception. + +<code>find(<i>name</i>)</code> +: Return a reference to the innermost environment, starting with this + environment, that binds <i>name</i>. If <i>name</i> is not in scope in + this environment, return `null`. <i>Name</i> must be a string whose + value is a valid ECMAScript identifier name. + diff --git a/js/src/doc/Debugger/Debugger.Frame.md b/js/src/doc/Debugger/Debugger.Frame.md new file mode 100644 index 0000000000..68daaae6ce --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Frame.md @@ -0,0 +1,328 @@ +# Debugger.Frame + +A `Debugger.Frame` instance represents a [visible stack frame][vf]. Given a +`Debugger.Frame` instance, you can find the script the frame is executing, +walk the stack to older frames, find the lexical environment in which the +execution is taking place, and so on. + +For a given [`Debugger`][debugger-object] instance, SpiderMonkey creates +only one `Debugger.Frame` instance for a given visible frame. Every handler +method called while the debuggee is running in a given frame is given the +same frame object. Similarly, walking the stack back to a previously +accessed frame yields the same frame object as before. Debugger code can +add its own properties to a frame object and expect to find them later, use +`==` to decide whether two expressions refer to the same frame, and so on. + +(If more than one [`Debugger`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Frame` instance for a given frame. This allows the code using +each [`Debugger`][debugger-object] instance to place whatever properties it +likes on its `Debugger.Frame` instances, without worrying about interfering +with other debuggers.) + +When the debuggee pops a stack frame (say, because a function call has +returned or an exception has been thrown from it), the `Debugger.Frame` +instance referring to that frame becomes inactive: its `live` property +becomes `false`, and accessing its other properties or calling its methods +throws an exception. Note that frames only become inactive at times that +are predictable for the debugger: when the debuggee runs, or when the +debugger removes frames from the stack itself. + + +## Visible Frames + +When inspecting the call stack, [`Debugger`][debugger-object] does not +reveal all the frames that are actually present on the stack: while it does +reveal all frames running debuggee code, it omits frames running the +debugger's own code, and omits most frames running non-debuggee code. We +call those stack frames a [`Debugger`][debugger-object] does reveal +<i>visible frames</i>. + +A frame is a visible frame if any of the following are true: + +* it is running [debuggee code][dbg code]; + +* its immediate caller is a frame running debuggee code; or + +* it is a [`"debugger"` frame][inv fr], + representing the continuation of debuggee code invoked by the debugger. + +The "immediate caller" rule means that, when debuggee code calls a +non-debuggee function, it looks like a call to a primitive: you see a frame +for the non-debuggee function that was accessible to the debuggee, but any +further calls that function makes are treated as internal details, and +omitted from the stack trace. If the non-debuggee function eventually calls +back into debuggee code, then those frames are visible. + +(Note that the debuggee is not considered an "immediate caller" of handler +methods it triggers. Even though the debuggee and debugger share the same +JavaScript stack, frames pushed for SpiderMonkey's calls to handler methods +to report events in the debuggee are never considered visible frames.) + + +## <span id='invf'>Invocation</span> Functions and "debugger" Frames + +An <i>invocation function</i> is any function in this interface that allows +the debugger to invoke code in the debuggee: +`Debugger.Object.prototype.call`, `Debugger.Frame.prototype.eval`, and so +on. + +While invocation functions differ in the code to be run and how to pass +values to it, they all follow this general procedure: + +1. Let <i>older</i> be the youngest visible frame on the stack, or `null` + if there is no such frame. (This is never one of the the debugger's own + frames; those never appear as `Debugger.Frame` instances.) + +2. Push a `"debugger"` frame on the stack, with <i>older</i> as its + `older` property. + +3. Invoke the debuggee code as appropriate for the given invocation + function, with the `"debugger"` frame as its continuation. For example, + `Debugger.Frame.prototype.eval` pushes an `"eval"` frame for code it + runs, whereas `Debugger.Object.prototype.call` pushes a `"call"` frame. + +4. When the debuggee code completes, whether by returning, throwing an + exception or being terminated, pop the `"debugger"` frame, and return an + appropriate [completion value][cv] from the invocation function to the + debugger. + +When a debugger calls an invocation function to run debuggee code, that +code's continuation is the debugger, not the next debuggee code frame. +Pushing a `"debugger"` frame makes this continuation explicit, and makes it +easier to find the extent of the stack created for the invocation. + + +## Accessor Properties of the Debugger.Frame Prototype Object + +A `Debugger.Frame` instance inherits the following accessor properties from +its prototype: + +`type` +: A string describing what sort of frame this is: + + * `"call"`: a frame running a function call. (We may not be able to obtain + frames for calls to host functions.) + + * `"eval"`: a frame running code passed to `eval`. + + * `"global"`: a frame running global code (JavaScript that is neither of + the above). + + * `"module"`: a frame running code at the top level of a module. + + * `"debugger"`: a frame for a call to user code invoked by the debugger + (see the `eval` method below). + +`implementation` +: A string describing which tier of the JavaScript engine this frame is + executing in: + + * `"interpreter"`: a frame running in the interpreter. + + * `"baseline"`: a frame running in the unoptimizing, baseline JIT. + + * `"ion"`: a frame running in the optimizing JIT. + +`this` +: The value of `this` for this frame (a debuggee value). + +`older` +: The next-older visible frame, in which control will resume when this + frame completes. If there is no older frame, this is `null`. + +`depth` +: The depth of this frame, counting from oldest to youngest; the oldest + frame has a depth of zero. + +`live` +: True if the frame this `Debugger.Frame` instance refers to is still on + the stack; false if it has completed execution or been + popped in some other way. + +`script` +: The script being executed in this frame (a [`Debugger.Script`][script] + instance), or `null` on frames that do not represent calls to debuggee + code. On frames whose `callee` property is not null, this is equal to + `callee.script`. + +`offset` +: The offset of the bytecode instruction currently being executed in + `script`, or `undefined` if the frame's `script` property is `null`. + +`environment` +: The lexical environment within which evaluation is taking place (a + [`Debugger.Environment`][environment] instance), or `null` on frames + that do not represent the evaluation of debuggee code, like calls + non-debuggee functions, host functions or `"debugger"` frames. + +`callee` +: The function whose application created this frame, as a debuggee value, + or `null` if this is not a `"call"` frame. + +`generator` +: True if this frame is a generator frame, false otherwise. + +`constructing` +: True if this frame is for a function called as a constructor, false + otherwise. + +`arguments` +: The arguments passed to the current frame, or `null` if this is not a + `"call"` frame. When non-`null`, this is an object, allocated in the + same global as the debugger, with `Array.prototype` on its prototype + chain, a non-writable `length` property, and properties whose names are + array indices. Each property is a read-only accessor property whose + getter returns the current value of the corresponding parameter. When + the referent frame is popped, the argument value's properties' getters + throw an error. + + +## Handler Methods of Debugger.Frame Instances + +Each `Debugger.Frame` instance inherits accessor properties holding handler +functions for SpiderMonkey to call when given events occur in the frame. + +Calls to frames' handler methods are cross-compartment, intra-thread calls: +the call takes place in the thread to which the frame belongs, and runs in +the compartment to which the handler method belongs. + +`Debugger.Frame` instances inherit the following handler method properties: + +`onStep` +: This property must be either `undefined` or a function. If it is a + function, SpiderMonkey calls it when execution in this frame makes a + small amount of progress, passing no arguments and providing this + `Debugger.Frame` instance as the `this`value. The function should + return a [resumption value][rv] specifying how the debuggee's execution + should proceed. + + What constitutes "a small amount of progress" varies depending on the + implementation, but it is fine-grained enough to implement useful + "step" and "next" behavior. + + If multiple [`Debugger`][debugger-object] instances each have + `Debugger.Frame` instances for a given stack frame with `onStep` + handlers set, their handlers are run in an unspecified order. If any + `onStep` handler forces the frame to return early (by returning a + resumption value other than `undefined`), any remaining debuggers' + `onStep` handlers do not run. + + This property is ignored on frames that are not executing debuggee + code, like `"call"` frames for calls to host functions and `"debugger"` + frames. + +`onPop` +: This property must be either `undefined` or a function. If it is a + function, SpiderMonkey calls it just before this frame is popped, + passing a [completion value][cv] indicating how this frame's execution + completed, and providing this `Debugger.Frame` instance as the `this` + value. The function should return a [resumption value][rv] indicating + how execution should proceed. On newly created frames, this property's + value is `undefined`. + + When this handler is called, this frame's current execution location, as + reflected in its `offset` and `environment` properties, is the operation + which caused it to be unwound. In frames returning or throwing an + exception, the location is often a return or a throw statement. In frames + propagating exceptions, the location is a call. + + When an `onPop` call reports the completion of a construction call + (that is, a function called via the `new` operator), the completion + value passed to the handler describes the value returned by the + function body. If this value is not an object, it may be different from + the value produced by the `new` expression, which will be the value of + the frame's `this` property. (In ECMAScript terms, the `onPop` handler + receives the value returned by the `[[Call]]` method, not the value + returned by the `[[Construct]]` method.) + + When a debugger handler function forces a frame to complete early, by + returning a `{ return:... }`, `{ throw:... }`, or `null` resumption + value, SpiderMonkey calls the frame's `onPop` handler, if any. The + completion value passed in this case reflects the resumption value that + caused the frame to complete. + + When SpiderMonkey calls an `onPop` handler for a frame that is throwing + an exception or being terminated, and the handler returns `undefined`, + then SpiderMonkey proceeds with the exception or termination. That is, + an `undefined` resumption value leaves the frame's throwing and + termination process undisturbed. + + If multiple [`Debugger`][debugger-object] instances each have + `Debugger.Frame` instances for a given stack frame with `onPop` + handlers set, their handlers are run in an unspecified order. The + resumption value each handler returns establishes the completion value + reported to the next handler. + + This handler is not called on `"debugger"` frames. It is also not called + when unwinding a frame due to an over-recursion or out-of-memory + exception. + + +## Function Properties of the Debugger.Frame Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger.Frame` instance; they may not be used as +methods of other kinds of objects. + +<code id="eval">eval(<i>code</i>, [<i>options</i>])</code> +: Evaluate <i>code</i> in the execution context of this frame, and return + a [completion value][cv] describing how it completed. <i>Code</i> is a + string. If this frame's `environment` property is `null`, throw a + `TypeError`. All extant handler methods, breakpoints, and + so on remain active during the call. This function follows the + [invocation function conventions][inv fr]. + + <i>Code</i> is interpreted as strict mode code when it contains a Use + Strict Directive, or the code executing in this frame is strict mode + code. + + If <i>code</i> is not strict mode code, then variable declarations in + <i>code</i> affect the environment of this frame. (In the terms used by + the ECMAScript specification, the `VariableEnvironment` of the + execution context for the eval code is the `VariableEnvironment` of the + execution context that this frame represents.) If implementation + restrictions prevent SpiderMonkey from extending this frame's + environment as requested, this call throws an Error exception. + + If given, <i>options</i> should be an object whose properties specify + details of how the evaluation should occur. The `eval` method + recognizes the following properties: + + <code>url</code> + : The filename or URL to which we should attribute <i>code</i>. If this + property is omitted, the URL defaults to `"debugger eval code"`. + + <code>lineNumber</code> + : The line number at which the evaluated code should be claimed to begin + within <i>url</i>. + +<code>evalWithBindings(<i>code</i>, <i>bindings</i>, [<i>options</i>])</code> +: Like `eval`, but evaluate <i>code</i> in the environment of this frame, + extended with bindings from the object <i>bindings</i>. For each own + enumerable property of <i>bindings</i> named <i>name</i> whose value is + <i>value</i>, include a variable in the environment in which + <i>code</i> is evaluated named <i>name</i>, whose value is + <i>value</i>. Each <i>value</i> must be a debuggee value. (This is not + like a `with` statement: <i>code</i> may access, assign to, and delete + the introduced bindings without having any effect on the + <i>bindings</i> object.) + + This method allows debugger code to introduce temporary bindings that + are visible to the given debuggee code and which refer to debugger-held + debuggee values, and do so without mutating any existing debuggee + environment. + + Note that, like `eval`, declarations in the <i>code</i> passed to + `evalWithBindings` affect the environment of this frame, even as that + environment is extended by bindings visible within <i>code</i>. (In the + terms used by the ECMAScript specification, the `VariableEnvironment` + of the execution context for the eval code is the `VariableEnvironment` + of the execution context that this frame represents, and the + <i>bindings</i> appear in a new declarative environment, which is the + eval code's `LexicalEnvironment`.) If implementation restrictions + prevent SpiderMonkey from extending this frame's environment as + requested, this call throws an `Error` exception. + + The <i>options</i> argument is as for + [`Debugger.Frame.prototype.eval`][fr eval], described above. diff --git a/js/src/doc/Debugger/Debugger.Memory.md b/js/src/doc/Debugger/Debugger.Memory.md new file mode 100644 index 0000000000..a5ccd93cda --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Memory.md @@ -0,0 +1,549 @@ +Debugger.Memory +=============== + +The [`Debugger API`][debugger] can help tools observe the debuggee's memory use +in various ways: + +- It can mark each new object with the JavaScript call stack at which it was + allocated. + +- It can log all object allocations, yielding a stream of JavaScript call stacks + at which allocations have occurred. + +- It can compute a *census* of items belonging to the debuggee, categorizing + items in various ways, and yielding item counts. + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, then the methods and +accessor properties of <code><i>dbg</i>.memory</code> control how <i>dbg</i> +observes its debuggees' memory use. The <code><i>dbg</i>.memory</code> object is +an instance of `Debugger.Memory`; its inherited accesors and methods are +described below. + + +### Allocation Site Tracking + +The JavaScript engine marks each new object with the call stack at which it was +allocated, if: + +- the object is allocated in the scope of a global object that is a debuggee of + some [`Debugger`][debugger-object] instance <i>dbg</i>; and + +- <code><i>dbg</i>.memory.[trackingAllocationSites][tracking-allocs]</code> is + set to `true`. + +- A [Bernoulli trial][bernoulli-trial] succeeds, with probability equal to the + maximum of + [`d.memory.allocationSamplingProbability`][alloc-sampling-probability] of all + `Debugger` instances `d` that are observing the global that this object is + allocated within the scope of. + +Given a [`Debugger.Object`][object] instance <i>dobj</i> referring to some +object, <code><i>dobj</i>.[allocationSite][allocation-site]</code> returns a +[saved call stack][saved-frame] indicating where <i>dobj</i>'s referent was +allocated. + + +### Allocation Logging + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, and +<code><i>dbg</i>.memory.[trackingAllocationSites][tracking-allocs]</code> is set +to `true`, then the JavaScript engine logs each object allocated by <i>dbg</i>'s +debuggee code. You can retrieve the current log by calling +<code><i>dbg</i>.memory.[drainAllocationsLog][drain-alloc-log]</code>. You can +control the limit on the log's size by setting +<code><i>dbg</i>.memory.[maxAllocationsLogLength][max-alloc-log]</code>. + + +### Censuses + +A *census* is a complete traversal of the graph of all reachable memory items +belonging to a particular `Debugger`'s debuggees. It produces a count of those +items, broken down by various criteria. If <i>dbg</i> is a +[`Debugger`][debugger-object] instance, you can call +<code><i>dbg</i>.memory.[takeCensus][take-census]</code> to conduct a census of +its debuggees' possessions. + + +Accessor Properties of the `Debugger.Memory.prototype` Object +------------------------------------------------------------- + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, then +`<i>dbg</i>.memory` is a `Debugger.Memory` instance, which inherits the +following accessor properties from its prototype: + +<code id='trackingallocationsites'>trackingAllocationSites</code> +: A boolean value indicating whether this `Debugger.Memory` instance is + capturing the JavaScript execution stack when each Object is allocated. This + accessor property has both a getter and setter: assigning to it enables or + disables the allocation site tracking. Reading the accessor produces `true` + if the Debugger is capturing stacks for Object allocations, and `false` + otherwise. Allocation site tracking is initially disabled in a new Debugger. + + Assignment is fallible: if the Debugger cannot track allocation sites, it + throws an `Error` instance. + + You can retrieve the allocation site for a given object with the + [`Debugger.Object.prototype.allocationSite`][allocation-site] accessor + property. + +<code id='alloc-sampling-probability'>allocationSamplingProbability</code> +: A number between 0 and 1 that indicates the probability with which each new + allocation should be entered into the allocations log. 0 is equivalent to + "never", 1 is "always", and .05 would be "one out of twenty". + + The default is 1, or logging every allocation. + + Note that in the presence of multiple <code>Debugger</code> instances + observing the same allocations within a global's scope, the maximum + <code>allocationSamplingProbability</code> of all the + <code>Debugger</code>s is used. + +<code id='max-alloc-log'>maxAllocationsLogLength</code> +: The maximum number of allocation sites to accumulate in the allocations log + at a time. This accessor can be both fetched and stored to. Its default + value is `5000`. + +<code id='allocationsLogOverflowed'>allocationsLogOverflowed</code> +: Returns `true` if there have been more than + [`maxAllocationsLogLength`][#max-alloc-log] allocations since the last time + [`drainAllocationsLog`][#drain-alloc-log] was called and some data has been + lost. Returns `false` otherwise. + +Debugger.Memory Handler Functions +--------------------------------- + +Similar to [`Debugger`'s handler functions][debugger], `Debugger.Memory` +inherits accessor properties that store handler functions for SpiderMonkey to +call when given events occur in debuggee code. + +Unlike `Debugger`'s hooks, `Debugger.Memory`'s handlers' return values are not +significant, and are ignored. The handler functions receive the +`Debugger.Memory`'s owning `Debugger` instance as their `this` value. The owning +`Debugger`'s `uncaughtExceptionHandler` is still fired for errors thrown in +`Debugger.Memory` hooks. + +On a new `Debugger.Memory` instance, each of these properties is initially +`undefined`. Any value assigned to a debugging handler must be either a function +or `undefined`; otherwise a `TypeError` is thrown. + +Handler functions run in the same thread in which the event occurred. +They run in the compartment to which they belong, not in a debuggee +compartment. + +<code>onGarbageCollection(<i>statistics</i>)</code> +: A garbage collection cycle spanning one or more debuggees has just been + completed. + + The *statistics* parameter is an object containing information about the GC + cycle. It has the following properties: + + `collections` + : The `collections` property's value is an array. Because SpiderMonkey's + collector is incremental, a full collection cycle may consist of + multiple discrete collection slices with the JS mutator running + interleaved. For each collection slice that occurred, there is an entry + in the `collections` array with the following form: + + <pre class='language-js'><code> + { + "startTimestamp": <i>timestamp</i>, + "endTimestamp": <i>timestamp</i>, + } + </code></pre> + + Here the *timestamp* values are [timestamps][] of the GC slice's start + and end events. + + `reason` + : A very short string describing the reason why the collection was + triggered. Known values include the following: + + * "API" + * "EAGER_ALLOC_TRIGGER" + * "DESTROY_RUNTIME" + * "LAST_DITCH" + * "TOO_MUCH_MALLOC" + * "ALLOC_TRIGGER" + * "DEBUG_GC" + * "COMPARTMENT_REVIVED" + * "RESET" + * "OUT_OF_NURSERY" + * "EVICT_NURSERY" + * "FULL_STORE_BUFFER" + * "SHARED_MEMORY_LIMIT" + * "PERIODIC_FULL_GC" + * "INCREMENTAL_TOO_SLOW" + * "DOM_WINDOW_UTILS" + * "COMPONENT_UTILS" + * "MEM_PRESSURE" + * "CC_WAITING" + * "CC_FORCED" + * "LOAD_END" + * "PAGE_HIDE" + * "NSJSCONTEXT_DESTROY" + * "SET_NEW_DOCUMENT" + * "SET_DOC_SHELL" + * "DOM_UTILS" + * "DOM_IPC" + * "DOM_WORKER" + * "INTER_SLICE_GC" + * "REFRESH_FRAME" + * "FULL_GC_TIMER" + * "SHUTDOWN_CC" + * "FINISH_LARGE_EVALUATE" + * "USER_INACTIVE" + + `nonincrementalReason` + : If SpiderMonkey's collector determined it could not incrementally + collect garbage, and had to do a full GC all at once, this is a short + string describing the reason it determined the full GC was necessary. + Otherwise, `null` is returned. Known values include the following: + + * "GC mode" + * "malloc bytes trigger" + * "allocation trigger" + * "requested" + + `gcCycleNumber` + : The GC cycle's "number". Does not correspond to the number + of GC cycles that have run, but is guaranteed to be monotonically + increasing. + +Function Properties of the `Debugger.Memory.prototype` Object +------------------------------------------------------------- + +<code id='drain-alloc-log'>drainAllocationsLog()</code> +: When `trackingAllocationSites` is `true`, this method returns an array of + recent `Object` allocations within the set of debuggees. *Recent* is + defined as the `maxAllocationsLogLength` most recent `Object` allocations + since the last call to `drainAllocationsLog`. Therefore, calling this + method effectively clears the log. + + Objects in the array are of the form: + + <pre class='language-js'><code> + { + "timestamp": <i>timestamp</i>, + "frame": <i>allocationSite</i>, + "class": <i>className</i>, + "constructor": <i>constructorName</i>, + "size": <i>byteSize</i>, + "inNursery": <i>inNursery</i>, + } + </code></pre> + + Where + + * *timestamp* is the [timestamp][timestamps] of the allocation event. + + * *allocationSite* is an allocation site (as a + [captured stack][saved-frame]). Note that this property can be null if the + object was allocated with no JavaScript frames on the stack. + + * *className* is the string name of the allocated object's internal + `[[Class]]` property, for example "Array", "Date", "RegExp", or (most + commonly) "Object". + + * *constructorName* is the constructor function's display name for objects + created by `new Ctor`. If that data is not available, or the object was + not created with a `new` expression, this property is `null`. + + * *byteSize* is the size of the object in bytes. + + * *inNursery* is true if the allocation happened inside the nursery. False + if the allocation skipped the nursery and started in the tenured heap. + + When `trackingAllocationSites` is `false`, `drainAllocationsLog()` throws an + `Error`. + +<code id='take-census'>takeCensus(<i>options</i>)</code> +: Carry out a census of the debuggee compartments' contents. A *census* is a + complete traversal of the graph of all reachable memory items belonging to a + particular `Debugger`'s debuggees. The census produces a count of those + items, broken down by various criteria. + + The <i>options</i> argument is an object whose properties specify how the + census should be carried out. + + If <i>options</i> has a `breakdown` property, that determines how the census + categorizes the items it finds, and what data it collects about them. For + example, if `dbg` is a `Debugger` instance, the following performs a simple + count of debuggee items: + + dbg.memory.takeCensus({ breakdown: { by: 'count' } }) + + That might produce a result like: + + { "count": 1616, "bytes": 93240 } + + Here is a breakdown that groups JavaScript objects by their class name, and + non-string, non-script items by their C++ type name: + + { + by: "coarseType", + objects: { by: "objectClass" }, + other: { by: "internalType" } + } + + which produces a result like this: + + { + "objects": { + "Function": { "count": 404, "bytes": 37328 }, + "Object": { "count": 11, "bytes": 1264 }, + "Debugger": { "count": 1, "bytes": 416 }, + "ScriptSource": { "count": 1, "bytes": 64 }, + // ... omitted for brevity... + }, + "scripts": { "count": 1, "bytes": 0 }, + "strings": { "count": 701, "bytes": 49080 }, + "other": { + "js::Shape": { "count": 450, "bytes": 0 }, + "js::BaseShape": { "count": 21, "bytes": 0 }, + "js::ObjectGroup": { "count": 17, "bytes": 0 } + } + } + + In general, a `breakdown` value has one of the following forms: + + <code>{ by: "count", count:<i>count<i>, bytes:<i>bytes</i> }</code> + : The trivial categorization: none whatsoever. Simply tally up the items + visited. If <i>count</i> is true, count the number of items visited; if + <i>bytes</i> is true, total the number of bytes the items use directly. + Both <i>count</i> and <i>bytes</i> are optional; if omitted, they + default to `true`. In the result of the census, this breakdown produces + a value of the form: + + { "count":<i>n</b>, "bytes":<i>b</i> } + + where the `count` and `bytes` properties are present as directed by the + <i>count</i> and <i>bytes</i> properties on the breakdown. + + Note that the census can produce byte sizes only for the most common + types. When the census cannot find the byte size for a given type, it + returns zero. + + <code>{ by: "bucket" }</code> + : Do not do any filtering or categorizing. Instead, accumulate a bucket of + each node's ID for every node that matches. The resulting report is an + array of the IDs. + + For example, to find the ID of all nodes whose internal object + `[[class]]` property is named "RegExp", you could use the following code: + + const report = dbg.memory.takeCensus({ + breakdown: { + by: "objectClass", + then: { by: "bucket" } + } + }); + doStuffWithRegExpIDs(report.RegExp); + + <code>{ by: "allocationStack", then:<i>breakdown</i>, noStack:<i>noStackBreakdown</i> }</code> + : Group items by the full JavaScript stack trace at which they were + allocated. + + Further categorize all the items allocated at each distinct stack using + <i>breakdown</i>. + + In the result of the census, this breakdown produces a JavaScript `Map` + value whose keys are `SavedFrame` values, and whose values are whatever + sort of result <i>breakdown</i> produces. Objects allocated on an empty + JavaScript stack appear under the key `null`. + + SpiderMonkey only tracks allocation sites for items if requested via the + [`trackingAllocationSites`][tracking-allocs] flag; even then, it does + not record allocation sites for every kind of item that appears in the + heap. Items that lack allocation site information are counted using + <i>noStackBreakdown</i>. These appear in the result `Map` under the key + string `"noStack"`. + + <code>{ by: "objectClass", then:<i>breakdown</i>, other:<i>otherBreakdown</i> }</code> + : Group JavaScript objects by their ECMAScript `[[Class]]` internal property values. + + Further categorize JavaScript objects in each class using + <i>breakdown</i>. Further categorize items that are not JavaScript + objects using <i>otherBreakdown</i>. + + In the result of the census, this breakdown produces a JavaScript object + with no prototype whose own property names are strings naming classes, + and whose values are whatever sort of result <i>breakdown</i> produces. + The results for non-object items appear as the value of the property + named `"other"`. + + <code>{ by: "coarseType", objects:<i>objects</i>, scripts:<i>scripts</i>, strings:<i>strings</i>, other:<i>other</i> }</code> + : Group items by their coarse type. + + Use the breakdown value <i>objects</i> for items that are JavaScript + objects. + + Use the breakdown value <i>scripts</i> for items that are + representations of JavaScript code. This includes bytecode, compiled + machine code, and saved source code. + + Use the breakdown value <i>strings</i> for JavaScript strings. + + Use the breakdown value <i>other</i> for items that don't fit into any of + the above categories. + + In the result of the census, this breakdown produces a JavaScript object + of the form: + + <pre class='language-js'><code> + { + "objects": <i>result</i>, + "scripts": <i>result</i>, + "strings": <i>result</i>, + "other": <i>result</i> + } + </code></pre> + + where each <i>result</i> is a value of whatever sort the corresponding + breakdown value produces. All breakdown values are optional, and default + to `{ type: "count" }`. + + <code>{ by: "internalType", then:<i>breakdown</i> }</code> + : Group items by the names given their types internally by SpiderMonkey. + These names are not meaningful to web developers, but this type of + breakdown does serve as a catch-all that can be useful to Firefox tool + developers. + + For example, a census of a pristine debuggee global broken down by + internal type name typically looks like this: + + { + "JSString": { "count": 701, "bytes": 49080 }, + "js::Shape": { "count": 450, "bytes": 0 }, + "JSObject": { "count": 426, "bytes": 44160 }, + "js::BaseShape": { "count": 21, "bytes": 0 }, + "js::ObjectGroup": { "count": 17, "bytes": 0 }, + "JSScript": { "count": 1, "bytes": 0 } + } + + In the result of the census, this breakdown produces a JavaScript object + with no prototype whose own property names are strings naming types, + and whose values are whatever sort of result <i>breakdown</i> produces. + + <code>[ <i>breakdown</i>, ... ]</code> + : Group each item using all the given breakdown values. In the result of + the census, this breakdown produces an array of values of the sort + produced by each listed breakdown. + + To simplify breakdown values, all `then` and `other` properties are optional. + If omitted, they are treated as if they were `{ type: "count" }`. + + If the `options` argument has no `breakdown` property, `takeCensus` defaults + to the following: + + <pre class='language-js'><code> + { + by: "coarseType", + objects: { by: "objectClass" }, + other: { by: "internalType" } + } + </code></pre> + + which produces results of the form: + + <pre class='language-js'><code> + { + objects: { <i>class</i>: <i>count</i>, ... }, + scripts: <i>count</i>, + strings: <i>count</i>, + other: { <i>type name</i>: <i>count</i>, ... } + } + </code></pre> + + where each <i>count</i> has the form: + + <pre class='language-js'><code> + { "count": <i>count</i>, bytes:<i>bytes</i> } + </code></pre> + + Because performing a census requires traversing the entire graph of objects + in debuggee compartments, it is an expensive operation. On developer + hardware in 2014, traversing a memory graph containing roughly 130,000 nodes + and 410,000 edges took roughly 100ms. The traversal itself temporarily + allocates one hash table entry per node (roughly two address-sized words) in + addition to the per-category counts, whose size depends on the number of + categories. + + +Memory Use Analysis Exposes Implementation Details +-------------------------------------------------- + +Memory analysis may yield surprising results, because browser implementation +details that are transparent to content JavaScript often have visible effects on +memory consumption. Web developers need to know their pages' actual memory +consumption on real browsers, so it is correct for the tool to expose these +behaviors, as long as it is done in a way that helps developers make decisions +about their own code. + +This section covers some areas where Firefox's actual behavior deviates from +what one might expect from the specified behavior of the web platform. + + +### Objects + +SpiderMonkey objects usually use less memory than the naïve "table of properties +with attributes" model would suggest. For example, it is typical for many +objects to have identical sets of properties, with only the properties' values +varying from one object to the next. To take advantage of this regularity, +SpiderMonkey objects with identical sets of properties may share their property +metadata; only property values are stored directly in the object. + +Array objects may also be optimized, if the set of live indices is dense. + + +### Strings + +SpiderMonkey has three representations of strings: + +- Normal: the string's text is counted in its size. + +- Substring: the string is a substring of some other string, and points to that + string for its storage. This representation may result in a small string + retaining a very large string. However, the memory consumed by the string + itself is a small constant independent of its size, since it is simply a + reference to the larger string, a start position, and a length. + +- Concatenations: When asked to concatenate two strings, SpiderMonkey may elect + to delay copying the strings' data, and represent the result simply as a + pointer to the two original strings. Again, such a string retains other + strings, but the memory consumed by the string itself is a small constant + independent of its size, since it is simply a pair of pointers. + +SpiderMonkey converts strings from the more complex representations to the +simpler ones when it pleases. Such conversions usually increase memory +consumption. + +SpiderMonkey shares some strings amongst all web pages and browser JS. These +shared strings, called *atoms*, are not included in censuses' string counts. + + +### Scripts + +SpiderMonkey has a complex, hybrid representation of JavaScript code. There +are four representations kept in memory: + +- _Source code_. SpiderMonkey retains a copy of most JavaScript source code. + +- _Compressed source code_. SpiderMonkey compresses JavaScript source code, + and de-compresses it on demand. Heuristics determine how long to retain the + uncompressed code. + +- _Bytecode_. This is SpiderMonkey's parsed representation of JavaScript. + Bytecode can be interpreted directly, or used as input to a just-in-time + compiler. Source is parsed into bytecode on demand; functions that are never + called are never parsed. + +- _Machine code_. SpiderMonkey includes several just-in-time compilers, each of + which translates JavaScript source or bytecode to machine code. Heuristics + determine which code to compile, and which compiler to use. Machine code may + be dropped in response to memory pressure, and regenerated as needed. + +Furthermore, SpiderMonkey tracks which types of values have appeared in +variables and object properties. This type information can be large. + +In a census, all the various forms of JavaScript code are placed in the +`"script"` category. Type information is accounted to the `"types"` category. diff --git a/js/src/doc/Debugger/Debugger.Object.md b/js/src/doc/Debugger/Debugger.Object.md new file mode 100644 index 0000000000..ee1e588f49 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Object.md @@ -0,0 +1,559 @@ +# Debugger.Object + +A `Debugger.Object` instance represents an object in the debuggee, +providing reflection-oriented methods to inspect and modify its referent. +The referent's properties do not appear directly as properties of the +`Debugger.Object` instance; the debugger can access them only through +methods like `Debugger.Object.prototype.getOwnPropertyDescriptor` and +`Debugger.Object.prototype.defineProperty`, ensuring that the debugger will +not inadvertently invoke the referent's getters and setters. + +SpiderMonkey creates exactly one `Debugger.Object` instance for each +debuggee object it presents to a given [`Debugger`][debugger-object] +instance: if the debugger encounters the same object through two different +routes (perhaps two functions are called on the same object), SpiderMonkey +presents the same `Debugger.Object` instance to the debugger each time. +This means that the debugger can use the `==` operator to recognize when +two `Debugger.Object` instances refer to the same debuggee object, and +place its own properties on a `Debugger.Object` instance to store metadata +about particular debuggee objects. + +JavaScript code in different compartments can have different views of the +same object. For example, in Firefox, code in privileged compartments sees +content DOM element objects without redefinitions or extensions made to +that object's properties by content code. (In Firefox terminology, +privileged code sees the element through an "xray wrapper".) To ensure that +debugger code sees each object just as the debuggee would, each +`Debugger.Object` instance presents its referent as it would be seen from a +particular compartment. This "viewing compartment" is chosen to match the +way the debugger came across the referent. As a consequence, a single +[`Debugger`][debugger-object] instance may actually have several +`Debugger.Object` instances: one for each compartment from which the +referent is viewed. + +If more than one [`Debugger`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Object` instance for a given object. This allows the code using +each [`Debugger`][debugger-object] instance to place whatever properties it +likes on its own `Debugger.Object` instances, without worrying about +interfering with other debuggers. + +While most `Debugger.Object` instances are created by SpiderMonkey in the +process of exposing debuggee's behavior and state to the debugger, the +debugger can use `Debugger.Object.prototype.makeDebuggeeValue` to create +`Debugger.Object` instances for given debuggee objects, or use +`Debugger.Object.prototype.copy` and `Debugger.Object.prototype.create` to +create new objects in debuggee compartments, allocated as if by particular +debuggee globals. + +`Debugger.Object` instances protect their referents from the garbage +collector; as long as the `Debugger.Object` instance is live, the referent +remains live. This means that garbage collection has no visible effect on +`Debugger.Object` instances. + + +## Accessor Properties of the Debugger.Object prototype + +A `Debugger.Object` instance inherits the following accessor properties +from its prototype: + +`proto` +: The referent's prototype (as a new `Debugger.Object` instance), or + `null` if it has no prototype. + +`class` +: A string naming the ECMAScript `[[Class]]` of the referent. + +`callable` +: `true` if the referent is a callable object (such as a function or a + function proxy); false otherwise. + +`name` +: The name of the referent, if it is a named function. If the referent is + an anonymous function, or not a function at all, this is `undefined`. + + This accessor returns whatever name appeared after the `function` + keyword in the source code, regardless of whether the function is the + result of instantiating a function declaration (which binds the + function to its name in the enclosing scope) or evaluating a function + expression (which binds the function to its name only within the + function's body). + +`displayName` +: The referent's display name, if the referent is a function with a + display name. If the referent is not a function, or if it has no display + name, this is `undefined`. + + If a function has a given name, its display name is the same as its + given name. In this case, the `displayName` and `name` properties are + equal. + + If a function has no name, SpiderMonkey attempts to infer an appropriate + name for it given its context. For example: + + ```language-js + function f() {} // display name: f (the given name) + var g = function () {}; // display name: g + o.p = function () {}; // display name: o.p + var q = { + r: function () {} // display name: q.r + }; + ``` + + Note that the display name may not be a proper JavaScript identifier, + or even a proper expression: we attempt to find helpful names even when + the function is not immediately assigned as the value of some variable + or property. Thus, we use <code><i>a</i>/<i>b</i></code> to refer to + the <i>b</i> defined within <i>a</i>, and <code><i>a</i><</code> to + refer to a function that occurs somewhere within an expression that is + assigned to <i>a</i>. For example: + + ```language-js + function h() { + var i = function() {}; // display name: h/i + f(function () {}); // display name: h/< + } + var s = f(function () {}); // display name: s< + ``` + +`parameterNames` +: If the referent is a debuggee function, the names of the its parameters, + as an array of strings. If the referent is not a debuggee function, or + not a function at all, this is `undefined`. + + If the referent is a host function for which parameter names are not + available, return an array with one element per parameter, each of which + is `undefined`. + + If the referent is a function proxy, return an empty array. + + If the referent uses destructuring parameters, then the array's elements + reflect the structure of the parameters. For example, if the referent is + a function declared in this way: + + ```language-js + function f(a, [b, c], {d, e:f}) { ... } + ``` + + then this `Debugger.Object` instance's `parameterNames` property would + have the value: + + ```language-js + ["a", ["b", "c"], {d:"d", e:"f"}] + ``` + +`script` +: If the referent is a function that is debuggee code, this is that + function's script, as a [`Debugger.Script`][script] instance. If the + referent is a function proxy or not debuggee code, this is `undefined`. + +`environment` +: If the referent is a function that is debuggee code, a + [`Debugger.Environment`][environment] instance representing the lexical + environment enclosing the function when it was created. If the referent + is a function proxy or not debuggee code, this is `undefined`. + +`errorMessageName` +: If the referent is an error created with an engine internal message template + this is a string which is the name of the template; `undefined` otherwise. + +`errorLineNumber` +: If the referent is an Error object, this is the line number at which the + referent was created; `undefined` otherwise. + +`errorColumnNumber` +: If the referent is an Error object, this is the column number at which the + referent was created; `undefined` otherwise. + +`isBoundFunction` +: If the referent is a debuggee function, returns `true` if the referent is a + bound function; `false` otherwise. If the referent is not a debuggee + function, or not a function at all, returns `undefined` instead. + +`isArrowFunction` +: If the referent is a debuggee function, returns `true` if the referent is an + arrow function; `false` otherwise. If the referent is not a debuggee + function, or not a function at all, returns `undefined` instead. + +`isPromise` +: `true` if the referent is a Promise; `false` otherwise. + +`boundTargetFunction` +: If the referent is a bound debuggee function, this is its target function— + the function that was bound to a particular `this` object. If the referent + is either not a bound function, not a debuggee function, or not a function + at all, this is `undefined`. + +`boundThis` +: If the referent is a bound debuggee function, this is the `this` value it + was bound to. If the referent is either not a bound function, not a debuggee + function, or not a function at all, this is `undefined`. + +`boundArguments` +: If the referent is a bound debuggee function, this is an array (in the + Debugger object's compartment) that contains the debuggee values of the + `arguments` object it was bound to. If the referent is either not a bound + function, not a debuggee function, or not a function at all, this is + `undefined`. + +`isProxy` +: If the referent is a (scripted) proxy, either revoked or not, return `true`. + If the referent is not a (scripted) proxy, return `false`. + +`proxyTarget` +: If the referent is a non-revoked (scripted) proxy, return a `Debugger.Object` + instance referring to the ECMAScript `[[ProxyTarget]]` of the referent. + If the referent is a revoked (scripted) proxy, return `null`. + If the referent is not a (scripted) proxy, return `undefined`. + +`proxyHandler` +: If the referent is a non-revoked (scripted) proxy, return a `Debugger.Object` + instance referring to the ECMAScript `[[ProxyHandler]]` of the referent. + If the referent is a revoked (scripted) proxy, return `null`. + If the referent is not a (scripted) proxy, return `undefined`. + +`promiseState` +: If the referent is a [`Promise`][promise], return a string indicating + whether the [`Promise`][promise] is pending, or has been fulfilled or + rejected. This string takes one of the following values: + + * `"pending"`, if the [`Promise`][promise] is pending. + + * `"fulfilled"`, if the [`Promise`][promise] has been fulfilled. + + * `"rejected"`, if the [`Promise`][promise] has been rejected. + + If the referent is not a [`Promise`][promise], throw a `TypeError`. + +`promiseValue` +: Return a debuggee value representing the value the [`Promise`][promise] has + been fulfilled with. + + If the referent is not a [`Promise`][promise], or the [`Promise`][promise] + has not been fulfilled, throw a `TypeError`. + +`promiseReason` +: Return a debuggee value representing the value the [`Promise`][promise] has + been rejected with. + + If the referent is not a [`Promise`][promise], or the [`Promise`][promise] + has not been rejected, throw a `TypeError`. + +`promiseAllocationSite` +: If the referent is a [`Promise`][promise], this is the + [JavaScript execution stack][saved-frame] captured at the time of the + promise's allocation. This can return null if the promise was not + created from script. If the referent is not a [`Promise`][promise], throw + a `TypeError` exception. + +`promiseResolutionSite` +: If the referent is a [`Promise`][promise], this is the + [JavaScript execution stack][saved-frame] captured at the time of the + promise's resolution. This can return null if the promise was not + resolved by calling its `resolve` or `reject` resolving functions from + script. If the referent is not a [`Promise`][promise], throw a `TypeError` + exception. + +`promiseID` +: If the referent is a [`Promise`][promise], this is a process-unique + identifier for the [`Promise`][promise]. With e10s, the same id can + potentially be assigned to multiple [`Promise`][promise] instances, if + those instances were created in different processes. If the referent is + not a [`Promise`][promise], throw a `TypeError` exception. + +`promiseDependentPromises` +: If the referent is a [`Promise`][promise], this is an `Array` of + `Debugger.Objects` referring to the promises directly depending on the + referent [`Promise`][promise]. These are: + + 1) Return values of `then()` calls on the promise. + 2) Return values of `Promise.all()` if the referent [`Promise`][promise] + was passed in as one of the arguments. + 3) Return values of `Promise.race()` if the referent [`Promise`][promise] + was passed in as one of the arguments. + + Once a [`Promise`][promise] is settled, it will generally notify its + dependent promises and forget about them, so this is most useful on + *pending* promises. + + Note that the `Array` only contains the promises that directly depend on + the referent [`Promise`][promise]. It does not contain promises that depend + on promises that depend on the referent [`Promise`][promise]. + + If the referent is not a [`Promise`][promise], throw a `TypeError` + exception. + +`promiseLifetime` +: If the referent is a [`Promise`][promise], this is the number of + milliseconds elapsed since the [`Promise`][promise] was created. If the + referent is not a [`Promise`][promise], throw a `TypeError` exception. + +`promiseTimeToResolution` +: If the referent is a [`Promise`][promise], this is the number of + milliseconds elapsed between when the [`Promise`][promise] was created and + when it was resolved. If the referent hasn't been resolved or is not a + [`Promise`][promise], throw a `TypeError` exception. + +`global` +: A `Debugger.Object` instance referring to the global object in whose + scope the referent was allocated. This does not unwrap cross-compartment + wrappers: if the referent is a wrapper, the result refers to the + wrapper's global, not the wrapped object's global. The result refers to + the global directly, not via a wrapper. + +<code id="allocationsite">allocationSite</code> +: If [object allocation site tracking][tracking-allocs] was enabled when this + `Debugger.Object`'s referent was allocated, return the + [JavaScript execution stack][saved-frame] captured at the time of the + allocation. Otherwise, return `null`. + + +## Function Properties of the Debugger.Object prototype + +The functions described below may only be called with a `this` value +referring to a `Debugger.Object` instance; they may not be used as methods +of other kinds of objects. The descriptions use "referent" to mean "the +referent of this `Debugger.Object` instance". + +Unless otherwise specified, these methods are not +[invocation functions][inv fr]; if a call would cause debuggee code to run +(say, because it gets or sets an accessor property whose handler is +debuggee code, or because the referent is a proxy whose traps are debuggee +code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception. + +<code>getProperty(<i>name</i>)</code> +: Return the value of the referent's property named <i>name</i>, or + `undefined` if it has no such property. <i>Name</i> must be a string. + The result is a debuggee value. + +<code>setProperty(<i>name</i>, <i>value</i>)</code> +: Store <i>value</i> as the value of the referent's property named + <i>name</i>, creating the property if it does not exist. <i>Name</i> + must be a string; <i>value</i> must be a debuggee value. + +<code>getOwnPropertyDescriptor(<i>name</i>)</code> +: Return a property descriptor for the property named <i>name</i> of the + referent. If the referent has no such property, return `undefined`. + (This function behaves like the standard + `Object.getOwnPropertyDescriptor` function, except that the object being + inspected is implicit; the property descriptor returned is allocated as + if by code scoped to the debugger's global object (and is thus in the + debugger's compartment); and its `value`, `get`, and `set` properties, + if present, are debuggee values.) + +`getOwnPropertyNames()` +: Return an array of strings naming all the referent's own properties, as + if <code>Object.getOwnPropertyNames(<i>referent</i>)</code> had been + called in the debuggee, and the result copied in the scope of the + debugger's global object. + +`getOwnPropertySymbols()` +: Return an array of strings naming all the referent's own symbols, as + if <code>Object.getOwnPropertySymbols(<i>referent</i>)</code> had been + called in the debuggee, and the result copied in the scope of the + debugger's global object. + +<code>defineProperty(<i>name</i>, <i>attributes</i>)</code> +: Define a property on the referent named <i>name</i>, as described by + the property descriptor <i>descriptor</i>. Any `value`, `get`, and + `set` properties of <i>attributes</i> must be debuggee values. (This + function behaves like `Object.defineProperty`, except that the target + object is implicit, and in a different compartment from the function + and descriptor.) + +<code>defineProperties(<i>properties</i>)</code> +: Add the properties given by <i>properties</i> to the referent. (This + function behaves like `Object.defineProperties`, except that the target + object is implicit, and in a different compartment from the + <i>properties</i> argument.) + +<code>deleteProperty(<i>name</i>)</code> +: Remove the referent's property named <i>name</i>. Return true if the + property was successfully removed, or if the referent has no such + property. Return false if the property is non-configurable. + +`seal()` +: Prevent properties from being added to or deleted from the referent. + Return this `Debugger.Object` instance. (This function behaves like the + standard `Object.seal` function, except that the object to be sealed is + implicit and in a different compartment from the caller.) + +`freeze()` +: Prevent properties from being added to or deleted from the referent, and + mark each property as non-writable. Return this `Debugger.Object` + instance. (This function behaves like the standard `Object.freeze` + function, except that the object to be sealed is implicit and in a + different compartment from the caller.) + +`preventExtensions()` +: Prevent properties from being added to the referent. (This function + behaves like the standard `Object.preventExtensions` function, except + that the object to operate on is implicit and in a different compartment + from the caller.) + +`isSealed()` +: Return true if the referent is sealed—that is, if it is not extensible, + and all its properties have been marked as non-configurable. (This + function behaves like the standard `Object.isSealed` function, except + that the object inspected is implicit and in a different compartment + from the caller.) + +`isFrozen()` +: Return true if the referent is frozen—that is, if it is not extensible, + and all its properties have been marked as non-configurable and + read-only. (This function behaves like the standard `Object.isFrozen` + function, except that the object inspected is implicit and in a + different compartment from the caller.) + +`isExtensible()` +: Return true if the referent is extensible—that is, if it can have new + properties defined on it. (This function behaves like the standard + `Object.isExtensible` function, except that the object inspected is + implicit and in a different compartment from the caller.) + +<code>copy(<i>value</i>)</code> +: Apply the HTML5 "structured cloning" algorithm to create a copy of + <i>value</i> in the referent's global object (and thus in the referent's + compartment), and return a `Debugger.Object` instance referring to the + copy. + + Note that this returns primitive values unchanged. This means you can + use `Debugger.Object.prototype.copy` as a generic "debugger value to + debuggee value" conversion function—within the limitations of the + "structured cloning" algorithm. + +<code>create(<i>prototype</i>, [<i>properties</i>])</code> +: Create a new object in the referent's global (and thus in the + referent's compartment), and return a `Debugger.Object` referring to + it. The new object's prototype is <i>prototype</i>, which must be an + `Debugger.Object` instance. The new object's properties are as given by + <i>properties</i>, as if <i>properties</i> were passed to + `Debugger.Object.prototype.defineProperties`, with the new + `Debugger.Object` instance as the `this` value. + +<code>makeDebuggeeValue(<i>value</i>)</code> +: Return the debuggee value that represents <i>value</i> in the debuggee. + If <i>value</i> is a primitive, we return it unchanged; if <i>value</i> + is an object, we return the `Debugger.Object` instance representing + that object, wrapped appropriately for use in this `Debugger.Object`'s + referent's compartment. + + Note that, if <i>value</i> is an object, it need not be one allocated + in a debuggee global, nor even a debuggee compartment; it can be any + object the debugger wishes to use as a debuggee value. + + As described above, each `Debugger.Object` instance presents its + referent as viewed from a particular compartment. Given a + `Debugger.Object` instance <i>d</i> and an object <i>o</i>, the call + <code><i>d</i>.makeDebuggeeValue(<i>o</i>)</code> returns a + `Debugger.Object` instance that presents <i>o</i> as it would be seen + by code in <i>d</i>'s compartment. + +<code>decompile([<i>pretty</i>])</code> +: If the referent is a function that is debuggee code, return the + JavaScript source code for a function definition equivalent to the + referent function in its effect and result, as a string. If + <i>pretty</i> is present and true, produce indented code with line + breaks. If the referent is not a function that is debuggee code, return + `undefined`. + +<code>call(<i>this</i>, <i>argument</i>, ...)</code> +: If the referent is callable, call it with the given <i>this</i> value + and <i>argument</i> values, and return a [completion value][cv] + describing how the call completed. <i>This</i> should be a debuggee + value, or `{ asConstructor: true }` to invoke the referent as a + constructor, in which case SpiderMonkey provides an appropriate `this` + value itself. Each <i>argument</i> must be a debuggee value. All extant + handler methods, breakpoints, and so on remain active + during the call. If the referent is not callable, throw a `TypeError`. + This function follows the [invocation function conventions][inv fr]. + +<code>apply(<i>this</i>, <i>arguments</i>)</code> +: If the referent is callable, call it with the given <i>this</i> value + and the argument values in <i>arguments</i>, and return a + [completion value][cv] describing how the call completed. <i>This</i> + should be a debuggee value, or `{ asConstructor: true }` to invoke + <i>function</i> as a constructor, in which case SpiderMonkey provides + an appropriate `this` value itself. <i>Arguments</i> must either be an + array (in the debugger) of debuggee values, or `null` or `undefined`, + which are treated as an empty array. All extant handler methods, + breakpoints, and so on remain active during the call. If + the referent is not callable, throw a `TypeError`. This function + follows the [invocation function conventions][inv fr]. + +<code>executeInGlobal(<i>code</i>, [<i>options</i>])</code> +: If the referent is a global object, evaluate <i>code</i> in that global + environment, and return a [completion value][cv] describing how it completed. + <i>Code</i> is a string. All extant handler methods, breakpoints, + and so on remain active during the call. This function + follows the [invocation function conventions][inv fr]. + If the referent is not a global object, throw a `TypeError` exception. + + <i>Code</i> is interpreted as strict mode code when it contains a Use + Strict Directive. + + This evaluation is semantically equivalent to executing statements at the + global level, not an indirect eval. Regardless of <i>code</i> being strict + mode code, variable declarations in <i>code</i> affect the referent global + object. + + The <i>options</i> argument is as for [`Debugger.Frame.prototype.eval`][fr eval]. + +<code>executeInGlobalWithBindings(<i>code</i>, <i>bindings</i>, [<i>options</i>])</code> +: Like `executeInGlobal`, but evaluate <i>code</i> using the referent as the + variable object, but with a lexical environment extended with bindings + from the object <i>bindings</i>. For each own enumerable property of + <i>bindings</i> named <i>name</i> whose value is <i>value</i>, include a + variable in the lexical environment in which <i>code</i> is evaluated + named <i>name</i>, whose value is <i>value</i>. Each <i>value</i> must + be a debuggee value. (This is not like a `with` statement: <i>code</i> + may access, assign to, and delete the introduced bindings without having + any effect on the <i>bindings</i> object.) + + This method allows debugger code to introduce temporary bindings that + are visible to the given debuggee code and which refer to debugger-held + debuggee values, and do so without mutating any existing debuggee + environment. + + Note that, like `executeInGlobal`, any declarations it contains affect the + referent global object, even as <i>code</i> is evaluated in an environment + extended according to <i>bindings</i>. (In the terms used by the ECMAScript + specification, the `VariableEnvironment` of the execution context for + <i>code</i> is the referent, and the <i>bindings</i> appear in a new + declarative environment, which is the eval code's `LexicalEnvironment`.) + + The <i>options</i> argument is as for [`Debugger.Frame.prototype.eval`][fr eval]. + +`asEnvironment()` +: If the referent is a global object, return the [`Debugger.Environment`][environment] + instance representing the referent's global lexical scope. The global + lexical scope's enclosing scope is the global object. If the referent is + not a global object, throw a `TypeError`. + +`unwrap()` +: If the referent is a wrapper that this `Debugger.Object`'s compartment + is permitted to unwrap, return a `Debugger.Object` instance referring to + the wrapped object. If we are not permitted to unwrap the referent, + return `null`. If the referent is not a wrapper, return this + `Debugger.Object` instance unchanged. + +`unsafeDereference()` +: Return the referent of this `Debugger.Object` instance. + + If the referent is an inner object (say, an HTML5 `Window` object), + return the corresponding outer object (say, the HTML5 `WindowProxy` + object). This makes `unsafeDereference` more useful in producing values + appropriate for direct use by debuggee code, without using [invocation functions][inv fr]. + + This method pierces the membrane of `Debugger.Object` instances meant to + protect debugger code from debuggee code, and allows debugger code to + access debuggee objects through the standard cross-compartment wrappers, + rather than via `Debugger.Object`'s reflection-oriented interfaces. This + method makes it easier to gradually adapt large code bases to this + Debugger API: adapted portions of the code can use `Debugger.Object` + instances, but use this method to pass direct object references to code + that has not yet been updated. + +<code>forceLexicalInitializationByName(<i>binding</i>)</code> +: If <i>binding</i> is in an uninitialized state initialize it to undefined + and return true, otherwise do nothing and return false. diff --git a/js/src/doc/Debugger/Debugger.Script.md b/js/src/doc/Debugger/Debugger.Script.md new file mode 100644 index 0000000000..65eac90262 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Script.md @@ -0,0 +1,379 @@ +# Debugger.Script + +A `Debugger.Script` instance may refer to a sequence of bytecode in the +debuggee or to a block of WebAssembly code. For the former, it is the +[`Debugger`][debugger-object] API's presentation of a JSAPI `JSScript` +object. The two cases are distinguished by their `format` property being +`"js"` or `"wasm"`. + +## Debugger.Script for JSScripts + +For `Debugger.Script` instances referring to a `JSScript`, they are +distinguished by their `format` property being `"js"`. + +Each of the following is represented by a single `JSScript` object: + +* The body of a function—that is, all the code in the function that is not + contained within some nested function. + +* The code passed to a single call to `eval`, excluding the bodies of any + functions that code defines. + +* The contents of a `<script>` element. + +* A DOM event handler, whether embedded in HTML or attached to the element + by other JavaScript code. + +* Code appearing in a `javascript:` URL. + +The [`Debugger`][debugger-object] interface constructs `Debugger.Script` objects as scripts +of debuggee code are uncovered by the debugger: via the `onNewScript` +handler method; via [`Debugger.Frame`][frame]'s `script` properties; via the +`functionScript` method of [`Debugger.Object`][object] instances; and so on. For a +given [`Debugger`][debugger-object] instance, SpiderMonkey constructs exactly one +`Debugger.Script` instance for each underlying script object; debugger +code can add its own properties to a script object and expect to find +them later, use `==` to decide whether two expressions refer to the same +script, and so on. + +(If more than one [`Debugger`][debugger-object] instance is debugging the same code, each +[`Debugger`][debugger-object] gets a separate `Debugger.Script` instance for a given +script. This allows the code using each [`Debugger`][debugger-object] instance to place +whatever properties it likes on its `Debugger.Script` instances, without +worrying about interfering with other debuggers.) + +A `Debugger.Script` instance is a strong reference to a JSScript object; +it protects the script it refers to from being garbage collected. + +Note that SpiderMonkey may use the same `Debugger.Script` instances for +equivalent functions or evaluated code—that is, scripts representing the +same source code, at the same position in the same source file, +evaluated in the same lexical environment. + +## Debugger.Script for WebAssembly + +For `Debugger.Script` instances referring to a block of WebAssembly code, they +are distinguished by their `format` property being `"wasm"`. + +Currently only entire modules evaluated via `new WebAssembly.Module` are +represented. + +`Debugger.Script` objects for WebAssembly are uncovered via `onNewScript` when +a new WebAssembly module is instantiated and via the `findScripts` method on +[`Debugger`][debugger-object] instances. SpiderMonkey constructs exactly one +`Debugger.Script` for each underlying WebAssembly module per +[`Debugger`][debugger-object] instance. + +A `Debugger.Script` instance is a strong reference to the underlying +WebAssembly module; it protects the module it refers to from being garbage +collected. + +Please note at the time of this writing, support for WebAssembly is +very preliminary. Many properties and methods below throw. + +## Convention + +For descriptions of properties and methods below, if the behavior of the +property or method differs between the instance referring to a `JSScript` or +to a block of WebAssembly code, the text will be split into two sections, +headed by "**if the instance refers to a `JSScript`**" and "**if the instance +refers to WebAssembly code**", respectively. If the behavior does not differ, +no such emphasized headings will appear. + +## Accessor Properties of the Debugger.Script Prototype Object + +A `Debugger.Script` instance inherits the following accessor properties +from its prototype: + +`displayName` +: **If the instance refers to a `JSScript`**, this is the script's display + name, if it has one. If the script has no display name — for example, + if it is a top-level `eval` script — this is `undefined`. + + If the script's function has a given name, its display name is the same as + its function's given name. + + If the script's function has no name, SpiderMonkey attempts to infer an + appropriate name for it given its context. For example: + + ```language-js + function f() {} // display name: f (the given name) + var g = function () {}; // display name: g + o.p = function () {}; // display name: o.p + var q = { + r: function () {} // display name: q.r + }; + ``` + + Note that the display name may not be a proper JavaScript identifier, + or even a proper expression: we attempt to find helpful names even when + the function is not immediately assigned as the value of some variable + or property. Thus, we use <code><i>a</i>/<i>b</i></code> to refer to + the <i>b</i> defined within <i>a</i>, and <code><i>a</i><</code> to + refer to a function that occurs somewhere within an expression that is + assigned to <i>a</i>. For example: + + ```language-js + function h() { + var i = function() {}; // display name: h/i + f(function () {}); // display name: h/< + } + var s = f(function () {}); // display name: s< + ``` + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`url` +: **If the instance refers to a `JSScript`**, the filename or URL from which + this script's code was loaded. If the `source` property is non-`null`, + then this is equal to `source.url`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`startLine` +: **If the instance refers to a `JSScript`**, the number of the line at + which this script's code starts, within the file or document named by + `url`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`lineCount` +: **If the instance refers to a `JSScript`**, the number of lines this + script's code occupies, within the file or document named by `url`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`source` +: **If the instance refers to a `JSScript`**, the + [`Debugger.Source`][source] instance representing the source code from + which this script was produced. This is `null` if the source code was not + retained. + + **If the instance refers to WebAssembly code**, the + [`Debugger.Source`][source] instance representing the serialized text + format of the WebAssembly code. + +`sourceStart` +: **If the instance refers to a `JSScript`**, the character within the + [`Debugger.Source`][source] instance given by `source` at which this + script's code starts; zero-based. If this is a function's script, this is + the index of the start of the `function` token in the source code. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`sourceLength` +: **If the instance refers to a `JSScript`**, the length, in characters, of + this script's code within the [`Debugger.Source`][source] instance given + by `source`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`global` +: **If the instance refers to a `JSScript`**, a [`Debugger.Object`][object] + instance referring to the global object in whose scope this script + runs. The result refers to the global directly, not via a wrapper or a + `WindowProxy` ("outer window", in Firefox). + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`format` +: **If the instance refers to a `JSScript`**, `"js"`. + + **If the instance refers to WebAssembly code**, `"wasm"`. + +## Function Properties of the Debugger.Script Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger.Script` instance; they may not be used as +methods of other kinds of objects. + +`getAllOffsets()` +: **If the instance refers to a `JSScript`**, return an array <i>L</i> + describing the relationship between bytecode instruction offsets and + source code positions in this script. <i>L</i> is sparse, and indexed by + source line number. If a source line number <i>line</i> has no code, then + <i>L</i> has no <i>line</i> property. If there is code for <i>line</i>, + then <code><i>L</i>[<i>line</i>]</code> is an array of offsets of byte + code instructions that are entry points to that line. + + For example, suppose we have a script for the following source code: + + ```language-js + a=[] + for (i=1; i < 10; i++) + // It's hip to be square. + a[i] = i*i; + ``` + + Calling `getAllOffsets()` on that code might yield an array like this: + + ```language-js + [[0], [5, 20], , [10]] + ``` + + This array indicates that: + + * the first line's code starts at offset 0 in the script; + + * the `for` statement head has two entry points at offsets 5 and 20 (for + the initialization, which is performed only once, and the loop test, + which is performed at the start of each iteration); + + * the third line has no code; + + * and the fourth line begins at offset 10. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`getAllColumnOffsets()`: +: **If the instance refers to a `JSScript`**, return an array describing the + relationship between bytecode instruction offsets and source code + positions in this script. Unlike getAllOffsets(), which returns all + offsets that are entry points for each line, getAllColumnOffsets() returns + all offsets that are entry points for each (line, column) pair. + + The elements of the array are objects, each of which describes a single + entry point, and contains the following properties: + + * lineNumber: the line number for which offset is an entry point + + * columnNumber: the column number for which offset is an entry point + + * offset: the bytecode instruction offset of the entry point + + For example, suppose we have a script for the following source code: + + ```language-js + a=[] + for (i=1; i < 10; i++) + // It's hip to be square. + a[i] = i*i; + ``` + + Calling `getAllColumnOffsets()` on that code might yield an array like this: + + ```language-js + [{ lineNumber: 0, columnNumber: 0, offset: 0 }, + { lineNumber: 1, columnNumber: 5, offset: 5 }, + { lineNumber: 1, columnNumber: 10, offset: 20 }, + { lineNumber: 3, columnNumber: 4, offset: 10 }] + ``` + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>getLineOffsets(<i>line</i>)</code> +: **If the instance refers to a `JSScript`**, return an array of bytecode + instruction offsets representing the entry points to source line + <i>line</i>. If the script contains no executable code at that line, the + array returned is empty. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>getOffsetLocation(<i>offset</i>)</code> +: **If the instance refers to a `JSScript`**, return an object describing the + source code location responsible for the bytecode at <i>offset</i> in this + script. The object has the following properties: + + * lineNumber: the line number for which offset is an entry point + + * columnNumber: the column number for which offset is an entry point + + * isEntryPoint: true if the offset is a column entry point, as + would be reported by getAllColumnOffsets(); otherwise false. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`getOffsetsCoverage()`: +: **If the instance refers to a `JSScript`**, return `null` or an array which + contains informations about the coverage of all opcodes. The elements of + the array are objects, each of which describes a single opcode, and + contains the following properties: + + * lineNumber: the line number of the current opcode. + + * columnNumber: the column number of the current opcode. + + * offset: the bytecode instruction offset of the current opcode. + + * count: the number of times the current opcode got executed. + + If this script has no coverage, or if it is not instrumented, then this + function will return `null`. To ensure that the debuggee is instrumented, + the flag `Debugger.collectCoverageInfo` should be set to `true`. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +`getChildScripts()` +: **If the instance refers to a `JSScript`**, return a new array whose + elements are Debugger.Script objects for each function + in this script. Only direct children are included; nested + children can be reached by walking the tree. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>setBreakpoint(<i>offset</i>, <i>handler</i>)</code> +: **If the instance refers to a `JSScript`**, set a breakpoint at the + bytecode instruction at <i>offset</i> in this script, reporting hits to + the `hit` method of <i>handler</i>. If <i>offset</i> is not a valid offset + in this script, throw an error. + + When execution reaches the given instruction, SpiderMonkey calls the + `hit` method of <i>handler</i>, passing a [`Debugger.Frame`][frame] + instance representing the currently executing stack frame. The `hit` + method's return value should be a [resumption value][rv], determining + how execution should continue. + + Any number of breakpoints may be set at a single location; when control + reaches that point, SpiderMonkey calls their handlers in an unspecified + order. + + Any number of breakpoints may use the same <i>handler</i> object. + + Breakpoint handler method calls are cross-compartment, intra-thread + calls: the call takes place in the same thread that hit the breakpoint, + and in the compartment containing the handler function (typically the + debugger's compartment). + + The new breakpoint belongs to the [`Debugger`][debugger-object] instance to + which this script belongs. Disabling the [`Debugger`][debugger-object] + instance disables this breakpoint; and removing a global from the + [`Debugger`][debugger-object] instance's set of debuggees clears all the + breakpoints belonging to that [`Debugger`][debugger-object] instance in that + global's scripts. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>getBreakpoints([<i>offset</i>])</code> +: **If the instance refers to a `JSScript`**, return an array containing the + handler objects for all the breakpoints set at <i>offset</i> in this + script. If <i>offset</i> is omitted, return the handlers of all + breakpoints set anywhere in this script. If <i>offset</i> is present, but + not a valid offset in this script, throw an error. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>clearBreakpoints(handler, [<i>offset</i>])</code> +: **If the instance refers to a `JSScript`**, remove all breakpoints set in + this [`Debugger`][debugger-object] instance that use <i>handler</i> as + their handler. If <i>offset</i> is given, remove only those breakpoints + set at <i>offset</i> that use <i>handler</i>; if <i>offset</i> is not a + valid offset in this script, throw an error. + + Note that, if breakpoints using other handler objects are set at the + same location(s) as <i>handler</i>, they remain in place. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>clearAllBreakpoints([<i>offset</i>])</code> +: **If the instance refers to a `JSScript`**, remove all breakpoints set in + this script. If <i>offset</i> is present, remove all breakpoints set at + that offset in this script; if <i>offset</i> is not a valid bytecode + offset in this script, throw an error. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. + +<code>isInCatchScope([<i>offset</i>])</code> +: **If the instance refers to a `JSScript`**, this is `true` if this offset + falls within the scope of a try block, and `false` otherwise. + + **If the instance refers to WebAssembly code**, throw a `TypeError`. diff --git a/js/src/doc/Debugger/Debugger.Source.md b/js/src/doc/Debugger/Debugger.Source.md new file mode 100644 index 0000000000..6a5535b742 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Source.md @@ -0,0 +1,220 @@ +# Debugger.Source + +A `Debugger.Source` instance represents either a piece of JavaScript source +code or the serialized text of a block of WebAssembly code. The two cases are +distinguished by the latter having its `introductionType` property always +being `"wasm"` and the former having its `introductionType` property never +being `"wasm"`. + +Each [`Debugger`][debugger-object] instance has a separate collection of +`Debugger.Source` instances representing the source code that has been +presented to the system. + +A debugger may place its own properties on `Debugger.Source` instances, +to store metadata about particular pieces of source code. + +## Debugger.Source for JavaScript + +For a `Debugger.Source` instance representing a piece of JavaScript source +code, its properties provide the source code itself as a string, and describe +where it came from. Each [`Debugger.Script`][script] instance refers to the +`Debugger.Source` instance holding the source code from which it was produced. + +If a single piece of source code contains both top-level code and +function definitions, perhaps with nested functions, then the +[`Debugger.Script`][script] instances for those all refer to the same +`Debugger.Source` instance. Each script indicates the substring of the +overall source to which it corresponds. + +A `Debugger.Source` instance may represent only a portion of a larger +source document. For example, an HTML document can contain JavaScript in +multiple `<script>` elements and event handler content attributes. +In this case, there may be either a single `Debugger.Source` instance +for the entire HTML document, with each [`Debugger.Script`][script] referring to +its substring of the document; or there may be a separate +`Debugger.Source` instance for each `<script>` element and +attribute. The choice is left up to the implementation. + +If a given piece of source code is presented to the JavaScript +implementation more than once, with the same origin metadata, the +JavaScript implementation may generate a fresh `Debugger.Source` +instance to represent each presentation, or it may use a single +`Debugger.Source` instance to represent them all. + +## Debugger.Source for WebAssembly + +For a `Debugger.Source` instance representing the serialized text of a block +of WebAssembly code, its properties provide the serialized text as a string. + +Currently only entire modules evaluated via `new WebAssembly.Module` are +represented. SpiderMonkey constructs exactly one `Debugger.Source` for each +underlying WebAssembly module per [`Debugger`][debugger-object] instance. + +Please note at the time of this writing, support for WebAssembly is very +preliminary. Many properties below return placeholder values. + +## Convention + +For descriptions of properties and methods below, if the behavior of the +property or method differs between the instance referring to JavaScript source +or to a block of WebAssembly code, the text will be split into two sections, +headed by "**if the instance refers to JavaScript source**" and "**if the +instance refers to WebAssembly code**", respectively. If the behavior does not +differ, no such emphasized headings will appear. + +## Accessor Properties of the Debugger.Source Prototype Object + +A `Debugger.Source` instance inherits the following accessor properties +from its prototype: + +`text` +: **If the instance refers to JavaScript source**, the JavaScript source + code, as a string. The value satisfies the `Program`, + `FunctionDeclaration`, or `FunctionExpression` productions in the + ECMAScript standard. + + **If the instance refers to WebAssembly code**, the serialized text + representation. The format is yet to be specified in the WebAssembly + standard. Currently, the text is an s-expression based syntax. + +`url` +: **If the instance refers to JavaScript source**, the URL from which this + source was loaded, if this source was loaded from a URL. Otherwise, this + is `undefined`. Source may be loaded from a URL in the following ways: + + * The URL may appear as the `src` attribute of a `<script>` element + in markup text. + + * The URL may be passed to the `Worker` web worker constructor, or the web + worker `importScripts` function. + + * The URL may be the name of a XPCOM JavaScript module or subscript. + + (Note that code passed to `eval`, the `Function` constructor, or a + similar function is <i>not</i> considered to be loaded from a URL; the + `url` accessor on `Debugger.Source` instances for such sources should + return `undefined`.) + + **If the instance refers to WebAssembly code**, the URL of the script that + called `new WebAssembly.Module` with the string `"> wasm"` appended. + +`sourceMapURL` +: **If the instance refers to JavaScript source**, if this source was + produced by a minimizer or translated from some other language, and we + know the URL of a <b>source map</b> document relating the source positions + in this source to the corresponding source positions in the original + source, then this property's value is that URL. Otherwise, this is `null`. + + (On the web, the translator may provide the source map URL in a + specially formatted comment in the JavaScript source code, or via a + header in the HTTP reply that carried the generated JavaScript.) + + This property is writable, so you can change the source map URL by + setting it. All Debugger.Source objects referencing the same + source will see the change. Setting an empty string has no affect + and will not change existing value. + + **If the instance refers to WebAssembly code**, `null`. Attempts to write + to this property throw a `TypeError`. + +`element` +: The [`Debugger.Object`][object] instance referring to the DOM element to which + this source code belongs, if any, or `undefined` if it belongs to no DOM + element. Source belongs to a DOM element in the following cases: + + * Source belongs to a `<script>` element if it is the element's text + content (that is, it is written out as the body of the `<script>` + element in the markup text), or is the source document referenced by its + `src` attribute. + + * Source belongs to a DOM element if it is an event handler content + attribute (that is, if it is written out in the markup text as an + attribute value). + + * Source belongs to a DOM element if it was assigned to one of the + element's event handler IDL attributes as a string. (Note that one may + assign both strings and functions to DOM elements' event handler IDL + attributes. If one assigns a function, that function's script's source + does <i>not</i> belong to the DOM element; the function's definition + must appear elsewhere.) + + (If the sources attached to a DOM element change, the `Debugger.Source` + instances representing superceded code still refer to the DOM element; + this accessor only reflects origins, not current relationships.) + +`elementAttributeName` +: If this source belongs to a DOM element because it is an event handler + content attribute or an event handler IDL attribute, this is the name of + that attribute, a string. Otherwise, this is `undefined`. + +`introductionType` +: **If the instance refers to JavaScript source**, a string indicating how + this source code was introduced into the system. This accessor returns + one of the following values: + + * `"eval"`, for code passed to `eval`. + + * `"Function"`, for code passed to the `Function` constructor. + + * `"Worker"`, for code loaded by calling the Web worker constructor—the + worker's main script. + + * `"importScripts"`, for code by calling `importScripts` in a web worker. + + * `"eventHandler"`, for code assigned to DOM elements' event handler IDL + attributes as a string. + + * `"scriptElement"`, for code belonging to `<script>` elements. + + * `"javascriptURL"`, for code presented in `javascript:` URLs. + + * `"setTimeout"`, for code passed to `setTimeout` as a string. + + * `"setInterval"`, for code passed to `setInterval` as a string. + + * `undefined`, if the implementation doesn't know how the code was + introduced. + + **If the instance refers to WebAssembly code**, `"wasm"`. + +`introductionScript`, `introductionOffset` +: **If the instance refers to JavaScript source**, and if this source was + introduced by calling a function from debuggee code, then + `introductionScript` is the [`Debugger.Script`][script] instance referring + to the script containing that call, and `introductionOffset` is the call's + bytecode offset within that script. Otherwise, these are both `undefined`. + Taken together, these properties indicate the location of the introducing + call. + + For the purposes of these accessors, assignments to accessor properties are + treated as function calls. Thus, setting a DOM element's event handler IDL + attribute by assigning to the corresponding JavaScript property creates a + source whose `introductionScript` and `introductionOffset` refer to the + property assignment. + + Since a `<script>` element parsed from a web page's original HTML was not + introduced by any scripted call, its source's `introductionScript` and + `introductionOffset` accessors both return `undefined`. + + If a `<script>` element was dynamically inserted into a document, then these + accessors refer to the call that actually caused the script to run—usually + the call that made the element part of the document. Thus, they do + <i>not</i> refer to the call that created the element; stored the source as + the element's text child; made the element a child of some uninserted parent + node that was later inserted; or the like. + + Although the main script of a worker thread is introduced by a call to + `Worker` or `SharedWorker`, these accessors always return `undefined` on + such script's sources. A worker's main script source and the call that + created the worker are always in separate threads, but + [`Debugger`][debugger-object] is an inherently single-threaded facility: its + debuggees must all run in the same thread. Since the global that created the + worker is in a different thread, it is guaranteed not to be a debuggee of + the [`Debugger`][debugger-object] instance that owns this source; and thus + the creating call is never "in debuggee code". Relating a worker to its + creator, and other multi-threaded debugging concerns, are out of scope for + [`Debugger`][debugger-object]. + + **If the instance refers to WebAssembly code**, `introductionScript` is + the [`Debugger.Script`][script] instance referring to the same underlying + WebAssembly module. `introductionOffset` is `undefined`. diff --git a/js/src/doc/Debugger/Debugger.md b/js/src/doc/Debugger/Debugger.md new file mode 100644 index 0000000000..ebb75d789f --- /dev/null +++ b/js/src/doc/Debugger/Debugger.md @@ -0,0 +1,496 @@ +# The Debugger Object + +When called as a constructor, the `Debugger` object creates a new +`Debugger` instance. + +<code>new Debugger([<i>global</i>, ...])</code> +: Create a debugger object, and apply its [`addDebuggee`][add] method to + each of the given <i>global</i> objects to add them as the initial + debuggees. + +## Accessor Properties of the Debugger Prototype Object + +A `Debugger` instance inherits the following accessor properties from +its prototype: + +`enabled` +: A boolean value indicating whether this `Debugger` instance's handlers, + breakpoints, and the like are currently enabled. It is an + accessor property with a getter and setter: assigning to it enables or + disables this `Debugger` instance; reading it produces true if the + instance is enabled, or false otherwise. This property is initially + `true` in a freshly created `Debugger` instance. + + This property gives debugger code a single point of control for + disentangling itself from the debuggee, regardless of what sort of + events or handlers or "points" we add to the interface. + +`allowUnobservedAsmJS` +: A boolean value indicating whether asm.js code running inside this + `Debugger` instance's debuggee globals is invisible to Debugger API + handlers and breakpoints. Setting this to `false` inhibits the + ahead-of-time asm.js compiler and forces asm.js code to run as normal + JavaScript. This is an accessor property with a getter and setter. It is + initially `false` in a freshly created `Debugger` instance. + + Setting this flag to `true` is intended for uses of subsystems of the + Debugger API (e.g, [`Debugger.Source`][source]) for purposes other than + step debugging a target JavaScript program. + +`collectCoverageInfo` +: A boolean value indicating whether code coverage should be enabled inside + each debuggee of this `Debugger` instance. Changing this flag value will + recompile all JIT code to add or remove code coverage + instrumentation. Changing this flag when any frame of the debuggee is + currently active on the stack will produce an exception. + + Setting this to `true` enables code coverage instrumentation, which can be + accessed via the [`Debugger.Script`][script] `getOffsetsCoverage` + function. In some cases, the code coverage might expose information which + pre-date the modification of this flag. Code coverage reports are monotone, + thus one can take a snapshot when the Debugger is enabled, and output the + difference. + + Setting this to `false` prevents this `Debugger` instance from requiring any + code coverage instrumentation, but it does not guarantee that the + instrumentation is not present. + +`uncaughtExceptionHook` +: Either `null` or a function that SpiderMonkey calls when a call to a + debug event handler, breakpoint handler, or similar + function throws some exception, which we refer to as + <i>debugger-exception</i> here. Exceptions thrown in the debugger are + not propagated to debuggee code; instead, SpiderMonkey calls this + function, passing <i>debugger-exception</i> as its sole argument and + the `Debugger` instance as the `this` value. This function should + return a [resumption value][rv], which determines how the debuggee + should continue. + + If the uncaught exception hook itself throws an exception, + <i>uncaught-hook-exception</i>, SpiderMonkey throws a new error object, + <i>confess-to-debuggee-exception</i>, to the debuggee whose message + blames the debugger, and includes textual descriptions of + <i>uncaught-hook-exception</i> and the original + <i>debugger-exception</i>. + + If `uncaughtExceptionHook`'s value is `null`, SpiderMonkey throws an + exception to the debuggee whose message blames the debugger, and + includes a textual description of <i>debugger-exception</i>. + + Assigning anything other than a callable value or `null` to this + property throws a `TypeError` exception. + + (This is not an ideal way to handle debugger bugs, but the hope here is + that some sort of backstop, even if imperfect, will make life easier for + debugger developers. For example, an uncaught exception hook may have + access to browser-level features like the `alert` function, which this + API's implementation does not, making it possible to present debugger + errors to the developer in a way suited to the context.) + + +## Debugger Handler Functions + +Each `Debugger` instance inherits accessor properties with which you can +store handler functions for SpiderMonkey to call when given events occur +in debuggee code. + +When one of the events described below occurs in debuggee code, the engine +pauses the debuggee and calls the corresponding debugging handler on each +`Debugger` instance that is observing the debuggee. The handler functions +receive the `Debugger` instance as their `this` value. Most handler +functions can return a [resumption value][rv] indicating how the debuggee's +execution should proceed. + +On a new `Debugger` instance, each of these properties is initially +`undefined`. Any value assigned to a debugging handler must be either a +function or `undefined`; otherwise a `TypeError` is thrown. + +Handler functions run in the same thread in which the event occurred. +They run in the compartment to which they belong, not in a debuggee +compartment. + +<code>onNewScript(<i>script</i>, <i>global</i>)</code> +: New code, represented by the [`Debugger.Script`][script] instance + <i>script</i>, has been loaded in the scope of the debuggees. + + This method's return value is ignored. + +<code>onNewPromise(<i>promise</i>)</code> +: A new Promise object, referenced by the [`Debugger.Object`][object] instance + *promise*, has been allocated in the scope of the debuggees. The Promise's + allocation stack can be obtained using the *promiseAllocationStack* + accessor property of the [`Debugger.Object`][object] instance *promise*. + + This handler method should return a [resumption value][rv] specifying how + the debuggee's execution should proceed. However, note that a <code>{ + return: <i>value</i> }</code> resumption value is treated like `undefined` + ("continue normally"); <i>value</i> is ignored. + +<code>onPromiseSettled(<i>promise</i>)</code> +: A Promise object, referenced by the [`Debugger.Object`][object] instance + *promise* that was allocated within a debuggee scope, has settled (either + fulfilled or rejected). The Promise's state, fulfillment or rejection + value, and the allocation and resolution stacks can be obtained using the + Promise-related accessor properties of the [`Debugger.Object`][object] + instance *promise*. + + This handler method should return a [resumption value][rv] specifying how + the debuggee's execution should proceed. However, note that a <code>{ + return: <i>value</i> }</code> resumption value is treated like `undefined` + ("continue normally"); <i>value</i> is ignored. + +<code>onDebuggerStatement(<i>frame</i>)</code> +: Debuggee code has executed a <i>debugger</i> statement in <i>frame</i>. + This method should return a [resumption value][rv] specifying how the + debuggee's execution should proceed. + +<code>onEnterFrame(<i>frame</i>)</code> +: The stack frame <i>frame</i> is about to begin executing code. + (Naturally, <i>frame</i> is currently the youngest + [visible frame][vf].) This method should return + a [resumption value][rv] specifying how the debuggee's execution should + proceed. + + SpiderMonkey only calls `onEnterFrame` to report + [visible][vf], non-`"debugger"` frames. + +<code>onExceptionUnwind(<i>frame</i>, <i>value</i>)</code> +: The exception <i>value</i> has been thrown, and has propagated to + <i>frame</i>; <i>frame</i> is the youngest remaining stack frame, and is a + debuggee frame. This method should return a [resumption value][rv] + specifying how the debuggee's execution should proceed. If it returns + `undefined`, the exception continues to propagate as normal: if control in + `frame` is in a `try` block, control jumps to the corresponding `catch` or + `finally` block; otherwise, <i>frame</i> is popped, and the exception + propagates to <i>frame</i>'s caller. + + When an exception's propagation causes control to enter a `finally` + block, the exception is temporarily set aside. If the `finally` block + finishes normally, the exception resumes propagation, and the debugger's + `onExceptionUnwind` handler is called again, in the same frame. (The + other possibility is for the `finally` block to exit due to a `return`, + `continue`, or `break` statement, or a new exception. In those cases the + old exception does not continue to propagate; it is discarded.) + + This handler is not called when unwinding a frame due to an over-recursion + or out-of-memory exception. + +<code>sourceHandler(<i>ASuffusionOfYellow</i>)</code> +: This method is never called. If it is ever called, a contradiction has + been proven, and the debugger is free to assume that everything is true. + +<code>onError(<i>frame</i>, <i>report</i>)</code> +: SpiderMonkey is about to report an error in <i>frame</i>. <i>Report</i> + is an object describing the error, with the following properties: + + `message` + : The fully formatted error message. + + `file` + : If present, the source file name, URL, etc. (If this property is + present, the <i>line</i> property will be too, and vice versa.) + + `line` + : If present, the source line number at which the error occurred. + + `lineText` + : If present, this is the source code of the offending line. + + `offset` + : The index of the character within lineText at which the error occurred. + + `warning` + : Present and true if this is a warning; absent otherwise. + + `strict` + : Present and true if this error or warning is due to the strict option + (not to be confused with ES strict mode) + + `exception` + : Present and true if an exception will be thrown; absent otherwise. + + `arguments` + : An array of strings, representing the arguments substituted into the + error message. + + This method's return value is ignored. + +`onNewGlobalObject(global)` +: A new global object, <i>global</i>, has been created. + + This handler method should return a [resumption value][rv] specifying how + the debuggee's execution should proceed. However, note that a <code>{ return: + <i>value</i> }</code> resumption value is treated like `undefined` ("continue + normally"); <i>value</i> is ignored. (Allowing the handler to substitute + its own value for the new global object doesn't seem useful.) + + This handler method is only available to debuggers running in privileged + code ("chrome", in Firefox). Most functions provided by this `Debugger` + API observe activity in only those globals that are reachable by the + API's user, thus imposing capability-based restrictions on a + `Debugger`'s reach. However, the `onNewGlobalObject` method allows the + API user to monitor all global object creation that occurs anywhere + within the JavaScript system (the "JSRuntime", in SpiderMonkey terms), + thereby escaping the capability-based limits. For this reason, + `onNewGlobalObject` is only available to privileged code. + + + +## Function Properties of the Debugger Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger` instance; they may not be used as methods of +other kinds of objects. + +<code id="addDebuggee">addDebuggee(<i>global</i>)</code> +: Add the global object designated by <i>global</i> to the set of global + objects this `Debugger` instance is debugging. If the designated global + is already a debuggee, this has no effect. Return this `Debugger`'s + [`Debugger.Object`][object] instance referring to the designated global. + + The value <i>global</i> may be any of the following: + + * A global object. + + * An HTML5 `WindowProxy` object (an "outer window", in Firefox + terminology), which is treated as if the `Window` object of the + browsing context's active document (the "inner window") were passed. + + * A cross-compartment wrapper of an object; we apply the prior rules to + the wrapped object. + + * A [`Debugger.Object`][object] instance belonging to this `Debugger` instance; + we apply the prior rules to the referent. + + * Any other sort of value is treated as a `TypeError`. (Note that each + rule is only applied once in the process of resolving a given + <i>global</i> argument. Thus, for example, a [`Debugger.Object`][object] + referring to a second [`Debugger.Object`][object] which refers to a global does + not designate that global for the purposes of this function.) + + The global designated by <i>global</i> must be in a different + compartment than this `Debugger` instance itself. If adding the + designated global's compartment would create a cycle of debugger and + debuggee compartments, this method throws an error. + + This method returns the [`Debugger.Object`][object] instance whose referent is + the designated global object. + + The `Debugger` instance does not hold a strong reference to its + debuggee globals: if a debuggee global is not otherwise reachable, then + it is dropped from the `Debugger`'s set of debuggees. (Naturally, the + [`Debugger.Object`][object] instance this method returns does hold a strong + reference to the added global.) + + If this debugger is [tracking allocation sites][tracking-allocs] and cannot + track allocation sites for <i>global</i>, this method throws an `Error`. + +`addAllGlobalsAsDebuggees()` +: This method is like [`addDebuggee`][add], but adds all the global + objects from all compartments to this `Debugger` instance's set of + debuggees. Note that it skips this debugger's compartment. + + If this debugger is [tracking allocation sites][tracking-allocs] and cannot + track allocation sites for some global, this method throws an `Error`. + Otherwise this method returns `undefined`. + + This method is only available to debuggers running in privileged + code ("chrome", in Firefox). Most functions provided by this `Debugger` + API observe activity in only those globals that are reachable by the + API's user, thus imposing capability-based restrictions on a + `Debugger`'s reach. However, the `addAllGlobalsAsDebuggees` method + allows the API user to monitor all global object creation that + occurs anywhere within the JavaScript system (the "JSRuntime", in + SpiderMonkey terms), thereby escaping the capability-based + limits. For this reason, `addAllGlobalsAsDebuggees` is only + available to privileged code. + +<code>removeDebuggee(<i>global</i>)</code> +: Remove the global object designated by <i>global</i> from this + `Debugger` instance's set of debuggees. Return `undefined`. + + This method interprets <i>global</i> using the same rules that + [`addDebuggee`][add] does. + + Removing a global as a debuggee from this `Debugger` clears all breakpoints + that belong to that `Debugger` in that global. + +`removeAllDebuggees()` +: Remove all the global objects from this `Debugger` instance's set + of debuggees. Return `undefined`. + +<code>hasDebuggee(<i>global</i>)</code> +: Return `true` if the global object designated by <i>global</i> is a + debuggee of this `Debugger` instance. + + This method interprets <i>global</i> using the same rules that + [`addDebuggee`][add] does. + +`getDebuggees()` +: Return an array of distinct [`Debugger.Object`][object] instances whose referents + are all the global objects this `Debugger` instance is debugging. + + Since `Debugger` instances don't hold strong references to their + debuggee globals, if a debuggee global is otherwise unreachable, it may + be dropped at any moment from the array this method returns. + +`getNewestFrame()` +: Return a [`Debugger.Frame`][frame] instance referring to the youngest + [visible frame][vf] currently on the calling thread's stack, or `null` + if there are no visible frames on the stack. + +<code>findSources([<i>query</i>]) <i>(not yet implemented)</i></code> +: Return an array of all [`Debugger.Source`][source] instances matching + <i>query</i>. Each source appears only once in the array. <i>Query</i> + is an object whose properties restrict which sources are returned; a + source must meet all the criteria given by <i>query</i> to be returned. + If <i>query</i> is omitted, we return all sources of all debuggee + scripts. + + <i>Query</i> may have the following properties: + + `url` + : The source's `url` property must be equal to this value. + + `global` + : The source must have been evaluated in the scope of the given global + object. If this property's value is a [`Debugger.Object`][object] instance + belonging to this `Debugger` instance, then its referent is used. If the + object is not a global object, then the global in whose scope it was + allocated is used. + + Note that the result may include sources that can no longer ever be + used by the debuggee: say, eval code that has finished running, or + source for unreachable functions. Whether such sources appear can be + affected by the garbage collector's behavior, so this function's result + is not entirely deterministic. + +<code>findScripts([<i>query</i>])</code> +: Return an array of [`Debugger.Script`][script] instances for all debuggee scripts + matching <i>query</i>. Each instance appears only once in the array. + <i>Query</i> is an object whose properties restrict which scripts are + returned; a script must meet all the criteria given by <i>query</i> to + be returned. If <i>query</i> is omitted, we return the [`Debugger.Script`][script] + instances for all debuggee scripts. + + <i>Query</i> may have the following properties: + + `url` + : The script's `url` property must be equal to this value. + + `source` + : The script's `source` property must be equal to this value. + + `line` + : The script must at least partially cover the given source line. If this + property is present, the `url` property must be present as well. + + `column` + : The script must include given column on the line given by the `line` + property. If this property is present, the `url` and `line` properties + must both be present as well. + + `innermost` + : If this property is present and true, the script must be the innermost + script covering the given source location; scripts of enclosing code are + omitted. + + `global` + : The script must be in the scope of the given global object. If this + property's value is a [`Debugger.Object`][object] instance belonging to this + `Debugger` instance, then its referent is used. If the object is not a + global object, then the global in whose scope it was allocated is used. + + All properties of <i>query</i> are optional. Passing an empty object + returns all debuggee code scripts. + + Note that the result may include [`Debugger.Script`][script] instances for + scripts that can no longer ever be used by the debuggee, say, those for + eval code that has finished running, or unreachable functions. Whether + such scripts appear can be affected by the garbage collector's + behavior, so this function's behavior is not entirely deterministic. + +<code>findObjects([<i>query</i>])</code> +: Return an array of [`Debugger.Object`][object] instances referring to each + live object allocated in the scope of the debuggee globals that matches + *query*. Each instance appears only once in the array. *Query* is an object + whose properties restrict which objects are returned; an object must meet + all the criteria given by *query* to be returned. If *query* is omitted, we + return the [`Debugger.Object`][object] instances for all objects allocated + in the scope of debuggee globals. + + The *query* object may have the following properties: + + `class` + : If present, only return objects whose internal `[[Class]]`'s name + matches the given string. Note that in some cases, the prototype object + for a given constructor has the same `[[Class]]` as the instances that + refer to it, but cannot itself be used as a valid instance of the + class. Code gathering objects by class name may need to examine them + further before trying to use them. + + All properties of *query* are optional. Passing an empty object returns all + objects in debuggee globals. + + Unlike `findScripts`, this function is deterministic and will never return + [`Debugger.Object`s][object] referring to previously unreachable objects + that had not been collected yet. + +<code>clearBreakpoint(<i>handler</i>)</code> +: Remove all breakpoints set in this `Debugger` instance that use + <i>handler</i> as their handler. Note that, if breakpoints using other + handler objects are set at the same location(s) as <i>handler</i>, they + remain in place. + +`clearAllBreakpoints()` +: Remove all breakpoints set using this `Debugger` instance. + +`findAllGlobals()` +: Return an array of [`Debugger.Object`][object] instances referring to all the + global objects present in this JavaScript instance. + + The results of this call can be affected in non-deterministic ways by + the details of the JavaScript implementation. The array may include + [`Debugger.Object`][object] instances referring to global objects that are not + actually reachable by the debuggee or any other code in the system. + (Naturally, once the function has returned, the array's + [`Debugger.Object`][object] instances strongly reference the globals they refer + to.) + + This handler method is only available to debuggers running in privileged + code ("chrome", in Firefox). Most functions provided by this `Debugger` + API observe activity in only those globals that are reachable by the + API's user, thus imposing capability-based restrictions on a + `Debugger`'s reach. However, `findAllGlobals` allows the API user to + find all global objects anywhere within the JavaScript system (the + "JSRuntime", in SpiderMonkey terms), thereby escaping the + capability-based limits. For this reason, `findAllGlobals` is only + available to privileged code. + +<code>makeGlobalObjectReference(<i>global</i>)</code> +: Return the [`Debugger.Object`][object] whose referent is the global object + designated by <i>global</i>, without adding the designated global as a + debuggee. If <i>global</i> does not designate a global object, throw a + `TypeError`. Determine which global is designated by <i>global</i> + using the same rules as [`Debugger.prototype.addDebuggee`][add]. + +<code>adoptDebuggeeValue(<i>value</i>)</code> +: Given a debuggee value `value` owned by an arbitrary `Debugger`, return an + equivalent debuggee value owned by this `Debugger`. + + If `value` is a primitive value, return it unchanged. If `value` is a + `Debugger.Object` owned by an arbitrary `Debugger`, return an equivalent + `Debugger.Object` owned by this `Debugger`. Otherwise, if `value` is some + other kind of object, and hence not a proper debuggee value, throw a + TypeError instead. + +## Static methods of the Debugger Object + +The functions described below are not called with a `this` value. + +<code id="isCompilableUnit">isCompilableUnit(<i>source</i>)</code> +: Given a string of source code, designated by <i>source</i>, return false if + the string might become a valid JavaScript statement with the addition of + more lines. Otherwise return true. The intent is to support interactive + compilation - accumulate lines in a buffer until isCompilableUnit is true, + then pass it to the compiler. diff --git a/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md b/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md new file mode 100644 index 0000000000..d9a92769ac --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md @@ -0,0 +1,222 @@ +Tutorial: Show Allocations Per Call Path +======================================== + +{{ gecko_minversion_header(\'34\') }} + +This page shows how to use the [`Debugger` API][debugger] to show how many +objects a web page allocates, sorted by the function call path that allocated +them. + +1) Visit the URL `about:config`, and set the `devtools.chrome.enabled` + preference to `true`: + + ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] + +2) Open a developer Scratchpad (Menu button > Developer > Scratchpad), and + select "Browser" from the "Environment" menu. (This menu will not be + present unless you have changed the preference as explained above.) + + ![Selecting the 'browser' context in the Scratchpad][img-scratchpad-browser] + +3) Enter the following code in the Scratchpad: + + ```language-js + // This simply defines the 'Debugger' constructor in this + // Scratchpad; it doesn't actually start debugging anything. + Components.utils.import('resource://gre/modules/jsdebugger.jsm'); + addDebuggerToGlobal(window); + + (function () { + // The debugger we'll use to observe a tab's allocation. + var dbg; + + // Start measuring the selected tab's main window's memory + // consumption. This function is available in the browser + // console. + window.demoTrackAllocations = function() { + dbg = new Debugger; + + // This makes hacking on the demo *much* more + // pleasant. + dbg.uncaughtExceptionHook = handleUncaughtException; + + // Find the current tab's main content window. + var w = gBrowser.selectedBrowser.contentWindow; + console.log("Tracking allocations in page: " + + w.location.href); + + // Make that window a debuggee of our Debugger. + dbg.addDebuggee(w.wrappedJSObject); + + // Enable allocation tracking in dbg's debuggees. + dbg.memory.trackingAllocationSites = true; + } + + window.demoPlotAllocations = function() { + // Grab the allocation log. + var log = dbg.memory.drainAllocationsLog(); + + // Neutralize the Debugger, and drop it on the floor + // for the GC to collect. + console.log("Stopping allocation tracking."); + dbg.removeAllDebuggees(); + dbg = undefined; + + // Analyze and display the allocation log. + plot(log); + } + + function handleUncaughtException(ex) { + console.log('Debugger hook threw:'); + console.log(ex.toString()); + console.log('Stack:'); + console.log(ex.stack); + }; + + function plot(log) { + // Given the log, compute a map from allocation sites to + // allocation counts. Note that stack entries are '===' if + // they represent the same site with the same callers. + var counts = new Map; + for (let site of log) { + // This is a kludge, necessary for now. The saved stacks + // are new, and Firefox doesn't yet understand that they + // are safe for chrome code to use, so we must tell it + // so explicitly. + site = Components.utils.waiveXrays(site.frame); + + if (!counts.has(site)) + counts.set(site, 0); + counts.set(site, counts.get(site) + 1); + } + + // Walk from each site that allocated something up to the + // root, computing allocation totals that include + // children. Remember that 'null' is a valid site, + // representing the root. + var totals = new Map; + for (let [site, count] of counts) { + for(;;) { + if (!totals.has(site)) + totals.set(site, 0); + totals.set(site, totals.get(site) + count); + if (!site) + break; + site = site.parent; + } + } + + // Compute parent-to-child links, since saved stack frames + // have only parent links. + var rootChildren = new Map; + function childMapFor(site) { + if (!site) + return rootChildren; + + let parentMap = childMapFor(site.parent); + if (parentMap.has(site)) + return parentMap.get(site); + + var m = new Map; + parentMap.set(site, m); + return m; + } + for (let [site, total] of totals) { + childMapFor(site); + } + + // Print the allocation count for |site|. Print + // |children|'s entries as |site|'s child nodes. Indent + // the whole thing by |indent|. + function walk(site, children, indent) { + var name, place; + if (site) { + name = site.functionDisplayName; + place = ' ' + site.source + ':' + site.line + ':' + site.column; + } else { + name = '(root)'; + place = ''; + } + console.log(indent + totals.get(site) + ': ' + name + place); + for (let [child, grandchildren] of children) + walk(child, grandchildren, indent + ' '); + } + walk(null, rootChildren, ''); + } + })(); + ``` + +4) In the Scratchpad, ensure that no text is selected, and press the "Run" + button. (If you get an error complaining that `Components.utils` is not + defined, be sure you've selected `Browser` from the scratchpad's + `Environment` menu, as described in step 2.) + +5) Save the following HTML text to a file, and visit the file in your browser. + Make sure the current browser tab is displaying this page. + + ```language-html + <div onclick="doDivsAndSpans()"> + Click here to make the page do some allocations. + </div> + + <script> + function makeFactory(type) { + return function factory(content) { + var elt = document.createElement(type); + elt.textContent = content; + return elt; + }; + } + + var divFactory = makeFactory('div'); + var spanFactory = makeFactory('span'); + + function divsAndSpans() { + for (i = 0; i < 10; i++) { + var div = divFactory('div #' + i); + div.appendChild(spanFactory('span #' + i)); + document.body.appendChild(div); + } + } + + function doDivsAndSpans() { divsAndSpans(); } + </script> + ``` + +6) Open the browser console (Menu Button > Developer > Browser Console), and + then evaluate the expression `demoTrackAllocations()` in the browser + console. This begins logging allocations in the current browser tab. + +7) In the browser tab, click on the text that says "Click here...". The event + handler should add some text to the end of the page. + +8) Back in the browser console, evaluate the expression + `demoPlotAllocations()`. This stops logging allocations, and displays a tree + of allocations: + + ![An allocation plot, displayed in the console][img-alloc-plot] + + The numbers at the left edge of each line show the total number of objects + allocated at that site or at sites called from there. After the count, we + see the function name, and the source code location of the call site or + allocation. + + The `(root)` node's count includes objects allocated in the content page by + the web browser, like DOM events. Indeed, this display shows that + `popup.xml` and `content.js`, which are internal components of Firefox, + allocated more objects in the page's compartment than the page itself. (We + will probably revise the allocation log to present such allocations in a way + that is more informative, and that exposes less of Firefox's internal + structure.) + + As expected, the `onclick` handler is responsible for all allocation done by + the page's own code. (The line number for the onclick handler is `1`, + indicating that the allocating call is located on line one of the handler + text itself. We will probably change this to be the line number within + `page.html`, not the line number within the handler code.) + + The `onclick` handler calls `doDivsAndSpans`, which calls `divsAndSpans`, + which invokes closures of `factory` to do all the actual allocation. (It is + unclear why `spanFactory` allocated thirteen objects, despite being called + only ten times.) + diff --git a/js/src/doc/Debugger/Tutorial-Debugger-Statement.md b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md new file mode 100644 index 0000000000..58360ff6fd --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md @@ -0,0 +1,82 @@ +Tutorial: Evaluate an Expression When a debugger; Statement Is Executed +======================================================================= + +This page shows how you can try out the [`Debugger` API][debugger] yourself +using Firefox's Scratchpad. We use the API to evaluate an expression in the web +page whenever it executes a JavaScript `debugger;` statement. + +1) Visit the URL `about:config`, and set the `devtools.chrome.enabled` + preference to `true`: + + ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] + +2) Save the following HTML text to a file, and visit the file in your + browser: + + ```language-html + <div onclick="var x = 'snoo'; debugger;">Click me!</div> + ``` + +3) Open a developer Scratchpad (Menu button > Developer > Scratchpad), and + select "Browser" from the "Environment" menu. (This menu will not be + present unless you have changed the preference as explained above.) + + ![Selecting the 'browser' context in the Scratchpad][img-scratchpad-browser] + +4) Enter the following code in the Scratchpad: + + ```language-js + // This simply defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + Components.utils.import("resource://gre/modules/jsdebugger.jsm"); + addDebuggerToGlobal(window); + + // Create a 'Debugger' instance. + var dbg = new Debugger; + + // Get the current tab's content window, and make it a debuggee. + var w = gBrowser.selectedBrowser.contentWindow.wrappedJSObject; + dbg.addDebuggee(w); + + // When the debuggee executes a 'debugger' statement, evaluate + // the expression 'x' in that stack frame, and show its value. + dbg.onDebuggerStatement = function (frame) { + alert('hit debugger statement; x = ' + frame.eval('x').return); + } + ``` + +5) In the Scratchpad, ensure that no text is selected, and press the "Run" + button. + +6) Now, click on the text that says "Click me!" in the web page. This runs + the `div` element's `onclick` handler. When control reaches the + `debugger;` statement, `Debugger` calls your callback function, passing + a `Debugger.Frame` instance. Your callback function evaluates the + expression `x` in the given stack frame, and displays the alert: + + ![The Debugger callback displaying an alert][img-example-alert] + +7) Press "Run" in the Scratchpad again. Now, clicking on the "Click me!" + text causes *two* alerts to show---one for each `Debugger` + instance. + + Multiple `Debugger` instances can observe the same debuggee. Re-running + the code in the Scratchpad created a fresh `Debugger` instance, added + the same web page as its debuggee, and then registered a fresh + `debugger;` statement handler with the new instance. When you clicked + on the `div` element, both of them ran. This shows how any number of + `Debugger`-based tools can observe a single web page + simultaneously---although, since the order in which their handlers + run is not specified, such tools should probably only observe, and not + influence, the debuggee's behavior. + +8) Close the web page and the Scratchpad. + + Since both the Scratchpad's global object and the debuggee window are + now gone, the `Debugger` instances will be garbage collected, since + they can no longer have any visible effect on Firefox's behavior. The + `Debugger` API tries to interact with garbage collection as + transparently as possible; for example, if both a `Debugger.Object` + instance and its referent are not reachable, they will both be + collected, even while the `Debugger` instance to which the shadow + belonged continues to exist. diff --git a/js/src/doc/Debugger/alloc-plot-console.png b/js/src/doc/Debugger/alloc-plot-console.png Binary files differnew file mode 100644 index 0000000000..5411724724 --- /dev/null +++ b/js/src/doc/Debugger/alloc-plot-console.png diff --git a/js/src/doc/Debugger/config.sh b/js/src/doc/Debugger/config.sh new file mode 100644 index 0000000000..eda4ed8d7a --- /dev/null +++ b/js/src/doc/Debugger/config.sh @@ -0,0 +1,66 @@ +### Description of Debugger docs: how to format, where to install. +### See js/src/doc/README.md for a description. + +base-url https://developer.mozilla.org/en-US/docs/Tools/ + +markdown Debugger-API.md Debugger-API + label 'debugger' "The Debugger API" + +markdown Conventions.md Debugger-API/Conventions + label 'conventions' "Debugger API: General Conventions" + label 'dbg code' '#debuggee-code' "Debugger API: General Conventions: Debuggee Code" + label 'cv' '#completion-values' "Debugger API: General Conventions: Completion Values" + label 'rv' '#resumption-values' "Debugger API: General Conventions: Resumption Values" + label 'timestamps' '#timestamps' "Debugger API: General Conventions: Timestamps" + label 'wouldrun' '#the-debugger.debuggeewouldrun-exception' "Debugger API: DebuggeeWouldRun" + +markdown Debugger.md Debugger-API/Debugger + label 'debugger-object' "The Debugger object" + label 'add' '#addDebuggee' "The Debugger object: addDebuggee" + +markdown Debugger.Environment.md Debugger-API/Debugger.Environment + label 'environment' "Debugger.Environment" + +markdown Debugger.Frame.md Debugger-API/Debugger.Frame + label 'frame' "Debugger.Frame" + label 'vf' '#visible-frames' "Debugger.Frame: Visible Frames" + label 'inv fr' '#invf' "Debugger.Frame: Invocation Frames" + label 'fr eval' '#eval' "Debugger.Frame: Eval" + +markdown Debugger.Object.md Debugger-API/Debugger.Object + label 'object' "Debugger.Object" + label 'allocation-site' '#allocationsite' "Debugger.Object: allocationSite" + +markdown Debugger.Script.md Debugger-API/Debugger.Script + label 'script' "Debugger.Script" + +markdown Debugger.Source.md Debugger-API/Debugger.Source + label 'source' "Debugger.Source" + +markdown Debugger.Memory.md Debugger-API/Debugger.Memory + label 'memory' "Debugger.Memory" + label 'tracking-allocs' '#trackingallocationsites' "Debugger.Memory: trackingAllocationSites" + label 'drain-alloc-log' '#drain-alloc-log' "Debugger.Memory: drainAllocationsLog" + label 'max-alloc-log' '#max-alloc-log' "Debugger.Memory: maxAllocationsLogLength" + label 'alloc-sampling-probability' '#alloc-sampling-probability' "Debugger.Memory: allocationSamplingProbability" + label 'take-census' '#take-census' "Debugger.Memory: takeCensus" + +markdown Tutorial-Debugger-Statement.md Debugger-API/Tutorial-Debugger-Statement + label 'tut debugger' "Tutorial: the debugger; statement" + +markdown Tutorial-Alloc-Log-Tree.md Debugger-API/Tutorial-Allocation-Log-Tree + label 'tut alloc log' "Tutorial: the allocation log" + +# Images: +RBASE=https://mdn.mozillademos.org/files +resource 'img-shadows' shadows.svg $RBASE/7225/shadows.svg +resource 'img-chrome-pref' enable-chrome-devtools.png $RBASE/7233/enable-chrome-devtools.png +resource 'img-scratchpad-browser' scratchpad-browser-environment.png $RBASE/7229/scratchpad-browser-environment.png +resource 'img-example-alert' debugger-alert.png $RBASE/7231/debugger-alert.png +resource 'img-alloc-plot' alloc-plot-console.png $RBASE/8461/alloc-plot-console.png + +# External links: +absolute-label 'protocol' https://wiki.mozilla.org/Remote_Debugging_Protocol "Remote Debugging Protocol" +absolute-label 'saved-frame' https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/SavedFrame "SavedFrame" +absolute-label 'bernoulli-trial' https://en.wikipedia.org/wiki/Bernoulli_trial "Bernoulli Trial" +absolute-label 'promise' https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise" diff --git a/js/src/doc/Debugger/debugger-alert.png b/js/src/doc/Debugger/debugger-alert.png Binary files differnew file mode 100644 index 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/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..5774fc78f3 --- /dev/null +++ b/js/src/doc/Debugger/shadows.svg @@ -0,0 +1,997 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="650" + height="400" + id="svg10302" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="shadows.svg" + viewBox="0 0 650 400"> + <defs + id="defs10304"> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-0" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-3" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-2" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-6" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-6" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-3" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-9" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-4" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-4" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-79" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-8" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-11" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-9" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-0" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-0" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-96" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-4" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-72" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-1" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-8" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-67" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-5" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-78" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-89" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-6" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-5" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-3" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-5" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-18" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-35" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-2" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.6369231" + inkscape:cx="338.70355" + inkscape:cy="200" + inkscape:document-units="px" + inkscape:current-layer="alertLater" + showgrid="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1920" + inkscape:window-height="1021" + inkscape:window-x="0" + inkscape:window-y="867" + inkscape:window-maximized="1" /> + <metadata + id="metadata10307"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="debuggeeset" + inkscape:label="debuggeeset" + style="display:inline" + class="flip"> + <rect + style="fill:#c8c0c0;fill-opacity:1;stroke:#000000;stroke-width:2.18226266;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.36452564, 4.36452564;stroke-dashoffset:0;display:inline" + id="rect3755-3-0-6-1-8-6" + width="171.31711" + height="150.68491" + x="0.10979491" + y="248.55035" + ry="4.814189" + rx="4.0350175" /> + <text + xml:space="preserve" + style="font-size:14.11999989px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="9.8749857" + y="268.84415" + id="text22690" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan22692" + x="9.8749857" + y="268.84415">Debugger</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="shadows" + inkscape:label="shadows" + style="display:inline" + class="flip"> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-68" + width="119.42149" + height="42.397526" + x="25.236485" + y="160.0201" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-8" + width="177.65483" + height="46.614746" + x="209.36084" + y="336.41965" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-43" + width="149.12187" + height="46.614746" + x="237.8938" + y="287.1636" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-6-1" + width="119.42149" + height="42.397526" + x="192.17702" + y="161.09923" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-2-4" + width="136.4054" + height="46.614731" + x="26.006214" + y="336.41965" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-1-5-9" + width="242.77243" + height="75.739441" + x="183.31783" + y="47.05508" + ry="6.1249413" + rx="6.1249394" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:#ffffff;stroke-width:2.92859435;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + id="rect3755-1-5-1-7-2" + width="186.27605" + height="18.674528" + x="285.59784" + y="66.98394" + ry="6.1249413" + rx="6.1249399" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-0" + width="194.03011" + height="46.614746" + x="413.20236" + y="336.41989" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-4-6" + width="90.035484" + height="46.614746" + x="296.98013" + y="237.91023" + ry="4.0393105" + rx="4.0393105" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="33.257587" + y="378.20139" + id="text4037" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4039" + x="33.257587" + y="378.20139">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="218.72287" + y="377.52206" + id="text4041" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4043" + x="218.72287" + y="377.52206">Debugger.Environment</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="421.84915" + y="377.52206" + id="text4045" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4047" + x="421.84915" + y="377.52206">Debugger.Frame</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="29.181461" + y="196.81334" + id="text4049" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4051" + x="29.181461" + y="196.81334">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="199.2216" + y="197.89247" + id="text4053" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4055" + x="199.2216" + y="197.89247">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="190.1888" + y="118.00808" + id="text4057" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4059" + x="190.1888" + y="118.00808">Debugger.Script</tspan></text> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-5-8" + width="194.03011" + height="46.614746" + x="413.20236" + y="287.1636" + ry="4.0393105" + rx="4.0393105" /> + </g> + <g + inkscape:groupmode="layer" + id="global" + inkscape:label="global" + transform="translate(0,-344.09448)" + class="flip" + style="display:inline"> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1" + width="177.9265" + height="46.614887" + x="201.82011" + y="664.15881" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="210.6344" + y="691.88666" + id="text10970" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan10972" + x="210.6344" + y="691.88666">global environment</tspan></text> + <rect + style="fill:#83d4ff;fill-opacity:1;stroke:none" + id="rect3755-5-2" + width="136.61398" + height="46.614872" + x="18.810663" + y="664.15881" + ry="11.065418" + rx="11.065418" /> + <path + style="fill:none;stroke:#000000;stroke-width:2.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8)" + d="m 204.0527,687.46628 -46.26685,0" + id="path11163-2" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="3.020442" + y="659.44391" + id="text15699" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan15701" + x="3.020442" + y="659.44391">global object:</tspan></text> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="87.051254" + y="698.41443" + id="text15703" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + x="87.051254" + y="698.41443" + id="tspan15707">Date; Math; ...</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="scripts" + inkscape:label="scripts" + style="display:inline" + class="flip"> + <rect + transform="translate(0,-652.36218)" + style="fill:#83ff9a;fill-opacity:1;stroke:none;display:inline" + id="rect3755-1-5" + width="243.14369" + height="75.73967" + x="176.36284" + y="683.0611" + ry="6.1249409" + rx="6.1249394" + class="nonvalue" /> + <rect + transform="translate(0,-652.36218)" + style="fill:#83ff9a;fill-opacity:1;stroke:#e6e4dd;stroke-width:2.92859435000000001;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + id="rect3755-1-5-1-7" + width="186.56091" + height="18.674585" + x="278.79929" + y="702.98999" + ry="6.1249409" + rx="6.1249399" + class="nonvalue" /> + </g> + <g + inkscape:groupmode="layer" + id="code" + inkscape:label="code" + class="flip" + style="display:inline"> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="182.99957" + y="699.52692" + id="text10314" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923777,0,-652.36218)"><tspan + sodipodi:role="line" + x="182.99957" + y="699.52692" + id="tspan4518">function alertLater(msg, delay) {</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="717.1795" + id="tspan4527"> setTimeout( function () { alert(msg); },</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="734.83215" + id="tspan4529"> delay);</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="752.48474" + id="tspan4531">}</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="alertLater" + inkscape:label="alertLater" + style="display:inline" + class="flip"> + <rect + transform="translate(0,-652.36218)" + style="fill:#83d4ff;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5" + width="119.60412" + height="42.397655" + x="18.039759" + y="796.02643" + ry="11.065418" + rx="11.065418" /> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="98.427589" + y="811.1579" + id="text3761-6" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan3763-0" + x="98.427589" + y="811.1579">[[Code]]:</tspan></text> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="98.427589" + y="831.64917" + id="text3765-0" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan3767-2" + x="98.427589" + y="831.64917">[[Scope]]:</tspan></text> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#e6e4dd;stroke-width:2.95684338000000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + d="m 18.039736,817.89404 119.601824,0" + id="path3773-4-3" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="2.4066608" + y="791.50812" + id="text10404" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan10406" + x="2.4066608" + y="791.50812">alertLater:</tspan></text> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 113.60688,806.63271 c 0,-45.02374 23.466,-83.54273 59.2075,-83.54273" + id="path3868-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 113.60688,827.22105 c 51.73315,0 90.50566,99.84829 90.50566,141.32477" + id="path3868-4-0" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="87.051254" + y="991.61841" + id="text15703-4" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + x="89.299202" + y="991.61841" + id="tspan15707-0">alertLater; </tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="call1env" + inkscape:label="call1env" + style="display:inline" + class="flip"> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3" + width="149.34991" + height="46.614887" + x="229.7888" + y="270.80811" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <g + style="display:inline" + id="g3550" + transform="matrix(0.58886915,0,0,0.58797179,4.5262952,-176.54364)"> + <text + transform="scale(1.0009935,0.9990075)" + sodipodi:linespacing="125%" + id="text13153" + y="795.71594" + x="499.133" + style="font-size:22px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="795.71594" + x="499.133" + id="tspan13155" + sodipodi:role="line">msg:</tspan><tspan + y="823.21594" + x="499.133" + sodipodi:role="line" + id="tspan3538">delay:</tspan></text> + <text + sodipodi:linespacing="125%" + id="text3540" + y="794.92615" + x="507" + style="font-size:22px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="794.92615" + x="507" + id="tspan3542" + sodipodi:role="line">'xlerb'</tspan><tspan + id="tspan3544" + y="822.42615" + x="507" + sodipodi:role="line">1000</tspan></text> + </g> + </g> + <g + inkscape:groupmode="layer" + id="anon" + inkscape:label="anon" + style="display:inline" + class="flip"> + <rect + style="fill:#83d4ff;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-6" + width="119.60412" + height="42.397655" + x="185.84294" + y="143.66425" + ry="11.065418" + rx="11.065418" /> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="266.10284" + y="158.2981" + id="text3761-6-0" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan3763-0-7" + x="266.10284" + y="158.2981">[[Code]]:</tspan></text> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="266.10284" + y="178.78937" + id="text3765-0-7" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan3767-2-6" + x="266.10284" + y="178.78937">[[Scope]]:</tspan></text> + <path + style="fill:none;stroke:#e6e4dd;stroke-width:2.95684338000000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + d="m 185.84293,165.53187 119.60182,0" + id="path3773-4-3-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 285.44496,176.59077 c 0,31.25836 -38.08897,59.76616 -38.08897,91.05227" + id="path3868-4-0-4-7" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 285.95088,154.47813 c 165.68371,0 170.77148,-32.93057 158.20494,-82.18402" + id="path3868-4-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> + <g + inkscape:groupmode="layer" + id="fire" + inkscape:label="fire" + style="display:inline" + class="flip"> + <rect + style="fill:#ff9457;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-4" + width="194.03011" + height="46.614746" + x="406.38477" + y="320.06448" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="434.10962" + y="346.93411" + id="text10970-7-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + x="434.10962" + y="346.93411" + id="tspan3534-4"><tspan + style="font-style:italic;-inkscape-font-specification:Sans Italic" + id="tspan5754">anonymous</tspan>()</tspan></text> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-4" + width="90.035484" + height="46.614746" + x="289.10324" + y="221.55482" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:italic;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans;-inkscape-font-specification:Sans Italic" + x="312.99686" + y="248.05814" + id="text5774" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5776" + x="312.99686" + y="248.05814">empty</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8);display:inline" + d="m 415.85979,342.35006 c -51.09645,0 11.10573,-95.59507 -39.0289,-95.59507" + id="path11163-1" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8);display:inline" + d="m 588.68444,343.33325 c 66.19678,0 27.60009,-283.14206 -117.22439,-283.14206" + id="path11163-1-6" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> + <g + inkscape:groupmode="layer" + id="call2" + inkscape:label="call2" + class="flip" + style="display:inline"> + <rect + style="fill:#ff9457;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-5-4" + width="194.03011" + height="46.614746" + x="406.38477" + y="270.80817" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="433.86328" + y="298.87103" + id="text6389" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan6391" + x="433.86328" + y="298.87103">alert('xlerb')</tspan></text> + </g> +</svg> diff --git a/js/src/doc/JITOptimizations/Outcomes.md b/js/src/doc/JITOptimizations/Outcomes.md new file mode 100644 index 0000000000..b0eb9c43af --- /dev/null +++ b/js/src/doc/JITOptimizations/Outcomes.md @@ -0,0 +1,581 @@ +# JIT Optimization Outcomes + +SpiderMonkey's optimizing JIT, IonMonkey, uses a number of different +optimization strategies to speed up various operations. The most commonplace +operations that are relevant for fast program execution are property accesses +and function calls. + +This page documents the meaning of different optimization outcomes. + +## General Outcomes + +General outcomes shared between various optimization strategies. + +### GenericFailure + +The optimization attempt failed, and the reason was not recorded. + +### GenericSuccess + +Optimization succeeded. + +### Disabled + +The optimization has been explicitly disallowed. + +### NoTypeInfo + +Optimization failed because there was no type information associated with +object containing the property. This failure mode is unlikely, and occurs +if the target object is obtained in some roundabout way. + +### NoAnalysisInfo + +TODO + +### NoShapeInfo + +The baseline compiler recorded no usable shape information for this operation. + +### UnknownObject + +The type of the object is not known. This can happen if the operation sees many different types of objects, and so the type of the input to the operation cannot be resolved to a single type. + +### UnknownProperties + +Optimization failed because the object containing the property was marked +as having unknown properties. This can happen if too many properties are +defined on the object, or if `delete` is used to remove one of the object's +properties. + +### Singleton + +One of the types present in the typeset was a singleton type, preventing the optimization from being enabled. + +### NotSingleton + +Optimization failed because the object containing the property did not +have a 'singleton' type. Singleton types are assigned to objects that are +"one of a kind", such as global objects, literal objects declared in the +global scope, and top-level function objects. + +### NotFixedSlot + +The property being accessed is not stored at a known location in the object. This can occur if one of the expected types of objects to be used in this operation has unknown properties, or if different instances of the object store the property at different locations (for example, some instances have the property assigned in a different order than others). + +### InconsistentFixedSlot + +The property being accessed is not stored at a known location in the object. This can occur if the operation is polymorphic on different object types and one or more of the object types contain the property at a different slot than the others. + +### NotObject + +Optimization failed because the stored in the property could potentially +be a non-object value. Since only objects can be uniquely typed, the +optimization strategy fails in this case. + +### NotStruct + +The object holding the property is not a typed struct object. + +### NotUnboxed + +The object whose property is being accessed is not formatted as an +unboxed object. + +### UnboxedConvertedToNative + +The object whose property is being accessed was previously unboxed, +but was deoptimized and converted to a native object. + +### StructNoField + +The unboxed property being accessed does not correspond to a field on typed +object. + +### InconsistentFieldType + +The type of an unboxed field is not consistent across all the different types of objects it could be accessed from. + +### InconsistentFieldOffset + +The offset of an unboxed field is not consistent across all the different types of objects it could be accessed from. + +### NeedsTypeBarrier + +Optimization failed because somehow the property was accessed in a way +that returned a different type than the expected constant. This is an +unlikely failure mode, and should not occur. + +### InDictionaryMode + +The object whose property is being accessed is in dictionary mode. Objects which are used in ways that suggest they are hashtables, are turned into dictionary objects and their types marked as such. + +### NoProtoFound + +A prototype object was not found for all the object used by this operation. + +### MultiProtoPaths + +Objects used in this operation had differing prototypes. + +### NonWritableProperty + +The property being assigned to is not writable for some types of objects which are used in this operation. + +### ProtoIndexedProps + +The object being accessed has indexed properties that are exotic (for example, defined as a property on a prototype object and left as a hole in the underlying object). + +### ArrayBadFlags + +The array being accessed may have flags that the optimization strategy cannot handle. For example, if the array has sparse indexes, or has indexes that overflow the array's length, the optimization strategy may fail. + +### ArrayDoubleConversion + +The type-system indicates that some arrays at this site should be converted to packed arrays of doubles, while others should not. The optimization strategy fails for this condition. + +### ArrayRange + +Could not accurately calculate the range attributes of an inline array creation. + +### ArraySeenNegativeIndex + +Arrays at this element access location have seen negative indexes. + +### TypedObjectHasDetachedBuffer + +The storage for the typed object being accessed at this location might be a detached ArrayBuffer. (This can happen if the typed object, or its underlying buffer as accessed using `TypedObject.storage(typedObject).buffer`, is transferred using the structured clone algorithm.) + +### TypedObjectArrayRange + +Failed to do range check of element access on a typed object. + +### AccessNotDense + +### AccessNotSimdObject + +The observed type of the target of the property access doesn't guarantee +that it is a SIMD object. + +### AccessNotTypedObject + +The observed type of the target of the property access doesn't guarantee +that it is a TypedObject. + +### AccessNotTypedArray + +The observed type of the target of the property access doesn't guarantee +that it is a TypedArray. + +### AccessNotString + +The observed type of the target of the property access doesn't guarantee +that it is a String. + +### OperandNotString + +Optimization failed because of failing to speculate the operand is a string. + +### OperandNotNumber + +Optimization failed because of failing to speculate the operand is a number. + +### OperandNotStringOrNumber + +Optimization failed because of failing to speculate the operand is a string or a number. + +### OperandNotSimpleArith + +Optimization failed because of failing to speculate the operand is a simple arithmetic type. I.e. definitely not an object, string, symbol or internal magic type. + +### StaticTypedArrayUint32 + +Typed Arrays of uint32 values are not yet fully optimized. + +### StaticTypedArrayCantComputeMask +### OutOfBounds + +The element access has been observed to be out of the length bounds of +the string being accessed. + +### GetElemStringNotCached + +IonMonkey does not generate inline caches for element accesses on +target values which may be strings. + +### NonNativeReceiver + +IonMonkey does not generate inline caches for indexed element accesses on +target values which may be non-native objects (e.g. DOM elements). + +### IndexType + +IonMonkey does not generate inline caches for element reads in which +the keys have never been observed to be a String, Symbol, or Int32. + +### SetElemNonDenseNonTANotCached + +IonMonkey only generates inline caches for element accesses which are +either on dense objects (e.g. dense Arrays), or Typed Arrays. + +### NoSimdJitSupport + +Optimization failed because SIMD JIT support was not enabled. + +### SimdTypeNotOptimized + +The type observed as being retrieved from this property access did not +match an optimizable type. + +### HasCommonInliningPath + +Inlining was abandoned because the inlining call path was repeated. A +repeated call path is indicative of a potentially mutually recursive +function call chain. + +### Inlined + +Method has been successfully inlined. + +### DOM + +Successfully optimized a call to a DOM getter or setter function. + +### Monomorphic + +Successfully optimized a guarded monomorphic property access. This +property access is about twice as expensive as a definite property access. +A guarded monomorphic property access is basically a direct memory load +from an object, guarded by a type or shape check. In pseucodcode: + + if HiddenType(obj) === CONSTANT_TYPE: + return obj[CONSTANT_SLOT_OFFSET] + else + BAILOUT() + +### Polymorphic + +Successfully optimized a guarded polymorphic property access. This +property access is similar to a monomorphic property access, except +that multiple different hidden types have been observed, and each of +them are being checked for. In pseudocode: + + if HiddenType(obj) === CONSTANT_TYPE_1: + return obj[CONSTANT_SLOT_OFFSET_1] + elif HiddenType(obj) === CONSTANT_TYPE_2: + return obj[CONSTANT_SLOT_OFFSET_2] + ... + elif HiddenType(obj) === CONSTANT_TYPE_K: + return obj[CONSTANT_SLOT_OFFSET_K] + else + BAILOUT() + +## Inline Cache Outcomes + +Outcomes describing inline cache stubs that were generated. + +### ICOptStub_GenericSuccess + +Generic success condition for generating an optimized inline cache stub. + +### ICGetPropStub_ReadSlot + +An inline cache property read which loads a value from a known slot +location within an object. + +### ICGetPropStub_CallGetter + +An inline cache property read which calls a getter function. + +### ICGetPropStub_ArrayLength + +An inline cache property read which retrieves the length of an array +object. + +### ICGetPropStub_UnboxedRead + +An inline cache property read which retrieves an value from an unboxed +object. + +### ICGetPropStub_UnboxedReadExpando + +An inline cache property read which retrieves an value from the expando +component of an unboxed object. + +### ICGetPropStub_UnboxedArrayLength + +An inline cache property read which retrieves the length of an unboxed +array object. + +### ICGetPropStub_TypedArrayLength + +An inline cache property read which retrieves the length of an typed array +object. + +### ICGetPropStub_DOMProxyShadowed + +An inline cache property read which retrieves a shadowed property from a +DOM object. A shadowed property is an inbuilt DOM property that has been +overwritten by JS code. + +### ICGetPropStub_DOMProxyUnshadowed + +An inline cache property read which retrieves an unshadowed property from +a DOM object. + +### ICGetPropStub_GenericProxy + +An inline cache property read which retrieves the property by calling into +a generic proxy trap. + +### ICGetPropStub_ArgumentsLength + +An inline cache property read which reads the length value from an +arguments object. + +### ICSetPropStub_Slot + +An inline cache property write which sets a value to a known slot location +in an object. + +### ICSetPropStub_GenericProxy + +An inline cache property write which sets the value by calling into a +generic proxy trap. + +### ICSetPropStub_DOMProxyShadowed + +An inline cache property write which sets a shadowed property from a DOM +object. A shadowed property is an inbuilt DOM property that has been +overwritten by JS code. + +### ICSetPropStub_DOMProxyUnshadowed + +An inline cache property write which sets an unshadowed property on an +DOM object. + +### ICSetPropStub_CallSetter + +An inline cache property write which calls a setter function on an object. + +### ICSetPropStub_AddSlot + +An inline cache property write which adds a new slot to an object, +because the object is known to not have such a property already. + +### ICSetPropStub_SetUnboxed + +An inline cache property write which sets an unboxed value on an unboxed +object. + +### ICGetElemStub_ReadSlot + +An inline cache element read which loads a value from a known property +slot in an object. + +### ICGetElemStub_CallGetter + +An inline cache element read which calls a getter function on the object. + +### ICGetElemStub_ReadUnboxed + +An inline cache element read which loads an unboxed value from a known +property slot on an unboxed object. + +### ICGetElemStub_Dense + +An inline cache element read which loads an element from a dense array. + +### ICGetElemStub_DenseHole + +An inline cache element read which loads an element from a dense array +which might have a hole. + +### ICGetElemStub_TypedArray + +An inline cache element read which loads an element from a typed array. + +### ICGetElemStub_ArgsElement + +An inline cache element read which loads an element from an `arguments` +object. + +### ICGetElemStub_ArgsElementStrict + +An inline cache element read which loads an element from an `arguments` +object in strict mode. + +### ICSetElemStub_Dense + +An inline cache element write which sets an element on a dense array. + +### ICSetElemStub_TypedArray + +An inline cache element write which sets an element on a typed array. + +### ICNameStub_ReadSlot + +An inline cache element which loads a bare variable name from a slot on +a scope chain object. + +### ICNameStub_CallGetter + +An inline cache element which loads a bare variable name by calling a +getter function on the global object. + +### ICNameStub_TypeOfNoProperty + +An inline cache element which loads undefined for the type +of a missing property. + +## Call Inlining Outcomes + +Optimization outcomes of attempts to inline function calls. + +### CantInlineGeneric + +Generic failure-to-inline outcome. + +### CantInlineClassConstructor + +Cannot inline a class constructor function. + +### CantInlineExceededDepth + +Inlining stopped because inlining depth was exceeded. + +### CantInlineExceededTotalBytecodeLength + +Inlining stopped because the total bytecode length of all inlined +functions exceeded a threshold. + +### CantInlineBigCaller + +Inlining was aborted because the caller function is very large. + +### CantInlineBigCallee + +Inlining was aborted because the callee function is very large. + +### CantInlineBigCalleeInlinedBytecodeLength + +Inlining was aborted because the callee is known to inline a lot of code. + +### CantInlineNotHot + +Inlining was aborted because the callee is not hot enough. + +### CantInlineNotInDispatch + +This failure mode relates to inlining a method call of the form: + + obj.method() + +where a hidden type check on the target object reveals the method to be +inlined. If the property access for `method` does not identify +the method to be inlined, the inlining fails with this code. + +### CantInlineNativeBadType + +Inlining attempt of native function was aborted because the hidden +type for the function, or the result of the function, was not +compatible with one of the known inline-able native functions. + +### CantInlineNoTarget + +Unable to inline function call. The callee (target) function, f in f(x), +is not known at JIT time. + +### CantInlineNotInterpreted + +Unable to inline function call. The callee function is not an interpreted +function. For example, it could be a native function for which Ion has no +built-in specialization. + +### CantInlineNoBaseline + +Unable to inline function call. The interpreted callee function could not +be compiled by the Baseline compiler. + +### CantInlineLazy + +Unable to inline function call. The interpreted callee function has a +lazy, compiled on-demand script instead of an already compiled script. + +### CantInlineNotConstructor + +Unable to inline function call. Rare. The interpreted callee function is +invoked with new but cannot be called as a constructor. Property +accessors, Function.prototype, and arrow (=>) functions cannot be called +as constructors. + +### CantInlineDisableIon + +Unable to inline function call. The interpreted callee function has been +explicitly blacklisted against Ion compilation. + +### CantInlineTooManyArgs + +Unable to inline function call. The interpreted callee function either has +too many parameters or is called with too many arguments. These thresholds +are subject to change. + +### CantInlineRecursive + +Unable to inline function call. The interpreted callee function recurs for +more than one level. The first level of recursion is inlineable. + +### CantInlineHeavyweight + +Unable to inline function call. The interpreted callee function contains +variables bindings that are closed over. For example, +`function f() { var x; return function () { x++; } }` +closes over x in f. + +### CantInlineNeedsArgsObj + +Unable to inline function call. The interpreted callee function requires +an arguments object to be created. + +### CantInlineDebuggee + +Unable to inline function call. The interpreted callee function is being +debugged by the Debugger API. + +### CantInlineUnknownProps + +Unable to inline function call. The engine knows nothing definite about +the type of the callee function object. + +### CantInlineUnreachable + +Unable to inline function call. The call site has not been observed to +have ever been executed. It lacks observed type information for its arguments, +its return value, or both. + +### CantInlineBound + +Unable to inline function call. The interpreted callee is a bound function +generated from `Function.prototype.bind` that failed some sub-checks. +(expand) + +### CantInlineNativeNoSpecialization + +Unable to inline function call. Ion does not have built-in specialization for +the native (implemented in C++) callee function. + +### CantInlineNativeBadForm + +Unable to inline function call. The native callee function was called with an +unsupported number of arguments, or calling non-constructing functions with new. + +### CantInlineBadType + +Unable to inline function call. The native callee function was called with +arguments with types that the built-in specialization does not support. For +example, calling Math functions on objects. + +### CantInlineNativeNoTemplateObj + +Unable to inline function call. Cannot inline a native constructor (e.g., new +Array) because no template object was cached by the Baseline compiler. (expand) diff --git a/js/src/doc/JITOptimizations/Strategies.md b/js/src/doc/JITOptimizations/Strategies.md new file mode 100644 index 0000000000..d4feef3103 --- /dev/null +++ b/js/src/doc/JITOptimizations/Strategies.md @@ -0,0 +1,582 @@ +# JIT Optimization Strategies + +SpiderMonkey's optimizing JIT, IonMonkey, uses a number of different +optimization strategies to speed up various operations. The most commonplace +operations that are relevant for fast program execution are property accesses +and function calls. + +Optimization information is currently collected for the following operations: + +- [BinaryArith](#binaryarith) (`+-/*%`) +- [GetProperty](#getprop) (`obj.prop`) +- [SetProperty](#setprop) (`obj.prop = val`) +- [GetElement](#getelem) (`obj[elemName]`) +- [SetElement](#setelem) (`obj[elemName] = val`) +- [Call](#call) (`func(...)`) + +At each operation site, IonMonkey tries a battery of <i>strategies</i>, from +the most optimized but most restrictive to the least optimized but least +restrictive. For each strategy attempted, its <i>outcome</i> is tracked. An +outcome is either success or a reason why the strategy failed to apply. + +This page documents the various optimization strategies and their outcomes. It +provides information on what they attempt to do, what general level of +speed-up they provide, what kind of program characteristics can prevent them +from being used, and common ways to enable the engine to utilize that +optimization. + +## <a name="binaryarith"></a>BinaryArith + +### BinaryArith_Concat + +Attempts to optimize an addition to string concatenation. The types of the operands are checked to contain a hint of being a concatenation. Both have to be string or one has to be a string and the other a type that easily can get converted to string (like numbers). + +### BinaryArith_SpecializedTypes + +Attempts to do a numeric arithmetic based on operand types. One of the inputs need to be a numeric type and the other a simple arithmetic type. + +### BinaryArith_SpecializedOnBaselineTypes + +Just like BinrayArith_SpecializedTypes tries to do a numeric arithmetic, but based on the observed types in the Baseline Compiler. If it succeeds it will roughly give same optimization as BinaryArith_SpecializedTypes, but the deduction is more fragile. In the best case one should try to get this optimization based on the input types. + +### BinaryArith_SharedCache + +Attempts to optimize a binary arithmetic using inline cache. + +### BinaryArith_Call + +Last resort which cannot get specialized at all and takes the slow path to do the arithmetic. + + +## <a name="getprop"></a>GetProperty + +### GetProp_ArgumentsLength + +Attempts to optimize an `arguments.length` property access. This optimization +only works if the arguments object is used in well-understood ways within the +function. The function containing the arguments.length is allowed to use the +arguments object in the following ways without disabling this optimization: + +- Access `arguments.length` +- Access `arguments.callee` +- Access individual args using `arguments[i]` +- Save arguments into variables, as long as those variables cannot be accessed + by any nested function, and as long as there exists no eval nywhere within + the function or nested function definitions. +- Call a function using `f.apply(obj, arguments)` + +If the function contains any use of the arguments object that falls out of the +cases defined above, this optimization will be suppressed. In particular, +`arguments` cannot be returned from the function, or passed as an argument into +calls (except for the `apply` case above). + +### GetProp_ArgumentsCallee + +Attempts to optimize an `arguments.callee` property access. This optimization +only works if the arguments object is used in well-understood ways within the +function. The function containing the `arguments.callee` is allowed to use the +arguments object in the following ways without disabling this optimization: + +- Access arguments.length +- Access arguments.callee +- Access individual args using arguments[i] +- Save arguments into variables, as long as those variables cannot be accessed + by any nested function, and as long as there exists no eval nywhere within + the function or nested function definitions. +- Call a function using `f.apply(obj, arguments)` + +If the function contains any use of the `arguments` object that falls out of +the cases defined above, this optimization will be suppressed. In particular, +arguments cannot be returned from the function, or passed as an argument into +calls (except for the `apply` example listed above). + +### GetProp_InferredConstant + +Attempts to optimize an access to a property that seems to be a constant. It +applies to property accesses on objects which are global-like in that there is +only one instance of them per program. This includes global objects, object +literals defined at the top-level of a script, and top-level function objects. + +This optimization makes the assumption that a property that has not changed +after it was first assigned, is likely a constant property. It then directly +inlines the value of the property into hot code that accesses it. For +example, in the following code: + + var Constants = {}; + Constants.N = 100; + + function testArray(array) { + for (var i = 0; i < array.length; i++) { + if (array[i] > Constants.N) + return true; + } + return false; + } + +Will have the loop compiled into the following when `testArray` gets hot. + + for (var i = 0; i < array.length; i++) { + if (array[i] > 100) + return true; + } + +When this optimization is successful, property access is eliminated entirely +and replaced with an inline constant. + +### GetProp_Constant + +Attempts to optimize reading a property that contains a uniquely-typed (or +"singleton") object. With uniquely-typed objects, it is guaranteed that +no other object has that same type. Unique (or "singleton") types are +assigned to certain kinds of objects, like global objects, top-level +functions, and object literals declared at the top-level of a script. If +a property has always contained the same uniquely-typed object, then the +engine can use the unique type to map back to a specific object, and +eliminate the property access, replacing it with a constant reference to +the object. + +When this optimization is successful, property access is eliminated entirely +and replaced with an inline constant. The different success and failure +conditions are documented below: + +### GetProp_StaticName + +Attempts to optimize a property access on `window` which refers to +a property on the global object. + +### GetProp_TypedObject + +Optimizes accesses to properties on TypedObjects. + +### GetProp_DefiniteSlot + +Optimizes access to a well-known regular property on an object. For this +optimization to succeed, the property needs to be well-defined on the object. +For objects constructed by constructor functions, this means that the property +needs to be defined in the constructor, before any complex logic occurs within +the constructor. + +This is the best case for a regular "field" type property that is not +turned into a constant. It compiles down to a single CPU-level load +instruction. + + function SomeConstructor() { + this.x = 10; // x is a definite slot property + this.y = 10; // y is a definite slot property + someComplicatedFunctionCall(); + this.z = 20; // z is not a definite slot property. + } + +In the above example, the properties `x` and `y` can be determined to always +exist on any instance of `SomeConstructor` at definite locations, allowing +the engine to deterministically infer the position of `x` without a shape +guard. + +This optimization can fail for a number of reasons. If the types observed +at the property access are polymorphic (more than one type), this optimization +cannot succeed. Furthermore, even if the object type is monomorphic, the +optimization will fail if the property being accessed is not a definite +slot as described above. + +### GetProp_Unboxed + +Similar to `GetProp_DefiniteSlot`. Unboxed property reads are possible on +properties which satisfy all the characteristics of a definite slot, and +additionally have been observed to only store values of one kind of value. + +Consider the following constructor: + + function Point(x, y) { + this.x = x; + this.y = y; + } + +If only integers are ever stored in the `x` and `y` properties, +then the instances of `Point` will be represented in an "unboxed" mode - +with the property values stored as raw 4-byte values within the object. + +Objects which have the unboxed optimization are more compact. + +### GetProp_CommonGetter + +Optimizes access to properties which are implemented by a getter function, +where the getter is shared between multiple types. + +This optimization applies most often when the property access site is +polymorphic, but all the object types are derived variants of a single +base class, where the property access refers to a getter on the base +class. + +Consider the following example: + function Base() {} + Base.prototype = { + get x() { return 3; } + }; + + function Derived1() {} + Derived1.prototype = Object.create(Base.prototype); + + function Derived2() {} + Derived1.prototype = Object.create(Base.prototype); + +If a property access for `d.x` sees only instances of both `Derived1` and +`Derived2` for `d`, it can optimize the access to `x` to a call to the +getter function defined on `Base`. + +This optimization applies to shared getters on both pure JS objects as well +as DOM objects. + +### GetProp_InlineAccess + +Optimizes a polymorphic property access where there are only a few different +types of objects seen, and the property on all of the different types is +determinable through a shape-check. + +If a property access is monomorphic and the property's location is determinable +from the object's shape, but the property is not definite (see: +GetProp_DefiniteProperty), then this optimization may be used. + +Alternatively, if the property access is polymorphic, but only has a few +different shapes observed at the access site, this optimization may be used. + +This optimization compiles down to one-or more shape-guarded direct loads +from the object. The following pseudocode describes the kind of machine +code generated by this optimization: + + if obj.shape == Shape1 then + obj.slots[0] + elif obj.shape == Shape2 then + obj.slots[5] + elif obj.shape == Shape3 then + obj.slots[2] + ... + end + +### GetProp_Innerize + +Attempts to optimize a situation where a property access of the form +`window.PROP` can be directly translated into a property access on +the inner global object. + +This optimization will always fail on property accesses which are +not on the window object. + +It is useful because accessing global names via the 'window' object +is a common idiom in web programming. + +### GetProp_InlineCache + +This is the worst-case scenario for a property access optimization. This +strategy is used when all the others fail. The engine simply inserts +an inline cache at the property access site. + +Inline caches start off as a jump to a separate piece of code called +a "fallback". The fallback calls into the interpreter VM (which is +very slow) to perform the operation, and then decides if the operation +can be optimized in that particular case. If so, it generates a new +"stub" (or freestanding piece of jitcode) and changes the inline cache +to jump to the stub. The stub attempts to optimize further occurrences +of that same kind of operation. + +Inline caches are an order of magnitude slower than the other optimization +strategies, and are an indication that the type inference engine has +failed to collect enough information to guide the optimization process. + +## <a name="setprop"></a>SetProperty + +### SetProp_CommonSetter + +Optimizes access to properties which are implemented by a setter function, +where the setter is shared between multiple types. + +This optimization applies most often when the property access site is +polymorphic, but all the object types are derived variants of a single +base class, where the property access refers to a setter on the base +class. + +Consider the following example: + function Base() {} + Base.prototype = { + set x(val) { ... } + }; + + function Derived1() {} + Derived1.prototype = Object.create(Base.prototype); + + function Derived2() {} + Derived1.prototype = Object.create(Base.prototype); + +If a property write for `d.x = val` sees only instances of both `Derived1` and +`Derived2` for `d`, it can optimize the write to `x` to a call to the +setter function defined on `Base`. + +This optimization applies to shared setters on both pure JS objects as well +as DOM objects. + +### SetProp_TypedObject + +Optimizes accesses to properties on TypedObjects. + +### SetProp_DefiniteSlot + +Optimizes a write to a well-known regular property on an object. For this +optimization to succeed, the property needs to be well-defined on the object. +For objects constructed by constructor functions, this means that the property +needs to be defined in the constructor, before any complex logic occurs within +the constructor. + +This is the best case for a regular "field" type property that is not +turned into a constant. It compiles down to a single CPU-level load +instruction. + + function SomeConstructor() { + this.x = 10; // x is a definite slot property + this.y = 10; // y is a definite slot property + someComplicatedFunctionCall(); + this.z = 20; // z is not a definite slot property. + } + +In the above example, the properties `x` and `y` can be determined to always +exist on any instance of `SomeConstructor` at definite locations, allowing +the engine to deterministically infer the position of `x` without a shape +guard. + +This optimization can fail for a number of reasons. If the types observed +at the property access are polymorphic (more than one type), this optimization +cannot succeed. Furthermore, even if the object type is monomorphic, the +optimization will fail if the property being written is not a definite +slot as described above. + +### SetProp_Unboxed + +Similar to `SetProp_DefiniteSlot`. Unboxed property writes are possible on +properties which satisfy all the characteristics of a definite slot, and +additionally have been observed to only store values of one kind of value. + +Consider the following constructor: + + function Point(x, y) { + this.x = x; + this.y = y; + } + +If only integers are ever stored in the `x` and `y` properties, +then the instances of `Point` will be represented in an "unboxed" mode - +with the property values stored as raw 4-byte values within the object. + +Objects which have the unboxed optimization are more compact. + +### SetProp_InlineAccess + +Optimizes a polymorphic property write where there are only a few different +types of objects seen, and the property on all of the different types is +determinable through a shape-check. + +If a property write is monomorphic and the property's location is determinable +from the object's shape, but the property is not definite (see: +GetProp_DefiniteProperty), then this optimization may be used. + +Alternatively, if the property write is polymorphic, but only has a few +different shapes observed at the access site, this optimization may be used. + +This optimization compiles down to one-or more shape-guarded direct stores +to the object. The following pseudocode describes the kind of machine +code generated by this optimization: + + if obj.shape == Shape1 then + obj.slots[0] = val + elif obj.shape == Shape2 then + obj.slots[5] = val + elif obj.shape == Shape3 then + obj.slots[2] = val + ... + end + +### SetProp_InlineCache + +This is the worst-case scenario for a property access optimization. This +strategy is used when all the others fail. The engine simply inserts +an inline cache at the property write site. + +Inline caches start off as a jump to a separate piece of code called +a "fallback". The fallback calls into the interpreter VM (which is +very slow) to perform the operation, and then decides if the operation +can be optimized in that particular case. If so, it generates a new +"stub" (or freestanding piece of jitcode) and changes the inline cache +to jump to the stub. The stub attempts to optimize further occurrences +of that same kind of operation. + +Inline caches are an order of magnitude slower than the other optimization +strategies, and are an indication that the type inference engine has +failed to collect enough information to guide the optimization process. + +## <a name="getelem"></a>GetElement + +### GetElem_TypedObject + +Attempts to optimized element accesses on array Typed Objects. + +### GetElem_Dense + +Attempts to optimize element accesses on densely packed array objects. Dense +arrays are arrays which do not have any 'holes'. This means that the array +has valid values for all indexes from `0` to `length-1`. + +### GetElem_TypedStatic + +Attempts to optimize element accesses on a typed array that can be determined +to always refer to the same array object. If this optimization succeeds, the +'array' object is treated as a constant, and is not looked up or retrieved from +a variable. + +### GetElem_TypedArray + +Attempts to optimize element accesses on a typed array. + +### GetElem_String + +Attempts to optimize element accesses on a string. + +### GetElem_Arguments + +Attempts to optimize element accesses on the `arguments` special object +available in functions. This optimization only works if the arguments +object is used in well-understood ways within the function. The function +containing the arguments.length is allowed to use the arguments object in +the following ways without disabling this optimization: + +- Access `arguments.length` +- Access `arguments.callee` +- Access individual args using `arguments[i]` +- Save `arguments` into variables, as long as those variables cannot be + accessed by any nested function, and as long as there exists no `eval` + anywhere within the function or nested function definitions. +- Call a function using `f.apply(obj, arguments)` + +If the function contains any use of the arguments object that falls out of the +cases defined above, this optimization will be suppressed. In particular, +`arguments` cannot be returned from the function, or passed as an argument into +calls (except for the `apply` case above). + +### GetElem_ArgumentsInlined + +Similar to GetEelem_Arguments, but optimizes cases where the access on +`arguments` is happening within an inlined function. In these cases, an +access of the form `arguments[i]` can be directly translated into a +direct reference to the corresponding argument value in the inlined call. + +Consider the following: + function foo(arg) { + return bar(arg, 3); + } + function bar() { + return arguments[0] + arguments[1]; + } + +In the above case, if `foo` is compiled with Ion, and the call to `bar` +is inlined, then this optimization can transform the entire procedure to +the following pseudo-code: + + compiled foo(arg): + // inlined call to bar(arg, 3) { + return arg + 3; + // } + +### GetElem_InlineCache + +This is the worst-case scenario for a element access optimization. This +strategy is used when all the others fail. The engine simply inserts +an inline cache at the property write site. + +Inline caches start off as a jump to a separate piece of code called +a "fallback". The fallback calls into the interpreter VM (which is +very slow) to perform the operation, and then decides if the operation +can be optimized in that particular case. If so, it generates a new +"stub" (or freestanding piece of jitcode) and changes the inline cache +to jump to the stub. The stub attempts to optimize further occurrences +of that same kind of operation. + +Inline caches are an order of magnitude slower than the other optimization +strategies, and are an indication that the type inference engine has +failed to collect enough information to guide the optimization process. + +## <a name="setelem"></a>SetElement + +### SetElem_TypedObject + +Attempts to optimized element writes on array Typed Objects. + +### SetElem_TypedStatic + +Attempts to optimize element writes on a typed array that can be determined +to always refer to the same array object. If this optimization succeeds, the +'array' object is treated as a constant, and is not looked up or retrieved from +a variable. + +### SetElem_TypedArray + +Attempts to optimize element writes on a typed array. + +### SetElem_Dense + +Attempts to optimize element writes on densely packed array objects. Dense +arrays are arrays which do not have any 'holes'. This means that the array +has valid values for all indexes from `0` to `length-1`. + +### SetElem_Arguments + +Attempts to optimize element writes to the `arguments` special object +available in functions. This optimization only works if the arguments +object is used in well-understood ways within the function. The function +containing the arguments.length is allowed to use the arguments object in +the following ways without disabling this optimization: + +- Access `arguments.length` +- Access `arguments.callee` +- Access individual args using `arguments[i]` +- Save `arguments` into variables, as long as those variables cannot be + accessed by any nested function, and as long as there exists no `eval` + anywhere within the function or nested function definitions. +- Call a function using `f.apply(obj, arguments)` + +If the function contains any use of the arguments object that falls out of the +cases defined above, this optimization will be suppressed. In particular, +`arguments` cannot be returned from the function, or passed as an argument into +calls (except for the `apply` case above). + +### SetElem_InlineCache + +This is the worst-case scenario for a element write optimization. This +strategy is used when all the others fail. The engine simply inserts +an inline cache at the property write site. + +Inline caches start off as a jump to a separate piece of code called +a "fallback". The fallback calls into the interpreter VM (which is +very slow) to perform the operation, and then decides if the operation +can be optimized in that particular case. If so, it generates a new +"stub" (or freestanding piece of jitcode) and changes the inline cache +to jump to the stub. The stub attempts to optimize further occurrences +of that same kind of operation. + +Inline caches are an order of magnitude slower than the other optimization +strategies, and are an indication that the type inference engine has +failed to collect enough information to guide the optimization process. + +## <a name="call"></a>Call + +### Call_Inline + +A function call `f(x)` usually pushes a frame onto the call stack. Inlining a +call site conceptually copies the body of the callee function and pastes it +in place of the call site and avoids pushing a new execution frame. Usually, +hot functions do well to be inlined. This is one of the most important +optimizations the JIT performs. + +Ion inlines both interpreted (i.e., written in JavaScript) functions and +native (i.e., built-ins such as `Math.sin` implemented in C++). + +A successfully inlined call site has the outcome Inlined. + +Failure to inline comes in two flavors: unable (e.g., unable to determine +exact callee) and unwilling (e.g., heuristics concluded that the time-space +tradeoff will not pay off). diff --git a/js/src/doc/JITOptimizations/config.sh b/js/src/doc/JITOptimizations/config.sh new file mode 100644 index 0000000000..087384ec64 --- /dev/null +++ b/js/src/doc/JITOptimizations/config.sh @@ -0,0 +1,15 @@ +base-url https://developer.mozilla.org/en-US/docs/Tools/ + +markdown Strategies.md JIT-Optimization/Strategies + label 'jitstrategies' 'JIT Optimization Strategies' + label 'getprop' '#getproperty' 'JIT Optimization Strategies : GetProperty' + label 'setprop' '#setproperty' 'JIT Optimization Strategies : SetProperty' + label 'getelem' '#getelement' 'JIT Optimization Strategies : GetElement' + label 'setelem' '#setelement' 'JIT Optimization Strategies : SetElement' + label 'call' '#call' 'JIT Optimization Strategies : Call' + +markdown Strategies.md JIT-Optimization/Outcomes + label 'jitoutcomes' 'JIT Optimization Outcomes' + label 'general' '#general' 'JIT Optimization Outcomes : General Outcomes' + label 'ic' '#inlinecache' 'JIT Optimization Outcomes : Inline Cache Outcomes' + label 'callinline' '#callinline' 'JIT Optimization Outcomes : Call Inlining Outcomes' diff --git a/js/src/doc/README.md b/js/src/doc/README.md new file mode 100644 index 0000000000..989fa40db7 --- /dev/null +++ b/js/src/doc/README.md @@ -0,0 +1,160 @@ +SpiderMonkey in-tree documentation +================================== + +This directory contains documentation for selected parts of SpiderMonkey. The +docs are published on the [Mozilla Developer Network][mdn], but this is the +authoritative copy; the MDN pages are updated from these files by a script. At +the moment, we have: + +- `js/src/doc/Debugger`, SpiderMonkey's JavaScript debugging API, commonly + known as `Debugger`. + +- `js/src/doc/SavedFrame`, SpiderMonkey's compact representation for captured + call stacks. + +and that's it. + +To format the documentation, you'll need to install [Pandoc][], a +documentation format conversion program. Pandoc can convert Markdown text, +which is pleasant to read and write, into HTML, which is what MDN expects. + +[mdn]: http://developer.mozilla.org "Mozilla Developer Network" +[Pandoc]: http://www.johnmacfarlane.net/pandoc/installing.html + +Management scripts +------------------ + +There are two scripts you may want to use while working with `js/src/doc` +subdirectories: + +- `format.sh [--mdn] SOURCEDIR OUTPUTDIR` produces HTML from the document + sources in SOURCEDIR, placing the results in OUTPUTDIR. You can then + check their appearance with a web browser. + + Normally, format.sh arranges the links and HTML metadata so that the + pages view correctly when visited at `file://` URLS pointing into + OUTPUTDIR. However, pages published to MDN should not have the HTML + metadata that stand-alone pages need, and their relative positions may be + different; passing the `--mdn` switch tells `format.sh` to produce output + for publication to MDN, not for previewing on disk. + + (Why are the links affected by `--mdn`? The MDN wiki allows you to create + a page named `.../foo`, and then create sub-pages named `.../foo/bar`. + This is a nice way to arrange, say, a summary page and then sub-pages + providing details. But it's impossible to create the parallel structure + on a POSIX file system: `.../foo` can't be both a file and a directory, + so the links that would be correct when published on the wiki cannot be + correct when previewing those pages on disk. Since OUTPUTDIR's layout + can't match that of the wiki, we make it match that of SOURCEDIR.) + +- `publish.sh SOURCEDIR OUTPUTDIR KEYID SECRET` calls `format.sh`, and then + posts the pages to MDN, using KEYID and SECRET to establish your + identity. This posts only changed pages, so you can run it whenever the + text you have is the right stuff to publish, without creating gratuitous + churn in the MDN page history. + + To generate KEYID and SECRET, visit the [MDN API key generation page][mdnkey]. + +[mdnkey]: https://developer.mozilla.org/en-US/keys/ "MDN API key generation" + + +Why not make the wiki the authoritative copy? +--------------------------------------------- + +Storing documentation in the same tree as the sources it describes has several +advantages: + +- It's easy to handle documentation changes as part of the same review process + we use for code changes. A patch posted to Bugzilla can contain code, test, + and doc changes, all of which can be discussed together. + +- The version control system that manages the code also manages its + documentation. Branching the code (for Nightly, Aurora, Beta, or Release) + branches the docs as well, so one can always find the docs that match the + code in a given release stage. + +- Documentation for proposed changes has a natural home: in the patches + that implement the change (or, at least, in a patch attached to the bug + discussing the change). There's no need to include "(not yet + implemented)" markers in the published docs. + + +Subdirectory layout and script interface +---------------------------------------- + +Alongside the documentation source files, the SOURCEDIR passed to +`format.sh` should contain a file named `config.sh` describing the +directory's contents, how to format them, and where they should be +installed. This data is represented as executable shell code; `format.sh` +and `publish.sh` run the subdirectory's `config.sh` script to learn what +they should do. + +The only effect of running a `SOURCEDIR/config.sh` script should be to +invoke the following commands: + +`base-url BASE` +: Treat BASE as the common prefix for some URLs appearing as arguments to + subsequently executed commands (other than resource files). In + describing the other commands, we use the metavariable RELATIVE-URL for + URLs that are relative to BASE. + + This command should appear before those taking RELATIVE-URL arguments. + + BASE is treated as the root directory of the tree of formatted files. + If OUTPUTDIR is the output directory passed to `format.sh` or + `publish.sh`, then formatted files appear in OUTPUTDIR at the paths + they would appear on MDN relative to BASE. + +`markdown FILE RELATIVE-URL` +: Treat FILE as Markdown source for a documentation page to be published + at RELATIVE-URL. + +`label LABEL [FRAGMENT] [TITLE]` +: Define a label for use in Markdown reference-style links referring to + the file given in the preceding `markdown` command. If the second + argument begins with a `#` character, it is taken to be an HTML + fragment identifier; the link refers to that fragment in the page. + TITLE, if present, is the title for the link. + + For example: + + base-url https://devmo/Tools/ + markdown Conventions.md Debugger-API/Conventions + label conventions "Debugger API: general conventions" + label cv #completion-values "Debugger API: completion values" + + would mean that `Conventions.md` should be processed to create + `https://devmo/Tools/Debugger-API/Conventions`; that Markdown files can + refer to that page like this: + + ... follows some [common conventions][conventions] which ... + + and to the `#completion-values` fragment in that page like this: + + ... is passed a [completion value][cv] indicating ... + +`absolute-label LABEL URL [TITLE]` +: For reference-style links in this directory's Markdown files, define + LABEL to refer to URL, an absolute URL. TITLE is an optional link title. + +`resource LABEL FILE URL` +: Treat FILE as a resource file (an image, for example) that should + appear at URL. Since MDN likes to place "attachments" like images under + different URL prefixes than the wiki pages themselves, URL is not + relative to the BASE passed to base-url. + + LABEL can be the empty string if no Markdown documents need to refer to + this resource file. (For example, the Markdown might use an SVG file, + which in turn use the resource file.) + + Unfortunately, `publish.sh` can't automatically post these resources to + MDN at the moment. However, it will check if the contents have changed, + and print a warning. + + +This ought to be integrated with mach +------------------------------------- + +Indeed. It should somehow be unified with 'mach doc', which seems to +format in-tree docs of a different kind, and use a different markup +language (ReStructuredText) and a different formatter (Sphinx). diff --git a/js/src/doc/SavedFrame/SavedFrame.md b/js/src/doc/SavedFrame/SavedFrame.md new file mode 100644 index 0000000000..7d06b445b8 --- /dev/null +++ b/js/src/doc/SavedFrame/SavedFrame.md @@ -0,0 +1,88 @@ +# `SavedFrame` + +A `SavedFrame` instance is a singly linked list of stack frames. It represents a +JavaScript call stack at a past moment of execution. Younger frames hold a +reference to the frames that invoked them. The older tails are shared across +many younger frames. + +`SavedFrame` stacks should generally be captured, allocated, and live within the +compartment that is being observed or debugged. Usually this is a content +compartment. + +## Capturing `SavedFrame` Stacks + +### From C++ + +Use `JS::CaptureCurrentStack` declared in `jsapi.h`. + +### From JS + +Use `saveStack`, accessible via `Components.utils.getJSTestingFunction()`. + +## Including and Excluding Chrome Frames + +Consider the following `SavedFrame` stack. Arrows represent links from child to +parent frame, `content.js` is from a compartment with content principals, and +`chrome.js` is from a compartment with chrome principals. + + function A from content.js + | + V + function B from chrome.js + | + V + function C from content.js + +The content compartment will ever have one view of this stack: `A -> C`. + +However, a chrome compartment has a choice: it can either take the same view +that the content compartment has (`A -> C`), or it can view all stack frames, +including the frames from chrome compartments (`A -> B -> C`). To view +everything, use an `XrayWrapper`. This is the default wrapper. To see the stack +as the content compartment sees it, waive the xray wrapper with +`Components.utils.waiveXrays`: + + const contentViewOfStack = Components.utils.waiveXrays(someStack); + +## Accessor Properties of the `SavedFrame.prototype` Object + +`source` +: The source URL for this stack frame, as a string. + +`line` +: The line number for this stack frame. + +`column` +: The column number for this stack frame. + +`functionDisplayName` +: Either SpiderMonkey's inferred name for this stack frame's function, or + `null`. + +`asyncCause` +: If this stack frame is the `asyncParent` of other stack frames, then this is + a string representing the type of asynchronous call by which this frame + invoked its children. For example, if this frame's children are calls to + handlers for a promise this frame created, this frame's `asyncCause` would + be `"Promise"`. If the asynchronous call was started in a descendant frame + to which the requester of the property does not have access, this will be + the generic string `"Async"`. If this is not an asynchronous call point, + this will be `null`. + +`asyncParent` +: If this stack frame was called as a result of an asynchronous operation, for + example if the function referenced by this frame is a promise handler, this + property points to the stack frame responsible for the asynchronous call, + for example where the promise was created. If the frame responsible for the + call is not accessible to the caller, this points to the youngest accessible + ancestor of the real frame, if any. In all other cases, this is `null`. + +`parent` +: This stack frame's caller, or `null` if this is the oldest frame on the + stack. In this case, there might be an `asyncParent` instead. + +## Function Properties of the `SavedFrame.prototype` Object + +`toString` +: Return this frame and its parents formatted as a human readable stack trace + string. diff --git a/js/src/doc/SavedFrame/config.sh b/js/src/doc/SavedFrame/config.sh new file mode 100644 index 0000000000..373f3436ad --- /dev/null +++ b/js/src/doc/SavedFrame/config.sh @@ -0,0 +1,6 @@ +### Description of SavedFrame docs: how to format, where to install. +### See js/src/doc/README.md for a description. + +base-url https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/ + +markdown SavedFrame.md SavedFrame diff --git a/js/src/doc/format.sh b/js/src/doc/format.sh new file mode 100755 index 0000000000..754466bdfa --- /dev/null +++ b/js/src/doc/format.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +# Format in-tree documentation for posting to MDN. +# See js/src/doc/README.md for general usage information. +# +# Usage: +# +# ./format.sh [--mdn] SOURCEDIR OUTPUTDIR +# +# Pages are tagged with the current Mercurial parent changeset ID. +# +# Normally, the generated HTML includes appropriate headers for in-place +# viewing, and the links are set up to allow the files to refer to each +# other where they're placed in OUTPUTDIR. However, if the --mdn flag is +# given, we omit non-body elements from the HTML (which seems to be what +# MDN prefers), and generate links to work with the URLs at which the pages +# will be published on MDN. + +set -eu + +progname=$(basename $0) +lib=$(cd $(dirname $0)/lib; pwd) + +# If we're producing text meant to be reviewed locally, then ask Pandoc to +# produce standalone HTML files (which include text encoding metadata and +# other helpful things). +standalone_arg=--standalone + +# If we're producing text for MDN, then generate links using the wiki URLs. +mdn_arg= + +while true; do + case "${1-}" in + '--mdn') + mdn_arg=--mdn + standalone_arg= + shift + ;; + *) + break + ;; + esac +done + +sourcedir=$1 +outputdir=$2 + +config=$sourcedir/config.sh +if ! [ -f "$config" ]; then + echo "SOURCEDIR doesn't seem to contain a 'config.sh' file: $sourcedir" >&2 + exit 1 +fi + +export JS_DOC_HG_IDENTIFY="$(hg identify | sed -e 's/ .*$//')" + +# Compute the name of the source directory relative to the hg root, for the +# "this text computed from..." message. +hg_relative_sourcedir=$((cd $sourcedir; pwd) | sed -e "s|$(hg root)/||") + +checked_pandoc=false + +source $lib/dummy-config.sh + +markdown() { + INPUT_FILE=$1 + URL=$BASE_URL$2 + + if ! $checked_pandoc; then + if ! pandoc -v > /dev/null; then + echo "$progname: This script uses the 'pandoc' formatter, but that doesn't seem" >&2 + echo "to be installed." >&2 + exit 1 + fi + checked_pandoc=true + fi + + local file=$sourcedir/$INPUT_FILE + if ! [ -f "$file" ]; then + echo "$progname: Can't find markdown file $file, mentioned by $config" >&2 + exit 1 + fi + + local output_file=$outputdir/${INPUT_FILE/md/html} + + mkdir -p $(dirname "$output_file") + pandoc $standalone_arg \ + -f markdown --smart -t html \ + "$file" \ + <("$lib/make-bibliography.sh" $mdn_arg "$config" "$URL") \ + -o "$output_file" + + "$lib/make-watermark.sh" "$output_file" "$hg_relative_sourcedir/$INPUT_FILE" >> "$output_file" +} + +resource() { + local label=$1 file=$2 url=$3 + ln -f $sourcedir/$file $outputdir/$file +} + +source "$config" diff --git a/js/src/doc/lib/common.sh b/js/src/doc/lib/common.sh new file mode 100644 index 0000000000..785f7dec12 --- /dev/null +++ b/js/src/doc/lib/common.sh @@ -0,0 +1,23 @@ +# Common utility functions for js/src/doc scripts. + +# `relative BASE ABSOLUTE` prints the URL relative to BASE that is +# equivalent to ABSOLUTE. BASE must end with a '/'. This function will +# introduce at most one level of '..'. +relative() { + local parent=$(dirname "$1") + case "$2" in + "$1"*) + # ABSOLUTE is within BASE; just remove BASE. + echo "$2" | sed -e "s|^$1||" + ;; + "$parent/"*) + # ABSOLUTE is within BASE/.. + echo "$2" | sed -e "s|^$parent/|../|" + ;; + *) + # ABSOLUTE is unrelated to BASE. + echo "$2" + ;; + esac +} + diff --git a/js/src/doc/lib/dummy-config.sh b/js/src/doc/lib/dummy-config.sh new file mode 100644 index 0000000000..dd9d39a4bb --- /dev/null +++ b/js/src/doc/lib/dummy-config.sh @@ -0,0 +1,24 @@ +# Dummy definitions of functions used in doc directory config.sh files. +# Scripts reading config.sh files should source this first, and then +# override the definitions of the functions they care about. + +base-url() { + BASE_URL=$1 +} + +markdown() { + INPUT_FILE=$1 + RELATIVE_URL=$2 +} + +label() { + : +} + +absolute-label() { + : +} + +resource() { + : +} diff --git a/js/src/doc/lib/extract-watermark.sh b/js/src/doc/lib/extract-watermark.sh new file mode 100755 index 0000000000..9f1de86421 --- /dev/null +++ b/js/src/doc/lib/extract-watermark.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Extract the js/src/doc watermark from standard input, assumed to be the +# text of a page formatted for publication on MDN. +# +# We can apply this to both the output of the formatter, and to pages +# retrieved from MDN, to see if anything needs to be updated. +# +# Usage: +# +# extract-watermark.sh +# +# For example: +# +# $ curl --silent 'https://developer.mozilla.org/en-US/docs/Tools/Debugger-API' | ./doc/lib/extract-watermark.sh +# sha256:168224ee2d58521b7c8368eddf4ac4fa53a897fa803ae484007af4e61a00ddcd +# $ + +set -eu + +# Since $(foo) trims any final newline in foo's output, this 'echo' ensures +# that our output is terminated by a newline, whether or not curl | sed's +# is. +echo $(sed -n -e "s|.*<dd id=.watermark.>\([^<]*\)</dd>.*|\1|p") diff --git a/js/src/doc/lib/make-bibliography.sh b/js/src/doc/lib/make-bibliography.sh new file mode 100755 index 0000000000..33068ca9c4 --- /dev/null +++ b/js/src/doc/lib/make-bibliography.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +# Generate a Markdown bibliography file --- that is, definitions for +# reference-style link labels --- from a documentation directory's +# config.sh file. +# +# Usage: +# make-bibliography.sh [--mdn] CONFIG CITING-URL +# +# where: +# - CONFIG is the name of the config.sh file to process; and +# - CITING-URL is the absolute URL at which the document using these labels +# will appear. The links we output use URLs relative to CITING-URL to +# refer to the URLs given in CONFIG. +# +# If given the --mdn flag, generate links that are correct for files +# as they appear in format.sh's OUTPUTDIR, not for publishing on MDN. + +set -eu + +mdn=false + +while true; do + case "${1-}" in + '--mdn') + mdn=true + shift + ;; + *) + break + ;; + esac +done + +lib=$(dirname $0) +config=$1 +citing=$2 + +source "$lib/common.sh" + +source "$lib/dummy-config.sh" + +label() { + local label=$1; shift + local fragment= + case "$1" in + '#'*) + fragment=$1; shift + ;; + esac + local title=${1:+ \"$1\"} + + citing_prefix=$(dirname "$citing")/ + if $mdn; then + echo "[$label]: $(relative "$citing_prefix" "$BASE_URL$RELATIVE_URL")$fragment$title" + else + echo "[$label]: ${INPUT_FILE/md/html}$fragment$title" + fi +} + +absolute-label() { + local label=$1 + local absolute_url=$2 + local title=$3 + + echo "[$label]: $absolute_url${title:+ \"$title\"}" +} + +resource() { + local label=$1 file=$2 absolute_url=$3 + + if [ -n "$label" ]; then + if $mdn; then + echo "[$label]: $absolute_url" + else + echo "[$label]: $file" + fi + fi +} + +source "$config" diff --git a/js/src/doc/lib/make-watermark.sh b/js/src/doc/lib/make-watermark.sh new file mode 100755 index 0000000000..a39ff5436a --- /dev/null +++ b/js/src/doc/lib/make-watermark.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Generate an HTML watermark for the given file. Claim the text was +# generated from SOURCE. +# +# Usage: +# +# make-watermark.sh FILE SOURCE + +set -eu + +# Include the watermark as visible text in the page, as suggested in bug 990662. + +echo '<h4>Source Metadata</h4>' +echo '<dl>' +echo ' <dt>Generated from file:<dt>' +echo " <dd>$2</dd>" +echo ' <dt>Watermark:</dt>' +echo " <dd id='watermark'>sha256:$(cat "$1" | shasum -a 256 | sed 's/ .*$//')</dd>" + +# If we have Mercurial changeset ID, include it. +if [ "${JS_DOC_HG_IDENTIFY:+set}" = set ]; then + # If the changeset ID has a '+' on the end (indicating local + # modifications), omit that from the link. + cat <<EOF + <dt>Changeset:</dt> + <dd><a href="https://hg.mozilla.org/mozilla-central/rev/${JS_DOC_HG_IDENTIFY%+}">${JS_DOC_HG_IDENTIFY}</a></dd> +EOF +fi + +echo '</dl>' diff --git a/js/src/doc/publish.sh b/js/src/doc/publish.sh new file mode 100755 index 0000000000..fad0b27d94 --- /dev/null +++ b/js/src/doc/publish.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +# Format js/src/doc documentation in SOURCEDIR, place formatted files in OUTPUTDIR, +# and post all changed pages to MDN using KEYID and SECRET to identify the poster. +# See js/src/doc/README.md for general usage information. +# +# Usage: +# +# ./publish.sh SOURCEDIR OUTPUTDIR KEYID SECRET +# +# Pages are tagged with the current Mercurial parent changeset ID. + +set -eu + +progname=$(basename $0) +doc=$(cd $(dirname $0); pwd) +lib=$doc/lib + +sourcedir=$1 +outputdir=$2 +keyid=$3 +secret=$4 + +$doc/format.sh --mdn "$sourcedir" "$outputdir" + +config=$sourcedir/config.sh + +watermark=$lib/extract-watermark.sh + +# Fetch a URL, with caching disabled. +fetch() { + curl --silent -XGET "$1" \ + -H"Cache-Control: no-cache, no-store, must-revalidate" \ + -H"Pragma: no-cache" \ + -H"Expires: 0" +} + +source $lib/dummy-config.sh + +markdown() { + INPUT_FILE=$1 + URL=$BASE_URL$2 + + local formatted_file=$outputdir/${INPUT_FILE/md/html} + + # Extract the watermark from the formatted file. + local local_watermark=$("$watermark" < "$formatted_file") + + # Get the existing page, and extract its watermark, if any. + local public_watermark=$(fetch "$URL?raw" | "$watermark") + + if [ "$local_watermark" != "$public_watermark" ]; then + echo "$progname: Updating: $URL" >&2 + local status + status=$(curl --silent -X PUT -H"Content-Type: text/html" --upload-file "$formatted_file" -u "$keyid:$secret" "$URL") + case "$status" in + CREATED | RESET) + ;; + *) + echo "$progname: Error posting $URL, from $config: $status" >&2 + exit 1 + ;; + esac + else + echo "$progname: Unchanged: $URL" >&2 + fi +} + +# MDN can't currently update attached resources. But we can verify that the current +# published versions match what we have. +resource() { + local label=$1 + local file=$sourcedir/$2 + local url=$3 + + if cmp "$file" <(fetch "$url") > /dev/null; then + echo "$progname: Unchanged: $url" >&2 + else + echo "$progname: Warning: resource out of date: $url" >&2 + fi +} + +source $config |