# Sunday, 10 August 2008
« New Development Snapshot | Main | Using Mono.Cecil instead of Reflection.E... »
Reflection.Emit Bug

I started working on support for compiling multiple assemblies at once with ikvmc (to support mutual depedencies) and ran into a rather annoying bug:

using System;
using System.Reflection;
using System.Reflection.Emit;

class Program
  static void Main()
    AssemblyBuilder ab1 = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("A1"), AssemblyBuilderAccess.Save);
    AssemblyBuilder ab2 = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("A2"), AssemblyBuilderAccess.Save);
    ModuleBuilder mod1 = ab1.DefineDynamicModule("A1");
    ModuleBuilder mod2 = ab2.DefineDynamicModule("A2");

    TypeBuilder tb1 = mod1.DefineType("T1");
    TypeBuilder tb2 = mod2.DefineType("T2");

    ConstructorBuilder cb1 = tb1.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
    ConstructorBuilder cb2 = tb2.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { tb1 });

    ILGenerator ilgen = cb1.GetILGenerator();

    ilgen.Emit(OpCodes.Newobj, cb2);

Running this on .NET 2.0 SP1 results in:

Unhandled Exception: System.Runtime.InteropServices.COMException (0x80131130): Record not found on lookup. (Exception from HRESULT: 0x80131130)
   at System.Reflection.Module._InternalGetMemberRef(Module refedModule, Int32 tr, Int32 defToken)
   at System.Reflection.Emit.ModuleBuilder.InternalGetConstructorToken(ConstructorInfo con, Boolean usingRef)
   at System.Reflection.Emit.ILGenerator.GetMethodToken(MethodBase method, Type[] optionalParameterTypes)
   at System.Reflection.Emit.ILGenerator.Emit(OpCode opcode, ConstructorInfo con)
   at Program.Main() in c:\vsp\RefEmitBugRepro\Program.cs:line 23

The "no Microsoft bug filing" policy is still in effect, so I won't be filing a bug with Microsoft for this.


For the scenario above there is a (painful) workaround. You can create your own ConstructorInfo subclass that represents the constructor you want to call, if you do that ILGenerator.Emit() will end up in a different code path to lookup the token and that code path does work.

I haven't tried it, but I assume this workaround also works for methods and fields.

I think that for ikvmc I won't be using this workaround, but instead I'll treat this as a good reason to finally start looking into using Mono.Cecil instead of Reflection.Emit.

Sunday, 10 August 2008 10:02:40 (W. Europe Daylight Time, UTC+02:00)  #    Comments [3]