Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create mutually callable MethodInfos from MethodBuilders created from LambdaExpressions?

I'm currently working on an compiler in C#, where the behaviour is defined by LambdaExpressions, and then using CompileToMethod, transformed into MethodBuilders and saved to DLL. All functions are public and static.

However, I could not find a way to extract usable MethodInfo (or another method of reference) from the MethodBuilder until the behaviour is defined and declaring type is created/sealed. That means that at that until that point, it is impossible to use Expression.Call to call these functions. That makes self-recursion or mutual referencing between two functions impossible.

I ended up using Reflection to invoke the functions at runtime, but it's very suboptimal, and I'm still curious if there's a better way.

How do i ensure functions created with LambdaExpression.CompileToMethod(MethodBuilder) can self-call?

Alternatively, is there any other way to use LambdaExpressions which would allow this and support saving as a static method to a dll?

like image 991
Kravaros Avatar asked Apr 23 '20 02:04

Kravaros


1 Answers

I hope this helps.
This is complete code example which produces runtime defined type with single static recursive method.
For the simplicity of the example the recursive method is infinite - at the end of the Main method the recursive method is called

static void Main(string[] args)
{
    var moduleBuilder = CreateDynamicModuleBuilder();
    var typeBuilder = moduleBuilder.DefineType("Person", TypeAttributes.Public | TypeAttributes.Class);
    var methodBuilder = typeBuilder.DefineMethod("SayHello", MethodAttributes.Static | MethodAttributes.Public);
    var methodExpression = CreateRecursiveExpression();
    var lambda = Expression.Lambda(methodExpression);
    lambda.CompileToMethod(methodBuilder);

    var typeInfo = typeBuilder.CreateType();
    var methodInfo = typeInfo.GetMethod("SayHello", BindingFlags.Public | BindingFlags.Static);
    methodInfo.Invoke(null, null);
}

private static Expression CreateRecursiveExpression()
{
    var methodInfo = typeof(Console).GetMethod("WriteLine", new[] { typeof(String) });
    var arg = Expression.Constant("Hello");
    var consoleCall = Expression.Call(methodInfo, arg);
    var sayHelloActionVariable = Expression.Variable(typeof(Action), "sayHelloAction");
    var block = Expression.Block(
        new[] { sayHelloActionVariable },
        Expression.Assign(
            sayHelloActionVariable,
            Expression.Lambda(
                Expression.Block(
                    consoleCall,
                    Expression.Invoke(sayHelloActionVariable)
                )
            )
        ),
        Expression.Invoke(sayHelloActionVariable)
    );

    return block;
}

private static ModuleBuilder CreateDynamicModuleBuilder()
{
    var name = new AssemblyName("Example.DynamicRecursion");
    var am = AssemblyBuilder.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);
    var mb = am.DefineDynamicModule(name.Name, $"{name.Name}.dll");
    return mb;
}

This code will create type with the following signature

public class Person
{
   public static void SayHello()
   {
       Action sayHelloAction;
       sayHelloAction = () => 
       {
           Console.WriteLine("Hello");
           sayHelloAction();
       }
       sayHelloAction();
   }
}
like image 185
vasil oreshenski Avatar answered Oct 17 '22 23:10

vasil oreshenski