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() { public void compressCatchTable() {
ListBuffer<char[]> compressedCatchInfo = ListBuffer.lb(); ListBuffer<char[]> compressedCatchInfo = ListBuffer.lb();
List<Integer> handlerPcs = List.nil(); List<Integer> handlerPcs = List.nil();
for (char[] catchEntry : catchInfo.elems) { for (char[] catchEntry : catchInfo) {
handlerPcs = handlerPcs.prepend((int)catchEntry[2]); handlerPcs = handlerPcs.prepend((int)catchEntry[2]);
} }
for (char[] catchEntry : catchInfo.elems) { for (char[] catchEntry : catchInfo) {
int startpc = catchEntry[0]; int startpc = catchEntry[0];
int endpc = catchEntry[1]; int endpc = catchEntry[1];
if (startpc == endpc || if (startpc == endpc ||

View file

@ -818,9 +818,7 @@ public class JavacParser implements Parser {
* | "*" | "/" | "%" * | "*" | "/" | "%"
*/ */
JCExpression term2Rest(JCExpression t, int minprec) { JCExpression term2Rest(JCExpression t, int minprec) {
List<JCExpression[]> savedOd = odStackSupply.elems;
JCExpression[] odStack = newOdStack(); JCExpression[] odStack = newOdStack();
List<Token[]> savedOp = opStackSupply.elems;
Token[] opStack = newOpStack(); Token[] opStack = newOpStack();
// optimization, was odStack = new Tree[...]; opStack = new Tree[...]; // optimization, was odStack = new Tree[...]; opStack = new Tree[...];
@ -851,8 +849,8 @@ public class JavacParser implements Parser {
} }
} }
odStackSupply.elems = savedOd; // optimization odStackSupply.add(odStack);
opStackSupply.elems = savedOp; // optimization opStackSupply.add(opStack);
return t; return t;
} }
//where //where
@ -906,23 +904,19 @@ public class JavacParser implements Parser {
/** optimization: To save allocating a new operand/operator stack /** optimization: To save allocating a new operand/operator stack
* for every binary operation, we use supplys. * for every binary operation, we use supplys.
*/ */
ListBuffer<JCExpression[]> odStackSupply = new ListBuffer<JCExpression[]>(); ArrayList<JCExpression[]> odStackSupply = new ArrayList<JCExpression[]>();
ListBuffer<Token[]> opStackSupply = new ListBuffer<Token[]>(); ArrayList<Token[]> opStackSupply = new ArrayList<Token[]>();
private JCExpression[] newOdStack() { private JCExpression[] newOdStack() {
if (odStackSupply.elems == odStackSupply.last) if (odStackSupply.isEmpty())
odStackSupply.append(new JCExpression[infixPrecedenceLevels + 1]); return new JCExpression[infixPrecedenceLevels + 1];
JCExpression[] odStack = odStackSupply.elems.head; return odStackSupply.remove(odStackSupply.size() - 1);
odStackSupply.elems = odStackSupply.elems.tail;
return odStack;
} }
private Token[] newOpStack() { private Token[] newOpStack() {
if (opStackSupply.elems == opStackSupply.last) if (opStackSupply.isEmpty())
opStackSupply.append(new Token[infixPrecedenceLevels + 1]); return new Token[infixPrecedenceLevels + 1];
Token[] opStack = opStackSupply.elems.head; return opStackSupply.remove(opStackSupply.size() - 1);
opStackSupply.elems = opStackSupply.elems.tail;
return opStack;
} }
/** /**
@ -2001,7 +1995,7 @@ public class JavacParser implements Parser {
ListBuffer<JCStatement> stats = ListBuffer<JCStatement> stats =
variableDeclarators(mods, t, new ListBuffer<JCStatement>()); variableDeclarators(mods, t, new ListBuffer<JCStatement>());
// A "LocalVariableDeclarationStatement" subsumes the terminating semicolon // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon
storeEnd(stats.elems.last(), token.endPos); storeEnd(stats.last(), token.endPos);
accept(SEMI); accept(SEMI);
return stats.toList(); return stats.toList();
} }
@ -2042,7 +2036,7 @@ public class JavacParser implements Parser {
ListBuffer<JCStatement> stats = ListBuffer<JCStatement> stats =
variableDeclarators(mods, t, new ListBuffer<JCStatement>()); variableDeclarators(mods, t, new ListBuffer<JCStatement>());
// A "LocalVariableDeclarationStatement" subsumes the terminating semicolon // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon
storeEnd(stats.elems.last(), token.endPos); storeEnd(stats.last(), token.endPos);
accept(SEMI); accept(SEMI);
return stats.toList(); return stats.toList();
} else { } else {
@ -2577,7 +2571,7 @@ public class JavacParser implements Parser {
vdefs.append(variableDeclaratorRest(pos, mods, type, name, reqInit, dc)); vdefs.append(variableDeclaratorRest(pos, mods, type, name, reqInit, dc));
while (token.kind == COMMA) { while (token.kind == COMMA) {
// All but last of multiple declarators subsume a comma // All but last of multiple declarators subsume a comma
storeEnd((JCTree)vdefs.elems.last(), token.endPos); storeEnd((JCTree)vdefs.last(), token.endPos);
nextToken(); nextToken();
vdefs.append(variableDeclarator(mods, type, reqInit, dc)); vdefs.append(variableDeclarator(mods, type, reqInit, dc));
} }
@ -2632,7 +2626,7 @@ public class JavacParser implements Parser {
defs.append(resource()); defs.append(resource());
while (token.kind == SEMI) { while (token.kind == SEMI) {
// All but last of multiple declarators must subsume a semicolon // 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; int semiColonPos = token.pos;
nextToken(); nextToken();
if (token.kind == RPAREN) { // Optional trailing semicolon 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()); JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(packageAnnotations, pid, defs.toList());
if (!consumedToplevelDoc) if (!consumedToplevelDoc)
attach(toplevel, firstToken.comment(CommentStyle.JAVADOC)); attach(toplevel, firstToken.comment(CommentStyle.JAVADOC));
if (defs.elems.isEmpty()) if (defs.isEmpty())
storeEnd(toplevel, S.prevToken().endPos); storeEnd(toplevel, S.prevToken().endPos);
if (keepDocComments) if (keepDocComments)
toplevel.docComments = docComments; 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * 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. /** 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. /** The number of element in this buffer.
*/ */
public int count; private int count;
/** Has a list been created from this buffer yet? /** Has a list been created from this buffer yet?
*/ */
public boolean shared; private boolean shared;
/** Create a new initially empty list buffer. /** Create a new initially empty list buffer.
*/ */
@ -73,8 +74,8 @@ public class ListBuffer<A> extends AbstractQueue<A> {
} }
public final void clear() { public final void clear() {
this.elems = new List<A>(null,null); this.elems = List.nil();
this.last = this.elems; this.last = null;
count = 0; count = 0;
shared = false; shared = false;
} }
@ -103,22 +104,23 @@ public class ListBuffer<A> extends AbstractQueue<A> {
/** Copy list and sets last. /** Copy list and sets last.
*/ */
private void copy() { private void copy() {
List<A> p = elems = new List<A>(elems.head, elems.tail); if (elems.nonEmpty()) {
while (true) { List<A> orig = elems;
List<A> tail = p.tail;
if (tail == null) break; elems = last = List.<A>of(orig.head);
tail = new List<A>(tail.head, tail.tail);
p.setTail(tail); while ((orig = orig.tail).nonEmpty()) {
p = tail; last.tail = List.<A>of(orig.head);
last = last.tail;
}
} }
last = p;
shared = false;
} }
/** Prepend an element to buffer. /** Prepend an element to buffer.
*/ */
public ListBuffer<A> prepend(A x) { public ListBuffer<A> prepend(A x) {
elems = elems.prepend(x); elems = elems.prepend(x);
if (last == null) last = elems;
count++; count++;
return this; return this;
} }
@ -128,9 +130,13 @@ public class ListBuffer<A> extends AbstractQueue<A> {
public ListBuffer<A> append(A x) { public ListBuffer<A> append(A x) {
x.getClass(); // null check x.getClass(); // null check
if (shared) copy(); if (shared) copy();
last.head = x; List<A> newLast = List.<A>of(x);
last.setTail(new List<A>(null,null)); if (last != null) {
last = last.tail; last.tail = newLast;
last = newLast;
} else {
elems = last = newLast;
}
count++; count++;
return this; return this;
} }
@ -192,8 +198,9 @@ public class ListBuffer<A> extends AbstractQueue<A> {
*/ */
public A next() { public A next() {
A x = elems.head; A x = elems.head;
if (elems != last) { if (!elems.isEmpty()) {
elems = elems.tail; elems = elems.tail;
if (elems.isEmpty()) last = null;
count--; count--;
} }
return x; return x;
@ -205,10 +212,10 @@ public class ListBuffer<A> extends AbstractQueue<A> {
return new Iterator<A>() { return new Iterator<A>() {
List<A> elems = ListBuffer.this.elems; List<A> elems = ListBuffer.this.elems;
public boolean hasNext() { public boolean hasNext() {
return elems != last; return !elems.isEmpty();
} }
public A next() { public A next() {
if (elems == last) if (elems.isEmpty())
throw new NoSuchElementException(); throw new NoSuchElementException();
A elem = elems.head; A elem = elems.head;
elems = elems.tail; elems = elems.tail;
@ -263,4 +270,8 @@ public class ListBuffer<A> extends AbstractQueue<A> {
public A peek() { public A peek() {
return first(); 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);
}
}
}