diff options
author | Lex Spoon <lex@lexspoon.org> | 2007-05-21 15:49:37 +0000 |
---|---|---|
committer | Lex Spoon <lex@lexspoon.org> | 2007-05-21 15:49:37 +0000 |
commit | fe55fa336be15afe509fbc7096e8cf51026cbfd9 (patch) | |
tree | 6d47073ccecfaacf588e017e22551c92a3d055d0 /src | |
parent | 22a8618b48e945323723755525bbdeb97c63da3e (diff) | |
download | scala-fe55fa336be15afe509fbc7096e8cf51026cbfd9.tar.gz scala-fe55fa336be15afe509fbc7096e8cf51026cbfd9.tar.bz2 scala-fe55fa336be15afe509fbc7096e8cf51026cbfd9.zip |
Annotations clean up, including:
- AnnotationInfo has no type parameter. It always has a parse tree, and
sometimes has a constant. - SymbolReifier is separated from LiftCode for
independent use - reification into reflect.* structures handles more
cases - unreify is added, for enough cases that annotations work
Diffstat (limited to 'src')
17 files changed, 454 insertions, 165 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala index f114426d3e..31cc368ec8 100644 --- a/src/compiler/scala/tools/nsc/ast/NodePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/NodePrinters.scala @@ -41,7 +41,7 @@ abstract class NodePrinters { if (comma) buf.append(",") buf.append(EOL) } - def annotationInfoToString(attr: AnnotationInfo[Constant]) = { + def annotationInfoToString(attr: AnnotationInfo) = { val str = new StringBuilder str.append(attr.atp.toString()) if (!attr.args.isEmpty) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala index cd7096283f..9ee4404cc9 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -151,7 +151,7 @@ abstract class GenJVM extends SubComponent { case AnnotationInfo(CloneableAttr, _, _) => parents = parents ::: List(CloneableClass.tpe) case AnnotationInfo(SerialVersionUID, value :: _, _) => - serialVUID = Some(value.longValue) + serialVUID = Some(value.constant.get.longValue) case AnnotationInfo(RemoteAttr, _, _) => parents = parents ::: List(RemoteInterface.tpe) remoteClass = true @@ -215,7 +215,9 @@ abstract class GenJVM extends SubComponent { buf.putShort(0xbaba.toShort) for (val AnnotationInfo(ThrowsAttr, List(exc), _) <- excs.removeDuplicates) { - buf.putShort(cpool.addClass(javaName(exc.typeValue.symbol)).shortValue) + buf.putShort( + cpool.addClass( + javaName(exc.constant.get.typeValue.symbol)).shortValue) nattr = nattr + 1 } @@ -224,7 +226,7 @@ abstract class GenJVM extends SubComponent { addAttribute(jmethod, nme.ExceptionsATTR, buf) } - private def emitAttributes(buf: ByteBuffer, attributes: List[AnnotationInfo[Constant]]): Int = { + private def emitAttributes(buf: ByteBuffer, attributes: List[AnnotationInfo]): Int = { val cpool = jclass.getConstantPool() def emitElement(const: Constant): Unit = const.tag match { @@ -272,11 +274,12 @@ abstract class GenJVM extends SubComponent { var nattr = 0 val pos = buf.position() - // put some radom value; the actual number of annotations is determined at the end + // put some random value; the actual number of annotations is determined at the end buf.putShort(0xbaba.toShort) - for (val AnnotationInfo(typ, consts, nvPairs) <- attributes; - typ.symbol isNonBottomSubClass definitions.ClassfileAnnotationClass) { + for (attrib@AnnotationInfo(typ, consts, nvPairs) <- attributes; + if attrib.isConstant; + if typ.symbol isNonBottomSubClass definitions.ClassfileAnnotationClass) { nattr = nattr + 1 val jtype = javaType(typ) buf.putShort(cpool.addUtf8(jtype.getSignature()).toShort) @@ -284,11 +287,11 @@ abstract class GenJVM extends SubComponent { buf.putShort((consts.length + nvPairs.length).toShort) if (!consts.isEmpty) { buf.putShort(cpool.addUtf8("value").toShort) - emitElement(consts.head) + emitElement(consts.head.constant.get) } for (val (name, value) <- nvPairs) { buf.putShort(cpool.addUtf8(name.toString).toShort) - emitElement(value) + emitElement(value.constant.get) } } @@ -297,20 +300,23 @@ abstract class GenJVM extends SubComponent { nattr } - def addAnnotations(jmember: JMember, attributes: List[AnnotationInfo[Constant]]): Unit = { - if (attributes.isEmpty) return + def addAnnotations(jmember: JMember, attributes: List[AnnotationInfo]): Unit = { + val toEmit = attributes.filter(.isConstant) + + if (toEmit.isEmpty) return val buf: ByteBuffer = ByteBuffer.allocate(2048) - emitAttributes(buf, attributes) + emitAttributes(buf, toEmit) addAttribute(jmember, nme.RuntimeAnnotationATTR, buf) } - def addParamAnnotations(pattrss: List[List[AnnotationInfo[Constant]]]): Unit = { - val attributes = for (val attrs <- pattrss) yield - for (val attr @ AnnotationInfo(tpe, _, _) <- attrs; - tpe.symbol isNonBottomSubClass definitions.ClassfileAnnotationClass) yield attr; + def addParamAnnotations(pattrss: List[List[AnnotationInfo]]): Unit = { + val attributes = for (attrs <- pattrss) yield + for (attr @ AnnotationInfo(tpe, _, _) <- attrs; + if attr.isConstant; + if tpe.symbol isNonBottomSubClass definitions.ClassfileAnnotationClass) yield attr; if (attributes.forall(.isEmpty)) return; val buf: ByteBuffer = ByteBuffer.allocate(2048) @@ -411,8 +417,8 @@ abstract class GenJVM extends SubComponent { (m.symbol.attributes contains AnnotationInfo(RemoteAttr, Nil, Nil))) && jmethod.isPublic() && !forCLDC) { - m.symbol.attributes = - AnnotationInfo(ThrowsAttr, List(Constant(RemoteException)), List()) :: m.symbol.attributes; + val ainfo = AnnotationInfo(ThrowsAttr, List(new AnnotationArgument(Constant(RemoteException))), List()) + m.symbol.attributes = ainfo :: m.symbol.attributes; } if (!jmethod.isAbstract()) { diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index 6c6835bb6a..4eb8f987da 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -293,12 +293,15 @@ abstract class GenMSIL extends SubComponent { log("Could not find pickle information for " + sym) } - def addAttributes(member: ICustomAttributeSetter, attributes: List[AnnotationInfo[Constant]]): Unit = { + def addAttributes(member: ICustomAttributeSetter, attributes: List[AnnotationInfo]): Unit = { return // FIXME if (settings.debug.value) log("creating attributes: " + attributes + " for member : " + member) - for (AnnotationInfo(typ, consts, nvPairs) <- attributes /* !typ.symbol.hasFlag(Flags.JAVA) */ ) { + for (attr@ AnnotationInfo(typ, annArgs, nvPairs) <- attributes ; + if attr.isConstant) + /* !typ.symbol.hasFlag(Flags.JAVA) */ + { // assert(consts.length <= 1, // "too many constant arguments for attribute; "+consts.toString()) @@ -317,7 +320,10 @@ abstract class GenMSIL extends SubComponent { // otehr way than GetConstructors()(0) to get the constructor, if there's // no constructor symbol available. - val args: Array[Byte] = getAttributeArgs(consts, nvPairs) + val args: Array[Byte] = + getAttributeArgs( + annArgs map (.constant.get), + (for((n,v) <- nvPairs) yield (n, v.constant.get))) member.SetCustomAttribute(constr, args) } } diff --git a/src/compiler/scala/tools/nsc/doc/ModelToXML.scala b/src/compiler/scala/tools/nsc/doc/ModelToXML.scala index 234b640546..31c59cd18a 100644 --- a/src/compiler/scala/tools/nsc/doc/ModelToXML.scala +++ b/src/compiler/scala/tools/nsc/doc/ModelToXML.scala @@ -245,12 +245,12 @@ trait ModelToXML extends ModelExtractor { </tr> def attrsFor(entity: Entity)(implicit from: Frame): NodeSeq = { - def attrFor(attr: AnnotationInfo[Constant]): Node = { + def attrFor(attr: AnnotationInfo): Node = { val buf = new StringBuilder val AnnotationInfo(tpe, args, nvPairs) = attr val name = link(decode(tpe.symbol)) if (!args.isEmpty) - buf.append(args.map(.escapedStringValue).mkString("(", ",", ")")) + buf.append(args.mkString("(", ",", ")")) if (!nvPairs.isEmpty) for (((name, value), index) <- nvPairs.zipWithIndex) { if (index > 0) diff --git a/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala new file mode 100644 index 0000000000..97667a5dc3 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/AnnotationInfos.scala @@ -0,0 +1,149 @@ +package scala.tools.nsc.symtab +import scala.tools.nsc.transform.SymbolReifier +import util._ + +/** AnnotationInfo and its helpers */ +trait AnnotationInfos { + self : SymbolTable => + + + /** Convert a reflect tree to a Constant, if possible */ + private def refltree2cons(tree: reflect.Tree): Option[Constant] = + tree match { + case reflect.Literal(v) => Some(Constant(v)) + + case reflect.Apply( + reflect.TypeApply( + reflect.Select(_, + reflect.Method( + "scala.Array.apply", + reflect.PolyType(_, _, + reflect.MethodType(_, reflect.AppliedType(arrayType,_))))), + List(elemType)), + members) => + + refltrees2consArray( + members, + reflect.AppliedType(arrayType, List(elemType))) + + + case reflect.Apply( + reflect.Select(_, + reflect.Method( + "scala.Array.apply", + reflect.MethodType(_, arrayType))), + members) => + + refltrees2consArray(members, arrayType) + + case tree => + //println("could not convert: " + tree); + None + } + + private object symbolReifier extends SymbolReifier { + val symbols: AnnotationInfos.this.type = AnnotationInfos.this + } + + /** Convert a sequence of trees to an array type, + * if all of the array elements are constants. + * Use arrayType as type of the resulting constant. + */ + private def refltrees2consArray( + trees: Seq[reflect.Tree], + arrayType: reflect.Type) + : Option[Constant] = + { + // println("arrayType is " + arrayType + " (" + + // symbolReifier.unreify(arrayType) + ")") + + val mems = trees.map(refltree2cons) + + if(mems.exists(.isEmpty)) + None + else + Some(new ArrayConstant( + mems.map(.get).toArray, + symbolReifier.unreify(arrayType))) + } + + + /** Convert a constant to an equivalent reflect tree. */ + private def cons2refltree(cons: Constant): reflect.Tree = { + import reflect._ + + (cons: @unchecked) match { + case acons:ArrayConstant => + val elems = acons.arrayValue.toList + val arrayType = symbolReifier.reify(cons.tpe) + val elemType: reflect.Type = arrayType match { + case AppliedType(_, List(et)) => et + case _ => + assert(false, "array type is not an array type"); + reflect.NoType + } + val elemTrees = elems map cons2refltree + + val arrayObject = reflect.This(reflect.Class("scala.Array")) + + // The following two gigantic trees were found by printing + // out what the reifier makes. If the reifier changes, they + // should be updated. + if(symbolReifier.unreify(elemType) <:< definitions.AnyValClass.tpe) + Apply(Select(Select(Ident(Field("scala",PrefixedType(reflect.ThisType(RootSymbol),Class("scala")))),Field("scala.Array",PrefixedType(reflect.ThisType(Class("scala")),Class("scala.Array")))),Method("scala.Array.apply",reflect.MethodType(List(AppliedType(PrefixedType(reflect.ThisType(Class("scala")),Class("scala.<repeated>")),List(PrefixedType(reflect.ThisType(Class("scala")),Class("scala.Int"))))),AppliedType(PrefixedType(reflect.ThisType(Class("scala")),Class("scala.Array")), + List(elemType))))), elemTrees) + + + else + Apply(TypeApply(Select(Select(Ident(Field("scala",PrefixedType(reflect.ThisType(RootSymbol),Class("scala")))),Field("scala.Array",PrefixedType(reflect.ThisType(Class("scala")),Class("scala.Array")))),Method("scala.Array.apply",reflect.PolyType(List(reflect.NoSymbol),List((PrefixedType(reflect.ThisType(Class("scala")),Class("scala.Nothing")),PrefixedType(reflect.ThisType(Class("scala")),TypeField("scala.AnyRef",PrefixedType(reflect.ThisType(Class("java.lang")),Class("java.lang.Object")))))),reflect.MethodType(List(AppliedType(PrefixedType(reflect.ThisType(Class("scala")),Class("scala.<repeated>")),List(PrefixedType(reflect.NoType,reflect.NoSymbol)))),AppliedType(PrefixedType(reflect.ThisType(Class("scala")),Class("scala.Array")),List(PrefixedType(reflect.NoType,reflect.NoSymbol))))))), + List(elemType)), elemTrees) + + case Constant(value) => reflect.Literal(value) + } + } + + + + + + /** An argument to an annotation. It includes a parse tree, + * and it includes a compile-time constant for the tree if possible. + */ + class AnnotationArgument(val tree: reflect.Tree) + { + def this(cons: Constant) = this(cons2refltree(cons)) + +//println("tree is: " + tree) + + val constant: Option[Constant] = refltree2cons(tree) + + def isConstant = !constant.isEmpty + + override def toString: String = + constant match { + case Some(cons) => cons.escapedStringValue + case None => tree.toString + } + } + + /** Typed information about an annotation. It can be attached to + * either a symbol or an annotated type. + */ + case class AnnotationInfo( + atp: Type, + args: List[AnnotationArgument], + assocs: List[(Name, AnnotationArgument)]) + { + override def toString: String = + atp + + (if (args.isEmpty) "" + else args.mkString("(", ", ", ")")) + + (if (assocs.isEmpty) "" + else (assocs map { case (x, y) => x+" = "+y } mkString ("{", ", ", "}"))) + + /** Check whether all arguments and assocations are constants */ + def isConstant = + ((args forall (.isConstant)) && + (assocs map (._2) forall (.isConstant))) + } +} diff --git a/src/compiler/scala/tools/nsc/symtab/Constants.scala b/src/compiler/scala/tools/nsc/symtab/Constants.scala index e622d85d47..6926edf826 100644 --- a/src/compiler/scala/tools/nsc/symtab/Constants.scala +++ b/src/compiler/scala/tools/nsc/symtab/Constants.scala @@ -232,5 +232,4 @@ trait Constants { extends Constant(arrayValue) { override def toString() = arrayValue.mkString("Constant(", "," , ")") } - } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala index c68d2dfee3..c066492fe1 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala @@ -15,6 +15,7 @@ abstract class SymbolTable extends Names with Constants with InfoTransformers with StdNames + with AnnotationInfos { def settings: Settings def rootLoader: LazyType diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index c23477e3d0..c1d2d222ea 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -80,7 +80,7 @@ trait Symbols { else -1 } - var attributes: List[AnnotationInfo[Constant]] = List() + var attributes: List[AnnotationInfo] = List() var privateWithin: Symbol = _ @@ -554,7 +554,7 @@ trait Symbols { def typeParams: List[Symbol] = if (isMonomorphicType) List() else { rawInfo.load(this); rawInfo.typeParams } - def getAttributes(clazz: Symbol): List[AnnotationInfo[Constant]] = + def getAttributes(clazz: Symbol): List[AnnotationInfo] = attributes.filter(.atp.symbol.isNonBottomSubClass(clazz)) /** Reset symbol to initial state @@ -1347,14 +1347,7 @@ trait Symbols { def cloneSymbolImpl(owner: Symbol): Symbol = throw new Error() } - case class AnnotationInfo[+T](atp: Type, args: List[T], assocs: List[(Name, T)]) { - override def toString: String = - atp + - (if (args.isEmpty) "" - else args.mkString("(", ", ", ")")) + - (if (assocs.isEmpty) "" - else (assocs map { case (x, y) => x+" = "+y } mkString ("{", ", ", "}"))) - } + def cloneSymbols(syms: List[Symbol]): List[Symbol] = { val syms1 = syms map (.cloneSymbol) diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index 07fb96923b..9cfec0d6e6 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -129,7 +129,7 @@ trait Types { override def isComplete = tp.isComplete override def complete(sym: Symbol) = tp.complete(sym) override def load(sym: Symbol): unit = tp.load(sym) - override def withAttributes(attribs: List[AnnotationInfo[Any]]) = tp.withAttributes(attribs) + override def withAttributes(attribs: List[AnnotationInfo]) = tp.withAttributes(attribs) override def withoutAttributes = tp.withoutAttributes } @@ -603,10 +603,10 @@ trait Types { } /** Add an attribute to this type */ - def withAttribute(attrib: AnnotationInfo[Any]) = withAttributes(List(attrib)) + def withAttribute(attrib: AnnotationInfo) = withAttributes(List(attrib)) /** Add a number of attributes to this type */ - def withAttributes(attribs: List[AnnotationInfo[Any]]): Type = + def withAttributes(attribs: List[AnnotationInfo]): Type = attribs match { case Nil => this case _ => AnnotatedType(attribs, this) @@ -1445,7 +1445,7 @@ A type's symbol should never be inspected directly. * to the core compiler, but can be observed by type-system plugins. The * core compiler does take care to propagate attributes and to save them * in the symbol tables of object files. */ - case class AnnotatedType(attributes: List[AnnotationInfo[Any]], tp: Type) extends TypeProxy { + case class AnnotatedType(attributes: List[AnnotationInfo], tp: Type) extends TypeProxy { override def toString(): String = { val attString = if (attributes.isEmpty) @@ -1458,7 +1458,7 @@ A type's symbol should never be inspected directly. /** Add a number of attributes to this type */ - override def withAttributes(attribs: List[AnnotationInfo[Any]]): Type = + override def withAttributes(attribs: List[AnnotationInfo]): Type = AnnotatedType(attribs:::this.attributes, this) /** Remove any attributes from this type */ diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 92b54344bf..d991a2e601 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -704,10 +704,10 @@ abstract class ClassfileParser { val attrNameIndex = in.nextChar val attrType = pool.getType(attrNameIndex) val nargs = in.nextChar - val nvpairs = new ListBuffer[(Name,Constant)] + val nvpairs = new ListBuffer[(Name,AnnotationArgument)] for (i <- 0 until nargs) { val name = pool.getName(in.nextChar) - nvpairs += (name, parseTaggedConstant) + nvpairs += ((name, new AnnotationArgument(parseTaggedConstant))) } sym.attributes = AnnotationInfo(attrType, List(), nvpairs.toList) :: sym.attributes } diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala b/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala index f890eebaa2..859a42804f 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/PickleBuffer.scala @@ -83,6 +83,9 @@ class PickleBuffer(data: Array[byte], from: int, to: int) { // -- Basic input routines -------------------------------------------- + /** Peek at the current byte without moving the read index */ + def peekByte(): int = bytes(readIndex) + /** Read a byte */ def readByte(): int = { val x = bytes(readIndex); readIndex += 1; x diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala index 3aca840982..c499679961 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala @@ -4,7 +4,6 @@ */ // $Id$ - package scala.tools.nsc.symtab.classfile import java.lang.{Float, Double} @@ -273,14 +272,14 @@ abstract class Pickler extends SubComponent { children foreach putSymbol } - private def putAnnotation(sym: Symbol, attr: AnnotationInfo[Constant]): unit = { + private def putAnnotation(sym: Symbol, attr: AnnotationInfo): unit = { assert(putEntry((sym, attr))) putType(attr.atp) - for (c <- attr.args) putConstant(c) - for ((name, c) <- attr.assocs) { putEntry(name); putConstant(c) } + for (c <- attr.args) putTreeOrConstant(c) + for ((name, c) <- attr.assocs) { putEntry(name); putTreeOrConstant(c) } } - private def putAnnotation(annot: AnnotationInfo[Any]): unit = + private def putAnnotation(annot: AnnotationInfo): unit = if(putEntry(annot)) { val AnnotationInfo(tpe, args, assocs) = annot putType(tpe) @@ -288,7 +287,7 @@ abstract class Pickler extends SubComponent { for ((name, rhs) <- assocs) { putEntry(name); putTreeOrConstant(rhs) } } - private def putTreeOrConstant(x: Any) { + private def putTreeOrConstant(x: AnyRef) { x match { case c:Constant => putConstant(c) case tree:reflect.Tree => putTree(tree) @@ -297,7 +296,7 @@ abstract class Pickler extends SubComponent { } } - private def putAnnotations(annots: List[AnnotationInfo[Any]]): unit = + private def putAnnotations(annots: List[AnnotationInfo]): unit = annots foreach putAnnotation // Phase 2 methods: Write all entries to byte array ------------------------------ @@ -406,8 +405,8 @@ abstract class Pickler extends SubComponent { case (target: Symbol, attr @ AnnotationInfo(atp, args, assocs)) => writeRef(target) writeRef(atp) - for (c <- args) writeRef(c.asInstanceOf[Constant]) - for ((name, c) <- assocs) { writeRef(name); writeRef(c.asInstanceOf[Constant]) } + for (c <- args) writeRef(c) + for ((name, c) <- assocs) { writeRef(name); writeRef(c) } ATTRIBUTE case (target: Symbol, children: List[_]) => writeRef(target) @@ -434,7 +433,7 @@ abstract class Pickler extends SubComponent { case reflect.TypeApply(fun, args) => writeNat(TYPEAPPLYtree) writeRef(fun) - writeRef(args) + writeRefs(args) REFLTREE case reflect.Function(params, body) => writeNat(FUNCTIONtree) @@ -596,14 +595,15 @@ abstract class Pickler extends SubComponent { REFLSYM case reflect.LabelSymbol(name) => writeNat(LABELSYMBOLrsym) + writeRef(name) REFLSYM case AnnotationInfo(target, args, assocs) => writeRef(target) writeNat(args.length) - for (tree <- args) writeRef(tree.asInstanceOf[reflect.Tree]) - for ((name, tree) <- assocs) { + for (arg <- args) writeRef(arg) + for ((name, arg) <- assocs) { writeRef(name); - writeRef(tree.asInstanceOf[reflect.Tree]) + writeRef(arg) } ATTRIBTREE diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala index 5e3c4a5ce0..70735b79ff 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala @@ -292,6 +292,19 @@ abstract class UnPickler { } } + /** Read an annotation argument. It can be either a Constant or + * a reflect.Tree. + */ + private def readAnnotationArg(): AnnotationArgument = { + if (peekByte() == REFLTREE) { + val tree = readReflTree() + new AnnotationArgument(tree) + } else { + val const = readConstant() + new AnnotationArgument(const) + } + } + /** Read an attribute and store in referenced symbol */ private def readAnnotation(): AnyRef = { val tag = readByte() @@ -299,12 +312,14 @@ abstract class UnPickler { val target = readSymbolRef() if (tag == ATTRIBUTE) { val attrType = readTypeRef() - val args = new ListBuffer[Constant] - val assocs = new ListBuffer[(Name, Constant)] + val args = new ListBuffer[AnnotationArgument] + val assocs = new ListBuffer[(Name, AnnotationArgument)] while (readIndex != end) { val argref = readNat() - if (isNameEntry(argref)) assocs += (at(argref, readName), readConstantRef()) - else args += at(argref, readConstant) + if (isNameEntry(argref)) + assocs += (at(argref, readName), readAnnotationArgRef) + else + args += at(argref, readAnnotationArg) } val attr = AnnotationInfo(attrType, args.toList, assocs.toList) target.attributes = attr :: target.attributes @@ -497,7 +512,7 @@ abstract class UnPickler { } /** Read an annotation with reflect.Tree's */ - private def readTreeAttrib(): AnnotationInfo[reflect.Tree] = { + private def readTreeAttrib(): AnnotationInfo = { val tag = readByte() if(tag != ATTRIBTREE) errorBadSignature("tree-based annotation expected (" + tag + ")") @@ -505,11 +520,11 @@ abstract class UnPickler { val target = readTypeRef() val numargs = readNat() - val args = times(numargs, readReflTreeRef) + val args = times(numargs, readAnnotationArgRef) val assocs = until(end, {() => val name = readNameRef() - val tree = readReflTreeRef() + val tree = readAnnotationArgRef() (name,tree)}) AnnotationInfo(target, args, assocs) } @@ -519,13 +534,15 @@ abstract class UnPickler { private def readSymbolRef(): Symbol = at(readNat(), readSymbol) private def readTypeRef(): Type = at(readNat(), readType) private def readConstantRef(): Constant = at(readNat(), readConstant) + private def readAnnotationArgRef(): AnnotationArgument = + at(readNat(), readAnnotationArg) private def readReflTreeRef(): reflect.Tree = at(readNat(), readReflTree) private def readReflSymbolRef(): reflect.Symbol = at(readNat(), readReflSymbol) private def readReflTypeRef(): reflect.Type = at(readNat(), readReflType) - private def readTreeAttribRef(): AnnotationInfo[reflect.Tree] = + private def readTreeAttribRef(): AnnotationInfo = at(readNat(), readTreeAttrib) private def errorBadSignature(msg: String) = diff --git a/src/compiler/scala/tools/nsc/transform/LiftCode.scala b/src/compiler/scala/tools/nsc/transform/LiftCode.scala index 77d0992617..1359eafcf9 100644 --- a/src/compiler/scala/tools/nsc/transform/LiftCode.scala +++ b/src/compiler/scala/tools/nsc/transform/LiftCode.scala @@ -64,7 +64,12 @@ abstract class LiftCode extends Transform { super.update(sym,rsym) } - class Reifier(env: ReifyEnvironment, currentOwner: reflect.Symbol) { + + class Reifier(env: ReifyEnvironment, currentOwner: reflect.Symbol) + extends SymbolReifier + { + val symbols: global.type = global + def reify(tree: Tree): reflect.Tree = tree match { case Ident(_) => @@ -163,72 +168,13 @@ abstract class LiftCode extends Transform { throw new TypeError("cannot reify tree ("+tree.getClass()+"): " + tree) } - private def mkGlobalSymbol(fullname: String, sym: Symbol): reflect.Symbol = - if (sym.isClass) reflect.Class(fullname) - else if (sym.isType) reflect.TypeField(fullname, reify(sym.info)) - else if (sym.isMethod) reflect.Method(fullname, reify(sym.info)) - else reflect.Field(fullname, reify(sym.info)); - - def reify(sym: Symbol): reflect.Symbol = env.get(sym) match { - case Some(rsym) => - rsym - case None => - if (sym.isRoot || sym.isRootPackage || sym.isEmptyPackageClass || sym.isEmptyPackage) - reflect.RootSymbol - else if (sym.owner.isTerm) - reflect.NoSymbol - else reify(sym.owner) match { - case reflect.NoSymbol => - reflect.NoSymbol; - case reflect.RootSymbol => - mkGlobalSymbol(sym.name.toString(), sym) - case reflect.Class(ownername) => - mkGlobalSymbol(ownername + "." + sym.name, sym) - case _ => - reflect.NoSymbol - } - } - - var _log_reify_type_ = false - - def reify(tp: Type): reflect.Type = tp match { - case ErrorType => - if (_log_reify_type_) println("cannot handle ErrorType"); reflect.NoType - case WildcardType => - if (_log_reify_type_) println("cannot handle WildcardType"); reflect.NoType - case NoType => - if (_log_reify_type_) println("cannot handle NoType"); reflect.NoType - case NoPrefix => - if (_log_reify_type_) println("cannot handle NoPrefix"); reflect.NoType - case ThisType(sym) => - if (_log_reify_type_) println("ThisType ("+sym+")") - val rsym = reify(sym) - if (_log_reify_type_) println("reified is "+rsym+" cannot handle ThisType "+tp); reflect.NoType - case SingleType(pre, sym) => - if (_log_reify_type_) println("cannot handle SingleType "+tp); reflect.NoType - case ConstantType(value) => - if (_log_reify_type_) println("cannot handle ConstantType("+value+") "+tp); reflect.NoType - case TypeRef(pre, sym, args) => - if (_log_reify_type_) println("TypeRef! try to handle prefix") - val rpre = reify(pre) - if (_log_reify_type_) println("cannot handle TypeRef("+pre+","+sym+","+args+") == "+tp+")"); reflect.NoType - - case TypeBounds(lo, hi) => - if (_log_reify_type_) println("cannot handle TypeBounds "+tp); reflect.NoType - case RefinedType(parents, defs) => - if (_log_reify_type_) println("cannot handle RefinedType "+tp); reflect.NoType - case ClassInfoType(parents, defs, clazz) => - if (_log_reify_type_) println("cannot handle ClassInfoType "+tp); reflect.NoType - case MethodType(paramtypes, result) => - if (_log_reify_type_) println("cannot handle MethodType "+tp); reflect.NoType - case PolyType(tparams, result) => - if (_log_reify_type_) println("cannot handle PolyType "+tp); reflect.NoType - case AnnotatedType(attribs, tp) => - reify(tp) - case _ => - reflect.NoType - } - + override def reify(sym: Symbol): reflect.Symbol = + env.get(sym) match { + case Some(rsym) => + rsym + case None => + super.reify(sym) + } } type InjectEnvironment = ListMap[reflect.Symbol, Name] diff --git a/src/compiler/scala/tools/nsc/transform/SymbolReifier.scala b/src/compiler/scala/tools/nsc/transform/SymbolReifier.scala new file mode 100644 index 0000000000..e1fa4f6cfc --- /dev/null +++ b/src/compiler/scala/tools/nsc/transform/SymbolReifier.scala @@ -0,0 +1,172 @@ +package scala.tools.nsc.transform +import scala.tools.nsc.symtab.SymbolTable +import scala.reflect + +/** Functions to reify (and un-reify) symbols and types. + * These can be used with only a symbol table; they do not + * need a full compiler. + * + * @author Gilles Dubochet, Lex Spoon + */ +trait SymbolReifier { + val symbols: SymbolTable + import symbols._ + + private def mkGlobalSymbol(fullname: String, sym: Symbol): reflect.Symbol = + if (sym.isClass) reflect.Class(fullname) + else if (sym.isType) reflect.TypeField(fullname, reify(sym.info)) + else if (sym.isMethod) reflect.Method(fullname, reify(sym.info)) + else reflect.Field(fullname, reify(sym.info)); + + def reify(sym: Symbol): reflect.Symbol = { + if (sym.isRoot || sym.isRootPackage || sym.isEmptyPackageClass || sym.isEmptyPackage) + reflect.RootSymbol + else if (sym.owner.isTerm) + reflect.NoSymbol + else reify(sym.owner) match { + case reflect.NoSymbol => + reflect.NoSymbol; + case reflect.RootSymbol => + mkGlobalSymbol(sym.name.toString(), sym) + case reflect.Class(ownername) => + mkGlobalSymbol(ownername + "." + sym.name, sym) + case _ => + reflect.NoSymbol + } + } + + var _log_reify_type_ = true//false + + def reify(tp: Type): reflect.Type = tp match { + case ErrorType => + reflect.NoType + case WildcardType => + if (_log_reify_type_) println("cannot handle WildcardType") + reflect.NoType + case NoType => + reflect.NoType + case NoPrefix => + reflect.NoType + case ThisType(sym) => + val rsym = reify(sym) + reflect.ThisType(rsym) + case SingleType(pre, sym) => + reflect.SingleType(reify(pre), reify(sym)) + case ConstantType(value) => + reify(value.tpe) + case TypeRef(pre, sym, args) => + val rpre = reify(pre) + val rsym = reify(sym) + val rargs = args map reify + val beforeArgs = reflect.PrefixedType(rpre, rsym) + if(rargs.isEmpty) + beforeArgs + else if(beforeArgs == NoType) + beforeArgs + else + reflect.AppliedType(beforeArgs, rargs) + case TypeBounds(lo, hi) => + reflect.TypeBounds(reify(lo), reify(hi)) + case RefinedType(parents, defs) => + if (_log_reify_type_) println("cannot handle RefinedType "+tp); reflect.NoType + case ClassInfoType(parents, defs, clazz) => + if (_log_reify_type_) println("cannot handle ClassInfoType "+tp); reflect.NoType + case MethodType(paramtypes, result) => + reflect.MethodType(paramtypes.map(reify), reify(result)) + case PolyType(tparams, result) => + val boundss = + for { + param <- tparams + TypeBounds(lo,hi) = param.info.bounds + } yield (reify(lo), reify(hi)) + + reflect.PolyType( + tparams.map(reify), + boundss, + reify(result)) + case AnnotatedType(attribs, tp) => + reify(tp) + case _ => + println("could not reify: " + tp) + reflect.NoType + } + + + /** This is woefully incomplete. It is barely enough + * to process the types of Constant's . + */ + def unreify(tpe: reflect.Type): Type = + tpe match { + case reflect.NoPrefix => NoPrefix + case reflect.NoType => NoType + case reflect.NamedType(fullname) => + //NamedType(fullname) + println("NamedType: " + fullname) + NoType + case reflect.PrefixedType(_, reflect.Class("scala.Array")) => + definitions.ArrayClass.tpe + case reflect.PrefixedType(_, reflect.Class("java.lang.String")) => + definitions.StringClass.tpe + case reflect.PrefixedType(_, reflect.Class("scala.Unit")) => + definitions.UnitClass.tpe + case reflect.PrefixedType(_, reflect.Class("scala.Boolean")) => + definitions.BooleanClass.tpe + case reflect.PrefixedType(_, reflect.Class("scala.Byte")) => + definitions.ByteClass.tpe + case reflect.PrefixedType(_, reflect.Class("scala.Short")) => + definitions.ShortClass.tpe + case reflect.PrefixedType(_, reflect.Class("scala.Int")) => + definitions.IntClass.tpe + case reflect.PrefixedType(_, reflect.Class("scala.Long")) => + definitions.LongClass.tpe + case reflect.PrefixedType(_, reflect.Class("scala.Float")) => + definitions.FloatClass.tpe + case reflect.PrefixedType(_, reflect.Class("scala.Double")) => + definitions.DoubleClass.tpe + case reflect.PrefixedType(pre, sym) => + NoType + case reflect.SingleType(pre, sym) => + SingleType(unreify(pre), unreify(sym)) + case reflect.ThisType(clazz) => + ThisType(unreify(clazz)) + case reflect.AppliedType(tpe, args) => + val untpe = unreify(tpe) + if (untpe == NoType) + NoType + else + appliedType(untpe, args.map(unreify)) + case reflect.TypeBounds(lo, hi) => + TypeBounds(unreify(lo), unreify(hi)) + case reflect.MethodType(formals, restpe) => + MethodType(formals.map(unreify), unreify(restpe)) + case reflect.PolyType(typeParams, typeBounds, resultType) => + PolyType(typeParams.map(unreify), unreify(resultType)) + case _ => NoType + } + + + /** This is woefully incomplete. It is barely enough + * to process the types of Constant's . + */ + def unreify(symbol: reflect.Symbol): Symbol = + symbol match { + case reflect.Class(fullname) => + fullname match { + case "scala.Unit" => definitions.UnitClass + case "scala.Boolean" => definitions.BooleanClass + case "scala.Byte" => definitions.ByteClass + case "scala.Short" => definitions.ShortClass + case "scala.Int" => definitions.IntClass + case "scala.Long" => definitions.LongClass + case "scala.Float" => definitions.FloatClass + case "scala.Double" => definitions.DoubleClass + + case "scala.Array" => definitions.ArrayClass + + case _ => NoSymbol + + } + + case _ => NoSymbol + } +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 236d46a272..10b390f041 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -645,7 +645,7 @@ trait Namers requires Analyzer { case defn: MemberDef => val ainfos = for { annot <- defn.mods.annotations - val ainfo = typer.typedAnnotation(annot, typer.getConstant) + val ainfo = typer.typedAnnotation(annot) if !ainfo.atp.isError } yield ainfo if (!ainfos.isEmpty) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index b1e81f6ad4..411662c96e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -110,11 +110,6 @@ trait Typers requires Analyzer { */ val LHSmode = 0x400 - /** The mode <code>CONSTmode</code> is set when expressions should evaluate - * to constant sused for attribute arguments. - */ - val CONSTmode = 0x800 - /** The mode <code>REGPATmode</code> is set when regular expression patterns * are allowed. */ @@ -129,7 +124,7 @@ trait Typers requires Analyzer { */ val HKmode = 0x4000 // @M: could also use POLYmode | TAPPmode - private val stickyModes: int = EXPRmode | PATTERNmode | TYPEmode | CONSTmode | ALTmode + private val stickyModes: int = EXPRmode | PATTERNmode | TYPEmode | ALTmode private def funMode(mode: int) = mode & (stickyModes | SCCmode) | FUNmode | POLYmode @@ -1536,21 +1531,7 @@ trait Typers requires Analyzer { if (fun.symbol == List_apply && args.isEmpty) { atPos(tree.pos) { gen.mkNil setType restpe } - } else if ((mode & CONSTmode) != 0 && - fun.symbol.owner == definitions.ArrayModule.tpe.symbol && - fun.symbol.name == nme.apply) { - val elems = new Array[Constant](args2.length) - var i = 0; - for (val arg <- args2) arg match { - case Literal(value) => - elems(i) = value - i = i + 1 - case _ => errorTree(arg, "constant required") - } - val arrayConst = new ArrayConstant(elems, restpe) - Literal(arrayConst) setType mkConstantType(arrayConst) - } - else + } else constfold(copy.Apply(tree, fun, args2).setType(ifPatternSkipFormals(restpe))) } else { assert((mode & PATTERNmode) == 0); // this case cannot arise for patterns @@ -1645,26 +1626,37 @@ trait Typers requires Analyzer { } } - def getConstant(tree: Tree): Constant = tree match { - case Literal(value) => value - case arg => error(arg.pos, "attribute argument needs to be a constant; found: "+arg); Constant(null) - } - - def typedAnnotation[T](annot: Annotation, reify: Tree => T): AnnotationInfo[T] = { + def typedAnnotation(annot: Annotation): AnnotationInfo = { var attrError: Boolean = false; def error(pos: Position, msg: String): Null = { context.error(pos, msg) attrError = true null } - typed(annot.constr, EXPRmode | CONSTmode, AnnotationClass.tpe) match { + def needConst(tr: Tree) { + error(tr.pos, "attribute argument needs to be a constant; found: "+tr) + } + + typed(annot.constr, EXPRmode, AnnotationClass.tpe) match { case t @ Apply(Select(New(tpt), nme.CONSTRUCTOR), args) => if (t.isErroneous) { - AnnotationInfo[T](ErrorType, List(), List()) + AnnotationInfo(ErrorType, List(), List()) } else { val annType = tpt.tpe - val constrArgs = args map reify + + val needsConstant = + (!settings.Xplugtypes.value || + annType <:< ClassfileAnnotationClass.tpe) + + def annotArg(tree: Tree): AnnotationArgument = { + val arg = new AnnotationArgument(liftcode.reify(tree)) + if(needsConstant && !arg.isConstant) + needConst(tree) + arg + } + val constrArgs = args map annotArg + val attrScope = annType.decls .filter(sym => sym.isMethod && !sym.isConstructor && sym.hasFlag(JAVA)) val names = new collection.mutable.HashSet[Symbol] @@ -1681,7 +1673,10 @@ trait Typers requires Analyzer { error(ntree.pos, "duplicate value for element " + name) } else { names -= sym - (sym.name, reify(typed(rhs, EXPRmode | CONSTmode, sym.tpe.resultType))) + val annArg = + annotArg( + typed(rhs, EXPRmode, sym.tpe.resultType)) + (sym.name, annArg) } } } @@ -1716,14 +1711,16 @@ trait Typers requires Analyzer { case _ => NoType } - def typedAnnotated(annot: Annotation, arg1: Tree) = { - def annotTypeTree(ainfo: AnnotationInfo[Any]): Tree = + def typedAnnotated(annot: Annotation, arg1: Tree): Tree = { + def annotTypeTree(ainfo: AnnotationInfo): Tree = TypeTree(arg1.tpe.withAttribute(ainfo)) setOriginal tree + if (arg1.isType) { - val annotInfo = typedAnnotation(annot, liftcode.reify) + val annotInfo = typedAnnotation(annot) if (settings.Xplugtypes.value) annotTypeTree(annotInfo) else arg1 } else { - val annotInfo = typedAnnotation(annot, getConstant) + val annotInfo = typedAnnotation(annot) + arg1 match { case _: DefTree => if (!annotInfo.atp.isError) { |