| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE.md file. | |
| 4 | |
| 5 library fletchc.debug_info; | |
| 6 | |
| 7 import 'dart:math' show | |
| 8 min; | |
| 9 | |
| 10 import 'package:compiler/src/colors.dart' as colors; | |
| 11 | |
| 12 import 'package:compiler/src/diagnostics/source_span.dart' show | |
| 13 SourceSpan; | |
| 14 | |
| 15 import 'package:compiler/src/elements/elements.dart'; | |
| 16 | |
| 17 import 'package:compiler/src/io/source_file.dart'; | |
| 18 | |
| 19 import 'package:compiler/src/tree/tree.dart' show | |
| 20 Node, | |
| 21 unparse; | |
| 22 | |
| 23 import 'package:compiler/src/source_file_provider.dart' show | |
| 24 SourceFileProvider; | |
| 25 | |
| 26 import 'codegen_visitor.dart'; | |
| 27 import '../fletch_system.dart'; | |
| 28 import 'hub/session_manager.dart' show | |
| 29 SessionState; | |
| 30 | |
| 31 import 'fletch_compiler_implementation.dart' show | |
| 32 FletchCompilerImplementation; | |
| 33 | |
| 34 class ScopeInfo { | |
| 35 static const ScopeInfo sentinel = const ScopeInfo(0, null, null); | |
| 36 | |
| 37 final int bytecodeIndex; | |
| 38 final LocalValue local; | |
| 39 final ScopeInfo previous; | |
| 40 const ScopeInfo(this.bytecodeIndex, this.local, this.previous); | |
| 41 | |
| 42 LocalValue lookup(String name) { | |
| 43 for (ScopeInfo current = this; | |
| 44 current != sentinel; | |
| 45 current = current.previous) { | |
| 46 Element element = current.local.element; | |
| 47 if (element == null && name == 'this') { | |
| 48 return current.local; | |
| 49 } | |
| 50 if (element != null && element.name == name) { | |
| 51 return current.local; | |
| 52 } | |
| 53 } | |
| 54 return null; | |
| 55 } | |
| 56 | |
| 57 String get name { | |
| 58 return (local.element == null) ? 'this' : local.element.name; | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 class SourceLocation { | |
| 63 final int bytecodeIndex; | |
| 64 final Node node; | |
| 65 final SourceSpan span; | |
| 66 final SourceFile file; | |
| 67 SourceLocation(this.bytecodeIndex, this.node, this.span, this.file); | |
| 68 | |
| 69 bool contains(SourceLocation other) { | |
| 70 return span.begin <= other.span.begin && other.span.end <= span.end; | |
| 71 } | |
| 72 | |
| 73 bool containsPosition(int position) { | |
| 74 return span.begin <= position && position <= span.end; | |
| 75 } | |
| 76 | |
| 77 bool isSameSourceLevelLocationAs(SourceLocation other) { | |
| 78 if (other == null) return false; | |
| 79 if (file != other.file) return false; | |
| 80 if (span == null || other.span == null) return span == other.span; | |
| 81 return span.begin == other.span.begin && span.end == other.span.end; | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 class DebugInfo { | |
| 86 final FletchFunction function; | |
| 87 final List<SourceLocation> locations = <SourceLocation>[]; | |
| 88 final List<ScopeInfo> scopeInfos = <ScopeInfo>[ScopeInfo.sentinel]; | |
| 89 | |
| 90 DebugInfo(this.function); | |
| 91 | |
| 92 void addLocation( | |
| 93 FletchCompilerImplementation compiler, | |
| 94 int bytecodeIndex, | |
| 95 Node node) { | |
| 96 SourceSpan span = compiler.reporter.spanFromSpannable(node); | |
| 97 SourceFile file = null; | |
| 98 // TODO(ahe): What to do if compiler.provider isn't a SourceFileProvider? | |
| 99 // Perhaps we can create a new type of diagnostic, see | |
| 100 // package:compiler/compiler.dart. The class Diagnostic is an "extensible" | |
| 101 // enum class. This way, the debugger doesn't hold on to files. | |
| 102 // Alternatively, source files should be obtained by iterating through the | |
| 103 // compilation units. | |
| 104 if (span != null && compiler.provider is SourceFileProvider) { | |
| 105 SourceFileProvider provider = compiler.provider; | |
| 106 Uri resourceUri = compiler.translateUri(span, span.uri); | |
| 107 file = provider.sourceFiles[resourceUri]; | |
| 108 } | |
| 109 locations.add(new SourceLocation(bytecodeIndex, node, span, file)); | |
| 110 } | |
| 111 | |
| 112 void pushScope(int bytecodeIndex, LocalValue local) { | |
| 113 scopeInfos.add(new ScopeInfo(bytecodeIndex, | |
| 114 local, | |
| 115 scopeInfos.last)); | |
| 116 } | |
| 117 | |
| 118 void popScope(int bytecodeIndex) { | |
| 119 ScopeInfo previous = scopeInfos.last.previous; | |
| 120 scopeInfos.add(new ScopeInfo(bytecodeIndex, | |
| 121 previous.local, | |
| 122 previous.previous)); | |
| 123 } | |
| 124 | |
| 125 ScopeInfo scopeInfoFor(int bytecodeIndex) { | |
| 126 return scopeInfos.lastWhere( | |
| 127 (location) => location.bytecodeIndex <= bytecodeIndex, | |
| 128 orElse: () => ScopeInfo.sentinel); | |
| 129 } | |
| 130 | |
| 131 SourceLocation locationFor(int bytecodeIndex) { | |
| 132 return locations.lastWhere( | |
| 133 (location) => location.bytecodeIndex <= bytecodeIndex, | |
| 134 orElse: () => null); | |
| 135 } | |
| 136 | |
| 137 SourceLocation locationForPosition(int position) { | |
| 138 SourceLocation current; | |
| 139 bool foundContaining = false; | |
| 140 | |
| 141 for (SourceLocation location in locations) { | |
| 142 if (location.span == null) continue; | |
| 143 if (location.containsPosition(position)) { | |
| 144 if (foundContaining) { | |
| 145 if (current.contains(location)) current = location; | |
| 146 } else { | |
| 147 foundContaining = true; | |
| 148 current = location; | |
| 149 } | |
| 150 } else if (!foundContaining) { | |
| 151 current = current != null ? current : location; | |
| 152 if (location.span.begin > position && | |
| 153 (current.span.begin > location.span.begin || | |
| 154 current.span.begin < position)) { | |
| 155 current = location; | |
| 156 } | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 return current; | |
| 161 } | |
| 162 | |
| 163 // TODO(ager): Should this be upstreamed to dart2js? | |
| 164 String fileAndLineStringFor(int bytecodeIndex) { | |
| 165 SourceLocation location = locationFor(bytecodeIndex); | |
| 166 if (location == null) return ''; | |
| 167 SourceSpan span = location.span; | |
| 168 if (span == null) return ''; | |
| 169 SourceFile file = location.file; | |
| 170 if (file == null) return ''; | |
| 171 int currentLine = file.getLine(span.begin); | |
| 172 int column = file.getColumn(currentLine, span.begin); | |
| 173 return '${file.filename}:${currentLine + 1}:${column + 1}'; | |
| 174 } | |
| 175 | |
| 176 String astStringFor(int bytecodeIndex) { | |
| 177 SourceLocation location = locationFor(bytecodeIndex); | |
| 178 if (location == null || location.node == null) return null; | |
| 179 return unparse(location.node, minify: false); | |
| 180 } | |
| 181 | |
| 182 // TODO(ager): Should something like this be upstreamed to dart2js? | |
| 183 String sourceListStringFor( | |
| 184 int bytecodeIndex, | |
| 185 SessionState state, | |
| 186 {int contextLines: 5}) { | |
| 187 SourceLocation location = locationFor(bytecodeIndex); | |
| 188 if (location == null) return ''; | |
| 189 SourceSpan span = location.span; | |
| 190 if (span == null) return ''; | |
| 191 SourceFile file = location.file; | |
| 192 if (file == null) return ''; | |
| 193 | |
| 194 int currentLine = file.getLine(span.begin); | |
| 195 int column = file.getColumn(currentLine, span.begin); | |
| 196 int startLine = currentLine - contextLines; | |
| 197 if (startLine < 0) startLine = 0; | |
| 198 StringBuffer buffer = new StringBuffer(); | |
| 199 | |
| 200 buffer.writeln(fileAndLineStringFor(bytecodeIndex)); | |
| 201 | |
| 202 // Add contextLines before the breakpoint line. | |
| 203 for (; startLine < currentLine; startLine++) { | |
| 204 var l = file.slowSubstring(file.lineStarts[startLine], | |
| 205 file.lineStarts[startLine + 1]); | |
| 206 buffer.write('${startLine + 1}'.padRight(5) + l); | |
| 207 } | |
| 208 | |
| 209 // Add the breakpoint line highlighting the actual breakpoint location. | |
| 210 var l = file.slowSubstring( | |
| 211 file.lineStarts[currentLine], | |
| 212 min(file.lineStarts[currentLine + 1], file.length)); | |
| 213 var toColumn = min(column + (span.end - span.begin), l.length); | |
| 214 var prefix = l.substring(0, column); | |
| 215 var focus = highlight(l.substring(column, toColumn), colors.red, state); | |
| 216 var postfix = l.substring(toColumn); | |
| 217 buffer.write('${currentLine + 1}'.padRight(5) + prefix); | |
| 218 buffer.write(focus); | |
| 219 buffer.write(postfix); | |
| 220 | |
| 221 // Add contextLines after the breakpoint line. | |
| 222 int endLine = currentLine + contextLines; | |
| 223 if (endLine > file.lineStarts.length - 3) { | |
| 224 endLine = file.lineStarts.length - 3; | |
| 225 } | |
| 226 for (startLine = currentLine + 1; startLine <= endLine; startLine++) { | |
| 227 var lineEnd = file.lineStarts[startLine + 1]; | |
| 228 // For last line remove the newline. | |
| 229 if (startLine == endLine) --lineEnd; | |
| 230 var l = file.slowSubstring(file.lineStarts[startLine], lineEnd); | |
| 231 buffer.write('${startLine + 1}'.padRight(5) + l); | |
| 232 } | |
| 233 | |
| 234 return buffer.toString(); | |
| 235 } | |
| 236 | |
| 237 String highlight(String message, Function color, SessionState state) { | |
| 238 return state.colorsDisabled ? message : color(message); | |
| 239 } | |
| 240 | |
| 241 SourceLocation sourceLocationFor(int bytecodeIndex) { | |
| 242 return locationFor(bytecodeIndex); | |
| 243 } | |
| 244 } | |
| OLD | NEW |