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

Side by Side Diff: pkg/kernel/lib/transformations/treeshaker.dart

Issue 2627723003: Improvements to the kernel tree shaker. (Closed)
Patch Set: Fix issue with async method whose body becomes tree shaken, but not its interface 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library kernel.tree_shaker; 5 library kernel.tree_shaker;
6 6
7 import '../ast.dart'; 7 import '../ast.dart';
8 import '../class_hierarchy.dart'; 8 import '../class_hierarchy.dart';
9 import '../core_types.dart'; 9 import '../core_types.dart';
10 import '../type_environment.dart';
10 11
11 Program transformProgram(Program program) { 12 Program transformProgram(Program program) {
12 new TreeShaker(program).transform(program); 13 new TreeShaker(program).transform(program);
13 return program; 14 return program;
14 } 15 }
15 16
16 /// Tree shaking based on class hierarchy analysis. 17 /// Tree shaking based on class hierarchy analysis.
17 /// 18 ///
18 /// Any dynamic dispatch not on `this` is conservatively assumed to target 19 /// Any dynamic dispatch not on `this` is conservatively assumed to target
19 /// any instantiated class that implements a member matching the selector. 20 /// any instantiated class that implements a member matching the selector.
20 /// 21 ///
21 /// Member bodies are analyzed relative to a given "host class" which is the 22 /// Member bodies are analyzed relative to a given "host class" which is the
22 /// concrete type of `this` (or null if in static context), so dispatches on 23 /// concrete type of `this` (or null if in static context), so dispatches on
23 /// `this` can be resolved more precisely. 24 /// `this` can be resolved more precisely.
24 /// 25 ///
25 /// The tree shaker computes the following in a fixed-point iteration: 26 /// The tree shaker computes the following in a fixed-point iteration:
26 /// - a set of instantiated classes 27 /// - a set of instantiated classes
27 /// - for each member, a set of potential host classes 28 /// - for each member, a set of potential host classes
28 /// - a set of names used in dynamic dispatch not on `this` 29 /// - a set of names used in dynamic dispatch not on `this`
29 /// 30 ///
30 /// The `dart:mirrors` library is not supported. 31 /// If the `dart:mirrors` library is used then nothing will be tree-shaken.
31 // 32 //
32 // TODO(asgerf): Shake off parts of the core libraries based on the Target. 33 // TODO(asgerf): Shake off parts of the core libraries based on the Target.
33 // TODO(asgerf): Tree shake unused instance fields. 34 // TODO(asgerf): Tree shake unused instance fields.
34 class TreeShaker { 35 class TreeShaker {
35 final Program program; 36 final Program program;
36 final ClassHierarchy hierarchy; 37 final ClassHierarchy hierarchy;
37 final CoreTypes coreTypes; 38 final CoreTypes coreTypes;
39 final bool strongMode;
38 40
39 /// Names used in a dynamic dispatch invocation that could not be resolved 41 /// Map from classes to set of names that have been dispatched with that class
40 /// to a concrete target (i.e. not on `this`). 42 /// as the static receiver type (meaning any subtype of that class can be
41 final Set<Name> _dispatchedNames = new Set<Name>(); 43 /// the potential concrete receiver).
44 ///
45 /// The map is implemented as a list, indexed by
46 /// [ClassHierarchy.getClassIndex].
47 final List<Set<Name>> _dispatchedNames;
48
49 /// Map from names to the set of classes that might be the concrete receiver
50 /// of a call with the given name.
51 final Map<Name, ClassSet> _receiversOfName = <Name, ClassSet>{};
42 52
43 /// Instance members that are potential targets for dynamic dispatch, but 53 /// Instance members that are potential targets for dynamic dispatch, but
44 /// whose name has not yet been seen in a dynamic dispatch invocation. 54 /// whose name has not yet been seen in a dynamic dispatch invocation.
45 /// 55 ///
46 /// The map is indexed by the name of the member, and value is a list of 56 /// The map is indexed by the name of the member, and value is a list of
47 /// interleaved (host class, member) pairs. 57 /// interleaved (host class, member) pairs.
48 final Map<Name, List<TreeNode>> _dispatchTargetCandidates = 58 final Map<Name, List<TreeNode>> _dispatchTargetCandidates =
49 <Name, List<TreeNode>>{}; 59 <Name, List<TreeNode>>{};
50 60
51 /// Map from classes to the set of members that are reachable with that 61 /// Map from classes to the set of members that are reachable with that
(...skipping 18 matching lines...) Expand all
70 /// See [ClassRetention]. 80 /// See [ClassRetention].
71 final List<ClassRetention> _classRetention; 81 final List<ClassRetention> _classRetention;
72 82
73 /// Interleaved (host class, member) pairs that are reachable but have not yet 83 /// Interleaved (host class, member) pairs that are reachable but have not yet
74 /// been analyzed for more uses. 84 /// been analyzed for more uses.
75 final List<TreeNode> _worklist = new List<TreeNode>(); 85 final List<TreeNode> _worklist = new List<TreeNode>();
76 86
77 /// Classes whose interface can be used by external code to invoke user code. 87 /// Classes whose interface can be used by external code to invoke user code.
78 final Set<Class> _escapedClasses = new Set<Class>(); 88 final Set<Class> _escapedClasses = new Set<Class>();
79 89
90 /// Members that have been overridden by a member whose concrete body is
91 /// needed. These must be preserved in order to maintain interface targets
92 /// for typed calls.
93 final Set<Member> _overriddenMembers = new Set<Member>();
94
95 final List<Expression> _typedCalls = <Expression>[];
96
80 /// AST visitor for finding static uses and dynamic dispatches in code. 97 /// AST visitor for finding static uses and dynamic dispatches in code.
81 _TreeShakerVisitor _visitor; 98 _TreeShakerVisitor _visitor;
82 99
83 /// AST visitor for analyzing type annotations on external members. 100 /// AST visitor for analyzing type annotations on external members.
84 _ExternalTypeVisitor _covariantVisitor; 101 _ExternalTypeVisitor _covariantVisitor;
85 _ExternalTypeVisitor _contravariantVisitor; 102 _ExternalTypeVisitor _contravariantVisitor;
86 _ExternalTypeVisitor _bivariantVisitor; 103 _ExternalTypeVisitor _invariantVisitor;
87 104
88 TreeShaker(Program program, {ClassHierarchy hierarchy, CoreTypes coreTypes}) 105 Library _mirrorsLibrary;
106
107 /// Set to true if any use of the `dart:mirrors` API is found.
108 bool isUsingMirrors = false;
109
110 TreeShaker(Program program,
111 {ClassHierarchy hierarchy, CoreTypes coreTypes, bool strongMode: false})
89 : this._internal(program, hierarchy ?? new ClassHierarchy(program), 112 : this._internal(program, hierarchy ?? new ClassHierarchy(program),
90 coreTypes ?? new CoreTypes(program)); 113 coreTypes ?? new CoreTypes(program), strongMode);
114
115 bool isMemberBodyUsed(Member member) {
116 return _usedMembers.containsKey(member);
117 }
118
119 bool isMemberOverridden(Member member) {
120 return _overriddenMembers.contains(member);
121 }
91 122
92 bool isMemberUsed(Member member) { 123 bool isMemberUsed(Member member) {
93 return _usedMembers.containsKey(member); 124 return isMemberBodyUsed(member) || isMemberOverridden(member);
94 } 125 }
95 126
96 bool isInstantiated(Class classNode) { 127 bool isInstantiated(Class classNode) {
97 return getClassRetention(classNode).index >= ClassRetention.Instance.index; 128 return getClassRetention(classNode).index >= ClassRetention.Instance.index;
98 } 129 }
99 130
100 bool isHierarchyUsed(Class classNode) { 131 bool isHierarchyUsed(Class classNode) {
101 return getClassRetention(classNode).index >= ClassRetention.Hierarchy.index; 132 return getClassRetention(classNode).index >= ClassRetention.Hierarchy.index;
102 } 133 }
103 134
104 ClassRetention getClassRetention(Class classNode) { 135 ClassRetention getClassRetention(Class classNode) {
105 int index = hierarchy.getClassIndex(classNode); 136 int index = hierarchy.getClassIndex(classNode);
106 return _classRetention[index]; 137 return _classRetention[index];
107 } 138 }
108 139
109 /// Applies the tree shaking results to the program. 140 /// Applies the tree shaking results to the program.
110 /// 141 ///
111 /// This removes unused classes, members, and hierarchy data. 142 /// This removes unused classes, members, and hierarchy data.
112 void transform(Program program) { 143 void transform(Program program) {
144 if (isUsingMirrors) return; // Give up if using mirrors.
113 new _TreeShakingTransformer(this).transform(program); 145 new _TreeShakingTransformer(this).transform(program);
114 } 146 }
115 147
116 TreeShaker._internal(this.program, ClassHierarchy hierarchy, this.coreTypes) 148 TreeShaker._internal(
149 this.program, ClassHierarchy hierarchy, this.coreTypes, this.strongMode)
117 : this.hierarchy = hierarchy, 150 : this.hierarchy = hierarchy,
151 this._dispatchedNames = new List<Set<Name>>(hierarchy.classes.length),
118 this._usedMembersWithHost = 152 this._usedMembersWithHost =
119 new List<Set<Member>>(hierarchy.classes.length), 153 new List<Set<Member>>(hierarchy.classes.length),
120 this._classRetention = new List<ClassRetention>.filled( 154 this._classRetention = new List<ClassRetention>.filled(
121 hierarchy.classes.length, ClassRetention.None) { 155 hierarchy.classes.length, ClassRetention.None) {
122 _visitor = new _TreeShakerVisitor(this); 156 _visitor = new _TreeShakerVisitor(this);
123 _covariantVisitor = new _ExternalTypeVisitor(this, isCovariant: true); 157 _covariantVisitor = new _ExternalTypeVisitor(this, isCovariant: true);
124 _contravariantVisitor = 158 _contravariantVisitor =
125 new _ExternalTypeVisitor(this, isContravariant: true); 159 new _ExternalTypeVisitor(this, isContravariant: true);
126 _bivariantVisitor = new _ExternalTypeVisitor(this, 160 _invariantVisitor = new _ExternalTypeVisitor(this,
127 isCovariant: true, isContravariant: true); 161 isCovariant: true, isContravariant: true);
128 _build(); 162 _mirrorsLibrary = coreTypes.getCoreLibrary('dart:mirrors');
163 try {
164 _build();
165 } on _UsingMirrorsException {
166 isUsingMirrors = true;
167 }
129 } 168 }
130 169
131 void _build() { 170 void _build() {
132 if (program.mainMethod == null) { 171 if (program.mainMethod == null) {
133 throw 'Cannot perform tree shaking on a program without a main method'; 172 throw 'Cannot perform tree shaking on a program without a main method';
134 } 173 }
135 if (program.mainMethod.function.positionalParameters.length > 0) { 174 if (program.mainMethod.function.positionalParameters.length > 0) {
136 // The main method takes a List<String> as argument. 175 // The main method takes a List<String> as argument.
137 _addInstantiatedExternalSubclass(coreTypes.listClass); 176 _addInstantiatedExternalSubclass(coreTypes.listClass);
138 _addInstantiatedExternalSubclass(coreTypes.stringClass); 177 _addInstantiatedExternalSubclass(coreTypes.stringClass);
139 } 178 }
140 _addDispatchedName(new Name('noSuchMethod')); 179 _addDispatchedName(hierarchy.rootClass, new Name('noSuchMethod'));
141 _addPervasiveUses(); 180 _addPervasiveUses();
142 _addUsedMember(null, program.mainMethod); 181 _addUsedMember(null, program.mainMethod);
143 _iterateWorklist(); 182 _iterateWorklist();
183
184 // Mark overridden members in order to preserve abstract members as
185 // necessary.
186 if (strongMode) {
187 for (int i = hierarchy.classes.length - 1; i >= 0; --i) {
188 Class class_ = hierarchy.classes[i];
189 if (isHierarchyUsed(class_)) {
190 hierarchy.forEachOverridePair(class_,
191 (Member ownMember, Member superMember, bool isSetter) {
192 if (isMemberBodyUsed(ownMember) ||
193 _overriddenMembers.contains(ownMember)) {
194 _overriddenMembers.add(superMember);
195 // Ensure the types mentioned in the member can be preserved.
196 _visitor.visitMemberInterface(superMember);
197 }
198 });
199 }
200 }
201 // Marking members as overridden should not cause new code to become
202 // reachable.
203 assert(_worklist.isEmpty);
204 }
144 } 205 }
145 206
146 /// Registers some extremely commonly used core classes as instantiated, so 207 /// Registers some extremely commonly used core classes as instantiated, so
147 /// we don't have to register them for every use we find. 208 /// we don't have to register them for every use we find.
148 void _addPervasiveUses() { 209 void _addPervasiveUses() {
149 _addInstantiatedExternalSubclass(coreTypes.stringClass); 210 _addInstantiatedExternalSubclass(coreTypes.stringClass);
150 _addInstantiatedExternalSubclass(coreTypes.intClass); 211 _addInstantiatedExternalSubclass(coreTypes.intClass);
151 _addInstantiatedExternalSubclass(coreTypes.boolClass); 212 _addInstantiatedExternalSubclass(coreTypes.boolClass);
152 _addInstantiatedExternalSubclass(coreTypes.nullClass); 213 _addInstantiatedExternalSubclass(coreTypes.nullClass);
214 _addInstantiatedExternalSubclass(coreTypes.functionClass);
215 _addInstantiatedExternalSubclass(coreTypes.invocationClass);
153 } 216 }
154 217
155 /// Registers the given name as seen in a dynamic dispatch, and discovers used 218 /// Registers the given name as seen in a dynamic dispatch, and discovers used
156 /// instance members accordingly. 219 /// instance members accordingly.
157 void _addDispatchedName(Name name) { 220 void _addDispatchedName(Class receiver, Name name) {
221 int index = hierarchy.getClassIndex(receiver);
222 Set<Name> receiverNames = _dispatchedNames[index] ??= new Set<Name>();
158 // TODO(asgerf): make use of selector arity and getter/setter kind 223 // TODO(asgerf): make use of selector arity and getter/setter kind
159 if (_dispatchedNames.add(name)) { 224 if (receiverNames.add(name)) {
160 List<TreeNode> targets = _dispatchTargetCandidates[name]; 225 List<TreeNode> candidates = _dispatchTargetCandidates[name];
161 if (targets != null) { 226 if (candidates != null) {
162 for (int i = 0; i < targets.length; i += 2) { 227 for (int i = 0; i < candidates.length; i += 2) {
163 _addUsedMember(targets[i], targets[i + 1]); 228 Class host = candidates[i];
229 if (hierarchy.isSubtypeOf(host, receiver)) {
230 // This (host, member) pair is a potential target of the dispatch.
231 Member member = candidates[i + 1];
232
233 // Remove the (host,member) pair from the candidate list.
234 // Move the last pair into the current index and shrink the list.
235 int lastPair = candidates.length - 2;
236 candidates[i] = candidates[lastPair];
237 candidates[i + 1] = candidates[lastPair + 1];
238 candidates.length -= 2;
239 i -= 2; // Revisit the same index now that it has been updated.
240
241 // Mark the pair as used. This should be done after removing it
242 // from the candidate list, since this call may recursively scan
243 // for more used members.
244 _addUsedMember(host, member);
245 }
164 } 246 }
165 } 247 }
248 var subtypes = hierarchy.getSubtypesOf(receiver);
249 var receiverSet = _receiversOfName[name];
250 _receiversOfName[name] = receiverSet == null
251 ? subtypes
252 : _receiversOfName[name].union(subtypes);
166 } 253 }
167 } 254 }
168 255
169 /// Registers the given method as a potential target of dynamic dispatch on 256 /// Registers the given method as a potential target of dynamic dispatch on
170 /// the given class. 257 /// the given class.
171 void _addDispatchTarget(Class host, Member member) { 258 void _addDispatchTarget(Class host, Member member) {
172 if (_dispatchedNames.contains(member.name)) { 259 ClassSet receivers = _receiversOfName[member.name];
260 if (receivers != null && receivers.contains(host)) {
173 _addUsedMember(host, member); 261 _addUsedMember(host, member);
174 } else { 262 } else {
175 _dispatchTargetCandidates.putIfAbsent(member.name, _makeTreeNodeList) 263 _dispatchTargetCandidates.putIfAbsent(member.name, _makeTreeNodeList)
176 ..add(host) 264 ..add(host)
177 ..add(member); 265 ..add(member);
178 } 266 }
179 } 267 }
180 268
181 static List<TreeNode> _makeTreeNodeList() => <TreeNode>[]; 269 static List<TreeNode> _makeTreeNodeList() => <TreeNode>[];
182 270
(...skipping 23 matching lines...) Expand all
206 if (oldRetention.index >= ClassRetention.ExternalInstance.index) { 294 if (oldRetention.index >= ClassRetention.ExternalInstance.index) {
207 return; 295 return;
208 } 296 }
209 _propagateClassInstanceLevel(classNode, oldRetention); 297 _propagateClassInstanceLevel(classNode, oldRetention);
210 for (Member member in hierarchy.getInterfaceMembers(classNode)) { 298 for (Member member in hierarchy.getInterfaceMembers(classNode)) {
211 if (member is Field) { 299 if (member is Field) {
212 _covariantVisitor.visit(member.type); 300 _covariantVisitor.visit(member.type);
213 } else { 301 } else {
214 _addCallToExternalProcedure(member); 302 _addCallToExternalProcedure(member);
215 } 303 }
304 _addDispatchTarget(classNode, member);
305 }
306 for (Member member
307 in hierarchy.getInterfaceMembers(classNode, setters: true)) {
308 _addDispatchTarget(classNode, member);
216 } 309 }
217 } 310 }
218 311
219 /// Called when the retention level for [classNode] has been raised from 312 /// Called when the retention level for [classNode] has been raised from
220 /// [oldRetention] to instance level. 313 /// [oldRetention] to instance level.
221 /// 314 ///
222 /// Ensures that the relevant members are put in the worklist, and super types 315 /// Ensures that the relevant members are put in the worklist, and super types
223 /// and raised to hierarchy level. 316 /// and raised to hierarchy level.
224 void _propagateClassInstanceLevel( 317 void _propagateClassInstanceLevel(
225 Class classNode, ClassRetention oldRetention) { 318 Class classNode, ClassRetention oldRetention) {
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
300 } 393 }
301 } 394 }
302 395
303 /// Registers the given member as being used, in the following sense: 396 /// Registers the given member as being used, in the following sense:
304 /// - Fields are used if they can be read or written or their initializer is 397 /// - Fields are used if they can be read or written or their initializer is
305 /// evaluated. 398 /// evaluated.
306 /// - Constructors are used if they can be invoked, either directly or through 399 /// - Constructors are used if they can be invoked, either directly or through
307 /// the initializer list of another constructor. 400 /// the initializer list of another constructor.
308 /// - Procedures are used if they can be invoked or torn off. 401 /// - Procedures are used if they can be invoked or torn off.
309 void _addUsedMember(Class host, Member member) { 402 void _addUsedMember(Class host, Member member) {
403 if (member.enclosingLibrary == _mirrorsLibrary) {
404 throw new _UsingMirrorsException();
405 }
310 if (host != null) { 406 if (host != null) {
311 // Check if the member has been seen with this host before. 407 // Check if the member has been seen with this host before.
312 int index = hierarchy.getClassIndex(host); 408 int index = hierarchy.getClassIndex(host);
313 Set<Member> members = _usedMembersWithHost[index] ??= new Set<Member>(); 409 Set<Member> members = _usedMembersWithHost[index] ??= new Set<Member>();
314 if (!members.add(member)) return; 410 if (!members.add(member)) return;
315 _usedMembers.putIfAbsent(member, _makeIncompleteSummary); 411 _usedMembers.putIfAbsent(member, _makeIncompleteSummary);
316 } else { 412 } else {
317 // Check if the member has been seen before. 413 // Check if the member has been seen before.
318 if (_usedMembers.containsKey(member)) return; 414 if (_usedMembers.containsKey(member)) return;
319 _usedMembers[member] = _makeIncompleteSummary(); 415 _usedMembers[member] = _makeIncompleteSummary();
(...skipping 22 matching lines...) Expand all
342 for (int i = 0; i < function.namedParameters.length; ++i) { 438 for (int i = 0; i < function.namedParameters.length; ++i) {
343 _contravariantVisitor.visit(function.namedParameters[i].type); 439 _contravariantVisitor.visit(function.namedParameters[i].type);
344 } 440 }
345 } 441 }
346 442
347 /// Called when external code may invoke the interface of the given class. 443 /// Called when external code may invoke the interface of the given class.
348 void _addEscapedClass(Class node) { 444 void _addEscapedClass(Class node) {
349 if (!_escapedClasses.add(node)) return; 445 if (!_escapedClasses.add(node)) return;
350 for (Member member in hierarchy.getInterfaceMembers(node)) { 446 for (Member member in hierarchy.getInterfaceMembers(node)) {
351 if (member is Procedure) { 447 if (member is Procedure) {
352 _addDispatchedName(member.name); 448 _addDispatchedName(node, member.name);
353 } 449 }
354 } 450 }
355 } 451 }
356 452
357 /// Creates a incomplete summary object, indicating that a member has not 453 /// Creates a incomplete summary object, indicating that a member has not
358 /// yet been analyzed. 454 /// yet been analyzed.
359 static List<Node> _makeIncompleteSummary() => <Node>[null]; 455 static List<Node> _makeIncompleteSummary() => <Node>[null];
360 456
361 bool isIncompleteSummary(List<Node> summary) { 457 bool isIncompleteSummary(List<Node> summary) {
362 return summary.isNotEmpty && summary[0] == null; 458 return summary.isNotEmpty && summary[0] == null;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
411 } 507 }
412 508
413 /// Sentinel that occurs in method summaries in front of each name that should 509 /// Sentinel that occurs in method summaries in front of each name that should
414 /// be interpreted as a setter. 510 /// be interpreted as a setter.
415 final Node _setterSentinel = const InvalidType(); 511 final Node _setterSentinel = const InvalidType();
416 512
417 /// Searches the AST for static references and dynamically dispatched names. 513 /// Searches the AST for static references and dynamically dispatched names.
418 class _TreeShakerVisitor extends RecursiveVisitor { 514 class _TreeShakerVisitor extends RecursiveVisitor {
419 final TreeShaker shaker; 515 final TreeShaker shaker;
420 final CoreTypes coreTypes; 516 final CoreTypes coreTypes;
517 final TypeEnvironment types;
518 final bool strongMode;
421 List<Node> summary; 519 List<Node> summary;
422 520
423 _TreeShakerVisitor(TreeShaker shaker) 521 _TreeShakerVisitor(TreeShaker shaker)
424 : this.shaker = shaker, 522 : this.shaker = shaker,
425 this.coreTypes = shaker.coreTypes; 523 this.coreTypes = shaker.coreTypes,
524 this.strongMode = shaker.strongMode,
525 this.types = new TypeEnvironment(shaker.coreTypes, shaker.hierarchy) {
526 types.errorHandler = handleError;
527 }
426 528
427 void analyzeAndBuildSummary(Node node, List<Node> summary) { 529 void handleError(TreeNode node, String message) {
530 print('[error] $message (${node.location})');
531 }
532
533 void analyzeAndBuildSummary(Member member, List<Node> summary) {
428 this.summary = summary; 534 this.summary = summary;
429 node.accept(this); 535 types.thisType = member.enclosingClass?.thisType;
536 member.accept(this);
537 }
538
539 void visitMemberInterface(Member node) {
540 if (node is Field) {
541 node.type.accept(this);
542 } else if (node is Procedure) {
543 visitFunctionInterface(node.function);
544 }
545 }
546
547 visitFunctionInterface(FunctionNode node) {
548 for (var parameter in node.typeParameters) {
549 parameter.bound.accept(this);
550 }
551 for (var parameter in node.positionalParameters) {
552 parameter.type.accept(this);
553 }
554 for (var parameter in node.namedParameters) {
555 parameter.type.accept(this);
556 }
557 node.returnType.accept(this);
430 } 558 }
431 559
432 @override 560 @override
433 visitFunctionNode(FunctionNode node) { 561 visitFunctionNode(FunctionNode node) {
434 switch (node.asyncMarker) { 562 switch (node.asyncMarker) {
435 case AsyncMarker.Sync: 563 case AsyncMarker.Sync:
436 break; 564 break;
437 case AsyncMarker.SyncStar: 565 case AsyncMarker.SyncStar:
438 shaker._addInstantiatedExternalSubclass(coreTypes.iterableClass); 566 shaker._addInstantiatedExternalSubclass(coreTypes.iterableClass);
439 break; 567 break;
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
497 @override 625 @override
498 visitDirectMethodInvocation(DirectMethodInvocation node) { 626 visitDirectMethodInvocation(DirectMethodInvocation node) {
499 if (node.receiver is! ThisExpression) { 627 if (node.receiver is! ThisExpression) {
500 // TODO(asgerf): Support arbitrary direct calls. 628 // TODO(asgerf): Support arbitrary direct calls.
501 throw 'Direct calls are only supported on "this"'; 629 throw 'Direct calls are only supported on "this"';
502 } 630 }
503 addUseFromCurrentHost(node.target); 631 addUseFromCurrentHost(node.target);
504 node.visitChildren(this); 632 node.visitChildren(this);
505 } 633 }
506 634
635 Class getKnownSupertype(DartType type) {
636 if (type is InterfaceType) {
637 return type.classNode;
638 } else if (type is TypeParameterType) {
639 return getKnownSupertype(type.parameter.bound);
640 } else if (type is FunctionType) {
641 return coreTypes.functionClass;
642 } else if (type is BottomType) {
643 return coreTypes.nullClass;
644 } else {
645 return coreTypes.objectClass;
646 }
647 }
648
649 Class getStaticType(Expression node) {
650 if (!strongMode) return coreTypes.objectClass;
651 return getKnownSupertype(node.getStaticType(types));
652 }
653
507 @override 654 @override
508 visitMethodInvocation(MethodInvocation node) { 655 visitMethodInvocation(MethodInvocation node) {
509 if (node.receiver is ThisExpression) { 656 if (node.receiver is ThisExpression) {
510 addSelfDispatch(node.name); 657 addSelfDispatch(node.name);
511 } else { 658 } else {
512 shaker._addDispatchedName(node.name); 659 shaker._addDispatchedName(getStaticType(node.receiver), node.name);
660 if (node.interfaceTarget != null) {
661 shaker._typedCalls.add(node);
662 }
513 } 663 }
514 node.visitChildren(this); 664 node.visitChildren(this);
515 } 665 }
516 666
517 @override 667 @override
518 visitStaticGet(StaticGet node) { 668 visitStaticGet(StaticGet node) {
519 addStaticUse(node.target); 669 addStaticUse(node.target);
520 node.visitChildren(this); 670 node.visitChildren(this);
521 } 671 }
522 672
(...skipping 21 matching lines...) Expand all
544 } 694 }
545 addUseFromCurrentHost(node.target); 695 addUseFromCurrentHost(node.target);
546 node.visitChildren(this); 696 node.visitChildren(this);
547 } 697 }
548 698
549 @override 699 @override
550 visitPropertyGet(PropertyGet node) { 700 visitPropertyGet(PropertyGet node) {
551 if (node.receiver is ThisExpression) { 701 if (node.receiver is ThisExpression) {
552 addSelfDispatch(node.name); 702 addSelfDispatch(node.name);
553 } else { 703 } else {
554 shaker._addDispatchedName(node.name); 704 shaker._addDispatchedName(getStaticType(node.receiver), node.name);
705 if (node.interfaceTarget != null) {
706 shaker._typedCalls.add(node);
707 }
555 } 708 }
556 node.visitChildren(this); 709 node.visitChildren(this);
557 } 710 }
558 711
559 @override 712 @override
560 visitPropertySet(PropertySet node) { 713 visitPropertySet(PropertySet node) {
561 if (node.receiver is ThisExpression) { 714 if (node.receiver is ThisExpression) {
562 addSelfDispatch(node.name, setter: true); 715 addSelfDispatch(node.name, setter: true);
563 } else { 716 } else {
564 shaker._addDispatchedName(node.name); 717 shaker._addDispatchedName(getStaticType(node.receiver), node.name);
718 if (node.interfaceTarget != null) {
719 shaker._typedCalls.add(node);
720 }
565 } 721 }
566 node.visitChildren(this); 722 node.visitChildren(this);
567 } 723 }
568 724
569 @override 725 @override
570 visitListLiteral(ListLiteral node) { 726 visitListLiteral(ListLiteral node) {
571 shaker._addInstantiatedExternalSubclass(coreTypes.listClass); 727 shaker._addInstantiatedExternalSubclass(coreTypes.listClass);
572 node.visitChildren(this); 728 node.visitChildren(this);
573 } 729 }
574 730
575 @override 731 @override
576 visitMapLiteral(MapLiteral node) { 732 visitMapLiteral(MapLiteral node) {
577 shaker._addInstantiatedExternalSubclass(coreTypes.mapClass); 733 shaker._addInstantiatedExternalSubclass(coreTypes.mapClass);
578 node.visitChildren(this); 734 node.visitChildren(this);
579 } 735 }
580 736
581 static final Name _toStringName = new Name('toString'); 737 static final Name _toStringName = new Name('toString');
582 738
583 @override 739 @override
584 visitStringConcatenation(StringConcatenation node) { 740 visitStringConcatenation(StringConcatenation node) {
585 shaker._addDispatchedName(_toStringName); 741 for (var expression in node.expressions) {
742 shaker._addDispatchedName(getStaticType(expression), _toStringName);
743 }
586 node.visitChildren(this); 744 node.visitChildren(this);
587 } 745 }
588 746
589 @override 747 @override
590 visitInterfaceType(InterfaceType node) { 748 visitInterfaceType(InterfaceType node) {
591 shaker._addClassUsedInType(node.classNode); 749 shaker._addClassUsedInType(node.classNode);
592 node.visitChildren(this); 750 node.visitChildren(this);
593 } 751 }
594 752
595 @override 753 @override
754 visitSupertype(Supertype node) {
755 shaker._addClassUsedInType(node.classNode);
756 node.visitChildren(this);
757 }
758
759 @override
596 visitDoubleLiteral(DoubleLiteral node) { 760 visitDoubleLiteral(DoubleLiteral node) {
597 shaker._addInstantiatedExternalSubclass(coreTypes.doubleClass); 761 shaker._addInstantiatedExternalSubclass(coreTypes.doubleClass);
598 } 762 }
599 763
600 @override 764 @override
601 visitSymbolLiteral(SymbolLiteral node) { 765 visitSymbolLiteral(SymbolLiteral node) {
602 shaker._addInstantiatedExternalSubclass(coreTypes.symbolClass); 766 shaker._addInstantiatedExternalSubclass(coreTypes.symbolClass);
603 // Note: we do not support 'dart:mirrors' right now, so nothing else needs
604 // to be done for symbols.
605 } 767 }
606 768
607 @override 769 @override
608 visitTypeLiteral(TypeLiteral node) { 770 visitTypeLiteral(TypeLiteral node) {
609 shaker._addInstantiatedExternalSubclass(coreTypes.typeClass); 771 shaker._addInstantiatedExternalSubclass(coreTypes.typeClass);
610 node.visitChildren(this); 772 node.visitChildren(this);
611 } 773 }
612 } 774 }
613 775
614 /// The degree to which a class is needed in a program. 776 /// The degree to which a class is needed in a program.
(...skipping 18 matching lines...) Expand all
633 } 795 }
634 796
635 /// Removes classes and members that are not needed. 797 /// Removes classes and members that are not needed.
636 /// 798 ///
637 /// There must not be any dangling references in the program afterwards. 799 /// There must not be any dangling references in the program afterwards.
638 class _TreeShakingTransformer extends Transformer { 800 class _TreeShakingTransformer extends Transformer {
639 final TreeShaker shaker; 801 final TreeShaker shaker;
640 802
641 _TreeShakingTransformer(this.shaker); 803 _TreeShakingTransformer(this.shaker);
642 804
805 Member _translateInterfaceTarget(Member target) {
806 return target != null && shaker.isMemberUsed(target) ? target : null;
807 }
808
643 void transform(Program program) { 809 void transform(Program program) {
644 for (var library in program.libraries) { 810 for (var library in program.libraries) {
645 if (library.importUri.scheme == 'dart') { 811 if (library.importUri.scheme == 'dart') {
646 // As long as patching happens in the backend, we cannot shake off 812 // The backend expects certain things to be present in the core
647 // anything in the core libraries. 813 // libraries, so we currently don't shake off anything there.
648 continue; 814 continue;
649 } 815 }
650 library.transformChildren(this); 816 library.transformChildren(this);
651 // Note: we can't shake off empty libraries yet since we don't check if 817 // Note: we can't shake off empty libraries yet since we don't check if
652 // there are private names that use the library. 818 // there are private names that use the library.
653 } 819 }
820 for (Expression node in shaker._typedCalls) {
821 // We should not leave dangling references, so if the target of a typed
822 // call has been removed, we must remove the reference. The receiver of
823 // such a call can only be null.
824 // TODO(asgerf): Rewrite to a NSM call instead of adding dynamic calls.
825 if (node is MethodInvocation) {
826 node.interfaceTarget = _translateInterfaceTarget(node.interfaceTarget);
827 } else if (node is PropertyGet) {
828 node.interfaceTarget = _translateInterfaceTarget(node.interfaceTarget);
829 } else if (node is PropertySet) {
830 node.interfaceTarget = _translateInterfaceTarget(node.interfaceTarget);
831 }
832 }
654 } 833 }
655 834
656 Class visitClass(Class node) { 835 Class visitClass(Class node) {
657 switch (shaker.getClassRetention(node)) { 836 switch (shaker.getClassRetention(node)) {
658 case ClassRetention.None: 837 case ClassRetention.None:
659 return null; // Remove the class. 838 return null; // Remove the class.
660 839
661 case ClassRetention.Namespace: 840 case ClassRetention.Namespace:
662 // The class is only a namespace for static members. Remove its 841 // The class is only a namespace for static members. Remove its
663 // hierarchy information. This is mandatory, since these references 842 // hierarchy information. This is mandatory, since these references
664 // might otherwise become dangling. 843 // might otherwise become dangling.
665 node.supertype = shaker.coreTypes.objectClass.asRawSupertype; 844 node.supertype = shaker.coreTypes.objectClass.asRawSupertype;
666 node.implementedTypes.clear(); 845 node.implementedTypes.clear();
667 node.typeParameters.clear(); 846 node.typeParameters.clear();
847 node.isAbstract = true;
668 // Mixin applications cannot have static members. 848 // Mixin applications cannot have static members.
669 assert(node.mixedInType == null); 849 assert(node.mixedInType == null);
670 // Unused members will be removed below. 850 // Unused members will be removed below.
671 break; 851 break;
672 852
673 case ClassRetention.Hierarchy: 853 case ClassRetention.Hierarchy:
854 node.isAbstract = true;
855 break;
856
674 case ClassRetention.Instance: 857 case ClassRetention.Instance:
675 case ClassRetention.ExternalInstance: 858 case ClassRetention.ExternalInstance:
676 break; 859 break;
677 } 860 }
678 node.transformChildren(this); 861 node.transformChildren(this);
679 if (node.constructors.isEmpty && node.procedures.isEmpty) {
680 // The VM does not like classes without any members, so ensure there is
681 // always a constructor left.
682 node.addMember(new Constructor(new FunctionNode(new EmptyStatement())));
683 }
684 return node; 862 return node;
685 } 863 }
686 864
687 Member defaultMember(Member node) { 865 Member defaultMember(Member node) {
688 if (!shaker.isMemberUsed(node)) { 866 if (!shaker.isMemberBodyUsed(node)) {
689 return null; // Remove unused member. 867 if (!shaker.isMemberOverridden(node)) {
868 return null;
869 }
870 if (node is Procedure) {
871 // Remove body of unused member.
872 if (node.enclosingClass.isAbstract) {
873 node.isAbstract = true;
874 node.function.body = null;
875 } else {
876 // If the enclosing class is not abstract, the method should still
877 // have a body even if it can never be called.
878 if (node.function.body != null) {
879 node.function.body = new InvalidStatement()..parent = node.function;
Kevin Millikin (Google) 2017/01/19 11:40:20 Is there something else we could put here than Inv
asgerf 2017/01/19 11:46:30 Changed to a 'throw'. Should make it easier to deb
880 }
881 }
882 node.function.asyncMarker = AsyncMarker.Sync;
883 } else if (node is Field) {
884 node.initializer = null;
885 }
690 } 886 }
691 return node; 887 return node;
692 } 888 }
693 889
694 TreeNode defaultTreeNode(TreeNode node) { 890 TreeNode defaultTreeNode(TreeNode node) {
695 return node; // Do not traverse into other nodes. 891 return node; // Do not traverse into other nodes.
696 } 892 }
697 } 893 }
698 894
699 class _ExternalTypeVisitor extends DartTypeVisitor { 895 class _ExternalTypeVisitor extends DartTypeVisitor {
(...skipping 13 matching lines...) Expand all
713 type?.accept(this); 909 type?.accept(this);
714 } else if (isContravariant) { 910 } else if (isContravariant) {
715 type?.accept(shaker._covariantVisitor); 911 type?.accept(shaker._covariantVisitor);
716 } else { 912 } else {
717 type?.accept(shaker._contravariantVisitor); 913 type?.accept(shaker._contravariantVisitor);
718 } 914 }
719 } 915 }
720 916
721 visitCovariant(DartType type) => type?.accept(this); 917 visitCovariant(DartType type) => type?.accept(this);
722 918
723 visitBivariant(DartType type) => shaker._bivariantVisitor.visit(type); 919 visitInvariant(DartType type) => shaker._invariantVisitor.visit(type);
724 920
725 visitInvalidType(InvalidType node) {} 921 visitInvalidType(InvalidType node) {}
726 922
727 visitDynamicType(DynamicType node) { 923 visitDynamicType(DynamicType node) {
728 // TODO(asgerf): Find a suitable model for untyped externals, e.g. track 924 // TODO(asgerf): Find a suitable model for untyped externals, e.g. track
729 // them to the first type boundary. 925 // them to the first type boundary.
730 } 926 }
731 927
732 visitVoidType(VoidType node) {} 928 visitVoidType(VoidType node) {}
733 929
734 visitInterfaceType(InterfaceType node) { 930 visitInterfaceType(InterfaceType node) {
735 if (isCovariant) { 931 if (isCovariant) {
736 shaker._addInstantiatedExternalSubclass(node.classNode); 932 shaker._addInstantiatedExternalSubclass(node.classNode);
737 } 933 }
738 if (isContravariant) { 934 if (isContravariant) {
739 shaker._addEscapedClass(node.classNode); 935 shaker._addEscapedClass(node.classNode);
740 } 936 }
741 for (int i = 0; i < node.typeArguments.length; ++i) { 937 for (int i = 0; i < node.typeArguments.length; ++i) {
742 DartType typeArgument = node.typeArguments[i]; 938 DartType typeArgument = node.typeArguments[i];
743 // In practice we don't get much out of analyzing variance here, so 939 // In practice we don't get much out of analyzing variance here, so
744 // just use a whitelist of classes that can be seen as covariant 940 // just use a whitelist of classes that can be seen as covariant
745 // for external purposes. 941 // for external purposes.
746 // TODO(asgerf): Variance analysis might pay off for other external APIs. 942 // TODO(asgerf): Variance analysis might pay off for other external APIs.
747 if (isWhitelistedCovariant(node.classNode)) { 943 if (isWhitelistedCovariant(node.classNode)) {
748 visitCovariant(typeArgument); 944 visitCovariant(typeArgument);
749 } else { 945 } else {
750 visitBivariant(typeArgument); 946 visitInvariant(typeArgument);
751 } 947 }
752 } 948 }
753 } 949 }
754 950
755 visitFunctionType(FunctionType node) { 951 visitFunctionType(FunctionType node) {
756 visit(node.returnType); 952 visit(node.returnType);
757 for (int i = 0; i < node.positionalParameters.length; ++i) { 953 for (int i = 0; i < node.positionalParameters.length; ++i) {
758 visitContravariant(node.positionalParameters[i]); 954 visitContravariant(node.positionalParameters[i]);
759 } 955 }
760 for (int i = 0; i < node.namedParameters.length; ++i) { 956 for (int i = 0; i < node.namedParameters.length; ++i) {
761 visitContravariant(node.namedParameters[i].type); 957 visitContravariant(node.namedParameters[i].type);
762 } 958 }
763 } 959 }
764 960
765 visitTypeParameterType(TypeParameterType node) {} 961 visitTypeParameterType(TypeParameterType node) {}
766 962
767 /// Just treat a couple of whitelisted classes as having covariant type 963 /// Just treat a couple of whitelisted classes as having covariant type
768 /// parameters. 964 /// parameters.
769 bool isWhitelistedCovariant(Class classNode) { 965 bool isWhitelistedCovariant(Class classNode) {
770 if (classNode.typeParameters.isEmpty) return false; 966 if (classNode.typeParameters.isEmpty) return false;
771 CoreTypes coreTypes = shaker.coreTypes; 967 CoreTypes coreTypes = shaker.coreTypes;
772 return classNode == coreTypes.iteratorClass || 968 return classNode == coreTypes.iteratorClass ||
773 classNode == coreTypes.iterableClass || 969 classNode == coreTypes.iterableClass ||
774 classNode == coreTypes.futureClass || 970 classNode == coreTypes.futureClass ||
775 classNode == coreTypes.streamClass || 971 classNode == coreTypes.streamClass ||
776 classNode == coreTypes.listClass || 972 classNode == coreTypes.listClass ||
777 classNode == coreTypes.mapClass; 973 classNode == coreTypes.mapClass;
778 } 974 }
779 } 975 }
976
977 /// Exception that is thrown to stop the tree shaking analysis when a use
978 /// of `dart:mirrors` is found.
979 class _UsingMirrorsException {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698