OLD | NEW |
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 import '../type_environment.dart'; |
| 11 import '../library_index.dart'; |
11 | 12 |
12 Program transformProgram(Program program, {List<ProgramRoot> programRoots}) { | 13 Program transformProgram(Program program, {List<ProgramRoot> programRoots}) { |
13 new TreeShaker(program, programRoots: programRoots).transform(program); | 14 new TreeShaker(program, programRoots: programRoots).transform(program); |
14 return program; | 15 return program; |
15 } | 16 } |
16 | 17 |
17 enum ProgramRootKind { | 18 enum ProgramRootKind { |
18 /// The root is a class which will be instantiated by | 19 /// The root is a class which will be instantiated by |
19 /// external / non-Dart code. | 20 /// external / non-Dart code. |
20 ExternallyInstantiatedClass, | 21 ExternallyInstantiatedClass, |
(...skipping 21 matching lines...) Expand all Loading... |
42 | 43 |
43 /// The name of the member inside the library (or class, optional). | 44 /// The name of the member inside the library (or class, optional). |
44 final String member; | 45 final String member; |
45 | 46 |
46 /// The kind of this program root. | 47 /// The kind of this program root. |
47 final ProgramRootKind kind; | 48 final ProgramRootKind kind; |
48 | 49 |
49 ProgramRoot(this.library, this.klass, this.member, this.kind); | 50 ProgramRoot(this.library, this.klass, this.member, this.kind); |
50 | 51 |
51 String toString() => "ProgramRoot($library, $klass, $member, $kind)"; | 52 String toString() => "ProgramRoot($library, $klass, $member, $kind)"; |
| 53 |
| 54 String get disambiguatedName { |
| 55 if (kind == ProgramRootKind.Getter) return 'get:$member'; |
| 56 if (kind == ProgramRootKind.Setter) return 'set:$member'; |
| 57 return member; |
| 58 } |
| 59 |
| 60 Member getMember(LibraryIndex table) { |
| 61 assert(klass != null); |
| 62 assert(member != null); |
| 63 return table.getMember( |
| 64 library, klass ?? LibraryIndex.topLevel, disambiguatedName); |
| 65 } |
| 66 |
| 67 Class getClass(LibraryIndex table) { |
| 68 assert(klass != null); |
| 69 return table.getClass(library, klass); |
| 70 } |
52 } | 71 } |
53 | 72 |
54 /// Tree shaking based on class hierarchy analysis. | 73 /// Tree shaking based on class hierarchy analysis. |
55 /// | 74 /// |
56 /// Any dynamic dispatch not on `this` is conservatively assumed to target | 75 /// Any dynamic dispatch not on `this` is conservatively assumed to target |
57 /// any instantiated class that implements a member matching the selector. | 76 /// any instantiated class that implements a member matching the selector. |
58 /// | 77 /// |
59 /// Member bodies are analyzed relative to a given "host class" which is the | 78 /// Member bodies are analyzed relative to a given "host class" which is the |
60 /// concrete type of `this` (or null if in static context), so dispatches on | 79 /// concrete type of `this` (or null if in static context), so dispatches on |
61 /// `this` can be resolved more precisely. | 80 /// `this` can be resolved more precisely. |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
196 this._usedMembersWithHost = | 215 this._usedMembersWithHost = |
197 new List<Set<Member>>(hierarchy.classes.length), | 216 new List<Set<Member>>(hierarchy.classes.length), |
198 this._classRetention = new List<ClassRetention>.filled( | 217 this._classRetention = new List<ClassRetention>.filled( |
199 hierarchy.classes.length, ClassRetention.None) { | 218 hierarchy.classes.length, ClassRetention.None) { |
200 _visitor = new _TreeShakerVisitor(this); | 219 _visitor = new _TreeShakerVisitor(this); |
201 _covariantVisitor = new _ExternalTypeVisitor(this, isCovariant: true); | 220 _covariantVisitor = new _ExternalTypeVisitor(this, isCovariant: true); |
202 _contravariantVisitor = | 221 _contravariantVisitor = |
203 new _ExternalTypeVisitor(this, isContravariant: true); | 222 new _ExternalTypeVisitor(this, isContravariant: true); |
204 _invariantVisitor = new _ExternalTypeVisitor(this, | 223 _invariantVisitor = new _ExternalTypeVisitor(this, |
205 isCovariant: true, isContravariant: true); | 224 isCovariant: true, isContravariant: true); |
206 _mirrorsLibrary = coreTypes.getCoreLibrary('dart:mirrors'); | 225 _mirrorsLibrary = coreTypes.tryGetLibrary('dart:mirrors'); |
207 try { | 226 try { |
208 _build(); | 227 _build(); |
209 } on _UsingMirrorsException { | 228 } on _UsingMirrorsException { |
210 isUsingMirrors = true; | 229 isUsingMirrors = true; |
211 } | 230 } |
212 } | 231 } |
213 | 232 |
214 void _build() { | 233 void _build() { |
215 if (program.mainMethod == null) { | 234 if (program.mainMethod == null) { |
216 throw 'Cannot perform tree shaking on a program without a main method'; | 235 throw 'Cannot perform tree shaking on a program without a main method'; |
217 } | 236 } |
218 if (program.mainMethod.function.positionalParameters.length > 0) { | 237 if (program.mainMethod.function.positionalParameters.length > 0) { |
219 // The main method takes a List<String> as argument. | 238 // The main method takes a List<String> as argument. |
220 _addInstantiatedExternalSubclass(coreTypes.listClass); | 239 _addInstantiatedExternalSubclass(coreTypes.listClass); |
221 _addInstantiatedExternalSubclass(coreTypes.stringClass); | 240 _addInstantiatedExternalSubclass(coreTypes.stringClass); |
222 } | 241 } |
223 _addDispatchedName(hierarchy.rootClass, new Name('noSuchMethod')); | 242 _addDispatchedName(hierarchy.rootClass, new Name('noSuchMethod')); |
224 _addPervasiveUses(); | 243 _addPervasiveUses(); |
225 _addUsedMember(null, program.mainMethod); | 244 _addUsedMember(null, program.mainMethod); |
226 programRoots?.forEach(_addUsedRoot); | 245 if (programRoots != null) { |
| 246 var table = new LibraryIndex(program, programRoots.map((r) => r.library)); |
| 247 for (var root in programRoots) { |
| 248 _addUsedRoot(root, table); |
| 249 } |
| 250 } |
227 | 251 |
228 _iterateWorklist(); | 252 _iterateWorklist(); |
229 | 253 |
230 // Mark overridden members in order to preserve abstract members as | 254 // Mark overridden members in order to preserve abstract members as |
231 // necessary. | 255 // necessary. |
232 if (strongMode) { | 256 if (strongMode) { |
233 for (int i = hierarchy.classes.length - 1; i >= 0; --i) { | 257 for (int i = hierarchy.classes.length - 1; i >= 0; --i) { |
234 Class class_ = hierarchy.classes[i]; | 258 Class class_ = hierarchy.classes[i]; |
235 if (isHierarchyUsed(class_)) { | 259 if (isHierarchyUsed(class_)) { |
236 hierarchy.forEachOverridePair(class_, | 260 hierarchy.forEachOverridePair(class_, |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
410 /// Ensures that all annotations on the class are analyzed. | 434 /// Ensures that all annotations on the class are analyzed. |
411 void _propagateClassNamespaceLevel( | 435 void _propagateClassNamespaceLevel( |
412 Class classNode, ClassRetention oldRetention) { | 436 Class classNode, ClassRetention oldRetention) { |
413 if (oldRetention.index >= ClassRetention.Namespace.index) { | 437 if (oldRetention.index >= ClassRetention.Namespace.index) { |
414 return; | 438 return; |
415 } | 439 } |
416 visitList(classNode.annotations, _visitor); | 440 visitList(classNode.annotations, _visitor); |
417 } | 441 } |
418 | 442 |
419 /// Registers the given root as being used. | 443 /// Registers the given root as being used. |
420 void _addUsedRoot(ProgramRoot root) { | 444 void _addUsedRoot(ProgramRoot root, LibraryIndex table) { |
421 Library rootLibrary = _findLibraryRoot(root, program); | |
422 | |
423 if (root.kind == ProgramRootKind.ExternallyInstantiatedClass) { | 445 if (root.kind == ProgramRootKind.ExternallyInstantiatedClass) { |
424 Class rootClass = _findClassRoot(root, rootLibrary); | 446 Class class_ = root.getClass(table); |
425 | 447 |
426 // This is a class which will be instantiated by non-Dart code (whether it | 448 // This is a class which will be instantiated by non-Dart code (whether it |
427 // has a valid generative construtor or not). | 449 // has a valid generative construtor or not). |
428 _addInstantiatedClass(rootClass); | 450 _addInstantiatedClass(class_); |
429 | 451 |
430 // We keep all the constructors of externally instantiated classes. | 452 // We keep all the constructors of externally instantiated classes. |
431 // Sometimes the runtime might do a constructor call and sometimes it | 453 // Sometimes the runtime might do a constructor call and sometimes it |
432 // might just allocate the class without invoking the constructor. | 454 // might just allocate the class without invoking the constructor. |
433 // So we try to be on the safe side here! | 455 // So we try to be on the safe side here! |
434 for (var constructor in rootClass.constructors) { | 456 for (var constructor in class_.constructors) { |
435 _addUsedMember(rootClass, constructor); | 457 _addUsedMember(class_, constructor); |
436 } | 458 } |
437 | 459 |
438 // We keep all factory constructors as well for the same reason. | 460 // We keep all factory constructors as well for the same reason. |
439 for (var member in rootClass.procedures) { | 461 for (var member in class_.procedures) { |
440 if (member.isStatic && member.kind == ProcedureKind.Factory) { | 462 if (member.isStatic && member.kind == ProcedureKind.Factory) { |
441 _addUsedMember(rootClass, member); | 463 _addUsedMember(class_, member); |
442 } | 464 } |
443 } | 465 } |
444 } else { | 466 } else { |
445 if (root.klass != null) { | 467 var member = root.getMember(table); |
446 // For class members we mark the Field/Procedure/Constructor as used. | 468 _addUsedMember(member.enclosingClass, member); |
447 // We also mark it as instantiated if it's a constructor. | 469 if (member is Constructor) { |
448 Class rootClass = _findClassRoot(root, rootLibrary); | 470 _addInstantiatedClass(member.enclosingClass); |
449 Member rootMember = _findMemberRoot(root, rootClass.members); | |
450 _addUsedMember(rootClass, rootMember); | |
451 if (rootMember is Constructor) { | |
452 _addInstantiatedClass(rootClass); | |
453 } | |
454 } else { | |
455 // For library members we mark the Field/Procedure as used. | |
456 Member rootMember = _findMemberRoot(root, rootLibrary.members); | |
457 _addUsedMember(null, rootMember); | |
458 } | 471 } |
459 } | 472 } |
460 } | 473 } |
461 | 474 |
462 /// Registers the given class as being used in a type annotation. | 475 /// Registers the given class as being used in a type annotation. |
463 void _addClassUsedInType(Class classNode) { | 476 void _addClassUsedInType(Class classNode) { |
464 int index = hierarchy.getClassIndex(classNode); | 477 int index = hierarchy.getClassIndex(classNode); |
465 ClassRetention retention = _classRetention[index]; | 478 ClassRetention retention = _classRetention[index]; |
466 if (retention.index < ClassRetention.Hierarchy.index) { | 479 if (retention.index < ClassRetention.Hierarchy.index) { |
467 _classRetention[index] = ClassRetention.Hierarchy; | 480 _classRetention[index] = ClassRetention.Hierarchy; |
(...skipping 595 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1063 classNode == coreTypes.futureClass || | 1076 classNode == coreTypes.futureClass || |
1064 classNode == coreTypes.streamClass || | 1077 classNode == coreTypes.streamClass || |
1065 classNode == coreTypes.listClass || | 1078 classNode == coreTypes.listClass || |
1066 classNode == coreTypes.mapClass; | 1079 classNode == coreTypes.mapClass; |
1067 } | 1080 } |
1068 } | 1081 } |
1069 | 1082 |
1070 /// Exception that is thrown to stop the tree shaking analysis when a use | 1083 /// Exception that is thrown to stop the tree shaking analysis when a use |
1071 /// of `dart:mirrors` is found. | 1084 /// of `dart:mirrors` is found. |
1072 class _UsingMirrorsException {} | 1085 class _UsingMirrorsException {} |
1073 | |
1074 Library _findLibraryRoot(ProgramRoot root, Program program) { | |
1075 for (var library in program.libraries) { | |
1076 if (library.importUri.toString() == root.library) { | |
1077 return library; | |
1078 } | |
1079 } | |
1080 | |
1081 throw "$root not found!"; | |
1082 } | |
1083 | |
1084 Class _findClassRoot(ProgramRoot root, Library rootLibrary) { | |
1085 for (var klass in rootLibrary.classes) { | |
1086 if (klass.name == root.klass) { | |
1087 return klass; | |
1088 } | |
1089 } | |
1090 throw "$root not found!"; | |
1091 } | |
1092 | |
1093 Member _findMemberRoot(ProgramRoot root, Iterable<Member> membersToSearch) { | |
1094 for (var member in membersToSearch) { | |
1095 if (member.name.name == root.member) { | |
1096 switch (root.kind) { | |
1097 case ProgramRootKind.Constructor: | |
1098 if (member is Procedure && member.kind == ProcedureKind.Factory || | |
1099 member is Constructor) { | |
1100 return member; | |
1101 } | |
1102 break; | |
1103 case ProgramRootKind.Setter: | |
1104 if (member is Procedure && member.kind == ProcedureKind.Setter || | |
1105 member is Field) { | |
1106 return member; | |
1107 } | |
1108 break; | |
1109 case ProgramRootKind.Getter: | |
1110 if (member is Procedure && member.kind == ProcedureKind.Getter || | |
1111 member is Field) { | |
1112 return member; | |
1113 } | |
1114 break; | |
1115 case ProgramRootKind.Other: | |
1116 return member; | |
1117 default: | |
1118 } | |
1119 } | |
1120 } | |
1121 throw "$root not found!"; | |
1122 } | |
OLD | NEW |