From 81e8df3f9682a229f9c9b10f94020961cb582bc1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 8 Jul 2016 17:19:27 +0900 Subject: [PATCH] Move DelegateConstruction transform to ILAst --- .../CSharp/CSharpDecompiler.cs | 97 +++------- .../CSharp/ExpressionBuilder.cs | 117 +++++++++-- .../CSharp/StatementBuilder.cs | 6 +- .../CSharp/Transforms/DelegateConstruction.cs | 2 +- .../ICSharpCode.Decompiler.csproj | 3 +- .../IL/Instructions/ILFunction.cs | 19 +- .../IL/Transforms/DelegateConstruction.cs | 181 ++++++++++++++++++ .../RemoveCachedDelegateInitialization.cs | 95 --------- ICSharpCode.Decompiler/NRExtensions.cs | 18 ++ 9 files changed, 353 insertions(+), 185 deletions(-) create mode 100644 ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs delete mode 100644 ICSharpCode.Decompiler/IL/Transforms/RemoveCachedDelegateInitialization.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index d3dddc546..ce85d382e 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -46,30 +46,34 @@ namespace ICSharpCode.Decompiler.CSharp readonly DecompilerTypeSystem typeSystem; readonly DecompilerSettings settings; - List ilTransforms = new List { - new SplitVariables(), - new ControlFlowSimplification(), - new ILInlining(), - new DetectPinRegions(), - new LoopDetection(), - new IntroduceExitPoints(), - new ConditionDetection(), - new ILInlining(), - new CopyPropagation(), - new InlineCompilerGeneratedVariables(), - new ExpressionTransforms(), // must run once before "the loop" to allow RemoveDeadVariablesInit - new RemoveDeadVariableInit(), // must run after ExpressionTransforms because it does not handle stobj(ldloca V, ...) - new RemoveCachedDelegateInitialization(), - new LoopingTransform( - new ExpressionTransforms(), - new TransformArrayInitializers(), - new ILInlining() - ) - }; + List ilTransforms = GetILTransforms(); + + public static List GetILTransforms() + { + return new List { + new SplitVariables(), + new ControlFlowSimplification(), + new ILInlining(), + new DetectPinRegions(), + new LoopDetection(), + new IntroduceExitPoints(), + new ConditionDetection(), + new ILInlining(), + new CopyPropagation(), + new InlineCompilerGeneratedVariables(), + new ExpressionTransforms(), // must run once before "the loop" to allow RemoveDeadVariablesInit + new RemoveDeadVariableInit(), // must run after ExpressionTransforms because it does not handle stobj(ldloca V, ...) + new DelegateConstruction(), + new LoopingTransform( + new ExpressionTransforms(), + new TransformArrayInitializers(), + new ILInlining() + ) + }; + } List astTransforms = new List { //new PushNegation(), - new DelegateConstruction(), new PatternStatementTransform(), new ReplaceMethodCallsWithOperators(), new IntroduceUnsafeModifier(), @@ -605,29 +609,10 @@ namespace ICSharpCode.Decompiler.CSharp return methodDecl; } - IDecompilerTypeSystem GetSpecializingTypeSystem(ITypeResolveContext decompilationContext) - { - IList classTypeParameters = null; - IList methodTypeParameters = null; - - if (decompilationContext.CurrentTypeDefinition != null) - classTypeParameters = decompilationContext.CurrentTypeDefinition.TypeArguments; - IMethod method = decompilationContext.CurrentMember as IMethod; - if (method != null) - methodTypeParameters = method.TypeArguments; - - if ((classTypeParameters != null && classTypeParameters.Count > 0) || (methodTypeParameters != null && methodTypeParameters.Count > 0)) - return new SpecializingDecompilerTypeSystem(typeSystem, new TypeParameterSubstitution(classTypeParameters, methodTypeParameters)); - else - return typeSystem; - } - - BlockStatement DecompileBodyInternal(MethodDefinition methodDefinition, IMethod method, EntityDeclaration entityDecl, ITypeResolveContext decompilationContext, string variablesPrefix = null) + void DecompileBody(MethodDefinition methodDefinition, IMethod method, EntityDeclaration entityDecl, ITypeResolveContext decompilationContext) { - var specializingTypeSystem = GetSpecializingTypeSystem(decompilationContext); - var ilReader = new ILReader(specializingTypeSystem); - var function = ilReader.ReadIL(methodDefinition.Body, CancellationToken); - function.CheckInvariant(ILPhase.Normal); + var specializingTypeSystem = typeSystem.GetSpecializingTypeSystem(decompilationContext); + ILFunction function = ILFunction.Read(specializingTypeSystem, methodDefinition, CancellationToken); if (entityDecl != null) { int i = 0; @@ -640,13 +625,6 @@ namespace ICSharpCode.Decompiler.CSharp } } - if (!string.IsNullOrWhiteSpace(variablesPrefix)) { - var variables = function.Variables.Where(v => v.Kind != VariableKind.Parameter); - foreach (var v in variables) { - v.Name = variablesPrefix + v.Name; - } - } - var context = new ILTransformContext { TypeSystem = specializingTypeSystem, CancellationToken = CancellationToken }; foreach (var transform in ilTransforms) { CancellationToken.ThrowIfCancellationRequested(); @@ -654,23 +632,8 @@ namespace ICSharpCode.Decompiler.CSharp function.CheckInvariant(ILPhase.Normal); } - var statementBuilder = new StatementBuilder(decompilationContext, method); - var body = statementBuilder.ConvertAsBlock(function.Body); - body.AddAnnotation(function.Variables); - return body; - } - - internal BlockStatement DecompileLambdaBody(IMethod method) - { - MethodDefinition definition = typeSystem.GetCecil(method) as MethodDefinition; - if (definition == null) - throw new InvalidOperationException("Could not find method in type system"); - return DecompileBodyInternal(definition, method, null, new SimpleTypeResolveContext(method), method.Name + "_"); - } - - void DecompileBody(MethodDefinition methodDefinition, IMethod method, EntityDeclaration entityDecl, ITypeResolveContext decompilationContext) - { - entityDecl.AddChild(DecompileBodyInternal(methodDefinition, method, entityDecl, decompilationContext), Roles.Body); + var statementBuilder = new StatementBuilder(specializingTypeSystem, decompilationContext, method); + entityDecl.AddChild(statementBuilder.ConvertAsBlock(function.Body), Roles.Body); } EntityDeclaration DoDecompile(FieldDefinition fieldDefinition, IField field, ITypeResolveContext decompilationContext) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 6686fd8e9..90ff838a6 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -60,13 +60,17 @@ namespace ICSharpCode.Decompiler.CSharp /// class ExpressionBuilder : ILVisitor { + readonly IDecompilerTypeSystem typeSystem; + readonly ITypeResolveContext decompilationContext; internal readonly ICompilation compilation; internal readonly CSharpResolver resolver; readonly TypeSystemAstBuilder astBuilder; - public ExpressionBuilder(ITypeResolveContext decompilationContext) + public ExpressionBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext) { Debug.Assert(decompilationContext != null); + this.typeSystem = typeSystem; + this.decompilationContext = decompilationContext; this.compilation = decompilationContext.Compilation; this.resolver = new CSharpResolver(new CSharpTypeResolveContext(compilation.MainAssembly, null, decompilationContext.CurrentTypeDefinition, decompilationContext.CurrentMember)); this.astBuilder = new TypeSystemAstBuilder(resolver); @@ -730,22 +734,20 @@ namespace ICSharpCode.Decompiler.CSharp return HandleCallInstruction(inst); } - internal static bool IsDelegateConstruction(CallInstruction inst) - { - return inst.Arguments.Count == 2 - && (inst.Arguments[1].OpCode == OpCode.LdFtn - || inst.Arguments[1].OpCode == OpCode.LdVirtFtn) - && inst.Method.DeclaringType.Kind == TypeKind.Delegate; - } - TranslatedExpression HandleDelegateConstruction(CallInstruction inst) { ILInstruction func = inst.Arguments[1]; IMethod method; - if (func.OpCode == OpCode.LdFtn) { - method = ((LdFtn)func).Method; - } else { - method = ((LdVirtFtn)func).Method; + switch (func.OpCode) { + case OpCode.LdFtn: + method = ((LdFtn)func).Method; + break; + case OpCode.LdVirtFtn: + method = ((LdVirtFtn)func).Method; + break; + default: + method = (IMethod)typeSystem.Resolve(((ILFunction)func).Method); + break; } var target = TranslateTarget(method, inst.Arguments[0], func.OpCode == OpCode.LdFtn); @@ -763,14 +765,97 @@ namespace ICSharpCode.Decompiler.CSharp var mre = new MemberReferenceExpression(target, method.Name); mre.TypeArguments.AddRange(method.TypeArguments.Select(a => ConvertType(a))); - return new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), mre) - .WithAnnotation(new DelegateConstruction.Annotation(func.OpCode == OpCode.LdVirtFtn, target, method.Name)) + var oce = new ObjectCreateExpression(ConvertType(inst.Method.DeclaringType), mre) +// .WithAnnotation(new DelegateConstruction.Annotation(func.OpCode == OpCode.LdVirtFtn, target, method.Name)) .WithILInstruction(inst) .WithRR(new ConversionResolveResult( inst.Method.DeclaringType, new MemberResolveResult(target.ResolveResult, method), // TODO handle extension methods capturing the first argument Conversion.MethodGroupConversion(method, func.OpCode == OpCode.LdVirtFtn, false))); + + if (func is ILFunction) { + return TranslateFunction(oce, target, (ILFunction)func); + } else { + return oce; + } + } + + TranslatedExpression TranslateFunction(TranslatedExpression objectCreateExpression, TranslatedExpression target, ILFunction function) + { + var method = typeSystem.Resolve(function.Method) as IMethod; + Debug.Assert(method != null); + + // Create AnonymousMethodExpression and prepare parameters + AnonymousMethodExpression ame = new AnonymousMethodExpression(); + ame.Parameters.AddRange(MakeParameters(method, function)); + ame.HasParameterList = true; + + StatementBuilder builder = new StatementBuilder(typeSystem, decompilationContext, method); + var body = builder.ConvertAsBlock(function.Body); + + bool isLambda = false; + if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) { + isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement); + } + // Remove the parameter list from an AnonymousMethodExpression if the original method had no names, + // and the parameters are not used in the method body + if (!isLambda && method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) { + var parameterReferencingIdentifiers = + from ident in body.Descendants.OfType() + let v = ident.Annotation() + where v != null && v.Kind == VariableKind.Parameter + select ident; + if (!parameterReferencingIdentifiers.Any()) { + ame.Parameters.Clear(); + ame.HasParameterList = false; + } + } + + // Replace all occurrences of 'this' in the method body with the delegate's target: + foreach (AstNode node in body.Descendants) { + if (node is ThisReferenceExpression) + node.ReplaceWith(target.Expression.Clone()); + } + Expression replacement; + if (isLambda) { + LambdaExpression lambda = new LambdaExpression(); + lambda.CopyAnnotationsFrom(ame); + ame.Parameters.MoveTo(lambda.Parameters); + Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression; + returnExpr.Remove(); + lambda.Body = returnExpr; + replacement = lambda; + } else { + ame.Body = body; + replacement = ame; + } + var expectedType = objectCreateExpression.ResolveResult.Type.GetDefinition(); + if (expectedType != null && expectedType.Kind != TypeKind.Delegate) { + var simplifiedDelegateCreation = (ObjectCreateExpression)objectCreateExpression.Expression.Clone(); + simplifiedDelegateCreation.Arguments.Clear(); + simplifiedDelegateCreation.Arguments.Add(replacement); + replacement = simplifiedDelegateCreation; + } + return replacement + .WithILInstruction(function) + .WithRR(objectCreateExpression.ResolveResult); + } + + IEnumerable MakeParameters(IMethod method, ILFunction function) + { + var variables = function.Variables.Where(v => v.Kind == VariableKind.Parameter).ToDictionary(v => v.Index); + int i = 0; + foreach (var parameter in method.Parameters) { + var pd = astBuilder.ConvertParameter(parameter); + if (parameter.Type.ContainsAnonymousType()) + pd.Type = null; + ILVariable v; + if (variables.TryGetValue(i, out v)) + pd.AddAnnotation(new ILVariableResolveResult(v, method.Parameters[i].Type)); + yield return pd; + i++; + } } TranslatedExpression TranslateTarget(IMember member, ILInstruction target, bool nonVirtualInvocation) @@ -800,7 +885,7 @@ namespace ICSharpCode.Decompiler.CSharp // Used for Call, CallVirt and NewObj TranslatedExpression target; if (inst.OpCode == OpCode.NewObj) { - if (IsDelegateConstruction(inst)) { + if (IL.Transforms.DelegateConstruction.IsDelegateConstruction((NewObj)inst, true)) { return HandleDelegateConstruction(inst); } target = default(TranslatedExpression); // no target diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs index 694e3f192..e8c078b0b 100644 --- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs @@ -33,10 +33,10 @@ namespace ICSharpCode.Decompiler.CSharp internal readonly ExpressionBuilder exprBuilder; readonly IMethod currentMethod; - public StatementBuilder(ITypeResolveContext decompilationContext, IMethod currentMethod) + public StatementBuilder(IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext, IMethod currentMethod) { - Debug.Assert(decompilationContext != null && currentMethod != null); - this.exprBuilder = new ExpressionBuilder(decompilationContext); + Debug.Assert(typeSystem != null && decompilationContext != null && currentMethod != null); + this.exprBuilder = new ExpressionBuilder(typeSystem, decompilationContext); this.currentMethod = currentMethod; } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DelegateConstruction.cs index 83629baa8..2e1039604 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/DelegateConstruction.cs @@ -34,7 +34,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// For anonymous methods, creates an AnonymousMethodExpression. /// Also gets rid of any "Display Classes" left over after inlining an anonymous method. /// - public class DelegateConstruction : ContextTrackingVisitor, IAstTransform + public class DelegateConstructionOld : ContextTrackingVisitor, IAstTransform { internal sealed class Annotation { diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 0d2b189ca..70f956854 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -68,7 +68,6 @@ - @@ -132,7 +131,7 @@ - + diff --git a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs index 4e42c9458..61aa01041 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs @@ -18,6 +18,8 @@ using System; using System.Collections.Generic; +using System.Threading; +using ICSharpCode.NRefactory.TypeSystem; using Mono.Cecil; using ICSharpCode.Decompiler.Disassembler; @@ -73,13 +75,28 @@ namespace ICSharpCode.Decompiler.IL /// /// Apply a list of transforms to this function. /// - public void RunTransforms(IEnumerable transforms, ILTransformContext context) + public void RunTransforms(IEnumerable transforms, ILTransformContext context, Func stopTransform = null) { foreach (var transform in transforms) { context.CancellationToken.ThrowIfCancellationRequested(); + if (stopTransform != null && stopTransform(transform)) + break; transform.Run(this, context); this.CheckInvariant(ILPhase.Normal); } } + + public static ILFunction Read(IDecompilerTypeSystem context, IMethod method, CancellationToken cancellationToken = default(CancellationToken)) + { + return Read(context, (MethodDefinition)context.GetCecil(method), cancellationToken); + } + + public static ILFunction Read(IDecompilerTypeSystem context, MethodDefinition methodDefinition, CancellationToken cancellationToken = default(CancellationToken)) + { + var ilReader = new ILReader(context); + var function = ilReader.ReadIL(methodDefinition.Body, cancellationToken); + function.CheckInvariant(ILPhase.Normal); + return function; + } } } diff --git a/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs new file mode 100644 index 000000000..17d4a0ea5 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/Transforms/DelegateConstruction.cs @@ -0,0 +1,181 @@ +// Copyright (c) 2011-2016 Siegfried Pammer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Linq; +using ICSharpCode.Decompiler.CSharp; +using ICSharpCode.Decompiler.IL; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.Decompiler.IL.Transforms +{ + public class DelegateConstruction : IILTransform + { + ILTransformContext context; + ITypeResolveContext decompilationContext; + + void IILTransform.Run(ILFunction function, ILTransformContext context) + { + if (!new DecompilerSettings().AnonymousMethods) + return; + this.context = context; + this.decompilationContext = new SimpleTypeResolveContext(context.TypeSystem.Resolve(function.Method)); + foreach (var block in function.Descendants.OfType()) { + for (int i = block.Instructions.Count - 1; i >= 0; i--) { + foreach (var call in block.Instructions[i].Descendants.OfType()) { + ILFunction f = TransformDelegateConstruction(call); + if (f != null) + call.Arguments[1].ReplaceWith(f); + } + + var inst = block.Instructions[i] as IfInstruction; + if (inst != null) { + if (CachedDelegateInitializationWithField(inst)) { + block.Instructions.RemoveAt(i); + continue; + } + if (CachedDelegateInitializationWithLocal(inst)) { + block.Instructions.RemoveAt(i); + continue; + } + } + } + } + } + + #region TransformDelegateConstruction + internal static bool IsDelegateConstruction(NewObj inst, bool allowTransformed = false) + { + if (inst == null || inst.Arguments.Count != 2 || inst.Method.DeclaringType.Kind != TypeKind.Delegate) + return false; + var opCode = inst.Arguments[1].OpCode; + + return opCode == OpCode.LdFtn || opCode == OpCode.LdVirtFtn || (allowTransformed && opCode == OpCode.ILFunction); + } + + static bool IsAnonymousMethod(ITypeDefinition decompiledTypeDefinition, IMethod method) + { + if (method == null || !(method.HasGeneratedName() || method.Name.Contains("$"))) + return false; + if (!(method.IsCompilerGenerated() || IsPotentialClosure(decompiledTypeDefinition, method.DeclaringTypeDefinition))) + return false; + return true; + } + + static bool IsPotentialClosure(ITypeDefinition decompiledTypeDefinition, ITypeDefinition potentialDisplayClass) + { + if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) + return false; + // check that methodContainingType is within containingType + while (potentialDisplayClass != decompiledTypeDefinition) { + potentialDisplayClass = potentialDisplayClass.DeclaringTypeDefinition; + if (potentialDisplayClass == null) + return false; + } + return true; + } + + ILFunction TransformDelegateConstruction(NewObj value) + { + if (!IsDelegateConstruction(value)) + return null; + var targetMethod = ((IInstructionWithMethodOperand)value.Arguments[1]).Method; + if (IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod)) { + var target = value.Arguments[0]; + var localTypeSystem = context.TypeSystem.GetSpecializingTypeSystem(decompilationContext); + var function = ILFunction.Read(localTypeSystem, targetMethod, context.CancellationToken); + + var contextPrefix = targetMethod.Name; + foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter)) { + v.Name = contextPrefix + v.Name; + } + + function.RunTransforms(CSharpDecompiler.GetILTransforms(), context, t => t is DelegateConstruction); + function.AcceptVisitor(new ReplaceDelegateTargetVisitor(target, function.Variables.SingleOrDefault(v => v.Index == -1 && v.Kind == VariableKind.Parameter))); + ((IILTransform)this).Run(function, new ILTransformContext { CancellationToken = context.CancellationToken, TypeSystem = localTypeSystem }); + return function; + } + return null; + } + + class ReplaceDelegateTargetVisitor : ILVisitor + { + readonly ILVariable thisVariable; + readonly ILInstruction target; + + public ReplaceDelegateTargetVisitor(ILInstruction target, ILVariable thisVariable) + { + this.target = target; + this.thisVariable = thisVariable; + } + + protected override void Default(ILInstruction inst) + { + foreach (var child in inst.Children) { + child.AcceptVisitor(this); + } + } + + protected internal override void VisitLdLoc(LdLoc inst) + { + if (inst.MatchLdLoc(thisVariable)) { + inst.ReplaceWith(target.Clone()); + return; + } + base.VisitLdLoc(inst); + } + } + #endregion + + bool CachedDelegateInitializationWithField(IfInstruction inst) + { + // if (comp(ldsfld CachedAnonMethodDelegate == ldnull) { + // stsfld CachedAnonMethodDelegate(DelegateConstruction) + // } + // ... one usage of CachedAnonMethodDelegate ... + // => + // ... one usage of DelegateConstruction ... + Block trueInst = inst.TrueInst as Block; + var condition = inst.Condition as Comp; + if (condition == null || trueInst == null || trueInst.Instructions.Count != 1 || !inst.FalseInst.MatchNop()) + return false; + IField field, field2; + ILInstruction value; + var storeInst = trueInst.Instructions[0]; + if (!condition.Left.MatchLdsFld(out field) || !condition.Right.MatchLdNull()) + return false; + if (!storeInst.MatchStsFld(out value, out field2) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) + return false; + if (!IsDelegateConstruction(value as NewObj, true)) + return false; + var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); + if (nextInstruction == null) + return false; + var usages = nextInstruction.Descendants.OfType().Where(i => i.Field.Equals(field)).ToArray(); + if (usages.Length != 1) + return false; + usages[0].ReplaceWith(value); + return true; + } + + bool CachedDelegateInitializationWithLocal(IfInstruction inst) + { + return false; + } + } +} diff --git a/ICSharpCode.Decompiler/IL/Transforms/RemoveCachedDelegateInitialization.cs b/ICSharpCode.Decompiler/IL/Transforms/RemoveCachedDelegateInitialization.cs deleted file mode 100644 index 7bb451bb7..000000000 --- a/ICSharpCode.Decompiler/IL/Transforms/RemoveCachedDelegateInitialization.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2011-2016 Siegfried Pammer -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this -// software and associated documentation files (the "Software"), to deal in the Software -// without restriction, including without limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons -// to whom the Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or -// substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR -// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE -// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -using System; -using System.Linq; -using ICSharpCode.Decompiler.CSharp; -using ICSharpCode.Decompiler.CSharp.Transforms; -using ICSharpCode.NRefactory.TypeSystem; - -namespace ICSharpCode.Decompiler.IL.Transforms -{ - public class RemoveCachedDelegateInitialization : IILTransform - { - ILTransformContext context; - ITypeResolveContext decompilationContext; - - void IILTransform.Run(ILFunction function, ILTransformContext context) - { - if (!new DecompilerSettings().AnonymousMethods) - return; - this.context = context; - this.decompilationContext = new SimpleTypeResolveContext(context.TypeSystem.Resolve(function.Method)); - foreach (var block in function.Descendants.OfType()) { - for (int i = block.Instructions.Count - 1; i >= 0; i--) { - var inst = block.Instructions[i] as IfInstruction; - if (inst != null) { - if (CachedDelegateInitializationWithField(inst)) { - block.Instructions.RemoveAt(i); - continue; - } - if (CachedDelegateInitializationWithLocal(inst)) { - block.Instructions.RemoveAt(i); - continue; - } - } - } - } - } - - bool CachedDelegateInitializationWithField(IfInstruction inst) - { - // if (comp(ldsfld CachedAnonMethodDelegate == ldnull) { - // stsfld CachedAnonMethodDelegate(DelegateConstruction) - // } - // ... one usage of CachedAnonMethodDelegate ... - // => - // ... one usage of DelegateConstruction ... - Block trueInst = inst.TrueInst as Block; - var condition = inst.Condition as Comp; - if (condition == null || trueInst == null || trueInst.Instructions.Count != 1 || !inst.FalseInst.MatchNop()) - return false; - IField field, field2; - ILInstruction value; - var storeInst = trueInst.Instructions[0]; - if (!condition.Left.MatchLdsFld(out field) || !condition.Right.MatchLdNull()) - return false; - if (!storeInst.MatchStsFld(out value, out field2) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) - return false; - if (value.OpCode != OpCode.NewObj || !ExpressionBuilder.IsDelegateConstruction((CallInstruction)value)) - return false; - var targetMethod = ((IInstructionWithMethodOperand)((CallInstruction)value).Arguments[1]).Method; - if (!DelegateConstruction.IsAnonymousMethod(decompilationContext.CurrentTypeDefinition, targetMethod)) - return false; - - var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); - if (nextInstruction == null) - return false; - var usages = nextInstruction.Descendants.OfType().Where(i => i.Field.Equals(field)).ToArray(); - if (usages.Length > 1) - return false; - usages[0].ReplaceWith(value); - return true; - } - - bool CachedDelegateInitializationWithLocal(IfInstruction inst) - { - return false; - } - } -} diff --git a/ICSharpCode.Decompiler/NRExtensions.cs b/ICSharpCode.Decompiler/NRExtensions.cs index 439eef510..ff2c2cb72 100644 --- a/ICSharpCode.Decompiler/NRExtensions.cs +++ b/ICSharpCode.Decompiler/NRExtensions.cs @@ -16,12 +16,30 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; +using System.Collections.Generic; using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.Decompiler { public static class NRExtensions { + public static IDecompilerTypeSystem GetSpecializingTypeSystem(this IDecompilerTypeSystem typeSystem, ITypeResolveContext decompilationContext) + { + IList classTypeParameters = null; + IList methodTypeParameters = null; + + if (decompilationContext.CurrentTypeDefinition != null) + classTypeParameters = decompilationContext.CurrentTypeDefinition.TypeArguments; + IMethod method = decompilationContext.CurrentMember as IMethod; + if (method != null) + methodTypeParameters = method.TypeArguments; + + if ((classTypeParameters != null && classTypeParameters.Count > 0) || (methodTypeParameters != null && methodTypeParameters.Count > 0)) + return new SpecializingDecompilerTypeSystem(typeSystem, new TypeParameterSubstitution(classTypeParameters, methodTypeParameters)); + else + return typeSystem; + } + public static bool IsCompilerGenerated(this IEntity entity) { if (entity != null) {