From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- js/src/doc/Debugger/Conventions.md | 168 ++++ js/src/doc/Debugger/Debugger-API.md | 126 +++ js/src/doc/Debugger/Debugger.Environment.md | 153 ++++ js/src/doc/Debugger/Debugger.Frame.md | 328 +++++++ js/src/doc/Debugger/Debugger.Memory.md | 549 ++++++++++++ js/src/doc/Debugger/Debugger.Object.md | 559 ++++++++++++ js/src/doc/Debugger/Debugger.Script.md | 379 ++++++++ js/src/doc/Debugger/Debugger.Source.md | 220 +++++ js/src/doc/Debugger/Debugger.md | 496 ++++++++++ js/src/doc/Debugger/Tutorial-Alloc-Log-Tree.md | 222 +++++ js/src/doc/Debugger/Tutorial-Debugger-Statement.md | 82 ++ js/src/doc/Debugger/alloc-plot-console.png | Bin 0 -> 82359 bytes js/src/doc/Debugger/config.sh | 66 ++ js/src/doc/Debugger/debugger-alert.png | Bin 0 -> 27770 bytes js/src/doc/Debugger/enable-chrome-devtools.png | Bin 0 -> 28465 bytes .../Debugger/scratchpad-browser-environment.png | Bin 0 -> 30443 bytes js/src/doc/Debugger/shadows.svg | 997 +++++++++++++++++++++ js/src/doc/JITOptimizations/Outcomes.md | 581 ++++++++++++ js/src/doc/JITOptimizations/Strategies.md | 582 ++++++++++++ js/src/doc/JITOptimizations/config.sh | 15 + js/src/doc/README.md | 160 ++++ js/src/doc/SavedFrame/SavedFrame.md | 88 ++ js/src/doc/SavedFrame/config.sh | 6 + js/src/doc/format.sh | 100 +++ js/src/doc/lib/common.sh | 23 + js/src/doc/lib/dummy-config.sh | 24 + js/src/doc/lib/extract-watermark.sh | 24 + js/src/doc/lib/make-bibliography.sh | 81 ++ js/src/doc/lib/make-watermark.sh | 31 + js/src/doc/publish.sh | 83 ++ 30 files changed, 6143 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-Debugger-Statement.md create mode 100644 js/src/doc/Debugger/alloc-plot-console.png create mode 100644 js/src/doc/Debugger/config.sh 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/scratchpad-browser-environment.png create mode 100644 js/src/doc/Debugger/shadows.svg create mode 100644 js/src/doc/JITOptimizations/Outcomes.md create mode 100644 js/src/doc/JITOptimizations/Strategies.md create mode 100644 js/src/doc/JITOptimizations/config.sh create mode 100644 js/src/doc/README.md create mode 100644 js/src/doc/SavedFrame/SavedFrame.md create mode 100644 js/src/doc/SavedFrame/config.sh create mode 100755 js/src/doc/format.sh create mode 100644 js/src/doc/lib/common.sh create mode 100644 js/src/doc/lib/dummy-config.sh create mode 100755 js/src/doc/lib/extract-watermark.sh create mode 100755 js/src/doc/lib/make-bibliography.sh create mode 100755 js/src/doc/lib/make-watermark.sh create mode 100755 js/src/doc/publish.sh (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 000000000..e8bd3ee43 --- /dev/null +++ b/js/src/doc/Debugger/Conventions.md @@ -0,0 +1,168 @@ +# General Conventions + +This page describes general conventions used in the [`Debugger`][debugger] API, +and defines some terminology used throughout the specification. + + +## Properties + +Properties of objects that comprise the `Debugger` interface, and those +that the interface creates, follow some general conventions: + +- Instances and prototypes are extensible; you can add your own properties + and methods to them. + +- Properties are configurable. This applies to both "own" and prototype + properties, and to both methods and data properties. (Leaving these + properties open to redefinition will hopefully make it easier for + JavaScript debugger code to cope with bugs, bug fixes, and changes in the + interface over time.) + +- Method properties are writable. + +- We prefer inherited accessor properties to own data properties. Both are + read using the same syntax, but inherited accessors seem like a more + accurate reflection of what's going on. Unless otherwise noted, these + properties have getters but no setters, as they cannot meaningfully be + assigned to. + + +## Debuggee Values + +The `Debugger` interface follows some conventions to help debuggers safely +inspect and modify the debuggee's objects and values. Primitive values are +passed freely between debugger and debuggee; copying or wrapping is handled +transparently. Objects received from the debuggee (including host objects +like DOM elements) are fronted in the debugger by `Debugger.Object` +instances, which provide reflection-oriented methods for inspecting their +referents; see `Debugger.Object`, below. + +Of the debugger's objects, only `Debugger.Object` instances may be passed +to the debuggee: when this occurs, the debuggee receives the +`Debugger.Object`'s referent, not the `Debugger.Object` instance itself. + +In the descriptions below, the term "debuggee value" means either a +primitive value or a `Debugger.Object` instance; it is a value that might +be received from the debuggee, or that could be passed to the debuggee. + + +## Debuggee Code + +Each `Debugger` instance maintains a set of global objects that, taken +together, comprise the debuggee. Code evaluated in the scope of a debuggee +global object, directly or indirectly, is considered *debuggee code*. +Similarly: + +- a *debuggee frame* is a frame running debuggee code; + +- a *debuggee function* is a function that closes over a debuggee + global object (and thus the function's code is debuggee code); + +- a *debuggee environment* is an environment whose outermost + enclosing environment is a debuggee global object; and + +- a *debuggee script* is a script containing debuggee code. + + +## Completion Values + +When a debuggee stack frame completes its execution, or when some sort +of debuggee call initiated by the debugger finishes, the `Debugger` +interface provides a value describing how the code completed; these are +called *completion values*. A completion value has one of the +following forms: + +{ return: value } +: The code completed normally, returning value. Value is a + debuggee value. + +{ throw: value } +: The code threw value as an exception. Value is a debuggee + value. + +`null` +: The code was terminated, as if by the "slow script" dialog box. + +If control reaches the end of a generator frame, the completion value is +{ throw: stop } where stop is a +`Debugger.Object` object representing the `StopIteration` object being +thrown. + + +## Resumption Values + +As the debuggee runs, the `Debugger` interface calls various +debugger-provided handler functions to report the debuggee's behavior. +Some of these calls can return a value indicating how the debuggee's +execution should continue; these are called *resumption values*. A +resumption value has one of the following forms: + +`undefined` +: The debuggee should continue execution normally. + +{ return: value } +: Return value immediately as the current value of the function. + Value must be a debuggee value. (Most handler functions support + this, except those whose descriptions say otherwise.) If the function + was called as a constructor (that is, via a `new` expression), then + 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. + If the frame is a generator or async function, then value must + conform to the iterator protocol: it must be a non-proxy object of the form + { done: boolean, value: v }, where + both `done` and `value` are ordinary properties. + +{ throw: value } +: Throw value as an exception from the current bytecode + instruction. Value must be a debuggee value. + +`null` +: Terminate the debuggee, as if it had been cancelled by the "slow script" + dialog box. + +If a function that would normally return a resumption value to indicate +how the debuggee should continue instead throws an exception, we never +propagate such an exception to the debuggee; instead, we call the +associated `Debugger` instance's `uncaughtExceptionHook` property, as +described below. + + +## Timestamps + +Timestamps are expressed in units of milliseconds since an arbitrary, +but fixed, epoch. The resolution of timestamps is generally greater +than milliseconds, though no specific resolution is guaranteed. + + +## The `Debugger.DebuggeeWouldRun` Exception + +Some debugger operations that appear to simply inspect the debuggee's state +may actually cause debuggee code to run. For example, reading a variable +might run a getter function on the global or on a `with` expression's +operand; and getting an object's property descriptor will run a handler +trap if the object is a proxy. To protect the debugger's integrity, only +methods whose stated purpose is to run debuggee code can do so. These +methods are called [invocation functions][inv fr], and they follow certain +common conventions to report the debuggee's behavior safely. For other +methods, if their normal operation would cause debuggee code to run, they +throw an instance of the `Debugger.DebuggeeWouldRun` exception. + +If there are debugger frames on stack from multiple Debugger instances, the +thrown exception is an instance of the topmost locking debugger's global's +`Debugger.DebuggeeWouldRun`. + +A `Debugger.DebuggeeWouldRun` exception may have a `cause` property, +providing more detailed information on why the debuggee would have run. The +`cause` property's value is one of the following strings: + + cause value meaning + -------------------- -------------------------------------------------------------------------------- + "proxy" Carrying out the operation would have caused a proxy handler to run. + "getter" Carrying out the operation would have caused an object property getter to run. + "setter" Carrying out the operation would have caused an object property setter to run. + +If the system can't determine why control attempted to enter the debuggee, +it will leave the exception's `cause` property undefined. diff --git a/js/src/doc/Debugger/Debugger-API.md b/js/src/doc/Debugger/Debugger-API.md new file mode 100644 index 000000000..10f6ebf89 --- /dev/null +++ b/js/src/doc/Debugger/Debugger-API.md @@ -0,0 +1,126 @@ +# The `Debugger` Interface + +Mozilla's JavaScript engine, SpiderMonkey, provides a debugging interface +named `Debugger` which lets JavaScript code observe and manipulate the +execution of other JavaScript code. Both Firefox's built-in developer tools +and the Firebug add-on use `Debugger` to implement their JavaScript +debuggers. However, `Debugger` is quite general, and can be used to +implement other kinds of tools like tracers, coverage analysis, +patch-and-continue, and so on. + +`Debugger` has three essential qualities: + +- It is a *source level* interface: it operates in terms of the JavaScript + language, not machine language. It operates on JavaScript objects, stack + frames, environments, and code, and presents a consistent interface + regardless of whether the debuggee is interpreted, compiled, or + optimized. If you have a strong command of the JavaScript language, you + should have all the background you need to use `Debugger` successfully, + even if you have never looked into the language's implementation. + +- It is for use *by JavaScript code*. JavaScript is both the debuggee + language and the tool implementation language, so the qualities that make + JavaScript effective on the web can be brought to bear in crafting tools + for developers. As is expected of JavaScript APIs, `Debugger` is a + *sound* interface: using (or even misusing) `Debugger` should never cause + Gecko to crash. Errors throw proper JavaScript exceptions. + +- It is an *intra-thread* debugging API. Both the debuggee and the code + using `Debugger` to observe it must run in the same thread. Cross-thread, + cross-process, and cross-device tools must use `Debugger` to observe the + debuggee from within the same thread, and then handle any needed + communication themselves. (Firefox's builtin tools have a + [protocol][protocol] defined for this purpose.) + +In Gecko, the `Debugger` API is available to chrome code only. By design, +it ought not to introduce security holes, so in principle it could be made +available to content as well; but it is hard to justify the security risks +of the additional attack surface. + +The `Debugger` API cannot currently observe self-hosted JavaScript. This is not +inherent in the API's design, but simply that the self-hosting infrastructure +isn't prepared for the kind of invasions the `Debugger` API can perform. + + +## Debugger Instances and Shadow Objects + +`Debugger` reflects every aspect of the debuggee's state as a JavaScript +value---not just actual JavaScript values like objects and primitives, +but also stack frames, environments, scripts, and compilation units, which +are not normally accessible as objects in their own right. + +Here is a JavaScript program in the process of running a timer callback function: + +![A running JavaScript program and its Debugger shadows][img-shadows] + +This diagram shows the various types of shadow objects that make up the +Debugger API (which all follow some [general conventions][conventions]): + +- A [`Debugger.Object`][object] represents a debuggee object, offering a + reflection-oriented API that protects the debugger from accidentally + invoking getters, setters, proxy traps, and so on. + +- A [`Debugger.Script`][script] represents a block of JavaScript + code---either a function body or a top-level script. Given a + `Debugger.Script`, one can set breakpoints, translate between source + positions and bytecode offsets (a deviation from the "source level" + design principle), and find other static characteristics of the code. + +- A [`Debugger.Frame`][frame] represents a running stack frame. You can use + these to walk the stack and find each frame's script and environment. You + can also set `onStep` and `onPop` handlers on frames. + +- A [`Debugger.Environment`][environment] represents an environment, + associating variable names with storage locations. Environments may + belong to a running stack frame, captured by a function closure, or + reflect some global object's properties as variables. + +The [`Debugger`][debugger-object] instance itself is not really a shadow of +anything in the debuggee; rather, it maintains the set of global objects +which are to be considered debuggees. A `Debugger` observes only execution +taking place in the scope of these global objects. You can set functions to +be called when new stack frames are pushed; when new code is loaded; and so +on. + +Omitted from this picture are [`Debugger.Source`][source] instances, which +represent JavaScript compilation units. A `Debugger.Source` can furnish a +full copy of its source code, and explain how the code entered the system, +whether via a call to `eval`, a ` + ``` + +6) Open the browser console (Menu Button > Developer > Browser Console), and + then evaluate the expression `demoTrackAllocations()` in the browser + console. This begins logging allocations in the current browser tab. + +7) In the browser tab, click on the text that says "Click here...". The event + handler should add some text to the end of the page. + +8) Back in the browser console, evaluate the expression + `demoPlotAllocations()`. This stops logging allocations, and displays a tree + of allocations: + + ![An allocation plot, displayed in the console][img-alloc-plot] + + The numbers at the left edge of each line show the total number of objects + allocated at that site or at sites called from there. After the count, we + see the function name, and the source code location of the call site or + allocation. + + The `(root)` node's count includes objects allocated in the content page by + the web browser, like DOM events. Indeed, this display shows that + `popup.xml` and `content.js`, which are internal components of Firefox, + allocated more objects in the page's compartment than the page itself. (We + will probably revise the allocation log to present such allocations in a way + that is more informative, and that exposes less of Firefox's internal + structure.) + + As expected, the `onclick` handler is responsible for all allocation done by + the page's own code. (The line number for the onclick handler is `1`, + indicating that the allocating call is located on line one of the handler + text itself. We will probably change this to be the line number within + `page.html`, not the line number within the handler code.) + + The `onclick` handler calls `doDivsAndSpans`, which calls `divsAndSpans`, + which invokes closures of `factory` to do all the actual allocation. (It is + unclear why `spanFactory` allocated thirteen objects, despite being called + only ten times.) + diff --git a/js/src/doc/Debugger/Tutorial-Debugger-Statement.md b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md new file mode 100644 index 000000000..58360ff6f --- /dev/null +++ b/js/src/doc/Debugger/Tutorial-Debugger-Statement.md @@ -0,0 +1,82 @@ +Tutorial: Evaluate an Expression When a debugger; Statement Is Executed +======================================================================= + +This page shows how you can try out the [`Debugger` API][debugger] yourself +using Firefox's Scratchpad. We use the API to evaluate an expression in the web +page whenever it executes a JavaScript `debugger;` statement. + +1) Visit the URL `about:config`, and set the `devtools.chrome.enabled` + preference to `true`: + + ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] + +2) Save the following HTML text to a file, and visit the file in your + browser: + + ```language-html +
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: + + ```language-js + // This simply defines 'Debugger' in this Scratchpad; + // it doesn't actually start debugging anything. + Components.utils.import("resource://gre/modules/jsdebugger.jsm"); + addDebuggerToGlobal(window); + + // Create a 'Debugger' instance. + var dbg = new Debugger; + + // Get the current tab's content window, and make it a debuggee. + var w = gBrowser.selectedBrowser.contentWindow.wrappedJSObject; + dbg.addDebuggee(w); + + // When the debuggee executes a 'debugger' statement, evaluate + // the expression 'x' in that stack frame, and show its value. + dbg.onDebuggerStatement = function (frame) { + alert('hit debugger statement; x = ' + frame.eval('x').return); + } + ``` + +5) In the Scratchpad, ensure that no text is selected, and press the "Run" + button. + +6) Now, click on the text that says "Click me!" in the web page. This runs + the `div` element's `onclick` handler. When control reaches the + `debugger;` statement, `Debugger` calls your callback function, passing + a `Debugger.Frame` instance. Your callback function evaluates the + expression `x` in the given stack frame, and displays the alert: + + ![The Debugger callback displaying an alert][img-example-alert] + +7) Press "Run" in the Scratchpad again. Now, clicking on the "Click me!" + text causes *two* alerts to show---one for each `Debugger` + instance. + + Multiple `Debugger` instances can observe the same debuggee. Re-running + the code in the Scratchpad created a fresh `Debugger` instance, added + the same web page as its debuggee, and then registered a fresh + `debugger;` statement handler with the new instance. When you clicked + on the `div` element, both of them ran. This shows how any number of + `Debugger`-based tools can observe a single web page + simultaneously---although, since the order in which their handlers + run is not specified, such tools should probably only observe, and not + influence, the debuggee's behavior. + +8) Close the web page and the Scratchpad. + + Since both the Scratchpad's global object and the debuggee window are + now gone, the `Debugger` instances will be garbage collected, since + they can no longer have any visible effect on Firefox's behavior. The + `Debugger` API tries to interact with garbage collection as + transparently as possible; for example, if both a `Debugger.Object` + instance and its referent are not reachable, they will both be + collected, even while the `Debugger` instance to which the shadow + belonged continues to exist. diff --git a/js/src/doc/Debugger/alloc-plot-console.png b/js/src/doc/Debugger/alloc-plot-console.png new file mode 100644 index 000000000..541172472 Binary files /dev/null and b/js/src/doc/Debugger/alloc-plot-console.png differ diff --git a/js/src/doc/Debugger/config.sh b/js/src/doc/Debugger/config.sh new file mode 100644 index 000000000..eda4ed8d7 --- /dev/null +++ b/js/src/doc/Debugger/config.sh @@ -0,0 +1,66 @@ +### Description of Debugger docs: how to format, where to install. +### See js/src/doc/README.md for a description. + +base-url https://developer.mozilla.org/en-US/docs/Tools/ + +markdown Debugger-API.md Debugger-API + label 'debugger' "The Debugger API" + +markdown Conventions.md Debugger-API/Conventions + label 'conventions' "Debugger API: General Conventions" + label 'dbg code' '#debuggee-code' "Debugger API: General Conventions: Debuggee Code" + label 'cv' '#completion-values' "Debugger API: General Conventions: Completion Values" + label 'rv' '#resumption-values' "Debugger API: General Conventions: Resumption Values" + label 'timestamps' '#timestamps' "Debugger API: General Conventions: Timestamps" + label 'wouldrun' '#the-debugger.debuggeewouldrun-exception' "Debugger API: DebuggeeWouldRun" + +markdown Debugger.md Debugger-API/Debugger + label 'debugger-object' "The Debugger object" + label 'add' '#addDebuggee' "The Debugger object: addDebuggee" + +markdown Debugger.Environment.md Debugger-API/Debugger.Environment + label 'environment' "Debugger.Environment" + +markdown Debugger.Frame.md Debugger-API/Debugger.Frame + label 'frame' "Debugger.Frame" + label 'vf' '#visible-frames' "Debugger.Frame: Visible Frames" + label 'inv fr' '#invf' "Debugger.Frame: Invocation Frames" + label 'fr eval' '#eval' "Debugger.Frame: Eval" + +markdown Debugger.Object.md Debugger-API/Debugger.Object + label 'object' "Debugger.Object" + label 'allocation-site' '#allocationsite' "Debugger.Object: allocationSite" + +markdown Debugger.Script.md Debugger-API/Debugger.Script + label 'script' "Debugger.Script" + +markdown Debugger.Source.md Debugger-API/Debugger.Source + label 'source' "Debugger.Source" + +markdown Debugger.Memory.md Debugger-API/Debugger.Memory + label 'memory' "Debugger.Memory" + label 'tracking-allocs' '#trackingallocationsites' "Debugger.Memory: trackingAllocationSites" + label 'drain-alloc-log' '#drain-alloc-log' "Debugger.Memory: drainAllocationsLog" + label 'max-alloc-log' '#max-alloc-log' "Debugger.Memory: maxAllocationsLogLength" + label 'alloc-sampling-probability' '#alloc-sampling-probability' "Debugger.Memory: allocationSamplingProbability" + label 'take-census' '#take-census' "Debugger.Memory: takeCensus" + +markdown Tutorial-Debugger-Statement.md Debugger-API/Tutorial-Debugger-Statement + label 'tut debugger' "Tutorial: the debugger; statement" + +markdown Tutorial-Alloc-Log-Tree.md Debugger-API/Tutorial-Allocation-Log-Tree + label 'tut alloc log' "Tutorial: the allocation log" + +# Images: +RBASE=https://mdn.mozillademos.org/files +resource 'img-shadows' shadows.svg $RBASE/7225/shadows.svg +resource 'img-chrome-pref' enable-chrome-devtools.png $RBASE/7233/enable-chrome-devtools.png +resource 'img-scratchpad-browser' scratchpad-browser-environment.png $RBASE/7229/scratchpad-browser-environment.png +resource 'img-example-alert' debugger-alert.png $RBASE/7231/debugger-alert.png +resource 'img-alloc-plot' alloc-plot-console.png $RBASE/8461/alloc-plot-console.png + +# External links: +absolute-label 'protocol' https://wiki.mozilla.org/Remote_Debugging_Protocol "Remote Debugging Protocol" +absolute-label 'saved-frame' https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/SavedFrame "SavedFrame" +absolute-label 'bernoulli-trial' https://en.wikipedia.org/wiki/Bernoulli_trial "Bernoulli Trial" +absolute-label 'promise' https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise" diff --git a/js/src/doc/Debugger/debugger-alert.png b/js/src/doc/Debugger/debugger-alert.png new file mode 100644 index 000000000..2bf936224 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 000000000..033468991 Binary files /dev/null and b/js/src/doc/Debugger/enable-chrome-devtools.png differ 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 000000000..534d0f950 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 000000000..5774fc78f --- /dev/null +++ b/js/src/doc/Debugger/shadows.svg @@ -0,0 +1,997 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/JITOptimizations/Outcomes.md b/js/src/doc/JITOptimizations/Outcomes.md new file mode 100644 index 000000000..b0eb9c43a --- /dev/null +++ b/js/src/doc/JITOptimizations/Outcomes.md @@ -0,0 +1,581 @@ +# JIT Optimization Outcomes + +SpiderMonkey's optimizing JIT, IonMonkey, uses a number of different +optimization strategies to speed up various operations. The most commonplace +operations that are relevant for fast program execution are property accesses +and function calls. + +This page documents the meaning of different optimization outcomes. + +## General Outcomes + +General outcomes shared between various optimization strategies. + +### GenericFailure + +The optimization attempt failed, and the reason was not recorded. + +### GenericSuccess + +Optimization succeeded. + +### Disabled + +The optimization has been explicitly disallowed. + +### NoTypeInfo + +Optimization failed because there was no type information associated with +object containing the property. This failure mode is unlikely, and occurs +if the target object is obtained in some roundabout way. + +### NoAnalysisInfo + +TODO + +### NoShapeInfo + +The baseline compiler recorded no usable shape information for this operation. + +### UnknownObject + +The type of the object is not known. This can happen if the operation sees many different types of objects, and so the type of the input to the operation cannot be resolved to a single type. + +### UnknownProperties + +Optimization failed because the object containing the property was marked +as having unknown properties. This can happen if too many properties are +defined on the object, or if `delete` is used to remove one of the object's +properties. + +### Singleton + +One of the types present in the typeset was a singleton type, preventing the optimization from being enabled. + +### NotSingleton + +Optimization failed because the object containing the property did not +have a 'singleton' type. Singleton types are assigned to objects that are +"one of a kind", such as global objects, literal objects declared in the +global scope, and top-level function objects. + +### NotFixedSlot + +The property being accessed is not stored at a known location in the object. This can occur if one of the expected types of objects to be used in this operation has unknown properties, or if different instances of the object store the property at different locations (for example, some instances have the property assigned in a different order than others). + +### InconsistentFixedSlot + +The property being accessed is not stored at a known location in the object. This can occur if the operation is polymorphic on different object types and one or more of the object types contain the property at a different slot than the others. + +### NotObject + +Optimization failed because the stored in the property could potentially +be a non-object value. Since only objects can be uniquely typed, the +optimization strategy fails in this case. + +### NotStruct + +The object holding the property is not a typed struct object. + +### NotUnboxed + +The object whose property is being accessed is not formatted as an +unboxed object. + +### UnboxedConvertedToNative + +The object whose property is being accessed was previously unboxed, +but was deoptimized and converted to a native object. + +### StructNoField + +The unboxed property being accessed does not correspond to a field on typed +object. + +### InconsistentFieldType + +The type of an unboxed field is not consistent across all the different types of objects it could be accessed from. + +### InconsistentFieldOffset + +The offset of an unboxed field is not consistent across all the different types of objects it could be accessed from. + +### NeedsTypeBarrier + +Optimization failed because somehow the property was accessed in a way +that returned a different type than the expected constant. This is an +unlikely failure mode, and should not occur. + +### InDictionaryMode + +The object whose property is being accessed is in dictionary mode. Objects which are used in ways that suggest they are hashtables, are turned into dictionary objects and their types marked as such. + +### NoProtoFound + +A prototype object was not found for all the object used by this operation. + +### MultiProtoPaths + +Objects used in this operation had differing prototypes. + +### NonWritableProperty + +The property being assigned to is not writable for some types of objects which are used in this operation. + +### ProtoIndexedProps + +The object being accessed has indexed properties that are exotic (for example, defined as a property on a prototype object and left as a hole in the underlying object). + +### ArrayBadFlags + +The array being accessed may have flags that the optimization strategy cannot handle. For example, if the array has sparse indexes, or has indexes that overflow the array's length, the optimization strategy may fail. + +### ArrayDoubleConversion + +The type-system indicates that some arrays at this site should be converted to packed arrays of doubles, while others should not. The optimization strategy fails for this condition. + +### ArrayRange + +Could not accurately calculate the range attributes of an inline array creation. + +### ArraySeenNegativeIndex + +Arrays at this element access location have seen negative indexes. + +### TypedObjectHasDetachedBuffer + +The storage for the typed object being accessed at this location might be a detached ArrayBuffer. (This can happen if the typed object, or its underlying buffer as accessed using `TypedObject.storage(typedObject).buffer`, is transferred using the structured clone algorithm.) + +### TypedObjectArrayRange + +Failed to do range check of element access on a typed object. + +### AccessNotDense + +### AccessNotSimdObject + +The observed type of the target of the property access doesn't guarantee +that it is a SIMD object. + +### AccessNotTypedObject + +The observed type of the target of the property access doesn't guarantee +that it is a TypedObject. + +### AccessNotTypedArray + +The observed type of the target of the property access doesn't guarantee +that it is a TypedArray. + +### AccessNotString + +The observed type of the target of the property access doesn't guarantee +that it is a String. + +### OperandNotString + +Optimization failed because of failing to speculate the operand is a string. + +### OperandNotNumber + +Optimization failed because of failing to speculate the operand is a number. + +### OperandNotStringOrNumber + +Optimization failed because of failing to speculate the operand is a string or a number. + +### OperandNotSimpleArith + +Optimization failed because of failing to speculate the operand is a simple arithmetic type. I.e. definitely not an object, string, symbol or internal magic type. + +### StaticTypedArrayUint32 + +Typed Arrays of uint32 values are not yet fully optimized. + +### StaticTypedArrayCantComputeMask +### OutOfBounds + +The element access has been observed to be out of the length bounds of +the string being accessed. + +### GetElemStringNotCached + +IonMonkey does not generate inline caches for element accesses on +target values which may be strings. + +### NonNativeReceiver + +IonMonkey does not generate inline caches for indexed element accesses on +target values which may be non-native objects (e.g. DOM elements). + +### IndexType + +IonMonkey does not generate inline caches for element reads in which +the keys have never been observed to be a String, Symbol, or Int32. + +### SetElemNonDenseNonTANotCached + +IonMonkey only generates inline caches for element accesses which are +either on dense objects (e.g. dense Arrays), or Typed Arrays. + +### NoSimdJitSupport + +Optimization failed because SIMD JIT support was not enabled. + +### SimdTypeNotOptimized + +The type observed as being retrieved from this property access did not +match an optimizable type. + +### HasCommonInliningPath + +Inlining was abandoned because the inlining call path was repeated. A +repeated call path is indicative of a potentially mutually recursive +function call chain. + +### Inlined + +Method has been successfully inlined. + +### DOM + +Successfully optimized a call to a DOM getter or setter function. + +### Monomorphic + +Successfully optimized a guarded monomorphic property access. This +property access is about twice as expensive as a definite property access. +A guarded monomorphic property access is basically a direct memory load +from an object, guarded by a type or shape check. In pseucodcode: + + if HiddenType(obj) === CONSTANT_TYPE: + return obj[CONSTANT_SLOT_OFFSET] + else + BAILOUT() + +### Polymorphic + +Successfully optimized a guarded polymorphic property access. This +property access is similar to a monomorphic property access, except +that multiple different hidden types have been observed, and each of +them are being checked for. In pseudocode: + + if HiddenType(obj) === CONSTANT_TYPE_1: + return obj[CONSTANT_SLOT_OFFSET_1] + elif HiddenType(obj) === CONSTANT_TYPE_2: + return obj[CONSTANT_SLOT_OFFSET_2] + ... + elif HiddenType(obj) === CONSTANT_TYPE_K: + return obj[CONSTANT_SLOT_OFFSET_K] + else + BAILOUT() + +## Inline Cache Outcomes + +Outcomes describing inline cache stubs that were generated. + +### ICOptStub_GenericSuccess + +Generic success condition for generating an optimized inline cache stub. + +### ICGetPropStub_ReadSlot + +An inline cache property read which loads a value from a known slot +location within an object. + +### ICGetPropStub_CallGetter + +An inline cache property read which calls a getter function. + +### ICGetPropStub_ArrayLength + +An inline cache property read which retrieves the length of an array +object. + +### ICGetPropStub_UnboxedRead + +An inline cache property read which retrieves an value from an unboxed +object. + +### ICGetPropStub_UnboxedReadExpando + +An inline cache property read which retrieves an value from the expando +component of an unboxed object. + +### ICGetPropStub_UnboxedArrayLength + +An inline cache property read which retrieves the length of an unboxed +array object. + +### ICGetPropStub_TypedArrayLength + +An inline cache property read which retrieves the length of an typed array +object. + +### ICGetPropStub_DOMProxyShadowed + +An inline cache property read which retrieves a shadowed property from a +DOM object. A shadowed property is an inbuilt DOM property that has been +overwritten by JS code. + +### ICGetPropStub_DOMProxyUnshadowed + +An inline cache property read which retrieves an unshadowed property from +a DOM object. + +### ICGetPropStub_GenericProxy + +An inline cache property read which retrieves the property by calling into +a generic proxy trap. + +### ICGetPropStub_ArgumentsLength + +An inline cache property read which reads the length value from an +arguments object. + +### ICSetPropStub_Slot + +An inline cache property write which sets a value to a known slot location +in an object. + +### ICSetPropStub_GenericProxy + +An inline cache property write which sets the value by calling into a +generic proxy trap. + +### ICSetPropStub_DOMProxyShadowed + +An inline cache property write which sets a shadowed property from a DOM +object. A shadowed property is an inbuilt DOM property that has been +overwritten by JS code. + +### ICSetPropStub_DOMProxyUnshadowed + +An inline cache property write which sets an unshadowed property on an +DOM object. + +### ICSetPropStub_CallSetter + +An inline cache property write which calls a setter function on an object. + +### ICSetPropStub_AddSlot + +An inline cache property write which adds a new slot to an object, +because the object is known to not have such a property already. + +### ICSetPropStub_SetUnboxed + +An inline cache property write which sets an unboxed value on an unboxed +object. + +### ICGetElemStub_ReadSlot + +An inline cache element read which loads a value from a known property +slot in an object. + +### ICGetElemStub_CallGetter + +An inline cache element read which calls a getter function on the object. + +### ICGetElemStub_ReadUnboxed + +An inline cache element read which loads an unboxed value from a known +property slot on an unboxed object. + +### ICGetElemStub_Dense + +An inline cache element read which loads an element from a dense array. + +### ICGetElemStub_DenseHole + +An inline cache element read which loads an element from a dense array +which might have a hole. + +### ICGetElemStub_TypedArray + +An inline cache element read which loads an element from a typed array. + +### ICGetElemStub_ArgsElement + +An inline cache element read which loads an element from an `arguments` +object. + +### ICGetElemStub_ArgsElementStrict + +An inline cache element read which loads an element from an `arguments` +object in strict mode. + +### ICSetElemStub_Dense + +An inline cache element write which sets an element on a dense array. + +### ICSetElemStub_TypedArray + +An inline cache element write which sets an element on a typed array. + +### ICNameStub_ReadSlot + +An inline cache element which loads a bare variable name from a slot on +a scope chain object. + +### ICNameStub_CallGetter + +An inline cache element which loads a bare variable name by calling a +getter function on the global object. + +### ICNameStub_TypeOfNoProperty + +An inline cache element which loads undefined for the type +of a missing property. + +## Call Inlining Outcomes + +Optimization outcomes of attempts to inline function calls. + +### CantInlineGeneric + +Generic failure-to-inline outcome. + +### CantInlineClassConstructor + +Cannot inline a class constructor function. + +### CantInlineExceededDepth + +Inlining stopped because inlining depth was exceeded. + +### CantInlineExceededTotalBytecodeLength + +Inlining stopped because the total bytecode length of all inlined +functions exceeded a threshold. + +### CantInlineBigCaller + +Inlining was aborted because the caller function is very large. + +### CantInlineBigCallee + +Inlining was aborted because the callee function is very large. + +### CantInlineBigCalleeInlinedBytecodeLength + +Inlining was aborted because the callee is known to inline a lot of code. + +### CantInlineNotHot + +Inlining was aborted because the callee is not hot enough. + +### CantInlineNotInDispatch + +This failure mode relates to inlining a method call of the form: + + obj.method() + +where a hidden type check on the target object reveals the method to be +inlined. If the property access for `method` does not identify +the method to be inlined, the inlining fails with this code. + +### CantInlineNativeBadType + +Inlining attempt of native function was aborted because the hidden +type for the function, or the result of the function, was not +compatible with one of the known inline-able native functions. + +### CantInlineNoTarget + +Unable to inline function call. The callee (target) function, f in f(x), +is not known at JIT time. + +### CantInlineNotInterpreted + +Unable to inline function call. The callee function is not an interpreted +function. For example, it could be a native function for which Ion has no +built-in specialization. + +### CantInlineNoBaseline + +Unable to inline function call. The interpreted callee function could not +be compiled by the Baseline compiler. + +### CantInlineLazy + +Unable to inline function call. The interpreted callee function has a +lazy, compiled on-demand script instead of an already compiled script. + +### CantInlineNotConstructor + +Unable to inline function call. Rare. The interpreted callee function is +invoked with new but cannot be called as a constructor. Property +accessors, Function.prototype, and arrow (=>) functions cannot be called +as constructors. + +### CantInlineDisableIon + +Unable to inline function call. The interpreted callee function has been +explicitly blacklisted against Ion compilation. + +### CantInlineTooManyArgs + +Unable to inline function call. The interpreted callee function either has +too many parameters or is called with too many arguments. These thresholds +are subject to change. + +### CantInlineRecursive + +Unable to inline function call. The interpreted callee function recurs for +more than one level. The first level of recursion is inlineable. + +### CantInlineHeavyweight + +Unable to inline function call. The interpreted callee function contains +variables bindings that are closed over. For example, +`function f() { var x; return function () { x++; } }` +closes over x in f. + +### CantInlineNeedsArgsObj + +Unable to inline function call. The interpreted callee function requires +an arguments object to be created. + +### CantInlineDebuggee + +Unable to inline function call. The interpreted callee function is being +debugged by the Debugger API. + +### CantInlineUnknownProps + +Unable to inline function call. The engine knows nothing definite about +the type of the callee function object. + +### CantInlineUnreachable + +Unable to inline function call. The call site has not been observed to +have ever been executed. It lacks observed type information for its arguments, +its return value, or both. + +### CantInlineBound + +Unable to inline function call. The interpreted callee is a bound function +generated from `Function.prototype.bind` that failed some sub-checks. +(expand) + +### CantInlineNativeNoSpecialization + +Unable to inline function call. Ion does not have built-in specialization for +the native (implemented in C++) callee function. + +### CantInlineNativeBadForm + +Unable to inline function call. The native callee function was called with an +unsupported number of arguments, or calling non-constructing functions with new. + +### CantInlineBadType + +Unable to inline function call. The native callee function was called with +arguments with types that the built-in specialization does not support. For +example, calling Math functions on objects. + +### CantInlineNativeNoTemplateObj + +Unable to inline function call. Cannot inline a native constructor (e.g., new +Array) because no template object was cached by the Baseline compiler. (expand) diff --git a/js/src/doc/JITOptimizations/Strategies.md b/js/src/doc/JITOptimizations/Strategies.md new file mode 100644 index 000000000..d4feef310 --- /dev/null +++ b/js/src/doc/JITOptimizations/Strategies.md @@ -0,0 +1,582 @@ +# JIT Optimization Strategies + +SpiderMonkey's optimizing JIT, IonMonkey, uses a number of different +optimization strategies to speed up various operations. The most commonplace +operations that are relevant for fast program execution are property accesses +and function calls. + +Optimization information is currently collected for the following operations: + +- [BinaryArith](#binaryarith) (`+-/*%`) +- [GetProperty](#getprop) (`obj.prop`) +- [SetProperty](#setprop) (`obj.prop = val`) +- [GetElement](#getelem) (`obj[elemName]`) +- [SetElement](#setelem) (`obj[elemName] = val`) +- [Call](#call) (`func(...)`) + +At each operation site, IonMonkey tries a battery of strategies, from +the most optimized but most restrictive to the least optimized but least +restrictive. For each strategy attempted, its outcome is tracked. An +outcome is either success or a reason why the strategy failed to apply. + +This page documents the various optimization strategies and their outcomes. It +provides information on what they attempt to do, what general level of +speed-up they provide, what kind of program characteristics can prevent them +from being used, and common ways to enable the engine to utilize that +optimization. + +## BinaryArith + +### BinaryArith_Concat + +Attempts to optimize an addition to string concatenation. The types of the operands are checked to contain a hint of being a concatenation. Both have to be string or one has to be a string and the other a type that easily can get converted to string (like numbers). + +### BinaryArith_SpecializedTypes + +Attempts to do a numeric arithmetic based on operand types. One of the inputs need to be a numeric type and the other a simple arithmetic type. + +### BinaryArith_SpecializedOnBaselineTypes + +Just like BinrayArith_SpecializedTypes tries to do a numeric arithmetic, but based on the observed types in the Baseline Compiler. If it succeeds it will roughly give same optimization as BinaryArith_SpecializedTypes, but the deduction is more fragile. In the best case one should try to get this optimization based on the input types. + +### BinaryArith_SharedCache + +Attempts to optimize a binary arithmetic using inline cache. + +### BinaryArith_Call + +Last resort which cannot get specialized at all and takes the slow path to do the arithmetic. + + +## GetProperty + +### GetProp_ArgumentsLength + +Attempts to optimize an `arguments.length` property access. This optimization +only works if the arguments object is used in well-understood ways within the +function. The function containing the arguments.length is allowed to use the +arguments object in the following ways without disabling this optimization: + +- Access `arguments.length` +- Access `arguments.callee` +- Access individual args using `arguments[i]` +- Save arguments into variables, as long as those variables cannot be accessed + by any nested function, and as long as there exists no eval nywhere within + the function or nested function definitions. +- Call a function using `f.apply(obj, arguments)` + +If the function contains any use of the arguments object that falls out of the +cases defined above, this optimization will be suppressed. In particular, +`arguments` cannot be returned from the function, or passed as an argument into +calls (except for the `apply` case above). + +### GetProp_ArgumentsCallee + +Attempts to optimize an `arguments.callee` property access. This optimization +only works if the arguments object is used in well-understood ways within the +function. The function containing the `arguments.callee` is allowed to use the +arguments object in the following ways without disabling this optimization: + +- Access arguments.length +- Access arguments.callee +- Access individual args using arguments[i] +- Save arguments into variables, as long as those variables cannot be accessed + by any nested function, and as long as there exists no eval nywhere within + the function or nested function definitions. +- Call a function using `f.apply(obj, arguments)` + +If the function contains any use of the `arguments` object that falls out of +the cases defined above, this optimization will be suppressed. In particular, +arguments cannot be returned from the function, or passed as an argument into +calls (except for the `apply` example listed above). + +### GetProp_InferredConstant + +Attempts to optimize an access to a property that seems to be a constant. It +applies to property accesses on objects which are global-like in that there is +only one instance of them per program. This includes global objects, object +literals defined at the top-level of a script, and top-level function objects. + +This optimization makes the assumption that a property that has not changed +after it was first assigned, is likely a constant property. It then directly +inlines the value of the property into hot code that accesses it. For +example, in the following code: + + var Constants = {}; + Constants.N = 100; + + function testArray(array) { + for (var i = 0; i < array.length; i++) { + if (array[i] > Constants.N) + return true; + } + return false; + } + +Will have the loop compiled into the following when `testArray` gets hot. + + for (var i = 0; i < array.length; i++) { + if (array[i] > 100) + return true; + } + +When this optimization is successful, property access is eliminated entirely +and replaced with an inline constant. + +### GetProp_Constant + +Attempts to optimize reading a property that contains a uniquely-typed (or +"singleton") object. With uniquely-typed objects, it is guaranteed that +no other object has that same type. Unique (or "singleton") types are +assigned to certain kinds of objects, like global objects, top-level +functions, and object literals declared at the top-level of a script. If +a property has always contained the same uniquely-typed object, then the +engine can use the unique type to map back to a specific object, and +eliminate the property access, replacing it with a constant reference to +the object. + +When this optimization is successful, property access is eliminated entirely +and replaced with an inline constant. The different success and failure +conditions are documented below: + +### GetProp_StaticName + +Attempts to optimize a property access on `window` which refers to +a property on the global object. + +### GetProp_TypedObject + +Optimizes accesses to properties on TypedObjects. + +### GetProp_DefiniteSlot + +Optimizes access to a well-known regular property on an object. For this +optimization to succeed, the property needs to be well-defined on the object. +For objects constructed by constructor functions, this means that the property +needs to be defined in the constructor, before any complex logic occurs within +the constructor. + +This is the best case for a regular "field" type property that is not +turned into a constant. It compiles down to a single CPU-level load +instruction. + + function SomeConstructor() { + this.x = 10; // x is a definite slot property + this.y = 10; // y is a definite slot property + someComplicatedFunctionCall(); + this.z = 20; // z is not a definite slot property. + } + +In the above example, the properties `x` and `y` can be determined to always +exist on any instance of `SomeConstructor` at definite locations, allowing +the engine to deterministically infer the position of `x` without a shape +guard. + +This optimization can fail for a number of reasons. If the types observed +at the property access are polymorphic (more than one type), this optimization +cannot succeed. Furthermore, even if the object type is monomorphic, the +optimization will fail if the property being accessed is not a definite +slot as described above. + +### GetProp_Unboxed + +Similar to `GetProp_DefiniteSlot`. Unboxed property reads are possible on +properties which satisfy all the characteristics of a definite slot, and +additionally have been observed to only store values of one kind of value. + +Consider the following constructor: + + function Point(x, y) { + this.x = x; + this.y = y; + } + +If only integers are ever stored in the `x` and `y` properties, +then the instances of `Point` will be represented in an "unboxed" mode - +with the property values stored as raw 4-byte values within the object. + +Objects which have the unboxed optimization are more compact. + +### GetProp_CommonGetter + +Optimizes access to properties which are implemented by a getter function, +where the getter is shared between multiple types. + +This optimization applies most often when the property access site is +polymorphic, but all the object types are derived variants of a single +base class, where the property access refers to a getter on the base +class. + +Consider the following example: + function Base() {} + Base.prototype = { + get x() { return 3; } + }; + + function Derived1() {} + Derived1.prototype = Object.create(Base.prototype); + + function Derived2() {} + Derived1.prototype = Object.create(Base.prototype); + +If a property access for `d.x` sees only instances of both `Derived1` and +`Derived2` for `d`, it can optimize the access to `x` to a call to the +getter function defined on `Base`. + +This optimization applies to shared getters on both pure JS objects as well +as DOM objects. + +### GetProp_InlineAccess + +Optimizes a polymorphic property access where there are only a few different +types of objects seen, and the property on all of the different types is +determinable through a shape-check. + +If a property access is monomorphic and the property's location is determinable +from the object's shape, but the property is not definite (see: +GetProp_DefiniteProperty), then this optimization may be used. + +Alternatively, if the property access is polymorphic, but only has a few +different shapes observed at the access site, this optimization may be used. + +This optimization compiles down to one-or more shape-guarded direct loads +from the object. The following pseudocode describes the kind of machine +code generated by this optimization: + + if obj.shape == Shape1 then + obj.slots[0] + elif obj.shape == Shape2 then + obj.slots[5] + elif obj.shape == Shape3 then + obj.slots[2] + ... + end + +### GetProp_Innerize + +Attempts to optimize a situation where a property access of the form +`window.PROP` can be directly translated into a property access on +the inner global object. + +This optimization will always fail on property accesses which are +not on the window object. + +It is useful because accessing global names via the 'window' object +is a common idiom in web programming. + +### GetProp_InlineCache + +This is the worst-case scenario for a property access optimization. This +strategy is used when all the others fail. The engine simply inserts +an inline cache at the property access site. + +Inline caches start off as a jump to a separate piece of code called +a "fallback". The fallback calls into the interpreter VM (which is +very slow) to perform the operation, and then decides if the operation +can be optimized in that particular case. If so, it generates a new +"stub" (or freestanding piece of jitcode) and changes the inline cache +to jump to the stub. The stub attempts to optimize further occurrences +of that same kind of operation. + +Inline caches are an order of magnitude slower than the other optimization +strategies, and are an indication that the type inference engine has +failed to collect enough information to guide the optimization process. + +## SetProperty + +### SetProp_CommonSetter + +Optimizes access to properties which are implemented by a setter function, +where the setter is shared between multiple types. + +This optimization applies most often when the property access site is +polymorphic, but all the object types are derived variants of a single +base class, where the property access refers to a setter on the base +class. + +Consider the following example: + function Base() {} + Base.prototype = { + set x(val) { ... } + }; + + function Derived1() {} + Derived1.prototype = Object.create(Base.prototype); + + function Derived2() {} + Derived1.prototype = Object.create(Base.prototype); + +If a property write for `d.x = val` sees only instances of both `Derived1` and +`Derived2` for `d`, it can optimize the write to `x` to a call to the +setter function defined on `Base`. + +This optimization applies to shared setters on both pure JS objects as well +as DOM objects. + +### SetProp_TypedObject + +Optimizes accesses to properties on TypedObjects. + +### SetProp_DefiniteSlot + +Optimizes a write to a well-known regular property on an object. For this +optimization to succeed, the property needs to be well-defined on the object. +For objects constructed by constructor functions, this means that the property +needs to be defined in the constructor, before any complex logic occurs within +the constructor. + +This is the best case for a regular "field" type property that is not +turned into a constant. It compiles down to a single CPU-level load +instruction. + + function SomeConstructor() { + this.x = 10; // x is a definite slot property + this.y = 10; // y is a definite slot property + someComplicatedFunctionCall(); + this.z = 20; // z is not a definite slot property. + } + +In the above example, the properties `x` and `y` can be determined to always +exist on any instance of `SomeConstructor` at definite locations, allowing +the engine to deterministically infer the position of `x` without a shape +guard. + +This optimization can fail for a number of reasons. If the types observed +at the property access are polymorphic (more than one type), this optimization +cannot succeed. Furthermore, even if the object type is monomorphic, the +optimization will fail if the property being written is not a definite +slot as described above. + +### SetProp_Unboxed + +Similar to `SetProp_DefiniteSlot`. Unboxed property writes are possible on +properties which satisfy all the characteristics of a definite slot, and +additionally have been observed to only store values of one kind of value. + +Consider the following constructor: + + function Point(x, y) { + this.x = x; + this.y = y; + } + +If only integers are ever stored in the `x` and `y` properties, +then the instances of `Point` will be represented in an "unboxed" mode - +with the property values stored as raw 4-byte values within the object. + +Objects which have the unboxed optimization are more compact. + +### SetProp_InlineAccess + +Optimizes a polymorphic property write where there are only a few different +types of objects seen, and the property on all of the different types is +determinable through a shape-check. + +If a property write is monomorphic and the property's location is determinable +from the object's shape, but the property is not definite (see: +GetProp_DefiniteProperty), then this optimization may be used. + +Alternatively, if the property write is polymorphic, but only has a few +different shapes observed at the access site, this optimization may be used. + +This optimization compiles down to one-or more shape-guarded direct stores +to the object. The following pseudocode describes the kind of machine +code generated by this optimization: + + if obj.shape == Shape1 then + obj.slots[0] = val + elif obj.shape == Shape2 then + obj.slots[5] = val + elif obj.shape == Shape3 then + obj.slots[2] = val + ... + end + +### SetProp_InlineCache + +This is the worst-case scenario for a property access optimization. This +strategy is used when all the others fail. The engine simply inserts +an inline cache at the property write site. + +Inline caches start off as a jump to a separate piece of code called +a "fallback". The fallback calls into the interpreter VM (which is +very slow) to perform the operation, and then decides if the operation +can be optimized in that particular case. If so, it generates a new +"stub" (or freestanding piece of jitcode) and changes the inline cache +to jump to the stub. The stub attempts to optimize further occurrences +of that same kind of operation. + +Inline caches are an order of magnitude slower than the other optimization +strategies, and are an indication that the type inference engine has +failed to collect enough information to guide the optimization process. + +## GetElement + +### GetElem_TypedObject + +Attempts to optimized element accesses on array Typed Objects. + +### GetElem_Dense + +Attempts to optimize element accesses on densely packed array objects. Dense +arrays are arrays which do not have any 'holes'. This means that the array +has valid values for all indexes from `0` to `length-1`. + +### GetElem_TypedStatic + +Attempts to optimize element accesses on a typed array that can be determined +to always refer to the same array object. If this optimization succeeds, the +'array' object is treated as a constant, and is not looked up or retrieved from +a variable. + +### GetElem_TypedArray + +Attempts to optimize element accesses on a typed array. + +### GetElem_String + +Attempts to optimize element accesses on a string. + +### GetElem_Arguments + +Attempts to optimize element accesses on the `arguments` special object +available in functions. This optimization only works if the arguments +object is used in well-understood ways within the function. The function +containing the arguments.length is allowed to use the arguments object in +the following ways without disabling this optimization: + +- Access `arguments.length` +- Access `arguments.callee` +- Access individual args using `arguments[i]` +- Save `arguments` into variables, as long as those variables cannot be + accessed by any nested function, and as long as there exists no `eval` + anywhere within the function or nested function definitions. +- Call a function using `f.apply(obj, arguments)` + +If the function contains any use of the arguments object that falls out of the +cases defined above, this optimization will be suppressed. In particular, +`arguments` cannot be returned from the function, or passed as an argument into +calls (except for the `apply` case above). + +### GetElem_ArgumentsInlined + +Similar to GetEelem_Arguments, but optimizes cases where the access on +`arguments` is happening within an inlined function. In these cases, an +access of the form `arguments[i]` can be directly translated into a +direct reference to the corresponding argument value in the inlined call. + +Consider the following: + function foo(arg) { + return bar(arg, 3); + } + function bar() { + return arguments[0] + arguments[1]; + } + +In the above case, if `foo` is compiled with Ion, and the call to `bar` +is inlined, then this optimization can transform the entire procedure to +the following pseudo-code: + + compiled foo(arg): + // inlined call to bar(arg, 3) { + return arg + 3; + // } + +### GetElem_InlineCache + +This is the worst-case scenario for a element access optimization. This +strategy is used when all the others fail. The engine simply inserts +an inline cache at the property write site. + +Inline caches start off as a jump to a separate piece of code called +a "fallback". The fallback calls into the interpreter VM (which is +very slow) to perform the operation, and then decides if the operation +can be optimized in that particular case. If so, it generates a new +"stub" (or freestanding piece of jitcode) and changes the inline cache +to jump to the stub. The stub attempts to optimize further occurrences +of that same kind of operation. + +Inline caches are an order of magnitude slower than the other optimization +strategies, and are an indication that the type inference engine has +failed to collect enough information to guide the optimization process. + +## SetElement + +### SetElem_TypedObject + +Attempts to optimized element writes on array Typed Objects. + +### SetElem_TypedStatic + +Attempts to optimize element writes on a typed array that can be determined +to always refer to the same array object. If this optimization succeeds, the +'array' object is treated as a constant, and is not looked up or retrieved from +a variable. + +### SetElem_TypedArray + +Attempts to optimize element writes on a typed array. + +### SetElem_Dense + +Attempts to optimize element writes on densely packed array objects. Dense +arrays are arrays which do not have any 'holes'. This means that the array +has valid values for all indexes from `0` to `length-1`. + +### SetElem_Arguments + +Attempts to optimize element writes to the `arguments` special object +available in functions. This optimization only works if the arguments +object is used in well-understood ways within the function. The function +containing the arguments.length is allowed to use the arguments object in +the following ways without disabling this optimization: + +- Access `arguments.length` +- Access `arguments.callee` +- Access individual args using `arguments[i]` +- Save `arguments` into variables, as long as those variables cannot be + accessed by any nested function, and as long as there exists no `eval` + anywhere within the function or nested function definitions. +- Call a function using `f.apply(obj, arguments)` + +If the function contains any use of the arguments object that falls out of the +cases defined above, this optimization will be suppressed. In particular, +`arguments` cannot be returned from the function, or passed as an argument into +calls (except for the `apply` case above). + +### SetElem_InlineCache + +This is the worst-case scenario for a element write optimization. This +strategy is used when all the others fail. The engine simply inserts +an inline cache at the property write site. + +Inline caches start off as a jump to a separate piece of code called +a "fallback". The fallback calls into the interpreter VM (which is +very slow) to perform the operation, and then decides if the operation +can be optimized in that particular case. If so, it generates a new +"stub" (or freestanding piece of jitcode) and changes the inline cache +to jump to the stub. The stub attempts to optimize further occurrences +of that same kind of operation. + +Inline caches are an order of magnitude slower than the other optimization +strategies, and are an indication that the type inference engine has +failed to collect enough information to guide the optimization process. + +## Call + +### Call_Inline + +A function call `f(x)` usually pushes a frame onto the call stack. Inlining a +call site conceptually copies the body of the callee function and pastes it +in place of the call site and avoids pushing a new execution frame. Usually, +hot functions do well to be inlined. This is one of the most important +optimizations the JIT performs. + +Ion inlines both interpreted (i.e., written in JavaScript) functions and +native (i.e., built-ins such as `Math.sin` implemented in C++). + +A successfully inlined call site has the outcome Inlined. + +Failure to inline comes in two flavors: unable (e.g., unable to determine +exact callee) and unwilling (e.g., heuristics concluded that the time-space +tradeoff will not pay off). diff --git a/js/src/doc/JITOptimizations/config.sh b/js/src/doc/JITOptimizations/config.sh new file mode 100644 index 000000000..087384ec6 --- /dev/null +++ b/js/src/doc/JITOptimizations/config.sh @@ -0,0 +1,15 @@ +base-url https://developer.mozilla.org/en-US/docs/Tools/ + +markdown Strategies.md JIT-Optimization/Strategies + label 'jitstrategies' 'JIT Optimization Strategies' + label 'getprop' '#getproperty' 'JIT Optimization Strategies : GetProperty' + label 'setprop' '#setproperty' 'JIT Optimization Strategies : SetProperty' + label 'getelem' '#getelement' 'JIT Optimization Strategies : GetElement' + label 'setelem' '#setelement' 'JIT Optimization Strategies : SetElement' + label 'call' '#call' 'JIT Optimization Strategies : Call' + +markdown Strategies.md JIT-Optimization/Outcomes + label 'jitoutcomes' 'JIT Optimization Outcomes' + label 'general' '#general' 'JIT Optimization Outcomes : General Outcomes' + label 'ic' '#inlinecache' 'JIT Optimization Outcomes : Inline Cache Outcomes' + label 'callinline' '#callinline' 'JIT Optimization Outcomes : Call Inlining Outcomes' diff --git a/js/src/doc/README.md b/js/src/doc/README.md new file mode 100644 index 000000000..989fa40db --- /dev/null +++ b/js/src/doc/README.md @@ -0,0 +1,160 @@ +SpiderMonkey in-tree documentation +================================== + +This directory contains documentation for selected parts of SpiderMonkey. The +docs are published on the [Mozilla Developer Network][mdn], but this is the +authoritative copy; the MDN pages are updated from these files by a script. At +the moment, we have: + +- `js/src/doc/Debugger`, SpiderMonkey's JavaScript debugging API, commonly + known as `Debugger`. + +- `js/src/doc/SavedFrame`, SpiderMonkey's compact representation for captured + call stacks. + +and that's it. + +To format the documentation, you'll need to install [Pandoc][], a +documentation format conversion program. Pandoc can convert Markdown text, +which is pleasant to read and write, into HTML, which is what MDN expects. + +[mdn]: http://developer.mozilla.org "Mozilla Developer Network" +[Pandoc]: http://www.johnmacfarlane.net/pandoc/installing.html + +Management scripts +------------------ + +There are two scripts you may want to use while working with `js/src/doc` +subdirectories: + +- `format.sh [--mdn] SOURCEDIR OUTPUTDIR` produces HTML from the document + sources in SOURCEDIR, placing the results in OUTPUTDIR. You can then + check their appearance with a web browser. + + Normally, format.sh arranges the links and HTML metadata so that the + pages view correctly when visited at `file://` URLS pointing into + OUTPUTDIR. However, pages published to MDN should not have the HTML + metadata that stand-alone pages need, and their relative positions may be + different; passing the `--mdn` switch tells `format.sh` to produce output + for publication to MDN, not for previewing on disk. + + (Why are the links affected by `--mdn`? The MDN wiki allows you to create + a page named `.../foo`, and then create sub-pages named `.../foo/bar`. + This is a nice way to arrange, say, a summary page and then sub-pages + providing details. But it's impossible to create the parallel structure + on a POSIX file system: `.../foo` can't be both a file and a directory, + so the links that would be correct when published on the wiki cannot be + correct when previewing those pages on disk. Since OUTPUTDIR's layout + can't match that of the wiki, we make it match that of SOURCEDIR.) + +- `publish.sh SOURCEDIR OUTPUTDIR KEYID SECRET` calls `format.sh`, and then + posts the pages to MDN, using KEYID and SECRET to establish your + identity. This posts only changed pages, so you can run it whenever the + text you have is the right stuff to publish, without creating gratuitous + churn in the MDN page history. + + To generate KEYID and SECRET, visit the [MDN API key generation page][mdnkey]. + +[mdnkey]: https://developer.mozilla.org/en-US/keys/ "MDN API key generation" + + +Why not make the wiki the authoritative copy? +--------------------------------------------- + +Storing documentation in the same tree as the sources it describes has several +advantages: + +- It's easy to handle documentation changes as part of the same review process + we use for code changes. A patch posted to Bugzilla can contain code, test, + and doc changes, all of which can be discussed together. + +- The version control system that manages the code also manages its + documentation. Branching the code (for Nightly, Aurora, Beta, or Release) + branches the docs as well, so one can always find the docs that match the + code in a given release stage. + +- Documentation for proposed changes has a natural home: in the patches + that implement the change (or, at least, in a patch attached to the bug + discussing the change). There's no need to include "(not yet + implemented)" markers in the published docs. + + +Subdirectory layout and script interface +---------------------------------------- + +Alongside the documentation source files, the SOURCEDIR passed to +`format.sh` should contain a file named `config.sh` describing the +directory's contents, how to format them, and where they should be +installed. This data is represented as executable shell code; `format.sh` +and `publish.sh` run the subdirectory's `config.sh` script to learn what +they should do. + +The only effect of running a `SOURCEDIR/config.sh` script should be to +invoke the following commands: + +`base-url BASE` +: Treat BASE as the common prefix for some URLs appearing as arguments to + subsequently executed commands (other than resource files). In + describing the other commands, we use the metavariable RELATIVE-URL for + URLs that are relative to BASE. + + This command should appear before those taking RELATIVE-URL arguments. + + BASE is treated as the root directory of the tree of formatted files. + If OUTPUTDIR is the output directory passed to `format.sh` or + `publish.sh`, then formatted files appear in OUTPUTDIR at the paths + they would appear on MDN relative to BASE. + +`markdown FILE RELATIVE-URL` +: Treat FILE as Markdown source for a documentation page to be published + at RELATIVE-URL. + +`label LABEL [FRAGMENT] [TITLE]` +: Define a label for use in Markdown reference-style links referring to + the file given in the preceding `markdown` command. If the second + argument begins with a `#` character, it is taken to be an HTML + fragment identifier; the link refers to that fragment in the page. + TITLE, if present, is the title for the link. + + For example: + + base-url https://devmo/Tools/ + markdown Conventions.md Debugger-API/Conventions + label conventions "Debugger API: general conventions" + label cv #completion-values "Debugger API: completion values" + + would mean that `Conventions.md` should be processed to create + `https://devmo/Tools/Debugger-API/Conventions`; that Markdown files can + refer to that page like this: + + ... follows some [common conventions][conventions] which ... + + and to the `#completion-values` fragment in that page like this: + + ... is passed a [completion value][cv] indicating ... + +`absolute-label LABEL URL [TITLE]` +: For reference-style links in this directory's Markdown files, define + LABEL to refer to URL, an absolute URL. TITLE is an optional link title. + +`resource LABEL FILE URL` +: Treat FILE as a resource file (an image, for example) that should + appear at URL. Since MDN likes to place "attachments" like images under + different URL prefixes than the wiki pages themselves, URL is not + relative to the BASE passed to base-url. + + LABEL can be the empty string if no Markdown documents need to refer to + this resource file. (For example, the Markdown might use an SVG file, + which in turn use the resource file.) + + Unfortunately, `publish.sh` can't automatically post these resources to + MDN at the moment. However, it will check if the contents have changed, + and print a warning. + + +This ought to be integrated with mach +------------------------------------- + +Indeed. It should somehow be unified with 'mach doc', which seems to +format in-tree docs of a different kind, and use a different markup +language (ReStructuredText) and a different formatter (Sphinx). diff --git a/js/src/doc/SavedFrame/SavedFrame.md b/js/src/doc/SavedFrame/SavedFrame.md new file mode 100644 index 000000000..7d06b445b --- /dev/null +++ b/js/src/doc/SavedFrame/SavedFrame.md @@ -0,0 +1,88 @@ +# `SavedFrame` + +A `SavedFrame` instance is a singly linked list of stack frames. It represents a +JavaScript call stack at a past moment of execution. Younger frames hold a +reference to the frames that invoked them. The older tails are shared across +many younger frames. + +`SavedFrame` stacks should generally be captured, allocated, and live within the +compartment that is being observed or debugged. Usually this is a content +compartment. + +## Capturing `SavedFrame` Stacks + +### From C++ + +Use `JS::CaptureCurrentStack` declared in `jsapi.h`. + +### From JS + +Use `saveStack`, accessible via `Components.utils.getJSTestingFunction()`. + +## Including and Excluding Chrome Frames + +Consider the following `SavedFrame` stack. Arrows represent links from child to +parent frame, `content.js` is from a compartment with content principals, and +`chrome.js` is from a compartment with chrome principals. + + function A from content.js + | + V + function B from chrome.js + | + V + function C from content.js + +The content compartment will ever have one view of this stack: `A -> C`. + +However, a chrome compartment has a choice: it can either take the same view +that the content compartment has (`A -> C`), or it can view all stack frames, +including the frames from chrome compartments (`A -> B -> C`). To view +everything, use an `XrayWrapper`. This is the default wrapper. To see the stack +as the content compartment sees it, waive the xray wrapper with +`Components.utils.waiveXrays`: + + const contentViewOfStack = Components.utils.waiveXrays(someStack); + +## Accessor Properties of the `SavedFrame.prototype` Object + +`source` +: The source URL for this stack frame, as a string. + +`line` +: The line number for this stack frame. + +`column` +: The column number for this stack frame. + +`functionDisplayName` +: Either SpiderMonkey's inferred name for this stack frame's function, or + `null`. + +`asyncCause` +: If this stack frame is the `asyncParent` of other stack frames, then this is + a string representing the type of asynchronous call by which this frame + invoked its children. For example, if this frame's children are calls to + handlers for a promise this frame created, this frame's `asyncCause` would + be `"Promise"`. If the asynchronous call was started in a descendant frame + to which the requester of the property does not have access, this will be + the generic string `"Async"`. If this is not an asynchronous call point, + this will be `null`. + +`asyncParent` +: If this stack frame was called as a result of an asynchronous operation, for + example if the function referenced by this frame is a promise handler, this + property points to the stack frame responsible for the asynchronous call, + for example where the promise was created. If the frame responsible for the + call is not accessible to the caller, this points to the youngest accessible + ancestor of the real frame, if any. In all other cases, this is `null`. + +`parent` +: This stack frame's caller, or `null` if this is the oldest frame on the + stack. In this case, there might be an `asyncParent` instead. + +## Function Properties of the `SavedFrame.prototype` Object + +`toString` +: Return this frame and its parents formatted as a human readable stack trace + string. diff --git a/js/src/doc/SavedFrame/config.sh b/js/src/doc/SavedFrame/config.sh new file mode 100644 index 000000000..373f3436a --- /dev/null +++ b/js/src/doc/SavedFrame/config.sh @@ -0,0 +1,6 @@ +### Description of SavedFrame docs: how to format, where to install. +### See js/src/doc/README.md for a description. + +base-url https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/ + +markdown SavedFrame.md SavedFrame diff --git a/js/src/doc/format.sh b/js/src/doc/format.sh new file mode 100755 index 000000000..754466bdf --- /dev/null +++ b/js/src/doc/format.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +# Format in-tree documentation for posting to MDN. +# See js/src/doc/README.md for general usage information. +# +# Usage: +# +# ./format.sh [--mdn] SOURCEDIR OUTPUTDIR +# +# Pages are tagged with the current Mercurial parent changeset ID. +# +# Normally, the generated HTML includes appropriate headers for in-place +# viewing, and the links are set up to allow the files to refer to each +# other where they're placed in OUTPUTDIR. However, if the --mdn flag is +# given, we omit non-body elements from the HTML (which seems to be what +# MDN prefers), and generate links to work with the URLs at which the pages +# will be published on MDN. + +set -eu + +progname=$(basename $0) +lib=$(cd $(dirname $0)/lib; pwd) + +# If we're producing text meant to be reviewed locally, then ask Pandoc to +# produce standalone HTML files (which include text encoding metadata and +# other helpful things). +standalone_arg=--standalone + +# If we're producing text for MDN, then generate links using the wiki URLs. +mdn_arg= + +while true; do + case "${1-}" in + '--mdn') + mdn_arg=--mdn + standalone_arg= + shift + ;; + *) + break + ;; + esac +done + +sourcedir=$1 +outputdir=$2 + +config=$sourcedir/config.sh +if ! [ -f "$config" ]; then + echo "SOURCEDIR doesn't seem to contain a 'config.sh' file: $sourcedir" >&2 + exit 1 +fi + +export JS_DOC_HG_IDENTIFY="$(hg identify | sed -e 's/ .*$//')" + +# Compute the name of the source directory relative to the hg root, for the +# "this text computed from..." message. +hg_relative_sourcedir=$((cd $sourcedir; pwd) | sed -e "s|$(hg root)/||") + +checked_pandoc=false + +source $lib/dummy-config.sh + +markdown() { + INPUT_FILE=$1 + URL=$BASE_URL$2 + + if ! $checked_pandoc; then + if ! pandoc -v > /dev/null; then + echo "$progname: This script uses the 'pandoc' formatter, but that doesn't seem" >&2 + echo "to be installed." >&2 + exit 1 + fi + checked_pandoc=true + fi + + local file=$sourcedir/$INPUT_FILE + if ! [ -f "$file" ]; then + echo "$progname: Can't find markdown file $file, mentioned by $config" >&2 + exit 1 + fi + + local output_file=$outputdir/${INPUT_FILE/md/html} + + mkdir -p $(dirname "$output_file") + pandoc $standalone_arg \ + -f markdown --smart -t html \ + "$file" \ + <("$lib/make-bibliography.sh" $mdn_arg "$config" "$URL") \ + -o "$output_file" + + "$lib/make-watermark.sh" "$output_file" "$hg_relative_sourcedir/$INPUT_FILE" >> "$output_file" +} + +resource() { + local label=$1 file=$2 url=$3 + ln -f $sourcedir/$file $outputdir/$file +} + +source "$config" diff --git a/js/src/doc/lib/common.sh b/js/src/doc/lib/common.sh new file mode 100644 index 000000000..785f7dec1 --- /dev/null +++ b/js/src/doc/lib/common.sh @@ -0,0 +1,23 @@ +# Common utility functions for js/src/doc scripts. + +# `relative BASE ABSOLUTE` prints the URL relative to BASE that is +# equivalent to ABSOLUTE. BASE must end with a '/'. This function will +# introduce at most one level of '..'. +relative() { + local parent=$(dirname "$1") + case "$2" in + "$1"*) + # ABSOLUTE is within BASE; just remove BASE. + echo "$2" | sed -e "s|^$1||" + ;; + "$parent/"*) + # ABSOLUTE is within BASE/.. + echo "$2" | sed -e "s|^$parent/|../|" + ;; + *) + # ABSOLUTE is unrelated to BASE. + echo "$2" + ;; + esac +} + diff --git a/js/src/doc/lib/dummy-config.sh b/js/src/doc/lib/dummy-config.sh new file mode 100644 index 000000000..dd9d39a4b --- /dev/null +++ b/js/src/doc/lib/dummy-config.sh @@ -0,0 +1,24 @@ +# Dummy definitions of functions used in doc directory config.sh files. +# Scripts reading config.sh files should source this first, and then +# override the definitions of the functions they care about. + +base-url() { + BASE_URL=$1 +} + +markdown() { + INPUT_FILE=$1 + RELATIVE_URL=$2 +} + +label() { + : +} + +absolute-label() { + : +} + +resource() { + : +} diff --git a/js/src/doc/lib/extract-watermark.sh b/js/src/doc/lib/extract-watermark.sh new file mode 100755 index 000000000..9f1de8642 --- /dev/null +++ b/js/src/doc/lib/extract-watermark.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Extract the js/src/doc watermark from standard input, assumed to be the +# text of a page formatted for publication on MDN. +# +# We can apply this to both the output of the formatter, and to pages +# retrieved from MDN, to see if anything needs to be updated. +# +# Usage: +# +# extract-watermark.sh +# +# For example: +# +# $ curl --silent 'https://developer.mozilla.org/en-US/docs/Tools/Debugger-API' | ./doc/lib/extract-watermark.sh +# sha256:168224ee2d58521b7c8368eddf4ac4fa53a897fa803ae484007af4e61a00ddcd +# $ + +set -eu + +# Since $(foo) trims any final newline in foo's output, this 'echo' ensures +# that our output is terminated by a newline, whether or not curl | sed's +# is. +echo $(sed -n -e "s|.*
\([^<]*\)
.*|\1|p") diff --git a/js/src/doc/lib/make-bibliography.sh b/js/src/doc/lib/make-bibliography.sh new file mode 100755 index 000000000..33068ca9c --- /dev/null +++ b/js/src/doc/lib/make-bibliography.sh @@ -0,0 +1,81 @@ +#!/usr/bin/env bash + +# Generate a Markdown bibliography file --- that is, definitions for +# reference-style link labels --- from a documentation directory's +# config.sh file. +# +# Usage: +# make-bibliography.sh [--mdn] CONFIG CITING-URL +# +# where: +# - CONFIG is the name of the config.sh file to process; and +# - CITING-URL is the absolute URL at which the document using these labels +# will appear. The links we output use URLs relative to CITING-URL to +# refer to the URLs given in CONFIG. +# +# If given the --mdn flag, generate links that are correct for files +# as they appear in format.sh's OUTPUTDIR, not for publishing on MDN. + +set -eu + +mdn=false + +while true; do + case "${1-}" in + '--mdn') + mdn=true + shift + ;; + *) + break + ;; + esac +done + +lib=$(dirname $0) +config=$1 +citing=$2 + +source "$lib/common.sh" + +source "$lib/dummy-config.sh" + +label() { + local label=$1; shift + local fragment= + case "$1" in + '#'*) + fragment=$1; shift + ;; + esac + local title=${1:+ \"$1\"} + + citing_prefix=$(dirname "$citing")/ + if $mdn; then + echo "[$label]: $(relative "$citing_prefix" "$BASE_URL$RELATIVE_URL")$fragment$title" + else + echo "[$label]: ${INPUT_FILE/md/html}$fragment$title" + fi +} + +absolute-label() { + local label=$1 + local absolute_url=$2 + local title=$3 + + echo "[$label]: $absolute_url${title:+ \"$title\"}" +} + +resource() { + local label=$1 file=$2 absolute_url=$3 + + if [ -n "$label" ]; then + if $mdn; then + echo "[$label]: $absolute_url" + else + echo "[$label]: $file" + fi + fi +} + +source "$config" diff --git a/js/src/doc/lib/make-watermark.sh b/js/src/doc/lib/make-watermark.sh new file mode 100755 index 000000000..a39ff5436 --- /dev/null +++ b/js/src/doc/lib/make-watermark.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Generate an HTML watermark for the given file. Claim the text was +# generated from SOURCE. +# +# Usage: +# +# make-watermark.sh FILE SOURCE + +set -eu + +# Include the watermark as visible text in the page, as suggested in bug 990662. + +echo '

