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

Unified 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, 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 side-by-side diff with in-line comments
Download patch
Index: tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart
diff --git a/tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart b/tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d7aa763be8dc34f44ac9c2243d9688a64552e893
--- /dev/null
+++ b/tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart
@@ -0,0 +1,331 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library sourcemap.helper;
+
+import 'dart:async';
+import 'package:compiler/src/apiimpl.dart' as api;
+import 'package:compiler/src/dart2jslib.dart' show NullSink;
+import "package:compiler/src/elements/elements.dart";
+import 'package:compiler/src/filenames.dart';
+import 'package:compiler/src/io/source_file.dart';
+import 'package:compiler/src/io/source_information.dart';
+import 'package:compiler/src/js/js.dart' as js;
+import 'package:compiler/src/js/js_debug.dart';
+import 'package:compiler/src/js/js_source_mapping.dart';
+import 'package:compiler/src/js_backend/js_backend.dart';
+import 'package:compiler/src/source_file_provider.dart';
+import '../memory_compiler.dart';
+import '../output_collector.dart';
+
+class OutputProvider {
+ BufferedEventSink jsMapOutput;
+
+ EventSink<String> call(String name, String extension) {
+ if (extension == 'js.map') {
+ return jsMapOutput = new BufferedEventSink();
+ }
+ return new NullSink('$name.$extension');
+ }
+}
+
+class CloningOutputProvider extends OutputProvider {
+ RandomAccessFileOutputProvider outputProvider;
+
+ CloningOutputProvider(Uri jsUri, Uri jsMapUri)
+ : outputProvider = new RandomAccessFileOutputProvider(jsUri, jsMapUri);
+
+ EventSink<String> call(String name, String extension) {
+ EventSink<String> output = outputProvider(name, extension);
+ if (extension == 'js.map') {
+ output = new CloningEventSink(
+ [output, jsMapOutput = new BufferedEventSink()]);
+ }
+ return output;
+ }
+}
+
+abstract class SourceFileManager {
+ SourceFile getSourceFile(var uri);
+}
+
+class ProviderSourceFileManager implements SourceFileManager {
+ final SourceFileProvider sourceFileProvider;
+
+ ProviderSourceFileManager(this.sourceFileProvider);
+
+ @override
+ SourceFile getSourceFile(uri) {
+ return sourceFileProvider.getSourceFile(uri);
+ }
+}
+
+class RecordingPrintingContext extends LenientPrintingContext {
+ CodePositionListener listener;
+
+ RecordingPrintingContext(this.listener);
+
+ @override
+ void exitNode(js.Node node,
+ int startPosition,
+ int endPosition,
+ int closingPosition) {
+ listener.onPositions(
+ node, startPosition, endPosition, closingPosition);
+ }
+}
+
+/// Processor that computes [SourceMapInfo] for the JavaScript compiled for a
+/// given Dart file.
+class SourceMapProcessor {
+ /// If `true` the output from the compilation is written to files.
+ final bool outputToFile;
+
+ /// The [Uri] of the Dart entrypoint.
+ Uri inputUri;
+
+ /// The name of the JavaScript output file.
+ String jsPath;
+
+ /// The [Uri] of the JavaScript output file.
+ Uri targetUri;
+
+ /// The [Uri] of the JavaScript source map file.
+ Uri sourceMapFileUri;
+
+ /// The [SourceFileManager] created for the processing.
+ SourceFileManager sourceFileManager;
+
+ /// Creates a processor for the Dart file [filename].
+ SourceMapProcessor(String filename, {this.outputToFile: false}) {
+ inputUri = Uri.base.resolve(nativeToUriPath(filename));
+ jsPath = 'out.js';
+ targetUri = Uri.base.resolve(jsPath);
+ sourceMapFileUri = Uri.base.resolve('${jsPath}.map');
+ }
+
+ /// Computes the [SourceMapInfo] for the compiled elements.
+ Future<List<SourceMapInfo>> process(List<String> options) async {
+ OutputProvider outputProvider = outputToFile
+ ? new OutputProvider()
+ : new CloningOutputProvider(targetUri, sourceMapFileUri);
+ if (options.contains('--use-new-source-info')) {
+ print('Using the new source information system.');
+ useNewSourceInfo = true;
+ }
+ api.Compiler compiler = await compilerFor({},
+ outputProvider: outputProvider,
+ options: ['--out=$targetUri', '--source-map=$sourceMapFileUri']
+ ..addAll(options));
+ if (options.contains('--disable-inlining')) {
+ print('Inlining disabled');
+ compiler.disableInlining = true;
+ }
+
+ JavaScriptBackend backend = compiler.backend;
+ var handler = compiler.handler;
+ SourceFileProvider sourceFileProvider = handler.provider;
+ sourceFileManager = new ProviderSourceFileManager(sourceFileProvider);
+ await compiler.runCompiler(inputUri);
+
+ List<SourceMapInfo> infoList = <SourceMapInfo>[];
+ backend.generatedCode.forEach((Element element, js.Expression node) {
+ js.JavaScriptPrintingOptions options =
+ new js.JavaScriptPrintingOptions();
+ JavaScriptSourceInformationStrategy sourceInformationStrategy =
+ compiler.backend.sourceInformationStrategy;
+ NodeToSourceLocationsMap nodeMap = new NodeToSourceLocationsMap();
+ SourceInformationProcessor sourceInformationProcessor =
+ sourceInformationStrategy.createProcessor(nodeMap);
+ RecordingPrintingContext printingContext =
+ new RecordingPrintingContext(sourceInformationProcessor);
+ new js.Printer(options, printingContext).visit(node);
+ sourceInformationProcessor.process(node);
+
+ String code = printingContext.getText();
+ CodePointComputer visitor =
+ new CodePointComputer(sourceFileManager, code, nodeMap);
+ visitor.apply(node);
+ List<CodePoint> codePoints = visitor.codePoints;
+ infoList.add(new SourceMapInfo(element, code, node, codePoints, nodeMap));
+ });
+
+ return infoList;
+ }
+}
+
+/// Source mapping information for the JavaScript code of an [Element].
+class SourceMapInfo {
+ final String name;
+ final Element element;
+ final String code;
+ final js.Expression node;
+ final List<CodePoint> codePoints;
+ final NodeToSourceLocationsMap nodeMap;
+
+ SourceMapInfo(
+ Element element, this.code, this.node, this.codePoints, this.nodeMap)
+ : this.name = computeElementNameForSourceMaps(element),
+ this.element = element;
+}
+
+/// Collection of JavaScript nodes with their source mapped target offsets
+/// and source locations.
+class NodeToSourceLocationsMap implements SourceMapper {
+ final Map<js.Node, Map<int, List<SourceLocation>>> _nodeMap = {};
+
+ @override
+ void register(js.Node node, int codeOffset, SourceLocation sourceLocation) {
+ _nodeMap.putIfAbsent(node, () => {})
+ .putIfAbsent(codeOffset, () => [])
+ .add(sourceLocation);
+ }
+
+ Iterable<js.Node> get nodes => _nodeMap.keys;
+
+ Map<int, List<SourceLocation>> operator[] (js.Node node) {
+ return _nodeMap[node];
+ }
+}
+
+/// Visitor that computes the [CodePoint]s for source mapping locations.
+class CodePointComputer extends js.BaseVisitor {
+ final SourceFileManager sourceFileManager;
+ final String code;
+ final NodeToSourceLocationsMap nodeMap;
+ List<CodePoint> codePoints = [];
+
+ CodePointComputer(this.sourceFileManager, this.code, this.nodeMap);
+
+ String nodeToString(js.Node node) {
+ js.JavaScriptPrintingOptions options = new js.JavaScriptPrintingOptions(
+ shouldCompressOutput: true,
+ preferSemicolonToNewlineInMinifiedOutput: true);
+ LenientPrintingContext printingContext = new LenientPrintingContext();
+ new js.Printer(options, printingContext).visit(node);
+ return printingContext.buffer.toString();
+ }
+
+ String positionToString(int position) {
+ String line = code.substring(position);
+ int nl = line.indexOf('\n');
+ if (nl != -1) {
+ line = line.substring(0, nl);
+ }
+ return line;
+ }
+
+ void register(String kind, js.Node node, {bool expectInfo: true}) {
+
+ String dartCodeFromSourceLocation(SourceLocation sourceLocation) {
+ SourceFile sourceFile =
+ sourceFileManager.getSourceFile(sourceLocation.sourceUri);
+ return sourceFile.getLineText(sourceLocation.line)
+ .substring(sourceLocation.column).trim();
+ }
+
+ void addLocation(SourceLocation sourceLocation, String jsCode) {
+ if (sourceLocation == null) {
+ if (expectInfo) {
+ SourceInformation sourceInformation = node.sourceInformation;
+ SourceLocation sourceLocation;
+ String dartCode;
+ if (sourceInformation != null) {
+ sourceLocation = sourceInformation.sourceLocations.first;
+ dartCode = dartCodeFromSourceLocation(sourceLocation);
+ }
+ codePoints.add(new CodePoint(
+ kind, jsCode, sourceLocation, dartCode, isMissing: true));
+ }
+ } else {
+ codePoints.add(new CodePoint(kind, jsCode, sourceLocation,
+ dartCodeFromSourceLocation(sourceLocation)));
+ }
+ }
+
+ Map<int, List<SourceLocation>> locationMap = nodeMap[node];
+ if (locationMap == null) {
+ addLocation(null, nodeToString(node));
+ } else {
+ locationMap.forEach((int targetOffset, List<SourceLocation> locations) {
+ String jsCode = positionToString(targetOffset);
+ for (SourceLocation location in locations) {
+ addLocation(location, jsCode);
+ }
+ });
+ }
+ }
+
+ void apply(js.Node node) {
+ node.accept(this);
+ }
+
+ void visitNode(js.Node node) {
+ register('${node.runtimeType}', node, expectInfo: false);
+ super.visitNode(node);
+ }
+
+ @override
+ void visitNew(js.New node) {
+ node.arguments.forEach(apply);
+ register('New', node);
+ }
+
+ @override
+ void visitReturn(js.Return node) {
+ if (node.value != null) {
+ apply(node.value);
+ }
+ register('Return', node);
+ }
+
+ @override
+ void visitCall(js.Call node) {
+ apply(node.target);
+ node.arguments.forEach(apply);
+ register('Call (${node.target.runtimeType})', node);
+ }
+
+ @override
+ void visitFun(js.Fun node) {
+ node.visitChildren(this);
+ register('Fun', node);
+ }
+
+ @override
+ visitExpressionStatement(js.ExpressionStatement node) {
+ node.visitChildren(this);
+ }
+
+ @override
+ visitBinary(js.Binary node) {
+ node.visitChildren(this);
+ }
+
+ @override
+ visitAccess(js.PropertyAccess node) {
+ node.visitChildren(this);
+ }
+}
+
+/// A JavaScript code point and its mapped dart source location.
+class CodePoint {
+ final String kind;
+ final String jsCode;
+ final SourceLocation sourceLocation;
+ final String dartCode;
+ final bool isMissing;
+
+ CodePoint(
+ this.kind,
+ this.jsCode,
+ this.sourceLocation,
+ this.dartCode,
+ {this.isMissing: false});
+
+ String toString() {
+ return 'CodePoint[kind=$kind,js=$jsCode,dart=$dartCode,'
+ 'location=$sourceLocation]';
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698