summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiguel Garcia <magarcia@epfl.ch>2010-08-24 07:38:58 +0000
committerMiguel Garcia <magarcia@epfl.ch>2010-08-24 07:38:58 +0000
commite987f72df36f33bd40c99792b7b97aeebe2ef605 (patch)
treefb09417d3ec4f078ecb396774942f95ff6af88a5
parent79a7191e605d22ffe9d2142039f586682c6902d6 (diff)
downloadscala-e987f72df36f33bd40c99792b7b97aeebe2ef605.tar.gz
scala-e987f72df36f33bd40c99792b7b97aeebe2ef605.tar.bz2
scala-e987f72df36f33bd40c99792b7b97aeebe2ef605.zip
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/Bootstr apping3.pdf [2] http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2010Q3/Bootstr apping4.pdf [3] http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/2010Q2/SigToTy pe.pdf
-rw-r--r--lib/msil.jar.desired.sha12
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala199
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala60
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala215
-rw-r--r--src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala14
-rw-r--r--src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala383
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/ConstructorInfo.java20
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/MethodBase.java54
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java31
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/Type.java22
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/emit/ILPrinterVisitor.scala5
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/emit/OpCode.scala4
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/emit/OpCodes.scala10
14 files changed, 830 insertions, 195 deletions
diff --git a/lib/msil.jar.desired.sha1 b/lib/msil.jar.desired.sha1
index 0997c0773e..f309ebd5ae 100644
--- a/lib/msil.jar.desired.sha1
+++ b/lib/msil.jar.desired.sha1
@@ -1 +1 @@
-f328123333822ed034a65392cf4df6f9dec542b8 ?msil.jar
+bfddab220ed24fc71057820192ad2fcf26b6486f ?msil.jar
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 27bf9ac29b..d5cafccd30 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -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)
@@ -458,6 +460,146 @@ abstract class GenICode extends SubComponent {
}
/**
+ * 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
*
* @param tree The tree to be translated
@@ -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.
*/
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
index aa53a22ade..7024687e0e 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/Opcodes.scala
@@ -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))
+ }
+
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
index 65a5291f36..24e025518e 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/TypeKinds.scala
@@ -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)
+ }
+
}
diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
index 707cf7cf59..937d1e3d45 100644
--- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
+++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
@@ -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(_) => ()
}
diff --git a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala b/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala
index f62a42375a..1b18def1a0 100644
--- a/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala
+++ b/src/compiler/scala/tools/nsc/symtab/clr/CLRTypes.scala
@@ -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
diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala
index f661f7bb31..975b6bfc49 100644
--- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala
@@ -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()
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/ConstructorInfo.java b/src/msil/ch/epfl/lamp/compiler/msil/ConstructorInfo.java
index 99e5c5fe69..69f5d6d32a 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/ConstructorInfo.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/ConstructorInfo.java
@@ -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!";
}
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/MethodBase.java b/src/msil/ch/epfl/lamp/compiler/msil/MethodBase.java
index 793ee362e9..fe6404346e 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/MethodBase.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/MethodBase.java
@@ -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() {
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java b/src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java
index 8c53a768fc..a415e7551f 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java
@@ -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
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/Type.java b/src/msil/ch/epfl/lamp/compiler/msil/Type.java
index 78704062b7..a4de4ae11b 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/Type.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/Type.java
@@ -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);
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILPrinterVisitor.scala b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILPrinterVisitor.scala
index cc359b813f..66f53b8132 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILPrinterVisitor.scala
+++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILPrinterVisitor.scala
@@ -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])
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCode.scala b/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCode.scala
index bfccc42214..fbcdbf893f 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCode.scala
+++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCode.scala
@@ -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)
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCodes.scala b/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCodes.scala
index db2a6fedc7..d486c31af0 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCodes.scala
+++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCodes.scala
@@ -235,6 +235,16 @@ object OpCodes {
final val Call = OpCode.Call
/**
+ * 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.
*/