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 / June 2006

Tip: Looking for answers? Try searching our database.

PEVerify generates improper ERROR

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Ben Voigt - 23 Jun 2006 21:16 GMT
I am calling a method on a member field.  The declaring type of the method
appears in my inheritance hierarchy, and I am making the call from an
override of the same method.  Here is the MSIL with the project path and
classname replaced by ellipses (read via Reflector, I've used
Reflection.Emit to create the assembly):

.method public hidebysig virtual instance bool Equals(object obj) cil
managed
{
     .override object::Equals
     .maxstack 2
     L_0000: ldarg.0
     L_0001: ldfld [mscorlib]System.Collections.BitArray
......::internalValue
     L_0006: ldarg.1
     L_0007: call instance bool object::Equals(object)
     L_000c: ret
}

For my troubles, I get the following message from PEVerify:
[IL]: Error: [......Types.dll : ......::Equals][offset 0x00000007] The
'this' parameter to the call must be the calling method's 'this' parameter.

My automated algorithm has determined that a direct call can be made --  
since BitArray is sealed, the type is known statically.  The most derived
implementation of Equals in BitArray is Object::BitArray.  I cannot
understand why PEVerify would believe this is an error.
Ben Voigt - 23 Jun 2006 22:33 GMT
>I am calling a method on a member field.  The declaring type of the method
>appears in my inheritance hierarchy, and I am making the call from an
[quoted text clipped - 23 lines]
> since BitArray is sealed, the type is known statically.  The most derived
> implementation of Equals in BitArray is Object::BitArray.  I cannot
garrr -- that's supposed to say Object::Equals
> understand why PEVerify would believe this is an error.
Barry Kelly - 23 Jun 2006 23:52 GMT
I can't find the above message. Did you cancel it?

I think I understand your problem, but can you post a complete .IL file
which compiles to this unverifiable sequence?

> > .method public hidebysig virtual instance bool Equals(object obj) cil
> > managed
[...]
> >      L_0001: ldfld [mscorlib]System.Collections.BitArray
> > ......::internalValue
> >      L_0006: ldarg.1
> >      L_0007: call instance bool object::Equals(object)

Here, I don't think you can do this. As far as I know, the only IL
sequence that permits you to statically call a particular version of a
virtual method (i.e. not using callvirt) is:

        ldarg.0
        /* possibly load arguments */
        call instance /* immediate ancestor */

and you can only do this in the overridden method.

> > For my troubles, I get the following message from PEVerify:
> > [IL]: Error: [......Types.dll : ......::Equals][offset 0x00000007] The
> > 'this' parameter to the call must be the calling method's 'this'
> > parameter.

That sounds correct to me. Can you find something in the CLI spec that
allows what you're trying to do?

> > My automated algorithm has determined that a direct call can be made --  
> > since BitArray is sealed, the type is known statically.  The most derived
> > implementation of Equals in BitArray is Object::Equals.  I cannot
> > understand why PEVerify would believe this is an error.

The BitArray in the other assembly may change its implementation in the
future (the assembly may be upgraded or changed), without a requirement
for the client assembly (i.e. your assembly) to be recompiled. If
BitArray became non-sealed at some time in the future, and a new
descendant with an overridden Equals() method happened to get into your
field, then you'd be avoiding calling the correct overridden version.

The CLR and JIT could make the leap of faith you're trying to make at
runtime, but I don't think compilers can at compile time.

-- Barry

Signature

http://barrkel.blogspot.com/

Ben Voigt - 26 Jun 2006 13:22 GMT
> The BitArray in the other assembly may change its implementation in the
> future (the assembly may be upgraded or changed), without a requirement
> for the client assembly (i.e. your assembly) to be recompiled. If
> BitArray became non-sealed at some time in the future, and a new
> descendant with an overridden Equals() method happened to get into your
> field, then you'd be avoiding calling the correct overridden version.

Becoming non-sealed is in most cases an interface-breaking change.  I see
essentially two scenarios:

(1) The class introduces new non-virtual methods.  All existing clients are
using direct call to invoke these methods.  These methods (at least some
fraction) will be changed to virtual, requiring clients to recompile.

(2) The class overrides existing virtual methods, and adds no new publicly
visible methods.  Existing clients will continue to make virtual calls using
the type of the abstract base class or interface.  No recompilation needed.

