diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 3a53fe812..e069c0499 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -172,7 +172,8 @@ namespace ICSharpCode.Decompiler.ILAst foreach(ILBlock block in method.GetSelfAndChildrenRecursive()) { for (int i = 0; i < block.Body.Count; i++) { // TODO: Move before loops - CachedDelegateInitialization(block, ref i); + CachedDelegateInitializationWithField(block, ref i); + CachedDelegateInitializationWithLocal(block, ref i); } } diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index d65b26e24..c7dbd836b 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -130,7 +130,7 @@ namespace ICSharpCode.Decompiler.ILAst #endregion #region CachedDelegateInitialization - void CachedDelegateInitialization(ILBlock block, ref int i) + void CachedDelegateInitializationWithField(ILBlock block, ref int i) { // if (logicnot(ldsfld(field))) { // stsfld(field, newobj(Action::.ctor, ldnull(), ldftn(method))) @@ -181,6 +181,74 @@ namespace ICSharpCode.Decompiler.ILAst } } } + + void CachedDelegateInitializationWithLocal(ILBlock block, ref int i) + { + // if (logicnot(ldloc(v))) { + // stloc(v, newobj(Action::.ctor, ldloc(displayClass), ldftn(method))) + // } else { + // } + // ...(..., ldloc(v), ...) + + ILCondition c = block.Body[i] as ILCondition; + if (c == null || c.Condition == null && c.TrueBlock == null || c.FalseBlock == null) + return; + if (!(c.TrueBlock.Body.Count == 1 && c.FalseBlock.Body.Count == 0)) + return; + if (!c.Condition.Match(ILCode.LogicNot)) + return; + ILExpression condition = c.Condition.Arguments.Single() as ILExpression; + if (condition == null || condition.Code != ILCode.Ldloc) + return; + ILVariable v = (ILVariable)condition.Operand; + ILExpression stloc = c.TrueBlock.Body[0] as ILExpression; + if (!(stloc != null && stloc.Code == ILCode.Stloc && (ILVariable)stloc.Operand == v)) + return; + ILExpression newObj = stloc.Arguments[0]; + if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2)) + return; + if (newObj.Arguments[0].Code != ILCode.Ldloc) + return; + if (newObj.Arguments[1].Code != ILCode.Ldftn) + return; + MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly + if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod)) + return; + + ILNode followingNode = block.Body.ElementAtOrDefault(i + 1); + if (followingNode != null && followingNode.GetSelfAndChildrenRecursive().Count( + e => e.Code == ILCode.Ldloc && (ILVariable)e.Operand == v) == 1) + { + ILInlining inlining = new ILInlining(method); + if (!(inlining.numLdloc.GetOrDefault(v) == 2 && inlining.numStloc.GetOrDefault(v) == 2 && inlining.numLdloca.GetOrDefault(v) == 0)) + return; + + // Find the store instruction that initializes the local to null: + foreach (ILBlock storeBlock in method.GetSelfAndChildrenRecursive()) { + for (int j = 0; j < storeBlock.Body.Count; j++) { + ILVariable storedVar; + ILExpression storedExpr; + if (storeBlock.Body[j].Match(ILCode.Stloc, out storedVar, out storedExpr) && storedVar == v && storedExpr.Match(ILCode.Ldnull)) { + storeBlock.Body.RemoveAt(j); + if (storeBlock == block && j < i) + i--; + break; + } + } + } + + foreach (ILExpression parent in followingNode.GetSelfAndChildrenRecursive()) { + for (int j = 0; j < parent.Arguments.Count; j++) { + if (parent.Arguments[j].Code == ILCode.Ldloc && (ILVariable)parent.Arguments[j].Operand == v) { + parent.Arguments[j] = newObj; + block.Body.RemoveAt(i); + i -= new ILInlining(method).InlineInto(block.Body, i, aggressive: true); + return; + } + } + } + } + } #endregion #region MakeAssignmentExpression @@ -646,7 +714,7 @@ namespace ICSharpCode.Decompiler.ILAst // that can be eliminated. if (arrayLoadingExpr.Code == ILCode.Stloc) { ILInlining inlining = new ILInlining(method); - if (inlining.numLdloc.GetOrDefault(arrayVariable) == 2 && + if (inlining.numLdloc.GetOrDefault(arrayVariable) == 2 && inlining.numStloc.GetOrDefault(arrayVariable) == 1 && inlining.numLdloca.GetOrDefault(arrayVariable) == 0) { arrayLoadingExpr = arrayLoadingExpr.Arguments[0];