OLD | NEW |
---|---|
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 "use strict"; | 5 "use strict"; |
6 | 6 |
7 // If true, prints all messages sent and received by inspector. | 7 // If true, prints all messages sent and received by inspector. |
8 const printProtocolMessages = false; | 8 const printProtocolMessages = false; |
9 | 9 |
10 // The active wrapper instance. | 10 // The active wrapper instance. |
11 let activeWrapper = undefined; | 11 let activeWrapper = undefined; |
12 | 12 |
13 // Receiver function called by inspector, delegating to active wrapper. | 13 // Receiver function called by inspector, delegating to active wrapper. |
14 function receive(message) { | 14 function receive(message) { |
15 activeWrapper.receiveMessage(message); | 15 activeWrapper.receiveMessage(message); |
16 } | 16 } |
17 | 17 |
18 class DebugWrapper { | 18 class DebugWrapper { |
19 constructor() { | 19 constructor() { |
20 // Message dictionary storing {id, message} pairs. | 20 // Message dictionary storing {id, message} pairs. |
21 this.receivedMessages = {}; | 21 this.receivedMessages = new Map(); |
22 | 22 |
23 // Each message dispatched by the Debug wrapper is assigned a unique number | 23 // Each message dispatched by the Debug wrapper is assigned a unique number |
24 // using nextMessageId. | 24 // using nextMessageId. |
25 this.nextMessageId = 0; | 25 this.nextMessageId = 0; |
26 | 26 |
27 // The listener method called on certain events. | 27 // The listener method called on certain events. |
28 this.listener = undefined; | 28 this.listener = undefined; |
29 | 29 |
30 // TODO(jgruber): Determine which of these are still required and possible. | 30 // TODO(jgruber): Determine which of these are still required and possible. |
31 // Debug events which can occur in the V8 JavaScript engine. | 31 // Debug events which can occur in the V8 JavaScript engine. |
32 this.DebugEvent = { Break: 1 | 32 this.DebugEvent = { Break: 1 |
33 , Exception: 2 | 33 , Exception: 2 |
34 , NewFunction: 3 | 34 , NewFunction: 3 |
35 , BeforeCompile: 4 | 35 , BeforeCompile: 4 |
36 , AfterCompile: 5 | 36 , AfterCompile: 5 |
37 , CompileError: 6 | 37 , CompileError: 6 |
38 , AsyncTaskEvent: 7 | 38 , AsyncTaskEvent: 7 |
39 }; | 39 }; |
40 | 40 |
41 // The different types of steps. | |
42 this.StepAction = { StepOut: 0 | |
43 , StepNext: 1 | |
44 , StepIn: 2 | |
45 , StepFrame: 3 | |
46 }; | |
47 | |
48 // A copy of the scope types from runtime-debug.cc. | |
49 // NOTE: these constants should be backward-compatible, so | |
50 // add new ones to the end of this list. | |
51 this.ScopeType = { Global: 0 | |
52 , Local: 1 | |
53 , With: 2 | |
54 , Closure: 3 | |
55 , Catch: 4 | |
56 , Block: 5 | |
57 , Script: 6 | |
58 , Eval: 7 | |
59 , Module: 8 | |
60 }; | |
61 | |
62 // Store the current script id so we can skip corresponding break events. | |
63 this.thisScriptId = %FunctionGetScriptId(receive); | |
64 | |
41 // Register as the active wrapper. | 65 // Register as the active wrapper. |
42 assertTrue(activeWrapper === undefined); | 66 assertTrue(activeWrapper === undefined); |
43 activeWrapper = this; | 67 activeWrapper = this; |
44 } | 68 } |
45 | 69 |
46 enable() { this.sendMessageForMethodChecked("Debugger.enable"); } | 70 enable() { this.sendMessageForMethodChecked("Debugger.enable"); } |
47 disable() { this.sendMessageForMethodChecked("Debugger.disable"); } | 71 disable() { this.sendMessageForMethodChecked("Debugger.disable"); } |
48 | 72 |
49 setListener(listener) { this.listener = listener; } | 73 setListener(listener) { this.listener = listener; } |
50 | 74 |
(...skipping 18 matching lines...) Expand all Loading... | |
69 | 93 |
70 const {msgid, msg} = this.createMessage( | 94 const {msgid, msg} = this.createMessage( |
71 "Debugger.setBreakpoint", | 95 "Debugger.setBreakpoint", |
72 { location : { scriptId : scriptid.toString() | 96 { location : { scriptId : scriptid.toString() |
73 , lineNumber : loc.line | 97 , lineNumber : loc.line |
74 , columnNumber : loc.column | 98 , columnNumber : loc.column |
75 } | 99 } |
76 }); | 100 }); |
77 this.sendMessage(msg); | 101 this.sendMessage(msg); |
78 | 102 |
79 const reply = this.receivedMessages[msgid]; | 103 const reply = this.takeReplyChecked(msgid); |
104 assertTrue(reply.result !== undefined); | |
80 const breakid = reply.result.breakpointId; | 105 const breakid = reply.result.breakpointId; |
81 assertTrue(breakid !== undefined); | 106 assertTrue(breakid !== undefined); |
82 | 107 |
83 return breakid; | 108 return breakid; |
84 } | 109 } |
85 | 110 |
86 clearBreakPoint(breakid) { | 111 clearBreakPoint(breakid) { |
87 const {msgid, msg} = this.createMessage( | 112 const {msgid, msg} = this.createMessage( |
88 "Debugger.removeBreakpoint", { breakpointId : breakid }); | 113 "Debugger.removeBreakpoint", { breakpointId : breakid }); |
89 this.sendMessage(msg); | 114 this.sendMessage(msg); |
90 assertTrue(this.receivedMessages[msgid] !== undefined); | 115 this.takeReplyChecked(msgid); |
91 } | 116 } |
92 | 117 |
93 // Returns the serialized result of the given expression. For example: | 118 // Returns the serialized result of the given expression. For example: |
94 // {"type":"number", "value":33, "description":"33"}. | 119 // {"type":"number", "value":33, "description":"33"}. |
95 evaluate(frameid, expression) { | 120 evaluate(frameid, expression) { |
96 const {msgid, msg} = this.createMessage( | 121 const {msgid, msg} = this.createMessage( |
97 "Debugger.evaluateOnCallFrame", | 122 "Debugger.evaluateOnCallFrame", |
98 { callFrameId : frameid | 123 { callFrameId : frameid |
99 , expression : expression | 124 , expression : expression |
100 }); | 125 }); |
101 this.sendMessage(msg); | 126 this.sendMessage(msg); |
102 | 127 |
103 const reply = this.receivedMessages[msgid]; | 128 const reply = this.takeReplyChecked(msgid); |
104 return reply.result.result; | 129 return reply.result.result; |
105 } | 130 } |
106 | 131 |
107 // --- Internal methods. ----------------------------------------------------- | 132 // --- Internal methods. ----------------------------------------------------- |
108 | 133 |
109 getNextMessageId() { | 134 getNextMessageId() { |
110 return this.nextMessageId++; | 135 return this.nextMessageId++; |
111 } | 136 } |
112 | 137 |
113 createMessage(method, params) { | 138 createMessage(method, params) { |
(...skipping 18 matching lines...) Expand all Loading... | |
132 } | 157 } |
133 | 158 |
134 sendMessage(message) { | 159 sendMessage(message) { |
135 if (printProtocolMessages) print(message); | 160 if (printProtocolMessages) print(message); |
136 send(message); | 161 send(message); |
137 } | 162 } |
138 | 163 |
139 sendMessageForMethodChecked(method) { | 164 sendMessageForMethodChecked(method) { |
140 const {msgid, msg} = this.createMessage(method); | 165 const {msgid, msg} = this.createMessage(method); |
141 this.sendMessage(msg); | 166 this.sendMessage(msg); |
142 assertTrue(this.receivedMessages[msgid] !== undefined); | 167 this.takeReplyChecked(msgid); |
168 } | |
169 | |
170 takeReplyChecked(msgid) { | |
171 const reply = this.receivedMessages[msgid]; | |
172 assertTrue(reply !== undefined); | |
173 delete this.receivedMessages[msgid]; | |
Yang
2016/11/02 10:36:04
I don't think this is how to use a Map. You would
jgruber
2016/11/07 11:35:11
Done.
| |
174 return reply; | |
175 } | |
176 | |
177 execStatePrepareStep(action) { | |
178 switch(action) { | |
179 case this.StepAction.StepOut: this.stepOut(); break; | |
180 case this.StepAction.StepNext: this.stepOver(); break; | |
181 case this.StepAction.StepIn: this.stepInto(); break; | |
182 default: %AbortJS("Unsupported StepAction"); break; | |
183 } | |
184 } | |
185 | |
186 execStateScope(scope) { | |
187 // TODO(jgruber): Mapping | |
188 return { scopeType: () => scope.type | |
189 , scopeObject: () => scope.object | |
Yang
2016/11/02 10:36:04
Can we have the comma on the previous line? Elsewh
jgruber
2016/11/07 11:35:11
Dang, thought I could sneak some nice haskell-styl
| |
190 }; | |
191 } | |
192 | |
193 execStateFrame(frame) { | |
194 const scriptid = parseInt(frame.location.scriptId); | |
195 const line = frame.location.lineNumber; | |
196 const column = frame.location.columnNumber; | |
197 const loc = %ScriptLocationFromLine2(scriptid, line, column, 0); | |
198 const func = { name : () => frame.functionName }; | |
199 return { sourceLineText : () => loc.sourceText | |
200 , functionName : () => frame.functionName | |
201 , func : () => func | |
202 , scopeCount : () => frame.scopeChain.length | |
203 , scope : (index) => this.execStateScope(frame.scopeChain[index]) | |
204 }; | |
143 } | 205 } |
144 | 206 |
145 // --- Message handlers. ----------------------------------------------------- | 207 // --- Message handlers. ----------------------------------------------------- |
146 | 208 |
147 dispatchMessage(message) { | 209 dispatchMessage(message) { |
148 const method = message.method; | 210 const method = message.method; |
149 if (method == "Debugger.paused") { | 211 if (method == "Debugger.paused") { |
150 this.handleDebuggerPaused(message); | 212 this.handleDebuggerPaused(message); |
151 } else if (method == "Debugger.scriptParsed") { | 213 } else if (method == "Debugger.scriptParsed") { |
152 this.handleDebuggerScriptParsed(message); | 214 this.handleDebuggerScriptParsed(message); |
153 } | 215 } |
154 } | 216 } |
155 | 217 |
156 handleDebuggerPaused(message) { | 218 handleDebuggerPaused(message) { |
157 const params = message.params; | 219 const params = message.params; |
158 | 220 |
221 // Skip break events in this file. | |
222 if (params.callFrames[0].location.scriptId == this.thisScriptId) return; | |
223 | |
159 // TODO(jgruber): Arguments as needed. | 224 // TODO(jgruber): Arguments as needed. |
160 let execState = { frames: params.callFrames }; | 225 let execState = { frames : params.callFrames |
161 this.invokeListener(this.DebugEvent.Break, execState); | 226 , prepareStep : this.execStatePrepareStep.bind(this) |
227 , frame : (index) => this.execStateFrame(index ? params.call Frames[index] : params.callFrames[0]) | |
228 , frameCount : () => params.callFrames.length | |
Yang
2016/11/02 10:36:04
80-char limit.
jgruber
2016/11/07 11:35:11
Done.
| |
229 }; | |
230 let eventData = this.execStateFrame(params.callFrames[0]); | |
231 this.invokeListener(this.DebugEvent.Break, execState, eventData); | |
162 } | 232 } |
163 | 233 |
164 handleDebuggerScriptParsed(message) { | 234 handleDebuggerScriptParsed(message) { |
165 const params = message.params; | 235 const params = message.params; |
166 let eventData = { scriptId : params.scriptId | 236 let eventData = { scriptId : params.scriptId |
167 , eventType : this.DebugEvent.AfterCompile | 237 , eventType : this.DebugEvent.AfterCompile |
168 } | 238 } |
169 | 239 |
170 // TODO(jgruber): Arguments as needed. Still completely missing exec_state, | 240 // TODO(jgruber): Arguments as needed. Still completely missing exec_state, |
171 // and eventData used to contain the script mirror instead of its id. | 241 // and eventData used to contain the script mirror instead of its id. |
172 this.invokeListener(this.DebugEvent.AfterCompile, undefined, eventData, | 242 this.invokeListener(this.DebugEvent.AfterCompile, undefined, eventData, |
173 undefined); | 243 undefined); |
174 } | 244 } |
175 | 245 |
176 invokeListener(event, exec_state, event_data, data) { | 246 invokeListener(event, exec_state, event_data, data) { |
177 if (this.listener) { | 247 if (this.listener) { |
178 this.listener(event, exec_state, event_data, data); | 248 this.listener(event, exec_state, event_data, data); |
179 } | 249 } |
180 } | 250 } |
181 } | 251 } |
252 | |
253 // Simulate the debug object generated by --expose-debug-as debug. | |
254 var debug = { instance : undefined }; | |
255 Object.defineProperty(debug, 'Debug', { get: function() { | |
256 if (!debug.instance) { | |
257 debug.instance = new DebugWrapper(); | |
258 debug.instance.enable(); | |
259 } | |
260 return debug.instance; | |
261 }}); | |
OLD | NEW |