How to inline a Call? - c#

How can I retrieve the body of a defined method and convert it directly to a LINQ Expression?
For example this method is defined (in Program.cs):
public static void InlineMethod(string message)
{
Console.WriteLine(message);
Debug.WriteLine(message);
}
And the following represents the LINQ Expression:
var method = typeof(Program).GetMethod(nameof(InlineMethod))!;
var param = Expression.Parameter(typeof(string));
var call = Expression.Call(method, param);
var block = Expression.Block(call);
var lambda = Expression.Lambda(block, param);
((Action<string>)lambda.Compile())("Hello World");
This works of course because the called method is compiled as a call:
.Block() {
.Call Solution.Program.InlineMethod($var1)
}
The goal is to dynamically (even if the method body changes) retrieve the body of the InlineMethod and produce (in this example) the following compile results:
.Block() {
.Call System.Console.WriteLine($var1);
.Call System.Diagnostics.Debug.WriteLine($var1);
}

Related

Text query parsing in Sprache

I'm trying to write some code to match strings based on a pattern:
pattern: "dog and (cat or goat)"
test string: "doggoat" result: true
test string: "dogfrog" result: false
I'm trying to write a parser using Sprache, with most of the logic provided by Corey's excellent answer to a similar problem. I'm almost there, but I'm getting an exception when the code is run:
'The binary operator AndAlso is not defined for the types System.Func2[System.String,System.Boolean]' and ''System.Func`2[System.String,System.Boolean]'.'
I understand that this means that I need to combine the lambdas at the expression tree nodes with the logical operators, which I've attempted using an ExpressionVisitor based on the answer to another question here. However, the program crashes before the ExpressionVisitor is executed - it appears that the Parse command is executed first, but I don't quite understand why (maybe it's because the Sprache.Parse.Select statement doesn't force execution of the lambda?), or how to force it to be executed first.
Sample code is below (I've stripped all operators but the 'and' out for brevity, reintroducing them from Corey's template is trivial. Sprache must be added from NuGet for the code to compile.
class Program
{
static void Main(string[] args)
{
var patternString = "dog and cat";
var strTest = "dog cat";
var strTest2 = "dog frog";
var conditionTest = ConditionParser.ParseCondition(patternString);
var fnTest = conditionTest.Compile();
bool res1 = fnTest(strTest); //true
bool res2 = fnTest(strTest2); //false
}
}
public static class ConditionParser
{
static ParameterExpression Param = Expression.Parameter(typeof(string), "_");
public static Expression<Func<string, bool>> ParseCondition(string text)
{
return Lambda.Parse(text);
}
private static Parser<Expression<Func<string, bool>>> Lambda
{
get
{
var reduced = AndTerm.End().Select(delegate (Expression body)
{
var replacer = new ParameterReplacer(Param);
return Expression.Lambda<Func<string, bool>>((BinaryExpression)replacer.Visit(body), Param);
});
return reduced;
}
}
static Parser<Expression> AndTerm =>
Parse.ChainOperator(OpAnd, StringMatch, Expression.MakeBinary);
// Other operators (or, not etc.) can be chained here, between AndTerm and StringMatch
static Parser<ExpressionType> OpAnd = MakeOperator("and", ExpressionType.AndAlso);
private static Parser<Expression> StringMatch =>
Parse.Letter.AtLeastOnce()
.Text().Token()
.Select(value => StringContains(value));
static Expression StringContains(string subString)
{
MethodInfo contains = typeof(string).GetMethod("Contains");
var call = Expression.Call(
Expression.Constant(subString),
contains,
Param
);
var ret = Expression.Lambda<Func<string, bool>>(call, Param);
return ret;
}
// Helper: define an operator parser
static Parser<ExpressionType> MakeOperator(string token, ExpressionType type)
=> Parse.IgnoreCase(token).Token().Return(type);
}
internal class ParameterReplacer : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
protected override Expression VisitParameter(ParameterExpression node)
{
return base.VisitParameter(_parameter);
}
internal ParameterReplacer(ParameterExpression parameter)
{
_parameter = parameter;
}
}
There are several issues with your code, but the main problem causing the exception in question is the StringContains method which returns lambda expression. And Expression.AndAlso (as well as most Expression methods) are based on simple non lambda expressions (or lambda expression bodies). The whole idea of the parsing code is to identify and combine simple expressions, and make a single lambda expression from the resulting expression.
To fix the original problem, the StringContains method should return directly the MethodCall expression rather than lambda expression.
The second problem in the same StringContains method is that it reverses the arguments to string.Contains. It basically does token.Contains(parameter) while according to the expected results it should do the opposite.
The whole method (using another handy Expression.Call overload) can be reduced to
static Expression StringContains(string subString) =>
Expression.Call(Param, "Contains", Type.EmptyTypes, Expression.Constant(subString));
Now everything should work as expected.
However, since the ConditionParser class is using a single ParameterExpression instance, which then is used to build the lambda expression, there is no need for ParameterReplacer, so the Lambda method (property) can be reduced to
private static Parser<Expression<Func<string, bool>>> Lambda =>
AndTerm.End().Select(body => Expression.Lambda<Func<string, bool>>(body, Param));

Incorrect number of arguments supplied for call to method. Expression.call(MethodInfo, Expression)

The following code does:
Creates a Lambda Expression
Compiles it to a method
Calls the method via Expression.Call
The code
public static void Main()
{
ParameterExpression paramExpr = Expression.Parameter(typeof(int), "arg");
LambdaExpression lambdaExpr = Expression.Lambda(
Expression.Add(
paramExpr,
Expression.Constant(1)
),
new List<ParameterExpression>() { paramExpr }
);
Console.WriteLine(lambdaExpr);
var method = lambdaExpr.Compile().Method;
var expressions = new Expression[]
{
Expression.Constant(5)
};
Expression.Call(method, expressions);
}
The error
Run-time exception (line 27): Incorrect number of arguments supplied
for call to method 'Int32
lambda_method(System.Runtime.CompilerServices.Closure, Int32)'
Where did the System.Runtime.CompilerServices.Closure, come from?
How do I invoke this method using Expression.Call?
I know there are other ways of achieving the same goal, like calling lambdaExpr.Compile().DynamicInvoke() but, due to constraints, I need to do it in the above way.

Can I generate an async method dynamically using System.Linq.Expressions?

I know the compiler can't convert an async lambda expression to an expression tree, but is it possible to generate the expression tree manually ?
var expr = Expression.Lambda<Func<Task>>(
// how do I use 'await' in the body here?
);
var func = expr.Compile();
I can't find any method related to async or await in the Expression class, but perhaps there's another way?
await involves significant compiler re-writing; the generated IL is quite dissimilar to the original C#, with variable hoisting (onto a class) and branching, tasks, continuations, etc. It certainly isn't something that can be represented in a simple lambda, although in recent versions of .NET with richer Expression support (Expression.Block etc), technically I suppose it is probably possible to mimic most of the things the compiler does for await - but you'd probably go quite loopy trying to do it by hand.
No, AFAIK, no facility to automate this translation exists in the Expression API, and frankly I wouldn't ever expect there to be.
The same probably could be say of ILGenerator; frankly, AFAIK the only "easy" way (and I use the word "easy" quite incorrectly) to use await in meta-programming would be to generate C# and run it through roslyn or CSharpCodeProvider.
Late to the party, but I just published a lib which does accomplish this.
https://github.com/avonwyss/bsn.AsyncLambdaExpression
It leverages the fact that nested Lambdas in an Expression Tree can access the outer lambda variables (which are captured as closure, just like nested lambdas behave in C#). Thus the code creates a main lambda containing all the necessary variables and returning a Task, and a state machine in a nested lambda which is callable as continuation on awaiter.OnComplete(). Since the state, current awaiter etc. is stored in the closure, the state machine keeps its state even when the inner lambda stops execution.
The API to use it consists of two extension methods, one for awaiting and one for converting the Expression Tree with await to a lambda implementing the state machine and returning the Task.
Here's a code sample:
// Build a normal expression tree with "await" calls
var paraInput = Expression.Parameter(typeof(string), "input");
var exprTree = Expression.Lambda<Func<Task<string>, string>>(
Expression.Block(
Expression.Call(typeof(Task), nameof(Task.Delay), null, Expression.Constant(1000)).Await(false),
paraInput.Await(false)),
paraInput);
// Create compilable state machine async expression tree (result must be Task<?> or Task)
var asyncExprTree = exprTree.Async<Func<Task<string>, Task<string>>>();
var asyncCompiled = asyncExprTree.Compile();
// Invoke delegate as usual
var result = await asyncCompiled(Task.FromResult("test")).ConfigureAwait(false);
The DebugView of the original lambda Expression Tree is like this:
.Lambda #Lambda1<System.Func`2[System.Threading.Tasks.Task`1[System.String],System.String]>(System.Threading.Tasks.Task`1[System.String] $input)
{
.Block() {
.Call bsn.AsyncLambdaExpression.AsyncExpressionExtensions.AwaitVoid(.Call (.Call System.Threading.Tasks.Task.Delay(1000)).ConfigureAwait(False));
.Call bsn.AsyncLambdaExpression.AsyncExpressionExtensions.AwaitResult(.Call $input.ConfigureAwait(False))
}
}
The async conversion creates the following Expression Tree:
.Lambda #Lambda1<System.Func`2[System.Threading.Tasks.Task`1[System.String],System.Threading.Tasks.Task`1[System.String]]>(System.Threading.Tasks.Task`1[System.String] $input)
{
.Block(
System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter $awaiter,
System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[System.String] $awaiter,
System.Int32 $state,
System.Int32 $resumeState,
System.Threading.Tasks.TaskCompletionSource`1[System.String] $taskCompletionSource,
System.Action $continuation,
System.Exception $exception) {
$taskCompletionSource = .New System.Threading.Tasks.TaskCompletionSource`1[System.String](.Constant<System.Threading.Tasks.TaskCreationOptions>(RunContinuationsAsynchronously));
$continuation = .Lambda #Lambda2<System.Action>;
.Invoke $continuation();
$taskCompletionSource.Task
}
}
.Lambda #Lambda2<System.Action>() {
.Try {
.Loop {
.Switch ($state) {
.Case (0):
.Block() {
$state = 1;
.If (
!($awaiter = .Call (.Call (.Call System.Threading.Tasks.Task.Delay(50)).ConfigureAwait(False)).GetAwaiter()).IsCompleted
) {
.Block() {
.Call $awaiter.OnCompleted($continuation);
.Break :break { }
}
} .Else {
.Default(System.Void)
}
}
.Case (1):
.Block() {
$state = 2;
.Call $awaiter.GetResult();
.If (
!($awaiter = .Call (.Call $input.ConfigureAwait(False)).GetAwaiter()).IsCompleted
) {
.Block() {
.Call $awaiter.OnCompleted($continuation);
.Break :break { }
}
} .Else {
.Default(System.Void)
}
}
.Case (2):
.Block(System.String $result:2) {
$result:2 = .Call $awaiter.GetResult();
.Call $taskCompletionSource.SetResult($result:2);
.Break :break { }
}
.Default:
.Throw .New System.InvalidOperationException()
}
}
.LabelTarget :break:
} .Catch (System.Exception $ex) {
.Call $taskCompletionSource.SetException($ex)
}
}
This is a simple example; but the async conversion can not only handle await, but also the control flow expressions (loop, conditional, label/goto, try..catch..finally) in order to offer support for a wide variety of Expression Trees.

