Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Unified Diff: pkg/kernel/lib/transformations/treeshaker.dart

Issue 2644543004: Revert "Improvements to the kernel tree shaker." (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/kernel/lib/target/vm.dart ('k') | pkg/kernel/test/treeshaker_bench.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/kernel/lib/transformations/treeshaker.dart
diff --git a/pkg/kernel/lib/transformations/treeshaker.dart b/pkg/kernel/lib/transformations/treeshaker.dart
index 90540a0283633ab252284d88eaef1ed179a4837c..bd33595d2e8885218cb405893a1f59983890d801 100644
--- a/pkg/kernel/lib/transformations/treeshaker.dart
+++ b/pkg/kernel/lib/transformations/treeshaker.dart
@@ -7,7 +7,6 @@ library kernel.tree_shaker;
import '../ast.dart';
import '../class_hierarchy.dart';
import '../core_types.dart';
-import '../type_environment.dart';
Program transformProgram(Program program) {
new TreeShaker(program).transform(program);
@@ -28,7 +27,7 @@ Program transformProgram(Program program) {
/// - for each member, a set of potential host classes
/// - a set of names used in dynamic dispatch not on `this`
///
-/// If the `dart:mirrors` library is used then nothing will be tree-shaken.
+/// The `dart:mirrors` library is not supported.
//
// TODO(asgerf): Shake off parts of the core libraries based on the Target.
// TODO(asgerf): Tree shake unused instance fields.
@@ -36,19 +35,10 @@ class TreeShaker {
final Program program;
final ClassHierarchy hierarchy;
final CoreTypes coreTypes;
- final bool strongMode;
- /// Map from classes to set of names that have been dispatched with that class
- /// as the static receiver type (meaning any subtype of that class can be
- /// the potential concrete receiver).
- ///
- /// The map is implemented as a list, indexed by
- /// [ClassHierarchy.getClassIndex].
- final List<Set<Name>> _dispatchedNames;
-
- /// Map from names to the set of classes that might be the concrete receiver
- /// of a call with the given name.
- final Map<Name, ClassSet> _receiversOfName = <Name, ClassSet>{};
+ /// Names used in a dynamic dispatch invocation that could not be resolved
+ /// to a concrete target (i.e. not on `this`).
+ final Set<Name> _dispatchedNames = new Set<Name>();
/// Instance members that are potential targets for dynamic dispatch, but
/// whose name has not yet been seen in a dynamic dispatch invocation.
@@ -87,41 +77,20 @@ class TreeShaker {
/// Classes whose interface can be used by external code to invoke user code.
final Set<Class> _escapedClasses = new Set<Class>();
- /// Members that have been overridden by a member whose concrete body is
- /// needed. These must be preserved in order to maintain interface targets
- /// for typed calls.
- final Set<Member> _overriddenMembers = new Set<Member>();
-
- final List<Expression> _typedCalls = <Expression>[];
-
/// AST visitor for finding static uses and dynamic dispatches in code.
_TreeShakerVisitor _visitor;
/// AST visitor for analyzing type annotations on external members.
_ExternalTypeVisitor _covariantVisitor;
_ExternalTypeVisitor _contravariantVisitor;
- _ExternalTypeVisitor _invariantVisitor;
-
- Library _mirrorsLibrary;
+ _ExternalTypeVisitor _bivariantVisitor;
- /// Set to true if any use of the `dart:mirrors` API is found.
- bool isUsingMirrors = false;
-
- TreeShaker(Program program,
- {ClassHierarchy hierarchy, CoreTypes coreTypes, bool strongMode: false})
+ TreeShaker(Program program, {ClassHierarchy hierarchy, CoreTypes coreTypes})
: this._internal(program, hierarchy ?? new ClassHierarchy(program),
- coreTypes ?? new CoreTypes(program), strongMode);
-
- bool isMemberBodyUsed(Member member) {
- return _usedMembers.containsKey(member);
- }
-
- bool isMemberOverridden(Member member) {
- return _overriddenMembers.contains(member);
- }
+ coreTypes ?? new CoreTypes(program));
bool isMemberUsed(Member member) {
- return isMemberBodyUsed(member) || isMemberOverridden(member);
+ return _usedMembers.containsKey(member);
}
bool isInstantiated(Class classNode) {
@@ -141,14 +110,11 @@ class TreeShaker {
///
/// This removes unused classes, members, and hierarchy data.
void transform(Program program) {
- if (isUsingMirrors) return; // Give up if using mirrors.
new _TreeShakingTransformer(this).transform(program);
}
- TreeShaker._internal(
- this.program, ClassHierarchy hierarchy, this.coreTypes, this.strongMode)
+ TreeShaker._internal(this.program, ClassHierarchy hierarchy, this.coreTypes)
: this.hierarchy = hierarchy,
- this._dispatchedNames = new List<Set<Name>>(hierarchy.classes.length),
this._usedMembersWithHost =
new List<Set<Member>>(hierarchy.classes.length),
this._classRetention = new List<ClassRetention>.filled(
@@ -157,14 +123,9 @@ class TreeShaker {
_covariantVisitor = new _ExternalTypeVisitor(this, isCovariant: true);
_contravariantVisitor =
new _ExternalTypeVisitor(this, isContravariant: true);
- _invariantVisitor = new _ExternalTypeVisitor(this,
+ _bivariantVisitor = new _ExternalTypeVisitor(this,
isCovariant: true, isContravariant: true);
- _mirrorsLibrary = coreTypes.getCoreLibrary('dart:mirrors');
- try {
- _build();
- } on _UsingMirrorsException {
- isUsingMirrors = true;
- }
+ _build();
}
void _build() {
@@ -176,32 +137,10 @@ class TreeShaker {
_addInstantiatedExternalSubclass(coreTypes.listClass);
_addInstantiatedExternalSubclass(coreTypes.stringClass);
}
- _addDispatchedName(hierarchy.rootClass, new Name('noSuchMethod'));
+ _addDispatchedName(new Name('noSuchMethod'));
_addPervasiveUses();
_addUsedMember(null, program.mainMethod);
_iterateWorklist();
-
- // Mark overridden members in order to preserve abstract members as
- // necessary.
- if (strongMode) {
- for (int i = hierarchy.classes.length - 1; i >= 0; --i) {
- Class class_ = hierarchy.classes[i];
- if (isHierarchyUsed(class_)) {
- hierarchy.forEachOverridePair(class_,
- (Member ownMember, Member superMember, bool isSetter) {
- if (isMemberBodyUsed(ownMember) ||
- _overriddenMembers.contains(ownMember)) {
- _overriddenMembers.add(superMember);
- // Ensure the types mentioned in the member can be preserved.
- _visitor.visitMemberInterface(superMember);
- }
- });
- }
- }
- // Marking members as overridden should not cause new code to become
- // reachable.
- assert(_worklist.isEmpty);
- }
}
/// Registers some extremely commonly used core classes as instantiated, so
@@ -211,53 +150,26 @@ class TreeShaker {
_addInstantiatedExternalSubclass(coreTypes.intClass);
_addInstantiatedExternalSubclass(coreTypes.boolClass);
_addInstantiatedExternalSubclass(coreTypes.nullClass);
- _addInstantiatedExternalSubclass(coreTypes.functionClass);
- _addInstantiatedExternalSubclass(coreTypes.invocationClass);
}
/// Registers the given name as seen in a dynamic dispatch, and discovers used
/// instance members accordingly.
- void _addDispatchedName(Class receiver, Name name) {
- int index = hierarchy.getClassIndex(receiver);
- Set<Name> receiverNames = _dispatchedNames[index] ??= new Set<Name>();
+ void _addDispatchedName(Name name) {
// TODO(asgerf): make use of selector arity and getter/setter kind
- if (receiverNames.add(name)) {
- List<TreeNode> candidates = _dispatchTargetCandidates[name];
- if (candidates != null) {
- for (int i = 0; i < candidates.length; i += 2) {
- Class host = candidates[i];
- if (hierarchy.isSubtypeOf(host, receiver)) {
- // This (host, member) pair is a potential target of the dispatch.
- Member member = candidates[i + 1];
-
- // Remove the (host,member) pair from the candidate list.
- // Move the last pair into the current index and shrink the list.
- int lastPair = candidates.length - 2;
- candidates[i] = candidates[lastPair];
- candidates[i + 1] = candidates[lastPair + 1];
- candidates.length -= 2;
- i -= 2; // Revisit the same index now that it has been updated.
-
- // Mark the pair as used. This should be done after removing it
- // from the candidate list, since this call may recursively scan
- // for more used members.
- _addUsedMember(host, member);
- }
+ if (_dispatchedNames.add(name)) {
+ List<TreeNode> targets = _dispatchTargetCandidates[name];
+ if (targets != null) {
+ for (int i = 0; i < targets.length; i += 2) {
+ _addUsedMember(targets[i], targets[i + 1]);
}
}
- var subtypes = hierarchy.getSubtypesOf(receiver);
- var receiverSet = _receiversOfName[name];
- _receiversOfName[name] = receiverSet == null
- ? subtypes
- : _receiversOfName[name].union(subtypes);
}
}
/// Registers the given method as a potential target of dynamic dispatch on
/// the given class.
void _addDispatchTarget(Class host, Member member) {
- ClassSet receivers = _receiversOfName[member.name];
- if (receivers != null && receivers.contains(host)) {
+ if (_dispatchedNames.contains(member.name)) {
_addUsedMember(host, member);
} else {
_dispatchTargetCandidates.putIfAbsent(member.name, _makeTreeNodeList)
@@ -301,11 +213,6 @@ class TreeShaker {
} else {
_addCallToExternalProcedure(member);
}
- _addDispatchTarget(classNode, member);
- }
- for (Member member
- in hierarchy.getInterfaceMembers(classNode, setters: true)) {
- _addDispatchTarget(classNode, member);
}
}
@@ -400,9 +307,6 @@ class TreeShaker {
/// the initializer list of another constructor.
/// - Procedures are used if they can be invoked or torn off.
void _addUsedMember(Class host, Member member) {
- if (member.enclosingLibrary == _mirrorsLibrary) {
- throw new _UsingMirrorsException();
- }
if (host != null) {
// Check if the member has been seen with this host before.
int index = hierarchy.getClassIndex(host);
@@ -445,7 +349,7 @@ class TreeShaker {
if (!_escapedClasses.add(node)) return;
for (Member member in hierarchy.getInterfaceMembers(node)) {
if (member is Procedure) {
- _addDispatchedName(node, member.name);
+ _addDispatchedName(member.name);
}
}
}
@@ -514,47 +418,15 @@ final Node _setterSentinel = const InvalidType();
class _TreeShakerVisitor extends RecursiveVisitor {
final TreeShaker shaker;
final CoreTypes coreTypes;
- final TypeEnvironment types;
- final bool strongMode;
List<Node> summary;
_TreeShakerVisitor(TreeShaker shaker)
: this.shaker = shaker,
- this.coreTypes = shaker.coreTypes,
- this.strongMode = shaker.strongMode,
- this.types = new TypeEnvironment(shaker.coreTypes, shaker.hierarchy) {
- types.errorHandler = handleError;
- }
-
- void handleError(TreeNode node, String message) {
- print('[error] $message (${node.location})');
- }
+ this.coreTypes = shaker.coreTypes;
- void analyzeAndBuildSummary(Member member, List<Node> summary) {
+ void analyzeAndBuildSummary(Node node, List<Node> summary) {
this.summary = summary;
- types.thisType = member.enclosingClass?.thisType;
- member.accept(this);
- }
-
- void visitMemberInterface(Member node) {
- if (node is Field) {
- node.type.accept(this);
- } else if (node is Procedure) {
- visitFunctionInterface(node.function);
- }
- }
-
- visitFunctionInterface(FunctionNode node) {
- for (var parameter in node.typeParameters) {
- parameter.bound.accept(this);
- }
- for (var parameter in node.positionalParameters) {
- parameter.type.accept(this);
- }
- for (var parameter in node.namedParameters) {
- parameter.type.accept(this);
- }
- node.returnType.accept(this);
+ node.accept(this);
}
@override
@@ -632,34 +504,12 @@ class _TreeShakerVisitor extends RecursiveVisitor {
node.visitChildren(this);
}
- Class getKnownSupertype(DartType type) {
- if (type is InterfaceType) {
- return type.classNode;
- } else if (type is TypeParameterType) {
- return getKnownSupertype(type.parameter.bound);
- } else if (type is FunctionType) {
- return coreTypes.functionClass;
- } else if (type is BottomType) {
- return coreTypes.nullClass;
- } else {
- return coreTypes.objectClass;
- }
- }
-
- Class getStaticType(Expression node) {
- if (!strongMode) return coreTypes.objectClass;
- return getKnownSupertype(node.getStaticType(types));
- }
-
@override
visitMethodInvocation(MethodInvocation node) {
if (node.receiver is ThisExpression) {
addSelfDispatch(node.name);
} else {
- shaker._addDispatchedName(getStaticType(node.receiver), node.name);
- if (node.interfaceTarget != null) {
- shaker._typedCalls.add(node);
- }
+ shaker._addDispatchedName(node.name);
}
node.visitChildren(this);
}
@@ -701,10 +551,7 @@ class _TreeShakerVisitor extends RecursiveVisitor {
if (node.receiver is ThisExpression) {
addSelfDispatch(node.name);
} else {
- shaker._addDispatchedName(getStaticType(node.receiver), node.name);
- if (node.interfaceTarget != null) {
- shaker._typedCalls.add(node);
- }
+ shaker._addDispatchedName(node.name);
}
node.visitChildren(this);
}
@@ -714,10 +561,7 @@ class _TreeShakerVisitor extends RecursiveVisitor {
if (node.receiver is ThisExpression) {
addSelfDispatch(node.name, setter: true);
} else {
- shaker._addDispatchedName(getStaticType(node.receiver), node.name);
- if (node.interfaceTarget != null) {
- shaker._typedCalls.add(node);
- }
+ shaker._addDispatchedName(node.name);
}
node.visitChildren(this);
}
@@ -738,9 +582,7 @@ class _TreeShakerVisitor extends RecursiveVisitor {
@override
visitStringConcatenation(StringConcatenation node) {
- for (var expression in node.expressions) {
- shaker._addDispatchedName(getStaticType(expression), _toStringName);
- }
+ shaker._addDispatchedName(_toStringName);
node.visitChildren(this);
}
@@ -751,12 +593,6 @@ class _TreeShakerVisitor extends RecursiveVisitor {
}
@override
- visitSupertype(Supertype node) {
- shaker._addClassUsedInType(node.classNode);
- node.visitChildren(this);
- }
-
- @override
visitDoubleLiteral(DoubleLiteral node) {
shaker._addInstantiatedExternalSubclass(coreTypes.doubleClass);
}
@@ -764,6 +600,8 @@ class _TreeShakerVisitor extends RecursiveVisitor {
@override
visitSymbolLiteral(SymbolLiteral node) {
shaker._addInstantiatedExternalSubclass(coreTypes.symbolClass);
+ // Note: we do not support 'dart:mirrors' right now, so nothing else needs
+ // to be done for symbols.
}
@override
@@ -802,34 +640,17 @@ class _TreeShakingTransformer extends Transformer {
_TreeShakingTransformer(this.shaker);
- Member _translateInterfaceTarget(Member target) {
- return target != null && shaker.isMemberUsed(target) ? target : null;
- }
-
void transform(Program program) {
for (var library in program.libraries) {
if (library.importUri.scheme == 'dart') {
- // The backend expects certain things to be present in the core
- // libraries, so we currently don't shake off anything there.
+ // As long as patching happens in the backend, we cannot shake off
+ // anything in the core libraries.
continue;
}
library.transformChildren(this);
// Note: we can't shake off empty libraries yet since we don't check if
// there are private names that use the library.
}
- for (Expression node in shaker._typedCalls) {
- // We should not leave dangling references, so if the target of a typed
- // call has been removed, we must remove the reference. The receiver of
- // such a call can only be null.
- // TODO(asgerf): Rewrite to a NSM call instead of adding dynamic calls.
- if (node is MethodInvocation) {
- node.interfaceTarget = _translateInterfaceTarget(node.interfaceTarget);
- } else if (node is PropertyGet) {
- node.interfaceTarget = _translateInterfaceTarget(node.interfaceTarget);
- } else if (node is PropertySet) {
- node.interfaceTarget = _translateInterfaceTarget(node.interfaceTarget);
- }
- }
}
Class visitClass(Class node) {
@@ -844,47 +665,28 @@ class _TreeShakingTransformer extends Transformer {
node.supertype = shaker.coreTypes.objectClass.asRawSupertype;
node.implementedTypes.clear();
node.typeParameters.clear();
- node.isAbstract = true;
// Mixin applications cannot have static members.
assert(node.mixedInType == null);
// Unused members will be removed below.
break;
case ClassRetention.Hierarchy:
- node.isAbstract = true;
- break;
-
case ClassRetention.Instance:
case ClassRetention.ExternalInstance:
break;
}
node.transformChildren(this);
+ if (node.constructors.isEmpty && node.procedures.isEmpty) {
+ // The VM does not like classes without any members, so ensure there is
+ // always a constructor left.
+ node.addMember(new Constructor(new FunctionNode(new EmptyStatement())));
+ }
return node;
}
Member defaultMember(Member node) {
- if (!shaker.isMemberBodyUsed(node)) {
- if (!shaker.isMemberOverridden(node)) {
- return null;
- }
- if (node is Procedure) {
- // Remove body of unused member.
- if (node.enclosingClass.isAbstract) {
- node.isAbstract = true;
- node.function.body = null;
- } else {
- // If the enclosing class is not abstract, the method should still
- // have a body even if it can never be called.
- if (node.function.body != null) {
- node.function.body = new ExpressionStatement(
- new Throw(new StringLiteral('Method removed by tree-shaking')))
- ..parent = node.function;
- }
- }
- node.function.asyncMarker = AsyncMarker.Sync;
- } else if (node is Field) {
- node.initializer = null;
- }
+ if (!shaker.isMemberUsed(node)) {
+ return null; // Remove unused member.
}
return node;
}
@@ -918,7 +720,7 @@ class _ExternalTypeVisitor extends DartTypeVisitor {
visitCovariant(DartType type) => type?.accept(this);
- visitInvariant(DartType type) => shaker._invariantVisitor.visit(type);
+ visitBivariant(DartType type) => shaker._bivariantVisitor.visit(type);
visitInvalidType(InvalidType node) {}
@@ -945,7 +747,7 @@ class _ExternalTypeVisitor extends DartTypeVisitor {
if (isWhitelistedCovariant(node.classNode)) {
visitCovariant(typeArgument);
} else {
- visitInvariant(typeArgument);
+ visitBivariant(typeArgument);
}
}
}
@@ -975,7 +777,3 @@ class _ExternalTypeVisitor extends DartTypeVisitor {
classNode == coreTypes.mapClass;
}
}
-
-/// Exception that is thrown to stop the tree shaking analysis when a use
-/// of `dart:mirrors` is found.
-class _UsingMirrorsException {}
« no previous file with comments | « pkg/kernel/lib/target/vm.dart ('k') | pkg/kernel/test/treeshaker_bench.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698