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

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