summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiguel Garcia <magarcia@epfl.ch>2010-08-13 13:49:28 +0000
committerMiguel Garcia <magarcia@epfl.ch>2010-08-13 13:49:28 +0000
commit29f9d75674205eb8f3db63829eeb1ff7d781694c (patch)
treec656f19edef3fc3224c78a755ba15183934bc80b
parent4b9de7deb27682100e09e0b55780f029754e0b7e (diff)
downloadscala-29f9d75674205eb8f3db63829eeb1ff7d781694c.tar.gz
scala-29f9d75674205eb8f3db63829eeb1ff7d781694c.tar.bz2
scala-29f9d75674205eb8f3db63829eeb1ff7d781694c.zip
for MSIL: Previous changesets were applied more...
for MSIL: Previous changesets were applied more-or-less incrementally to scala-msil, this changeset brings them all together to scala trunk. Next stop will be supporting CLR valuetypes (another big one). Afterwards (once emitted .NET bytecode passes peverify) changesets will become more manageable in size. Well, no, there's generics coming. But believe me, soon MSIL changesets will get smaller in size.
-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