 Chromium Code Reviews
 Chromium Code Reviews Issue 2466273005:
  [debugger] Further stepping support in test wrapper  (Closed)
    
  
    Issue 2466273005:
  [debugger] Further stepping support in test wrapper  (Closed) 
  | 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 |