Simplify the FunctionSet implementation and remove a slightly broken optimization from SelectorMap.

This saves around 70 ms when compiling swarm, so it's not a big deal performance-wise
but the code is much simpler.

R=ngeoffray@google.com
BUG=

Review URL: https://codereview.chromium.org//12261005

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@18444 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/sdk/lib/_internal/compiler/implementation/universe/function_set.dart b/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
index 7e119da..6afdf09 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/function_set.dart
@@ -7,51 +7,47 @@
 // TODO(kasperl): This actually holds getters and setters just fine
 // too and stricly they aren't functions. Maybe this needs a better
 // name -- something like ElementSet seems a bit too generic.
-class FunctionSet extends PartialTypeTree {
+class FunctionSet {
+  final Compiler compiler;
+  final Map<SourceString, FunctionSetNode> nodes =
+      new Map<SourceString, FunctionSetNode>();
+  FunctionSet(this.compiler);
 
-  FunctionSet(Compiler compiler) : super(compiler);
-
-  FunctionSetNode newSpecializedNode(ClassElement type)
-      => new FunctionSetNode(type);
-
-  // TODO(kasperl): Allow static members too?
   void add(Element element) {
     assert(element.isMember());
-    FunctionSetNode node = findNode(element.getEnclosingClass(), true);
-    node.membersByName[element.name] = element;
+    SourceString name = element.name;
+    FunctionSetNode node = nodes.putIfAbsent(
+        name, () => new FunctionSetNode(name));
+    node.add(element);
   }
 
-  // TODO(kasperl): Allow static members too?
   void remove(Element element) {
     assert(element.isMember());
-    FunctionSetNode node = findNode(element.getEnclosingClass(), false);
-    if (node != null) node.membersByName.remove(element.name);
+    SourceString name = element.name;
+    FunctionSetNode node = nodes[name];
+    if (node != null) {
+      node.remove(element);
+    }
   }
 
-  // TODO(kasperl): Allow static members too?
   bool contains(Element element) {
     assert(element.isMember());
-    FunctionSetNode node = findNode(element.getEnclosingClass(), false);
+    SourceString name = element.name;
+    FunctionSetNode node = nodes[name];
     return (node != null)
-        ? node.membersByName.containsKey(element.name)
+        ? node.contains(element)
         : false;
   }
 
-  bool shouldVisitAll(Selector selector) {
-    // TODO(kasperl): For now, we use a different implementation for
-    // filtering if the tree contains interface subtypes.
-    return containsInterfaceSubtypes
-        && (selector.typeKind == TypedSelectorKind.INTERFACE
-            || selector.typeKind == TypedSelectorKind.UNKNOWN);
-  }
-
   /**
    * Returns all elements that may be invoked with the given [selector].
    */
-  Set<Element> filterBySelector(Selector selector) {
-    return shouldVisitAll(selector)
-        ? filterAllBySelector(selector)
-        : filterHierarchyBySelector(selector);
+  Iterable<Element> filterBySelector(Selector selector) {
+    SourceString name = selector.name;
+    FunctionSetNode node = nodes[name];
+    return (node != null)
+        ? node.filter(selector, compiler)
+        : const <Element>[];
   }
 
   /**
@@ -59,86 +55,92 @@
    * [selector].
    */
   bool hasAnyElementMatchingSelector(Selector selector) {
-    return shouldVisitAll(selector)
-        ? hasAnyInAll(selector)
-        : hasAnyInHierarchy(selector);
+    return !filterBySelector(selector).isEmpty;
   }
 
-  Set<Element> filterAllBySelector(Selector selector) {
-    Set<Element> result = new Set<Element>();
-    if (root == null) return result;
-    root.visitRecursively((FunctionSetNode node) {
-      Element member = node.membersByName[selector.name];
-      // Since we're running through the entire tree we have to use
-      // the applies method that takes types into account.
-      if (member != null && selector.appliesUnnamed(member, compiler)) {
-        result.add(member);
-      }
-      return true;
-    });
-    return result;
-  }
-
-  Set<Element> filterHierarchyBySelector(Selector selector) {
-    Set<Element> result = new Set<Element>();
-    if (root == null) return result;
-    visitHierarchy(selectorType(selector), (FunctionSetNode node) {
-      Element member = node.membersByName[selector.name];
-      if (member != null && selector.appliesUntyped(member, compiler)) {
-        result.add(member);
-      }
-      return true;
-    });
-    return result;
-  }
-
-  bool hasAnyInAll(Selector selector) {
-    bool result = false;
-    if (root == null) return result;
-    root.visitRecursively((FunctionSetNode node) {
-      Element member = node.membersByName[selector.name];
-      // Since we're running through the entire tree we have to use
-      // the applies method that takes types into account.
-      if (member != null && selector.appliesUnnamed(member, compiler)) {
-        result = true;
-        // End the traversal.
-        return false;
-      }
-      return true;
-    });
-    return result;
-  }
-
-  bool hasAnyInHierarchy(Selector selector) {
-    bool result = false;
-    if (root == null) return result;
-    visitHierarchy(selectorType(selector), (FunctionSetNode node) {
-      Element member = node.membersByName[selector.name];
-      if (member != null && selector.appliesUntyped(member, compiler)) {
-        result = true;
-        // End the traversal.
-        return false;
-      }
-      return true;
-    });
-    return result;
-  }
-
-  void forEach(Function f) {
-    if (root == null) return;
-    root.visitRecursively((FunctionSetNode node) {
-      node.membersByName.forEach(
-          (SourceString _, Element element) => f(element));
-      return true;
+  void forEach(Function action) {
+    nodes.forEach((SourceString name, FunctionSetNode node) {
+      node.forEach(action);
     });
   }
 }
 
-class FunctionSetNode extends PartialTypeTreeNode {
+class FunctionSetNode {
+  final SourceString name;
+  final Map<Selector, List<Element>> cache = new Map<Selector, List<Element>>();
 
-  final Map<SourceString, Element> membersByName;
+  // Initially, we keep the elements in a list because it is more
+  // compact than a hash set. Once we get enough elements, we change
+  // the representation to be a set to get faster contains checks.
+  static const int MAX_ELEMENTS_IN_LIST = 8;
+  Collection<Element> elements = <Element>[];
+  bool isList = true;
 
-  FunctionSetNode(ClassElement type) : super(type),
-      membersByName = new Map<SourceString, Element>();
+  FunctionSetNode(this.name);
 
+  void add(Element element) {
+    assert(element.name == name);
+    // We try to avoid clearing the cache unless we have to. For that
+    // reason we keep the explicit contains check even though the add
+    // method ends up doing the work again (for sets).
+    if (!elements.contains(element)) {
+      if (isList && elements.length >= MAX_ELEMENTS_IN_LIST) {
+        elements = elements.toSet();
+        isList = false;
+      }
+      elements.add(element);
+      cache.clear();
+    }
+  }
+
+  void remove(Element element) {
+    assert(element.name == name);
+    if (isList) {
+      List list = elements;
+      int index = list.indexOf(element);
+      if (index < 0) return;
+      Element last = list.removeLast();
+      if (index != list.length) {
+        list[index] = last;
+      }
+      cache.clear();
+    } else {
+      Set set = elements;
+      if (set.remove(element)) {
+        // To avoid wobbling between the two representations, we do
+        // not transition back to the list representation even if we
+        // end up with few enough elements at this point.
+        cache.clear();
+      }
+    }
+  }
+
+  bool contains(Element element) {
+    assert(element.name == name);
+    return elements.contains(element);
+  }
+
+  void forEach(Function action) {
+    elements.forEach(action);
+  }
+
+  Iterable<Element> filter(Selector selector, Compiler compiler) {
+    assert(selector.name == name);
+    List<Element> result = cache[selector];
+    if (result != null) return result;
+    for (Element element in elements) {
+      if (selector.appliesUnnamed(element, compiler)) {
+        if (result == null) {
+          // Defer the allocation of the resulting list until we are
+          // sure we need it. This allows us to return immutable empty
+          // lists when the filtering produced no results.
+          result = <Element>[];
+        }
+        result.add(element);
+      }
+    }
+    if (result == null) result = const <Element>[];
+    cache[selector] = result;
+    return result;
+  }
 }
diff --git a/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart b/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
index 53be749..b0ed8dc 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/partial_type_tree.dart
@@ -5,62 +5,18 @@
 part of universe;
 
 abstract class PartialTypeTree {
-
   final Compiler compiler;
   PartialTypeTreeNode root;
 
-  final Map<ClassElement, PartialTypeTreeNode> nodes =
-      new Map<ClassElement, PartialTypeTreeNode>();
-
-  // TODO(kasperl): For now, we keep track of whether or not the tree
-  // contains two classes with a subtype relationship that isn't a
-  // subclass relationship.
-  bool containsInterfaceSubtypes = false;
-
-  final Set<ClassElement> unseenInterfaceSubtypes =
-      new Set<ClassElement>();
-
   PartialTypeTree(this.compiler);
 
-  PartialTypeTreeNode newSpecializedNode(ClassElement type);
+  PartialTypeTreeNode newNode(ClassElement type);
 
-  PartialTypeTreeNode newNode(ClassElement type) {
-    PartialTypeTreeNode node = newSpecializedNode(type);
-    nodes[type] = node;
-    if (containsInterfaceSubtypes) return node;
-
-    // Check if the implied interface of the new class is implemented
-    // by another class that is already in the tree.
-    if (unseenInterfaceSubtypes.contains(type)) {
-      containsInterfaceSubtypes = true;
-      unseenInterfaceSubtypes.clear();
-      return node;
-    }
-
-    // Run through all the implied interfaces the class that we're
-    // adding implements and see if any of them are already in the
-    // tree. If so, we have a tree with interface subtypes. If not,
-    // keep track of them so we can deal with it if the interface is
-    // added to the tree later.
-    for (Link link = type.interfaces; !link.isEmpty; link = link.tail) {
-      InterfaceType superType = link.head;
-      ClassElement superTypeElement = superType.element;
-      if (nodes.containsKey(superTypeElement)) {
-        containsInterfaceSubtypes = true;
-        unseenInterfaceSubtypes.clear();
-        break;
-      } else {
-        unseenInterfaceSubtypes.add(superTypeElement);
-      }
-    }
-    return node;
-  }
-
-  // TODO(kasperl): Move this to the Selector class?
   /**
    * Returns a [ClassElement] that is an upper bound of the receiver type on
    * [selector].
    */
+  // TODO(kasperl): Move this to the Selector class?
   ClassElement selectorType(Selector selector) {
     // TODO(ngeoffray): Should the tree be specialized with DartType?
     DartType type = selector.receiverType;
diff --git a/sdk/lib/_internal/compiler/implementation/universe/selector_map.dart b/sdk/lib/_internal/compiler/implementation/universe/selector_map.dart
index cf98a10..36b51399 100644
--- a/sdk/lib/_internal/compiler/implementation/universe/selector_map.dart
+++ b/sdk/lib/_internal/compiler/implementation/universe/selector_map.dart
@@ -4,12 +4,13 @@
 
 part of universe;
 
+// TODO(kasperl): It seems possible to rewrite this class to be more
+// like the FunctionSet abstraction which is a lot simpler.
 class SelectorMap<T> extends PartialTypeTree {
 
   SelectorMap(Compiler compiler) : super(compiler);
 
-  SelectorMapNode<T> newSpecializedNode(ClassElement type)
-      => new SelectorMapNode<T>(type);
+  SelectorMapNode<T> newNode(ClassElement type) => new SelectorMapNode<T>(type);
 
   T operator [](Selector selector) {
     SelectorMapNode<T> node = findNode(selectorType(selector), false);
@@ -23,32 +24,29 @@
     return null;
   }
 
+  // TODO(kasperl): Do we need to support removing selectors by
+  // passing null as the value?
   void operator []=(Selector selector, T value) {
-    SelectorMapNode<T> node = findNode(selectorType(selector), true);
-    Link<SelectorValue<T>> selectors = node.selectorsByName[selector.name];
-    if (selectors == null) {
-      // No existing selectors with the given name. Create a new
-      // linked list.
-      SelectorValue<T> head = new SelectorValue<T>(selector, value);
-      node.selectorsByName[selector.name] =
-          new Link<SelectorValue<T>>().prepend(head);
-    } else {
-      // Run through the linked list of selectors with the same name. If
-      // we find one that matches, we update the value in the mapping.
-      for (Link link = selectors; !link.isEmpty; link = link.tail) {
-        SelectorValue<T> existing = link.head;
-        // It is safe to ignore the type here, because all selector
-        // mappings that are stored in a single node have the same type.
-        if (existing.selector.equalsUntyped(selector)) {
-          existing.value = value;
-          return;
-        }
+    ClassElement type = selectorType(selector);
+    SelectorMapNode<T> node = findNode(type, true);
+    SourceString name = selector.name;
+    Link<SelectorValue<T>> selectors = node.selectorsByName.putIfAbsent(
+        name, () => const Link());
+    // Run through the linked list of selectors with the same name. If
+    // we find one that matches, we update the value in the mapping.
+    for (Link link = selectors; !link.isEmpty; link = link.tail) {
+      SelectorValue<T> existing = link.head;
+      // It is safe to ignore the type here, because all selector
+      // mappings that are stored in a single node have the same type.
+      if (existing.selector.equalsUntyped(selector)) {
+        existing.value = value;
+        return;
       }
-      // We could not find an existing mapping for the selector, so
-      // we add a new one to the existing linked list.
-      SelectorValue<T> head = new SelectorValue<T>(selector, value);
-      node.selectorsByName[selector.name] = selectors.prepend(head);
     }
+    // We could not find an existing mapping for the selector, so
+    // we add a new one to the existing linked list.
+    SelectorValue<T> head = new SelectorValue<T>(selector, value);
+    node.selectorsByName[name] = selectors.prepend(head);
   }
 
   // TODO(kasperl): Share code with the [] operator?
@@ -72,13 +70,10 @@
   void visitMatching(Element member, bool visit(Selector selector, T value)) {
     assert(member.isMember());
     if (root == null) return;
-    // TODO(kasperl): For now, we use a different implementation for
-    // visiting if the tree contains interface subtypes.
-    if (containsInterfaceSubtypes) {
-      visitAllMatching(member, visit);
-    } else {
-      visitHierarchyMatching(member, visit);
-    }
+    // TODO(kasperl): Use visitHierachyMatching when possible. It is
+    // currently broken in subtle ways when it comes to finding typed
+    // selectors where we only know the interface of the receiver.
+    visitAllMatching(member, visit);
   }
 
   void visitAllMatching(Element member, bool visit(selector, value)) {
@@ -116,12 +111,9 @@
 }
 
 class SelectorMapNode<T> extends PartialTypeTreeNode {
-
-  final Map<SourceString, Link<SelectorValue<T>>> selectorsByName;
-
-  SelectorMapNode(ClassElement type) : super(type),
-      selectorsByName = new Map<SourceString, Link<SelectorValue<T>>>();
-
+  final Map<SourceString, Link<SelectorValue<T>>> selectorsByName =
+      new Map<SourceString, Link<SelectorValue<T>>>();
+  SelectorMapNode(ClassElement type) : super(type);
 }
 
 class SelectorValue<T> {