Skip to content

Commit 1b1740e

Browse files
osa1Commit Queue
authored and
Commit Queue
committed
[dart2wasm] Pass source maps to wasm-opt when optimizing
To be able to know when we are generating a source map, make `dart compile wasm` aware of the `--no-source-maps` flag. The "name" segments of source mappings are also made `null` with this patch. Browsers don't use that segment and binaryen doesn't support it. Change-Id: I7b52c8fb7cef92ed60547e97ad137e0cd3967f26 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/378421 Commit-Queue: Ömer Ağacan <[email protected]> Reviewed-by: Martin Kustermann <[email protected]>
1 parent d71837a commit 1b1740e

File tree

9 files changed

+202
-117
lines changed

9 files changed

+202
-117
lines changed

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,7 @@ abstract class AstCodeGenerator
189189
final location = source.getLocation(fileUri, fileOffset);
190190
final old = _sourceMapFileOffset;
191191
_sourceMapFileOffset = fileOffset;
192-
b.startSourceMapping(fileUri, location.line - 1, location.column - 1,
193-
enclosingMember.name.text);
192+
b.startSourceMapping(fileUri, location.line - 1, location.column - 1, null);
194193
return old;
195194
}
196195

pkg/dart2wasm/tool/compile_benchmark

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ OPT_FLAGS_L4=($(find_flags 'optimizationLevel4Flags'))
5151

