From eda207369c972116fc20f373b371cb7934442f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 20 Feb 2011 09:23:46 +0000 Subject: [PATCH 1/9] Slight improvement in code order --- ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index fb1c3db78..abfb0826e 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -225,10 +225,15 @@ namespace Decompiler.ControlFlow { List result = new List(); - Queue agenda = new Queue(); - agenda.Enqueue(entryNode); - while(agenda.Count > 0) { - ControlFlowNode node = agenda.Dequeue(); + HashSet agenda = new HashSet(); + agenda.Add(entryNode); + while(agenda.Any()) { + ControlFlowNode node = agenda.First(); + // Attempt for a good order + while(agenda.Contains(node.ImmediateDominator)) { + node = node.ImmediateDominator; + } + agenda.Remove(node); // Find a block that represents a simple condition if (nodes.Contains(node)) { @@ -346,7 +351,7 @@ namespace Decompiler.ControlFlow // Using the dominator tree should ensure we find the the widest loop first foreach(var child in node.DominatorTreeChildren) { - agenda.Enqueue(child); + agenda.Add(child); } } From adfc7fc3e4c7ef7a3f920a8d2e799a26b174ba19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 20 Feb 2011 10:26:19 +0000 Subject: [PATCH 2/9] Handle unreachable code --- .../Ast/AstMethodBodyBuilder.cs | 2 ++ ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 27 +++++++++++++++++-- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 11 ++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 6ebcfae52..3dfc1dcc4 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -160,6 +160,8 @@ namespace Decompiler yield return tryCatchStmt; } else if (node is ILBlock) { yield return TransformBlock((ILBlock)node); + } else if (node is ILComment) { + yield return new CommentStatement(((ILComment)node).Text).WithAnnotation(((ILComment)node).ILRanges); } else { throw new Exception("Unknown node type"); } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 5dae13326..e361b3202 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -57,7 +57,17 @@ namespace Decompiler public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.AppendFormat("{0}:{1} {2} {3}", this.Name, this.Label != null ? " *" : "", this.Code.GetName(), this.Operand); + sb.AppendFormat("{0}:{1} {2} ", this.Name, this.Label != null ? " *" : "", this.Code.GetName()); + if (this.Operand is ILLabel) { + sb.Append(((ILLabel)this.Operand).Name); + } else if (this.Operand is ILLabel[]) { + foreach(ILLabel label in (ILLabel[])this.Operand) { + sb.Append(label.Name); + sb.Append(" "); + } + } else { + sb.Append(this.Operand.ToString()); + } if (this.StackBefore != null) { sb.Append(" StackBefore = {"); bool first = true; @@ -234,6 +244,9 @@ namespace Decompiler // Genertate temporary variables to replace stack foreach(ByteCode byteCode in body) { + if (byteCode.StackBefore == null) + continue; + int argIdx = 0; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { @@ -395,8 +408,18 @@ namespace Decompiler // Convert stack-based IL code to ILAst tree foreach(ByteCode byteCode in body) { + ILRange ilRange = new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset }; + + if (byteCode.StackBefore == null) { + ast.Add(new ILComment() { + Text = "Unreachable code: " + byteCode.Code.GetName(), + ILRanges = new List(new[] { ilRange }) + }); + continue; + } + ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand); - expr.ILRanges.Add(new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset }); + expr.ILRanges.Add(ilRange); // Label for this instruction if (byteCode.Label != null) { diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 143548b6d..a7c8e297b 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -83,6 +83,17 @@ namespace Decompiler } } + public class ILComment: ILNode + { + public string Text; + public List ILRanges { get; set; } + + public override void WriteTo(ITextOutput output) + { + output.WriteLine("// " + this.Text); + } + } + public class ILTryCatchBlock: ILNode { public class CatchBlock: ILBlock From 5c8f11eaab8693998ee9ad3eaddac24b115dbf7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 20 Feb 2011 10:46:23 +0000 Subject: [PATCH 3/9] Minor fix in variable naming (Unable to cast object of type 'Decompiler.ILVariable' to type 'Mono.Cecil.ParameterDefinition') --- ICSharpCode.Decompiler/Ast/NameVariables.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs index 8797b43f1..febbb8add 100644 --- a/ICSharpCode.Decompiler/Ast/NameVariables.cs +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -97,7 +97,7 @@ namespace Decompiler switch (expr.Code) { case ILCode.Ldfld: // Use the field name only if it's not a field on this (avoid confusion between local variables and fields) - if (!(expr.Arguments[0].Code == ILCode.Ldloc && ((ParameterDefinition)expr.Arguments[0].Operand).Index < 0)) + if (!(expr.Arguments[0].Code == ILCode.Ldarg && ((ParameterDefinition)expr.Arguments[0].Operand).Index < 0)) return ((FieldReference)expr.Operand).Name; break; case ILCode.Ldsfld: From 377ec99c437c700228dcd95de80cc08326eb0c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 20 Feb 2011 11:18:47 +0000 Subject: [PATCH 4/9] Show how long decompilation of whole assembly took. --- ILSpy/TextView/DecompilerTextView.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 62bc54fb8..d8a883bcc 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -419,6 +419,8 @@ namespace ICSharpCode.ILSpy.TextView Thread thread = new Thread(new ThreadStart( delegate { try { + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); using (StreamWriter w = new StreamWriter(fileName)) { try { DecompileNodes(context, new PlainTextOutput(w)); @@ -428,8 +430,9 @@ namespace ICSharpCode.ILSpy.TextView throw; } } + stopwatch.Stop(); AvalonEditTextOutput output = new AvalonEditTextOutput(); - output.WriteLine("Decompilation complete."); + output.WriteLine("Decompilation complete in " + stopwatch.Elapsed.TotalSeconds.ToString("F1") + " seconds."); output.WriteLine(); output.AddButton(null, "Open Explorer", delegate { Process.Start("explorer", "/select,\"" + fileName + "\""); }); output.WriteLine(); From a52f198549c1a7eb861a2e155d52597fcf5581ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sun, 20 Feb 2011 11:19:20 +0000 Subject: [PATCH 5/9] Do not use NotImplementedException so much to speed up debug builds --- .../Ast/AstMethodBodyBuilder.cs | 109 ++++++++++-------- 1 file changed, 58 insertions(+), 51 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 3dfc1dcc4..ddc9e8ca0 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -305,8 +305,6 @@ namespace Decompiler AstNode TransformByteCode_Internal(ILExpression byteCode, List args) { - // throw new NotImplementedException(); - ILCode opCode = byteCode.Code; object operand = byteCode.Operand; AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); @@ -369,7 +367,7 @@ namespace Decompiler case Code.Ldelem_Ref: return arg1.Indexer(arg2); case Code.Ldelem_Any: - throw new NotImplementedException(); + return InlineAssembly(byteCode, args); case Code.Ldelema: return MakeRef(arg1.Indexer(arg2)); @@ -383,7 +381,7 @@ namespace Decompiler case Code.Stelem_Ref: return new Ast.AssignmentExpression(arg1.Indexer(arg2), arg3); case Code.Stelem_Any: - throw new NotImplementedException(); + return InlineAssembly(byteCode, args); #endregion #region Branching case Code.Br: return new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name); @@ -445,30 +443,30 @@ namespace Decompiler case Code.Conv_Ovf_U8_Un: return arg1.CastTo(typeof(UInt64)); #endregion #region Indirect - case Code.Ldind_I: throw new NotImplementedException(); - case Code.Ldind_I1: throw new NotImplementedException(); - case Code.Ldind_I2: throw new NotImplementedException(); - case Code.Ldind_I4: throw new NotImplementedException(); - case Code.Ldind_I8: throw new NotImplementedException(); - case Code.Ldind_U1: throw new NotImplementedException(); - case Code.Ldind_U2: throw new NotImplementedException(); - case Code.Ldind_U4: throw new NotImplementedException(); - case Code.Ldind_R4: throw new NotImplementedException(); - case Code.Ldind_R8: throw new NotImplementedException(); - case Code.Ldind_Ref: throw new NotImplementedException(); + case Code.Ldind_I: return InlineAssembly(byteCode, args); + case Code.Ldind_I1: return InlineAssembly(byteCode, args); + case Code.Ldind_I2: return InlineAssembly(byteCode, args); + case Code.Ldind_I4: return InlineAssembly(byteCode, args); + case Code.Ldind_I8: return InlineAssembly(byteCode, args); + case Code.Ldind_U1: return InlineAssembly(byteCode, args); + case Code.Ldind_U2: return InlineAssembly(byteCode, args); + case Code.Ldind_U4: return InlineAssembly(byteCode, args); + case Code.Ldind_R4: return InlineAssembly(byteCode, args); + case Code.Ldind_R8: return InlineAssembly(byteCode, args); + case Code.Ldind_Ref: return InlineAssembly(byteCode, args); - case Code.Stind_I: throw new NotImplementedException(); - case Code.Stind_I1: throw new NotImplementedException(); - case Code.Stind_I2: throw new NotImplementedException(); - case Code.Stind_I4: throw new NotImplementedException(); - case Code.Stind_I8: throw new NotImplementedException(); - case Code.Stind_R4: throw new NotImplementedException(); - case Code.Stind_R8: throw new NotImplementedException(); - case Code.Stind_Ref: throw new NotImplementedException(); + case Code.Stind_I: return InlineAssembly(byteCode, args); + case Code.Stind_I1: return InlineAssembly(byteCode, args); + case Code.Stind_I2: return InlineAssembly(byteCode, args); + case Code.Stind_I4: return InlineAssembly(byteCode, args); + case Code.Stind_I8: return InlineAssembly(byteCode, args); + case Code.Stind_R4: return InlineAssembly(byteCode, args); + case Code.Stind_R8: return InlineAssembly(byteCode, args); + case Code.Stind_Ref: return InlineAssembly(byteCode, args); #endregion - case Code.Arglist: throw new NotImplementedException(); - case Code.Box: throw new NotImplementedException(); - case Code.Break: throw new NotImplementedException(); + case Code.Arglist: return InlineAssembly(byteCode, args); + case Code.Box: return InlineAssembly(byteCode, args); + case Code.Break: return InlineAssembly(byteCode, args); case Code.Call: return TransformCall(false, operand, methodDef, args); case Code.Callvirt: @@ -492,19 +490,19 @@ namespace Decompiler .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); } - case Code.Calli: throw new NotImplementedException(); + case Code.Calli: return InlineAssembly(byteCode, args); case Code.Castclass: return arg1.CastTo(operandAsTypeRef); - case Code.Ckfinite: throw new NotImplementedException(); - case Code.Constrained: throw new NotImplementedException(); - case Code.Cpblk: throw new NotImplementedException(); - case Code.Cpobj: throw new NotImplementedException(); + case Code.Ckfinite: return InlineAssembly(byteCode, args); + case Code.Constrained: return InlineAssembly(byteCode, args); + case Code.Cpblk: return InlineAssembly(byteCode, args); + case Code.Cpobj: return InlineAssembly(byteCode, args); case Code.Dup: return arg1; - case Code.Endfilter: throw new NotImplementedException(); + case Code.Endfilter: return InlineAssembly(byteCode, args); case Code.Endfinally: return null; - case Code.Initblk: throw new NotImplementedException(); - case Code.Initobj: throw new NotImplementedException(); + case Code.Initblk: return InlineAssembly(byteCode, args); + case Code.Initobj: return InlineAssembly(byteCode, args); case Code.Isinst: return arg1.CastAs(AstBuilder.ConvertType((Cecil.TypeReference)operand)); - case Code.Jmp: throw new NotImplementedException(); + case Code.Jmp: return InlineAssembly(byteCode, args); case Code.Ldarg: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return new Ast.ThisReferenceExpression(); @@ -559,17 +557,17 @@ namespace Decompiler case Code.Ldloca: return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name)); case Code.Ldnull: return new Ast.NullReferenceExpression(); - case Code.Ldobj: throw new NotImplementedException(); + case Code.Ldobj: return InlineAssembly(byteCode, args); case Code.Ldstr: return new Ast.PrimitiveExpression(operand); case Code.Ldtoken: if (operand is Cecil.TypeReference) { return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle"); } else { - throw new NotImplementedException(); + return InlineAssembly(byteCode, args); } case Code.Leave: return null; - case Code.Localloc: throw new NotImplementedException(); - case Code.Mkrefany: throw new NotImplementedException(); + case Code.Localloc: return InlineAssembly(byteCode, args); + case Code.Mkrefany: return InlineAssembly(byteCode, args); case Code.Newobj: { Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; @@ -586,12 +584,12 @@ namespace Decompiler oce.Arguments.AddRange(args); return oce.WithAnnotation(operand); } - case Code.No: throw new NotImplementedException(); + case Code.No: return InlineAssembly(byteCode, args); case Code.Nop: return null; case Code.Pop: return arg1; - case Code.Readonly: throw new NotImplementedException(); - case Code.Refanytype: throw new NotImplementedException(); - case Code.Refanyval: throw new NotImplementedException(); + case Code.Readonly: return InlineAssembly(byteCode, args); + case Code.Refanytype: return InlineAssembly(byteCode, args); + case Code.Refanyval: return InlineAssembly(byteCode, args); case Code.Ret: { if (methodDef.ReturnType.FullName != "System.Void") { arg1 = Convert(arg1, methodDef.ReturnType); @@ -602,7 +600,7 @@ namespace Decompiler } case Code.Rethrow: return new Ast.ThrowStatement(); case Code.Sizeof: return new Ast.SizeOfExpression { Type = AstBuilder.ConvertType(operand as TypeReference) }; - case Code.Starg: throw new NotImplementedException(); + case Code.Starg: return InlineAssembly(byteCode, args); case Code.Stloc: { ILVariable locVar = (ILVariable)operand; if (!definedLocalVars.Contains(locVar)) { @@ -615,14 +613,14 @@ namespace Decompiler return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name), arg1); } } - case Code.Stobj: throw new NotImplementedException(); - case Code.Switch: throw new NotImplementedException(); - case Code.Tail: throw new NotImplementedException(); + case Code.Stobj: return InlineAssembly(byteCode, args); + case Code.Switch: return InlineAssembly(byteCode, args); + case Code.Tail: return InlineAssembly(byteCode, args); case Code.Throw: return new Ast.ThrowStatement { Expression = arg1 }; - case Code.Unaligned: throw new NotImplementedException(); - case Code.Unbox: throw new NotImplementedException(); - case Code.Unbox_Any: throw new NotImplementedException(); - case Code.Volatile: throw new NotImplementedException(); + case Code.Unaligned: return InlineAssembly(byteCode, args); + case Code.Unbox: return InlineAssembly(byteCode, args); + case Code.Unbox_Any: return InlineAssembly(byteCode, args); + case Code.Volatile: return InlineAssembly(byteCode, args); default: throw new Exception("Unknown OpCode: " + opCode); } } @@ -692,6 +690,15 @@ namespace Decompiler return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod); } + static Expression InlineAssembly(ILExpression byteCode, List args) + { + // Output the operand of the unknown IL code as well + if (byteCode.Operand != null) { + args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); + } + return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); + } + static IEnumerable ConvertTypeArguments(MethodReference cecilMethod) { GenericInstanceMethod g = cecilMethod as GenericInstanceMethod; From 8dadc50191717f0a9b71f266c61448112456512e Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 20 Feb 2011 13:09:57 +0100 Subject: [PATCH 6/9] Add some improvements to pattern matching. --- .../Ast/AstMethodBodyBuilder.cs | 2 +- .../Ast/Transforms/PushNegation.cs | 2 +- .../Ast/Transforms/UsingStatementTransform.cs | 38 +++++++++----- .../Ast/PatternMatching/Backreference.cs | 2 +- .../CSharp/Ast/PatternMatching/Choice.cs | 18 +++++-- .../CSharp/Ast/PatternMatching/Match.cs | 28 +++++++--- .../CSharp/Ast/PatternMatching/Pattern.cs | 5 ++ .../CSharp/Ast/PatternMatching/Placeholder.cs | 52 +++++++++---------- 8 files changed, 96 insertions(+), 51 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index ddc9e8ca0..203a68a72 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -278,7 +278,7 @@ namespace Decompiler } } - string FormatByteCodeOperand(object operand) + static string FormatByteCodeOperand(object operand) { if (operand == null) { return string.Empty; diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs index 1bc4ad36b..0a9d943ee 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs @@ -114,7 +114,7 @@ namespace Decompiler.Transforms negate = true; } if (m != null) { - Expression expr = ((Expression)m["expr"].Single()).Detach().IsType((AstType)m["type"].Single().Detach()); + Expression expr = m.Get("expr").Single().Detach().IsType(m.Get("type").Single().Detach()); if (negate) expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr); binaryOperatorExpression.ReplaceWith(expr); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/UsingStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/UsingStatementTransform.cs index 453c7a84a..780a2ed18 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/UsingStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/UsingStatementTransform.cs @@ -6,6 +6,7 @@ using System.Linq; using Decompiler.Transforms; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.PatternMatching; +using Mono.Cecil; namespace Decompiler.Transforms { @@ -29,18 +30,25 @@ namespace Decompiler.Transforms TryBlock = new AnyNode("body").ToBlock(), FinallyBlock = new BlockStatement { Statements = { - new IfElseStatement { - Condition = new BinaryOperatorExpression( - new NamedNode("ident", new IdentifierExpression()).ToExpression(), - BinaryOperatorType.InEquality, - new NullReferenceExpression() - ), - TrueStatement = new BlockStatement { - Statements = { - new ExpressionStatement(new Backreference("ident").ToExpression().Invoke("Dispose")) + new Choice { + { "valueType", + new ExpressionStatement(new NamedNode("ident", new IdentifierExpression()).ToExpression().Invoke("Dispose")) + }, + { "referenceType", + new IfElseStatement { + Condition = new BinaryOperatorExpression( + new NamedNode("ident", new IdentifierExpression()).ToExpression(), + BinaryOperatorType.InEquality, + new NullReferenceExpression() + ), + TrueStatement = new BlockStatement { + Statements = { + new ExpressionStatement(new Backreference("ident").ToExpression().Invoke("Dispose")) + } + } } } - } + }.ToStatement() } } }; @@ -52,8 +60,14 @@ namespace Decompiler.Transforms if (m1 == null) continue; Match m2 = usingTryCatchPattern.Match(node.NextSibling); if (m2 == null) continue; - if (((VariableInitializer)m1["variable"].Single()).Name == ((IdentifierExpression)m2["ident"].Single()).Identifier) { - BlockStatement body = (BlockStatement)m2["body"].Single(); + if (m1.Get("variable").Single().Name == m2.Get("ident").Single().Identifier) { + if (m2.Has("valueType")) { + // if there's no if(x!=null), then it must be a value type + TypeReference tr = m1.Get("type").Single().Annotation(); + if (tr == null || !tr.IsValueType) + continue; + } + BlockStatement body = m2.Get("body").Single(); body.Remove(); node.NextSibling.Remove(); node.ReplaceWith( diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs index c5573f01a..db3e3eceb 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Backreference.cs @@ -22,7 +22,7 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching protected internal override bool DoMatch(AstNode other, Match match) { - return match[referencedGroupName].Last().Match(other) != null; + return match.Get(referencedGroupName).Last().Match(other) != null; } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs index ba0954884..6435cc324 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Choice.cs @@ -2,20 +2,25 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections; namespace ICSharpCode.NRefactory.CSharp.PatternMatching { /// /// Matches one of several alternatives. /// - public class Choice : Pattern + public class Choice : Pattern, IEnumerable { public static readonly Role AlternativeRole = new Role("Alternative", AstNode.Null); - public Choice(params AstNode[] alternatives) + public void Add(string name, AstNode alternative) { - foreach (AstNode node in alternatives) - AddChild(node, AlternativeRole); + AddChild(new NamedNode(name, alternative), AlternativeRole); + } + + public void Add(AstNode alternative) + { + AddChild(alternative, AlternativeRole); } protected internal override bool DoMatch(AstNode other, Match match) @@ -29,5 +34,10 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching } return false; } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetChildrenByRole(AlternativeRole).GetEnumerator(); + } } } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Match.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Match.cs index 4a44dc36f..9b1ff905f 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Match.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Match.cs @@ -24,13 +24,29 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching results.RemoveRange(checkPoint, results.Count - checkPoint); } - public IEnumerable this[string groupName] { - get { - foreach (var pair in results) { - if (pair.Key == groupName) - yield return pair.Value; - } + public IEnumerable Get(string groupName) + { + foreach (var pair in results) { + if (pair.Key == groupName) + yield return pair.Value; + } + } + + public IEnumerable Get(string groupName) where T : AstNode + { + foreach (var pair in results) { + if (pair.Key == groupName) + yield return (T)pair.Value; + } + } + + public bool Has(string groupName) + { + foreach (var pair in results) { + if (pair.Key == groupName) + return true; } + return false; } public void Add(string groupName, AstNode node) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs index 794806a86..2e08092a9 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Pattern.cs @@ -29,6 +29,11 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching return new ExpressionPlaceholder(this); } + public Statement ToStatement() + { + return new StatementPlaceholder(this); + } + public BlockStatement ToBlock() { return new BlockStatementPlaceholder(this); diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs index 16ca73cef..f84faaa39 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/PatternMatching/Placeholder.cs @@ -7,113 +7,113 @@ namespace ICSharpCode.NRefactory.CSharp.PatternMatching { sealed class TypePlaceholder : AstType { - public static readonly Role PatternRole = new Role("Pattern"); + public static readonly Role ChildRole = new Role("Child", AstNode.Null); - public TypePlaceholder(Pattern pattern) + public TypePlaceholder(AstNode child) { - AddChild(pattern, TypePlaceholder.PatternRole); + AddChild(child, TypePlaceholder.ChildRole); } public override NodeType NodeType { - get { return NodeType.Pattern; } + get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return default(S); + return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); } protected internal override bool DoMatch(AstNode other, Match match) { - return GetChildByRole(TypePlaceholder.PatternRole).DoMatch(other, match); + return GetChildByRole(TypePlaceholder.ChildRole).DoMatch(other, match); } } sealed class ExpressionPlaceholder : Expression { - public ExpressionPlaceholder(Pattern pattern) + public ExpressionPlaceholder(AstNode child) { - AddChild(pattern, TypePlaceholder.PatternRole); + AddChild(child, TypePlaceholder.ChildRole); } public override NodeType NodeType { - get { return NodeType.Pattern; } + get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return default(S); + return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); } protected internal override bool DoMatch(AstNode other, Match match) { - return GetChildByRole(TypePlaceholder.PatternRole).DoMatch(other, match); + return GetChildByRole(TypePlaceholder.ChildRole).DoMatch(other, match); } } sealed class StatementPlaceholder : Statement { - public StatementPlaceholder(Pattern pattern) + public StatementPlaceholder(AstNode child) { - AddChild(pattern, TypePlaceholder.PatternRole); + AddChild(child, TypePlaceholder.ChildRole); } public override NodeType NodeType { - get { return NodeType.Pattern; } + get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return default(S); + return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); } protected internal override bool DoMatch(AstNode other, Match match) { - return GetChildByRole(TypePlaceholder.PatternRole).DoMatch(other, match); + return GetChildByRole(TypePlaceholder.ChildRole).DoMatch(other, match); } } sealed class BlockStatementPlaceholder : BlockStatement { - public BlockStatementPlaceholder(Pattern pattern) + public BlockStatementPlaceholder(AstNode child) { - AddChild(pattern, TypePlaceholder.PatternRole); + AddChild(child, TypePlaceholder.ChildRole); } public override NodeType NodeType { - get { return NodeType.Pattern; } + get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return default(S); + return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); } protected internal override bool DoMatch(AstNode other, Match match) { - return GetChildByRole(TypePlaceholder.PatternRole).DoMatch(other, match); + return GetChildByRole(TypePlaceholder.ChildRole).DoMatch(other, match); } } sealed class VariablePlaceholder : VariableInitializer { - public VariablePlaceholder(Pattern pattern) + public VariablePlaceholder(AstNode child) { - AddChild(pattern, TypePlaceholder.PatternRole); + AddChild(child, TypePlaceholder.ChildRole); } public override NodeType NodeType { - get { return NodeType.Pattern; } + get { return GetChildByRole(TypePlaceholder.ChildRole).NodeType; } } public override S AcceptVisitor(IAstVisitor visitor, T data) { - return default(S); + return GetChildByRole(TypePlaceholder.ChildRole).AcceptVisitor(visitor, data); } protected internal override bool DoMatch(AstNode other, Match match) { - return GetChildByRole(TypePlaceholder.PatternRole).DoMatch(other, match); + return GetChildByRole(TypePlaceholder.ChildRole).DoMatch(other, match); } } } From 275f0f6d21c0c077a9ae46d7850a3c1dee9c84a0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 20 Feb 2011 15:21:53 +0100 Subject: [PATCH 7/9] Implemented "Save Assembly as C# Project" --- .../Ast/AstMethodBodyBuilder.cs | 26 +++ .../ICSharpCode.Decompiler.csproj | 1 + ICSharpCode.Decompiler/ITextOutput.cs | 1 + ICSharpCode.Decompiler/TextOutputWriter.cs | 55 ++++++ ILSpy/CSharpLanguage.cs | 173 +++++++++++++++++- ILSpy/DecompilationOptions.cs | 5 + ILSpy/Language.cs | 4 + ILSpy/MainWindow.xaml.cs | 2 +- ILSpy/TextView/DecompilerTextView.cs | 8 + ILSpy/TreeNodes/AssemblyTreeNode.cs | 33 ++++ ILSpy/TreeNodes/ILSpyTreeNode.cs | 2 +- ILSpy/TreeNodes/ResourceEntryNode.cs | 4 +- ILSpy/TreeNodes/ResourceListTreeNode.cs | 4 +- 13 files changed, 307 insertions(+), 11 deletions(-) create mode 100644 ICSharpCode.Decompiler/TextOutputWriter.cs diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 203a68a72..9351fd969 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -690,8 +691,33 @@ namespace Decompiler return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod); } + #if DEBUG + static readonly ConcurrentDictionary unhandledOpcodes = new ConcurrentDictionary(); + #endif + + [Conditional("DEBUG")] + public static void ClearUnhandledOpcodes() + { + #if DEBUG + unhandledOpcodes.Clear(); + #endif + } + + [Conditional("DEBUG")] + public static void PrintNumberOfUnhandledOpcodes() + { + #if DEBUG + foreach (var pair in unhandledOpcodes) { + Debug.WriteLine("AddMethodBodyBuilder unhandled opcode: {1}x {0}", pair.Key, pair.Value); + } + #endif + } + static Expression InlineAssembly(ILExpression byteCode, List args) { + #if DEBUG + unhandledOpcodes.AddOrUpdate(byteCode.Code, c => 1, (c, n) => n+1); + #endif // Output the operand of the unknown IL code as well if (byteCode.Operand != null) { args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 8a541de43..d96ec99e9 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -97,6 +97,7 @@ + diff --git a/ICSharpCode.Decompiler/ITextOutput.cs b/ICSharpCode.Decompiler/ITextOutput.cs index f862d5f0a..b4374d50b 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.IO; namespace ICSharpCode.Decompiler { diff --git a/ICSharpCode.Decompiler/TextOutputWriter.cs b/ICSharpCode.Decompiler/TextOutputWriter.cs new file mode 100644 index 000000000..964187a31 --- /dev/null +++ b/ICSharpCode.Decompiler/TextOutputWriter.cs @@ -0,0 +1,55 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// 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.IO; +using System.Text; + +namespace ICSharpCode.Decompiler +{ + public class TextOutputWriter : TextWriter + { + readonly ITextOutput output; + + public TextOutputWriter(ITextOutput output) + { + if (output == null) + throw new ArgumentNullException("output"); + this.output = output; + } + + public override Encoding Encoding { + get { return Encoding.UTF8; } + } + + public override void Write(char value) + { + output.Write(value); + } + + public override void Write(string value) + { + output.Write(value); + } + + public override void WriteLine() + { + output.WriteLine(); + } + } +} diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index a1eaf8d72..ca17d220c 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -21,6 +21,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; +using System.Threading.Tasks; +using System.Xml; using Decompiler; using Decompiler.Transforms; using ICSharpCode.Decompiler; @@ -70,6 +72,10 @@ namespace ICSharpCode.ILSpy get { return ".cs"; } } + public override string ProjectFileExtension { + get { return ".csproj"; } + } + public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { AstBuilder codeDomBuilder = CreateAstBuilder(options, method.DeclaringType); @@ -108,17 +114,174 @@ namespace ICSharpCode.ILSpy public override void DecompileAssembly(AssemblyDefinition assembly, string fileName, ITextOutput output, DecompilationOptions options) { if (options.FullDecompilation) { - foreach (TypeDefinition type in assembly.MainModule.Types) { - AstBuilder codeDomBuilder = CreateAstBuilder(options, type); - codeDomBuilder.AddType(type); - codeDomBuilder.GenerateCode(output, transformAbortCondition); - output.WriteLine(); + if (options.SaveAsProjectDirectory != null) { + var files = WriteFilesInProject(assembly, options); + WriteProjectFile(new TextOutputWriter(output), files, assembly.MainModule); + } else { + foreach (TypeDefinition type in assembly.MainModule.Types) { + AstBuilder codeDomBuilder = CreateAstBuilder(options, type); + codeDomBuilder.AddType(type); + codeDomBuilder.GenerateCode(output, transformAbortCondition); + output.WriteLine(); + } } } else { base.DecompileAssembly(assembly, fileName, output, options); } } + void WriteProjectFile(TextWriter writer, IEnumerable files, ModuleDefinition module) + { + const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; + string platformName; + switch (module.Architecture) { + case TargetArchitecture.I386: + if ((module.Attributes & ModuleAttributes.Required32Bit) == ModuleAttributes.Required32Bit) + platformName = "x86"; + else + platformName = "AnyCPU"; + break; + case TargetArchitecture.AMD64: + platformName = "x64"; + break; + case TargetArchitecture.IA64: + platformName = "Itanium"; + break; + default: + throw new NotSupportedException("Invalid value for TargetArchitecture"); + } + using (XmlTextWriter w = new XmlTextWriter(writer)) { + w.Formatting = Formatting.Indented; + w.WriteStartDocument(); + w.WriteStartElement("Project", ns); + w.WriteAttributeString("ToolsVersion", "4.0"); + w.WriteAttributeString("DefaultTargets", "Build"); + + w.WriteStartElement("PropertyGroup"); + w.WriteElementString("ProjectGuid", Guid.NewGuid().ToString().ToUpperInvariant()); + + w.WriteStartElement("Configuration"); + w.WriteAttributeString("Condition", " '$(Configuration)' == '' "); + w.WriteValue("Debug"); + w.WriteEndElement(); // + + w.WriteStartElement("Platform"); + w.WriteAttributeString("Condition", " '$(Platform)' == '' "); + w.WriteValue(platformName); + w.WriteEndElement(); // + + switch (module.Kind) { + case ModuleKind.Windows: + w.WriteElementString("OutputType", "WinExe"); + break; + case ModuleKind.Console: + w.WriteElementString("OutputType", "Exe"); + break; + default: + w.WriteElementString("OutputType", "Library"); + break; + } + + w.WriteElementString("AssemblyName", module.Assembly.Name.Name); + switch (module.Runtime) { + case TargetRuntime.Net_1_0: + w.WriteElementString("TargetFrameworkVersion", "v1.0"); + break; + case TargetRuntime.Net_1_1: + w.WriteElementString("TargetFrameworkVersion", "v1.1"); + break; + case TargetRuntime.Net_2_0: + w.WriteElementString("TargetFrameworkVersion", "v2.0"); + // TODO: Detect when .NET 3.0/3.5 is required + break; + default: + w.WriteElementString("TargetFrameworkVersion", "v4.0"); + // TODO: Detect TargetFrameworkProfile + break; + } + w.WriteElementString("WarningLevel", "4"); + + w.WriteEndElement(); // + + w.WriteStartElement("PropertyGroup"); // platform-specific + w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' "); + w.WriteElementString("PlatformTarget", platformName); + w.WriteEndElement(); // (platform-specific) + + w.WriteStartElement("PropertyGroup"); // Debug + w.WriteAttributeString("Condition", " '$(Configuration)' == 'Debug' "); + w.WriteElementString("OutputPath", "bin\\Debug\\"); + w.WriteElementString("DebugSymbols", "true"); + w.WriteElementString("DebugType", "full"); + w.WriteElementString("Optimize", "false"); + w.WriteEndElement(); // (Debug) + + w.WriteStartElement("PropertyGroup"); // Release + w.WriteAttributeString("Condition", " '$(Configuration)' == 'Release' "); + w.WriteElementString("OutputPath", "bin\\Release\\"); + w.WriteElementString("DebugSymbols", "true"); + w.WriteElementString("DebugType", "pdbonly"); + w.WriteElementString("Optimize", "true"); + w.WriteEndElement(); // (Release) + + + w.WriteStartElement("ItemGroup"); // References + foreach (AssemblyNameReference r in module.AssemblyReferences) { + if (r.Name != "mscorlib") { + w.WriteStartElement("Reference"); + w.WriteAttributeString("Include", r.Name); + // TODO: RequiredTargetFramework + w.WriteEndElement(); + } + } + w.WriteEndElement(); // (References) + + w.WriteStartElement("ItemGroup"); // Code + foreach (string file in files.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)) { + w.WriteStartElement("Compile"); + w.WriteAttributeString("Include", file); + w.WriteEndElement(); + } + w.WriteEndElement(); + + w.WriteStartElement("Import"); + w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); + w.WriteEndElement(); + + w.WriteEndDocument(); + } + } + + IEnumerable WriteFilesInProject(AssemblyDefinition assembly, DecompilationOptions options) + { + var files = assembly.MainModule.Types.Where(t => t.Name != "").GroupBy( + delegate (TypeDefinition type) { + string file = TextView.DecompilerTextView.CleanUpName(type.Name) + this.FileExtension; + if (string.IsNullOrEmpty(type.Namespace)) { + return file; + } else { + string dir = TextView.DecompilerTextView.CleanUpName(type.Namespace); + Directory.CreateDirectory(Path.Combine(options.SaveAsProjectDirectory, dir)); + return Path.Combine(dir, file); + } + }, StringComparer.OrdinalIgnoreCase).ToList(); + + AstMethodBodyBuilder.ClearUnhandledOpcodes(); + Parallel.ForEach( + files, + new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, + delegate (IGrouping file) { + using (StreamWriter w = new StreamWriter(Path.Combine(options.SaveAsProjectDirectory, file.Key))) { + AstBuilder codeDomBuilder = CreateAstBuilder(options, null); + foreach (TypeDefinition type in file) + codeDomBuilder.AddType(type); + codeDomBuilder.GenerateCode(new PlainTextOutput(w), transformAbortCondition); + } + }); + AstMethodBodyBuilder.PrintNumberOfUnhandledOpcodes(); + return files.Select(f => f.Key); + } + AstBuilder CreateAstBuilder(DecompilationOptions options, TypeDefinition currentType) { return new AstBuilder( diff --git a/ILSpy/DecompilationOptions.cs b/ILSpy/DecompilationOptions.cs index 120583be6..8dc953c6d 100644 --- a/ILSpy/DecompilationOptions.cs +++ b/ILSpy/DecompilationOptions.cs @@ -32,6 +32,11 @@ namespace ICSharpCode.ILSpy /// public bool FullDecompilation { get; set; } + /// + /// Gets/Sets the directory into which the project is saved. + /// + public string SaveAsProjectDirectory { get; set; } + /// /// Gets the cancellation token that is used to abort the decompiler. /// diff --git a/ILSpy/Language.cs b/ILSpy/Language.cs index 5a96521b0..3bce054af 100644 --- a/ILSpy/Language.cs +++ b/ILSpy/Language.cs @@ -40,6 +40,10 @@ namespace ICSharpCode.ILSpy /// public abstract string FileExtension { get; } + public virtual string ProjectFileExtension { + get { return null; } + } + /// /// Gets the syntax highlighting used for this language. /// diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 7a79cc166..b998739aa 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -392,7 +392,7 @@ namespace ICSharpCode.ILSpy { if (treeView.SelectedItems.Count == 1) { ILSpyTreeNode node = treeView.SelectedItem as ILSpyTreeNode; - if (node != null && node.Save()) + if (node != null && node.Save(decompilerTextView)) return; } decompilerTextView.SaveToDisk(sessionSettings.FilterSettings.Language, diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index d8a883bcc..7b628539e 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -384,6 +384,11 @@ namespace ICSharpCode.ILSpy.TextView } } + public void SaveToDisk(ILSpy.Language language, IEnumerable treeNodes, DecompilationOptions options, string fileName) + { + SaveToDisk(new DecompilationContext(language, treeNodes.ToArray(), options), fileName); + } + /// /// Starts the decompilation of the given nodes. /// The result will be saved to the given file name. @@ -458,6 +463,9 @@ namespace ICSharpCode.ILSpy.TextView internal static string CleanUpName(string text) { int pos = text.IndexOf(':'); + if (pos > 0) + text = text.Substring(0, pos); + pos = text.IndexOf('`'); if (pos > 0) text = text.Substring(0, pos); text = text.Trim(); diff --git a/ILSpy/TreeNodes/AssemblyTreeNode.cs b/ILSpy/TreeNodes/AssemblyTreeNode.cs index 7b9136717..72708fd20 100644 --- a/ILSpy/TreeNodes/AssemblyTreeNode.cs +++ b/ILSpy/TreeNodes/AssemblyTreeNode.cs @@ -27,7 +27,9 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.TextView; using ICSharpCode.TreeView; +using Microsoft.Win32; using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes @@ -201,5 +203,36 @@ namespace ICSharpCode.ILSpy.TreeNodes assembly.WaitUntilLoaded(); // necessary so that load errors are passed on to the caller language.DecompileAssembly(assembly.AssemblyDefinition, assembly.FileName, output, options); } + + public override bool Save(DecompilerTextView textView) + { + Language language = this.Language; + if (string.IsNullOrEmpty(language.ProjectFileExtension)) + return false; + SaveFileDialog dlg = new SaveFileDialog(); + dlg.FileName = DecompilerTextView.CleanUpName(assembly.ShortName); + dlg.Filter = language.Name + " project|*" + language.ProjectFileExtension + "|" + language.Name + " single file|*" + language.FileExtension + "|All files|*.*"; + if (dlg.ShowDialog() == true) { + DecompilationOptions options = new DecompilationOptions(); + options.FullDecompilation = true; + if (dlg.FilterIndex == 1) { + options.SaveAsProjectDirectory = Path.GetDirectoryName(dlg.FileName); + foreach (string entry in Directory.GetFileSystemEntries(options.SaveAsProjectDirectory)) { + if (!string.Equals(entry, dlg.FileName, StringComparison.OrdinalIgnoreCase)) { + var result = MessageBox.Show( + "The directory is not empty. File will be overwritten." + Environment.NewLine + + "Are you sure you want to continue?", + "Project Directory not empty", + MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); + if (result == MessageBoxResult.No) + return true; // don't save, but mark the Save operation as handled + break; + } + } + } + textView.SaveToDisk(language, new[]{this}, options, dlg.FileName); + } + return true; + } } } diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 44a9483d3..3aef3bfc5 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.TreeNodes /// This method is called on the main thread when only a single item is selected. /// If it returns false, normal decompilation is used to save the item. /// - public virtual bool Save() + public virtual bool Save(TextView.DecompilerTextView textView) { return false; } diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceEntryNode.cs index af1c01baf..a167a916d 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceEntryNode.cs @@ -75,7 +75,7 @@ namespace ICSharpCode.ILSpy.TreeNodes image.EndInit(); output.AddUIElement(() => new Image { Source = image }); output.WriteLine(); - output.AddButton(Images.Save, "Save", delegate { Save(); }); + output.AddButton(Images.Save, "Save", delegate { Save(null); }); } catch (Exception) { return false; } @@ -106,7 +106,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return true; } - public override bool Save() + public override bool Save(DecompilerTextView textView) { SaveFileDialog dlg = new SaveFileDialog(); dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index cc38111c5..f3d3ed5ca 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy.TreeNodes ISmartTextOutput smartOutput = output as ISmartTextOutput; if (smartOutput != null && r is EmbeddedResource) { - smartOutput.AddButton(Images.Save, "Save", delegate { Save(); }); + smartOutput.AddButton(Images.Save, "Save", delegate { Save(null); }); output.WriteLine(); } } @@ -132,7 +132,7 @@ namespace ICSharpCode.ILSpy.TreeNodes return false; } - public override bool Save() + public override bool Save(TextView.DecompilerTextView textView) { EmbeddedResource er = r as EmbeddedResource; if (er != null) { From 240c0ed6265a8292dda3de607e395569d1162f0c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 20 Feb 2011 15:24:35 +0100 Subject: [PATCH 8/9] OutputVisitor bugfix: show return type for custom events. --- .../CSharp/OutputVisitor/OutputVisitor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index 42f049978..19499ecc6 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -1689,6 +1689,7 @@ namespace ICSharpCode.NRefactory.CSharp WriteModifiers(eventDeclaration.ModifierTokens); WriteKeyword("event"); eventDeclaration.ReturnType.AcceptVisitor(this, data); + Space(); WriteCommaSeparatedList(eventDeclaration.Variables); Semicolon(); return EndNode(eventDeclaration); @@ -1700,6 +1701,8 @@ namespace ICSharpCode.NRefactory.CSharp WriteAttributes(customEventDeclaration.Attributes); WriteModifiers(customEventDeclaration.ModifierTokens); WriteKeyword("event"); + customEventDeclaration.ReturnType.AcceptVisitor(this, data); + Space(); WritePrivateImplementationType(customEventDeclaration.PrivateImplementationType); WriteIdentifier(customEventDeclaration.Name); OpenBrace(policy.EventBraceStyle); From 758f18c73f2667591014666e73ee139fe449c022 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 20 Feb 2011 15:36:17 +0100 Subject: [PATCH 9/9] Remove CFG debugging code - ILSpy doesn't use the CFG as shown in the graph. Closes #9. --- ILSpy/MainWindow.xaml.cs | 63 ---------------------------------------- 1 file changed, 63 deletions(-) diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index b998739aa..1c4b6263d 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -72,9 +72,6 @@ namespace ICSharpCode.ILSpy } sessionSettings.FilterSettings.PropertyChanged += filterSettings_PropertyChanged; - #if DEBUG - AddDebugItemsToToolbar(); - #endif this.Loaded += new RoutedEventHandler(MainWindow_Loaded); } @@ -254,66 +251,6 @@ namespace ICSharpCode.ILSpy } #endregion - #region Debugging CFG - #if DEBUG - void AddDebugItemsToToolbar() - { - toolBar.Items.Add(new Separator()); - - Button cfg = new Button() { Content = "CFG" }; - cfg.Click += new RoutedEventHandler(cfg_Click); - toolBar.Items.Add(cfg); - - Button ssa = new Button() { Content = "SSA" }; - ssa.Click += new RoutedEventHandler(ssa_Click); - toolBar.Items.Add(ssa); - - Button varGraph = new Button() { Content = "Var" }; - varGraph.Click += new RoutedEventHandler(varGraph_Click); - toolBar.Items.Add(varGraph); - } - - void cfg_Click(object sender, RoutedEventArgs e) - { - MethodTreeNode node = treeView.SelectedItem as MethodTreeNode; - if (node != null && node.MethodDefinition.HasBody) { - var cfg = ControlFlowGraphBuilder.Build(node.MethodDefinition.Body); - cfg.ComputeDominance(); - cfg.ComputeDominanceFrontier(); - ShowGraph(node.MethodDefinition.Name + "-cfg", cfg.ExportGraph()); - } - } - - void ssa_Click(object sender, RoutedEventArgs e) - { - MethodTreeNode node = treeView.SelectedItem as MethodTreeNode; - if (node != null && node.MethodDefinition.HasBody) { - node.MethodDefinition.Body.SimplifyMacros(); - ShowGraph(node.MethodDefinition.Name + "-ssa", SsaFormBuilder.Build(node.MethodDefinition).ExportBlockGraph()); - } - } - - void varGraph_Click(object sender, RoutedEventArgs e) - { - MethodTreeNode node = treeView.SelectedItem as MethodTreeNode; - if (node != null && node.MethodDefinition.HasBody) { - node.MethodDefinition.Body.SimplifyMacros(); - ShowGraph(node.MethodDefinition.Name + "-var", SsaFormBuilder.Build(node.MethodDefinition).ExportVariableGraph()); - } - } - - void ShowGraph(string name, GraphVizGraph graph) - { - foreach (char c in Path.GetInvalidFileNameChars()) - name = name.Replace(c, '-'); - string fileName = Path.Combine(Path.GetTempPath(), name); - graph.Save(fileName + ".gv"); - Process.Start("dot", "\"" + fileName + ".gv\" -Tpng -o \"" + fileName + ".png\"").WaitForExit(); - Process.Start(fileName + ".png"); - } - #endif - #endregion - #region Open/Refresh void OpenCommandExecuted(object sender, ExecutedRoutedEventArgs e) {