all groups > dotnet clr > june 2006 >
You're in the

dotnet clr

group:

PEVerify generates improper ERROR



PEVerify generates improper ERROR Ben Voigt
6/23/2006 3:16:07 PM
dotnet clr: 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.

Re: PEVerify generates improper ERROR Ben Voigt
6/23/2006 4:33:41 PM

[quoted text, click to view]

Re: PEVerify generates improper ERROR Barry Kelly
6/23/2006 11:52:09 PM
[quoted text, click to view]

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?

[quoted text, click to view]

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.

[quoted text, click to view]

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

[quoted text, click to view]

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

--
Re: PEVerify generates improper ERROR jetan NO[at]SPAM online.microsoft.com (
6/26/2006 12:00:00 AM
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.
Re: PEVerify generates improper ERROR Ben Voigt
6/26/2006 7:09:04 AM
[quoted text, click to view]

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

[quoted text, click to view]

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?

[quoted text, click to view]

Re: PEVerify generates improper ERROR Ben Voigt
6/26/2006 7:22:10 AM
[quoted text, click to view]

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'


[quoted text, click to view]

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.

[quoted text, click to view]

Re: PEVerify generates improper ERROR Ben Voigt
6/26/2006 8:10:46 AM

[quoted text, click to view]

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);

}


[quoted text, click to view]

Re: PEVerify generates improper ERROR Ben Voigt
6/26/2006 12:25:58 PM

[quoted text, click to view]

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?

[quoted text, click to view]

Re: PEVerify generates improper ERROR Ben Voigt
6/26/2006 1:14:12 PM

[quoted text, click to view]

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.

[quoted text, click to view]

Re: PEVerify generates improper ERROR Ben Voigt
6/26/2006 6:07:26 PM

[quoted text, click to view]

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.

[quoted text, click to view]

Re: PEVerify generates improper ERROR Barry Kelly
6/26/2006 6:10:55 PM
[quoted text, click to view]

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

-- Barry

--
Re: PEVerify generates improper ERROR Barry Kelly
6/26/2006 6:40:40 PM
[quoted text, click to view]

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

[quoted text, click to view]

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

--
Re: PEVerify generates improper ERROR Barry Kelly
6/26/2006 9:49:00 PM
[quoted text, click to view]

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.

[quoted text, click to view]

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.

[quoted text, click to view]

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

--
Re: PEVerify generates improper ERROR jetan NO[at]SPAM online.microsoft.com (
6/27/2006 12:00:00 AM
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.

[quoted text, click to view]
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.
Re: PEVerify generates improper ERROR Barry Kelly
6/27/2006 1:08:35 AM
[quoted text, click to view]

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

[quoted text, click to view]

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.

[quoted text, click to view]

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

--
AddThis Social Bookmark Button