blob: 29e705ceb1e0ea87d9965e8ffbce2ecef3903278 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of universe;
class SelectorMap<T> {
final Compiler compiler;
final Map<SourceString, SelectorMapNode<T>> nodes =
new Map<SourceString, SelectorMapNode<T>>();
SelectorMap(this.compiler);
T operator [](Selector selector) {
SourceString name = selector.name;
SelectorMapNode node = nodes[name];
return (node != null)
? node.lookup(selector)
: null;
}
void operator []=(Selector selector, T value) {
SourceString name = selector.name;
SelectorMapNode node = nodes.putIfAbsent(
name, () => new SelectorMapNode(name));
node.update(selector, value);
}
bool containsKey(Selector selector) {
SourceString name = selector.name;
SelectorMapNode node = nodes[name];
return (node != null)
? node.containsKey(selector)
: false;
}
T remove(Selector selector) {
SourceString name = selector.name;
SelectorMapNode node = nodes[name];
return (node != null)
? node.remove(selector)
: null;
}
/**
* Visits all mappings for selectors that may be used to invoke the
* given [member] element. If the [visit] function ever returns false,
* we abort the traversal early.
*/
void visitMatching(Element member, bool visit(Selector selector, T value)) {
assert(member.isMember());
SourceString name = member.name;
SelectorMapNode node = nodes[name];
if (node != null) {
node.visitMatching(member, compiler, visit);
}
}
}
class SelectorMapNode<T> {
final SourceString name;
final Map<Selector, T> selectors = new Map<Selector, T>();
// We start caching which selectors match which elements when the
// number of different selectors exceed a threshold. This way we
// avoid lots of repeated calls to the Selector.applies method.
static const int MAX_SELECTORS_NO_CACHE = 8;
Map<Element, List<Selector>> cache;
SelectorMapNode(this.name);
T lookup(Selector selector) {
assert(selector.name == name);
return selectors[selector];
}
void update(Selector selector, T value) {
assert(selector.name == name);
bool existing = selectors.containsKey(selector);
selectors[selector] = value;
if (existing) return;
// The update has introduced a new selector in the map, so we need
// to consider if we should start caching. At the very least, we
// have to clear the cache because the new element may invalidate
// existing cache entries.
if (cache == null) {
if (selectors.length > MAX_SELECTORS_NO_CACHE) {
cache = new Map<Element, List<Selector>>();
}
} else if (!cache.isEmpty) {
cache.clear();
}
}
bool containsKey(Selector selector) {
assert(selector.name == name);
return selectors.containsKey(selector);
}
T remove(Selector selector) {
assert(selector.name == name);
if (!selectors.containsKey(selector)) return null;
if (cache != null && !cache.isEmpty) cache.clear();
return selectors.remove(selector);
}
void visitMatching(Element member, Compiler compiler,
bool visit(Selector selector, T value)) {
assert(member.name == name);
Iterable<Selector> matching = computeMatching(member, compiler);
for (Selector selector in matching) {
if (!visit(selector, selectors[selector])) return;
}
}
Iterable<Selector> computeMatching(Element member, Compiler compiler) {
// Probe the cache if it exists. Do this before creating the
// matching iterable to cut down on the overhead for cache hits.
if (cache != null) {
List<Selector> cached = cache[member];
if (cached != null) return cached;
}
// Filter the selectors keys so we only have the ones that apply
// to the given member element.
Iterable<Selector> matching = selectors.keys.where(
(Selector selector) => selector.appliesUnnamed(member, compiler));
if (cache == null) return matching;
return cache[member] = matching.toList();
}
}