Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library debugger_page_element; | 5 library debugger_page_element; |
| 6 | 6 |
| 7 import 'dart:async'; | |
| 7 import 'dart:html'; | 8 import 'dart:html'; |
| 8 import 'observatory_element.dart'; | 9 import 'observatory_element.dart'; |
| 10 import 'package:observatory/cli.dart'; | |
| 9 import 'package:observatory/service.dart'; | 11 import 'package:observatory/service.dart'; |
| 10 import 'package:polymer/polymer.dart'; | 12 import 'package:polymer/polymer.dart'; |
| 11 | 13 |
| 14 abstract class DebuggerCommand extends Command { | |
| 15 Debugger debugger; | |
| 16 | |
| 17 DebuggerCommand(this.debugger, name, children) | |
|
Cutch
2015/02/09 21:45:05
Shouldn't all of these DebuggerCommands exists som
turnidge
2015/02/09 21:53:08
Yes. I want to leave them here until the interfac
| |
| 18 : super(name, children); | |
| 19 } | |
| 20 | |
| 21 class HelpCommand extends DebuggerCommand { | |
| 22 HelpCommand(Debugger debugger) : super(debugger, 'help', []); | |
| 23 | |
| 24 Future run(List<String> args) { | |
| 25 var con = debugger.console; | |
| 26 con.printLine('List of commands:'); | |
| 27 con.newline(); | |
| 28 | |
| 29 // TODO(turnidge): Build a real help system. | |
| 30 List completions = debugger.cmd.completeCommand(''); | |
| 31 completions = completions.map((s )=> s.trimRight()).toList(); | |
| 32 completions.sort(); | |
| 33 con.printLine(completions.toString()); | |
| 34 con.newline(); | |
| 35 con.printLine("Command prefixes are accepted (e.g. 'h' for 'help')"); | |
| 36 con.printLine("Hit [TAB] to complete a command (try 'i[TAB][TAB]')"); | |
| 37 con.printLine("Hit [ENTER] to repeat the last command"); | |
| 38 | |
| 39 return new Future.value(null); | |
| 40 } | |
| 41 } | |
| 42 | |
| 43 class PauseCommand extends DebuggerCommand { | |
| 44 PauseCommand(Debugger debugger) : super(debugger, 'pause', []); | |
| 45 | |
| 46 Future run(List<String> args) { | |
| 47 if (!debugger.isolatePaused()) { | |
|
Cutch
2015/02/09 21:45:06
There is a race here if the user requests an isola
turnidge
2015/02/09 21:53:08
Added a TODO on Isolate.pause.
| |
| 48 return debugger.isolate.pause(); | |
| 49 } else { | |
| 50 debugger.console.printLine('The program is already paused'); | |
| 51 return new Future.value(null); | |
| 52 } | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 class ContinueCommand extends DebuggerCommand { | |
| 57 ContinueCommand(Debugger debugger) : super(debugger, 'continue', []); | |
| 58 | |
| 59 Future run(List<String> args) { | |
| 60 if (debugger.isolatePaused()) { | |
| 61 return debugger.isolate.resume().then((_) { | |
| 62 debugger.warnOutOfDate(); | |
| 63 }); | |
| 64 } else { | |
| 65 debugger.console.printLine('The program must be paused'); | |
| 66 return new Future.value(null); | |
| 67 } | |
| 68 } | |
| 69 } | |
| 70 | |
| 71 class NextCommand extends DebuggerCommand { | |
| 72 NextCommand(Debugger debugger) : super(debugger, 'next', []); | |
| 73 | |
| 74 Future run(List<String> args) { | |
| 75 if (debugger.isolatePaused()) { | |
| 76 var event = debugger.isolate.pauseEvent; | |
| 77 if (event.eventType == 'IsolateCreated') { | |
| 78 debugger.console.printLine("Type 'continue' to start the isolate"); | |
| 79 return new Future.value(null); | |
| 80 } | |
| 81 if (event.eventType == 'IsolateShutdown') { | |
| 82 debugger.console.printLine("Type 'continue' to exit the isolate"); | |
| 83 return new Future.value(null); | |
| 84 } | |
| 85 return debugger.isolate.stepOver(); | |
| 86 } else { | |
| 87 debugger.console.printLine('The program is already running'); | |
| 88 return new Future.value(null); | |
| 89 } | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 class StepCommand extends DebuggerCommand { | |
| 94 StepCommand(Debugger debugger) : super(debugger, 'step', []); | |
| 95 | |
| 96 Future run(List<String> args) { | |
| 97 if (debugger.isolatePaused()) { | |
| 98 var event = debugger.isolate.pauseEvent; | |
| 99 if (event.eventType == 'IsolateCreated') { | |
| 100 debugger.console.printLine("Type 'continue' to start the isolate"); | |
| 101 return new Future.value(null); | |
| 102 } | |
| 103 if (event.eventType == 'IsolateShutdown') { | |
| 104 debugger.console.printLine("Type 'continue' to exit the isolate"); | |
| 105 return new Future.value(null); | |
| 106 } | |
| 107 return debugger.isolate.stepInto(); | |
| 108 } else { | |
| 109 debugger.console.printLine('The program is already running'); | |
| 110 return new Future.value(null); | |
| 111 } | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 class FinishCommand extends DebuggerCommand { | |
| 116 FinishCommand(Debugger debugger) : super(debugger, 'finish', []); | |
| 117 | |
| 118 Future run(List<String> args) { | |
| 119 if (debugger.isolatePaused()) { | |
| 120 return debugger.isolate.stepOut(); | |
| 121 } else { | |
| 122 debugger.console.printLine('The program is already running'); | |
| 123 return new Future.value(null); | |
| 124 } | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 // TODO(turnidge): Add argument completion. | |
| 129 class DeleteCommand extends DebuggerCommand { | |
| 130 DeleteCommand(Debugger debugger) : super(debugger, 'delete', []); | |
| 131 | |
| 132 Future run(List<String> args) { | |
| 133 if (args.length < 1) { | |
| 134 debugger.console.printLine('delete expects one or more arguments'); | |
| 135 return new Future.value(null); | |
| 136 } | |
| 137 List toDelete = []; | |
| 138 for (var arg in args) { | |
| 139 int id = int.parse(arg); | |
| 140 var bpt = null; | |
| 141 for (var candidate in debugger.isolate.breakpoints) { | |
| 142 if (candidate['breakpointNumber'] == id) { | |
| 143 bpt = candidate; | |
| 144 break; | |
| 145 } | |
| 146 } | |
| 147 if (bpt == null) { | |
| 148 debugger.console.printLine("Invalid breakpoint id '${id}'"); | |
| 149 return new Future.value(null); | |
| 150 } | |
| 151 toDelete.add(bpt); | |
| 152 } | |
| 153 List pending = []; | |
| 154 for (var bpt in toDelete) { | |
| 155 pending.add(debugger.isolate.removeBreakpoint(bpt).then((_) { | |
| 156 var id = bpt['breakpointNumber']; | |
| 157 debugger.console.printLine("Removed breakpoint $id"); | |
| 158 })); | |
| 159 } | |
| 160 return Future.wait(pending); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 class InfoBreakpointsCommand extends DebuggerCommand { | |
| 165 InfoBreakpointsCommand(Debugger debugger) : super(debugger, 'breakpoints', []) ; | |
|
Cutch
2015/02/09 21:45:05
80 column limit.
turnidge
2015/02/09 21:53:08
Done.
| |
| 166 | |
| 167 Future run(List<String> args) { | |
| 168 return debugger.isolate.reloadBreakpoints().then((_) { | |
| 169 if (debugger.isolate.breakpoints.isEmpty) { | |
| 170 debugger.console.printLine('No breakpoints'); | |
| 171 } | |
| 172 for (var bpt in debugger.isolate.breakpoints) { | |
| 173 var bpId = bpt['breakpointNumber']; | |
| 174 var script = bpt['location']['script']; | |
| 175 var tokenPos = bpt['location']['tokenPos']; | |
| 176 var line = script.tokenToLine(tokenPos); | |
| 177 var col = script.tokenToCol(tokenPos); | |
| 178 debugger.console.printLine( | |
| 179 'Breakpoint ${bpId} at ${script.name}:${line}:${col}'); | |
| 180 } | |
| 181 }); | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 class InfoIsolatesCommand extends DebuggerCommand { | |
| 186 InfoIsolatesCommand(Debugger debugger) : super(debugger, 'isolates', []); | |
| 187 | |
| 188 Future run(List<String> args) { | |
| 189 for (var isolate in debugger.isolate.vm.isolates) { | |
| 190 debugger.console.printLine( | |
| 191 "Isolate ${isolate.id} '${isolate.name}'"); | |
| 192 } | |
| 193 return new Future.value(null); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 class InfoCommand extends DebuggerCommand { | |
| 198 InfoCommand(Debugger debugger) : super(debugger, 'info', [ | |
| 199 new InfoBreakpointsCommand(debugger), | |
| 200 new InfoIsolatesCommand(debugger), | |
| 201 ]); | |
| 202 | |
| 203 Future run(List<String> args) { | |
| 204 debugger.console.printLine("Invalid info command"); | |
| 205 return new Future.value(null); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 class RefreshCoverageCommand extends DebuggerCommand { | |
| 210 RefreshCoverageCommand(Debugger debugger) : super(debugger, 'coverage', []); | |
| 211 | |
| 212 Future run(List<String> args) { | |
| 213 Set<Script> scripts = debugger.stackElement.activeScripts(); | |
| 214 List pending = []; | |
| 215 for (var script in scripts) { | |
| 216 pending.add(script.refreshCoverage().then((_) { | |
| 217 debugger.console.printLine('Refreshed coverage for ${script.name}'); | |
| 218 })); | |
| 219 } | |
| 220 return Future.wait(pending); | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 class RefreshCommand extends DebuggerCommand { | |
| 225 RefreshCommand(Debugger debugger) : super(debugger, 'refresh', [ | |
| 226 new RefreshCoverageCommand(debugger), | |
| 227 ]); | |
| 228 | |
| 229 Future run(List<String> args) { | |
| 230 return debugger.refreshStack(); | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 // Tracks the state for an isolate debugging session. | |
| 235 class Debugger { | |
| 236 RootCommand cmd; | |
| 237 DebuggerConsoleElement console; | |
| 238 DebuggerStackElement stackElement; | |
| 239 ServiceMap stack; | |
| 240 | |
| 241 Debugger() { | |
| 242 cmd = new RootCommand([ | |
| 243 new HelpCommand(this), | |
| 244 new PauseCommand(this), | |
| 245 new ContinueCommand(this), | |
| 246 new NextCommand(this), | |
| 247 new StepCommand(this), | |
| 248 new FinishCommand(this), | |
| 249 new DeleteCommand(this), | |
| 250 new InfoCommand(this), | |
| 251 new RefreshCommand(this), | |
| 252 ]); | |
| 253 } | |
| 254 | |
| 255 void set isolate(Isolate iso) { | |
| 256 _isolate = iso; | |
| 257 if (_isolate != null) { | |
| 258 _isolate.reload().then((_) { | |
| 259 _isolate.vm.events.stream.listen(_onEvent); | |
| 260 _refreshStack(isolate.pauseEvent).then((_) { | |
| 261 reportStatus(); | |
| 262 }); | |
| 263 }); | |
| 264 } | |
| 265 } | |
| 266 Isolate get isolate => _isolate; | |
| 267 Isolate _isolate; | |
| 268 | |
| 269 void init() { | |
| 270 console.newline(); | |
| 271 console.printBold("Type 'h' for help"); | |
| 272 } | |
| 273 | |
| 274 Future refreshStack() { | |
| 275 return _refreshStack(isolate.pauseEvent).then((_) { | |
| 276 reportStatus(); | |
| 277 }); | |
| 278 } | |
| 279 | |
| 280 bool isolatePaused() { | |
| 281 // TODO(turnidge): Stop relying on the isolate to track the last | |
| 282 // pause event. Since we listen to events directly in the | |
| 283 // debugger, this could introduce a race. | |
| 284 return isolate.pauseEvent != null; | |
| 285 } | |
| 286 | |
| 287 void warnOutOfDate() { | |
| 288 // Wait a bit, then tell the user that the stack may be out of date. | |
| 289 new Timer(const Duration(seconds:2), () { | |
| 290 if (!isolatePaused()) { | |
| 291 stackElement.isSampled = true; | |
| 292 } | |
| 293 }); | |
| 294 } | |
| 295 | |
| 296 Future<ServiceMap> _refreshStack(ServiceEvent pauseEvent) { | |
| 297 return isolate.getStack().then((result) { | |
| 298 stack = result; | |
| 299 // TODO(turnidge): Replace only the changed part of the stack to | |
| 300 // reduce flicker. | |
| 301 // stackElement.stack = stack; | |
| 302 stackElement.updateStack(stack, pauseEvent); | |
| 303 }); | |
| 304 } | |
| 305 | |
| 306 void reportStatus() { | |
| 307 if (_isolate.idle) { | |
| 308 console.printLine('Isolate is idle'); | |
| 309 } else if (_isolate.running) { | |
| 310 console.printLine("Isolate is running (type 'pause' to interrupt)"); | |
| 311 } else if (_isolate.pauseEvent != null) { | |
| 312 _reportPause(_isolate.pauseEvent); | |
| 313 } else { | |
| 314 console.printLine('Isolate is in unknown state'); | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 void _reportPause(ServiceEvent event) { | |
| 319 if (event.eventType == 'IsolateCreated') { | |
| 320 console.printLine( | |
| 321 "Paused at isolate start (type 'continue' to start the isolate')"); | |
| 322 } else if (event.eventType == 'IsolateShutdown') { | |
| 323 console.printLine( | |
| 324 "Paused at isolate exit (type 'continue' to exit the isolate')"); | |
| 325 } | |
| 326 if (stack['frames'].length > 0) { | |
| 327 var frame = stack['frames'][0]; | |
| 328 var script = frame['script']; | |
| 329 script.load().then((_) { | |
| 330 var line = script.tokenToLine(frame['tokenPos']); | |
| 331 var col = script.tokenToCol(frame['tokenPos']); | |
| 332 if (event.breakpoint != null) { | |
| 333 var bpId = event.breakpoint['breakpointNumber']; | |
| 334 console.printLine('Breakpoint ${bpId} at ${script.name}:${line}:${col} '); | |
| 335 } else if (event.exception != null) { | |
| 336 // TODO(turnidge): Test this. | |
| 337 console.printLine( | |
| 338 'Exception ${event.exception} at ${script.name}:${line}:${col}'); | |
| 339 } else { | |
| 340 console.printLine('Paused at ${script.name}:${line}:${col}'); | |
| 341 } | |
| 342 }); | |
| 343 } | |
| 344 } | |
| 345 | |
| 346 void _onEvent(ServiceEvent event) { | |
| 347 if (event.owner != isolate) { | |
| 348 return; | |
| 349 } | |
| 350 switch(event.eventType) { | |
| 351 case 'IsolateShutdown': | |
| 352 console.printLine('Isolate shutdown'); | |
| 353 isolate = null; | |
| 354 break; | |
| 355 | |
| 356 case 'BreakpointReached': | |
| 357 case 'IsolateInterrupted': | |
| 358 case 'ExceptionThrown': | |
| 359 _refreshStack(event).then((_) { | |
| 360 _reportPause(event); | |
| 361 }); | |
| 362 break; | |
| 363 | |
| 364 case 'IsolateResumed': | |
| 365 console.printLine('Continuing...'); | |
| 366 break; | |
| 367 | |
| 368 case '_Graph': | |
| 369 case 'BreakpointResolved': | |
| 370 case 'IsolateCreated': | |
| 371 case 'GC': | |
| 372 // Ignore these events for now. | |
| 373 break; | |
| 374 | |
| 375 default: | |
| 376 console.printLine('Unrecognized event: $event'); | |
| 377 break; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 String complete(String line) { | |
| 382 List<String> completions = cmd.completeCommand(line); | |
| 383 if (completions.length == 0) { | |
| 384 // No completions. Leave the line alone. | |
| 385 return line; | |
| 386 } else if (completions.length == 1) { | |
| 387 // Unambiguous completion. | |
| 388 return completions[0]; | |
| 389 } else { | |
| 390 // Ambigous completion. | |
| 391 completions = completions.map((s )=> s.trimRight()).toList(); | |
| 392 completions.sort(); | |
| 393 console.printBold(completions.toString()); | |
| 394 | |
| 395 // TODO(turnidge): Complete to common prefix of all completions. | |
| 396 return line; | |
| 397 } | |
| 398 } | |
| 399 | |
| 400 // TODO(turnidge): Implement real command line history. | |
| 401 String lastCommand; | |
| 402 bool busy = false; | |
| 403 | |
| 404 Future run(String command) { | |
| 405 assert(!busy); | |
| 406 busy = true; | |
| 407 if (command == '') { | |
| 408 command = lastCommand; | |
| 409 } | |
| 410 lastCommand = command; | |
| 411 console.printBold('\$ $command'); | |
| 412 return cmd.runCommand(command).then((_) { | |
| 413 busy = false; | |
| 414 }).catchError((e) { | |
| 415 console.printLine('ERROR $e'); | |
| 416 }); | |
| 417 } | |
| 418 } | |
| 419 | |
| 12 @CustomTag('debugger-page') | 420 @CustomTag('debugger-page') |
| 13 class DebuggerPageElement extends ObservatoryElement { | 421 class DebuggerPageElement extends ObservatoryElement { |
| 14 @published Isolate isolate; | 422 @published Isolate isolate; |
| 15 @published bool showConsole = false; | 423 |
| 424 isolateChanged(oldValue) { | |
| 425 if (isolate != null) { | |
| 426 debugger.isolate = isolate; | |
| 427 } | |
| 428 } | |
| 429 Debugger debugger = new Debugger(); | |
| 16 | 430 |
| 17 DebuggerPageElement.created() : super.created(); | 431 DebuggerPageElement.created() : super.created(); |
| 18 | 432 |
| 19 @override | 433 @override |
| 20 void attached() { | 434 void attached() { |
| 21 super.attached(); | 435 super.attached(); |
| 22 | 436 |
| 23 // TODO(turnidge): Get these values from the DOM. | 437 var navbarDiv = $['navbarDiv']; |
| 24 // TODO(turnidge): splitterHeight is 0 until I implement it. | 438 var stackDiv = $['stackDiv']; |
| 25 const int navbarHeight = 56; | 439 var splitterDiv = $['splitterDiv']; |
| 26 const int splitterHeight = 0; | 440 var cmdDiv = $['commandDiv']; |
| 27 const int cmdHeight = 22; | 441 var consoleDiv = $['consoleDiv']; |
| 28 | 442 |
| 29 var stack = $['stack']; | 443 int navbarHeight = navbarDiv.clientHeight; |
| 444 int splitterHeight = splitterDiv.clientHeight; | |
| 445 int cmdHeight = cmdDiv.clientHeight; | |
| 446 | |
| 30 int windowHeight = window.innerHeight; | 447 int windowHeight = window.innerHeight; |
| 31 int available = windowHeight - (navbarHeight + splitterHeight); | 448 int fixedHeight = navbarHeight + splitterHeight + cmdHeight; |
| 32 int stackHeight = available ~/ 1.3; | 449 int available = windowHeight - fixedHeight; |
| 33 if (showConsole) { | 450 int stackHeight = available ~/ 1.6; |
| 34 stack.style.setProperty('height', '${stackHeight}px'); | 451 stackDiv.style.setProperty('height', '${stackHeight}px'); |
| 35 } else { | 452 |
| 36 stack.style.setProperty('height', '${available}px'); | 453 // Wire the debugger object to the stack, console, and command line. |
| 37 } | 454 var stackElement = $['stackElement']; |
| 455 debugger.stackElement = stackElement; | |
| 456 stackElement.debugger = debugger; | |
| 457 debugger.console = $['console']; | |
| 458 $['commandline'].debugger = debugger; | |
| 459 debugger.init(); | |
| 38 } | 460 } |
| 461 | |
| 39 } | 462 } |
| 40 | 463 |
| 41 @CustomTag('debugger-stack') | 464 @CustomTag('debugger-stack') |
| 42 class DebuggerStackElement extends ObservatoryElement { | 465 class DebuggerStackElement extends ObservatoryElement { |
| 43 @published Isolate isolate; | 466 @published Isolate isolate; |
| 44 @published ServiceMap stack; | 467 @observable bool hasStack = false; |
| 45 @published int activeFrame = 0; | 468 @observable bool isSampled = false; |
| 469 Debugger debugger = null; | |
| 46 | 470 |
| 47 isolateChanged(oldValue) { | 471 _addFrame(List frameList, ObservableMap frameInfo, bool expand) { |
| 48 isolate.getStack().then((result) { | 472 DebuggerFrameElement frameElement = new Element.tag('debugger-frame'); |
| 49 stack = result; | 473 frameElement.expand = expand; |
| 50 }); | 474 frameElement.frame = frameInfo; |
| 475 | |
| 476 var li = new LIElement(); | |
| 477 li.classes.add('list-group-item'); | |
| 478 li.children.insert(0, frameElement); | |
| 479 | |
| 480 frameList.insert(0, li); | |
| 481 } | |
| 482 | |
| 483 void updateStack(ServiceMap newStack, ServiceEvent pauseEvent) { | |
| 484 List frameElements = $['frameList'].children; | |
| 485 List newFrames = newStack['frames']; | |
| 486 | |
| 487 // Remove any frames whose functions don't match, starting from | |
| 488 // bottom of stack. | |
| 489 int oldPos = frameElements.length - 1; | |
| 490 int newPos = newFrames.length - 1; | |
| 491 while (oldPos >= 0 && newPos >= 0) { | |
| 492 if (!frameElements[oldPos].children[0].matchFrame(newFrames[newPos])) { | |
| 493 // The rest of the frame elements no longer match. Remove them. | |
| 494 for (int i = 0; i <= oldPos; i++) { | |
| 495 // NOTE(turnidge): removeRange is missing, sadly. | |
| 496 frameElements.removeAt(0); | |
| 497 } | |
| 498 break; | |
| 499 } | |
| 500 oldPos--; | |
| 501 newPos--; | |
| 502 } | |
| 503 | |
| 504 // Remove any extra frames. | |
| 505 if (frameElements.length > newFrames.length) { | |
| 506 // Remove old frames from the top of stack. | |
| 507 int removeCount = frameElements.length - newFrames.length; | |
| 508 for (int i = 0; i < removeCount; i++) { | |
| 509 frameElements.removeAt(0); | |
| 510 } | |
| 511 } | |
| 512 | |
| 513 // Add any new frames. | |
| 514 int newCount = 0; | |
| 515 if (frameElements.length < newFrames.length) { | |
| 516 // Add new frames to the top of stack. | |
| 517 newCount = newFrames.length - frameElements.length; | |
| 518 for (int i = newCount-1; i >= 0; i--) { | |
| 519 _addFrame(frameElements, newFrames[i], i == 0); | |
| 520 } | |
| 521 } | |
| 522 assert(frameElements.length == newFrames.length); | |
| 523 | |
| 524 if (frameElements.isNotEmpty) { | |
| 525 frameElements[0].children[0].expand = true; | |
| 526 for (int i = newCount; i < frameElements.length; i++) { | |
| 527 frameElements[i].children[0].updateFrame(newFrames[i]); | |
| 528 } | |
| 529 } | |
| 530 | |
| 531 isSampled = pauseEvent == null; | |
| 532 hasStack = frameElements.isNotEmpty; | |
| 533 } | |
| 534 | |
| 535 Set<Script> activeScripts() { | |
| 536 var s = new Set<Script>(); | |
| 537 List frameElements = $['frameList'].children; | |
| 538 for (var frameElement in frameElements) { | |
| 539 s.add(frameElement.children[0].script); | |
| 540 } | |
| 541 return s; | |
| 542 } | |
| 543 | |
| 544 doPauseIsolate(_) { | |
| 545 if (debugger != null) { | |
| 546 return debugger.isolate.pause(); | |
| 547 } else { | |
| 548 return new Future.value(null); | |
| 549 } | |
| 550 } | |
| 551 | |
| 552 doRefreshStack(_) { | |
| 553 if (debugger != null) { | |
| 554 return debugger.refreshStack(); | |
| 555 } else { | |
| 556 return new Future.value(null); | |
| 557 } | |
| 51 } | 558 } |
| 52 | 559 |
| 53 DebuggerStackElement.created() : super.created(); | 560 DebuggerStackElement.created() : super.created(); |
| 54 } | 561 } |
| 55 | 562 |
| 56 @CustomTag('debugger-frame') | 563 @CustomTag('debugger-frame') |
| 57 class DebuggerFrameElement extends ObservatoryElement { | 564 class DebuggerFrameElement extends ObservatoryElement { |
| 58 @published ObservableMap frame; | 565 @published ObservableMap frame; |
| 59 | 566 |
| 60 // When true, the frame will start out expanded. | 567 // When true, the frame will start out expanded. |
| 61 @published bool expand = false; | 568 @published bool expand = false; |
| 62 | 569 |
| 63 @observable String scriptHeight; | 570 @observable String scriptHeight; |
| 64 @observable bool expanded = false; | 571 @observable bool expanded = false; |
| 65 @observable bool busy = false; | 572 @observable bool busy = false; |
| 66 | 573 |
| 67 DebuggerFrameElement.created() : super.created(); | 574 DebuggerFrameElement.created() : super.created(); |
| 68 | 575 |
| 576 bool matchFrame(ObservableMap newFrame) { | |
| 577 return newFrame['function'].id == frame['function'].id; | |
| 578 } | |
| 579 | |
| 580 void updateFrame(ObservableMap newFrame) { | |
| 581 assert(matchFrame(newFrame)); | |
| 582 frame['depth'] = newFrame['depth']; | |
| 583 frame['tokenPos'] = newFrame['tokenPos']; | |
| 584 frame['vars'] = newFrame['vars']; | |
| 585 } | |
| 586 | |
| 587 Script get script => frame['script']; | |
| 588 | |
| 69 @override | 589 @override |
| 70 void attached() { | 590 void attached() { |
| 71 super.attached(); | 591 super.attached(); |
| 72 int windowHeight = window.innerHeight; | 592 int windowHeight = window.innerHeight; |
| 73 scriptHeight = '${windowHeight ~/ 1.6}px'; | 593 scriptHeight = '${windowHeight ~/ 1.6}px'; |
| 74 } | 594 } |
| 75 | 595 |
| 76 void expandChanged(oldValue) { | 596 void expandChanged(oldValue) { |
| 77 if (expand != expanded) { | 597 if (expand != expanded) { |
| 78 toggleExpand(null, null, null); | 598 toggleExpand(null, null, null); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 95 busy = false; | 615 busy = false; |
| 96 }); | 616 }); |
| 97 } | 617 } |
| 98 } | 618 } |
| 99 | 619 |
| 100 @CustomTag('debugger-console') | 620 @CustomTag('debugger-console') |
| 101 class DebuggerConsoleElement extends ObservatoryElement { | 621 class DebuggerConsoleElement extends ObservatoryElement { |
| 102 @published Isolate isolate; | 622 @published Isolate isolate; |
| 103 | 623 |
| 104 DebuggerConsoleElement.created() : super.created(); | 624 DebuggerConsoleElement.created() : super.created(); |
| 625 | |
| 626 void printLine(String line) { | |
| 627 var div = new DivElement(); | |
| 628 div.classes.add('normal'); | |
| 629 div.appendText(line); | |
| 630 $['consoleText'].children.add(div); | |
| 631 div.scrollIntoView(); | |
| 632 } | |
| 633 | |
| 634 void printBold(String line) { | |
| 635 var div = new DivElement(); | |
| 636 div.classes.add('bold'); | |
| 637 div.appendText(line); | |
| 638 $['consoleText'].children.add(div); | |
| 639 div.scrollIntoView(); | |
| 640 } | |
| 641 | |
| 642 void newline() { | |
| 643 var br = new BRElement(); | |
| 644 $['consoleText'].children.add(br); | |
| 645 br.scrollIntoView(); | |
| 646 } | |
| 105 } | 647 } |
| 106 | 648 |
| 107 @CustomTag('debugger-input') | 649 @CustomTag('debugger-input') |
| 108 class DebuggerInputElement extends ObservatoryElement { | 650 class DebuggerInputElement extends ObservatoryElement { |
| 109 @published Isolate isolate; | 651 @published Isolate isolate; |
| 110 @published String text = ''; | 652 @published String text = ''; |
| 653 @observable Debugger debugger; | |
| 111 | 654 |
| 112 @override | 655 @override |
| 113 void ready() { | 656 void ready() { |
| 114 super.ready(); | 657 super.ready(); |
| 115 var textBox = $['textBox']; | 658 var textBox = $['textBox']; |
| 116 textBox.select(); | 659 textBox.select(); |
| 117 textBox.onKeyDown.listen((KeyboardEvent e) { | 660 textBox.onKeyDown.listen((KeyboardEvent e) { |
| 118 switch (e.keyCode) { | 661 switch (e.keyCode) { |
| 119 case KeyCode.TAB: | 662 case KeyCode.TAB: |
| 120 e.preventDefault(); | 663 e.preventDefault(); |
| 121 textBox.setRangeText('TAB'); | 664 int cursorPos = textBox.selectionStart; |
| 122 textBox.setSelectionRange(textBox.selectionStart + 3, | 665 var completion = debugger.complete(text.substring(0, cursorPos)); |
| 123 textBox.selectionStart + 3); | 666 text = completion + text.substring(cursorPos); |
| 667 // TODO(turnidge): Move the cursor to the end of the | |
| 668 // completion, rather than the end of the string. | |
| 124 break; | 669 break; |
| 125 case KeyCode.ENTER: | 670 case KeyCode.ENTER: |
| 126 print('Debugger command (not implemented): $text'); | 671 if (!debugger.busy) { |
| 127 text = ''; | 672 debugger.run(text); |
| 673 text = ''; | |
| 674 } | |
| 128 break; | 675 break; |
| 129 } | 676 } |
| 130 }); | 677 }); |
| 131 } | 678 } |
| 132 | 679 |
| 133 DebuggerInputElement.created() : super.created(); | 680 DebuggerInputElement.created() : super.created(); |
| 134 } | 681 } |
| 135 | 682 |
| OLD | NEW |