for MSIL:

(a) The bytecode that Scala.NET emitted had a tough time in passing peverify due to valuetypes (aka structs) and their related managed-pointer types. With these changes (details in [1] and [2]) external APIs exposing valuetypes can be used, yet the extra step of supporting defining valuetypes in Scala programs has been left for later. Supporting the unsigned integral valuetypes (used, among others, by IKVM) is also pending. 

(b) A very first step towards generics can be found in TypeParser.parseClass, for the time being commented out (search for the label "TODO CLR generics"). It's commented out because without CLRManifests codegen won't work as expected. Details in [3]. 

review by rytz

Refs:

[1] http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2010Q3/Bootstrapping3.pdf

[2] http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2010Q3/Bootstrapping4.pdf

[3] http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2010Q2/SigToType.pdf


git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@22831 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
magarcia 2010-08-24 07:38:58 +00:00
parent f9dad11629
commit bfb6fbb76d
14 changed files with 829 additions and 194 deletions

Binary file not shown.

View File

@ -167,7 +167,9 @@ abstract class GenICode extends SubComponent {
private def genStat(tree: Tree, ctx: Context): Context = tree match {
case Assign(lhs @ Select(_, _), rhs) =>
val isStatic = lhs.symbol.isStaticMember
var ctx1 = if (isStatic) ctx else genLoadQualifier(lhs, ctx)
var ctx1 = if (isStatic) ctx
else if (forMSIL && msil_IsValuetypeInstField(lhs.symbol)) msil_genLoadQualifierAddress(lhs, ctx)
else genLoadQualifier(lhs, ctx)
ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info))
ctx1.bb.emit(STORE_FIELD(lhs.symbol, isStatic), tree.pos)
@ -457,6 +459,146 @@ abstract class GenICode extends SubComponent {
fun.symbol.simpleName + ") " + " at: " + (tree.pos))
}
/**
* forMSIL
*/
private def msil_IsValuetypeInstMethod(msym: Symbol) = {
val mMSILOpt = loaders.clrTypes.methods.get(msym)
if (mMSILOpt.isEmpty) false
else {
val mMSIL = mMSILOpt.get
val res = mMSIL.IsInstance && mMSIL.DeclaringType.IsValueType
res
}
}
/**
* forMSIL
*/
private def msil_IsValuetypeInstField(fsym: Symbol) = {
val fMSILOpt = loaders.clrTypes.fields.get(fsym)
if (fMSILOpt.isEmpty) false
else {
val fMSIL = fMSILOpt.get
val res = !fMSIL.IsStatic && fMSIL.DeclaringType.IsValueType
res
}
}
/**
* 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))
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
if (settings.debug.value)
log("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
if (settings.debug.value)
assert(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
*
@ -657,15 +799,31 @@ abstract class GenICode extends SubComponent {
if (settings.debug.value)
assert(ctor.owner == cls,
"Symbol " + ctor.owner.fullName + " is different than " + tpt)
val nw = NEW(rt)
ctx.bb.emit(nw, tree.pos)
ctx.bb.emit(DUP(generatedType))
val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx)
val init = CALL_METHOD(ctor, Static(true))
nw.init = init
ctx1.bb.emit(init, tree.pos)
ctx1
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))
val ctx1 = genLoadArguments(args, ctor.info.paramTypes, ctx)
val init = CALL_METHOD(ctor, Static(true))
nw.init = init
ctx1.bb.emit(init, tree.pos)
ctx1
}
ctx2
case _ =>
abort("Cannot instantiate " + tpt + "of kind: " + generatedType)
@ -697,6 +855,13 @@ abstract class GenICode extends SubComponent {
ctx1.bb.emit(UNBOX(boxType), expr.pos)
ctx1
case Apply(fun @ _, List(expr)) if (forMSIL && loaders.clrTypes.isAddressOf(fun.symbol)) =>
if (settings.debug.value)
log("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
@ -737,7 +902,11 @@ abstract class GenICode extends SubComponent {
Dynamic
var ctx1 =
if (invokeStyle.hasInstance) genLoadQualifier(fun, ctx)
if (invokeStyle.hasInstance)
if (forMSIL && !(invokeStyle.isInstanceOf[SuperCall]) && msil_IsValuetypeInstMethod(sym))
msil_genLoadQualifierAddress(fun, ctx)
else
genLoadQualifier(fun, ctx)
else ctx
ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx1)
@ -770,6 +939,7 @@ abstract class GenICode extends SubComponent {
}
case ApplyDynamic(qual, args) =>
assert(!forMSIL)
ctx.clazz.bootstrapClass = Some("scala.runtime.DynamicDispatch")
val ctx1 = genLoad(qual, ctx, ANY_REF_CLASS)
genLoadArguments(args, tree.symbol.info.paramTypes, ctx1)
@ -984,6 +1154,15 @@ abstract class GenICode extends SubComponent {
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.
*/

