├── $packages ├── AshMind.Extensions.1.0.4 │ ├── AshMind.Extensions.1.0.4.nupkg │ └── lib │ │ └── 3.5 │ │ ├── AshMind.Extensions.dll │ │ └── AshMind.Extensions.xml └── repositories.config ├── $tools └── NuGet.exe ├── .gitignore ├── Expressive.Tests ├── ExpectedExpressionAttribute.cs ├── Expressive.Tests.csproj ├── GeneralTests.cs ├── Helpers │ ├── Assembler.cs │ ├── Method.cs │ ├── Property.cs │ └── ToStringVisitor.cs ├── InlinerTest.cs ├── Massive │ ├── InstructionCollectionVisitor.cs │ └── InstructionSupportTest.cs ├── Methods │ ├── TestDisassembler.cs │ ├── TestMethod.cs │ ├── TestMethodBuilder.cs │ └── TestMethodParameter.cs ├── Properties │ └── AssemblyInfo.cs ├── TestClasses │ ├── Booleans.cs │ ├── ClassWithNames.cs │ ├── Constants.cs │ ├── Conversions.cs │ ├── Initializers.cs │ ├── Lambdas.cs │ ├── Nulls.cs │ ├── Numerics.cs │ └── Range.cs └── packages.config ├── Expressive.sln ├── Expressive ├── Abstraction │ ├── IManagedContext.cs │ ├── IManagedMethod.cs │ ├── IManagedMethodParameter.cs │ ├── MethodBaseAdapter.cs │ ├── MethodBaseModuleContext.cs │ └── ParameterInfoAdapter.cs ├── Decompilation │ ├── BooleanSupport.cs │ ├── DecompilationContext.cs │ ├── IDecompilationStep.cs │ ├── Pipelines │ │ ├── DecompilationPipeline.cs │ │ ├── DefaultPipeline.cs │ │ └── IDecompilationPipeline.cs │ └── Steps │ │ ├── AddressOfToCreateDelegateVisitor.cs │ │ ├── BooleanEqualityImprovementVisitor.cs │ │ ├── BranchProcessing.cs │ │ ├── BranchResolutionStep.cs │ │ ├── BranchStackFrame.cs │ │ ├── BranchingAwareStepBase.cs │ │ ├── Clarity │ │ ├── CoalescingVisitor.cs │ │ ├── ConditionImprovementVisitor.cs │ │ ├── NewNullableToCastVisitor.cs │ │ └── NotImprovementVisitor.cs │ │ ├── ContextualVisitor.cs │ │ ├── IndividualDecompilationContext.cs │ │ ├── IndividualDecompilationStep.cs │ │ ├── IndividualElements │ │ ├── BranchToCondition.cs │ │ ├── CallToExpression.cs │ │ ├── ConvToConvert.cs │ │ ├── CxxToCondition.cs │ │ ├── DupToCopy.cs │ │ ├── ElementInterpretation.cs │ │ ├── IElementInterpretation.cs │ │ ├── InstructionToExpression.cs │ │ ├── LdargToParameter.cs │ │ ├── LdcToConstant.cs │ │ ├── LdfldToField.cs │ │ ├── LdftnToAddressOf.cs │ │ ├── LdlocToVariable.cs │ │ ├── LdnullToConstant.cs │ │ ├── LdstrToConstant.cs │ │ ├── MathToExpression.cs │ │ ├── NewarrToNewArray.cs │ │ ├── NewobjToNew.cs │ │ ├── OtherToCondition.cs │ │ ├── PopToRemove.cs │ │ ├── RetToReturn.cs │ │ ├── StelemToAssignment.cs │ │ ├── StfldToAssignment.cs │ │ └── StlocToAssignment.cs │ │ ├── NopRemovalStep.cs │ │ ├── StatementInlining │ │ ├── AssignmentInlining │ │ │ ├── AssignmentEstimatingVisitor.cs │ │ │ ├── AssignmentInliner.cs │ │ │ └── AssignmentInliningVisitor.cs │ │ ├── IfAssignmentInliningVisitor.cs │ │ ├── IfReturnInliningVisitor.cs │ │ ├── IfThenCollapsingVisitor.cs │ │ ├── InitializerCollectors │ │ │ ├── ArrayInitializerCollector.cs │ │ │ ├── CollectionInitializerCollector.cs │ │ │ ├── IInitializerCollector.cs │ │ │ ├── InitializerCollector.cs │ │ │ └── ObjectInitializerCollector.cs │ │ ├── InitializerDetectingVisitor.cs │ │ ├── LambdaInliningVisitor.cs │ │ └── VariableInliningStep.cs │ │ ├── UnconditionalBranchesRemovalStep.cs │ │ └── VisitorSequenceStep.cs ├── DecompilationException.cs ├── Decompiler.cs ├── Disassembler.cs ├── Disassembly │ ├── IInstructionReader.cs │ ├── InstructionReader.cs │ └── Instructions │ │ ├── BranchInstruction.cs │ │ ├── FieldReferenceInstruction.cs │ │ ├── IValueInstruction.cs │ │ ├── Instruction.cs │ │ ├── MemberReferenceInstruction.cs │ │ ├── MethodReferenceInstruction.cs │ │ ├── SimpleInstruction.cs │ │ ├── SwitchInstruction.cs │ │ ├── TypeReferenceInstruction.cs │ │ ├── UnsupportedInstruction.cs │ │ ├── ValueInstruction.cs │ │ └── VariableReferenceInstruction.cs ├── Elements │ ├── ArrayItemAssignmentElement.cs │ ├── BranchingElement.cs │ ├── ElementKind.cs │ ├── ElementVisitor.cs │ ├── ExpressionElement.cs │ ├── Expressions │ │ ├── AddressOfExpression.cs │ │ ├── ExpressionTreeVisitor.cs │ │ └── LocalExpression.cs │ ├── IElement.cs │ ├── IPreservingOffset.cs │ ├── IfThenElement.cs │ ├── InstructionElement.cs │ ├── MemberAssignmentElement.cs │ ├── Presentation │ │ ├── ElementHelper.cs │ │ └── Indent.cs │ ├── ReturnElement.cs │ └── VariableAssignmentElement.cs ├── Expressive.csproj ├── Expressive.nuspec ├── ExpressiveEngine.cs ├── ExpressiveExtensions.cs ├── External │ └── ExpressionVisitor.cs ├── IDecompiler.cs ├── IDisassembler.cs ├── IInliner.cs ├── Inliner.cs ├── Inlining │ └── InliningVisitor.cs ├── Matching │ ├── Matcher.cs │ ├── MatcherBinaryExtensions.cs │ ├── MatcherCollectionExtensions.cs │ ├── MatcherConstantExtensions.cs │ ├── MatcherExpressionExtensions.cs │ ├── MatcherMemberExtensions.cs │ ├── MatcherMethodCallExtensions.cs │ ├── MatcherNewExtensions.cs │ ├── MatcherParameterExtensions.cs │ └── MatcherUnaryExtensions.cs ├── Properties │ └── AssemblyInfo.cs └── packages.config ├── nuget-pack.bat ├── nuget.bat ├── nuget.config └── readme.md /$packages/AshMind.Extensions.1.0.4/AshMind.Extensions.1.0.4.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashmind/expressive/4c33c8db5f6e9a89d559bfc99746cb7b7df47a98/$packages/AshMind.Extensions.1.0.4/AshMind.Extensions.1.0.4.nupkg -------------------------------------------------------------------------------- /$packages/AshMind.Extensions.1.0.4/lib/3.5/AshMind.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashmind/expressive/4c33c8db5f6e9a89d559bfc99746cb7b7df47a98/$packages/AshMind.Extensions.1.0.4/lib/3.5/AshMind.Extensions.dll -------------------------------------------------------------------------------- /$packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /$tools/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashmind/expressive/4c33c8db5f6e9a89d559bfc99746cb7b7df47a98/$tools/NuGet.exe -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | Bin 3 | obj 4 | Obj 5 | *.user 6 | *.suo 7 | *.vs10x 8 | _ReSharper.* 9 | -------------------------------------------------------------------------------- /Expressive.Tests/ExpectedExpressionAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | 6 | using AshMind.Extensions; 7 | 8 | namespace Expressive.Tests { 9 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field, AllowMultiple = false)] 10 | public class ExpectedExpressionAttribute : Attribute { 11 | public ExpectedExpressionAttribute(params string[] patterns) { 12 | this.Patterns = patterns.AsReadOnly(); 13 | } 14 | 15 | public ReadOnlyCollection Patterns { get; private set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Expressive.Tests/GeneralTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using Expressive.Abstraction; 7 | using Expressive.Disassembly; 8 | using Expressive.Tests.Helpers; 9 | using MbUnit.Framework; 10 | 11 | using AshMind.Extensions; 12 | 13 | using Expressive.Decompilation.Pipelines; 14 | using Expressive.Tests.Methods; 15 | using Expressive.Tests.TestClasses; 16 | 17 | namespace Expressive.Tests { 18 | [TestFixture] 19 | public class GeneralTests { 20 | [Test] 21 | [Factory("GetTestMethods")] 22 | public void TestDecompilesTo(IManagedMethod method, IEnumerable patterns) { 23 | var decompiled = Decompile(method); 24 | 25 | var parameterNames = decompiled.Parameters.Select(p => p.Name).ToArray(); 26 | var expected = patterns.Select( 27 | p => p.Contains("{0}") ? string.Format(p, parameterNames) : p 28 | ).ToList(); 29 | 30 | if (expected.Count <= 1) { 31 | Assert.AreEqual(expected.Single(), ToStringVisitor.ToString(decompiled)); 32 | } 33 | else { 34 | Assert.Contains(expected, ToStringVisitor.ToString(decompiled)); 35 | } 36 | } 37 | 38 | [Test] 39 | [Factory("GetTestMethods")] 40 | public void TestDecompilationResultHasCorrectParameters(IManagedMethod method) { 41 | var decompiled = Decompile(method); 42 | var parameterTypes = method.GetParameters().Select(p => p.ParameterType).ToList(); 43 | if (!method.IsStatic) 44 | parameterTypes.Insert(0, method.DeclaringType); 45 | 46 | Assert.AreElementsSame(parameterTypes, decompiled.Parameters.Select(p => p.Type)); 47 | } 48 | 49 | [Test] 50 | [Factory("GetTestMethods")] 51 | public void TestDecompilationResultIsCompilable(IManagedMethod method) { 52 | var decompiled = Decompile(method); 53 | Assert.DoesNotThrow(() => decompiled.Compile()); 54 | } 55 | 56 | [Test] 57 | [Ignore("http://connect.microsoft.com/VisualStudio/feedback/details/361546/provide-implementation-for-getmethodbody-in-dynamicmethod")] 58 | public void TestDecompilationOfConversion( 59 | [Column(typeof(int))] Type originalType, 60 | [Column(typeof(int))] Type targetType 61 | ) { 62 | var parameter = Expression.Parameter(originalType, "x"); 63 | var lambda = Expression.Lambda( 64 | typeof(Func<,>).MakeGenericType(originalType, targetType), 65 | parameter, 66 | parameter 67 | ).Compile(); 68 | 69 | var decompiled = Decompile(new MethodBaseAdapter(lambda.Method)); 70 | Assert.AreEqual("x => x", decompiled.ToString()); 71 | } 72 | 73 | private static LambdaExpression Decompile(IManagedMethod method) { 74 | return new Decompiler( 75 | new TestDisassembler((bytes, context) => new InstructionReader(bytes, context)), 76 | new DefaultPipeline() 77 | ).Decompile(method); 78 | } 79 | 80 | public IEnumerable GetTestMethods() { 81 | var someTestClass = typeof(ClassWithNames); 82 | var attributed = from type in someTestClass.Assembly.GetTypes() 83 | where type.Namespace == someTestClass.Namespace 84 | from member in type.GetMembers() 85 | let attribute = member.GetCustomAttributes().SingleOrDefault() 86 | where attribute != null 87 | select new { method = GetMethod(member), attribute }; 88 | 89 | return attributed.Select(a => new object[] { 90 | a.method, 91 | a.attribute.Patterns 92 | }); 93 | } 94 | 95 | private IManagedMethod GetMethod(MemberInfo member) { 96 | var property = member as PropertyInfo; 97 | if (property != null) 98 | return new MethodBaseAdapter(property.GetGetMethod()); 99 | 100 | var field = member as FieldInfo; 101 | if (field != null) 102 | return (IManagedMethod)field.GetValue(null); 103 | 104 | return new MethodBaseAdapter((MethodBase)member); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Expressive.Tests/Helpers/Assembler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | 7 | using AshMind.Extensions; 8 | using Expressive.Disassembly.Instructions; 9 | 10 | namespace Expressive.Tests.Helpers { 11 | public class Assembler { 12 | private readonly IDictionary OperandSizes = new Dictionary { 13 | { OperandType.InlineBrTarget, 4 }, 14 | { OperandType.InlineField, 4 }, 15 | { OperandType.InlineI, 4 }, 16 | { OperandType.InlineI8, 8 }, 17 | { OperandType.InlineMethod, 4 }, 18 | { OperandType.InlineR, 8 }, 19 | { OperandType.InlineSig, 4 }, 20 | { OperandType.InlineString, 4 }, 21 | { OperandType.InlineSwitch, 4 }, 22 | { OperandType.InlineTok, 4 }, 23 | { OperandType.InlineType, 4 }, 24 | { OperandType.InlineVar, 2 }, 25 | { OperandType.ShortInlineBrTarget, 1 }, 26 | { OperandType.ShortInlineI, 1 }, 27 | { OperandType.ShortInlineR, 4 }, 28 | { OperandType.ShortInlineVar, 1 } 29 | }; 30 | 31 | private int offset = 0; 32 | private readonly IList instructions = new List(); 33 | 34 | public static Assembler Start { 35 | get { return new Assembler(); } 36 | } 37 | 38 | private Assembler Append(Instruction instruction) { 39 | this.instructions.Add(instruction); 40 | this.offset += instruction.OpCode.Size + OperandSizes.GetValueOrDefault(instruction.OpCode.OperandType); 41 | return this; 42 | } 43 | 44 | private Assembler Simple(OpCode opCode) { 45 | return this.Append(new SimpleInstruction(this.offset, opCode)); 46 | } 47 | 48 | private Assembler Branch(OpCode opCode, int targetOffset) { 49 | return this.Append(new BranchInstruction(this.offset, opCode, targetOffset)); 50 | } 51 | 52 | public IList End { 53 | get { return this.instructions; } 54 | } 55 | 56 | public Assembler Nop { 57 | get { return Simple(OpCodes.Nop); } 58 | } 59 | 60 | public Assembler Ldarg_0 { 61 | get { return Simple(OpCodes.Ldarg_0); } 62 | } 63 | 64 | public Assembler Ldarg_1 { 65 | get { return Simple(OpCodes.Ldarg_1); } 66 | } 67 | 68 | public Assembler Ldc_I4_0 { 69 | get { return Simple(OpCodes.Ldc_I4_0); } 70 | } 71 | 72 | public Assembler Ldc_I4_1 { 73 | get { return Simple(OpCodes.Ldc_I4_1); } 74 | } 75 | 76 | public Assembler Stloc_0 { 77 | get { return Simple(OpCodes.Stloc_0); } 78 | } 79 | 80 | public Assembler Ldloc_0 { 81 | get { return Simple(OpCodes.Ldloc_0); } 82 | } 83 | 84 | public Assembler Ret { 85 | get { return Simple(OpCodes.Ret); } 86 | } 87 | 88 | public Assembler Br_S(int targetOffset) { 89 | return Branch(OpCodes.Br_S, targetOffset); 90 | } 91 | 92 | public Assembler Beq_S(int targetOffset) { 93 | return Branch(OpCodes.Beq_S, targetOffset); 94 | } 95 | 96 | public Assembler Bne_Un_S(int targetOffset) { 97 | return Branch(OpCodes.Bne_Un_S, targetOffset); 98 | } 99 | 100 | public Assembler Brtrue_S(int targetOffset) { 101 | return Branch(OpCodes.Brtrue_S, targetOffset); 102 | } 103 | 104 | public Assembler Brfalse_S(int targetOffset) { 105 | return Branch(OpCodes.Brfalse_S, targetOffset); 106 | } 107 | 108 | public Assembler Callvirt(Expression> call) { 109 | return Append(new MethodReferenceInstruction( 110 | this.offset, OpCodes.Callvirt, 111 | Method.Get(call) 112 | )); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Expressive.Tests/Helpers/Method.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | namespace Expressive.Tests.Helpers { 8 | public static class Method { 9 | public static string Name(Expression> call) { 10 | return Method.Get(call).Name; 11 | } 12 | 13 | public static MethodInfo Get(Expression call) { 14 | return ((MethodCallExpression)call.Body).Method; 15 | } 16 | 17 | public static MethodInfo Get(Expression> call) { 18 | var method = ((MethodCallExpression)call.Body).Method; 19 | if (typeof(TDeclaring).IsValueType && method.DeclaringType == typeof(object)) { 20 | // weird stuff :) 21 | method = typeof(TDeclaring).GetMethod( 22 | method.Name, 23 | method.GetParameters().Select(p => p.ParameterType).ToArray() 24 | ); 25 | if (method == null) 26 | throw new InvalidOperationException("Method resolution failed for " + call); 27 | } 28 | 29 | return method; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Expressive.Tests/Helpers/Property.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | namespace Expressive.Tests.Helpers { 8 | public static class Property { 9 | public static PropertyInfo Get(Expression> reference) { 10 | return Property.Get((LambdaExpression)reference); 11 | } 12 | 13 | public static PropertyInfo Get(Expression> reference) { 14 | return Property.Get((LambdaExpression)reference); 15 | } 16 | 17 | public static PropertyInfo Get(LambdaExpression reference) { 18 | return Property.Hierarchy(reference).Single(); 19 | } 20 | 21 | public static PropertyInfo Get(MemberExpression member) { 22 | return Property.Hierarchy(member).Single(); 23 | } 24 | 25 | public static IEnumerable GetAll(params Expression>[] references) { 26 | return references.SelectMany(Property.GetAll); 27 | } 28 | 29 | public static IEnumerable GetAll(Expression> reference) { 30 | var target = UnpackUnaryIfRequired(reference.Body); 31 | var member = target as MemberExpression; 32 | if (member != null) 33 | return new[] { Property.Get(member) }; 34 | 35 | var @new = target as NewExpression; 36 | if (@new != null) 37 | return Property.GetAll(@new.Arguments); 38 | 39 | var array = target as NewArrayExpression; 40 | if (array != null && array.NodeType == ExpressionType.NewArrayInit) 41 | return Property.GetAll(array.Expressions); 42 | 43 | throw new NotSupportedException(); 44 | } 45 | 46 | private static IEnumerable GetAll(IEnumerable expressions) { 47 | return expressions.Select(UnpackUnaryIfRequired) 48 | .Cast() 49 | .Select(Property.Get); 50 | } 51 | 52 | private static Expression UnpackUnaryIfRequired(Expression expression) { 53 | // Uncast is required for cases like 'Func f = () => value.Id;' 54 | // If Id is int, compiler will put automatic cast here, changing code to 55 | // 'Func f = () => (object)value.Id;' 56 | var cast = (expression as UnaryExpression); 57 | if (cast != null && cast.NodeType == ExpressionType.Convert) 58 | return cast.Operand; 59 | 60 | return expression; 61 | } 62 | 63 | private static PropertyInfo[] Hierarchy(LambdaExpression reference) { 64 | var member = (MemberExpression)UnpackUnaryIfRequired(reference.Body); 65 | return Property.Hierarchy(member); 66 | } 67 | 68 | private static PropertyInfo[] Hierarchy(MemberExpression member) { 69 | var properties = new List(); 70 | 71 | var next = member; 72 | while (next != null) { 73 | // The next check is required for closured values. 74 | // For example, '() => value.Name' is not just a MemberReference to Name, it 75 | // is an '__automatic_closure_instance.value.Name'. 76 | // I am not sure if it is the best way to check for it, but I do not see the 77 | // perfect solution right now. 78 | if (next.Member is FieldInfo && next.Expression is ConstantExpression) 79 | break; 80 | 81 | properties.Add((PropertyInfo)next.Member); 82 | next = next.Expression as MemberExpression; 83 | } 84 | 85 | properties.Reverse(); 86 | return properties.ToArray(); 87 | } 88 | 89 | public static string[] Names(params Expression>[] references) { 90 | return Array.ConvertAll(references, Property.Name); 91 | } 92 | 93 | public static string Name(Expression> reference) { 94 | return Property.Name(Property.Hierarchy(reference)); 95 | } 96 | 97 | public static string Name(Expression> reference) { 98 | return Property.Name(Property.Hierarchy(reference)); 99 | } 100 | 101 | public static string Name(Expression> reference) { 102 | return Property.Name(Property.Hierarchy(reference)); 103 | } 104 | 105 | public static string Name(MemberExpression reference) { 106 | return Property.Name(Property.Hierarchy(reference)); 107 | } 108 | 109 | private static string Name(PropertyInfo[] hierarchy) { 110 | return string.Join(".", Array.ConvertAll(hierarchy, p => p.Name)); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Expressive.Tests/InlinerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using Expressive.Decompilation.Pipelines; 6 | using Expressive.Tests.Helpers; 7 | using MbUnit.Framework; 8 | 9 | using Expressive.Tests.TestClasses; 10 | 11 | namespace Expressive.Tests { 12 | [TestFixture] 13 | public class InlinerTest { 14 | [Test] 15 | public void TestInliningOfBasicProperty() { 16 | Expression> predicate = c => c.FullNameSimple.Contains("Test"); // not a recommended database scenario 17 | var inliner = new Inliner(ExpressiveEngine.GetDecompiler()); 18 | 19 | var inlined = inliner.Inline(predicate, p => p.Name == "FullNameSimple"); 20 | 21 | Assert.AreEqual("c => String.Concat(c.FirstName, \" \", c.LastName).Contains(\"Test\")", ToStringVisitor.ToString(inlined)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Expressive.Tests/Massive/InstructionCollectionVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection.Emit; 3 | 4 | using Expressive.Elements; 5 | 6 | namespace Expressive.Tests.Massive { 7 | public class InstructionCollectingVisitor : ElementVisitor { 8 | public HashSet OpCodes { get; private set; } 9 | 10 | public InstructionCollectingVisitor() { 11 | this.OpCodes = new HashSet(); 12 | } 13 | 14 | protected override IElement VisitInstruction(InstructionElement instruction) { 15 | this.OpCodes.Add(instruction.OpCode); 16 | return base.VisitInstruction(instruction); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Expressive.Tests/Massive/InstructionSupportTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | 8 | using Expressive.Abstraction; 9 | using Expressive.Decompilation; 10 | using Expressive.Decompilation.Pipelines; 11 | using Expressive.Decompilation.Steps.StatementInlining; 12 | using Expressive.Disassembly.Instructions; 13 | using Expressive.Elements; 14 | using MbUnit.Framework; 15 | 16 | namespace Expressive.Tests.Massive { 17 | [TestFixture] 18 | public class InstructionSupportTest { 19 | private static readonly HashSet UnsupportedOpCodes = new HashSet { 20 | OpCodes.Leave_S, 21 | OpCodes.Stfld, 22 | OpCodes.Switch, 23 | OpCodes.Throw, 24 | OpCodes.Endfinally 25 | }; 26 | 27 | [Test] 28 | [Ignore("Manual only for now")] 29 | [Factory("GetAllMethodsThatHaveBodies")] 30 | public void TestAllInstructionsCanBeDisassembled(IManagedMethod method) { 31 | var disassembler = ExpressiveEngine.GetDisassembler(); 32 | Assert.DoesNotThrow(() => disassembler.Disassemble(method)); 33 | } 34 | 35 | [Test] 36 | [Ignore("Not passing yet")] 37 | public void TestAllInstructionsExceptSpecificOnesAreProcessed() { 38 | var pipeline = new DefaultPipeline().Without(); 39 | var disassembler = ExpressiveEngine.GetDisassembler(); 40 | var visitor = new InstructionCollectingVisitor(); 41 | 42 | foreach (var method in GetAllNonGenericMethods()) { 43 | var elements = disassembler.Disassemble(method) 44 | .Select(i => (IElement)new InstructionElement(i)).ToList(); 45 | try { ApplyPipeline(pipeline, elements, method); } catch { continue; } 46 | visitor.VisitList(elements); 47 | } 48 | 49 | Assert.AreElementsEqual( 50 | new OpCode[0], 51 | visitor.OpCodes.Except(UnsupportedOpCodes).OrderBy(code => code.Name) 52 | ); 53 | } 54 | 55 | [Test] 56 | [Ignore("Manual only for now")] 57 | [Factory("GetAllSupportedMethods")] 58 | public void TestNoExceptionsAreThrownWhenDecompiling(IManagedMethod method, IList instructions) { 59 | var pipeline = new DefaultPipeline().Without(); 60 | var elements = instructions.Select(i => (IElement)new InstructionElement(i)).ToList(); 61 | Assert.DoesNotThrow(() => { 62 | try { 63 | ApplyPipeline(pipeline, elements, method); 64 | } 65 | catch (NotSupportedException) { 66 | } 67 | }); 68 | } 69 | 70 | private IEnumerable GetAllSupportedMethods() { 71 | var disassembler = ExpressiveEngine.GetDisassembler(); 72 | return GetAllNonGenericMethods() 73 | .Select(method => new { method, instructions = disassembler.Disassemble(method).ToList() }) 74 | .Where(x => !x.instructions.Any(i => UnsupportedOpCodes.Contains(i.OpCode))) 75 | .Select(x => new object[] { x.method, x.instructions }); 76 | } 77 | 78 | private IEnumerable GetAllNonGenericMethods() { 79 | return GetAllMethodsRaw().Where(m => !m.IsGenericMethodDefinition) 80 | .Select(method => new MethodBaseAdapter(method)); 81 | } 82 | 83 | private IEnumerable GetAllMethodsThatHaveBodies() { 84 | return GetAllMethodsRaw().Where(m => m.GetMethodBody() != null) 85 | .Select(method => new MethodBaseAdapter(method)); 86 | } 87 | 88 | private static IEnumerable GetAllMethodsRaw() { 89 | return typeof(string).Assembly.GetTypes() 90 | .SelectMany(t => t.GetMethods()); 91 | } 92 | 93 | private static void ApplyPipeline(IDecompilationPipeline pipeline, IList elements, IManagedMethod method) { 94 | var parameters = method.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToList(); 95 | if (!method.IsStatic) 96 | parameters.Insert(0, Expression.Parameter(method.DeclaringType, "")); 97 | 98 | var context = new DecompilationContext(null, method, i => parameters[i]); 99 | foreach (var step in pipeline.GetSteps()) { 100 | step.Apply(elements, context); 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Expressive.Tests/Methods/TestDisassembler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Abstraction; 6 | using Expressive.Disassembly; 7 | using Expressive.Disassembly.Instructions; 8 | 9 | namespace Expressive.Tests.Methods { 10 | public class TestDisassembler : Disassembler { 11 | public TestDisassembler(Func instructionReaderFactory) : base(instructionReaderFactory) { 12 | } 13 | 14 | public override IEnumerable Disassemble(IManagedMethod method) { 15 | var assembled = method as TestMethod; 16 | if (assembled != null) 17 | return assembled.GetInstructions(); 18 | 19 | return base.Disassemble(method); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Expressive.Tests/Methods/TestMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | using Expressive.Abstraction; 7 | using Expressive.Disassembly.Instructions; 8 | 9 | namespace Expressive.Tests.Methods { 10 | public class TestMethod : IManagedMethod { 11 | private readonly string name; 12 | private readonly Type returnType; 13 | private readonly IEnumerable parameters; 14 | private readonly IList localTypes; 15 | private readonly Instruction[] instructions; 16 | 17 | public TestMethod(string name, Type returnType, IEnumerable parameters, IList localTypes, Instruction[] instructions) { 18 | this.name = name; 19 | this.returnType = returnType; 20 | this.parameters = parameters; 21 | this.localTypes = localTypes; 22 | this.instructions = instructions; 23 | } 24 | 25 | public string Name { 26 | get { return this.name; } 27 | } 28 | 29 | public Type ReturnType { 30 | get { return returnType; } 31 | } 32 | 33 | public IEnumerable GetParameters() { 34 | return parameters.ToArray(); 35 | } 36 | 37 | public Type GetTypeOfLocal(int index) { 38 | return this.localTypes[index]; 39 | } 40 | 41 | public Instruction[] GetInstructions() { 42 | return this.instructions; 43 | } 44 | 45 | public IManagedContext Context { 46 | get { throw new NotImplementedException(); } 47 | } 48 | 49 | public bool IsStatic { 50 | get { return true; } 51 | } 52 | 53 | public Type DeclaringType { 54 | get { return null; } 55 | } 56 | 57 | public byte[] GetBodyByteArray() { 58 | throw new NotSupportedException(); 59 | } 60 | 61 | public override string ToString() { 62 | return string.Format( 63 | "{0} {1}({2})", 64 | this.ReturnType.Name, this.Name, 65 | string.Join(", ", this.parameters.Select(p => p.ParameterType.Name)) 66 | ); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Expressive.Tests/Methods/TestMethodBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Abstraction; 6 | using Expressive.Disassembly.Instructions; 7 | using Expressive.Tests.Helpers; 8 | 9 | namespace Expressive.Tests.Methods { 10 | public class TestMethodBuilder { 11 | private string name; 12 | private Type returnType; 13 | private readonly IList parameters = new List(); 14 | private readonly IList localTypes = new List(); 15 | private Instruction[] instructions; 16 | 17 | public TestMethodBuilder Name(string name) { 18 | this.name = name; 19 | return this; 20 | } 21 | 22 | public TestMethodBuilder Returns() { 23 | return this.Returns(typeof(T)); 24 | } 25 | 26 | public TestMethodBuilder Returns(Type type) { 27 | this.returnType = type; 28 | return this; 29 | } 30 | 31 | public TestMethodBuilder Parameter(string name) { 32 | return this.Parameter(name, typeof(T)); 33 | } 34 | 35 | public TestMethodBuilder Parameter(string name, Type type) { 36 | this.parameters.Add(new TestMethodParameter(name, type)); 37 | return this; 38 | } 39 | 40 | public TestMethodBuilder Local() { 41 | return this.Local(typeof(T)); 42 | } 43 | 44 | public TestMethodBuilder Local(Type type) { 45 | this.localTypes.Add(type); 46 | return this; 47 | } 48 | 49 | public TestMethodBuilder Assemble(Func assemble) { 50 | this.instructions = assemble(Assembler.Start).End.ToArray(); 51 | return this; 52 | } 53 | 54 | public IManagedMethod ToMethod() { 55 | return new TestMethod( 56 | this.name ?? "TestMethod?", 57 | this.returnType ?? typeof(void), 58 | this.parameters, 59 | this.localTypes, 60 | this.instructions 61 | ); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Expressive.Tests/Methods/TestMethodParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Abstraction; 6 | 7 | namespace Expressive.Tests.Methods { 8 | public class TestMethodParameter : IManagedMethodParameter { 9 | public TestMethodParameter(string name, Type parameterType) { 10 | this.Name = name; 11 | this.ParameterType = parameterType; 12 | } 13 | 14 | public string Name { get; private set; } 15 | public Type ParameterType { get; private set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Expressive.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Expressive.Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Expressive.Tests")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2011")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("55633dc3-3c71-47fc-854b-e529e0617d44")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/Booleans.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Abstraction; 6 | using Expressive.Tests.Methods; 7 | 8 | namespace Expressive.Tests.TestClasses { 9 | public static class Booleans { 10 | [ExpectedExpression("a => !a")] 11 | public static bool Not(bool a) 12 | { 13 | return !a; 14 | } 15 | 16 | [ExpectedExpression("(a, b) => (a && b)")] 17 | public static bool And(bool a, bool b) { 18 | return a && b; 19 | } 20 | 21 | [ExpectedExpression("(a, b) => (a || b)")] 22 | public static bool Or(bool a, bool b) { 23 | return a || b; 24 | } 25 | 26 | [ExpectedExpression("() => Booleans.Or(True, False)")] 27 | public static bool CallWithConstants() { 28 | return Or(true, false); 29 | } 30 | 31 | private const string ObjectEqualsExpression1 = "(a, b) => ((a == b) || ((a != null) && ((b != null) && a.Equals(b))))"; 32 | private const string ObjectEqualsExpression2 = "(a, b) => ((a == b) || (((a != null) && (b != null)) && a.Equals(b)))"; 33 | 34 | [ExpectedExpression(ObjectEqualsExpression1, ObjectEqualsExpression2)] 35 | public static bool ObjectEquals(object a, object b) { 36 | return a == b 37 | || ( 38 | (a != null) && (b != null) && a.Equals(b) 39 | ); 40 | } 41 | 42 | [ExpectedExpression(ObjectEqualsExpression1, ObjectEqualsExpression2)] 43 | public static readonly IManagedMethod ObjectEqualsRelease = new TestMethodBuilder() 44 | .Name("ObjectEquals{Release}") 45 | .Parameter("a") 46 | .Parameter("b") 47 | .Assemble(a => a 48 | .Ldarg_0 49 | .Ldarg_1 50 | .Beq_S(0x14) 51 | .Ldarg_0 52 | .Brfalse_S(0x12) 53 | .Ldarg_1 54 | .Brfalse_S(0x12) 55 | .Ldarg_0 56 | .Ldarg_1 57 | .Callvirt(o => o.Equals(null)) 58 | .Ret 59 | .Ldc_I4_0 60 | .Ret 61 | .Ldc_I4_1 62 | .Ret 63 | ) 64 | .Returns() 65 | .ToMethod(); 66 | 67 | [ExpectedExpression(ObjectEqualsExpression1, ObjectEqualsExpression2)] 68 | public static readonly IManagedMethod OtherEquals = new TestMethodBuilder() 69 | .Name("AssembledEquals") 70 | .Parameter("a") 71 | .Parameter("b") 72 | .Assemble(a => a 73 | .Ldarg_0 74 | .Ldarg_1 75 | .Bne_Un_S(0x6) 76 | .Ldc_I4_1 77 | .Ret 78 | .Ldarg_0 79 | .Brfalse_S(0xC) 80 | .Ldarg_1 81 | .Brtrue_S(0xE) 82 | .Ldc_I4_0 83 | .Ret 84 | .Ldarg_0 85 | .Ldarg_1 86 | .Callvirt(o => o.Equals(null)) 87 | .Ret 88 | ) 89 | .Returns() 90 | .ToMethod(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/ClassWithNames.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Tests.TestClasses { 6 | public class ClassWithNames { 7 | public string FirstName { get; set; } 8 | public string LastName { get; set; } 9 | 10 | [ExpectedExpression(@"() => ""Test""")] 11 | public static string StaticName { 12 | get { return "Test"; } 13 | } 14 | 15 | [ExpectedExpression("{0} => {0}.FirstName")] 16 | public string JustFirstName { 17 | get { return this.FirstName; } 18 | } 19 | 20 | [ExpectedExpression(@"{0} => String.Concat({0}.FirstName, "" "", {0}.LastName)")] 21 | public string FullNameSimple { 22 | get { return this.FirstName + " " + this.LastName; } 23 | } 24 | 25 | private const string FullNameExpression1 = @"{0} => (!String.IsNullOrEmpty({0}.FirstName) ? String.Concat({0}.FirstName, "" "", {0}.LastName) : {0}.LastName)"; 26 | private const string FullNameExpression2 = @"{0} => (String.IsNullOrEmpty({0}.FirstName) ? {0}.LastName : String.Concat({0}.FirstName, "" "", {0}.LastName))"; 27 | 28 | [ExpectedExpression(FullNameExpression1, FullNameExpression2)] 29 | public string FullNameWithInlineConditional { 30 | get { 31 | return string.IsNullOrEmpty(this.FirstName) 32 | ? this.LastName 33 | : this.FirstName + " " + this.LastName; 34 | } 35 | } 36 | 37 | [ExpectedExpression(FullNameExpression1, FullNameExpression2)] 38 | public string FullNameWithExplicitConditional { 39 | get { 40 | if (string.IsNullOrEmpty(this.FirstName)) 41 | return this.LastName; 42 | 43 | return this.FirstName + " " + this.LastName; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/Constants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Tests.TestClasses { 6 | public static class Constants { 7 | [ExpectedExpression("() => 10")] 8 | public static sbyte SByte() { 9 | return 10; 10 | } 11 | 12 | [ExpectedExpression("() => 10")] 13 | public static byte Byte() { 14 | return 10; 15 | } 16 | 17 | [ExpectedExpression("() => 1000")] 18 | public static short Int16() { 19 | return 1000; 20 | } 21 | 22 | [ExpectedExpression("() => 1000")] 23 | public static ushort UInt16() { 24 | return 1000; 25 | } 26 | 27 | [ExpectedExpression("() => 100000")] 28 | public static int Int32() { 29 | return 100000; 30 | } 31 | 32 | [ExpectedExpression("() => 100000")] 33 | public static uint UInt32() { 34 | return 100000; 35 | } 36 | 37 | [ExpectedExpression("() => 10000000000")] 38 | public static long Int64() { 39 | return 10000000000; 40 | } 41 | 42 | [ExpectedExpression("() => 10000000000")] 43 | public static ulong UInt64() { 44 | return 10000000000; 45 | } 46 | 47 | [ExpectedExpression("() => 1")] 48 | public static float Single() { 49 | return 1.0F; 50 | } 51 | 52 | [ExpectedExpression("() => 1")] 53 | public static double Double() { 54 | return 1.0D; 55 | } 56 | 57 | [ExpectedExpression("() => \"test\"")] 58 | public static string String() { 59 | return "test"; 60 | } 61 | 62 | // [ExpectedExpression("() => 'x'")] 63 | // not working as expected yet 64 | public static char Char() { 65 | return 'x'; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/Conversions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Tests.TestClasses { 6 | public static class Conversions { 7 | private const string Implicit = "x => x"; 8 | 9 | #region Int32ToXXX 10 | 11 | [ExpectedExpression("x => (SByte)x")] 12 | public static sbyte Int32ToSByte(int x) { 13 | return (sbyte)x; 14 | } 15 | 16 | [ExpectedExpression("x => (Byte)x")] 17 | public static byte Int32ToByte(int x) { 18 | return (byte)x; 19 | } 20 | 21 | [ExpectedExpression("x => (Int16)x")] 22 | public static short Int32ToInt16(int x) { 23 | return (short)x; 24 | } 25 | 26 | [ExpectedExpression("x => (UInt16)x")] 27 | public static ushort Int32ToUInt16(int x) { 28 | return (ushort)x; 29 | } 30 | 31 | [ExpectedExpression(Implicit)] 32 | public static uint Int32ToUInt32(int x) { 33 | return (uint)x; 34 | } 35 | 36 | [ExpectedExpression("x => (Int64)x")] 37 | public static long Int32ToInt64(int x) { 38 | return x; 39 | } 40 | 41 | [ExpectedExpression("x => (Int64)x")] 42 | public static ulong Int32ToUInt64(int x) { 43 | return (ulong)x; 44 | } 45 | 46 | [ExpectedExpression("x => (Single)x")] 47 | public static float Int32ToSingle(int x) { 48 | return x; 49 | } 50 | 51 | [ExpectedExpression("x => (Double)x")] 52 | public static double Int32ToDouble(int x) { 53 | return x; 54 | } 55 | 56 | #endregion 57 | 58 | #region Int64ToXXX 59 | 60 | [ExpectedExpression("x => (SByte)x")] 61 | public static sbyte Int64ToSByte(long x) { 62 | return (sbyte)x; 63 | } 64 | 65 | [ExpectedExpression("x => (Byte)x")] 66 | public static byte Int64ToByte(long x) { 67 | return (byte)x; 68 | } 69 | 70 | [ExpectedExpression("x => (Int16)x")] 71 | public static short Int64ToInt16(long x) { 72 | return (short)x; 73 | } 74 | 75 | [ExpectedExpression("x => (UInt16)x")] 76 | public static ushort Int64ToUInt16(long x) { 77 | return (ushort)x; 78 | } 79 | 80 | [ExpectedExpression("x => (Int32)x")] 81 | public static int Int64ToInt32(long x) { 82 | return (int)x; 83 | } 84 | 85 | [ExpectedExpression("x => (UInt32)x")] 86 | public static uint Int64ToUInt32(long x) { 87 | return (uint)x; 88 | } 89 | 90 | [ExpectedExpression(Implicit)] 91 | public static ulong Int64ToUInt64(long x) { 92 | return (ulong)x; 93 | } 94 | 95 | [ExpectedExpression("x => (Single)x")] 96 | public static float Int64ToSingle(long x) { 97 | return x; 98 | } 99 | 100 | [ExpectedExpression("x => (Double)x")] 101 | public static double Int64ToDouble(long x) { 102 | return x; 103 | } 104 | 105 | #endregion 106 | 107 | #region DoubleToXXX 108 | 109 | [ExpectedExpression("x => (SByte)x")] 110 | public static sbyte DoubleToSByte(double x) { 111 | return (sbyte)x; 112 | } 113 | 114 | [ExpectedExpression("x => (Byte)x")] 115 | public static byte DoubleToByte(double x) { 116 | return (byte)x; 117 | } 118 | 119 | [ExpectedExpression("x => (Int16)x")] 120 | public static short DoubleToInt16(double x) { 121 | return (short)x; 122 | } 123 | 124 | [ExpectedExpression("x => (UInt16)x")] 125 | public static ushort DoubleToUInt16(double x) { 126 | return (ushort)x; 127 | } 128 | 129 | [ExpectedExpression("x => (Int32)x")] 130 | public static int DoubleToInt32(double x) { 131 | return (int)x; 132 | } 133 | 134 | [ExpectedExpression("x => (UInt32)x")] 135 | public static uint DoubleToUInt32(double x) { 136 | return (uint)x; 137 | } 138 | 139 | [ExpectedExpression("x => (Int64)x")] 140 | public static long DoubleToInt64(double x) { 141 | return (long)x; 142 | } 143 | 144 | [ExpectedExpression("x => (UInt64)x")] 145 | public static ulong DoubleToUInt64(double x) { 146 | return (ulong)x; 147 | } 148 | 149 | [ExpectedExpression("x => (Single)x")] 150 | public static float DoubleToSingle(double x) { 151 | return (float)x; 152 | } 153 | 154 | #endregion 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/Initializers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Tests.TestClasses { 6 | public static class Initializers { 7 | public class SimpleClass { 8 | public object FieldA; 9 | public object FieldB; 10 | 11 | public object PropertyA { get; set; } 12 | public object PropertyB { get; set; } 13 | 14 | public SimpleClass Recursive { get; set; } 15 | } 16 | 17 | [ExpectedExpression("(a, b) => new [] { a, b }")] 18 | public static object[] ObjectArray(object a, object b) { 19 | return new[] { a, b }; 20 | } 21 | 22 | [ExpectedExpression("(a, b) => new [] { a, b }")] 23 | public static int[] Int32Array(int a, int b) { 24 | return new[] { a, b }; 25 | } 26 | 27 | [ExpectedExpression("(a, b) => new List { a, b }")] 28 | public static IList ObjectList(object a, object b) { 29 | return new List { a, b }; 30 | } 31 | 32 | [ExpectedExpression("(key, value) => new Dictionary { { key, value } }")] 33 | public static IDictionary ObjectDictionary(object key, object value) { 34 | return new Dictionary { { key, value } }; 35 | } 36 | 37 | [ExpectedExpression("(a, b) => new SimpleClass { PropertyA = a, PropertyB = b }")] 38 | public static SimpleClass ClassWithProperties(object a, object b) { 39 | return new SimpleClass { PropertyA = a, PropertyB = b }; 40 | } 41 | 42 | [ExpectedExpression("(a, b) => new SimpleClass { FieldA = a, FieldB = b }")] 43 | public static SimpleClass ClassWithFields(object a, object b) { 44 | return new SimpleClass { FieldA = a, FieldB = b }; 45 | } 46 | 47 | [ExpectedExpression("(a, b) => new SimpleClass { PropertyA = a, Recursive = new SimpleClass { PropertyB = b } }")] 48 | public static SimpleClass ClassWithRecursiveUsingDirectSetter(object a, object b) { 49 | return new SimpleClass { 50 | PropertyA = a, 51 | Recursive = new SimpleClass { PropertyB = b } 52 | }; 53 | } 54 | 55 | // not supported for now: 56 | // [ExpectedExpression("(a, b) => new SimpleClass { PropertyA = a, Recursive = { PropertyB = b }}")] 57 | public static SimpleClass ClassWithRecursiveUsingImplicitSetter(object a, object b) { 58 | return new SimpleClass { 59 | PropertyA = a, 60 | Recursive = { PropertyB = b } 61 | }; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/Lambdas.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Tests.TestClasses { 6 | public static class Lambdas { 7 | [ExpectedExpression("query => query.Where(c => (c.FirstName.Length > 5))")] 8 | public static IEnumerable SimpleLambda(IEnumerable query) { 9 | return query.Where(c => c.FirstName.Length > 5); 10 | } 11 | 12 | [ExpectedExpression("(query, length) => query.Where(c => (c.FirstName.Length > length))")] 13 | public static IEnumerable LambdaWithClosureOverParameter(IEnumerable query, int length) { 14 | return query.Where(c => c.FirstName.Length > length); 15 | } 16 | 17 | [ExpectedExpression("() => Lambdas.Return(() => Lambdas.Return(() => Lambdas.Return(() => null)))")] 18 | public static object LambdaWithinLambdaWithnLambda() { 19 | return Return(() => Return(() => Return(() => null))); 20 | } 21 | 22 | private static object Return(Func func) { 23 | return func(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/Nulls.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Tests.TestClasses { 6 | public static class Nulls { 7 | [ExpectedExpression("(a, b) => (a ?? b)")] 8 | public static object CoalesceObjects(object a, object b) { 9 | return a ?? b; 10 | } 11 | 12 | [ExpectedExpression("(a, b) => (a ?? b)")] 13 | public static int? CoalesceNullable(int? a, int? b) { 14 | return a ?? b; 15 | } 16 | 17 | [ExpectedExpression("(a, b) => (a ?? b)")] 18 | public static object IfNull(object a, object b) { 19 | return a != null ? a : b; 20 | } 21 | 22 | [ExpectedExpression("a => a.Value")] 23 | public static int CastFromNullable(int? a) { 24 | return (int)a; 25 | } 26 | 27 | [ExpectedExpression("a => (Int32?)a")] 28 | public static int? CastToNullable(int a) { 29 | return a; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/Numerics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Tests.TestClasses { 6 | public static class Numerics { 7 | [ExpectedExpression("(a, b) => (a + b)")] 8 | public static int Add(int a, int b) { 9 | return a + b; 10 | } 11 | 12 | [ExpectedExpression("(a, b) => (a - b)")] 13 | public static int Subtract(int a, int b) { 14 | return a - b; 15 | } 16 | 17 | [ExpectedExpression("a => -a")] 18 | public static int Minus(int a) 19 | { 20 | return -a; 21 | } 22 | 23 | [ExpectedExpression("(a, b) => (a % b)")] 24 | public static int Mod(int a, int b) { 25 | return a % b; 26 | } 27 | 28 | [ExpectedExpression("(a, b) => (a / b)")] 29 | public static int Div(int a, int b) { 30 | return a / b; 31 | } 32 | 33 | [ExpectedExpression("(a, b) => (a * b)")] 34 | public static int Multiply(int a, int b) { 35 | return a * b; 36 | } 37 | 38 | [ExpectedExpression("a => !a")] 39 | public static int Not(int a) 40 | { 41 | return ~a; 42 | } 43 | 44 | [ExpectedExpression("(a, b) => (a & b)")] 45 | public static int And(int a, int b) { 46 | return a & b; 47 | } 48 | 49 | [ExpectedExpression("(a, b) => (a | b)")] 50 | public static int Or(int a, int b) { 51 | return a | b; 52 | } 53 | 54 | [ExpectedExpression("(a, b) => (a ^ b)")] 55 | public static int Xor(int a, int b) { 56 | return a ^ b; 57 | } 58 | 59 | [ExpectedExpression("(a, b) => (a >> (b & (Int32)31))")] 60 | public static int Shr(int a, int b) { 61 | return a >> b; 62 | } 63 | 64 | [ExpectedExpression("(a, b) => (a << (b & (Int32)31))")] 65 | public static int Shl(int a, int b) { 66 | return a << b; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Expressive.Tests/TestClasses/Range.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Expressive.Tests.TestClasses { 7 | // http://stackoverflow.com/questions/3916122/nhibernate-3-extending-linq-provider-basehqlgeneratorformethod-buildhql-problem 8 | public class Range { 9 | public int Min { get; set; } 10 | public int Max { get; set; } 11 | 12 | [ExpectedExpression("({0}, value) => ((value >= {0}.Min) && (value <= {0}.Max))")] 13 | public bool Contains(int value) { 14 | return value >= this.Min && value <= this.Max; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Expressive.Tests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Expressive.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Expressive", "Expressive\Expressive.csproj", "{25C28BD4-127C-4F95-9FDC-25D1A10E5012}" 5 | EndProject 6 | Project("{00000000-0000-0000-0000-000000000000}") = "Expressive.Tests", "Expressive.Tests\Expressive.Tests.csproj", "{5DC88E59-9153-4986-BCD7-0A5E449BA191}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {25C28BD4-127C-4F95-9FDC-25D1A10E5012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {25C28BD4-127C-4F95-9FDC-25D1A10E5012}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {25C28BD4-127C-4F95-9FDC-25D1A10E5012}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {25C28BD4-127C-4F95-9FDC-25D1A10E5012}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {5DC88E59-9153-4986-BCD7-0A5E449BA191}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {5DC88E59-9153-4986-BCD7-0A5E449BA191}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {5DC88E59-9153-4986-BCD7-0A5E449BA191}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {5DC88E59-9153-4986-BCD7-0A5E449BA191}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /Expressive/Abstraction/IManagedContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Expressive.Abstraction { 7 | public interface IManagedContext { 8 | MethodBase ResolveMethod(int token); 9 | FieldInfo ResolveField(int token); 10 | Type ResolveType(int token); 11 | string ResolveString(int token); 12 | MemberInfo ResolveMember(int token); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Expressive/Abstraction/IManagedMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Abstraction { 6 | public interface IManagedMethod { 7 | IManagedContext Context { get; } 8 | 9 | bool IsStatic { get; } 10 | Type DeclaringType { get; } 11 | 12 | Type ReturnType { get; } 13 | IEnumerable GetParameters(); 14 | Type GetTypeOfLocal(int index); 15 | 16 | byte[] GetBodyByteArray(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Expressive/Abstraction/IManagedMethodParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Abstraction { 6 | public interface IManagedMethodParameter { 7 | string Name { get; } 8 | Type ParameterType { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Expressive/Abstraction/MethodBaseAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Expressive.Abstraction { 7 | public class MethodBaseAdapter : IManagedMethod { 8 | private readonly MethodBase actual; 9 | private readonly MethodBody bodyOfActual; 10 | 11 | public MethodBaseAdapter(MethodBase actual) { 12 | this.actual = actual; 13 | this.bodyOfActual = this.actual.GetMethodBody(); 14 | if (bodyOfActual == null) 15 | throw new NotSupportedException("Method " + actual + " must have a body to adapted using this class."); 16 | 17 | this.Context = new MethodBaseModuleContext(actual); 18 | } 19 | 20 | public IManagedContext Context { get; private set; } 21 | 22 | public bool IsStatic { 23 | get { return this.actual.IsStatic; } 24 | } 25 | 26 | public Type DeclaringType { 27 | get { return this.actual.DeclaringType; } 28 | } 29 | 30 | public Type ReturnType { 31 | get { 32 | var info = this.actual as MethodInfo; 33 | return info != null ? info.ReturnType : null; 34 | } 35 | } 36 | 37 | public IEnumerable GetParameters() { 38 | return this.actual.GetParameters().Select(p => (IManagedMethodParameter)new ParameterInfoAdapter(p)); 39 | } 40 | 41 | public Type GetTypeOfLocal(int index) { 42 | return this.bodyOfActual.LocalVariables[index].LocalType; 43 | } 44 | 45 | public byte[] GetBodyByteArray() { 46 | return this.bodyOfActual.GetILAsByteArray(); 47 | } 48 | 49 | public override string ToString() { 50 | return this.actual.ToString(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Expressive/Abstraction/MethodBaseModuleContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace Expressive.Abstraction { 7 | public class MethodBaseModuleContext : IManagedContext { 8 | private readonly Module module; 9 | private readonly Type[] methodGenericArguments; 10 | private readonly Type[] typeGenericArguments; 11 | 12 | public MethodBaseModuleContext(MethodBase method) { 13 | this.module = method.Module; 14 | this.methodGenericArguments = (method.IsGenericMethod || method.IsGenericMethodDefinition) ? method.GetGenericArguments() : new Type[0]; 15 | 16 | var type = method.DeclaringType; 17 | this.typeGenericArguments = (type != null && (type.IsGenericType || type.IsGenericTypeDefinition)) ? type.GetGenericArguments() : new Type[0]; 18 | } 19 | 20 | public FieldInfo ResolveField(int token) { 21 | return this.module.ResolveField(token, this.typeGenericArguments, this.methodGenericArguments); 22 | } 23 | 24 | public MemberInfo ResolveMember(int token) { 25 | return this.module.ResolveMember(token, this.typeGenericArguments, this.methodGenericArguments); 26 | } 27 | 28 | public MethodBase ResolveMethod(int token) { 29 | return this.module.ResolveMethod(token, this.typeGenericArguments, this.methodGenericArguments); 30 | } 31 | 32 | public string ResolveString(int token) { 33 | return this.module.ResolveString(token); 34 | } 35 | 36 | public Type ResolveType(int token) { 37 | return this.module.ResolveType(token, this.typeGenericArguments, this.methodGenericArguments); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Expressive/Abstraction/ParameterInfoAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace Expressive.Abstraction { 5 | public class ParameterInfoAdapter : IManagedMethodParameter { 6 | private readonly ParameterInfo actual; 7 | 8 | public ParameterInfoAdapter(ParameterInfo actual) { 9 | this.actual = actual; 10 | } 11 | 12 | public string Name { 13 | get { return this.actual.Name; } 14 | } 15 | 16 | public Type ParameterType { 17 | get { return this.actual.ParameterType; } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/BooleanSupport.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements.Expressions; 9 | 10 | namespace Expressive.Decompilation { 11 | public static class BooleanSupport { 12 | private class BooleanConvertingVisitor : ExpressionTreeVisitor { 13 | protected override Expression Visit(Expression exp) { 14 | return exp; 15 | } 16 | 17 | public Expression AttemptToConvert(Expression exp) { 18 | var converted = base.Visit(exp); 19 | if (converted.Type == typeof(bool)) 20 | return converted; 21 | 22 | if (exp.Type == typeof(int)) 23 | return exp.NotEqual(Expression.Constant(0)); 24 | 25 | if (!exp.Type.IsValueType) 26 | return exp.NotEqual(Expression.Constant(null)); 27 | 28 | return exp; 29 | } 30 | 31 | protected override Expression VisitConstant(ConstantExpression constant) { 32 | return Expression.Constant( 33 | constant.Value != null 34 | && !Equals(constant.Value, 0) 35 | ); 36 | } 37 | 38 | protected override Expression VisitConditional(ConditionalExpression conditional) { 39 | var test = this.AttemptToConvert(conditional.Test); 40 | var ifTrue = this.AttemptToConvert(conditional.IfTrue); 41 | var ifFalse = this.AttemptToConvert(conditional.IfFalse); 42 | 43 | if (test == conditional.Test && ifTrue == conditional.IfTrue && ifFalse == conditional.IfFalse) 44 | return conditional; 45 | 46 | return Expression.Condition(test, ifTrue, ifFalse); 47 | } 48 | 49 | protected override Expression VisitBinary(BinaryExpression b) { 50 | if (b.NodeType != ExpressionType.Equal && b.NodeType != ExpressionType.NotEqual) 51 | return base.VisitBinary(b); 52 | 53 | if (b.Left.Type != typeof(int) || b.Right.Type != typeof(int)) 54 | return base.VisitBinary(b); 55 | 56 | var left = this.AttemptToConvert(b.Left); 57 | var right = this.AttemptToConvert(b.Right); 58 | 59 | if (left == b.Left && right == b.Right) 60 | return b; 61 | 62 | if (left.Type != typeof(bool) || right.Type != typeof(bool)) 63 | return b; 64 | 65 | return Expression.MakeBinary(b.NodeType, left, right); 66 | } 67 | } 68 | 69 | public static void ConvertIfRequired(ref Expression left, ref Expression right) { 70 | left = ConvertIfRequired(left, right.Type); 71 | right = ConvertIfRequired(right, left.Type); 72 | } 73 | 74 | public static Expression ConvertIfRequired(Expression expression, Type requiredType) { 75 | if (requiredType != typeof(bool)) 76 | return expression; 77 | 78 | return expression.Type == typeof(bool) 79 | ? expression 80 | : ConvertToBoolean(expression); 81 | } 82 | 83 | private static Expression ConvertToBoolean(Expression expression) { 84 | var converted = new BooleanConvertingVisitor().AttemptToConvert(expression); 85 | if (converted.Type != typeof(bool)) 86 | throw new InvalidOperationException("Could not convert type of " + expression + " to System.Boolean."); 87 | 88 | return converted; 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Expressive/Decompilation/DecompilationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | using Expressive.Abstraction; 6 | 7 | namespace Expressive.Decompilation { 8 | public class DecompilationContext { 9 | public DecompilationContext(IDecompiler decompiler, IManagedMethod method, Func getParameter) { 10 | this.Decompiler = decompiler; 11 | this.Method = method; 12 | this.GetParameter = getParameter; 13 | } 14 | 15 | public IDecompiler Decompiler { get; private set; } 16 | public IManagedMethod Method { get; private set; } 17 | public Func GetParameter { get; private set; } 18 | } 19 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/IDecompilationStep.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Elements; 6 | 7 | namespace Expressive.Decompilation { 8 | public interface IDecompilationStep { 9 | void Apply(IList elements, DecompilationContext context); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Pipelines/DecompilationPipeline.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Decompilation.Pipelines { 6 | public class DecompilationPipeline : IDecompilationPipeline { 7 | public IList Steps { get; private set; } 8 | 9 | protected DecompilationPipeline(params IDecompilationStep[] steps) { 10 | this.Steps = steps.ToList(); 11 | } 12 | 13 | IEnumerable IDecompilationPipeline.GetSteps() { 14 | return this.Steps; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Pipelines/DefaultPipeline.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using AshMind.Extensions; 6 | 7 | using Expressive.Decompilation.Steps; 8 | using Expressive.Decompilation.Steps.Clarity; 9 | using Expressive.Decompilation.Steps.IndividualElements; 10 | using Expressive.Decompilation.Steps.StatementInlining; 11 | using Expressive.Decompilation.Steps.StatementInlining.AssignmentInlining; 12 | using Expressive.Decompilation.Steps.StatementInlining.InitializerCollectors; 13 | 14 | namespace Expressive.Decompilation.Pipelines { 15 | public class DefaultPipeline : DecompilationPipeline { 16 | public DefaultPipeline() : base( 17 | new NopRemovalStep(), 18 | new BranchResolutionStep(), 19 | new UnconditionalBranchesRemovalStep(), 20 | new IndividualDecompilationStep( 21 | new LdfldToField(), 22 | new LdstrToConstant(), 23 | new LdftnToAddressOf(), 24 | new LdnullToConstant(), 25 | new ConvToConvert(), 26 | new CxxToCondition(), 27 | new MathToExpression(), 28 | new CallToElement(), 29 | new NewobjToNew(), 30 | new NewarrToNewArray(), 31 | new LdlocToVariable(), 32 | new StlocToAssignment(), 33 | new StfldToAssignment(), 34 | new StelemToAssignment(), 35 | new LdargToParameter(), 36 | new LdcToConstant(), 37 | new BranchToCondition(), 38 | new DupToCopy(), 39 | new PopToRemove(), 40 | new RetToReturn() 41 | ), 42 | new VisitorSequenceStep( 43 | c => new AddressOfToCreateDelegateVisitor(), 44 | c => new IfThenCollapsingVisitor(), // must be before following two 45 | c => new IfAssignmentInliningVisitor(), 46 | c => new IfReturnInliningVisitor(), 47 | c => new ConditionImprovementVisitor(), 48 | c => new BooleanEqualityImprovementVisitor(), 49 | c => new NotImprovementVisitor(), 50 | c => new InitializerDetectingVisitor( 51 | new AssignmentInliner(), 52 | new ObjectInitializerCollector(), 53 | new ArrayInitializerCollector(), 54 | new CollectionInitializerCollector() 55 | ), 56 | c => new NewNullableToCastVisitor() 57 | ), 58 | new VariableInliningStep(new AssignmentInliner()), 59 | new VisitorSequenceStep( 60 | c => new CoalescingVisitor(), 61 | c => new LambdaInliningVisitor(c) 62 | ) 63 | ) { 64 | } 65 | 66 | public virtual DefaultPipeline Without() { 67 | this.Steps.RemoveWhere(s => s is TPipelinePart); 68 | this.Steps.ForEach(this.RemoveFromStep); 69 | 70 | return this; 71 | } 72 | 73 | protected virtual void RemoveFromStep(IDecompilationStep step) { 74 | var individual = step as IndividualDecompilationStep; 75 | if (individual != null) { 76 | individual.Interpretations.RemoveWhere(i => i is TPipelinePart); 77 | return; 78 | } 79 | 80 | var sequence = step as VisitorSequenceStep; 81 | if (sequence != null) { 82 | sequence.Visitors.RemoveWhere(v => v is TPipelinePart); 83 | return; 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Pipelines/IDecompilationPipeline.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Decompilation.Pipelines { 6 | public interface IDecompilationPipeline { 7 | IEnumerable GetSteps(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/AddressOfToCreateDelegateVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | using AshMind.Extensions; 8 | 9 | using Expressive.Elements; 10 | using Expressive.Elements.Expressions; 11 | using Expressive.Elements.Expressions.Matchers; 12 | using Expressive.Matching; 13 | 14 | namespace Expressive.Decompilation.Steps { 15 | public class AddressOfToCreateDelegateVisitor : ElementVisitor { 16 | public static readonly MethodInfo CreateDelegateMethodInfo = ((Func)Delegate.CreateDelegate).Method; 17 | 18 | protected override Expression VisitNew(NewExpression nex) { 19 | nex = (NewExpression)base.VisitNew(nex); 20 | 21 | var instance = (Expression)null; 22 | var method = (MethodBase)null; 23 | return Matcher 24 | .For(nex) 25 | .Type(t => t.IsSubclassOf()) 26 | .Argument(0).AssignTo(out instance) 27 | 28 | .For(nex) 29 | .Argument(1).As() 30 | .Do(x => method = x.Method) 31 | 32 | .IfMatched( 33 | () => Expression.Call(null, CreateDelegateMethodInfo, new[] { 34 | Expression.Constant(nex.Type), 35 | instance, 36 | Expression.Constant(method), 37 | Expression.Constant(true) 38 | }).Convert(nex.Type), 39 | nex 40 | ); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/BooleanEqualityImprovementVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps { 9 | public class BooleanEqualityImprovementVisitor : ElementVisitor { 10 | protected override Expression VisitBinary(BinaryExpression b) { 11 | b = (BinaryExpression) base.VisitBinary(b); 12 | if (b.Left.Type != typeof(bool) || b.Right.Type != typeof(bool)) 13 | return b; 14 | 15 | if (b.NodeType != ExpressionType.Equal && b.NodeType != ExpressionType.NotEqual) 16 | return b; 17 | 18 | var leftAsConstant = b.Left as ConstantExpression; 19 | var rightAsConstant = b.Right as ConstantExpression; 20 | if (leftAsConstant == null && rightAsConstant == null) 21 | return b; 22 | 23 | return rightAsConstant != null 24 | ? Calculate(b.Left, b.NodeType, (bool)rightAsConstant.Value) 25 | : Calculate(b.Right, b.NodeType, (bool)leftAsConstant.Value); 26 | } 27 | 28 | private Expression Calculate(Expression expression, ExpressionType binaryType, bool constant) { 29 | if (binaryType == ExpressionType.NotEqual) 30 | constant = !constant; 31 | 32 | return constant ? expression : Expression.Not(expression); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/BranchProcessing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | using Expressive.Disassembly.Instructions; 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps { 9 | public static class BranchProcessing { 10 | public static bool Matches(IElement element, Func predicate) { 11 | var instruction = element as InstructionElement; 12 | if (instruction == null) 13 | return false; 14 | 15 | return predicate(instruction.OpCode); 16 | } 17 | 18 | public static int GetTargetOffset(IElement br) { 19 | var instruction = ((InstructionElement)br).Instruction; 20 | return ((BranchInstruction)instruction).TargetOffset; 21 | } 22 | 23 | public static int FindTargetIndexOrThrow(IElement br, IList elements) { 24 | var targetIndex = FindTargetIndexOrNull(br, elements); 25 | if (targetIndex == null) 26 | BranchProcessing.ThrowTargetNotFound(br); 27 | 28 | return (int)targetIndex; 29 | } 30 | 31 | public static int? FindTargetIndexOrNull(IElement br, IList elements) { 32 | var targetOffset = GetTargetOffset(br); 33 | for (var i = 0; i < elements.Count; i++) { 34 | var withOffset = elements[i] as IPreservingOffset; 35 | if (withOffset != null && withOffset.Offset == targetOffset) 36 | return i; 37 | } 38 | return null; 39 | } 40 | 41 | public static void ThrowTargetNotFound(IElement br) { 42 | throw new InvalidOperationException( 43 | "After previous steps, cannot find jump target that was at 0x" + GetTargetOffset(br).ToString("X") + "." 44 | ); 45 | } 46 | 47 | public static void EnsureNotBackward(int index, int targetIndex) { 48 | if (targetIndex < index) 49 | throw new NotSupportedException("Backward jumps are currently not supported."); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/BranchResolutionStep.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps { 11 | public class BranchResolutionStep : IDecompilationStep { 12 | private static readonly HashSet opCodes = new HashSet { 13 | OpCodes.Br, OpCodes.Br_S, 14 | OpCodes.Brfalse, OpCodes.Brfalse_S, 15 | OpCodes.Brtrue, OpCodes.Brtrue_S, 16 | OpCodes.Beq, OpCodes.Beq_S, 17 | OpCodes.Bge, OpCodes.Bge_S, OpCodes.Bge_Un, OpCodes.Bge_Un_S, 18 | OpCodes.Bgt, OpCodes.Bgt_S, OpCodes.Bgt_Un, OpCodes.Bgt_Un_S, 19 | OpCodes.Ble, OpCodes.Ble_S, OpCodes.Ble_Un, OpCodes.Ble_Un_S, 20 | OpCodes.Blt, OpCodes.Blt_S, OpCodes.Blt_Un, OpCodes.Blt_Un_S, 21 | OpCodes.Bne_Un, OpCodes.Bne_Un_S 22 | }; 23 | 24 | public void Apply(IList elements, DecompilationContext context) { 25 | var results = new List(); 26 | this.ProcessRange(elements, 0, results, new Dictionary()); 27 | elements.Clear(); 28 | elements.AddRange(results); 29 | } 30 | 31 | private void ProcessRange(IList elements, int startIndex, IList results, IDictionary original) { 32 | for (var i = startIndex; i < elements.Count; i++) { 33 | var element = elements[i]; 34 | if (!BranchProcessing.Matches(element, opCodes.Contains)) { 35 | results.Add(element); 36 | original[element] = element; 37 | continue; 38 | } 39 | 40 | var targetIndexOrNull = BranchProcessing.FindTargetIndexOrNull(element, elements); 41 | if (targetIndexOrNull == null) 42 | BranchProcessing.ThrowTargetNotFound(element); 43 | 44 | var targetIndex = targetIndexOrNull.Value; 45 | BranchProcessing.EnsureNotBackward(i, targetIndex); 46 | 47 | var targetRange = new List(); 48 | this.ProcessRange(elements, targetIndex, targetRange, original); 49 | 50 | var followingRange = new List(); 51 | this.ProcessRange(elements, i + 1, followingRange, original); 52 | 53 | var convergingRange = new List(); 54 | while (targetRange.Count > 0 && followingRange.Count > 0) { 55 | var lastTarget = targetRange.Last(); 56 | var lastFollowing = followingRange.Last(); 57 | if (original[lastTarget] != original[lastFollowing]) 58 | break; 59 | 60 | convergingRange.Add(lastFollowing); 61 | targetRange.RemoveAt(targetRange.Count - 1); 62 | followingRange.RemoveAt(followingRange.Count - 1); 63 | } 64 | convergingRange.Reverse(); 65 | 66 | if (targetRange.Count > 0 || followingRange.Count > 0) { 67 | var branching = new BranchingElement(((InstructionElement)element).OpCode, targetRange, followingRange); 68 | original[branching] = element; 69 | results.Add(branching); 70 | } 71 | 72 | results.AddRange(convergingRange); 73 | break; 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/BranchStackFrame.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Elements; 6 | 7 | namespace Expressive.Decompilation.Steps { 8 | public class BranchStackFrame { 9 | public IList Elements { get; private set; } 10 | public BranchingElement Branching { get; private set; } 11 | public int CurrentIndex { get; set; } 12 | 13 | public BranchStackFrame(IList elements, BranchingElement branching, int currentIndex) { 14 | this.Elements = elements; 15 | this.Branching = branching; 16 | this.CurrentIndex = currentIndex; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/BranchingAwareStepBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Elements; 6 | 7 | namespace Expressive.Decompilation.Steps { 8 | public abstract class BranchingAwareStepBase : IDecompilationStep { 9 | public virtual void Apply(IList elements, DecompilationContext context) { 10 | ApplyRecursive(elements, new Stack(), context); 11 | } 12 | 13 | private void ApplyRecursive(IList elements, Stack branchStack, DecompilationContext context) { 14 | for (var i = 0; i < elements.Count; i++) { 15 | var element = elements[i]; 16 | var branching = element as BranchingElement; 17 | if (branching != null) { 18 | foreach (var branch in branching.GetBranches()) { 19 | var frame = new BranchStackFrame(elements, branching, i); 20 | branchStack.Push(frame); 21 | this.ApplyRecursive(branch, branchStack, context); 22 | branchStack.Pop(); 23 | 24 | i = frame.CurrentIndex; 25 | } 26 | } 27 | 28 | this.ApplyToSpecificElement(ref i, elements, branchStack, context); 29 | } 30 | } 31 | 32 | protected abstract void ApplyToSpecificElement(ref int index, IList elements, Stack branchStack, DecompilationContext context); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/Clarity/CoalescingVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements; 9 | using Expressive.Elements.Expressions.Matchers; 10 | using Expressive.Matching; 11 | 12 | namespace Expressive.Decompilation.Steps.Clarity { 13 | public class CoalescingVisitor : ElementVisitor { 14 | protected override Expression VisitConditional(ConditionalExpression c) { 15 | c = (ConditionalExpression)base.VisitConditional(c); 16 | 17 | return TryToCoalesceAsObjects(c) 18 | ?? TryToCoalesceAsNullable(c) 19 | ?? c; 20 | } 21 | 22 | private Expression TryToCoalesceAsNullable(ConditionalExpression c) { 23 | var hasValueTarget = (Expression)null; 24 | var getValueTarget = (Expression)null; 25 | 26 | var matched = Matcher 27 | .For(c.Test).AsPropertyOrField() 28 | .Property(property => property.Name == "HasValue" && property.DeclaringType.IsGenericTypeDefinedAs(typeof(Nullable<>))) 29 | .Do(p => hasValueTarget = p.Expression) 30 | 31 | .For(c.IfTrue).AsConvert() 32 | .Type(typeof(Nullable<>)) 33 | .Operand().AsMethodCall() 34 | .Method(method => method.Name == "GetValueOrDefault" 35 | && method.DeclaringType.IsGenericTypeDefinedAs(typeof(Nullable<>))) 36 | .Do(call => getValueTarget = call.Object) 37 | 38 | .Matched; 39 | 40 | if (!matched) 41 | return null; 42 | 43 | if (hasValueTarget != getValueTarget) 44 | return null; 45 | 46 | return Expression.Coalesce(getValueTarget, c.IfFalse); 47 | } 48 | 49 | 50 | private static Expression TryToCoalesceAsObjects(ConditionalExpression c) { 51 | var testPart = (Expression)null; 52 | var matched = Matcher 53 | .For(c.Test) 54 | .OneOf(ExpressionType.Equal, ExpressionType.NotEqual) 55 | .AsBinary() 56 | .LeftOrRight( 57 | leftOrRight => leftOrRight.AsConstant().ValueIsNull(), 58 | other => testPart = other 59 | ) 60 | .Matched; 61 | 62 | if (!matched) 63 | return null; 64 | 65 | if (testPart == c.IfTrue && c.Test.NodeType == ExpressionType.NotEqual) 66 | return Expression.Coalesce(testPart, c.IfFalse); 67 | 68 | if (testPart == c.IfFalse && c.Test.NodeType == ExpressionType.Equal) 69 | return Expression.Coalesce(testPart, c.IfTrue); 70 | 71 | return null; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/Clarity/ConditionImprovementVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps.Clarity { 11 | using ExpressionConversion = Func; 12 | 13 | public class ConditionImprovementVisitor : ElementVisitor { 14 | private static readonly IDictionary table = new Dictionary { 15 | // a ? true : b => a || b 16 | // a ? false : b => !a && b 17 | // a ? b : true => !a || b 18 | // a ? b : false => a && b 19 | { Pair(true, null), (a, _, b) => a.OrElse(b) }, 20 | { Pair(false, null), (a, _, b) => Expression.Not(a).AndAlso(b) }, 21 | { Pair(null, true), (a, b, _) => Expression.Not(a).OrElse(b) }, 22 | { Pair(null, false), (a, b, _) => a.AndAlso(b) }, 23 | 24 | // a ? true : true => true 25 | // a ? true : false => a 26 | // a ? false : true => !a 27 | // a ? false : false => false 28 | { Pair(true, true), Just(Expression.Constant(true)) }, 29 | { Pair(true, false), Just(a => a) }, 30 | { Pair(false, true), Just(a => Expression.Not(a)) }, 31 | { Pair(false, false), Just(Expression.Constant(false)) }, 32 | }; 33 | 34 | protected override Expression VisitConditional(ConditionalExpression c) { 35 | c = (ConditionalExpression)base.VisitConditional(c); 36 | if (c.Type != typeof(bool)) 37 | return c; 38 | 39 | var ifTrueAsConstant = c.IfTrue as ConstantExpression; 40 | var ifFalseAsConstant = c.IfFalse as ConstantExpression; 41 | if (ifTrueAsConstant == null && ifFalseAsConstant == null) 42 | return c; 43 | 44 | return table[Pair(Boolean(ifTrueAsConstant), Boolean(ifFalseAsConstant))].Invoke( 45 | c.Test, c.IfTrue, c.IfFalse 46 | ); 47 | } 48 | 49 | private static bool? Boolean(ConstantExpression constant) { 50 | return constant != null ? (bool?)constant.Value : null; 51 | } 52 | 53 | private static object Pair(bool? a, bool? b) { 54 | return new { a, b }; 55 | } 56 | 57 | private static ExpressionConversion Just(Expression constant) { 58 | return delegate { return constant; }; 59 | } 60 | 61 | private static ExpressionConversion Just(Func func) { 62 | return (a, _1, _2) => func(a); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/Clarity/NewNullableToCastVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps.Clarity { 11 | public class NewNullableToCastVisitor : ElementVisitor { 12 | protected override Expression VisitNew(NewExpression nex) { 13 | nex = (NewExpression)base.VisitNew(nex); 14 | if (!nex.Constructor.DeclaringType.IsGenericTypeDefinedAs(typeof(Nullable<>))) 15 | return nex; 16 | 17 | return Expression.Convert(nex.Arguments[0], nex.Type); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/Clarity/NotImprovementVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps.Clarity { 11 | public class NotImprovementVisitor : ElementVisitor { 12 | private static readonly Dictionary> binaryInversions = new Dictionary> { 13 | { ExpressionType.Equal, e => Expression.NotEqual(e.Left, e.Right) }, 14 | { ExpressionType.NotEqual, e => Expression.Equal(e.Left, e.Right) }, 15 | { ExpressionType.LessThan, e => Expression.GreaterThanOrEqual(e.Left, e.Right) }, 16 | { ExpressionType.GreaterThan, e => Expression.LessThanOrEqual(e.Left, e.Right) }, 17 | { ExpressionType.LessThanOrEqual, e => Expression.GreaterThan(e.Left, e.Right) }, 18 | { ExpressionType.GreaterThanOrEqual, e => Expression.LessThan(e.Left, e.Right) }, 19 | }; 20 | 21 | protected override Expression VisitUnary(UnaryExpression u) { 22 | u = (UnaryExpression)base.VisitUnary(u); 23 | if (u.NodeType != ExpressionType.Not) 24 | return u; 25 | 26 | return Invert(u.Operand, u); 27 | } 28 | 29 | private static Expression Invert(Expression expression, Expression fallback) { 30 | if (expression.NodeType == ExpressionType.Not) 31 | return ((UnaryExpression)expression).Operand; 32 | 33 | var invert = binaryInversions.GetValueOrDefault(expression.NodeType); 34 | if (invert == null) 35 | return fallback; 36 | 37 | return invert((BinaryExpression)expression); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/ContextualVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Elements; 6 | 7 | namespace Expressive.Decompilation.Steps { 8 | public class ContextualVisitor : ElementVisitor { 9 | public DecompilationContext Context { get; private set; } 10 | 11 | public ContextualVisitor(DecompilationContext context) { 12 | this.Context = context; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualDecompilationContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements; 7 | using Expressive.Elements.Presentation; 8 | 9 | namespace Expressive.Decompilation.Steps { 10 | public class IndividualDecompilationContext { 11 | private readonly IList elements; 12 | private readonly Stack branchStack; 13 | 14 | public IndividualDecompilationContext(IList elements, Stack branchStack) { 15 | this.elements = elements; 16 | this.branchStack = branchStack; 17 | } 18 | 19 | public Expression CapturePreceding() { 20 | var precedingIndex = this.GetPrecedingIndex(); 21 | var preceding = this.elements[precedingIndex]; 22 | 23 | this.elements.RemoveAt(precedingIndex); 24 | this.CurrentIndex -= 1; 25 | 26 | return ToExpression(preceding); 27 | } 28 | 29 | public Expression GetPreceding() { 30 | var preceding = this.elements[this.GetPrecedingIndex()]; 31 | return ToExpression(preceding); 32 | } 33 | 34 | private int GetPrecedingIndex() { 35 | //BranchStackFrame frame; 36 | //IEnumerator stackEnumerator; 37 | 38 | //if (branchStack.Count == 0) { 39 | var frame = new BranchStackFrame(this.elements, null, this.CurrentIndex); 40 | var stackEnumerator = this.branchStack.GetEnumerator(); 41 | //} 42 | //else { 43 | // stackEnumerator = this.branchStack.GetEnumerator(); 44 | // stackEnumerator.MoveNext(); 45 | // frame = stackEnumerator.Current; 46 | //} 47 | 48 | var index = this.FindPrecedingIndex(this.CurrentIndex - 1, frame, stackEnumerator); 49 | this.CurrentIndex = frame.CurrentIndex; 50 | return index; 51 | } 52 | 53 | private int FindPrecedingIndex(int startingIndex, BranchStackFrame frame, IEnumerator stack) { 54 | var index = startingIndex; 55 | while (true) { 56 | if (index < 0) { 57 | this.ImportPrecedingIntoBranches(stack); 58 | frame.CurrentIndex += 1; 59 | index += 1; 60 | } 61 | 62 | var kind = frame.Elements[index].Kind; 63 | if (kind == ElementKind.Undefined) 64 | throw new InvalidOperationException("Element " + frame.Elements[index] + " must be identified as either statement or expression to be traversed over or captured."); 65 | 66 | if (kind == ElementKind.Expression) 67 | return index; 68 | 69 | if (kind == ElementKind.Statement) 70 | index -= 1; 71 | } 72 | } 73 | 74 | private void ImportPrecedingIntoBranches(IEnumerator stack) { 75 | if (!stack.MoveNext()) 76 | throw new InvalidOperationException("There is no preceding expression in this context."); 77 | 78 | var frame = stack.Current; 79 | var targetIndex = frame.CurrentIndex - frame.Branching.ParameterCount; 80 | var index = this.FindPrecedingIndex(targetIndex, frame, stack); 81 | 82 | foreach (var branch in frame.Branching.GetBranches()) { 83 | branch.Insert(0, frame.Elements[index]); 84 | } 85 | frame.Elements.RemoveAt(index); 86 | frame.CurrentIndex -= 1; 87 | } 88 | 89 | private static Expression ToExpression(IElement element) { 90 | var typed = element as ExpressionElement; 91 | if (typed == null) 92 | throw new InvalidOperationException("Element " + element + " must be an ExpressionElement to be used in this context."); 93 | return typed.Expression; 94 | } 95 | 96 | public void VerifyPrecedingCount(int requiredCount, Func getMessage) { 97 | if (this.CurrentIndex >= requiredCount) 98 | return; 99 | 100 | throw new InvalidOperationException(getMessage( 101 | this.CurrentIndex, 102 | ElementHelper.ToString(elements.Take(this.CurrentIndex), Indent.FourSpaces) 103 | )); 104 | } 105 | 106 | public int CurrentIndex { get; set; } 107 | } 108 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualDecompilationStep.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using AshMind.Extensions; 6 | 7 | using Expressive.Decompilation.Steps.IndividualElements; 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps { 11 | public class IndividualDecompilationStep : BranchingAwareStepBase { 12 | public IList Interpretations { get; private set; } 13 | 14 | public IndividualDecompilationStep(params IElementInterpretation[] interpretations) { 15 | this.Interpretations = interpretations.ToList(); 16 | } 17 | 18 | public override void Apply(IList elements, DecompilationContext context) { 19 | this.Interpretations.ForEach(x => x.Initialize(context)); 20 | base.Apply(elements, context); 21 | } 22 | 23 | protected override void ApplyToSpecificElement(ref int index, IList elements, Stack branchStack, DecompilationContext context) { 24 | var individualContext = new IndividualDecompilationContext(elements, branchStack); 25 | 26 | var element = elements[index]; 27 | var indexFixed = index; 28 | var interpretation = this.Interpretations.FirstOrDefault(x => x.CanInterpret(elements[indexFixed])); 29 | if (interpretation == null) 30 | return; 31 | 32 | individualContext.CurrentIndex = index; 33 | var result = interpretation.Interpret(element, individualContext); 34 | index = individualContext.CurrentIndex; // some elements may have been captured, changing the collection, so index resync is needed 35 | if (result != null) { 36 | elements[index] = result; 37 | } 38 | else { 39 | elements.RemoveAt(index); 40 | index -= 1; 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/BranchToCondition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | 7 | using AshMind.Extensions; 8 | 9 | using Expressive.Elements; 10 | 11 | namespace Expressive.Decompilation.Steps.IndividualElements { 12 | public class BranchToCondition : ElementInterpretation { 13 | private static readonly IDictionary> unary = new Dictionary> { 14 | { OpCodes.Brtrue.Name, e => e }, 15 | { OpCodes.Brfalse.Name, e => Expression.Not(e) }, 16 | }; 17 | 18 | private static readonly IDictionary> binary = new Dictionary> { 19 | { OpCodes.Beq.Name, Expression.Equal }, 20 | { OpCodes.Bge.Name, Expression.GreaterThanOrEqual }, 21 | { OpCodes.Bgt.Name, Expression.GreaterThan }, 22 | { OpCodes.Ble.Name, Expression.LessThanOrEqual }, 23 | { OpCodes.Blt.Name, Expression.LessThan }, 24 | { OpCodes.Bne_Un.Name.SubstringBefore("."), Expression.NotEqual } 25 | }; 26 | 27 | public override bool CanInterpret(BranchingElement element) { 28 | return true; 29 | } 30 | 31 | public override IElement Interpret(BranchingElement branch, IndividualDecompilationContext context) { 32 | var condition = CaptureCondition(branch, context); 33 | var targetAsExpression = AsSingleExpression(branch.Target); 34 | var fallbackAsExpression = AsSingleExpression(branch.Fallback); 35 | 36 | condition = BooleanSupport.ConvertIfRequired(condition, typeof(bool)); 37 | 38 | if (targetAsExpression != null && fallbackAsExpression != null) { 39 | BooleanSupport.ConvertIfRequired(ref targetAsExpression, ref fallbackAsExpression); 40 | return new ExpressionElement(Expression.Condition(condition, targetAsExpression, fallbackAsExpression)); 41 | } 42 | 43 | var ifTrue = branch.Target; 44 | var ifFalse = branch.Fallback; 45 | if (ifTrue.Count == 0) { 46 | condition = Expression.Not(condition); 47 | ifFalse = branch.Target; 48 | ifTrue = branch.Fallback; 49 | } 50 | 51 | return new IfThenElement(condition, ifTrue, ifFalse); 52 | } 53 | 54 | private Expression CaptureCondition(BranchingElement branch, IndividualDecompilationContext context) { 55 | var rootOpCodeName = branch.OpCode.Name.SubstringBefore("."); 56 | var isUnary = unary.ContainsKey(rootOpCodeName); 57 | if (isUnary) { 58 | var operand = context.CapturePreceding(); 59 | operand = BooleanSupport.ConvertIfRequired(operand, typeof(bool)); 60 | return unary[rootOpCodeName].Invoke(operand); 61 | } 62 | 63 | var right = context.CapturePreceding(); 64 | var left = context.CapturePreceding(); 65 | return binary[rootOpCodeName].Invoke(left, right); 66 | } 67 | 68 | private Expression AsSingleExpression(IList elements) { 69 | if (elements.Count == 0) 70 | return null; 71 | 72 | if (elements.Count > 1) 73 | return null; 74 | 75 | var expressionElement = elements[0] as ExpressionElement; 76 | return expressionElement != null ? expressionElement.Expression : null; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/CallToExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | using Expressive.Disassembly.Instructions; 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps.IndividualElements { 11 | public class CallToElement : ElementInterpretation { 12 | public override bool CanInterpret(InstructionElement instruction) { 13 | return instruction.OpCode == OpCodes.Call 14 | || instruction.OpCode == OpCodes.Callvirt; 15 | } 16 | 17 | public override IElement Interpret(InstructionElement instruction, IndividualDecompilationContext context) { 18 | var method = ((MethodReferenceInstruction)instruction.Instruction).Method; 19 | return IdentifyAndCollectCall(method, context); 20 | } 21 | 22 | protected IElement IdentifyAndCollectCall(MethodBase methodBase, IndividualDecompilationContext context) { 23 | var parameters = methodBase.GetParameters(); 24 | var captureTarget = !methodBase.IsStatic 25 | ? (Func)(context.CapturePreceding) 26 | : () => null; 27 | 28 | bool isSetter; 29 | var property = GetProperty(methodBase, out isSetter); 30 | if (property != null) { 31 | if (isSetter) { 32 | var value = context.CapturePreceding(); 33 | return new MemberAssignmentElement(captureTarget(), property, value); 34 | } 35 | 36 | return new ExpressionElement(Expression.Property(captureTarget(), property)); 37 | } 38 | 39 | var method = methodBase as MethodInfo; 40 | if (method == null) 41 | throw new NotImplementedException("Only method and property calls are implemented."); 42 | 43 | var arguments = CaptureArguments(parameters, methodBase, context); 44 | return new ExpressionElement(Expression.Call(captureTarget(), method, arguments)); 45 | } 46 | 47 | protected IEnumerable CaptureArguments(ParameterInfo[] parameters, MethodBase methodBase, IndividualDecompilationContext context) { 48 | context.VerifyPrecedingCount(parameters.Length, (actualCount, precedingString) => string.Format( 49 | "Method {0} expects {1} parameters." + Environment.NewLine + 50 | "However, there are only {2} preceding elements: " + Environment.NewLine + "{3}", 51 | methodBase, parameters.Length, actualCount, precedingString 52 | )); 53 | 54 | var arguments = new List(); 55 | while (arguments.Count < parameters.Length) { 56 | arguments.Add(context.CapturePreceding()); 57 | } 58 | 59 | arguments.Reverse(); 60 | for (var i = 0; i < arguments.Count; i++) { 61 | arguments[i] = BooleanSupport.ConvertIfRequired(arguments[i], parameters[i].ParameterType); 62 | } 63 | 64 | return arguments; 65 | } 66 | 67 | private PropertyInfo GetProperty(MethodBase method, out bool isSetter) { 68 | isSetter = false; 69 | if (!method.IsSpecialName) 70 | return null; 71 | 72 | var properties = method.DeclaringType.GetProperties( 73 | (method.IsStatic ? BindingFlags.Static : BindingFlags.Instance) | BindingFlags.Public | BindingFlags.NonPublic 74 | ); 75 | 76 | foreach (var property in properties) { 77 | if (method == property.GetGetMethod()) 78 | return property; 79 | 80 | if (method == property.GetSetMethod()) { 81 | isSetter = true; 82 | return property; 83 | } 84 | } 85 | 86 | return null; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/ConvToConvert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class ConvToConvert : InstructionToExpression { 10 | private static readonly IDictionary> conversions = new Dictionary> { 11 | { OpCodes.Conv_I1, e => Expression.Convert(e, typeof(sbyte)) }, 12 | { OpCodes.Conv_U1, e => Expression.Convert(e, typeof(byte)) }, 13 | { OpCodes.Conv_I2, e => Expression.Convert(e, typeof(short)) }, 14 | { OpCodes.Conv_U2, e => Expression.Convert(e, typeof(ushort)) }, 15 | { OpCodes.Conv_I4, e => Expression.Convert(e, typeof(int)) }, 16 | { OpCodes.Conv_U4, e => Expression.Convert(e, typeof(uint)) }, 17 | { OpCodes.Conv_I8, e => Expression.Convert(e, typeof(long)) }, 18 | { OpCodes.Conv_U8, e => Expression.Convert(e, typeof(ulong)) }, 19 | { OpCodes.Conv_R4, e => Expression.Convert(e, typeof(float)) }, 20 | { OpCodes.Conv_R8, e => Expression.Convert(e, typeof(double)) } 21 | }; 22 | 23 | public override bool CanInterpret(Instruction instruction) { 24 | return conversions.ContainsKey(instruction.OpCode); 25 | } 26 | 27 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 28 | var target = context.CapturePreceding(); 29 | return conversions[instruction.OpCode](target); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/CxxToCondition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using AshMind.Extensions; 7 | using Expressive.Disassembly.Instructions; 8 | 9 | namespace Expressive.Decompilation.Steps.IndividualElements { 10 | using BinaryConverter = Func; 11 | 12 | public class CxxToCondition : InstructionToExpression { 13 | private static readonly IDictionary conditions = new Dictionary { 14 | { OpCodes.Ceq.Name, Expression.Equal }, 15 | { OpCodes.Cgt.Name, Expression.GreaterThan }, 16 | { OpCodes.Clt.Name, Expression.LessThan } 17 | }; 18 | 19 | public override bool CanInterpret(Instruction instruction) { 20 | return conditions.Keys.Any(k => instruction.OpCode.Name.StartsWith(k)); 21 | } 22 | 23 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 24 | var right = context.CapturePreceding(); 25 | var left = context.CapturePreceding(); 26 | 27 | BooleanSupport.ConvertIfRequired(ref left, ref right); 28 | 29 | var condition = conditions[instruction.OpCode.Name.SubstringBefore(".")]; 30 | return Expression.Condition( 31 | condition(left, right), 32 | Expression.Constant(1), 33 | Expression.Constant(0) 34 | ); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/DupToCopy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class DupToCopy : InstructionToExpression { 10 | public override bool CanInterpret(Instruction instruction) { 11 | return instruction.OpCode == OpCodes.Dup; 12 | } 13 | 14 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 15 | return context.GetPreceding(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/ElementInterpretation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Expressive.Elements; 5 | 6 | namespace Expressive.Decompilation.Steps.IndividualElements { 7 | public abstract class ElementInterpretation : IElementInterpretation 8 | where TInputElement : class, IElement 9 | where TResultElement : IElement 10 | { 11 | public virtual void Initialize(DecompilationContext context) { 12 | } 13 | 14 | bool IElementInterpretation.CanInterpret(IElement element) { 15 | var input = element as TInputElement; 16 | if (input == null) 17 | return false; 18 | 19 | return this.CanInterpret(input); 20 | } 21 | 22 | IElement IElementInterpretation.Interpret(IElement element, IndividualDecompilationContext context) { 23 | return this.Interpret((TInputElement)element, context); 24 | } 25 | 26 | public abstract bool CanInterpret(TInputElement element); 27 | public abstract TResultElement Interpret(TInputElement element, IndividualDecompilationContext context); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/IElementInterpretation.cs: -------------------------------------------------------------------------------- 1 | using Expressive.Elements; 2 | 3 | namespace Expressive.Decompilation.Steps.IndividualElements { 4 | public interface IElementInterpretation { 5 | void Initialize(DecompilationContext context); 6 | bool CanInterpret(IElement element); 7 | IElement Interpret(IElement element, IndividualDecompilationContext context); 8 | } 9 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/InstructionToExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using Expressive.Disassembly.Instructions; 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public abstract class InstructionToExpression : ElementInterpretation { 10 | public override bool CanInterpret(InstructionElement element) { 11 | return this.CanInterpret(element.Instruction); 12 | } 13 | 14 | public override ExpressionElement Interpret(InstructionElement element, IndividualDecompilationContext context) { 15 | return new ExpressionElement(this.Interpret(element.Instruction, context)); 16 | } 17 | 18 | public abstract bool CanInterpret(Instruction instruction); 19 | public abstract Expression Interpret(Instruction instruction, IndividualDecompilationContext context); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/LdargToParameter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class LdargToParameter : InstructionToExpression { 10 | private static readonly IDictionary> parameterIndexGetters = new Dictionary> { 11 | { OpCodes.Ldarg_0, _ => 0 }, 12 | { OpCodes.Ldarg_1, _ => 1 }, 13 | { OpCodes.Ldarg_2, _ => 2 }, 14 | { OpCodes.Ldarg_3, _ => 3 }, 15 | { OpCodes.Ldarg, x => ((VariableReferenceInstruction)x).Ordinal }, 16 | { OpCodes.Ldarg_S, x => ((VariableReferenceInstruction)x).Ordinal }, 17 | { OpCodes.Ldarga, x => ((VariableReferenceInstruction)x).Ordinal }, 18 | { OpCodes.Ldarga_S, x => ((VariableReferenceInstruction)x).Ordinal }, 19 | }; 20 | 21 | private DecompilationContext primaryContext; 22 | 23 | public override void Initialize(DecompilationContext context) { 24 | this.primaryContext = context; 25 | } 26 | 27 | public override bool CanInterpret(Instruction instruction) { 28 | return parameterIndexGetters.ContainsKey(instruction.OpCode); 29 | } 30 | 31 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 32 | var indexGetter = parameterIndexGetters[instruction.OpCode]; 33 | return this.primaryContext.GetParameter(indexGetter(instruction)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/LdcToConstant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | 7 | using AshMind.Extensions; 8 | using Expressive.Disassembly.Instructions; 9 | using Expressive.Elements; 10 | 11 | namespace Expressive.Decompilation.Steps.IndividualElements { 12 | public class LdcToConstant : InstructionToExpression { 13 | private static readonly string LdcNamePrefix = OpCodes.Ldc_I4.Name.SubstringBefore("."); 14 | 15 | public override bool CanInterpret(Instruction instruction) { 16 | return instruction.OpCode.Name.StartsWith(LdcNamePrefix); 17 | } 18 | 19 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 20 | return Expression.Constant(GetValue(instruction)); 21 | } 22 | 23 | private object GetValue(Instruction instruction) { 24 | var valueInstruction = instruction as IValueInstruction; 25 | if (valueInstruction != null) 26 | return valueInstruction.Value; 27 | 28 | var parts = instruction.OpCode.Name.Split('.'); 29 | if (parts.Length < 3) 30 | throw new InvalidOperationException("Cannot extract value from " + instruction.OpCode + "."); 31 | 32 | if (!parts[1].Equals("i4", StringComparison.InvariantCultureIgnoreCase)) 33 | throw new NotSupportedException("Cannot extract value from " + instruction.OpCode + ": " + parts[1] + " is not yet supported here."); 34 | 35 | var valueString = parts[2]; 36 | if (valueString.Equals("M1", StringComparison.InvariantCultureIgnoreCase)) 37 | return -1; 38 | 39 | return int.Parse(valueString); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/LdfldToField.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class LdfldToField : InstructionToExpression { 10 | public override bool CanInterpret(Instruction instruction) { 11 | return instruction.OpCode == OpCodes.Ldfld 12 | || instruction.OpCode == OpCodes.Ldsfld; 13 | } 14 | 15 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 16 | var field = ((FieldReferenceInstruction)instruction).Field; 17 | var instance = !field.IsStatic 18 | ? context.CapturePreceding() 19 | : null; 20 | return Expression.Field(instance, field); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/LdftnToAddressOf.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | using Expressive.Elements.Expressions; 8 | 9 | namespace Expressive.Decompilation.Steps.IndividualElements { 10 | public class LdftnToAddressOf : InstructionToExpression { 11 | public override bool CanInterpret(Instruction instruction) { 12 | return instruction.OpCode == OpCodes.Ldftn; 13 | } 14 | 15 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 16 | var method = ((MethodReferenceInstruction)instruction).Method; 17 | return new AddressOfExpression(method); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/LdlocToVariable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | using Expressive.Abstraction; 7 | using Expressive.Disassembly.Instructions; 8 | using Expressive.Elements; 9 | using Expressive.Elements.Expressions; 10 | 11 | namespace Expressive.Decompilation.Steps.IndividualElements { 12 | public class LdlocToVariable : ElementInterpretation { 13 | private static readonly IDictionary> variableIndexGetters = new Dictionary> { 14 | { OpCodes.Ldloc_0, _ => 0 }, 15 | { OpCodes.Ldloc_1, _ => 1 }, 16 | { OpCodes.Ldloc_2, _ => 2 }, 17 | { OpCodes.Ldloc_3, _ => 3 }, 18 | { OpCodes.Ldloc, x => ((VariableReferenceInstruction)x).Ordinal }, 19 | { OpCodes.Ldloc_S, x => ((VariableReferenceInstruction)x).Ordinal }, 20 | { OpCodes.Ldloca, x => ((VariableReferenceInstruction)x).Ordinal }, 21 | { OpCodes.Ldloca_S, x => ((VariableReferenceInstruction)x).Ordinal } 22 | }; 23 | 24 | private IManagedMethod method; 25 | 26 | public override void Initialize(DecompilationContext context) { 27 | this.method = context.Method; 28 | } 29 | 30 | public override bool CanInterpret(InstructionElement instruction) { 31 | return variableIndexGetters.ContainsKey(instruction.OpCode); 32 | } 33 | 34 | public override ExpressionElement Interpret(InstructionElement instruction, IndividualDecompilationContext context) { 35 | var indexGetter = variableIndexGetters[instruction.OpCode]; 36 | var variableIndex = indexGetter(instruction.Instruction); 37 | 38 | return new ExpressionElement( 39 | new LocalExpression(variableIndex, this.method.GetTypeOfLocal(variableIndex)) 40 | ); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/LdnullToConstant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class LdstrToConstant : InstructionToExpression { 10 | public override bool CanInterpret(Instruction instruction) { 11 | return instruction.OpCode == OpCodes.Ldstr; 12 | } 13 | 14 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 15 | return Expression.Constant( 16 | ((ValueInstruction)instruction).Value 17 | ); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/LdstrToConstant.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class LdnullToConstant : InstructionToExpression { 10 | public override bool CanInterpret(Instruction instruction) { 11 | return instruction.OpCode == OpCodes.Ldnull; 12 | } 13 | 14 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 15 | return Expression.Constant(null); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/MathToExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | 7 | using AshMind.Extensions; 8 | using Expressive.Disassembly.Instructions; 9 | 10 | namespace Expressive.Decompilation.Steps.IndividualElements { 11 | using BinaryOperator = Func; 12 | using UnaryOperator = Func; 13 | 14 | public class MathToExpression : InstructionToExpression { 15 | private static readonly IDictionary binaryOperators = new Dictionary { 16 | { OpCodes.Add, Expression.Add }, 17 | { OpCodes.And, Expression.And }, 18 | { OpCodes.Div, Expression.Divide }, 19 | { OpCodes.Mul, Expression.Multiply }, 20 | { OpCodes.Or, Expression.Or }, 21 | { OpCodes.Rem, Expression.Modulo }, 22 | { OpCodes.Shl, Expression.LeftShift }, 23 | { OpCodes.Shr, Expression.RightShift }, 24 | { OpCodes.Sub, Expression.Subtract }, 25 | { OpCodes.Xor, Expression.ExclusiveOr } 26 | }; 27 | 28 | private static readonly IDictionary unaryOperators = new Dictionary { 29 | { OpCodes.Not, Expression.Not }, 30 | { OpCodes.Neg, Expression.Negate } 31 | }; 32 | 33 | public override bool CanInterpret(Instruction instruction) { 34 | return binaryOperators.ContainsKey(instruction.OpCode) 35 | || unaryOperators.ContainsKey(instruction.OpCode); 36 | } 37 | 38 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 39 | var singleOrRight = context.CapturePreceding(); 40 | var unary = unaryOperators.GetValueOrDefault(instruction.OpCode); 41 | if (unary != null) 42 | return unary(singleOrRight); 43 | 44 | var left = context.CapturePreceding(); 45 | var binary = binaryOperators[instruction.OpCode]; 46 | 47 | Adapt(left, ref singleOrRight); 48 | 49 | return binary(left, singleOrRight); 50 | } 51 | 52 | private void Adapt(Expression left, ref Expression right) { 53 | if (left.Type == right.Type) 54 | return; 55 | 56 | right = Expression.Convert(right, left.Type); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/NewarrToNewArray.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class NewarrToNewArray : InstructionToExpression { 10 | public override bool CanInterpret(Instruction instruction) { 11 | return instruction.OpCode == OpCodes.Newarr; 12 | } 13 | 14 | public override Expression Interpret(Instruction instruction, IndividualDecompilationContext context) { 15 | var type = ((TypeReferenceInstruction)instruction).Type; 16 | return Expression.NewArrayBounds(type, context.CapturePreceding()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/NewobjToNew.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using System.Reflection.Emit; 7 | using Expressive.Disassembly.Instructions; 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps.IndividualElements { 11 | public class NewobjToNew : CallToElement { 12 | public override bool CanInterpret(InstructionElement instruction) { 13 | return instruction.OpCode == OpCodes.Newobj; 14 | } 15 | 16 | public override IElement Interpret(InstructionElement instruction, IndividualDecompilationContext context) { 17 | var constructor = (ConstructorInfo)((MethodReferenceInstruction)instruction.Instruction).Method; 18 | var arguments = this.CaptureArguments(constructor.GetParameters(), constructor, context); 19 | 20 | return new ExpressionElement(Expression.New(constructor, arguments)); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/OtherToCondition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | 7 | using Expressive.Elements; 8 | using Expressive.Elements.Expressions; 9 | using Expressive.Pipeline.Steps.IndividualElements.Support; 10 | 11 | namespace Expressive.Pipeline.Steps.IndividualElements { 12 | public class OtherToCondition : ElementInterpretation { 13 | private static readonly IDictionary> comparisons = new Dictionary> { 14 | { OpCodes.Beq, Expression.Equal }, 15 | }; 16 | 17 | public override bool CanInterpret(InstructionElement instruction) { 18 | return comparisons.ContainsKey(instruction.OpCode); 19 | } 20 | 21 | public override ExpressionElement Interpret(InstructionElement instruction, IndividualInterpretationContext context) { 22 | var right = context.CapturePreceding(-1).Expression; 23 | var left = context.CapturePreceding(-1).Expression; 24 | 25 | this.EnsureCompatibilityInOneDirection(ref left, ref right); 26 | this.EnsureCompatibilityInOneDirection(ref right, ref left); 27 | 28 | return new ExpressionElement(Expression.Condition( 29 | comparisons[instruction.OpCode].Invoke(left, right), 30 | Expression.Constant(1), 31 | Expression.Constant(0) 32 | )); 33 | } 34 | 35 | private void EnsureCompatibilityInOneDirection(ref Expression left, ref Expression right) { 36 | if (left.Type != typeof(bool)) 37 | return; 38 | 39 | if (right.Type == typeof(bool)) 40 | return; 41 | 42 | // left is bool, right is not bool (int?) 43 | right = new BooleanAdapterExpression(right); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/PopToRemove.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class PopToRemove : ElementInterpretation { 10 | public override bool CanInterpret(InstructionElement instruction) { 11 | return instruction.OpCode == OpCodes.Pop; 12 | } 13 | 14 | public override IElement Interpret(InstructionElement instruction, IndividualDecompilationContext context) { 15 | context.CapturePreceding(); 16 | return null; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/RetToReturn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | 7 | using Expressive.Elements; 8 | 9 | namespace Expressive.Decompilation.Steps.IndividualElements { 10 | public class RetToReturn : ElementInterpretation { 11 | private Type returnType; 12 | 13 | public override void Initialize(DecompilationContext context) { 14 | this.returnType = context.Method.ReturnType; 15 | base.Initialize(context); 16 | } 17 | 18 | public override bool CanInterpret(InstructionElement instruction) { 19 | return instruction.OpCode == OpCodes.Ret; 20 | } 21 | 22 | public override ReturnElement Interpret(InstructionElement instruction, IndividualDecompilationContext context) { 23 | var result = this.returnType != typeof(void) 24 | ? context.CapturePreceding() 25 | : null; 26 | 27 | result = BooleanSupport.ConvertIfRequired(result, this.returnType); 28 | return new ReturnElement(result); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/StelemToAssignment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps.IndividualElements { 9 | public class StelemToAssignment : ElementInterpretation { 10 | public override bool CanInterpret(InstructionElement instruction) { 11 | return instruction.OpCode.Name.StartsWith(OpCodes.Stelem.Name); 12 | } 13 | 14 | public override ArrayItemAssignmentElement Interpret(InstructionElement instruction, IndividualDecompilationContext context) { 15 | var value = context.CapturePreceding(); 16 | var index = context.CapturePreceding(); 17 | var array = context.CapturePreceding(); 18 | 19 | return new ArrayItemAssignmentElement(array, index, value); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/StfldToAssignment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection.Emit; 6 | using Expressive.Disassembly.Instructions; 7 | using Expressive.Elements; 8 | 9 | namespace Expressive.Decompilation.Steps.IndividualElements { 10 | public class StfldToAssignment : ElementInterpretation { 11 | public override bool CanInterpret(InstructionElement instruction) { 12 | return instruction.OpCode == OpCodes.Stfld 13 | || instruction.OpCode == OpCodes.Stsfld; 14 | } 15 | 16 | public override MemberAssignmentElement Interpret(InstructionElement instruction, IndividualDecompilationContext context) { 17 | var field = ((FieldReferenceInstruction)instruction.Instruction).Field; 18 | var value = context.CapturePreceding(); 19 | var instance = (Expression)null; 20 | if (!field.IsStatic) 21 | instance = context.CapturePreceding(); 22 | 23 | value = BooleanSupport.ConvertIfRequired(value, field.FieldType); 24 | return new MemberAssignmentElement(instance, field, value); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/IndividualElements/StlocToAssignment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | using Expressive.Abstraction; 7 | using Expressive.Disassembly.Instructions; 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps.IndividualElements { 11 | public class StlocToAssignment : ElementInterpretation { 12 | private static readonly IDictionary> variableIndexGetters = new Dictionary> { 13 | { OpCodes.Stloc_0, _ => 0 }, 14 | { OpCodes.Stloc_1, _ => 1 }, 15 | { OpCodes.Stloc_2, _ => 2 }, 16 | { OpCodes.Stloc_3, _ => 3 }, 17 | { OpCodes.Stloc_S, x => ((VariableReferenceInstruction)x).Ordinal }, 18 | { OpCodes.Stloc, x => ((VariableReferenceInstruction)x).Ordinal } 19 | }; 20 | 21 | private IManagedMethod method; 22 | 23 | public override void Initialize(DecompilationContext context) { 24 | this.method = context.Method; 25 | } 26 | 27 | public override bool CanInterpret(InstructionElement instruction) { 28 | return variableIndexGetters.ContainsKey(instruction.OpCode); 29 | } 30 | 31 | public override VariableAssignmentElement Interpret(InstructionElement instruction, IndividualDecompilationContext context) { 32 | var value = context.CapturePreceding(); 33 | var index = variableIndexGetters[instruction.OpCode](instruction.Instruction); 34 | var type = this.method.GetTypeOfLocal(index); 35 | 36 | value = BooleanSupport.ConvertIfRequired(value, type); 37 | 38 | return new VariableAssignmentElement(index, value); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/NopRemovalStep.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps { 9 | public class NopRemovalStep : BranchingAwareStepBase { 10 | protected override void ApplyToSpecificElement(ref int index, IList elements, Stack branchStack, DecompilationContext context) { 11 | var instruction = elements[index] as InstructionElement; 12 | if (instruction != null && instruction.OpCode == OpCodes.Nop) { 13 | elements.RemoveAt(index); 14 | index -= 1; 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/AssignmentInlining/AssignmentEstimatingVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements; 9 | using Expressive.Elements.Expressions; 10 | 11 | namespace Expressive.Decompilation.Steps.StatementInlining.AssignmentInlining { 12 | public class AssignmentEstimatingVisitor : ElementVisitor { 13 | private class VariableDetails { 14 | public VariableDetails() { 15 | this.Trivial = true; 16 | } 17 | 18 | public int AssignmentCount { get; set; } 19 | public int UseCount { get; set; } 20 | public bool Trivial { get; set; } 21 | } 22 | 23 | private readonly Func variableIndexPredicate; 24 | private readonly IDictionary details = new Dictionary(); 25 | 26 | private AssignmentEstimatingVisitor(Func variableIndexPredicate) { 27 | this.variableIndexPredicate = variableIndexPredicate; 28 | } 29 | 30 | public static HashSet Estimate(IList elements, Func variableIndexPredicate) { 31 | var visitor = new AssignmentEstimatingVisitor(variableIndexPredicate); 32 | visitor.VisitList(elements); 33 | return visitor.details.Where(p => p.Value.AssignmentCount == 1 && (p.Value.UseCount < 2 || p.Value.Trivial)) 34 | .Select(p => p.Key) 35 | .ToSet(); 36 | } 37 | 38 | protected override IElement VisitVariableAssignment(VariableAssignmentElement assignment) { 39 | if (variableIndexPredicate(assignment.VariableIndex)) { 40 | var details = GetDetails(assignment.VariableIndex); 41 | details.AssignmentCount += 1; 42 | details.Trivial = details.Trivial && IsTrivial(assignment.Value); 43 | } 44 | return base.VisitVariableAssignment(assignment); 45 | } 46 | 47 | protected override Expression VisitLocal(LocalExpression local) { 48 | if (variableIndexPredicate(local.Index)) { 49 | GetDetails(local.Index).UseCount += 1; 50 | } 51 | 52 | return base.VisitLocal(local); 53 | } 54 | 55 | private VariableDetails GetDetails(int localIndex) { 56 | var detailsItem = this.details.GetValueOrDefault(localIndex); 57 | if (detailsItem == null) { 58 | detailsItem = new VariableDetails(); 59 | this.details.Add(localIndex, detailsItem); 60 | } 61 | 62 | return detailsItem; 63 | } 64 | 65 | private bool IsTrivial(Expression expression) { 66 | return expression is ParameterExpression 67 | || ( 68 | expression is ConstantExpression 69 | && expression.Type.IsPrimitive 70 | ); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/AssignmentInlining/AssignmentInliner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Expressive.Elements; 5 | 6 | namespace Expressive.Decompilation.Steps.StatementInlining.AssignmentInlining { 7 | public class AssignmentInliner { 8 | public void Inline(IList elements, Func variableIndexPredicate) { 9 | var inlineable = AssignmentEstimatingVisitor.Estimate(elements, variableIndexPredicate); 10 | AssignmentInliningVisitor.Inline(elements, inlineable); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/AssignmentInlining/AssignmentInliningVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | 4 | using AshMind.Extensions; 5 | 6 | using Expressive.Elements; 7 | using Expressive.Elements.Expressions; 8 | 9 | namespace Expressive.Decompilation.Steps.StatementInlining.AssignmentInlining { 10 | internal class AssignmentInliningVisitor : ElementVisitor { 11 | private readonly HashSet inlineable; 12 | private readonly IDictionary values = new Dictionary(); 13 | 14 | private AssignmentInliningVisitor(HashSet inlineable) { 15 | this.inlineable = inlineable; 16 | } 17 | 18 | public static void Inline(IList elements, HashSet inlineable) { 19 | var visitor = new AssignmentInliningVisitor(inlineable); 20 | visitor.VisitList(elements); 21 | } 22 | 23 | protected override Expression VisitLocal(LocalExpression local) { 24 | var value = this.values.GetValueOrDefault(local.Index); 25 | return value ?? local; 26 | } 27 | 28 | protected override IElement VisitVariableAssignment(VariableAssignmentElement assignment) { 29 | assignment = (VariableAssignmentElement)base.VisitVariableAssignment(assignment); 30 | if (!inlineable.Contains(assignment.VariableIndex)) 31 | return assignment; 32 | 33 | values.Add(assignment.VariableIndex, assignment.Value); 34 | return null; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/IfAssignmentInliningVisitor.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using Expressive.Elements; 3 | 4 | namespace Expressive.Decompilation.Steps.StatementInlining { 5 | public class IfAssignmentInliningVisitor : ElementVisitor { 6 | protected override IElement VisitIfThen(IfThenElement ifThen) { 7 | ifThen = (IfThenElement)base.VisitIfThen(ifThen); 8 | if (ifThen.Then.Count != 1 || ifThen.Else.Count != 1) 9 | return ifThen; 10 | 11 | var thenAssignment = ifThen.Then[0] as VariableAssignmentElement; 12 | var elseAssignment = ifThen.Else[0] as VariableAssignmentElement; 13 | 14 | if (thenAssignment == null || elseAssignment == null || thenAssignment.VariableIndex != elseAssignment.VariableIndex) 15 | return ifThen; 16 | 17 | return new VariableAssignmentElement(thenAssignment.VariableIndex, Expression.Condition( 18 | ifThen.Condition, thenAssignment.Value, elseAssignment.Value 19 | )); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/IfReturnInliningVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements; 9 | 10 | namespace Expressive.Decompilation.Steps.StatementInlining { 11 | public class IfReturnInliningVisitor : ElementVisitor { 12 | public override void VisitList(IList elements) { 13 | base.VisitList(elements); 14 | for (var i = elements.Count - 1; i >= 0; i--) { 15 | var ifThen = elements[i] as IfThenElement; 16 | if (ifThen == null) 17 | continue; 18 | 19 | if (TryExtractElseIfThenEndsWithReturn(ifThen, elements, i)) { 20 | i = elements.Count; 21 | continue; 22 | } 23 | 24 | TryInlineIfThenReturnFollowedByReturn(ifThen, elements, i); 25 | } 26 | } 27 | 28 | private bool TryExtractElseIfThenEndsWithReturn(IfThenElement ifThen, IList elements, int index) { 29 | if (ifThen.Else.Count == 0 || !(ifThen.Then.Last() is ReturnElement)) 30 | return false; 31 | 32 | elements.InsertRange(index + 1, ifThen.Else); 33 | ifThen.Else.Clear(); 34 | return true; 35 | } 36 | 37 | private static void TryInlineIfThenReturnFollowedByReturn(IfThenElement ifThen, IList elements, int index) { 38 | if (index == elements.Count - 1) 39 | return; 40 | 41 | if (ifThen.Then.Count != 1 || ifThen.Else.Count > 0) 42 | return; 43 | 44 | var returnInIf = ifThen.Then.Single() as ReturnElement; 45 | var returnAfterIf = elements[index + 1] as ReturnElement; 46 | if (returnInIf == null || returnAfterIf == null) 47 | return; 48 | 49 | if (returnInIf.Result == null || returnAfterIf.Result == null) 50 | return; 51 | 52 | elements[index] = new ReturnElement(Expression.Condition( 53 | ifThen.Condition, 54 | returnInIf.Result, 55 | returnAfterIf.Result 56 | )); 57 | 58 | elements.RemoveAt(index + 1); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/IfThenCollapsingVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps.StatementInlining { 9 | public class IfThenCollapsingVisitor : ElementVisitor { 10 | protected override IElement VisitIfThen(IfThenElement ifThen) { 11 | ifThen = (IfThenElement)base.VisitIfThen(ifThen); 12 | if (ifThen.Else.Count > 0) 13 | return ifThen; 14 | 15 | if (ifThen.Then.Count > 1) 16 | return ifThen; 17 | 18 | var thenAsIf = ifThen.Then[0] as IfThenElement; 19 | if (thenAsIf == null) 20 | return ifThen; 21 | 22 | return new IfThenElement( 23 | Expression.AndAlso(ifThen.Condition, thenAsIf.Condition), 24 | thenAsIf.Then, thenAsIf.Else 25 | ); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/InitializerCollectors/ArrayInitializerCollector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements; 7 | using Expressive.Matching; 8 | 9 | namespace Expressive.Decompilation.Steps.StatementInlining.InitializerCollectors { 10 | public class ArrayInitializerCollector : InitializerCollector { 11 | protected override Matcher MatchFollowing(Matcher itemMatcher, int indexOffset, out Expression expression) { 12 | var expressionFixed = (Expression)null; 13 | var matcher = itemMatcher 14 | .For( 15 | e => e.Index, 16 | m => m.AsConstant().Value().Is(indexOffset) 17 | ) 18 | .Do(a => expressionFixed = a.Value); 19 | 20 | expression = expressionFixed; 21 | return matcher; 22 | } 23 | 24 | protected override Expression GetVariable(ArrayItemAssignmentElement rawElement) { 25 | return rawElement.Array; 26 | } 27 | 28 | protected override Expression ToInitializer(NewArrayExpression @new, IList values) { 29 | return Expression.NewArrayInit(@new.Type.GetElementType(), values); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/InitializerCollectors/CollectionInitializerCollector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | 7 | using AshMind.Extensions; 8 | 9 | using Expressive.Elements; 10 | using Expressive.Matching; 11 | 12 | namespace Expressive.Decompilation.Steps.StatementInlining.InitializerCollectors { 13 | public class CollectionInitializerCollector : InitializerCollector { 14 | public override bool MatchNew(NewExpression expression) { 15 | return expression.Constructor.DeclaringType.HasInterface() 16 | || base.MatchNew(expression); 17 | } 18 | 19 | protected override Matcher MatchFollowing(Matcher addMatcher, int indexOffset, out ElementInit add) { 20 | var addFixed = (ElementInit)null; 21 | var matcher = addMatcher 22 | .For( 23 | e => e.Expression, 24 | c => c.AsMethodCall() 25 | .Method(m => m.Name == "Add") 26 | .Do(m => addFixed = Expression.ElementInit(m.Method, m.Arguments)) 27 | ); 28 | 29 | add = addFixed; 30 | return matcher; 31 | } 32 | 33 | protected override Expression GetVariable(ExpressionElement rawElement) { 34 | var call = (MethodCallExpression)rawElement.Expression; 35 | return call.Object; 36 | } 37 | 38 | protected override Expression ToInitializer(NewExpression @new, IList values) { 39 | return Expression.ListInit(@new, values); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/InitializerCollectors/IInitializerCollector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq.Expressions; 4 | 5 | using Expressive.Elements; 6 | 7 | namespace Expressive.Decompilation.Steps.StatementInlining.InitializerCollectors { 8 | public interface IInitializerCollector { 9 | bool MatchNew(Expression expression); 10 | Expression AttemptToCollect(Expression @new, int variableIndex, int elementIndex, IList elements); 11 | } 12 | } -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/InitializerCollectors/InitializerCollector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements; 7 | using Expressive.Elements.Expressions; 8 | using Expressive.Matching; 9 | 10 | namespace Expressive.Decompilation.Steps.StatementInlining.InitializerCollectors { 11 | public abstract class InitializerCollector : IInitializerCollector 12 | where TNewExpression : Expression 13 | where TFollowingElement : IElement 14 | { 15 | public virtual bool MatchNew(TNewExpression expression) { 16 | return true; 17 | } 18 | 19 | public Expression AttemptToCollect(TNewExpression @new, int variableIndex, int elementIndex, IList elements) { 20 | var values = new List(); 21 | var indexOffset = 0; 22 | for (var i = elementIndex + 1; i < elements.Count; i++) { 23 | TValue value; 24 | var itemMatcher = MatchFollowing(Matcher.For(elements[i]).As(), indexOffset, out value); 25 | var matched = itemMatcher.For(GetVariable).As() 26 | .Match(l => l.Index == variableIndex) 27 | .Matched; 28 | 29 | if (!matched) 30 | break; 31 | 32 | values.Add(value); 33 | elements.RemoveAt(i); 34 | i -= 1; 35 | 36 | indexOffset += 1; 37 | } 38 | 39 | if (values.Count == 0) 40 | return null; 41 | 42 | return ToInitializer(@new, values); 43 | } 44 | 45 | protected abstract Matcher MatchFollowing(Matcher itemMatcher, int indexOffset, out TValue value); 46 | protected abstract Expression GetVariable(TFollowingElement rawElement); 47 | protected abstract Expression ToInitializer(TNewExpression newExpression, IList values); 48 | 49 | #region IInitializerCollector Members 50 | 51 | bool IInitializerCollector.MatchNew(Expression expression) { 52 | var typed = expression as TNewExpression; 53 | if (typed == null) 54 | return false; 55 | 56 | return this.MatchNew(typed); 57 | } 58 | 59 | Expression IInitializerCollector.AttemptToCollect(Expression @new, int variableIndex, int elementIndex, IList elements) { 60 | return this.AttemptToCollect((TNewExpression)@new, variableIndex, elementIndex, elements); 61 | } 62 | 63 | #endregion 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/InitializerCollectors/ObjectInitializerCollector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements; 7 | using Expressive.Elements.Expressions; 8 | using Expressive.Matching; 9 | 10 | namespace Expressive.Decompilation.Steps.StatementInlining.InitializerCollectors { 11 | public class ObjectInitializerCollector : InitializerCollector { 12 | protected override Matcher MatchFollowing(Matcher itemMatcher, int indexOffset, out MemberBinding binding) { 13 | var bindingFixed = (MemberBinding)null; 14 | var matcher = itemMatcher 15 | .Do(a => bindingFixed = Expression.Bind(a.Member, a.Value)); 16 | 17 | binding = bindingFixed; 18 | return matcher; 19 | } 20 | 21 | protected override Expression GetVariable(MemberAssignmentElement rawElement) { 22 | return rawElement.Instance; 23 | } 24 | 25 | protected override Expression ToInitializer(NewExpression @new, IList bindings) { 26 | return Expression.MemberInit(@new, bindings); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/InitializerDetectingVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Decompilation.Steps.StatementInlining.AssignmentInlining; 7 | using Expressive.Decompilation.Steps.StatementInlining.InitializerCollectors; 8 | using Expressive.Elements; 9 | using Expressive.Matching; 10 | 11 | namespace Expressive.Decompilation.Steps.StatementInlining { 12 | public class InitializerDetectingVisitor : ElementVisitor { 13 | private readonly AssignmentInliner inliner; 14 | private readonly IInitializerCollector[] collectors; 15 | 16 | public InitializerDetectingVisitor(AssignmentInliner inliner, params IInitializerCollector[] collectors) { 17 | this.inliner = inliner; 18 | this.collectors = collectors; 19 | } 20 | 21 | public override void VisitList(IList elements) { 22 | base.VisitList(elements); 23 | for (var i = elements.Count - 1; i >= 0; i--) { 24 | foreach (var collector in this.collectors) { 25 | if (TryWith(collector, i, elements)) 26 | break; 27 | } 28 | } 29 | } 30 | 31 | protected virtual bool TryWith(IInitializerCollector collector, int index, IList elements) { 32 | Expression @new; 33 | VariableAssignmentElement assignment; 34 | var matched = Matcher 35 | .For(elements[index]).As() 36 | .AssignTo(out assignment) 37 | .For(v => v.Value).Match(collector.MatchNew) 38 | .AssignTo(out @new) 39 | .Matched; 40 | 41 | if (!matched) 42 | return false; 43 | 44 | var initializer = collector.AttemptToCollect(@new, assignment.VariableIndex, index, elements); 45 | if (initializer != null) { 46 | assignment.Value = initializer; 47 | this.inliner.Inline(elements, i => i == assignment.VariableIndex); 48 | return true; 49 | } 50 | 51 | return false; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/StatementInlining/VariableInliningStep.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Decompilation.Steps.StatementInlining.AssignmentInlining; 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps.StatementInlining { 9 | public class VariableInliningStep : IDecompilationStep { 10 | private readonly AssignmentInliner inliner; 11 | 12 | public VariableInliningStep(AssignmentInliner inliner) { 13 | this.inliner = inliner; 14 | } 15 | 16 | public virtual void Apply(IList elements, DecompilationContext context) { 17 | this.inliner.Inline(elements, _ => true); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/UnconditionalBranchesRemovalStep.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | using Expressive.Elements; 7 | 8 | namespace Expressive.Decompilation.Steps { 9 | public class UnconditionalBranchesRemovalStep : BranchingAwareStepBase { 10 | protected override void ApplyToSpecificElement(ref int index, IList elements, Stack branchStack, DecompilationContext context) { 11 | var branching = elements[index] as BranchingElement; 12 | if (branching != null && (branching.OpCode == OpCodes.Br || branching.OpCode == OpCodes.Br_S)) { 13 | elements.RemoveAt(index); 14 | index -= 1; 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Expressive/Decompilation/Steps/VisitorSequenceStep.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using AshMind.Extensions; 6 | 7 | using Expressive.Elements; 8 | 9 | namespace Expressive.Decompilation.Steps { 10 | public class VisitorSequenceStep : IDecompilationStep { 11 | public IList> Visitors { get; private set; } 12 | 13 | public VisitorSequenceStep(params Func[] visitors) { 14 | this.Visitors = visitors.ToList(); 15 | } 16 | 17 | public void Apply(IList elements, DecompilationContext context) { 18 | this.Visitors.ForEach(visitor => visitor(context).VisitList(elements)); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Expressive/DecompilationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Serialization; 5 | 6 | namespace Expressive { 7 | [Serializable] 8 | public class DecompilationException : Exception { 9 | public DecompilationException() { 10 | } 11 | 12 | public DecompilationException(string message) : base(message) { 13 | } 14 | 15 | public DecompilationException(string message, Exception inner) : base(message, inner) { 16 | } 17 | 18 | protected DecompilationException(SerializationInfo info, StreamingContext context) : base(info, context) { 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Expressive/Decompiler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Abstraction; 7 | using Expressive.Decompilation.Pipelines; 8 | using Expressive.Elements; 9 | using Expressive.Elements.Presentation; 10 | using Expressive.Decompilation; 11 | 12 | namespace Expressive { 13 | public class Decompiler : IDecompiler { 14 | private readonly IDisassembler disassembler; 15 | private readonly IDecompilationPipeline pipeline; 16 | 17 | public Decompiler(IDisassembler disassembler, IDecompilationPipeline pipeline) { 18 | this.disassembler = disassembler; 19 | this.pipeline = pipeline; 20 | } 21 | 22 | public virtual LambdaExpression Decompile(IManagedMethod method) { 23 | var parameters = method.GetParameters().Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToList(); 24 | if (!method.IsStatic) 25 | parameters.Insert(0, Expression.Parameter(method.DeclaringType, "")); 26 | 27 | var body = this.Decompile(method, parameters.Cast().ToArray()); 28 | return Expression.Lambda(body, parameters.ToArray()); 29 | } 30 | 31 | public virtual Expression Decompile(IManagedMethod method, IList arguments) { 32 | var elements = this.disassembler.Disassemble(method) 33 | .Select(i => (IElement)new InstructionElement(i)) 34 | .ToList(); 35 | 36 | var context = new DecompilationContext(this, method, i => arguments[i]); 37 | try { 38 | foreach (var step in this.pipeline.GetSteps()) { 39 | step.Apply(elements, context); 40 | } 41 | } 42 | catch (Exception ex) { 43 | throw new DecompilationException( 44 | "Exception while interpreting " 45 | + Environment.NewLine 46 | + ElementHelper.ToString(elements, Indent.FourSpaces) 47 | + Environment.NewLine 48 | + ex.Message, ex 49 | ); 50 | } 51 | 52 | return GetSingleExpression(elements); 53 | } 54 | 55 | protected virtual Expression GetSingleExpression(IList elements) { 56 | if (elements.Count > 1) 57 | throw new InvalidOperationException("Expected 1 element after interpretation, got: " + Environment.NewLine + ElementHelper.ToString(elements, Indent.FourSpaces)); 58 | 59 | var returnElement = elements[0] as ReturnElement; 60 | var expressionElement = elements[0] as ExpressionElement; 61 | if (returnElement == null && expressionElement == null) 62 | throw new InvalidOperationException("Expected ReturnElement or ExpressionElement after interpretation, got " + elements[0].ToString(Indent.FourSpaces) + "."); 63 | 64 | var expression = returnElement != null ? returnElement.Result : expressionElement.Expression; 65 | return expression; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Expressive/Disassembler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Abstraction; 6 | using Expressive.Disassembly; 7 | using Expressive.Disassembly.Instructions; 8 | 9 | namespace Expressive { 10 | public class Disassembler : IDisassembler { 11 | private readonly Func instructionReaderFactory; 12 | 13 | public Disassembler(Func instructionReaderFactory) { 14 | this.instructionReaderFactory = instructionReaderFactory; 15 | } 16 | 17 | public virtual IEnumerable Disassemble(IManagedMethod method) { 18 | return this.instructionReaderFactory( 19 | method.GetBodyByteArray(), 20 | method.Context 21 | ).ReadAll(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Expressive/Disassembly/IInstructionReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Expressive.Disassembly.Instructions; 3 | 4 | namespace Expressive.Disassembly { 5 | public interface IInstructionReader { 6 | IEnumerable ReadAll(); 7 | } 8 | } -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/BranchInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | namespace Expressive.Disassembly.Instructions { 7 | public class BranchInstruction : Instruction { 8 | public int TargetOffset { get; private set; } 9 | 10 | public BranchInstruction(int offset, OpCode opCode, int targetOffset) : base(offset, opCode) { 11 | this.TargetOffset = targetOffset; 12 | } 13 | 14 | public override string ToString() { 15 | return base.ToString() + string.Format(" 0x{0:X2}", this.TargetOffset); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/FieldReferenceInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | 7 | namespace Expressive.Disassembly.Instructions { 8 | public class FieldReferenceInstruction : Instruction { 9 | public FieldInfo Field { get; private set; } 10 | 11 | public FieldReferenceInstruction(int offset, OpCode opCode, FieldInfo field) : base(offset, opCode) { 12 | this.Field = field; 13 | } 14 | 15 | public override string ToString() { 16 | return base.ToString() + " " + this.Field; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/IValueInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Disassembly.Instructions { 6 | public interface IValueInstruction { 7 | object Value { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/Instruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection.Emit; 3 | 4 | namespace Expressive.Disassembly.Instructions { 5 | public abstract class Instruction { 6 | public OpCode OpCode { get; private set; } 7 | public int Offset { get; private set; } 8 | 9 | protected Instruction(int offset, OpCode opCode) { 10 | this.Offset = offset; 11 | this.OpCode = opCode; 12 | } 13 | 14 | public override string ToString() { 15 | return string.Format( 16 | "0x{0} {1}", 17 | this.Offset.ToString("X2"), 18 | this.OpCode.ToString() 19 | ); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/MemberReferenceInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | 7 | namespace Expressive.Disassembly.Instructions { 8 | public class MemberReferenceInstruction : Instruction { 9 | public MemberInfo Member { get; private set; } 10 | 11 | public MemberReferenceInstruction(int offset, OpCode opCode, MemberInfo member) : base(offset, opCode) { 12 | this.Member = member; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/MethodReferenceInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Reflection.Emit; 6 | 7 | namespace Expressive.Disassembly.Instructions { 8 | public class MethodReferenceInstruction : Instruction { 9 | public MethodBase Method { get; private set; } 10 | 11 | public MethodReferenceInstruction(int offset, OpCode opCode, MethodBase method) : base(offset, opCode) { 12 | this.Method = method; 13 | } 14 | 15 | public override string ToString() { 16 | return base.ToString() + " " + this.Method; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/SimpleInstruction.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection.Emit; 2 | 3 | namespace Expressive.Disassembly.Instructions { 4 | public class SimpleInstruction : Instruction { 5 | public SimpleInstruction(int offset, OpCode opCode) : base(offset, opCode) { 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/SwitchInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Reflection.Emit; 6 | 7 | using AshMind.Extensions; 8 | 9 | namespace Expressive.Disassembly.Instructions { 10 | public class SwitchInstruction : Instruction { 11 | public ReadOnlyCollection TargetOffsets { get; set; } 12 | 13 | public SwitchInstruction(int offset, OpCode opCode, int[] targetOffsets) : base(offset, opCode) { 14 | TargetOffsets = targetOffsets.AsReadOnly(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/TypeReferenceInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | namespace Expressive.Disassembly.Instructions { 7 | public class TypeReferenceInstruction : Instruction { 8 | public Type Type { get; private set; } 9 | 10 | public TypeReferenceInstruction(int offset, OpCode opCode, Type type) : base(offset, opCode) { 11 | this.Type = type; 12 | } 13 | 14 | public override string ToString() { 15 | return base.ToString() + " " + this.Type; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/UnsupportedInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | namespace Expressive.Disassembly.Instructions { 7 | public class UnsupportedInstruction : Instruction { 8 | public UnsupportedInstruction(int offset, OpCode opCode) : base(offset, opCode) { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/ValueInstruction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | namespace Expressive.Disassembly.Instructions { 7 | public class ValueInstruction : Instruction, IValueInstruction { 8 | public T Value { get; private set; } 9 | 10 | public ValueInstruction(int offset, OpCode opCode, T value) : base(offset, opCode) { 11 | this.Value = value; 12 | } 13 | 14 | public override string ToString() { 15 | return base.ToString() + " " + this.Value; 16 | } 17 | 18 | #region IValueInstruction Members 19 | 20 | object IValueInstruction.Value { 21 | get { return this.Value; } 22 | } 23 | 24 | #endregion 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Expressive/Disassembly/Instructions/VariableReferenceInstruction.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection.Emit; 2 | 3 | namespace Expressive.Disassembly.Instructions { 4 | public class VariableReferenceInstruction : Instruction { 5 | public ushort Ordinal { get; private set; } 6 | 7 | public VariableReferenceInstruction(int offset, OpCode opCode, ushort ordinal) : base(offset, opCode) { 8 | this.Ordinal = ordinal; 9 | } 10 | 11 | public override string ToString() { 12 | return base.ToString() + " " + this.Ordinal; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Expressive/Elements/ArrayItemAssignmentElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using Expressive.Elements.Presentation; 6 | 7 | namespace Expressive.Elements { 8 | public class ArrayItemAssignmentElement : IElement { 9 | public Expression Array { get; set; } 10 | public Expression Index { get; set; } 11 | public Expression Value { get; set; } 12 | 13 | public ArrayItemAssignmentElement(Expression array, Expression index, Expression value) { 14 | this.Array = array; 15 | this.Index = index; 16 | this.Value = value; 17 | } 18 | 19 | public ElementKind Kind { 20 | get { return ElementKind.Statement; } 21 | } 22 | 23 | public override string ToString() { 24 | return this.ToString(Indent.FourSpaces); 25 | } 26 | 27 | public string ToString(Indent indent) { 28 | return string.Format( 29 | "{0}{1}[{2}] = {3}", 30 | indent.Value, this.Array, this.Index, this.Value 31 | ); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Expressive/Elements/BranchingElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | using System.Text; 6 | 7 | using AshMind.Extensions; 8 | 9 | using Expressive.Elements.Presentation; 10 | 11 | namespace Expressive.Elements { 12 | public class BranchingElement : IElement { 13 | private static readonly IDictionary nullaryAndUnaryParameterCounts = new Dictionary { 14 | { OpCodes.Br.Name, 0 }, 15 | { OpCodes.Brtrue.Name, 1 }, 16 | { OpCodes.Brfalse.Name, 1 } 17 | }; 18 | 19 | public OpCode OpCode { get; set; } 20 | public IList Target { get; private set; } 21 | public IList Fallback { get; private set; } 22 | 23 | public int ParameterCount { 24 | get { return nullaryAndUnaryParameterCounts.GetValueOrDefault(this.OpCode.Name.SubstringBefore("."), 2); } 25 | } 26 | 27 | public BranchingElement(OpCode opCode, IList target, IList fallback) { 28 | this.OpCode = opCode; 29 | this.Target = target; 30 | this.Fallback = fallback; 31 | } 32 | 33 | public ElementKind Kind { 34 | get { return ElementKind.Undefined; } 35 | } 36 | 37 | public override string ToString() { 38 | return this.ToString(Indent.FourSpaces); 39 | } 40 | 41 | public string ToString(Indent indent) { 42 | var builder = new StringBuilder(); 43 | var innerIndent = indent.Increment(); 44 | builder.Append(indent.Value) 45 | .AppendFormat("branch ({0}):", this.OpCode) 46 | .AppendLine() 47 | .Append(innerIndent.Value) 48 | .Append("target:") 49 | .AppendLine() 50 | .Append(ElementHelper.ToString(this.Target, innerIndent.Increment())); 51 | 52 | builder.AppendLine() 53 | .Append(innerIndent.Value) 54 | .Append("fallback:") 55 | .AppendLine() 56 | .Append(ElementHelper.ToString(this.Fallback, innerIndent.Increment())); 57 | return builder.ToString(); 58 | } 59 | 60 | public IEnumerable> GetBranches() { 61 | yield return this.Target; 62 | yield return this.Fallback; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Expressive/Elements/ElementKind.cs: -------------------------------------------------------------------------------- 1 | namespace Expressive.Elements { 2 | public enum ElementKind { 3 | Undefined, 4 | Expression, 5 | Statement 6 | } 7 | } -------------------------------------------------------------------------------- /Expressive/Elements/ElementVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Elements.Expressions; 6 | 7 | namespace Expressive.Elements { 8 | public abstract class ElementVisitor : ExpressionTreeVisitor { 9 | public virtual void VisitList(IList elements) { 10 | for (var i = 0; i < elements.Count; i++) { 11 | var result = this.Visit(elements[i]); 12 | if (result != null) { 13 | elements[i] = result; 14 | } 15 | else { 16 | elements.RemoveAt(i); 17 | i -= 1; 18 | } 19 | } 20 | } 21 | 22 | public virtual IElement Visit(IElement element) { 23 | if (element == null) 24 | return null; 25 | 26 | var visited = this.TryVisit(this.TransparentlyVisitExpressionElement, ref element) 27 | || this.TryVisit(this.VisitInstruction, ref element) 28 | || this.TryVisit(this.VisitVariableAssignment, ref element) 29 | || this.TryVisit(this.VisitMemberAssignment, ref element) 30 | || this.TryVisit(this.VisitArrayItemAssignment, ref element) 31 | || this.TryVisit(this.VisitReturn, ref element) 32 | || this.TryVisit(this.VisitBranching, ref element) 33 | || this.TryVisit(this.VisitIfThen, ref element); 34 | 35 | if (!visited) 36 | throw new NotSupportedException("Element type " + element.GetType() + " is not supported."); 37 | 38 | return element; 39 | } 40 | 41 | private bool TryVisit(Func visit, ref IElement element) 42 | where TElement : class, IElement 43 | { 44 | var cast = element as TElement; 45 | if (cast == null) 46 | return false; 47 | 48 | element = visit(cast); 49 | return true; 50 | } 51 | 52 | private IElement TransparentlyVisitExpressionElement(ExpressionElement expression) { 53 | expression.Expression = this.Visit(expression.Expression); 54 | return expression; 55 | } 56 | 57 | protected virtual IElement VisitInstruction(InstructionElement instruction) { 58 | return instruction; 59 | } 60 | 61 | protected virtual IElement VisitVariableAssignment(VariableAssignmentElement assignment) { 62 | assignment.Value = this.Visit(assignment.Value); 63 | return assignment; 64 | } 65 | 66 | protected virtual IElement VisitMemberAssignment(MemberAssignmentElement assignment) { 67 | assignment.Instance = this.Visit(assignment.Instance); 68 | assignment.Value = this.Visit(assignment.Value); 69 | return assignment; 70 | } 71 | 72 | protected virtual IElement VisitArrayItemAssignment(ArrayItemAssignmentElement assignment) { 73 | assignment.Array = this.Visit(assignment.Array); 74 | assignment.Index = this.Visit(assignment.Index); 75 | assignment.Value = this.Visit(assignment.Value); 76 | return assignment; 77 | } 78 | 79 | protected virtual IElement VisitReturn(ReturnElement @return) { 80 | @return.Result = this.Visit(@return.Result); 81 | return @return; 82 | } 83 | 84 | protected virtual IElement VisitBranching(BranchingElement branch) { 85 | this.VisitList(branch.Target); 86 | this.VisitList(branch.Fallback); 87 | 88 | return branch; 89 | } 90 | 91 | protected virtual IElement VisitIfThen(IfThenElement ifThen) { 92 | ifThen.Condition = this.Visit(ifThen.Condition); 93 | this.VisitList(ifThen.Then); 94 | this.VisitList(ifThen.Else); 95 | 96 | return ifThen; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Expressive/Elements/ExpressionElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements.Presentation; 7 | 8 | namespace Expressive.Elements { 9 | public class ExpressionElement : IElement { 10 | public Expression Expression { get; set; } 11 | 12 | public ExpressionElement(Expression expression) { 13 | this.Expression = expression; 14 | } 15 | 16 | public ElementKind Kind { 17 | get { return ElementKind.Expression; } 18 | } 19 | 20 | public override string ToString() { 21 | return this.Expression.ToString(); 22 | } 23 | 24 | public string ToString(Indent indent) { 25 | return indent.Value + this; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Expressive/Elements/Expressions/AddressOfExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | namespace Expressive.Elements.Expressions { 8 | public class AddressOfExpression : Expression { 9 | public new const ExpressionType NodeType = (ExpressionType)2001; 10 | 11 | public MethodBase Method { get; private set; } 12 | 13 | public AddressOfExpression(MethodBase method) : base(NodeType, typeof(IntPtr)) { 14 | this.Method = method; 15 | } 16 | 17 | public override string ToString() { 18 | return "AddressOf " + this.Method; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Expressive/Elements/Expressions/ExpressionTreeVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace Expressive.Elements.Expressions { 7 | public class ExpressionTreeVisitor : MsdnExamples.ExpressionVisitor { 8 | protected override Expression Visit(Expression exp) { 9 | var local = exp as LocalExpression; 10 | if (local != null) 11 | return this.VisitLocal(local); 12 | 13 | var addressOf = exp as AddressOfExpression; 14 | if (addressOf != null) 15 | return this.VisitAddressOf(addressOf); 16 | 17 | return base.Visit(exp); 18 | } 19 | 20 | protected virtual Expression VisitLocal(LocalExpression local) { 21 | return local; 22 | } 23 | 24 | protected virtual Expression VisitAddressOf(AddressOfExpression addressOf) { 25 | return addressOf; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Expressive/Elements/Expressions/LocalExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace Expressive.Elements.Expressions { 7 | public class LocalExpression : Expression { 8 | public new const ExpressionType NodeType = (ExpressionType)2000; 9 | 10 | public int Index { get; set; } 11 | 12 | public LocalExpression(int index, Type type) : base(NodeType, type) { 13 | this.Index = index; 14 | } 15 | 16 | public override string ToString() { 17 | return "local" + this.Index; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Expressive/Elements/IElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Elements.Presentation; 6 | 7 | namespace Expressive.Elements { 8 | public interface IElement { 9 | string ToString(Indent indent); 10 | ElementKind Kind { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Expressive/Elements/IPreservingOffset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Expressive.Elements { 7 | public interface IPreservingOffset { 8 | int Offset { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Expressive/Elements/IfThenElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Text; 6 | using Expressive.Elements.Presentation; 7 | 8 | namespace Expressive.Elements { 9 | public class IfThenElement : IElement { 10 | public Expression Condition { get; set; } 11 | public IList Then { get; set; } 12 | public IList Else { get; set; } 13 | 14 | public IfThenElement(Expression condition, IList then, IList @else) { 15 | if (condition.Type != typeof(bool)) 16 | throw new ArgumentException("Expected condition to be boolean.", "condition"); 17 | 18 | this.Condition = condition; 19 | this.Then = then; 20 | this.Else = @else; 21 | } 22 | 23 | public ElementKind Kind { 24 | get { 25 | return this.GetBranches().SelectMany(e => e).All(e => e.Kind == ElementKind.Statement) 26 | ? ElementKind.Statement 27 | : ElementKind.Undefined; 28 | } 29 | } 30 | 31 | private IEnumerable> GetBranches() { 32 | yield return this.Then; 33 | yield return this.Else; 34 | } 35 | 36 | public override string ToString() { 37 | return this.ToString(Indent.FourSpaces); 38 | } 39 | 40 | public string ToString(Indent indent) { 41 | var builder = new StringBuilder(); 42 | builder.Append(indent.Value) 43 | .Append("if ") 44 | .Append(this.Condition) 45 | .Append(" then:") 46 | .AppendLine() 47 | .Append(ElementHelper.ToString(this.Then, indent.Increment())); 48 | 49 | if (this.Else.Count == 0) 50 | return builder.ToString(); 51 | 52 | builder.AppendLine() 53 | .Append(indent.Value) 54 | .Append("else:") 55 | .AppendLine() 56 | .Append(ElementHelper.ToString(this.Else, indent.Increment())); 57 | return builder.ToString(); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Expressive/Elements/InstructionElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection.Emit; 5 | 6 | using Expressive.Disassembly.Instructions; 7 | using Expressive.Elements.Presentation; 8 | 9 | namespace Expressive.Elements { 10 | public class InstructionElement : IElement, IPreservingOffset { 11 | public Instruction Instruction { get; private set; } 12 | 13 | public InstructionElement(Instruction instruction) { 14 | this.Instruction = instruction; 15 | } 16 | 17 | public OpCode OpCode { 18 | get { return this.Instruction.OpCode; } 19 | } 20 | 21 | public int Offset { 22 | get { return this.Instruction.Offset; } 23 | } 24 | 25 | public ElementKind Kind { 26 | get { return ElementKind.Undefined; } 27 | } 28 | 29 | public override string ToString() { 30 | return this.Instruction.ToString(); 31 | } 32 | 33 | public string ToString(Indent indent) { 34 | return indent.Value + this; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Expressive/Elements/MemberAssignmentElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | using Expressive.Elements.Presentation; 8 | 9 | namespace Expressive.Elements { 10 | public class MemberAssignmentElement : IElement { 11 | public Expression Instance { get; set; } 12 | public MemberInfo Member { get; private set; } 13 | public Expression Value { get; set; } 14 | 15 | public MemberAssignmentElement(Expression instance, MemberInfo member, Expression value) { 16 | this.Instance = instance; 17 | this.Member = member; 18 | this.Value = value; 19 | } 20 | 21 | public ElementKind Kind { 22 | get { return ElementKind.Statement; } 23 | } 24 | 25 | public override string ToString() { 26 | return this.ToString(Indent.FourSpaces); 27 | } 28 | 29 | public string ToString(Indent indent) { 30 | var target = this.Instance != null 31 | ? this.Instance.ToString() 32 | : this.Member.DeclaringType.FullName; 33 | 34 | return string.Format( 35 | "{0}{1}.{2} = {3}", 36 | indent.Value, target, this.Member.Name, this.Value 37 | ); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Expressive/Elements/Presentation/ElementHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Elements.Presentation { 6 | public static class ElementHelper { 7 | public static string ToString(IEnumerable elements, Indent indent) { 8 | return string.Join( 9 | Environment.NewLine, 10 | elements.Select(e => e.ToString(indent)).ToArray() 11 | ); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Expressive/Elements/Presentation/Indent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Elements.Presentation { 6 | public class Indent { 7 | public static Indent None { get; private set; } 8 | public static Indent FourSpaces { get; private set; } 9 | 10 | private readonly Func increase; 11 | public string Value { get; private set; } 12 | 13 | static Indent() { 14 | None = new Indent("", _ => _); 15 | FourSpaces = new Indent("", s => s + " "); 16 | } 17 | 18 | public Indent(string value, Func increase) { 19 | this.increase = increase; 20 | this.Value = value; 21 | } 22 | 23 | public Indent Increment() { 24 | return new Indent(this.increase(this.Value), this.increase); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Expressive/Elements/ReturnElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements.Presentation; 7 | 8 | namespace Expressive.Elements { 9 | public class ReturnElement : IElement { 10 | public Expression Result { get; set; } 11 | 12 | public ReturnElement() : this(null) { 13 | } 14 | 15 | public ReturnElement(Expression result) { 16 | this.Result = result; 17 | } 18 | 19 | public ElementKind Kind { 20 | get { return ElementKind.Statement; } 21 | } 22 | 23 | public override string ToString() { 24 | return "return" + (this.Result != null ? " " + this.Result : ""); 25 | } 26 | 27 | public string ToString(Indent indent) { 28 | return indent.Value + this; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Expressive/Elements/VariableAssignmentElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using Expressive.Elements.Presentation; 7 | 8 | namespace Expressive.Elements { 9 | public class VariableAssignmentElement : IElement { 10 | public int VariableIndex { get; private set; } 11 | public Expression Value { get; set; } 12 | 13 | public VariableAssignmentElement(int index, Expression value) { 14 | this.VariableIndex = index; 15 | this.Value = value; 16 | } 17 | 18 | public ElementKind Kind { 19 | get { return ElementKind.Statement; } 20 | } 21 | 22 | public override string ToString() { 23 | return "local" + this.VariableIndex + " = " + this.Value; 24 | } 25 | 26 | public string ToString(Indent indent) { 27 | return indent.Value + this; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Expressive/Expressive.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Expressive 5 | $version$ 6 | Andrey Shchekin 7 | Andrey Shchekin 8 | http://github.com/ashmind/expressive 9 | false 10 | Expressive is a library that converts compiled methods into expression trees. 11 | Expression-Trees 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Expressive/ExpressiveEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Expressive.Decompilation.Pipelines; 6 | using Expressive.Disassembly; 7 | 8 | namespace Expressive { 9 | public static class ExpressiveEngine { 10 | static ExpressiveEngine() { 11 | ExpressiveEngine.GetDisassembler = () => new Disassembler((bytes, context) => new InstructionReader(bytes, context)); 12 | ExpressiveEngine.GetDecompiler = () => new Decompiler(ExpressiveEngine.GetDisassembler(), new DefaultPipeline()); 13 | ExpressiveEngine.GetInliner = () => new Inliner(ExpressiveEngine.GetDecompiler()); 14 | } 15 | 16 | public static Func GetDisassembler { get; set; } 17 | public static Func GetDecompiler { get; set; } 18 | public static Func GetInliner { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Expressive/ExpressiveExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | using Expressive.Abstraction; 8 | using Expressive.Disassembly.Instructions; 9 | 10 | namespace Expressive { 11 | public static class ExpressiveExtensions { 12 | public static LambdaExpression Decompile(this IDecompiler decompiler, MethodBase method) { 13 | return decompiler.Decompile(new MethodBaseAdapter(method)); 14 | } 15 | 16 | public static Expression Decompile(this IDecompiler decompiler, MethodBase method, IList arguments) { 17 | return decompiler.Decompile(new MethodBaseAdapter(method), arguments); 18 | } 19 | 20 | public static IEnumerable Disassemble(this IDisassembler disassembler, MethodBase method) { 21 | return disassembler.Disassemble(new MethodBaseAdapter(method)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Expressive/IDecompiler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq.Expressions; 3 | 4 | using Expressive.Abstraction; 5 | 6 | namespace Expressive { 7 | public interface IDecompiler { 8 | LambdaExpression Decompile(IManagedMethod method); 9 | Expression Decompile(IManagedMethod method, IList arguments); 10 | } 11 | } -------------------------------------------------------------------------------- /Expressive/IDisassembler.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using Expressive.Abstraction; 4 | using Expressive.Disassembly.Instructions; 5 | 6 | namespace Expressive { 7 | public interface IDisassembler { 8 | IEnumerable Disassemble(IManagedMethod method); 9 | } 10 | } -------------------------------------------------------------------------------- /Expressive/IInliner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | using System.Reflection; 4 | 5 | namespace Expressive { 6 | public interface IInliner { 7 | TExpression Inline(TExpression expression, Func shouldInline) 8 | where TExpression : Expression; 9 | } 10 | } -------------------------------------------------------------------------------- /Expressive/Inliner.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | using Expressive.Inlining; 8 | 9 | namespace Expressive { 10 | public class Inliner : IInliner { 11 | private readonly IDecompiler decompiler; 12 | 13 | public Inliner(IDecompiler decompiler) { 14 | this.decompiler = decompiler; 15 | } 16 | 17 | public TExpression Inline(TExpression expression, Func shouldInline) 18 | where TExpression : Expression 19 | { 20 | return new InliningVisitor(this.decompiler, shouldInline).Inline(expression); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Expressive/Inlining/InliningVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | using MsdnExamples; 8 | 9 | namespace Expressive.Inlining { 10 | public class InliningVisitor : ExpressionVisitor { 11 | private readonly IDecompiler decompiler; 12 | private readonly Func shouldInline; 13 | 14 | public InliningVisitor(IDecompiler decompiler, Func shouldInline) { 15 | this.decompiler = decompiler; 16 | this.shouldInline = shouldInline; 17 | } 18 | 19 | public TExpression Inline(TExpression expression) 20 | where TExpression : Expression 21 | { 22 | return (TExpression)this.Visit(expression); 23 | } 24 | 25 | protected override Expression VisitMethodCall(MethodCallExpression m) { 26 | m = (MethodCallExpression)base.VisitMethodCall(m); 27 | if (!shouldInline(m.Method)) 28 | return m; 29 | 30 | var argumentList = (IList)m.Arguments; 31 | if (m.Object != null) { 32 | argumentList = argumentList.ToList(); 33 | argumentList.Insert(0, m.Object); 34 | } 35 | 36 | return this.decompiler.Decompile(m.Method, argumentList); 37 | } 38 | 39 | protected override Expression VisitMemberAccess(MemberExpression m) { 40 | m = (MemberExpression)base.VisitMemberAccess(m); 41 | var property = m.Member as PropertyInfo; 42 | if (property == null) 43 | return m; 44 | 45 | var getter = property.GetGetMethod(); 46 | if (!shouldInline(property) && (getter == null || !shouldInline(getter))) 47 | return m; 48 | 49 | return this.decompiler.Decompile(getter, new[] { m.Expression }); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Expressive/Matching/Matcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Expressive.Matching { 6 | public class Matcher : Matcher { 7 | public T Target { get; private set; } 8 | 9 | internal Matcher(T target) { 10 | this.Target = target; 11 | this.Matched = target != null; 12 | } 13 | 14 | public Matcher AssignTo(out T value) { 15 | if (!this.Matched) { 16 | value = default(T); 17 | return this; 18 | } 19 | 20 | value = this.Target; 21 | return this; 22 | } 23 | 24 | public Matcher Do(Action action) { 25 | if (!this.Matched) 26 | return this; 27 | 28 | action(this.Target); 29 | return this; 30 | } 31 | 32 | 33 | public TResult IfMatched(Func ifMatched, TResult valueIfNotMatched) { 34 | return this.Matched ? ifMatched() : valueIfNotMatched; 35 | } 36 | 37 | public Matcher As() { 38 | if (!this.Matched || !(this.Target is TOther)) 39 | return new Matcher(default(TOther)) { Matched = false }; 40 | 41 | return new Matcher((TOther)(object)this.Target); 42 | } 43 | 44 | public new Matcher For(TOther value) { 45 | return this.For(t => value); 46 | } 47 | 48 | public Matcher For(Func get) { 49 | if (!this.Matched) 50 | return new Matcher(default(TOther)) { Matched = false }; 51 | 52 | return new Matcher(get(this.Target)); 53 | } 54 | 55 | public Matcher For(Func get, Func, Matcher> match) { 56 | var matcher = this.For(get); 57 | return this.Match(match(matcher).Matched); 58 | } 59 | 60 | public Matcher Is(T value) { 61 | return this.Match(Equals(this.Target, value)); 62 | } 63 | 64 | protected Matcher Match(bool matched) { 65 | this.Matched = this.Matched && matched; 66 | return this; 67 | } 68 | 69 | public Matcher Match(Func match) { 70 | if (!this.Matched) 71 | return this; 72 | 73 | this.Matched = this.Matched && match(this.Target); 74 | return this; 75 | } 76 | } 77 | 78 | public abstract class Matcher { 79 | public static Matcher For(T target) { 80 | return new Matcher(target); 81 | } 82 | 83 | public bool Matched { get; protected set; } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherBinaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using Expressive.Matching; 6 | 7 | namespace Expressive.Elements.Expressions.Matchers { 8 | public static class MatcherBinaryExtensions { 9 | public static Matcher LeftOrRight( 10 | this Matcher matcher, 11 | Func, Matcher> matchLeftOrRight, 12 | Action doWithOther 13 | ) { 14 | return matcher.Match(b => { 15 | var leftMatched = matchLeftOrRight(Matcher.For(b.Left)).Matched; 16 | if (leftMatched) { 17 | doWithOther(b.Right); 18 | return true; 19 | } 20 | 21 | var rightMatched = matchLeftOrRight(Matcher.For(b.Right)).Matched; 22 | if (rightMatched) { 23 | doWithOther(b.Left); 24 | return true; 25 | } 26 | 27 | return false; 28 | }); 29 | } 30 | 31 | public static Matcher LeftAndRight( 32 | this Matcher matcher, 33 | Func, Matcher> matchOne, 34 | Func, Matcher> matchOther 35 | ) { 36 | return matcher.Match( 37 | b => (matchOne(Matcher.For(b.Left)).Matched && matchOther(Matcher.For(b.Right)).Matched) 38 | || (matchOne(Matcher.For(b.Right)).Matched && matchOther(Matcher.For(b.Left)).Matched) 39 | ); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Expressive.Matching; 5 | 6 | namespace Expressive.Elements.Expressions.Matchers { 7 | public static class MatcherCollectionExtensions { 8 | public static Matcher> Count(this Matcher> matcher, int count) { 9 | return matcher.Match(target => target.Count == count); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherConstantExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace Expressive.Matching { 7 | public static class MatcherConstantExtensions { 8 | public static Matcher Value(this Matcher matcher) { 9 | return matcher.For(c => c.Value).As(); 10 | } 11 | 12 | public static Matcher Value(this Matcher matcher, Func matchValue) { 13 | return matcher.Match(c => matchValue(c.Value)); 14 | } 15 | 16 | public static Matcher Value(this Matcher matcher, object value) { 17 | return matcher.Match(c => Equals(c.Value, value)); 18 | } 19 | 20 | public static Matcher ValueIsNull(this Matcher matcher) { 21 | return matcher.Match(c => c.Value == null); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherExpressionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | using AshMind.Extensions; 7 | 8 | using Expressive.Elements.Expressions.Matchers; 9 | 10 | namespace Expressive.Matching { 11 | public static class MatcherExpressionExtensions { 12 | public static Matcher OneOf(this Matcher matcher, params ExpressionType[] types) 13 | where TExpression : Expression 14 | { 15 | return matcher.Match(target => types.Contains(target.NodeType)); 16 | } 17 | 18 | public static Matcher AsBinary(this Matcher matcher) 19 | where TExpression : Expression 20 | { 21 | return matcher.As(); 22 | } 23 | 24 | public static Matcher AsMethodCall(this Matcher matcher) 25 | where TExpression : Expression 26 | { 27 | return matcher.As(); 28 | } 29 | 30 | public static Matcher AsPropertyOrField(this Matcher matcher) 31 | where TExpression : Expression 32 | { 33 | return matcher.As(); 34 | } 35 | 36 | public static Matcher AsConvert(this Matcher matcher) 37 | where TExpression : Expression 38 | { 39 | return matcher.OneOf(ExpressionType.Convert, ExpressionType.ConvertChecked) 40 | .As(); 41 | } 42 | 43 | public static Matcher AsConstant(this Matcher matcher) 44 | where TExpression : Expression 45 | { 46 | return matcher.As(); 47 | } 48 | 49 | public static Matcher AsParameter(this Matcher matcher) 50 | where TExpression : Expression 51 | { 52 | return matcher.As(); 53 | } 54 | 55 | public static Matcher Type(this Matcher matcher, Type type) 56 | where TExpression : Expression 57 | { 58 | return matcher.Type(t => t == type || t.IsGenericTypeDefinedAs(type)); 59 | } 60 | 61 | public static Matcher Type(this Matcher matcher, Func matchType) 62 | where TExpression : Expression 63 | { 64 | return matcher.Match(target => matchType(target.Type)); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherMemberExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using Expressive.Matching; 7 | 8 | namespace Expressive.Elements.Expressions.Matchers { 9 | public static class MatcherMemberExtensions { 10 | public static Matcher Property( 11 | this Matcher matcher, 12 | Func matchProperty 13 | ) { 14 | return matcher.Match(target => { 15 | var property = target.Member as PropertyInfo; 16 | return property != null && matchProperty(property); 17 | }); 18 | } 19 | 20 | public static Matcher Field(this Matcher matcher) { 21 | return matcher.For(m => m.Member).As(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherMethodCallExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | using AshMind.Extensions; 8 | 9 | namespace Expressive.Matching { 10 | public static class MatcherMethodCallExtensions { 11 | public static Matcher Method( 12 | this Matcher matcher, 13 | Func matchMethod 14 | ) { 15 | return matcher.Match(target => matchMethod(target.Method)); 16 | } 17 | 18 | public static Matcher Argument(this Matcher matcher, int index) { 19 | return matcher.Match(n => index >= 0 && index <= n.Arguments.Count - 1) 20 | .For(n => n.Arguments[index]); 21 | } 22 | 23 | public static Matcher Argument(this Matcher matcher) { 24 | return matcher.For(n => { 25 | var parameters = n.Method.GetParameters(); 26 | for (var i = 0; i < parameters.Length; i++) { 27 | if (parameters[i].ParameterType == typeof(TParameter)) 28 | return n.Arguments[i]; 29 | } 30 | 31 | return null; 32 | }); 33 | } 34 | 35 | public static Matcher Argument(this Matcher matcher, Func, Matcher> matchArgument) { 36 | return matcher.Match(_ => matchArgument(matcher.Argument()).Matched); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherNewExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | using Expressive.Elements.Expressions.Matchers; 8 | 9 | namespace Expressive.Matching { 10 | public static class MatcherNewExtensions { 11 | public static Matcher Constructor( 12 | this Matcher matcher, 13 | Func matchConstructor 14 | ) { 15 | return matcher.Match(target => matchConstructor(target.Constructor)); 16 | } 17 | 18 | public static Matcher Argument(this Matcher matcher, int index) { 19 | return matcher.Match(n => index >= 0 && index <= n.Arguments.Count - 1) 20 | .For(n => n.Arguments[index]); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherParameterExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | 6 | namespace Expressive.Matching { 7 | public static class MatcherParameterExtensions { 8 | public static Matcher Name(this Matcher matcher, string name) { 9 | return matcher.Match(target => matcher.Target.Name == name); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Expressive/Matching/MatcherUnaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using Expressive.Matching; 6 | 7 | namespace Expressive.Elements.Expressions.Matchers { 8 | public static class MatcherUnaryExtensions { 9 | public static Matcher Operand(this Matcher matcher) { 10 | return matcher.For(u => u.Operand); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Expressive/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Expressive")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyProduct("Expressive")] 12 | [assembly: AssemblyCopyright("Copyright © Andrey Shchekin 2011")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("f4c11f5e-e162-4c77-aa8f-ec5f98990e73")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("0.6.0.0")] 35 | [assembly: AssemblyFileVersion("0.6.0.0")] 36 | -------------------------------------------------------------------------------- /Expressive/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /nuget-pack.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | mkdir !nupkg 3 | nuget pack Expressive\Expressive.csproj -symbols -OutputDirectory .\!nupkg\ -------------------------------------------------------------------------------- /nuget.bat: -------------------------------------------------------------------------------- 1 | @$tools\nuget %* -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | $packages 3 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | About 2 | ----- 3 | 4 | Expressive is a library that converts method IL into expression trees. 5 | Or, at least, it attempts to, since not all methods can be converted into a single expression. 6 | 7 | Example: 8 | 9 | public string FullName { 10 | get { return this.FirstName + " " + this.LastName; } 11 | } 12 | 13 | is converted to 14 | 15 | x => string.Concat(x.FirstName, " ", x.LastName) 16 | 17 | Use 18 | --- 19 | NuGet Package: http://nuget.org/List/Packages/Expressive 20 | API Docs: https://github.com/ashmind/expressive/wiki/External-API 21 | 22 | Reason 23 | ------ 24 | 25 | There are situations (mostly LINQ-related) when there is a large difference between use of a simple (for example, DB-mapped) property and a calculated property that contains some business/domain logic. Above example is the most classical one, but there are a lot more. 26 | 27 | These cases can be solved using things like HqlGeneratorForProperty in NHibernate and rewriters/visitors in other frameworks, but this requires logic/effort duplication, and such logic has to be maintained in both places. 28 | 29 | Expressive allows you to reuse the code directly. In addition to queries, other benefits of such reuse can be, for example, automatic generation of formula columns or transfer of logic between compiled C# and JavaScript. 30 | 31 | Issues 32 | ------ 33 | 34 | All code that uses Expressive should be used in combination with integration tests since significant changes to methods may render them 'inexpressible' (there are some cases when statements can be reduced to expressions, but these cases are rather limited). One rather annoying breaking case is IL instrumentation by, for example, Visual Studio Profiler. 35 | 36 | Status 37 | ------ 38 | 39 | What already works: 40 | 41 | 1. Fields/Properties 42 | 2. Methods 43 | 3. Operators 44 | 4. Lambdas 45 | 5. Object initializers (only one level deep) 46 | 6. Simple `if` that can be expressed as `?:` 47 | 48 | What does not work: 49 | 50 | 1. Array item access 51 | 2. Deep object initializers 52 | 3. Statements 53 | 54 | Note: it is completely possible to allow .NET 4.0 "statement expressions" in the results by changing the default Pipeline and replacing a Decompiler. --------------------------------------------------------------------------------