Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating method dynamically, and executing it

Background:

I want to define few static methods in C# , and generate IL code as byte array, from one of these methods, selected at runtime (on client), and send the byte array over network to another machine (server) where it should be executed after re-generating the IL code from the byte array.

My Attempt: (POC)

public static class Experiment {     public static int Multiply(int a, int b)     {         Console.WriteLine("Arguments ({0}, {1})", a, b);         return a * b;     } } 

And then I get the IL code of the method body, as:

BindingFlags flags = BindingFlags.Public | BindingFlags.Static; MethodInfo meth = typeof(Experiment).GetMethod("Multiply", flags); byte[] il = meth.GetMethodBody().GetILAsByteArray(); 

So far I didn't create anything dynamically. But I've IL code as byte array, and I want to create an assembly, then a module in it, then a type, then a method - all dynamically. When creating the method body of the dynamically created method, I use the IL code which I got using reflection in the above code.

The code-generation code is as follows:

AppDomain domain = AppDomain.CurrentDomain; AssemblyName aname = new AssemblyName("MyDLL"); AssemblyBuilder assemBuilder = domain.DefineDynamicAssembly(                                                aname,                                                 AssemblyBuilderAccess.Run);  ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule("MainModule");  TypeBuilder tb = modBuilder.DefineType("MyType",                              TypeAttributes.Public | TypeAttributes.Class);  MethodBuilder mb = tb.DefineMethod("MyMethod",       MethodAttributes.Static | MethodAttributes.Public,       CallingConventions.Standard,      typeof(int),                          // Return type      new[] { typeof(int), typeof(int) });  // Parameter types  mb.DefineParameter(1, ParameterAttributes.None, "value1");  // Assign name  mb.DefineParameter(2, ParameterAttributes.None, "value2");  // Assign name   //using the IL code to generate the method body mb.CreateMethodBody(il, il.Count());   Type realType = tb.CreateType();  var meth = realType.GetMethod("MyMethod"); try {     object result = meth.Invoke(null, new object[] { 10, 9878 });     Console.WriteLine(result);  //should print 98780 (i.e 10 * 9878) } catch (Exception e) {     Console.WriteLine(e.ToString()); } 

But instead of printing 98780 on the output window, it throws an exception saying,

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.TypeLoadException: Could not load type 'Invalid_Token.0x0100001E' from assembly 'MyDLL, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
     at MyType.MyMethod(Int32 value1, Int32 value2) [...]

Please help me figuring out the cause of the error, and how to fix it.

like image 261
Nawaz Avatar asked Oct 06 '11 07:10

Nawaz


People also ask

What is dynamic method?

The Dynamic Method is an approach for solving thermodynamic equilibria in single process units (Zinser et al., 2015, 2016b). It can handle chemical and phase equilibria, as well as mixed problems. This approach is based on the solution of a set of ODEs into their steady state.

How do you call a method dynamically in C#?

This can be useful in a situation where you dynamically created an object of unknown type and need to call its method: var do = xs. Deserialize(new XmlTextReader(ms)); // example - XML deserialization do. GetType().

How to generate a Python code dynamically?

To execute dynamically generated Python code, use Python's exec function; to evaluate a dynamically generated Python expression, use Python's eval function. Beware: using these functions (or any of the below) with input from the user potentially allows the user to execute arbitrary code.

What is dynamic function in Java?

Dynamic method dispatch is the mechanism in which a call to an overridden method is resolved at run time instead of compile time. This is an important concept because of how Java implements run-time polymorphism.


2 Answers

Run ildasm.exe on an arbitrary assembly. Use View + Show token values and look at some disassembled code. You'll see that the IL contains references to other methods and variables through a number. The number is an index into the metadata tables for an assembly.

Perhaps you now see the problem, you cannot transplant a chunk of IL from one assembly to another unless that target assembly has the same metadata. Or unless you replace the metadata token values in the IL with values that match the metadata of the target assembly. This is wildly impractical of course, you essentially end up duplicating the assembly. Might as well make a copy of the assembly. Or for that matter, might as well use the existing IL in the original assembly.

You need to think this through a bit, it is pretty unclear what you actually try to accomplish. The System.CodeDom and Reflection.Emit namespaces are available to dynamically generate code.

like image 169
Hans Passant Avatar answered Oct 04 '22 08:10

Hans Passant


If I take the following method:

public static int Multiply(int a, int b) {     return a * b; } 

compile it in Release mode and use it in your code, everything works fine. And if I inspect the il array, it contains 4 bytes that correspond exactly to the four instructions from Jon's answer (ldarg.0, ldarg.1, mul, ret).

If I compile it in debug mode, the code is 9 bytes. And in Reflector, it looks like this:

.method public hidebysig static int32 Multiply(int32 a, int32 b) cil managed {     .maxstack 2     .locals init (         [0] int32 CS$1$0000)     L_0000: nop      L_0001: ldarg.0      L_0002: ldarg.1      L_0003: mul      L_0004: stloc.0      L_0005: br.s L_0007     L_0007: ldloc.0      L_0008: ret  } 

The problematic part is the local variable. If you just copy the instruction bytes, you emit instruction that uses a local variable, but you never declare it.

If you were using ILGenerator, you could use DeclareLocal(). It seems you will be able to set local variables using MethodBuilder.SetMethodBody() in .Net 4.5 (the following works for me in VS 11 DP):

var module = typeof(Experiment).Module; mb.SetMethodBody(il, methodBody.MaxStackSize,                  module.ResolveSignature(methodBody.LocalSignatureMetadataToken),                  null, null); 

But I haven't found a way to do that in .Net 4, except by using reflection to set a private field of MethodBuilder, that contains the local variables, after calling CreateMethodBody():

typeof(MethodBuilder)     .GetField("m_localSignature", BindingFlags.NonPublic | BindingFlags.Instance)     .SetValue(mb, module.ResolveSignature(methodBody.LocalSignatureMetadataToken)); 

Regarding the original error: Types and methods from other assemblies (like System.Console and System.Console.WriteLine) are referenced using tokens. And those tokens are different from assembly to assembly. That means code to call Console.WriteLine() in one assembly will be different from code to call the same method in another assembly, if you look at the instruction bytes.

What that means is that you would have to actually understand what the IL bytes mean and replace all tokens that reference types, methods, etc. to ones that are valid in the assembly you're building.

like image 34
svick Avatar answered Oct 04 '22 08:10

svick