summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/AnnotationInfos.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/reflect/scala/reflect/internal/AnnotationInfos.scala')
-rw-r--r--src/reflect/scala/reflect/internal/AnnotationInfos.scala63
1 files changed, 61 insertions, 2 deletions
diff --git a/src/reflect/scala/reflect/internal/AnnotationInfos.scala b/src/reflect/scala/reflect/internal/AnnotationInfos.scala
index d634034fe9..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
@@ -75,7 +77,7 @@ trait AnnotationInfos extends api.Annotations { self: SymbolTable =>
* - arrays of constants
* - or nested classfile annotations
*/
- sealed abstract class ClassfileAnnotArg extends Product
+ sealed abstract class ClassfileAnnotArg extends Product with JavaArgumentApi
implicit val JavaArgumentTag = ClassTag[ClassfileAnnotArg](classOf[ClassfileAnnotArg])
case object UnmappableAnnotArg extends ClassfileAnnotArg
@@ -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)