Skip to content

Commit d32231a

Browse files
authored
Merge pull request #291 from dart-lang/merge-source_map_stack_trace-package
Merge `source_map_stack_trace` package
2 parents 5b15f8b + 5b2a616 commit d32231a

File tree

12 files changed

+641
-0
lines changed

12 files changed

+641
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Dart CI
2+
3+
on:
4+
# Run on PRs and pushes to the default branch.
5+
push:
6+
branches: [ main ]
7+
paths:
8+
- '.github/workflows/source_map_stack_trace.yml'
9+
- 'pkgs/source_map_stack_trace/**'
10+
pull_request:
11+
branches: [ main ]
12+
paths:
13+
- '.github/workflows/source_map_stack_trace.yml'
14+
- 'pkgs/source_map_stack_trace/**'
15+
schedule:
16+
- cron: "0 0 * * 0"
17+
18+
env:
19+
PUB_ENVIRONMENT: bot.github
20+
21+
jobs:
22+
# Check code formatting and static analysis on a single OS (linux)
23+
# against Dart dev.
24+
analyze:
25+
runs-on: ubuntu-latest
26+
defaults:
27+
run:
28+
working-directory: pkgs/source_map_stack_trace/
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
sdk: [dev]
33+
steps:
34+
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
35+
- uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672
36+
with:
37+
sdk: ${{ matrix.sdk }}
38+
- id: install
39+
name: Install dependencies
40+
run: dart pub get
41+
- name: Check formatting
42+
run: dart format --output=none --set-exit-if-changed .
43+
if: always() && steps.install.outcome == 'success'
44+
- name: Analyze code
45+
run: dart analyze --fatal-infos
46+
if: always() && steps.install.outcome == 'success'
47+
48+
# Run tests on a matrix consisting of two dimensions:
49+
# 1. OS: ubuntu-latest, (macos-latest, windows-latest)
50+
# 2. release channel: dev
51+
test:
52+
needs: analyze
53+
runs-on: ${{ matrix.os }}
54+
defaults:
55+
run:
56+
working-directory: pkgs/source_map_stack_trace/
57+
strategy:
58+
fail-fast: false
59+
matrix:
60+
# Add macos-latest and/or windows-latest if relevant for this package.
61+
os: [ubuntu-latest]
62+
sdk: [3.3, dev]
63+
steps:
64+
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332
65+
- uses: dart-lang/setup-dart@0a8a0fc875eb934c15d08629302413c671d3f672
66+
with:
67+
sdk: ${{ matrix.sdk }}
68+
- id: install
69+
name: Install dependencies
70+
run: dart pub get
71+
- name: Run VM tests
72+
run: dart test --platform vm
73+
if: always() && steps.install.outcome == 'success'
74+
- name: Run Chrome tests
75+
run: dart test --platform chrome
76+
if: always() && steps.install.outcome == 'success'

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ don't naturally belong to other topic monorepos (like
1818
| [extension_discovery](pkgs/extension_discovery/) | A convention and utilities for package extension discovery. | [![pub package](https://img.shields.io/pub/v/extension_discovery.svg)](https://pub.dev/packages/extension_discovery) |
1919
| [graphs](pkgs/graphs/) | Graph algorithms that operate on graphs in any representation | [![pub package](https://img.shields.io/pub/v/graphs.svg)](https://pub.dev/packages/graphs) |
2020
| [unified_analytics](pkgs/unified_analytics/) | A package for logging analytics for all Dart and Flutter related tooling to Google Analytics. | [![pub package](https://img.shields.io/pub/v/unified_analytics.svg)](https://pub.dev/packages/unified_analytics) |
21+
| [source_map_stack_trace](pkgs/source_map_stack_trace/) | A package for applying source maps to stack traces. | [![pub package](https://img.shields.io/pub/v/source_map_stack_trace.svg)](https://pub.dev/packages/source_map_stack_trace) |
2122

2223
## Publishing automation
2324

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.dart_tool/
2+
.packages
3+
.pub/
4+
pubspec.lock
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"test_package": {
3+
"platforms": ["vm"]
4+
}
5+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Below is a list of people and organizations that have contributed
2+
# to the project. Names should be added to the list like so:
3+
#
4+
# Name/Organization <email address>
5+
6+
Google Inc.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
## 2.1.2-wip
2+
3+
* Require Dart 3.3.0
4+
* Move to `dart-lang/tools` monorepo.
5+
6+
## 2.1.1
7+
8+
* Populate the pubspec `repository` field.
9+
10+
## 2.1.0
11+
12+
* Stable release for null safety.
13+
* Require Dart 2.12
14+
15+
## 2.0.0
16+
17+
### Breaking Changes
18+
19+
* Removed dependency on `package_resolver` and changed the apis to accept a
20+
`Map<String, Uri>` which maps package names to the base uri to resolve the
21+
`package:` uris for those packages.
22+
* The `sdkRoot` argument must be an `Uri`. Use `Uri.parse` for use
23+
cases previously passing a `String`.
24+
* The deprecated `packageRoot` argument has been removed.
25+
26+
## 1.1.5
27+
28+
* Set max SDK version to `<3.0.0`.
29+
30+
## 1.1.4
31+
32+
* Support source maps that depend on the uri of the location to resolve spans
33+
correctly.
34+
35+
## 1.1.3
36+
37+
* Add a missing dependency on `path`.
38+
39+
## 1.1.2
40+
41+
* Fix a typo in the previous fix.
42+
43+
## 1.1.1
44+
45+
* Don't crash if the `SyncPackageResolver` has no package information at all.
46+
47+
## 1.1.0
48+
49+
* `mapStackTrace()` now uses a `SyncPackageResolver` object from the
50+
[`package_resolver` package][package_resolver] to recreate `package:` URIs.
51+
52+
* **Deprecation**: the `packageRoot` parameter to `mapStackTrace` is deprecated
53+
in favor of the `packageInfo` parameter described above. It will be removed in
54+
a future release.
55+
56+
[package_resolver]: https://pub.dartlang.org/packages/package_resolver
57+
58+
## 1.0.5
59+
60+
* Add compatibility for member names that include named arguments.
61+
62+
## 1.0.4
63+
64+
* Add compatibility for Dart 1.10-style name munging.
65+
66+
## 1.0.3
67+
68+
* Prefer "dart:" URLs to "package:" URLs.
69+
70+
## 1.0.2
71+
72+
* Fix an off-by-one bug that was causing line numbers to be slightly off.
73+
74+
## 1.0.1
75+
76+
* Don't crash when mapping stack chains.
77+
78+
## 1.0.0
79+
80+
* Initial release.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright 2015, the Dart project authors.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
6+
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above
10+
copyright notice, this list of conditions and the following
11+
disclaimer in the documentation and/or other materials provided
12+
with the distribution.
13+
* Neither the name of Google LLC nor the names of its
14+
contributors may be used to endorse or promote products derived
15+
from this software without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
[![Dart CI](https://github.com/dart-lang/source_map_stack_trace/actions/workflows/test-package.yml/badge.svg)](https://github.com/dart-lang/source_map_stack_trace/actions/workflows/test-package.yml)
2+
[![pub package](https://img.shields.io/pub/v/source_map_stack_trace.svg)](https://pub.dev/packages/source_map_stack_trace)
3+
[![package publisher](https://img.shields.io/pub/publisher/source_map_stack_trace.svg)](https://pub.dev/packages/source_map_stack_trace/publisher)
4+
5+
`source_map_stack_trace` is a package for converting stack traces generated by
6+
dart2js-compiled JavaScript code into readable native Dart stack traces using
7+
source maps. For example:
8+
9+
```dart
10+
import 'package:source_map_stack_trace/source_map_stack_trace.dart';
11+
12+
void main() {
13+
var jsTrace = // Get a StackTrace generated by dart2js.
14+
var mapping = // Get a source map mapping the JS to the Dart source.
15+
16+
// Convert jsTrace to refer to the Dart source instead.
17+
var dartTrace = mapStackTrace(jsTrace, sourceMap);
18+
print(dartTrace);
19+
}
20+
```
21+
22+
This can convert the following JavaScript trace:
23+
24+
```
25+
expect_async_test.dart.browser_test.dart.js 2636:15 dart.wrapException
26+
expect_async_test.dart.browser_test.dart.js 14661:15 main__closure16.call$0
27+
expect_async_test.dart.browser_test.dart.js 18237:26 Declarer_test__closure.call$1
28+
expect_async_test.dart.browser_test.dart.js 17905:23 StackZoneSpecification_registerUnaryCallback__closure.call$0
29+
expect_async_test.dart.browser_test.dart.js 17876:16 StackZoneSpecification._stack_zone_specification$_run$2
30+
expect_async_test.dart.browser_test.dart.js 17899:26 StackZoneSpecification_registerUnaryCallback_closure.call$1
31+
expect_async_test.dart.browser_test.dart.js 6115:16 _rootRunUnary
32+
expect_async_test.dart.browser_test.dart.js 8576:39 _CustomZone.runUnary$2
33+
expect_async_test.dart.browser_test.dart.js 7135:57 _Future__propagateToListeners_handleValueCallback.call$0
34+
expect_async_test.dart.browser_test.dart.js 7031:147 dart._Future.static._Future__propagateToListeners
35+
```
36+
37+
to:
38+
39+
```
40+
dart:_internal/compiler/js_lib/js_helper.dart 1210:1 wrapException
41+
test/frontend/expect_async_test.dart 24:5 main.<fn>.<fn>
42+
package:test/src/backend/declarer.dart 45:48 Declarer.test.<fn>.<fn>
43+
package:stack_trace/src/stack_zone_specification.dart 134:30 StackZoneSpecification.registerUnaryCallback.<fn>.<fn>
44+
package:stack_trace/src/stack_zone_specification.dart 210:7 StackZoneSpecification._run
45+
package:stack_trace/src/stack_zone_specification.dart 135:5 StackZoneSpecification.registerUnaryCallback.<fn>
46+
dart:async/zone.dart 904:14 _rootRunUnary
47+
dart:async/zone.dart 806:3 _CustomZone.runUnary
48+
dart:async/future_impl.dart 486:13 _Future._propagateToListeners.handleValueCallback
49+
dart:async/future_impl.dart 567:32 _Future._propagateToListeners
50+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# https://dart.dev/tools/analysis#the-analysis-options-file
2+
include: package:dart_flutter_team_lints/analysis_options.yaml
3+
4+
analyzer:
5+
language:
6+
strict-casts: true
7+
strict-raw-types: true
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) 2015, 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 'package:path/path.dart' as p;
6+
import 'package:source_maps/source_maps.dart';
7+
import 'package:stack_trace/stack_trace.dart';
8+
9+
/// Convert [stackTrace], a stack trace generated by dart2js-compiled
10+
/// JavaScript, to a native-looking stack trace using [sourceMap].
11+
///
12+
/// [minified] indicates whether or not the dart2js code was minified. If it
13+
/// hasn't, this tries to clean up the stack frame member names.
14+
///
15+
/// The [packageMap] maps package names to the base uri used to resolve the
16+
/// `package:` uris for those packages. It is used to it's used to reconstruct
17+
/// `package:` URIs for stack frames that come from packages.
18+
///
19+
/// [sdkRoot] is the URI surfaced in the stack traces for SDK libraries.
20+
/// If it's passed, stack frames from the SDK will have `dart:` URLs.
21+
StackTrace mapStackTrace(Mapping sourceMap, StackTrace stackTrace,
22+
{bool minified = false, Map<String, Uri>? packageMap, Uri? sdkRoot}) {
23+
if (stackTrace is Chain) {
24+
return Chain(stackTrace.traces.map((trace) {
25+
return Trace.from(mapStackTrace(sourceMap, trace,
26+
minified: minified, packageMap: packageMap, sdkRoot: sdkRoot));
27+
}));
28+
}
29+
30+
var sdkLib = sdkRoot == null ? null : '$sdkRoot/lib';
31+
32+
var trace = Trace.from(stackTrace);
33+
return Trace(trace.frames.map((frame) {
34+
var line = frame.line;
35+
// If there's no line information, there's no way to translate this frame.
36+
// We could return it as-is, but these lines are usually not useful anyways.
37+
if (line == null) return null;
38+
39+
// If there's no column, try using the first column of the line.
40+
var column = frame.column ?? 0;
41+
42+
// Subtract 1 because stack traces use 1-indexed lines and columns and
43+
// source maps uses 0-indexed.
44+
var span =
45+
sourceMap.spanFor(line - 1, column - 1, uri: frame.uri.toString());
46+
47+
// If we can't find a source span, ignore the frame. It's probably something
48+
// internal that the user doesn't care about.
49+
if (span == null) return null;
50+
51+
var sourceUrl = span.sourceUrl.toString();
52+
if (sdkLib != null && p.url.isWithin(sdkLib, sourceUrl)) {
53+
sourceUrl = 'dart:${p.url.relative(sourceUrl, from: sdkLib)}';
54+
} else if (packageMap != null) {
55+
for (var package in packageMap.keys) {
56+
var packageUrl = packageMap[package].toString();
57+
if (!p.url.isWithin(packageUrl, sourceUrl)) continue;
58+
59+
sourceUrl =
60+
'package:$package/${p.url.relative(sourceUrl, from: packageUrl)}';
61+
break;
62+
}
63+
}
64+
65+
return Frame(
66+
Uri.parse(sourceUrl),
67+
span.start.line + 1,
68+
span.start.column + 1,
69+
// If the dart2js output is minified, there's no use trying to prettify
70+
// its member names. Use the span's identifier if available, otherwise
71+
// use the minified member name.
72+
minified
73+
? (span.isIdentifier ? span.text : frame.member)
74+
: _prettifyMember(frame.member!));
75+
}).whereType<Frame>());
76+
}
77+
78+
/// Reformats a JS member name to make it look more Dart-like.
79+
String _prettifyMember(String member) {
80+
return member
81+
// Get rid of the noise that Firefox sometimes adds.
82+
.replaceAll(RegExp(r'/?<$'), '')
83+
// Get rid of arity indicators and named arguments.
84+
.replaceAll(RegExp(r'\$\d+(\$[a-zA-Z_0-9]+)*$'), '')
85+
// Convert closures to <fn>.
86+
.replaceAllMapped(
87+
RegExp(r'(_+)closure\d*\.call$'),
88+
// The number of underscores before "closure" indicates how nested it
89+
// is.
90+
(match) => '.<fn>' * match[1]!.length)
91+
// Get rid of explicitly-generated calls.
92+
.replaceAll(RegExp(r'\.call$'), '')
93+
// Get rid of the top-level method prefix.
94+
.replaceAll(RegExp(r'^dart\.'), '')
95+
// Get rid of library namespaces.
96+
.replaceAll(RegExp(r'[a-zA-Z_0-9]+\$'), '')
97+
// Get rid of the static method prefix. The class name also exists in the
98+
// invocation, so we're not getting rid of any information.
99+
.replaceAll(RegExp(r'^[a-zA-Z_0-9]+.(static|dart).'), '')
100+
// Convert underscores after identifiers to dots. This runs the risk of
101+
// incorrectly converting members that contain underscores, but those are
102+
// contrary to the style guide anyway.
103+
.replaceAllMapped(RegExp(r'([a-zA-Z0-9]+)_'), (match) => '${match[1]!}.');
104+
}

0 commit comments

Comments
 (0)