Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / CLR / October 2006

Tip: Looking for answers? Try searching our database.

Emit.Call failing on non-value types

Thread view: 
Enable EMail Alerts  Start New Thread
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 Magazines

Get 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 ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.