summaryrefslogtreecommitdiff
path: root/src/library/scala/collection/mutable/ListBuffer.scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-09-30 09:43:51 -0700
committerPaul Phillips <paulp@improving.org>2012-09-30 09:59:19 -0700
commitd16326a7b679ec7877ff6b5223d2176f8a651b70 (patch)
tree6857acfbd9f821f1a069d7025d71614eb2063f1c /src/library/scala/collection/mutable/ListBuffer.scala
parent2e14b0771569b4fb6cd0273e1480f69c18743832 (diff)
downloadscala-d16326a7b679ec7877ff6b5223d2176f8a651b70.tar.gz
scala-d16326a7b679ec7877ff6b5223d2176f8a651b70.tar.bz2
scala-d16326a7b679ec7877ff6b5223d2176f8a651b70.zip
Fix for SI-6452, leak in ListBuffer.
The private var which holds a pointer to the end of the list was not cleared even when the length of the buffer was reduced to 0.
Diffstat (limited to 'src/library/scala/collection/mutable/ListBuffer.scala')
-rw-r--r--src/library/scala/collection/mutable/ListBuffer.scala44
1 files changed, 31 insertions, 13 deletions
diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala
index cd743999bc..bced92e663 100644
--- a/src/library/scala/collection/mutable/ListBuffer.scala
+++ b/src/library/scala/collection/mutable/ListBuffer.scala
@@ -56,12 +56,18 @@ final class ListBuffer[A]
import scala.collection.Traversable
import scala.collection.immutable.ListSerializeEnd
+ /** Expected invariants:
+ * If start.isEmpty, last0 == null
+ * If start.nonEmpty, last0 != null
+ * If len == 0, start.isEmpty
+ * If len > 0, start.nonEmpty
+ */
private var start: List[A] = Nil
private var last0: ::[A] = _
private var exported: Boolean = false
private var len = 0
- protected def underlying: immutable.Seq[A] = start
+ protected def underlying: List[A] = start
private def writeObject(out: ObjectOutputStream) {
// write start
@@ -133,7 +139,7 @@ final class ListBuffer[A]
if (exported) copy()
if (n == 0) {
val newElem = new :: (x, start.tail);
- if (last0 eq start) {
+ if ((last0 eq null) || (last0 eq start)) {
last0 = newElem
}
start = newElem
@@ -162,7 +168,7 @@ final class ListBuffer[A]
*/
def += (x: A): this.type = {
if (exported) copy()
- if (start.isEmpty) {
+ if (isEmpty) {
last0 = new :: (x, Nil)
start = last0
} else {
@@ -184,6 +190,7 @@ final class ListBuffer[A]
*/
def clear() {
start = Nil
+ last0 = null
exported = false
len = 0
}
@@ -197,7 +204,7 @@ final class ListBuffer[A]
def +=: (x: A): this.type = {
if (exported) copy()
val newElem = new :: (x, start)
- if (start.isEmpty) last0 = newElem
+ if (isEmpty) last0 = newElem
start = newElem
len += 1
this
@@ -219,7 +226,7 @@ final class ListBuffer[A]
if (n == 0) {
while (!elems.isEmpty) {
val newElem = new :: (elems.head, start)
- if (start.isEmpty) last0 = newElem
+ if (isEmpty) last0 = newElem
start = newElem
elems = elems.tail
}
@@ -243,6 +250,15 @@ final class ListBuffer[A]
}
}
+ /** Reduce the length of the buffer, and null out last0
+ * if this reduces the length to 0.
+ */
+ private def reduceLengthBy(num: Int) {
+ len -= num
+ if (len <= 0) // obviously shouldn't be < 0, but still better not to leak
+ last0 = null
+ }
+
/** Removes a given number of elements on a given index position. May take
* time linear in the buffer size.
*
@@ -274,7 +290,7 @@ final class ListBuffer[A]
c -= 1
}
}
- len -= count1
+ reduceLengthBy(count1)
}
// Implementation of abstract method in Builder
@@ -285,7 +301,7 @@ final class ListBuffer[A]
* copied lazily, the first time it is mutated.
*/
override def toList: List[A] = {
- exported = !start.isEmpty
+ exported = !isEmpty
start
}
@@ -296,7 +312,7 @@ final class ListBuffer[A]
* @param xs the list to which elements are prepended
*/
def prependToList(xs: List[A]): List[A] = {
- if (start.isEmpty) xs
+ if (isEmpty) xs
else {
if (exported) copy()
last0.tl = xs
@@ -331,7 +347,7 @@ final class ListBuffer[A]
if (last0 eq cursor.tail) last0 = cursor.asInstanceOf[::[A]]
cursor.asInstanceOf[::[A]].tl = cursor.tail.tail
}
- len -= 1
+ reduceLengthBy(1)
old
}
@@ -343,11 +359,12 @@ final class ListBuffer[A]
*/
override def -= (elem: A): this.type = {
if (exported) copy()
- if (start.isEmpty) {}
+ if (isEmpty) {}
else if (start.head == elem) {
start = start.tail
- len -= 1
- } else {
+ reduceLengthBy(1)
+ }
+ else {
var cursor = start
while (!cursor.tail.isEmpty && cursor.tail.head != elem) {
cursor = cursor.tail
@@ -357,7 +374,7 @@ final class ListBuffer[A]
if (z.tl == last0)
last0 = z
z.tl = cursor.tail.tail
- len -= 1
+ reduceLengthBy(1)
}
}
this
@@ -397,6 +414,7 @@ final class ListBuffer[A]
/** Copy contents of this buffer */
private def copy() {
+ if (isEmpty) return
var cursor = start
val limit = last0.tail
clear()