Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | # The `Debugger` Interface |
michael@0 | 2 | |
michael@0 | 3 | Mozilla's JavaScript engine, SpiderMonkey, provides a debugging interface |
michael@0 | 4 | named `Debugger` which lets JavaScript code observe and manipulate the |
michael@0 | 5 | execution of other JavaScript code. Both Firefox's built-in developer tools |
michael@0 | 6 | and the Firebug add-on use `Debugger` to implement their JavaScript |
michael@0 | 7 | debuggers. However, `Debugger` is quite general, and can be used to |
michael@0 | 8 | implement other kinds of tools like tracers, coverage analysis, |
michael@0 | 9 | patch-and-continue, and so on. |
michael@0 | 10 | |
michael@0 | 11 | `Debugger` has three essential qualities: |
michael@0 | 12 | |
michael@0 | 13 | - It is a *source level* interface: it operates in terms of the JavaScript |
michael@0 | 14 | language, not machine language. It operates on JavaScript objects, stack |
michael@0 | 15 | frames, environments, and code, and presents a consistent interface |
michael@0 | 16 | regardless of whether the debuggee is interpreted, compiled, or |
michael@0 | 17 | optimized. If you have a strong command of the JavaScript language, you |
michael@0 | 18 | should have all the background you need to use `Debugger` successfully, |
michael@0 | 19 | even if you have never looked into the language's implementation. |
michael@0 | 20 | |
michael@0 | 21 | - It is for use *by JavaScript code*. JavaScript is both the debuggee |
michael@0 | 22 | language and the tool implementation language, so the qualities that make |
michael@0 | 23 | JavaScript effective on the web can be brought to bear in crafting tools |
michael@0 | 24 | for developers. As is expected of JavaScript APIs, `Debugger` is a |
michael@0 | 25 | *sound* interface: using (or even misusing) `Debugger` should never cause |
michael@0 | 26 | Gecko to crash. Errors throw proper JavaScript exceptions. |
michael@0 | 27 | |
michael@0 | 28 | - It is an *intra-thread* debugging API. Both the debuggee and the code |
michael@0 | 29 | using `Debugger` to observe it must run in the same thread. Cross-thread, |
michael@0 | 30 | cross-process, and cross-device tools must use `Debugger` to observe the |
michael@0 | 31 | debuggee from within the same thread, and then handle any needed |
michael@0 | 32 | communication themselves. (Firefox's builtin tools have a |
michael@0 | 33 | [protocol][protocol] defined for this purpose.) |
michael@0 | 34 | |
michael@0 | 35 | In Gecko, the `Debugger` API is available to chrome code only. By design, |
michael@0 | 36 | it ought not to introduce security holes, so in principle it could be made |
michael@0 | 37 | available to content as well; but it is hard to justify the security risks |
michael@0 | 38 | of the additional attack surface. |
michael@0 | 39 | |
michael@0 | 40 | |
michael@0 | 41 | ## Debugger Instances and Shadow Objects |
michael@0 | 42 | |
michael@0 | 43 | `Debugger` reflects every aspect of the debuggee's state as a JavaScript |
michael@0 | 44 | value—not just actual JavaScript values like objects and primitives, |
michael@0 | 45 | but also stack frames, environments, scripts, and compilation units, which |
michael@0 | 46 | are not normally accessible as objects in their own right. |
michael@0 | 47 | |
michael@0 | 48 | Here is a JavaScript program in the process of running a timer callback function: |
michael@0 | 49 | |
michael@0 | 50 | ![A running JavaScript program and its Debugger shadows][img-shadows] |
michael@0 | 51 | |
michael@0 | 52 | This diagram shows the various types of shadow objects that make up the |
michael@0 | 53 | Debugger API (which all follow some [general conventions][conventions]): |
michael@0 | 54 | |
michael@0 | 55 | - A [`Debugger.Object`][object] represents a debuggee object, offering a |
michael@0 | 56 | reflection-oriented API that protects the debugger from accidentally |
michael@0 | 57 | invoking getters, setters, proxy traps, and so on. |
michael@0 | 58 | |
michael@0 | 59 | - A [`Debugger.Script`][script] represents a block of JavaScript |
michael@0 | 60 | code—either a function body or a top-level script. Given a |
michael@0 | 61 | `Debugger.Script`, one can set breakpoints, translate between source |
michael@0 | 62 | positions and bytecode offsets (a deviation from the "source level" |
michael@0 | 63 | design principle), and find other static characteristics of the code. |
michael@0 | 64 | |
michael@0 | 65 | - A [`Debugger.Frame`][frame] represents a running stack frame. You can use |
michael@0 | 66 | these to walk the stack and find each frame's script and environment. You |
michael@0 | 67 | can also set `onStep` and `onPop` handlers on frames. |
michael@0 | 68 | |
michael@0 | 69 | - A [`Debugger.Environment`][environment] represents an environment, |
michael@0 | 70 | associating variable names with storage locations. Environments may |
michael@0 | 71 | belong to a running stack frame, captured by a function closure, or |
michael@0 | 72 | reflect some global object's properties as variables. |
michael@0 | 73 | |
michael@0 | 74 | The [`Debugger`][debugger-object] instance itself is not really a shadow of |
michael@0 | 75 | anything in the debuggee; rather, it maintains the set of global objects |
michael@0 | 76 | which are to be considered debuggees. A `Debugger` observes only execution |
michael@0 | 77 | taking place in the scope of these global objects. You can set functions to |
michael@0 | 78 | be called when new stack frames are pushed; when new code is loaded; and so |
michael@0 | 79 | on. |
michael@0 | 80 | |
michael@0 | 81 | Omitted from this picture are [`Debugger.Source`][source] instances, which |
michael@0 | 82 | represent JavaScript compilation units. A `Debugger.Source` can furnish a |
michael@0 | 83 | full copy of its source code, and explain how the code entered the system, |
michael@0 | 84 | whether via a call to `eval`, a `<script>` element, or otherwise. A |
michael@0 | 85 | `Debugger.Script` points to the `Debugger.Source` from which it is derived. |
michael@0 | 86 | |
michael@0 | 87 | All these types follow some [general conventions][conventions], which you |
michael@0 | 88 | should look through before drilling down into any particular type's |
michael@0 | 89 | specification. |
michael@0 | 90 | |
michael@0 | 91 | All shadow objects are unique per `Debugger` and per referent. For a given |
michael@0 | 92 | `Debugger`, there is exactly one `Debugger.Object` that refers to a |
michael@0 | 93 | particular debuggee object; exactly one `Debugger.Frame` for a particular |
michael@0 | 94 | stack frame; and so on. Thus, a tool can store metadata about a shadow's |
michael@0 | 95 | referent as a property on the shadow itself, and count on finding that |
michael@0 | 96 | metadata again if it comes across the same referent. And since shadows are |
michael@0 | 97 | per-`Debugger`, tools can do so without worrying about interfering with |
michael@0 | 98 | other tools that use their own `Debugger` instances. |
michael@0 | 99 | |
michael@0 | 100 | |
michael@0 | 101 | ## Example |
michael@0 | 102 | |
michael@0 | 103 | You can try out `Debugger` yourself in Firefox's Scratchpad. |
michael@0 | 104 | |
michael@0 | 105 | 1) Visit the URL `about:config`, and set the `devtools.chrome.enabled` |
michael@0 | 106 | preference to `true`: |
michael@0 | 107 | |
michael@0 | 108 | ![Setting the 'devtools.chrome.enabled' preference][img-chrome-pref] |
michael@0 | 109 | |
michael@0 | 110 | 2) Save the following HTML text to a file, and visit the file in your |
michael@0 | 111 | browser: |
michael@0 | 112 | |
michael@0 | 113 | <div onclick="var x = 'snoo'; debugger;">Click me!</div> |
michael@0 | 114 | |
michael@0 | 115 | 3) Open a developer Scratchpad (Menu button > Developer > Scratchpad), and |
michael@0 | 116 | select "Browser" from the "Environment" menu. (This menu will not be |
michael@0 | 117 | present unless you have changed the preference as explained above.) |
michael@0 | 118 | |
michael@0 | 119 | ![Selecting the 'browser' context in the Scratchpad][img-scratchpad-browser] |
michael@0 | 120 | |
michael@0 | 121 | 4) Enter the following code in the Scratchpad: |
michael@0 | 122 | |
michael@0 | 123 | // This simply defines 'Debugger' in this Scratchpad; |
michael@0 | 124 | // it doesn't actually start debugging anything. |
michael@0 | 125 | Cu.import("resource://gre/modules/jsdebugger.jsm"); |
michael@0 | 126 | addDebuggerToGlobal(window); |
michael@0 | 127 | |
michael@0 | 128 | // Create a 'Debugger' instance. |
michael@0 | 129 | var dbg = new Debugger; |
michael@0 | 130 | |
michael@0 | 131 | // Get the current tab's content window, and make it a debuggee. |
michael@0 | 132 | var w = gBrowser.selectedBrowser.contentWindow.wrappedJSObject; |
michael@0 | 133 | dbg.addDebuggee(w); |
michael@0 | 134 | |
michael@0 | 135 | // When the debuggee executes a 'debugger' statement, evaluate |
michael@0 | 136 | // the expression 'x' in that stack frame, and show its value. |
michael@0 | 137 | dbg.onDebuggerStatement = function (frame) { |
michael@0 | 138 | alert('hit debugger statement; x = ' + frame.eval('x').return); |
michael@0 | 139 | } |
michael@0 | 140 | |
michael@0 | 141 | 5) In the Scratchpad, ensure that no text is selected, and press the "Run" |
michael@0 | 142 | button. |
michael@0 | 143 | |
michael@0 | 144 | 6) Now, click on the text that says "Click me!" in the web page. This runs |
michael@0 | 145 | the `div` element's `onclick` handler. When control reaches the |
michael@0 | 146 | `debugger;` statement, `Debugger` calls your callback function, passing |
michael@0 | 147 | a `Debugger.Frame` instance. Your callback function evaluates the |
michael@0 | 148 | expression `x` in the given stack frame, and displays the alert: |
michael@0 | 149 | |
michael@0 | 150 | ![The Debugger callback displaying an alert][img-example-alert] |
michael@0 | 151 | |
michael@0 | 152 | 7) Press "Run" in the Scratchpad again. Now, clicking on the "Click me!" |
michael@0 | 153 | text causes *two* alerts to show—one for each `Debugger` |
michael@0 | 154 | instance. |
michael@0 | 155 | |
michael@0 | 156 | Multiple `Debugger` instances can observe the same debuggee. Re-running |
michael@0 | 157 | the code in the Scratchpad created a fresh `Debugger` instance, added |
michael@0 | 158 | the same web page as its debuggee, and then registered a fresh |
michael@0 | 159 | `debugger;` statement handler with the new instance. When you clicked |
michael@0 | 160 | on the `div` element, both of them ran. This shows how any number of |
michael@0 | 161 | `Debugger`-based tools can observe a single web page |
michael@0 | 162 | simultaneously—although, since the order in which their handlers |
michael@0 | 163 | run is not specified, such tools should probably only observe, and not |
michael@0 | 164 | influence, the debuggee's behavior. |
michael@0 | 165 | |
michael@0 | 166 | 8) Close the web page and the Scratchpad. |
michael@0 | 167 | |
michael@0 | 168 | Since both the Scratchpad's global object and the debuggee window are |
michael@0 | 169 | now gone, the `Debugger` instances will be garbage collected, since |
michael@0 | 170 | they can no longer have any visible effect on Firefox's behavior. The |
michael@0 | 171 | `Debugger` API tries to interact with garbage collection as |
michael@0 | 172 | transparently as possible; for example, if both a `Debugger.Object` |
michael@0 | 173 | instance and its referent are not reachable, they will both be |
michael@0 | 174 | collected, even while the `Debugger` instance to which the shadow |
michael@0 | 175 | belonged continues to exist. |
michael@0 | 176 | |
michael@0 | 177 | |
michael@0 | 178 | ## Gecko-specific features |
michael@0 | 179 | |
michael@0 | 180 | While the `Debugger` core API deals only with concepts common to any |
michael@0 | 181 | JavaScript implementation, it also includes some Gecko-specific features: |
michael@0 | 182 | |
michael@0 | 183 | - [Global tracking][global] supports debugging all the code running in a |
michael@0 | 184 | Gecko instance at once—the 'chrome debugging' model. |
michael@0 | 185 | - [Object wrapper][wrapper] functions help manipulate object references |
michael@0 | 186 | that cross privilege boundaries. |