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

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: Update comments. Created 5 years, 6 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 String get shortText {
72 StringBuffer sb = new StringBuffer();
73 if (startPosition != null) {
74 sb.write('${startPosition.sourceUri.pathSegments.last}:');
75 } else {
76 sb.write('${closingPosition.sourceUri.pathSegments.last}:');
77 }
78 // Use 1-based line/column info to match usual dart tool output.
79 if (startPosition != null) {
80 sb.write('[${startPosition.line + 1},'
81 '${startPosition.column + 1}]');
82 }
83 if (closingPosition != null) {
84 sb.write('-[${closingPosition.line + 1},'
85 '${closingPosition.column + 1}]');
86 }
87 return sb.toString();
88 }
89
90 String toString() {
91 StringBuffer sb = new StringBuffer();
92 if (startPosition != null) {
93 sb.write('${startPosition.sourceUri}:');
94 } else {
95 sb.write('${closingPosition.sourceUri}:');
96 }
97 // Use 1-based line/column info to match usual dart tool output.
98 if (startPosition != null) {
99 sb.write('[${startPosition.line + 1},'
100 '${startPosition.column + 1}]');
101 }
102 if (closingPosition != null) {
103 sb.write('-[${closingPosition.line + 1},'
104 '${closingPosition.column + 1}]');
105 }
106 return sb.toString();
107 }
108 }
109
110 class PositionSourceInformationStrategy
111 implements JavaScriptSourceInformationStrategy {
112 const PositionSourceInformationStrategy();
113
114 @override
115 SourceInformationBuilder createBuilderForContext(AstElement element) {
116 return new PositionSourceInformationBuilder(element);
117 }
118
119 @override
120 SourceInformationProcessor createProcessor(SourceMapper mapper) {
121 return new PositionSourceInformationProcessor(mapper);
122 }
123 }
124
125 /// [SourceInformationBuilder] that generates [PositionSourceInformation].
126 class PositionSourceInformationBuilder implements SourceInformationBuilder {
127 final SourceFile sourceFile;
128 final String name;
129
130 PositionSourceInformationBuilder(AstElement element)
131 : sourceFile = element.implementation.compilationUnit.script.file,
132 name = computeElementNameForSourceMaps(element);
133
134 SourceInformation buildDeclaration(AstElement element) {
135 if (element.isSynthesized) {
136 return new PositionSourceInformation(
137 new OffsetSourceLocation(
138 sourceFile, element.position.charOffset, name));
139 } else {
140 return new PositionSourceInformation(
141 null,
floitsch 2015/06/29 08:55:51 why does this not have a start-position?
Johnni Winther 2015/06/29 12:36:29 The start of a function (the signature) is not a s
142 new OffsetSourceLocation(sourceFile,
143 element.resolvedAst.node.getEndToken().charOffset, name));
144 }
145 }
146
147 SourceInformation buildBegin(Node node) {
floitsch 2015/06/29 08:55:51 add documentation.
Johnni Winther 2015/06/29 12:36:28 Done.
148 return new PositionSourceInformation(new OffsetSourceLocation(
149 sourceFile, node.getBeginToken().charOffset, name));
150 }
151
152 @override
153 SourceInformation buildGeneric(Node node) => buildBegin(node);
154
155 @override
156 SourceInformation buildReturn(Node node) => buildBegin(node);
157
158 @override
159 SourceInformation buildImplicitReturn(AstElement element) {
160 if (element.isSynthesized) {
161 return new PositionSourceInformation(
162 new OffsetSourceLocation(
163 sourceFile, element.position.charOffset, name));
164 } else {
165 return new PositionSourceInformation(
166 new OffsetSourceLocation(sourceFile,
167 element.resolvedAst.node.getEndToken().charOffset, name));
168 }
169 }
170
171
172 @override
173 SourceInformation buildLoop(Node node) => buildBegin(node);
174
175 @override
176 SourceInformation buildGet(Node node) => buildBegin(node);
177
178 @override
179 SourceInformation buildCall(Node receiver, Node call) {
180 return new PositionSourceInformation(
181 new OffsetSourceLocation(
182 sourceFile, receiver.getBeginToken().charOffset, name),
183 new OffsetSourceLocation(
184 sourceFile, call.getBeginToken().charOffset, name));
185 }
186
187 @override
188 SourceInformation buildNew(Node node) {
189 return buildBegin(node);
190 }
191
192 @override
193 SourceInformation buildIf(Node node) => buildBegin(node);
194
195 @override
196 SourceInformation buildThrow(Node node) => buildBegin(node);
197
198 @override
199 SourceInformation buildAssignment(Node node) => buildBegin(node);
200
201 @override
202 SourceInformationBuilder forContext(AstElement element) {
203 return new PositionSourceInformationBuilder(element);
204 }
205 }
206
207 /// The start, end and closing offsets for a [js.Node].
208 class CodePosition {
209 final int startPosition;
210 final int endPosition;
211 final int closingPosition;
212
213 CodePosition(this.startPosition, this.endPosition, this.closingPosition);
214 }
215
216 /// Registry for mapping [js.Node]s to their [CodePosition].
217 class CodePositionRecorder {
218 Map<js.Node, CodePosition> _codePositionMap = <js.Node, CodePosition>{};
219
220 void registerPositions(js.Node node,
221 int startPosition,
222 int endPosition,
223 int closingPosition) {
224 registerCodePosition(node,
225 new CodePosition(startPosition, endPosition, closingPosition));
226 }
227
228 void registerCodePosition(js.Node node, CodePosition codePosition) {
229 _codePositionMap[node] = codePosition;
230 }
231
232 CodePosition operator [](js.Node node) => _codePositionMap[node];
233 }
234
235 enum SourcePositionKind {
236 START,
237 CLOSING,
238 END,
239 }
240
241 enum CodePositionKind {
242 START,
243 CLOSING,
244 END,
245 }
246
247 /// Processor that associates [SourceLocation]s from [SourceInformation] on
248 /// [js.Node]s with the target offsets in a [SourceMapper].
249 class PositionSourceInformationProcessor
250 extends js.BaseVisitor implements SourceInformationProcessor {
251 final CodePositionRecorder codePositions = new CodePositionRecorder();
252 final SourceMapper sourceMapper;
253
254 PositionSourceInformationProcessor(this.sourceMapper);
255
256 void process(js.Node node) {
257 node.accept(this);
258 }
259
260 void visitChildren(js.Node node) {
261 node.visitChildren(this);
262 }
263
264 CodePosition getCodePosition(js.Node node) {
265 return codePositions[node];
266 }
267
268 /// Associates [sourceInformation] with [codePosition] for [node] using
269 /// [codePositionKind] and [sourcePositionKind] to pick the offsets and
270 /// source locations.
271 void apply(js.Node node,
272 CodePosition codePosition,
273 CodePositionKind codePositionKind,
274 SourceInformation sourceInformation,
275 SourcePositionKind sourcePositionKind) {
276 if (sourceInformation != null) {
277 // We should always have recorded the needed code positions.
278 assert(codePosition != null);
279 if (codePosition == null) return;
floitsch 2015/06/29 08:55:51 that sounds wrong: first an assert, but then a tes
Johnni Winther 2015/06/29 12:36:29 It _should_ be the case that we have points for th
280 int codeLocation;
281 SourceLocation sourceLocation;
282 switch (codePositionKind) {
283 case CodePositionKind.START:
284 codeLocation = codePosition.startPosition;
285 break;
286 case CodePositionKind.CLOSING:
287 codeLocation = codePosition.closingPosition;
288 break;
289 case CodePositionKind.END:
290 codeLocation = codePosition.endPosition;
291 break;
292 }
293 switch (sourcePositionKind) {
294 case SourcePositionKind.START:
295 sourceLocation = sourceInformation.startPosition;
296 break;
297 case SourcePositionKind.CLOSING:
298 sourceLocation = sourceInformation.closingPosition;
299 break;
300 case SourcePositionKind.END:
301 sourceLocation = sourceInformation.endPosition;
302 break;
303 }
304 if (codeLocation != null && sourceLocation != null) {
305 sourceMapper.register(node, codeLocation, sourceLocation);
306 }
307 }
308 }
309
310 @override
311 visitNode(js.Node node) {
312 SourceInformation sourceInformation = node.sourceInformation;
313 if (sourceInformation != null) {
314 apply(node,
315 getCodePosition(node), CodePositionKind.START,
floitsch 2015/06/29 08:55:51 Add comments when "start" and when "close" is used
316 sourceInformation, SourcePositionKind.START);
317 }
318 visitChildren(node);
319 }
320
321 @override
322 visitFun(js.Fun node) {
323 SourceInformation sourceInformation = node.sourceInformation;
324 if (sourceInformation != null) {
325 apply(node,
326 getCodePosition(node), CodePositionKind.CLOSING,
327 sourceInformation, SourcePositionKind.CLOSING);
328 }
329
330 visitChildren(node);
331 }
332
333 @override
334 visitExpressionStatement(js.ExpressionStatement node) {
335 visitChildren(node);
336 }
337
338 @override
339 visitBinary(js.Binary node) {
340 visitChildren(node);
341 }
342
343 @override
344 visitAccess(js.PropertyAccess node) {
345 visitChildren(node);
346 }
347
348 @override
349 visitCall(js.Call node) {
350 const bool LOG = false;
351 SourceInformation sourceInformation = node.sourceInformation;
352 if (sourceInformation != null) {
353 if (node.target is js.PropertyAccess) {
354 js.PropertyAccess access = node.target;
355 js.Node target = access;
356 bool pureAccess = false;
357 while (target is js.PropertyAccess) {
358 js.PropertyAccess targetAccess = target;
359 if (targetAccess.receiver is js.VariableUse ||
360 targetAccess.receiver is js.This) {
361 pureAccess = true;
362 break;
363 } else {
364 target = targetAccess.receiver;
365 }
366 }
367 if (pureAccess) {
368 // a.m() this.m() a.b.c.d.m()
369 // ^ ^ ^
370 apply(
371 node,
372 getCodePosition(node),
373 CodePositionKind.START,
374 sourceInformation,
375 SourcePositionKind.START);
376 if (LOG) {
floitsch 2015/06/29 08:55:52 I would remove this and the const bool.
Johnni Winther 2015/06/29 12:36:29 Done.
377 print('case 1: `${nodeToString(node)}`');
378 print(DebugPrinter.prettyPrint(node));
379 }
380 } else {
381 // *.m() *.a.b.c.d.m()
382 // ^ ^
383 apply(
384 node,
385 getCodePosition(access.selector),
386 CodePositionKind.START,
387 sourceInformation,
388 SourcePositionKind.CLOSING);
389 }
390 } else if (node.target is js.VariableUse) {
391 // m()
392 // ^
393 apply(
394 node,
395 getCodePosition(node),
396 CodePositionKind.START,
397 sourceInformation,
398 SourcePositionKind.START);
399 } else if (node.target is js.Fun || node.target is js.New) {
400 // function(){}() new Function("...")()
401 // ^ ^
402 apply(
403 node,
404 getCodePosition(node.target),
405 CodePositionKind.END,
406 sourceInformation,
407 SourcePositionKind.CLOSING);
408 } else {
409 assert(invariant(NO_LOCATION_SPANNABLE, false,
410 message: "Unexpected property access ${nodeToString(node)}:\n"
411 "${DebugPrinter.prettyPrint(node)}"));
412 // Don't know....
floitsch 2015/06/29 08:55:51 Given that there is an assert, we should throw an
Johnni Winther 2015/06/29 12:36:29 I don't want compilation to fail because of this (
413 apply(
414 node,
415 getCodePosition(node),
416 CodePositionKind.START,
417 sourceInformation,
418 SourcePositionKind.START);
419 }
420 }
421 visitChildren(node);
422 }
423
424 @override
425 void onPositions(js.Node node,
426 int startPosition,
427 int endPosition,
428 int closingPosition) {
429 codePositions.registerPositions(
430 node, startPosition, endPosition, closingPosition);
floitsch 2015/06/29 08:55:52 extra space.
Johnni Winther 2015/06/29 12:36:29 Done.
431 }
432 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698