Avoid allocation and bottom-type on Link

Change-Id: Ib23e596edf55285cc63e3a391da3712b2bcb1224
Reviewed-on: https://dart-review.googlesource.com/56761
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/compiler/lib/src/kernel/element_map_impl.dart b/pkg/compiler/lib/src/kernel/element_map_impl.dart
index a9eb519..e067c26 100644
--- a/pkg/compiler/lib/src/kernel/element_map_impl.dart
+++ b/pkg/compiler/lib/src/kernel/element_map_impl.dart
@@ -311,11 +311,12 @@
         node.implementedTypes.forEach((ir.Supertype supertype) {
           linkBuilder.addLast(processSupertype(supertype));
         });
-        Link<InterfaceType> interfaces = linkBuilder.toLink();
+        Link<InterfaceType> interfaces =
+            linkBuilder.toLink(const Link<InterfaceType>());
         OrderedTypeSetBuilder setBuilder =
             new _KernelOrderedTypeSetBuilder(this, cls);
         data.orderedTypeSet = setBuilder.createOrderedTypeSet(
-            data.supertype, interfaces.reverse());
+            data.supertype, interfaces.reverse(const Link<InterfaceType>()));
         data.interfaces = new List<InterfaceType>.from(interfaces.toList());
       }
     }
diff --git a/pkg/compiler/lib/src/ordered_typeset.dart b/pkg/compiler/lib/src/ordered_typeset.dart
index 67c594a..0df07ef 100644
--- a/pkg/compiler/lib/src/ordered_typeset.dart
+++ b/pkg/compiler/lib/src/ordered_typeset.dart
@@ -283,7 +283,7 @@
       }
     }
     return new OrderedTypeSet.internal(
-        levels, levels.last, allSupertypes.toLink());
+        levels, levels.last, allSupertypes.toLink(const Link<InterfaceType>()));
   }
 
   String toString() {
@@ -291,13 +291,7 @@
     for (int depth = 0; depth <= maxDepth; depth++) {
       sb.write('$depth: ');
       LinkEntry<InterfaceType> first = map[depth];
-      if (first.isNotEmpty) {
-        sb.write('${first.head}');
-        while (first.tail.isNotEmpty) {
-          sb.write(', ${first.tail.head}');
-          first = first.tail;
-        }
-      }
+      first.printOn(sb, ", ");
       sb.write('\n');
     }
     return sb.toString();
diff --git a/pkg/compiler/lib/src/util/util.dart b/pkg/compiler/lib/src/util/util.dart
index 113a2ed..c4452f6 100644
--- a/pkg/compiler/lib/src/util/util.dart
+++ b/pkg/compiler/lib/src/util/util.dart
@@ -210,7 +210,7 @@
   if (isExternal) builder.addLast('external');
   if (isCovariant) builder.addLast('covariant');
   StringBuffer buffer = new StringBuffer();
-  builder.toLink().printOn(buffer, ', ');
+  builder.toLink(const Link<String>()).printOn(buffer, ', ');
   return buffer.toString();
 }
 
diff --git a/pkg/front_end/lib/src/fasta/util/link.dart b/pkg/front_end/lib/src/fasta/util/link.dart
index 5236d28..4debdc7 100644
--- a/pkg/front_end/lib/src/fasta/util/link.dart
+++ b/pkg/front_end/lib/src/fasta/util/link.dart
@@ -61,7 +61,7 @@
   bool get isEmpty => true;
   bool get isNotEmpty => false;
 
-  Link<T> reverse() => this;
+  Link<T> reverse(Link<T> tail) => this;
 
   Link<T> reversePrependAll(Link<T> from) {
     if (from.isEmpty) return this;
@@ -121,8 +121,6 @@
     return true;
   }
 
-  Link copyWithout(e) => this;
-
   //
   // Unsupported Iterable<T> methods.
   //
@@ -159,7 +157,7 @@
 
   /// Prepends all elements added to the builder to [tail]. The resulting list
   /// is returned and the builder is cleared.
-  Link<T> toLink([Link<T> tail]);
+  Link<T> toLink(Link<T> tail);
 
   /// Creates a new fixed length containing all added elements. The
   /// resulting list is returned and the builder is cleared.
diff --git a/pkg/front_end/lib/src/fasta/util/link_implementation.dart b/pkg/front_end/lib/src/fasta/util/link_implementation.dart
index 5ff4693..9849ecb 100644
--- a/pkg/front_end/lib/src/fasta/util/link_implementation.dart
+++ b/pkg/front_end/lib/src/fasta/util/link_implementation.dart
@@ -88,10 +88,11 @@
     return buffer.toString();
   }
 
-  Link<T> reverse() {
-    Link<T> result = const Link<Null>();
+  @override
+  Link<T> reverse(Link<T> tail) {
+    Link<T> result = tail;
     for (Link<T> link = this; link.isNotEmpty; link = link.tail) {
-      result = new LinkEntry<T>(link.head, result);
+      result = result.prepend(link.head);
     }
     return result;
   }
@@ -146,17 +147,6 @@
     }
     return length;
   }