(3) The third scenario is that the class added new virtual methods in
anticipation of later overrides, but this is incompatible with being sealed
in the first place:

warning CS0628: 'XYZ.Test()': new protected member declared in sealed class

error CS0549: 'XYZ.Test()' is a new virtual member in sealed class 'XYZ'

> The CLR and JIT could make the leap of faith you're trying to make at
> runtime, but I don't think compilers can at compile time.

You betcha:

TimeSpanConverter tsc = new TimeSpanConverter();
tsc.ConvertTo(null, null, DateTime.Now(), typeof(string));

While this example is a little contrived, it should be quite obvious that
the compiler knows exactly which method is called, even though the method
has a virtual slot.

> -- Barry
Barry Kelly - 26 Jun 2006 18:10 GMT
> > The CLR and JIT could make the leap of faith you're trying to make at
> > runtime, but I don't think compilers can at compile time.
[quoted text clipped - 7 lines]
> the compiler knows exactly which method is called, even though the method
> has a virtual slot.

I'm afraid you've lost me :) Which virtual method is being called
statically?

-- Barry

Signature

http://barrkel.blogspot.com/

Ben Voigt - 26 Jun 2006 18:25 GMT
>> > The CLR and JIT could make the leap of faith you're trying to make at
>> > runtime, but I don't think compilers can at compile time.
[quoted text clipped - 10 lines]
> I'm afraid you've lost me :) Which virtual method is being called
> statically?

Sorry for not clarifying that.  There's only one virtual method: ConvertTo.
The compiler knows without question the exact runtime type of tsc, so
there's no need to use callvirt.

Or maybe callvirt doesn't mean "call a method through the v-table" but "call
a virtual method the most efficient way possible"?  And the JIT will
substitute a direct call instead of v-table lookup when the exact method is
known?

> -- Barry
Barry Kelly - 26 Jun 2006 18:40 GMT
> >> > The CLR and JIT could make the leap of faith you're trying to make at
> >> > runtime, but I don't think compilers can at compile time.
[quoted text clipped - 14 lines]
> The compiler knows without question the exact runtime type of tsc, so
> there's no need to use callvirt.

Sure - but the C# compiler doesn't, it uses callvirt.

> Or maybe callvirt doesn't mean "call a method through the v-table" but "call
> a virtual method the most efficient way possible"?  And the JIT will
> substitute a direct call instead of v-table lookup when the exact method is
> known?

I think that's the general pattern. The CLR uses extremely late binding.
If the C# compiler used 'call', and if some point in the future the
assembly containing TimeSpanConverter was changed so that it no longer
overrode ConvertTo and instead implemented an ancestor implementation,
the code would break.

I know you said in an earlier post that "Becoming non-sealed is in most
cases an interface-breaking change.". I think that could be true in a
library in the unmanaged world, but I have difficulty seeing how it
would be a breaking change in the managed world - unless it's to break
your optimization, which I reckon is probably most safely left to the
JIT.

The C# compiler isn't aggressive at all in the optimizations it does,
and sometimes what it produces even looks sub-optimal (I'm thinking of
boolean expressions inside if statements - it often creates spurious
locals, IIRC). It appears that most of the focus is on JIT compilation.

-- Barry

Signature

http://barrkel.blogspot.com/

Ben Voigt - 26 Jun 2006 19:14 GMT
>> >> > The CLR and JIT could make the leap of faith you're trying to make
>> >> > at
[quoted text clipped - 40 lines]
> your optimization, which I reckon is probably most safely left to the
> JIT.

But the C# compiler is using call, not callvirt, to call into your sealed
class, except for overriden methods.... you aren't allowed to define new
virtual members in a sealed class.

Basically if the JIT is making the decision, there should be only one call
instruction, no distinction between call and callvirt.

> The C# compiler isn't aggressive at all in the optimizations it does,
> and sometimes what it produces even looks sub-optimal (I'm thinking of
> boolean expressions inside if statements - it often creates spurious
> locals, IIRC). It appears that most of the focus is on JIT compilation.
>
> -- Barry
Barry Kelly - 26 Jun 2006 21:49 GMT
> > I think that's the general pattern. The CLR uses extremely late binding.
> > If the C# compiler used 'call', and if some point in the future the
[quoted text clipped - 11 lines]
> But the C# compiler is using call, not callvirt, to call into your sealed
> class, except for overriden methods....

