import java.io._
class Config(s: String)
class ConfigException(@transient val config: Config = null)
extends java.io.Serializable {}
object Main {
def main(args: Array[String]): Unit = {
val e =
new ConfigException(new Config("not serializable"))
val byteStream = new ByteArrayOutputStream()
val objectStream = new ObjectOutputStream(byteStream)
objectStream.writeObject(e)
objectStream.close()
val bytes = byteStream.toByteArray()
val inStream = new ByteArrayInputStream(bytes)
val inObjectStream = new ObjectInputStream(inStream)
val copy = inObjectStream.readObject()
inObjectStream.close()
}
}
sbt:dotty-simple> run
[info] Compiling 1 Scala source to /Users/eric/workspace/dotty-test/target/scala-0.27/classes ...
[info] running Main
[error] (run-main-1a) java.io.NotSerializableException: Config
[error] java.io.NotSerializableException: Config
[error] at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
[error] at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
[error] at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
[error] at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
[error] at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
[error] at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
[error] at Main$.main(Main.scala:15)
[error] at Main.main(Main.scala)
[error] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error] at java.lang.reflect.Method.invoke(Method.java:498)
[error] stack trace is suppressed; run last Compile / bgRun for the full output
[error] Nonzero exit code: 1
[error] (Compile / run) Nonzero exit code: 1
[error] Total time: 0 s, completed Sep 25, 2020 11:33:00 AM
sbt:dotty-simple>
In Scala 2, serialization avoids the @transient val so I am thinking it should avoid in Scala 3 too.
Note: If the @transient val or var is in the body of the class, Scala 3 does not try to serialize.
Here is the info about the class. I don't see any flags for @transient, ACC_TRANSIENT? so I think it may be just omitted.
Edit: removed classfile info as javap wasn't run with -private flag so the next comment is better.
Ok, I have confirmed that if the @transient is in the body we get the following:
private final transient Config config;
descriptor: LConfig;
flags: ACC_PRIVATE, ACC_FINAL, ACC_TRANSIENT
If the @transient is in the constructor we get the following:
private final Config config;
descriptor: LConfig;
flags: ACC_PRIVATE, ACC_FINAL
@transient is defined with the meta-annotation @field which lets the compiler know that it needs to be put on the field: https://github.com/scala/scala/blob/8c86b7d7136839538cca0ff8fca50f59437564c0/src/library/scala/transient.scala#L17-L18, but we have almost no support for meta-annotations currently (see https://github.com/scala/scala/blob/2.13.x/src/library/scala/annotation/meta/package.scala for how they're supposed to work) so this isn't trivial to fix. Here's a sketch of what should be done:
keepAnnotations = true for the constructor parameters but this is incorrect if meta-annotations exist for the current annotation unless the meta-annotaton @param is part of these meta-annotations.@field, @getter or @setter as a meta-annotationIn conclusion I really don't want to do this so I hope someone else will :).
Thank-you. With all that research it would seem that coderbot could do it.
Wow, very cool - thanks.
Most helpful comment
@transientis defined with the meta-annotation@fieldwhich lets the compiler know that it needs to be put on the field: https://github.com/scala/scala/blob/8c86b7d7136839538cca0ff8fca50f59437564c0/src/library/scala/transient.scala#L17-L18, but we have almost no support for meta-annotations currently (see https://github.com/scala/scala/blob/2.13.x/src/library/scala/annotation/meta/package.scala for how they're supposed to work) so this isn't trivial to fix. Here's a sketch of what should be done:keepAnnotations = truefor the constructor parameters but this is incorrect if meta-annotations exist for the current annotation unless the meta-annotaton@paramis part of these meta-annotations.@field,@getteror@setteras a meta-annotationIn conclusion I really don't want to do this so I hope someone else will :).