diff options
author | Paul Phillips <paulp@improving.org> | 2012-09-16 13:01:37 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-09-16 13:01:37 -0700 |
commit | 7281ec070dc538a58a74c1a1a23b74f1dfbce7a0 (patch) | |
tree | 4eab6a8806934728340a88f11c0c75ee89ca4067 | |
parent | 4d8d2e52361586f32a1a52b9a78728aca0d5bf0c (diff) | |
parent | 5933b9f00a02783793456f2c3963d94552c83b43 (diff) | |
download | scala-7281ec070dc538a58a74c1a1a23b74f1dfbce7a0.tar.gz scala-7281ec070dc538a58a74c1a1a23b74f1dfbce7a0.tar.bz2 scala-7281ec070dc538a58a74c1a1a23b74f1dfbce7a0.zip |
Merge pull request #1314 from paulp/pullreq-1306
Pullreq 1306
7 files changed, 111 insertions, 15 deletions
diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 045fb5b252..bb7a26a8c5 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -15,6 +15,7 @@ import scala.collection.generic.{ Sorted } import scala.reflect.{ ClassTag, classTag } import scala.util.control.ControlThrowable import scala.xml.{ Node, MetaData } +import java.lang.{ Class => jClass } import java.lang.Double.doubleToLongBits import java.lang.reflect.{ Modifier, Method => JMethod } @@ -28,10 +29,10 @@ object ScalaRunTime { def isArray(x: Any, atLevel: Int): Boolean = x != null && isArrayClass(x.getClass, atLevel) - private def isArrayClass(clazz: Class[_], atLevel: Int): Boolean = + private def isArrayClass(clazz: jClass[_], atLevel: Int): Boolean = clazz.isArray && (atLevel == 1 || isArrayClass(clazz.getComponentType, atLevel - 1)) - def isValueClass(clazz: Class[_]) = clazz.isPrimitive() + def isValueClass(clazz: jClass[_]) = clazz.isPrimitive() def isTuple(x: Any) = x != null && tupleNames(x.getClass.getName) def isAnyVal(x: Any) = x match { case _: Byte | _: Short | _: Char | _: Int | _: Long | _: Float | _: Double | _: Boolean | _: Unit => true @@ -50,7 +51,7 @@ object ScalaRunTime { /** Return the class object representing an array with element class `clazz`. */ - def arrayClass(clazz: Class[_]): Class[_] = { + def arrayClass(clazz: jClass[_]): jClass[_] = { // newInstance throws an exception if the erasure is Void.TYPE. see SI-5680 if (clazz == java.lang.Void.TYPE) classOf[Array[Unit]] else java.lang.reflect.Array.newInstance(clazz, 0).getClass @@ -58,18 +59,19 @@ object ScalaRunTime { /** Return the class object representing elements in arrays described by a given schematic. */ - def arrayElementClass(schematic: Any): Class[_] = schematic match { - case cls: Class[_] => cls.getComponentType + def arrayElementClass(schematic: Any): jClass[_] = schematic match { + case cls: jClass[_] => cls.getComponentType case tag: ClassTag[_] => tag.runtimeClass - case _ => throw new UnsupportedOperationException("unsupported schematic %s (%s)".format(schematic, if (schematic == null) "null" else schematic.getClass)) + case _ => + throw new UnsupportedOperationException(s"unsupported schematic $schematic (${schematic.getClass})") } /** Return the class object representing an unboxed value type, * e.g. classOf[int], not classOf[java.lang.Integer]. The compiler * rewrites expressions like 5.getClass to come here. */ - def anyValClass[T <: AnyVal : ClassTag](value: T): Class[T] = - classTag[T].runtimeClass.asInstanceOf[Class[T]] + def anyValClass[T <: AnyVal : ClassTag](value: T): jClass[T] = + classTag[T].runtimeClass.asInstanceOf[jClass[T]] /** Retrieve generic array element */ def array_apply(xs: AnyRef, idx: Int): Any = xs match { diff --git a/src/partest/scala/tools/partest/nest/RunnerManager.scala b/src/partest/scala/tools/partest/nest/RunnerManager.scala index 376e0e9bdb..4961424e1b 100644 --- a/src/partest/scala/tools/partest/nest/RunnerManager.scala +++ b/src/partest/scala/tools/partest/nest/RunnerManager.scala @@ -217,6 +217,7 @@ class RunnerManager(kind: String, val fileManager: FileManager, params: TestRunP "-Dpartest.output="+outDir.getAbsolutePath, "-Dpartest.lib="+LATEST_LIB, "-Dpartest.reflect="+LATEST_REFLECT, + "-Dpartest.comp="+LATEST_COMP, "-Dpartest.cwd="+outDir.getParent, "-Dpartest.test-path="+testFullPath, "-Dpartest.testname="+fileBase, diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala index 8853b872c0..3bd7f4f4fa 100644 --- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala +++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala @@ -65,6 +65,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => */ abstract class ClassfileAnnotArg extends Product implicit val JavaArgumentTag = ClassTag[ClassfileAnnotArg](classOf[ClassfileAnnotArg]) + case object UnmappableAnnotArg extends ClassfileAnnotArg /** Represents a compile-time Constant (`Boolean`, `Byte`, `Short`, * `Char`, `Int`, `Long`, `Float`, `Double`, `String`, `java.lang.Class` or @@ -173,11 +174,14 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => this } - override def toString = ( - atp + - (if (!args.isEmpty) args.mkString("(", ", ", ")") else "") + - (if (!assocs.isEmpty) (assocs map { case (x, y) => x+" = "+y } mkString ("(", ", ", ")")) else "") - ) + override def toString = completeAnnotationToString(this) + } + + private[scala] def completeAnnotationToString(annInfo: AnnotationInfo) = { + import annInfo._ + val s_args = if (!args.isEmpty) args.mkString("(", ", ", ")") else "" + val s_assocs = if (!assocs.isEmpty) (assocs map { case (x, y) => x+" = "+y } mkString ("(", ", ", ")")) else "" + s"${atp}${s_args}${s_assocs}" } /** Symbol annotations parsed in `Namer` (typeCompleter of @@ -215,7 +219,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable => * * `assocs` stores arguments to classfile annotations as name-value pairs. */ - sealed abstract class AnnotationInfo extends AnnotationApi { + abstract class AnnotationInfo extends AnnotationApi { def atp: Type def args: List[Tree] def assocs: List[(Name, ClassfileAnnotArg)] diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala index e56441b672..47978821a3 100644 --- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala +++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala @@ -9,6 +9,7 @@ import java.lang.reflect.{ Method => jMethod, Constructor => jConstructor, Modifier => jModifier, Field => jField, Member => jMember, Type => jType, TypeVariable => jTypeVariable, Array => jArray, GenericDeclaration, GenericArrayType, ParameterizedType, WildcardType, AnnotatedElement } +import java.lang.annotation.{Annotation => jAnnotation} import java.io.IOException import internal.MissingRequirementError import internal.pickling.ByteCodecs @@ -135,6 +136,53 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { thisUnive private def ErrorNotConstructor(sym: Symbol, owner: Symbol) = throw new ScalaReflectionException(s"expected a constructor of $owner, you provided $sym") private def ErrorFree(member: Symbol, freeType: Symbol) = throw new ScalaReflectionException(s"cannot reflect ${member.kindString} ${member.name}, because it's a member of a weak type ${freeType.name}") + /** Helper functions for extracting typed values from a (Class[_], Any) + * representing an annotation argument. + */ + private object toAnnotArg { + val StringClass = classOf[String] + val ClassClass = classOf[jClass[_]] + object PrimitiveClass { def unapply(x: jClass[_]) = x.isPrimitive } + object EnumClass { def unapply(x: jClass[_]) = x.isEnum } + object ArrayClass { def unapply(x: jClass[_]) = x.isArray } + object AnnotationClass { def unapply(x: jClass[_]) = x.isAnnotation } + + object ConstantArg { + def enumToSymbol(enum: Enum[_]): Symbol = + classToScala(enum.getClass).typeSignature.declaration(enum.name: TermName) + + def unapply(schemaAndValue: (jClass[_], Any)): Option[Any] = schemaAndValue match { + case (StringClass | PrimitiveClass(), value) => Some(value) + case (ClassClass, value: jClass[_]) => Some(classToScala(value).toType) + case (EnumClass(), value: Enum[_]) => Some(enumToSymbol(value)) + case _ => None + } + } + def apply(schemaAndValue: (jClass[_], Any)): ClassfileAnnotArg = schemaAndValue match { + case ConstantArg(value) => LiteralAnnotArg(Constant(value)) + case (clazz @ ArrayClass(), value: Array[_]) => ArrayAnnotArg(value map (x => apply(ScalaRunTime.arrayElementClass(clazz) -> x))) + case (AnnotationClass(), value: jAnnotation) => NestedAnnotArg(JavaAnnotationProxy(value)) + case _ => UnmappableAnnotArg + } + } + private case class JavaAnnotationProxy(jann: jAnnotation) extends AnnotationInfo { + override val atp: Type = classToScala(jann.annotationType).toType + override val args: List[Tree] = Nil + override def original: Tree = EmptyTree + override def setOriginal(t: Tree): this.type = throw new Exception("setOriginal inapplicable for " + this) + override def pos: Position = NoPosition + override def setPos(pos: Position): this.type = throw new Exception("setPos inapplicable for " + this) + override def toString = completeAnnotationToString(this) + + // todo. find out the exact order of assocs as they are written in the class file + // currently I'm simply sorting the methods to guarantee stability of the output + override lazy val assocs: List[(Name, ClassfileAnnotArg)] = ( + jann.annotationType.getDeclaredMethods.sortBy(_.getName).toList map (m => + (m.getName: TermName) -> toAnnotArg(m.getReturnType -> m.invoke(jann)) + ) + ) + } + def reflect[T: ClassTag](obj: T): InstanceMirror = new JavaInstanceMirror(obj) def reflectClass(cls: ClassSymbol): ClassMirror = { @@ -572,7 +620,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { thisUnive * Note: If `sym` is a method or constructor, its parameter annotations are copied as well. */ private def copyAnnotations(sym: Symbol, jann: AnnotatedElement) { - // to do: implement + sym setAnnotations (jann.getAnnotations map JavaAnnotationProxy).toList } /** @@ -611,6 +659,7 @@ trait JavaMirrors extends internal.SymbolTable with api.JavaUniverse { thisUnive } override def complete(sym: Symbol): Unit = { + if (jclazz.isEnum) throw new ScalaReflectionException("implementation restriction: Java enums are not supported") load(sym) completeRest() } diff --git a/test/files/run/reflection-java-annotations.check b/test/files/run/reflection-java-annotations.check new file mode 100644 index 0000000000..9954c59d4a --- /dev/null +++ b/test/files/run/reflection-java-annotations.check @@ -0,0 +1,20 @@ +Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala>
+
+scala> import scala.reflect.runtime.universe._
+import scala.reflect.runtime.universe._
+
+scala> val sym = typeOf[Foo].typeSymbol
+sym: reflect.runtime.universe.Symbol = class Foo
+
+scala> sym.typeSignature
+res0: reflect.runtime.universe.Type = java.lang.Object{def <init>(): Foo}
+
+scala> println(sym.getAnnotations)
+List(ComplexAnnotation(v1 = 1, v10 = "hello", v101 = [101, 101], v102 = [102, 102], v103 = ['g', 'g'], v104 = [104, 104], v105 = [105L, 105L], v106 = [106.0, 106.0], v107 = [107.0, 107.0], v108 = [false, true], v11 = classOf[Foo], v110 = ["hello", "world"], v111 = [classOf[SimpleAnnotation], classOf[ComplexAnnotation]], v113 = [SimpleAnnotation(v1 = 21, v10 = "world2", v11 = classOf[ComplexAnnotation], v2 = 22, v3 = '\027', v4 = 24, v5 = 25L, v6 = 26.0, v7 = 27.0, v8 = false)], v13 = SimpleAnnotation(v1 = 11, v10 = "world1", v11 = classOf[SimpleAnnotation], v2 = 12, v3 = '\r', v4 = 14, v5 = 15L, v6 = 16.0, v7 = 17.0, v8 = false), v2 = 2, v3 = '\03', v4 = 4, v5 = 5L, v6 = 6.0, v7 = 7.0, v8 = false))
+
+scala>
+
+scala>
diff --git a/test/files/run/reflection-java-annotations.jar.desired.sha1 b/test/files/run/reflection-java-annotations.jar.desired.sha1 new file mode 100644 index 0000000000..430e7626e6 --- /dev/null +++ b/test/files/run/reflection-java-annotations.jar.desired.sha1 @@ -0,0 +1 @@ +c35876a529c6be33bdda7b3f48ac8ae800d2f36a ?reflection-java-annotations.jar diff --git a/test/files/run/reflection-java-annotations.scala b/test/files/run/reflection-java-annotations.scala new file mode 100644 index 0000000000..96dbca86ad --- /dev/null +++ b/test/files/run/reflection-java-annotations.scala @@ -0,0 +1,19 @@ +import scala.tools.partest._ +import scala.tools.nsc.Settings + +object Test extends ReplTest { + def code = """ + import scala.reflect.runtime.universe._ + val sym = typeOf[Foo].typeSymbol + sym.typeSignature + println(sym.getAnnotations) + """ + + override def transformSettings(settings: Settings): Settings = { + val thisFile = testPath.jfile.getAbsolutePath + val javaCompiledAnnotationsJar = thisFile.substring(0, thisFile.length - ".scala".length) + ".jar" + val classpath = List(sys.props("partest.lib"), sys.props("partest.reflect"), sys.props("partest.comp"), javaCompiledAnnotationsJar) mkString sys.props("path.separator") + settings.processArguments(List("-cp", classpath), true) + settings + } +}
\ No newline at end of file |