Skip to content

Commit a5fc6e6

Browse files
author
Scott Carey
committed
SI-8042 Use Serialization Proxy Pattern in List
Modify List to use the Serialization Proxy Pattern instead of directly mutating its state during deserialization. Use the proxy at the List level for both Nil and :: for simplicity. Change one member variable (hd) to val from var as a result. The other member variable (tl) cannot yet be changed to val because it is mutated by ListBuffer in the library and Types in reflection.
1 parent 73cddba commit a5fc6e6

File tree

1 file changed

+34
-24
lines changed

1 file changed

+34
-24
lines changed

src/library/scala/collection/immutable/List.scala

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,39 @@ sealed abstract class List[+A] extends AbstractSeq[A]
322322
override def toStream : Stream[A] =
323323
if (isEmpty) Stream.Empty
324324
else new Stream.Cons(head, tail.toStream)
325+
326+
// Create a proxy for Java serialization that allows us to avoid mutation
327+
// during de-serialization. This is the Serialization Proxy Pattern.
328+
protected final def writeReplace(): AnyRef = new SerializationProxy(this)
329+
}
330+
331+
@SerialVersionUID(1L)
332+
private class SerializationProxy[B](@transient private var orig: List[B]) extends Serializable {
333+
334+
private def writeObject(out: ObjectOutputStream) {
335+
var xs: List[B] = orig
336+
while (!xs.isEmpty) {
337+
out.writeObject(xs.head)
338+
xs = xs.tail
339+
}
340+
out.writeObject(ListSerializeEnd)
341+
}
342+
343+
// Java serialization calls this before readResolve during de-serialization.
344+
// Read the whole list and store it in `orig`.
345+
private def readObject(in: ObjectInputStream) {
346+
val builder = List.newBuilder[B]
347+
while (true) in.readObject match {
348+
case ListSerializeEnd =>
349+
orig = builder.result()
350+
return
351+
case a =>
352+
builder += a.asInstanceOf[B]
353+
}
354+
}
355+
356+
// Provide the result stored in `orig` for Java serialization
357+
private def readResolve(): AnyRef = orig
325358
}
326359

327360
/** The empty list.
@@ -352,33 +385,10 @@ case object Nil extends List[Nothing] {
352385
* @version 1.0, 15/07/2003
353386
* @since 2.8
354387
*/
355-
@SerialVersionUID(0L - 8476791151983527571L)
356-
final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extends List[B] {
388+
final case class ::[B](private val hd: B, private[scala] var tl: List[B]) extends List[B] {
357389
override def head : B = hd
358390
override def tail : List[B] = tl
359391
override def isEmpty: Boolean = false
360-
361-
private def readObject(in: ObjectInputStream) {
362-
val firstObject = in.readObject()
363-
hd = firstObject.asInstanceOf[B]
364-
assert(hd != ListSerializeEnd)
365-
var current: ::[B] = this
366-
while (true) in.readObject match {
367-
case ListSerializeEnd =>
368-
current.tl = Nil
369-
return
370-
case a =>
371-
val list : ::[B] = new ::(a.asInstanceOf[B], Nil)
372-
current.tl = list
373-
current = list
374-
}
375-
}
376-
377-
private def writeObject(out: ObjectOutputStream) {
378-
var xs: List[B] = this
379-
while (!xs.isEmpty) { out.writeObject(xs.head); xs = xs.tail }
380-
out.writeObject(ListSerializeEnd)
381-
}
382392
}
383393

384394
/** $factoryInfo

0 commit comments

Comments
 (0)