All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.tools.nsc.symtab.AnnotationInfos.scala Maven / Gradle / Ivy

There is a newer version: 2.10.2_1
Show newest version
/* NSC -- new Scala compiler
 * Copyright 2007-2011 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc
package symtab

import scala.tools.nsc.transform.Reifiers
import util._

/** AnnotationInfo and its helpers */
trait AnnotationInfos extends reflect.generic.AnnotationInfos { self: SymbolTable =>

  /** Arguments to classfile annotations (which are written to
   *  bytecode as java annotations) are either:
   *
   *  - constants
   *  - arrays of constants
   *  - or nested classfile annotations
   */
  abstract class ClassfileAnnotArg

  /** Represents a compile-time Constant (`Boolean`, `Byte`, `Short`,
   *  `Char`, `Int`, `Long`, `Float`, `Double`, `String`, `java.lang.Class` or
   *  an instance of a Java enumeration value).
   */
  case class LiteralAnnotArg(const: Constant)
  extends ClassfileAnnotArg {
    override def toString = const.escapedStringValue
  }

  object LiteralAnnotArg extends LiteralAnnotArgExtractor

  /** Represents an array of classfile annotation arguments */
  case class ArrayAnnotArg(args: Array[ClassfileAnnotArg])
  extends ClassfileAnnotArg {
    override def toString = args.mkString("[", ", ", "]")
  }

  object ArrayAnnotArg extends ArrayAnnotArgExtractor

  /** A specific annotation argument that encodes an array of bytes as an
   *  array of `Long`. The type of the argument declared in the annotation
   *  must be `String`. This specialised class is used to encode Scala
   *  signatures for reasons of efficiency, both in term of class-file size
   *  and in term of compiler performance.
   */
  case class ScalaSigBytes(bytes: Array[Byte]) extends ClassfileAnnotArg {
    override def toString = (bytes map { byte => (byte & 0xff).toHexString }).mkString("[ ", " ", " ]")
    lazy val encodedBytes =
      reflect.generic.ByteCodecs.encode(bytes)
    def isLong: Boolean = (encodedBytes.length > 65535)
    def sigAnnot: Type =
      if (this.isLong)
        definitions.ScalaLongSignatureAnnotation.tpe
      else
        definitions.ScalaSignatureAnnotation.tpe
  }

  /** Represents a nested classfile annotation */
  case class NestedAnnotArg(annInfo: AnnotationInfo)
  extends ClassfileAnnotArg {
    // The nested annotation should not have any Scala annotation arguments
    assert(annInfo.args.isEmpty, annInfo.args)
    override def toString = annInfo.toString
  }

  object NestedAnnotArg extends NestedAnnotArgExtractor

  class AnnotationInfoBase

  /** Typed information about an annotation. It can be attached to either
   *  a symbol or an annotated type.
   *
   *  Annotations are written to the classfile as Java annotations
   *  if `atp` conforms to `ClassfileAnnotation` (the classfile parser adds
   *  this interface to any Java annotation class).
   *
   *  Annotations are pickled (written to scala symtab attribute in the
   *  classfile) if `atp` inherits form `StaticAnnotation`.
   *
   *  `args` stores arguments to Scala annotations, represented as typed
   *  trees. Note that these trees are not transformed by any phases
   *  following the type-checker.
   *
   *  `assocs` stores arguments to classfile annotations as name-value pairs.
   */
  case class AnnotationInfo(atp: Type, args: List[Tree],
                            assocs: List[(Name, ClassfileAnnotArg)])
  extends AnnotationInfoBase {

    // Classfile annot: args empty. Scala annot: assocs empty.
    assert(args.isEmpty || assocs.isEmpty)

    private var rawpos: Position = NoPosition
    def pos = rawpos
    def setPos(pos: Position): this.type = {
      rawpos = pos
      this
    }

    lazy val isTrivial: Boolean = atp.isTrivial && !(args exists (_.exists(_.isInstanceOf[This]))) // see annotationArgRewriter

    override def toString: String = atp +
      (if (!args.isEmpty) args.mkString("(", ", ", ")") else "") +
      (if (!assocs.isEmpty) (assocs map { case (x, y) => x+" = "+y } mkString ("(", ", ", ")")) else "")

    /** Check whether the type or any of the arguments are erroneous */
    def isErroneous = atp.isErroneous || args.exists(_.isErroneous)

    /** Check whether any of the arguments mention a symbol */
    def refsSymbol(sym: Symbol) =
      args.exists(_.exists(_.symbol == sym))

    /** Change all ident's with Symbol "from" to instead use symbol "to" */
    def substIdentSyms(from: Symbol, to: Symbol) = {
      val subs = new TreeSymSubstituter(List(from), List(to))
      AnnotationInfo(atp, args.map(subs(_)), assocs).setPos(pos)
    }

    // !!! when annotation arguments are not literal strings, but any sort of
    // assembly of strings, there is a fair chance they will turn up here not as
    // Literal(const) but some arbitrary AST.
    def stringArg(index: Int): Option[String] = if(args.size > index) Some(args(index) match {
      case Literal(const) => const.stringValue
      case x              => x.toString // should not be necessary, but better than silently ignoring an issue
    }) else None

    def intArg(index: Int): Option[Int] = if(args.size > index) Some(args(index)) collect {
      case Literal(Constant(x: Int)) => x
    } else None
  }

  object AnnotationInfo extends AnnotationInfoExtractor

  lazy val classfileAnnotArgManifest: ClassManifest[ClassfileAnnotArg] =
    reflect.ClassManifest.classType(classOf[ClassfileAnnotArg])

  /** Symbol annotations parsed in `Namer` (typeCompleter of
   *  definitions) have to be lazy (#1782)
   */
  case class LazyAnnotationInfo(annot: () => AnnotationInfo)
  extends AnnotationInfoBase
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy