Skip to content

Proxy-based serialization for all collections #6676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/library/scala/Enumeration.scala
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ abstract class Enumeration (initial: Int) extends Serializable {

def map(f: Value => Value): ValueSet = fromSpecificIterable(new View.Map(toIterable, f))
def flatMap(f: Value => IterableOnce[Value]): ValueSet = fromSpecificIterable(new View.FlatMap(toIterable, f))

override protected[this] def writeReplace(): AnyRef = this
}

/** A factory object for value sets */
Expand Down
32 changes: 32 additions & 0 deletions src/library/scala/collection/BitSet.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package scala
package collection

import java.io.{ObjectInputStream, ObjectOutputStream}

import scala.annotation.unchecked.uncheckedVariance
import scala.collection.mutable.Builder

Expand All @@ -27,6 +29,36 @@ object BitSet extends SpecificIterableFactory[Int, BitSet] {
def empty: BitSet = immutable.BitSet.empty
def newBuilder: Builder[Int, BitSet] = immutable.BitSet.newBuilder
def fromSpecific(it: IterableOnce[Int]): BitSet = immutable.BitSet.fromSpecific(it)

@SerialVersionUID(3L)
private[collection] abstract class SerializationProxy(@transient protected val coll: BitSet) extends Serializable {

@transient protected var elems: Array[Long] = _

private[this] def writeObject(out: ObjectOutputStream): Unit = {
out.defaultWriteObject()
val nwords = coll.nwords
out.writeInt(nwords)
var i = 0
while(i < nwords) {
out.writeLong(coll.word(i))
i += 1
}
}

private[this] def readObject(in: ObjectInputStream): Unit = {
in.defaultReadObject()
val nwords = in.readInt()
elems = new Array[Long](nwords)
var i = 0
while(i < nwords) {
elems(i) = in.readLong()
i += 1
}
}

protected[this] def readResolve(): Any
}
}

/** Base implementation type of bitsets */
Expand Down
23 changes: 10 additions & 13 deletions src/library/scala/collection/Factory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ package scala
package collection

import scala.collection.immutable.NumericRange

import scala.language.{ higherKinds, implicitConversions }
import scala.language.{higherKinds, implicitConversions}
import scala.collection.mutable.Builder

import scala.collection.generic.{DefaultSerializationProxy, SerializeEnd}
import scala.annotation.unchecked.uncheckedVariance
import scala.reflect.ClassTag