No it isn't. The program:

---8<---
using System;
using System.ComponentModel;

class App
{
   static void Main()
   {
       TimeSpanConverter tsc = new TimeSpanConverter();
       tsc.ConvertTo(null, null, DateTime.Now, typeof(string));
   }
}
--->8---

Produces code like this for Main():

---8<---
 .method private hidebysig static void  Main() cil managed
 {
   .entrypoint
   // Code size       36 (0x24)
   .maxstack  5
   .locals init ([0] class
[System]System.ComponentModel.TimeSpanConverter tsc)
   IL_0000:  newobj     instance void
[System]System.ComponentModel.TimeSpanConverter::.ctor()
   IL_0005:  stloc.0
   IL_0006:  ldloc.0
   IL_0007:  ldnull
   IL_0008:  ldnull
   IL_0009:  call       valuetype [mscorlib]System.DateTime
[mscorlib]System.DateTime::get_Now()
   IL_000e:  box        [mscorlib]System.DateTime
   IL_0013:  ldtoken    [mscorlib]System.String
   IL_0018:  call       class [mscorlib]System.Type
[mscorlib]System.Type::GetTypeFromHandle(valuetype
[mscorlib]System.RuntimeTypeHandle)
   IL_001d:  callvirt   instance object
[System]System.ComponentModel.TypeConverter::ConvertTo(class
[System]System.ComponentModel.ITypeDescriptorContext, class
[mscorlib]System.Globalization.CultureInfo, object, class
[mscorlib]System.Type)
   IL_0022:  pop
   IL_0023:  ret
 } // end of method App::Main
--->8---

You can see there at IL_001d that TypeConverter::ConvertTo is clearly
called via callvirt.

> you aren't allowed to define new
> virtual members in a sealed class.

The class could be unsealed, a virtual method overridden, and the
assembly recompiled long after your code has baked in a static call to
the wrong method.

> Basically if the JIT is making the decision, there should be only one call
> instruction, no distinction between call and callvirt.

Actually, call and callvirt have different semantics even independent of
whether or not the method in question is virtual - callvirt checks for a
null instance, even if the method is static. I've written about it on by
blog:

http://barrkel.blogspot.com/2006/05/call-vs-callvirt-for-c-non-virtual.html

The C# compiler appears to only uses 'call' for calling ancestor
methods, static methods and constructors.

-- Barry

Signature

http://barrkel.blogspot.com/

Ben Voigt - 27 Jun 2006 00:07 GMT
>> > I think that's the general pattern. The CLR uses extremely late
>> > binding.
[quoted text clipped - 14 lines]
>
> No it isn't. The program:

>> you aren't allowed to define new
>> virtual members in a sealed class.
[quoted text clipped - 16 lines]
> The C# compiler appears to only uses 'call' for calling ancestor
> methods, static methods and constructors.

not exactly... I see scattered throughout the class libraries, and my own C#
compiled code:
call instance string int32::ToString()

Despite the fact that ToString is a virtual method override.  When I tried
calling all virtual methods virtually, I quickly ran into trouble with value
classes -- it seems that only call is valid for use with value classes
(unless called through an interface reference I suppose).

I'm about to go read your blog article.  I'm hoping that the JIT is
generating static calls for a lot of callvirt MSIL.

> -- Barry
Barry Kelly - 27 Jun 2006 01:08 GMT
> > The C# compiler appears to only uses 'call' for calling ancestor
> > methods, static methods and constructors.
>
> not exactly... I see scattered throughout the class libraries, and my own C#
> compiled code:
> call instance string int32::ToString()

Yes, I'll have to add instance calls on value types by way of ldarga /
ldloca (i.e. not boxed) to my list.

> Despite the fact that ToString is a virtual method override.  When I tried
> calling all virtual methods virtually, I quickly ran into trouble with value
> classes -- it seems that only call is valid for use with value classes
> (unless called through an interface reference I suppose).

If you called ToString() on a boxed value type rather than on the
unboxed version (via object.ToString() or whatever), I think you'll end
up with callvirt again.

