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

Unified Diff: tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart

Issue 1617083002: Base JavaScript code position computation on JavaScript tracer. (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Updated cf. comments. Created 4 years, 11 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
index 9cb53e566d977cf1e567ef943f6dc7ee9507ba74..123cbe2a918ac1e427c2b116d9d8a5ad7422ab7a 100644
--- a/tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart
+++ b/tests/compiler/dart2js/sourcemaps/sourcemap_helper.dart
@@ -11,8 +11,10 @@ import 'package:compiler/src/null_compiler_output.dart' show NullSink;
import 'package:compiler/src/elements/elements.dart';
import 'package:compiler/src/helpers/helpers.dart';
import 'package:compiler/src/filenames.dart';
+import 'package:compiler/src/io/code_output.dart';
import 'package:compiler/src/io/source_file.dart';
import 'package:compiler/src/io/source_information.dart';
+import 'package:compiler/src/io/position_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';
@@ -21,15 +23,51 @@ import 'package:compiler/src/source_file_provider.dart';
import '../memory_compiler.dart';
import '../output_collector.dart';
+class SourceFileSink implements EventSink<String> {
+ final String filename;
+ StringBuffer sb = new StringBuffer();
+ SourceFile sourceFile;
+
+ SourceFileSink(this.filename);
+
+ @override
+ void add(String event) {
+ sb.write(event);
+ }
+
+ @override
+ void addError(errorEvent, [StackTrace stackTrace]) {
+ // Ignore.
+ }
+
+ @override
+ void close() {
+ sourceFile = new StringSourceFile.fromName(filename, sb.toString());
+ }
+}
+
class OutputProvider implements CompilerOutput {
- BufferedEventSink jsMapOutput;
+ Map<Uri, SourceFileSink> outputMap = <Uri, SourceFileSink>{};
+
+ SourceFile getSourceFile(Uri uri) {
+ SourceFileSink sink = outputMap[uri];
+ if (sink != null) {
+ return sink.sourceFile;
+ }
+ return null;
+ }
+
+ SourceFileSink createSourceFileSink(String name, String extension) {
+ String filename = '$name.$extension';
+ SourceFileSink sink = new SourceFileSink(filename);
+ Uri uri = Uri.parse(filename);
+ outputMap[uri] = sink;
+ return sink;
+ }
@override
EventSink<String> createEventSink(String name, String extension) {
- if (extension == 'js.map') {
- return jsMapOutput = new BufferedEventSink();
- }
- return new NullSink('$name.$extension');
+ return createSourceFileSink(name, extension);
}
}
@@ -42,11 +80,8 @@ class CloningOutputProvider extends OutputProvider {
@override
EventSink<String> createEventSink(String name, String extension) {
EventSink<String> output = outputProvider(name, extension);
- if (extension == 'js.map') {
- output = new CloningEventSink(
- [output, jsMapOutput = new BufferedEventSink()]);
- }
- return output;
+ return new CloningEventSink(
+ [output, createSourceFileSink(name, extension)]);
}
}
@@ -56,17 +91,23 @@ abstract class SourceFileManager {
class ProviderSourceFileManager implements SourceFileManager {
final SourceFileProvider sourceFileProvider;
+ final OutputProvider outputProvider;
- ProviderSourceFileManager(this.sourceFileProvider);
+ ProviderSourceFileManager(this.sourceFileProvider, this.outputProvider);
@override
SourceFile getSourceFile(uri) {
- return sourceFileProvider.getSourceFile(uri);
+ SourceFile sourceFile = sourceFileProvider.getSourceFile(uri);
+ if (sourceFile == null) {
+ sourceFile = outputProvider.getSourceFile(uri);
+ }
+ return sourceFile;
}
}
class RecordingPrintingContext extends LenientPrintingContext {
CodePositionListener listener;
+ Map<js.Node, CodePosition> codePositions = <js.Node, CodePosition>{};
RecordingPrintingContext(this.listener);
@@ -75,11 +116,163 @@ class RecordingPrintingContext extends LenientPrintingContext {
int startPosition,
int endPosition,
int closingPosition) {
+ codePositions[node] =
+ new CodePosition(startPosition, endPosition, closingPosition);
listener.onPositions(
node, startPosition, endPosition, closingPosition);
}
}
+/// A [SourceMapper] that records the source locations on each node.
+class RecordingSourceMapper implements SourceMapper {
+ final SourceMapper sourceMapper;
+ final _LocationRecorder nodeToSourceLocationsMap;
+
+ RecordingSourceMapper(this.sourceMapper, this.nodeToSourceLocationsMap);
+
+ @override
+ void register(js.Node node, int codeOffset, SourceLocation sourceLocation) {
+ nodeToSourceLocationsMap.register(node, codeOffset, sourceLocation);
+ sourceMapper.register(node, codeOffset, sourceLocation);
+ }
+}
+
+/// A wrapper of [SourceInformationProcessor] that records source locations and
+/// code positions.
+class RecordingSourceInformationProcessor
+ implements SourceInformationProcessor {
+ final RecordingSourceInformationStrategy wrapper;
+ final SourceInformationProcessor processor;
+ final CodePositionRecorder codePositions;
+ final LocationMap nodeToSourceLocationsMap;
+
+ RecordingSourceInformationProcessor(
+ this.wrapper,
+ this.processor,
+ this.codePositions,
+ this.nodeToSourceLocationsMap);
+
+ @override
+ void onPositions(js.Node node,
+ int startPosition,
+ int endPosition,
+ int closingPosition) {
+ codePositions.registerPositions(
+ node, startPosition, endPosition, closingPosition);
+ processor.onPositions(node, startPosition, endPosition, closingPosition);
+ }
+
+ @override
+ void process(js.Node node, BufferedCodeOutput code) {
+ processor.process(node, code);
+ wrapper.registerProcess(
+ node, code, codePositions, nodeToSourceLocationsMap);
+ }
+}
+
+/// Information recording for a use of [SourceInformationProcessor].
+class RecordedSourceInformationProcess {
+ final js.Node root;
+ final String code;
+ final CodePositionRecorder codePositions;
+ final LocationMap nodeToSourceLocationsMap;
+
+ RecordedSourceInformationProcess(
+ this.root,
+ this.code,
+ this.codePositions,
+ this.nodeToSourceLocationsMap);
+}
+
+
+/// A wrapper of [JavaScriptSourceInformationStrategy] that records
+/// [RecordedSourceInformationProcess].
+class RecordingSourceInformationStrategy
+ extends JavaScriptSourceInformationStrategy {
+ final JavaScriptSourceInformationStrategy strategy;
+ final Map<RecordedSourceInformationProcess, js.Node> processMap =
+ <RecordedSourceInformationProcess, js.Node>{};
+ final Map<js.Node, RecordedSourceInformationProcess> nodeMap =
+ <js.Node, RecordedSourceInformationProcess>{};
+
+ RecordingSourceInformationStrategy(this.strategy);
+
+ @override
+ SourceInformationBuilder createBuilderForContext(AstElement element) {
+ return strategy.createBuilderForContext(element);
+ }
+
+ @override
+ SourceInformationProcessor createProcessor(SourceMapper sourceMapper) {
+ LocationMap nodeToSourceLocationsMap =
+ new _LocationRecorder();
+ CodePositionRecorder codePositions = new CodePositionRecorder();
+ return new RecordingSourceInformationProcessor(
+ this,
+ strategy.createProcessor(new RecordingSourceMapper(
+ sourceMapper, nodeToSourceLocationsMap)),
+ codePositions, nodeToSourceLocationsMap);
+ }
+
+ void registerProcess(js.Node root,
+ BufferedCodeOutput code,
+ CodePositionRecorder codePositions,
+ LocationMap nodeToSourceLocationsMap) {
+ RecordedSourceInformationProcess subProcess =
+ new RecordedSourceInformationProcess(
+ root, code.getText(), codePositions, nodeToSourceLocationsMap);
+ processMap[subProcess] = root;
+ }
+
+ RecordedSourceInformationProcess subProcessForNode(js.Node node) {
+ return nodeMap.putIfAbsent(node, () {
+ for (RecordedSourceInformationProcess subProcess in processMap.keys) {
+ js.Node root = processMap[subProcess];
+ FindVisitor visitor = new FindVisitor(node);
+ root.accept(visitor);
+ if (visitor.found) {
+ return new RecordedSourceInformationProcess(
+ node,
+ subProcess.code,
+ subProcess.codePositions,
+ new _FilteredLocationMap(
+ visitor.nodes, subProcess.nodeToSourceLocationsMap));
+ }
+ return null;
+ }
+ });
+ }
+}
+
+/// Visitor that collects all nodes that are within a function. Used by the
+/// [RecordingSourceInformationStrategy] to filter what is recorded in a
+/// [RecordedSourceInformationProcess].
+class FindVisitor extends js.BaseVisitor {
+ final js.Node soughtNode;
+ bool found = false;
+ bool add = false;
+ final Set<js.Node> nodes = new Set<js.Node>();
+
+ FindVisitor(this.soughtNode);
+
+ visitNode(js.Node node) {
+ if (node == soughtNode) {
+ found = true;
+ add = true;
+ }
+ if (add) {
+ nodes.add(node);
+ }
+ node.visitChildren(this);
+ if (node == soughtNode) {
+ add = false;
+ }
+ }
+}
+
+const String USE_NEW_SOURCE_INFO = '--use-new-source-info';
+const String DISABLE_INLINING = '--disable-inlining';
+
/// Processor that computes [SourceMapInfo] for the JavaScript compiled for a
/// given Dart file.
class SourceMapProcessor {
@@ -112,11 +305,12 @@ class SourceMapProcessor {
/// Computes the [SourceMapInfo] for the compiled elements.
Future<List<SourceMapInfo>> process(
List<String> options,
- {bool verbose: true}) async {
+ {bool verbose: true,
+ bool perElement: true}) async {
OutputProvider outputProvider = outputToFile
- ? new OutputProvider()
- : new CloningOutputProvider(targetUri, sourceMapFileUri);
- if (options.contains('--use-new-source-info')) {
+ ? new CloningOutputProvider(targetUri, sourceMapFileUri)
+ : new OutputProvider();
+ if (options.contains(USE_NEW_SOURCE_INFO)) {
if (verbose) print('Using the new source information system.');
useNewSourceInfo = true;
}
@@ -125,7 +319,7 @@ class SourceMapProcessor {
// TODO(johnniwinther): Use [verbose] to avoid showing diagnostics.
options: ['--out=$targetUri', '--source-map=$sourceMapFileUri']
..addAll(options));
- if (options.contains('--disable-inlining')) {
+ if (options.contains(DISABLE_INLINING)) {
if (verbose) print('Inlining disabled');
compiler.disableInlining = true;
}
@@ -133,30 +327,58 @@ class SourceMapProcessor {
JavaScriptBackend backend = compiler.backend;
var handler = compiler.handler;
SourceFileProvider sourceFileProvider = handler.provider;
- sourceFileManager = new ProviderSourceFileManager(sourceFileProvider);
+ sourceFileManager = new ProviderSourceFileManager(
+ sourceFileProvider,
+ outputProvider);
+ RecordingSourceInformationStrategy strategy =
+ new RecordingSourceInformationStrategy(backend.sourceInformationStrategy);
+ backend.sourceInformationStrategy = strategy;
await compiler.run(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();
+ if (perElement) {
+ backend.generatedCode.forEach((Element element, js.Expression node) {
+ RecordedSourceInformationProcess subProcess =
+ strategy.subProcessForNode(node);
+ if (subProcess == null) {
+ // TODO(johnniwinther): Find out when this is happening and if it
+ // is benign. (Known to happen for `bool#fromString`)
+ print('No subProcess found for $element');
+ return;
+ }
+ LocationMap nodeMap = subProcess.nodeToSourceLocationsMap;
+ String code = subProcess.code;
+ CodePositionRecorder codePositions = subProcess.codePositions;
+ CodePointComputer visitor =
+ new CodePointComputer(sourceFileManager, code, nodeMap);
+ visitor.apply(node);
+ List<CodePoint> codePoints = visitor.codePoints;
+ infoList.add(new SourceMapInfo(
+ element, code, node,
+ codePoints,
+ codePositions/*strategy.codePositions*/,
+ nodeMap));
+ });
+ } else {
+ // TODO(johnniwinther): Supported multiple output units.
+ RecordedSourceInformationProcess process = strategy.processMap.keys.first;
+ js.Node node = strategy.processMap[process];
+ String code;
+ LocationMap nodeMap;
+ CodePositionRecorder codePositions;
+ nodeMap = process.nodeToSourceLocationsMap;
+ code = process.code;
+ codePositions = process.codePositions;
CodePointComputer visitor =
new CodePointComputer(sourceFileManager, code, nodeMap);
visitor.apply(node);
List<CodePoint> codePoints = visitor.codePoints;
- infoList.add(new SourceMapInfo(element, code, node, codePoints, nodeMap));
- });
+ infoList.add(new SourceMapInfo(
+ null, code, node,
+ codePoints,
+ codePositions,
+ nodeMap));
+ }
return infoList;
}
@@ -167,19 +389,43 @@ class SourceMapInfo {
final String name;
final Element element;
final String code;
- final js.Expression node;
+ final js.Node node;
final List<CodePoint> codePoints;
- final NodeToSourceLocationsMap nodeMap;
+ final CodePositionMap jsCodePositions;
+ final LocationMap nodeMap;
SourceMapInfo(
- Element element, this.code, this.node, this.codePoints, this.nodeMap)
- : this.name = computeElementNameForSourceMaps(element),
+ Element element,
+ this.code,
+ this.node,
+ this.codePoints,
+ this.jsCodePositions,
+ this.nodeMap)
+ : this.name =
+ element != null ? computeElementNameForSourceMaps(element) : '',
this.element = element;
+
+ String toString() {
+ return '$name:$element';
+ }
}
/// Collection of JavaScript nodes with their source mapped target offsets
/// and source locations.
-class NodeToSourceLocationsMap implements SourceMapper {
+abstract class LocationMap {
+ Iterable<js.Node> get nodes;
+
+ Map<int, List<SourceLocation>> operator[] (js.Node node);
+
+ factory LocationMap.recorder() = _LocationRecorder;
+
+ factory LocationMap.filter(Set<js.Node> nodes, LocationMap map) =
+ _FilteredLocationMap;
+
+}
+
+class _LocationRecorder
+ implements SourceMapper, LocationMap {
final Map<js.Node, Map<int, List<SourceLocation>>> _nodeMap = {};
@override
@@ -196,11 +442,25 @@ class NodeToSourceLocationsMap implements SourceMapper {
}
}
+class _FilteredLocationMap implements LocationMap {
+ final Set<js.Node> _nodes;
+ final LocationMap map;
+
+ _FilteredLocationMap(this._nodes, this.map);
+
+ Iterable<js.Node> get nodes => map.nodes.where((n) => _nodes.contains(n));
+
+ Map<int, List<SourceLocation>> operator[] (js.Node node) {
+ return map[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;
+ final LocationMap nodeMap;
List<CodePoint> codePoints = [];
CodePointComputer(this.sourceFileManager, this.code, this.nodeMap);
« no previous file with comments | « tests/compiler/dart2js/sourcemaps/js_tracer.dart ('k') | tests/compiler/dart2js/sourcemaps/sourcemap_html_helper.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698