package scala.tools.nsc
package backend.msil
import java.io.{File, IOException}
import java.nio.{ByteBuffer, ByteOrder}
import scala.collection.mutable.{Map, HashMap, HashSet, Stack, ListBuffer}
import scala.tools.nsc.symtab._
import ch.epfl.lamp.compiler.msil.{Type => MsilType, _}
import ch.epfl.lamp.compiler.msil.emit._
import ch.epfl.lamp.compiler.msil.util.PECustomMod
abstract class GenMSIL extends SubComponent {
import global._
import loaders.clrTypes
import clrTypes.{types, constructors, methods, fields}
import icodes._
import icodes.opcodes._
override def newPhase(p: Phase) = new MsilPhase(p)
val phaseName = "msil"
class MsilPhase(prev: Phase) extends GlobalPhase(prev) {
def name = phaseName
override def newFlags = phaseNewFlags
override def erasedTypes = true
override def run() {
if (settings.debug.value) inform("[running phase " + name + " on icode]")
val codeGenerator = new BytecodeGenerator
classes.values foreach codeGenerator.findEntryPoint
if( opt.showClass.isDefined && (codeGenerator.entryPoint == null) ) {
val entryclass = opt.showClass.get.toString
warning("Couldn't find entry class " + entryclass)
}
codeGenerator.initAssembly
val classesSorted = classes.values.toList.sortBy(c => c.symbol.id)
classesSorted foreach codeGenerator.createTypeBuilder
classesSorted foreach codeGenerator.createClassMembers
try {
classesSorted foreach codeGenerator.genClass
} finally {
codeGenerator.writeAssembly
}
}
override def apply(unit: CompilationUnit) {
abort("MSIL works on icode classes, not on compilation units!")
}
}
class BytecodeGenerator {
val MODULE_INSTANCE_NAME = "MODULE$"
import clrTypes.{VOID => MVOID, BOOLEAN => MBOOL, BYTE => MBYTE, SHORT => MSHORT,
CHAR => MCHAR, INT => MINT, LONG => MLONG, FLOAT => MFLOAT,
DOUBLE => MDOUBLE, OBJECT => MOBJECT, STRING => MSTRING,
STRING_ARRAY => MSTRING_ARRAY,
SYMTAB_CONSTR => SYMTAB_ATTRIBUTE_CONSTRUCTOR,
SYMTAB_DEFAULT_CONSTR => SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR}
val EXCEPTION = clrTypes.getType("System.Exception")
val MBYTE_ARRAY = clrTypes.mkArrayType(MBYTE)
val ICLONEABLE = clrTypes.getType("System.ICloneable")
val MEMBERWISE_CLONE = MOBJECT.GetMethod("MemberwiseClone", MsilType.EmptyTypes)
val MMONITOR = clrTypes.getType("System.Threading.Monitor")
val MMONITOR_ENTER = MMONITOR.GetMethod("Enter", Array(MOBJECT))
val MMONITOR_EXIT = MMONITOR.GetMethod("Exit", Array(MOBJECT))
val MSTRING_BUILDER = clrTypes.getType("System.Text.StringBuilder")
val MSTRING_BUILDER_CONSTR = MSTRING_BUILDER.GetConstructor(MsilType.EmptyTypes)
val MSTRING_BUILDER_TOSTRING = MSTRING_BUILDER.GetMethod("ToString",
MsilType.EmptyTypes)
val TYPE_FROM_HANDLE =
clrTypes.getType("System.Type").GetMethod("GetTypeFromHandle", Array(clrTypes.getType("System.RuntimeTypeHandle")))
val INT_PTR = clrTypes.getType("System.IntPtr")
val JOBJECT = definitions.ObjectClass
val JSTRING = definitions.StringClass
val SystemConvert = clrTypes.getType("System.Convert")
val objParam = Array(MOBJECT)
val toBool: MethodInfo = SystemConvert.GetMethod("ToBoolean", objParam)
val toSByte: MethodInfo = SystemConvert.GetMethod("ToSByte", objParam)
val toShort: MethodInfo = SystemConvert.GetMethod("ToInt16", objParam)
val toChar: MethodInfo = SystemConvert.GetMethod("ToChar", objParam)
val toInt: MethodInfo = SystemConvert.GetMethod("ToInt32", objParam)
val toLong: MethodInfo = SystemConvert.GetMethod("ToInt64", objParam)
val toFloat: MethodInfo = SystemConvert.GetMethod("ToSingle", objParam)
val toDouble: MethodInfo = SystemConvert.GetMethod("ToDouble", objParam)
val boxedUnit: FieldInfo = fields(definitions.BoxedUnit_UNIT)
val SerializableAttr = definitions.SerializableAttr.tpe
val CloneableAttr = definitions.getClass("scala.cloneable").tpe
val TransientAtt = definitions.getClass("scala.transient").tpe
val dynToStatMapped: HashSet[Symbol] = new HashSet()
initMappings()
private def initMappings() {
mapType(definitions.AnyClass, MOBJECT)
mapType(definitions.AnyRefClass, MOBJECT)
mapType(definitions.NullClass, EXCEPTION)
mapType(definitions.NothingClass, EXCEPTION)
mapType(definitions.BooleanClass, MBOOL)
mapType(definitions.ByteClass, MBYTE)
mapType(definitions.ShortClass, MSHORT)
mapType(definitions.CharClass, MCHAR)
mapType(definitions.IntClass, MINT)
mapType(definitions.LongClass, MLONG)
mapType(definitions.FloatClass, MFLOAT)
mapType(definitions.DoubleClass, MDOUBLE)
}
var clasz: IClass = _
var method: IMethod = _
var massembly: AssemblyBuilder = _
var mmodule: ModuleBuilder = _
var mcode: ILGenerator = _
var assemName: String = _
var firstSourceName = ""
var outDir: File = _
var srcPath: File = _
var moduleName: String = _
def initAssembly() {
assemName = settings.assemname.value
if (assemName == "") {
if (entryPoint != null) {
assemName = msilName(entryPoint.enclClass)
assemName = assemName.substring(0, assemName.length() - 1)
} else {
assert(firstSourceName.endsWith(".scala"), firstSourceName)
assemName = firstSourceName.substring(0, firstSourceName.length() - 6)
}
} else {
if (assemName.endsWith(".msil"))
assemName = assemName.substring(0, assemName.length()-5)
if (assemName.endsWith(".il"))
assemName = assemName.substring(0, assemName.length()-3)
val f: File = new File(assemName)
assemName = f.getName()
}
outDir = new File(settings.outdir.value)
srcPath = new File(settings.sourcedir.value)
val assemblyName = new AssemblyName()
assemblyName.Name = assemName
massembly = AssemblyBuilderFactory.DefineDynamicAssembly(assemblyName)
moduleName = assemName
mmodule = massembly.DefineDynamicModule(moduleName,
new File(outDir, moduleName).getAbsolutePath())
assert (mmodule != null)
}
def addSymtabAttribute(sym: Symbol, tBuilder: TypeBuilder) {
def addMarker() {
val markerSymtab = new Array[Byte](4)
markerSymtab(0) = 1.toByte
tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR, markerSymtab)
}
if (tBuilder.Name.endsWith("$") || sym.isModuleClass) {
addMarker()
} else {
currentRun.symData.get(sym) match {
case Some(pickle) =>
var size = pickle.writeIndex
val symtab = new Array[Byte](size + 8)
symtab(0) = 1.toByte
for (i <- 2 until 6) {
symtab(i) = (size & 0xff).toByte
size = size >> 8
}
java.lang.System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex)
tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab)
currentRun.symData -= sym
currentRun.symData -= sym.companionSymbol
case _ =>
addMarker()
}
}
}
def addAttributes(member: ICustomAttributeSetter, annotations: List[AnnotationInfo]) {
val attributes = annotations.map(_.atp.typeSymbol).collect {
case definitions.TransientAttr => null
}
return
}
def customModifiers(annotations: List[AnnotationInfo]): Array[CustomModifier] = {
annotations.map(_.atp.typeSymbol).collect {
case definitions.VolatileAttr => new CustomModifier(true, CustomModifier.VolatileMarker)
} toArray
}
def writeAssembly() {
if (entryPoint != null) {
assert(entryPoint.enclClass.isModuleClass, entryPoint.enclClass)
val mainMethod = methods(entryPoint)
val stringArrayTypes: Array[MsilType] = Array(MSTRING_ARRAY)
val globalMain = mmodule.DefineGlobalMethod(
"Main", MethodAttributes.Public | MethodAttributes.Static,
MVOID, stringArrayTypes)
globalMain.DefineParameter(0, ParameterAttributes.None, "args")
massembly.SetEntryPoint(globalMain)
val code = globalMain.GetILGenerator()
val moduleField = getModuleInstanceField(entryPoint.enclClass)
code.Emit(OpCodes.Ldsfld, moduleField)
code.Emit(OpCodes.Ldarg_0)
code.Emit(OpCodes.Callvirt, mainMethod)
code.Emit(OpCodes.Ret)
}
createTypes()
var outDirName: String = null
try {
if (settings.Ygenjavap.isDefault) {
outDirName = outDir.getPath()
massembly.Save(outDirName + "\\" + assemName + ".msil")
} else {
outDirName = srcPath.getPath()
massembly.Save(settings.Ygenjavap.value, outDirName)
}
} catch {
case e:IOException => abort("Could not write to " + outDirName + ": " + e.getMessage())
}
}
private def createTypes() {
for (sym <- classes.keys) {
val iclass = classes(sym)
val tBuilder = types(sym).asInstanceOf[TypeBuilder]
if (settings.debug.value)
log("Calling CreatType for " + sym + ", " + tBuilder.toString)
tBuilder.CreateType()
tBuilder.setSourceFilepath(iclass.cunit.source.file.path)
}
}
private[GenMSIL] def ilasmFileName(iclass: IClass) : String = {
iclass.cunit.source.file.toString.replace("\\", "\\\\")
}
private[GenMSIL] def genClass(iclass: IClass) {
val sym = iclass.symbol
if (settings.debug.value)
log("Generating class " + sym + " flags: " + Flags.flagsToString(sym.flags))
clasz = iclass
val tBuilder = getType(sym).asInstanceOf[TypeBuilder]
if (isCloneable(sym)) {
if (iclass.methods.forall(m => {
!((m.symbol.name.toString() != "clone" || m.symbol.name.toString() != "Clone") &&
m.symbol.tpe.paramTypes.length != 0)
})) {
if (settings.debug.value)
log("auto-generating cloneable method for " + sym)
val attrs: Short = (MethodAttributes.Public | MethodAttributes.Virtual |
MethodAttributes.HideBySig).toShort
val cloneMethod = tBuilder.DefineMethod("Clone", attrs, MOBJECT,
MsilType.EmptyTypes)
val clCode = cloneMethod.GetILGenerator()
clCode.Emit(OpCodes.Ldarg_0)
clCode.Emit(OpCodes.Call, MEMBERWISE_CLONE)
clCode.Emit(OpCodes.Ret)
}
}
val line = sym.pos.line
tBuilder.setPosition(line, ilasmFileName(iclass))
if (isTopLevelModule(sym)) {
if (sym.companionClass == NoSymbol)
dumpMirrorClass(sym)
else
log("No mirror class for module with linked class: " +
sym.fullName)
}
addSymtabAttribute(sym, tBuilder)
addAttributes(tBuilder, sym.annotations)
if (iclass.symbol != definitions.ArrayClass)
iclass.methods foreach genMethod
}
private def genMethod(m: IMethod) {
if (settings.debug.value)
log("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) +
" owner: " + m.symbol.owner)
method = m
localBuilders.clear
computeLocalVarsIndex(m)
if (m.symbol.isClassConstructor) {
mcode = constructors(m.symbol).asInstanceOf[ConstructorBuilder].GetILGenerator()
} else {
val mBuilder = methods(m.symbol).asInstanceOf[MethodBuilder]
if (!mBuilder.IsAbstract())
try {
mcode = mBuilder.GetILGenerator()
} catch {
case e: Exception =>
java.lang.System.out.println("m.symbol = " + Flags.flagsToString(m.symbol.flags) + " " + m.symbol)
java.lang.System.out.println("m.symbol.owner = " + Flags.flagsToString(m.symbol.owner.flags) + " " + m.symbol.owner)
java.lang.System.out.println("mBuilder = " + mBuilder)
java.lang.System.out.println("mBuilder.DeclaringType = " +
TypeAttributes.toString(mBuilder.DeclaringType.Attributes) +
"::" + mBuilder.DeclaringType)
throw e
}
else
mcode = null
}
if (mcode != null) {
for (local <- m.locals ; if !(m.params contains local)) {
if (settings.debug.value)
log("add local var: " + local + ", of kind " + local.kind)
val t: MsilType = msilType(local.kind)
val localBuilder = mcode.DeclareLocal(t)
localBuilder.SetLocalSymInfo(msilName(local.sym))
localBuilders(local) = localBuilder
}
genCode(m)
}
}
val msilLinearizer = new MSILLinearizer()
val labels: HashMap[BasicBlock, Label] = new HashMap()
var dbFilenameSeen = false
def genCode(m: IMethod) {
def makeLabels(blocks: List[BasicBlock]) = {
if (settings.debug.value)
log("Making labels for: " + method)
for (bb <- blocks) labels(bb) = mcode.DefineLabel()
}
labels.clear
var linearization = if(!m.exh.isEmpty) msilLinearizer.linearize(m)
else linearizer.linearize(m)
if (!m.exh.isEmpty)
linearization = computeExceptionMaps(linearization, m)
makeLabels(linearization)
dbFilenameSeen = false
genBlocks(linearization)
if (handlerReturnMethod == m) {
mcode.MarkLabel(handlerReturnLabel)
if (handlerReturnKind != UNIT)
mcode.Emit(OpCodes.Ldloc, handlerReturnLocal)
mcode.Emit(OpCodes.Ret)
}
beginExBlock.clear()
beginCatchBlock.clear()
endExBlock.clear()
endFinallyLabels.clear()
}
def genBlocks(blocks: List[BasicBlock], previous: BasicBlock = null) {
blocks match {
case Nil => ()
case x :: Nil => genBlock(x, prev = previous, next = null)
case x :: y :: ys => genBlock(x, prev = previous, next = y); genBlocks(y :: ys, previous = x)
}
}
val beginExBlock = new HashMap[BasicBlock, List[ExceptionHandler]]()
val beginCatchBlock = new HashMap[BasicBlock, ExceptionHandler]()
val endExBlock = new HashMap[BasicBlock, List[ExceptionHandler]]()
var currentHandlers = new Stack[ExceptionHandler]
var handlerReturnMethod: IMethod = _
var handlerReturnLocal: LocalBuilder = _
var handlerReturnLabel: Label = _
var handlerReturnKind: TypeKind = _
def returnFromHandler(kind: TypeKind): (LocalBuilder, Label) = {
if (handlerReturnMethod != method) {
handlerReturnMethod = method
if (kind != UNIT) {
handlerReturnLocal = mcode.DeclareLocal(msilType(kind))
handlerReturnLocal.SetLocalSymInfo("$handlerReturn")
}
handlerReturnLabel = mcode.DefineLabel()
handlerReturnKind = kind
}
(handlerReturnLocal, handlerReturnLabel)
}
val endFinallyLabels = new HashMap[ExceptionHandler, Label]()
private def computeExceptionMaps(blocks: List[BasicBlock], m: IMethod): List[BasicBlock] = {
val visitedBlocks = new HashSet[BasicBlock]()
var openHandlers = m.exh
val currentTryHandlers = new Stack[List[ExceptionHandler]]()
val currentCatchHandlers = new Stack[List[ExceptionHandler]]()
for (b <- blocks) {
def endHandlers(): List[ExceptionHandler] = {
var res: List[ExceptionHandler] = Nil
if (!currentCatchHandlers.isEmpty) {
val handler = currentCatchHandlers.top.head
if (!handler.blocks.contains(b)) {
assert(handler.blocks.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)),
"Bad linearization of basic blocks inside catch. Found block not part of the handler\n"+
b.fullString +"\nwhile in catch-part of\n"+ handler)
val rest = currentCatchHandlers.pop.tail
if (rest.isEmpty) {
res = handler :: endHandlers()
} else {
currentCatchHandlers.push(rest)
beginCatchBlock(b) = rest.head
}
}
}
res
}
val end = endHandlers()
if (!end.isEmpty) endExBlock(b) = end
if (!currentTryHandlers.isEmpty) {
val handler = currentTryHandlers.top.head
if (!handler.covers(b)) {
assert(handler.covered.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)),
"Bad linearization of basic blocks inside try. Found non-covered block\n"+
b.fullString +"\nwhile in try-part of\n"+ handler)
assert(handler.startBlock == b,
"Bad linearization of basic blocks. The entry block of a catch does not directly follow the try\n"+
b.fullString +"\n"+ handler)
val handlers = currentTryHandlers.pop
currentCatchHandlers.push(handlers)
beginCatchBlock(b) = handler
}
}
val (newHandlers, stillOpen) = openHandlers.partition(_.covers(b))
openHandlers = stillOpen
val newHandlersBySize = newHandlers.groupBy(_.covered.size)
val sizes = newHandlersBySize.keys.toList.sortWith(_ > _)
val beginHandlers = new ListBuffer[ExceptionHandler]
for (s <- sizes) {
val sHandlers = newHandlersBySize(s)
for (h <- sHandlers) {
assert(h.covered == sHandlers.head.covered,
"bad nesting of exception handlers. same size, but not covering same blocks\n"+
h +"\n"+ sHandlers.head)
assert(h.resultKind == sHandlers.head.resultKind,
"bad nesting of exception handlers. same size, but the same resultKind\n"+
h +"\n"+ sHandlers.head)
}
for (bigger <- beginHandlers; h <- sHandlers) {
assert(h.covered.subsetOf(bigger.covered),
"bad nesting of exception handlers. try blocks of smaller handler are not nested in bigger one.\n"+
h +"\n"+ bigger)
assert(h.blocks.toSet.subsetOf(bigger.covered),
"bad nesting of exception handlers. catch blocks of smaller handler are not nested in bigger one.\n"+
h +"\n"+ bigger)
}
beginHandlers += sHandlers.head
currentTryHandlers.push(sHandlers)
}
beginExBlock(b) = beginHandlers.toList
visitedBlocks += b
}
val liveBlocks = if (openHandlers.isEmpty) blocks else {
blocks.filter(b => openHandlers.forall(h => !h.blocks.contains(b)))
}
val rest = currentCatchHandlers.map(handlers => {
assert(handlers.length == 1, handlers)
handlers.head
}).toList
if (rest.isEmpty) {
liveBlocks
} else {
val b = m.code.newBlock
b.emit(Seq(
NEW(REFERENCE(definitions.ThrowableClass)),
DUP(REFERENCE(definitions.ObjectClass)),
CALL_METHOD(definitions.ThrowableClass.primaryConstructor, Static(true)),
THROW(definitions.ThrowableClass)
))
b.close
endExBlock(b) = rest
liveBlocks ::: List(b)
}
}
def genBlock(block: BasicBlock, prev: BasicBlock, next: BasicBlock) {
def loadLocalOrAddress(local: Local, msg : String , loadAddr : Boolean) {
if (settings.debug.value)
log(msg + " for " + local)
val isArg = local.arg
val i = local.index
if (isArg)
loadArg(mcode, loadAddr)(i)
else
loadLocal(i, local, mcode, loadAddr)
}
def loadFieldOrAddress(field: Symbol, isStatic: Boolean, msg: String, loadAddr : Boolean) {
if (settings.debug.value)
log(msg + " with owner: " + field.owner +
" flags: " + Flags.flagsToString(field.owner.flags))
var fieldInfo = fields.get(field) match {
case Some(fInfo) => fInfo
case None =>
val fInfo = getType(field.owner).GetField(msilName(field))
fields(field) = fInfo
fInfo
}
if (fieldInfo.IsVolatile) {
mcode.Emit(OpCodes.Volatile)
}
if (!fieldInfo.IsLiteral) {
if (loadAddr) {
mcode.Emit(if (isStatic) OpCodes.Ldsflda else OpCodes.Ldflda, fieldInfo)
} else {
mcode.Emit(if (isStatic) OpCodes.Ldsfld else OpCodes.Ldfld, fieldInfo)
}
} else {
assert(!loadAddr, "can't take AddressOf a literal field (not even with readonly. prefix) because no memory was allocated to such field ...")
val value = fieldInfo.getValue()
if (value == null) {
mcode.Emit(OpCodes.Ldnull)
} else {
val typ = if (fieldInfo.FieldType.IsEnum) fieldInfo.FieldType.getUnderlyingType
else fieldInfo.FieldType
if (typ == clrTypes.STRING) {
mcode.Emit(OpCodes.Ldstr, value.asInstanceOf[String])
} else if (typ == clrTypes.BOOLEAN) {
mcode.Emit(if (value.asInstanceOf[Boolean]) OpCodes.Ldc_I4_1
else OpCodes.Ldc_I4_0)
} else if (typ == clrTypes.BYTE || typ == clrTypes.UBYTE) {
loadI4(value.asInstanceOf[Byte], mcode)
} else if (typ == clrTypes.SHORT || typ == clrTypes.USHORT) {
loadI4(value.asInstanceOf[Int], mcode)
} else if (typ == clrTypes.CHAR) {
loadI4(value.asInstanceOf[Char], mcode)
} else if (typ == clrTypes.INT || typ == clrTypes.UINT) {
loadI4(value.asInstanceOf[Int], mcode)
} else if (typ == clrTypes.LONG || typ == clrTypes.ULONG) {
mcode.Emit(OpCodes.Ldc_I8, value.asInstanceOf[Long])
} else if (typ == clrTypes.FLOAT) {
mcode.Emit(OpCodes.Ldc_R4, value.asInstanceOf[Float])
} else if (typ == clrTypes.DOUBLE) {
mcode.Emit(OpCodes.Ldc_R8, value.asInstanceOf[Double])
} else {
abort("Unknown type for static literal field: " + fieldInfo)
}
}
}
}
var previousWasNEW = false
var lastLineNr: Int = 0
var lastPos: Position = NoPosition
for (handlers <- endExBlock.get(block); exh <- handlers) {
currentHandlers.pop()
for (l <- endFinallyLabels.get(exh))
mcode.MarkLabel(l)
mcode.EndExceptionBlock()
}
mcode.MarkLabel(labels(block))
if (settings.debug.value)
log("Generating code for block: " + block)
for (handler <- beginCatchBlock.get(block)) {
if (!currentHandlers.isEmpty && currentHandlers.top.covered == handler.covered) {
currentHandlers.pop()
currentHandlers.push(handler)
}
if (handler.cls == NoSymbol) {
mcode.BeginFinallyBlock()
} else {
val t = getType(handler.cls)
mcode.BeginCatchBlock(t)
}
}
for (handlers <- beginExBlock.get(block); exh <- handlers) {
currentHandlers.push(exh)
mcode.BeginExceptionBlock()
}
for (instr <- block) {
try {
val currentLineNr = instr.pos.line
val skip = if(instr.pos.isRange) instr.pos.sameRange(lastPos) else (currentLineNr == lastLineNr);
if(!skip || !dbFilenameSeen) {
val fileName = if(dbFilenameSeen) "" else {dbFilenameSeen = true; ilasmFileName(clasz)};
if(instr.pos.isRange) {
val startLine = instr.pos.focusStart.line
val endLine = instr.pos.focusEnd.line
val startCol = instr.pos.focusStart.column
val endCol = instr.pos.focusEnd.column
mcode.setPosition(startLine, endLine, startCol, endCol, fileName)
} else {
mcode.setPosition(instr.pos.line, fileName)
}
lastLineNr = currentLineNr
lastPos = instr.pos
}
} catch { case _: UnsupportedOperationException => () }
if (previousWasNEW)
assert(instr.isInstanceOf[DUP], block)
instr match {
case THIS(clasz) =>
mcode.Emit(OpCodes.Ldarg_0)
case CONSTANT(const) =>
const.tag match {
case UnitTag => ()
case BooleanTag => mcode.Emit(if (const.booleanValue) OpCodes.Ldc_I4_1
else OpCodes.Ldc_I4_0)
case ByteTag => loadI4(const.byteValue, mcode)
case ShortTag => loadI4(const.shortValue, mcode)
case CharTag => loadI4(const.charValue, mcode)
case IntTag => loadI4(const.intValue, mcode)
case LongTag => mcode.Emit(OpCodes.Ldc_I8, const.longValue)
case FloatTag => mcode.Emit(OpCodes.Ldc_R4, const.floatValue)
case DoubleTag => mcode.Emit(OpCodes.Ldc_R8, const.doubleValue)
case StringTag => mcode.Emit(OpCodes.Ldstr, const.stringValue)
case NullTag => mcode.Emit(OpCodes.Ldnull)
case ClassTag =>
mcode.Emit(OpCodes.Ldtoken, msilType(const.typeValue))
mcode.Emit(OpCodes.Call, TYPE_FROM_HANDLE)
case _ => abort("Unknown constant value: " + const)
}
case LOAD_ARRAY_ITEM(kind) =>
(kind: @unchecked) match {
case BOOL => mcode.Emit(OpCodes.Ldelem_I1)
case BYTE => mcode.Emit(OpCodes.Ldelem_I1)
case SHORT => mcode.Emit(OpCodes.Ldelem_I2)
case CHAR => mcode.Emit(OpCodes.Ldelem_U2)
case INT => mcode.Emit(OpCodes.Ldelem_I4)
case LONG => mcode.Emit(OpCodes.Ldelem_I8)
case FLOAT => mcode.Emit(OpCodes.Ldelem_R4)
case DOUBLE => mcode.Emit(OpCodes.Ldelem_R8)
case REFERENCE(cls) => mcode.Emit(OpCodes.Ldelem_Ref)
case ARRAY(elem) => mcode.Emit(OpCodes.Ldelem_Ref)
}
case LOAD_LOCAL(local) => loadLocalOrAddress(local, "load_local", false)
case CIL_LOAD_LOCAL_ADDRESS(local) => loadLocalOrAddress(local, "cil_load_local_address", true)
case LOAD_FIELD(field, isStatic) => loadFieldOrAddress(field, isStatic, "load_field", false)
case CIL_LOAD_FIELD_ADDRESS(field, isStatic) => loadFieldOrAddress(field, isStatic, "cil_load_field_address", true)
case CIL_LOAD_ARRAY_ITEM_ADDRESS(kind) => mcode.Emit(OpCodes.Ldelema, msilType(kind))
case CIL_NEWOBJ(msym) =>
assert(msym.isClassConstructor)
val constructorInfo: ConstructorInfo = getConstructor(msym)
mcode.Emit(OpCodes.Newobj, constructorInfo)
case LOAD_MODULE(module) =>
if (settings.debug.value)
log("Generating LOAD_MODULE for: " + showsym(module))
mcode.Emit(OpCodes.Ldsfld, getModuleInstanceField(module))
case STORE_ARRAY_ITEM(kind) =>
(kind: @unchecked) match {
case BOOL => mcode.Emit(OpCodes.Stelem_I1)
case BYTE => mcode.Emit(OpCodes.Stelem_I1)
case SHORT => mcode.Emit(OpCodes.Stelem_I2)
case CHAR => mcode.Emit(OpCodes.Stelem_I2)
case INT => mcode.Emit(OpCodes.Stelem_I4)
case LONG => mcode.Emit(OpCodes.Stelem_I8)
case FLOAT => mcode.Emit(OpCodes.Stelem_R4)
case DOUBLE => mcode.Emit(OpCodes.Stelem_R8)
case REFERENCE(cls) => mcode.Emit(OpCodes.Stelem_Ref)
case ARRAY(elem) => mcode.Emit(OpCodes.Stelem_Ref)
}
case STORE_LOCAL(local) =>
val isArg = local.arg
val i = local.index
if (settings.debug.value)
log("store_local for " + local + ", index " + i)
if (isArg) {
if (i >= -128 && i <= 127)
mcode.Emit(OpCodes.Starg_S, i)
else
mcode.Emit(OpCodes.Starg, i)
} else {
i match {
case 0 => mcode.Emit(OpCodes.Stloc_0)
case 1 => mcode.Emit(OpCodes.Stloc_1)
case 2 => mcode.Emit(OpCodes.Stloc_2)
case 3 => mcode.Emit(OpCodes.Stloc_3)
case _ =>
if (i >= -128 && i <= 127)
mcode.Emit(OpCodes.Stloc_S, localBuilders(local))
else
mcode.Emit(OpCodes.Stloc, localBuilders(local))
}
}
case STORE_THIS(_) =>
mcode.Emit(OpCodes.Starg_S, 0)
case STORE_FIELD(field, isStatic) =>
val fieldInfo = fields.get(field) match {
case Some(fInfo) => fInfo
case None =>
val fInfo = getType(field.owner).GetField(msilName(field))
fields(field) = fInfo
fInfo
}
mcode.Emit(if (isStatic) OpCodes.Stsfld else OpCodes.Stfld, fieldInfo)
case CALL_PRIMITIVE(primitive) =>
genPrimitive(primitive, instr.pos)
case CALL_METHOD(msym, style) =>
if (msym.isClassConstructor) {
val constructorInfo: ConstructorInfo = getConstructor(msym)
(style: @unchecked) match {
case Static(_) =>
if (method.symbol.isClassConstructor && method.symbol.owner == msym.owner)
mcode.Emit(OpCodes.Call, constructorInfo)
else
mcode.Emit(OpCodes.Newobj, constructorInfo)
case SuperCall(_) =>
mcode.Emit(OpCodes.Call, constructorInfo)
if (isStaticModule(clasz.symbol) &&
notInitializedModules.contains(clasz.symbol) &&
method.symbol.isClassConstructor)
{
notInitializedModules -= clasz.symbol
mcode.Emit(OpCodes.Ldarg_0)
mcode.Emit(OpCodes.Stsfld, getModuleInstanceField(clasz.symbol))
}
}
} else {
var doEmit = true
getTypeOpt(msym.owner) match {
case Some(typ) if (typ.IsEnum) => {
def negBool() = {
mcode.Emit(OpCodes.Ldc_I4_0)
mcode.Emit(OpCodes.Ceq)
}
doEmit = false
val name = msym.name
if (name eq nme.EQ) { mcode.Emit(OpCodes.Ceq) }
else if (name eq nme.NE) { mcode.Emit(OpCodes.Ceq); negBool }
else if (name eq nme.LT) { mcode.Emit(OpCodes.Clt) }
else if (name eq nme.LE) { mcode.Emit(OpCodes.Cgt); negBool }
else if (name eq nme.GT) { mcode.Emit(OpCodes.Cgt) }
else if (name eq nme.GE) { mcode.Emit(OpCodes.Clt); negBool }
else if (name eq nme.OR) { mcode.Emit(OpCodes.Or) }
else if (name eq nme.AND) { mcode.Emit(OpCodes.And) }
else if (name eq nme.XOR) { mcode.Emit(OpCodes.Xor) }
else
doEmit = true
}
case _ => ()
}
val (isDelegateView, paramType, resType) = atPhase(currentRun.typerPhase) {
msym.tpe match {
case MethodType(params, resultType)
if (params.length == 1 && msym.name == nme.view_) =>
val paramType = params(0).tpe
val isDel = definitions.isCorrespondingDelegate(resultType, paramType)
(isDel, paramType, resultType)
case _ => (false, null, null)
}
}
if (doEmit && isDelegateView) {
doEmit = false
createDelegateCaller(paramType, resType)
}
if (doEmit &&
(msym.name == nme.PLUS || msym.name == nme.MINUS)
&& clrTypes.isDelegateType(msilType(msym.owner.tpe)))
{
doEmit = false
val methodInfo: MethodInfo = getMethod(msym)
mcode.Emit(OpCodes.Call, methodInfo)
mcode.Emit(OpCodes.Castclass, msilType(msym.owner.tpe))
}
if (doEmit && definitions.Delegate_scalaCallers.contains(msym)) {
doEmit = false
val methodSym: Symbol = definitions.Delegate_scalaCallerTargets(msym)
val delegateType: Type = msym.tpe match {
case MethodType(_, retType) => retType
case _ => abort("not a method type: " + msym.tpe)
}
val methodInfo: MethodInfo = getMethod(methodSym)
val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR))
if (methodSym.isStatic) {
mcode.Emit(OpCodes.Ldftn, methodInfo)
} else {
mcode.Emit(OpCodes.Dup)
mcode.Emit(OpCodes.Ldvirtftn, methodInfo)
}
mcode.Emit(OpCodes.Newobj, delegCtor)
}
if (doEmit) {
val methodInfo: MethodInfo = getMethod(msym)
(style: @unchecked) match {
case SuperCall(_) =>
mcode.Emit(OpCodes.Call, methodInfo)
case Dynamic =>
val isValuetypeMethod = (methodInfo.DeclaringType ne null) && (methodInfo.DeclaringType.IsValueType)
val isValuetypeVirtualMethod = isValuetypeMethod && (methodInfo.IsVirtual)
if (dynToStatMapped(msym)) {
mcode.Emit(OpCodes.Call, methodInfo)
} else if (isValuetypeVirtualMethod) {
mcode.Emit(OpCodes.Constrained, methodInfo.DeclaringType)
mcode.Emit(OpCodes.Callvirt, methodInfo)
} else if (isValuetypeMethod) {
mcode.Emit(OpCodes.Call, methodInfo)
} else {
mcode.Emit(OpCodes.Callvirt, methodInfo)
}
case Static(_) =>
if(methodInfo.IsVirtual && !mcode.Ldarg0WasJustEmitted) {
mcode.Emit(OpCodes.Callvirt, methodInfo)
} else mcode.Emit(OpCodes.Call, methodInfo)
}
}
}
case BOX(boxType) =>
emitBox(mcode, boxType)
case UNBOX(boxType) =>
emitUnbox(mcode, boxType)
case CIL_UNBOX(boxType) =>
mcode.Emit(OpCodes.Unbox, msilType(boxType))
case CIL_INITOBJ(valueType) =>
mcode.Emit(OpCodes.Initobj, msilType(valueType))
case NEW(REFERENCE(cls)) =>
previousWasNEW = true
case CREATE_ARRAY(elem, dims) =>
assert(dims == 1, "Can't handle multi dimensional arrays")
mcode.Emit(OpCodes.Newarr, msilType(elem))
case IS_INSTANCE(tpe) =>
mcode.Emit(OpCodes.Isinst, msilType(tpe))
mcode.Emit(OpCodes.Ldnull)
mcode.Emit(OpCodes.Ceq)
mcode.Emit(OpCodes.Ldc_I4_0)
mcode.Emit(OpCodes.Ceq)
case CHECK_CAST(tpknd) =>
val tMSIL = msilType(tpknd)
mcode.Emit(OpCodes.Castclass, tMSIL)
case SWITCH(tags, branches) =>
val switchLocal = mcode.DeclareLocal(MINT)
switchLocal.SetLocalSymInfo("$switch_var")
mcode.Emit(OpCodes.Stloc, switchLocal)
var i = 0
for (l <- tags) {
var targetLabel = labels(branches(i))
for (i <- l) {
mcode.Emit(OpCodes.Ldloc, switchLocal)
loadI4(i, mcode)
mcode.Emit(OpCodes.Beq, targetLabel)
}
i += 1
}
val defaultTarget = labels(branches(i))
if (next != defaultTarget)
mcode.Emit(OpCodes.Br, defaultTarget)
case JUMP(whereto) =>
val (leaveHandler, leaveFinally, lfTarget) = leavesHandler(block, whereto)
if (leaveHandler) {
if (leaveFinally) {
if (lfTarget.isDefined) mcode.Emit(OpCodes.Leave, lfTarget.get)
else mcode.Emit(OpCodes.Endfinally)
} else
mcode.Emit(OpCodes.Leave, labels(whereto))
} else if (next != whereto)
mcode.Emit(OpCodes.Br, labels(whereto))
case CJUMP(success, failure, cond, kind) =>
val isFloat = kind == FLOAT || kind == DOUBLE
val emit = (c: TestOp, l: Label) => emitBr(c, l, isFloat)
emitCondBr(block, cond, success, failure, next, emit)
case CZJUMP(success, failure, cond, kind) =>
emitCondBr(block, cond, success, failure, next, emitBrBool(_, _))
case RETURN(kind) =>
if (currentHandlers.isEmpty)
mcode.Emit(OpCodes.Ret)
else {
val (local, label) = returnFromHandler(kind)
if (kind != UNIT)
mcode.Emit(OpCodes.Stloc, local)
mcode.Emit(OpCodes.Leave, label)
}
case THROW(_) =>
mcode.Emit(OpCodes.Throw)
case DROP(kind) =>
mcode.Emit(OpCodes.Pop)
case DUP(kind) =>
if (!previousWasNEW)
mcode.Emit(OpCodes.Dup)
else
previousWasNEW = false
case MONITOR_ENTER() =>
mcode.Emit(OpCodes.Call, MMONITOR_ENTER)
case MONITOR_EXIT() =>
mcode.Emit(OpCodes.Call, MMONITOR_EXIT)
case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION(_) =>
()
}
}
}
def genPrimitive(primitive: Primitive, pos: Position) {
primitive match {
case Negation(kind) =>
kind match {
case BOOL | BYTE | CHAR | SHORT | INT | LONG | FLOAT | DOUBLE =>
mcode.Emit(OpCodes.Neg)
case _ => abort("Impossible to negate a " + kind)
}
case Arithmetic(op, kind) =>
op match {
case ADD => mcode.Emit(OpCodes.Add)
case SUB => mcode.Emit(OpCodes.Sub)
case MUL => mcode.Emit(OpCodes.Mul)
case DIV => mcode.Emit(OpCodes.Div)
case REM => mcode.Emit(OpCodes.Rem)
case NOT => mcode.Emit(OpCodes.Not)
case _ => abort("Unknown arithmetic primitive " + primitive )
}
case Logical(op, kind) => op match {
case AND => mcode.Emit(OpCodes.And)
case OR => mcode.Emit(OpCodes.Or)
case XOR => mcode.Emit(OpCodes.Xor)
}
case Shift(op, kind) => op match {
case LSL => mcode.Emit(OpCodes.Shl)
case ASR => mcode.Emit(OpCodes.Shr)
case LSR => mcode.Emit(OpCodes.Shr_Un)
}
case Conversion(src, dst) =>
if (settings.debug.value)
log("Converting from: " + src + " to: " + dst)
dst match {
case BYTE => mcode.Emit(OpCodes.Conv_I1)
case SHORT => mcode.Emit(OpCodes.Conv_I2)
case CHAR => mcode.Emit(OpCodes.Conv_U2)
case INT => mcode.Emit(OpCodes.Conv_I4)
case LONG => mcode.Emit(OpCodes.Conv_I8)
case FLOAT => mcode.Emit(OpCodes.Conv_R4)
case DOUBLE => mcode.Emit(OpCodes.Conv_R8)
case _ =>
Console.println("Illegal conversion at: " + clasz +
" at: " + pos.source + ":" + pos.line)
}
case ArrayLength(_) =>
mcode.Emit(OpCodes.Ldlen)
case StartConcat =>
mcode.Emit(OpCodes.Newobj, MSTRING_BUILDER_CONSTR)
case StringConcat(el) =>
val elemType : MsilType = el match {
case REFERENCE(_) | ARRAY(_) => MOBJECT
case _ => msilType(el)
}
val argTypes:Array[MsilType] = Array(elemType)
val stringBuilderAppend = MSTRING_BUILDER.GetMethod("Append", argTypes )
mcode.Emit(OpCodes.Callvirt, stringBuilderAppend)
case EndConcat =>
mcode.Emit(OpCodes.Callvirt, MSTRING_BUILDER_TOSTRING)
case _ =>
abort("Unimplemented primitive " + primitive)
}
}
def loadI4(value: Int, code: ILGenerator): Unit = value match {
case -1 => code.Emit(OpCodes.Ldc_I4_M1)
case 0 => code.Emit(OpCodes.Ldc_I4_0)
case 1 => code.Emit(OpCodes.Ldc_I4_1)
case 2 => code.Emit(OpCodes.Ldc_I4_2)
case 3 => code.Emit(OpCodes.Ldc_I4_3)
case 4 => code.Emit(OpCodes.Ldc_I4_4)
case 5 => code.Emit(OpCodes.Ldc_I4_5)
case 6 => code.Emit(OpCodes.Ldc_I4_6)
case 7 => code.Emit(OpCodes.Ldc_I4_7)
case 8 => code.Emit(OpCodes.Ldc_I4_8)
case _ =>
if (value >= -128 && value <= 127)
code.Emit(OpCodes.Ldc_I4_S, value)
else
code.Emit(OpCodes.Ldc_I4, value)
}
def loadArg(code: ILGenerator, loadAddr: Boolean)(i: Int) =
if (loadAddr) {
if (i >= -128 && i <= 127)
code.Emit(OpCodes.Ldarga_S, i)
else
code.Emit(OpCodes.Ldarga, i)
} else {
i match {
case 0 => code.Emit(OpCodes.Ldarg_0)
case 1 => code.Emit(OpCodes.Ldarg_1)
case 2 => code.Emit(OpCodes.Ldarg_2)
case 3 => code.Emit(OpCodes.Ldarg_3)
case _ =>
if (i >= -128 && i <= 127)
code.Emit(OpCodes.Ldarg_S, i)
else
code.Emit(OpCodes.Ldarg, i)
}
}
def loadLocal(i: Int, local: Local, code: ILGenerator, loadAddr: Boolean) =
if (loadAddr) {
if (i >= -128 && i <= 127)
code.Emit(OpCodes.Ldloca_S, localBuilders(local))
else
code.Emit(OpCodes.Ldloca, localBuilders(local))
} else {
i match {
case 0 => code.Emit(OpCodes.Ldloc_0)
case 1 => code.Emit(OpCodes.Ldloc_1)
case 2 => code.Emit(OpCodes.Ldloc_2)
case 3 => code.Emit(OpCodes.Ldloc_3)
case _ =>
if (i >= -128 && i <= 127)
code.Emit(OpCodes.Ldloc_S, localBuilders(local))
else
code.Emit(OpCodes.Ldloc, localBuilders(local))
}
}
def leavesHandler(from: BasicBlock, to: BasicBlock): (Boolean, Boolean, Option[Label]) =
if (currentHandlers.isEmpty) (false, false, None)
else {
val h = currentHandlers.head
val leaveHead = { h.covers(from) != h.covers(to) ||
h.blocks.contains(from) != h.blocks.contains(to) }
if (leaveHead) {
currentHandlers.find(e => {
e.cls == NoSymbol && e.blocks.contains(from) != e.blocks.contains(to)
}) match {
case Some(finallyHandler) =>
if (h == finallyHandler) {
(true, true, None)
} else {
val l = endFinallyLabels.getOrElseUpdate(finallyHandler, mcode.DefineLabel())
(true, true, Some(l))
}
case None =>
(true, false, None)
}
} else (false, false, None)
}
def emitCondBr(block: BasicBlock, cond: TestOp, success: BasicBlock, failure: BasicBlock,
next: BasicBlock, emitBrFun: (TestOp, Label) => Unit) {
val (sLeaveHandler, sLeaveFinally, slfTarget) = leavesHandler(block, success)
val (fLeaveHandler, fLeaveFinally, flfTarget) = leavesHandler(block, failure)
if (sLeaveHandler || fLeaveHandler) {
val sLabelOpt = if (sLeaveHandler) {
val leaveSLabel = mcode.DefineLabel()
emitBrFun(cond, leaveSLabel)
Some(leaveSLabel)
} else {
emitBrFun(cond, labels(success))
None
}
if (fLeaveHandler) {
if (fLeaveFinally) {
if (flfTarget.isDefined) mcode.Emit(OpCodes.Leave, flfTarget.get)
else mcode.Emit(OpCodes.Endfinally)
} else
mcode.Emit(OpCodes.Leave, labels(failure))
} else
mcode.Emit(OpCodes.Br, labels(failure))
sLabelOpt.map(l => {
mcode.MarkLabel(l)
if (sLeaveFinally) {
if (slfTarget.isDefined) mcode.Emit(OpCodes.Leave, slfTarget.get)
else mcode.Emit(OpCodes.Endfinally)
} else
mcode.Emit(OpCodes.Leave, labels(success))
})
} else {
if (next == success) {
emitBrFun(cond.negate, labels(failure))
} else {
emitBrFun(cond, labels(success))
if (next != failure) {
mcode.Emit(OpCodes.Br, labels(failure))
}
}
}
}
def emitBr(condition: TestOp, dest: Label, isFloat: Boolean) {
condition match {
case EQ => mcode.Emit(OpCodes.Beq, dest)
case NE => mcode.Emit(OpCodes.Bne_Un, dest)
case LT => mcode.Emit(if (isFloat) OpCodes.Blt_Un else OpCodes.Blt, dest)
case GE => mcode.Emit(if (isFloat) OpCodes.Bge_Un else OpCodes.Bge, dest)
case LE => mcode.Emit(if (isFloat) OpCodes.Ble_Un else OpCodes.Ble, dest)
case GT => mcode.Emit(if (isFloat) OpCodes.Bgt_Un else OpCodes.Bgt, dest)
}
}
def emitBrBool(cond: TestOp, dest: Label) {
cond match {
case EQ => mcode.Emit(OpCodes.Brfalse, dest)
case NE => mcode.Emit(OpCodes.Brtrue, dest)
}
}
def computeLocalVarsIndex(m: IMethod) {
var idx = if (m.symbol.isStaticMember) 0 else 1
val params = m.params
for (l <- params) {
if (settings.debug.value)
log("Index value for parameter " + l + ": " + idx)
l.index = idx
idx += 1
}
val locvars = m.locals filterNot (params contains)
idx = 0
for (l <- locvars) {
if (settings.debug.value)
log("Index value for local variable " + l + ": " + idx)
l.index = idx
idx += 1
}
}
def msilName(sym: Symbol): String = {
val suffix: String = if (sym.hasModuleFlag && !sym.isMethod &&
!sym.isImplClass &&
!sym.isJavaDefined) "$" else ""
if (sym == definitions.NothingClass)
return "scala.runtime.Nothing$"
else if (sym == definitions.NullClass)
return "scala.runtime.Null$"
(if (sym.isClass || (sym.isModule && !sym.isMethod)) {
if (sym.isNestedClass) sym.simpleName
else sym.fullName
} else
sym.simpleName.toString().trim()) + suffix
}
def msilTypeFlags(sym: Symbol): Int = {
var mf: Int = TypeAttributes.AutoLayout | TypeAttributes.AnsiClass
if(sym.isNestedClass) {
mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NestedPrivate else TypeAttributes.NestedPublic)
} else {
mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NotPublic else TypeAttributes.Public)
}
mf = mf | (if (sym hasFlag Flags.ABSTRACT) TypeAttributes.Abstract else 0)
mf = mf | (if (sym.isTrait && !sym.isImplClass) TypeAttributes.Interface else TypeAttributes.Class)
mf = mf | (if (sym isFinal) TypeAttributes.Sealed else 0)
sym.annotations foreach { a => a match {
case AnnotationInfo(SerializableAttr, _, _) =>
mf = mf | TypeAttributes.Serializable
case _ => ()
}}
mf
}
def msilMethodFlags(sym: Symbol): Short = {
var mf: Int = MethodAttributes.HideBySig |
(if (sym hasFlag Flags.PRIVATE) MethodAttributes.Private
else MethodAttributes.Public)
if (!sym.isClassConstructor) {
if (sym.isStaticMember)
mf = mf | FieldAttributes.Static
else {
mf = mf | MethodAttributes.Virtual
if (sym.isFinal && !getType(sym.owner).IsInterface)
mf = mf | MethodAttributes.Final
if (sym.isDeferred || getType(sym.owner).IsInterface)
mf = mf | MethodAttributes.Abstract
}
}
if (sym.isStaticMember) {
mf = mf | MethodAttributes.Static
}
if (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) {
mf |= MethodAttributes.Private
mf &= ~(MethodAttributes.Public)
}
mf.toShort
}
def msilFieldFlags(sym: Symbol): Short = {
var mf: Int =
if (sym hasFlag Flags.PRIVATE) FieldAttributes.Private
else if (sym hasFlag Flags.PROTECTED) FieldAttributes.FamORAssem
else FieldAttributes.Public
if (sym hasFlag Flags.FINAL)
mf = mf | FieldAttributes.InitOnly
if (sym.isStaticMember)
mf = mf | FieldAttributes.Static
sym.annotations.foreach( a => a match {
case AnnotationInfo(TransientAtt, _, _) =>
mf = mf | FieldAttributes.NotSerialized
case _ => ()
})
mf.toShort
}
var entryPoint: Symbol = _
val notInitializedModules: HashSet[Symbol] = new HashSet()
val localBuilders: HashMap[Local, LocalBuilder] = new HashMap()
private[GenMSIL] def findEntryPoint(cls: IClass) {
def isEntryPoint(sym: Symbol):Boolean = {
if (isStaticModule(sym.owner) && msilName(sym) == "main")
if (sym.tpe.paramTypes.length == 1) {
toTypeKind(sym.tpe.paramTypes(0)) match {
case ARRAY(elem) =>
if (elem.toType.typeSymbol == definitions.StringClass) {
return true
}
case _ => ()
}
}
false
}
if((entryPoint == null) && opt.showClass.isDefined) {
val entryclass = opt.showClass.get.toString
val cfn = cls.symbol.fullName
if(cfn == entryclass) {
for (m <- cls.methods; if isEntryPoint(m.symbol)) { entryPoint = m.symbol }
if(entryPoint == null) { warning("Couldn't find main method in class " + cfn) }
}
}
if (firstSourceName == "")
if (cls.symbol.sourceFile != null)
firstSourceName = cls.symbol.sourceFile.name
}
private def msilType(t: TypeKind): MsilType = (t: @unchecked) match {
case UNIT => MVOID
case BOOL => MBOOL
case BYTE => MBYTE
case SHORT => MSHORT
case CHAR => MCHAR
case INT => MINT
case LONG => MLONG
case FLOAT => MFLOAT
case DOUBLE => MDOUBLE
case REFERENCE(cls) => getType(cls)
case ARRAY(elem) =>
msilType(elem) match {
case tb: TypeBuilder => tb.MakeArrayType()
case tp: MsilType => clrTypes.mkArrayType(tp)
}
}
private def msilType(tpe: Type): MsilType = msilType(toTypeKind(tpe))
private def msilParamTypes(sym: Symbol): Array[MsilType] = {
sym.tpe.paramTypes.map(msilType).toArray
}
def getType(sym: Symbol) = getTypeOpt(sym).getOrElse(abort(showsym(sym)))
def getTypeOpt(sym: Symbol): Option[MsilType] = {
val tmp = types.get(sym)
tmp match {
case typ @ Some(_) => typ
case None =>
def typeString(sym: Symbol): String = {
val s = if (sym.isNestedClass) typeString(sym.owner) +"+"+ sym.simpleName
else sym.fullName
if (sym.isModuleClass && !sym.isTrait) s + "$" else s
}
val name = typeString(sym)
val typ = clrTypes.getType(name)
if (typ == null)
None
else {
types(sym) = typ
Some(typ)
}
}
}
def mapType(sym: Symbol, mType: MsilType) {
assert(mType != null, showsym(sym))
types(sym) = mType
}
def createTypeBuilder(iclass: IClass) {
def msilTypeFromSym(sym: Symbol): MsilType = {
types.get(sym).getOrElse {
classes.get(sym) match {
case Some(iclass) =>
msilTypeBuilderFromSym(sym)
case None =>
getType(sym)
}
}
}
def msilTypeBuilderFromSym(sym: Symbol): TypeBuilder = {
if(!(types.contains(sym) && types(sym).isInstanceOf[TypeBuilder])){
val iclass = classes(sym)
assert(iclass != null)
createTypeBuilder(iclass)
}
types(sym).asInstanceOf[TypeBuilder]
}
val sym = iclass.symbol
if (types.contains(sym) && types(sym).isInstanceOf[TypeBuilder])
return
def isInterface(s: Symbol) = s.isTrait && !s.isImplClass
val parents: List[Type] =
if (sym.info.parents.isEmpty) List(definitions.ObjectClass.tpe)
else sym.info.parents.distinct
val superType : MsilType = if (isInterface(sym)) null else msilTypeFromSym(parents.head.typeSymbol)
if (settings.debug.value)
log("super type: " + parents(0).typeSymbol + ", msil type: " + superType)
val interfaces: Array[MsilType] =
parents.tail.map(p => msilTypeFromSym(p.typeSymbol)).toArray
if (parents.length > 1) {
if (settings.debug.value) {
log("interfaces:")
for (i <- 0.until(interfaces.length)) {
log(" type: " + parents(i + 1).typeSymbol + ", msil type: " + interfaces(i))
}
}
}
val tBuilder = if (sym.isNestedClass) {
val ownerT = msilTypeBuilderFromSym(sym.owner).asInstanceOf[TypeBuilder]
ownerT.DefineNestedType(msilName(sym), msilTypeFlags(sym), superType, interfaces)
} else {
mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces)
}
mapType(sym, tBuilder)
}
def createClassMembers(iclass: IClass) {
try {
createClassMembers0(iclass)
}
catch {
case e: Throwable =>
java.lang.System.err.println(showsym(iclass.symbol))
java.lang.System.err.println("with methods = " + iclass.methods)
throw e
}
}
def createClassMembers0(iclass: IClass) {
val mtype = getType(iclass.symbol).asInstanceOf[TypeBuilder]
for (ifield <- iclass.fields) {
val sym = ifield.symbol
if (settings.debug.value)
log("Adding field: " + sym.fullName)
var attributes = msilFieldFlags(sym)
val fieldTypeWithCustomMods =
new PECustomMod(msilType(sym.tpe),
customModifiers(sym.annotations))
val fBuilder = mtype.DefineField(msilName(sym),
fieldTypeWithCustomMods,
attributes)
fields(sym) = fBuilder
addAttributes(fBuilder, sym.annotations)
}
if (isStaticModule(iclass.symbol)) {
val sc = iclass.lookupStaticCtor
if (sc.isDefined) {
val m = sc.get
val oldLastBlock = m.code.blocks.last
val lastBlock = m.code.newBlock
oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock))
lastBlock.emit(CIL_NEWOBJ(iclass.symbol.primaryConstructor))
lastBlock.emit(DROP(toTypeKind(iclass.symbol.tpe)))
lastBlock emit RETURN(UNIT)
lastBlock.close
}
}
if (iclass.symbol != definitions.ArrayClass) {
for (m: IMethod <- iclass.methods) {
val sym = m.symbol
if (settings.debug.value)
log("Creating MethodBuilder for " + Flags.flagsToString(sym.flags) + " " +
sym.owner.fullName + "::" + sym.name)
val ownerType = getType(sym.enclClass).asInstanceOf[TypeBuilder]
assert(mtype == ownerType, "mtype = " + mtype + "; ownerType = " + ownerType)
var paramTypes = msilParamTypes(sym)
val attr = msilMethodFlags(sym)
if (m.symbol.isClassConstructor) {
val constr =
ownerType.DefineConstructor(attr, CallingConventions.Standard, paramTypes)
for (i <- 0.until(paramTypes.length)) {
constr.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym))
}
mapConstructor(sym, constr)
addAttributes(constr, sym.annotations)
} else {
var resType = msilType(m.returnType)
val method =
ownerType.DefineMethod(msilName(sym), attr, resType, paramTypes)
for (i <- 0.until(paramTypes.length)) {
method.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym))
}
if (!methods.contains(sym))
mapMethod(sym, method)
addAttributes(method, sym.annotations)
if (settings.debug.value)
log("\t created MethodBuilder " + method)
}
}
}
if (isStaticModule(iclass.symbol)) {
addModuleInstanceField(iclass.symbol)
notInitializedModules += iclass.symbol
if (iclass.lookupStaticCtor.isEmpty) {
addStaticInit(iclass.symbol)
}
}
}
private def isTopLevelModule(sym: Symbol): Boolean =
atPhase (currentRun.refchecksPhase) {
sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass
}
private def isStaticModule(sym: Symbol): Boolean = {
sym.isModuleClass && sym.isStatic && !sym.isImplClass
}
private def isCloneable(sym: Symbol): Boolean = {
!sym.annotations.forall( a => a match {
case AnnotationInfo(CloneableAttr, _, _) => false
case _ => true
})
}
private def addModuleInstanceField(sym: Symbol) {
if (settings.debug.value)
log("Adding Module-Instance Field for " + showsym(sym))
val tBuilder = getType(sym).asInstanceOf[TypeBuilder]
val fb = tBuilder.DefineField(MODULE_INSTANCE_NAME,
tBuilder,
(FieldAttributes.Public |
FieldAttributes.Static).toShort)
fields(sym) = fb
}
private def getModuleInstanceField(sym: Symbol): FieldInfo = {
assert(sym.isModule || sym.isModuleClass, "Expected module: " + showsym(sym))
val moduleClassSym = if (sym.isModule) sym.moduleClass else sym
fields get moduleClassSym match {
case Some(sym) => sym
case None =>
val nameInMetadata = nestingAwareFullClassname(moduleClassSym)
val mClass = clrTypes.getType(nameInMetadata)
val mfield = mClass.GetField("MODULE$")
assert(mfield ne null, "module not found " + showsym(moduleClassSym))
fields(moduleClassSym) = mfield
mfield
}
}
def nestingAwareFullClassname(csym: Symbol) : String = {
val suffix = moduleSuffix(csym)
val res = if (csym.isNestedClass)
nestingAwareFullClassname(csym.owner) + "+" + csym.encodedName
else
csym.fullName
res + suffix
}
def moduleSuffix(sym: Symbol) =
if (sym.hasFlag(Flags.MODULE) && !sym.isMethod &&
!sym.isImplClass && !sym.hasFlag(Flags.JAVA)) "$"
else "";
private def addStaticInit(sym: Symbol) {
val tBuilder = getType(sym).asInstanceOf[TypeBuilder]
val staticInit = tBuilder.DefineConstructor(
(MethodAttributes.Static | MethodAttributes.Public).toShort,
CallingConventions.Standard,
MsilType.EmptyTypes)
val sicode = staticInit.GetILGenerator()
val instanceConstructor = constructors(sym.primaryConstructor)
sicode.Emit(OpCodes.Newobj, instanceConstructor)
sicode.Emit(OpCodes.Pop)
sicode.Emit(OpCodes.Ret)
}
private def dumpMirrorClass(sym: Symbol) {
val tBuilder = getType(sym)
assert(sym.isModuleClass, "Can't generate Mirror-Class for the Non-Module class " + sym)
if (settings.debug.value)
log("Dumping mirror class for object: " + sym)
val moduleName = msilName(sym)
val mirrorName = moduleName.substring(0, moduleName.length() - 1)
val mirrorTypeBuilder = mmodule.DefineType(mirrorName,
TypeAttributes.Class |
TypeAttributes.Public |
TypeAttributes.Sealed,
MOBJECT,
MsilType.EmptyTypes)
val iclass = classes(sym)
for (m <- sym.tpe.nonPrivateMembers
if m.owner != definitions.ObjectClass && !m.isProtected &&
m.isMethod && !m.isClassConstructor && !m.isStaticMember && !m.isCase &&
!m.isDeferred)
{
if (settings.debug.value)
log(" Mirroring method: " + m)
val paramTypes = msilParamTypes(m)
val paramNames: Array[String] = new Array[String](paramTypes.length)
for (i <- 0 until paramTypes.length)
paramNames(i) = "x_" + i
val mirrorMethod = mirrorTypeBuilder.DefineMethod(msilName(m),
(MethodAttributes.Public |
MethodAttributes.Static).toShort,
msilType(m.tpe.resultType),
paramTypes)
var i = 0
while (i < paramTypes.length) {
mirrorMethod.DefineParameter(i, ParameterAttributes.None, paramNames(i))
i += 1
}
val mirrorCode = mirrorMethod.GetILGenerator()
mirrorCode.Emit(OpCodes.Ldsfld, getModuleInstanceField(sym))
val mInfo = getMethod(m)
for (paramidx <- 0.until(paramTypes.length)) {
val mInfoParams = mInfo.GetParameters
val loadAddr = mInfoParams(paramidx).ParameterType.IsByRef
loadArg(mirrorCode, loadAddr)(paramidx)
}
mirrorCode.Emit(OpCodes.Callvirt, getMethod(m))
mirrorCode.Emit(OpCodes.Ret)
}
addSymtabAttribute(sym.sourceModule, mirrorTypeBuilder)
mirrorTypeBuilder.CreateType()
mirrorTypeBuilder.setSourceFilepath(iclass.cunit.source.file.path)
}
var delegateCallers: TypeBuilder = _
var nbDelegateCallers: Int = 0
private def initDelegateCallers() = {
delegateCallers = mmodule.DefineType("$DelegateCallers", TypeAttributes.Public |
TypeAttributes.Sealed)
}
private def createDelegateCaller(functionType: Type, delegateType: Type) = {
if (delegateCallers == null)
initDelegateCallers()
val mFunctionType: MsilType = msilType(functionType)
val anonfunField: FieldBuilder = delegateCallers.DefineField(
"$anonfunField$$" + nbDelegateCallers, mFunctionType,
(FieldAttributes.InitOnly | FieldAttributes.Public | FieldAttributes.Static).toShort)
mcode.Emit(OpCodes.Stsfld, anonfunField)
val (params, returnType) = delegateType.member(nme.apply).tpe match {
case MethodType(delParams, delReturn) => (delParams, delReturn)
case _ => abort("not a delegate type: " + delegateType)
}
val caller: MethodBuilder = delegateCallers.DefineMethod(
"$delegateCaller$$" + nbDelegateCallers,
(MethodAttributes.Final | MethodAttributes.Public | MethodAttributes.Static).toShort,
msilType(returnType), (params map (_.tpe)).map(msilType).toArray)
for (i <- 0 until params.length)
caller.DefineParameter(i, ParameterAttributes.None, "arg" + i)
val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR))
mcode.Emit(OpCodes.Ldnull)
mcode.Emit(OpCodes.Ldftn, caller)
mcode.Emit(OpCodes.Newobj, delegCtor)
val functionApply: MethodInfo = getMethod(functionType.member(nme.apply))
val dcode: ILGenerator = caller.GetILGenerator()
dcode.Emit(OpCodes.Ldsfld, anonfunField)
for (i <- 0 until params.length) {
loadArg(dcode, false )(i)
emitBox(dcode, toTypeKind(params(i).tpe))
}
dcode.Emit(OpCodes.Callvirt, functionApply)
emitUnbox(dcode, toTypeKind(returnType))
dcode.Emit(OpCodes.Ret)
nbDelegateCallers = nbDelegateCallers + 1
}
def emitBox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match {
case UNIT => code.Emit(OpCodes.Ldsfld, boxedUnit)
case BOOL | BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE =>
code.Emit(OpCodes.Box, msilType(boxType))
case REFERENCE(cls) if clrTypes.isValueType(cls) =>
code.Emit(OpCodes.Box, (msilType(boxType)))
case REFERENCE(_) | ARRAY(_) =>
warning("Tried to BOX a non-valuetype.")
()
}
def emitUnbox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match {
case UNIT => code.Emit(OpCodes.Pop)
case BOOL => code.Emit(OpCodes.Call, toBool)
case BYTE => code.Emit(OpCodes.Call, toSByte)
case SHORT => code.Emit(OpCodes.Call, toShort)
case CHAR => code.Emit(OpCodes.Call, toChar)
case INT => code.Emit(OpCodes.Call, toInt)
case LONG => code.Emit(OpCodes.Call, toLong)
case FLOAT => code.Emit(OpCodes.Call, toFloat)
case DOUBLE => code.Emit(OpCodes.Call, toDouble)
case REFERENCE(cls) if clrTypes.isValueType(cls) =>
code.Emit(OpCodes.Unbox, msilType(boxType))
code.Emit(OpCodes.Ldobj, msilType(boxType))
case REFERENCE(_) | ARRAY(_) =>
warning("Tried to UNBOX a non-valuetype.")
()
}
def getConstructor(sym: Symbol): ConstructorInfo = constructors.get(sym) match {
case Some(constr) => constr
case None =>
val mClass = getType(sym.owner)
val constr = mClass.GetConstructor(msilParamTypes(sym))
if (constr eq null) {
java.lang.System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name)
java.lang.System.out.println("scope = " + sym.owner.tpe.decls)
abort(sym.fullName)
}
else {
mapConstructor(sym, constr)
constr
}
}
def mapConstructor(sym: Symbol, cInfo: ConstructorInfo) = {
constructors(sym) = cInfo
}
private def getMethod(sym: Symbol): MethodInfo = {
methods.get(sym) match {
case Some(method) => method
case None =>
val mClass = getType(sym.owner)
try {
val method = mClass.GetMethod(msilName(sym), msilParamTypes(sym),
msilType(sym.tpe.resultType))
if (method eq null) {
java.lang.System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym))
java.lang.System.out.println("scope = " + sym.owner.tpe.decls)
abort(sym.fullName)
}
else {
mapMethod(sym, method)
method
}
}
catch {
case e: Exception =>
Console.println("While looking up " + mClass + "::" + sym.nameString)
Console.println("\t" + showsym(sym))
throw e
}
}
}
private def mapMethod(sym: Symbol, mInfo: MethodInfo) {
assert (mInfo != null, mInfo)
methods(sym) = mInfo
}
private def mapMethod(sym: Symbol, newClass: MsilType, newName: String, paramTypes: Array[MsilType]) {
val methodInfo = newClass.GetMethod(newName, paramTypes)
assert(methodInfo != null, "Can't find mapping for " + sym + " -> " +
newName + "(" + paramTypes + ")")
mapMethod(sym, methodInfo)
if (methodInfo.IsStatic)
dynToStatMapped += sym
}
private def mapMethod(
clazz: Symbol, name: Name, paramTypes: Array[Type],
newClass: MsilType, newName: String, newParamTypes: Array[MsilType]) {
val methodSym = lookupMethod(clazz, name, paramTypes)
assert(methodSym != null, "cannot find method " + name + "(" +
paramTypes + ")" + " in class " + clazz)
mapMethod(methodSym, newClass, newName, newParamTypes)
}
private def mapMethod(
clazz: Symbol, name: Name, paramTypes: Array[Type],
newClass: MsilType, newName: String) {
mapMethod(clazz, name, paramTypes, newClass, newName, paramTypes map msilType)
}
private def mapMethod(
clazz: Symbol, name: Name,
newClass: MsilType, newName: String) {
val memberSym: Symbol = clazz.tpe.member(name)
memberSym.tpe match {
case OverloadedType(_, alternatives) =>
alternatives.foreach(s => mapMethod(s, newClass, newName, msilParamTypes(s)))
case MethodType(params, resType) =>
mapMethod(memberSym, newClass, newName, msilParamTypes(memberSym))
case _ =>
abort("member not found: " + clazz + ", " + name)
}
}
private def lookupMethod(clazz: Symbol, name: Name, paramTypes: Array[Type]): Symbol = {
val memberSym = clazz.tpe.member(name)
memberSym.tpe match {
case OverloadedType(_, alternatives) =>
alternatives.find(s => {
var i: Int = 0
var typesOK: Boolean = true
if (paramTypes.length == s.tpe.paramTypes.length) {
while(i < paramTypes.length) {
if (paramTypes(i) != s.tpe.paramTypes(i))
typesOK = false
i += 1
}
} else {
typesOK = false
}
typesOK
}) match {
case Some(sym) => sym
case None => abort("member of " + clazz + ", " + name + "(" +
paramTypes + ") not found")
}
case MethodType(_, _) => memberSym
case _ => abort("member not found: " + name + " of " + clazz)
}
}
private def showsym(sym: Symbol): String = (sym.toString +
"\n symbol = " + Flags.flagsToString(sym.flags) + " " + sym +
"\n owner = " + Flags.flagsToString(sym.owner.flags) + " " + sym.owner
)
}
}