Skip to content

Mitigate #9596 for SJS: Make objects serializable only in the JVM back-end. #9600

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
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
21 changes: 20 additions & 1 deletion compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,30 @@ trait BCodeSkelBuilder extends BCodeHelpers {

val ps = claszSymbol.info.parents
val superClass: String = if (ps.isEmpty) ObjectReference.internalName else internalName(ps.head.widenDealias.typeSymbol)
val interfaceNames = classBTypeFromSymbol(claszSymbol).info.interfaces map {
val interfaceNames0 = classBTypeFromSymbol(claszSymbol).info.interfaces map {
case classBType =>
if (classBType.isNestedClass) { innerClassBufferASM += classBType }
classBType.internalName
}
/* To avoid deadlocks when combining objects, lambdas and multi-threading,
* lambdas in objects are compiled to instance methods of the module class
* instead of static methods (see tests/run/deadlock.scala and
* https://github.com/scala/scala-dev/issues/195 for details).
* This has worked well for us so far but this is problematic for
* serialization: serializing a lambda requires serializing all the values
* it captures, if this lambda is in an object, this means serializing the
* enclosing object, which fails if the object does not extend
* Serializable.
* Because serializing objects is basically free since #5775, it seems like
* the simplest solution is to simply make all objects Serializable, this
* certainly seems preferable to deadlocks.
* This cannot be done earlier because Scala.js would not like it (#9596).
*/
val interfaceNames =
if (claszSymbol.is(ModuleClass) && !interfaceNames0.contains("java/io/Serializable"))
interfaceNames0 :+ "java/io/Serializable"
else
interfaceNames0

val flags = javaFlags(claszSymbol)

Expand Down
2 changes: 0 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -614,8 +614,6 @@ object desugar {
parents1 = enumClassTypeRef :: Nil
if (isCaseClass | isCaseObject)
parents1 = parents1 :+ scalaDot(str.Product.toTypeName) :+ scalaDot(nme.Serializable.toTypeName)
else if (isObject)
parents1 = parents1 :+ scalaDot(nme.Serializable.toTypeName)
if (isEnum)
parents1 = parents1 :+ ref(defn.EnumClass.typeRef)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,15 +358,18 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
newSymbol(clazz, nme.writeReplace, Method | Private | Synthetic,
MethodType(Nil, defn.AnyRefType), coord = clazz.coord).entered.asTerm

/** If this is a serializable static object `Foo`, add the method:
/** If this is a static object `Foo`, add the method:
*
* private def writeReplace(): AnyRef =
* new scala.runtime.ModuleSerializationProxy(classOf[Foo.type])
*
* unless an implementation already exists, otherwise do nothing.
*
* All static objects receive the `Serializable` flag in the back-end, so
* we do that even for objects that are not serializable at this phase.
*/
def serializableObjectMethod(clazz: ClassSymbol)(using Context): List[Tree] =
if clazz.is(Module) && clazz.isStatic && clazz.isSerializable && !hasWriteReplace(clazz) then
if clazz.is(Module) && clazz.isStatic && !hasWriteReplace(clazz) then
List(
DefDef(writeReplaceDef(clazz),
_ => New(defn.ModuleSerializationProxyClass.typeRef,
Expand Down
8 changes: 4 additions & 4 deletions tests/run-macros/tasty-extractors-2.check
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ OrType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")
Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, Nil)), Literal(Constant(()))))
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")

Inlined(None, Nil, Block(List(ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), "<init>"), Nil))), ClassDef("Foo$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), Nil, Some(ValDef("_", Singleton(Ident("Foo")), None)), Nil)), Literal(Constant(()))))
Inlined(None, Nil, Block(List(ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), "<init>"), Nil))), ClassDef("Foo$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, Some(ValDef("_", Singleton(Ident("Foo")), None)), Nil)), Literal(Constant(()))))
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")

Inlined(None, Nil, Block(List(TypeDef("Foo", TypeBoundsTree(Inferred(), Inferred()))), Literal(Constant(()))))
Expand All @@ -49,7 +49,7 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(DefDef("a", Nil, Nil, Inferred(), Some(Literal(Constant(0))))))), Literal(Constant(()))))
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")

Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Product"), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), Nil, None, List(DefDef("copy", Nil, List(Nil), Inferred(), Some(Apply(Select(New(Inferred()), "<init>"), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), "<init>"), Nil))), ClassDef("Foo$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil), Applied(Inferred(), List(Inferred())), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), Nil, Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), Inferred(), Some(Apply(Select(New(Inferred()), "<init>"), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", Inferred(), None))), Singleton(Literal(Constant(true))), Some(Literal(Constant(true))))))), Literal(Constant(()))))
Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Product"), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), Nil, None, List(DefDef("copy", Nil, List(Nil), Inferred(), Some(Apply(Select(New(Inferred()), "<init>"), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), "<init>"), Nil))), ClassDef("Foo$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil), Applied(Inferred(), List(Inferred()))), Nil, Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), Inferred(), Some(Apply(Select(New(Inferred()), "<init>"), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", Inferred(), None))), Singleton(Literal(Constant(true))), Some(Literal(Constant(true))))))), Literal(Constant(()))))
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")

Inlined(None, Nil, Block(List(ClassDef("Foo1", DefDef("<init>", Nil, List(List(ValDef("a", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(ValDef("a", Inferred(), None)))), Literal(Constant(()))))
Expand All @@ -58,13 +58,13 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")
Inlined(None, Nil, Block(List(ClassDef("Foo2", DefDef("<init>", Nil, List(List(ValDef("b", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(ValDef("b", Inferred(), None)))), Literal(Constant(()))))
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")

Inlined(None, Nil, Block(List(ClassDef("Foo3", DefDef("<init>", Nil, List(List(ValDef("a", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(ValDef("a", Inferred(), None))), ValDef("Foo3", TypeIdent("Foo3$"), Some(Apply(Select(New(TypeIdent("Foo3$")), "<init>"), Nil))), ClassDef("Foo3$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), Nil, Some(ValDef("_", Singleton(Ident("Foo3")), None)), List(DefDef("$lessinit$greater$default$1", Nil, Nil, Inferred(), Some(Literal(Constant(5))))))), Literal(Constant(()))))
Inlined(None, Nil, Block(List(ClassDef("Foo3", DefDef("<init>", Nil, List(List(ValDef("a", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(ValDef("a", Inferred(), None))), ValDef("Foo3", TypeIdent("Foo3$"), Some(Apply(Select(New(TypeIdent("Foo3$")), "<init>"), Nil))), ClassDef("Foo3$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, Some(ValDef("_", Singleton(Ident("Foo3")), None)), List(DefDef("$lessinit$greater$default$1", Nil, Nil, Inferred(), Some(Literal(Constant(5))))))), Literal(Constant(()))))
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")

Inlined(None, Nil, Block(List(ClassDef("Foo4", DefDef("<init>", Nil, List(List(ValDef("a", TypeIdent("Int"), None)), List(ValDef("b", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(ValDef("a", Inferred(), None), ValDef("b", Inferred(), None)))), Literal(Constant(()))))
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")

Inlined(None, Nil, Block(List(ClassDef("Foo5", DefDef("<init>", Nil, List(List(ValDef("a", TypeIdent("Int"), None)), List(ValDef("b", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(ValDef("a", Inferred(), None), ValDef("b", Inferred(), None))), ValDef("Foo5", TypeIdent("Foo5$"), Some(Apply(Select(New(TypeIdent("Foo5$")), "<init>"), Nil))), ClassDef("Foo5$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil), TypeSelect(Select(Ident("_root_"), "scala"), "Serializable")), Nil, Some(ValDef("_", Singleton(Ident("Foo5")), None)), List(DefDef("$lessinit$greater$default$2", Nil, List(List(ValDef("a", TypeIdent("Int"), None))), Inferred(), Some(Ident("a")))))), Literal(Constant(()))))
Inlined(None, Nil, Block(List(ClassDef("Foo5", DefDef("<init>", Nil, List(List(ValDef("a", TypeIdent("Int"), None)), List(ValDef("b", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(ValDef("a", Inferred(), None), ValDef("b", Inferred(), None))), ValDef("Foo5", TypeIdent("Foo5$"), Some(Apply(Select(New(TypeIdent("Foo5$")), "<init>"), Nil))), ClassDef("Foo5$", DefDef("<init>", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, Some(ValDef("_", Singleton(Ident("Foo5")), None)), List(DefDef("$lessinit$greater$default$2", Nil, List(List(ValDef("a", TypeIdent("Int"), None))), Inferred(), Some(Ident("a")))))), Literal(Constant(()))))
TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit")

Inlined(None, Nil, Block(List(ClassDef("Foo6", DefDef("<init>", Nil, List(List(ValDef("a", TypeIdent("Int"), None)), List(ValDef("b", Singleton(Ident("a")), None))), Inferred(), None), List(Apply(Select(New(Inferred()), "<init>"), Nil)), Nil, None, List(ValDef("a", Inferred(), None), ValDef("b", Inferred(), None)))), Literal(Constant(()))))
Expand Down