diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/doc | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/doc')
31 files changed, 7299 insertions, 0 deletions
diff --git a/js/src/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md new file mode 100644 index 0000000000..5d89eb7343 --- /dev/null +++ b/js/src/doc/Debugger/Conventions.md @@ -0,0 +1,254 @@ +# General Conventions + +This page describes general conventions used in the [`Debugger`][debugger] API, +and defines some terminology used throughout the specification. + + +## Properties + +Properties of objects that comprise the `Debugger` interface, and those +that the interface creates, follow some general conventions: + +- Instances and prototypes are extensible; you can add your own properties + and methods to them. + +- Properties are configurable. This applies to both "own" and prototype + properties, and to both methods and data properties. (Leaving these + properties open to redefinition will hopefully make it easier for + JavaScript debugger code to cope with bugs, bug fixes, and changes in the + interface over time.) + +- Method properties are writable. + +- We prefer inherited accessor properties to own data properties. Both are + read using the same syntax, but inherited accessors seem like a more + accurate reflection of what's going on. Unless otherwise noted, these + properties have getters but no setters, as they cannot meaningfully be + assigned to. + + +## Debuggee Values + +The `Debugger` interface follows some conventions to help debuggers safely +inspect and modify the debuggee's objects and values. Primitive values are +passed freely between debugger and debuggee; copying or wrapping is handled +transparently. Objects received from the debuggee (including host objects +like DOM elements) are fronted in the debugger by `Debugger.Object` +instances, which provide reflection-oriented methods for inspecting their +referents; see `Debugger.Object`, below. + +Of the debugger's objects, only `Debugger.Object` instances may be passed +to the debuggee: when this occurs, the debuggee receives the +`Debugger.Object`'s referent, not the `Debugger.Object` instance itself. + +In the descriptions below, the term "debuggee value" means either a +primitive value or a `Debugger.Object` instance; it is a value that might +be received from the debuggee, or that could be passed to the debuggee. + + +## Debuggee Code + +Each `Debugger` instance maintains a set of global objects that, taken +together, comprise the debuggee. Code evaluated in the scope of a debuggee +global object, directly or indirectly, is considered *debuggee code*. +Similarly: + +- a *debuggee frame* is a frame running debuggee code; + +- a *debuggee function* is a function that closes over a debuggee + global object (and thus the function's code is debuggee code); + +- a *debuggee environment* is an environment whose outermost + enclosing environment is a debuggee global object; and + +- a *debuggee script* is a script containing debuggee code. + + +## Completion Values + +The `Debugger` API often needs to convey the result of running some JS code. For example, suppose you get a `frame.onPop` callback telling you that a method in the debuggee just finished. Did it return successfully? Did it throw? What did it return? The debugger passes the `onPop` handler a *completion value* that tells what happened. + +A completion value is one of these: + +* `{ return: value }` + + The code completed normally, returning <i>value</i>. <i>Value</i> is a + debuggee value. + +* `{ throw: value, stack: stack }` + + The code threw <i>value</i> as an exception. <i>Value</i> is a debuggee + value. <i>stack</i> is a `SavedFrame` representing the location from which + the value was thrown, and may be missing. + +* `null` + + The code was terminated, as if by the "slow script" ribbon. + +Generators and async functions add a wrinkle: they can suspend themselves (with `yield` or `await`), which removes their frame from the stack. Later, the generator or async frame might be returned to the stack and continue running where it left off. Does it count as "completion" when a generator suspends itself? + +The `Debugger` API says yes. `yield` and `await` do trigger the `frame.onPop` handler, passing a completion value that explains why the frame is being suspended. The completion value gets an extra `.yield` or `.await` property, to distinguish this kind of completion from a normal `return`. + +```js +{ return: value, yield: true } +``` + +where *value* is a debuggee value for the iterator result object, like `{ value: 1, done: false }`, for the yield. + +When a generator function is called, it first evaluates any default argument +expressions and destructures its arguments. Then its frame is suspended, and the +new generator object is returned to the caller. This initial suspension is reported +to any `onPop` handlers as a completion value of the form: + +```js +{ return: generatorObject, yield: true, initial: true } +``` + +where *generatorObject* is a debuggee value for the generator object being +returned to the caller. + +When an async function awaits a promise, its suspension is reported to any +`onPop` handlers as a completion value of the form: + +```js +{ return: promise, await: true } +``` + +where *promise* is a debuggee value for the promise being returned to the +caller. + +The first time a call to an async function awaits, returns, or throws, a promise +of its result is returned to the caller. Subsequent resumptions of the async +call, if any, are initiated directly from the job queue's event loop, with no +calling frame on the stack. Thus, if needed, an `onPop` handler can distinguish +an async call's initial suspension, which returns the promise, from any +subsequent suspensions by checking the `Debugger.Frame`'s `older` property: if +that is `null`, the call was resumed directly from the event loop. + +Async generators are a combination of async functions and generators that can +use both `yield` and `await` expressions. Suspensions of async generator frames +are reported using any combination of the completion values above. + + +## Resumption Values + +As the debuggee runs, the `Debugger` interface calls various +debugger-provided handler functions to report the debuggee's behavior. +Some of these calls can return a value indicating how the debuggee's +execution should continue; these are called *resumption values*. A +resumption value has one of the following forms: + +* `undefined` + + The debuggee should continue execution normally. + +* `{ return: value }` + + Force the top frame of the debuggee to return <i>value</i> immediately, + as if by executing a `return` statement. <i>Value</i> must be a debuggee + value. (Most handler functions support this, except those whose + descriptions say otherwise.) See the list of special cases below. + +* `{ throw: value }` + + Throw <i>value</i> as an exception from the current bytecode + instruction. <i>Value</i> must be a debuggee value. Note that unlike + completion values, resumption values do not specify a stack. When + initiating an exceptional return from a handler, the current debuggee stack + will be used. If a handler wants to avoid modifying the stack of an + already-thrown exception, it should return `undefined`. + +* `null` + + Terminate the debuggee, as if it had been cancelled by the "slow script" + dialog box. + +In some places, the JS language treats `return` statements specially or +doesn't allow them at all. So there are a few special cases. + +* An arrow function without curly braces can't contain a return + statement, but `{ return: value }` works anyway, + returning the specified value. + + Likewise, if the top frame of the debuggee is not in a function at + all—that is, it's running toplevel code in a `script` tag, or `eval` + code—then <i>value</i> is returned even though `return` statements + aren't legal in that kind of code. (In the case of a `script` tag, + the browser discards the return value.) + +* If the debuggee is in a function that was called as a constructor (that + is, via a `new` expression), then <i>value</i> serves as the value + returned by the function's body, not that produced by the `new` + expression: if the value is not an object, the `new` expression returns + the frame's `this` value. + + Similarly, if the function is the constructor for a subclass, then a + non-object value may result in a `TypeError`. + +* Returning from a generator simulates a `return`, not a `yield`; + there is no way to force a debuggee generator to `yield`. + + The way generators execute is rather odd. When a generator-function + is first called, it is put onto the stack and runs just a few + bytecode instructions (or more, if the generator-function has any + default argument values to compute), then performs the "initial + suspend". At that point, a new generator object is created and + returned to the caller. Thereafter, the caller may cause execution + of the generator to resume at any time, by calling `genObj.next()`, + and the generator may pause itself again using `yield`. + + JS generators normally can't return before the "initial + suspend"—there’s no place to put a `return` statement—but + `{ return: value }` there works anyway, replacing + the generator object that the initial suspend would normally create + and return. + + Returning from a generator that's been resumed via `genobj.next()` + (or one of the other methods) closes the generator, and the + `genobj.next()` or other method returns a new object of the form + `{ done: true, value: value }`. + +If a debugger hook function throws an exception, rather than returning a +resumption value, we never propagate such an exception to the debuggee; +instead, we call the associated `Debugger` instance's +`uncaughtExceptionHook` property, as described below. + + +## Timestamps + +Timestamps are expressed in units of milliseconds since an arbitrary, +but fixed, epoch. The resolution of timestamps is generally greater +than milliseconds, though no specific resolution is guaranteed. + + +## The `Debugger.DebuggeeWouldRun` Exception + +Some debugger operations that appear to simply inspect the debuggee's state +may actually cause debuggee code to run. For example, reading a variable +might run a getter function on the global or on a `with` expression's +operand; and getting an object's property descriptor will run a handler +trap if the object is a proxy. To protect the debugger's integrity, only +methods whose stated purpose is to run debuggee code can do so. These +methods are called [invocation functions][inv fr], and they follow certain +common conventions to report the debuggee's behavior safely. For other +methods, if their normal operation would cause debuggee code to run, they +throw an instance of the `Debugger.DebuggeeWouldRun` exception. + +If there are debugger frames on stack from multiple Debugger instances, the +thrown exception is an instance of the topmost locking debugger's global's +`Debugger.DebuggeeWouldRun`. + +A `Debugger.DebuggeeWouldRun` exception may have a `cause` property, +providing more detailed information on why the debuggee would have run. The +`cause` property's value is one of the following strings: + +* `"proxy"`: Carrying out the operation would have caused a proxy handler to run. | +* `"getter"`: Carrying out the operation would have caused an object property getter to run. | +* `"setter"`: Carrying out the operation would have caused an object property setter to run. | + +If the system can't determine why control attempted to enter the debuggee, +it will leave the exception's `cause` property undefined. + + +[debugger]: Debugger-API.md +[inv fr]: Debugger.Frame.md#invocation-functions-and-debugger-frames diff --git a/js/src/doc/Debugger/Debugger-API.md b/js/src/doc/Debugger/Debugger-API.md new file mode 100644 index 0000000000..79f36c0e22 --- /dev/null +++ b/js/src/doc/Debugger/Debugger-API.md @@ -0,0 +1,129 @@ +# The `Debugger` Interface + +Mozilla's JavaScript engine, SpiderMonkey, provides a debugging interface +named `Debugger` which lets JavaScript code observe and manipulate the +execution of other JavaScript code. Both Firefox's built-in developer tools +and the Firebug add-on use `Debugger` to implement their JavaScript +debuggers. However, `Debugger` is quite general, and can be used to +implement other kinds of tools like tracers, coverage analysis, +patch-and-continue, and so on. + +`Debugger` has three essential qualities: + +- It is a *source level* interface: it operates in terms of the JavaScript + language, not machine language. It operates on JavaScript objects, stack + frames, environments, and code, and presents a consistent interface + regardless of whether the debuggee is interpreted, compiled, or + optimized. If you have a strong command of the JavaScript language, you + should have all the background you need to use `Debugger` successfully, + even if you have never looked into the language's implementation. + +- It is for use *by JavaScript code*. JavaScript is both the debuggee + language and the tool implementation language, so the qualities that make + JavaScript effective on the web can be brought to bear in crafting tools + for developers. As is expected of JavaScript APIs, `Debugger` is a + *sound* interface: using (or even misusing) `Debugger` should never cause + Gecko to crash. Errors throw proper JavaScript exceptions. + +- It is an *intra-thread* debugging API. Both the debuggee and the code + using `Debugger` to observe it must run in the same thread. Cross-thread, + cross-process, and cross-device tools must use `Debugger` to observe the + debuggee from within the same thread, and then handle any needed + communication themselves. (Firefox's builtin tools have a + [protocol][protocol] defined for this purpose.) + +In Gecko, the `Debugger` API is available to chrome code only. By design, +it ought not to introduce security holes, so in principle it could be made +available to content as well; but it is hard to justify the security risks +of the additional attack surface. + +The `Debugger` API cannot currently observe self-hosted JavaScript. This is not +inherent in the API's design, but simply that the self-hosting infrastructure +isn't prepared for the kind of invasions the `Debugger` API can perform. + + +## Debugger Instances and Shadow Objects + +`Debugger` reflects every aspect of the debuggee's state as a JavaScript +value---not just actual JavaScript values like objects and primitives, +but also stack frames, environments, scripts, and compilation units, which +are not normally accessible as objects in their own right. + +Here is a JavaScript program in the process of running a timer callback function: + +![A running JavaScript program and its Debugger shadows][img-shadows] + +This diagram shows the various types of shadow objects that make up the +Debugger API (which all follow some [general conventions][conventions]): + +- A [`Debugger.Object`][object] represents a debuggee object, offering a + reflection-oriented API that protects the debugger from accidentally + invoking getters, setters, proxy traps, and so on. + +- A [`Debugger.Script`][script] represents a block of JavaScript + code---either a function body or a top-level script. Given a + `Debugger.Script`, one can set breakpoints, translate between source + positions and bytecode offsets (a deviation from the "source level" + design principle), and find other static characteristics of the code. + +- A [`Debugger.Frame`][frame] represents a running stack frame. You can use + these to walk the stack and find each frame's script and environment. You + can also set `onStep` and `onPop` handlers on frames. + +- A [`Debugger.Environment`][environment] represents an environment, + associating variable names with storage locations. Environments may + belong to a running stack frame, captured by a function closure, or + reflect some global object's properties as variables. + +The [`Debugger`][debugger-object] instance itself is not really a shadow of +anything in the debuggee; rather, it maintains the set of global objects +which are to be considered debuggees. A `Debugger` observes only execution +taking place in the scope of these global objects. You can set functions to +be called when new stack frames are pushed; when new code is loaded; and so +on. + +Omitted from this picture are [`Debugger.Source`][source] instances, which +represent JavaScript compilation units. A `Debugger.Source` can furnish a +full copy of its source code, and explain how the code entered the system, +whether via a call to `eval`, a `<script>` element, or otherwise. A +`Debugger.Script` points to the `Debugger.Source` from which it is derived. + +Also omitted is the `Debugger`'s [`Debugger.Memory`][memory] instance, which +holds methods and accessors for observing the debuggee's memory use. + +All these types follow some [general conventions][conventions], which you +should look through before drilling down into any particular type's +specification. + +All shadow objects are unique per `Debugger` and per referent. For a given +`Debugger`, there is exactly one `Debugger.Object` that refers to a +particular debuggee object; exactly one `Debugger.Frame` for a particular +stack frame; and so on. Thus, a tool can store metadata about a shadow's +referent as a property on the shadow itself, and count on finding that +metadata again if it comes across the same referent. And since shadows are +per-`Debugger`, tools can do so without worrying about interfering with +other tools that use their own `Debugger` instances. + + +## Examples + +Here are some things you can try out yourself that show off some of `Debugger`'s +features: + +- [Setting a breakpoint][tut breakpoint] in a page, running a handler function + when it is hit that evaluates an expression in the page's context. + +- [Showing how many objects different call paths allocate.][tut alloc log] + +[object]: Debugger.Object.md +[protocol]: https://wiki.mozilla.org/Remote_Debugging_Protocol +[img-shadows]: shadows.svg +[script]: Debugger.Script.md +[frame]: Debugger.Frame.md +[environment]: Debugger.Environment.md +[debugger-object]: Debugger.md +[source]: Debugger.Source.md +[memory]: Debugger.Memory.md +[conventions]: Conventions.md +[tut breakpoint]: Tutorial-Breakpoint.md +[tut alloc log]: Tutorial-Alloc-Log-Tree.md diff --git a/js/src/doc/Debugger/Debugger.Environment.md b/js/src/doc/Debugger/Debugger.Environment.md new file mode 100644 index 0000000000..eef4d35818 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Environment.md @@ -0,0 +1,182 @@ +# Debugger.Environment + +A `Debugger.Environment` instance represents a lexical environment, +associating names with variables. Each [`Debugger.Frame`][frame] instance +representing a debuggee frame has an associated environment object +describing the variables in scope in that frame; and each +[`Debugger.Object`][object] instance representing a debuggee function has an +environment object representing the environment the function has closed +over. + +ECMAScript environments form a tree, in which each local environment is +parented by its enclosing environment (in ECMAScript terms, its 'outer' +environment). We say an environment <i>binds</i> an identifier if that +environment itself associates the identifier with a variable, independently +of its outer environments. We say an identifier is <i>in scope</i> in an +environment if the identifier is bound in that environment or any enclosing +environment. + +SpiderMonkey creates `Debugger.Environment` instances as needed as the +debugger inspects stack frames and function objects; calling +`Debugger.Environment` as a function or constructor raises a `TypeError` +exception. + +SpiderMonkey creates exactly one `Debugger.Environment` instance for each +environment it presents via a given [`Debugger`][debugger-object] instance: +if the debugger encounters the same environment through two different +routes (perhaps two functions have closed over the same environment), +SpiderMonkey presents the same `Debugger.Environment` instance to the +debugger each time. This means that the debugger can use the `==` operator +to recognize when two `Debugger.Environment` instances refer to the same +environment in the debuggee, and place its own properties on a +`Debugger.Environment` instance to store metadata about particular +environments. + +(If more than one [`Debugger`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Environment` instance for a given environment. This allows the +code using each [`Debugger`][debugger-object] instance to place whatever +properties it likes on its own [`Debugger.Object`][object] instances, +without worrying about interfering with other debuggers.) + +If a `Debugger.Environment` instance's referent is not a debuggee +environment, then attempting to access its properties (other than +`inspectable`) or call any its methods throws an instance of `Error`. + +`Debugger.Environment` instances protect their referents from the +garbage collector; as long as the `Debugger.Environment` instance is +live, the referent remains live. Garbage collection has no visible +effect on `Debugger.Environment` instances. + + +## Accessor Properties of the Debugger.Environment Prototype Object + +A `Debugger.Environment` instance inherits the following accessor +properties from its prototype: + +### `inspectable` +True if this environment is a debuggee environment, and can therefore +be inspected. False otherwise. All other properties and methods of +`Debugger.Environment` instances throw if applied to a non-inspectable +environment. + +### `type` +The type of this environment object, one of the following values: + +* "declarative", indicating that the environment is a declarative + environment record. Function calls, calls to `eval`, `let` blocks, + `catch` blocks, and the like create declarative environment records. + +* "object", indicating that the environment's bindings are the + properties of an object. The global object and DOM elements appear in + the chain of environments via object environments. (Note that `with` + statements have their own environment type.) + +* "with", indicating that the environment was introduced by a `with` + statement. + +### `scopeKind` +If this is a declarative environment, a string describing the kind of scope +which this environment is associated with, or `null` for other types of +environments. There is an assortment of possible scope kinds which can be +generated, with a selection of possible values below. Unlike the type +accessor, the categorization this performs is specific to SpiderMonkey's +implementation, and not derived from distinctions made in the ECMAScript +language specification. + +* "function", indicating the top level body scope of a function for +arguments and 'var' variables. + +* "function lexical", indicating the top level lexical scope in a function. + +### `parent` +The environment that encloses this one (the "outer" environment, in +ECMAScript terminology), or `null` if this is the outermost environment. + +### `object` +A [`Debugger.Object`][object] instance referring to the object whose +properties this environment reflects. If this is a declarative +environment record, this accessor throws a `TypeError` (since +declarative environment records have no such object). Both `"object"` +and `"with"` environments have `object` properties that provide the +object whose properties they reflect as variable bindings. + +### `calleeScript` +If this environment represents the variable environment (the top-level +environment within the function, which receives `var` definitions) for +a call to a function <i>f</i>, then this property's value is a +[`Debugger.Script`][script] instance referring to <i>f</i>'s script. Otherwise, +this property's value is `null`. + +### `optimizedOut` +True if this environment is optimized out. False otherwise. For example, +functions whose locals are never aliased may present optimized-out +environments. When true, `getVariable` returns an ordinary JavaScript +object whose `optimizedOut` property is true on all bindings, and +`setVariable` throws a `ReferenceError`. + + +## Function Properties of the Debugger.Environment Prototype Object + +The methods described below may only be called with a `this` value +referring to a `Debugger.Environment` instance; they may not be used as +methods of other kinds of objects. + +### `names()` +Return an array of strings giving the names of the identifiers bound by +this environment. The result does not include the names of identifiers +bound by enclosing environments. + +### `getVariable(name)` + +Return the value of the variable bound to <i>name</i> in this +environment, or `undefined` if this environment does not bind +<i>name</i>. <i>Name</i> must be a string that is a valid ECMAScript +identifier name. The result is a debuggee value, in most cases. + +JavaScript engines often omit variables from environments, to save space +and reduce execution time. If the given variable should be in scope, but +`getVariable` is unable to produce its value, it returns an ordinary +JavaScript object (not a [`Debugger.Object`][object] instance) whose +`optimizedOut` property is `true`. + +Aside from the above case, this method can return something that is not a +debuggee value in two other cases. If a function argument is missing, then it +returns an ordinary JavaScript object whose `missingArgument` property is +`true`. Finally, if a variable name is bound in the environment but not yet +initialized (for example, if the debuggee is paused in the middle of an +initializer expression) then it returns an ordinary JavaScript object whose +`uninitialized` property is `true`. + +This is not an [invocation function][inv fr]; +if this call would cause debuggee code to run (say, because the +environment is a `"with"` environment, and <i>name</i> refers to an +accessor property of the `with` statement's operand), this call throws a +[`Debugger.DebuggeeWouldRun`][wouldrun] +exception. + +### `setVariable(name, value)` +Store <i>value</i> as the value of the variable bound to <i>name</i> in +this environment. <i>Name</i> must be a string that is a valid +ECMAScript identifier name; <i>value</i> must be a debuggee value. + +If this environment binds no variable named <i>name</i>, throw a +`ReferenceError`. + +This is not an [invocation function][inv fr]; +if this call would cause debuggee code to run, this call throws a +[`Debugger.DebuggeeWouldRun`][wouldrun] +exception. + +### `find(name)` +Return a reference to the innermost environment, starting with this +environment, that binds <i>name</i>. If <i>name</i> is not in scope in +this environment, return `null`. <i>Name</i> must be a string whose +value is a valid ECMAScript identifier name. + + +[frame]: Debugger.Frame.md +[object]: Debugger.Object.md +[debugger-object]: Debugger.md +[inv fr]: Debugger.Frame.html#invocation-functions-and-debugger-frames +[wouldrun]: Conventions.html#the-debugger-debuggeewouldrun-exception diff --git a/js/src/doc/Debugger/Debugger.Frame.md b/js/src/doc/Debugger/Debugger.Frame.md new file mode 100644 index 0000000000..32ae5027de --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Frame.md @@ -0,0 +1,489 @@ +# Debugger.Frame + +A `Debugger.Frame` instance represents a [visible stack frame][vf]. Given a +`Debugger.Frame` instance, you can find the script the frame is executing, +walk the stack to older frames, find the lexical environment in which the +execution is taking place, and so on. + +For a given [`Debugger`][debugger-object] instance, SpiderMonkey creates +only one `Debugger.Frame` instance for a given visible frame. Every handler +method called while the debuggee is running in a given frame is given the +same frame object. Similarly, walking the stack back to a previously +accessed frame yields the same frame object as before. Debugger code can +add its own properties to a frame object and expect to find them later, use +`==` to decide whether two expressions refer to the same frame, and so on. + +(If more than one [`Debugger`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Frame` instance for a given frame. This allows the code using +each [`Debugger`][debugger-object] instance to place whatever properties it +likes on its `Debugger.Frame` instances, without worrying about interfering +with other debuggers.) + +When the debuggee pops a stack frame (say, because a function call has +returned or an exception has been thrown from it), the `Debugger.Frame` +instance referring to that frame becomes inactive: its `onStack` property +becomes `false`, and accessing its many properties or calling its methods +throws an exception. Note that frames only become inactive at times that +are predictable for the debugger: when the debuggee runs, or when the +debugger removes frames from the stack itself. + + +## Visible Frames + +When inspecting the call stack, [`Debugger`][debugger-object] does not +reveal all the frames that are actually present on the stack: while it does +reveal all frames running debuggee code, it omits frames running the +debugger's own code, and omits most frames running non-debuggee code. We +call those stack frames a [`Debugger`][debugger-object] does reveal +<i>visible frames</i>. + +A frame is a visible frame if any of the following are true: + +* it is running [debuggee code][dbg code]; + +* its immediate caller is a frame running debuggee code; or + +* it is a [`"debugger"` frame][inv fr], + representing the continuation of debuggee code invoked by the debugger. + +The "immediate caller" rule means that, when debuggee code calls a +non-debuggee function, it looks like a call to a primitive: you see a frame +for the non-debuggee function that was accessible to the debuggee, but any +further calls that function makes are treated as internal details, and +omitted from the stack trace. If the non-debuggee function eventually calls +back into debuggee code, then those frames are visible. + +(Note that the debuggee is not considered an "immediate caller" of handler +methods it triggers. Even though the debuggee and debugger share the same +JavaScript stack, frames pushed for SpiderMonkey's calls to handler methods +to report events in the debuggee are never considered visible frames.) + + +## Invocation Functions and "debugger" Frames + +An <i>invocation function</i> is any function in this interface that allows +the debugger to invoke code in the debuggee: +`Debugger.Object.prototype.call`, `Debugger.Frame.prototype.eval`, and so +on. + +While invocation functions differ in the code to be run and how to pass +values to it, they all follow this general procedure: + +1. Let <i>older</i> be the youngest visible frame on the stack, or `null` + if there is no such frame. (This is never one of the the debugger's own + frames; those never appear as `Debugger.Frame` instances.) + +2. Push a `"debugger"` frame on the stack, with <i>older</i> as its + `older` property. + +3. Invoke the debuggee code as appropriate for the given invocation + function, with the `"debugger"` frame as its continuation. For example, + `Debugger.Frame.prototype.eval` pushes an `"eval"` frame for code it + runs, whereas `Debugger.Object.prototype.call` pushes a `"call"` frame. + +4. When the debuggee code completes, whether by returning, throwing an + exception or being terminated, pop the `"debugger"` frame, and return an + appropriate [completion value][cv] from the invocation function to the + debugger. + +When a debugger calls an invocation function to run debuggee code, that +code's continuation is the debugger, not the next debuggee code frame. +Pushing a `"debugger"` frame makes this continuation explicit, and makes it +easier to find the extent of the stack created for the invocation. + + +## Suspended Frames + +Some frames can be *suspended*. + +When a generator `yield`s a value, or when an async function `await`s a +value, the current frame is suspended and removed from the stack, and +other JS code has a chance to run. Later (if the `await`ed promise +becomes resolved, for example), SpiderMonkey will *resume* the frame. It +will be put back onto the stack, and execution will continue where it +left off. Only generator and async function call frames can be suspended +and resumed. + +Currently, a frame's `onStack` property is `false` while it's suspended +([bug 1448880](https://bugzilla.mozilla.org/show_bug.cgi?id=1448880)). + +SpiderMonkey uses the same `Debugger.Frame` object each time a generator +or async function call is put back onto the stack. This means that the +`onStep` handler can be used to step over `yield` and `await`. + +The `frame.onPop` handler is called each time a frame is suspended, and +the `Debugger.onEnterFrame` handler is called each time a frame is +resumed. (This means these events can fire multiple times for the same +`Frame` object, which is odd, but accurately conveys what's happening.) + +The [completion value][cv] passed to the `frame.onPop` handler for a suspension +contains additional properties to clarify what's going on. See the documentation +for completion values for details. + + +## Stepping Into Generators: The "Initial Yield" + +When a debuggee generator is called, something weird happens. The +`.onEnterFrame` hook fires, as though we're stepping into the generator. +But the code inside the generator doesn't run. Instead it immediately +returns. Then we sometimes get *another* `.onEnterFrame` event for the +same generator. What's going on? + +To explain this, we first have to describe how generator calls work, +according to the ECMAScript language specification. Note that except for +step 3, it's exactly like a regular function call. + +1. An "execution context" (what we call a `Frame`) is pushed to the stack. +2. An environment is created (for arguments and local variables). + Argument-default-value-expressions, if any, are evaluated. +3. A generator object is created, initially suspended at the start of the generator body. +4. The stack frame is popped, and the generator object is returned to the caller. + +The JavaScript engine actually carries out these steps, in this order. +So when a debuggee generator is called, here's what you'll observe: + +1. The `debugger.onEnterFrame` hook fires. +2. The debugger can step through the argument-default-value code, if any. +3. The body of the generator does not run yet. Instead, a generator object + is created and suspended (which does not fire any debugger events). +4. The `frame.onPop` hook fires, with a completion value of + `{return:` *(the new generator object)* `}`. + +In SpiderMonkey, this process of suspending and returning a new +generator object is called the "initial yield". + +If the caller then uses the generator's `.next()` method, which may or +may not happen right away depending on the debuggee code, the suspended +generator will be resumed, firing `.onEnterFrame` again. + +## Accessor Properties of the Debugger.Frame Prototype Object + +A `Debugger.Frame` instance inherits the following accessor properties from +its prototype: + +### `type` + +A string describing what sort of frame this is: + +* `"call"`: a frame running a function call. (We may not be able to obtain + frames for calls to host functions.) + +* `"eval"`: a frame running code passed to `eval`. + +* `"global"`: a frame running global code (JavaScript that is neither of + the above). + +* `"module"`: a frame running code at the top level of a module. + +* `"wasmcall"`: a frame running a WebAssembly function call. + +* `"debugger"`: a frame for a call to user code invoked by the debugger + (see the `eval` method below). + +Accessing this property will throw if `.terminated == true`. + +### `implementation` +A string describing which tier of the JavaScript engine this frame is +executing in: + +* `"interpreter"`: a frame running in the interpreter. + +* `"baseline"`: a frame running in the unoptimizing, baseline JIT. + +* `"ion"`: a frame running in the optimizing JIT. + +* `"wasm"`: a frame running in WebAssembly baseline JIT. + +Accessing this property will throw if `.onStack == false`. + +### `this` +The value of `this` for this frame (a debuggee value). For a `wasmcall` +frame, this property throws a `TypeError`. + +Accessing this property will throw if `.terminated == true`. + +### `older` +The `Debugger.Frame` for the next-older visible frame, in which control will +resume when this frame completes. If there is no older frame, this is `null`. +If there an explicitly inserted asynchronous stack trace above this frame, +this is `null`, since the explicit saved frame takes priority. +If this frame is a suspended generator or async call, this will also be `null`. + +Accessing this property will throw if `.terminated == true`. + +### `olderSavedFrame` + +If this frame has no `older` frame, this field may hold a [`SavedFrame`][saved-frame] +object representing the saved asynchronous stack that triggered execution of +this `Debugger.Frame` instance. + +Accessing this property will throw if `.terminated == true`. + +### `onStack` +True if the frame this `Debugger.Frame` instance refers to is still on +the stack; false if it has completed execution or been popped in some other way. +Note that this property may be accessed regardless of what state the frame is +in, so it can be used to verify whether it is safe to access other properties +that require an on-stack frame. + +### `terminated` +True if the frame this `Debugger.Frame` instance refers to will never run +again; false if it is on-stack or is a suspended generator/async call that +may be resumed later. Note that this property may be accessed regardless of +what state the frame is in, so it can be used to verify whether it is safe to +access other properties that require a non-terminated frame. + +### `script` +The script being executed in this frame (a [`Debugger.Script`][script] +instance), or `null` on frames that do not represent calls to debuggee +code. On frames whose `callee` property is not null, this is equal to +`callee.script`. + +Accessing this property will throw if `.terminated == true`. + +### `offset` +The offset of the bytecode instruction currently being executed in +`script`, or `undefined` if the frame's `script` property is `null`. +For a `wasmcall` frame, this property throws a `TypeError`. + +If this is used on a suspended function frame, the offset will reference +the offset where the frame will be resumed. + +Accessing this property will throw if `.terminated == true`. + +### `environment` +The lexical environment within which evaluation is taking place (a +[`Debugger.Environment`][environment] instance), or `null` on frames +that do not represent the evaluation of debuggee code, like calls +non-debuggee functions, host functions or `"debugger"` frames. + +Accessing this property will throw if `.terminated == true`. + +### `callee` +The function whose application created this frame, as a debuggee value, +or `null` if this is not a `"call"` frame. + +Accessing this property will throw if `.terminated == true`. + +### `constructing` +True if this frame is for a function called as a constructor, false +otherwise. + +Accessing this property will throw if `.terminated == true`. + +### `arguments` +The arguments passed to the current frame, or `null` if this is not a +`"call"` frame. When non-`null`, this is an object, allocated in the +same global as the debugger, with `Array.prototype` on its prototype +chain, a non-writable `length` property, and properties whose names are +array indices. Each property is a read-only accessor property whose +getter returns the current value of the corresponding parameter. When +the referent frame is popped, the argument value's properties' getters +throw an error. + +Accessing this property will throw if `.onStack == false`. + +### `asyncPromise` + +If the frame is not an async (generator) function, this will be `undefined`. + +For async functions, this will be a [`Debugger.Object`][object] whose referent +is the promise for async function call's return value. Note that this +property will be `null` if the value is accessed during `onEnterFrame`, +since the promise doesn't exist yet at that point. + +For async generator functions, this will be a [`Debugger.Object`][object] +whose referent is the promise for the current iteration's "value"+"done" object, +which will be resolved when the generator next throws/yields/returns. +Note that this will be `null` if the value is accessed during the initial +generator `onEnterFrame`/`onPop` (before the first `.next` call), +since there is no promise yet at that point. + +Accessing this property will throw if `.terminated == true`. + + +## Handler Methods of Debugger.Frame Instances + +Each `Debugger.Frame` instance inherits accessor properties holding handler +functions for SpiderMonkey to call when given events occur in the frame. + +Calls to frames' handler methods are cross-compartment, intra-thread calls: +the call takes place in the thread to which the frame belongs, and runs in +the compartment to which the handler method belongs. + +`Debugger.Frame` instances inherit the following handler method properties: + +### `onStep` +This property must be either `undefined` or a function. If it is a +function, SpiderMonkey calls it when execution in this frame makes a +small amount of progress, passing no arguments and providing this +`Debugger.Frame` instance as the `this`value. The function should +return a [resumption value][rv] specifying how the debuggee's execution +should proceed. + +What constitutes "a small amount of progress" varies depending on the +implementation, but it is fine-grained enough to implement useful +"step" and "next" behavior. + +If multiple [`Debugger`][debugger-object] instances each have +`Debugger.Frame` instances for a given stack frame with `onStep` +handlers set, their handlers are run in an unspecified order. If any +`onStep` handler forces the frame to return early (by returning a +resumption value other than `undefined`), any remaining debuggers' +`onStep` handlers do not run. + +This property is ignored on frames that are not executing debuggee +code, like `"call"` frames for calls to host functions and `"debugger"` +frames. + +Accessing and reassigning this property is allowed independent of whether +or not the frame is currently on-stack/suspended/terminated. + +### `onPop` +This property must be either `undefined` or a function. If it is a function, +SpiderMonkey calls it just before this frame is popped or suspended, passing +a [completion value][cv] indicating the reason, and providing this +`Debugger.Frame` instance as the `this` value. The function should return a +[resumption value][rv] indicating how execution should proceed. On newly +created frames, this property's value is `undefined`. + +When this handler is called, this frame's current execution location, as +reflected in its `offset` and `environment` properties, is the operation +which caused it to be unwound. In frames returning or throwing an exception, +the location is often a return or a throw statement. In frames propagating +exceptions, the location is a call. In generator or async function frames, +the location may be a `yield` or `await` expression. + +When an `onPop` call reports the completion of a construction call +(that is, a function called via the `new` operator), the completion +value passed to the handler describes the value returned by the +function body. If this value is not an object, it may be different from +the value produced by the `new` expression, which will be the value of +the frame's `this` property. (In ECMAScript terms, the `onPop` handler +receives the value returned by the `[[Call]]` method, not the value +returned by the `[[Construct]]` method.) + +When a debugger handler function forces a frame to complete early, by +returning a `{ return:... }`, `{ throw:... }`, or `null` resumption +value, SpiderMonkey calls the frame's `onPop` handler, if any. The +completion value passed in this case reflects the resumption value that +caused the frame to complete. + +When SpiderMonkey calls an `onPop` handler for a frame that is throwing +an exception or being terminated, and the handler returns `undefined`, +then SpiderMonkey proceeds with the exception or termination. That is, +an `undefined` resumption value leaves the frame's throwing and +termination process undisturbed. + +If multiple [`Debugger`][debugger-object] instances each have +`Debugger.Frame` instances for a given stack frame with `onPop` +handlers set, their handlers are run in an unspecified order. The +resumption value each handler returns establishes the completion value +reported to the next handler. + +The `onPop` handler is typically called only once for a given +`Debugger.Frame`, after which the frame becomes inactive. However, in the +case of [generators and async functions](#suspended-frames), `onPop` fires +each time the frame is suspended. + +This handler is not called on `"debugger"` frames. It is also not called +when unwinding a frame due to an over-recursion or out-of-memory +exception. + +Accessing and reassigning this property is allowed independent of whether +or not the frame is currently on-stack/suspended/terminated. + + +## Function Properties of the Debugger.Frame Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger.Frame` instance; they may not be used as +methods of other kinds of objects. + +### `eval(code, [options])` +Evaluate <i>code</i> in the execution context of this frame, and return +a [completion value][cv] describing how it completed. <i>Code</i> is a +string. If this frame's `environment` property is `null` or `type` property +is `wasmcall`, throw a `TypeError`. All extant handler methods, breakpoints, +and so on remain active during the call. This function follows the +[invocation function conventions][inv fr]. + +<i>Code</i> is interpreted as strict mode code when it contains a Use +Strict Directive, or the code executing in this frame is strict mode +code. + +If <i>code</i> is not strict mode code, then variable declarations in +<i>code</i> affect the environment of this frame. (In the terms used by +the ECMAScript specification, the `VariableEnvironment` of the +execution context for the eval code is the `VariableEnvironment` of the +execution context that this frame represents.) If implementation +restrictions prevent SpiderMonkey from extending this frame's +environment as requested, this call throws an Error exception. + +If given, <i>options</i> should be an object whose properties specify +details of how the evaluation should occur. The `eval` method +recognizes the following properties: + +* `url` + + The filename or URL to which we should attribute <i>code</i>. If this + property is omitted, the URL defaults to `"debugger eval code"`. + +* `lineNumber` + + The line number at which the evaluated code should be claimed to begin + within <i>url</i>. + +Accessing this property will throw if `.onStack == false`. + +### `evalWithBindings(code, bindings, [options])` +Like `eval`, but evaluate <i>code</i> in the environment of this frame, +extended with bindings from the object <i>bindings</i>. For each own +enumerable property of <i>bindings</i> named <i>name</i> whose value is +<i>value</i>, include a variable in the environment in which +<i>code</i> is evaluated named <i>name</i>, whose value is +<i>value</i>. Each <i>value</i> must be a debuggee value. (This is not +like a `with` statement: <i>code</i> may access, assign to, and delete +the introduced bindings without having any effect on the +<i>bindings</i> object.) + +This method allows debugger code to introduce temporary bindings that +are visible to the given debuggee code and which refer to debugger-held +debuggee values, and do so without mutating any existing debuggee +environment. + +Note that, like `eval`, declarations in the <i>code</i> passed to +`evalWithBindings` affect the environment of this frame, even as that +environment is extended by bindings visible within <i>code</i>. (In the +terms used by the ECMAScript specification, the `VariableEnvironment` +of the execution context for the eval code is the `VariableEnvironment` +of the execution context that this frame represents, and the +<i>bindings</i> appear in a new declarative environment, which is the +eval code's `LexicalEnvironment`.) If implementation restrictions +prevent SpiderMonkey from extending this frame's environment as +requested, this call throws an `Error` exception. + +The <i>options</i> argument is as for +[`Debugger.Frame.prototype.eval`][fr eval], described above. +Also like `eval`, if this frame's `environment` property is `null` or +`type` property is `wasmcall`, throw a `TypeError`. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +Accessing this property will throw if `.onStack == false`. + + +[vf]: #visible-frames +[debugger-object]: Debugger.md +[object]: Debugger.Object.md +[dbg code]: Conventions.md#debuggee-code +[inv fr]: #invocation-functions-and-debugger-frames +[cv]: Conventions.md#completion-values +[script]: Debugger.Script.md +[environment]: Debugger.Environment.md +[rv]: Conventions.md#resumption-values +[fr eval]: #eval-code-options +[saved-frame]: ../SavedFrame/index diff --git a/js/src/doc/Debugger/Debugger.Memory.md b/js/src/doc/Debugger/Debugger.Memory.md new file mode 100644 index 0000000000..c20354ef3c --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Memory.md @@ -0,0 +1,575 @@ +Debugger.Memory +=============== + +The [`Debugger API`][debugger] can help tools observe the debuggee's memory use +in various ways: + +- It can mark each new object with the JavaScript call stack at which it was + allocated. + +- It can log all object allocations, yielding a stream of JavaScript call stacks + at which allocations have occurred. + +- It can compute a *census* of items belonging to the debuggee, categorizing + items in various ways, and yielding item counts. + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, then the methods and +accessor properties of `dbg.memory` control how <i>dbg</i> +observes its debuggees' memory use. The `dbg.memory` object is +an instance of `Debugger.Memory`; its inherited accessors and methods are +described below. + + +## Allocation Site Tracking + +The JavaScript engine marks each new object with the call stack at which it was +allocated, if: + +- the object is allocated in the scope of a global object that is a debuggee of + some [`Debugger`][debugger-object] instance <i>dbg</i>; and + +- <code><i>dbg</i>.memory.[trackingAllocationSites][tracking-allocs]</code> is + set to `true`. + +- A [Bernoulli trial][bernoulli-trial] succeeds, with probability equal to the + maximum of + [`d.memory.allocationSamplingProbability`][alloc-sampling-probability] of all + `Debugger` instances `d` that are observing the global that this object is + allocated within the scope of. + +Given a [`Debugger.Object`][object] instance <i>dobj</i> referring to some +object, <code><i>dobj</i>.[allocationSite][allocation-site]</code> returns a +[saved call stack][saved-frame] indicating where <i>dobj</i>'s referent was +allocated. + + +## Allocation Logging + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, and +<code><i>dbg</i>.memory.[trackingAllocationSites][tracking-allocs]</code> is set +to `true`, then the JavaScript engine logs each object allocated by <i>dbg</i>'s +debuggee code. You can retrieve the current log by calling +<code><i>dbg</i>.memory.[drainAllocationsLog][drain-alloc-log]</code>. You can +control the limit on the log's size by setting +<code><i>dbg</i>.memory.[maxAllocationsLogLength][max-alloc-log]</code>. + + +## Censuses + +A *census* is a complete traversal of the graph of all reachable memory items +belonging to a particular `Debugger`'s debuggees. It produces a count of those +items, broken down by various criteria. If <i>dbg</i> is a +[`Debugger`][debugger-object] instance, you can call +<code><i>dbg</i>.memory.[takeCensus][take-census]</code> to conduct a census of +its debuggees' possessions. + + +Accessor Properties of the `Debugger.Memory.prototype` Object +------------------------------------------------------------- + +If <i>dbg</i> is a [`Debugger`][debugger-object] instance, then +`<i>dbg</i>.memory` is a `Debugger.Memory` instance, which inherits the +following accessor properties from its prototype: + +## `trackingAllocationSites` +A boolean value indicating whether this `Debugger.Memory` instance is +capturing the JavaScript execution stack when each Object is allocated. This +accessor property has both a getter and setter: assigning to it enables or +disables the allocation site tracking. Reading the accessor produces `true` +if the Debugger is capturing stacks for Object allocations, and `false` +otherwise. Allocation site tracking is initially disabled in a new Debugger. + +Assignment is fallible: if the Debugger cannot track allocation sites, it +throws an `Error` instance. + +You can retrieve the allocation site for a given object with the +[`Debugger.Object.prototype.allocationSite`][allocation-site] accessor +property. + +## `allocationSamplingProbability` +A number between 0 and 1 that indicates the probability with which each new +allocation should be entered into the allocations log. 0 is equivalent to +"never", 1 is "always", and .05 would be "one out of twenty". + +The default is 1, or logging every allocation. + +Note that in the presence of multiple <code>Debugger</code> instances +observing the same allocations within a global's scope, the maximum +<code>allocationSamplingProbability</code> of all the +<code>Debugger</code>s is used. + +## `maxAllocationsLogLength` +The maximum number of allocation sites to accumulate in the allocations log +at a time. This accessor can be both fetched and stored to. Its default +value is `5000`. + +## `allocationsLogOverflowed` +Returns `true` if there have been more than +[`maxAllocationsLogLength`][#max-alloc-log] allocations since the last time +[`drainAllocationsLog`][#drain-alloc-log] was called and some data has been +lost. Returns `false` otherwise. + +Debugger.Memory Handler Functions +--------------------------------- + +Similar to [`Debugger`'s handler functions][debugger], `Debugger.Memory` +inherits accessor properties that store handler functions for SpiderMonkey to +call when given events occur in debuggee code. + +Unlike `Debugger`'s hooks, `Debugger.Memory`'s handlers' return values are not +significant, and are ignored. The handler functions receive the +`Debugger.Memory`'s owning `Debugger` instance as their `this` value. The owning +`Debugger`'s `uncaughtExceptionHandler` is still fired for errors thrown in +`Debugger.Memory` hooks. + +On a new `Debugger.Memory` instance, each of these properties is initially +`undefined`. Any value assigned to a debugging handler must be either a function +or `undefined`; otherwise a `TypeError` is thrown. + +Handler functions run in the same thread in which the event occurred. +They run in the compartment to which they belong, not in a debuggee +compartment. + +## `onGarbageCollection(statistics)` +A garbage collection cycle spanning one or more debuggees has just been +completed. + +The *statistics* parameter is an object containing information about the GC +cycle. It has the following properties: + +## `collections` +The `collections` property's value is an array. Because SpiderMonkey's +collector is incremental, a full collection cycle may consist of +multiple discrete collection slices with the JS mutator running +interleaved. For each collection slice that occurred, there is an entry +in the `collections` array with the following form: + +``` +{ + "startTimestamp": timestamp, + "endTimestamp": timestamp, +} +``` + +Here the `timestamp` values are [timestamps][timestamps] of the GC slice's start +and end events. + +## `reason` +A very short string describing the reason why the collection was +triggered. Known values include the following: + +* `"API"` +* `"EAGER_ALLOC_TRIGGER"` +* `"DESTROY_RUNTIME"` +* `"LAST_DITCH"` +* `"TOO_MUCH_MALLOC"` +* `"ALLOC_TRIGGER"` +* `"DEBUG_GC"` +* `"COMPARTMENT_REVIVED"` +* `"RESET"` +* `"OUT_OF_NURSERY"` +* `"EVICT_NURSERY"` +* `"FULL_STORE_BUFFER"` +* `"SHARED_MEMORY_LIMIT"` +* `"PERIODIC_FULL_GC"` +* `"INCREMENTAL_TOO_SLOW"` +* `"DOM_WINDOW_UTILS"` +* `"COMPONENT_UTILS"` +* `"MEM_PRESSURE"` +* `"CC_FINISHED"` +* `"CC_FORCED"` +* `"LOAD_END"` +* `"PAGE_HIDE"` +* `"NSJSCONTEXT_DESTROY"` +* `"SET_NEW_DOCUMENT"` +* `"SET_DOC_SHELL"` +* `"DOM_UTILS"` +* `"DOM_IPC"` +* `"DOM_WORKER"` +* `"INTER_SLICE_GC"` +* `"REFRESH_FRAME"` +* `"FULL_GC_TIMER"` +* `"SHUTDOWN_CC"` +* `"USER_INACTIVE"` + +## `nonincrementalReason` +If SpiderMonkey's collector determined it could not incrementally +collect garbage, and had to do a full GC all at once, this is a short +string describing the reason it determined the full GC was necessary. +Otherwise, `null` is returned. Known values include the following: + +* `"GC mode"` +* `"malloc bytes trigger"` +* `"allocation trigger"` +* `"requested"` + +## `gcCycleNumber` +The GC cycle's "number". Does not correspond to the number +of GC cycles that have run, but is guaranteed to be monotonically +increasing. + +Function Properties of the `Debugger.Memory.prototype` Object +------------------------------------------------------------- + +## `drainAllocationsLog()` +When `trackingAllocationSites` is `true`, this method returns an array of +recent `Object` allocations within the set of debuggees. *Recent* is +defined as the `maxAllocationsLogLength` most recent `Object` allocations +since the last call to `drainAllocationsLog`. Therefore, calling this +method effectively clears the log. + +Objects in the array are of the form: + +``` +{ + "timestamp": timestamp, + "frame": allocationSite, + "class": className, + "size": byteSize, + "inNursery": inNursery, +} +``` + +Where + +* `timestamp` is the [timestamp][timestamps] of the allocation event. + +* `allocationSite` is an allocation site (as a + [captured stack][saved-frame]). Note that this property can be null if the + object was allocated with no JavaScript frames on the stack. + +* `className` is the string name of the allocated object's internal +`[[Class]]` property, for example "Array", "Date", "RegExp", or (most +commonly) "Object". + +* `byteSize` is the size of the object in bytes. + +* `inNursery` is true if the allocation happened inside the nursery. False + if the allocation skipped the nursery and started in the tenured heap. + +When `trackingAllocationSites` is `false`, `drainAllocationsLog()` throws an +`Error`. + +## `takeCensus(options)` +Carry out a census of the debuggee compartments' contents. A *census* is a +complete traversal of the graph of all reachable memory items belonging to a +particular `Debugger`'s debuggees. The census produces a count of those +items, broken down by various criteria. + +The <i>options</i> argument is an object whose properties specify how the +census should be carried out. + +If <i>options</i> has a `breakdown` property, that determines how the census +categorizes the items it finds, and what data it collects about them. For +example, if `dbg` is a `Debugger` instance, the following performs a simple +count of debuggee items: + + dbg.memory.takeCensus({ breakdown: { by: 'count' } }) + +That might produce a result like: + + { "count": 1616, "bytes": 93240 } + +Here is a breakdown that groups JavaScript objects by their class name, +non-string, non-script items by their C++ type name, and DOM nodes with +their node name: + + { + by: "coarseType", + objects: { by: "objectClass" }, + other: { by: "internalType" }, + domNode: { by: "descriptiveType" } + } + +which produces a result like this: + + { + "objects": { + "Function": { "count": 404, "bytes": 37328 }, + "Object": { "count": 11, "bytes": 1264 }, + "Debugger": { "count": 1, "bytes": 416 }, + "ScriptSource": { "count": 1, "bytes": 64 }, + // ... omitted for brevity... + }, + "scripts": { "count": 1, "bytes": 0 }, + "strings": { "count": 701, "bytes": 49080 }, + "other": { + "js::Shape": { "count": 450, "bytes": 0 }, + "js::BaseShape": { "count": 21, "bytes": 0 }, + "js::ObjectGroup": { "count": 17, "bytes": 0 } + }, + "domNode": { + "#text": { "count": 1, "bytes": 12 } + } + } + +In general, a `breakdown` value has one of the following forms: + +* <code>{ by: "count", count:<i>count<i>, bytes:<i>bytes</i> }</code> + + The trivial categorization: none whatsoever. Simply tally up the items + visited. If <i>count</i> is true, count the number of items visited; if + <i>bytes</i> is true, total the number of bytes the items use directly. + Both <i>count</i> and <i>bytes</i> are optional; if omitted, they + default to `true`. In the result of the census, this breakdown produces + a value of the form: + + { "count": n, "bytes": b } + + where the `count` and `bytes` properties are present as directed by the + <i>count</i> and <i>bytes</i> properties on the breakdown. + + Note that the census can produce byte sizes only for the most common + types. When the census cannot find the byte size for a given type, it + returns zero. + +* <code>{ by: "bucket" }</code> + + Do not do any filtering or categorizing. Instead, accumulate a bucket of + each node's ID for every node that matches. The resulting report is an + array of the IDs. + + For example, to find the ID of all nodes whose internal object + `[[class]]` property is named "RegExp", you could use the following code: + + const report = dbg.memory.takeCensus({ + breakdown: { + by: "objectClass", + then: { by: "bucket" } + } + }); + doStuffWithRegExpIDs(report.RegExp); + +* <code>{ by: "allocationStack", then:<i>breakdown</i>, noStack:<i>noStackBreakdown</i> }</code> + + Group items by the full JavaScript stack trace at which they were + allocated. + + Further categorize all the items allocated at each distinct stack using + <i>breakdown</i>. + + In the result of the census, this breakdown produces a JavaScript `Map` + value whose keys are `SavedFrame` values, and whose values are whatever + sort of result <i>breakdown</i> produces. Objects allocated on an empty + JavaScript stack appear under the key `null`. + + SpiderMonkey only tracks allocation sites for items if requested via the + [`trackingAllocationSites`][tracking-allocs] flag; even then, it does + not record allocation sites for every kind of item that appears in the + heap. Items that lack allocation site information are counted using + <i>noStackBreakdown</i>. These appear in the result `Map` under the key + string `"noStack"`. + +* <code>{ by: "objectClass", then:<i>breakdown</i>, other:<i>otherBreakdown</i> }</code> + + Group JavaScript objects by their ECMAScript `[[Class]]` internal property values. + + Further categorize JavaScript objects in each class using + <i>breakdown</i>. Further categorize items that are not JavaScript + objects using <i>otherBreakdown</i>. + + In the result of the census, this breakdown produces a JavaScript object + with no prototype whose own property names are strings naming classes, + and whose values are whatever sort of result <i>breakdown</i> produces. + The results for non-object items appear as the value of the property + named `"other"`. + +* <code>{ by: "coarseType", objects:<i>objects</i>, scripts:<i>scripts</i>, strings:<i>strings</i>, domNode:<i>domNode</i>, other:<i>other</i> }</code> + + Group items by their coarse type. + + Use the breakdown value <i>objects</i> for items that are JavaScript + objects. + + Use the breakdown value <i>scripts</i> for items that are + representations of JavaScript code. This includes bytecode, compiled + machine code, and saved source code. + + Use the breakdown value <i>strings</i> for JavaScript strings. + + Use the breakdown value <i>domNode</i> for DOM nodes. + + Use the breakdown value <i>other</i> for items that don't fit into any of + the above categories. + + In the result of the census, this breakdown produces a JavaScript object + of the form: + + ``` + { + "objects": result, + "scripts": result, + "strings": result, + "domNode:" result, + "other": result, + } + ``` + + where each <i>result</i> is a value of whatever sort the corresponding + breakdown value produces. All breakdown values are optional, and default + to `{ type: "count" }`. + +* `{ by: "internalType", then: breakdown }` + + Group items by the names given their types internally by SpiderMonkey. + These names are not meaningful to web developers, but this type of + breakdown does serve as a catch-all that can be useful to Firefox tool + developers. + + For example, a census of a pristine debuggee global broken down by + internal type name typically looks like this: + + { + "JSString": { "count": 701, "bytes": 49080 }, + "js::Shape": { "count": 450, "bytes": 0 }, + "JSObject": { "count": 426, "bytes": 44160 }, + "js::BaseShape": { "count": 21, "bytes": 0 }, + "js::ObjectGroup": { "count": 17, "bytes": 0 }, + "JSScript": { "count": 1, "bytes": 0 } + } + + In the result of the census, this breakdown produces a JavaScript object + with no prototype whose own property names are strings naming types, + and whose values are whatever sort of result <i>breakdown</i> produces. + +* <code>[ <i>breakdown</i>, ... ]</code> + + Group each item using all the given breakdown values. In the result of + the census, this breakdown produces an array of values of the sort + produced by each listed breakdown. + +To simplify breakdown values, all `then` and `other` properties are optional. +If omitted, they are treated as if they were `{ type: "count" }`. + +If the `options` argument has no `breakdown` property, `takeCensus` defaults +to the following: + +```js +{ + by: "coarseType", + objects: { by: "objectClass" }, + domNode: { by: "descriptiveType" }, + other: { by: "internalType" } +} +``` + +which produces results of the form: + +``` +{ + objects: { class: count, ... }, + scripts: count, + strings: count, + domNode: { node name:count, ... }, + other: { type name:count, ... } +} +``` + +where each `count` has the form: + +```js +{ "count": count, bytes: bytes } +``` + +Because performing a census requires traversing the entire graph of objects +in debuggee compartments, it is an expensive operation. On developer +hardware in 2014, traversing a memory graph containing roughly 130,000 nodes +and 410,000 edges took roughly 100ms. The traversal itself temporarily +allocates one hash table entry per node (roughly two address-sized words) in +addition to the per-category counts, whose size depends on the number of +categories. + + +Memory Use Analysis Exposes Implementation Details +-------------------------------------------------- + +Memory analysis may yield surprising results, because browser implementation +details that are transparent to content JavaScript often have visible effects on +memory consumption. Web developers need to know their pages' actual memory +consumption on real browsers, so it is correct for the tool to expose these +behaviors, as long as it is done in a way that helps developers make decisions +about their own code. + +This section covers some areas where Firefox's actual behavior deviates from +what one might expect from the specified behavior of the web platform. + + +## Objects + +SpiderMonkey objects usually use less memory than the naïve "table of properties +with attributes" model would suggest. For example, it is typical for many +objects to have identical sets of properties, with only the properties' values +varying from one object to the next. To take advantage of this regularity, +SpiderMonkey objects with identical sets of properties may share their property +metadata; only property values are stored directly in the object. + +Array objects may also be optimized, if the set of live indices is dense. + + +## Strings + +SpiderMonkey has three representations of strings: + +- Normal: the string's text is counted in its size. + +- Substring: the string is a substring of some other string, and points to that + string for its storage. This representation may result in a small string + retaining a very large string. However, the memory consumed by the string + itself is a small constant independent of its size, since it is simply a + reference to the larger string, a start position, and a length. + +- Concatenations: When asked to concatenate two strings, SpiderMonkey may elect + to delay copying the strings' data, and represent the result simply as a + pointer to the two original strings. Again, such a string retains other + strings, but the memory consumed by the string itself is a small constant + independent of its size, since it is simply a pair of pointers. + +SpiderMonkey converts strings from the more complex representations to the +simpler ones when it pleases. Such conversions usually increase memory +consumption. + +SpiderMonkey shares some strings amongst all web pages and browser JS. These +shared strings, called *atoms*, are not included in censuses' string counts. + + +## Scripts + +SpiderMonkey has a complex, hybrid representation of JavaScript code. There +are four representations kept in memory: + +- _Source code_. SpiderMonkey retains a copy of most JavaScript source code. + +- _Compressed source code_. SpiderMonkey compresses JavaScript source code, + and de-compresses it on demand. Heuristics determine how long to retain the + uncompressed code. + +- _Bytecode_. This is SpiderMonkey's parsed representation of JavaScript. + Bytecode can be interpreted directly, or used as input to a just-in-time + compiler. Source is parsed into bytecode on demand; functions that are never + called are never parsed. + +- _Machine code_. SpiderMonkey includes several just-in-time compilers, each of + which translates JavaScript source or bytecode to machine code. Heuristics + determine which code to compile, and which compiler to use. Machine code may + be dropped in response to memory pressure, and regenerated as needed. + +Furthermore, SpiderMonkey's just-in-time compilers generate inline caches for +type specialization. This information is dropped periodically to reduce memory +usage. + +In a census, all the various forms of JavaScript code are placed in the +`"scripts"` category. + + +[debugger]: Debugger-API.md +[debugger-object]: Debugger.md +[tracking-allocs]: #trackingallocationsites +[bernoulli-trial]: https://en.wikipedia.org/wiki/Bernoulli_trial +[alloc-sampling-probability]: #allocsamplingprobability +[object]: Debugger.Object.md +[allocation-site]: Debugger.Object.md#allocationsite +[saved-frame]: ../SavedFrame/index +[drain-alloc-log]: #drainAllocationsLog +[max-alloc-log]: #maxAllocationsLogLength +[take-census]: #takecensus-options +[timestamps]: ./Conventions.md#timestamps diff --git a/js/src/doc/Debugger/Debugger.Object.md b/js/src/doc/Debugger/Debugger.Object.md new file mode 100644 index 0000000000..c6f8b90a49 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Object.md @@ -0,0 +1,684 @@ +# Debugger.Object + +A `Debugger.Object` instance represents an object in the debuggee, +providing reflection-oriented methods to inspect and modify its referent. +The referent's properties do not appear directly as properties of the +`Debugger.Object` instance; the debugger can access them only through +methods like `Debugger.Object.prototype.getOwnPropertyDescriptor` and +`Debugger.Object.prototype.defineProperty`, ensuring that the debugger will +not inadvertently invoke the referent's getters and setters. + +SpiderMonkey creates exactly one `Debugger.Object` instance for each +debuggee object it presents to a given [`Debugger`][debugger-object] +instance: if the debugger encounters the same object through two different +routes (perhaps two functions are called on the same object), SpiderMonkey +presents the same `Debugger.Object` instance to the debugger each time. +This means that the debugger can use the `==` operator to recognize when +two `Debugger.Object` instances refer to the same debuggee object, and +place its own properties on a `Debugger.Object` instance to store metadata +about particular debuggee objects. + +JavaScript code in different compartments can have different views of the +same object. For example, in Firefox, code in privileged compartments sees +content DOM element objects without redefinitions or extensions made to +that object's properties by content code. (In Firefox terminology, +privileged code sees the element through an "xray wrapper".) To ensure that +debugger code sees each object just as the debuggee would, each +`Debugger.Object` instance presents its referent as it would be seen from a +particular compartment. This "viewing compartment" is chosen to match the +way the debugger came across the referent. As a consequence, a single +[`Debugger`][debugger-object] instance may actually have several +`Debugger.Object` instances: one for each compartment from which the +referent is viewed. + +If more than one [`Debugger`][debugger-object] instance is debugging the +same code, each [`Debugger`][debugger-object] gets a separate +`Debugger.Object` instance for a given object. This allows the code using +each [`Debugger`][debugger-object] instance to place whatever properties it +likes on its own `Debugger.Object` instances, without worrying about +interfering with other debuggers. + +While most `Debugger.Object` instances are created by SpiderMonkey in the +process of exposing debuggee's behavior and state to the debugger, the +debugger can use `Debugger.Object.prototype.makeDebuggeeValue` to create +`Debugger.Object` instances for given debuggee objects, or use +`Debugger.Object.prototype.copy` and `Debugger.Object.prototype.create` to +create new objects in debuggee compartments, allocated as if by particular +debuggee globals. + +`Debugger.Object` instances protect their referents from the garbage +collector; as long as the `Debugger.Object` instance is live, the referent +remains live. This means that garbage collection has no visible effect on +`Debugger.Object` instances. + + +## Accessor Properties of the Debugger.Object prototype + +A `Debugger.Object` instance inherits the following accessor properties +from its prototype: + +### `proto` +The referent's prototype (as a new `Debugger.Object` instance), or `null` if +it has no prototype. This accessor may throw if the referent is a scripted +proxy or some other sort of exotic object (an opaque wrapper, for example). + +### `class` +A string naming the ECMAScript `[[Class]]` of the referent. + +### `callable` +`true` if the referent is a callable object (such as a function or a +function proxy); false otherwise. + +### `name` +The name of the referent, if it is a named function. If the referent is +an anonymous function, or not a function at all, this is `undefined`. + +This accessor returns whatever name appeared after the `function` +keyword in the source code, regardless of whether the function is the +result of instantiating a function declaration (which binds the +function to its name in the enclosing scope) or evaluating a function +expression (which binds the function to its name only within the +function's body). + +### `displayName` +The referent's display name, if the referent is a function with a +display name. If the referent is not a function, or if it has no display +name, this is `undefined`. + +If a function has a given name, its display name is the same as its +given name. In this case, the `displayName` and `name` properties are +equal. + +If a function has no name, SpiderMonkey attempts to infer an appropriate +name for it given its context. For example: + +```js +function f() {} // display name: f (the given name) +var g = function () {}; // display name: g +o.p = function () {}; // display name: o.p +var q = { + r: function () {} // display name: q.r +}; +``` + +Note that the display name may not be a proper JavaScript identifier, +or even a proper expression: we attempt to find helpful names even when +the function is not immediately assigned as the value of some variable +or property. Thus, we use <code><i>a</i>/<i>b</i></code> to refer to +the <i>b</i> defined within <i>a</i>, and <code><i>a</i><</code> to +refer to a function that occurs somewhere within an expression that is +assigned to <i>a</i>. For example: + +```js +function h() { + var i = function() {}; // display name: h/i + f(function () {}); // display name: h/< +} +var s = f(function () {}); // display name: s< +``` + +### `parameterNames` +If the referent is a debuggee function, the names of its parameters, +as an array of strings. If the referent is not a debuggee function, or +not a function at all, this is `undefined`. + +If the referent is a host function for which parameter names are not +available, return an array with one element per parameter, each of which +is `undefined`. + +If the referent is a function proxy, return an empty array. + +If the function uses destructuring parameters, the corresponding array elements +are `undefined`. For example, if the referent is a function declared in this +way: + +```js +function f(a, [b, c], {d, e:f}) { ... } +``` + +then this `Debugger.Object` instance's `parameterNames` property would +have the value: + +```js +["a", undefined, undefined] +``` + +### `script` +If the referent is a function that is debuggee code, this is that +function's script, as a [`Debugger.Script`][script] instance. If the +referent is a function proxy or not debuggee code, this is `undefined`. + +### `environment` +If the referent is a function that is debuggee code, a +[`Debugger.Environment`][environment] instance representing the lexical +environment enclosing the function when it was created. If the referent +is a function proxy or not debuggee code, this is `undefined`. + +### `isError` +`true` if the referent is any potentially wrapped Error; `false` otherwise. + +### `errorMessageName` +If the referent is an error created with an engine internal message template +this is a string which is the name of the template; `undefined` otherwise. + +### `errorLineNumber` +If the referent is an Error object, this is the 1-origin line number at which +the referent was created; `undefined` otherwise. + +### `errorColumnNumber` +If the referent is an Error object, this is the 1-origin column number in +UTF-16 code units at which the referent was created; `undefined` otherwise. + +### `isBoundFunction` +If the referent is a debuggee function, returns `true` if the referent is a +bound function; `false` otherwise. If the referent is not a debuggee +function, or not a function at all, returns `undefined` instead. + +### `isArrowFunction` +If the referent is a debuggee function, returns `true` if the referent is an +arrow function; `false` otherwise. If the referent is not a debuggee +function, or not a function at all, returns `undefined` instead. + +### `isGeneratorFunction` +If the referent is a debuggee function, returns `true` if the referent was +created with a `function*` expression or statement, or false if it is some +other sort of function. If the referent is not a debuggee function, or not a +function at all, this is `undefined`. (This is always equal to +`obj.script.isGeneratorFunction`, assuming `obj.script` is a +`Debugger.Script`.) + +### `isAsyncFunction` +If the referent is a debuggee function, returns `true` if the referent is an +async function, defined with an `async function` expression or statement, or +false if it is some other sort of function. If the referent is not a +debuggee function, or not a function at all, this is `undefined`. (This is +always equal to `obj.script.isAsyncFunction`, assuming `obj.script` is a +`Debugger.Script`.) + +### `isClassConstructor` +If the referent is a debuggee function, returns `true` if the referent is a class, +or false if it is some other sort of function. If the referent is not a debuggee +function, or not a function at all, this is `undefined`. (This is always equal to +`obj.script.isClassConstructor`, assuming `obj.script` is a `Debugger.Script`.) + +### `isPromise` +`true` if the referent is a Promise; `false` otherwise. + +### `boundTargetFunction` +If the referent is a bound debuggee function, this is its target function— +the function that was bound to a particular `this` object. If the referent +is either not a bound function, not a debuggee function, or not a function +at all, this is `undefined`. + +### `boundThis` +If the referent is a bound debuggee function, this is the `this` value it +was bound to. If the referent is either not a bound function, not a debuggee +function, or not a function at all, this is `undefined`. + +### `boundArguments` +If the referent is a bound debuggee function, this is an array (in the +Debugger object's compartment) that contains the debuggee values of the +`arguments` object it was bound to. If the referent is either not a bound +function, not a debuggee function, or not a function at all, this is +`undefined`. + +### `isProxy` +If the referent is a (scripted) proxy, either revoked or not, return `true`. +If the referent is not a (scripted) proxy, return `false`. + +### `proxyTarget` +If the referent is a non-revoked (scripted) proxy, return a `Debugger.Object` +instance referring to the ECMAScript `[[ProxyTarget]]` of the referent. +If the referent is a revoked (scripted) proxy, return `null`. +If the referent is not a (scripted) proxy, return `undefined`. + +### `proxyHandler` +If the referent is a non-revoked (scripted) proxy, return a `Debugger.Object` +instance referring to the ECMAScript `[[ProxyHandler]]` of the referent. +If the referent is a revoked (scripted) proxy, return `null`. +If the referent is not a (scripted) proxy, return `undefined`. + +### `promiseState` +If the referent is a [`Promise`][promise], return a string indicating +whether the [`Promise`][promise] is pending, or has been fulfilled or +rejected. This string takes one of the following values: + +* `"pending"`, if the [`Promise`][promise] is pending. + +* `"fulfilled"`, if the [`Promise`][promise] has been fulfilled. + +* `"rejected"`, if the [`Promise`][promise] has been rejected. + +If the referent is not a [`Promise`][promise], throw a `TypeError`. + +### `promiseValue` +Return a debuggee value representing the value the [`Promise`][promise] has +been fulfilled with. + +If the referent is not a [`Promise`][promise], or the [`Promise`][promise] +has not been fulfilled, throw a `TypeError`. + +### `promiseReason` +Return a debuggee value representing the value the [`Promise`][promise] has +been rejected with. + +If the referent is not a [`Promise`][promise], or the [`Promise`][promise] +has not been rejected, throw a `TypeError`. + +### `promiseAllocationSite` +If the referent is a [`Promise`][promise], this is the +[JavaScript execution stack][saved-frame] captured at the time of the +promise's allocation. This can return null if the promise was not +created from script. If the referent is not a [`Promise`][promise], throw +a `TypeError` exception. + +### `promiseResolutionSite` +If the referent is a [`Promise`][promise], this is the +[JavaScript execution stack][saved-frame] captured at the time of the +promise's resolution. This can return null if the promise was not +resolved by calling its `resolve` or `reject` resolving functions from +script. If the referent is not a [`Promise`][promise], throw a `TypeError` +exception. + +### `promiseID` +If the referent is a [`Promise`][promise], this is a process-unique +identifier for the [`Promise`][promise]. With e10s, the same id can +potentially be assigned to multiple [`Promise`][promise] instances, if +those instances were created in different processes. If the referent is +not a [`Promise`][promise], throw a `TypeError` exception. + +### `promiseDependentPromises` +If the referent is a [`Promise`][promise], this is an `Array` of +`Debugger.Objects` referring to the promises directly depending on the +referent [`Promise`][promise]. These are: + +1) Return values of `then()` calls on the promise. +2) Return values of `Promise.all()` if the referent [`Promise`][promise] + was passed in as one of the arguments. +3) Return values of `Promise.race()` if the referent [`Promise`][promise] + was passed in as one of the arguments. + +Once a [`Promise`][promise] is settled, it will generally notify its +dependent promises and forget about them, so this is most useful on +*pending* promises. + +Note that the `Array` only contains the promises that directly depend on +the referent [`Promise`][promise]. It does not contain promises that depend +on promises that depend on the referent [`Promise`][promise]. + +If the referent is not a [`Promise`][promise], throw a `TypeError` +exception. + +### `promiseLifetime` +If the referent is a [`Promise`][promise], this is the number of +milliseconds elapsed since the [`Promise`][promise] was created. If the +referent is not a [`Promise`][promise], throw a `TypeError` exception. + +### `promiseTimeToResolution` +If the referent is a [`Promise`][promise], this is the number of +milliseconds elapsed between when the [`Promise`][promise] was created and +when it was resolved. If the referent hasn't been resolved or is not a +[`Promise`][promise], throw a `TypeError` exception. + +### `allocationSite` +If [object allocation site tracking][tracking-allocs] was enabled when this +`Debugger.Object`'s referent was allocated, return the +[JavaScript execution stack][saved-frame] captured at the time of the +allocation. Otherwise, return `null`. + + +## Function Properties of the Debugger.Object prototype + +The functions described below may only be called with a `this` value +referring to a `Debugger.Object` instance; they may not be used as methods +of other kinds of objects. The descriptions use "referent" to mean "the +referent of this `Debugger.Object` instance". + +Unless otherwise specified, these methods are not +[invocation functions][inv fr]; if a call would cause debuggee code to run +(say, because it gets or sets an accessor property whose handler is +debuggee code, or because the referent is a proxy whose traps are debuggee +code), the call throws a [`Debugger.DebuggeeWouldRun`][wouldrun] exception. + +These methods may throw if the referent is not a native object. Even simple +accessors like `isExtensible` may throw if the referent is a proxy or some sort +of exotic object like an opaque wrapper. + +### `getProperty(key, [receiver])` +Return a [completion value][cv] with "return" being the value of the +referent's property named <i>key</i>, or `undefined` if it has no such +property. <i>key</i> must be a string or symbol; <i>receiver</i> must be +a debuggee value. If the property is a getter, it will be evaluated with +<i>receiver</i> as the receiver, defaulting to the `Debugger.Object` +if omitted. All extant handler methods, breakpoints, and so on remain +active during the call. + +### `setProperty(key, value<, [receiver])` +Store <i>value</i> as the value of the referent's property named +<i>key</i>, creating the property if it does not exist. <i>key</i> +must be a string or symbol; <i>value</i> and <i>receiver</i> must be +debuggee values. If the property is a setter, it will be evaluated with +<i>receiver</i> as the receiver, defaulting to the `Debugger.Object` +if omitted. Return a [completion value][cv] with "return" being a +success/failure boolean, or else "throw" being the exception thrown +during property assignment. All extant handler methods, breakpoints, +and so on remain active during the call. + +### `getOwnPropertyDescriptor(key)` +Return a property descriptor for the property named <i>key</i> of the +referent. If the referent has no such property, return `undefined`. +(This function behaves like the standard +`Object.getOwnPropertyDescriptor` function, except that the object being +inspected is implicit; the property descriptor returned is allocated as +if by code scoped to the debugger's global object (and is thus in the +debugger's compartment); and its `value`, `get`, and `set` properties, +if present, are debuggee values.) + +### `getOwnPropertyNames()` +Return an array of strings naming all the referent's own properties, as +if <code>Object.getOwnPropertyNames(<i>referent</i>)</code> had been +called in the debuggee, and the result copied in the scope of the +debugger's global object. + +### `getOwnPropertyNamesLength()` +Return the number of the referent's own properties. + +### `getOwnPropertySymbols()` +Return an array of strings naming all the referent's own symbols, as +if <code>Object.getOwnPropertySymbols(<i>referent</i>)</code> had been +called in the debuggee, and the result copied in the scope of the +debugger's global object. + +### `defineProperty(key, attributes)` +Define a property on the referent named <i>key</i>, as described by +the property descriptor <i>descriptor</i>. Any `value`, `get`, and +`set` properties of <i>attributes</i> must be debuggee values. (This +function behaves like `Object.defineProperty`, except that the target +object is implicit, and in a different compartment from the function +and descriptor.) + +### `defineProperties(properties)` +Add the properties given by <i>properties</i> to the referent. (This +function behaves like `Object.defineProperties`, except that the target +object is implicit, and in a different compartment from the +<i>properties</i> argument.) + +### `deleteProperty(key)` +Remove the referent's property named <i>key</i>. Return true if the +property was successfully removed, or if the referent has no such +property. Return false if the property is non-configurable. + +### `seal()` +Prevent properties from being added to or deleted from the referent. +Return this `Debugger.Object` instance. (This function behaves like the +standard `Object.seal` function, except that the object to be sealed is +implicit and in a different compartment from the caller.) + +### `freeze()` +Prevent properties from being added to or deleted from the referent, and +mark each property as non-writable. Return this `Debugger.Object` +instance. (This function behaves like the standard `Object.freeze` +function, except that the object to be sealed is implicit and in a +different compartment from the caller.) + +### `preventExtensions()` +Prevent properties from being added to the referent. (This function +behaves like the standard `Object.preventExtensions` function, except +that the object to operate on is implicit and in a different compartment +from the caller.) + +### `isSealed()` +Return true if the referent is sealed—that is, if it is not extensible, +and all its properties have been marked as non-configurable. (This +function behaves like the standard `Object.isSealed` function, except +that the object inspected is implicit and in a different compartment +from the caller.) + +### `isFrozen()` +Return true if the referent is frozen—that is, if it is not extensible, +and all its properties have been marked as non-configurable and +read-only. (This function behaves like the standard `Object.isFrozen` +function, except that the object inspected is implicit and in a +different compartment from the caller.) + +### `isExtensible()` +Return true if the referent is extensible—that is, if it can have new +properties defined on it. (This function behaves like the standard +`Object.isExtensible` function, except that the object inspected is +implicit and in a different compartment from the caller.) + +### `makeDebuggeeValue(value)` +Return the debuggee value that represents <i>value</i> in the debuggee. +If <i>value</i> is a primitive, we return it unchanged; if <i>value</i> +is an object, we return the `Debugger.Object` instance representing +that object, wrapped appropriately for use in this `Debugger.Object`'s +referent's compartment. + +Note that, if <i>value</i> is an object, it need not be one allocated +in a debuggee global, nor even a debuggee compartment; it can be any +object the debugger wishes to use as a debuggee value. + +As described above, each `Debugger.Object` instance presents its +referent as viewed from a particular compartment. Given a +`Debugger.Object` instance <i>d</i> and an object <i>o</i>, the call +<code><i>d</i>.makeDebuggeeValue(<i>o</i>)</code> returns a +`Debugger.Object` instance that presents <i>o</i> as it would be seen +by code in <i>d</i>'s compartment. + +### `isSameNative(value)` +If <i>value</i> is a native function in the debugger's compartment, return +whether the referent is a native function for the same C++ native. + +### `isNativeGetterWithJitInfo()` +Return whether the referent is a native getter function with JSJitInfo. + +### `decompile([pretty])` +If the referent is a function that is debuggee code, return the +JavaScript source code for a function definition equivalent to the +referent function in its effect and result, as a string. If +<i>pretty</i> is present and `true`, produce indented code with line +breaks. If the referent is not a function that is debuggee code, return +`undefined`. + +### `call(this, argument, ...)` +If the referent is callable, call it with the given <i>this</i> value +and <i>argument</i> values, and return a [completion value][cv] +describing how the call completed. <i>This</i> should be a debuggee +value, or `{ asConstructor: true }` to invoke the referent as a +constructor, in which case SpiderMonkey provides an appropriate `this` +value itself. Each <i>argument</i> must be a debuggee value. All extant +handler methods, breakpoints, and so on remain active +during the call. If the referent is not callable, throw a `TypeError`. +This function follows the [invocation function conventions][inv fr]. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +### `apply(this, arguments)` +If the referent is callable, call it with the given <i>this</i> value +and the argument values in <i>arguments</i>, and return a +[completion value][cv] describing how the call completed. <i>This</i> +should be a debuggee value, or `{ asConstructor: true }` to invoke +<i>function</i> as a constructor, in which case SpiderMonkey provides +an appropriate `this` value itself. <i>Arguments</i> must either be an +array (in the debugger) of debuggee values, or `null` or `undefined`, +which are treated as an empty array. All extant handler methods, +breakpoints, and so on remain active during the call. If +the referent is not callable, throw a `TypeError`. This function +follows the [invocation function conventions][inv fr]. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +### `executeInGlobal(code, [options])` +If the referent is a global object, evaluate <i>code</i> in that global +environment, and return a [completion value][cv] describing how it completed. +<i>Code</i> is a string. All extant handler methods, breakpoints, +and so on remain active during the call (pending note below). This function +follows the [invocation function conventions][inv fr]. +If the referent is not a global object, throw a `TypeError` exception. + +<i>Code</i> is interpreted as strict mode code when it contains a Use +Strict Directive. + +This evaluation is semantically equivalent to executing statements at the +global level, not an indirect eval. Regardless of <i>code</i> being strict +mode code, variable declarations in <i>code</i> affect the referent global +object. + +The <i>options</i> argument is as for [`Debugger.Frame.prototype.eval`][fr eval]. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +### `executeInGlobalWithBindings(code, bindings, [options])` +Like `executeInGlobal`, but evaluate <i>code</i> using the referent as the +variable object, but with a lexical environment extended with bindings +from the object <i>bindings</i>. + +An extra environment is created with bindings specified by <i>bindings</i>. +The environment contains bindings corresponding to each own enumerable property +of <i>bindings</i>, where the property name <i>name</i> as binding name, +and the property value <i>value</i> as binding's initial value. + +If `options.useInnerBindings` is not specified or is specified as `false`, +it emulates where the bindings environment is placed outside of global, +which means, if the binding conflicts with any global variable declared in +the <i>code</i>, or any existing global variable, the binding is ignored. + +If `options.useInnerBindings` is speified as `true`, it directly performs as +the bindings environment is placed inside the global, and the provided bindings +shadow the global variables. + +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 passed +<i>bindings</i> object, because the properties are copied to a new object for +each invocation. + +This method allows debugger code to introduce temporary bindings that +are visible to the given debuggee code and which refer to debugger-held +debuggee values, and do so without mutating any existing debuggee +environment. + +Note that, like `executeInGlobal`, any declarations it contains affect the +referent global object, even as <i>code</i> is evaluated in an environment +extended according to <i>bindings</i>. (In the terms used by the ECMAScript +specification, the `VariableEnvironment` of the execution context for +<i>code</i> is the referent, and the <i>bindings</i> appear in a new +declarative environment, which is the eval code's `LexicalEnvironment`.) + +The <i>options</i> argument is as for [`Debugger.Frame.prototype.eval`][fr eval]. + +Note: If this method is called on an object whose owner +[Debugger object][debugger-object] has an onNativeCall handler, only hooks +on objects associated with that debugger will be called during the evaluation. + +### `createSource(options)` +If the referent is a global object, return a new JavaScript source in the +global's realm which has its properties filled in according to the `options` +object. If the referent is not a global object, throw a `TypeError` +exception. The `options` object can have the following properties: + * `text`: String contents of the JavaScript in the source. + * `url`: URL the resulting source should be associated with. + * `startLine`: Starting line of the source. + * `startColumn`: Starting column of the source. + * `sourceMapURL`: Optional URL specifying the source's source map URL. + If not specified, the source map URL can be filled in if specified by + the source's text. + * `isScriptElement`: Optional boolean which will set the source's + `introductionType` to `"inlineScript"` if specified. Otherwise, the + source's `introductionType` will be `undefined`. + +### `asEnvironment()` +If the referent is a global object, return the [`Debugger.Environment`][environment] +instance representing the referent's global lexical scope. The global +lexical scope's enclosing scope is the global object. If the referent is +not a global object, throw a `TypeError`. + +### `unwrap()` +If the referent is a wrapper that this `Debugger.Object`'s compartment +is permitted to unwrap, return a `Debugger.Object` instance referring to +the wrapped object. If we are not permitted to unwrap the referent, +return `null`. If the referent is not a wrapper, return this +`Debugger.Object` instance unchanged. + +### `unsafeDereference()` +Return the referent of this `Debugger.Object` instance. + +If the referent is an inner object (say, an HTML5 `Window` object), +return the corresponding outer object (say, the HTML5 `WindowProxy` +object). This makes `unsafeDereference` more useful in producing values +appropriate for direct use by debuggee code, without using [invocation functions][inv fr]. + +This method pierces the membrane of `Debugger.Object` instances meant to +protect debugger code from debuggee code, and allows debugger code to +access debuggee objects through the standard cross-compartment wrappers, +rather than via `Debugger.Object`'s reflection-oriented interfaces. This +method makes it easier to gradually adapt large code bases to this +Debugger API: adapted portions of the code can use `Debugger.Object` +instances, but use this method to pass direct object references to code +that has not yet been updated. + +### `forceLexicalInitializationByName(binding)` +If <i>binding</i> is in an uninitialized state initialize it to undefined +and return true, otherwise do nothing and return false. + +### `getPromiseReactions` + +If the referent is a [`Promise`][promise] or a cross-compartment wrapper of one, +this returns an array of objects describing the reaction records added to the +promise. There are several different sorts of reaction records: + +- The array entry for a reaction record added with `then` or `catch` has the + form `{ resolve: F, reject: F, result: P }`, where each `F` is a `Debugger.Object` + referring to a function object, and `P` is the promise that will be resolved + with the result of calling them. + + The `resolve` and `reject` properties may be absent in some cases. A call to + `then` can omit the rejection handler, and a call to `catch` omits the + resolution handler. Furthermore, various promise facilities create records + like this as internal implementation details, creating handlers that are not + representable as JavaScript functions. + +- When a promise `P1` is resolved to another promise `P2` (such that resolving + `P2` resolves `P1` in the same way) that adds a reaction record to `P2`. The + array entry for that reaction record is simply the `Debugger.Object` + representing `P1`. + + Note that, if `P1` and `P2` are in different compartments, resolving `P1` to + `P2` creates the same sort of reaction record as a call to `then` or + `catch`, with `P1` stored only in a private slot of the `resolve` and + `reject` functions, and not directly available from the reaction record. + +- An `await` expression calls `PromiseResolve` on its operand to obtain a + promise `P`, and then adds a reaction record to `P` that resumes the + suspended call appropriately. The array entry for that reaction record is a + `Debugger.Frame` representing the suspended call. + + If the `await`'s operand `A` is a native promise with the standard + constructor, then `PromiseResolve` simply returns `A` unchanged, and the + reaction record for resuming the suspended call is added to `A`'s list. But + if `A` is some other sort of 'thenable', then `PromiseResolve` creates a new + promise and enqueues a job to call `A`'s `then` method; this may produce + more indirect chains from awaitees to awaiters. + +- `JS::AddPromiseReactions` and + `JS::AddPromiseReactionsIgnoringUnhandledRejection` create a reaction record + whose promise field is `null`. + +[debugger-object]: Debugger.md +[script]: Debugger.Script.md +[environment]: Debugger.Environment.md +[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise +[saved-frame]: ../SavedFrame/index + +[tracking-allocs]: Debugger.Memory.md#trackingallocationsites +[inv fr]: Debugger.Frame.md#invocation-functions-and-debugger-frames +[wouldrun]: Conventions.md#the-debugger-debuggeewouldrun-exception +[cv]: Conventions.md#completion-values +[fr eval]: Debugger.Frame.md#eval-code-options diff --git a/js/src/doc/Debugger/Debugger.Script.md b/js/src/doc/Debugger/Debugger.Script.md new file mode 100644 index 0000000000..1c9665c435 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Script.md @@ -0,0 +1,506 @@ +# Debugger.Script + +A `Debugger.Script` instance may refer to a sequence of bytecode in the +debuggee or to a block of WebAssembly code. For the former, it is the +[`Debugger`][debugger-object] API's presentation of a JSAPI `JSScript` +object. The two cases are distinguished by their `format` property being +`"js"` or `"wasm"`. + +## Debugger.Script for JSScripts + +For `Debugger.Script` instances referring to a `JSScript`, they are +distinguished by their `format` property being `"js"`. + +Each of the following is represented by a single `JSScript` object: + +* The body of a function—that is, all the code in the function that is not + contained within some nested function. + +* The code passed to a single call to `eval`, excluding the bodies of any + functions that code defines. + +* The contents of a `<script>` element. + +* A DOM event handler, whether embedded in HTML or attached to the element + by other JavaScript code. + +* Code appearing in a `javascript:` URL. + +The [`Debugger`][debugger-object] interface constructs `Debugger.Script` objects as scripts +of debuggee code are uncovered by the debugger: via the `onNewScript` +handler method; via [`Debugger.Frame`][frame]'s `script` properties; via the +`functionScript` method of [`Debugger.Object`][object] instances; and so on. For a +given [`Debugger`][debugger-object] instance, SpiderMonkey constructs exactly one +`Debugger.Script` instance for each underlying script object; debugger +code can add its own properties to a script object and expect to find +them later, use `==` to decide whether two expressions refer to the same +script, and so on. + +(If more than one [`Debugger`][debugger-object] instance is debugging the same code, each +[`Debugger`][debugger-object] gets a separate `Debugger.Script` instance for a given +script. This allows the code using each [`Debugger`][debugger-object] instance to place +whatever properties it likes on its `Debugger.Script` instances, without +worrying about interfering with other debuggers.) + +A `Debugger.Script` instance is a strong reference to a JSScript object; +it protects the script it refers to from being garbage collected. + +Note that SpiderMonkey may use the same `Debugger.Script` instances for +equivalent functions or evaluated code—that is, scripts representing the +same source code, at the same position in the same source file, +evaluated in the same lexical environment. + +## Debugger.Script for WebAssembly + +For `Debugger.Script` instances referring to a block of WebAssembly code, they +are distinguished by their `format` property being `"wasm"`. + +Currently only entire modules evaluated via `new WebAssembly.Module` are +represented. + +`Debugger.Script` objects for WebAssembly are uncovered via `onNewScript` when +a new WebAssembly module is instantiated and via the `findScripts` method on +[`Debugger`][debugger-object] instances. SpiderMonkey constructs exactly one +`Debugger.Script` for each underlying WebAssembly module per +[`Debugger`][debugger-object] instance. + +A `Debugger.Script` instance is a strong reference to the underlying +WebAssembly module; it protects the module it refers to from being garbage +collected. + +Please note at the time of this writing, support for WebAssembly is +very preliminary. Many properties and methods below throw. + +## Convention + +For descriptions of properties and methods below, if the behavior of the +property or method differs between the instance referring to a `JSScript` or +to a block of WebAssembly code, the text will be split into two sections, +headed by "**if the instance refers to a `JSScript`**" and "**if the instance +refers to WebAssembly code**", respectively. If the behavior does not differ, +no such emphasized headings will appear. + +## Accessor Properties of the Debugger.Script Prototype Object + +A `Debugger.Script` instance inherits the following accessor properties +from its prototype: + +### `isGeneratorFunction` +True if this instance refers to a `JSScript` for a function defined with a +`function*` expression or statement. False otherwise. + +### `isAsyncFunction` +True if this instance refers to a `JSScript` for an async function, defined +with an `async function` expression or statement. False otherwise. + +### `isFunction` +True if this instance refers to a `JSScript` for a function. False +otherwise. + +### `isModule` +True if this instance refers to a `JSScript` that was parsed and loaded +as an ECMAScript module. False otherwise. + +### `displayName` +**If the instance refers to a `JSScript`**, this is the script's display +name, if it has one. If the script has no display name — for example, +if it is a top-level `eval` script — this is `undefined`. + +If the script's function has a given name, its display name is the same as +its function's given name. + +If the script's function has no name, SpiderMonkey attempts to infer an +appropriate name for it given its context. For example: + +```js +function f() {} // display name: f (the given name) +var g = function () {}; // display name: g +o.p = function () {}; // display name: o.p +var q = { + r: function () {} // display name: q.r +}; +``` + +Note that the display name may not be a proper JavaScript identifier, +or even a proper expression: we attempt to find helpful names even when +the function is not immediately assigned as the value of some variable +or property. Thus, we use <code><i>a</i>/<i>b</i></code> to refer to +the <i>b</i> defined within <i>a</i>, and <code><i>a</i><</code> to +refer to a function that occurs somewhere within an expression that is +assigned to <i>a</i>. For example: + +```js +function h() { + var i = function() {}; // display name: h/i + f(function () {}); // display name: h/< +} +var s = f(function () {}); // display name: s< +``` + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `parameterNames` +**If the instance refers to a `JSScript`**, the names of its parameters, +as an array of strings. If the script is not a function script this is +`undefined`. + +If the function uses destructuring parameters, the corresponding array elements +are `undefined`. For example, if the referent is a function script declared in this +way: + +```js +function f(a, [b, c], {d, e:f}) { ... } +``` + +then this `Debugger.Script` instance's `parameterNames` property would +have the value: + +```js +["a", undefined, undefined] +``` + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `url` +**If the instance refers to a `JSScript`**, the filename or URL from which +this script's code was loaded. For scripts created by `eval` or the +`Function` constructor, this may be a synthesized filename, starting with a +valid URL and followed by information tracking how the code was introduced +into the system; the entire string is not a valid URL. For +`Function.prototype`'s script, this is `null`. If this `Debugger.Script`'s +`source` property is non-`null`, then this is equal to `source.url`. + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `startLine` +**If the instance refers to a `JSScript`**, the 1-origin number of the line at +which this script's code starts, within the file or document named by +`url`. + +### `startColumn` +**If the instance refers to a `JSScript`**, the 1-origin column number at +which this script's code starts, within the file or document named by `url`. +For functions, this is the start of the function's arguments: +```js +function f() { ... } +// ^ start (column 11) +let g = x => x*x; +// ^ start (column 9) +let h = (x) => x*x; +// ^ start (column 9) +``` +For default class constructors, it is the start of the `class` keyword: +```js +let MyClass = class { }; +// ^ start (column 15) +``` +For scripts from other sources, such as `eval` or the `Function` +constructor, it is typically 0: +```js +let f = new Function(" console.log('hello world');"); +// ^ start (column 1, from the string's perspective) +``` + +### `lineCount` +**If the instance refers to a `JSScript`**, the 1-origin number of lines this +script's code occupies, within the file or document named by `url`. + +### `source` +**If the instance refers to a `JSScript`**, the +[`Debugger.Source`][source] instance representing the source code from +which this script was produced. This is `null` if the source code was not +retained. + +**If the instance refers to WebAssembly code**, the +[`Debugger.Source`][source] instance representing the serialized text +format of the WebAssembly code. + +### `sourceStart` +**If the instance refers to a `JSScript`**, the character within the +[`Debugger.Source`][source] instance given by `source` at which this +script's code starts; zero-based. If this is a function's script, this is +the index of the start of the `function` token in the source code. + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `sourceLength` +**If the instance refers to a `JSScript`**, the length, in characters, of +this script's code within the [`Debugger.Source`][source] instance given +by `source`. + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `mainOffset` +**If the instance refers to a `JSScript`**, the 0-origin offset of the main +entry point of the script, excluding any prologue. + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `global` +**If the instance refers to a `JSScript`**, a [`Debugger.Object`][object] +instance referring to the global object in whose scope this script +runs. The result refers to the global directly, not via a wrapper or a +`WindowProxy` ("outer window", in Firefox). + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `format` +**If the instance refers to a `JSScript`**, `"js"`. + +**If the instance refers to WebAssembly code**, `"wasm"`. + +## Function Properties of the Debugger.Script Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger.Script` instance; they may not be used as +methods of other kinds of objects. + +### `getChildScripts()` +**If the instance refers to a `JSScript`**, return a new array whose +elements are Debugger.Script objects for each function +in this script. Only direct children are included; nested +children can be reached by walking the tree. + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `getPossibleBreakpoints(query)` +Query for the recommended breakpoint locations available in SpiderMonkey. +Returns a result array of objects with the following properties: + * `offset: number` - The 0-origin offset the breakpoint. + * `lineNumber: number` - The 1-origin line number of the breakpoint. + * `columnNumber: number` - The 1-origin column number of the breakpoint. + * `isStepStart: boolean` - True if SpiderMonkey recommends that the + breakpoint be treated as a step location when users of debuggers + step to the next item. This _roughly_ translates to the start of + each statement, though not entirely. + +The `query` argument can be used to filter the set of breakpoints. +The `query` object can contain the following properties: + + * `minOffset: number` - The inclusive lower bound of `offset` values to include (0-origin). + * `maxOffset: number` - The exclusive upper bound of `offset` values to include (0-origin). + * `line: number` - Limit to breakpoints on the given line (1-origin). + * `minLine: number` - The inclusive lower bound of lines to include (1-origin). + * `minColumn: number` - The inclusive lower bound of the line/minLine column to include (1-origin). + * `maxLine: number` - The exclusive upper bound of lines to include (1-origin). + * `maxColumn: number` - The exclusive upper bound of the line/maxLine column to include (1-origin). + +### `getPossibleBreakpointOffsets(query)` +Query for the recommended breakpoint locations available in SpiderMonkey. +Identical to getPossibleBreakpoints except this returns an array of `offset` +values instead of offset metadata objects. + +### `getOffsetMetadata(offset)` +Get metadata about a given bytecode offset (0-origin). +Returns an object with the following properties: + * `lineNumber: number` - The 1-origin line number of the breakpoint. + * `columnNumber: number` - The 1-origin column number of the breakpoint. + * `isBreakpoint: boolean` - True if this offset qualifies as a breakpoint, + defined using the same semantics used for `getPossibleBreakpoints()`. + * `isStepStart: boolean` - True if SpiderMonkey recommends that the + breakpoint be treated as a step location when users of debuggers + step to the next item. This _roughly_ translates to the start of + each statement, though not entirely. + +### `setBreakpoint(offset, handler)` +**If the instance refers to a `JSScript`**, set a breakpoint at the +bytecode instruction at <i>offset</i> (0-origin) in this script, reporting hits +to the `hit` method of <i>handler</i>. If <i>offset</i> is not a valid offset +in this script, throw an error. Also, even if <i>offset</i> is valid offset +in this script, some instructions for engine-internal operation (e.g. +SetAliasedVar in the generator function initialization) don't allow setting +breakpoints, and in that case, this also throws an error. + +When execution reaches the given instruction, SpiderMonkey calls the +`hit` method of <i>handler</i>, passing a [`Debugger.Frame`][frame] +instance representing the currently executing stack frame. The `hit` +method's return value should be a [resumption value][rv], determining +how execution should continue. + +Any number of breakpoints may be set at a single location; when control +reaches that point, SpiderMonkey calls their handlers in an unspecified +order. + +Any number of breakpoints may use the same <i>handler</i> object. + +Breakpoint handler method calls are cross-compartment, intra-thread +calls: the call takes place in the same thread that hit the breakpoint, +and in the compartment containing the handler function (typically the +debugger's compartment). + +The new breakpoint belongs to the [`Debugger`][debugger-object] instance to +which this script belongs. Removing a global from the +[`Debugger`][debugger-object] instance's set of debuggees clears all the +breakpoints belonging to that [`Debugger`][debugger-object] instance in that +global's scripts. + +### `getBreakpoints([offset])` +**If the instance refers to a `JSScript`**, return an array containing the +handler objects for all the breakpoints set at <i>offset</i> (0-origin) in this +script. If <i>offset</i> is omitted, return the handlers of all +breakpoints set anywhere in this script. If <i>offset</i> is present, but +not a valid offset in this script, throw an error. + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `clearBreakpoint(handler, [offset])` +**If the instance refers to a `JSScript`**, remove all breakpoints set in +this [`Debugger`][debugger-object] instance that use <i>handler</i> as +their handler. If <i>offset</i> (0-origin) is given, remove only those +breakpoints set at <i>offset</i> that use <i>handler</i>; if <i>offset</i> is +not a valid offset in this script, throw an error. + +Note that, if breakpoints using other handler objects are set at the +same location(s) as <i>handler</i>, they remain in place. + +### `clearAllBreakpoints([offset])` +**If the instance refers to a `JSScript`**, remove all breakpoints set in +this script. If <i>offset</i> (0-origin) is present, remove all breakpoints set +at that offset in this script; if <i>offset</i> is not a valid bytecode +offset in this script, throw an error. + +### `getEffectfulOffsets()` +**If the instance refers to a `JSScript`**, return an array +containing the 0-origin offsets of all bytecodes in the script which can have +direct side effects that are visible outside the currently executing frame. +This includes, for example, operations that set properties or elements on +objects, or that may set names in environments created outside the frame. + +This doesn't include some instructions for engine-internal operation (e.g. +SetAliasedVar in the generator function initialization). Those instructions +can be effectful in term of engine-internal, but that's not user-visible and +can be treated as not-effectful here. + +### `getOffsetsCoverage()`: +**If the instance refers to a `JSScript`**, return `null` or an array which +contains information about the coverage of all opcodes. The elements of +the array are objects, each of which describes a single opcode, and +contains the following properties: + + * `lineNumber`: the 1-origin line number of the current opcode. + + * `columnNumber`: the 1-origin column number of the current opcode. + + * `offset`: the 0-origin bytecode instruction offset of the current opcode. + + * `count`: the number of times the current opcode got executed. + +If this script has no coverage, or if it is not instrumented, then this +function will return `null`. To ensure that the debuggee is instrumented, +the flag `Debugger.collectCoverageInfo` should be set to `true`. + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### `isInCatchScope([offset])` +**If the instance refers to a `JSScript`**, this is `true` if this 0-origin +offset falls within the scope of a try block, and `false` otherwise. + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +### Deprecated Debugger.Script Prototype Functions + +The following functions have all been deprecated in favor of `getOffsetMetadata`, +`getPossibleBreakpoints`, and `getPossibleBreakpointOffsets`. These functions +all have an under-defined concept of what offsets are and are not included +in their results. + +#### `getAllOffsets()` +**If the instance refers to a `JSScript`**, return an array <i>L</i> +describing the relationship between bytecode instruction offsets (0-origin) and +source code positions in this script. <i>L</i> is sparse, and indexed by +source line number. If a source line number <i>line</i> has no code, then +<i>L</i> has no <i>line</i> property. If there is code for <i>line</i>, +then <code><i>L</i>[<i>line</i>]</code> is an array of offsets of byte +code instructions that are entry points to that line. + +For example, suppose we have a script for the following source code: + +```js +a=[] +for (i=1; i < 10; i++) + // It's hip to be square. + a[i] = i*i; +``` + +Calling `getAllOffsets()` on that code might yield an array like this: + +```js +[, [0], [16, 75], , [52]] +``` + +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 16 and 75 (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 52. + +**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 1-origin line number for which offset is an entry point + +* columnNumber: the 1-origin column number for which offset is an entry point + +* offset: the 0-origin bytecode instruction offset of the entry point + +For example, suppose we have a script for the following source code: + +```js +a=[] +for (i=1; i < 10; i++) + // It's hip to be square. + a[i] = i*i; +``` + +Calling `getAllColumnOffsets()` on that code might yield an array like this: + +```js +[{ lineNumber: 1, columnNumber: 1, offset: 0 }, + { lineNumber: 2, columnNumber: 6, offset: 16 }, + { lineNumber: 2, columnNumber: 11, offset: 28 }, + { lineNumber: 4, columnNumber: 5, offset: 52 }, + { lineNumber: 4, columnNumber: 14, offset: 67 }, + { lineNumber: 2, columnNumber: 19, offset: 75 }] +``` + +**If the instance refers to WebAssembly code**, throw a `TypeError`. + +#### `getLineOffsets(line)` +**If the instance refers to a `JSScript`**, return an array of bytecode +instruction offsets representing the entry points to source line +<i>line</i> (1-origin). If the script contains no executable code at that line, +the array returned is empty. + +#### `getOffsetLocation(offset)` +**If the instance refers to a `JSScript`**, return an object describing the +source code location responsible for the bytecode at <i>offset</i> in this +script. The object has the following properties: + +* `lineNumber`: the 1-origin line number for which offset is an entry point + +* `columnNumber`: the 1-origin column number for which offset is an entry point + +* `isEntryPoint`: true if the offset is a column entry point, as + would be reported by getAllColumnOffsets(); otherwise false. + + +[debugger-object]: Debugger.md +[source]: Debugger.Source.md +[object]: Debugger.Object.md +[frame]: Debugger.Frame.md +[rv]: Conventions.md#resumption-values diff --git a/js/src/doc/Debugger/Debugger.Source.md b/js/src/doc/Debugger/Debugger.Source.md new file mode 100644 index 0000000000..722446eec9 --- /dev/null +++ b/js/src/doc/Debugger/Debugger.Source.md @@ -0,0 +1,286 @@ +# Debugger.Source + +A `Debugger.Source` instance represents either a piece of JavaScript source +code or the serialized text of a block of WebAssembly code. The two cases are +distinguished by the latter having its `introductionType` property always +being `"wasm"` and the former having its `introductionType` property never +being `"wasm"`. + +Each [`Debugger`][debugger-object] instance has a separate collection of +`Debugger.Source` instances representing the source code that has been +presented to the system. + +A debugger may place its own properties on `Debugger.Source` instances, +to store metadata about particular pieces of source code. + +## Debugger.Source for JavaScript + +For a `Debugger.Source` instance representing a piece of JavaScript source +code, its properties provide the source code itself as a string, and describe +where it came from. Each [`Debugger.Script`][script] instance refers to the +`Debugger.Source` instance holding the source code from which it was produced. + +If a single piece of source code contains both top-level code and +function definitions, perhaps with nested functions, then the +[`Debugger.Script`][script] instances for those all refer to the same +`Debugger.Source` instance. Each script indicates the substring of the +overall source to which it corresponds. + +A `Debugger.Source` instance may represent only a portion of a larger +source document. For example, an HTML document can contain JavaScript in +multiple `<script>` elements and event handler content attributes. +In this case, there may be either a single `Debugger.Source` instance +for the entire HTML document, with each [`Debugger.Script`][script] referring to +its substring of the document; or there may be a separate +`Debugger.Source` instance for each `<script>` element and +attribute. The choice is left up to the implementation. + +If a given piece of source code is presented to the JavaScript +implementation more than once, with the same origin metadata, the +JavaScript implementation may generate a fresh `Debugger.Source` +instance to represent each presentation, or it may use a single +`Debugger.Source` instance to represent them all. + +## Debugger.Source for WebAssembly + +For a `Debugger.Source` instance representing the serialized text of a block +of WebAssembly code, its properties provide the serialized text as a string. + +Currently only entire modules evaluated via `new WebAssembly.Module` are +represented. SpiderMonkey constructs exactly one `Debugger.Source` for each +underlying WebAssembly module per [`Debugger`][debugger-object] instance. + +Please note at the time of this writing, support for WebAssembly is very +preliminary. Many properties below return placeholder values. + +## Convention + +For descriptions of properties and methods below, if the behavior of the +property or method differs between the instance referring to JavaScript source +or to a block of WebAssembly code, the text will be split into two sections, +headed by "**if the instance refers to JavaScript source**" and "**if the +instance refers to WebAssembly code**", respectively. If the behavior does not +differ, no such emphasized headings will appear. + +## Accessor Properties of the Debugger.Source Prototype Object + +A `Debugger.Source` instance inherits the following accessor properties +from its prototype: + +### `text` +**If the instance refers to JavaScript source**, the JavaScript source +code, as a string. The value satisfies the `Program`, +`FunctionDeclaration`, or `FunctionExpression` productions in the +ECMAScript standard. + +**If the instance refers to WebAssembly code**, the `"[wasm]"` value will +be returned. + +### `binary` +**If the instance refers to WebAssembly code**, a Uint8Array that contains +the WebAssembly bytecode. + +### `url` +**If the instance refers to JavaScript source**, the filename or URL from +which this script's code was loaded. For scripts created by `eval` or the +`Function` constructor, this may be a synthesized filename, starting with a +valid URL and followed by information tracking how the code was introduced +into the system; the entire string is not a valid URL. For +`Function.prototype`'s script, this is `null`. Source may be loaded from a +URL in the following ways: + +* The URL may appear as the `src` attribute of a `<script>` element + in markup text. + +* The URL may be passed to the `Worker` web worker constructor, or the web + worker `importScripts` function. + +* The URL may be the name of a XPCOM JavaScript module or subscript. + +(Note that code passed to `eval`, the `Function` constructor, or a +similar function is <i>not</i> considered to be loaded from a URL; the +`url` accessor on `Debugger.Source` instances for such sources should +return `undefined`.) + +**If the instance refers to WebAssembly code**, the URL of the script that +called `new WebAssembly.Module` with the string `"> wasm"` appended. + +### `startLine` +**If the instance refers to JavaScript source**, the 1-origin start line of the +source within the file or URL it was loaded from. This is normally `1`, but +may have another value if the source is part of an HTML document. + +### `startColumn` +**If the instance refers to JavaScript source**, the 1-origin start column in +UTF-16 code units of the source within the file or URL it was loaded from. This +is normally `1`, but may have another value if the source is part of an HTML +document. + +### `id` +**If the instance refers to JavaScript source**, an int32 counter that identifies +the source within the current process. This ID is used in other places in Gecko +that weakly refer to sources, such as nsIScriptError. + +### `sourceMapURL` +**If the instance refers to JavaScript source**, if this source was +produced by a minimizer or translated from some other language, and we +know the URL of a <b>source map</b> document relating the source positions +in this source to the corresponding source positions in the original +source, then this property's value is that URL. Otherwise, this is `null`. + +(On the web, the translator may provide the source map URL in a +specially formatted comment in the JavaScript source code, or via a +header in the HTTP reply that carried the generated JavaScript.) + +This property is writable, so you can change the source map URL by +setting it. All Debugger.Source objects referencing the same +source will see the change. Setting an empty string has no effect +and will not change existing value. + +**If the instance refers to WebAssembly code**, `null`. Attempts to write +to this property throw a `TypeError`. + +### `displayURL` +If the script had a special `//# sourceURL` comment, as described in +the source maps specification, then this property's value holds +the string that was given. Otherwise, this is `null`. + +### `element` +The [`Debugger.Object`][object] instance referring to the DOM element to which +this source code belongs, if any, or `undefined` if it belongs to no DOM +element. Source belongs to a DOM element in the following cases: + +* Source belongs to a `<script>` element if it is the element's text + content (that is, it is written out as the body of the `<script>` + element in the markup text), or is the source document referenced by its + `src` attribute. + +* Source belongs to a DOM element if it is an event handler content + attribute (that is, if it is written out in the markup text as an + attribute value). + +* Source belongs to a DOM element if it was assigned to one of the + element's event handler IDL attributes as a string. (Note that one may + assign both strings and functions to DOM elements' event handler IDL + attributes. If one assigns a function, that function's script's source + does <i>not</i> belong to the DOM element; the function's definition + must appear elsewhere.) + +(If the sources attached to a DOM element change, the `Debugger.Source` +instances representing superseded code still refer to the DOM element; +this accessor only reflects origins, not current relationships.) + +### `elementAttributeName` +If this source belongs to a DOM element because it is an event handler +content attribute or an event handler IDL attribute, this is the name of +that attribute, a string. Otherwise, this is `undefined`. + +### `introductionType` +**If the instance refers to JavaScript source**, a string indicating how +this source code was introduced into the system. This accessor returns +one of the following values: + +* `"eval"`, for code passed to `eval`. + +* `"debugger eval"`, for code evaluated by debugger. + +* `"Function"`, for code passed to the `Function` constructor. + +* `"GeneratorFunction"`, for code passed to the generator constructor. + +* `"AsyncFunction"`, for code passed to the async function constructor. + +* `"AsyncGenerator"`, for code passed to the async generator constructor. + +* `"Worklet"`, for code loaded by worklet. + +* `"importScripts"`, for code by calling `importScripts` in a web worker. + +* `"eventHandler"`, for code assigned to DOM elements' event handler IDL + attributes as a string. + +* `"srcScript"`, for code belonging to `<script src="file.js">` elements. + +* `"inlineScript"`, for code belonging to `<script>code;</script>` elements. + +* `"injectedScript"`, for code belonging to scripts that _would_ be + `"inlineScript"` except that they were not part of the initial file itself. + + For example, scripts created via: + + * `document.write("<script>code;</script>")` + * `var s = document.createElement("script"); s.text = "code";` + +* `"importedModule"`, for code that was loaded indirectly by being imported + by another script using ESM static or dynamic imports. + +* `"javascriptURL"`, for code presented in `javascript:` URLs. + +* `"domTimer"`, for code passed to `setTimeout`/`setInterval` as a string. + +* `"self-hosted"`, for internal self-hosted JS code. + +* `undefined`, if the implementation doesn't know how the code was + introduced. + +**If the instance refers to WebAssembly code**, `"wasm"`. + +### `introductionScript` & `introductionOffset` +**If the instance refers to JavaScript source**, and if this source was +introduced by calling a function from debuggee code, then +`introductionScript` is the [`Debugger.Script`][script] instance referring +to the script containing that call, and `introductionOffset` is the call's +bytecode offset within that script. Otherwise, these are both `undefined`. +Taken together, these properties indicate the location of the introducing +call. + +For the purposes of these accessors, assignments to accessor properties are +treated as function calls. Thus, setting a DOM element's event handler IDL +attribute by assigning to the corresponding JavaScript property creates a +source whose `introductionScript` and `introductionOffset` refer to the +property assignment. + +Since a `<script>` element parsed from a web page's original HTML was not +introduced by any scripted call, its source's `introductionScript` and +`introductionOffset` accessors both return `undefined`. + +If a `<script>` element was dynamically inserted into a document, then these +accessors refer to the call that actually caused the script to run—usually +the call that made the element part of the document. Thus, they do +<i>not</i> refer to the call that created the element; stored the source as +the element's text child; made the element a child of some uninserted parent +node that was later inserted; or the like. + +Although the main script of a worker thread is introduced by a call to +`Worker` or `SharedWorker`, these accessors always return `undefined` on +such script's sources. A worker's main script source and the call that +created the worker are always in separate threads, but +[`Debugger`][debugger-object] is an inherently single-threaded facility: its +debuggees must all run in the same thread. Since the global that created the +worker is in a different thread, it is guaranteed not to be a debuggee of +the [`Debugger`][debugger-object] instance that owns this source; and thus +the creating call is never "in debuggee code". Relating a worker to its +creator, and other multi-threaded debugging concerns, are out of scope for +[`Debugger`][debugger-object]. + +**If the instance refers to WebAssembly code**, `introductionScript` is +the [`Debugger.Script`][script] instance referring to the same underlying +WebAssembly module. `introductionOffset` is `undefined`. + +## Function Properties of the Debugger.Source Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger.Source` instance; they may not be used as +methods of other kinds of objects. + +### `reparse()` + +**If the instance refers to JavaScript source**, and if the source has text +available, return a `Debugger.Script` instance referring to a new script +created by reparsing this source's text. Debugger `onNewScript` hooks will +not be invoked for the new script. Otherwise, throw an `Error`. + + +[debugger-object]: Debugger.md +[script]: Debugger.Script.md +[object]: Debugger.Object.md diff --git a/js/src/doc/Debugger/Debugger.md b/js/src/doc/Debugger/Debugger.md new file mode 100644 index 0000000000..ad7d57e22d --- /dev/null +++ b/js/src/doc/Debugger/Debugger.md @@ -0,0 +1,570 @@ +# The Debugger Object + +When called as a constructor, the `Debugger` object creates a new +`Debugger` instance. + +## `new Debugger([global, ...])` +Create a debugger object, and apply its [`addDebuggee`][add] method to +each of the given <i>global</i> objects to add them as the initial +debuggees. + +## Accessor Properties of the Debugger Prototype Object + +A `Debugger` instance inherits the following accessor properties from +its prototype: + +### `allowUnobservedAsmJS` +A boolean value indicating whether asm.js code running inside this +`Debugger` instance's debuggee globals is invisible to Debugger API +handlers and breakpoints. Setting this to `false` inhibits the +ahead-of-time asm.js compiler and forces asm.js code to run as normal +JavaScript. This is an accessor property with a getter and setter. It is +initially `false` in a freshly created `Debugger` instance. + +Setting this flag to `true` is intended for uses of subsystems of the +Debugger API (e.g, [`Debugger.Source`][source]) for purposes other than +step debugging a target JavaScript program. + +### `collectCoverageInfo` +A boolean value indicating whether code coverage should be enabled inside +each debuggee of this `Debugger` instance. Changing this flag value will +recompile all JIT code to add or remove code coverage +instrumentation. Changing this flag when any frame of the debuggee is +currently active on the stack will produce an exception. + +Setting this to `true` enables code coverage instrumentation, which can be +accessed via the [`Debugger.Script`][script] `getOffsetsCoverage` +function. In some cases, the code coverage might expose information which +pre-date the modification of this flag. Code coverage reports are monotone, +thus one can take a snapshot when coverage first is enabled, and output the +difference. + +Setting this to `false` prevents this `Debugger` instance from requiring any +code coverage instrumentation, but it does not guarantee that the +instrumentation is not present. + +### `exclusiveDebuggerOnEval` +A boolean value indicating whether other Debugger instances should +have their hook functions called when this instance uses: +* Debugger.Frame.evalWithBindings, +* Debugger.Object.executeInGlobalWithBindings +* Debugger.Object.call +When set to true, other Debugger instances are not notified. + +### `inspectNativeCallArguments` +A boolean value, to be set to true in order to pass the two last +arguments of `onNativeCall` hook. + +### `uncaughtExceptionHook` +Either `null` or a function that SpiderMonkey calls when a call to a +debug event handler, breakpoint handler, or similar +function throws some exception, which we refer to as +<i>debugger-exception</i> here. Exceptions thrown in the debugger are +not propagated to debuggee code; instead, SpiderMonkey calls this +function, passing <i>debugger-exception</i> as its sole argument and +the `Debugger` instance as the `this` value. This function should +return a [resumption value][rv], which determines how the debuggee +should continue. + +If the uncaught exception hook itself throws an exception, +<i>uncaught-hook-exception</i>, SpiderMonkey throws a new error object, +<i>confess-to-debuggee-exception</i>, to the debuggee whose message +blames the debugger, and includes textual descriptions of +<i>uncaught-hook-exception</i> and the original +<i>debugger-exception</i>. + +If `uncaughtExceptionHook`'s value is `null`, SpiderMonkey throws an +exception to the debuggee whose message blames the debugger, and +includes a textual description of <i>debugger-exception</i>. + +Assigning anything other than a callable value or `null` to this +property throws a `TypeError` exception. + +(This is not an ideal way to handle debugger bugs, but the hope here is +that some sort of backstop, even if imperfect, will make life easier for +debugger developers. For example, an uncaught exception hook may have +access to browser-level features like the `alert` function, which this +API's implementation does not, making it possible to present debugger +errors to the developer in a way suited to the context.) + + +## Debugger Handler Functions + +Each `Debugger` instance inherits accessor properties with which you can +store handler functions for SpiderMonkey to call when given events occur +in debuggee code. + +When one of the events described below occurs in debuggee code, the engine +pauses the debuggee and calls the corresponding debugging handler on each +`Debugger` instance that is observing the debuggee. The handler functions +receive the `Debugger` instance as their `this` value. Most handler +functions can return a [resumption value][rv] indicating how the debuggee's +execution should proceed. + +On a new `Debugger` instance, each of these properties is initially +`undefined`. Any value assigned to a debugging handler must be either a +function or `undefined`; otherwise a `TypeError` is thrown. + +Handler functions run in the same thread in which the event occurred. +They run in the compartment to which they belong, not in a debuggee +compartment. + +### `onNewScript(script, global)` +New code, represented by the [`Debugger.Script`][script] instance +<i>script</i>, has been loaded in the scope of the debuggees. + +Since each function has its own [`Debugger.Script`][script], separate from +the top-level code or function that encloses it, loading JavaScript code +typically introduces not just a single script, but a tree of scripts +representing the top-level code and any functions it includes. The +`onNewScript` hook reports only the root script of such a tree. If +necessary, the handler function can use the scripts' `getChildScripts` +method to walk the tree and obtain all the newly introduced scripts. + +This method's return value is ignored. + +### `onNewPromise(promise)` +A new Promise object, referenced by the [`Debugger.Object`][object] instance +*promise*, has been allocated in the scope of the debuggees. The Promise's +allocation stack can be obtained using the *promiseAllocationStack* +accessor property of the [`Debugger.Object`][object] instance *promise*. + +This handler method should return a [resumption value][rv] specifying how +the debuggee's execution should proceed. However, note that a <code>{ +return: <i>value</i> }</code> resumption value is treated like `undefined` +("continue normally"); <i>value</i> is ignored. + +### `onPromiseSettled(promise)` +A Promise object, referenced by the [`Debugger.Object`][object] instance +*promise* that was allocated within a debuggee scope, has settled (either +fulfilled or rejected). The Promise's state, fulfillment or rejection +value, and the allocation and resolution stacks can be obtained using the +Promise-related accessor properties of the [`Debugger.Object`][object] +instance *promise*. + +This handler method should return a [resumption value][rv] specifying how +the debuggee's execution should proceed. However, note that a <code>{ +return: <i>value</i> }</code> resumption value is treated like `undefined` +("continue normally"); <i>value</i> is ignored. + +### `onDebuggerStatement(frame)` +Debuggee code has executed a <i>debugger</i> statement in <i>frame</i>. +This method should return a [resumption value][rv] specifying how the +debuggee's execution should proceed. + +### `onEnterFrame(frame)` +The stack frame <i>frame</i> is about to begin executing code. +(Naturally, <i>frame</i> is currently the youngest +[visible frame][vf].) This method should return +a [resumption value][rv] specifying how the debuggee's execution should +proceed. + +SpiderMonkey only calls `onEnterFrame` to report +[visible][vf], non-`"debugger"` frames. + +### `onNativeCall(callee, reason)` +A call to a native function is being made from a debuggee realm. +<i>callee</i> is a [`Debugger.Object`] for the function being called, and +<i>reason</i> is a string describing the reason the call was made, and +has one of the following values: + +`get`: The native is the getter for a property which is being accessed. +`set`: The native is the setter for a property being written to. +`call`: Any call not fitting into the above categories. + +<i>object</i> is a [`Debugger.Object`][object] reference to the object + onto which the native method is called, if any. +<i>args</i> is a [`Debugger.Object`][object] for the array of arguments +being passed to the native function. + +The two last arguments, <i>object</i> and <i>args</i> are only passed +if `Debugger.inspectNativeCallArguments` is set to true. + +This method should return a [resumption value][rv] specifying how the debuggee's +execution should proceed. If a return value is overridden for a constructor +call, it must be an object. + +SpiderMonkey only calls `onNativeCall` hooks when execution is inside a +debugger evaluation associated with the debugger that has the `onNativeCall` +hook. Such evaluation methods include `Debugger.Object.apply`, `Debugger.Object.call`, +`Debugger.Object.executeInGlobal`, `Debugger.Frame.eval`, and associated methods. + +Separately, any Debugger hooks triggered during calls to `Debugger.Object.apply`, `Debugger.Object.call`, +`Debugger.Object.executeInGlobal`, `Debugger.Frame.eval`, and associated methods +will only be triggered on Debugger objects owned by the Debugger performing +the evaluation. + +### `onExceptionUnwind(frame, value)` +The exception <i>value</i> has been thrown, and has propagated to +<i>frame</i>; <i>frame</i> is the youngest remaining stack frame, and is a +debuggee frame. This method should return a [resumption value][rv] +specifying how the debuggee's execution should proceed. If it returns +`undefined`, the exception continues to propagate as normal: if control in +`frame` is in a `try` block, control jumps to the corresponding `catch` or +`finally` block; otherwise, <i>frame</i> is popped, and the exception +propagates to <i>frame</i>'s caller. + +When an exception's propagation causes control to enter a `finally` +block, the exception is temporarily set aside. If the `finally` block +finishes normally, the exception resumes propagation, and the debugger's +`onExceptionUnwind` handler is called again, in the same frame. (The +other possibility is for the `finally` block to exit due to a `return`, +`continue`, or `break` statement, or a new exception. In those cases the +old exception does not continue to propagate; it is discarded.) + +This handler is not called when unwinding a frame due to an over-recursion +or out-of-memory exception. + +### `sourceHandler(ASuffusionOfYellow)` +This method is never called. If it is ever called, a contradiction has +been proven, and the debugger is free to assume that everything is true. + +### `onError(frame, report)` +SpiderMonkey is about to report an error in <i>frame</i>. <i>Report</i> +is an object describing the error, with the following properties: + +* `message` + The fully formatted error message. + +* `file` + If present, the source file name, URL, etc. (If this property is + present, the <i>line</i> property will be too, and vice versa.) + +* `line` + If present, the source line number at which the error occurred. + +* `lineText` + If present, this is the source code of the offending line. + +* `offset` + The index of the character within lineText at which the error occurred. + +* `warning` + Present and true if this is a warning; absent otherwise. + +* `strict` + Present and true if this error or warning is due to the strict option + (not to be confused with ES strict mode) + +* `exception` + Present and true if an exception will be thrown; absent otherwise. + +* `arguments` + An array of strings, representing the arguments substituted into the + error message. + +This method's return value is ignored. + +### `onNewGlobalObject(global)` +A new global object, <i>global</i>, has been created. + +This handler method should return a [resumption value][rv] specifying how +the debuggee's execution should proceed. However, note that a <code>{ return: +<i>value</i> }</code> resumption value is treated like `undefined` ("continue +normally"); <i>value</i> is ignored. (Allowing the handler to substitute +its own value for the new global object doesn't seem useful.) + +This handler method is only available to debuggers running in privileged +code ("chrome", in Firefox). Most functions provided by this `Debugger` +API observe activity in only those globals that are reachable by the +API's user, thus imposing capability-based restrictions on a +`Debugger`'s reach. However, the `onNewGlobalObject` method allows the +API user to monitor all global object creation that occurs anywhere +within the JavaScript system (the "JSRuntime", in SpiderMonkey terms), +thereby escaping the capability-based limits. For this reason, +`onNewGlobalObject` is only available to privileged code. + +Note that, even though the presence of a `Debugger`'s `onNewGlobalObject` +hook can have arbitrary side effects, the garbage collector does not +consider the presence of the hook sufficient reason to keep the `Debugger` +alive. Thus, the behavior of code that uses `onNewGlobalObject` on unrooted +`Debugger`s may be affected by the garbage collector's activity, and +is not entirely deterministic. + + +## Function Properties of the Debugger Prototype Object + +The functions described below may only be called with a `this` value +referring to a `Debugger` instance; they may not be used as methods of +other kinds of objects. + +### `addDebuggee(global)` +Add the global object designated by <i>global</i> to the set of global +objects this `Debugger` instance is debugging. If the designated global +is already a debuggee, this has no effect. Return this `Debugger`'s +[`Debugger.Object`][object] instance referring to the designated global. + +The value <i>global</i> may be any of the following: + +* A global object. + +* An HTML5 `WindowProxy` object (an "outer window", in Firefox + terminology), which is treated as if the `Window` object of the + browsing context's active document (the "inner window") were passed. + +* A cross-compartment wrapper of an object; we apply the prior rules to + the wrapped object. + +* A [`Debugger.Object`][object] instance belonging to this `Debugger` instance; + we apply the prior rules to the referent. + +* Any other sort of value is treated as a `TypeError`. (Note that each + rule is only applied once in the process of resolving a given + <i>global</i> argument. Thus, for example, a [`Debugger.Object`][object] + referring to a second [`Debugger.Object`][object] which refers to a global does + not designate that global for the purposes of this function.) + +The global designated by <i>global</i> must be in a different +compartment than this `Debugger` instance itself. If adding the +designated global's compartment would create a cycle of debugger and +debuggee compartments, this method throws an error. + +This method returns the [`Debugger.Object`][object] instance whose referent is +the designated global object. + +The `Debugger` instance does not hold a strong reference to its +debuggee globals: if a debuggee global is not otherwise reachable, then +it is dropped from the `Debugger`'s set of debuggees. (Naturally, the +[`Debugger.Object`][object] instance this method returns does hold a strong +reference to the added global.) + +If this debugger is [tracking allocation sites][tracking-allocs] and cannot +track allocation sites for <i>global</i>, this method throws an `Error`. + +### `addAllGlobalsAsDebuggees()` +This method is like [`addDebuggee`][add], but adds all the global +objects from all compartments to this `Debugger` instance's set of +debuggees. Note that it skips this debugger's compartment. + +If this debugger is [tracking allocation sites][tracking-allocs] and cannot +track allocation sites for some global, this method throws an `Error`. +Otherwise this method returns `undefined`. + +This method is only available to debuggers running in privileged +code ("chrome", in Firefox). Most functions provided by this `Debugger` +API observe activity in only those globals that are reachable by the +API's user, thus imposing capability-based restrictions on a +`Debugger`'s reach. However, the `addAllGlobalsAsDebuggees` method +allows the API user to monitor all global object creation that +occurs anywhere within the JavaScript system (the "JSRuntime", in +SpiderMonkey terms), thereby escaping the capability-based +limits. For this reason, `addAllGlobalsAsDebuggees` is only +available to privileged code. + +### `removeDebuggee(global)` +Remove the global object designated by <i>global</i> from this +`Debugger` instance's set of debuggees. Return `undefined`. + +This method interprets <i>global</i> using the same rules that +[`addDebuggee`][add] does. + +Removing a global as a debuggee from this `Debugger` clears all breakpoints +that belong to that `Debugger` in that global. + +### `removeAllDebuggees()` +Remove all the global objects from this `Debugger` instance's set +of debuggees. Return `undefined`. + +### `hasDebuggee(global)` +Return `true` if the global object designated by <i>global</i> is a +debuggee of this `Debugger` instance. + +This method interprets <i>global</i> using the same rules that +[`addDebuggee`][add] does. + +### `getDebuggees()` +Return an array of distinct [`Debugger.Object`][object] instances whose referents +are all the global objects this `Debugger` instance is debugging. + +Since `Debugger` instances don't hold strong references to their +debuggee globals, if a debuggee global is otherwise unreachable, it may +be dropped at any moment from the array this method returns. + +### `getNewestFrame()` +Return a [`Debugger.Frame`][frame] instance referring to the youngest +[visible frame][vf] currently on the calling thread's stack, or `null` +if there are no visible frames on the stack. + +### `findSources()` +Return an array of all [`Debugger.Source`][source] instances of all debuggee +scripts. + +Note that the result may include sources that can no longer ever be +used by the debuggee: say, eval code that has finished running, or +source for unreachable functions. Whether such sources appear can be +affected by the garbage collector's behavior, so this function's result +is not entirely deterministic. + +### `findScripts([query])` +Return an array of [`Debugger.Script`][script] instances for all debuggee scripts +matching <i>query</i>. Each instance appears only once in the array. +<i>Query</i> is an object whose properties restrict which scripts are +returned; a script must meet all the criteria given by <i>query</i> to +be returned. If <i>query</i> is omitted, we return the [`Debugger.Script`][script] +instances for all debuggee scripts. + +<i>Query</i> may have the following properties: + +* `url` + + The script's `url` property must be equal to this value. + +* `source` + + The script's `source` property must be equal to this value. + +* `line` + + The script must at least partially cover the given source line. If this + property is present, the `url` property must be present as well. + +* `innermost` + + If this property is present and true, the script must be the innermost + script covering the given source location; scripts of enclosing code are + omitted. + +* `global` + + The script must be in the scope of the given global object. If this + property's value is a [`Debugger.Object`][object] instance belonging to this + `Debugger` instance, then its referent is used. If the object is not a + global object, then the global in whose scope it was allocated is used. + +All properties of <i>query</i> are optional. Passing an empty object +returns all debuggee code scripts. + +Note that the result may include [`Debugger.Script`][script] instances for +scripts that can no longer ever be used by the debuggee, say, those for +eval code that has finished running, or unreachable functions. Whether +such scripts appear can be affected by the garbage collector's +behavior, so this function's behavior is not entirely deterministic. + +### `findSourceURLs()` + +Return an array of strings containing the URLs of all known sources that +have been created in any debuggee realm. The array will have one entry for +each source, so may have duplicates. The URLs for the realms are +occasionally purged and the returned array might not be complete. + +### `findObjects([query])` +Return an array of [`Debugger.Object`][object] instances referring to each +live object allocated in the scope of the debuggee globals that matches +*query*. Each instance appears only once in the array. *Query* is an object +whose properties restrict which objects are returned; an object must meet +all the criteria given by *query* to be returned. If *query* is omitted, we +return the [`Debugger.Object`][object] instances for all objects allocated +in the scope of debuggee globals. + +The *query* object may have the following properties: + +* `class` + + If present, only return objects whose internal `[[Class]]`'s name + matches the given string. Note that in some cases, the prototype object + for a given constructor has the same `[[Class]]` as the instances that + refer to it, but cannot itself be used as a valid instance of the + class. Code gathering objects by class name may need to examine them + further before trying to use them. + +All properties of *query* are optional. Passing an empty object returns all +objects in debuggee globals. + +Unlike `findScripts`, this function is deterministic and will never return +[`Debugger.Object`s][object] referring to previously unreachable objects +that had not been collected yet. + +### `clearBreakpoint(handler)` +Remove all breakpoints set in this `Debugger` instance that use +<i>handler</i> as their handler. Note that, if breakpoints using other +handler objects are set at the same location(s) as <i>handler</i>, they +remain in place. + +### `clearAllBreakpoints()` +Remove all breakpoints set using this `Debugger` instance. + +### `findAllGlobals()` +Return an array of [`Debugger.Object`][object] instances referring to all the +global objects present in this JavaScript instance. + +The results of this call can be affected in non-deterministic ways by +the details of the JavaScript implementation. The array may include +[`Debugger.Object`][object] instances referring to global objects that are not +actually reachable by the debuggee or any other code in the system. +(Naturally, once the function has returned, the array's +[`Debugger.Object`][object] instances strongly reference the globals they refer +to.) + +This handler method is only available to debuggers running in privileged +code ("chrome", in Firefox). Most functions provided by this `Debugger` +API observe activity in only those globals that are reachable by the +API's user, thus imposing capability-based restrictions on a +`Debugger`'s reach. However, `findAllGlobals` allows the API user to +find all global objects anywhere within the JavaScript system (the +"JSRuntime", in SpiderMonkey terms), thereby escaping the +capability-based limits. For this reason, `findAllGlobals` is only +available to privileged code. + +### `makeGlobalObjectReference(global)` +Return the [`Debugger.Object`][object] whose referent is the global object +designated by <i>global</i>, without adding the designated global as a +debuggee. If <i>global</i> does not designate a global object, throw a +`TypeError`. Determine which global is designated by <i>global</i> +using the same rules as [`Debugger.prototype.addDebuggee`][add]. + +### `adoptDebuggeeValue(value)` +Given a debuggee value `value` owned by an arbitrary `Debugger`, return an +equivalent debuggee value owned by this `Debugger`. + +If `value` is a primitive value, return it unchanged. If `value` is a +`Debugger.Object` owned by an arbitrary `Debugger`, return an equivalent +`Debugger.Object` owned by this `Debugger`. Otherwise, if `value` is some +other kind of object, and hence not a proper debuggee value, throw a +TypeError instead. + +### `adoptFrame(frame)` +Given `frame` of type `Debugger.Frame` which is owned by an arbitrary +`Debugger`, return an equivalent `Debugger.Frame` owned by this `Debugger`. +If the `frame` is associated with a debuggee that is _not_ a debuggee of +the adopting debugger, this method will throw. + +### `adoptSource(source)` +Given `source` of type `Debugger.Source` which is owned by an arbitrary +`Debugger`, return an equivalent `Debugger.Source` owned by this `Debugger`. + +### `enableAsyncStack(global)` +Enable async stack capturing for the realm for the global object designated by +<i>global</i>. + +### `disableAsyncStack(global)` +Disable async stack capturing for the realm for the global object designated by +<i>global</i>. + +### `enableUnlimitedStacksCapturing(global)` +Allow to capture more than 50 stacktraces for the realm for the global object +designated by <i>global</i>, even if it is not a debuggee. + +### `disableUnlimitedStacksCapturing(global)` +Disallow to capture more than 50 stacktraces for the realm for the global object +designated by <i>global</i>, unless it is a debuggee. + +## Static methods of the Debugger Object + +The functions described below are not called with a `this` value. + +### `isCompilableUnit(source)` +: Given a string of source code, designated by <i>source</i>, return false if + the string might become a valid JavaScript statement with the addition of + more lines. Otherwise return true. The intent is to support interactive + compilation - accumulate lines in a buffer until isCompilableUnit is true, + then pass it to the compiler. + +[add]: #adddebuggee-global +[source]: Debugger.Source.md +[script]: Debugger.Script.md +[rv]: Conventions.md#resumption-values +[object]: Debugger.Object.md +[vf]: Debugger.Frame.md#visible-frames +[tracking-allocs]: Debugger.Memory.md#trackingallocationsites +[frame]: Debugger.Frame.md +[object]: Debugger.Object.md diff --git a/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md b/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md new file mode 100644 index 0000000000..635b8eca1b --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md @@ -0,0 +1,226 @@ +# Tutorial: Show Allocations Per Call Path + +This page shows how to use the [`Debugger` API][debugger] to show how many +objects a web page allocates, sorted by the function call path that allocated +them. + +1. Visit the URL `about:config`, and set the `devtools.chrome.enabled` + preference to `true`: + + ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] + +2. Open a developer Scratchpad (Menu button > Developer > Scratchpad), and + select "Browser" from the "Environment" menu. (This menu will not be + present unless you have changed the preference as explained above.) + + ![Selecting the 'browser' context in the Scratchpad][img-scratchpad-browser] + +3. Enter the following code in the Scratchpad: + + ```js + // This simply defines the 'Debugger' constructor in this + // Scratchpad; it doesn't actually start debugging anything. + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + 'resource://gre/modules/jsdebugger.sys.mjs' + ); + addDebuggerToGlobal(window); + + (function () { + // The debugger we'll use to observe a tab's allocation. + var dbg; + + // Start measuring the selected tab's main window's memory + // consumption. This function is available in the browser + // console. + window.demoTrackAllocations = function() { + dbg = new Debugger; + + // This makes hacking on the demo *much* more + // pleasant. + dbg.uncaughtExceptionHook = handleUncaughtException; + + // Find the current tab's main content window. + var w = gBrowser.selectedBrowser.contentWindow; + console.log("Tracking allocations in page: " + + w.location.href); + + // Make that window a debuggee of our Debugger. + dbg.addDebuggee(w.wrappedJSObject); + + // Enable allocation tracking in dbg's debuggees. + dbg.memory.trackingAllocationSites = true; + } + + window.demoPlotAllocations = function() { + // Grab the allocation log. + var log = dbg.memory.drainAllocationsLog(); + + // Neutralize the Debugger, and drop it on the floor + // for the GC to collect. + console.log("Stopping allocation tracking."); + dbg.removeAllDebuggees(); + dbg = undefined; + + // Analyze and display the allocation log. + plot(log); + } + + function handleUncaughtException(ex) { + console.log('Debugger hook threw:'); + console.log(ex.toString()); + console.log('Stack:'); + console.log(ex.stack); + }; + + function plot(log) { + // Given the log, compute a map from allocation sites to + // allocation counts. Note that stack entries are '===' if + // they represent the same site with the same callers. + var counts = new Map; + for (let site of log) { + // This is a kludge, necessary for now. The saved stacks + // are new, and Firefox doesn't yet understand that they + // are safe for chrome code to use, so we must tell it + // so explicitly. + site = Components.utils.waiveXrays(site.frame); + + if (!counts.has(site)) + counts.set(site, 0); + counts.set(site, counts.get(site) + 1); + } + + // Walk from each site that allocated something up to the + // root, computing allocation totals that include + // children. Remember that 'null' is a valid site, + // representing the root. + var totals = new Map; + for (let [site, count] of counts) { + for(;;) { + if (!totals.has(site)) + totals.set(site, 0); + totals.set(site, totals.get(site) + count); + if (!site) + break; + site = site.parent; + } + } + + // Compute parent-to-child links, since saved stack frames + // have only parent links. + var rootChildren = new Map; + function childMapFor(site) { + if (!site) + return rootChildren; + + let parentMap = childMapFor(site.parent); + if (parentMap.has(site)) + return parentMap.get(site); + + var m = new Map; + parentMap.set(site, m); + return m; + } + for (let [site, total] of totals) { + childMapFor(site); + } + + // Print the allocation count for |site|. Print + // |children|'s entries as |site|'s child nodes. Indent + // the whole thing by |indent|. + function walk(site, children, indent) { + var name, place; + if (site) { + name = site.functionDisplayName; + place = ' ' + site.source + ':' + site.line + ':' + site.column; + } else { + name = '(root)'; + place = ''; + } + console.log(indent + totals.get(site) + ': ' + name + place); + for (let [child, grandchildren] of children) + walk(child, grandchildren, indent + ' '); + } + walk(null, rootChildren, ''); + } + })(); + ``` + +4. In the Scratchpad, ensure that no text is selected, and press the "Run" + button. (If you get an error complaining that `Components.utils` is not + defined, be sure you've selected `Browser` from the scratchpad's + `Environment` menu, as described in step 2.) + +5. Save the following HTML text to a file, and visit the file in your browser. + Make sure the current browser tab is displaying this page. + + ```html + <div onclick="doDivsAndSpans()"> + Click here to make the page do some allocations. + </div> + + <script> + function makeFactory(type) { + return function factory(content) { + var elt = document.createElement(type); + elt.textContent = content; + return elt; + }; + } + + var divFactory = makeFactory('div'); + var spanFactory = makeFactory('span'); + + function divsAndSpans() { + for (i = 0; i < 10; i++) { + var div = divFactory('div #' + i); + div.appendChild(spanFactory('span #' + i)); + document.body.appendChild(div); + } + } + + function doDivsAndSpans() { divsAndSpans(); } + </script> + ``` + +6. Open the browser console (Menu Button > Developer > Browser Console), and + then evaluate the expression `demoTrackAllocations()` in the browser + console. This begins logging allocations in the current browser tab. + +7. In the browser tab, click on the text that says "Click here...". The event + handler should add some text to the end of the page. + +8. Back in the browser console, evaluate the expression + `demoPlotAllocations()`. This stops logging allocations, and displays a tree + of allocations: + + ![An allocation plot, displayed in the console][img-alloc-plot] + + The numbers at the left edge of each line show the total number of objects + allocated at that site or at sites called from there. After the count, we + see the function name, and the source code location of the call site or + allocation. + + The `(root)` node's count includes objects allocated in the content page by + the web browser, like DOM events. Indeed, this display shows that + `popup.xml` and `content.js`, which are internal components of Firefox, + allocated more objects in the page's compartment than the page itself. (We + will probably revise the allocation log to present such allocations in a way + that is more informative, and that exposes less of Firefox's internal + structure.) + + As expected, the `onclick` handler is responsible for all allocation done by + the page's own code. (The line number for the onclick handler is `1`, + indicating that the allocating call is located on line one of the handler + text itself. We will probably change this to be the line number within + `page.html`, not the line number within the handler code.) + + The `onclick` handler calls `doDivsAndSpans`, which calls `divsAndSpans`, + which invokes closures of `factory` to do all the actual allocation. (It is + unclear why `spanFactory` allocated thirteen objects, despite being called + only ten times.) + + +[debugger]: Debugger-API.md +[img-chrome-pref]: enable-chrome-devtools.png +[img-scratchpad-browser]: scratchpad-browser-environment.png +[img-alloc-plot]: alloc-plot-console.png diff --git a/js/src/doc/Debugger/Tutorial-Breakpoint.md b/js/src/doc/Debugger/Tutorial-Breakpoint.md new file mode 100644 index 0000000000..014cf947aa --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Breakpoint.md @@ -0,0 +1,130 @@ +Tutorial: Set a breakpoint using `Debugger` +=========================================== + +This page shows how you can try out the [`Debugger` API][debugger] yourself +using Firefox's Scratchpad. We use `Debugger` to set a breakpoint in a function, +and then evaluate an expression whenever it is hit. + +This tutorial was tested against Firefox 58 Beta and Nightly. It does not work in Firefox 57. + +1. Since the `Debugger` API is only available to privileged JavaScript code, + you'll need to use the Browser Content Toolbox to try it out. To do this, + open the Firefox developer tools, click on the options gear at the upper + right of the toolbox, and make sure that both “Enable browser chrome and + add-on debugging toolboxes” and “Enable remote debugging” are checked. These + are located at the bottom right of the options panel; you may need to scroll + to see them. Once they're checked, you can close the developer tools. + + +2. Save the following text to an HTML file: + + ```html + <div onclick="report('the best div');">Click me!</div> + <div onclick="report('another great div');">Or me!</div> + <script> + function report(what) { + console.log('clicked: ' + what); + } + </script> + ``` + +3. Visit the HTML file in your browser, and open the Browser Content Toolbox by + opening the Firefox menu, choosing “Browser Tools”, and then “Browser + Content Toolbox”. If that item doesn't appear in the “Browser Tools” menu, + make sure you checked both boxes to enable the Browser Content Toolbox as + explained in Step 1. + +4. Our example code is long enough that the best way to run it is to use the + Scratchpad panel, which is not enabled by default. To enable it, click on + the options gear at the upper right of the Browser Content Toolbox, and make + sure the “Scratchpad” box in the “Default Developer Tools” section the left + is checked. The Scratchpad panel should appear at the top of the Toolbox + alongside the Console, Debugger, and Memory panels. + +5. Click on the Scratchpad panel and enter the following code: + + ```js + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + "resource://gre/modules/jsdebugger.sys.mjs" + ); + + // This simply defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + addDebuggerToGlobal(globalThis); + + // Create a 'Debugger' instance. + var dbg = new Debugger; + + // Make the tab's top window a debuggee, and get a + // Debugger.Object referring to the window. + var windowDO = dbg.addDebuggee(tabs[0].content); + + // Get a Debugger.Object referring to the window's `report` + // function. + var reportDO = windowDO.getOwnPropertyDescriptor('report').value; + + // Set a breakpoint at the entry point of `report`. + reportDO.script.setBreakpoint(0, { + hit: function (frame) { + console.log('hit breakpoint in ' + frame.callee.name); + console.log('what = ' + frame.eval('what').return); + } + }); + + console.log('Finished setting breakpoint!'); + ``` + +6. In the Scratchpad, ensure that no text is selected, and press the "Run" + button. + + Now, click on the text that says "Click me!" in the web page. + This runs the `div` element's `onclick` handler. + When control reaches the start of the `report` function, + `Debugger` calls the breakpoint handler's `hit` method, + passing a `Debugger.Frame` instance. + The `hit` method logs the breakpoint hit to the browser content toolbox's console. + Then it evaluates the expression `what` in the given stack frame, and logs its result. + The toolbox's console now looks like this: + + ![The breakpoint handler's console output][img-example-console] + <!-- HTML comment prevents caption, which MDN styles poorly --> + + You can also click on the text that says “Or me!”, to see `report` called from a + different handler. + + If `Debugger` is unable to find the `report` function, or the console output + does not appear, evaluate the expression `tabs[0].content.document.location` + in the console to make sure that `tabs[0]` indeed refers to the HTML file you + visited. If you have more than one tab visiting a `file:` URL, they all share + a single content process, so you may need to use a different element of the + array as the debuggee. + +7. Press "Run" in the Scratchpad again. Now, clicking on "Click me!" causes the + breakpoint hit to be logged twice---one for each `Debugger` instance. + + Multiple `Debugger` instances can observe the same debuggee. Re-running the code + in the Scratchpad creates a fresh `Debugger` instance, adds the same web page as + its debuggee, and then sets a new breakpoint. When you click on the `div` + element, both `Debugger`s' breakpoints are hit, and both handlers run. + + This shows how any number of `Debugger`-based tools can observe a single web + page simultaneously. In fact, you can use the Browser Content Toolbox's Debugger + panel to set its own breakpoint in `report`, and it will trigger along with the + first two. Keep in mind, however, that when multiple Debuggers share a debuggee, + the order in which their handlers run is not specified. If more than one tool + tries to influence the debuggee's behavior, their combined behavior could be + unpredictable. + +8. Close the web page and the Browser Content Toolbox. + + Since both the Scratchpad's global object and the debuggee window are + now gone, the `Debugger` instances will be garbage collected, since + they can no longer have any visible effect on Firefox's behavior. The + `Debugger` API tries to interact with garbage collection as + transparently as possible; for example, if both a `Debugger.Object` + instance and its referent are not reachable, they will both be + collected, even while the `Debugger` instance to which the shadow + belonged continues to exist. + +[debugger]: Debugger-API.md +[img-example-console]: console.png diff --git a/js/src/doc/Debugger/Tutorial-Debugger-Statement.md b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md new file mode 100644 index 0000000000..ebac35e445 --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md @@ -0,0 +1,94 @@ +Tutorial: Evaluate an Expression When a debugger; Statement Is Executed +======================================================================= + +**NOTE: This tutorial no longer works in current versions of Firefox.** +Instead, please try the updated and expanded [breakpoint tutorial][tut breakpoint]. + +This page shows how you can try out the [`Debugger` API][debugger] yourself +using Firefox's Scratchpad. We use the API to evaluate an expression in the web +page whenever it executes a JavaScript `debugger;` statement. + +1. Visit the URL `about:config`, and set the `devtools.chrome.enabled` + preference to `true`: + + ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] + +2. Save the following HTML text to a file, and visit the file in your + browser: + + ```html + <div onclick="var x = 'snoo'; debugger;">Click me!</div> + ``` + +3. Open a developer Scratchpad (Menu button > Developer > Scratchpad), and + select "Browser" from the "Environment" menu. (This menu will not be + present unless you have changed the preference as explained above.) + + ![Selecting the 'browser' context in the Scratchpad][img-scratchpad-browser] + +4. Enter the following code in the Scratchpad: + + ```js + // This simply defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + const { addDebuggerToGlobal } = ChromeUtils.importESModule( + "resource://gre/modules/jsdebugger.sys.mjs" + ); + addDebuggerToGlobal(window); + + // Create a 'Debugger' instance. + var dbg = new Debugger; + + // Get the current tab's content window, and make it a debuggee. + var w = gBrowser.selectedBrowser.contentWindow.wrappedJSObject; + dbg.addDebuggee(w); + + // When the debuggee executes a 'debugger' statement, evaluate + // the expression 'x' in that stack frame, and show its value. + dbg.onDebuggerStatement = function (frame) { + alert('hit debugger statement; x = ' + frame.eval('x').return); + } + ``` + +5. In the Scratchpad, ensure that no text is selected, and press the "Run" + button. + +6. Now, click on the text that says "Click me!" in the web page. This runs + the `div` element's `onclick` handler. When control reaches the + `debugger;` statement, `Debugger` calls your callback function, passing + a `Debugger.Frame` instance. Your callback function evaluates the + expression `x` in the given stack frame, and displays the alert: + + ![The Debugger callback displaying an alert][img-example-alert] + +7. Press "Run" in the Scratchpad again. Now, clicking on the "Click me!" + text causes *two* alerts to show---one for each `Debugger` + instance. + + Multiple `Debugger` instances can observe the same debuggee. Re-running + the code in the Scratchpad created a fresh `Debugger` instance, added + the same web page as its debuggee, and then registered a fresh + `debugger;` statement handler with the new instance. When you clicked + on the `div` element, both of them ran. This shows how any number of + `Debugger`-based tools can observe a single web page + simultaneously---although, since the order in which their handlers + run is not specified, such tools should probably only observe, and not + influence, the debuggee's behavior. + +8. Close the web page and the Scratchpad. + + Since both the Scratchpad's global object and the debuggee window are + now gone, the `Debugger` instances will be garbage collected, since + they can no longer have any visible effect on Firefox's behavior. The + `Debugger` API tries to interact with garbage collection as + transparently as possible; for example, if both a `Debugger.Object` + instance and its referent are not reachable, they will both be + collected, even while the `Debugger` instance to which the shadow + belonged continues to exist. + +[tut breakpoint]: Tutorial-Breakpoint.md +[debugger]: Debugger-API.md + +[img-chrome-pref]: enable-chrome-devtools.png +[img-scratchpad-browser]: scratchpad-browser-environment.png +[img-example-alert]: debugger-alert.png diff --git a/js/src/doc/Debugger/alloc-plot-console.png b/js/src/doc/Debugger/alloc-plot-console.png Binary files differnew file mode 100644 index 0000000000..5411724724 --- /dev/null +++ b/js/src/doc/Debugger/alloc-plot-console.png diff --git a/js/src/doc/Debugger/console.png b/js/src/doc/Debugger/console.png Binary files differnew file mode 100644 index 0000000000..9e9ce4ae74 --- /dev/null +++ b/js/src/doc/Debugger/console.png diff --git a/js/src/doc/Debugger/debugger-alert.png b/js/src/doc/Debugger/debugger-alert.png Binary files differnew file mode 100644 index 0000000000..2bf9362243 --- /dev/null +++ b/js/src/doc/Debugger/debugger-alert.png diff --git a/js/src/doc/Debugger/enable-chrome-devtools.png b/js/src/doc/Debugger/enable-chrome-devtools.png Binary files differnew file mode 100644 index 0000000000..033468991f --- /dev/null +++ b/js/src/doc/Debugger/enable-chrome-devtools.png diff --git a/js/src/doc/Debugger/index.rst b/js/src/doc/Debugger/index.rst new file mode 100644 index 0000000000..35ed8fa10f --- /dev/null +++ b/js/src/doc/Debugger/index.rst @@ -0,0 +1,20 @@ +===================== +Debugger API +===================== + +.. toctree:: + :maxdepth: 4 + + Debugger-API.md + Debugger.md + Debugger.Object.md + Debugger.Script.md + Debugger.Source.md + Debugger.Environment.md + Debugger.Frame.md + Debugger.Memory.md + Conventions.md + + Tutorial-Alloc-Log-Tree.md + Tutorial-Breakpoint.md + Tutorial-Debugger-Statement.md diff --git a/js/src/doc/Debugger/scratchpad-browser-environment.png b/js/src/doc/Debugger/scratchpad-browser-environment.png Binary files differnew file mode 100644 index 0000000000..534d0f9500 --- /dev/null +++ b/js/src/doc/Debugger/scratchpad-browser-environment.png diff --git a/js/src/doc/Debugger/shadows.svg b/js/src/doc/Debugger/shadows.svg new file mode 100644 index 0000000000..44bceddb4c --- /dev/null +++ b/js/src/doc/Debugger/shadows.svg @@ -0,0 +1,1000 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="650" + height="400" + id="svg10302" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="shadows.svg" + viewBox="0 0 650 400"> + <defs + id="defs10304"> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-0" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-3" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-2" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-6" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-6" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-3" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-7" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-9" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-4" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-4" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-4" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-79" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-8" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-11" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-9" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-0" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-0" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-96" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-2" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-4" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-72" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-1" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-8" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-67" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-5" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-78" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-89" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-6" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-5" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-3" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-5" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + <marker + inkscape:stockid="DotS" + orient="auto" + refY="0" + refX="0" + id="DotS-1-8" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3941-63-18" + d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none;marker-end:none" + transform="matrix(0.2,0,0,0.2,1.48,0.2)" /> + </marker> + <marker + inkscape:stockid="Arrow1Send" + orient="auto" + refY="0" + refX="0" + id="Arrow1Send-8-35" + style="overflow:visible"> + <path + inkscape:connector-curvature="0" + id="path3888-6-2" + d="M 0,0 5,-5 -12.5,0 5,5 0,0 z" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;marker-start:none" + transform="matrix(-0.2,0,0,-0.2,-1.2,0)" /> + </marker> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="1.6369231" + inkscape:cx="338.70355" + inkscape:cy="200" + inkscape:document-units="px" + inkscape:current-layer="alertLater" + showgrid="false" + showguides="true" + inkscape:guide-bbox="true" + inkscape:window-width="1920" + inkscape:window-height="1021" + inkscape:window-x="0" + inkscape:window-y="867" + inkscape:window-maximized="1" /> + <metadata + id="metadata10307"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:groupmode="layer" + id="debuggeeset" + inkscape:label="debuggeeset" + style="display:inline" + class="flip"> + <rect + style="fill:#c8c0c0;fill-opacity:1;stroke:#000000;stroke-width:2.18226266;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4.36452564, 4.36452564;stroke-dashoffset:0;display:inline" + id="rect3755-3-0-6-1-8-6" + width="171.31711" + height="150.68491" + x="0.10979491" + y="248.55035" + ry="4.814189" + rx="4.0350175" /> + <text + xml:space="preserve" + style="font-size:14.11999989px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="9.8749857" + y="268.84415" + id="text22690" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan22692" + x="9.8749857" + y="268.84415">Debugger</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="shadows" + inkscape:label="shadows" + style="display:inline" + class="flip"> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-68" + width="119.42149" + height="42.397526" + x="25.236485" + y="160.0201" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-8" + width="177.65483" + height="46.614746" + x="209.36084" + y="336.41965" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-43" + width="149.12187" + height="46.614746" + x="237.8938" + y="287.1636" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-6-1" + width="119.42149" + height="42.397526" + x="192.17702" + y="161.09923" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-2-4" + width="136.4054" + height="46.614731" + x="26.006214" + y="336.41965" + ry="11.065418" + rx="11.065418" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-1-5-9" + width="242.77243" + height="75.739441" + x="183.31783" + y="47.05508" + ry="6.1249413" + rx="6.1249394" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:#ffffff;stroke-width:2.92859435;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + id="rect3755-1-5-1-7-2" + width="186.27605" + height="18.674528" + x="285.59784" + y="66.98394" + ry="6.1249413" + rx="6.1249399" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-0" + width="194.03011" + height="46.614746" + x="413.20236" + y="336.41989" + ry="4.0393105" + rx="4.0393105" /> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-4-6" + width="90.035484" + height="46.614746" + x="296.98013" + y="237.91023" + ry="4.0393105" + rx="4.0393105" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="33.257587" + y="378.20139" + id="text4037" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4039" + x="33.257587" + y="378.20139">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="218.72287" + y="377.52206" + id="text4041" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4043" + x="218.72287" + y="377.52206">Debugger.Environment</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="421.84915" + y="377.52206" + id="text4045" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4047" + x="421.84915" + y="377.52206">Debugger.Frame</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="29.181461" + y="196.81334" + id="text4049" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4051" + x="29.181461" + y="196.81334">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="199.2216" + y="197.89247" + id="text4053" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4055" + x="199.2216" + y="197.89247">Debugger.Object</tspan></text> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="190.1888" + y="118.00808" + id="text4057" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan4059" + x="190.1888" + y="118.00808">Debugger.Script</tspan></text> + <rect + style="fill:#969696;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-5-8" + width="194.03011" + height="46.614746" + x="413.20236" + y="287.1636" + ry="4.0393105" + rx="4.0393105" /> + </g> + <g + inkscape:groupmode="layer" + id="global" + inkscape:label="global" + transform="translate(0,-344.09448)" + class="flip" + style="display:inline"> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1" + width="177.9265" + height="46.614887" + x="201.82011" + y="664.15881" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="210.6344" + y="691.88666" + id="text10970" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan10972" + x="210.6344" + y="691.88666">global environment</tspan></text> + <rect + style="fill:#83d4ff;fill-opacity:1;stroke:none" + id="rect3755-5-2" + width="136.61398" + height="46.614872" + x="18.810663" + y="664.15881" + ry="11.065418" + rx="11.065418" /> + <path + style="fill:none;stroke:#000000;stroke-width:2.99999976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8)" + d="m 204.0527,687.46628 -46.26685,0" + id="path11163-2" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="3.020442" + y="659.44391" + id="text15699" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan15701" + x="3.020442" + y="659.44391">global object:</tspan></text> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="87.051254" + y="698.41443" + id="text15703" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + x="87.051254" + y="698.41443" + id="tspan15707">Date; Math; ...</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="scripts" + inkscape:label="scripts" + style="display:inline" + class="flip"> + <rect + transform="translate(0,-652.36218)" + style="fill:#83ff9a;fill-opacity:1;stroke:none;display:inline" + id="rect3755-1-5" + width="243.14369" + height="75.73967" + x="176.36284" + y="683.0611" + ry="6.1249409" + rx="6.1249394" + class="nonvalue" /> + <rect + transform="translate(0,-652.36218)" + style="fill:#83ff9a;fill-opacity:1;stroke:#e6e4dd;stroke-width:2.92859435000000001;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + id="rect3755-1-5-1-7" + width="186.56091" + height="18.674585" + x="278.79929" + y="702.98999" + ry="6.1249409" + rx="6.1249399" + class="nonvalue" /> + </g> + <g + inkscape:groupmode="layer" + id="code" + inkscape:label="code" + class="flip" + style="display:inline"> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="182.99957" + y="699.52692" + id="text10314" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923777,0,-652.36218)"><tspan + sodipodi:role="line" + x="182.99957" + y="699.52692" + id="tspan4518">function alertLater(msg, delay) {</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="717.1795" + id="tspan4527"> setTimeout( function () { alert(msg); },</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="734.83215" + id="tspan4529"> delay);</tspan><tspan + sodipodi:role="line" + x="182.99957" + y="752.48474" + id="tspan4531">}</tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="alertLater" + inkscape:label="alertLater" + style="display:inline" + class="flip"> + <rect + transform="translate(0,-652.36218)" + style="fill:#83d4ff;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5" + width="119.60412" + height="42.397655" + x="18.039759" + y="796.02643" + ry="11.065418" + rx="11.065418" /> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="98.427589" + y="811.1579" + id="text3761-6" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan3763-0" + x="98.427589" + y="811.1579">[[Code]]:</tspan></text> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="98.427589" + y="831.64917" + id="text3765-0" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan3767-2" + x="98.427589" + y="831.64917">[[Scope]]:</tspan></text> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#e6e4dd;stroke-width:2.95684338000000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + d="m 18.039736,817.89404 119.601824,0" + id="path3773-4-3" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="2.4066608" + y="791.50812" + id="text10404" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + id="tspan10406" + x="2.4066608" + y="791.50812">alertLater:</tspan></text> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 113.60688,806.63271 c 0,-45.02374 23.466,-83.54273 59.2075,-83.54273" + id="path3868-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + transform="translate(0,-652.36218)" + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 113.60688,827.22105 c 51.73315,0 90.50566,99.84829 90.50566,141.32477" + id="path3868-4-0" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <text + xml:space="preserve" + style="font-size:14.12208748px;font-style:normal;font-weight:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="87.051254" + y="991.61841" + id="text15703-4" + sodipodi:linespacing="125%" + transform="matrix(1.0007628,0,0,0.99923778,0,-652.36218)"><tspan + sodipodi:role="line" + x="89.299202" + y="991.61841" + id="tspan15707-0">alertLater; </tspan></text> + </g> + <g + inkscape:groupmode="layer" + id="call1env" + inkscape:label="call1env" + style="display:inline" + class="flip"> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3" + width="149.34991" + height="46.614887" + x="229.7888" + y="270.80811" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <g + style="display:inline" + id="g3550" + transform="matrix(0.58886915,0,0,0.58797179,4.5262952,-176.54364)"> + <text + transform="scale(1.0009935,0.9990075)" + sodipodi:linespacing="125%" + id="text13153" + y="795.71594" + x="499.133" + style="font-size:22px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="795.71594" + x="499.133" + id="tspan13155" + sodipodi:role="line">msg:</tspan><tspan + y="823.21594" + x="499.133" + sodipodi:role="line" + id="tspan3538">delay:</tspan></text> + <text + sodipodi:linespacing="125%" + id="text3540" + y="794.92615" + x="507" + style="font-size:22px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + xml:space="preserve"><tspan + y="794.92615" + x="507" + id="tspan3542" + sodipodi:role="line">'xlerb'</tspan><tspan + id="tspan3544" + y="822.42615" + x="507" + sodipodi:role="line">1000</tspan></text> + </g> + </g> + <g + inkscape:groupmode="layer" + id="anon" + inkscape:label="anon" + style="display:inline" + class="flip"> + <rect + style="fill:#83d4ff;fill-opacity:1;stroke:none;display:inline" + id="rect3755-5-6" + width="119.60412" + height="42.397655" + x="185.84294" + y="143.66425" + ry="11.065418" + rx="11.065418" /> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="266.10284" + y="158.2981" + id="text3761-6-0" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan3763-0-7" + x="266.10284" + y="158.2981">[[Code]]:</tspan></text> + <text + xml:space="preserve" + style="font-size:13.91893291px;font-style:normal;font-weight:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="266.10284" + y="178.78937" + id="text3765-0-7" + sodipodi:linespacing="125%" + transform="scale(1.0007628,0.99923778)"><tspan + sodipodi:role="line" + id="tspan3767-2-6" + x="266.10284" + y="178.78937">[[Scope]]:</tspan></text> + <path + style="fill:none;stroke:#e6e4dd;stroke-width:2.95684338000000002;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" + d="m 185.84293,165.53187 119.60182,0" + id="path3773-4-3-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 285.44496,176.59077 c 0,31.25836 -38.08897,59.76616 -38.08897,91.05227" + id="path3868-4-0-4-7" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS);marker-end:url(#Arrow1Send);display:inline" + d="m 285.95088,154.47813 c 165.68371,0 170.77148,-32.93057 158.20494,-82.18402" + id="path3868-4-4" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> + <g + inkscape:groupmode="layer" + id="fire" + inkscape:label="fire" + style="display:inline" + class="flip"> + <rect + style="fill:#ff9457;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-4" + width="194.03011" + height="46.614746" + x="406.38477" + y="320.06448" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="434.10962" + y="346.93411" + id="text10970-7-4" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + x="434.10962" + y="346.93411" + id="tspan3534-4"><tspan + style="font-style:italic;-inkscape-font-specification:Sans Italic" + id="tspan5754">anonymous</tspan>()</tspan></text> + <rect + style="fill:#ffd257;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-3-4" + width="90.035484" + height="46.614746" + x="289.10324" + y="221.55482" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:italic;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans;-inkscape-font-specification:Sans Italic" + x="312.99686" + y="248.05814" + id="text5774" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan5776" + x="312.99686" + y="248.05814">empty</tspan></text> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8);display:inline" + d="m 415.85979,342.35006 c -51.09645,0 11.10573,-95.59507 -39.0289,-95.59507" + id="path11163-1" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + style="fill:none;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:url(#DotS-1);marker-end:url(#Arrow1Send-8);display:inline" + d="m 588.68444,343.33325 c 66.19678,0 27.60009,-283.14206 -117.22439,-283.14206" + id="path11163-1-6" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> + <g + inkscape:groupmode="layer" + id="call2" + inkscape:label="call2" + class="flip" + style="display:inline"> + <rect + style="fill:#ff9457;fill-opacity:1;stroke:none;display:inline" + id="rect3755-3-0-6-1-1-5-4" + width="194.03011" + height="46.614746" + x="406.38477" + y="270.80817" + ry="4.0393105" + rx="4.0393105" + class="nonvalue" /> + <text + xml:space="preserve" + style="font-size:12.93533993px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;display:inline;font-family:Sans" + x="433.86328" + y="298.87103" + id="text6389" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan6391" + x="433.86328" + y="298.87103">alert('xlerb')</tspan></text> + </g> +</svg> diff --git a/js/src/doc/HazardAnalysis/CFG.md b/js/src/doc/HazardAnalysis/CFG.md new file mode 100644 index 0000000000..1038e9f350 --- /dev/null +++ b/js/src/doc/HazardAnalysis/CFG.md @@ -0,0 +1,347 @@ +# sixgill CFG format + +The main output of the sixgill plugin is what is loosely labeled a control flow graph (CFG) associated with each function compiled. +These are stored in the file src_body.xdb, which contains a mapping from function names ("mangled\$unmangled") to function data. + +The graph is really a set of directed acyclic data flow graphs, stitched together via "loops" that imply back edges in the control flow graph. + +Function data is an array of "bodies", one body for the toplevel code in the function, and another body for each loop. A body is _not_ a basic block, since they can contain interior branches. (The nodes in a body do not necessarily dominate the following nodes.) A body is a DAG, and thus has no back edges or cross edges. Flow starts only at the entry point and ends only at the exit point, though (1) a loop body's entry point implicitly follows its exit point and (2) `Call` nodes will cause the actual program counter to go to another (possibly recursive) body. A body really describes data flow, not dynamic control flow. + +## Function Body + +A body (whether toplevel or loop) contains: + +- .BlockId + - `.Kind`: "Function" for the toplevel function or "Loop" for a (possibly nested) loop within it. + - `.Loop`: if .Kind == "Loop", then a string identifier distinguishing the loop, in the format "loop#n" where n is the index of the loop in the body. Nested loops will extend this to "loop#n#m". + - `.Variable`: + - `.Kind`: "Func" + - `.Name[]`: the function `Name` (see below) +- `.Version`: always zero +- `.Command`: the command used to compile this function, if recorded. This command will _not_ include the -fplugin parameters. +- `.Location[]`: a length-2 array of the source positions of the first and last line of the function definition. Hopefully it will be in the same file. Note that this Location is different from a `PPoint.Location` (see below), which will have a single source position. Each source position is: + - `.CacheString`: the filename + - `.Line`: the line number +- `.DefineVariable[]`: a list of variables defined in the body. The first one is for the function itself. Each variable has: + - `.Type`: the type of the variable. See `Type`, below. + - `.Variable`: + - `.Kind`: one of + - "Func" for the function itself + - "This" for the C++ `this` parameter + - "Arg" for parameters + - "Temp" for temporaries + - `.Name[]`: the variable `Name` (see below) +- `.Index[]`: a 2-tuple of the first and last index in the body. +- `.PPoint[]`: the filename and line number of each point in the body + - `.Location`: a single source point (see above). +- `.PEdge[]`: the bulk of the body. See Edges, below. +- `.LoopIsomorphic[]`: a list of `{"Index": point}` points in the body that are cloned in loop bodies. See the edge Kind `Loop`, below. + +A loop body (a body with BlockId.Kind == "Loop") will additionally have: + +- `.BlockPPoint`: an array of full references to points within parent bodies that represent the entry point of this loop. Each has: + - `.BlockId`: the BlockId of the parent body + - `.Index`: the index of the point within the parent body + - `.Version`: the value zero, intended for incremental analyses but unused in the GC hazard analysis. + +Note that a loop may appear in more than one parent body. I believe this will not be used for regular structured code, but could be necessary to properly disentangle loops when using `goto`. + +`Name`: a 2-tuple containing a variable or function name. The first element is a raw, internal name, and the second is a more user-facing name. For non-functions, both elements are normally the same, but `.Name[0]` could have a `:<n>` suffix if there are multiple variables of that name in different scopes within the same function, or a `<file>:` prefix for static variables. For functions, `.Name[0]` is the full name of the function (in format "mangled\$unmangled") and .Name[1] is the base name of the function (unqualified, with no type or parameters): + + "Variable": { + "Kind": "Func", + "Name": [ + "_Z12refptr_test9v$Cell* refptr_test9()", + "refptr_test9" + ] + } + +Bodies are an array of "edges" between "points". All behavior is described as happening on these edges. `body.Index[0]` gives the first point in the body. Each edge has a source and destination point. So eg if `body.Index[0]` is 1, then (unless the body is empty) there will be at least one edge with `edge.Index = [1, 2]`. The code `if (C) { x = 1; } else { x = 2; }; f();`, will have two edges sharing a common destination: + + Assume(1,2, C*, true) + Assign(2,4, x := 1) + Assume(1,3, C*, false) + Assign(3,4, x := 2) + Call(4,5, f()) + +Note that the above syntax is part of the default output of `xdbfind src_body.xdb <functionName>`. It is a much-simplified version of the full JSON output from `xdbfind -json src_body.xdb <functionName>`. It will be used in this document to describe examples because the JSON output is much too verbose. + +Every body is a directed acyclic graph (DAG), stored as a set of edges with source,destination point tuples. Any cycles in the original flow graph are replaced with Loop edges (see below). + +## Edges + +The edges are stored in an array named `PEdge`, with properties: + +- `.Index[]`: a 2-tuple giving the source and destination points. +- `.Kind`: One of 7 different Kinds. The rest of the attributes will depend on this Kind. + +Sixgill boils the control flow graph down to a small set of edge Kinds: + +### Assign + +- `.Exp[]`: a 2-tuple of [lhs, rhs] of the assignment, each an expression (see `Expressions`, below.) +- `.Type`: the overall type of the expression, which I believe is the type of the lhs? (See `Types`, below.) + +Note that `Call` is also used for assignments, when the result of the function call is being assigned to a variable. + +### Call + +- `.Exp[0]`: an expression representing the function being called (the "callee"). The callee might be a simple function, in which case `exp.Kind == "Var"`. Or it could be a computed function pointer or whatever. The expression evaluates to the function being called. +- `.Exp[1]` (optional): where to assign the return value. +- `.PEdgeCallArguments[]`: an array of expressions, one for each argument being passed. This does not include the `this` argument. +- `.PEdgeCallInstance`: the expression for the object to call the method on, which will be passed as the `this` argument. + +### Assume + +The destination of an `Assume` node can rely on the given value assumption, eg `Assume(1,2, __temp_1* == 7)` means that `__temp_1` will be 7 at point 2. + +A conditional branch will be represented as a pair of `Assume` edges coming off of the expression for the branch condition. These edges produce a data flow graph where you can know the value of a variable if it has passed through an `Assume` edge (at least, until it reaches an `Assign` or `Call` edge.) + +- `.Exp`: the expression being tested. +- `.PEdgeAssumeNonZero`: if present, this will be set to true, and means we are on the edge where `Exp` is `!= 0`. If this is not present, then `Exp` is `0`. + +Example: the C++ function body + + SomeRAIIType raii; + if (flipcoin()) { + return 1; + } else { + return 2; + } + +could produce something like: + + Call(3,4, __temp_1 := flipcoin()) + Assume(4,5, __temp_1*, true) + Assume(4,6, __temp_1*, false) + Assign(5,7, return := 1) + Assign(6,7, return := 2) + Call(7,8, raii.~__dt_comp ()) + +### Loop + +The edge corresponds to an entire loop. The meaning of a "loop" is subtle. It is mainly what is required to convert a general graph into a set of acyclic DAGs by finding back edges, and creating a "loop body" from the subgraph between the entry point (the destination of the back edge) and the source of the back edge. (Multiple back edges with a common destination will be a single loop.) Only the main body nodes that are necessary for (postdominated by) one of the back edges will be removed. Shared nodes will be cloned and will appear in both the main body and the loop body. The cloned nodes are described as "isomorphic". + +- `.BlockId` : the `BlockId` of the loop body. +- `.Loop` : an id like "loop#0" that will match up with the .BlockId.Loop property of the corresponding loop body. + +Example: consider the C++ code + + float testfunc(int val) { + int x = val; + x++; + loophead: + int y = x + 2; + if (y == 8) goto loophead; + y++; + if (y == 10) return 2.4; + if (y == 12) goto loophead; + return 3.6; + } + +This will produce the loop body: + + block: float32 testfunc(int32):loop#0 + parent: float32 testfunc(int32):3 + pentry: 1 + pexit: 6 + Assign(1,2, y := (x* + 2)) + Assume(2,6, (y* == 8), true) /* 6 is the exit point, so loops back to the entry point 1 */ + Assume(2,3, (y* == 8), false) + Assign(3,4, y := (y* + 1)) + Assume(4,5, (y* == 10), false) + Assume(5,6, (y* == 12), true) /* 6 is the exit point, so loops back to the entry point 1 */ + +and the main body: + + block: float32 testfunc(int32) + pentry: 1 + pexit: 11 + isomorphic: [4,5,6,7,9] + Assign(1,2, x := val*) + Assign(2,3, x := (x* + 1)) + Loop(3,4, loop#0) + Assign(4,5, y := (x* + 2)) /* edge is also in the loop */ + Assume(5,6, (y* == 8), false) /* edge is also in the loop */ + Assign(6,7, y := (y* + 1)) /* edge is also in the loop */ + Assume(7,8, (y* == 10), true) + Assume(7,9, (y* == 10), false) /* edge is also in the loop */ + Assign(8,11, return := 2.4) + Assume(9,10, (y* == 12), false) + Assign(10,11, return := 3.6) + +The isomorphic points correspond to the C++ code: + + y = x + 2; + if (y == 8) /* when y != 8 */ + y++; + if (y == 10) /* when y != 10 */ + +which is the code that will execute in order to reach the post-loop edge `Assume(9,10, (y* == 12), false)`. (If point 9 in the main body is reached and y _is_ equal to 12, then the `Assume(9,10,...)` edge will not be taken. Point 9 in the main body corresponds to point 5 in the loop body, so the edge `Assume(5,6, (y* == 12), true)` will be taken instead.) When "control flow" is at an isomorphic point, it can be considered to be at all "instantiations" of that point at the same time. Really, though, these are acyclic data flow graphs where a loop's exit point is externally known to flow into the entry point, and the main body lacks any `Assume` or other back edges that would make it cyclic. + +For a `while` loop, the isomorphic points will evaluate the conditional expression. + +Another example: the C++ code + + void testfunc() { + static Cell cell; + RefPtr<float> v10; + v10.assign_with_AddRef(&somefloat); + while (flipcoin()) { + v10.forget(); + } + } + +generates + + block: void testfunc():loop#0 + parent: void testfunc():3 + pentry: 1 + pexit: 4 + Call(1,2, __temp_1 := flipcoin()) + Assume(2,3, __temp_1*, true) + Call(3,4, v10.forget()) + + block: void testfunc() + pentry: 1 + pexit: 7 + isomorphic: [3,4] + Call(1,2, v10.assign_with_AddRef(somefloat)) + Loop(2,3, loop#0) + Call(3,4, __temp_1 := flipcoin()) + Assume(4,5, __temp_1*, false) + Call(5,6, v10.~__dt_comp ()) + +The first block is the loop body, the second is the main body. Points 3 and 4 of the main body are equivalent to points 1 and 2 of the loop body. Notice the "parent" field of the loop body, which gives the equivalent point (3) of the loop's entry point in the body main. + +### Assembly + +An opaque wad of assembly code. + +### Annotation + +I'm not sure if I've seen these? They might be for the old annotation mechanism. + +### Skip + +These appear to be internal "epsilon" edges to simplify graph building and loop splitting. They are removed before the final CFG is emitted. + +## Expressions + +Expressions are the bulk of the CFG. + +- `.Width` (optional) : width in bits. I'm not sure when this is used. It is much more common for a Type to have a width. +- `.Unsigned` (optional) : boolean saying that this expression is unsigned. +- `.Kind` : one of the following values + +### Program lvalues + +- "Empty" : used in limited contexts when nothing is needed. +- "Var" : expression referring to a variable + - `.Type` +- "Drf" : dereference (as in, \*foo or foo->... or something implicit) + - `.Exp[0]` : target being dereferenced + - `.Type` +- "Fld" + - `.Exp[0]` : target object containing the field + - `.Field` + - `.Name[]` : 2-tuple of [qualified name, unqualified name] + - can be unnamed, in which case the name will be "field:<number>". This is used for base classes. + - `.FieldCSU` : type of the CSU that the field is a member of + - `.Type` : type of the field + - `.FieldInstanceFunction` : "whether this is a virtual instance function rather than data field of the containing CSU". Presence or absence is what matters. All examples I have seen are for pure virtual functions (`virtual void foo() = 0`). + - `.Annotation[]` : any annotations on the specific field +- "Rfld" : ? some kind of "reverse" field access + - same children as Fld +- "Index" : array element access + - `.Exp[0]` : the target array + - `.Index` : the index being accessed (an Exp) + - `.Type` : the type of the element +- "String" : string constant + - `.Type` : the type of the string + - `.Count` : number of elements (chars) in the string + - `.String` : the actual data in the string +- "Clobber" : "additional lvalue generated by the memory model" (?) + - callee + - overwrite + - optional value kind + - point + - optional location + +### Program rvalues + +- "Int", "Float" : constant values + - `.String` : the string form of the value (this is the only way the value is stored) +- "Unop", "Binop" : operators + - `.OpCode` : the various opcodes + - `.Exp[0]` and `.Exp[1]` (the latter for Binop only) : parameters + - stride type (optional) + +### Expression modifiers + +- "Exit", "Initial" : ? + - `.Exp[0]` : target expression + - value kind (optional) +- "Val" : ? + - lvalue + - value kind (optional) + - index (body point) + - boolean saying whether it is relative (?) +- "Frame" : (unused) + +### Immutable properties + +These appear to be synthetic properties intended for the built-in analyses that we are not using. + +- "NullTest" : ? + - `.Exp[0]` : target being tested +- "Bound" : ? appears to be bounds-checked index access + - bound kind + - stride type + - `.Exp[0]` (optional) : target that the bound applies to +- "Directive" : ? + - directive kind + +### Mutable properties + +These appear to be synthetic properties intended for the built-in analyses that we are not using. + +- "Terminate" + - stride type + - terminate test (Exp) + - terminate int (Exp) + - `.Exp[0]` (optional) : target +- "GCSafe" : (unused) + - `.Exp[0]` (optional) : target + +## Types + +- `.Kind` : the kind of type being described, one of: + +Possible Type Kinds: + +- "Void" : the C/C++ void type +- "Int" + - `.Width` : width in bits + - `.Sign` (optional) : whether the type is signed + - `.Variant` (optional) : ? +- "Float" + - `.Width` : width in bits +- "Pointer" : pointer or reference type + - `.Width` : width in bits + - `.Reference` : 0 for pointer, 1 for regular reference, 2 for rvalue reference + - `.Type` : type of the target +- "Array" + - `.Type` : type of the elements + - `.Count` : number of elements, given as a plain constant integer +- "CSU" : class, structured, or union + - `.Name` : qualified name, as a plain string +- "Function" + - `.TypeFunctionCSU` (optional) : if present, the type of the CSU containing the function + - `.FunctionVarArgs` (?) (optional) : if this is present, the function is varargs (eg f(...)) + - `.TypeFunctionArgument` : array of argument types. Present if at least one parameter. + - `.Type` : type of argument + - `.Annotation` (optional) : any explicit annotations (**attribute**((foo))) for this parameter + - `.Variable` : the variable representing the function + - `.Annotation` (optional) : any explicit annotation for this function +- "Error" : there was an error handling this type in sixgill. Probably something unimplemented. diff --git a/js/src/doc/HazardAnalysis/index.md b/js/src/doc/HazardAnalysis/index.md new file mode 100644 index 0000000000..813369404d --- /dev/null +++ b/js/src/doc/HazardAnalysis/index.md @@ -0,0 +1,100 @@ +# Static Analysis for Rooting and Heap Write Hazards + +Treeherder can run two static analysis builds: the full browser (linux64-haz), just the JS shell (linux64-shell-haz). They show up on treeherder as `H` and `SM(H)`. + +## Diagnosing a hazard failure + +The first step is to look at what sort of hazard is being reported. There are two types that cause the job to fail: stack rooting hazards for garbage collection, and heap write thread safety hazards for stylo. + +The summary output will include either the string `<N> rooting hazards detected` or `<N> heap write hazards detected out of <M> allowed`. See the appropriate section below for each. + +## Diagnosing a rooting hazards failure + +Click on the `H` build link, select the "Artifacts" pane on the bottom left, and download the `public/build/hazards.txt.gz` and `public/build/hazards.html.gz` files. The HTML file is most useful when running the analysis locally, since it will link to the exact parts of the code in question, but it's easier to talk about the text file here. + +Example snippet from `hazards.txt`: + + Function 'jsopcode.cpp:uint8 DecompileExpressionFromStack(JSContext*, int32, int32, class JS::Handle<JS::Value>, int8**)' has unrooted 'ed' of type 'ExpressionDecompiler' live across GC call 'uint8 ExpressionDecompiler::decompilePC(uint8*)' at js/src/jsopcode.cpp:1866 + js/src/jsopcode.cpp:1866: Assume(74,75, !__temp_23*, true) + js/src/jsopcode.cpp:1867: Assign(75,76, return := 0) + js/src/jsopcode.cpp:1867: Call(76,77, ed.~ExpressionDecompiler()) + GC Function: uint8 ExpressionDecompiler::decompilePC(uint8*) + JSString* js::ValueToSource(JSContext*, class JS::Handle<JS::Value>) + uint8 js::Invoke(JSContext*, JS::Value*, JS::Value*, uint32, JS::Value*, class JS::MutableHandle<JS::Value>) + uint8 js::Invoke(JSContext*, JS::CallArgs, uint32) + JSScript* JSFunction::getOrCreateScript(JSContext*) + uint8 JSFunction::createScriptForLazilyInterpretedFunction(JSContext*, class JS::Handle<JSFunction*>) + uint8 JSRuntime::cloneSelfHostedFunctionScript(JSContext*, class JS::Handle<js::PropertyName*>, class JS::Handle<JSFunction*>) + JSScript* js::CloneScript(JSContext*, class JS::Handle<JSObject*>, class JS::Handle<JSFunction*>, const class JS::Handle<JSScript*>, uint32) + JSObject* js::CloneStaticBlockObject(JSContext*, class JS::Handle<JSObject*>, class JS::Handle<js::StaticBlockObject*>) + js::StaticBlockObject* js::StaticBlockObject::create(js::ExclusiveContext*) + js::Shape* js::EmptyShape::getInitialShape(js::ExclusiveContext*, js::Class*, js::TaggedProto, JSObject*, JSObject*, uint32, uint32) + js::Shape* js::EmptyShape::getInitialShape(js::ExclusiveContext*, js::Class*, js::TaggedProto, JSObject*, JSObject*, uint64, uint32) + js::UnownedBaseShape* js::BaseShape::getUnowned(js::ExclusiveContext*, js::StackBaseShape*) + js::BaseShape* js_NewGCBaseShape(js::ThreadSafeContext*) [with js::AllowGC allowGC = (js::AllowGC)1u] + js::BaseShape* js::gc::NewGCThing(js::ThreadSafeContext*, uint32, uint64, uint32) [with T = js::BaseShape; js::AllowGC allowGC = (js::AllowGC)1u; size_t = long unsigned int] + void js::gc::RunDebugGC(JSContext*) + void js::MinorGC(JSRuntime*, uint32) + GC + +This means that a rooting hazard was discovered at `js/src/jsopcode.cpp` line 1866, in the function `DecompileExpressionFromStack` (it is prefixed with the filename because it's a static function.) The problem is that there is an unrooted variable `ed` that holds an `ExpressionDecompiler` live across a call to `decompilePC`. "Live" means that the variable is used after the call to `decompilePC` returns. `decompilePC` may trigger a GC according to the static call stack given starting from the line beginning with "`GC Function:`". + +The hazard itself has some barely comprehensible `Assume(...)` and `Call(...)` [gibberish][CFG] that describes the exact data flow path of the variable into the function call. That stuff is rarely useful -- usually, you'll only need to look at it if it's complaining about a temporary and you want to know where the temporary came from. The type `ExpressionDecompiler` is believed to hold pointers to GC-controlled objects of some sort. The analysis currently does not describe the exact field it is worried about. + +To unpack this a little, the analysis is saying the following can happen: + +* `ExpressionDecompiler` contains some pointer to a GC thing. For example, it might have a field `obj` of type `JSObject*`. (There is a `gcTypes.txt` file inside `hazardIntermediates.tar.xz` that will give the detailed explanation for all types.) +* `DecompileExpressionFromStack` is called. +* A pointer is stored in that field of the `ed` variable. +* `decompilePC` is invoked, which calls `ValueToSource`, which calls `Invoke`, which eventually calls `js::MinorGC` +* During the resulting garbage collection, the object pointed to by `ed.obj` is moved to a different location. All pointers stored in the JS heap are updated automatically, as are all rooted pointers. `ed.obj` is not, because the GC doesn't know about it. +* After `decompilePC` returns, something accesses `ed.obj`. This is now a stale pointer, and may refer to just about anything -- the wrong object, an invalid object, or whatever. As TeX would say, **badness 10000**. + +## Diagnosing a heap write hazard failure + +OBSOLETE: The heap write hazard analysis has not been updated in years and is looking for things that no longer exist, and therefore will always report zero problems. + +For the thread unsafe heap write analysis, a hazard means that some Gecko_* function calls, directly or indirectly, code that writes to something on the heap, or calls an unknown function that *might* write to something on the heap. The analysis requires quite a few annotations to describe things that are actually safe. This section will be expanded as we gain more experience with the analysis, but here are some common issues: + +* Adding a new Gecko_* function: often, you will need to annotate any outparams or owned (thread-local) parameters in the `treatAsSafeArgument` function in `js/src/devtools/rootAnalysis/analyzeHeapWrites.js`. +* Calling some libc function: if you add a call to some random libc function (eg `sin()` or `floor()` or `ceil()`, though the latter two are already annotated), the analysis will report an "External Function". Add it to `checkExternalFunction`, assuming it *doesn't* have the possibility of writing to shared heap memory. +* If you call some non-returning (crashing) function that the analysis doesn't know about, you'll need to add it to `ignoreContents`. + +On the other hand, you might have a real thread safety issue on your hands. Shared caches are common problems. Fix it. + +## Analysis implementation + +These builds do the following: + +* set up a build environment and run the analysis within it, then upload the resulting files + * compile an optimized JS shell to later run the analysis + * compile the browser with gcc, using a slightly modified version of the sixgill (http://svn.sixgill.org) gcc plugin +* produce a set of `.xdb` files describing everything encountered during the compilation +* analyze the `.xdb` files with scripts in `js/src/devtools/rootAnalysis` + +The format of the information stored in those files is [somewhat documented][CFG]. + +## Running the analysis + +### Pushing to try + +The easiest way to run an analysis is to push to try with `mach try fuzzy -q "'haz"` (or, if the hazards of interest are contained entirely within `js/src`, use `mach try fuzzy -q "'shell-haz"` for a much faster result). The expected turnaround time for linux64-haz is just under 1.5 hours (~20 minutes for `hazard-linux64-shell-haz`). + +The output will be uploaded and an output file `hazards.txt.xz` will be placed into the "Artifacts" info pane on treeherder. + +### Running locally + +The rooting [hazard analysis may be run][running] using mach. + +## So you broke the analysis by adding a hazard. Now what? + +Backout, fix the hazard, or (final resort) update the expected number of hazards in `js/src/devtools/rootAnalysis/expect.browser.json` (but don't do that). + +The most common way to fix a hazard is to change the variable to be a `Rooted` type, as described in [RootingAPI.h][rooting] + +For more complicated cases, ask on the Matrix channel (see [spidermonkey.dev][spidermonkey] for contact info). If you don't get a response, ping sfink or jonco for rooting hazards, bholley or sfink for heap write hazards. + +[running]: running.md +[rooting]: https://searchfox.org/mozilla-central/source/js/public/RootingAPI.h +[spidermonkey]: https://spidermonkey.dev/ +[CFG]: CFG.md diff --git a/js/src/doc/HazardAnalysis/running.md b/js/src/doc/HazardAnalysis/running.md new file mode 100644 index 0000000000..4de0696986 --- /dev/null +++ b/js/src/doc/HazardAnalysis/running.md @@ -0,0 +1,124 @@ +# Running the Rooting Hazard Analysis + +The `js/src/devtools/rootAnalysis` directory contains scripts for running Brian +Hackett's static GC rooting and thread heap write safety analyses on a JS +source directory. + +To run the analysis on SpiderMonkey: + +1. Unset your $MOZCONFIG + + unset MOZCONFIG + +2. Install prerequisites. + + mach hazards bootstrap + +3. Build the shell to run the analysis. + + mach hazards build-shell + +4. Compile all the code to gather info. + + mach hazards gather --project=js + +5. Analyze the gathered info. + + mach hazards analyze --project=js + +Output goes to `$srctop/haz-js/hazards.txt`. This will run the analysis on the js/src +tree only; if you wish to analyze the full browser, use + + --project=browser + +(or leave it off; `--project=browser` is the default) + +6. (optional) View the resulting hazards. + + mach hazards view --project=js + +After running the analysis once, you can reuse the `*.xdb` database files +generated, using modified analysis scripts, by running either the `mach hazards +analyze` command above, or by adding on `mach hazards analyze <step>` to +run a subset of the analysis steps; `mach hazards analyze -- --list` to see +step names. + +Also, you can pass `-- -v` to get exact command lines to cut & paste for running +the various stages, which is helpful for running under a debugger. + +## Incremental Analyses + +Once you have an analysis, you can make code changes and rebuild with `mach hazards gather`. This will add to the existing `*.xdb` files, which will *usually* work ok, but sometimes older compilations will have left around information that will get in the way. A typical example is with lambda functions: you may get hazards reported due to lambdas that no longer exist, but the newer compile will not replace them. Although this could be fixed with some amount of effort, you're fighting against something of a fundamental problem where the analysis is depending on certain things *NOT* happening (eg calls to the GC) and incremental compilation only adds and replaces existing information. It does not remove information unless it is replacing it with something of a matching name (and things like lambdas have autogenerated numbers in their names that vary between compiles.) + +In short: for development speed, feel free to use incremental analyses but don't trust them. If the hazard analysis starts claiming the impossible is happening, try `mach hazards clobber` and do a full rebuild. + +## Overview of what is going on here + +So what does this actually do? + +1. It downloads a GCC compiler and plugin ("sixgill") from Mozilla servers. + +2. It runs `run_complete`, a script that builds the target codebase with the + downloaded GCC, generating a few database files containing control flow + graphs of the full compile, along with type information etc. + +3. Then it runs `analyze.py`, a Python script, which runs all the scripts + which actually perform the analysis -- the tricky parts. + (Those scripts are written in JS.) + +The easiest way to get this running is to not try to do the instrumented +compilation locally. Instead, grab the relevant files from a try server push +and analyze them locally. + +## Local Analysis of Downloaded Intermediate Files + +Another useful path is to let the continuous integration system do the hard +work of generating the intermediate files and analyze them locally. This is +particularly useful if you are working on the analysis itself. + +* Do a try push with "--upload-xdbs" appended to the try: ..." line. + + mach try fuzzy -q "'haz" --upload-xdbs + +* Create an empty directory to run the analysis. + +* When the try job is complete, download the resulting `src_body.xdb.bz2`, +`src_comp.xdb.bz2`, and `file_source.xdb.bz2` files into your directory. + +* Fetch a compiler and sixgill plugin to use: + + mach hazards bootstrap + +If you are on osx, these will not be available. Instead, build sixgill manually +(these directions are a little stale): + + hg clone https://hg.mozilla.org/users/sfink_mozilla.com/sixgill + cd sixgill + CC=$HOME/.mozbuild/hazard-tools/gcc/bin/gcc ./release.sh --build # This will fail horribly. + make bin/xdb.so CXX=clang++ + +* Build an optimized JS shell with ctypes. Note that this does not need to +match the source you are analyzing in any way; in fact, you pretty much never +need to update this once you've built it. (Though I reserve the right to use +any new JS features implemented in Spidermonkey in the future...) + + mach hazards build-shell + +The shell will be placed by default in `$topsrcdir/obj-haz-shell`. + +* Make a defaults.py file containing the following, with your own paths filled in: + + js = "<objdir>/dist/bin/js" + sixgill_bin = "<sixgill-dir>/bin" + +* For the rooting analysis, run + + python <srcdir>/js/src/devtools/rootAnalysis/analyze.py gcTypes + +* For the heap write analysis, run + + python <srcdir>/js/src/devtools/rootAnalysis/analyze.py heapwrites + +Also, you may wish to run with -v (aka --verbose) to see the exact commands +executed that you can cut & paste if needed. (I use them to run under the JS +debugger when I'm working on the analysis.) diff --git a/js/src/doc/MIR-optimizations/index.md b/js/src/doc/MIR-optimizations/index.md new file mode 100644 index 0000000000..32c38bd36a --- /dev/null +++ b/js/src/doc/MIR-optimizations/index.md @@ -0,0 +1,97 @@ +# MIR optimizations from a thousand feet + +MIR is the intermediate representation (`IR`) used in Ion, SpiderMonkey's optimizing compiler backend. MIR is generated by [WarpBuilder](https://hacks.mozilla.org/2020/11/warp-improved-js-performance-in-firefox-83/), then optimized by a succession of passes. (MIR is also used to compile WebAssembly code, but this document is focused on JavaScript compilation). + +This is a quick summary of all the MIR passes as of Feb 2021. The italicized passes are classic optimizations that are likely to be extensively covered in a compiler textbook. Non-italicized passes are either JS-specific, or too trivial to cover. + +The state of the MIR after each of these passes can be visualized using [iongraph](https://github.com/sstangl/iongraph). + +## *BuildSSA* +[Single Static Assignment](https://en.wikipedia.org/wiki/Static_single_assignment_form) is a form of IR in which every value is defined exactly once. It has several nice properties for optimization. Note: SSA is why we have phi nodes. + +## Prune Unused Branches +What it says on the tin: prunes away branches that are never taken. + +## Fold Empty Blocks +A simple cleanup pass to get rid of empty blocks with one predecessor and one successor by folding them into their successor. + +## Fold Tests +Simplifies the code generated for conditional operations. [See the comment here](https://searchfox.org/mozilla-central/rev/bd92b9b4a3c2ff022e830c1358968a84e6e69c95/js/src/jit/IonAnalysis.cpp#849-871). + +## Split Critical Edges +In subsequent passes, we may choose to move code around. In preparation, this pass adds empty blocks along [critical edges](https://en.wikipedia.org/wiki/Control-flow_graph#Special_edges), so that we have a safe place to put those instructions. + +## Renumber Blocks +This pass literally just reassigns block numbers. + +## Eliminate Phis +After some of the above optimizations, some of our phi nodes may be things like `b = phi(a,a)`, which is redundant. This pass cleans those up. + +## *Scalar Replacement* +If a function allocates and uses an object, but we can [prove that the object never escapes the function](https://en.wikipedia.org/wiki/Escape_analysis), then we can sometimes avoid the allocation entirely by tracking each of the object’s components (fields in C/C++; slots/elements in JS) individually. + +## Apply types +Each type of MIR node has a [TypePolicy](https://searchfox.org/mozilla-central/rev/fd853f4aea89186efdb368e759a71b7a90c2b89c/js/src/jit/TypePolicy.h#23-35) defining what type of input it accepts. If necessary, this pass inserts (potentially fallible) conversions to guarantee that the types work out. + +## *Alias Analysis* +[Alias analysis](https://en.wikipedia.org/wiki/Alias_analysis) determines whether two instructions may use/modify the same memory. If they do, then they can not be reordered with respect to each other, because that could change the semantics of the program. + +## *GVN* +[Global Value Numbering](https://en.wikipedia.org/wiki/Value_numbering) is a classic optimization for finding places where we compute the same value multiple times, and eliminating the redundancy. + +## *LICM* +[Loop-Invariant Code Motion](https://en.wikipedia.org/wiki/Loop-invariant_code_motion) finds instructions in a loop that will compute the same value every time and hoists them out of the loop. + +## Beta +This is done in preparation for range analysis. This particular approach to range analysis is [taken from a paper by Gough and Klaren](https://searchfox.org/mozilla-central/rev/fd853f4aea89186efdb368e759a71b7a90c2b89c/js/src/jit/RangeAnalysis.cpp#49-108). + +## *Range Analysis* +[A classic optimization](https://en.wikipedia.org/wiki/Value_range_analysis) that determines the possible range of values a definition can take on. Used to implement many of the following passes. + +## De-Beta +Remove beta nodes now that we don’t need them. + +## RA Check UCE +Check to see if range analysis has made any code eligible for Unreachable Code Elimination. + +## Truncate Doubles +Strength-reduce double arithmetic to integer arithmetic if range analysis says it’s okay. + +## Sink +If we compute a value that will only be used along some paths, and we could recover the value if one of the other paths bailed out, then we can postpone the computation of that variable until we are sure we will need it. [More details here](https://bugzilla.mozilla.org/show_bug.cgi?id=1093674). + +## Remove Unnecessary Bitops +Remove bit-wise arithmetic operators that don’t do anything (like `x | 0` on integer input). + +## Fold Linear Arithmetic Constants +Fold `a + constant1 + constant2` into `a + (constant1+constant2)`. + +## Effective Address Analysis +This was added to ensure that we generated good code for memory accesses in asm.js. + +## *DCE* +[Dead code elimination](https://en.wikipedia.org/wiki/Dead_code_elimination) removes instructions whose results are never needed. + +## *Reordering* +Shuffle instructions around within a block to reduce the lifetime of intermediate values and reduce register pressure. This is a relatively simple version of [instruction scheduling](https://en.wikipedia.org/wiki/Instruction_scheduling). + +## Make loops contiguous +Reorder blocks so that all the blocks in a loop are generated in one contiguous chunk, which is good for cache locality. + +## Edge Case Analysis (Late) +A place to check for edge cases after code has stopped being moved around. Currently used for checking whether some instructions need to handle negative zero. + +## *Bounds Check Elimination* +[A classic optimization](https://en.wikipedia.org/wiki/Bounds-checking_elimination) that eliminates bounds checks if we can prove at compile time that they can’t fail. + +## FoldLoadsWithUnbox +Loading a NaN-boxed value and then unboxing it can be slightly more efficient if we do both operations at once. + +## Add KeepAlive Instructions +While we access the slots or elements of an object, we have to ensure that the object itself is not collected by the GC. See [bug 1160884](https://bugzilla.mozilla.org/show_bug.cgi?id=1160884). + +## Generate LIR +After optimization is over, we lower our IR from MIR to LIR to do register allocation and code generation. + +## *Allocate Registers* +Programs can have way more variables than hardware has registers, so we have to decide which values live in which registers when. [This is a very well studied area](https://en.wikipedia.org/wiki/Register_allocation). diff --git a/js/src/doc/SavedFrame/index.md b/js/src/doc/SavedFrame/index.md new file mode 100644 index 0000000000..cdd4643a64 --- /dev/null +++ b/js/src/doc/SavedFrame/index.md @@ -0,0 +1,95 @@ +# 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. + +```text +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. + +### `sourceId` +The process-unique internal integer ID of this source. Usable to match up +a SavedFrame with a [Debugger.Source][dbg-source] using its `id` property. + +### `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. + +[dbg-source]: ../Debugger/Debugger.Source.md diff --git a/js/src/doc/build.rst b/js/src/doc/build.rst new file mode 100644 index 0000000000..7237c80dff --- /dev/null +++ b/js/src/doc/build.rst @@ -0,0 +1,247 @@ +Building and testing SpiderMonkey +================================= + +**The first step is to run our “bootstrap” script to help ensure you have the +right build tools for your operating system. This will also help you get a copy +of the source code. You do not need to run the “mach build” command just yet +though.** + +* :ref:`Building Firefox On Linux` +* :ref:`Building Firefox On Windows` +* :ref:`Building Firefox On MacOS` + +This guide shows you how to build SpiderMonkey using ``mach``, which is +Mozilla's multipurpose build tool. This replaces old guides that advised +running the "configure" script directly. + +These instructions assume you have a clone of `mozilla-unified` and are +interested in building the JS shell. + +Developer (debug) build +~~~~~~~~~~~~~~~~~~~~~~~ + +For developing and debugging SpiderMonkey itself, it is best to have +both a debug build (for everyday debugging) and an optimized build (for +performance testing), in separate build directories. We'll start by +covering how to create a debug build. + +Setting up a MOZCONFIG +----------------------- + +First, we will create a ``MOZCONFIG`` file. This file describes the characteristics +of the build you'd like `mach` to create. Since it is likely you will have a +couple of ``MOZCONFIGs``, a directory like ``$HOME/mozconfigs`` is a useful thing to +have. + +A basic ``MOZCONFIG`` file for doing a debug build, put into ``$HOME/mozconfigs/debug`` looks like this + +.. code:: text + + # Build only the JS shell + ac_add_options --enable-project=js + + # Enable the debugging tools: Assertions, debug only code etc. + ac_add_options --enable-debug + + # Enable optimizations as well so that the test suite runs much faster. If + # you are having trouble using a debugger, you should disable optimization. + ac_add_options --enable-optimize + + # Use a dedicated objdir for SpiderMonkey debug builds to avoid + # conflicting with Firefox build with default configuration. + mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-debug-@CONFIG_GUESS@ + +To activate a particular ``MOZCONFIG``, set the environment variable: + +.. code:: text + + export MOZCONFIG=$HOME/mozconfigs/debug + +Building +-------- + +Once you have activated a ``MOZCONFIG`` by setting the environment variable +you can then ask ``mach``, located in the top directory of your checkout, +to do your build: + +.. code:: console + + $ cd <path to mozilla-central> + $ ./mach build + +.. note:: + + If you are on Mac and baldrdash fails to compile with something similar to + + :: + + /usr/local/Cellar/llvm/7.0.1/lib/clang/7.0.1/include/inttypes.h:30:15: fatal error: 'inttypes.h' file not found + + This is because, starting from Mojave, headers are no longer + installed in ``/usr/include``. Refer the `release + notes <https://developer.apple.com/documentation/xcode_release_notes/xcode_10_release_notes>`__ under + Command Line Tools -> New Features + + The release notes also states that this compatibility package will no longer be provided in the near + future, so the build system on macOS will have to be adapted to look for headers in the SDK + + Until then, the following should help, + + :: + + open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pk + +Once you have successfully built the shell, you can run it using ``mach run``. + +Testing +~~~~~~~ + +Once built, you can then use ``mach`` to run the ``jit-tests``: + +.. code:: console + + $ ./mach jit-test + +Similarly you can use also run ``jstests``. These include a local, +intermittently updated, copy of all `test262 <https://github.com/tc39/test262/>`_ +tests. + +.. code:: console + + $ ./mach jstests + +See :doc:`Running Automated JavaScript Tests<test>` for more details. + +Optimized Builds +~~~~~~~~~~~~~~~~ + +To switch to an optimized build, such as for performance testing, one need only +have an optimized build ``MOZCONFIG``, and then activate it. An example +``$HOME/mozconfigs/optimized`` ``MOZCONFIG`` looks like this: + +.. code:: text + + # Build only the JS shell + ac_add_options --enable-project=js + + # Enable optimization for speed + ac_add_options --enable-optimize + + # Disable debug checks to better match a release build of Firefox. + ac_add_options --disable-debug + + # Use a separate objdir for optimized builds to allow easy + # switching between optimized and debug builds while developing. + mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-opt-@CONFIG_GUESS@ + +SpiderMonkey on Android aarch64 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Building SpiderMonkey on Android +-------------------------------- + +- First, run `mach bootstrap` and answer `GeckoView/Firefox for Android` when + asked which project you want to build. This will download a recent Android + NDK, make sure all the build dependencies required to compile on Android are + present, etc. +- Make sure that `$MOZBUILD_DIR/android-sdk-linux/platform-tools` is present in + your `PATH` environment. You can do this by running the following line in a + shell, or adding it to a shell profile init file: + +.. code:: console + + $ export PATH="$PATH:~/.mozbuild/android-sdk-linux/platform-tools" + +- Create a typical `mozconfig` file for compiling SpiderMonkey, as outlined in + the :ref:`Setting up a MOZCONFIG` documentation, and include the following + line: + +.. code:: console + + $ ac_add_options --target=aarch64-linux-android + +- Then compile as usual with `mach build` with this `MOZCONFIG` file. + +Running jit-tests on Android +---------------------------- + +- Plug your Android device to the machine which compiled the shell for aarch64 + as described above, or make sure it is on the same subnetwork as the host. It + should appear in the list of devices seen by `adb`: + +.. code:: console + + $ adb devices + +This command should show you a device ID with the name of the device. If it +doesn't, make sure that you have enabled Developer options on your device, as +well as `enabled USB debugging on the device <https://developer.android.com/studio/debug/dev-options>`_. + +- Run `mach jit-test --remote {JIT_TEST_ARGS}` with the android-aarch64 + `MOZCONFIG` file. This will upload the JS shell and its dependencies to the + Android device, in a temporary directory (`/data/local/tmp/test_root/bin` as + of 2020-09-02). Then it will start running the jit-test suite. + +Debugging jit-tests on Android +------------------------------ + +Debugging on Android uses the GDB remote debugging protocol, so we'll set up a +GDB server on the Android device, that is going to be controlled remotely by +the host machine. + +- Upload the `gdbserver` precompiled binary from the NDK from the host machine + to the Android device, using this command on the host: + +.. code:: console + + $ adb push \ + ~/.mozbuild/android-ndk-r23c/prebuilt/android-arm64/gdbserver/gdbserver \ + /data/local/tmp/test_root/bin + +- Make sure that the `ncurses5` library is installed on the host. On + Debian-like distros, this can be done with `sudo apt install -y libncurses5`. + +- Set up port forwarding for the GDB port, from the Android device to the host, + so we can connect to a local port from the host, without needing to find what + the IP address of the Android device is: + +.. code:: console + + $ adb forward tcp:5039 tcp:5039 + +- Start `gdbserver` on the phone, passing the JS shell command line arguments + to gdbserver: + +.. code:: console + + $ adb shell export LD_LIBRARY_PATH=/data/local/tmp/test_root/bin '&&' /data/local/tmp/test_root/bin/gdbserver :5039 /data/local/tmp/test_root/bin/js /path/to/test.js + +.. note:: + + Note this will make the gdbserver listen on the 5039 port on all the + network interfaces. In particular, the gdbserver will be reachable from + every other devices on the same networks as your phone. Since the gdbserver + protocol is unsafe, it is strongly recommended to double-check that the + gdbserver process has properly terminated when exiting the shell, and to + not run it more than needed. + +.. note:: + + You can find the full command line that the `jit_test.py` script is + using by giving it the `-s` parameter, and copy/paste it as the final + argument to the gdbserver invocation above. + +- On the host, start the precompiled NDK version of GDB that matches your host + architecture, passing it the path to the shell compiled with `mach` above: + +.. code:: console + + $ ~/.mozbuild/android-ndk-r23c/prebuilt/linux-x86_64/bin/gdb /path/to/objdir-aarch64-linux-android/dist/bin/js + +- Then connect remotely to the GDB server that's listening on the Android + device: + +.. code:: console + + $(gdb) target remote :5039 + $(gdb) continue diff --git a/js/src/doc/bytecode_checklist.md b/js/src/doc/bytecode_checklist.md new file mode 100644 index 0000000000..be3c1011a4 --- /dev/null +++ b/js/src/doc/bytecode_checklist.md @@ -0,0 +1,44 @@ +# So You Want to Add a Bytecode Op + +Occasionally we need to add a new bytecode operation in order to express or optimize +some feature of JavaScript. This document is intended to assist you in determining if +you ought to add a bytecode op, and what kind of changes and integrations will be +required if you do this. + +Think of this as a more specialized version of the feature addition document. + +## First: Do you need a new bytecode op? + +There are alternatives to increasing the bytecode space! + +* [Self-hosted intrinsics][intrinsic] can be accessed directly from the bytecode + generator using the `GetIntrinsic` bytecode. For an example see + [`BytecodeEmitter::emitCopyDataProperties`][emitCopy]. Calls to intrinsics can be + [(optionally) optimized with CacheIR as well][optimize]. +* Desugar the behavior you want into a sequence of already existing ops. If this is + possible this is often the right choice: Support for everything comes along mostly + for free. + * Is it possible to teach the JIT compilers to recognize your sequence of bytecode + if special handling is required? If that kind of idiom recognition is too costly, + you may be better served by a new bytecode op. + + +## Second: Can we make your bytecode fast? + +To implement bytecode in our JIT compilers we put a few constraints on our bytecode. +e.g. Each bytecode must be atomic, to support bailouts and exception behaviour. This +means that your op must not create torn objects or invalid states. + +Ideally there would be a fast inline cache possible to implement your bytecode, as +this brings speed and performance to our BaselineInterpreter layer, rather than +waiting for Warp. + +ICs are valuable when behaviour varies substantially at runtime (ie, we choose +different paths based on operand types). Examples of where we've used this are +`JSOp::OptimizeSpreadCall` and `JSOp::CloseIter`, both of which let us generate a +fastpath in the common case or fall back to something more expensive if necessary. + + +[intrinsic]: https://searchfox.org/mozilla-central/search?q=intrinsic_&path=js%2Fsrc%2F&case=false®exp=false +[emitCopy]: https://searchfox.org/mozilla-central/rev/650c19c96529eb28d081062c1ca274bc50ef3635/js/src/frontend/BytecodeEmitter.cpp#5018,5027,5039,5045,5050,5055,5067 +[optimize]: https://searchfox.org/mozilla-central/rev/c1180ea13e73eb985a49b15c0d90e977a1aa919c/js/src/jit/CacheIR.cpp#10140 diff --git a/js/src/doc/feature_checklist.md b/js/src/doc/feature_checklist.md new file mode 100644 index 0000000000..7cf847f725 --- /dev/null +++ b/js/src/doc/feature_checklist.md @@ -0,0 +1,79 @@ +# JavaScript Language Feature Checklist +So you're working on a new JavaScript feature in SpiderMonkey: Congratulations! Here's a set of checklists and guidelines to help you on your way. + +## High Level Feature Ship Checklist. +(Note: Some of these pieces can happen in parallel, so it's not necessary to +work directly top-down) + +- ☐ Send an Intent to Prototype email to `dev-platform`. This is part of the + [Exposure Guidelines](https://wiki.mozilla.org/ExposureGuidelines) process. We + historically haven't been amazing at sending intent-to-prototype emails, but + we can always get better. +- ☐ Create a shell option for the feature. +- ☐ Stage 2 or earlier proposals should be developed under compile time guards, + disabled by default. +- ☐ Create a browser preference for the feature. +- ☐ Implement the Feature. +- ☐ Land feature disabled by pref and shell-option. +- ☐ Import the test262 test cases for the feature, or enable them if they're + already imported. (See `js/src/test/Readme.txt` for guidance) +- ☐ Contact `fuzzing@mozilla.org` to arrange fuzzing for the feature. +- ☐ Add shell option to `js/src/shell/fuzz-flags.txt`. This signals to other + fuzzers as well that the feature is ready for fuzzing. +- ☐ Send an Intent to Ship Email to `dev-platform`. This is also part of the + [Exposure Guidelines](https://wiki.mozilla.org/ExposureGuidelines) process. +- ☐ Ship the feature; default the preference to true and the command-line + option to true. +- ☐ Open a followup bug to later remove the preference and the command line + option. + + +## Supplemental Checklists +### Shipping Consideration Checklist + +- ☐ If it seems possible that the feature will cause webcompat issues, + consider shipping `NIGHTLY_ONLY` for a cycle or two, to use nightly as an + attempt to shake out potential webcompat issues. + + +### Web Platform Integration Checklist + +_Sometimes Complexity of the web-platform leaks into JS Feature works_ + +- ☐ Ensure the appropriate web-platform tests exist, and are being run. +- ☐ Is your feature correctly enabled inside of Workers? (They have different + option set than main thread, and it's easy to forget them!) You may want to + write a mochitest. + +### Syntax Features Checklist + +- ☐ Does `Reflect.parse` correctly parse and return results for your new syntax? + - `Reflect.parse` tests are interesting as well, because they can be written + for new syntax before bytecode emission is done. + - ☐ Are the locations correct for the new syntax entries in the parse tree? +- ☐ Are your errors emitted with sensible location info? + +### Testing Consideration Checklist + +_There's lots of complexity in SpiderMonkey that isn't always captured by the +specification, so the below is some useful guidance to behaviour to pay +attention to that may not be tested by a feature's test262 tests_ + +- ☐ How does your feature interact with multiple compartments? What happens if + references happen across compartments, or if `this` is a + `CrossCompartmentWrapper`? +- ☐ Are your error messages being emitted in the correct realm, with the + correct prototype? +- ☐ If async functions or promises are involved, are user-code objects + resolved? If so, does the feature correctly handle [the `.then` property + behaviour of promise + resolution?](https://www.stefanjudis.com/today-i-learned/promise-resolution-with-objects-including-a-then-property/) +- ☐ Have you written some OOM tests for your feature to ensure your OOM + handling is correct? + +#### Web Platform Testing Considerations +- ☐ Does the feature have to handle exotic objects specially? Consider what + happens when your feature interacts with the very exotic objects on the web + platform, such as `WindowProxy`, `Location` (cross-origin objects). +- ☐ What happens when your feature interacts with + [X-rays](/dom/scriptSecurity/xray_vision.rst)? diff --git a/js/src/doc/gc.rst b/js/src/doc/gc.rst new file mode 100644 index 0000000000..e0a70c9d7a --- /dev/null +++ b/js/src/doc/gc.rst @@ -0,0 +1,140 @@ +SpiderMonkey garbage collector +============================== + +The SpiderMonkey garbage collector is responsible for allocating memory +representing JavaScript data structures and deallocating them when they are no +longer in use. It aims to collect as much data as possible in as little time +as possible. As well as JavaScript data it is also used to allocate some +internal SpiderMonkey data structures. + +The garbage collector is a hybrid tracing collector, and has the following +features: + + - :ref:`Precise <precise-gc>` + - :ref:`Incremental <incremental-gc>` + - :ref:`Generational <generational-gc>` + - :ref:`Partially concurrent <concurrent-gc>` + - :ref:`Parallel <parallel-gc>` + - :ref:`Compacting <compacting-gc>` + - :ref:`Partitioned heap <partitioned-heap>` + +For an overview of garbage collection see: +https://en.wikipedia.org/wiki/Tracing_garbage_collection + +Description of features +####################### + +.. _precise-gc: + +Precise collection +****************** + +The GC is 'precise' in that it knows the layout of allocations (which is used +to determine reachable children) and also the location of all stack roots. This +means it does not need to resort to conservative techniques that may cause +garbage to be retained unnecessarily. + +Knowledge of the stack is achieved with C++ wrapper classes that must be used +for stack roots and handles (pointers) to them. This is enforced by the +SpiderMonkey API (which operates in terms of these types) and checked by a +static analysis that reports places when unrooted GC pointers can be present +when a GC could occur. + +For details of stack rooting, see: https://github.com/mozilla-spidermonkey/spidermonkey-embedding-examples/blob/esr78/docs/GC%20Rooting%20Guide.md + +We also have a :doc:`static analysis <HazardAnalysis/index>` for detecting +errors in rooting. It can be :doc:`run locally or in CI <HazardAnalysis/running>`. + +.. _incremental-gc: + +Incremental collection +********************** + +'Stop the world' collectors run a whole collection in one go, which can result +in unacceptable pauses for users. An incremental collector breaks its +execution into a number of small slices, reducing user impact. + +As far as possible the SpiderMonkey collector runs incrementally. Not all +parts of a collection can be performed incrementally however as there are some +operations that need to complete atomically with respect to the rest of the +program. + +Currently, most of the collection is performed incrementally. Root marking, +compacting, and an initial part of sweeping are not. + +.. _generational-gc: + +Generational collection +*********************** + +Most real world allocations either die very quickly or live for a long +time. This suggests an approach to collection where allocations are moved +between 'generations' (separate heaps) depending on how long they have +survived. Generations containing young allocations are fast to collect and can +be collected more frequently; older generations are collected less often. + +The SpiderMonkey collector implements a single young generation (the nursery) +and a single old generation (the tenured heap). Collecting the nursery is +known as a minor GC as opposed to a major GC that collects the whole heap +(including the nursery). + +.. _concurrent-gc: + +Concurrent collection +********************* + +Many systems have more than one CPU and therefore can benefit from offloading +GC work to another core. In GC terms 'concurrent' usually refers to GC work +happening while the main program continues to run. + +The SpiderMonkey collector currently only uses concurrency in limited phases. + +This includes most finalization work (there are some restrictions as not all +finalization code can tolerate this) and some other aspects such as allocating +and decommitting blocks of memory. + +Performing marking work concurrently is currently being investigated. + +.. _parallel-gc: + +Parallel collection +******************* + +In GC terms 'parallel' usually means work performed in parallel while the +collector is running, as opposed to the main program itself. The SpiderMonkey +collector performs work within GC slices in parallel wherever possible. + +.. _compacting-gc: + +Compacting collection +********************* + +The collector allocates data with the same type and size in 'arenas' (often know +as slabs). After many allocations have died this can leave many arenas +containing free space (external fragmentation). Compacting remedies this by +moving allocations between arenas to free up as much memory as possible. + +Compacting involves tracing the entire heap to update pointers to moved data +and is not incremental so it only happens rarely, or in response to memory +pressure notifications. + +.. _partitioned-heap: + +Partitioned heap +**************** + +The collector has the concept of 'zones' which are separate heaps which can be +collected independently. Objects in different zones can refer to each other +however. + +Zones are also used to help incrementalize parts of the collection. For +example, compacting is not fully incremental but can be performed one zone at +a time. + +Other documentation +################### + +More details about the Garbage Collector (GC) can be found by looking for the +`[SMDOC] Garbage Collector`_ comment in the sources. + +.. _[SMDOC] Garbage Collector: https://searchfox.org/mozilla-central/search?q=[SMDOC]+Garbage+Collector diff --git a/js/src/doc/hacking_tips.md b/js/src/doc/hacking_tips.md new file mode 100644 index 0000000000..58c42ee5e0 --- /dev/null +++ b/js/src/doc/hacking_tips.md @@ -0,0 +1,588 @@ +# Hacking Tips + +**These tips were archived from [MDN](https://mdn-archive.mossop.dev/en-US/docs/Mozilla/Projects/SpiderMonkey/Hacking_Tips)**: This may be out of date! + +This is archived here because it captures valuable documentation that even if potentially out of date, provides inspiration. + +--- + + +This page lists a few tips to help you investigate issues related to SpiderMonkey. All tips listed here are dealing with the JavaScript shell obtained at the end of the [build documentation of SpiderMonkey](build.rst). It is separated in 2 parts, one section related to debugging and another section related to drafting optimizations. Many of these tips only apply to debug builds of the JS shell; they will not function in a release build. + +## Tools + +Here are some debugging tools above and beyond your standard debugger that might help you: + +* [rr](https://rr-project.org/) is a record-and-replay deterministic debugger for Linux +* [Pernosco](https://pernos.co/) takes an rr recording and adds omniscient debugging tools to help you + * It is a paid service with a free trial + * Mozilla has a license for internal developers; you can contact Matthew Gaudet <mgaudet@mozilla.com> for details + + +## Debugging Tips + +### Getting help (from JS shell) + +Use the **help** function to get the list of all primitive functions of the shell with their description. Note that some functions have been moved under an 'os' object, and **help(os)** will give brief help on just the members of that "namespace". + +You can also use **help(/Regex/)** to get help for members of the global namespace that match the given regular expression. + +### Getting the bytecode of a function (from JS shell) + +The shell has a small function named **dis** to dump the bytecode of a function with its source notes. Without arguments, it will dump the bytecode of its caller. + +``` +js> function f () { + return 1; +} +js> dis(f); +flags: +loc op +----- -- +main: +00000: one +00001: return +00002: stop + +Source notes: + ofs line pc delta desc args +---- ---- ----- ------ -------- ------ + 0: 1 0 [ 0] newline + 1: 2 0 [ 0] colspan 2 + 3: 2 2 [ 2] colspan 9 + +``` + +### Getting the bytecode of a function (from gdb) + +In _jsopcode.cpp_, a function named **js::DisassembleAtPC** can print the bytecode of a script. Some variants of this function, such as **js::DumpScript** etc., are convenient for debugging. + +### Printing the JS stack (from gdb) + +In _jsobj.cpp_, a function named **js::DumpBacktrace** prints a backtrace à la gdb for the JS stack. The backtrace contains in the following order, the stack depth, the interpreter frame pointer (see _js/src/vm/Stack.h_, **StackFrame** class) or (nil) if compiled with IonMonkey, the file and line number of the call location and under parentheses, the **JSScript** pointer and the **jsbytecode** pointer (pc) executed. + +``` +$ gdb --args js +[…] +(gdb) b js::ReportOverRecursed +(gdb) r +js> function f(i) { + if (i % 2) f(i + 1); + else f(i + 3); +} +js> f(0) + +Breakpoint 1, js::ReportOverRecursed (maybecx=0xfdca70) at /home/nicolas/mozilla/ionmonkey/js/src/jscntxt.cpp:495 +495 if (maybecx) +(gdb) call js::DumpBacktrace(maybecx) +#0 (nil) typein:2 (0x7fffef1231c0 @ 0) +#1 (nil) typein:2 (0x7fffef1231c0 @ 24) +#2 (nil) typein:3 (0x7fffef1231c0 @ 47) +#3 (nil) typein:2 (0x7fffef1231c0 @ 24) +#4 (nil) typein:3 (0x7fffef1231c0 @ 47) +[…] +#25157 0x7fffefbbc250 typein:2 (0x7fffef1231c0 @ 24) +#25158 0x7fffefbbc1c8 typein:3 (0x7fffef1231c0 @ 47) +#25159 0x7fffefbbc140 typein:2 (0x7fffef1231c0 @ 24) +#25160 0x7fffefbbc0b8 typein:3 (0x7fffef1231c0 @ 47) +#25161 0x7fffefbbc030 typein:5 (0x7fffef123280 @ 9) + +``` + +Note, you can do the exact same exercise above using `lldb` (necessary on OSX after Apple removed `gdb`) by running `lldb -f js` then following the remaining steps. + +Since SpiderMonkey 48, we have a gdb unwinder. This unwinder is able to read the frames created by the JIT, and to display the frames which are after these JIT frames. + +``` +$ gdb --args out/dist/bin/js ./foo.js +[…] +SpiderMonkey unwinder is disabled by default, to enable it type: + enable unwinder .* SpiderMonkey +(gdb) b js::math_cos +(gdb) run +[…] +#0 js::math_cos (cx=0x14f2640, argc=1, vp=0x7fffffff6a88) at js/src/jsmath.cpp:338 +338 CallArgs args = CallArgsFromVp(argc, vp); +(gdb) enable unwinder .* SpiderMonkey +(gdb) backtrace 10 +#0 0x0000000000f89979 in js::math_cos(JSContext*, unsigned int, JS::Value*) (cx=0x14f2640, argc=1, vp=0x7fffffff6a88) at js/src/jsmath.cpp:338 +#1 0x0000000000ca9c6e in js::CallJSNative(JSContext*, bool (*)(JSContext*, unsigned int, JS::Value*), JS::CallArgs const&) (cx=0x14f2640, native=0xf89960 , args=...) at js/src/jscntxtinlines.h:235 +#2 0x0000000000c87625 in js::Invoke(JSContext*, JS::CallArgs const&, js::MaybeConstruct) (cx=0x14f2640, args=..., construct=js::NO_CONSTRUCT) at js/src/vm/Interpreter.cpp:476 +#3 0x000000000069bdcf in js::jit::DoCallFallback(JSContext*, js::jit::BaselineFrame*, js::jit::ICCall_Fallback*, uint32_t, JS::Value*, JS::MutableHandleValue) (cx=0x14f2640, frame=0x7fffffff6ad8, stub_=0x1798838, argc=1, vp=0x7fffffff6a88, res=JSVAL_VOID) at js/src/jit/BaselineIC.cpp:6113 +#4 0x00007ffff7f41395 in +``` + +Note, when you enable the unwinder, the current version of gdb (7.10.1) does not flush the backtrace. Therefore, the JIT frames do not appear until you settle on the next breakpoint. To work-around this issue you can use the recording feature of `gdb`, to step one instruction, and settle back to where you came from with the following set of `gdb` commands: + +``` +(gdb) record full +(gdb) si +(gdb) record goto 0 +(gdb) record stop +``` + +If you have a core file, you can use the gdb unwinder the same way, or do everything from the command line as follows: + +``` +$ gdb -ex 'enable unwinder .* SpiderMonkey' -ex 'bt 0' -ex 'thread apply all backtrace' -ex 'quit' out/dist/bin/js corefile +``` + +The gdb unwinder is supposed to be loaded by `dist/bin/js-gdb.py` and load python scripts which are located in `js/src/gdb/mozilla` under gdb. If gdb does not load the unwinder by default, you can force it to, by using the `source` command with the `js-gdb.py` file. + +### Setting a breakpoint in the generated code (from gdb, x86 / x86-64, arm) + +To set a breakpoint in the generated code of a specific JSScript compiled with IonMonkey, set a breakpoint on the instruction you are interested in. If you have no precise idea which function you are looking at, you can set a breakpoint on the **js::ion::CodeGenerator::visitStart** function. Optionally, a condition on the **ins->id()** of the LIR instruction can be added to select precisely the instruction you are looking for. Once the breakpoint is on the **CodeGenerator** function of the LIR instruction, add a command to generate a static breakpoint in the generated code. + +``` +$ gdb --args js +[…] +(gdb) b js::ion::CodeGenerator::visitStart +(gdb) command +>call masm.breakpoint() +>continue +>end +(gdb) r +js> function f(a, b) { return a + b; } +js> for (var i = 0; i < 100000; i++) f(i, i + 1); + +Breakpoint 1, js::ion::CodeGenerator::visitStart (this=0x101ed20, lir=0x10234e0) + at /home/nicolas/mozilla/ionmonkey/js/src/ion/CodeGenerator.cpp:609 +609 } + +Program received signal SIGTRAP, Trace/breakpoint trap. +0x00007ffff7fb165a in ?? () +(gdb) + +``` + +Once you hit the generated breakpoint, you can replace it by a gdb breakpoint to make it conditional. The procedure is to first replace the generated breakpoint by a nop instruction, and to set a breakpoint at the address of the nop. + +``` +(gdb) x /5i $pc - 1 + 0x7ffff7fb1659: int3 +=> 0x7ffff7fb165a: mov 0x28(%rsp),%rax + 0x7ffff7fb165f: mov %eax,%ecx + 0x7ffff7fb1661: mov 0x30(%rsp),%rdx + 0x7ffff7fb1666: mov %edx,%ebx + +(gdb) # replace the int3 by a nop +(gdb) set *(unsigned char *) ($pc - 1) = 0x90 +(gdb) x /1i $pc - 1 + 0x7ffff7fb1659: nop + +(gdb) # set a breakpoint at the previous location +(gdb) b *0x7ffff7fb1659 +Breakpoint 2 at 0x7ffff7fb1659 +``` + +### Printing Ion generated assembly code (from gdb) + +If you want to look at the assembly code generated by IonMonkey, you can follow this procedure: + +1. Place a breakpoint at CodeGenerator.cpp on the CodeGenerator::link method. +1. Step next a few times, so that the "code" variable gets generated +1. Print code->code\_, which is the address of the code +1. Disassemble code read at this address (using x/Ni address, where N is the number of instructions you would like to see) + +Here is an example. It might be simpler to use the CodeGenerator::link lineno instead of the full qualified name to put the breakpoint. Let's say that the line number of this function is 4780, for instance: + + (gdb) b CodeGenerator.cpp:4780 + Breakpoint 1 at 0x84cade0: file /home/code/mozilla-central/js/src/ion/CodeGenerator.cpp, line 4780. + (gdb) r + Starting program: /home/code/mozilla-central/js/src/32-release/js -f /home/code/jaeger.js + [Thread debugging using libthread_db enabled] + Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". + [New Thread 0xf7903b40 (LWP 12563)] + [New Thread 0xf6bdeb40 (LWP 12564)] + Run#0 + + Breakpoint 1, js::ion::CodeGenerator::link (this=0x86badf8) + at /home/code/mozilla-central/js/src/ion/CodeGenerator.cpp:4780 + 4780 { + (gdb) n + 4781 JSContext *cx = GetIonContext()->cx; + (gdb) n + 4783 Linker linker(masm); + (gdb) n + 4784 IonCode *code = linker.newCode(cx, JSC::ION*CODE); + (gdb) n + 4785 if (!code) + (gdb) p code->code* + $1 = (uint8_t \*) 0xf7fd25a8 "\201", <incomplete sequence \354\200> + (gdb) x/2i 0xf7fd25a8 + 0xf7fd25a8: sub $0x80,%esp + 0xf7fd25ae: mov 0x94(%esp),%ecx + +On arm, the compiled JS code will always be ARM machine code, whereas SpiderMonkey itself is frequently Thumb2. Since there isn't debug info for the JIT'd code, you will need to tell gdb that you are looking at ARM code: + + (gdb) set arm force-mode arm + +Or you can wrap the x command in your own command: + + def xi + set arm force-mode arm + eval "x/%di %d", $arg0, $arg1 + set arm force-mode auto + end + +### Printing asm.js/wasm generated assembly code (from gdb) + +- Set a breakpoint on `js::wasm::Instance::callExport` (defined in `WasmInstance.cpp` as of November 18th 2016). This will trigger for _any_ asm.js/wasm call, so you should find a way to set this breakpoint for the only generated codes you want to look at. +- Run the program. +- Do `next` in gdb until you reach the definition of the `funcPtr`: +``` +// Call the per-exported-function trampoline created by GenerateEntry. +auto funcPtr = JS*DATA_TO_FUNC_PTR(ExportFuncPtr, codeBase() + func.entryOffset()); +if (!CALL_GENERATED_2(funcPtr, exportArgs.begin(), &tlsData*)) + return false; +``` +- After it's set, `x/64i funcPtr` will show you the trampoline code. There should be a call to an address at some point; that's what we're targeting. Copy that address. + +``` + 0x7ffff7ff6000: push %r15 + 0x7ffff7ff6002: push %r14 + 0x7ffff7ff6004: push %r13 + 0x7ffff7ff6006: push %r12 + 0x7ffff7ff6008: push %rbp + 0x7ffff7ff6009: push %rbx + 0x7ffff7ff600a: movabs $0xea4f80,%r10 + 0x7ffff7ff6014: mov 0x178(%r10),%r10 + 0x7ffff7ff601b: mov %rsp,0x40(%r10) + 0x7ffff7ff601f: mov (%rsi),%r15 + 0x7ffff7ff6022: mov %rdi,%r10 + 0x7ffff7ff6025: push %r10 + 0x7ffff7ff6027: test $0xf,%spl + 0x7ffff7ff602b: je 0x7ffff7ff6032 + 0x7ffff7ff6031: int3 + 0x7ffff7ff6032: callq 0x7ffff7ff5000 <------ right here +``` + +- `x/64i address` (in this case: `x/64i 0x7ffff7ff6032`). +- If you want to put a breakpoint at the function's entry, you can do: `b *address` (for instance here, `b* 0x7ffff7ff6032`). Then you can display the instructions around pc with `x/20i $pc,` and execute instruction by instruction with `stepi`. + +### Finding the script of Ion generated assembly (from gdb) + +When facing a bug in which you are in the middle of IonMonkey generated code, the first thing to note is that gdb's backtrace is not reliable, because the generated code does not keep a frame pointer. To figure it out, you have to read the stack to infer the IonMonkey frame. + +``` +(gdb) x /64a $sp +[…] +0x7fffffff9838: 0x7ffff7fad2da 0x141 +0x7fffffff9848: 0x7fffef134d40 0x2 +[…] +(gdb) p (*(JSFunction**) 0x7fffffff9848)->u.i.script_->lineno +$1 = 1 +(gdb) p (*(JSFunction**) 0x7fffffff9848)->u.i.script_->filename +$2 = 0xff92d1 "typein" +``` + +The stack is ordered as defined in js/src/ion/IonFrames-x86-shared.h. It is composed of the return address, a descriptor (a small value), the JSFunction (if it is even) or a JSScript (if it is odd; remove it to dereference the pointer) and the frame ends with the number of actual arguments (a small value too). If you want to know at which LIR the code is failing at, the **js::ion::CodeGenerator::generateBody** function can be instrumented to dump the LIR **id** before each instruction. + +``` +for (; iter != current->end(); iter++) { + IonSpew(IonSpew_Codegen, "instruction %s", iter->opName()); + […] + + masm.store16(Imm32(iter->id()), Address(StackPointer, -8)); // added + if (!iter->accept(this)) + return false; +``` + +`This modification will add an instruction which abuses the stack pointer` to store an immediate value (the LIR id) to a location which would never be generated by any sane compiler. Thus when dumping the assembly under gdb, this kind of instructions would be easily noticeable. + +### Viewing the MIRGraph of Ion/Odin compilations (from gdb) + +With gdb instrumentation, we can call [iongraph](https://github.com/sstangl/iongraph) program within gdb when the execution is stopped. This instrumentation adds an **`iongraph`** command when provided with an instance of a **`MIRGenerator*`**, will call `iongraph`, `graphviz` and your preferred png viewer to display the MIR graph at the precise time of the execution. To find **`MIRGenetator*`** instances, it is best to look up into the stack for `OptimizeMIR`, or `CodeGenerator::generateBody`. **`OptimizeMIR`** function has a **`mir`** argument, and the **`CodeGenerator::generateBody`** function has a member **`this->gen`**. + +``` +(gdb) bt +#0 0x00000000007eaad4 in js::InlineList<js::jit::MBasicBlock>::begin() const (this=0x33dbbc0) at …/js/src/jit/InlineList.h:280 +#1 0x00000000007cb845 in js::jit::MIRGraph::begin() (this=0x33dbbc0) at …/js/src/jit/MIRGraph.h:787 +#2 0x0000000000837d25 in js::jit::BuildPhiReverseMapping(js::jit::MIRGraph&) (graph=...) at …/js/src/jit/IonAnalysis.cpp:2436 +#3 0x000000000083317f in js::jit::OptimizeMIR(js::jit::MIRGenerator*) (mir=0x33dbdf0) at …/js/src/jit/Ion.cpp:1570 +… +(gdb) frame 3 +#3 0x000000000083317f in js::jit::OptimizeMIR(js::jit::MIRGenerator*) (mir=0x33dbdf0) at …/js/src/jit/Ion.cpp:1570 +(gdb) iongraph mir + function 0 (asm.js compilation): success; 1 passes. +/* open your png viewer with the result of iongraph */ +``` + +This gdb instrumentation is supposed to work with debug builds, or with optimized builds compiled with `--enable-jitspew` configure flag. External programs such as `iongraph`, `dot`, and your png viewer are searched for in the `PATH`; otherwise custom one can either be configured with environment variables (`GDB_IONGRAPH`, `GDB_DOT`, `GDB_PNGVIEWER`) before starting gdb, or with gdb parameters (`set iongraph-bin <path>`, `set dot-bin <path>`, `set pngviewer-bin <path>`) within gdb. + +Enabling GDB instrumentation may require launching a JS shell executable that shares a directory with a file name "js-gdb.py". If js/src/js does not provide the "iongraph" command, try js/src/shell/js. GDB may complain that ~/.gdbinit requires modification to authorize user scripts, and if so will print out directions. + +### Finding the code that generated a JIT instruction (from rr) + +If you are looking at a JIT instruction and need to know what code generated it, you can use [jitsrc.py](https://searchfox.org/mozilla-central/source/js/src/gdb/mozilla/jitsrc.py). This script adds a `jitsrc` command to rr that will trace backwards from the JIT instruction to the code that generated it. + +To use the `jitsrc` command, add the following line to your .gdbinit file, or run it manually: + + source js/src/gdb/mozilla/jitsrc.py + +And you use the command like this: `jitsrc <address of JIT instruction>`. + +Running the command will leave the application at the point of execution where that JIT instruction was originally emitted. For example, the backtrace might contain a frame at [js::jit::MacroAssemblerX64::loadPtr](https://searchfox.org/mozilla-central/rev/ddde3bbcafabe0fc8a36c660b3b673507d3e3874/js/src/jit/x64/MacroAssembler-x64.h#575). + +The way this works is by setting a watchpoint on the JIT instruction and `reverse-continue`ing the program execution to reach the point when that memory address was assigned to. JIT instruction memory can be copied or moved, so the `jitsrc` command automates updating the watchpoint across the copy/move to continue back to the original source of the JIT instruction. + +### Break on valgrind errors + +Sometimes, a bug can be reproduced under valgrind but with great difficulty under gdb. One way to investigate is to let valgrind start gdb for you; the other way documented here is to let valgrind act as a gdb server which can be manipulated from the gdb remote. + +``` +$ valgrind --smc-check=all-non-file +``` + +This command will tell you how to start gdb as a remote. Be aware that functions which are usually dumping some output will do it in the shell where valgrind is started and not in the shell where gdb is started. Thus functions such as **js::DumpBacktrace**, when called from gdb, will print their output in the shell containing valgrind. + +### Adding spew for Compilations & Bailouts & Invalidations (from gdb) + +If you are in rr, and forgot to record with the spew enabled with IONFLAGS or because this is an optimized build, then you can add similar spew with extra breakpoints within gdb. gdb has the ability to set breakpoints with commands, but a simpler / friendlier version is to use **dprintf**, with a location, and followed by printf-like arguments. + + (gdb) dprintf js::jit::IonBuilder::IonBuilder, "Compiling %s:%d:%d-%d\n", info->script*->scriptSource()->filename*.mTuple.mFirstA, info->script*->lineno*, info->script*->sourceStart*, info->script*->sourceEnd* + Dprintf 1 at 0x7fb4f6a104eb: file /home/nicolas/mozilla/contrib-push/js/src/jit/IonBuilder.cpp, line 159. + (gdb) cond 1 inliningDepth == 0 + (gdb) dprintf js::jit::BailoutIonToBaseline, "Bailout from %s:%d:%d-%d\n", iter.script()->scriptSource()->filename*.mTuple.mFirstA, iter.script()->lineno*, iter.script()->sourceStart*, iter.script()->sourceEnd* + Dprintf 2 at 0x7fb4f6fe43dc: js::jit::BailoutIonToBaseline. (2 locations) + (gdb) dprintf Ion.cpp:3196, "Invalidate %s:%d:%d-%d\n", co->script*->scriptSource()->filename*.mTuple.mFirstA, co->script*->lineno*, co->script*->sourceStart*, co->script*->sourceEnd* + Dprintf 3 at 0x7fb4f6a0b62a: file /home/nicolas/mozilla/contrib-push/js/src/jit/Ion.cpp, line 3196. + `(gdb) continue` + Compiling self-hosted:650:20470-21501 + Bailout from self-hosted:20:403-500 + Invalidate self-hosted:20:403-500 + +Note: the line 3196, listed above, corresponds to the location of the [Jit spew inside jit::Invalidate function](https://searchfox.org/mozilla-central/rev/655f49c541108e3d0a232aa7173fbcb9af88d80b/js/src/jit/Ion.cpp#2475). + +## Hacking tips + + +### Using the Gecko Profiler (browser / xpcshell) + +See the section dedicated to [profiling with the Gecko Profiler](/tools/profiler/index.rst). This method of profiling has the advantage of mixing the JavaScript stack with the C++ stack, which is useful for analyzing library function issues. + +One tip is to start looking at a script with an inverted JS stack to locate the most expensive JS function, then to focus on the frame of this JS function, and to remove the inverted stack and look at C++ part of this function to determine from where the cost is coming from. + +These archived [tips on using the Gecko Profiler](https://mdn-archive.mossop.dev/en-US/docs/Performance/Profiling_with_the_Built-in_Profiler "/en-US/docs/Performance/Profiling_with_the_Built-in_Profiler") and [FAQ](https://mdn-archive.mossop.dev/en-US/docs/Mozilla/Performance/Gecko_Profiler_FAQ "Gecko Profiler FAQ") might also be useful as inspiration, but are old enough that they are probably not accurate any more. + +### Using callgrind (JS shell) + +Because SpiderMonkey just-in-time compilers rewrite the executed program, valgrind should be informed from the command line by adding **--smc-check=all-non-file**. + +``` +$ valgrind --tool=callgrind --callgrind-out-file=bench.clg \ + --smc-check=all-non-file +``` + +The output file can then be used with **kcachegrind**, which provides a graphical view of the call graph. + +### Using IonMonkey spew (JS shell) + +IonMonkey spew is extremely verbose (not as much as the INFER spew), but you can filter it to focus on the list of compiled scripts or channels, IonMonkey spew channels can be selected with the IONFLAGS environment variable, and compilation spew can be filtered with IONFILTER. + +IONFLAGS contains the names of [each channel separated by commas](https://searchfox.org/mozilla-central/source/js/src/jit/JitSpewer.cpp#338). The **logs** channel produces one file (_/tmp/ion.json_), made to be used with [iongraph](https://github.com/sstangl/iongraph) (made by Sean Stangl). This tool will show the MIR & LIR steps done by IonMonkey during the compilation. To use [iongraph](https://github.com/sstangl/iongraph), you must install [Graphviz](https://www.graphviz.org/download/ "graphviz downloads"). + +Compilation logs and spew can be filtered with the IONFILTER environment variable which contains locations as output by other spew channels. Multiple locations can be specified using comma as a separator. + +``` +$ IONFILTER=pdfjs.js:16934 IONFLAGS=logs,scripts,osi,bailouts ./js --ion-offthread-compile=off ./run.js 2>&1 | less +``` + +The **bailouts** channel is likely to be the first thing you should focus on, because this means that something does not stay in IonMonkey and fallback to the interpreter. This channel outputs locations (as returned by the **id()** function of both instructions) of the latest MIR and the latest LIR phases. These locations should correspond to phases of the **logs** and a filter can be used to remove uninteresting functions. + +### Using the ARM simulator + +The ARM simulator can be used to test the ARM JIT backend on x86/x64 hardware. An ARM simulator build is an x86 shell (or browser) with the ARM JIT backend. Instead of entering JIT code, it runs it in a simulator (interpreter) for ARM code. To use the simulator, compile an x86 shell (32-bit, x64 doesn't work as we use a different Value format there), and pass --enable-arm-simulator to configure. For instance, on a 64-bit Linux host you can use the following configure command to get an ARM simulator build: + +```shell +AR=ar CC="gcc -m32" CXX="g++ -m32" ../configure --target=i686-pc-linux +--enable-debug --disable-optimize --enable-threadsafe --enable-simulator=arm +``` + +Or on OS X: + +```shell +$ AR=ar CC="clang -m32" CXX="clang++ -m32" ../configure --target=i686-apple-darwin10.0.0 --enable-debug --disable-optimize --enable-threadsafe --enable-arm-simulator +``` + +An **--enable-debug --enable-optimize** build is recommended if you want to run jit-tests or jstests. + +#### Use the VIXL Debugger in the simulator (arm64) + +Set a breakpoint (see the section above about setting a breakpoint in generated code) and run with the environment variable `USE_DEBUGGER=1`. This will then drop you into a simple debugger provided with VIXL, the ARM simulator technology used for arm64 simulation. + +#### Use the Simulator Debugger for arm32 + +The same instructions for arm64 in the preceding section apply, but the environment variable differs: Use `ARM_SIM_DEBUGGER=1`. + +#### Building the browser with the ARM simulator + +You can also build the entire browser with the ARM simulator backend, for instance to reproduce browser-only JS failures on ARM. Make sure to build a browser for x86 (32-bits) and add this option to your mozconfig file: + +ac_add_options --enable-arm-simulator + +If you are under an Ubuntu or Debian 64-bits distribution and you want to build a 32-bits browser, it might be hard to find the relevant 32-bits dependencies. You can use [padenot's scripts](https://github.com/padenot/fx-32-on-64.sh) which will magically setup a chrooted 32-bits environment and do All The Things (c) for you (you just need to modify the mozconfig file). + +### Using rr on a test + +Get the command line for your test run using -s: + +./jit_test.py -s $JS_SHELL saved-stacks/async.js + +Insert 'rr' before the shell invocation: + +``` +rr $JS_SHELL -f $JS_SRC/jit-test/lib/prolog.js --js-cache $JS_SRC/jit-test/.js-cache -e "const platform='linux2'; const libdir='$JS_SRC/jit-test/lib/'; const scriptdir='$JS_SRC/jit-test/tests/saved-stacks/'" -f $JS_SRC/jit-test/tests/saved-stacks/async.js +``` + +(note that the above is an example; simply setting JS_SHELL and JS_SRC will not work). Or if this is an intermittent, run it in a loop capturing an rr log for every one until it fails: + +``` +n=1; while rr ...same.as.above...; do echo passed $n; n=$(( $n + 1 )); done +``` + +Wait until it hits a failure. Now you can run `rr replay` to replay that last (failed) run under gdb. + +#### rr with reftest + +To break on the write of a differing pixel: + +1. Find the X/Y of a pixel that differs +2. Use `run Z` where Z is the mark in the log for TEST-START. For example in '[rr 28496 607198]REFTEST TEST-START | file:///home/bgirard/mozilla-central/tree/image/test/reftest/bmp/bmpsuite/b/wrapper.html?badpalettesize.bmp', Z would be 607198. +3. `break 'mozilla::dom::CanvasRenderingContext2D::DrawWindow(nsGlobalWindow&, double, double, double, double, nsAString_internal const&, unsigned int, mozilla::ErrorResult&)'` +4. `cont` +5. `break 'PresShell::RenderDocument(nsRect const&, unsigned int, unsigned int, gfxContext\*)'` +6. `set print object on` +7. `set $x = <YOUR X VALUE>` +8. `set $y = <YOUR Y VALUE>` +9. `print &((cairo_image_surface_t*)aThebesContext->mDT.mRawPtr->mSurface).data[$y * ((cairo_image_surface_t*)aThebesContext->mDT.mRawPtr->mSurface).stride + $x * ((cairo_image_surface_t\*)aThebesContext->mDT.mRawPtr->mSurface).depth / 8]` +10. `watch *(char*)<ADDRESS OF PREVIOUS COMMAND>` (NOTE: If you set a watch on the previous expression gdb will watch the expression and run out of watchpoints) + +#### rr with emacs + +Within emacs, do `M-x gud-gdb` and replace the command line with `rr replay`. When gdb comes up, enter + +``` +set annot 1 +``` + +to get it to emit file location information so that emacs will pop up the corresponding source. Note that if you `reverse-continue` over a SIGSEGV and you're using the standard .gdbinit that sets a catchpoint for that signal, you'll get an additional stop at the catchpoint. Just `reverse-continue` again to continue to your breakpoints or whatever. + +### [Hack] Replacing one instruction + +To replace one specific instruction, you can customize the instruction's visit function using the JSScript **filename** in **lineno** fields, as well as the **id()** of the LIR / MIR instructions. The JSScript can be obtained from **info().script()**. + +``` +bool +CodeGeneratorX86Shared::visitGuardShape(LGuardShape *guard) +{ + if (info().script()->lineno == 16934 && guard->id() == 522) { + [… another impl only for this one …] + return true; + } + [… old impl …] +``` + +### [Hack] Spewing all compiled code + +I usually just add this to the appropriate `executableCopy()` function. + + if (getenv("INST_DUMP")) { + char buf[4096]; + sprintf(buf, "gdb /proc/%d/exe %d -batch -ex 'set pagination off' -ex 'set arm force-mode arm' -ex 'x/%di %p' -ex 'set arm force-mode auto'", getpid(), getpid(), m_buffer.size() / 4, buffer); + system(buf); + } + +If you aren't running on arm, you should omit the `-ex 'set arm force-mode arm'` and `-ex 'set arm force-mode auto'`. And you should change the size()/4 to be something more appropriate for your architecture. + +### Benchmarking with sub-milliseconds (JS shell) + +In the shell, we have 2 simple ways to benchmark a script. We can either use the **-b** shell option (**--print-timing**) which will evaluate a script given on the command line without any need to instrument the benchmark and print an extra line showing the run-time of the script. The other way is to wrap the section that you want to measure with the **dateNow()** function call, which returns the number of milliseconds, with a decimal part for sub-milliseconds. + +```js +js> dateNow() - dateNow() +-0.0009765625 +``` + +Since [Firefox 61](https://bugzilla.mozilla.org/show_bug.cgi?id=1439788), the shell also has **performance.now()** available. + +### Benchmarking with sub-milliseconds (browser) + +Similar to how you can use **dateNow()** in the JS shell, you can use **performance.now()** in the JavaScript code of a page. + +### Dumping the JavaScript heap + +From the shell, you can call the `dumpHeap` function to dump out all GC things (reachable and unreachable) that are present in the heap. By default, the function writes to stdout, but a filename can be specified as an argument. + +Example output might look as follows: + +``` +0x1234abcd B global object +``` + +The output is textual. The first section of the file contains a list of roots, one per line. Each root has the form "0xabcd1234 \<color> \<description>", where \<color> is the color of the given GC thing (B for black, G for gray, W for white) and \<description> is a string. The list of roots ends with a line containing "==========". + +After the roots come a series of zones. A zone starts with several "comment lines" that start with hashes. The first comment declares the zone. It is followed by lines listing each compartment within the zone. After all the compartments come arenas, which is where the GC things are actually stored. Each arena is followed by all the GC things in the arena. A GC thing starts with a line giving its address, its color, and the thing kind (object, function, whatever). After this comes a list of addresses that the GC thing points to, each one starting with ">". + +It's also possible to dump the JavaScript heap from C++ code (or from gdb) using the `js::DumpHeap` function. It is part of jsfriendapi.h and it is available in release builds. + +### Inspecting MIR objects within a debugger + +For MIRGraph, MBasicBlock, and MDefinition and its subclasses (MInstruction, MConstant, etc.), call the dump member function. + + (gdb) call graph->dump() + (gdb) call block->dump() + (gdb) call def->dump() + + +### How to debug oomTest() failures + +The oomTest() function executes a piece of code many times, simulating an OOM failure at each successive allocation it makes. It's designed to highlight incorrect OOM handling, which may show up as a crash or assertion failure at some later point. + +When debugging such a crash, the most useful thing is to locate the last simulated allocation failure, as that is usually what has caused the subsequent crash. + +My workflow for doing this is as follows: + +1. Build a version of the engine with `--enable-debug` and `--enable-oom-breakpoint` configure flags. +2. Set the environment variable `OOM_VERBOSE=1` and reproduce the failure. This will print an allocation count at each simulated failure. Note the count of the last allocation. +3. Run the engine under a debugger and set a breakpoint on the function `js_failedAllocBreakpoint`. +4. Run the program and `continue` the necessary number of times until you reach the final allocation. + - e.g. in lldb, if the allocation failure number shown is 1500, run `continue -i 1498` (subtracted 2 because we've already hit it once and don't want to skip the last). Drop "-i" for gdb. +5. Dump a backtrace. This should show you the point at which the OOM is incorrectly handled, which will be a few frames up from the breakpoint. + +Note: if you are on linux, it may be simpler to use rr. + +Some guidelines for handling OOM that lead to failures when they are not followed: + +1. Check for allocation failure! + - Fallible allocations should always must be checked and handled, at a minimum by returning a status indicating failure to the caller. +2. Report OOM to the context if you have one + - If a function has a `JSContext*` argument, usually it should call `js::ReportOutOfMemory(cx)` on allocation failure to report this to the context. +3. Sometimes it's OK to ignore OOM + - For example if you are performing a speculative optimisation you might abandon it and continue anyway. In this case, you may have to call cx->recoverFromOutOfMemory() if something further down the stack has already reported the failure. + +### Debugging GC marking/rooting + +The **js::debug** namespace contains some functions that are useful for watching mark bits for an individual JSObject* (or any Cell*). [js/src/gc/Heap.h](https://searchfox.org/mozilla-central/rev/dc5027f02e5ea1d6b56cfbd10f4d3a0830762115/js/src/gc/Heap.h#817-835) contains a comment describing an example usage. Reproduced here: + + // Sample usage from gdb: + // + // (gdb) p $word = js::debug::GetMarkWordAddress(obj) + // $1 = (uintptr_t *) 0x7fa56d5fe360 + // (gdb) p/x $mask = js::debug::GetMarkMask(obj, js::gc::GRAY) + // $2 = 0x200000000 + // (gdb) watch *$word + // Hardware watchpoint 7: *$word + // (gdb) cond 7 *$word & $mask + // (gdb) cont + // + // Note that this is *not* a watchpoint on a single bit. It is a watchpoint on + // the whole word, which will trigger whenever the word changes and the + // selected bit is set after the change. + // + // So if the bit changing is the desired one, this is exactly what you want. + // But if a different bit changes (either set or cleared), you may still stop + // execution if the $mask bit happened to already be set. gdb does not expose + // enough information to restrict the watchpoint to just a single bit. + +Most of the time, you will want **js::gc::BLACK** (or you can just use 0) for the 2nd param to **js::debug::GetMarkMask**. diff --git a/js/src/doc/index.rst b/js/src/doc/index.rst new file mode 100644 index 0000000000..82c36d52ad --- /dev/null +++ b/js/src/doc/index.rst @@ -0,0 +1,204 @@ +============ +SpiderMonkey +============ + +*SpiderMonkey* is the *JavaScript* and *WebAssembly* implementation library of +the *Mozilla Firefox* web browser. The implementation behaviour is defined by +the `ECMAScript <https://tc39.es/ecma262/>`_ and `WebAssembly +<https://webassembly.org/>`_ specifications. + +Much of the internal technical documentation of the engine can be found +throughout the source files themselves by looking for comments labelled with +`[SMDOC]`_. Information about the team, our processes, and about embedding +*SpiderMonkey* in your own projects can be found at https://spidermonkey.dev. + +Specific documentation on a few topics is available at: + +.. toctree:: + :maxdepth: 1 + + build + test + hacking_tips + Debugger/index + SavedFrame/index + feature_checklist + bytecode_checklist + + +Components of SpiderMonkey +########################## + +🧹 Garbage Collector +********************* + +.. toctree:: + :maxdepth: 2 + :hidden: + + Overview <gc> + Rooting Hazard Analysis <HazardAnalysis/index> + Running the Analysis <HazardAnalysis/running> + +*JavaScript* is a garbage collected language and at the core of *SpiderMonkey* +we manage a garbage-collected memory heap. Elements of this heap have a base +C++ type of `gc::Cell`_. Each round of garbage collection will free up any +*Cell* that is not referenced by a *root* or another live *Cell* in turn. + +See :doc:`GC overview<gc>` for more details. + + +📦 JS::Value and JSObject +************************** + +*JavaScript* values are divided into either objects or primitives +(*Undefined*, *Null*, *Boolean*, *Number*, *BigInt*, *String*, or *Symbol*). +Values are represented with the `JS::Value`_ type which may in turn point to +an object that extends from the `JSObject`_ type. Objects include both plain +*JavaScript* objects and exotic objects representing various things from +functions to *ArrayBuffers* to *HTML Elements* and more. + +Most objects extend ``NativeObject`` (which is a subtype of ``JSObject``) +which provides a way to store properties as key-value pairs similar to a hash +table. These objects hold their *values* and point to a *Shape* that +represents the set of *keys*. Similar objects point to the same *Shape* which +saves memory and allows the JITs to quickly work with objects similar to ones +it has seen before. See the `[SMDOC] Shapes`_ comment for more details. + +C++ (and Rust) code may create and manipulate these objects using the +collection of interfaces we traditionally call the **JSAPI**. + + +🗃️ JavaScript Parser +********************* + +In order to evaluate script text, we parse it using the *Parser* into an +`Abstract Syntax Tree`_ (AST) temporarily and then run the *BytecodeEmitter* +(BCE) to generate `Bytecode`_ and associated metadata. We refer to this +resulting format as `Stencil`_ and it has the helpful characteristic that it +does not utilize the Garbage Collector. The *Stencil* can then be +instantiated into a series of GC *Cells* that can be mutated and understood +by the execution engines described below. + +Each function as well as the top-level itself generates a distinct script. +This is the unit of execution granularity since functions may be set as +callbacks that the host runs at a later time. There are both +``ScriptStencil`` and ``js::BaseScript`` forms of scripts. + +By default, the parser runs in a mode called *syntax* or *lazy* parsing where +we avoid generating full bytecode for functions within the source that we are +parsing. This lazy parsing is still required to check for all *early errors* +that the specification describes. When such a lazily compiled inner function +is first executed, we recompile just that function in a process called +*delazification*. Lazy parsing avoids allocating the AST and bytecode which +saves both CPU time and memory. In practice, many functions are never +executed during a given load of a webpage so this delayed parsing can be +quite beneficial. + + +⚙️ JavaScript Interpreter +************************** + +The *bytecode* generated by the parser may be executed by an interpreter +written in C++ that manipulates objects in the GC heap and invokes native +code of the host (eg. web browser). See `[SMDOC] Bytecode Definitions`_ for +descriptions of each bytecode opcode and ``js/src/vm/Interpreter.cpp`` for +their implementation. + + +⚡ JavaScript JITs +******************* + +.. toctree:: + :maxdepth: 1 + :hidden: + + MIR-optimizations/index + +In order to speed up execution of *bytecode*, we use a series of Just-In-Time +(JIT) compilers to generate specialized machine code (eg. x86, ARM, etc) +tailored to the *JavaScript* that is run and the data that is processed. + +As an individual script runs more times (or has a loop that runs many times) +we describe it as getting *hotter* and at certain thresholds we *tier-up* by +JIT-compiling it. Each subsequent JIT tier spends more time compiling but +aims for better execution performance. + +Baseline Interpreter +-------------------- + +The *Baseline Interpreter* is a hybrid interpreter/JIT that interprets the +*bytecode* one opcode at a time, but attaches small fragments of code called +*Inline Caches* (ICs) that rapidly speed-up executing the same opcode the next +time (if the data is similar enough). See the `[SMDOC] JIT Inline Caches`_ +comment for more details. + +Baseline Compiler +----------------- + +The *Baseline Compiler* use the same *Inline Caches* mechanism from the +*Baseline Interpreter* but additionally translates the entire bytecode to +native machine code. This removes dispatch overhead and does minor local +optimizations. This machine code still calls back into C++ for complex +operations. The translation is very fast but the ``BaselineScript`` uses +memory and requires ``mprotect`` and flushing CPU caches. + +WarpMonkey +---------- + +The *WarpMonkey* JIT replaces the former *IonMonkey* engine and is the +highest level of optimization for the most frequently run scripts. It is able +to inline other scripts and specialize code based on the data and arguments +being processed. + +We translate the *bytecode* and *Inline Cache* data into a Mid-level +`Intermediate Representation`_ (Ion MIR) representation. This graph is +transformed and optimized before being *lowered* to a Low-level Intermediate +Representation (Ion LIR). This *LIR* performs register allocation and then +generates native machine code in a process called *Code Generation*. + +See `MIR Optimizations`_ for an overview of MIR optimizations. + +The optimizations here assume that a script continues to see data similar +what has been seen before. The *Baseline* JITs are essential to success here +because they generate *ICs* that match observed data. If after a script is +compiled with *Warp*, it encounters data that it is not prepared to handle it +performs a *bailout*. The *bailout* mechanism reconstructs the native machine +stack frame to match the layout used by the *Baseline Interpreter* and then +branches to that interpreter as though we were running it all along. Building +this stack frame may use special side-table saved by *Warp* to reconstruct +values that are not otherwise available. + + +🟪 WebAssembly +*************** + +In addition to *JavaScript*, the engine is also able to execute *WebAssembly* +(WASM) sources. + +WASM-Baseline (RabaldrMonkey) +----------------------------- + +This engine performs fast translation to machine code in order to minimize +latency to first execution. + +WASM-Ion (BaldrMonkey) +---------------------- + +This engine translates the WASM input into same *MIR* form that *WarpMonkey* +uses and uses the *IonBackend* to optimize. These optimizations (and in +particular, the register allocation) generate very fast native machine code. + + +.. _gc::Cell: https://searchfox.org/mozilla-central/search?q=[SMDOC]+GC+Cell +.. _JSObject: https://searchfox.org/mozilla-central/search?q=[SMDOC]+JSObject+layout +.. _JS::Value: https://searchfox.org/mozilla-central/search?q=[SMDOC]+JS%3A%3AValue+type&path=js%2F +.. _[SMDOC]: https://searchfox.org/mozilla-central/search?q=[SMDOC]&path=js%2F +.. _[SMDOC] Shapes: https://searchfox.org/mozilla-central/search?q=[SMDOC]+Shapes +.. _[SMDOC] Bytecode Definitions: https://searchfox.org/mozilla-central/search?q=[SMDOC]+Bytecode+Definitions&path=js%2F +.. _[SMDOC] JIT Inline Caches: https://searchfox.org/mozilla-central/search?q=[SMDOC]+JIT+Inline+Caches +.. _Stencil: https://searchfox.org/mozilla-central/search?q=[SMDOC]+Script+Stencil +.. _Bytecode: https://en.wikipedia.org/wiki/Bytecode +.. _Abstract Syntax Tree: https://en.wikipedia.org/wiki/Abstract_syntax_tree +.. _Intermediate Representation: https://en.wikipedia.org/wiki/Intermediate_representation +.. _MIR Optimizations: ./MIR-optimizations/index.html diff --git a/js/src/doc/test.rst b/js/src/doc/test.rst new file mode 100644 index 0000000000..32d7a2abf2 --- /dev/null +++ b/js/src/doc/test.rst @@ -0,0 +1,89 @@ +Running Automated JavaScript Tests +================================== + +SpiderMonkey uses two separate test suites. + +The `Test262 test suite <https://github.com/tc39/test262>`__ is the implementation conformance test suite for ECMA-262, the language specification for JavaScript. All JavaScript engines use Test262 to ensure that they implement JavaScript correctly. Test262 is run using ``mach jstests``. + +In addition to Test262, SpiderMonkey also has a large collection of internal tests. These tests are run using ``mach jit-test``. + +Both sets of tests can be run from a normal build of the JS shell. + +Running Test262 locally +~~~~~~~~~~~~~~~~~~~~~~~ + +The jstests shell harness is in ``js/src/tests/jstests.py``. It can be invoked using + +.. code:: bash + + ./mach jstests + +Note that mach will generally find the JS shell itself; the --shell argument can be used to specify the location manually. All other flags will be passed along to the harness. + +Test262 includes a lot of tests. When working on a specific feature, it is often useful to specify a subset of tests: + +.. code:: bash + + ./mach jstests TEST_PATH_SUBSTRING [TEST_PATH_SUBSTRING_2 ...] + +This runs all tests whose paths contain any TEST_PATH_SUBSTRING. To exclude tests, you can use the ``--exclude=EXCLUDED`` option. Other useful options include: + +- ``-s`` (``--show-cmd``): Show the exact command line used to run each test. +- ``-o`` (``--show-output``): Print each test's output. +- ``--args SHELL_ARGS``: Extra arguments to pass to the JS shell. +- ``--rr`` Run tests under the `rr <https://rr-project.org/>`__ record-and-replay debugger. + +For a complete list of options, use: + +.. code:: bash + + ./mach jstests -- -h + +The Test262 tests can also be run in the browser, using: + +.. code:: bash + + ./mach jstestbrowser + +To run a specific test, you can use the ``--filter=PATTERN`` option, where PATTERN is a RegExp pattern that is tested against ``file:///{PATH_TO_OBJ_DIR}/dist/test-stage/jsreftest/tests/jsreftest.html?test={RELATIVE_PATH_TO_TEST_FROM_js/src/tests}``. + +For historical reasons, ``jstests`` also includes a SpiderMonkey specific suite of additional language feature tests in ``js/src/tests/non262``. + +Running jit-tests locally +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The jit-test shell harness is in ``js/src/jit-test/jit_test.py``. It can be invoked using + +.. code:: bash + + ./mach jit-test + +Basic usage of ``mach jit-test`` is similar to ``mach jstests``. A subset of tests can be specified: + +.. code:: bash + + ./mach jit-test TEST_PATH_SUBSTRING [TEST_PATH_SUBSTRING_2 ...] + +The ``--jitflags`` option allows you to test the JS executable with different flags. The flags are used to control which features are run, such as which JITs are enabled and how quickly they will kick in. The valid options are named bundles like 'all', 'interp', 'none', etc. See the full list in the ``-h`` / ``--help`` output. + +Another helpful option specific to ``jit-tests`` is ``-R <filename>`` (or ``--retest <filename>``). The first time this is run, it runs the entire test suite and writes a list of tests that failed to the given filename. The next time it is run, it will run only the tests in the given filename, and then will write the list of tests that failed when it is done. Thus, you can keep retesting as you fix bugs, and only the tests that failed the last time will run, speeding things up a bit. + +Other useful options include: + +- ``-s`` (``--show-cmd``): Show the exact command line used to run each test. +- ``-o`` (``--show-output``): Print each test's output. +- ``--args SHELL_ARGS``: Extra arguments to pass to the JS shell. +- ``--debug-rr`` Run a test under the `rr <https://rr-project.org/>`__ record-and-replay debugger. +- ``--cgc`` Run a test with frequent compacting GCs (equivalent to ``SM(cgc)``) + +Adding new jit-tests +~~~~~~~~~~~~~~~~~~~~ + +Creating new tests for jit-tests is easy. Just add a new JS file in an appropriate directory under ``js/src/jit-test/tests``. (By default, tests should go in ``test/basic``.) The test harness will automatically find the test and run it. The test is considered to pass if the exit code of the JS shell is zero (i.e., JS didn't crash and there were no JS errors). Use the ``assertEq`` function to verify values in your test. + +There are some advanced options for tests. See the README (in ``js/src/jit-tests``) for details. + +Running jstests on Treeherder +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On Treeherder, jstests run in the browser are shown as ``R(J)`` (search for ``jsreftest`` in ``mach try fuzzy``). SpiderMonkey shell jobs are shown as ``SM(...)``; most of them include JS shell runs of both jstests and jit-tests. |