From afc755916f7e67f1f2c899972de8801b1ef62543 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 26 Nov 2011 07:39:27 +0000 Subject: Annotations reacquainted with reification. Had AnnotationInfo extend Product3 since it's no longer a case class. Tried to make reflection a little more robust. Closes SI-5223, review by vogt. --- .../scala/reflect/internal/AnnotationInfos.scala | 11 +- .../scala/tools/nsc/transform/LiftCode.scala | 135 +++++++++++---------- src/library/scala/runtime/ScalaRunTime.scala | 9 +- test/files/pos/t5223.scala | 6 + 4 files changed, 90 insertions(+), 71 deletions(-) create mode 100644 test/files/pos/t5223.scala diff --git a/src/compiler/scala/reflect/internal/AnnotationInfos.scala b/src/compiler/scala/reflect/internal/AnnotationInfos.scala index f17243bc28..22c56642a7 100644 --- a/src/compiler/scala/reflect/internal/AnnotationInfos.scala +++ b/src/compiler/scala/reflect/internal/AnnotationInfos.scala @@ -47,7 +47,7 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => * - arrays of constants * - or nested classfile annotations */ - abstract class ClassfileAnnotArg + abstract class ClassfileAnnotArg extends Product /** Represents a compile-time Constant (`Boolean`, `Byte`, `Short`, * `Char`, `Int`, `Long`, `Float`, `Double`, `String`, `java.lang.Class` or @@ -154,11 +154,18 @@ trait AnnotationInfos extends api.AnnotationInfos { self: SymbolTable => * * `assocs` stores arguments to classfile annotations as name-value pairs. */ - sealed abstract class AnnotationInfo { + sealed abstract class AnnotationInfo extends Product3[Type, List[Tree], List[(Name, ClassfileAnnotArg)]] { def atp: Type def args: List[Tree] def assocs: List[(Name, ClassfileAnnotArg)] + /** Hand rolling Product. */ + def _1 = atp + def _2 = args + def _3 = assocs + def canEqual(other: Any) = other.isInstanceOf[AnnotationInfo] + override def productPrefix = "AnnotationInfo" + // see annotationArgRewriter lazy val isTrivial = atp.isTrivial && !hasArgWhich(_.isInstanceOf[This]) diff --git a/src/compiler/scala/tools/nsc/transform/LiftCode.scala b/src/compiler/scala/tools/nsc/transform/LiftCode.scala index fd279ee494..f55fe4f75e 100644 --- a/src/compiler/scala/tools/nsc/transform/LiftCode.scala +++ b/src/compiler/scala/tools/nsc/transform/LiftCode.scala @@ -11,6 +11,7 @@ import Flags._ import scala.collection.{ mutable, immutable } import scala.collection.mutable.ListBuffer import scala.tools.nsc.util.FreshNameCreator +import scala.runtime.ScalaRunTime.{ isAnyVal, isTuple } /** * Translate expressions of the form reflect.Code.lift(exp) @@ -34,6 +35,23 @@ abstract class LiftCode extends Transform with TypingTransformers { def newTransformer(unit: CompilationUnit): Transformer = new Codifier(unit) + private lazy val MirrorMemberNames = + ReflectRuntimeMirror.info.nonPrivateMembers filter (_.isTerm) map (_.toString) toSet + + // Would be nice if we could use something like this to check the names, + // but it seems that info is unavailable when I need it. + private def mirrorFactoryName(value: Any): Option[String] = value match { + // Modest (inadequate) sanity check that there's a member by this name. + case x: Product if MirrorMemberNames(x.productPrefix) => + Some(x.productPrefix) + case _ => + Some(value.getClass.getName split """[$.]""" last) filter MirrorMemberNames + } + private def isMirrorMemberObject(value: Product) = value match { + case NoType | NoPrefix | NoPosition | EmptyTree => true + case _ => false + } + class Codifier(unit: CompilationUnit) extends TypingTransformer(unit) { val reifyDebug = settings.Yreifydebug.value @@ -231,10 +249,7 @@ abstract class LiftCode extends Transform with TypingTransformers { private def registerReifiableSymbol(sym: Symbol): Unit = if (!(symIndex contains sym)) { - sym.owner.ownersIterator.find(!isLocatable(_)) match { - case Some(outer) => registerReifiableSymbol(outer) - case None => - } + sym.owner.ownersIterator find (x => !isLocatable(x)) foreach registerReifiableSymbol symIndex(sym) = reifiableSyms.length reifiableSyms += sym } @@ -261,16 +276,8 @@ abstract class LiftCode extends Transform with TypingTransformers { private def mkList(args: List[Tree]): Tree = scalaFactoryCall("collection.immutable.List", args: _*) - /** - * Reify a case object defined in Mirror - */ - private def reifyCaseObject(value: Product) = mirrorSelect(value.productPrefix) - - /** - * Reify an instance of a case class defined in Mirror - */ - private def reifyCaseClassInstance(value: Product) = - mirrorFactoryCall(value, (value.productIterator map reify).toList: _*) + private def reifyModifiers(m: Modifiers) = + mirrorCall("modifiersFromInternalFlags", reify(m.flags), reify(m.privateWithin), reify(m.annotations)) private def reifyAggregate(name: String, args: Any*) = scalaFactoryCall(name, (args map reify).toList: _*) @@ -379,15 +386,12 @@ abstract class LiftCode extends Transform with TypingTransformers { if (tsym.isClass && tpe == tsym.typeConstructor && tsym.isStatic) Select(reifySymRef(tpe.typeSymbol), "asTypeConstructor") else tpe match { - case NoType | NoPrefix => - reifyCaseObject(tpe.asInstanceOf[Product]) - case tpe @ ThisType(clazz) => - if (clazz.isModuleClass && clazz.isStatic) mirrorCall("thisModuleType", reify(clazz.fullName)) - else reifyCaseClassInstance(tpe) - case SuperType(_, _) | SingleType(_, _) | ConstantType(_) | - TypeRef(_, _, _) | AnnotatedType(_, _, _) | - TypeBounds(_, _) | NullaryMethodType(_) | OverloadedType(_, _) => - reifyCaseClassInstance(tpe.asInstanceOf[Product]) + case t @ NoType => + reifyMirrorObject(t) + case t @ NoPrefix => + reifyMirrorObject(t) + case tpe @ ThisType(clazz) if clazz.isModuleClass && clazz.isStatic => + mirrorCall("thisModuleType", reify(clazz.fullName)) case t @ RefinedType(parents, decls) => registerReifiableSymbol(tpe.typeSymbol) mirrorFactoryCall(t, reify(parents), reify(decls), reify(t.typeSymbol)) @@ -401,14 +405,14 @@ abstract class LiftCode extends Transform with TypingTransformers { case t @ MethodType(params, restpe) => reifyTypeBinder(t, params, restpe) case _ => - cannotReify(tpe) + reifyProductUnsafe(tpe) } } /** Reify a tree */ private def reifyTree(tree: Tree): Tree = tree match { case EmptyTree => - reifyCaseObject(tree) + reifyMirrorObject(EmptyTree) case This(_) if !(boundSyms contains tree.symbol) => reifyFree(tree) case Ident(_) if !(boundSyms contains tree.symbol) => @@ -416,8 +420,10 @@ abstract class LiftCode extends Transform with TypingTransformers { case TypeTree() if (tree.tpe != null) => mirrorCall("TypeTree", reifyType(tree.tpe)) case _ => - if (tree.isDef) boundSyms += tree.symbol - reifyCaseClassInstance(tree.asInstanceOf[Product]) + if (tree.isDef) + boundSyms += tree.symbol + + reifyProduct(tree) /* if (tree.isDef || tree.isInstanceOf[Function]) registerReifiableSymbol(tree.symbol) @@ -434,45 +440,42 @@ abstract class LiftCode extends Transform with TypingTransformers { private def reifyFree(tree: Tree): Tree = mirrorCall("Ident", reifySymRef(tree.symbol)) + // todo: consider whether we should also reify positions + private def reifyPosition(pos: Position): Tree = + reifyMirrorObject(NoPosition) + + // !!! we must eliminate these casts. + private def reifyProductUnsafe(x: Any): Tree = + reifyProduct(x.asInstanceOf[Product]) + private def reifyProduct(x: Product): Tree = + mirrorCall(x.productPrefix, (x.productIterator map reify).toList: _*) + + /** + * Reify a case object defined in Mirror + */ + private def reifyMirrorObject(name: String): Tree = mirrorSelect(name) + private def reifyMirrorObject(x: Product): Tree = reifyMirrorObject(x.productPrefix) + + private def isReifiableConstant(value: Any) = value match { + case null => true // seems pretty reifable to me? + case _: String => true + case _ => isAnyVal(value) + } + /** Reify an arbitary value */ - private def reify(value: Any): Tree = { - value match { - case tree: Tree => - reifyTree(tree) - case sym: Symbol => - reifySymRef(sym) - case tpe: Type => - reifyType(tpe) - case xs: List[_] => - scalaFactoryCall("collection.immutable.List", xs map reify: _*) - case xs: Array[_] => - scalaFactoryCall("Array", xs map reify: _*) - case scope: Scope => - reifyScope(scope) - case x: Name => - reifyName(x) - case pos: Position => // todo: consider whether we should also reify positions - reifyCaseObject(NoPosition) - case Constant(_) | AnnotationInfo(_, _, _) => - reifyCaseClassInstance(value.asInstanceOf[Product]) - case Modifiers(flags, qual, annots) => - mirrorCall("modifiersFromInternalFlags", reify(flags), reify(qual), reify(annots)) - case arg: ClassfileAnnotArg => - reifyCaseClassInstance(arg.asInstanceOf[Product]) - case x: Product if x.getClass.getName startsWith "scala.Tuple" => - reifyCaseClassInstance(x) - case () => Literal(Constant(())) - case x: String => Literal(Constant(x)) - case x: Boolean => Literal(Constant(x)) - case x: Byte => Literal(Constant(x)) - case x: Short => Literal(Constant(x)) - case x: Char => Literal(Constant(x)) - case x: Int => Literal(Constant(x)) - case x: Long => Literal(Constant(x)) - case x: Float => Literal(Constant(x)) - case x: Double => Literal(Constant(x)) - case _ => cannotReify(value) - } + private def reify(value: Any): Tree = value match { + case tree: Tree => reifyTree(tree) + case sym: Symbol => reifySymRef(sym) + case tpe: Type => reifyType(tpe) + case xs: List[_] => reifyList(xs) + case xs: Array[_] => scalaFactoryCall("Array", xs map reify: _*) + case scope: Scope => reifyScope(scope) + case x: Name => reifyName(x) + case x: Position => reifyPosition(x) + case x: Modifiers => reifyModifiers(x) + case _ => + if (isReifiableConstant(value)) Literal(Constant(value)) + else reifyProductUnsafe(value) } /** @@ -516,6 +519,6 @@ abstract class LiftCode extends Transform with TypingTransformers { } private def cannotReify(value: Any): Nothing = - abort("don't know how to reify " + value + " of class " + value.getClass) + abort("don't know how to reify " + value + " of " + value.getClass) } } diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index aa4830369e..951bdd888e 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -31,6 +31,12 @@ object ScalaRunTime { clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) def isValueClass(clazz: Class[_]) = clazz.isPrimitive() + def isTuple(x: Any) = tupleNames(x.getClass.getName) + def isAnyVal(x: Any) = x match { + case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true + case _ => false + } + private val tupleNames = 1 to 22 map ("scala.Tuple" + _) toSet /** Return the class object representing an unboxed value type, * e.g. classOf[int], not classOf[java.lang.Integer]. The compiler @@ -273,9 +279,6 @@ object ScalaRunTime { def isScalaClass(x: AnyRef) = Option(x.getClass.getPackage) exists (_.getName startsWith "scala.") - def isTuple(x: AnyRef) = - x.getClass.getName matches """^scala\.Tuple(\d+).*""" - // When doing our own iteration is dangerous def useOwnToString(x: Any) = x match { // Node extends NodeSeq extends Seq[Node] and MetaData extends Iterable[MetaData] diff --git a/test/files/pos/t5223.scala b/test/files/pos/t5223.scala new file mode 100644 index 0000000000..51682e9254 --- /dev/null +++ b/test/files/pos/t5223.scala @@ -0,0 +1,6 @@ +import scala.reflect._ + +object Foo extends App { + Code.lift{def printf(format: String, args: Any*): String = null } + Code.lift{def printf(format: String, args: Any*): String = ("abc": @cloneable)} +} -- cgit v1.2.3