diff options
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 |