Load an object reference onto the stack ???

V

VivekR

Hi

I have a class CodeGen in which GenerateILBody is a method that uses
ILGenerator.Emit to create the IL code as method body for a dynamic
method DynMethod. GenerateILBody at runtime must call another method
CallBack of the class CodeGen.

All i have to do is to emit the IL code that loads the object reference
of CodeGen onto the stack, which means I must emit IL in GenerateILBody
to load 'this', which may look like:-

ilgen.Emit(OpCodeToLoad, this);

The this here refers to the CodeGen object reference, and at runtime
will have to use the reference to call the CallBack method.

But there is no overload in ILgenerator.Emit that takes an object
reference.

Please let me know how to load an object reference onto the stack in
IL.

Thanks
Vivek Ragunathan
 
B

Barry Kelly

Please let me know how to load an object reference onto the stack in
IL.

You can't, because IL is designed for storage in assemblies, and
objects currently need to live on the heap.

The way I get the behaviour you're after is by using an object[]
reference as the implicit 'this' argument to the delegate definition.
There are two DynamicMethod.CreateDelegate() overloads, and one of
them takes an object which will be stored in the created delegate as
the 'this' reference.

public Delegate CreateDelegate (
Type delegateType,
Object target
)

I pass the object[] as the second argument to this method. The
delegateType doesn't include this object[] in the signature, however,
from the MSDN docs:

delegateType
A delegate type whose signature matches that of the dynamic method,
minus the first parameter.

Using this technique, you can create a delegate which you call as
normal, but it gets passed the object[] automatically.

When generating code, you need to write something like this:

int refIndex = refs.Count;
refs.Add(myRef.Value);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4, refIndex);
il.Emit(OpCodes.Ldelem_Ref);
if (myRef.Type.IsValueType)
il.Emit(OpCodes.Unbox_Any, myRef.Type.AsNativeType);
else
il.Emit(OpCodes.Castclass, myRef.Type.AsNativeType);

This code is taken from my own code generators, where myRef is a pair
of (object reference, object type). You need the type because the
reference might be null.

Then at the end, you get your object[] from refs.ToArray().

HTH,

-- Barry
 
B

Barry Kelly

Please let me know how to load an object reference onto the stack in
IL.

You can't, because IL is designed for storage in assemblies, and
objects currently need to live on the heap.

The way I get the behaviour you're after is by using an object[]
reference as the implicit 'this' argument to the delegate definition.
There are two DynamicMethod.CreateDelegate() overloads, and one of
them takes an object which will be stored in the created delegate as
the 'this' reference.

public Delegate CreateDelegate (
Type delegateType,
Object target
)

I pass the object[] as the second argument to this method. The
delegateType doesn't include this object[] in the signature, however,
from the MSDN docs:

delegateType
A delegate type whose signature matches that of the dynamic method,
minus the first parameter.

Using this technique, you can create a delegate which you call as
normal, but it gets passed the object[] automatically.

When generating code, you need to write something like this:

int refIndex = refs.Count;
refs.Add(myRef.Value);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4, refIndex);
il.Emit(OpCodes.Ldelem_Ref);
if (myRef.Type.IsValueType)
il.Emit(OpCodes.Unbox_Any, myRef.Type);
else
il.Emit(OpCodes.Castclass, myRef.Type);

This code is taken from my own code generators, where myRef is a pair
of (object reference, object type). You need the type because the
reference might be null.

Then at the end, you get your object[] from refs.ToArray().

HTH,

-- Barry

-- Barry
 
V

VivekR

Hi Barry

The method CallBack does not reside in the same assembly as the
GenerateILbody method, and hence i need the object reference of the
GenerateILBody ie CodeGen in the dynamically generated method.

Am i clear cuz it is tough to explain in words.

Thanks
Vivek Ragunathan



Barry said:
Please let me know how to load an object reference onto the stack in
IL.

You can't, because IL is designed for storage in assemblies, and
objects currently need to live on the heap.

The way I get the behaviour you're after is by using an object[]
reference as the implicit 'this' argument to the delegate definition.
There are two DynamicMethod.CreateDelegate() overloads, and one of
them takes an object which will be stored in the created delegate as
the 'this' reference.

