package scala.tools.nsc
package backend.jvm
import java.io.{ DataOutputStream, OutputStream }
import java.nio.ByteBuffer
import scala.collection.{ mutable, immutable }
import scala.reflect.generic.{ PickleFormat, PickleBuffer }
import scala.tools.reflect.SigParser
import scala.tools.nsc.io.{ AbstractFile, Path }
import scala.tools.nsc.util.ScalaClassLoader
import scala.tools.nsc.symtab._
import scala.tools.nsc.symtab.classfile.ClassfileConstants._
import ch.epfl.lamp.fjbg._
import JAccessFlags._
import JObjectType.{ JAVA_LANG_STRING, JAVA_LANG_OBJECT }
import java.util.jar.{ JarEntry, JarOutputStream }
abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with BytecodeWriters {
import global._
import icodes._
import icodes.opcodes._
import definitions.{
NullClass, RuntimeNullClass, NothingClass, RuntimeNothingClass,
AnyClass, ObjectClass, ThrowsClass, ThrowableClass, ClassfileAnnotationClass,
SerializableClass, StringClass, ClassClass, FunctionClass,
DeprecatedAttr, SerializableAttr, SerialVersionUIDAttr, VolatileAttr,
TransientAttr, CloneableAttr, RemoteAttr
}
val phaseName = "jvm"
override def newPhase(p: Phase): Phase = new JvmPhase(p)
private def outputDirectory(sym: Symbol): AbstractFile = (
settings.outputDirs.outputDirFor {
atPhase(currentRun.flattenPhase.prev)(sym.sourceFile)
}
)
private def getFile(base: AbstractFile, cls: JClass, suffix: String): AbstractFile = {
var dir = base
val pathParts = cls.getName().split("[./]").toList
for (part <- pathParts.init) {
dir = dir.subdirectoryNamed(part)
}
dir.fileNamed(pathParts.last + suffix)
}
private def getFile(sym: Symbol, cls: JClass, suffix: String): AbstractFile =
getFile(outputDirectory(sym), cls, suffix)
class JvmPhase(prev: Phase) extends ICodePhase(prev) {
def name = phaseName
override def erasedTypes = true
def apply(cls: IClass) = sys.error("no implementation")
override def run() {
if (settings.debug.value) inform("[running phase " + name + " on icode]")
if (settings.Xdce.value)
for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym))
icodes.classes -= sym
val bytecodeWriter = settings.outputDirs.getSingleOutput match {
case Some(f) if f hasExtension "jar" =>
new DirectToJarfileWriter(f)
case _ =>
if (settings.Ygenjavap.isDefault) new ClassBytecodeWriter { }
else new ClassBytecodeWriter with JavapBytecodeWriter { }
}
val codeGenerator = new BytecodeGenerator(bytecodeWriter)
classes.values foreach (codeGenerator genClass _)
bytecodeWriter.close()
classes.clear()
}
}
def moduleSuffix(sym: Symbol) =
if (sym.hasModuleFlag && !sym.isMethod &&
!sym.isImplClass && !sym.isJavaDefined) "$"
else ""
var pickledBytes = 0
class BytecodeGenerator(bytecodeWriter: BytecodeWriter) extends BytecodeUtil {
def this() = this(new ClassBytecodeWriter { })
def debugLevel = settings.debuginfo.indexOfChoice
import bytecodeWriter.writeClass
val MIN_SWITCH_DENSITY = 0.7
val INNER_CLASSES_FLAGS =
(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT)
val PublicStatic = ACC_PUBLIC | ACC_STATIC
val PublicStaticFinal = ACC_PUBLIC | ACC_STATIC | ACC_FINAL
val StringBuilderClassName = definitions.StringBuilderClass.fullName
val BoxesRunTime = "scala.runtime.BoxesRunTime"
val StringBuilderType = new JObjectType(StringBuilderClassName)
val toStringType = new JMethodType(JAVA_LANG_STRING, JType.EMPTY_ARRAY)
val arrayCloneType = new JMethodType(JAVA_LANG_OBJECT, JType.EMPTY_ARRAY)
val MethodTypeType = new JObjectType("java.dyn.MethodType")
val JavaLangClassType = new JObjectType("java.lang.Class")
val MethodHandleType = new JObjectType("java.dyn.MethodHandle")
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
val versionPickle = {
val vp = new PickleBuffer(new Array[Byte](16), -1, 0)
assert(vp.writeIndex == 0)
vp writeNat PickleFormat.MajorVersion
vp writeNat PickleFormat.MinorVersion
vp writeNat 0
vp
}
var clasz: IClass = _
var method: IMethod = _
var jclass: JClass = _
var jmethod: JMethod = _
val fjbgContext = new FJBGContext(49, 0)
val emitSource = debugLevel >= 1
val emitLines = debugLevel >= 2
val emitVars = debugLevel >= 3
override def javaName(sym: Symbol): String = {
if (sym.isClass && !sym.rawowner.isPackageClass && !sym.isModuleClass)
innerClassBuffer += sym
super.javaName(sym)
}
def emitClass(jclass: JClass, sym: Symbol) {
addInnerClasses(jclass)
writeClass("" + sym.name, jclass, sym)
}
def scalaSignatureAddingMarker(jclass: JClass, sym: Symbol): Option[AnnotationInfo] =
currentRun.symData get sym match {
case Some(pickle) if !jclass.getName().endsWith("$") =>
val scalaAttr =
fjbgContext.JOtherAttribute(jclass, jclass, tpnme.ScalaSignatureATTR.toString,
versionPickle.bytes, versionPickle.writeIndex)
jclass addAttribute scalaAttr
val scalaAnnot = {
val sigBytes = ScalaSigBytes(pickle.bytes.take(pickle.writeIndex))
AnnotationInfo(sigBytes.sigAnnot, Nil, List((nme.bytes, sigBytes)))
}
pickledBytes += pickle.writeIndex
currentRun.symData -= sym
currentRun.symData -= sym.companionSymbol
Some(scalaAnnot)
case _ =>
val markerAttr =
fjbgContext.JOtherAttribute(jclass, jclass, tpnme.ScalaATTR.toString, new Array[Byte](0), 0)
jclass addAttribute markerAttr
None
}
var serialVUID: Option[Long] = None
var isRemoteClass: Boolean = false
var isParcelableClass = false
private val innerClassBuffer = new mutable.ListBuffer[Symbol]
def genClass(c: IClass) {
clasz = c
innerClassBuffer.clear()
var parents = c.symbol.info.parents
var ifaces = JClass.NO_INTERFACES
val name = javaName(c.symbol)
serialVUID = None
isRemoteClass = false
isParcelableClass = isAndroidParcelableClass(c.symbol)
if (parents.isEmpty)
parents = List(ObjectClass.tpe)
for (annot <- c.symbol.annotations) annot match {
case AnnotationInfo(tp, _, _) if tp.typeSymbol == SerializableAttr =>
parents :+= SerializableClass.tpe
case AnnotationInfo(tp, _, _) if tp.typeSymbol == CloneableAttr =>
parents :+= CloneableClass.tpe
case AnnotationInfo(tp, Literal(const) :: _, _) if tp.typeSymbol == SerialVersionUIDAttr =>
serialVUID = Some(const.longValue)
case AnnotationInfo(tp, _, _) if tp.typeSymbol == RemoteAttr =>
parents :+= RemoteInterface.tpe
isRemoteClass = true
case _ =>
}
parents = parents.distinct
if (parents.tail.nonEmpty)
ifaces = mkArray(parents drop 1 map (x => javaName(x.typeSymbol)))
jclass = fjbgContext.JClass(javaFlags(c.symbol),
name,
javaName(parents(0).typeSymbol),
ifaces,
c.cunit.source.toString)
if (isStaticModule(c.symbol) || serialVUID != None || isParcelableClass ||
clasz.bootstrapClass.isDefined) {
if (isStaticModule(c.symbol))
addModuleInstanceField
addStaticInit(jclass, c.lookupStaticCtor)
if (isTopLevelModule(c.symbol)) {
if (c.symbol.companionClass == NoSymbol)
dumpMirrorClass(c.symbol, c.cunit.source.toString)
else
log("No mirror class for module with linked class: " +
c.symbol.fullName)
}
}
else {
c.lookupStaticCtor foreach (constructor => addStaticInit(jclass, Some(constructor)))
def isCandidateForForwarders(sym: Symbol): Boolean =
atPhase(currentRun.picklerPhase.next) {
!(sym.name.toString contains '$') && sym.hasModuleFlag && !sym.isImplClass && !sym.isNestedClass
}
val lmoc = (
try c.symbol.companionModule
catch { case x: AssertionError =>
Console.println("Suppressing failed assert: " + x)
NoSymbol
}
)
if (lmoc != NoSymbol && !c.symbol.isInterface) {
if (isCandidateForForwarders(lmoc) && !settings.noForwarders.value) {
log("Adding static forwarders from '%s' to implementations in '%s'".format(c.symbol, lmoc))
addForwarders(jclass, lmoc.moduleClass)
}
}
}
if (clasz.bootstrapClass.isDefined)
jclass setBootstrapClass clasz.bootstrapClass.get
clasz.fields foreach genField
clasz.methods foreach genMethod
val ssa = scalaSignatureAddingMarker(jclass, c.symbol)
addGenericSignature(jclass, c.symbol, c.symbol.owner)
addAnnotations(jclass, c.symbol.annotations ++ ssa)
addEnclosingMethodAttribute(jclass, c.symbol)
emitClass(jclass, c.symbol)
if (c.symbol hasAnnotation BeanInfoAttr)
genBeanInfoClass(c)
}
private def addEnclosingMethodAttribute(jclass: JClass, clazz: Symbol) {
val sym = clazz.originalEnclosingMethod
if (sym.isMethod) {
log("enclosing method for %s is %s (in %s)".format(clazz, sym, sym.enclClass))
jclass addAttribute fjbgContext.JEnclosingMethodAttribute(
jclass,
javaName(sym.enclClass),
javaName(sym),
javaType(sym)
)
} else if (clazz.isAnonymousClass) {
val enclClass = clazz.rawowner
assert(enclClass.isClass, "" + enclClass)
val sym = enclClass.primaryConstructor
if (sym == NoSymbol)
log("Ran out of room looking for an enclosing method for %s: no constructor here.".format(
enclClass, clazz)
)
else {
log("enclosing method for %s is %s (in %s)".format(clazz, sym, enclClass))
jclass addAttribute fjbgContext.JEnclosingMethodAttribute(
jclass,
javaName(enclClass),
javaName(sym),
javaType(sym).asInstanceOf[JMethodType]
)
}
}
}
def genBeanInfoClass(c: IClass) {
val description = c.symbol.annotations.find(_.atp.typeSymbol == BeanDescriptionAttr)
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 && !(f.symbol.name startsWith "$"))
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(ACC_PUBLIC, "<init>", JType.VOID, new Array[JType](0), new Array[String](0))
val jcode = constructor.getCode().asInstanceOf[JExtendedCode]
val strKind = new JObjectType(javaName(StringClass))
val stringArrayKind = new JArrayType(strKind)
val conType = new JMethodType(JType.VOID, Array(javaType(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()
jcode emitPUSH javaType(c.symbol).asInstanceOf[JReferenceType]
jcode emitPUSH fieldList.length
jcode emitANEWARRAY strKind
push(fieldList)
jcode emitPUSH methodList.length
jcode emitANEWARRAY strKind
push(methodList)
jcode.emitINVOKESPECIAL("scala/reflect/ScalaBeanInfo", "<init>", conType)
jcode.emitRETURN()
writeClass("BeanInfo ", beanInfoClass, c.symbol)
}
def addExceptionsAttribute(jmethod: JMethod, excs: List[AnnotationInfo]) {
if (excs.isEmpty) return
val cpool = jmethod.getConstantPool
val buf: ByteBuffer = ByteBuffer.allocate(512)
var nattr = 0
buf putShort 0xbaba.toShort
for (AnnotationInfo(tp, List(exc), _) <- excs.distinct if tp.typeSymbol == ThrowsClass) {
val Literal(const) = exc
buf.putShort(
cpool.addClass(
javaName(const.typeValue.typeSymbol)).shortValue)
nattr += 1
}
assert(nattr > 0)
buf.putShort(0, nattr.toShort)
addAttribute(jmethod, tpnme.ExceptionsATTR, buf)
}
private def shouldEmitAnnotation(annot: AnnotationInfo) =
annot.atp.typeSymbol.initialize.isJavaDefined &&
annot.atp.typeSymbol.isNonBottomSubClass(ClassfileAnnotationClass) &&
annot.args.isEmpty
private def emitJavaAnnotations(cpool: JConstantPool, buf: ByteBuffer, annotations: List[AnnotationInfo]): Int = {
def emitArgument(arg: ClassfileAnnotArg): Unit = arg match {
case LiteralAnnotArg(const) =>
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 sb@ScalaSigBytes(bytes) if !sb.isLong =>
buf put 's'.toByte
buf putShort cpool.addUtf8(sb.encodedBytes).toShort
case sb@ScalaSigBytes(bytes) if sb.isLong =>
buf put '['.toByte
val stringCount = (sb.encodedBytes.length / 65534) + 1
buf putShort stringCount.toShort
for (i <- 0 until stringCount) {
buf put 's'.toByte
val j = i * 65535
val string = sb.encodedBytes.slice(j, j + 65535)
buf putShort cpool.addUtf8(string).toShort
}
case ArrayAnnotArg(args) =>
buf put '['.toByte
buf putShort args.length.toShort
args foreach emitArgument
case NestedAnnotArg(annInfo) =>
buf put '@'.toByte
emitAnnotation(annInfo)
}
def emitAnnotation(annotInfo: AnnotationInfo) {
val AnnotationInfo(typ, args, assocs) = annotInfo
val jtype = javaType(typ)
buf putShort cpool.addUtf8(jtype.getSignature()).toShort
assert(args.isEmpty, args.toString)
buf putShort assocs.length.toShort
for ((name, value) <- assocs) {
buf putShort cpool.addUtf8(name.toString).toShort
emitArgument(value)
}
}
var nannots = 0
val pos = buf.position()
buf putShort 0xbaba.toShort
for (annot <- annotations if shouldEmitAnnotation(annot)) {
nannots += 1
emitAnnotation(annot)
}
buf.putShort(pos, nannots.toShort)
nannots
}
def isValidSignature(sym: Symbol, sig: String) = (
if (sym.isMethod) SigParser verifyMethod sig
else if (sym.isTerm) SigParser verifyType sig
else SigParser verifyClass sig
)
private def needsGenericSignature(sym: Symbol) = !(
sym.isSynthetic
|| sym.isLiftedMethod
|| sym.isBridge
|| (sym.ownerChain exists (_.isImplClass))
)
def addGenericSignature(jmember: JMember, sym: Symbol, owner: Symbol) {
if (needsGenericSignature(sym)) {
val memberTpe = atPhase(currentRun.erasurePhase)(owner.thisType.memberInfo(sym))
erasure.javaSig(sym, memberTpe) foreach { sig =>
if (settings.Xverify.value && SigParser.isParserAvailable && !isValidSignature(sym, sig)) {
clasz.cunit.warning(sym.pos,
"""|compiler bug: created invalid generic signature for %s in %s
|signature: %s
|if this is reproducible, please report bug at http://lampsvn.epfl.ch/trac/scala
""".trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig))
return
}
if ((settings.check.value contains "genjvm")) {
val normalizedTpe = atPhase(currentRun.erasurePhase)(erasure.prepareSigMap(memberTpe))
val bytecodeTpe = owner.thisType.memberInfo(sym)
if (!sym.isType && !sym.isConstructor && !(erasure.erasure(normalizedTpe) =:= bytecodeTpe)) {
clasz.cunit.warning(sym.pos,
"""|compiler bug: created generic signature for %s in %s that does not conform to its erasure
|signature: %s
|original type: %s
|normalized type: %s
|erasure type: %s
|if this is reproducible, please report bug at http://lampsvn.epfl.ch/trac/scala
""".trim.stripMargin.format(sym, sym.owner.skipPackageObject.fullName, sig, memberTpe, normalizedTpe, bytecodeTpe))
return
}
}
val index = jmember.getConstantPool.addUtf8(sig).toShort
if (opt.verboseDebug)
atPhase(currentRun.erasurePhase) {
println("add generic sig "+sym+":"+sym.info+" ==> "+sig+" @ "+index)
}
val buf = ByteBuffer.allocate(2)
buf putShort index
addAttribute(jmember, tpnme.SignatureATTR, buf)
}
}
}
def addAnnotations(jmember: JMember, annotations: List[AnnotationInfo]) {
if (annotations.exists(_.atp.typeSymbol == definitions.DeprecatedAttr)) {
val attr = jmember.getContext().JOtherAttribute(
jmember.getJClass(), jmember, tpnme.DeprecatedATTR.toString,
new Array[Byte](0), 0)
jmember addAttribute attr
}
val toEmit = annotations filter shouldEmitAnnotation
if (toEmit.isEmpty) return
val buf: ByteBuffer = ByteBuffer.allocate(2048)
emitJavaAnnotations(jmember.getConstantPool, buf, toEmit)
addAttribute(jmember, tpnme.RuntimeAnnotationATTR, buf)
}
def addParamAnnotations(jmethod: JMethod, pannotss: List[List[AnnotationInfo]]) {
val annotations = pannotss map (_ filter shouldEmitAnnotation)
if (annotations forall (_.isEmpty)) return
val buf: ByteBuffer = ByteBuffer.allocate(2048)
buf.put(annotations.length.toByte)
for (annots <- annotations)
emitJavaAnnotations(jmethod.getConstantPool, buf, annots)
addAttribute(jmethod, tpnme.RuntimeParamAnnotationATTR, buf)
}
def addAttribute(jmember: JMember, name: Name, buf: ByteBuffer) {
if (buf.position() < 2)
return
val length = buf.position()
val arr = buf.array().slice(0, length)
val attr = jmember.getContext().JOtherAttribute(jmember.getJClass(),
jmember,
name.toString,
arr,
length)
jmember addAttribute attr
}
def addInnerClasses(jclass: JClass) {
def outerName(innerSym: Symbol): String = {
if (innerSym.originalEnclosingMethod != NoSymbol)
null
else {
val outerName = javaName(innerSym.rawowner)
if (isTopLevelModule(innerSym.rawowner)) outerName stripSuffix "$"
else outerName
}
}
def innerName(innerSym: Symbol): String =
if (innerSym.isAnonymousClass || innerSym.isAnonymousFunction)
null
else
innerSym.rawname.toString
atPhase(currentRun.erasurePhase.next) {
for (sym <- List(clasz.symbol, clasz.symbol.linkedClassOfClass) ; m <- sym.info.decls ; if m.isClass)
innerClassBuffer += m
}
val allInners = innerClassBuffer.toList
if (allInners.nonEmpty) {
val innerClassesAttr = jclass.getInnerClasses()
for (innerSym <- allInners.distinct sortBy (_.name.length)) {
var flags = javaFlags(innerSym)
if (innerSym.rawowner.hasModuleFlag)
flags |= ACC_STATIC
innerClassesAttr.addEntry(
javaName(innerSym),
outerName(innerSym),
innerName(innerSym),
flags & INNER_CLASSES_FLAGS
)
}
}
}
def genField(f: IField) {
if (settings.debug.value)
log("Adding field: " + f.symbol.fullName)
val attributes = f.symbol.annotations.map(_.atp.typeSymbol).foldLeft(0) {
case (res, TransientAttr) => res | ACC_TRANSIENT
case (res, VolatileAttr) => res | ACC_VOLATILE
case (res, _) => res
}
var flags = javaFlags(f.symbol)
if (!f.symbol.isMutable)
flags |= ACC_FINAL
val jfield =
jclass.addNewField(flags | attributes,
javaName(f.symbol),
javaType(f.symbol.tpe))
addGenericSignature(jfield, f.symbol, clasz.symbol)
addAnnotations(jfield, f.symbol.annotations)
}
def genMethod(m: IMethod) {
if (m.symbol.isStaticConstructor) return
if ((m.symbol.name == nme.getClass_) && m.params.isEmpty) return
log("Generating method " + m.symbol.fullName)
method = m
endPC.clear
computeLocalVarsIndex(m)
var resTpe = javaType(m.symbol.tpe.resultType)
if (m.symbol.isClassConstructor)
resTpe = JType.VOID
var flags = javaFlags(m.symbol)
if (jclass.isInterface)
flags |= ACC_ABSTRACT
if (m.symbol.isStrictFP)
flags |= ACC_STRICT
if (method.native)
flags |= ACC_NATIVE
jmethod = jclass.addNewMethod(flags,
javaName(m.symbol),
resTpe,
mkArray(m.params map (p => javaType(p.kind))),
mkArray(m.params map (p => javaName(p.sym))))
addRemoteException(jmethod, m.symbol)
if (!jmethod.isAbstract() && !method.native) {
val jcode = jmethod.getCode().asInstanceOf[JExtendedCode]
if (emitVars && isClosureApply(method.symbol)) {
val outerField = clasz.symbol.info.decl(nme.OUTER_LOCAL)
if (outerField != NoSymbol) {
log("Adding fake local to represent outer 'this' for closure " + clasz)
val _this = new Local(
method.symbol.newVariable(NoPosition, nme.FAKE_LOCAL_THIS), toTypeKind(outerField.tpe), false)
m.locals = m.locals ::: List(_this)
computeLocalVarsIndex(m)
jcode.emitALOAD_0()
jcode.emitGETFIELD(javaName(clasz.symbol),
javaName(outerField),
javaType(outerField))
jcode.emitSTORE(indexOf(_this), javaType(_this.kind))
}
}
for (local <- m.locals if ! m.params.contains(local)) {
if (settings.debug.value)
log("add local var: " + local)
jmethod.addNewLocalVariable(javaType(local.kind), javaName(local.sym))
}
genCode(m)
if (emitVars)
genLocalVariableTable(m, jcode)
}
addGenericSignature(jmethod, m.symbol, clasz.symbol)
val (excs, others) = splitAnnotations(m.symbol.annotations, ThrowsClass)
addExceptionsAttribute(jmethod, excs)
addAnnotations(jmethod, others)
addParamAnnotations(jmethod, m.params.map(_.sym.annotations))
}
private def addRemoteException(jmethod: JMethod, meth: Symbol) {
def isRemoteThrows(ainfo: AnnotationInfo) = ainfo match {
case AnnotationInfo(tp, List(arg), _) if tp.typeSymbol == ThrowsClass =>
arg match {
case Literal(Constant(tpe: Type)) if tpe.typeSymbol == RemoteException.typeSymbol => true
case _ => false
}
case _ => false
}
if (isRemoteClass ||
(meth.hasAnnotation(RemoteAttr) && jmethod.isPublic)) {
val c = Constant(RemoteException)
val ainfo = AnnotationInfo(ThrowsClass.tpe, List(Literal(c).setType(c.tpe)), List())
if (!meth.annotations.exists(isRemoteThrows)) {
meth addAnnotation ainfo
}
}
}
private def splitAnnotations(annotations: List[AnnotationInfo], annotSym: Symbol): (List[AnnotationInfo], List[AnnotationInfo]) = {
annotations.partition { a => a match {
case AnnotationInfo(tp, _, _) if tp.typeSymbol == annotSym => true
case _ => false
}}
}
private def isClosureApply(sym: Symbol): Boolean = {
(sym.name == nme.apply) &&
sym.owner.isSynthetic &&
sym.owner.tpe.parents.exists { t =>
val TypeRef(_, sym, _) = t
FunctionClass contains sym
}
}
def addModuleInstanceField() {
jclass.addNewField(PublicStaticFinal,
nme.MODULE_INSTANCE_FIELD.toString,
jclass.getType())
}
def addStaticInit(cls: JClass, mopt: Option[IMethod]) {
val clinitMethod = cls.addNewMethod(PublicStatic,
"<clinit>",
JType.VOID,
JType.EMPTY_ARRAY,
new Array[String](0))
val clinit = clinitMethod.getCode().asInstanceOf[JExtendedCode]
mopt match {
case Some(m) =>
if (clasz.bootstrapClass.isDefined) legacyEmitBootstrapMethodInstall(clinit)
val oldLastBlock = m.code.blocks.last
val lastBlock = m.code.newBlock
oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock))
if (isStaticModule(clasz.symbol)) {
lastBlock emit NEW(REFERENCE(m.symbol.enclClass))
lastBlock emit CALL_METHOD(m.symbol.enclClass.primaryConstructor, Static(true))
}
serialVUID foreach { value =>
import Flags._, definitions._
val fieldName = "serialVersionUID"
val fieldSymbol = clasz.symbol.newValue(NoPosition, newTermName(fieldName))
.setFlag(STATIC | FINAL)
.setInfo(longType)
clasz addField new IField(fieldSymbol)
lastBlock emit CONSTANT(Constant(value))
lastBlock emit STORE_FIELD(fieldSymbol, true)
}
if (isParcelableClass)
addCreatorCode(BytecodeGenerator.this, lastBlock)
if (clasz.bootstrapClass.isDefined) {
}
lastBlock emit RETURN(UNIT)
lastBlock.close
method = m
jmethod = clinitMethod
genCode(m)
case None =>
legacyStaticInitializer(cls, clinit)
}
}
private def legacyStaticInitializer(cls: JClass, clinit: JExtendedCode) {
if (isStaticModule(clasz.symbol)) {
clinit emitNEW cls.getName()
clinit.emitINVOKESPECIAL(cls.getName(),
JMethod.INSTANCE_CONSTRUCTOR_NAME,
JMethodType.ARGLESS_VOID_FUNCTION)
}
serialVUID foreach { value =>
val fieldName = "serialVersionUID"
jclass.addNewField(PublicStaticFinal, fieldName, JType.LONG)
clinit emitPUSH value
clinit.emitPUSH(value)
clinit.emitPUTSTATIC(jclass.getName(), fieldName, JType.LONG)
}
if (isParcelableClass)
legacyAddCreatorCode(BytecodeGenerator.this, clinit)
if (clasz.bootstrapClass.isDefined)
legacyEmitBootstrapMethodInstall(clinit)
clinit.emitRETURN()
}
def legacyEmitBootstrapMethodInstall(jcode: JExtendedCode) {
jcode emitPUSH jclass.getType.asInstanceOf[JReferenceType]
jcode emitPUSH new JObjectType("scala.runtime.DynamicDispatch")
jcode emitPUSH "bootstrapInvokeDynamic"
jcode.emitGETSTATIC("java.dyn.Linkage", "BOOTSTRAP_METHOD_TYPE", MethodTypeType)
jcode.emitDUP
jcode.emitINVOKESTATIC("scala.Console", "println", new JMethodType(JType.VOID, Array(JAVA_LANG_OBJECT)))
jcode.emitINVOKESTATIC("java.dyn.MethodHandles", "findStatic",
new JMethodType(MethodHandleType, Array(JavaLangClassType, JAVA_LANG_STRING, MethodTypeType)))
jcode.emitINVOKESTATIC("java.dyn.Linkage", "registerBootstrapMethod",
new JMethodType(JType.VOID, Array(JavaLangClassType, MethodHandleType)))
}
def addForwarder(jclass: JClass, module: Symbol, m: Symbol, accessFlags: Int) {
val moduleName = javaName(module)
val methodInfo = module.thisType.memberInfo(m)
val paramJavaTypes = methodInfo.paramTypes map javaType
val paramNames = 0 until paramJavaTypes.length map ("x_" + _)
val mirrorMethod = jclass.addNewMethod(
accessFlags,
javaName(m),
javaType(methodInfo.resultType),
mkArray(paramJavaTypes),
mkArray(paramNames))
val mirrorCode = mirrorMethod.getCode().asInstanceOf[JExtendedCode]
mirrorCode.emitGETSTATIC(moduleName,
nme.MODULE_INSTANCE_FIELD.toString,
new JObjectType(moduleName))
var i = 0
var index = 0
var argTypes = mirrorMethod.getArgumentTypes()
while (i < argTypes.length) {
mirrorCode.emitLOAD(index, argTypes(i))
index += argTypes(i).getSize()
i += 1
}
mirrorCode.emitINVOKEVIRTUAL(moduleName, mirrorMethod.getName, javaType(m).asInstanceOf[JMethodType])
mirrorCode emitRETURN mirrorMethod.getReturnType()
addRemoteException(mirrorMethod, m)
if (!m.isDeferred)
addGenericSignature(mirrorMethod, m, module)
val (throws, others) = splitAnnotations(m.annotations, ThrowsClass)
addExceptionsAttribute(mirrorMethod, throws)
addAnnotations(mirrorMethod, others)
addParamAnnotations(mirrorMethod, m.info.params.map(_.annotations))
}
def addForwarders(jclass: JClass, moduleClass: Symbol) {
assert(moduleClass.isModuleClass)
if (settings.debug.value)
log("Dumping mirror class for object: " + moduleClass)
val className = jclass.getName
val linkedClass = moduleClass.companionClass
val linkedModule = linkedClass.companionSymbol
val isIncestuous = moduleClass.tpe <:< linkedClass.tpe
val accessFlags = if (isIncestuous) PublicStatic else PublicStaticFinal
def memberNames(sym: Symbol) = sym.info.members map (_.name.toString) toSet
lazy val membersInCommon =
memberNames(linkedModule) intersect memberNames(linkedClass)
def shouldForward(m: Symbol): Boolean = (
m.owner != ObjectClass
&& m.isMethod
&& m.isPublic
&& !m.hasFlag(Flags.CASE | Flags.DEFERRED | Flags.SPECIALIZED | Flags.LIFTED)
&& !m.isConstructor
&& !m.isStaticMember
&& !membersInCommon(m.name.toString)
)
for (m <- moduleClass.info.nonPrivateMembers) {
if (shouldForward(m)) {
log("Adding static forwarder for '%s' from %s to '%s'".format(m, className, moduleClass))
addForwarder(jclass, moduleClass, m, accessFlags)
}
else if (settings.debug.value) {
log("No forwarder for '%s' from %s to '%s'".format(m, className, moduleClass))
}
}
}
def dumpMirrorClass(clasz: Symbol, sourceFile: String) {
import JAccessFlags._
val moduleName = javaName(clasz)
val mirrorName = moduleName.substring(0, moduleName.length() - 1)
val mirrorClass = fjbgContext.JClass(ACC_SUPER | ACC_PUBLIC | ACC_FINAL,
mirrorName,
JAVA_LANG_OBJECT.getName,
JClass.NO_INTERFACES,
sourceFile)
log("Dumping mirror class for '%s'".format(mirrorClass.getName))
addForwarders(mirrorClass, clasz)
val ssa = scalaSignatureAddingMarker(mirrorClass, clasz.companionSymbol)
addAnnotations(mirrorClass, clasz.annotations ++ ssa)
emitClass(mirrorClass, clasz)
}
var linearization: List[BasicBlock] = Nil
var isModuleInitialized = false
def genCode(m: IMethod) {
val jcode = jmethod.getCode.asInstanceOf[JExtendedCode]
def makeLabels(bs: List[BasicBlock]) = {
if (settings.debug.value)
log("Making labels for: " + method)
mutable.HashMap(bs map (_ -> jcode.newLabel) : _*)
}
isModuleInitialized = false
linearization = linearizer.linearize(m)
val labels = makeLabels(linearization)
val varsInBlock: mutable.Set[Local] = new mutable.HashSet
var nextBlock: BasicBlock = linearization.head
def genBlocks(l: List[BasicBlock]): Unit = l match {
case Nil => ()
case x :: Nil => nextBlock = null; genBlock(x)
case x :: y :: ys => nextBlock = y; genBlock(x); genBlocks(y :: ys)
}
def genExceptionHandlers() {
def ranges(e: ExceptionHandler): List[(Int, Int)] = {
var covered = e.covered
var ranges: List[(Int, Int)] = Nil
var start = -1
var end = -1
linearization foreach { b =>
if (! (covered contains b) ) {
if (start >= 0) {
end = labels(b).getAnchor()
ranges ::= (start, end)
start = -1
}
} else {
if (start < 0)
start = labels(b).getAnchor()
end = endPC(b)
covered -= b
}
}
if (start >= 0) {
ranges ::= (start, jcode.getPC())
}
if (!covered.isEmpty)
if (settings.debug.value)
log("Some covered blocks were not found in method: " + method +
" covered: " + covered + " not in " + linearization)
ranges
}
for (e <- this.method.exh ; p <- ranges(e).sortBy(_._1)) {
if (p._1 < p._2) {
if (settings.debug.value)
log("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method +
" from: " + p._1 + " to: " + p._2 + " catching: " + e.cls);
val cls = if (e.cls == NoSymbol || e.cls == ThrowableClass) null
else javaName(e.cls)
jcode.addExceptionHandler(p._1, p._2,
labels(e.startBlock).getAnchor(),
cls)
} else
log("Empty exception range: " + p)
}
}
def genBlock(b: BasicBlock) {
labels(b).anchorToNext()
if (settings.debug.value)
log("Generating code for block: " + b + " at pc: " + labels(b).getAnchor())
var lastMappedPC = 0
var lastLineNr = 0
var crtPC = 0
varsInBlock.clear()
for (instr <- b) {
class CompilationException(msg: String) extends Exception(msg) {
override def toString: String = {
msg +
"\nCurrent method: " + method +
"\nCurrent block: " + b +
"\nCurrent instruction: " + instr +
"\n---------------------" +
method.dump
}
}
def assert(cond: Boolean, msg: String) =
if (!cond) throw new CompilationException(msg)
instr match {
case THIS(clasz) =>
jcode.emitALOAD_0()
case CONSTANT(const) =>
genConstant(jcode, const)
case LOAD_ARRAY_ITEM(kind) =>
jcode.emitALOAD(javaType(kind))
case LOAD_LOCAL(local) =>
jcode.emitLOAD(indexOf(local), javaType(local.kind))
case lf @ LOAD_FIELD(field, isStatic) =>
var owner = javaName(lf.hostClass)
if (settings.debug.value)
log("LOAD_FIELD with owner: " + owner +
" flags: " + Flags.flagsToString(field.owner.flags))
if (isStatic)
jcode.emitGETSTATIC(owner,
javaName(field),
javaType(field))
else
jcode.emitGETFIELD(owner,
javaName(field),
javaType(field))
case LOAD_MODULE(module) =>
if (settings.debug.value)
log("generating LOAD_MODULE for: " + module + " flags: " +
Flags.flagsToString(module.flags));
if (clasz.symbol == module.moduleClass && jmethod.getName() != nme.readResolve.toString)
jcode.emitALOAD_0()
else
jcode.emitGETSTATIC(javaName(module) ,
nme.MODULE_INSTANCE_FIELD.toString,
javaType(module))
case STORE_ARRAY_ITEM(kind) =>
jcode emitASTORE javaType(kind)
case STORE_LOCAL(local) =>
jcode.emitSTORE(indexOf(local), javaType(local.kind))
case STORE_THIS(_) =>
jcode.emitASTORE_0()
case STORE_FIELD(field, isStatic) =>
val owner = javaName(field.owner)
if (isStatic)
jcode.emitPUTSTATIC(owner,
javaName(field),
javaType(field))
else
jcode.emitPUTFIELD(owner,
javaName(field),
javaType(field))
case CALL_PRIMITIVE(primitive) =>
genPrimitive(primitive, instr.pos)
case call @ CALL_METHOD(definitions.Array_clone, Dynamic) =>
val target: String = javaType(call.targetTypeKind).getSignature()
jcode.emitINVOKEVIRTUAL(target, "clone", arrayCloneType)
case call @ CALL_METHOD(method, style) =>
val owner: String = javaName(method.owner)
val dynamicOwner =
if (needsInterfaceCall(call.hostClass)) owner
else javaName(call.hostClass)
val jname = javaName(method)
val jtype = javaType(method).asInstanceOf[JMethodType]
style match {
case InvokeDynamic =>
jcode.emitINVOKEINTERFACE("java.dyn.Dynamic", jname, jtype)
case Dynamic =>
if (needsInterfaceCall(method.owner))
jcode.emitINVOKEINTERFACE(owner, jname, jtype)
else
jcode.emitINVOKEVIRTUAL(dynamicOwner, jname, jtype)
case Static(instance) =>
if (instance)
jcode.emitINVOKESPECIAL(owner, jname, jtype)
else
jcode.emitINVOKESTATIC(owner, jname, jtype)
case SuperCall(_) =>
jcode.emitINVOKESPECIAL(owner, jname, jtype)
if (isStaticModule(clasz.symbol) && !isModuleInitialized &&
jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME &&
jname == JMethod.INSTANCE_CONSTRUCTOR_NAME) {
isModuleInitialized = true
jcode.emitALOAD_0()
jcode.emitPUTSTATIC(jclass.getName(),
nme.MODULE_INSTANCE_FIELD.toString,
jclass.getType())
}
}
case BOX(kind) =>
val boxedType = definitions.boxedClass(kind.toType.typeSymbol)
val mtype = new JMethodType(javaType(boxedType), Array(javaType(kind)))
jcode.emitINVOKESTATIC(BoxesRunTime, "boxTo" + boxedType.decodedName, mtype)
case UNBOX(kind) =>
val mtype = new JMethodType(javaType(kind), Array(JAVA_LANG_OBJECT))
jcode.emitINVOKESTATIC(BoxesRunTime, "unboxTo" + kind.toType.typeSymbol.decodedName, mtype)
case NEW(REFERENCE(cls)) =>
val className = javaName(cls)
jcode emitNEW className
case CREATE_ARRAY(elem, 1) => elem match {
case REFERENCE(_) | ARRAY(_) =>
jcode emitANEWARRAY javaType(elem).asInstanceOf[JReferenceType]
case _ =>
jcode emitNEWARRAY javaType(elem)
}
case CREATE_ARRAY(elem, dims) =>
jcode.emitMULTIANEWARRAY(javaType(ArrayN(elem, dims)).asInstanceOf[JReferenceType], dims)
case IS_INSTANCE(tpe) =>
tpe match {
case REFERENCE(cls) =>
jcode emitINSTANCEOF new JObjectType(javaName(cls))
case ARRAY(elem) =>
jcode emitINSTANCEOF new JArrayType(javaType(elem))
case _ =>
abort("Unknown reference type in IS_INSTANCE: " + tpe)
}
case CHECK_CAST(tpe) =>
tpe match {
case REFERENCE(cls) =>
if (cls != ObjectClass)
jcode emitCHECKCAST new JObjectType(javaName(cls))
case ARRAY(elem) =>
jcode emitCHECKCAST new JArrayType(javaType(elem))
case _ =>
abort("Unknown reference type in IS_INSTANCE: " + tpe)
}
case SWITCH(tags, branches) =>
val tagArray = new Array[Array[Int]](tags.length)
var caze = tags
var i = 0
while (i < tagArray.length) {
tagArray(i) = new Array[Int](caze.head.length)
caze.head.copyToArray(tagArray(i), 0)
i += 1
caze = caze.tail
}
val branchArray = jcode.newLabels(tagArray.length)
i = 0
while (i < branchArray.length) {
branchArray(i) = labels(branches(i))
i += 1
}
if (settings.debug.value)
log("Emitting SWITCH:\ntags: " + tags + "\nbranches: " + branches)
jcode.emitSWITCH(tagArray,
branchArray,
labels(branches.last),
MIN_SWITCH_DENSITY)
()
case JUMP(whereto) =>
if (nextBlock != whereto)
jcode.emitGOTO_maybe_W(labels(whereto), false)
case CJUMP(success, failure, cond, kind) =>
kind match {
case BOOL | BYTE | CHAR | SHORT | INT =>
if (nextBlock == success) {
jcode.emitIF_ICMP(conds(negate(cond)), labels(failure))
} else {
jcode.emitIF_ICMP(conds(cond), labels(success))
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
case REFERENCE(_) | ARRAY(_) =>
if (nextBlock == success) {
jcode.emitIF_ACMP(conds(negate(cond)), labels(failure))
} else {
jcode.emitIF_ACMP(conds(cond), labels(success))
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
case _ =>
(kind: @unchecked) match {
case LONG => jcode.emitLCMP()
case FLOAT =>
if (cond == LT || cond == LE) jcode.emitFCMPG()
else jcode.emitFCMPL()
case DOUBLE =>
if (cond == LT || cond == LE) jcode.emitDCMPG()
else jcode.emitDCMPL()
}
if (nextBlock == success) {
jcode.emitIF(conds(negate(cond)), labels(failure))
} else {
jcode.emitIF(conds(cond), labels(success));
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
}
case CZJUMP(success, failure, cond, kind) =>
kind match {
case BOOL | BYTE | CHAR | SHORT | INT =>
if (nextBlock == success) {
jcode.emitIF(conds(negate(cond)), labels(failure))
} else {
jcode.emitIF(conds(cond), labels(success))
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
case REFERENCE(_) | ARRAY(_) =>
val Success = success
val Failure = failure
(cond, nextBlock) match {
case (EQ, Success) =>
jcode emitIFNONNULL labels(failure)
case (NE, Failure) =>
jcode emitIFNONNULL labels(success)
case (EQ, Failure) =>
jcode emitIFNULL labels(success)
case (NE, Success) =>
jcode emitIFNULL labels(failure)
case (EQ, _) =>
jcode emitIFNULL labels(success)
jcode.emitGOTO_maybe_W(labels(failure), false)
case (NE, _) =>
jcode emitIFNONNULL labels(success)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
case _ =>
(kind: @unchecked) match {
case LONG =>
jcode.emitLCONST_0(); jcode.emitLCMP()
case FLOAT =>
jcode.emitFCONST_0()
if (cond == LT || cond == LE) jcode.emitFCMPG()
else jcode.emitFCMPL()
case DOUBLE =>
jcode.emitDCONST_0()
if (cond == LT || cond == LE) jcode.emitDCMPG()
else jcode.emitDCMPL()
}
if (nextBlock == success) {
jcode.emitIF(conds(negate(cond)), labels(failure))
} else {
jcode.emitIF(conds(cond), labels(success))
if (nextBlock != failure)
jcode.emitGOTO_maybe_W(labels(failure), false)
}
}
case RETURN(kind) =>
jcode emitRETURN javaType(kind)
case THROW(_) =>
jcode.emitATHROW()
case DROP(kind) =>
kind match {
case LONG | DOUBLE => jcode.emitPOP2()
case _ => jcode.emitPOP()
}
case DUP(kind) =>
kind match {
case LONG | DOUBLE => jcode.emitDUP2()
case _ => jcode.emitDUP()
}
case MONITOR_ENTER() =>
jcode.emitMONITORENTER()
case MONITOR_EXIT() =>
jcode.emitMONITOREXIT()
case SCOPE_ENTER(lv) =>
varsInBlock += lv
lv.start = jcode.getPC()
case SCOPE_EXIT(lv) =>
if (varsInBlock(lv)) {
lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges
varsInBlock -= lv
}
else if (b.varsInScope(lv)) {
lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges
b.varsInScope -= lv
} else
assert(false, "Illegal local var nesting: " + method)
case LOAD_EXCEPTION(_) =>
()
}
crtPC = jcode.getPC()
val crtLine = try {
if (instr.pos == NoPosition) lastLineNr else (instr.pos).line
} catch {
case _: UnsupportedOperationException =>
log("Warning: wrong position in: " + method)
lastLineNr
}
if (b.lastInstruction == instr)
endPC(b) = jcode.getPC()
if (crtPC > lastMappedPC) {
jcode.completeLineNumber(lastMappedPC, crtPC, crtLine)
lastMappedPC = crtPC
lastLineNr = crtLine
}
}
for (lv <- varsInBlock) {
lv.ranges = (lv.start, jcode.getPC()) :: lv.ranges
}
for (lv <- b.varsInScope) {
lv.ranges = (labels(b).getAnchor(), jcode.getPC()) :: lv.ranges
}
}
def genPrimitive(primitive: Primitive, pos: Position) {
primitive match {
case Negation(kind) =>
kind match {
case BOOL | BYTE | CHAR | SHORT | INT =>
jcode.emitINEG()
case LONG => jcode.emitLNEG()
case FLOAT => jcode.emitFNEG()
case DOUBLE => jcode.emitDNEG()
case _ => abort("Impossible to negate a " + kind)
}
case Arithmetic(op, kind) =>
op match {
case ADD => jcode.emitADD(javaType(kind))
case SUB =>
(kind: @unchecked) match {
case BOOL | BYTE | CHAR | SHORT | INT =>
jcode.emitISUB()
case LONG => jcode.emitLSUB()
case FLOAT => jcode.emitFSUB()
case DOUBLE => jcode.emitDSUB()
}
case MUL =>
(kind: @unchecked) match {
case BOOL | BYTE | CHAR | SHORT | INT =>
jcode.emitIMUL()
case LONG => jcode.emitLMUL()
case FLOAT => jcode.emitFMUL()
case DOUBLE => jcode.emitDMUL()
}
case DIV =>
(kind: @unchecked) match {
case BOOL | BYTE | CHAR | SHORT | INT =>
jcode.emitIDIV()
case LONG => jcode.emitLDIV()
case FLOAT => jcode.emitFDIV()
case DOUBLE => jcode.emitDDIV()
}
case REM =>
(kind: @unchecked) match {
case BOOL | BYTE | CHAR | SHORT | INT =>
jcode.emitIREM()
case LONG => jcode.emitLREM()
case FLOAT => jcode.emitFREM()
case DOUBLE => jcode.emitDREM()
}
case NOT =>
kind match {
case BOOL | BYTE | CHAR | SHORT | INT =>
jcode.emitPUSH(-1)
jcode.emitIXOR()
case LONG =>
jcode.emitPUSH(-1l)
jcode.emitLXOR()
case _ =>
abort("Impossible to negate an " + kind)
}
case _ =>
abort("Unknown arithmetic primitive " + primitive)
}
case Logical(op, kind) => (op, kind) match {
case (AND, LONG) =>
jcode.emitLAND()
case (AND, INT) =>
jcode.emitIAND()
case (AND, _) =>
jcode.emitIAND()
if (kind != BOOL)
jcode.emitT2T(javaType(INT), javaType(kind));
case (OR, LONG) =>
jcode.emitLOR()
case (OR, INT) =>
jcode.emitIOR()
case (OR, _) =>
jcode.emitIOR()
if (kind != BOOL)
jcode.emitT2T(javaType(INT), javaType(kind));
case (XOR, LONG) =>
jcode.emitLXOR()
case (XOR, INT) =>
jcode.emitIXOR()
case (XOR, _) =>
jcode.emitIXOR()
if (kind != BOOL)
jcode.emitT2T(javaType(INT), javaType(kind));
}
case Shift(op, kind) => (op, kind) match {
case (LSL, LONG) =>
jcode.emitLSHL()
case (LSL, INT) =>
jcode.emitISHL()
case (LSL, _) =>
jcode.emitISHL()
jcode.emitT2T(javaType(INT), javaType(kind))
case (ASR, LONG) =>
jcode.emitLSHR()
case (ASR, INT) =>
jcode.emitISHR()
case (ASR, _) =>
jcode.emitISHR()
jcode.emitT2T(javaType(INT), javaType(kind))
case (LSR, LONG) =>
jcode.emitLUSHR()
case (LSR, INT) =>
jcode.emitIUSHR()
case (LSR, _) =>
jcode.emitIUSHR()
jcode.emitT2T(javaType(INT), javaType(kind))
}
case Comparison(op, kind) => ((op, kind): @unchecked) match {
case (CMP, LONG) => jcode.emitLCMP()
case (CMPL, FLOAT) => jcode.emitFCMPL()
case (CMPG, FLOAT) => jcode.emitFCMPG()
case (CMPL, DOUBLE) => jcode.emitDCMPL()
case (CMPG, DOUBLE) => jcode.emitDCMPL()
}
case Conversion(src, dst) =>
if (settings.debug.value)
log("Converting from: " + src + " to: " + dst)
if (dst == BOOL) {
println("Illegal conversion at: " + clasz +
" at: " + pos.source + ":" + pos.line)
} else
jcode.emitT2T(javaType(src), javaType(dst))
case ArrayLength(_) =>
jcode.emitARRAYLENGTH()
case StartConcat =>
jcode emitNEW StringBuilderClassName
jcode.emitDUP()
jcode.emitINVOKESPECIAL(StringBuilderClassName,
JMethod.INSTANCE_CONSTRUCTOR_NAME,
JMethodType.ARGLESS_VOID_FUNCTION)
case StringConcat(el) =>
val jtype = el match {
case REFERENCE(_) | ARRAY(_) => JAVA_LANG_OBJECT
case _ => javaType(el)
}
jcode.emitINVOKEVIRTUAL(StringBuilderClassName,
"append",
new JMethodType(StringBuilderType,
Array(jtype)))
case EndConcat =>
jcode.emitINVOKEVIRTUAL(StringBuilderClassName,
"toString",
toStringType)
case _ =>
abort("Unimplemented primitive " + primitive)
}
}
genBlocks(linearization)
if (this.method.exh != Nil)
genExceptionHandlers;
}
private def genLocalVariableTable(m: IMethod, jcode: JCode) {
val vars = m.locals filterNot (_.sym.isSynthetic)
if (vars.isEmpty) return
val pool = jclass.getConstantPool
val pc = jcode.getPC()
var anonCounter = 0
var entries = 0
vars.foreach { lv =>
lv.ranges = mergeEntries(lv.ranges.reverse);
entries += lv.ranges.length
}
if (!jmethod.isStatic()) entries += 1
val lvTab = ByteBuffer.allocate(2 + 10 * entries)
def emitEntry(name: String, signature: String, idx: Short, start: Short, end: Short) {
lvTab putShort start
lvTab putShort end
lvTab putShort pool.addUtf8(name).toShort
lvTab putShort pool.addUtf8(signature).toShort
lvTab putShort idx
}
lvTab.putShort(entries.toShort)
if (!jmethod.isStatic()) {
emitEntry("this", jclass.getType().getSignature(), 0, 0.toShort, pc.toShort)
}
for (lv <- vars) {
val name = if (javaName(lv.sym) eq null) {
anonCounter += 1
"<anon" + anonCounter + ">"
} else javaName(lv.sym)
val index = indexOf(lv).toShort
val tpe = javaType(lv.kind).getSignature()
for ((start, end) <- lv.ranges) {
emitEntry(name, tpe, index, start.toShort, (end - start).toShort)
}
}
val attr =
fjbgContext.JOtherAttribute(jclass,
jcode,
tpnme.LocalVariableTableATTR.toString,
lvTab.array())
jcode addAttribute attr
}
val endPC = new mutable.HashMap[BasicBlock, Int]
def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe))
def sizeOf(k: TypeKind): Int = k match {
case DOUBLE | LONG => 2
case _ => 1
}
def indexOf(m: IMethod, sym: Symbol): Int = {
val Some(local) = m lookupLocal sym
indexOf(local)
}
def indexOf(local: Local): Int = {
assert(local.index >= 0,
"Invalid index for: " + local + "{" + local.## + "}: ")
local.index
}
def computeLocalVarsIndex(m: IMethod) {
var idx = 1
if (m.symbol.isStaticMember)
idx = 0;
for (l <- m.locals) {
if (settings.debug.value)
log("Index value for " + l + "{" + l.## + "}: " + idx)
l.index = idx
idx += sizeOf(l.kind)
}
}
def needsInterfaceCall(sym: Symbol): Boolean = {
log("checking for interface call: " + sym.fullName)
if (sym.isTrait) sym.info
sym.isInterface ||
(sym.isJavaDefined && sym.isNonBottomSubClass(ClassfileAnnotationClass))
}
private def mergeEntries(ranges: List[(Int, Int)]): List[(Int, Int)] =
(ranges.foldLeft(Nil: List[(Int, Int)]) { (collapsed: List[(Int, Int)], p: (Int, Int)) => (collapsed, p) match {
case (Nil, _) => List(p)
case ((s1, e1) :: rest, (s2, e2)) if (e1 == s2) => (s1, e2) :: rest
case _ => p :: collapsed
}}).reverse
def assert(cond: Boolean, msg: => String) = if (!cond) {
method.dump
abort(msg + "\nMethod: " + method)
}
def assert(cond: Boolean) { assert(cond, "Assertion failed.") }
}
def javaFlags(sym: Symbol): Int = {
def mkFlags(args: Int*) = args.foldLeft(0)(_ | _)
val isConsideredPrivate =
sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModule(sym.owner))
mkFlags(
if (isConsideredPrivate) ACC_PRIVATE else ACC_PUBLIC,
if (sym.isDeferred || sym.hasAbstractFlag) ACC_ABSTRACT else 0,
if (sym.isInterface) ACC_INTERFACE else 0,
if (sym.isFinal && !sym.enclClass.isInterface && !sym.isClassConstructor) ACC_FINAL else 0,
if (sym.isStaticMember) ACC_STATIC else 0,
if (sym.isBridge || sym.hasFlag(Flags.MIXEDIN) && sym.isMethod) ACC_BRIDGE else 0,
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
if (sym.isVarargsMethod) ACC_VARARGS else 0
)
}
def isTopLevelModule(sym: Symbol): Boolean =
atPhase (currentRun.picklerPhase.next) {
sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
}
def isStaticModule(sym: Symbol): Boolean = {
sym.isModuleClass && !sym.isImplClass && !sym.isLifted
}
}