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