From 81ccd75a1c728754aebf32439e5cb21c83dc413d Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sat, 8 Sep 2018 00:28:55 +0200 Subject: [PATCH 01/31] add the possibility to check the IL in tests --- .../FastExpressionCompiler.IssueTests.csproj | 6 ++++++ .../Issue91_Issue95_Tests.cs | 13 ++++++++++++- ...onCompiler.LightExpression.IssueTests.csproj | 6 ++++++ ...ionCompiler.LightExpression.UnitTests.csproj | 6 ++++++ .../FastExpressionCompiler.UnitTests.csproj | 6 ++++++ test/libs/ILDebugging.Decoder.dll | Bin 0 -> 25088 bytes test/libs/ILDebugging.Visualizer.dll | Bin 0 -> 24576 bytes 7 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 test/libs/ILDebugging.Decoder.dll create mode 100644 test/libs/ILDebugging.Visualizer.dll diff --git a/test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj b/test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj index eda4e03a..3adaebb3 100644 --- a/test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj +++ b/test/FastExpressionCompiler.IssueTests/FastExpressionCompiler.IssueTests.csproj @@ -19,4 +19,10 @@ + + + ..\libs\ILDebugging.Decoder.dll + + + \ No newline at end of file diff --git a/test/FastExpressionCompiler.IssueTests/Issue91_Issue95_Tests.cs b/test/FastExpressionCompiler.IssueTests/Issue91_Issue95_Tests.cs index 31f339f1..0e8defb1 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue91_Issue95_Tests.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue91_Issue95_Tests.cs @@ -1,6 +1,7 @@ using System; -using System.Reflection; +using System.Linq; using System.Reflection.Emit; +using ILDebugging.Decoder; using NUnit.Framework; #if LIGHT_EXPRESSION @@ -39,6 +40,16 @@ public void NullComparisonTest() var lambda = Lambda>(condition, pParam); var convert1 = lambda.CompileFast(true); Assert.NotNull(convert1); + Assert.AreEqual(1, convert1("aaa")); + + // Check TryEmitInvertedNullComparison is used + var il = ILReaderFactory.Create(convert1.Method).ToList(); + Assert.AreEqual(il[0].OpCode, OpCodes.Ldarg_0); + Assert.AreEqual(il[1].OpCode, OpCodes.Brfalse); + Assert.AreEqual(il[2].OpCode, OpCodes.Ldc_I4_1); + Assert.AreEqual(il[3].OpCode, OpCodes.Br); + Assert.AreEqual(il[4].OpCode, OpCodes.Ldc_I4_0); + Assert.AreEqual(il[5].OpCode, OpCodes.Ret); } [Test] diff --git a/test/FastExpressionCompiler.LightExpression.IssueTests/FastExpressionCompiler.LightExpression.IssueTests.csproj b/test/FastExpressionCompiler.LightExpression.IssueTests/FastExpressionCompiler.LightExpression.IssueTests.csproj index 06a4173d..a537d143 100644 --- a/test/FastExpressionCompiler.LightExpression.IssueTests/FastExpressionCompiler.LightExpression.IssueTests.csproj +++ b/test/FastExpressionCompiler.LightExpression.IssueTests/FastExpressionCompiler.LightExpression.IssueTests.csproj @@ -32,4 +32,10 @@ + + + ..\libs\ILDebugging.Decoder.dll + + + diff --git a/test/FastExpressionCompiler.LightExpression.UnitTests/FastExpressionCompiler.LightExpression.UnitTests.csproj b/test/FastExpressionCompiler.LightExpression.UnitTests/FastExpressionCompiler.LightExpression.UnitTests.csproj index 98898bdd..37227218 100644 --- a/test/FastExpressionCompiler.LightExpression.UnitTests/FastExpressionCompiler.LightExpression.UnitTests.csproj +++ b/test/FastExpressionCompiler.LightExpression.UnitTests/FastExpressionCompiler.LightExpression.UnitTests.csproj @@ -31,4 +31,10 @@ + + + ..\libs\ILDebugging.Decoder.dll + + + diff --git a/test/FastExpressionCompiler.UnitTests/FastExpressionCompiler.UnitTests.csproj b/test/FastExpressionCompiler.UnitTests/FastExpressionCompiler.UnitTests.csproj index 2d4e4847..d13af86f 100644 --- a/test/FastExpressionCompiler.UnitTests/FastExpressionCompiler.UnitTests.csproj +++ b/test/FastExpressionCompiler.UnitTests/FastExpressionCompiler.UnitTests.csproj @@ -24,4 +24,10 @@ + + + ..\libs\ILDebugging.Decoder.dll + + + diff --git a/test/libs/ILDebugging.Decoder.dll b/test/libs/ILDebugging.Decoder.dll new file mode 100644 index 0000000000000000000000000000000000000000..3b1cae9c990822198f9459d64fca1db20c5129c1 GIT binary patch literal 25088 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

