8004504: ListBuffer could reuse List.nil() as the sentinel element

ListBuffer.last now points to the last elements with client data, or null if none.

Reviewed-by: jjg, mcimadamore
This commit is contained in:
Jan Lahoda 2012-12-12 20:26:56 +01:00
parent 1c8e65a20e
commit 9537c44ca5
4 changed files with 163 additions and 46 deletions

View file

@ -1545,10 +1545,10 @@ public class Code {
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 ||

View file

@ -818,9 +818,7 @@ public class JavacParser implements Parser {
* | "*" | "/" | "%"
*/
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 @@ public class JavacParser implements Parser {
}
}
odStackSupply.elems = savedOd; // optimization
opStackSupply.elems = savedOp; // optimization
odStackSupply.add(odStack);
opStackSupply.add(opStack);
return t;
}
//where
@ -906,23 +904,19 @@ public class JavacParser implements Parser {
/** 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 @@ public class JavacParser implements Parser {
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 @@ public class JavacParser implements Parser {
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 @@ public class JavacParser implements Parser {
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 @@ public class JavacParser implements Parser {
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 @@ public class JavacParser implements Parser {
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;

View file

@ -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 @@ public class ListBuffer<A> extends AbstractQueue<A> {
/** 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 class ListBuffer<A> extends AbstractQueue<A> {
}
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 @@ public class ListBuffer<A> extends AbstractQueue<A> {
/** 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 class ListBuffer<A> extends AbstractQueue<A> {
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 class ListBuffer<A> extends AbstractQueue<A> {
*/
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 @@ public class ListBuffer<A> extends AbstractQueue<A> {
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 class ListBuffer<A> extends AbstractQueue<A> {
public A peek() {
return first();
}
public A last() {
return last != null ? last.head : null;
}
}

View file

@ -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);
}
}
}