diff --git a/project/MimaFilters.scala b/project/MimaFilters.scala index 31f5633182e6..c29288cb2467 100644 --- a/project/MimaFilters.scala +++ b/project/MimaFilters.scala @@ -78,6 +78,16 @@ object MimaFilters extends AutoPlugin { // #9741 ProblemFilters.exclude[MissingClassProblem]("scala.collection.immutable.SeqMap$SeqMapBuilderImpl"), // private[SeqMap] + + // #9752 + ProblemFilters.exclude[MissingTypesProblem]("scala.reflect.ClassTag$cache$"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.ModuleSerializationProxy$"), + ProblemFilters.exclude[MissingTypesProblem]("scala.reflect.runtime.JavaMirrors$JavaMirror$typeTagCache$"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.ClassValueCompat"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.ClassValueCompat$"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.ClassValueCompat$ClassValueInterface"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.ClassValueCompat$JavaClassValue"), + ProblemFilters.exclude[MissingClassProblem]("scala.runtime.ClassValueCompat$FallbackClassValue"), ) override val buildSettings = Seq( diff --git a/src/library/scala/reflect/ClassTag.scala b/src/library/scala/reflect/ClassTag.scala index b3ef8f781a9d..5226bb5577a8 100644 --- a/src/library/scala/reflect/ClassTag.scala +++ b/src/library/scala/reflect/ClassTag.scala @@ -15,8 +15,8 @@ package reflect import java.lang.{Class => jClass} import java.lang.ref.{WeakReference => jWeakReference} - import scala.annotation.{implicitNotFound, nowarn} +import scala.runtime.ClassValueCompat /** * @@ -116,7 +116,7 @@ object ClassTag { val Null : ClassTag[scala.Null] = Manifest.Null private val cacheDisabled = java.lang.Boolean.getBoolean("scala.reflect.classtag.cache.disable") - private[this] object cache extends ClassValue[jWeakReference[ClassTag[_]]] { + private[this] object cache extends ClassValueCompat[jWeakReference[ClassTag[_]]] { override def computeValue(runtimeClass: jClass[_]): jWeakReference[ClassTag[_]] = new jWeakReference(computeTag(runtimeClass)) diff --git a/src/library/scala/runtime/ClassValueCompat.scala b/src/library/scala/runtime/ClassValueCompat.scala new file mode 100644 index 000000000000..908c36c6ef3b --- /dev/null +++ b/src/library/scala/runtime/ClassValueCompat.scala @@ -0,0 +1,53 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + + +import scala.runtime.ClassValueCompat._ + +private[scala] abstract class ClassValueCompat[T] extends ClassValueInterface[T] { self => + private val instance: ClassValueInterface[T] = + if (classValueAvailable) new JavaClassValue() + else new FallbackClassValue() + + private class JavaClassValue extends ClassValue[T] with ClassValueInterface[T] { + override def computeValue(cls: Class[_]): T = self.computeValue(cls) + } + + private class FallbackClassValue extends ClassValueInterface[T] { + override def get(cls: Class[_]): T = self.computeValue(cls) + + override def remove(cls: Class[_]): Unit = {} + } + + def get(cls: Class[_]): T = instance.get(cls) + + def remove(cls: Class[_]): Unit = instance.remove(cls) + + protected def computeValue(cls: Class[_]): T +} + +private[scala] object ClassValueCompat { + trait ClassValueInterface[T] { + def get(cls: Class[_]): T + + def remove(cls: Class[_]): Unit + } + + private val classValueAvailable: Boolean = try { + Class.forName("java.lang.ClassValue", false, classOf[Object].getClassLoader) + true + } catch { + case _: ClassNotFoundException => false + } +} diff --git a/src/library/scala/runtime/ModuleSerializationProxy.java b/src/library/scala/runtime/ModuleSerializationProxy.java deleted file mode 100644 index d023faa1389c..000000000000 --- a/src/library/scala/runtime/ModuleSerializationProxy.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Scala (https://www.scala-lang.org) - * - * Copyright EPFL and Lightbend, Inc. - * - * Licensed under Apache License 2.0 - * (http://www.apache.org/licenses/LICENSE-2.0). - * - * See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - */ - -package scala.runtime; - -import java.io.Serializable; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.HashSet; -import java.util.Set; - -/** A serialization proxy for singleton objects */ -public final class ModuleSerializationProxy implements Serializable { - private static final long serialVersionUID = 1L; - private final Class moduleClass; - private static final ClassValue instances = new ClassValue() { - @Override - @SuppressWarnings("removal") // JDK 17 deprecates AccessController - protected Object computeValue(Class type) { - try { - return java.security.AccessController.doPrivileged((PrivilegedExceptionAction) () -> type.getField("MODULE$").get(null)); - } catch (PrivilegedActionException e) { - return rethrowRuntime(e.getCause()); - } - } - }; - - private static Object rethrowRuntime(Throwable e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException) throw (RuntimeException) cause; - else throw new RuntimeException(cause); - } - - public ModuleSerializationProxy(Class moduleClass) { - this.moduleClass = moduleClass; - } - - @SuppressWarnings("unused") - private Object readResolve() { - return instances.get(moduleClass); - } -} diff --git a/src/library/scala/runtime/ModuleSerializationProxy.scala b/src/library/scala/runtime/ModuleSerializationProxy.scala new file mode 100644 index 000000000000..cbb75d8e1032 --- /dev/null +++ b/src/library/scala/runtime/ModuleSerializationProxy.scala @@ -0,0 +1,43 @@ +/* + * Scala (https://www.scala-lang.org) + * + * Copyright EPFL and Lightbend, Inc. + * + * Licensed under Apache License 2.0 + * (http://www.apache.org/licenses/LICENSE-2.0). + * + * See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + */ + +package scala.runtime + +import java.io.Serializable +import java.security.PrivilegedActionException +import java.security.PrivilegedExceptionAction + +private[runtime] object ModuleSerializationProxy { + private val instances = new ClassValueCompat[Object] { + override protected def computeValue(cls: Class[_]): Object = { + try { + java.security.AccessController.doPrivileged((() => cls.getField("MODULE$").get(null)): PrivilegedExceptionAction[Object]) + } catch { + case e: PrivilegedActionException => + rethrowRuntime(e.getCause) + } + } + } + + private def rethrowRuntime(e: Throwable): Object = { + val cause = e.getCause + cause match { + case exception: RuntimeException => throw exception + case _ => throw new RuntimeException(cause) + } + } +} + +@SerialVersionUID(1L) +final class ModuleSerializationProxy(moduleClass: Class[_]) extends Serializable { + private def readResolve = ModuleSerializationProxy.instances.get(moduleClass) +} diff --git a/src/reflect/scala/reflect/macros/Attachments.scala b/src/reflect/scala/reflect/macros/Attachments.scala index 688721e410e3..05318a84ba5f 100644 --- a/src/reflect/scala/reflect/macros/Attachments.scala +++ b/src/reflect/scala/reflect/macros/Attachments.scala @@ -15,6 +15,7 @@ package reflect package macros import reflect.internal.util.Position +import scala.runtime.ClassValueCompat /** * EXPERIMENTAL @@ -109,7 +110,7 @@ abstract class Attachments { self => } private object Attachments { - private val matchesTagCache = new ClassValue[Function1[Any, Boolean]] { + private val matchesTagCache = new ClassValueCompat[Function1[Any, Boolean]] { override def computeValue(cls: Class[_]): Function[Any, Boolean] = cls.isInstance(_) } } diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index d0f318bedd37..69ff6474c8cb 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -38,7 +38,7 @@ import internal.Flags._ import ReflectionUtils._ import scala.annotation.nowarn import scala.reflect.api.TypeCreator -import scala.runtime.{ BoxesRunTime, ScalaRunTime } +import scala.runtime.{BoxesRunTime, ClassValueCompat, ScalaRunTime} private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse with TwoWayCaches { thisUniverse: SymbolTable => @@ -120,7 +120,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive private[this] val fieldCache = new TwoWayCache[jField, TermSymbol] private[this] val tparamCache = new TwoWayCache[jTypeVariable[_ <: GenericDeclaration], TypeSymbol] - private[this] object typeTagCache extends ClassValue[jWeakReference[TypeTag[_]]]() { + private[this] object typeTagCache extends ClassValueCompat[jWeakReference[TypeTag[_]]]() { val typeCreator = new ThreadLocal[TypeCreator]() override protected def computeValue(cls: jClass[_]): jWeakReference[TypeTag[_]] = {