Chromium Code Reviews| 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 part of universe; | 5 part of universe; |
| 6 | 6 |
| 7 // TODO(kasperl): It seems possible to rewrite this class to be more | 7 class SelectorMap<T> { |
| 8 // like the FunctionSet abstraction which is a lot simpler. | 8 final Compiler compiler; |
| 9 class SelectorMap<T> extends PartialTypeTree { | 9 final Map<SourceString, SelectorMapNode<T>> nodes = |
| 10 | 10 new Map<SourceString, SelectorMapNode<T>>(); |
| 11 SelectorMap(Compiler compiler) : super(compiler); | 11 SelectorMap(this.compiler); |
| 12 | |
| 13 SelectorMapNode<T> newNode(ClassElement type) => new SelectorMapNode<T>(type); | |
| 14 | 12 |
| 15 T operator [](Selector selector) { | 13 T operator [](Selector selector) { |
| 16 SelectorMapNode<T> node = findNode(selectorType(selector), false); | 14 SourceString name = selector.name; |
| 17 if (node == null) return null; | 15 SelectorMapNode node = nodes[name]; |
| 18 Link<SelectorValue<T>> selectors = node.selectorsByName[selector.name]; | 16 return (node != null) |
| 19 if (selectors == null) return null; | 17 ? node.lookup(selector) |
| 20 for (Link link = selectors; !link.isEmpty; link = link.tail) { | 18 : null; |
|
ngeoffray
2013/02/14 10:14:33
fits in one line?
| |
| 21 SelectorValue<T> existing = link.head; | |
| 22 if (existing.selector.equalsUntyped(selector)) return existing.value; | |
| 23 } | |
| 24 return null; | |
| 25 } | 19 } |
| 26 | 20 |
| 27 // TODO(kasperl): Do we need to support removing selectors by | |
| 28 // passing null as the value? | |
| 29 void operator []=(Selector selector, T value) { | 21 void operator []=(Selector selector, T value) { |
| 30 ClassElement type = selectorType(selector); | |
| 31 SelectorMapNode<T> node = findNode(type, true); | |
| 32 SourceString name = selector.name; | 22 SourceString name = selector.name; |
| 33 Link<SelectorValue<T>> selectors = node.selectorsByName.putIfAbsent( | 23 SelectorMapNode node = nodes.putIfAbsent( |
| 34 name, () => const Link()); | 24 name, () => new SelectorMapNode(name)); |
| 35 // Run through the linked list of selectors with the same name. If | 25 node.update(selector, value); |
| 36 // we find one that matches, we update the value in the mapping. | |
| 37 for (Link link = selectors; !link.isEmpty; link = link.tail) { | |
| 38 SelectorValue<T> existing = link.head; | |
| 39 // It is safe to ignore the type here, because all selector | |
| 40 // mappings that are stored in a single node have the same type. | |
| 41 if (existing.selector.equalsUntyped(selector)) { | |
| 42 existing.value = value; | |
| 43 return; | |
| 44 } | |
| 45 } | |
| 46 // We could not find an existing mapping for the selector, so | |
| 47 // we add a new one to the existing linked list. | |
| 48 SelectorValue<T> head = new SelectorValue<T>(selector, value); | |
| 49 node.selectorsByName[name] = selectors.prepend(head); | |
| 50 } | 26 } |
| 51 | 27 |
| 52 // TODO(kasperl): Share code with the [] operator? | |
| 53 bool containsKey(Selector selector) { | 28 bool containsKey(Selector selector) { |
| 54 SelectorMapNode<T> node = findNode(selectorType(selector), false); | 29 SourceString name = selector.name; |
| 55 if (node == null) return false; | 30 SelectorMapNode node = nodes[name]; |
| 56 Link<SelectorValue<T>> selectors = node.selectorsByName[selector.name]; | 31 return (node != null) |
| 57 if (selectors == null) return false; | 32 ? node.containsKey(selector) |
| 58 for (Link link = selectors; !link.isEmpty; link = link.tail) { | 33 : false; |
| 59 SelectorValue<T> existing = link.head; | 34 } |
| 60 if (existing.selector.equalsUntyped(selector)) return true; | 35 |
| 61 } | 36 T remove(Selector selector) { |
| 62 return false; | 37 SourceString name = selector.name; |
| 38 SelectorMapNode node = nodes[name]; | |
| 39 return (node != null) | |
| 40 ? node.remove(selector) | |
| 41 : null; | |
| 63 } | 42 } |
| 64 | 43 |
| 65 /** | 44 /** |
| 66 * Visits all mappings for selectors that may be used to invoke the | 45 * Visits all mappings for selectors that may be used to invoke the |
| 67 * given [member] element. If the [visit] function ever returns false, | 46 * given [member] element. If the [visit] function ever returns false, |
| 68 * we abort the traversal early. | 47 * we abort the traversal early. |
| 69 */ | 48 */ |
| 70 void visitMatching(Element member, bool visit(Selector selector, T value)) { | 49 void visitMatching(Element member, bool visit(Selector selector, T value)) { |
| 71 assert(member.isMember()); | 50 assert(member.isMember()); |
| 72 if (root == null) return; | 51 SourceString name = member.name; |
| 73 // TODO(kasperl): Use visitHierachyMatching when possible. It is | 52 SelectorMapNode node = nodes[name]; |
| 74 // currently broken in subtle ways when it comes to finding typed | 53 if (node != null) { |
| 75 // selectors where we only know the interface of the receiver. | 54 node.visitMatching(member, compiler, visit); |
| 76 visitAllMatching(member, visit); | 55 } |
| 56 } | |
| 57 } | |
| 58 | |
| 59 class SelectorMapNode<T> { | |
| 60 final SourceString name; | |
| 61 final Map<Selector, T> selectors = new Map<Selector, T>(); | |
| 62 | |
| 63 // We start caching which selectors match which elements when the | |
| 64 // number of different selectors exceed a threshold. This way we | |
| 65 // avoid lots of repeated calls to the Selector.applies method. | |
| 66 static const int MAX_SELECTORS_NO_CACHE = 8; | |
| 67 Map<Element, List<Selector>> cache; | |
| 68 | |
| 69 SelectorMapNode(this.name); | |
| 70 | |
| 71 T lookup(Selector selector) { | |
| 72 assert(selector.name == name); | |
| 73 return selectors[selector]; | |
| 77 } | 74 } |
| 78 | 75 |
| 79 void visitAllMatching(Element member, bool visit(selector, value)) { | 76 void update(Selector selector, T value) { |
| 80 root.visitRecursively((SelectorMapNode<T> node) { | 77 assert(selector.name == name); |
| 81 Link<SelectorValue<T>> selectors = node.selectorsByName[member.name]; | 78 bool existing = selectors.containsKey(selector); |
| 82 if (selectors == null) return true; | 79 selectors[selector] = value; |
| 83 for (Link link = selectors; !link.isEmpty; link = link.tail) { | 80 if (existing) return; |
| 84 SelectorValue<T> existing = link.head; | 81 // The update has introduced a new selector in the map, so we need |
| 85 Selector selector = existing.selector; | 82 // to consider if we should start caching. At the very least, we |
| 86 // Since we're running through the entire tree we have to use | 83 // have to clear the cache because the new element may invalidate |
| 87 // the applies method that takes types into account. | 84 // existing cache entries. |
| 88 if (selector.appliesUnnamed(member, compiler)) { | 85 if (cache == null) { |
| 89 if (!visit(selector, existing.value)) return false; | 86 if (selectors.length > MAX_SELECTORS_NO_CACHE) { |
| 90 } | 87 cache = new Map<Element, List<Selector>>(); |
| 91 } | 88 } |
| 92 return true; | 89 } else if (!cache.isEmpty) { |
|
ngeoffray
2013/02/14 10:14:33
Remove this check?
| |
| 93 }); | 90 cache.clear(); |
| 91 } | |
| 94 } | 92 } |
| 95 | 93 |
| 96 void visitHierarchyMatching(Element member, bool visit(selector, value)) { | 94 bool containsKey(Selector selector) { |
| 97 visitHierarchy(member.getEnclosingClass(), (SelectorMapNode<T> node) { | 95 assert(selector.name == name); |
| 98 Link<SelectorValue<T>> selectors = node.selectorsByName[member.name]; | 96 return selectors.containsKey(selector); |
| 99 if (selectors == null) return true; | |
| 100 for (Link link = selectors; !link.isEmpty; link = link.tail) { | |
| 101 SelectorValue<T> existing = link.head; | |
| 102 Selector selector = existing.selector; | |
| 103 if (selector.appliesUntyped(member, compiler)) { | |
| 104 if (!visit(selector, existing.value)) return false; | |
| 105 } | |
| 106 } | |
| 107 return true; | |
| 108 }); | |
| 109 } | 97 } |
| 110 | 98 |
| 99 T remove(Selector selector) { | |
| 100 assert(selector.name == name); | |
| 101 if (!selectors.containsKey(selector)) return null; | |
| 102 if (cache != null && !cache.isEmpty) cache.clear(); | |
|
ngeoffray
2013/02/14 10:14:33
Remove this check?
| |
| 103 return selectors.remove(selector); | |
| 104 } | |
| 105 | |
| 106 void visitMatching(Element member, Compiler compiler, | |
| 107 bool visit(Selector selector, T value)) { | |
| 108 assert(member.name == name); | |
| 109 Iterable<Selector> matching = computeMatching(member, compiler); | |
| 110 for (Selector selector in matching) { | |
| 111 if (!visit(selector, selectors[selector])) return; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 Iterable<Selector> computeMatching(Element member, Compiler compiler) { | |
| 116 // Probe the cache if it exists. Do this before creating the | |
| 117 // matching iterable to cut down on the overhead for cache hits. | |
|
ngeoffray
2013/02/14 10:14:33
Not sure I understand the last sentence. Looks lik
| |
| 118 if (cache != null) { | |
| 119 List<Selector> cached = cache[member]; | |
| 120 if (cached != null) return cached; | |
| 121 } | |
| 122 // Filter the selectors keys so we only have the ones that apply | |
| 123 // to the given member element. | |
| 124 Iterable<Selector> matching = selectors.keys.where( | |
| 125 (Selector selector) => selector.appliesUnnamed(member, compiler)); | |
| 126 if (cache == null) return matching; | |
| 127 return cache[member] = matching.toList(); | |
| 128 } | |
| 111 } | 129 } |
| 112 | |
| 113 class SelectorMapNode<T> extends PartialTypeTreeNode { | |
| 114 final Map<SourceString, Link<SelectorValue<T>>> selectorsByName = | |
| 115 new Map<SourceString, Link<SelectorValue<T>>>(); | |
| 116 SelectorMapNode(ClassElement type) : super(type); | |
| 117 } | |
| 118 | |
| 119 class SelectorValue<T> { | |
| 120 final Selector selector; | |
| 121 T value; | |
| 122 SelectorValue(this.selector, this.value); | |
| 123 toString() => "$selector -> $value"; | |
| 124 } | |
| OLD | NEW |