From 2f717a41ad002789309894bfb4fd43cb25d26c2a Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 14 Mar 2022 16:27:00 +0100 Subject: [PATCH] Emit all classes as public to avoid object deserialization issues This aligns our behavior with Scala 2 and fixes the issue encountered in https://github.com/typelevel/cats-effect/pull/2360#issuecomment-1066895758. Alternatively, we could change ModuleSerializationProxy upstream to call `setAccessible(true)` on the MODULE$ field, but this wouldn't work if the object in question is inside a Java 9+ module. --- .../tools/backend/jvm/BTypesFromSymbols.scala | 6 +++++- tests/run/i4404a.check | 12 ++++-------- tests/run/i4404a.scala | 12 +++++------- tests/run/i4404b.check | 2 +- tests/run/serialize.scala | 15 +++++++++++++++ 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index 25a05077626a..dbdc92f95e9f 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -296,7 +296,11 @@ class BTypesFromSymbols[I <: DottyBackendInterface](val int: I) extends BTypes { */ final def javaFlags(sym: Symbol): Int = { - val privateFlag = sym.is(Private) || (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass) + // Classes are always emitted as public. This matches the behavior of Scala 2 + // and is necessary for object deserialization to work properly, otherwise + // ModuleSerializationProxy may fail with an accessiblity error (see + // tests/run/serialize.scala). + val privateFlag = !sym.isClass && (sym.is(Private) || (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass)) val finalFlag = sym.is(Final) && !toDenot(sym).isClassConstructor && !sym.is(Mutable) && !sym.enclosingClass.is(Trait) diff --git a/tests/run/i4404a.check b/tests/run/i4404a.check index 11444aa1f1fe..fa056b1cd9d5 100644 --- a/tests/run/i4404a.check +++ b/tests/run/i4404a.check @@ -1,8 +1,4 @@ -false -false -false -true -false -true -true -true +public class Car +public final class Volvo +public final class Car$ +public static final class Car$$anon$1 diff --git a/tests/run/i4404a.scala b/tests/run/i4404a.scala index b322218cf930..41d5e7064714 100644 --- a/tests/run/i4404a.scala +++ b/tests/run/i4404a.scala @@ -8,12 +8,10 @@ object Car { } object Test { - def main(args: Array[String]) = { - List(new Car, new Volvo, Car, Car.car) - .map(_.getClass.getModifiers) - .foreach { m => - println(Modifier.isPrivate(m)) - println(Modifier.isFinal(m)) - } + def main(args: Array[String]): Unit = { + val l = List(new Car, new Volvo, Car, Car.car) + .map(_.getClass) + .map(cls => s"${Modifier.toString(cls.getModifiers)} $cls") + println(l.mkString("\n")) } } diff --git a/tests/run/i4404b.check b/tests/run/i4404b.check index bb101b641b9b..1d474d525571 100644 --- a/tests/run/i4404b.check +++ b/tests/run/i4404b.check @@ -1,2 +1,2 @@ -true +false true diff --git a/tests/run/serialize.scala b/tests/run/serialize.scala index 0622807bee21..830c2935ac97 100644 --- a/tests/run/serialize.scala +++ b/tests/run/serialize.scala @@ -1,3 +1,14 @@ +package a { + object Outer extends Serializable { + private object Inner extends Serializable + + val inner: AnyRef = Inner + } + class Bar extends Serializable { + val x: AnyRef = Outer.inner + } +} + object Test { def serializeDeserialize[T <: AnyRef](obj: T): T = { import java.io.* @@ -26,5 +37,9 @@ object Test { val baz = serializeDeserialize(Baz) assert(baz ne Baz) + + val bar = new a.Bar + val bar1 = serializeDeserialize(bar) + assert(bar.x eq bar1.x) } }