Body from Func<T>

how can I get a body from function
Func<bool> methodCall = () => output.SendToFile();
if (methodCall())
Console.WriteLine("Success!");
I need to get this output.SendToFile() as a string
Another example:
string log = "";
public void Foo<T>(Func<T> func)
{
try
{
var t = func();
}
catch (Exception)
{
//here I need to add the body of the lambda
// log += func.body;
}
}
public void Test()
{
var a = 5;
var b = 6;
Foo(() => a > b);
}
Edit:
For more information on this topic see: Expression Trees
You can't. A Func<T> is nothing you can easily analyze. If you want to analyze a lambda, you need to create a Expression<Func<bool>> and analyze it.
Getting the body of an expression is simple:
Expression<Func<bool>> methodCall = () => output.SendToFile();
var body = methodCall.Body;
body would be a MethodCallExpression you could further analyze or just output via ToString. Using ToString won't result exactly in what you would like to have, but it contains that information, too.
For example, executing ToString() on body in LINQPad results in something like this:
value(UserQuery+<>c__DisplayClass0).output.SendToFile()
As you can see, "output.SendToFile()" is there.
To actually execute the code defined by an expression, you first need to compile it:
var func = methodCall.Compile();
func();
Which can be shortened to this:
methodCall.Compile()(); // looks strange but is valid.

