| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 // Simple interactive debugger shell that connects to the Dart VM's debugger | |
| 6 // connection port. | |
| 7 | |
| 8 import "dart:convert"; | |
| 9 import "dart:io"; | |
| 10 import "dart:async"; | |
| 11 | |
| 12 | |
| 13 Map<int, Completer> outstandingCommands; | |
| 14 | |
| 15 Socket vmSock; | |
| 16 String vmData; | |
| 17 var stdinSubscription; | |
| 18 var vmSubscription; | |
| 19 int seqNum = 0; | |
| 20 int isolate_id = -1; | |
| 21 | |
| 22 final verbose = false; | |
| 23 final printMessages = false; | |
| 24 | |
| 25 // The location of the last paused event. | |
| 26 Map pausedLocation = null; | |
| 27 | |
| 28 | |
| 29 void printHelp() { | |
| 30 print(""" | |
| 31 q Quit debugger shell | |
| 32 bt Show backtrace | |
| 33 r Resume execution | |
| 34 s Single step | |
| 35 so Step over | |
| 36 si Step into | |
| 37 sbp [<file>] <line> Set breakpoint | |
| 38 rbp <id> Remove breakpoint with given id | |
| 39 po <id> Print object info for given id | |
| 40 eval obj <id> <expr> Evaluate expr on object id | |
| 41 eval cls <id> <expr> Evaluate expr on class id | |
| 42 eval lib <id> <expr> Evaluate expr in toplevel of library id | |
| 43 pl <id> <idx> [<len>] Print list element/slice | |
| 44 pc <id> Print class info for given id | |
| 45 ll List loaded libraries | |
| 46 plib <id> Print library info for given library id | |
| 47 slib <id> <true|false> Set library id debuggable | |
| 48 pg <id> Print all global variables visible within given library id | |
| 49 ls <lib_id> List loaded scripts in library | |
| 50 gs <lib_id> <script_url> Get source text of script in library | |
| 51 tok <lib_id> <script_url> Get line and token table of script in library | |
| 52 epi <none|all|unhandled> Set exception pause info | |
| 53 li List ids of all isolates in the VM | |
| 54 i <id> Interrupt execution of given isolate id | |
| 55 h Print help | |
| 56 """); | |
| 57 } | |
| 58 | |
| 59 | |
| 60 void quitShell() { | |
| 61 vmSubscription.cancel(); | |
| 62 vmSock.close(); | |
| 63 stdinSubscription.cancel(); | |
| 64 } | |
| 65 | |
| 66 | |
| 67 Future sendCmd(Map<String, dynamic> cmd) { | |
| 68 var completer = new Completer(); | |
| 69 int id = cmd["id"]; | |
| 70 outstandingCommands[id] = completer; | |
| 71 if (verbose) { | |
| 72 print("sending: '${JSON.encode(cmd)}'"); | |
| 73 } | |
| 74 vmSock.write(JSON.encode(cmd)); | |
| 75 return completer.future; | |
| 76 } | |
| 77 | |
| 78 void processCommand(String cmdLine) { | |
| 79 | |
| 80 void huh() { | |
| 81 print("'$cmdLine' not understood, try h for help"); | |
| 82 } | |
| 83 | |
| 84 seqNum++; | |
| 85 var args = cmdLine.split(' '); | |
| 86 if (args.length == 0) { | |
| 87 return; | |
| 88 } | |
| 89 var command = args[0]; | |
| 90 var simple_commands = | |
| 91 { 'r':'resume', 's':'stepOver', 'si':'stepInto', 'so':'stepOut'}; | |
| 92 if (simple_commands[command] != null) { | |
| 93 var cmd = { "id": seqNum, | |
| 94 "command": simple_commands[command], | |
| 95 "params": { "isolateId" : isolate_id } }; | |
| 96 sendCmd(cmd).then((result) => handleGenericResponse(result)); | |
| 97 } else if (command == "bt") { | |
| 98 var cmd = { "id": seqNum, | |
| 99 "command": "getStackTrace", | |
| 100 "params": { "isolateId" : isolate_id } }; | |
| 101 sendCmd(cmd).then((result) => handleStackTraceResponse(result)); | |
| 102 } else if (command == "ll") { | |
| 103 var cmd = { "id": seqNum, | |
| 104 "command": "getLibraries", | |
| 105 "params": { "isolateId" : isolate_id } }; | |
| 106 sendCmd(cmd).then((result) => handleGetLibraryResponse(result)); | |
| 107 } else if (command == "sbp" && args.length >= 2) { | |
| 108 var url, line; | |
| 109 if (args.length == 2 && pausedLocation != null) { | |
| 110 url = pausedLocation["url"]; | |
| 111 assert(url != null); | |
| 112 line = int.parse(args[1]); | |
| 113 } else { | |
| 114 url = args[1]; | |
| 115 line = int.parse(args[2]); | |
| 116 } | |
| 117 var cmd = { "id": seqNum, | |
| 118 "command": "setBreakpoint", | |
| 119 "params": { "isolateId" : isolate_id, | |
| 120 "url": url, | |
| 121 "line": line }}; | |
| 122 sendCmd(cmd).then((result) => handleSetBpResponse(result)); | |
| 123 } else if (command == "rbp" && args.length == 2) { | |
| 124 var cmd = { "id": seqNum, | |
| 125 "command": "removeBreakpoint", | |
| 126 "params": { "isolateId" : isolate_id, | |
| 127 "breakpointId": int.parse(args[1]) } }; | |
| 128 sendCmd(cmd).then((result) => handleGenericResponse(result)); | |
| 129 } else if (command == "ls" && args.length == 2) { | |
| 130 var cmd = { "id": seqNum, | |
| 131 "command": "getScriptURLs", | |
| 132 "params": { "isolateId" : isolate_id, | |
| 133 "libraryId": int.parse(args[1]) } }; | |
| 134 sendCmd(cmd).then((result) => handleGetScriptsResponse(result)); | |
| 135 } else if (command == "eval" && args.length > 3) { | |
| 136 var expr = args.getRange(3, args.length).join(" "); | |
| 137 var target = args[1]; | |
| 138 if (target == "obj") { | |
| 139 target = "objectId"; | |
| 140 } else if (target == "cls") { | |
| 141 target = "classId"; | |
| 142 } else if (target == "lib") { | |
| 143 target = "libraryId"; | |
| 144 } else { | |
| 145 huh(); | |
| 146 return; | |
| 147 } | |
| 148 var cmd = { "id": seqNum, | |
| 149 "command": "evaluateExpr", | |
| 150 "params": { "isolateId": isolate_id, | |
| 151 target: int.parse(args[2]), | |
| 152 "expression": expr } }; | |
| 153 sendCmd(cmd).then((result) => handleEvalResponse(result)); | |
| 154 } else if (command == "po" && args.length == 2) { | |
| 155 var cmd = { "id": seqNum, | |
| 156 "command": "getObjectProperties", | |
| 157 "params": { "isolateId" : isolate_id, | |
| 158 "objectId": int.parse(args[1]) } }; | |
| 159 sendCmd(cmd).then((result) => handleGetObjPropsResponse(result)); | |
| 160 } else if (command == "pl" && args.length >= 3) { | |
| 161 var cmd; | |
| 162 if (args.length == 3) { | |
| 163 cmd = { "id": seqNum, | |
| 164 "command": "getListElements", | |
| 165 "params": { "isolateId" : isolate_id, | |
| 166 "objectId": int.parse(args[1]), | |
| 167 "index": int.parse(args[2]) } }; | |
| 168 } else { | |
| 169 cmd = { "id": seqNum, | |
| 170 "command": "getListElements", | |
| 171 "params": { "isolateId" : isolate_id, | |
| 172 "objectId": int.parse(args[1]), | |
| 173 "index": int.parse(args[2]), | |
| 174 "length": int.parse(args[3]) } }; | |
| 175 } | |
| 176 sendCmd(cmd).then((result) => handleGetListResponse(result)); | |
| 177 } else if (command == "pc" && args.length == 2) { | |
| 178 var cmd = { "id": seqNum, | |
| 179 "command": "getClassProperties", | |
| 180 "params": { "isolateId" : isolate_id, | |
| 181 "classId": int.parse(args[1]) } }; | |
| 182 sendCmd(cmd).then((result) => handleGetClassPropsResponse(result)); | |
| 183 } else if (command == "plib" && args.length == 2) { | |
| 184 var cmd = { "id": seqNum, | |
| 185 "command": "getLibraryProperties", | |
| 186 "params": {"isolateId" : isolate_id, | |
| 187 "libraryId": int.parse(args[1]) } }; | |
| 188 sendCmd(cmd).then((result) => handleGetLibraryPropsResponse(result)); | |
| 189 } else if (command == "slib" && args.length == 3) { | |
| 190 var cmd = { "id": seqNum, | |
| 191 "command": "setLibraryProperties", | |
| 192 "params": {"isolateId" : isolate_id, | |
| 193 "libraryId": int.parse(args[1]), | |
| 194 "debuggingEnabled": args[2] } }; | |
| 195 sendCmd(cmd).then((result) => handleSetLibraryPropsResponse(result)); | |
| 196 } else if (command == "pg" && args.length == 2) { | |
| 197 var cmd = { "id": seqNum, | |
| 198 "command": "getGlobalVariables", | |
| 199 "params": { "isolateId" : isolate_id, | |
| 200 "libraryId": int.parse(args[1]) } }; | |
| 201 sendCmd(cmd).then((result) => handleGetGlobalVarsResponse(result)); | |
| 202 } else if (command == "gs" && args.length == 3) { | |
| 203 var cmd = { "id": seqNum, | |
| 204 "command": "getScriptSource", | |
| 205 "params": { "isolateId" : isolate_id, | |
| 206 "libraryId": int.parse(args[1]), | |
| 207 "url": args[2] } }; | |
| 208 sendCmd(cmd).then((result) => handleGetSourceResponse(result)); | |
| 209 } else if (command == "tok" && args.length == 3) { | |
| 210 var cmd = { "id": seqNum, | |
| 211 "command": "getLineNumberTable", | |
| 212 "params": { "isolateId" : isolate_id, | |
| 213 "libraryId": int.parse(args[1]), | |
| 214 "url": args[2] } }; | |
| 215 sendCmd(cmd).then((result) => handleGetLineTableResponse(result)); | |
| 216 } else if (command == "epi" && args.length == 2) { | |
| 217 var cmd = { "id": seqNum, | |
| 218 "command": "setPauseOnException", | |
| 219 "params": { "isolateId" : isolate_id, | |
| 220 "exceptions": args[1] } }; | |
| 221 sendCmd(cmd).then((result) => handleGenericResponse(result)); | |
| 222 } else if (command == "li") { | |
| 223 var cmd = { "id": seqNum, "command": "getIsolateIds" }; | |
| 224 sendCmd(cmd).then((result) => handleGetIsolatesResponse(result)); | |
| 225 } else if (command == "i" && args.length == 2) { | |
| 226 var cmd = { "id": seqNum, | |
| 227 "command": "interrupt", | |
| 228 "params": { "isolateId": int.parse(args[1]) } }; | |
| 229 sendCmd(cmd).then((result) => handleGenericResponse(result)); | |
| 230 } else if (command == "q") { | |
| 231 quitShell(); | |
| 232 } else if (command == "h") { | |
| 233 printHelp(); | |
| 234 } else { | |
| 235 huh(); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 | |
| 240 String remoteObject(value) { | |
| 241 var kind = value["kind"]; | |
| 242 var text = value["text"]; | |
| 243 var id = value["objectId"]; | |
| 244 if (kind == "string") { | |
| 245 return "(string, id $id) '$text'"; | |
| 246 } else if (kind == "list") { | |
| 247 var len = value["length"]; | |
| 248 return "(list, id $id, len $len) $text"; | |
| 249 } else if (kind == "object") { | |
| 250 return "(obj, id $id) $text"; | |
| 251 } else if (kind == "function") { | |
| 252 var location = value['location'] != null | |
| 253 ? ", file '${value['location']['url']}'" | |
| 254 ", token pos ${value['location']['tokenOffset']}" | |
| 255 : ""; | |
| 256 var name = value['name']; | |
| 257 var signature = value['signature']; | |
| 258 return "(closure ${name}${signature} $location)"; | |
| 259 } else { | |
| 260 return "$text"; | |
| 261 } | |
| 262 } | |
| 263 | |
| 264 | |
| 265 printNamedObject(obj) { | |
| 266 var name = obj["name"]; | |
| 267 var value = obj["value"]; | |
| 268 print(" $name = ${remoteObject(value)}"); | |
| 269 } | |
| 270 | |
| 271 | |
| 272 handleGetObjPropsResponse(response) { | |
| 273 Map props = response["result"]; | |
| 274 int class_id = props["classId"]; | |
| 275 if (class_id == -1) { | |
| 276 print(" null"); | |
| 277 return; | |
| 278 } | |
| 279 List fields = props["fields"]; | |
| 280 print(" class id: $class_id"); | |
| 281 for (int i = 0; i < fields.length; i++) { | |
| 282 printNamedObject(fields[i]); | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 handleGetListResponse(response) { | |
| 287 Map result = response["result"]; | |
| 288 if (result["elements"] != null) { | |
| 289 // List slice. | |
| 290 var index = result["index"]; | |
| 291 var length = result["length"]; | |
| 292 List elements = result["elements"]; | |
| 293 assert(length == elements.length); | |
| 294 for (int i = 0; i < length; i++) { | |
| 295 var kind = elements[i]["kind"]; | |
| 296 var text = elements[i]["text"]; | |
| 297 print(" ${index + i}: ($kind) $text"); | |
| 298 } | |
| 299 } else { | |
| 300 // One element, a remote object. | |
| 301 print(result); | |
| 302 print(" ${remoteObject(result)}"); | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 | |
| 307 handleGetClassPropsResponse(response) { | |
| 308 Map props = response["result"]; | |
| 309 assert(props["name"] != null); | |
| 310 int libId = props["libraryId"]; | |
| 311 assert(libId != null); | |
| 312 print(" class ${props["name"]} (library id: $libId)"); | |
| 313 List fields = props["fields"]; | |
| 314 if (fields.length > 0) { | |
| 315 print(" static fields:"); | |
| 316 for (int i = 0; i < fields.length; i++) { | |
| 317 printNamedObject(fields[i]); | |
| 318 } | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 | |
| 323 handleGetLibraryPropsResponse(response) { | |
| 324 Map props = response["result"]; | |
| 325 assert(props["url"] != null); | |
| 326 print(" library url: ${props["url"]}"); | |
| 327 assert(props["debuggingEnabled"] != null); | |
| 328 print(" debugging enabled: ${props["debuggingEnabled"]}"); | |
| 329 List imports = props["imports"]; | |
| 330 assert(imports != null); | |
| 331 if (imports.length > 0) { | |
| 332 print(" imports:"); | |
| 333 for (int i = 0; i < imports.length; i++) { | |
| 334 print(" id ${imports[i]["libraryId"]} prefix ${imports[i]["prefix"]}"); | |
| 335 } | |
| 336 } | |
| 337 List globals = props["globals"]; | |
| 338 assert(globals != null); | |
| 339 if (globals.length > 0) { | |
| 340 print(" global variables:"); | |
| 341 for (int i = 0; i < globals.length; i++) { | |
| 342 printNamedObject(globals[i]); | |
| 343 } | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 | |
| 348 handleSetLibraryPropsResponse(response) { | |
| 349 Map props = response["result"]; | |
| 350 assert(props["debuggingEnabled"] != null); | |
| 351 print(" debugging enabled: ${props["debuggingEnabled"]}"); | |
| 352 } | |
| 353 | |
| 354 | |
| 355 handleGetGlobalVarsResponse(response) { | |
| 356 List globals = response["result"]["globals"]; | |
| 357 for (int i = 0; i < globals.length; i++) { | |
| 358 printNamedObject(globals[i]); | |
| 359 } | |
| 360 } | |
| 361 | |
| 362 | |
| 363 handleGetSourceResponse(response) { | |
| 364 Map result = response["result"]; | |
| 365 String source = result["text"]; | |
| 366 print("Source text:\n$source\n--------"); | |
| 367 } | |
| 368 | |
| 369 | |
| 370 handleGetLineTableResponse(response) { | |
| 371 Map result = response["result"]; | |
| 372 var info = result["lines"]; | |
| 373 print("Line info table:\n$info"); | |
| 374 } | |
| 375 | |
| 376 | |
| 377 handleGetIsolatesResponse(response) { | |
| 378 Map result = response["result"]; | |
| 379 print("Isolates: ${result["isolateIds"]}"); | |
| 380 } | |
| 381 | |
| 382 | |
| 383 void handleGetLibraryResponse(response) { | |
| 384 Map result = response["result"]; | |
| 385 List libs = result["libraries"]; | |
| 386 print("Loaded libraries:"); | |
| 387 print(libs); | |
| 388 for (int i = 0; i < libs.length; i++) { | |
| 389 print(" ${libs[i]["id"]} ${libs[i]["url"]}"); | |
| 390 } | |
| 391 } | |
| 392 | |
| 393 | |
| 394 void handleGetScriptsResponse(response) { | |
| 395 Map result = response["result"]; | |
| 396 List urls = result["urls"]; | |
| 397 print("Loaded scripts:"); | |
| 398 for (int i = 0; i < urls.length; i++) { | |
| 399 print(" $i ${urls[i]}"); | |
| 400 } | |
| 401 } | |
| 402 | |
| 403 | |
| 404 void handleEvalResponse(response) { | |
| 405 Map result = response["result"]; | |
| 406 print(remoteObject(result)); | |
| 407 } | |
| 408 | |
| 409 | |
| 410 void handleSetBpResponse(response) { | |
| 411 Map result = response["result"]; | |
| 412 var id = result["breakpointId"]; | |
| 413 assert(id != null); | |
| 414 print("Set BP $id"); | |
| 415 } | |
| 416 | |
| 417 | |
| 418 void handleGenericResponse(response) { | |
| 419 if (response["error"] != null) { | |
| 420 print("Error: ${response["error"]}"); | |
| 421 } | |
| 422 } | |
| 423 | |
| 424 | |
| 425 void handleStackTraceResponse(response) { | |
| 426 Map result = response["result"]; | |
| 427 List callFrames = result["callFrames"]; | |
| 428 assert(callFrames != null); | |
| 429 printStackTrace(callFrames); | |
| 430 } | |
| 431 | |
| 432 | |
| 433 void printStackFrame(frame_num, Map frame) { | |
| 434 var fname = frame["functionName"]; | |
| 435 var libId = frame["location"]["libraryId"]; | |
| 436 var url = frame["location"]["url"]; | |
| 437 var toff = frame["location"]["tokenOffset"]; | |
| 438 print("$frame_num $fname (url: $url token: $toff lib: $libId)"); | |
| 439 List locals = frame["locals"]; | |
| 440 for (int i = 0; i < locals.length; i++) { | |
| 441 printNamedObject(locals[i]); | |
| 442 } | |
| 443 } | |
| 444 | |
| 445 | |
| 446 void printStackTrace(List frames) { | |
| 447 for (int i = 0; i < frames.length; i++) { | |
| 448 printStackFrame(i, frames[i]); | |
| 449 } | |
| 450 } | |
| 451 | |
| 452 | |
| 453 void handlePausedEvent(msg) { | |
| 454 assert(msg["params"] != null); | |
| 455 var reason = msg["params"]["reason"]; | |
| 456 isolate_id = msg["params"]["isolateId"]; | |
| 457 assert(isolate_id != null); | |
| 458 pausedLocation = msg["params"]["location"]; | |
| 459 assert(pausedLocation != null); | |
| 460 if (reason == "breakpoint") { | |
| 461 print("Isolate $isolate_id paused on breakpoint"); | |
| 462 print("location: $pausedLocation"); | |
| 463 } else if (reason == "interrupted") { | |
| 464 print("Isolate $isolate_id paused due to an interrupt"); | |
| 465 } else { | |
| 466 assert(reason == "exception"); | |
| 467 var excObj = msg["params"]["exception"]; | |
| 468 print("Isolate $isolate_id paused on exception"); | |
| 469 print(remoteObject(excObj)); | |
| 470 } | |
| 471 } | |
| 472 | |
| 473 | |
| 474 void processVmMessage(String jsonString) { | |
| 475 var msg = JSON.decode(jsonString); | |
| 476 if (msg == null) { | |
| 477 return; | |
| 478 } | |
| 479 var event = msg["event"]; | |
| 480 if (event == "paused") { | |
| 481 handlePausedEvent(msg); | |
| 482 return; | |
| 483 } | |
| 484 if (event == "breakpointResolved") { | |
| 485 Map params = msg["params"]; | |
| 486 assert(params != null); | |
| 487 print("BP ${params["breakpointId"]} resolved and " | |
| 488 "set at line ${params["line"]}."); | |
| 489 return; | |
| 490 } | |
| 491 if (event == "isolate") { | |
| 492 Map params = msg["params"]; | |
| 493 assert(params != null); | |
| 494 print("Isolate ${params["id"]} has been ${params["reason"]}."); | |
| 495 return; | |
| 496 } | |
| 497 if (msg["id"] != null) { | |
| 498 var id = msg["id"]; | |
| 499 if (outstandingCommands.containsKey(id)) { | |
| 500 if (msg["error"] != null) { | |
| 501 print("VM says: ${msg["error"]}"); | |
| 502 } else { | |
| 503 var completer = outstandingCommands[id]; | |
| 504 completer.complete(msg); | |
| 505 } | |
| 506 outstandingCommands.remove(id); | |
| 507 } | |
| 508 } | |
| 509 } | |
| 510 | |
| 511 bool haveGarbageVmData() { | |
| 512 if (vmData == null || vmData.length == 0) return false; | |
| 513 var i = 0, char = " "; | |
| 514 while (i < vmData.length) { | |
| 515 char = vmData[i]; | |
| 516 if (char != " " && char != "\n" && char != "\r" && char != "\t") break; | |
| 517 i++; | |
| 518 } | |
| 519 if (i >= vmData.length) { | |
| 520 return false; | |
| 521 } else { | |
| 522 return char != "{"; | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 | |
| 527 void processVmData(String data) { | |
| 528 if (vmData == null || vmData.length == 0) { | |
| 529 vmData = data; | |
| 530 } else { | |
| 531 vmData = vmData + data; | |
| 532 } | |
| 533 if (haveGarbageVmData()) { | |
| 534 print("Error: have garbage data from VM: '$vmData'"); | |
| 535 return; | |
| 536 } | |
| 537 int msg_len = jsonObjectLength(vmData); | |
| 538 if (printMessages && msg_len == 0) { | |
| 539 print("have partial or illegal json message" | |
| 540 " of ${vmData.length} chars:\n'$vmData'"); | |
| 541 return; | |
| 542 } | |
| 543 while (msg_len > 0 && msg_len <= vmData.length) { | |
| 544 if (msg_len == vmData.length) { | |
| 545 if (printMessages) { print("have one full message:\n$vmData"); } | |
| 546 processVmMessage(vmData); | |
| 547 vmData = null; | |
| 548 return; | |
| 549 } | |
| 550 if (printMessages) { print("at least one message: '$vmData'"); } | |
| 551 var msg = vmData.substring(0, msg_len); | |
| 552 if (printMessages) { print("first message: $msg"); } | |
| 553 vmData = vmData.substring(msg_len); | |
| 554 if (haveGarbageVmData()) { | |
| 555 print("Error: garbage data after previous message: '$vmData'"); | |
| 556 print("Previous message was: '$msg'"); | |
| 557 return; | |
| 558 } | |
| 559 processVmMessage(msg); | |
| 560 msg_len = jsonObjectLength(vmData); | |
| 561 } | |
| 562 if (printMessages) { print("leftover vm data '$vmData'"); } | |
| 563 } | |
| 564 | |
| 565 /** | |
| 566 * Skip past a JSON object value. | |
| 567 * The object value must start with '{' and continues to the | |
| 568 * matching '}'. No attempt is made to otherwise validate the contents | |
| 569 * as JSON. If it is invalid, a later [parseJson] will fail. | |
| 570 */ | |
| 571 int jsonObjectLength(String string) { | |
| 572 int skipWhitespace(int index) { | |
| 573 while (index < string.length) { | |
| 574 String char = string[index]; | |
| 575 if (char != " " && char != "\n" && char != "\r" && char != "\t") break; | |
| 576 index++; | |
| 577 } | |
| 578 return index; | |
| 579 } | |
| 580 int skipString(int index) { | |
| 581 assert(string[index - 1] == '"'); | |
| 582 while (index < string.length) { | |
| 583 String char = string[index]; | |
| 584 if (char == '"') return index + 1; | |
| 585 if (char == r'\') index++; | |
| 586 if (index == string.length) return index; | |
| 587 index++; | |
| 588 } | |
| 589 return index; | |
| 590 } | |
| 591 int index = 0; | |
| 592 index = skipWhitespace(index); | |
| 593 // Bail out if the first non-whitespace character isn't '{'. | |
| 594 if (index == string.length || string[index] != '{') return 0; | |
| 595 int nesting = 0; | |
| 596 while (index < string.length) { | |
| 597 String char = string[index++]; | |
| 598 if (char == '{') { | |
| 599 nesting++; | |
| 600 } else if (char == '}') { | |
| 601 nesting--; | |
| 602 if (nesting == 0) return index; | |
| 603 } else if (char == '"') { | |
| 604 // Strings can contain braces. Skip their content. | |
| 605 index = skipString(index); | |
| 606 } | |
| 607 } | |
| 608 return 0; | |
| 609 } | |
| 610 | |
| 611 | |
| 612 void debuggerMain() { | |
| 613 outstandingCommands = new Map<int, Completer>(); | |
| 614 Socket.connect("127.0.0.1", 5858).then((s) { | |
| 615 vmSock = s; | |
| 616 vmSock.setOption(SocketOption.TCP_NODELAY, true); | |
| 617 var stringStream = vmSock.transform(UTF8.decoder); | |
| 618 vmSubscription = stringStream.listen( | |
| 619 (String data) { | |
| 620 processVmData(data); | |
| 621 }, | |
| 622 onDone: () { | |
| 623 print("VM debugger connection closed"); | |
| 624 quitShell(); | |
| 625 }, | |
| 626 onError: (err) { | |
| 627 print("Error in debug connection: $err"); | |
| 628 // TODO(floitsch): do we want to print the stack trace? | |
| 629 quitShell(); | |
| 630 }); | |
| 631 stdinSubscription = stdin.transform(UTF8.decoder) | |
| 632 .transform(new LineSplitter()) | |
| 633 .listen((String line) => processCommand(line)); | |
| 634 }); | |
| 635 } | |
| 636 | |
| 637 void main(List<String> arguments) { | |
| 638 if (arguments.length > 0) { | |
| 639 arguments = <String>['--debug', '--verbose_debug']..addAll(arguments); | |
| 640 Process.start(Platform.executable, arguments).then((Process process) { | |
| 641 process.stdin.close(); | |
| 642 process.exitCode.then((int exitCode) { | |
| 643 print('${arguments.join(" ")} exited with $exitCode'); | |
| 644 }); | |
| 645 debuggerMain(); | |
| 646 }); | |
| 647 } else { | |
| 648 debuggerMain(); | |
| 649 } | |
| 650 } | |
| OLD | NEW |