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:
parent
f9dad11629
commit
bfb6fbb76d
BIN
lib/msil.jar
BIN
lib/msil.jar
Binary file not shown.
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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(_) => ()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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!";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue