Index: tests/compiler/dart2js/sourcemaps/sourcemap_visualizer.dart |
diff --git a/tests/compiler/dart2js/sourcemaps/sourcemap_visualizer.dart b/tests/compiler/dart2js/sourcemaps/sourcemap_visualizer.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..3b6a96dd747c1b855c4ae53e00da249aef4c6863 |
--- /dev/null |
+++ b/tests/compiler/dart2js/sourcemaps/sourcemap_visualizer.dart |
@@ -0,0 +1,194 @@ |
+// Copyright (c) 2017, 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. |
+ |
+/// Tool for visualizing the source mapped parts of a generated JS file. |
+ |
+import 'dart:convert'; |
+import 'dart:io'; |
+import 'package:source_maps/source_maps.dart'; |
+import 'sourcemap_html_helper.dart'; |
+ |
+main(List<String> args) { |
+ String jsFileName = 'out.js'; |
+ if (args.length > 0) { |
+ jsFileName = args[0]; |
+ } |
+ String jsMapFileName = '$jsFileName.map'; |
+ if (args.length > 1) { |
+ jsMapFileName = args[1]; |
+ } |
+ generateHtml(jsFileName, jsMapFileName); |
+} |
+ |
+class MappingState { |
+ final String cssClass; |
+ final int _continuedState; |
+ final int _nextState; |
+ |
+ const MappingState(this.cssClass, this._continuedState, this._nextState); |
+ |
+ MappingState get continuedState => values[_continuedState]; |
+ |
+ MappingState get nextState => values[_nextState]; |
+ |
+ static const MappingState INITIAL = const MappingState('initial', 0, 1); |
+ static const MappingState MAPPED0 = const MappingState('mapped0', 2, 3); |
+ static const MappingState MAPPED0_CONTINUED = |
+ const MappingState('mapped0continued', 2, 3); |
+ static const MappingState MAPPED1 = const MappingState('mapped1', 4, 1); |
+ static const MappingState MAPPED1_CONTINUED = |
+ const MappingState('mapped1continued', 4, 1); |
+ static const MappingState UNMAPPED = const MappingState('unmapped', 5, 1); |
+ |
+ static const List<MappingState> values = const <MappingState>[ |
+ INITIAL, |
+ MAPPED0, |
+ MAPPED0_CONTINUED, |
+ MAPPED1, |
+ MAPPED1_CONTINUED, |
+ UNMAPPED |
+ ]; |
+} |
+ |
+void generateHtml(String jsFileName, String jsMapFileName) { |
+ String jsFile = new File(jsFileName).readAsStringSync(); |
+ String jsMapFile = new File(jsMapFileName).readAsStringSync(); |
+ SingleMapping mapping = new SingleMapping.fromJson(JSON.decode(jsMapFile)); |
+ StringBuffer output = new StringBuffer(); |
+ output.write(''' |
+<html> |
+ <head> |
+ <title>${escape(jsFileName)} / ${escape(jsMapFileName)}</title> |
+ <style type="text/css"> |
+ .initial{ |
+ background-color: #FFFFFF; |
+ } |
+ .mapped0 { |
+ background-color: #E0E0C0; |
+ } |
+ .mapped0continued { |
+ background-color: #F8F8D8; |
+ } |
+ .mapped1 { |
+ background-color: #C0E0E0; |
+ } |
+ .mapped1continued { |
+ background-color: #D8F8F8; |
+ } |
+ .unmapped { |
+ background-color: #E0E0E0; |
+ } |
+ .code { |
+ font-family: monospace; |
+ white-space: pre; |
+ font-size: smaller; |
+ } |
+ .lineNumber { |
+ color: #C0C0C0; |
+ font-size: small; |
+ } |
+ .legend { |
+ position: fixed; |
+ top: 5px; |
+ right: 5px; |
+ border: 1px solid black; |
+ padding: 5px; |
+ background-color: #F0F0F0; |
+ } |
+ .box { |
+ border: 1px solid grey; |
+ } |
+ </style> |
+ </head> |
+ <body> |
+ <div class="legend"> |
+ <span class="initial"> </span> no mapping (yet)<br/> |
+ <span class="mapped0"> </span> |
+ <span class="mapped1"> </span> mapped<br/> |
+ <span class="mapped0continued"> </span> |
+ <span class="mapped1continued"> </span> mapping |
+ continued from previous line<br/> |
+ <span class="unmapped"> </span> mapping off<br/> |
+ </div> |
+ <pre class="code"> |
+'''); |
+ |
+ MappingState state = MappingState.INITIAL; |
+ TargetEntry lastEntry; |
+ |
+ void write(String text, TargetEntry entry) { |
+ output.write('<span class="${state.cssClass}"'); |
+ String prefix = ''; |
+ if (entry == lastEntry) { |
+ prefix = 'continued: '; |
+ } |
+ lastEntry = entry; |
+ if (lastEntry != null) { |
+ if (lastEntry.sourceUrlId != null) { |
+ output.write(' title="$prefix'); |
+ output.write(escape(mapping.urls[lastEntry.sourceUrlId])); |
+ output.write( |
+ ':${lastEntry.sourceLine + 1}:${lastEntry.sourceColumn + 1}'); |
+ if (lastEntry.sourceNameId != null) { |
+ output.write(' ('); |
+ output.write(escape(mapping.names[lastEntry.sourceNameId])); |
+ output.write(')'); |
+ } |
+ output.write('"'); |
+ } else { |
+ output.write(' title="unmapped"'); |
+ } |
+ } |
+ output.write('>'); |
+ output.write(escape(text)); |
+ output.write('</span>'); |
+ } |
+ |
+ int nextTargetLineIndex = 0; |
+ List<String> lines = jsFile.split('\n'); |
+ int lineNoWidth = '${lines.length}'.length; |
+ for (int lineNo = 0; lineNo < lines.length; lineNo++) { |
+ output.write(lineNumber(lineNo, width: lineNoWidth)); |
+ String line = lines[lineNo]; |
+ TargetLineEntry targetLineEntry; |
+ while (nextTargetLineIndex < mapping.lines.length) { |
+ TargetLineEntry entry = mapping.lines[nextTargetLineIndex]; |
+ if (entry.line == lineNo) { |
+ targetLineEntry = entry; |
+ nextTargetLineIndex++; |
+ break; |
+ } else if (entry.line > lineNo) { |
+ break; |
+ } else { |
+ nextTargetLineIndex++; |
+ } |
+ } |
+ if (targetLineEntry != null) { |
+ int columnNo = 0; |
+ for (int index = 0; index < targetLineEntry.entries.length; index++) { |
+ TargetEntry entry = targetLineEntry.entries[index]; |
+ if (entry.column > columnNo) { |
+ write(line.substring(columnNo, entry.column), lastEntry); |
+ columnNo = entry.column; |
+ } |
+ state = |
+ entry.sourceUrlId != null ? state.nextState : MappingState.UNMAPPED; |
+ int end; |
+ if (index + 1 < targetLineEntry.entries.length) { |
+ end = targetLineEntry.entries[index + 1].column; |
+ } else { |
+ end = line.length; |
+ } |
+ write(line.substring(entry.column, end), entry); |
+ columnNo = end; |
+ } |
+ } else { |
+ write(line, lastEntry); |
+ } |
+ output.write('\n'); |
+ state = state.continuedState; |
+ } |
+ output.write('</pre></body></html>'); |
+ new File('out.js.map.html').writeAsStringSync(output.toString()); |
+} |