Version 2.17.0-162.0.dev
Merge commit 'ec829affeaf40df7ed4613904cb324acdd11f3e9' into 'dev'
diff --git a/pkg/analyzer/lib/src/summary2/ast_text_printer.dart b/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
index aff9c4e..7f45994 100644
--- a/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_text_printer.dart
@@ -242,6 +242,12 @@
}
@override
+ void visitConstructorSelector(ConstructorSelector node) {
+ _token(node.period);
+ node.name.accept(this);
+ }
+
+ @override
void visitContinueStatement(ContinueStatement node) {
_token(node.continueKeyword);
node.label?.accept(this);
@@ -296,9 +302,17 @@
}
@override
+ void visitEnumConstantArguments(EnumConstantArguments node) {
+ node.typeArguments?.accept(this);
+ node.constructorSelector?.accept(this);
+ node.argumentList.accept(this);
+ }
+
+ @override
void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
_declaration(node);
node.name.accept(this);
+ node.arguments?.accept(this);
}
@override
@@ -306,8 +320,13 @@
_compilationUnitMember(node);
_token(node.enumKeyword);
node.name.accept(this);
+ node.typeParameters?.accept(this);
+ node.withClause?.accept(this);
+ node.implementsClause?.accept(this);
_token(node.leftBracket);
- _nodeList(node.constants, node.rightBracket);
+ _nodeList(node.constants, node.semicolon ?? node.rightBracket);
+ _token(node.semicolon);
+ node.members.accept(this);
_token(node.rightBracket);
}
@@ -877,6 +896,18 @@
}
@override
+ void visitSuperFormalParameter(SuperFormalParameter node) {
+ _normalFormalParameter(node);
+ _token(node.keyword);
+ node.type?.accept(this);
+ _token(node.superKeyword);
+ _token(node.period);
+ node.identifier.accept(this);
+ node.typeParameters?.accept(this);
+ node.parameters?.accept(this);
+ }
+
+ @override
void visitSwitchCase(SwitchCase node) {
_nodeList(node.labels);
_token(node.keyword);
diff --git a/pkg/analyzer/test/generated/error_parser_test.dart b/pkg/analyzer/test/generated/error_parser_test.dart
index 7073755..139bb79 100644
--- a/pkg/analyzer/test/generated/error_parser_test.dart
+++ b/pkg/analyzer/test/generated/error_parser_test.dart
@@ -11,6 +11,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../util/ast_type_matchers.dart';
+import '../util/feature_sets.dart';
import 'parser_test_base.dart';
main() {
@@ -2431,7 +2432,7 @@
}
void test_positionalAfterNamedArgument() {
- createParser('(x: 1, 2)');
+ createParser('(x: 1, 2)', featureSet: FeatureSets.language_2_16);
ArgumentList list = parser.parseArgumentList();
expectNotNullIfNoErrors(list);
listener.assertErrors(
diff --git a/pkg/analyzer/test/util/feature_sets.dart b/pkg/analyzer/test/util/feature_sets.dart
index 34ff0eb..85a4cd9 100644
--- a/pkg/analyzer/test/util/feature_sets.dart
+++ b/pkg/analyzer/test/util/feature_sets.dart
@@ -27,6 +27,11 @@
flags: [],
);
+ static final FeatureSet language_2_16 = FeatureSet.fromEnableFlags2(
+ sdkLanguageVersion: Version.parse('2.16.0'),
+ flags: [],
+ );
+
static final FeatureSet latest = FeatureSet.latestLanguageVersion();
static final FeatureSet latestWithExperiments = FeatureSet.fromEnableFlags2(
diff --git a/pkg/dart2js_info/README.md b/pkg/dart2js_info/README.md
index 5f05010..0f59cc2 100644
--- a/pkg/dart2js_info/README.md
+++ b/pkg/dart2js_info/README.md
@@ -51,8 +51,7 @@
## Format
-There are several formats of info files. Dart2js today produces a JSON format,
-but very soon will switch to produce a binary format by default.
+Dart2js info files are produced in either a binary or JSON format.
## Info API
@@ -502,7 +501,7 @@
`myMethodName`, you will save at least that 13.97%, and possibly some more from
the reachable size, but how much of that we are not certain.
-### Coverage tools
+### Coverage Server Analysis
Coverage information requires a bit more setup and work to get them running. The
steps are as follows:
@@ -539,6 +538,42 @@
$ dart2js_info coverage_analysis main.dart.info.data main.dart.coverage.json
```
+### Runtime Code Analysis
+
+Runtime code analysis requires both an info file and a runtime data file.
+
+The info file is emitted by compiling a dart2js app with `--dump-info`:
+
+```console
+$ dart2js --dump-info main.dart
+```
+
+Enable the collection of runtime data by compiling a dart2js app with an
+experimental flag:
+
+```console
+$ dart2js --experimental-track-allocations main.dart
+```
+
+After using your app (manually or via integration tests), dump the top-level
+window object below to a text file:
+
+```javascript
+JSON.stringify($__dart_deferred_initializers__.allocations)
+```
+
+Finally run this tool:
+
+```console
+$ dart2js_info runtime_coverage main.dart.info.data main.runtime.data.txt
+```
+
+And with the following to view package-level information:
+
+```console
+$ dart2js_info runtime_coverage --show-packages main.dart.info.data main.runtime.data.txt
+```
+
## Code location, features and bugs
This package is developed in [github][repo]. Please file feature requests and
diff --git a/pkg/dart2js_info/bin/src/runtime_coverage_analysis.dart b/pkg/dart2js_info/bin/src/runtime_coverage_analysis.dart
index 577aee6..c2fa5f6 100644
--- a/pkg/dart2js_info/bin/src/runtime_coverage_analysis.dart
+++ b/pkg/dart2js_info/bin/src/runtime_coverage_analysis.dart
@@ -21,7 +21,8 @@
/// This can be combined with the --dump-info step above.
///
/// * Load your app, exercise your code, then extract the runtime code
-/// coverage JSON blob by querying `$__dart_deferred_initializers__.allocations` in the page.
+/// coverage JSON blob by querying
+/// `$__dart_deferred_initializers__.allocations` in the page.
///
/// * Finally, run this tool.
library compiler.tool.runtime_coverage_analysis;
@@ -45,7 +46,10 @@
@override
final String description = "Analyze runtime coverage data";
- RuntimeCoverageAnalysisCommand();
+ RuntimeCoverageAnalysisCommand() {
+ argParser.addFlag('show-packages',
+ defaultsTo: false, help: "Show coverage details at the package level.");
+ }
@override
void run() async {
@@ -53,16 +57,23 @@
if (args.length < 2) {
usageException('Missing arguments, expected: info.data coverage.json');
}
- await _runtimeCoverageAnalysis(args[0], args[1]);
+ var showPackages = argResults['show-packages'];
+ if (showPackages) {
+ await _reportWithPackages(args[0], args[1]);
+ } else {
+ await _report(args[0], args[1]);
+ }
}
}
-Future<void> _runtimeCoverageAnalysis(infoFile, coverageFile) async {
+Future<void> _report(
+ String infoFile,
+ String coverageFile,
+) async {
var info = await infoFromFile(infoFile);
var coverageRaw = jsonDecode(File(coverageFile).readAsStringSync());
- var coverage = <String, bool>{};
- coverageRaw
- .forEach((k, v) => coverage[k] = coverage[k] ?? false || v as bool);
+ // The value associated with each coverage item isn't used for now.
+ var coverage = coverageRaw.keys.toSet();
int totalProgramSize = info.program.size;
int totalLibSize = info.libraries.fold(0, (n, lib) => n + lib.size);
@@ -74,9 +85,8 @@
void tallyCode(Info i) {
totalCode += i.size;
var name = qualifiedName(i);
- var used = coverage[name];
-
- if (used != null) {
+ var used = coverage.contains(name);
+ if (used) {
usedCode += i.size;
} else {
unused.add(i);
@@ -102,8 +112,6 @@
print('');
var unusedTotal = totalCode - usedCode;
_section('Runtime Coverage Breakdown', size: unusedTotal);
-
- // TODO(markzipan): support grouping results by package/library.
for (int i = 0; i < unused.length; i++) {
var item = unused.removeFirst();
var percent = (item.size * 100 / unusedTotal).toStringAsFixed(2);
@@ -111,6 +119,95 @@
}
}
+Future<void> _reportWithPackages(
+ String infoFile,
+ String coverageFile,
+) async {
+ var info = await infoFromFile(infoFile);
+ var coverageRaw = jsonDecode(File(coverageFile).readAsStringSync());
+ // The value associated with each coverage item isn't used for now.
+ var coverage = coverageRaw.keys.toSet();
+
+ int totalProgramSize = info.program.size;
+ int totalLibSize = info.libraries.fold(0, (n, lib) => n + lib.size);
+
+ int totalCode = 0;
+ int usedCode = 0;
+ var packageData = <String, PackageInfo>{};
+ var unused = PriorityQueue<Info>((a, b) => b.size.compareTo(a.size));
+
+ void tallyCode(Info i) {
+ totalCode += i.size;
+ var name = qualifiedName(i);
+ var used = coverage.contains(name);
+
+ var package = packageNameOrScheme(i);
+ packageData.putIfAbsent(package, () => PackageInfo());
+ packageData[package].add(i, used: used);
+
+ if (used) {
+ usedCode += i.size;
+ } else {
+ unused.add(i);
+ }
+ }
+
+ info.classes.forEach(tallyCode);
+ info.closures.forEach(tallyCode);
+
+ _section('Runtime Coverage Summary');
+ _showHeader('', 'bytes', '%');
+ _show('Program size', totalProgramSize, totalProgramSize);
+ _show('Libraries (excluding statics)', totalLibSize, totalProgramSize);
+ _show('Code (classes + closures)', totalCode, totalProgramSize);
+ _show('Used', usedCode, totalProgramSize);
+
+ print('');
+ _showHeader('', 'count', '%');
+ var total = info.classes.length + info.closures.length;
+ _show('Classes + closures', total, total);
+ _show('Used', total - unused.length, total);
+
+ print('');
+ var unusedTotal = totalCode - usedCode;
+ _section('Runtime Coverage Breakdown (packages)', size: unusedTotal);
+ for (var entry in packageData.entries.sortedBy((e) => -e.value.unusedSize)) {
+ var packageLabel = entry.key;
+ var packageInfo = entry.value;
+
+ print(' $packageLabel (${packageInfo.unusedSize} bytes unused)');
+
+ var packageRatioString = (packageInfo.usedRatio * 100).toStringAsFixed(2);
+ _leftPadded(
+ ' proportion of package used:',
+ '${packageInfo.usedSize}/${packageInfo.totalSize} '
+ '($packageRatioString%)');
+
+ var codeRatioString =
+ (packageInfo.unusedSize / totalCode * 100).toStringAsFixed(2);
+ _leftPadded(' proportion of unused code to all code:',
+ '${packageInfo.unusedSize}/$totalCode ($codeRatioString%)');
+
+ var unusedCodeRatioString =
+ (packageInfo.unusedSize / unusedTotal * 100).toStringAsFixed(2);
+ _leftPadded(' proportion of unused code to all unused code:',
+ '${packageInfo.unusedSize}/$unusedTotal ($unusedCodeRatioString%)');
+
+ _leftPadded(' package breakdown: ', '');
+ for (var item in packageInfo.elements.toList()) {
+ var percent =
+ (item.size * 100 / packageInfo.totalSize).toStringAsFixed(2);
+ var name = qualifiedName(item);
+ var used = coverage.contains(name);
+ var usedNotch = used ? '+' : '-';
+ _leftPadded(' $usedNotch ${qualifiedName(item)}:',
+ '${item.size} bytes ($percent% of package)');
+ }
+
+ print('');
+ }
+}
+
void _section(String title, {int size}) {
if (size == null) {
print(title);
@@ -128,3 +225,28 @@
var percent = (size * 100 / total).toStringAsFixed(2);
print(' ${pad(msg, 30, right: true)} ${pad(size, 8)} ${pad(percent, 6)}%');
}
+
+_leftPadded(String msg1, String msg2) {
+ print(' ${pad(msg1, 50, right: true)} $msg2');
+}
+
+class PackageInfo {
+ final elements = PriorityQueue<Info>((a, b) => b.size.compareTo(a.size));
+ num totalSize = 0;
+ num usedSize = 0;
+ num unusedSize = 0;
+ num usedRatio = 0.0;
+
+ PackageInfo();
+
+ void add(Info i, {bool used = true}) {
+ totalSize += i.size;
+ if (used) {
+ usedSize += i.size;
+ } else {
+ unusedSize += i.size;
+ }
+ elements.add(i);
+ usedRatio = usedSize / totalSize;
+ }
+}
diff --git a/pkg/dart2js_info/lib/src/util.dart b/pkg/dart2js_info/lib/src/util.dart
index 874202c..5478ec8 100644
--- a/pkg/dart2js_info/lib/src/util.dart
+++ b/pkg/dart2js_info/lib/src/util.dart
@@ -123,6 +123,23 @@
return null;
}
+/// Provides the package name associated with [info], the URI scheme if
+/// available, or null otherwise.
+String packageNameOrScheme(Info info) {
+ while (info.parent != null) {
+ info = info.parent;
+ }
+ if (info is LibraryInfo) {
+ if (info.uri.isScheme('package')) {
+ return '${info.uri}'.split('/').first;
+ }
+ if (info.uri.hasScheme) {
+ return info.uri.scheme;
+ }
+ }
+ return null;
+}
+
/// Produce a string containing [value] padded with white space up to [n] chars.
pad(value, n, {bool right = false}) {
var s = '$value';
diff --git a/tools/VERSION b/tools/VERSION
index d69f4db..ae3ec1b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 17
PATCH 0
-PRERELEASE 161
+PRERELEASE 162
PRERELEASE_PATCH 0
\ No newline at end of file