|
dev
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Load an object reference onto the stack ???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 On 20 Apr 2006 16:13:11 -0700, "VivekR" <KenB***@gmail.com> wrote: You can't, because IL is designed for storage in assemblies, and> Please let me know how to load an object reference onto the stack in > IL. 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 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 Kelly wrote: Show quote > On 20 Apr 2006 16:13:11 -0700, "VivekR" <KenB***@gmail.com> wrote: > > > 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 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 "VivekR" <KenB***@gmail.com> wrote: Here's a complete, compiling example.> 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. ---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 "VivekR" <KenB***@gmail.com> wrote: Here's a complete, compiling example.> 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. ---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 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 "VivekR" <KenB***@gmail.com> wrote: The IL I generated calls a passed-in delegate. Delegates are objects which> * In your example, does the runtime IL code call an instance method of > class App ? 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 The reason I used List<object> is because it grows automatically, and it> will contain multiple objects in 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 With 'ldarg 0' I'm loading the first argument to the function onto the> 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]. 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 OK. Here's a simpler example:> - 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. ---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 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 "VivekR" <KenB***@gmail.com> wrote: Then you use callvirt instead of call.> 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]. -- Barry 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 "Vivek Ragunathan" <KenB***@gmail.com> wrote: The best thing you could do is rewrite your usage case as a small program> 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. (i.e. < 100 lines, similar to my examples) that is independently compilable and demonstrates your problem, and post it here. -- Barry On 20 Apr 2006 16:13:11 -0700, "VivekR" <KenB***@gmail.com> wrote: You can't, because IL is designed for storage in assemblies, and> Please let me know how to load an object reference onto the stack in > IL. 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 |
|||||||||||||||||||||||