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