Skip to content

Commit c89a3b2

Browse files
committed
implementing fixes from PR #558 - support for the nullable IsFalse, IsTrue, Quote; opti for Invocation(Lambda(..))
1 parent 233ed5a commit c89a3b2

3 files changed

Lines changed: 135 additions & 56 deletions

File tree

‎src/FastExpressionCompiler.LightExpression/Expression.cs‎

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -627,11 +627,15 @@ public static UnaryExpression Increment(Expression expression) =>
627627

628628
/// <summary>Returns whether the expression evaluates to false.</summary>
629629
public static UnaryExpression IsFalse(Expression expression) =>
630-
new TypedUnaryExpression<bool>(ExpressionType.IsFalse, expression);
630+
Nullable.GetUnderlyingType(expression.Type) != null
631+
? new TypedUnaryExpression<bool?>(ExpressionType.IsFalse, expression)
632+
: new TypedUnaryExpression<bool>(ExpressionType.IsFalse, expression);
631633

632634
/// <summary>Returns whether the expression evaluates to true.</summary>
633635
public static UnaryExpression IsTrue(Expression expression) =>
634-
new TypedUnaryExpression<bool>(ExpressionType.IsTrue, expression);
636+
Nullable.GetUnderlyingType(expression.Type) != null
637+
? new TypedUnaryExpression<bool?>(ExpressionType.IsTrue, expression)
638+
: new TypedUnaryExpression<bool>(ExpressionType.IsTrue, expression);
635639

636640
/// <summary>Creates a UnaryExpression, given an operand and its type</summary>
637641
public static UnaryExpression MakeUnary(ExpressionType unaryType, Expression operand, Type type = null) =>
@@ -696,7 +700,8 @@ public static UnaryExpression PostDecrementAssign(Expression expression) =>
696700

697701
/// <summary>Creates a UnaryExpression that represents an expression that has a constant value of type Expression.</summary>
698702
public static UnaryExpression Quote(Expression expression) =>
699-
new NodeTypedUnaryExpression(ExpressionType.Quote, expression);
703+
new TypedUnaryExpression(ExpressionType.Quote, expression,
704+
typeof(System.Linq.Expressions.Expression<>).MakeGenericType(expression.Type));
700705

701706
/// <summary>Creates a UnaryExpression that represents a unary plus operation.</summary>
702707
public static UnaryExpression UnaryPlus(Expression expression) =>
@@ -1017,7 +1022,7 @@ public static Expression<TDelegate> Lambda<TDelegate>(Expression body, IEnumerab
10171022
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, string name, params ParameterExpression[] parameters) where TDelegate : System.Delegate =>
10181023
Lambda<TDelegate>(body, parameters, GetDelegateReturnType(typeof(TDelegate)));
10191024

1020-
// todo: @perf for now it's the fastest method until UnsafeAccessAttribute supports generics
1025+
// todo: @perf for now it's the fastest method until UnsafeAccessorAttribute supports generics
10211026
[RequiresUnreferencedCode(Trimming.Message)]
10221027
[MethodImpl((MethodImplOptions)256)]
10231028
private static Type GetDelegateReturnType(Type delegateType) => delegateType.FindDelegateInvokeMethod().ReturnType;
@@ -1250,11 +1255,20 @@ public static InvocationExpression Invoke(LambdaExpression expression) =>
12501255
public static InvocationExpression Invoke(LambdaExpression expression, Expression a0) =>
12511256
new OneArgumentInvocationExpression(expression, a0);
12521257

1258+
[MethodImpl((MethodImplOptions)256)]
1259+
[RequiresUnreferencedCode(Trimming.Message)]
1260+
private static Type GetInvocationReturnType(Expression expression) =>
1261+
expression is LambdaExpression lambda
1262+
? lambda.ReturnType
1263+
: expression is UnaryExpression unExpr && unExpr.NodeType == ExpressionType.Quote && unExpr.Operand is LambdaExpression quotedLambda
1264+
? quotedLambda.ReturnType
1265+
: expression.Type.FindDelegateInvokeMethod().ReturnType;
1266+
12531267
[RequiresUnreferencedCode(Trimming.Message)]
12541268
public static InvocationExpression Invoke(Expression expression, Expression a0) =>
12551269
expression is LambdaExpression
12561270
? new OneArgumentInvocationExpression(expression, a0)
1257-
: new TypedOneArgumentInvocationExpression(expression, expression.Type.FindDelegateInvokeMethod().ReturnType, a0);
1271+
: new TypedOneArgumentInvocationExpression(expression, GetInvocationReturnType(expression), a0);
12581272

