all groups > dotnet clr > april 2007 >
You're in the

dotnet clr

group:

Reflection.Emit Call


Re: Reflection.Emit Call Barry Kelly
4/12/2007 2:27:13 PM
dotnet clr: [quoted text, click to view]

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

--
Reflection.Emit Call Fabio
4/12/2007 2:32:14 PM
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!

Re: Reflection.Emit Call Fabio
4/12/2007 6:08:37 PM
"Barry Kelly" <barry.j.kelly@gmail.com> ha scritto nel messaggio
[quoted text, click to view]

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

Re: Reflection.Emit Call Barry Kelly
4/12/2007 7:09:18 PM
[quoted text, click to view]

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

--
Re: Reflection.Emit Call Fabio
4/12/2007 11:50:43 PM
"Barry Kelly" <barry.j.kelly@gmail.com> ha scritto nel messaggio
news:nhss13pb3m6td0es0tamsdmn7mc1mnodl8@4ax.com...

[quoted text, click to view]

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.


[quoted text, click to view]

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

[quoted text, click to view]

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

Thanks


Re: Reflection.Emit Call Barry Kelly
4/12/2007 11:54:02 PM
[quoted text, click to view]

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

[quoted text, click to view]

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

[quoted text, click to view]

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

--
Re: Reflection.Emit Call Fabio
4/13/2007 2:36:45 AM
"Barry Kelly" <barry.j.kelly@gmail.com> ha scritto nel messaggio
news:9oct13ln5hgipevammti8clevtvmhi8vki@4ax.com...


[quoted text, click to view]

No.
I got the same error launching the compiled exe :(
[quoted text, click to view]

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 :(



[quoted text, click to view]

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

Re: Reflection.Emit Call Fabio
4/13/2007 2:46:04 AM
"Fabio" <znt.fabio@virgilio.it> ha scritto nel messaggio
news:%23o4RHPWfHHA.4772@TK2MSFTNGP05.phx.gbl...


[quoted text, click to view]

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


Re: Reflection.Emit Call Ben Voigt
4/13/2007 2:53:04 PM
[quoted text, click to view]

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.

[quoted text, click to view]

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.

Re: Reflection.Emit Call Barry Kelly
4/13/2007 8:57:15 PM
[quoted text, click to view]

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

--
Re: Reflection.Emit Call Barry Kelly
4/13/2007 10:32:12 PM
[quoted text, click to view]

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

--
Re: Reflection.Emit Call [find out] Fabio
4/14/2007 12:00:00 AM
"Barry Kelly" <barry.j.kelly@gmail.com> ha scritto nel messaggio
news:jitv13p5rm0s346cstppkl4ngh021a1sj0@4ax.com...

[quoted text, click to view]

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!

Re: Reflection.Emit Call [find out] Ben Voigt
4/17/2007 8:29:36 PM

[quoted text, click to view]

see MethodInfo.IsStatic, MethodInfo.IsVirtual properties

[quoted text, click to view]

AddThis Social Bookmark Button