OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 universe.function_set; | 5 library universe.function_set; |
6 | 6 |
7 import '../common/names.dart' show Identifiers, Selectors; | 7 import '../common/names.dart' show Identifiers, Selectors; |
8 import '../elements/elements.dart' show MemberElement; | |
9 import '../elements/entities.dart'; | 8 import '../elements/entities.dart'; |
10 import '../types/types.dart'; | 9 import '../types/types.dart'; |
11 import '../util/util.dart' show Hashing, Setlet; | 10 import '../util/util.dart' show Hashing, Setlet; |
12 import '../world.dart' show ClosedWorld; | 11 import '../world.dart' show ClosedWorld; |
13 import 'selector.dart' show Selector; | 12 import 'selector.dart' show Selector; |
14 import 'world_builder.dart' show ReceiverConstraint; | 13 import 'world_builder.dart' show ReceiverConstraint; |
15 | 14 |
16 class FunctionSetBuilder { | 15 class FunctionSetBuilder { |
17 final Map<String, FunctionSetNode> nodes = new Map<String, FunctionSetNode>(); | 16 final Map<String, FunctionSetNode> nodes = new Map<String, FunctionSetNode>(); |
18 | 17 |
19 FunctionSetNode newNode(String name) => new FunctionSetNode(name); | 18 FunctionSetNode newNode(String name) => new FunctionSetNode(name); |
20 | 19 |
21 void add(MemberElement element) { | 20 void add(MemberEntity element) { |
22 assert(element.isInstanceMember); | 21 assert(element.isInstanceMember); |
23 assert(!element.isAbstract); | 22 assert(!element.isAbstract); |
24 String name = element.name; | 23 String name = element.name; |
25 FunctionSetNode node = nodes.putIfAbsent(name, () => newNode(name)); | 24 FunctionSetNode node = nodes.putIfAbsent(name, () => newNode(name)); |
26 node.add(element); | 25 node.add(element); |
27 } | 26 } |
28 | 27 |
29 void remove(MemberElement element) { | 28 void remove(MemberEntity element) { |
30 assert(element.isInstanceMember); | 29 assert(element.isInstanceMember); |
31 assert(!element.isAbstract); | 30 assert(!element.isAbstract); |
32 String name = element.name; | 31 String name = element.name; |
33 FunctionSetNode node = nodes[name]; | 32 FunctionSetNode node = nodes[name]; |
34 if (node != null) { | 33 if (node != null) { |
35 node.remove(element); | 34 node.remove(element); |
36 } | 35 } |
37 } | 36 } |
38 | 37 |
39 FunctionSet close(ClosedWorld closedWorld) { | 38 FunctionSet close(ClosedWorld closedWorld) { |
40 return new FunctionSet(closedWorld, nodes); | 39 return new FunctionSet(closedWorld, nodes); |
41 } | 40 } |
42 } | 41 } |
43 | 42 |
44 // TODO(kasperl): This actually holds getters and setters just fine | 43 // TODO(kasperl): This actually holds getters and setters just fine |
45 // too and stricly they aren't functions. Maybe this needs a better | 44 // too and stricly they aren't functions. Maybe this needs a better |
46 // name -- something like ElementSet seems a bit too generic. | 45 // name -- something like ElementSet seems a bit too generic. |
47 class FunctionSet { | 46 class FunctionSet { |
48 final ClosedWorld closedWorld; | 47 final ClosedWorld closedWorld; |
49 final Map<String, FunctionSetNode> nodes; | 48 final Map<String, FunctionSetNode> nodes; |
50 | 49 |
51 FunctionSet(this.closedWorld, this.nodes); | 50 FunctionSet(this.closedWorld, this.nodes); |
52 | 51 |
53 bool contains(MemberElement element) { | 52 bool contains(MemberEntity element) { |
54 assert(element.isInstanceMember); | 53 assert(element.isInstanceMember); |
55 assert(!element.isAbstract); | 54 assert(!element.isAbstract); |
56 String name = element.name; | 55 String name = element.name; |
57 FunctionSetNode node = nodes[name]; | 56 FunctionSetNode node = nodes[name]; |
58 return (node != null) ? node.contains(element) : false; | 57 return (node != null) ? node.contains(element) : false; |
59 } | 58 } |
60 | 59 |
61 /// Returns all the functions that may be invoked with the [selector] on a | 60 /// Returns all the functions that may be invoked with the [selector] on a |
62 /// receiver with the given [constraint]. The returned elements may include | 61 /// receiver with the given [constraint]. The returned elements may include |
63 /// noSuchMethod handlers that are potential targets indirectly through the | 62 /// noSuchMethod handlers that are potential targets indirectly through the |
64 /// noSuchMethod mechanism. | 63 /// noSuchMethod mechanism. |
65 Iterable<MemberElement> filter( | 64 Iterable<MemberEntity> filter( |
66 Selector selector, ReceiverConstraint constraint) { | 65 Selector selector, ReceiverConstraint constraint) { |
67 return query(selector, constraint).functions; | 66 return query(selector, constraint).functions; |
68 } | 67 } |
69 | 68 |
70 /// Returns the mask for the potential receivers of a dynamic call to | 69 /// Returns the mask for the potential receivers of a dynamic call to |
71 /// [selector] on [constraint]. | 70 /// [selector] on [constraint]. |
72 /// | 71 /// |
73 /// This will narrow the constraints of [constraint] to a [TypeMask] of the | 72 /// This will narrow the constraints of [constraint] to a [TypeMask] of the |
74 /// set of classes that actually implement the selected member or implement | 73 /// set of classes that actually implement the selected member or implement |
75 /// the handling 'noSuchMethod' where the selected member is unimplemented. | 74 /// the handling 'noSuchMethod' where the selected member is unimplemented. |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 /// selectors with the same [name]. | 154 /// selectors with the same [name]. |
156 class FunctionSetNode { | 155 class FunctionSetNode { |
157 final String name; | 156 final String name; |
158 final Map<SelectorMask, FunctionSetQuery> cache = | 157 final Map<SelectorMask, FunctionSetQuery> cache = |
159 <SelectorMask, FunctionSetQuery>{}; | 158 <SelectorMask, FunctionSetQuery>{}; |
160 | 159 |
161 // Initially, we keep the elements in a list because it is more | 160 // Initially, we keep the elements in a list because it is more |
162 // compact than a hash set. Once we get enough elements, we change | 161 // compact than a hash set. Once we get enough elements, we change |
163 // the representation to be a set to get faster contains checks. | 162 // the representation to be a set to get faster contains checks. |
164 static const int MAX_ELEMENTS_IN_LIST = 8; | 163 static const int MAX_ELEMENTS_IN_LIST = 8; |
165 var elements = <MemberElement>[]; | 164 var elements = <MemberEntity>[]; |
166 bool isList = true; | 165 bool isList = true; |
167 | 166 |
168 FunctionSetNode(this.name); | 167 FunctionSetNode(this.name); |
169 | 168 |
170 void add(MemberElement element) { | 169 void add(MemberEntity element) { |
171 assert(element.name == name); | 170 assert(element.name == name); |
172 // We try to avoid clearing the cache unless we have to. For that | 171 // We try to avoid clearing the cache unless we have to. For that |
173 // reason we keep the explicit contains check even though the add | 172 // reason we keep the explicit contains check even though the add |
174 // method ends up doing the work again (for sets). | 173 // method ends up doing the work again (for sets). |
175 if (!elements.contains(element)) { | 174 if (!elements.contains(element)) { |
176 if (isList && elements.length >= MAX_ELEMENTS_IN_LIST) { | 175 if (isList && elements.length >= MAX_ELEMENTS_IN_LIST) { |
177 elements = elements.toSet(); | 176 elements = elements.toSet(); |
178 isList = false; | 177 isList = false; |
179 } | 178 } |
180 elements.add(element); | 179 elements.add(element); |
181 if (!cache.isEmpty) cache.clear(); | 180 if (!cache.isEmpty) cache.clear(); |
182 } | 181 } |
183 } | 182 } |
184 | 183 |
185 void remove(MemberElement element) { | 184 void remove(MemberEntity element) { |
186 assert(element.name == name); | 185 assert(element.name == name); |
187 if (isList) { | 186 if (isList) { |
188 List list = elements; | 187 List list = elements; |
189 int index = list.indexOf(element); | 188 int index = list.indexOf(element); |
190 if (index < 0) return; | 189 if (index < 0) return; |
191 MemberElement last = list.removeLast(); | 190 MemberEntity last = list.removeLast(); |
192 if (index != list.length) { | 191 if (index != list.length) { |
193 list[index] = last; | 192 list[index] = last; |
194 } | 193 } |
195 if (!cache.isEmpty) cache.clear(); | 194 if (!cache.isEmpty) cache.clear(); |
196 } else { | 195 } else { |
197 Set set = elements; | 196 Set set = elements; |
198 if (set.remove(element)) { | 197 if (set.remove(element)) { |
199 // To avoid wobbling between the two representations, we do | 198 // To avoid wobbling between the two representations, we do |
200 // not transition back to the list representation even if we | 199 // not transition back to the list representation even if we |
201 // end up with few enough elements at this point. | 200 // end up with few enough elements at this point. |
202 if (!cache.isEmpty) cache.clear(); | 201 if (!cache.isEmpty) cache.clear(); |
203 } | 202 } |
204 } | 203 } |
205 } | 204 } |
206 | 205 |
207 bool contains(MemberElement element) { | 206 bool contains(MemberEntity element) { |
208 assert(element.name == name); | 207 assert(element.name == name); |
209 return elements.contains(element); | 208 return elements.contains(element); |
210 } | 209 } |
211 | 210 |
212 void forEach(Function action) { | 211 void forEach(Function action) { |
213 elements.forEach(action); | 212 elements.forEach(action); |
214 } | 213 } |
215 | 214 |
216 /// Returns the set of functions that can be the target of [selectorMask] | 215 /// Returns the set of functions that can be the target of [selectorMask] |
217 /// including no such method handling where applicable. | 216 /// including no such method handling where applicable. |
218 FunctionSetQuery query(SelectorMask selectorMask, ClosedWorld closedWorld, | 217 FunctionSetQuery query(SelectorMask selectorMask, ClosedWorld closedWorld, |
219 [FunctionSetNode noSuchMethods, SelectorMask noSuchMethodMask]) { | 218 [FunctionSetNode noSuchMethods, SelectorMask noSuchMethodMask]) { |
220 assert(selectorMask.name == name); | 219 assert(selectorMask.name == name); |
221 FunctionSetQuery result = cache[selectorMask]; | 220 FunctionSetQuery result = cache[selectorMask]; |
222 if (result != null) return result; | 221 if (result != null) return result; |
223 | 222 |
224 Setlet<MemberElement> functions; | 223 Setlet<MemberEntity> functions; |
225 for (MemberElement element in elements) { | 224 for (MemberEntity element in elements) { |
226 if (selectorMask.applies(element, closedWorld)) { | 225 if (selectorMask.applies(element, closedWorld)) { |
227 if (functions == null) { | 226 if (functions == null) { |
228 // Defer the allocation of the functions set until we are | 227 // Defer the allocation of the functions set until we are |
229 // sure we need it. This allows us to return immutable empty | 228 // sure we need it. This allows us to return immutable empty |
230 // lists when the filtering produced no results. | 229 // lists when the filtering produced no results. |
231 functions = new Setlet<MemberElement>(); | 230 functions = new Setlet<MemberEntity>(); |
232 } | 231 } |
233 functions.add(element); | 232 functions.add(element); |
234 } | 233 } |
235 } | 234 } |
236 | 235 |
237 // If we cannot ensure a method will be found at runtime, we also | 236 // If we cannot ensure a method will be found at runtime, we also |
238 // add [noSuchMethod] implementations that apply to [mask] as | 237 // add [noSuchMethod] implementations that apply to [mask] as |
239 // potential targets. | 238 // potential targets. |
240 if (noSuchMethods != null && | 239 if (noSuchMethods != null && |
241 selectorMask.needsNoSuchMethodHandling(closedWorld)) { | 240 selectorMask.needsNoSuchMethodHandling(closedWorld)) { |
242 FunctionSetQuery noSuchMethodQuery = | 241 FunctionSetQuery noSuchMethodQuery = |
243 noSuchMethods.query(noSuchMethodMask, closedWorld); | 242 noSuchMethods.query(noSuchMethodMask, closedWorld); |
244 if (!noSuchMethodQuery.functions.isEmpty) { | 243 if (!noSuchMethodQuery.functions.isEmpty) { |
245 if (functions == null) { | 244 if (functions == null) { |
246 functions = | 245 functions = |
247 new Setlet<MemberElement>.from(noSuchMethodQuery.functions); | 246 new Setlet<MemberEntity>.from(noSuchMethodQuery.functions); |
248 } else { | 247 } else { |
249 functions.addAll(noSuchMethodQuery.functions); | 248 functions.addAll(noSuchMethodQuery.functions); |
250 } | 249 } |
251 } | 250 } |
252 } | 251 } |
253 cache[selectorMask] = result = (functions != null) | 252 cache[selectorMask] = result = (functions != null) |
254 ? new FullFunctionSetQuery(functions) | 253 ? new FullFunctionSetQuery(functions) |
255 : const EmptyFunctionSetQuery(); | 254 : const EmptyFunctionSetQuery(); |
256 return result; | 255 return result; |
257 } | 256 } |
258 } | 257 } |
259 | 258 |
260 /// A set of functions that are the potential targets of all call sites sharing | 259 /// A set of functions that are the potential targets of all call sites sharing |
261 /// the same receiver mask and selector. | 260 /// the same receiver mask and selector. |
262 abstract class FunctionSetQuery { | 261 abstract class FunctionSetQuery { |
263 const FunctionSetQuery(); | 262 const FunctionSetQuery(); |
264 | 263 |
265 /// Compute the type of all potential receivers of this function set. | 264 /// Compute the type of all potential receivers of this function set. |
266 TypeMask computeMask(ClosedWorld closedWorld); | 265 TypeMask computeMask(ClosedWorld closedWorld); |
267 | 266 |
268 /// Returns all potential targets of this function set. | 267 /// Returns all potential targets of this function set. |
269 Iterable<MemberElement> get functions; | 268 Iterable<MemberEntity> get functions; |
270 } | 269 } |
271 | 270 |
272 class EmptyFunctionSetQuery implements FunctionSetQuery { | 271 class EmptyFunctionSetQuery implements FunctionSetQuery { |
273 const EmptyFunctionSetQuery(); | 272 const EmptyFunctionSetQuery(); |
274 | 273 |
275 @override | 274 @override |
276 TypeMask computeMask(ClosedWorld closedWorld) => | 275 TypeMask computeMask(ClosedWorld closedWorld) => |
277 const TypeMask.nonNullEmpty(); | 276 const TypeMask.nonNullEmpty(); |
278 | 277 |
279 @override | 278 @override |
280 Iterable<MemberElement> get functions => const <MemberElement>[]; | 279 Iterable<MemberEntity> get functions => const <MemberEntity>[]; |
281 } | 280 } |
282 | 281 |
283 class FullFunctionSetQuery implements FunctionSetQuery { | 282 class FullFunctionSetQuery implements FunctionSetQuery { |
284 @override | 283 @override |
285 final Iterable<MemberElement> functions; | 284 final Iterable<MemberEntity> functions; |
286 | 285 |
287 TypeMask _mask; | 286 TypeMask _mask; |
288 | 287 |
289 FullFunctionSetQuery(this.functions); | 288 FullFunctionSetQuery(this.functions); |
290 | 289 |
291 @override | 290 @override |
292 TypeMask computeMask(ClosedWorld closedWorld) { | 291 TypeMask computeMask(ClosedWorld closedWorld) { |
293 assert(closedWorld | 292 assert(closedWorld |
294 .hasAnyStrictSubclass(closedWorld.commonElements.objectClass)); | 293 .hasAnyStrictSubclass(closedWorld.commonElements.objectClass)); |
295 if (_mask != null) return _mask; | 294 if (_mask != null) return _mask; |
296 return _mask = new TypeMask.unionOf( | 295 return _mask = new TypeMask.unionOf( |
297 functions.expand((MemberElement element) { | 296 functions.expand((MemberEntity element) { |
298 ClassEntity cls = element.enclosingClass; | 297 ClassEntity cls = element.enclosingClass; |
299 return [cls]..addAll(closedWorld.mixinUsesOf(cls)); | 298 return [cls]..addAll(closedWorld.mixinUsesOf(cls)); |
300 }).map((cls) { | 299 }).map((cls) { |
301 if (closedWorld.backendClasses.nullClass == cls) { | 300 if (closedWorld.backendClasses.nullClass == cls) { |
302 return const TypeMask.empty(); | 301 return const TypeMask.empty(); |
303 } else if (closedWorld.isInstantiated(cls)) { | 302 } else if (closedWorld.isInstantiated(cls)) { |
304 return new TypeMask.nonNullSubclass(cls, closedWorld); | 303 return new TypeMask.nonNullSubclass(cls, closedWorld); |
305 } else { | 304 } else { |
306 // TODO(johnniwinther): Avoid the need for this case. | 305 // TODO(johnniwinther): Avoid the need for this case. |
307 return const TypeMask.empty(); | 306 return const TypeMask.empty(); |
308 } | 307 } |
309 }), | 308 }), |
310 closedWorld); | 309 closedWorld); |
311 } | 310 } |
312 } | 311 } |
OLD | NEW |