View File

@ -665,5 +665,65 @@ trait Opcodes { self: ICodes =>
/** Call through super[mix]. */
case class SuperCall(mix: Name) extends InvokeStyle
// 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.toString() //+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 = AnyRefReference :: 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 = AnyRefReference :: 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))
}
}
}

View File

@ -488,4 +488,10 @@ trait TypeKinds { self: ICodes =>
}
}
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)
}
}

View File

@ -461,8 +461,8 @@ abstract class GenMSIL extends SubComponent {
if (settings.debug.value)
log("Output path: " + filename)
try {
massembly.Save(filename + "\\" + assemName + ".msil") /* use SingleFileILPrinterVisitor */
// massembly.Save(filename, srcPath.getPath()) /* use MultipleFilesILPrinterVisitor */
// massembly.Save(filename + "\\" + assemName + ".msil") /* use SingleFileILPrinterVisitor */
massembly.Save(filename, srcPath.getPath()) /* use MultipleFilesILPrinterVisitor */
} catch {
case e:IOException => abort("Could not write to " + filename + ": " + e.getMessage())
}
@ -822,6 +822,76 @@ abstract class GenMSIL extends SubComponent {
* @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) {
if (settings.debug.value)
log(msg + " for " + local)
val isArg = local.arg
val i = local.index
if (isArg)
loadArg(mcode, loadAddr)(i)
else
loadLocal(i, local, mcode, loadAddr)
}
def loadFieldOrAddress(field: Symbol, isStatic: Boolean, msg: String, loadAddr : Boolean) {
if (settings.debug.value)
log(msg + " with owner: " + field.owner +
" flags: " + Flags.flagsToString(field.owner.flags))
var fieldInfo = fields.get(field) match {
case Some(fInfo) => fInfo
case None =>
val fInfo = getType(field.owner).GetField(msilName(field))
fields(field) = fInfo
fInfo
}
if (!fieldInfo.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_R4, 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)
@ -927,66 +997,20 @@ abstract class GenMSIL extends SubComponent {
// Array[scala.runtime.BoxedUnit] (-> case REFERENCE)
}
case LOAD_LOCAL(local) =>
if (settings.debug.value)
log("load_local for " + local)
val isArg = local.arg
val i = local.index
if (isArg)
loadArg(mcode)(i)
else
loadLocal(i, local, mcode)
case LOAD_LOCAL(local) => loadLocalOrAddress(local, "load_local", false)
case LOAD_FIELD(field, isStatic) =>
if (settings.debug.value)
log("LOAD_FIELD 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.IsLiteral) {
mcode.Emit(if (isStatic) OpCodes.Ldsfld else OpCodes.Ldfld, fieldInfo)
} else {
/* 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_R4, value.asInstanceOf[Double])
} else {
/* TODO one more case is described in Partition II, 16.2: bytearray(...) */
abort("Unknown type for static literal field: " + fieldInfo)
}
}
}
case CIL_LOAD_LOCAL_ADDRESS(local) => loadLocalOrAddress(local, "cil_load_local_address", true)
case LOAD_FIELD(field, isStatic) => loadFieldOrAddress(field, isStatic, "load_field", false)
case CIL_LOAD_FIELD_ADDRESS(field, isStatic) => loadFieldOrAddress(field, isStatic, "cil_load_field_address", true)
case CIL_LOAD_ARRAY_ITEM_ADDRESS(kind) => mcode.Emit(OpCodes.Ldelema, msilType(kind))
case CIL_NEWOBJ(msym) =>
assert(msym.isClassConstructor)
val constructorInfo: ConstructorInfo = getConstructor(msym)
mcode.Emit(OpCodes.Newobj, constructorInfo)
case LOAD_MODULE(module) =>
if (settings.debug.value)
@ -1169,13 +1193,20 @@ abstract class GenMSIL extends SubComponent {
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 ((methodInfo.DeclaringType ne null) && (methodInfo.DeclaringType.IsValueType)) {
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)
@ -1184,9 +1215,17 @@ abstract class GenMSIL extends SubComponent {
}
}
case BOX(boxType) => emitBox(mcode, boxType)
case BOX(boxType) =>
emitBox(mcode, boxType)
case UNBOX(boxType) => emitUnbox(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`
@ -1211,7 +1250,14 @@ abstract class GenMSIL extends SubComponent {
// Array[S] <: Array[T] in Scala. However, it is possible
// to cast an array of S to an array of T if such a cast
// is permitted in the host environment."
case CHECK_CAST(tpe) => mcode.Emit(OpCodes.Castclass, msilType(tpe))
case CHECK_CAST(tpknd) =>
val tMSIL = msilType(tpknd)
if (tMSIL.IsValueType) {
// calling emitUnbox does nothing because there's no unbox method for tMSIL
mcode.Emit(OpCodes.Unbox, tMSIL)
mcode.Emit(OpCodes.Ldobj, tMSIL)
} else
mcode.Emit(OpCodes.Castclass, tMSIL)
// no SWITCH is generated when there's
// - a default case ("case _ => ...") in the matching expr
@ -1398,7 +1444,14 @@ abstract class GenMSIL extends SubComponent {
code.Emit(OpCodes.Ldc_I4, value)
}
def loadArg(code: ILGenerator)(i: Int) = i match {
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)
@ -1409,8 +1462,16 @@ abstract class GenMSIL extends SubComponent {
else
code.Emit(OpCodes.Ldarg, i)
}
}
def loadLocal(i: Int, local: Local, code: ILGenerator) = i match {
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)
@ -1421,6 +1482,7 @@ abstract class GenMSIL extends SubComponent {
else
code.Emit(OpCodes.Ldloc, localBuilders(local))
}
}
////////////////////// branches ///////////////////////
@ -1826,7 +1888,9 @@ abstract class GenMSIL extends SubComponent {
}
def createClassMembers0(iclass: IClass) {
val mtype = getType(iclass.symbol).asInstanceOf[TypeBuilder]
for (ifield <- iclass.fields) {
val sym = ifield.symbol
if (settings.debug.value)
@ -1836,7 +1900,7 @@ abstract class GenMSIL extends SubComponent {
val fBuilder = mtype.DefineField(msilName(sym), msilType(sym.tpe), attributes)
fields(sym) = fBuilder
addAttributes(fBuilder, sym.annotations)
}
} // all iclass.fields iterated over
if (iclass.symbol != definitions.ArrayClass) {
for (m: IMethod <- iclass.methods) {
@ -2010,7 +2074,12 @@ abstract class GenMSIL extends SubComponent {
val mirrorCode = mirrorMethod.GetILGenerator()
mirrorCode.Emit(OpCodes.Ldsfld, getModuleInstanceField(sym))
0.until(paramTypes.length) foreach loadArg(mirrorCode)
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)
@ -2067,7 +2136,7 @@ abstract class GenMSIL extends SubComponent {
val dcode: ILGenerator = caller.GetILGenerator()
dcode.Emit(OpCodes.Ldsfld, anonfunField)
for (i <- 0 until params.length) {
loadArg(dcode)(i)
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)
@ -2083,7 +2152,7 @@ abstract class GenMSIL extends SubComponent {
case UNIT => code.Emit(OpCodes.Ldsfld, boxedUnit)
case BOOL | BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE =>
code.Emit(OpCodes.Box, msilType(boxType))
case REFERENCE(cls) if (definitions.unboxMethod.contains(cls)) =>
case REFERENCE(cls) if (definitions.boxMethod.contains(cls)) =>
code.Emit(OpCodes.Box, (msilType(boxType)))
case REFERENCE(_) | ARRAY(_) => ()
}

View File

@ -61,7 +61,19 @@ abstract class CLRTypes {
val methods: Map[Symbol,MethodInfo] = new HashMap
val fields: Map[Symbol, FieldInfo] = new HashMap
val sym2type: Map[Type,Symbol] = new HashMap
val addressOfViews: HashSet[Symbol] = new HashSet[Symbol]
val mdgptrcls4clssym: Map[ /*cls*/ Symbol, /*cls*/ Symbol] = new HashMap
def isAddressOf(msym : Symbol) = addressOfViews.contains(msym)
def isNonEnumValuetype(clssym : Symbol) = {
val msilTOpt = types.get(clssym)
val res = msilTOpt.isDefined && {
val msilT = msilTOpt.get
msilT.IsValueType && !msilT.IsEnum
}
res
}
def init() = try { // initialize
// the MsilClasspath (nsc/util/Classpath.scala) initializes the msil-library by calling

View File

@ -13,6 +13,7 @@ import ch.epfl.lamp.compiler.msil.{Type => MSILType, Attribute => MSILAttribute,
import scala.collection.mutable.{HashMap, HashSet}
import classfile.UnPickler
import ch.epfl.lamp.compiler.msil.Type.TMVarUsage
/**
* @author Nikolay Mihaylov
@ -64,9 +65,61 @@ abstract class TypeParser {
busy = false
}
/* 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(definitions.NothingClass.tpe, 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);
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)) {
@ -86,29 +139,115 @@ abstract class TypeParser {
return
}
val flags = translateAttributes(typ)
val ifaces: Array[MSILType] = typ.getInterfaces()
val superType = if (typ.BaseType() != null) getCLRType(typ.BaseType())
else if (typ.IsInterface()) definitions.ObjectClass.tpe
else definitions.AnyClass.tpe; // this is System.Object
val parents = superType :: ifaces.map(getCLRType).toList
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 + "Boxed")
clazzMgdPtr = clazz.owner.newClass(clazz.name + "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 = new Scope
val classInfoMgdPtr = ClassInfoType(definitions.anyvalparam, instanceDefsMgdPtr, clazzMgdPtr)
clazzMgdPtr.setFlag(flags)
clazzMgdPtr.setInfo(classInfoMgdPtr)
}
/* TODO CLR generics
// 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(NoPosition, 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
}
*/
val ownTypeParams = newTParams.toList
/* TODO CLR generics
if (!ownTypeParams.isEmpty) {
clazz.setInfo(new TypeParamsType(ownTypeParams))
if(typ.IsValueType && !typ.IsEnum) {
clazzBoxed.setInfo(new TypeParamsType(ownTypeParams))
}
}
*/
instanceDefs = new Scope
staticDefs = new Scope
val classInfo = ClassInfoType(parents, instanceDefs, clazz)
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 = new Scope
ClassInfoType(parents.toList, instanceDefsBoxed, clazzBoxed)
} else
ClassInfoType(parents.toList, instanceDefs, clazz)
}
val staticInfo = ClassInfoType(List(), staticDefs, statics)
clazz.setFlag(flags)
clazz.setInfo(classInfo)
if (canBeTakenAddressOf) {
clazzBoxed.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata
else polyType(ownTypeParams, classInfoAsInMetadata) )
clazzBoxed.setFlag(flags)
val rawValueInfoType = ClassInfoType(definitions.anyvalparam, instanceDefs, clazz)
clazz.setInfo( if (ownTypeParams.isEmpty) rawValueInfoType
else polyType(ownTypeParams, rawValueInfoType) )
} else {
clazz.setInfo( if (ownTypeParams.isEmpty) classInfoAsInMetadata
else polyType(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)
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.MSILTypeLoader(ntype)
val nclazz = statics.newClass(NoPosition, ntype.Name.toTypeName)
@ -123,13 +262,18 @@ abstract class TypeParser {
}
val fields = typ.getFields()
for (field <- fields if !(field.IsPrivate() || field.IsAssembly() || field.IsFamilyAndAssembly)) {
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)
ConstantType(getConstant(getCLRType(field.FieldType), field.getValue))
else getCLRType(field.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
@ -138,10 +282,10 @@ abstract class TypeParser {
}
for (constr <- typ.getConstructors() if !constr.IsStatic() && !constr.IsPrivate() &&
!constr.IsAssembly() && !constr.IsFamilyAndAssembly())
!constr.IsAssembly() && !constr.IsFamilyAndAssembly() && !constr.HasPtrParamOrRetType())
createMethod(constr);
// initially also contains getters an setters of properties.
// initially also contains getters and setters of properties.
val methodsSet = new HashSet[MethodInfo]();
methodsSet ++= typ.getMethods();
@ -152,7 +296,7 @@ abstract class TypeParser {
val setter: MethodInfo = prop.GetSetMethod(true);
var gparamsLength: Int = -1;
if (!(getter == null || getter.IsPrivate || getter.IsAssembly
|| getter.IsFamilyAndAssembly))
|| getter.IsFamilyAndAssembly || getter.HasPtrParamOrRetType))
{
assert(prop.PropertyType == getter.ReturnType);
val gparams: Array[ParameterInfo] = getter.GetParameters();
@ -161,7 +305,7 @@ abstract class TypeParser {
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) PolyType(List(), propType)
val mtype: Type = if (gparamsLength == 0) PolyType(List(), propType) // .NET properties can't be polymorphic
else methodType(getter, getter.ReturnType)(methodSym)
methodSym.setInfo(mtype);
methodSym.setFlag(Flags.ACCESSOR);
@ -170,7 +314,7 @@ abstract class TypeParser {
methodsSet -= getter;
}
if (!(setter == null || setter.IsPrivate || setter.IsAssembly
|| setter.IsFamilyAndAssembly))
|| setter.IsFamilyAndAssembly || setter.HasPtrParamOrRetType))
{
val sparams: Array[ParameterInfo] = setter.GetParameters()
if(getter != null)
@ -224,8 +368,35 @@ abstract class TypeParser {
}
} */
/* 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())
if (!method.IsPrivate() && !method.IsAssembly() && !method.IsFamilyAndAssembly()
&& !method.HasPtrParamOrRetType)
createMethod(method);
// Create methods and views for delegate support
@ -234,19 +405,8 @@ abstract class TypeParser {
createDelegateChainers(typ)
}
// create the box/unbox methods for value types
if (typ.IsValueType) {
val box = statics.newMethod(NoPosition, nme.box)
box.setInfo(MethodType(box.newSyntheticValueParams(List(clazz.tpe)), definitions.ObjectClass.tpe))
definitions.boxMethod(clazz) = box
val unbox = statics.newMethod(NoPosition, nme.unbox)
unbox.setInfo(MethodType(unbox.newSyntheticValueParams(List(definitions.ObjectClass.tpe)), clazz.tpe))
definitions.unboxMethod(clazz) = unbox
//Console.println(typ.FullName + " : " + parents)
}
// for enumerations introduce comparison and bitwise logical operations;
// the backend should recognize and replace them with comparison or
// the backend will recognize them and replace them with comparison or
// bitwise logical operations on the primitive underlying type
if (typ.IsEnum) {
@ -263,7 +423,7 @@ abstract class TypeParser {
for (bitLogName <- ENUM_BIT_LOG_NAMES) {
val enumBitLog = clazz.newMethod(NoPosition, bitLogName)
val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), classInfo)
val enumBitLogType = JavaMethodType(enumBitLog.newSyntheticValueParams(List(clazz.tpe)), clazz.tpe /* was classInfo, infinite typer */)
enumBitLog.setFlag(flags).setInfo(enumBitLogType)
instanceDefs.enter(enumBitLog)
}
@ -271,16 +431,49 @@ abstract class TypeParser {
} // 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(NoPosition, 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)
// TODO CLR generics val newMethodTParams = populateMethodTParams(method, methodSym)
val rettype = if (method.IsConstructor()) clazz.tpe
else getCLSType(method.asInstanceOf[MethodInfo].ReturnType);
if (rettype == null) return;
val mtype = methodType(method, rettype);
if (mtype == null) return;
val flags = translateAttributes(method);
val owner = if (method.IsStatic()) statics else clazz;
val methodSym = owner.newMethod(NoPosition, getName(method)).setFlag(flags)
methodSym.setInfo(mtype(methodSym))
/* TODO CLR generics
val mInfo = if (method.IsGeneric) polyType(newMethodTParams, mtype(methodSym))
else mtype(methodSym)
*/
val mInfo = mtype(methodSym)
methodSym.setInfo(mInfo)
(if (method.IsStatic()) staticDefs else instanceDefs).enter(methodSym);
if (method.IsConstructor())
clrTypes.constructors(methodSym) = method.asInstanceOf[ConstructorInfo]
@ -435,7 +628,7 @@ abstract class TypeParser {
/** 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
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
@ -452,21 +645,23 @@ abstract class TypeParser {
res
}
private def getCLSType(typ: MSILType): Type = {
if (/*type == clrTypes.BYTE ||*/ typ == clrTypes.USHORT
|| typ == clrTypes.UINT || typ == clrTypes.ULONG
|| typ.IsNotPublic() || typ.IsNestedPrivate()
|| typ.IsNestedAssembly() || typ.IsNestedFamANDAssem()
|| typ.IsPointer()
|| (typ.IsArray() && getCLSType(typ.GetElementType()) == null))
null;
//Symbol s = clrTypes.getSymbol(type);
//scalac.symtab.Type t = s != null ? make.classType(s) : getCLRType(type);
private def getCLSType(typ: MSILType): Type = { // getCLS returns non-null for types GenMSIL can handle, be they CLS-compliant or not
if (typ.IsTMVarUsage())
null // TODO after generics: getCLRType(typ)
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 getCLRType(typ: MSILType): Type =
private def getCLRTypeIfPrimitiveNullOtherwise(typ: MSILType): Type =
if (typ == clrTypes.OBJECT)
definitions.ObjectClass.tpe;
else if (typ == clrTypes.VALUE_TYPE)
@ -479,32 +674,67 @@ abstract class TypeParser {
definitions.BooleanClass.tpe
else if (typ == clrTypes.CHAR)
definitions.CharClass.tpe
else if (typ == clrTypes.BYTE || typ == clrTypes.UBYTE)
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.USHORT)
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)
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.ULONG)
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 if (typ.IsArray())
appliedType(definitions.ArrayClass.tpe,
List(getCLRType(typ.GetElementType())));
else if (typ.isInstanceOf[ConstructedType]) {
val ct = typ.asInstanceOf[ConstructedType]
getCLRType(ct.instantiatedType)
} else {
val res = clrTypes.sym2type.get (typ) match {
case Some(sym) => sym.tpe
case None => getClassType(typ);
}
assert (res != null, typ)
res
}
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]
/* TODO CLR generics: uncomment next two lines and comment out the hack after them
val cttpArgs = ct.typeArgs.map(tmsil => getCLRType(tmsil)).toList
appliedType(getCLRType(ct.instantiatedType), cttpArgs)
*/
getCLRType(ct.instantiatedType)
} else if (tMSIL.isInstanceOf[TMVarUsage]) {
/* TODO CLR generics: uncomment next lines and comment out the hack after them
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
*/
null // definitions.ObjectClass.tpe
} 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)
@ -533,6 +763,23 @@ abstract class TypeParser {
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()

View File

@ -24,23 +24,23 @@ public class ConstructorInfo extends MethodBase {
protected static final String CTOR = ".ctor";
protected static final String CCTOR = ".cctor";
protected static final ConstructorInfo[] EMPTY_ARRAY =
new ConstructorInfo[0];
protected static final ConstructorInfo[] EMPTY_ARRAY = new ConstructorInfo[0];
protected static String getName(int attrs) {
return (attrs & MethodAttributes.Static) == 0 ? CTOR : CCTOR;
return (attrs & MethodAttributes.Static) == 0 ? CTOR : CCTOR;
}
/** Protected constructor */
protected ConstructorInfo(Type declType, int attrs, Type[] paramTypes) {
super(getName(attrs), declType, attrs, paramTypes);
assert declType != null : "Owner can't be 'null' for a constructor!";
/** Public constructors */
public ConstructorInfo(Type declType, int attrs, Type[] paramTypes) {
super(getName(attrs), declType, attrs, paramTypes);
assert declType != null : "Owner can't be 'null' for a constructor!";
}
protected ConstructorInfo(Type declType, int attrs, ParameterInfo[] params)
public ConstructorInfo(Type declType, int attrs, ParameterInfo[] params)
{
super(getName(attrs), declType, attrs, params);
assert declType != null : "Owner can't be 'null' for a constructor!";
super(getName(attrs), declType, attrs, params);
assert declType != null : "Owner can't be 'null' for a constructor!";
}

View File

@ -5,6 +5,8 @@
package ch.epfl.lamp.compiler.msil;
import java.util.Iterator;
/**
* The common superclass of MemberInfo and ConstructorInfo
*
@ -16,6 +18,34 @@ public abstract class MethodBase extends MemberInfo {
//##########################################################################
// public interface
private java.util.List /* GenericParamAndConstraints */ mVars = new java.util.LinkedList();
private GenericParamAndConstraints[] sortedMVars = null;
public void addMVar(GenericParamAndConstraints tvarAndConstraints) {
sortedMVars = null;
mVars.add(tvarAndConstraints);
}
public GenericParamAndConstraints[] getSortedMVars() {
if(sortedMVars == null) {
sortedMVars = new GenericParamAndConstraints[mVars.size()];
for (int i = 0; i < sortedMVars.length; i ++){
Iterator iter = mVars.iterator();
while(iter.hasNext()) {
GenericParamAndConstraints tvC = (GenericParamAndConstraints)iter.next();
if(tvC.Number == i) {
sortedMVars[i] = tvC;
}
}
}
}
return sortedMVars;
}
public final boolean IsGeneric() {
return mVars.size() > 0;
}
/** The attributes associated with this method/constructor. */
public final short Attributes;
@ -36,6 +66,10 @@ public abstract class MethodBase extends MemberInfo {
return (Attributes& MethodAttributes.Virtual) != 0;
}
public final boolean IsInstance() {
return !IsStatic() && !IsVirtual();
}
public final boolean IsStatic() {
return (Attributes & MethodAttributes.Static) != 0;
}
@ -79,6 +113,26 @@ public abstract class MethodBase extends MemberInfo {
== MethodAttributes.FamANDAssem;
}
public boolean HasPtrParamOrRetType() {
// the override in MethodInfo checks the return type
ParameterInfo[] ps = GetParameters();
for (int i = 0; i < ps.length; i++) {
Type pT = ps[i].ParameterType;
if(pT.IsPointer()) {
// Type.mkPtr creates a msil.Type for a pointer type
return true;
}
if(pT.IsByRef() && !pT.GetElementType().CanBeTakenAddressOf()) {
/* TODO Cases where GenMSIL (so far) con't emit good bytecode:
the type being taken address of IsArray(), IsGeneric(), or IsTMVarUsage.
For example, System.Enum declares
public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct, new();
*/
return true;
}
}
return false;
}
/** Returns the parameters of the method/constructor. */
public ParameterInfo[] GetParameters() {

View File

@ -15,32 +15,17 @@ import java.util.Iterator;
*/
public class MethodInfo extends MethodBase {
private java.util.List /* GenericParamAndConstraints */ mVars = new java.util.LinkedList();
private GenericParamAndConstraints[] sortedMVars = null;
public void addMVar(GenericParamAndConstraints tvarAndConstraints) {
sortedMVars = null;
mVars.add(tvarAndConstraints);
}
public GenericParamAndConstraints[] getSortedMVars() {
if(sortedMVars == null) {
sortedMVars = new GenericParamAndConstraints[mVars.size()];
for (int i = 0; i < sortedMVars.length; i ++){
Iterator iter = mVars.iterator();
while(iter.hasNext()) {
GenericParamAndConstraints tvC = (GenericParamAndConstraints)iter.next();
if(tvC.Number == i) {
sortedMVars[i] = tvC;
}
}
}
public boolean HasPtrParamOrRetType() {
if(ReturnType.IsByRef() && !(ReturnType.GetElementType().IsValueType())) {
/* A method returning ByRef won't pass peverify, so I guess this is dead code. */
return true;
}
return sortedMVars;
if(ReturnType.IsPointer()) {
return true;
}
return super.HasPtrParamOrRetType();
}
//##########################################################################
// public members

View File

@ -285,6 +285,19 @@ public abstract class Type extends MemberInfo {
public final boolean IsEnum() {
return BaseType() == ENUM();
}
public boolean CanBeTakenAddressOf() {
/* TODO should be overridden in TMVarUsage,
but there's currently no way to bind a TMVarUsage to its GenericParamAndConstraints definition. Why?
Because of the way the msil library is organized (e.g., mkArray() returns the same !0[] representation
for all !0[] usages, irrespective of the scope of the !0 type-param)
This in turn is so because without generics there's no harm in using a type-def instance
where a type-ref should go (e.g., the ParameterType of a ParameterInfo nowadays may point to a PEType).
The net effect is that this method (CanBeTakenAddressOf) is conservative, it will answer "no"
for example for !0 where !0 refers to a type-param with the isValuetype constraint set.
The whole thing is ok at this point in time, where generics are not supported at the backend. */
return IsValueType() && (this != ENUM());
/* ENUM() is a singleton, i.e. System.Enum is not generic */
}
/** IsGeneric, true for a PEType or TypeBuilder (i.e., a type definition)
* containing one or more type params. Not to be called on a reference
@ -325,7 +338,8 @@ public abstract class Type extends MemberInfo {
public final int Number;
public final boolean isTVar;
/** Non-defining reference to either a TVar or an MVar */
/** Non-defining reference to either a TVar or an MVar.
* An instance of GenericParamAndConstraints represents a TVar or an MVar definition. */
public TMVarUsage(int Number, boolean isTVar) {
super(null, 0, ((isTVar ? "!" : "!!") + Number), null, null, null, AuxAttr.None, null);
this.Number = Number;
@ -378,7 +392,7 @@ public abstract class Type extends MemberInfo {
if (array != null)
return array;
array = new PrimitiveType(elemType.Module,
TypeAttributes.Public
elemType.Attributes
| TypeAttributes.Sealed
| TypeAttributes.Serializable,
elemType.FullName + arrSig,
@ -393,7 +407,7 @@ public abstract class Type extends MemberInfo {
Type type = getType(name);
if (type != null) return type;
type = new PrimitiveType(elemType.Module,
TypeAttributes.NotPublic,
elemType.Attributes,
name, null, EmptyTypes, null,
AuxAttr.Pointer, elemType);
return addType(type);
@ -405,7 +419,7 @@ public abstract class Type extends MemberInfo {
Type type = getType(name);
if (type != null) return type;
type = new PrimitiveType(elemType.Module,
TypeAttributes.NotPublic,
elemType.Attributes,
name, null, EmptyTypes, null,
AuxAttr.ByRef, elemType);
return addType(type);

View File

@ -450,7 +450,7 @@ abstract class ILPrinterVisitor extends Visitor {
def caseOpCode(opCode: OpCode) {
var opString = opCode.toString()
print(opString)
pad(12 - opString.length())
pad(14 - opString.length())
// switch opcode
if (opCode == OpCode.Ldstr) {
@ -484,9 +484,8 @@ abstract class ILPrinterVisitor extends Visitor {
print(" \'"); print(loc.name); print("\'")
//print("'") print(((LocalBuilder)argument).name) print("'")
} else if (opCode == OpCode.Readonly) {
println("readonly. ")
// nothing to do
} else if (opCode == OpCode.Constrained) {
print("constrained. ")
printReference(argument.asInstanceOf[Type])
} else if (opCode == OpCode.Ldelema) {
printReference(argument.asInstanceOf[Type])

View File

@ -805,13 +805,13 @@ object OpCode {
* constrained prefix
*/
final val Constrained = new OpCode()
opcode(Constrained, CEE_CONSTRAINED , "constrained" , 0xFFFFFE16, POP_NONE, PUSH_NONE, INLINE_NONE , FLOW_NEXT)
opcode(Constrained, CEE_CONSTRAINED , "constrained." , 0xFFFFFE16, POP_NONE, PUSH_NONE, INLINE_NONE , FLOW_NEXT)
/**
* readonly prefix
*/
final val Readonly = new OpCode()
opcode(Readonly, CEE_READONLY , "readonly" , 0xFFFFFE1E, POP_NONE, PUSH_NONE, INLINE_NONE , FLOW_NEXT)
opcode(Readonly, CEE_READONLY , "readonly." , 0xFFFFFE1E, POP_NONE, PUSH_NONE, INLINE_NONE , FLOW_NEXT)
/**
* Calls the method indicated on the evaluation stack (as a pointer to an entry point)

View File

@ -235,7 +235,17 @@ object OpCodes {
final val Call = OpCode.Call
/**
* Calls the method indicated on the evaluation stack (as a pointer to an entry point)
* constrained. prefix
*/
final val Constrained = OpCode.Constrained
/**
* readonly. prefix
*/
final val Readonly = OpCode.Readonly
/**
* Calls the method indicated on the evaluation stack (as a pointer to an entry point)
* with arguments described by a calling convention.
*/
final val Calli = OpCode.Calli