Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 USE_NEW_SOURCE_INFO = true; | |
|
floitsch
2015/06/29 08:55:52
If we can assign to it, it's not a constant and sh
Johnni Winther
2015/06/29 12:36:30
Done.
| |
| 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 } | |
| OLD | NEW |