y#x0HxK;r7;r=hUW5CFL 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 literal 0 HcmV?d00001 diff --git a/test/libs/ILDebugging.Visualizer.dll b/test/libs/ILDebugging.Visualizer.dll new file mode 100644 index 0000000000000000000000000000000000000000..b31449e8ce2a90010040a57caa6cfc2fa5d4c2a6 GIT binary patch literal 24576 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 Date: Sat, 8 Sep 2018 07:31:14 +0200 Subject: [PATCH 02/31] use collectionassert --- .../Issue91_Issue95_Tests.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/test/FastExpressionCompiler.IssueTests/Issue91_Issue95_Tests.cs b/test/FastExpressionCompiler.IssueTests/Issue91_Issue95_Tests.cs index 0e8defb1..7e1a1791 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue91_Issue95_Tests.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue91_Issue95_Tests.cs @@ -43,13 +43,9 @@ public void NullComparisonTest() Assert.AreEqual(1, convert1("aaa")); // Check TryEmitInvertedNullComparison is used - var il = ILReaderFactory.Create(convert1.Method).ToList(); - Assert.AreEqual(il[0].OpCode, OpCodes.Ldarg_0); - Assert.AreEqual(il[1].OpCode, OpCodes.Brfalse); - Assert.AreEqual(il[2].OpCode, OpCodes.Ldc_I4_1); - Assert.AreEqual(il[3].OpCode, OpCodes.Br); - Assert.AreEqual(il[4].OpCode, OpCodes.Ldc_I4_0); - Assert.AreEqual(il[5].OpCode, OpCodes.Ret); + var il = ILReaderFactory.Create(convert1.Method); + CollectionAssert.AreEqual(il.Select(x => x.OpCode), + new[] {OpCodes.Ldarg_0, OpCodes.Brfalse, OpCodes.Ldc_I4_1, OpCodes.Br, OpCodes.Ldc_I4_0, OpCodes.Ret}); } [Test] From 1e03dc7151a0057f6fbfbac3ffac432609fd14ec Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sat, 8 Sep 2018 07:36:31 +0200 Subject: [PATCH 03/31] check multiple constant returns are removed --- .../Issue78_blocks_with_constant_return.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/FastExpressionCompiler.IssueTests/Issue78_blocks_with_constant_return.cs b/test/FastExpressionCompiler.IssueTests/Issue78_blocks_with_constant_return.cs index 5365b639..28950747 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue78_blocks_with_constant_return.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue78_blocks_with_constant_return.cs @@ -1,5 +1,8 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using ILDebugging.Decoder; using NUnit.Framework; #if LIGHT_EXPRESSION @@ -23,6 +26,20 @@ public void BlockWithConstanReturnIsSupported() Assert.AreEqual(7, fastCompiled()); } + [Test] + public void MultipleConstantReturnsAreRemoved() + { + var ret = Block(Constant(7), Constant(7), Constant(7)); + var lambda = Lambda>(ret); + var fastCompiled = lambda.CompileFast>(true); + Assert.IsNotNull(fastCompiled); + Assert.AreEqual(7, fastCompiled()); + + var il = ILReaderFactory.Create(fastCompiled.Method); + CollectionAssert.AreEqual(il.Select(x => x.OpCode), + new[] {OpCodes.Ldc_I4_7, OpCodes.Ret}); + } + [Test] public void ConstantReturnIsSupported() { From db67a26b08630fedd0ed3a85f143ed6d51c9e9b7 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sat, 8 Sep 2018 18:52:04 +0200 Subject: [PATCH 04/31] Nullable -> Nullable convert --- .../FastExpressionCompiler.cs | 83 ++++++++++++++----- .../Issue83_linq2db.cs | 26 ++++++ 2 files changed, 87 insertions(+), 22 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 9575d5ee..f22aa67f 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1722,34 +1722,45 @@ private static bool TryEmitConvert(UnaryExpression expr, Type targetType, // Conversion to Nullable: new Nullable(T val); else if (targetType.IsNullable()) + { + if (sourceType.IsNullable()) // Nullable -> It's the only ValueType comparable to null + { + var labelFalse = il.DefineLabel(); + var labelDone = il.DefineLabel(); + var loc = il.DeclareLocal(sourceType); + var locT = il.DeclareLocal(targetType); + il.Emit(OpCodes.Stloc_S, loc); + il.Emit(OpCodes.Ldloca_S, loc); + var hasValueMethod = sourceTypeInfo.GetDeclaredMethod("get_HasValue"); + if (!EmitMethodCall(il, hasValueMethod)) + return false; + il.Emit(OpCodes.Brfalse, labelFalse); + il.Emit(OpCodes.Ldloca_S, loc); + var mthValue = sourceTypeInfo.GetDeclaredMethods("GetValueOrDefault").GetFirst(x => x.GetParameters().Length == 0); + if (!EmitMethodCall(il, mthValue)) + return false; + TryEmitValueConvert(Nullable.GetUnderlyingType(targetType), il); + il.Emit(OpCodes.Newobj, targetType.FindConstructor(targetTypeInfo.GenericTypeArguments[0])); + il.Emit(OpCodes.Stloc_S, locT); + il.Emit(OpCodes.Br_S, labelDone); + il.MarkLabel(labelFalse); + il.Emit(OpCodes.Ldloca_S, locT); + il.Emit(OpCodes.Initobj, targetType); + il.MarkLabel(labelDone); + il.Emit(OpCodes.Ldloc_S, locT); + if (ignoreResult) + il.Emit(OpCodes.Pop); + return true; + } il.Emit(OpCodes.Newobj, targetType.FindConstructor(targetTypeInfo.GenericTypeArguments[0])); + } else { if (targetType.GetTypeInfo().IsEnum) targetType = Enum.GetUnderlyingType(targetType); - if (targetType == typeof(int)) - il.Emit(OpCodes.Conv_I4); - else if (targetType == typeof(float)) - il.Emit(OpCodes.Conv_R4); - else if (targetType == typeof(uint)) - il.Emit(OpCodes.Conv_U4); - else if (targetType == typeof(sbyte)) - il.Emit(OpCodes.Conv_I1); - else if (targetType == typeof(byte)) - il.Emit(OpCodes.Conv_U1); - else if (targetType == typeof(short)) - il.Emit(OpCodes.Conv_I2); - else if (targetType == typeof(ushort)) - il.Emit(OpCodes.Conv_U2); - else if (targetType == typeof(long)) - il.Emit(OpCodes.Conv_I8); - else if (targetType == typeof(ulong)) - il.Emit(OpCodes.Conv_U8); - else if (targetType == typeof(double)) - il.Emit(OpCodes.Conv_R8); - - else // cast as the last resort and let's it fail if unlucky + // cast as the last resort and let's it fail if unlucky + if (!TryEmitValueConvert(targetType, il)) il.Emit(OpCodes.Castclass, targetType); } @@ -1758,6 +1769,34 @@ private static bool TryEmitConvert(UnaryExpression expr, Type targetType, return true; } + private static bool TryEmitValueConvert(Type targetType, ILGenerator il) + { + if (targetType == typeof(int)) + il.Emit(OpCodes.Conv_I4); + else if (targetType == typeof(float)) + il.Emit(OpCodes.Conv_R4); + else if (targetType == typeof(uint)) + il.Emit(OpCodes.Conv_U4); + else if (targetType == typeof(sbyte)) + il.Emit(OpCodes.Conv_I1); + else if (targetType == typeof(byte)) + il.Emit(OpCodes.Conv_U1); + else if (targetType == typeof(short)) + il.Emit(OpCodes.Conv_I2); + else if (targetType == typeof(ushort)) + il.Emit(OpCodes.Conv_U2); + else if (targetType == typeof(long)) + il.Emit(OpCodes.Conv_I8); + else if (targetType == typeof(ulong)) + il.Emit(OpCodes.Conv_U8); + else if (targetType == typeof(double)) + il.Emit(OpCodes.Conv_R8); + else + return false; + + return true; + } + internal static MethodInfo FirstConvertOperatorOrDefault(TypeInfo typeInfo, Type targetType, Type sourceType) => typeInfo.DeclaredMethods.GetFirst(m => m.IsStatic && m.ReturnType == targetType && diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index 1283ce99..c606d41c 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -623,6 +623,32 @@ public void TestConverterFailure() compiled1(null); compiled2(null); } + + class sPrp { + public short? v; + } + + [Test] + public void TestConverterNullable() + { + var p = Parameter(typeof(sPrp), "p"); + + var mapperBody = /*Convert(*/Convert(Field(p, nameof(sPrp.v)), typeof(int?))/*, typeof(object))*/; + var mapper = Lambda>(mapperBody, p); + + var compiled1 = mapper.Compile(); + var compiled2 = mapper.CompileFast(true); + + var a = compiled1(new sPrp() { v = short.MaxValue }); + var b = compiled2(new sPrp() { v = short.MaxValue }); + + Assert.AreEqual(a, b); + + var c = compiled1(new sPrp() { v = short.MinValue }); + var d = compiled2(new sPrp() { v = short.MinValue }); + + Assert.AreEqual(c, d); + } #endif [Test] From 9e5ddded32f475b3dcaefb7d7cb591f2660ea40c Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sat, 8 Sep 2018 19:49:49 +0200 Subject: [PATCH 05/31] test for ldarg error --- .../FastExpressionCompiler.cs | 2 +- .../Issue83_linq2db.cs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index f22aa67f..2b3ddbb4 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1723,7 +1723,7 @@ private static bool TryEmitConvert(UnaryExpression expr, Type targetType, // Conversion to Nullable: new Nullable(T val); else if (targetType.IsNullable()) { - if (sourceType.IsNullable()) // Nullable -> It's the only ValueType comparable to null + if (sourceType.IsNullable()) { var labelFalse = il.DefineLabel(); var labelDone = il.DefineLabel(); diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index c606d41c..c5139cf6 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -649,6 +649,27 @@ public void TestConverterNullable() Assert.AreEqual(c, d); } + + public static string aa(int nr) { + return nr.ToString(); + } + + [Test] + public void TestLdArg() + { + var p = Parameter(typeof(int), "p"); + + var mapperBody = Call(typeof(Issue83_linq2db).GetTypeInfo().GetMethod("aa"), p); + var mapper = Lambda>(mapperBody, p); + + var compiled1 = mapper.Compile(); + var compiled2 = mapper.CompileFast(true); + + var a = compiled1(5); + var b = compiled2(5); + + Assert.AreEqual(a, b); + } #endif [Test] From e00efb34fd746d0280fc85d2d9ac9ada0d637d26 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sat, 8 Sep 2018 19:59:52 +0200 Subject: [PATCH 06/31] bugfix ladarg --- .../FastExpressionCompiler.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 2b3ddbb4..a6ff6d14 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1152,13 +1152,13 @@ private static class EmittingVisitor public static bool TryEmit(Expression expr, Type exprType, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ExpressionType parent, - bool ignoreResult = false, bool isMemberAccess = false, int byRefIndex = -1) + bool ignoreResult = false, bool isMemberAccess = false, bool isMethodCallInstance = false, int byRefIndex = -1) { switch (expr.NodeType) { case ExpressionType.Parameter: return ignoreResult || TryEmitParameter((ParameterExpression)expr, paramExprs, il, ref closure, - parent, isMemberAccess, byRefIndex); + parent, isMemberAccess, isMethodCallInstance, byRefIndex); case ExpressionType.Convert: return TryEmitConvert((UnaryExpression)expr, exprType, paramExprs, il, ref closure, ignoreResult, isMemberAccess); @@ -1537,7 +1537,7 @@ private static bool TryEmitThrow(UnaryExpression exprObj, private static bool TryEmitParameter(ParameterExpression paramExpr, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ExpressionType parent, bool isMemberAccess, int byRefIndex = -1) + ExpressionType parent, bool isMemberAccess, bool isMethodCallInstance, int byRefIndex = -1) { // if parameter is passed through, then just load it on stack var paramIndex = paramExprs.GetFirstIndex(paramExpr); @@ -1546,7 +1546,7 @@ private static bool TryEmitParameter(ParameterExpression paramExpr, if (closure.HasClosure) paramIndex += 1; // shift parameter indices by one, because the first one will be closure - var asAddress = (parent == ExpressionType.Call || parent == ExpressionType.MemberAccess) && paramExpr.Type.IsValueType() && !paramExpr.IsByRef; + var asAddress = ((parent == ExpressionType.Call && isMethodCallInstance) || parent == ExpressionType.MemberAccess) && paramExpr.Type.IsValueType() && !paramExpr.IsByRef; EmitLoadParamArg(il, paramIndex, asAddress); if (paramExpr.IsByRef) @@ -1668,7 +1668,7 @@ private static bool TryEmitMany(IReadOnlyList exprs, var expr = exprs[i]; // ignore the result of expression if it is not the result expression, e.g. not the last expression in block var ignore = ignoreResult || parent == ExpressionType.Block && expr != closure.CurrentBlock.ResultExpr; - if (!TryEmit(expr, expr.Type, paramExprs, il, ref closure, parent, ignore, isMemberAccess, i)) + if (!TryEmit(expr, expr.Type, paramExprs, il, ref closure, parent, ignore, isMemberAccess, false, i)) return false; } @@ -1682,7 +1682,7 @@ private static bool TryEmitConvert(UnaryExpression expr, Type targetType, var opExpr = expr.Operand; var method = expr.Method; if (method != null && method.Name != "op_Implicit" && method.Name != "op_Explicit") - return TryEmit(opExpr, opExpr.Type, paramExprs, il, ref closure, ExpressionType.Call, false, isMemberAccess, 0) + return TryEmit(opExpr, opExpr.Type, paramExprs, il, ref closure, ExpressionType.Call, false, isMemberAccess, true, 0) && EmitMethodCall(il, method, ignoreResult); if (!TryEmit(opExpr, opExpr.Type, paramExprs, il, ref closure, ExpressionType.Convert, false, isMemberAccess)) @@ -2398,7 +2398,7 @@ private static bool TryEmitMethodCall(MethodCallExpression expr, if (objExpr != null) { objType = objExpr.Type; - if (!TryEmit(objExpr, objType, paramExprs, il, ref closure, ExpressionType.Call, false, isMemberAccess)) + if (!TryEmit(objExpr, objType, paramExprs, il, ref closure, ExpressionType.Call, false, isMemberAccess, true)) return false; isValueTypeObj = objType.GetTypeInfo().IsValueType; From 62557ff059fee45bb7704b7fab28a2d7f24de8a7 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sun, 9 Sep 2018 00:11:25 +0200 Subject: [PATCH 07/31] fixes for linq2db --- .../FastExpressionCompiler.cs | 18 ++++----- ...ith_nullables_throws_at_delegate_invoke.cs | 37 +++++++++++++++++++ .../ValueTypeTests.cs | 13 +++++-- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index a6ff6d14..a7c865a2 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1152,13 +1152,13 @@ private static class EmittingVisitor public static bool TryEmit(Expression expr, Type exprType, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ExpressionType parent, - bool ignoreResult = false, bool isMemberAccess = false, bool isMethodCallInstance = false, int byRefIndex = -1) + bool ignoreResult = false, bool isMemberAccess = false, bool isInstanceAccess = false, int byRefIndex = -1) { switch (expr.NodeType) { case ExpressionType.Parameter: return ignoreResult || TryEmitParameter((ParameterExpression)expr, paramExprs, il, ref closure, - parent, isMemberAccess, isMethodCallInstance, byRefIndex); + parent, isMemberAccess, isInstanceAccess, byRefIndex); case ExpressionType.Convert: return TryEmitConvert((UnaryExpression)expr, exprType, paramExprs, il, ref closure, ignoreResult, isMemberAccess); @@ -1170,7 +1170,7 @@ public static bool TryEmit(Expression expr, Type exprType, case ExpressionType.Call: return TryEmitMethodCall((MethodCallExpression)expr, paramExprs, il, ref closure, ignoreResult, isMemberAccess); case ExpressionType.MemberAccess: - return TryEmitMemberAccess((MemberExpression)expr, paramExprs, il, ref closure, ignoreResult); + return TryEmitMemberAccess((MemberExpression)expr, paramExprs, il, ref closure, ignoreResult, isInstanceAccess); case ExpressionType.New: var newExpr = (NewExpression)expr; return TryEmitMany(newExpr.Arguments, paramExprs, il, ref closure, ExpressionType.New, ignoreResult, isMemberAccess) @@ -1537,7 +1537,7 @@ private static bool TryEmitThrow(UnaryExpression exprObj, private static bool TryEmitParameter(ParameterExpression paramExpr, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - ExpressionType parent, bool isMemberAccess, bool isMethodCallInstance, int byRefIndex = -1) + ExpressionType parent, bool isMemberAccess, bool isInstanceAccess, int byRefIndex = -1) { // if parameter is passed through, then just load it on stack var paramIndex = paramExprs.GetFirstIndex(paramExpr); @@ -1546,7 +1546,7 @@ private static bool TryEmitParameter(ParameterExpression paramExpr, if (closure.HasClosure) paramIndex += 1; // shift parameter indices by one, because the first one will be closure - var asAddress = ((parent == ExpressionType.Call && isMethodCallInstance) || parent == ExpressionType.MemberAccess) && paramExpr.Type.IsValueType() && !paramExpr.IsByRef; + var asAddress = ((parent == ExpressionType.Call && isInstanceAccess) || parent == ExpressionType.MemberAccess) && paramExpr.Type.IsValueType() && !paramExpr.IsByRef; EmitLoadParamArg(il, paramIndex, asAddress); if (paramExpr.IsByRef) @@ -2402,7 +2402,7 @@ private static bool TryEmitMethodCall(MethodCallExpression expr, return false; isValueTypeObj = objType.GetTypeInfo().IsValueType; - if (isValueTypeObj && objExpr.NodeType != ExpressionType.Parameter) + if (isValueTypeObj && objExpr.NodeType != ExpressionType.Parameter && objExpr.NodeType != ExpressionType.MemberAccess) StoreAsVarAndLoadItsAddress(il, objType); } @@ -2450,14 +2450,14 @@ private static void StoreAsVarAndLoadItsAddress(ILGenerator il, Type varType) private static bool TryEmitMemberAccess(MemberExpression expr, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - bool ignoreResult) + bool ignoreResult, bool isInstanceAccess) { var prop = expr.Member as PropertyInfo; var instanceExpr = expr.Expression; if (instanceExpr != null && !TryEmit(instanceExpr, instanceExpr.Type, paramExprs, il, ref closure, //prop != null ? ExpressionType.Call : ExpressionType.MemberAccess, - ExpressionType.MemberAccess, ignoreResult, true)) + ExpressionType.MemberAccess, ignoreResult, true, true)) return false; if (prop != null) @@ -2474,7 +2474,7 @@ private static bool TryEmitMemberAccess(MemberExpression expr, var field = expr.Member as FieldInfo; if (field != null) { - il.Emit(field.IsStatic ? OpCodes.Ldsfld : OpCodes.Ldfld, field); + il.Emit(field.IsStatic ? OpCodes.Ldsfld : (field.FieldType.GetTypeInfo().IsValueType && isInstanceAccess) ? OpCodes.Ldflda : OpCodes.Ldfld, field); return true; } diff --git a/test/FastExpressionCompiler.IssueTests/Issue67_Equality_comparison_with_nullables_throws_at_delegate_invoke.cs b/test/FastExpressionCompiler.IssueTests/Issue67_Equality_comparison_with_nullables_throws_at_delegate_invoke.cs index 42a626fe..15a30bd0 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue67_Equality_comparison_with_nullables_throws_at_delegate_invoke.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue67_Equality_comparison_with_nullables_throws_at_delegate_invoke.cs @@ -16,6 +16,30 @@ class Foo public Point? PropP { get; set; } public int Prop3 { get; set; } + + public aa Prop4 { get; set; } + } + + public struct aa + { + public int b; + + public override bool Equals(Object obj) + { + return obj is aa && this == (aa)obj; + } + public override int GetHashCode() + { + return b.GetHashCode(); + } + public static bool operator ==(aa x, aa y) + { + return x.b == y.b; + } + public static bool operator !=(aa x, aa y) + { + return !(x == y); + } } [Test] @@ -34,6 +58,19 @@ public void Comparing_nullable_equal_works() Assert.AreEqual(f2(new Foo() { Prop = 0 }), f(new Foo() { Prop = 0 })); } + [Test] + public void Comparing_struct_equal_works() + { + var aaComparand = new aa(); + Expression> e = foo => foo.Prop4 == aaComparand; + + var f = e.CompileFast(true); + var f2 = e.Compile(); + Assert.IsNotNull(f); + + Assert.AreEqual(f2(new Foo() { Prop4 = aaComparand }), f(new Foo() { Prop4 = aaComparand })); + } + [Test] public void Comparing_int_equal_works() { diff --git a/test/FastExpressionCompiler.UnitTests/ValueTypeTests.cs b/test/FastExpressionCompiler.UnitTests/ValueTypeTests.cs index 0fe1513c..4b204f01 100644 --- a/test/FastExpressionCompiler.UnitTests/ValueTypeTests.cs +++ b/test/FastExpressionCompiler.UnitTests/ValueTypeTests.cs @@ -1,8 +1,14 @@ using System; -using System.Linq.Expressions; using NUnit.Framework; +#if LIGHT_EXPRESSION +using static FastExpressionCompiler.LightExpression.Expression; +namespace FastExpressionCompiler.LightExpression.UnitTests +#else +using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; namespace FastExpressionCompiler.UnitTests +#endif { [TestFixture] public class ValueTypeTests @@ -91,7 +97,7 @@ struct A public override string ToString() => N.ToString(); } - + [Test] public void Action_using_with_struct_closure_field() { @@ -99,9 +105,8 @@ public void Action_using_with_struct_closure_field() Expression> expr = a => s.SetValue(a); var lambda = expr.CompileFast(ifFastFailedReturnNull: true); - lambda("a"); - Assert.IsNull(s.Value); + Assert.AreEqual("a", s.Value); } public struct SS From 61de7c1d4bf56081eba19bfd05cd26de91127790 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sun, 9 Sep 2018 06:24:51 +0200 Subject: [PATCH 08/31] bugfixes for linq2db tests --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index a7c865a2..a365512a 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -2402,7 +2402,8 @@ private static bool TryEmitMethodCall(MethodCallExpression expr, return false; isValueTypeObj = objType.GetTypeInfo().IsValueType; - if (isValueTypeObj && objExpr.NodeType != ExpressionType.Parameter && objExpr.NodeType != ExpressionType.MemberAccess) + if (isValueTypeObj && objExpr.NodeType != ExpressionType.Parameter + && !((objExpr is MemberExpression f && f.Member is FieldInfo))) //if it's field expression ldflda already used StoreAsVarAndLoadItsAddress(il, objType); } @@ -2465,7 +2466,7 @@ private static bool TryEmitMemberAccess(MemberExpression expr, // Value type special treatment to load address of value instance in order to access a field or call a method. // Parameter should be excluded because it already loads an address via Ldarga, and you don't need to. // And for field access no need to load address, cause the field stored on stack nearby - if (instanceExpr != null && instanceExpr.NodeType != ExpressionType.Parameter && instanceExpr.Type.IsValueType()) + if (instanceExpr != null && instanceExpr.NodeType != ExpressionType.Parameter && instanceExpr.Type.IsValueType() && (!isInstanceAccess || instanceExpr.NodeType != ExpressionType.MemberAccess)) StoreAsVarAndLoadItsAddress(il, instanceExpr.Type); return EmitMethodCall(il, TryGetPropertyGetMethod(prop)); From b64b2963060fb1a1f5414f5292a3206cf4960b63 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sun, 9 Sep 2018 19:52:53 +0200 Subject: [PATCH 09/31] more tests, more fixes --- .../FastExpressionCompiler.cs | 35 +++++++++++---- .../Issue83_linq2db.cs | 43 +++++++++++++++++++ 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index a365512a..d2c36d7d 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -352,6 +352,8 @@ private struct ClosureInfo public bool HasClosure => ClosureType != null; + public bool LastEmitIsAddress; + // Constant expressions to find an index (by reference) of constant expression from compiled expression. public ConstantExpression[] Constants; @@ -389,6 +391,7 @@ public ClosureInfo(bool isConstructed, object closure = null, CurrentBlock = BlockInfo.Empty; Labels = null; LabelCount = 0; + LastEmitIsAddress = false; if (closure == null) { @@ -1154,6 +1157,8 @@ public static bool TryEmit(Expression expr, Type exprType, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ExpressionType parent, bool ignoreResult = false, bool isMemberAccess = false, bool isInstanceAccess = false, int byRefIndex = -1) { + closure.LastEmitIsAddress = false; + switch (expr.NodeType) { case ExpressionType.Parameter: @@ -2296,7 +2301,7 @@ private static bool TryEmitAssign(BinaryExpression expr, Type exprType, var objExpr = memberExpr.Expression; if (objExpr != null && - !TryEmit(objExpr, objExpr.Type, paramExprs, il, ref closure, ExpressionType.Assign, false, true)) + !TryEmit(objExpr, objExpr.Type, paramExprs, il, ref closure, ExpressionType.Assign, false, true, true)) return false; if (!TryEmit(right, exprType, paramExprs, il, ref closure, ExpressionType.Assign)) @@ -2402,8 +2407,7 @@ private static bool TryEmitMethodCall(MethodCallExpression expr, return false; isValueTypeObj = objType.GetTypeInfo().IsValueType; - if (isValueTypeObj && objExpr.NodeType != ExpressionType.Parameter - && !((objExpr is MemberExpression f && f.Member is FieldInfo))) //if it's field expression ldflda already used + if (isValueTypeObj && objExpr.NodeType != ExpressionType.Parameter && !closure.LastEmitIsAddress) StoreAsVarAndLoadItsAddress(il, objType); } @@ -2415,6 +2419,8 @@ private static bool TryEmitMethodCall(MethodCallExpression expr, if (isValueTypeObj && method.IsVirtual) il.Emit(OpCodes.Constrained, objType); + closure.LastEmitIsAddress = false; + return EmitMethodCall(il, method, ignoreResult); } @@ -2458,7 +2464,7 @@ private static bool TryEmitMemberAccess(MemberExpression expr, if (instanceExpr != null && !TryEmit(instanceExpr, instanceExpr.Type, paramExprs, il, ref closure, //prop != null ? ExpressionType.Call : ExpressionType.MemberAccess, - ExpressionType.MemberAccess, ignoreResult, true, true)) + ExpressionType.MemberAccess, false, true, true)) return false; if (prop != null) @@ -2466,16 +2472,23 @@ private static bool TryEmitMemberAccess(MemberExpression expr, // Value type special treatment to load address of value instance in order to access a field or call a method. // Parameter should be excluded because it already loads an address via Ldarga, and you don't need to. // And for field access no need to load address, cause the field stored on stack nearby - if (instanceExpr != null && instanceExpr.NodeType != ExpressionType.Parameter && instanceExpr.Type.IsValueType() && (!isInstanceAccess || instanceExpr.NodeType != ExpressionType.MemberAccess)) + if (instanceExpr != null && !closure.LastEmitIsAddress && instanceExpr.NodeType != ExpressionType.Parameter && instanceExpr.Type.IsValueType()) StoreAsVarAndLoadItsAddress(il, instanceExpr.Type); + closure.LastEmitIsAddress = false; return EmitMethodCall(il, TryGetPropertyGetMethod(prop)); } var field = expr.Member as FieldInfo; if (field != null) { - il.Emit(field.IsStatic ? OpCodes.Ldsfld : (field.FieldType.GetTypeInfo().IsValueType && isInstanceAccess) ? OpCodes.Ldflda : OpCodes.Ldfld, field); + if(field.IsStatic) + il.Emit(OpCodes.Ldsfld, field); + else + { + closure.LastEmitIsAddress = (field.FieldType.GetTypeInfo().IsValueType && isInstanceAccess); + il.Emit(closure.LastEmitIsAddress ? OpCodes.Ldflda : OpCodes.Ldfld, field); + } return true; } @@ -2758,7 +2771,7 @@ private static bool TryEmitComparison(Expression exprLeft, Expression exprRight, return false; LocalBuilder lVar = null, rVar = null; - if (!TryEmit(exprLeft, exprLeft.Type, paramExprs, il, ref closure, ExpressionType.Default, ignoreResult, isMemberAccess)) + if (!TryEmit(exprLeft, exprLeft.Type, paramExprs, il, ref closure, ExpressionType.Default, false, isMemberAccess)) return false; if (leftIsNull) @@ -2774,7 +2787,7 @@ private static bool TryEmitComparison(Expression exprLeft, Expression exprRight, leftOpType = Nullable.GetUnderlyingType(leftOpType); } - if (!TryEmit(exprRight, exprRight.Type, paramExprs, il, ref closure, ExpressionType.Default, ignoreResult, isMemberAccess)) + if (!TryEmit(exprRight, exprRight.Type, paramExprs, il, ref closure, ExpressionType.Default, false, isMemberAccess)) return false; if (rightOpType.IsNullable()) @@ -2822,6 +2835,9 @@ var methodName if (leftIsNull) goto nullCheck; + if(ignoreResult) + il.Emit(OpCodes.Pop); + return true; } @@ -2903,6 +2919,9 @@ var methodName } } + if (ignoreResult) + il.Emit(OpCodes.Pop); + return true; } diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index c5139cf6..73fc8739 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -821,6 +821,48 @@ public void Struct_test2() Assert.That(obj.Class2.Struct1P.Class3P.Class4.Field1, Is.EqualTo(42)); } + [Test] + public void NullableEnum() + { + var objParam = Parameter(typeof(TestClass2), "obj"); + + var body = Block( + Assign(Field(objParam, nameof(TestClass2.NullEnum2)), Constant(Enum2.Value1, typeof(Enum2?))) + ); + + var expr = Lambda>(body, objParam); + + var compiled = expr.CompileFast(true); + + var obj = new TestClass2(); + + compiled(obj); + + Assert.That(obj.NullEnum2, Is.EqualTo(Enum2.Value1)); + } + +#if !LIGHT_EXPRESSION + + [Test] + public void NullableEnum2() + { + var objParam = Parameter(typeof(TestClass2), "obj"); + + var body = Block( + Equal(Field(objParam, nameof(TestClass2.NullEnum2)), Constant(Enum2.Value1, typeof(Enum2?))) + ); + + var expr = Lambda>(body, objParam); + + + var c = expr.Compile(); + var compiled = expr.CompileFast(true); + + var obj = new TestClass2(); + + compiled(obj); + } +#endif class TestClass1 { public int Prop1 @@ -841,6 +883,7 @@ public int Prop3 class TestClass2 { + public Enum2? NullEnum2; public TestClass3 Class3; public TestStruct1 Struct1; public TestStruct1 Struct1P { get; set; } From 9485794a96d24e3a270d74f108b148591d890cf8 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sun, 9 Sep 2018 21:27:42 +0200 Subject: [PATCH 10/31] support convert checked --- .../Expression.cs | 6 +++ .../FastExpressionCompiler.cs | 23 ++++++----- .../Issue83_linq2db.cs | 41 +++++++++++++++++-- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/Expression.cs b/src/FastExpressionCompiler.LightExpression/Expression.cs index 55ba01c6..f34394e2 100644 --- a/src/FastExpressionCompiler.LightExpression/Expression.cs +++ b/src/FastExpressionCompiler.LightExpression/Expression.cs @@ -165,6 +165,12 @@ public static UnaryExpression Convert(Expression operand, Type targetType) => public static UnaryExpression Convert(Expression operand, Type targetType, MethodInfo method) => new UnaryExpression(ExpressionType.Convert, operand, targetType, method); + public static UnaryExpression ConvertChecked(Expression operand, Type targetType) => + new UnaryExpression(ExpressionType.ConvertChecked, operand, targetType); + + public static UnaryExpression ConvertChecked(Expression operand, Type targetType, MethodInfo method) => + new UnaryExpression(ExpressionType.ConvertChecked, operand, targetType, method); + public static UnaryExpression PreIncrementAssign(Expression operand) => new UnaryExpression(ExpressionType.PreIncrementAssign, operand, operand.Type); diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index d2c36d7d..5e2d5952 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1165,6 +1165,7 @@ public static bool TryEmit(Expression expr, Type exprType, return ignoreResult || TryEmitParameter((ParameterExpression)expr, paramExprs, il, ref closure, parent, isMemberAccess, isInstanceAccess, byRefIndex); case ExpressionType.Convert: + case ExpressionType.ConvertChecked: return TryEmitConvert((UnaryExpression)expr, exprType, paramExprs, il, ref closure, ignoreResult, isMemberAccess); case ExpressionType.ArrayIndex: @@ -1744,7 +1745,7 @@ private static bool TryEmitConvert(UnaryExpression expr, Type targetType, var mthValue = sourceTypeInfo.GetDeclaredMethods("GetValueOrDefault").GetFirst(x => x.GetParameters().Length == 0); if (!EmitMethodCall(il, mthValue)) return false; - TryEmitValueConvert(Nullable.GetUnderlyingType(targetType), il); + TryEmitValueConvert(Nullable.GetUnderlyingType(targetType), il, expr.NodeType == ExpressionType.ConvertChecked); il.Emit(OpCodes.Newobj, targetType.FindConstructor(targetTypeInfo.GenericTypeArguments[0])); il.Emit(OpCodes.Stloc_S, locT); il.Emit(OpCodes.Br_S, labelDone); @@ -1765,7 +1766,7 @@ private static bool TryEmitConvert(UnaryExpression expr, Type targetType, targetType = Enum.GetUnderlyingType(targetType); // cast as the last resort and let's it fail if unlucky - if (!TryEmitValueConvert(targetType, il)) + if (!TryEmitValueConvert(targetType, il, expr.NodeType == ExpressionType.ConvertChecked)) il.Emit(OpCodes.Castclass, targetType); } @@ -1774,26 +1775,26 @@ private static bool TryEmitConvert(UnaryExpression expr, Type targetType, return true; } - private static bool TryEmitValueConvert(Type targetType, ILGenerator il) + private static bool TryEmitValueConvert(Type targetType, ILGenerator il, bool @checked) { if (targetType == typeof(int)) - il.Emit(OpCodes.Conv_I4); + il.Emit(@checked ? OpCodes.Conv_Ovf_I4 : OpCodes.Conv_I4); else if (targetType == typeof(float)) il.Emit(OpCodes.Conv_R4); else if (targetType == typeof(uint)) - il.Emit(OpCodes.Conv_U4); + il.Emit(@checked ? OpCodes.Conv_Ovf_U4 : OpCodes.Conv_U4); else if (targetType == typeof(sbyte)) - il.Emit(OpCodes.Conv_I1); + il.Emit(@checked ? OpCodes.Conv_Ovf_I1 : OpCodes.Conv_I1); else if (targetType == typeof(byte)) - il.Emit(OpCodes.Conv_U1); + il.Emit(@checked ? OpCodes.Conv_Ovf_U1 : OpCodes.Conv_U1); else if (targetType == typeof(short)) - il.Emit(OpCodes.Conv_I2); + il.Emit(@checked ? OpCodes.Conv_Ovf_I2 : OpCodes.Conv_I2); else if (targetType == typeof(ushort)) - il.Emit(OpCodes.Conv_U2); + il.Emit(@checked ? OpCodes.Conv_Ovf_U2 : OpCodes.Conv_U2); else if (targetType == typeof(long)) - il.Emit(OpCodes.Conv_I8); + il.Emit(@checked ? OpCodes.Conv_Ovf_I8 : OpCodes.Conv_I8); else if (targetType == typeof(ulong)) - il.Emit(OpCodes.Conv_U8); + il.Emit(@checked ? OpCodes.Conv_Ovf_U8 : OpCodes.Conv_U8); else if (targetType == typeof(double)) il.Emit(OpCodes.Conv_R8); else diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index 73fc8739..767aea9f 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -841,8 +841,6 @@ public void NullableEnum() Assert.That(obj.NullEnum2, Is.EqualTo(Enum2.Value1)); } -#if !LIGHT_EXPRESSION - [Test] public void NullableEnum2() { @@ -855,14 +853,49 @@ public void NullableEnum2() var expr = Lambda>(body, objParam); - var c = expr.Compile(); var compiled = expr.CompileFast(true); var obj = new TestClass2(); compiled(obj); } -#endif + + [Test] + public void NewNullableTest() + { + var body = New(typeof(int?).GetTypeInfo().DeclaredConstructors.First(), Constant(6, typeof(int))); + + var expr = Lambda>(body); + + var compiled = expr.CompileFast(true); + + compiled(); + } + + [Test] + public void ConvertNullableTest() + { + var body = Convert(ConvertChecked(Constant(long.MaxValue-1, typeof(long)), typeof(int)), typeof(int?)); + + var expr = Lambda>(body); + + var compiled = expr.CompileFast(true); + + Assert.Throws(()=> compiled()); + } + + [Test] + public void ConvertNullable2Test() + { + var body = Convert(ConvertChecked(Constant(5l, typeof(long)), typeof(int)), typeof(int?)); + + var expr = Lambda>(body); + + var compiled = expr.CompileFast(true); + + compiled(); + } + class TestClass1 { public int Prop1 From 27c43afba03fed37504b1060c9d2f4d736acb42e Mon Sep 17 00:00:00 2001 From: jkuehner Date: Sun, 9 Sep 2018 21:50:16 +0200 Subject: [PATCH 11/31] bugfix more for linq2db --- .../FastExpressionCompiler.cs | 2 +- .../Issue83_linq2db.cs | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 5e2d5952..1468b5d1 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1789,7 +1789,7 @@ private static bool TryEmitValueConvert(Type targetType, ILGenerator il, bool @c il.Emit(@checked ? OpCodes.Conv_Ovf_U1 : OpCodes.Conv_U1); else if (targetType == typeof(short)) il.Emit(@checked ? OpCodes.Conv_Ovf_I2 : OpCodes.Conv_I2); - else if (targetType == typeof(ushort)) + else if (targetType == typeof(ushort) || targetType == typeof(char)) il.Emit(@checked ? OpCodes.Conv_Ovf_U2 : OpCodes.Conv_U2); else if (targetType == typeof(long)) il.Emit(@checked ? OpCodes.Conv_Ovf_I8 : OpCodes.Conv_I8); diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index 767aea9f..db4ddab9 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -896,6 +896,34 @@ public void ConvertNullable2Test() compiled(); } + [Test] + public void ConvertTest() + { + var body = ConvertChecked(Constant(0x10, typeof(int)), typeof(char)); + + var expr = Lambda>(body); + + var compiled = expr.CompileFast(true); + + var ret = compiled(); + + Assert.AreEqual('\x10', ret); + } + + [Test] + public void ConvertTest2() + { + var body = ConvertChecked(Constant('\x10', typeof(char)), typeof(int)); + + var expr = Lambda>(body); + + var compiled = expr.CompileFast(true); + + var ret = compiled(); + + Assert.AreEqual(0x10, ret); + } + class TestClass1 { public int Prop1 From 8335febf46768497aae83648365becbfe614c504 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Mon, 10 Sep 2018 11:02:30 +0200 Subject: [PATCH 12/31] bugfix implement not --- .../Expression.cs | 3 ++ .../FastExpressionCompiler.cs | 27 +++++++++-- .../Issue83_linq2db.cs | 48 +++++++++++++++++++ 3 files changed, 75 insertions(+), 3 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/Expression.cs b/src/FastExpressionCompiler.LightExpression/Expression.cs index f34394e2..2477ef33 100644 --- a/src/FastExpressionCompiler.LightExpression/Expression.cs +++ b/src/FastExpressionCompiler.LightExpression/Expression.cs @@ -159,6 +159,9 @@ public static LambdaExpression Lambda(Expression body, params ParameterExpressio public static LambdaExpression Lambda(Type delegateType, Expression body, params ParameterExpression[] parameters) => new LambdaExpression(delegateType, body, parameters); + public static UnaryExpression Not(Expression operand) => + new UnaryExpression(ExpressionType.Not, operand, operand.Type); + public static UnaryExpression Convert(Expression operand, Type targetType) => new UnaryExpression(ExpressionType.Convert, operand, targetType); diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 1468b5d1..b8f1aa53 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1164,10 +1164,13 @@ public static bool TryEmit(Expression expr, Type exprType, case ExpressionType.Parameter: return ignoreResult || TryEmitParameter((ParameterExpression)expr, paramExprs, il, ref closure, parent, isMemberAccess, isInstanceAccess, byRefIndex); + case ExpressionType.Not: + return TryEmitNot((UnaryExpression)expr, exprType, paramExprs, il, ref closure, + ignoreResult, isMemberAccess, isInstanceAccess); case ExpressionType.Convert: case ExpressionType.ConvertChecked: return TryEmitConvert((UnaryExpression)expr, exprType, paramExprs, il, ref closure, - ignoreResult, isMemberAccess); + ignoreResult, isMemberAccess, isInstanceAccess); case ExpressionType.ArrayIndex: return EmitBinary((BinaryExpression)expr, paramExprs, il, ref closure, ExpressionType.ArrayIndex, ignoreResult, isMemberAccess) && TryEmitArrayIndex(expr.Type, il); @@ -1681,9 +1684,27 @@ private static bool TryEmitMany(IReadOnlyList exprs, return true; } + private static bool TryEmitNot(UnaryExpression expr, Type targetType, + IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, + bool ignoreResult, bool isMemberAccess, bool isInstanceAccess) + { + if (!TryEmit(expr.Operand, expr.Operand.Type, paramExprs, il, ref closure, ExpressionType.Convert, false, isMemberAccess, isInstanceAccess)) + return false; + + if (ignoreResult) + il.Emit(OpCodes.Pop); + else + { + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + } + + return true; + } + private static bool TryEmitConvert(UnaryExpression expr, Type targetType, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, - bool ignoreResult, bool isMemberAccess) + bool ignoreResult, bool isMemberAccess, bool isInstanceAccess) { var opExpr = expr.Operand; var method = expr.Method; @@ -1691,7 +1712,7 @@ private static bool TryEmitConvert(UnaryExpression expr, Type targetType, return TryEmit(opExpr, opExpr.Type, paramExprs, il, ref closure, ExpressionType.Call, false, isMemberAccess, true, 0) && EmitMethodCall(il, method, ignoreResult); - if (!TryEmit(opExpr, opExpr.Type, paramExprs, il, ref closure, ExpressionType.Convert, false, isMemberAccess)) + if (!TryEmit(opExpr, opExpr.Type, paramExprs, il, ref closure, ExpressionType.Convert, false, isMemberAccess, isInstanceAccess)) return false; var sourceType = opExpr.Type; diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index db4ddab9..6acb3296 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -872,6 +872,54 @@ public void NewNullableTest() compiled(); } + [Test] + public void TestToString() + { + var body = Call(Constant(true), + typeof(bool).GetTypeInfo().DeclaredMethods + .First(x => x.Name == "ToString" && x.GetParameters().Length == 0)); + + var expr = Lambda>(body); + + var compiled = expr.CompileFast(true); + + var ret = compiled(); + + Assert.AreEqual("True", ret); + } + + [Test] + public void Test2ToString() + { + var p = Parameter(typeof(bool)); + var body = Call(p, + typeof(bool).GetTypeInfo().DeclaredMethods + .First(x => x.Name == "ToString" && x.GetParameters().Length == 0)); + + var expr = Lambda>(body, p); + + var compiled = expr.CompileFast(true); + + var ret = compiled(true); + + Assert.AreEqual("True", ret); + } + + [Test] + public void Test3Bool() + { + var p = Parameter(typeof(bool)); + var body = Not(p); + + var expr = Lambda>(body, p); + + var compiled = expr.CompileFast(true); + + var ret = compiled(true); + + Assert.AreEqual(false, ret); + } + [Test] public void ConvertNullableTest() { From 69db8c5a41f7e19039ba51dccac6664dab2a78cd Mon Sep 17 00:00:00 2001 From: jkuehner Date: Mon, 10 Sep 2018 11:20:08 +0200 Subject: [PATCH 13/31] try to reproduce 146 --- .../Issue146_bool_par_error.cs | 49 +++++++++++++++++++ .../Issue83_linq2db.cs | 15 ++++++ 2 files changed, 64 insertions(+) create mode 100644 test/FastExpressionCompiler.IssueTests/Issue146_bool_par_error.cs diff --git a/test/FastExpressionCompiler.IssueTests/Issue146_bool_par_error.cs b/test/FastExpressionCompiler.IssueTests/Issue146_bool_par_error.cs new file mode 100644 index 00000000..af817847 --- /dev/null +++ b/test/FastExpressionCompiler.IssueTests/Issue146_bool_par_error.cs @@ -0,0 +1,49 @@ +using System; +using NUnit.Framework; + +#pragma warning disable IDE1006 // Naming Styles for linq2db +#pragma warning disable 649 // Unaasigned fields + +#if LIGHT_EXPRESSION +using static FastExpressionCompiler.LightExpression.Expression; +namespace FastExpressionCompiler.LightExpression.UnitTests +#else +using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; +namespace FastExpressionCompiler.UnitTests +#endif +{ +[TestFixture] + public class Issue146_bool_par_error + { + + class MyObject + { + public bool a(b i) + { + return object.Equals(i, false); + } + } + + + [Test] + public void Test3Bool() + { + var objParam = Parameter(typeof(MyObject), "myObj"); + var boolParam = Parameter(typeof(bool), "isSomething"); + var myMethod = typeof(MyObject).GetMethod("a").MakeGenericMethod(typeof(bool)); + var call = Call(objParam, myMethod, boolParam); + + var lambda = Lambda>( + call, + objParam, + boolParam); + + var func = lambda.CompileFast(); + + var ret = func.Invoke(new MyObject(), false); + + Assert.AreEqual(true, ret); + } + } +} diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index 6acb3296..1526d419 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -920,6 +920,21 @@ public void Test3Bool() Assert.AreEqual(false, ret); } + [Test] + public void Test4Bool() + { + var p = Parameter(typeof(bool)); + var body = Not(p); + + var expr = Lambda>(body, p); + + var compiled = expr.CompileFast(true); + + var ret = compiled(false); + + Assert.AreEqual(true, ret); + } + [Test] public void ConvertNullableTest() { From 8984a48ea499cf405ecace9a13222ce3d57dbf34 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Mon, 10 Sep 2018 20:37:48 +0200 Subject: [PATCH 14/31] more test for 146 --- .../Issue146_bool_par_error.cs | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/test/FastExpressionCompiler.IssueTests/Issue146_bool_par_error.cs b/test/FastExpressionCompiler.IssueTests/Issue146_bool_par_error.cs index af817847..e231717a 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue146_bool_par_error.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue146_bool_par_error.cs @@ -21,13 +21,13 @@ class MyObject { public bool a(b i) { - return object.Equals(i, false); + return Equals(i, false); } } [Test] - public void Test3Bool() + public void Test1() { var objParam = Parameter(typeof(MyObject), "myObj"); var boolParam = Parameter(typeof(bool), "isSomething"); @@ -45,5 +45,34 @@ public void Test3Bool() Assert.AreEqual(true, ret); } + + + private class MyClass + { + public bool MyMethod(bool i) + { + Console.WriteLine("Got " + i); + + return Equals(i, false); + } + } + + [Test] + public void Test2() + { + var objParam = Parameter(typeof(MyClass), "myObj"); + var boolParam = Parameter(typeof(bool), "isSomething"); + var myMethod = typeof(MyClass).GetMethod("MyMethod").MakeGenericMethod(typeof(object)); + var call = Call(objParam, myMethod, boolParam); + + var lambda = Lambda>( + call, + objParam, + boolParam); + + var func = lambda.CompileFast(); + + func.Invoke(new MyClass(), false); + } } } From 8392052b405e274266f287bb07baf27ebcde2499 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Mon, 10 Sep 2018 20:55:13 +0200 Subject: [PATCH 15/31] work on issue #147 --- .../FastExpressionCompiler.cs | 6 -- .../Issue147_int_try_parse.cs | 67 +++++++++++++++++++ 2 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 test/FastExpressionCompiler.IssueTests/Issue147_int_try_parse.cs diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index b8f1aa53..2585fe26 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -3101,12 +3101,6 @@ private static bool TryEmitConditional(ConditionalExpression expr, var labelDone = il.DefineLabel(); var ifFalseExpr = expr.IfFalse; - if (ifFalseExpr.NodeType == ExpressionType.Default) - { - il.MarkLabel(labelIfFalse); - return true; - } - il.Emit(OpCodes.Br, labelDone); il.MarkLabel(labelIfFalse); diff --git a/test/FastExpressionCompiler.IssueTests/Issue147_int_try_parse.cs b/test/FastExpressionCompiler.IssueTests/Issue147_int_try_parse.cs new file mode 100644 index 00000000..99f04368 --- /dev/null +++ b/test/FastExpressionCompiler.IssueTests/Issue147_int_try_parse.cs @@ -0,0 +1,67 @@ +using System; +using System.Linq; +using System.Reflection; +using NUnit.Framework; + +#pragma warning disable IDE1006 // Naming Styles for linq2db +#pragma warning disable 649 // Unaasigned fields + +#if LIGHT_EXPRESSION +using static FastExpressionCompiler.LightExpression.Expression; +namespace FastExpressionCompiler.LightExpression.UnitTests +#else +using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; +namespace FastExpressionCompiler.UnitTests +#endif +{ +[TestFixture] + public class Issue147_int_try_parse + { + + class MyObject + { + public bool a(b i) + { + return Equals(i, false); + } + } + +#if !LIGHT_EXPRESSION + [Test] + public void Test1() + { + var intValueParameter = Parameter(typeof(int), "intValue"); + + var tryParseMethod = typeof(int) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .First(m => m.Name == "TryParse" && m.GetParameters().Length == 2); + + var tryParseCall = Call( + tryParseMethod, + Constant("123", typeof(string)), + intValueParameter); + + var parsedValueOrDefault = Condition( + tryParseCall, + intValueParameter, + Default(typeof(int))); + + var conditionBlock = Block(new[] { intValueParameter }, parsedValueOrDefault); + + var conditionLambda = Lambda>(conditionBlock); + + var conditionFunc = conditionLambda.Compile(); + + var parsedValue = conditionFunc.Invoke(); + + + var conditionFuncFast = conditionLambda.CompileFast(); + + var parsedValueFast = conditionFuncFast.Invoke(); + + Assert.AreEqual(parsedValue, parsedValueFast); + } +#endif + } +} From ef5ed6eed942249f5709c5d526e99b93e12209cc Mon Sep 17 00:00:00 2001 From: jkuehner Date: Tue, 11 Sep 2018 22:49:10 +0200 Subject: [PATCH 16/31] bugfix remove instance access flag --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 999aac38..e39fd388 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1252,9 +1252,9 @@ public static bool TryEmit(Expression expr, IReadOnlyList p var arithmeticExpr = (BinaryExpression)expr; return TryEmit(arithmeticExpr.Left, paramExprs, il, ref closure, - parent | ParentFlags.Arithmetic) && + (parent | ParentFlags.Arithmetic) & ~ParentFlags.InstanceAccess) && TryEmit(arithmeticExpr.Right, paramExprs, il, ref closure, - parent | ParentFlags.Arithmetic) && + (parent | ParentFlags.Arithmetic) & ~ParentFlags.InstanceAccess) && TryEmitArithmeticOperation(expr.NodeType, expr.Type, il); case ExpressionType.AndAlso: From 7e7c2568131464561316d38702006b7de055fbd3 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Tue, 11 Sep 2018 23:45:43 +0200 Subject: [PATCH 17/31] try fix byref for nested lambdas --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index e39fd388..bbf5c02e 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -2695,8 +2695,11 @@ private static bool TryEmitInvoke(InvocationExpression expr, var argExprs = expr.Arguments; for (var i = 0; i < argExprs.Count; i++) - if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult, i)) + { + var byRefIndex = argExprs[i].Type.IsByRef ? i : -1; + if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult, byRefIndex)) return false; + } return EmitMethodCall(il, lambda.Type.FindDelegateInvokeMethod(), parent); } From b5ed5fc1ad7fd78adb941a5853cfef57b87cc5f2 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Wed, 12 Sep 2018 10:12:34 +0200 Subject: [PATCH 18/31] support custom expression nodes --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index bbf5c02e..09e48206 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1009,6 +1009,10 @@ private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression return false; return true; + case ExpressionType.Extension: + expr = expr.Reduce(); + continue; + case ExpressionType.Default: return true; @@ -1339,6 +1343,10 @@ when Tools.GetArithmeticFromArithmeticAssignOrSelf(arithmeticAssign) != arithmet case ExpressionType.Switch: return TryEmitSwitch((SwitchExpression)expr, paramExprs, il, ref closure, parent); + case ExpressionType.Extension: + expr = expr.Reduce(); + continue; + default: return false; } From c43fe1b77880169c83a1d38886242402b5eb8146 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Wed, 12 Sep 2018 10:24:46 +0200 Subject: [PATCH 19/31] support reduce --- .../Expression.cs | 3 ++ .../FastExpressionCompiler.cs | 35 ++++++++++--------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/Expression.cs b/src/FastExpressionCompiler.LightExpression/Expression.cs index 09eb335c..4348ef9a 100644 --- a/src/FastExpressionCompiler.LightExpression/Expression.cs +++ b/src/FastExpressionCompiler.LightExpression/Expression.cs @@ -50,6 +50,9 @@ public abstract class Expression ///

