mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 11:04:34 +02:00
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:
parent
1c8e65a20e
commit
9537c44ca5
4 changed files with 163 additions and 46 deletions
|
@ -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 ||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
112
langtools/test/tools/javac/util/list/ListBufferTest.java
Normal file
112
langtools/test/tools/javac/util/list/ListBufferTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue