| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
| 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.md file. | |
| 4 | |
| 5 library fletchc.dynamic_call_enqueuer; | |
| 6 | |
| 7 import 'dart:collection' show | |
| 8 Queue; | |
| 9 | |
| 10 import 'package:compiler/src/universe/selector.dart' show | |
| 11 Selector; | |
| 12 | |
| 13 import 'package:compiler/src/universe/use.dart' show | |
| 14 DynamicUse; | |
| 15 | |
| 16 import 'package:compiler/src/dart_types.dart' show | |
| 17 DartType, | |
| 18 InterfaceType, | |
| 19 TypeKind; | |
| 20 | |
| 21 import 'package:compiler/src/elements/elements.dart' show | |
| 22 ClassElement, | |
| 23 Element, | |
| 24 FunctionElement, | |
| 25 LibraryElement, | |
| 26 MemberElement, | |
| 27 Name; | |
| 28 | |
| 29 import 'package:compiler/src/common/names.dart' show | |
| 30 Identifiers, | |
| 31 Names; | |
| 32 | |
| 33 import 'package:compiler/src/util/util.dart' show | |
| 34 Hashing; | |
| 35 | |
| 36 import 'fletch_compiler_implementation.dart' show | |
| 37 FletchCompilerImplementation; | |
| 38 | |
| 39 import 'fletch_enqueuer.dart' show | |
| 40 shouldReportEnqueuingOfElement; | |
| 41 | |
| 42 import 'fletch_registry.dart' show | |
| 43 ClosureKind; | |
| 44 | |
| 45 abstract class UsageRecorder { | |
| 46 void recordElementUsage(Element element, Selector selector); | |
| 47 | |
| 48 void recordClosurizationUsage(Closurization closurization, Selector selector); | |
| 49 | |
| 50 void recordTypeTest(ClassElement element, InterfaceType type); | |
| 51 } | |
| 52 | |
| 53 /// Implements the dynamic part of the tree-shaking algorithm. | |
| 54 /// | |
| 55 /// By "dynamic" part we mean the part that is about matching instantiated | |
| 56 /// classes with called instance methods. | |
| 57 class DynamicCallEnqueuer { | |
| 58 final FletchCompilerImplementation compiler; | |
| 59 | |
| 60 final Set<ClassElement> instantiatedClasses = new Set<ClassElement>(); | |
| 61 | |
| 62 final Queue<ClassElement> pendingInstantiatedClasses = | |
| 63 new Queue<ClassElement>(); | |
| 64 | |
| 65 final Set<Selector> enqueuedSelectors = new Set<Selector>(); | |
| 66 | |
| 67 final Queue<Selector> pendingSelectors = new Queue<Selector>(); | |
| 68 | |
| 69 /// Set of functions that have been closurized | |
| 70 final Set<Closurization> implicitClosurizations = new Set<Closurization>(); | |
| 71 | |
| 72 /// Queue of functions that have been closurized and have yet to be | |
| 73 /// processed | |
| 74 final Queue<Closurization> pendingImplicitClosurizations = | |
| 75 new Queue<Closurization>(); | |
| 76 | |
| 77 final Set<InterfaceType> typeTests = new Set<InterfaceType>(); | |
| 78 | |
| 79 final Queue<InterfaceType> pendingTypeTests = new Queue<InterfaceType>(); | |
| 80 | |
| 81 DynamicCallEnqueuer(this.compiler); | |
| 82 | |
| 83 void registerInstantiatedType(InterfaceType type) { | |
| 84 ClassElement cls = type.element.declaration; | |
| 85 if (instantiatedClasses.add(cls)) { | |
| 86 pendingInstantiatedClasses.addLast(cls); | |
| 87 } | |
| 88 } | |
| 89 | |
| 90 void enqueueApplicableMembers( | |
| 91 ClassElement cls, | |
| 92 Selector selector, | |
| 93 UsageRecorder recorder) { | |
| 94 Element member = cls.lookupByName(selector.memberName); | |
| 95 if (member == null) return; | |
| 96 if (!member.isInstanceMember) return; | |
| 97 if (selector.isGetter) { | |
| 98 if (member.isField || member.isGetter) { | |
| 99 recorder.recordElementUsage(member, selector); | |
| 100 } else { | |
| 101 // Tear-off. | |
| 102 compiler.reportVerboseInfo(member, "enqueued as tear-off"); | |
| 103 // This lets the backend generate the tear-off getter because it is | |
| 104 // told that [member] is used as a getter. | |
| 105 recorder.recordElementUsage(member, selector); | |
| 106 // This registers [member] as an instantiated closure class. | |
| 107 enqueueClosure(member, ClosureKind.tearOff); | |
| 108 } | |
| 109 } else if (selector.isSetter) { | |
| 110 if (member.isField || member.isSetter) { | |
| 111 recorder.recordElementUsage(member, selector); | |
| 112 } | |
| 113 } else if (member.isFunction && selector.signatureApplies(member)) { | |
| 114 recorder.recordElementUsage(member, selector); | |
| 115 } else if (member.isGetter || member.isField) { | |
| 116 // A getter/field can be invoked as a method, for example, if the getter | |
| 117 // returns a closure. | |
| 118 recorder.recordElementUsage(member, selector); | |
| 119 } | |
| 120 } | |
| 121 | |
| 122 void enqueueClosureIfApplicable( | |
| 123 Closurization closurization, | |
| 124 Selector selector, | |
| 125 UsageRecorder recorder) { | |
| 126 FunctionElement function = closurization.function; | |
| 127 if ((selector.isGetter || selector.isCall) && | |
| 128 selector.memberName == Names.call && | |
| 129 selector.signatureApplies(function)) { | |
| 130 recorder.recordClosurizationUsage(closurization, selector); | |
| 131 } | |
| 132 } | |
| 133 | |
| 134 void enqueueApplicableTypeTests( | |
| 135 ClassElement cls, | |
| 136 InterfaceType type, | |
| 137 UsageRecorder recorder) { | |
| 138 if (cls == type.element) { | |
| 139 recorder.recordTypeTest(cls, type); | |
| 140 return; | |
| 141 } | |
| 142 for (DartType supertype in cls.allSupertypes) { | |
| 143 if (supertype.element == type.element) { | |
| 144 recorder.recordTypeTest(cls, type); | |
| 145 return; | |
| 146 } | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 void enqueueInstanceMethods(UsageRecorder recorder) { | |
| 151 // TODO(ahe): Implement a faster way to iterate through selectors. For | |
| 152 // example, use the same approach as dart2js uses where selectors are | |
| 153 // grouped by name. This applies both to enqueuedSelectors and | |
| 154 // pendingSelectors. | |
| 155 while (pendingInstantiatedClasses.isNotEmpty) { | |
| 156 ClassElement cls = pendingInstantiatedClasses.removeFirst(); | |
| 157 if (shouldReportEnqueuingOfElement(compiler, cls)) { | |
| 158 compiler.reportVerboseInfo(cls, "was instantiated"); | |
| 159 } | |
| 160 for (Selector selector in enqueuedSelectors) { | |
| 161 // TODO(ahe): As we iterate over enqueuedSelectors, we may end up | |
| 162 // processing calling _enqueueApplicableMembers twice for newly | |
| 163 // instantiated classes. Once here, and then once more in the while | |
| 164 // loop below. | |
| 165 enqueueApplicableMembers(cls, selector, recorder); | |
| 166 } | |
| 167 for (InterfaceType type in typeTests) { | |
| 168 enqueueApplicableTypeTests(cls, type, recorder); | |
| 169 } | |
| 170 } | |
| 171 while (pendingImplicitClosurizations.isNotEmpty) { | |
| 172 Closurization closurization = pendingImplicitClosurizations.removeFirst(); | |
| 173 if (shouldReportEnqueuingOfElement(compiler, closurization.function)) { | |
| 174 compiler.reportVerboseInfo(closurization.function, "was closurized"); | |
| 175 } | |
| 176 for (Selector selector in enqueuedSelectors) { | |
| 177 enqueueClosureIfApplicable(closurization, selector, recorder); | |
| 178 } | |
| 179 // TODO(ahe): Also enqueue type tests here. | |
| 180 } | |
| 181 while (pendingSelectors.isNotEmpty) { | |
| 182 Selector selector = pendingSelectors.removeFirst(); | |
| 183 for (ClassElement cls in instantiatedClasses) { | |
| 184 enqueueApplicableMembers(cls, selector, recorder); | |
| 185 } | |
| 186 for (Closurization closurization in implicitClosurizations) { | |
| 187 enqueueClosureIfApplicable(closurization, selector, recorder); | |
| 188 } | |
| 189 } | |
| 190 while(!pendingTypeTests.isEmpty) { | |
| 191 InterfaceType type = pendingTypeTests.removeFirst(); | |
| 192 for (ClassElement cls in instantiatedClasses) { | |
| 193 enqueueApplicableTypeTests(cls, type, recorder); | |
| 194 } | |
| 195 // TODO(ahe): Also enqueue type tests for closures. | |
| 196 } | |
| 197 } | |
| 198 | |
| 199 void enqueueSelector(DynamicUse use) { | |
| 200 assert(use.mask == null); | |
| 201 Selector selector = use.selector; | |
| 202 if (enqueuedSelectors.add(selector)) { | |
| 203 pendingSelectors.add(selector); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 void enqueueClosure(FunctionElement function, ClosureKind kind) { | |
| 208 Closurization closurization = new Closurization(function, kind); | |
| 209 if (implicitClosurizations.add(closurization)) { | |
| 210 pendingImplicitClosurizations.add(closurization); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 void forgetElement(Element element) { | |
| 215 void revisitClass(ClassElement cls) { | |
| 216 if (instantiatedClasses.remove(cls)) { | |
| 217 // [cls] was already instantiated, now we need to make sure enqueue its | |
| 218 // members again in [enqueueInstanceMethods] above. | |
| 219 pendingInstantiatedClasses.add(cls); | |
| 220 } | |
| 221 } | |
| 222 if (!instantiatedClasses.remove(element)) { | |
| 223 // If a class is removed, the incremental compiler will first forget its | |
| 224 // members. This can move the class to pendingInstantiatedClasses. | |
| 225 pendingInstantiatedClasses.remove(element); | |
| 226 } | |
| 227 if (element.isInstanceMember) { | |
| 228 ClassElement modifiedClass = element.enclosingClass; | |
| 229 revisitClass(modifiedClass); | |
| 230 MemberElement member = element; | |
| 231 for (ClassElement cls in instantiatedClasses) { | |
| 232 // TODO(ahe): Make O(1). | |
| 233 if (cls.lookupByName(member.memberName) == member) { | |
| 234 revisitClass(cls); | |
| 235 // Once we have found one class that implements [member], we're | |
| 236 // done. When we later call [enqueueInstanceMethods] (via | |
| 237 // [FletchEnqueuer.processQueue]) the method will be enqueued again | |
| 238 // (if it exists). | |
| 239 break; | |
| 240 } | |
| 241 } | |
| 242 } | |
| 243 List<Closurization> toBeRemoved = <Closurization>[]; | |
| 244 for (Closurization closurization in implicitClosurizations) { | |
| 245 if (closurization.function == element) { | |
| 246 toBeRemoved.add(closurization); | |
| 247 } | |
| 248 } | |
| 249 implicitClosurizations.removeAll(toBeRemoved); | |
| 250 } | |
| 251 | |
| 252 void enqueueTypeTest(DartType type) { | |
| 253 type = type.asRaw(); | |
| 254 switch (type.kind) { | |
| 255 case TypeKind.INTERFACE: | |
| 256 enqueueRawInterfaceType(type); | |
| 257 break; | |
| 258 | |
| 259 default: | |
| 260 // Ignored. | |
| 261 break; | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 void enqueueRawInterfaceType(InterfaceType type) { | |
| 266 assert(type.isRaw); | |
| 267 if (typeTests.add(type)) { | |
| 268 pendingTypeTests.add(type); | |
| 269 } | |
| 270 } | |
| 271 } | |
| 272 | |
| 273 /// Track that [function] is used as a closure. This can happen for various | |
| 274 /// reasons, see [ClosureKind] for more details. In practical terms, this means | |
| 275 /// that we need a class whose `call` method invokes [function]. For | |
| 276 /// [ClosureKind.functionLike], the class is a normal class with source | |
| 277 /// code. For most other kinds, the class is synthetic and doesn't have a | |
| 278 /// corresponding element. | |
| 279 class Closurization { | |
| 280 final FunctionElement function; | |
| 281 | |
| 282 final ClosureKind kind; | |
| 283 | |
| 284 final int hashCode; | |
| 285 | |
| 286 Closurization(FunctionElement function, ClosureKind kind) | |
| 287 : function = function, | |
| 288 kind = kind, | |
| 289 hashCode = Hashing.mixHashCodeBits(function.hashCode, kind.hashCode); | |
| 290 | |
| 291 bool operator ==(other) { | |
| 292 return other is Closurization && | |
| 293 function == other.function && kind == other.kind; | |
| 294 } | |
| 295 | |
| 296 String toString() => "Closurization($function, $kind)"; | |
| 297 } | |
| OLD | NEW |