5252
RUN_BINARYEN=1
5353
RUN_SRC=0
54+
GENERATE_SOURCE_MAP=1
5455
COMPILE_BENCHMARK_BASE_NAME=""
5556
PLATFORM_FILENAME="$BIN_DIR/dart2wasm_platform.dill"
5657
SNAPSHOT_NAME="dart2wasm"
@@ -130,6 +131,11 @@ while [ $# -gt 0 ]; do
130131
shift
131132
;;
132133

134+
--no-source-maps)
135+
GENERATE_SOURCE_MAP=0
136+
shift
137+
;;
138+
133139
-o)
134140
shift
135141
WASM_FILE="$1"
@@ -158,6 +164,10 @@ while [ $# -gt 0 ]; do
158164
esac
159165
done
160166

167+
if [ $GENERATE_SOURCE_MAP -eq 1 ]; then
168+
BINARYEN_FLAGS+=("-ism" "${WASM_FILE}.map" "-osm" "${WASM_FILE}.map")
169+
fi
170+
161171
if [ -z "$DART_FILE" -o -z "$WASM_FILE" ]; then
162172
echo "Expected <file.dart> <file.wasm>"
163173
exit 1

pkg/dartdev/lib/src/commands/compile.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,11 @@ class CompileWasmCommand extends CompileSubcommandCommand {
707707
},
708708
hide: !verbose,
709709
)
710+
..addFlag(
711+
'no-source-maps',
712+
help: 'Do not generate a source map file.',
713+
negatable: false,
714+
)
710715
..addOption(
711716
packagesOption.flag,
712717
abbr: packagesOption.abbr,
@@ -814,6 +819,7 @@ class CompileWasmCommand extends CompileSubcommandCommand {
814819
if (args.flag('print-wasm')) '--print-wasm',
815820
if (args.flag('print-kernel')) '--print-kernel',
816821
if (args.flag('enable-asserts')) '--enable-asserts',
822+
if (args.flag('no-source-maps')) '--no-source-maps',
817823
for (final define in defines) '-D$define',
818824
if (maxPages != null) ...[
819825
'--import-shared-memory',
@@ -843,14 +849,26 @@ class CompileWasmCommand extends CompileSubcommandCommand {
843849
}
844850

845851
final bool strip = args.flag('strip-wasm');
852+
final bool generateSourceMap = !args.flag('no-source-maps');
846853

847854
if (runWasmOpt) {
848855
final unoptFile = '$outputFileBasename.unopt.wasm';
849856
File(outputFile).renameSync(unoptFile);
850857

858+
final unoptSourceMapFile = '$outputFileBasename.unopt.wasm.map';
859+
if (generateSourceMap) {
860+
File('$outputFile.map').renameSync(unoptSourceMapFile);
861+
}
862+
851863
final flags = [
852864
...binaryenFlags,
853865
if (!strip) '-g',
866+
if (generateSourceMap) ...[
867+
'-ism',
868+
unoptSourceMapFile,
869+
'-osm',
870+
'$outputFile.map'
871+
]
854872
];
855873

856874
if (verbose) {

pkg/wasm_builder/lib/source_map.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,26 @@ String _serializeSourceMap(List<SourceMapping> mappings) {
126126
int lastSourceColumn = 0;
127127
int lastNameIndex = 0;
128128

129+
bool first = true;
130+
129131
for (int i = 0; i < mappings.length; ++i) {
130132
final mapping = mappings[i];
133+
final sourceInfo = mapping.sourceInfo;
134+
135+
if (sourceInfo == null && first) {
136+
// Initial parts of the code will be unmapped my default, we don't need to
137+
// explicitly unmap them. More importantly, current version of binaryen
138+
// cannot handle single-segment mappings at the beginning of the mappings.
139+
// We can remove this block of code after switching to a version with
140+
// https://github.com/WebAssembly/binaryen/pull/6794.
141+
continue;
142+
}
143+
144+
first = false;
131145

132146
lastTargetColumn =
133147
_encodeVLQ(mappingsStr, mapping.instructionOffset, lastTargetColumn);
134148

135-
final sourceInfo = mapping.sourceInfo;
136149
if (sourceInfo != null) {
137150
final sourceIndex = sourceIndices[sourceInfo.fileUri]!;
138151

tests/language/language_dart2wasm.status

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55

66
[ $compiler == dart2wasm ]
77
inference_update_2/why_not_promoted_external_error_test: SkipByDesign # Non-JS-interop external members are not supported
8-
number/separators_test: SkipByDesign # WASM has real integers.
9-
number/web_int_literals_test: SkipByDesign # WASM has real integers.
10-
vm/*: SkipByDesign # Tests for the VM.
8+
number/separators_test: SkipByDesign # Wasm has real integers.
9+
number/web_int_literals_test: SkipByDesign # Wasm has real integers.
10+
vm/*: SkipByDesign # Tests for the VM.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) 2024, 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+
import 'dart:js_interop';
6+
import 'dart:typed_data';
7+
import 'dart:convert';
8+
9+
import 'package:source_maps/parser.dart';
10+
11+
void f() {
12+
g();
13+
}
14+
15+
void g() {
16+
throw 'hi';
17+
}
18+
19+
runtimeFalse() => int.parse('1') == 0;
20+
21+
// `frameDetails` is (line, column) of the frames we check.
22+
//
23+
// Information we don't check are "null": we don't want to check line/column
24+
// of standard library functions to avoid breaking the test with unrelated
25+
// changes to the standard library.
26+
void testMain(String testName, List<(int?, int?)?> frameDetails) {
27+
// Use `f` and `g` in a few places to make sure wasm-opt won't inline them
28+
// in the test.
29+
final fTearOff = f;
30+
final gTearOff = g;
31+
32+
if (runtimeFalse()) f();
33+
if (runtimeFalse()) g();
34+
35+
// Read source map of the current program.
36+
final compilationDir = const String.fromEnvironment("TEST_COMPILATION_DIR");
37+
final sourceMapFileContents =
38+
readfile('$compilationDir/${testName}_test.wasm.map');
39+
final mapping = parse(utf8.decode(sourceMapFileContents)) as SingleMapping;
40+
41+
// Get some simple stack trace.
42+
String? stackTraceString;
43+
try {
44+
f();
45+
} catch (e, st) {
46+
stackTraceString = st.toString();
47+
}
48+
49+
// Print the stack trace to make it easy to update the test.
50+
print("-----");
51+
print(stackTraceString);
52+
print("-----");
53+
54+
final stackTraceLines = stackTraceString!.split('\n');
55+
56+
for (int frameIdx = 0; frameIdx < frameDetails.length; frameIdx += 1) {
57+
final line = stackTraceLines[frameIdx];
58+
final hexOffsetMatch = stackTraceHexOffsetRegExp.firstMatch(line);
59+
if (hexOffsetMatch == null) {
60+
throw "Unable to parse hex offset from stack frame $frameIdx";
61+
}
62+
final hexOffsetStr = hexOffsetMatch.group(1)!; // includes '0x'
63+
final offset = int.tryParse(hexOffsetStr);
64+
if (offset == null) {
65+
throw "Unable to parse hex number in frame $frameIdx: $hexOffsetStr";
66+
}
67+
final span = mapping.spanFor(0, offset);
68+
final frameInfo = frameDetails[frameIdx];
69+
if (frameInfo == null) {
70+
if (span != null) {
71+
throw "Stack frame $frameIdx should not have a source span, but it is mapped: $span";
72+
}
73+
continue;
74+
}
75+
if (span == null) {
76+
print("Stack frame $frameIdx does not have source mapping");
77+
} else {
78+
if (frameInfo.$1 != null) {
79+
if (span.start.line + 1 != frameInfo.$1) {
80+
throw "Stack frame $frameIdx is expected to have line ${frameInfo.$1}, but it has line ${span.start.line + 1}";
81+
}
82+
}
83+
if (frameInfo.$2 != null) {
84+
if (span.start.column + 1 != frameInfo.$2) {
85+
throw "Stack frame $frameIdx is expected to have column ${frameInfo.$2}, but it has column ${span.start.column + 1}";
86+
}
87+
}
88+
}
89+
}
90+
}
91+
92+
/// Read the file at the given [path].
93+
///
94+
/// This relies on the `readbuffer` function provided by d8.
95+
@JS()
96+
external JSArrayBuffer readbuffer(JSString path);
97+
98+
/// Read the file at the given [path].
99+
Uint8List readfile(String path) => Uint8List.view(readbuffer(path.toJS).toDart);
100+
101+
final stackTraceHexOffsetRegExp = RegExp(r'wasm-function.*(0x[0-9a-fA-F]+)\)$');
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) 2024, 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+
// dart2wasmOptions=-O4 --no-strip-wasm --extra-compiler-option=-DTEST_COMPILATION_DIR=$TEST_COMPILATION_DIR
6+
7+
import 'source_map_simple_lib.dart' as Lib;
8+
9+
void main() {
10+
Lib.testMain('source_map_simple_optimized', frameDetails);
11+
}
12+
13+
const List<(int?, int?)?> frameDetails = [
14+
(null, null), // _throwWithCurrentStackTrace
15+
(16, 3), // g
16+
(12, 3), // f
17+
(44, 5), // testMain, inlined in main
18+
(null, null), // _invokeMain
19+
];
20+
21+
/*
22+
at Error._throwWithCurrentStackTrace (wasm://wasm/0008d08e:wasm-function[115]:0xc095)
23+
at g (wasm://wasm/0008d08e:wasm-function[359]:0x11e15)
24+
at f (wasm://wasm/0008d08e:wasm-function[358]:0x11e0b)
25+
at main (wasm://wasm/0008d08e:wasm-function[357]:0x11913)
26+
at _invokeMain (wasm://wasm/0008d08e:wasm-function[82]:0xb349)
27+
at Module.invoke (...)
28+
at main (...)
29+
at async action (...)
30+
*/

0 commit comments

Comments
 (0)