Source Metadata

' +echo '
' +echo '
Generated from file:
' +echo "
$2
" +echo '
Watermark:
' +echo "
sha256:$(cat "$1" | shasum -a 256 | sed 's/ .*$//')
" + +# If we have Mercurial changeset ID, include it. +if [ "${JS_DOC_HG_IDENTIFY:+set}" = set ]; then + # If the changeset ID has a '+' on the end (indicating local + # modifications), omit that from the link. + cat <Changeset: +
${JS_DOC_HG_IDENTIFY}
+EOF +fi + +echo '
' diff --git a/js/src/doc/publish.sh b/js/src/doc/publish.sh new file mode 100755 index 000000000..fad0b27d9 --- /dev/null +++ b/js/src/doc/publish.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash + +# Format js/src/doc documentation in SOURCEDIR, place formatted files in OUTPUTDIR, +# and post all changed pages to MDN using KEYID and SECRET to identify the poster. +# See js/src/doc/README.md for general usage information. +# +# Usage: +# +# ./publish.sh SOURCEDIR OUTPUTDIR KEYID SECRET +# +# Pages are tagged with the current Mercurial parent changeset ID. + +set -eu + +progname=$(basename $0) +doc=$(cd $(dirname $0); pwd) +lib=$doc/lib + +sourcedir=$1 +outputdir=$2 +keyid=$3 +secret=$4 + +$doc/format.sh --mdn "$sourcedir" "$outputdir" + +config=$sourcedir/config.sh + +watermark=$lib/extract-watermark.sh + +# Fetch a URL, with caching disabled. +fetch() { + curl --silent -XGET "$1" \ + -H"Cache-Control: no-cache, no-store, must-revalidate" \ + -H"Pragma: no-cache" \ + -H"Expires: 0" +} + +source $lib/dummy-config.sh + +markdown() { + INPUT_FILE=$1 + URL=$BASE_URL$2 + + local formatted_file=$outputdir/${INPUT_FILE/md/html} + + # Extract the watermark from the formatted file. + local local_watermark=$("$watermark" < "$formatted_file") + + # Get the existing page, and extract its watermark, if any. + local public_watermark=$(fetch "$URL?raw" | "$watermark") + + if [ "$local_watermark" != "$public_watermark" ]; then + echo "$progname: Updating: $URL" >&2 + local status + status=$(curl --silent -X PUT -H"Content-Type: text/html" --upload-file "$formatted_file" -u "$keyid:$secret" "$URL") + case "$status" in + CREATED | RESET) + ;; + *) + echo "$progname: Error posting $URL, from $config: $status" >&2 + exit 1 + ;; + esac + else + echo "$progname: Unchanged: $URL" >&2 + fi +} + +# MDN can't currently update attached resources. But we can verify that the current +# published versions match what we have. +resource() { + local label=$1 + local file=$sourcedir/$2 + local url=$3 + + if cmp "$file" <(fetch "$url") > /dev/null; then + echo "$progname: Unchanged: $url" >&2 + else + echo "$progname: Warning: resource out of date: $url" >&2 + fi +} + +source $config -- cgit v1.2.3