From de82b3723271c5ca3655cfd76eea0638f0d18eb1 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 3 Feb 2012 11:57:47 -0800 Subject: Disabled all things MSIL. It's a huge drag when making fundamental changes. --- src/compiler/scala/tools/nsc/Global.scala | 9 +- .../scala/tools/nsc/backend/MSILPlatform.scala | 130 +- .../scala/tools/nsc/backend/icode/GenICode.scala | 322 +- .../scala/tools/nsc/backend/icode/Opcodes.scala | 114 +- .../scala/tools/nsc/backend/icode/TypeKinds.scala | 13 +- .../scala/tools/nsc/backend/msil/GenMSIL.scala | 4714 ++++++++++---------- src/compiler/scala/tools/nsc/io/MsilFile.scala | 36 +- .../scala/tools/nsc/symtab/SymbolLoaders.scala | 32 +- .../scala/tools/nsc/symtab/clr/CLRTypes.scala | 274 +- .../scala/tools/nsc/symtab/clr/TypeParser.scala | 1702 +++---- .../scala/tools/nsc/util/MsilClassPath.scala | 338 +- 11 files changed, 3842 insertions(+), 3842 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 8e5ca2156a..31269829bf 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -27,7 +27,8 @@ import typechecker._ import transform._ import backend.icode.{ ICodes, GenICode, ICodeCheckers } -import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform } +// import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform } +import backend.{ ScalaPrimitives, Platform, JavaPlatform } import backend.jvm.GenJVM import backend.opt.{ Inliners, InlineExceptionHandlers, ClosureElimination, DeadCodeElimination } import backend.icode.analysis._ @@ -65,9 +66,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb type ThisPlatform = Platform { val global: Global.this.type } - lazy val platform: ThisPlatform = - if (forMSIL) new { val global: Global.this.type = Global.this } with MSILPlatform - else new { val global: Global.this.type = Global.this } with JavaPlatform + lazy val platform: ThisPlatform = new { val global: Global.this.type = Global.this } with JavaPlatform + // if (forMSIL) new { val global: Global.this.type = Global.this } with MSILPlatform + // else new { val global: Global.this.type = Global.this } with JavaPlatform def classPath: ClassPath[platform.BinaryRepr] = platform.classPath def rootLoader: LazyType = platform.rootLoader diff --git a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala b/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala index 65b1fbc229..a86528d492 100644 --- a/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/MSILPlatform.scala @@ -1,65 +1,65 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package backend - -import ch.epfl.lamp.compiler.{ msil => msillib } -import util.{ ClassPath, MsilClassPath } -import msil.GenMSIL -import io.{ AbstractFile, MsilFile } - -trait MSILPlatform extends Platform { - import global._ - import definitions.{ ComparatorClass, BoxedNumberClass, getMember } - - type BinaryRepr = MsilFile - - if (settings.verbose.value) - inform("[AssemRefs = " + settings.assemrefs.value + "]") - - // phaseName = "msil" - object genMSIL extends { - val global: MSILPlatform.this.global.type = MSILPlatform.this.global - val runsAfter = List[String]("dce") - val runsRightAfter = None - } with GenMSIL - - lazy val classPath = MsilClassPath.fromSettings(settings) - def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]]) - // See discussion in JavaPlatForm for why we need a cast here. - - def platformPhases = List( - genMSIL // generate .msil files - ) - - lazy val externalEquals = getMember(ComparatorClass.companionModule, nme.equals_) - def isMaybeBoxed(sym: Symbol) = sym isNonBottomSubClass BoxedNumberClass - - def newClassLoader(bin: MsilFile): loaders.SymbolLoader = new loaders.MsilFileLoader(bin) - - /** - * Tells whether a class should be loaded and entered into the package - * scope. On .NET, this method returns `false` for all synthetic classes - * (anonymous classes, implementation classes, module classes), their - * symtab is encoded in the pickle of another class. - */ - def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean = { - if (cls.binary.isDefined) { - val typ = cls.binary.get.msilType - if (typ.IsDefined(loaders.clrTypes.SCALA_SYMTAB_ATTR, false)) { - val attrs = typ.GetCustomAttributes(loaders.clrTypes.SCALA_SYMTAB_ATTR, false) - assert(attrs.length == 1, attrs.length) - val a = attrs(0).asInstanceOf[msillib.Attribute] - // symtab_constr takes a byte array argument (the pickle), i.e. typ has a pickle. - // otherwise, symtab_default_constr was used, which marks typ as scala-synthetic. - a.getConstructor() == loaders.clrTypes.SYMTAB_CONSTR - } else true // always load non-scala types - } else true // always load source - } - - def needCompile(bin: MsilFile, src: AbstractFile) = - false // always use compiled file on .net -} +// /* NSC -- new Scala compiler +// * Copyright 2005-2011 LAMP/EPFL +// * @author Paul Phillips +// */ +// +// package scala.tools.nsc +// package backend +// +// import ch.epfl.lamp.compiler.{ msil => msillib } +// import util.{ ClassPath, MsilClassPath } +// import msil.GenMSIL +// import io.{ AbstractFile, MsilFile } +// +// trait MSILPlatform extends Platform { +// import global._ +// import definitions.{ ComparatorClass, BoxedNumberClass, getMember } +// +// type BinaryRepr = MsilFile +// +// if (settings.verbose.value) +// inform("[AssemRefs = " + settings.assemrefs.value + "]") +// +// // phaseName = "msil" +// object genMSIL extends { +// val global: MSILPlatform.this.global.type = MSILPlatform.this.global +// val runsAfter = List[String]("dce") +// val runsRightAfter = None +// } with GenMSIL +// +// lazy val classPath = MsilClassPath.fromSettings(settings) +// def rootLoader = new loaders.PackageLoader(classPath.asInstanceOf[ClassPath[platform.BinaryRepr]]) +// // See discussion in JavaPlatForm for why we need a cast here. +// +// def platformPhases = List( +// genMSIL // generate .msil files +// ) +// +// lazy val externalEquals = getMember(ComparatorClass.companionModule, nme.equals_) +// def isMaybeBoxed(sym: Symbol) = sym isNonBottomSubClass BoxedNumberClass +// +// def newClassLoader(bin: MsilFile): loaders.SymbolLoader = new loaders.MsilFileLoader(bin) +// +// /** +// * Tells whether a class should be loaded and entered into the package +// * scope. On .NET, this method returns `false` for all synthetic classes +// * (anonymous classes, implementation classes, module classes), their +// * symtab is encoded in the pickle of another class. +// */ +// def doLoad(cls: ClassPath[BinaryRepr]#ClassRep): Boolean = { +// if (cls.binary.isDefined) { +// val typ = cls.binary.get.msilType +// if (typ.IsDefined(loaders.clrTypes.SCALA_SYMTAB_ATTR, false)) { +// val attrs = typ.GetCustomAttributes(loaders.clrTypes.SCALA_SYMTAB_ATTR, false) +// assert(attrs.length == 1, attrs.length) +// val a = attrs(0).asInstanceOf[msillib.Attribute] +// // symtab_constr takes a byte array argument (the pickle), i.e. typ has a pickle. +// // otherwise, symtab_default_constr was used, which marks typ as scala-synthetic. +// a.getConstructor() == loaders.clrTypes.SYMTAB_CONSTR +// } else true // always load non-scala types +// } else true // always load source +// } +// +// def needCompile(bin: MsilFile, src: AbstractFile) = +// false // always use compiled file on .net +// } diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 3baff7da9e..acb20b4627 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -160,8 +160,8 @@ abstract class GenICode extends SubComponent { case Assign(lhs @ Select(_, _), rhs) => val isStatic = lhs.symbol.isStaticMember var ctx1 = if (isStatic) ctx - else if (forMSIL && msil_IsValuetypeInstField(lhs.symbol)) - msil_genLoadQualifierAddress(lhs, ctx) + // else if (forMSIL && msil_IsValuetypeInstField(lhs.symbol)) + // msil_genLoadQualifierAddress(lhs, ctx) else genLoadQualifier(lhs, ctx) ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info)) @@ -460,132 +460,132 @@ abstract class GenICode extends SubComponent { fun.symbol.simpleName + ") " + " at: " + (tree.pos) ) } - - /** - * forMSIL - */ - private def msil_IsValuetypeInstMethod(msym: Symbol) = ( - loaders.clrTypes.methods get msym exists (mMSIL => - mMSIL.IsInstance && mMSIL.DeclaringType.IsValueType - ) - ) - private def msil_IsValuetypeInstField(fsym: Symbol) = ( - loaders.clrTypes.fields get fsym exists (fMSIL => - !fMSIL.IsStatic && fMSIL.DeclaringType.IsValueType - ) - ) - - /** - * forMSIL: Adds a local var, the emitted code requires one more slot on the stack as on entry - */ - private def msil_genLoadZeroOfNonEnumValuetype(ctx: Context, kind: TypeKind, pos: Position, leaveAddressOnStackInstead: Boolean) { - val REFERENCE(clssym) = kind - assert(loaders.clrTypes.isNonEnumValuetype(clssym), clssym) - val local = ctx.makeLocal(pos, clssym.tpe, "tmp") - ctx.method.addLocal(local) - ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(local), pos) - ctx.bb.emit(CIL_INITOBJ(kind), pos) - val instr = if (leaveAddressOnStackInstead) - CIL_LOAD_LOCAL_ADDRESS(local) - else - LOAD_LOCAL(local) - ctx.bb.emit(instr, pos) - } - - /** - * forMSIL - */ - private def msil_genLoadAddressOf(tree: Tree, ctx: Context, expectedType: TypeKind, butRawValueIsAlsoGoodEnough: Boolean): Context = { - var generatedType = expectedType - var addressTaken = false - debuglog("at line: " + (if (tree.pos.isDefined) tree.pos.line else tree.pos)) - - var resCtx: Context = tree match { - - // emits CIL_LOAD_FIELD_ADDRESS - case Select(qualifier, selector) if (!tree.symbol.isModule) => - addressTaken = true - val sym = tree.symbol - generatedType = toTypeKind(sym.info) - - if (sym.isStaticMember) { - ctx.bb.emit(CIL_LOAD_FIELD_ADDRESS(sym, true), tree.pos) - ctx - } else { - val ctx1 = genLoadQualifier(tree, ctx) - ctx1.bb.emit(CIL_LOAD_FIELD_ADDRESS(sym, false), tree.pos) - ctx1 - } - - // emits CIL_LOAD_LOCAL_ADDRESS - case Ident(name) if (!tree.symbol.isPackage && !tree.symbol.isModule)=> - addressTaken = true - val sym = tree.symbol - try { - val Some(l) = ctx.method.lookupLocal(sym) - ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(l), tree.pos) - generatedType = l.kind // actually, should be "V&" but the callsite is aware of this - } catch { - case ex: MatchError => - abort("symbol " + sym + " does not exist in " + ctx.method) - } - ctx - - // emits CIL_LOAD_ARRAY_ITEM_ADDRESS - case Apply(fun, args) => - if (isPrimitive(fun.symbol)) { - - val sym = tree.symbol - val Apply(fun @ Select(receiver, _), args) = tree - val code = scalaPrimitives.getPrimitive(sym, receiver.tpe) - - if (isArrayOp(code)) { - val arrayObj = receiver - val k = toTypeKind(arrayObj.tpe) - val ARRAY(elementType) = k - if (scalaPrimitives.isArrayGet(code)) { - var ctx1 = genLoad(arrayObj, ctx, k) - // load argument on stack - debugassert(args.length == 1, "Too many arguments for array get operation: " + tree) - ctx1 = genLoad(args.head, ctx1, INT) - generatedType = elementType // actually "managed pointer to element type" but the callsite is aware of this - ctx1.bb.emit(CIL_LOAD_ARRAY_ITEM_ADDRESS(elementType), tree.pos) - addressTaken = true - ctx1 - } else null - } else null - } else null - - case This(qual) => - /* TODO: this case handler is a placeholder for the time when Level 2 support for valuetypes is in place, - in particular when invoking other methods on this where this is a valuetype value (boxed or not). - As receiver, a managed pointer is expected, and a plain ldarg.0 achieves just that. */ - addressTaken = true - genLoad(tree, ctx, expectedType) - - case _ => - null /* A method returning ByRef won't pass peverify, so I guess this case handler is dead code. - Even if it's not, the code below to handler !addressTaken below. */ - } - - if (!addressTaken) { - resCtx = genLoad(tree, ctx, expectedType) - if (!butRawValueIsAlsoGoodEnough) { - // raw value on stack (must be an intermediate result, e.g. returned by method call), take address - addressTaken = true - val boxType = expectedType // toTypeKind(expectedType /* TODO FIXME */) - resCtx.bb.emit(BOX(boxType), tree.pos) - resCtx.bb.emit(CIL_UNBOX(boxType), tree.pos) - } - } - - // emit conversion - if (generatedType != expectedType) - abort("Unexpected tree in msil_genLoadAddressOf: " + tree + " at: " + tree.pos) - - resCtx - } - + // + // /** + // * forMSIL + // */ + // private def msil_IsValuetypeInstMethod(msym: Symbol) = ( + // loaders.clrTypes.methods get msym exists (mMSIL => + // mMSIL.IsInstance && mMSIL.DeclaringType.IsValueType + // ) + // ) + // private def msil_IsValuetypeInstField(fsym: Symbol) = ( + // loaders.clrTypes.fields get fsym exists (fMSIL => + // !fMSIL.IsStatic && fMSIL.DeclaringType.IsValueType + // ) + // ) + // + // /** + // * forMSIL: Adds a local var, the emitted code requires one more slot on the stack as on entry + // */ + // private def msil_genLoadZeroOfNonEnumValuetype(ctx: Context, kind: TypeKind, pos: Position, leaveAddressOnStackInstead: Boolean) { + // val REFERENCE(clssym) = kind + // assert(loaders.clrTypes.isNonEnumValuetype(clssym), clssym) + // val local = ctx.makeLocal(pos, clssym.tpe, "tmp") + // ctx.method.addLocal(local) + // ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(local), pos) + // ctx.bb.emit(CIL_INITOBJ(kind), pos) + // val instr = if (leaveAddressOnStackInstead) + // CIL_LOAD_LOCAL_ADDRESS(local) + // else + // LOAD_LOCAL(local) + // ctx.bb.emit(instr, pos) + // } + // + // /** + // * forMSIL + // */ + // private def msil_genLoadAddressOf(tree: Tree, ctx: Context, expectedType: TypeKind, butRawValueIsAlsoGoodEnough: Boolean): Context = { + // var generatedType = expectedType + // var addressTaken = false + // debuglog("at line: " + (if (tree.pos.isDefined) tree.pos.line else tree.pos)) + // + // var resCtx: Context = tree match { + // + // // emits CIL_LOAD_FIELD_ADDRESS + // case Select(qualifier, selector) if (!tree.symbol.isModule) => + // addressTaken = true + // val sym = tree.symbol + // generatedType = toTypeKind(sym.info) + // + // if (sym.isStaticMember) { + // ctx.bb.emit(CIL_LOAD_FIELD_ADDRESS(sym, true), tree.pos) + // ctx + // } else { + // val ctx1 = genLoadQualifier(tree, ctx) + // ctx1.bb.emit(CIL_LOAD_FIELD_ADDRESS(sym, false), tree.pos) + // ctx1 + // } + // + // // emits CIL_LOAD_LOCAL_ADDRESS + // case Ident(name) if (!tree.symbol.isPackage && !tree.symbol.isModule)=> + // addressTaken = true + // val sym = tree.symbol + // try { + // val Some(l) = ctx.method.lookupLocal(sym) + // ctx.bb.emit(CIL_LOAD_LOCAL_ADDRESS(l), tree.pos) + // generatedType = l.kind // actually, should be "V&" but the callsite is aware of this + // } catch { + // case ex: MatchError => + // abort("symbol " + sym + " does not exist in " + ctx.method) + // } + // ctx + // + // // emits CIL_LOAD_ARRAY_ITEM_ADDRESS + // case Apply(fun, args) => + // if (isPrimitive(fun.symbol)) { + // + // val sym = tree.symbol + // val Apply(fun @ Select(receiver, _), args) = tree + // val code = scalaPrimitives.getPrimitive(sym, receiver.tpe) + // + // if (isArrayOp(code)) { + // val arrayObj = receiver + // val k = toTypeKind(arrayObj.tpe) + // val ARRAY(elementType) = k + // if (scalaPrimitives.isArrayGet(code)) { + // var ctx1 = genLoad(arrayObj, ctx, k) + // // load argument on stack + // debugassert(args.length == 1, "Too many arguments for array get operation: " + tree) + // ctx1 = genLoad(args.head, ctx1, INT) + // generatedType = elementType // actually "managed pointer to element type" but the callsite is aware of this + // ctx1.bb.emit(CIL_LOAD_ARRAY_ITEM_ADDRESS(elementType), tree.pos) + // addressTaken = true + // ctx1 + // } else null + // } else null + // } else null + // + // case This(qual) => + // /* TODO: this case handler is a placeholder for the time when Level 2 support for valuetypes is in place, + // in particular when invoking other methods on this where this is a valuetype value (boxed or not). + // As receiver, a managed pointer is expected, and a plain ldarg.0 achieves just that. */ + // addressTaken = true + // genLoad(tree, ctx, expectedType) + // + // case _ => + // null /* A method returning ByRef won't pass peverify, so I guess this case handler is dead code. + // Even if it's not, the code below to handler !addressTaken below. */ + // } + // + // if (!addressTaken) { + // resCtx = genLoad(tree, ctx, expectedType) + // if (!butRawValueIsAlsoGoodEnough) { + // // raw value on stack (must be an intermediate result, e.g. returned by method call), take address + // addressTaken = true + // val boxType = expectedType // toTypeKind(expectedType /* TODO FIXME */) + // resCtx.bb.emit(BOX(boxType), tree.pos) + // resCtx.bb.emit(CIL_UNBOX(boxType), tree.pos) + // } + // } + // + // // emit conversion + // if (generatedType != expectedType) + // abort("Unexpected tree in msil_genLoadAddressOf: " + tree + " at: " + tree.pos) + // + // resCtx + // } + // /** * Generate code for trees that produce values on the stack @@ -793,19 +793,19 @@ abstract class GenICode extends SubComponent { debugassert(ctor.owner == cls, "Symbol " + ctor.owner.fullName + " is different than " + tpt) - val ctx2 = if (forMSIL && loaders.clrTypes.isNonEnumValuetype(cls)) { - /* parameterful constructors are the only possible custom constructors, - a default constructor can't be defined for valuetypes, CLR dixit */ - val isDefaultConstructor = args.isEmpty - if (isDefaultConstructor) { - msil_genLoadZeroOfNonEnumValuetype(ctx, rt, tree.pos, leaveAddressOnStackInstead = false) - ctx - } else { - val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) - ctx1.bb.emit(CIL_NEWOBJ(ctor), tree.pos) - ctx1 - } - } else { + // val ctx2 = if (forMSIL && loaders.clrTypes.isNonEnumValuetype(cls)) { + // /* parameterful constructors are the only possible custom constructors, + // a default constructor can't be defined for valuetypes, CLR dixit */ + // val isDefaultConstructor = args.isEmpty + // if (isDefaultConstructor) { + // msil_genLoadZeroOfNonEnumValuetype(ctx, rt, tree.pos, leaveAddressOnStackInstead = false) + // ctx + // } else { + // val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx) + // ctx1.bb.emit(CIL_NEWOBJ(ctor), tree.pos) + // ctx1 + // } + // } else { val nw = NEW(rt) ctx.bb.emit(nw, tree.pos) ctx.bb.emit(DUP(generatedType)) @@ -815,8 +815,8 @@ abstract class GenICode extends SubComponent { nw.init = init ctx1.bb.emit(init, tree.pos) ctx1 - } - ctx2 + // } + // ctx2 case _ => abort("Cannot instantiate " + tpt + "of kind: " + generatedType) @@ -845,12 +845,12 @@ abstract class GenICode extends SubComponent { generatedType = boxType ctx1.bb.emit(UNBOX(boxType), expr.pos) ctx1 - - case Apply(fun @ _, List(expr)) if (forMSIL && loaders.clrTypes.isAddressOf(fun.symbol)) => - debuglog("ADDRESSOF : " + fun.symbol.fullName); - val ctx1 = msil_genLoadAddressOf(expr, ctx, toTypeKind(expr.tpe), butRawValueIsAlsoGoodEnough = false) - generatedType = toTypeKind(fun.symbol.tpe.resultType) - ctx1 + // + // case Apply(fun @ _, List(expr)) if (forMSIL && loaders.clrTypes.isAddressOf(fun.symbol)) => + // debuglog("ADDRESSOF : " + fun.symbol.fullName); + // val ctx1 = msil_genLoadAddressOf(expr, ctx, toTypeKind(expr.tpe), butRawValueIsAlsoGoodEnough = false) + // generatedType = toTypeKind(fun.symbol.tpe.resultType) + // ctx1 case app @ Apply(fun, args) => val sym = fun.symbol @@ -887,9 +887,9 @@ abstract class GenICode extends SubComponent { var ctx1 = if (invokeStyle.hasInstance) { - if (forMSIL && !(invokeStyle.isInstanceOf[SuperCall]) && msil_IsValuetypeInstMethod(sym)) - msil_genLoadQualifierAddress(fun, ctx) - else + // if (forMSIL && !(invokeStyle.isInstanceOf[SuperCall]) && msil_IsValuetypeInstMethod(sym)) + // msil_genLoadQualifierAddress(fun, ctx) + // else genLoadQualifier(fun, ctx) } else ctx @@ -1150,15 +1150,15 @@ abstract class GenICode extends SubComponent { case _ => abort("Unknown qualifier " + tree) } - - /** forMSIL */ - private def msil_genLoadQualifierAddress(tree: Tree, ctx: Context): Context = - tree match { - case Select(qualifier, _) => - msil_genLoadAddressOf(qualifier, ctx, toTypeKind(qualifier.tpe), butRawValueIsAlsoGoodEnough = false) - case _ => - abort("Unknown qualifier " + tree) - } + // + // /** forMSIL */ + // private def msil_genLoadQualifierAddress(tree: Tree, ctx: Context): Context = + // tree match { + // case Select(qualifier, _) => + // msil_genLoadAddressOf(qualifier, ctx, toTypeKind(qualifier.tpe), butRawValueIsAlsoGoodEnough = false) + // case _ => + // abort("Unknown qualifier " + tree) + // } /** * Generate code that loads args into label parameters. diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala index 2bcfb9d4a9..bf8580b903 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala @@ -636,63 +636,63 @@ trait Opcodes { self: ICodes => // CLR backend - - case class CIL_LOAD_LOCAL_ADDRESS(local: Local) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = "CIL_LOAD_LOCAL_ADDRESS "+local //+isArgument?" (argument)":""; - - override def consumed = 0 - override def produced = 1 - - override def producedTypes = List(msil_mgdptr(local.kind)) - } - - case class CIL_LOAD_FIELD_ADDRESS(field: Symbol, isStatic: Boolean) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = - "CIL_LOAD_FIELD_ADDRESS " + (if (isStatic) field.fullName else field.toString) - - override def consumed = if (isStatic) 0 else 1 - override def produced = 1 - - override def consumedTypes = if (isStatic) Nil else List(REFERENCE(field.owner)); - override def producedTypes = List(msil_mgdptr(REFERENCE(field.owner))); -} - - case class CIL_LOAD_ARRAY_ITEM_ADDRESS(kind: TypeKind) extends Instruction { - /** Returns a string representation of this instruction */ - override def toString(): String = "CIL_LOAD_ARRAY_ITEM_ADDRESS (" + kind + ")" - - override def consumed = 2 - override def produced = 1 - - override def consumedTypes = List(ARRAY(kind), INT) - override def producedTypes = List(msil_mgdptr(kind)) - } - - case class CIL_UNBOX(valueType: TypeKind) extends Instruction { - override def toString(): String = "CIL_UNBOX " + valueType - override def consumed = 1 - override def consumedTypes = ObjectReference :: Nil // actually consumes a 'boxed valueType' - override def produced = 1 - override def producedTypes = List(msil_mgdptr(valueType)) - } - - case class CIL_INITOBJ(valueType: TypeKind) extends Instruction { - override def toString(): String = "CIL_INITOBJ " + valueType - override def consumed = 1 - override def consumedTypes = ObjectReference :: Nil // actually consumes a managed pointer - override def produced = 0 - } - - case class CIL_NEWOBJ(method: Symbol) extends Instruction { - override def toString(): String = "CIL_NEWOBJ " + hostClass.fullName + method.fullName - var hostClass: Symbol = method.owner; - override def consumed = method.tpe.paramTypes.length - override def consumedTypes = method.tpe.paramTypes map toTypeKind - override def produced = 1 - override def producedTypes = List(toTypeKind(method.tpe.resultType)) - } +// +// case class CIL_LOAD_LOCAL_ADDRESS(local: Local) extends Instruction { +// /** Returns a string representation of this instruction */ +// override def toString(): String = "CIL_LOAD_LOCAL_ADDRESS "+local //+isArgument?" (argument)":""; +// +// override def consumed = 0 +// override def produced = 1 +// +// override def producedTypes = List(msil_mgdptr(local.kind)) +// } +// +// case class CIL_LOAD_FIELD_ADDRESS(field: Symbol, isStatic: Boolean) extends Instruction { +// /** Returns a string representation of this instruction */ +// override def toString(): String = +// "CIL_LOAD_FIELD_ADDRESS " + (if (isStatic) field.fullName else field.toString) +// +// override def consumed = if (isStatic) 0 else 1 +// override def produced = 1 +// +// override def consumedTypes = if (isStatic) Nil else List(REFERENCE(field.owner)); +// override def producedTypes = List(msil_mgdptr(REFERENCE(field.owner))); +// } +// +// case class CIL_LOAD_ARRAY_ITEM_ADDRESS(kind: TypeKind) extends Instruction { +// /** Returns a string representation of this instruction */ +// override def toString(): String = "CIL_LOAD_ARRAY_ITEM_ADDRESS (" + kind + ")" +// +// override def consumed = 2 +// override def produced = 1 +// +// override def consumedTypes = List(ARRAY(kind), INT) +// override def producedTypes = List(msil_mgdptr(kind)) +// } +// +// case class CIL_UNBOX(valueType: TypeKind) extends Instruction { +// override def toString(): String = "CIL_UNBOX " + valueType +// override def consumed = 1 +// override def consumedTypes = ObjectReference :: Nil // actually consumes a 'boxed valueType' +// override def produced = 1 +// override def producedTypes = List(msil_mgdptr(valueType)) +// } +// +// case class CIL_INITOBJ(valueType: TypeKind) extends Instruction { +// override def toString(): String = "CIL_INITOBJ " + valueType +// override def consumed = 1 +// override def consumedTypes = ObjectReference :: Nil // actually consumes a managed pointer +// override def produced = 0 +// } +// +// case class CIL_NEWOBJ(method: Symbol) extends Instruction { +// override def toString(): String = "CIL_NEWOBJ " + hostClass.fullName + method.fullName +// var hostClass: Symbol = method.owner; +// override def consumed = method.tpe.paramTypes.length +// override def consumedTypes = method.tpe.paramTypes map toTypeKind +// override def produced = 1 +// override def producedTypes = List(toTypeKind(method.tpe.resultType)) +// } } } diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala index a485272ca6..998ac90778 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala @@ -423,11 +423,10 @@ trait TypeKinds { self: ICodes => primitiveTypeMap.getOrElse(sym, newReference(sym)) private def primitiveOrClassType(sym: Symbol, targs: List[Type]) = primitiveTypeMap.getOrElse(sym, arrayOrClassType(sym, targs)) - - def msil_mgdptr(tk: TypeKind): TypeKind = (tk: @unchecked) match { - case REFERENCE(cls) => REFERENCE(loaders.clrTypes.mdgptrcls4clssym(cls)) - // TODO have ready class-symbols for the by-ref versions of built-in valuetypes - case _ => abort("cannot obtain a managed pointer for " + tk) - } - + // + // def msil_mgdptr(tk: TypeKind): TypeKind = (tk: @unchecked) match { + // case REFERENCE(cls) => REFERENCE(loaders.clrTypes.mdgptrcls4clssym(cls)) + // // TODO have ready class-symbols for the by-ref versions of built-in valuetypes + // case _ => abort("cannot obtain a managed pointer for " + tk) + // } } diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index d2e54ff3f1..ab07f2e4fc 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -1,2357 +1,2357 @@ -/* NSC -- new scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Nikolay Mihaylov - */ - - -package scala.tools.nsc -package backend.msil - -import java.io.{File, IOException} -import java.nio.{ByteBuffer, ByteOrder} -import scala.collection.{ mutable, immutable } -import scala.tools.nsc.symtab._ - -import ch.epfl.lamp.compiler.msil.{Type => MsilType, _} -import ch.epfl.lamp.compiler.msil.emit._ -import ch.epfl.lamp.compiler.msil.util.PECustomMod - -abstract class GenMSIL extends SubComponent { - import global._ - import loaders.clrTypes - import clrTypes.{types, constructors, methods, fields} - import icodes._ - import icodes.opcodes._ - - val x = loaders - - /** 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() { - 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 - if( opt.showClass.isDefined && (codeGenerator.entryPoint == null) ) { // TODO introduce dedicated setting instead - val entryclass = opt.showClass.get.toString - warning("Couldn't find entry class " + entryclass) - } - - codeGenerator.initAssembly - - val classesSorted = classes.values.toList.sortBy(c => c.symbol.id) // simplifies comparing cross-compiler vs. .exe output - classesSorted foreach codeGenerator.createTypeBuilder - classesSorted foreach codeGenerator.createClassMembers - - try { - classesSorted foreach codeGenerator.genClass - } finally { - codeGenerator.writeAssembly - } - } - - override def apply(unit: CompilationUnit) { - abort("MSIL works on icode classes, not on compilation units!") - } - } - - /** - * MSIL bytecode generator. - * - */ - class BytecodeGenerator { - - val MODULE_INSTANCE_NAME = "MODULE$" - - import clrTypes.{VOID => MVOID, BOOLEAN => MBOOL, BYTE => MBYTE, SHORT => MSHORT, - CHAR => MCHAR, INT => MINT, LONG => MLONG, FLOAT => MFLOAT, - DOUBLE => MDOUBLE, OBJECT => MOBJECT, STRING => MSTRING, - STRING_ARRAY => MSTRING_ARRAY, - SYMTAB_CONSTR => SYMTAB_ATTRIBUTE_CONSTRUCTOR, - SYMTAB_DEFAULT_CONSTR => SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR} - - val EXCEPTION = clrTypes.getType("System.Exception") - val MBYTE_ARRAY = clrTypes.mkArrayType(MBYTE) - - val ICLONEABLE = clrTypes.getType("System.ICloneable") - val MEMBERWISE_CLONE = MOBJECT.GetMethod("MemberwiseClone", MsilType.EmptyTypes) - - val MMONITOR = clrTypes.getType("System.Threading.Monitor") - val MMONITOR_ENTER = MMONITOR.GetMethod("Enter", Array(MOBJECT)) - val MMONITOR_EXIT = MMONITOR.GetMethod("Exit", Array(MOBJECT)) - - val MSTRING_BUILDER = clrTypes.getType("System.Text.StringBuilder") - val MSTRING_BUILDER_CONSTR = MSTRING_BUILDER.GetConstructor(MsilType.EmptyTypes) - val MSTRING_BUILDER_TOSTRING = MSTRING_BUILDER.GetMethod("ToString", - MsilType.EmptyTypes) - - val TYPE_FROM_HANDLE = - clrTypes.getType("System.Type").GetMethod("GetTypeFromHandle", Array(clrTypes.getType("System.RuntimeTypeHandle"))) - - val INT_PTR = clrTypes.getType("System.IntPtr") - - val JOBJECT = definitions.ObjectClass - val JSTRING = definitions.StringClass - - val SystemConvert = clrTypes.getType("System.Convert") - - val objParam = Array(MOBJECT) - - val toBool: MethodInfo = SystemConvert.GetMethod("ToBoolean", objParam) // see comment in emitUnbox - val toSByte: MethodInfo = SystemConvert.GetMethod("ToSByte", objParam) - val toShort: MethodInfo = SystemConvert.GetMethod("ToInt16", objParam) - val toChar: MethodInfo = SystemConvert.GetMethod("ToChar", objParam) - val toInt: MethodInfo = SystemConvert.GetMethod("ToInt32", objParam) - val toLong: MethodInfo = SystemConvert.GetMethod("ToInt64", objParam) - val toFloat: MethodInfo = SystemConvert.GetMethod("ToSingle", objParam) - val toDouble: MethodInfo = SystemConvert.GetMethod("ToDouble", objParam) - - //val boxedUnit: FieldInfo = 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.CloneableAttr.tpe - val TransientAtt = definitions.TransientAttr.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 = mutable.HashSet[Symbol]() - - initMappings() - - /** Create the mappings between java and .net classes and methods */ - private def initMappings() { - mapType(definitions.AnyClass, MOBJECT) - mapType(definitions.AnyRefClass, MOBJECT) - //mapType(definitions.NullClass, clrTypes.getType("scala.AllRef$")) - //mapType(definitions.NothingClass, clrTypes.getType("scala.All$")) - // FIXME: for some reason the upper two lines map to null - mapType(definitions.NullClass, EXCEPTION) - mapType(definitions.NothingClass, EXCEPTION) - - mapType(definitions.BooleanClass, MBOOL) - mapType(definitions.ByteClass, MBYTE) - mapType(definitions.ShortClass, MSHORT) - mapType(definitions.CharClass, MCHAR) - mapType(definitions.IntClass, MINT) - mapType(definitions.LongClass, MLONG) - mapType(definitions.FloatClass, MFLOAT) - mapType(definitions.DoubleClass, MDOUBLE) - } - - var clasz: IClass = _ - var method: IMethod = _ - - var massembly: AssemblyBuilder = _ - var mmodule: ModuleBuilder = _ - var mcode: ILGenerator = _ - - var assemName: String = _ - var firstSourceName = "" - var outDir: File = _ - var srcPath: File = _ - var moduleName: String = _ - - def initAssembly() { - - assemName = settings.assemname.value - - if (assemName == "") { - if (entryPoint != null) { - assemName = msilName(entryPoint.enclClass) - // 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"), firstSourceName) - assemName = firstSourceName.substring(0, firstSourceName.length() - 6) - } - } else { - if (assemName.endsWith(".msil")) - assemName = assemName.substring(0, assemName.length()-5) - if (assemName.endsWith(".il")) - assemName = assemName.substring(0, assemName.length()-3) - val f: File = new File(assemName) - assemName = f.getName() - } - - outDir = new File(settings.outdir.value) - - srcPath = new File(settings.sourcedir.value) - - val assemblyName = new AssemblyName() - assemblyName.Name = assemName - massembly = AssemblyBuilderFactory.DefineDynamicAssembly(assemblyName) - - moduleName = assemName // + (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) - } - - - /** - * 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, 4 bytes, least significant first) - * 2.2: the byte array data - * 3: NumNamed (unsigned int16, number of named fields and properties, 0x0000) - */ - def addSymtabAttribute(sym: Symbol, tBuilder: TypeBuilder) { - def addMarker() { - val markerSymtab = new Array[Byte](4) - markerSymtab(0) = 1.toByte - tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR, markerSymtab) - } - - // both conditions are needed (why exactly..?) - if (tBuilder.Name.endsWith("$") || sym.isModuleClass) { - addMarker() - } else { - currentRun.symData.get(sym) match { - case Some(pickle) => - var size = pickle.writeIndex - val symtab = new Array[Byte](size + 8) - symtab(0) = 1.toByte - for (i <- 2 until 6) { - symtab(i) = (size & 0xff).toByte - size = size >> 8 - } - java.lang.System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex) - - tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab) - - currentRun.symData -= sym - currentRun.symData -= sym.companionSymbol - - case _ => - addMarker() - } - } - } - - /** - * Mutates `member` adding CLR attributes (if any) based on sym.annotations. - * Please notice that CLR custom modifiers are a different beast (see customModifiers below) - * and thus shouldn't be added by this method. - */ - def addAttributes(member: ICustomAttributeSetter, annotations: List[AnnotationInfo]) { - val attributes = annotations.map(_.atp.typeSymbol).collect { - case definitions.TransientAttr => null // TODO this is just an example - } - return // TODO: implement at some point - } - - /** - * What's a CLR custom modifier? Intro available as source comments in compiler.msil.CustomModifier. - * It's basically a marker associated with a location (think of FieldInfo, ParameterInfo, and PropertyInfo) - * and thus that marker (be it optional or required) becomes part of the signature of that location. - * Some annotations will become CLR attributes (see addAttributes above), others custom modifiers (this method). - */ - def customModifiers(annotations: List[AnnotationInfo]): Array[CustomModifier] = { - annotations.map(_.atp.typeSymbol).collect { - case definitions.VolatileAttr => new CustomModifier(true, CustomModifier.VolatileMarker) - } toArray - } - - - - /* - debuglog("creating annotations: " + annotations + " for member : " + member) - for (annot@ AnnotationInfo(typ, annArgs, nvPairs) <- annotations ; - if annot.isConstant) - //!typ.typeSymbol.isJavaDefined - { -// assert(consts.length <= 1, -// "too many constant arguments for annotations; "+consts.toString()) - - // Problem / TODO having the symbol of the annotations type would be nicer - // (i hope that type.typeSymbol is the same as the one in types2create) - // AND: this will crash if the annotations Type is already compiled (-> not a typeBuilder) - // when this is solved, types2create will be the same as icodes.classes, thus superfluous - val annType: TypeBuilder = getType(typ.typeSymbol).asInstanceOf[TypeBuilder] -// val annType: MsilType = getType(typ.typeSymbol) - - // Problem / TODO: i have no idea which constructor is used. This - // information should be available in AnnotationInfo. - annType.CreateType() // else, GetConstructors can't be used - val constr: ConstructorInfo = annType.GetConstructors()(0) - // prevent a second call of CreateType, only needed because there's no - // other way than GetConstructors()(0) to get the constructor, if there's - // no constructor symbol available. - - val args: Array[Byte] = - getAttributeArgs( - annArgs map (_.constant.get), - (for((n,v) <- nvPairs) yield (n, v.constant.get))) - member.SetCustomAttribute(constr, args) - } - } */ - -/* def getAttributeArgs(consts: List[Constant], nvPairs: List[(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: (Name, Constant)) { - // 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().slice(0, length) - } */ - - def writeAssembly() { - if (entryPoint != null) { - assert(entryPoint.enclClass.isModuleClass, entryPoint.enclClass) - val mainMethod = methods(entryPoint) - val stringArrayTypes: Array[MsilType] = Array(MSTRING_ARRAY) - val globalMain = mmodule.DefineGlobalMethod( - "Main", MethodAttributes.Public | MethodAttributes.Static, - MVOID, stringArrayTypes) - globalMain.DefineParameter(0, ParameterAttributes.None, "args") - massembly.SetEntryPoint(globalMain) - val code = globalMain.GetILGenerator() - val moduleField = getModuleInstanceField(entryPoint.enclClass) - code.Emit(OpCodes.Ldsfld, moduleField) - code.Emit(OpCodes.Ldarg_0) - code.Emit(OpCodes.Callvirt, mainMethod) - code.Emit(OpCodes.Ret) - } - createTypes() - var outDirName: String = null - try { - if (settings.Ygenjavap.isDefault) { // we reuse the JVM-sounding setting because it's conceptually similar - outDirName = outDir.getPath() - massembly.Save(outDirName + "\\" + assemName + ".msil") /* use SingleFileILPrinterVisitor */ - } else { - outDirName = srcPath.getPath() - massembly.Save(settings.Ygenjavap.value, outDirName) /* use MultipleFilesILPrinterVisitor */ - } - } catch { - case e:IOException => abort("Could not write to " + outDirName + ": " + e.getMessage()) - } - } - - private def createTypes() { - for (sym <- classes.keys) { - val iclass = classes(sym) - val tBuilder = types(sym).asInstanceOf[TypeBuilder] - - debuglog("Calling CreatType for " + sym + ", " + tBuilder.toString) - - tBuilder.CreateType() - tBuilder.setSourceFilepath(iclass.cunit.source.file.path) - } - } - - private[GenMSIL] def ilasmFileName(iclass: IClass) : String = { - // method.sourceFile contains just the filename - iclass.cunit.source.file.toString.replace("\\", "\\\\") - } - - private[GenMSIL] def genClass(iclass: IClass) { - val sym = iclass.symbol - debuglog("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) - })) { - debuglog("auto-generating cloneable method for " + sym) - val attrs: Short = (MethodAttributes.Public | MethodAttributes.Virtual | - MethodAttributes.HideBySig).toShort - val cloneMethod = tBuilder.DefineMethod("Clone", attrs, MOBJECT, - MsilType.EmptyTypes) - val clCode = cloneMethod.GetILGenerator() - clCode.Emit(OpCodes.Ldarg_0) - clCode.Emit(OpCodes.Call, MEMBERWISE_CLONE) - clCode.Emit(OpCodes.Ret) - } - } - - val line = sym.pos.line - tBuilder.setPosition(line, ilasmFileName(iclass)) - - if (isTopLevelModule(sym)) { - if (sym.companionClass == NoSymbol) - generateMirrorClass(sym) - else - log("No mirror class for module with linked class: " + - sym.fullName) - } - - addSymtabAttribute(sym, tBuilder) - addAttributes(tBuilder, sym.annotations) - - if (iclass.symbol != definitions.ArrayClass) - iclass.methods foreach genMethod - - } //genClass - - - private def genMethod(m: IMethod) { - debuglog("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) + - " owner: " + m.symbol.owner) - method = m - localBuilders.clear - computeLocalVarsIndex(m) - - if (m.symbol.isClassConstructor) { - mcode = constructors(m.symbol).asInstanceOf[ConstructorBuilder].GetILGenerator() - } else { - val mBuilder = methods(m.symbol).asInstanceOf[MethodBuilder] - if (!mBuilder.IsAbstract()) - try { - mcode = mBuilder.GetILGenerator() - } catch { - case e: Exception => - java.lang.System.out.println("m.symbol = " + Flags.flagsToString(m.symbol.flags) + " " + m.symbol) - java.lang.System.out.println("m.symbol.owner = " + Flags.flagsToString(m.symbol.owner.flags) + " " + m.symbol.owner) - java.lang.System.out.println("mBuilder = " + mBuilder) - java.lang.System.out.println("mBuilder.DeclaringType = " + - TypeAttributes.toString(mBuilder.DeclaringType.Attributes) + - "::" + mBuilder.DeclaringType) - throw e - } - else - mcode = null - } - - if (mcode != null) { - for (local <- m.locals ; if !(m.params contains local)) { - debuglog("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) - } - - } - - /** Special linearizer for methods with at least one exception handler. This - * linearizer brings all basic blocks in the right order so that nested - * try-catch and try-finally blocks can be emitted. - */ - val msilLinearizer = new MSILLinearizer() - - val labels = mutable.HashMap[BasicBlock, Label]() - - /* when emitting .line, it's enough to include the full filename just once per method, thus reducing filesize. - * this scheme relies on the fact that the entry block is emitted first. */ - var dbFilenameSeen = false - - def genCode(m: IMethod) { - - def makeLabels(blocks: List[BasicBlock]) = { - debuglog("Making labels for: " + method) - for (bb <- blocks) labels(bb) = mcode.DefineLabel() - } - - labels.clear - - var linearization = if(!m.exh.isEmpty) msilLinearizer.linearize(m) - else linearizer.linearize(m) - - if (!m.exh.isEmpty) - linearization = computeExceptionMaps(linearization, m) - - makeLabels(linearization) - - // debug val blocksInM = m.code.blocks.toList.sortBy(bb => bb.label) - // debug val blocksInL = linearization.sortBy(bb => bb.label) - // debug val MButNotL = (blocksInM.toSet) diff (blocksInL.toSet) // if non-empty, a jump to B fails to find a label for B (case CJUMP, case CZJUMP) - // debug if(!MButNotL.isEmpty) { } - - dbFilenameSeen = false - genBlocks(linearization) - - // RETURN inside exception blocks are replaced by Leave. The target of the - // leave is a `Ret` outside any exception block (generated here). - if (handlerReturnMethod == m) { - mcode.MarkLabel(handlerReturnLabel) - if (handlerReturnKind != UNIT) - mcode.Emit(OpCodes.Ldloc, handlerReturnLocal) - mcode.Emit(OpCodes.Ret) - } - - beginExBlock.clear() - beginCatchBlock.clear() - endExBlock.clear() - endFinallyLabels.clear() - } - - def genBlocks(blocks: List[BasicBlock], previous: BasicBlock = null) { - blocks match { - case Nil => () - case x :: Nil => genBlock(x, prev = previous, next = null) - case x :: y :: ys => genBlock(x, prev = previous, next = y); genBlocks(y :: ys, previous = x) - } - } - - // the try blocks starting at a certain BasicBlock - val beginExBlock = mutable.HashMap[BasicBlock, List[ExceptionHandler]]() - - // the catch blocks starting / endling at a certain BasicBlock - val beginCatchBlock = mutable.HashMap[BasicBlock, ExceptionHandler]() - val endExBlock = mutable.HashMap[BasicBlock, List[ExceptionHandler]]() - - /** When emitting the code (genBlock), the number of currently active try / catch - * blocks. When seeing a `RETURN` inside a try / catch, we need to - * - store the result in a local (if it's not UNIT) - * - emit `Leave handlerReturnLabel` instead of the Return - * - emit code at the end: load the local and return its value - */ - var currentHandlers = new mutable.Stack[ExceptionHandler] - // The IMethod the Local/Label/Kind below belong to - var handlerReturnMethod: IMethod = _ - // Stores the result when returning inside an exception block - var handlerReturnLocal: LocalBuilder = _ - // Label for a return instruction outside any exception block - var handlerReturnLabel: Label = _ - // The result kind. - var handlerReturnKind: TypeKind = _ - def returnFromHandler(kind: TypeKind): (LocalBuilder, Label) = { - if (handlerReturnMethod != method) { - handlerReturnMethod = method - if (kind != UNIT) { - handlerReturnLocal = mcode.DeclareLocal(msilType(kind)) - handlerReturnLocal.SetLocalSymInfo("$handlerReturn") - } - handlerReturnLabel = mcode.DefineLabel() - handlerReturnKind = kind - } - (handlerReturnLocal, handlerReturnLabel) - } - - /** For try/catch nested inside a finally, we can't use `Leave OutsideFinally`, the - * Leave target has to be inside the finally (and it has to be the `endfinally` instruction). - * So for every finalizer, we have a label which marks the place of the `endfinally`, - * nested try/catch blocks will leave there. - */ - val endFinallyLabels = mutable.HashMap[ExceptionHandler, Label]() - - /** Computes which blocks are the beginning / end of a try or catch block */ - private def computeExceptionMaps(blocks: List[BasicBlock], m: IMethod): List[BasicBlock] = { - val visitedBlocks = new mutable.HashSet[BasicBlock]() - - // handlers which have not been introduced so far - var openHandlers = m.exh - - - /** Example - * try { - * try { - * // *1* - * } catch { - * case h1 => - * } - * } catch { - * case h2 => - * case h3 => - * try { - * - * } catch { - * case h4 => // *2* - * case h5 => - * } - * } - */ - - // Stack of nested try blocks. Each bloc has a List of ExceptionHandler (multiple - // catch statements). Example *1*: Stack(List(h2, h3), List(h1)) - val currentTryHandlers = new mutable.Stack[List[ExceptionHandler]]() - - // Stack of nested catch blocks. The head of the list is the current catch block. The - // tail is all following catch blocks. Example *2*: Stack(List(h3), List(h4, h5)) - val currentCatchHandlers = new mutable.Stack[List[ExceptionHandler]]() - - for (b <- blocks) { - - // are we past the current catch blocks? - def endHandlers(): List[ExceptionHandler] = { - var res: List[ExceptionHandler] = Nil - if (!currentCatchHandlers.isEmpty) { - val handler = currentCatchHandlers.top.head - if (!handler.blocks.contains(b)) { - // all blocks of the handler are either visited, or not part of the linearization (i.e. dead) - assert(handler.blocks.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)), - "Bad linearization of basic blocks inside catch. Found block not part of the handler\n"+ - b.fullString +"\nwhile in catch-part of\n"+ handler) - - val rest = currentCatchHandlers.pop.tail - if (rest.isEmpty) { - // all catch blocks of that exception handler are covered - res = handler :: endHandlers() - } else { - // there are more catch blocks for that try (handlers covering the same) - currentCatchHandlers.push(rest) - beginCatchBlock(b) = rest.head - } - } - } - res - } - val end = endHandlers() - if (!end.isEmpty) endExBlock(b) = end - - // are we past the current try block? - if (!currentTryHandlers.isEmpty) { - val handler = currentTryHandlers.top.head - if (!handler.covers(b)) { - // all of the covered blocks are visited, or not part of the linearization - assert(handler.covered.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)), - "Bad linearization of basic blocks inside try. Found non-covered block\n"+ - b.fullString +"\nwhile in try-part of\n"+ handler) - - assert(handler.startBlock == b, - "Bad linearization of basic blocks. The entry block of a catch does not directly follow the try\n"+ - b.fullString +"\n"+ handler) - - val handlers = currentTryHandlers.pop - currentCatchHandlers.push(handlers) - beginCatchBlock(b) = handler - } - } - - // are there try blocks starting at b? - val (newHandlers, stillOpen) = openHandlers.partition(_.covers(b)) - openHandlers = stillOpen - - val newHandlersBySize = newHandlers.groupBy(_.covered.size) - // big handlers first, smaller ones are nested inside the try of the big one - // (checked by the assertions below) - val sizes = newHandlersBySize.keys.toList.sortWith(_ > _) - - val beginHandlers = new mutable.ListBuffer[ExceptionHandler] - for (s <- sizes) { - val sHandlers = newHandlersBySize(s) - for (h <- sHandlers) { - assert(h.covered == sHandlers.head.covered, - "bad nesting of exception handlers. same size, but not covering same blocks\n"+ - h +"\n"+ sHandlers.head) - assert(h.resultKind == sHandlers.head.resultKind, - "bad nesting of exception handlers. same size, but the same resultKind\n"+ - h +"\n"+ sHandlers.head) - } - for (bigger <- beginHandlers; h <- sHandlers) { - assert(h.covered.subsetOf(bigger.covered), - "bad nesting of exception handlers. try blocks of smaller handler are not nested in bigger one.\n"+ - h +"\n"+ bigger) - assert(h.blocks.toSet.subsetOf(bigger.covered), - "bad nesting of exception handlers. catch blocks of smaller handler are not nested in bigger one.\n"+ - h +"\n"+ bigger) - } - beginHandlers += sHandlers.head - currentTryHandlers.push(sHandlers) - } - beginExBlock(b) = beginHandlers.toList - visitedBlocks += b - } - - // if there handlers left (i.e. handlers covering nothing, or a - // non-existent (dead) block), remove their catch-blocks. - val liveBlocks = if (openHandlers.isEmpty) blocks else { - blocks.filter(b => openHandlers.forall(h => !h.blocks.contains(b))) - } - - /** There might be open handlers, but no more blocks. happens when try/catch end - * with `throw` or `return` - * def foo() { try { .. throw } catch { _ => .. throw } } - * - * In this case we need some code after the catch block for the auto-generated - * `leave` instruction. So we're adding a (dead) `throw new Exception`. - */ - val rest = currentCatchHandlers.map(handlers => { - assert(handlers.length == 1, handlers) - handlers.head - }).toList - - if (rest.isEmpty) { - liveBlocks - } else { - val b = m.code.newBlock - b.emit(Seq( - NEW(REFERENCE(definitions.ThrowableClass)), - DUP(REFERENCE(definitions.ObjectClass)), - CALL_METHOD(definitions.ThrowableClass.primaryConstructor, Static(true)), - THROW(definitions.ThrowableClass) - )) - b.close - endExBlock(b) = rest - liveBlocks ::: List(b) - } - } - - /** - * @param block the BasicBlock to emit code for - * @param next the following BasicBlock, `null` if `block` is the last one - */ - def genBlock(block: BasicBlock, prev: BasicBlock, next: BasicBlock) { - - def loadLocalOrAddress(local: Local, msg : String , loadAddr : Boolean) { - debuglog(msg + " for " + local) - val isArg = local.arg - val i = local.index - if (isArg) - loadArg(mcode, loadAddr)(i) - else - loadLocal(i, local, mcode, loadAddr) - } - - def loadFieldOrAddress(field: Symbol, isStatic: Boolean, msg: String, loadAddr : Boolean) { - debuglog(msg + " with owner: " + field.owner + - " flags: " + Flags.flagsToString(field.owner.flags)) - var fieldInfo = fields.get(field) match { - case Some(fInfo) => fInfo - case None => - val fInfo = getType(field.owner).GetField(msilName(field)) - fields(field) = fInfo - fInfo - } - if (fieldInfo.IsVolatile) { - mcode.Emit(OpCodes.Volatile) - } - if (!fieldInfo.IsLiteral) { - if (loadAddr) { - mcode.Emit(if (isStatic) OpCodes.Ldsflda else OpCodes.Ldflda, fieldInfo) - } else { - mcode.Emit(if (isStatic) OpCodes.Ldsfld else OpCodes.Ldfld, fieldInfo) - } - } else { - assert(!loadAddr, "can't take AddressOf a literal field (not even with readonly. prefix) because no memory was allocated to such field ...") - // TODO the above can be overcome by loading the value, boxing, and finally unboxing. An address to a copy of the raw value will be on the stack. - /* We perform `field inlining' as required by CLR. - * Emit as for a CONSTANT ICode stmt, with the twist that the constant value is available - * as a java.lang.Object and its .NET type allows constant initialization in CLR, i.e. that type - * is one of I1, I2, I4, I8, R4, R8, CHAR, BOOLEAN, STRING, or CLASS (in this last case, - * only accepting nullref as value). See Table 9-1 in Lidin's book on ILAsm. */ - val value = fieldInfo.getValue() - if (value == null) { - mcode.Emit(OpCodes.Ldnull) - } else { - val typ = if (fieldInfo.FieldType.IsEnum) fieldInfo.FieldType.getUnderlyingType - else fieldInfo.FieldType - if (typ == clrTypes.STRING) { - mcode.Emit(OpCodes.Ldstr, value.asInstanceOf[String]) - } else if (typ == clrTypes.BOOLEAN) { - mcode.Emit(if (value.asInstanceOf[Boolean]) OpCodes.Ldc_I4_1 - else OpCodes.Ldc_I4_0) - } else if (typ == clrTypes.BYTE || typ == clrTypes.UBYTE) { - loadI4(value.asInstanceOf[Byte], mcode) - } else if (typ == clrTypes.SHORT || typ == clrTypes.USHORT) { - loadI4(value.asInstanceOf[Int], mcode) - } else if (typ == clrTypes.CHAR) { - loadI4(value.asInstanceOf[Char], mcode) - } else if (typ == clrTypes.INT || typ == clrTypes.UINT) { - loadI4(value.asInstanceOf[Int], mcode) - } else if (typ == clrTypes.LONG || typ == clrTypes.ULONG) { - mcode.Emit(OpCodes.Ldc_I8, value.asInstanceOf[Long]) - } else if (typ == clrTypes.FLOAT) { - mcode.Emit(OpCodes.Ldc_R4, value.asInstanceOf[Float]) - } else if (typ == clrTypes.DOUBLE) { - mcode.Emit(OpCodes.Ldc_R8, value.asInstanceOf[Double]) - } else { - /* TODO one more case is described in Partition II, 16.2: bytearray(...) */ - abort("Unknown type for static literal field: " + fieldInfo) - } - } - } - } - - /** Creating objects works differently on .NET. On the JVM - * - NEW(type) => reference on Stack - * - DUP, load arguments, CALL_METHOD(constructor) - * - * On .NET, the NEW and DUP are ignored, but we emit a special method call - * - load arguments - * - NewObj(constructor) => reference on stack - * - * This variable tells whether the previous instruction was a NEW, - * we expect a DUP which is not emitted. */ - var previousWasNEW = false - - var lastLineNr: Int = 0 - var lastPos: Position = NoPosition - - - // EndExceptionBlock must happen before MarkLabel because it adds the - // Leave instruction. Otherwise, labels(block) points to the Leave - // (inside the catch) instead of the instruction afterwards. - for (handlers <- endExBlock.get(block); exh <- handlers) { - currentHandlers.pop() - for (l <- endFinallyLabels.get(exh)) - mcode.MarkLabel(l) - mcode.EndExceptionBlock() - } - - mcode.MarkLabel(labels(block)) - debuglog("Generating code for block: " + block) - - for (handler <- beginCatchBlock.get(block)) { - if (!currentHandlers.isEmpty && currentHandlers.top.covered == handler.covered) { - currentHandlers.pop() - currentHandlers.push(handler) - } - if (handler.cls == NoSymbol) { - // `finally` blocks are represented the same as `catch`, but with no catch-type - mcode.BeginFinallyBlock() - } else { - val t = getType(handler.cls) - mcode.BeginCatchBlock(t) - } - } - for (handlers <- beginExBlock.get(block); exh <- handlers) { - currentHandlers.push(exh) - mcode.BeginExceptionBlock() - } - - for (instr <- block) { - try { - val currentLineNr = instr.pos.line - val skip = if(instr.pos.isRange) instr.pos.sameRange(lastPos) else (currentLineNr == lastLineNr); - if(!skip || !dbFilenameSeen) { - val fileName = if(dbFilenameSeen) "" else {dbFilenameSeen = true; ilasmFileName(clasz)}; - if(instr.pos.isRange) { - val startLine = instr.pos.focusStart.line - val endLine = instr.pos.focusEnd.line - val startCol = instr.pos.focusStart.column - val endCol = instr.pos.focusEnd.column - mcode.setPosition(startLine, endLine, startCol, endCol, fileName) - } else { - mcode.setPosition(instr.pos.line, fileName) - } - lastLineNr = currentLineNr - lastPos = instr.pos - } - } catch { case _: UnsupportedOperationException => () } - - if (previousWasNEW) - assert(instr.isInstanceOf[DUP], block) - - instr match { - case THIS(clasz) => - mcode.Emit(OpCodes.Ldarg_0) - - case CONSTANT(const) => - const.tag match { - case UnitTag => () - case BooleanTag => mcode.Emit(if (const.booleanValue) OpCodes.Ldc_I4_1 - else OpCodes.Ldc_I4_0) - case ByteTag => loadI4(const.byteValue, mcode) - case ShortTag => loadI4(const.shortValue, mcode) - case CharTag => loadI4(const.charValue, mcode) - case IntTag => loadI4(const.intValue, mcode) - case LongTag => mcode.Emit(OpCodes.Ldc_I8, const.longValue) - case FloatTag => mcode.Emit(OpCodes.Ldc_R4, const.floatValue) - case DoubleTag => mcode.Emit(OpCodes.Ldc_R8, const.doubleValue) - case StringTag => mcode.Emit(OpCodes.Ldstr, const.stringValue) - case NullTag => mcode.Emit(OpCodes.Ldnull) - case ClassTag => - mcode.Emit(OpCodes.Ldtoken, msilType(const.typeValue)) - mcode.Emit(OpCodes.Call, TYPE_FROM_HANDLE) - case _ => abort("Unknown constant value: " + const) - } - - case LOAD_ARRAY_ITEM(kind) => - (kind: @unchecked) match { - case BOOL => mcode.Emit(OpCodes.Ldelem_I1) - case BYTE => mcode.Emit(OpCodes.Ldelem_I1) // I1 for System.SByte, i.e. a scala.Byte - case SHORT => mcode.Emit(OpCodes.Ldelem_I2) - case CHAR => mcode.Emit(OpCodes.Ldelem_U2) - case INT => mcode.Emit(OpCodes.Ldelem_I4) - case LONG => mcode.Emit(OpCodes.Ldelem_I8) - case FLOAT => mcode.Emit(OpCodes.Ldelem_R4) - case DOUBLE => mcode.Emit(OpCodes.Ldelem_R8) - case REFERENCE(cls) => mcode.Emit(OpCodes.Ldelem_Ref) - case ARRAY(elem) => mcode.Emit(OpCodes.Ldelem_Ref) - - // case UNIT is not possible: an Array[Unit] will be an - // Array[scala.runtime.BoxedUnit] (-> case REFERENCE) - } - - case LOAD_LOCAL(local) => loadLocalOrAddress(local, "load_local", false) - - case CIL_LOAD_LOCAL_ADDRESS(local) => loadLocalOrAddress(local, "cil_load_local_address", true) - - case LOAD_FIELD(field, isStatic) => loadFieldOrAddress(field, isStatic, "load_field", false) - - case CIL_LOAD_FIELD_ADDRESS(field, isStatic) => loadFieldOrAddress(field, isStatic, "cil_load_field_address", true) - - case CIL_LOAD_ARRAY_ITEM_ADDRESS(kind) => mcode.Emit(OpCodes.Ldelema, msilType(kind)) - - case CIL_NEWOBJ(msym) => - assert(msym.isClassConstructor) - val constructorInfo: ConstructorInfo = getConstructor(msym) - mcode.Emit(OpCodes.Newobj, constructorInfo) - - case LOAD_MODULE(module) => - debuglog("Generating LOAD_MODULE for: " + showsym(module)) - mcode.Emit(OpCodes.Ldsfld, getModuleInstanceField(module)) - - case STORE_ARRAY_ITEM(kind) => - (kind: @unchecked) match { - case BOOL => mcode.Emit(OpCodes.Stelem_I1) - case BYTE => mcode.Emit(OpCodes.Stelem_I1) - case SHORT => mcode.Emit(OpCodes.Stelem_I2) - case CHAR => mcode.Emit(OpCodes.Stelem_I2) - case INT => mcode.Emit(OpCodes.Stelem_I4) - case LONG => mcode.Emit(OpCodes.Stelem_I8) - case FLOAT => mcode.Emit(OpCodes.Stelem_R4) - case DOUBLE => mcode.Emit(OpCodes.Stelem_R8) - case REFERENCE(cls) => mcode.Emit(OpCodes.Stelem_Ref) - case ARRAY(elem) => mcode.Emit(OpCodes.Stelem_Ref) // @TODO: test this! (occurs when calling a Array[Object]* vararg param method) - - // case UNIT not possible (see comment at LOAD_ARRAY_ITEM) - } - - case STORE_LOCAL(local) => - val isArg = local.arg - val i = local.index - debuglog("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_THIS(_) => - // this only works for impl classes because the self parameter comes first - // in the method signature. If that changes, this code has to be revisited. - mcode.Emit(OpCodes.Starg_S, 0) - - case STORE_FIELD(field, isStatic) => - val fieldInfo = fields.get(field) match { - case Some(fInfo) => fInfo - case None => - val fInfo = getType(field.owner).GetField(msilName(field)) - fields(field) = fInfo - fInfo - } - mcode.Emit(if (isStatic) OpCodes.Stsfld else OpCodes.Stfld, fieldInfo) - - case CALL_PRIMITIVE(primitive) => - genPrimitive(primitive, instr.pos) - - case CALL_METHOD(msym, style) => - if (msym.isClassConstructor) { - val constructorInfo: ConstructorInfo = getConstructor(msym) - (style: @unchecked) match { - // normal constructor calls are Static.. - case Static(_) => - if (method.symbol.isClassConstructor && method.symbol.owner == msym.owner) - // we're generating a constructor (method: IMethod is a constructor), and we're - // calling another constructor of the same class. - - // @LUC TODO: this can probably break, namely when having: class A { def this() { new A() } } - // instead, we should instruct the CALL_METHOD with additional information, know whether it's - // an instance creation constructor call or not. - mcode.Emit(OpCodes.Call, constructorInfo) - else - mcode.Emit(OpCodes.Newobj, constructorInfo) - case SuperCall(_) => - mcode.Emit(OpCodes.Call, constructorInfo) - if (isStaticModule(clasz.symbol) && - notInitializedModules.contains(clasz.symbol) && - method.symbol.isClassConstructor) - { - notInitializedModules -= clasz.symbol - mcode.Emit(OpCodes.Ldarg_0) - mcode.Emit(OpCodes.Stsfld, getModuleInstanceField(clasz.symbol)) - } - } - - } else { - - var doEmit = true - getTypeOpt(msym.owner) match { - case Some(typ) if (typ.IsEnum) => { - def negBool() = { - mcode.Emit(OpCodes.Ldc_I4_0) - mcode.Emit(OpCodes.Ceq) - } - doEmit = false - val name = msym.name - if (name eq nme.EQ) { mcode.Emit(OpCodes.Ceq) } - else if (name eq nme.NE) { mcode.Emit(OpCodes.Ceq); negBool } - else if (name eq nme.LT) { mcode.Emit(OpCodes.Clt) } - else if (name eq nme.LE) { mcode.Emit(OpCodes.Cgt); negBool } - else if (name eq nme.GT) { mcode.Emit(OpCodes.Cgt) } - else if (name eq nme.GE) { mcode.Emit(OpCodes.Clt); negBool } - else if (name eq nme.OR) { mcode.Emit(OpCodes.Or) } - else if (name eq nme.AND) { mcode.Emit(OpCodes.And) } - else if (name eq nme.XOR) { mcode.Emit(OpCodes.Xor) } - else - doEmit = true - } - case _ => () - } - - // method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType - val (isDelegateView, paramType, resType) = atPhase(currentRun.typerPhase) { - msym.tpe match { - case MethodType(params, resultType) - if (params.length == 1 && msym.name == nme.view_) => - val paramType = params(0).tpe - val isDel = definitions.isCorrespondingDelegate(resultType, paramType) - (isDel, paramType, resultType) - case _ => (false, null, null) - } - } - if (doEmit && isDelegateView) { - doEmit = false - createDelegateCaller(paramType, resType) - } - - if (doEmit && - (msym.name == nme.PLUS || msym.name == nme.MINUS) - && clrTypes.isDelegateType(msilType(msym.owner.tpe))) - { - doEmit = false - val methodInfo: MethodInfo = getMethod(msym) - // 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 methodInfo: MethodInfo = getMethod(methodSym) - val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR)) - if (methodSym.isStatic) { - mcode.Emit(OpCodes.Ldftn, methodInfo) - } else { - mcode.Emit(OpCodes.Dup) - mcode.Emit(OpCodes.Ldvirtftn, methodInfo) - } - mcode.Emit(OpCodes.Newobj, delegCtor) - } - - if (doEmit) { - val methodInfo: MethodInfo = getMethod(msym) - (style: @unchecked) match { - case SuperCall(_) => - mcode.Emit(OpCodes.Call, methodInfo) - case Dynamic => - // methodInfo.DeclaringType is null for global methods - val isValuetypeMethod = (methodInfo.DeclaringType ne null) && (methodInfo.DeclaringType.IsValueType) - val isValuetypeVirtualMethod = isValuetypeMethod && (methodInfo.IsVirtual) - if (dynToStatMapped(msym)) { - mcode.Emit(OpCodes.Call, methodInfo) - } else if (isValuetypeVirtualMethod) { - mcode.Emit(OpCodes.Constrained, methodInfo.DeclaringType) - mcode.Emit(OpCodes.Callvirt, methodInfo) - } else if (isValuetypeMethod) { - // otherwise error "Callvirt on a value type method" ensues - mcode.Emit(OpCodes.Call, methodInfo) - } else { - mcode.Emit(OpCodes.Callvirt, methodInfo) - } - case Static(_) => - if(methodInfo.IsVirtual && !mcode.Ldarg0WasJustEmitted) { - mcode.Emit(OpCodes.Callvirt, methodInfo) - } else mcode.Emit(OpCodes.Call, methodInfo) - } - } - } - - case BOX(boxType) => - emitBox(mcode, boxType) - - case UNBOX(boxType) => - emitUnbox(mcode, boxType) - - case CIL_UNBOX(boxType) => - mcode.Emit(OpCodes.Unbox, msilType(boxType)) - - case CIL_INITOBJ(valueType) => - mcode.Emit(OpCodes.Initobj, msilType(valueType)) - - case NEW(REFERENCE(cls)) => - // the next instruction must be a DUP, see comment on `var previousWasNEW` - previousWasNEW = true - - // works also for arrays and reference-types - case CREATE_ARRAY(elem, dims) => - // TODO: handle multi dimensional arrays - assert(dims == 1, "Can't handle multi dimensional arrays") - 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(tpknd) => - val tMSIL = msilType(tpknd) - mcode.Emit(OpCodes.Castclass, tMSIL) - - // 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 is 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 = 0 - for (l <- tags) { - var targetLabel = labels(branches(i)) - for (i <- l) { - mcode.Emit(OpCodes.Ldloc, switchLocal) - loadI4(i, mcode) - mcode.Emit(OpCodes.Beq, targetLabel) - } - i += 1 - } - val defaultTarget = labels(branches(i)) - if (next != branches(i)) - mcode.Emit(OpCodes.Br, defaultTarget) - - case JUMP(whereto) => - val (leaveHandler, leaveFinally, lfTarget) = leavesHandler(block, whereto) - if (leaveHandler) { - if (leaveFinally) { - if (lfTarget.isDefined) mcode.Emit(OpCodes.Leave, lfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(whereto)) - } else if (next != whereto) - mcode.Emit(OpCodes.Br, labels(whereto)) - - case CJUMP(success, failure, cond, kind) => - // 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 - val emit = (c: TestOp, l: Label) => emitBr(c, l, isFloat) - emitCondBr(block, cond, success, failure, next, emit) - - case CZJUMP(success, failure, cond, kind) => - emitCondBr(block, cond, success, failure, next, emitBrBool(_, _)) - - case RETURN(kind) => - if (currentHandlers.isEmpty) - mcode.Emit(OpCodes.Ret) - else { - val (local, label) = returnFromHandler(kind) - if (kind != UNIT) - mcode.Emit(OpCodes.Stloc, local) - mcode.Emit(OpCodes.Leave, label) - } - - case THROW(_) => - mcode.Emit(OpCodes.Throw) - - case DROP(kind) => - mcode.Emit(OpCodes.Pop) - - case DUP(kind) => - // see comment on `var previousWasNEW` - if (!previousWasNEW) - mcode.Emit(OpCodes.Dup) - else - previousWasNEW = false - - case MONITOR_ENTER() => - mcode.Emit(OpCodes.Call, MMONITOR_ENTER) - - case MONITOR_EXIT() => - mcode.Emit(OpCodes.Call, MMONITOR_EXIT) - - case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION(_) => - () - } - - } // end for (instr <- b) { .. } - } // end genBlock - - def genPrimitive(primitive: Primitive, pos: Position) { - 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) => - debuglog("Converting from: " + src + " to: " + dst) - - dst match { - case BYTE => mcode.Emit(OpCodes.Conv_I1) // I1 for System.SByte, i.e. a scala.Byte - case SHORT => mcode.Emit(OpCodes.Conv_I2) - case CHAR => mcode.Emit(OpCodes.Conv_U2) - case INT => mcode.Emit(OpCodes.Conv_I4) - case LONG => mcode.Emit(OpCodes.Conv_I8) - case FLOAT => mcode.Emit(OpCodes.Conv_R4) - case DOUBLE => mcode.Emit(OpCodes.Conv_R8) - case _ => - Console.println("Illegal conversion at: " + clasz + - " at: " + pos.source + ":" + pos.line) - } - - case ArrayLength(_) => - mcode.Emit(OpCodes.Ldlen) - - case StartConcat => - mcode.Emit(OpCodes.Newobj, MSTRING_BUILDER_CONSTR) - - - case StringConcat(el) => - val elemType : MsilType = el match { - case REFERENCE(_) | ARRAY(_) => MOBJECT - case _ => msilType(el) - } - - val argTypes:Array[MsilType] = Array(elemType) - val stringBuilderAppend = MSTRING_BUILDER.GetMethod("Append", argTypes ) - mcode.Emit(OpCodes.Callvirt, stringBuilderAppend) - - case EndConcat => - mcode.Emit(OpCodes.Callvirt, MSTRING_BUILDER_TOSTRING) - - case _ => - abort("Unimplemented primitive " + primitive) - } - } // end genPrimitive - - - ////////////////////// 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, loadAddr: Boolean)(i: Int) = - if (loadAddr) { - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldarga_S, i) - else - code.Emit(OpCodes.Ldarga, i) - } else { - i match { - case 0 => code.Emit(OpCodes.Ldarg_0) - case 1 => code.Emit(OpCodes.Ldarg_1) - case 2 => code.Emit(OpCodes.Ldarg_2) - case 3 => code.Emit(OpCodes.Ldarg_3) - case _ => - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldarg_S, i) - else - code.Emit(OpCodes.Ldarg, i) - } - } - - def loadLocal(i: Int, local: Local, code: ILGenerator, loadAddr: Boolean) = - if (loadAddr) { - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldloca_S, localBuilders(local)) - else - code.Emit(OpCodes.Ldloca, localBuilders(local)) - } else { - i match { - case 0 => code.Emit(OpCodes.Ldloc_0) - case 1 => code.Emit(OpCodes.Ldloc_1) - case 2 => code.Emit(OpCodes.Ldloc_2) - case 3 => code.Emit(OpCodes.Ldloc_3) - case _ => - if (i >= -128 && i <= 127) - code.Emit(OpCodes.Ldloc_S, localBuilders(local)) - else - code.Emit(OpCodes.Ldloc, localBuilders(local)) - } - } - - ////////////////////// branches /////////////////////// - - /** Returns a Triple (Boolean, Boolean, Option[Label]) - * - whether the jump leaves some exception block (try / catch / finally) - * - whether it leaves a finally handler (finally block, but not it's try / catch) - * - a label where to jump for leaving the finally handler - * . None to leave directly using `endfinally` - * . Some(label) to emit `leave label` (for try / catch inside a finally handler) - */ - def leavesHandler(from: BasicBlock, to: BasicBlock): (Boolean, Boolean, Option[Label]) = - if (currentHandlers.isEmpty) (false, false, None) - else { - val h = currentHandlers.head - val leaveHead = { h.covers(from) != h.covers(to) || - h.blocks.contains(from) != h.blocks.contains(to) } - if (leaveHead) { - // we leave the innermost exception block. - // find out if we also leave som e `finally` handler - currentHandlers.find(e => { - e.cls == NoSymbol && e.blocks.contains(from) != e.blocks.contains(to) - }) match { - case Some(finallyHandler) => - if (h == finallyHandler) { - // the finally handler is the innermost, so we can emit `endfinally` directly - (true, true, None) - } else { - // we need to `Leave` to the `endfinally` of the next outer finally handler - val l = endFinallyLabels.getOrElseUpdate(finallyHandler, mcode.DefineLabel()) - (true, true, Some(l)) - } - case None => - (true, false, None) - } - } else (false, false, None) - } - - def emitCondBr(block: BasicBlock, cond: TestOp, success: BasicBlock, failure: BasicBlock, - next: BasicBlock, emitBrFun: (TestOp, Label) => Unit) { - val (sLeaveHandler, sLeaveFinally, slfTarget) = leavesHandler(block, success) - val (fLeaveHandler, fLeaveFinally, flfTarget) = leavesHandler(block, failure) - - if (sLeaveHandler || fLeaveHandler) { - val sLabelOpt = if (sLeaveHandler) { - val leaveSLabel = mcode.DefineLabel() - emitBrFun(cond, leaveSLabel) - Some(leaveSLabel) - } else { - emitBrFun(cond, labels(success)) - None - } - - if (fLeaveHandler) { - if (fLeaveFinally) { - if (flfTarget.isDefined) mcode.Emit(OpCodes.Leave, flfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(failure)) - } else - mcode.Emit(OpCodes.Br, labels(failure)) - - sLabelOpt.map(l => { - mcode.MarkLabel(l) - if (sLeaveFinally) { - if (slfTarget.isDefined) mcode.Emit(OpCodes.Leave, slfTarget.get) - else mcode.Emit(OpCodes.Endfinally) - } else - mcode.Emit(OpCodes.Leave, labels(success)) - }) - } else { - if (next == success) { - emitBrFun(cond.negate, labels(failure)) - } else { - emitBrFun(cond, labels(success)) - if (next != failure) { - mcode.Emit(OpCodes.Br, labels(failure)) - } - } - } - } - - def emitBr(condition: TestOp, dest: Label, isFloat: Boolean) { - condition match { - case EQ => mcode.Emit(OpCodes.Beq, dest) - case NE => mcode.Emit(OpCodes.Bne_Un, dest) - case LT => mcode.Emit(if (isFloat) OpCodes.Blt_Un else OpCodes.Blt, dest) - case GE => mcode.Emit(if (isFloat) OpCodes.Bge_Un else OpCodes.Bge, dest) - case LE => mcode.Emit(if (isFloat) OpCodes.Ble_Un else OpCodes.Ble, dest) - case GT => mcode.Emit(if (isFloat) OpCodes.Bgt_Un else OpCodes.Bgt, dest) - } - } - - def emitBrBool(cond: TestOp, dest: Label) { - cond match { - // EQ -> Brfalse, NE -> Brtrue; this is because we come from - // a CZJUMP. If the value on the stack is 0 (e.g. a boolean - // 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) - } - } - - ////////////////////// local vars /////////////////////// - - /** - * Compute the indexes of each local variable of the given - * method. - */ - def computeLocalVarsIndex(m: IMethod) { - var idx = if (m.symbol.isStaticMember) 0 else 1 - - val params = m.params - for (l <- params) { - debuglog("Index value for parameter " + l + ": " + idx) - l.index = idx - idx += 1 // sizeOf(l.kind) - } - - val locvars = m.locals filterNot (params contains) - idx = 0 - - for (l <- locvars) { - debuglog("Index value for local variable " + l + ": " + idx) - l.index = 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 = sym.moduleSuffix - // Flags.JAVA: "symbol was not defined by a scala-class" (java, or .net-class) - - if (sym == definitions.NothingClass) - return "scala.runtime.Nothing$" - else if (sym == definitions.NullClass) - return "scala.runtime.Null$" - - (if (sym.isClass || (sym.isModule && !sym.isMethod)) { - if (sym.isNestedClass) sym.simpleName - else sym.fullName - } else - sym.simpleName.toString.trim()) + suffix - } - - - ////////////////////// flags /////////////////////// - - def msilTypeFlags(sym: Symbol): Int = { - var mf: Int = TypeAttributes.AutoLayout | TypeAttributes.AnsiClass - - if(sym.isNestedClass) { - mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NestedPrivate else TypeAttributes.NestedPublic) - } else { - mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NotPublic else TypeAttributes.Public) - } - mf = mf | (if (sym hasFlag Flags.ABSTRACT) TypeAttributes.Abstract else 0) - mf = mf | (if (sym.isTrait && !sym.isImplClass) TypeAttributes.Interface else TypeAttributes.Class) - mf = mf | (if (sym isFinal) TypeAttributes.Sealed else 0) - - sym.annotations foreach { a => a match { - case AnnotationInfo(SerializableAttr, _, _) => - // TODO: add the Serializable TypeAttribute also if the annotation - // System.SerializableAttribute is present (.net annotation, 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 MethodAttributes.Public) - - if (!sym.isClassConstructor) { - if (sym.isStaticMember) - mf = mf | FieldAttributes.Static // coincidentally, same value as for MethodAttributes.Static ... - else { - mf = mf | MethodAttributes.Virtual - if (sym.isFinal && !getType(sym.owner).IsInterface) - mf = mf | MethodAttributes.Final - if (sym.isDeferred || getType(sym.owner).IsInterface) - mf = mf | MethodAttributes.Abstract - } - } - - if (sym.isStaticMember) { - mf = mf | MethodAttributes.Static - } - - // constructors of module classes should be private - if (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) { - mf |= MethodAttributes.Private - mf &= ~(MethodAttributes.Public) - } - - mf.toShort - } - - def msilFieldFlags(sym: Symbol): Short = { - var mf: Int = - if (sym hasFlag Flags.PRIVATE) FieldAttributes.Private - else if (sym hasFlag Flags.PROTECTED) FieldAttributes.FamORAssem - else FieldAttributes.Public - - if (sym hasFlag Flags.FINAL) - mf = mf | FieldAttributes.InitOnly - - if (sym.isStaticMember) - mf = mf | FieldAttributes.Static - - // TRANSIENT: "not serialized", VOLATILE: doesn't exist on .net - // TODO: add this annotation also if the class has the custom attribute - // System.NotSerializedAttribute - sym.annotations.foreach( a => a match { - case AnnotationInfo(TransientAtt, _, _) => - mf = mf | FieldAttributes.NotSerialized - case _ => () - }) - - mf.toShort - } - - ////////////////////// builders, types /////////////////////// - - var entryPoint: Symbol = _ - - val notInitializedModules = mutable.HashSet[Symbol]() - - // 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 = mutable.HashMap[Local, LocalBuilder]() - - private[GenMSIL] def findEntryPoint(cls: IClass) { - - def isEntryPoint(sym: Symbol):Boolean = { - if (isStaticModule(sym.owner) && msilName(sym) == "main") - if (sym.tpe.paramTypes.length == 1) { - toTypeKind(sym.tpe.paramTypes(0)) match { - case ARRAY(elem) => - if (elem.toType.typeSymbol == definitions.StringClass) { - return true - } - case _ => () - } - } - false - } - - if((entryPoint == null) && opt.showClass.isDefined) { // TODO introduce dedicated setting instead - val entryclass = opt.showClass.get.toString - val cfn = cls.symbol.fullName - if(cfn == entryclass) { - for (m <- cls.methods; if isEntryPoint(m.symbol)) { entryPoint = m.symbol } - if(entryPoint == null) { warning("Couldn't find main method in class " + cfn) } - } - } - - if (firstSourceName == "") - if (cls.symbol.sourceFile != null) // is null for nested classes - firstSourceName = cls.symbol.sourceFile.name - } - - // ##################################################################### - // get and create types - - private def msilType(t: TypeKind): MsilType = (t: @unchecked) match { - case UNIT => MVOID - case BOOL => MBOOL - case BYTE => MBYTE - case SHORT => MSHORT - case CHAR => MCHAR - case INT => MINT - case LONG => MLONG - case FLOAT => MFLOAT - case DOUBLE => MDOUBLE - case REFERENCE(cls) => getType(cls) - case ARRAY(elem) => - msilType(elem) match { - // For type builders, cannot call "clrTypes.mkArrayType" because this looks up - // the type "tp" in the assembly (not in the HashMap "types" of the backend). - // This can fail for nested types because the builders are not complete yet. - case tb: TypeBuilder => tb.MakeArrayType() - case tp: MsilType => clrTypes.mkArrayType(tp) - } - } - - private def msilType(tpe: Type): MsilType = msilType(toTypeKind(tpe)) - - private def msilParamTypes(sym: Symbol): Array[MsilType] = { - sym.tpe.paramTypes.map(msilType).toArray - } - - def getType(sym: Symbol) = getTypeOpt(sym).getOrElse(abort(showsym(sym))) - - /** - * Get an MSIL type from a symbol. First look in the clrTypes.types map, then - * lookup the name using clrTypes.getType - */ - def getTypeOpt(sym: Symbol): Option[MsilType] = { - val tmp = types.get(sym) - tmp match { - case typ @ Some(_) => typ - case None => - def typeString(sym: Symbol): String = { - val s = if (sym.isNestedClass) typeString(sym.owner) +"+"+ sym.simpleName - else sym.fullName - if (sym.isModuleClass && !sym.isTrait) s + "$" else s - } - val name = typeString(sym) - val typ = clrTypes.getType(name) - if (typ == null) - None - else { - types(sym) = typ - Some(typ) - } - } - } - - def mapType(sym: Symbol, mType: MsilType) { - assert(mType != null, showsym(sym)) - types(sym) = mType - } - - def createTypeBuilder(iclass: IClass) { - /** - * First look in the clrTypes.types map, if that fails check if it's a class being compiled, otherwise - * lookup by name (clrTypes.getType calls the static method msil.Type.GetType(fullname)). - */ - def msilTypeFromSym(sym: Symbol): MsilType = { - types.get(sym).getOrElse { - classes.get(sym) match { - case Some(iclass) => - msilTypeBuilderFromSym(sym) - case None => - getType(sym) - } - } - } - - def msilTypeBuilderFromSym(sym: Symbol): TypeBuilder = { - if(!(types.contains(sym) && types(sym).isInstanceOf[TypeBuilder])){ - val iclass = classes(sym) - assert(iclass != null) - createTypeBuilder(iclass) - } - types(sym).asInstanceOf[TypeBuilder] - } - - val sym = iclass.symbol - if (types.contains(sym) && types(sym).isInstanceOf[TypeBuilder]) - return - - def isInterface(s: Symbol) = s.isTrait && !s.isImplClass - val parents: List[Type] = - if (sym.info.parents.isEmpty) List(definitions.ObjectClass.tpe) - else sym.info.parents.distinct - - val superType : MsilType = if (isInterface(sym)) null else msilTypeFromSym(parents.head.typeSymbol) - debuglog("super type: " + parents(0).typeSymbol + ", msil type: " + superType) - - val interfaces: Array[MsilType] = - parents.tail.map(p => msilTypeFromSym(p.typeSymbol)).toArray - if (parents.length > 1) { - if (settings.debug.value) { - log("interfaces:") - for (i <- 0.until(interfaces.length)) { - log(" type: " + parents(i + 1).typeSymbol + ", msil type: " + interfaces(i)) - } - } - } - - val tBuilder = if (sym.isNestedClass) { - val ownerT = msilTypeBuilderFromSym(sym.owner).asInstanceOf[TypeBuilder] - ownerT.DefineNestedType(msilName(sym), msilTypeFlags(sym), superType, interfaces) - } else { - mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces) - } - mapType(sym, tBuilder) - } // createTypeBuilder - - def createClassMembers(iclass: IClass) { - try { - createClassMembers0(iclass) - } - catch { - case e: Throwable => - java.lang.System.err.println(showsym(iclass.symbol)) - java.lang.System.err.println("with methods = " + iclass.methods) - throw e - } - } - - def createClassMembers0(iclass: IClass) { - - val mtype = getType(iclass.symbol).asInstanceOf[TypeBuilder] - - for (ifield <- iclass.fields) { - val sym = ifield.symbol - debuglog("Adding field: " + sym.fullName) - - var attributes = msilFieldFlags(sym) - val fieldTypeWithCustomMods = - new PECustomMod(msilType(sym.tpe), - customModifiers(sym.annotations)) - val fBuilder = mtype.DefineField(msilName(sym), - fieldTypeWithCustomMods, - attributes) - fields(sym) = fBuilder - addAttributes(fBuilder, sym.annotations) - } // all iclass.fields iterated over - - if (isStaticModule(iclass.symbol)) { - val sc = iclass.lookupStaticCtor - if (sc.isDefined) { - val m = sc.get - val oldLastBlock = m.lastBlock - val lastBlock = m.newBlock() - oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) - // call object's private ctor from static ctor - lastBlock.emit(CIL_NEWOBJ(iclass.symbol.primaryConstructor)) - lastBlock.emit(DROP(toTypeKind(iclass.symbol.tpe))) - lastBlock emit RETURN(UNIT) - lastBlock.close - } - } - - if (iclass.symbol != definitions.ArrayClass) { - for (m: IMethod <- iclass.methods) { - val sym = m.symbol - debuglog("Creating MethodBuilder for " + Flags.flagsToString(sym.flags) + " " + - sym.owner.fullName + "::" + sym.name) - - val ownerType = getType(sym.enclClass).asInstanceOf[TypeBuilder] - assert(mtype == ownerType, "mtype = " + mtype + "; ownerType = " + ownerType) - var paramTypes = msilParamTypes(sym) - val attr = msilMethodFlags(sym) - - if (m.symbol.isClassConstructor) { - val constr = - ownerType.DefineConstructor(attr, CallingConventions.Standard, paramTypes) - for (i <- 0.until(paramTypes.length)) { - constr.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) - } - mapConstructor(sym, constr) - addAttributes(constr, sym.annotations) - } else { - var resType = msilType(m.returnType) - val method = - ownerType.DefineMethod(msilName(sym), attr, resType, paramTypes) - for (i <- 0.until(paramTypes.length)) { - method.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) - } - if (!methods.contains(sym)) - mapMethod(sym, method) - addAttributes(method, sym.annotations) - debuglog("\t created MethodBuilder " + method) - } - } - } // method builders created for non-array iclass - - if (isStaticModule(iclass.symbol)) { - addModuleInstanceField(iclass.symbol) - notInitializedModules += iclass.symbol - if (iclass.lookupStaticCtor.isEmpty) { - addStaticInit(iclass.symbol) - } - } - - } // createClassMembers0 - - private def isTopLevelModule(sym: Symbol): Boolean = - atPhase (currentRun.refchecksPhase) { - sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass - } - - // if the module is lifted it does not need to be initialized in - // its static constructor, and the MODULE$ field is not required. - // the outer class will care about it. - private def isStaticModule(sym: Symbol): Boolean = { - // .net inner classes: removed '!sym.hasFlag(Flags.LIFTED)', added - // 'sym.isStatic'. -> no longer compatible without skipping flatten! - sym.isModuleClass && sym.isStatic && !sym.isImplClass - } - - private def isCloneable(sym: Symbol): Boolean = { - !sym.annotations.forall( a => a match { - case AnnotationInfo(CloneableAttr, _, _) => false - case _ => true - }) - } - - private def addModuleInstanceField(sym: Symbol) { - debuglog("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 nameInMetadata = nestingAwareFullClassname(moduleClassSym) - val mClass = clrTypes.getType(nameInMetadata) - val mfield = mClass.GetField("MODULE$") - assert(mfield ne null, "module not found " + showsym(moduleClassSym)) - fields(moduleClassSym) = mfield - mfield - } - - //fields(moduleClassSym) - } - - def nestingAwareFullClassname(csym: Symbol) : String = { - val suffix = csym.moduleSuffix - val res = if (csym.isNestedClass) - nestingAwareFullClassname(csym.owner) + "+" + csym.encodedName - else - csym.fullName - res + suffix - } - - /** Adds a static initializer which creates an instance of the module - * class (calls the primary constructor). A special primary constructor - * will be generated (notInitializedModules) which stores the new instance - * in the MODULE$ field right after the super call. - */ - private def addStaticInit(sym: Symbol) { - val tBuilder = getType(sym).asInstanceOf[TypeBuilder] - - val staticInit = tBuilder.DefineConstructor( - (MethodAttributes.Static | MethodAttributes.Public).toShort, - CallingConventions.Standard, - MsilType.EmptyTypes) - - val sicode = staticInit.GetILGenerator() - - val instanceConstructor = constructors(sym.primaryConstructor) - - // there are no constructor parameters. assuming the constructor takes no parameter - // is fine: we call (in the static constructor) the constructor of the module class, - // which takes no arguments - an object definition cannot take constructor arguments. - 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 generateMirrorClass(sym: Symbol) { - val tBuilder = getType(sym) - assert(sym.isModuleClass, "Can't generate Mirror-Class for the Non-Module class " + sym) - debuglog("Dumping mirror class for object: " + sym) - val moduleName = msilName(sym) - val mirrorName = moduleName.substring(0, moduleName.length() - 1) - val mirrorTypeBuilder = mmodule.DefineType(mirrorName, - TypeAttributes.Class | - TypeAttributes.Public | - TypeAttributes.Sealed, - MOBJECT, - MsilType.EmptyTypes) - - val iclass = classes(sym) - - for (m <- sym.tpe.nonPrivateMembers - if m.owner != definitions.ObjectClass && !m.isProtected && - m.isMethod && !m.isClassConstructor && !m.isStaticMember && !m.isCase && - !m.isDeferred) - { - debuglog(" Mirroring method: " + m) - val paramTypes = msilParamTypes(m) - val paramNames: Array[String] = new Array[String](paramTypes.length) - for (i <- 0 until paramTypes.length) - paramNames(i) = "x_" + i - - // CHECK: verify if getMethodName is better than msilName - val mirrorMethod = mirrorTypeBuilder.DefineMethod(msilName(m), - (MethodAttributes.Public | - MethodAttributes.Static).toShort, - msilType(m.tpe.resultType), - paramTypes) - - var i = 0 - while (i < paramTypes.length) { - mirrorMethod.DefineParameter(i, ParameterAttributes.None, paramNames(i)) - i += 1 - } - - val mirrorCode = mirrorMethod.GetILGenerator() - mirrorCode.Emit(OpCodes.Ldsfld, getModuleInstanceField(sym)) - val mInfo = getMethod(m) - for (paramidx <- 0.until(paramTypes.length)) { - val mInfoParams = mInfo.GetParameters - val loadAddr = mInfoParams(paramidx).ParameterType.IsByRef - loadArg(mirrorCode, loadAddr)(paramidx) - } - - mirrorCode.Emit(OpCodes.Callvirt, getMethod(m)) - mirrorCode.Emit(OpCodes.Ret) - } - - addSymtabAttribute(sym.sourceModule, mirrorTypeBuilder) - - mirrorTypeBuilder.CreateType() - mirrorTypeBuilder.setSourceFilepath(iclass.cunit.source.file.path) - } - - - // ##################################################################### - // 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).toShort) - mcode.Emit(OpCodes.Stsfld, anonfunField) - - - // create the static caller method and the delegate object - val (params, returnType) = delegateType.member(nme.apply).tpe match { - case MethodType(delParams, delReturn) => (delParams, delReturn) - case _ => abort("not a delegate type: " + delegateType) - } - val caller: MethodBuilder = delegateCallers.DefineMethod( - "$delegateCaller$$" + nbDelegateCallers, - (MethodAttributes.Final | MethodAttributes.Public | MethodAttributes.Static).toShort, - msilType(returnType), (params map (_.tpe)).map(msilType).toArray) - for (i <- 0 until params.length) - caller.DefineParameter(i, ParameterAttributes.None, "arg" + i) // FIXME: use name of parameter symbol - 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 (i <- 0 until params.length) { - loadArg(dcode, false /* TODO confirm whether passing actual as-is to formal is correct wrt the ByRef attribute of the param */)(i) - emitBox(dcode, toTypeKind(params(i).tpe)) - } - 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: @unchecked) 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 clrTypes.isValueType(cls) => - code.Emit(OpCodes.Box, (msilType(boxType))) - case REFERENCE(_) | ARRAY(_) => - warning("Tried to BOX a non-valuetype.") - () - } - - def emitUnbox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match { - case UNIT => code.Emit(OpCodes.Pop) - /* (1) it's essential to keep the code emitted here (as of now plain calls to System.Convert.ToBlaBla methods) - behaviorally.equiv.wrt. BoxesRunTime.unboxToBlaBla methods - (case null: that's easy, case boxed: track changes to unboxBlaBla) - (2) See also: asInstanceOf to cast from Any to number, - tracked in http://lampsvn.epfl.ch/trac/scala/ticket/4437 */ - case BOOL => code.Emit(OpCodes.Call, toBool) - case BYTE => code.Emit(OpCodes.Call, toSByte) - case SHORT => code.Emit(OpCodes.Call, toShort) - case CHAR => code.Emit(OpCodes.Call, toChar) - case INT => code.Emit(OpCodes.Call, toInt) - case LONG => code.Emit(OpCodes.Call, toLong) - case FLOAT => code.Emit(OpCodes.Call, toFloat) - case DOUBLE => code.Emit(OpCodes.Call, toDouble) - case REFERENCE(cls) if clrTypes.isValueType(cls) => - code.Emit(OpCodes.Unbox, msilType(boxType)) - code.Emit(OpCodes.Ldobj, msilType(boxType)) - case REFERENCE(_) | ARRAY(_) => - warning("Tried to UNBOX a non-valuetype.") - () - } - - // ##################################################################### - // 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) { - java.lang.System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name) - java.lang.System.out.println("scope = " + sym.owner.tpe.decls) - abort(sym.fullName) - } - else { - mapConstructor(sym, constr) - constr - } - } - - def mapConstructor(sym: Symbol, cInfo: ConstructorInfo) = { - constructors(sym) = cInfo - } - - private def getMethod(sym: Symbol): MethodInfo = { - - methods.get(sym) match { - case Some(method) => method - case None => - val mClass = getType(sym.owner) - try { - val method = mClass.GetMethod(msilName(sym), msilParamTypes(sym), - msilType(sym.tpe.resultType)) - if (method eq null) { - java.lang.System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym)) - java.lang.System.out.println("scope = " + sym.owner.tpe.decls) - abort(sym.fullName) - } - else { - mapMethod(sym, method) - method - } - } - catch { - case e: Exception => - Console.println("While looking up " + mClass + "::" + sym.nameString) - Console.println("\t" + showsym(sym)) - throw e - } - } - } - - /* - * add a mapping between sym and mInfo - */ - private def mapMethod(sym: Symbol, mInfo: MethodInfo) { - 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]) { - 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]) { - val methodSym = lookupMethod(clazz, name, paramTypes) - assert(methodSym != null, "cannot find method " + name + "(" + - paramTypes + ")" + " in class " + clazz) - mapMethod(methodSym, newClass, newName, newParamTypes) - } - - /* - * add mapping 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) { - 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) { - 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(params, 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 += 1 - } - } else { - typesOK = false - } - typesOK - }) match { - case Some(sym) => sym - case None => abort("member of " + clazz + ", " + name + "(" + - paramTypes + ") not found") - } - - case MethodType(_, _) => memberSym - - case _ => abort("member not found: " + name + " of " + clazz) - } - } - - private def showsym(sym: Symbol): String = (sym.toString + - "\n symbol = " + Flags.flagsToString(sym.flags) + " " + sym + - "\n owner = " + Flags.flagsToString(sym.owner.flags) + " " + sym.owner - ) - - } // class BytecodeGenerator - -} // class GenMSIL +// /* NSC -- new scala compiler +// * Copyright 2005-2011 LAMP/EPFL +// * @author Nikolay Mihaylov +// */ +// +// +// package scala.tools.nsc +// package backend.msil +// +// import java.io.{File, IOException} +// import java.nio.{ByteBuffer, ByteOrder} +// import scala.collection.{ mutable, immutable } +// import scala.tools.nsc.symtab._ +// +// import ch.epfl.lamp.compiler.msil.{Type => MsilType, _} +// import ch.epfl.lamp.compiler.msil.emit._ +// import ch.epfl.lamp.compiler.msil.util.PECustomMod +// +// abstract class GenMSIL extends SubComponent { +// import global._ +// import loaders.clrTypes +// import clrTypes.{types, constructors, methods, fields} +// import icodes._ +// import icodes.opcodes._ +// +// val x = loaders +// +// /** 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() { +// 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 +// if( opt.showClass.isDefined && (codeGenerator.entryPoint == null) ) { // TODO introduce dedicated setting instead +// val entryclass = opt.showClass.get.toString +// warning("Couldn't find entry class " + entryclass) +// } +// +// codeGenerator.initAssembly +// +// val classesSorted = classes.values.toList.sortBy(c => c.symbol.id) // simplifies comparing cross-compiler vs. .exe output +// classesSorted foreach codeGenerator.createTypeBuilder +// classesSorted foreach codeGenerator.createClassMembers +// +// try { +// classesSorted foreach codeGenerator.genClass +// } finally { +// codeGenerator.writeAssembly +// } +// } +// +// override def apply(unit: CompilationUnit) { +// abort("MSIL works on icode classes, not on compilation units!") +// } +// } +// +// /** +// * MSIL bytecode generator. +// * +// */ +// class BytecodeGenerator { +// +// val MODULE_INSTANCE_NAME = "MODULE$" +// +// import clrTypes.{VOID => MVOID, BOOLEAN => MBOOL, BYTE => MBYTE, SHORT => MSHORT, +// CHAR => MCHAR, INT => MINT, LONG => MLONG, FLOAT => MFLOAT, +// DOUBLE => MDOUBLE, OBJECT => MOBJECT, STRING => MSTRING, +// STRING_ARRAY => MSTRING_ARRAY, +// SYMTAB_CONSTR => SYMTAB_ATTRIBUTE_CONSTRUCTOR, +// SYMTAB_DEFAULT_CONSTR => SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR} +// +// val EXCEPTION = clrTypes.getType("System.Exception") +// val MBYTE_ARRAY = clrTypes.mkArrayType(MBYTE) +// +// val ICLONEABLE = clrTypes.getType("System.ICloneable") +// val MEMBERWISE_CLONE = MOBJECT.GetMethod("MemberwiseClone", MsilType.EmptyTypes) +// +// val MMONITOR = clrTypes.getType("System.Threading.Monitor") +// val MMONITOR_ENTER = MMONITOR.GetMethod("Enter", Array(MOBJECT)) +// val MMONITOR_EXIT = MMONITOR.GetMethod("Exit", Array(MOBJECT)) +// +// val MSTRING_BUILDER = clrTypes.getType("System.Text.StringBuilder") +// val MSTRING_BUILDER_CONSTR = MSTRING_BUILDER.GetConstructor(MsilType.EmptyTypes) +// val MSTRING_BUILDER_TOSTRING = MSTRING_BUILDER.GetMethod("ToString", +// MsilType.EmptyTypes) +// +// val TYPE_FROM_HANDLE = +// clrTypes.getType("System.Type").GetMethod("GetTypeFromHandle", Array(clrTypes.getType("System.RuntimeTypeHandle"))) +// +// val INT_PTR = clrTypes.getType("System.IntPtr") +// +// val JOBJECT = definitions.ObjectClass +// val JSTRING = definitions.StringClass +// +// val SystemConvert = clrTypes.getType("System.Convert") +// +// val objParam = Array(MOBJECT) +// +// val toBool: MethodInfo = SystemConvert.GetMethod("ToBoolean", objParam) // see comment in emitUnbox +// val toSByte: MethodInfo = SystemConvert.GetMethod("ToSByte", objParam) +// val toShort: MethodInfo = SystemConvert.GetMethod("ToInt16", objParam) +// val toChar: MethodInfo = SystemConvert.GetMethod("ToChar", objParam) +// val toInt: MethodInfo = SystemConvert.GetMethod("ToInt32", objParam) +// val toLong: MethodInfo = SystemConvert.GetMethod("ToInt64", objParam) +// val toFloat: MethodInfo = SystemConvert.GetMethod("ToSingle", objParam) +// val toDouble: MethodInfo = SystemConvert.GetMethod("ToDouble", objParam) +// +// //val boxedUnit: FieldInfo = 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.CloneableAttr.tpe +// val TransientAtt = definitions.TransientAttr.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 = mutable.HashSet[Symbol]() +// +// initMappings() +// +// /** Create the mappings between java and .net classes and methods */ +// private def initMappings() { +// mapType(definitions.AnyClass, MOBJECT) +// mapType(definitions.AnyRefClass, MOBJECT) +// //mapType(definitions.NullClass, clrTypes.getType("scala.AllRef$")) +// //mapType(definitions.NothingClass, clrTypes.getType("scala.All$")) +// // FIXME: for some reason the upper two lines map to null +// mapType(definitions.NullClass, EXCEPTION) +// mapType(definitions.NothingClass, EXCEPTION) +// +// mapType(definitions.BooleanClass, MBOOL) +// mapType(definitions.ByteClass, MBYTE) +// mapType(definitions.ShortClass, MSHORT) +// mapType(definitions.CharClass, MCHAR) +// mapType(definitions.IntClass, MINT) +// mapType(definitions.LongClass, MLONG) +// mapType(definitions.FloatClass, MFLOAT) +// mapType(definitions.DoubleClass, MDOUBLE) +// } +// +// var clasz: IClass = _ +// var method: IMethod = _ +// +// var massembly: AssemblyBuilder = _ +// var mmodule: ModuleBuilder = _ +// var mcode: ILGenerator = _ +// +// var assemName: String = _ +// var firstSourceName = "" +// var outDir: File = _ +// var srcPath: File = _ +// var moduleName: String = _ +// +// def initAssembly() { +// +// assemName = settings.assemname.value +// +// if (assemName == "") { +// if (entryPoint != null) { +// assemName = msilName(entryPoint.enclClass) +// // 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"), firstSourceName) +// assemName = firstSourceName.substring(0, firstSourceName.length() - 6) +// } +// } else { +// if (assemName.endsWith(".msil")) +// assemName = assemName.substring(0, assemName.length()-5) +// if (assemName.endsWith(".il")) +// assemName = assemName.substring(0, assemName.length()-3) +// val f: File = new File(assemName) +// assemName = f.getName() +// } +// +// outDir = new File(settings.outdir.value) +// +// srcPath = new File(settings.sourcedir.value) +// +// val assemblyName = new AssemblyName() +// assemblyName.Name = assemName +// massembly = AssemblyBuilderFactory.DefineDynamicAssembly(assemblyName) +// +// moduleName = assemName // + (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) +// } +// +// +// /** +// * 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, 4 bytes, least significant first) +// * 2.2: the byte array data +// * 3: NumNamed (unsigned int16, number of named fields and properties, 0x0000) +// */ +// def addSymtabAttribute(sym: Symbol, tBuilder: TypeBuilder) { +// def addMarker() { +// val markerSymtab = new Array[Byte](4) +// markerSymtab(0) = 1.toByte +// tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR, markerSymtab) +// } +// +// // both conditions are needed (why exactly..?) +// if (tBuilder.Name.endsWith("$") || sym.isModuleClass) { +// addMarker() +// } else { +// currentRun.symData.get(sym) match { +// case Some(pickle) => +// var size = pickle.writeIndex +// val symtab = new Array[Byte](size + 8) +// symtab(0) = 1.toByte +// for (i <- 2 until 6) { +// symtab(i) = (size & 0xff).toByte +// size = size >> 8 +// } +// java.lang.System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex) +// +// tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab) +// +// currentRun.symData -= sym +// currentRun.symData -= sym.companionSymbol +// +// case _ => +// addMarker() +// } +// } +// } +// +// /** +// * Mutates `member` adding CLR attributes (if any) based on sym.annotations. +// * Please notice that CLR custom modifiers are a different beast (see customModifiers below) +// * and thus shouldn't be added by this method. +// */ +// def addAttributes(member: ICustomAttributeSetter, annotations: List[AnnotationInfo]) { +// val attributes = annotations.map(_.atp.typeSymbol).collect { +// case definitions.TransientAttr => null // TODO this is just an example +// } +// return // TODO: implement at some point +// } +// +// /** +// * What's a CLR custom modifier? Intro available as source comments in compiler.msil.CustomModifier. +// * It's basically a marker associated with a location (think of FieldInfo, ParameterInfo, and PropertyInfo) +// * and thus that marker (be it optional or required) becomes part of the signature of that location. +// * Some annotations will become CLR attributes (see addAttributes above), others custom modifiers (this method). +// */ +// def customModifiers(annotations: List[AnnotationInfo]): Array[CustomModifier] = { +// annotations.map(_.atp.typeSymbol).collect { +// case definitions.VolatileAttr => new CustomModifier(true, CustomModifier.VolatileMarker) +// } toArray +// } +// +// +// +// /* +// debuglog("creating annotations: " + annotations + " for member : " + member) +// for (annot@ AnnotationInfo(typ, annArgs, nvPairs) <- annotations ; +// if annot.isConstant) +// //!typ.typeSymbol.isJavaDefined +// { +// // assert(consts.length <= 1, +// // "too many constant arguments for annotations; "+consts.toString()) +// +// // Problem / TODO having the symbol of the annotations type would be nicer +// // (i hope that type.typeSymbol is the same as the one in types2create) +// // AND: this will crash if the annotations Type is already compiled (-> not a typeBuilder) +// // when this is solved, types2create will be the same as icodes.classes, thus superfluous +// val annType: TypeBuilder = getType(typ.typeSymbol).asInstanceOf[TypeBuilder] +// // val annType: MsilType = getType(typ.typeSymbol) +// +// // Problem / TODO: i have no idea which constructor is used. This +// // information should be available in AnnotationInfo. +// annType.CreateType() // else, GetConstructors can't be used +// val constr: ConstructorInfo = annType.GetConstructors()(0) +// // prevent a second call of CreateType, only needed because there's no +// // other way than GetConstructors()(0) to get the constructor, if there's +// // no constructor symbol available. +// +// val args: Array[Byte] = +// getAttributeArgs( +// annArgs map (_.constant.get), +// (for((n,v) <- nvPairs) yield (n, v.constant.get))) +// member.SetCustomAttribute(constr, args) +// } +// } */ +// +// /* def getAttributeArgs(consts: List[Constant], nvPairs: List[(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: (Name, Constant)) { +// // 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().slice(0, length) +// } */ +// +// def writeAssembly() { +// if (entryPoint != null) { +// assert(entryPoint.enclClass.isModuleClass, entryPoint.enclClass) +// val mainMethod = methods(entryPoint) +// val stringArrayTypes: Array[MsilType] = Array(MSTRING_ARRAY) +// val globalMain = mmodule.DefineGlobalMethod( +// "Main", MethodAttributes.Public | MethodAttributes.Static, +// MVOID, stringArrayTypes) +// globalMain.DefineParameter(0, ParameterAttributes.None, "args") +// massembly.SetEntryPoint(globalMain) +// val code = globalMain.GetILGenerator() +// val moduleField = getModuleInstanceField(entryPoint.enclClass) +// code.Emit(OpCodes.Ldsfld, moduleField) +// code.Emit(OpCodes.Ldarg_0) +// code.Emit(OpCodes.Callvirt, mainMethod) +// code.Emit(OpCodes.Ret) +// } +// createTypes() +// var outDirName: String = null +// try { +// if (settings.Ygenjavap.isDefault) { // we reuse the JVM-sounding setting because it's conceptually similar +// outDirName = outDir.getPath() +// massembly.Save(outDirName + "\\" + assemName + ".msil") /* use SingleFileILPrinterVisitor */ +// } else { +// outDirName = srcPath.getPath() +// massembly.Save(settings.Ygenjavap.value, outDirName) /* use MultipleFilesILPrinterVisitor */ +// } +// } catch { +// case e:IOException => abort("Could not write to " + outDirName + ": " + e.getMessage()) +// } +// } +// +// private def createTypes() { +// for (sym <- classes.keys) { +// val iclass = classes(sym) +// val tBuilder = types(sym).asInstanceOf[TypeBuilder] +// +// debuglog("Calling CreatType for " + sym + ", " + tBuilder.toString) +// +// tBuilder.CreateType() +// tBuilder.setSourceFilepath(iclass.cunit.source.file.path) +// } +// } +// +// private[GenMSIL] def ilasmFileName(iclass: IClass) : String = { +// // method.sourceFile contains just the filename +// iclass.cunit.source.file.toString.replace("\\", "\\\\") +// } +// +// private[GenMSIL] def genClass(iclass: IClass) { +// val sym = iclass.symbol +// debuglog("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) +// })) { +// debuglog("auto-generating cloneable method for " + sym) +// val attrs: Short = (MethodAttributes.Public | MethodAttributes.Virtual | +// MethodAttributes.HideBySig).toShort +// val cloneMethod = tBuilder.DefineMethod("Clone", attrs, MOBJECT, +// MsilType.EmptyTypes) +// val clCode = cloneMethod.GetILGenerator() +// clCode.Emit(OpCodes.Ldarg_0) +// clCode.Emit(OpCodes.Call, MEMBERWISE_CLONE) +// clCode.Emit(OpCodes.Ret) +// } +// } +// +// val line = sym.pos.line +// tBuilder.setPosition(line, ilasmFileName(iclass)) +// +// if (isTopLevelModule(sym)) { +// if (sym.companionClass == NoSymbol) +// generateMirrorClass(sym) +// else +// log("No mirror class for module with linked class: " + +// sym.fullName) +// } +// +// addSymtabAttribute(sym, tBuilder) +// addAttributes(tBuilder, sym.annotations) +// +// if (iclass.symbol != definitions.ArrayClass) +// iclass.methods foreach genMethod +// +// } //genClass +// +// +// private def genMethod(m: IMethod) { +// debuglog("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) + +// " owner: " + m.symbol.owner) +// method = m +// localBuilders.clear +// computeLocalVarsIndex(m) +// +// if (m.symbol.isClassConstructor) { +// mcode = constructors(m.symbol).asInstanceOf[ConstructorBuilder].GetILGenerator() +// } else { +// val mBuilder = methods(m.symbol).asInstanceOf[MethodBuilder] +// if (!mBuilder.IsAbstract()) +// try { +// mcode = mBuilder.GetILGenerator() +// } catch { +// case e: Exception => +// java.lang.System.out.println("m.symbol = " + Flags.flagsToString(m.symbol.flags) + " " + m.symbol) +// java.lang.System.out.println("m.symbol.owner = " + Flags.flagsToString(m.symbol.owner.flags) + " " + m.symbol.owner) +// java.lang.System.out.println("mBuilder = " + mBuilder) +// java.lang.System.out.println("mBuilder.DeclaringType = " + +// TypeAttributes.toString(mBuilder.DeclaringType.Attributes) + +// "::" + mBuilder.DeclaringType) +// throw e +// } +// else +// mcode = null +// } +// +// if (mcode != null) { +// for (local <- m.locals ; if !(m.params contains local)) { +// debuglog("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) +// } +// +// } +// +// /** Special linearizer for methods with at least one exception handler. This +// * linearizer brings all basic blocks in the right order so that nested +// * try-catch and try-finally blocks can be emitted. +// */ +// val msilLinearizer = new MSILLinearizer() +// +// val labels = mutable.HashMap[BasicBlock, Label]() +// +// /* when emitting .line, it's enough to include the full filename just once per method, thus reducing filesize. +// * this scheme relies on the fact that the entry block is emitted first. */ +// var dbFilenameSeen = false +// +// def genCode(m: IMethod) { +// +// def makeLabels(blocks: List[BasicBlock]) = { +// debuglog("Making labels for: " + method) +// for (bb <- blocks) labels(bb) = mcode.DefineLabel() +// } +// +// labels.clear +// +// var linearization = if(!m.exh.isEmpty) msilLinearizer.linearize(m) +// else linearizer.linearize(m) +// +// if (!m.exh.isEmpty) +// linearization = computeExceptionMaps(linearization, m) +// +// makeLabels(linearization) +// +// // debug val blocksInM = m.code.blocks.toList.sortBy(bb => bb.label) +// // debug val blocksInL = linearization.sortBy(bb => bb.label) +// // debug val MButNotL = (blocksInM.toSet) diff (blocksInL.toSet) // if non-empty, a jump to B fails to find a label for B (case CJUMP, case CZJUMP) +// // debug if(!MButNotL.isEmpty) { } +// +// dbFilenameSeen = false +// genBlocks(linearization) +// +// // RETURN inside exception blocks are replaced by Leave. The target of the +// // leave is a `Ret` outside any exception block (generated here). +// if (handlerReturnMethod == m) { +// mcode.MarkLabel(handlerReturnLabel) +// if (handlerReturnKind != UNIT) +// mcode.Emit(OpCodes.Ldloc, handlerReturnLocal) +// mcode.Emit(OpCodes.Ret) +// } +// +// beginExBlock.clear() +// beginCatchBlock.clear() +// endExBlock.clear() +// endFinallyLabels.clear() +// } +// +// def genBlocks(blocks: List[BasicBlock], previous: BasicBlock = null) { +// blocks match { +// case Nil => () +// case x :: Nil => genBlock(x, prev = previous, next = null) +// case x :: y :: ys => genBlock(x, prev = previous, next = y); genBlocks(y :: ys, previous = x) +// } +// } +// +// // the try blocks starting at a certain BasicBlock +// val beginExBlock = mutable.HashMap[BasicBlock, List[ExceptionHandler]]() +// +// // the catch blocks starting / endling at a certain BasicBlock +// val beginCatchBlock = mutable.HashMap[BasicBlock, ExceptionHandler]() +// val endExBlock = mutable.HashMap[BasicBlock, List[ExceptionHandler]]() +// +// /** When emitting the code (genBlock), the number of currently active try / catch +// * blocks. When seeing a `RETURN` inside a try / catch, we need to +// * - store the result in a local (if it's not UNIT) +// * - emit `Leave handlerReturnLabel` instead of the Return +// * - emit code at the end: load the local and return its value +// */ +// var currentHandlers = new mutable.Stack[ExceptionHandler] +// // The IMethod the Local/Label/Kind below belong to +// var handlerReturnMethod: IMethod = _ +// // Stores the result when returning inside an exception block +// var handlerReturnLocal: LocalBuilder = _ +// // Label for a return instruction outside any exception block +// var handlerReturnLabel: Label = _ +// // The result kind. +// var handlerReturnKind: TypeKind = _ +// def returnFromHandler(kind: TypeKind): (LocalBuilder, Label) = { +// if (handlerReturnMethod != method) { +// handlerReturnMethod = method +// if (kind != UNIT) { +// handlerReturnLocal = mcode.DeclareLocal(msilType(kind)) +// handlerReturnLocal.SetLocalSymInfo("$handlerReturn") +// } +// handlerReturnLabel = mcode.DefineLabel() +// handlerReturnKind = kind +// } +// (handlerReturnLocal, handlerReturnLabel) +// } +// +// /** For try/catch nested inside a finally, we can't use `Leave OutsideFinally`, the +// * Leave target has to be inside the finally (and it has to be the `endfinally` instruction). +// * So for every finalizer, we have a label which marks the place of the `endfinally`, +// * nested try/catch blocks will leave there. +// */ +// val endFinallyLabels = mutable.HashMap[ExceptionHandler, Label]() +// +// /** Computes which blocks are the beginning / end of a try or catch block */ +// private def computeExceptionMaps(blocks: List[BasicBlock], m: IMethod): List[BasicBlock] = { +// val visitedBlocks = new mutable.HashSet[BasicBlock]() +// +// // handlers which have not been introduced so far +// var openHandlers = m.exh +// +// +// /** Example +// * try { +// * try { +// * // *1* +// * } catch { +// * case h1 => +// * } +// * } catch { +// * case h2 => +// * case h3 => +// * try { +// * +// * } catch { +// * case h4 => // *2* +// * case h5 => +// * } +// * } +// */ +// +// // Stack of nested try blocks. Each bloc has a List of ExceptionHandler (multiple +// // catch statements). Example *1*: Stack(List(h2, h3), List(h1)) +// val currentTryHandlers = new mutable.Stack[List[ExceptionHandler]]() +// +// // Stack of nested catch blocks. The head of the list is the current catch block. The +// // tail is all following catch blocks. Example *2*: Stack(List(h3), List(h4, h5)) +// val currentCatchHandlers = new mutable.Stack[List[ExceptionHandler]]() +// +// for (b <- blocks) { +// +// // are we past the current catch blocks? +// def endHandlers(): List[ExceptionHandler] = { +// var res: List[ExceptionHandler] = Nil +// if (!currentCatchHandlers.isEmpty) { +// val handler = currentCatchHandlers.top.head +// if (!handler.blocks.contains(b)) { +// // all blocks of the handler are either visited, or not part of the linearization (i.e. dead) +// assert(handler.blocks.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)), +// "Bad linearization of basic blocks inside catch. Found block not part of the handler\n"+ +// b.fullString +"\nwhile in catch-part of\n"+ handler) +// +// val rest = currentCatchHandlers.pop.tail +// if (rest.isEmpty) { +// // all catch blocks of that exception handler are covered +// res = handler :: endHandlers() +// } else { +// // there are more catch blocks for that try (handlers covering the same) +// currentCatchHandlers.push(rest) +// beginCatchBlock(b) = rest.head +// } +// } +// } +// res +// } +// val end = endHandlers() +// if (!end.isEmpty) endExBlock(b) = end +// +// // are we past the current try block? +// if (!currentTryHandlers.isEmpty) { +// val handler = currentTryHandlers.top.head +// if (!handler.covers(b)) { +// // all of the covered blocks are visited, or not part of the linearization +// assert(handler.covered.forall(b => visitedBlocks.contains(b) || !blocks.contains(b)), +// "Bad linearization of basic blocks inside try. Found non-covered block\n"+ +// b.fullString +"\nwhile in try-part of\n"+ handler) +// +// assert(handler.startBlock == b, +// "Bad linearization of basic blocks. The entry block of a catch does not directly follow the try\n"+ +// b.fullString +"\n"+ handler) +// +// val handlers = currentTryHandlers.pop +// currentCatchHandlers.push(handlers) +// beginCatchBlock(b) = handler +// } +// } +// +// // are there try blocks starting at b? +// val (newHandlers, stillOpen) = openHandlers.partition(_.covers(b)) +// openHandlers = stillOpen +// +// val newHandlersBySize = newHandlers.groupBy(_.covered.size) +// // big handlers first, smaller ones are nested inside the try of the big one +// // (checked by the assertions below) +// val sizes = newHandlersBySize.keys.toList.sortWith(_ > _) +// +// val beginHandlers = new mutable.ListBuffer[ExceptionHandler] +// for (s <- sizes) { +// val sHandlers = newHandlersBySize(s) +// for (h <- sHandlers) { +// assert(h.covered == sHandlers.head.covered, +// "bad nesting of exception handlers. same size, but not covering same blocks\n"+ +// h +"\n"+ sHandlers.head) +// assert(h.resultKind == sHandlers.head.resultKind, +// "bad nesting of exception handlers. same size, but the same resultKind\n"+ +// h +"\n"+ sHandlers.head) +// } +// for (bigger <- beginHandlers; h <- sHandlers) { +// assert(h.covered.subsetOf(bigger.covered), +// "bad nesting of exception handlers. try blocks of smaller handler are not nested in bigger one.\n"+ +// h +"\n"+ bigger) +// assert(h.blocks.toSet.subsetOf(bigger.covered), +// "bad nesting of exception handlers. catch blocks of smaller handler are not nested in bigger one.\n"+ +// h +"\n"+ bigger) +// } +// beginHandlers += sHandlers.head +// currentTryHandlers.push(sHandlers) +// } +// beginExBlock(b) = beginHandlers.toList +// visitedBlocks += b +// } +// +// // if there handlers left (i.e. handlers covering nothing, or a +// // non-existent (dead) block), remove their catch-blocks. +// val liveBlocks = if (openHandlers.isEmpty) blocks else { +// blocks.filter(b => openHandlers.forall(h => !h.blocks.contains(b))) +// } +// +// /** There might be open handlers, but no more blocks. happens when try/catch end +// * with `throw` or `return` +// * def foo() { try { .. throw } catch { _ => .. throw } } +// * +// * In this case we need some code after the catch block for the auto-generated +// * `leave` instruction. So we're adding a (dead) `throw new Exception`. +// */ +// val rest = currentCatchHandlers.map(handlers => { +// assert(handlers.length == 1, handlers) +// handlers.head +// }).toList +// +// if (rest.isEmpty) { +// liveBlocks +// } else { +// val b = m.code.newBlock +// b.emit(Seq( +// NEW(REFERENCE(definitions.ThrowableClass)), +// DUP(REFERENCE(definitions.ObjectClass)), +// CALL_METHOD(definitions.ThrowableClass.primaryConstructor, Static(true)), +// THROW(definitions.ThrowableClass) +// )) +// b.close +// endExBlock(b) = rest +// liveBlocks ::: List(b) +// } +// } +// +// /** +// * @param block the BasicBlock to emit code for +// * @param next the following BasicBlock, `null` if `block` is the last one +// */ +// def genBlock(block: BasicBlock, prev: BasicBlock, next: BasicBlock) { +// +// def loadLocalOrAddress(local: Local, msg : String , loadAddr : Boolean) { +// debuglog(msg + " for " + local) +// val isArg = local.arg +// val i = local.index +// if (isArg) +// loadArg(mcode, loadAddr)(i) +// else +// loadLocal(i, local, mcode, loadAddr) +// } +// +// def loadFieldOrAddress(field: Symbol, isStatic: Boolean, msg: String, loadAddr : Boolean) { +// debuglog(msg + " with owner: " + field.owner + +// " flags: " + Flags.flagsToString(field.owner.flags)) +// var fieldInfo = fields.get(field) match { +// case Some(fInfo) => fInfo +// case None => +// val fInfo = getType(field.owner).GetField(msilName(field)) +// fields(field) = fInfo +// fInfo +// } +// if (fieldInfo.IsVolatile) { +// mcode.Emit(OpCodes.Volatile) +// } +// if (!fieldInfo.IsLiteral) { +// if (loadAddr) { +// mcode.Emit(if (isStatic) OpCodes.Ldsflda else OpCodes.Ldflda, fieldInfo) +// } else { +// mcode.Emit(if (isStatic) OpCodes.Ldsfld else OpCodes.Ldfld, fieldInfo) +// } +// } else { +// assert(!loadAddr, "can't take AddressOf a literal field (not even with readonly. prefix) because no memory was allocated to such field ...") +// // TODO the above can be overcome by loading the value, boxing, and finally unboxing. An address to a copy of the raw value will be on the stack. +// /* We perform `field inlining' as required by CLR. +// * Emit as for a CONSTANT ICode stmt, with the twist that the constant value is available +// * as a java.lang.Object and its .NET type allows constant initialization in CLR, i.e. that type +// * is one of I1, I2, I4, I8, R4, R8, CHAR, BOOLEAN, STRING, or CLASS (in this last case, +// * only accepting nullref as value). See Table 9-1 in Lidin's book on ILAsm. */ +// val value = fieldInfo.getValue() +// if (value == null) { +// mcode.Emit(OpCodes.Ldnull) +// } else { +// val typ = if (fieldInfo.FieldType.IsEnum) fieldInfo.FieldType.getUnderlyingType +// else fieldInfo.FieldType +// if (typ == clrTypes.STRING) { +// mcode.Emit(OpCodes.Ldstr, value.asInstanceOf[String]) +// } else if (typ == clrTypes.BOOLEAN) { +// mcode.Emit(if (value.asInstanceOf[Boolean]) OpCodes.Ldc_I4_1 +// else OpCodes.Ldc_I4_0) +// } else if (typ == clrTypes.BYTE || typ == clrTypes.UBYTE) { +// loadI4(value.asInstanceOf[Byte], mcode) +// } else if (typ == clrTypes.SHORT || typ == clrTypes.USHORT) { +// loadI4(value.asInstanceOf[Int], mcode) +// } else if (typ == clrTypes.CHAR) { +// loadI4(value.asInstanceOf[Char], mcode) +// } else if (typ == clrTypes.INT || typ == clrTypes.UINT) { +// loadI4(value.asInstanceOf[Int], mcode) +// } else if (typ == clrTypes.LONG || typ == clrTypes.ULONG) { +// mcode.Emit(OpCodes.Ldc_I8, value.asInstanceOf[Long]) +// } else if (typ == clrTypes.FLOAT) { +// mcode.Emit(OpCodes.Ldc_R4, value.asInstanceOf[Float]) +// } else if (typ == clrTypes.DOUBLE) { +// mcode.Emit(OpCodes.Ldc_R8, value.asInstanceOf[Double]) +// } else { +// /* TODO one more case is described in Partition II, 16.2: bytearray(...) */ +// abort("Unknown type for static literal field: " + fieldInfo) +// } +// } +// } +// } +// +// /** Creating objects works differently on .NET. On the JVM +// * - NEW(type) => reference on Stack +// * - DUP, load arguments, CALL_METHOD(constructor) +// * +// * On .NET, the NEW and DUP are ignored, but we emit a special method call +// * - load arguments +// * - NewObj(constructor) => reference on stack +// * +// * This variable tells whether the previous instruction was a NEW, +// * we expect a DUP which is not emitted. */ +// var previousWasNEW = false +// +// var lastLineNr: Int = 0 +// var lastPos: Position = NoPosition +// +// +// // EndExceptionBlock must happen before MarkLabel because it adds the +// // Leave instruction. Otherwise, labels(block) points to the Leave +// // (inside the catch) instead of the instruction afterwards. +// for (handlers <- endExBlock.get(block); exh <- handlers) { +// currentHandlers.pop() +// for (l <- endFinallyLabels.get(exh)) +// mcode.MarkLabel(l) +// mcode.EndExceptionBlock() +// } +// +// mcode.MarkLabel(labels(block)) +// debuglog("Generating code for block: " + block) +// +// for (handler <- beginCatchBlock.get(block)) { +// if (!currentHandlers.isEmpty && currentHandlers.top.covered == handler.covered) { +// currentHandlers.pop() +// currentHandlers.push(handler) +// } +// if (handler.cls == NoSymbol) { +// // `finally` blocks are represented the same as `catch`, but with no catch-type +// mcode.BeginFinallyBlock() +// } else { +// val t = getType(handler.cls) +// mcode.BeginCatchBlock(t) +// } +// } +// for (handlers <- beginExBlock.get(block); exh <- handlers) { +// currentHandlers.push(exh) +// mcode.BeginExceptionBlock() +// } +// +// for (instr <- block) { +// try { +// val currentLineNr = instr.pos.line +// val skip = if(instr.pos.isRange) instr.pos.sameRange(lastPos) else (currentLineNr == lastLineNr); +// if(!skip || !dbFilenameSeen) { +// val fileName = if(dbFilenameSeen) "" else {dbFilenameSeen = true; ilasmFileName(clasz)}; +// if(instr.pos.isRange) { +// val startLine = instr.pos.focusStart.line +// val endLine = instr.pos.focusEnd.line +// val startCol = instr.pos.focusStart.column +// val endCol = instr.pos.focusEnd.column +// mcode.setPosition(startLine, endLine, startCol, endCol, fileName) +// } else { +// mcode.setPosition(instr.pos.line, fileName) +// } +// lastLineNr = currentLineNr +// lastPos = instr.pos +// } +// } catch { case _: UnsupportedOperationException => () } +// +// if (previousWasNEW) +// assert(instr.isInstanceOf[DUP], block) +// +// instr match { +// case THIS(clasz) => +// mcode.Emit(OpCodes.Ldarg_0) +// +// case CONSTANT(const) => +// const.tag match { +// case UnitTag => () +// case BooleanTag => mcode.Emit(if (const.booleanValue) OpCodes.Ldc_I4_1 +// else OpCodes.Ldc_I4_0) +// case ByteTag => loadI4(const.byteValue, mcode) +// case ShortTag => loadI4(const.shortValue, mcode) +// case CharTag => loadI4(const.charValue, mcode) +// case IntTag => loadI4(const.intValue, mcode) +// case LongTag => mcode.Emit(OpCodes.Ldc_I8, const.longValue) +// case FloatTag => mcode.Emit(OpCodes.Ldc_R4, const.floatValue) +// case DoubleTag => mcode.Emit(OpCodes.Ldc_R8, const.doubleValue) +// case StringTag => mcode.Emit(OpCodes.Ldstr, const.stringValue) +// case NullTag => mcode.Emit(OpCodes.Ldnull) +// case ClassTag => +// mcode.Emit(OpCodes.Ldtoken, msilType(const.typeValue)) +// mcode.Emit(OpCodes.Call, TYPE_FROM_HANDLE) +// case _ => abort("Unknown constant value: " + const) +// } +// +// case LOAD_ARRAY_ITEM(kind) => +// (kind: @unchecked) match { +// case BOOL => mcode.Emit(OpCodes.Ldelem_I1) +// case BYTE => mcode.Emit(OpCodes.Ldelem_I1) // I1 for System.SByte, i.e. a scala.Byte +// case SHORT => mcode.Emit(OpCodes.Ldelem_I2) +// case CHAR => mcode.Emit(OpCodes.Ldelem_U2) +// case INT => mcode.Emit(OpCodes.Ldelem_I4) +// case LONG => mcode.Emit(OpCodes.Ldelem_I8) +// case FLOAT => mcode.Emit(OpCodes.Ldelem_R4) +// case DOUBLE => mcode.Emit(OpCodes.Ldelem_R8) +// case REFERENCE(cls) => mcode.Emit(OpCodes.Ldelem_Ref) +// case ARRAY(elem) => mcode.Emit(OpCodes.Ldelem_Ref) +// +// // case UNIT is not possible: an Array[Unit] will be an +// // Array[scala.runtime.BoxedUnit] (-> case REFERENCE) +// } +// +// case LOAD_LOCAL(local) => loadLocalOrAddress(local, "load_local", false) +// +// case CIL_LOAD_LOCAL_ADDRESS(local) => loadLocalOrAddress(local, "cil_load_local_address", true) +// +// case LOAD_FIELD(field, isStatic) => loadFieldOrAddress(field, isStatic, "load_field", false) +// +// case CIL_LOAD_FIELD_ADDRESS(field, isStatic) => loadFieldOrAddress(field, isStatic, "cil_load_field_address", true) +// +// case CIL_LOAD_ARRAY_ITEM_ADDRESS(kind) => mcode.Emit(OpCodes.Ldelema, msilType(kind)) +// +// case CIL_NEWOBJ(msym) => +// assert(msym.isClassConstructor) +// val constructorInfo: ConstructorInfo = getConstructor(msym) +// mcode.Emit(OpCodes.Newobj, constructorInfo) +// +// case LOAD_MODULE(module) => +// debuglog("Generating LOAD_MODULE for: " + showsym(module)) +// mcode.Emit(OpCodes.Ldsfld, getModuleInstanceField(module)) +// +// case STORE_ARRAY_ITEM(kind) => +// (kind: @unchecked) match { +// case BOOL => mcode.Emit(OpCodes.Stelem_I1) +// case BYTE => mcode.Emit(OpCodes.Stelem_I1) +// case SHORT => mcode.Emit(OpCodes.Stelem_I2) +// case CHAR => mcode.Emit(OpCodes.Stelem_I2) +// case INT => mcode.Emit(OpCodes.Stelem_I4) +// case LONG => mcode.Emit(OpCodes.Stelem_I8) +// case FLOAT => mcode.Emit(OpCodes.Stelem_R4) +// case DOUBLE => mcode.Emit(OpCodes.Stelem_R8) +// case REFERENCE(cls) => mcode.Emit(OpCodes.Stelem_Ref) +// case ARRAY(elem) => mcode.Emit(OpCodes.Stelem_Ref) // @TODO: test this! (occurs when calling a Array[Object]* vararg param method) +// +// // case UNIT not possible (see comment at LOAD_ARRAY_ITEM) +// } +// +// case STORE_LOCAL(local) => +// val isArg = local.arg +// val i = local.index +// debuglog("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_THIS(_) => +// // this only works for impl classes because the self parameter comes first +// // in the method signature. If that changes, this code has to be revisited. +// mcode.Emit(OpCodes.Starg_S, 0) +// +// case STORE_FIELD(field, isStatic) => +// val fieldInfo = fields.get(field) match { +// case Some(fInfo) => fInfo +// case None => +// val fInfo = getType(field.owner).GetField(msilName(field)) +// fields(field) = fInfo +// fInfo +// } +// mcode.Emit(if (isStatic) OpCodes.Stsfld else OpCodes.Stfld, fieldInfo) +// +// case CALL_PRIMITIVE(primitive) => +// genPrimitive(primitive, instr.pos) +// +// case CALL_METHOD(msym, style) => +// if (msym.isClassConstructor) { +// val constructorInfo: ConstructorInfo = getConstructor(msym) +// (style: @unchecked) match { +// // normal constructor calls are Static.. +// case Static(_) => +// if (method.symbol.isClassConstructor && method.symbol.owner == msym.owner) +// // we're generating a constructor (method: IMethod is a constructor), and we're +// // calling another constructor of the same class. +// +// // @LUC TODO: this can probably break, namely when having: class A { def this() { new A() } } +// // instead, we should instruct the CALL_METHOD with additional information, know whether it's +// // an instance creation constructor call or not. +// mcode.Emit(OpCodes.Call, constructorInfo) +// else +// mcode.Emit(OpCodes.Newobj, constructorInfo) +// case SuperCall(_) => +// mcode.Emit(OpCodes.Call, constructorInfo) +// if (isStaticModule(clasz.symbol) && +// notInitializedModules.contains(clasz.symbol) && +// method.symbol.isClassConstructor) +// { +// notInitializedModules -= clasz.symbol +// mcode.Emit(OpCodes.Ldarg_0) +// mcode.Emit(OpCodes.Stsfld, getModuleInstanceField(clasz.symbol)) +// } +// } +// +// } else { +// +// var doEmit = true +// getTypeOpt(msym.owner) match { +// case Some(typ) if (typ.IsEnum) => { +// def negBool() = { +// mcode.Emit(OpCodes.Ldc_I4_0) +// mcode.Emit(OpCodes.Ceq) +// } +// doEmit = false +// val name = msym.name +// if (name eq nme.EQ) { mcode.Emit(OpCodes.Ceq) } +// else if (name eq nme.NE) { mcode.Emit(OpCodes.Ceq); negBool } +// else if (name eq nme.LT) { mcode.Emit(OpCodes.Clt) } +// else if (name eq nme.LE) { mcode.Emit(OpCodes.Cgt); negBool } +// else if (name eq nme.GT) { mcode.Emit(OpCodes.Cgt) } +// else if (name eq nme.GE) { mcode.Emit(OpCodes.Clt); negBool } +// else if (name eq nme.OR) { mcode.Emit(OpCodes.Or) } +// else if (name eq nme.AND) { mcode.Emit(OpCodes.And) } +// else if (name eq nme.XOR) { mcode.Emit(OpCodes.Xor) } +// else +// doEmit = true +// } +// case _ => () +// } +// +// // method: implicit view(FunctionX[PType0, PType1, ...,PTypeN, ResType]):DelegateType +// val (isDelegateView, paramType, resType) = atPhase(currentRun.typerPhase) { +// msym.tpe match { +// case MethodType(params, resultType) +// if (params.length == 1 && msym.name == nme.view_) => +// val paramType = params(0).tpe +// val isDel = definitions.isCorrespondingDelegate(resultType, paramType) +// (isDel, paramType, resultType) +// case _ => (false, null, null) +// } +// } +// if (doEmit && isDelegateView) { +// doEmit = false +// createDelegateCaller(paramType, resType) +// } +// +// if (doEmit && +// (msym.name == nme.PLUS || msym.name == nme.MINUS) +// && clrTypes.isDelegateType(msilType(msym.owner.tpe))) +// { +// doEmit = false +// val methodInfo: MethodInfo = getMethod(msym) +// // 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 methodInfo: MethodInfo = getMethod(methodSym) +// val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR)) +// if (methodSym.isStatic) { +// mcode.Emit(OpCodes.Ldftn, methodInfo) +// } else { +// mcode.Emit(OpCodes.Dup) +// mcode.Emit(OpCodes.Ldvirtftn, methodInfo) +// } +// mcode.Emit(OpCodes.Newobj, delegCtor) +// } +// +// if (doEmit) { +// val methodInfo: MethodInfo = getMethod(msym) +// (style: @unchecked) match { +// case SuperCall(_) => +// mcode.Emit(OpCodes.Call, methodInfo) +// case Dynamic => +// // methodInfo.DeclaringType is null for global methods +// val isValuetypeMethod = (methodInfo.DeclaringType ne null) && (methodInfo.DeclaringType.IsValueType) +// val isValuetypeVirtualMethod = isValuetypeMethod && (methodInfo.IsVirtual) +// if (dynToStatMapped(msym)) { +// mcode.Emit(OpCodes.Call, methodInfo) +// } else if (isValuetypeVirtualMethod) { +// mcode.Emit(OpCodes.Constrained, methodInfo.DeclaringType) +// mcode.Emit(OpCodes.Callvirt, methodInfo) +// } else if (isValuetypeMethod) { +// // otherwise error "Callvirt on a value type method" ensues +// mcode.Emit(OpCodes.Call, methodInfo) +// } else { +// mcode.Emit(OpCodes.Callvirt, methodInfo) +// } +// case Static(_) => +// if(methodInfo.IsVirtual && !mcode.Ldarg0WasJustEmitted) { +// mcode.Emit(OpCodes.Callvirt, methodInfo) +// } else mcode.Emit(OpCodes.Call, methodInfo) +// } +// } +// } +// +// case BOX(boxType) => +// emitBox(mcode, boxType) +// +// case UNBOX(boxType) => +// emitUnbox(mcode, boxType) +// +// case CIL_UNBOX(boxType) => +// mcode.Emit(OpCodes.Unbox, msilType(boxType)) +// +// case CIL_INITOBJ(valueType) => +// mcode.Emit(OpCodes.Initobj, msilType(valueType)) +// +// case NEW(REFERENCE(cls)) => +// // the next instruction must be a DUP, see comment on `var previousWasNEW` +// previousWasNEW = true +// +// // works also for arrays and reference-types +// case CREATE_ARRAY(elem, dims) => +// // TODO: handle multi dimensional arrays +// assert(dims == 1, "Can't handle multi dimensional arrays") +// 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(tpknd) => +// val tMSIL = msilType(tpknd) +// mcode.Emit(OpCodes.Castclass, tMSIL) +// +// // 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 is 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 = 0 +// for (l <- tags) { +// var targetLabel = labels(branches(i)) +// for (i <- l) { +// mcode.Emit(OpCodes.Ldloc, switchLocal) +// loadI4(i, mcode) +// mcode.Emit(OpCodes.Beq, targetLabel) +// } +// i += 1 +// } +// val defaultTarget = labels(branches(i)) +// if (next != branches(i)) +// mcode.Emit(OpCodes.Br, defaultTarget) +// +// case JUMP(whereto) => +// val (leaveHandler, leaveFinally, lfTarget) = leavesHandler(block, whereto) +// if (leaveHandler) { +// if (leaveFinally) { +// if (lfTarget.isDefined) mcode.Emit(OpCodes.Leave, lfTarget.get) +// else mcode.Emit(OpCodes.Endfinally) +// } else +// mcode.Emit(OpCodes.Leave, labels(whereto)) +// } else if (next != whereto) +// mcode.Emit(OpCodes.Br, labels(whereto)) +// +// case CJUMP(success, failure, cond, kind) => +// // 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 +// val emit = (c: TestOp, l: Label) => emitBr(c, l, isFloat) +// emitCondBr(block, cond, success, failure, next, emit) +// +// case CZJUMP(success, failure, cond, kind) => +// emitCondBr(block, cond, success, failure, next, emitBrBool(_, _)) +// +// case RETURN(kind) => +// if (currentHandlers.isEmpty) +// mcode.Emit(OpCodes.Ret) +// else { +// val (local, label) = returnFromHandler(kind) +// if (kind != UNIT) +// mcode.Emit(OpCodes.Stloc, local) +// mcode.Emit(OpCodes.Leave, label) +// } +// +// case THROW(_) => +// mcode.Emit(OpCodes.Throw) +// +// case DROP(kind) => +// mcode.Emit(OpCodes.Pop) +// +// case DUP(kind) => +// // see comment on `var previousWasNEW` +// if (!previousWasNEW) +// mcode.Emit(OpCodes.Dup) +// else +// previousWasNEW = false +// +// case MONITOR_ENTER() => +// mcode.Emit(OpCodes.Call, MMONITOR_ENTER) +// +// case MONITOR_EXIT() => +// mcode.Emit(OpCodes.Call, MMONITOR_EXIT) +// +// case SCOPE_ENTER(_) | SCOPE_EXIT(_) | LOAD_EXCEPTION(_) => +// () +// } +// +// } // end for (instr <- b) { .. } +// } // end genBlock +// +// def genPrimitive(primitive: Primitive, pos: Position) { +// 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) => +// debuglog("Converting from: " + src + " to: " + dst) +// +// dst match { +// case BYTE => mcode.Emit(OpCodes.Conv_I1) // I1 for System.SByte, i.e. a scala.Byte +// case SHORT => mcode.Emit(OpCodes.Conv_I2) +// case CHAR => mcode.Emit(OpCodes.Conv_U2) +// case INT => mcode.Emit(OpCodes.Conv_I4) +// case LONG => mcode.Emit(OpCodes.Conv_I8) +// case FLOAT => mcode.Emit(OpCodes.Conv_R4) +// case DOUBLE => mcode.Emit(OpCodes.Conv_R8) +// case _ => +// Console.println("Illegal conversion at: " + clasz + +// " at: " + pos.source + ":" + pos.line) +// } +// +// case ArrayLength(_) => +// mcode.Emit(OpCodes.Ldlen) +// +// case StartConcat => +// mcode.Emit(OpCodes.Newobj, MSTRING_BUILDER_CONSTR) +// +// +// case StringConcat(el) => +// val elemType : MsilType = el match { +// case REFERENCE(_) | ARRAY(_) => MOBJECT +// case _ => msilType(el) +// } +// +// val argTypes:Array[MsilType] = Array(elemType) +// val stringBuilderAppend = MSTRING_BUILDER.GetMethod("Append", argTypes ) +// mcode.Emit(OpCodes.Callvirt, stringBuilderAppend) +// +// case EndConcat => +// mcode.Emit(OpCodes.Callvirt, MSTRING_BUILDER_TOSTRING) +// +// case _ => +// abort("Unimplemented primitive " + primitive) +// } +// } // end genPrimitive +// +// +// ////////////////////// 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, loadAddr: Boolean)(i: Int) = +// if (loadAddr) { +// if (i >= -128 && i <= 127) +// code.Emit(OpCodes.Ldarga_S, i) +// else +// code.Emit(OpCodes.Ldarga, i) +// } else { +// i match { +// case 0 => code.Emit(OpCodes.Ldarg_0) +// case 1 => code.Emit(OpCodes.Ldarg_1) +// case 2 => code.Emit(OpCodes.Ldarg_2) +// case 3 => code.Emit(OpCodes.Ldarg_3) +// case _ => +// if (i >= -128 && i <= 127) +// code.Emit(OpCodes.Ldarg_S, i) +// else +// code.Emit(OpCodes.Ldarg, i) +// } +// } +// +// def loadLocal(i: Int, local: Local, code: ILGenerator, loadAddr: Boolean) = +// if (loadAddr) { +// if (i >= -128 && i <= 127) +// code.Emit(OpCodes.Ldloca_S, localBuilders(local)) +// else +// code.Emit(OpCodes.Ldloca, localBuilders(local)) +// } else { +// i match { +// case 0 => code.Emit(OpCodes.Ldloc_0) +// case 1 => code.Emit(OpCodes.Ldloc_1) +// case 2 => code.Emit(OpCodes.Ldloc_2) +// case 3 => code.Emit(OpCodes.Ldloc_3) +// case _ => +// if (i >= -128 && i <= 127) +// code.Emit(OpCodes.Ldloc_S, localBuilders(local)) +// else +// code.Emit(OpCodes.Ldloc, localBuilders(local)) +// } +// } +// +// ////////////////////// branches /////////////////////// +// +// /** Returns a Triple (Boolean, Boolean, Option[Label]) +// * - whether the jump leaves some exception block (try / catch / finally) +// * - whether it leaves a finally handler (finally block, but not it's try / catch) +// * - a label where to jump for leaving the finally handler +// * . None to leave directly using `endfinally` +// * . Some(label) to emit `leave label` (for try / catch inside a finally handler) +// */ +// def leavesHandler(from: BasicBlock, to: BasicBlock): (Boolean, Boolean, Option[Label]) = +// if (currentHandlers.isEmpty) (false, false, None) +// else { +// val h = currentHandlers.head +// val leaveHead = { h.covers(from) != h.covers(to) || +// h.blocks.contains(from) != h.blocks.contains(to) } +// if (leaveHead) { +// // we leave the innermost exception block. +// // find out if we also leave som e `finally` handler +// currentHandlers.find(e => { +// e.cls == NoSymbol && e.blocks.contains(from) != e.blocks.contains(to) +// }) match { +// case Some(finallyHandler) => +// if (h == finallyHandler) { +// // the finally handler is the innermost, so we can emit `endfinally` directly +// (true, true, None) +// } else { +// // we need to `Leave` to the `endfinally` of the next outer finally handler +// val l = endFinallyLabels.getOrElseUpdate(finallyHandler, mcode.DefineLabel()) +// (true, true, Some(l)) +// } +// case None => +// (true, false, None) +// } +// } else (false, false, None) +// } +// +// def emitCondBr(block: BasicBlock, cond: TestOp, success: BasicBlock, failure: BasicBlock, +// next: BasicBlock, emitBrFun: (TestOp, Label) => Unit) { +// val (sLeaveHandler, sLeaveFinally, slfTarget) = leavesHandler(block, success) +// val (fLeaveHandler, fLeaveFinally, flfTarget) = leavesHandler(block, failure) +// +// if (sLeaveHandler || fLeaveHandler) { +// val sLabelOpt = if (sLeaveHandler) { +// val leaveSLabel = mcode.DefineLabel() +// emitBrFun(cond, leaveSLabel) +// Some(leaveSLabel) +// } else { +// emitBrFun(cond, labels(success)) +// None +// } +// +// if (fLeaveHandler) { +// if (fLeaveFinally) { +// if (flfTarget.isDefined) mcode.Emit(OpCodes.Leave, flfTarget.get) +// else mcode.Emit(OpCodes.Endfinally) +// } else +// mcode.Emit(OpCodes.Leave, labels(failure)) +// } else +// mcode.Emit(OpCodes.Br, labels(failure)) +// +// sLabelOpt.map(l => { +// mcode.MarkLabel(l) +// if (sLeaveFinally) { +// if (slfTarget.isDefined) mcode.Emit(OpCodes.Leave, slfTarget.get) +// else mcode.Emit(OpCodes.Endfinally) +// } else +// mcode.Emit(OpCodes.Leave, labels(success)) +// }) +// } else { +// if (next == success) { +// emitBrFun(cond.negate, labels(failure)) +// } else { +// emitBrFun(cond, labels(success)) +// if (next != failure) { +// mcode.Emit(OpCodes.Br, labels(failure)) +// } +// } +// } +// } +// +// def emitBr(condition: TestOp, dest: Label, isFloat: Boolean) { +// condition match { +// case EQ => mcode.Emit(OpCodes.Beq, dest) +// case NE => mcode.Emit(OpCodes.Bne_Un, dest) +// case LT => mcode.Emit(if (isFloat) OpCodes.Blt_Un else OpCodes.Blt, dest) +// case GE => mcode.Emit(if (isFloat) OpCodes.Bge_Un else OpCodes.Bge, dest) +// case LE => mcode.Emit(if (isFloat) OpCodes.Ble_Un else OpCodes.Ble, dest) +// case GT => mcode.Emit(if (isFloat) OpCodes.Bgt_Un else OpCodes.Bgt, dest) +// } +// } +// +// def emitBrBool(cond: TestOp, dest: Label) { +// cond match { +// // EQ -> Brfalse, NE -> Brtrue; this is because we come from +// // a CZJUMP. If the value on the stack is 0 (e.g. a boolean +// // 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) +// } +// } +// +// ////////////////////// local vars /////////////////////// +// +// /** +// * Compute the indexes of each local variable of the given +// * method. +// */ +// def computeLocalVarsIndex(m: IMethod) { +// var idx = if (m.symbol.isStaticMember) 0 else 1 +// +// val params = m.params +// for (l <- params) { +// debuglog("Index value for parameter " + l + ": " + idx) +// l.index = idx +// idx += 1 // sizeOf(l.kind) +// } +// +// val locvars = m.locals filterNot (params contains) +// idx = 0 +// +// for (l <- locvars) { +// debuglog("Index value for local variable " + l + ": " + idx) +// l.index = 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 = sym.moduleSuffix +// // Flags.JAVA: "symbol was not defined by a scala-class" (java, or .net-class) +// +// if (sym == definitions.NothingClass) +// return "scala.runtime.Nothing$" +// else if (sym == definitions.NullClass) +// return "scala.runtime.Null$" +// +// (if (sym.isClass || (sym.isModule && !sym.isMethod)) { +// if (sym.isNestedClass) sym.simpleName +// else sym.fullName +// } else +// sym.simpleName.toString.trim()) + suffix +// } +// +// +// ////////////////////// flags /////////////////////// +// +// def msilTypeFlags(sym: Symbol): Int = { +// var mf: Int = TypeAttributes.AutoLayout | TypeAttributes.AnsiClass +// +// if(sym.isNestedClass) { +// mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NestedPrivate else TypeAttributes.NestedPublic) +// } else { +// mf = mf | (if (sym hasFlag Flags.PRIVATE) TypeAttributes.NotPublic else TypeAttributes.Public) +// } +// mf = mf | (if (sym hasFlag Flags.ABSTRACT) TypeAttributes.Abstract else 0) +// mf = mf | (if (sym.isTrait && !sym.isImplClass) TypeAttributes.Interface else TypeAttributes.Class) +// mf = mf | (if (sym isFinal) TypeAttributes.Sealed else 0) +// +// sym.annotations foreach { a => a match { +// case AnnotationInfo(SerializableAttr, _, _) => +// // TODO: add the Serializable TypeAttribute also if the annotation +// // System.SerializableAttribute is present (.net annotation, 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 MethodAttributes.Public) +// +// if (!sym.isClassConstructor) { +// if (sym.isStaticMember) +// mf = mf | FieldAttributes.Static // coincidentally, same value as for MethodAttributes.Static ... +// else { +// mf = mf | MethodAttributes.Virtual +// if (sym.isFinal && !getType(sym.owner).IsInterface) +// mf = mf | MethodAttributes.Final +// if (sym.isDeferred || getType(sym.owner).IsInterface) +// mf = mf | MethodAttributes.Abstract +// } +// } +// +// if (sym.isStaticMember) { +// mf = mf | MethodAttributes.Static +// } +// +// // constructors of module classes should be private +// if (sym.isPrimaryConstructor && isTopLevelModule(sym.owner)) { +// mf |= MethodAttributes.Private +// mf &= ~(MethodAttributes.Public) +// } +// +// mf.toShort +// } +// +// def msilFieldFlags(sym: Symbol): Short = { +// var mf: Int = +// if (sym hasFlag Flags.PRIVATE) FieldAttributes.Private +// else if (sym hasFlag Flags.PROTECTED) FieldAttributes.FamORAssem +// else FieldAttributes.Public +// +// if (sym hasFlag Flags.FINAL) +// mf = mf | FieldAttributes.InitOnly +// +// if (sym.isStaticMember) +// mf = mf | FieldAttributes.Static +// +// // TRANSIENT: "not serialized", VOLATILE: doesn't exist on .net +// // TODO: add this annotation also if the class has the custom attribute +// // System.NotSerializedAttribute +// sym.annotations.foreach( a => a match { +// case AnnotationInfo(TransientAtt, _, _) => +// mf = mf | FieldAttributes.NotSerialized +// case _ => () +// }) +// +// mf.toShort +// } +// +// ////////////////////// builders, types /////////////////////// +// +// var entryPoint: Symbol = _ +// +// val notInitializedModules = mutable.HashSet[Symbol]() +// +// // 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 = mutable.HashMap[Local, LocalBuilder]() +// +// private[GenMSIL] def findEntryPoint(cls: IClass) { +// +// def isEntryPoint(sym: Symbol):Boolean = { +// if (isStaticModule(sym.owner) && msilName(sym) == "main") +// if (sym.tpe.paramTypes.length == 1) { +// toTypeKind(sym.tpe.paramTypes(0)) match { +// case ARRAY(elem) => +// if (elem.toType.typeSymbol == definitions.StringClass) { +// return true +// } +// case _ => () +// } +// } +// false +// } +// +// if((entryPoint == null) && opt.showClass.isDefined) { // TODO introduce dedicated setting instead +// val entryclass = opt.showClass.get.toString +// val cfn = cls.symbol.fullName +// if(cfn == entryclass) { +// for (m <- cls.methods; if isEntryPoint(m.symbol)) { entryPoint = m.symbol } +// if(entryPoint == null) { warning("Couldn't find main method in class " + cfn) } +// } +// } +// +// if (firstSourceName == "") +// if (cls.symbol.sourceFile != null) // is null for nested classes +// firstSourceName = cls.symbol.sourceFile.name +// } +// +// // ##################################################################### +// // get and create types +// +// private def msilType(t: TypeKind): MsilType = (t: @unchecked) match { +// case UNIT => MVOID +// case BOOL => MBOOL +// case BYTE => MBYTE +// case SHORT => MSHORT +// case CHAR => MCHAR +// case INT => MINT +// case LONG => MLONG +// case FLOAT => MFLOAT +// case DOUBLE => MDOUBLE +// case REFERENCE(cls) => getType(cls) +// case ARRAY(elem) => +// msilType(elem) match { +// // For type builders, cannot call "clrTypes.mkArrayType" because this looks up +// // the type "tp" in the assembly (not in the HashMap "types" of the backend). +// // This can fail for nested types because the builders are not complete yet. +// case tb: TypeBuilder => tb.MakeArrayType() +// case tp: MsilType => clrTypes.mkArrayType(tp) +// } +// } +// +// private def msilType(tpe: Type): MsilType = msilType(toTypeKind(tpe)) +// +// private def msilParamTypes(sym: Symbol): Array[MsilType] = { +// sym.tpe.paramTypes.map(msilType).toArray +// } +// +// def getType(sym: Symbol) = getTypeOpt(sym).getOrElse(abort(showsym(sym))) +// +// /** +// * Get an MSIL type from a symbol. First look in the clrTypes.types map, then +// * lookup the name using clrTypes.getType +// */ +// def getTypeOpt(sym: Symbol): Option[MsilType] = { +// val tmp = types.get(sym) +// tmp match { +// case typ @ Some(_) => typ +// case None => +// def typeString(sym: Symbol): String = { +// val s = if (sym.isNestedClass) typeString(sym.owner) +"+"+ sym.simpleName +// else sym.fullName +// if (sym.isModuleClass && !sym.isTrait) s + "$" else s +// } +// val name = typeString(sym) +// val typ = clrTypes.getType(name) +// if (typ == null) +// None +// else { +// types(sym) = typ +// Some(typ) +// } +// } +// } +// +// def mapType(sym: Symbol, mType: MsilType) { +// assert(mType != null, showsym(sym)) +// types(sym) = mType +// } +// +// def createTypeBuilder(iclass: IClass) { +// /** +// * First look in the clrTypes.types map, if that fails check if it's a class being compiled, otherwise +// * lookup by name (clrTypes.getType calls the static method msil.Type.GetType(fullname)). +// */ +// def msilTypeFromSym(sym: Symbol): MsilType = { +// types.get(sym).getOrElse { +// classes.get(sym) match { +// case Some(iclass) => +// msilTypeBuilderFromSym(sym) +// case None => +// getType(sym) +// } +// } +// } +// +// def msilTypeBuilderFromSym(sym: Symbol): TypeBuilder = { +// if(!(types.contains(sym) && types(sym).isInstanceOf[TypeBuilder])){ +// val iclass = classes(sym) +// assert(iclass != null) +// createTypeBuilder(iclass) +// } +// types(sym).asInstanceOf[TypeBuilder] +// } +// +// val sym = iclass.symbol +// if (types.contains(sym) && types(sym).isInstanceOf[TypeBuilder]) +// return +// +// def isInterface(s: Symbol) = s.isTrait && !s.isImplClass +// val parents: List[Type] = +// if (sym.info.parents.isEmpty) List(definitions.ObjectClass.tpe) +// else sym.info.parents.distinct +// +// val superType : MsilType = if (isInterface(sym)) null else msilTypeFromSym(parents.head.typeSymbol) +// debuglog("super type: " + parents(0).typeSymbol + ", msil type: " + superType) +// +// val interfaces: Array[MsilType] = +// parents.tail.map(p => msilTypeFromSym(p.typeSymbol)).toArray +// if (parents.length > 1) { +// if (settings.debug.value) { +// log("interfaces:") +// for (i <- 0.until(interfaces.length)) { +// log(" type: " + parents(i + 1).typeSymbol + ", msil type: " + interfaces(i)) +// } +// } +// } +// +// val tBuilder = if (sym.isNestedClass) { +// val ownerT = msilTypeBuilderFromSym(sym.owner).asInstanceOf[TypeBuilder] +// ownerT.DefineNestedType(msilName(sym), msilTypeFlags(sym), superType, interfaces) +// } else { +// mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces) +// } +// mapType(sym, tBuilder) +// } // createTypeBuilder +// +// def createClassMembers(iclass: IClass) { +// try { +// createClassMembers0(iclass) +// } +// catch { +// case e: Throwable => +// java.lang.System.err.println(showsym(iclass.symbol)) +// java.lang.System.err.println("with methods = " + iclass.methods) +// throw e +// } +// } +// +// def createClassMembers0(iclass: IClass) { +// +// val mtype = getType(iclass.symbol).asInstanceOf[TypeBuilder] +// +// for (ifield <- iclass.fields) { +// val sym = ifield.symbol +// debuglog("Adding field: " + sym.fullName) +// +// var attributes = msilFieldFlags(sym) +// val fieldTypeWithCustomMods = +// new PECustomMod(msilType(sym.tpe), +// customModifiers(sym.annotations)) +// val fBuilder = mtype.DefineField(msilName(sym), +// fieldTypeWithCustomMods, +// attributes) +// fields(sym) = fBuilder +// addAttributes(fBuilder, sym.annotations) +// } // all iclass.fields iterated over +// +// if (isStaticModule(iclass.symbol)) { +// val sc = iclass.lookupStaticCtor +// if (sc.isDefined) { +// val m = sc.get +// val oldLastBlock = m.lastBlock +// val lastBlock = m.newBlock() +// oldLastBlock.replaceInstruction(oldLastBlock.length - 1, JUMP(lastBlock)) +// // call object's private ctor from static ctor +// lastBlock.emit(CIL_NEWOBJ(iclass.symbol.primaryConstructor)) +// lastBlock.emit(DROP(toTypeKind(iclass.symbol.tpe))) +// lastBlock emit RETURN(UNIT) +// lastBlock.close +// } +// } +// +// if (iclass.symbol != definitions.ArrayClass) { +// for (m: IMethod <- iclass.methods) { +// val sym = m.symbol +// debuglog("Creating MethodBuilder for " + Flags.flagsToString(sym.flags) + " " + +// sym.owner.fullName + "::" + sym.name) +// +// val ownerType = getType(sym.enclClass).asInstanceOf[TypeBuilder] +// assert(mtype == ownerType, "mtype = " + mtype + "; ownerType = " + ownerType) +// var paramTypes = msilParamTypes(sym) +// val attr = msilMethodFlags(sym) +// +// if (m.symbol.isClassConstructor) { +// val constr = +// ownerType.DefineConstructor(attr, CallingConventions.Standard, paramTypes) +// for (i <- 0.until(paramTypes.length)) { +// constr.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) +// } +// mapConstructor(sym, constr) +// addAttributes(constr, sym.annotations) +// } else { +// var resType = msilType(m.returnType) +// val method = +// ownerType.DefineMethod(msilName(sym), attr, resType, paramTypes) +// for (i <- 0.until(paramTypes.length)) { +// method.DefineParameter(i, ParameterAttributes.None, msilName(m.params(i).sym)) +// } +// if (!methods.contains(sym)) +// mapMethod(sym, method) +// addAttributes(method, sym.annotations) +// debuglog("\t created MethodBuilder " + method) +// } +// } +// } // method builders created for non-array iclass +// +// if (isStaticModule(iclass.symbol)) { +// addModuleInstanceField(iclass.symbol) +// notInitializedModules += iclass.symbol +// if (iclass.lookupStaticCtor.isEmpty) { +// addStaticInit(iclass.symbol) +// } +// } +// +// } // createClassMembers0 +// +// private def isTopLevelModule(sym: Symbol): Boolean = +// atPhase (currentRun.refchecksPhase) { +// sym.isModuleClass && !sym.isImplClass && !sym.isNestedClass +// } +// +// // if the module is lifted it does not need to be initialized in +// // its static constructor, and the MODULE$ field is not required. +// // the outer class will care about it. +// private def isStaticModule(sym: Symbol): Boolean = { +// // .net inner classes: removed '!sym.hasFlag(Flags.LIFTED)', added +// // 'sym.isStatic'. -> no longer compatible without skipping flatten! +// sym.isModuleClass && sym.isStatic && !sym.isImplClass +// } +// +// private def isCloneable(sym: Symbol): Boolean = { +// !sym.annotations.forall( a => a match { +// case AnnotationInfo(CloneableAttr, _, _) => false +// case _ => true +// }) +// } +// +// private def addModuleInstanceField(sym: Symbol) { +// debuglog("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 nameInMetadata = nestingAwareFullClassname(moduleClassSym) +// val mClass = clrTypes.getType(nameInMetadata) +// val mfield = mClass.GetField("MODULE$") +// assert(mfield ne null, "module not found " + showsym(moduleClassSym)) +// fields(moduleClassSym) = mfield +// mfield +// } +// +// //fields(moduleClassSym) +// } +// +// def nestingAwareFullClassname(csym: Symbol) : String = { +// val suffix = csym.moduleSuffix +// val res = if (csym.isNestedClass) +// nestingAwareFullClassname(csym.owner) + "+" + csym.encodedName +// else +// csym.fullName +// res + suffix +// } +// +// /** Adds a static initializer which creates an instance of the module +// * class (calls the primary constructor). A special primary constructor +// * will be generated (notInitializedModules) which stores the new instance +// * in the MODULE$ field right after the super call. +// */ +// private def addStaticInit(sym: Symbol) { +// val tBuilder = getType(sym).asInstanceOf[TypeBuilder] +// +// val staticInit = tBuilder.DefineConstructor( +// (MethodAttributes.Static | MethodAttributes.Public).toShort, +// CallingConventions.Standard, +// MsilType.EmptyTypes) +// +// val sicode = staticInit.GetILGenerator() +// +// val instanceConstructor = constructors(sym.primaryConstructor) +// +// // there are no constructor parameters. assuming the constructor takes no parameter +// // is fine: we call (in the static constructor) the constructor of the module class, +// // which takes no arguments - an object definition cannot take constructor arguments. +// 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 generateMirrorClass(sym: Symbol) { +// val tBuilder = getType(sym) +// assert(sym.isModuleClass, "Can't generate Mirror-Class for the Non-Module class " + sym) +// debuglog("Dumping mirror class for object: " + sym) +// val moduleName = msilName(sym) +// val mirrorName = moduleName.substring(0, moduleName.length() - 1) +// val mirrorTypeBuilder = mmodule.DefineType(mirrorName, +// TypeAttributes.Class | +// TypeAttributes.Public | +// TypeAttributes.Sealed, +// MOBJECT, +// MsilType.EmptyTypes) +// +// val iclass = classes(sym) +// +// for (m <- sym.tpe.nonPrivateMembers +// if m.owner != definitions.ObjectClass && !m.isProtected && +// m.isMethod && !m.isClassConstructor && !m.isStaticMember && !m.isCase && +// !m.isDeferred) +// { +// debuglog(" Mirroring method: " + m) +// val paramTypes = msilParamTypes(m) +// val paramNames: Array[String] = new Array[String](paramTypes.length) +// for (i <- 0 until paramTypes.length) +// paramNames(i) = "x_" + i +// +// // CHECK: verify if getMethodName is better than msilName +// val mirrorMethod = mirrorTypeBuilder.DefineMethod(msilName(m), +// (MethodAttributes.Public | +// MethodAttributes.Static).toShort, +// msilType(m.tpe.resultType), +// paramTypes) +// +// var i = 0 +// while (i < paramTypes.length) { +// mirrorMethod.DefineParameter(i, ParameterAttributes.None, paramNames(i)) +// i += 1 +// } +// +// val mirrorCode = mirrorMethod.GetILGenerator() +// mirrorCode.Emit(OpCodes.Ldsfld, getModuleInstanceField(sym)) +// val mInfo = getMethod(m) +// for (paramidx <- 0.until(paramTypes.length)) { +// val mInfoParams = mInfo.GetParameters +// val loadAddr = mInfoParams(paramidx).ParameterType.IsByRef +// loadArg(mirrorCode, loadAddr)(paramidx) +// } +// +// mirrorCode.Emit(OpCodes.Callvirt, getMethod(m)) +// mirrorCode.Emit(OpCodes.Ret) +// } +// +// addSymtabAttribute(sym.sourceModule, mirrorTypeBuilder) +// +// mirrorTypeBuilder.CreateType() +// mirrorTypeBuilder.setSourceFilepath(iclass.cunit.source.file.path) +// } +// +// +// // ##################################################################### +// // 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).toShort) +// mcode.Emit(OpCodes.Stsfld, anonfunField) +// +// +// // create the static caller method and the delegate object +// val (params, returnType) = delegateType.member(nme.apply).tpe match { +// case MethodType(delParams, delReturn) => (delParams, delReturn) +// case _ => abort("not a delegate type: " + delegateType) +// } +// val caller: MethodBuilder = delegateCallers.DefineMethod( +// "$delegateCaller$$" + nbDelegateCallers, +// (MethodAttributes.Final | MethodAttributes.Public | MethodAttributes.Static).toShort, +// msilType(returnType), (params map (_.tpe)).map(msilType).toArray) +// for (i <- 0 until params.length) +// caller.DefineParameter(i, ParameterAttributes.None, "arg" + i) // FIXME: use name of parameter symbol +// 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 (i <- 0 until params.length) { +// loadArg(dcode, false /* TODO confirm whether passing actual as-is to formal is correct wrt the ByRef attribute of the param */)(i) +// emitBox(dcode, toTypeKind(params(i).tpe)) +// } +// 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: @unchecked) 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 clrTypes.isValueType(cls) => +// code.Emit(OpCodes.Box, (msilType(boxType))) +// case REFERENCE(_) | ARRAY(_) => +// warning("Tried to BOX a non-valuetype.") +// () +// } +// +// def emitUnbox(code: ILGenerator, boxType: TypeKind) = (boxType: @unchecked) match { +// case UNIT => code.Emit(OpCodes.Pop) +// /* (1) it's essential to keep the code emitted here (as of now plain calls to System.Convert.ToBlaBla methods) +// behaviorally.equiv.wrt. BoxesRunTime.unboxToBlaBla methods +// (case null: that's easy, case boxed: track changes to unboxBlaBla) +// (2) See also: asInstanceOf to cast from Any to number, +// tracked in http://lampsvn.epfl.ch/trac/scala/ticket/4437 */ +// case BOOL => code.Emit(OpCodes.Call, toBool) +// case BYTE => code.Emit(OpCodes.Call, toSByte) +// case SHORT => code.Emit(OpCodes.Call, toShort) +// case CHAR => code.Emit(OpCodes.Call, toChar) +// case INT => code.Emit(OpCodes.Call, toInt) +// case LONG => code.Emit(OpCodes.Call, toLong) +// case FLOAT => code.Emit(OpCodes.Call, toFloat) +// case DOUBLE => code.Emit(OpCodes.Call, toDouble) +// case REFERENCE(cls) if clrTypes.isValueType(cls) => +// code.Emit(OpCodes.Unbox, msilType(boxType)) +// code.Emit(OpCodes.Ldobj, msilType(boxType)) +// case REFERENCE(_) | ARRAY(_) => +// warning("Tried to UNBOX a non-valuetype.") +// () +// } +// +// // ##################################################################### +// // 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) { +// java.lang.System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name) +// java.lang.System.out.println("scope = " + sym.owner.tpe.decls) +// abort(sym.fullName) +// } +// else { +// mapConstructor(sym, constr) +// constr +// } +// } +// +// def mapConstructor(sym: Symbol, cInfo: ConstructorInfo) = { +// constructors(sym) = cInfo +// } +// +// private def getMethod(sym: Symbol): MethodInfo = { +// +// methods.get(sym) match { +// case Some(method) => method +// case None => +// val mClass = getType(sym.owner) +// try { +// val method = mClass.GetMethod(msilName(sym), msilParamTypes(sym), +// msilType(sym.tpe.resultType)) +// if (method eq null) { +// java.lang.System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym)) +// java.lang.System.out.println("scope = " + sym.owner.tpe.decls) +// abort(sym.fullName) +// } +// else { +// mapMethod(sym, method) +// method +// } +// } +// catch { +// case e: Exception => +// Console.println("While looking up " + mClass + "::" + sym.nameString) +// Console.println("\t" + showsym(sym)) +// throw e +// } +// } +// } +// +// /* +// * add a mapping between sym and mInfo +// */ +// private def mapMethod(sym: Symbol, mInfo: MethodInfo) { +// 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]) { +// 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]) { +// val methodSym = lookupMethod(clazz, name, paramTypes) +// assert(methodSym != null, "cannot find method " + name + "(" + +// paramTypes + ")" + " in class " + clazz) +// mapMethod(methodSym, newClass, newName, newParamTypes) +// } +// +// /* +// * add mapping 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) { +// 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) { +// 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(params, 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 += 1 +// } +// } else { +// typesOK = false +// } +// typesOK +// }) match { +// case Some(sym) => sym +// case None => abort("member of " + clazz + ", " + name + "(" + +// paramTypes + ") not found") +// } +// +// case MethodType(_, _) => memberSym +// +// case _ => abort("member not found: " + name + " of " + clazz) +// } +// } +// +// private def showsym(sym: Symbol): String = (sym.toString + +// "\n symbol = " + Flags.flagsToString(sym.flags) + " " + sym + +// "\n owner = " + Flags.flagsToString(sym.owner.flags) + " " + sym.owner +// ) +// +// } // class BytecodeGenerator +// +// } // class GenMSIL diff --git a/src/compiler/scala/tools/nsc/io/MsilFile.scala b/src/compiler/scala/tools/nsc/io/MsilFile.scala index d970d0e9c9..2982812fdd 100644 --- a/src/compiler/scala/tools/nsc/io/MsilFile.scala +++ b/src/compiler/scala/tools/nsc/io/MsilFile.scala @@ -1,18 +1,18 @@ -/* NSC -- new Scala compiler - * Copyright 2005-2011 LAMP/EPFL - * @author Paul Phillips - */ - -package scala.tools.nsc -package io - -import ch.epfl.lamp.compiler.msil.{ Type => MsilType, _ } - -/** This class wraps an MsilType. It exists only so - * ClassPath can treat all of JVM/MSIL/bin/src files - * uniformly, as AbstractFiles. - */ -class MsilFile(val msilType: MsilType) extends VirtualFile(msilType.FullName, msilType.Namespace) { -} - -object NoMsilFile extends MsilFile(null) { } +// /* NSC -- new Scala compiler +// * Copyright 2005-2011 LAMP/EPFL +// * @author Paul Phillips +// */ +// +// package scala.tools.nsc +// package io +// +// import ch.epfl.lamp.compiler.msil.{ Type => MsilType, _ } +// +// /** This class wraps an MsilType. It exists only so +// * ClassPath can treat all of JVM/MSIL/bin/src files +// * uniformly, as AbstractFiles. +// */ +// class MsilFile(val msilType: MsilType) extends VirtualFile(msilType.FullName, msilType.Namespace) { +// } +// +// object NoMsilFile extends MsilFile(null) { } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala index 942ec1fa86..2241bb224e 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala @@ -13,7 +13,7 @@ import classfile.ClassfileParser import reflect.internal.Flags._ import reflect.internal.MissingRequirementError import util.Statistics._ -import scala.tools.nsc.io.{ AbstractFile, MsilFile } +import scala.tools.nsc.io.{ AbstractFile } //, MsilFile } /** This class ... * @@ -236,16 +236,16 @@ abstract class SymbolLoaders { } override def sourcefile: Option[AbstractFile] = classfileParser.srcfile } - - class MsilFileLoader(msilFile: MsilFile) extends SymbolLoader { - private def typ = msilFile.msilType - private object typeParser extends clr.TypeParser { - val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global - } - - protected def description = "MsilFile "+ typ.FullName + ", assembly "+ typ.Assembly.FullName - protected def doComplete(root: Symbol) { typeParser.parse(typ, root) } - } + // + // class MsilFileLoader(msilFile: MsilFile) extends SymbolLoader { + // private def typ = msilFile.msilType + // private object typeParser extends clr.TypeParser { + // val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global + // } + // + // protected def description = "MsilFile "+ typ.FullName + ", assembly "+ typ.Assembly.FullName + // protected def doComplete(root: Symbol) { typeParser.parse(typ, root) } + // } class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader { protected def description = "source file "+ srcfile.toString @@ -258,11 +258,11 @@ abstract class SymbolLoaders { protected def description = "module class loader" protected def doComplete(root: Symbol) { root.sourceModule.initialize } } - - object clrTypes extends clr.CLRTypes { - val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global - if (global.forMSIL) init() - } + // + // object clrTypes extends clr.CLRTypes { + // val global: SymbolLoaders.this.global.type = SymbolLoaders.this.global + // if (global.forMSIL) init() + // } /** used from classfile parser to avoid cyclies */ var parentsLevel = 0 diff --git a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala b/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala index 7be0fcb146..4346cb6f8d 100644 --- a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala +++ b/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala @@ -1,137 +1,137 @@ -/* NSC -- new scala compiler - * Copyright 2004-2011 LAMP/EPFL - */ - - -package scala.tools.nsc -package symtab -package clr - -import java.io.File -import java.util.{Comparator, StringTokenizer} -import scala.util.Sorting -import ch.epfl.lamp.compiler.msil._ -import scala.collection.{ mutable, immutable } -import scala.tools.nsc.util.{Position, NoPosition} - -/** - * 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: mutable.Map[Symbol,Type] = new mutable.HashMap - val constructors: mutable.Map[Symbol,ConstructorInfo] = new mutable.HashMap - val methods: mutable.Map[Symbol,MethodInfo] = new mutable.HashMap - val fields: mutable.Map[Symbol, FieldInfo] = new mutable.HashMap - val sym2type: mutable.Map[Type,Symbol] = new mutable.HashMap - val addressOfViews = new mutable.HashSet[Symbol] - val mdgptrcls4clssym: mutable.Map[ /*cls*/ Symbol, /*cls*/ Symbol] = new mutable.HashMap - - def isAddressOf(msym : Symbol) = addressOfViews.contains(msym) - - def isNonEnumValuetype(cls: Symbol) = { - val msilTOpt = types.get(cls) - val res = msilTOpt.isDefined && { - val msilT = msilTOpt.get - msilT.IsValueType && !msilT.IsEnum - } - res - } - - def isValueType(cls: Symbol): Boolean = { - val opt = types.get(cls) - opt.isDefined && opt.get.IsValueType - } - - def init() = try { // initialize - // the MsilClasspath (nsc/util/Classpath.scala) initializes the msil-library by calling - // Assembly.LoadFrom("mscorlib.dll"), so this type should be found - Type.initMSCORLIB(getTypeSafe("System.String").Assembly) - - 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) - - 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) - } - catch { - case e: RuntimeException => - Console.println(e.getMessage) - throw e - } - - //########################################################################## - // type mapping and lookup - - 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 } -} // CLRTypes +// /* NSC -- new scala compiler +// * Copyright 2004-2011 LAMP/EPFL +// */ +// +// +// package scala.tools.nsc +// package symtab +// package clr +// +// import java.io.File +// import java.util.{Comparator, StringTokenizer} +// import scala.util.Sorting +// import ch.epfl.lamp.compiler.msil._ +// import scala.collection.{ mutable, immutable } +// import scala.tools.nsc.util.{Position, NoPosition} +// +// /** +// * 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: mutable.Map[Symbol,Type] = new mutable.HashMap +// val constructors: mutable.Map[Symbol,ConstructorInfo] = new mutable.HashMap +// val methods: mutable.Map[Symbol,MethodInfo] = new mutable.HashMap +// val fields: mutable.Map[Symbol, FieldInfo] = new mutable.HashMap +// val sym2type: mutable.Map[Type,Symbol] = new mutable.HashMap +// val addressOfViews = new mutable.HashSet[Symbol] +// val mdgptrcls4clssym: mutable.Map[ /*cls*/ Symbol, /*cls*/ Symbol] = new mutable.HashMap +// +// def isAddressOf(msym : Symbol) = addressOfViews.contains(msym) +// +// def isNonEnumValuetype(cls: Symbol) = { +// val msilTOpt = types.get(cls) +// val res = msilTOpt.isDefined && { +// val msilT = msilTOpt.get +// msilT.IsValueType && !msilT.IsEnum +// } +// res +// } +// +// def isValueType(cls: Symbol): Boolean = { +// val opt = types.get(cls) +// opt.isDefined && opt.get.IsValueType +// } +// +// def init() = try { // initialize +// // the MsilClasspath (nsc/util/Classpath.scala) initializes the msil-library by calling +// // Assembly.LoadFrom("mscorlib.dll"), so this type should be found +// Type.initMSCORLIB(getTypeSafe("System.String").Assembly) +// +// 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) +// +// 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) +// } +// catch { +// case e: RuntimeException => +// Console.println(e.getMessage) +// throw e +// } +// +// //########################################################################## +// // type mapping and lookup +// +// 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 } +// } // CLRTypes diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala index 736d1c78a3..82d1dd6bc3 100644 --- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala @@ -1,851 +1,851 @@ -/* NSC -- new scala compiler - * Copyright 2004-2011 LAMP/EPFL - */ - -package scala.tools.nsc -package symtab -package clr - -import java.io.IOException -import io.MsilFile -import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute, _} -import scala.collection.{ mutable, immutable } -import scala.reflect.internal.pickling.UnPickler -import ch.epfl.lamp.compiler.msil.Type.TMVarUsage - -/** - * @author Nikolay Mihaylov - */ -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 implicit def stringToTermName(s: String): TermName = newTermName(s) - - private object unpickler extends UnPickler { - val global: TypeParser.this.global.type = TypeParser.this.global - } - - def parse(typ: MSILType, root: Symbol) { - - def handleError(e: Throwable) = { - 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.companionClass - this.staticModule = root - } else { - this.clazz = root - this.staticModule = root.companionModule - } - try { - parseClass(typ) - } catch { - case e: FatalError => handleError(e) - case e: RuntimeException => handleError(e) - } - busy = false - } - - class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType { - override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") } - } - - /* the names `classTParams` and `newTParams` stem from the forJVM version (ClassfileParser.sigToType()) - * but there are differences that should be kept in mind. - * forMSIL, a nested class knows nothing about any type-params in the nesting class, - * therefore newTParams is redundant (other than for recording lexical order), - * it always contains the same elements as classTParams.value */ - val classTParams = scala.collection.mutable.Map[Int,Symbol]() // TODO should this be a stack? (i.e., is it possible for >1 invocation to getCLRType on the same TypeParser instance be active ) - val newTParams = new scala.collection.mutable.ListBuffer[Symbol]() - val methodTParams = scala.collection.mutable.Map[Int,Symbol]() - - private def sig2typeBounds(tvarCILDef: GenericParamAndConstraints): Type = { - val ts = new scala.collection.mutable.ListBuffer[Type] - for (cnstrnt <- tvarCILDef.Constraints) { - ts += getCLRType(cnstrnt) // TODO we're definitely not at or after erasure, no need to call objToAny, right? - } - TypeBounds.upper(intersectionType(ts.toList, clazz)) - // TODO variance??? - } - - private def createViewFromTo(viewSuffix : String, fromTpe : Type, toTpe : Type, - addToboxMethodMap : Boolean, isAddressOf : Boolean) : Symbol = { - val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead? - val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(List(fromTpe)), toTpe) - val vmsym = createMethod(nme.view_ + viewSuffix, flags, viewMethodType, null, true); - // !!! this used to mutate a mutable map in definitions, but that map became - // immutable and this kept "working" with a no-op. So now it's commented out - // since I retired the deprecated code which allowed for that bug. - // - // if (addToboxMethodMap) definitions.boxMethod(clazz) = vmsym - - if (isAddressOf) clrTypes.addressOfViews += vmsym - vmsym - } - - private def createDefaultConstructor(typ: MSILType) { - val attrs = MethodAttributes.Public | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName // TODO instance - val declType= typ - val method = new ConstructorInfo(declType, attrs, Array[MSILType]()) - val flags = Flags.JAVA - val owner = clazz - val methodSym = owner.newMethod(NoPosition, nme.CONSTRUCTOR).setFlag(flags) - val rettype = clazz.tpe - val mtype = methodType(Array[MSILType](), rettype); - val mInfo = mtype(methodSym) - methodSym.setInfo(mInfo) - instanceDefs.enter(methodSym); - clrTypes.constructors(methodSym) = method - } - - private def parseClass(typ: MSILType) { - - { - val t4c = clrTypes.types.get(clazz) - assert(t4c == None || t4c == Some(typ)) - } - clrTypes.types(clazz) = typ - - { - val c4t = clrTypes.sym2type.get(typ) - assert(c4t == None || c4t == Some(clazz)) - } - 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) - - var clazzBoxed : Symbol = NoSymbol - var clazzMgdPtr : Symbol = NoSymbol - - val canBeTakenAddressOf = (typ.IsValueType || typ.IsEnum) && (typ.FullName != "System.Enum") - - if(canBeTakenAddressOf) { - clazzBoxed = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("Boxed")) - clazzMgdPtr = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("MgdPtr")) - clrTypes.mdgptrcls4clssym(clazz) = clazzMgdPtr - /* adding typMgdPtr to clrTypes.sym2type should happen early (before metadata for supertypes is parsed, - before metadata for members are parsed) so that clazzMgdPtr can be found by getClRType. */ - val typMgdPtr = MSILType.mkByRef(typ) - clrTypes.types(clazzMgdPtr) = typMgdPtr - clrTypes.sym2type(typMgdPtr) = clazzMgdPtr - /* clazzMgdPtr but not clazzBoxed is mapped by clrTypes.types into an msil.Type instance, - because there's no metadata-level representation for a "boxed valuetype" */ - val instanceDefsMgdPtr = newScope - val classInfoMgdPtr = ClassInfoType(definitions.anyvalparam, instanceDefsMgdPtr, clazzMgdPtr) - clazzMgdPtr.setFlag(flags) - clazzMgdPtr.setInfo(classInfoMgdPtr) - } - -/* START CLR generics (snippet 1) */ - // first pass - for (tvarCILDef <- typ.getSortedTVars() ) { - val tpname = newTypeName(tvarCILDef.Name.replaceAll("!", "")) // TODO are really all type-params named in all assemblies out there? (NO) - val tpsym = clazz.newTypeParameter(tpname) - classTParams.put(tvarCILDef.Number, tpsym) - newTParams += tpsym - // TODO wouldn't the following also be needed later, i.e. during getCLRType - tpsym.setInfo(definitions.AnyClass.tpe) - } - // second pass - for (tvarCILDef <- typ.getSortedTVars() ) { - val tpsym = classTParams(tvarCILDef.Number) - tpsym.setInfo(sig2typeBounds(tvarCILDef)) // we never skip bounds unlike in forJVM - } -/* END CLR generics (snippet 1) */ - val ownTypeParams = newTParams.toList -/* START CLR generics (snippet 2) */ - if (!ownTypeParams.isEmpty) { - clazz.setInfo(new TypeParamsType(ownTypeParams)) - if(typ.IsValueType && !typ.IsEnum) { - clazzBoxed.setInfo(new TypeParamsType(ownTypeParams)) - } - } -/* END CLR generics (snippet 2) */ - instanceDefs = newScope - staticDefs = newScope - - val classInfoAsInMetadata = { - 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 branch activates for System.Object only. - // parents (i.e., base type and interfaces) - val parents = new scala.collection.mutable.ListBuffer[Type]() - parents += superType - for (iface <- ifaces) { - parents += getCLRType(iface) // here the variance doesn't matter - } - // methods, properties, events, fields are entered in a moment - if (canBeTakenAddressOf) { - val instanceDefsBoxed = newScope - ClassInfoType(parents.toList, instanceDefsBoxed, clazzBoxed) - } else - ClassInfoType(parents.toList, instanceDefs, clazz) - } - - val staticInfo = ClassInfoType(List(), staticDefs, statics) - - clazz.setFlag(flags) - - if (canBeTakenAddressOf) { - clazzBoxed.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata - else GenPolyType(ownTypeParams, classInfoAsInMetadata) ) - clazzBoxed.setFlag(flags) - val rawValueInfoType = ClassInfoType(definitions.anyvalparam, instanceDefs, clazz) - clazz.setInfo( if (ownTypeParams.isEmpty) rawValueInfoType - else GenPolyType(ownTypeParams, rawValueInfoType) ) - } else { - clazz.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata - else GenPolyType(ownTypeParams, classInfoAsInMetadata) ) - } - - // TODO I don't remember if statics.setInfo and staticModule.setInfo should also know about type params - statics.setFlag(Flags.JAVA) - statics.setInfo(staticInfo) - staticModule.setFlag(Flags.JAVA) - staticModule.setInfo(statics.tpe) - - - if (canBeTakenAddressOf) { - // implicit conversions are owned by staticModule.moduleClass - createViewFromTo("2Boxed", clazz.tpe, clazzBoxed.tpe, addToboxMethodMap = true, isAddressOf = false) - // createViewFromTo("2Object", clazz.tpe, definitions.ObjectClass.tpe, addToboxMethodMap = true, isAddressOf = false) - createViewFromTo("2MgdPtr", clazz.tpe, clazzMgdPtr.tpe, addToboxMethodMap = false, isAddressOf = true) - // a return can't have type managed-pointer, thus a dereference-conversion is not needed - // similarly, a method can't declare as return type "boxed valuetype" - if (!typ.IsEnum) { - // a synthetic default constructor for raw-type allows `new X' syntax - createDefaultConstructor(typ) - } - } - - // import nested types - for (ntype <- typ.getNestedTypes() if !(ntype.IsNestedPrivate || ntype.IsNestedAssembly || ntype.IsNestedFamANDAssem) - || ntype.IsInterface /* TODO why shouldn't nested ifaces be type-parsed too? */ ) - { - val loader = new loaders.MsilFileLoader(new MsilFile(ntype)) - val nclazz = statics.newClass(ntype.Name.toTypeName) - val nmodule = statics.newModule(ntype.Name) - nclazz.setInfo(loader) - nmodule.setInfo(loader) - staticDefs.enter(nclazz) - staticDefs.enter(nmodule) - - assert(nclazz.companionModule == nmodule, nmodule) - assert(nmodule.companionClass == nclazz, nclazz) - } - - val fields = typ.getFields() - for (field <- fields - if !(field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly) - if (getCLRType(field.FieldType) != null) - ) { - assert (!field.FieldType.IsPointer && !field.FieldType.IsByRef, "CLR requirement") - val flags = translateAttributes(field); - val name = newTermName(field.Name); - val fieldType = - if (field.IsLiteral && !field.FieldType.IsEnum && isDefinedAtgetConstant(getCLRType(field.FieldType))) - ConstantType(getConstant(getCLRType(field.FieldType), field.getValue)) - else - getCLRType(field.FieldType) - val owner = if (field.IsStatic()) statics else clazz; - val sym = owner.newValue(NoPosition, 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 (constr <- typ.getConstructors() if !constr.IsStatic() && !constr.IsPrivate() && - !constr.IsAssembly() && !constr.IsFamilyAndAssembly() && !constr.HasPtrParamOrRetType()) - createMethod(constr); - - // initially also contains getters and setters of properties. - val methodsSet = new mutable.HashSet[MethodInfo](); - methodsSet ++= typ.getMethods(); - - for (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 || getter.HasPtrParamOrRetType)) - { - 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 owner: Symbol = if (getter.IsStatic) statics else clazz; - val methodSym = owner.newMethod(NoPosition, name).setFlag(flags) - val mtype: Type = if (gparamsLength == 0) NullaryMethodType(propType) // .NET properties can't be polymorphic - else methodType(getter, getter.ReturnType)(methodSym) - methodSym.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 || setter.HasPtrParamOrRetType)) - { - 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 = methodType(setter, definitions.UnitClass.tpe); - val owner: Symbol = if (setter.IsStatic) statics else clazz; - val methodSym = owner.newMethod(NoPosition, name).setFlag(flags) - methodSym.setInfo(mtype(methodSym)) - methodSym.setFlag(Flags.ACCESSOR); - (if (setter.IsStatic) staticDefs else instanceDefs).enter(methodSym); - clrTypes.methods(methodSym) = setter; - methodsSet -= setter; - } - } - } - -/* for (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; - } - } */ - -/* Adds view amounting to syntax sugar for a CLR implicit overload. - The long-form syntax can also be supported if "methodsSet -= method" (last statement) is removed. - - /* remember, there's typ.getMethods and type.GetMethods */ - for (method <- typ.getMethods) - if(!method.HasPtrParamOrRetType && - method.IsPublic && method.IsStatic && method.IsSpecialName && - method.Name == "op_Implicit") { - // create a view: typ => method's return type - val viewRetType: Type = getCLRType(method.ReturnType) - val viewParamTypes: List[Type] = method.GetParameters().map(_.ParameterType).map(getCLSType).toList; - /* The spec says "The operator method shall be defined as a static method on either the operand or return type." - * We don't consider the declaring type for the purposes of definitions.functionType, - * instead we regard op_Implicit's argument type and return type as defining the view's signature. - */ - if (viewRetType != null && !viewParamTypes.contains(null)) { - /* The check above applies e.g. to System.Decimal that has a conversion from UInt16, a non-CLS type, whose CLS-mapping returns null */ - val funType: Type = definitions.functionType(viewParamTypes, viewRetType); - val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead? - val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(viewParamTypes), funType) - val vmsym = createMethod(nme.view_, flags, viewMethodType, method, true); - methodsSet -= method; - } - } -*/ - - for (method <- methodsSet.iterator) - if (!method.IsPrivate() && !method.IsAssembly() && !method.IsFamilyAndAssembly() - && !method.HasPtrParamOrRetType) - createMethod(method); - - // Create methods and views for delegate support - if (clrTypes.isDelegateType(typ)) { - createDelegateView(typ) - createDelegateChainers(typ) - } - - // for enumerations introduce comparison and bitwise logical operations; - // the backend will recognize them 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 (cmpName <- ENUM_CMP_NAMES) { - val enumCmp = clazz.newMethod(NoPosition, cmpName) - val enumCmpType = JavaMethodType(enumCmp.newSyntheticValueParams(List(clazz.tpe)), definitions.BooleanClass.tpe) - enumCmp.setFlag(flags).setInfo(enumCmpType) - instanceDefs.enter(enumCmp) - } - - for (bitLogName <- ENUM_BIT_LOG_NAMES) { - val enumBitLog = clazz.newMethod(NoPosition, bitLogName) - val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), clazz.tpe /* was classInfo, infinite typer */) - enumBitLog.setFlag(flags).setInfo(enumBitLogType) - instanceDefs.enter(enumBitLog) - } - } - - } // parseClass - - private def populateMethodTParams(method: MethodBase, methodSym: MethodSymbol) : List[Symbol] = { - if(!method.IsGeneric) Nil - else { - methodTParams.clear - val newMethodTParams = new scala.collection.mutable.ListBuffer[Symbol]() - - // first pass - for (mvarCILDef <- method.getSortedMVars() ) { - val mtpname = newTypeName(mvarCILDef.Name.replaceAll("!", "")) // TODO are really all method-level-type-params named in all assemblies out there? (NO) - val mtpsym = methodSym.newTypeParameter(mtpname) - methodTParams.put(mvarCILDef.Number, mtpsym) - newMethodTParams += mtpsym - // TODO wouldn't the following also be needed later, i.e. during getCLRType - mtpsym.setInfo(definitions.AnyClass.tpe) - } - // second pass - for (mvarCILDef <- method.getSortedMVars() ) { - val mtpsym = methodTParams(mvarCILDef.Number) - mtpsym.setInfo(sig2typeBounds(mvarCILDef)) // we never skip bounds unlike in forJVM - } - - newMethodTParams.toList - } - } - - private def createMethod(method: MethodBase) { - - val flags = translateAttributes(method); - val owner = if (method.IsStatic()) statics else clazz; - val methodSym = owner.newMethod(NoPosition, getName(method)).setFlag(flags) - /* START CLR generics (snippet 3) */ - val newMethodTParams = populateMethodTParams(method, methodSym) - /* END CLR generics (snippet 3) */ - - 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; -/* START CLR generics (snippet 4) */ - val mInfo = if (method.IsGeneric) GenPolyType(newMethodTParams, mtype(methodSym)) - else mtype(methodSym) -/* END CLR generics (snippet 4) */ -/* START CLR non-generics (snippet 4) - val mInfo = mtype(methodSym) - END CLR non-generics (snippet 4) */ - methodSym.setInfo(mInfo) - (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 = methodType(args, getCLSType(retType)) - assert(mtype != null) - createMethod(name, flags, mtype, method, statik) - } - - private def createMethod(name: Name, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = { - val methodSym: Symbol = (if (statik) statics else clazz).newMethod(NoPosition, name) - methodSym.setFlag(flags).setInfo(mtype(methodSym)) - (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 = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(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 = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(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 = { - - def operatorOverload(name : String, paramsArity : Int) : Option[Name] = paramsArity match { - case 1 => name match { - // PartitionI.10.3.1 - case "op_Decrement" => Some(encode("--")) - case "op_Increment" => Some(encode("++")) - case "op_UnaryNegation" => Some(nme.UNARY_-) - case "op_UnaryPlus" => Some(nme.UNARY_+) - case "op_LogicalNot" => Some(nme.UNARY_!) - case "op_OnesComplement" => Some(nme.UNARY_~) - /* op_True and op_False have no operator symbol assigned, - Other methods that will have to be written in full are: - op_AddressOf & (unary) - op_PointerDereference * (unary) */ - case _ => None - } - case 2 => name match { - // PartitionI.10.3.2 - case "op_Addition" => Some(nme.ADD) - case "op_Subtraction" => Some(nme.SUB) - case "op_Multiply" => Some(nme.MUL) - case "op_Division" => Some(nme.DIV) - case "op_Modulus" => Some(nme.MOD) - case "op_ExclusiveOr" => Some(nme.XOR) - case "op_BitwiseAnd" => Some(nme.AND) - case "op_BitwiseOr" => Some(nme.OR) - case "op_LogicalAnd" => Some(nme.ZAND) - case "op_LogicalOr" => Some(nme.ZOR) - case "op_LeftShift" => Some(nme.LSL) - case "op_RightShift" => Some(nme.ASR) - case "op_Equality" => Some(nme.EQ) - case "op_GreaterThan" => Some(nme.GT) - case "op_LessThan" => Some(nme.LT) - case "op_Inequality" => Some(nme.NE) - case "op_GreaterThanOrEqual" => Some(nme.GE) - case "op_LessThanOrEqual" => Some(nme.LE) - - /* op_MemberSelection is reserved in Scala */ - - /* The standard does not assign operator symbols to op_Assign , op_SignedRightShift , op_UnsignedRightShift , - * and op_UnsignedRightShiftAssignment so those names will be used instead to invoke those methods. */ - - /* - The remaining binary operators are not overloaded in C# and are therefore not in widespread use. They have to be written in full. - - op_RightShiftAssignment >>= - op_MultiplicationAssignment *= - op_PointerToMemberSelection ->* - op_SubtractionAssignment -= - op_ExclusiveOrAssignment ^= - op_LeftShiftAssignment <<= - op_ModulusAssignment %= - op_AdditionAssignment += - op_BitwiseAndAssignment &= - op_BitwiseOrAssignment |= - op_Comma , - op_DivisionAssignment /= - */ - case _ => None - } - case _ => None - } - - if (method.IsConstructor()) return nme.CONSTRUCTOR; - val name = method.Name; - if (method.IsStatic()) { - if(method.IsSpecialName) { - val paramsArity = method.GetParameters().size - // handle operator overload, otherwise handle as any static method - val operName = operatorOverload(name, paramsArity) - if (operName.isDefined) { return operName.get; } - } - 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): Symbol => 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): Symbol => 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): Symbol => Type = { - def paramType(typ: MSILType): Type = - if (typ eq clrTypes.OBJECT) definitions.AnyClass.tpe // TODO a hack to compile scalalib, should be definitions.AnyRefClass.tpe - else getCLSType(typ); - val ptypes = argtypes.map(paramType).toList; - if (ptypes.contains(null)) null - else method => JavaMethodType(method.newSyntheticValueParams(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 = { // getCLS returns non-null for types GenMSIL can handle, be they CLS-compliant or not - if (typ.IsTMVarUsage()) - /* START CLR generics (snippet 5) */ - getCLRType(typ) - /* END CLR generics (snippet 5) */ - /* START CLR non-generics (snippet 5) - null - END CLR non-generics (snippet 5) */ - else if ( /* TODO hack if UBYE, uncommented, "ambiguous reference to overloaded definition" ensues, for example for System.Math.Max(x, y) */ - typ == clrTypes.USHORT || typ == clrTypes.UINT || typ == clrTypes.ULONG - /* || typ == clrTypes.UBYTE */ - || typ.IsNotPublic() || typ.IsNestedPrivate() - || typ.IsNestedAssembly() || typ.IsNestedFamANDAssem() - || typ.IsPointer() - || (typ.IsArray() && getCLRType(typ.GetElementType()) == null) /* TODO hack: getCLR instead of getCLS */ - || (typ.IsByRef() && !typ.GetElementType().CanBeTakenAddressOf())) - null - else - getCLRType(typ) - } - - private def getCLRTypeIfPrimitiveNullOtherwise(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)) // TODO U... is a hack to compile scalalib - definitions.ByteClass.tpe - else if ((typ == clrTypes.SHORT) || (typ == clrTypes.SHORT)) // TODO U... is a hack to compile scalalib - definitions.ShortClass.tpe - else if ((typ == clrTypes.INT) || (typ == clrTypes.UINT)) // TODO U... is a hack to compile scalalib - definitions.IntClass.tpe - else if ((typ == clrTypes.LONG) || (typ == clrTypes.LONG)) // TODO U... is a hack to compile scalalib - definitions.LongClass.tpe - else if (typ == clrTypes.FLOAT) - definitions.FloatClass.tpe - else if (typ == clrTypes.DOUBLE) - definitions.DoubleClass.tpe - else null - - - private def getCLRType(tMSIL: MSILType): Type = { - var res = getCLRTypeIfPrimitiveNullOtherwise(tMSIL) - if (res != null) res - else if (tMSIL.isInstanceOf[ConstructedType]) { - val ct = tMSIL.asInstanceOf[ConstructedType] - /* START CLR generics (snippet 6) */ - val cttpArgs = ct.typeArgs.map(tmsil => getCLRType(tmsil)).toList - appliedType(getCLRType(ct.instantiatedType), cttpArgs) - /* END CLR generics (snippet 6) */ - /* START CLR non-generics (snippet 6) - getCLRType(ct.instantiatedType) - END CLR non-generics (snippet 6) */ - } else if (tMSIL.isInstanceOf[TMVarUsage]) { - /* START CLR generics (snippet 7) */ - val tVarUsage = tMSIL.asInstanceOf[TMVarUsage] - val tVarNumber = tVarUsage.Number - if (tVarUsage.isTVar) classTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst - else methodTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst - /* END CLR generics (snippet 7) */ - /* START CLR non-generics (snippet 7) - null // definitions.ObjectClass.tpe - END CLR non-generics (snippet 7) */ - } else if (tMSIL.IsArray()) { - var elemtp = getCLRType(tMSIL.GetElementType()) - // cut&pasted from ClassfileParser - // make unbounded Array[T] where T is a type variable into Array[T with Object] - // (this is necessary because such arrays have a representation which is incompatible - // with arrays of primitive types). - // TODO does that incompatibility also apply to .NET? - if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< definitions.ObjectClass.tpe)) - elemtp = intersectionType(List(elemtp, definitions.ObjectClass.tpe)) - appliedType(definitions.ArrayClass.tpe, List(elemtp)) - } else { - res = clrTypes.sym2type.get(tMSIL) match { - case Some(sym) => sym.tpe - case None => if (tMSIL.IsByRef && tMSIL.GetElementType.IsValueType) { - val addressed = getCLRType(tMSIL.GetElementType) - val clasym = addressed.typeSymbolDirect // TODO should be .typeSymbol? - clasym.info.load(clasym) - val secondAttempt = clrTypes.sym2type.get(tMSIL) - secondAttempt match { case Some(sym) => sym.tpe - case None => null - } - } else getClassType(tMSIL) - } - if (res == null) - null // TODO new RuntimeException() - else 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.typeSymbol - 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) - } - - def isDefinedAtgetConstant(constType: Type): Boolean = { - val typeClass = constType.typeSymbol - if ( (typeClass == definitions.BooleanClass) - || (typeClass == definitions.ByteClass) - || (typeClass == definitions.ShortClass) - || (typeClass == definitions.CharClass) - || (typeClass == definitions.IntClass) - || (typeClass == definitions.LongClass) - || (typeClass == definitions.FloatClass) - || (typeClass == definitions.DoubleClass) - || (typeClass == definitions.StringClass) - ) - true - else - false - } - - 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() || field.IsLiteral()) - 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 - } -} +// /* NSC -- new scala compiler +// * Copyright 2004-2011 LAMP/EPFL +// */ +// +// package scala.tools.nsc +// package symtab +// package clr +// +// import java.io.IOException +// import io.MsilFile +// import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute, _} +// import scala.collection.{ mutable, immutable } +// import scala.reflect.internal.pickling.UnPickler +// import ch.epfl.lamp.compiler.msil.Type.TMVarUsage +// +// /** +// * @author Nikolay Mihaylov +// */ +// 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 implicit def stringToTermName(s: String): TermName = newTermName(s) +// +// private object unpickler extends UnPickler { +// val global: TypeParser.this.global.type = TypeParser.this.global +// } +// +// def parse(typ: MSILType, root: Symbol) { +// +// def handleError(e: Throwable) = { +// 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.companionClass +// this.staticModule = root +// } else { +// this.clazz = root +// this.staticModule = root.companionModule +// } +// try { +// parseClass(typ) +// } catch { +// case e: FatalError => handleError(e) +// case e: RuntimeException => handleError(e) +// } +// busy = false +// } +// +// class TypeParamsType(override val typeParams: List[Symbol]) extends LazyType { +// override def complete(sym: Symbol) { throw new AssertionError("cyclic type dereferencing") } +// } +// +// /* the names `classTParams` and `newTParams` stem from the forJVM version (ClassfileParser.sigToType()) +// * but there are differences that should be kept in mind. +// * forMSIL, a nested class knows nothing about any type-params in the nesting class, +// * therefore newTParams is redundant (other than for recording lexical order), +// * it always contains the same elements as classTParams.value */ +// val classTParams = scala.collection.mutable.Map[Int,Symbol]() // TODO should this be a stack? (i.e., is it possible for >1 invocation to getCLRType on the same TypeParser instance be active ) +// val newTParams = new scala.collection.mutable.ListBuffer[Symbol]() +// val methodTParams = scala.collection.mutable.Map[Int,Symbol]() +// +// private def sig2typeBounds(tvarCILDef: GenericParamAndConstraints): Type = { +// val ts = new scala.collection.mutable.ListBuffer[Type] +// for (cnstrnt <- tvarCILDef.Constraints) { +// ts += getCLRType(cnstrnt) // TODO we're definitely not at or after erasure, no need to call objToAny, right? +// } +// TypeBounds.upper(intersectionType(ts.toList, clazz)) +// // TODO variance??? +// } +// +// private def createViewFromTo(viewSuffix : String, fromTpe : Type, toTpe : Type, +// addToboxMethodMap : Boolean, isAddressOf : Boolean) : Symbol = { +// val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead? +// val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(List(fromTpe)), toTpe) +// val vmsym = createMethod(nme.view_ + viewSuffix, flags, viewMethodType, null, true); +// // !!! this used to mutate a mutable map in definitions, but that map became +// // immutable and this kept "working" with a no-op. So now it's commented out +// // since I retired the deprecated code which allowed for that bug. +// // +// // if (addToboxMethodMap) definitions.boxMethod(clazz) = vmsym +// +// if (isAddressOf) clrTypes.addressOfViews += vmsym +// vmsym +// } +// +// private def createDefaultConstructor(typ: MSILType) { +// val attrs = MethodAttributes.Public | MethodAttributes.RTSpecialName | MethodAttributes.SpecialName // TODO instance +// val declType= typ +// val method = new ConstructorInfo(declType, attrs, Array[MSILType]()) +// val flags = Flags.JAVA +// val owner = clazz +// val methodSym = owner.newMethod(NoPosition, nme.CONSTRUCTOR).setFlag(flags) +// val rettype = clazz.tpe +// val mtype = methodType(Array[MSILType](), rettype); +// val mInfo = mtype(methodSym) +// methodSym.setInfo(mInfo) +// instanceDefs.enter(methodSym); +// clrTypes.constructors(methodSym) = method +// } +// +// private def parseClass(typ: MSILType) { +// +// { +// val t4c = clrTypes.types.get(clazz) +// assert(t4c == None || t4c == Some(typ)) +// } +// clrTypes.types(clazz) = typ +// +// { +// val c4t = clrTypes.sym2type.get(typ) +// assert(c4t == None || c4t == Some(clazz)) +// } +// 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) +// +// var clazzBoxed : Symbol = NoSymbol +// var clazzMgdPtr : Symbol = NoSymbol +// +// val canBeTakenAddressOf = (typ.IsValueType || typ.IsEnum) && (typ.FullName != "System.Enum") +// +// if(canBeTakenAddressOf) { +// clazzBoxed = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("Boxed")) +// clazzMgdPtr = clazz.owner.newClass(clazz.name.toTypeName append newTypeName("MgdPtr")) +// clrTypes.mdgptrcls4clssym(clazz) = clazzMgdPtr +// /* adding typMgdPtr to clrTypes.sym2type should happen early (before metadata for supertypes is parsed, +// before metadata for members are parsed) so that clazzMgdPtr can be found by getClRType. */ +// val typMgdPtr = MSILType.mkByRef(typ) +// clrTypes.types(clazzMgdPtr) = typMgdPtr +// clrTypes.sym2type(typMgdPtr) = clazzMgdPtr +// /* clazzMgdPtr but not clazzBoxed is mapped by clrTypes.types into an msil.Type instance, +// because there's no metadata-level representation for a "boxed valuetype" */ +// val instanceDefsMgdPtr = newScope +// val classInfoMgdPtr = ClassInfoType(definitions.anyvalparam, instanceDefsMgdPtr, clazzMgdPtr) +// clazzMgdPtr.setFlag(flags) +// clazzMgdPtr.setInfo(classInfoMgdPtr) +// } +// +// /* START CLR generics (snippet 1) */ +// // first pass +// for (tvarCILDef <- typ.getSortedTVars() ) { +// val tpname = newTypeName(tvarCILDef.Name.replaceAll("!", "")) // TODO are really all type-params named in all assemblies out there? (NO) +// val tpsym = clazz.newTypeParameter(tpname) +// classTParams.put(tvarCILDef.Number, tpsym) +// newTParams += tpsym +// // TODO wouldn't the following also be needed later, i.e. during getCLRType +// tpsym.setInfo(definitions.AnyClass.tpe) +// } +// // second pass +// for (tvarCILDef <- typ.getSortedTVars() ) { +// val tpsym = classTParams(tvarCILDef.Number) +// tpsym.setInfo(sig2typeBounds(tvarCILDef)) // we never skip bounds unlike in forJVM +// } +// /* END CLR generics (snippet 1) */ +// val ownTypeParams = newTParams.toList +// /* START CLR generics (snippet 2) */ +// if (!ownTypeParams.isEmpty) { +// clazz.setInfo(new TypeParamsType(ownTypeParams)) +// if(typ.IsValueType && !typ.IsEnum) { +// clazzBoxed.setInfo(new TypeParamsType(ownTypeParams)) +// } +// } +// /* END CLR generics (snippet 2) */ +// instanceDefs = newScope +// staticDefs = newScope +// +// val classInfoAsInMetadata = { +// 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 branch activates for System.Object only. +// // parents (i.e., base type and interfaces) +// val parents = new scala.collection.mutable.ListBuffer[Type]() +// parents += superType +// for (iface <- ifaces) { +// parents += getCLRType(iface) // here the variance doesn't matter +// } +// // methods, properties, events, fields are entered in a moment +// if (canBeTakenAddressOf) { +// val instanceDefsBoxed = newScope +// ClassInfoType(parents.toList, instanceDefsBoxed, clazzBoxed) +// } else +// ClassInfoType(parents.toList, instanceDefs, clazz) +// } +// +// val staticInfo = ClassInfoType(List(), staticDefs, statics) +// +// clazz.setFlag(flags) +// +// if (canBeTakenAddressOf) { +// clazzBoxed.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata +// else GenPolyType(ownTypeParams, classInfoAsInMetadata) ) +// clazzBoxed.setFlag(flags) +// val rawValueInfoType = ClassInfoType(definitions.anyvalparam, instanceDefs, clazz) +// clazz.setInfo( if (ownTypeParams.isEmpty) rawValueInfoType +// else GenPolyType(ownTypeParams, rawValueInfoType) ) +// } else { +// clazz.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata +// else GenPolyType(ownTypeParams, classInfoAsInMetadata) ) +// } +// +// // TODO I don't remember if statics.setInfo and staticModule.setInfo should also know about type params +// statics.setFlag(Flags.JAVA) +// statics.setInfo(staticInfo) +// staticModule.setFlag(Flags.JAVA) +// staticModule.setInfo(statics.tpe) +// +// +// if (canBeTakenAddressOf) { +// // implicit conversions are owned by staticModule.moduleClass +// createViewFromTo("2Boxed", clazz.tpe, clazzBoxed.tpe, addToboxMethodMap = true, isAddressOf = false) +// // createViewFromTo("2Object", clazz.tpe, definitions.ObjectClass.tpe, addToboxMethodMap = true, isAddressOf = false) +// createViewFromTo("2MgdPtr", clazz.tpe, clazzMgdPtr.tpe, addToboxMethodMap = false, isAddressOf = true) +// // a return can't have type managed-pointer, thus a dereference-conversion is not needed +// // similarly, a method can't declare as return type "boxed valuetype" +// if (!typ.IsEnum) { +// // a synthetic default constructor for raw-type allows `new X' syntax +// createDefaultConstructor(typ) +// } +// } +// +// // import nested types +// for (ntype <- typ.getNestedTypes() if !(ntype.IsNestedPrivate || ntype.IsNestedAssembly || ntype.IsNestedFamANDAssem) +// || ntype.IsInterface /* TODO why shouldn't nested ifaces be type-parsed too? */ ) +// { +// val loader = new loaders.MsilFileLoader(new MsilFile(ntype)) +// val nclazz = statics.newClass(ntype.Name.toTypeName) +// val nmodule = statics.newModule(ntype.Name) +// nclazz.setInfo(loader) +// nmodule.setInfo(loader) +// staticDefs.enter(nclazz) +// staticDefs.enter(nmodule) +// +// assert(nclazz.companionModule == nmodule, nmodule) +// assert(nmodule.companionClass == nclazz, nclazz) +// } +// +// val fields = typ.getFields() +// for (field <- fields +// if !(field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly) +// if (getCLRType(field.FieldType) != null) +// ) { +// assert (!field.FieldType.IsPointer && !field.FieldType.IsByRef, "CLR requirement") +// val flags = translateAttributes(field); +// val name = newTermName(field.Name); +// val fieldType = +// if (field.IsLiteral && !field.FieldType.IsEnum && isDefinedAtgetConstant(getCLRType(field.FieldType))) +// ConstantType(getConstant(getCLRType(field.FieldType), field.getValue)) +// else +// getCLRType(field.FieldType) +// val owner = if (field.IsStatic()) statics else clazz; +// val sym = owner.newValue(NoPosition, 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 (constr <- typ.getConstructors() if !constr.IsStatic() && !constr.IsPrivate() && +// !constr.IsAssembly() && !constr.IsFamilyAndAssembly() && !constr.HasPtrParamOrRetType()) +// createMethod(constr); +// +// // initially also contains getters and setters of properties. +// val methodsSet = new mutable.HashSet[MethodInfo](); +// methodsSet ++= typ.getMethods(); +// +// for (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 || getter.HasPtrParamOrRetType)) +// { +// 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 owner: Symbol = if (getter.IsStatic) statics else clazz; +// val methodSym = owner.newMethod(NoPosition, name).setFlag(flags) +// val mtype: Type = if (gparamsLength == 0) NullaryMethodType(propType) // .NET properties can't be polymorphic +// else methodType(getter, getter.ReturnType)(methodSym) +// methodSym.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 || setter.HasPtrParamOrRetType)) +// { +// 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 = methodType(setter, definitions.UnitClass.tpe); +// val owner: Symbol = if (setter.IsStatic) statics else clazz; +// val methodSym = owner.newMethod(NoPosition, name).setFlag(flags) +// methodSym.setInfo(mtype(methodSym)) +// methodSym.setFlag(Flags.ACCESSOR); +// (if (setter.IsStatic) staticDefs else instanceDefs).enter(methodSym); +// clrTypes.methods(methodSym) = setter; +// methodsSet -= setter; +// } +// } +// } +// +// /* for (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; +// } +// } */ +// +// /* Adds view amounting to syntax sugar for a CLR implicit overload. +// The long-form syntax can also be supported if "methodsSet -= method" (last statement) is removed. +// +// /* remember, there's typ.getMethods and type.GetMethods */ +// for (method <- typ.getMethods) +// if(!method.HasPtrParamOrRetType && +// method.IsPublic && method.IsStatic && method.IsSpecialName && +// method.Name == "op_Implicit") { +// // create a view: typ => method's return type +// val viewRetType: Type = getCLRType(method.ReturnType) +// val viewParamTypes: List[Type] = method.GetParameters().map(_.ParameterType).map(getCLSType).toList; +// /* The spec says "The operator method shall be defined as a static method on either the operand or return type." +// * We don't consider the declaring type for the purposes of definitions.functionType, +// * instead we regard op_Implicit's argument type and return type as defining the view's signature. +// */ +// if (viewRetType != null && !viewParamTypes.contains(null)) { +// /* The check above applies e.g. to System.Decimal that has a conversion from UInt16, a non-CLS type, whose CLS-mapping returns null */ +// val funType: Type = definitions.functionType(viewParamTypes, viewRetType); +// val flags = Flags.JAVA | Flags.STATIC | Flags.IMPLICIT; // todo: static? shouldn't be final instead? +// val viewMethodType = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(viewParamTypes), funType) +// val vmsym = createMethod(nme.view_, flags, viewMethodType, method, true); +// methodsSet -= method; +// } +// } +// */ +// +// for (method <- methodsSet.iterator) +// if (!method.IsPrivate() && !method.IsAssembly() && !method.IsFamilyAndAssembly() +// && !method.HasPtrParamOrRetType) +// createMethod(method); +// +// // Create methods and views for delegate support +// if (clrTypes.isDelegateType(typ)) { +// createDelegateView(typ) +// createDelegateChainers(typ) +// } +// +// // for enumerations introduce comparison and bitwise logical operations; +// // the backend will recognize them 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 (cmpName <- ENUM_CMP_NAMES) { +// val enumCmp = clazz.newMethod(NoPosition, cmpName) +// val enumCmpType = JavaMethodType(enumCmp.newSyntheticValueParams(List(clazz.tpe)), definitions.BooleanClass.tpe) +// enumCmp.setFlag(flags).setInfo(enumCmpType) +// instanceDefs.enter(enumCmp) +// } +// +// for (bitLogName <- ENUM_BIT_LOG_NAMES) { +// val enumBitLog = clazz.newMethod(NoPosition, bitLogName) +// val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), clazz.tpe /* was classInfo, infinite typer */) +// enumBitLog.setFlag(flags).setInfo(enumBitLogType) +// instanceDefs.enter(enumBitLog) +// } +// } +// +// } // parseClass +// +// private def populateMethodTParams(method: MethodBase, methodSym: MethodSymbol) : List[Symbol] = { +// if(!method.IsGeneric) Nil +// else { +// methodTParams.clear +// val newMethodTParams = new scala.collection.mutable.ListBuffer[Symbol]() +// +// // first pass +// for (mvarCILDef <- method.getSortedMVars() ) { +// val mtpname = newTypeName(mvarCILDef.Name.replaceAll("!", "")) // TODO are really all method-level-type-params named in all assemblies out there? (NO) +// val mtpsym = methodSym.newTypeParameter(mtpname) +// methodTParams.put(mvarCILDef.Number, mtpsym) +// newMethodTParams += mtpsym +// // TODO wouldn't the following also be needed later, i.e. during getCLRType +// mtpsym.setInfo(definitions.AnyClass.tpe) +// } +// // second pass +// for (mvarCILDef <- method.getSortedMVars() ) { +// val mtpsym = methodTParams(mvarCILDef.Number) +// mtpsym.setInfo(sig2typeBounds(mvarCILDef)) // we never skip bounds unlike in forJVM +// } +// +// newMethodTParams.toList +// } +// } +// +// private def createMethod(method: MethodBase) { +// +// val flags = translateAttributes(method); +// val owner = if (method.IsStatic()) statics else clazz; +// val methodSym = owner.newMethod(NoPosition, getName(method)).setFlag(flags) +// /* START CLR generics (snippet 3) */ +// val newMethodTParams = populateMethodTParams(method, methodSym) +// /* END CLR generics (snippet 3) */ +// +// 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; +// /* START CLR generics (snippet 4) */ +// val mInfo = if (method.IsGeneric) GenPolyType(newMethodTParams, mtype(methodSym)) +// else mtype(methodSym) +// /* END CLR generics (snippet 4) */ +// /* START CLR non-generics (snippet 4) +// val mInfo = mtype(methodSym) +// END CLR non-generics (snippet 4) */ +// methodSym.setInfo(mInfo) +// (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 = methodType(args, getCLSType(retType)) +// assert(mtype != null) +// createMethod(name, flags, mtype, method, statik) +// } +// +// private def createMethod(name: Name, flags: Long, mtype: Symbol => Type, method: MethodInfo, statik: Boolean): Symbol = { +// val methodSym: Symbol = (if (statik) statics else clazz).newMethod(NoPosition, name) +// methodSym.setFlag(flags).setInfo(mtype(methodSym)) +// (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 = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(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 = (msym: Symbol) => JavaMethodType(msym.newSyntheticValueParams(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 = { +// +// def operatorOverload(name : String, paramsArity : Int) : Option[Name] = paramsArity match { +// case 1 => name match { +// // PartitionI.10.3.1 +// case "op_Decrement" => Some(encode("--")) +// case "op_Increment" => Some(encode("++")) +// case "op_UnaryNegation" => Some(nme.UNARY_-) +// case "op_UnaryPlus" => Some(nme.UNARY_+) +// case "op_LogicalNot" => Some(nme.UNARY_!) +// case "op_OnesComplement" => Some(nme.UNARY_~) +// /* op_True and op_False have no operator symbol assigned, +// Other methods that will have to be written in full are: +// op_AddressOf & (unary) +// op_PointerDereference * (unary) */ +// case _ => None +// } +// case 2 => name match { +// // PartitionI.10.3.2 +// case "op_Addition" => Some(nme.ADD) +// case "op_Subtraction" => Some(nme.SUB) +// case "op_Multiply" => Some(nme.MUL) +// case "op_Division" => Some(nme.DIV) +// case "op_Modulus" => Some(nme.MOD) +// case "op_ExclusiveOr" => Some(nme.XOR) +// case "op_BitwiseAnd" => Some(nme.AND) +// case "op_BitwiseOr" => Some(nme.OR) +// case "op_LogicalAnd" => Some(nme.ZAND) +// case "op_LogicalOr" => Some(nme.ZOR) +// case "op_LeftShift" => Some(nme.LSL) +// case "op_RightShift" => Some(nme.ASR) +// case "op_Equality" => Some(nme.EQ) +// case "op_GreaterThan" => Some(nme.GT) +// case "op_LessThan" => Some(nme.LT) +// case "op_Inequality" => Some(nme.NE) +// case "op_GreaterThanOrEqual" => Some(nme.GE) +// case "op_LessThanOrEqual" => Some(nme.LE) +// +// /* op_MemberSelection is reserved in Scala */ +// +// /* The standard does not assign operator symbols to op_Assign , op_SignedRightShift , op_UnsignedRightShift , +// * and op_UnsignedRightShiftAssignment so those names will be used instead to invoke those methods. */ +// +// /* +// The remaining binary operators are not overloaded in C# and are therefore not in widespread use. They have to be written in full. +// +// op_RightShiftAssignment >>= +// op_MultiplicationAssignment *= +// op_PointerToMemberSelection ->* +// op_SubtractionAssignment -= +// op_ExclusiveOrAssignment ^= +// op_LeftShiftAssignment <<= +// op_ModulusAssignment %= +// op_AdditionAssignment += +// op_BitwiseAndAssignment &= +// op_BitwiseOrAssignment |= +// op_Comma , +// op_DivisionAssignment /= +// */ +// case _ => None +// } +// case _ => None +// } +// +// if (method.IsConstructor()) return nme.CONSTRUCTOR; +// val name = method.Name; +// if (method.IsStatic()) { +// if(method.IsSpecialName) { +// val paramsArity = method.GetParameters().size +// // handle operator overload, otherwise handle as any static method +// val operName = operatorOverload(name, paramsArity) +// if (operName.isDefined) { return operName.get; } +// } +// 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): Symbol => 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): Symbol => 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): Symbol => Type = { +// def paramType(typ: MSILType): Type = +// if (typ eq clrTypes.OBJECT) definitions.AnyClass.tpe // TODO a hack to compile scalalib, should be definitions.AnyRefClass.tpe +// else getCLSType(typ); +// val ptypes = argtypes.map(paramType).toList; +// if (ptypes.contains(null)) null +// else method => JavaMethodType(method.newSyntheticValueParams(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 = { // getCLS returns non-null for types GenMSIL can handle, be they CLS-compliant or not +// if (typ.IsTMVarUsage()) +// /* START CLR generics (snippet 5) */ +// getCLRType(typ) +// /* END CLR generics (snippet 5) */ +// /* START CLR non-generics (snippet 5) +// null +// END CLR non-generics (snippet 5) */ +// else if ( /* TODO hack if UBYE, uncommented, "ambiguous reference to overloaded definition" ensues, for example for System.Math.Max(x, y) */ +// typ == clrTypes.USHORT || typ == clrTypes.UINT || typ == clrTypes.ULONG +// /* || typ == clrTypes.UBYTE */ +// || typ.IsNotPublic() || typ.IsNestedPrivate() +// || typ.IsNestedAssembly() || typ.IsNestedFamANDAssem() +// || typ.IsPointer() +// || (typ.IsArray() && getCLRType(typ.GetElementType()) == null) /* TODO hack: getCLR instead of getCLS */ +// || (typ.IsByRef() && !typ.GetElementType().CanBeTakenAddressOf())) +// null +// else +// getCLRType(typ) +// } +// +// private def getCLRTypeIfPrimitiveNullOtherwise(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)) // TODO U... is a hack to compile scalalib +// definitions.ByteClass.tpe +// else if ((typ == clrTypes.SHORT) || (typ == clrTypes.SHORT)) // TODO U... is a hack to compile scalalib +// definitions.ShortClass.tpe +// else if ((typ == clrTypes.INT) || (typ == clrTypes.UINT)) // TODO U... is a hack to compile scalalib +// definitions.IntClass.tpe +// else if ((typ == clrTypes.LONG) || (typ == clrTypes.LONG)) // TODO U... is a hack to compile scalalib +// definitions.LongClass.tpe +// else if (typ == clrTypes.FLOAT) +// definitions.FloatClass.tpe +// else if (typ == clrTypes.DOUBLE) +// definitions.DoubleClass.tpe +// else null +// +// +// private def getCLRType(tMSIL: MSILType): Type = { +// var res = getCLRTypeIfPrimitiveNullOtherwise(tMSIL) +// if (res != null) res +// else if (tMSIL.isInstanceOf[ConstructedType]) { +// val ct = tMSIL.asInstanceOf[ConstructedType] +// /* START CLR generics (snippet 6) */ +// val cttpArgs = ct.typeArgs.map(tmsil => getCLRType(tmsil)).toList +// appliedType(getCLRType(ct.instantiatedType), cttpArgs) +// /* END CLR generics (snippet 6) */ +// /* START CLR non-generics (snippet 6) +// getCLRType(ct.instantiatedType) +// END CLR non-generics (snippet 6) */ +// } else if (tMSIL.isInstanceOf[TMVarUsage]) { +// /* START CLR generics (snippet 7) */ +// val tVarUsage = tMSIL.asInstanceOf[TMVarUsage] +// val tVarNumber = tVarUsage.Number +// if (tVarUsage.isTVar) classTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst +// else methodTParams(tVarNumber).typeConstructor // shouldn't fail, just return definitions.AnyClass.tpe at worst +// /* END CLR generics (snippet 7) */ +// /* START CLR non-generics (snippet 7) +// null // definitions.ObjectClass.tpe +// END CLR non-generics (snippet 7) */ +// } else if (tMSIL.IsArray()) { +// var elemtp = getCLRType(tMSIL.GetElementType()) +// // cut&pasted from ClassfileParser +// // make unbounded Array[T] where T is a type variable into Array[T with Object] +// // (this is necessary because such arrays have a representation which is incompatible +// // with arrays of primitive types). +// // TODO does that incompatibility also apply to .NET? +// if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< definitions.ObjectClass.tpe)) +// elemtp = intersectionType(List(elemtp, definitions.ObjectClass.tpe)) +// appliedType(definitions.ArrayClass.tpe, List(elemtp)) +// } else { +// res = clrTypes.sym2type.get(tMSIL) match { +// case Some(sym) => sym.tpe +// case None => if (tMSIL.IsByRef && tMSIL.GetElementType.IsValueType) { +// val addressed = getCLRType(tMSIL.GetElementType) +// val clasym = addressed.typeSymbolDirect // TODO should be .typeSymbol? +// clasym.info.load(clasym) +// val secondAttempt = clrTypes.sym2type.get(tMSIL) +// secondAttempt match { case Some(sym) => sym.tpe +// case None => null +// } +// } else getClassType(tMSIL) +// } +// if (res == null) +// null // TODO new RuntimeException() +// else 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.typeSymbol +// 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) +// } +// +// def isDefinedAtgetConstant(constType: Type): Boolean = { +// val typeClass = constType.typeSymbol +// if ( (typeClass == definitions.BooleanClass) +// || (typeClass == definitions.ByteClass) +// || (typeClass == definitions.ShortClass) +// || (typeClass == definitions.CharClass) +// || (typeClass == definitions.IntClass) +// || (typeClass == definitions.LongClass) +// || (typeClass == definitions.FloatClass) +// || (typeClass == definitions.DoubleClass) +// || (typeClass == definitions.StringClass) +// ) +// true +// else +// false +// } +// +// 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() || field.IsLiteral()) +// 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/util/MsilClassPath.scala b/src/compiler/scala/tools/nsc/util/MsilClassPath.scala index 6215506141..260c350ede 100644 --- a/src/compiler/scala/tools/nsc/util/MsilClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/MsilClassPath.scala @@ -1,169 +1,169 @@ -/* NSC -- new Scala compiler - * Copyright 2006-2011 LAMP/EPFL - * @author Martin Odersky - */ - -// $Id$ - -package scala.tools.nsc -package util - -import java.io.File -import java.net.URL -import java.util.StringTokenizer -import scala.util.Sorting -import scala.collection.mutable -import scala.tools.nsc.io.{ AbstractFile, MsilFile } -import ch.epfl.lamp.compiler.msil.{ Type => MSILType, Assembly } -import ClassPath.{ ClassPathContext, isTraitImplementation } - -/** Keeping the MSIL classpath code in its own file is important to make sure - * we don't accidentally introduce a dependency on msil.jar in the jvm. - */ - -object MsilClassPath { - def collectTypes(assemFile: AbstractFile) = { - var res: Array[MSILType] = MSILType.EmptyTypes - val assem = Assembly.LoadFrom(assemFile.path) - if (assem != null) { - // DeclaringType == null: true for non-inner classes - res = assem.GetTypes() filter (_.DeclaringType == null) - Sorting.stableSort(res, (t1: MSILType, t2: MSILType) => (t1.FullName compareTo t2.FullName) < 0) - } - res - } - - /** On the java side this logic is in PathResolver, but as I'm not really - * up to folding MSIL into that, I am encapsulating it here. - */ - def fromSettings(settings: Settings): MsilClassPath = { - val context = - if (settings.inline.value) new MsilContext - else new MsilContext { override def isValidName(name: String) = !isTraitImplementation(name) } - - import settings._ - new MsilClassPath(assemextdirs.value, assemrefs.value, sourcepath.value, context) - } - - class MsilContext extends ClassPathContext[MsilFile] { - def toBinaryName(rep: MsilFile) = rep.msilType.Name - def newClassPath(assemFile: AbstractFile) = new AssemblyClassPath(MsilClassPath collectTypes assemFile, "", this) - } - - private def assembleEntries(ext: String, user: String, source: String, context: MsilContext): List[ClassPath[MsilFile]] = { - import ClassPath._ - val etr = new mutable.ListBuffer[ClassPath[MsilFile]] - val names = new mutable.HashSet[String] - - // 1. Assemblies from -Xassem-extdirs - for (dirName <- expandPath(ext, expandStar = false)) { - val dir = AbstractFile.getDirectory(dirName) - if (dir ne null) { - for (file <- dir) { - val name = file.name.toLowerCase - if (name.endsWith(".dll") || name.endsWith(".exe")) { - names += name - etr += context.newClassPath(file) - } - } - } - } - - // 2. Assemblies from -Xassem-path - for (fileName <- expandPath(user, expandStar = false)) { - val file = AbstractFile.getFile(fileName) - if (file ne null) { - val name = file.name.toLowerCase - if (name.endsWith(".dll") || name.endsWith(".exe")) { - names += name - etr += context.newClassPath(file) - } - } - } - - def check(n: String) { - if (!names.contains(n)) - throw new AssertionError("Cannot find assembly "+ n + - ". Use -Xassem-extdirs or -Xassem-path to specify its location") - } - check("mscorlib.dll") - check("scalaruntime.dll") - - // 3. Source path - for (dirName <- expandPath(source, expandStar = false)) { - val file = AbstractFile.getDirectory(dirName) - if (file ne null) etr += new SourcePath[MsilFile](file, context) - } - - etr.toList - } -} -import MsilClassPath._ - -/** - * A assembly file (dll / exe) containing classes and namespaces - */ -class AssemblyClassPath(types: Array[MSILType], namespace: String, val context: MsilContext) extends ClassPath[MsilFile] { - def name = { - val i = namespace.lastIndexOf('.') - if (i < 0) namespace - else namespace drop (i + 1) - } - def asURLs = List(new java.net.URL(name)) - def asClasspathString = sys.error("Unknown") // I don't know what if anything makes sense here? - - private lazy val first: Int = { - var m = 0 - var n = types.length - 1 - while (m < n) { - val l = (m + n) / 2 - val res = types(l).FullName.compareTo(namespace) - if (res < 0) m = l + 1 - else n = l - } - if (types(m).FullName.startsWith(namespace)) m else types.length - } - - lazy val classes = { - val cls = new mutable.ListBuffer[ClassRep] - var i = first - while (i < types.length && types(i).Namespace.startsWith(namespace)) { - // CLRTypes used to exclude java.lang.Object and java.lang.String (no idea why..) - if (types(i).Namespace == namespace) - cls += ClassRep(Some(new MsilFile(types(i))), None) - i += 1 - } - cls.toIndexedSeq - } - - lazy val packages = { - val nsSet = new mutable.HashSet[String] - var i = first - while (i < types.length && types(i).Namespace.startsWith(namespace)) { - val subns = types(i).Namespace - if (subns.length > namespace.length) { - // example: namespace = "System", subns = "System.Reflection.Emit" - // => find second "." and "System.Reflection" to nsSet. - val end = subns.indexOf('.', namespace.length + 1) - nsSet += (if (end < 0) subns - else subns.substring(0, end)) - } - i += 1 - } - val xs = for (ns <- nsSet.toList) - yield new AssemblyClassPath(types, ns, context) - - xs.toIndexedSeq - } - - val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq() - - override def toString() = "assembly classpath "+ namespace -} - -/** - * The classpath when compiling with target:msil. Binary files are represented as - * MSILType values. - */ -class MsilClassPath(ext: String, user: String, source: String, context: MsilContext) -extends MergedClassPath[MsilFile](MsilClassPath.assembleEntries(ext, user, source, context), context) { } \ No newline at end of file +// /* NSC -- new Scala compiler +// * Copyright 2006-2011 LAMP/EPFL +// * @author Martin Odersky +// */ +// +// // $Id$ +// +// package scala.tools.nsc +// package util +// +// import java.io.File +// import java.net.URL +// import java.util.StringTokenizer +// import scala.util.Sorting +// import scala.collection.mutable +// import scala.tools.nsc.io.{ AbstractFile, MsilFile } +// import ch.epfl.lamp.compiler.msil.{ Type => MSILType, Assembly } +// import ClassPath.{ ClassPathContext, isTraitImplementation } +// +// /** Keeping the MSIL classpath code in its own file is important to make sure +// * we don't accidentally introduce a dependency on msil.jar in the jvm. +// */ +// +// object MsilClassPath { +// def collectTypes(assemFile: AbstractFile) = { +// var res: Array[MSILType] = MSILType.EmptyTypes +// val assem = Assembly.LoadFrom(assemFile.path) +// if (assem != null) { +// // DeclaringType == null: true for non-inner classes +// res = assem.GetTypes() filter (_.DeclaringType == null) +// Sorting.stableSort(res, (t1: MSILType, t2: MSILType) => (t1.FullName compareTo t2.FullName) < 0) +// } +// res +// } +// +// /** On the java side this logic is in PathResolver, but as I'm not really +// * up to folding MSIL into that, I am encapsulating it here. +// */ +// def fromSettings(settings: Settings): MsilClassPath = { +// val context = +// if (settings.inline.value) new MsilContext +// else new MsilContext { override def isValidName(name: String) = !isTraitImplementation(name) } +// +// import settings._ +// new MsilClassPath(assemextdirs.value, assemrefs.value, sourcepath.value, context) +// } +// +// class MsilContext extends ClassPathContext[MsilFile] { +// def toBinaryName(rep: MsilFile) = rep.msilType.Name +// def newClassPath(assemFile: AbstractFile) = new AssemblyClassPath(MsilClassPath collectTypes assemFile, "", this) +// } +// +// private def assembleEntries(ext: String, user: String, source: String, context: MsilContext): List[ClassPath[MsilFile]] = { +// import ClassPath._ +// val etr = new mutable.ListBuffer[ClassPath[MsilFile]] +// val names = new mutable.HashSet[String] +// +// // 1. Assemblies from -Xassem-extdirs +// for (dirName <- expandPath(ext, expandStar = false)) { +// val dir = AbstractFile.getDirectory(dirName) +// if (dir ne null) { +// for (file <- dir) { +// val name = file.name.toLowerCase +// if (name.endsWith(".dll") || name.endsWith(".exe")) { +// names += name +// etr += context.newClassPath(file) +// } +// } +// } +// } +// +// // 2. Assemblies from -Xassem-path +// for (fileName <- expandPath(user, expandStar = false)) { +// val file = AbstractFile.getFile(fileName) +// if (file ne null) { +// val name = file.name.toLowerCase +// if (name.endsWith(".dll") || name.endsWith(".exe")) { +// names += name +// etr += context.newClassPath(file) +// } +// } +// } +// +// def check(n: String) { +// if (!names.contains(n)) +// throw new AssertionError("Cannot find assembly "+ n + +// ". Use -Xassem-extdirs or -Xassem-path to specify its location") +// } +// check("mscorlib.dll") +// check("scalaruntime.dll") +// +// // 3. Source path +// for (dirName <- expandPath(source, expandStar = false)) { +// val file = AbstractFile.getDirectory(dirName) +// if (file ne null) etr += new SourcePath[MsilFile](file, context) +// } +// +// etr.toList +// } +// } +// import MsilClassPath._ +// +// /** +// * A assembly file (dll / exe) containing classes and namespaces +// */ +// class AssemblyClassPath(types: Array[MSILType], namespace: String, val context: MsilContext) extends ClassPath[MsilFile] { +// def name = { +// val i = namespace.lastIndexOf('.') +// if (i < 0) namespace +// else namespace drop (i + 1) +// } +// def asURLs = List(new java.net.URL(name)) +// def asClasspathString = sys.error("Unknown") // I don't know what if anything makes sense here? +// +// private lazy val first: Int = { +// var m = 0 +// var n = types.length - 1 +// while (m < n) { +// val l = (m + n) / 2 +// val res = types(l).FullName.compareTo(namespace) +// if (res < 0) m = l + 1 +// else n = l +// } +// if (types(m).FullName.startsWith(namespace)) m else types.length +// } +// +// lazy val classes = { +// val cls = new mutable.ListBuffer[ClassRep] +// var i = first +// while (i < types.length && types(i).Namespace.startsWith(namespace)) { +// // CLRTypes used to exclude java.lang.Object and java.lang.String (no idea why..) +// if (types(i).Namespace == namespace) +// cls += ClassRep(Some(new MsilFile(types(i))), None) +// i += 1 +// } +// cls.toIndexedSeq +// } +// +// lazy val packages = { +// val nsSet = new mutable.HashSet[String] +// var i = first +// while (i < types.length && types(i).Namespace.startsWith(namespace)) { +// val subns = types(i).Namespace +// if (subns.length > namespace.length) { +// // example: namespace = "System", subns = "System.Reflection.Emit" +// // => find second "." and "System.Reflection" to nsSet. +// val end = subns.indexOf('.', namespace.length + 1) +// nsSet += (if (end < 0) subns +// else subns.substring(0, end)) +// } +// i += 1 +// } +// val xs = for (ns <- nsSet.toList) +// yield new AssemblyClassPath(types, ns, context) +// +// xs.toIndexedSeq +// } +// +// val sourcepaths: IndexedSeq[AbstractFile] = IndexedSeq() +// +// override def toString() = "assembly classpath "+ namespace +// } +// +// /** +// * The classpath when compiling with target:msil. Binary files are represented as +// * MSILType values. +// */ +// class MsilClassPath(ext: String, user: String, source: String, context: MsilContext) +// extends MergedClassPath[MsilFile](MsilClassPath.assembleEntries(ext, user, source, context), context) { } \ No newline at end of file -- cgit v1.2.3