.NET Forum / .NET Framework / CLR / October 2006
Emit.Call failing on non-value types
|
|
Thread rating:  |
Johannes Hansen - 11 Oct 2006 11:29 GMT Im currently updating my dynamic comparer class (which you can read about on codeproject) but I've run into a slight problem which I hope you guys can help me with...
My current emit code looks similar to this:
il.EmitCall(OpCodes.Call, member.DefaultComparerProperty.GetGetMethod(), null); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, member.FieldInfo); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldfld, member.FieldInfo); il.EmitCall(OpCodes.Callvirt, member.DefaultComparerCompareMethod, null);
Now, "member" is just a custom class containing various properties for easy access to defaul comparers, field types etc. for the member that needs to be compared. "DefaultComparerProperty" is the PropertyInfo for the Comparer<x>.Default property. "DefaultComparerCompareMethod" is the MethodInfo for the "Compare" method on the default comparer for the member's type.
My problem is that if the member is a value type this setup works, but if it is a reference type it fails with the following exception:
System.InvalidOperationException: Failed to compare two elements in the array. ---> System.MethodAccessException: System.Collections.Generic.Comparer`1.Compare(System.__Canon, System.__Canon) at DynamicComparison(Person , Person ) at DynamicComparer.DynamicComparerB`1.CompareMethodInvoker.Invoke(T itemA, T itemB) at DynamicComparer.DynamicComparerB`1.Compare(T itemA, T itemB) in C:\SomePath\DynamicComparer.cs:line 44 at System.Array.FunctorComparer`1.Compare(T x, T y) at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) --- End of inner exception stack trace --- at System.Collections.Generic.ArraySortHelper`1.QuickSort[TValue](T[] keys, TValue[] values, Int32 left, Int32 right, IComparer`1 comparer) at System.Collections.Generic.ArraySortHelper`1.Sort[TValue](T[] keys, TValue[] values, Int32 index, Int32 length, IComparer`1 comparer) at System.Collections.Generic.ArraySortHelper`1.Sort(T[] items, Int32 index, Int32 length, IComparer`1 comparer) at System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer) at System.Collections.Generic.List`1.Sort(Comparison`1 comparison) at DynamicMethod.Sample.Program.Main(String[] args) in C:\somePath\Program.cs:line 69
Please note the first line which indicates that instead of calling the default generic comparer's compare method using the type "Address" (or whatever ref type is passed in) it is using the type "System.__Canon" (ie. "Compare(System.__Canon, System.__Canon)" instead of "Compare(Address, Address)")?! Where does this "__Canon" class come from. Please help!
 Signature Johannes Hansen
System Consultant, frontAvenue A/S
Ben Voigt - 11 Oct 2006 14:22 GMT > Im currently updating my dynamic comparer class (which you can read about > on [quoted text clipped - 56 lines] > "Compare(System.__Canon, System.__Canon)" instead of "Compare(Address, > Address)")?! Where does this "__Canon" class come from. Please help! Can you write the dynamic assembly to disk and look at it with .NET Reflector?
Whenever I've had problems with a reference to the wrong method, it's been because I either used a plain MemberInfo (i.e. result of GetMethod()) gotten before I called CreateType, or used a MethodBuilder (ConstructorBuilder, PropertyBuilder) after calling CreateType.
Johannes Hansen - 11 Oct 2006 15:09 GMT Thanks for the reply Ben but the MethodInfo for the "Compare" method is taken from the correct instance type of the comparer. I've extracted the IL for the working hard-coded method body and the IL for the failing dynamic method body. The dynamic method IL was extracted using Haibo Luo's Dynamic Method Visualizer and the IL for the hard-coded comparer was extracted using Lutz Roeder's Reflector, this might account for some of the syntactical differences but I'm not sure.
Dynamic IL: L_0005: call System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] L_000a: ldarg.0 L_000b: ldfld DynamicMethod.Sample.Address address/DynamicMethod.Sample.Person L_0010: ldarg.1 L_0011: ldfld DynamicMethod.Sample.Address address/DynamicMethod.Sample.Person L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address]
Hard-coded (target) IL: L_0005: call [mscorlib]System.Collections.Generic.Comparer`1<!0> [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::get_Default() L_000a: ldarg.0 L_000b: ldfld DynamicMethod.Sample.Address DynamicMethod.Sample.Person::address L_0010: ldarg.1 L_0011: ldfld DynamicMethod.Sample.Address DynamicMethod.Sample.Person::address L_0016: callvirt instance int32 [mscorlib]System.Collections.Generic.Comparer`1<DynamicMethod.Sample.Address>::Compare(!0, !0)
Please remember that the failing code only fails when arg0 and arg1 is reference types and not value types. I belive the error is thrown at the final line (L_0016).
 Signature Johannes Hansen
