Issue with modopt based overloads

Topics: PE Writer
Jan 26, 2010 at 4:57 PM

Hi all,

I've tried to use CCI metadata to solve a few issues met in System.Reflection.Emit.

There is an assembly with a following internal structure (created with ILASM)

public class MyType
{
    // Methods
    public MyType();
    public static bool modopt(OverloadedOnConstraints) Foo<T>(T x) where T: class; { return false; }
 public static bool Foo<T>(T x) where T: struct { return false; }
} public class OverloadedOnConstraints { // Methods public OverloadedOnConstraints(); }


According to ECMA 335 - 7.1.1 "modreq and modopt" - modopts are part of signature so signatures of Foo methods differs
I can write code that invokes these methods in IL and the result assembly is perfectly valid (PEVerify confirms this).
But unfortunatly I wasn't manage to generate the similar code with CCI, result makes me, ILDasm and PEVerify sad :)

I've used the following code, most of it was taken from HelloIL sample:

        var host = new PeReader.DefaultHost();
var nameTable = host.NameTable;

var coreAssembly = host.LoadAssembly(host.CoreAssemblySymbolicIdentity);

var resultAssembly = new Assembly
{
Name = nameTable.GetNameFor("modopttest"),
ModuleName = nameTable.GetNameFor("modopttest.exe"),
Kind = ModuleKind.ConsoleApplication,
TargetRuntimeVersion = coreAssembly.TargetRuntimeVersion,
};
resultAssembly.AssemblyReferences.Add(coreAssembly);
var modoptAssembly = (IAssembly)host.LoadUnitFrom("modopt.dll");

resultAssembly.AssemblyReferences.Add(modoptAssembly);
var myTypeDef = modoptAssembly.GetAllTypes().Where(t => t.Name == nameTable.GetNameFor("MyType")).First();
var methods = myTypeDef.Methods.Where(m => m.Name == nameTable.GetNameFor("Foo")).ToArray();

IMethodDefinition clsMethod;
IMethodDefinition structMethod;
if (methods[0].ReturnValueCustomModifiers.Any())
{
clsMethod = methods[0];
structMethod = methods[1];
}
else
{
clsMethod = methods[1];
structMethod = methods[0];
}

var rootNS = new RootUnitNamespace();
resultAssembly.UnitNamespaceRoot = rootNS;
rootNS.Unit = resultAssembly;

var module = new NamespaceTypeDefinition
{
ContainingUnitNamespace = rootNS,
Name = nameTable.GetNameFor("<Module>"),
InternFactory = host.InternFactory,
IsClass = true
};
resultAssembly.AllTypes.Add(module);

var programType = new NamespaceTypeDefinition
{
ContainingUnitNamespace = rootNS,
InternFactory = host.InternFactory,
Name = nameTable.GetNameFor("Program"),
IsClass = true,
IsPublic = true,
BaseClasses = { host.PlatformType.SystemObject }
};
resultAssembly.AllTypes.Add(programType);

var method = new MethodDefinition()
{
Type = host.PlatformType.SystemVoid,
Name = nameTable.GetNameFor("Main"),
IsCil = true,
IsStatic = true,
Visibility = TypeMemberVisibility.Public,
ContainingType = programType,
InternFactory = host.InternFactory
};


programType.Methods.Add(method);
resultAssembly.EntryPoint = method;

var il = new ILGenerator(host);

var body = new ILGeneratorMethodBody(il, true, 1) {MethodDefinition = method};
method.Body = body;

il.Emit(OperationCode.Ldstr, "test");
il.Emit(OperationCode.Call, new GenericMethodInstanceReference(clsMethod, new[] { host.PlatformType.SystemString }, host.InternFactory));
il.Emit(OperationCode.Pop);

il.Emit(OperationCode.Ldnull);
il.Emit(OperationCode.Call, new GenericMethodInstanceReference(clsMethod, new[] { host.PlatformType.SystemString }, host.InternFactory));
il.Emit(OperationCode.Pop);

il.Emit(OperationCode.Ldc_I4_1);
il.Emit(OperationCode.Call, new GenericMethodInstanceReference(structMethod, new[] { host.PlatformType.SystemInt32 }, host.InternFactory));
il.Emit(OperationCode.Pop);

il.Emit(OperationCode.Ret);

using (var fs = File.Create("modopttest.exe"))
PeWriter.WritePeToStream(resultAssembly, host, fs);
Result in ILDasm
.method public static void  Main() cil managed
{
  .entrypoint
  // Code size       26 (0x1a)
  .maxstack  8
  IL_0000:  ldstr      "test"
  IL_0005:  call       bool modopt(['modopt']OverloadedOnConstraints) ['modopt']MyType::Foo<string>(!!0)
  IL_000a:  pop
  IL_000b:  ldnull
  IL_000c:  call       bool modopt(['modopt']OverloadedOnConstraints) ['modopt']MyType::Foo<string>(!!0)
  IL_0011:  pop
  IL_0012:  ldc.i4.1
  IL_0013:  call       bool modopt(['modopt']OverloadedOnConstraints) ['modopt']MyType::Foo<string>(!!0)
  IL_0018:  pop
  IL_0019:  ret
} // end of method Program::Main
Second and third methods calls are incorrect.

Maybe someone can give me the clue, what I was doing wrong and how to solve this issue?
Coordinator
Feb 15, 2010 at 4:16 PM

This seems very much like a bug in the PeWriter module. I'll see if I can get the time to take a closer look.

Feb 17, 2010 at 12:16 PM

That will be great! We are investigating the possibility to add new method overload features to Nemerle language and it seems that CCI metadata is the only acceptable way for generating assemblies.

System.Reflection.Emit and Cecil cannot complete this task too.

Coordinator
Feb 17, 2010 at 5:05 PM

Could you attach modopt.dll (or the ildasm listing) to this discussion. It will save me a bit of time.

Feb 17, 2010 at 8:06 PM

Sure

Download link modopt.dll

Coordinator
Feb 17, 2010 at 11:26 PM

It turns out that the problem lies here:

var resultAssembly = new Assembly
                             {
                                 Name = nameTable.GetNameFor("modopttest"),
                                 ModuleName = nameTable.GetNameFor("modopttest.exe"),
                                 Kind = ModuleKind.ConsoleApplication,
                                 TargetRuntimeVersion = coreAssembly.TargetRuntimeVersion,
                             };

This does not specify enough properties and one consequence of leaving MetadataFormatMajorVersion with its default value of 0 is that the PeWriter will not mark any of the generics related tables as valid. If you add "MetadataFormatMajorVersion  = 2" to the above statement, you'll see the right output from ildasm.

You may need to set additioal properties as well. See the SmallBasic sample for a list of all the properties that need values different from the default.

Feb 18, 2010 at 4:10 AM

Thank you!

 

Mar 8, 2010 at 1:18 AM

Where I can find SmallBasic sample?

Coordinator
Mar 8, 2010 at 1:28 AM

ccisamples.codeplex.com