Index: pkg/dartino_compiler/lib/src/debug_service_protocol.dart |
diff --git a/pkg/dartino_compiler/lib/src/debug_service_protocol.dart b/pkg/dartino_compiler/lib/src/debug_service_protocol.dart |
index c21df67cc9e8f4c913bc81f548c29eddb31339ba..148bde39a3d6e507f63e5d2cde275a0b8994fa29 100644 |
--- a/pkg/dartino_compiler/lib/src/debug_service_protocol.dart |
+++ b/pkg/dartino_compiler/lib/src/debug_service_protocol.dart |
@@ -6,7 +6,6 @@ |
/// Processes are mapped to isolates. |
// TODO(sigurdm): Handle processes better. |
// TODO(sigurdm): Find a way to represent fibers. |
-// TODO(sigurdm): Represent remote values. |
// TODO(sigurdm): Use https://pub.dartlang.org/packages/json_rpc_2 for serving. |
import "dart:async" show Future; |
@@ -20,7 +19,15 @@ import 'hub/session_manager.dart' show SessionState; |
import 'guess_configuration.dart' show dartinoVersion; |
import '../debug_state.dart' |
- show BackTrace, BackTraceFrame, Breakpoint, RemoteObject; |
+ show |
+ BackTrace, |
+ BackTraceFrame, |
+ Breakpoint, |
+ RemoteArray, |
+ RemoteErrorObject, |
+ RemoteInstance, |
+ RemoteObject, |
+ RemoteValue; |
import '../vm_context.dart' show DartinoVmContext, DebugListener; |
@@ -31,10 +38,20 @@ import 'debug_info.dart' show DebugInfo, ScopeInfo, SourceLocation; |
import 'dartino_compiler_implementation.dart' |
show DartinoCompilerImplementation; |
- |
-import 'codegen_visitor.dart' show LocalValue; |
+import 'element_utils.dart'; |
import '../program_info.dart' show Configuration; |
+import '../vm_commands.dart' |
+ show |
+ Array, |
+ Boolean, |
+ ClassValue, |
+ DartValue, |
+ Double, |
+ Instance, |
+ Integer, |
+ NullValue, |
+ StringValue; |
import 'package:collection/collection.dart' show binarySearch; |
@@ -44,8 +61,10 @@ import 'package:compiler/src/io/source_file.dart' show SourceFile; |
import 'package:compiler/src/elements/visitor.dart' show BaseElementVisitor; |
import 'package:compiler/src/elements/elements.dart' |
show |
+ ClassElement, |
CompilationUnitElement, |
Element, |
+ FieldElement, |
FunctionElement, |
LibraryElement, |
MemberElement, |
@@ -53,43 +72,6 @@ import 'package:compiler/src/elements/elements.dart' |
const bool logging = const bool.fromEnvironment("dartino-log-debug-server"); |
-//TODO(danrubel): Verify const map values |
-const Map<String, dynamic> dartCoreLibRefDesc = const <String, dynamic>{ |
- "type": "@Library", |
- "id": "libraries/dart:core", |
- "fixedId": true, |
- "name": "dart:core", |
- "uri": "dart:core", |
-}; |
- |
-//TODO(danrubel): Verify correct id |
-const String nullId = "objects/Null"; |
- |
-//TODO(danrubel): Verify const map values |
-const Map<String, dynamic> nullClassRefDesc = const <String, dynamic>{ |
- "type": "@Class", |
- "id": "classes/NullClass", |
- "name": "NullClass", |
-}; |
- |
-//TODO(danrubel): Verify const map values |
-const Map<String, dynamic> nullRefDesc = const <String, dynamic>{ |
- "type": "@Null", |
- "id": nullId, |
- "kind": "Null", |
- "class": nullClassRefDesc, |
- "valueAsString": "null", |
-}; |
- |
-//TODO(danrubel): Verify const map values |
-const Map<String, dynamic> nullDesc = const <String, dynamic>{ |
- "type": "Null", |
- "id": nullId, |
- "kind": "Null", |
- "class": nullClassRefDesc, |
- "valueAsString": "null", |
-}; |
- |
class DebugServer { |
Future<int> serveSingleShot(SessionState state, |
{int port: 0, Uri snapshotLocation}) async { |
@@ -145,15 +127,18 @@ class DebugConnection implements DebugListener { |
Map<String, CompilationUnitElement> scripts = |
new Map<String, CompilationUnitElement>(); |
- Map frameDesc(BackTraceFrame frame, int index) { |
+ Future<Map> frameDesc(BackTraceFrame frame, int index) async { |
List<Map> vars = new List<Map>(); |
for (ScopeInfo current = frame.scopeInfo(); |
current != ScopeInfo.sentinel; |
current = current.previous) { |
+ RemoteValue remoteValue = |
+ await vmContext.processLocal(index, current.local.slot); |
vars.add({ |
"type": "BoundVariable", |
"name": current.name, |
- "value": valueDesc(current.lookup(current.name)) |
+ "value": instanceRef( |
+ remoteValue.value, "objects/$index.${current.local.slot}"), |
}); |
} |
return { |
@@ -338,10 +323,136 @@ class DebugConnection implements DebugListener { |
}; |
} |
- Map valueDesc(LocalValue lookup) { |
- //TODO(danrubel): Translate local value into description? |
- // Need to return an @InstanceRef or Sentinel |
- return nullRefDesc; |
+ Map classRef(ClassElement classElement) { |
+ if (classElement == null) { |
+ return {"type": "@Class", "id": "unknown", "name": "unknown class"}; |
+ } |
+ String symbolicName = |
+ "${classElement.library.canonicalUri}.${classElement.name}"; |
+ return { |
+ "type": "@Class", |
+ "id": "classes/$symbolicName", |
+ "name": "${classElement.name}", |
+ }; |
+ } |
+ |
+ Map instanceRef(DartValue value, String id) { |
+ int classId; |
+ Element classElement; |
+ String stringValue; |
+ String kind; |
+ int length; |
+ String name; |
+ if (value is Instance) { |
+ kind = "PlainInstance"; |
+ classId = value.classId; |
+ } else if (value is Integer) { |
+ kind = "Int"; |
+ classElement = vmContext.compiler.compiler.backend.intImplementation; |
+ stringValue = "${value.value}"; |
+ } else if (value is StringValue) { |
+ kind = "String"; |
+ classElement = vmContext.compiler.compiler.backend.stringImplementation; |
+ stringValue = value.value; |
+ } else if (value is Boolean) { |
+ kind = "Bool"; |
+ classElement = vmContext.compiler.compiler.backend.boolImplementation; |
+ stringValue = "${value.value}"; |
+ } else if (value is Double) { |
+ kind = "Double"; |
+ classElement = vmContext.compiler.compiler.backend.doubleImplementation; |
+ stringValue = "${value.value}"; |
+ } else if (value is ClassValue) { |
+ kind = "Type"; |
+ Element element = |
+ vmContext.dartinoSystem.classesById[value.classId].element; |
+ classElement = vmContext.compiler.compiler.backend.typeImplementation; |
+ name = "${element}"; |
+ } else if (value is NullValue) { |
+ kind = "Null"; |
+ classElement = vmContext.compiler.compiler.backend.nullImplementation; |
+ stringValue = "null"; |
+ } else if (value is Array) { |
+ kind = "List"; |
+ classElement = vmContext.compiler.compiler.backend.listImplementation; |
+ length = value.length; |
+ } else { |
+ throw "Unexpected remote value $value"; |
+ } |
+ Map classReference = classRef( |
+ classElement ?? vmContext.dartinoSystem.classesById[classId]?.element); |
+ Map result = { |
+ "type": "@Instance", |
+ "id": id, |
+ "kind": kind, |
+ "class": classReference, |
+ }; |
+ if (stringValue != null) { |
+ result["valueAsString"] = stringValue; |
+ } |
+ if (length != null) { |
+ result["length"] = length; |
+ } |
+ if (name != null) { |
+ result["name"] = name; |
+ } |
+ return result; |
+ } |
+ |
+ Map instanceDesc(RemoteObject remoteObject, String id) { |
+ // TODO(sigurdm): Allow inspecting any frame. |
+ assert(remoteObject is! RemoteErrorObject); |
+ if (remoteObject is RemoteInstance) { |
+ int classId = remoteObject.instance.classId; |
+ Element classElement = |
+ vmContext.dartinoSystem.classesById[classId].element; |
+ List<FieldElement> fieldElements = computeFields(classElement); |
+ assert(fieldElements.length == remoteObject.fields.length); |
+ List fields = new List(); |
+ for (int i = 0; i < fieldElements.length; i++) { |
+ FieldElement fieldElement = fieldElements[i]; |
+ fields.add({ |
+ "type": "BoundField", |
+ "decl": { |
+ "type": "@Field", |
+ "name": fieldElement.name, |
+ "owner": classRef(fieldElement.contextClass), |
+ "declaredType": null, // TODO(sigurdm): fill this in. |
+ "const": fieldElement.isConst, |
+ "final": fieldElement.isFinal, |
+ "static": fieldElement.isStatic, |
+ }, |
+ "value": instanceRef(remoteObject.fields[i], "$id.$i"), |
+ }); |
+ } |
+ return <String, dynamic>{ |
+ "type": "Instance", |
+ "id": id, |
+ "kind": "PlainInstance", |
+ "class": classRef(classElement), |
+ "fields": fields, |
+ }; |
+ } else if (remoteObject is RemoteArray) { |
+ // TODO(sigurdm): Handle large arrays. (Issue #536). |
+ List elements = new List(); |
+ for (int i = 0; i < remoteObject.array.length; i++) { |
+ elements.add(instanceRef(remoteObject.values[i], "$id.$i")); |
+ } |
+ return <String, dynamic>{ |
+ "type": "Instance", |
+ "id": id, |
+ "kind": "List", |
+ "class": |
+ classRef(vmContext.compiler.compiler.backend.listImplementation), |
+ "elements": elements, |
+ }; |
+ } else if (remoteObject is RemoteValue) { |
+ Map instance = instanceRef(remoteObject.value, id); |
+ instance["type"] = "Instance"; |
+ return instance; |
+ } else { |
+ throw "Unexpected remote object kind"; |
+ } |
} |
initialize(DartinoCompilerImplementation compiler) { |
@@ -504,13 +615,15 @@ class DebugConnection implements DebugListener { |
int.parse(id.substring(slashIndex + 1))])); |
break; |
case "objects": |
- if (id == nullId) { |
- sendResult(nullDesc); |
- } else { |
- //TODO(danrubel): Need to return an Instance description |
- // or Sentinel for the given @InstanceRef |
- throw 'Unknown object: $id'; |
- } |
+ String path = id.substring(slashIndex + 1); |
+ List<int> dotted = path.split(".").map(int.parse).toList(); |
+ int localFrame = dotted.first; |
+ int localSlot = dotted[1]; |
+ List<int> fieldAccesses = dotted.skip(2).toList(); |
+ RemoteObject remoteObject = await vmContext.processLocalStructure( |
+ localFrame, localSlot, |
+ fieldAccesses: fieldAccesses); |
+ sendResult(instanceDesc(remoteObject, id)); |
break; |
default: |
throw "Unsupported object type $id"; |
@@ -525,7 +638,7 @@ class DebugConnection implements DebugListener { |
List frames = []; |
int index = 0; |
for (BackTraceFrame frame in backTrace.frames) { |
- frames.add(frameDesc(frame, index)); |
+ frames.add(await frameDesc(frame, index)); |
index++; |
} |
@@ -710,7 +823,7 @@ class DebugConnection implements DebugListener { |
@override |
pauseBreakpoint( |
- int processId, BackTraceFrame topFrame, Breakpoint breakpoint) { |
+ int processId, BackTraceFrame topFrame, Breakpoint breakpoint) async { |
//TODO(danrubel): are there any other breakpoints |
// at which we are currently paused for a PauseBreakpoint event? |
List<Breakpoint> pauseBreakpoints = <Breakpoint>[]; |
@@ -720,7 +833,7 @@ class DebugConnection implements DebugListener { |
"kind": "PauseBreakpoint", |
"isolate": isolateRef(processId), |
"timestamp": new DateTime.now().millisecondsSinceEpoch, |
- "topFrame": frameDesc(topFrame, 0), |
+ "topFrame": await frameDesc(topFrame, 0), |
"atAsyncSuspension": false, |
"breakpoint": breakpointDesc(breakpoint), |
"pauseBreakpoints": |
@@ -731,13 +844,14 @@ class DebugConnection implements DebugListener { |
} |
@override |
- pauseException(int processId, BackTraceFrame topFrame, RemoteObject thrown) { |
+ pauseException( |
+ int processId, BackTraceFrame topFrame, RemoteObject thrown) async { |
Map event = { |
"type": "Event", |
"kind": "PauseException", |
"isolate": isolateRef(processId), |
"timestamp": new DateTime.now().millisecondsSinceEpoch, |
- "topFrame": frameDesc(topFrame, 0), |
+ "topFrame": await frameDesc(topFrame, 0), |
"atAsyncSuspension": false, |
// TODO(sigurdm): pass thrown as an instance. |
}; |
@@ -750,13 +864,13 @@ class DebugConnection implements DebugListener { |
} |
@override |
- pauseInterrupted(int processId, BackTraceFrame topFrame) { |
+ pauseInterrupted(int processId, BackTraceFrame topFrame) async { |
Map event = { |
"type": "Event", |
"kind": "PauseInterrupted", |
"isolate": isolateRef(processId), |
"timestamp": new DateTime.now().millisecondsSinceEpoch, |
- "topFrame": frameDesc(topFrame, 0), |
+ "topFrame": await frameDesc(topFrame, 0), |
"atAsyncSuspension": false, |
}; |
lastPauseEvent = event; |
@@ -808,7 +922,7 @@ class DebugConnection implements DebugListener { |
} |
@override |
- resume(int processId) { |
+ resume(int processId) async { |
Map event = { |
"type": "Event", |
"kind": "Resume", |
@@ -817,7 +931,7 @@ class DebugConnection implements DebugListener { |
}; |
BackTraceFrame topFrame = vmContext.debugState.topFrame; |
if (topFrame != null) { |
- event["topFrame"] = frameDesc(vmContext.debugState.topFrame, 0); |
+ event["topFrame"] = await frameDesc(vmContext.debugState.topFrame, 0); |
} |
lastPauseEvent = event; |
streamNotify("Debug", event); |