public Delegate CreateDelegate (
Type delegateType,
Object target
)

I pass the object[] as the second argument to this method. The
delegateType doesn't include this object[] in the signature, however,
from the MSDN docs:

delegateType
A delegate type whose signature matches that of the dynamic method,
minus the first parameter.

Using this technique, you can create a delegate which you call as
normal, but it gets passed the object[] automatically.

When generating code, you need to write something like this:

int refIndex = refs.Count;
refs.Add(myRef.Value);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldc_I4, refIndex);
il.Emit(OpCodes.Ldelem_Ref);
if (myRef.Type.IsValueType)
il.Emit(OpCodes.Unbox_Any, myRef.Type.AsNativeType);
else
il.Emit(OpCodes.Castclass, myRef.Type.AsNativeType);

This code is taken from my own code generators, where myRef is a pair
of (object reference, object type). You need the type because the
reference might be null.

Then at the end, you get your object[] from refs.ToArray().

HTH,

-- Barry
 
V

VivekR

Hi Barry

May be your idea might work, I did not understand it fully.

Let me try to be somewhat more clear than before. I am generating
dynamic code in CodeGen, and the dynamic code is in a different
assembly, and it must call the CallBack method in CodeGen.

If you could help me with the ILGen.Emit code to call the delegate or
the method, it would be nice.

Thanks
Vivek Ragunathan
 
B

Barry Kelly

VivekR said:
Let me try to be somewhat more clear than before. I am generating
dynamic code in CodeGen, and the dynamic code is in a different
assembly, and it must call the CallBack method in CodeGen.

If you could help me with the ILGen.Emit code to call the delegate or
the method, it would be nice.

Here's a complete, compiling example.

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

class App
{
delegate void SomeMethod(string arg);

static void Main()
{
SomeMethod printer = Console.WriteLine;

SomeMethod compiled = CompileMethod(printer, "This was passed: ");

compiled("42");
compiled("64");
}

static SomeMethod CompileMethod(SomeMethod reference, string prefix)
{
DynamicMethod result = new DynamicMethod("f", typeof(void),
new Type[] { typeof(object[]), typeof(string) }, typeof(App));

ILGenerator cg = result.GetILGenerator();

List<object> refs = new List<object>();

// load 'reference', which will be the 'this' for the reference.
LoadConstant(refs, cg, reference, typeof(SomeMethod));

// load 'prefix'
LoadConstant(refs, cg, prefix, typeof(string));

// load 'arg'
cg.Emit(OpCodes.Ldarg_1);

// Concatenate the two strings, prefix and 'arg'.
cg.Emit(OpCodes.Call,
typeof(string).GetMethod("Concat",
new Type[] { typeof(string), typeof(string) }));

// Invoke the passed-in delegate.
cg.Emit(OpCodes.Call, typeof(SomeMethod).GetMethod("Invoke"));

cg.Emit(OpCodes.Ret);

return (SomeMethod) result.CreateDelegate(typeof(SomeMethod),
refs.ToArray());
}

static void LoadConstant(List<object> refs, ILGenerator cg,
object constant, Type type)
{
int index = refs.FindIndex(delegate(object obj)
{ return object.Equals(obj, constant); });
if (index < 0)
{
index = refs.Count;
refs.Add(constant);
}

cg.Emit(OpCodes.Ldarg_0);
cg.Emit(OpCodes.Ldc_I4, index);
cg.Emit(OpCodes.Ldelem_Ref);
if (type.IsValueType)
cg.Emit(OpCodes.Unbox_Any, type);
else
cg.Emit(OpCodes.Castclass, type);
}
}
--->8---

HTH,

-- Barry
 
B

Barry Kelly

VivekR said:
Let me try to be somewhat more clear than before. I am generating
dynamic code in CodeGen, and the dynamic code is in a different
assembly, and it must call the CallBack method in CodeGen.

If you could help me with the ILGen.Emit code to call the delegate or
the method, it would be nice.

Here's a complete, compiling example.

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

