diff --git a/build.xml b/build.xml index 4170ac5d3..fc645de8b 100644 --- a/build.xml +++ b/build.xml @@ -39,6 +39,7 @@ PROPERTIES + @@ -77,6 +78,7 @@ INITIALISATION + @@ -206,6 +208,7 @@ SETUP + + + + + + + + + @@ -317,6 +330,7 @@ BUILD LOCAL REFERENCE (LOCKER) LAYER + @@ -462,6 +476,7 @@ BUILD QUICK-TEST LAYER + @@ -607,6 +622,7 @@ TEST + @@ -743,6 +759,7 @@ DOCUMENTATION + @@ -851,6 +868,7 @@ GENERATES A DISTRIBUTION + diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 414f66fde..93884cc37 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -28,6 +28,7 @@ import transform._ import backend.icode.{ICodes, GenICode, Checkers} import backend.ScalaPrimitives import backend.jvm.GenJVM +import backend.msil.GenMSIL import backend.opt.{Inliners, ClosureElimination, DeadCodeElimination} import backend.icode.analysis._ @@ -170,11 +171,11 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val classPath0 = new ClassPath(false && onlyPresentation) val classPath = new classPath0.Build( - settings.classpath.value, + if (forMSIL) "" else settings.classpath.value, settings.sourcepath.value, settings.outdir.value, - settings.bootclasspath.value, - settings.extdirs.value) + if (forMSIL) "" else settings.bootclasspath.value, + if (forMSIL) "" else settings.extdirs.value) if (settings.verbose.value) { inform("[Classpath = " + classPath+"]") @@ -201,7 +202,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val global: Global.this.type = Global.this } - def rootLoader: LazyType = new loaders.PackageLoader(classPath.root /* getRoot() */) + def rootLoader: LazyType = if (forMSIL) new loaders.NamespaceLoader(classPath.root) + else new loaders.PackageLoader(classPath.root /* getRoot() */) val migrateMsg = "migration problem when moving from Scala version 1.0 to version 2.0:\n" @@ -343,6 +345,10 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val global: Global.this.type = Global.this } + object genMSIL extends GenMSIL { + val global: Global.this.type = Global.this + } + object icodeChecker extends checkers.ICodeChecker() object typer extends analyzer.Typer( @@ -371,7 +377,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable // inliner, // closureElimination, // deadCode, - genJVM, + if (forMSIL) genMSIL else genJVM, sampleTransform) protected def insertBefore(c: SubComponent, cs: List[SubComponent], before: SubComponent): List[SubComponent] = cs match { @@ -633,6 +639,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable } def forCLDC: Boolean = settings.target.value == "cldc" + def forMSIL: Boolean = settings.target.value == "msil" def onlyPresentation = settings.doc.value // position stuff final val positionConfiguration: PositionConfiguration = initConfig; diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index cc9f9b21c..a84ce6ecc 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -92,6 +92,8 @@ class Settings(error: String => unit) { val documenttitle = StringSetting ("-documenttitle", "documenttitle", "Specify document title of generated HTML documentation", documenttitleDefault) val target = ChoiceSetting ("-target", "Specify which backend to use", List("jvm-1.5", "jvm-1.4", "msil", "cldc"), "jvm-1.4") val migrate = BooleanSetting("-migrate", "Assist in migrating from Scala version 1.0") + val assemname = StringSetting ("-o", "file", "Name of the output assembly (only relevant with -target:msil)", "") + val assemrefs = StringSetting ("-r", "path", "List of assemblies referenced by the program (only relevant with -target:msil)", ".") val debug = new BooleanSetting("-debug", "Output debugging messages") { override def hiddenToIDE = true } val deprecation = BooleanSetting ("-deprecation", "enable detailed deprecation warnings") val unchecked = BooleanSetting ("-unchecked", "enable detailed unchecked warnings") diff --git a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala index 74319dfb0..7b4e72abd 100644 --- a/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala +++ b/src/compiler/scala/tools/nsc/backend/ScalaPrimitives.scala @@ -445,7 +445,7 @@ abstract class ScalaPrimitives { /** Add a primitive operation to the map */ def addPrimitive(s: Symbol, code: Int): Unit = { assert(!(primitives contains s), "Duplicate primitive " + s) - primitives += s -> code + primitives(s) = code } def addPrimitives(cls: Symbol, method: Name, code: Int): Unit = { diff --git a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala index be6f273c9..565729d39 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/ExceptionHandlers.scala @@ -23,7 +23,9 @@ trait ExceptionHandlers requires ICodes { class ExceptionHandler(val method: IMethod, val label: String, val cls: Symbol) { private var _startBlock: BasicBlock = _; var finalizer: Finalizer = _; - + + var resultKind: TypeKind = _; + def setStartBlock(b: BasicBlock) = _startBlock = b; def startBlock = _startBlock; diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 92a7e524c..137a22b21 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -480,14 +480,14 @@ abstract class GenICode extends SubComponent { val kind = toTypeKind(tree.tpe) var handlers = for (val CaseDef(pat, _, body) <- catches.reverse) yield pat match { - case Typed(Ident(nme.WILDCARD), tpt) => Pair(tpt.tpe.symbol, { + case Typed(Ident(nme.WILDCARD), tpt) => Triple(tpt.tpe.symbol, kind, { ctx: Context => ctx.bb.emit(DROP(REFERENCE(tpt.tpe.symbol))); val ctx1 = genLoad(body, ctx, kind); genLoad(finalizer, ctx1, UNIT); }) - case Ident(nme.WILDCARD) => Pair(definitions.ThrowableClass, { + case Ident(nme.WILDCARD) => Triple(definitions.ThrowableClass, kind, { ctx: Context => ctx.bb.emit(DROP(REFERENCE(definitions.ThrowableClass))) val ctx1 = genLoad(body, ctx, kind) @@ -498,7 +498,7 @@ abstract class GenICode extends SubComponent { val exception = new Local(pat.symbol, toTypeKind(pat.symbol.tpe), false) ctx.method.addLocal(exception); - Pair(pat.symbol.tpe.symbol, { + Triple(pat.symbol.tpe.symbol, kind, { ctx: Context => ctx.bb.emit(STORE_LOCAL(exception), pat.pos); val ctx1 = genLoad(body, ctx, kind); @@ -507,7 +507,7 @@ abstract class GenICode extends SubComponent { } if (finalizer != EmptyTree) - handlers = Pair(NoSymbol, { + handlers = Triple(NoSymbol, kind, { ctx: Context => val exception = new Local(ctx.method.symbol.newVariable(finalizer.pos, unit.fresh.newName("exc")) .setFlag(Flags.SYNTHETIC) @@ -686,10 +686,10 @@ abstract class GenICode extends SubComponent { ctx1.bb.close ctx1.newBlock - } else if (isPrimitive(fun.symbol)) { // primitive method call + } else if (isPrimitive(sym)) { // primitive method call val Select(receiver, _) = fun - val code = scalaPrimitives.getPrimitive(fun.symbol, receiver.tpe) + val code = scalaPrimitives.getPrimitive(sym, receiver.tpe) var ctx1 = ctx if (scalaPrimitives.isArithmeticOp(code)) { @@ -733,6 +733,7 @@ abstract class GenICode extends SubComponent { if (settings.debug.value) log("synchronized block start"); + ctx1 = ctx1.Try( bodyCtx => { val ctx1 = genLoad(args.head, bodyCtx, expectedType /* toTypeKind(tree.tpe.resultType) */) @@ -740,7 +741,8 @@ abstract class GenICode extends SubComponent { ctx1.bb.emit(MONITOR_EXIT(), tree.pos) ctx1 }, List( - Pair(NoSymbol, exhCtx => { + // tree.tpe / fun.tpe is object, which is no longer true after this transformation + Triple(NoSymbol, expectedType, exhCtx => { exhCtx.bb.emit(LOAD_LOCAL(monitor)) exhCtx.bb.emit(MONITOR_EXIT(), tree.pos) exhCtx.bb.emit(THROW()) @@ -756,9 +758,8 @@ abstract class GenICode extends SubComponent { genCoercion(tree, ctx1, code) generatedType = scalaPrimitives.generatedKind(code) } else - abort("Primitive operation not handled yet: " + - fun.symbol.fullNameString + "(" + fun.symbol.simpleName + ") " - + " at: " + unit.position(tree.pos)); + abort("Primitive operation not handled yet: " + sym.fullNameString + "(" + + fun.symbol.simpleName + ") " + " at: " + unit.position(tree.pos)); ctx1 } else { // normal method call if (settings.debug.value) @@ -775,7 +776,7 @@ abstract class GenICode extends SubComponent { if (invokeStyle.hasInstance) genLoadQualifier(fun, ctx) else ctx - ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx1) + ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx1) val hostClass = fun match { case Select(qualifier, _) @@ -983,6 +984,11 @@ abstract class GenICode extends SubComponent { def isStaticSymbol(s: Symbol): Boolean = s.hasFlag(Flags.STATIC) || s.hasFlag(Flags.STATICMEMBER) || s.owner.isImplClass +// def isUnbox(s: Symbol): Boolean = ( +// s.name.==(nme.unbox) && s.owner.isModuleClass +// && s.owner.linkedClassOfClass.isSubClass(definitions.AnyValClass) +// ) + /** * Generate code that loads args into label parameters. */ @@ -1044,7 +1050,7 @@ abstract class GenICode extends SubComponent { * @param ctx ... * @param cast ... */ - def genBoxedConversion(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean) = { + def genBoxedConversion(from: TypeKind, to: TypeKind, ctx: Context, cast: Boolean): Unit = { assert(to.isValueType || to.isArrayType, "Expecting conversion to value type: " + to) @@ -1054,7 +1060,12 @@ abstract class GenICode extends SubComponent { case ARRAY(elem) => definitions.boxedArrayClass(elem.toType.symbol) case _ => - definitions.boxedClass(to.toType.symbol) + if (cast) { + ctx.bb.emit(UNBOX(to)) + return + } + else + definitions.boxedClass(to.toType.symbol) } if (cast) { @@ -1709,9 +1720,10 @@ abstract class GenICode extends SubComponent { * 'covered' by this exception handler (in addition to the * previously active handlers). */ - def newHandler(cls: Symbol): ExceptionHandler = { + def newHandler(cls: Symbol, resultKind: TypeKind): ExceptionHandler = { handlerCount = handlerCount + 1 val exh = new ExceptionHandler(method, "" + handlerCount, cls) + exh.resultKind = resultKind method.addHandler(exh) handlers = exh :: handlers if (settings.debug.value) @@ -1762,14 +1774,14 @@ abstract class GenICode extends SubComponent { * } )) */ def Try(body: Context => Context, - handlers: List[Pair[Symbol, (Context => Context)]], + handlers: List[Triple[Symbol, TypeKind, (Context => Context)]], finalizer: Tree) = { val outerCtx = this.dup val afterCtx = outerCtx.newBlock val exhs = handlers.map { handler => - val exh = this.newHandler(handler._1) - val ctx1 = handler._2(outerCtx.enterHandler(exh)) + val exh = this.newHandler(handler._1, handler._2) + val ctx1 = handler._3(outerCtx.enterHandler(exh)) ctx1.bb.emit(JUMP(afterCtx.bb)) ctx1.bb.close exh diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index 0017d3edd..f64576d34 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -32,23 +32,7 @@ trait TypeKinds requires ICodes { * represent the types that the VM know (or the ICode * view of what VMs know). */ - abstract class TypeKind { - - /** Returns a string representation of this type kind. */ - override def toString(): String = this match { - case UNIT => "UNIT" - case BOOL => "BOOL" - case BYTE => "BYTE" - case SHORT => "SHORT" - case CHAR => "CHAR" - case INT => "INT" - case LONG => "LONG" - case FLOAT => "FLOAT" - case DOUBLE => "DOUBLE" - case REFERENCE(cls) => "REFERENCE(" + cls.fullNameString + ")" - case ARRAY(elem) => "ARRAY[" + elem + "]" - case _ => abort("Unkown type kind.") - } + sealed abstract class TypeKind { def toType: Type = this match { case UNIT => definitions.UnitClass.tpe @@ -61,6 +45,7 @@ trait TypeKinds requires ICodes { case FLOAT => definitions.FloatClass.tpe case DOUBLE => definitions.DoubleClass.tpe case REFERENCE(cls) => typeRef(cls.typeConstructor.prefix, cls, Nil) + //case VALUE(cls) => typeRef(cls.typeConstructor.prefix, cls, Nil); case ARRAY(elem) => typeRef(definitions.ArrayClass.typeConstructor.prefix, definitions.ArrayClass, elem.toType :: Nil) @@ -216,7 +201,7 @@ trait TypeKinds requires ICodes { // } /** A class type. */ - case class REFERENCE(cls: Symbol) extends TypeKind { + final case class REFERENCE(cls: Symbol) extends TypeKind { assert(cls ne null, "REFERENCE to null class symbol.") assert(cls != definitions.ArrayClass, @@ -260,8 +245,20 @@ trait TypeKinds requires ICodes { } } +// final case class VALUE(cls: Symbol) extends TypeKind { +// override def equals(other: Any): Boolean = other match { +// case VALUE(cls2) => cls == cls2; +// case _ => false; +// } - case class ARRAY(val elem: TypeKind) extends TypeKind { +// def maxType(other: TypeKind): TypeKind = +// abort(toString() + " maxType " + other.toString()); + +// override def toString(): String = +// "VALUE(" + cls.fullNameString + ")"; +// } + + final case class ARRAY(val elem: TypeKind) extends TypeKind { override def toString(): String = "ARRAY[" + elem + "]" diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala new file mode 100644 index 000000000..e4f66a861 --- /dev/null +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -0,0 +1,2464 @@ +/* NSC -- new scala compiler + * Copyright 2005-2007 LAMP/EPFL + */ + +// $Id$ + +package scala.tools.nsc.backend.msil + +import java.io.File +import java.nio.{ByteBuffer, ByteOrder} + +import scala.collection.mutable.{Map, HashMap, HashSet, Stack} +import scala.tools.nsc.symtab._ +import scala.tools.nsc.util.Position +import compat.StringBuilder + +import ch.epfl.lamp.compiler.msil.{Type => MsilType, _} +import ch.epfl.lamp.compiler.msil.emit._ + +/** + */ +abstract class GenMSIL extends SubComponent { + import global._ + import loaders.clrTypes + import clrTypes.{types, constructors, methods, fields} + import icodes._ + import icodes.opcodes._ + + /** Create a new phase */ + override def newPhase(p: Phase) = new MsilPhase(p) + + val phaseName = "msil" + /** MSIL code generation phase + */ + class MsilPhase(prev: Phase) extends GlobalPhase(prev) { + def name = phaseName + override def newFlags = phaseNewFlags + + override def erasedTypes = true + + override def run: Unit = { + if (settings.debug.value) inform("[running phase " + name + " on icode]") + + val codeGenerator = new BytecodeGenerator + + //classes is ICodes.classes, a HashMap[Symbol, IClass] + classes.values foreach codeGenerator.findEntryPoint + + codeGenerator.initAssembly + + classes.values foreach codeGenerator.createTypeBuilder + classes.values foreach codeGenerator.createClassMembers + + + try { + classes.values foreach codeGenerator.genClass + } finally { + codeGenerator.writeAssembly + } + } + + override def apply(unit: CompilationUnit): Unit = + abort("MSIL works on icode classes, not on compilation units!") + } + + /** + * MSIL bytecode generator. + * + */ + class BytecodeGenerator { + + val MODULE_INSTANCE_NAME = "MODULE$" + + import clrTypes.{VOID => MVOID, BOOLEAN => MBOOL, UBYTE => MBYTE, SHORT => MSHORT, + CHAR => MCHAR, INT => MINT, LONG => MLONG, FLOAT => MFLOAT, + DOUBLE => MDOUBLE, OBJECT => MOBJECT, STRING => MSTRING, + STRING_ARRAY => MSTRING_ARRAY, SCALA_SYMTAB_ATTR => SYMTAB_ATTRIBUTE, + 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 + + var JSTRING_SUBSTRING_INT_INT: Symbol = _ + + val SystemConvert = clrTypes.getType("System.Convert") + + val objParam = Array(MOBJECT) + +// val toBool: MethodInfo = SystemConvert.GetMethod("ToBoolean", objParam) + val toByte: MethodInfo = SystemConvert.GetMethod("ToByte", 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 = msilType(definitions.BoxedUnitModule.info).GetField("UNIT") + val boxedUnit: FieldInfo = fields(definitions.BoxedUnit_UNIT) + + // Scala attributes + // symtab.Definitions -> object (singleton..) + val SerializableAttr = definitions.SerializableAttr.tpe + val CloneableAttr = definitions.getClass("scala.cloneable").tpe + val TransientAtt = definitions.getClass("scala.transient").tpe + // remoting: the architectures are too different, no mapping (no portable code + // possible) + + // java instance methods that are mapped to static methods in .net + // these will need to be called with OpCodes.Call (not Callvirt) + val dynToStatMapped: HashSet[Symbol] = new HashSet() + + initMappings() + // ******************************************************************** + // Create the mappings + private def initMappings(): Unit = { + mapType(definitions.AnyClass, MOBJECT) + mapType(definitions.AnyRefClass, MOBJECT) + //mapType(definitions.AllRefClass, clrTypes.getType("scala.AllRef$")) + //mapType(definitions.AllClass, clrTypes.getType("scala.All$")) + // FIXME: for some reason the upper two lines map to null + mapType(definitions.AllRefClass, EXCEPTION) + mapType(definitions.AllClass, EXCEPTION) + + val jEmpty = new Array[Type](0) + val jString1 = Array(JSTRING.tpe) + val jInt1 = Array(definitions.IntClass.tpe) + val jInt2 = Array(definitions.IntClass.tpe, definitions.IntClass.tpe) + val jLong1 = Array(definitions.LongClass.tpe) + val jStringInt = Array(JSTRING.tpe, definitions.IntClass.tpe) + val jChar2 = Array(definitions.CharClass.tpe, definitions.CharClass.tpe) + + val mObject1 = Array(MOBJECT) + val mString1 = Array(MSTRING) + val mString2 = Array(MSTRING, MSTRING) + val mChar1 = Array(MCHAR) + val mCharInt = Array(MCHAR, MINT) + + JSTRING_SUBSTRING_INT_INT = lookupMethod(JSTRING, "substring", jInt2) + + mapMethod(JOBJECT, "clone", MOBJECT, "MemberwiseClone") + mapMethod(JOBJECT, nme.equals_, MOBJECT, "Equals") + mapMethod(JOBJECT, nme.hashCode_, MOBJECT, "GetHashCode") + mapMethod(JOBJECT, nme.toString_, MOBJECT, "ToString") + mapMethod(JOBJECT, nme.finalize_, MOBJECT, "Finalize") + mapMethod(JOBJECT, nme.wait_, jEmpty, MMONITOR, "Wait", mObject1) + mapMethod(JOBJECT, nme.wait_, jLong1, MMONITOR, "Wait", Array(MOBJECT, MINT)) + mapMethod(JOBJECT, nme.notify_, jEmpty, MMONITOR, "Pulse", mObject1) + mapMethod(JOBJECT, nme.notifyAll_, jEmpty, MMONITOR, "PulseAll", mObject1) + + mapMethod(JSTRING, "compareTo",MSTRING, "CompareTo") + mapMethod(JSTRING, "length", MSTRING, "get_Length") + mapMethod(JSTRING, "charAt", MSTRING, "get_Chars") + + mapMethod(JSTRING, "concat", jString1, MSTRING, "Concat", mString2) + mapMethod(JSTRING, "indexOf", jInt1, MSTRING, "IndexOf", mChar1) + mapMethod(JSTRING, "indexOf", jInt2, MSTRING, "IndexOf", mCharInt) + + mapMethod(JSTRING, "indexOf", jString1, MSTRING, "IndexOf") + mapMethod(JSTRING, "indexOf", jStringInt, MSTRING, "IndexOf") + mapMethod(JSTRING, "lastIndexOf", jInt1, MSTRING, "LastIndexOf", mChar1) + mapMethod(JSTRING, "lastIndexOf", jInt2, MSTRING, "LastIndexOf", mCharInt) + mapMethod(JSTRING, "lastIndexOf", jString1, MSTRING, "LastIndexOf") + mapMethod(JSTRING, "lastIndexOf", jStringInt, MSTRING, "LastIndexOf") + + mapMethod(JSTRING, "toLowerCase", jEmpty, MSTRING, "ToLower") + mapMethod(JSTRING, "toUpperCase", jEmpty, MSTRING, "ToUpper") + mapMethod(JSTRING, "startsWith", jString1, MSTRING, "StartsWith") + mapMethod(JSTRING, "endsWith", jString1, MSTRING, "EndsWith") + mapMethod(JSTRING, "substring", jInt1, MSTRING, "Substring") + mapMethod(JSTRING, "substring", jInt2, MSTRING, "Substring") + mapMethod(JSTRING, "trim", jEmpty, MSTRING, "Trim") + mapMethod(JSTRING, "intern", jEmpty, MSTRING, "Intern", mString1) + mapMethod(JSTRING, "replace", jChar2, MSTRING, "Replace") + mapMethod(JSTRING, "toCharArray", MSTRING, "ToCharArray") + + 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 code: Code = _ + + var massembly: AssemblyBuilder = _ + var mmodule: ModuleBuilder = _ + var mcode: ILGenerator = _ + + var assemName : String = _ + var firstSourceName = "" + var outDir : File = _ + + def initAssembly(): Unit = { + + assemName = settings.assemname.value + if (assemName == "") { + + if (entryPoint != null) { + assemName = msilName(entryPoint.enclClass) + // remove the $ at the end (from module-name) + assemName = assemName.substring(0, assemName.length() - 1) + } else { + // assuming filename of first source file + assert(firstSourceName.endsWith(".scala"), "Source file doesn't end with .scala") + 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) + outDir = f.getParentFile() + assemName = f.getName() + } + if (outDir == null) + outDir = new File(".") + + + val assemblyName = new AssemblyName() + assemblyName.Name = assemName + massembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName) + + val moduleName = assemName + (if (entryPoint == null) ".dll" else ".exe") + // filename here: .dll or .exe (in both parameters), second: give absolute-path + mmodule = massembly.DefineDynamicModule(moduleName, + new File(outDir, moduleName).getAbsolutePath()) + assert (mmodule != null) + initMappings() + + } + + + /** + * Form of the custom Attribute parameter (Ecma-335.pdf) + * - p. 163 for CustomAttrib Form, + * - p. 164 for FixedArg Form (Array and Element) (if array or not is known!) + * !! least significant *byte* first if values longer than one byte !! + * + * 1: Prolog (unsigned int16, value 0x0001) -> symtab[0] = 0x01, symtab[1] = 0x00 + * 2: FixedArgs (directly the data, get number and types from related constructor) + * 2.1: length of the array (unsigned int32, take care on order of the 4 bytes) + * 2.2: the byte array data + * 3: NumNamed (unsigned int16, number of named fields and properties, 0x0000) + * + **/ + def addSymtabAttribute(sym: Symbol, tBuilder: TypeBuilder): Unit = currentRun.symData.get(sym) match { + case Some(pickle) => + val symtab: Array[Byte] = new Array[Byte](pickle.writeIndex + 8) + symtab(0) = 1.toByte + var size:Int = pickle.writeIndex + for(val i <- Iterator.range(2, 6)) { + symtab(i) = (size & 0xff).toByte + size = size >> 8 + } + + System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex) + + tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab) + + currentRun.symData -= sym + currentRun.symData -= sym.linkedSym + //log("Generated ScalaSig Attr for " + sym)//debug + case _ => + log("Could not find pickle information for " + sym) + } + + def addAttributes(member: ICustomAttributeSetter, attributes: List[AttrInfo]): Unit = { + return // FIXME + + if (settings.debug.value) + log("creating attributes: " + attributes + " for member : " + member) + for(val AttrInfo(typ, consts, nvPairs) <- attributes /* !typ.symbol.hasFlag(Flags.JAVA) */ ) { +// assert(consts.length <= 1, +// "too many constant arguments for attribute; "+consts.toString()) + + // Problem / TODO having the symbol of the attribute type would be nicer + // (i hope that type.symbol is the same as the one in types2create) + // AND: this will crash if the attribute Type is already compiled (-> not a typeBuilder) + // when this is solved, types2create will be the same as icodes.classes, thus superfluous + val attrType: TypeBuilder = getType(typ.symbol).asInstanceOf[TypeBuilder] +// val attrType: MsilType = getType(typ.symbol) + + // Problem / TODO: i have no idea which constructor is used. This + // information should be available in AttrInfo. + attrType.CreateType() // else, GetConstructors can't be used + val constr: ConstructorInfo = attrType.GetConstructors()(0) + // prevent a second call of CreateType, only needed because there's no + // otehr way than GetConstructors()(0) to get the constructor, if there's + // no constructor symbol available. + + val args: Array[Byte] = getAttributeArgs(consts, nvPairs) + member.SetCustomAttribute(constr, args) + } + } + + def getAttributeArgs(consts: List[Constant], nvPairs: List[Pair[Name, Constant]]): Array[Byte] = { + val buf = ByteBuffer.allocate(2048) // FIXME: this may be not enough! + buf.order(ByteOrder.LITTLE_ENDIAN) + buf.putShort(1.toShort) // signature + + def emitSerString(str: String) = { + // this is wrong, it has to be the length of the UTF-8 byte array, which + // may be longer (see clr-book on page 302) +// val length: Int = str.length + val strBytes: Array[Byte] = try { + str.getBytes("UTF-8") + } catch { + case _: Error => abort("could not get byte-array for string: " + str) + } + val length: Int = strBytes.length //this length is stored big-endian + if (length < 128) + buf.put(length.toByte) + else if (length < (1<<14)) { + buf.put(((length >> 8) | 0x80).toByte) // the bits 14 and 15 of length are '0' + buf.put((length | 0xff).toByte) + } else if (length < (1 << 29)) { + buf.put(((length >> 24) | 0xc0).toByte) + buf.put(((length >> 16) & 0xff).toByte) + buf.put(((length >> 8) & 0xff).toByte) + buf.put(((length ) & 0xff).toByte) + } else + abort("string too long for attribute parameter: " + length) + buf.put(strBytes) + } + + def emitConst(const: Constant): Unit = const.tag match { + case BooleanTag => buf.put((if (const.booleanValue) 1 else 0).toByte) + case ByteTag => buf.put(const.byteValue) + case ShortTag => buf.putShort(const.shortValue) + case CharTag => buf.putChar(const.charValue) + case IntTag => buf.putInt(const.intValue) + case LongTag => buf.putLong(const.longValue) + case FloatTag => buf.putFloat(const.floatValue) + case DoubleTag => buf.putDouble(const.doubleValue) + case StringTag => + val str: String = const.stringValue + if (str == null) { + buf.put(0xff.toByte) + } else { + emitSerString(str) + } + case ArrayTag => + val arr: Array[Constant] = const.arrayValue + if (arr == null) { + buf.putInt(0xffffffff) + } else { + buf.putInt(arr.length) + arr.foreach(emitConst) + } + + // TODO: other Tags: NoTag, UnitTag, ClassTag, EnumTag, ArrayTag ??? + + case _ => abort("could not handle attribute argument: " + const) + } + + consts foreach emitConst + buf.putShort(nvPairs.length.toShort) + def emitNamedArg(nvPair: Pair[Name, Constant]): Unit = { + // the named argument is a property of the attribute (it can't be a field, since + // all fields in scala are private) + buf.put(0x54.toByte) + + def emitType(c: Constant) = c.tag match { // type of the constant, Ecma-335.pdf, page 151 + case BooleanTag => buf.put(0x02.toByte) + case ByteTag => buf.put(0x05.toByte) + case ShortTag => buf.put(0x06.toByte) + case CharTag => buf.put(0x07.toByte) + case IntTag => buf.put(0x08.toByte) + case LongTag => buf.put(0x0a.toByte) + case FloatTag => buf.put(0x0c.toByte) + case DoubleTag => buf.put(0x0d.toByte) + case StringTag => buf.put(0x0e.toByte) + + // TODO: other Tags: NoTag, UnitTag, ClassTag, EnumTag ??? + + // ArrayTag falls in here + case _ => abort("could not handle attribute argument: " + c) + } + + val cnst: Constant = nvPair._2 + if (cnst.tag == ArrayTag) { + buf.put(0x1d.toByte) + emitType(cnst.arrayValue(0)) // FIXME: will crash if array length = 0 + } else if (cnst.tag == EnumTag) { + buf.put(0x55.toByte) + // TODO: put a SerString (don't know what exactly, names of the enums somehow..) + } else { + buf.put(0x51.toByte) + emitType(cnst) + } + + emitSerString(nvPair._1.toString) + emitConst(nvPair._2) + } + + val length = buf.position() + buf.array().subArray(0, length) + } + + def writeAssembly(): Unit = { + if (entryPoint != null) { + assert(entryPoint.enclClass.isModuleClass, "main-method not defined in a module") + 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() + val filename = new File(outDir, assemName + ".msil").getPath() + if (settings.debug.value) + log("output file name: " + filename) + try { + massembly.Save(filename) + } catch { + case _: Error => abort("Could not save file " + filename) + } + } + + private def createTypes(): Unit = + for (val sym <- classes.keys) { + if (settings.debug.value) + log("Calling CreatType for " + sym + ", " + types(sym)) + types(sym).asInstanceOf[TypeBuilder].CreateType() + } + + private[GenMSIL] def genClass(iclass: IClass): Unit = { + 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)){ + // FIXME: why there's no nme.clone_ ? + // "Clone": if the code is non-portable, "Clone" is defined, not "clone" + // TODO: improve condition (should override AnyRef.clone) + 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) + 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) + } + } + + tBuilder.setPosition(iclass.cunit.position(sym.pos).line, iclass.cunit.source.file.name) + + if (isTopLevelModule(sym)) { + if (settings.debug.value) + log("TopLevelModule: " + sym) + if (sym.linkedClassOfModule == NoSymbol) { + if (settings.debug.value) + log(" no linked class: " + sym) + dumpMirrorClass(sym) + } else if (!currentRun.compiles(sym.linkedClassOfModule)) { + if (settings.debug.value) + log(" not compiling linked class: " + sym) + dumpMirrorClass(sym) + } + } + + // the pickling info is not written to the module class, but to it's + // linked class (the mirror class eventually dumped) + if (!(tBuilder.Name.endsWith("$") && sym.isModuleClass)){ + // think the if inside could be removed, because in this case, addSymtabAttribute is + // called in the dumpMirrorClass method + addSymtabAttribute(if (isTopLevelModule(sym)) sym.sourceModule else sym, tBuilder) + + // TODO: remove; check the above think: + assert(!isTopLevelModule(sym), "can't remove the 'if'") + } + + addAttributes(tBuilder, sym.attributes) + + if (iclass.symbol != definitions.ArrayClass) + iclass.methods foreach genMethod + + } //genClass + + + private 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 + 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 => + System.out.println("m.symbol = " + Flags.flagsToString(m.symbol.flags) + " " + m.symbol) + System.out.println("m.symbol.owner = " + Flags.flagsToString(m.symbol.owner.flags) + " " + m.symbol.owner) + System.out.println("mBuilder = " + mBuilder) + System.out.println("mBuilder.DeclaringType = " + + TypeAttributes.toString(mBuilder.DeclaringType.Attributes) + + "::" + mBuilder.DeclaringType) + throw e + } + else + mcode = null + } + + if (mcode != null) { + for (val local <- (m.locals.diff(m.params))) { + 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) + } + + } + + var linearization: List[BasicBlock] = Nil + // a "ret" instruction is needed (which is not present in + // icode) if there's no code after a try-catch block + var needAdditionalRet: Boolean = false + + def genCode(m: IMethod): Unit = { + + code = m.code + + labels.clear + linearization = linearizer.linearize(m) + val orderedBlocks = (if (m.exh != Nil) orderBlocksForExh(linearization, m.exh) + else linearization) + + makeLabels(orderedBlocks) // orderBlocksForExh may create new Blocks -> new Labels + genBlocks(orderedBlocks) + if (needAdditionalRet) { + mcode.Emit(OpCodes.Ret) + needAdditionalRet = false + } + } + + abstract class ExHInstruction(handler: ExceptionHandler) { } + case class BeginExceptionBlock(handler: ExceptionHandler) extends ExHInstruction(handler) + case class BeginCatchBlock(handler: ExceptionHandler, exceptionType: MsilType) extends ExHInstruction(handler) + case class BeginFinallyBlock(handler: ExceptionHandler) extends ExHInstruction(handler) + case class EndExceptionBlock(handler: ExceptionHandler) extends ExHInstruction(handler) + + + abstract class Block { + var closed: Boolean = false + def parentBlockList: Option[BlockList0] + def firstBasicBlock: BasicBlock + def lastBasicBlock: BasicBlock +// def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] + def close(): Unit +/* protected def findExceptionBlock(list: List[Block], exh: ExceptionHandler): Option[ExceptionBlock] = { + var res: Option[ExceptionBlock] = None + var i: Int = 0 + while (i < list.length && res == None) { + val b = list(i) + val exB = b.getExceptionBlock(exh) + exB match { + case some: Some[ExceptionBlock] => res = some + case None => () + } + i = i + 1 + } + res + } */ + } + case class CodeBlock(parent: BlockList0) extends Block { + var basicBlocks: List[BasicBlock] = Nil + def isEmpty = basicBlocks.isEmpty + override def firstBasicBlock: BasicBlock = { + if(isEmpty) null + else { + if (closed) basicBlocks.head + else basicBlocks.last + } + } + override def lastBasicBlock: BasicBlock = { + if(isEmpty) null + else { + if (closed) basicBlocks.last + else basicBlocks.head + } + } + override def parentBlockList = Some(parent) +// override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = None + override def close(): Unit = { + basicBlocks = basicBlocks.reverse + closed = true + } + override def toString() = { + var res = "" + res = res + TopBlock.indent + "CodeBlock(" + basicBlocks + ")\n" + res + } + } + abstract class BlockList0 extends Block { + var blocks: List[Block] = Nil + override def firstBasicBlock: BasicBlock = { + if(blocks.isEmpty) null + else { + if (closed) blocks.head.firstBasicBlock + else blocks.last.firstBasicBlock + } + } + override def lastBasicBlock: BasicBlock = { + if(blocks.isEmpty) null + else { + if (closed) blocks.last.lastBasicBlock + else blocks.head.lastBasicBlock + } + } +/* override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = { + findExceptionBlock(blocks, exh) + } */ + def addExceptionBlock(exh: ExceptionHandler) = { + if (settings.debug.value) + log("new exc block with " + exh + " to " + this) + val e = new ExceptionBlock(this, exh) + blocks = e :: blocks + e + } + def addBasicBlock(bb: BasicBlock) = { + if (settings.debug.value) + log("adding bb " + bb + " to " + this) + var cb: CodeBlock = if (!blocks.isEmpty) { + blocks.head match { + case blk: CodeBlock => blk + case _ => null + } + } else null + if (cb == null) { + cb = new CodeBlock(this) + blocks = cb :: blocks + } + cb.basicBlocks = bb :: cb.basicBlocks + } + override def close(): Unit = { + blocks.foreach(.close) + blocks = blocks.reverse + closed = true + } + override def toString() = { + var res = "" + res = res + TopBlock.indent + "BlockList0:\n" + TopBlock.indent = TopBlock.indent + " " + for(val b <- blocks) + res = res + b + "\n" + TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-2) + res + } + } + case class BlockList(parent: Block) extends BlockList0 { + override def parentBlockList: Option[BlockList0] = { + if (parent == TopBlock) + Some(TopBlock) + else parent match { + case bl: BlockList => Some(bl) + case cb: CatchBlock => Some(cb) + case _ => parent.parentBlockList + } + } + override def toString() = { + var res = "" + res = res + TopBlock.indent + "BlockList:\n" + res = res + super.toString() + res + } + } + case class ExceptionBlock(parent: Block, handler: ExceptionHandler) extends Block { + var tryBlock: BlockList = new BlockList(this) + var catchBlocks: List[CatchBlock] = Nil + var finallyBlock: BlockList = new BlockList(this) + override def firstBasicBlock = { + tryBlock.firstBasicBlock + } + override def lastBasicBlock = { + if (!finallyBlock.blocks.isEmpty) + finallyBlock.lastBasicBlock + else if(!catchBlocks.isEmpty) { + if (closed) catchBlocks.last.lastBasicBlock + else catchBlocks.head.lastBasicBlock + } else { + tryBlock.lastBasicBlock + } + } + override def parentBlockList: Option[BlockList0] = { + if (parent == TopBlock) + Some(TopBlock) + else parent match { + case bl: BlockList => Some(bl) + case cb: CatchBlock => Some(cb) + case _ => parent.parentBlockList + } + } +/* override def getExceptionBlock(exh: ExceptionHandler): Option[ExceptionBlock] = { + if (exh == handler) Some(this) + else { + val t = if (tryBlock == null) Nil else List(tryBlock) + val f = if (finallyBlock == null) Nil else List(finallyBlock) + findExceptionBlock(t ::: catchBlocks ::: f, exh) + } + } +*/ + def addCatchBlock(exSym: Symbol): CatchBlock = { + if (settings.debug.value) + log("new catch block with " + exSym + " to " + this) + val c = new CatchBlock(this, exSym) + catchBlocks = c :: catchBlocks + c + } + override def close(): Unit = { + tryBlock.close + catchBlocks.foreach(.close) + catchBlocks = catchBlocks.reverse + finallyBlock.close + closed = true + } + override def toString() = { + var res = "" + res = res + TopBlock.indent + "ExceptionBlock, handler: " + handler + "\n" + res = res + TopBlock.indent + " " + "try:\n" + TopBlock.indent = TopBlock.indent + " " + res = res + tryBlock + "\n" + TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4) + res = res + TopBlock.indent + " " + "catch:\n" + TopBlock.indent = TopBlock.indent + " " + for(val b <- catchBlocks) + res = res + b + "\n" + TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4) + res = res + TopBlock.indent + " " + "finally:\n" + TopBlock.indent = TopBlock.indent + " " + res = res + finallyBlock + "\n" + TopBlock.indent = TopBlock.indent.substring(0,TopBlock.indent.length-4) + res + } + } + case class CatchBlock(parent: ExceptionBlock, exSym: Symbol) extends BlockList0 { + override def parentBlockList: Option[BlockList0] = { + parent.parentBlockList + } + override def toString() = { + var res = "" + res = res + TopBlock.indent + "CatchBlock:\n" + res = res + super.toString() + res + } + } + case object TopBlock extends BlockList0 { + var indent = "" + override def parentBlockList = None + override def toString() = { + var res = "" + res = res + TopBlock.indent + "TopBlock:\n" + res = res + super.toString() + res + } + } + + // for every basic block, a list of ExHInstructions to be executed: + // - Begin_ are executed before the block + // - EndExceptionBlock is executed after the block + val bb2exHInstructions: HashMap[BasicBlock, List[ExHInstruction]] = new HashMap() + // at the end of a try, catch or finally block, the jumps must not be emitted, + // the automatically generated leave (or endfinally) will do the job. + val omitJumpBlocks: HashSet[BasicBlock] = new HashSet() + + // suposes that finalizers are the same for different handlers + // covering the same blocks + def orderBlocksForExh(blocks: List[BasicBlock], exH: List[ExceptionHandler]): List[BasicBlock] = { + + var blocksToPut: List[BasicBlock] = blocks + var nextBlock: BasicBlock = null + var untreatedHandlers: List[ExceptionHandler] = exH + TopBlock.blocks = Nil + var currentBlock: BlockList0 = TopBlock + def addBlocks(b: List[BasicBlock]):Unit = b match { + case Nil => if (settings.debug.value) log("adding " + b) + + case x :: xs => + if (settings.debug.value) log("adding " + b) + // problem: block may already be added, and and needs to be moved. + // if nextblock NOT in b: check if nextblock in blocksToPut, if NOT, check if movable, else don't put + if (nextBlock != null && b.contains(nextBlock)) { + val blocksToAdd = nextBlock :: b.diff(List(nextBlock)) + nextBlock = null + addBlocks(blocksToAdd) + } + else if (untreatedHandlers.forall(h => !(h.covers(x)))) { + + if (settings.debug.value) log(" no new handler for " + x) + if (untreatedHandlers.forall(h => !(h.blocks.contains(x) || + (h.finalizer != null && + h.finalizer.covers(x))))) + { + // the block is not part of some catch or finally code + currentBlock.addBasicBlock(x) + blocksToPut = blocksToPut.diff(List(x)) + if (settings.debug.value) log(" -> addBlocks(" + xs + ")") + addBlocks(xs) + } else { + if (settings.debug.value) log("x is part of catch or finally block") + + // check if the covered code of the handler x belongs to is empty + // this check is not needed for finalizers: empty try with finalizer + // is optimized by compiler (no try left) + if(untreatedHandlers.forall(h => + (!h.blocks.contains(x) || h.covered.isEmpty))) { + blocksToPut = blocksToPut.diff(List(x)) + addBlocks(xs) + } else + addBlocks(xs ::: List(x)) + } + } else { // there are new handlers for this block + + var firstBlockAfter: HashMap[ExceptionHandler,BasicBlock] = new HashMap() + val savedCurrentBlock = currentBlock + /** + * the output blocks of this method are changed so that: + * - only one block has a successor outside the set of blocks + * - this block is the last of the reusulting list + * + * side-effect: it stores the successor in the hashMap + * firstBlockAfter, which has to be emitted first after try/catch/finally, + * because the target of the Leave-instruction will always be the first + * instruction after EndExceptionBlock + * + * returns: the output blocks plus an Option containing the possibly created + * new block + **/ + def adaptBlocks(blocks: List[BasicBlock], exh: ExceptionHandler): Pair[List[BasicBlock], Option[BasicBlock]] = { + def outsideTargets(block: BasicBlock, blocks: List[BasicBlock]) = { + block.successors.filter(scc => !blocks.contains(scc)) + } + // get leaving blocks and their outside targets + def leavingBlocks(blocks: List[BasicBlock]): List[Pair[BasicBlock, List[BasicBlock]]] = { + for {val b <- blocks + val t = outsideTargets(b, blocks) + t.length != 0 } yield {b, t} + } + + def replaceOutJumps(blocks: List[BasicBlock], leaving: List[Pair[BasicBlock, List[BasicBlock]]], exh: ExceptionHandler):Pair[List[BasicBlock], Option[BasicBlock]] = { + def replaceJump(block: BasicBlock, from: BasicBlock, to: BasicBlock) = block.lastInstruction match { + case JUMP(where) => + assert(from == where) + block.replaceInstruction(block.lastInstruction, JUMP(to)) + case CJUMP(success, failure, cond, kind) => + if (from == success) + block.replaceInstruction(block.lastInstruction, CJUMP(to, failure, cond, kind)) + else + assert(from == failure) + if (from == failure) + block.replaceInstruction(block.lastInstruction, CJUMP(success, to, cond, kind)) + case CZJUMP(success, failure, cond, kind) => + if (from == success) + block.replaceInstruction(block.lastInstruction, CZJUMP(to, failure, cond, kind)) + else + assert(from == failure) + if (from == failure) + block.replaceInstruction(block.lastInstruction, CZJUMP(success, to, cond, kind)) + case SWITCH(tags, labels) => // labels: List[BasicBlock] + val newLabels = labels.map(b => if (b == from) to else b) + assert(newLabels.contains(to)) + block.replaceInstruction(block.lastInstruction, SWITCH(tags, newLabels)) + case _ => abort("expected branch at the end of block " + block) + } + + val jumpOutBlock = blocks.last.code.newBlock + jumpOutBlock.emit(JUMP(firstBlockAfter(exh))) + jumpOutBlock.close + leaving.foreach(p => { + val lBlock = p._1 + val target = p._2(0) // the elemets of p._2 are all the same, checked before + replaceJump(lBlock, target, jumpOutBlock) + }) + {blocks ::: List(jumpOutBlock), Some(jumpOutBlock)} + } + + val leaving = leavingBlocks(blocks) + if (leaving.length == 0) + {blocks, None} + else if (leaving.length == 1) { + val outside = leaving(0)._2 + assert(outside.forall(b => b == outside(0)), "exception-block leaving to multiple targets") + if (!firstBlockAfter.isDefinedAt(exh)) + firstBlockAfter(exh) = outside(0) + else + assert(firstBlockAfter(exh) == outside(0), "try/catch leaving to multiple targets: " + firstBlockAfter(exh) + ", new: " + outside(0)) + val last = leaving(0)._1 + {blocks.diff(List(last)) ::: List(last), None} + } else { + val outside = leaving.flatMap(p => p._2) + assert(outside.forall(b => b == outside(0)), "exception-block leaving to multiple targets") + if (!firstBlockAfter.isDefinedAt(exh)) + firstBlockAfter(exh) = outside(0) + else + assert(firstBlockAfter(exh) == outside(0), "try/catch leaving to multiple targets") + replaceOutJumps(blocks, leaving, exh) + } + } + + var affectedHandlers: List[ExceptionHandler] = Nil + untreatedHandlers.foreach( (h) => { + if (h.covers(x)){ + affectedHandlers = h :: affectedHandlers + } + }) + affectedHandlers = affectedHandlers.filter(h => {h.covered.length == affectedHandlers(0).covered.length}) + untreatedHandlers = untreatedHandlers.diff(affectedHandlers) + + // shorter try-catch-finally last (the ones contained in another) + affectedHandlers = affectedHandlers.sort({(h1, h2) => h1.covered.length > h2.covered.length}) + + // more than one catch produces more than one exh, but we only need one + var singleAffectedHandler: ExceptionHandler = affectedHandlers(0) // List[ExceptionHandler] = Nil + var exceptionBlock: Option[ExceptionBlock] = None + affectedHandlers.foreach(h1 => { + val {adaptedBlocks, newBlock} = adaptBlocks(blocksToPut.intersect(h1.blocks), singleAffectedHandler) + newBlock match { + case Some(block) => + blocksToPut = blocksToPut ::: List(block) + h1.addBlock(block) + case None => () + } + val orderedCatchBlocks = h1.startBlock :: adaptedBlocks.diff(List(h1.startBlock)) + + exceptionBlock match { + case Some(excBlock) => + val catchBlock = excBlock.addCatchBlock(h1.cls) + currentBlock = catchBlock + addBlocks(orderedCatchBlocks) + case None => + val excBlock = currentBlock.addExceptionBlock(singleAffectedHandler) + exceptionBlock = Some(excBlock) + + val {tryBlocks, newBlock} = adaptBlocks(blocksToPut.intersect(singleAffectedHandler.covered), singleAffectedHandler) + + newBlock match { + case Some(block) => + blocksToPut = blocksToPut ::: List(block) + singleAffectedHandler.addCoveredBlock(block) + case None => () + } + currentBlock = excBlock.tryBlock + addBlocks(tryBlocks) + + if (singleAffectedHandler.finalizer != null && singleAffectedHandler.finalizer != NoFinalizer) { + val {blocks0, newBlock} = adaptBlocks(blocksToPut.intersect(singleAffectedHandler.finalizer.blocks), singleAffectedHandler) + newBlock match { + case Some(block) => + blocksToPut = blocksToPut ::: List(block) + singleAffectedHandler.finalizer.addBlock(block) + case None => () + } + val blocks = singleAffectedHandler.finalizer.startBlock :: blocks0.diff(List(singleAffectedHandler.finalizer.startBlock)) + currentBlock = excBlock.finallyBlock + addBlocks(blocks) + } + + val catchBlock = excBlock.addCatchBlock(singleAffectedHandler.cls) + currentBlock = catchBlock + addBlocks(orderedCatchBlocks) + } + if (firstBlockAfter.isDefinedAt(singleAffectedHandler)) + nextBlock = firstBlockAfter(singleAffectedHandler) + else + nextBlock = null + }) + + currentBlock = savedCurrentBlock + + if (settings.debug.value) + log(" -> addBlocks(" + xs.intersect(blocksToPut) + ")") + addBlocks(xs.intersect(blocksToPut)) + } + } + + // begin method orderBlocksForExh + + if (settings.debug.value) + log("before: " + blocks) + // some blocks may have been removed by linearization + untreatedHandlers.foreach(h => { + h.blocks = h.blocks.intersect(blocksToPut) + h.covered = h.covered.intersect(blocksToPut) + if (h.finalizer != null && h.finalizer != NoFinalizer) + h.finalizer.blocks = h.finalizer.blocks.intersect(blocksToPut) + }) + addBlocks(blocks) + + TopBlock.close() + + if (settings.debug.value) log("TopBlock tree is: ") + if (settings.debug.value) Console.println(TopBlock) + + bb2exHInstructions.clear + def addExHInstruction(b: BasicBlock, ehi: ExHInstruction) = { + if (settings.debug.value) + log("adding exhinstr: " + b + " -> " + ehi) + + if (bb2exHInstructions.contains(b)){ + bb2exHInstructions(b) = ehi :: bb2exHInstructions(b) + } else { + bb2exHInstructions(b) = List(ehi) + } + } + omitJumpBlocks.clear + def omitJump(blk: BasicBlock) = { + omitJumpBlocks += blk + } + var orderedBlocks: List[BasicBlock] = Nil + def flatten(block: Block): Unit = { + if (block == TopBlock) { + for (val b <- TopBlock.blocks) flatten(b) + } else block match { + case cb: CodeBlock => + orderedBlocks = orderedBlocks ::: cb.basicBlocks + case bl: BlockList => + for (val b <- bl.blocks) flatten(b) + case cb: CatchBlock => + for (val b <- cb.blocks) flatten(b) + case eb: ExceptionBlock => + val handler = eb.handler + addExHInstruction(eb.tryBlock.firstBasicBlock, new BeginExceptionBlock(handler)) + omitJump(eb.tryBlock.lastBasicBlock) + flatten(eb.tryBlock) + for(val c <- eb.catchBlocks) { + val t: MsilType = (if (c.exSym == NoSymbol) EXCEPTION + else getType(c.exSym)) + addExHInstruction(c.firstBasicBlock, new BeginCatchBlock(handler, t)) + omitJump(c.lastBasicBlock) + flatten(c) + } + if (handler.finalizer != null && handler.finalizer != NoFinalizer) { + addExHInstruction(eb.finallyBlock.firstBasicBlock, new BeginFinallyBlock(handler)) + flatten(eb.finallyBlock) + addExHInstruction(eb.finallyBlock.lastBasicBlock, new EndExceptionBlock(handler)) + omitJump(eb.finallyBlock.lastBasicBlock) + } else { + addExHInstruction(eb.catchBlocks.last.lastBasicBlock, new EndExceptionBlock(handler)) + } + } + } + + flatten(TopBlock) + + assert(untreatedHandlers.forall((h) => h.covered.isEmpty), + "untreated exception handlers left: " + untreatedHandlers) + // remove catch blocks from empty handlers (finally-blocks remain) + untreatedHandlers.foreach((h) => { + orderedBlocks = orderedBlocks.diff(h.blocks) + }) + + // take care of order in which exHInstructions are executed (BeginExceptionBlock as last) + bb2exHInstructions.keys.foreach((b) => { + bb2exHInstructions(b).sort((i1, i2) => (!i1.isInstanceOf[BeginExceptionBlock])) + }) + + + if (settings.debug.value){ + log("after: " + orderedBlocks) + log(" exhInstr: " + bb2exHInstructions) + } + + orderedBlocks + } + + var currentBlock: BasicBlock = _ + var lastBlock: BasicBlock = _ + var nextBlock: BasicBlock = _ + + def genBlocks(l: List[BasicBlock]): Unit = l match { + case Nil => () + case x :: Nil => currentBlock = x; nextBlock = null; genBlock(x) + case x :: y :: ys => currentBlock = x; nextBlock = y; genBlock(x); genBlocks(y :: ys) + } + + + var ignoreNextDup: Boolean = false + val excResultLocals: Stack[LocalBuilder] = new Stack() + + def genBlock(b: BasicBlock): Unit = { + // at begin of the first block, there's nothing to save => + // lastBlock != null is secure + def saveResult(resType: MsilType) = if (resType != MVOID && lastBlock != null) { + lastBlock.lastInstruction match { + case THROW() => () + case _ => + val lb: LocalBuilder = excResultLocals.top + mcode.Emit(OpCodes.Stloc, lb) + } + } + + if (bb2exHInstructions.contains(b)){ + bb2exHInstructions(b).foreach((i) => i match { + case BeginExceptionBlock(handler) => + if (settings.debug.value) log("begin ex blk: " + handler) + mcode.BeginExceptionBlock() + val resType = msilType(handler.resultKind) + if (resType != MVOID) { + val l = mcode.DeclareLocal(resType) + l.SetLocalSymInfo("$exhResult") + excResultLocals.push(l) + } + case BeginCatchBlock(handler, exType) => + if (settings.debug.value) log("begin catch blk: " + handler + ", tpe: " + exType) + saveResult(msilType(handler.resultKind)) + mcode.BeginCatchBlock(exType) + case BeginFinallyBlock(handler) => + saveResult(msilType(handler.resultKind)) + mcode.BeginFinallyBlock() + case EndExceptionBlock(handler) => () + case _ => abort("unknown case: " + i) + }) + } + + mcode.MarkLabel(labels(b)) + if (settings.debug.value) + log("Generating code for block: " + b) + + var lastLineNr: Int = 0 + + b traverse ( instr => { + + needAdditionalRet = false + + var currentLineNr = try { clasz.cunit.position(instr.pos).line } catch { + case _: Error => + log("Warning: wrong position in: " + method) + lastLineNr + } // if getting line number fails + + if (currentLineNr != lastLineNr) { + mcode.setPosition(currentLineNr) + lastLineNr = currentLineNr + } + + 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 match { + case BOOL => mcode.Emit(OpCodes.Ldelem_I1) + case BYTE => mcode.Emit(OpCodes.Ldelem_U1) + 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) is not possible, for Array[Array[Int]], the + // load will be case REFERENCE(java.lang.Object) + + // case UNIT is not possible: an Array[Unit] will be an + // Array[scala.runtime.BoxedUnit] (-> case REFERENCE) + } + + case LOAD_LOCAL(local) => + if (settings.debug.value) + log("load_local for " + local) + val isArg: Boolean = local.arg + val i = local.index + if (isArg) { + loadArg(mcode)(i) + } + else { + loadLocal(i, local, mcode) + } + + case LOAD_FIELD(field, isStatic) => + if (settings.debug.value) + log("LOAD_FIELD with owner: " + field.owner + + " flags: " + Flags.flagsToString(field.owner.flags)) + + var fieldInfo: 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.Ldsfld else OpCodes.Ldfld, fieldInfo) + + + case LOAD_MODULE(module) => + if (settings.debug.value) + log("genearting LOAD_MODULE for: " + showsym(module)) + mcode.Emit(OpCodes.Ldsfld, getModuleInstanceField(module)) + + case STORE_ARRAY_ITEM(kind) => + kind 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 UNIT / ARRRAY are not possible (see comment at LOAD_ARRAY_ITEM) + } + + case STORE_LOCAL(local) => + val isArg: Boolean = local.arg + val i = local.index + if (settings.debug.value) + log("store_local for " + local + ", index " + i) + + // there are some locals defined by the compiler that + // are isArg and are need to be stored. + 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_FIELD(field, isStatic) => + val fieldInfo: 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 match { + // normal constructor calls are Static.. + 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)) + { + notInitializedModules -= clasz.symbol + mcode.Emit(OpCodes.Ldarg_0) + mcode.Emit(OpCodes.Stsfld, getModuleInstanceField(clasz.symbol)) + } + } + + } else { + // java.lang.String.substring(int start_incl, int end_excl) + // System.String.Substring(int start_incl, int length) + if (msym == JSTRING_SUBSTRING_INT_INT) { + val endLocal = mcode.DeclareLocal(MINT) + endLocal.SetLocalSymInfo("$substring_end") + mcode.Emit(OpCodes.Stloc, endLocal) + mcode.Emit(OpCodes.Dup) // duplicate start_incl + mcode.Emit(OpCodes.Neg) + mcode.Emit(OpCodes.Ldloc, endLocal) // load end_excl + mcode.Emit(OpCodes.Add) // compute length (-start + end) + } + + var doEmit: Boolean = true + types.get(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 _ => () + } + + // method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType + val {isDelegateView, paramType, resType} = atPhase(currentRun.typerPhase){ + msym.tpe match { + case MethodType(parameterTypes, resultType) + if (parameterTypes.length == 1 && msym.name == nme.view_) => + val isDel = definitions.isCorrespondingDelegate(resultType, parameterTypes(0)) + {isDel, parameterTypes(0), 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) + // call it as a static method, even if the compiler (symbol) thinks it's virtual + 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 method: MethodInfo = getMethod(methodSym) + val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR)) + if (methodSym.isStatic) { + mcode.Emit(OpCodes.Ldftn, method) + } else { + mcode.Emit(OpCodes.Dup) + mcode.Emit(OpCodes.Ldvirtftn, method) + } + mcode.Emit(OpCodes.Newobj, delegCtor) + } + + if (doEmit) { + val methodInfo: MethodInfo = getMethod(msym) + style match { + case SuperCall(_) => + mcode.Emit(OpCodes.Call, methodInfo) + case Dynamic => + mcode.Emit(if (dynToStatMapped(msym)) OpCodes.Call else OpCodes.Callvirt, + methodInfo) + case Static(_) => + mcode.Emit(OpCodes.Call, methodInfo) + } + } + } + + case BOX(boxType) => emitBox(mcode, boxType) //mcode.Emit(OpCodes.Box, msilType(boxType)) + + case UNBOX(boxType) => emitUnbox(mcode, boxType) + + case NEW(REFERENCE(cls)) => + ignoreNextDup = true + + // works also for arrays and reference-types + case CREATE_ARRAY(elem) => mcode.Emit(OpCodes.Newarr, msilType(elem)) + + // works for arrays and reference-types + 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) + + + // works for arrays and reference-types + // part from the scala reference: "S <: T does not imply + // Array[S] <: Array[T] in Scala. However, it is possible + // to cast an array of S to an array of T if such a cast + // is permitted in the host environment." + case CHECK_CAST(tpe) => mcode.Emit(OpCodes.Castclass, msilType(tpe)) + + + // no SWITCH is generated when there's + // - a default case ("case _ => ...") in the matching expr + // - OR is used ("case 1 | 2 => ...") + case SWITCH(tags, branches) => + // tags is List[List[Int]]; a list of integers for every label. + // if the int on stack is 4, and 4 is in the second list => jump + // to second label + // branches is List[BasicBlock] + // the labels to jump to (the last one ist the default one) + + val switchLocal = mcode.DeclareLocal(MINT) + // several switch variables will appear with the same name in the + // assembly code, but this makes no truble + switchLocal.SetLocalSymInfo("$switch_var") + + mcode.Emit(OpCodes.Stloc, switchLocal) + var i: Int = 0 + for(val l <- tags) { + var targetLabel = labels(branches(i)) + for(val i <- l) { + mcode.Emit(OpCodes.Ldloc, switchLocal) + loadI4(i, mcode) + mcode.Emit(OpCodes.Beq, targetLabel) + } + i = i + 1 + } + val defaultTarget = labels(branches(i)) + if (nextBlock != defaultTarget && !omitJumpBlocks.contains(currentBlock)) + mcode.Emit(OpCodes.Br, defaultTarget) + + + case JUMP(where) => + if (nextBlock != where && !omitJumpBlocks.contains(currentBlock)) + mcode.Emit(OpCodes.Br, labels(where)) + + + case CJUMP(success, failure, cond, kind) => + // cond is TestOp (see Primitives.scala), and can take + // values EQ, NE, LT, GE LE, GT + // kind is TypeKind + val isFloat = kind == FLOAT || kind == DOUBLE + if (nextBlock == success || omitJumpBlocks.contains(currentBlock)) { + emitBr(cond.negate, labels(failure), isFloat) + } else { + emitBr(cond, labels(success), isFloat) + if (nextBlock != failure && !omitJumpBlocks.contains(currentBlock)) { + mcode.Emit(OpCodes.Br, labels(failure)) + } + } + + case CZJUMP(success, failure, cond, kind) => + kind match { + case BOOL | REFERENCE(_) => + if (nextBlock == success || omitJumpBlocks.contains(currentBlock)) { + emitBrBool(cond.negate, labels(failure)) + } else { + emitBrBool(cond, labels(success)) + if (nextBlock != failure && !omitJumpBlocks.contains(currentBlock)) { + mcode.Emit(OpCodes.Br, labels(failure)) + } + } + } + + case RETURN(kind) => + mcode.Emit(OpCodes.Ret) + + case THROW() => + mcode.Emit(OpCodes.Throw) + + case DROP(kind) => + mcode.Emit(OpCodes.Pop) + + case DUP(kind) => + // needed to create new instances + if (!ignoreNextDup) { + mcode.Emit(OpCodes.Dup) + } else { + ignoreNextDup = false + } + + + case MONITOR_ENTER() => + mcode.Emit(OpCodes.Call, MMONITOR_ENTER) + + case MONITOR_EXIT() => + mcode.Emit(OpCodes.Call, MMONITOR_EXIT) + } + + }) // end b traverse instr => { .. } + + lastBlock = b // this way, saveResult knows lastBlock + + if (bb2exHInstructions.contains(b)){ + bb2exHInstructions(b).foreach((i) => i match { + case BeginExceptionBlock(handler) => () + case BeginCatchBlock(handler, exType) => () + case BeginFinallyBlock(handler) => () + case EndExceptionBlock(handler) => + if (settings.debug.value) log("end ex blk: " + handler) + val resType = msilType(handler.resultKind) + if (handler.finalizer == null || handler.finalizer == NoFinalizer) + saveResult(resType) + mcode.EndExceptionBlock() + if (resType != MVOID) { + val lb: LocalBuilder = excResultLocals.pop + mcode.Emit(OpCodes.Ldloc, lb) + } else + needAdditionalRet = true + case _ => abort("unknown case: " + i) + }) + } + + } // end genBlock + + def genPrimitive(primitive: Primitive, pos: Int): Unit = { + primitive match { + case Negation(kind) => + kind match { + // CHECK: is ist possible to get this for BOOL? in this case, verify. + 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) //bitwise complement (one's complement) + 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_U1) + 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: " + method.sourceFile + ":" + + Position.line(clasz.cunit.source, pos)) + } + + 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) + } + } + + + ////////////////////// loading /////////////////////// + + 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)(i: Int) = 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) = 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)) + } + + ////////////////////// labels /////////////////////// + + + val labels: HashMap[BasicBlock, Label] = new HashMap() // labels for branches + + 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 { + // EQ -> Brfalse, NE -> Brtrue; this is because we come from + // a CZJUMP. If the value on the stack is 0 (e.g. a boolen + // method returned false), and we are in the case EQ, then + // we need to emit Brfalse (EQ Zero means false). vice versa + case EQ => mcode.Emit(OpCodes.Brfalse, dest) + case NE => mcode.Emit(OpCodes.Brtrue, dest) + } + } + + def makeLabels(bs: List[BasicBlock]) = { + if (settings.debug.value) + log("Making labels for: " + method) + for (val bb <- bs) labels(bb) = mcode.DefineLabel() + } + + ////////////////////// local vars /////////////////////// + + /** + * Compute the indexes of each local variable of the given + * method. + */ + def computeLocalVarsIndex(m: IMethod): Unit = { + val params = m.params + var idx = 1 + if (isStaticSymbol(m.symbol)) + idx = 0 + + for (val l <- params) { + if (settings.debug.value) + log("Index value for parameter " + l + ": " + idx) + l.index = idx + idx = idx + 1 // sizeOf(l.kind) + } + + val locvars = m.locals.diff(params) + idx = 0 + + for (val l <- locvars) { + if (settings.debug.value) + log("Index value for local variable " + l + ": " + idx) + l.index = idx + idx = idx + 1 // sizeOf(l.kind) + } + + } + + ////////////////////// Utilities //////////////////////// + + /** Return the a name of this symbol that can be used on the .NET + * 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 msilName(sym: Symbol): String = { + val suffix: String = if (sym.hasFlag(Flags.MODULE) && !sym.isMethod && + !sym.isImplClass && + !sym.hasFlag(Flags.JAVA)) "$" else "" + // Flags.JAVA: "symbol was not defined by a scala-class" (java, or .net-class) + + 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 + } + + + ////////////////////// flags /////////////////////// + + def msilTypeFlags(sym: Symbol): Int = { + var mf: Int = TypeAttributes.AutoLayout | TypeAttributes.AnsiClass + + 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.attributes foreach { a => a match { + case AttrInfo(SerializableAttr, _, _) => + // TODO: add the Serializable TypeAttribute also if the attribute + // System.SerializableAttribute is present (.net attribute, not scala) + // Best way to do it: compare with + // definitions.getClass("System.SerializableAttribute").tpe + // when frontend available + mf = mf | TypeAttributes.Serializable + case _ => () + }} + + mf + // static: not possible (or?) + } + + def msilMethodFlags(sym: Symbol): Short = { + var mf: Int = MethodAttributes.HideBySig | + (if (sym hasFlag Flags.PRIVATE) MethodAttributes.Private + else if (sym hasFlag Flags.PROTECTED) MethodAttributes.Family + else MethodAttributes.Public) + + if (!sym.isClassConstructor) { + if (isStaticSymbol(sym)) + mf = mf | FieldAttributes.Static + else { + mf = mf | MethodAttributes.Virtual + if (sym.isFinal && !types(sym.owner).IsInterface) + mf = mf | MethodAttributes.Final + if (sym.hasFlag(Flags.DEFERRED) || types(sym.owner).IsInterface) + mf = mf | MethodAttributes.Abstract + } + } + + 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 (isStaticSymbol(sym)) + mf = mf | FieldAttributes.Static + + // TRANSIENT: "not nerialized", VOLATILE: doesn't exist on .net + // TODO: add this attribute also if the class has the custom attribute + // System.NotSerializedAttribute + sym.attributes.foreach( a => a match { + case AttrInfo(TransientAtt, _, _) => + mf = mf | FieldAttributes.NotSerialized + case _ => () + }) + + mf.toShort + } + + + def isStaticSymbol(s: Symbol): Boolean = + s.hasFlag(Flags.STATIC) || s.hasFlag(Flags.STATICMEMBER) || s.owner.isImplClass + + + ////////////////////// builders, types /////////////////////// + + var entryPoint: Symbol = _ + + val notInitializedModules: HashSet[Symbol] = new HashSet() + + // TODO: create fields also in def createType, and not in genClass, + // add a getField method (it only works as it is because fields never + // accessed from outside a class) + + 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.symbol == definitions.StringClass){ + return true + } + case _ => () + } + } + false + } + + for (val m <- cls.methods) { + if (isEntryPoint(m.symbol)) { + if (entryPoint == null) + entryPoint = m.symbol + } + } + if (firstSourceName == "") + if (cls.symbol.sourceFile != null) // is null for nested classes + firstSourceName = cls.symbol.sourceFile.name + } + + // ##################################################################### + // get and create types + + private def msilType(t: TypeKind): MsilType = t 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) => clrTypes.mkArrayType(msilType(elem)) + } + + 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): MsilType = types.get(sym) match { + case Some(typ) => typ + case None => + val name = if (sym.isModuleClass && !sym.isTrait) sym.fullNameString + "$" + else sym.fullNameString + val typ = clrTypes.getType(name) + if (typ == null) + throw new Error(showsym(sym) + " with name " + name) + else { + clrTypes.types(sym) = typ + typ + } + } + + def mapType(sym: Symbol, mType: MsilType) = { + assert(mType != null, showsym(sym)) + types(sym) = mType + } + + def createTypeBuilder(iclass: IClass): Unit = { + def getMsilType(tpe: Type): MsilType = { + val sym = tpe.symbol + types.get(sym) match { + case Some(mtype) => mtype + case None => createTypeBuilder(classes(sym)); types(sym) + } + } + + val sym = iclass.symbol + if (types contains sym) 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.removeDuplicates + + val superType = if (isInterface(sym)) null else getMsilType(parents.head) + if (settings.debug.value) + log("super type: " + parents(0).symbol + ", msil type: " + superType) + + val interfaces: Array[MsilType] = parents.tail.map(getMsilType).toArray + if (parents.length > 1) { + if (settings.debug.value){ + log("interfaces:") + for(val i <- Iterator.range(0, interfaces.length)){ + log(" type: " + parents(i + 1).symbol + ", msil type: " + interfaces(i)) + } + } + } + + //TODO here: if the class is not top-level, use DefineNestedType? + val tBuilder = + mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces) + mapType(sym, tBuilder) + } // createTypeBuilder + + + def createClassMembers(iclass: IClass): Unit = try { + createClassMembers0(iclass) + } + catch { + case e: Throwable => + System.err.println(showsym(iclass.symbol)) + System.err.println("with methods = " + iclass.methods) + throw e + } + + def createClassMembers0(iclass: IClass): Unit = { + val mtype = getType(iclass.symbol).asInstanceOf[TypeBuilder] + for (val ifield <- iclass.fields) { + val sym = ifield.symbol + if (settings.debug.value) + log("Adding field: " + sym.fullNameString) + + var attributes = msilFieldFlags(sym) + val fBuilder = mtype.DefineField(msilName(sym), msilType(sym.tpe), attributes) + fields(sym) = fBuilder + addAttributes(fBuilder, sym.attributes) + } + + if (iclass.symbol != definitions.ArrayClass) + for (val m: IMethod <- iclass.methods) { + val sym = m.symbol + if (settings.debug.value) + log("Creating MethodBuilder for " + Flags.flagsToString(sym.flags) + " " + + sym.owner.fullNameString + "::" + 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 (val i <- Iterator.range(0, paramTypes.length)) { + constr.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) + } + mapConstructor(sym, constr) + addAttributes(constr, sym.attributes) + } else { + var resType = msilType(m.returnType) + val method = + ownerType.DefineMethod(getMethodName(sym), attr, resType, paramTypes) + for (val i <- Iterator.range(0, paramTypes.length)){ + method.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) + } + if (!methods.contains(sym)) + mapMethod(sym, method) + addAttributes(method, sym.attributes) + if (settings.debug.value) + log("\t created MethodBuilder " + method) + } + } + + if (isStaticModule(iclass.symbol)){ + addModuleInstanceField(iclass.symbol) + notInitializedModules += iclass.symbol + addStaticInit(iclass.symbol) + } + + } // createClassMembers + + private def isTopLevelModule(sym: Symbol): Boolean = + atPhase (currentRun.refchecksPhase) { + sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass + } + + private def isStaticModule(sym: Symbol): Boolean = { + sym.isModuleClass && !sym.isImplClass && !sym.hasFlag(Flags.LIFTED) + } + + private def isCloneable(sym: Symbol): Boolean = { + !sym.attributes.forall( a => a match { + case AttrInfo(CloneableAttr, _, _) => false + case _ => true + }) + } + + private def addModuleInstanceField(sym: Symbol): Unit = { + 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.InitOnly | + FieldAttributes.Static).toShort) + fields(sym) = fb + } + + + // the symbol may be a object-symbol (module-symbol), or a module-class-symbol + private def getModuleInstanceField(sym: Symbol): FieldInfo = { + assert(sym.isModule || sym.isModuleClass, "Expected module: " + showsym(sym)) + + // when called by LOAD_MODULE, the corresponding type maybe doesn't + // exist yet -> make a getType + val moduleClassSym = if (sym.isModule) sym.moduleClass else sym + + // TODO: get module field for modules not defined in the + // source currently compiling (e.g. Console) + + fields get moduleClassSym match { + case Some(sym) => sym + case None => + //val mclass = types(moduleClassSym) + val mClass = clrTypes.getType(moduleClassSym.fullNameString + "$") + val mfield = mClass.GetField("MODULE$") + assert(mfield ne null, "module not found " + showsym(moduleClassSym)) + fields(moduleClassSym) = mfield + mfield + } + + //fields(moduleClassSym) + } + + private def addStaticInit(sym: Symbol): Unit = { + 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) + // the stsfld is done in the instance constructor, just after the super call. + sicode.Emit(OpCodes.Pop) + + sicode.Emit(OpCodes.Ret) + } + + + private def dumpMirrorClass(sym: Symbol): Unit = { + 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, + //FIXME: an object may have a super-type (a class, not an object) -> not in mirror class? + MOBJECT, + MsilType.EmptyTypes) + + for (val m <- sym.tpe.nonPrivateMembers; + m.owner != definitions.ObjectClass && !m.hasFlag(Flags.PROTECTED) && + m.isMethod && !m.isClassConstructor && !isStaticSymbol(m) && !m.hasFlag(Flags.CASE)) + { + if (settings.debug.value) + log(" Mirroring method: " + m) + val paramTypes = msilParamTypes(m) + val paramNames: Array[String] = new Array[String](paramTypes.length) + for (val i <- Iterator.range(0, paramTypes.length)) + paramNames(i) = "x_" + i + + // CHECK: verify if getMethodName is better than msilName + val mirrorMethod = mirrorTypeBuilder.DefineMethod(getMethodName(m), + MethodAttributes.Public | + MethodAttributes.Static, + msilType(m.tpe.resultType), + paramTypes) + + var i = 0 + while(i < paramTypes.length) { + mirrorMethod.DefineParameter(i, ParameterAttributes.None, paramNames(i)) + i = i + 1 + } + + val mirrorCode = mirrorMethod.GetILGenerator() + mirrorCode.Emit(OpCodes.Ldsfld, getModuleInstanceField(sym)) + Iterator.range(0, paramTypes.length) foreach loadArg(mirrorCode) + + mirrorCode.Emit(OpCodes.Call, getMethod(m)) + mirrorCode.Emit(OpCodes.Ret) + } + + addSymtabAttribute(sym.sourceModule, mirrorTypeBuilder) + + mirrorTypeBuilder.CreateType() + } + + + // ##################################################################### + // delegate callers + + 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() + // create a field an store the function-object + val mFunctionType: MsilType = msilType(functionType) + val anonfunField: FieldBuilder = delegateCallers.DefineField( + "$anonfunField$$" + nbDelegateCallers, mFunctionType, + FieldAttributes.InitOnly | FieldAttributes.Public | FieldAttributes.Static) + mcode.Emit(OpCodes.Stsfld, anonfunField) + + + // create the static caller method and the delegate object + val {paramTypes, 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, + msilType(returnType), paramTypes.map(msilType).toArray) + for(val i <- Iterator.range(0, paramTypes.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) + + + // create the static caller method body + val functionApply: MethodInfo = getMethod(functionType.member(nme.apply)) + val dcode: ILGenerator = caller.GetILGenerator() + dcode.Emit(OpCodes.Ldsfld, anonfunField) + for(val i <- Iterator.range(0, paramTypes.length)) { + loadArg(dcode)(i) + emitBox(dcode, toTypeKind(paramTypes(i))) + } + dcode.Emit(OpCodes.Callvirt, functionApply) + emitUnbox(dcode, toTypeKind(returnType)) + dcode.Emit(OpCodes.Ret) + + nbDelegateCallers = nbDelegateCallers + 1 + + } //def createDelegateCaller + + def emitBox(code: ILGenerator, boxType: TypeKind) = boxType match { + // doesn't make sense, unit as parameter.. + 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 (definitions.unboxMethod.contains(cls)) => + code.Emit(OpCodes.Box, (msilType(boxType))) + case REFERENCE(_) | ARRAY(_) => () + } + +// def emitUnbox(code: ILGenerator, boxType: Type) = toTypeKind(boxType) match { +// case UNIT => code.Emit(OpCodes.Pop) +// case BOOL => code.Emit(OpCodes.Unbox, MBOOL); code.Emit(OpCodes.Ldind_I1) +// case BYTE => code.Emit(OpCodes.Unbox, MBYTE); code.Emit(OpCodes.Ldind_U1) +// case SHORT => code.Emit(OpCodes.Unbox, MSHORT); code.Emit(OpCodes.Ldind_I2) +// case CHAR => code.Emit(OpCodes.Unbox, MCHAR); code.Emit(OpCodes.Ldind_U2) +// case INT => code.Emit(OpCodes.Unbox, MINT); code.Emit(OpCodes.Ldind_I4) +// case LONG => code.Emit(OpCodes.Unbox, MLONG); code.Emit(OpCodes.Ldind_I8) +// case FLOAT => code.Emit(OpCodes.Unbox, MFLOAT); code.Emit(OpCodes.Ldind_R4) +// case DOUBLE => code.Emit(OpCodes.Unbox, MDOUBLE); code.Emit(OpCodes.Ldind_R8) +// case REFERENCE(typ) => () ///code.Emit(OpCode.Unbox, msilType(typ)); code.Emit(OpCodes.Ldobj, msilType(typ)) +// case ARRAY(_) => () +// } + + def emitUnbox(code: ILGenerator, boxType: TypeKind) = boxType match { + case UNIT => code.Emit(OpCodes.Pop) + case BOOL => code.Emit(OpCodes.Unbox, MBOOL); code.Emit(OpCodes.Ldind_I1) + case BYTE => code.Emit(OpCodes.Call, toByte) + 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 (definitions.unboxMethod.contains(cls)) => + code.Emit(OpCodes.Unbox, msilType(boxType)) + code.Emit(OpCodes.Ldobj, msilType(boxType)) + case REFERENCE(_) | ARRAY(_) => () + } + + // ##################################################################### + // get and create methods / constructors + + 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) { + System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name) + System.out.println("scope = " + sym.owner.tpe.decls) + throw new Error(sym.fullNameString) + } + else { + mapConstructor(sym, constr) + constr + } + } + + def mapConstructor(sym: Symbol, cInfo: ConstructorInfo) = { + constructors(sym) = cInfo + } + + private def getMethod(sym: Symbol): MethodInfo = { + //private def getMethod(sym: Symbol): MethodInfo = sym match { +// case SRToInt => toInt +// case SRToDouble => toDouble +// case SRToLong => toLong +// case SRToChar => toChar +// case SRToFloat => toFloat +// case SRToBool => toBool +// case SRToByte => toByte +// case SRToShort => toShort +// case _ => + + methods.get(sym) match { + case Some(method) => method + case None => + val mClass = getType(sym.owner) + try { + val method = mClass.GetMethod(getMethodName(sym), msilParamTypes(sym), + msilType(sym.tpe.resultType)) + if (method eq null) { + System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym)) + System.out.println("scope = " + sym.owner.tpe.decls) + throw new Error(sym.fullNameString) + } + else { + mapMethod(sym, method) + method + } + } + catch { + case e: Exception => + Console.println("While looking up " + mClass + "::" + sym.nameString) + Console.println("\t" + showsym(sym)) + throw e + } + } + } + + /* + * add a mapping between sym and mInfo + */ + private def mapMethod(sym: Symbol, mInfo: MethodInfo):Unit = { + assert (mInfo != null, mInfo) + methods(sym) = mInfo + } + + /* + * add mapping between sym and method with newName, paramTypes of newClass + */ + private def mapMethod(sym: Symbol, newClass: MsilType, newName: String, paramTypes: Array[MsilType]): Unit = { + 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 + } + + /* + * add mapping between method with name and paramTypes of clazz to + * method with newName and newParamTypes of newClass (used for instance + * for "wait") + */ + private def mapMethod( + clazz: Symbol, name: Name, paramTypes: Array[Type], + newClass: MsilType, newName: String, newParamTypes: Array[MsilType]):Unit = { + val methodSym = lookupMethod(clazz, name, paramTypes) + assert(methodSym != null, "cannot find method " + name + "(" + + paramTypes + ")" + " in class " + clazz) + mapMethod(methodSym, newClass, newName, newParamTypes) + } + + /* + * add maping for member with name and paramTypes to member + * newName of newClass (same parameters) + */ + private def mapMethod( + clazz: Symbol, name: Name, paramTypes: Array[Type], + newClass: MsilType, newName: String):Unit = { + mapMethod(clazz, name, paramTypes, newClass, newName, paramTypes map msilType) + } + + /* + * add mapping for all methods with name of clazz to the corresponding + * method (same parameters) with newName of newClass + */ + private def mapMethod( + clazz: Symbol, name: Name, + newClass: MsilType, newName: String):Unit = { + val memberSym: Symbol = clazz.tpe.member(name) + memberSym.tpe match { + // alternatives: List[Symbol] + case OverloadedType(_, alternatives) => + alternatives.foreach(s => mapMethod(s, newClass, newName, msilParamTypes(s))) + + // paramTypes: List[Type], resType: Type + case MethodType(paramTypes, resType) => + mapMethod(memberSym, newClass, newName, msilParamTypes(memberSym)) + + case _ => abort("member not found: " + clazz + ", " + name) + } + } + + + /* + * find the method in clazz with name and paramTypes + */ + 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 = 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) + } + } + + def getMethodName(methodSym: Symbol): String = { + val name = methodSym.name + val params = methodSym.tpe.paramTypes + if (name == nme.finalize_ && params.length == 0) + "Finalize" + else if (name == nme.toString_ && params.length == 0) + "ToString" + else if (name == nme.hashCode_ && params.length == 0) + "GetHashCode" + else if (name == nme.equals_ && params.length == 1 && + params(0) == definitions.ObjectClass.tpe) + "Equals" + // FIXME: why is there no nme.clone_ ? + else if (name.toString() == "clone" && params.length == 0) + "Clone" + else + msilName(methodSym) + } + + private def showsym(sym: Symbol): String = (sym.toString + + "\n symbol = " + Flags.flagsToString(sym.flags) + " " + sym + + "\n owner = " + Flags.flagsToString(sym.owner.flags) + " " + sym.owner + ) + + } // class BytecodeGenerator + +} // class GenMSIL diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala index 674af619b..e24282c82 100644 --- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala +++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala @@ -41,6 +41,13 @@ trait Definitions requires SymbolTable { var NullPointerExceptionClass: Symbol = _ var NonLocalReturnExceptionClass: Symbol = _ + var ValueTypeClass: Symbol = _ // System.ValueType + var DelegateClass: Symbol = _ // System.MulticastDelegate + var Delegate_scalaCallers: List[Symbol] = List() + // Symbol -> (Symbol, Type): scalaCaller -> (scalaMethodSym, DelegateType) + // var Delegate_scalaCallerInfos: HashMap[Symbol, Pair[Symbol, Type]] = _ + var Delegate_scalaCallerTargets: HashMap[Symbol, Symbol] = _ + // the scala value classes var UnitClass: Symbol = _ var BooleanClass: Symbol = _ @@ -269,6 +276,32 @@ trait Definitions requires SymbolTable { case _ => false } + + def isCorrespondingDelegate(delegateType: Type, functionType: Type): boolean = { + var isCD: Boolean = false; + if (DelegateClass != null && delegateType != null && + isSubType(delegateType, DelegateClass.tpe)) + { + val meth: Symbol = delegateType.member(nme.apply) + meth.tpe match { + case MethodType(delegateParams, delegateReturn) => + val delegateParamsO = delegateParams.map(pt => {if (pt == definitions.AnyClass.tpe) definitions.ObjectClass.tpe else pt}); + if(isFunctionType(functionType)) + functionType match { + case TypeRef(_, _, args) => + if(delegateParamsO == args.dropRight(1) && + delegateReturn == args.last) + isCD = true; + + case _ => (); + } + + case _ => () + } + } + isCD + } + /* val RemoteFunctionClass: Array[Symbol] = new Array(MaxFunctionArity + 1) def remoteFunctionApply(n: Int) = getMember(RemoteFunctionClass(n), nme.apply) def remoteFunctionType(formals: List[Type], restpe: Type) = @@ -428,7 +461,17 @@ trait Definitions requires SymbolTable { private def newValueClass(name: Name, tag: char): Symbol = { def boxedName: String = - "scala.runtime.Boxed" + name + if (!forMSIL) "scala.runtime.Boxed" + name + else "System." + (name match { + case nme.Boolean => "Boolean" + case nme.Byte => "Byte" + case nme.Char => "Char" + case nme.Short => "Int16" + case nme.Int => "Int32" + case nme.Long => "Int64" + case nme.Float => "Single" + case nme.Double => "Double" + }) val clazz = newClass(ScalaPackageClass, name, List(AnyValClass.typeConstructor)) .setFlag(ABSTRACT /* SEALED */) // bq: SEALED is interesting for case class descendants, only @@ -606,9 +649,12 @@ trait Definitions requires SymbolTable { def isNumericValueClass(sym: Symbol): boolean = (sym ne BooleanClass) && (boxedClass contains sym) + def isValueType(sym: Symbol) = + isValueClass(sym) || unboxMethod.contains(sym) + /** Is symbol a value or array class? */ def isUnboxedClass(sym: Symbol): boolean = - isValueClass(sym) || sym == ArrayClass + isValueType(sym) || sym == ArrayClass def signature(tp: Type): String = { def erasure(tp: Type): Type = tp match { @@ -650,7 +696,7 @@ trait Definitions requires SymbolTable { RootClass.info.decls.enter(EmptyPackage) RootClass.info.decls.enter(RootPackage) - JavaLangPackage = getModule("java.lang") + JavaLangPackage = getModule(if (forMSIL) "System" else "java.lang") ScalaPackage = getModule("scala") assert(ScalaPackage ne null, "Scala package is null") ScalaPackageClass = ScalaPackage.tpe.symbol @@ -662,7 +708,7 @@ trait Definitions requires SymbolTable { AnyValClass = newClass(ScalaPackageClass, nme.AnyVal, anyparam) .setFlag(FINAL | SEALED) - ObjectClass = getClass("java.lang.Object") + ObjectClass = getClass(if (forMSIL) "System.Object" else "java.lang.Object") AnyRefClass = newAlias(ScalaPackageClass, nme.AnyRef, ObjectClass.typeConstructor) @@ -675,13 +721,17 @@ trait Definitions requires SymbolTable { AllClass = newClass(ScalaPackageClass, nme.Nothing, anyparam) .setFlag(ABSTRACT | TRAIT | FINAL) - StringClass = getClass("java.lang.String") + StringClass = getClass(if (forMSIL) "System.String" else "java.lang.String") - ClassClass = getClass("java.lang.Class") - ThrowableClass = getClass("java.lang.Throwable") - NullPointerExceptionClass = getClass("java.lang.NullPointerException") + ClassClass = getClass(if (forMSIL) "System.Type" else "java.lang.Class") + ThrowableClass = getClass(if (forMSIL) "System.Exception" else "java.lang.Throwable") + NullPointerExceptionClass = getClass(if (forMSIL) "System.NullReferenceException" + else "java.lang.NullPointerException") NonLocalReturnExceptionClass = getClass("scala.runtime.NonLocalReturnException") + ValueTypeClass = if (forMSIL) getClass("System.ValueType") else null + DelegateClass = if (forMSIL) getClass("System.MulticastDelegate") else null + UnitClass = newClass(ScalaPackageClass, nme.Unit, List(AnyValClass.typeConstructor)) abbrvTag(UnitClass) = 'V' @@ -704,7 +754,7 @@ trait Definitions requires SymbolTable { StaticAttributeClass = getClass("scala.StaticAttribute") //ChannelClass = getClass("scala.distributed.Channel") //RemoteRefClass = getClass("scala.distributed.RemoteRef") - if (!forCLDC) { + if (!forCLDC && ! forMSIL) { CodeClass = getClass("scala.reflect.Code") CodeModule = getModule("scala.reflect.Code") } @@ -716,12 +766,14 @@ trait Definitions requires SymbolTable { ListClass = getClass("scala.List") ListModule = getModule("scala.List") ArrayClass = getClass("scala.Array") - SerializableClass = if (forCLDC) null else getClass("java.io.Serializable") + SerializableClass = if (forMSIL || forCLDC) null else getClass("java.io.Serializable") PredefModule = getModule("scala.Predef") ConsoleModule = getModule("scala.Console") MatchErrorClass = getClass("scala.MatchError") MatchErrorModule = getModule("scala.MatchError") - IndexOutOfBoundsExceptionClass = getClass("java.lang.IndexOutOfBoundsException") + IndexOutOfBoundsExceptionClass = + getClass(if (forMSIL) "System.IndexOutOfRangeException" + else "java.lang.IndexOutOfBoundsException") //RemoteExecutionModule = getModule("scala.distributed.RemoteExecution") ScalaRunTimeModule = getModule("scala.runtime.ScalaRunTime") RepeatedParamClass = newCovariantPolyClass( @@ -792,12 +844,85 @@ trait Definitions requires SymbolTable { BoxedUnitModule = getModule("scala.runtime.BoxedUnit") ObjectRefClass = getClass("scala.runtime.ObjectRef") + if (forMSIL) { + val intType = IntClass.typeConstructor; + val intParam = List(intType); + val longType = LongClass.typeConstructor; + val charType = CharClass.typeConstructor; + val unitType = UnitClass.typeConstructor; + val stringType = StringClass.typeConstructor; + val stringParam = List(stringType); + + // additional methods of Object + newMethod(ObjectClass, "clone", List(), AnyRefClass.typeConstructor); + newMethod(ObjectClass, "wait", List(), unitType); + newMethod(ObjectClass, "wait", List(longType), unitType); + newMethod(ObjectClass, "wait", List(longType, intType), unitType); + newMethod(ObjectClass, "notify", List(), unitType); + newMethod(ObjectClass, "notifyAll", List(), unitType); + + // additional methods of String + newMethod(StringClass, "length", List(), intType); + newMethod(StringClass, "compareTo", stringParam, intType); + newMethod(StringClass, "charAt", intParam, charType); + newMethod(StringClass, "concat", stringParam, stringType); + newMethod(StringClass, "indexOf", intParam, intType); + newMethod(StringClass, "indexOf", List(intType, intType), intType); + newMethod(StringClass, "indexOf", stringParam, intType); + newMethod(StringClass, "indexOf", List(stringType, intType), intType); + newMethod(StringClass, "lastIndexOf", intParam, intType); + newMethod(StringClass, "lastIndexOf", List(intType, intType), intType); + newMethod(StringClass, "lastIndexOf", stringParam, intType); + newMethod(StringClass, "lastIndexOf", List(stringType, intType), intType); + newMethod(StringClass, "toLowerCase", List(), stringType); + newMethod(StringClass, "toUpperCase", List(), stringType); + newMethod(StringClass, "startsWith", stringParam, booltype); + newMethod(StringClass, "endsWith", stringParam, booltype); + newMethod(StringClass, "substring", intParam, stringType); + newMethod(StringClass, "substring", List(intType, intType), stringType); + newMethod(StringClass, "trim", List(), stringType); + newMethod(StringClass, "intern", List(), stringType); + newMethod(StringClass, "replace", List(charType, charType), stringType); + newMethod(StringClass, "toCharArray", List(), + appliedType(ArrayClass.typeConstructor, List(charType))); + + // Delegate_scalaCallerInfos = new HashMap() + Delegate_scalaCallerTargets = new HashMap() + } + AnnotationDefaultAttr = newClass(RootClass, nme.AnnotationDefaultATTR, List(AttributeClass.typeConstructor)) SerializableAttr = getClass("scala.serializable") + BeanPropertyAttr = if (forCLDC || forMSIL) null else getClass("scala.reflect.BeanProperty") DeprecatedAttr = getClass("scala.deprecated") - BeanPropertyAttr = if (forCLDC) null else getClass("scala.reflect.BeanProperty") + } + + var nbScalaCallers: Int = 0 + def newScalaCaller(delegateType: Type): Symbol = { + assert(forMSIL, "scalaCallers can only be created if target is .NET") + // object: reference to object on which to call (scala-)metod + val paramTypes: List[Type] = List(ObjectClass.tpe) + val name: String = "$scalaCaller$$" + nbScalaCallers + // tparam => resultType, which is the resultType of PolyType, i.e. the result type after applying the + // type parameter =-> a MethodType in this case + // TODO: set type bounds manually (-> MulticastDelegate), see newTypeParam + val newCaller = newMethod(DelegateClass, name, paramTypes, delegateType) setFlag (FINAL | STATIC) + // val newCaller = newPolyMethod(DelegateClass, name, + // tparam => MethodType(paramTypes, tparam.typeConstructor)) setFlag (FINAL | STATIC) + Delegate_scalaCallers = Delegate_scalaCallers ::: List(newCaller) + nbScalaCallers = nbScalaCallers + 1 + newCaller + } + + // def addScalaCallerInfo(scalaCaller: Symbol, methSym: Symbol, delType: Type) = { + // assert(Delegate_scalaCallers contains scalaCaller) + // Delegate_scalaCallerInfos += scalaCaller -> Pair(methSym, delType) + // } + + def addScalaCallerInfo(scalaCaller: Symbol, methSym: Symbol) = { + assert(Delegate_scalaCallers contains scalaCaller) + Delegate_scalaCallerTargets += scalaCaller -> methSym } } } diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index eca319ab2..920e66a96 100644 --- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -257,7 +257,7 @@ trait StdNames requires SymbolTable { val filter = newTermName("filter") val finalize_ = newTermName("finalize") val flatMap = newTermName("flatMap") - val forName = newTermName("forName") + val forName = newTermName(if (forMSIL) "GetType" else "forName") val foreach = newTermName("foreach") val get = newTermName("get") val getClass_ = newTermName("getClass") diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 699dc0909..178c6155f 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -8,12 +8,12 @@ package scala.tools.nsc.symtab import compat.Platform.currentTime import java.io.{File, IOException} - -import scala.collection.mutable.HashMap +import scala.collection.mutable.{HashMap, HashSet} import scala.tools.nsc.io.AbstractFile import scala.tools.nsc.util.{ClassPath, NameTransformer, Position} import classfile.{ClassfileParser, SymblfileParser} import Flags._ +import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute}; /** This class ... * @@ -57,8 +57,8 @@ abstract class SymbolLoaders { phase = currentphase def source = kindString + " " + sourceString informTime("loaded " + source, start) - if (root.rawInfo == this && root.linkedSym.rawInfo == this) - throw new TypeError(source + " does not define " + root) + //if (root.rawInfo == this && root.linkedSym.rawInfo == this) + // throw new TypeError(source + " does not define " + root) ok = true } catch { case ex: IOException => @@ -88,12 +88,50 @@ abstract class SymbolLoaders { /** Load contents of a package */ class PackageLoader(directory: global.classPath0.Context) extends SymbolLoader { - // System.err.println("PACKAGE LOADER: " + directory) protected def sourceString = directory.toString() + protected def kindString: String = "directory path" + + protected def newPackageLoader(dir: global.classPath0.Context): PackageLoader = + new PackageLoader(dir) + + protected def checkSource(name: String, source: AbstractFile): Boolean = true; + + protected var root: Symbol = _ + + def enterPackage(name: String, completer: SymbolLoader): unit = { + val pkg = root.newPackage(NoPos, newTermName(name)) + pkg.moduleClass.setInfo(completer) + pkg.setInfo(pkg.moduleClass.tpe) + root.info.decls.enter(pkg) + } + + // @return - the symbol of the class + def enterClassAndModule(name: String, completer: SymbolLoader): Symbol = { + val owner = if (root.isRoot) definitions.EmptyPackageClass else root + val className = newTermName(name) + assert(owner.info.decls.lookup(name) == NoSymbol, owner.fullNameString + "." + name) + val clazz = owner.newClass(NoPos, name.toTypeName) + val module = owner.newModule(NoPos, name) + clazz.setInfo(completer) + module.setInfo(completer) + module.moduleClass.setInfo(moduleClassLoader) + owner.info.decls.enter(clazz) + owner.info.decls.enter(module) +// if (completer.sourceFile != null) { +// clazz.sourceFile = completer.sourceFile; +// module.moduleClass.sourceFile = completer.sourceFile +// } + assert(clazz.linkedModuleOfClass == module, module) + assert(module.linkedClassOfModule == clazz, clazz) + //System.out.println("Added class " + clazz.fullNameString); + clazz + } + protected def doComplete(root: Symbol): unit = { assert(root.isPackageClass, root) + this.root = root val scope = newScope root.setInfo(new PackageClassInfoType(scope, root)) @@ -102,40 +140,13 @@ abstract class SymbolLoaders { name.length() > 0 && (settings.XbytecodeRead.value || (!name.endsWith("$class") && name.indexOf("$anon") == -1)); - def enterPackage(str: String, completer: SymbolLoader): unit = { - val pkg = root.newPackage(NoPos, newTermName(str)) - pkg.moduleClass.setInfo(completer) - pkg.setInfo(pkg.moduleClass.tpe) - root.info.decls.enter(pkg) - } - - def enterClassAndModule(str: String, completer: SymbolLoader): unit = { - val owner = if (root.isRoot) definitions.EmptyPackageClass else root - val name = newTermName(str) - val clazz = owner.newClass(NoPos, name.toTypeName) - val module = owner.newModule(NoPos, name) - clazz.setInfo(completer) - module.setInfo(completer) - module.moduleClass.setInfo(moduleClassLoader) - owner.info.decls.enter(clazz) - owner.info.decls.enter(module) -/* - if (completer.sourceFile ne null) { - clazz.sourceFile = completer.sourceFile; - module.moduleClass.sourceFile = completer.sourceFile - } -*/ - assert(clazz.linkedModuleOfClass == module, module) - assert(module.linkedClassOfModule == clazz, clazz) - } - val classes = new HashMap[String, global.classPath0.Context] val packages = new HashMap[String, global.classPath0.Context] for (val dir <- directory.entries) if (dir.location ne null) { for (val file <- dir.location) { if (file.isDirectory && directory.validPackage(file.name) && !packages.isDefinedAt(file.name)) packages(file.name) = directory.find(file.name, true); - else if (!file.isDirectory && file.name.endsWith(".class")) { + else if (!global.forMSIL && !file.isDirectory && file.name.endsWith(".class")) { val name = file.name.substring(0, file.name.length() - (".class").length()); if (isValid(name) && !classes.isDefinedAt(name)) { val clazz = directory.find(name, false) @@ -147,33 +158,35 @@ abstract class SymbolLoaders { for (val dir <- directory.entries) if (dir.source ne null) { for (val file <- dir.source.location) { if (file.isDirectory && directory.validPackage(file.name) && !packages.isDefinedAt(file.name)) - packages(file.name) = directory.find(file.name, true); + packages(file.name) = directory.find(file.name, true) else if (dir.source.compile && !file.isDirectory && file.name.endsWith(".scala")) { - val name = file.name.substring(0, file.name.length() - (".scala").length()); + val name = file.name.substring(0, file.name.length() - (".scala").length()) if (isValid(name) && !classes.isDefinedAt(name)) { val source = directory.find(name, false) - if (source ne null) classes(name) = source + if ((source ne null) && (source.sourceFile ne null)) + if (checkSource(name, source.sourceFile)) + classes(name) = source + else if (settings.debug.value) + Console.println("Skipping source file " + source.sourceFile) } } } } - //if (!packages.isEmpty) System.err.println("COMPLETE: " + packages) - //if (! classes.isEmpty) System.err.println("COMPLETE: " + classes) - // do classes first + // do classes first for (val {name, file} <- classes.elements) { val loader = if (!file.isSourceFile) { - new ClassfileLoader(file.classFile, file.sourceFile, file.sourcePath); + new ClassfileLoader(file.classFile, file.sourceFile, file.sourcePath) } else { assert(file.sourceFile ne null) new SourcefileLoader(file.sourceFile) } enterClassAndModule(name, loader) } + for (val {name, file} <- packages.elements) - enterPackage(name, new PackageLoader(file)) + enterPackage(name, newPackageLoader(file)) } - protected def kindString: String = "directory path" } /* @@ -182,7 +195,68 @@ abstract class SymbolLoaders { } */ + class NamespaceLoader(directory: global.classPath0.Context) extends PackageLoader(directory) { + + override protected def kindString: String = "namespace " + namespace + + override protected def sourceString = ""; + + override def newPackageLoader(dir: global.classPath0.Context): PackageLoader = + new NamespaceLoader(dir); + + val types = new HashMap[String,MSILType](); + + val namespaces = new HashSet[String](); + + def namespace: String = if (root.isRoot) "" else root.fullNameString; + + // TODO: Add check whether the source is newer than the assembly + override protected def checkSource(name: String, source: AbstractFile): Boolean = { + !types.contains(name) + } + + override protected def doComplete(root: Symbol): Unit = { + clrTypes.collectMembers(root, types, namespaces); + + super.doComplete(root); + + for (val namespace <- namespaces.elements) { + val oldPkg = root.info.decls.lookup(newTermName(namespace)); + if (oldPkg == NoSymbol) + enterPackage(namespace, new NamespaceLoader(new classPath0.Context(List()))); + //else System.out.println("PackageLoader: package already in scope: " + oldPkg.fullNameString); + } + + // import the CLR types contained in the package (namespace) + for (val Pair(name, typ) <- types.elements) { + assert(namespace == typ.Namespace, typ.FullName); + + if (typ.IsDefined(clrTypes.SCALA_SYMTAB_ATTR, false)) { + val attrs = typ.GetCustomAttributes(clrTypes.SCALA_SYMTAB_ATTR, false); + assert (attrs.length == 1, attrs.length); + val a = attrs(0).asInstanceOf[MSILAttribute]; + if (a.getConstructor() == clrTypes.SYMTAB_CONSTR) + enterClassAndModule(name, new MSILTypeLoader(typ)); + } + else + enterClassAndModule(name, new MSILTypeLoader(typ)); + } + } + } // NamespaceLoader + + class MSILTypeLoader(typ: MSILType) extends SymbolLoader { + private object typeParser extends clr.TypeParser { + val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global; + } + protected def doComplete(root: Symbol): Unit = { + typeParser.parse(typ, root); + } + protected def kindString: String = typ.FullName; + protected def sourceString = typ.Assembly.FullName; + } + class ClassfileLoader(classFile: AbstractFile, override val sourceFile: AbstractFile, sourcePath0: AbstractFile) extends SymbolLoader { + //throw new Error("classfile = " + classFile + "; sourcefile = " + sourceFile + "; sourcepath = " + sourcePath0) private object classfileParser extends ClassfileParser { val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global override def sourcePath = sourcePath0 /* could be null */ @@ -215,4 +289,10 @@ abstract class SymbolLoaders { protected def kindString: String = ""; protected def sourceString = ""; } + + object clrTypes extends clr.CLRTypes { + val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global; + if (global.forMSIL) init(); + } + } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala index b72ee159f..f2e92a9e9 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala @@ -23,7 +23,10 @@ abstract class SymbolTable extends Names /** Are we compiling for the J2ME CLDC platform? */ def forCLDC: Boolean - /** A period is an ordinal number for a phase in a run. + /** Are we compiling for .NET*/ + def forMSIL: Boolean + + /** A period is an ordinal number for a phase in a run. * Phases in later runs have higher periods than phases in earlier runs. * Later phases have higher periods than earlier phases in the same run. */ diff --git a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala b/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala new file mode 100644 index 000000000..f90f6d662 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala @@ -0,0 +1,254 @@ +/* NSC -- new scala compiler + * Copyright 2004-2006 LAMP/EPFL + */ + +// $Id: CLRTypes.scala 9524 2006-12-14 17:54:07Z mihaylov $ + +package scala.tools.nsc.symtab.clr; + +import scala.tools.nsc.util.Position; + +import scala.collection.mutable.{ListBuffer, Map, HashMap, Set, HashSet}; +import java.util.{Arrays, Comparator, StringTokenizer}; + +import java.io.File; + +import ch.epfl.lamp.compiler.msil._; + +/** + * Collects all types from all reference assemblies. + */ +abstract class CLRTypes { + + val global: Global; + import global.Symbol; + import global.definitions; + + //########################################################################## + + var BYTE: Type = _; + var UBYTE: Type = _; + var SHORT: Type = _; + var USHORT: Type = _; + var CHAR: Type = _; + var INT: Type = _; + var UINT: Type = _; + var LONG: Type = _; + var ULONG: Type = _; + var FLOAT: Type = _; + var DOUBLE: Type = _; + var BOOLEAN: Type = _; + var VOID: Type = _; + var ENUM: Type = _; + var DELEGATE: Type = _; + + var OBJECT: Type = _; + var STRING: Type = _; + var STRING_ARRAY: Type = _; + + var VALUE_TYPE: Type = _; + + var SCALA_SYMTAB_ATTR: Type = _; + var SYMTAB_CONSTR: ConstructorInfo = _; + var SYMTAB_DEFAULT_CONSTR: ConstructorInfo = _; + + var DELEGATE_COMBINE: MethodInfo = _; + var DELEGATE_REMOVE: MethodInfo = _; + + val types: Map[Symbol,Type] = new HashMap; + val constructors: Map[Symbol,ConstructorInfo] = new HashMap; + val methods: Map[Symbol,MethodInfo] = new HashMap; + val fields: Map[Symbol, FieldInfo] = new HashMap; + val sym2type: Map[Type,Symbol] = new HashMap; + + private var alltypes: Array[Type] = _; + + def init(): Unit = { // initialize + + val assems = new StringTokenizer(global.settings.assemrefs.value, File.pathSeparator); + while (assems.hasMoreTokens()) { + assemrefs += new File(assems.nextToken()); + } + + val mscorlib = findAssembly("mscorlib.dll"); + Type.initMSCORLIB(mscorlib); + findAssembly("scalaruntime.dll"); + findAllAssemblies(); + + BYTE = getTypeSafe("System.SByte"); + UBYTE = getTypeSafe("System.Byte"); + CHAR = getTypeSafe("System.Char"); + SHORT = getTypeSafe("System.Int16"); + USHORT = getTypeSafe("System.UInt16"); + INT = getTypeSafe("System.Int32"); + UINT = getTypeSafe("System.UInt32"); + LONG = getTypeSafe("System.Int64"); + ULONG = getTypeSafe("System.UInt64"); + FLOAT = getTypeSafe("System.Single"); + DOUBLE = getTypeSafe("System.Double"); + BOOLEAN = getTypeSafe("System.Boolean"); + VOID = getTypeSafe("System.Void"); + ENUM = getTypeSafe("System.Enum"); + DELEGATE = getTypeSafe("System.MulticastDelegate"); + + OBJECT = getTypeSafe("System.Object"); + STRING = getTypeSafe("System.String"); + STRING_ARRAY = getTypeSafe("System.String[]"); + VALUE_TYPE = getTypeSafe("System.ValueType"); + + SCALA_SYMTAB_ATTR = getTypeSafe("scala.runtime.SymtabAttribute"); + val bytearray: Array[Type] = Array( Type.GetType("System.Byte[]") ); + SYMTAB_CONSTR = SCALA_SYMTAB_ATTR.GetConstructor(bytearray); + SYMTAB_DEFAULT_CONSTR = SCALA_SYMTAB_ATTR.GetConstructor(Type.EmptyTypes); + + //assert(SCALA_SYMTAB_ATTR != null); + + val delegate: Type = getTypeSafe("System.Delegate"); + val dargs: Array[Type] = Array(delegate, delegate); + DELEGATE_COMBINE = delegate.GetMethod("Combine", dargs); + DELEGATE_REMOVE = delegate.GetMethod("Remove", dargs); + //assert(DELEGATE_COMBINE != null); + //assert(DELEGATE_REMOVE != null); + + + var alltypes: Array[Type] = Type.EmptyTypes; + for (val assem <- assemblies) { + val atypes = assem.GetTypes().filter((typ: Type) => typ.DeclaringType == null); + alltypes = Array.concat(alltypes, atypes); + } + + val typeNameComparator: Comparator = + new Comparator() { + def compare(o1: Any, o2: Any): Int = { + val t1 = o1.asInstanceOf[Type]; + val t2 = o2.asInstanceOf[Type]; + t1.FullName.compareTo(t2.FullName); + } + }; + + Arrays.sort(alltypes.asInstanceOf[Array[Object]], typeNameComparator); + this.alltypes = alltypes; + } + + //########################################################################## + // type mapping and lookup + +// private class MyHashMap[A, B <: AnyRef] extends HashMap[A, B] { +// override def default(key: A): B = null; +// } + + def getType(name: String): Type = Type.GetType(name); + + def getTypeSafe(name: String): Type = { + val t = Type.GetType(name); + assert(t != null, name); + t; + } + + def mkArrayType(elemType: Type): Type = getType(elemType.FullName + "[]"); + + def isDelegateType(t: Type): Boolean = { t.BaseType() == DELEGATE } + + //########################################################################## + // assembly loading methods + + // a list of all loaded assemblies + private var assemblies: ListBuffer[Assembly] = new ListBuffer(); + + // a set of all directories and assembly files + private var assemrefs: Set[File] = new HashSet(); + + /** Load the assembly with the given name + */ + private def findAssembly(name: String): Assembly = { + // see if the assembly is referenced directly + for (val file <- assemrefs.elements; file.getName() == name) { + val assem = Assembly.LoadFrom(file.getPath()); + if (assem != null) { + assemrefs -= file; + assemblies += assem; + return assem; + } + } + // look in directories specified with the '-r' option + for (val dir <- assemrefs.elements; dir.isDirectory()) { + val file = new File(dir, name); + if (file.exists()) { + val assem = Assembly.LoadFrom(file.getPath()); + if (assem != null) { + assemblies += assem; + return assem; + } + } + } + // try in the current directory + val file = new File(".", name); + if (file.exists()) { + val assem = Assembly.LoadFrom(file.getPath()); + if (assem != null) { + assemblies += assem; + return assem; + } + } + global.reporter.error(new Position(null, Position.NOPOS), + "cannot find assembly " + name + "; use the -r option to specify its location"); + throw new Error(); + } + + /** Load the rest of the assemblies specified with the '-r' option + */ + private def findAllAssemblies(): Unit = { + for (val file <- assemrefs.elements) { + if (file.isFile()) { + //System.out.println("Loading assembly " + file) + val assem = Assembly.LoadFrom(file.getPath()); + if (assem != null) { + assemblies += assem; + } + } + assemrefs -= file; + } + assert(assemrefs.isEmpty, assemrefs.toString()); + } + + //########################################################################## + // collect the members contained in a given namespace + + /** Find the position of the first type whose name starts with + * the given prefix; return the length of the types array if no match + * is found so the result can be used to terminate loop conditions + */ + private def findFirst(prefix: String): Int = { + var m = 0; + var n = alltypes.length - 1; + while (m < n) { + val l = (m + n) / 2; + val res = alltypes(l).FullName.compareTo(prefix); + if (res < 0) m = l + 1; + else n = l; + } + if (alltypes(m).FullName.startsWith(prefix)) m else alltypes.length; + } + + /** Collects the members contained in the given Scala package (namespace) + */ + def collectMembers(pakage: Symbol, typesMap: Map[String,Type], namespacesSet: Set[String]) = { + val namespace = if (pakage.isRoot) "" else pakage.fullNameString + "."; + val nl = namespace.length(); + var i = findFirst(namespace); + while (i < alltypes.length && alltypes(i).FullName.startsWith(namespace)) { + val typ = alltypes(i); + if (typ.FullName != "java.lang.Object" && typ.FullName != "java.lang.String") { + val k = typ.FullName.indexOf(".", nl); + if (k < 0) { + typesMap.update(typ.Name, typ); + } else { + namespacesSet += (typ.Namespace.substring(nl, k)); + } + } + i = i + 1; + } + } + + //########################################################################## +} // CLRTypes diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala new file mode 100644 index 000000000..2331ca997 --- /dev/null +++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala @@ -0,0 +1,506 @@ +/* NSC -- new scala compiler + * Copyright 2004-2006 LAMP/EPFL + */ + +// $Id: TypeParser.scala 8734 2006-09-21 14:56:02Z mihaylov $ + +package scala.tools.nsc.symtab.clr; + +import scala.tools.nsc.util.Position; +import classfile.UnPickler; +import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute, _}; + +import scala.collection.mutable.{HashMap, HashSet}; +import java.io.IOException; + +abstract class TypeParser { + + val global: Global; + + import global._; + import loaders.clrTypes; + + //########################################################################## + + private var clazz: Symbol = _; + private var instanceDefs: Scope = _; // was members + private var staticModule: Symbol = _; // was staticsClass + private var staticDefs: Scope = _; // was statics + + protected def statics: Symbol = staticModule.moduleClass + + protected var busy: boolean = false // lock to detect recursive reads + + private object unpickler extends UnPickler { + val global: TypeParser.this.global.type = TypeParser.this.global + } + + + def parse(typ: MSILType, root: Symbol): Unit = { + + def handleError(e: Exception) = { + if (settings.debug.value) e.printStackTrace(); //debug + throw new IOException("type '" + typ.FullName + "' is broken\n(" + e.getMessage() + ")") + } + assert(!busy) + busy = true + + if (root.isModule) { + this.clazz = root.linkedClassOfModule + this.staticModule = root + } else { + this.clazz = root + this.staticModule = root.linkedModuleOfClass + } + try { + parseClass(typ) + } catch { + case e: FatalError => handleError(e) + case e: RuntimeException => handleError(e) + } + busy = false + } + + private def parseClass(typ: MSILType): Unit = { + + clrTypes.types(clazz) = typ; + clrTypes.sym2type(typ) = clazz; + + if (typ.IsDefined(clrTypes.SCALA_SYMTAB_ATTR, false)) { + val attrs = typ.GetCustomAttributes(clrTypes.SCALA_SYMTAB_ATTR, false); + assert (attrs.length == 1, attrs.length); + val a = attrs(0).asInstanceOf[MSILAttribute]; + assert (a.getConstructor() == clrTypes.SYMTAB_CONSTR); + val symtab = a.getConstructorArguments()(0).asInstanceOf[Array[Byte]] + unpickler.unpickle(symtab, 0, clazz, staticModule, typ.FullName); + val mClass = clrTypes.getType(typ.FullName + "$"); + if (mClass != null) { + clrTypes.types(statics) = mClass; + val moduleInstance = mClass.GetField("MODULE$"); + assert (moduleInstance != null, mClass); + clrTypes.fields(statics) = moduleInstance; + } + return; + } + val flags = translateAttributes(typ); + val ifaces: Array[MSILType] = typ.getInterfaces(); + val superType = if (typ.BaseType() != null) getCLRType(typ.BaseType()) + else if (typ.IsInterface()) definitions.ObjectClass.tpe + else definitions.AnyClass.tpe; // this is System.Object + val parents = superType :: ifaces.map(getCLRType).toList; + instanceDefs = newScope; + staticDefs = newScope; + + val classInfo = ClassInfoType(parents, instanceDefs, clazz); + val staticInfo = ClassInfoType(List(), staticDefs, statics); + + clazz.setFlag(flags); + clazz.setInfo(classInfo); + statics.setFlag(Flags.JAVA); + statics.setInfo(staticInfo); + staticModule.setFlag(Flags.JAVA); + staticModule.setInfo(statics.tpe); + + // import nested types + for (val ntype <- typ.getNestedTypes(); (!(ntype.IsNestedPrivate + || ntype.IsNestedAssembly + || ntype.IsNestedFamANDAssem) + || ntype.IsInterface) ) + { + val loader = new loaders.MSILTypeLoader(ntype) + val nclazz = statics.newClass(NoPos, ntype.Name.toTypeName) + val nmodule = statics.newModule(NoPos, ntype.Name) + nclazz.setInfo(loader) + nmodule.setInfo(loader) + staticDefs.enter(nclazz) + staticDefs.enter(nmodule) + + assert(nclazz.linkedModuleOfClass == nmodule, nmodule); + assert(nmodule.linkedClassOfModule == nclazz, nclazz); + } + + val fields = typ.getFields(); + for (val field <- fields; !(field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly)) { + val flags = translateAttributes(field); + val name = newTermName(field.Name); + val fieldType = + if (field.IsLiteral && !field.FieldType.IsEnum) + ConstantType(getConstant(getCLRType(field.FieldType), field.getValue)) + else getCLRType(field.FieldType); + val owner = if (field.IsStatic()) statics else clazz; + val sym = owner.newValue(NoPos, name).setFlag(flags).setInfo(fieldType); + // TODO: set private within!!! -> look at typechecker/Namers.scala + (if (field.IsStatic()) staticDefs else instanceDefs).enter(sym); + clrTypes.fields(sym) = field; + } + + for (val constr <- typ.getConstructors(); !constr.IsStatic() && !constr.IsPrivate() && + !constr.IsAssembly() && !constr.IsFamilyAndAssembly()) + createMethod(constr); + + // initially also contains getters an setters of properties. + val methodsSet = new HashSet[MethodInfo](); + methodsSet ++= typ.getMethods(); + + for (val prop <- typ.getProperties) { + val propType: Type = getCLSType(prop.PropertyType); + if (propType != null) { + val getter: MethodInfo = prop.GetGetMethod(true); + val setter: MethodInfo = prop.GetSetMethod(true); + var gparamsLength: Int = -1; + if (!(getter == null || getter.IsPrivate || getter.IsAssembly + || getter.IsFamilyAndAssembly)) + { + assert(prop.PropertyType == getter.ReturnType); + val gparams: Array[ParameterInfo] = getter.GetParameters(); + gparamsLength = gparams.length; + val name: Name = if (gparamsLength == 0) prop.Name else nme.apply; + val flags = translateAttributes(getter); + val mtype: Type = if (gparamsLength == 0) PolyType(List(), propType) + else methodType(getter, getter.ReturnType); + val owner: Symbol = if (getter.IsStatic) statics else clazz; + val methodSym = owner.newMethod(NoPos, name).setFlag(flags).setInfo(mtype); + methodSym.setFlag(Flags.ACCESSOR); + (if (getter.IsStatic) staticDefs else instanceDefs).enter(methodSym) + clrTypes.methods(methodSym) = getter; + methodsSet -= getter; + } + if (!(setter == null || setter.IsPrivate || setter.IsAssembly + || setter.IsFamilyAndAssembly)) + { + val sparams: Array[ParameterInfo] = setter.GetParameters() + if(getter != null) + assert(getter.IsStatic == setter.IsStatic); + assert(setter.ReturnType == clrTypes.VOID); + if(getter != null) + assert(sparams.length == gparamsLength + 1, "" + getter + "; " + setter); + + val name: Name = if (gparamsLength == 0) nme.getterToSetter(prop.Name) + else nme.update; + val flags = translateAttributes(setter); + val mtype: Type = methodType(setter, definitions.UnitClass.tpe); + val owner: Symbol = if (setter.IsStatic) statics else clazz; + val methodSym = owner.newMethod(NoPos, name).setFlag(flags).setInfo(mtype); + methodSym.setFlag(Flags.ACCESSOR); + (if (setter.IsStatic) staticDefs else instanceDefs).enter(methodSym); + clrTypes.methods(methodSym) = setter; + methodsSet -= setter; + } + } + } + +/* for (val event <- typ.GetEvents) { + // adding += and -= methods to add delegates to an event. + // raising the event ist not possible from outside the class (this is so + // generally in .net world) + val adder: MethodInfo = event.GetAddMethod(); + val remover: MethodInfo = event.GetRemoveMethod(); + if (!(adder == null || adder.IsPrivate || adder.IsAssembly + || adder.IsFamilyAndAssembly)) + { + assert(adder.ReturnType == clrTypes.VOID); + assert(adder.GetParameters().map(.ParameterType).toList == List(event.EventHandlerType)); + val name = encode("+="); + val flags = translateAttributes(adder); + val mtype: Type = methodType(adder, adder.ReturnType); + createMethod(name, flags, mtype, adder, adder.IsStatic) + methodsSet -= adder; + } + if (!(remover == null || remover.IsPrivate || remover.IsAssembly + || remover.IsFamilyAndAssembly)) + { + assert(remover.ReturnType == clrTypes.VOID); + assert(remover.GetParameters().map(.ParameterType).toList == List(event.EventHandlerType)); + val name = encode("-="); + val flags = translateAttributes(remover); + val mtype: Type = methodType(remover, remover.ReturnType); + createMethod(name, flags, mtype, remover, remover.IsStatic) + methodsSet -= remover; + } + } */ + + for (val method <- methodsSet.elements) + if (!method.IsPrivate() && !method.IsAssembly() && !method.IsFamilyAndAssembly()) + createMethod(method); + + + // Create methods and views for delegate support + if (clrTypes.isDelegateType(typ)) { + createDelegateView(typ); + createDelegateChainers(typ); + } + + // create the box/unbox methods for value types + if (typ.IsValueType) { + val box = statics.newMethod(NoPos, nme.box). + setInfo(MethodType(List(clazz.tpe), definitions.ObjectClass.tpe)); + definitions.isBox += box; + definitions.boxMethod(clazz) = box; + val unbox = statics.newMethod(NoPos, nme.unbox). + setInfo(MethodType(List(definitions.ObjectClass.tpe), clazz.tpe)); + definitions.isUnbox += unbox; + definitions.unboxMethod(clazz) = unbox; + //Console.println(typ.FullName + " : " + parents); + } + + // for enumerations introduce comparison and bitwise logical operations; + // the backend should recognize and replace them with comparison or + // bitwise logical operations on the primitive underlying type + + if (typ.IsEnum) { + val ENUM_CMP_NAMES = List(nme.EQ, nme.NE, nme.LT, nme.LE, nme.GT, nme.GE); + val ENUM_BIT_LOG_NAMES = List(nme.OR, nme.AND, nme.XOR); + + val flags = Flags.JAVA | Flags.FINAL; + for (val cmpName <- ENUM_CMP_NAMES) { + val enumCmpType: Type = JavaMethodType(List(clazz.tpe), definitions.BooleanClass.tpe); + val enumCmp: Symbol = clazz.newMethod(NoPos, cmpName); + enumCmp.setFlag(flags).setInfo(enumCmpType) + instanceDefs.enter(enumCmp); + } + + for (val bitLogName <- ENUM_BIT_LOG_NAMES) { + val enumBitLogType = JavaMethodType(List(clazz.tpe), classInfo); + val enumBitLog = clazz.newMethod(NoPos, bitLogName); + enumBitLog.setFlag(flags).setInfo(enumBitLogType); + instanceDefs.enter(enumBitLog); + } + } + + } // parseClass + + private def createMethod(method: MethodBase): Unit = { + val rettype = if (method.IsConstructor()) clazz.tpe + else getCLSType(method.asInstanceOf[MethodInfo].ReturnType); + if (rettype == null) return; + val mtype = methodType(method, rettype); + if (mtype == null) return; + val flags = translateAttributes(method); + val owner = if (method.IsStatic()) statics else clazz; + val methodSym = owner.newMethod(NoPos, getName(method)).setFlag(flags). + setInfo(mtype); + (if (method.IsStatic()) staticDefs else instanceDefs).enter(methodSym); + if (method.IsConstructor()) + clrTypes.constructors(methodSym) = method.asInstanceOf[ConstructorInfo] + else clrTypes.methods(methodSym) = method.asInstanceOf[MethodInfo]; + } + + private def createMethod(name: Name, flags: Long, args: Array[MSILType], retType: MSILType, method: MethodInfo, statik: Boolean): Symbol = { + val mtype: Type = methodType(args, getCLSType(retType)); + assert(mtype != null); + createMethod(name, flags, mtype, method, statik) + } + + private def createMethod(name: Name, flags: Long, mtype: Type, method: MethodInfo, statik: Boolean): Symbol = { + val methodSym: Symbol = (if (statik) statics else clazz).newMethod(NoPos, name); + methodSym.setFlag(flags).setInfo(mtype); + (if (statik) staticDefs else instanceDefs).enter(methodSym); + if (method != null) + clrTypes.methods(methodSym) = method; + methodSym + } + + private def createDelegateView(typ: MSILType) = { + val invoke: MethodInfo = typ.GetMember("Invoke")(0).asInstanceOf[MethodInfo]; + val invokeRetType: Type = getCLRType(invoke.ReturnType); + val invokeParamTypes: List[Type] =invoke.GetParameters().map(.ParameterType).map(getCLSType).toList; + val funType: Type = definitions.functionType(invokeParamTypes, invokeRetType); + + val typClrType: Type = getCLRType(typ); + val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? think not needed + + // create the forward view: delegate => function + val delegateParamTypes: List[Type] = List(typClrType); + // not ImplicitMethodType, this is for methods with implicit parameters (not implicit methods) + val forwardViewMethodType = JavaMethodType(delegateParamTypes, funType); + val fmsym = createMethod(nme.view_, flags, forwardViewMethodType, null, true); + + // create the backward view: function => delegate + val functionParamTypes: List[Type] = List(funType); + val backwardViewMethodType = JavaMethodType(functionParamTypes, typClrType); + val bmsym = createMethod(nme.view_, flags, backwardViewMethodType, null, true); + } + + private def createDelegateChainers(typ: MSILType) = { + val flags: Long = Flags.JAVA | Flags.FINAL; + val args: Array[MSILType] = Array(typ); + + var s = createMethod(encode("+="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_COMBINE, false); + s = createMethod(encode("-="), flags, args, clrTypes.VOID, clrTypes.DELEGATE_REMOVE, false); + + s = createMethod(nme.PLUS, flags, args, typ, clrTypes.DELEGATE_COMBINE, false); + s = createMethod(nme.MINUS, flags, args, typ, clrTypes.DELEGATE_REMOVE, false); + } + + private def getName(method: MethodBase): Name = { + if (method.IsConstructor()) return nme.CONSTRUCTOR; + val name = method.Name; + if (method.IsStatic()) return newTermName(name); + val params = method.GetParameters(); + name match { + case "GetHashCode" if (params.length == 0) => nme.hashCode_; + case "ToString" if (params.length == 0) => nme.toString_; + case "Finalize" if (params.length == 0) => nme.finalize_; + case "Equals" if (params.length == 1 && params(0).ParameterType == clrTypes.OBJECT) => + nme.equals_; + case "Invoke" if (clrTypes.isDelegateType(method.DeclaringType)) => nme.apply; + case _ => newTermName(name); + } + } + + //########################################################################## + + private def methodType(method: MethodBase, rettype: MSILType): Type = { + val rtype = getCLSType(rettype); + if (rtype == null) null else methodType(method, rtype); + } + + /** Return a method type for the given method. */ + private def methodType(method: MethodBase, rettype: Type): Type = + methodType(method.GetParameters().map(.ParameterType), rettype); + + /** Return a method type for the provided argument types and return type. */ + private def methodType(argtypes: Array[MSILType], rettype: Type): Type = { + def paramType(typ: MSILType): Type = + if (typ eq clrTypes.OBJECT) definitions.AnyClass.tpe + else getCLSType(typ); + val ptypes = argtypes.map(paramType).toList; + if (ptypes.contains(null)) null + else JavaMethodType(ptypes, rettype); + } + + //########################################################################## + + private def getClassType(typ: MSILType): Type = { + assert(typ != null); + val res = definitions.getClass(typ.FullName.replace('+', '.')).tpe; + //if (res.isError()) + // global.reporter.error("unknown class reference " + type.FullName); + res + } + + private def getCLSType(typ: MSILType): Type = { + if (/*type == clrTypes.BYTE ||*/ typ == clrTypes.USHORT + || typ == clrTypes.UINT || typ == clrTypes.ULONG + || typ.IsNotPublic() || typ.IsNestedPrivate() + || typ.IsNestedAssembly() || typ.IsNestedFamANDAssem() + || typ.IsPointer() + || (typ.IsArray() && getCLSType(typ.GetElementType()) == null)) + null; + //Symbol s = clrTypes.getSymbol(type); + //scalac.symtab.Type t = s != null ? make.classType(s) : getCLRType(type); + else + getCLRType(typ) + } + + private def getCLRType(typ: MSILType): Type = + if (typ == clrTypes.OBJECT) + definitions.ObjectClass.tpe; + else if (typ == clrTypes.VALUE_TYPE) + definitions.AnyValClass.tpe + else if (typ == clrTypes.STRING) + definitions.StringClass.tpe; + else if (typ == clrTypes.VOID) + definitions.UnitClass.tpe + else if (typ == clrTypes.BOOLEAN) + definitions.BooleanClass.tpe + else if (typ == clrTypes.CHAR) + definitions.CharClass.tpe + else if (typ == clrTypes.BYTE || typ == clrTypes.UBYTE) + definitions.ByteClass.tpe + else if (typ == clrTypes.SHORT || typ == clrTypes.USHORT) + definitions.ShortClass.tpe + else if (typ == clrTypes.INT || typ == clrTypes.UINT) + definitions.IntClass.tpe + else if (typ == clrTypes.LONG || typ == clrTypes.ULONG) + definitions.LongClass.tpe + else if (typ == clrTypes.FLOAT) + definitions.FloatClass.tpe + else if (typ == clrTypes.DOUBLE) + definitions.DoubleClass.tpe + else if (typ.IsArray()) + appliedType(definitions.ArrayClass.tpe, + List(getCLRType(typ.GetElementType()))); + else { + val res = clrTypes.sym2type.get (typ) match { + case Some(sym) => sym.tpe + case None => getClassType(typ); + } + assert (res != null, typ) + res + } + + // the values are Java-Box-Classes (e.g. Integer, Boolean, Character) + // java.lang.Number to get the value (if a number, not for boolean, character) + // see ch.epfl.lamp.compiler.msil.util.PEStream.java + def getConstant(constType: Type, value: Object): Constant = { + val typeClass = constType.symbol + if (typeClass == definitions.BooleanClass) + Constant(value.asInstanceOf[java.lang.Boolean].booleanValue) + else if (typeClass == definitions.ByteClass) + Constant(value.asInstanceOf[java.lang.Number].byteValue) + else if (typeClass == definitions.ShortClass) + Constant(value.asInstanceOf[java.lang.Number].shortValue) + else if (typeClass == definitions.CharClass) + Constant(value.asInstanceOf[java.lang.Character].charValue) + else if (typeClass == definitions.IntClass) + Constant(value.asInstanceOf[java.lang.Number].intValue) + else if (typeClass == definitions.LongClass) + Constant(value.asInstanceOf[java.lang.Number].longValue) + else if (typeClass == definitions.FloatClass) + Constant(value.asInstanceOf[java.lang.Number].floatValue) + else if (typeClass == definitions.DoubleClass) + Constant(value.asInstanceOf[java.lang.Number].doubleValue) + else if (typeClass == definitions.StringClass) + Constant(value.asInstanceOf[java.lang.String]) + else + abort("illegal value: " + value + ", class-symbol: " + typeClass) + } + + private def translateAttributes(typ: MSILType): Long = { + var flags: Long = Flags.JAVA; + if (typ.IsNotPublic() || typ.IsNestedPrivate() + || typ.IsNestedAssembly() || typ.IsNestedFamANDAssem()) + flags = flags | Flags.PRIVATE; + else if (typ.IsNestedFamily() || typ.IsNestedFamORAssem()) + flags = flags | Flags.PROTECTED; + if (typ.IsAbstract()) + flags = flags | Flags.ABSTRACT; + if (typ.IsSealed()) + flags = flags | Flags.FINAL; + if (typ.IsInterface()) + flags = flags | Flags.INTERFACE | Flags.TRAIT | Flags.ABSTRACT; + + flags + } + + private def translateAttributes(field: FieldInfo): Long = { + var flags: Long = Flags.JAVA; + if (field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly()) + flags = flags | Flags.PRIVATE; + else if (field.IsFamily() || field.IsFamilyOrAssembly()) + flags = flags | Flags.PROTECTED; + if (field.IsInitOnly()) + flags = flags | Flags.FINAL; + else + flags = flags | Flags.MUTABLE; + if (field.IsStatic) + flags = flags | Flags.STATIC + + flags + } + + private def translateAttributes(method: MethodBase): Long = { + var flags: Long = Flags.JAVA; + if (method.IsPrivate() || method.IsAssembly() || method.IsFamilyAndAssembly()) + flags = flags | Flags.PRIVATE; + else if (method.IsFamily() || method.IsFamilyOrAssembly()) + flags = flags | Flags.PROTECTED; + if (method.IsAbstract()) + flags = flags | Flags.DEFERRED; + if (method.IsStatic) + flags = flags | Flags.STATIC + + flags + } +} diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index 8bfe442d9..f20f5a9bc 100644 --- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala +++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala @@ -69,7 +69,7 @@ abstract class CleanUp extends Transform { } override def transformUnit(unit: CompilationUnit) = - if (settings.target.value != "jvm-1.5") { + if (settings.target.value != "jvm-1.5" && !forMSIL) { unit.body = transform(unit.body) } diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala index 02aebda8d..b72e557b0 100644 --- a/src/compiler/scala/tools/nsc/transform/Erasure.scala +++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala @@ -85,7 +85,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { else apply(parents.head) case ClassInfoType(parents, decls, clazz) => ClassInfoType( - if ((clazz == ObjectClass) || (isValueClass(clazz))) List() + if ((clazz == ObjectClass) || (isValueType(clazz))) List() else if (clazz == ArrayClass) List(erasedTypeRef(ObjectClass)) else removeDoubleObject(parents map this), decls, clazz) @@ -308,7 +308,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { } else gen.mkAttributedCast(tree, pt) } else gen.mkAttributedCast(tree, pt) - + /** Is symbol a member of unboxed arrays (which will be expanded directly * later)? * @@ -330,10 +330,10 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { */ private def adaptToType(tree: Tree, pt: Type): Tree = { if (settings.debug.value && pt != WildcardType) - log("adapting " + tree + ":" + tree.tpe + " to " + pt)//debug - if (tree.tpe <:< pt) + log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug + if (tree.tpe <:< pt) tree - else if (isUnboxedClass(tree.tpe.symbol) && !isUnboxedClass(pt.symbol)) + else if (isUnboxedClass(tree.tpe.symbol) && !isUnboxedClass(pt.symbol)) adaptToType(box(tree), pt) else if (tree.tpe.isInstanceOf[MethodType] && tree.tpe.paramTypes.isEmpty) { assert(tree.symbol.isStable); @@ -403,6 +403,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { * */ private def adaptMember(tree: Tree): Tree = { + //Console.println("adaptMember: " + tree); tree match { case Apply(Select(New(tpt), name), args) if (tpt.tpe.symbol == BoxedArrayClass) => assert(name == nme.CONSTRUCTOR); @@ -417,7 +418,7 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { if (isNumericValueClass(qualClass) && isNumericValueClass(targClass)) // convert numeric type casts atPos(tree.pos)(Apply(Select(qual1, "to" + targClass.name), List())) - else if (isValueClass(targClass) || + else if (isValueType(targClass) || (targClass == ArrayClass && (qualClass isNonBottomSubClass BoxedArrayClass))) unbox(qual1, targ.tpe) else if (targClass == ArrayClass && qualClass == ObjectClass) @@ -435,10 +436,10 @@ abstract class Erasure extends AddInterfaces with typechecker.Analyzer { adaptMember(atPos(tree.pos)(Select(qual, getMember(ObjectClass, name)))) else { var qual1 = typedQualifier(qual); - if ((isValueClass(qual1.tpe.symbol) && !isUnboxedValueMember(tree.symbol)) || + if ((isValueType(qual1.tpe.symbol) && !isUnboxedValueMember(tree.symbol)) || (qual1.tpe.symbol == ArrayClass && !isUnboxedArrayMember(tree.symbol))) qual1 = box(qual1); - else if (!isValueClass(qual1.tpe.symbol) && isUnboxedValueMember(tree.symbol)) + else if (!isValueType(qual1.tpe.symbol) && isUnboxedValueMember(tree.symbol)) qual1 = unbox(qual1, tree.symbol.owner.tpe) else if (tree.symbol.owner == ArrayClass && qual1.tpe.symbol == ObjectClass) qual1 = cast(qual1, BoxedArrayClass.tpe) diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 82c61cfe1..6e95086d2 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -243,7 +243,7 @@ trait SyntheticMethods requires Analyzer { // jw-04-2003/jw-0425-designpatterns_p.html) if (!hasImplementation(nme.readResolve)) ts += readResolveMethod } - if (!forCLDC) + if (!forCLDC && !forMSIL) for (val sym <- clazz.info.decls.toList) if (!sym.getAttributes(BeanPropertyAttr).isEmpty) if (sym.isGetter) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index d2ebabfc4..bd03d1357 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -639,6 +639,14 @@ trait Typers requires Analyzer { // (13); the condition prevents chains of views if (settings.debug.value) log("inferring view from "+tree.tpe+" to "+pt) val coercion = inferView(tree.pos, tree.tpe, pt, true) + // convert forward views of delegate types into closures wrapped around + // the delegate's apply method (the "Invoke" method, which was translated into apply) + if (forMSIL && coercion != null && isCorrespondingDelegate(tree.tpe, pt)) { + val meth: Symbol = tree.tpe.member(nme.apply) + if(settings.debug.value) + log("replacing forward delegate view with: " + meth + ":" + meth.tpe) + return typed(Select(tree, meth), mode, pt) + } if (coercion != EmptyTree) { if (settings.debug.value) log("inferred view from "+tree.tpe+" to "+pt+" = "+coercion+":"+coercion.tpe) return typed(Apply(coercion, List(tree)) setPos tree.pos, mode, pt) @@ -1187,7 +1195,7 @@ trait Typers requires Analyzer { * @return ... */ def typedFunction(fun: Function, mode: int, pt: Type): Tree = { - val codeExpected = !forCLDC && (pt.symbol isNonBottomSubClass CodeClass) + val codeExpected = !forCLDC && !forMSIL && (pt.symbol isNonBottomSubClass CodeClass) def decompose(pt: Type): {Symbol, List[Type], Type} = if (isFunctionType(pt) @@ -1361,6 +1369,28 @@ trait Typers requires Analyzer { case MethodType(_, rtp) if ((mode & PATTERNmode) != 0) => rtp case _ => tp } + + // Replace the Delegate-Chainer methods += and -= with corresponding + // + and - calls, which are translated in the code generator into + // Combine and Remove + if (forMSIL) { + fun match { + case Select(qual, name) => + if (isSubType(qual.tpe, definitions.DelegateClass.tpe) + && (name == encode("+=") || name == encode("-="))) + { + val n = if (name == encode("+=")) nme.PLUS else nme.MINUS + val f = Select(qual, n) + // the compiler thinks, the PLUS method takes only one argument, + // but he thinks it's an instance method -> still two ref's on the stack + // -> translated by backend + val rhs = copy.Apply(tree, f, args) + return typed(Assign(qual, rhs)) + } + case _ => () + } + } + if (fun.symbol == List_apply && args.isEmpty) { atPos(tree.pos) { gen.mkNil setType restpe } } else if ((mode & CONSTmode) != 0 && fun.symbol.owner == PredefModule.tpe.symbol && fun.symbol.name == nme.Array) { @@ -2007,7 +2037,23 @@ trait Typers requires Analyzer { case PolyType(_, MethodType(formals, _)) => adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) case MethodType(formals, _) => - adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) + expr1 match { + case Select(qual, name) => + if(forMSIL && pt != WildcardType && pt != ErrorType && isSubType(pt, definitions.DelegateClass.tpe)) { + val scalaCaller = newScalaCaller(pt); + addScalaCallerInfo(scalaCaller, expr1.symbol) + val n: Name = scalaCaller.name + val del = Ident(DelegateClass) setType DelegateClass.tpe + val f = Select(del, n) + //val f1 = TypeApply(f, List(Ident(pt.symbol) setType pt)) + val args: List[Tree] = if(expr1.symbol.isStatic) List(Literal(Constant(null))) + else List(qual) // where the scala-method is located + val rhs = Apply(f, args); + return typed(rhs) + } + case _ => () + } + adapt(expr1, mode, functionType(formals map (t => WildcardType), WildcardType)) case ErrorType => expr1 case _ => diff --git a/src/library/scala/Console.scala b/src/library/scala/Console.scala index 6b8c5e64f..c4aabd9ba 100644 --- a/src/library/scala/Console.scala +++ b/src/library/scala/Console.scala @@ -204,7 +204,7 @@ object Console { */ def readLine(text: String, args: Any*): String = { format(text, args: _*) - in.readLine() + readLine } @@ -212,7 +212,7 @@ object Console { * * @return the boolean value read from the terminal. */ - def readBoolean: Boolean = in.readLine().toLowerCase() match { + def readBoolean: Boolean = readLine.toLowerCase() match { case "true" => true case "t" => true case "yes" => true @@ -252,7 +252,7 @@ object Console { * @return a list of all extracted values. */ def readf(format: String): List[Any] = - textComponents(new MessageFormat(format).parse(in.readLine())) + textComponents(new MessageFormat(format).parse(readLine)) /** Read in some structured input, specified by a format specifier. * Opposed to readf, this function only returns the diff --git a/src/library/scala/Enumeration.scala b/src/library/scala/Enumeration.scala index ac8a51c1c..d5e5e8023 100644 --- a/src/library/scala/Enumeration.scala +++ b/src/library/scala/Enumeration.scala @@ -45,7 +45,7 @@ abstract class Enumeration(initial: Int, names: String*) { def this(names: String*) = this(0, names: _*) def name = { - val cname = compat.Platform.getClassName(this) + val cname = this.getClass().getName() if (cname.endsWith("$")) cname.substring(0, cname.length() - 1) else if (cname.endsWith("$class")) diff --git a/src/library/scala/compat/Platform.scala b/src/library/scala/compat/Platform.scala index 028c9c8ab..11e90a078 100644 --- a/src/library/scala/compat/Platform.scala +++ b/src/library/scala/compat/Platform.scala @@ -26,15 +26,12 @@ object Platform { def createArray(elemClass: Class, length: Int): AnyRef = java.lang.reflect.Array.newInstance(elemClass, length) - def getClass(obj: AnyRef) = obj.getClass() - def getClassName(obj: AnyRef) = obj.getClass().getName() - def getName(cls: Class) = cls.getName() - def getElementClass(obj: AnyRef) = obj.getClass().getComponentType() def getClassForName(name: String): Class = java.lang.Class.forName(name) val EOL = System.getProperty("line.separator", "\n") def currentTime: Long = System.currentTimeMillis() + def collectGarbage: Unit = System.gc() } diff --git a/src/library/scala/runtime/BoxedObjectArray.scala b/src/library/scala/runtime/BoxedObjectArray.scala index b9303960f..4bb8e18d9 100644 --- a/src/library/scala/runtime/BoxedObjectArray.scala +++ b/src/library/scala/runtime/BoxedObjectArray.scala @@ -13,7 +13,7 @@ package scala.runtime import Predef.Class -import compat.Platform.{createArray, getElementClass} +import compat.Platform.createArray [serializable] final class BoxedObjectArray(val value: Array[AnyRef]) extends BoxedArray { @@ -36,7 +36,7 @@ final class BoxedObjectArray(val value: Array[AnyRef]) extends BoxedArray { override def hashCode(): Int = value.hashCode() private def create(length: Int): Array[AnyRef] = { - createArray(getElementClass(value), length).asInstanceOf[Array[AnyRef]] + createArray(value.getClass().getComponentType(), length).asInstanceOf[Array[AnyRef]] } override def subArray(start: Int, end: Int): Array[AnyRef] = { diff --git a/src/library/scala/util/automata/BaseBerrySethi.scala b/src/library/scala/util/automata/BaseBerrySethi.scala index 32231b86b..295c1d681 100644 --- a/src/library/scala/util/automata/BaseBerrySethi.scala +++ b/src/library/scala/util/automata/BaseBerrySethi.scala @@ -16,7 +16,6 @@ import scala.util.regexp.Base import scala.collection.mutable import scala.collection.immutable -import compat.Platform /** this turns a regexp over A into a NondetWorkAutom over A using the * celebrated position automata construction (also called Berry-Sethi or @@ -64,7 +63,7 @@ abstract class BaseBerrySethi { tmp case Star(t) => compFirst(t) case _ => - throw new IllegalArgumentException("unexpected pattern " + Platform.getClass(r)) + throw new IllegalArgumentException("unexpected pattern " + r.getClass()) } /** computes last( r ) for the regexp r */ @@ -89,7 +88,7 @@ abstract class BaseBerrySethi { tmp case Star(t) => compLast(t) case _ => - throw new IllegalArgumentException("unexpected pattern " + Platform.getClass(r)) + throw new IllegalArgumentException("unexpected pattern " + r.getClass()) } /** Starts from the right-to-left @@ -170,7 +169,7 @@ abstract class BaseBerrySethi { first case _ => - throw new IllegalArgumentException("unexpected pattern: " + Platform.getClass(r)) + throw new IllegalArgumentException("unexpected pattern: " + r.getClass()) } } @@ -193,7 +192,7 @@ abstract class BaseBerrySethi { case Star(t) => traverse(t) case _ => - throw new IllegalArgumentException("unexp pattern " + Platform.getClass(r)) + throw new IllegalArgumentException("unexp pattern " + r.getClass()) } } diff --git a/test/files/run/bug560bis.check b/test/files/jvm/bug560bis.check similarity index 100% rename from test/files/run/bug560bis.check rename to test/files/jvm/bug560bis.check diff --git a/test/files/run/bug560bis.scala b/test/files/jvm/bug560bis.scala similarity index 100% rename from test/files/run/bug560bis.scala rename to test/files/jvm/bug560bis.scala diff --git a/test/files/run/Course-2002-03.scala b/test/files/run/Course-2002-03.scala index 7933514f9..5a470a356 100644 --- a/test/files/run/Course-2002-03.scala +++ b/test/files/run/Course-2002-03.scala @@ -3,6 +3,8 @@ //############################################################################ // $Id$ +import scala.compat.StringBuilder; + object M0 { class Rational(x: Int, y: Int) { def numer = x; @@ -230,10 +232,10 @@ object M8 { def intersect2(that: IntSet): IntSet = filter(x => that.contains(x)); def filter(f: Int => Boolean): IntSet = filter0(f, new Empty); - def printOn(out: java.io.PrintStream) = foreach(out.println); + def print() = foreach(Console.println); override def toString(): String = { - val buffer: java.lang.StringBuffer = new java.lang.StringBuffer(); + val buffer: StringBuilder = new StringBuilder(); buffer.append('['); foreach(i => { if (buffer.length() > 1) {buffer.append(','); ()}; // !!! ; () @@ -312,7 +314,7 @@ object M8 { Console.println; Console.println("set4 contains the following elements:"); - set4.printOn(java.lang.System.out); + set4.print(); Console.println; Console.println("2 <- set2: " + (set2 contains 2)); diff --git a/test/files/run/bugs.scala b/test/files/run/bugs.scala index 92dd0c04d..512e25225 100644 --- a/test/files/run/bugs.scala +++ b/test/files/run/bugs.scala @@ -359,9 +359,9 @@ trait Bug266BB extends Bug266BA { val in = 3; } -object Bug266BTest extends Application { - val a: Bug266BA1 = new Bug266BA1 with Bug266BB; - a.mkP.f(a.in); +object Bug266BTest { + val a: Bug266BA1 = new Bug266BA1 with Bug266BB; + def test(args: Array[String]): Unit = a.mkP.f(a.in); } // main @@ -369,7 +369,7 @@ object Bug266BTest extends Application { object Bug266Test { def test(args: Array[String]): Unit = { Bug266ATest.test(args); - Bug266BTest; + Bug266BTest.test(args); } } @@ -412,9 +412,9 @@ trait Bug396C extends Bug396A { override def run = { super.run; Console.println("C"); } } } -object Bug396Test extends Application with Bug396B with Bug396C { +object Bug396Test extends Bug396B with Bug396C { class I2 extends super[Bug396B].I with super[Bug396C].I; - (new I2).run + def test(args: Array[String]): Unit = (new I2).run } //############################################################################ @@ -478,7 +478,7 @@ object Test { test(266, Bug266Test.test(args)); test(316, Bug316Test.test(args)); test(328, Bug328Test.test(args)); - test(396, Bug396Test); + test(396, Bug396Test.test(args)); test(399, Bug399Test.test(args)); if (errors > 0) { diff --git a/test/files/run/richs-msil.check b/test/files/run/richs-msil.check new file mode 100644 index 000000000..e628ad399 --- /dev/null +++ b/test/files/run/richs-msil.check @@ -0,0 +1,66 @@ + +RichCharTest1: +True +True +True +False + +RichIntTest: +10 +11 +12 +13 +0 +0 + +RichStringTest1: +s1: abc +s2: abc\txyz\n +s3: abc + xyz +s4: abc + |xyz +s5: abc + #xyz + +RichStringTest2: +s1: abc +s2: abc\txyz\n +s3: abc + xyz +s4: abc + |xyz +s5: abc + #xyz + +RichStringTest3: +s1: abc +s2: abc\txyz\n +s3: abc + xyz +s4: abc + |xyz +s5: abc + #xyz + +RichStringTest4: +s1: abc +s2: abc\txyz\n +s3: abc + xyz +s4: abc +xyz +s5: abc + #xyz + +RichStringTest5: +s1: abc + xyz +s2: abc + xyz +s3: abc + xyz +s4: abc + |xyz +s5: abc +xyz diff --git a/test/files/run/richs.check b/test/files/run/richs.check index 3b73f084a..80b2ef3ef 100644 --- a/test/files/run/richs.check +++ b/test/files/run/richs.check @@ -5,9 +5,6 @@ true true true -RichCharTest2: -true - RichIntTest: 10 11 diff --git a/test/files/run/richs.scala b/test/files/run/richs.scala index 90eb46d9a..4df7bc3bf 100644 --- a/test/files/run/richs.scala +++ b/test/files/run/richs.scala @@ -8,7 +8,7 @@ trait RichTest { val s5 = """abc #xyz""" def getObjectName: String = { - val cn = compat.Platform.getClassName(this) + val cn = this.getClass().getName() cn.substring(0, cn.length-1) } def length[A](it: Iterator[A]) = it.toList length @@ -23,24 +23,24 @@ object RichCharTest1 extends RichTest { Console.println('A'.asDigit == 10) } } -object RichCharTest2 extends RichTest { - case class C(s: String) { - private val it = s.elements - private var c: Char = _ - def ch(): Char = c - def nextch(): Unit = { c = if (it.hasNext) it.next else ';' } - def err(msg: String) = Console.println(msg) - nextch() - } - def run { - Console.println("\n" + getObjectName + ":") - val c1 = C("x4A;") - val s1 = xml.Utility.parseCharRef(c1.ch, c1.nextch, c1.err) - val c2 = C("74;") - val s2 = xml.Utility.parseCharRef(c2.ch, c2.nextch, c2.err) - Console.println(s1 == s2) - } -} +// object RichCharTest2 extends RichTest { +// case class C(s: String) { +// private val it = s.elements +// private var c: Char = _ +// def ch(): Char = c +// def nextch(): Unit = { c = if (it.hasNext) it.next else ';' } +// def err(msg: String) = Console.println(msg) +// nextch() +// } +// def run { +// Console.println("\n" + getObjectName + ":") +// val c1 = C("x4A;") +// val s1 = xml.Utility.parseCharRef(c1.ch, c1.nextch, c1.err) +// val c2 = C("74;") +// val s2 = xml.Utility.parseCharRef(c2.ch, c2.nextch, c2.err) +// Console.println(s1 == s2) +// } +// } object RichIntTest extends RichTest { private val n = 10 private val m = -2 @@ -107,7 +107,7 @@ object RichStringTest5 extends RichTest { object Test { def main(args: Array[String]): Unit = { RichCharTest1.run - RichCharTest2.run + //RichCharTest2.run RichIntTest.run RichStringTest1.run RichStringTest2.run diff --git a/test/files/run/try.scala b/test/files/run/try.scala index 853afce8a..2e853d204 100644 --- a/test/files/run/try.scala +++ b/test/files/run/try.scala @@ -43,12 +43,12 @@ object Test extends AnyRef with Application { def try4 = { if (instance == null) { instance = try { - new String(); - } catch { + "" //new String(); + } catch { case _ => val cs = "aaa"; if (cs.length() > 0) { - new String(); + "" //new String(); } else { throw new Error("fatal error"); null diff --git a/test/scalatest b/test/scalatest index a9dc13cc7..283cd5a56 100755 --- a/test/scalatest +++ b/test/scalatest @@ -687,9 +687,9 @@ fi; SCALA="${BIN_DIR}scala"; if [ "$USEFSC" = "true" ]; then - SCALAC="${BIN_DIR}fsc -deprecation -encoding iso-8859-1"; + SCALAC="${BIN_DIR}fsc -nowarn -deprecation -encoding iso-8859-1"; else - SCALAC="${BIN_DIR}scalac -deprecation -encoding iso-8859-1"; + SCALAC="${BIN_DIR}scalac -nowarn -deprecation -encoding iso-8859-1"; fi; SCALAP="scalap"; ANT="ant";