Does an expression tree Parameter need to reuse same instance?

I'm using a combination of reflection and expression trees, and want to pass back certain property accessors from a class to a calling method. My current code has a method traversing the class and returning a list of MemberExpressions. The caller then iterates over the member expressions and creates lambdas, which should then be called with an instance of the inspected class to return the value of the property.
Here is a sample of what it would look like without the method calls (Runnable in LINQPad):
void Main()
{
var t = new Test { Prop = "Test" };
var property = t.GetType().GetProperty("Prop");
var baseType = Expression.Parameter(typeof(Test), "baseType");
var memberAccess = Expression.MakeMemberAccess(baseType, property);
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, Expression.Parameter(typeof(Test), "baseType"));
var func = lambda.Compile();
var result = func(t);
result.Dump();
}
class Test {
public string Prop { get; set; }
}
This does not work, throwing this exception:
InvalidOperationException: variable 'baseType' of type 'UserQuery+Test' referenced from scope '', but it is not defined
However, if I change the creation of the lambda to this:
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, baseType);
That is, replace the Expression.Parameter with the variable used earlier, then it works. This is not (easily) possible in the scenario where I want to use it, since I would have to return the original parameter along with the list (I could return a tuple, of course, but I would prefer not to, if it is not necessary).
Why does it work like this? Inspecting the DebugView of the lambda, they are exactly the same no matter what approach is used:
.Lambda #Lambda1<System.Func`2[UserQuery+Test,System.String]>(UserQuery+Test $baseType)
{
$baseType.S
}
Yes, you need to refer ParameterExpression, used earlier. This won't compile too:
private String Foo(Test myParam)
{
return myAnotherParam.MyProperty;
}
With instatiating new ParameterExpression in lambda, you're doing the same thing (but note, when making lambda, you're doing it in reversed order - first, you're constructing a method body, then - a method declaration):
// return myAnotherParam.MyProperty;
var baseType = Expression.Parameter(typeof(Test), "baseType");
var memberAccess = Expression.MakeMemberAccess(baseType, property);
// private String Foo(MyClass myParam)
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, Expression.Parameter(typeof(Test), "baseType"));

Categories