From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- js/src/doc/Debugger/Conventions.md | 254 +++++ js/src/doc/Debugger/Debugger-API.md | 129 +++ js/src/doc/Debugger/Debugger.Environment.md | 182 ++++ js/src/doc/Debugger/Debugger.Frame.md | 489 ++++++++++ js/src/doc/Debugger/Debugger.Memory.md | 574 +++++++++++ js/src/doc/Debugger/Debugger.Object.md | 703 ++++++++++++++ js/src/doc/Debugger/Debugger.Script.md | 504 ++++++++++ js/src/doc/Debugger/Debugger.Source.md | 273 ++++++ js/src/doc/Debugger/Debugger.md | 538 +++++++++++ js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md | 224 +++++ js/src/doc/Debugger/Tutorial-Breakpoint.md | 129 +++ js/src/doc/Debugger/Tutorial-Debugger-Statement.md | 92 ++ js/src/doc/Debugger/alloc-plot-console.png | Bin 0 -> 82359 bytes js/src/doc/Debugger/console.png | Bin 0 -> 41695 bytes js/src/doc/Debugger/debugger-alert.png | Bin 0 -> 27770 bytes js/src/doc/Debugger/enable-chrome-devtools.png | Bin 0 -> 28465 bytes js/src/doc/Debugger/index.rst | 20 + .../Debugger/scratchpad-browser-environment.png | Bin 0 -> 30443 bytes js/src/doc/Debugger/shadows.svg | 1000 ++++++++++++++++++++ js/src/doc/SavedFrame/index.md | 95 ++ js/src/doc/build.rst | 314 ++++++ js/src/doc/gc.rst | 137 +++ js/src/doc/index.rst | 192 ++++ 23 files changed, 5849 insertions(+) create mode 100644 js/src/doc/Debugger/Conventions.md create mode 100644 js/src/doc/Debugger/Debugger-API.md create mode 100644 js/src/doc/Debugger/Debugger.Environment.md create mode 100644 js/src/doc/Debugger/Debugger.Frame.md create mode 100644 js/src/doc/Debugger/Debugger.Memory.md create mode 100644 js/src/doc/Debugger/Debugger.Object.md create mode 100644 js/src/doc/Debugger/Debugger.Script.md create mode 100644 js/src/doc/Debugger/Debugger.Source.md create mode 100644 js/src/doc/Debugger/Debugger.md create mode 100644 js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md create mode 100644 js/src/doc/Debugger/Tutorial-Breakpoint.md create mode 100644 js/src/doc/Debugger/Tutorial-Debugger-Statement.md create mode 100644 js/src/doc/Debugger/alloc-plot-console.png create mode 100644 js/src/doc/Debugger/console.png create mode 100644 js/src/doc/Debugger/debugger-alert.png create mode 100644 js/src/doc/Debugger/enable-chrome-devtools.png create mode 100644 js/src/doc/Debugger/index.rst create mode 100644 js/src/doc/Debugger/scratchpad-browser-environment.png create mode 100644 js/src/doc/Debugger/shadows.svg create mode 100644 js/src/doc/SavedFrame/index.md create mode 100644 js/src/doc/build.rst create mode 100644 js/src/doc/gc.rst create mode 100644 js/src/doc/index.rst (limited to 'js/src/doc') diff --git a/js/src/doc/Debugger/Conventions.md b/js/src/doc/Debugger/Conventions.md new file mode 100644 index 0000000000..76728924fc --- /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 value. Value is a + debuggee value. + +* `{ throw: value, stack: stack }` + + The code threw value as an exception. Value is a debuggee + value. stack 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 value immediately, + as if by executing a `return` statement. Value 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 value as an exception from the current bytecode + instruction. Value 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 value 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 value 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.html#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 `` 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("")` + * `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. + +* `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 ` + ``` + +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..85b17de1f4 --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Breakpoint.md @@ -0,0 +1,129 @@ +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 +
Click me!
+
Or me!
+ + ``` + +3. Visit the HTML file in your browser, and open the Browser Content Toolbox by + opening the Firefox menu, choosing “Web Developer”, and then “Browser + Content Toolbox”. If that item doesn't appear in the “Web Developer” 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 + Components.utils.import("resource://gre/modules/jsdebugger.jsm"); + Components.utils.import("resource://gre/modules/Console.jsm"); + + // This simply defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + addDebuggerToGlobal(this); + + // 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] + + + 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..6dd5583485 --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md @@ -0,0 +1,92 @@ +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 +
Click me!
+ ``` + +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. + Components.utils.import("resource://gre/modules/jsdebugger.jsm"); + addDebuggerToGlobal(window); + + // Create a 'Debugger' instance. + var dbg = new Debugger; + + // Get the current tab's content window, and make it a debuggee. + var w = gBrowser.selectedBrowser.contentWindow.wrappedJSObject; + dbg.addDebuggee(w); + + // When the debuggee executes a 'debugger' statement, evaluate + // the expression 'x' in that stack frame, and show its value. + dbg.onDebuggerStatement = function (frame) { + alert('hit debugger statement; x = ' + frame.eval('x').return); + } + ``` + +5. In the Scratchpad, ensure that no text is selected, and press the "Run" + button. + +6. Now, click on the text that says "Click me!" in the web page. This runs + the `div` element's `onclick` handler. When control reaches the + `debugger;` statement, `Debugger` calls your callback function, passing + a `Debugger.Frame` instance. Your callback function evaluates the + expression `x` in the given stack frame, and displays the alert: + + ![The Debugger callback displaying an alert][img-example-alert] + +7. Press "Run" in the Scratchpad again. Now, clicking on the "Click me!" + text causes *two* alerts to show---one for each `Debugger` + instance. + + Multiple `Debugger` instances can observe the same debuggee. Re-running + the code in the Scratchpad created a fresh `Debugger` instance, added + the same web page as its debuggee, and then registered a fresh + `debugger;` statement handler with the new instance. When you clicked + on the `div` element, both of them ran. This shows how any number of + `Debugger`-based tools can observe a single web page + simultaneously---although, since the order in which their handlers + run is not specified, such tools should probably only observe, and not + influence, the debuggee's behavior. + +8. Close the web page and the Scratchpad. + + Since both the Scratchpad's global object and the debuggee window are + now gone, the `Debugger` instances will be garbage collected, since + they can no longer have any visible effect on Firefox's behavior. The + `Debugger` API tries to interact with garbage collection as + transparently as possible; for example, if both a `Debugger.Object` + instance and its referent are not reachable, they will both be + collected, even while the `Debugger` instance to which the shadow + belonged continues to exist. + +[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 new file mode 100644 index 0000000000..5411724724 Binary files /dev/null and b/js/src/doc/Debugger/alloc-plot-console.png differ diff --git a/js/src/doc/Debugger/console.png b/js/src/doc/Debugger/console.png new file mode 100644 index 0000000000..9e9ce4ae74 Binary files /dev/null and b/js/src/doc/Debugger/console.png differ diff --git a/js/src/doc/Debugger/debugger-alert.png b/js/src/doc/Debugger/debugger-alert.png new file mode 100644 index 0000000000..2bf9362243 Binary files /dev/null and b/js/src/doc/Debugger/debugger-alert.png differ diff --git a/js/src/doc/Debugger/enable-chrome-devtools.png b/js/src/doc/Debugger/enable-chrome-devtools.png new file mode 100644 index 0000000000..033468991f Binary files /dev/null and b/js/src/doc/Debugger/enable-chrome-devtools.png differ 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 new file mode 100644 index 0000000000..534d0f9500 Binary files /dev/null and b/js/src/doc/Debugger/scratchpad-browser-environment.png differ 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Debugger + + + + + + + + + + + + Debugger.Object + Debugger.Environment + Debugger.Frame + Debugger.Object + Debugger.Object + Debugger.Script + + + + + global environment + + + global object: + Date; Math; ... + + + + + + + function alertLater(msg, delay) { setTimeout( function () { alert(msg); }, delay);} + + + + [[Code]]: + [[Scope]]: + + alertLater: + + + alertLater; + + + + + msg:delay: + 'xlerb'1000 + + + + + [[Code]]: + [[Scope]]: + + + + + + + anonymous() + + empty + + + + + + alert('xlerb') + + 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..059ab6d7ae --- /dev/null +++ b/js/src/doc/build.rst @@ -0,0 +1,314 @@ +Building SpiderMonkey +===================== + +**Before you begin, make sure you have the right build tools for your +computer:** + +* :ref:`Building Firefox On Linux` +* :ref:`Building Firefox On Windows` +* :ref:`Building Firefox On MacOS` +* `Others `__ + +This guide shows you how to build SpiderMonkey using ``mach``, which is Mozilla's multipurpose build tool. +For builds using ``configure && make``, and translations into other languages see +:ref:`these instructions on MDN `. + +These instructions assume you have a clone of `mozilla-central` 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:: + + # Build only the JS shell + ac_add_options --enable-application=js + + # Disable Optimization, for the most accurate debugging experience + ac_add_options --disable-optimize + # Enable the debugging tools: Assertions, debug only code etc. + ac_add_options --enable-debug + +To activate a particular ``MOZCONFIG``, set the environment variable: + +.. code:: + + 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:: + + $ cd + $ ./mach build + +.. note:: + + **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 `__  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:: + + $ ./mach jit-test + +Optimized Builds +~~~~~~~~~~~~~~~~ + +To switch to an optimized build, one need only have an optimized build ``MOZCONFIG``, +and then activate it. An example ``$HOME/mozconfigs/optimized`` ``MOZCONFIG`` +looks like this: + +.. code:: + + # Build only the JS shell + ac_add_options --enable-application=js + + # Enable optimization for speed + ac_add_options --enable-optimize + # Enable the debugging tools: Assertions, debug only code etc. + # For performance testing you would probably want to change this + # to --disable-debug. + ac_add_options --enable-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:: + + $ 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:: + + ac_add_options --target=aarch64-linux-android + +- Then compile as usual with `mach compile` 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:: + + 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 `_. + +- 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:: + + adb push \ + ~/.mozbuild/android-ndk-r20/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:: + + adb forward tcp:5039 tcp:5039 + +- Start `gdbserver` on the phone, passing the JS shell command line arguments + to gdbserver: + +.. code:: + + 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:: + + ~/.mozbuild/android-ndk-r20/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:: + + (gdb) target remote :5039 + (gdb) continue + +Cross-Compiling +~~~~~~~~~~~~~~~ + +It is possible to cross-compile a SpiderMonkey shell binary for another +architecture. For example, one can develop and compile on an x86 host while +building a ``js`` binary for AArch64 (ARM64). + +Unlike the rest of this document, this section will use the old-style +``configure`` script. + +To do this, first you must install the appropriate cross-compiler and system +libraries for the desired target. This is system- and distribution-specific. +Look for a package such as (using AArch64 as an example) +``aarch64-linux-gnu-gcc``. This document will assume that you have the +appropriate compiler and libraries; you can test this by compiling a C or C++ +hello-world program. + +You will also need the appropriate Rust compiler target support installed. For +example: + +.. code:: + + $ rustup target add aarch64-unknown-linux-gnu + +Once you have these prerequisites installed, you simply need to set a few +environment variables and configure the build appropriately: + +.. code:: + + $ cd js/src/ + $ export CC=aarch64-linux-gnu-gcc # adjust for target as appropriate. + $ export CXX=aarch64-linux-gnu-g++ + $ export AR=aarch64-linux-gnu-ar + $ export BINDGEN_CFLAGS="--sysroot /usr/aarch64-linux-gnu/sys-root" + $ mkdir BUILD_AARCH64.OBJ + $ cd BUILD_AARCH64.OBJ/ + $ ../configure --target=aarch64-unknown-linux-gnu + $ make + +This will produce a binary that is appropriate for the target architecture. +Note that you will not be able to run this binary natively on your host system; +to do so, keep reading to set up Qemu-based user-space emulation. + +Cross-Architecture Testing using Qemu +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is sometimes desirable to test a cross-compiled binary directly. Unlike the +target-ISA emulators that SpiderMonkey also supports, testing a cross-compiled +binary ensures that the actual binary, running as it would on the target +system, works appropriately. As far as the JS shell is concerned, it is running +on the target ISA. + +This is possible using the Qemu emulator. Qemu supports a mode called +"user-space emulation", where an individual process executes a binary that +targets a non-native ISA, and system calls are translated as appropriate to the +host system. This allows transparent execution of cross-compiled binaries. + +To set this up, you will need Qemu (check your system package manager) and +shared libraries for the target system. You will likely have the necessary +shared libraries already if you cross-compiled as described above. + +Then, write a small wrapper script that invokes the JS shell under Qemu. For +example: + +.. code:: + + #!/bin/sh + + # This is the binary compiled in the previous section. + CROSS_BIN=`dirname $0`/BUILD_AARCH64.OBJ/dist/bin/js + + # Adjust the library path as needed; this is prefixed to paths such as + # `/lib64/libc.so.64`, and so should contain `lib` (and perhaps `lib64`) + # subdirectories. + exec qemu-aarch64 -L /usr/aarch64-linux-gnu/sys-root/ $CROSS_BIN "$@" + +You can then invoke this wrapper as if it were a normal JS shell, and use it +with ``jit_test.py`` to run tests: + +.. code:: + + $ jit-test/jit_test.py ./js-cross-wrapper diff --git a/js/src/doc/gc.rst b/js/src/doc/gc.rst new file mode 100644 index 0000000000..19ad90ad85 --- /dev/null +++ b/js/src/doc/gc.rst @@ -0,0 +1,137 @@ +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 ` + - :ref:`Incremental ` + - :ref:`Generational ` + - :ref:`Partially concurrent ` + - :ref:`Parallel ` + - :ref:`Compacting ` + - :ref:`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 + +.. _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/index.rst b/js/src/doc/index.rst new file mode 100644 index 0000000000..40199f7e90 --- /dev/null +++ b/js/src/doc/index.rst @@ -0,0 +1,192 @@ +============ +SpiderMonkey +============ + +*SpiderMonkey* is the *JavaScript* and *WebAssembly* implementation library of +the *Mozilla Firefox* web browser. The implementation behaviour is defined by +the `ECMAScript `_ and `WebAssembly +`_ 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. + +Components of SpiderMonkey +########################## + +🧹 Garbage Collector +********************* + +*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` 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 +******************* + +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*. + +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. + +Cranelift +--------- + +This experimental alternative to *BaldrMonkey* is an optimizing WASM compiler +written in Rust. This currently is used on ARM64-based platforms (which do +not support *BaldrMonkey*). + + +Other documentation +################### + +.. toctree:: + :maxdepth: 1 + + build + gc + Debugger/index + SavedFrame/index + + +.. _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 -- cgit v1.2.3