12591273
public static InvocationExpression Invoke(Type returnType, Expression expression, Expression a0) =>
12601274
expression is LambdaExpression lambdaExpr && lambdaExpr.ReturnType == returnType
@@ -1268,7 +1282,7 @@ public static InvocationExpression Invoke(LambdaExpression expression, Expressio
12681282
public static InvocationExpression Invoke(Expression expression, Expression a0, Expression a1) =>
12691283
expression is LambdaExpression
12701284
? new TwoArgumentsInvocationExpression(expression, a0, a1)
1271-
: new TypedTwoArgumentsInvocationExpression(expression, expression.Type.FindDelegateInvokeMethod().ReturnType, a0, a1);
1285+
: new TypedTwoArgumentsInvocationExpression(expression, GetInvocationReturnType(expression), a0, a1);
12721286

12731287
public static InvocationExpression Invoke(Type returnType, Expression expression, Expression a0, Expression a1) =>
12741288
expression is LambdaExpression lambdaExpr && lambdaExpr.ReturnType == returnType
@@ -1282,7 +1296,7 @@ public static InvocationExpression Invoke(LambdaExpression expression, Expressio
12821296
public static InvocationExpression Invoke(Expression expression, Expression a0, Expression a1, Expression a2) =>
12831297
expression is LambdaExpression
12841298
? new ThreeArgumentsInvocationExpression(expression, a0, a1, a2)
1285-
: new TypedThreeArgumentsInvocationExpression(expression, expression.Type.FindDelegateInvokeMethod().ReturnType, a0, a1, a2);
1299+
: new TypedThreeArgumentsInvocationExpression(expression, GetInvocationReturnType(expression), a0, a1, a2);
12861300

12871301
public static InvocationExpression Invoke(Type returnType, Expression expression, Expression a0, Expression a1, Expression a2) =>
12881302
expression is LambdaExpression lambdaExpr && lambdaExpr.ReturnType == returnType
@@ -1296,7 +1310,7 @@ public static InvocationExpression Invoke(LambdaExpression expression, Expressio
12961310
public static InvocationExpression Invoke(Expression expression, Expression a0, Expression a1, Expression a2, Expression a3) =>
12971311
expression is LambdaExpression
12981312
? new FourArgumentsInvocationExpression(expression, a0, a1, a2, a3)
1299-
: new TypedFourArgumentsInvocationExpression(expression, expression.Type.FindDelegateInvokeMethod().ReturnType, a0, a1, a2, a3);
1313+
: new TypedFourArgumentsInvocationExpression(expression, GetInvocationReturnType(expression), a0, a1, a2, a3);
13001314

13011315
public static InvocationExpression Invoke(Type returnType, Expression expression, Expression a0, Expression a1, Expression a2, Expression a3) =>
13021316
expression is LambdaExpression lambdaExpr && lambdaExpr.ReturnType == returnType
@@ -1310,7 +1324,7 @@ public static InvocationExpression Invoke(LambdaExpression expression, Expressio
13101324
public static InvocationExpression Invoke(Expression expression, Expression a0, Expression a1, Expression a2, Expression a3, Expression a4) =>
13111325
expression is LambdaExpression
13121326
? new FiveArgumentsInvocationExpression(expression, a0, a1, a2, a3, a4)
1313-
: new TypedFiveArgumentsInvocationExpression(expression, expression.Type.FindDelegateInvokeMethod().ReturnType, a0, a1, a2, a3, a4);
1327+
: new TypedFiveArgumentsInvocationExpression(expression, GetInvocationReturnType(expression), a0, a1, a2, a3, a4);
13141328

13151329
public static InvocationExpression Invoke(Type returnType, Expression expression, Expression a0, Expression a1, Expression a2, Expression a3, Expression a4) =>
13161330
expression is LambdaExpression lambdaExpr && lambdaExpr.ReturnType == returnType
@@ -1324,7 +1338,7 @@ public static InvocationExpression Invoke(LambdaExpression expression, Expressio
13241338
public static InvocationExpression Invoke(Expression expression, Expression a0, Expression a1, Expression a2, Expression a3, Expression a4, Expression a5) =>
13251339
expression is LambdaExpression
13261340
? new SixArgumentsInvocationExpression(expression, a0, a1, a2, a3, a4, a5)
1327-
: new TypedSixArgumentsInvocationExpression(expression, expression.Type.FindDelegateInvokeMethod().ReturnType, a0, a1, a2, a3, a4, a5);
1341+
: new TypedSixArgumentsInvocationExpression(expression, GetInvocationReturnType(expression), a0, a1, a2, a3, a4, a5);
13281342

13291343
public static InvocationExpression Invoke(Type returnType, Expression expression, Expression a0, Expression a1, Expression a2, Expression a3, Expression a4, Expression a5) =>
13301344
expression is LambdaExpression lambdaExpr && lambdaExpr.ReturnType == returnType
@@ -1335,7 +1349,7 @@ public static InvocationExpression Invoke(Type returnType, Expression expression
13351349
public static InvocationExpression Invoke(Expression expression, IReadOnlyList<Expression> args) =>
13361350
expression is LambdaExpression
13371351
? new ManyArgumentsInvocationExpression(expression, args)
1338-
: new TypedManyArgumentsInvocationExpression(expression, args, expression.Type.FindDelegateInvokeMethod().ReturnType);
1352+
: new TypedManyArgumentsInvocationExpression(expression, args, GetInvocationReturnType(expression));
13391353

13401354
[RequiresUnreferencedCode(Trimming.Message)]
13411355
public static InvocationExpression Invoke(Expression expression, params Expression[] args) =>
@@ -2773,11 +2787,11 @@ internal static Expression ConvertToLightExpression(SysExpr sysExpr, ref SmallLi
27732787
var pe = (System.Linq.Expressions.ParameterExpression)sysExpr;
27742788
return Expression.Parameter(pe.IsByRef ? exprType.MakeByRefType() : exprType, pe.Name);
27752789
}
2776-
case ExpressionType.Lambda:
2777-
{
2778-
var le = (System.Linq.Expressions.LambdaExpression)sysExpr;
2779-
var body = le.Body.ToLightExpression(ref exprsConverted);
2780-
var retType = le.ReturnType;
2790+
case ExpressionType.Lambda:
2791+
{
2792+
var le = (System.Linq.Expressions.LambdaExpression)sysExpr;
2793+
var body = le.Body.ToLightExpression(ref exprsConverted);
2794+
var retType = le.ReturnType;
27812795
var pars = le.Parameters;
27822796
var parCount = pars.Count;
27832797
switch (parCount)
@@ -2819,40 +2833,40 @@ internal static Expression ConvertToLightExpression(SysExpr sysExpr, ref SmallLi
28192833
(ParameterExpression)pars[5].ToLightExpression(ref exprsConverted), retType);
28202834
default:
28212835
var pes = new ParameterExpression[parCount];
2822-
for (var i = 0; i < pes.Length; i++)
2823-
pes[i] = (ParameterExpression)le.Parameters[i].ToLightExpression(ref exprsConverted);
2824-
return Expression.Lambda(exprType, body, pes, retType);
2825-
}
2826-
}
2827-
case ExpressionType.Dynamic:
2828-
{
2829-
var de = (System.Linq.Expressions.DynamicExpression)sysExpr;
2830-
var sysArgs = de.Arguments;
2831-
var args = new Expression[sysArgs.Count];
2832-
for (var i = 0; i < args.Length; ++i)
2833-
args[i] = sysArgs[i].ToLightExpression(ref exprsConverted);
2834-
return new DynamicExpression(de.DelegateType, de.Binder, args);
2835-
}
2836-
case ExpressionType.RuntimeVariables:
2837-
{
2838-
var rve = (System.Linq.Expressions.RuntimeVariablesExpression)sysExpr;
2839-
var sysVars = rve.Variables;
2840-
var vars = new ParameterExpression[sysVars.Count];
2841-
for (var i = 0; i < vars.Length; ++i)
2842-
vars[i] = (ParameterExpression)sysVars[i].ToLightExpression(ref exprsConverted);
2843-
return new RuntimeVariablesExpression(vars);
2844-
}
2845-
case ExpressionType.DebugInfo:
2846-
{
2847-
var die = (System.Linq.Expressions.DebugInfoExpression)sysExpr;
2848-
return Expression.DebugInfo(new SymbolDocumentInfo(die.Document.FileName),
2849-
die.StartLine, die.StartColumn, die.EndLine, die.EndColumn);
2850-
}
2851-
default:
2852-
if (sysExpr is System.Linq.Expressions.UnaryExpression ue)
2853-
{
2854-
var operand = ue.Operand.ToLightExpression(ref exprsConverted);
2855-
return Expression.MakeUnary(nodeType, operand, exprType, ue.Method);
2836+
for (var i = 0; i < pes.Length; i++)
2837+
pes[i] = (ParameterExpression)le.Parameters[i].ToLightExpression(ref exprsConverted);
2838+
return Expression.Lambda(exprType, body, pes, retType);
2839+
}
2840+
}
2841+
case ExpressionType.Dynamic:
2842+
{
2843+
var de = (System.Linq.Expressions.DynamicExpression)sysExpr;
2844+
var sysArgs = de.Arguments;
2845+
var args = new Expression[sysArgs.Count];
2846+
for (var i = 0; i < args.Length; ++i)
2847+
args[i] = sysArgs[i].ToLightExpression(ref exprsConverted);
2848+
return new DynamicExpression(de.DelegateType, de.Binder, args);
2849+
}
2850+
case ExpressionType.RuntimeVariables:
2851+
{
2852+
var rve = (System.Linq.Expressions.RuntimeVariablesExpression)sysExpr;
2853+
var sysVars = rve.Variables;
2854+
var vars = new ParameterExpression[sysVars.Count];
2855+
for (var i = 0; i < vars.Length; ++i)
2856+
vars[i] = (ParameterExpression)sysVars[i].ToLightExpression(ref exprsConverted);
2857+
return new RuntimeVariablesExpression(vars);
2858+
}
2859+
case ExpressionType.DebugInfo:
2860+
{
2861+
var die = (System.Linq.Expressions.DebugInfoExpression)sysExpr;
2862+
return Expression.DebugInfo(new SymbolDocumentInfo(die.Document.FileName),
2863+
die.StartLine, die.StartColumn, die.EndLine, die.EndColumn);
2864+
}
2865+
default:
2866+
if (sysExpr is System.Linq.Expressions.UnaryExpression ue)
2867+
{
2868+
var operand = ue.Operand.ToLightExpression(ref exprsConverted);
2869+
return Expression.MakeUnary(nodeType, operand, exprType, ue.Method);
28562870
}
28572871

28582872
if (sysExpr is System.Linq.Expressions.BinaryExpression be)
@@ -5948,4 +5962,4 @@ public interface IParameterProvider
59485962
ParameterExpression GetParameter(int index);
59495963
}
59505964

5951-
#nullable restore
5965+
#nullable restore

‎src/FastExpressionCompiler/FastExpressionCompiler.cs‎

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2949,13 +2949,45 @@ private static bool TryEmitSimpleUnaryExpression(UnaryExpression expr, Expressio
29492949
ILGenerator il, ref CompilerContext context, ParentFlags parent)
29502950
{
29512951
var exprType = expr.Type;
2952+
var exprOper = expr.Operand;
2953+
if ((nodeType == ExpressionType.IsTrue | nodeType == ExpressionType.IsFalse) &&
2954+
exprOper.Type == typeof(bool?))
2955+
{
2956+
if (!TryEmit(exprOper, il, ref context, parent & ~ParentFlags.IgnoreResult))
2957+
return false;
2958+
2959+
var valueVar = EmitStoreLocalVariable(il, typeof(bool?));
2960+
var nullLabel = il.DefineLabel();
2961+
var endLabel = il.DefineLabel();
2962+
2963+
EmitLoadLocalVariableAddress(il, valueVar);
2964+
il.Demit(OpCodes.Call, typeof(bool?).GetNullableHasValueGetterMethod());
2965+
il.Demit(OpCodes.Brfalse, nullLabel);
2966+
2967+
EmitLoadLocalVariableAddress(il, valueVar);
2968+
il.Demit(OpCodes.Ldfld, typeof(bool?).GetNullableValueUnsafeAkaGetValueOrDefaultMethod());
2969+
if (nodeType == ExpressionType.IsFalse)
2970+
{
2971+
il.Demit(OpCodes.Ldc_I4_0);
2972+
il.Demit(OpCodes.Ceq);
2973+
}
2974+
il.Demit(OpCodes.Newobj, typeof(bool?).GetNullableConstructor());
2975+
il.Demit(OpCodes.Br, endLabel);
2976+
2977+
il.DmarkLabel(nullLabel);
2978+
InitValueTypeVariable(il, typeof(bool?), valueVar);
2979+
EmitLoadLocalVariable(il, valueVar);
2980+
il.DmarkLabel(endLabel);
2981+
2982+
return il.EmitPopIfIgnoreResult(parent);
2983+
}
29522984

2953-
if (!TryEmit(expr.Operand, il, ref context, parent))
2985+
if (!TryEmit(exprOper, il, ref context, parent))
29542986
return false;
29552987

29562988
if (nodeType == ExpressionType.TypeAs)
29572989
{
2958-
il.TryEmitBoxOf(expr.Operand.Type);
2990+
il.TryEmitBoxOf(exprOper.Type);
29592991
il.Demit(OpCodes.Isinst, exprType);
29602992
if (exprType.IsValueType)
29612993
il.Demit(OpCodes.Unbox_Any, exprType);
@@ -8103,8 +8135,8 @@ internal static MethodInfo FindMethod(this Type type, string methodName)
81038135

81048136
[MethodImpl((MethodImplOptions)256)]
81058137
internal static MethodInfo FindDelegateInvokeMethod(
8106-
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] this Type type) =>
8107-
type.GetMethod("Invoke");
8138+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] this Type type) =>
8139+
type.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
81088140

81098141
[RequiresUnreferencedCode(Trimming.Message)]
81108142
internal static class NullableReflected<T> where T : struct

‎test/FastExpressionCompiler.UnitTests/UnaryExpressionTests.cs‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ public int Run()
3333
Decrement_compiles();
3434
Increment_compiles();
3535
IsFalse_compiles();
36+
IsFalse_nullable_compiles();
3637
IsTrue_compiles();
38+
IsTrue_nullable_compiles();
3739
MakeUnary_compiles();
3840
Negate_compiles();
3941
NegateChecked_compiles();
@@ -53,7 +55,7 @@ public int Run()
5355
UnaryPlus_compiles();
5456
Unbox_compiles();
5557

56-
return 28;
58+
return 30;
5759
}
5860

5961

@@ -173,6 +175,21 @@ public void IsFalse_compiles()
173175
Asserts.IsTrue(result);
174176
}
175177

178+
public void IsFalse_nullable_compiles()
179+
{
180+
var param = Parameter(typeof(bool?), "b");
181+
var expression = Lambda<Func<bool?, bool?>>(IsFalse(param), param);
182+
var sys = expression.CompileSys();
183+
var fast = expression.CompileFast(true);
184+
185+
Asserts.AreEqual(false, sys(true));
186+
Asserts.AreEqual(true, sys(false));
187+
Asserts.AreEqual(null, sys(null));
188+
189+
Asserts.AreEqual(false, fast(true));
190+
Asserts.AreEqual(true, fast(false));
191+
Asserts.AreEqual(null, fast(null));
192+
}
176193

177194
public void IsTrue_compiles()
178195
{
@@ -186,6 +203,22 @@ public void IsTrue_compiles()
186203
Asserts.IsTrue(result);
187204
}
188205

206+
public void IsTrue_nullable_compiles()
207+
{
208+
var param = Parameter(typeof(bool?), "b");
209+
var expression = Lambda<Func<bool?, bool?>>(IsTrue(param), param);
210+
var sys = expression.CompileSys();
211+
var fast = expression.CompileFast(true);
212+
213+
Asserts.AreEqual(true, sys(true));
214+
Asserts.AreEqual(false, sys(false));
215+
Asserts.AreEqual(null, sys(null));
216+
217+
Asserts.AreEqual(true, fast(true));
218+
Asserts.AreEqual(false, fast(false));
219+
Asserts.AreEqual(null, fast(null));
220+
}
221+
189222

190223
public void MakeUnary_compiles()
191224
{

0 commit comments

Comments
 (0)