Skip to content

Commit ae65ecb

Browse files
authored
Allow explicit return type object (#186)
* No longer omit the conversion expression when the return type is explicitly object. Force the return type of a lambda expression to typeof(void) to prevent the emission of a conversion expression. Fixes #185 * Remove PrepareDelegateInvoke to avoid modifying the arguments list.
1 parent afaeaaf commit ae65ecb

File tree

3 files changed

+66
-30
lines changed

3 files changed

+66
-30
lines changed

src/DynamicExpresso.Core/Interpreter.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,16 @@ public Expression<TDelegate> ParseAsExpression<TDelegate>(string expressionText,
405405

406406
internal LambdaExpression ParseAsExpression(Type delegateType, string expressionText, params string[] parametersNames)
407407
{
408-
var lambda = ParseAs(delegateType, expressionText, parametersNames);
408+
var delegateInfo = ReflectionExtensions.GetDelegateInfo(delegateType, parametersNames);
409+
410+
// return type is object means that we have no information beforehand
411+
// => we force it to typeof(void) so that no conversion expression is emitted by the parser
412+
// and the actual expression type is preserved
413+
var returnType = delegateInfo.ReturnType;
414+
if (returnType == typeof(object))
415+
returnType = typeof(void);
416+
417+
var lambda = ParseAsLambda(expressionText, returnType, delegateInfo.Parameters);
409418
return lambda.LambdaExpression(delegateType);
410419
}
411420

src/DynamicExpresso.Core/Parsing/Parser.cs

+35-29
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private Expression ParseExpressionSegment(Type returnType)
101101
int errorPos = _token.pos;
102102
var expression = ParseExpressionSegment();
103103

104-
if (returnType != typeof(void) && returnType != typeof(object))
104+
if (returnType != typeof(void))
105105
{
106106
return GenerateConversion(expression, returnType, errorPos);
107107
}
@@ -1126,31 +1126,41 @@ private MemberBinding[] ParseMemberInitializerList(Type newType)
11261126
}
11271127

11281128
private Expression ParseLambdaInvocation(LambdaExpression lambda, int errorPos)
1129+
{
1130+
return ParseInvocation(lambda, errorPos, ErrorMessages.ArgsIncompatibleWithLambda);
1131+
}
1132+
1133+
private Expression ParseDelegateInvocation(Expression delegateExp, int errorPos)
1134+
{
1135+
return ParseInvocation(delegateExp, errorPos, ErrorMessages.ArgsIncompatibleWithDelegate);
1136+
}
1137+
1138+
private Expression ParseInvocation(Expression expr, int errorPos, string error)
11291139
{
11301140
var args = ParseArgumentList();
11311141

1132-
if (!PrepareDelegateInvoke(lambda.Type, ref args))
1133-
throw CreateParseException(errorPos, ErrorMessages.ArgsIncompatibleWithLambda);
1142+
var invokeMethod = FindInvokeMethod(expr.Type);
1143+
if (invokeMethod == null || !CheckIfMethodIsApplicableAndPrepareIt(invokeMethod, args))
1144+
throw CreateParseException(errorPos, error);
11341145

1135-
return Expression.Invoke(lambda, args);
1146+
return Expression.Invoke(expr, invokeMethod.PromotedParameters);
11361147
}
11371148

11381149
private Expression ParseMethodGroupInvocation(MethodGroupExpression methodGroup, int errorPos)
11391150
{
11401151
var args = ParseArgumentList();
11411152

11421153
// find the best delegates that can be used with the provided arguments
1143-
var flags = BindingFlags.Public | BindingFlags.Instance;
11441154
var candidates = methodGroup.Overloads
11451155
.Select(_ => new
11461156
{
11471157
Delegate = _,
11481158
Method = _.Method,
1149-
InvokeMethods = _.GetType().FindMembers(MemberTypes.Method, flags, Type.FilterName, "Invoke").Cast<MethodInfo>(),
1159+
InvokeMethod = FindInvokeMethod(_.GetType()),
11501160
})
11511161
.ToList();
11521162

1153-
var applicableMethods = FindBestMethod(candidates.SelectMany(_ => _.InvokeMethods), args);
1163+
var applicableMethods = FindBestMethod(candidates.Select(_ => _.InvokeMethod), args);
11541164

11551165
// no method found: retry with the delegate's method
11561166
// (the parameters might be different, e.g. params array, default value, etc)
@@ -1164,31 +1174,10 @@ private Expression ParseMethodGroupInvocation(MethodGroupExpression methodGroup,
11641174
throw CreateParseException(errorPos, ErrorMessages.AmbiguousDelegateInvocation);
11651175

11661176
var applicableMethod = applicableMethods[0];
1167-
var usedDeledate = candidates.Single(_ => new[] { _.Method }.Concat(_.InvokeMethods).Any(m => m == applicableMethod.MethodBase)).Delegate;
1177+
var usedDeledate = candidates.Single(_ => new[] { _.Method, _.InvokeMethod?.MethodBase }.Any(m => m == applicableMethod.MethodBase)).Delegate;
11681178
return Expression.Invoke(Expression.Constant(usedDeledate), applicableMethod.PromotedParameters);
11691179
}
11701180

1171-
private Expression ParseDelegateInvocation(Expression delegateExp, int errorPos)
1172-
{
1173-
var args = ParseArgumentList();
1174-
1175-
if (!PrepareDelegateInvoke(delegateExp.Type, ref args))
1176-
throw CreateParseException(errorPos, ErrorMessages.ArgsIncompatibleWithDelegate);
1177-
1178-
return Expression.Invoke(delegateExp, args);
1179-
}
1180-
1181-
private bool PrepareDelegateInvoke(Type type, ref Expression[] args)
1182-
{
1183-
var applicableMethods = FindMethods(type, "Invoke", false, args);
1184-
if (applicableMethods.Length != 1)
1185-
return false;
1186-
1187-
args = applicableMethods[0].PromotedParameters;
1188-
1189-
return true;
1190-
}
1191-
11921181
private Type ParseKnownType()
11931182
{
11941183
var name = _token.text;
@@ -1797,6 +1786,23 @@ private MethodData[] FindMethods(Type type, string methodName, bool staticAccess
17971786
return new MethodData[0];
17981787
}
17991788

1789+
private MethodData FindInvokeMethod(Type type)
1790+
{
1791+
var flags = BindingFlags.Public | BindingFlags.DeclaredOnly |
1792+
BindingFlags.Instance | _bindingCase;
1793+
foreach (var t in SelfAndBaseTypes(type))
1794+
{
1795+
var method = t.FindMembers(MemberTypes.Method, flags, _memberFilterCase, "Invoke")
1796+
.Cast<MethodBase>()
1797+
.SingleOrDefault();
1798+
1799+
if (method != null)
1800+
return MethodData.Gen(method);
1801+
}
1802+
1803+
return null;
1804+
}
1805+
18001806
private MethodData[] FindExtensionMethods(string methodName, Expression[] args)
18011807
{
18021808
var matchMethods = _arguments.GetExtensionMethods(methodName);

test/DynamicExpresso.UnitTest/GithubIssues.cs

+21
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,27 @@ public void GitHub_Issue_169_quatro()
347347
Assert.AreEqual("56", result);
348348
}
349349

350+
[Test]
351+
public void GitHub_Issue_185()
352+
{
353+
var interpreter = new Interpreter().SetVariable("a", 123L);
354+
355+
// forcing the return type to object should work
356+
// (ie a conversion expression should be emitted from long to object)
357+
var del = interpreter.ParseAsDelegate<Func<object>>("a*2");
358+
var result = del();
359+
Assert.AreEqual(246, result);
360+
}
361+
362+
[Test]
363+
public void GitHub_Issue_185_2()
364+
{
365+
var interpreter = new Interpreter().SetVariable("a", 123L);
366+
var del = interpreter.ParseAsDelegate<Func<dynamic>>("a*2");
367+
var result = del();
368+
Assert.AreEqual(246, result);
369+
}
370+
350371
[Test]
351372
public void GitHub_Issue_191()
352373
{

0 commit comments

Comments
 (0)