You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@Testdefcoll():Unit= {
valb=ListBuffer[AnyRef]()
valbar=newBar(b)
b += bar
SD.serializeDeserialize(b)
}
This fails with
java.lang.ClassCastException:
cannot assign instance of scala.collection.generic.DefaultSerializationProxy
to field scala.collection.mutable.Bar.c of type scala.collection.mutable.Iterable
in instance of scala.collection.mutable.Bar
@Testdefrepr():Unit= {
vala=newA(null)
valb=newB(a)
a.b = b
SD.serializeDeserialize(a)
}
The readResolve method is only invoked once the AProxy instance is fully deserialized. During deserialization, references to this a object resolve to the proxy.
The readResolve method is not invoked on the object until the object is fully constructed, so any references to this object in its object graph will not be updated to the new object nominated by readResolve. [...] if the reference types [...] are not compatible, the construction of the object graph will raise a ClassCastException.
The same behavior can be triggered with Java collections (agian @retronym's example), just that the use of a serialization proxy is less widespread in Java collections.
@Testdefjcoll():Unit= {
importjava.util.{ArrayList=>JAL}
importjava.util.{List=>JL}
valc1=newJAL[JL[_]]()
valc2=JL.of(c1)
c1.add(c2)
valc2c=SD.serializeDeserialize(c2)
c2c.get(0).get(0).size() // ClassCastException: class java.util.CollSer cannot be cast to class java.util.List
}
The text was updated successfully, but these errors were encountered:
Noting that this was present in 2.12 the cicularly-referred to collection was one of the ones that used serialization proxies (immutable.List notably). Scala 2.13 uses them pervasively so is more exposed to the issue.
A workaround may be indirect the reference through a wrapper that does not use the serialization proxy pattern.
The JDK could probably make it work for classes that use the default serialization. JDK deserialization uses Unsafe.putObject(obj, fieldOffset, value). That call can be delayed if value has a readResolve method. Once the readResolve is actually called, the resulting object can be stored in the field. I did an experiment with ByteBuddy: https://github.com/lrytz/scala/tree/t13092.
But the issue is with classes that implement their own writeObject / readObject, like our DefaultSerializationProxy. The readObject method does
I also saw that there are writeUnshared / readUnshared methods in ObjectOutputStream / ObjectInputStream. But I don't see how that would help, duplicating the proxies would lead to separate collection instances on deserialization.
Scala collections use a serialization proxy, which can leak during deserialization of a cyclic object graph.
Utility:
Test code:
This fails with
A stand-alone reproducer:
Test code:
The
readResolve
method is only invoked once theAProxy
instance is fully deserialized. During deserialization, references to thisa
object resolve to the proxy.@retronym points out that this is documented, last paragraph in https://docs.oracle.com/javase/8/docs/platform/serialization/spec/input.html#a5903
Links
The same behavior can be triggered with Java collections (agian @retronym's example), just that the use of a serialization proxy is less widespread in Java collections.
The text was updated successfully, but these errors were encountered: