summaryrefslogtreecommitdiff
path: root/src/msil/ch
diff options
context:
space:
mode:
Diffstat (limited to 'src/msil/ch')
-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
15 files changed, 880 insertions, 51 deletions
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