-
-  Link copyWithout(e) {
-    LinkBuilder copy = new LinkBuilder();
-    Link link = this;
-    for (; link.isNotEmpty; link = link.tail) {
-      if (link.head != e) {
-        copy.addLast(link.head);
-      }
-    }
-    return copy.toLink(link);
-  }
 }
 
 class LinkBuilderImplementation<T> implements LinkBuilder<T> {
@@ -166,13 +156,9 @@
 
   LinkBuilderImplementation();
 
-  Link<T> toLink([Link<T> tail]) {
-    if (head == null) {
-      // TODO(ahe): We should consider making the [tail] argument mandatory to
-      // avoid creating unneeded objects.
-      return tail ?? new Link<T>();
-    }
-    tail ??= const Link<Null>();
+  @override
+  Link<T> toLink(Link<T> tail) {
+    if (head == null) return tail;
     lastLink.tail = tail;
     Link<T> link = head;
     lastLink = null;
diff --git a/pkg/front_end/test/fasta/link_test.dart b/pkg/front_end/test/fasta/link_test.dart
index a248fbd..5fe7b42 100644
--- a/pkg/front_end/test/fasta/link_test.dart
+++ b/pkg/front_end/test/fasta/link_test.dart
@@ -9,25 +9,36 @@
 main() {
   Link<String> strings = const Link<String>().prepend("B").prepend("A");
   Expect.stringEquals("[ A, B ]", "${strings}");
-  Expect.stringEquals("[ B, A ]", "${strings.reverse()}");
-
-  strings = (new LinkBuilder<String>()..addLast("A")..addLast("B")).toLink();
-
-  Expect.stringEquals("[ A, B ]", "${strings}");
-  Expect.stringEquals("[ B, A ]", "${strings.reverse()}");
-
-  strings = new LinkBuilder<String>().toLink().prepend("B").prepend("A");
-
-  Expect.stringEquals("[ A, B ]", "${strings}");
-  Expect.stringEquals("[ B, A ]", "${strings.reverse()}");
-
-  strings = const Link<String>().reverse().prepend("B").prepend("A");
-  Expect.stringEquals("[ A, B ]", "${strings}");
-  Expect.stringEquals("[ B, A ]", "${strings.reverse()}");
+  Expect.stringEquals("[ B, A ]", "${strings.reverse(const Link<String>())}");
 
   strings = (new LinkBuilder<String>()..addLast("A")..addLast("B"))
       .toLink(const Link<String>());
 
   Expect.stringEquals("[ A, B ]", "${strings}");
-  Expect.stringEquals("[ B, A ]", "${strings.reverse()}");
+  Expect.stringEquals("[ B, A ]", "${strings.reverse(const Link<String>())}");
+
+  strings = new LinkBuilder<String>()
+      .toLink(const Link<String>())
+      .prepend("B")
+      .prepend("A");
+
+  Expect.stringEquals("[ A, B ]", "${strings}");
+  Expect.stringEquals("[ B, A ]", "${strings.reverse(const Link<String>())}");
+
+  strings = const Link<String>()
+      .reverse(const Link<String>())
+      .prepend("B")
+      .prepend("A");
+  Expect.stringEquals("[ A, B ]", "${strings}");
+  Expect.stringEquals("[ B, A ]", "${strings.reverse(const Link<String>())}");
+
+  strings = (new LinkBuilder<String>()..addLast("A")..addLast("B"))
+      .toLink(const Link<String>());
+
+  Expect.stringEquals("[ A, B ]", "${strings}");
+  Expect.stringEquals("[ B, A ]", "${strings.reverse(const Link<String>())}");
+
+  Link<int> ints =
+      const Link<int>().prepend(1).reverse(const Link<int>()).tail.prepend(1);
+  Expect.stringEquals("[ 1 ]", "${ints}");
 }
diff --git a/tests/compiler/dart2js/link_test.dart b/tests/compiler/dart2js/link_test.dart
index 841c89f..eabf688 100644
--- a/tests/compiler/dart2js/link_test.dart
+++ b/tests/compiler/dart2js/link_test.dart
@@ -21,8 +21,6 @@
   testFromList([0, 1, 2, 3, 4]);
   testFromList([0, 1, 2, 3, 4, 5]);
   testSkip();
-
-  testCopyWithout();
 }
 
 testFromList(List list) {
@@ -63,14 +61,3 @@
   Expect.isTrue(emptyLink.skip(0).isEmpty);
   Expect.throws(() => emptyLink.skip(1), (e) => e is RangeError);
 }
-
-testCopyWithout() {
-  test(const Link().copyWithout(0), []);
-
-  test(LinkFromList([0]).copyWithout(0), []);
-  test(LinkFromList([0, 1]).copyWithout(0), [1]);
-  test(LinkFromList([0, 1, 2]).copyWithout(0), [1, 2]);
-  test(LinkFromList([0, 1, 2]).copyWithout(1), [0, 2]);
-  test(LinkFromList([0, 1, 2]).copyWithout(2), [0, 1]);
-  test(LinkFromList([0, 1, 2]).copyWithout(3), [0, 1, 2]);
-}