From ab25d6be26db7a1a03a09f03b650fe53d77e70e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 19 Aug 2020 18:44:18 +0200 Subject: [PATCH] Mitigate #9596 for SJS: Make objects serializable only in the JVM back-end. --- .../tools/backend/jvm/BCodeSkelBuilder.scala | 21 ++++++++++++++++++- .../src/dotty/tools/dotc/ast/Desugar.scala | 2 -- .../dotc/transform/SyntheticMembers.scala | 7 +++++-- tests/run-macros/tasty-extractors-2.check | 8 +++---- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala index d9a6346168e9..9a1bafa423c7 100644 --- a/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala +++ b/compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 6c07cc1a7a4d..c113634c9212 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 37f0128d5520..05a15059fffd 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -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, diff --git a/tests/run-macros/tasty-extractors-2.check b/tests/run-macros/tasty-extractors-2.check index b732b4353c2b..e5a3d6b42b84 100644 --- a/tests/run-macros/tasty-extractors-2.check +++ b/tests/run-macros/tasty-extractors-2.check @@ -25,7 +25,7 @@ OrType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int") Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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(())))) @@ -49,7 +49,7 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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()), ""), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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()), ""), 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("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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()), ""), Nil))))), ValDef("Foo", TypeIdent("Foo$"), Some(Apply(Select(New(TypeIdent("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil), Applied(Inferred(), List(Inferred()))), Nil, Some(ValDef("_", Singleton(Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), Inferred(), Some(Apply(Select(New(Inferred()), ""), 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("", Nil, List(List(ValDef("a", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(ValDef("a", Inferred(), None)))), Literal(Constant(())))) @@ -58,13 +58,13 @@ TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") Inlined(None, Nil, Block(List(ClassDef("Foo2", DefDef("", Nil, List(List(ValDef("b", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(ValDef("b", Inferred(), None)))), Literal(Constant(())))) TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Unit") -Inlined(None, Nil, Block(List(ClassDef("Foo3", DefDef("", Nil, List(List(ValDef("a", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(ValDef("a", Inferred(), None))), ValDef("Foo3", TypeIdent("Foo3$"), Some(Apply(Select(New(TypeIdent("Foo3$")), ""), Nil))), ClassDef("Foo3$", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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("", Nil, List(List(ValDef("a", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(ValDef("a", Inferred(), None))), ValDef("Foo3", TypeIdent("Foo3$"), Some(Apply(Select(New(TypeIdent("Foo3$")), ""), Nil))), ClassDef("Foo3$", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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("", Nil, List(List(ValDef("a", TypeIdent("Int"), None)), List(ValDef("b", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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("", Nil, List(List(ValDef("a", TypeIdent("Int"), None)), List(ValDef("b", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(ValDef("a", Inferred(), None), ValDef("b", Inferred(), None))), ValDef("Foo5", TypeIdent("Foo5$"), Some(Apply(Select(New(TypeIdent("Foo5$")), ""), Nil))), ClassDef("Foo5$", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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("", Nil, List(List(ValDef("a", TypeIdent("Int"), None)), List(ValDef("b", TypeIdent("Int"), None))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(ValDef("a", Inferred(), None), ValDef("b", Inferred(), None))), ValDef("Foo5", TypeIdent("Foo5$"), Some(Apply(Select(New(TypeIdent("Foo5$")), ""), Nil))), ClassDef("Foo5$", DefDef("", Nil, List(Nil), Inferred(), None), List(Apply(Select(New(Inferred()), ""), 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("", Nil, List(List(ValDef("a", TypeIdent("Int"), None)), List(ValDef("b", Singleton(Ident("a")), None))), Inferred(), None), List(Apply(Select(New(Inferred()), ""), Nil)), Nil, None, List(ValDef("a", Inferred(), None), ValDef("b", Inferred(), None)))), Literal(Constant(()))))