Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: pkg/compiler/lib/src/io/position_information.dart

Issue 1196433002: Create and test source mapping for invocations. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Rebased Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart 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 file.
4
5 /// Source information system mapping that attempts a semantic mapping between
6 /// offsets of JavaScript code points to offsets of Dart code points.
7
8 library dart2js.source_information.position;
9
10 import '../dart2jslib.dart' show
11 invariant,
12 MessageKind,
13 SourceSpan;
14 import '../elements/elements.dart' show
15 AstElement,
16 LocalElement;
17 import '../js/js.dart' as js;
18 import '../js/js_source_mapping.dart';
19 import '../js/js_debug.dart';
20 import '../tree/tree.dart' show Node, Send;
21 import '../util/util.dart' show NO_LOCATION_SPANNABLE;
22
23 import 'source_file.dart';
24 import 'source_information.dart';
25
26 /// [SourceInformation] that consists of an offset position into the source
27 /// code.
28 class PositionSourceInformation extends SourceInformation {
29 @override
30 final SourceLocation startPosition;
31
32 @override
33 final SourceLocation closingPosition;
34
35 PositionSourceInformation(this.startPosition,
36 [this.closingPosition]);
37
38 @override
39 List<SourceLocation> get sourceLocations {
40 List<SourceLocation> list = <SourceLocation>[];
41 if (startPosition != null) {
42 list.add(startPosition);
43 }
44 if (closingPosition != null) {
45 list.add(closingPosition);
46 }
47 return list;
48 }
49
50 @override
51 SourceSpan get sourceSpan {
52 SourceLocation location =
53 startPosition != null ? startPosition : closingPosition;
54 Uri uri = location.sourceUri;
55 int offset = location.offset;
56 return new SourceSpan(uri, offset, offset);
57 }
58
59 int get hashCode {
60 return 0x7FFFFFFF &
61 (startPosition.hashCode * 17 + closingPosition.hashCode * 19);
62 }
63
64 bool operator ==(other) {
65 if (identical(this, other)) return true;
66 if (other is! PositionSourceInformation) return false;
67 return startPosition == other.startPosition &&
68 closingPosition == other.closingPosition;
69 }
70
71 /// Create a textual representation of the source information using [uriText]
72 /// as the Uri representation.
73 String _computeText(String uriText) {
74 StringBuffer sb = new StringBuffer();
75 sb.write('$uriText:');
76 // Use 1-based line/column info to match usual dart tool output.
77 if (startPosition != null) {
78 sb.write('[${startPosition.line + 1},'
79 '${startPosition.column + 1}]');
80 }
81 if (closingPosition != null) {
82 sb.write('-[${closingPosition.line + 1},'
83 '${closingPosition.column + 1}]');
84 }
85 return sb.toString();
86 }
87
88 String get shortText {
89 if (startPosition != null) {
90 return _computeText(startPosition.sourceUri.pathSegments.last);
91 } else {
92 return _computeText(closingPosition.sourceUri.pathSegments.last);
93 }
94 }
95
96 String toString() {
97 if (startPosition != null) {
98 return _computeText('${startPosition.sourceUri}');
99 } else {
100 return _computeText('${closingPosition.sourceUri}');
101 }
102 }
103 }
104
105 class PositionSourceInformationStrategy
106 implements JavaScriptSourceInformationStrategy {
107 const PositionSourceInformationStrategy();
108
109 @override
110 SourceInformationBuilder createBuilderForContext(AstElement element) {
111 return new PositionSourceInformationBuilder(element);
112 }
113
114 @override
115 SourceInformationProcessor createProcessor(SourceMapper mapper) {
116 return new PositionSourceInformationProcessor(mapper);
117 }
118 }
119
120 /// [SourceInformationBuilder] that generates [PositionSourceInformation].
121 class PositionSourceInformationBuilder implements SourceInformationBuilder {
122 final SourceFile sourceFile;
123 final String name;
124
125 PositionSourceInformationBuilder(AstElement element)
126 : sourceFile = element.implementation.compilationUnit.script.file,
127 name = computeElementNameForSourceMaps(element);
128
129 SourceInformation buildDeclaration(AstElement element) {
130 if (element.isSynthesized) {
131 return new PositionSourceInformation(
132 new OffsetSourceLocation(
133 sourceFile, element.position.charOffset, name));
134 } else {
135 return new PositionSourceInformation(
136 null,
137 new OffsetSourceLocation(sourceFile,
138 element.resolvedAst.node.getEndToken().charOffset, name));
139 }
140 }
141
142 /// Builds a source information object pointing the start position of [node].
143 SourceInformation buildBegin(Node node) {
144 return new PositionSourceInformation(new OffsetSourceLocation(
145 sourceFile, node.getBeginToken().charOffset, name));
146 }
147
148 @override
149 SourceInformation buildGeneric(Node node) => buildBegin(node);
150
151 @override
152 SourceInformation buildReturn(Node node) => buildBegin(node);
153
154 @override
155 SourceInformation buildImplicitReturn(AstElement element) {
156 if (element.isSynthesized) {
157 return new PositionSourceInformation(
158 new OffsetSourceLocation(
159 sourceFile, element.position.charOffset, name));
160 } else {
161 return new PositionSourceInformation(
162 new OffsetSourceLocation(sourceFile,
163 element.resolvedAst.node.getEndToken().charOffset, name));
164 }
165 }
166
167
168 @override
169 SourceInformation buildLoop(Node node) => buildBegin(node);
170
171 @override
172 SourceInformation buildGet(Node node) => buildBegin(node);
173
174 @override
175 SourceInformation buildCall(Node receiver, Node call) {
176 return new PositionSourceInformation(
177 new OffsetSourceLocation(
178 sourceFile, receiver.getBeginToken().charOffset, name),
179 new OffsetSourceLocation(
180 sourceFile, call.getBeginToken().charOffset, name));
181 }
182
183 @override
184 SourceInformation buildNew(Node node) {
185 return buildBegin(node);
186 }
187
188 @override
189 SourceInformation buildIf(Node node) => buildBegin(node);
190
191 @override
192 SourceInformation buildThrow(Node node) => buildBegin(node);
193
194 @override
195 SourceInformation buildAssignment(Node node) => buildBegin(node);
196
197 @override
198 SourceInformationBuilder forContext(AstElement element) {
199 return new PositionSourceInformationBuilder(element);
200 }
201 }
202
203 /// The start, end and closing offsets for a [js.Node].
204 class CodePosition {
205 final int startPosition;
206 final int endPosition;
207 final int closingPosition;
208
209 CodePosition(this.startPosition, this.endPosition, this.closingPosition);
210 }
211
212 /// Registry for mapping [js.Node]s to their [CodePosition].
213 class CodePositionRecorder {
214 Map<js.Node, CodePosition> _codePositionMap = <js.Node, CodePosition>{};
215
216 void registerPositions(js.Node node,
217 int startPosition,
218 int endPosition,
219 int closingPosition) {
220 registerCodePosition(node,
221 new CodePosition(startPosition, endPosition, closingPosition));
222 }
223
224 void registerCodePosition(js.Node node, CodePosition codePosition) {
225 _codePositionMap[node] = codePosition;
226 }
227
228 CodePosition operator [](js.Node node) => _codePositionMap[node];
229 }
230
231 enum SourcePositionKind {
232 START,
233 CLOSING,
234 END,
235 }
236
237 enum CodePositionKind {
238 START,
239 CLOSING,
240 END,
241 }
242
243 /// Processor that associates [SourceLocation]s from [SourceInformation] on
244 /// [js.Node]s with the target offsets in a [SourceMapper].
245 class PositionSourceInformationProcessor
246 extends js.BaseVisitor implements SourceInformationProcessor {
247 final CodePositionRecorder codePositions = new CodePositionRecorder();
248 final SourceMapper sourceMapper;
249
250 PositionSourceInformationProcessor(this.sourceMapper);
251
252 void process(js.Node node) {
253 node.accept(this);
254 }
255
256 void visitChildren(js.Node node) {
257 node.visitChildren(this);
258 }
259
260 CodePosition getCodePosition(js.Node node) {
261 return codePositions[node];
262 }
263
264 /// Associates [sourceInformation] with the JavaScript [node].
265 ///
266 /// The offset into the JavaScript code is computed by pulling the
267 /// [codePositionKind] from the code positions associated with
268 /// [codePositionNode].
269 ///
270 /// The mapped Dart source location is computed by pulling the
271 /// [sourcePositionKind] source location from [sourceInformation].
272 void apply(js.Node node,
273 js.Node codePositionNode,
274 CodePositionKind codePositionKind,
275 SourceInformation sourceInformation,
276 SourcePositionKind sourcePositionKind) {
277 if (sourceInformation != null) {
278 CodePosition codePosition = getCodePosition(codePositionNode);
279 // We should always have recorded the needed code positions.
280 assert(invariant(
281 NO_LOCATION_SPANNABLE,
282 codePosition != null,
283 message:
284 "Code position missing for "
285 "${nodeToString(codePositionNode)}:\n"
286 "${DebugPrinter.prettyPrint(node)}"));
287 if (codePosition == null) return;
288 int codeLocation;
289 SourceLocation sourceLocation;
290 switch (codePositionKind) {
291 case CodePositionKind.START:
292 codeLocation = codePosition.startPosition;
293 break;
294 case CodePositionKind.CLOSING:
295 codeLocation = codePosition.closingPosition;
296 break;
297 case CodePositionKind.END:
298 codeLocation = codePosition.endPosition;
299 break;
300 }
301 switch (sourcePositionKind) {
302 case SourcePositionKind.START:
303 sourceLocation = sourceInformation.startPosition;
304 break;
305 case SourcePositionKind.CLOSING:
306 sourceLocation = sourceInformation.closingPosition;
307 break;
308 case SourcePositionKind.END:
309 sourceLocation = sourceInformation.endPosition;
310 break;
311 }
312 if (codeLocation != null && sourceLocation != null) {
313 sourceMapper.register(node, codeLocation, sourceLocation);
314 }
315 }
316 }
317
318 @override
319 visitNode(js.Node node) {
320 SourceInformation sourceInformation = node.sourceInformation;
321 if (sourceInformation != null) {
322 /// Associates the left-most position of the JS code with the left-most
323 /// position of the Dart code.
324 apply(node,
325 node, CodePositionKind.START,
326 sourceInformation, SourcePositionKind.START);
327 }
328 visitChildren(node);
329 }
330
331 @override
332 visitFun(js.Fun node) {
333 SourceInformation sourceInformation = node.sourceInformation;
334 if (sourceInformation != null) {
335 /// Associates the end brace of the JavaScript function with the end brace
336 /// of the Dart function (or the `;` in case of arrow notation).
337 apply(node,
338 node, CodePositionKind.CLOSING,
339 sourceInformation, SourcePositionKind.CLOSING);
340 }
341
342 visitChildren(node);
343 }
344
345 @override
346 visitExpressionStatement(js.ExpressionStatement node) {
347 visitChildren(node);
348 }
349
350 @override
351 visitBinary(js.Binary node) {
352 visitChildren(node);
353 }
354
355 @override
356 visitAccess(js.PropertyAccess node) {
357 visitChildren(node);
358 }
359
360 @override
361 visitCall(js.Call node) {
362 SourceInformation sourceInformation = node.sourceInformation;
363 if (sourceInformation != null) {
364 if (node.target is js.PropertyAccess) {
365 js.PropertyAccess access = node.target;
366 js.Node target = access;
367 bool pureAccess = false;
368 while (target is js.PropertyAccess) {
369 js.PropertyAccess targetAccess = target;
370 if (targetAccess.receiver is js.VariableUse ||
371 targetAccess.receiver is js.This) {
372 pureAccess = true;
373 break;
374 } else {
375 target = targetAccess.receiver;
376 }
377 }
378 if (pureAccess) {
379 // a.m() this.m() a.b.c.d.m()
380 // ^ ^ ^
381 apply(
382 node,
383 node,
384 CodePositionKind.START,
385 sourceInformation,
386 SourcePositionKind.START);
387 } else {
388 // *.m() *.a.b.c.d.m()
389 // ^ ^
390 apply(
391 node,
392 access.selector,
393 CodePositionKind.START,
394 sourceInformation,
395 SourcePositionKind.CLOSING);
396 }
397 } else if (node.target is js.VariableUse) {
398 // m()
399 // ^
400 apply(
401 node,
402 node,
403 CodePositionKind.START,
404 sourceInformation,
405 SourcePositionKind.START);
406 } else if (node.target is js.Fun || node.target is js.New) {
407 // function(){}() new Function("...")()
408 // ^ ^
409 apply(
410 node,
411 node.target,
412 CodePositionKind.END,
413 sourceInformation,
414 SourcePositionKind.CLOSING);
415 } else {
416 assert(invariant(NO_LOCATION_SPANNABLE, false,
417 message: "Unexpected property access ${nodeToString(node)}:\n"
418 "${DebugPrinter.prettyPrint(node)}"));
419 // Don't know....
420 apply(
421 node,
422 node,
423 CodePositionKind.START,
424 sourceInformation,
425 SourcePositionKind.START);
426 }
427 }
428 visitChildren(node);
429 }
430
431 @override
432 void onPositions(js.Node node,
433 int startPosition,
434 int endPosition,
435 int closingPosition) {
436 codePositions.registerPositions(
437 node, startPosition, endPosition, closingPosition);
438 }
439 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/io/line_column_provider.dart ('k') | pkg/compiler/lib/src/io/source_file.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698