From 5762110d0dc2cb492e34d5595c473aa0f9ca786a Mon Sep 17 00:00:00 2001 From: Philipp Haller Date: Wed, 9 Jul 2014 14:43:12 +0200 Subject: SI-5919 TypeTags and Exprs should be serializable - Make TypeCreator and TreeCreator extend Serializable. - When replacing a SerializedTypeTag with a TypeTag or WeakTypeTag, do not use scala.reflect.runtime.universe.rootMirror, since it is unlikely to find user classes; instead, create a runtime mirror using the context ClassLoader of the current thread. Use the same logic for SerializedExpr. - Remove writeObject/readObject methods from SerializedTypeTag and SerializedExpr since they are unused. - Add @throws annotation on writeReplace and readResolve methods. - Handle SecurityException if the current thread cannot access the context ClassLoader. - To make type tags of primitive value classes serializable, make PredefTypeCreator a top-level class. Otherwise, it would retain a reference to the enclosing Universe, rendering the TypeCreator non-serializable. Binary compatibility: - Keep nested PredefTypeCreator class to avoid backward binary incompatible change. - Keep `var` modifiers on the class parameters of SerializedTypeTag for backward binary compatibility. - Adds filter rules to forward binary compatibility whitelist: - `TypeCreator`, `PredefTypeCreator`, and `TreeCreator` must now extend from `Serializable`. - Must have new class `scala.reflect.api.PredefTypeCreator` to avoid problematic outer reference. --- src/reflect/scala/reflect/api/Exprs.scala | 22 +++++++------- src/reflect/scala/reflect/api/TreeCreator.scala | 6 ++-- src/reflect/scala/reflect/api/TypeCreator.scala | 2 +- src/reflect/scala/reflect/api/TypeTags.scala | 40 +++++++++++++++++-------- 4 files changed, 42 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/reflect/scala/reflect/api/Exprs.scala b/src/reflect/scala/reflect/api/Exprs.scala index 5b6ff2325c..6d401b5a79 100644 --- a/src/reflect/scala/reflect/api/Exprs.scala +++ b/src/reflect/scala/reflect/api/Exprs.scala @@ -9,6 +9,7 @@ package api import scala.reflect.runtime.{universe => ru} import scala.annotation.compileTimeOnly +import java.io.ObjectStreamException /** * EXPERIMENTAL @@ -157,23 +158,22 @@ trait Exprs { self: Universe => |if you want to get a value of the underlying expression, add scala-compiler.jar to the classpath, |import `scala.tools.reflect.Eval` and call `.eval` instead.""".trim.stripMargin) + @throws(classOf[ObjectStreamException]) private def writeReplace(): AnyRef = new SerializedExpr(treec, implicitly[WeakTypeTag[T]].in(ru.rootMirror)) } } private[scala] class SerializedExpr(var treec: TreeCreator, var tag: ru.WeakTypeTag[_]) extends Serializable { - private def writeObject(out: java.io.ObjectOutputStream): Unit = { - out.writeObject(treec) - out.writeObject(tag) - } - - private def readObject(in: java.io.ObjectInputStream): Unit = { - treec = in.readObject().asInstanceOf[TreeCreator] - tag = in.readObject().asInstanceOf[ru.WeakTypeTag[_]] - } + import scala.reflect.runtime.universe.{Expr, runtimeMirror} + @throws(classOf[ObjectStreamException]) private def readResolve(): AnyRef = { - import ru._ - Expr(rootMirror, treec)(tag) + val loader: ClassLoader = try { + Thread.currentThread().getContextClassLoader() + } catch { + case se: SecurityException => null + } + val m = runtimeMirror(loader) + Expr(m, treec)(tag.in(m)) } } diff --git a/src/reflect/scala/reflect/api/TreeCreator.scala b/src/reflect/scala/reflect/api/TreeCreator.scala index 027c840955..000eaa1aa6 100644 --- a/src/reflect/scala/reflect/api/TreeCreator.scala +++ b/src/reflect/scala/reflect/api/TreeCreator.scala @@ -2,12 +2,12 @@ package scala package reflect package api -/** This is an internal implementation class. +/** A mirror-aware factory for trees. * * This class is used internally by Scala Reflection, and is not recommended for use in client code. * - * @group ReflectionAPI + * @group ReflectionAPI */ -abstract class TreeCreator { +abstract class TreeCreator extends Serializable { def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]): U # Tree } diff --git a/src/reflect/scala/reflect/api/TypeCreator.scala b/src/reflect/scala/reflect/api/TypeCreator.scala index 37fff90b43..cbd55b9428 100644 --- a/src/reflect/scala/reflect/api/TypeCreator.scala +++ b/src/reflect/scala/reflect/api/TypeCreator.scala @@ -8,6 +8,6 @@ package api * * @group ReflectionAPI */ -abstract class TypeCreator { +abstract class TypeCreator extends Serializable { def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]): U # Type } diff --git a/src/reflect/scala/reflect/api/TypeTags.scala b/src/reflect/scala/reflect/api/TypeTags.scala index 1dfc84be69..1d53453bde 100644 --- a/src/reflect/scala/reflect/api/TypeTags.scala +++ b/src/reflect/scala/reflect/api/TypeTags.scala @@ -9,6 +9,7 @@ package api import java.lang.{ Class => jClass } import scala.language.implicitConversions +import java.io.ObjectStreamException /** * A `TypeTag[T]` encapsulates the runtime type representation of some type `T`. @@ -233,6 +234,7 @@ trait TypeTags { self: Universe => val otherMirror1 = otherMirror.asInstanceOf[scala.reflect.api.Mirror[otherMirror.universe.type]] otherMirror.universe.WeakTypeTag[T](otherMirror1, tpec) } + @throws(classOf[ObjectStreamException]) private def writeReplace(): AnyRef = new SerializedTypeTag(tpec, concrete = false) } @@ -293,10 +295,13 @@ trait TypeTags { self: Universe => val otherMirror1 = otherMirror.asInstanceOf[scala.reflect.api.Mirror[otherMirror.universe.type]] otherMirror.universe.TypeTag[T](otherMirror1, tpec) } + @throws(classOf[ObjectStreamException]) private def writeReplace(): AnyRef = new SerializedTypeTag(tpec, concrete = true) } /* @group TypeTags */ + // This class only exists to silence MIMA complaining about a binary incompatibility. + // Only the top-level class (api.PredefTypeCreator) should be used. private class PredefTypeCreator[T](copyIn: Universe => Universe#TypeTag[T]) extends TypeCreator { def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]): U # Type = { copyIn(m.universe).asInstanceOf[U # TypeTag[T]].tpe @@ -304,8 +309,9 @@ trait TypeTags { self: Universe => } /* @group TypeTags */ - private class PredefTypeTag[T](_tpe: Type, copyIn: Universe => Universe#TypeTag[T]) extends TypeTagImpl[T](rootMirror, new PredefTypeCreator(copyIn)) { + private class PredefTypeTag[T](_tpe: Type, copyIn: Universe => Universe#TypeTag[T]) extends TypeTagImpl[T](rootMirror, new api.PredefTypeCreator(copyIn)) { override lazy val tpe: Type = _tpe + @throws(classOf[ObjectStreamException]) private def writeReplace(): AnyRef = new SerializedTypeTag(tpec, concrete = true) } @@ -341,20 +347,28 @@ trait TypeTags { self: Universe => def symbolOf[T: WeakTypeTag]: TypeSymbol } +// This class should be final, but we can't do that in Scala 2.11.x without breaking +// binary incompatibility. +// Since instances of this class are serialized, this class should have a +// SerialVersionUID annotation. private[scala] class SerializedTypeTag(var tpec: TypeCreator, var concrete: Boolean) extends Serializable { - private def writeObject(out: java.io.ObjectOutputStream): Unit = { - out.writeObject(tpec) - out.writeBoolean(concrete) - } - - private def readObject(in: java.io.ObjectInputStream): Unit = { - tpec = in.readObject().asInstanceOf[TypeCreator] - concrete = in.readBoolean() + import scala.reflect.runtime.universe.{TypeTag, WeakTypeTag, runtimeMirror} + @throws(classOf[ObjectStreamException]) + private def readResolve(): AnyRef = { + val loader: ClassLoader = try { + Thread.currentThread().getContextClassLoader() + } catch { + case se: SecurityException => null + } + val m = runtimeMirror(loader) + if (concrete) TypeTag(m, tpec) + else WeakTypeTag(m, tpec) } +} - private def readResolve(): AnyRef = { - import scala.reflect.runtime.universe._ - if (concrete) TypeTag(rootMirror, tpec) - else WeakTypeTag(rootMirror, tpec) +/* @group TypeTags */ +private class PredefTypeCreator[T](copyIn: Universe => Universe#TypeTag[T]) extends TypeCreator { + def apply[U <: Universe with Singleton](m: scala.reflect.api.Mirror[U]): U # Type = { + copyIn(m.universe).asInstanceOf[U # TypeTag[T]].tpe } } -- cgit v1.2.3