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 |