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 |