Description
We are having trouble with the reliability of applying source maps to stack traces. One of the things I'm finding is Mapping.spanFor
sometimes returns null
when I'd have expected to get a specific line in my code. It's possible that the way we are using source_maps
is at fault, so if that's the case please let me know!
Here's a simple example that demonstrates the problem. It renders a single button that throws an exception when clicked. It catches the exception and applies the source map to the exception and then rethrows the exception so we can let Chrome handle it too.
pubspec.yaml:
name: stack_trace_parser
dependencies:
browser: ">=0.10.0 <0.11.0"
source_maps: ^0.10.1
stack_trace: ^1.6.6
transformers:
- $dart2js:
sourceMaps: true
web/index.html:
<html>
<head>
<title>Stack Trace Parsing</title>
</head>
<body>
<script type="application/dart" src="main.dart"></script>
<script src="/packages/browser/dart.js"></script>
</body>
</html>
web/main.dart
import 'dart:async';
import 'dart:html';
import 'package:stack_trace_parser/stack_trace_parser.dart';
Future main() async {
await loadSourceMap();
addElements();
}
void addElements() {
querySelector('body')
..append(new ButtonElement()
..text = 'Throw Exception'
..onClick.listen(_handleExceptionButton));
}
void _handleExceptionButton(_) {
try {
throw new IntegerDivisionByZeroException();
} catch (e, stackTrace) {
print(mapStackTrace(stackTrace));
throw e;
}
}
lib/stack_trace_parser.dart:
import 'dart:async';
import 'dart:html';
import 'package:source_maps/source_maps.dart';
import 'package:stack_trace/stack_trace.dart';
Mapping _sourceMap;
Future loadSourceMap() async {
_sourceMap = parse(await HttpRequest.getString('main.dart.js.map'));
}
StackTrace mapStackTrace(StackTrace stackTrace) {
var frames = new Trace.from(stackTrace).frames.map((Frame frame) {
var span = _sourceMap.spanFor(frame.line - 1, (frame.column ?? 1) - 1);
if (span == null) {
return frame;
}
return new Frame(
span.sourceUrl, span.start.line + 1, span.start.column + 1, span.text);
});
return new Trace(frames);
}
Steps to generate stack traces:
- Open Chrome
- Open the dev tools
- Edit dev tools settings and ensure
Enable JavaScript source maps
is checked pub serve
- Navigate to
localhost:8080
in Chrome - Click the button labeled
Throw Exception
- Switch to the dev tools Console
- You should see the output from us applying the source map followed by Chrome handling the exception and applying the source map itself:
Notice that our output differs on the second line of the stack trace from that of Chrome. In our case Mapping.spanFor
returned null and we just passed the stack trace frame through unchanged. On the other hand, Chrome was able to use the source map to identify a line number in the original source file, main.dart (although the actual line number is off by two 😉 ).
Am I using the source_maps
API correctly? If so, it seems like there might be an issue here in the source map parser.