Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016, the Dartino 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.md file. | |
| 4 | |
| 5 /// An implementation of the vm service-protocol in terms of a DartinoVmContext. | |
| 6 /// Processes are mapped to isolates. | |
| 7 // TODO(sigurdm): Handle processes better. | |
| 8 // TODO(sigurdm): Find a way to represent fibers. | |
| 9 // TODO(sigurdm): Represent remote values. | |
| 10 | |
| 11 import "dart:async" show Future; | |
| 12 | |
| 13 import 'dart:convert' show JSON; | |
| 14 | |
| 15 import 'dart:io' show File, HttpServer, WebSocket, WebSocketTransformer; | |
| 16 | |
| 17 import 'hub/session_manager.dart' show SessionState; | |
| 18 | |
| 19 import 'guess_configuration.dart' show dartinoVersion; | |
| 20 | |
| 21 import '../debug_state.dart' | |
| 22 show BackTrace, BackTraceFrame, Breakpoint, RemoteObject; | |
| 23 | |
| 24 import '../vm_context.dart' show DartinoVmContext, DebugListener; | |
| 25 | |
| 26 import '../dartino_system.dart' | |
| 27 show DartinoFunction, DartinoFunctionKind, DartinoSystem; | |
| 28 | |
| 29 import 'debug_info.dart' show DebugInfo, ScopeInfo, SourceLocation; | |
| 30 | |
| 31 import 'dartino_compiler_implementation.dart' | |
| 32 show DartinoCompilerImplementation; | |
| 33 | |
| 34 import 'codegen_visitor.dart' show LocalValue; | |
| 35 | |
| 36 import '../program_info.dart' show Configuration; | |
| 37 | |
| 38 import 'package:compiler/src/scanner/scanner.dart' show Scanner; | |
| 39 import 'package:compiler/src/tokens/token.dart' show Token; | |
| 40 import 'package:compiler/src/io/source_file.dart' show SourceFile; | |
| 41 import 'package:compiler/src/elements/visitor.dart' show BaseElementVisitor; | |
| 42 import 'package:compiler/src/elements/elements.dart' | |
| 43 show | |
| 44 CompilationUnitElement, | |
| 45 Element, | |
| 46 FunctionElement, | |
| 47 LibraryElement, | |
| 48 MemberElement, | |
| 49 ScopeContainerElement; | |
| 50 | |
| 51 // import 'package:stack_trace/stack_trace.dart'; | |
| 52 | |
| 53 const bool logging = const bool.fromEnvironment("dartino-log-debug-server"); | |
| 54 | |
| 55 //TODO(danrubel): Verify const map values | |
| 56 const Map<String, dynamic> dartCoreLibRefDesc = const <String, dynamic>{ | |
| 57 "type": "@Library", | |
| 58 "id": "libraries/dart:core", | |
| 59 "fixedId": true, | |
| 60 "name": "dart:core", | |
| 61 "uri": "dart:core", | |
| 62 }; | |
| 63 | |
| 64 //TODO(danrubel): Verify correct id | |
| 65 const String nullId = "objects/Null"; | |
| 66 | |
| 67 //TODO(danrubel): Verify const map values | |
| 68 const Map<String, dynamic> nullClassRefDesc = const <String, dynamic>{ | |
| 69 "type": "@Class", | |
| 70 "id": "classes/NullClass", | |
| 71 "name": "NullClass", | |
| 72 }; | |
| 73 | |
| 74 //TODO(danrubel): Verify const map values | |
| 75 const Map<String, dynamic> nullRefDesc = const <String, dynamic>{ | |
| 76 "type": "@Null", | |
| 77 "id": nullId, | |
| 78 "kind": "Null", | |
| 79 "class": nullClassRefDesc, | |
| 80 "valueAsString": "null", | |
| 81 }; | |
| 82 | |
| 83 //TODO(danrubel): Verify const map values | |
| 84 const Map<String, dynamic> nullDesc = const <String, dynamic>{ | |
| 85 "type": "Null", | |
| 86 "id": nullId, | |
| 87 "kind": "Null", | |
| 88 "class": nullClassRefDesc, | |
| 89 "valueAsString": "null", | |
| 90 }; | |
| 91 | |
| 92 class DebugServer { | |
| 93 Future<int> serveSingleShot(SessionState state, | |
| 94 {int port: 0, Uri snapshotLocation}) async { | |
| 95 HttpServer server = await HttpServer.bind("127.0.0.1", port); | |
| 96 // The Atom Dartino plugin waits for "localhost:<port-number>" | |
| 97 // to determine the observatory port for debugging. | |
| 98 print("localhost:${server.port}"); | |
| 99 WebSocket socket = await server.transform(new WebSocketTransformer()).first; | |
| 100 // await Chain.capture(() async { | |
|
danrubel
2016/06/09 14:35:46
Remove commented code? What's the convention regar
sigurdm
2016/06/09 14:38:23
Usually we remove it - but I thought it was nice t
| |
| 101 await new DebugConnection(state, socket, snapshotLocation: snapshotLocation) | |
| 102 .serve(); | |
| 103 // }, onError: (final error, final Chain chain) { | |
| 104 // print("$error ${chain.terse}"); | |
| 105 // }); | |
| 106 await state.vmContext.terminate(); | |
| 107 await server.close(); | |
| 108 return 0; | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 class DebugConnection implements DebugListener { | |
| 113 final Map<String, bool> streams = new Map<String, bool>(); | |
| 114 | |
| 115 DartinoVmContext get vmContext => state.vmContext; | |
| 116 final SessionState state; | |
| 117 final WebSocket socket; | |
| 118 final Uri snapshotLocation; | |
| 119 | |
| 120 final Map<Uri, List<int>> tokenTables = new Map<Uri, List<int>>(); | |
| 121 | |
| 122 Map lastPauseEvent; | |
| 123 | |
| 124 DebugConnection(this.state, this.socket, {this.snapshotLocation}); | |
| 125 | |
| 126 List<int> makeTokenTable(CompilationUnitElement compilationUnit) { | |
| 127 // TODO(sigurdm): Are these cached somewhere? | |
| 128 Token t = new Scanner(compilationUnit.script.file).tokenize(); | |
| 129 List<int> result = new List<int>(); | |
| 130 while (t.next != t) { | |
| 131 result.add(t.charOffset); | |
| 132 t = t.next; | |
| 133 } | |
| 134 return result; | |
| 135 } | |
| 136 | |
| 137 void send(Map message) { | |
| 138 if (logging) { | |
| 139 print("Sending ${JSON.encode(message)}"); | |
| 140 } | |
| 141 socket.add(JSON.encode(message)); | |
| 142 } | |
| 143 | |
| 144 void setStream(String streamId, bool value) { | |
| 145 streams[streamId] = value; | |
| 146 } | |
| 147 | |
| 148 Map<String, CompilationUnitElement> scripts = | |
| 149 new Map<String, CompilationUnitElement>(); | |
| 150 | |
| 151 static int binarySearch(List<int> table, int needle) { | |
| 152 int l = 0; | |
| 153 int r = table.length - 1; | |
| 154 while (true) { | |
| 155 if (l > r) return -1; | |
| 156 int m = (l + r) ~/ 2; | |
| 157 int a = table[m]; | |
| 158 if (a < needle) { | |
| 159 l = m + 1; | |
| 160 } else if (a > needle) { | |
| 161 r = m - 1; | |
| 162 } else { | |
| 163 return m; | |
| 164 } | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 Map frameDesc(BackTraceFrame frame, int index) { | |
| 169 List<Map> vars = new List<Map>(); | |
| 170 for (ScopeInfo current = frame.scopeInfo(); | |
| 171 current != ScopeInfo.sentinel; | |
| 172 current = current.previous) { | |
| 173 vars.add({ | |
| 174 "type": "BoundVariable", | |
| 175 "name": current.name, | |
| 176 "value": valueDesc(current.lookup(current.name)) | |
| 177 }); | |
| 178 } | |
| 179 return { | |
| 180 "type": "Frame", | |
| 181 "index": index, | |
| 182 "function": functionRef(frame.function), | |
| 183 "code": { | |
| 184 "id": "code-id", //TODO(danrubel): what is the unique id here? | |
| 185 "type": "@Code", | |
| 186 "name": "code-name", // TODO(sigurdm): How to create a name here? | |
| 187 "kind": "Dart", | |
| 188 }, | |
| 189 "location": locationDesc(frame.sourceLocation(), false), | |
| 190 "vars": vars, | |
| 191 }; | |
| 192 } | |
| 193 | |
| 194 Map locationDesc(SourceLocation location, bool includeEndToken) { | |
| 195 // TODO(sigurdm): Investigate when this happens. | |
| 196 if (location == null || location.file == null) | |
| 197 return { | |
| 198 "type": "SourceLocation", | |
| 199 "script": scriptRef(Uri.parse('file:///unknown')), | |
| 200 "tokenPos": 0, | |
| 201 }; | |
| 202 Uri uri = location.file.uri; | |
| 203 // TODO(sigurdm): Avoid this. The uri should be the same as we get from | |
| 204 // `CompilationUnit.script.file.uri`. | |
| 205 uri = new File(new File.fromUri(uri).resolveSymbolicLinksSync()).uri; | |
| 206 | |
| 207 int tokenPos = | |
| 208 binarySearch(tokenTables[location.file.uri], location.span.begin); | |
| 209 | |
| 210 Map result = { | |
| 211 "type": "SourceLocation", | |
| 212 "script": scriptRef(location.file.uri), | |
| 213 "tokenPos": tokenPos, | |
| 214 }; | |
| 215 if (includeEndToken) { | |
| 216 int endTokenPos = | |
| 217 binarySearch(tokenTables[location.file.uri], location.span.end); | |
| 218 result["endTokenPos"] = endTokenPos; | |
| 219 } | |
| 220 | |
| 221 return result; | |
| 222 } | |
| 223 | |
| 224 Map isolateRef(int isolateId) { | |
| 225 return { | |
| 226 "name": "Isolate $isolateId", | |
| 227 "id": "isolates/$isolateId", | |
| 228 "fixedId": true, | |
| 229 "type": "@Isolate", | |
| 230 "number": "$isolateId" | |
| 231 }; | |
| 232 } | |
| 233 | |
| 234 Map libraryRef(LibraryElement library) { | |
| 235 return { | |
| 236 "type": "@Library", | |
| 237 "id": "libraries/${library.canonicalUri}", | |
| 238 "fixedId": true, | |
| 239 "name": "${library.canonicalUri}", | |
| 240 "uri": "${library.canonicalUri}", | |
| 241 }; | |
| 242 } | |
| 243 | |
| 244 Map libraryDesc(LibraryElement library) { | |
| 245 return { | |
| 246 "type": "Library", | |
| 247 "id": "libraries/${library.canonicalUri}", | |
| 248 "fixedId": true, | |
| 249 "name": "${library.canonicalUri}", | |
| 250 "uri": "${library.canonicalUri}", | |
| 251 // TODO(danrubel): determine proper values for the next entry | |
| 252 "debuggable": true, | |
| 253 // TODO(sigurdm): The following fields are not used by the atom-debugger. | |
| 254 // We might want to include them to get full compatibility with the | |
| 255 // observatory. | |
| 256 "dependencies": [], | |
| 257 "classes": [], | |
| 258 "variables": [], | |
| 259 "functions": [], | |
| 260 "scripts": library.compilationUnits | |
| 261 .map((CompilationUnitElement compilationUnit) => | |
| 262 scriptRef(compilationUnit.script.resourceUri)) | |
| 263 .toList() | |
| 264 }; | |
| 265 } | |
| 266 | |
| 267 Map breakpointDesc(Breakpoint breakpoint) { | |
| 268 DebugInfo debugInfo = | |
| 269 state.vmContext.debugState.getDebugInfo(breakpoint.function); | |
| 270 SourceLocation location = debugInfo.locationFor(breakpoint.bytecodeIndex); | |
| 271 return { | |
| 272 "type": "Breakpoint", | |
| 273 "breakpointNumber": breakpoint.id, | |
| 274 "id": "breakpoints/${breakpoint.id}", | |
| 275 "fixedId": true, | |
| 276 "resolved": true, | |
| 277 "location": locationDesc(location, false), | |
| 278 }; | |
| 279 } | |
| 280 | |
| 281 Map scriptRef(Uri uri) { | |
| 282 return { | |
| 283 "type": "@Script", | |
| 284 "id": "scripts/${uri}", | |
| 285 "fixedId": true, | |
| 286 "uri": "${uri}", | |
| 287 "_kind": "script" | |
| 288 }; | |
| 289 } | |
| 290 | |
| 291 Map scriptDesc(CompilationUnitElement compilationUnit) { | |
| 292 String text = compilationUnit.script.text; | |
| 293 SourceFile file = compilationUnit.script.file; | |
| 294 List<List<int>> tokenPosTable = []; | |
| 295 List<int> tokenTable = tokenTables[compilationUnit.script.resourceUri]; | |
| 296 int currentLine = -1; | |
| 297 for (int i = 0; i < tokenTable.length; i++) { | |
| 298 int line = file.getLine(tokenTable[i]); | |
| 299 int column = file.getColumn(line, tokenTable[i]); | |
| 300 if (line != currentLine) { | |
| 301 currentLine = line; | |
| 302 // The debugger lines starts from 1. | |
| 303 tokenPosTable.add([currentLine + 1]); | |
| 304 } | |
| 305 tokenPosTable.last.add(i); | |
| 306 tokenPosTable.last.add(column); | |
| 307 } | |
| 308 return { | |
| 309 "type": "Script", | |
| 310 "id": "scripts/${compilationUnit.script.resourceUri}", | |
| 311 "fixedId": true, | |
| 312 "uri": "${compilationUnit.script.resourceUri}", | |
| 313 "library": libraryRef(compilationUnit.library), | |
| 314 "source": text, | |
| 315 "tokenPosTable": tokenPosTable, | |
| 316 "lineOffset": 0, // TODO(sigurdm): What is this? | |
| 317 "columnOffset": 0, // TODO(sigurdm): What is this? | |
| 318 }; | |
| 319 } | |
| 320 | |
| 321 String functionKind(DartinoFunctionKind kind) { | |
| 322 return { | |
| 323 DartinoFunctionKind.NORMAL: "RegularFunction", | |
| 324 DartinoFunctionKind.LAZY_FIELD_INITIALIZER: "Stub", | |
| 325 DartinoFunctionKind.INITIALIZER_LIST: "RegularFunction", | |
| 326 DartinoFunctionKind.PARAMETER_STUB: "Stub", | |
| 327 DartinoFunctionKind.ACCESSOR: "GetterFunction" | |
| 328 }[kind]; | |
| 329 } | |
| 330 | |
| 331 Map functionRef(DartinoFunction function) { | |
| 332 String name = function.name; | |
| 333 //TODO(danrubel): Investigate why this happens. | |
| 334 if (name == null || name.isEmpty) name = 'unknown'; | |
| 335 return { | |
| 336 "type": "@Function", | |
| 337 "id": "functions/${function.functionId}", | |
| 338 "fixedId": true, | |
| 339 "name": "${name}", | |
| 340 // TODO(sigurdm): All kinds of owner. | |
| 341 "owner": libraryRef(function?.element?.library ?? state.compiler.mainApp), | |
| 342 "static": function.element?.isStatic ?? false, | |
| 343 "const": function.element?.isConst ?? false, | |
| 344 "_kind": functionKind(function.kind), | |
| 345 }; | |
| 346 } | |
| 347 | |
| 348 Map functionDesc(DartinoFunction function) { | |
| 349 FunctionElement element = function.element; | |
| 350 return { | |
| 351 "type": "Function", | |
| 352 "id": "functions/${function.functionId}", | |
| 353 // TODO(sigurdm): All kinds of owner. | |
| 354 "owner": libraryRef(element.library), | |
| 355 "static": element.isStatic, | |
| 356 "const": element.isConst, | |
| 357 "_kind": functionKind(function.kind), | |
| 358 }; | |
| 359 } | |
| 360 | |
| 361 Map valueDesc(LocalValue lookup) { | |
| 362 //TODO(danrubel): Translate local value into description? | |
| 363 // Need to return an @InstanceRef or Sentinel | |
| 364 return nullRefDesc; | |
| 365 } | |
| 366 | |
| 367 initialize(DartinoCompilerImplementation compiler) { | |
| 368 for (LibraryElement library in compiler.libraryLoader.libraries) { | |
| 369 cacheScripts(LibraryElement library) { | |
| 370 for (CompilationUnitElement compilationUnit | |
| 371 in library.compilationUnits) { | |
| 372 Uri uri = compilationUnit.script.file.uri; | |
| 373 scripts["scripts/$uri"] = compilationUnit; | |
| 374 tokenTables[uri] = makeTokenTable(compilationUnit); | |
| 375 } | |
| 376 } | |
| 377 cacheScripts(library); | |
| 378 if (library.isPatched) { | |
| 379 cacheScripts(library.patch); | |
| 380 } | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 serve() async { | |
| 385 vmContext.listeners.add(this); | |
| 386 | |
| 387 await vmContext.initialize(state, snapshotLocation: snapshotLocation); | |
| 388 | |
| 389 initialize(state.compiler.compiler); | |
| 390 | |
| 391 await for (var message in socket) { | |
| 392 if (message is! String) throw "Expected String"; | |
| 393 var decodedMessage = JSON.decode(message); | |
| 394 if (logging) { | |
| 395 print("Received $decodedMessage"); | |
| 396 } | |
| 397 | |
| 398 if (decodedMessage is! Map) throw "Expected Map"; | |
| 399 var id = decodedMessage['id']; | |
| 400 void sendResult(Map result) { | |
| 401 Map message = {"jsonrpc": "2.0", "result": result, "id": id}; | |
| 402 send(message); | |
| 403 } | |
| 404 void sendError(Map error) { | |
| 405 Map message = {"jsonrpc": "2.0", "error": error, "id": id}; | |
| 406 send(message); | |
| 407 } | |
| 408 switch (decodedMessage["method"]) { | |
| 409 case "streamListen": | |
| 410 setStream(decodedMessage["params"]["streamId"], true); | |
| 411 sendResult({"type": "Success"}); | |
| 412 break; | |
| 413 case "streamCancel": | |
| 414 setStream(decodedMessage["streamId"], false); | |
| 415 sendResult({"type": "Success"}); | |
| 416 break; | |
| 417 case "getVersion": | |
| 418 sendResult({"type": "Version", "major": 3, "minor": 4}); | |
| 419 break; | |
| 420 case "getVM": | |
| 421 List<int> isolates = await state.vmContext.processes(); | |
| 422 sendResult({ | |
| 423 "type": "VM", | |
| 424 "name": "dartino-vm", | |
| 425 "architectureBits": { | |
| 426 Configuration.Offset64BitsDouble: 64, | |
| 427 Configuration.Offset64BitsFloat: 64, | |
| 428 Configuration.Offset32BitsDouble: 32, | |
| 429 Configuration.Offset32BitsFloat: 32, | |
| 430 }[vmContext.configuration], | |
| 431 // TODO(sigurdm): Can we give a better description? | |
| 432 "targetCPU": "${vmContext.configuration}", | |
| 433 // TODO(sigurdm): Can we give a better description? | |
| 434 "hostCPU": "${vmContext.configuration}", | |
| 435 "version": "$dartinoVersion", | |
| 436 // TODO(sigurdm): Can we say something meaningful? | |
| 437 "pid": 0, | |
| 438 // TODO(sigurdm): Implement a startTime for devices with a clock. | |
| 439 "startTime": 0, | |
| 440 "isolates": isolates.map(isolateRef).toList() | |
| 441 }); | |
| 442 break; | |
| 443 case "getIsolate": | |
| 444 String isolateIdString = decodedMessage["params"]["isolateId"]; | |
| 445 int isolateId = int.parse( | |
| 446 isolateIdString.substring(isolateIdString.indexOf("/") + 1)); | |
| 447 | |
| 448 sendResult({ | |
| 449 "type": "Isolate", | |
| 450 "runnable": true, | |
| 451 "livePorts": 0, | |
| 452 "startTime": 0, | |
| 453 "name": "Isolate $isolateId", | |
| 454 "number": "$isolateId", | |
| 455 // TODO(sigurdm): This seems to be required by the observatory. | |
| 456 "_originNumber": "$isolateId", | |
| 457 "breakpoints": | |
| 458 state.vmContext.breakpoints().map(breakpointDesc).toList(), | |
| 459 "rootLib": libraryRef(state.compiler.compiler.mainApp), | |
| 460 "id": "$isolateIdString", | |
| 461 "libraries": state.compiler.compiler.libraryLoader.libraries | |
| 462 .map(libraryRef) | |
| 463 .toList(), | |
| 464 "pauseEvent": lastPauseEvent, | |
| 465 // TODO(danrubel): determine proper values for these 2 entries | |
| 466 "pauseOnExit": false, | |
| 467 "exceptionPauseMode": "Unhandled", | |
| 468 // Needed by observatory. | |
| 469 "_debuggerSettings": {"_exceptions": "unhandled"}, | |
| 470 }); | |
| 471 break; | |
| 472 case "addBreakpoint": | |
| 473 String scriptId = decodedMessage["params"]["scriptId"]; | |
| 474 Uri uri = scripts[scriptId].script.resourceUri; | |
| 475 int line = decodedMessage["params"]["line"]; | |
| 476 int column = decodedMessage["params"]["column"] ?? 1; | |
| 477 // TODO(sigurdm): Use the isolateId. | |
| 478 Breakpoint breakpoint = | |
| 479 await state.vmContext.setFileBreakpoint(uri, line, column); | |
| 480 if (breakpoint != null) { | |
| 481 sendResult({ | |
| 482 "type": "Breakpoint", | |
| 483 "id": "breakpoints/${breakpoint.id}", | |
| 484 "breakpointNumber": breakpoint.id, | |
| 485 "resolved": true, | |
| 486 "location": locationDesc( | |
| 487 breakpoint.location(vmContext.debugState), false), | |
| 488 }); | |
| 489 } else { | |
| 490 sendError({"code": 102, "message": "Cannot add breakpoint"}); | |
| 491 } | |
| 492 break; | |
| 493 case "removeBreakpoint": | |
| 494 String breakpointId = decodedMessage["params"]["breakpointId"]; | |
| 495 int id = | |
| 496 int.parse(breakpointId.substring(breakpointId.indexOf("/") + 1)); | |
| 497 if (vmContext.isRunning || vmContext.isTerminated) { | |
| 498 sendError({"code": 106, "message": "Isolate must be paused"}); | |
| 499 break; | |
| 500 } | |
| 501 Breakpoint breakpoint = await vmContext.deleteBreakpoint(id); | |
| 502 if (breakpoint == null) { | |
| 503 // TODO(sigurdm): Is this the right message? | |
| 504 sendError({"code": 102, "message": "Cannot remove breakpoint"}); | |
| 505 } else { | |
| 506 sendResult({"type": "Success"}); | |
| 507 } | |
| 508 break; | |
| 509 case "getObject": | |
| 510 // TODO(sigurdm): should not be ignoring the isolate id. | |
| 511 String id = decodedMessage["params"]["objectId"]; | |
| 512 int slashIndex = id.indexOf('/'); | |
| 513 switch (id.substring(0, slashIndex)) { | |
| 514 case "libraries": | |
| 515 String uri = id.substring(slashIndex + 1); | |
| 516 sendResult(libraryDesc(state.compiler.compiler.libraryLoader | |
| 517 .lookupLibrary(Uri.parse(uri)))); | |
| 518 break; | |
| 519 case "scripts": | |
| 520 sendResult(scriptDesc(scripts[id])); | |
| 521 break; | |
| 522 case "functions": | |
| 523 sendResult(functionDesc(vmContext.dartinoSystem.functionsById[ | |
| 524 int.parse(id.substring(slashIndex + 1))])); | |
| 525 break; | |
| 526 case "objects": | |
| 527 if (id == nullId) { | |
| 528 sendResult(nullDesc); | |
| 529 } else { | |
| 530 //TODO(danrubel): Need to return an Instance description | |
| 531 // or Sentinel for the given @InstanceRef | |
| 532 throw 'Unknown object: $id'; | |
| 533 } | |
| 534 break; | |
| 535 default: | |
| 536 throw "Unsupported object type $id"; | |
| 537 } | |
| 538 break; | |
| 539 case "getStack": | |
| 540 String isolateIdString = decodedMessage["params"]["isolateId"]; | |
| 541 int isolateId = int.parse( | |
| 542 isolateIdString.substring(isolateIdString.indexOf("/") + 1)); | |
| 543 BackTrace backTrace = | |
| 544 await state.vmContext.backTrace(processId: isolateId); | |
| 545 List frames = []; | |
| 546 int index = 0; | |
| 547 for (BackTraceFrame frame in backTrace.frames) { | |
| 548 frames.add(frameDesc(frame, index)); | |
| 549 index++; | |
| 550 } | |
| 551 | |
| 552 sendResult({"type": "Stack", "frames": frames, "messages": [],}); | |
| 553 break; | |
| 554 case "getSourceReport": | |
| 555 String scriptId = decodedMessage["params"]["scriptId"]; | |
| 556 CompilationUnitElement compilationUnit = scripts[scriptId]; | |
| 557 Uri scriptUri = compilationUnit.script.file.uri; | |
| 558 List<int> tokenTable = tokenTables[scriptUri]; | |
| 559 | |
| 560 // We do not support coverage. | |
| 561 assert(decodedMessage["params"]["reports"] | |
| 562 .contains("PossibleBreakpoints")); | |
| 563 int tokenPos = decodedMessage["params"]["tokenPos"] ?? 0; | |
| 564 int endTokenPos = | |
| 565 decodedMessage["params"]["endTokenPos"] ?? tokenTable.length - 1; | |
| 566 int startPos = tokenTable[tokenPos]; | |
| 567 int endPos = tokenTable[endTokenPos]; | |
| 568 List<int> possibleBreakpoints = new List<int>(); | |
| 569 DartinoSystem system = state.compilationResults.last.system; | |
| 570 for (FunctionElement function | |
| 571 in FunctionsFinder.findNestedFunctions(compilationUnit)) { | |
| 572 DartinoFunction dartinoFunction = | |
| 573 system.functionsByElement[function]; | |
| 574 if (dartinoFunction == null) break; | |
| 575 DebugInfo info = state.compiler | |
| 576 .createDebugInfo(system.functionsByElement[function], system); | |
| 577 if (info == null) break; | |
| 578 for (SourceLocation location in info.locations) { | |
| 579 // TODO(sigurdm): Investigate these. | |
| 580 if (location == null || location.span == null) continue; | |
| 581 int position = location.span.begin; | |
| 582 if (!(position >= startPos && position <= endPos)) continue; | |
| 583 possibleBreakpoints.add(binarySearch(tokenTable, position)); | |
| 584 } | |
| 585 } | |
| 586 Map range = { | |
| 587 "scriptIndex": 0, | |
| 588 "compiled": true, | |
| 589 "startPos": tokenPos, | |
| 590 "endPos": endTokenPos, | |
| 591 "possibleBreakpoints": possibleBreakpoints, | |
| 592 "callSites": [], | |
| 593 }; | |
| 594 sendResult({ | |
| 595 "type": "SourceReport", | |
| 596 "ranges": [range], | |
| 597 "scripts": [scriptRef(scriptUri)], | |
| 598 }); | |
| 599 break; | |
| 600 case "resume": | |
| 601 // TODO(sigurdm): use isolateId. | |
| 602 String stepOption = decodedMessage["params"]["step"]; | |
| 603 switch (stepOption) { | |
| 604 case "Into": | |
| 605 // TODO(sigurdm): avoid needing await here. | |
| 606 await vmContext.step(); | |
| 607 sendResult({"type": "Success"}); | |
| 608 break; | |
| 609 case "Over": | |
| 610 // TODO(sigurdm): avoid needing await here. | |
| 611 await vmContext.stepOver(); | |
| 612 sendResult({"type": "Success"}); | |
| 613 break; | |
| 614 case "Out": | |
| 615 // TODO(sigurdm): avoid needing await here. | |
| 616 await vmContext.stepOut(); | |
| 617 sendResult({"type": "Success"}); | |
| 618 break; | |
| 619 case "OverAsyncSuspension": | |
| 620 sendError({ | |
| 621 "code": 100, | |
| 622 "message": "Feature is disabled", | |
| 623 "data": { | |
| 624 "details": | |
| 625 "Stepping over async suspensions is not implemented", | |
| 626 } | |
| 627 }); | |
| 628 break; | |
| 629 default: | |
| 630 assert(stepOption == null); | |
| 631 if (vmContext.isScheduled) { | |
| 632 // TODO(sigurdm): Ensure other commands are not issued during | |
| 633 // this. | |
| 634 vmContext.cont(); | |
| 635 } else { | |
| 636 vmContext.startRunning(); | |
| 637 } | |
| 638 sendResult({"type": "Success"}); | |
| 639 } | |
| 640 break; | |
| 641 case "setExceptionPauseMode": | |
| 642 // TODO(sigurdm): implement exception-pause-mode. | |
| 643 sendResult({"type": "Success"}); | |
| 644 break; | |
| 645 case "pause": | |
| 646 await vmContext.interrupt(); | |
| 647 sendResult({"type": "Success"}); | |
| 648 break; | |
| 649 case "addBreakpointWithScriptUri": | |
| 650 // TODO(sigurdm): Use the isolateId. | |
| 651 String scriptUri = decodedMessage["params"]["scriptUri"]; | |
| 652 int line = decodedMessage["params"]["line"] ?? 1; | |
| 653 int column = decodedMessage["params"]["column"] ?? 1; | |
| 654 if (vmContext.isRunning) { | |
| 655 sendError({"code": 102, "message": "Cannot add breakpoint"}); | |
| 656 break; | |
| 657 } | |
| 658 Breakpoint breakpoint = await vmContext.setFileBreakpoint( | |
| 659 Uri.parse(scriptUri), line, column); | |
| 660 if (breakpoint == null) { | |
| 661 sendError({"code": 102, "message": "Cannot add breakpoint"}); | |
| 662 } else { | |
| 663 sendResult({ | |
| 664 "type": "Breakpoint", | |
| 665 "id": "breakpoints/${breakpoint.id}", | |
| 666 "breakpointNumber": breakpoint.id, | |
| 667 "resolved": true, | |
| 668 "location": locationDesc( | |
| 669 breakpoint.location(vmContext.debugState), false), | |
| 670 }); | |
| 671 } | |
| 672 break; | |
| 673 default: | |
| 674 sendError({ | |
| 675 "code": 100, | |
| 676 "message": "Feature is disabled", | |
| 677 "data": { | |
| 678 "details": | |
| 679 "Request type ${decodedMessage["method"]} not implemented", | |
| 680 } | |
| 681 }); | |
| 682 if (logging) { | |
| 683 print("Unhandled request type: ${decodedMessage["method"]}"); | |
| 684 } | |
| 685 } | |
| 686 } | |
| 687 } | |
| 688 | |
| 689 void streamNotify(String streamId, Map event) { | |
| 690 if (streams[streamId] ?? false) { | |
| 691 send({ | |
| 692 "method": "streamNotify", | |
| 693 "params": {"streamId": streamId, "event": event,}, | |
| 694 "jsonrpc": "2.0", | |
| 695 }); | |
| 696 } | |
| 697 } | |
| 698 | |
| 699 @override | |
| 700 breakpointAdded(int processId, Breakpoint breakpoint) { | |
| 701 streamNotify("Debug", { | |
| 702 "type": "Event", | |
| 703 "kind": "BreakpointAdded", | |
| 704 "isolate": isolateRef(processId), | |
| 705 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 706 "breakpoint": breakpointDesc(breakpoint), | |
| 707 }); | |
| 708 } | |
| 709 | |
| 710 @override | |
| 711 breakpointRemoved(int processId, Breakpoint breakpoint) { | |
| 712 streamNotify("Debug", { | |
| 713 "type": "Event", | |
| 714 "kind": "BreakpointRemoved", | |
| 715 "isolate": isolateRef(processId), | |
| 716 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 717 "breakpoint": breakpointDesc(breakpoint), | |
| 718 }); | |
| 719 } | |
| 720 | |
| 721 @override | |
| 722 gc(int processId) { | |
| 723 // TODO(sigurdm): Implement gc notification. | |
| 724 } | |
| 725 | |
| 726 @override | |
| 727 lostConnection() { | |
| 728 socket.close(); | |
| 729 } | |
| 730 | |
| 731 @override | |
| 732 pauseBreakpoint( | |
| 733 int processId, BackTraceFrame topFrame, Breakpoint breakpoint) { | |
| 734 //TODO(danrubel): are there any other breakpoints | |
| 735 // at which we are currently paused for a PauseBreakpoint event? | |
| 736 List<Breakpoint> pauseBreakpoints = <Breakpoint>[]; | |
| 737 pauseBreakpoints.add(breakpoint); | |
| 738 Map event = { | |
| 739 "type": "Event", | |
| 740 "kind": "PauseBreakpoint", | |
| 741 "isolate": isolateRef(processId), | |
| 742 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 743 "topFrame": frameDesc(topFrame, 0), | |
| 744 "atAsyncSuspension": false, | |
| 745 "breakpoint": breakpointDesc(breakpoint), | |
| 746 "pauseBreakpoints": | |
| 747 new List.from(pauseBreakpoints.map((bp) => breakpointDesc(bp))), | |
| 748 }; | |
| 749 lastPauseEvent = event; | |
| 750 streamNotify("Debug", event); | |
| 751 } | |
| 752 | |
| 753 @override | |
| 754 pauseException(int processId, BackTraceFrame topFrame, RemoteObject thrown) { | |
| 755 Map event = { | |
| 756 "type": "Event", | |
| 757 "kind": "PauseException", | |
| 758 "isolate": isolateRef(processId), | |
| 759 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 760 "topFrame": frameDesc(topFrame, 0), | |
| 761 "atAsyncSuspension": false, | |
| 762 // TODO(sigurdm): pass thrown as an instance. | |
| 763 }; | |
| 764 streamNotify("Debug", event); | |
| 765 } | |
| 766 | |
| 767 @override | |
| 768 pauseExit(int processId, BackTraceFrame topFrame) { | |
| 769 // TODO(sigurdm): implement pauseExit | |
| 770 } | |
| 771 | |
| 772 @override | |
| 773 pauseInterrupted(int processId, BackTraceFrame topFrame) { | |
| 774 Map event = { | |
| 775 "type": "Event", | |
| 776 "kind": "PauseInterrupted", | |
| 777 "isolate": isolateRef(processId), | |
| 778 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 779 "topFrame": frameDesc(topFrame, 0), | |
| 780 "atAsyncSuspension": false, | |
| 781 }; | |
| 782 lastPauseEvent = event; | |
| 783 streamNotify("Debug", event); | |
| 784 } | |
| 785 | |
| 786 @override | |
| 787 pauseStart(int processId) { | |
| 788 Map event = { | |
| 789 "type": "Event", | |
| 790 "kind": "PauseStart", | |
| 791 "isolate": isolateRef(processId), | |
| 792 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 793 }; | |
| 794 lastPauseEvent = event; | |
| 795 streamNotify("Debug", event); | |
| 796 } | |
| 797 | |
| 798 @override | |
| 799 processExit(int processId) { | |
| 800 streamNotify("Isolate", { | |
| 801 "type": "Event", | |
| 802 "kind": "IsolateExit", | |
| 803 "isolate": isolateRef(processId), | |
| 804 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 805 }); | |
| 806 socket.close(); | |
| 807 } | |
| 808 | |
| 809 @override | |
| 810 processRunnable(int processId) { | |
| 811 Map event = { | |
| 812 "type": "Event", | |
| 813 "kind": "IsolateRunnable", | |
| 814 "isolate": isolateRef(processId), | |
| 815 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 816 }; | |
| 817 streamNotify("Isolate", event); | |
| 818 } | |
| 819 | |
| 820 @override | |
| 821 processStart(int processId) { | |
| 822 streamNotify("Isolate", { | |
| 823 "type": "Event", | |
| 824 "kind": "IsolateStart", | |
| 825 "isolate": isolateRef(processId), | |
| 826 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 827 }); | |
| 828 } | |
| 829 | |
| 830 @override | |
| 831 resume(int processId) { | |
| 832 Map event = { | |
| 833 "type": "Event", | |
| 834 "kind": "Resume", | |
| 835 "isolate": isolateRef(processId), | |
| 836 "timestamp": new DateTime.now().millisecondsSinceEpoch, | |
| 837 }; | |
| 838 BackTraceFrame topFrame = vmContext.debugState.topFrame; | |
| 839 if (topFrame != null) { | |
| 840 event["topFrame"] = frameDesc(vmContext.debugState.topFrame, 0); | |
| 841 } | |
| 842 lastPauseEvent = event; | |
| 843 streamNotify("Debug", event); | |
| 844 } | |
| 845 | |
| 846 @override | |
| 847 writeStdErr(int processId, List<int> data) { | |
| 848 Map event = { | |
| 849 "type": "Event", | |
| 850 "kind": "WriteEvent", | |
| 851 "bytes": new String.fromCharCodes(data), | |
| 852 }; | |
| 853 streamNotify("Stderr", event); | |
| 854 } | |
| 855 | |
| 856 @override | |
| 857 writeStdOut(int processId, List<int> data) { | |
| 858 Map event = { | |
| 859 "type": "Event", | |
| 860 "kind": "WriteEvent", | |
| 861 "bytes": new String.fromCharCodes(data), | |
| 862 }; | |
| 863 streamNotify("Stdout", event); | |
| 864 } | |
| 865 | |
| 866 @override | |
| 867 terminated() {} | |
| 868 } | |
| 869 | |
| 870 class FunctionsFinder extends BaseElementVisitor { | |
| 871 final List<FunctionElement> result = new List<FunctionElement>(); | |
| 872 | |
| 873 FunctionsFinder(); | |
| 874 | |
| 875 static List<FunctionElement> findNestedFunctions( | |
| 876 CompilationUnitElement element) { | |
| 877 FunctionsFinder finder = new FunctionsFinder(); | |
| 878 finder.visit(element); | |
| 879 return finder.result; | |
| 880 } | |
| 881 | |
| 882 visit(Element e, [arg]) => e.accept(this, arg); | |
| 883 | |
| 884 visitElement(Element e, _) {} | |
| 885 | |
| 886 visitFunctionElement(FunctionElement element, _) { | |
| 887 result.add(element); | |
| 888 MemberElement memberContext = element.memberContext; | |
| 889 if (memberContext == element) { | |
| 890 memberContext.nestedClosures.forEach(visit); | |
| 891 } | |
| 892 } | |
| 893 | |
| 894 visitScopeContainerElement(ScopeContainerElement e, _) { | |
| 895 e.forEachLocalMember(visit); | |
| 896 } | |
| 897 | |
| 898 visitCompilationUnitElement(CompilationUnitElement e, _) { | |
| 899 e.forEachLocalMember(visit); | |
| 900 } | |
| 901 } | |
| OLD | NEW |