summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/msil.jar.desired.sha12
-rw-r--r--src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala141
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala3
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala11
-rw-r--r--src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala78
-rw-r--r--src/compiler/scala/tools/nsc/transform/CleanUp.scala2
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/Assembly.java4
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/ConstructedType.java48
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/GenericParamAndConstraints.java40
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java28
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/PEFile.java88
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/PEModule.java129
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/PEType.java37
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/PrimitiveType.java59
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/Type.java101
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala7
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/emit/ILPrinterVisitor.scala93
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/emit/OpCode.scala14
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/emit/SingleFileILPrinterVisitor.scala2
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/util/Signature.java13
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/util/Table.java268
21 files changed, 1062 insertions, 106 deletions
diff --git a/lib/msil.jar.desired.sha1 b/lib/msil.jar.desired.sha1
index a5722deadd..0997c0773e 100644
--- a/lib/msil.jar.desired.sha1
+++ b/lib/msil.jar.desired.sha1
@@ -1 +1 @@
-682f60e7a3315c8dc3e7a39c10ba8069f0b0fca4 ?msil.jar
+f328123333822ed034a65392cf4df6f9dec542b8 ?msil.jar
diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
index 174a1b778e..2bdf8a3a73 100644
--- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
+++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala
@@ -286,7 +286,7 @@ abstract class GenMSIL extends SubComponent {
symtab(i) = (size & 0xff).toByte
size = size >> 8
}
- System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex)
+ java.lang.System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex)
tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab)
@@ -461,7 +461,8 @@ abstract class GenMSIL extends SubComponent {
if (settings.debug.value)
log("Output path: " + filename)
try {
- massembly.Save(filename, srcPath.getPath())
+ 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())
}
@@ -545,10 +546,10 @@ abstract class GenMSIL extends SubComponent {
mcode = mBuilder.GetILGenerator()
} catch {
case e: Exception =>
- System.out.println("m.symbol = " + Flags.flagsToString(m.symbol.flags) + " " + m.symbol)
- System.out.println("m.symbol.owner = " + Flags.flagsToString(m.symbol.owner.flags) + " " + m.symbol.owner)
- System.out.println("mBuilder = " + mBuilder)
- System.out.println("mBuilder.DeclaringType = " +
+ java.lang.System.out.println("m.symbol = " + Flags.flagsToString(m.symbol.flags) + " " + m.symbol)
+ java.lang.System.out.println("m.symbol.owner = " + Flags.flagsToString(m.symbol.owner.flags) + " " + m.symbol.owner)
+ java.lang.System.out.println("mBuilder = " + mBuilder)
+ java.lang.System.out.println("mBuilder.DeclaringType = " +
TypeAttributes.toString(mBuilder.DeclaringType.Attributes) +
"::" + mBuilder.DeclaringType)
throw e
@@ -947,7 +948,45 @@ abstract class GenMSIL extends SubComponent {
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 LOAD_MODULE(module) =>
if (settings.debug.value)
@@ -1113,13 +1152,13 @@ abstract class GenMSIL extends SubComponent {
case MethodType(_, retType) => retType
case _ => abort("not a method type: " + msym.tpe)
}
- val method: MethodInfo = getMethod(methodSym)
+ val methodInfo: MethodInfo = getMethod(methodSym)
val delegCtor = msilType(delegateType).GetConstructor(Array(MOBJECT, INT_PTR))
if (methodSym.isStatic) {
- mcode.Emit(OpCodes.Ldftn, method)
+ mcode.Emit(OpCodes.Ldftn, methodInfo)
} else {
mcode.Emit(OpCodes.Dup)
- mcode.Emit(OpCodes.Ldvirtftn, method)
+ mcode.Emit(OpCodes.Ldvirtftn, methodInfo)
}
mcode.Emit(OpCodes.Newobj, delegCtor)
}
@@ -1130,13 +1169,20 @@ abstract class GenMSIL extends SubComponent {
case SuperCall(_) =>
mcode.Emit(OpCodes.Call, methodInfo)
case Dynamic =>
- mcode.Emit(if (dynToStatMapped(msym)) OpCodes.Call else OpCodes.Callvirt,
- methodInfo)
- case Static(_) =>
+ if (dynToStatMapped(msym)) {
mcode.Emit(OpCodes.Call, methodInfo)
+ } else if ((methodInfo.DeclaringType ne null) && (methodInfo.DeclaringType.IsValueType)) {
+ mcode.Emit(OpCodes.Call, methodInfo)
+ } else {
+ mcode.Emit(OpCodes.Callvirt, methodInfo)
}
+ case Static(_) =>
+ if(methodInfo.IsVirtual && !mcode.Ldarg0WasJustEmitted) {
+ mcode.Emit(OpCodes.Callvirt, methodInfo)
+ } else mcode.Emit(OpCodes.Call, methodInfo)
}
}
+ }
case BOX(boxType) => emitBox(mcode, boxType)
@@ -1253,7 +1299,7 @@ abstract class GenMSIL extends SubComponent {
}
} // end for (instr <- b) { .. }
- }
+ } // end genBlock
def genPrimitive(primitive: Primitive, pos: Position) {
primitive match {
@@ -1329,7 +1375,7 @@ abstract class GenMSIL extends SubComponent {
case _ =>
abort("Unimplemented primitive " + primitive)
}
- }
+ } // end genPrimitive
////////////////////// loading ///////////////////////
@@ -1379,8 +1425,8 @@ abstract class GenMSIL extends SubComponent {
////////////////////// branches ///////////////////////
/** Returns a Triple (Boolean, Boolean, Option[Label])
- * - wether the jump leaves some exception block (try / catch / finally)
- * - wether the it leaves a finally handler (finally block, but not it's try / catch)
+ * - whether the jump leaves some exception block (try / catch / finally)
+ * - whether it leaves a finally handler (finally block, but not it's try / catch)
* - a label where to jump for leaving the finally handler
* . None to leave directly using `endfinally`
* . Some(label) to emit `leave label` (for try / catch inside a finally handler)
@@ -1485,11 +1531,9 @@ abstract class GenMSIL extends SubComponent {
* method.
*/
def computeLocalVarsIndex(m: IMethod) {
- val params = m.params
- var idx = 1
- if (m.symbol.isStaticMember)
- idx = 0
+ var idx = if (m.symbol.isStaticMember) 0 else 1
+ val params = m.params
for (l <- params) {
if (settings.debug.value)
log("Index value for parameter " + l + ": " + idx)
@@ -1670,7 +1714,7 @@ abstract class GenMSIL extends SubComponent {
msilType(elem) match {
// For type builders, cannot call "clrTypes.mkArrayType" because this looks up
// the type "tp" in the assembly (not in the HashMap "types" of the backend).
- // This can fail for nested types because the biulders are not complete yet.
+ // This can fail for nested types because the builders are not complete yet.
case tb: TypeBuilder => tb.MakeArrayType()
case tp: MsilType => clrTypes.mkArrayType(tp)
}
@@ -1685,7 +1729,7 @@ abstract class GenMSIL extends SubComponent {
def getType(sym: Symbol) = getTypeOpt(sym).getOrElse(abort(showsym(sym)))
/**
- * Get an MSIL type form a symbol. First look in the clrTypes.types map, then
+ * Get an MSIL type from a symbol. First look in the clrTypes.types map, then
* lookup the name using clrTypes.getType
*/
def getTypeOpt(sym: Symbol): Option[MsilType] = types.get(sym) match {
@@ -1713,31 +1757,40 @@ abstract class GenMSIL extends SubComponent {
def createTypeBuilder(iclass: IClass) {
/**
- * First look in the clrTypes.types map, then see if it's a class we're
- * currently compiling by looking at the icodes.classes map, then finally
- * lookup the name using clrTypes.getType (by calling getType).
+ * First look in the clrTypes.types map, if that fails check if it's a class being compiled, otherwise
+ * lookup by name (clrTypes.getType calls the static method msil.Type.GetType(fullname)).
*/
def msilTypeFromSym(sym: Symbol): MsilType = {
types.get(sym).getOrElse {
classes.get(sym) match {
case Some(iclass) =>
- createTypeBuilder(iclass)
- types (sym)
+ msilTypeBuilderFromSym(sym)
case None =>
getType(sym)
}
}
}
+ def msilTypeBuilderFromSym(sym: Symbol): TypeBuilder = {
+ if(!(types.contains(sym) && types(sym).isInstanceOf[TypeBuilder])){
+ val iclass = classes(sym)
+ assert(iclass != null)
+ createTypeBuilder(iclass)
+ }
+ types(sym).asInstanceOf[TypeBuilder]
+ }
+
val sym = iclass.symbol
- if (types contains sym) return
+ if (types contains sym)
+ if (types(sym).isInstanceOf[TypeBuilder])
+ return
def isInterface(s: Symbol) = s.isTrait && !s.isImplClass
val parents: List[Type] =
if (sym.info.parents.isEmpty) List(definitions.ObjectClass.tpe)
else sym.info.parents.distinct
- val superType = if (isInterface(sym)) null else msilTypeFromSym(parents.head.typeSymbol)
+ val superType : MsilType = if (isInterface(sym)) null else msilTypeFromSym(parents.head.typeSymbol)
if (settings.debug.value)
log("super type: " + parents(0).typeSymbol + ", msil type: " + superType)
@@ -1752,16 +1805,13 @@ abstract class GenMSIL extends SubComponent {
}
}
- if (sym.isNestedClass) {
- val ownerT = msilTypeFromSym(sym.owner).asInstanceOf[TypeBuilder]
- val tBuilder =
- ownerT.DefineNestedType(msilName(sym), msilTypeFlags(sym), superType, interfaces)
- mapType(sym, tBuilder)
+ val tBuilder = if (sym.isNestedClass) {
+ val ownerT = msilTypeBuilderFromSym(sym.owner).asInstanceOf[TypeBuilder]
+ ownerT.DefineNestedType(msilName(sym), msilTypeFlags(sym), superType, interfaces)
} else {
- val tBuilder =
- mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces)
- mapType(sym, tBuilder)
+ mmodule.DefineType(msilName(sym), msilTypeFlags(sym), superType, interfaces)
}
+ mapType(sym, tBuilder)
} // createTypeBuilder
def createClassMembers(iclass: IClass) {
@@ -1770,8 +1820,8 @@ abstract class GenMSIL extends SubComponent {
}
catch {
case e: Throwable =>
- System.err.println(showsym(iclass.symbol))
- System.err.println("with methods = " + iclass.methods)
+ java.lang.System.err.println(showsym(iclass.symbol))
+ java.lang.System.err.println("with methods = " + iclass.methods)
throw e
}
}
@@ -1789,7 +1839,7 @@ abstract class GenMSIL extends SubComponent {
addAttributes(fBuilder, sym.annotations)
}
- if (iclass.symbol != definitions.ArrayClass)
+ if (iclass.symbol != definitions.ArrayClass) {
for (m: IMethod <- iclass.methods) {
val sym = m.symbol
if (settings.debug.value)
@@ -1823,6 +1873,7 @@ abstract class GenMSIL extends SubComponent {
log("\t created MethodBuilder " + method)
}
}
+ } // method builders created for non-array iclass
if (isStaticModule(iclass.symbol)) {
addModuleInstanceField(iclass.symbol)
@@ -1830,7 +1881,7 @@ abstract class GenMSIL extends SubComponent {
addStaticInit(iclass.symbol)
}
- } // createClassMembers
+ } // createClassMembers0
private def isTopLevelModule(sym: Symbol): Boolean =
atPhase (currentRun.refchecksPhase) {
@@ -2063,8 +2114,8 @@ abstract class GenMSIL extends SubComponent {
val mClass = getType(sym.owner)
val constr = mClass.GetConstructor(msilParamTypes(sym))
if (constr eq null) {
- System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name)
- System.out.println("scope = " + sym.owner.tpe.decls)
+ java.lang.System.out.println("Cannot find constructor " + sym.owner + "::" + sym.name)
+ java.lang.System.out.println("scope = " + sym.owner.tpe.decls)
abort(sym.fullName)
}
else {
@@ -2097,8 +2148,8 @@ abstract class GenMSIL extends SubComponent {
val method = mClass.GetMethod(getMethodName(sym), msilParamTypes(sym),
msilType(sym.tpe.resultType))
if (method eq null) {
- System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym))
- System.out.println("scope = " + sym.owner.tpe.decls)
+ java.lang.System.out.println("Cannot find method " + sym.owner + "::" + msilName(sym))
+ java.lang.System.out.println("scope = " + sym.owner.tpe.decls)
abort(sym.fullName)
}
else {
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 198c225e8b..d9e453291f 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -108,8 +108,9 @@ trait Definitions extends reflect.generic.StandardDefinitions {
lazy val IndexOutOfBoundsExceptionClass = getClass(sn.IOOBException)
lazy val UninitializedErrorClass = getClass("scala.UninitializedFieldError")
lazy val MatchErrorClass = getClass("scala.MatchError")
+ lazy val InvocationTargetExceptionClass = getClass(if (forMSIL) "System.Reflection.TargetInvocationException"
+ else "java.lang.reflect.InvocationTargetException")
// java is hard coded because only used by structural values
- lazy val InvocationTargetExceptionClass = getClass("java.lang.reflect.InvocationTargetException")
lazy val NoSuchMethodExceptionClass = getClass("java.lang.NoSuchMethodException")
// annotations
diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
index 8c4078e91e..aa47441a1a 100644
--- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala
+++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
@@ -299,14 +299,15 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable =>
val forName = newTermName(if (forMSIL) "GetType" else "forName")
val foreach = newTermName("foreach")
val get = newTermName("get")
- val getCause = newTermName("getCause")
- val getClass_ = newTermName("getClass")
- val getMethod_ = newTermName("getMethod")
+ val getCause = newTermName(if (forMSIL) "InnerException" /* System.Reflection.TargetInvocationException.InnerException */
+ else "getCause")
+ val getClass_ = newTermName(if (forMSIL) "GetType" else "getClass")
+ val getMethod_ = newTermName(if (forMSIL) "GetMethod" else "getMethod")
val hash_ = newTermName("hash")
val hashCode_ = newTermName("hashCode")
val hasNext = newTermName("hasNext")
val head = newTermName("head")
- val invoke_ = newTermName("invoke")
+ val invoke_ = newTermName(if (forMSIL) "Invoke" else "invoke")
val isArray = newTermName("isArray")
val isInstanceOf_ = newTermName("isInstanceOf")
val isDefinedAt = newTermName("isDefinedAt")
@@ -496,7 +497,7 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable =>
final val BoxedNumber = newTermName("System.IConvertible")
final val BoxedCharacter = newTermName("System.IConvertible")
final val BoxedBoolean = newTermName("System.IConvertible")
- final val MethodAsObject = nme.NOSYMBOL // TODO: is there something like Method in MSIL?
+ final val MethodAsObject = newTermName("System.Reflection.MethodInfo")
Boxed += (nme.Boolean -> newTermName("System.Boolean"))
Boxed += (nme.Byte -> newTermName("System.Byte"))
diff --git a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala
index a90fb8b66c..f661f7bb31 100644
--- a/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/clr/TypeParser.scala
@@ -335,9 +335,80 @@ abstract class TypeParser {
}
private def getName(method: MethodBase): Name = {
+
+ def operatorOverload(name : String, paramsArity : Int) : Option[Name] = paramsArity match {
+ case 1 => name match {
+ // PartitionI.10.3.1
+ case "op_Decrement" => Some(encode("--"))
+ case "op_Increment" => Some(encode("++"))
+ case "op_UnaryNegation" => Some(nme.UNARY_-)
+ case "op_UnaryPlus" => Some(nme.UNARY_+)
+ case "op_LogicalNot" => Some(nme.UNARY_!)
+ case "op_OnesComplement" => Some(nme.UNARY_~)
+ /* op_True and op_False have no operator symbol assigned,
+ Other methods that will have to be written in full are:
+ op_AddressOf & (unary)
+ op_PointerDereference * (unary) */
+ case _ => None
+ }
+ case 2 => name match {
+ // PartitionI.10.3.2
+ case "op_Addition" => Some(nme.ADD)
+ case "op_Subtraction" => Some(nme.SUB)
+ case "op_Multiply" => Some(nme.MUL)
+ case "op_Division" => Some(nme.DIV)
+ case "op_Modulus" => Some(nme.MOD)
+ case "op_ExclusiveOr" => Some(nme.XOR)
+ case "op_BitwiseAnd" => Some(nme.AND)
+ case "op_BitwiseOr" => Some(nme.OR)
+ case "op_LogicalAnd" => Some(nme.ZAND)
+ case "op_LogicalOr" => Some(nme.ZOR)
+ case "op_LeftShift" => Some(nme.LSL)
+ case "op_RightShift" => Some(nme.ASR)
+ case "op_Equality" => Some(nme.EQ)
+ case "op_GreaterThan" => Some(nme.GT)
+ case "op_LessThan" => Some(nme.LT)
+ case "op_Inequality" => Some(nme.NE)
+ case "op_GreaterThanOrEqual" => Some(nme.GE)
+ case "op_LessThanOrEqual" => Some(nme.LE)
+
+ /* op_MemberSelection is reserved in Scala */
+
+ /* The standard does not assign operator symbols to op_Assign , op_SignedRightShift , op_UnsignedRightShift ,
+ * and op_UnsignedRightShiftAssignment so those names will be used instead to invoke those methods. */
+
+ /*
+ The remaining binary operators are not overloaded in C# and are therefore not in widespread use. They have to be written in full.
+
+ op_RightShiftAssignment >>=
+ op_MultiplicationAssignment *=
+ op_PointerToMemberSelection ->*
+ op_SubtractionAssignment -=
+ op_ExclusiveOrAssignment ^=
+ op_LeftShiftAssignment <<=
+ op_ModulusAssignment %=
+ op_AdditionAssignment +=
+ op_BitwiseAndAssignment &=
+ op_BitwiseOrAssignment |=
+ op_Comma ,
+ op_DivisionAssignment /=
+ */
+ case _ => None
+ }
+ case _ => None
+ }
+
if (method.IsConstructor()) return nme.CONSTRUCTOR;
val name = method.Name;
- if (method.IsStatic()) return newTermName(name);
+ if (method.IsStatic()) {
+ if(method.IsSpecialName) {
+ val paramsArity = method.GetParameters().size
+ // handle operator overload, otherwise handle as any static method
+ val operName = operatorOverload(name, paramsArity)
+ if (operName.isDefined) { return operName.get; }
+ }
+ return newTermName(name);
+ }
val params = method.GetParameters();
name match {
case "GetHashCode" if (params.length == 0) => nme.hashCode_;
@@ -423,7 +494,10 @@ abstract class TypeParser {
else if (typ.IsArray())
appliedType(definitions.ArrayClass.tpe,
List(getCLRType(typ.GetElementType())));
- else {
+ 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);
diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
index 9b569fa45e..d613124013 100644
--- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala
+++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -530,7 +530,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
newStaticInits.clear
symbolsStoredAsStatic.clear
val transformedTemplate: Template =
- if (!forMSIL) {
+ if (!forMSIL || forMSIL) {
var newBody =
transformTrees(body)
treeCopy.Template(tree, parents, self, transformTrees(newStaticMembers.toList) ::: newBody)
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/Assembly.java b/src/msil/ch/epfl/lamp/compiler/msil/Assembly.java
index 0d7102c305..d2adf3750a 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/Assembly.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/Assembly.java
@@ -118,7 +118,9 @@ public abstract class Assembly extends CustomAttributeProvider {
PEFile pefile = null;
try { pefile = new PEFile(f.getAbsolutePath()); }
catch (FileNotFoundException e) {}
- catch (RuntimeException e) {}
+ catch (RuntimeException e) {
+ java.lang.System.out.println("swallowed RuntimeException at getPEFile");
+ }
return pefile;
}
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/ConstructedType.java b/src/msil/ch/epfl/lamp/compiler/msil/ConstructedType.java
new file mode 100644
index 0000000000..dee67fda43
--- /dev/null
+++ b/src/msil/ch/epfl/lamp/compiler/msil/ConstructedType.java
@@ -0,0 +1,48 @@
+package ch.epfl.lamp.compiler.msil;
+
+import java.util.Arrays;
+
+/* The only reason for ConstructedType to extend Type is complying with existing code
+ (e.g., caseFieldBuilder in ILPrinterVisitor) expecting a Type.
+ */
+public class ConstructedType extends Type {
+
+ public final Type instantiatedType;
+ public final Type[] typeArgs;
+
+ public ConstructedType(Type instantiatedType, Type[] typeArgs) {
+ super (null, instantiatedType.Attributes, "", null, null, null, instantiatedType.auxAttr /*AuxAttr.None*/ , null);
+ this.instantiatedType = instantiatedType;
+ this.typeArgs = typeArgs;
+ }
+
+ public String toString() {
+ String res = instantiatedType.toString() + "[";
+ for (int i = 0; i < typeArgs.length; i++) {
+ res = res + typeArgs[i].toString();
+ if(i + 1 < typeArgs.length) {
+ res = res + ", ";
+ }
+ }
+ return res + "]";
+ }
+
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ConstructedType that = (ConstructedType) o;
+
+ if (!instantiatedType.equals(that.instantiatedType)) return false;
+ if (!Arrays.equals(typeArgs, that.typeArgs)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result = instantiatedType.hashCode();
+ result = 31 * result + Arrays.hashCode(typeArgs);
+ return result;
+ }
+}
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/GenericParamAndConstraints.java b/src/msil/ch/epfl/lamp/compiler/msil/GenericParamAndConstraints.java
new file mode 100644
index 0000000000..6237fbafee
--- /dev/null
+++ b/src/msil/ch/epfl/lamp/compiler/msil/GenericParamAndConstraints.java
@@ -0,0 +1,40 @@
+package ch.epfl.lamp.compiler.msil;
+
+/**
+ * @author Miguel Garcia
+ */
+public class GenericParamAndConstraints {
+
+ public GenericParamAndConstraints(int Number, String Name, Type[] Constraints,
+ boolean isInvariant, boolean isCovariant, boolean isContravariant,
+ boolean isReferenceType, boolean isValueType, boolean hasDefaultConstructor) {
+ this.Number = Number;
+ this.Name = Name;
+ this.Constraints = Constraints; // TODO representation for the class and new() constraints missing
+ this.isInvariant = isInvariant;
+ this.isCovariant = isCovariant;
+ this.isContravariant = isContravariant;
+ this.isReferenceType = isReferenceType;
+ this.isValueType = isValueType;
+ this.hasDefaultConstructor = hasDefaultConstructor;
+
+ }
+
+ public final int Number;
+ public final String Name; // can be null
+ public final Type[] Constraints; // can be empty array
+ public final boolean isInvariant; // only relevant for TVars, not for an MVar
+ public final boolean isCovariant; // only relevant for TVars, not for an MVar
+ public final boolean isContravariant; // only relevant for TVars, not for an MVar
+ public final boolean isReferenceType;
+ public final boolean isValueType;
+ public final boolean hasDefaultConstructor;
+
+ public String toString() {
+ String res = Name == null ? "<NoName>" : (Name.equals("") ? "<NoName>" : Name);
+ res = res + " <: " + Constraints;
+ return res;
+ }
+
+}
+
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java b/src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java
index 5e227fba35..8c53a768fc 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/MethodInfo.java
@@ -5,6 +5,8 @@
package ch.epfl.lamp.compiler.msil;
+import java.util.Iterator;
+
/**
* Discovers the attributes of a method and provides access to method metadata.
*
@@ -13,6 +15,32 @@ package ch.epfl.lamp.compiler.msil;
*/
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;
+ }
+ }
+ }
+ }
+ return sortedMVars;
+ }
+
+
+
//##########################################################################
// public members
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/PEFile.java b/src/msil/ch/epfl/lamp/compiler/msil/PEFile.java
index f84598e20b..3ac90291b4 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/PEFile.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/PEFile.java
@@ -107,7 +107,7 @@ public class PEFile {
/** Ecma 335, 25.2.1 MS-DOS header:
*
* "At offset 0x3c in the DOS header is a 4-byte unsigned integer offset, lfanew,
- * to the PE signature (shall be “PE\0\0”), immediately followed by the PE file header.
+ * to the PE signature (shall be "PE\0\0"), immediately followed by the PE file header.
*/
seek(0x3c);
@@ -286,6 +286,27 @@ public class PEFile {
public ParamDef ParamDef;
public ParamDef ParamDef(int i) { ParamDef.readRow(i); return ParamDef; }
+ public GenericParam GenericParam;
+
+ public GenericParam GenericParam(int i) {
+ GenericParam.readRow(i);
+ return GenericParam;
+ }
+
+ public MethodSpec MethodSpec;
+
+ public MethodSpec MethodSpec(int i) {
+ MethodSpec.readRow(i);
+ return MethodSpec;
+ }
+
+ public GenericParamConstraint GenericParamConstraint;
+
+ public GenericParamConstraint GenericParamConstraint(int i) {
+ GenericParamConstraint.readRow(i);
+ return GenericParamConstraint;
+ }
+
public InterfaceImpl InterfaceImpl;
public MemberRef MemberRef;
public Constant Constant;
@@ -348,6 +369,9 @@ public class PEFile {
NestedClass = (NestedClass) getTable(Table.NestedClass.ID);
ManifestResource =
(ManifestResource) getTable(Table.ManifestResource.ID);
+ GenericParam = (GenericParam) getTable(Table.GenericParam.ID);
+ MethodSpec = (MethodSpec) getTable(Table.MethodSpec.ID);
+ GenericParamConstraint = (GenericParamConstraint) getTable(Table.GenericParamConstraint.ID);
}
public static String long2hex(long a) {
@@ -683,12 +707,14 @@ public class PEFile {
public String toString() {
StringBuffer b = new StringBuffer("(");
+ int savedPos = buf.position();
reset();
for (int i = 0; i < length; i++) {
b.append(byte2hex(readByte()));
if (i < length - 1)
b.append(" ");
}
+ buf.position(savedPos);
return b.append(")").toString();
}
@@ -725,7 +751,7 @@ public class PEFile {
}
/** @return - the type encoded at the current position in the signature
- * according to 22.2.12
+ * according to 23.2.12
*/
public Type decodeType() {
try { return decodeType0(); }
@@ -761,10 +787,11 @@ public class PEFile {
type = Type.mkPtr(Type.GetType("System.Void"));
} else type = Type.mkPtr(decodeType());
break;
- case ELEMENT_TYPE_BYREF: // Followed by <type> token.
- case ELEMENT_TYPE_VALUETYPE: // Followed by <type> token
- //System.out.println("Signature.getType(): valuetype");
- //type = pemodule.getTypeDefOrRef(decodeInt());
+ case ELEMENT_TYPE_BYREF: /* although BYREF is not listed in 23.2.12. as possible alternative, this method is also called when parsing the signatures of a method param and a method return, which do allow for BYREF */
+ type = Type.mkByRef(decodeType());
+ break;
+ case ELEMENT_TYPE_VALUETYPE: // Followed by TypeDefOrRefEncoded
+ assert true;
case ELEMENT_TYPE_CLASS:
// Followed by <type> token
type = pemodule.getTypeDefOrRef(decodeInt());
@@ -777,19 +804,52 @@ public class PEFile {
break;
case ELEMENT_TYPE_ARRAY:
// <type> <rank> <boundsCount> <bound1> ... <loCount> <lo1> ...
+ // ArrayShape defined in 23.2.13 ArrayShape
Type elem = decodeType();
int rank = decodeInt();
int numSizes = decodeInt();
for (int i = 0; i < numSizes; i++)
- decodeInt();
+ decodeInt(); // TODO don't ignore
int numLoBounds = decodeInt();
for (int i = 0; i < numLoBounds; i++)
- decodeInt();
+ decodeInt(); // TODO don't ignore
type = Type.mkArray(elem, rank);
break;
+ // a grammar production from 23.2.12 Type
+ // GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type*
+ case ELEMENT_TYPE_GENERICINST:
+ int b = readByte();
+ /*- TODO don't ignore b as done above. Should .NET valuetypes be represented as Scala case classes? */
+ Type instantiatedType = pemodule.getTypeDefOrRef(decodeInt());
+ int numberOfTypeArgs = decodeInt();
+ Type[] typeArgs = new Type[numberOfTypeArgs];
+ for (int iarg = 0; iarg < numberOfTypeArgs; iarg++) {
+ typeArgs[iarg] = decodeType();
+ }
+ type = new ConstructedType(instantiatedType, typeArgs);
+ break;
+
+ // another grammar production from 23.2.12 Type
+ // ELEMENT_TYPE_VAR number The number non-terminal following MVAR
+ // or VAR is an unsigned integer value (compressed).
+ /* See also duplicate code in PEModule.java */
+ case ELEMENT_TYPE_VAR:
+ int typeArgAsZeroBased = decodeInt();
+ type = new Type.TMVarUsage(typeArgAsZeroBased, true);
+ break;
+
+ // another grammar production from 23.2.12 Type
+ // ELEMENT_TYPE_MVAR number The number non-terminal following MVAR
+ // or VAR is an unsigned integer value (compressed).
+ /* See also duplicate code in PEModule.java */
+ case ELEMENT_TYPE_MVAR:
+ typeArgAsZeroBased = decodeInt();
+ type = new Type.TMVarUsage(typeArgAsZeroBased, false);
+ break;
+
case ELEMENT_TYPE_FNPTR:
- // Followed by full method signature.
+ // Followed MethodDefSig or by MethodRefSig.
case ELEMENT_TYPE_END:
// Marks end of a list
case ELEMENT_TYPE_CMOD_REQD:
@@ -811,7 +871,7 @@ public class PEFile {
}
if (type == null) throw new RuntimeException();
return type;
- } // getType()
+ } // decodeType0()
public Type decodeFieldType() {
skipByte(FIELD);
@@ -829,7 +889,6 @@ public class PEFile {
case ELEMENT_TYPE_TYPEDBYREF:
return Type.GetType("System.TypedReference");
case ELEMENT_TYPE_BYREF:
- skipByte(ELEMENT_TYPE_BYREF);
return decodeType();
default:
return decodeType();
@@ -840,12 +899,11 @@ public class PEFile {
skipCustomMods();
switch (getByte()) {
case ELEMENT_TYPE_BYREF:
- skipByte(ELEMENT_TYPE_BYREF);
- return decodeType();
+ return decodeType();
case ELEMENT_TYPE_TYPEDBYREF:
- return Type.GetType("System.TypedReference");
+ return Type.GetType("System.TypedReference");
default:
- return decodeType();
+ return decodeType();
}
}
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/PEModule.java b/src/msil/ch/epfl/lamp/compiler/msil/PEModule.java
index 18e9c37bb4..5cf3e964a7 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/PEModule.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/PEModule.java
@@ -7,13 +7,11 @@ package ch.epfl.lamp.compiler.msil;
import ch.epfl.lamp.compiler.msil.PEFile;
import ch.epfl.lamp.compiler.msil.PEFile.Sig;
+import ch.epfl.lamp.compiler.msil.util.Signature;
import ch.epfl.lamp.compiler.msil.util.Table;
import ch.epfl.lamp.compiler.msil.util.Table.*;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.ArrayList;
-import java.util.Iterator;
+import java.nio.ByteBuffer;
/** Represents a module corresponding to a PE/COFF file
*
@@ -97,9 +95,40 @@ final class PEModule extends Module {
(this, attrs, name, declType, Type.AuxAttr.None, pefile, row);
types[row - 2] = t;
addType(t);
+ int[] tvarIdxes = pefile.GenericParam.getTVarIdxes(row);
+ // if(tvarIdxes.length > 0) { System.out.println("Type: " + t); }
+ for(int i = 0; i < tvarIdxes.length; i++) {
+ GenericParamAndConstraints tvarAndConstraints = getTypeConstraints(tvarIdxes[i]);
+ // add tvarAndConstraints as i-th TVar in t
+ t.addTVar(tvarAndConstraints);
+ }
return t;
}
+ public GenericParamAndConstraints getTypeConstraints(int genParamIdx) {
+ int tvarNumber = pefile.GenericParam(genParamIdx).Number;
+ // tvarName can be null
+ String tvarName = pefile.GenericParam.getName();
+ boolean isInvariant = pefile.GenericParam.isInvariant();
+ boolean isCovariant = pefile.GenericParam.isCovariant();
+ boolean isContravariant = pefile.GenericParam.isContravariant();
+ boolean isReferenceType = pefile.GenericParam.isReferenceType();
+ boolean isValueType = pefile.GenericParam.isValueType();
+ boolean hasDefaultConstructor = pefile.GenericParam.hasDefaultConstructor();
+ // grab constraints
+ int[] TypeDefOrRefIdxes = pefile.GenericParamConstraint.getTypeDefOrRefIdxes(genParamIdx);
+ Type[] tCtrs = new Type[TypeDefOrRefIdxes.length];
+ for(int i = 0; i < TypeDefOrRefIdxes.length; i++) {
+ Type tConstraint = getTypeDefOrRef(TypeDefOrRefIdxes[i]);
+ tCtrs[i] = tConstraint;
+ // System.out.println("\t\tConstraint: " + tConstraint);
+ }
+ GenericParamAndConstraints res = new GenericParamAndConstraints(tvarNumber, tvarName, tCtrs,
+ isInvariant, isCovariant, isContravariant,
+ isReferenceType, isValueType, hasDefaultConstructor);
+ return res;
+ }
+
/**
* Load the desription of the module-global fields and methods
*/
@@ -211,14 +240,104 @@ final class PEModule extends Module {
case Table.TypeRef.ID:
return getTypeRef(row);
case Table.TypeSpec.ID:
+ Table.TypeSpec ts = pefile.TypeSpec;
+ ts.readRow(row);
+ int posInBlobStream = ts.Signature;
+ byte[] blobArrWithLengthStripped = pefile.Blob.getBlob(posInBlobStream);
+ byte[] compressedUInt = compressUInt(blobArrWithLengthStripped.length);
+ byte[] byteArr = new byte[blobArrWithLengthStripped.length + compressedUInt.length];
+ System.arraycopy(compressedUInt, 0, byteArr, 0, compressedUInt.length);
+ System.arraycopy(blobArrWithLengthStripped, 0, byteArr, compressedUInt.length, blobArrWithLengthStripped.length);
+ ByteBuffer buf = ByteBuffer.wrap(byteArr);
+ Sig sig = pefile.new Sig(buf);
+ int desc = sig.readByte();
+
+ switch (desc) {
+
+ // GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncodred GenArgCount Type*
+ case Signature.ELEMENT_TYPE_GENERICINST: // i.e. 0x15
+ int b = sig.readByte(); // i.e. (0x12 | 0x11)
+ /* TODO don't ignore b as done above */
+ Type instantiatedType = getTypeDefOrRef(sig.decodeInt()); // TypeDefOrRefEncoded
+ int numberOfTypeArgs = sig.decodeInt(); // GenArgCount
+ Type[] typeArgs = new Type[numberOfTypeArgs];
+ for (int iarg = 0; iarg < numberOfTypeArgs; iarg++) {
+ typeArgs[iarg] = sig.decodeType(); // Type*
+ }
+ type = new ConstructedType(instantiatedType, typeArgs);
+ break;
+
+ /* Miguel says: Actually the following grammar rule production is not among those for a TypeSpecBlob
+ but I've found it in assemblies compiled from C# 3.0.
+ See also duplicate code in PEFile.java */
+ case Signature.ELEMENT_TYPE_VAR:
+ int typeArgAsZeroBased = sig.decodeInt();
+ type = new Type.TMVarUsage(typeArgAsZeroBased, true);
+ break;
+
+ /* Miguel says: Actually the following grammar rule production is not among those for a TypeSpecBlob
+ but I've found it in assemblies compiled from C# 3.0.
+ See also duplicate code in PEFile.java */
+ case Signature.ELEMENT_TYPE_MVAR:
+ typeArgAsZeroBased = sig.decodeInt();
+ type = new Type.TMVarUsage(typeArgAsZeroBased, false);
+ break;
+
+ case Signature.ELEMENT_TYPE_SZARRAY: // Single-dim array with 0 lower bound.
+ sig.skipCustomMods();
+ type = Type.mkArray(sig.decodeType(), 1);
+ break;
+
+ case Signature.ELEMENT_TYPE_ARRAY:
+ // <type> <rank> <boundsCount> <bound1> ... <loCount> <lo1> ...
+ // ArrayShape defined in 23.2.13 ArrayShape
+ Type elem = sig.decodeType();
+ int rank = sig.decodeInt();
+ int numSizes = sig.decodeInt();
+ for (int i = 0; i < numSizes; i++)
+ sig.decodeInt(); // TODO don't ignore
+ int numLoBounds = sig.decodeInt();
+ for (int i = 0; i < numLoBounds; i++)
+ sig.decodeInt(); // TODO don't ignore
+ type = Type.mkArray(elem, rank);
+ break;
+
+ default:
+ // TODO remaining grammar productions in 23.2.14 are for PTR and FNPTR only
throw new RuntimeException("PEModule.getTypeDefOrRef(): TypeSpec");
+ }
+ break;
default:
throw new RuntimeException("PEModule.getTypeDefOrRef(): oops!");
}
return type;
}
- /** Returns the method defined at the given row of the MethodDef table
+ private byte[] compressUInt(int u) {
+ // 23.2 in Partition II
+ // TODO add tests based on the examples in 23.2 in Partition II
+ // the CCI implementation is WriteCompressedUInt
+
+ /* informal discussion at http://www.cnblogs.com/AndersLiu/archive/2010/02/09/en-compressed-integer-in-metadata.html */
+ if (u <= 127 && 0 <= u) {
+ return new byte[]{(byte) u};
+ } else if (u > 127 && u <= (2 ^ 14 - 1)) {
+ byte loByte = (byte)(u & 0xff);
+ byte hiByte = (byte)((u >> 8) | 0x80);
+ byte[] res = new byte[] { hiByte, loByte };
+ return res;
+ } else {
+ byte b0 = (byte)(u & 0xff);
+ byte b1 = (byte)((u & 0xff00)>>8);
+ byte b2 = (byte)((u & 0xff0000)>>16);
+ byte b3 = (byte)((u >> 24)|0xc0);
+ byte[] res = new byte[] { b3, b2, b1, b0 };
+ return res;
+ }
+ }
+
+ /**
+ * Returns the method defined at the given row of the MethodDef table
* by looking up the type that defines the method.
*/
MethodBase getMethod(int row) {
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/PEType.java b/src/msil/ch/epfl/lamp/compiler/msil/PEType.java
index ace364d2ed..25e5373df1 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/PEType.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/PEType.java
@@ -108,9 +108,11 @@ final class PEType extends Type implements Signature {
}
protected MethodBase[] methoddefs;
+
protected MethodInfo getMethod(int n) {
return (MethodInfo)methoddefs[n - methodListBeg];
}
+
protected void loadMethods() {
methoddefs = new MethodBase[methodListEnd - methodListBeg];
@@ -123,9 +125,18 @@ final class PEType extends Type implements Signature {
int attrs = file.MethodDef(mrow).Flags;
String name = file.MethodDef.getName();
Sig sig = file.MethodDef.getSignature();
+ /* we're about to parse a MethodDefSig, defined in Sec. 23.2.1 of Partition II () */
+
int callConv = sig.readByte();
+ // TODO decode HASTHIS from high byte of calling convention
+ // TODO decode EXPLICITTHIS from high byte of calling convention
+ // TODO handle VARARG calling convention (not CLS but may show up )
+ if((callConv & 0x1F) == Signature.GENERIC) {
+ int genParamCount = sig.decodeInt();
+ /* genParamCount is ignored because the method's type params will be obtained below
+ (see: file.GenericParam.getMVarIdxes(row) ) */
+ }
int paramCount = sig.decodeInt();
- sig.skipByte(Signature.ELEMENT_TYPE_BYREF);
Type retType = sig.decodeRetType();
Type[] paramType = new Type[paramCount];
for (int i = 0; i < paramCount; i++)
@@ -133,10 +144,11 @@ final class PEType extends Type implements Signature {
ParameterInfo[] params = new ParameterInfo[paramCount];
int paramListBeg = file.MethodDef.ParamList;
- int paramListEnd = file.ParamDef.rows + 1;
- // if not the last method
- if (file.MethodDef.currentRow() < file.MethodDef.rows) {
- paramListEnd = file.MethodDef(mrow + 1).ParamList;
+ int paramListEnd = paramListBeg + paramCount;
+ if (paramListEnd > file.ParamDef.rows) {
+ /* don't try to read param names past ParamDef's row count
+ Some assembly-writers don't bother to give names for all params. */
+ paramListEnd = file.ParamDef.rows + 1;
}
for (int i = paramListBeg; i < paramListEnd; i++) {
int pattr = file.ParamDef(i).Flags;
@@ -159,9 +171,19 @@ final class PEType extends Type implements Signature {
&& (attrs & MethodAttributes.RTSpecialName) != 0
&& (name.equals(ConstructorInfo.CTOR)
|| name.equals(ConstructorInfo.CCTOR)))
+ {
method = new PEConstructorInfo(row, attrs, params);
- else
+ }
+ else {
method = new PEMethodInfo(row, name, attrs, retType, params);
+ int[] mvarIdxes = file.GenericParam.getMVarIdxes(row);
+ // if(mvarIdxes.length > 0) { System.out.println("Method: " + method); }
+ for(int i = 0; i < mvarIdxes.length; i++) {
+ GenericParamAndConstraints mvarAndConstraints = pemodule.getTypeConstraints(mvarIdxes[i]);
+ // add mvarAndConstraints as i-th MVar in method
+ ((PEMethodInfo)method).addMVar(mvarAndConstraints);
+ }
+ }
(method.IsConstructor() ? constrs : methods).add(method);
methoddefs[row - methodListBeg] = method;
}
@@ -274,7 +296,8 @@ final class PEType extends Type implements Signature {
add = getMethod(msem.Method);
else if (msem.isRemoveOn())
remove = getMethod(msem.Method);
- else {}
+ else {
+ }
}
events.add(new PEEventInfo(i, edef.getName(),
(short)edef.EventFlags,
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/PrimitiveType.java b/src/msil/ch/epfl/lamp/compiler/msil/PrimitiveType.java
new file mode 100644
index 0000000000..ccca52edba
--- /dev/null
+++ b/src/msil/ch/epfl/lamp/compiler/msil/PrimitiveType.java
@@ -0,0 +1,59 @@
+package ch.epfl.lamp.compiler.msil;
+
+public final class PrimitiveType extends Type {
+ public PrimitiveType(Module module,
+ int attributes,
+ String fullName,
+ Type baseType,
+ Type[] interfaces,
+ Type declType,
+ int auxAttr,
+ Type elemType) {
+ super(module, attributes, fullName,
+ baseType, interfaces, declType, auxAttr, elemType);
+ clearMembers();
+ }
+
+ public void clearMembers() {
+ fields = FieldInfo.EMPTY_ARRAY;
+ methods = MethodInfo.EMPTY_ARRAY;
+ constructors = ConstructorInfo.EMPTY_ARRAY;
+ events = EventInfo.EMPTY_ARRAY;
+
+ initBaseType();
+ initInterfaces();
+
+ initFields();
+ initMethods();
+ initEvents();
+ initProperties();
+ initNestedTypes();
+ }
+
+ public FieldInfo addField(String name, int attrs, Type fieldType) {
+ FieldInfo res = new FieldInfo(name, this, attrs, fieldType);
+ FieldInfo[] ms = new FieldInfo[fields.length + 1];
+ System.arraycopy(fields, 0, ms, 0, fields.length);
+ ms[ms.length - 1] = res;
+ fields = ms;
+ return res;
+ }
+
+ public MethodInfo addMethod(String name, int attrs, Type returnType, Type[] paramTypes) {
+ MethodInfo res = new MethodInfo(name, this, attrs, returnType, paramTypes);
+ MethodInfo[] ms = new MethodInfo[methods.length + 1];
+ System.arraycopy(methods, 0, ms, 0, methods.length);
+ ms[ms.length - 1] = res;
+ return res;
+ }
+
+ public ConstructorInfo addConstructor(int attrs, Type[] paramTypes) {
+ ConstructorInfo res = new ConstructorInfo(this, attrs, paramTypes);
+ ConstructorInfo[] ms = new ConstructorInfo[constructors.length + 1];
+ System.arraycopy(constructors, 0, ms, 0, constructors.length);
+ ms[ms.length - 1] = res;
+ return res;
+ }
+
+}
+
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/Type.java b/src/msil/ch/epfl/lamp/compiler/msil/Type.java
index f7d44980c4..78704062b7 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/Type.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/Type.java
@@ -21,6 +21,31 @@ import java.util.Arrays;
*/
public abstract class Type extends MemberInfo {
+ private java.util.List /* GenericParamAndConstraints */ tVars = new java.util.LinkedList();
+ private GenericParamAndConstraints[] sortedTVars = null;
+
+ public void addTVar(GenericParamAndConstraints tvarAndConstraints) {
+ sortedTVars = null;
+ tVars.add(tvarAndConstraints);
+ }
+
+ public GenericParamAndConstraints[] getSortedTVars() {
+ if(sortedTVars == null) {
+ sortedTVars = new GenericParamAndConstraints[tVars.size()];
+ for (int i = 0; i < sortedTVars.length; i ++){
+ Iterator iter = tVars.iterator();
+ while(iter.hasNext()) {
+ GenericParamAndConstraints tvC = (GenericParamAndConstraints)iter.next();
+ if(tvC.Number == i) {
+ sortedTVars[i] = tvC;
+ }
+ }
+ }
+ }
+ return sortedTVars;
+ }
+
+
//##########################################################################
// public static members
@@ -90,7 +115,7 @@ public abstract class Type extends MemberInfo {
// the underlying type of an enumeration. null if the type is not enum.
protected Type underlyingType;
- private int auxAttr;
+ protected int auxAttr;
//##########################################################################
// Map with all the types known so far and operations on it
@@ -102,6 +127,8 @@ public abstract class Type extends MemberInfo {
}
protected static Type addType(Type t) {
+ assert(!(t instanceof TMVarUsage));
+ assert(!(t instanceof ConstructedType));
Type oldType = (Type) types.put(t.FullName, t);
// if (oldType != null)
// throw new RuntimeException("The type: [" + t.Assembly + "]" + t
@@ -259,10 +286,22 @@ public abstract class Type extends MemberInfo {
return BaseType() == ENUM();
}
+ /** 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
+ * to a constructed type. */
+ public final boolean IsGeneric() {
+ return tVars.size() > 0;
+ }
+
public final boolean HasElementType() {
return IsArray() || IsPointer() || IsByRef();
}
+ public boolean IsTMVarUsage() {
+ // overridden in TMVarUsage
+ return false;
+ }
+
//public final boolean IsCOMObject;
//public final boolean IsContextful;
//public final boolean IsMarshalByRef;
@@ -281,19 +320,43 @@ public abstract class Type extends MemberInfo {
//##########################################################################
- static final class PrimitiveType extends Type {
- public PrimitiveType(Module module,
- int attributes,
- String fullName,
- Type baseType,
- Type[] interfaces,
- Type declType,
- int auxAttr,
- Type elemType)
- {
- super(module, attributes, fullName,
- baseType, interfaces, declType, auxAttr, elemType);
+ public static final class TMVarUsage extends Type {
+
+ public final int Number;
+ public final boolean isTVar;
+
+ /** Non-defining reference to either a TVar or an MVar */
+ public TMVarUsage(int Number, boolean isTVar) {
+ super(null, 0, ((isTVar ? "!" : "!!") + Number), null, null, null, AuxAttr.None, null);
+ this.Number = Number;
+ this.isTVar = isTVar;
}
+
+ public String toString() {
+ return (isTVar ? "!" : "!!") + Number;
+ }
+
+ public final boolean IsTMVarUsage() {
+ return true;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ TMVarUsage that = (TMVarUsage) o;
+
+ if (Number != that.Number) return false;
+ if (isTVar != that.isTVar) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result = Number;
+ result = 31 * result + (isTVar ? 1 : 0);
+ return result;
+ }
}
protected static final class AuxAttr {
@@ -336,6 +399,18 @@ public abstract class Type extends MemberInfo {
return addType(type);
}
+ /***/
+ public static Type mkByRef(Type elemType) {
+ String name = elemType.FullName + "&";
+ Type type = getType(name);
+ if (type != null) return type;
+ type = new PrimitiveType(elemType.Module,
+ TypeAttributes.NotPublic,
+ name, null, EmptyTypes, null,
+ AuxAttr.ByRef, elemType);
+ return addType(type);
+ }
+
//##########################################################################
// public methods
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala
index 4ef7069254..40888a9b83 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala
+++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILGenerator.scala
@@ -495,6 +495,13 @@ import ILGenerator._
pc = pc + 1
}
+ def Ldarg0WasJustEmitted() : Boolean = {
+ if(opcodeList.isEmpty)
+ return false
+ val lastEmitted = opcodeList.get(opcodeList.size - 1)
+ lastEmitted eq OpCode.Ldarg_0
+ }
+
private def emitSpecialLabel(l: Label) {
emitSpecialLabel(l, null)
}
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 ef1e3bc86a..cc359b813f 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/emit/ILPrinterVisitor.scala
+++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/ILPrinterVisitor.scala
@@ -143,6 +143,54 @@ abstract class ILPrinterVisitor extends Visitor {
def caseModuleBuilder(module: ModuleBuilder)
protected var currentType: Type = null
+
+ def printTypeParams(sortedTVars : Array[GenericParamAndConstraints]) {
+
+ def constraintFlags(tVar : GenericParamAndConstraints) = {
+ val varianceDirective = (if (tVar.isCovariant) "+ " else (if (tVar.isContravariant) "- " else ""))
+ val typeKindDirective = (if (tVar.isReferenceType) "class " else (if (tVar.isValueType) "valuetype " else ""))
+ val dfltConstrDirective = (if (tVar.hasDefaultConstructor) ".ctor " else "")
+ varianceDirective + typeKindDirective + dfltConstrDirective
+ }
+
+ def tparamName(tVar : GenericParamAndConstraints) = {
+ /* TODO Type-params in referenced assemblies may lack a name (those in a TypeBuilder or MethodBuilder shouldn't).
+ Given that we need not list (in ilasm syntax) the original type-params' names when
+ providing type arguments to it, the only type-param-names we'll serialize into a .msil file
+ are those for type-params in a TypeBuilder or MethodBuilder. Still, more details on this
+ appear in Sec. 4.5 "Faulty metadata in XMLReaderFactory" of
+ http://lamp.epfl.ch/~magarcia/ScalaCompilerCornerReloaded/Libs4Lib.pdf
+
+ To avoid name clashes when choosing a param name,
+ first collect all existing tparam-names from a type (and its nested types).
+ Not that those names are needed (ordinal positions can be used instead)
+ but will look better when disassembling with ildasm. */
+ assert(tVar.Name != null)
+ tVar.Name
+ }
+
+ if(sortedTVars.length == 0) { return }
+ print('<')
+ val lastIdx = sortedTVars.length - 1
+ for (it <- 0 until sortedTVars.length) {
+ val tVar = sortedTVars(it)
+ print(constraintFlags(tVar))
+ if(tVar.Constraints.length > 0) {
+ print('(')
+ val lastCnstrtIdx = tVar.Constraints.length - 1
+ for (ic <- 0 until tVar.Constraints.length) {
+ val cnstrt = tVar.Constraints(ic)
+ printReference(cnstrt)
+ if (ic < lastIdx) { print(", ") }
+ }
+ print(')')
+ }
+ print(" " + tparamName(tVar))
+ if (it < lastIdx) { print(", ") }
+ }
+ print('>')
+ }
+
/**
* Visit a TypeBuilder
*/
@@ -160,6 +208,7 @@ abstract class ILPrinterVisitor extends Visitor {
// [implements <typeReference> [, <typeReference>]*]
print(TypeAttributes.toString(`type`.Attributes))
print(" \'"); print(`type`.Name); print("\'")
+ printTypeParams(`type`.getSortedTVars())
if (`type`.BaseType() != null) {
println()
print(" extends ")
@@ -263,14 +312,24 @@ abstract class ILPrinterVisitor extends Visitor {
val bits = java.lang.Float.floatToRawIntBits((value.asInstanceOf[Float]).floatValue())
// float32(float32(...)) != float32(...)
print("float32 (float32 (")
- print(bits)
+ /* see p. 170 in Lidin's book Expert .NET 2.0 IL Assembler */
+ val valFlo = value.asInstanceOf[Float]
+ if (java.lang.Float.NaN == valFlo) print("0xFFC00000 /* NaN */ ") /* TODO this is 'quiet NaN, http://www.savrola.com/resources/NaN.html , what's the difference with a 'signaling NaN'?? */
+ else if (java.lang.Float.NEGATIVE_INFINITY == valFlo) print("0xFF800000 /* NEGATIVE_INFINITY */ ")
+ else if (java.lang.Float.POSITIVE_INFINITY == valFlo) print("0x7F800000 /* POSITIVE_INFINITY */ ")
+ else print(bits)
print("))")
} else if (value.isInstanceOf[Double]) {
// !!! check if encoding is correct
var bits = java.lang.Double.doubleToRawLongBits((value.asInstanceOf[Double]).doubleValue())
// float64(float64(...)) != float64(...)
print("float64 (float64 (")
- print(bits)
+ /* see p. 170 in Lidin's book Expert .NET 2.0 IL Assembler */
+ val valDou = value.asInstanceOf[Double]
+ if (java.lang.Double.NaN == valDou) print("0xffffffffffffffff /* NaN */ ") /* TODO this is 'quiet NaN, http://www.savrola.com/resources/NaN.html , what's the difference with a 'signaling NaN'?? */
+ else if (java.lang.Double.NEGATIVE_INFINITY == valDou) print("0xfff0000000000000 /* NEGATIVE_INFINITY */ ")
+ else if (java.lang.Double.POSITIVE_INFINITY == valDou) print("0x7ff0000000000000 /* POSITIVE_INFINITY */ ")
+ else print(bits)
print("))")
} else {
throw new Error("ILPrinterVisitor: Illegal default value: "
@@ -417,13 +476,20 @@ abstract class ILPrinterVisitor extends Visitor {
printSignature(argument.asInstanceOf[FieldInfo])
} else if (opCode == OpCode.Castclass || opCode == OpCode.Isinst || opCode == OpCode.Ldobj || opCode == OpCode.Newarr) {
printSignature(argument.asInstanceOf[Type])
- } else if (opCode == OpCode.Box || opCode == OpCode.Unbox || opCode == OpCode.Ldtoken) {
+ } else if (opCode == OpCode.Box || opCode == OpCode.Unbox || opCode == OpCode.Ldtoken || opCode == OpCode.Initobj) {
printReference(argument.asInstanceOf[Type])
} else if (opCode == OpCode.Ldloc || opCode == OpCode.Ldloc_S || opCode == OpCode.Ldloca || opCode == OpCode.Ldloca_S || opCode == OpCode.Stloc || opCode == OpCode.Stloc_S) {
val loc = argument.asInstanceOf[LocalBuilder]
print(loc.slot); print("\t// "); printSignature(loc.LocalType)
print(" \'"); print(loc.name); print("\'")
//print("'") print(((LocalBuilder)argument).name) print("'")
+ } else if (opCode == OpCode.Readonly) {
+ println("readonly. ")
+ } else if (opCode == OpCode.Constrained) {
+ print("constrained. ")
+ printReference(argument.asInstanceOf[Type])
+ } else if (opCode == OpCode.Ldelema) {
+ printReference(argument.asInstanceOf[Type])
} else {
// by default print toString argument if any
if (argument != null)
@@ -537,6 +603,10 @@ abstract class ILPrinterVisitor extends Visitor {
print(' '); printSignature(returnType)
//print(' ') print(marshal)
print(' '); printName(method.Name)
+ if(method.isInstanceOf[MethodInfo]) {
+ val mthdInfo = method.asInstanceOf[MethodInfo]
+ printTypeParams(mthdInfo.getSortedMVars())
+ }
val params = method.GetParameters()
print('(')
for (i <- 0 until params.length) {
@@ -607,7 +677,22 @@ abstract class ILPrinterVisitor extends Visitor {
}
def printTypeName(`type`: Type) {
- if (`type`.DeclaringType != null) {
+ if (`type`.isInstanceOf[ConstructedType]) {
+ val ct = `type`.asInstanceOf[ConstructedType]
+ printSignature(ct.instantiatedType)
+ print("<")
+ var i = 0
+ while (i < ct.typeArgs.length) {
+ val ta = ct.typeArgs(i)
+ printTypeName(ta); /* should be printSignature, but don't want `class' or `valuetype'
+ appearing before a type param usage. */
+ i = i + 1;
+ if (i < ct.typeArgs.length) {
+ print(", ")
+ }
+ }
+ print(">")
+ } else if (`type`.DeclaringType != null) {
printTypeName(`type`.DeclaringType)
print('/')
printName(`type`.Name)
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 ef9e002495..bfccc42214 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCode.scala
+++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/OpCode.scala
@@ -349,6 +349,8 @@ object OpCode {
final val CEE_VOLATILE : Int = 0x0113
final val CEE_TAILCALL : Int = 0x0114
final val CEE_INITOBJ : Int = 0x0115
+ final val CEE_CONSTRAINED : Int = 0xFE16
+ final val CEE_READONLY : Int = 0xFE1E
final val CEE_UNUSED68 : Int = 0x0116
final val CEE_CPBLK : Int = 0x0117
final val CEE_INITBLK : Int = 0x0118
@@ -800,6 +802,18 @@ object OpCode {
opcode(Call, CEE_CALL , "call" , 0xFFFFFF28, POP_SPECIAL, PUSH_SPECIAL, INLINE_METHOD , FLOW_CALL)
/**
+ * constrained prefix
+ */
+ final val Constrained = new OpCode()
+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)
+
+ /**
* Calls the method indicated on the evaluation stack (as a pointer to an entry point)
* with arguments described by a calling convention.
*/
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/emit/SingleFileILPrinterVisitor.scala b/src/msil/ch/epfl/lamp/compiler/msil/emit/SingleFileILPrinterVisitor.scala
index 7288cf41a7..d1b13054bc 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/emit/SingleFileILPrinterVisitor.scala
+++ b/src/msil/ch/epfl/lamp/compiler/msil/emit/SingleFileILPrinterVisitor.scala
@@ -19,7 +19,7 @@ import ch.epfl.lamp.compiler.msil.util.Table
/**
* The MSIL printer Visitor. It prints a complete
- * assembly in a single file. Then this file can be compiled by ilasm.
+ * assembly in a single file that can be compiled by ilasm.
*
* @author Nikolay Mihaylov
* @author Daniel Lorch
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/util/Signature.java b/src/msil/ch/epfl/lamp/compiler/msil/util/Signature.java
index 1b16508be5..d5dc0ff32c 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/util/Signature.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/util/Signature.java
@@ -55,8 +55,15 @@ public interface Signature {
public static final int ELEMENT_TYPE_VALUETYPE = 0x11;
/** Followed by <type> token */
public static final int ELEMENT_TYPE_CLASS = 0x12;
- /** <type> <rank> <boundsCount> <bound1> ... <loCount> <lo1> ... */
+
+ public static final int ELEMENT_TYPE_VAR = 0x13;
+
+ /**
+ * <type> <rank> <boundsCount> <bound1> ... <loCount> <lo1> ...
+ */
public static final int ELEMENT_TYPE_ARRAY = 0x14;
+
+ public static final int ELEMENT_TYPE_GENERICINST = 0x15;
/***/
public static final int ELEMENT_TYPE_TYPEDBYREF = 0x16;
/** System.IntPtr */
@@ -69,6 +76,9 @@ public interface Signature {
public static final int ELEMENT_TYPE_OBJECT = 0x1c;
/** Single-dim array with 0 lower bound. */
public static final int ELEMENT_TYPE_SZARRAY = 0x1d;
+
+ public static final int ELEMENT_TYPE_MVAR = 0x1e;
+
/** Required modifier : followed by a TypeDef or TypeRef token. */
public static final int ELEMENT_TYPE_CMOD_REQD = 0x1f;
/** Optional modifier : followed by a TypeDef or TypeRef token. */
@@ -89,6 +99,7 @@ public interface Signature {
public static final int EXPLICITTHIS = 0x40;
public static final int DEFAULT = 0x00;
public static final int VARARG = 0x05;
+ public static final int GENERIC = 0x10;
public static final int SENTINEL = 0x41;
public static final int C = 0x01;
public static final int STDCALL = 0x02;
diff --git a/src/msil/ch/epfl/lamp/compiler/msil/util/Table.java b/src/msil/ch/epfl/lamp/compiler/msil/util/Table.java
index ba9d317dcf..119d9e6ba3 100644
--- a/src/msil/ch/epfl/lamp/compiler/msil/util/Table.java
+++ b/src/msil/ch/epfl/lamp/compiler/msil/util/Table.java
@@ -29,7 +29,7 @@ public abstract class Table {
//##########################################################################
// fields and methods for handling predefined sets of tables
- public static final int TABLE_SET_LENGTH = 12;
+ public static final int TABLE_SET_LENGTH = 13;
public static final int _TypeDefOrRef = 0;
public static final int _HasConstant = 1;
@@ -43,9 +43,11 @@ public abstract class Table {
public static final int _Implementation = 9;
public static final int _CustomAttributeType = 10;
public static final int _ResolutionScope = 11;
+ public static final int _TypeOrMethodDef = 12;
public static final int[][] TableSet = new int[TABLE_SET_LENGTH][];
+
static {
TableSet[_TypeDefOrRef] =
new int[] {TypeDef.ID, TypeRef.ID, TypeSpec.ID};
@@ -75,10 +77,12 @@ public abstract class Table {
new int[] {-1, -1, MethodDef.ID, MemberRef.ID, -1};
TableSet[_ResolutionScope] =
new int[] {ModuleDef.ID, ModuleRef.ID, AssemblyRef.ID, TypeRef.ID};
+ TableSet[_TypeOrMethodDef] =
+ new int[]{TypeDef.ID, MethodDef.ID};
}
public static final int[] NoBits =
- new int[] {2, 2, 5, 1, 2, 3, 1, 1, 1, 2, 3, 2};
+ new int[]{2, 2, 5, 1, 2, 3, 1, 1, 1, 2, 3, 2, 1};
public static int getMask(int tableSetId) {
return (1 << NoBits[tableSetId]) - 1;
@@ -115,8 +119,8 @@ public abstract class Table {
"ImplMap", "FieldRVA", "", "",
"Assembly", "AssemblyProcessor","AssemblyOS", "AssemblyRef",
"AssemblyRefProcessor","AssemblyRefOS", "File", "ExportedType",
- "ManifestResource", "NestedClass", "", "",
- "", "", "", "",//0x28-0x2f
+ "ManifestResource", "NestedClass", "GenericParam", "MethodSpec",
+ "GenericParamConstraint", "", "", "",
"", "", "", "",
"", "", "", "",//0x30-0x37
"", "", "", "",
@@ -166,6 +170,15 @@ public abstract class Table {
case ExportedType.ID: table = new ExportedType(file, rows); break;
case ManifestResource.ID: table = new ManifestResource(file, rows); break;
case NestedClass.ID: table = new NestedClass(file, rows); break;
+ case GenericParam.ID:
+ table = new GenericParam(file, rows);
+ break;
+ case MethodSpec.ID:
+ table = new MethodSpec(file, rows);
+ break;
+ case GenericParamConstraint.ID:
+ table = new GenericParamConstraint(file, rows);
+ break;
default:
table = new Empty(id);
}
@@ -1595,5 +1608,252 @@ public abstract class Table {
} // class NestedClass
//##########################################################################
+ // table GenericParam; ID=0x2a; p137, 22.20
+
+ public static final class GenericParam extends Table {
+ public static final int ID = 0x2a;
+
+ public int Number;
+ public int Flags;
+ public int Owner; // a TypeOrMethodDef (24.2.6) coded index
+ public int Name; // a non-null index into the String heap
+
+ private java.util.Map /*<Integer, java.util.Set<Integer>>*/ GenericParamIdxesForMethodDefIdx =
+ new java.util.HashMap();
+ private java.util.Map /*<Integer, java.util.Set<Integer>>*/ GenericParamIdxesForTypeDefIdx =
+ new java.util.HashMap();
+
+ private void addToMap(int key, int value, java.util.Map IdxesForIdx) {
+ java.util.Set /*<Integer>*/ bucket = (java.util.Set)IdxesForIdx.get(Integer.valueOf(key));
+ if(bucket == null) {
+ bucket = new java.util.HashSet();
+ IdxesForIdx.put(Integer.valueOf(key), bucket);
+ }
+ bucket.add(Integer.valueOf(value));
+ }
+
+ /** Indexes of rows in the GenericParam table representing type parameters defined by the type given by
+ * its row index TypeDefIdx (in the TypeDef table).
+ * No need to position the current record before invoking this method. */
+ public int[] getTVarIdxes(int TypeDefIdx) {
+ if(!mapsPopulated) {
+ initMaps();
+ }
+ java.util.Set bucket = (java.util.Set)GenericParamIdxesForTypeDefIdx.get(Integer.valueOf(TypeDefIdx));
+ if(bucket == null) {
+ bucket = java.util.Collections.EMPTY_SET;
+ }
+ int[] res = new int[bucket.size()];
+ java.util.Iterator /*<Integer>*/ it = bucket.iterator();
+ for(int i = 0; i < bucket.size(); i++) {
+ res[i] = ((Integer)it.next()).intValue();
+ }
+ return res;
+ }
+
+ /** Indexes of rows in the GenericParam table representing type parameters defined by the method given by
+ * its row index MethodDefIdx (in the MethodDef table)
+ * No need to position the current record before invoking this method. */
+ public int[] getMVarIdxes(int MethodDefIdx) {
+ if(!mapsPopulated) {
+ initMaps();
+ }
+ java.util.Set bucket = (java.util.Set)GenericParamIdxesForMethodDefIdx.get(Integer.valueOf(MethodDefIdx));
+ if(bucket == null) {
+ bucket = java.util.Collections.EMPTY_SET;
+ }
+ int[] res = new int[bucket.size()];
+ java.util.Iterator /*<Integer>*/ it = bucket.iterator();
+ for(int i = 0; i < bucket.size(); i++) {
+ res[i] = ((Integer)it.next()).intValue();
+ }
+ return res;
+ }
+
+ private boolean mapsPopulated = false;
+
+ private void initMaps() {
+ mapsPopulated = true;
+ for (int currentParamRow = 1; currentParamRow <= rows; currentParamRow++) {
+ int currentOwner = file.GenericParam(currentParamRow).Owner;
+ int targetTableId = Table.getTableId(Table._TypeOrMethodDef, currentOwner);
+ int targetRow = currentOwner >> Table.NoBits[Table._TypeOrMethodDef];
+ if(targetTableId == TypeDef.ID){
+ addToMap(targetRow, currentParamRow, GenericParamIdxesForTypeDefIdx);
+ } else if(targetTableId == MethodDef.ID) {
+ addToMap(targetRow, currentParamRow, GenericParamIdxesForMethodDefIdx);
+ } else {
+ throw new RuntimeException();
+ }
+ }
+ }
+
+ public GenericParam(PEFile file, int rows) {
+ super(file, ID, rows);
+ this.newMapping = true;
+ }
+
+ protected void populateFields() {
+ Number = readShort();
+ Flags = readShort();
+ Owner = readTableSetIndex(_TypeOrMethodDef);
+ Name = readStringIndex();
+ }
+
+ /** This method assumes populateFields() has been just called to set Flags for the current record */
+ public boolean isInvariant() {
+ /* 23.1.7 Flags for Generic Parameters [GenericParamAttributes tributes] */
+ return (Flags & 0x0003) == 0;
+ }
+
+ /** This method assumes populateFields() has been just called to set Flags for the current record */
+ public boolean isCovariant() {
+ /* 23.1.7 Flags for Generic Parameters [GenericParamAttributes tributes] */
+ return (Flags & 0x0003) == 1;
+ }
+
+ /** This method assumes populateFields() has been just called to set Flags for the current record */
+ public boolean isContravariant() {
+ /* 23.1.7 Flags for Generic Parameters [GenericParamAttributes tributes] */
+ return (Flags & 0x0003) == 2;
+ }
+
+ /** This method assumes populateFields() has been just called to set Flags for the current record */
+ public boolean isReferenceType() {
+ /* 23.1.7 Flags for Generic Parameters [GenericParamAttributes tributes] */
+ return (Flags & 0x001C) == 4;
+ }
+
+ /** This method assumes populateFields() has been just called to set Flags for the current record */
+ public boolean isValueType() {
+ /* 23.1.7 Flags for Generic Parameters [GenericParamAttributes tributes] */
+ return (Flags & 0x001C) == 8;
+ }
+
+ /** This method assumes populateFields() has been just called to set Flags for the current record */
+ public boolean hasDefaultConstructor() {
+ /* 23.1.7 Flags for Generic Parameters [GenericParamAttributes tributes] */
+ return (Flags & 0x001C) == 0x0010;
+ }
+
+ protected int getRowSize() {
+ return 2 + 2 + file.getTableSetIndexSize(_TypeOrMethodDef) + file.getStringIndexSize();
+ /* Columns:
+ Number (2 bytes),
+ Flags (2 bytes),
+ Owner (coded token of type TypeOrMethodDef),
+ Name (offset in the #Strings stream).
+ */
+ }
+
+ public String getName() {
+ return file.getString(Name);
+ }
+
+ } // class GenericParam
+
+
+ //##########################################################################
+ // table GenericParamConstraint; ID=0x2c; p139, 22.20
+
+ public static final class GenericParamConstraint extends Table {
+ public static final int ID = 0x2c;
+
+ public int Owner; // an index into the GenericParam table
+ public int Constraint; // a TypeDefOrRef (24.2.6) coded index
+
+ public GenericParamConstraint(PEFile file, int rows) {
+ super(file, ID, rows);
+ this.newMapping = true;
+ }
+
+ protected void populateFields() {
+ Owner = readTableIndex(GenericParam.ID);
+ Constraint = readTableSetIndex(_TypeDefOrRef);
+ }
+
+ protected int getRowSize() {
+ return file.getTableIndexSize(GenericParam.ID) + file.getTableSetIndexSize(_TypeDefOrRef);
+ /* Columns:
+ Owner (RID in the GenericParam table),
+ Constraint (coded token of type TypeDefOrRef).
+ */
+ }
+
+ private boolean mapPopulated = false;
+
+ /** Indexes of rows (in the TypeDef, TypeRef, or TypeSpec tables) denoting the base class (if any)
+ * and interfaces (if any) that the generic parameter (of TVar or MVar kind) should support, where
+ * that generic parameter is represented by its index into the GenericParam table. */
+ public int[] getTypeDefOrRefIdxes(int genParamIdx) {
+ if(!mapPopulated) {
+ initMap();
+ }
+ java.util.Set bucket = (java.util.Set)TypeDefOrRefIdxesForGenParamIdx.get(Integer.valueOf(genParamIdx));
+ if(bucket == null) {
+ bucket = java.util.Collections.EMPTY_SET;
+ }
+ int[] res = new int[bucket.size()];
+ java.util.Iterator /*<Integer>*/ it = bucket.iterator();
+ for(int i = 0; i < bucket.size(); i++) {
+ res[i] = ((Integer)it.next()).intValue();
+ }
+ return res;
+ }
+
+
+ private void initMap() {
+ mapPopulated = true;
+ for (int currentConstraintRow = 1; currentConstraintRow <= rows; currentConstraintRow++) {
+ int targetGenericParam = file.GenericParamConstraint(currentConstraintRow).Owner;
+ int value = file.GenericParamConstraint.Constraint;
+ addToMap(targetGenericParam, value);
+ }
+ }
+
+ private java.util.Map /*<Integer, java.util.Set<Integer>>*/ TypeDefOrRefIdxesForGenParamIdx =
+ new java.util.HashMap();
+
+ private void addToMap(int key, int value) {
+ java.util.Set /*<Integer>*/ bucket = (java.util.Set)TypeDefOrRefIdxesForGenParamIdx.get(Integer.valueOf(key));
+ if(bucket == null) {
+ bucket = new java.util.HashSet();
+ TypeDefOrRefIdxesForGenParamIdx.put(Integer.valueOf(key), bucket);
+ }
+ bucket.add(Integer.valueOf(value));
+ }
+
+ } // class GenericParamConstraint
+
+ //##########################################################################
+ // table MethodSpec; ID=0x2b; p149, in Sec. 22.29 of Partition II
+
+ public static final class MethodSpec extends Table {
+ public static final int ID = 0x2b;
+
+ /* an index into the MethodDef or MemberRef table, specifying which generic method this row is an instantiation of.
+ A MethodDefOrRef (Sec. 24.2.6) coded index */
+ public int Method;
+
+ /* an index into the Blob heap (Sec. 23.2.15), holding the signature of this instantiation */
+ public int Instantiation;
+
+ public MethodSpec(PEFile file, int rows) {
+ super(file, ID, rows);
+ this.newMapping = true;
+ }
+
+ protected void populateFields() {
+ Method = readTableSetIndex(_MethodDefOrRef);
+ Instantiation = readBlobIndex();
+ }
+
+ protected int getRowSize() {
+ return file.getTableSetIndexSize(_MethodDefOrRef) + file.getBlobIndexSize();
+ }
+
+
+ } // class MethodSpec
+ //##########################################################################
} // class Table