├── .config └── dotnet-tools.json ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── actions.yml ├── .gitignore ├── .rebuild-nuget-package.cmd ├── .rebuild.cmd ├── .run-all-tests.cmd ├── .run-unit-tests.cmd ├── CHANGELOG.md ├── Common.DotSettings ├── Directory.Build.props ├── Directory.Build.targets ├── GrobExp.Compiler.Tests ├── ArithmeticTests │ ├── TestAdd.cs │ ├── TestBitwiseOperations.cs │ ├── TestDecrement.cs │ ├── TestDivide.cs │ ├── TestIncrement.cs │ ├── TestIsTrueFalse.cs │ ├── TestModulo.cs │ ├── TestMultiply.cs │ ├── TestPower.cs │ ├── TestShifts.cs │ ├── TestSubtract.cs │ └── TestUnaryPlusMinus.cs ├── AssignTests │ ├── AddAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── AndAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── Assign │ │ ├── TestAssign.cs │ │ └── TestAssignArray.cs │ ├── DivideAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── ExclusiveOrAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── LeftShiftAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── ModuloAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── MultiplyAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── OrAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── PostDecrementAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── PostIncrementAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── PowerAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── PreDecrementAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── PreIncrementAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ ├── RightShiftAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs │ └── SubtractAssign │ │ ├── TestComplexProperty.cs │ │ ├── TestInstanceMember.cs │ │ ├── TestMultiDimensionalArray.cs │ │ ├── TestParameter.cs │ │ ├── TestSimpleArray.cs │ │ └── TestStaticMember.cs ├── ExtensionTests │ ├── ForEachExpression.cs │ └── TestForEach.cs ├── GrobExp.Compiler.Tests.csproj ├── ParameterReplacer.cs ├── Test.cs ├── TestArrayLength.cs ├── TestBase.cs ├── TestBlock.cs ├── TestCheckArrayIndex.cs ├── TestCheckNullReference.cs ├── TestComparison.cs ├── TestCompileToMethod.cs ├── TestConstant.cs ├── TestConvert.cs ├── TestConvertChecked.cs ├── TestCustomOperators.cs ├── TestDynamic.cs ├── TestEqual.cs ├── TestExpressionHashCalculator.cs ├── TestExtensionMethod.cs ├── TestIndex.cs ├── TestInvoke.cs ├── TestLabels.cs ├── TestListInit.cs ├── TestLogical.cs ├── TestLoop.cs ├── TestMemberAccess.cs ├── TestNewObjectCreation.cs ├── TestNotEqual.cs ├── TestParameterExtractor.cs ├── TestPerformance.cs ├── TestQuote.cs ├── TestRuntimeVariables.cs ├── TestSubLambda.cs ├── TestSwitch.cs ├── TestThrow.cs ├── TestTypeAs.cs ├── TestTypeEqual.cs ├── TestTypeIs.cs ├── TestUnbox.cs └── TryCatchTests │ ├── FilterExceptionTest.cs │ ├── TestFault.cs │ └── TestTryCatchFinally.cs ├── GrobExp.Compiler.sln ├── GrobExp.Compiler.sln.DotSettings ├── GrobExp.Compiler ├── AdvancedDebugViewWriter.cs ├── Closures │ ├── AnonymousTypeBuilder.cs │ ├── ClosureSubstituter.cs │ ├── DynamicClosureBuilder.cs │ ├── ExpressionAnonymousTypeReplacer.cs │ ├── ExpressionClosureBuilder.cs │ ├── ExpressionClosureResolver.cs │ ├── ExpressionPrivateMembersAccessor.cs │ ├── ExpressionQuoter.cs │ ├── IClosureBuilder.cs │ ├── MethodInvokerBuilder.cs │ ├── RuntimeVariablesInliner.cs │ └── StaticClosureBuilder.cs ├── CompiledLambda.cs ├── CompilerOptions.cs ├── EmittingContext.cs ├── ExpressionEmitters │ ├── ArrayIndexExpressionEmitter.cs │ ├── ArrayLengthExpressionEmitter.cs │ ├── AssignExpressionEmitter.cs │ ├── BinaryArithmeticOperationExpressionEmitter.cs │ ├── BlockExpressionEmitter.cs │ ├── CallExpressionEmitter.cs │ ├── CoalesceExpressionEmitter.cs │ ├── ComparisonExpressionEmitter.cs │ ├── ConditionalExpressionEmitter.cs │ ├── ConstantExpressionEmitter.cs │ ├── ConvertExpressionEmitter.cs │ ├── DebugInfoExpressionEmitter.cs │ ├── DefaultExpressionEmitter.cs │ ├── DynamicMethodInvokerBuilder.cs │ ├── EqualityExpressionEmitter.cs │ ├── ExpressionEmitter.cs │ ├── GotoExpressionEmitter.cs │ ├── IExpressionEmitter.cs │ ├── IndexExpressionEmitter.cs │ ├── InvocationExpressionEmitter.cs │ ├── LabelExpressionEmitter.cs │ ├── LambdaExpressionEmitter.cs │ ├── ListInitExpressionEmitter.cs │ ├── LogicalExpressionEmitter.cs │ ├── LoopExpressionEmitter.cs │ ├── MemberAccessExpressionEmitter.cs │ ├── MemberInitExpressionEmitter.cs │ ├── NewArrayBoundsExpressionEmitter.cs │ ├── NewArrayInitExpressionEmitter.cs │ ├── NewExpressionEmitter.cs │ ├── NotExpressionEmitter.cs │ ├── ParameterExpressionEmitter.cs │ ├── RuntimeVariables.cs │ ├── SwitchExpressionEmitter.cs │ ├── ThrowExpressionEmitter.cs │ ├── TryExpressionEmitter.cs │ ├── TypeAsExpressionEmitter.cs │ ├── TypeEqualExpressionEmitter.cs │ ├── TypeIsExpressionEmitter.cs │ ├── UnaryAritmeticOperationExpressionEmitter.cs │ ├── UnaryAssignExpressionEmitter.cs │ └── UnboxExpressionEmitter.cs ├── ExpressionEmittersCollection.cs ├── ExpressionHashCalculator.cs ├── Extensions.cs ├── GrobExp.Compiler.csproj ├── LabelsCloner.cs ├── LambdaCompiler.cs ├── LambdaExpressionCreator.cs ├── LambdaPreparer.cs ├── ParametersExtractor.cs ├── PlatformHelpers.cs ├── ResultType.cs ├── StaticClosures.cs └── TypedDebugInfoExpression.cs ├── LICENSE ├── README.md ├── global.json ├── nuget.config └── version.json /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "jetbrains.resharper.globaltools": { 6 | "version": "2021.2.2", 7 | "commands": [ 8 | "jb" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # stop looking for other EditorConfig files upward in directory tree 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | 7 | # code files 8 | [*.cs] 9 | indent_size = 4 10 | charset = utf-8 11 | 12 | # xml project files 13 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] 14 | indent_size = 2 15 | 16 | # xml config files 17 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] 18 | indent_size = 2 19 | 20 | # json files 21 | [*.json] 22 | indent_size = 2 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings 2 | * text=auto 3 | 4 | # The above will handle all files NOT found below 5 | *.cs text diff=csharp 6 | *.csproj text 7 | *.sln text 8 | *.DotSettings text 9 | *.yml text 10 | *.cmd text 11 | *.sh text 12 | *.md text 13 | *.json text 14 | *.config text 15 | *.props text 16 | *.targets text -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | paths-ignore: 4 | - "**/*.md" 5 | pull_request: 6 | env: 7 | DOTNET_VERSION: 6.0.x 8 | SOLUTION_FILE: GrobExp.Compiler.sln 9 | jobs: 10 | test: 11 | runs-on: windows-2019 12 | steps: 13 | - uses: actions/checkout@v3 14 | with: 15 | fetch-depth: 0 16 | 17 | - name: Setup .NET Core 18 | uses: actions/setup-dotnet@v3 19 | with: 20 | dotnet-version: ${{ env.DOTNET_VERSION }} 21 | 22 | - name: Install dependencies 23 | run: dotnet restore ${{ env.SOLUTION_FILE }} --verbosity minimal && dotnet tool restore 24 | 25 | - name: Build 26 | run: dotnet build --configuration Release ${{ env.SOLUTION_FILE }} 27 | 28 | - name: Check codestyle 29 | run: dotnet jb cleanupcode ${{ env.SOLUTION_FILE }} --profile=CatalogueCleanup --verbosity=WARN && git diff --exit-code 30 | 31 | - name: Run tests 32 | run: dotnet test --no-build --configuration Release --filter TestCategory!=LongRunning ${{ env.SOLUTION_FILE }} 33 | publish: 34 | runs-on: windows-2019 35 | needs: test 36 | if: startsWith(github.event.ref, 'refs/tags/v') 37 | steps: 38 | - uses: actions/checkout@v3 39 | with: 40 | fetch-depth: 0 41 | 42 | - name: Setup .NET 43 | uses: actions/setup-dotnet@v3 44 | with: 45 | dotnet-version: ${{ env.DOTNET_VERSION }} 46 | 47 | - name: Build 48 | run: dotnet build --configuration Release ${{ env.SOLUTION_FILE }} 49 | 50 | - name: Check version 51 | run: | 52 | $ErrorActionPreference = "Stop" 53 | $tagName = "${{ github.ref_name }}" 54 | $version = $tagName.Substring(1) 55 | Write-Host "Will publish nuget package for $tagName tag" -ForegroundColor "Green" 56 | if ($tagName -match '^v\d+\.\d+-release') # tag name starts with 'vX.Y-release' (e.g. use 'v4.2-release.1' tag for the first patch for release v4.2) 57 | { 58 | $version = $version.Substring(0, $version.IndexOf("-release")) 59 | echo "SHOULD_CREATE_RELEASE=true" >> $env:GITHUB_ENV 60 | Write-Host "Will create release for $tagName tag" -ForegroundColor "Green" 61 | } 62 | $matchVersion = Select-String -Path ./version.json -Pattern "`"version`": `"$version`"" 63 | if ($matchVersion -eq $null) 64 | { 65 | Write-Error "Version in tag ($version) does not match version in version.json" 66 | } 67 | - name: Pack dotnet 68 | run: dotnet pack --no-build --configuration Release ${{ env.SOLUTION_FILE }} 69 | 70 | - name: Upload artifacts 71 | uses: actions/upload-artifact@v3 72 | with: 73 | path: "**/*.nupkg" 74 | if-no-files-found: error 75 | 76 | - name: Publish NuGet 77 | run: dotnet nuget push "**/*.nupkg" --source https://api.nuget.org/v3/index.json --no-symbols --api-key $env:NUGET_API_KEY 78 | env: 79 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 80 | 81 | - name: Create release 82 | uses: softprops/action-gh-release@v1 83 | if: ${{ env.SHOULD_CREATE_RELEASE == 'true' }} 84 | with: 85 | fail_on_unmatched_files: true 86 | draft: false 87 | prerelease: false 88 | files: "**/*.nupkg" 89 | -------------------------------------------------------------------------------- /.rebuild-nuget-package.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set SolutionName=GrobExp.Compiler 4 | 5 | rem reset current directory to the location of this script 6 | pushd "%~dp0" 7 | 8 | if exist "./%SolutionName%/bin" ( 9 | rd "./%SolutionName%/bin" /Q /S || exit /b 1 10 | ) 11 | 12 | dotnet build --force --no-incremental --configuration Release "./%SolutionName%.sln" || exit /b 1 13 | 14 | dotnet pack --no-build --configuration Release "./%SolutionName%.sln" || exit /b 1 15 | 16 | pause -------------------------------------------------------------------------------- /.rebuild.cmd: -------------------------------------------------------------------------------- 1 | dotnet build --force --no-incremental --configuration Release ./GrobExp.Compiler.sln 2 | pause 3 | -------------------------------------------------------------------------------- /.run-all-tests.cmd: -------------------------------------------------------------------------------- 1 | dotnet test --configuration Release ./GrobExp.Compiler.Tests/GrobExp.Compiler.Tests.csproj 2 | pause -------------------------------------------------------------------------------- /.run-unit-tests.cmd: -------------------------------------------------------------------------------- 1 | dotnet test --configuration Release --filter TestCategory!=LongRunning ./GrobExp.Compiler.Tests/GrobExp.Compiler.Tests.csproj 2 | pause -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v1.3.23 - 2021.11.30 4 | - Add net6.0 support. 5 | - Update GrEmit dependency to v3.4.10. 6 | 7 | ## v1.3.20 - 2021.03.11 8 | - Update GrEmit dependency to v3.4.1. 9 | - Run tests against net5.0 tfm. 10 | 11 | ## v1.3.10 - 2019.11.21 12 | - Update GrEmit to v3.2.2 supporting SourceLink. 13 | 14 | ## v1.3.7 - 2019.11.21 15 | - Use [SourceLink](https://github.com/dotnet/sourcelink) to help ReSharper decompiler show actual code. 16 | 17 | ## v1.3.2 - 2019.09.25 18 | - Fix bug in `LambdaExpressionCreator`. 19 | - Update dependencies. 20 | 21 | ## v1.2.1 - 2018.09.15 22 | - Use [Nerdbank.GitVersioning](https://github.com/AArnott/Nerdbank.GitVersioning) to automate generation of assembly 23 | and nuget package versions. 24 | - Update [Gremit](https://github.com/skbkontur/gremit) dependency to v2.3. 25 | - Fix bugs in `CallExpressionEmitter` (PR [#5](https://github.com/skbkontur/GrobExp.Compiler/pull/5)). 26 | - Support `enum`, `decimal`, and `Nullable` as types of constants in expressions passed 27 | to `LambdaCompiler.CompileToMethod()` (PR [#6](https://github.com/skbkontur/GrobExp.Compiler/pull/6)). 28 | 29 | ## v1.1.0 - 2018.01.07 30 | - Support .NET Standard 2.0. 31 | - Switch to SDK-style project format and dotnet core build tooling. 32 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | full 6 | true 7 | 8 | 9 | 10 | 11 | true 12 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | $(AssemblyName) 13 | Igor Chevdar 14 | .NET Expression Tree Compiler 15 | Efficient compiler of .NET expression trees 16 | git 17 | https://github.com/skbkontur/GrobExp.Compiler 18 | $(RepositoryUrl) 19 | $(RepositoryUrl)/releases 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ArithmeticTests/TestDecrement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.ArithmeticTests 7 | { 8 | public class TestDecrement : TestBase 9 | { 10 | [Test] 11 | public void Test1() 12 | { 13 | ParameterExpression parameter = Expression.Parameter(typeof(int)); 14 | Expression> exp = Expression.Lambda>(Expression.Decrement(parameter), parameter); 15 | var f = Compile(exp, CompilerOptions.All); 16 | Assert.AreEqual(-1, f(0)); 17 | Assert.AreEqual(0, f(1)); 18 | Assert.AreEqual(-2, f(-1)); 19 | Assert.AreEqual(int.MaxValue, f(int.MinValue)); 20 | } 21 | 22 | [Test] 23 | public void Test2() 24 | { 25 | ParameterExpression parameter = Expression.Parameter(typeof(int?)); 26 | Expression> exp = Expression.Lambda>(Expression.Decrement(parameter), parameter); 27 | var f = Compile(exp, CompilerOptions.All); 28 | Assert.AreEqual(-1, f(0)); 29 | Assert.AreEqual(0, f(1)); 30 | Assert.AreEqual(-2, f(-1)); 31 | Assert.AreEqual(int.MaxValue, f(int.MinValue)); 32 | Assert.IsNull(f(null)); 33 | } 34 | 35 | [Test] 36 | public void Test3() 37 | { 38 | ParameterExpression parameter = Expression.Parameter(typeof(long)); 39 | Expression> exp = Expression.Lambda>(Expression.Decrement(parameter), parameter); 40 | var f = Compile(exp, CompilerOptions.All); 41 | Assert.AreEqual(-1, f(0)); 42 | Assert.AreEqual(0, f(1)); 43 | Assert.AreEqual(-2, f(-1)); 44 | Assert.AreEqual(long.MaxValue, f(long.MinValue)); 45 | } 46 | 47 | [Test] 48 | public void Test4() 49 | { 50 | ParameterExpression parameter = Expression.Parameter(typeof(long?)); 51 | Expression> exp = Expression.Lambda>(Expression.Decrement(parameter), parameter); 52 | var f = Compile(exp, CompilerOptions.All); 53 | Assert.AreEqual(-1, f(0)); 54 | Assert.AreEqual(0, f(1)); 55 | Assert.AreEqual(-2, f(-1)); 56 | Assert.AreEqual(long.MaxValue, f(long.MinValue)); 57 | Assert.IsNull(f(null)); 58 | } 59 | 60 | [Test] 61 | public void Test5() 62 | { 63 | ParameterExpression parameter = Expression.Parameter(typeof(double)); 64 | Expression> exp = Expression.Lambda>(Expression.Decrement(parameter), parameter); 65 | var f = Compile(exp, CompilerOptions.All); 66 | Assert.AreEqual(-0.5, f(0.5)); 67 | Assert.AreEqual(0.125, f(1.125)); 68 | Assert.AreEqual(0, f(1)); 69 | } 70 | 71 | [Test] 72 | public void Test6() 73 | { 74 | ParameterExpression parameter = Expression.Parameter(typeof(double?)); 75 | Expression> exp = Expression.Lambda>(Expression.Decrement(parameter), parameter); 76 | var f = Compile(exp, CompilerOptions.All); 77 | Assert.AreEqual(-0.5, f(0.5)); 78 | Assert.AreEqual(0.125, f(1.125)); 79 | Assert.AreEqual(0, f(1)); 80 | Assert.IsNull(f(null)); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ArithmeticTests/TestDivide.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.ArithmeticTests 7 | { 8 | public class TestDivide : TestBase 9 | { 10 | [Test] 11 | public void Test1() 12 | { 13 | Expression> exp = (a, b) => a / b; 14 | var f = Compile(exp, CompilerOptions.All); 15 | Assert.AreEqual(0, f(1, 2)); 16 | Assert.AreEqual(2, f(5, 2)); 17 | Assert.AreEqual(-1, f(-3, 2)); 18 | } 19 | 20 | [Test] 21 | public void Test2() 22 | { 23 | Expression> exp = (a, b) => a / b; 24 | var f = Compile(exp, CompilerOptions.All); 25 | Assert.AreEqual(0, f(1, 2)); 26 | Assert.AreEqual(2, f(5, 2)); 27 | Assert.AreEqual(-1, f(-3, 2)); 28 | Assert.IsNull(f(null, 2)); 29 | Assert.IsNull(f(1, null)); 30 | Assert.IsNull(f(null, null)); 31 | } 32 | 33 | [Test] 34 | public void Test3() 35 | { 36 | Expression> exp = (a, b) => a / b; 37 | var f = Compile(exp, CompilerOptions.All); 38 | Assert.AreEqual(0, f(1, 2)); 39 | Assert.AreEqual(2, f(5, 2)); 40 | Assert.AreEqual(-1, f(-3, 2)); 41 | Assert.AreEqual(0, f(2000000000, 20000000000)); 42 | Assert.IsNull(f(null, 2)); 43 | Assert.IsNull(f(1, null)); 44 | Assert.IsNull(f(null, null)); 45 | } 46 | 47 | [Test] 48 | public void Test4() 49 | { 50 | Expression> exp = (a, b) => a / b; 51 | var f = Compile(exp, CompilerOptions.All); 52 | Assert.AreEqual(0.5, f(1, 2)); 53 | Assert.AreEqual(2.5, f(5, 2)); 54 | Assert.AreEqual(-1.5, f(-3, 2)); 55 | } 56 | 57 | [Test] 58 | public void Test5() 59 | { 60 | Expression> exp = (a, b) => a / b; 61 | var f = Compile(exp, CompilerOptions.All); 62 | Assert.AreEqual(0, f(1, 2)); 63 | Assert.AreEqual(2, f(5, 2)); 64 | Assert.AreEqual(2147483646, f(uint.MaxValue - 3 + 1, 2)); 65 | } 66 | 67 | [Test] 68 | public void Test6() 69 | { 70 | Expression> exp = (a, b) => a / b; 71 | var f = Compile(exp, CompilerOptions.All); 72 | Assert.AreEqual(0, f(1, 2)); 73 | Assert.AreEqual(2, f(5, 2)); 74 | Assert.AreEqual(-1, f(-3, 2)); 75 | Assert.IsNull(f(null, 2)); 76 | } 77 | } 78 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ArithmeticTests/TestIncrement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.ArithmeticTests 7 | { 8 | public class TestIncrement : TestBase 9 | { 10 | [Test] 11 | public void Test1() 12 | { 13 | ParameterExpression parameter = Expression.Parameter(typeof(int)); 14 | Expression> exp = Expression.Lambda>(Expression.Increment(parameter), parameter); 15 | var f = Compile(exp, CompilerOptions.All); 16 | Assert.AreEqual(1, f(0)); 17 | Assert.AreEqual(2, f(1)); 18 | Assert.AreEqual(0, f(-1)); 19 | Assert.AreEqual(int.MinValue, f(int.MaxValue)); 20 | } 21 | 22 | [Test] 23 | public void Test2() 24 | { 25 | ParameterExpression parameter = Expression.Parameter(typeof(int?)); 26 | Expression> exp = Expression.Lambda>(Expression.Increment(parameter), parameter); 27 | var f = Compile(exp, CompilerOptions.All); 28 | Assert.AreEqual(1, f(0)); 29 | Assert.AreEqual(2, f(1)); 30 | Assert.AreEqual(0, f(-1)); 31 | Assert.AreEqual(int.MinValue, f(int.MaxValue)); 32 | Assert.IsNull(f(null)); 33 | } 34 | 35 | [Test] 36 | public void Test3() 37 | { 38 | ParameterExpression parameter = Expression.Parameter(typeof(long)); 39 | Expression> exp = Expression.Lambda>(Expression.Increment(parameter), parameter); 40 | var f = Compile(exp, CompilerOptions.All); 41 | Assert.AreEqual(1, f(0)); 42 | Assert.AreEqual(2, f(1)); 43 | Assert.AreEqual(0, f(-1)); 44 | Assert.AreEqual(long.MinValue, f(long.MaxValue)); 45 | } 46 | 47 | [Test] 48 | public void Test4() 49 | { 50 | ParameterExpression parameter = Expression.Parameter(typeof(long?)); 51 | Expression> exp = Expression.Lambda>(Expression.Increment(parameter), parameter); 52 | var f = Compile(exp, CompilerOptions.All); 53 | Assert.AreEqual(1, f(0)); 54 | Assert.AreEqual(2, f(1)); 55 | Assert.AreEqual(0, f(-1)); 56 | Assert.AreEqual(long.MinValue, f(long.MaxValue)); 57 | Assert.IsNull(f(null)); 58 | } 59 | 60 | [Test] 61 | public void Test5() 62 | { 63 | ParameterExpression parameter = Expression.Parameter(typeof(double)); 64 | Expression> exp = Expression.Lambda>(Expression.Increment(parameter), parameter); 65 | var f = Compile(exp, CompilerOptions.All); 66 | Assert.AreEqual(1.5, f(0.5)); 67 | Assert.AreEqual(2.125, f(1.125)); 68 | Assert.AreEqual(0, f(-1)); 69 | } 70 | 71 | [Test] 72 | public void Test6() 73 | { 74 | ParameterExpression parameter = Expression.Parameter(typeof(double?)); 75 | Expression> exp = Expression.Lambda>(Expression.Increment(parameter), parameter); 76 | var f = Compile(exp, CompilerOptions.All); 77 | Assert.AreEqual(1.5, f(0.5)); 78 | Assert.AreEqual(2.125, f(1.125)); 79 | Assert.AreEqual(0, f(-1)); 80 | Assert.IsNull(f(null)); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ArithmeticTests/TestIsTrueFalse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.ArithmeticTests 7 | { 8 | public class TestIsTrueFalse : TestBase 9 | { 10 | [Test] 11 | public void TestIsTrue1() 12 | { 13 | Expression> exp = Expression.Lambda>(Expression.IsTrue(Expression.Constant(true))); 14 | var f = Compile(exp, CompilerOptions.All); 15 | Assert.IsTrue(f()); 16 | } 17 | 18 | [Test] 19 | public void TestIsTrue2() 20 | { 21 | ParameterExpression parameter = Expression.Parameter(typeof(int)); 22 | Expression> exp = Expression.Lambda>(Expression.IsTrue(Expression.NotEqual(parameter, Expression.Constant(0))), parameter); 23 | var f = Compile(exp, CompilerOptions.All); 24 | Assert.IsTrue(f(10)); 25 | Assert.IsFalse(f(0)); 26 | } 27 | 28 | [Test] 29 | public void TestIsTrue3() 30 | { 31 | ParameterExpression parameter = Expression.Parameter(typeof(TestClassA)); 32 | Expression> exp = Expression.Lambda>(Expression.IsTrue(Expression.GreaterThanOrEqual(Expression.MakeMemberAccess(parameter, typeof(TestClassA).GetProperty("X")), Expression.Constant(0, typeof(int?)))), parameter); 33 | var f = Compile(exp, CompilerOptions.All); 34 | Assert.IsFalse(f(null)); 35 | Assert.IsFalse(f(new TestClassA())); 36 | Assert.IsFalse(f(new TestClassA {X = -1})); 37 | Assert.IsTrue(f(new TestClassA {X = 0})); 38 | } 39 | 40 | [Test] 41 | public void TestIsFalse1() 42 | { 43 | Expression> exp = Expression.Lambda>(Expression.IsFalse(Expression.Constant(false))); 44 | var f = Compile(exp, CompilerOptions.All); 45 | Assert.IsTrue(f()); 46 | } 47 | 48 | [Test] 49 | public void TestIsFalse2() 50 | { 51 | ParameterExpression parameter = Expression.Parameter(typeof(int)); 52 | Expression> exp = Expression.Lambda>(Expression.IsFalse(Expression.NotEqual(parameter, Expression.Constant(0))), parameter); 53 | var f = Compile(exp, CompilerOptions.All); 54 | Assert.IsFalse(f(10)); 55 | Assert.IsTrue(f(0)); 56 | } 57 | 58 | [Test] 59 | public void TestIsFalse3() 60 | { 61 | ParameterExpression parameter = Expression.Parameter(typeof(TestClassA)); 62 | Expression> exp = Expression.Lambda>(Expression.IsFalse(Expression.GreaterThanOrEqual(Expression.MakeMemberAccess(parameter, typeof(TestClassA).GetProperty("X")), Expression.Constant(0, typeof(int?)))), parameter); 63 | var f = Compile(exp, CompilerOptions.All); 64 | Assert.IsFalse(f(null)); 65 | Assert.IsFalse(f(new TestClassA())); 66 | Assert.IsTrue(f(new TestClassA {X = -1})); 67 | Assert.IsFalse(f(new TestClassA {X = 0})); 68 | } 69 | 70 | public class TestClassA 71 | { 72 | public int? X { get; set; } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ArithmeticTests/TestModulo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.ArithmeticTests 7 | { 8 | public class TestModulo : TestBase 9 | { 10 | [Test] 11 | public void Test1() 12 | { 13 | Expression> exp = (a, b) => a % b; 14 | var f = Compile(exp, CompilerOptions.All); 15 | Assert.AreEqual(1, f(1, 2)); 16 | Assert.AreEqual(2, f(5, 3)); 17 | Assert.AreEqual(-1, f(-3, 2)); 18 | } 19 | 20 | [Test] 21 | public void Test2() 22 | { 23 | Expression> exp = (a, b) => a % b; 24 | var f = Compile(exp, CompilerOptions.All); 25 | Assert.AreEqual(1, f(1, 2)); 26 | Assert.AreEqual(2, f(5, 3)); 27 | Assert.AreEqual(-1, f(-3, 2)); 28 | Assert.IsNull(f(null, 2)); 29 | Assert.IsNull(f(1, null)); 30 | Assert.IsNull(f(null, null)); 31 | } 32 | 33 | [Test] 34 | public void Test3() 35 | { 36 | Expression> exp = (a, b) => a % b; 37 | var f = Compile(exp, CompilerOptions.All); 38 | Assert.AreEqual(1, f(1, 2)); 39 | Assert.AreEqual(2, f(5, 3)); 40 | Assert.AreEqual(-1, f(-3, 2)); 41 | Assert.AreEqual(2000000000, f(2000000000, 20000000000)); 42 | Assert.IsNull(f(null, 2)); 43 | Assert.IsNull(f(1, null)); 44 | Assert.IsNull(f(null, null)); 45 | } 46 | 47 | [Test] 48 | public void Test4() 49 | { 50 | Expression> exp = (a, b) => a % b; 51 | var f = Compile(exp, CompilerOptions.All); 52 | Assert.AreEqual(1, f(1, 2)); 53 | Assert.AreEqual(2, f(5, 3)); 54 | Assert.AreEqual(1, f(uint.MaxValue - 3 + 1, 2)); 55 | } 56 | 57 | [Test] 58 | public void Test5() 59 | { 60 | Expression> exp = (a, b) => a % b; 61 | var f = Compile(exp, CompilerOptions.All); 62 | Assert.AreEqual(1, f(1, 2)); 63 | Assert.AreEqual(2, f(5, 3)); 64 | Assert.AreEqual(-1, f(-3, 2)); 65 | Assert.IsNull(f(null, 2)); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ArithmeticTests/TestPower.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.ArithmeticTests 7 | { 8 | public class TestPower : TestBase 9 | { 10 | [Test] 11 | public void TestPower1() 12 | { 13 | ParameterExpression a = Expression.Parameter(typeof(double)); 14 | ParameterExpression b = Expression.Parameter(typeof(double)); 15 | var exp = Expression.Lambda>(Expression.Power(a, b), a, b); 16 | var f = Compile(exp, CompilerOptions.All); 17 | Assert.AreEqual(1, f(0, 0)); 18 | Assert.AreEqual(1, f(1, 2)); 19 | Assert.AreEqual(16, f(2, 4)); 20 | Assert.AreEqual(1, f(-1, 2)); 21 | } 22 | 23 | [Test] 24 | public void TestPower2() 25 | { 26 | ParameterExpression a = Expression.Parameter(typeof(double?)); 27 | ParameterExpression b = Expression.Parameter(typeof(double?)); 28 | var exp = Expression.Lambda>(Expression.Power(a, b), a, b); 29 | var f = Compile(exp, CompilerOptions.All); 30 | Assert.AreEqual(1, f(0, 0)); 31 | Assert.AreEqual(1, f(1, 2)); 32 | Assert.AreEqual(16, f(2, 4)); 33 | Assert.AreEqual(1, f(-1, 2)); 34 | Assert.IsNull(f(null, 2)); 35 | Assert.IsNull(f(1, null)); 36 | Assert.IsNull(f(null, null)); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ArithmeticTests/TestUnaryPlusMinus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.ArithmeticTests 7 | { 8 | public class TestUnaryPlusMinus : TestBase 9 | { 10 | [Test] 11 | public void TestNegate1() 12 | { 13 | Expression> exp = x => -x; 14 | var f = Compile(exp, CompilerOptions.All); 15 | Assert.AreEqual(0, f(0)); 16 | Assert.AreEqual(-1, f(1)); 17 | Assert.AreEqual(1, f(-1)); 18 | Assert.AreEqual(int.MinValue, f(int.MinValue)); 19 | } 20 | 21 | [Test] 22 | public void TestNegate2() 23 | { 24 | Expression> exp = x => -x; 25 | var f = Compile(exp, CompilerOptions.All); 26 | Assert.AreEqual(0, f(0)); 27 | Assert.AreEqual(-1, f(1)); 28 | Assert.AreEqual(1, f(-1)); 29 | Assert.IsNull(f(null)); 30 | Assert.AreEqual(int.MinValue, f(int.MinValue)); 31 | } 32 | 33 | [Test] 34 | public void TestNegate3() 35 | { 36 | ParameterExpression parameter = Expression.Parameter(typeof(int)); 37 | Expression> exp = Expression.Lambda>(Expression.NegateChecked(parameter), parameter); 38 | var f = Compile(exp, CompilerOptions.All); 39 | Assert.AreEqual(0, f(0)); 40 | Assert.AreEqual(-1, f(1)); 41 | Assert.AreEqual(1, f(-1)); 42 | Assert.Throws(() => f(int.MinValue)); 43 | } 44 | 45 | [Test] 46 | public void TestNegate4() 47 | { 48 | ParameterExpression parameter = Expression.Parameter(typeof(int?)); 49 | Expression> exp = Expression.Lambda>(Expression.NegateChecked(parameter), parameter); 50 | var f = Compile(exp, CompilerOptions.All); 51 | Assert.AreEqual(0, f(0)); 52 | Assert.AreEqual(-1, f(1)); 53 | Assert.AreEqual(1, f(-1)); 54 | Assert.IsNull(f(null)); 55 | Assert.Throws(() => f(int.MinValue)); 56 | } 57 | 58 | [Test] 59 | public void TestUnaryPlus1() 60 | { 61 | ParameterExpression parameter = Expression.Parameter(typeof(int)); 62 | Expression> exp = Expression.Lambda>(Expression.UnaryPlus(parameter), parameter); 63 | var f = Compile(exp, CompilerOptions.All); 64 | Assert.AreEqual(0, f(0)); 65 | Assert.AreEqual(1, f(1)); 66 | Assert.AreEqual(-1, f(-1)); 67 | Assert.AreEqual(int.MinValue, f(int.MinValue)); 68 | } 69 | 70 | [Test] 71 | public void TestUnaryPlus2() 72 | { 73 | ParameterExpression parameter = Expression.Parameter(typeof(int?)); 74 | Expression> exp = Expression.Lambda>(Expression.UnaryPlus(parameter), parameter); 75 | var f = Compile(exp, CompilerOptions.All); 76 | Assert.AreEqual(0, f(0)); 77 | Assert.AreEqual(1, f(1)); 78 | Assert.AreEqual(-1, f(-1)); 79 | Assert.IsNull(f(null)); 80 | Assert.AreEqual(int.MinValue, f(int.MinValue)); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/AndAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.AndAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestInt() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int), "a"); 15 | ParameterExpression b = Expression.Parameter(typeof(int), "b"); 16 | Expression> exp = Expression.Lambda>(Expression.AndAssign(a, b), a, b); 17 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 18 | Assert.AreEqual(0, f(0, 123)); 19 | Assert.AreEqual(1, f(3, 5)); 20 | Assert.AreEqual(17235476 & 73172563, f(17235476, 73172563)); 21 | 22 | exp = Expression.Lambda>(Expression.Block(typeof(int), Expression.AndAssign(a, b), a), a, b); 23 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 24 | Assert.AreEqual(0, f(0, 123)); 25 | Assert.AreEqual(1, f(3, 5)); 26 | Assert.AreEqual(17235476 & 73172563, f(17235476, 73172563)); 27 | } 28 | 29 | [Test] 30 | public void TestNullableInt() 31 | { 32 | ParameterExpression a = Expression.Parameter(typeof(int?), "a"); 33 | ParameterExpression b = Expression.Parameter(typeof(int?), "b"); 34 | Expression> exp = Expression.Lambda>(Expression.AndAssign(a, b), a, b); 35 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 36 | Assert.AreEqual(0, f(0, 123)); 37 | Assert.AreEqual(1, f(3, 5)); 38 | Assert.AreEqual(17235476 & 73172563, f(17235476, 73172563)); 39 | Assert.IsNull(f(null, 1)); 40 | Assert.IsNull(f(123, null)); 41 | Assert.IsNull(f(null, null)); 42 | 43 | exp = Expression.Lambda>(Expression.Block(typeof(int?), Expression.AndAssign(a, b), a), a, b); 44 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 45 | Assert.AreEqual(0, f(0, 123)); 46 | Assert.AreEqual(1, f(3, 5)); 47 | Assert.AreEqual(17235476 & 73172563, f(17235476, 73172563)); 48 | Assert.IsNull(f(null, 1)); 49 | Assert.IsNull(f(123, null)); 50 | Assert.IsNull(f(null, null)); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/AndAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.AndAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestIntProp() 13 | { 14 | ParameterExpression b = Expression.Parameter(typeof(int), "b"); 15 | Expression> exp = Expression.Lambda>(Expression.AndAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("IntProp")), b), b); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 17 | TestClassA.IntProp = 0; 18 | Assert.AreEqual(0, f(123)); 19 | Assert.AreEqual(0, TestClassA.IntProp); 20 | TestClassA.IntProp = 5; 21 | Assert.AreEqual(1, f(3)); 22 | Assert.AreEqual(1, TestClassA.IntProp); 23 | TestClassA.IntProp = 17235476; 24 | Assert.AreEqual(17235476 & 73172563, f(73172563)); 25 | Assert.AreEqual(17235476 & 73172563, TestClassA.IntProp); 26 | } 27 | 28 | [Test] 29 | public void TestNullable() 30 | { 31 | ParameterExpression b = Expression.Parameter(typeof(int?), "b"); 32 | Expression> exp = Expression.Lambda>(Expression.AndAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("NullableIntField")), b), b); 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | TestClassA.NullableIntField = 0; 35 | Assert.AreEqual(0, f(123)); 36 | Assert.AreEqual(0, TestClassA.NullableIntField); 37 | TestClassA.NullableIntField = 5; 38 | Assert.AreEqual(1, f(3)); 39 | Assert.AreEqual(1, TestClassA.NullableIntField); 40 | TestClassA.NullableIntField = 17235476; 41 | Assert.AreEqual(17235476 & 73172563, f(73172563)); 42 | Assert.AreEqual(17235476 & 73172563, TestClassA.NullableIntField); 43 | TestClassA.NullableIntField = null; 44 | Assert.IsNull(f(2)); 45 | Assert.IsNull(TestClassA.NullableIntField); 46 | TestClassA.NullableIntField = 1; 47 | Assert.IsNull(f(null)); 48 | Assert.IsNull(TestClassA.NullableIntField); 49 | Assert.IsNull(f(null)); 50 | Assert.IsNull(TestClassA.NullableIntField); 51 | } 52 | 53 | public class TestClassA 54 | { 55 | public static int IntProp { get; set; } 56 | public static int? NullableIntField; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/ExclusiveOrAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.ExclusiveOrAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestInt() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int), "a"); 15 | ParameterExpression b = Expression.Parameter(typeof(int), "b"); 16 | Expression> exp = Expression.Lambda>(Expression.ExclusiveOrAssign(a, b), a, b); 17 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 18 | Assert.AreEqual(123, f(0, 123)); 19 | Assert.AreEqual(6, f(3, 5)); 20 | Assert.AreEqual(17235476 ^ 73172563, f(17235476, 73172563)); 21 | 22 | exp = Expression.Lambda>(Expression.Block(typeof(int), Expression.ExclusiveOrAssign(a, b), a), a, b); 23 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 24 | Assert.AreEqual(123, f(0, 123)); 25 | Assert.AreEqual(6, f(3, 5)); 26 | Assert.AreEqual(17235476 ^ 73172563, f(17235476, 73172563)); 27 | } 28 | 29 | [Test] 30 | public void TestNullableInt() 31 | { 32 | ParameterExpression a = Expression.Parameter(typeof(int?), "a"); 33 | ParameterExpression b = Expression.Parameter(typeof(int?), "b"); 34 | Expression> exp = Expression.Lambda>(Expression.ExclusiveOrAssign(a, b), a, b); 35 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 36 | Assert.AreEqual(123, f(0, 123)); 37 | Assert.AreEqual(6, f(3, 5)); 38 | Assert.AreEqual(17235476 ^ 73172563, f(17235476, 73172563)); 39 | Assert.IsNull(f(null, 1)); 40 | Assert.IsNull(f(123, null)); 41 | Assert.IsNull(f(null, null)); 42 | 43 | exp = Expression.Lambda>(Expression.Block(typeof(int?), Expression.ExclusiveOrAssign(a, b), a), a, b); 44 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 45 | Assert.AreEqual(123, f(0, 123)); 46 | Assert.AreEqual(6, f(3, 5)); 47 | Assert.AreEqual(17235476 ^ 73172563, f(17235476, 73172563)); 48 | Assert.IsNull(f(null, 1)); 49 | Assert.IsNull(f(123, null)); 50 | Assert.IsNull(f(null, null)); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/ExclusiveOrAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.ExclusiveOrAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestIntProp() 13 | { 14 | ParameterExpression b = Expression.Parameter(typeof(int), "b"); 15 | Expression> exp = Expression.Lambda>(Expression.ExclusiveOrAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("IntProp")), b), b); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 17 | TestClassA.IntProp = 0; 18 | Assert.AreEqual(123, f(123)); 19 | Assert.AreEqual(123, TestClassA.IntProp); 20 | TestClassA.IntProp = 5; 21 | Assert.AreEqual(6, f(3)); 22 | Assert.AreEqual(6, TestClassA.IntProp); 23 | TestClassA.IntProp = 17235476; 24 | Assert.AreEqual(17235476 ^ 73172563, f(73172563)); 25 | Assert.AreEqual(17235476 ^ 73172563, TestClassA.IntProp); 26 | } 27 | 28 | [Test] 29 | public void TestNullable() 30 | { 31 | ParameterExpression b = Expression.Parameter(typeof(int?), "b"); 32 | Expression> exp = Expression.Lambda>(Expression.ExclusiveOrAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("NullableIntField")), b), b); 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | TestClassA.NullableIntField = 0; 35 | Assert.AreEqual(123, f(123)); 36 | Assert.AreEqual(123, TestClassA.NullableIntField); 37 | TestClassA.NullableIntField = 5; 38 | Assert.AreEqual(6, f(3)); 39 | Assert.AreEqual(6, TestClassA.NullableIntField); 40 | TestClassA.NullableIntField = 17235476; 41 | Assert.AreEqual(17235476 ^ 73172563, f(73172563)); 42 | Assert.AreEqual(17235476 ^ 73172563, TestClassA.NullableIntField); 43 | TestClassA.NullableIntField = null; 44 | Assert.IsNull(f(2)); 45 | Assert.IsNull(TestClassA.NullableIntField); 46 | TestClassA.NullableIntField = 1; 47 | Assert.IsNull(f(null)); 48 | Assert.IsNull(TestClassA.NullableIntField); 49 | Assert.IsNull(f(null)); 50 | Assert.IsNull(TestClassA.NullableIntField); 51 | } 52 | 53 | public class TestClassA 54 | { 55 | public static int IntProp { get; set; } 56 | public static int? NullableIntField; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/ModuloAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.ModuloAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestInt() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int), "a"); 15 | ParameterExpression b = Expression.Parameter(typeof(int), "b"); 16 | Expression> exp = Expression.Lambda>(Expression.ModuloAssign(a, b), a, b); 17 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 18 | Assert.AreEqual(1, f(1, 2)); 19 | Assert.AreEqual(2, f(5, 3)); 20 | Assert.AreEqual(-1, f(-3, 2)); 21 | 22 | exp = Expression.Lambda>(Expression.Block(typeof(int), Expression.ModuloAssign(a, b), a), a, b); 23 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 24 | Assert.AreEqual(1, f(1, 2)); 25 | Assert.AreEqual(2, f(5, 3)); 26 | Assert.AreEqual(-1, f(-3, 2)); 27 | } 28 | 29 | [Test] 30 | public void TestNullableInt() 31 | { 32 | ParameterExpression a = Expression.Parameter(typeof(int?), "a"); 33 | ParameterExpression b = Expression.Parameter(typeof(int?), "b"); 34 | Expression> exp = Expression.Lambda>(Expression.ModuloAssign(a, b), a, b); 35 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 36 | Assert.AreEqual(1, f(1, 2)); 37 | Assert.AreEqual(2, f(5, 3)); 38 | Assert.AreEqual(-1, f(-3, 2)); 39 | Assert.IsNull(f(null, 2)); 40 | Assert.IsNull(f(1, null)); 41 | Assert.IsNull(f(null, null)); 42 | 43 | exp = Expression.Lambda>(Expression.Block(typeof(int?), Expression.ModuloAssign(a, b), a), a, b); 44 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 45 | Assert.AreEqual(1, f(1, 2)); 46 | Assert.AreEqual(2, f(5, 3)); 47 | Assert.AreEqual(-1, f(-3, 2)); 48 | Assert.IsNull(f(null, 2)); 49 | Assert.IsNull(f(1, null)); 50 | Assert.IsNull(f(null, null)); 51 | } 52 | 53 | [Test] 54 | public void TestUInt() 55 | { 56 | ParameterExpression a = Expression.Parameter(typeof(uint), "a"); 57 | ParameterExpression b = Expression.Parameter(typeof(uint), "b"); 58 | Expression> exp = Expression.Lambda>(Expression.ModuloAssign(a, b), a, b); 59 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 60 | Assert.AreEqual(1, f(1, 2)); 61 | Assert.AreEqual(2, f(5, 3)); 62 | Assert.AreEqual(1, f(uint.MaxValue - 3 + 1, 2)); 63 | 64 | exp = Expression.Lambda>(Expression.Block(typeof(uint), Expression.ModuloAssign(a, b), a), a, b); 65 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 66 | Assert.AreEqual(1, f(1, 2)); 67 | Assert.AreEqual(2, f(5, 3)); 68 | Assert.AreEqual(1, f(uint.MaxValue - 3 + 1, 2)); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/ModuloAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.ModuloAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestIntProp() 13 | { 14 | ParameterExpression b = Expression.Parameter(typeof(int), "b"); 15 | Expression> exp = Expression.Lambda>(Expression.ModuloAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("IntProp")), b), b); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 17 | TestClassA.IntProp = 1; 18 | Assert.AreEqual(1, f(2)); 19 | Assert.AreEqual(1, TestClassA.IntProp); 20 | TestClassA.IntProp = 5; 21 | Assert.AreEqual(2, f(3)); 22 | Assert.AreEqual(2, TestClassA.IntProp); 23 | TestClassA.IntProp = -3; 24 | Assert.AreEqual(-1, f(2)); 25 | Assert.AreEqual(-1, TestClassA.IntProp); 26 | } 27 | 28 | [Test] 29 | public void TestNullable() 30 | { 31 | ParameterExpression b = Expression.Parameter(typeof(int?), "b"); 32 | Expression> exp = Expression.Lambda>(Expression.ModuloAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("NullableIntProp")), b), b); 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | TestClassA.NullableIntProp = 1; 35 | Assert.AreEqual(1, f(2)); 36 | Assert.AreEqual(1, TestClassA.NullableIntProp); 37 | TestClassA.NullableIntProp = 5; 38 | Assert.AreEqual(2, f(3)); 39 | Assert.AreEqual(2, TestClassA.NullableIntProp); 40 | TestClassA.NullableIntProp = -3; 41 | Assert.AreEqual(-1, f(2)); 42 | Assert.AreEqual(-1, TestClassA.NullableIntProp); 43 | TestClassA.NullableIntProp = null; 44 | Assert.IsNull(f(2)); 45 | Assert.IsNull(TestClassA.NullableIntProp); 46 | TestClassA.NullableIntProp = 1; 47 | Assert.IsNull(f(null)); 48 | Assert.IsNull(TestClassA.NullableIntProp); 49 | Assert.IsNull(f(null)); 50 | Assert.IsNull(TestClassA.NullableIntProp); 51 | } 52 | 53 | [Test] 54 | public void TestUInt() 55 | { 56 | ParameterExpression b = Expression.Parameter(typeof(uint), "b"); 57 | Expression> exp = Expression.Lambda>(Expression.ModuloAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("UIntField")), b), b); 58 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 59 | TestClassA.UIntField = 1; 60 | Assert.AreEqual(1, f(2)); 61 | Assert.AreEqual(1, TestClassA.UIntField); 62 | TestClassA.UIntField = 5; 63 | Assert.AreEqual(2, f(3)); 64 | Assert.AreEqual(2, TestClassA.UIntField); 65 | TestClassA.UIntField = uint.MaxValue - 3 + 1; 66 | Assert.AreEqual(1, f(2)); 67 | Assert.AreEqual(1, TestClassA.UIntField); 68 | } 69 | 70 | public class TestClassA 71 | { 72 | public static int IntProp { get; set; } 73 | public static int? NullableIntProp { get; set; } 74 | public static uint UIntField; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/OrAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.OrAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestInt() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int), "a"); 15 | ParameterExpression b = Expression.Parameter(typeof(int), "b"); 16 | Expression> exp = Expression.Lambda>(Expression.OrAssign(a, b), a, b); 17 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 18 | Assert.AreEqual(123, f(0, 123)); 19 | Assert.AreEqual(7, f(3, 5)); 20 | Assert.AreEqual(17235476 | 73172563, f(17235476, 73172563)); 21 | 22 | exp = Expression.Lambda>(Expression.Block(typeof(int), Expression.OrAssign(a, b), a), a, b); 23 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 24 | Assert.AreEqual(123, f(0, 123)); 25 | Assert.AreEqual(7, f(3, 5)); 26 | Assert.AreEqual(17235476 | 73172563, f(17235476, 73172563)); 27 | } 28 | 29 | [Test] 30 | public void TestNullableInt() 31 | { 32 | ParameterExpression a = Expression.Parameter(typeof(int?), "a"); 33 | ParameterExpression b = Expression.Parameter(typeof(int?), "b"); 34 | Expression> exp = Expression.Lambda>(Expression.OrAssign(a, b), a, b); 35 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 36 | Assert.AreEqual(123, f(0, 123)); 37 | Assert.AreEqual(7, f(3, 5)); 38 | Assert.AreEqual(17235476 | 73172563, f(17235476, 73172563)); 39 | Assert.IsNull(f(null, 1)); 40 | Assert.IsNull(f(123, null)); 41 | Assert.IsNull(f(null, null)); 42 | 43 | exp = Expression.Lambda>(Expression.Block(typeof(int?), Expression.OrAssign(a, b), a), a, b); 44 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 45 | Assert.AreEqual(123, f(0, 123)); 46 | Assert.AreEqual(7, f(3, 5)); 47 | Assert.AreEqual(17235476 | 73172563, f(17235476, 73172563)); 48 | Assert.IsNull(f(null, 1)); 49 | Assert.IsNull(f(123, null)); 50 | Assert.IsNull(f(null, null)); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/OrAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.OrAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestIntProp() 13 | { 14 | ParameterExpression b = Expression.Parameter(typeof(int), "b"); 15 | Expression> exp = Expression.Lambda>(Expression.OrAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("IntProp")), b), b); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 17 | TestClassA.IntProp = 0; 18 | Assert.AreEqual(123, f(123)); 19 | Assert.AreEqual(123, TestClassA.IntProp); 20 | TestClassA.IntProp = 5; 21 | Assert.AreEqual(7, f(3)); 22 | Assert.AreEqual(7, TestClassA.IntProp); 23 | TestClassA.IntProp = 17235476; 24 | Assert.AreEqual(17235476 | 73172563, f(73172563)); 25 | Assert.AreEqual(17235476 | 73172563, TestClassA.IntProp); 26 | } 27 | 28 | [Test] 29 | public void TestNullable() 30 | { 31 | ParameterExpression b = Expression.Parameter(typeof(int?), "b"); 32 | Expression> exp = Expression.Lambda>(Expression.OrAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("NullableIntField")), b), b); 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | TestClassA.NullableIntField = 0; 35 | Assert.AreEqual(123, f(123)); 36 | Assert.AreEqual(123, TestClassA.NullableIntField); 37 | TestClassA.NullableIntField = 5; 38 | Assert.AreEqual(7, f(3)); 39 | Assert.AreEqual(7, TestClassA.NullableIntField); 40 | TestClassA.NullableIntField = 17235476; 41 | Assert.AreEqual(17235476 | 73172563, f(73172563)); 42 | Assert.AreEqual(17235476 | 73172563, TestClassA.NullableIntField); 43 | TestClassA.NullableIntField = null; 44 | Assert.IsNull(f(2)); 45 | Assert.IsNull(TestClassA.NullableIntField); 46 | TestClassA.NullableIntField = 1; 47 | Assert.IsNull(f(null)); 48 | Assert.IsNull(TestClassA.NullableIntField); 49 | Assert.IsNull(f(null)); 50 | Assert.IsNull(TestClassA.NullableIntField); 51 | } 52 | 53 | public class TestClassA 54 | { 55 | public static int IntProp { get; set; } 56 | public static int? NullableIntField; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PostDecrementAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PostDecrementAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestInt() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int), "a"); 15 | Expression> exp = Expression.Lambda>(Expression.PostDecrementAssign(a), a); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.AreEqual(0, f(0)); 18 | Assert.AreEqual(1, f(1)); 19 | Assert.AreEqual(int.MinValue, f(int.MinValue)); 20 | 21 | exp = Expression.Lambda>(Expression.Block(typeof(int), Expression.PostDecrementAssign(a), a), a); 22 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 23 | Assert.AreEqual(-1, f(0)); 24 | Assert.AreEqual(0, f(1)); 25 | Assert.AreEqual(int.MaxValue, f(int.MinValue)); 26 | } 27 | 28 | [Test] 29 | public void TestNullableInt() 30 | { 31 | ParameterExpression a = Expression.Parameter(typeof(int?), "a"); 32 | Expression> exp = Expression.Lambda>(Expression.PostDecrementAssign(a), a); 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | Assert.AreEqual(0, f(0)); 35 | Assert.AreEqual(1, f(1)); 36 | Assert.AreEqual(int.MinValue, f(int.MinValue)); 37 | Assert.IsNull(f(null)); 38 | 39 | exp = Expression.Lambda>(Expression.Block(typeof(int?), Expression.PostDecrementAssign(a), a), a); 40 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 41 | Assert.AreEqual(-1, f(0)); 42 | Assert.AreEqual(0, f(1)); 43 | Assert.AreEqual(int.MaxValue, f(int.MinValue)); 44 | Assert.IsNull(f(null)); 45 | } 46 | 47 | [Test] 48 | public void TestDouble() 49 | { 50 | ParameterExpression a = Expression.Parameter(typeof(double), "a"); 51 | Expression> exp = Expression.Lambda>(Expression.PostDecrementAssign(a), a); 52 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 53 | Assert.AreEqual(0, f(0)); 54 | Assert.AreEqual(1, f(1)); 55 | Assert.AreEqual(0.5, f(0.5)); 56 | 57 | exp = Expression.Lambda>(Expression.Block(typeof(double), Expression.PostDecrementAssign(a), a), a); 58 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 59 | Assert.AreEqual(-1, f(0)); 60 | Assert.AreEqual(0, f(1)); 61 | Assert.AreEqual(-0.5, f(0.5)); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PostDecrementAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PostDecrementAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestIntProp() 13 | { 14 | Expression> exp = Expression.Lambda>(Expression.PostDecrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("IntProp")))); 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 16 | TestClassA.IntProp = 0; 17 | Assert.AreEqual(0, f()); 18 | Assert.AreEqual(-1, TestClassA.IntProp); 19 | TestClassA.IntProp = 1; 20 | Assert.AreEqual(1, f()); 21 | Assert.AreEqual(0, TestClassA.IntProp); 22 | TestClassA.IntProp = int.MinValue; 23 | Assert.AreEqual(int.MinValue, f()); 24 | Assert.AreEqual(int.MaxValue, TestClassA.IntProp); 25 | } 26 | 27 | [Test] 28 | public void TestDoubleField() 29 | { 30 | Expression> exp = Expression.Lambda>(Expression.PostDecrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("DoubleField")))); 31 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 32 | TestClassA.DoubleField = 0; 33 | Assert.AreEqual(0, f()); 34 | Assert.AreEqual(-1, TestClassA.DoubleField); 35 | TestClassA.DoubleField = 1; 36 | Assert.AreEqual(1, f()); 37 | Assert.AreEqual(0, TestClassA.DoubleField); 38 | TestClassA.DoubleField = 0.5; 39 | Assert.AreEqual(0.5, f()); 40 | Assert.AreEqual(-0.5, TestClassA.DoubleField); 41 | } 42 | 43 | [Test] 44 | public void TestNullable() 45 | { 46 | Expression> exp = Expression.Lambda>(Expression.PostDecrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("NullableIntProp")))); 47 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 48 | TestClassA.NullableIntProp = 0; 49 | Assert.AreEqual(0, f()); 50 | Assert.AreEqual(-1, TestClassA.NullableIntProp); 51 | TestClassA.NullableIntProp = 1; 52 | Assert.AreEqual(1, f()); 53 | Assert.AreEqual(0, TestClassA.NullableIntProp); 54 | TestClassA.NullableIntProp = int.MinValue; 55 | Assert.AreEqual(int.MinValue, f()); 56 | Assert.AreEqual(int.MaxValue, TestClassA.NullableIntProp); 57 | TestClassA.NullableIntProp = null; 58 | Assert.IsNull(f()); 59 | Assert.IsNull(TestClassA.NullableIntProp); 60 | } 61 | 62 | public class TestClassA 63 | { 64 | public static int IntProp { get; set; } 65 | public static int? NullableIntProp { get; set; } 66 | public static double DoubleField; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PostIncrementAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PostIncrementAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestInt() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int), "a"); 15 | Expression> exp = Expression.Lambda>(Expression.PostIncrementAssign(a), a); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.AreEqual(0, f(0)); 18 | Assert.AreEqual(-1, f(-1)); 19 | Assert.AreEqual(int.MaxValue, f(int.MaxValue)); 20 | 21 | exp = Expression.Lambda>(Expression.Block(typeof(int), Expression.PostIncrementAssign(a), a), a); 22 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 23 | Assert.AreEqual(1, f(0)); 24 | Assert.AreEqual(0, f(-1)); 25 | Assert.AreEqual(int.MinValue, f(int.MaxValue)); 26 | } 27 | 28 | [Test] 29 | public void TestNullableInt() 30 | { 31 | ParameterExpression a = Expression.Parameter(typeof(int?), "a"); 32 | Expression> exp = Expression.Lambda>(Expression.PostIncrementAssign(a), a); 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | Assert.AreEqual(0, f(0)); 35 | Assert.AreEqual(-1, f(-1)); 36 | Assert.AreEqual(int.MaxValue, f(int.MaxValue)); 37 | Assert.IsNull(f(null)); 38 | 39 | exp = Expression.Lambda>(Expression.Block(typeof(int?), Expression.PostIncrementAssign(a), a), a); 40 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 41 | Assert.AreEqual(1, f(0)); 42 | Assert.AreEqual(0, f(-1)); 43 | Assert.AreEqual(int.MinValue, f(int.MaxValue)); 44 | Assert.IsNull(f(null)); 45 | } 46 | 47 | [Test] 48 | public void TestDouble() 49 | { 50 | ParameterExpression a = Expression.Parameter(typeof(double), "a"); 51 | Expression> exp = Expression.Lambda>(Expression.PostIncrementAssign(a), a); 52 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 53 | Assert.AreEqual(0, f(0)); 54 | Assert.AreEqual(-1, f(-1)); 55 | Assert.AreEqual(-0.5, f(-0.5)); 56 | 57 | exp = Expression.Lambda>(Expression.Block(typeof(double), Expression.PostIncrementAssign(a), a), a); 58 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 59 | Assert.AreEqual(1, f(0)); 60 | Assert.AreEqual(0, f(-1)); 61 | Assert.AreEqual(0.5, f(-0.5)); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PostIncrementAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PostIncrementAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestIntProp() 13 | { 14 | Expression> exp = Expression.Lambda>(Expression.PostIncrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("IntProp")))); 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 16 | TestClassA.IntProp = 0; 17 | Assert.AreEqual(0, f()); 18 | Assert.AreEqual(1, TestClassA.IntProp); 19 | TestClassA.IntProp = -1; 20 | Assert.AreEqual(-1, f()); 21 | Assert.AreEqual(0, TestClassA.IntProp); 22 | TestClassA.IntProp = int.MaxValue; 23 | Assert.AreEqual(int.MaxValue, f()); 24 | Assert.AreEqual(int.MinValue, TestClassA.IntProp); 25 | } 26 | 27 | [Test] 28 | public void TestDoubleField() 29 | { 30 | Expression> exp = Expression.Lambda>(Expression.PostIncrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("DoubleField")))); 31 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 32 | TestClassA.DoubleField = 0; 33 | Assert.AreEqual(0, f()); 34 | Assert.AreEqual(1, TestClassA.DoubleField); 35 | TestClassA.DoubleField = -1; 36 | Assert.AreEqual(-1, f()); 37 | Assert.AreEqual(0, TestClassA.DoubleField); 38 | TestClassA.DoubleField = -0.5; 39 | Assert.AreEqual(-0.5, f()); 40 | Assert.AreEqual(0.5, TestClassA.DoubleField); 41 | } 42 | 43 | [Test] 44 | public void TestNullable() 45 | { 46 | Expression> exp = Expression.Lambda>(Expression.PostIncrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("NullableIntProp")))); 47 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 48 | TestClassA.NullableIntProp = 0; 49 | Assert.AreEqual(0, f()); 50 | Assert.AreEqual(1, TestClassA.NullableIntProp); 51 | TestClassA.NullableIntProp = -1; 52 | Assert.AreEqual(-1, f()); 53 | Assert.AreEqual(0, TestClassA.NullableIntProp); 54 | TestClassA.NullableIntProp = int.MaxValue; 55 | Assert.AreEqual(int.MaxValue, f()); 56 | Assert.AreEqual(int.MinValue, TestClassA.NullableIntProp); 57 | TestClassA.NullableIntProp = null; 58 | Assert.IsNull(f()); 59 | Assert.IsNull(TestClassA.NullableIntProp); 60 | } 61 | 62 | public class TestClassA 63 | { 64 | public static int IntProp { get; set; } 65 | public static int? NullableIntProp { get; set; } 66 | public static double DoubleField; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PowerAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PowerAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestDouble() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(double), "a"); 15 | ParameterExpression b = Expression.Parameter(typeof(double), "b"); 16 | Expression> exp = Expression.Lambda>(Expression.PowerAssign(a, b), a, b); 17 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 18 | Assert.AreEqual(1, f(0, 0)); 19 | Assert.AreEqual(1, f(1, 2)); 20 | Assert.AreEqual(16, f(2, 4)); 21 | Assert.AreEqual(1, f(-1, 2)); 22 | 23 | exp = Expression.Lambda>(Expression.Block(typeof(double), Expression.PowerAssign(a, b), a), a, b); 24 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 25 | Assert.AreEqual(1, f(0, 0)); 26 | Assert.AreEqual(1, f(1, 2)); 27 | Assert.AreEqual(16, f(2, 4)); 28 | Assert.AreEqual(1, f(-1, 2)); 29 | } 30 | 31 | [Test] 32 | public void TestNullableDouble() 33 | { 34 | ParameterExpression a = Expression.Parameter(typeof(double?), "a"); 35 | ParameterExpression b = Expression.Parameter(typeof(double?), "b"); 36 | Expression> exp = Expression.Lambda>(Expression.PowerAssign(a, b), a, b); 37 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 38 | Assert.AreEqual(1, f(0, 0)); 39 | Assert.AreEqual(1, f(1, 2)); 40 | Assert.AreEqual(16, f(2, 4)); 41 | Assert.AreEqual(1, f(-1, 2)); 42 | Assert.IsNull(f(null, 2)); 43 | Assert.IsNull(f(1, null)); 44 | Assert.IsNull(f(null, null)); 45 | 46 | exp = Expression.Lambda>(Expression.Block(typeof(double?), Expression.PowerAssign(a, b), a), a, b); 47 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 48 | Assert.AreEqual(1, f(0, 0)); 49 | Assert.AreEqual(1, f(1, 2)); 50 | Assert.AreEqual(16, f(2, 4)); 51 | Assert.AreEqual(1, f(-1, 2)); 52 | Assert.IsNull(f(null, 2)); 53 | Assert.IsNull(f(1, null)); 54 | Assert.IsNull(f(null, null)); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PowerAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PowerAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestDoubleField() 13 | { 14 | ParameterExpression b = Expression.Parameter(typeof(double), "b"); 15 | Expression> exp = Expression.Lambda>(Expression.PowerAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("DoubleField")), b), b); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 17 | TestClassA.DoubleField = 0; 18 | Assert.AreEqual(1, f(0)); 19 | Assert.AreEqual(1, TestClassA.DoubleField); 20 | TestClassA.DoubleField = 1; 21 | Assert.AreEqual(1, f(2)); 22 | Assert.AreEqual(1, TestClassA.DoubleField); 23 | TestClassA.DoubleField = 2; 24 | Assert.AreEqual(16, f(4)); 25 | Assert.AreEqual(16, TestClassA.DoubleField); 26 | TestClassA.DoubleField = -1; 27 | Assert.AreEqual(1, f(2)); 28 | Assert.AreEqual(1, TestClassA.DoubleField); 29 | } 30 | 31 | [Test] 32 | public void TestNullableDouble() 33 | { 34 | ParameterExpression b = Expression.Parameter(typeof(double?), "b"); 35 | Expression> exp = Expression.Lambda>(Expression.PowerAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("NullableDoubleProp")), b), b); 36 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 37 | TestClassA.NullableDoubleProp = 0; 38 | Assert.AreEqual(1, f(0)); 39 | Assert.AreEqual(1, TestClassA.NullableDoubleProp); 40 | TestClassA.NullableDoubleProp = 1; 41 | Assert.AreEqual(1, f(2)); 42 | Assert.AreEqual(1, TestClassA.NullableDoubleProp); 43 | TestClassA.NullableDoubleProp = 2; 44 | Assert.AreEqual(16, f(4)); 45 | Assert.AreEqual(16, TestClassA.NullableDoubleProp); 46 | TestClassA.NullableDoubleProp = -1; 47 | Assert.AreEqual(1, f(2)); 48 | Assert.AreEqual(1, TestClassA.NullableDoubleProp); 49 | TestClassA.NullableDoubleProp = null; 50 | Assert.IsNull(f(2)); 51 | Assert.IsNull(TestClassA.NullableDoubleProp); 52 | TestClassA.NullableDoubleProp = 1; 53 | Assert.IsNull(f(null)); 54 | Assert.IsNull(TestClassA.NullableDoubleProp); 55 | Assert.IsNull(f(null)); 56 | Assert.IsNull(TestClassA.NullableDoubleProp); 57 | } 58 | 59 | public class TestClassA 60 | { 61 | public static double? NullableDoubleProp { get; set; } 62 | public static double DoubleField; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PreDecrementAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PreDecrementAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestInt() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int), "a"); 15 | Expression> exp = Expression.Lambda>(Expression.PreDecrementAssign(a), a); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.AreEqual(-1, f(0)); 18 | Assert.AreEqual(0, f(1)); 19 | Assert.AreEqual(int.MaxValue, f(int.MinValue)); 20 | 21 | exp = Expression.Lambda>(Expression.Block(typeof(int), Expression.PreDecrementAssign(a), a), a); 22 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 23 | Assert.AreEqual(-1, f(0)); 24 | Assert.AreEqual(0, f(1)); 25 | Assert.AreEqual(int.MaxValue, f(int.MinValue)); 26 | } 27 | 28 | [Test] 29 | public void TestNullableInt() 30 | { 31 | ParameterExpression a = Expression.Parameter(typeof(int?), "a"); 32 | Expression> exp = Expression.Lambda>(Expression.PreDecrementAssign(a), a); 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | Assert.AreEqual(-1, f(0)); 35 | Assert.AreEqual(0, f(1)); 36 | Assert.AreEqual(int.MaxValue, f(int.MinValue)); 37 | Assert.IsNull(f(null)); 38 | 39 | exp = Expression.Lambda>(Expression.Block(typeof(int?), Expression.PreDecrementAssign(a), a), a); 40 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 41 | Assert.AreEqual(-1, f(0)); 42 | Assert.AreEqual(0, f(1)); 43 | Assert.AreEqual(int.MaxValue, f(int.MinValue)); 44 | Assert.IsNull(f(null)); 45 | } 46 | 47 | [Test] 48 | public void TestDouble() 49 | { 50 | ParameterExpression a = Expression.Parameter(typeof(double), "a"); 51 | Expression> exp = Expression.Lambda>(Expression.PreDecrementAssign(a), a); 52 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 53 | Assert.AreEqual(-1, f(0)); 54 | Assert.AreEqual(0, f(1)); 55 | Assert.AreEqual(-0.5, f(0.5)); 56 | 57 | exp = Expression.Lambda>(Expression.Block(typeof(double), Expression.PreDecrementAssign(a), a), a); 58 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 59 | Assert.AreEqual(-1, f(0)); 60 | Assert.AreEqual(0, f(1)); 61 | Assert.AreEqual(-0.5, f(0.5)); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PreDecrementAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PreDecrementAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestIntProp() 13 | { 14 | Expression> exp = Expression.Lambda>(Expression.PreDecrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("IntProp")))); 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 16 | TestClassA.IntProp = 0; 17 | Assert.AreEqual(-1, f()); 18 | Assert.AreEqual(-1, TestClassA.IntProp); 19 | TestClassA.IntProp = 1; 20 | Assert.AreEqual(0, f()); 21 | Assert.AreEqual(0, TestClassA.IntProp); 22 | TestClassA.IntProp = int.MinValue; 23 | Assert.AreEqual(int.MaxValue, f()); 24 | Assert.AreEqual(int.MaxValue, TestClassA.IntProp); 25 | } 26 | 27 | [Test] 28 | public void TestDoubleField() 29 | { 30 | Expression> exp = Expression.Lambda>(Expression.PreDecrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("DoubleField")))); 31 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 32 | TestClassA.DoubleField = 0; 33 | Assert.AreEqual(-1, f()); 34 | Assert.AreEqual(-1, TestClassA.DoubleField); 35 | TestClassA.DoubleField = 1; 36 | Assert.AreEqual(0, f()); 37 | Assert.AreEqual(0, TestClassA.DoubleField); 38 | TestClassA.DoubleField = 0.5; 39 | Assert.AreEqual(-0.5, f()); 40 | Assert.AreEqual(-0.5, TestClassA.DoubleField); 41 | } 42 | 43 | [Test] 44 | public void TestNullable() 45 | { 46 | Expression> exp = Expression.Lambda>(Expression.PreDecrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("NullableIntProp")))); 47 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 48 | TestClassA.NullableIntProp = 0; 49 | Assert.AreEqual(-1, f()); 50 | Assert.AreEqual(-1, TestClassA.NullableIntProp); 51 | TestClassA.NullableIntProp = 1; 52 | Assert.AreEqual(0, f()); 53 | Assert.AreEqual(0, TestClassA.NullableIntProp); 54 | TestClassA.NullableIntProp = int.MinValue; 55 | Assert.AreEqual(int.MaxValue, f()); 56 | Assert.AreEqual(int.MaxValue, TestClassA.NullableIntProp); 57 | TestClassA.NullableIntProp = null; 58 | Assert.IsNull(f()); 59 | Assert.IsNull(TestClassA.NullableIntProp); 60 | } 61 | 62 | public class TestClassA 63 | { 64 | public static int IntProp { get; set; } 65 | public static int? NullableIntProp { get; set; } 66 | public static double DoubleField; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PreIncrementAssign/TestParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PreIncrementAssign 7 | { 8 | [TestFixture] 9 | public class TestParameter 10 | { 11 | [Test] 12 | public void TestInt() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int), "a"); 15 | Expression> exp = Expression.Lambda>(Expression.PreIncrementAssign(a), a); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.AreEqual(1, f(0)); 18 | Assert.AreEqual(0, f(-1)); 19 | Assert.AreEqual(int.MinValue, f(int.MaxValue)); 20 | 21 | exp = Expression.Lambda>(Expression.Block(typeof(int), Expression.PreIncrementAssign(a), a), a); 22 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 23 | Assert.AreEqual(1, f(0)); 24 | Assert.AreEqual(0, f(-1)); 25 | Assert.AreEqual(int.MinValue, f(int.MaxValue)); 26 | } 27 | 28 | [Test] 29 | public void TestNullableInt() 30 | { 31 | ParameterExpression a = Expression.Parameter(typeof(int?), "a"); 32 | Expression> exp = Expression.Lambda>(Expression.PreIncrementAssign(a), a); 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | Assert.AreEqual(1, f(0)); 35 | Assert.AreEqual(0, f(-1)); 36 | Assert.AreEqual(int.MinValue, f(int.MaxValue)); 37 | Assert.IsNull(f(null)); 38 | 39 | exp = Expression.Lambda>(Expression.Block(typeof(int?), Expression.PreIncrementAssign(a), a), a); 40 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 41 | Assert.AreEqual(1, f(0)); 42 | Assert.AreEqual(0, f(-1)); 43 | Assert.AreEqual(int.MinValue, f(int.MaxValue)); 44 | Assert.IsNull(f(null)); 45 | } 46 | 47 | [Test] 48 | public void TestDouble() 49 | { 50 | ParameterExpression a = Expression.Parameter(typeof(double), "a"); 51 | Expression> exp = Expression.Lambda>(Expression.PreIncrementAssign(a), a); 52 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 53 | Assert.AreEqual(1, f(0)); 54 | Assert.AreEqual(0, f(-1)); 55 | Assert.AreEqual(0.5, f(-0.5)); 56 | 57 | exp = Expression.Lambda>(Expression.Block(typeof(double), Expression.PreIncrementAssign(a), a), a); 58 | f = LambdaCompiler.Compile(exp, CompilerOptions.All); 59 | Assert.AreEqual(1, f(0)); 60 | Assert.AreEqual(0, f(-1)); 61 | Assert.AreEqual(0.5, f(-0.5)); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/AssignTests/PreIncrementAssign/TestStaticMember.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.AssignTests.PreIncrementAssign 7 | { 8 | [TestFixture] 9 | public class TestStaticMember 10 | { 11 | [Test] 12 | public void TestIntProp() 13 | { 14 | Expression> exp = Expression.Lambda>(Expression.PreIncrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("IntProp")))); 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 16 | TestClassA.IntProp = 0; 17 | Assert.AreEqual(1, f()); 18 | Assert.AreEqual(1, TestClassA.IntProp); 19 | TestClassA.IntProp = -1; 20 | Assert.AreEqual(0, f()); 21 | Assert.AreEqual(0, TestClassA.IntProp); 22 | TestClassA.IntProp = int.MaxValue; 23 | Assert.AreEqual(int.MinValue, f()); 24 | Assert.AreEqual(int.MinValue, TestClassA.IntProp); 25 | } 26 | 27 | [Test] 28 | public void TestDoubleField() 29 | { 30 | Expression> exp = Expression.Lambda>(Expression.PreIncrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetField("DoubleField")))); 31 | var f = LambdaCompiler.Compile(exp, CompilerOptions.CheckNullReferences); 32 | TestClassA.DoubleField = 0; 33 | Assert.AreEqual(1, f()); 34 | Assert.AreEqual(1, TestClassA.DoubleField); 35 | TestClassA.DoubleField = -1; 36 | Assert.AreEqual(0, f()); 37 | Assert.AreEqual(0, TestClassA.DoubleField); 38 | TestClassA.DoubleField = -0.5; 39 | Assert.AreEqual(0.5, f()); 40 | Assert.AreEqual(0.5, TestClassA.DoubleField); 41 | } 42 | 43 | [Test] 44 | public void TestNullable() 45 | { 46 | Expression> exp = Expression.Lambda>(Expression.PreIncrementAssign(Expression.MakeMemberAccess(null, typeof(TestClassA).GetProperty("NullableIntProp")))); 47 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 48 | TestClassA.NullableIntProp = 0; 49 | Assert.AreEqual(1, f()); 50 | Assert.AreEqual(1, TestClassA.NullableIntProp); 51 | TestClassA.NullableIntProp = -1; 52 | Assert.AreEqual(0, f()); 53 | Assert.AreEqual(0, TestClassA.NullableIntProp); 54 | TestClassA.NullableIntProp = int.MaxValue; 55 | Assert.AreEqual(int.MinValue, f()); 56 | Assert.AreEqual(int.MinValue, TestClassA.NullableIntProp); 57 | TestClassA.NullableIntProp = null; 58 | Assert.IsNull(f()); 59 | Assert.IsNull(TestClassA.NullableIntProp); 60 | } 61 | 62 | public class TestClassA 63 | { 64 | public static int IntProp { get; set; } 65 | public static int? NullableIntProp { get; set; } 66 | public static double DoubleField; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ExtensionTests/ForEachExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | namespace GrobExp.Compiler.Tests.ExtensionTests 8 | { 9 | public class ForEachExpression : Expression 10 | { 11 | public ForEachExpression(Expression enumerable, Type elementType, LambdaExpression body) 12 | { 13 | Enumerable = enumerable; 14 | ElementType = elementType; 15 | Body = body; 16 | } 17 | 18 | public override Expression Reduce() 19 | { 20 | Type enumeratorType = typeof(IEnumerator<>).MakeGenericType(ElementType); 21 | ParameterExpression enumerator = Parameter(enumeratorType, "enumerator"); 22 | MethodInfo getEnumeratorMethod = typeof(IEnumerable<>).MakeGenericType(ElementType).GetMethod("GetEnumerator"); 23 | MethodInfo resetMethod = typeof(IEnumerator).GetMethod("Reset"); 24 | MethodInfo moveNextMethod = typeof(IEnumerator).GetMethod("MoveNext"); 25 | PropertyInfo currentProperty = enumeratorType.GetProperty("Current"); 26 | LabelTarget breakLabel = Label(); 27 | return Block( 28 | new[] {enumerator}, 29 | Assign(enumerator, Call(Enumerable, getEnumeratorMethod)), 30 | Call(enumerator, resetMethod), 31 | Loop( 32 | Block( 33 | IfThen(Not(Call(enumerator, moveNextMethod)), Break(breakLabel)), 34 | Invoke(Body, Call(enumerator, currentProperty.GetGetMethod())) 35 | ), 36 | breakLabel 37 | ) 38 | ); 39 | } 40 | 41 | public override string ToString() 42 | { 43 | return base.ToString(); 44 | } 45 | 46 | public Expression Enumerable { get; private set; } 47 | public Type ElementType { get; set; } 48 | public LambdaExpression Body { get; private set; } 49 | 50 | public override ExpressionType NodeType { get { return ExpressionType.Extension; } } 51 | 52 | public override Type Type { get { return typeof(void); } } 53 | 54 | public override bool CanReduce { get { return true; } } 55 | 56 | protected override Expression VisitChildren(ExpressionVisitor visitor) 57 | { 58 | return base.VisitChildren(visitor); 59 | } 60 | 61 | protected override Expression Accept(ExpressionVisitor visitor) 62 | { 63 | return base.Accept(visitor); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ExtensionTests/TestForEach.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests.ExtensionTests 7 | { 8 | [TestFixture] 9 | public class TestForEach 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | ParameterExpression array = Expression.Parameter(typeof(int[]), "array"); 15 | ParameterExpression result = Expression.Parameter(typeof(int), "result"); 16 | ParameterExpression item = Expression.Parameter(typeof(int), "item"); 17 | BlockExpression block = Expression.Block( 18 | new[] {result}, 19 | Expression.Assign(result, Expression.Constant(0)), 20 | ForEach( 21 | array, typeof(int), 22 | Expression.Lambda( 23 | Expression.IfThen( 24 | Expression.GreaterThan(item, Expression.Constant(0)), 25 | Expression.AddAssign(result, item)), 26 | item) 27 | ), 28 | result); 29 | Expression> exp = Expression.Lambda>(block, array); 30 | 31 | Func f = LambdaCompiler.Compile(exp, CompilerOptions.All); 32 | Assert.AreEqual(6, f(new[] {1, -1, 2, -2, 3, -3})); 33 | Assert.AreEqual(6, f(new[] {1, 2, 3})); 34 | Assert.AreEqual(0, f(new[] {-1, -2, -3})); 35 | 36 | f = LambdaCompiler.Compile(exp, CompilerOptions.None); 37 | Assert.AreEqual(6, f(new[] {1, -1, 2, -2, 3, -3})); 38 | Assert.AreEqual(6, f(new[] {1, 2, 3})); 39 | Assert.AreEqual(0, f(new[] {-1, -2, -3})); 40 | 41 | f = exp.Compile(); 42 | Assert.AreEqual(6, f(new[] {1, -1, 2, -2, 3, -3})); 43 | Assert.AreEqual(6, f(new[] {1, 2, 3})); 44 | Assert.AreEqual(0, f(new[] {-1, -2, -3})); 45 | } 46 | 47 | public static ForEachExpression ForEach(Expression enumerable, Type elementType, LambdaExpression body) 48 | { 49 | return new ForEachExpression(enumerable, elementType, body); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/GrobExp.Compiler.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | false 5 | net45;netcoreapp3.1;net6.0 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/ParameterReplacer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | 3 | namespace GrobExp.Compiler.Tests 4 | { 5 | public class ParameterReplacer : ExpressionVisitor 6 | { 7 | public ParameterReplacer(ParameterExpression from, ParameterExpression to) 8 | { 9 | this.to = to; 10 | this.from = from; 11 | } 12 | 13 | protected override Expression VisitParameter(ParameterExpression node) 14 | { 15 | return node == from ? to : base.VisitParameter(node); 16 | } 17 | 18 | private readonly ParameterExpression to; 19 | private readonly ParameterExpression from; 20 | } 21 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestArrayLength.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestArrayLength 10 | { 11 | [Test] 12 | public void Test() 13 | { 14 | Expression> exp = a => a.ArrayB.Length; 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 16 | Assert.AreEqual(0, f(null)); 17 | Assert.AreEqual(0, f(new TestClassA())); 18 | Assert.AreEqual(1, f(new TestClassA {ArrayB = new TestClassB[1]})); 19 | } 20 | 21 | public class TestClassA 22 | { 23 | public string S { get; set; } 24 | public int Y { get; set; } 25 | public TestClassB[] ArrayB { get; set; } 26 | } 27 | 28 | public class TestClassB 29 | { 30 | public string S { get; set; } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | 7 | using GrEmit; 8 | 9 | namespace GrobExp.Compiler.Tests 10 | { 11 | public class TestBase 12 | { 13 | protected TDelegate Compile(Expression lambda, CompilerOptions options) where TDelegate : class 14 | { 15 | return LambdaCompiler.Compile(lambda, options); 16 | } 17 | 18 | protected TDelegate CompileToMethod(Expression lambda, CompilerOptions options) where TDelegate : class 19 | { 20 | var typeBuilder = TestPerformance.Module.DefineType(Guid.NewGuid().ToString(), TypeAttributes.Public); 21 | var method = typeBuilder.DefineMethod("lambda", MethodAttributes.Public | MethodAttributes.Static, lambda.ReturnType, lambda.Parameters.Select(parameter => parameter.Type).ToArray()); 22 | LambdaCompiler.CompileToMethod(lambda, method, options); 23 | var type = typeBuilder.CreateType(); 24 | var dynamicMethod = new DynamicMethod(Guid.NewGuid().ToString(), MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof(TDelegate), null, TestPerformance.Module, true); 25 | using (var il = new GroboIL(dynamicMethod)) 26 | { 27 | il.Ldnull(); 28 | il.Ldftn(type.GetMethod("lambda")); 29 | il.Newobj(typeof(TDelegate).GetConstructor(new[] {typeof(object), typeof(IntPtr)})); 30 | il.Ret(); 31 | } 32 | 33 | return ((Func)dynamicMethod.CreateDelegate(typeof(Func)))(); 34 | } 35 | 36 | protected static string SelectValue(string netcoreValue, string net45Value) 37 | { 38 | #if NETCOREAPP 39 | return netcoreValue; 40 | #else 41 | return net45Value; 42 | #endif 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestConstant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestConstant 10 | { 11 | [Test] 12 | public void TestBool() 13 | { 14 | Expression> exp = Expression.Lambda>(Expression.Constant(true, typeof(bool))); 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 16 | Assert.AreEqual(true, f()); 17 | } 18 | 19 | [Test] 20 | public void TestByte() 21 | { 22 | Expression> exp = Expression.Lambda>(Expression.Constant((byte)250, typeof(byte))); 23 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 24 | Assert.AreEqual(250, f()); 25 | } 26 | 27 | [Test] 28 | public void TestSByte() 29 | { 30 | Expression> exp = Expression.Lambda>(Expression.Constant((sbyte)-123, typeof(sbyte))); 31 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 32 | Assert.AreEqual(-123, f()); 33 | } 34 | 35 | [Test] 36 | public void TestUShort() 37 | { 38 | Expression> exp = Expression.Lambda>(Expression.Constant((ushort)65500, typeof(ushort))); 39 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 40 | Assert.AreEqual(65500, f()); 41 | } 42 | 43 | [Test] 44 | public void TestShort() 45 | { 46 | Expression> exp = Expression.Lambda>(Expression.Constant((short)-32000, typeof(short))); 47 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 48 | Assert.AreEqual(-32000, f()); 49 | } 50 | 51 | [Test] 52 | public void TestUInt() 53 | { 54 | Expression> exp = Expression.Lambda>(Expression.Constant(3500000000, typeof(uint))); 55 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 56 | Assert.AreEqual(3500000000, f()); 57 | } 58 | 59 | [Test] 60 | public void TestInt() 61 | { 62 | Expression> exp = Expression.Lambda>(Expression.Constant(-2000000000, typeof(int))); 63 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 64 | Assert.AreEqual(-2000000000, f()); 65 | } 66 | 67 | [Test] 68 | public void TestULong() 69 | { 70 | Expression> exp = Expression.Lambda>(Expression.Constant(ulong.MaxValue, typeof(ulong))); 71 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 72 | Assert.AreEqual(ulong.MaxValue, f()); 73 | } 74 | 75 | [Test] 76 | public void TestLong() 77 | { 78 | Expression> exp = Expression.Lambda>(Expression.Constant(long.MinValue, typeof(long))); 79 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 80 | Assert.AreEqual(long.MinValue, f()); 81 | } 82 | 83 | [Test] 84 | public void TestFloat() 85 | { 86 | Expression> exp = Expression.Lambda>(Expression.Constant(float.Epsilon, typeof(float))); 87 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 88 | Assert.AreEqual(float.Epsilon, f()); 89 | } 90 | 91 | [Test] 92 | public void TestDouble() 93 | { 94 | Expression> exp = Expression.Lambda>(Expression.Constant(double.Epsilon, typeof(double))); 95 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 96 | Assert.AreEqual(double.Epsilon, f()); 97 | } 98 | 99 | [Test] 100 | public void TestString() 101 | { 102 | Expression> exp = Expression.Lambda>(Expression.Constant("zzz", typeof(string))); 103 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 104 | Assert.AreEqual("zzz", f()); 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestDynamic.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using Microsoft.CSharp.RuntimeBinder; 5 | 6 | using NUnit.Framework; 7 | 8 | namespace GrobExp.Compiler.Tests 9 | { 10 | [TestFixture] 11 | public class TestDynamic 12 | { 13 | [Test] 14 | public void Test() 15 | { 16 | var x = Expression.Parameter(typeof(object), "x"); 17 | var y = Expression.Parameter(typeof(object), "y"); 18 | var binder = Binder.BinaryOperation( 19 | CSharpBinderFlags.None, ExpressionType.Add, typeof(TestDynamic), 20 | new[] 21 | { 22 | CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 23 | CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) 24 | }); 25 | 26 | var exp = Expression.Lambda>( 27 | Expression.Dynamic(binder, typeof(object), x, y), 28 | new[] {x, y} 29 | ); 30 | 31 | Func f = LambdaCompiler.Compile(exp, CompilerOptions.All); 32 | Assert.AreEqual(3, f(1, 2)); 33 | 34 | f = LambdaCompiler.Compile(exp, CompilerOptions.None); 35 | Assert.AreEqual(3, f(1, 2)); 36 | 37 | f = exp.Compile(); 38 | Assert.AreEqual(3, f(1, 2)); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestEqual.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestEqual 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | Expression> exp = (a, b) => a == b; 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 16 | Assert.AreEqual(false, f(0, 1)); 17 | Assert.AreEqual(true, f(1, 1)); 18 | } 19 | 20 | [Test] 21 | public void Test2() 22 | { 23 | Expression> exp = (a, b) => a == b; 24 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 25 | Assert.AreEqual(false, f(0, 1)); 26 | Assert.AreEqual(true, f(1, 1)); 27 | } 28 | 29 | [Test] 30 | public void Test3() 31 | { 32 | Expression> exp = (a, b) => a == b; 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | Assert.AreEqual(false, f(0, 1)); 35 | Assert.AreEqual(true, f(1, 1)); 36 | } 37 | 38 | [Test] 39 | public void Test4() 40 | { 41 | Expression> exp = (a, b) => a == b; 42 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 43 | Assert.AreEqual(false, f("zzz", "qxx")); 44 | Assert.AreEqual(true, f("zzz", "zzz")); 45 | } 46 | 47 | [Test] 48 | public void Test5() 49 | { 50 | Expression> exp = (a, b) => a == b; 51 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 52 | Assert.AreEqual(false, f(0, 1m)); 53 | Assert.AreEqual(true, f(1, 1m)); 54 | } 55 | 56 | [Test] 57 | public void Test6() 58 | { 59 | Expression> exp = (a, b) => a == b; 60 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 61 | Assert.AreEqual(false, f(TestEnum.Zero, TestEnum.One)); 62 | Assert.AreEqual(true, f(TestEnum.One, TestEnum.One)); 63 | } 64 | 65 | [Test] 66 | public void Test7() 67 | { 68 | ParameterExpression a = Expression.Parameter(typeof(TestEnum)); 69 | ParameterExpression b = Expression.Parameter(typeof(TestEnum)); 70 | Expression body = Expression.Equal(a, b); 71 | Expression> exp = Expression.Lambda>(body, a, b); 72 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 73 | Assert.AreEqual(false, f(TestEnum.Zero, TestEnum.One)); 74 | Assert.AreEqual(true, f(TestEnum.One, TestEnum.One)); 75 | } 76 | 77 | [Test] 78 | public void Test8() 79 | { 80 | ParameterExpression a = Expression.Parameter(typeof(int?)); 81 | Expression body = Expression.Equal(a, Expression.Constant(null)); 82 | Expression> exp = Expression.Lambda>(body, a); 83 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 84 | Assert.AreEqual(false, f(1)); 85 | Assert.AreEqual(true, f(null)); 86 | } 87 | 88 | private enum TestEnum 89 | { 90 | Zero = 0, 91 | One = 1, 92 | Two = 2, 93 | Three = 3, 94 | Four = 4, 95 | Five = 5, 96 | Six = 6, 97 | Seven = 7, 98 | Eight = 8, 99 | Nine = 9, 100 | Ten = 10, 101 | Eleven = 11, 102 | Twelve = 12 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestExtensionMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | using NUnit.Framework; 6 | 7 | namespace GrobExp.Compiler.Tests 8 | { 9 | [TestFixture] 10 | public class TestExtensionMethod 11 | { 12 | [Test] 13 | public void TestEnum() 14 | { 15 | Expression> exp = zerg => zerg.Flies(); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.IsTrue(f(Zerg.Mutalisk)); 18 | Assert.IsFalse(f(Zerg.Zergling)); 19 | } 20 | 21 | [Test] 22 | public void TestNullable() 23 | { 24 | Expression> exp = zerg => zerg.AttacksAir(); 25 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 26 | Assert.IsTrue(f(Zerg.Mutalisk)); 27 | Assert.IsFalse(f(Zerg.Zergling)); 28 | Assert.IsFalse(f(null)); 29 | } 30 | 31 | [Test] 32 | public void TestLinq1() 33 | { 34 | Expression> exp = ints => ints.All(i => i > 0); 35 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 36 | Assert.IsTrue(f(null)); 37 | Assert.IsTrue(f(new int[0])); 38 | Assert.IsTrue(f(new[] {1})); 39 | Assert.IsFalse(f(new[] {-1})); 40 | } 41 | 42 | [Test] 43 | public void TestLinq2() 44 | { 45 | Expression> exp = ints => ints.Select(i => i * i).FirstOrDefault(); 46 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 47 | Assert.AreEqual(0, f(null)); 48 | Assert.AreEqual(0, f(new int[0])); 49 | Assert.AreEqual(9, f(new[] {3})); 50 | } 51 | 52 | /// 53 | /// Test validates that null reference check not applied to the first argument of an extension method 54 | /// 55 | [Test] 56 | public void TestNullTargetArgument() 57 | { 58 | Expression> exp = zurg => zurg.IsNull(); 59 | Func compiledExp = LambdaCompiler.Compile(exp, CompilerOptions.All); 60 | Assert.That(compiledExp(null), Is.EqualTo(true)); 61 | Assert.That(compiledExp(new Zurg()), Is.EqualTo(false)); 62 | } 63 | 64 | [Test] 65 | public void TestRefParameters() 66 | { 67 | Expression> exp = (o, i, j) => o.Nonsense(ref i, j) ? -1 : j; 68 | Func compiledExp = LambdaCompiler.Compile(exp, CompilerOptions.All); 69 | Assert.That(compiledExp(new Zurg {Length = 1}, 1, 2), Is.EqualTo(2)); 70 | } 71 | } 72 | 73 | public enum Zerg 74 | { 75 | Drone = 1, 76 | Overlord = 2, 77 | Zergling = 3, 78 | Hydralisk = 4, 79 | Mutalisk = 5, 80 | Ultralisk = 6, 81 | Guardian = 7, 82 | Devourer = 8, 83 | Lurker = 9 84 | } 85 | 86 | public class Zurg 87 | { 88 | public int Length { get; set; } 89 | } 90 | 91 | public static class ZergExtensions 92 | { 93 | public static bool Flies(this Zerg zerg) 94 | { 95 | return zerg == Zerg.Overlord || zerg == Zerg.Mutalisk || zerg == Zerg.Guardian || zerg == Zerg.Devourer; 96 | } 97 | 98 | public static bool AttacksAir(this Zerg? zerg) 99 | { 100 | return zerg == Zerg.Hydralisk || zerg == Zerg.Mutalisk || zerg == Zerg.Devourer; 101 | } 102 | } 103 | 104 | public static class ZurgExtensions 105 | { 106 | public static bool IsNull(this Zurg zurg) 107 | { 108 | return zurg == null; 109 | } 110 | 111 | public static bool Nonsense(this Zurg zurg, ref int a, int b) 112 | { 113 | a = zurg.Length; 114 | return a == b; 115 | } 116 | } 117 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestInvoke.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestInvoke 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | Func func = (a, b) => a + b; 15 | Expression> exp = (a, b) => func(a, b); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.AreEqual(3, f(1, 2)); 18 | } 19 | 20 | [Test] 21 | public void Test2() 22 | { 23 | Expression> lambda = (a, b) => a + b; 24 | ParameterExpression parameterA = Expression.Parameter(typeof(int)); 25 | ParameterExpression parameterB = Expression.Parameter(typeof(int)); 26 | Expression> exp = Expression.Lambda>(Expression.Invoke(lambda, parameterA, parameterB), parameterA, parameterB); 27 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 28 | Assert.AreEqual(3, f(1, 2)); 29 | } 30 | 31 | [Test] 32 | public void TestWithLabels1() 33 | { 34 | var target = Expression.Label(typeof(int)); 35 | Expression body = Expression.Block(Expression.Goto(target, Expression.Constant(1)), Expression.Label(target, Expression.Constant(2))); 36 | Expression> lambda = Expression.Lambda>(body); 37 | Expression> exp = Expression.Lambda>(Expression.Block(Expression.Invoke(lambda), Expression.Invoke(lambda))); 38 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 39 | Assert.AreEqual(1, f()); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestLabels.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestLabels 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | var target = Expression.Label(typeof(int)); 15 | Expression body = Expression.Block(Expression.Goto(target, Expression.Constant(1)), Expression.Label(target, Expression.Constant(2))); 16 | Expression> exp = Expression.Lambda>(body); 17 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 18 | Assert.AreEqual(1, f()); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestListInit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq.Expressions; 5 | 6 | using NUnit.Framework; 7 | 8 | namespace GrobExp.Compiler.Tests 9 | { 10 | [TestFixture] 11 | public class TestListInit 12 | { 13 | [Test] 14 | public void TestList() 15 | { 16 | Expression>> exp = () => new List {1, 2}; 17 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 18 | var list = f(); 19 | Assert.IsNotNull(list); 20 | Assert.AreEqual(2, list.Count); 21 | Assert.AreEqual(1, list[0]); 22 | Assert.AreEqual(2, list[1]); 23 | } 24 | 25 | [Test] 26 | public void TestDictionary() 27 | { 28 | Expression>> exp = () => new Dictionary {{1, "zzz"}, {2, "qxx"}}; 29 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 30 | var dict = f(); 31 | Assert.IsNotNull(dict); 32 | Assert.AreEqual(2, dict.Count); 33 | Assert.AreEqual("zzz", dict[1]); 34 | Assert.AreEqual("qxx", dict[2]); 35 | } 36 | 37 | [Test] 38 | public void TestStruct() 39 | { 40 | Expression> exp = () => new TestStructA(3) {1, 2}; 41 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 42 | var list = f(); 43 | Assert.IsNotNull(list); 44 | Assert.AreEqual(2, list.Count); 45 | Assert.AreEqual(1, list[0]); 46 | Assert.AreEqual(2, list[1]); 47 | } 48 | 49 | public struct TestStructA : IEnumerable 50 | { 51 | public TestStructA(int x) 52 | { 53 | list = new List(); 54 | } 55 | 56 | public IEnumerator GetEnumerator() 57 | { 58 | return list.GetEnumerator(); 59 | } 60 | 61 | IEnumerator IEnumerable.GetEnumerator() 62 | { 63 | return GetEnumerator(); 64 | } 65 | 66 | public void Add(int x) 67 | { 68 | list.Add(x); 69 | } 70 | 71 | public int Count { get { return list.Count; } } 72 | 73 | public int this[int index] { get { return list[index]; } set { list[index] = value; } } 74 | private readonly List list; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestMemberAccess.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestMemberAccess 10 | { 11 | [Test] 12 | public void TestPropertyAccess() 13 | { 14 | Expression> exp = o => o.S; 15 | Func compiledExp = LambdaCompiler.Compile(exp, CompilerOptions.All); 16 | Assert.That(compiledExp(null), Is.EqualTo(null)); 17 | Assert.That(compiledExp(new TestClassA {S = "zzz"}), Is.EqualTo("zzz")); 18 | } 19 | 20 | [Test] 21 | public void TestFieldAccess() 22 | { 23 | Expression> exp = o => o.Y; 24 | Func compiledExp = LambdaCompiler.Compile(exp, CompilerOptions.All); 25 | Assert.That(compiledExp(null), Is.EqualTo(0)); 26 | Assert.That(compiledExp(new TestClassA {Y = 1}), Is.EqualTo(1)); 27 | } 28 | 29 | public class TestClassA 30 | { 31 | public string S { get; set; } 32 | public int Y { get; set; } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestNotEqual.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestNotEqual 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | Expression> exp = (a, b) => a != b; 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 16 | Assert.AreEqual(true, f(0, 1)); 17 | Assert.AreEqual(false, f(1, 1)); 18 | } 19 | 20 | [Test] 21 | public void Test2() 22 | { 23 | Expression> exp = (a, b) => a != b; 24 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 25 | Assert.AreEqual(true, f(0, 1)); 26 | Assert.AreEqual(false, f(1, 1)); 27 | } 28 | 29 | [Test] 30 | public void Test3() 31 | { 32 | Expression> exp = (a, b) => a != b; 33 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 34 | Assert.AreEqual(true, f(0, 1)); 35 | Assert.AreEqual(false, f(1, 1)); 36 | } 37 | 38 | [Test] 39 | public void Test4() 40 | { 41 | Expression> exp = (a, b) => a != b; 42 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 43 | Assert.AreEqual(true, f("zzz", "qxx")); 44 | Assert.AreEqual(false, f("zzz", "zzz")); 45 | } 46 | 47 | [Test] 48 | public void Test5() 49 | { 50 | Expression> exp = (a, b) => a != b; 51 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 52 | Assert.AreEqual(true, f(0, 1m)); 53 | Assert.AreEqual(false, f(1, 1m)); 54 | } 55 | 56 | [Test] 57 | public void Test6() 58 | { 59 | ParameterExpression a = Expression.Parameter(typeof(int?)); 60 | Expression body = Expression.NotEqual(a, Expression.Constant(null)); 61 | Expression> exp = Expression.Lambda>(body, a); 62 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 63 | Assert.AreEqual(true, f(1)); 64 | Assert.AreEqual(false, f(null)); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestParameterExtractor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestParameterExtractor 10 | { 11 | [Test] 12 | public void ParameterExpression() 13 | { 14 | var parameter = Expression.Parameter(typeof(int)); 15 | 16 | Assert.That(extractor.Extract(parameter), Is.EqualTo(new object[] {parameter})); 17 | } 18 | 19 | [Test] 20 | public void TwoUsagesOfParameter() 21 | { 22 | var parameter = Expression.Parameter(typeof(int)); 23 | var sum = Expression.Add(parameter, parameter); 24 | 25 | Assert.That(extractor.Extract(sum), Is.EqualTo(new[] {parameter})); 26 | } 27 | 28 | [Test] 29 | public void TwoParametersOfDifferentTypes() 30 | { 31 | var intParameter = Expression.Parameter(typeof(int)); 32 | var doubleParameter = Expression.Parameter(typeof(double)); 33 | var sum = Expression.Add(doubleParameter, Expression.Convert(intParameter, typeof(double))); 34 | 35 | Assert.That(extractor.Extract(sum), Is.EqualTo(new[] {doubleParameter, intParameter})); 36 | } 37 | 38 | [Test] 39 | public void TwoParametersOfSameType() 40 | { 41 | var firstParameter = Expression.Parameter(typeof(int), "a"); 42 | var secondParameter = Expression.Parameter(typeof(int), "a"); 43 | 44 | var sum = Expression.Add(firstParameter, secondParameter); 45 | Assert.That(extractor.Extract(sum), Is.EqualTo(new[] {firstParameter, secondParameter})); 46 | } 47 | 48 | [Test] 49 | public void TwoVariablesOfSameType() 50 | { 51 | var firstParameter = Expression.Variable(typeof(int), "a"); 52 | var secondParameter = Expression.Variable(typeof(int), "a"); 53 | 54 | var sum = Expression.Add(firstParameter, secondParameter); 55 | Assert.That(extractor.Extract(sum), Is.EqualTo(new[] {firstParameter, secondParameter})); 56 | } 57 | 58 | [Test] 59 | public void IgnoreLocalVariables() 60 | { 61 | var firstParameter = Expression.Parameter(typeof(int)); 62 | var secondParameter = Expression.Parameter(typeof(int)); 63 | 64 | var variable = Expression.Variable(typeof(int)); 65 | var assign = Expression.Assign(variable, Expression.Constant(5)); 66 | 67 | var sum = Expression.Add(Expression.Add(variable, firstParameter), secondParameter); 68 | var block = Expression.Block(typeof(int), variables : new[] {variable}, expressions : new Expression[] {assign, sum}); 69 | 70 | Assert.That(extractor.Extract(block), Is.EqualTo(new[] {firstParameter, secondParameter})); 71 | } 72 | 73 | [Test] 74 | public void IgnoreNestedLambdaParameters() 75 | { 76 | var firstParameter = Expression.Parameter(typeof(int)); 77 | var secondParameter = Expression.Parameter(typeof(int)); 78 | 79 | var lambdaParameter = Expression.Variable(typeof(int)); 80 | var body = Expression.Multiply(Expression.Add(lambdaParameter, firstParameter), secondParameter); 81 | 82 | var lambda = Expression.Lambda(body, lambdaParameter); 83 | Assert.That(extractor.Extract(lambda), Is.EqualTo(new[] {firstParameter, secondParameter})); 84 | } 85 | 86 | [Test] 87 | public void ExtractFromPureLambda() 88 | { 89 | Expression> lambda = (a, b, s) => s + a.B.S + b.S + a.S; 90 | Assert.That(extractor.Extract(lambda.Body), Is.EqualTo(new[] {lambda.Parameters[2], lambda.Parameters[0], lambda.Parameters[1]})); 91 | } 92 | 93 | private readonly ParametersExtractor extractor = new ParametersExtractor(); 94 | 95 | private class A 96 | { 97 | public string S { get; set; } 98 | 99 | public B B { get; set; } 100 | } 101 | 102 | public class B 103 | { 104 | public string S { get; set; } 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestQuote.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | 6 | using NUnit.Framework; 7 | 8 | namespace GrobExp.Compiler.Tests 9 | { 10 | [TestFixture] 11 | public class TestQuote 12 | { 13 | [Test] 14 | public void Test1() 15 | { 16 | Expression> exp = i => F(j => j * j); 17 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 18 | Assert.AreEqual(25, f(2)); 19 | } 20 | 21 | [Test] 22 | public void Test2() 23 | { 24 | Expression> exp = i => F(j => j * i); 25 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 26 | Assert.AreEqual(10, f(2)); 27 | } 28 | 29 | [Test] 30 | public void Test3() 31 | { 32 | ParameterExpression x = Expression.Parameter(typeof(int)); 33 | ParameterExpression y = Expression.Parameter(typeof(int)); 34 | Expression body = Expression.Call(typeof(TestQuote).GetMethod("F2", BindingFlags.Public | BindingFlags.Static), Expression.Quote(Expression.Lambda>(Expression.RuntimeVariables(x, y), y))); 35 | Expression> exp = Expression.Lambda>(body, x); 36 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 37 | Assert.AreEqual(10, f(10)); 38 | } 39 | 40 | public static int F(Expression> exp) 41 | { 42 | return LambdaCompiler.Compile(exp, CompilerOptions.All)(5); 43 | } 44 | 45 | public static int F2(Expression> exp) 46 | { 47 | return (int)LambdaCompiler.Compile(exp, CompilerOptions.All)(5)[0]; 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestRuntimeVariables.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Runtime.CompilerServices; 4 | 5 | using NUnit.Framework; 6 | 7 | namespace GrobExp.Compiler.Tests 8 | { 9 | public class TestRuntimeVariables : TestBase 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | ParameterExpression a = Expression.Parameter(typeof(int)); 15 | ParameterExpression b = Expression.Parameter(typeof(int)); 16 | Expression> exp = 17 | Expression.Lambda>( 18 | Expression.Block( 19 | Expression.AddAssign(a, b), 20 | Expression.RuntimeVariables(a, b) 21 | ), a, b); 22 | var f = Compile(exp, CompilerOptions.All); 23 | Assert.AreEqual(3, f(1, 2)[0]); 24 | Assert.AreEqual(2, f(1, 2)[1]); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestThrow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestThrow 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | Expression exp = Expression.Lambda(Expression.Throw(Expression.Constant(new Exception()))); 15 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 16 | Assert.Throws(() => f()); 17 | } 18 | 19 | [Test] 20 | public void Test2() 21 | { 22 | Expression exp = Expression.Lambda(Expression.Throw(Expression.New(typeof(Exception)))); 23 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 24 | Assert.Throws(() => f()); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestTypeAs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestTypeAs 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | var parameter = Expression.Parameter(typeof(int)); 15 | var exp = Expression.Lambda>(Expression.TypeAs(parameter, typeof(double?)), parameter); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.IsNull(f(5)); 18 | } 19 | 20 | [Test] 21 | public void Test2() 22 | { 23 | var parameter = Expression.Parameter(typeof(int)); 24 | var exp = Expression.Lambda>(Expression.TypeAs(parameter, typeof(int?)), parameter); 25 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 26 | int? x = f(5); 27 | Console.WriteLine(x.GetType()); 28 | Assert.AreEqual(5, f(5)); 29 | } 30 | 31 | [Test] 32 | public void Test3() 33 | { 34 | var parameter = Expression.Parameter(typeof(int)); 35 | var exp = Expression.Lambda>(Expression.TypeAs(parameter, typeof(object)), parameter); 36 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 37 | object res = f(5); 38 | Assert.AreEqual(5, res); 39 | } 40 | 41 | [Test] 42 | public void Test4() 43 | { 44 | var parameter = Expression.Parameter(typeof(TestClassB)); 45 | var exp = Expression.Lambda>(Expression.TypeAs(parameter, typeof(TestClassA)), parameter); 46 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 47 | var b = new TestClassB(); 48 | TestClassA a = f(b); 49 | Assert.AreEqual(b, a); 50 | } 51 | 52 | [Test] 53 | public void Test5() 54 | { 55 | var parameter = Expression.Parameter(typeof(TestClassA)); 56 | var exp = Expression.Lambda>(Expression.TypeAs(parameter, typeof(TestClassB)), parameter); 57 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 58 | var a = new TestClassA(); 59 | TestClassB b = f(a); 60 | Assert.IsNull(b); 61 | b = new TestClassB(); 62 | var bb = f(b); 63 | Assert.AreEqual(b, bb); 64 | } 65 | 66 | [Test] 67 | public void Test6() 68 | { 69 | var parameter = Expression.Parameter(typeof(object)); 70 | var exp = Expression.Lambda>(Expression.TypeAs(parameter, typeof(int?)), parameter); 71 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 72 | Assert.AreEqual(5, f(5)); 73 | Assert.IsNull(f(5.5)); 74 | } 75 | 76 | [Test] 77 | public void Test7() 78 | { 79 | var parameter = Expression.Parameter(typeof(object)); 80 | var exp = Expression.Lambda>(Expression.TypeAs(parameter, typeof(TestEnum?)), parameter); 81 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 82 | Assert.AreEqual(TestEnum.One, f(TestEnum.One)); 83 | Assert.IsNull(f(5.5)); 84 | } 85 | 86 | [Test] 87 | public void Test8() 88 | { 89 | var parameter = Expression.Parameter(typeof(TestEnum)); 90 | var exp = Expression.Lambda>(Expression.TypeAs(parameter, typeof(Enum)), parameter); 91 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 92 | Enum actual = f(TestEnum.One); 93 | Assert.AreEqual(TestEnum.One, actual); 94 | } 95 | 96 | public enum TestEnum 97 | { 98 | One, 99 | Two 100 | } 101 | 102 | public class TestClassA 103 | { 104 | } 105 | 106 | public class TestClassB : TestClassA 107 | { 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestTypeEqual.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestTypeEqual 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | var parameter = Expression.Parameter(typeof(int)); 15 | var exp = Expression.Lambda>(Expression.TypeEqual(parameter, typeof(double)), parameter); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.IsFalse(f(5)); 18 | } 19 | 20 | [Test] 21 | public void Test2() 22 | { 23 | var parameter = Expression.Parameter(typeof(int)); 24 | var exp = Expression.Lambda>(Expression.TypeEqual(parameter, typeof(int)), parameter); 25 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 26 | Assert.IsTrue(f(5)); 27 | } 28 | 29 | [Test] 30 | public void Test3() 31 | { 32 | var parameter = Expression.Parameter(typeof(int)); 33 | var exp = Expression.Lambda>(Expression.TypeEqual(parameter, typeof(object)), parameter); 34 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 35 | Assert.IsFalse(f(5)); 36 | } 37 | 38 | [Test] 39 | public void Test4() 40 | { 41 | var parameter = Expression.Parameter(typeof(TestClassB)); 42 | var exp = Expression.Lambda>(Expression.TypeEqual(parameter, typeof(TestClassA)), parameter); 43 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 44 | Assert.IsFalse(f(new TestClassB())); 45 | } 46 | 47 | [Test] 48 | public void Test5() 49 | { 50 | var parameter = Expression.Parameter(typeof(TestClassA)); 51 | var exp = Expression.Lambda>(Expression.TypeEqual(parameter, typeof(TestClassB)), parameter); 52 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 53 | Assert.IsFalse(f(new TestClassA())); 54 | Assert.IsTrue(f(new TestClassB())); 55 | } 56 | 57 | [Test] 58 | public void Test6() 59 | { 60 | var parameter = Expression.Parameter(typeof(object)); 61 | var exp = Expression.Lambda>(Expression.TypeEqual(parameter, typeof(int)), parameter); 62 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 63 | Assert.IsTrue(f(5)); 64 | Assert.IsFalse(f(5.5)); 65 | } 66 | 67 | [Test] 68 | public void Test7() 69 | { 70 | var parameter = Expression.Parameter(typeof(object)); 71 | var exp = Expression.Lambda>(Expression.TypeEqual(parameter, typeof(TestEnum)), parameter); 72 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 73 | Assert.IsTrue(f(TestEnum.One)); 74 | Assert.IsFalse(f(5.5)); 75 | } 76 | 77 | [Test] 78 | public void Test8() 79 | { 80 | var parameter = Expression.Parameter(typeof(TestEnum)); 81 | var exp = Expression.Lambda>(Expression.TypeEqual(parameter, typeof(Enum)), parameter); 82 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 83 | Assert.IsFalse(f(TestEnum.One)); 84 | } 85 | 86 | private enum TestEnum 87 | { 88 | One, 89 | Two 90 | } 91 | 92 | public class TestClassA 93 | { 94 | } 95 | 96 | public class TestClassB : TestClassA 97 | { 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestTypeIs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestTypeIs 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | var parameter = Expression.Parameter(typeof(int)); 15 | var exp = Expression.Lambda>(Expression.TypeIs(parameter, typeof(double)), parameter); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.IsFalse(f(5)); 18 | } 19 | 20 | [Test] 21 | public void Test2() 22 | { 23 | var parameter = Expression.Parameter(typeof(int)); 24 | var exp = Expression.Lambda>(Expression.TypeIs(parameter, typeof(int)), parameter); 25 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 26 | Assert.IsTrue(f(5)); 27 | } 28 | 29 | [Test] 30 | public void Test3() 31 | { 32 | var parameter = Expression.Parameter(typeof(int)); 33 | var exp = Expression.Lambda>(Expression.TypeIs(parameter, typeof(object)), parameter); 34 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 35 | Assert.IsTrue(f(5)); 36 | } 37 | 38 | [Test] 39 | public void Test4() 40 | { 41 | var parameter = Expression.Parameter(typeof(TestClassB)); 42 | var exp = Expression.Lambda>(Expression.TypeIs(parameter, typeof(TestClassA)), parameter); 43 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 44 | Assert.IsTrue(f(new TestClassB())); 45 | } 46 | 47 | [Test] 48 | public void Test5() 49 | { 50 | var parameter = Expression.Parameter(typeof(TestClassA)); 51 | var exp = Expression.Lambda>(Expression.TypeIs(parameter, typeof(TestClassB)), parameter); 52 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 53 | Assert.IsFalse(f(new TestClassA())); 54 | Assert.IsTrue(f(new TestClassB())); 55 | } 56 | 57 | [Test] 58 | public void Test6() 59 | { 60 | var parameter = Expression.Parameter(typeof(object)); 61 | var exp = Expression.Lambda>(Expression.TypeIs(parameter, typeof(int)), parameter); 62 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 63 | Assert.IsTrue(f(5)); 64 | Assert.IsFalse(f(5.5)); 65 | } 66 | 67 | [Test] 68 | public void Test7() 69 | { 70 | var parameter = Expression.Parameter(typeof(object)); 71 | var exp = Expression.Lambda>(Expression.TypeIs(parameter, typeof(TestEnum)), parameter); 72 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 73 | Assert.IsTrue(f(TestEnum.One)); 74 | Assert.IsFalse(f(5.5)); 75 | } 76 | 77 | [Test] 78 | public void Test8() 79 | { 80 | var parameter = Expression.Parameter(typeof(TestEnum)); 81 | var exp = Expression.Lambda>(Expression.TypeIs(parameter, typeof(Enum)), parameter); 82 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 83 | Assert.IsTrue(f(TestEnum.One)); 84 | } 85 | 86 | public enum TestEnum 87 | { 88 | One, 89 | Two 90 | } 91 | 92 | public class TestClassA 93 | { 94 | } 95 | 96 | public class TestClassB : TestClassA 97 | { 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.Tests/TestUnbox.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace GrobExp.Compiler.Tests 7 | { 8 | [TestFixture] 9 | public class TestUnbox 10 | { 11 | [Test] 12 | public void Test1() 13 | { 14 | ParameterExpression parameter = Expression.Parameter(typeof(object)); 15 | Expression> exp = Expression.Lambda>(Expression.Unbox(parameter, typeof(int)), parameter); 16 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 17 | Assert.AreEqual(-1, f(-1)); 18 | Assert.AreEqual(1, f(1)); 19 | Assert.AreEqual(0, f(null)); 20 | f = LambdaCompiler.Compile(exp, CompilerOptions.None); 21 | Assert.AreEqual(-1, f(-1)); 22 | Assert.AreEqual(1, f(1)); 23 | Assert.Throws(() => f(null)); 24 | } 25 | 26 | [Test] 27 | public void Test2() 28 | { 29 | ParameterExpression parameter = Expression.Parameter(typeof(TestClassA)); 30 | Expression> exp = Expression.Lambda>(Expression.Unbox(Expression.MakeMemberAccess(parameter, typeof(TestClassA).GetField("X")), typeof(int)), parameter); 31 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 32 | Assert.AreEqual(-1, f(new TestClassA {X = -1})); 33 | Assert.AreEqual(1, f(new TestClassA {X = 1})); 34 | Assert.AreEqual(0, f(null)); 35 | Assert.AreEqual(0, f(new TestClassA())); 36 | f = LambdaCompiler.Compile(exp, CompilerOptions.None); 37 | Assert.AreEqual(-1, f(new TestClassA {X = -1})); 38 | Assert.AreEqual(1, f(new TestClassA {X = 1})); 39 | Assert.Throws(() => f(new TestClassA())); 40 | Assert.Throws(() => f(null)); 41 | } 42 | 43 | [Test] 44 | public void Test3() 45 | { 46 | ParameterExpression parameter = Expression.Parameter(typeof(object)); 47 | Expression> exp = Expression.Lambda>(Expression.Unbox(parameter, typeof(TestEnum)), parameter); 48 | var f = LambdaCompiler.Compile(exp, CompilerOptions.All); 49 | Assert.AreEqual(TestEnum.One, f(TestEnum.One)); 50 | Assert.AreEqual(TestEnum.Two, f(TestEnum.Two)); 51 | Assert.AreEqual(TestEnum.Zero, f(null)); 52 | f = LambdaCompiler.Compile(exp, CompilerOptions.None); 53 | Assert.AreEqual(TestEnum.One, f(TestEnum.One)); 54 | Assert.AreEqual(TestEnum.Two, f(TestEnum.Two)); 55 | Assert.Throws(() => f(null)); 56 | } 57 | 58 | public enum TestEnum 59 | { 60 | Zero, 61 | One, 62 | Two 63 | } 64 | 65 | public class TestClassA 66 | { 67 | public object X; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /GrobExp.Compiler.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2010 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GrobExp.Compiler", "GrobExp.Compiler\GrobExp.Compiler.csproj", "{D6849FE1-E358-4CB6-86B9-59D079D3D75E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GrobExp.Compiler.Tests", "GrobExp.Compiler.Tests\GrobExp.Compiler.Tests.csproj", "{E0D8A77D-638E-4553-B7A9-26D3D5389FB6}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {D6849FE1-E358-4CB6-86B9-59D079D3D75E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {D6849FE1-E358-4CB6-86B9-59D079D3D75E}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {D6849FE1-E358-4CB6-86B9-59D079D3D75E}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {D6849FE1-E358-4CB6-86B9-59D079D3D75E}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {E0D8A77D-638E-4553-B7A9-26D3D5389FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E0D8A77D-638E-4553-B7A9-26D3D5389FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E0D8A77D-638E-4553-B7A9-26D3D5389FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E0D8A77D-638E-4553-B7A9-26D3D5389FB6}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {8FF910CC-E8CB-4FC8-B2E4-24DC9A85A464} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /GrobExp.Compiler.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | C:\projects\open-source\skbkontur\GrobExp.Compiler\Common.DotSettings 3 | ..\Common.DotSettings 4 | True 5 | True 6 | 1 7 | LongRunning 8 | True -------------------------------------------------------------------------------- /GrobExp.Compiler/Closures/ClosureSubstituter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | 4 | namespace GrobExp.Compiler.Closures 5 | { 6 | internal class ClosureSubstituter : ExpressionVisitor 7 | { 8 | public ClosureSubstituter(ParameterExpression closure, IClosureBuilder closureBuilder, Dictionary parameters) 9 | { 10 | this.closure = closure; 11 | this.closureBuilder = closureBuilder; 12 | this.parameters = parameters; 13 | } 14 | 15 | protected override Expression VisitParameter(ParameterExpression node) 16 | { 17 | int field; 18 | return parameters.TryGetValue(node, out field) 19 | ? closureBuilder.MakeAccess(closure, field) 20 | : base.VisitParameter(node); 21 | } 22 | 23 | private readonly ParameterExpression closure; 24 | private readonly IClosureBuilder closureBuilder; 25 | private readonly Dictionary parameters; 26 | } 27 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/Closures/DynamicClosureBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | using System.Runtime.CompilerServices; 8 | using System.Threading; 9 | 10 | using GrEmit.Utils; 11 | 12 | namespace GrobExp.Compiler.Closures 13 | { 14 | internal class DynamicClosureBuilder : IClosureBuilder 15 | { 16 | public DynamicClosureBuilder(ModuleBuilder module) 17 | { 18 | this.module = module; 19 | } 20 | 21 | public Type Create() 22 | { 23 | return typeBuilder == null ? typeof(object) : typeBuilder.CreateTypeInfo(); 24 | } 25 | 26 | public Expression MakeAccess(ParameterExpression root, int id) 27 | { 28 | var field = fields[id]; 29 | var fieldInfo = root.Type.GetField(field.Name); 30 | var result = Expression.Field(root, fieldInfo); 31 | if (field.IsPrivateType) 32 | result = Expression.Field(result, fieldInfo.FieldType.GetField("Value", BindingFlags.Public | BindingFlags.Instance)); 33 | return result; 34 | } 35 | 36 | public Expression Assign(ParameterExpression root, int id, Expression value) 37 | { 38 | var field = fields[id]; 39 | var fieldInfo = root.Type.GetField(field.Name); 40 | if (field.IsPrivateType) 41 | value = Expression.New(fieldInfo.FieldType.GetConstructor(new[] {value.Type}), value); 42 | return Expression.Assign(Expression.Field(root, fieldInfo), value); 43 | } 44 | 45 | public Expression Init(ParameterExpression root) 46 | { 47 | return Expression.Assign(root, Expression.New(root.Type)); 48 | } 49 | 50 | public int DefineField(Type type) 51 | { 52 | if (typeBuilder == null) 53 | { 54 | var id = (uint)Interlocked.Increment(ref closureId); 55 | typeBuilder = module.DefineType("Closure_" + id, TypeAttributes.Public | TypeAttributes.Class); 56 | } 57 | int fieldId = fields.Count; 58 | var fieldName = Formatter.Format(type) + "_" + fieldId; 59 | var isPrivateType = IsPrivate(type) && type.IsValueType; 60 | var fieldType = !isPrivateType ? type : typeof(StrongBox<>).MakeGenericType(type); 61 | typeBuilder.DefineField(fieldName, fieldType, FieldAttributes.Public); 62 | fields.Add(new Field 63 | { 64 | Name = fieldName, 65 | IsPrivateType = isPrivateType 66 | }); 67 | return fieldId; 68 | } 69 | 70 | public static bool IsPrivate(Type type) 71 | { 72 | // TODO check this method 73 | if (type.IsNestedPrivate || type.IsNotPublic) 74 | return true; 75 | return type.IsGenericType && type.GetGenericArguments().Any(IsPrivate); 76 | } 77 | 78 | private readonly ModuleBuilder module; 79 | private TypeBuilder typeBuilder; 80 | 81 | private readonly List fields = new List(); 82 | private static int closureId; 83 | 84 | private class Field 85 | { 86 | public string Name { get; set; } 87 | public bool IsPrivateType { get; set; } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/Closures/ExpressionQuoter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace GrobExp.Compiler.Closures 5 | { 6 | public class ExpressionQuoter : ExpressionVisitor 7 | { 8 | public ExpressionQuoter(object closure) 9 | { 10 | closureType = closure.GetType(); 11 | this.closure = Expression.Constant(closure, closureType); 12 | } 13 | 14 | protected override Expression VisitParameter(ParameterExpression node) 15 | { 16 | return node.Type == closureType ? closure : base.VisitParameter(node); 17 | } 18 | 19 | private readonly ConstantExpression closure; 20 | private readonly Type closureType; 21 | } 22 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/Closures/IClosureBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace GrobExp.Compiler.Closures 5 | { 6 | internal interface IClosureBuilder 7 | { 8 | int DefineField(Type type); 9 | Type Create(); 10 | Expression MakeAccess(ParameterExpression root, int id); 11 | Expression Assign(ParameterExpression root, int id, Expression value); 12 | Expression Init(ParameterExpression root); 13 | } 14 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/Closures/MethodInvokerBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | 8 | using GrEmit; 9 | using GrEmit.Utils; 10 | 11 | namespace GrobExp.Compiler.Closures 12 | { 13 | internal static class MethodInvokerBuilder 14 | { 15 | public static Delegate GetInvoker(MethodInfo method) 16 | { 17 | var result = (Delegate)hashtable[method]; 18 | if (result == null) 19 | { 20 | lock (lockObject) 21 | { 22 | result = (Delegate)hashtable[method]; 23 | if (result == null) 24 | hashtable[method] = result = BuildInvoker(method); 25 | } 26 | } 27 | return result; 28 | } 29 | 30 | private static Delegate BuildInvoker(MethodInfo method) 31 | { 32 | var parameterTypes = new List(); 33 | if (!method.IsStatic) 34 | parameterTypes.Add(method.ReflectedType); 35 | parameterTypes.AddRange(method.GetParameters().Select(p => p.ParameterType)); 36 | var prefix = "MethodInvoker"; 37 | if (method.IsStatic) 38 | prefix += "$" + Formatter.Format(method.DeclaringType); 39 | var dynamicMethod = new DynamicMethod(prefix + "$" + method.Name + "$" + Guid.NewGuid(), method.ReturnType, parameterTypes.ToArray(), typeof(MethodInvokerBuilder), true); 40 | using (var il = new GroboIL(dynamicMethod)) 41 | { 42 | for (var i = 0; i < parameterTypes.Count; ++i) 43 | il.Ldarg(i); 44 | il.Call(method); 45 | il.Ret(); 46 | } 47 | return dynamicMethod.CreateDelegate(Extensions.GetDelegateType(parameterTypes.ToArray(), method.ReturnType)); 48 | } 49 | 50 | private static readonly Hashtable hashtable = new Hashtable(); 51 | private static readonly object lockObject = new object(); 52 | } 53 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/Closures/RuntimeVariablesInliner.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Linq.Expressions; 3 | 4 | using GrobExp.Compiler.ExpressionEmitters; 5 | 6 | namespace GrobExp.Compiler.Closures 7 | { 8 | internal class RuntimeVariablesInliner : ExpressionVisitor 9 | { 10 | protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) 11 | { 12 | var constructor = typeof(RuntimeVariables).GetConstructor(new[] {typeof(object[])}); 13 | return Expression.New(constructor, Expression.NewArrayInit(typeof(object), node.Variables.Select(parameter => parameter.Type.IsValueType ? Expression.Convert(parameter, typeof(object)) : (Expression)parameter))); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/CompiledLambda.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | 6 | using GrobExp.Compiler.Closures; 7 | 8 | namespace GrobExp.Compiler 9 | { 10 | internal class CompiledLambda 11 | { 12 | public Delegate Delegate { get; set; } 13 | public MethodInfo Method { get; set; } 14 | public int Index { get; set; } 15 | } 16 | 17 | internal class ParsedLambda 18 | { 19 | public IClosureBuilder ClosureBuilder { get; set; } 20 | public Type ClosureType { get; set; } 21 | public ParameterExpression ClosureParameter { get; set; } 22 | public IClosureBuilder ConstantsBuilder { get; set; } 23 | public Type ConstantsType { get; set; } 24 | public ParameterExpression ConstantsParameter { get; set; } 25 | public object Constants { get; set; } 26 | public int DelegatesFieldId { get; set; } 27 | public Dictionary ParsedParameters { get; set; } 28 | public Dictionary ParsedConstants { get; set; } 29 | public Dictionary> ParsedSwitches { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/CompilerOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GrobExp.Compiler 4 | { 5 | [Flags] 6 | public enum CompilerOptions 7 | { 8 | None = 0, 9 | CheckNullReferences = 1, 10 | CheckArrayIndexes = 2, 11 | ExtendOnAssign = 4, 12 | UseTernaryLogic = 8, 13 | CheckDictionaryKeys = 16, 14 | CreateDynamicClosure = 32, 15 | All = CheckNullReferences | CheckArrayIndexes | ExtendOnAssign | UseTernaryLogic | CheckDictionaryKeys | CreateDynamicClosure 16 | } 17 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/ArrayLengthExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class ArrayLengthExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | var il = context.Il; 13 | Type arrayType; 14 | var result = ExpressionEmittersCollection.Emit(node.Operand, context, returnDefaultValueLabel, out arrayType); 15 | if (!arrayType.IsArray) 16 | throw new InvalidOperationException("Expected an array but was '" + arrayType + "'"); 17 | if (context.Options.HasFlag(CompilerOptions.CheckNullReferences)) 18 | { 19 | result = true; 20 | il.Dup(); 21 | il.Brfalse(returnDefaultValueLabel); 22 | } 23 | il.Ldlen(); 24 | resultType = typeof(int); 25 | return result; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/BinaryArithmeticOperationExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class BinaryArithmeticOperationExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | Expression left = node.Left; 13 | Expression right = node.Right; 14 | Type leftType, rightType; 15 | context.EmitLoadArgument(left, false, out leftType); 16 | context.EmitLoadArgument(right, false, out rightType); 17 | context.EmitArithmeticOperation(node.NodeType, node.Type, leftType, rightType, node.Method); 18 | resultType = node.Type; 19 | return false; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/BlockExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | using GrEmit; 6 | 7 | namespace GrobExp.Compiler.ExpressionEmitters 8 | { 9 | internal class BlockExpressionEmitter : ExpressionEmitter 10 | { 11 | protected override bool EmitInternal(BlockExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 12 | { 13 | var variables = node.Variables.Where(variable => !context.VariablesToLocals.ContainsKey(variable)).ToArray(); 14 | foreach (var variable in variables) 15 | { 16 | var local = string.IsNullOrEmpty(variable.Name) 17 | ? context.Il.DeclareLocal(variable.Type) 18 | : context.Il.DeclareLocal(variable.Type, variable.Name, appendUniquePrefix : false); 19 | if (context.DebugInfoGenerator != null) 20 | local.SetLocalSymInfo(local.Name); 21 | context.VariablesToLocals.Add(variable, local); 22 | context.Variables.Push(variable); 23 | } 24 | resultType = typeof(void); 25 | for (var index = 0; index < node.Expressions.Count; index++) 26 | { 27 | var expression = node.Expressions[index]; 28 | var il = context.Il; 29 | var valueIsNullLabel = il.DefineLabel("valueIsNull"); 30 | var labelUsed = ExpressionEmittersCollection.Emit(expression, context, valueIsNullLabel, index < node.Expressions.Count - 1 ? ResultType.Void : whatReturn, extend, out resultType); 31 | if (resultType != typeof(void) && index < node.Expressions.Count - 1) 32 | { 33 | // eat results of all expressions except the last one 34 | if (resultType.IsStruct()) 35 | { 36 | using (var temp = context.DeclareLocal(resultType)) 37 | context.Il.Stloc(temp); 38 | } 39 | else context.Il.Pop(); 40 | } 41 | if (labelUsed) 42 | { 43 | var doneLabel = il.DefineLabel("done"); 44 | il.Br(doneLabel); 45 | context.MarkLabelAndSurroundWithSP(valueIsNullLabel); 46 | il.Pop(); 47 | if (resultType != typeof(void) && index == node.Expressions.Count - 1) 48 | { 49 | // return default value for the last expression in the block 50 | context.EmitLoadDefaultValue(resultType); 51 | } 52 | context.MarkLabelAndSurroundWithSP(doneLabel); 53 | } 54 | } 55 | if (node.Type == typeof(bool) && resultType == typeof(bool?)) 56 | { 57 | resultType = typeof(bool); 58 | context.ConvertFromNullableBoolToBool(); 59 | } 60 | else if (node.Type == typeof(void) && resultType != typeof(void)) 61 | { 62 | // eat result of the last expression if the result of block is void 63 | if (resultType.IsStruct()) 64 | { 65 | using (var temp = context.DeclareLocal(resultType)) 66 | context.Il.Stloc(temp); 67 | } 68 | else context.Il.Pop(); 69 | resultType = typeof(void); 70 | } 71 | foreach (var variable in variables) 72 | { 73 | context.VariablesToLocals.Remove(variable); 74 | context.Variables.Pop(); 75 | } 76 | return false; 77 | } 78 | } 79 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/CoalesceExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class CoalesceExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | if (node.Conversion != null) 13 | throw new NotSupportedException("Coalesce with conversion is not supported"); 14 | // note ich: ��� ��������� 15 | // ReSharper disable HeuristicUnreachableCode 16 | var left = node.Left; 17 | var right = node.Right; 18 | GroboIL il = context.Il; 19 | GroboIL.Label valueIsNullLabel = il.DefineLabel("valueIsNull"); 20 | Type leftType; 21 | bool labelUsed = ExpressionEmittersCollection.Emit(left, context, valueIsNullLabel, out leftType); 22 | if (left.Type.IsValueType) 23 | { 24 | using (var temp = context.DeclareLocal(left.Type)) 25 | { 26 | il.Stloc(temp); 27 | il.Ldloca(temp); 28 | } 29 | } 30 | labelUsed |= context.EmitNullChecking(left.Type, valueIsNullLabel); 31 | if (left.Type.IsValueType) 32 | { 33 | if (!left.Type.IsNullable()) 34 | throw new InvalidOperationException("Type '" + left.Type + "' cannot be null"); 35 | if (node.Type != left.Type) 36 | context.EmitValueAccess(left.Type); 37 | else 38 | il.Ldobj(left.Type); 39 | } 40 | var valueIsNotNullLabel = il.DefineLabel("valueIsNotNull"); 41 | il.Br(valueIsNotNullLabel); 42 | if (labelUsed) 43 | { 44 | context.MarkLabelAndSurroundWithSP(valueIsNullLabel); 45 | il.Pop(); 46 | } 47 | Type rightType; 48 | var result = ExpressionEmittersCollection.Emit(right, context, returnDefaultValueLabel, out rightType); 49 | context.MarkLabelAndSurroundWithSP(valueIsNotNullLabel); 50 | resultType = node.Type; 51 | return result; 52 | // ReSharper restore HeuristicUnreachableCode 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/ConditionalExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class ConditionalExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(ConditionalExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | var test = node.Test; 13 | var ifTrue = node.IfTrue; 14 | var ifFalse = node.IfFalse; 15 | var ifTrueBranchIsEmpty = ifTrue.NodeType == ExpressionType.Default && ifTrue.Type == typeof(void); 16 | if (ifTrueBranchIsEmpty) 17 | { 18 | test = Expression.Not(test); 19 | var temp = ifTrue; 20 | ifTrue = ifFalse; 21 | ifFalse = temp; 22 | } 23 | var result = false; 24 | var il = context.Il; 25 | var testIsNullLabel = il.DefineLabel("testIsNull"); 26 | Type testType; 27 | var testIsNullLabelUsed = ExpressionEmittersCollection.Emit(test, context, testIsNullLabel, out testType); 28 | if (testType == typeof(bool?)) 29 | context.ConvertFromNullableBoolToBool(); 30 | var ifFalseLabel = il.DefineLabel("ifFalse"); 31 | il.Brfalse(ifFalseLabel); 32 | Type ifTrueType; 33 | result |= ExpressionEmittersCollection.Emit(ifTrue, context, returnDefaultValueLabel, whatReturn, extend, out ifTrueType); 34 | if (node.Type == typeof(void) && ifTrueType != typeof(void)) 35 | { 36 | using (var temp = context.DeclareLocal(ifTrueType)) 37 | il.Stloc(temp); 38 | } 39 | var doneLabel = il.DefineLabel("done"); 40 | il.Br(doneLabel); 41 | if (testIsNullLabelUsed) 42 | { 43 | context.MarkLabelAndSurroundWithSP(testIsNullLabel); 44 | il.Pop(); 45 | } 46 | context.MarkLabelAndSurroundWithSP(ifFalseLabel); 47 | Type ifFalseType; 48 | result |= ExpressionEmittersCollection.Emit(ifFalse, context, returnDefaultValueLabel, whatReturn, extend, out ifFalseType); 49 | if (node.Type == typeof(void) && ifFalseType != typeof(void)) 50 | { 51 | using (var temp = context.DeclareLocal(ifFalseType)) 52 | il.Stloc(temp); 53 | } 54 | context.MarkLabelAndSurroundWithSP(doneLabel); 55 | if (ifTrueType != typeof(void) && ifFalseType != typeof(void) && ifTrueType != ifFalseType) 56 | throw new InvalidOperationException(string.Format("ifTrue type '{0}' is not equal to ifFalse type '{1}'", ifTrueType, ifFalseType)); 57 | resultType = node.Type == typeof(void) ? typeof(void) : ifTrueType; 58 | return result; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/ConstantExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class ConstantExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(ConstantExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | if (node.Value == null) 13 | context.EmitLoadDefaultValue(node.Type); 14 | else 15 | { 16 | var typeCode = Type.GetTypeCode(node.Type.IsNullable() ? node.Type.GetGenericArguments()[0] : node.Type); 17 | switch (typeCode) 18 | { 19 | case TypeCode.Boolean: 20 | context.Il.Ldc_I4(((bool)node.Value) ? 1 : 0); 21 | break; 22 | case TypeCode.Byte: 23 | context.Il.Ldc_I4((byte)node.Value); 24 | break; 25 | case TypeCode.Char: 26 | context.Il.Ldc_I4((char)node.Value); 27 | break; 28 | case TypeCode.Int16: 29 | context.Il.Ldc_I4((short)node.Value); 30 | break; 31 | case TypeCode.Int32: 32 | context.Il.Ldc_I4((int)node.Value); 33 | break; 34 | case TypeCode.SByte: 35 | context.Il.Ldc_I4((sbyte)node.Value); 36 | break; 37 | case TypeCode.UInt16: 38 | context.Il.Ldc_I4((ushort)node.Value); 39 | break; 40 | case TypeCode.UInt32: 41 | unchecked 42 | { 43 | context.Il.Ldc_I4((int)(uint)node.Value); 44 | } 45 | break; 46 | case TypeCode.Int64: 47 | context.Il.Ldc_I8((long)node.Value); 48 | break; 49 | case TypeCode.UInt64: 50 | unchecked 51 | { 52 | context.Il.Ldc_I8((long)(ulong)node.Value); 53 | } 54 | break; 55 | case TypeCode.Single: 56 | context.Il.Ldc_R4((float)node.Value); 57 | break; 58 | case TypeCode.Double: 59 | context.Il.Ldc_R8((double)node.Value); 60 | break; 61 | case TypeCode.String: 62 | context.Il.Ldstr((string)node.Value); 63 | break; 64 | case TypeCode.Decimal: 65 | context.Il.LdDec((decimal)node.Value); 66 | break; 67 | default: 68 | throw new NotSupportedException("Constant of type '" + node.Type + "' is not supported"); 69 | } 70 | if (node.Type.IsNullable()) 71 | context.Il.Newobj(node.Type.GetConstructor(new[] {node.Type.GetGenericArguments()[0]})); 72 | } 73 | resultType = node.Type; 74 | return false; 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/ConvertExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class ConvertExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | var il = context.Il; 13 | GroboIL.Label operandIsNullLabel = context.CanReturn ? il.DefineLabel("operandIsNull") : null; 14 | var operandIsNullLabelUsed = ExpressionEmittersCollection.Emit(node.Operand, context, operandIsNullLabel, ResultType.Value, extend, out resultType); // stack: [obj] 15 | if (operandIsNullLabelUsed) 16 | context.EmitReturnDefaultValue(resultType, operandIsNullLabel, il.DefineLabel("operandIsNotNull")); 17 | if (resultType != node.Type && !(context.Options.HasFlag(CompilerOptions.UseTernaryLogic) && resultType == typeof(bool?) && node.Type == typeof(bool))) 18 | { 19 | if (node.Method != null) 20 | { 21 | if (!resultType.IsNullable()) 22 | { 23 | il.Call(node.Method); 24 | if (node.Type.IsNullable()) 25 | il.Newobj(node.Type.GetConstructor(new[] {node.Method.ReturnType})); 26 | } 27 | else 28 | { 29 | using (var temp = context.DeclareLocal(resultType)) 30 | { 31 | il.Stloc(temp); 32 | il.Ldloca(temp); 33 | il.Dup(); 34 | context.EmitHasValueAccess(resultType); 35 | var valueIsNullLabel = operandIsNullLabelUsed ? operandIsNullLabel : il.DefineLabel("valueIsNull"); 36 | il.Brfalse(valueIsNullLabel); 37 | context.EmitValueAccess(resultType); 38 | il.Call(node.Method); 39 | il.Newobj(node.Type.GetConstructor(new[] {node.Method.ReturnType})); 40 | if (!operandIsNullLabelUsed) 41 | context.EmitReturnDefaultValue(node.Type, valueIsNullLabel, il.DefineLabel("valueIsNotNull")); 42 | } 43 | } 44 | } 45 | else 46 | { 47 | switch (node.NodeType) 48 | { 49 | case ExpressionType.Convert: 50 | context.EmitConvert(resultType, node.Type); // stack: [(type)obj] 51 | break; 52 | case ExpressionType.ConvertChecked: 53 | context.EmitConvert(resultType, node.Type, true); // stack: [(type)obj] 54 | break; 55 | default: 56 | throw new InvalidOperationException("Node type '" + node.NodeType + "' is not valid at this point"); 57 | } 58 | } 59 | resultType = node.Type; 60 | } 61 | return false; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/DebugInfoExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using System.Reflection.Emit; 5 | using System.Runtime.CompilerServices; 6 | 7 | using GrEmit; 8 | 9 | namespace GrobExp.Compiler.ExpressionEmitters 10 | { 11 | internal class DebugInfoExpressionEmitter : ExpressionEmitter 12 | { 13 | protected override bool EmitInternal(Expression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 14 | { 15 | resultType = typeof(void); 16 | if (context.DebugInfoGenerator == null) 17 | return false; 18 | var result = false; 19 | DebugInfoExpression debugInfo; 20 | if (!(node is TypedDebugInfoExpression)) 21 | debugInfo = (DebugInfoExpression)node; 22 | else 23 | { 24 | var typedNode = node as TypedDebugInfoExpression; 25 | result = ExpressionEmittersCollection.Emit(typedNode.Expression, context, returnDefaultValueLabel, out resultType); 26 | debugInfo = typedNode.DebugInfo; 27 | } 28 | markSequencePoint(context.DebugInfoGenerator, context.Lambda, context.Method, context.Il, debugInfo); 29 | context.Il.Nop(); 30 | return result; 31 | } 32 | 33 | private static Action BuildSequencePointMarker() 34 | { 35 | var parameterTypes = new[] {typeof(DebugInfoGenerator), typeof(LambdaExpression), typeof(MethodBase), typeof(GroboIL), typeof(DebugInfoExpression)}; 36 | var dynamicMethod = new DynamicMethod(Guid.NewGuid().ToString(), typeof(void), parameterTypes, typeof(DebugInfoExpressionEmitter), true); 37 | using (var il = new GroboIL(dynamicMethod)) 38 | { 39 | il.Ldarg(0); 40 | il.Ldarg(1); 41 | il.Ldarg(2); 42 | il.Ldarg(3); 43 | il.Ldfld(typeof(GroboIL).GetField("il", BindingFlags.NonPublic | BindingFlags.Instance)); 44 | il.Ldarg(4); 45 | var markSequencePointMethod = typeof(DebugInfoGenerator).GetMethod("MarkSequencePoint", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] {typeof(LambdaExpression), typeof(MethodBase), typeof(ILGenerator), typeof(DebugInfoExpression)}, null); 46 | il.Call(markSequencePointMethod, typeof(DebugInfoGenerator)); 47 | il.Ret(); 48 | } 49 | return (Action)dynamicMethod.CreateDelegate(typeof(Action)); 50 | } 51 | 52 | private static readonly Action markSequencePoint = BuildSequencePointMarker(); 53 | } 54 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/DefaultExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class DefaultExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(DefaultExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | resultType = node.Type; 13 | if (node.Type != typeof(void)) 14 | context.EmitLoadDefaultValue(node.Type); 15 | return false; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/ExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal abstract class ExpressionEmitter : IExpressionEmitter where TExpression : Expression 9 | { 10 | public bool Emit(Expression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | return EmitInternal((TExpression)node, context, returnDefaultValueLabel, node.Type == typeof(void) ? ResultType.Void : whatReturn, extend, out resultType); 13 | } 14 | 15 | protected abstract bool EmitInternal(TExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType); 16 | } 17 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/GotoExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class GotoExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(GotoExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | bool result = false; 13 | if (node.Value != null) 14 | result = ExpressionEmittersCollection.Emit(node.Value, context, returnDefaultValueLabel, out resultType); 15 | resultType = typeof(void); 16 | GroboIL.Label label; 17 | if (!context.Labels.TryGetValue(node.Target, out label)) 18 | context.Labels.Add(node.Target, label = context.Il.DefineLabel(node.Target.Name)); 19 | context.Il.Br(label); 20 | return result; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/IExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal interface IExpressionEmitter 9 | { 10 | bool Emit(Expression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType); 11 | } 12 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/InvocationExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | 6 | using GrEmit; 7 | 8 | namespace GrobExp.Compiler.ExpressionEmitters 9 | { 10 | internal class InvocationExpressionEmitter : ExpressionEmitter 11 | { 12 | protected override bool EmitInternal(InvocationExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 13 | { 14 | bool result; 15 | if (node.Expression.NodeType != ExpressionType.Lambda) 16 | { 17 | Type delegateType; 18 | result = ExpressionEmittersCollection.Emit(node.Expression, context, returnDefaultValueLabel, ResultType.Value, extend, out delegateType); 19 | context.EmitLoadArguments(node.Arguments.ToArray()); 20 | var invokeMethod = delegateType.GetMethod("Invoke", node.Arguments.Select(argument => argument.Type).ToArray()); 21 | context.Il.Call(invokeMethod, delegateType); 22 | } 23 | else 24 | { 25 | result = false; 26 | var lambda = (LambdaExpression)node.Expression; 27 | Type[] constantTypes; 28 | var compiledLambda = LambdaExpressionEmitter.CompileAndLoadConstants(lambda, context, out constantTypes); 29 | context.EmitLoadArguments(node.Arguments.ToArray()); 30 | context.LoadCompiledLambdaPointer(compiledLambda); 31 | context.Il.Calli(CallingConventions.Standard, lambda.ReturnType, constantTypes.Concat(lambda.Parameters.Select(parameter => parameter.Type)).ToArray()); 32 | } 33 | resultType = node.Type; 34 | return result; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/LabelExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class LabelExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(LabelExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | bool result = false; 13 | if (node.DefaultValue != null) 14 | result = ExpressionEmittersCollection.Emit(node.DefaultValue, context, returnDefaultValueLabel, out resultType); 15 | else 16 | resultType = typeof(void); 17 | GroboIL.Label label; 18 | if (!context.Labels.TryGetValue(node.Target, out label)) 19 | context.Labels.Add(node.Target, label = context.Il.DefineLabel(node.Target.Name)); 20 | context.MarkLabelAndSurroundWithSP(label); 21 | return result; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/ListInitExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | using GrEmit; 6 | 7 | namespace GrobExp.Compiler.ExpressionEmitters 8 | { 9 | internal class ListInitExpressionEmitter : ExpressionEmitter 10 | { 11 | protected override bool EmitInternal(ListInitExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 12 | { 13 | var il = context.Il; 14 | var result = ExpressionEmittersCollection.Emit(node.NewExpression, context, returnDefaultValueLabel, out resultType); 15 | if (!resultType.IsValueType) 16 | { 17 | foreach (var initializer in node.Initializers) 18 | { 19 | il.Dup(); 20 | context.EmitLoadArguments(initializer.Arguments.ToArray()); 21 | il.Call(initializer.AddMethod, resultType); 22 | } 23 | } 24 | else 25 | { 26 | if (node.Initializers.Count > 0) 27 | { 28 | using (var temp = context.DeclareLocal(resultType)) 29 | { 30 | il.Stloc(temp); 31 | foreach (var initializer in node.Initializers) 32 | { 33 | il.Ldloca(temp); 34 | context.EmitLoadArguments(initializer.Arguments.ToArray()); 35 | il.Call(initializer.AddMethod, resultType); 36 | } 37 | il.Ldloc(temp); 38 | } 39 | } 40 | } 41 | return result; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/LoopExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class LoopExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(LoopExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | var il = context.Il; 13 | GroboIL.Label continueLabel; 14 | if (node.ContinueLabel == null) 15 | continueLabel = context.Il.DefineLabel("continue"); 16 | else 17 | { 18 | if (!context.Labels.TryGetValue(node.ContinueLabel, out continueLabel)) 19 | context.Labels.Add(node.ContinueLabel, continueLabel = context.Il.DefineLabel(string.IsNullOrEmpty(node.ContinueLabel.Name) ? "continue" : node.ContinueLabel.Name)); 20 | } 21 | context.MarkLabelAndSurroundWithSP(continueLabel); 22 | GroboIL.Label breakLabel; 23 | if (node.BreakLabel == null) 24 | breakLabel = null; 25 | else 26 | { 27 | if (!context.Labels.TryGetValue(node.BreakLabel, out breakLabel)) 28 | context.Labels.Add(node.BreakLabel, breakLabel = context.Il.DefineLabel(string.IsNullOrEmpty(node.BreakLabel.Name) ? "break" : node.BreakLabel.Name)); 29 | } 30 | ExpressionEmittersCollection.Emit(node.Body, context, out resultType); 31 | if (resultType != typeof(void)) 32 | { 33 | if (resultType.IsStruct()) 34 | { 35 | using (var temp = context.DeclareLocal(resultType)) 36 | context.Il.Stloc(temp); 37 | } 38 | else context.Il.Pop(); 39 | } 40 | il.Br(continueLabel); 41 | if (breakLabel != null) 42 | context.MarkLabelAndSurroundWithSP(breakLabel); 43 | if (node.BreakLabel != null) 44 | resultType = node.BreakLabel.Type; 45 | return false; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/MemberAccessExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class MemberAccessExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(MemberExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | EmittingContext.LocalHolder owner; 13 | var result = context.EmitMemberAccess(node, returnDefaultValueLabel, context.Options.HasFlag(CompilerOptions.CheckNullReferences), extend, whatReturn, out resultType, out owner); 14 | if (owner != null) 15 | owner.Dispose(); 16 | return result; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/MemberInitExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class MemberInitExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(MemberInitExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | ExpressionEmittersCollection.Emit(node.NewExpression, context, out resultType); // stack: [new obj(args)] 13 | GroboIL il = context.Il; 14 | if (!node.Type.IsValueType) 15 | { 16 | foreach (MemberAssignment assignment in node.Bindings) 17 | { 18 | il.Dup(); 19 | context.EmitLoadArguments(assignment.Expression); 20 | context.EmitMemberAssign(node.Type, assignment.Member); 21 | } 22 | } 23 | else 24 | { 25 | using (var temp = context.DeclareLocal(node.Type)) 26 | { 27 | il.Stloc(temp); 28 | il.Ldloca(temp); 29 | foreach (MemberAssignment assignment in node.Bindings) 30 | { 31 | il.Dup(); 32 | context.EmitLoadArguments(assignment.Expression); 33 | context.EmitMemberAssign(node.Type, assignment.Member); 34 | } 35 | // todo ��� il.Ldobj()? 36 | il.Pop(); 37 | il.Ldloc(temp); 38 | } 39 | } 40 | return false; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/NewArrayBoundsExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | using GrEmit; 6 | 7 | namespace GrobExp.Compiler.ExpressionEmitters 8 | { 9 | internal class NewArrayBoundsExpressionEmitter : ExpressionEmitter 10 | { 11 | protected override bool EmitInternal(NewArrayExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 12 | { 13 | var il = context.Il; 14 | 15 | if (node.Expressions.Count != 1) 16 | { 17 | context.EmitLoadArguments(node.Expressions.ToArray()); 18 | il.Newobj(node.Type.GetConstructor(node.Expressions.Select(exp => exp.Type).ToArray())); 19 | } 20 | else 21 | { 22 | GroboIL.Label lengthIsNullLabel = context.CanReturn ? il.DefineLabel("lengthIsNull") : null; 23 | Type lengthType; 24 | var labelUsed = ExpressionEmittersCollection.Emit(node.Expressions.Single(), context, lengthIsNullLabel, out lengthType); 25 | if (lengthType != typeof(int)) 26 | throw new InvalidOperationException("Cannot create an array with length of type '" + lengthType + "'"); 27 | if (labelUsed && context.CanReturn) 28 | { 29 | var lengthIsNotNullLabel = il.DefineLabel("lengthIsNotNull"); 30 | il.Br(lengthIsNotNullLabel); 31 | context.MarkLabelAndSurroundWithSP(lengthIsNullLabel); 32 | il.Pop(); 33 | il.Ldc_I4(0); 34 | context.MarkLabelAndSurroundWithSP(lengthIsNotNullLabel); 35 | } 36 | il.Newarr(node.Type.GetElementType()); 37 | } 38 | resultType = node.Type; 39 | return false; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/NewArrayInitExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class NewArrayInitExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(NewArrayExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | var il = context.Il; 13 | Type elementType = node.Type.GetElementType(); 14 | il.Ldc_I4(node.Expressions.Count); // stack: [length] 15 | il.Newarr(elementType); // stack: [new type[length]] 16 | for (int index = 0; index < node.Expressions.Count; index++) 17 | { 18 | var expression = node.Expressions[index]; 19 | il.Dup(); 20 | il.Ldc_I4(index); 21 | if (elementType.IsValueType && !elementType.IsPrimitive) 22 | { 23 | il.Ldelema(elementType); 24 | context.EmitLoadArguments(expression); 25 | il.Stobj(elementType); 26 | } 27 | else 28 | { 29 | context.EmitLoadArguments(expression); 30 | il.Stelem(elementType); 31 | } 32 | } 33 | resultType = node.Type; 34 | return false; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/NewExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | using GrEmit; 6 | 7 | namespace GrobExp.Compiler.ExpressionEmitters 8 | { 9 | internal class NewExpressionEmitter : ExpressionEmitter 10 | { 11 | protected override bool EmitInternal(NewExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 12 | { 13 | context.EmitLoadArguments(node.Arguments.ToArray()); 14 | // note ich: ��� ��������� 15 | // ReSharper disable ConditionIsAlwaysTrueOrFalse 16 | // ReSharper disable HeuristicUnreachableCode 17 | GroboIL il = context.Il; 18 | if (node.Constructor != null) 19 | il.Newobj(node.Constructor); 20 | else 21 | { 22 | if (node.Type.IsValueType) 23 | { 24 | using (var temp = context.DeclareLocal(node.Type)) 25 | { 26 | il.Ldloca(temp); 27 | il.Initobj(node.Type); 28 | il.Ldloc(temp); 29 | } 30 | } 31 | else throw new InvalidOperationException("Missing constructor for type '" + node.Type + "'"); 32 | } 33 | resultType = node.Type; 34 | // ReSharper restore ConditionIsAlwaysTrueOrFalse 35 | // ReSharper restore HeuristicUnreachableCode 36 | return false; 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/NotExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | using GrEmit; 6 | 7 | namespace GrobExp.Compiler.ExpressionEmitters 8 | { 9 | internal class NotExpressionEmitter : ExpressionEmitter 10 | { 11 | protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 12 | { 13 | if (node.Type != typeof(bool) && node.Type != typeof(bool?)) 14 | return ExpressionEmittersCollection.Emit(Expression.OnesComplement(node.Operand, node.Method), context, returnDefaultValueLabel, whatReturn, extend, out resultType); 15 | GroboIL il = context.Il; 16 | if (node.Method != null) 17 | throw new NotSupportedException("Custom operator '" + node.NodeType + "' is not supported"); 18 | var operand = node.Operand; 19 | 20 | context.EmitLoadArgument(operand, false, out resultType); 21 | if (resultType == typeof(bool)) 22 | { 23 | il.Ldc_I4(1); 24 | il.Xor(); 25 | } 26 | else if (resultType == typeof(bool?)) 27 | { 28 | using (var value = context.DeclareLocal(typeof(bool?))) 29 | { 30 | il.Stloc(value); 31 | il.Ldloca(value); 32 | context.EmitHasValueAccess(typeof(bool?)); 33 | var returnLabel = il.DefineLabel("return"); 34 | il.Brfalse(returnLabel); 35 | il.Ldloca(value); 36 | context.EmitValueAccess(typeof(bool?)); 37 | il.Ldc_I4(1); 38 | il.Xor(); 39 | il.Newobj(nullableBoolConstructor); 40 | il.Stloc(value); 41 | context.MarkLabelAndSurroundWithSP(returnLabel); 42 | il.Ldloc(value); 43 | } 44 | } 45 | else throw new InvalidOperationException("Cannot perform '" + node.NodeType + "' operator on type '" + resultType + "'"); 46 | return false; 47 | } 48 | 49 | // ReSharper disable RedundantExplicitNullableCreation 50 | private static readonly ConstructorInfo nullableBoolConstructor = ((NewExpression)((Expression>)(b => new bool?(b))).Body).Constructor; 51 | // ReSharper restore RedundantExplicitNullableCreation 52 | } 53 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/RuntimeVariables.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace GrobExp.Compiler.ExpressionEmitters 4 | { 5 | public class RuntimeVariables : IRuntimeVariables 6 | { 7 | public RuntimeVariables(object[] values) 8 | { 9 | this.values = values; 10 | } 11 | 12 | public int Count { get { return values.Length; } } 13 | 14 | public object this[int index] { get { return values[index]; } set { values[index] = value; } } 15 | private readonly object[] values; 16 | } 17 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/ThrowExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class ThrowExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | Type operandType; 13 | var result = ExpressionEmittersCollection.Emit(node.Operand, context, returnDefaultValueLabel, out operandType); 14 | context.Il.Throw(); 15 | resultType = typeof(void); 16 | return result; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/TypeAsExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class TypeAsExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | Type operandType; 13 | var result = ExpressionEmittersCollection.Emit(node.Operand, context, returnDefaultValueLabel, ResultType.Value, extend, out operandType); 14 | GroboIL il = context.Il; 15 | if (operandType.IsValueType) 16 | il.Box(operandType); 17 | il.Isinst(node.Type); 18 | if (node.Type.IsValueType) 19 | il.Unbox_Any(node.Type); 20 | resultType = node.Type; 21 | return result; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/TypeEqualExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class TypeEqualExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(TypeBinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | bool result; 13 | GroboIL il = context.Il; 14 | if (!node.Expression.Type.IsAssignableFrom(node.TypeOperand)) 15 | { 16 | il.Ldc_I4(0); 17 | result = false; 18 | } 19 | else if (node.Expression.Type == node.TypeOperand && node.TypeOperand.IsValueType) 20 | { 21 | il.Ldc_I4(1); 22 | result = false; 23 | } 24 | else 25 | { 26 | Type operandType; 27 | result = ExpressionEmittersCollection.Emit(node.Expression, context, returnDefaultValueLabel, ResultType.Value, extend, out operandType); 28 | if (operandType.IsValueType) 29 | { 30 | using (var temp = context.DeclareLocal(operandType)) 31 | { 32 | il.Stloc(temp); 33 | il.Ldloca(temp); 34 | } 35 | } 36 | var returnFalseLabel = il.DefineLabel("returnFalse"); 37 | il.Dup(); 38 | il.Brfalse(returnFalseLabel); 39 | il.Call(typeof(object).GetMethod("GetType"), operandType); 40 | il.Ldtoken(node.TypeOperand); 41 | il.Call(typeof(Type).GetMethod("GetTypeFromHandle")); 42 | il.Ceq(); 43 | var doneLabel = il.DefineLabel("done"); 44 | il.Br(doneLabel); 45 | context.MarkLabelAndSurroundWithSP(returnFalseLabel); 46 | il.Pop(); 47 | il.Ldc_I4(0); 48 | context.MarkLabelAndSurroundWithSP(doneLabel); 49 | } 50 | resultType = typeof(bool); 51 | return result; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/TypeIsExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class TypeIsExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(TypeBinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | Type operandType; 13 | var result = ExpressionEmittersCollection.Emit(node.Expression, context, returnDefaultValueLabel, ResultType.Value, extend, out operandType); 14 | var il = context.Il; 15 | if (operandType.IsValueType) 16 | il.Box(operandType); 17 | il.Isinst(node.TypeOperand); 18 | il.Ldnull(); 19 | il.Cgt(true); 20 | resultType = typeof(bool); 21 | return result; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ExpressionEmitters/UnboxExpressionEmitter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | using GrEmit; 5 | 6 | namespace GrobExp.Compiler.ExpressionEmitters 7 | { 8 | internal class UnboxExpressionEmitter : ExpressionEmitter 9 | { 10 | protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType) 11 | { 12 | Type operandType; 13 | var result = ExpressionEmittersCollection.Emit(node.Operand, context, returnDefaultValueLabel, ResultType.Value, extend, out operandType); 14 | if (context.Options.HasFlag(CompilerOptions.CheckNullReferences)) 15 | { 16 | context.Il.Dup(); 17 | context.Il.Brfalse(returnDefaultValueLabel); 18 | result = true; 19 | } 20 | context.Il.Unbox_Any(node.Type); 21 | resultType = node.Type; 22 | return result; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace GrobExp.Compiler 6 | { 7 | public static class Extensions 8 | { 9 | static Extensions() 10 | { 11 | var systemTypes = typeof(Func<>).Assembly.ExportedTypes.ToArray(); 12 | 13 | funcTypesByNumberOfGenericParameters = systemTypes.Where(type => type.FullName?.StartsWith("System.Func`") == true) 14 | .ToDictionary(type => type.GetGenericArguments().Length); 15 | 16 | actionTypesByNumberOfGenericParameters = systemTypes.Where(type => type.FullName?.StartsWith("System.Action`") == true) 17 | .ToDictionary(type => type.GetGenericArguments().Length); 18 | } 19 | 20 | public static bool IsMono { get; } = Type.GetType("Mono.Runtime") != null; 21 | 22 | public static bool IsNullable(this Type type) 23 | { 24 | return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); 25 | } 26 | 27 | public static bool IsStruct(this Type type) 28 | { 29 | return type.IsValueType && !type.IsPrimitive && !type.IsEnum; 30 | } 31 | 32 | public static bool IsList(this Type type) 33 | { 34 | return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>); 35 | } 36 | 37 | public static bool IsDictionary(this Type type) 38 | { 39 | return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>); 40 | } 41 | 42 | public static bool IsStaticClosure(this Type type) 43 | { 44 | return type.IsNested && type.DeclaringType == typeof(StaticClosures); 45 | } 46 | 47 | public static bool Unsigned(this Type type) 48 | { 49 | if (type == typeof(IntPtr)) 50 | return false; 51 | if (type == typeof(UIntPtr)) 52 | return true; 53 | switch (Type.GetTypeCode(type)) 54 | { 55 | case TypeCode.Boolean: 56 | case TypeCode.Byte: 57 | case TypeCode.Char: 58 | case TypeCode.UInt16: 59 | case TypeCode.UInt32: 60 | case TypeCode.UInt64: 61 | return true; 62 | case TypeCode.SByte: 63 | case TypeCode.Int16: 64 | case TypeCode.Int32: 65 | case TypeCode.Int64: 66 | case TypeCode.Single: 67 | case TypeCode.Double: 68 | return false; 69 | default: 70 | throw new InvalidOperationException($"Type '{type}' cannot be used in comparison operations"); 71 | } 72 | } 73 | 74 | public static Type GetDelegateType(Type[] parameterTypes, Type returnType) 75 | { 76 | if (returnType == typeof(void)) 77 | { 78 | if (parameterTypes.Length == 0) 79 | return typeof(Action); 80 | 81 | if (actionTypesByNumberOfGenericParameters.TryGetValue(parameterTypes.Length, out var actionType)) 82 | return actionType.MakeGenericType(parameterTypes); 83 | 84 | throw new NotSupportedException($"Too many parameters for creating Action type: {parameterTypes.Length}"); 85 | } 86 | 87 | parameterTypes = parameterTypes.Concat(new[] {returnType}).ToArray(); 88 | 89 | if (funcTypesByNumberOfGenericParameters.TryGetValue(parameterTypes.Length, out var funcType)) 90 | return funcType.MakeGenericType(parameterTypes); 91 | 92 | throw new NotSupportedException($"Too many parameters for creating Func type: {parameterTypes.Length}"); 93 | } 94 | 95 | private static readonly Dictionary funcTypesByNumberOfGenericParameters; 96 | private static readonly Dictionary actionTypesByNumberOfGenericParameters; 97 | } 98 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/GrobExp.Compiler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;net45 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /GrobExp.Compiler/LabelsCloner.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | 4 | namespace GrobExp.Compiler 5 | { 6 | internal class LabelsCloner : ExpressionVisitor 7 | { 8 | protected override LabelTarget VisitLabelTarget(LabelTarget node) 9 | { 10 | if (node == null) 11 | return null; 12 | LabelTarget newLabel; 13 | if (!labels.TryGetValue(node, out newLabel)) 14 | { 15 | newLabel = Expression.Label(node.Type, node.Name); 16 | labels.Add(node, newLabel); 17 | } 18 | return newLabel; 19 | } 20 | 21 | private readonly Dictionary labels = new Dictionary(); 22 | } 23 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/LambdaExpressionCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Reflection.Emit; 8 | 9 | using GrEmit; 10 | 11 | namespace GrobExp.Compiler 12 | { 13 | public static class LambdaExpressionCreator 14 | { 15 | public static LambdaExpression Create(Expression body, Type returnType, ParameterExpression[] parameters, string name = null, bool tailCall = false) 16 | { 17 | if (parameters.Any(parameter => parameter.IsByRef)) // TODO handle this 18 | return Expression.Lambda(body, name, tailCall, parameters); 19 | var parameterz = new ParameterExpression[parameters.Length]; 20 | Array.Copy(parameters, parameterz, parameters.Length); 21 | return GetLambdaFactory(returnType, parameters)(body, name, tailCall, new ReadOnlyCollection(parameterz)); 22 | } 23 | 24 | public static LambdaExpression Create(Expression body, params ParameterExpression[] parameters) 25 | { 26 | return Create(body, body.Type, parameters); 27 | } 28 | 29 | public static Expression Create(Expression body, ParameterExpression[] parameters, string name = null, bool tailCall = false) 30 | { 31 | if (parameters.Any(parameter => parameter.IsByRef)) // TODO handle this 32 | return Expression.Lambda(body, name, tailCall, parameters); 33 | var parameterz = new ParameterExpression[parameters.Length]; 34 | Array.Copy(parameters, parameterz, parameters.Length); 35 | return (Expression)GetLambdaFactory(typeof(TDelegate))(body, name, tailCall, new ReadOnlyCollection(parameterz)); 36 | } 37 | 38 | private static LambdaCreateDelegate GetLambdaFactory(Type returnType, ParameterExpression[] parameters) 39 | { 40 | return GetLambdaFactory(Extensions.GetDelegateType(parameters.Select(parameter => parameter.Type).ToArray(), returnType)); 41 | } 42 | 43 | private static LambdaCreateDelegate GetLambdaFactory(Type delegateType) 44 | { 45 | var factory = (LambdaCreateDelegate)factories[delegateType]; 46 | if (factory == null) 47 | { 48 | lock (factoriesLock) 49 | { 50 | factory = (LambdaCreateDelegate)factories[delegateType]; 51 | if (factory == null) 52 | { 53 | factory = BuildLambdaFactory(delegateType); 54 | factories[delegateType] = factory; 55 | } 56 | } 57 | } 58 | return factory; 59 | } 60 | 61 | private static LambdaCreateDelegate BuildLambdaFactory(Type delegateType) 62 | { 63 | var resultType = typeof(Expression<>).MakeGenericType(delegateType); 64 | var parameterTypes = new[] {typeof(Expression), typeof(string), typeof(bool), typeof(ReadOnlyCollection)}; 65 | var method = new DynamicMethod(Guid.NewGuid().ToString(), typeof(LambdaExpression), parameterTypes, typeof(LambdaExpressionCreator), true); 66 | using (var il = new GroboIL(method)) 67 | { 68 | il.Ldarg(0); 69 | il.Ldarg(1); 70 | il.Ldarg(2); 71 | il.Ldarg(3); 72 | il.Callnonvirt(resultType.GetMethod("Create", BindingFlags.NonPublic | BindingFlags.Static)); 73 | il.Ret(); 74 | } 75 | return (LambdaCreateDelegate)method.CreateDelegate(typeof(LambdaCreateDelegate)); 76 | } 77 | 78 | private delegate LambdaExpression LambdaCreateDelegate(Expression body, string name, bool tailCall, ReadOnlyCollection parameters); 79 | 80 | private static readonly Hashtable factories = new Hashtable(); 81 | private static readonly object factoriesLock = new object(); 82 | } 83 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/LambdaPreparer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace GrobExp.Compiler 7 | { 8 | internal class LambdaPreparer : ExpressionVisitor 9 | { 10 | protected override Expression VisitUnary(UnaryExpression node) 11 | { 12 | return node.NodeType == ExpressionType.Quote ? node : base.VisitUnary(node); 13 | } 14 | 15 | // TODO inline small lambdas 16 | //protected override Expression VisitInvocation(InvocationExpression node) 17 | //{ 18 | // if(node.Expression.NodeType != ExpressionType.Lambda) 19 | // return base.VisitInvocation(node); 20 | // var lambda = (LambdaExpression)node.Expression; 21 | // var expressions = lambda.Parameters.Select((t, i) => Expression.Assign(t, Visit(node.Arguments[i]))).Cast().ToList(); 22 | // expressions.Add(Visit(new LabelsCloner().Visit(lambda.Body))); 23 | // return Expression.Block(lambda.Body.Type, lambda.Parameters, expressions); 24 | //} 25 | 26 | protected override Expression VisitExtension(Expression node) 27 | { 28 | return !(node is TypedDebugInfoExpression) && node.CanReduce ? Visit(node.Reduce()) : base.VisitExtension(node); 29 | } 30 | 31 | protected override Expression VisitDynamic(DynamicExpression node) 32 | { 33 | var site = CallSite.Create(node.DelegateType, node.Binder); 34 | var siteType = site.GetType(); 35 | var constant = Expression.Constant(site, siteType); 36 | return Expression.Call(Expression.MakeMemberAccess(constant, siteType.GetField("Target")), node.DelegateType.GetMethod("Invoke"), new[] {constant}.Concat(node.Arguments.Select(Visit))); 37 | } 38 | 39 | protected override Expression VisitBinary(BinaryExpression node) 40 | { 41 | if (node.NodeType == ExpressionType.Equal || node.NodeType == ExpressionType.NotEqual) 42 | { 43 | var left = Visit(node.Left); 44 | var right = Visit(node.Right); 45 | if (left.Type.IsNullable() && right.Type == typeof(object)) 46 | right = Expression.Convert(right, node.Left.Type); 47 | return node.Update(left, (LambdaExpression)Visit(node.Conversion), right); 48 | } 49 | return base.VisitBinary(node); 50 | } 51 | 52 | protected override Expression VisitMethodCall(MethodCallExpression node) 53 | { 54 | if (node.Object == null) 55 | return base.VisitMethodCall(node); 56 | var indexer = node.Object.Type.GetProperty("Item", BindingFlags.Public | BindingFlags.Instance); 57 | if (indexer != null && indexer.GetGetMethod(true) == node.Method) 58 | return Expression.MakeIndex(node.Object, indexer, node.Arguments); 59 | return base.VisitMethodCall(node); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ParametersExtractor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | 5 | namespace GrobExp.Compiler 6 | { 7 | public class ParametersExtractor : ExpressionVisitor 8 | { 9 | public ParameterExpression[] Extract(Expression exp) 10 | { 11 | localParameters.Clear(); 12 | result.Clear(); 13 | Visit(exp); 14 | return result.ToArray(); 15 | } 16 | 17 | protected override Expression VisitLambda(Expression lambda) 18 | { 19 | foreach (var parameter in lambda.Parameters) 20 | localParameters.Add(parameter); 21 | var res = base.VisitLambda(lambda); 22 | foreach (var parameter in lambda.Parameters) 23 | localParameters.Remove(parameter); 24 | return res; 25 | } 26 | 27 | protected override Expression VisitBlock(BlockExpression node) 28 | { 29 | foreach (var variable in node.Variables) 30 | localParameters.Add(variable); 31 | var res = base.VisitBlock(node); 32 | foreach (var variable in node.Variables) 33 | localParameters.Remove(variable); 34 | return res; 35 | } 36 | 37 | protected override Expression VisitParameter(ParameterExpression p) 38 | { 39 | if (!localParameters.Contains(p)) 40 | result.Add(p); 41 | return base.VisitParameter(p); 42 | } 43 | 44 | private readonly HashSet localParameters = new HashSet(); 45 | private readonly HashSet result = new HashSet(); 46 | } 47 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/PlatformHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GrobExp.Compiler 4 | { 5 | public static class PlatformHelpers 6 | { 7 | static PlatformHelpers() 8 | { 9 | IsDotNet60OrGreater = HasSystemType("System.DateOnly"); 10 | } 11 | 12 | public static bool IsDotNet60OrGreater { get; } 13 | 14 | private static bool HasSystemType(string typeName) 15 | { 16 | try 17 | { 18 | return Type.GetType(typeName) != null; 19 | } 20 | catch 21 | { 22 | return false; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/ResultType.cs: -------------------------------------------------------------------------------- 1 | namespace GrobExp.Compiler 2 | { 3 | internal enum ResultType 4 | { 5 | Value, 6 | ByRefAll, 7 | ByRefValueTypesOnly, 8 | Void 9 | } 10 | } -------------------------------------------------------------------------------- /GrobExp.Compiler/TypedDebugInfoExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace GrobExp.Compiler 5 | { 6 | public class TypedDebugInfoExpression : Expression 7 | { 8 | public TypedDebugInfoExpression(SymbolDocumentInfo document, Expression expression, int startLine, int startColumn, int endLine, int endColumn) 9 | { 10 | Document = document; 11 | this.Expression = expression; 12 | StartLine = startLine; 13 | StartColumn = startColumn; 14 | EndLine = endLine; 15 | EndColumn = endColumn; 16 | DebugInfo = DebugInfo(Document, StartLine, StartColumn, EndLine, EndColumn); 17 | } 18 | 19 | public TypedDebugInfoExpression(Expression expression, DebugInfoExpression debugInfo) 20 | { 21 | Document = debugInfo.Document; 22 | this.Expression = expression; 23 | StartLine = debugInfo.StartLine; 24 | StartColumn = debugInfo.StartColumn; 25 | EndLine = debugInfo.EndLine; 26 | EndColumn = debugInfo.EndColumn; 27 | this.DebugInfo = debugInfo; 28 | } 29 | 30 | /// 31 | /// Gets the static type of the expression that this represents. (Inherited from .) 32 | /// 33 | /// The that represents the static type of the expression. 34 | public sealed override Type Type { get { return Expression.Type; } } 35 | 36 | public Expression Expression { get; } 37 | 38 | public new DebugInfoExpression DebugInfo { get; } 39 | 40 | /// 41 | /// Returns the node type of this . (Inherited from .) 42 | /// 43 | /// The that represents this expression. 44 | public sealed override ExpressionType NodeType { get { return ExpressionType.DebugInfo; } } 45 | 46 | public int StartLine { get; } 47 | 48 | public int StartColumn { get; } 49 | 50 | public int EndLine { get; } 51 | 52 | public int EndColumn { get; } 53 | 54 | /// 55 | /// Gets the that represents the source file. 56 | /// 57 | public SymbolDocumentInfo Document { get; } 58 | 59 | public override Expression Reduce() 60 | { 61 | return Expression; 62 | } 63 | 64 | protected override Expression Accept(ExpressionVisitor visitor) 65 | { 66 | return this; 67 | } 68 | 69 | protected override Expression VisitChildren(ExpressionVisitor visitor) 70 | { 71 | return this; 72 | } 73 | 74 | public override bool CanReduce { get { return true; } } 75 | } 76 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 SKB Kontur 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GrobExp.Compiler 2 | 3 | [![NuGet Status](https://img.shields.io/nuget/v/GrobExp.Compiler.svg)](https://www.nuget.org/packages/GrobExp.Compiler/) 4 | [![Build status](https://github.com/skbkontur/GrobExp.Compiler/actions/workflows/actions.yml/badge.svg)](https://github.com/skbkontur/GrobExp.Compiler/actions) 5 | 6 | GrobExp.Compiler helps efficiently compile expression trees. 7 | 8 | ## Release Notes 9 | 10 | See [CHANGELOG](CHANGELOG.md). 11 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "6.0.100", 4 | "rollForward": "latestFeature" 5 | } 6 | } -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.3", 3 | "assemblyVersion": { 4 | "precision": "build" 5 | }, 6 | "publicReleaseRefSpec": [ 7 | "^refs/heads/master$", 8 | "^refs/tags/v\\d+\\.\\d+" 9 | ], 10 | "nugetPackageVersion": { 11 | "semVer": 2 12 | }, 13 | "cloudBuild": { 14 | "setVersionVariables": true, 15 | "buildNumber": { 16 | "enabled": false 17 | } 18 | } 19 | } --------------------------------------------------------------------------------