Reputation: 18296
I have a method which generates a DynamicMethod using ILEmit, and I want to inline its contents inside an expression tree. I need to do this in order to write the expression tree to an assembly.
I can compile the DynamicMethod and include that into the tree, but this prevents me from writing the expression tree to an assembly with the following error:
CompileToMethod cannot compile constant 'BulkUtil+BlitMethod[Byte]' because it is a non-trivial value, such as a live object. Instead, create an expression tree that can construct this value.
Since I'm generating the body of the method anyways, I figured I'd just inline it into the tree so that it could be output. However, because the method generates unsafe code I don't know how to translate it directly.
Is there a way to directly add IL to an expression tree?
Upvotes: 1
Views: 405
Reputation: 7587
Unfortunately, expression trees being written to methods is very limited. You can only write static methods and they can only reference already materialized classes or methods. (i.e. no builders).
Before I gave up on the idea entirely, I tried the approach of creating "stub" static and instance methods, that would call a delegate with all the parameters of the callee with the same name.
e.g.
public class MyClass<Proxy>
{
static Func<MyClass,int,float,long,double,object> <Delegate>Foo
public override object Foo(int a,float b,long c,double d){
return MyClass_Proxy.<Delegate>Foo.Invoke(this,a,b,c,d)
}
}
That way since everything is materialized the expression tree can reference all members. It's a terrible hack that grew up into a dynamic proxy. This works (I'd even say well) until you come to generic methods or generic class methods and then it falls on its face.
You can take a look at my take on these proxy's here: https://bitbucket.org/mburbea/delegatedproxy/src ProxiedTypeBuilder and ProxiedMethodBuilder do most of the hard work creating the proxy classes based off a class or an interface. My problem surface was a little more severe as I need the ability for my proxied methods to call their base implementations (which is what most of the ugly code is there to do), and I came up with a suitable way for my need to handle methods with generic parameters.
Upvotes: 1
Reputation: 244908
I think that trying to inline the IL into the expression tree is not the right approach, because it would mean you would have to know how the IL generated from the expression looks like (at least to some degree). Also, as far as I know, there is no simple way to do that.
What you could do instead is to create your IL method as a normal MethodBuilder
in the generated assembly (i.e. not DynamicMethod
) and then call that method from the expression.
The code, with a very simple IL and expression, could look something like this:
var assembly = AssemblyBuilder.DefineDynamicAssembly(
new AssemblyName("a"), AssemblyBuilderAccess.RunAndSave);
var module = assembly.DefineDynamicModule("a.dll");
var ilType = module.DefineType("IlType");
var ilMethod = ilType.DefineMethod(
"M", MethodAttributes.Public | MethodAttributes.Static,
typeof(int), Type.EmptyTypes);
var il = ilMethod.GetILGenerator();
il.Emit(OpCodes.Ldc_I4, 42);
il.Emit(OpCodes.Ret);
var createdIlType = ilType.CreateType();
var expressionType = module.DefineType("ExpressionType");
var expressionMethod = expressionType.DefineMethod(
"M", MethodAttributes.Public | MethodAttributes.Static,
typeof(int), Type.EmptyTypes);
var lambda = Expression.Lambda<Func<int>>(
Expression.Call(createdIlType.GetMethod("M")));
lambda.CompileToMethod(expressionMethod);
expressionType.CreateType();
assembly.Save("a.dll");
It seems that an Expression
can't call MethodBuilder
directly, which means that the two methods have to be in separate types and that the type that contains the IL method has to be created first.
Upvotes: 2