@@ -77,8 +77,8 @@ public enum CompilerFlags : byte
7777 EnableDelegateDebugInfo = 1 << 1,
7878 /// <summary>When the flag is set then instead of the returning `null` the specific exception is thrown*346</summary>
7979 ThrowOnNotSupportedExpression = 1 << 2,
80- /// <summary>Will try to evalaute constant, arithmetic, logical, comparison expressions consisting of the former expression types,
81- /// and emit the result only to the IL instead the whole computation. Minimizes IL and moves optimization to the compilation phase if possible .</summary>
80+ /// <summary>Will try to Interpret arithmetic, logical, comparison expressions for the primitive types,
81+ /// and emit the IL the result only instead of the whole computation .</summary>
8282 DisableInterpreter = 1 << 4
8383 }
8484
@@ -493,7 +493,7 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
493493 Type[] closurePlusParamTypes, Type returnType, CompilerFlags flags)
494494 {
495495#endif
496- // Try to avoid complilation altogether for Func<bool> delegates via Interpreter, see #468
496+ // Try to avoid compilation altogether for Func<bool> delegates via Interpreter, see #468
497497 if ((flags & CompilerFlags.DisableInterpreter) == 0 &
498498 returnType == typeof(bool) & closurePlusParamTypes.Length == 1
499499 && Interpreter.IsCandidateForInterpretation(bodyExpr)
@@ -521,7 +521,7 @@ internal static object TryCompileBoundToFirstClosureParam(Type delegateType, Exp
521521 closure = new DebugArrayClosure(constantsAndNestedLambdas, debugExpr);
522522 }
523523
524- // note: @slow this is what System.Compiles does and which makes the compilation significally slower 10x , but the invocation become faster by a single branch instruction
524+ // note: @slow this is what System.Compiles does and which makes the compilation 10x slower, but the invocation become faster by a single branch instruction
525525 // var method = new DynamicMethod(string.Empty, returnType, closurePlusParamTypes, true);
526526 // this is FEC way, significantly faster compilation, but +1 branch instruction in the invocation
527527 var method = new DynamicMethod(string.Empty, returnType, closurePlusParamTypes, typeof(ArrayClosure), true);
@@ -6391,7 +6391,7 @@ private static void EmitLoadArgAddress(ILGenerator il, int paramIndex)
63916391 }
63926392 }
63936393
6394- /// <summary>Interprter </summary>
6394+ /// <summary>Interpreter </summary>
63956395 public static class Interpreter
63966396 {
63976397 /// <summary>Always returns true</summary>
@@ -6574,7 +6574,7 @@ public static object GetZeroDefaultObject(TypeCode typeCode)
65746574 }
65756575
65766576 /// <summary>Fast, mostly negative check to skip or proceed with interpretation.
6577- /// Depending on the context you may avoid calling it because you know the interpeted expression beforehand,</summary>
6577+ /// Depending on the context you may avoid calling it because you know the interpreted expression beforehand,</summary>
65786578 [MethodImpl(MethodImplOptions.AggressiveInlining)]
65796579 public static bool IsCandidateForInterpretation(Expression expr)
65806580 {
@@ -6588,7 +6588,7 @@ public static bool IsCandidateForInterpretation(Expression expr)
65886588 expr is BinaryExpression;
65896589 }
65906590
6591- /// <summary>In case of exception FEC will emit the whole computation to throw expection in the invocation phase</summary>
6591+ /// <summary>In case of exception FEC will emit the whole computation to throw exception in the invocation phase</summary>
65926592 public static bool TryInterpretBoolean(out bool result, Expression expr)
65936593 {
65946594 try
@@ -6607,50 +6607,31 @@ public static bool TryInterpretBoolean(out bool result, Expression expr)
66076607 return false;
66086608 }
66096609
6610- /// <summary>Tries to interpret the expression of the Primitive type of Constant, Convert, Logical, Comparison, Arithmetic</summary>
6610+ /// <summary>Tries to interpret the expression of the Primitive type of Constant, Convert, Logical, Comparison, Arithmetic. </summary>
66116611 public static bool TryInterpretPrimitive(out object result, Expression expr)
66126612 {
66136613 Debug.Assert(expr.Type.IsPrimitive);
66146614 result = null;
66156615
6616- var nodeType = expr.NodeType;
6617- if (nodeType == ExpressionType.Constant)
6618- {
6619- #if LIGHT_EXPRESSION
6620- if (((ConstantExpression)expr).RefField != null)
6621- return false;
6622- #endif
6623- result = ((ConstantExpression)expr).Value;
6624- if (expr.Type == typeof(bool))
6625- result = (bool)result ? TrueObject : FalseObject;
6626- return true;
6627- }
6628-
6629- if (nodeType == ExpressionType.Default)
6630- {
6631- result = GetZeroDefaultObject(Type.GetTypeCode(expr.Type));
6632- return true;
6633- }
6616+ // The order of the checks for the type of the expression is deliberate,
6617+ // because we are starting with complex expressions first.
6618+ // And for the simplest ones like a Constant? the method may not be even called.
6619+ // Instead, the Constant check and interpretation may be done inline.
66346620
6635- if (nodeType == ExpressionType.Convert)
6621+ var nodeType = expr.NodeType;
6622+ if (nodeType == ExpressionType.Not)
66366623 {
66376624 var unaryExpr = (UnaryExpression)expr;
6638- var operand = unaryExpr.Operand;
6639- if (!TryInterpretPrimitive(out result, operand))
6640- return false;
6641- var exprType = expr.Type;
6642- if (operand.Type != exprType && !exprType.IsAssignableFrom(operand.Type))
6625+ var operandExpr = unaryExpr.Operand;
6626+ if (operandExpr is ConstantExpression co)
66436627 {
6644- var converted = System.Convert.ChangeType(result, exprType);
6645- result = exprType != typeof(bool) ? converted : (bool)converted ? TrueObject : FalseObject;
6628+ #if LIGHT_EXPRESSION
6629+ if (co.RefField != null) return false;
6630+ #endif
6631+ result = (bool)co.Value ? FalseObject : TrueObject;
6632+ return true;
66466633 }
6647- return true;
6648- }
6649-
6650- if (nodeType == ExpressionType.Not)
6651- {
6652- var unaryExpr = (UnaryExpression)expr;
6653- if (!TryInterpretPrimitive(out var boolVal, unaryExpr.Operand))
6634+ if (!TryInterpretPrimitive(out var boolVal, operandExpr))
66546635 return false;
66556636 result = boolVal == TrueObject ? FalseObject : TrueObject;
66566637 return true;
@@ -6659,71 +6640,152 @@ public static bool TryInterpretPrimitive(out object result, Expression expr)
66596640 if (IsLogical(nodeType))
66606641 {
66616642 var binaryExpr = (BinaryExpression)expr;
6662- if (!TryInterpretPrimitive(out var leftVal, binaryExpr.Left))
6643+
6644+ // Interpreting the left part as the first candidate for the result
6645+ var left = binaryExpr.Left;
6646+ if (left is ConstantExpression lc)
6647+ {
6648+ #if LIGHT_EXPRESSION
6649+ if (lc.RefField != null) return false;
6650+ #endif
6651+ result = (bool)lc.Value ? TrueObject : FalseObject;
6652+ }
6653+ else if (!TryInterpretPrimitive(out result, left))
66636654 return false;
66646655
6665- // Short circuit the evalution, because this is an actual logic of these logical operations
6666- if (leftVal == TrueObject & nodeType == ExpressionType.OrElse ||
6667- leftVal == FalseObject & nodeType == ExpressionType.AndAlso)
6656+ // Short circuit the interpretation, because this is an actual logic of these logical operations
6657+ if (result == TrueObject & nodeType == ExpressionType.OrElse ||
6658+ result == FalseObject & nodeType == ExpressionType.AndAlso)
6659+ return true;
6660+
6661+ // If the first part is not enough to decide of the expression result, go right
6662+ var right = binaryExpr.Right;
6663+ if (right is ConstantExpression rc)
66686664 {
6669- result = leftVal;
6665+ #if LIGHT_EXPRESSION
6666+ if (rc.RefField != null) return false;
6667+ #endif
6668+ result = (bool)rc.Value ? TrueObject : FalseObject;
66706669 return true;
66716670 }
6672- return TryInterpretPrimitive(out result, binaryExpr.Right );
6671+ return TryInterpretPrimitive(out result, right );
66736672 }
66746673
6675- if (IsComparison(nodeType))
6674+ var isComparison = IsComparison(nodeType);
6675+ if (isComparison || IsArithmetic(nodeType))
66766676 {
66776677 var binaryExpr = (BinaryExpression)expr;
6678- if (!TryInterpretPrimitive(out var leftVal, binaryExpr.Left) ||
6679- !TryInterpretPrimitive(out var rightVal, binaryExpr.Right))
6678+
6679+ // Interpreting left part
6680+ var left = binaryExpr.Left;
6681+ object leftVal = null;
6682+ if (left is ConstantExpression lc)
6683+ {
6684+ #if LIGHT_EXPRESSION
6685+ if (lc.RefField != null) return false;
6686+ #endif
6687+ leftVal = lc.Value;
6688+ }
6689+ else if (!TryInterpretPrimitive(out leftVal, left))
66806690 return false;
66816691
6682- if (nodeType == ExpressionType.Equal | nodeType == ExpressionType.NotEqual)
6692+ // Interpreting right part
6693+ var right = binaryExpr.Right;
6694+ object rightVal = null;
6695+ if (right is ConstantExpression rc)
66836696 {
6684- var boolVal = leftVal.Equals(rightVal);
6685- result = nodeType == ExpressionType.Equal
6686- ? (boolVal ? TrueObject : FalseObject)
6687- : (boolVal ? FalseObject : TrueObject) ;
6697+ #if LIGHT_EXPRESSION
6698+ if (rc.RefField != null) return false;
6699+ #endif
6700+ rightVal = rc.Value ;
66886701 }
6689- else
6702+ else if (!TryInterpretPrimitive(out rightVal, right))
6703+ return false;
6704+
6705+ // Now do the operation on the left and right
6706+ if (isComparison)
66906707 {
6691- // Assuming that the both sides are of the same type, we can use only the left one for comparison
6692- var cmp = leftVal as IComparable;
6693- if (cmp == null)
6694- return false;
6695- var res = cmp.CompareTo(rightVal);
6696- var boolVal = nodeType switch
6708+ if (nodeType == ExpressionType.Equal | nodeType == ExpressionType.NotEqual)
6709+ {
6710+ var boolVal = leftVal.Equals(rightVal);
6711+ result = nodeType == ExpressionType.Equal
6712+ ? (boolVal ? TrueObject : FalseObject)
6713+ : (boolVal ? FalseObject : TrueObject);
6714+ }
6715+ else
66976716 {
6698- ExpressionType.GreaterThan => res > 0,
6699- ExpressionType.GreaterThanOrEqual => res >= 0,
6700- ExpressionType.LessThan => res < 0,
6701- ExpressionType.LessThanOrEqual => res <= 0,
6702- _ => UnreachableCase<bool>(),
6703- };
6704- result = boolVal ? TrueObject : FalseObject;
6717+ // Assuming that the both sides are of the same type, we can use only the left one for comparison
6718+ var cmp = leftVal as IComparable;
6719+ if (cmp == null)
6720+ return false;
6721+ var res = cmp.CompareTo(rightVal);
6722+ var boolVal = nodeType switch
6723+ {
6724+ ExpressionType.GreaterThan => res > 0,
6725+ ExpressionType.GreaterThanOrEqual => res >= 0,
6726+ ExpressionType.LessThan => res < 0,
6727+ ExpressionType.LessThanOrEqual => res <= 0,
6728+ _ => UnreachableCase<bool>(),
6729+ };
6730+ result = boolVal ? TrueObject : FalseObject;
6731+ }
6732+ return true;
67056733 }
6706- return true;
6734+
6735+ // For Arithmetic
6736+ result = DoArithmeticOrNull(leftVal, rightVal, nodeType);
6737+ return result != null;
67076738 }
67086739
67096740 if (nodeType == ExpressionType.Negate)
67106741 {
67116742 var unaryExpr = (UnaryExpression)expr;
6712- if (!TryInterpretPrimitive(out var val, unaryExpr.Operand))
6743+ var operandExpr = unaryExpr.Operand;
6744+ object val = null;
6745+ if (operandExpr is ConstantExpression co)
6746+ {
6747+ #if LIGHT_EXPRESSION
6748+ if (co.RefField != null) return false;
6749+ #endif
6750+ val = co.Value;
6751+ }
6752+ if (!TryInterpretPrimitive(out val, operandExpr))
67136753 return false;
67146754 result = DoNegateOrNull(val);
67156755 return result != null;
67166756 }
67176757
6718- if (IsArithmetic(nodeType) )
6758+ if (expr is ConstantExpression constExpr )
67196759 {
6720- var binaryExpr = (BinaryExpression)expr;
6721- if (!TryInterpretPrimitive(out var leftVal, binaryExpr.Left) ||
6722- !TryInterpretPrimitive(out var rightVal, binaryExpr.Right))
6723- return false;
6760+ #if LIGHT_EXPRESSION
6761+ if (constExpr.RefField != null) return false;
6762+ #endif
6763+ result = constExpr.Value;
6764+ if (expr.Type == typeof(bool))
6765+ result = (bool)result ? TrueObject : FalseObject;
6766+ return true;
6767+ }
67246768
6725- result = DoArithmeticOrNull(leftVal, rightVal, nodeType);
6726- return result != null;
6769+ if (nodeType == ExpressionType.Default)
6770+ {
6771+ result = GetZeroDefaultObject(Type.GetTypeCode(expr.Type));
6772+ return true;
6773+ }
6774+
6775+ if (nodeType == ExpressionType.Convert)
6776+ {
6777+ var unaryExpr = (UnaryExpression)expr;
6778+ var operandExpr = unaryExpr.Operand;
6779+
6780+ if (!TryInterpretPrimitive(out result, operandExpr))
6781+ return false;
6782+ var exprType = expr.Type;
6783+ if (operandExpr.Type != exprType && !exprType.IsAssignableFrom(operandExpr.Type))
6784+ {
6785+ var converted = System.Convert.ChangeType(result, exprType);
6786+ result = exprType != typeof(bool) ? converted : (bool)converted ? TrueObject : FalseObject;
6787+ }
6788+ return true;
67276789 }
67286790
67296791 result = null;
0 commit comments