From c54d3f9a16896febfa0335d8ffdf9b3243f0d531 Mon Sep 17 00:00:00 2001 From: Salavat Galiamov Date: Sun, 11 Apr 2021 20:57:13 +0200 Subject: [PATCH] Illuminator (#40) --- .gitmodules | 3 - .vscode/settings.json | 2 + ILLightenComparer.sln | 12 -- ILLightenComparer.sln.DotSettings | 9 + appveyor.yml | 2 +- docs/deployment.md | 18 +- external/illuminator | 1 - .../Benchmark/EqualityBenchmark.cs | 28 +-- .../ComparerTests/GenericTests.cs | 3 +- .../EqualityExamples.cs | 1 + .../EqualityTests/GenericTests.cs | 3 +- .../EqualityTests/SampleTypesTests.cs | 8 +- .../Utilities/CasualNullGenerator.cs | 1 - .../Utilities/FixtureBuilder.cs | 1 - .../Utilities/Helper.cs | 1 - .../Utilities/ObjectExtensions.cs | 3 +- .../Utilities/ObjectWalker.cs | 1 - .../Utilities/ReflectionExtensions.cs | 32 ++- .../Abstractions/IComparisonEmitter.cs | 9 +- .../Abstractions/IHasherEmitter.cs | 20 +- .../Comparer/CompareStaticMethodEmitter.cs | 28 +-- .../Comparer/ComparisonResolver.cs | 2 +- .../Comparisons/ComparablesComparison.cs | 22 +- .../Comparisons/IntegralsComparison.cs | 1 - .../Comparer/CustomEmitters.cs | 88 ++++---- .../Config/ConfigurationProvider.cs | 1 - .../Comparisons/BacisEqualityComparison.cs | 12 +- .../Comparisons/CeqEqualityComparison.cs | 3 +- .../Comparisons/OperatorEqualityComparison.cs | 12 +- .../Equality/CustomEmitters.cs | 60 +++--- .../Equality/EqualityContext.cs | 1 - .../Equality/EqualityResolver.cs | 4 +- .../Equality/EqualsStaticMethodEmitter.cs | 21 +- .../GetHashCodeStaticMethodEmitter.cs | 27 +-- .../Equality/HasherResolver.cs | 2 +- .../Equality/Hashers/ArrayHashEmitter.cs | 22 +- .../Equality/Hashers/ArrayHasher.cs | 20 +- .../Equality/Hashers/BasicHasher.cs | 10 +- .../Equality/Hashers/EnumerablesHasher.cs | 38 ++-- .../Equality/Hashers/IndirectHasher.cs | 14 +- .../Equality/Hashers/MembersHasher.cs | 8 +- .../Equality/Hashers/NullableHasher.cs | 15 +- .../Equality/Hashers/StringHasher.cs | 24 ++- src/ILLightenComparer/Extensions/Functions.cs | 45 ++++ .../Extensions/ILEmitterExtensions.cs | 198 ++++++++++++++---- .../Extensions/MemberInfoExtensions.cs | 10 + .../Extensions/MethodBuilderExtensions.cs | 26 +++ .../Extensions/MethodInfoExtensions.cs | 23 ++ src/ILLightenComparer/Extensions/Methods.cs | 1 - .../Extensions/ModuleBuilderExtensions.cs | 26 +++ .../Extensions/TypeBuilderExtensions.cs | 27 +++ .../Extensions/TypeExtensions.cs | 88 +++++--- .../ILLightenComparer.csproj | 7 +- .../Comparisons/ArrayComparisonEmitter.cs | 37 ++-- .../Comparisons/EnumerablesComparison.cs | 27 +-- .../Shared/Comparisons/IndirectComparison.cs | 19 +- .../Shared/Comparisons/MembersComparison.cs | 5 +- .../Shared/Comparisons/NullableComparison.cs | 46 ++-- .../Shared/Comparisons/ObjectComparison.cs | 17 +- .../Shared/Comparisons/StringsComparison.cs | 14 +- .../Shared/CycleDetectionSet.cs | 37 ++-- .../Shared/GenericProvider.cs | 1 - .../Shared/GenericTypeBuilder.cs | 63 +++--- .../Shared/MembersProvider.cs | 2 +- .../Variables/ArgumentVariable.cs | 1 + .../Variables/ArrayItemVariable.cs | 21 +- .../Variables/EnumerableItemVariable.cs | 11 +- .../Variables/FieldMemberVariable.cs | 5 +- src/ILLightenComparer/Variables/IVariable.cs | 4 +- .../Variables/NullableVariables.cs | 19 +- .../Variables/PropertyMemberVariable.cs | 8 +- 71 files changed, 858 insertions(+), 523 deletions(-) create mode 100644 ILLightenComparer.sln.DotSettings delete mode 160000 external/illuminator create mode 100644 src/ILLightenComparer/Extensions/Functions.cs create mode 100644 src/ILLightenComparer/Extensions/MemberInfoExtensions.cs create mode 100644 src/ILLightenComparer/Extensions/MethodBuilderExtensions.cs create mode 100644 src/ILLightenComparer/Extensions/MethodInfoExtensions.cs create mode 100644 src/ILLightenComparer/Extensions/ModuleBuilderExtensions.cs create mode 100644 src/ILLightenComparer/Extensions/TypeBuilderExtensions.cs diff --git a/.gitmodules b/.gitmodules index 6c1e7765..e69de29b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "external/illuminator"] - path = external/illuminator - url = https://github.com/sgaliamov/illuminator.git diff --git a/.vscode/settings.json b/.vscode/settings.json index 439bd509..95b999dd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,8 @@ "*.sln" ], "cSpell.words": [ + "Brfalse", + "MSIL", "Redstone", "Skylake", "Struct", diff --git a/ILLightenComparer.sln b/ILLightenComparer.sln index 5b0f8fa6..5fca82c7 100644 --- a/ILLightenComparer.sln +++ b/ILLightenComparer.sln @@ -17,8 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution readme.md = readme.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Illuminator", "external\illuminator\src\Illuminator\Illuminator.csproj", "{BA1BD1BA-215E-483D-BD64-63DAA30FF778}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{15FA73AD-EF43-4228-A4F6-44E186E5C06B}" ProjectSection(SolutionItems) = preProject scripts\benchmarks.ps1 = scripts\benchmarks.ps1 @@ -34,13 +32,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{EBC9FF7F-B docs\roadmap.md = docs\roadmap.md EndProjectSection EndProject -Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Illuminator.Shared", "external\illuminator\src\Illuminator\Illuminator.Shared.shproj", "{4497D5CE-4813-46BA-868D-58D696AE81D4}" -EndProject Global - GlobalSection(SharedMSBuildProjectFiles) = preSolution - external\illuminator\src\Illuminator\Illuminator.Shared.projitems*{020c78a5-4642-42c5-900c-dd3658051481}*SharedItemsImports = 5 - external\illuminator\src\Illuminator\Illuminator.Shared.projitems*{4497d5ce-4813-46ba-868d-58d696ae81d4}*SharedItemsImports = 13 - EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU @@ -58,10 +50,6 @@ Global {52C5C857-3A08-4D74-B954-1B99FEB403D5}.Debug|Any CPU.Build.0 = Debug|Any CPU {52C5C857-3A08-4D74-B954-1B99FEB403D5}.Release|Any CPU.ActiveCfg = Release|Any CPU {52C5C857-3A08-4D74-B954-1B99FEB403D5}.Release|Any CPU.Build.0 = Release|Any CPU - {BA1BD1BA-215E-483D-BD64-63DAA30FF778}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BA1BD1BA-215E-483D-BD64-63DAA30FF778}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BA1BD1BA-215E-483D-BD64-63DAA30FF778}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BA1BD1BA-215E-483D-BD64-63DAA30FF778}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ILLightenComparer.sln.DotSettings b/ILLightenComparer.sln.DotSettings new file mode 100644 index 00000000..750651ca --- /dev/null +++ b/ILLightenComparer.sln.DotSettings @@ -0,0 +1,9 @@ + + True + True + True + USUAL_INDENT + True + True + True + True \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index a305145f..a3cee534 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -23,7 +23,7 @@ install: before_build: - pwsh: | - gitversion /l console /output buildserver /verbosity warn + gitversion /l console /output buildserver /verbosity quiet if ($env:APPVEYOR_REPO_TAG -eq $false) { Set-AppveyorBuildVariable -Name Configuration -Value debug } diff --git a/docs/deployment.md b/docs/deployment.md index 166b05b9..8781c2b6 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,15 +1,7 @@ -# Deployment process +# Development -## Preliminary statements +1. Create a new feature branch from *master*. +1. Squash and merge RP. +1. Publish [release](https://github.com/sgaliamov/il-lighten-comparer/releases/new). -1. Use GitFlow to have separate `develop` and `master` branch. -1. Version of the next package is defined by tag on `master` branch. -1. Promotion on productions must happen manually, but automatically to private `nuget` hub. - -## Release - -1. Create PR from `develop` branch to `master`. Make sure that all checks is passed. -1. Merge RP (do not squash `develop`). -1. Wait for all builds to ensure that it works after the merge. -1. Add [release notes](https://github.com/sgaliamov/il-lighten-comparer/tags) and publish the release. -1. [Deploy](https://ci.appveyor.com/environment/40781/deployments/new) to [nuget.org](https://nuget.org) if need be. +It will publish the package to [nuget.org](https://www.nuget.org/packages/ILLightenComparer/). diff --git a/external/illuminator b/external/illuminator deleted file mode 160000 index b45d397c..00000000 --- a/external/illuminator +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b45d397c592de4f9a9d2d5bdecf01a2c5eae9efc diff --git a/src/ILLightenComparer.Benchmarks/Benchmark/EqualityBenchmark.cs b/src/ILLightenComparer.Benchmarks/Benchmark/EqualityBenchmark.cs index 9400e21b..d0ced9e2 100644 --- a/src/ILLightenComparer.Benchmarks/Benchmark/EqualityBenchmark.cs +++ b/src/ILLightenComparer.Benchmarks/Benchmark/EqualityBenchmark.cs @@ -3,8 +3,8 @@ using System.Reflection.Emit; using System.Runtime.CompilerServices; using BenchmarkDotNet.Attributes; -using Illuminator.Extensions; -using static Illuminator.Functional; +using Illuminator; +using static Illuminator.Functions; namespace ILLightenComparer.Benchmarks.Benchmark { @@ -15,9 +15,9 @@ public class EqualityBenchmark private const int N = 10000; private readonly int[] _one = new int[N]; private readonly int[] _other = new int[N]; - [SuppressMessage("Code Quality", "IDE0052:Remove unread private members", Justification = "")] private bool _out; + private Func _subCompare; private Func _subNot; @@ -32,21 +32,23 @@ public void Setup() } var subCompare = new DynamicMethod("SubCompare", typeof(bool), new[] { typeof(int), typeof(int) }); - using (var il = subCompare.GetILGenerator().CreateILEmitter()) { - il.Sub(LoadArgument(0), LoadArgument(1)) - .IfFalse_S(out var equals) - .Return(0) + using (var il = subCompare.GetILGenerator().UseIlluminator()) { + il.Sub(Ldarg(0), Ldarg(1)) + .Brfalse_S(out var equals) + .Ldc_I4_0() + .Ret() .MarkLabel(equals) - .Return(1); + .Ldc_I4_1() + .Ret(); _subCompare = subCompare.CreateDelegate>(); } var subNot = new DynamicMethod("SubNot", typeof(bool), new[] { typeof(int), typeof(int) }); - using (var il = subNot.GetILGenerator().CreateILEmitter()) { - il.Sub(LoadArgument(0), LoadArgument(1)) + using (var il = subNot.GetILGenerator().UseIlluminator()) { + il.Sub(Ldarg(0), Ldarg(1)) .Not() - .Return(); + .Ret(); _subNot = subNot.CreateDelegate>(); } @@ -77,7 +79,7 @@ public void Sub() } [MethodImpl(MethodImplOptions.NoInlining)] - private static bool Sub(int a, int b) => (a - b) == 0; + private static bool Sub(int a, int b) => a - b == 0; [Benchmark] public void Equals() @@ -101,4 +103,4 @@ public void Operator() [MethodImpl(MethodImplOptions.NoInlining)] private static bool Operator(int a, int b) => a == b; } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer.Tests/ComparerTests/GenericTests.cs b/src/ILLightenComparer.Tests/ComparerTests/GenericTests.cs index 2b028a1b..587e2ea2 100644 --- a/src/ILLightenComparer.Tests/ComparerTests/GenericTests.cs +++ b/src/ILLightenComparer.Tests/ComparerTests/GenericTests.cs @@ -9,7 +9,6 @@ using FluentAssertions.Execution; using Force.DeepCloner; using ILLightenComparer.Tests.Utilities; -using Illuminator.Extensions; namespace ILLightenComparer.Tests.ComparerTests { @@ -152,7 +151,7 @@ private static void Comparisons_work_identical(IComparer referenceComparer var expected = referenceComparer.Compare(x, y).Normalize(); var actual = typedComparer.Compare(x, y).Normalize(); - var message = $"{type.DisplayName()} should be supported.\n" + var message = $"{type.FullName} should be supported.\n" + $"x: {x.ToJson()},\n" + $"y: {y.ToJson()}"; diff --git a/src/ILLightenComparer.Tests/EqualityExamples.cs b/src/ILLightenComparer.Tests/EqualityExamples.cs index a874e412..6f8e261d 100644 --- a/src/ILLightenComparer.Tests/EqualityExamples.cs +++ b/src/ILLightenComparer.Tests/EqualityExamples.cs @@ -18,6 +18,7 @@ public void Basic_usage() var equality = comparer.Equals(x, y); var hashX = comparer.GetHashCode(x); + var hashY = comparer.GetHashCode(y); using (new AssertionScope()) { diff --git a/src/ILLightenComparer.Tests/EqualityTests/GenericTests.cs b/src/ILLightenComparer.Tests/EqualityTests/GenericTests.cs index bb066972..7f8bf566 100644 --- a/src/ILLightenComparer.Tests/EqualityTests/GenericTests.cs +++ b/src/ILLightenComparer.Tests/EqualityTests/GenericTests.cs @@ -9,7 +9,6 @@ using FluentAssertions.Execution; using Force.DeepCloner; using ILLightenComparer.Tests.Utilities; -using Illuminator.Extensions; namespace ILLightenComparer.Tests.EqualityTests { @@ -166,7 +165,7 @@ private void Comparisons_work_identical(IEqualityComparer referenceCompare var expectedEquals = referenceComparer.Equals(x, y); var actualEquals = typedComparer.Equals(x, y); - var message = $"{type.DisplayName()} should be supported."; + var message = $"{type.FullName} should be supported."; actualEquals.Should().Be(expectedEquals, message); if (_compareHashes) { diff --git a/src/ILLightenComparer.Tests/EqualityTests/SampleTypesTests.cs b/src/ILLightenComparer.Tests/EqualityTests/SampleTypesTests.cs index 7050f642..6a5f4fcb 100644 --- a/src/ILLightenComparer.Tests/EqualityTests/SampleTypesTests.cs +++ b/src/ILLightenComparer.Tests/EqualityTests/SampleTypesTests.cs @@ -36,13 +36,11 @@ public void Compare_nullable_types_directly() } [Fact] - public void Compare_types_directly() - { + public void Compare_types_directly() => Parallel.ForEach(TestTypes.Types, item => { var (type, referenceComparer) = item; new GenericTests(false).GenericTest(type, referenceComparer, Constants.SmallCount); }); - } [Fact] public void Should_use_delayed_comparison() @@ -69,14 +67,12 @@ public void Should_use_delayed_comparison() } } - private static void TestCollection(Type genericCollectionType = null) - { + private static void TestCollection(Type genericCollectionType = null) => Parallel.ForEach(TestTypes.Types, item => { var (type, referenceComparer) = item; TestCollection(type, referenceComparer, genericCollectionType, false); TestCollection(type, referenceComparer, genericCollectionType, true); }); - } private static void TestNullableCollection(Type genericCollectionType = null) { diff --git a/src/ILLightenComparer.Tests/Utilities/CasualNullGenerator.cs b/src/ILLightenComparer.Tests/Utilities/CasualNullGenerator.cs index b2d3a10f..40b57b10 100644 --- a/src/ILLightenComparer.Tests/Utilities/CasualNullGenerator.cs +++ b/src/ILLightenComparer.Tests/Utilities/CasualNullGenerator.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Reflection; using AutoFixture.Kernel; -using Illuminator.Extensions; namespace ILLightenComparer.Tests.Utilities { diff --git a/src/ILLightenComparer.Tests/Utilities/FixtureBuilder.cs b/src/ILLightenComparer.Tests/Utilities/FixtureBuilder.cs index 14af7f51..f05caf1e 100644 --- a/src/ILLightenComparer.Tests/Utilities/FixtureBuilder.cs +++ b/src/ILLightenComparer.Tests/Utilities/FixtureBuilder.cs @@ -5,7 +5,6 @@ using AutoFixture; using AutoFixture.Kernel; using Force.DeepCloner; -using Illuminator.Extensions; using Xunit; namespace ILLightenComparer.Tests.Utilities diff --git a/src/ILLightenComparer.Tests/Utilities/Helper.cs b/src/ILLightenComparer.Tests/Utilities/Helper.cs index ef196fec..83a4f8fb 100644 --- a/src/ILLightenComparer.Tests/Utilities/Helper.cs +++ b/src/ILLightenComparer.Tests/Utilities/Helper.cs @@ -8,7 +8,6 @@ using FluentAssertions.Execution; using ILLightenComparer.Tests.Comparers; using ILLightenComparer.Tests.EqualityComparers; -using Illuminator.Extensions; using Xunit; namespace ILLightenComparer.Tests.Utilities diff --git a/src/ILLightenComparer.Tests/Utilities/ObjectExtensions.cs b/src/ILLightenComparer.Tests/Utilities/ObjectExtensions.cs index a942f2c3..00353770 100644 --- a/src/ILLightenComparer.Tests/Utilities/ObjectExtensions.cs +++ b/src/ILLightenComparer.Tests/Utilities/ObjectExtensions.cs @@ -6,7 +6,6 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; -using Illuminator.Extensions; using Newtonsoft.Json; namespace ILLightenComparer.Tests.Utilities @@ -34,7 +33,7 @@ public static T GetOrAddProperty(this TTarget obj, string name, Func public static Type GetGenericInterface(this Type type, Type generic) { if (!generic.IsGenericType) { - throw new ArgumentException($"{generic.DisplayName()} should be generic type.", nameof(generic)); + throw new ArgumentException($"{generic.FullName} should be generic type.", nameof(generic)); } if (type.IsInterface && type.IsGenericType && type.GetGenericTypeDefinition() == generic) { diff --git a/src/ILLightenComparer.Tests/Utilities/ObjectWalker.cs b/src/ILLightenComparer.Tests/Utilities/ObjectWalker.cs index 841efa0b..73557945 100644 --- a/src/ILLightenComparer.Tests/Utilities/ObjectWalker.cs +++ b/src/ILLightenComparer.Tests/Utilities/ObjectWalker.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using Illuminator.Extensions; namespace ILLightenComparer.Tests.Utilities { diff --git a/src/ILLightenComparer.Tests/Utilities/ReflectionExtensions.cs b/src/ILLightenComparer.Tests/Utilities/ReflectionExtensions.cs index 667fffc3..dfd4f8b3 100644 --- a/src/ILLightenComparer.Tests/Utilities/ReflectionExtensions.cs +++ b/src/ILLightenComparer.Tests/Utilities/ReflectionExtensions.cs @@ -14,5 +14,35 @@ public static Type MakeNullable(this Type type) } public static bool IsSealedType(this Type type) => type.IsValueType || type.IsSealed; + + public static bool IsNullable(this Type type) => + type.IsValueType + && type.IsGenericType + && !type.IsGenericTypeDefinition + && ReferenceEquals(type.GetGenericTypeDefinition(), typeof(Nullable<>)); + + public static bool IsPrimitive(this Type type) => + type.IsPrimitive + || type.IsEnum + || ReferenceEquals(type, typeof(string)) + // || ReferenceEquals(type, typeof(object)) // todo: 3. move extensions to separate assembly + || ReferenceEquals(type, typeof(decimal)); + + public static bool ImplementsGenericInterface(this Type type, Type generic) => type.FindGenericInterface(generic) != null; + + public static Type FindGenericInterface(this Type type, Type generic) + { + if (!generic.IsGenericType) { + throw new ArgumentException($"{generic.FullName} should be generic type.", nameof(generic)); + } + + if (type.IsInterface && type.IsGenericType && type.GetGenericTypeDefinition() == generic) { + return type; + } + + return Array.Find( + type.GetInterfaces(), + t => t.IsGenericType && generic == t.GetGenericTypeDefinition()); + } } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Abstractions/IComparisonEmitter.cs b/src/ILLightenComparer/Abstractions/IComparisonEmitter.cs index 2165f529..d5db4c0c 100644 --- a/src/ILLightenComparer/Abstractions/IComparisonEmitter.cs +++ b/src/ILLightenComparer/Abstractions/IComparisonEmitter.cs @@ -19,7 +19,10 @@ internal interface IComparisonEmitter internal static class ComparisonEmitterExtensions { - public static ILEmitterFunc Emit(this IComparisonEmitter emitter, Label next) => il => emitter.Emit(il, next); - public static ILEmitterFunc EmitCheckForResult(this IComparisonEmitter emitter, Label next) => il => emitter.EmitCheckForResult(il, next); + public static ILEmitterFunc Emit(this IComparisonEmitter emitter, Label next) => + (in ILEmitter il) => emitter.Emit(il, next); + + public static ILEmitterFunc EmitCheckForResult(this IComparisonEmitter emitter, Label next) => + (in ILEmitter il) => emitter.EmitCheckForResult(il, next); } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Abstractions/IHasherEmitter.cs b/src/ILLightenComparer/Abstractions/IHasherEmitter.cs index 9ef3b514..c7ba5223 100644 --- a/src/ILLightenComparer/Abstractions/IHasherEmitter.cs +++ b/src/ILLightenComparer/Abstractions/IHasherEmitter.cs @@ -1,35 +1,37 @@ using System.Reflection.Emit; using Illuminator; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; +using static Illuminator.Functions; namespace ILLightenComparer.Abstractions { internal interface IHasherEmitter { /// - /// Hashing logic builder. + /// Hashing logic builder. /// ILEmitter Emit(ILEmitter il); /// - /// Hashing logic builder with variable to accumulate hash. + /// Hashing logic builder with variable to accumulate hash. /// ILEmitter Emit(ILEmitter il, LocalBuilder hash); } internal static class HasherEmitterExtensions { - public static ILEmitterFunc Emit(this IHasherEmitter hasher, LocalBuilder hash) => il => hasher.Emit(il, hash); + public static ILEmitterFunc Emit(this IHasherEmitter hasher, LocalBuilder hash) => + (in ILEmitter il) => hasher.Emit(il, hash); public static ILEmitter EmitHashing(this IHasherEmitter hasher, ILEmitter il, LocalBuilder hash) { var add = Add( - ShiftLeft(LoadLocal(hash), LoadInteger(5)), - LoadLocal(hash)); + Shl(Ldloc(hash), Ldc_I4(5)), + Ldloc(hash)); return il - .Xor(add, Cast(hasher.Emit(hash))) // todo: 2. need to cast? - .Store(hash); + .Xor(add, Cast(hasher.Emit(hash))) // todo: 2. need to cast? + .Stloc(hash); } } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Comparer/CompareStaticMethodEmitter.cs b/src/ILLightenComparer/Comparer/CompareStaticMethodEmitter.cs index 94473dd2..8b1c71e6 100644 --- a/src/ILLightenComparer/Comparer/CompareStaticMethodEmitter.cs +++ b/src/ILLightenComparer/Comparer/CompareStaticMethodEmitter.cs @@ -5,9 +5,9 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; using static ILLightenComparer.Shared.CycleDetectionSet; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; +using static Illuminator.Functions; namespace ILLightenComparer.Comparer { @@ -29,7 +29,7 @@ public void Build(Type objectType, bool detecCycles, MethodBuilder staticMethodB if (needReferenceComparison) { if (!objectType.IsValueType) { - il.EmitReferenceComparison(LoadArgument(Arg.X), LoadArgument(Arg.Y), Return(0)); + il.EmitReferenceComparison(LoadArgument(Arg.X), LoadArgument(Arg.Y), Ret(0)); } else if (objectType.IsNullable()) { il.EmitCheckNullablesForValue(LoadArgumentAddress(Arg.X), LoadArgumentAddress(Arg.Y), objectType, exit); } @@ -44,22 +44,24 @@ public void Build(Type objectType, bool detecCycles, MethodBuilder staticMethodB emitter.Emit(il, exit); if (detecCycles) { - il.Execute(Remove(Arg.SetX, Arg.X, objectType)) - .Execute(Remove(Arg.SetY, Arg.Y, objectType)); + il.Emit(Remove(Arg.SetX, Arg.X, objectType)) + .Emit(Remove(Arg.SetY, Arg.Y, objectType)); } - il.Execute(emitter.EmitCheckForResult(exit)) + il.Emit(emitter.EmitCheckForResult(exit)) .MarkLabel(exit) - .Return(0); + .Ret(0); } // no need detect cycle as flow goes outside context public bool NeedCreateCycleDetectionSets(Type objectType) => !objectType.IsComparable(); - private static void EmitCycleDetection(ILEmitter il, Type objectType) => il - .AreSame(LoadInteger(0), Or(TryAdd(Arg.SetX, Arg.X, objectType), TryAdd(Arg.SetY, Arg.Y, objectType))) - .IfFalse_S(out var next) - .Return(Sub(GetCount(Arg.SetX), GetCount(Arg.SetY))) - .MarkLabel(next); + private static void EmitCycleDetection(ILEmitter il, Type objectType) => + il.Ceq(Ldc_I4(0), + Or(TryAdd(Arg.SetX, Arg.X, objectType), + TryAdd(Arg.SetY, Arg.Y, objectType))) + .Brfalse_S(out var next) + .Ret(Sub(GetCount(Arg.SetX), GetCount(Arg.SetY))) + .MarkLabel(next); } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Comparer/ComparisonResolver.cs b/src/ILLightenComparer/Comparer/ComparisonResolver.cs index 97a54c26..4d1bc4a9 100644 --- a/src/ILLightenComparer/Comparer/ComparisonResolver.cs +++ b/src/ILLightenComparer/Comparer/ComparisonResolver.cs @@ -5,10 +5,10 @@ using ILLightenComparer.Abstractions; using ILLightenComparer.Comparer.Comparisons; using ILLightenComparer.Config; +using ILLightenComparer.Extensions; using ILLightenComparer.Shared; using ILLightenComparer.Shared.Comparisons; using ILLightenComparer.Variables; -using Illuminator.Extensions; namespace ILLightenComparer.Comparer { diff --git a/src/ILLightenComparer/Comparer/Comparisons/ComparablesComparison.cs b/src/ILLightenComparer/Comparer/Comparisons/ComparablesComparison.cs index 7822ba9b..b645e50e 100644 --- a/src/ILLightenComparer/Comparer/Comparisons/ComparablesComparison.cs +++ b/src/ILLightenComparer/Comparer/Comparisons/ComparablesComparison.cs @@ -4,7 +4,6 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Comparer.Comparisons { @@ -36,20 +35,19 @@ public ILEmitter Emit(ILEmitter il, Label gotoNext) _variable.LoadAddress(il, Arg.X); _variable.Load(il, Arg.Y); } else { - _variable.Load(il, Arg.X).Store(variableType, out var x); - _variable.Load(il, Arg.Y) - .Store(variableType, out var y) - .LoadLocal(x) - .IfTrue_S(out var call) - .LoadLocal(y) - .IfFalse_S(gotoNext) - .Return(-1) + _variable.Load(il, Arg.X).Stloc(variableType, out var x); + _variable.Load(il, Arg.Y).Stloc(variableType, out var y) + .Ldloc(x) + .Brtrue_S(out var call) + .Ldloc(y) + .Brfalse_S(gotoNext) + .Ret(-1) .MarkLabel(call) - .LoadLocal(x) - .LoadLocal(y); + .Ldloc(x) + .Ldloc(y); } - return il.Call(_compareToMethod); + return il.CallMethod(_compareToMethod); } public ILEmitter EmitCheckForResult(ILEmitter il, Label next) => il.EmitReturnIfTruthy(next); diff --git a/src/ILLightenComparer/Comparer/Comparisons/IntegralsComparison.cs b/src/ILLightenComparer/Comparer/Comparisons/IntegralsComparison.cs index f7bd8b4f..41b91d70 100644 --- a/src/ILLightenComparer/Comparer/Comparisons/IntegralsComparison.cs +++ b/src/ILLightenComparer/Comparer/Comparisons/IntegralsComparison.cs @@ -3,7 +3,6 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Comparer.Comparisons { diff --git a/src/ILLightenComparer/Comparer/CustomEmitters.cs b/src/ILLightenComparer/Comparer/CustomEmitters.cs index 05be396e..4beb23b4 100644 --- a/src/ILLightenComparer/Comparer/CustomEmitters.cs +++ b/src/ILLightenComparer/Comparer/CustomEmitters.cs @@ -2,66 +2,64 @@ using System.Reflection.Emit; using ILLightenComparer.Extensions; using Illuminator; -using static Illuminator.Functional; +using static Illuminator.Functions; namespace ILLightenComparer.Comparer { internal static class CustomEmitters { /// - /// Returns non zero value if stack has it, otherwise got to . + /// Returns non zero value if stack has it, otherwise got to . /// - public static ILEmitter EmitReturnIfTruthy(this ILEmitter il, Label next) => il - .Store(typeof(int), out var result) - .LoadLocal(result) - .IfFalse(next) - .Return(LoadLocal(result)); + public static ILEmitter EmitReturnIfTruthy(this ILEmitter il, Label next) => + il.Stloc(typeof(int), out var result) + .Ldloc(result) + .Brfalse(next) + .Ret(Ldloc(result)); - public static ILEmitter EmitCheckIfLoopsAreDone(this ILEmitter il, LocalBuilder isDoneX, LocalBuilder isDoneY, Label gotoNext) => il - .LoadLocal(isDoneX) - .IfFalse_S(out var checkIsDoneY) - .LoadLocal(isDoneY) - .IfFalse_S(out var returnM1) - .GoTo(gotoNext) - .MarkLabel(returnM1) - .Return(-1) - .MarkLabel(checkIsDoneY) - .LoadLocal(isDoneY) - .IfFalse_S(out var compare) - .Return(1) - .MarkLabel(compare); + public static ILEmitter EmitCheckIfLoopsAreDone(this ILEmitter il, LocalBuilder isDoneX, LocalBuilder isDoneY, Label gotoNext) => + il.Ldloc(isDoneX) + .Brfalse_S(out var checkIsDoneY) + .Ldloc(isDoneY) + .Brfalse_S(out var returnM1) + .Br(gotoNext) + .MarkLabel(returnM1) + .Ret(-1) + .MarkLabel(checkIsDoneY) + .Ldloc(isDoneY) + .Brfalse_S(out var compare) + .Ret(1) + .MarkLabel(compare); - public static ILEmitter EmitReferenceComparison(this ILEmitter il, ILEmitterFunc loadX, ILEmitterFunc loadY, ILEmitterFunc ifEqual) => il - .IfNotEqual_Un_S(loadX, loadY, out var checkX) - .Execute(ifEqual) - .MarkLabel(checkX) - .Execute(loadX) - .IfTrue_S(out var checkY) - .Return(-1) - .MarkLabel(checkY) - .Execute(loadY) - .IfTrue_S(out var next) - .Return(1) - .MarkLabel(next); + public static ILEmitter EmitReferenceComparison(this ILEmitter il, ILEmitterFunc loadX, ILEmitterFunc loadY, ILEmitterFunc ifEqual) => + il.Bne_Un_S(loadX, loadY, out var checkX) + .Emit(ifEqual) + .MarkLabel(checkX) + .Emit(loadX) + .Brtrue_S(out var checkY) + .Ret(-1) + .MarkLabel(checkY) + .Emit(loadY) + .Brtrue_S(out var next) + .Ret(1) + .MarkLabel(next); public static ILEmitter EmitCheckNullablesForValue(this ILEmitter il, ILEmitterFunc nullableX, ILEmitterFunc nullableY, Type nullableType, Label ifBothNull) { var hasValueMethod = nullableType.GetPropertyGetter("HasValue"); - return il.Execute(nullableY) - .Call(hasValueMethod) - .Store(typeof(bool), out var secondHasValue) - .Execute(nullableX) - .Call(hasValueMethod) - .IfTrue_S(out var ifFirstHasValue) - .LoadLocal(secondHasValue) - .IfFalse(ifBothNull) - .Return(-1) + return il.CallMethod(hasValueMethod, nullableY) + .Stloc(typeof(bool), out var secondHasValue) + .CallMethod(hasValueMethod, nullableX) + .Brtrue_S(out var ifFirstHasValue) + .Ldloc(secondHasValue) + .Brfalse(ifBothNull) + .Ret(-1) .MarkLabel(ifFirstHasValue) - .LoadLocal(secondHasValue) - .IfTrue_S(out var next) - .Return(1) + .Ldloc(secondHasValue) + .Brtrue_S(out var next) + .Ret(1) .MarkLabel(next); } } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Config/ConfigurationProvider.cs b/src/ILLightenComparer/Config/ConfigurationProvider.cs index 5d9f8f82..83aa4206 100644 --- a/src/ILLightenComparer/Config/ConfigurationProvider.cs +++ b/src/ILLightenComparer/Config/ConfigurationProvider.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; using ILLightenComparer.Extensions; using ILLightenComparer.Shared; -using Illuminator.Extensions; namespace ILLightenComparer.Config { diff --git a/src/ILLightenComparer/Equality/Comparisons/BacisEqualityComparison.cs b/src/ILLightenComparer/Equality/Comparisons/BacisEqualityComparison.cs index b29ad0fb..718b9b5b 100644 --- a/src/ILLightenComparer/Equality/Comparisons/BacisEqualityComparison.cs +++ b/src/ILLightenComparer/Equality/Comparisons/BacisEqualityComparison.cs @@ -4,7 +4,6 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Equality.Comparisons { @@ -30,10 +29,13 @@ public static BacisEqualityComparison Create(IVariable variable) return null; } - public ILEmitter Emit(ILEmitter il, Label _) => il.Call( - _equalityMethod, - _variable.VariableType.IsValueType ? _variable.LoadAddress(Arg.X) : _variable.Load(Arg.X), - _variable.Load(Arg.Y)); + public ILEmitter Emit(ILEmitter il, Label _) => + il.CallMethod( + _equalityMethod, + _variable.VariableType.IsValueType + ? _variable.LoadAddress(Arg.X) + : _variable.Load(Arg.X), + _variable.Load(Arg.Y)); public ILEmitter EmitCheckForResult(ILEmitter il, Label next) => il.EmitReturnIfFalsy(next); } diff --git a/src/ILLightenComparer/Equality/Comparisons/CeqEqualityComparison.cs b/src/ILLightenComparer/Equality/Comparisons/CeqEqualityComparison.cs index bc0e26a6..ae041568 100644 --- a/src/ILLightenComparer/Equality/Comparisons/CeqEqualityComparison.cs +++ b/src/ILLightenComparer/Equality/Comparisons/CeqEqualityComparison.cs @@ -3,7 +3,6 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Equality.Comparisons { @@ -22,7 +21,7 @@ public static CeqEqualityComparison Create(IVariable variable) return null; } - public ILEmitter Emit(ILEmitter il, Label _) => il.AreSame(_variable.Load(Arg.X), _variable.Load(Arg.Y)); + public ILEmitter Emit(ILEmitter il, Label _) => il.Ceq(_variable.Load(Arg.X), _variable.Load(Arg.Y)); public ILEmitter EmitCheckForResult(ILEmitter il, Label next) => il.EmitReturnIfFalsy(next); } diff --git a/src/ILLightenComparer/Equality/Comparisons/OperatorEqualityComparison.cs b/src/ILLightenComparer/Equality/Comparisons/OperatorEqualityComparison.cs index 8f5b5206..fc5360f1 100644 --- a/src/ILLightenComparer/Equality/Comparisons/OperatorEqualityComparison.cs +++ b/src/ILLightenComparer/Equality/Comparisons/OperatorEqualityComparison.cs @@ -1,9 +1,9 @@ using System.Reflection; using System.Reflection.Emit; using ILLightenComparer.Abstractions; +using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Equality.Comparisons { @@ -22,6 +22,7 @@ public static OperatorEqualityComparison Create(IVariable variable) { var variableType = variable.VariableType.GetUnderlyingType(); var equalityMethod = variableType.GetMethod("op_Equality", BindingFlags.Public | BindingFlags.Static); + if (equalityMethod != null) { return new OperatorEqualityComparison(variable, equalityMethod); } @@ -29,10 +30,11 @@ public static OperatorEqualityComparison Create(IVariable variable) return null; } - public ILEmitter Emit(ILEmitter il, Label _) => il.Call( - _equalityMethod, - _variable.Load(Arg.X), - _variable.Load(Arg.Y)); + public ILEmitter Emit(ILEmitter il, Label _) => + il.CallMethod( + _equalityMethod, + _variable.Load(Arg.X), + _variable.Load(Arg.Y)); public ILEmitter EmitCheckForResult(ILEmitter il, Label next) => il.EmitReturnIfFalsy(next); } diff --git a/src/ILLightenComparer/Equality/CustomEmitters.cs b/src/ILLightenComparer/Equality/CustomEmitters.cs index b819250e..df136057 100644 --- a/src/ILLightenComparer/Equality/CustomEmitters.cs +++ b/src/ILLightenComparer/Equality/CustomEmitters.cs @@ -11,53 +11,51 @@ internal static class CustomEmitters /// Returns zero if stack has zero value, otherwise got to . /// public static ILEmitter EmitReturnIfFalsy(this ILEmitter il, Label next) => il - .IfTrue(next) - .Return(0); + .Brtrue(next) + .Ret(0); public static ILEmitter EmitCheckIfLoopsAreDone(this ILEmitter il, LocalBuilder isDoneX, LocalBuilder isDoneY, Label gotoNext) => il - .LoadLocal(isDoneX) - .IfFalse_S(out var checkIsDoneY) - .LoadLocal(isDoneY) - .IfFalse_S(out var returnFalse) - .GoTo(gotoNext) + .Ldloc(isDoneX) + .Brfalse_S(out var checkIsDoneY) + .Ldloc(isDoneY) + .Brfalse_S(out var returnFalse) + .Br(gotoNext) .MarkLabel(returnFalse) - .Return(0) + .Ret(0) .MarkLabel(checkIsDoneY) - .LoadLocal(isDoneY) - .IfFalse_S(out var next) - .Return(0) + .Ldloc(isDoneY) + .Brfalse_S(out var next) + .Ret(0) .MarkLabel(next); public static ILEmitter EmitReferenceComparison(this ILEmitter il, ILEmitterFunc loadX, ILEmitterFunc loadY, ILEmitterFunc ifEqual) => il - .IfNotEqual_Un_S(loadX, loadY, out var checkX) - .Execute(ifEqual) + .Bne_Un_S(loadX, loadY, out var checkX) + .Emit(ifEqual) .MarkLabel(checkX) - .Execute(loadX) - .IfTrue_S(out var checkY) - .Return(0) + .Emit(loadX) + .Brtrue_S(out var checkY) + .Ret(0) .MarkLabel(checkY) - .Execute(loadY) - .IfTrue_S(out var next) - .Return(0) + .Emit(loadY) + .Brtrue_S(out var next) + .Ret(0) .MarkLabel(next); public static ILEmitter EmitCheckNullablesForValue(this ILEmitter il, ILEmitterFunc nullableX, ILEmitterFunc nullableY, Type nullableType, Label ifBothNull) { var hasValueMethod = nullableType.GetPropertyGetter("HasValue"); - return il.Execute(nullableY) - .Call(hasValueMethod) - .Store(typeof(bool), out var secondHasValue) - .Execute(nullableX) - .Call(hasValueMethod) - .IfTrue_S(out var ifFirstHasValue) - .LoadLocal(secondHasValue) - .IfFalse(ifBothNull) - .Return(0) + return il.CallMethod(hasValueMethod, nullableY) + .Stloc(typeof(bool), out var secondHasValue) + .CallMethod(hasValueMethod, nullableX) + .Brtrue_S(out var ifFirstHasValue) + .Ldloc(secondHasValue) + .Brfalse(ifBothNull) + .Ret(0) .MarkLabel(ifFirstHasValue) - .LoadLocal(secondHasValue) - .IfTrue_S(out var next) - .Return(0) + .Ldloc(secondHasValue) + .Brtrue_S(out var next) + .Ret(0) .MarkLabel(next); } } diff --git a/src/ILLightenComparer/Equality/EqualityContext.cs b/src/ILLightenComparer/Equality/EqualityContext.cs index 88a479fc..24b061ba 100644 --- a/src/ILLightenComparer/Equality/EqualityContext.cs +++ b/src/ILLightenComparer/Equality/EqualityContext.cs @@ -5,7 +5,6 @@ using ILLightenComparer.Config; using ILLightenComparer.Extensions; using ILLightenComparer.Shared; -using Illuminator.Extensions; namespace ILLightenComparer.Equality { diff --git a/src/ILLightenComparer/Equality/EqualityResolver.cs b/src/ILLightenComparer/Equality/EqualityResolver.cs index 394f4306..17c0538f 100644 --- a/src/ILLightenComparer/Equality/EqualityResolver.cs +++ b/src/ILLightenComparer/Equality/EqualityResolver.cs @@ -10,7 +10,7 @@ using ILLightenComparer.Shared.Comparisons; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; +using ILLightenComparer.Extensions; namespace ILLightenComparer.Equality { @@ -52,7 +52,7 @@ public EqualityResolver( }; } - public void EmitCheckForIntermediateResult(ILEmitter il, Label next) => il.IfTrue(next).Return(0); + public void EmitCheckForIntermediateResult(ILEmitter il, Label next) => il.Brtrue(next).Ret(0); public IComparisonEmitter GetComparisonEmitter(IVariable variable) { diff --git a/src/ILLightenComparer/Equality/EqualsStaticMethodEmitter.cs b/src/ILLightenComparer/Equality/EqualsStaticMethodEmitter.cs index efd0bc83..fc7ebf15 100644 --- a/src/ILLightenComparer/Equality/EqualsStaticMethodEmitter.cs +++ b/src/ILLightenComparer/Equality/EqualsStaticMethodEmitter.cs @@ -2,11 +2,12 @@ using System.Collections.Generic; using System.Reflection.Emit; using ILLightenComparer.Abstractions; +using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; using static ILLightenComparer.Shared.CycleDetectionSet; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; +using static Illuminator.Functions; namespace ILLightenComparer.Equality { @@ -26,7 +27,7 @@ public void Build(Type objectType, bool detecCycles, MethodBuilder staticMethodB if (needReferenceComparison) { if (!objectType.IsValueType) { - il.EmitReferenceComparison(LoadArgument(Arg.X), LoadArgument(Arg.Y), Return(1)); + il.EmitReferenceComparison(LoadArgument(Arg.X), LoadArgument(Arg.Y), Ret(1)); } else if (objectType.IsNullable()) { il.EmitCheckNullablesForValue(LoadArgumentAddress(Arg.X), LoadArgumentAddress(Arg.Y), objectType, exit); } @@ -41,21 +42,21 @@ public void Build(Type objectType, bool detecCycles, MethodBuilder staticMethodB emitter.Emit(il, exit); if (detecCycles) { - il.Execute(Remove(Arg.SetX, Arg.X, objectType)) - .Execute(Remove(Arg.SetY, Arg.Y, objectType)); + il.Emit(Remove(Arg.SetX, Arg.X, objectType)) + .Emit(Remove(Arg.SetY, Arg.Y, objectType)); } - il.Execute(emitter.EmitCheckForResult(exit)) + il.Emit(emitter.EmitCheckForResult(exit)) .MarkLabel(exit) - .Return(1); + .Ret(1); } public bool NeedCreateCycleDetectionSets(Type objectType) => true; private static void EmitCycleDetection(ILEmitter il, Type objectType) => il - .AreSame(LoadInteger(0), Or(TryAdd(Arg.SetX, Arg.X, objectType), TryAdd(Arg.SetY, Arg.Y, objectType))) - .IfFalse_S(out var next) - .Return(AreSame(GetCount(Arg.SetX), GetCount(Arg.SetY))) + .Ceq(Ldc_I4(0), Or(TryAdd(Arg.SetX, Arg.X, objectType), TryAdd(Arg.SetY, Arg.Y, objectType))) + .Brfalse_S(out var next) + .Ret(Ceq(GetCount(Arg.SetX), GetCount(Arg.SetY))) .MarkLabel(next); } } diff --git a/src/ILLightenComparer/Equality/GetHashCodeStaticMethodEmitter.cs b/src/ILLightenComparer/Equality/GetHashCodeStaticMethodEmitter.cs index 8e1d4406..07552d2a 100644 --- a/src/ILLightenComparer/Equality/GetHashCodeStaticMethodEmitter.cs +++ b/src/ILLightenComparer/Equality/GetHashCodeStaticMethodEmitter.cs @@ -1,20 +1,23 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Reflection.Emit; using ILLightenComparer.Abstractions; +using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; using static ILLightenComparer.Shared.CycleDetectionSet; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; namespace ILLightenComparer.Equality { internal sealed class GetHashCodeStaticMethodEmitter : IStaticMethodEmitter { private readonly HasherResolver _resolver; + private static readonly MethodInfo _getHashCodeMethod = typeof(int).GetMethod(nameof(GetHashCode)); public GetHashCodeStaticMethodEmitter(HasherResolver resolver) => _resolver = resolver; + static GetHashCodeStaticMethodEmitter() { } public void Build(Type objectType, bool detecCycles, MethodBuilder staticMethodBuilder) { @@ -25,8 +28,8 @@ public void Build(Type objectType, bool detecCycles, MethodBuilder staticMethodB if (needNullCheck) { il.LoadArgument(Arg.Input) - .IfTrue_S(out var next) - .Return(0) + .Brtrue_S(out var next) + .Ret(0) .MarkLabel(next); } @@ -37,19 +40,19 @@ public void Build(Type objectType, bool detecCycles, MethodBuilder staticMethodB _resolver.GetHasherEmitter(new ArgumentVariable(objectType)).Emit(il); if (detecCycles) { - il.Execute(Remove(Arg.CycleSet, Arg.Input, objectType)); + il.Emit(Remove(Arg.CycleSet, Arg.Input, objectType)); } - il.Return(); + il.Ret(); } public bool NeedCreateCycleDetectionSets(Type _) => true; - private static void EmitCycleDetection(ILEmitter il, Type objectType) => il - .IfTrue_S(TryAdd(Arg.CycleSet, Arg.Input, objectType), out var next) - .Execute(GetCount(Arg.CycleSet)) - .Store(typeof(int), out var count) - .Return(Call(typeof(int).GetMethod(nameof(GetHashCode)), LoadCaller(count))) - .MarkLabel(next); + private static void EmitCycleDetection(ILEmitter il, Type objectType) => + il.Brtrue_S(TryAdd(Arg.CycleSet, Arg.Input, objectType), out var next) + .Emit(GetCount(Arg.CycleSet)) + .Stloc(typeof(int), out var count) + .Ret(CallMethod(_getHashCodeMethod, LoadCaller(count))) + .MarkLabel(next); } } diff --git a/src/ILLightenComparer/Equality/HasherResolver.cs b/src/ILLightenComparer/Equality/HasherResolver.cs index 2549ddad..a297029b 100644 --- a/src/ILLightenComparer/Equality/HasherResolver.cs +++ b/src/ILLightenComparer/Equality/HasherResolver.cs @@ -4,9 +4,9 @@ using ILLightenComparer.Abstractions; using ILLightenComparer.Config; using ILLightenComparer.Equality.Hashers; +using ILLightenComparer.Extensions; using ILLightenComparer.Shared; using ILLightenComparer.Variables; -using Illuminator.Extensions; namespace ILLightenComparer.Equality { diff --git a/src/ILLightenComparer/Equality/Hashers/ArrayHashEmitter.cs b/src/ILLightenComparer/Equality/Hashers/ArrayHashEmitter.cs index 48454736..070b4233 100644 --- a/src/ILLightenComparer/Equality/Hashers/ArrayHashEmitter.cs +++ b/src/ILLightenComparer/Equality/Hashers/ArrayHashEmitter.cs @@ -5,14 +5,14 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using static Illuminator.Functional; +using static Illuminator.Functions; namespace ILLightenComparer.Equality.Hashers { internal sealed class ArrayHashEmitter { - private readonly IVariable _variable; private readonly HasherResolver _resolver; + private readonly IVariable _variable; public ArrayHashEmitter(HasherResolver resolver, IVariable variable) { @@ -22,16 +22,16 @@ public ArrayHashEmitter(HasherResolver resolver, IVariable variable) public ILEmitter Emit(ILEmitter il, Type arrayType, LocalBuilder array, LocalBuilder hash) { - il.LoadInteger(0) // start loop - .Store(typeof(int), out var index) + il.Ldc_I4(0) // start loop + .Stloc(typeof(int), out var index) .EmitArrayLength(arrayType, array, out var count) .DefineLabel(out var loopStart) .DefineLabel(out var loopEnd); using (il.LocalsScope()) { il.MarkLabel(loopStart) - .IfNotEqual_Un_S(LoadLocal(index), LoadLocal(count), out var next) - .GoTo(loopEnd) + .Bne_Un_S(Ldloc(index), Ldloc(count), out var next) + .Br(loopEnd) .MarkLabel(next); } @@ -42,12 +42,12 @@ public ILEmitter Emit(ILEmitter il, Type arrayType, LocalBuilder array, LocalBui _resolver .GetHasherEmitter(itemVariable) .EmitHashing(il, hash) - .Add(LoadLocal(index), LoadInteger(1)) - .Store(index) - .GoTo(loopStart); + .Add(Ldloc(index), Ldc_I4(1)) + .Stloc(index) + .Br(loopStart); } - return il.MarkLabel(loopEnd).LoadLocal(hash); + return il.MarkLabel(loopEnd).Ldloc(hash); } } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Equality/Hashers/ArrayHasher.cs b/src/ILLightenComparer/Equality/Hashers/ArrayHasher.cs index 1177c103..5e403863 100644 --- a/src/ILLightenComparer/Equality/Hashers/ArrayHasher.cs +++ b/src/ILLightenComparer/Equality/Hashers/ArrayHasher.cs @@ -4,7 +4,7 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using static Illuminator.Functional; +using static Illuminator.Functions; namespace ILLightenComparer.Equality.Hashers { @@ -33,20 +33,20 @@ public static ArrayHasher Create(HasherResolver resolver, IConfigurationProvider return null; } - public ILEmitter Emit(ILEmitter il) => il - .LoadLong(_configuration.HashSeed) - .Store(typeof(long), out var hash) - .Execute(this.Emit(hash)); + public ILEmitter Emit(ILEmitter il) => + il.Ldc_I8(_configuration.HashSeed) + .Stloc(typeof(long), out var hash) + .Emit(this.Emit(hash)); public ILEmitter Emit(ILEmitter il, LocalBuilder hash) { var arrayType = _variable.VariableType; - il.Execute(_variable.Load(Arg.Input)) // load array - .Store(arrayType, out var array) - .IfTrue_S(LoadLocal(array), out var begin) - .LoadInteger(0) - .GoTo(out var end) + il.Emit(_variable.Load(Arg.Input)) // load array + .Stloc(arrayType, out var array) + .Brtrue_S(Ldloc(array), out var begin) + .Ldc_I4(0) + .Br(out var end) .MarkLabel(begin); if (_configuration.IgnoreCollectionOrder) { diff --git a/src/ILLightenComparer/Equality/Hashers/BasicHasher.cs b/src/ILLightenComparer/Equality/Hashers/BasicHasher.cs index 06fb7519..c12bc6b2 100644 --- a/src/ILLightenComparer/Equality/Hashers/BasicHasher.cs +++ b/src/ILLightenComparer/Equality/Hashers/BasicHasher.cs @@ -4,7 +4,6 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Equality.Hashers { @@ -28,9 +27,12 @@ public static BasicHasher Create(IVariable variable) return null; } - public ILEmitter Emit(ILEmitter il) => il.Call( - _getHashMethod, - _variable.VariableType.IsValueType ? _variable.LoadAddress(Arg.Input) : _variable.Load(Arg.Input)); + public ILEmitter Emit(ILEmitter il) => + il.CallMethod( + _getHashMethod, + _variable.VariableType.IsValueType + ? _variable.LoadAddress(Arg.Input) + : _variable.Load(Arg.Input)); public ILEmitter Emit(ILEmitter il, LocalBuilder _) => Emit(il); } diff --git a/src/ILLightenComparer/Equality/Hashers/EnumerablesHasher.cs b/src/ILLightenComparer/Equality/Hashers/EnumerablesHasher.cs index d5adeb35..8fa1c335 100644 --- a/src/ILLightenComparer/Equality/Hashers/EnumerablesHasher.cs +++ b/src/ILLightenComparer/Equality/Hashers/EnumerablesHasher.cs @@ -9,8 +9,8 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; +using static Illuminator.Functions; namespace ILLightenComparer.Equality.Hashers { @@ -61,21 +61,21 @@ public ILEmitter Emit(ILEmitter il) var config = _configuration.Get(_variable.OwnerType); return il - .LoadLong(config.HashSeed) - .Store(typeof(long), out var hash) - .Execute(this.Emit(hash)); + .Ldc_I8(config.HashSeed) + .Stloc(typeof(long), out var hash) + .Emit(this.Emit(hash)); } public ILEmitter Emit(ILEmitter il, LocalBuilder hash) { - il.Execute(_variable.Load(Arg.Input)) - .Store(_variable.VariableType, out var enumerable) + il.Emit(_variable.Load(Arg.Input)) + .Stloc(_variable.VariableType, out var enumerable) .DefineLabel(out var end); if (!_variable.VariableType.IsValueType) { - il.IfTrue_S(LoadLocal(enumerable), out var begin) - .LoadInteger(0) - .GoTo(end) + il.Brtrue_S(Ldloc(enumerable), out var begin) + .Ldc_I4(0) + .Br(end) .MarkLabel(begin); } @@ -83,14 +83,14 @@ public ILEmitter Emit(ILEmitter il, LocalBuilder hash) return EmitHashAsSortedArray(il, enumerable, hash).MarkLabel(end); } - il.Call(_getEnumeratorMethod, LoadCaller(enumerable)) - .Store(_enumeratorType, out var enumerator) + il.CallMethod(_getEnumeratorMethod, LoadCaller(enumerable)) + .Stloc(_enumeratorType, out var enumerator) .DefineLabel(out var loopStart); if (!_enumeratorType.IsValueType) { - il.IfTrue_S(LoadLocal(enumerator), loopStart) - .LoadInteger(0) - .GoTo(end); + il.Brtrue_S(Ldloc(enumerator), loopStart) + .Ldc_I4(0) + .Br(end); } // todo: 1. think how to use try/finally block @@ -104,7 +104,7 @@ public ILEmitter Emit(ILEmitter il, LocalBuilder hash) //il.EndExceptionBlock(); - return il.LoadLocal(hash).MarkLabel(end); + return il.Ldloc(hash).MarkLabel(end); } private ILEmitter EmitHashAsSortedArray(ILEmitter il, LocalBuilder enumerable, LocalBuilder hash) @@ -124,8 +124,8 @@ private void Loop(ILEmitter il, LocalBuilder enumerator, Label loopStart, LocalB using (il.LocalsScope()) { il.MarkLabel(loopStart) - .IfTrue_S(Call(_moveNextMethod, LoadCaller(enumerator)), out var next) - .GoTo(loopEnd) + .Brtrue_S(CallMethod(_moveNextMethod, LoadCaller(enumerator)), out var next) + .Br(loopEnd) .MarkLabel(next); } @@ -136,7 +136,7 @@ private void Loop(ILEmitter il, LocalBuilder enumerator, Label loopStart, LocalB _resolver .GetHasherEmitter(itemVariable) .EmitHashing(il, hash) - .GoTo(loopStart) + .Br(loopStart) .MarkLabel(loopEnd); } } diff --git a/src/ILLightenComparer/Equality/Hashers/IndirectHasher.cs b/src/ILLightenComparer/Equality/Hashers/IndirectHasher.cs index 55aaeb60..41238a5e 100644 --- a/src/ILLightenComparer/Equality/Hashers/IndirectHasher.cs +++ b/src/ILLightenComparer/Equality/Hashers/IndirectHasher.cs @@ -2,9 +2,10 @@ using System.Reflection.Emit; using ILLightenComparer.Abstractions; using ILLightenComparer.Extensions; +using ILLightenComparer.Shared; using ILLightenComparer.Variables; using Illuminator; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; namespace ILLightenComparer.Equality.Hashers { @@ -43,11 +44,12 @@ public static IndirectHasher Create(EqualityContext context, IVariable variable) return new IndirectHasher(hashMethod, variable); } - public ILEmitter Emit(ILEmitter il) => il.Call( - _hashMethod, - LoadArgument(Arg.Context), - _variable.Load(Arg.Input), - LoadArgument(Arg.CycleSet)); + public ILEmitter Emit(ILEmitter il) => + il.LoadArgument(Arg.Context) + .CallMethod( + _hashMethod, + _variable.Load(Arg.Input), + LoadArgument(Arg.CycleSet)); public ILEmitter Emit(ILEmitter il, LocalBuilder _) => Emit(il); } diff --git a/src/ILLightenComparer/Equality/Hashers/MembersHasher.cs b/src/ILLightenComparer/Equality/Hashers/MembersHasher.cs index 8a290866..1f603f07 100644 --- a/src/ILLightenComparer/Equality/Hashers/MembersHasher.cs +++ b/src/ILLightenComparer/Equality/Hashers/MembersHasher.cs @@ -46,9 +46,9 @@ public ILEmitter Emit(ILEmitter il) var config = _configuration.Get(_variable.OwnerType); return il - .LoadLong(config.HashSeed) - .Store(typeof(long), out var hash) - .Execute(this.Emit(hash)); + .Ldc_I8(config.HashSeed) + .Stloc(typeof(long), out var hash) + .Emit(this.Emit(hash)); } public ILEmitter Emit(ILEmitter il, LocalBuilder hash) @@ -64,7 +64,7 @@ public ILEmitter Emit(ILEmitter il, LocalBuilder hash) } // todo: 1. why cast only for members hasher? - return il.LoadLocal(hash).Cast(typeof(int)); // todo: 1. test overflow + return il.Ldloc(hash).Cast(typeof(int)); // todo: 1. test overflow } } } \ No newline at end of file diff --git a/src/ILLightenComparer/Equality/Hashers/NullableHasher.cs b/src/ILLightenComparer/Equality/Hashers/NullableHasher.cs index 54d29da6..1bce66df 100644 --- a/src/ILLightenComparer/Equality/Hashers/NullableHasher.cs +++ b/src/ILLightenComparer/Equality/Hashers/NullableHasher.cs @@ -5,7 +5,7 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; +using static ILLightenComparer.Extensions.Functions; namespace ILLightenComparer.Equality.Hashers { @@ -37,18 +37,17 @@ public ILEmitter Emit(ILEmitter il) _variable .Load(il, Arg.Input) - .Store(variableType, out var nullable) - .LoadAddress(nullable) - .Call(_hasValueMethod) - .IfTrue_S(out var next) - .LoadInteger(0) - .GoTo(out var exit) + .Stloc(variableType, out var nullable) + .CallMethod(_hasValueMethod, LoadLocalAddress(nullable)) + .Brtrue_S(out var next) + .Ldc_I4(0) + .Br(out var exit) .MarkLabel(next); var nullableVariable = new NullableVariables( variableType, _variable.OwnerType, - new Dictionary(1) { [Arg.Input] = nullable }); + new Dictionary(1) { [Arg.Input] = nullable }); return _resolver .GetHasherEmitter(nullableVariable) diff --git a/src/ILLightenComparer/Equality/Hashers/StringHasher.cs b/src/ILLightenComparer/Equality/Hashers/StringHasher.cs index 2d57c742..4c2af95a 100644 --- a/src/ILLightenComparer/Equality/Hashers/StringHasher.cs +++ b/src/ILLightenComparer/Equality/Hashers/StringHasher.cs @@ -5,7 +5,8 @@ using ILLightenComparer.Config; using ILLightenComparer.Variables; using Illuminator; -using static Illuminator.Functional; +using static Illuminator.Functions; +using ILLightenComparer.Extensions; namespace ILLightenComparer.Equality.Hashers { @@ -34,15 +35,18 @@ public static StringHasher Create(IConfigurationProvider configuration, IVariabl return null; } - public ILEmitter Emit(ILEmitter il) => _variable - .Load(il, Arg.Input) - .Store(typeof(string), out var local) - .IfFalse_S(LoadLocal(local), out var zero) - .Call(GetHashCodeMethod, LoadLocal(local), LoadInteger(_stringComparison)) - .GoTo_S(out var next) - .MarkLabel(zero) - .LoadInteger(0) - .MarkLabel(next); + public ILEmitter Emit(ILEmitter il) => + _variable.Load(il, Arg.Input) + .Stloc(typeof(string), out var local) + .Brfalse_S(Ldloc(local), out var zero) + .CallMethod( + GetHashCodeMethod, + Ldloc(local), + Ldc_I4(_stringComparison)) + .Br_S(out var next) + .MarkLabel(zero) + .Ldc_I4(0) + .MarkLabel(next); public ILEmitter Emit(ILEmitter il, LocalBuilder _) => Emit(il); } diff --git a/src/ILLightenComparer/Extensions/Functions.cs b/src/ILLightenComparer/Extensions/Functions.cs new file mode 100644 index 00000000..c38c53cd --- /dev/null +++ b/src/ILLightenComparer/Extensions/Functions.cs @@ -0,0 +1,45 @@ +using System.Reflection; +using System.Reflection.Emit; +using Illuminator; + +namespace ILLightenComparer.Extensions +{ + internal static class Functions + { + public static ILEmitterFunc Id() => + (in ILEmitter il) => il; + + public static ILEmitterFunc CallMethod(MethodInfo methodInfo, params ILEmitterFunc[] funcs) => + (in ILEmitter il) => il.CallMethod(methodInfo, funcs); + + public static ILEmitterFunc Cast(ILEmitterFunc value) => + (in ILEmitter il) => il.Cast(value); + + public static ILEmitterFunc EmitIf(bool condition, params ILEmitterFunc[] actions) => + (in ILEmitter il) => il.EmitIf(condition, actions); + + public static ILEmitterFunc If(ILEmitterFunc action, ILEmitterFunc whenTrue, ILEmitterFunc elseAction) => + (in ILEmitter il) => il.If(action, whenTrue, elseAction); + + public static ILEmitterFunc If(ILEmitterFunc action, ILEmitterFunc whenTrue) => + (in ILEmitter il) => il.If(action, whenTrue); + + public static ILEmitterFunc LoadLocalAddress(LocalBuilder local) => + (in ILEmitter il) => il.LoadLocalAddress(local); + + public static ILEmitterFunc LoadArgument(int argumentIndex) => + (in ILEmitter il) => il.LoadArgument(argumentIndex); + + public static ILEmitterFunc LoadArgumentAddress(ushort argumentIndex) => + (in ILEmitter il) => il.LoadArgumentAddress(argumentIndex); + + public static ILEmitterFunc LoadCaller(LocalBuilder local) => + (in ILEmitter il) => il.LoadCaller(local); + + public static ILEmitterFunc Ret(int value) => + (in ILEmitter il) => il.Ret(value); + + public static ILEmitterFunc Ret(LocalBuilder local) => + (in ILEmitter il) => il.Ret(local); + } +} \ No newline at end of file diff --git a/src/ILLightenComparer/Extensions/ILEmitterExtensions.cs b/src/ILLightenComparer/Extensions/ILEmitterExtensions.cs index 058bceb8..ce9a0138 100644 --- a/src/ILLightenComparer/Extensions/ILEmitterExtensions.cs +++ b/src/ILLightenComparer/Extensions/ILEmitterExtensions.cs @@ -5,22 +5,76 @@ using System.Reflection.Emit; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; -using static Illuminator.Functional; +using static Illuminator.Functions; namespace ILLightenComparer.Extensions { internal static class ILEmitterExtensions { + private const byte ShortFormLimit = byte.MaxValue; // 255 + private const string LengthMethodName = nameof(Array.Length); private static readonly MethodInfo ToArrayMethod = typeof(Enumerable).GetMethod(nameof(Enumerable.ToArray)); private static readonly MethodInfo GetComparerMethod = typeof(IComparerProvider).GetMethod(nameof(IComparerProvider.GetComparer)); private static readonly MethodInfo DisposeMethod = typeof(IDisposable).GetMethod(nameof(IDisposable.Dispose), Type.EmptyTypes); - public static ILEmitter EmitArrayLength(this ILEmitter il, Type arrayType, LocalBuilder array, out LocalBuilder count) => il - .LoadLocal(array) - .Call(arrayType.GetPropertyGetter(LengthMethodName)) - .Store(typeof(int), out count); + /// + /// Smart emission for Call methods. + /// + /// Self. + /// Static, virtual, instance method. + /// List of methods to prepare parameters in stack. + /// Self. + public static ILEmitter CallMethod( + this ILEmitter il, + MethodInfo methodInfo, + params ILEmitterFunc[] funcs) + { + var owner = methodInfo.DeclaringType; + if (owner == typeof(ValueType)) { + owner = methodInfo.ReflectedType; // todo: 0. test + } + + if (owner == null) { + throw new InvalidOperationException( + $"It's not expected that {methodInfo.DisplayName()} doesn't have a declaring type."); + } + + if (methodInfo.IsGenericMethodDefinition) { + throw new InvalidOperationException( + $"Generic method {methodInfo.DisplayName()} is not initialized."); + } + + // if the method belongs to Enum type, them it should be called as virtual and with constrained prefix + // https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.constrained + //var isEnum = owner.IsAssignableFrom(typeof(Enum)); + //if (isEnum) { + // Constrained(owner); // todo: 0. test + //} + + return methodInfo.IsStatic || owner.IsValueType || owner.IsSealed || !methodInfo.IsVirtual // todo: 0. test + ? il.Call(methodInfo, funcs) + : il.Callvirt(methodInfo, funcs); + } + + public static ILEmitter Cast(this ILEmitter self, ILEmitterFunc value) => value(self).Cast(typeof(T)); + + // todo: 3. test + public static ILEmitter Cast(this ILEmitter self, Type type) => Type.GetTypeCode(type) switch { + TypeCode.Int64 => self.Conv_I8(), + TypeCode.Int32 => self.Conv_I4(), + _ => type.IsValueType + ? self.Unbox_Any(type) + : self.Castclass(type) + }; + + public static ILEmitter Ceq(this ILEmitter il, ILEmitterFunc a, ILEmitterFunc b, out LocalBuilder local) => + il.Ceq(a, b) + .Stloc(typeof(int), out local); + + public static ILEmitter EmitArrayLength(this ILEmitter il, Type arrayType, LocalBuilder array, out LocalBuilder count) => + il.CallMethod(arrayType.GetPropertyGetter(LengthMethodName), Ldloc(array)) + .Stloc(typeof(int), out count); public static ILEmitter EmitArraySorting(this ILEmitter il, bool hasCustomComparer, Type elementType, params LocalBuilder[] arrays) { @@ -34,9 +88,8 @@ public static ILEmitter EmitArraySorting(this ILEmitter il, bool hasCustomCompar } else { var getComparerMethod = GetComparerMethod.MakeGenericMethod(elementType); - il.LoadArgument(Arg.Context) - .Call(getComparerMethod) - .Store(getComparerMethod.ReturnType, out var comparer); + il.CallMethod(getComparerMethod, Functions.LoadArgument(Arg.Context)) + .Stloc(getComparerMethod.ReturnType, out var comparer); foreach (var array in arrays) { EmitSortArray(il, elementType, array, comparer); @@ -46,22 +99,75 @@ public static ILEmitter EmitArraySorting(this ILEmitter il, bool hasCustomCompar return il; } - public static ILEmitter EmitDispose(this ILEmitter il, LocalBuilder local) => il - .LoadCaller(local) - .ExecuteIf(local.LocalType.IsValueType, Constrained(local.LocalType)) - .Call(DisposeMethod); + public static ILEmitter EmitDispose(this ILEmitter il, LocalBuilder local) => + il.LoadCaller(local) + .EmitIf(local.LocalType.IsValueType, Constrained(local.LocalType)) + .CallMethod(DisposeMethod); + + public static ILEmitter EmitIf(this ILEmitter il, bool condition, params ILEmitterFunc[] actions) => + condition ? il.Emit(actions) : il; + + public static ILEmitter If(this ILEmitter il, ILEmitterFunc action, ILEmitterFunc whenTrue, ILEmitterFunc elseAction) => + action(il) + .Brfalse(out var elseBlock) + .Emit(whenTrue) + .Br(out var next) + .MarkLabel(elseBlock) + .Emit(elseAction) + .MarkLabel(next); + + public static ILEmitter If(this ILEmitter il, ILEmitterFunc action, ILEmitterFunc whenTrue) => + action(il) + .Brfalse(out var exit) + .Emit(whenTrue) + .MarkLabel(exit); + + public static ILEmitter LoadLocalAddress(this ILEmitter self, LocalBuilder local) => + self.LoadLocalAddress(local.LocalIndex); + + public static ILEmitter LoadLocalAddress(this ILEmitter self, int localIndex) => + localIndex <= ShortFormLimit + ? self.Ldloca_S((byte)localIndex) + : self.Ldloca((short)localIndex); + + public static ILEmitter LoadArgument(this ILEmitter self, int argumentIndex) => + argumentIndex switch { + 0 => self.Ldarg_0(), + 1 => self.Ldarg_1(), + 2 => self.Ldarg_2(), + 3 => self.Ldarg_3(), + _ => argumentIndex <= ShortFormLimit + ? self.Ldarg_S((byte)argumentIndex) + : self.Ldarg((short)argumentIndex) + }; + + public static ILEmitter LoadArgumentAddress(this ILEmitter self, ushort argumentIndex) => + argumentIndex <= ShortFormLimit + ? self.Ldarga_S((byte)argumentIndex) + : self.Ldarga((short)argumentIndex); + + // todo: 3. make Constrained when method is virtual and caller is value type + public static ILEmitter LoadCaller(this ILEmitter il, LocalBuilder local) => + local.LocalType!.IsValueType + ? il.LoadLocalAddress(local.LocalIndex) + : il.Ldloc(local); + + public static ILEmitter Ret(this ILEmitter il, int value) => il.Ldc_I4(value).Ret(); + + public static ILEmitter Ret(this ILEmitter il, LocalBuilder local) => il.Ldloc(local).Ret(); + + public static ILEmitter Stloc(this ILEmitter il, Type type, out LocalBuilder local) => + il.DeclareLocal(type, out local) + .Stloc(local); private static void EmitSortArray(ILEmitter il, Type elementType, LocalBuilder array, LocalBuilder comparer) { var copyMethod = ToArrayMethod.MakeGenericMethod(elementType); var sortMethod = GetArraySortWithComparer(elementType); - il.LoadLocal(array) - .Call(copyMethod) - .Store(array) - .LoadLocal(array) - .LoadLocal(comparer) - .Call(sortMethod); + il.CallMethod(copyMethod, Ldloc(array)) + .Stloc(array) + .CallMethod(sortMethod, Ldloc(array), Ldloc(comparer)); } private static void EmitSortArray(ILEmitter il, Type elementType, LocalBuilder array) @@ -69,33 +175,33 @@ private static void EmitSortArray(ILEmitter il, Type elementType, LocalBuilder a var copyMethod = ToArrayMethod.MakeGenericMethod(elementType); var sortMethod = GetArraySortMethod(elementType); - il.LoadLocal(array) - .Call(copyMethod) - .Store(array) - .LoadLocal(array) - .Call(sortMethod); + il.CallMethod(copyMethod, Ldloc(array)) + .Stloc(array) + .CallMethod(sortMethod, Ldloc(array)); } - private static MethodInfo GetArraySortWithComparer(Type elementType) => typeof(Array) - .GetMethods(BindingFlags.Static | BindingFlags.Public) - .Where(x => x.Name == nameof(Array.Sort) && x.IsGenericMethodDefinition) - .Single(x => { - var parameters = x.GetParameters(); - - return parameters.Length == 2 - && parameters[0].ParameterType.IsArray - && parameters[1].ParameterType.IsGenericType - && parameters[1].ParameterType.GetGenericTypeDefinition() == typeof(IComparer<>); - }) - .MakeGenericMethod(elementType); - - private static MethodInfo GetArraySortMethod(Type elementType) => typeof(Array) - .GetMethods(BindingFlags.Static | BindingFlags.Public) - .Where(x => x.Name == nameof(Array.Sort) && x.IsGenericMethodDefinition) - .Single(x => { - var parameters = x.GetParameters(); - return parameters.Length == 1 && parameters[0].ParameterType.IsArray; - }) - .MakeGenericMethod(elementType); + private static MethodInfo GetArraySortMethod(Type elementType) => + typeof(Array) + .GetMethods(BindingFlags.Static | BindingFlags.Public) + .Where(x => x.Name == nameof(Array.Sort) && x.IsGenericMethodDefinition) + .Single(x => { + var parameters = x.GetParameters(); + return parameters.Length == 1 && parameters[0].ParameterType.IsArray; + }) + .MakeGenericMethod(elementType); + + private static MethodInfo GetArraySortWithComparer(Type elementType) => + typeof(Array) + .GetMethods(BindingFlags.Static | BindingFlags.Public) + .Where(x => x.Name == nameof(Array.Sort) && x.IsGenericMethodDefinition) + .Single(x => { + var parameters = x.GetParameters(); + + return parameters.Length == 2 + && parameters[0].ParameterType.IsArray + && parameters[1].ParameterType.IsGenericType + && parameters[1].ParameterType.GetGenericTypeDefinition() == typeof(IComparer<>); + }) + .MakeGenericMethod(elementType); } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Extensions/MemberInfoExtensions.cs b/src/ILLightenComparer/Extensions/MemberInfoExtensions.cs new file mode 100644 index 00000000..787b8cf7 --- /dev/null +++ b/src/ILLightenComparer/Extensions/MemberInfoExtensions.cs @@ -0,0 +1,10 @@ +using System.Reflection; + +namespace ILLightenComparer.Extensions +{ + internal static class MemberInfoExtensions + { + public static string DisplayName(this MemberInfo memberInfo) => + $"{memberInfo.MemberType} {memberInfo.DeclaringType?.Name}::{memberInfo.Name}"; + } +} diff --git a/src/ILLightenComparer/Extensions/MethodBuilderExtensions.cs b/src/ILLightenComparer/Extensions/MethodBuilderExtensions.cs new file mode 100644 index 00000000..3a78e186 --- /dev/null +++ b/src/ILLightenComparer/Extensions/MethodBuilderExtensions.cs @@ -0,0 +1,26 @@ +using System.Reflection.Emit; +using Illuminator; + +namespace ILLightenComparer.Extensions +{ + internal static class MethodBuilderExtensions + { + public static ILEmitter CreateILEmitter(this MethodBuilder methodBuilder) + { + #if DEBUG + return methodBuilder.GetILGenerator().UseIlluminator(true); + #else + return methodBuilder.GetILGenerator().UseIlluminator(); + #endif + } + + public static ILEmitter CreateILEmitter(this ConstructorBuilder constructorBuilder) + { + #if DEBUG + return constructorBuilder.GetILGenerator().UseIlluminator(true); + #else + return constructorBuilder.GetILGenerator().UseIlluminator(); + #endif + } + } +} \ No newline at end of file diff --git a/src/ILLightenComparer/Extensions/MethodInfoExtensions.cs b/src/ILLightenComparer/Extensions/MethodInfoExtensions.cs new file mode 100644 index 00000000..d1796a64 --- /dev/null +++ b/src/ILLightenComparer/Extensions/MethodInfoExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace ILLightenComparer.Extensions +{ + internal static class MethodInfoExtensions + { + public static TDelegate CreateDelegate(this MethodInfo methodInfo) + where TDelegate : Delegate => + (TDelegate)methodInfo.CreateDelegate(typeof(TDelegate)); + + public static string GetMethodInfoName(this MethodInfo methodInfo) + { + var parameters = methodInfo is MethodBuilder + ? "..." + : string.Join(", ", methodInfo.GetParameters().Select(x => x.ParameterType.Name)); + + return $"{methodInfo.DeclaringType?.Name}::{methodInfo.Name}({parameters}): {methodInfo.ReturnType.Name}"; + } + } +} diff --git a/src/ILLightenComparer/Extensions/Methods.cs b/src/ILLightenComparer/Extensions/Methods.cs index bd448f9a..efff882a 100644 --- a/src/ILLightenComparer/Extensions/Methods.cs +++ b/src/ILLightenComparer/Extensions/Methods.cs @@ -1,7 +1,6 @@ using System; using System.Reflection; using ILLightenComparer.Shared; -using Illuminator.Extensions; namespace ILLightenComparer.Extensions { diff --git a/src/ILLightenComparer/Extensions/ModuleBuilderExtensions.cs b/src/ILLightenComparer/Extensions/ModuleBuilderExtensions.cs new file mode 100644 index 00000000..91ce8027 --- /dev/null +++ b/src/ILLightenComparer/Extensions/ModuleBuilderExtensions.cs @@ -0,0 +1,26 @@ +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace ILLightenComparer.Extensions +{ + internal static class ModuleBuilderExtensions + { + public static TypeBuilder DefineType( + this ModuleBuilder moduleBuilder, + string name, + params Type[] interfaceTypes) + { + var type = moduleBuilder.DefineType(name, TypeAttributes.Sealed | TypeAttributes.Public); + if (interfaceTypes == null) { + return type; + } + + foreach (var interfaceType in interfaceTypes) { + type.AddInterfaceImplementation(interfaceType); + } + + return type; + } + } +} diff --git a/src/ILLightenComparer/Extensions/TypeBuilderExtensions.cs b/src/ILLightenComparer/Extensions/TypeBuilderExtensions.cs new file mode 100644 index 00000000..82033de8 --- /dev/null +++ b/src/ILLightenComparer/Extensions/TypeBuilderExtensions.cs @@ -0,0 +1,27 @@ +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace ILLightenComparer.Extensions +{ + // todo: 3. return wrapper with parameters instead of MethodBuilder + internal static class TypeBuilderExtensions + { + public static MethodBuilder DefineInterfaceMethod( + this TypeBuilder typeBuilder, + MethodInfo interfaceMethod) + { + var method = typeBuilder.DefineMethod( + interfaceMethod.Name, + MethodAttributes.Public | MethodAttributes.Virtual, + CallingConventions.HasThis, + interfaceMethod.ReturnType, + interfaceMethod.GetParameters().Select(x => x.ParameterType).ToArray() + ); + + typeBuilder.DefineMethodOverride(method, interfaceMethod); + + return method; + } + } +} diff --git a/src/ILLightenComparer/Extensions/TypeExtensions.cs b/src/ILLightenComparer/Extensions/TypeExtensions.cs index 86ca069f..4972b312 100644 --- a/src/ILLightenComparer/Extensions/TypeExtensions.cs +++ b/src/ILLightenComparer/Extensions/TypeExtensions.cs @@ -5,40 +5,78 @@ using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; -using Illuminator.Extensions; namespace ILLightenComparer.Extensions { internal static class TypeExtensions { - private static readonly HashSet SmallIntegralTypes = new HashSet(new[] { - typeof(sbyte), - typeof(byte), - typeof(char), - typeof(short), - typeof(ushort) - }); - - private static readonly HashSet CeqCompatibleTypes = new HashSet(new[] { - typeof(sbyte), - typeof(byte), - typeof(char), - typeof(short), - typeof(ushort), - typeof(int), - typeof(long), - typeof(ulong), - typeof(float), - typeof(double) - }); - - private static readonly HashSet BasicTypes = new HashSet(typeof(object).Assembly + private static readonly HashSet SmallIntegralTypes = new(new[] { typeof(sbyte), typeof(byte), typeof(char), typeof(short), typeof(ushort) }); + + private static readonly HashSet CeqCompatibleTypes = new(new[] { typeof(sbyte), typeof(byte), typeof(char), typeof(short), typeof(ushort), typeof(int), typeof(long), typeof(ulong), typeof(float), typeof(double) }); + + private static readonly HashSet BasicTypes = new(typeof(object).Assembly .GetTypes() - .Where(x => x.FullName.StartsWith("System.")) + .Where(x => x.FullName!.StartsWith("System.")) .Where(x => x.IsPublic) .Where(x => !x.IsGenericType) .Except(new[] { typeof(object) })); // object is treated separately + /// + /// Not objects and structures. + /// Extended version of IsPrimitive property. + /// + /// The type to check. + /// true if the type is one of the primitive types; otherwise, false. + public static bool IsPrimitive(this Type type) => + type.IsPrimitive + || type.IsEnum + || ReferenceEquals(type, typeof(string)) + // || ReferenceEquals(type, typeof(object)) // todo: 3. move extensions to separate assembly + || ReferenceEquals(type, typeof(decimal)); + + public static bool ImplementsGenericInterface(this Type type, Type generic) => type.FindGenericInterface(generic) != null; + + public static Type FindGenericInterface(this Type type, Type generic) + { + if (!generic.IsGenericType) { + throw new ArgumentException($"{generic.DisplayName()} should be generic type.", nameof(generic)); + } + + if (type.IsInterface && type.IsGenericType && type.GetGenericTypeDefinition() == generic) { + return type; + } + + return Array.Find( + type.GetInterfaces(), + t => t.IsGenericType && generic == t.GetGenericTypeDefinition()); + } + + public static bool IsNullable(this Type type) => + type.IsValueType + && type.IsGenericType + && !type.IsGenericTypeDefinition + && ReferenceEquals(type.GetGenericTypeDefinition(), typeof(Nullable<>)); + + /// + /// Returns the underlying type of the specified Enum or Nullable. + /// + /// The type whose underlying type will be retrieved. + /// The underlying type of type. + public static Type GetUnderlyingType(this Type type) + { + while (true) { + if (type.IsEnum) { + return Enum.GetUnderlyingType(type); + } + + if (type.IsNullable()) { + type = type.GetGenericArguments()[0]; + } else { + return type; + } + } + } + /// /// Creates instance using static method. /// @@ -53,7 +91,7 @@ public static TResult Create(this Type type) { // todo: 2. benchmark creation var ctor = type.GetConstructor(Type.EmptyTypes) - ?? throw new ArgumentException($"Type {type.DisplayName()} should has default constructor.", nameof(type)); + ?? throw new ArgumentException($"Type {type.DisplayName()} should has default constructor.", nameof(type)); var lambda = Expression.Lambda(typeof(Func), Expression.New(ctor)); var compiled = (Func)lambda.Compile(); diff --git a/src/ILLightenComparer/ILLightenComparer.csproj b/src/ILLightenComparer/ILLightenComparer.csproj index 6436438c..2bc1e27d 100644 --- a/src/ILLightenComparer/ILLightenComparer.csproj +++ b/src/ILLightenComparer/ILLightenComparer.csproj @@ -27,14 +27,9 @@ full - - - - + - - diff --git a/src/ILLightenComparer/Shared/Comparisons/ArrayComparisonEmitter.cs b/src/ILLightenComparer/Shared/Comparisons/ArrayComparisonEmitter.cs index 992de19c..331efca8 100644 --- a/src/ILLightenComparer/Shared/Comparisons/ArrayComparisonEmitter.cs +++ b/src/ILLightenComparer/Shared/Comparisons/ArrayComparisonEmitter.cs @@ -5,15 +5,15 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using static Illuminator.Functional; +using static Illuminator.Functions; namespace ILLightenComparer.Shared.Comparisons { internal sealed class ArrayComparisonEmitter { - private readonly IResolver _resolver; - private readonly EmitReferenceComparisonDelegate _emitReferenceComparison; private readonly EmitCheckIfLoopsAreDoneDelegate _emitCheckIfLoopsAreDone; + private readonly EmitReferenceComparisonDelegate _emitReferenceComparison; + private readonly IResolver _resolver; public ArrayComparisonEmitter( IResolver resolver, @@ -27,11 +27,11 @@ public ArrayComparisonEmitter( public (LocalBuilder collectionX, LocalBuilder collectionY) EmitLoad(IVariable variable, ILEmitter il, Label gotoNext) { - variable.Load(il, Arg.X).Store(variable.VariableType, out var collectionX); - variable.Load(il, Arg.Y).Store(variable.VariableType, out var collectionY); + variable.Load(il, Arg.X).Stloc(variable.VariableType, out var collectionX); + variable.Load(il, Arg.Y).Stloc(variable.VariableType, out var collectionY); if (!variable.VariableType.IsValueType) { - _emitReferenceComparison(il, LoadLocal(collectionX), LoadLocal(collectionY), GoTo(gotoNext)); // need, because a collection can be a member of an object + _emitReferenceComparison(il, Ldloc(collectionX), Ldloc(collectionY), Br(gotoNext)); // need, because a collection can be a member of an object } return (collectionX, collectionY); @@ -48,15 +48,15 @@ public ILEmitter EmitCompareArrays( // todo: 2. compare array lengths at the beginning il.EmitArrayLength(arrayType, xArray, out var countX) .EmitArrayLength(arrayType, yArray, out var countY) - .LoadInteger(0) - .Store(typeof(int), out var index) + .Ldc_I4(0) + .Stloc(typeof(int), out var index) .DefineLabel(out var loopStart) .DefineLabel(out var continueLoop) .MarkLabel(loopStart); using (il.LocalsScope()) { - il.AreSame(LoadLocal(index), LoadLocal(countX), out var isDoneX) - .AreSame(LoadLocal(index), LoadLocal(countY), out var isDoneY); + il.Ceq(Ldloc(index), Ldloc(countX), out var isDoneX) + .Ceq(Ldloc(index), Ldloc(countY), out var isDoneY); _emitCheckIfLoopsAreDone(il, isDoneX, isDoneY, afterLoop); } @@ -68,15 +68,14 @@ public ILEmitter EmitCompareArrays( var itemVariable = new ArrayItemVariable(arrayType, ownerType, arrays, index); var itemComparison = _resolver.GetComparisonEmitter(itemVariable); - return il - .Execute( - itemComparison.Emit(continueLoop), - itemComparison.EmitCheckForResult(continueLoop)) - .MarkLabel(continueLoop) - .Add(LoadLocal(index), LoadInteger(1)) - .Store(index) - .GoTo(loopStart); + return il.Emit( + itemComparison.Emit(continueLoop), + itemComparison.EmitCheckForResult(continueLoop)) + .MarkLabel(continueLoop) + .Add(Ldloc(index), Ldc_I4(1)) + .Stloc(index) + .Br(loopStart); } } } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Shared/Comparisons/EnumerablesComparison.cs b/src/ILLightenComparer/Shared/Comparisons/EnumerablesComparison.cs index fc88d896..c5579670 100644 --- a/src/ILLightenComparer/Shared/Comparisons/EnumerablesComparison.cs +++ b/src/ILLightenComparer/Shared/Comparisons/EnumerablesComparison.cs @@ -9,8 +9,8 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; +using static Illuminator.Functions; namespace ILLightenComparer.Shared.Comparisons { @@ -89,7 +89,7 @@ public ILEmitter Emit(ILEmitter il, Label gotoNext) //il.EndExceptionBlock(); - return il.GoTo(gotoNext); + return il.Br(gotoNext); } public ILEmitter EmitCheckForResult(ILEmitter il, Label _) => il; @@ -107,13 +107,12 @@ private ILEmitter EmitCompareAsSortedArrays(ILEmitter il, Label gotoNext, LocalB private (LocalBuilder xEnumerator, LocalBuilder yEnumerator) EmitLoadEnumerators(ILEmitter il, LocalBuilder xEnumerable, LocalBuilder yEnumerable) { - il.Call(_getEnumeratorMethod, LoadCaller(xEnumerable)) - .Store(_enumeratorType, out var xEnumerator) - .Call(_getEnumeratorMethod, LoadCaller(yEnumerable)) - .Store(_enumeratorType, out var yEnumerator); + il.CallMethod(_getEnumeratorMethod, LoadCaller(xEnumerable)) + .Stloc(_enumeratorType, out var xEnumerator) + .CallMethod(_getEnumeratorMethod, LoadCaller(yEnumerable)) + .Stloc(_enumeratorType, out var yEnumerator); // todo: 3. check enumerators for null? - return (xEnumerator, yEnumerator); } @@ -137,16 +136,20 @@ private void Loop(ILEmitter il, LocalBuilder xEnumerator, LocalBuilder yEnumerat var itemVariable = new EnumerableItemVariable(_enumeratorType, _elementType, _getCurrentMethod, enumerators); var itemComparison = _resolver.GetComparisonEmitter(itemVariable); - il.Execute(itemComparison.Emit(loopStart)) - .Execute(itemComparison.EmitCheckForResult(loopStart)); + il.Emit(itemComparison.Emit(loopStart)) + .Emit(itemComparison.EmitCheckForResult(loopStart)); } } private (LocalBuilder xDone, LocalBuilder yDone) EmitMoveNext(LocalBuilder xEnumerator, LocalBuilder yEnumerator, ILEmitter il) { // todo: 3. it's possible to use "not done" flag. it will simplify emitted code in _emitCheckIfLoopsAreDone. - il.AreSame(Call(_moveNextMethod, LoadCaller(xEnumerator)), LoadInteger(0), out var xDone) - .AreSame(Call(_moveNextMethod, LoadCaller(yEnumerator)), LoadInteger(0), out var yDone); + il.Ceq(CallMethod(_moveNextMethod, LoadCaller(xEnumerator)), + Ldc_I4(0), + out var xDone) + .Ceq(CallMethod(_moveNextMethod, LoadCaller(yEnumerator)), + Ldc_I4(0), + out var yDone); return (xDone, yDone); } diff --git a/src/ILLightenComparer/Shared/Comparisons/IndirectComparison.cs b/src/ILLightenComparer/Shared/Comparisons/IndirectComparison.cs index 7ac3cf2a..9913a902 100644 --- a/src/ILLightenComparer/Shared/Comparisons/IndirectComparison.cs +++ b/src/ILLightenComparer/Shared/Comparisons/IndirectComparison.cs @@ -5,12 +5,12 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; namespace ILLightenComparer.Shared.Comparisons { /// - /// Delegates comparison to static method or delayed compare method in context. + /// Delegates comparison to static method or delayed compare method in context. /// internal sealed class IndirectComparison : IComparisonEmitter { @@ -55,13 +55,14 @@ public static IndirectComparison Create( return new IndirectComparison(checkForIntermediateResultEmitter, compareMethod, variable); } - public ILEmitter Emit(ILEmitter il, Label _) => il.Call( - _method, - LoadArgument(Arg.Context), - _variable.Load(Arg.X), - _variable.Load(Arg.Y), - LoadArgument(Arg.SetX), - LoadArgument(Arg.SetY)); + public ILEmitter Emit(ILEmitter il, Label _) => + il.CallMethod( + _method, + LoadArgument(Arg.Context), + _variable.Load(Arg.X), + _variable.Load(Arg.Y), + LoadArgument(Arg.SetX), + LoadArgument(Arg.SetY)); public ILEmitter EmitCheckForResult(ILEmitter il, Label next) => _checkForIntermediateResultEmitter(il, next); } diff --git a/src/ILLightenComparer/Shared/Comparisons/MembersComparison.cs b/src/ILLightenComparer/Shared/Comparisons/MembersComparison.cs index 8ebb2a96..076a47a4 100644 --- a/src/ILLightenComparer/Shared/Comparisons/MembersComparison.cs +++ b/src/ILLightenComparer/Shared/Comparisons/MembersComparison.cs @@ -5,7 +5,6 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Shared.Comparisons { @@ -50,8 +49,8 @@ public ILEmitter Emit(ILEmitter il, Label _) for (var i = 0; i < comparisons.Length; i++) { using (il.LocalsScope()) { il.DefineLabel(out var gotoNext) - .Execute(comparisons[i].Emit(gotoNext)) - .Execute(comparisons[i].EmitCheckForResult(gotoNext)) + .Emit(comparisons[i].Emit(gotoNext)) + .Emit(comparisons[i].EmitCheckForResult(gotoNext)) .MarkLabel(gotoNext); } } diff --git a/src/ILLightenComparer/Shared/Comparisons/NullableComparison.cs b/src/ILLightenComparer/Shared/Comparisons/NullableComparison.cs index 17309b81..fd57bf5f 100644 --- a/src/ILLightenComparer/Shared/Comparisons/NullableComparison.cs +++ b/src/ILLightenComparer/Shared/Comparisons/NullableComparison.cs @@ -1,18 +1,18 @@ using System.Collections.Generic; using System.Reflection.Emit; using ILLightenComparer.Abstractions; +using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; namespace ILLightenComparer.Shared.Comparisons { internal sealed class NullableComparison : IComparisonEmitter { - private readonly IResolver _resolver; private readonly EmitterDelegate _checkForIntermediateResultEmitter; private readonly EmitCheckNullablesForValueDelegate _emitCheckNullablesForValue; + private readonly IResolver _resolver; private readonly IVariable _variable; private NullableComparison( @@ -27,41 +27,41 @@ private NullableComparison( _variable = variable; } - public static NullableComparison Create( - IResolver resolver, - EmitterDelegate checkForIntermediateResultEmitter, - EmitCheckNullablesForValueDelegate emitCheckNullablesForValue, - IVariable variable) - { - if (variable.VariableType.IsNullable()) { - return new NullableComparison(resolver, checkForIntermediateResultEmitter, emitCheckNullablesForValue, variable); - } - - return null; - } - public ILEmitter Emit(ILEmitter il, Label gotoNext) { var variableType = _variable.VariableType; - _variable.Load(il, Arg.X).Store(variableType, out var nullableX); - _variable.Load(il, Arg.Y).Store(variableType, out var nullableY); + _variable.Load(il, Arg.X).Stloc(variableType, out var nullableX); + _variable.Load(il, Arg.Y).Stloc(variableType, out var nullableY); var isMember = !(_variable is ArgumentVariable); if (isMember) { - _emitCheckNullablesForValue(il, LoadAddress(nullableX), LoadAddress(nullableY), variableType, gotoNext); + _emitCheckNullablesForValue(il, LoadLocalAddress(nullableX), LoadLocalAddress(nullableY), variableType, gotoNext); } - var nullableVariables = new NullableVariables(variableType, _variable.OwnerType, new Dictionary(2) { + var nullableVariables = new NullableVariables(variableType, _variable.OwnerType, new Dictionary(2) { [Arg.X] = nullableX, [Arg.Y] = nullableY }); return _resolver - .GetComparisonEmitter(nullableVariables) - .Emit(il, gotoNext); + .GetComparisonEmitter(nullableVariables) + .Emit(il, gotoNext); } public ILEmitter EmitCheckForResult(ILEmitter il, Label next) => _checkForIntermediateResultEmitter(il, next); + + public static NullableComparison Create( + IResolver resolver, + EmitterDelegate checkForIntermediateResultEmitter, + EmitCheckNullablesForValueDelegate emitCheckNullablesForValue, + IVariable variable) + { + if (variable.VariableType.IsNullable()) { + return new NullableComparison(resolver, checkForIntermediateResultEmitter, emitCheckNullablesForValue, variable); + } + + return null; + } } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Shared/Comparisons/ObjectComparison.cs b/src/ILLightenComparer/Shared/Comparisons/ObjectComparison.cs index 6a800973..b45223cf 100644 --- a/src/ILLightenComparer/Shared/Comparisons/ObjectComparison.cs +++ b/src/ILLightenComparer/Shared/Comparisons/ObjectComparison.cs @@ -2,6 +2,7 @@ using ILLightenComparer.Abstractions; using ILLightenComparer.Variables; using Illuminator; +using ILLightenComparer.Extensions; namespace ILLightenComparer.Shared.Comparisons { @@ -16,14 +17,14 @@ public static ObjectComparison Create(IVariable variable) => (variable.VariableT ? new ObjectComparison(variable) : null; - public ILEmitter Emit(ILEmitter il, Label next) => il - .Execute(_variable.Load(Arg.Y)) - .IfTrue_S(_variable.Load(Arg.X), out var xIsNotNull) - .IfFalse_S(next) - .Return(-1) - .MarkLabel(xIsNotNull) - .IfTrue_S(next) - .Return(1); + public ILEmitter Emit(ILEmitter il, Label next) => + il.Emit(_variable.Load(Arg.Y)) + .Brtrue_S(_variable.Load(Arg.X), out var xIsNotNull) + .Brfalse_S(next) + .Ret(-1) + .MarkLabel(xIsNotNull) + .Brtrue_S(next) + .Ret(1); public ILEmitter EmitCheckForResult(ILEmitter il, Label next) => il; } diff --git a/src/ILLightenComparer/Shared/Comparisons/StringsComparison.cs b/src/ILLightenComparer/Shared/Comparisons/StringsComparison.cs index 1adae087..fa2b3dff 100644 --- a/src/ILLightenComparer/Shared/Comparisons/StringsComparison.cs +++ b/src/ILLightenComparer/Shared/Comparisons/StringsComparison.cs @@ -4,7 +4,8 @@ using ILLightenComparer.Config; using ILLightenComparer.Variables; using Illuminator; -using static Illuminator.Functional; +using static Illuminator.Functions; +using ILLightenComparer.Extensions; namespace ILLightenComparer.Shared.Comparisons { @@ -40,11 +41,12 @@ public static StringsComparison Create( return null; } - public ILEmitter Emit(ILEmitter il, Label _) => il.Call( - _compareMethod, - _variable.Load(Arg.X), - _variable.Load(Arg.Y), - LoadInteger(_stringComparisonType)); + public ILEmitter Emit(ILEmitter il, Label _) => + il.CallMethod( + _compareMethod, + _variable.Load(Arg.X), + _variable.Load(Arg.Y), + Ldc_I4(_stringComparisonType)); public ILEmitter EmitCheckForResult(ILEmitter il, Label next) => _checkForIntermediateResultEmitter(il, next); } diff --git a/src/ILLightenComparer/Shared/CycleDetectionSet.cs b/src/ILLightenComparer/Shared/CycleDetectionSet.cs index 983e24a3..51295a31 100644 --- a/src/ILLightenComparer/Shared/CycleDetectionSet.cs +++ b/src/ILLightenComparer/Shared/CycleDetectionSet.cs @@ -4,7 +4,8 @@ using System.Reflection; using ILLightenComparer.Extensions; using Illuminator; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; +using static Illuminator.Functions; namespace ILLightenComparer.Shared { @@ -12,23 +13,31 @@ internal sealed class CycleDetectionSet : ConcurrentDictionary { public static readonly ConstructorInfo DefaultConstructor = typeof(CycleDetectionSet).GetConstructor(Type.EmptyTypes); - public static ILEmitterFunc Remove(ushort set, ushort arg, Type argType) => Call( - RemoveMethod, - LoadArgument(set), - LoadArgument(arg) + ExecuteIf(argType.IsValueType, Box(argType))); + public static ILEmitterFunc Remove(short set, short arg, Type argType) => + CallMethod( + RemoveMethod, + LoadArgument(set), + LoadArgument(arg) + EmitIf(argType.IsValueType, Box((in ILEmitter il) => il, argType))); - public static ILEmitterFunc TryAdd(ushort set, ushort arg, Type argType) => Call( - TryAddMethod, - LoadArgument(set), - LoadArgument(arg) + ExecuteIf(argType.IsValueType, Box(argType)), - LoadInteger(0)); + public static ILEmitterFunc TryAdd(short set, short arg, Type argType) => + CallMethod( + TryAddMethod, + LoadArgument(set), + LoadArgument(arg), + EmitIf(argType.IsValueType, Box((in ILEmitter il) => il, argType)), + Ldc_I4(0)); - public static ILEmitterFunc GetCount(ushort arg) => Call(GetCountProperty, LoadArgument(arg)); + public static ILEmitterFunc GetCount(short arg) => + CallMethod(GetCountProperty, LoadArgument(arg)); - internal static readonly MethodInfo GetCountProperty = typeof(CycleDetectionSet).GetProperty(nameof(Count)).GetGetMethod(); + internal static readonly MethodInfo GetCountProperty = typeof(CycleDetectionSet).GetProperty(nameof(Count))!.GetGetMethod(); - private static readonly MethodInfo RemoveMethod = typeof(IDictionary).FindMethod(nameof(IDictionary.Remove), new[] { typeof(object) }); + private static readonly MethodInfo RemoveMethod = typeof(IDictionary).FindMethod( + nameof(IDictionary.Remove), + new[] { typeof(object) }); - private static readonly MethodInfo TryAddMethod = typeof(CycleDetectionSet).GetMethod(nameof(TryAdd), new[] { typeof(object), typeof(byte) }); + private static readonly MethodInfo TryAddMethod = typeof(CycleDetectionSet).GetMethod( + nameof(ConcurrentDictionary.TryAdd), + new[] { typeof(object), typeof(byte) }); } } diff --git a/src/ILLightenComparer/Shared/GenericProvider.cs b/src/ILLightenComparer/Shared/GenericProvider.cs index bb58e5e8..d020b8a4 100644 --- a/src/ILLightenComparer/Shared/GenericProvider.cs +++ b/src/ILLightenComparer/Shared/GenericProvider.cs @@ -5,7 +5,6 @@ using System.Reflection.Emit; using ILLightenComparer.Abstractions; using ILLightenComparer.Extensions; -using Illuminator.Extensions; namespace ILLightenComparer.Shared { diff --git a/src/ILLightenComparer/Shared/GenericTypeBuilder.cs b/src/ILLightenComparer/Shared/GenericTypeBuilder.cs index a9384d8e..58ffe8a8 100644 --- a/src/ILLightenComparer/Shared/GenericTypeBuilder.cs +++ b/src/ILLightenComparer/Shared/GenericTypeBuilder.cs @@ -8,15 +8,16 @@ using ILLightenComparer.Extensions; using ILLightenComparer.Variables; using Illuminator; -using Illuminator.Extensions; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; +using TypeExtensions = ILLightenComparer.Extensions.TypeExtensions; namespace ILLightenComparer.Shared { internal sealed class GenericTypeBuilder { - private readonly IReadOnlyDictionary _methodEmitter; + private static readonly Type ContextType = typeof(IContext); private readonly IConfigurationProvider _configuration; + private readonly IReadOnlyDictionary _methodEmitter; public GenericTypeBuilder( IReadOnlyDictionary methodEmitters, @@ -32,30 +33,30 @@ public Type Build(StaticMethodsInfo methodsInfo) var objectType = methodsInfo.ObjectType; var contextField = comparerTypeBuilder.DefineField( "_context", - typeof(IContext), + ContextType, FieldAttributes.InitOnly | FieldAttributes.Private); BuildConstructorAndFactoryMethod(comparerTypeBuilder, contextField); var names = methodsInfo - .GetAllMethodBuilders() - .Select(staticMethodBuilder => { - BuildStaticMethod(objectType, staticMethodBuilder); - BuildInstanceMethod( - methodsInfo.TypedComparerInterface, - comparerTypeBuilder, - staticMethodBuilder, - contextField, - objectType); - - return staticMethodBuilder.Name; - }) - .ToArray(); + .GetAllMethodBuilders() + .Select(staticMethodBuilder => { + BuildStaticMethod(objectType, staticMethodBuilder); + BuildInstanceMethod( + methodsInfo.TypedComparerInterface, + comparerTypeBuilder, + staticMethodBuilder, + contextField, + objectType); + + return staticMethodBuilder.Name; + }) + .ToArray(); var compiledComparerType = comparerTypeBuilder.CreateTypeInfo(); foreach (var name in names) { - var method = compiledComparerType.GetMethod(name, BindingFlags.Public | BindingFlags.Static); + var method = compiledComparerType!.GetMethod(name, BindingFlags.Public | BindingFlags.Static); methodsInfo.SetCompiledMethod(method); } @@ -76,15 +77,15 @@ private void BuildInstanceMethod( Type objectType) { var interfaceMethod = typedComparerInterface.GetMethod(staticMethod.Name); - var parametersCount = interfaceMethod.GetParameters().Length; + var parametersCount = interfaceMethod!.GetParameters().Length; var methodBuilder = typeBuilder.DefineInterfaceMethod(interfaceMethod); using var il = methodBuilder.CreateILEmitter(); Enumerable.Range(0, parametersCount) - .Aggregate(il - .LoadArgument(Arg.This) - .LoadField(contextField), (il, index) => il.LoadArgument((ushort)(index + 1))); + .Aggregate( + il.LoadArgument(Arg.This).Ldfld(contextField), + (emitter, index) => emitter.LoadArgument(index + 1)); EmitStaticMethodCall(il, objectType, staticMethod, parametersCount); } @@ -94,11 +95,11 @@ private void EmitStaticMethodCall(ILEmitter il, Type objectType, MethodBuilder s var createCycleDetectionSets = NeedCreateCycleDetectionSets(objectType, staticMethod.Name); Enumerable.Range(0, parametersCount) - .Aggregate(il, (il, _) => createCycleDetectionSets - ? il.New(CycleDetectionSet.DefaultConstructor) - : il.LoadNull()) - .Call(staticMethod) - .Return(); + .Aggregate(il, (emitter, _) => createCycleDetectionSets + ? emitter.Newobj(CycleDetectionSet.DefaultConstructor) + : emitter.Ldnull()) + .CallMethod(staticMethod) + .Ret(); } private bool NeedCreateCycleDetectionSets(Type objectType, string methodName) => @@ -124,17 +125,17 @@ private static void BuildConstructorAndFactoryMethod(TypeBuilder typeBuilder, Fi using (var il = constructorInfo.CreateILEmitter()) { il.LoadArgument(Arg.This) .Call(typeof(object).GetConstructor(Type.EmptyTypes)) - .SetField(LoadArgument(Arg.This), LoadArgument(1), contextField) - .Return(); + .Stfld(LoadArgument(Arg.This), LoadArgument(1), contextField) + .Ret(); } var methodBuilder = typeBuilder.DefineStaticMethod( - nameof(Extensions.TypeExtensions.CreateInstance), + nameof(TypeExtensions.CreateInstance), typeBuilder, parameters); using (var il = methodBuilder.CreateILEmitter()) { - il.New(constructorInfo, LoadArgument(Arg.This)).Return(); + il.Newobj(constructorInfo, LoadArgument(Arg.This)).Ret(); } } } diff --git a/src/ILLightenComparer/Shared/MembersProvider.cs b/src/ILLightenComparer/Shared/MembersProvider.cs index 19ce9a85..2516b556 100644 --- a/src/ILLightenComparer/Shared/MembersProvider.cs +++ b/src/ILLightenComparer/Shared/MembersProvider.cs @@ -3,8 +3,8 @@ using System.Linq; using System.Reflection; using ILLightenComparer.Config; +using ILLightenComparer.Extensions; using ILLightenComparer.Variables; -using Illuminator.Extensions; namespace ILLightenComparer.Shared { diff --git a/src/ILLightenComparer/Variables/ArgumentVariable.cs b/src/ILLightenComparer/Variables/ArgumentVariable.cs index 357396d0..feac8cf4 100644 --- a/src/ILLightenComparer/Variables/ArgumentVariable.cs +++ b/src/ILLightenComparer/Variables/ArgumentVariable.cs @@ -1,4 +1,5 @@ using System; +using ILLightenComparer.Extensions; using Illuminator; namespace ILLightenComparer.Variables diff --git a/src/ILLightenComparer/Variables/ArrayItemVariable.cs b/src/ILLightenComparer/Variables/ArrayItemVariable.cs index a2318140..d2db5a0b 100644 --- a/src/ILLightenComparer/Variables/ArrayItemVariable.cs +++ b/src/ILLightenComparer/Variables/ArrayItemVariable.cs @@ -3,8 +3,9 @@ using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; +using ILLightenComparer.Extensions; using Illuminator; -using static Illuminator.Functional; +using static Illuminator.Functions; namespace ILLightenComparer.Variables { @@ -34,13 +35,15 @@ public ArrayItemVariable( public Type VariableType { get; } public Type OwnerType { get; } - public ILEmitter Load(ILEmitter il, ushort arg) => il.Call( - _getItemMethod, - LoadLocal(_arrays[arg]), - LoadLocal(_indexVariable)); + public ILEmitter Load(ILEmitter il, ushort arg) => + il.CallMethod( + _getItemMethod, + Ldloc(_arrays[arg]), + Ldloc(_indexVariable)); - public ILEmitter LoadAddress(ILEmitter il, ushort arg) => Load(il, arg) - .Store(VariableType, out var local) - .LoadAddress(local); + public ILEmitter LoadAddress(ILEmitter il, ushort arg) => + Load(il, arg) + .Stloc(VariableType, out var local) + .LoadLocalAddress(local); } -} +} \ No newline at end of file diff --git a/src/ILLightenComparer/Variables/EnumerableItemVariable.cs b/src/ILLightenComparer/Variables/EnumerableItemVariable.cs index 80554945..a78c369e 100644 --- a/src/ILLightenComparer/Variables/EnumerableItemVariable.cs +++ b/src/ILLightenComparer/Variables/EnumerableItemVariable.cs @@ -2,8 +2,9 @@ using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; +using ILLightenComparer.Extensions; using Illuminator; -using static Illuminator.Functional; +using static ILLightenComparer.Extensions.Functions; namespace ILLightenComparer.Variables { @@ -31,11 +32,11 @@ public EnumerableItemVariable(Type ownerType, Type elementType, MethodInfo getCu /// public Type OwnerType { get; } - public ILEmitter Load(ILEmitter il, ushort arg) => il.Call(_getCurrentMethod, LoadCaller(_enumerators[arg])); + public ILEmitter Load(ILEmitter il, ushort arg) => il.CallMethod(_getCurrentMethod, LoadCaller(_enumerators[arg])); public ILEmitter LoadAddress(ILEmitter il, ushort arg) => il - .Call(_getCurrentMethod, LoadCaller(_enumerators[arg])) - .Store(VariableType, out var local) - .LoadAddress(local); + .CallMethod(_getCurrentMethod, LoadCaller(_enumerators[arg])) + .Stloc(VariableType, out var local) + .LoadLocalAddress(local); } } diff --git a/src/ILLightenComparer/Variables/FieldMemberVariable.cs b/src/ILLightenComparer/Variables/FieldMemberVariable.cs index b4b00657..573eb510 100644 --- a/src/ILLightenComparer/Variables/FieldMemberVariable.cs +++ b/src/ILLightenComparer/Variables/FieldMemberVariable.cs @@ -1,5 +1,6 @@ using System; using System.Reflection; +using ILLightenComparer.Extensions; using Illuminator; namespace ILLightenComparer.Variables @@ -13,7 +14,7 @@ internal sealed class FieldMemberVariable : IVariable public Type OwnerType => _fieldInfo.DeclaringType; public Type VariableType => _fieldInfo.FieldType; - public ILEmitter Load(ILEmitter il, ushort arg) => il.LoadArgument(arg).LoadField(_fieldInfo); + public ILEmitter Load(ILEmitter il, ushort arg) => il.LoadArgument(arg).Ldfld(_fieldInfo); public ILEmitter LoadAddress(ILEmitter il, ushort arg) { @@ -23,7 +24,7 @@ public ILEmitter LoadAddress(ILEmitter il, ushort arg) il.LoadArgument(arg); } - return il.LoadFieldAddress(_fieldInfo); + return il.Ldflda(_fieldInfo); } public static IVariable Create(MemberInfo memberInfo) => diff --git a/src/ILLightenComparer/Variables/IVariable.cs b/src/ILLightenComparer/Variables/IVariable.cs index 2794f976..5917dfd2 100644 --- a/src/ILLightenComparer/Variables/IVariable.cs +++ b/src/ILLightenComparer/Variables/IVariable.cs @@ -15,9 +15,9 @@ internal interface IVariable internal static class VariableExtensions { public static ILEmitterFunc Load(this IVariable variable, ushort arg) => - (ILEmitter il) => variable.Load(il, arg); + (in ILEmitter il) => variable.Load(il, arg); public static ILEmitterFunc LoadAddress(this IVariable variable, ushort arg) => - (ILEmitter il) => variable.LoadAddress(il, arg); + (in ILEmitter il) => variable.LoadAddress(il, arg); } } diff --git a/src/ILLightenComparer/Variables/NullableVariables.cs b/src/ILLightenComparer/Variables/NullableVariables.cs index 73830b5c..71de6b1d 100644 --- a/src/ILLightenComparer/Variables/NullableVariables.cs +++ b/src/ILLightenComparer/Variables/NullableVariables.cs @@ -5,16 +5,15 @@ using System.Reflection.Emit; using ILLightenComparer.Extensions; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Variables { internal sealed class NullableVariables : IVariable { private readonly MethodInfo _getValueMethod; - private readonly IReadOnlyDictionary _nullables; + private readonly IReadOnlyDictionary _nullables; - public NullableVariables(Type variableType, Type ownerType, IReadOnlyDictionary nullables) + public NullableVariables(Type variableType, Type ownerType, IReadOnlyDictionary nullables) { Debug.Assert(variableType.IsNullable()); @@ -28,18 +27,18 @@ public NullableVariables(Type variableType, Type ownerType, IReadOnlyDictionary< public Type VariableType { get; } public Type OwnerType { get; } - public ILEmitter Load(ILEmitter il, ushort arg) => il - .LoadAddress(_nullables[arg]) - .Call(_getValueMethod); + public ILEmitter Load(ILEmitter il, ushort arg) => + il.LoadLocalAddress(_nullables[arg]) + .CallMethod(_getValueMethod); public ILEmitter LoadAddress(ILEmitter il, ushort arg) { var underlyingType = VariableType.GetUnderlyingType(); - return il.LoadAddress(_nullables[arg]) - .Call(_getValueMethod) - .Store(underlyingType, out var x) - .LoadAddress(x); + return il.LoadLocalAddress(_nullables[arg]) + .CallMethod(_getValueMethod) + .Stloc(underlyingType, out var x) + .LoadLocalAddress(x); } } } diff --git a/src/ILLightenComparer/Variables/PropertyMemberVariable.cs b/src/ILLightenComparer/Variables/PropertyMemberVariable.cs index 964654d4..a1ab8dd8 100644 --- a/src/ILLightenComparer/Variables/PropertyMemberVariable.cs +++ b/src/ILLightenComparer/Variables/PropertyMemberVariable.cs @@ -1,7 +1,7 @@ using System; using System.Reflection; +using ILLightenComparer.Extensions; using Illuminator; -using Illuminator.Extensions; namespace ILLightenComparer.Variables { @@ -22,12 +22,12 @@ public ILEmitter Load(ILEmitter il, ushort arg) il.LoadArgument(arg); } - return il.Call(_propertyInfo.GetMethod); + return il.CallMethod(_propertyInfo.GetMethod); } public ILEmitter LoadAddress(ILEmitter il, ushort arg) => Load(il, arg) - .Store(VariableType.GetUnderlyingType(), out var local) - .LoadAddress(local); + .Stloc(VariableType.GetUnderlyingType(), out var local) + .LoadLocalAddress(local); public static IVariable Create(MemberInfo memberInfo) => memberInfo is PropertyInfo info