| 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 '../lookup_table.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(LookupTable table) { |
| 61 assert(klass != null); |
| 62 assert(member != null); |
| 63 return table.getMember( |
| 64 library, klass ?? LookupTable.topLevel, disambiguatedName); |
| 65 } |
| 66 |
| 67 Class getClass(LookupTable 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.getLibrary('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 LookupTable(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, LookupTable 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 |