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

Side by Side Diff: tests/compiler/dart2js/sourcemaps/sourcemap_helper.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 library sourcemap.helper;
6
7 import 'dart:async';
8 import 'package:compiler/src/apiimpl.dart' as api;
9 import 'package:compiler/src/dart2jslib.dart' show NullSink;
10 import "package:compiler/src/elements/elements.dart";
11 import 'package:compiler/src/filenames.dart';
12 import 'package:compiler/src/io/source_file.dart';
13 import 'package:compiler/src/io/source_information.dart';
14 import 'package:compiler/src/js/js.dart' as js;
15 import 'package:compiler/src/js/js_debug.dart';
16 import 'package:compiler/src/js/js_source_mapping.dart';
17 import 'package:compiler/src/js_backend/js_backend.dart';
18 import 'package:compiler/src/source_file_provider.dart';
19 import '../memory_compiler.dart';
20 import '../output_collector.dart';
21
22 class OutputProvider {
23 BufferedEventSink jsMapOutput;
24
25 EventSink<String> call(String name, String extension) {
26 if (extension == 'js.map') {
27 return jsMapOutput = new BufferedEventSink();
28 }
29 return new NullSink('$name.$extension');
30 }
31 }
32
33 class CloningOutputProvider extends OutputProvider {
34 RandomAccessFileOutputProvider outputProvider;
35
36 CloningOutputProvider(Uri jsUri, Uri jsMapUri)
37 : outputProvider = new RandomAccessFileOutputProvider(jsUri, jsMapUri);
38
39 EventSink<String> call(String name, String extension) {
40 EventSink<String> output = outputProvider(name, extension);
41 if (extension == 'js.map') {
42 output = new CloningEventSink(
43 [output, jsMapOutput = new BufferedEventSink()]);
44 }
45 return output;
46 }
47 }
48
49 abstract class SourceFileManager {
50 SourceFile getSourceFile(var uri);
51 }
52
53 class ProviderSourceFileManager implements SourceFileManager {
54 final SourceFileProvider sourceFileProvider;
55
56 ProviderSourceFileManager(this.sourceFileProvider);
57
58 @override
59 SourceFile getSourceFile(uri) {
60 return sourceFileProvider.getSourceFile(uri);
61 }
62 }
63
64 class RecordingPrintingContext extends LenientPrintingContext {
65 CodePositionListener listener;
66
67 RecordingPrintingContext(this.listener);
68
69 @override
70 void exitNode(js.Node node,
71 int startPosition,
72 int endPosition,
73 int closingPosition) {
74 listener.onPositions(
75 node, startPosition, endPosition, closingPosition);
76 }
77 }
78
79 /// Processor that computes [SourceMapInfo] for the JavaScript compiled for a
80 /// given Dart file.
81 class SourceMapProcessor {
82 /// If `true` the output from the compilation is written to files.
83 final bool outputToFile;
84
85 /// The [Uri] of the Dart entrypoint.
86 Uri inputUri;
87
88 /// The name of the JavaScript output file.
89 String jsPath;
90
91 /// The [Uri] of the JavaScript output file.
92 Uri targetUri;
93
94 /// The [Uri] of the JavaScript source map file.
95 Uri sourceMapFileUri;
96
97 /// The [SourceFileManager] created for the processing.
98 SourceFileManager sourceFileManager;
99
100 /// Creates a processor for the Dart file [filename].
101 SourceMapProcessor(String filename, {this.outputToFile: false}) {
102 inputUri = Uri.base.resolve(nativeToUriPath(filename));
103 jsPath = 'out.js';
104 targetUri = Uri.base.resolve(jsPath);
105 sourceMapFileUri = Uri.base.resolve('${jsPath}.map');
106 }
107
108 /// Computes the [SourceMapInfo] for the compiled elements.
109 Future<List<SourceMapInfo>> process(List<String> options) async {
110 OutputProvider outputProvider = outputToFile
111 ? new OutputProvider()
112 : new CloningOutputProvider(targetUri, sourceMapFileUri);
113 if (options.contains('--use-new-source-info')) {
114 print('Using the new source information system.');
115 useNewSourceInfo = true;
116 }
117 api.Compiler compiler = await compilerFor({},
118 outputProvider: outputProvider,
119 options: ['--out=$targetUri', '--source-map=$sourceMapFileUri']
120 ..addAll(options));
121 if (options.contains('--disable-inlining')) {
122 print('Inlining disabled');
123 compiler.disableInlining = true;
124 }
125
126 JavaScriptBackend backend = compiler.backend;
127 var handler = compiler.handler;
128 SourceFileProvider sourceFileProvider = handler.provider;
129 sourceFileManager = new ProviderSourceFileManager(sourceFileProvider);
130 await compiler.runCompiler(inputUri);
131
132 List<SourceMapInfo> infoList = <SourceMapInfo>[];
133 backend.generatedCode.forEach((Element element, js.Expression node) {
134 js.JavaScriptPrintingOptions options =
135 new js.JavaScriptPrintingOptions();
136 JavaScriptSourceInformationStrategy sourceInformationStrategy =
137 compiler.backend.sourceInformationStrategy;
138 NodeToSourceLocationsMap nodeMap = new NodeToSourceLocationsMap();
139 SourceInformationProcessor sourceInformationProcessor =
140 sourceInformationStrategy.createProcessor(nodeMap);
141 RecordingPrintingContext printingContext =
142 new RecordingPrintingContext(sourceInformationProcessor);
143 new js.Printer(options, printingContext).visit(node);
144 sourceInformationProcessor.process(node);
145
146 String code = printingContext.getText();
147 CodePointComputer visitor =
148 new CodePointComputer(sourceFileManager, code, nodeMap);
149 visitor.apply(node);
150 List<CodePoint> codePoints = visitor.codePoints;
151 infoList.add(new SourceMapInfo(element, code, node, codePoints, nodeMap));
152 });
153
154 return infoList;
155 }
156 }
157
158 /// Source mapping information for the JavaScript code of an [Element].
159 class SourceMapInfo {
160 final String name;
161 final Element element;
162 final String code;
163 final js.Expression node;
164 final List<CodePoint> codePoints;
165 final NodeToSourceLocationsMap nodeMap;
166
167 SourceMapInfo(
168 Element element, this.code, this.node, this.codePoints, this.nodeMap)
169 : this.name = computeElementNameForSourceMaps(element),
170 this.element = element;
171 }
172
173 /// Collection of JavaScript nodes with their source mapped target offsets
174 /// and source locations.
175 class NodeToSourceLocationsMap implements SourceMapper {
176 final Map<js.Node, Map<int, List<SourceLocation>>> _nodeMap = {};
177
178 @override
179 void register(js.Node node, int codeOffset, SourceLocation sourceLocation) {
180 _nodeMap.putIfAbsent(node, () => {})
181 .putIfAbsent(codeOffset, () => [])
182 .add(sourceLocation);
183 }
184
185 Iterable<js.Node> get nodes => _nodeMap.keys;
186
187 Map<int, List<SourceLocation>> operator[] (js.Node node) {
188 return _nodeMap[node];
189 }
190 }
191
192 /// Visitor that computes the [CodePoint]s for source mapping locations.
193 class CodePointComputer extends js.BaseVisitor {
194 final SourceFileManager sourceFileManager;
195 final String code;
196 final NodeToSourceLocationsMap nodeMap;
197 List<CodePoint> codePoints = [];
198
199 CodePointComputer(this.sourceFileManager, this.code, this.nodeMap);
200
201 String nodeToString(js.Node node) {
202 js.JavaScriptPrintingOptions options = new js.JavaScriptPrintingOptions(
203 shouldCompressOutput: true,
204 preferSemicolonToNewlineInMinifiedOutput: true);
205 LenientPrintingContext printingContext = new LenientPrintingContext();
206 new js.Printer(options, printingContext).visit(node);
207 return printingContext.buffer.toString();
208 }
209
210 String positionToString(int position) {
211 String line = code.substring(position);
212 int nl = line.indexOf('\n');
213 if (nl != -1) {
214 line = line.substring(0, nl);
215 }
216 return line;
217 }
218
219 void register(String kind, js.Node node, {bool expectInfo: true}) {
220
221 String dartCodeFromSourceLocation(SourceLocation sourceLocation) {
222 SourceFile sourceFile =
223 sourceFileManager.getSourceFile(sourceLocation.sourceUri);
224 return sourceFile.getLineText(sourceLocation.line)
225 .substring(sourceLocation.column).trim();
226 }
227
228 void addLocation(SourceLocation sourceLocation, String jsCode) {
229 if (sourceLocation == null) {
230 if (expectInfo) {
231 SourceInformation sourceInformation = node.sourceInformation;
232 SourceLocation sourceLocation;
233 String dartCode;
234 if (sourceInformation != null) {
235 sourceLocation = sourceInformation.sourceLocations.first;
236 dartCode = dartCodeFromSourceLocation(sourceLocation);
237 }
238 codePoints.add(new CodePoint(
239 kind, jsCode, sourceLocation, dartCode, isMissing: true));
240 }
241 } else {
242 codePoints.add(new CodePoint(kind, jsCode, sourceLocation,
243 dartCodeFromSourceLocation(sourceLocation)));
244 }
245 }
246
247 Map<int, List<SourceLocation>> locationMap = nodeMap[node];
248 if (locationMap == null) {
249 addLocation(null, nodeToString(node));
250 } else {
251 locationMap.forEach((int targetOffset, List<SourceLocation> locations) {
252 String jsCode = positionToString(targetOffset);
253 for (SourceLocation location in locations) {
254 addLocation(location, jsCode);
255 }
256 });
257 }
258 }
259
260 void apply(js.Node node) {
261 node.accept(this);
262 }
263
264 void visitNode(js.Node node) {
265 register('${node.runtimeType}', node, expectInfo: false);
266 super.visitNode(node);
267 }
268
269 @override
270 void visitNew(js.New node) {
271 node.arguments.forEach(apply);
272 register('New', node);
273 }
274
275 @override
276 void visitReturn(js.Return node) {
277 if (node.value != null) {
278 apply(node.value);
279 }
280 register('Return', node);
281 }
282
283 @override
284 void visitCall(js.Call node) {
285 apply(node.target);
286 node.arguments.forEach(apply);
287 register('Call (${node.target.runtimeType})', node);
288 }
289
290 @override
291 void visitFun(js.Fun node) {
292 node.visitChildren(this);
293 register('Fun', node);
294 }
295
296 @override
297 visitExpressionStatement(js.ExpressionStatement node) {
298 node.visitChildren(this);
299 }
300
301 @override
302 visitBinary(js.Binary node) {
303 node.visitChildren(this);
304 }
305
306 @override
307 visitAccess(js.PropertyAccess node) {
308 node.visitChildren(this);
309 }
310 }
311
312 /// A JavaScript code point and its mapped dart source location.
313 class CodePoint {
314 final String kind;
315 final String jsCode;
316 final SourceLocation sourceLocation;
317 final String dartCode;
318 final bool isMissing;
319
320 CodePoint(
321 this.kind,
322 this.jsCode,
323 this.sourceLocation,
324 this.dartCode,
325 {this.isMissing: false});
326
327 String toString() {
328 return 'CodePoint[kind=$kind,js=$jsCode,dart=$dartCode,'
329 'location=$sourceLocation]';
330 }
331 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698