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 + -
显示快捷键?