> I'm about to go read your blog article.  I'm hoping that the JIT is
> generating static calls for a lot of callvirt MSIL.

The generated calls appear to be indirect much of the time, even after
the target methods have been compiled. I'm not certain of the
circumstances that makes them static (though I'm somewhat sure that NGEN
will make method addresses (and thus calls) inside the assembly static,
based on the assembly dumps I've seen). I don't know if the JIT ever
revisits a method later on to make the indirect calls static.

-- Barry

Signature

http://barrkel.blogspot.com/

"Jeffrey Tan[MSFT]" - 26 Jun 2006 09:35 GMT
Hi Ben,

Thanks for your post!

Can you tell me which version of CLR you are using? In Whidbey, our CLR
team introduced a new rule regarding making non-virtual calls to virtual
methods, which only permits it if the target method is being called on the
caller's 'this' pointer.

For more information, please refer to the link below:
"Static calls to virtual methods and verifiability "
http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=c33b0dbc-a696-4b3d-
a136-4bee2d86be2a

Hope this helps!

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
Ben Voigt - 26 Jun 2006 13:09 GMT
> Hi Ben,
>
[quoted text clipped - 4 lines]
> methods, which only permits it if the target method is being called on the
> caller's 'this' pointer.

.NET 2.0 -- all my system assemblies are version 2.0.0.0

> For more information, please refer to the link below:
> "Static calls to virtual methods and verifiability "
> http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=c33b0dbc-a696-4b3d-
> a136-4bee2d86be2a

Hmmm.... I think the verifier should allow this when the target type can be
statically inferred, because the object was created in the same method using
Newobj (i.e. not via reflection), or the type is sealed.... especially when
the only virtual methods on the class are GetHashCode and ToString.  Or will
the JIT perform this optimization in all cases?

> Hope this helps!
>
[quoted text clipped - 7 lines]
> This posting is provided "AS IS" with no warranties, and confers no
> rights.
Ben Voigt - 26 Jun 2006 14:10 GMT
>> Hi Ben,
>>
[quoted text clipped - 18 lines]
> especially when the only virtual methods on the class are GetHashCode and
> ToString.  Or will the JIT perform this optimization in all cases?

After taking out the check for IsSealed, I now receive these verification
errors instead:
[IL]: Error: [.....dll : .....::ToString][offset 0x0000000C] Callvirt on a
value type method.
[IL]: Error: [.....dll : ......::get_DebugDisplay][offset 0x00000026][found
address of Byte][expected ref 'System.Object'] Unexpected type on the stack.
[IL]: Error: [......dll : .....::get_DebugDisplay][offset 0x00000026] Call
to base type of valuetype.

So it seems a direct call is expected on a value type, because all its
methods are implicitly sealed (but reflection says they aren't marked
final), but not on a sealed ref class?

I finally came up with this rule, which produces verifiable code:

public static bool ShouldCallDirect(Type targetType, MethodInfo method)

{

return method.IsPublic && (/*targetType.IsSealed ||*/ targetType.IsValueType
|| method.IsFinal);

}

>> Hope this helps!
>>
[quoted text clipped - 7 lines]
>> This posting is provided "AS IS" with no warranties, and confers no
>> rights.
"Jeffrey Tan[MSFT]" - 27 Jun 2006 08:28 GMT
Hi Ben,

Thanks for your feedback!

Yes, I see your concern. However, within Joe Duffy's blog we can see that
some other folkers have different concerns and our CLR team finally chooses
this decision. If you have strong suggestion and concern regarding this
rule, I recommend you feedback it in the link below:
http://connect.microsoft.com/Main/content/content.aspx?ContentID=2220

Our developer team will receive your feedback and follow up with you.
Thanks for your understanding.

> So it seems a direct call is expected on a value type
Yes, "callvirt requires an object reference as the this pointer and will
not accept a managed pointer to a value type instance". This is documented
in "Calling Methods" chapter of "Serge Lidin"'s book "Inside Microsoft .NET
IL Assembler".

Hope this helps.

Best regards,
Jeffrey Tan
Microsoft Online Community Support
==================================================
When responding to posts, please "Reply to Group" via your newsreader so
that others may learn and benefit from your issue.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.

Rate this thread:







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.