Skip to content

Commit 1a79132

Browse files
committed
Mitigate #9596 for SJS: Make objects serializable only in the JVM back-end.
1 parent c5e1f0c commit 1a79132

File tree

3 files changed

+25
-5
lines changed

3 files changed

+25
-5
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,11 +238,30 @@ trait BCodeSkelBuilder extends BCodeHelpers {
238238

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

247266
val flags = javaFlags(claszSymbol)
248267

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,8 +614,6 @@ object desugar {
614614
parents1 = enumClassTypeRef :: Nil
615615
if (isCaseClass | isCaseObject)
616616
parents1 = parents1 :+ scalaDot(str.Product.toTypeName) :+ scalaDot(nme.Serializable.toTypeName)
617-
else if (isObject)
618-
parents1 = parents1 :+ scalaDot(nme.Serializable.toTypeName)
619617
if (isEnum)
620618
parents1 = parents1 :+ ref(defn.EnumClass.typeRef)
621619

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,15 +358,18 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
358358
newSymbol(clazz, nme.writeReplace, Method | Private | Synthetic,
359359
MethodType(Nil, defn.AnyRefType), coord = clazz.coord).entered.asTerm
360360

361-
/** If this is a serializable static object `Foo`, add the method:
361+
/** If this is a static object `Foo`, add the method:
362362
*
363363
* private def writeReplace(): AnyRef =
364364
* new scala.runtime.ModuleSerializationProxy(classOf[Foo.type])
365365
*
366366
* unless an implementation already exists, otherwise do nothing.
367+
*
368+
* All static objects receive the `Serializable` flag in the back-end, so
369+
* we do that even for objects that are not serializable at this phase.
367370
*/
368371
def serializableObjectMethod(clazz: ClassSymbol)(using Context): List[Tree] =
369-
if clazz.is(Module) && clazz.isStatic && clazz.isSerializable && !hasWriteReplace(clazz) then
372+
if clazz.is(Module) && clazz.isStatic && !hasWriteReplace(clazz) then
370373
List(
371374
DefDef(writeReplaceDef(clazz),
372375
_ => New(defn.ModuleSerializationProxyClass.typeRef,

0 commit comments

Comments
 (0)