8004504: ListBuffer could reuse List.nil() as the sentinel element
authorjlahoda
Wed, 12 Dec 2012 20:26:56 +0100
changeset 14803 88347e495d34
parent 14802 969e90f68ac5
child 14804 f93a8d60b9a4
8004504: ListBuffer could reuse List.nil() as the sentinel element Summary: ListBuffer.last now points to the last elements with client data, or null if none. Reviewed-by: jjg, mcimadamore
langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java
langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java
langtools/src/share/classes/com/sun/tools/javac/util/ListBuffer.java
langtools/test/tools/javac/util/list/ListBufferTest.java
--- a/langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java	Tue Dec 11 15:05:55 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/jvm/Code.java	Wed Dec 12 20:26:56 2012 +0100
@@ -1545,10 +1545,10 @@
     public void compressCatchTable() {
         ListBuffer<char[]> compressedCatchInfo = ListBuffer.lb();
         List<Integer> handlerPcs = List.nil();
-        for (char[] catchEntry : catchInfo.elems) {
+        for (char[] catchEntry : catchInfo) {
             handlerPcs = handlerPcs.prepend((int)catchEntry[2]);
         }
-        for (char[] catchEntry : catchInfo.elems) {
+        for (char[] catchEntry : catchInfo) {
             int startpc = catchEntry[0];
             int endpc = catchEntry[1];
             if (startpc == endpc ||
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Tue Dec 11 15:05:55 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Wed Dec 12 20:26:56 2012 +0100
@@ -818,9 +818,7 @@
      *                  | "*" | "/" | "%"
      */
     JCExpression term2Rest(JCExpression t, int minprec) {
-        List<JCExpression[]> savedOd = odStackSupply.elems;
         JCExpression[] odStack = newOdStack();
-        List<Token[]> savedOp = opStackSupply.elems;
         Token[] opStack = newOpStack();
 
         // optimization, was odStack = new Tree[...]; opStack = new Tree[...];
@@ -851,8 +849,8 @@
             }
         }
 
-        odStackSupply.elems = savedOd; // optimization
-        opStackSupply.elems = savedOp; // optimization
+        odStackSupply.add(odStack);
+        opStackSupply.add(opStack);
         return t;
     }
 //where
@@ -906,23 +904,19 @@
         /** optimization: To save allocating a new operand/operator stack
          *  for every binary operation, we use supplys.
          */
-        ListBuffer<JCExpression[]> odStackSupply = new ListBuffer<JCExpression[]>();
-        ListBuffer<Token[]> opStackSupply = new ListBuffer<Token[]>();
+        ArrayList<JCExpression[]> odStackSupply = new ArrayList<JCExpression[]>();
+        ArrayList<Token[]> opStackSupply = new ArrayList<Token[]>();
 
         private JCExpression[] newOdStack() {
-            if (odStackSupply.elems == odStackSupply.last)
-                odStackSupply.append(new JCExpression[infixPrecedenceLevels + 1]);
-            JCExpression[] odStack = odStackSupply.elems.head;
-            odStackSupply.elems = odStackSupply.elems.tail;
-            return odStack;
+            if (odStackSupply.isEmpty())
+                return new JCExpression[infixPrecedenceLevels + 1];
+            return odStackSupply.remove(odStackSupply.size() - 1);
         }
 
         private Token[] newOpStack() {
-            if (opStackSupply.elems == opStackSupply.last)
-                opStackSupply.append(new Token[infixPrecedenceLevels + 1]);
-            Token[] opStack = opStackSupply.elems.head;
-            opStackSupply.elems = opStackSupply.elems.tail;
-            return opStack;
+            if (opStackSupply.isEmpty())
+                return new Token[infixPrecedenceLevels + 1];
+            return opStackSupply.remove(opStackSupply.size() - 1);
         }
 
     /**
@@ -2001,7 +1995,7 @@
                 ListBuffer<JCStatement> stats =
                         variableDeclarators(mods, t, new ListBuffer<JCStatement>());
                 // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon
-                storeEnd(stats.elems.last(), token.endPos);
+                storeEnd(stats.last(), token.endPos);
                 accept(SEMI);
                 return stats.toList();
             }
@@ -2042,7 +2036,7 @@
                 ListBuffer<JCStatement> stats =
                         variableDeclarators(mods, t, new ListBuffer<JCStatement>());
                 // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon
-                storeEnd(stats.elems.last(), token.endPos);
+                storeEnd(stats.last(), token.endPos);
                 accept(SEMI);
                 return stats.toList();
             } else {
@@ -2577,7 +2571,7 @@
         vdefs.append(variableDeclaratorRest(pos, mods, type, name, reqInit, dc));
         while (token.kind == COMMA) {
             // All but last of multiple declarators subsume a comma
-            storeEnd((JCTree)vdefs.elems.last(), token.endPos);
+            storeEnd((JCTree)vdefs.last(), token.endPos);
             nextToken();
             vdefs.append(variableDeclarator(mods, type, reqInit, dc));
         }
@@ -2632,7 +2626,7 @@
         defs.append(resource());
         while (token.kind == SEMI) {
             // All but last of multiple declarators must subsume a semicolon
-            storeEnd(defs.elems.last(), token.endPos);
+            storeEnd(defs.last(), token.endPos);
             int semiColonPos = token.pos;
             nextToken();
             if (token.kind == RPAREN) { // Optional trailing semicolon
@@ -2710,7 +2704,7 @@
         JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(packageAnnotations, pid, defs.toList());
         if (!consumedToplevelDoc)
             attach(toplevel, firstToken.comment(CommentStyle.JAVADOC));
-        if (defs.elems.isEmpty())
+        if (defs.isEmpty())
             storeEnd(toplevel, S.prevToken().endPos);
         if (keepDocComments)
             toplevel.docComments = docComments;
--- a/langtools/src/share/classes/com/sun/tools/javac/util/ListBuffer.java	Tue Dec 11 15:05:55 2012 -0800
+++ b/langtools/src/share/classes/com/sun/tools/javac/util/ListBuffer.java	Wed Dec 12 20:26:56 2012 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -52,19 +52,20 @@
 
     /** The list of elements of this buffer.
      */
-    public List<A> elems;
+    private List<A> elems;
 
-    /** A pointer pointing to the last, sentinel element of `elems'.
+    /** A pointer pointing to the last element of 'elems' containing data,
+     *  or null if the list is empty.
      */
-    public List<A> last;
+    private List<A> last;
 
     /** The number of element in this buffer.
      */
-    public int count;
+    private int count;
 
     /** Has a list been created from this buffer yet?
      */
-    public boolean shared;
+    private boolean shared;
 
     /** Create a new initially empty list buffer.
      */
@@ -73,8 +74,8 @@
     }
 
     public final void clear() {
-        this.elems = new List<A>(null,null);
-        this.last = this.elems;
+        this.elems = List.nil();
+        this.last = null;
         count = 0;
         shared = false;
     }
@@ -103,22 +104,23 @@
     /** Copy list and sets last.
      */
     private void copy() {
-        List<A> p = elems = new List<A>(elems.head, elems.tail);
-        while (true) {
-            List<A> tail = p.tail;
-            if (tail == null) break;
-            tail = new List<A>(tail.head, tail.tail);
-            p.setTail(tail);
-            p = tail;
+        if (elems.nonEmpty()) {
+            List<A> orig = elems;
+
+            elems = last = List.<A>of(orig.head);
+
+            while ((orig = orig.tail).nonEmpty()) {
+                last.tail = List.<A>of(orig.head);
+                last = last.tail;
+            }
         }
-        last = p;
-        shared = false;
     }
 
     /** Prepend an element to buffer.
      */
     public ListBuffer<A> prepend(A x) {
         elems = elems.prepend(x);
+        if (last == null) last = elems;
         count++;
         return this;
     }
@@ -128,9 +130,13 @@
     public ListBuffer<A> append(A x) {
         x.getClass(); // null check
         if (shared) copy();
-        last.head = x;
-        last.setTail(new List<A>(null,null));
-        last = last.tail;
+        List<A> newLast = List.<A>of(x);
+        if (last != null) {
+            last.tail = newLast;
+            last = newLast;
+        } else {
+            elems = last = newLast;
+        }
         count++;
         return this;
     }
@@ -192,8 +198,9 @@
      */
     public A next() {
         A x = elems.head;
-        if (elems != last) {
+        if (!elems.isEmpty()) {
             elems = elems.tail;
+            if (elems.isEmpty()) last = null;
             count--;
         }
         return x;
@@ -205,10 +212,10 @@
         return new Iterator<A>() {
             List<A> elems = ListBuffer.this.elems;
             public boolean hasNext() {
-                return elems != last;
+                return !elems.isEmpty();
             }
             public A next() {
-                if (elems == last)
+                if (elems.isEmpty())
                     throw new NoSuchElementException();
                 A elem = elems.head;
                 elems = elems.tail;
@@ -263,4 +270,8 @@
     public A peek() {
         return first();
     }
+
+    public A last() {
+        return last != null ? last.head : null;
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/util/list/ListBufferTest.java	Wed Dec 12 20:26:56 2012 +0100
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8004504
+ * @summary Ensure that ListBuffer is working properly
+ */
+
+import com.sun.tools.javac.util.List;
+import com.sun.tools.javac.util.ListBuffer;
+import java.util.Iterator;
+import java.util.Objects;
+
+public class ListBufferTest {
+    public static void main(String... args) {
+        testRemove();
+        testCopiedEndsWithList_nil();
+    }
+
+    private static void testCopiedEndsWithList_nil() {
+        ListBuffer<String> lb = new ListBuffer<>();
+
+        lb.add("a");
+        lb.add("b");
+        lb.add("c");
+
+        List<String> l1 = lb.toList();
+
+        assertListEquals(l1, "a", "b", "c");
+        assertEndsWithNil(l1);
+
+        lb.add("d");
+
+        List<String> l2 = lb.toList();
+        assertListEquals(l2, "a", "b", "c", "d");
+        assertEndsWithNil(l2);
+        assertListEquals(l1, "a", "b", "c");
+    }
+
+    private static void testRemove() {
+        ListBuffer<String> lb1 = new ListBuffer<>();
+
+        lb1.add("a");
+        lb1.add("b");
+        lb1.add("c");
+
+        assertListEquals(lb1.toList(), "a", "b", "c");
+        assertEquals(lb1.next(), "a");
+        assertListEquals(lb1.toList(), "b", "c");
+        assertEquals(lb1.next(), "b");
+        assertListEquals(lb1.toList(), "c");
+        assertEquals(lb1.next(), "c");
+        assertListEquals(lb1.toList());
+        assertEquals(lb1.next(), null);
+
+        lb1.add("d");
+
+        assertEquals(lb1.next(), "d");
+    }
+
+    private static void assertEndsWithNil(List<?> list) {
+        while (!list.isEmpty()) {
+            list = list.tail;
+        }
+
+        if (list != List.nil()) throw new IllegalStateException("Not ending with List.nil()");
+    }
+
+    private static <T> void assertListEquals(Iterable<T> list, T... data) {
+        int i = 0;
+        Iterator<T> it = list.iterator();
+
+        while (it.hasNext() && i < data.length) {
+            assertEquals(it.next(), data[i++]);
+        }
+
+        if (it.hasNext()) {
+            throw new IllegalStateException("Too many elements in the list");
+        }
+
+        if (i < data.length) {
+            throw new IllegalStateException("Too few elements in the list");
+        }
+    }
+
+    private static void assertEquals(Object expected, Object actual) {
+        if (!Objects.equals(expected, actual)) {
+            throw new IllegalStateException("Incorrect content. Expected: " + expected + ", actual: " + actual);
+        }
+    }
+}