public void RemoveGotos(ILBlock method) { // Build the navigation data parent[method] = null; foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) { ILNode previousChild = null; foreach (ILNode child in node.GetChildren()) { if (parent.ContainsKey(child)) throw new Exception("The following expression is linked from several locations: " + child.ToString()); parent[child] = node; if (previousChild != null) nextSibling[previousChild] = child; previousChild = child; } if (previousChild != null) nextSibling[previousChild] = null; } // Simplify gotos bool modified; do { modified = false; foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { modified |= TrySimplifyGoto(gotoExpr); } } while(modified); RemoveRedundantCode(method); }
public void Optimize(ILBlock method) { foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { SplitToMovableBlocks(block); } foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList()) { ControlFlowGraph graph; graph = BuildGraph(block.Body, block.EntryPoint); graph.ComputeDominance(); graph.ComputeDominanceFrontier(); block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, true); } foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList()) { ControlFlowGraph graph; graph = BuildGraph(block.Body, block.EntryPoint); graph.ComputeDominance(); graph.ComputeDominanceFrontier(); block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); } // OrderNodes(method); FlattenNestedMovableBlocks(method); SimpleGotoRemoval(method); RemoveDeadLabels(method); }
public static void Transform(ILBlock method) { // TODO: move this somewhere else // Eliminate 'dups': foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { for (int i = 0; i < expr.Arguments.Count; i++) { if (expr.Arguments[i].Code == ILCode.Dup) expr.Arguments[i] = expr.Arguments[i].Arguments[0]; } } var newArrPattern = new StoreToVariable(new ILExpression(ILCode.Newarr, ILExpression.AnyOperand, new ILExpression(ILCode.Ldc_I4, ILExpression.AnyOperand))); var arg1 = new StoreToVariable(new LoadFromVariable(newArrPattern)) { MustBeGenerated = true }; var arg2 = new StoreToVariable(new LoadFromVariable(newArrPattern)) { MustBeGenerated = true }; var initializeArrayPattern = new ILCall( "System.Runtime.CompilerServices.RuntimeHelpers", "InitializeArray", new LoadFromVariable(arg1), new ILExpression(ILCode.Ldtoken, ILExpression.AnyOperand)); foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { for (int i = block.Body.Count - 1; i >= 0; i--) { if (!newArrPattern.Match(block.Body[i])) continue; ILExpression newArrInst = ((ILExpression)block.Body[i]).Arguments[0]; int arrayLength = (int)newArrInst.Arguments[0].Operand; if (arrayLength == 0) continue; if (arg1.Match(block.Body.ElementAtOrDefault(i + 1)) && arg2.Match(block.Body.ElementAtOrDefault(i + 2))) { if (initializeArrayPattern.Match(block.Body.ElementAtOrDefault(i + 3))) { if (HandleStaticallyInitializedArray(arg2, block, i, newArrInst, arrayLength)) { i -= ILInlining.InlineInto(block, i + 1, method) - 1; } continue; } } if (i + 1 + arrayLength > block.Body.Count) continue; List<ILExpression> operands = new List<ILExpression>(); for (int j = 0; j < arrayLength; j++) { ILExpression expr = block.Body[i + 1 + j] as ILExpression; if (expr == null || !IsStoreToArray(expr.Code)) break; if (!(expr.Arguments[0].Code == ILCode.Ldloc && expr.Arguments[0].Operand == newArrPattern.LastVariable)) break; if (!(expr.Arguments[1].Code == ILCode.Ldc_I4 && (int)expr.Arguments[1].Operand == j)) break; operands.Add(expr.Arguments[2]); } if (operands.Count == arrayLength) { ((ILExpression)block.Body[i]).Arguments[0] = new ILExpression( ILCode.InitArray, newArrInst.Operand, operands.ToArray()); block.Body.RemoveRange(i + 1, arrayLength); i -= ILInlining.InlineInto(block, i + 1, method) - 1; } } } }
public SimpleControlFlow(DecompilerContext context, ILBlock method) { this.context = context; this.typeSystem = context.CurrentMethod.Module.TypeSystem; foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; } foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) { foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) { labelToBasicBlock[label] = bb; } } }
public BlockStatement CreateMethodBody() { if (methodDef.Body == null) return null; context.CancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true); context.CancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(context, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); var allVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable).Where(v => v != null && !v.IsGenerated).Distinct(); NameVariables.AssignNamesToVariables(methodDef.Parameters.Select(p => p.Name), allVariables, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments foreach (ILVariable v in localVariablesToDefine) { DeclareVariableInSmallestScope.DeclareVariable(astBlock, AstBuilder.ConvertType(v.Type), v.Name); } return astBlock; }
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { if (!method.HasBody) { return; } ILAstBuilder astBuilder = new ILAstBuilder(); ILBlock ilMethod = new ILBlock(); ilMethod.Body = astBuilder.Build(method, inlineVariables); if (abortBeforeStep != null) { DecompilerContext context = new DecompilerContext(method.Module) { CurrentType = method.DeclaringType, CurrentMethod = method }; new ILAstOptimizer().Optimize(context, ilMethod, abortBeforeStep.Value); } var allVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); foreach (ILVariable v in allVariables) { output.WriteDefinition(v.Name, v); if (v.Type != null) { output.Write(" : "); if (v.IsPinned) output.Write("pinned "); v.Type.WriteTo(output, ILNameSyntax.ShortTypeName); } output.WriteLine(); } output.WriteLine(); foreach (ILNode node in ilMethod.Body) { node.WriteTo(output); output.WriteLine(); } }
public override void DecompileMethod(MethodDef method, ITextOutput output, DecompilationOptions options) { WriteComment(output, "Method: "); output.WriteDefinition(IdentifierEscaper.Escape(method.FullName), method, TextTokenType.Comment, false); output.WriteLine(); if (!method.HasBody) { return; } StartKeywordBlock(output, ".body", method); ILAstBuilder astBuilder = new ILAstBuilder(); ILBlock ilMethod = new ILBlock(); DecompilerContext context = new DecompilerContext(method.Module) { CurrentType = method.DeclaringType, CurrentMethod = method }; ilMethod.Body = astBuilder.Build(method, inlineVariables, context); if (abortBeforeStep != null) { new ILAstOptimizer().Optimize(context, ilMethod, abortBeforeStep.Value); } if (context.CurrentMethodIsAsync) { output.Write("async", TextTokenType.Keyword); output.Write('/', TextTokenType.Operator); output.WriteLine("await", TextTokenType.Keyword); } var allVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); foreach (ILVariable v in allVariables) { output.WriteDefinition(IdentifierEscaper.Escape(v.Name), v, v.IsParameter ? TextTokenType.Parameter : TextTokenType.Local); if (v.Type != null) { output.WriteSpace(); output.Write(':', TextTokenType.Operator); output.WriteSpace(); if (v.IsPinned) { output.Write("pinned", TextTokenType.Keyword); output.WriteSpace(); } v.Type.WriteTo(output, ILNameSyntax.ShortTypeName); } if (v.IsGenerated) { output.WriteSpace(); output.Write('[', TextTokenType.Operator); output.Write("generated", TextTokenType.Keyword); output.Write(']', TextTokenType.Operator); } output.WriteLine(); } var memberMapping = new MemberMapping(method); foreach (ILNode node in ilMethod.Body) { node.WriteTo(output, memberMapping); if (!node.WritesNewLine) output.WriteLine(); } output.AddDebugSymbols(memberMapping); EndKeywordBlock(output); }
public static void AssignNamesToVariables(IEnumerable<string> existingNames, IEnumerable<ILVariable> variables, ILBlock methodBody) { NameVariables nv = new NameVariables(); nv.AddExistingNames(existingNames); foreach (ILVariable varDef in variables) { nv.AssignNameToVariable(varDef, methodBody.GetSelfAndChildrenRecursive<ILExpression>()); } }
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { SplitToBasicBlocks(block); } OptimizeShortCircuits(method); if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { ControlFlowGraph graph; graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); graph.ComputeDominance(context.CancellationToken); graph.ComputeDominanceFrontier(); block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false); } if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { ControlFlowGraph graph; graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand); // TODO: Fix if (graph == null) continue; graph.ComputeDominance(context.CancellationToken); graph.ComputeDominanceFrontier(); block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint); } if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; FlattenBasicBlocks(method); if (abortBeforeStep == ILAstOptimizationStep.SimpleGotoRemoval) return; SimpleGotoRemoval(method); if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return; RemoveDeadLabels(method); if (abortBeforeStep == ILAstOptimizationStep.HandleArrayInitializers) return; ArrayInitializers.Transform(method); if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; TypeAnalysis.Run(context, method); }
public static void RemoveRedundantCode(ILBlock method) { // Remove dead lables and nops HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())); foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); } // Remove redundant continue foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive<ILWhileLoop>()) { var body = loop.BodyBlock.Body; if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) { body.RemoveAt(body.Count - 1); } } // Remove redundant break at the end of case // Remove redundant case blocks altogether foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) { foreach(ILBlock ilCase in ilSwitch.CaseBlocks) { Debug.Assert(ilCase.EntryGoto == null); int count = ilCase.Body.Count; if (count >= 2) { if (ilCase.Body[count - 2].IsUnconditionalControlFlow() && ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) { ilCase.Body.RemoveAt(count - 1); } } } var defaultCase = ilSwitch.CaseBlocks.Where(cb => cb.Values == null).SingleOrDefault(); // If there is no default block, remove empty case blocks if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) { ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak)); } } // Remove redundant return if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { method.Body.RemoveAt(method.Body.Count - 1); } }
public void RemoveGotos(ILBlock method) { // Build the navigation data parent[method] = null; foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) { ILNode previousChild = null; foreach (ILNode child in node.GetChildren()) { ILExpression e = child as ILExpression; if (e != null && (e.Operand is ILValue || e.Operand is ILVariable)) continue; // This should fix alot of issues if (child is ILValue || child is ILVariable) continue; // we want to skip these. // Added them as nodes so I don't have to dick with them latter with another AST if (parent.ContainsKey(child)) { // this throws on one single file and I don't know why the hell it does // its on obj_screen_Step_2 not sure why but its on an expression, so I am putting // a hack to skip expressions that don't have any gotos in it. Meh // debug, where the f**k is it var nodes = parent.Keys.Where(x => x == child); throw new Exception("The following expression is linked from several locations: " + child.ToString()); } parent[child] = node; if (previousChild != null) nextSibling[previousChild] = child; previousChild = child; } if (previousChild != null) nextSibling[previousChild] = null; } // Simplify gotos bool modified; do { modified = false; foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.Code == GMCode.B)) { modified |= TrySimplifyGoto(gotoExpr); } } while (modified); RemoveRedundantCode(method); }
public static void RemoveRedundantCode(ILBlock method) { // Remove dead lables and nops HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())); foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); } // Remove redundant continue foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive<ILWhileLoop>()) { var body = loop.BodyBlock.Body; if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) { body.RemoveAt(body.Count - 1); } } // Remove redundant return if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { method.Body.RemoveAt(method.Body.Count - 1); } }
public void RemoveGotos(ILBlock method) { // Build the navigation data parent[method] = null; foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) { ILNode previousChild = null; foreach (ILNode child in node.GetChildren()) { Debug.Assert(!parent.ContainsKey(child)); parent[child] = node; if (previousChild != null) nextSibling[previousChild] = child; previousChild = child; } if (previousChild != null) nextSibling[previousChild] = null; } // Simplify gotos foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>().Where(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) { TrySimplifyGoto(gotoExpr); } RemoveRedundantCode(method); }
public void Initialize(DecompilerContext context, ILBlock method) { this.labelGlobalRefCount.Clear(); this.labelToBasicBlock.Clear(); this.context = context; this.corLib = context.CurrentMethod.Module.CorLibTypes; foreach (var e in method.GetSelfAndChildrenRecursive<ILExpression>(List_ILExpression, e => e.IsBranch())) { foreach (var target in e.GetBranchTargets()) labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; } foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>(List_ILBasicBlock)) { int index = 0; for (;;) { var node = bb.GetNext(ref index); if (node == null) break; var label = node as ILLabel; if (label == null) continue; labelToBasicBlock[label] = bb; } } }
/// <summary> /// Inlines 'expr' into 'next', if possible. /// </summary> public static bool InlineIfPossible(ILExpression expr, ILNode next, ILBlock method) { if (expr.Code != ILCode.Stloc) throw new ArgumentException("expr must be stloc"); // ensure the variable is accessed only a single time if (method.GetSelfAndChildrenRecursive<ILExpression>().Count(e => e != expr && e.Operand == expr.Operand) != 1) return false; ILExpression parent; int pos; if (FindLoadInNext(next as ILExpression, (ILVariable)expr.Operand, out parent, out pos) == true) { parent.Arguments[pos] = expr.Arguments[0]; return true; } return false; }
public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters) { if (methodDef.Body == null) return null; context.CancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true); context.CancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(context, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); var allVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); Debug.Assert(context.CurrentMethod == methodDef); NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, allVariables, ilMethod); if (parameters != null) { foreach (var pair in (from p in parameters join v in astBuilder.Parameters on p.Annotation<ParameterDefinition>() equals v.OriginalParameter select new { p, v.Name })) { pair.p.Name = pair.Name; } } context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments Statement insertionPoint = astBlock.Statements.FirstOrDefault(); foreach (ILVariable v in localVariablesToDefine) { var newVarDecl = new VariableDeclarationStatement(AstBuilder.ConvertType(v.Type), v.Name); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } return astBlock; }
public static void RemoveRedundantCode(ILBlock method, DecompilerContext context) { // Remove dead lables and nops HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())); foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { var newBody = new List<ILNode>(block.Body.Count); for (int i = 0; i < block.Body.Count; i++) { var node = block.Body[i]; if (node.Match(ILCode.Nop)) Utils.NopMergeILRanges(block, newBody, i); else if (node is ILLabel && !liveLabels.Contains((ILLabel)node)) Utils.LabelMergeILRanges(block, newBody, i); else newBody.Add(node); } block.Body = newBody; } // Remove redundant continue foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive<ILWhileLoop>()) { var body = loop.BodyBlock.Body; if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) { body[body.Count - 1].AddSelfAndChildrenRecursiveILRanges(loop.EndILRanges); body.RemoveAt(body.Count - 1); } } // Remove redundant break at the end of case // Remove redundant case blocks altogether foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) { foreach(ILBlock ilCase in ilSwitch.CaseBlocks) { Debug.Assert(ilCase.EntryGoto == null); int count = ilCase.Body.Count; if (count >= 2) { if (ilCase.Body[count - 2].IsUnconditionalControlFlow() && ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak)) { var prev = ilCase.Body[count - 2]; ilCase.Body[count - 1].AddSelfAndChildrenRecursiveILRanges(prev.EndILRanges); ilCase.Body.RemoveAt(count - 1); } } } var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null); // If there is no default block, remove empty case blocks if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) { for (int i = ilSwitch.CaseBlocks.Count - 1; i >= 0; i--) { var caseBlock = ilSwitch.CaseBlocks[i]; if (caseBlock.Body.Count != 1 || !caseBlock.Body.Single().Match(ILCode.LoopOrSwitchBreak)) continue; caseBlock.Body[0].AddSelfAndChildrenRecursiveILRanges(ilSwitch.EndILRanges); ilSwitch.CaseBlocks.RemoveAt(i); } } } // Remove redundant return at the end of method if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { method.Body[method.Body.Count - 1].AddSelfAndChildrenRecursiveILRanges(method.EndILRanges); method.Body.RemoveAt(method.Body.Count - 1); } // Remove unreachable return statements bool modified = false; foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) { for (int i = 0; i < block.Body.Count - 1;) { if (block.Body[i].IsUnconditionalControlFlow() && block.Body[i+1].Match(ILCode.Ret)) { modified = true; block.Body[i + 1].AddSelfAndChildrenRecursiveILRanges(block.EndILRanges); block.Body.RemoveAt(i+1); } else { i++; } } } if (modified) { // More removals might be possible var gr = context.Cache.GetGotoRemoval(); try { gr.RemoveGotos(method); } finally { context.Cache.Return(gr); } } }
Dictionary<int, Action<XamlContext, XElement>> ExtractConnectionId(XamlContext ctx, MethodDef method) { var context = new DecompilerContext(method.Module) { CurrentType = method.DeclaringType, CurrentMethod = method, CancellationToken = ctx.CancellationToken }; var body = new ILBlock(new ILAstBuilder().Build(method, true, context)); new ILAstOptimizer().Optimize(context, body); var sw = body.GetSelfAndChildrenRecursive<ILSwitch>().FirstOrDefault(); if (sw == null) return null; var connIds = new Dictionary<int, Action<XamlContext, XElement>>(); foreach (var cas in sw.CaseBlocks) { if (cas.Values == null) continue; Action<XamlContext, XElement> cb = null; foreach (var node in cas.Body) { var expr = node as ILExpression; if (expr == null) continue; switch (expr.Code) { case ILCode.Stfld: cb += new FieldAssignment { FieldName = ((IField)expr.Operand).Name }.Callback; break; case ILCode.Call: case ILCode.Callvirt: var operand = (IMethod)expr.Operand; if (operand.Name == "AddHandler" && operand.DeclaringType.FullName == "System.Windows.UIElement") { // Attached event var re = expr.Arguments[1]; var ctor = expr.Arguments[2]; var reField = re.Operand as IField; if (re.Code != ILCode.Ldsfld || ctor.Code != ILCode.Newobj || ctor.Arguments.Count != 2 || ctor.Arguments[1].Code != ILCode.Ldftn) { cb += new Error { Msg = "Attached event '" + reField.Name + "'." }.Callback; break; } var handler = (IMethod)ctor.Arguments[1].Operand; string evName = reField.Name; if (evName.EndsWith("Event")) evName = evName.Substring(0, evName.Length - 5); cb += new EventAttachment { AttachedType = reField.DeclaringType.ResolveTypeDefThrow(), EventName = evName, MethodName = handler.Name }.Callback; } else { // CLR event var add = operand.ResolveMethodDefThrow(); var ev = add.DeclaringType.Events.FirstOrDefault(e => e.AddMethod == add); var ctor = expr.Arguments[1]; if (ev == null || ctor.Code != ILCode.Newobj || ctor.Arguments.Count != 2 || ctor.Arguments[1].Code != ILCode.Ldftn) { cb += new Error { Msg = "Attached event '" + add.Name + "'." }.Callback; break; } var handler = (IMethod)ctor.Arguments[1].Operand; cb += new EventAttachment { EventName = ev.Name, MethodName = handler.Name }.Callback; } break; } } if (cb != null) { foreach (var id in cas.Values) connIds[id] = cb; } } return connIds.Count == 0 ? null : connIds; }
void UpdateLabelRefCounts(ILBlock method) { labelRefCount = new Dictionary<ILLabel, int>(); foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())) { if (!labelRefCount.ContainsKey(target)) labelRefCount[target] = 0; labelRefCount[target]++; } }
string GenerateNameForVariable(ILVariable variable, ILBlock methodBody) { string proposedName = null; if (variable.Type == context.CurrentType.Module.TypeSystem.Int32) { // test whether the variable might be a loop counter bool isLoopCounter = false; foreach (ILWhileLoop loop in methodBody.GetSelfAndChildrenRecursive <ILWhileLoop>()) { ILExpression expr = loop.Condition; while (expr != null && expr.Code == ILCode.LogicNot) { expr = expr.Arguments[0]; } if (expr != null) { switch (expr.Code) { case ILCode.Clt: case ILCode.Clt_Un: case ILCode.Cgt: case ILCode.Cgt_Un: case ILCode.Cle: case ILCode.Cle_Un: case ILCode.Cge: case ILCode.Cge_Un: ILVariable loadVar; if (expr.Arguments[0].Match(ILCode.Ldloc, out loadVar) && loadVar == variable) { isLoopCounter = true; } break; } } } if (isLoopCounter) { // For loop variables, use i,j,k,l,m,n for (char c = 'i'; c <= maxLoopVariableName; c++) { if (!typeNames.ContainsKey(c.ToString())) { proposedName = c.ToString(); break; } } } } if (string.IsNullOrEmpty(proposedName)) { var proposedNameForStores = (from expr in methodBody.GetSelfAndChildrenRecursive <ILExpression>() where expr.Code == ILCode.Stloc && expr.Operand == variable select GetNameFromExpression(expr.Arguments.Single()) ).Except(fieldNamesInCurrentType).ToList(); if (proposedNameForStores.Count == 1) { proposedName = proposedNameForStores[0]; } } if (string.IsNullOrEmpty(proposedName)) { var proposedNameForLoads = (from expr in methodBody.GetSelfAndChildrenRecursive <ILExpression>() from i in Enumerable.Range(0, expr.Arguments.Count) let arg = expr.Arguments[i] where arg.Code == ILCode.Ldloc && arg.Operand == variable select GetNameForArgument(expr, i) ).Except(fieldNamesInCurrentType).ToList(); if (proposedNameForLoads.Count == 1) { proposedName = proposedNameForLoads[0]; } } if (string.IsNullOrEmpty(proposedName)) { proposedName = GetNameByType(variable.Type); } // remove any numbers from the proposed name int number; proposedName = SplitName(proposedName, out number); if (!typeNames.ContainsKey(proposedName)) { typeNames.Add(proposedName, 0); } int count = ++typeNames[proposedName]; if (count > 1) { return(proposedName + count.ToString()); } else { return(proposedName); } }
void SimpleGotoRemoval(ILBlock ast) { // TODO: Assign IL ranges from br to something else var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().ToList(); foreach(ILBlock block in blocks) { for (int i = 0; i < block.Body.Count; i++) { ILExpression expr = block.Body[i] as ILExpression; // Uncoditional branch if (expr != null && (expr.OpCode == OpCodes.Br || expr.OpCode == OpCodes.Br_S)) { // Check that branch is followed by its label (allow multiple labels) for (int j = i + 1; j < block.Body.Count; j++) { ILLabel label = block.Body[j] as ILLabel; if (label == null) break; // Can not optimize if (expr.Operand == label) { block.Body.RemoveAt(i); break; // Branch removed } } } } } }
/* public enum ShortCircuitOperator { LeftAndRight, LeftOrRight, NotLeftAndRight, NotLeftOrRight, } static bool TryOptimizeShortCircuit(Node head) { if ((head is BasicBlock) && (head as BasicBlock).BranchBasicBlock != null && (head as BasicBlock).FallThroughBasicBlock != null) { head.Parent.MergeChilds<SimpleBranch>(head); return true; } Branch top = head as Branch; if (top == null) return false; Branch left = head.FloatUpToNeighbours(top.TrueSuccessor) as Branch; Branch right = head.FloatUpToNeighbours(top.FalseSuccessor) as Branch; // A & B if (left != null && left.Predecessors.Count == 1 && left.FalseSuccessor == top.FalseSuccessor) { ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left); scBranch.Operator = ShortCircuitOperator.LeftAndRight; return true; } // ~A | B if (left != null && left.Predecessors.Count == 1 && left.TrueSuccessor == top.FalseSuccessor) { ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, left); scBranch.Operator = ShortCircuitOperator.NotLeftOrRight; return true; } // A | B if (right != null && right.Predecessors.Count == 1 && right.TrueSuccessor == top.TrueSuccessor) { ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right); scBranch.Operator = ShortCircuitOperator.LeftOrRight; return true; } // ~A & B if (right != null && right.Predecessors.Count == 1 && right.FalseSuccessor == top.TrueSuccessor) { ShortCircuitBranch scBranch = top.Parent.MergeChilds<ShortCircuitBranch>(top, right); scBranch.Operator = ShortCircuitOperator.NotLeftAndRight; return true; } return false; } */ void OrderNodes(ILBlock ast) { // Order movable nodes var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList(); ILMoveableBlock first = new ILMoveableBlock() { OriginalOrder = -1 }; foreach(ILBlock block in blocks) { block.Body = block.Body.OrderBy(n => (n.GetSelfAndChildrenRecursive<ILMoveableBlock>().FirstOrDefault() ?? first).OriginalOrder).ToList(); } }
List <(IList <int> connIds, List <ILNode> nodes)> GetCaseBlocks(ILBlock method) { var list = new List <(IList <int>, List <ILNode>)>(); var body = method.Body; if (body.Count == 0) { return(list); } var sw = method.GetSelfAndChildrenRecursive <ILSwitch>().FirstOrDefault(); if (sw is not null) { foreach (var lbl in sw.CaseBlocks) { if (lbl.Values is null) { continue; } list.Add((lbl.Values, lbl.Body)); } return(list); } else { int pos = 0; for (;;) { if (pos >= body.Count) { return(null); } var cond = body[pos] as ILCondition; if (cond is null) { if (!body[pos].Match(ILCode.Stfld, out IField field, out var ldthis, out var ldci4) || !ldthis.MatchThis() || !ldci4.MatchLdcI4(1)) { return(null); } return(list); } pos++; if (cond.TrueBlock is null || cond.FalseBlock is null) { return(null); } bool isEq = true; var condExpr = cond.Condition; for (;;) { if (!condExpr.Match(ILCode.LogicNot, out ILExpression expr)) { break; } isEq = !isEq; condExpr = expr; } if (condExpr.Code != ILCode.Ceq && condExpr.Code != ILCode.Cne) { return(null); } if (condExpr.Arguments.Count != 2) { return(null); } if (!condExpr.Arguments[0].Match(ILCode.Ldloc, out ILVariable v) || v.OriginalParameter?.Index != 1) { return(null); } if (!condExpr.Arguments[1].Match(ILCode.Ldc_I4, out int val)) { return(null); } if (condExpr.Code == ILCode.Cne) { isEq ^= true; } if (isEq) { list.Add((new[] { val }, cond.TrueBlock.Body)); if (cond.FalseBlock.Body.Count != 0) { body = cond.FalseBlock.Body; pos = 0; } } else { if (cond.FalseBlock.Body.Count != 0) { list.Add((new[] { val }, cond.FalseBlock.Body)); if (cond.TrueBlock.Body.Count != 0) { body = cond.TrueBlock.Body; pos = 0; } } else { list.Add((new[] { val }, body.Skip(pos).ToList())); return(list); } } } } }
public override void Decompile(MethodDef method, IDecompilerOutput output, DecompilationContext ctx) { WriteCommentBegin(output, true); output.Write("Method: ", BoxedTextColor.Comment); output.Write(IdentifierEscaper.Escape(method.FullName), method, DecompilerReferenceFlags.Definition, BoxedTextColor.Comment); WriteCommentEnd(output, true); output.WriteLine(); if (!method.HasBody) { return; } var bodyInfo = StartKeywordBlock(output, ".body", method); ILAstBuilder astBuilder = new ILAstBuilder(); ILBlock ilMethod = new ILBlock(CodeBracesRangeFlags.MethodBraces); DecompilerContext context = new DecompilerContext(settingsVersion, method.Module, MetadataTextColorProvider) { CurrentType = method.DeclaringType, CurrentMethod = method, CalculateILSpans = ctx.CalculateILSpans, }; ilMethod.Body = astBuilder.Build(method, inlineVariables, context); var stateMachineKind = StateMachineKind.None; MethodDef? inlinedMethod = null; AsyncMethodDebugInfo?asyncInfo = null; string? compilerName = null; if (!(abortBeforeStep is null)) { var optimizer = new ILAstOptimizer(); optimizer.Optimize(context, ilMethod, out stateMachineKind, out inlinedMethod, out asyncInfo, abortBeforeStep.Value); compilerName = optimizer.CompilerName; } if (context.CurrentMethodIsYieldReturn) { output.Write("yield", BoxedTextColor.Keyword); output.Write(" ", BoxedTextColor.Text); output.WriteLine("return", BoxedTextColor.Keyword); } if (context.CurrentMethodIsAsync) { output.Write("async", BoxedTextColor.Keyword); output.Write("/", BoxedTextColor.Punctuation); output.WriteLine("await", BoxedTextColor.Keyword); } var allVariables = ilMethod.GetSelfAndChildrenRecursive <ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => !(v is null) && !v.IsParameter).Distinct(); foreach (ILVariable v in allVariables) { output.Write(IdentifierEscaper.Escape(v.Name), v.GetTextReferenceObject(), DecompilerReferenceFlags.Local | DecompilerReferenceFlags.Definition, v.IsParameter ? BoxedTextColor.Parameter : BoxedTextColor.Local); if (!(v.Type is null)) { output.Write(" ", BoxedTextColor.Text); output.Write(":", BoxedTextColor.Punctuation); output.Write(" ", BoxedTextColor.Text); if (v.IsPinned) { output.Write("pinned", BoxedTextColor.Keyword); output.Write(" ", BoxedTextColor.Text); } v.Type.WriteTo(output, ILNameSyntax.ShortTypeName); } if (v.GeneratedByDecompiler) { output.Write(" ", BoxedTextColor.Text); var start = output.NextPosition; output.Write("[", BoxedTextColor.Punctuation); output.Write("generated", BoxedTextColor.Keyword); var end = output.NextPosition; output.Write("]", BoxedTextColor.Punctuation); output.AddBracePair(new TextSpan(start, 1), new TextSpan(end, 1), CodeBracesRangeFlags.SquareBrackets); } output.WriteLine(); } var localVariables = new HashSet <ILVariable>(GetVariables(ilMethod)); var builder = new MethodDebugInfoBuilder(settingsVersion, stateMachineKind, inlinedMethod ?? method, !(inlinedMethod is null) ? method : null, CreateSourceLocals(localVariables), CreateSourceParameters(localVariables), asyncInfo); builder.CompilerName = compilerName; foreach (ILNode node in ilMethod.Body) { node.WriteTo(output, builder); if (!node.WritesNewLine) { output.WriteLine(); } } output.AddDebugInfo(builder.Create()); EndKeywordBlock(output, bodyInfo, CodeBracesRangeFlags.MethodBraces, addLineSeparator: true); }
bool BeforeConditionsDebugSainityCheck(ILBlock method) { HashSet <ILBasicBlock> badblocks = new HashSet <ILBasicBlock>(); Dictionary <GMCode, int> badCodes = new Dictionary <GMCode, int>(); foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { foreach (ILBasicBlock bb in block.GetSelfAndChildrenRecursive <ILBasicBlock>()) { foreach (ILExpression expr in bb.GetSelfAndChildrenRecursive <ILExpression>()) { if (expr.Operand is UnresolvedVar) { if (!badCodes.ContainsKey(expr.Code)) { badCodes[expr.Code] = 0; } badCodes[expr.Code]++; badblocks.Add(bb); } else if (expr.Code == GMCode.Dup || expr.Code == GMCode.Push || expr.Code == GMCode.Popz || expr.Code == GMCode.CallUnresolved) { if (!badCodes.ContainsKey(expr.Code)) { badCodes[expr.Code] = 0; } badCodes[expr.Code]++; badblocks.Add(bb); } else if ((expr.Code == GMCode.Bt || expr.Code == GMCode.Bf) && expr.Arguments.Count == 0) { if (!badCodes.ContainsKey(expr.Code)) { badCodes[expr.Code] = 0; } badCodes[expr.Code]++; badblocks.Add(bb); } } } } if (badblocks.Count > 0) { ControlFlowLabelMap map = new ControlFlowLabelMap(method, error); ILBlock badmethod = new ILBlock(); error.DebugSave(method, "bad_block_dump.txt"); // HashSet<ILBasicBlock> callies = new HashSet<ILBasicBlock>(); try { foreach (var bb in badblocks.ToList()) { badblocks.UnionWith(bb.GetChildren() .OfType <ILExpression>() .Where(x => x.Operand is ILLabel) .Select(x => x.Operand as ILLabel) .Select(x => map.LabelToBasicBlock(x))); var p = map.LabelToParrents(bb.EntryLabel()); p.Add(bb.GotoLabel()); // Debug.Assert(p.Count == 1); badblocks.UnionWith(p.Select(x => map.LabelToBasicBlock(x))); // badblocks.Add(map.LabelToBasicBlock(p[0])); } badmethod.Body = badblocks.OrderBy(b => b.GotoLabelName()).Select(b => (ILNode)b).ToList(); } catch (Exception e) { // we cannot build a map, so just copy bad blocks error.Error("Cannot build a map of the method, probery missing Label Exception: {0}", e.Message); badmethod.Body = method.Body; } string dfilename = error.MakeDebugFileName("bad_blocks.txt"); using (StreamWriter sw = new StreamWriter(dfilename)) { sw.WriteLine("Time : {0}", DateTime.Now); sw.WriteLine("Filename: {0}", dfilename); sw.WriteLine("Code File: {0}", error.CodeName); foreach (var kp in badCodes) { sw.WriteLine("Code: \"{0}\" Count: {1}", kp.Key, kp.Value); } sw.WriteLine(); sw.WriteLine(badmethod.ToString()); } error.Message("Saved '{0}'", dfilename); error.FatalError("Before graph sanity check failed, look at bad_block_dump.txt"); return(true); } return(false); }
public ILBlock Build(ILBlock method, Dictionary <string, ILVariable> locals, ErrorContext error) { if (method == null) { throw new ArgumentNullException("method"); } if (error == null) { throw new ArgumentNullException("error"); } if (locals == null) { throw new ArgumentNullException("locals"); } this.error = error; this.locals = locals; error.CheckDebugThenSave(method, "raw.txt", true); // Not sure I need this pass now // WE doo now, This converts popenv to either breaks or branchs. This is needed // as if you return from a pushenv, a popenv break is called FixAllPushesAndPopenv(method.Body); // makes sure all pushes have no operands and are all expressions for latter matches Optimize.RemoveRedundantCode(method); foreach (var block in method.GetSelfAndChildrenRecursive <ILBlock>()) { Optimize.SplitToBasicBlocks(block, true); } error.CheckDebugThenSave(method, "basic_blocks.txt"); bool modified = false; bool debug_once = true; foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { do { modified = false; do // Does all the internal things to a blocks for other passes to be easyer { modified = false; modified |= block.RunOptimization(MatchVariablePush); // checks pushes for instance or indexs for vars modified |= block.RunOptimization(SimpleAssignments); modified |= block.RunOptimization(AssignValueTo); modified |= block.RunOptimization(ComplexAssignments); // basicly self increment, this SHOULDN'T cross block boundrys modified |= block.RunOptimization(SimplifyBranches); // Any resolved pushes are put into a branch argument modified |= block.RunOptimization(CombineCall); // Any resolved pushes are put into a branch argument modified |= block.RunOptimization(CombineExpressions); modified |= block.RunOptimization(FixTempReturnValues); } while (modified); if (Context.Debug) { if (debug_once) { error.CheckDebugThenSave(method, "basic_blocks_resolved.txt"); debug_once = false; } } // modified |= block.RunOptimization(new SimpleControlFlow(method,error).DetectSwitch); // modified |= block.RunOptimization(new SimpleControlFlow(method, error).DetectSwitchAndConvertToBranches); modified |= block.RunOptimization(new SimpleControlFlow(method, error).DetectSwitch_GenerateSwitch); modified |= block.RunOptimization(MultiDimenionArray); modified |= block.RunOptimization(Optimize.SimplifyBoolTypes); modified |= block.RunOptimization(Optimize.SimplifyLogicNot); modified |= block.RunOptimization(PushEnviromentFix); // match all with's with expressions modified |= block.RunOptimization(new SimpleControlFlow(method, error).SimplifyShortCircuit); modified |= block.RunOptimization(new SimpleControlFlow(method, error).SimplifyTernaryOperator); modified |= block.RunOptimization(new SimpleControlFlow(method, error).MatchRepeatStructure); modified |= block.RunOptimization(new SimpleControlFlow(method, error).JoinBasicBlocks); // somewhere, so bug, is leaving an empty block, I think because of switches // It screws up the flatten block check for some reason modified |= block.RunOptimization(new SimpleControlFlow(method, error).RemoveRedundentBlocks); // want to run this at the end to fix return stuff } while (modified); } error.CheckDebugThenSave(method, "before_loops.txt"); if (BeforeConditionsDebugSainityCheck(method)) { return(null); // sainity check, evething must be ready for this } foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { new LoopsAndConditions(error).FindLoops(block); } error.CheckDebugThenSave(method, "before_conditions.txt"); foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { new LoopsAndConditions(error).FindConditions(block); } error.CheckDebugThenSave(method, "before_flatten.txt"); FlattenBasicBlocks(method); error.CheckDebugThenSave(method, "before_gotos.txt"); Optimize.RemoveRedundantCode(method); new GotoRemoval().RemoveGotos(method); error.CheckDebugThenSave(method, "before_if.txt"); // This is cleaned up in ILSpy latter when its converted to another ast structure, but I clean it up here // cause I don't convert it and mabye not converting all bt's to bf's dosn't // FixIfStatements(method); Optimize.RemoveRedundantCode(method); new GotoRemoval().RemoveGotos(method); error.CheckDebugThenSave(method, "final.txt"); if (AfterLoopsAndConditions(method)) { return(null); // another sanity check } return(method); }
public override void Decompile(MethodDef method, IDecompilerOutput output, DecompilationContext ctx) { WriteCommentBegin(output, true); output.Write("Method: ", BoxedTextColor.Comment); output.Write(IdentifierEscaper.Escape(method.FullName), method, DecompilerReferenceFlags.Definition, BoxedTextColor.Comment); WriteCommentEnd(output, true); output.WriteLine(); if (!method.HasBody) { return; } StartKeywordBlock(output, ".body", method); ILAstBuilder astBuilder = new ILAstBuilder(); ILBlock ilMethod = new ILBlock(); DecompilerContext context = new DecompilerContext(method.Module, MetadataTextColorProvider) { CurrentType = method.DeclaringType, CurrentMethod = method }; ilMethod.Body = astBuilder.Build(method, inlineVariables, context); if (abortBeforeStep != null) { new ILAstOptimizer().Optimize(context, ilMethod, abortBeforeStep.Value); } if (context.CurrentMethodIsAsync) { output.Write("async", BoxedTextColor.Keyword); output.Write("/", BoxedTextColor.Punctuation); output.WriteLine("await", BoxedTextColor.Keyword); } var allVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); foreach (ILVariable v in allVariables) { output.Write(IdentifierEscaper.Escape(v.Name), v, DecompilerReferenceFlags.Local | DecompilerReferenceFlags.Definition, v.IsParameter ? BoxedTextColor.Parameter : BoxedTextColor.Local); if (v.Type != null) { output.Write(" ", BoxedTextColor.Text); output.Write(":", BoxedTextColor.Punctuation); output.Write(" ", BoxedTextColor.Text); if (v.IsPinned) { output.Write("pinned", BoxedTextColor.Keyword); output.Write(" ", BoxedTextColor.Text); } v.Type.WriteTo(output, ILNameSyntax.ShortTypeName); } if (v.GeneratedByDecompiler) { output.Write(" ", BoxedTextColor.Text); output.Write("[", BoxedTextColor.Punctuation); output.Write("generated", BoxedTextColor.Keyword); output.Write("]", BoxedTextColor.Punctuation); } output.WriteLine(); } var builder = new MethodDebugInfoBuilder(method); foreach (ILNode node in ilMethod.Body) { node.WriteTo(output, builder); if (!node.WritesNewLine) output.WriteLine(); } output.AddDebugInfo(builder.Create()); EndKeywordBlock(output); }
/// <summary> /// Clears the type inference data on the method. /// </summary> public static void Reset(ILBlock method, List<ILExpression> list_ILExpression) { foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>(list_ILExpression)) { expr.InferredType = null; expr.ExpectedType = null; ILVariable v = expr.Operand as ILVariable; if (v != null && v.GeneratedByDecompiler) v.Type = null; } }
public override void Decompile(MethodDef method, IDecompilerOutput output, DecompilationContext ctx) { WriteCommentBegin(output, true); output.Write("Method: ", BoxedTextColor.Comment); output.Write(IdentifierEscaper.Escape(method.FullName), method, DecompilerReferenceFlags.Definition, BoxedTextColor.Comment); WriteCommentEnd(output, true); output.WriteLine(); if (!method.HasBody) { return; } var bodyInfo = StartKeywordBlock(output, ".body", method); ILAstBuilder astBuilder = new ILAstBuilder(); ILBlock ilMethod = new ILBlock(CodeBracesRangeFlags.MethodBraces); DecompilerContext context = new DecompilerContext(method.Module, MetadataTextColorProvider) { CurrentType = method.DeclaringType, CurrentMethod = method, CalculateBinSpans = ctx.CalculateBinSpans, }; ilMethod.Body = astBuilder.Build(method, inlineVariables, context); if (abortBeforeStep != null) { new ILAstOptimizer().Optimize(context, ilMethod, abortBeforeStep.Value); } if (context.CurrentMethodIsYieldReturn) { output.Write("yield", BoxedTextColor.Keyword); output.Write(" ", BoxedTextColor.Text); output.WriteLine("return", BoxedTextColor.Keyword); } if (context.CurrentMethodIsAsync) { output.Write("async", BoxedTextColor.Keyword); output.Write("/", BoxedTextColor.Punctuation); output.WriteLine("await", BoxedTextColor.Keyword); } var allVariables = ilMethod.GetSelfAndChildrenRecursive <ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); foreach (ILVariable v in allVariables) { output.Write(IdentifierEscaper.Escape(v.Name), (object)v.OriginalVariable ?? (object)v.OriginalParameter ?? v.Id, DecompilerReferenceFlags.Local | DecompilerReferenceFlags.Definition, v.IsParameter ? BoxedTextColor.Parameter : BoxedTextColor.Local); if (v.Type != null) { output.Write(" ", BoxedTextColor.Text); output.Write(":", BoxedTextColor.Punctuation); output.Write(" ", BoxedTextColor.Text); if (v.IsPinned) { output.Write("pinned", BoxedTextColor.Keyword); output.Write(" ", BoxedTextColor.Text); } v.Type.WriteTo(output, ILNameSyntax.ShortTypeName); } if (v.GeneratedByDecompiler) { output.Write(" ", BoxedTextColor.Text); var start = output.NextPosition; output.Write("[", BoxedTextColor.Punctuation); output.Write("generated", BoxedTextColor.Keyword); var end = output.NextPosition; output.Write("]", BoxedTextColor.Punctuation); output.AddBracePair(new TextSpan(start, 1), new TextSpan(end, 1), CodeBracesRangeFlags.SquareBrackets); } output.WriteLine(); } var builder = new MethodDebugInfoBuilder(method); foreach (ILNode node in ilMethod.Body) { node.WriteTo(output, builder); if (!node.WritesNewLine) { output.WriteLine(); } } output.AddDebugInfo(builder.Create()); EndKeywordBlock(output, bodyInfo, CodeBracesRangeFlags.MethodBraces, addLineSeparator: true); }
void OptimizeShortCircuits(ILBlock method) { AnalyseLabels(method); foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) { bool modified; do { modified = false; for (int i = 0; i < block.Body.Count;) { if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) { modified = true; } else { i++; } } } while(modified); } }
public static void RemoveRedundantCode(ILBlock method) { // Remove dead lables and nops and any popzs left HashSet <ILLabel> liveLabels = new HashSet <ILLabel>(method.GetSelfAndChildrenRecursive <ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())); foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { block.Body = block.Body.Where(n => !n.Match(GMCode.BadOp) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList(); } // Remove redundant continue foreach (ILWhileLoop loop in method.GetSelfAndChildrenRecursive <ILWhileLoop>()) { var body = loop.Body.Body; if (body.Count > 0 && body.Last().Match(GMCode.LoopContinue)) { body.RemoveAt(body.Count - 1); } } // Remove redundant continue foreach (ILWithStatement with in method.GetSelfAndChildrenRecursive <ILWithStatement>()) { var body = with.Body.Body; if (body.Count > 0 && body.Last().Match(GMCode.LoopContinue)) { body.RemoveAt(body.Count - 1); } } // Remove redundant break at the end of case // Remove redundant case blocks altogether foreach (ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive <ILSwitch>()) { foreach (ILBlock ilCase in ilSwitch.Cases) { Debug.Assert(ilCase.EntryGoto == null); int count = ilCase.Body.Count; if (count >= 2) { if (ilCase.Body[count - 2].IsUnconditionalControlFlow() && ilCase.Body[count - 1].Match(GMCode.LoopOrSwitchBreak)) { ilCase.Body.RemoveAt(count - 1); } } } // fix case block var defaultCase = ilSwitch.Default ?? ilSwitch.Cases.SingleOrDefault(cb => cb.Values == null); // If there is no default block, remove empty case blocks if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(GMCode.LoopOrSwitchBreak))) { foreach (var c in ilSwitch.Cases.Where(b => b.Body.Count == 1 && b.Body.Single().Match(GMCode.LoopOrSwitchBreak)).ToList()) { ilSwitch.Cases.Remove(c); } } } // Remove redundant return at the end of method if (method.Body.Count > 0 && (method.Body.Last().Match(GMCode.Ret) || method.Body.Last().Match(GMCode.Exit)) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) { method.Body.RemoveAt(method.Body.Count - 1); } // Remove unreachable return statements bool modified = false; foreach (ILBlock block in method.GetSelfAndChildrenRecursive <ILBlock>()) { for (int i = 0; i < block.Body.Count - 1;) { if (block.Body[i].IsUnconditionalControlFlow() && (block.Body[i + 1].Match(GMCode.Ret) || block.Body[i + 1].Match(GMCode.Exit))) { modified = true; block.Body.RemoveAt(i + 1); } else { i++; } } } // Remove empty falseBlocks foreach (ILCondition condition in method.GetSelfAndChildrenRecursive <ILCondition>().Where(x => x.FalseBlock != null && x.FalseBlock.Body.Count == 0)) { condition.FalseBlock = null; modified = true; } if (modified) { // More removals might be possible new GotoRemoval().RemoveGotos(method); } }
void RemoveDeadLabels(ILBlock ast) { HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(ast.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.GetBranchTargets())); var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().ToList(); foreach(ILBlock block in blocks) { for (int i = 0; i < block.Body.Count;) { ILLabel label = block.Body[i] as ILLabel; if (label != null && !liveLabels.Contains(label)) { block.Body.RemoveAt(i); } else { i++; } } } }
public override void Decompile(MethodDef method, ITextOutput output, DecompilationContext ctx) { WriteCommentBegin(output, true); output.Write("Method: ", TextTokenKind.Comment); output.WriteDefinition(IdentifierEscaper.Escape(method.FullName), method, TextTokenKind.Comment, false); WriteCommentEnd(output, true); output.WriteLine(); if (!method.HasBody) { return; } StartKeywordBlock(output, ".body", method); ILAstBuilder astBuilder = new ILAstBuilder(); ILBlock ilMethod = new ILBlock(); DecompilerContext context = new DecompilerContext(method.Module) { CurrentType = method.DeclaringType, CurrentMethod = method }; ilMethod.Body = astBuilder.Build(method, inlineVariables, context); if (abortBeforeStep != null) { new ILAstOptimizer().Optimize(context, ilMethod, abortBeforeStep.Value); } if (context.CurrentMethodIsAsync) { output.Write("async", TextTokenKind.Keyword); output.Write("/", TextTokenKind.Operator); output.WriteLine("await", TextTokenKind.Keyword); } var allVariables = ilMethod.GetSelfAndChildrenRecursive <ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); foreach (ILVariable v in allVariables) { output.WriteDefinition(IdentifierEscaper.Escape(v.Name), v, v.IsParameter ? TextTokenKind.Parameter : TextTokenKind.Local); if (v.Type != null) { output.WriteSpace(); output.Write(":", TextTokenKind.Operator); output.WriteSpace(); if (v.IsPinned) { output.Write("pinned", TextTokenKind.Keyword); output.WriteSpace(); } v.Type.WriteTo(output, ILNameSyntax.ShortTypeName); } if (v.GeneratedByDecompiler) { output.WriteSpace(); output.Write("[", TextTokenKind.Operator); output.Write("generated", TextTokenKind.Keyword); output.Write("]", TextTokenKind.Operator); } output.WriteLine(); } var memberMapping = new MemberMapping(method); foreach (ILNode node in ilMethod.Body) { node.WriteTo(output, memberMapping); if (!node.WritesNewLine) { output.WriteLine(); } } output.AddDebugSymbols(memberMapping); EndKeywordBlock(output); }
public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters, ConcurrentDictionary<int, IEnumerable<ILVariable>> localVariables) { if (methodDef.Body == null) return null; if (localVariables == null) throw new ArgumentException("localVariables must be instantiated"); context.CancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true); context.CancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(context, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); var allVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); Debug.Assert(context.CurrentMethod == methodDef); NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, allVariables, ilMethod); if (parameters != null) { foreach (var pair in (from p in parameters join v in astBuilder.Parameters on p.Annotation<ParameterDefinition>() equals v.OriginalParameter select new { p, v.Name })) { pair.p.Name = pair.Name; } } context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments Statement insertionPoint = astBlock.Statements.FirstOrDefault(); foreach (ILVariable v in localVariablesToDefine) { AstType type; if (v.Type.ContainsAnonymousType()) type = new SimpleType("var"); else type = AstBuilder.ConvertType(v.Type); var newVarDecl = new VariableDeclarationStatement(type, v.Name); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } // store the variables - used for debugger int token = methodDef.MetadataToken.ToInt32(); localVariables.AddOrUpdate(token, allVariables, (key, oldValue) => allVariables); return astBlock; }
internal JSFunctionExpression TranslateMethodExpression(DecompilerContext context, MethodReference method, MethodDefinition methodDef) { var oldMethod = context.CurrentMethod; try { if (method == null) throw new ArgumentNullException("method"); if (methodDef == null) throw new ArgumentNullException("methodDef"); var methodInfo = TypeInfoProvider.GetMemberInformation<JSIL.Internal.MethodInfo>(methodDef); var identifier = new QualifiedMemberIdentifier( methodInfo.DeclaringType.Identifier, methodInfo.Identifier ); JSFunctionExpression function = null; if (FunctionCache.TryGetExpression(identifier, out function)) return function; if (methodInfo.IsExternal) { FunctionCache.CreateNull(methodInfo, method, identifier); return null; } var bodyDef = methodDef; if (methodInfo.IsFromProxy && methodInfo.Member.HasBody) bodyDef = methodInfo.Member; var pr = new ProgressReporter(); context.CurrentMethod = methodDef; if ((methodDef.Body.Instructions.Count > LargeMethodThreshold) && (this.DecompilingMethod != null)) this.DecompilingMethod(method.FullName, pr); ILBlock ilb; var decompiler = new ILAstBuilder(); var optimizer = new ILAstOptimizer(); try { ilb = new ILBlock(decompiler.Build(bodyDef, true)); optimizer.Optimize(context, ilb); } catch (Exception exception) { if (CouldNotDecompileMethod != null) CouldNotDecompileMethod(bodyDef.FullName, exception); FunctionCache.CreateNull(methodInfo, method, identifier); pr.OnFinished(); return null; } var allVariables = ilb.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); foreach (var v in allVariables) { if (ILBlockTranslator.IsIgnoredType(v.Type)) { FunctionCache.CreateNull(methodInfo, method, identifier); pr.OnFinished(); return null; } } NameVariables.AssignNamesToVariables(context, decompiler.Parameters, allVariables, ilb); var translator = new ILBlockTranslator( this, context, method, methodDef, ilb, decompiler.Parameters, allVariables ); var body = translator.Translate(); if (body == null) { FunctionCache.CreateNull(methodInfo, method, identifier); pr.OnFinished(); return null; } var parameters = from p in translator.ParameterNames select translator.Variables[p]; if (method.HasGenericParameters) { var type = new TypeReference("System", "Type", context.CurrentModule.TypeSystem.Object.Module, context.CurrentModule.TypeSystem.Object.Scope); parameters = (from gp in method.GenericParameters select new JSVariable(gp.Name, type, method)).Concat(parameters); } function = FunctionCache.Create( methodInfo, methodDef, method, identifier, translator, parameters, body ); pr.OnFinished(); return function; } finally { context.CurrentMethod = oldMethod; } }
Dictionary <int, Action <XamlContext, XElement> > ExtractConnectionId(XamlContext ctx, MethodDef method) { var context = new DecompilerContext(method.Module) { CurrentType = method.DeclaringType, CurrentMethod = method, CancellationToken = ctx.CancellationToken }; var body = new ILBlock(new ILAstBuilder().Build(method, true, context)); new ILAstOptimizer().Optimize(context, body); var sw = body.GetSelfAndChildrenRecursive <ILSwitch>().FirstOrDefault(); if (sw == null) { return(null); } var connIds = new Dictionary <int, Action <XamlContext, XElement> >(); foreach (var cas in sw.CaseBlocks) { if (cas.Values == null) { continue; } Action <XamlContext, XElement> cb = null; foreach (var node in cas.Body) { var expr = node as ILExpression; if (expr == null) { continue; } switch (expr.Code) { case ILCode.Stfld: cb += new FieldAssignment { FieldName = ((IField)expr.Operand).Name }.Callback; break; case ILCode.Call: case ILCode.Callvirt: var operand = (IMethod)expr.Operand; if (operand.Name == "AddHandler" && operand.DeclaringType.FullName == "System.Windows.UIElement") { // Attached event var re = expr.Arguments[1]; var ctor = expr.Arguments[2]; var reField = re.Operand as IField; if (re.Code != ILCode.Ldsfld || ctor.Code != ILCode.Newobj || ctor.Arguments.Count != 2 || ctor.Arguments[1].Code != ILCode.Ldftn) { cb += new Error { Msg = string.Format(dnSpy_BamlDecompiler_Resources.Error_AttachedEvent, reField.Name) }.Callback; break; } var handler = (IMethod)ctor.Arguments[1].Operand; string evName = reField.Name; if (evName.EndsWith("Event")) { evName = evName.Substring(0, evName.Length - 5); } cb += new EventAttachment { AttachedType = reField.DeclaringType.ResolveTypeDefThrow(), EventName = evName, MethodName = handler.Name }.Callback; } else { // CLR event var add = operand.ResolveMethodDefThrow(); var ev = add.DeclaringType.Events.FirstOrDefault(e => e.AddMethod == add); var ctor = expr.Arguments[1]; if (ev == null || ctor.Code != ILCode.Newobj || ctor.Arguments.Count != 2 || ctor.Arguments[1].Code != ILCode.Ldftn) { cb += new Error { Msg = string.Format(dnSpy_BamlDecompiler_Resources.Error_AttachedEvent, add.Name) }.Callback; break; } var handler = (IMethod)ctor.Arguments[1].Operand; cb += new EventAttachment { EventName = ev.Name, MethodName = handler.Name }.Callback; } break; } } if (cb != null) { foreach (var id in cas.Values) { connIds[id] = cb; } } } return(connIds.Count == 0 ? null : connIds); }
BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters, out MemberMapping mm) { if (methodDef.Body == null) { mm = null; return null; } context.CancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true, context); context.CancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(context, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); var localVariables = ilMethod.GetSelfAndChildrenRecursive<ILExpression>().Select(e => e.Operand as ILVariable) .Where(v => v != null && !v.IsParameter).Distinct(); Debug.Assert(context.CurrentMethod == methodDef); NameVariables.AssignNamesToVariables(context, astBuilder.Parameters, localVariables, ilMethod); if (parameters != null) { foreach (var pair in (from p in parameters join v in astBuilder.Parameters on p.Annotation<Parameter>() equals v.OriginalParameter select new { p, v.Name })) { pair.p.NameToken = Identifier.Create(pair.Name).WithAnnotation(TextTokenType.Parameter); } } context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments Statement insertionPoint = astBlock.Statements.FirstOrDefault(); foreach (ILVariable v in localVariablesToDefine) { AstType type; if (v.Type.ContainsAnonymousType()) type = new SimpleType("var").WithAnnotation(TextTokenType.Keyword); else type = AstBuilder.ConvertType(v.Type); var newVarDecl = new VariableDeclarationStatement(v.IsParameter ? TextTokenType.Parameter : TextTokenType.Local, type, v.Name); newVarDecl.Variables.Single().AddAnnotation(v); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } mm = new MemberMapping(methodDef) { LocalVariables = localVariables.ToList() }; return astBlock; }
/// <summary> /// Clears the type inference data on the method. /// </summary> public static void Reset(ILBlock method) { foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { expr.InferredType = null; expr.ExpectedType = null; ILVariable v = expr.Operand as ILVariable; if (v != null && v.IsGenerated) v.Type = null; } }
string GenerateNameForVariable(ILVariable variable, ILBlock methodBody) { string proposedName = null; if (variable.Type == context.CurrentType.Module.TypeSystem.Int32) { // test whether the variable might be a loop counter bool isLoopCounter = false; foreach (ILWhileLoop loop in methodBody.GetSelfAndChildrenRecursive<ILWhileLoop>()) { ILExpression expr = loop.Condition; while (expr != null && expr.Code == ILCode.LogicNot) expr = expr.Arguments[0]; if (expr != null) { switch (expr.Code) { case ILCode.Clt: case ILCode.Clt_Un: case ILCode.Cgt: case ILCode.Cgt_Un: case ILCode.Cle: case ILCode.Cle_Un: case ILCode.Cge: case ILCode.Cge_Un: ILVariable loadVar; if (expr.Arguments[0].Match(ILCode.Ldloc, out loadVar) && loadVar == variable) { isLoopCounter = true; } break; } } } if (isLoopCounter) { // For loop variables, use i,j,k,l,m,n for (char c = 'i'; c <= maxLoopVariableName; c++) { if (!typeNames.ContainsKey(c.ToString())) { proposedName = c.ToString(); break; } } } } if (string.IsNullOrEmpty(proposedName)) { var proposedNameForStores = (from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>() where expr.Code == ILCode.Stloc && expr.Operand == variable select GetNameFromExpression(expr.Arguments.Single()) ).Except(fieldNamesInCurrentType).ToList(); if (proposedNameForStores.Count == 1) { proposedName = proposedNameForStores[0]; } } if (string.IsNullOrEmpty(proposedName)) { var proposedNameForLoads = (from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>() from i in Enumerable.Range(0, expr.Arguments.Count) let arg = expr.Arguments[i] where arg.Code == ILCode.Ldloc && arg.Operand == variable select GetNameForArgument(expr, i) ).Except(fieldNamesInCurrentType).ToList(); if (proposedNameForLoads.Count == 1) { proposedName = proposedNameForLoads[0]; } } if (string.IsNullOrEmpty(proposedName)) { proposedName = GetNameByType(variable.Type); } // remove any numbers from the proposed name int number; proposedName = SplitName(proposedName, out number); if (!typeNames.ContainsKey(proposedName)) { typeNames.Add(proposedName, 0); } int count = ++typeNames[proposedName]; if (count > 1) { return proposedName + count.ToString(); } else { return proposedName; } }