Expand Down Expand Up @@ -70,7 +69,7 @@ object Factory {
* @define coll collection
* @define Coll `Iterable`
*/
trait IterableFactory[+CC[_]] {
trait IterableFactory[+CC[_]] extends Serializable {

/** Creates a target $coll from an existing source collection
*
Expand Down Expand Up @@ -224,7 +223,6 @@ trait IterableFactory[+CC[_]] {
tabulate(n1)(i1 => tabulate(n2, n3, n4, n5)(f(i1, _, _, _, _)))

implicit def iterableFactory[A]: Factory[A, CC[A]] = IterableFactory.toFactory(this)

}

object IterableFactory {
Expand All @@ -238,7 +236,7 @@ object IterableFactory {
* of type `A`
*/
implicit def toFactory[A, CC[_]](factory: IterableFactory[CC]): Factory[A, CC[A]] =
new Factory[A, CC[A]] {
new Factory[A, CC[A]] with Serializable {
def fromSpecific(it: IterableOnce[A]): CC[A] = factory.from[A](it)
def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A]
}
Expand Down Expand Up @@ -326,7 +324,7 @@ trait SpecificIterableFactory[-A, +C] extends Factory[A, C] {
* @define coll collection
* @define Coll `Iterable`
*/
trait MapFactory[+CC[_, _]] {
trait MapFactory[+CC[_, _]] extends Serializable {

def empty[K, V]: CC[K, V]

Expand All @@ -337,7 +335,6 @@ trait MapFactory[+CC[_, _]] {
def newBuilder[K, V]: Builder[(K, V), CC[K, V]]

implicit def mapFactory[K, V]: Factory[(K, V), CC[K, V]] = MapFactory.toFactory(this)

}

object MapFactory {
Expand All @@ -352,7 +349,7 @@ object MapFactory {
* and values of type `V`
*/
implicit def toFactory[K, V, CC[_, _]](factory: MapFactory[CC]): Factory[(K, V), CC[K, V]] =
new Factory[(K, V), CC[K, V]] {
new Factory[(K, V), CC[K, V]] with Serializable {
def fromSpecific(it: IterableOnce[(K, V)]): CC[K, V] = factory.from[K, V](it)
def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V]
}
Expand Down Expand Up @@ -381,7 +378,7 @@ object MapFactory {
* @define coll collection
* @define Coll `Iterable`
*/
trait EvidenceIterableFactory[+CC[_], Ev[_]] {
trait EvidenceIterableFactory[+CC[_], Ev[_]] extends Serializable {

def from[E : Ev](it: IterableOnce[E]): CC[E]

Expand Down Expand Up @@ -420,7 +417,7 @@ object EvidenceIterableFactory {
* of type `A`
*/
implicit def toFactory[Ev[_], A: Ev, CC[_]](factory: EvidenceIterableFactory[CC, Ev]): Factory[A, CC[A]] =
new Factory[A, CC[A]] {
new Factory[A, CC[A]] with Serializable {
def fromSpecific(it: IterableOnce[A]): CC[A] = factory.from[A](it)
def newBuilder: Builder[A, CC[A]] = factory.newBuilder[A]
}
Expand Down Expand Up @@ -639,7 +636,7 @@ trait StrictOptimizedClassTagSeqFactory[+CC[_]] extends ClassTagSeqFactory[CC] {
* @define coll collection
* @define Coll `Iterable`
*/
trait SortedMapFactory[+CC[_, _]] {
trait SortedMapFactory[+CC[_, _]] extends Serializable {

def empty[K : Ordering, V]: CC[K, V]

Expand Down Expand Up @@ -667,7 +664,7 @@ object SortedMapFactory {
* type `K` and values of type `V`
*/
implicit def toFactory[K : Ordering, V, CC[_, _]](factory: SortedMapFactory[CC]): Factory[(K, V), CC[K, V]] =
new Factory[(K, V), CC[K, V]] {
new Factory[(K, V), CC[K, V]] with Serializable {
def fromSpecific(it: IterableOnce[(K, V)]): CC[K, V] = factory.from[K, V](it)
def newBuilder: Builder[(K, V), CC[K, V]] = factory.newBuilder[K, V]
}
Expand Down
26 changes: 15 additions & 11 deletions src/library/scala/collection/IndexedSeqView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@ trait IndexedSeqView[+A] extends IndexedSeqOps[A, View, View[A]] with SeqView[A]

override def view: IndexedSeqView[A] = this

override def iterator: Iterator[A] = new AbstractIterator[A] {
private var current = 0
override def knownSize: Int = self.size - current
def hasNext = current < self.size
def next(): A = {
val r = self.apply(current)
current += 1
r
}
}
override def iterator: Iterator[A] = new IndexedSeqView.IndexedSeqViewIterator(this)

override def prepended[B >: A](elem: B): IndexedSeqView[B] = new IndexedSeqView.Prepended(elem, this)
override def take(n: Int): IndexedSeqView[A] = new IndexedSeqView.Take(this, n)
Expand All @@ -30,6 +21,18 @@ trait IndexedSeqView[+A] extends IndexedSeqOps[A, View, View[A]] with SeqView[A]

object IndexedSeqView {

@SerialVersionUID(3L)
private final class IndexedSeqViewIterator[A](self: IndexedSeqView[A]) extends AbstractIterator[A] with Serializable {
private var current = 0
override def knownSize: Int = self.size - current
def hasNext = current < self.size
def next(): A = {
val r = self.apply(current)
current += 1
r
}
}

/** An `IndexedSeqOps` whose collection type and collection type constructor are unknown */
type SomeIndexedSeqOps[A] = IndexedSeqOps[A, AnyConstr, _]

Expand Down Expand Up @@ -82,4 +85,5 @@ object IndexedSeqView {
}

/** Explicit instantiation of the `IndexedSeqView` trait to reduce class file size in subclasses. */
abstract class AbstractIndexedSeqView[+A] extends AbstractSeqView[A] with IndexedSeqView[A]
@SerialVersionUID(3L)
abstract class AbstractIndexedSeqView[+A] extends AbstractSeqView[A] with IndexedSeqView[A]
16 changes: 12 additions & 4 deletions src/library/scala/collection/Iterable.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package scala
package collection

import scala.annotation.unchecked.uncheckedVariance
import scala.language.{higherKinds, implicitConversions}
import scala.annotation.unchecked.uncheckedVariance
import scala.collection.mutable.{ArrayBuffer, Builder, StringBuilder}
import scala.reflect.ClassTag
import java.io.{ObjectInputStream, ObjectOutputStream}
import java.lang.{String, UnsupportedOperationException}

import scala.collection.mutable.{ArrayBuffer, Builder, StringBuilder}
import java.lang.String
import scala.collection.generic.DefaultSerializationProxy

/** Base trait for generic collections.
*
Expand All @@ -16,7 +17,7 @@ import java.lang.String
* @define Coll `Iterable`
* @define coll iterable collection
*/
trait Iterable[+A] extends IterableOnce[A] with IterableOps[A, Iterable, Iterable[A]] {
trait Iterable[+A] extends IterableOnce[A] with IterableOps[A, Iterable, Iterable[A]] with Serializable {

// The collection itself
final def toIterable: this.type = this
Expand All @@ -39,6 +40,12 @@ trait Iterable[+A] extends IterableOnce[A] with IterableOps[A, Iterable, Iterabl

@deprecated("Iterable.seq always returns the iterable itself", "2.13.0")
def seq: this.type = this

/** Create a proxy for Java serialization. The default implementation will serialize all elements and
* deserialize by using a builder for `CC` via `iterableFactory`. Override in subclasses if more data needs
* to be preserved or a more efficient implementation is available. Override to return `this` in order to
* use self-serialization instead of a proxy. */
protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(iterableFactory.iterableFactory, this)
}

/** Base trait for Iterable operations
Expand Down Expand Up @@ -756,4 +763,5 @@ object Iterable extends IterableFactory.Delegate[Iterable](immutable.Iterable) {
}

/** Explicit instantiation of the `Iterable` trait to reduce class file size in subclasses. */
@SerialVersionUID(3L)
abstract class AbstractIterable[+A] extends Iterable[A]
7 changes: 6 additions & 1 deletion src/library/scala/collection/Iterator.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package scala.collection

import java.io.{ObjectInputStream, ObjectOutputStream}
import java.util.concurrent.atomic.{AtomicInteger, AtomicReference}

import scala.collection.mutable.{ArrayBuffer, Builder, ImmutableBuilder, StringBuilder}

import scala.annotation.tailrec
import scala.annotation.unchecked.uncheckedVariance

Expand Down Expand Up @@ -1090,6 +1090,11 @@ object Iterator extends IterableFactory[Iterator] {
}
}
}

// scalac generates a `readReplace` method to discard the deserialized state (see https://github.com/scala/bug/issues/10412).
// This prevents it from serializing it in the first place:
private[this] def writeObject(out: ObjectOutputStream): Unit = ()
private[this] def readObject(in: ObjectInputStream): Unit = ()
}

/** Explicit instantiation of the `Iterator` trait to reduce class file size in subclasses. */
Expand Down
9 changes: 5 additions & 4 deletions src/library/scala/collection/Map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package scala
package collection

import collection.mutable.Builder

import scala.annotation.unchecked.uncheckedVariance
import scala.language.{higherKinds,implicitConversions}
import scala.collection.generic.DefaultSerializationProxy
import scala.language.{higherKinds, implicitConversions}
import scala.util.hashing.MurmurHash3

/** Base Map type */
Expand Down Expand Up @@ -47,6 +47,7 @@ trait Map[K, +V]

override def hashCode(): Int = MurmurHash3.mapHash(toIterable)

override protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(mapFactory.mapFactory[K, V], this)
}

/** Base Map implementation type
Expand Down Expand Up @@ -132,13 +133,12 @@ trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]

/** The implementation class of the set returned by `keySet`.
*/
@SerialVersionUID(3L)
protected class KeySet extends AbstractSet[K] with GenKeySet {
def diff(that: Set[K]): Set[K] = fromSpecificIterable(view.filterNot(that))
}

/** A generic trait that is reused by keyset implementations */
protected trait GenKeySet extends Serializable { this: Set[K] =>
protected trait GenKeySet { this: Set[K] =>
def iterator: Iterator[K] = MapOps.this.keysIterator
def contains(key: K): Boolean = MapOps.this.contains(key)
override def size: Int = MapOps.this.size
Expand Down Expand Up @@ -315,4 +315,5 @@ object Map extends MapFactory.Delegate[Map](immutable.Map) {
}

/** Explicit instantiation of the `Map` trait to reduce class file size in subclasses. */
@SerialVersionUID(3L)
abstract class AbstractMap[K, +V] extends AbstractIterable[(K, V)] with Map[K, V]
1 change: 1 addition & 0 deletions src/library/scala/collection/MapView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ object MapView {
}

/** Explicit instantiation of the `MapView` trait to reduce class file size in subclasses. */
@SerialVersionUID(3L)
abstract class AbstractMapView[K, +V] extends AbstractView[(K, V)] with MapView[K, V]
1 change: 1 addition & 0 deletions src/library/scala/collection/Seq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1008,4 +1008,5 @@ object SeqOps {
}

/** Explicit instantiation of the `Seq` trait to reduce class file size in subclasses. */
@SerialVersionUID(3L)
abstract class AbstractSeq[+A] extends AbstractIterable[A] with Seq[A]
1 change: 1 addition & 0 deletions src/library/scala/collection/SeqView.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ object SeqView {
}

/** Explicit instantiation of the `SeqView` trait to reduce class file size in subclasses. */
@SerialVersionUID(3L)
abstract class AbstractSeqView[+A] extends AbstractView[A] with SeqView[A]
1 change: 1 addition & 0 deletions src/library/scala/collection/Set.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,5 @@ trait SetOps[A, +CC[_], +C <: SetOps[A, CC, C]]
object Set extends IterableFactory.Delegate[Set](immutable.Set)

/** Explicit instantiation of the `Set` trait to reduce class file size in subclasses. */
@SerialVersionUID(3L)
abstract class AbstractSet[A] extends AbstractIterable[A] with Set[A]
5 changes: 3 additions & 2 deletions src/library/scala/collection/SortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ package collection
import scala.collection.immutable.TreeMap
import scala.collection.mutable.Builder
import scala.language.higherKinds

import scala.annotation.unchecked.uncheckedVariance
import scala.collection.generic.DefaultSerializationProxy

/** Base type of sorted sets */
trait SortedMap[K, +V]
Expand All @@ -28,6 +28,8 @@ trait SortedMap[K, +V]
def sortedMapFactory: SortedMapFactory[SortedMapCC] = SortedMap

override def empty: SortedMapCC[K, V] @uncheckedVariance = sortedMapFactory.empty

override protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(sortedMapFactory.sortedMapFactory[K, V], this)
}

trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _], +C <: SortedMapOps[K, V, CC, C]]
Expand Down Expand Up @@ -117,7 +119,6 @@ trait SortedMapOps[K, +V, +CC[X, Y] <: Map[X, Y] with SortedMapOps[X, Y, CC, _],
override def keySet: SortedSet[K] = new KeySortedSet

/** The implementation class of the set returned by `keySet` */
@SerialVersionUID(3L)
protected class KeySortedSet extends SortedSet[K] with GenKeySet with GenKeySortedSet {
def diff(that: Set[K]): SortedSet[K] = fromSpecificIterable(view.filterNot(that))
def rangeImpl(from: Option[K], until: Option[K]): SortedSet[K] = {
Expand Down
3 changes: 3 additions & 0 deletions src/library/scala/collection/SortedSet.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala.collection

import scala.annotation.unchecked.uncheckedVariance
import scala.collection.generic.DefaultSerializationProxy
import scala.language.higherKinds

/** Base type of sorted sets */
Expand All @@ -21,6 +22,8 @@ trait SortedSet[A] extends Set[A] with SortedSetOps[A, SortedSet, SortedSet[A]]
def sortedIterableFactory: SortedIterableFactory[SortedIterableCC] = SortedSet

override def empty: SortedIterableCC[A] = sortedIterableFactory.empty

override protected[this] def writeReplace(): AnyRef = new DefaultSerializationProxy(sortedIterableFactory.evidenceIterableFactory[A], this)
}

trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSetOps[A, CC, C]]
Expand Down
Loading