class App
{
delegate void SomeMethod(string arg);

static void Main()
{
SomeMethod printer = Console.WriteLine;

SomeMethod compiled = CompileMethod(printer, "This was passed: ");

compiled("42");
compiled("64");
}

static SomeMethod CompileMethod(SomeMethod reference, string prefix)
{
DynamicMethod result = new DynamicMethod("f", typeof(void),
new Type[] { typeof(object[]), typeof(string) }, typeof(App));

ILGenerator cg = result.GetILGenerator();

List<object> refs = new List<object>();

// load 'reference', which will be the 'this' for the reference.
LoadConstant(refs, cg, reference, typeof(SomeMethod));

// load 'prefix'
LoadConstant(refs, cg, prefix, typeof(string));

// load 'arg'
cg.Emit(OpCodes.Ldarg_1);

// Concatenate the two strings, prefix and 'arg'.
cg.Emit(OpCodes.Call,
typeof(string).GetMethod("Concat",
new Type[] { typeof(string), typeof(string) }));

// Invoke the passed-in delegate.
cg.Emit(OpCodes.Call, typeof(SomeMethod).GetMethod("Invoke"));

cg.Emit(OpCodes.Ret);

return (SomeMethod) result.CreateDelegate(typeof(SomeMethod),
refs.ToArray());
}

static void LoadConstant(List<object> refs, ILGenerator cg,
object constant, Type type)
{
int index = refs.FindIndex(delegate(object obj)
{ return object.Equals(obj, constant); });
if (index < 0)
{
index = refs.Count;
refs.Add(constant);
}

cg.Emit(OpCodes.Ldarg_0);
cg.Emit(OpCodes.Ldc_I4, index);
cg.Emit(OpCodes.Ldelem_Ref);
if (type.IsValueType)
cg.Emit(OpCodes.Unbox_Any, type);
else
cg.Emit(OpCodes.Castclass, type);
}
}
--->8---

HTH,

-- Barry
 
V

VivekR

Hi Barry

* I was examing your example, and these are my queries:-

* In your example, does the runtime IL code call an instance method of
class App ?

* And I do not exactly understand why you are using List<object>, which
will contain multiple objects in it.

* Why are you comparing

