OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of universe; | |
6 | |
7 // TODO(kasperl): This actually holds getters and setters just fine | |
8 // too and stricly they aren't functions. Maybe this needs a better | |
9 // name -- something like ElementSet seems a bit too generic. | |
10 class FunctionSet { | |
11 final Compiler compiler; | |
12 final Map<String, FunctionSetNode> nodes = | |
13 new Map<String, FunctionSetNode>(); | |
14 FunctionSet(this.compiler); | |
15 | |
16 FunctionSetNode newNode(String name) | |
17 => new FunctionSetNode(name); | |
18 | |
19 void add(Element element) { | |
20 assert(element.isInstanceMember); | |
21 assert(!element.isAbstract); | |
22 String name = element.name; | |
23 FunctionSetNode node = nodes.putIfAbsent(name, () => newNode(name)); | |
24 node.add(element); | |
25 } | |
26 | |
27 void remove(Element element) { | |
28 assert(element.isInstanceMember); | |
29 assert(!element.isAbstract); | |
30 String name = element.name; | |
31 FunctionSetNode node = nodes[name]; | |
32 if (node != null) { | |
33 node.remove(element); | |
34 } | |
35 } | |
36 | |
37 bool contains(Element element) { | |
38 assert(element.isInstanceMember); | |
39 assert(!element.isAbstract); | |
40 String name = element.name; | |
41 FunctionSetNode node = nodes[name]; | |
42 return (node != null) | |
43 ? node.contains(element) | |
44 : false; | |
45 } | |
46 | |
47 /** | |
48 * Returns an object that allows iterating over all the functions | |
49 * that may be invoked with the given [selector]. | |
50 */ | |
51 Iterable<Element> filter(Selector selector) { | |
52 return query(selector).functions; | |
53 } | |
54 | |
55 TypeMask receiverType(Selector selector) { | |
56 return query(selector).computeMask(compiler.world); | |
57 } | |
58 | |
59 FunctionSetQuery query(Selector selector) { | |
60 String name = selector.name; | |
61 FunctionSetNode node = nodes[name]; | |
62 FunctionSetNode noSuchMethods = nodes[Compiler.NO_SUCH_METHOD]; | |
63 if (node != null) { | |
64 return node.query(selector, compiler, noSuchMethods); | |
65 } | |
66 // If there is no method that matches [selector] we know we can | |
67 // only hit [:noSuchMethod:]. | |
68 if (noSuchMethods == null) return const FunctionSetQuery(const <Element>[]); | |
69 selector = (selector.mask == null) | |
70 ? compiler.noSuchMethodSelector | |
71 : new TypedSelector(selector.mask, compiler.noSuchMethodSelector, | |
72 compiler.world); | |
73 | |
74 return noSuchMethods.query(selector, compiler, null); | |
75 } | |
76 | |
77 void forEach(Function action) { | |
78 nodes.forEach((String name, FunctionSetNode node) { | |
79 node.forEach(action); | |
80 }); | |
81 } | |
82 } | |
83 | |
84 | |
85 class FunctionSetNode { | |
86 final String name; | |
87 final Map<Selector, FunctionSetQuery> cache = | |
88 new Map<Selector, FunctionSetQuery>(); | |
89 | |
90 // Initially, we keep the elements in a list because it is more | |
91 // compact than a hash set. Once we get enough elements, we change | |
92 // the representation to be a set to get faster contains checks. | |
93 static const int MAX_ELEMENTS_IN_LIST = 8; | |
94 var elements = <Element>[]; | |
95 bool isList = true; | |
96 | |
97 FunctionSetNode(this.name); | |
98 | |
99 void add(Element element) { | |
100 assert(element.name == name); | |
101 // We try to avoid clearing the cache unless we have to. For that | |
102 // reason we keep the explicit contains check even though the add | |
103 // method ends up doing the work again (for sets). | |
104 if (!elements.contains(element)) { | |
105 if (isList && elements.length >= MAX_ELEMENTS_IN_LIST) { | |
106 elements = elements.toSet(); | |
107 isList = false; | |
108 } | |
109 elements.add(element); | |
110 if (!cache.isEmpty) cache.clear(); | |
111 } | |
112 } | |
113 | |
114 void remove(Element element) { | |
115 assert(element.name == name); | |
116 if (isList) { | |
117 List list = elements; | |
118 int index = list.indexOf(element); | |
119 if (index < 0) return; | |
120 Element last = list.removeLast(); | |
121 if (index != list.length) { | |
122 list[index] = last; | |
123 } | |
124 if (!cache.isEmpty) cache.clear(); | |
125 } else { | |
126 Set set = elements; | |
127 if (set.remove(element)) { | |
128 // To avoid wobbling between the two representations, we do | |
129 // not transition back to the list representation even if we | |
130 // end up with few enough elements at this point. | |
131 if (!cache.isEmpty) cache.clear(); | |
132 } | |
133 } | |
134 } | |
135 | |
136 bool contains(Element element) { | |
137 assert(element.name == name); | |
138 return elements.contains(element); | |
139 } | |
140 | |
141 void forEach(Function action) { | |
142 elements.forEach(action); | |
143 } | |
144 | |
145 TypeMask getNonNullTypeMaskOfSelector(Selector selector, Compiler compiler) { | |
146 // TODO(ngeoffray): We should probably change untyped selector | |
147 // to always be a subclass of Object. | |
148 return selector.mask != null | |
149 ? selector.mask | |
150 : new TypeMask.subclass(compiler.objectClass, compiler.world); | |
151 } | |
152 | |
153 FunctionSetQuery query(Selector selector, | |
154 Compiler compiler, | |
155 FunctionSetNode noSuchMethods) { | |
156 ClassWorld classWorld = compiler.world; | |
157 assert(selector.name == name); | |
158 FunctionSetQuery result = cache[selector]; | |
159 if (result != null) return result; | |
160 Setlet<Element> functions; | |
161 for (Element element in elements) { | |
162 if (selector.appliesUnnamed(element, classWorld)) { | |
163 if (functions == null) { | |
164 // Defer the allocation of the functions set until we are | |
165 // sure we need it. This allows us to return immutable empty | |
166 // lists when the filtering produced no results. | |
167 functions = new Setlet<Element>(); | |
168 } | |
169 functions.add(element); | |
170 } | |
171 } | |
172 | |
173 TypeMask mask = getNonNullTypeMaskOfSelector(selector, compiler); | |
174 // If we cannot ensure a method will be found at runtime, we also | |
175 // add [noSuchMethod] implementations that apply to [mask] as | |
176 // potential targets. | |
177 if (noSuchMethods != null | |
178 && mask.needsNoSuchMethodHandling(selector, classWorld)) { | |
179 FunctionSetQuery noSuchMethodQuery = noSuchMethods.query( | |
180 new TypedSelector( | |
181 mask, compiler.noSuchMethodSelector, classWorld), | |
182 compiler, | |
183 null); | |
184 if (!noSuchMethodQuery.functions.isEmpty) { | |
185 if (functions == null) { | |
186 functions = new Setlet<Element>.from(noSuchMethodQuery.functions); | |
187 } else { | |
188 functions.addAll(noSuchMethodQuery.functions); | |
189 } | |
190 } | |
191 } | |
192 cache[selector] = result = (functions != null) | |
193 ? newQuery(functions, selector, compiler) | |
194 : const FunctionSetQuery(const <Element>[]); | |
195 return result; | |
196 } | |
197 | |
198 FunctionSetQuery newQuery(Iterable<Element> functions, | |
199 Selector selector, | |
200 Compiler compiler) { | |
201 return new FullFunctionSetQuery(functions); | |
202 } | |
203 } | |
204 | |
205 class FunctionSetQuery { | |
206 final Iterable<Element> functions; | |
207 TypeMask computeMask(ClassWorld classWorld) => const TypeMask.nonNullEmpty(); | |
208 const FunctionSetQuery(this.functions); | |
209 } | |
210 | |
211 class FullFunctionSetQuery extends FunctionSetQuery { | |
212 TypeMask _mask; | |
213 | |
214 /** | |
215 * Compute the type of all potential receivers of this function set. | |
216 */ | |
217 TypeMask computeMask(ClassWorld classWorld) { | |
218 assert(classWorld.hasAnySubclass(classWorld.objectClass)); | |
219 if (_mask != null) return _mask; | |
220 return _mask = new TypeMask.unionOf(functions | |
221 .expand((element) { | |
222 ClassElement cls = element.enclosingClass; | |
223 return [cls]..addAll(classWorld.mixinUsesOf(cls)); | |
224 }) | |
225 .map((cls) { | |
226 if (classWorld.backend.isNullImplementation(cls)) { | |
227 return const TypeMask.empty(); | |
228 } else { | |
229 return new TypeMask.nonNullSubclass(cls.declaration, classWorld); | |
230 } | |
231 }), | |
232 classWorld); | |
233 } | |
234 | |
235 FullFunctionSetQuery(functions) : super(functions); | |
236 } | |
OLD | NEW |