diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala | 1054 |
1 files changed, 1054 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala new file mode 100644 index 0000000000..50a86fdf27 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala @@ -0,0 +1,1054 @@ +/* NSC -- new scala compiler + * Copyright 2005 LAMP/EPFL + * @author Martin Odersky + */ + +// $Id$ + +package scala.tools.nsc.backend.jvm; + +import java.io.File; + +import scala.collection.mutable.{Map, HashMap}; +import scala.tools.nsc.symtab._; +import scala.tools.nsc.util.Position; + +import ch.epfl.lamp.fjbg._; + +/** + */ +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 GlobalPhase(prev) { + def name = phaseName; + override def newFlags = phaseNewFlags; + + override def erasedTypes = true; + val codeGenerator = new BytecodeGenerator; + + override def run: Unit = + classes foreach codeGenerator.genClass; + + override def apply(unit: CompilationUnit): Unit = + abort("JVM works on icode classes, not on compilation units!"); + } + + /** + * Java bytecode generator. + * + */ + class BytecodeGenerator { + val MIN_SWITCH_DENSITY = 0.7; + val MODULE_INSTANCE_NAME = "MODULE$"; + val JAVA_LANG_STRINGBUFFER = "java.lang.StringBuffer"; + val JAVA_RMI_REMOTEEXCEPTION = "java.rmi.RemoteException"; + + val stringBufferType = new JObjectType(JAVA_LANG_STRINGBUFFER); + val toStringType = new JMethodType(JObjectType.JAVA_LANG_STRING, JType.EMPTY_ARRAY); + + // Scala attributes + val SerializableAttr = definitions.SerializableAttr; + val SerialVersionUID = definitions.getClass("scala.SerialVersionUID").tpe; + val CloneableAttr = definitions.getClass("scala.cloneable").tpe; + val TransientAtt = definitions.getClass("scala.transient").tpe; + val VolatileAttr = definitions.getClass("scala.volatile").tpe; + val RemoteAttr = definitions.getClass("scala.remote").tpe; + + val CloneableClass = definitions.getClass("java.lang.Cloneable"); + val RemoteInterface = definitions.getClass("java.rmi.Remote"); + + var clasz: IClass = _; + var method: IMethod = _; + var code: Code = _; + var jclass: JClass = _; + var jmethod: JMethod = _; + var jcode: JExtendedCode = _; + + val fjbgContext = new FJBGContext(); + + def emitClass(jclass: JClass, sym: Symbol): Unit = { + 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); + 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("$")) + addScalaAttr(if (isTopLevelModule(sym)) sym.sourceModule else sym); + val outfile = getFile(jclass, ".class"); + jclass.writeTo(outfile); + val file = scala.tools.util.AbstractFile.getFile(outfile); + informProgress("wrote " + outfile + " " + (if (file != null) "" + file.getFile() + " " + file.getFile().exists() else "no file")); + } + + var serialVUID: Option[Long] = None; + var remoteClass: Boolean = false; + + def genClass(c: IClass): Unit = { + if (settings.debug.value) + log("Generating class " + c.symbol + " flags: " + Flags.flagsToString(c.symbol.flags)); + clasz = c; + 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; + + c.symbol.attributes foreach { a => a match { + case Pair(SerializableAttr, _) => + parents = parents ::: List(definitions.SerializableClass.tpe); + case Pair(CloneableAttr, _) => + parents = parents ::: List(CloneableClass.tpe); + case Pair(SerialVersionUID, value :: _) => + serialVUID = Some(value.longValue); + case Pair(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.symbol)).copyToArray(ifaces, 0); + () + } + + jclass = fjbgContext.JClass(javaFlags(c.symbol), + name, + javaName(parents(0).symbol), + ifaces, + c.cunit.source.toString()); + + if (isTopLevelModule(c.symbol) || serialVUID != None) { + if (isTopLevelModule(c.symbol)) + addModuleInstanceField; + addStaticInit(jclass); + + if (c.symbol.linkedClass != NoSymbol) + log("No mirror class for module with linked class: " + c.symbol.fullNameString); + else + dumpMirrorClass; + } + + clasz.fields foreach genField; + clasz.methods foreach genMethod; + + emitClass(jclass, c.symbol) + } + + def isTopLevelModule(sym: Symbol): Boolean = { + sym.isModuleClass && !sym.isImplClass && !sym.hasFlag(Flags.LIFTED) /* && !atPhase(currentRun.erasurePhase)(sym.isNestedClass) */ + } + + def genField(f: IField): Unit = { + if (settings.debug.value) + log("Adding field: " + f.symbol.fullNameString); + var attributes = 0; + + f.symbol.attributes foreach { a => a match { + case Pair(TransientAtt, _) => attributes = attributes | JAccessFlags.ACC_TRANSIENT; + case Pair(VolatileAttr, _) => attributes = attributes | JAccessFlags.ACC_VOLATILE; + case _ => (); + }} + jclass.addNewField(javaFlags(f.symbol) | attributes, + javaName(f.symbol), + javaType(toTypeKind(f.symbol.tpe))); + } + + def genMethod(m: IMethod): Unit = { + if (settings.debug.value) + log("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) + + " owner: " + m.symbol.owner); + method = m; + endPC.clear; + computeLocalVarsIndex(m); + + var resTpe = javaType(toTypeKind(m.symbol.tpe.resultType)); + if (m.symbol.isClassConstructor) + resTpe = JType.VOID; + + var flags = javaFlags(m.symbol); + if (jclass.isInterface()) + flags = flags | JAccessFlags.ACC_ABSTRACT; + + jmethod = jclass.addNewMethod(flags, + javaName(m.symbol), + resTpe, + javaTypes(m.params map (.kind)), + javaNames(m.params map (.sym))); + + if (m.symbol.hasFlag(Flags.BRIDGE)) + jmethod.addAttribute(fjbgContext.JOtherAttribute(jclass, jmethod, "Bridge", + new Array[Byte](0))); + if (remoteClass || (m.symbol.attributes contains Pair(RemoteAttr, Nil))) + if (jmethod.isPublic()) { + // (see http://java.sun.com/docs/books/vmspec/html/ClassFile.doc.html#3129) + // Exceptions_attribute { + // .. + // u2 number_of_exceptions; + // u2 exception_index_table[number_of_exceptions]; + // } + val cp = jclass.getConstantPool(); + val reInx = cp.addClass(JAVA_RMI_REMOTEEXCEPTION); + val contents = java.nio.ByteBuffer.allocate(4); // u2 + u2[1] + contents.putShort(1.asInstanceOf[Short]); + contents.putShort(reInx.asInstanceOf[Short]); + if (settings.debug.value) + log("adding 'Exceptions_attribute' " + contents + " for remote method " + method); + jmethod.addAttribute( + fjbgContext.JOtherAttribute(jclass, jmethod, "Exceptions", contents.array())); + } + + if (!jmethod.isAbstract()) { + for (val local <- m.locals; (! m.params.contains(local))) { + if (settings.debug.value) + log("add local var: " + local); + jmethod.addNewLocalVariable(javaType(local.kind), javaName(local.sym)); + } + + jcode = jmethod.getCode().asInstanceOf[JExtendedCode]; + genCode(m); + genLocalVariableTable; + } + } + + def addModuleInstanceField: Unit = { + import JAccessFlags._; + jclass.addNewField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, + MODULE_INSTANCE_NAME, + jclass.getType()); + } + + def addStaticInit(cls: JClass): Unit = { + import JAccessFlags._; + val clinitMethod = cls.addNewMethod(ACC_PUBLIC | ACC_STATIC, + "<clinit>", + JType.VOID, + JType.EMPTY_ARRAY, + new Array[String](0)); + val clinit = clinitMethod.getCode().asInstanceOf[JExtendedCode]; + if (isTopLevelModule(clasz.symbol)) { + clinit.emitNEW(cls.getName()); + clinit.emitDUP(); + clinit.emitINVOKESPECIAL(cls.getName(), + JMethod.INSTANCE_CONSTRUCTOR_NAME, + JMethodType.ARGLESS_VOID_FUNCTION); + } + + serialVUID match { + case Some(value) => + val fieldName = "serialVersionUID"; + jclass.addNewField(JAccessFlags.ACC_STATIC | JAccessFlags.ACC_PUBLIC, + fieldName, + JType.LONG); + clinit.emitPUSH(value); + clinit.emitPUTSTATIC(jclass.getName(), fieldName, JType.LONG); + case None => (); + } + + clinit.emitRETURN(); + } + + def dumpMirrorClass: Unit = { + import JAccessFlags._; + assert(clasz.symbol.isModuleClass); + if (settings.debug.value) + log("Dumping mirror class for object: " + clasz); + val moduleName = javaName(clasz.symbol); // + "$"; + val mirrorName = moduleName.substring(0, moduleName.length() - 1); + val mirrorClass = fjbgContext.JClass(ACC_SUPER | ACC_PUBLIC | ACC_FINAL, + mirrorName, + "java.lang.Object", + JClass.NO_INTERFACES, + clasz.cunit.source.toString()); + for (val m <- clasz.methods; !(m.symbol.hasFlag(Flags.PRIVATE)) && !m.symbol.isClassConstructor && !isStaticSymbol(m.symbol) ) { + val mirrorMethod = mirrorClass.addNewMethod(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, + javaName(m.symbol), + javaType(toTypeKind(m.symbol.tpe.resultType)), + javaTypes(m.params map (.kind)), + javaNames(m.params map (.sym))); + val mirrorCode = mirrorMethod.getCode().asInstanceOf[JExtendedCode]; + mirrorCode.emitGETSTATIC(moduleName, + MODULE_INSTANCE_NAME, + new JObjectType(moduleName)); + var i = 0; + var index = 0; + var argTypes = mirrorMethod.getArgumentTypes(); + while (i < argTypes.length) { + mirrorCode.emitLOAD(index, argTypes(i)); + index = index + argTypes(i).getSize(); + i = i + 1; + } + + mirrorCode.emitINVOKEVIRTUAL(moduleName, mirrorMethod.getName(), mirrorMethod.getType().asInstanceOf[JMethodType]); + mirrorCode.emitRETURN(mirrorMethod.getReturnType()); + } + emitClass(mirrorClass, clasz.symbol); + } + + + val linearizer = new NormalLinearizer(); + var linearization: List[BasicBlock] = Nil; + + var isModuleInitialized = false; + def genCode(m: IMethod): Unit = { + labels.clear; + isModuleInitialized = false; + + code = m.code; + linearization = linearizer.linearize(m); + makeLabels(linearization); + genBlocks(linearization); + + if (this.method.exh != Nil) + genExceptionHandlers; + } + + var nextBlock: BasicBlock = _; + + 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); + } + + + /** Generate exception handlers for the current method. */ + def genExceptionHandlers: Unit = { + + def ranges(e: ExceptionHandler): List[Pair[Int, Int]] = { + var covered = e.covered; + var ranges: List[Pair[Int, Int]] = Nil; + var start = -1; + var end = -1; + + linearization foreach ((b) => { + if (! (covered contains b) ) { + if (start >= 0) { // we're inside a handler range + end = labels(b).getAnchor(); + ranges = Pair(start, end) :: ranges; + start = -1; + } + } else { + if (start >= 0) { // we're inside a handler range + end = endPC(b); + } else { + start = labels(b).getAnchor(); + end = endPC(b); + } + covered = covered remove b.==; + } + }); + + if (start >= 0) { + ranges = Pair(start, end) :: ranges; + } + + if (covered != Nil) + if (settings.debug.value) + log("Some covered blocks were not found in method: " + method + + " covered: " + covered + " not in " + linearization); + ranges + } + + this.method.exh foreach ((e) => { + ranges(e).sort({ (p1, p2) => p1._1 < p2._1 }) + .foreach ((p) => { + if (settings.debug.value) + log("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method + + " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls); + jcode.addExceptionHandler(p._1, p._2, + labels(e.startBlock).getAnchor(), + if (e.cls == NoSymbol) + null + else javaName(e.cls)) + }) + }); + } + + def genBlock(b: BasicBlock): Unit = { + 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; + + b traverse ( instr => { + if (b.lastInstruction == instr) + endPC(b) = jcode.getPC(); + + instr match { + case THIS(clasz) => + jcode.emitALOAD_0(); + + case CONSTANT(const) => + const.tag match { + case UnitTag => (); + case BooleanTag => jcode.emitPUSH(const.booleanValue); + case ByteTag => jcode.emitPUSH(const.byteValue); + case ShortTag => jcode.emitPUSH(const.shortValue); + case CharTag => jcode.emitPUSH(const.charValue); + case IntTag => jcode.emitPUSH(const.intValue); + case LongTag => jcode.emitPUSH(const.longValue); + case FloatTag => jcode.emitPUSH(const.floatValue); + case DoubleTag => jcode.emitPUSH(const.doubleValue); + case StringTag => jcode.emitPUSH(const.stringValue); + case NullTag => jcode.emitACONST_NULL(); + case _ => abort("Unknown constant value: " + const); + } + + case LOAD_ARRAY_ITEM(kind) => + jcode.emitALOAD(javaType(kind)); + + case LOAD_LOCAL(local, isArg) => + if (isArg) + jcode.emitLOAD(indexOf(local), javaType(local.kind)); + else + jcode.emitLOAD(indexOf(local), javaType(local.kind)); + + case LOAD_FIELD(field, isStatic) => + var owner = javaName(field.owner); +// if (field.owner.hasFlag(Flags.MODULE)) owner = owner + "$"; + 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) => + assert(module.isModule || module.isModuleClass, "Expected module: " + module); + if (settings.debug.value) + log("genearting LOAD_MODULE for: " + module + " flags: " + + Flags.flagsToString(module.flags)); + jcode.emitGETSTATIC(javaName(module) /* + "$" */ , + MODULE_INSTANCE_NAME, + javaType(module)); + + case STORE_ARRAY_ITEM(kind) => + jcode.emitASTORE(javaType(kind)); + + case STORE_LOCAL(local, isArg) => + if (isArg) + jcode.emitSTORE(indexOf(local), javaType(local.kind)); + else + jcode.emitSTORE(indexOf(local), javaType(local.kind)); + + case STORE_FIELD(field, isStatic) => + val owner = javaName(field.owner); // + (if (field.owner.hasFlag(Flags.MODULE)) "$" else ""); + 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); + + // TODO: reference the type of the receiver instead of the + // method owner. + case CALL_METHOD(method, style) => + val owner = javaName(method.owner); // + (if (method.owner.isModuleClass) "$" else ""); + + style match { + case Dynamic => + if (method.owner.hasFlag(Flags.INTERFACE)) + jcode.emitINVOKEINTERFACE(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]) + else + jcode.emitINVOKEVIRTUAL(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]); + + case Static(instance) => + if (instance) { + jcode.emitINVOKESPECIAL(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]); + } else + jcode.emitINVOKESTATIC(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]); + + case SuperCall(_) => + jcode.emitINVOKESPECIAL(owner, + javaName(method), + javaType(method).asInstanceOf[JMethodType]); + // we initialize the MODULE$ field immediately after the super ctor + if (isTopLevelModule(clasz.symbol) && !isModuleInitialized && + jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME && + javaName(method) == JMethod.INSTANCE_CONSTRUCTOR_NAME) { + isModuleInitialized = true; + jcode.emitALOAD_0(); + jcode.emitPUTSTATIC(jclass.getName(), + MODULE_INSTANCE_NAME, + jclass.getType()); + } + + + } + + case NEW(REFERENCE(cls)) => + val className = javaName(cls); + jcode.emitNEW(className); + + case CREATE_ARRAY(elem) => elem match { + case REFERENCE(_) | ARRAY(_) => + jcode.emitANEWARRAY(javaType(elem).asInstanceOf[JReferenceType]); + case _ => + jcode.emitNEWARRAY(javaType(elem)); + } + + 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) => 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 = i + 1; + caze = caze.tail; + } + val branchArray = new Array[JCode$Label](tagArray.length); + if (settings.debug.value) + log("Emitting SWITHCH:\ntags: " + tags + "\nbranches: " + branches); + jcode.emitSWITCH(tagArray, + (branches map labels dropRight 1).copyToArray(branchArray, 0), + labels(branches.last), + MIN_SWITCH_DENSITY); + + case JUMP(where) => + if (nextBlock != where) + jcode.emitGOTO_maybe_W(labels(where), false); // default to short jumps + + 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)); + // .. and fall through to success label + } 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)); + // .. and fall through to success label + } else { + jcode.emitIF_ACMP(conds(cond), labels(success)); + if (nextBlock != failure) + jcode.emitGOTO_maybe_W(labels(failure), false); + } + + case _ => + kind match { + case LONG => jcode.emitLCMP(); + case FLOAT => jcode.emitFCMPG(); + case DOUBLE => jcode.emitDCMPG(); + } + if (nextBlock == success) { + jcode.emitIF(conds(negate(cond)), labels(failure)); + // .. and fall through to success label + } 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(_) => + if (nextBlock == success) { + jcode.emitIFNONNULL(labels(failure)); + } else { + jcode.emitIFNULL(labels(success)); + if (nextBlock != failure) + jcode.emitGOTO_maybe_W(labels(failure), false); + } + + case _ => + kind match { + case LONG => jcode.emitLCONST_0(); jcode.emitLCMP(); + case FLOAT => jcode.emitFCONST_0(); jcode.emitFCMPL(); + case DOUBLE => jcode.emitDCONST_0(); 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(); + } + + crtPC = jcode.getPC(); + val crtLine = try { clasz.cunit.position(instr.pos).line; } catch { + case _: Error => lastLineNr; + } + //System.err.println("CRTLINE: " + instr.pos + " " + + // /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine); + + if (crtPC > lastMappedPC) { + jcode.completeLineNumber(lastMappedPC, crtPC, crtLine); + lastMappedPC = crtPC; + lastLineNr = crtLine; + } + + }); + } + + + def genPrimitive(primitive: Primitive, pos: Int): Unit = { + 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 match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitISUB(); + case LONG => jcode.emitLSUB(); + case FLOAT => jcode.emitFSUB(); + case DOUBLE => jcode.emitDSUB(); + } + + case MUL => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitIMUL(); + case LONG => jcode.emitLMUL(); + case FLOAT => jcode.emitFMUL(); + case DOUBLE => jcode.emitDMUL(); + } + + case DIV => + kind match { + case BOOL | BYTE | CHAR | SHORT | INT => + jcode.emitIDIV(); + case LONG => jcode.emitLDIV(); + case FLOAT => jcode.emitFDIV(); + case DOUBLE => jcode.emitDDIV(); + } + + case REM => + kind 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) => Pair(op, kind) match { + case Pair(AND, LONG) => + jcode.emitLAND(); + case Pair(AND, INT) => + jcode.emitIAND(); + case Pair(AND, _) => + jcode.emitIAND(); + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + + case Pair(OR, LONG) => + jcode.emitLOR(); + case Pair(OR, INT) => + jcode.emitIOR(); + case Pair(OR, _) => + jcode.emitIOR(); + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + + case Pair(XOR, LONG) => + jcode.emitLXOR(); + case Pair(XOR, INT) => + jcode.emitIXOR(); + case Pair(XOR, _) => + jcode.emitIXOR(); + if (kind != BOOL) + jcode.emitT2T(javaType(INT), javaType(kind)); + } + + case Shift(op, kind) => Pair(op, kind) match { + case Pair(LSL, LONG) => + jcode.emitLSHL(); + case Pair(LSL, INT) => + jcode.emitISHL(); + case Pair(LSL, _) => + jcode.emitISHL(); + jcode.emitT2T(javaType(INT), javaType(kind)); + + case Pair(ASR, LONG) => + jcode.emitLSHR(); + case Pair(ASR, INT) => + jcode.emitISHR(); + case Pair(ASR, _) => + jcode.emitISHR(); + jcode.emitT2T(javaType(INT), javaType(kind)); + + case Pair(LSR, LONG) => + jcode.emitLUSHR(); + case Pair(LSR, INT) => + jcode.emitIUSHR(); + case Pair(LSR, _) => + jcode.emitIUSHR(); + jcode.emitT2T(javaType(INT), javaType(kind)); + } + + case Conversion(src, dst) => + if (settings.debug.value) + log("Converting from: " + src + " to: " + dst); + if (dst == BOOL) { + Console.println("Illegal conversion at: " + clasz + + " at: " + method.sourceFile + ":" + Position.line(clasz.cunit.source, pos)); + } else + jcode.emitT2T(javaType(src), javaType(dst)); + + case ArrayLength(_) => + jcode.emitARRAYLENGTH(); + + case StartConcat => + jcode.emitNEW(JAVA_LANG_STRINGBUFFER); + jcode.emitDUP(); + jcode.emitINVOKESPECIAL(JAVA_LANG_STRINGBUFFER, + JMethod.INSTANCE_CONSTRUCTOR_NAME, + JMethodType.ARGLESS_VOID_FUNCTION); + + case StringConcat(el) => + val jtype = el match { + case REFERENCE(_) | ARRAY(_)=> JObjectType.JAVA_LANG_OBJECT; + case _ => javaType(el); + } + jcode.emitINVOKEVIRTUAL(JAVA_LANG_STRINGBUFFER, + "append", + new JMethodType(stringBufferType, + Predef.Array(jtype))); + case EndConcat => + jcode.emitINVOKEVIRTUAL(JAVA_LANG_STRINGBUFFER, + "toString", + toStringType); + + case _ => abort("Unimplemented primitive " + primitive); + } + } + + val endPC: HashMap[BasicBlock, Int] = new HashMap(); + val labels: HashMap[BasicBlock, JCode$Label] = new HashMap(); + val conds: HashMap[TestOp, Int] = new HashMap(); + + conds += EQ -> JExtendedCode.COND_EQ; + conds += NE -> JExtendedCode.COND_NE; + conds += LT -> JExtendedCode.COND_LT; + conds += GT -> JExtendedCode.COND_GT; + conds += LE -> JExtendedCode.COND_LE; + conds += GE -> JExtendedCode.COND_GE; + + val negate: HashMap[TestOp, TestOp] = new HashMap(); + + negate += EQ -> NE; + negate += NE -> EQ; + negate += LT -> GE; + negate += GT -> LE; + negate += LE -> GT; + negate += GE -> LT; + + def makeLabels(bs: List[BasicBlock]) = { + //labels.clear; + if (settings.debug.value) + log("Making labels for: " + method); + bs foreach (bb => labels += bb -> jcode.newLabel() ); + } + + + ////////////////////// local vars /////////////////////// + + 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); + assert (local.index >= 0, + "Invalid index for: " + local); + local.index + } + + def indexOf(local: Local): Int = { + assert (local.index >= 0, + "Invalid index for: " + local); + local.index + } + + /** + * Compute the indexes of each local variable of the given + * method. + */ + def computeLocalVarsIndex(m: IMethod): Unit = { + var idx = 1; + if (isStaticSymbol(m.symbol)) + idx = 0; + + for (val l <- m.locals) { + if (settings.debug.value) + log("Index value for " + l + ": " + idx); + l.index = idx; + idx = idx + sizeOf(l.kind); + } + } + + ////////////////////// Utilities //////////////////////// + + /** Return the a name of this symbol that can be used on the Java + * platform. It removes spaces from names. + * + * Special handling: scala.All and scala.AllRef are 'erased' to + * scala.All$ and scala.AllRef$. This is needed because they are + * not real classes, and they mean 'abrupt termination upon evaluation + * of that expression' or 'null' respectively. This handling is + * done already in GenICode, but here we need to remove references + * from method signatures to these types, because such classes can + * not exist in the classpath: the type checker will be very confused. + */ + def javaName(sym: Symbol): String = { + val suffix = if (sym.hasFlag(Flags.MODULE) && !sym.isMethod && + !sym.isImplClass && + !sym.hasFlag(Flags.JAVA)) "$" else ""; + + if (sym == definitions.AllClass) + return "scala.All$" + else if (sym == definitions.AllRefClass) + return "scala.AllRef$"; + + (if (sym.isClass || (sym.isModule && !sym.isMethod)) + sym.fullNameString('/') + else + sym.simpleName.toString().trim()) + suffix; + } + + def javaNames(syms: List[Symbol]): Array[String] = { + val res = new Array[String](syms.length); + var i = 0; + syms foreach ( s => { res(i) = javaName(s); i = i + 1; } ); + res + } + + /** + * Return the Java modifiers for the given symbol. + * Java modifiers for classes: + * - public, abstract, final, strictfp (not used) + * for interfaces: + * - the same as for classes, without 'final' + * for fields: + * - public, protected, private + * - static, final + * for methods: + * - the same as for fields, plus: + * - abstract, synchronized (not used), strictfp (not used), native (not used) + */ + def javaFlags(sym: Symbol): Int = { + import JAccessFlags._; + + var jf: Int = 0; + val f = sym.flags; + jf = jf | (if (sym hasFlag Flags.PRIVATE) ACC_PRIVATE else ACC_PUBLIC); + jf = jf | (if ((sym hasFlag Flags.ABSTRACT) || + (sym hasFlag Flags.DEFERRED)) ACC_ABSTRACT else 0); + jf = jf | (if (sym hasFlag Flags.INTERFACE) ACC_INTERFACE else 0); + jf = jf | (if ((sym hasFlag Flags.FINAL) && !sym.enclClass.hasFlag(Flags.INTERFACE)) ACC_FINAL else 0); + jf = jf | (if (isStaticSymbol(sym)) ACC_STATIC else 0); + jf + } + + def isStaticSymbol(s: Symbol): Boolean = + s.hasFlag(Flags.STATIC) || s.hasFlag(Flags.STATICMEMBER) || s.owner.isImplClass; + + def javaType(t: TypeKind): JType = t match { + case UNIT => JType.VOID; + + case BOOL => JType.BOOLEAN; + case BYTE => JType.BYTE; + case SHORT => JType.SHORT; + case CHAR => JType.CHAR; + case INT => JType.INT; + case LONG => JType.LONG; + case FLOAT => JType.FLOAT; + case DOUBLE => JType.DOUBLE; + case REFERENCE(cls) => new JObjectType(javaName(cls)); + case ARRAY(elem) => new JArrayType(javaType(elem)); + } + + def javaType(s: Symbol): JType = + if (s.isMethod) + new JMethodType( + if (s.isClassConstructor) + JType.VOID else javaType(toTypeKind(s.tpe.resultType)), + javaTypes(s.tpe.paramTypes map toTypeKind)) + else + javaType(toTypeKind(s.tpe)); + + def javaTypes(ts: List[TypeKind]): Array[JType] = { + val res = new Array[JType](ts.length); + var i = 0; + ts foreach ( t => { res(i) = javaType(t); i = i + 1; } ); + res + } + +// def javaTypes(syms: List[Symbol]): Array[JType] = { +// val res = new Array[JType](syms.length); +// var i = 0; +// syms foreach ( s => { res(i) = javaType(toTypeKind(s.tpe)); i = i + 1; } ); +// res +// } + + def getFile(cls: JClass, suffix: String): String = { + val path = cls.getName().replace('.', File.separatorChar); + settings.outdir.value + File.separatorChar + path + suffix + } + + private def genLocalVariableTable: Unit = { + val vars: Array[JLocalVariable] = jmethod.getLocalVariables(); + + if (!settings.debuginfo.value || vars.length == 0) + return; + + val pool = jclass.getConstantPool(); + val pc = jcode.getPC(); + var anonCounter = 0; + + val lvTab = java.nio.ByteBuffer.allocate(2 + 10 * vars.length); + lvTab.putShort(vars.length.asInstanceOf[Short]); + for (val lv <- vars) { + val name = if (lv.getName() == null) { + anonCounter = anonCounter + 1; + "<anon" + anonCounter + ">" + } else lv.getName(); + + lvTab.putShort(0.asInstanceOf[Short]); + lvTab.putShort(pc.asInstanceOf[Short]); + lvTab.putShort(pool.addUtf8(name).asInstanceOf[Short]); + lvTab.putShort(pool.addUtf8(lv.getType().getSignature()).asInstanceOf[Short]); + lvTab.putShort(lv.getIndex().asInstanceOf[Short]); + } + val attr = + fjbgContext.JOtherAttribute(jclass, + jmethod, + "LocalVariableTable", + lvTab.array()); + jcode.addAttribute(attr); + } + } +} |