* In the statements
cg.Emit(OpCodes.Ldarg_0);
cg.Emit(OpCodes.Ldc_I4, index);
cg.Emit(OpCodes.Ldelem_Ref);
When you Ldelem_Ref, do you mean to say that the value loaded is the
'this' [ie the App's object reference].

* Summarizing my required in your example's terms
- App must create a dynamic method [it does using Dynamic
Method].
- The IL code in the dynamic method must call a instance
method, say CallBackMethod, of App. [I am not clear if it does].
And for calling the CallBackMethod, i need to load the
object reference of App, and call the method.

Bear with me for my ignorance, cuz i feel this one is complex and
pretty hard to debug where it fails.

Thanks Very Much
Vivek Ragunathan
 
V

VivekR

Hi Barry

I also forgot to mention another important thing, that the instance
method to be called from the dynamic IL code must be virtual. Yes, it
must be a virtual method [CallBackMethod].

Let me know your ideas.

Thanks
Vivek Ragunathan
 
B

Barry Kelly

VivekR said:
* In your example, does the runtime IL code call an instance method of
class App ?

The IL I generated calls a passed-in delegate. Delegates are objects which
have a method called "Invoke". When you call a delegate in C#, the
compiler compiles it as a call to the instance method "Invoke" on the
delegate instance.

So, in order for the IL to call this Invoke method in my example, it needs
to do three things:

* Load the object reference (i.e. the delegate) onto the stack. This will
be the 'this' reference for the method call later on.
* Load the arguments to the method call. In the case of my example, the
Invoke method's arguments are the same as the delegate's method arguments,
that is to say a single argument of type string.
* Actually invoke the (non-virtual) instance method "Invoke".

So, my code calls an instance method called "Invoke" on the object
reference "SomeMethod reference" passed as a parameter to the
CompileMethod method.
* And I do not exactly understand why you are using List<object>, which
will contain multiple objects in it.

The reason I used List<object> is because it grows automatically, and it
can be converted easily to an object[] with the method ToArray(). The
reason I use object[] as the 'this' pointer associated with the created
delegate (passed to DynamicMethod.CreateDelegate) is because it is a
general solution (i.e. it will work in all cases) to the problem: "How to
load an object reference onto the stack?", for any number of object
references of any type.
* Why are you comparing
?

* In the statements
cg.Emit(OpCodes.Ldarg_0);
cg.Emit(OpCodes.Ldc_I4, index);
cg.Emit(OpCodes.Ldelem_Ref);
When you Ldelem_Ref, do you mean to say that the value loaded is the
'this' [ie the App's object reference].

With 'ldarg 0' I'm loading the first argument to the function onto the
stack. This will be the object reference associated with the delegate
which was invoked. When I created the delegate with
DynamicMethod.CreateDelegate, I passed in an 'object[]' as the object
reference associated with the delegate. That means that an 'object[]'
reference is the first parameter passed to the function. (Make sure you're
fully aware of how .NET delegates work - they are a pair of (object
reference, method reference).)

With ldc.i4 <index> I'm loading the index to grab out of the array onto
the stack. And with ldelem.ref I'm copying the object reference at the
given index out of the object array and onto the stack.
* Summarizing my required in your example's terms
- App must create a dynamic method [it does using Dynamic
Method].
- The IL code in the dynamic method must call a instance
method, say CallBackMethod, of App. [I am not clear if it does].
And for calling the CallBackMethod, i need to load the
object reference of App, and call the method.

Bear with me for my ignorance, cuz i feel this one is complex and
pretty hard to debug where it fails.

OK. Here's a simpler example:

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

class App
{
delegate void SomeMethod(string arg);

static void Main()
{
SomeMethod compiled = CreateCall(new App(), "TargetMethod");

compiled("Hello World!");
}

public void TargetMethod(string arg)
{
Console.WriteLine("Target Method got called with '{0}'.", arg);
}

static SomeMethod CreateCall(object reference, string methodName)
{
if (reference == null)
throw new NullReferenceException(
"'reference' can't be null.");

DynamicMethod result = new DynamicMethod("f", typeof(void),
new Type[] { reference.GetType(), typeof(string) },
typeof(App));

ILGenerator cg = result.GetILGenerator();

// load 'this', the reference instance
cg.Emit(OpCodes.Ldarg_0);
// load 'arg', the argument for the target method.
cg.Emit(OpCodes.Ldarg_1);

MethodInfo method = reference.GetType().GetMethod(methodName,
new Type[] { typeof(string) });
if (method == null)
throw new Exception(
string.Format("Method '{0}' not found.", methodName));

if (method.IsVirtual)
cg.Emit(OpCodes.Callvirt, method);
else
cg.Emit(OpCodes.Call, method);

cg.Emit(OpCodes.Ret);

return (SomeMethod) result.CreateDelegate(typeof(SomeMethod),
reference);
}
}
--->8---

-- Barry
 
B

Barry Kelly

VivekR said:
I also forgot to mention another important thing, that the instance
method to be called from the dynamic IL code must be virtual. Yes, it
must be a virtual method [CallBackMethod].

Then you use callvirt instead of call.

-- Barry
 
V

Vivek Ragunathan

Hi Barry,

Into specifics now, my situation is that the instance method
CallBackMethod is actually an evnet handler. Whenever an event occurs,
the dynamic il code will have to call this event handler, but in our
case, if it is virtual, i get
AccessViolationException/NullReferenceException followed by
System.Reflection.TargetInvocationException, which i suspect is due to
Ldarg_0. But it works fine if the instance is non-virtual.

Any quick ideas.

Thanks
Vivek Ragunathan
 
B

Barry Kelly

Vivek Ragunathan said:
Into specifics now, my situation is that the instance method
CallBackMethod is actually an evnet handler. Whenever an event occurs,
the dynamic il code will have to call this event handler, but in our
case, if it is virtual, i get
AccessViolationException/NullReferenceException followed by
System.Reflection.TargetInvocationException, which i suspect is due to
Ldarg_0. But it works fine if the instance is non-virtual.

The best thing you could do is rewrite your usage case as a small program
(i.e. < 100 lines, similar to my examples) that is independently
compilable and demonstrates your problem, and post it here.

-- Barry
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top