summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.
*/