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 / April 2007

Tip: Looking for answers? Try searching our database.

Reflection.Emit Call

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Fabio - 12 Apr 2007 13:32 GMT
Hi all!

Someone can tell me how to add a

call instance <method>

to my IL with Reflection.Emit()?

I can add

call <method>
callvirt <method>

but I need a call instance <method>, because (it seems) that with value
types the callvirt give the warning <'instance' added to method's calling
convention> compiling with ILASM :(

the code is:

.locals init (
int32 V_0
)
IL_0005: ldc.i4     3
IL_0011: stloc.0
IL_0012: ldloca.s   V_0
IL_0014: callvirt   string [mscorlib]System.Int32::ToString()

Using

IL_0014: call instance string [mscorlib]System.Int32::ToString()

All go fine, but I don't know how to write in with Reflection.Emit...

Some help?

Thanks!
Barry Kelly - 12 Apr 2007 14:27 GMT
> Hi all!
>
> Someone can tell me how to add a
>
> call instance <method>

The 'instance' is part of the method ref/def. If you have a MethodInfo
that refers to an instance method, then you get 'instance', if it's
static, you get 'static'. As long as you got the right MethodInfo, you
don't need to worry about it.

-- Barry

Signature

http://barrkel.blogspot.com/

Fabio - 12 Apr 2007 17:08 GMT
"Barry Kelly" <barry.j.kelly@gmail.com> ha scritto nel messaggio

>> Hi all!
>>
[quoted text clipped - 6 lines]
> static, you get 'static'. As long as you got the right MethodInfo, you
> don't need to worry about it.

mmm... the method in question is Int32.ToString() that is an instance method
and it is (i think) a "callvirt" method, since it is an override of
object.ToString() (I think I can correctly get that it is an override
because method.IsVirtual and method.IsHideBySig are true).

So, if I call Emit(CallVirt... it works, but the IlAsm tell me that the
right way to do it is call instance, and if I replace in the code callvirt
with call instance ilasm compile without claims.

So, my question is: can I do a Emit(Call ...  instance?

Thanks
Barry Kelly - 12 Apr 2007 19:09 GMT
> "Barry Kelly" <barry.j.kelly@gmail.com> ha scritto nel messaggio
> >
[quoted text clipped - 19 lines]
>
> So, my question is: can I do a Emit(Call ...  instance?

What's wrong with this?

---8<---
   static void Write42(ILGenerator cg)
   {
       LocalBuilder x = cg.DeclareLocal(typeof(Int32));
       cg.Emit(OpCodes.Ldc_I4, 42);
       cg.Emit(OpCodes.Stloc, x);
       cg.Emit(OpCodes.Ldloca, x);
       cg.Emit(OpCodes.Call,
           typeof(System.Int32).GetMethod("ToString",
               new Type[0]));
       cg.Emit(OpCodes.Call,
           typeof(System.Console).GetMethod("WriteLine",
               new Type[] { typeof(string) }));
   }
--->8---

Does it do something different to what you want?

I suggest you read the specification for 'call', in Partition III
section 3.19 of ECMA 335. In particular:

"The metadata token carries sufficient information to determine whether
the call is to a static method, an instance method, a virtual method, or
a global function."

[...]

"It is valid to call a virtual method using call (rather than callvirt);
this indicates that the method is to be resolved using the class
specified by method rather than as specified dynamically from the object
being invoked"

-- Barry

Signature

http://barrkel.blogspot.com/

Fabio - 12 Apr 2007 22:50 GMT
> What's wrong with this?
>
[quoted text clipped - 15 lines]
>
> Does it do something different to what you want?

Not for me, but just try to debug (i.e. with the DbgCLR.exe) the produced
code

----
.locals init (

int32 V_0

)

IL_0000: ldc.i4 42

IL_0005: stloc.0

IL_0006: ldloca.s V_0

IL_0008: call string [mscorlib]System.Int32::ToString()

IL_000d: call void [mscorlib]System.Console::WriteLine(string)

----

I got a (traslated from italian) 'cannot find the method 'System.String
System.Int32.ToString()'.

This because using the 'call' you must calla *static* method (i.e.
Console.WriteLine()).
For virtual methods you have to use 'callvirt', and for instance methods
(overridden or non virtual) you should use 'call instance', if you use
callvirt the compiler correct this for you, but it is not the right way.

> I suggest you read the specification for 'call', in Partition III
> section 3.19 of ECMA 335. In particular:

It's about a week that I'm studing that (obscene) document

> "The metadata token carries sufficient information to determine whether
> the call is to a static method, an instance method, a virtual method, or
[quoted text clipped - 6 lines]
> specified by method rather than as specified dynamically from the object
> being invoked"

and also something different as I described before and that I don't remember
where (mybe on MSDN).

Thanks
Barry Kelly - 12 Apr 2007 23:54 GMT
> Not for me, but just try to debug (i.e. with the DbgCLR.exe) the produced
> code
> [...]
> I got a (traslated from italian) 'cannot find the method 'System.String
> System.Int32.ToString()'.

Sounds like a bug in DbgCLR. I've never used it.

> This because using the 'call' you must calla *static* method (i.e.
> Console.WriteLine()).

This is not true. Read the specification. In particular, it's not
necessary to use callvirt for overridden ToString() calls on valuetypes,
because all valuetypes are sealed (aka final). Further quote from 3.19:

"When using the call opcode to call a non-final virtual method on an
instance other than a boxed value type, verification checks that the
instance reference to the method being called is the result of ldarg.s
0, ldarg 0 and the caller’s body does not contain starg.s 0, starg 0 or
ldarga.s 0, ldarga 0."

In fact it's important in this case, in order to avoid redundant boxing
and to permit the JIT to inline calls.

If I recall correctly, C# always generates callvirt even for non-virtual
instance calls on reference types because part of the semantics of C# is
that calling an instance method on a null instance causes a
NullPointerException. This is not the case for some other languages
targeting the CLR. For example, Delphi for .NET permits instance method
calls on null instances. In order for this to work on the CLR, it needs
to use the 'call' opcode instead of 'callvirt'.

> For virtual methods you have to use 'callvirt', and for instance methods
> (overridden or non virtual) you should use 'call instance'

This 'call instance' is a complete fabrication by *you*. It DOES NOT
EXIST in that form. The 'instance' is part of the METHOD specification,
to help ILASM perform the lookup. IT IS NOT PART OF THE CALL OPCODE.

When you see:

 'call instance void [foo]Bar.Baz()'

you should read this:

 (call)  (instance void [foo]Bar.Baz())

The 'instance' is saying that Bar.Baz is an instance method, i.e., is
not a static method.

At the binary level, the call opcode is a single byte (value 0x28), and
it is followed by a 32-bit integer token. This token refers to either a
MethodRef or MethodDef (or MethodSpec) in the assembly; it's the
MethodDef / MethodRef that contains all information necessary to
actually link to the target method. (MethodSpec is for generic methods.)

-- Barry

Signature

http://barrkel.blogspot.com/

Fabio - 13 Apr 2007 01:36 GMT
>> I got a (traslated from italian) 'cannot find the method 'System.String
>> System.Int32.ToString()'.
>
> Sounds like a bug in DbgCLR. I've never used it.

No.
I got the same error launching the compiled exe :(

>> This because using the 'call' you must calla *static* method (i.e.
>> Console.WriteLine()).
>
> This is not true. Read the specification. In particular, it's not
> necessary to use callvirt for overridden ToString() calls on valuetypes,
> because all valuetypes are sealed (aka final). Further quote from 3.19:

Well, here I think you can be right :)
I'm having another issue to investigate with Int32.ToString(string)
I'll re-read the call chapter :(

> This 'call instance' is a complete fabrication by *you*. It DOES NOT
> EXIST in that form. The 'instance' is part of the METHOD specification,
> to help ILASM perform the lookup. IT IS NOT PART OF THE CALL OPCODE.

I don't understand this.

call instance <method>

is compilable, and in decompiled code (I can see it with Reflector) I can
see the 4 distinct forms:
call
call instance
callvirt
callvirt instance

and If call instance doesn't exist, why ilasm tell me to specify it, and
when I do it all goes right?

Thanks again
Fabio - 13 Apr 2007 01:46 GMT
> I'm having another issue to investigate with Int32.ToString(string)

Nothing, As I sayd, If I check Method.IsHideBySig == true and use the
callvirt all works fine (except the ilasm that claims about it).

But I'll re-read the call chapter...
Ben Voigt - 13 Apr 2007 20:53 GMT
> call instance <method>
>
[quoted text clipped - 4 lines]
> callvirt
> callvirt instance

No, they are not distinct forms.  There is only call and callvirt.  The
argument to call or callvirt may have an instance modifier, but it is not a
new opcode.

> and If call instance doesn't exist, why ilasm tell me to specify it, and
> when I do it all goes right?

const int i;
int * p = &i; // error
const int * q = &i; // ok

But q is not const!  I can now do q++;  Only what is pointed *to* is const,
not the pointer.

Similarly, what is called is an instance method, the call is not a "call
instance".  The keyword "instance" modifies the method, not the call.
Barry Kelly - 13 Apr 2007 20:57 GMT
> >> I got a (traslated from italian) 'cannot find the method 'System.String
> >> System.Int32.ToString()'.
[quoted text clipped - 3 lines]
> No.
> I got the same error launching the compiled exe :(

Observe:

Step 1: Save the following as EmitWrite32.cs

---8<---
using System;
using System.Reflection;
using System.Reflection.Emit;

class Program
{
   delegate void Method();
   
   static void Main()
   {
       try
       {
           AssemblyBuilder assBuilder =
               AppDomain.CurrentDomain.DefineDynamicAssembly(
                   new AssemblyName("Write42"),
                   AssemblyBuilderAccess.Save);
           ModuleBuilder modBuilder =
               assBuilder.DefineDynamicModule("Write42.exe");
           TypeBuilder typeBuilder =
               modBuilder.DefineType("App");
           MethodBuilder mainMeth =
               typeBuilder.DefineMethod("Main",
                   MethodAttributes.Public
                   | MethodAttributes.Static);
           Write42(mainMeth.GetILGenerator());
           typeBuilder.CreateType();
           assBuilder.SetEntryPoint(mainMeth);
           assBuilder.Save("Write42.exe");
       }
       catch (Exception ex)
       {
           Console.WriteLine(ex.Message);
       }
   }
   
   static void Write42(ILGenerator cg)
   {
       LocalBuilder x = cg.DeclareLocal(typeof(Int32));
       cg.Emit(OpCodes.Ldc_I4, 42);
       cg.Emit(OpCodes.Stloc, x);
       cg.Emit(OpCodes.Ldloca, x);
       cg.Emit(OpCodes.Call,
           typeof(System.Int32).GetMethod("ToString",
               new Type[0]));
       cg.Emit(OpCodes.Call,
           typeof(System.Console).GetMethod("WriteLine",
               new Type[] { typeof(string) }));
       cg.Emit(OpCodes.Ret);
   }
}
--->8---

Step 2: Compile with 'csc EmitWrite42.cs'

Step 3: Execute EmitWrite42.exe and observe that Write42.exe is created
without error.

Step 4: Execute Write42.exe and verify that it does indeed write 42,
without error.

Step 5: Execute 'ildasm Write42.exe -all -out:Write42.il', look inside
Write42.il and observe the following code has been emitted for App.Main:

---8<---
 .method /*06000001*/ public static void
         Main() cil managed
 // SIG: 00 00 01
 {
   .entrypoint
   // Method begins at RVA 0x2050
   // Code size       19 (0x13)
   .maxstack  1
   .locals /*11000001*/ init (int32 V_0)
   IL_0000:  /* 20   | 2A000000         */ ldc.i4     0x2a
   IL_0005:  /* 0A   |                  */ stloc.0
   IL_0006:  /* 12   | 00               */ ldloca.s   V_0
   IL_0008:  /* 28   | (0A)000001       */ call       instance string
[mscorlib/*23000001*/]System.Int32/*01000002*/::ToString() /* 0A000001
*/
   IL_000d:  /* 28   | (0A)000002       */ call       void
[mscorlib/*23000001*/]System.Console/*01000003*/::WriteLine(string) /*
0A000002 */
   IL_0012:  /* 2A   |                  */ ret
 } // end of method App::Main
--->8---

In particular, I draw your attention to these two lines:

---8<---
   IL_0008:  /* 28   | (0A)000001       */ call       instance string
[mscorlib/*23000001*/]System.Int32/*01000002*/::ToString() /* 0A000001
*/
   IL_000d:  /* 28   | (0A)000002       */ call       void
[mscorlib/*23000001*/]System.Console/*01000003*/::WriteLine(string) /*
0A000002 */
--->8---

'call instance' in the first line, just 'call' in the second, all with
just a single opcode with value 0x28: magic!

Why?

Because the first call is to an instance method, and the second call is
to a static method. It really is as simple as that!

-- Barry

Signature

http://barrkel.blogspot.com/

Barry Kelly - 13 Apr 2007 22:32 GMT
> and If call instance doesn't exist, why ilasm tell me to specify it, and
> when I do it all goes right?

A piece of the puzzle you may be missing is that instance methods have a
hidden 'this' parameter. For example:

 class A { void M() {} static void M() {} }

Now 'void A::M()' is ambiguous - does it refer to the instance method or
the static method? The 'instance' is what ILASM needs to solve this
problem.

When you're using Reflection.Emit, you don't have this problem, because
the method has already been resolved, by dint of having acquired a
MethodInfo for it.

-- Barry

Signature

http://barrkel.blogspot.com/

Fabio - 14 Apr 2007 08:51 GMT
> When you're using Reflection.Emit, you don't have this problem, because
> the method has already been resolved, by dint of having acquired a
> MethodInfo for it.

I'm really sorry, it was all my fault :(

All the discussion was because I have a class that translate
OpCodes.Callvirt to the string "callvirt" regardless of the tharget method.
(for debug reasons I compile the code produced by this class with the ilasm,
I don't use the AssemblyBuilder.Save(), but I think I need a revision of
this strategy).

Just one finally question (I know you already explained this in theory, but
I need a "code" confirmation)
Having the MethodInfo, how can I say "ok, on this I can use
callvirt/call/call instance" to be compiled with ILasm?

Thanks again to all!
Ben Voigt - 18 Apr 2007 02:29 GMT
>> When you're using Reflection.Emit, you don't have this problem, because
>> the method has already been resolved, by dint of having acquired a
[quoted text clipped - 13 lines]
> Having the MethodInfo, how can I say "ok, on this I can use
> callvirt/call/call instance" to be compiled with ILasm?

see MethodInfo.IsStatic, MethodInfo.IsVirtual properties

> Thanks again to all!

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.