System Consultant, frontAvenue A/S
> > Im currently updating my dynamic comparer class (which you can read about > > on [quoted text clipped - 64 lines] > before I called CreateType, or used a MethodBuilder (ConstructorBuilder, > PropertyBuilder) after calling CreateType. Johannes Hansen - 11 Oct 2006 16:14 GMT I just saw that I'm apparently calling compare on a class called "GenericComparer<T>"... However, all my declaring types and such tells me that I am using Comparer<T>.
So when I'm using a value type the IL emitted looks like this: IL_0005: call System.Collections.Generic.Comparer`1[System.Int32] get_Default()/System.Collections.Generic.Comparer`1[System.Int32] IL_000a: ldarg.0 IL_000b: ldfld Int32 age/DynamicMethod.Sample.Person IL_0010: ldarg.1 IL_0011: ldfld Int32 age/DynamicMethod.Sample.Person IL_0016: callvirt Int32 Compare(Int32, Int32)/System.Collections.Generic.Comparer`1[System.Int32]
But when I'm using a reference type the emitted IL looks like this: L_0005: call System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] get_Default()/System.Collections.Generic.Comparer`1[DynamicMethod.Sample.Address] L_000a: ldarg.0 L_000b: ldfld DynamicMethod.Sample.Address address/DynamicMethod.Sample.Person L_0010: ldarg.1 L_0011: ldfld DynamicMethod.Sample.Address address/DynamicMethod.Sample.Person L_0016: callvirt Int32 Compare(DynamicMethod.Sample.Address, DynamicMethod.Sample.Address)/System.Collections.Generic.GenericComparer`1[DynamicMethod.Sample.Address]
Does anyone know what this is about?
 Signature Johannes Hansen
System Consultant, frontAvenue A/S
> Thanks for the reply Ben but the MethodInfo for the "Compare" method is taken > from the correct instance type of the comparer. I've extracted the IL for the [quoted text clipped - 101 lines] > > before I called CreateType, or used a MethodBuilder (ConstructorBuilder, > > PropertyBuilder) after calling CreateType. Ben Voigt - 12 Oct 2006 00:27 GMT >I just saw that I'm apparently calling compare on a class called > "GenericComparer<T>"... However, all my declaring types and such tells me [quoted text clipped - 24 lines] > > Does anyone know what this is about? Again, can you use the Save method (on either AssemblyBuilder or ModuleBuilder) to write the dynamic class to disk? Then you can open it with .NET Reflector same as the one written by hand, and comparison will be easier. Also, you can run PEVerify on it.
It certainly looks, though, like you're trying to invoke GenericComparer<Address>.Compare on an object of type Comparer<Address>. PEVerify will almost certainly confirm that you've a problem.
Don't all of these classes implement IComparer<T>? Try emitting a call to typeof(IComparer<>).GetGenericType(member.FieldInfo.FieldType).GetMethod("Compare").
>> Thanks for the reply Ben but the MethodInfo for the "Compare" method is >> taken [quoted text clipped - 125 lines] >> > (ConstructorBuilder, >> > PropertyBuilder) after calling CreateType. Johannes Hansen - 12 Oct 2006 10:46 GMT Calling through the IComparer<> interface did the job... Thanks Ben!
 Signature Johannes Hansen
System Consultant, frontAvenue A/S
> >I just saw that I'm apparently calling compare on a class called > > "GenericComparer<T>"... However, all my declaring types and such tells me [quoted text clipped - 166 lines] > >> > (ConstructorBuilder, > >> > PropertyBuilder) after calling CreateType. Ben Voigt - 12 Oct 2006 14:52 GMT > Calling through the IComparer<> interface did the job... Thanks Ben! You're welcome.... though on second thought, was the whole reason for using dynamic methods to avoid a v-table dispatch? Using the interface does cause a pointer lookup at runtime and prevents jit inlining :(.
BTW, while emitting, was member.DefaultComparerProperty.PropertyType == member.DefaultComparerCompareMethod.DeclaringType?
This might be a quick test that allows you to use direct dispatch in most cases and only fallback to IComparer<> when necessary.
>> >I just saw that I'm apparently calling compare on a class called >> > "GenericComparer<T>"... However, all my declaring types and such tells [quoted text clipped - 188 lines] >> >> > (ConstructorBuilder, >> >> > PropertyBuilder) after calling CreateType. Johannes Hansen - 12 Oct 2006 16:45 GMT Hi Ben
> You're welcome.... though on second thought, was the whole reason for using > dynamic methods to avoid a v-table dispatch? No... :) I knew that I probably would get a slight performance penalty due to the pointer lookup but initial perf. testing shows that the penalty is within the acceptable range. The primary goal for using the Comparer<>.Default/IComparer<>.Compare implementation was to get better maintainability and reliability which we got.
> BTW, while emitting, was member.DefaultComparerProperty.PropertyType == > member.DefaultComparerCompareMethod.DeclaringType? Yes, they are the same in both cases... So I guess I can't use that as a test.
 Signature Johannes Hansen
System Consultant, frontAvenue A/S
> > Calling through the IComparer<> interface did the job... Thanks Ben! > [quoted text clipped - 200 lines] > >> >> > (ConstructorBuilder, > >> >> > PropertyBuilder) after calling CreateType.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|