OLD | NEW |
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dartino 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.md file. | 3 // BSD-style license that can be found in the LICENSE.md file. |
4 | 4 |
5 library dartino.vm_session; | 5 library dartino.vm_session; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 | 8 |
9 import 'dart:typed_data' show | 9 import 'dart:typed_data' show |
10 ByteData; | 10 ByteData; |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 | 56 |
57 import 'src/vm_connection.dart' show | 57 import 'src/vm_connection.dart' show |
58 VmConnection; | 58 VmConnection; |
59 | 59 |
60 import 'program_info.dart' show | 60 import 'program_info.dart' show |
61 IdOffsetMapping, | 61 IdOffsetMapping, |
62 NameOffsetMapping, | 62 NameOffsetMapping, |
63 ProgramInfoJson; | 63 ProgramInfoJson; |
64 | 64 |
65 import 'package:compiler/src/elements/elements.dart' show | 65 import 'package:compiler/src/elements/elements.dart' show |
66 ClassElement, | 66 ClassElement; |
67 FieldElement; | |
68 | 67 |
69 class SessionCommandTransformerBuilder | 68 class SessionCommandTransformerBuilder |
70 extends CommandTransformerBuilder<Pair<int, ByteData>> { | 69 extends CommandTransformerBuilder<Pair<int, ByteData>> { |
71 | 70 |
72 Pair<int, ByteData> makeCommand(int code, ByteData payload) { | 71 Pair<int, ByteData> makeCommand(int code, ByteData payload) { |
73 return new Pair<int, ByteData>(code, payload); | 72 return new Pair<int, ByteData>(code, payload); |
74 } | 73 } |
75 } | 74 } |
76 | 75 |
77 // TODO(sigurdm): for now only the stdio and stderr events are actually | 76 // TODO(sigurdm): for now only the stdio and stderr events are actually |
(...skipping 771 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
849 compiler, | 848 compiler, |
850 debugState)); | 849 debugState)); |
851 } | 850 } |
852 return stackTrace; | 851 return stackTrace; |
853 } | 852 } |
854 | 853 |
855 Future<RemoteObject> uncaughtException() async { | 854 Future<RemoteObject> uncaughtException() async { |
856 assert(vmState == VmState.terminating); | 855 assert(vmState == VmState.terminating); |
857 if (debugState.currentUncaughtException == null) { | 856 if (debugState.currentUncaughtException == null) { |
858 await sendCommand(const ProcessUncaughtExceptionRequest()); | 857 await sendCommand(const ProcessUncaughtExceptionRequest()); |
859 VmCommand response = await readNextCommand(); | 858 debugState.currentUncaughtException = await readStructuredObject(); |
860 if (response is DartValue) { | |
861 debugState.currentUncaughtException = new RemoteValue(response); | |
862 } else { | |
863 assert(response is InstanceStructure); | |
864 List<DartValue> fields = await readInstanceStructureFields(response); | |
865 debugState.currentUncaughtException = | |
866 new RemoteInstance(response, fields); | |
867 } | |
868 } | 859 } |
869 return debugState.currentUncaughtException; | 860 return debugState.currentUncaughtException; |
870 } | 861 } |
871 | 862 |
872 Future<BackTrace> backTrace({int processId}) async { | 863 Future<BackTrace> backTrace({int processId}) async { |
873 processId ??= debugState.currentProcess; | 864 processId ??= debugState.currentProcess; |
874 assert(isSpawned); | 865 assert(isSpawned); |
875 if (debugState.currentBackTrace == null) { | 866 if (debugState.currentBackTrace == null) { |
876 ProcessBacktrace backtraceResponse = | 867 ProcessBacktrace backtraceResponse = |
877 await runCommand( | 868 await runCommand( |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
944 String fieldName = debugState.lookupFieldName(klass, i); | 935 String fieldName = debugState.lookupFieldName(klass, i); |
945 if (fieldName == null) { | 936 if (fieldName == null) { |
946 fieldName = '<unnamed>'; | 937 fieldName = '<unnamed>'; |
947 } | 938 } |
948 sb.writeln(' $fieldName: ${dartValueToString(value)}'); | 939 sb.writeln(' $fieldName: ${dartValueToString(value)}'); |
949 } | 940 } |
950 sb.write('}'); | 941 sb.write('}'); |
951 return '$sb'; | 942 return '$sb'; |
952 } | 943 } |
953 | 944 |
| 945 String arrayStructureToString(List<DartValue> items) { |
| 946 StringBuffer sb = new StringBuffer(); |
| 947 sb.writeln("Array with length ${items.length} ["); |
| 948 for (int i = 0; i < items.length; i++) { |
| 949 sb.writeln(" $i = ${dartValueToString(items[i])}"); |
| 950 } |
| 951 sb.write("]"); |
| 952 return '$sb'; |
| 953 } |
| 954 |
954 String noSuchMethodErrorToString(List<DartValue> nsmFields) { | 955 String noSuchMethodErrorToString(List<DartValue> nsmFields) { |
955 assert(nsmFields.length == 3); | 956 assert(nsmFields.length == 3); |
956 ClassValue receiverClass = nsmFields[1]; | 957 ClassValue receiverClass = nsmFields[1]; |
957 Integer receiverSelector = nsmFields[2]; | 958 Integer receiverSelector = nsmFields[2]; |
958 DartinoSelector selector = new DartinoSelector(receiverSelector.value); | 959 DartinoSelector selector = new DartinoSelector(receiverSelector.value); |
959 | 960 |
960 String method = | 961 String method = |
961 dartinoSystem.lookupSymbolBySelector(selector.encodedSelector); | 962 dartinoSystem.lookupSymbolBySelector(selector.encodedSelector); |
962 DartinoClass klass = dartinoSystem.lookupClassById(receiverClass.classId); | 963 DartinoClass klass = dartinoSystem.lookupClassById(receiverClass.classId); |
963 // TODO(ahe): If DartinoClass is a synthetic closure class, improve the | 964 // TODO(ahe): If DartinoClass is a synthetic closure class, improve the |
(...skipping 13 matching lines...) Expand all Loading... |
977 case SelectorKind.Getter: | 978 case SelectorKind.Getter: |
978 return "NoSuchMethodError: " | 979 return "NoSuchMethodError: " |
979 "Class '$name' has no getter named '$method'$raw"; | 980 "Class '$name' has no getter named '$method'$raw"; |
980 | 981 |
981 case SelectorKind.Setter: | 982 case SelectorKind.Setter: |
982 return "NoSuchMethodError: " | 983 return "NoSuchMethodError: " |
983 "Class '$name' has no setter named '$method'$raw"; | 984 "Class '$name' has no setter named '$method'$raw"; |
984 } | 985 } |
985 } | 986 } |
986 | 987 |
987 Future<List<DartValue>> readInstanceStructureFields( | 988 Future<RemoteObject> readStructuredObject() async { |
988 InstanceStructure structure) async { | 989 VmCommand response = await readNextCommand(); |
989 List<DartValue> fields = <DartValue>[]; | 990 if (response is DartValue) { |
990 for (int i = 0; i < structure.fields; i++) { | 991 return new RemoteValue(response); |
991 fields.add(await readNextCommand()); | 992 } else if (response is InstanceStructure) { |
| 993 List<DartValue> fields = new List<DartValue>(); |
| 994 for (int i = 0; i < response.fields; i++) { |
| 995 fields.add(await readNextCommand()); |
| 996 } |
| 997 return new RemoteInstance(response, fields); |
| 998 } else if (response is ArrayStructure) { |
| 999 List<DartValue> values = new List<DartValue>(); |
| 1000 for (int i = 0; i < response.length; i++) { |
| 1001 values.add(await readNextCommand()); |
| 1002 } |
| 1003 return new RemoteArray(response, values); |
| 1004 } else { |
| 1005 return new RemoteErrorObject("Failed reading structured object."); |
992 } | 1006 } |
993 return fields; | |
994 } | 1007 } |
995 | 1008 |
996 String exceptionToString(RemoteObject exception) { | 1009 String exceptionToString(RemoteObject exception) { |
997 String message; | 1010 String message; |
998 if (exception is RemoteValue) { | 1011 if (exception is RemoteValue) { |
999 message = dartValueToString(exception.value); | 1012 message = dartValueToString(exception.value); |
1000 } else if (exception is RemoteInstance) { | 1013 } else if (exception is RemoteInstance) { |
1001 InstanceStructure structure = exception.instance; | 1014 InstanceStructure structure = exception.instance; |
1002 int classId = structure.classId; | 1015 int classId = structure.classId; |
1003 DartinoClass klass = dartinoSystem.lookupClassById(classId); | 1016 DartinoClass klass = dartinoSystem.lookupClassById(classId); |
(...skipping 12 matching lines...) Expand all Loading... |
1016 throw new UnimplementedError(); | 1029 throw new UnimplementedError(); |
1017 } | 1030 } |
1018 return 'Uncaught exception: $message'; | 1031 return 'Uncaught exception: $message'; |
1019 } | 1032 } |
1020 | 1033 |
1021 Future<RemoteObject> _processVariable( | 1034 Future<RemoteObject> _processVariable( |
1022 List<String> names, bool getStructure) async { | 1035 List<String> names, bool getStructure) async { |
1023 assert(isSpawned); | 1036 assert(isSpawned); |
1024 assert(names.isNotEmpty); | 1037 assert(names.isNotEmpty); |
1025 String localName = names.first; | 1038 String localName = names.first; |
| 1039 int frame = debugState.currentFrame; |
1026 LocalValue local = await lookupValue(localName); | 1040 LocalValue local = await lookupValue(localName); |
1027 if (local == null) { | 1041 if (local == null) { |
1028 return new RemoteErrorObject("No local '$localName' in scope."); | 1042 return new RemoteErrorObject("No local '$localName' in scope."); |
1029 } | 1043 } |
1030 | 1044 |
1031 List<int> fieldIndices = new List<int>(); | 1045 List<int> fieldIndices = new List<int>(); |
1032 List<String> namesTillHere = <String>[localName]; | 1046 List<String> namesTillHere = <String>[localName]; |
1033 for (String fieldName in names.skip(1)) { | 1047 for (String fieldName in names.skip(1)) { |
1034 RemoteValue remoteValue = | 1048 RemoteValue remoteValue = await processLocal( |
1035 await processLocal(local, fieldAccesses: fieldIndices); | 1049 frame, |
| 1050 local.slot, |
| 1051 fieldAccesses: fieldIndices); |
1036 DartValue value = remoteValue.value; | 1052 DartValue value = remoteValue.value; |
1037 if (value is Instance) { | 1053 if (value is Instance) { |
1038 ClassElement classElement = | 1054 ClassElement classElement = |
1039 dartinoSystem.classesById[value.classId].element; | 1055 dartinoSystem.classesById[value.classId].element; |
1040 int fieldIndex = getFieldIndex(classElement, fieldName); | 1056 int fieldIndex = getFieldIndex(classElement, fieldName); |
1041 if (fieldIndex == null) { | 1057 if (fieldIndex == null) { |
1042 return new RemoteErrorObject( | 1058 return new RemoteErrorObject( |
1043 "'${namesTillHere.join(".")}' has type ${classElement}" | 1059 "'${namesTillHere.join(".")}' has type ${classElement}" |
1044 " that does not have a field named '${fieldName}'."); | 1060 " that does not have a field named '${fieldName}'."); |
1045 } | 1061 } |
1046 fieldIndices.add(fieldIndex); | 1062 fieldIndices.add(fieldIndex); |
| 1063 } else if (value is Array) { |
| 1064 int index = int.parse(fieldName, onError: (_) => -1); |
| 1065 if (index < 0 || index > value.length) { |
| 1066 return new RemoteErrorObject( |
| 1067 "${namesTillHere.join(".")}' is an array with length " |
| 1068 "${value.length}. It cannot be indexed with ${fieldName}"); |
| 1069 } |
| 1070 fieldIndices.add(index); |
1047 } else { | 1071 } else { |
1048 return new RemoteErrorObject("'${namesTillHere.join(".")}' " | 1072 return new RemoteErrorObject("'${namesTillHere.join(".")}' " |
1049 "is a primitive value '${dartValueToString(value)}' " | 1073 "is a primitive value '${dartValueToString(value)}' " |
1050 "and does not have a field named '${fieldName}'"); | 1074 "and does not have a field named '${fieldName}'"); |
1051 } | 1075 } |
1052 namesTillHere.add(fieldName); | 1076 namesTillHere.add(fieldName); |
1053 } | 1077 } |
1054 if (getStructure) { | 1078 if (getStructure) { |
1055 return await processLocalStructure(local, fieldAccesses: fieldIndices); | 1079 return await processLocalStructure( |
| 1080 frame, local.slot, fieldAccesses: fieldIndices); |
1056 } else { | 1081 } else { |
1057 return await processLocal(local, fieldAccesses: fieldIndices); | 1082 return await processLocal(frame, local.slot, fieldAccesses: fieldIndices); |
1058 } | 1083 } |
1059 } | 1084 } |
1060 | 1085 |
1061 Future<RemoteValue> processVariable(List<String> names) { | 1086 Future<RemoteValue> processVariable(List<String> names) { |
1062 return _processVariable(names, false); | 1087 return _processVariable(names, false); |
1063 } | 1088 } |
1064 | 1089 |
1065 Future<RemoteObject> processVariableStructure(List<String> names) { | 1090 Future<RemoteObject> processVariableStructure(List<String> names) { |
1066 return _processVariable(names, true); | 1091 return _processVariable(names, true); |
1067 } | 1092 } |
1068 | 1093 |
1069 Future<List<RemoteObject>> processAllVariables() async { | 1094 Future<List<RemoteObject>> processAllVariables() async { |
1070 assert(isSpawned); | 1095 assert(isSpawned); |
1071 BackTrace trace = await backTrace(); | 1096 BackTrace trace = await backTrace(); |
1072 ScopeInfo info = trace.scopeInfoForCurrentFrame; | 1097 ScopeInfo info = trace.scopeInfoForCurrentFrame; |
1073 List<RemoteObject> variables = []; | 1098 List<RemoteObject> variables = []; |
1074 for (ScopeInfo current = info; | 1099 for (ScopeInfo current = info; |
1075 current != ScopeInfo.sentinel; | 1100 current != ScopeInfo.sentinel; |
1076 current = current.previous) { | 1101 current = current.previous) { |
1077 variables.add(await processLocal(current.local, name: current.name)); | 1102 variables.add(await processLocal( |
| 1103 debugState.currentFrame, current.local.slot, name: current.name)); |
1078 } | 1104 } |
1079 return variables; | 1105 return variables; |
1080 } | 1106 } |
1081 | 1107 |
1082 Future<LocalValue> lookupValue(String name) async { | 1108 Future<LocalValue> lookupValue(String name) async { |
1083 assert(isSpawned); | 1109 assert(isSpawned); |
1084 BackTrace trace = await backTrace(); | 1110 BackTrace trace = await backTrace(); |
1085 return trace.scopeInfoForCurrentFrame.lookup(name); | 1111 return trace.scopeInfoForCurrentFrame.lookup(name); |
1086 } | 1112 } |
1087 | 1113 |
1088 Future<RemoteValue> processLocal( | 1114 Future<RemoteValue> processLocal( |
1089 LocalValue local, | 1115 int frameNumber, |
| 1116 int localSlot, |
1090 {String name, | 1117 {String name, |
1091 List<int> fieldAccesses: const <int>[]}) async { | 1118 List<int> fieldAccesses: const <int>[]}) async { |
1092 var actualFrameNumber = debugState.actualCurrentFrameNumber; | |
1093 VmCommand response = await runCommand( | 1119 VmCommand response = await runCommand( |
1094 new ProcessInstance(actualFrameNumber, local.slot, fieldAccesses)); | 1120 new ProcessInstance(frameNumber, localSlot, fieldAccesses)); |
1095 assert(response is DartValue); | 1121 assert(response is DartValue); |
1096 return new RemoteValue(response, name: name); | 1122 return new RemoteValue(response, name: name); |
1097 } | 1123 } |
1098 | 1124 |
1099 Future<RemoteObject> processLocalStructure( | 1125 Future<RemoteObject> processLocalStructure( |
1100 LocalValue local, | 1126 int frameNumber, |
| 1127 int localSlot, |
1101 {String name, | 1128 {String name, |
1102 List<int> fieldAccesses: const <int>[]}) async { | 1129 List<int> fieldAccesses: const <int>[]}) async { |
1103 var frameNumber = debugState.actualCurrentFrameNumber; | 1130 frameNumber ??= debugState.actualCurrentFrameNumber; |
1104 await sendCommand( | 1131 await sendCommand( |
1105 new ProcessInstanceStructure(frameNumber, local.slot, fieldAccesses)); | 1132 new ProcessInstanceStructure(frameNumber, localSlot, fieldAccesses)); |
1106 VmCommand response = await readNextCommand(); | 1133 return await readStructuredObject(); |
1107 if (response is DartValue) { | |
1108 return new RemoteValue(response); | |
1109 } else { | |
1110 assert(response is InstanceStructure); | |
1111 List<DartValue> fields = await readInstanceStructureFields(response); | |
1112 return new RemoteInstance(response, fields); | |
1113 } | |
1114 } | 1134 } |
1115 | 1135 |
1116 String remoteObjectToString(RemoteObject object) { | 1136 String remoteObjectToString(RemoteObject object) { |
1117 String message; | 1137 String message; |
1118 if (object is RemoteValue) { | 1138 if (object is RemoteValue) { |
1119 message = dartValueToString(object.value); | 1139 message = dartValueToString(object.value); |
1120 } else if (object is RemoteInstance) { | 1140 } else if (object is RemoteInstance) { |
1121 message = instanceStructureToString(object.instance, object.fields); | 1141 message = instanceStructureToString(object.instance, object.fields); |
| 1142 } else if (object is RemoteArray) { |
| 1143 message = arrayStructureToString(object.values); |
1122 } else { | 1144 } else { |
1123 throw new UnimplementedError(); | 1145 throw new UnimplementedError(); |
1124 } | 1146 } |
1125 if (object.name != null) { | 1147 if (object.name != null) { |
1126 // Prefix with name. | 1148 // Prefix with name. |
1127 message = "${object.name}: $message"; | 1149 message = "${object.name}: $message"; |
1128 } | 1150 } |
1129 return message; | 1151 return message; |
1130 } | 1152 } |
1131 | 1153 |
1132 bool toggleInternal() { | 1154 bool toggleInternal() { |
1133 debugState.showInternalFrames = !debugState.showInternalFrames; | 1155 debugState.showInternalFrames = !debugState.showInternalFrames; |
1134 if (debugState.currentBackTrace != null) { | 1156 if (debugState.currentBackTrace != null) { |
1135 debugState.currentBackTrace.visibilityChanged(); | 1157 debugState.currentBackTrace.visibilityChanged(); |
1136 } | 1158 } |
1137 return debugState.showInternalFrames; | 1159 return debugState.showInternalFrames; |
1138 } | 1160 } |
1139 } | 1161 } |
OLD | NEW |