Converts to Expression and outputs its as string public override string ToString() => ToExpression().ToString(); + /// Reduces the Expression to simple ones + public virtual Expression Reduce() => this; + public static ParameterExpression Parameter(Type type, string name = null) => new ParameterExpression(type.IsByRef ? type.GetElementType() : type, name, type.IsByRef); diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 09e48206..a179eb3f 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1177,17 +1177,7 @@ public static bool TryEmit(Expression expr, IReadOnlyList p byRefIndex); case ExpressionType.Not: - var notExpr = (UnaryExpression)expr; - if (!TryEmit(notExpr.Operand, paramExprs, il, ref closure, parent)) - return false; - if ((parent & ParentFlags.IgnoreResult) > 0) - il.Emit(OpCodes.Pop); - else - { - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - return true; + return TryEmitNot((UnaryExpression)expr, paramExprs, il, ref closure, parent); case ExpressionType.Convert: case ExpressionType.ConvertChecked: return TryEmitConvert((UnaryExpression)expr, paramExprs, il, ref closure, parent); @@ -1722,6 +1712,22 @@ private static void EmitLoadParamArg(ILGenerator il, int paramIndex, bool asAddr } } + private static bool TryEmitNot(UnaryExpression expr, + IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, + ParentFlags parent) + { + if (!TryEmit(expr.Operand, paramExprs, il, ref closure, parent)) + return false; + if ((parent & ParentFlags.IgnoreResult) > 0) + il.Emit(OpCodes.Pop); + else + { + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + } + return true; + } + private static bool TryEmitConvert(UnaryExpression expr, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) { @@ -1776,14 +1782,11 @@ private static bool TryEmitConvert(UnaryExpression expr, var locT = il.DeclareLocal(targetType); il.Emit(OpCodes.Stloc_S, loc); il.Emit(OpCodes.Ldloca_S, loc); - var hasValueMethod = sourceTypeInfo.GetDeclaredMethod("get_HasValue"); - if (!EmitMethodCall(il, hasValueMethod)) + if (!EmitMethodCall(il, sourceType.FindNullableHasValueGetterMethod())) return false; il.Emit(OpCodes.Brfalse, labelFalse); il.Emit(OpCodes.Ldloca_S, loc); - var mthValue = sourceTypeInfo.GetDeclaredMethods("GetValueOrDefault") - .GetFirst(x => x.GetParameters().Length == 0); - if (!EmitMethodCall(il, mthValue)) + if (!EmitMethodCall(il, sourceType.FindNullableValueOrDefaultMethod())) return false; TryEmitValueConvert(Nullable.GetUnderlyingType(targetType), il, expr.NodeType == ExpressionType.ConvertChecked); From 3bb21ab6e14d6461af51ee7b43cfcd74ad335a58 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Wed, 12 Sep 2018 10:55:34 +0200 Subject: [PATCH 20/31] bugfix byref by new --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index a179eb3f..eb0bcce9 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1202,8 +1202,11 @@ public static bool TryEmit(Expression expr, IReadOnlyList p var newExpr = (NewExpression)expr; var argExprs = newExpr.Arguments; for (var i = 0; i < argExprs.Count; i++) - if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent, i)) + { + var idx = argExprs[i].Type.IsByRef ? i : -1; + if (!TryEmit(argExprs[i], paramExprs, il, ref closure, parent, idx)) return false; + } return TryEmitNew(newExpr.Constructor, newExpr.Type, il); case ExpressionType.NewArrayBounds: From 933b66ea103703f91638154f81517370f7e24307 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 14:24:02 +0200 Subject: [PATCH 21/31] support decimal --- .../FastExpressionCompiler.cs | 40 ++++++++++++++++++- .../Issue83_linq2db.cs | 27 +++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index eb0bcce9..23e1203f 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -881,7 +881,7 @@ internal static Action private static bool IsClosureBoundConstant(object value, TypeInfo type) => value is Delegate || - !type.IsPrimitive && !type.IsEnum && !(value is string) && !(value is Type); + !type.IsPrimitive && !type.IsEnum && !(value is string) && !(value is Type) && !(value is decimal); // @paramExprs is required for nested lambda compilation private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression expr, IReadOnlyList paramExprs) @@ -1955,6 +1955,44 @@ private static bool TryEmitConstant(ConstantExpression expr, ILGenerator il, ref il.Emit(OpCodes.Ldc_I8, (long)((UIntPtr)constantValue).ToUInt64()); } } + else if (constantType == typeof(decimal)) + { + var value = (decimal)constantValue; + if (value % 1 == 0) + { + if (value <= int.MaxValue && value >= int.MinValue) + { + EmitLoadConstantInt(il, decimal.ToInt32(value)); + il.Emit(OpCodes.Newobj, + typeof(decimal).GetTypeInfo().DeclaredConstructors.First(x => + x.GetParameters().Length == 1 && + x.GetParameters()[0].ParameterType == typeof(int))); + } + else if (value <= long.MaxValue && value >= long.MinValue) + { + il.Emit(OpCodes.Ldc_I8, decimal.ToInt64(value)); + il.Emit(OpCodes.Newobj, + typeof(decimal).GetTypeInfo().DeclaredConstructors.First(x => + x.GetParameters().Length == 1 && + x.GetParameters()[0].ParameterType == typeof(int))); + } + } + else + { + int[] parts = Decimal.GetBits(value); + bool sign = (parts[3] & 0x80000000) != 0; + byte scale = (byte)((parts[3] >> 16) & 0x7F); + EmitLoadConstantInt(il, parts[0]); + EmitLoadConstantInt(il, parts[1]); + EmitLoadConstantInt(il, parts[2]); + il.Emit(sign ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0); + EmitLoadConstantInt(il, scale); + il.Emit(OpCodes.Conv_U1); + il.Emit(OpCodes.Newobj, + typeof(decimal).GetTypeInfo().DeclaredConstructors.First(x => + x.GetParameters().Length == 5)); + } + } else return false; } diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index 1526d419..b0afccce 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using NUnit.Framework; @@ -905,6 +906,32 @@ public void Test2ToString() Assert.AreEqual("True", ret); } + [Test] + public void TestDecimal() + { + var body = Constant(5.64m); + + var expr = Lambda>(body); + + var compiled = expr.CompileFast(true); + + var ret = compiled(); + Assert.AreEqual(5.64m, ret); + } + + [Test] + public void TestDecimal1() + { + var body = Constant(5m); + + var expr = Lambda>(body); + + var compiled = expr.CompileFast(true); + + var ret = compiled(); + Assert.AreEqual(5m, ret); + } + [Test] public void Test3Bool() { From 2a6595991636b9e4fe62e7e3bb8f6c001945a60e Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 14:40:42 +0200 Subject: [PATCH 22/31] bugfix fix #153 const access --- .../FastExpressionCompiler.cs | 19 +++++++++----- .../Issue153_MinValueMethodNotSupported.cs | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 test/FastExpressionCompiler.IssueTests/Issue153_MinValueMethodNotSupported.cs diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 23e1203f..368bbb6c 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1189,8 +1189,9 @@ public static bool TryEmit(Expression expr, IReadOnlyList p TryEmitArrayIndex(expr.Type, il); case ExpressionType.Constant: + var constantExpression = (ConstantExpression)expr; return ShouldIgnoreResult(parent) || - TryEmitConstant((ConstantExpression)expr, il, ref closure); + TryEmitConstant(constantExpression, constantExpression.Type, constantExpression.Value, il, ref closure); case ExpressionType.Call: return TryEmitMethodCall((MethodCallExpression)expr, paramExprs, il, ref closure, parent); @@ -1855,10 +1856,8 @@ private static MethodInfo FirstConvertOperatorOrDefault(TypeInfo typeInfo, Type (m.Name == "op_Implicit" || m.Name == "op_Explicit") && m.GetParameters()[0].ParameterType == sourceType); - private static bool TryEmitConstant(ConstantExpression expr, ILGenerator il, ref ClosureInfo closure) + private static bool TryEmitConstant(ConstantExpression expr, Type exprType, object constantValue, ILGenerator il, ref ClosureInfo closure) { - var exprType = expr.Type; - var constantValue = expr.Value; if (constantValue == null) { if (exprType.IsValueType()) // handles the conversion of null to Nullable @@ -1869,7 +1868,7 @@ private static bool TryEmitConstant(ConstantExpression expr, ILGenerator il, ref } var constantType = constantValue.GetType(); - if (IsClosureBoundConstant(constantValue, constantType.GetTypeInfo())) + if (expr != null && IsClosureBoundConstant(constantValue, constantType.GetTypeInfo())) { var constIndex = closure.Constants.GetFirstIndex(expr); if (constIndex == -1 || !LoadClosureFieldOrItem(ref closure, il, constIndex, exprType)) @@ -2551,7 +2550,15 @@ private static bool TryEmitMemberAccess(MemberExpression expr, if (field != null) { if (field.IsStatic) - il.Emit(OpCodes.Ldsfld, field); + { + if (field.IsLiteral) + { + var value = field.GetValue(null); + TryEmitConstant(null, field.FieldType, value, il, ref closure); + } + else + il.Emit(OpCodes.Ldsfld, field); + } else { closure.LastEmitIsAddress = (field.FieldType.GetTypeInfo().IsValueType && (parent & ParentFlags.InstanceAccess) > 0); diff --git a/test/FastExpressionCompiler.IssueTests/Issue153_MinValueMethodNotSupported.cs b/test/FastExpressionCompiler.IssueTests/Issue153_MinValueMethodNotSupported.cs new file mode 100644 index 00000000..4ef12735 --- /dev/null +++ b/test/FastExpressionCompiler.IssueTests/Issue153_MinValueMethodNotSupported.cs @@ -0,0 +1,26 @@ +using System; +using NUnit.Framework; +#pragma warning disable 659 +#if LIGHT_EXPRESSION +using static FastExpressionCompiler.LightExpression.Expression; +namespace FastExpressionCompiler.LightExpression.UnitTests +#else +using System.Linq.Expressions; +using static System.Linq.Expressions.Expression; +namespace FastExpressionCompiler.UnitTests +#endif +{ + [TestFixture] + public class Issue153_MinValueMethodNotSupported + { + [Test] + public void Int_MinValue_Should_Work() + { + var minValueField = typeof(int).GetField("MinValue"); + var minValue = Field(null, minValueField); + var minValueLambda = Lambda>(minValue); + var res = minValueLambda.CompileFast(); + Assert.AreEqual(int.MinValue, res()); + } + } +} \ No newline at end of file From 18892650a519248f03872f50e4956acfaa0c8639 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 18:20:26 +0200 Subject: [PATCH 23/31] bugfix 153 --- .../FastExpressionCompiler.cs | 5 +- ...sue150_New_AttemptToReadProtectedMemory.cs | 125 ++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 test/FastExpressionCompiler.IssueTests/Issue150_New_AttemptToReadProtectedMemory.cs diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 368bbb6c..78e306d7 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1323,8 +1323,11 @@ when Tools.GetArithmeticFromArithmeticAssignOrSelf(arithmeticAssign) != arithmet var indexArgExprs = indexExpr.Arguments; for (var i = 0; i < indexArgExprs.Count; i++) - if (!TryEmit(indexArgExprs[i], paramExprs, il, ref closure, parent, i)) + { + var idx = indexArgExprs[i].Type.IsByRef ? i : -1; + if (!TryEmit(indexArgExprs[i], paramExprs, il, ref closure, parent, idx)) return false; + } return TryEmitIndex((IndexExpression)expr, il); diff --git a/test/FastExpressionCompiler.IssueTests/Issue150_New_AttemptToReadProtectedMemory.cs b/test/FastExpressionCompiler.IssueTests/Issue150_New_AttemptToReadProtectedMemory.cs new file mode 100644 index 00000000..853af5e8 --- /dev/null +++ b/test/FastExpressionCompiler.IssueTests/Issue150_New_AttemptToReadProtectedMemory.cs @@ -0,0 +1,125 @@ +using System.Collections.Generic; +using NUnit.Framework; +#pragma warning disable 659 + +#if LIGHT_EXPRESSION +using static FastExpressionCompiler.LightExpression.Expression; +namespace FastExpressionCompiler.LightExpression.UnitTests +#else + using static System.Linq.Expressions.Expression; + namespace FastExpressionCompiler.UnitTests +#endif +{ + using System; + using System.Linq; + using System.Reflection; + + [TestFixture] + public class Issue150_New_AttemptToReadProtectedMemory + { + [Test] + public void Nested_Assignments_Should_Work() + { + // Builds: + // + // dsosToPpssData => + // { + // var publicPropertyStruct_String = dsosToPpssData.Target; + // string valueKey; + // object value; + // publicPropertyStruct_String.Value = ((valueKey = dsosToPpssData.Source.Keys.FirstOrDefault(key => key.MatchesKey("Value"))) != null) + // ? ((value = dsosToPpssData.Source[valueKey]) != null) ? value.ToString() : null + // : null; + + // return publicPropertyStruct_String; + // } + + var mappingDataParameter = Parameter( + typeof(MappingData, PublicPropertyStruct>), + "dsosToPpssData"); + + var structVariable = Variable(typeof(PublicPropertyStruct), "publicPropertyStruct_String"); + + var structVariableAssignment = Assign(structVariable, Property(mappingDataParameter, "Target")); + + var sourceDictionary = Property(mappingDataParameter, "Source"); + var nullString = Default(typeof(string)); + var valueKeyVariable = Variable(typeof(string), "valueKey"); + + var linqFirstOrDefaultMethod = typeof(Enumerable) + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .First(m => m.Name == "FirstOrDefault" && m.GetParameters().Length == 2) + .MakeGenericMethod(typeof(string)); + + var dictionaryKeys = Property(sourceDictionary, "Keys"); + + var keyParameter = Parameter(typeof(string), "key"); + var matchesKeyMethod = typeof(MyIssueExtensions).GetMethod("MatchesKey"); + var matchesKeyCall = Call(matchesKeyMethod, keyParameter, Constant("Value")); + var matchesKeyLambda = Lambda>(matchesKeyCall, keyParameter); + + var firstOrDefaultKeyCall = Call(linqFirstOrDefaultMethod, dictionaryKeys, matchesKeyLambda); + + var valueKeyAssignment = Assign(valueKeyVariable, firstOrDefaultKeyCall); + var valueKeyNotNull = NotEqual(valueKeyAssignment, nullString); + + var valueVariable = Variable(typeof(object), "value"); + + var dictionaryIndexer = sourceDictionary.Type.GetProperties().First(p => p.GetIndexParameters().Length != 0); + var dictionaryIndexAccess = MakeIndex(sourceDictionary, dictionaryIndexer, new[] { valueKeyVariable }); + var dictionaryValueAsObject = Convert(dictionaryIndexAccess, typeof(object)); + + var valueAssignment = Assign(valueVariable, dictionaryValueAsObject); + var valueNotNull = NotEqual(valueAssignment, Default(valueVariable.Type)); + + var objectToStringMethod = valueVariable.Type.GetMethod("ToString"); + var valueToString = Call(valueVariable, objectToStringMethod); + + var valueToStringOrNull = Condition(valueNotNull, valueToString, nullString); + + var dictionaryEntryOrNull = Condition(valueKeyNotNull, valueToStringOrNull, nullString); + + var structValueProperty = Property(structVariable, "Value"); + + var structValueAssignment = Assign(structValueProperty, dictionaryEntryOrNull); + + var structPopulation = Block( + new[] { valueKeyVariable, valueVariable }, + structValueAssignment); + + var structMapping = Block( + new[] { structVariable }, + structVariableAssignment, + structPopulation, + structVariable); + + var populationLambda = Lambda, PublicPropertyStruct>, PublicPropertyStruct>>( + structMapping, + mappingDataParameter); + + var populationFunc = populationLambda.CompileFast(); + populationFunc.Invoke(new MappingData, PublicPropertyStruct> + { + Source = new Dictionary { ["Value"] = 123 }, + Target = new PublicPropertyStruct() + }); + } + + public class MappingData + { + public TSource Source { get; set; } + + public TTarget Target { get; set; } + } + + public struct PublicPropertyStruct + { + public T Value { get; set; } + } + } + + public static class MyIssueExtensions + { + public static bool MatchesKey(this string value, string other) => value == other; + } +} \ No newline at end of file From a4516804bcf2a28cdd4fa00b3d060be2002c4a00 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 19:16:10 +0200 Subject: [PATCH 24/31] fix rest of linq2db tests --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 78e306d7..27e6cb88 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -2546,6 +2546,7 @@ private static bool TryEmitMemberAccess(MemberExpression expr, il.Emit(OpCodes.Ldloca, theVar); } + closure.LastEmitIsAddress = false; return EmitMethodCall(il, prop.FindPropertyGetMethod()); } @@ -2829,7 +2830,7 @@ private static bool TryEmitComparison(Expression exprLeft, Expression exprRight, return false; LocalBuilder lVar = null, rVar = null; - if (!TryEmit(exprLeft, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) + if (!TryEmit(exprLeft, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) return false; if (leftIsNull) @@ -2844,7 +2845,7 @@ private static bool TryEmitComparison(Expression exprLeft, Expression exprRight, leftOpType = Nullable.GetUnderlyingType(leftOpType); } - if (!TryEmit(exprRight, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult)) + if (!TryEmit(exprRight, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) return false; if (rightOpType.IsNullable()) From 05533ce4765126e76b0d9d37bb8257182efcc8d7 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 19:18:41 +0200 Subject: [PATCH 25/31] comment for decimal --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 27e6cb88..b83e08e4 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1959,6 +1959,7 @@ private static bool TryEmitConstant(ConstantExpression expr, Type exprType, obje } else if (constantType == typeof(decimal)) { + //check if decimal has decimal places, if not use shorter IL code (constructor from int or long) var value = (decimal)constantValue; if (value % 1 == 0) { From 3bba396aa7d227f51d901de5edc2319adf23b326 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 21:33:38 +0200 Subject: [PATCH 26/31] bugfix equality --- .../FastExpressionCompiler.cs | 29 +++++- .../Issue83_linq2db.cs | 89 +++++++++++++++++++ 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index b83e08e4..15046c06 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -2827,9 +2827,6 @@ private static bool TryEmitComparison(Expression exprLeft, Expression exprRight, if (exprRight is ConstantExpression c && c.Value == null && exprRight.Type == typeof(object)) rightOpType = leftOpType; - if (leftOpType != rightOpType) - return false; - LocalBuilder lVar = null, rVar = null; if (!TryEmit(exprLeft, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) return false; @@ -2849,6 +2846,32 @@ private static bool TryEmitComparison(Expression exprLeft, Expression exprRight, if (!TryEmit(exprRight, paramExprs, il, ref closure, parent & ~ParentFlags.IgnoreResult & ~ParentFlags.InstanceAccess)) return false; + if (leftOpType != rightOpType) + { + if (leftOpType.GetTypeInfo().IsClass && rightOpType.GetTypeInfo().IsClass && (leftOpType == typeof(object) || rightOpType == typeof(object))) + { + if (expressionType == ExpressionType.Equal) + { + il.Emit(OpCodes.Ceq); + if ((parent & ParentFlags.IgnoreResult) > 0) + il.Emit(OpCodes.Pop); + } + else if (expressionType == ExpressionType.NotEqual) + { + il.Emit(OpCodes.Ceq); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + } + else + return false; + + if ((parent & ParentFlags.IgnoreResult) > 0) + il.Emit(OpCodes.Pop); + + return true; + } + } + if (rightOpType.IsNullable()) { rVar = il.DeclareLocal(rightOpType); diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index b0afccce..817ef43a 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -279,6 +279,56 @@ enum Enum3 Value2 = 2, } +#if !LIGHT_EXPRESSION + [Test] + public void Equal1_Test() + { + var p = Parameter(typeof(object)); + var pp = new Patient(); + var body = Equal(Constant(pp), p); + Expression> e = (o) => o == pp; + var expr = Lambda>(body, p); + + var compiled = expr.CompileFast(true); + var c = expr.Compile(); + + Assert.AreEqual(c(pp), compiled(pp)); + Assert.AreEqual(c(new Patient()), compiled(new Patient())); + } + + [Test] + public void Equal2_Test() + { + var p = Parameter(typeof(Patient)); + var pp = new Patient(); + var body = Equal(Constant(pp), p); + Expression> e = (o) => o == pp; + var expr = Lambda>(body, p); + + var compiled = expr.CompileFast(true); + var c = expr.Compile(); + + Assert.AreEqual(c(pp), compiled(pp)); + Assert.AreEqual(c(new Patient()), compiled(new Patient())); + } + + [Test] + public void Equal3_Test() + { + var p = Parameter(typeof(Patient)); + var pp = new Patient2(); + var body = Equal(Constant(pp), p); + Expression> e = (o) => o == pp; + var expr = Lambda>(body, p); + + var compiled = expr.CompileFast(true); + var c = expr.Compile(); + + Assert.AreEqual(c(pp), compiled(pp)); + Assert.AreEqual(c(new Patient()), compiled(new Patient())); + } +#endif + [Test] public void Enum_to_enum_conversion() { @@ -1014,6 +1064,45 @@ public void ConvertTest2() Assert.AreEqual(0x10, ret); } + public class Patient2 : Patient { } + + public class Patient + { + public int PersonID; + public string Diagnosis; + + public static bool operator ==(Patient a, Patient b) + { + return Equals(a, b); + } + public static bool operator !=(Patient a, Patient b) + { + return !Equals(a, b); + } + + public override bool Equals(object obj) + { + return Equals(obj as Patient); + } + + public bool Equals(Patient other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return other.PersonID == PersonID && Equals(other.Diagnosis, Diagnosis); + } + + public override int GetHashCode() + { + unchecked + { + var result = PersonID; + result = (result * 397) ^ (Diagnosis != null ? Diagnosis.GetHashCode() : 0); + return result; + } + } + } + class TestClass1 { public int Prop1 From b83e80780eaa3b02a59bda505b6e9cd8c6805fb4 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 22:06:55 +0200 Subject: [PATCH 27/31] bugfix reduced expressions, we should only reduce once, or parameters are not found in closure --- src/FastExpressionCompiler/FastExpressionCompiler.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 15046c06..b500119c 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -360,6 +360,8 @@ private struct ClosureInfo public NestedLambdaInfo[] NestedLambdas; public LambdaExpression[] NestedLambdaExprs; + public Dictionary ReducedExpressions; + public int ClosedItemCount => Constants.Length + NonPassedParameters.Length + NestedLambdas.Length; // FieldInfos are needed to load field of closure object on stack in emitter. @@ -387,6 +389,7 @@ public ClosureInfo(bool isConstructed, object closure = null, Labels = null; LabelCount = 0; LastEmitIsAddress = false; + ReducedExpressions = null; if (closure == null) { @@ -1010,7 +1013,11 @@ private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression return true; case ExpressionType.Extension: - expr = expr.Reduce(); + var reducedExpr = expr.Reduce(); + if (closure.ReducedExpressions == null) + closure.ReducedExpressions = new Dictionary(); + closure.ReducedExpressions.Add(expr, reducedExpr); + expr = reducedExpr; continue; case ExpressionType.Default: @@ -1341,7 +1348,7 @@ when Tools.GetArithmeticFromArithmeticAssignOrSelf(arithmeticAssign) != arithmet return TryEmitSwitch((SwitchExpression)expr, paramExprs, il, ref closure, parent); case ExpressionType.Extension: - expr = expr.Reduce(); + expr = closure.ReducedExpressions[expr]; continue; default: From 58ff50ee10f5f5d0f134ef2ce3cad658819cfdbb Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 22:32:02 +0200 Subject: [PATCH 28/31] bugfix support string + --- .../FastExpressionCompiler.cs | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index b500119c..22154cd5 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -3018,18 +3018,28 @@ private static bool TryEmitArithmeticOperation(ExpressionType exprNodeType, Type { if (!exprType.IsPrimitive()) { - var methodName - = exprNodeType == ExpressionType.Add ? "op_Addition" - : exprNodeType == ExpressionType.AddChecked ? "op_Addition" - : exprNodeType == ExpressionType.Subtract ? "op_Subtraction" - : exprNodeType == ExpressionType.SubtractChecked ? "op_Subtraction" - : exprNodeType == ExpressionType.Multiply ? "op_Multiply" - : exprNodeType == ExpressionType.MultiplyChecked ? "op_Multiply" - : exprNodeType == ExpressionType.Divide ? "op_Division" - : exprNodeType == ExpressionType.Modulo ? "op_Modulus" - : null; + MethodInfo method = null; + if (exprType == typeof(string)) + method = typeof(string).GetTypeInfo().DeclaredMethods.First(x => + x.Name == "Concat" && x.GetParameters().Length == 2 && + x.GetParameters()[0].ParameterType == typeof(string)); + else + { + var methodName + = exprNodeType == ExpressionType.Add ? "op_Addition" + : exprNodeType == ExpressionType.AddChecked ? "op_Addition" + : exprNodeType == ExpressionType.Subtract ? "op_Subtraction" + : exprNodeType == ExpressionType.SubtractChecked ? "op_Subtraction" + : exprNodeType == ExpressionType.Multiply ? "op_Multiply" + : exprNodeType == ExpressionType.MultiplyChecked ? "op_Multiply" + : exprNodeType == ExpressionType.Divide ? "op_Division" + : exprNodeType == ExpressionType.Modulo ? "op_Modulus" + : null; + if (methodName != null) + method = exprType.FindMethod(methodName); + } - return methodName != null && EmitMethodCall(il, exprType.FindMethod(methodName)); + return method != null && EmitMethodCall(il, method); } switch (exprNodeType) From ef868a605cef8c9975840e09ddde7b83008ccc1a Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 23:01:45 +0200 Subject: [PATCH 29/31] support typeas --- .../Expression.cs | 3 +++ .../FastExpressionCompiler.cs | 18 +++++++++++++++++- .../Issue83_linq2db.cs | 16 ++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/FastExpressionCompiler.LightExpression/Expression.cs b/src/FastExpressionCompiler.LightExpression/Expression.cs index 4348ef9a..a30ee0fd 100644 --- a/src/FastExpressionCompiler.LightExpression/Expression.cs +++ b/src/FastExpressionCompiler.LightExpression/Expression.cs @@ -165,6 +165,9 @@ public static LambdaExpression Lambda(Type delegateType, Expression body, params public static UnaryExpression Not(Expression operand) => new UnaryExpression(ExpressionType.Not, operand, operand.Type); + public static UnaryExpression TypeAs(Expression operand, Type type) => + new UnaryExpression(ExpressionType.TypeAs, operand, type); + public static UnaryExpression Convert(Expression operand, Type targetType) => new UnaryExpression(ExpressionType.Convert, operand, targetType); diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 22154cd5..783c25a8 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1182,7 +1182,8 @@ public static bool TryEmit(Expression expr, IReadOnlyList p return ShouldIgnoreResult(parent) || TryEmitParameter((ParameterExpression)expr, paramExprs, il, ref closure, parent, byRefIndex); - + case ExpressionType.TypeAs: + return TryEmitTypeAs((UnaryExpression)expr, paramExprs, il, ref closure, parent); case ExpressionType.Not: return TryEmitNot((UnaryExpression)expr, paramExprs, il, ref closure, parent); case ExpressionType.Convert: @@ -1726,6 +1727,21 @@ private static void EmitLoadParamArg(ILGenerator il, int paramIndex, bool asAddr } } + private static bool TryEmitTypeAs(UnaryExpression expr, + IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, + ParentFlags parent) + { + if (!TryEmit(expr.Operand, paramExprs, il, ref closure, parent)) + return false; + if ((parent & ParentFlags.IgnoreResult) > 0) + il.Emit(OpCodes.Pop); + else + { + il.Emit(OpCodes.Isinst, expr.Type); + } + return true; + } + private static bool TryEmitNot(UnaryExpression expr, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, ParentFlags parent) diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index 817ef43a..31da7192 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -327,6 +327,22 @@ public void Equal3_Test() Assert.AreEqual(c(pp), compiled(pp)); Assert.AreEqual(c(new Patient()), compiled(new Patient())); } + + [Test] + public void TypeAs_Test() + { + var p = Parameter(typeof(object)); + var body = TypeAs(p, typeof(Patient)); + var expr = Lambda>(body, p); + + var compiled = expr.CompileFast(true); + var c = expr.Compile(); + + var pp = new Patient(); + var s = "a"; + Assert.AreEqual(c(pp), compiled(pp)); + Assert.AreEqual(c(s), compiled(s)); + } #endif [Test] From 61c1cf0f6be2acd1bd8847fbaa7fb52d8a138751 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 23:15:13 +0200 Subject: [PATCH 30/31] bugfix nullable arithmetic --- .../FastExpressionCompiler.cs | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index 783c25a8..c3b00ee7 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -3034,28 +3034,34 @@ private static bool TryEmitArithmeticOperation(ExpressionType exprNodeType, Type { if (!exprType.IsPrimitive()) { - MethodInfo method = null; - if (exprType == typeof(string)) - method = typeof(string).GetTypeInfo().DeclaredMethods.First(x => - x.Name == "Concat" && x.GetParameters().Length == 2 && - x.GetParameters()[0].ParameterType == typeof(string)); - else + if (exprType.IsNullable()) + exprType = Nullable.GetUnderlyingType(exprType); + + if (!exprType.IsPrimitive) { - var methodName - = exprNodeType == ExpressionType.Add ? "op_Addition" - : exprNodeType == ExpressionType.AddChecked ? "op_Addition" - : exprNodeType == ExpressionType.Subtract ? "op_Subtraction" - : exprNodeType == ExpressionType.SubtractChecked ? "op_Subtraction" - : exprNodeType == ExpressionType.Multiply ? "op_Multiply" - : exprNodeType == ExpressionType.MultiplyChecked ? "op_Multiply" - : exprNodeType == ExpressionType.Divide ? "op_Division" - : exprNodeType == ExpressionType.Modulo ? "op_Modulus" - : null; - if (methodName != null) - method = exprType.FindMethod(methodName); + MethodInfo method = null; + if (exprType == typeof(string)) + method = typeof(string).GetTypeInfo().DeclaredMethods.First(x => + x.Name == "Concat" && x.GetParameters().Length == 2 && + x.GetParameters()[0].ParameterType == typeof(string)); + else + { + var methodName + = exprNodeType == ExpressionType.Add ? "op_Addition" + : exprNodeType == ExpressionType.AddChecked ? "op_Addition" + : exprNodeType == ExpressionType.Subtract ? "op_Subtraction" + : exprNodeType == ExpressionType.SubtractChecked ? "op_Subtraction" + : exprNodeType == ExpressionType.Multiply ? "op_Multiply" + : exprNodeType == ExpressionType.MultiplyChecked ? "op_Multiply" + : exprNodeType == ExpressionType.Divide ? "op_Division" + : exprNodeType == ExpressionType.Modulo ? "op_Modulus" + : null; + + if (methodName != null) + method = exprType.FindMethod(methodName); + } + return method != null && EmitMethodCall(il, method); } - - return method != null && EmitMethodCall(il, method); } switch (exprNodeType) From ba85df0e56c71ff566331dc516df8b71909d5331 Mon Sep 17 00:00:00 2001 From: jkuehner Date: Thu, 13 Sep 2018 23:35:23 +0200 Subject: [PATCH 31/31] support typeis --- .../Expression.cs | 27 +++++++++++++++++-- .../FastExpressionCompiler.cs | 26 +++++++++++++++++- .../Issue83_linq2db.cs | 16 +++++++++++ 3 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/FastExpressionCompiler.LightExpression/Expression.cs b/src/FastExpressionCompiler.LightExpression/Expression.cs index a30ee0fd..819aafde 100644 --- a/src/FastExpressionCompiler.LightExpression/Expression.cs +++ b/src/FastExpressionCompiler.LightExpression/Expression.cs @@ -165,8 +165,11 @@ public static LambdaExpression Lambda(Type delegateType, Expression body, params public static UnaryExpression Not(Expression operand) => new UnaryExpression(ExpressionType.Not, operand, operand.Type); - public static UnaryExpression TypeAs(Expression operand, Type type) => - new UnaryExpression(ExpressionType.TypeAs, operand, type); + public static TypeBinaryExpression TypeAs(Expression operand, Type type) => + new TypeBinaryExpression(ExpressionType.TypeAs, operand, type); + + public static UnaryExpression TypeIs(Expression operand, Type type) => + new UnaryExpression(ExpressionType.TypeIs, operand, type); public static UnaryExpression Convert(Expression operand, Type targetType) => new UnaryExpression(ExpressionType.Convert, operand, targetType); @@ -583,6 +586,26 @@ protected BinaryExpression(ExpressionType nodeType, Expression left, Expression } } + public class TypeBinaryExpression : Expression + { + public override ExpressionType NodeType { get; } + public override Type Type { get; } + + public Type TypeOperand { get; } + + public readonly Expression Expression; + + public override SysExpr ToExpression() => SysExpr.TypeIs(Expression.ToExpression(), TypeOperand); + + internal TypeBinaryExpression(ExpressionType nodeType, Expression expression, Type typeOperand) + { + NodeType = nodeType; + Expression = expression; + Type = typeof(bool); + TypeOperand = typeOperand; + } + } + public sealed class SimpleBinaryExpression : BinaryExpression { public override SysExpr ToExpression() diff --git a/src/FastExpressionCompiler/FastExpressionCompiler.cs b/src/FastExpressionCompiler/FastExpressionCompiler.cs index c3b00ee7..4c3ad170 100644 --- a/src/FastExpressionCompiler/FastExpressionCompiler.cs +++ b/src/FastExpressionCompiler/FastExpressionCompiler.cs @@ -1038,6 +1038,12 @@ private static bool TryCollectBoundConstants(ref ClosureInfo closure, Expression continue; } + if (expr is TypeBinaryExpression typeBinaryExpr) + { + expr = typeBinaryExpr.Expression; + continue; + } + return false; } } @@ -1184,6 +1190,8 @@ public static bool TryEmit(Expression expr, IReadOnlyList p byRefIndex); case ExpressionType.TypeAs: return TryEmitTypeAs((UnaryExpression)expr, paramExprs, il, ref closure, parent); + case ExpressionType.TypeIs: + return TryEmitTypeIs((TypeBinaryExpression)expr, paramExprs, il, ref closure, parent); case ExpressionType.Not: return TryEmitNot((UnaryExpression)expr, paramExprs, il, ref closure, parent); case ExpressionType.Convert: @@ -1741,6 +1749,22 @@ private static bool TryEmitTypeAs(UnaryExpression expr, } return true; } + private static bool TryEmitTypeIs(TypeBinaryExpression expr, + IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, + ParentFlags parent) + { + if (!TryEmit(expr.Expression, paramExprs, il, ref closure, parent)) + return false; + if ((parent & ParentFlags.IgnoreResult) > 0) + il.Emit(OpCodes.Pop); + else + { + il.Emit(OpCodes.Isinst, expr.TypeOperand); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Cgt_Un); + } + return true; + } private static bool TryEmitNot(UnaryExpression expr, IReadOnlyList paramExprs, ILGenerator il, ref ClosureInfo closure, @@ -3037,7 +3061,7 @@ private static bool TryEmitArithmeticOperation(ExpressionType exprNodeType, Type if (exprType.IsNullable()) exprType = Nullable.GetUnderlyingType(exprType); - if (!exprType.IsPrimitive) + if (!exprType.IsPrimitive()) { MethodInfo method = null; if (exprType == typeof(string)) diff --git a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs index 31da7192..3b2a7142 100644 --- a/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs +++ b/test/FastExpressionCompiler.IssueTests/Issue83_linq2db.cs @@ -343,6 +343,22 @@ public void TypeAs_Test() Assert.AreEqual(c(pp), compiled(pp)); Assert.AreEqual(c(s), compiled(s)); } + + [Test] + public void TypeIs_Test() + { + var p = Parameter(typeof(object)); + var body = TypeIs(p, typeof(Patient)); + var expr = Lambda>(body, p); + + var compiled = expr.CompileFast(true); + var c = expr.Compile(); + + var pp = new Patient(); + var s = "a"; + Assert.AreEqual(c(pp), compiled(pp)); + Assert.AreEqual(c(s), compiled(s)); + } #endif [Test]