summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/AnnotationInfos.scala
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2014-01-28 19:37:59 +0300
committerEugene Burmako <xeno.by@gmail.com>2014-02-14 14:19:44 +0100
commit0268e03cb461b0c7e8ae2082894988395fc0994a (patch)
tree7dec4c177aad62a2209601a0f5327e243c3340b2 /src/reflect/scala/reflect/internal/AnnotationInfos.scala
parentc7fd03900b7023967f22932f1f32b98d57983e9b (diff)
downloadscala-0268e03cb461b0c7e8ae2082894988395fc0994a.tar.gz
scala-0268e03cb461b0c7e8ae2082894988395fc0994a.tar.bz2
scala-0268e03cb461b0c7e8ae2082894988395fc0994a.zip
SI-8118 simplifies Annotation down to a plain Tree
As per https://groups.google.com/forum/#!topic/scala-internals/8v2UL-LR9yY, annotations don’t have to be represented as AnnotationInfos and can be reduced to plain Trees. Due to compatibility reasons and because of the limitations of the cake pattern used in implementing current version of Reflection, we can’t just say `type Annotation = Tree`, however what we can definitely do is to deprecate all the methods on Annotation and expose `tree: Tree` instead.
Diffstat (limited to 'src/reflect/scala/reflect/internal/AnnotationInfos.scala')
-rw-r--r--src/reflect/scala/reflect/internal/AnnotationInfos.scala61
1 files changed, 60 insertions, 1 deletions
diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
index f42e0c44c9..19e9eef851 100644
--- a/src/reflect/scala/reflect/internal/AnnotationInfos.scala
+++ b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
@@ -10,10 +10,12 @@ package internal
import pickling.ByteCodecs
import scala.annotation.tailrec
import scala.collection.immutable.ListMap
+import scala.language.postfixOps
/** AnnotationInfo and its helpers */
trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
- import definitions.{ ThrowsClass, ThrowableClass, StaticAnnotationClass, isMetaAnnotation }
+ import definitions._
+ import treeInfo._
// Common annotation code between Symbol and Type.
// For methods altering the annotation list, on Symbol it mutates
@@ -344,6 +346,63 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
}
implicit val AnnotationTag = ClassTag[AnnotationInfo](classOf[AnnotationInfo])
+ protected[scala] def annotationToTree(ann: Annotation): Tree = {
+ def reverseEngineerArgs(): List[Tree] = {
+ def reverseEngineerArg(jarg: ClassfileAnnotArg): Tree = jarg match {
+ case LiteralAnnotArg(const) =>
+ val tpe = if (const.tag == UnitTag) UnitTpe else ConstantType(const)
+ Literal(const) setType tpe
+ case ArrayAnnotArg(jargs) =>
+ val args = jargs map reverseEngineerArg
+ // TODO: I think it would be a good idea to typecheck Java annotations using a more traditional algorithm
+ // sure, we can't typecheck them as is using the `new jann(foo = bar)` syntax (because jann is going to be an @interface)
+ // however we can do better than `typedAnnotation` by desugaring the aforementioned expression to
+ // something like `new jann() { override def annotatedType() = ...; override def foo = bar }`
+ // and then using the results of that typecheck to produce a Java-compatible classfile entry
+ // in that case we're going to have correctly typed Array.apply calls, however that's 2.12 territory
+ // and for 2.11 exposing an untyped call to ArrayModule should suffice
+ Apply(Ident(ArrayModule), args.toList)
+ case NestedAnnotArg(ann: Annotation) =>
+ annotationToTree(ann)
+ case _ =>
+ EmptyTree
+ }
+ def reverseEngineerArgs(jargs: List[(Name, ClassfileAnnotArg)]): List[Tree] = jargs match {
+ case (name, jarg) :: rest => AssignOrNamedArg(Ident(name), reverseEngineerArg(jarg)) :: reverseEngineerArgs(rest)
+ case Nil => Nil
+ }
+ if (ann.javaArgs.isEmpty) ann.scalaArgs
+ else reverseEngineerArgs(ann.javaArgs.toList)
+ }
+
+ // TODO: at the moment, constructor selection is unattributed, because AnnotationInfos lack necessary information
+ // later on, in 2.12, for every annotation we could save an entire tree instead of just bits and pieces
+ // but for 2.11 the current situation will have to do
+ val ctorSelection = Select(New(TypeTree(ann.atp)), nme.CONSTRUCTOR)
+ Apply(ctorSelection, reverseEngineerArgs()) setType ann.atp
+ }
+
+ protected[scala] def treeToAnnotation(tree: Tree): Annotation = tree match {
+ case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
+ def encodeJavaArg(arg: Tree): ClassfileAnnotArg = arg match {
+ case Literal(const) => LiteralAnnotArg(const)
+ case Apply(ArrayModule, args) => ArrayAnnotArg(args map encodeJavaArg toArray)
+ case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => NestedAnnotArg(treeToAnnotation(arg))
+ case _ => throw new Exception("unexpected java argument shape $arg: literals, arrays and nested annotations are supported")
+ }
+ def encodeJavaArgs(args: List[Tree]): List[(Name, ClassfileAnnotArg)] = args match {
+ case AssignOrNamedArg(Ident(name), arg) :: rest => (name, encodeJavaArg(arg)) :: encodeJavaArgs(rest)
+ case arg :: rest => throw new Exception("unexpected java argument shape $arg: only AssignOrNamedArg trees are supported")
+ case Nil => Nil
+ }
+ val atp = tpt.tpe
+ if (atp != null && (atp.typeSymbol isNonBottomSubClass StaticAnnotationClass)) AnnotationInfo(atp, args, Nil)
+ else if (atp != null && (atp.typeSymbol isNonBottomSubClass ClassfileAnnotationClass)) AnnotationInfo(atp, Nil, encodeJavaArgs(args))
+ else throw new Exception(s"unexpected annotation type $atp: only subclasses of StaticAnnotation and ClassfileAnnotation are supported")
+ case _ =>
+ throw new Exception("""unexpected tree shape: only q"new $annType(..$args)" is supported""")
+ }
+
object UnmappableAnnotation extends CompleteAnnotationInfo(NoType, Nil, Nil)
object ErroneousAnnotation extends CompleteAnnotationInfo(ErrorType, Nil, Nil)