mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:47:36 +00:00
5bdfd1e69b
MixinFullResolution is also updated to create new ClassHierarchy instance only if there are transformed classes. This improves incremental kernel generator initial time from 22 to 16 seconds. R=ahe@google.com, kmillikin@google.com, paulberry@google.com, sigmund@google.com BUG= Review-Url: https://codereview.chromium.org/2918593003 .
151 lines
5 KiB
Dart
151 lines
5 KiB
Dart
// Copyright (c) 2016, 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.
|
|
library kernel.treeshaker_dump;
|
|
|
|
import 'dart:io';
|
|
import 'package:kernel/class_hierarchy.dart';
|
|
import 'package:kernel/core_types.dart';
|
|
import 'package:kernel/kernel.dart';
|
|
import 'package:kernel/transformations/treeshaker.dart';
|
|
import 'package:args/args.dart';
|
|
import 'package:path/path.dart' as pathlib;
|
|
import 'package:kernel/text/ast_to_text.dart';
|
|
|
|
ArgParser parser = new ArgParser(allowTrailingOptions: true)
|
|
..addFlag('used', help: 'Print used members', negatable: false)
|
|
..addFlag('unused', help: 'Print unused members', negatable: false)
|
|
..addFlag('instantiated',
|
|
help: 'Print instantiated classes', negatable: false)
|
|
..addFlag('types', help: 'Print classes used as a type', negatable: false)
|
|
..addFlag('summary',
|
|
help: 'Print short summary of tree shaking results', defaultsTo: true)
|
|
..addFlag('diff',
|
|
help: 'Print textual output before and after tree shaking.\n'
|
|
'Files are written to FILE.before.txt and FILE.after.txt',
|
|
negatable: false)
|
|
..addOption('output',
|
|
help: 'The --diff files are written to the given directory instead of '
|
|
'the working directory')
|
|
..addFlag('strong', help: 'Run the tree shaker in strong mode');
|
|
|
|
String usage = '''
|
|
Usage: treeshaker_dump [options] FILE.dill
|
|
|
|
Runs tree shaking on the given program and prints information about the results.
|
|
|
|
Example:
|
|
treeshaker_dump --instantiated foo.dill
|
|
|
|
Example:
|
|
treeshaker_dump --diff foo.dill
|
|
diff -cd foo.{before,after}.txt > diff.txt
|
|
# open diff.txt in an editor
|
|
|
|
Options:
|
|
${parser.usage}
|
|
''';
|
|
|
|
main(List<String> args) {
|
|
if (args.isEmpty) {
|
|
print(usage);
|
|
exit(1);
|
|
}
|
|
ArgResults options = parser.parse(args);
|
|
if (options.rest.length != 1) {
|
|
print('Exactly one file should be given.');
|
|
exit(1);
|
|
}
|
|
String filename = options.rest.single;
|
|
|
|
if (options['output'] != null && !options['diff']) {
|
|
print('--output must be used with --diff');
|
|
exit(1);
|
|
}
|
|
|
|
bool strong = options['strong'];
|
|
|
|
Program program = loadProgramFromBinary(filename);
|
|
CoreTypes coreTypes = new CoreTypes(program);
|
|
ClassHierarchy hierarchy = new ClosedWorldClassHierarchy(program);
|
|
TreeShaker shaker =
|
|
new TreeShaker(coreTypes, hierarchy, program, strongMode: strong);
|
|
int totalClasses = 0;
|
|
int totalInstantiationCandidates = 0;
|
|
int totalMembers = 0;
|
|
int usedClasses = 0;
|
|
int instantiatedClasses = 0;
|
|
int usedMembers = 0;
|
|
|
|
void visitMember(Member member) {
|
|
if (member.isAbstract) return; // Abstract members are not relevant.
|
|
++totalMembers;
|
|
bool isUsed = shaker.isMemberBodyUsed(member);
|
|
if (isUsed) {
|
|
++usedMembers;
|
|
}
|
|
if (isUsed && options['used'] || !isUsed && options['unused']) {
|
|
String prefix = (options['used'] && options['unused'])
|
|
? (isUsed ? 'USED ' : 'UNUSED ')
|
|
: '';
|
|
print('$prefix$member');
|
|
}
|
|
}
|
|
|
|
for (var library in program.libraries) {
|
|
library.members.forEach(visitMember);
|
|
for (Class classNode in library.classes) {
|
|
++totalClasses;
|
|
if (shaker.isInstantiated(classNode)) {
|
|
++instantiatedClasses;
|
|
++totalInstantiationCandidates;
|
|
} else if (!classNode.isAbstract &&
|
|
classNode.members.any((m) => m.isInstanceMember)) {
|
|
++totalInstantiationCandidates;
|
|
}
|
|
if (shaker.isHierarchyUsed(classNode)) {
|
|
++usedClasses;
|
|
}
|
|
classNode.members.forEach(visitMember);
|
|
if (options['instantiated'] && shaker.isInstantiated(classNode)) {
|
|
print(classNode);
|
|
}
|
|
if (options['types'] && shaker.isHierarchyUsed(classNode)) {
|
|
print(classNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (options['summary']) {
|
|
print('Classes used: ${ratio(usedClasses, totalClasses)}');
|
|
print('Classes instantiated: '
|
|
'${ratio(instantiatedClasses, totalInstantiationCandidates)}');
|
|
print('Members used: ${ratio(usedMembers, totalMembers)}');
|
|
}
|
|
|
|
if (options['diff']) {
|
|
String name = pathlib.basenameWithoutExtension(filename);
|
|
String outputDir = options['output'] ?? '';
|
|
String beforeFile = pathlib.join(outputDir, '$name.before.txt');
|
|
String afterFile = pathlib.join(outputDir, '$name.after.txt');
|
|
NameSystem names = new NameSystem();
|
|
StringBuffer before = new StringBuffer();
|
|
new Printer(before, syntheticNames: names).writeProgramFile(program);
|
|
new File(beforeFile).writeAsStringSync('$before');
|
|
new TreeShaker(coreTypes, hierarchy, program, strongMode: strong)
|
|
.transform(program);
|
|
StringBuffer after = new StringBuffer();
|
|
new Printer(after, syntheticNames: names).writeProgramFile(program);
|
|
new File(afterFile).writeAsStringSync('$after');
|
|
print('Text written to $beforeFile and $afterFile');
|
|
}
|
|
}
|
|
|
|
String ratio(num x, num total) {
|
|
return '$x / $total (${percent(x, total)})';
|
|
}
|
|
|
|
String percent(num x, num total) {
|
|
return total == 0 ? '0%' : ((100 * x / total).toStringAsFixed(0) + '%');
|
|
}
|