| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library sourcemap.helper; | 5 library sourcemap.helper; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; |
| 8 import 'package:compiler/compiler_new.dart'; | 9 import 'package:compiler/compiler_new.dart'; |
| 9 import 'package:compiler/src/apiimpl.dart' as api; | 10 import 'package:compiler/src/apiimpl.dart' as api; |
| 10 import 'package:compiler/src/null_compiler_output.dart' show NullSink; | 11 import 'package:compiler/src/null_compiler_output.dart' show NullSink; |
| 11 import 'package:compiler/src/elements/elements.dart'; | 12 import 'package:compiler/src/elements/elements.dart'; |
| 12 import 'package:compiler/src/helpers/helpers.dart'; | 13 import 'package:compiler/src/helpers/helpers.dart'; |
| 13 import 'package:compiler/src/filenames.dart'; | 14 import 'package:compiler/src/filenames.dart'; |
| 14 import 'package:compiler/src/io/code_output.dart'; | 15 import 'package:compiler/src/io/code_output.dart'; |
| 15 import 'package:compiler/src/io/source_file.dart'; | 16 import 'package:compiler/src/io/source_file.dart'; |
| 16 import 'package:compiler/src/io/source_information.dart'; | 17 import 'package:compiler/src/io/source_information.dart'; |
| 17 import 'package:compiler/src/io/position_information.dart'; | 18 import 'package:compiler/src/io/position_information.dart'; |
| (...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 296 | 297 |
| 297 /// Creates a processor for the Dart file [filename]. | 298 /// Creates a processor for the Dart file [filename]. |
| 298 SourceMapProcessor(String filename, {this.outputToFile: false}) { | 299 SourceMapProcessor(String filename, {this.outputToFile: false}) { |
| 299 inputUri = Uri.base.resolve(nativeToUriPath(filename)); | 300 inputUri = Uri.base.resolve(nativeToUriPath(filename)); |
| 300 jsPath = 'out.js'; | 301 jsPath = 'out.js'; |
| 301 targetUri = Uri.base.resolve(jsPath); | 302 targetUri = Uri.base.resolve(jsPath); |
| 302 sourceMapFileUri = Uri.base.resolve('${jsPath}.map'); | 303 sourceMapFileUri = Uri.base.resolve('${jsPath}.map'); |
| 303 } | 304 } |
| 304 | 305 |
| 305 /// Computes the [SourceMapInfo] for the compiled elements. | 306 /// Computes the [SourceMapInfo] for the compiled elements. |
| 306 Future<List<SourceMapInfo>> process( | 307 Future<SourceMaps> process( |
| 307 List<String> options, | 308 List<String> options, |
| 308 {bool verbose: true, | 309 {bool verbose: true, |
| 309 bool perElement: true}) async { | 310 bool perElement: true, |
| 311 bool forMain: false}) async { |
| 310 OutputProvider outputProvider = outputToFile | 312 OutputProvider outputProvider = outputToFile |
| 311 ? new CloningOutputProvider(targetUri, sourceMapFileUri) | 313 ? new CloningOutputProvider(targetUri, sourceMapFileUri) |
| 312 : new OutputProvider(); | 314 : new OutputProvider(); |
| 313 if (options.contains(USE_NEW_SOURCE_INFO)) { | 315 if (options.contains(USE_NEW_SOURCE_INFO)) { |
| 314 if (verbose) print('Using the new source information system.'); | 316 if (verbose) print('Using the new source information system.'); |
| 315 useNewSourceInfo = true; | 317 useNewSourceInfo = true; |
| 316 } | 318 } |
| 317 api.CompilerImpl compiler = await compilerFor( | 319 api.CompilerImpl compiler = await compilerFor( |
| 318 outputProvider: outputProvider, | 320 outputProvider: outputProvider, |
| 319 // TODO(johnniwinther): Use [verbose] to avoid showing diagnostics. | 321 // TODO(johnniwinther): Use [verbose] to avoid showing diagnostics. |
| 320 options: ['--out=$targetUri', '--source-map=$sourceMapFileUri'] | 322 options: ['--out=$targetUri', '--source-map=$sourceMapFileUri'] |
| 321 ..addAll(options)); | 323 ..addAll(options)); |
| 322 if (options.contains(DISABLE_INLINING)) { | 324 if (options.contains(DISABLE_INLINING)) { |
| 323 if (verbose) print('Inlining disabled'); | 325 if (verbose) print('Inlining disabled'); |
| 324 compiler.disableInlining = true; | 326 compiler.disableInlining = true; |
| 325 } | 327 } |
| 326 | 328 |
| 327 JavaScriptBackend backend = compiler.backend; | 329 JavaScriptBackend backend = compiler.backend; |
| 328 var handler = compiler.handler; | 330 var handler = compiler.handler; |
| 329 SourceFileProvider sourceFileProvider = handler.provider; | 331 SourceFileProvider sourceFileProvider = handler.provider; |
| 330 sourceFileManager = new ProviderSourceFileManager( | 332 sourceFileManager = new ProviderSourceFileManager( |
| 331 sourceFileProvider, | 333 sourceFileProvider, |
| 332 outputProvider); | 334 outputProvider); |
| 333 RecordingSourceInformationStrategy strategy = | 335 RecordingSourceInformationStrategy strategy = |
| 334 new RecordingSourceInformationStrategy(backend.sourceInformationStrategy
); | 336 new RecordingSourceInformationStrategy(backend.sourceInformationStrategy
); |
| 335 backend.sourceInformationStrategy = strategy; | 337 backend.sourceInformationStrategy = strategy; |
| 336 await compiler.run(inputUri); | 338 await compiler.run(inputUri); |
| 337 | 339 |
| 338 List<SourceMapInfo> infoList = <SourceMapInfo>[]; | 340 SourceMapInfo mainSourceMapInfo; |
| 341 Map<Element, SourceMapInfo> elementSourceMapInfos = |
| 342 <Element, SourceMapInfo>{}; |
| 339 if (perElement) { | 343 if (perElement) { |
| 340 backend.generatedCode.forEach((Element element, js.Expression node) { | 344 backend.generatedCode.forEach((Element element, js.Expression node) { |
| 341 RecordedSourceInformationProcess subProcess = | 345 RecordedSourceInformationProcess subProcess = |
| 342 strategy.subProcessForNode(node); | 346 strategy.subProcessForNode(node); |
| 343 if (subProcess == null) { | 347 if (subProcess == null) { |
| 344 // TODO(johnniwinther): Find out when this is happening and if it | 348 // TODO(johnniwinther): Find out when this is happening and if it |
| 345 // is benign. (Known to happen for `bool#fromString`) | 349 // is benign. (Known to happen for `bool#fromString`) |
| 346 print('No subProcess found for $element'); | 350 print('No subProcess found for $element'); |
| 347 return; | 351 return; |
| 348 } | 352 } |
| 349 LocationMap nodeMap = subProcess.nodeToSourceLocationsMap; | 353 LocationMap nodeMap = subProcess.nodeToSourceLocationsMap; |
| 350 String code = subProcess.code; | 354 String code = subProcess.code; |
| 351 CodePositionRecorder codePositions = subProcess.codePositions; | 355 CodePositionRecorder codePositions = subProcess.codePositions; |
| 352 CodePointComputer visitor = | 356 CodePointComputer visitor = |
| 353 new CodePointComputer(sourceFileManager, code, nodeMap); | 357 new CodePointComputer(sourceFileManager, code, nodeMap); |
| 354 visitor.apply(node); | 358 new JavaScriptTracer(codePositions, [visitor]).apply(node); |
| 355 List<CodePoint> codePoints = visitor.codePoints; | 359 List<CodePoint> codePoints = visitor.codePoints; |
| 356 infoList.add(new SourceMapInfo( | 360 elementSourceMapInfos[element] = new SourceMapInfo( |
| 357 element, code, node, | 361 element, |
| 362 code, |
| 363 node, |
| 358 codePoints, | 364 codePoints, |
| 359 codePositions/*strategy.codePositions*/, | 365 codePositions, |
| 360 nodeMap)); | 366 nodeMap); |
| 361 }); | 367 }); |
| 362 } else { | 368 } |
| 369 if (forMain) { |
| 363 // TODO(johnniwinther): Supported multiple output units. | 370 // TODO(johnniwinther): Supported multiple output units. |
| 364 RecordedSourceInformationProcess process = strategy.processMap.keys.first; | 371 RecordedSourceInformationProcess process = strategy.processMap.keys.first; |
| 365 js.Node node = strategy.processMap[process]; | 372 js.Node node = strategy.processMap[process]; |
| 366 String code; | 373 String code; |
| 367 LocationMap nodeMap; | 374 LocationMap nodeMap; |
| 368 CodePositionRecorder codePositions; | 375 CodePositionRecorder codePositions; |
| 369 nodeMap = process.nodeToSourceLocationsMap; | 376 nodeMap = process.nodeToSourceLocationsMap; |
| 370 code = process.code; | 377 code = process.code; |
| 371 codePositions = process.codePositions; | 378 codePositions = process.codePositions; |
| 372 CodePointComputer visitor = | 379 CodePointComputer visitor = |
| 373 new CodePointComputer(sourceFileManager, code, nodeMap); | 380 new CodePointComputer(sourceFileManager, code, nodeMap); |
| 374 visitor.apply(node); | 381 new JavaScriptTracer(codePositions, [visitor]).apply(node); |
| 375 List<CodePoint> codePoints = visitor.codePoints; | 382 List<CodePoint> codePoints = visitor.codePoints; |
| 376 infoList.add(new SourceMapInfo( | 383 mainSourceMapInfo = new SourceMapInfo( |
| 377 null, code, node, | 384 null, code, node, |
| 378 codePoints, | 385 codePoints, |
| 379 codePositions, | 386 codePositions, |
| 380 nodeMap)); | 387 nodeMap); |
| 381 } | 388 } |
| 382 | 389 |
| 383 return infoList; | 390 return new SourceMaps( |
| 391 sourceFileManager, mainSourceMapInfo, elementSourceMapInfos); |
| 384 } | 392 } |
| 385 } | 393 } |
| 386 | 394 |
| 395 class SourceMaps { |
| 396 final SourceFileManager sourceFileManager; |
| 397 // TODO(johnniwinther): Supported multiple output units. |
| 398 final SourceMapInfo mainSourceMapInfo; |
| 399 final Map<Element, SourceMapInfo> elementSourceMapInfos; |
| 400 |
| 401 SourceMaps( |
| 402 this.sourceFileManager, |
| 403 this.mainSourceMapInfo, |
| 404 this.elementSourceMapInfos); |
| 405 } |
| 406 |
| 387 /// Source mapping information for the JavaScript code of an [Element]. | 407 /// Source mapping information for the JavaScript code of an [Element]. |
| 388 class SourceMapInfo { | 408 class SourceMapInfo { |
| 389 final String name; | 409 final String name; |
| 390 final Element element; | 410 final Element element; |
| 391 final String code; | 411 final String code; |
| 392 final js.Node node; | 412 final js.Node node; |
| 393 final List<CodePoint> codePoints; | 413 final List<CodePoint> codePoints; |
| 394 final CodePositionMap jsCodePositions; | 414 final CodePositionMap jsCodePositions; |
| 395 final LocationMap nodeMap; | 415 final LocationMap nodeMap; |
| 396 | 416 |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 450 | 470 |
| 451 Iterable<js.Node> get nodes => map.nodes.where((n) => _nodes.contains(n)); | 471 Iterable<js.Node> get nodes => map.nodes.where((n) => _nodes.contains(n)); |
| 452 | 472 |
| 453 Map<int, List<SourceLocation>> operator[] (js.Node node) { | 473 Map<int, List<SourceLocation>> operator[] (js.Node node) { |
| 454 return map[node]; | 474 return map[node]; |
| 455 } | 475 } |
| 456 } | 476 } |
| 457 | 477 |
| 458 | 478 |
| 459 /// Visitor that computes the [CodePoint]s for source mapping locations. | 479 /// Visitor that computes the [CodePoint]s for source mapping locations. |
| 460 class CodePointComputer extends js.BaseVisitor { | 480 class CodePointComputer extends TraceListener { |
| 461 final SourceFileManager sourceFileManager; | 481 final SourceFileManager sourceFileManager; |
| 462 final String code; | 482 final String code; |
| 463 final LocationMap nodeMap; | 483 final LocationMap nodeMap; |
| 464 List<CodePoint> codePoints = []; | 484 List<CodePoint> codePoints = []; |
| 465 | 485 |
| 466 CodePointComputer(this.sourceFileManager, this.code, this.nodeMap); | 486 CodePointComputer(this.sourceFileManager, this.code, this.nodeMap); |
| 467 | 487 |
| 468 String nodeToString(js.Node node) { | 488 String nodeToString(js.Node node) { |
| 469 js.JavaScriptPrintingOptions options = new js.JavaScriptPrintingOptions( | 489 js.JavaScriptPrintingOptions options = new js.JavaScriptPrintingOptions( |
| 470 shouldCompressOutput: true, | 490 shouldCompressOutput: true, |
| 471 preferSemicolonToNewlineInMinifiedOutput: true); | 491 preferSemicolonToNewlineInMinifiedOutput: true); |
| 472 LenientPrintingContext printingContext = new LenientPrintingContext(); | 492 LenientPrintingContext printingContext = new LenientPrintingContext(); |
| 473 new js.Printer(options, printingContext).visit(node); | 493 new js.Printer(options, printingContext).visit(node); |
| 474 return printingContext.buffer.toString(); | 494 return printingContext.buffer.toString(); |
| 475 } | 495 } |
| 476 | 496 |
| 477 String positionToString(int position) { | 497 String positionToString(int position) { |
| 478 String line = code.substring(position); | 498 String line = code.substring(position); |
| 479 int nl = line.indexOf('\n'); | 499 int nl = line.indexOf('\n'); |
| 480 if (nl != -1) { | 500 if (nl != -1) { |
| 481 line = line.substring(0, nl); | 501 line = line.substring(0, nl); |
| 482 } | 502 } |
| 483 return line; | 503 return line; |
| 484 } | 504 } |
| 485 | 505 |
| 506 /// Called when [node] defines a step of the given [kind] at the given |
| 507 /// [offset] when the generated JavaScript code. |
| 508 void onStep(js.Node node, Offset offset, StepKind kind) { |
| 509 register('$kind', node); |
| 510 } |
| 511 |
| 486 void register(String kind, js.Node node, {bool expectInfo: true}) { | 512 void register(String kind, js.Node node, {bool expectInfo: true}) { |
| 487 | 513 |
| 488 String dartCodeFromSourceLocation(SourceLocation sourceLocation) { | 514 String dartCodeFromSourceLocation(SourceLocation sourceLocation) { |
| 489 SourceFile sourceFile = | 515 SourceFile sourceFile = |
| 490 sourceFileManager.getSourceFile(sourceLocation.sourceUri); | 516 sourceFileManager.getSourceFile(sourceLocation.sourceUri); |
| 517 if (sourceFile == null) { |
| 518 return sourceLocation.shortText; |
| 519 } |
| 491 return sourceFile.getLineText(sourceLocation.line) | 520 return sourceFile.getLineText(sourceLocation.line) |
| 492 .substring(sourceLocation.column).trim(); | 521 .substring(sourceLocation.column).trim(); |
| 493 } | 522 } |
| 494 | 523 |
| 495 void addLocation(SourceLocation sourceLocation, String jsCode) { | 524 void addLocation(SourceLocation sourceLocation, String jsCode) { |
| 496 if (sourceLocation == null) { | 525 if (sourceLocation == null) { |
| 497 if (expectInfo) { | 526 if (expectInfo) { |
| 498 SourceInformation sourceInformation = node.sourceInformation; | 527 SourceInformation sourceInformation = node.sourceInformation; |
| 499 SourceLocation sourceLocation; | 528 SourceLocation sourceLocation; |
| 500 String dartCode; | 529 String dartCode; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 516 addLocation(null, nodeToString(node)); | 545 addLocation(null, nodeToString(node)); |
| 517 } else { | 546 } else { |
| 518 locationMap.forEach((int targetOffset, List<SourceLocation> locations) { | 547 locationMap.forEach((int targetOffset, List<SourceLocation> locations) { |
| 519 String jsCode = nodeToString(node); | 548 String jsCode = nodeToString(node); |
| 520 for (SourceLocation location in locations) { | 549 for (SourceLocation location in locations) { |
| 521 addLocation(location, jsCode); | 550 addLocation(location, jsCode); |
| 522 } | 551 } |
| 523 }); | 552 }); |
| 524 } | 553 } |
| 525 } | 554 } |
| 526 | |
| 527 void apply(js.Node node) { | |
| 528 node.accept(this); | |
| 529 } | |
| 530 | |
| 531 void visitNode(js.Node node) { | |
| 532 register('${node.runtimeType}', node, expectInfo: false); | |
| 533 super.visitNode(node); | |
| 534 } | |
| 535 | |
| 536 @override | |
| 537 void visitNew(js.New node) { | |
| 538 node.arguments.forEach(apply); | |
| 539 register('New', node); | |
| 540 } | |
| 541 | |
| 542 @override | |
| 543 void visitReturn(js.Return node) { | |
| 544 if (node.value != null) { | |
| 545 apply(node.value); | |
| 546 } | |
| 547 register('Return', node); | |
| 548 } | |
| 549 | |
| 550 @override | |
| 551 void visitCall(js.Call node) { | |
| 552 apply(node.target); | |
| 553 node.arguments.forEach(apply); | |
| 554 register('Call (${node.target.runtimeType})', node); | |
| 555 } | |
| 556 | |
| 557 @override | |
| 558 void visitFun(js.Fun node) { | |
| 559 node.visitChildren(this); | |
| 560 register('Fun', node); | |
| 561 } | |
| 562 | |
| 563 @override | |
| 564 visitExpressionStatement(js.ExpressionStatement node) { | |
| 565 node.visitChildren(this); | |
| 566 } | |
| 567 | |
| 568 @override | |
| 569 visitBinary(js.Binary node) { | |
| 570 node.visitChildren(this); | |
| 571 } | |
| 572 | |
| 573 @override | |
| 574 visitAccess(js.PropertyAccess node) { | |
| 575 node.visitChildren(this); | |
| 576 } | |
| 577 } | 555 } |
| 578 | 556 |
| 579 /// A JavaScript code point and its mapped dart source location. | 557 /// A JavaScript code point and its mapped dart source location. |
| 580 class CodePoint { | 558 class CodePoint { |
| 581 final String kind; | 559 final String kind; |
| 582 final String jsCode; | 560 final String jsCode; |
| 583 final SourceLocation sourceLocation; | 561 final SourceLocation sourceLocation; |
| 584 final String dartCode; | 562 final String dartCode; |
| 585 final bool isMissing; | 563 final bool isMissing; |
| 586 | 564 |
| 587 CodePoint( | 565 CodePoint( |
| 588 this.kind, | 566 this.kind, |
| 589 this.jsCode, | 567 this.jsCode, |
| 590 this.sourceLocation, | 568 this.sourceLocation, |
| 591 this.dartCode, | 569 this.dartCode, |
| 592 {this.isMissing: false}); | 570 {this.isMissing: false}); |
| 593 | 571 |
| 594 String toString() { | 572 String toString() { |
| 595 return 'CodePoint[kind=$kind,js=$jsCode,dart=$dartCode,' | 573 return 'CodePoint[kind=$kind,js=$jsCode,dart=$dartCode,' |
| 596 'location=$sourceLocation]'; | 574 'location=$sourceLocation]'; |
| 597 } | 575 } |
| 598 } | 576 } |
| 577 |
| 578 class IOSourceFileManager implements SourceFileManager { |
| 579 final Uri base; |
| 580 |
| 581 Map<Uri, SourceFile> sourceFiles = <Uri, SourceFile>{}; |
| 582 |
| 583 IOSourceFileManager(this.base); |
| 584 |
| 585 SourceFile getSourceFile(var uri) { |
| 586 Uri absoluteUri; |
| 587 if (uri is Uri) { |
| 588 absoluteUri = base.resolveUri(uri); |
| 589 } else { |
| 590 absoluteUri = base.resolve(uri); |
| 591 } |
| 592 return sourceFiles.putIfAbsent(absoluteUri, () { |
| 593 String text = new File.fromUri(absoluteUri).readAsStringSync(); |
| 594 return new StringSourceFile.fromUri(absoluteUri, text); |
| 595 }); |
| 596 } |
| 597 } |
| OLD | NEW |