| 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 { | |
| 252 return "$text"; | |
| 253 } | |
| 254 } | |
| 255 | |
| 256 | |
| 257 printNamedObject(obj) { | |
| 258 var name = obj["name"]; | |
| 259 var value = obj["value"]; | |
| 260 print(" $name = ${remoteObject(value)}"); | |
| 261 } | |
| 262 | |
| 263 | |
| 264 handleGetObjPropsResponse(response) { | |
| 265 Map props = response["result"]; | |
| 266 int class_id = props["classId"]; | |
| 267 if (class_id == -1) { | |
| 268 print(" null"); | |
| 269 return; | |
| 270 } | |
| 271 List fields = props["fields"]; | |
| 272 print(" class id: $class_id"); | |
| 273 for (int i = 0; i < fields.length; i++) { | |
| 274 printNamedObject(fields[i]); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 handleGetListResponse(response) { | |
| 279 Map result = response["result"]; | |
| 280 if (result["elements"] != null) { | |
| 281 // List slice. | |
| 282 var index = result["index"]; | |
| 283 var length = result["length"]; | |
| 284 List elements = result["elements"]; | |
| 285 assert(length == elements.length); | |
| 286 for (int i = 0; i < length; i++) { | |
| 287 var kind = elements[i]["kind"]; | |
| 288 var text = elements[i]["text"]; | |
| 289 print(" ${index + i}: ($kind) $text"); | |
| 290 } | |
| 291 } else { | |
| 292 // One element, a remote object. | |
| 293 print(result); | |
| 294 print(" ${remoteObject(result)}"); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 | |
| 299 handleGetClassPropsResponse(response) { | |
| 300 Map props = response["result"]; | |
| 301 assert(props["name"] != null); | |
| 302 int libId = props["libraryId"]; | |
| 303 assert(libId != null); | |
| 304 print(" class ${props["name"]} (library id: $libId)"); | |
| 305 List fields = props["fields"]; | |
| 306 if (fields.length > 0) { | |
| 307 print(" static fields:"); | |
| 308 for (int i = 0; i < fields.length; i++) { | |
| 309 printNamedObject(fields[i]); | |
| 310 } | |
| 311 } | |
| 312 } | |
| 313 | |
| 314 | |
| 315 handleGetLibraryPropsResponse(response) { | |
| 316 Map props = response["result"]; | |
| 317 assert(props["url"] != null); | |
| 318 print(" library url: ${props["url"]}"); | |
| 319 assert(props["debuggingEnabled"] != null); | |
| 320 print(" debugging enabled: ${props["debuggingEnabled"]}"); | |
| 321 List imports = props["imports"]; | |
| 322 assert(imports != null); | |
| 323 if (imports.length > 0) { | |
| 324 print(" imports:"); | |
| 325 for (int i = 0; i < imports.length; i++) { | |
| 326 print(" id ${imports[i]["libraryId"]} prefix ${imports[i]["prefix"]}"); | |
| 327 } | |
| 328 } | |
| 329 List globals = props["globals"]; | |
| 330 assert(globals != null); | |
| 331 if (globals.length > 0) { | |
| 332 print(" global variables:"); | |
| 333 for (int i = 0; i < globals.length; i++) { | |
| 334 printNamedObject(globals[i]); | |
| 335 } | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 | |
| 340 handleSetLibraryPropsResponse(response) { | |
| 341 Map props = response["result"]; | |
| 342 assert(props["debuggingEnabled"] != null); | |
| 343 print(" debugging enabled: ${props["debuggingEnabled"]}"); | |
| 344 } | |
| 345 | |
| 346 | |
| 347 handleGetGlobalVarsResponse(response) { | |
| 348 List globals = response["result"]["globals"]; | |
| 349 for (int i = 0; i < globals.length; i++) { | |
| 350 printNamedObject(globals[i]); | |
| 351 } | |
| 352 } | |
| 353 | |
| 354 | |
| 355 handleGetSourceResponse(response) { | |
| 356 Map result = response["result"]; | |
| 357 String source = result["text"]; | |
| 358 print("Source text:\n$source\n--------"); | |
| 359 } | |
| 360 | |
| 361 | |
| 362 handleGetLineTableResponse(response) { | |
| 363 Map result = response["result"]; | |
| 364 var info = result["lines"]; | |
| 365 print("Line info table:\n$info"); | |
| 366 } | |
| 367 | |
| 368 | |
| 369 handleGetIsolatesResponse(response) { | |
| 370 Map result = response["result"]; | |
| 371 print("Isolates: ${result["isolateIds"]}"); | |
| 372 } | |
| 373 | |
| 374 | |
| 375 void handleGetLibraryResponse(response) { | |
| 376 Map result = response["result"]; | |
| 377 List libs = result["libraries"]; | |
| 378 print("Loaded libraries:"); | |
| 379 print(libs); | |
| 380 for (int i = 0; i < libs.length; i++) { | |
| 381 print(" ${libs[i]["id"]} ${libs[i]["url"]}"); | |
| 382 } | |
| 383 } | |
| 384 | |
| 385 | |
| 386 void handleGetScriptsResponse(response) { | |
| 387 Map result = response["result"]; | |
| 388 List urls = result["urls"]; | |
| 389 print("Loaded scripts:"); | |
| 390 for (int i = 0; i < urls.length; i++) { | |
| 391 print(" $i ${urls[i]}"); | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 | |
| 396 void handleEvalResponse(response) { | |
| 397 Map result = response["result"]; | |
| 398 print(remoteObject(result)); | |
| 399 } | |
| 400 | |
| 401 | |
| 402 void handleSetBpResponse(response) { | |
| 403 Map result = response["result"]; | |
| 404 var id = result["breakpointId"]; | |
| 405 assert(id != null); | |
| 406 print("Set BP $id"); | |
| 407 } | |
| 408 | |
| 409 | |
| 410 void handleGenericResponse(response) { | |
| 411 if (response["error"] != null) { | |
| 412 print("Error: ${response["error"]}"); | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 | |
| 417 void handleStackTraceResponse(response) { | |
| 418 Map result = response["result"]; | |
| 419 List callFrames = result["callFrames"]; | |
| 420 assert(callFrames != null); | |
| 421 printStackTrace(callFrames); | |
| 422 } | |
| 423 | |
| 424 | |
| 425 void printStackFrame(frame_num, Map frame) { | |
| 426 var fname = frame["functionName"]; | |
| 427 var libId = frame["location"]["libraryId"]; | |
| 428 var url = frame["location"]["url"]; | |
| 429 var toff = frame["location"]["tokenOffset"]; | |
| 430 print("$frame_num $fname (url: $url token: $toff lib: $libId)"); | |
| 431 List locals = frame["locals"]; | |
| 432 for (int i = 0; i < locals.length; i++) { | |
| 433 printNamedObject(locals[i]); | |
| 434 } | |
| 435 } | |
| 436 | |
| 437 | |
| 438 void printStackTrace(List frames) { | |
| 439 for (int i = 0; i < frames.length; i++) { | |
| 440 printStackFrame(i, frames[i]); | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 | |
| 445 void handlePausedEvent(msg) { | |
| 446 assert(msg["params"] != null); | |
| 447 var reason = msg["params"]["reason"]; | |
| 448 isolate_id = msg["params"]["isolateId"]; | |
| 449 assert(isolate_id != null); | |
| 450 pausedLocation = msg["params"]["location"]; | |
| 451 assert(pausedLocation != null); | |
| 452 if (reason == "breakpoint") { | |
| 453 print("Isolate $isolate_id paused on breakpoint"); | |
| 454 print("location: $pausedLocation"); | |
| 455 } else if (reason == "interrupted") { | |
| 456 print("Isolate $isolate_id paused due to an interrupt"); | |
| 457 } else { | |
| 458 assert(reason == "exception"); | |
| 459 var excObj = msg["params"]["exception"]; | |
| 460 print("Isolate $isolate_id paused on exception"); | |
| 461 print(remoteObject(excObj)); | |
| 462 } | |
| 463 } | |
| 464 | |
| 465 | |
| 466 void processVmMessage(String jsonString) { | |
| 467 var msg = JSON.decode(jsonString); | |
| 468 if (msg == null) { | |
| 469 return; | |
| 470 } | |
| 471 var event = msg["event"]; | |
| 472 if (event == "paused") { | |
| 473 handlePausedEvent(msg); | |
| 474 return; | |
| 475 } | |
| 476 if (event == "breakpointResolved") { | |
| 477 Map params = msg["params"]; | |
| 478 assert(params != null); | |
| 479 print("BP ${params["breakpointId"]} resolved and " | |
| 480 "set at line ${params["line"]}."); | |
| 481 return; | |
| 482 } | |
| 483 if (event == "isolate") { | |
| 484 Map params = msg["params"]; | |
| 485 assert(params != null); | |
| 486 print("Isolate ${params["id"]} has been ${params["reason"]}."); | |
| 487 return; | |
| 488 } | |
| 489 if (msg["id"] != null) { | |
| 490 var id = msg["id"]; | |
| 491 if (outstandingCommands.containsKey(id)) { | |
| 492 if (msg["error"] != null) { | |
| 493 print("VM says: ${msg["error"]}"); | |
| 494 } else { | |
| 495 var completer = outstandingCommands[id]; | |
| 496 completer.complete(msg); | |
| 497 } | |
| 498 outstandingCommands.remove(id); | |
| 499 } | |
| 500 } | |
| 501 } | |
| 502 | |
| 503 bool haveGarbageVmData() { | |
| 504 if (vmData == null || vmData.length == 0) return false; | |
| 505 var i = 0, char = " "; | |
| 506 while (i < vmData.length) { | |
| 507 char = vmData[i]; | |
| 508 if (char != " " && char != "\n" && char != "\r" && char != "\t") break; | |
| 509 i++; | |
| 510 } | |
| 511 if (i >= vmData.length) { | |
| 512 return false; | |
| 513 } else { | |
| 514 return char != "{"; | |
| 515 } | |
| 516 } | |
| 517 | |
| 518 | |
| 519 void processVmData(String data) { | |
| 520 if (vmData == null || vmData.length == 0) { | |
| 521 vmData = data; | |
| 522 } else { | |
| 523 vmData = vmData + data; | |
| 524 } | |
| 525 if (haveGarbageVmData()) { | |
| 526 print("Error: have garbage data from VM: '$vmData'"); | |
| 527 return; | |
| 528 } | |
| 529 int msg_len = jsonObjectLength(vmData); | |
| 530 if (printMessages && msg_len == 0) { | |
| 531 print("have partial or illegal json message" | |
| 532 " of ${vmData.length} chars:\n'$vmData'"); | |
| 533 return; | |
| 534 } | |
| 535 while (msg_len > 0 && msg_len <= vmData.length) { | |
| 536 if (msg_len == vmData.length) { | |
| 537 if (printMessages) { print("have one full message:\n$vmData"); } | |
| 538 processVmMessage(vmData); | |
| 539 vmData = null; | |
| 540 return; | |
| 541 } | |
| 542 if (printMessages) { print("at least one message: '$vmData'"); } | |
| 543 var msg = vmData.substring(0, msg_len); | |
| 544 if (printMessages) { print("first message: $msg"); } | |
| 545 vmData = vmData.substring(msg_len); | |
| 546 if (haveGarbageVmData()) { | |
| 547 print("Error: garbage data after previous message: '$vmData'"); | |
| 548 print("Previous message was: '$msg'"); | |
| 549 return; | |
| 550 } | |
| 551 processVmMessage(msg); | |
| 552 msg_len = jsonObjectLength(vmData); | |
| 553 } | |
| 554 if (printMessages) { print("leftover vm data '$vmData'"); } | |
| 555 } | |
| 556 | |
| 557 /** | |
| 558 * Skip past a JSON object value. | |
| 559 * The object value must start with '{' and continues to the | |
| 560 * matching '}'. No attempt is made to otherwise validate the contents | |
| 561 * as JSON. If it is invalid, a later [parseJson] will fail. | |
| 562 */ | |
| 563 int jsonObjectLength(String string) { | |
| 564 int skipWhitespace(int index) { | |
| 565 while (index < string.length) { | |
| 566 String char = string[index]; | |
| 567 if (char != " " && char != "\n" && char != "\r" && char != "\t") break; | |
| 568 index++; | |
| 569 } | |
| 570 return index; | |
| 571 } | |
| 572 int skipString(int index) { | |
| 573 assert(string[index - 1] == '"'); | |
| 574 while (index < string.length) { | |
| 575 String char = string[index]; | |
| 576 if (char == '"') return index + 1; | |
| 577 if (char == r'\') index++; | |
| 578 if (index == string.length) return index; | |
| 579 index++; | |
| 580 } | |
| 581 return index; | |
| 582 } | |
| 583 int index = 0; | |
| 584 index = skipWhitespace(index); | |
| 585 // Bail out if the first non-whitespace character isn't '{'. | |
| 586 if (index == string.length || string[index] != '{') return 0; | |
| 587 int nesting = 0; | |
| 588 while (index < string.length) { | |
| 589 String char = string[index++]; | |
| 590 if (char == '{') { | |
| 591 nesting++; | |
| 592 } else if (char == '}') { | |
| 593 nesting--; | |
| 594 if (nesting == 0) return index; | |
| 595 } else if (char == '"') { | |
| 596 // Strings can contain braces. Skip their content. | |
| 597 index = skipString(index); | |
| 598 } | |
| 599 } | |
| 600 return 0; | |
| 601 } | |
| 602 | |
| 603 | |
| 604 void debuggerMain() { | |
| 605 outstandingCommands = new Map<int, Completer>(); | |
| 606 Socket.connect("127.0.0.1", 5858).then((s) { | |
| 607 vmSock = s; | |
| 608 vmSock.setOption(SocketOption.TCP_NODELAY, true); | |
| 609 var stringStream = vmSock.transform(UTF8.decoder); | |
| 610 vmSubscription = stringStream.listen( | |
| 611 (String data) { | |
| 612 processVmData(data); | |
| 613 }, | |
| 614 onDone: () { | |
| 615 print("VM debugger connection closed"); | |
| 616 quitShell(); | |
| 617 }, | |
| 618 onError: (err) { | |
| 619 print("Error in debug connection: $err"); | |
| 620 // TODO(floitsch): do we want to print the stack trace? | |
| 621 quitShell(); | |
| 622 }); | |
| 623 stdinSubscription = stdin.transform(UTF8.decoder) | |
| 624 .transform(new LineSplitter()) | |
| 625 .listen((String line) => processCommand(line)); | |
| 626 }); | |
| 627 } | |
| 628 | |
| 629 void main(List<String> arguments) { | |
| 630 if (arguments.length > 0) { | |
| 631 arguments = <String>['--debug', '--verbose_debug']..addAll(arguments); | |
| 632 Process.start(Platform.executable, arguments).then((Process process) { | |
| 633 process.stdin.close(); | |
| 634 process.exitCode.then((int exitCode) { | |
| 635 print('${arguments.join(" ")} exited with $exitCode'); | |
| 636 }); | |
| 637 debuggerMain(); | |
| 638 }); | |
| 639 } else { | |
| 640 debuggerMain(); | |
| 641 } | |
| 642 } | |
| OLD | NEW |