genjvm.scala

来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 1,594 行 · 第 1/4 页

SCALA
1,594
字号
/* NSC -- new Scala compiler * Copyright 2005-2008 LAMP/EPFL * @author  Iulian Dragos */// $Id: GenJVM.scala 14357 2008-03-11 13:53:09Z michelou $package scala.tools.nsc.backend.jvmimport java.io.Fileimport java.nio.ByteBufferimport scala.collection.immutable.{Set, ListSet}import scala.collection.mutable.{Map, HashMap, HashSet}import scala.tools.nsc.symtab._import scala.tools.nsc.util.{Position, NoPosition}import ch.epfl.lamp.fjbg._/** This class ... * *  @author  Iulian Dragos *  @version 1.0 */abstract class GenJVM extends SubComponent {  import global._  import icodes._  import icodes.opcodes._  val phaseName = "jvm"  /** Create a new phase */  override def newPhase(p: Phase) = new JvmPhase(p)  /** JVM code generation phase   */  class JvmPhase(prev: Phase) extends StdPhase(prev) {    override def erasedTypes = true    object codeGenerator extends BytecodeGenerator    override def run {      if (settings.debug.value) inform("[running phase " + name + " on icode]")      if (settings.Xdce.value)        icodes.classes.retain { (sym: Symbol, cls: IClass) => !inliner.isClosureClass(sym) || deadCode.liveClosures(sym) }       classes.values foreach codeGenerator.genClass    }    override def apply(unit: CompilationUnit) {      abort("JVM works on icode classes, not on compilation units!")    }  }  var pickledBytes = 0 // statistics  /**   * Java bytecode generator.   *   */  class BytecodeGenerator {    val MIN_SWITCH_DENSITY = 0.7    val StringBuilderClass = "scala.StringBuilder"    val BoxesRunTime = "scala.runtime.BoxesRunTime"    val StringBuilderType = new JObjectType(StringBuilderClass)    val toStringType = new JMethodType(JObjectType.JAVA_LANG_STRING, JType.EMPTY_ARRAY)    // Scala attributes    val SerializableAttr = definitions.SerializableAttr    val SerialVersionUID = definitions.getClass("scala.SerialVersionUID")    val CloneableAttr    = definitions.getClass("scala.cloneable")    val TransientAtt     = definitions.getClass("scala.transient")    val VolatileAttr     = definitions.getClass("scala.volatile")    val RemoteAttr       = definitions.getClass("scala.remote")    val ThrowsAttr       = definitions.getClass("scala.throws")    val BeanInfoAttr     = definitions.getClass("scala.reflect.BeanInfo")    val BeanInfoSkipAttr = definitions.getClass("scala.reflect.BeanInfoSkip")    val BeanDisplayNameAttr = definitions.getClass("scala.reflect.BeanDisplayName")    val BeanDescriptionAttr = definitions.getClass("scala.reflect.BeanDescription")    lazy val CloneableClass  = definitions.getClass("java.lang.Cloneable")    lazy val RemoteInterface = definitions.getClass("java.rmi.Remote")    lazy val RemoteException = definitions.getClass("java.rmi.RemoteException").tpe    var clasz: IClass = _    var method: IMethod = _    var code: Code = _    var jclass: JClass = _    var jmethod: JMethod = _    var jcode: JExtendedCode = _    var innerClasses: Set[Symbol] = ListSet.empty // referenced inner classes    val fjbgContext =      if (settings.target.value == "jvm-1.5") new FJBGContext(49, 0)      else new FJBGContext()    val emitSource = settings.debuginfo.level >= 1    val emitLines  = settings.debuginfo.level >= 2    val emitVars   = settings.debuginfo.level >= 3    /**     * @param jclass ...     * @param sym    ...     */    def emitClass(jclass: JClass, sym: Symbol) {      def addScalaAttr(sym: Symbol): Unit = currentRun.symData.get(sym) match {        case Some(pickle) =>          val scalaAttr = fjbgContext.JOtherAttribute(jclass,                                                  jclass,                                                  nme.ScalaSignatureATTR.toString,                                                  pickle.bytes,                                                  pickle.writeIndex)          pickledBytes = pickledBytes + pickle.writeIndex          jclass.addAttribute(scalaAttr)          currentRun.symData -= sym          currentRun.symData -= sym.linkedSym          //System.out.println("Generated ScalaSig Attr for " + sym)//debug        case _ =>          log("Could not find pickle information for " + sym)      }      if (!(jclass.getName().endsWith("$") && sym.isModuleClass))        addScalaAttr(if (isTopLevelModule(sym)) sym.sourceModule else sym);      addInnerClasses      val outfile = getFile(jclass, ".class")      jclass.writeTo(outfile)      val file = scala.tools.nsc.io.AbstractFile.getFile(outfile)      informProgress("wrote " + outfile)    }    var serialVUID: Option[Long] = None    var remoteClass: Boolean = false    def genClass(c: IClass) {      clasz = c      innerClasses = ListSet.empty      var parents = c.symbol.info.parents      var ifaces  = JClass.NO_INTERFACES      val name    = javaName(c.symbol)      serialVUID  = None      remoteClass = false      if (parents.isEmpty)        parents = definitions.ObjectClass.tpe :: parents;      if (!forCLDC)        for (val attr <- c.symbol.attributes) attr match {          case AnnotationInfo(tp, _, _) if tp.typeSymbol == SerializableAttr =>            parents = parents ::: List(definitions.SerializableClass.tpe)          case AnnotationInfo(tp, _, _) if tp.typeSymbol == CloneableAttr =>            parents = parents ::: List(CloneableClass.tpe)          case AnnotationInfo(tp, value :: _, _) if tp.typeSymbol == SerialVersionUID =>            serialVUID = Some(value.constant.get.longValue)          case AnnotationInfo(tp, _, _) if tp.typeSymbol == RemoteAttr =>            parents = parents ::: List(RemoteInterface.tpe)            remoteClass = true          case _ => ()        }      parents = parents.removeDuplicates      if (parents.length > 1) {        ifaces = new Array[String](parents.length - 1)        parents.drop(1).map((s) => javaName(s.typeSymbol)).copyToArray(ifaces, 0)        ()      }      jclass = fjbgContext.JClass(javaFlags(c.symbol),                                  name,                                  javaName(parents(0).typeSymbol),                                  ifaces,                                  c.cunit.source.toString)      if (isStaticModule(c.symbol) || serialVUID != None) {        if (isStaticModule(c.symbol))            addModuleInstanceField;        addStaticInit(jclass)        if (isTopLevelModule(c.symbol)) {          if (c.symbol.linkedClassOfModule == NoSymbol)            dumpMirrorClass;          else if (c.symbol.linkedClassOfModule != NoSymbol &&              !currentRun.compiles(c.symbol.linkedClassOfModule)) {            log("Dumping mirror class for " + c.symbol + " even though " +                "linked class exists, but is not compiled in this run")            dumpMirrorClass          } else            log("No mirror class for module with linked class: " +                c.symbol.fullNameString)        }      }      clasz.fields foreach genField      clasz.methods foreach genMethod      addAnnotations(jclass, c.symbol.attributes)      emitClass(jclass, c.symbol)            if (c.symbol.attributes.exists(_.atp.typeSymbol == BeanInfoAttr))        genBeanInfoClass(c)     }    /**     * Generate a bean info class that describes the given class.     *     * @author Ross Judson (ross.judson@soletta.com)     */    def genBeanInfoClass(c: IClass) {            val description = c.symbol.attributes.find(_.atp.typeSymbol == BeanDescriptionAttr)      // informProgress(description.toString())	      val beanInfoClass = fjbgContext.JClass(javaFlags(c.symbol),            javaName(c.symbol) + "BeanInfo",            "scala/reflect/ScalaBeanInfo",            JClass.NO_INTERFACES,            c.cunit.source.toString)	      var fieldList = List[String]()      for (f <- clasz.fields if f.symbol.hasGetter;	   val g = f.symbol.getter(c.symbol);	   val s = f.symbol.setter(c.symbol);	   if g.isPublic)	fieldList = javaName(f.symbol) :: javaName(g) :: (if (s != NoSymbol) javaName(s) else null) :: fieldList      val methodList = 	for (m <- clasz.methods 	    if !m.symbol.isConstructor &&	       m.symbol.isPublic &&	       !(m.symbol.name startsWith "$") &&	       !m.symbol.isGetter &&	       !m.symbol.isSetter) yield javaName(m.symbol)      val constructor = beanInfoClass.addNewMethod(JAccessFlags.ACC_PUBLIC, "<init>", JType.VOID, javaTypes(Nil), javaNames(Nil))      jcode = constructor.getCode().asInstanceOf[JExtendedCode]      val strKind = new JObjectType(javaName(definitions.StringClass))      val stringArrayKind = new JArrayType(strKind)      val conType = new JMethodType(JType.VOID, Array(javaType(definitions.ClassClass), stringArrayKind, stringArrayKind))	      def push(lst:Seq[String]) {	var fi = 0	for (f <- lst) {	  jcode.emitDUP()	  jcode.emitPUSH(fi)	  if (f != null)	    jcode.emitPUSH(f)	  else	    jcode.emitACONST_NULL()	  jcode.emitASTORE(strKind)	  fi += 1	}      }      jcode.emitALOAD_0()      // push the class	      jcode.emitPUSH(javaType(c.symbol).asInstanceOf[JReferenceType])            // push the the string array of field information      jcode.emitPUSH(fieldList.length)	      jcode.emitANEWARRAY(strKind)      push(fieldList)            // push the string array of method information	      jcode.emitPUSH(methodList.length)      jcode.emitANEWARRAY(strKind)      push(methodList)            // invoke the superclass constructor, which will do the      // necessary java reflection and create Method objects.	      jcode.emitINVOKESPECIAL("scala/reflect/ScalaBeanInfo", "<init>", conType)      jcode.emitRETURN()            // write the bean information class file.      val outfile = getFile(beanInfoClass, ".class")      beanInfoClass.writeTo(outfile)      val file = scala.tools.nsc.io.AbstractFile.getFile(outfile)      informProgress("wrote BeanInfo " + outfile)    }            /** Add the given 'throws' attributes to jmethod */    def addExceptionsAttribute(jmethod: JMethod, excs: List[AnnotationInfo]) {      if (excs.isEmpty) return      val cpool = jmethod.getConstantPool()      val buf: ByteBuffer = ByteBuffer.allocate(512)      var nattr = 0      // put some radom value; the actual number is determined at the end      buf.putShort(0xbaba.toShort)      for (AnnotationInfo(tp, List(exc), _) <- excs.removeDuplicates if tp.typeSymbol == ThrowsAttr) {        buf.putShort(          cpool.addClass(            javaName(exc.constant.get.typeValue.typeSymbol)).shortValue)        nattr += 1      }      assert(nattr > 0)      buf.putShort(0, nattr.toShort)      addAttribute(jmethod, nme.ExceptionsATTR, buf)    }    /** Whether an annotation should be emitted as a Java annotation */    private def shouldEmitAttribute(annot: AnnotationInfo) =      (annot.atp.typeSymbol.hasFlag(Flags.JAVA) &&       annot.atp.typeSymbol.isNonBottomSubClass(definitions.ClassfileAnnotationClass) &&       annot.isConstant)	         private def emitAttributes(cpool: JConstantPool, buf: ByteBuffer, attributes: List[AnnotationInfo]): Int = {//      val cpool = jclass.getConstantPool()      def emitElement(const: Constant): Unit = const.tag match {        case BooleanTag =>          buf.put('Z'.toByte)          buf.putShort(cpool.addInteger(if(const.booleanValue) 1 else 0).toShort)        case ByteTag    =>          buf.put('B'.toByte)          buf.putShort(cpool.addInteger(const.byteValue).toShort)        case ShortTag   =>          buf.put('S'.toByte)          buf.putShort(cpool.addInteger(const.shortValue).toShort)        case CharTag    =>          buf.put('C'.toByte)          buf.putShort(cpool.addInteger(const.charValue).toShort)        case IntTag     =>          buf.put('I'.toByte)          buf.putShort(cpool.addInteger(const.intValue).toShort)        case LongTag    =>          buf.put('J'.toByte)          buf.putShort(cpool.addLong(const.longValue).toShort)        case FloatTag   =>          buf.put('F'.toByte)          buf.putShort(cpool.addFloat(const.floatValue).toShort)        case DoubleTag  =>          buf.put('D'.toByte)          buf.putShort(cpool.addDouble(const.doubleValue).toShort)        case StringTag  =>          buf.put('s'.toByte)          buf.putShort(cpool.addUtf8(const.stringValue).toShort)        case ClassTag   =>          buf.put('c'.toByte)          buf.putShort(cpool.addUtf8(javaType(const.typeValue).getSignature()).toShort)        case EnumTag =>          buf.put('e'.toByte)          buf.putShort(cpool.addUtf8(javaType(const.tpe).getSignature()).toShort)          buf.putShort(cpool.addUtf8(const.symbolValue.name.toString).toShort)        case ArrayTag =>          buf.put('['.toByte)          val arr = const.arrayValue          buf.putShort(arr.length.toShort)          for (val elem <- arr) emitElement(elem)      }      var nattr = 0      val pos = buf.position()      // put some random value; the actual number of annotations is determined at the end      buf.putShort(0xbaba.toShort)      for (attrib@AnnotationInfo(typ, consts, nvPairs) <- attributes;            if shouldEmitAttribute(attrib))      {	nattr += 1        val jtype = javaType(typ)        buf.putShort(cpool.addUtf8(jtype.getSignature()).toShort)        assert(consts.length <= 1, consts.toString)        buf.putShort((consts.length + nvPairs.length).toShort)        if (!consts.isEmpty) {          buf.putShort(cpool.addUtf8("value").toShort)          emitElement(consts.head.constant.get)        }        for ((name, value) <- nvPairs) {          buf.putShort(cpool.addUtf8(name.toString).toShort)          emitElement(value.constant.get)        }      }      // save the number of annotations      buf.putShort(pos, nattr.toShort)      nattr    }    def addAnnotations(jmember: JMember, attributes: List[AnnotationInfo]) {      val toEmit = attributes.filter(shouldEmitAttribute(_))      if (toEmit.isEmpty) return      val buf: ByteBuffer = ByteBuffer.allocate(2048)      emitAttributes(jmember.getConstantPool, buf, toEmit)      addAttribute(jmember, nme.RuntimeAnnotationATTR, buf)    }    def addParamAnnotations(pattrss: List[List[AnnotationInfo]]) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?