| 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 |