From 6085b60fc227a3bf5266ca36946372774ba8c286 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 17 Feb 2011 23:53:39 +0100 Subject: [PATCH 01/27] Add dumb testing framework. --- .../Tests/DelegateConstruction.cs | 41 +++++++ .../Tests/ICSharpCode.Decompiler.Tests.csproj | 66 ++++++++++++ ICSharpCode.Decompiler/Tests/Loops.cs | 30 ++++++ ICSharpCode.Decompiler/Tests/TestRunner.cs | 101 ++++++++++++++++++ ILSpy.sln | 10 ++ 5 files changed, 248 insertions(+) create mode 100644 ICSharpCode.Decompiler/Tests/DelegateConstruction.cs create mode 100644 ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj create mode 100644 ICSharpCode.Decompiler/Tests/Loops.cs create mode 100644 ICSharpCode.Decompiler/Tests/TestRunner.cs diff --git a/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs new file mode 100644 index 000000000..34657126e --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs @@ -0,0 +1,41 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; + +public static class DelegateConstruction +{ + public static void Test(this string a) + { + } + + public static Action ExtensionMethodUnbound() + { + return new Action(DelegateConstruction.Test); + } + + public static Action ExtensionMethodBound() + { + return new Action("abc".Test); + } + + public static Action ExtensionMethodBoundOnNull() + { + return new Action(((string)null).Test); + } + + public static object StaticMethod() + { + return new Func(DelegateConstruction.ExtensionMethodBound); + } + + public static object InstanceMethod() + { + return new Func("hello".ToUpper); + } + + public static object InstanceMethodOnNull() + { + return new Func(((string)null).ToUpper); + } +} diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj new file mode 100644 index 000000000..8d0896ef5 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -0,0 +1,66 @@ + + + + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22} + Debug + x86 + Exe + ICSharpCode.Decompiler.Tests + ICSharpCode.Decompiler.Tests + v4.0 + Properties + False + False + 4 + false + + + x86 + False + Auto + 4194304 + 4096 + + + ..\bin\Debug\ + true + Full + False + True + DEBUG;TRACE + + + ..\bin\Release\ + false + None + True + False + TRACE + + + + + 3.5 + + + + 3.5 + + + + + + + + + + {D68133BD-1E63-496E-9EDE-4FBDBF77B486} + Mono.Cecil + + + {984CC812-9470-4A13-AFF9-CC44068D666C} + ICSharpCode.Decompiler + + + + \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Tests/Loops.cs b/ICSharpCode.Decompiler/Tests/Loops.cs new file mode 100644 index 000000000..838a6e886 --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/Loops.cs @@ -0,0 +1,30 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; + +public class Loops +{ + public void ForEach(IEnumerable enumerable) + { + foreach (string text in enumerable) { + text.ToLower(); + } + } + + public void ForEachOverArray(string[] array) + { + foreach (string text in array) { + text.ToLower(); + } + } + + public void ForOverArray(string[] array) + { + for (int i = 0; i < array.Length; i++) { + array[i].ToLower(); + } + } +} + diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs new file mode 100644 index 000000000..9c3ba5c9d --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -0,0 +1,101 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Decompiler; +using Microsoft.CSharp; +using Mono.Cecil; + +namespace ICSharpCode.Decompiler.Tests +{ + public class TestRunner + { + public static void Main() + { + Test(@"..\..\Tests\DelegateConstruction.cs"); + + Console.ReadKey(); + } + + + + static void Test(string fileName) + { + string code = File.ReadAllText(fileName); + AssemblyDefinition assembly = Compile(code); + AstBuilder decompiler = new AstBuilder(); + decompiler.AddAssembly(assembly); + StringWriter output = new StringWriter(); + decompiler.GenerateCode(new PlainTextOutput(output)); + StringWriter diff = new StringWriter(); + if (!Compare(code, output.ToString(), diff)) { + throw new Exception("Test failure." + Environment.NewLine + diff.ToString()); + } + } + + static bool Compare(string input1, string input2, StringWriter diff) + { + bool ok = true; + int numberOfContinuousMistakes = 0; + StringReader r1 = new StringReader(input1); + StringReader r2 = new StringReader(input2); + string line1, line2; + while ((line1 = r1.ReadLine()) != null) { + string trimmed = line1.Trim(); + if (trimmed.Length == 0 || trimmed.StartsWith("//", StringComparison.Ordinal) || line1.StartsWith("using ", StringComparison.Ordinal)) { + diff.WriteLine(" " + line1); + continue; + } + line2 = r2.ReadLine(); + while (line2 != null && (line2.StartsWith("using ", StringComparison.Ordinal) || line2.Trim().Length == 0)) + line2 = r2.ReadLine(); + if (line2 == null) { + ok = false; + diff.WriteLine("-" + line1); + continue; + } + if (line1 != line2) { + ok = false; + if (numberOfContinuousMistakes++ > 5) + return false; + diff.WriteLine("-" + line1); + diff.WriteLine("+" + line2); + } else { + if (numberOfContinuousMistakes > 0) + numberOfContinuousMistakes--; + diff.WriteLine(" " + line1); + } + } + while ((line2 = r2.ReadLine()) != null) { + ok = false; + diff.WriteLine("+" + line1); + } + return ok; + } + + static AssemblyDefinition Compile(string code) + { + CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary {{ "CompilerVersion", "v4.0" }}); + CompilerParameters options = new CompilerParameters(); + options.ReferencedAssemblies.Add("System.Core.dll"); + CompilerResults results = provider.CompileAssemblyFromSource(options, code); + try { + if (results.Errors.Count > 0) { + StringBuilder b = new StringBuilder("Compiler error:"); + foreach (var error in results.Errors) { + b.AppendLine(error.ToString()); + } + throw new Exception(b.ToString()); + } + return AssemblyDefinition.ReadAssembly(results.PathToAssembly); + } finally { + File.Delete(results.PathToAssembly); + results.TempFiles.Delete(); + } + } + } +} diff --git a/ILSpy.sln b/ILSpy.sln index 449ef4139..409cbd730 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -14,6 +14,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "I EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj", "{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler.Tests", "ICSharpCode.Decompiler\Tests\ICSharpCode.Decompiler.Tests.csproj", "{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -70,5 +72,13 @@ Global {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|x86.ActiveCfg = Release|Any CPU {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.Build.0 = Release|Any CPU {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|x86.Build.0 = Debug|x86 + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|x86.ActiveCfg = Debug|x86 + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|Any CPU.Build.0 = Debug|x86 + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Debug|Any CPU.ActiveCfg = Debug|x86 + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Release|x86.Build.0 = Release|x86 + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Release|x86.ActiveCfg = Release|x86 + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Release|Any CPU.Build.0 = Release|x86 + {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}.Release|Any CPU.ActiveCfg = Release|x86 EndGlobalSection EndGlobal From f333ed1e58bccd5654e88b7009b72783d3c7787c Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 17 Feb 2011 23:55:33 +0100 Subject: [PATCH 02/27] When decompiling a type, don't show the empty constructor generated by the compiler. --- .../ConvertConstructorCallIntoInitializer.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs index 36f6d4eb5..caf6b4d91 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -38,5 +38,20 @@ namespace Decompiler.Transforms } return null; } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + base.VisitTypeDeclaration(typeDeclaration, data); + // Remove single empty constructor: + var ctors = typeDeclaration.Members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); + if (ctors.Length == 1 && ctors[0].Body.Children.Count() == 0 + && ctors[0].Initializer.ConstructorInitializerType == ConstructorInitializerType.Base + && ctors[0].Initializer.Arguments.Count() == 0 + && ctors[0].Modifiers == ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public)) + { + ctors[0].Remove(); + } + return null; + } } } From 016b54563f620ca71bc8d2a64c6f2983e80f08b7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 10:50:59 +0100 Subject: [PATCH 03/27] Add cancellation support to the decompiler. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 16 +++++---- .../Ast/AstMethodBodyBuilder.cs | 9 ++++- .../Ast/Transforms/DelegateConstruction.cs | 8 +++-- .../Ast/Transforms/TransformationPipeline.cs | 16 ++++----- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 36 ++++++------------- ILSpy/CSharpLanguage.cs | 22 +++++++----- 6 files changed, 54 insertions(+), 53 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index d3480ca42..0f2560437 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using ICSharpCode.Decompiler; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -12,6 +13,7 @@ namespace Decompiler { public class AstBuilder { + public CancellationToken CancellationToken { get; set; } CompilationUnit astCompileUnit = new CompilationUnit(); Dictionary astNamespaces = new Dictionary(); @@ -22,7 +24,7 @@ namespace Decompiler public void GenerateCode(ITextOutput output, Predicate> transformAbortCondition) { - Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition); + Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition, this.CancellationToken); astCompileUnit.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null); var outputFormatter = new TextOutputFormatter(output); @@ -412,7 +414,7 @@ namespace Decompiler astMethod.Parameters = MakeParameters(methodDef.Parameters); if (!methodDef.DeclaringType.IsInterface) { astMethod.Modifiers = ConvertModifiers(methodDef); - astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef); + astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, this.CancellationToken); } return astMethod; } @@ -426,7 +428,7 @@ namespace Decompiler astMethod.Modifiers &= ~Modifiers.VisibilityMask; } astMethod.Parameters = MakeParameters(methodDef.Parameters); - astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef); + astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, this.CancellationToken); return astMethod; } @@ -438,12 +440,12 @@ namespace Decompiler astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { astProp.Getter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod) + Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, this.CancellationToken) }; } if (propDef.SetMethod != null) { astProp.Setter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod) + Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, this.CancellationToken) }; } return astProp; @@ -457,12 +459,12 @@ namespace Decompiler astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); if (eventDef.AddMethod != null) { astEvent.AddAccessor = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod) + Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, this.CancellationToken) }; } if (eventDef.RemoveMethod != null) { astEvent.RemoveAccessor = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod) + Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, this.CancellationToken) }; } return astEvent; diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index b80aa09ac..6edf089b1 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using Ast = ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp; using Cecil = Mono.Cecil; @@ -15,11 +16,13 @@ namespace Decompiler public class AstMethodBodyBuilder { MethodDefinition methodDef; + CancellationToken cancellationToken; HashSet definedLocalVars = new HashSet(); - public static BlockStatement CreateMethodBody(MethodDefinition methodDef) + public static BlockStatement CreateMethodBody(MethodDefinition methodDef, CancellationToken cancellationToken) { AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); + builder.cancellationToken = cancellationToken; builder.methodDef = methodDef; if (Debugger.IsAttached) { return builder.CreateMethodBody(); @@ -55,12 +58,15 @@ namespace Decompiler { if (methodDef.Body == null) return null; + cancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true); + cancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(ilMethod); + cancellationToken.ThrowIfCancellationRequested(); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); Dictionary typeNames = new Dictionary(); @@ -102,6 +108,7 @@ namespace Decompiler // astBlock.Children.Add(astLocalVar); } + cancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments return astBlock; diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index c2a4f7245..da30a39c8 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -3,6 +3,8 @@ using System; using System.Linq; +using System.Threading; + using ICSharpCode.Decompiler; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -15,6 +17,8 @@ namespace Decompiler.Transforms /// public class DelegateConstruction : DepthFirstAstVisitor { + public CancellationToken CancellationToken { get; set; } + internal sealed class Annotation { /// @@ -107,8 +111,8 @@ namespace Decompiler.Transforms } // Decompile the anonymous method: - BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method); - TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction); + BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, this.CancellationToken); + TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, this.CancellationToken); body.AcceptVisitor(this, null); AnonymousMethodExpression ame = new AnonymousMethodExpression(); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index 8c2d5b235..e0cc0a415 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -2,31 +2,28 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Threading; using ICSharpCode.NRefactory.CSharp; namespace Decompiler.Transforms { public static class TransformationPipeline { - public static IAstVisitor[] CreatePipeline() + public static IAstVisitor[] CreatePipeline(CancellationToken cancellationToken) { return new IAstVisitor[] { - new DelegateConstruction(), + new DelegateConstruction() { CancellationToken = cancellationToken }, new ConvertConstructorCallIntoInitializer(), new ReplaceMethodCallsWithOperators() }; } - public static void RunTransformations(AstNode node) - { - RunTransformationsUntil(node, null); - } - - public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition) + public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition, CancellationToken cancellationToken) { if (node == null) return; for (int i = 0; i < 4; i++) { + cancellationToken.ThrowIfCancellationRequested(); if (Options.ReduceAstJumps) { node.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); node.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); @@ -40,7 +37,8 @@ namespace Decompiler.Transforms } } - foreach (var visitor in CreatePipeline()) { + foreach (var visitor in CreatePipeline(cancellationToken)) { + cancellationToken.ThrowIfCancellationRequested(); if (abortCondition != null && abortCondition(visitor)) return; node.AcceptVisitor(visitor, null); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 0361aad76..ff79656b8 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -3,9 +3,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; + using Decompiler.ControlFlow; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; +using ICSharpCode.NRefactory.Utils; using Mono.Cecil; using Mono.Cecil.Cil; using Cecil = Mono.Cecil; @@ -16,31 +18,7 @@ namespace Decompiler { public IEnumerable GetSelfAndChildrenRecursive() where T: ILNode { - if (this is T) - yield return (T)this; - - Stack> stack = new Stack>(); - try { - stack.Push(GetChildren().GetEnumerator()); - while (stack.Count > 0) { - while (stack.Peek().MoveNext()) { - ILNode element = stack.Peek().Current; - if (element != null) { - if (element is T) - yield return (T)element; - IEnumerable children = element.GetChildren(); - if (children != null) { - stack.Push(children.GetEnumerator()); - } - } - } - stack.Pop().Dispose(); - } - } finally { - while (stack.Count > 0) { - stack.Pop().Dispose(); - } - } + return TreeTraversal.PreOrder(this, c => c != null ? c.GetChildren() : null).OfType(); } public virtual IEnumerable GetChildren() @@ -115,7 +93,9 @@ namespace Decompiler output.Write("catch "); output.WriteReference(ExceptionType.FullName, ExceptionType); output.WriteLine(" {"); + output.Indent(); base.WriteTo(output); + output.Unindent(); output.WriteLine("}"); } } @@ -137,14 +117,18 @@ namespace Decompiler public override void WriteTo(ITextOutput output) { output.WriteLine(".try {"); + output.Indent(); TryBlock.WriteTo(output); + output.Unindent(); output.WriteLine("}"); foreach (CatchBlock block in CatchBlocks) { block.WriteTo(output); } if (FinallyBlock != null) { output.WriteLine("finally {"); + output.Indent(); FinallyBlock.WriteTo(output); + output.Unindent(); output.WriteLine("}"); } } @@ -250,7 +234,7 @@ namespace Decompiler first = false; } foreach (ILExpression arg in this.Arguments) { - if (!first) output.Write(','); + if (!first) output.Write(", "); arg.WriteTo(output); first = false; } diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index e8800f8b0..ee92456e6 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -20,6 +20,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using Decompiler; using Decompiler.Transforms; using ICSharpCode.Decompiler; @@ -34,7 +35,7 @@ namespace ICSharpCode.ILSpy public class CSharpLanguage : Language { string name = "C#"; - Predicate> transformAbortCondition; + Predicate> transformAbortCondition = null; public CSharpLanguage() { @@ -44,7 +45,7 @@ namespace ICSharpCode.ILSpy internal static IEnumerable GetDebugLanguages() { string lastTransformName = "no transforms"; - foreach (Type _transformType in TransformationPipeline.CreatePipeline().Select(v => v.GetType()).Distinct()) { + foreach (Type _transformType in TransformationPipeline.CreatePipeline(CancellationToken.None).Select(v => v.GetType()).Distinct()) { Type transformType = _transformType; // copy for lambda yield return new CSharpLanguage { transformAbortCondition = v => transformType.IsInstanceOfType(v), @@ -68,35 +69,35 @@ namespace ICSharpCode.ILSpy public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddMethod(method); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddProperty(property); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddField(field); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddEvent(ev); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddType(type); codeDomBuilder.GenerateCode(output, transformAbortCondition); } @@ -105,7 +106,7 @@ namespace ICSharpCode.ILSpy { if (options.FullDecompilation) { foreach (TypeDefinition type in assembly.MainModule.Types) { - AstBuilder codeDomBuilder = new AstBuilder(); + AstBuilder codeDomBuilder = CreateAstBuilder(options); codeDomBuilder.AddType(type); codeDomBuilder.GenerateCode(output, transformAbortCondition); output.WriteLine(); @@ -115,6 +116,11 @@ namespace ICSharpCode.ILSpy } } + AstBuilder CreateAstBuilder(DecompilationOptions options) + { + return new AstBuilder { CancellationToken = options.CancellationToken }; + } + public override string TypeToString(TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes) { AstType astType = AstBuilder.ConvertType(type, typeAttributes); From cd2c70a6b44071c4f31edb805570934bda359831 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 14:12:38 +0100 Subject: [PATCH 04/27] First implementation of type inference. --- .../Ast/AstMethodBodyBuilder.cs | 90 +++---- .../ICSharpCode.Decompiler.csproj | 1 + .../ILAst/ILAstOptimizer.cs | 5 +- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 41 +++- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 221 ++++++++++++++++++ ILSpy/ILAstLanguage.cs | 18 +- 6 files changed, 317 insertions(+), 59 deletions(-) create mode 100644 ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 6edf089b1..45460e928 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -16,6 +16,7 @@ namespace Decompiler public class AstMethodBodyBuilder { MethodDefinition methodDef; + TypeSystem typeSystem; CancellationToken cancellationToken; HashSet definedLocalVars = new HashSet(); @@ -24,6 +25,7 @@ namespace Decompiler AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); builder.cancellationToken = cancellationToken; builder.methodDef = methodDef; + builder.typeSystem = methodDef.Module.TypeSystem; if (Debugger.IsAttached) { return builder.CreateMethodBody(); } else { @@ -65,7 +67,7 @@ namespace Decompiler cancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); - bodyGraph.Optimize(ilMethod); + bodyGraph.Optimize(methodDef, ilMethod); cancellationToken.ThrowIfCancellationRequested(); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); @@ -146,7 +148,7 @@ namespace Decompiler yield return new Ast.ForStatement { EmbeddedStatement = TransformBlock(((ILLoop)node).ContentBlock) }; - /* + /* } else if (node is Branch) { yield return new Ast.LabelStatement { Label = ((Branch)node).FirstBasicBlock.Label }; @@ -163,7 +165,7 @@ namespace Decompiler }; yield return ifElseStmt; - */ + */ } else if (node is ILCondition) { ILCondition conditionalNode = (ILCondition)node; if (conditionalNode.FalseBlock.Body.Any()) { @@ -185,9 +187,9 @@ namespace Decompiler SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition.Arguments[0]) }; for (int i = 0; i < ilSwitch.CaseBlocks.Count; i++) { switchStmt.AddChild(new SwitchSection() { - CaseLabels = new[] { new CaseLabel() { Expression = new PrimitiveExpression(i) } }, - Statements = new[] { TransformBlock(ilSwitch.CaseBlocks[i]) } - }, SwitchStatement.SwitchSectionRole); + CaseLabels = new[] { new CaseLabel() { Expression = new PrimitiveExpression(i) } }, + Statements = new[] { TransformBlock(ilSwitch.CaseBlocks[i]) } + }, SwitchStatement.SwitchSectionRole); } yield return switchStmt; } else if (node is ILTryCatchBlock) { @@ -195,10 +197,10 @@ namespace Decompiler List catchClauses = new List(); foreach (var catchClause in tryCatchNode.CatchBlocks) { catchClauses.Add(new Ast.CatchClause { - Type = AstBuilder.ConvertType(catchClause.ExceptionType), - VariableName = "exception", - Body = TransformBlock(catchClause) - }); + Type = AstBuilder.ConvertType(catchClause.ExceptionType), + VariableName = "exception", + Body = TransformBlock(catchClause) + }); } yield return new Ast.TryCatchStatement { TryBlock = TransformBlock(tryCatchNode.TryBlock), @@ -225,7 +227,7 @@ namespace Decompiler AstNode TransformExpression(ILExpression expr) { List args = TransformExpressionArguments(expr); - return TransformByteCode(methodDef, expr, args); + return TransformByteCode(expr, args); } Ast.Expression MakeBranchCondition(ILExpression expr) @@ -234,19 +236,19 @@ namespace Decompiler Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; switch(expr.OpCode.Code) { - case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); - case Code.Brtrue: return arg1; - case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); - case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); - default: throw new Exception("Bad opcode"); + case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); + case Code.Brtrue: return arg1; + case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); + case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); + case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); + case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); + default: throw new Exception("Bad opcode"); } /* } else if (branch is ShortCircuitBranch) { @@ -282,13 +284,13 @@ namespace Decompiler } else { throw new Exception("Bad type"); } - */ + */ } - AstNode TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List args) + AstNode TransformByteCode(ILExpression byteCode, List args) { try { - AstNode ret = TransformByteCode_Internal(methodDef, byteCode, args); + AstNode ret = TransformByteCode_Internal(byteCode, args); // ret.UserData["Type"] = byteCode.Type; return ret; } catch (NotImplementedException) { @@ -325,7 +327,7 @@ namespace Decompiler } } - AstNode TransformByteCode_Internal(MethodDefinition methodDef, ILExpression byteCode, List args) + AstNode TransformByteCode_Internal(ILExpression byteCode, List args) { // throw new NotImplementedException(); @@ -541,6 +543,20 @@ namespace Decompiler return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name)); } case Code.Ldc_I4: + if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 0) + return new Ast.PrimitiveExpression(false); + else if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 1) + return new Ast.PrimitiveExpression(true); + if (byteCode.InferredType != null && byteCode.InferredType.IsValueType) { + TypeDefinition enumDefinition = byteCode.InferredType.Resolve(); + if (enumDefinition != null && enumDefinition.IsEnum) { + foreach (FieldDefinition field in enumDefinition.Fields) { + if (field.IsStatic && object.Equals(field.Constant, operand)) + return AstBuilder.ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); + } + } + } + return new Ast.PrimitiveExpression(operand); case Code.Ldc_I8: case Code.Ldc_R4: case Code.Ldc_R8: @@ -724,26 +740,10 @@ namespace Decompiler if (reqType == null) { return expr; } else { - return Convert(expr, reqType.FullName); + return expr; } } - static Ast.Expression Convert(Ast.Expression expr, string reqType) - { -// if (expr.UserData.ContainsKey("Type")) { -// Cecil.TypeReference exprType = (Cecil.TypeReference)expr.UserData["Type"]; -// if (exprType == ByteCode.TypeZero && -// reqType == ByteCode.TypeBool.FullName) { -// return new PrimitiveExpression(false, "false"); -// } -// if (exprType == ByteCode.TypeOne && -// reqType == ByteCode.TypeBool.FullName) { -// return new PrimitiveExpression(true, "true"); -// } -// } - return expr; - } - static Ast.Expression ConvertIntToBool(Ast.Expression astInt) { return astInt; diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index fdda5b4ab..c2df0e929 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -87,6 +87,7 @@ + diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 233e00d84..d7eba8e58 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -16,6 +16,7 @@ namespace Decompiler.ControlFlow FlattenNestedMovableBlocks, SimpleGotoRemoval, RemoveDeadLabels, + TypeInference, None } @@ -23,7 +24,7 @@ namespace Decompiler.ControlFlow { Dictionary labelToCfNode = new Dictionary(); - public void Optimize(ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) + public void Optimize(MethodDefinition cecilMethod, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { @@ -55,6 +56,8 @@ namespace Decompiler.ControlFlow SimpleGotoRemoval(method); if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return; RemoveDeadLabels(method); + if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; + TypeAnalysis.Run(cecilMethod.Module.TypeSystem, method); } class ILMoveableBlock: ILBlock diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index ff79656b8..89b3b9667 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -55,7 +55,8 @@ namespace Decompiler public override IEnumerable GetChildren() { - yield return EntryPoint; + if (EntryPoint != null) + yield return EntryPoint; foreach(ILNode child in this.Body) { yield return child; } @@ -157,7 +158,7 @@ namespace Decompiler } } - public class ILExpression: ILNode + public class ILExpression : ILNode { public OpCode OpCode { get; set; } public object Operand { get; set; } @@ -165,6 +166,8 @@ namespace Decompiler // Mapping to the original instructions (useful for debugging) public List ILRanges { get; set; } + public TypeReference InferredType { get; set; } + public ILExpression(OpCode opCode, object operand, params ILExpression[] args) { this.OpCode = opCode; @@ -213,24 +216,39 @@ namespace Decompiler public override void WriteTo(ITextOutput output) { if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { - if (OpCode.Name == "stloc") { - output.Write(((ILVariable)Operand).Name + " = "); + if (OpCode == OpCodes.Stloc && this.InferredType == null) { + output.Write(((ILVariable)Operand).Name); + output.Write(" = "); Arguments.First().WriteTo(output); return; - } else if (OpCode.Name == "ldloc") { + } else if (OpCode == OpCodes.Ldloc) { output.Write(((ILVariable)Operand).Name); + if (this.InferredType != null) { + output.Write(':'); + this.InferredType.WriteTo(output, true, true); + } return; } } output.Write(OpCode.Name); + if (this.InferredType != null) { + output.Write(':'); + this.InferredType.WriteTo(output, true, true); + } output.Write('('); bool first = true; if (Operand != null) { - if (Operand is ILLabel) - output.Write(((ILLabel)Operand).Name); - else + if (Operand is ILLabel) { + output.WriteReference(((ILLabel)Operand).Name, Operand); + } else if (Operand is MethodReference) { + MethodReference method = (MethodReference)Operand; + method.DeclaringType.WriteTo(output, true, true); + output.Write("::"); + output.WriteReference(method.Name, method); + } else { DisassemblerHelpers.WriteOperand(output, Operand); + } first = false; } foreach (ILExpression arg in this.Arguments) { @@ -242,7 +260,7 @@ namespace Decompiler } } - public class ILLoop: ILNode + public class ILLoop : ILNode { public ILBlock ContentBlock; @@ -261,7 +279,7 @@ namespace Decompiler } } - public class ILCondition: ILNode + public class ILCondition : ILNode { public ILExpression Condition; public ILBlock TrueBlock; // Branch was taken @@ -271,7 +289,8 @@ namespace Decompiler { yield return Condition; yield return TrueBlock; - yield return FalseBlock; + if (FalseBlock != null) + yield return FalseBlock; } public override void WriteTo(ITextOutput output) diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs new file mode 100644 index 000000000..01c17f9a1 --- /dev/null +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -0,0 +1,221 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Decompiler; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Decompiler +{ + /// + /// Assigns C# types to IL expressions. + /// + /// + /// Types are inferred in a bidirectional manner: + /// The expected type flows from the outside to the inside, the actual inferred type flows from the inside to the outside. + /// + public class TypeAnalysis + { + public static void Run(TypeSystem typeSystem, ILNode node) + { + TypeAnalysis ta = new TypeAnalysis(); + ta.typeSystem = typeSystem; + ta.InferTypes(node); + } + + TypeSystem typeSystem; + List storedToGeneratedVariables = new List(); + + void InferTypes(ILNode node) + { + foreach (ILNode child in node.GetChildren()) { + ILExpression expr = child as ILExpression; + if (expr != null) { + ILVariable v = expr.Operand as ILVariable; + if (v != null && v.IsGenerated && v.Type == null && expr.OpCode == OpCodes.Stloc) { + // don't deal with this node or its children yet, + // wait for the expected type to be inferred first + storedToGeneratedVariables.Add(expr); + continue; + } + bool anyArgumentIsMissingType = expr.Arguments.Any(a => a.InferredType == null); + if (expr.InferredType == null || anyArgumentIsMissingType) + expr.InferredType = InferTypeForExpression(expr, null, forceInferChildren: anyArgumentIsMissingType); + } + InferTypes(child); + } + } + + /// + /// Infers the C# type of . + /// + /// The expression + /// The expected type of the expression + /// Whether direct children should be inferred even if its not necessary. (does not apply to nested children!) + /// The inferred type + TypeReference InferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) + { + if (forceInferChildren || expr.InferredType == null) + expr.InferredType = DoInferTypeForExpression(expr, expectedType, forceInferChildren); + return expr.InferredType; + } + + TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) + { + switch (expr.OpCode.Code) { + case Code.Stloc: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments.Single(), ((ILVariable)expr.Operand).Type); + return null; + case Code.Ldloc: + return ((ILVariable)expr.Operand).Type; + case Code.Ldarg: + return ((ParameterDefinition)expr.Operand).ParameterType; + case Code.Call: + case Code.Callvirt: + { + MethodReference method = (MethodReference)expr.Operand; + if (forceInferChildren) { + for (int i = 0; i < expr.Arguments.Count; i++) { + if (i == 0 && method.HasThis) + InferTypeForExpression(expr.Arguments[i], method.DeclaringType); + else + InferTypeForExpression(expr.Arguments[i], method.Parameters[method.HasThis ? i - 1: i].ParameterType); + } + } + return method.ReturnType; + } + case Code.Newobj: + { + MethodReference ctor = (MethodReference)expr.Operand; + if (forceInferChildren) { + for (int i = 0; i < ctor.Parameters.Count; i++) { + InferTypeForExpression(expr.Arguments[i], ctor.Parameters[i].ParameterType); + } + } + return ctor.DeclaringType; + } + case Code.Or: + return InferArgumentsInBinaryOperator(expr); + case Code.Shl: + case Code.Shr: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); + return InferTypeForExpression(expr.Arguments[0], expectedType); + case Code.Ldc_I4: + return (IsSigned(expectedType) != null || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32; + case Code.Ldc_I8: + return (IsSigned(expectedType) != null) ? expectedType : typeSystem.Int64; + case Code.Conv_I8: + return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64; + case Code.Dup: + return InferTypeForExpression(expr.Arguments.Single(), expectedType); + case Code.Ceq: + case Code.Clt: + if (forceInferChildren) + InferArgumentsInBinaryOperator(expr); + return typeSystem.Boolean; + case Code.Beq: + case Code.Blt: + if (forceInferChildren) + InferArgumentsInBinaryOperator(expr); + return null; + case Code.Brtrue: + case Code.Brfalse: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); + return null; + default: + //throw new NotImplementedException("Can't handle " + expr.OpCode.Name); + return null; + } + } + + TypeReference InferArgumentsInBinaryOperator(ILExpression expr) + { + ILExpression left = expr.Arguments[0]; + ILExpression right = expr.Arguments[1]; + TypeReference leftPreferred = DoInferTypeForExpression(left, null); + TypeReference rightPreferred = DoInferTypeForExpression(right, null); + if (leftPreferred == rightPreferred) { + return left.InferredType = right.InferredType = leftPreferred; + } else if (rightPreferred == DoInferTypeForExpression(left, rightPreferred)) { + return left.InferredType = right.InferredType = rightPreferred; + } else if (leftPreferred == DoInferTypeForExpression(right, leftPreferred)) { + return left.InferredType = right.InferredType = leftPreferred; + } else { + return left.InferredType = right.InferredType = TypeWithMoreInformation(leftPreferred, rightPreferred); + } + } + + TypeReference TypeWithMoreInformation(TypeReference leftPreferred, TypeReference rightPreferred) + { + int left = GetInformationAmount(typeSystem, leftPreferred); + int right = GetInformationAmount(typeSystem, rightPreferred); + if (left < right) + return rightPreferred; + else + return leftPreferred; + } + + int GetInformationAmount(TypeReference type) + { + return GetInformationAmount(typeSystem, type); + } + + static int GetInformationAmount(TypeSystem typeSystem, TypeReference type) + { + if (type == null) + return 0; + if (type.IsValueType) { + // value type might be an enum + TypeDefinition typeDef = type.Resolve() as TypeDefinition; + if (typeDef != null && typeDef.IsEnum) { + TypeReference underlyingType = typeDef.Fields.Single(f => f.IsRuntimeSpecialName && !f.IsStatic).FieldType; + return GetInformationAmount(typeDef.Module.TypeSystem, underlyingType); + } + } + if (type == typeSystem.Boolean) + return 1; + else if (type == typeSystem.Byte || type == typeSystem.SByte) + return 8; + else if (type == typeSystem.Int16 || type == typeSystem.UInt16) + return 16; + else if (type == typeSystem.Int32 || type == typeSystem.UInt32) + return 32; + else if (type == typeSystem.IntPtr || type == typeSystem.UIntPtr) + return 33; // treat native int as between int32 and int64 + else if (type == typeSystem.Int64 || type == typeSystem.UInt64) + return 64; + return 100; // we consider structs/objects to have more information than any primitives + } + + bool? IsSigned(TypeReference type) + { + return IsSigned(typeSystem, type); + } + + static bool? IsSigned(TypeSystem typeSystem, TypeReference type) + { + if (type == null) + return null; + if (type.IsValueType) { + // value type might be an enum + TypeDefinition typeDef = type.Resolve() as TypeDefinition; + if (typeDef != null && typeDef.IsEnum) { + TypeReference underlyingType = typeDef.Fields.Single(f => f.IsRuntimeSpecialName && !f.IsStatic).FieldType; + return IsSigned(typeDef.Module.TypeSystem, underlyingType); + } + } + if (type == typeSystem.Byte || type == typeSystem.UInt16 || type == typeSystem.UInt32 || type == typeSystem.UInt64 || type == typeSystem.UIntPtr) + return false; + if (type == typeSystem.SByte || type == typeSystem.Int16 || type == typeSystem.Int32 || type == typeSystem.Int64 || type == typeSystem.IntPtr) + return true; + return null; + } + } +} diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/ILAstLanguage.cs index 5478c24a8..7b19986ad 100644 --- a/ILSpy/ILAstLanguage.cs +++ b/ILSpy/ILAstLanguage.cs @@ -24,6 +24,7 @@ using Decompiler; using Decompiler.ControlFlow; using Decompiler.Transforms; using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -46,13 +47,26 @@ namespace ICSharpCode.ILSpy public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { + ILAstBuilder astBuilder = new ILAstBuilder(); ILBlock ilMethod = new ILBlock(); - ilMethod.Body = new ILAstBuilder().Build(method, inlineVariables); + ilMethod.Body = astBuilder.Build(method, inlineVariables); if (abortBeforeStep != null) { - new ILAstOptimizer().Optimize(ilMethod, abortBeforeStep.Value); + new ILAstOptimizer().Optimize(method, ilMethod, abortBeforeStep.Value); } + var allVariables = astBuilder.Variables + .Concat(ilMethod.GetSelfAndChildrenRecursive().Select(e => e.Operand as ILVariable).Where(v => v != null)).Distinct(); + foreach (ILVariable v in allVariables) { + output.Write(v.Name); + if (v.Type != null) { + output.Write(" : "); + v.Type.WriteTo(output, true, true); + } + output.WriteLine(); + } + output.WriteLine(); + foreach (ILNode node in ilMethod.Body) { node.WriteTo(output); output.WriteLine(); From d6f94bbc7539ee60aaa763fc82404489775756f1 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 14:43:38 +0100 Subject: [PATCH 05/27] Simplify "x == false" to "!x" in PushNegation. --- .../Ast/Transforms/PushNegation.cs | 74 +++++++++++++++---- .../Ast/Transforms/TransformationPipeline.cs | 4 +- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs index 10ff2f6fc..e8ee847d6 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using ICSharpCode.NRefactory.CSharp; -namespace Decompiler.Transforms.Ast +namespace Decompiler.Transforms { public class PushNegation: DepthFirstAstVisitor { @@ -24,29 +24,48 @@ namespace Decompiler.Transforms.Ast // !((a) op (b)) BinaryOperatorExpression binaryOp = unary.Expression as BinaryOperatorExpression; if (unary.Operator == UnaryOperatorType.Not && binaryOp != null) { - bool sucessful = true; + bool successful = true; switch (binaryOp.Operator) { - case BinaryOperatorType.Equality: binaryOp.Operator = BinaryOperatorType.InEquality; break; - case BinaryOperatorType.InEquality: binaryOp.Operator = BinaryOperatorType.Equality; break; - // TODO: these are invalid for floats (stupid NaN) - case BinaryOperatorType.GreaterThan: binaryOp.Operator = BinaryOperatorType.LessThanOrEqual; break; - case BinaryOperatorType.GreaterThanOrEqual: binaryOp.Operator = BinaryOperatorType.LessThan; break; - case BinaryOperatorType.LessThanOrEqual: binaryOp.Operator = BinaryOperatorType.GreaterThan; break; - case BinaryOperatorType.LessThan: binaryOp.Operator = BinaryOperatorType.GreaterThanOrEqual; break; - default: sucessful = false; break; + case BinaryOperatorType.Equality: + binaryOp.Operator = BinaryOperatorType.InEquality; + break; + case BinaryOperatorType.InEquality: + binaryOp.Operator = BinaryOperatorType.Equality; + break; + case BinaryOperatorType.GreaterThan: // TODO: these are invalid for floats (stupid NaN) + binaryOp.Operator = BinaryOperatorType.LessThanOrEqual; + break; + case BinaryOperatorType.GreaterThanOrEqual: + binaryOp.Operator = BinaryOperatorType.LessThan; + break; + case BinaryOperatorType.LessThanOrEqual: + binaryOp.Operator = BinaryOperatorType.GreaterThan; + break; + case BinaryOperatorType.LessThan: + binaryOp.Operator = BinaryOperatorType.GreaterThanOrEqual; + break; + default: + successful = false; + break; } - if (sucessful) { + if (successful) { unary.ReplaceWith(binaryOp); return binaryOp.AcceptVisitor(this, data); } - sucessful = true; + successful = true; switch (binaryOp.Operator) { - case BinaryOperatorType.ConditionalAnd: binaryOp.Operator = BinaryOperatorType.ConditionalOr; break; - case BinaryOperatorType.ConditionalOr: binaryOp.Operator = BinaryOperatorType.ConditionalAnd; break; - default: sucessful = false; break; + case BinaryOperatorType.ConditionalAnd: + binaryOp.Operator = BinaryOperatorType.ConditionalOr; + break; + case BinaryOperatorType.ConditionalOr: + binaryOp.Operator = BinaryOperatorType.ConditionalAnd; + break; + default: + successful = false; + break; } - if (sucessful) { + if (successful) { binaryOp.Left.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e)); binaryOp.Right.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e)); unary.ReplaceWith(binaryOp); @@ -55,5 +74,28 @@ namespace Decompiler.Transforms.Ast } return base.VisitUnaryOperatorExpression(unary, data); } + + public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data) + { + BinaryOperatorType op = binaryOperatorExpression.Operator; + bool? rightOperand = null; + if (binaryOperatorExpression.Right is PrimitiveExpression) + rightOperand = ((PrimitiveExpression)binaryOperatorExpression.Right).Value as bool?; + if (op == BinaryOperatorType.Equality && rightOperand == true || op == BinaryOperatorType.InEquality && rightOperand == false) { + // 'b == true' or 'b != false' is useless + binaryOperatorExpression.AcceptVisitor(this, data); + binaryOperatorExpression.ReplaceWith(binaryOperatorExpression.Left); + return null; + } else if (op == BinaryOperatorType.Equality && rightOperand == false || op == BinaryOperatorType.InEquality && rightOperand == true) { + // 'b == false' or 'b != true' is a negation: + Expression left = binaryOperatorExpression.Left; + left.Remove(); + UnaryOperatorExpression uoe = new UnaryOperatorExpression(UnaryOperatorType.Not, left); + binaryOperatorExpression.ReplaceWith(uoe); + return uoe.AcceptVisitor(this, data); + } else { + return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data); + } + } } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index e0cc0a415..89c18c575 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -12,9 +12,10 @@ namespace Decompiler.Transforms public static IAstVisitor[] CreatePipeline(CancellationToken cancellationToken) { return new IAstVisitor[] { + new PushNegation(), new DelegateConstruction() { CancellationToken = cancellationToken }, new ConvertConstructorCallIntoInitializer(), - new ReplaceMethodCallsWithOperators() + new ReplaceMethodCallsWithOperators(), }; } @@ -33,7 +34,6 @@ namespace Decompiler.Transforms } if (Options.ReduceAstOther) { node.AcceptVisitor(new Transforms.Ast.RemoveEmptyElseBody(), null); - node.AcceptVisitor(new Transforms.Ast.PushNegation(), null); } } From 798d156670d101a61a4c38fcaf82e2da66bcf415 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 15:47:39 +0100 Subject: [PATCH 06/27] Add comparisons with 0 or null if necessary. Closes #8. --- .../Ast/AstMethodBodyBuilder.cs | 47 ++++++++++++++----- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 25 +++++++++- 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 45460e928..68cff0617 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -235,19 +235,42 @@ namespace Decompiler List args = TransformExpressionArguments(expr); Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; + TypeReference arg1Type = args.Count >= 1 ? expr.Arguments[0].InferredType : null; switch(expr.OpCode.Code) { - case Code.Brfalse: return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); - case Code.Brtrue: return arg1; - case Code.Beq: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); - case Code.Bge: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case Code.Bge_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); - case Code.Bgt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Bgt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); - case Code.Ble: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case Code.Ble_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); - case Code.Blt: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case Code.Blt_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); - case Code.Bne_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); + case Code.Brfalse: + if (arg1Type == typeSystem.Boolean) + return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); + else if (TypeAnalysis.IsIntegerOrEnum(typeSystem, arg1Type)) + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, new PrimitiveExpression(0)); + else + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, new NullReferenceExpression()); + case Code.Brtrue: + if (arg1Type == typeSystem.Boolean) + return arg1; + else if (TypeAnalysis.IsIntegerOrEnum(typeSystem, arg1Type)) + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, new PrimitiveExpression(0)); + else + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, new NullReferenceExpression()); + case Code.Beq: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Equality, arg2); + case Code.Bge: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case Code.Bge_Un: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThanOrEqual, arg2); + case Code.Bgt: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Bgt_Un: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.GreaterThan, arg2); + case Code.Ble: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); + case Code.Ble_Un: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThanOrEqual, arg2); + case Code.Blt: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case Code.Blt_Un: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.LessThan, arg2); + case Code.Bne_Un: + return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.InEquality, arg2); default: throw new Exception("Bad opcode"); } /* diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 01c17f9a1..6a51a54a8 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -99,6 +99,10 @@ namespace Decompiler } return ctor.DeclaringType; } + case Code.Ldfld: + return UnpackModifiers(((FieldReference)expr.Operand).FieldType); + case Code.Ldsfld: + return UnpackModifiers(((FieldReference)expr.Operand).FieldType); case Code.Or: return InferArgumentsInBinaryOperator(expr); case Code.Shl: @@ -107,9 +111,9 @@ namespace Decompiler InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); return InferTypeForExpression(expr.Arguments[0], expectedType); case Code.Ldc_I4: - return (IsSigned(expectedType) != null || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32; + return (IsIntegerOrEnum(expectedType) || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32; case Code.Ldc_I8: - return (IsSigned(expectedType) != null) ? expectedType : typeSystem.Int64; + return (IsIntegerOrEnum(expectedType)) ? expectedType : typeSystem.Int64; case Code.Conv_I8: return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64; case Code.Dup: @@ -135,6 +139,13 @@ namespace Decompiler } } + static TypeReference UnpackModifiers(TypeReference type) + { + while (type is OptionalModifierType || type is RequiredModifierType) + type = ((TypeSpecification)type).ElementType; + return type; + } + TypeReference InferArgumentsInBinaryOperator(ILExpression expr) { ILExpression left = expr.Arguments[0]; @@ -194,6 +205,16 @@ namespace Decompiler return 100; // we consider structs/objects to have more information than any primitives } + bool IsIntegerOrEnum(TypeReference type) + { + return IsIntegerOrEnum(typeSystem, type); + } + + public static bool IsIntegerOrEnum(TypeSystem typeSystem, TypeReference type) + { + return IsSigned(typeSystem, type) != null; + } + bool? IsSigned(TypeReference type) { return IsSigned(typeSystem, type); From 10ee9895da499552f7c99940be919091d11d03c2 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 18 Feb 2011 10:18:17 +0100 Subject: [PATCH 07/27] read .reaources files as ResourceSet - support viewing single images --- ILSpy/TreeNodes/ResourceListTreeNode.cs | 100 +++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index b56b2abbd..e8cc9b0d6 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -2,10 +2,17 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections; using System.IO; +using System.Resources; using System.Text; +using System.Windows; +using System.Windows.Baml2006; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; using System.Windows.Threading; - +using System.Xaml; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.Decompiler; @@ -66,6 +73,7 @@ namespace ICSharpCode.ILSpy.TreeNodes public ResourceTreeNode(Resource r) { + this.LazyLoading = true; this.r = r; } @@ -144,5 +152,95 @@ namespace ICSharpCode.ILSpy.TreeNodes } return false; } + + protected override void LoadChildren() + { + EmbeddedResource er = r as EmbeddedResource; + if (er != null) { + try { + Stream s = er.GetResourceStream(); + ResourceSet set = new ResourceSet(s); + foreach (DictionaryEntry entry in set) { + if (entry.Value is Stream) { + Children.Add(new ResourceEntryNode(entry.Key.ToString(), entry.Value as Stream)); + } + } + } catch (Exception) { +// MessageBox.Show(ex.ToString()); + } + } + } + } + + class ResourceEntryNode : ILSpyTreeNode + { + string key; + Stream value; + + public override object Text { + get { return key.ToString(); } + } + + public override object Icon { + get { return Images.Resource; } + } + + public ResourceEntryNode(string key, Stream value) + { + this.key = key; + this.value = value; + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, string.Format("{0} = {1}", key, value)); + } + + internal override bool View(DecompilerTextView textView) + { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + if (LoadImage(output)) + textView.Show(output, null); + else if (LoadBaml(output)) + textView.Show(output, null); + else + return false; + return true; + } + + bool LoadImage(AvalonEditTextOutput output) + { + try { + BitmapImage image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = value; + image.EndInit(); + output.AddUIElement(() => new Image { Source = image }); + output.WriteLine(); + output.AddButton(Images.Save, "Save", delegate { Save(); }); + } catch (Exception ex) { + MessageBox.Show(ex.ToString()); + return false; + } + return true; + } + + bool LoadBaml(AvalonEditTextOutput output) + { + return false; + } + + public override bool Save() + { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); + if (dlg.ShowDialog() == true) { + value.Position = 0; + using (var fs = dlg.OpenFile()) { + value.CopyTo(fs); + } + } + return true; + } } } From b1df8e83269e1020e631e3a97755583b70a8b778 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 18 Feb 2011 16:14:50 +0100 Subject: [PATCH 08/27] added minimal support for reading BAML files --- ILSpy/ILSpy.csproj | 1 + ILSpy/TreeNodes/ResourceEntryNode.cs | 132 ++++++++++++++++++++++++ ILSpy/TreeNodes/ResourceListTreeNode.cs | 92 ++--------------- 3 files changed, 142 insertions(+), 83 deletions(-) create mode 100644 ILSpy/TreeNodes/ResourceEntryNode.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index afc104e15..8f9884320 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -146,6 +146,7 @@ + diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceEntryNode.cs new file mode 100644 index 000000000..9cc66cc45 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceEntryNode.cs @@ -0,0 +1,132 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Windows; +using System.Windows.Baml2006; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using System.Xaml; +using System.Xml; + +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.TextView; +using Microsoft.Win32; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + class ResourceEntryNode : ILSpyTreeNode + { + string key; + Stream value; + + public override object Text { + get { return key.ToString(); } + } + + public override object Icon { + get { return Images.Resource; } + } + + public ResourceEntryNode(string key, Stream value) + { + this.key = key; + this.value = value; + } + + public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) + { + language.WriteCommentLine(output, string.Format("{0} = {1}", key, value)); + } + + internal override bool View(DecompilerTextView textView) + { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + try { + if (LoadImage(output)) + textView.Show(output, null); + else if (LoadBaml(output)) { + textView.Show(output, HighlightingManager.Instance.GetDefinitionByExtension(".xml")); + } else + return false; + } catch (Exception ex) { + output.Write(ex.ToString()); + textView.Show(output, null); + } + return true; + } + + bool LoadImage(AvalonEditTextOutput output) + { + try { + value.Position = 0; + BitmapImage image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = value; + image.EndInit(); + output.AddUIElement(() => new Image { Source = image }); + output.WriteLine(); + output.AddButton(Images.Save, "Save", delegate { Save(); }); + } catch (Exception) { + return false; + } + return true; + } + + bool LoadBaml(AvalonEditTextOutput output) + { + value.Position = 0; + TextWriter w = new StringWriter(); + Baml2006Reader reader = new Baml2006Reader(value, new XamlReaderSettings() { ValuesMustBeString = true }); + XamlXmlWriter writer = new XamlXmlWriter(new XmlTextWriter(w) { Formatting = Formatting.Indented }, reader.SchemaContext); + while (reader.Read()) { + switch (reader.NodeType) { + case XamlNodeType.None: + + break; + case XamlNodeType.StartObject: + writer.WriteStartObject(reader.Type); + break; + case XamlNodeType.GetObject: + writer.WriteGetObject(); + break; + case XamlNodeType.EndObject: + writer.WriteEndObject(); + break; + case XamlNodeType.StartMember: + writer.WriteStartMember(reader.Member); + break; + case XamlNodeType.EndMember: + writer.WriteEndMember(); + break; + case XamlNodeType.Value: + // requires XamlReaderSettings.ValuesMustBeString = true to work properly + writer.WriteValue(reader.Value); + break; + case XamlNodeType.NamespaceDeclaration: + writer.WriteNamespace(reader.Namespace); + break; + default: + throw new Exception("Invalid value for XamlNodeType"); + } + } + output.Write(w.ToString()); + return true; + } + + public override bool Save() + { + SaveFileDialog dlg = new SaveFileDialog(); + dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); + if (dlg.ShowDialog() == true) { + value.Position = 0; + using (var fs = dlg.OpenFile()) { + value.CopyTo(fs); + } + } + return true; + } + } +} diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index e8cc9b0d6..c512ca70a 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -7,12 +7,8 @@ using System.IO; using System.Resources; using System.Text; using System.Windows; -using System.Windows.Baml2006; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows.Media.Imaging; using System.Windows.Threading; -using System.Xaml; + using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Utils; using ICSharpCode.Decompiler; @@ -159,88 +155,18 @@ namespace ICSharpCode.ILSpy.TreeNodes if (er != null) { try { Stream s = er.GetResourceStream(); - ResourceSet set = new ResourceSet(s); - foreach (DictionaryEntry entry in set) { - if (entry.Value is Stream) { - Children.Add(new ResourceEntryNode(entry.Key.ToString(), entry.Value as Stream)); + FileType type = GuessFileType.DetectFileType(s); + s.Position = 0; + if (type == FileType.Binary) { + ResourceSet set = new ResourceSet(s); + foreach (DictionaryEntry entry in set) { + Children.Add(new ResourceEntryNode(entry.Key.ToString(), (Stream)entry.Value)); } } - } catch (Exception) { -// MessageBox.Show(ex.ToString()); - } - } - } - } - - class ResourceEntryNode : ILSpyTreeNode - { - string key; - Stream value; - - public override object Text { - get { return key.ToString(); } - } - - public override object Icon { - get { return Images.Resource; } - } - - public ResourceEntryNode(string key, Stream value) - { - this.key = key; - this.value = value; - } - - public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) - { - language.WriteCommentLine(output, string.Format("{0} = {1}", key, value)); - } - - internal override bool View(DecompilerTextView textView) - { - AvalonEditTextOutput output = new AvalonEditTextOutput(); - if (LoadImage(output)) - textView.Show(output, null); - else if (LoadBaml(output)) - textView.Show(output, null); - else - return false; - return true; - } - - bool LoadImage(AvalonEditTextOutput output) - { - try { - BitmapImage image = new BitmapImage(); - image.BeginInit(); - image.StreamSource = value; - image.EndInit(); - output.AddUIElement(() => new Image { Source = image }); - output.WriteLine(); - output.AddButton(Images.Save, "Save", delegate { Save(); }); - } catch (Exception ex) { - MessageBox.Show(ex.ToString()); - return false; - } - return true; - } - - bool LoadBaml(AvalonEditTextOutput output) - { - return false; - } - - public override bool Save() - { - SaveFileDialog dlg = new SaveFileDialog(); - dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key)); - if (dlg.ShowDialog() == true) { - value.Position = 0; - using (var fs = dlg.OpenFile()) { - value.CopyTo(fs); + } catch (Exception ex) { + MessageBox.Show(ex.ToString()); } } - return true; } } } From bf3b04d3b914a00257478e6eb66c48a9a7f53540 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 16:52:42 +0100 Subject: [PATCH 09/27] Revert "Avoid loading children when they aren't needed." This reverts commit 56faf465977c43473e2a06d512556b47a4748319. The commit was causing issues with treeview filtering (e.g. Resources node not visible). --- ILSpy/TreeNodes/ILSpyTreeNode.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index c8563c533..44a9483d3 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -157,8 +157,7 @@ namespace ICSharpCode.ILSpy.TreeNodes void EnsureChildrenFiltered() { - // No need to ensure lazy children here: - // if the children get lazy-loaded later, they'll still be filtered. + EnsureLazyChildren(); if (childrenNeedFiltering) { childrenNeedFiltering = false; foreach (ILSpyTreeNode node in this.Children.OfType()) From f7330c08ce2c519b2ed5bac821b605557208cbba Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 17:03:53 +0100 Subject: [PATCH 10/27] Sort resources within a resource file. --- ILSpy/TreeNodes/ResourceListTreeNode.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index c512ca70a..9dcc0d377 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.IO; +using System.Linq; using System.Resources; using System.Text; using System.Windows; @@ -159,7 +160,7 @@ namespace ICSharpCode.ILSpy.TreeNodes s.Position = 0; if (type == FileType.Binary) { ResourceSet set = new ResourceSet(s); - foreach (DictionaryEntry entry in set) { + foreach (DictionaryEntry entry in set.Cast().OrderBy(e => e.Key.ToString())) { Children.Add(new ResourceEntryNode(entry.Key.ToString(), (Stream)entry.Value)); } } From d4c275410eada96489efcf7b7b2d1bc85f06e266 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 19:21:11 +0100 Subject: [PATCH 11/27] Implemented removal of closures ("display class" introduced by C# compiler for anonymous methods) --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 37 +++-- .../Ast/AstMethodBodyBuilder.cs | 51 ++++--- .../Ast/DecompilerContext.cs | 21 +++ .../Ast/Transforms/ContextTrackingVisitor.cs | 69 +++++++++ .../Ast/Transforms/DelegateConstruction.cs | 135 ++++++++++++++---- .../Ast/Transforms/TransformationPipeline.cs | 12 +- .../ICSharpCode.Decompiler.csproj | 2 + ICSharpCode.Decompiler/Tests/TestRunner.cs | 2 +- ILSpy/CSharpLanguage.cs | 25 ++-- 9 files changed, 280 insertions(+), 74 deletions(-) create mode 100644 ICSharpCode.Decompiler/Ast/DecompilerContext.cs create mode 100644 ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 0f2560437..d4c841fd1 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -13,10 +13,17 @@ namespace Decompiler { public class AstBuilder { - public CancellationToken CancellationToken { get; set; } + DecompilerContext context = new DecompilerContext(); CompilationUnit astCompileUnit = new CompilationUnit(); Dictionary astNamespaces = new Dictionary(); + public AstBuilder(DecompilerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + this.context = context; + } + public void GenerateCode(ITextOutput output) { GenerateCode(output, null); @@ -24,7 +31,7 @@ namespace Decompiler public void GenerateCode(ITextOutput output, Predicate> transformAbortCondition) { - Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition, this.CancellationToken); + Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition, context); astCompileUnit.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null); var outputFormatter = new TextOutputFormatter(output); @@ -105,6 +112,7 @@ namespace Decompiler public TypeDeclaration CreateType(TypeDefinition typeDef) { TypeDeclaration astType = new TypeDeclaration(); + astType.AddAnnotation(typeDef); astType.Modifiers = ConvertModifiers(typeDef); astType.Name = typeDef.Name; @@ -409,12 +417,13 @@ namespace Decompiler MethodDeclaration CreateMethod(MethodDefinition methodDef) { MethodDeclaration astMethod = new MethodDeclaration(); + astMethod.AddAnnotation(methodDef); astMethod.Name = methodDef.Name; astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType); astMethod.Parameters = MakeParameters(methodDef.Parameters); if (!methodDef.DeclaringType.IsInterface) { astMethod.Modifiers = ConvertModifiers(methodDef); - astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, this.CancellationToken); + astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); } return astMethod; } @@ -422,31 +431,33 @@ namespace Decompiler ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) { ConstructorDeclaration astMethod = new ConstructorDeclaration(); + astMethod.AddAnnotation(methodDef); astMethod.Modifiers = ConvertModifiers(methodDef); if (methodDef.IsStatic) { // don't show visibility for static ctors astMethod.Modifiers &= ~Modifiers.VisibilityMask; } astMethod.Parameters = MakeParameters(methodDef.Parameters); - astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, this.CancellationToken); + astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); return astMethod; } PropertyDeclaration CreateProperty(PropertyDefinition propDef) { PropertyDeclaration astProp = new PropertyDeclaration(); + astProp.AddAnnotation(propDef); astProp.Modifiers = ConvertModifiers(propDef.GetMethod ?? propDef.SetMethod); astProp.Name = propDef.Name; astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { astProp.Getter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, this.CancellationToken) - }; + Body = AstMethodBodyBuilder.CreateMethodBody(propDef.GetMethod, context) + }.WithAnnotation(propDef.GetMethod); } if (propDef.SetMethod != null) { astProp.Setter = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, this.CancellationToken) - }; + Body = AstMethodBodyBuilder.CreateMethodBody(propDef.SetMethod, context) + }.WithAnnotation(propDef.SetMethod); } return astProp; } @@ -454,18 +465,19 @@ namespace Decompiler CustomEventDeclaration CreateEvent(EventDefinition eventDef) { CustomEventDeclaration astEvent = new CustomEventDeclaration(); + astEvent.AddAnnotation(eventDef); astEvent.Name = eventDef.Name; astEvent.ReturnType = ConvertType(eventDef.EventType, eventDef); astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); if (eventDef.AddMethod != null) { astEvent.AddAccessor = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, this.CancellationToken) - }; + Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.AddMethod, context) + }.WithAnnotation(eventDef.AddMethod); } if (eventDef.RemoveMethod != null) { astEvent.RemoveAccessor = new Accessor { - Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, this.CancellationToken) - }; + Body = AstMethodBodyBuilder.CreateMethodBody(eventDef.RemoveMethod, context) + }.WithAnnotation(eventDef.RemoveMethod); } return astEvent; } @@ -473,6 +485,7 @@ namespace Decompiler FieldDeclaration CreateField(FieldDefinition fieldDef) { FieldDeclaration astField = new FieldDeclaration(); + astField.AddAnnotation(fieldDef); VariableInitializer initializer = new VariableInitializer(fieldDef.Name); astField.AddChild(initializer, FieldDeclaration.Roles.Variable); astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef); diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 68cff0617..cbdc86518 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -17,25 +17,32 @@ namespace Decompiler { MethodDefinition methodDef; TypeSystem typeSystem; - CancellationToken cancellationToken; + DecompilerContext context; HashSet definedLocalVars = new HashSet(); - public static BlockStatement CreateMethodBody(MethodDefinition methodDef, CancellationToken cancellationToken) + public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context) { - AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); - builder.cancellationToken = cancellationToken; - builder.methodDef = methodDef; - builder.typeSystem = methodDef.Module.TypeSystem; - if (Debugger.IsAttached) { - return builder.CreateMethodBody(); - } else { - try { + MethodDefinition oldCurrentMethod = context.CurrentMethod; + Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); + context.CurrentMethod = methodDef; + try { + AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); + builder.methodDef = methodDef; + builder.context = context; + builder.typeSystem = methodDef.Module.TypeSystem; + if (Debugger.IsAttached) { return builder.CreateMethodBody(); - } catch (OperationCanceledException) { - throw; - } catch (Exception ex) { - throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); + } else { + try { + return builder.CreateMethodBody(); + } catch (OperationCanceledException) { + throw; + } catch (Exception ex) { + throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); + } } + } finally { + context.CurrentMethod = oldCurrentMethod; } } @@ -60,15 +67,15 @@ namespace Decompiler { if (methodDef.Body == null) return null; - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); ILBlock ilMethod = new ILBlock(); ILAstBuilder astBuilder = new ILAstBuilder(); ilMethod.Body = astBuilder.Build(methodDef, true); - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); bodyGraph.Optimize(methodDef, ilMethod); - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); Dictionary typeNames = new Dictionary(); @@ -110,7 +117,7 @@ namespace Decompiler // astBlock.Children.Add(astLocalVar); } - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); CommentStatement.ReplaceAll(astBlock); // convert CommentStatements to Comments return astBlock; @@ -528,7 +535,7 @@ namespace Decompiler expr.TypeArguments = ConvertTypeArguments(cecilMethod); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false, methodDef.DeclaringType)); + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); } case Code.Ldvirtftn: { @@ -537,7 +544,7 @@ namespace Decompiler expr.TypeArguments = ConvertTypeArguments(cecilMethod); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldvirtftn").Invoke(expr) - .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true, methodDef.DeclaringType)); + .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); } case Code.Calli: throw new NotImplementedException(); @@ -557,13 +564,13 @@ namespace Decompiler if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return new Ast.ThisReferenceExpression(); } else { - return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name); + return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand); } case Code.Ldarga: if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) { return MakeRef(new Ast.ThisReferenceExpression()); } else { - return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name)); + return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name).WithAnnotation(operand)); } case Code.Ldc_I4: if (byteCode.InferredType == typeSystem.Boolean && (int)operand == 0) diff --git a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs new file mode 100644 index 000000000..108032e40 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs @@ -0,0 +1,21 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Threading; +using Mono.Cecil; + +namespace Decompiler +{ + public class DecompilerContext + { + public CancellationToken CancellationToken; + public TypeDefinition CurrentType; + public MethodDefinition CurrentMethod; + + public DecompilerContext Clone() + { + return (DecompilerContext)MemberwiseClone(); + } + } +} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs b/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs new file mode 100644 index 000000000..73b3d98fd --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs @@ -0,0 +1,69 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Diagnostics; +using ICSharpCode.NRefactory.CSharp; +using Mono.Cecil; + +namespace Decompiler.Transforms +{ + /// + /// Base class for AST visitors that need the current type/method context info. + /// + public abstract class ContextTrackingVisitor : DepthFirstAstVisitor + { + protected readonly DecompilerContext context; + + protected ContextTrackingVisitor(DecompilerContext context) + { + if (context == null) + throw new ArgumentNullException("context"); + this.context = context; + } + + public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) + { + TypeDefinition oldType = context.CurrentType; + try { + context.CurrentType = typeDeclaration.Annotation(); + return base.VisitTypeDeclaration(typeDeclaration, data); + } finally { + context.CurrentType = oldType; + } + } + + public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data) + { + Debug.Assert(context.CurrentMethod == null); + try { + context.CurrentMethod = methodDeclaration.Annotation(); + return base.VisitMethodDeclaration(methodDeclaration, data); + } finally { + context.CurrentMethod = null; + } + } + + public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data) + { + Debug.Assert(context.CurrentMethod == null); + try { + context.CurrentMethod = constructorDeclaration.Annotation(); + return base.VisitConstructorDeclaration(constructorDeclaration, data); + } finally { + context.CurrentMethod = null; + } + } + + public override object VisitAccessor(Accessor accessor, object data) + { + Debug.Assert(context.CurrentMethod == null); + try { + context.CurrentMethod = accessor.Annotation(); + return base.VisitAccessor(accessor, data); + } finally { + context.CurrentMethod = null; + } + } + } +} diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index da30a39c8..15a098b9d 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -2,9 +2,9 @@ // This code is distributed under MIT X11 license (for details please see \doc\license.txt) using System; +using System.Collections.Generic; using System.Linq; using System.Threading; - using ICSharpCode.Decompiler; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -15,10 +15,8 @@ namespace Decompiler.Transforms /// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)". /// For anonymous methods, creates an AnonymousMethodExpression. /// - public class DelegateConstruction : DepthFirstAstVisitor + public class DelegateConstruction : ContextTrackingVisitor { - public CancellationToken CancellationToken { get; set; } - internal sealed class Annotation { /// @@ -26,18 +24,16 @@ namespace Decompiler.Transforms /// public readonly bool IsVirtual; - /// - /// The method being decompiled. - /// - public readonly TypeDefinition ContainingType; - - public Annotation(bool isVirtual, TypeDefinition containingType) + public Annotation(bool isVirtual) { this.IsVirtual = isVirtual; - this.ContainingType = containingType; } } + public DelegateConstruction(DecompilerContext context) : base(context) + { + } + public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) { if (objectCreateExpression.Arguments.Count() == 2) { @@ -48,14 +44,14 @@ namespace Decompiler.Transforms IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single(); MethodReference method = methodIdent.Annotation(); if (method != null) { - if (HandleAnonymousMethod(objectCreateExpression, obj, method, annotation.ContainingType)) + if (HandleAnonymousMethod(objectCreateExpression, obj, method)) return null; // Perform the transformation to "new Action(obj.func)". obj.Remove(); methodIdent.Remove(); if (!annotation.IsVirtual && obj is ThisReferenceExpression) { // maybe it's getting the pointer of a base method? - if (method.DeclaringType != annotation.ContainingType) { + if (method.DeclaringType != context.CurrentType) { obj = new BaseReferenceExpression(); } } @@ -94,25 +90,21 @@ namespace Decompiler.Transforms return base.VisitObjectCreateExpression(objectCreateExpression, data); } - bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef, TypeDefinition containingType) + bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef) { // Anonymous methods are defined in the same assembly, so there's no need to Resolve(). MethodDefinition method = methodRef as MethodDefinition; if (method == null || !method.Name.StartsWith("<", StringComparison.Ordinal)) return false; - if (!(method.IsCompilerGenerated() || method.DeclaringType.IsCompilerGenerated())) + if (!(method.IsCompilerGenerated() || IsPotentialClosure(method.DeclaringType))) return false; - TypeDefinition methodContainingType = method.DeclaringType; - // check that methodContainingType is within containingType - while (methodContainingType != containingType) { - methodContainingType = methodContainingType.DeclaringType; - if (methodContainingType == null) - return false; - } // Decompile the anonymous method: - BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, this.CancellationToken); - TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, this.CancellationToken); + + DecompilerContext subContext = context.Clone(); + subContext.CurrentMethod = method; + BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, subContext); + TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, subContext); body.AcceptVisitor(this, null); AnonymousMethodExpression ame = new AnonymousMethodExpression(); @@ -132,5 +124,100 @@ namespace Decompiler.Transforms objectCreateExpression.ReplaceWith(ame); return true; } + + bool IsPotentialClosure(TypeDefinition potentialDisplayClass) + { + if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGenerated()) + return false; + // check that methodContainingType is within containingType + while (potentialDisplayClass != context.CurrentType) { + potentialDisplayClass = potentialDisplayClass.DeclaringType; + if (potentialDisplayClass == null) + return false; + } + return true; + } + + public override object VisitBlockStatement(BlockStatement blockStatement, object data) + { + base.VisitBlockStatement(blockStatement, data); + foreach (VariableDeclarationStatement stmt in blockStatement.Statements.OfType()) { + if (stmt.Variables.Count() != 1) + continue; + var variable = stmt.Variables.Single(); + TypeDefinition type = stmt.Type.Annotation(); + if (!IsPotentialClosure(type)) + continue; + ObjectCreateExpression oce = variable.Initializer as ObjectCreateExpression; + if (oce == null || oce.Type.Annotation() != type || oce.Arguments.Any() || !oce.Initializer.IsNull) + continue; + // Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses: + bool ok = true; + foreach (var identExpr in blockStatement.Descendants.OfType()) { + if (identExpr.Identifier == variable.Name) { + if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation() != null)) + ok = false; + } + } + if (!ok) + continue; + Dictionary dict = new Dictionary(); + // Delete the variable declaration statement: + AstNode cur; + AstNode next = stmt.NextSibling; + stmt.Remove(); + for (cur = next; cur != null; cur = next) { + next = cur.NextSibling; + + // Delete any following statements as long as they assign simple variables to the display class: + // Test for the pattern: + // "variableName.MemberName = right;" + ExpressionStatement es = cur as ExpressionStatement; + if (es == null) + break; + AssignmentExpression ae = es.Expression as AssignmentExpression; + if (ae == null || ae.Operator != AssignmentOperatorType.Assign) + break; + MemberReferenceExpression left = ae.Left as MemberReferenceExpression; + if (left == null || !IsParameter(ae.Right)) + break; + if (!(left.Target is IdentifierExpression) || (left.Target as IdentifierExpression).Identifier != variable.Name) + break; + dict[left.MemberName] = ae.Right; + es.Remove(); + } + + // Now create variables for all fields of the display class (except for those that we already handled) + foreach (FieldDefinition field in type.Fields) { + if (dict.ContainsKey(field.Name)) + continue; + VariableDeclarationStatement newVarDecl = new VariableDeclarationStatement(); + newVarDecl.Type = AstBuilder.ConvertType(field.FieldType, field); + newVarDecl.Variables = new [] { new VariableInitializer(field.Name) }; + blockStatement.InsertChildBefore(cur, newVarDecl, BlockStatement.StatementRole); + dict[field.Name] = new IdentifierExpression(field.Name); + } + + // Now figure out where the closure was accessed and use the simpler replacement expression there: + foreach (var identExpr in blockStatement.Descendants.OfType()) { + if (identExpr.Identifier == variable.Name) { + MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent; + Expression replacement; + if (dict.TryGetValue(mre.MemberName, out replacement)) { + mre.ReplaceWith(replacement.Clone()); + } + } + } + } + return null; + } + + bool IsParameter(Expression expr) + { + if (expr is ThisReferenceExpression) + return true; + IdentifierExpression ident = expr as IdentifierExpression; + return ident != null && ident.Annotation() != null; + } } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs index 89c18c575..e5b5fd644 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs @@ -9,22 +9,22 @@ namespace Decompiler.Transforms { public static class TransformationPipeline { - public static IAstVisitor[] CreatePipeline(CancellationToken cancellationToken) + public static IAstVisitor[] CreatePipeline(DecompilerContext context) { return new IAstVisitor[] { new PushNegation(), - new DelegateConstruction() { CancellationToken = cancellationToken }, + new DelegateConstruction(context), new ConvertConstructorCallIntoInitializer(), new ReplaceMethodCallsWithOperators(), }; } - public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition, CancellationToken cancellationToken) + public static void RunTransformationsUntil(AstNode node, Predicate> abortCondition, DecompilerContext context) { if (node == null) return; for (int i = 0; i < 4; i++) { - cancellationToken.ThrowIfCancellationRequested(); + context.CancellationToken.ThrowIfCancellationRequested(); if (Options.ReduceAstJumps) { node.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null); node.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null); @@ -37,8 +37,8 @@ namespace Decompiler.Transforms } } - foreach (var visitor in CreatePipeline(cancellationToken)) { - cancellationToken.ThrowIfCancellationRequested(); + foreach (var visitor in CreatePipeline(context)) { + context.CancellationToken.ThrowIfCancellationRequested(); if (abortCondition != null && abortCondition(visitor)) return; node.AcceptVisitor(visitor, null); diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index c2df0e929..dfe2997d9 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -52,8 +52,10 @@ + + diff --git a/ICSharpCode.Decompiler/Tests/TestRunner.cs b/ICSharpCode.Decompiler/Tests/TestRunner.cs index 9c3ba5c9d..099a8f3dc 100644 --- a/ICSharpCode.Decompiler/Tests/TestRunner.cs +++ b/ICSharpCode.Decompiler/Tests/TestRunner.cs @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.Tests { string code = File.ReadAllText(fileName); AssemblyDefinition assembly = Compile(code); - AstBuilder decompiler = new AstBuilder(); + AstBuilder decompiler = new AstBuilder(new DecompilerContext()); decompiler.AddAssembly(assembly); StringWriter output = new StringWriter(); decompiler.GenerateCode(new PlainTextOutput(output)); diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index ee92456e6..f7364caaa 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -45,7 +45,7 @@ namespace ICSharpCode.ILSpy internal static IEnumerable GetDebugLanguages() { string lastTransformName = "no transforms"; - foreach (Type _transformType in TransformationPipeline.CreatePipeline(CancellationToken.None).Select(v => v.GetType()).Distinct()) { + foreach (Type _transformType in TransformationPipeline.CreatePipeline(new DecompilerContext()).Select(v => v.GetType()).Distinct()) { Type transformType = _transformType; // copy for lambda yield return new CSharpLanguage { transformAbortCondition = v => transformType.IsInstanceOfType(v), @@ -69,35 +69,35 @@ namespace ICSharpCode.ILSpy public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, method.DeclaringType); codeDomBuilder.AddMethod(method); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, property.DeclaringType); codeDomBuilder.AddProperty(property); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, field.DeclaringType); codeDomBuilder.AddField(field); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, ev.DeclaringType); codeDomBuilder.AddEvent(ev); codeDomBuilder.GenerateCode(output, transformAbortCondition); } public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, type); codeDomBuilder.AddType(type); codeDomBuilder.GenerateCode(output, transformAbortCondition); } @@ -106,7 +106,7 @@ namespace ICSharpCode.ILSpy { if (options.FullDecompilation) { foreach (TypeDefinition type in assembly.MainModule.Types) { - AstBuilder codeDomBuilder = CreateAstBuilder(options); + AstBuilder codeDomBuilder = CreateAstBuilder(options, type); codeDomBuilder.AddType(type); codeDomBuilder.GenerateCode(output, transformAbortCondition); output.WriteLine(); @@ -116,9 +116,13 @@ namespace ICSharpCode.ILSpy } } - AstBuilder CreateAstBuilder(DecompilationOptions options) + AstBuilder CreateAstBuilder(DecompilationOptions options, TypeDefinition currentType) { - return new AstBuilder { CancellationToken = options.CancellationToken }; + return new AstBuilder( + new DecompilerContext { + CancellationToken = options.CancellationToken, + CurrentType = currentType + }); } public override string TypeToString(TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes) @@ -163,6 +167,9 @@ namespace ICSharpCode.ILSpy MethodDefinition method = member as MethodDefinition; if (method != null && (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)) return false; + TypeDefinition type = member as TypeDefinition; + if (type != null && type.Name.StartsWith("<>c__DisplayClass", StringComparison.Ordinal) && type.IsCompilerGenerated()) + return false; return true; } } From 2469227ed2d5d86ede3dd5698106dd0a59cee21d Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 18 Feb 2011 20:26:34 +0100 Subject: [PATCH 12/27] use separate AppDomain for decompiling BAML resources --- ILSpy/BamlDecompiler.cs | 63 ++++++++++++++++++++ ILSpy/ILSpy.csproj | 1 + ILSpy/TextView/DecompilerTextView.cs | 2 +- ILSpy/TreeNodes/ResourceEntryNode.cs | 88 ++++++++++++---------------- 4 files changed, 103 insertions(+), 51 deletions(-) create mode 100644 ILSpy/BamlDecompiler.cs diff --git a/ILSpy/BamlDecompiler.cs b/ILSpy/BamlDecompiler.cs new file mode 100644 index 000000000..df52eda97 --- /dev/null +++ b/ILSpy/BamlDecompiler.cs @@ -0,0 +1,63 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.IO; +using System.Reflection; +using System.Windows.Baml2006; +using System.Xaml; +using System.Xml; + +namespace ICSharpCode.ILSpy +{ + /// Caution: use in separate AppDomain only! + public class BamlDecompiler : MarshalByRefObject + { + public BamlDecompiler() + { + } + + public string DecompileBaml(MemoryStream bamlCode, string containingAssemblyFile) + { + bamlCode.Position = 0; + TextWriter w = new StringWriter(); + + Assembly assembly = Assembly.LoadFile(containingAssemblyFile); + + Baml2006Reader reader = new Baml2006Reader(bamlCode, new XamlReaderSettings() { ValuesMustBeString = true, LocalAssembly = assembly }); + XamlXmlWriter writer = new XamlXmlWriter(new XmlTextWriter(w) { Formatting = Formatting.Indented }, reader.SchemaContext); + while (reader.Read()) { + switch (reader.NodeType) { + case XamlNodeType.None: + + break; + case XamlNodeType.StartObject: + writer.WriteStartObject(reader.Type); + break; + case XamlNodeType.GetObject: + writer.WriteGetObject(); + break; + case XamlNodeType.EndObject: + writer.WriteEndObject(); + break; + case XamlNodeType.StartMember: + writer.WriteStartMember(reader.Member); + break; + case XamlNodeType.EndMember: + writer.WriteEndMember(); + break; + case XamlNodeType.Value: + // requires XamlReaderSettings.ValuesMustBeString = true to work properly + writer.WriteValue(reader.Value); + break; + case XamlNodeType.NamespaceDeclaration: + writer.WriteNamespace(reader.Namespace); + break; + default: + throw new Exception("Invalid value for XamlNodeType"); + } + } + return w.ToString(); + } + } +} \ No newline at end of file diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 8f9884320..0c62a786c 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -84,6 +84,7 @@ + diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 034fa8da3..62bc54fb8 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy.TextView /// When the task is cancelled before completing, the callback is not called; and any result /// of the task (including exceptions) are ignored. /// - void RunWithCancellation(Func> taskCreation, Action> taskCompleted) + public void RunWithCancellation(Func> taskCreation, Action> taskCompleted) { if (waitAdorner.Visibility != Visibility.Visible) { waitAdorner.Visibility = Visibility.Visible; diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceEntryNode.cs index 9cc66cc45..7eb246f71 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceEntryNode.cs @@ -3,12 +3,11 @@ using System; using System.IO; -using System.Windows; -using System.Windows.Baml2006; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; using System.Windows.Controls; using System.Windows.Media.Imaging; -using System.Xaml; -using System.Xml; using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; @@ -44,17 +43,23 @@ namespace ICSharpCode.ILSpy.TreeNodes internal override bool View(DecompilerTextView textView) { AvalonEditTextOutput output = new AvalonEditTextOutput(); - try { - if (LoadImage(output)) - textView.Show(output, null); - else if (LoadBaml(output)) { - textView.Show(output, HighlightingManager.Instance.GetDefinitionByExtension(".xml")); - } else - return false; - } catch (Exception ex) { - output.Write(ex.ToString()); - textView.Show(output, null); - } + IHighlightingDefinition highlighting = null; + + textView.RunWithCancellation( + token => Task.Factory.StartNew( + () => { + try { + if (LoadImage(output)) + highlighting = null; + else if (LoadBaml(output)) + highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); + } catch (Exception ex) { + output.Write(ex.ToString()); + } + return output; + }), + t => textView.Show(t.Result, highlighting) + ); return true; } @@ -77,42 +82,25 @@ namespace ICSharpCode.ILSpy.TreeNodes bool LoadBaml(AvalonEditTextOutput output) { + var asm = this.Ancestors().OfType().FirstOrDefault().LoadedAssembly; + + // Construct and initialize settings for a second AppDomain. + AppDomainSetup bamlDecompilerAppDomainSetup = new AppDomainSetup(); + bamlDecompilerAppDomainSetup.ApplicationBase = "file:///" + Path.GetDirectoryName(asm.FileName); + bamlDecompilerAppDomainSetup.DisallowBindingRedirects = false; + bamlDecompilerAppDomainSetup.DisallowCodeDownload = true; + bamlDecompilerAppDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; + + // Create the second AppDomain. + AppDomain bamlDecompilerAppDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup); + + BamlDecompiler decompiler = (BamlDecompiler)bamlDecompilerAppDomain.CreateInstanceAndUnwrap(Assembly.GetEntryAssembly().FullName, typeof(BamlDecompiler).FullName); + + MemoryStream bamlStream = new MemoryStream(); value.Position = 0; - TextWriter w = new StringWriter(); - Baml2006Reader reader = new Baml2006Reader(value, new XamlReaderSettings() { ValuesMustBeString = true }); - XamlXmlWriter writer = new XamlXmlWriter(new XmlTextWriter(w) { Formatting = Formatting.Indented }, reader.SchemaContext); - while (reader.Read()) { - switch (reader.NodeType) { - case XamlNodeType.None: - - break; - case XamlNodeType.StartObject: - writer.WriteStartObject(reader.Type); - break; - case XamlNodeType.GetObject: - writer.WriteGetObject(); - break; - case XamlNodeType.EndObject: - writer.WriteEndObject(); - break; - case XamlNodeType.StartMember: - writer.WriteStartMember(reader.Member); - break; - case XamlNodeType.EndMember: - writer.WriteEndMember(); - break; - case XamlNodeType.Value: - // requires XamlReaderSettings.ValuesMustBeString = true to work properly - writer.WriteValue(reader.Value); - break; - case XamlNodeType.NamespaceDeclaration: - writer.WriteNamespace(reader.Namespace); - break; - default: - throw new Exception("Invalid value for XamlNodeType"); - } - } - output.Write(w.ToString()); + value.CopyTo(bamlStream); + + output.Write(decompiler.DecompileBaml(bamlStream, asm.FileName)); return true; } From bba65cf3ae93a48e180aa554bc04d55c25b666bb Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 18 Feb 2011 20:52:06 +0100 Subject: [PATCH 13/27] do not load images on a background thread --- ILSpy/TreeNodes/ResourceEntryNode.cs | 32 +++++++++++++++------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceEntryNode.cs index 7eb246f71..1cff10836 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceEntryNode.cs @@ -45,21 +45,23 @@ namespace ICSharpCode.ILSpy.TreeNodes AvalonEditTextOutput output = new AvalonEditTextOutput(); IHighlightingDefinition highlighting = null; - textView.RunWithCancellation( - token => Task.Factory.StartNew( - () => { - try { - if (LoadImage(output)) - highlighting = null; - else if (LoadBaml(output)) - highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); - } catch (Exception ex) { - output.Write(ex.ToString()); - } - return output; - }), - t => textView.Show(t.Result, highlighting) - ); + if (LoadImage(output)) { + textView.Show(output, highlighting); + } else { + textView.RunWithCancellation( + token => Task.Factory.StartNew( + () => { + try { + if (LoadBaml(output)) + highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); + } catch (Exception ex) { + output.Write(ex.ToString()); + } + return output; + }), + t => textView.Show(t.Result, highlighting) + ); + } return true; } From e259a513e15d0458ff9fe7b7513f759946483fff Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 20:08:34 +0100 Subject: [PATCH 14/27] Remove unused code. --- .../Mono.Cecil.Rocks/MyRocks.cs | 241 ------------------ 1 file changed, 241 deletions(-) diff --git a/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs b/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs index 0fa8b9ec5..215051edf 100644 --- a/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs +++ b/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs @@ -15,15 +15,6 @@ namespace Decompiler.Rocks { static class MyRocks { - static public TypeReference TypeVoid = GetCecilType(typeof(void)); - static public TypeReference TypeObject = GetCecilType(typeof(Object)); - static public TypeReference TypeException = GetCecilType(typeof(Exception)); - static public TypeReference TypeBool = GetCecilType(typeof(bool)); - static public TypeReference TypeInt32 = GetCecilType(typeof(Int32)); - static public TypeReference TypeString = GetCecilType(typeof(string)); - static public TypeReference TypeZero = GetCecilType(typeof(Int32)); - static public TypeReference TypeOne = GetCecilType(typeof(Int32)); - public static List CutRange(this List list, int start, int count) { List ret = new List(count); @@ -123,237 +114,5 @@ namespace Decompiler.Rocks default: throw new Exception("Unknown push behaviour: " + inst.OpCode.StackBehaviourPush); } } - - static public TypeReference GetCecilType(Type type) - { - return new TypeReference(type.Name, type.Namespace, null, null, type.IsValueType); - } - - static public TypeReference GetTypeInternal(this Instruction inst, MethodDefinition methodDef, List args) - { - OpCode opCode = inst.OpCode; - object operand = inst.Operand; - TypeReference operandAsTypeRef = operand as TypeReference; - //ByteCode operandAsByteCode = operand as ByteCode; - //string operandAsByteCodeLabel = operand is ByteCode ? String.Format("IL_{0:X2}", ((ByteCode)operand).Offset) : null; - TypeReference arg1 = args.Count >= 1 ? args[0] : null; - TypeReference arg2 = args.Count >= 2 ? args[1] : null; - TypeReference arg3 = args.Count >= 3 ? args[2] : null; - - switch(opCode.Code) { - #region Arithmetic - case Code.Add: - case Code.Add_Ovf: - case Code.Add_Ovf_Un: - case Code.Div: - case Code.Div_Un: - case Code.Mul: - case Code.Mul_Ovf: - case Code.Mul_Ovf_Un: - case Code.Rem: - case Code.Rem_Un: - case Code.Sub: - case Code.Sub_Ovf: - case Code.Sub_Ovf_Un: - case Code.And: - case Code.Xor: - case Code.Shl: - case Code.Shr: - case Code.Shr_Un: return TypeInt32; - - case Code.Neg: return TypeInt32; - case Code.Not: return TypeInt32; - #endregion - #region Arrays - case Code.Newarr: - return new ArrayType(operandAsTypeRef); - - case Code.Ldlen: return TypeInt32; - - case Code.Ldelem_I: - case Code.Ldelem_I1: - case Code.Ldelem_I2: - case Code.Ldelem_I4: - case Code.Ldelem_I8: return TypeInt32; - case Code.Ldelem_U1: - case Code.Ldelem_U2: - case Code.Ldelem_U4: - case Code.Ldelem_R4: - case Code.Ldelem_R8: throw new NotImplementedException(); - case Code.Ldelem_Ref: - if (arg1 is ArrayType) { - return ((ArrayType)arg1).ElementType; - } else { - throw new NotImplementedException(); - } - case Code.Ldelem_Any: - case Code.Ldelema: throw new NotImplementedException(); - - case Code.Stelem_I: - case Code.Stelem_I1: - case Code.Stelem_I2: - case Code.Stelem_I4: - case Code.Stelem_I8: - case Code.Stelem_R4: - case Code.Stelem_R8: - case Code.Stelem_Ref: - case Code.Stelem_Any: return TypeVoid; - #endregion - #region Branching - case Code.Br: - case Code.Brfalse: - case Code.Brtrue: - case Code.Beq: - case Code.Bge: - case Code.Bge_Un: - case Code.Bgt: - case Code.Bgt_Un: - case Code.Ble: - case Code.Ble_Un: - case Code.Blt: - case Code.Blt_Un: - case Code.Bne_Un: return TypeVoid; - #endregion - #region Comparison - case Code.Ceq: - case Code.Cgt: - case Code.Cgt_Un: - case Code.Clt: - case Code.Clt_Un: return TypeBool; - #endregion - #region Conversions - case Code.Conv_I: - case Code.Conv_I1: - case Code.Conv_I2: - case Code.Conv_I4: - case Code.Conv_I8: - case Code.Conv_U: - case Code.Conv_U1: - case Code.Conv_U2: - case Code.Conv_U4: - case Code.Conv_U8: - case Code.Conv_R4: - case Code.Conv_R8: - case Code.Conv_R_Un: - - case Code.Conv_Ovf_I: - case Code.Conv_Ovf_I1: - case Code.Conv_Ovf_I2: - case Code.Conv_Ovf_I4: - case Code.Conv_Ovf_I8: - case Code.Conv_Ovf_U: - case Code.Conv_Ovf_U1: - case Code.Conv_Ovf_U2: - case Code.Conv_Ovf_U4: - case Code.Conv_Ovf_U8: - - case Code.Conv_Ovf_I_Un: - case Code.Conv_Ovf_I1_Un: - case Code.Conv_Ovf_I2_Un: - case Code.Conv_Ovf_I4_Un: - case Code.Conv_Ovf_I8_Un: - case Code.Conv_Ovf_U_Un: - case Code.Conv_Ovf_U1_Un: - case Code.Conv_Ovf_U2_Un: - case Code.Conv_Ovf_U4_Un: - case Code.Conv_Ovf_U8_Un: return TypeInt32; - #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.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(); - #endregion - case Code.Arglist: throw new NotImplementedException(); - case Code.Box: throw new NotImplementedException(); - case Code.Break: throw new NotImplementedException(); - case Code.Call: return ((MethodReference)operand).ReturnType; - case Code.Calli: throw new NotImplementedException(); - case Code.Callvirt: return ((MethodReference)operand).ReturnType; - case Code.Castclass: throw new NotImplementedException(); - 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.Dup: throw new NotImplementedException(); - case Code.Endfilter: throw new NotImplementedException(); - case Code.Endfinally: throw new NotImplementedException(); - case Code.Initblk: throw new NotImplementedException(); - case Code.Initobj: throw new NotImplementedException(); - case Code.Isinst: throw new NotImplementedException(); - case Code.Jmp: throw new NotImplementedException(); - case Code.Ldarg: - TypeReference typeRef = ((ParameterDefinition)operand).ParameterType; - // 'this' returns null; TODO: Return proper type of this - return typeRef ?? TypeObject; - case Code.Ldarga: throw new NotImplementedException(); - case Code.Ldc_I4: - if ((int)operand == 0) { - return TypeZero; - } else if ((int)operand == 1) { - return TypeOne; - } else { - return TypeInt32; - } - case Code.Ldc_I8: throw new NotImplementedException(); - case Code.Ldc_R4: throw new NotImplementedException(); - case Code.Ldc_R8: throw new NotImplementedException(); - case Code.Ldfld: return ((FieldDefinition)operand).FieldType; - case Code.Ldflda: throw new NotImplementedException(); - case Code.Ldftn: throw new NotImplementedException(); - case Code.Ldloc: return ((VariableDefinition)operand).VariableType; - case Code.Ldloca: throw new NotImplementedException(); - case Code.Ldnull: throw new NotImplementedException(); - case Code.Ldobj: throw new NotImplementedException(); - case Code.Ldsfld: throw new NotImplementedException(); - case Code.Ldsflda: throw new NotImplementedException(); - case Code.Ldstr: return TypeString; - case Code.Ldtoken: throw new NotImplementedException(); - case Code.Ldvirtftn: throw new NotImplementedException(); - case Code.Leave: throw new NotImplementedException(); - case Code.Localloc: throw new NotImplementedException(); - case Code.Mkrefany: throw new NotImplementedException(); - case Code.Newobj: throw new NotImplementedException(); - case Code.No: throw new NotImplementedException(); - case Code.Nop: return TypeVoid; - case Code.Or: throw new NotImplementedException(); - case Code.Pop: throw new NotImplementedException(); - case Code.Readonly: throw new NotImplementedException(); - case Code.Refanytype: throw new NotImplementedException(); - case Code.Refanyval: throw new NotImplementedException(); - case Code.Ret: return TypeVoid; - case Code.Rethrow: throw new NotImplementedException(); - case Code.Sizeof: throw new NotImplementedException(); - case Code.Starg: throw new NotImplementedException(); - case Code.Stfld: throw new NotImplementedException(); - case Code.Stloc: return TypeVoid; - case Code.Stobj: throw new NotImplementedException(); - case Code.Stsfld: throw new NotImplementedException(); - case Code.Switch: throw new NotImplementedException(); - case Code.Tail: throw new NotImplementedException(); - case Code.Throw: throw new NotImplementedException(); - 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(); - default: throw new Exception("Unknown OpCode: " + opCode); - } - } } } From 7dd487f8ab0c59a381acdc91d500c630f0645a24 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 20:09:08 +0100 Subject: [PATCH 15/27] Don't hide display classes from the tree view in the intermediate C# steps. --- ILSpy/CSharpLanguage.cs | 10 ++++++++-- ILSpy/ILAstLanguage.cs | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index f7364caaa..67cf419f4 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -35,6 +35,7 @@ namespace ICSharpCode.ILSpy public class CSharpLanguage : Language { string name = "C#"; + bool showAllMembers; Predicate> transformAbortCondition = null; public CSharpLanguage() @@ -49,12 +50,14 @@ namespace ICSharpCode.ILSpy Type transformType = _transformType; // copy for lambda yield return new CSharpLanguage { transformAbortCondition = v => transformType.IsInstanceOfType(v), - name = "C# - " + lastTransformName + name = "C# - " + lastTransformName, + showAllMembers = true }; lastTransformName = "after " + transformType.Name; } yield return new CSharpLanguage { - name = "C# - " + lastTransformName + name = "C# - " + lastTransformName, + showAllMembers = true }; } #endif @@ -164,6 +167,9 @@ namespace ICSharpCode.ILSpy public override bool ShowMember(MemberReference member) { + if (showAllMembers) { + return true; + } MethodDefinition method = member as MethodDefinition; if (method != null && (method.IsGetter || method.IsSetter || method.IsAddOn || method.IsRemoveOn)) return false; diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/ILAstLanguage.cs index 7b19986ad..526567ba8 100644 --- a/ILSpy/ILAstLanguage.cs +++ b/ILSpy/ILAstLanguage.cs @@ -91,5 +91,12 @@ namespace ICSharpCode.ILSpy return ".il"; } } + + public override string TypeToString(TypeReference t, bool includeNamespace, ICustomAttributeProvider attributeProvider) + { + PlainTextOutput output = new PlainTextOutput(); + t.WriteTo(output, true, shortName: !includeNamespace); + return output.ToString(); + } } } From 99123404144135aefb47d29bb14c2d548b2f67ee Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 21:38:16 +0100 Subject: [PATCH 16/27] NRefactory: remove collection setters from AST; expose AstNodeCollection instead. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 4 +- .../Ast/AstMethodBodyBuilder.cs | 71 ++++---- .../ConvertConstructorCallIntoInitializer.cs | 4 +- .../Ast/Transforms/DelegateConstruction.cs | 18 +- .../ReplaceMethodCallsWithOperators.cs | 4 +- .../Ast/Transforms/RestoreLoop.cs | 10 +- ILSpy/CSharpLanguage.cs | 6 +- .../CSharp/Ast/AstNode.cs | 31 +--- .../CSharp/Ast/AstNodeCollection.cs | 156 ++++++++++++++++++ .../CSharp/Ast/ComposedType.cs | 3 +- .../Expressions/AnonymousMethodExpression.cs | 5 +- .../Ast/Expressions/ArgListExpression.cs | 5 +- .../Ast/Expressions/ArrayCreateExpression.cs | 6 +- .../Expressions/ArrayInitializerExpression.cs | 5 +- .../CSharp/Ast/Expressions/Expression.cs | 39 +++-- .../Ast/Expressions/IdentifierExpression.cs | 5 +- .../Ast/Expressions/IndexerExpression.cs | 5 +- .../Ast/Expressions/InvocationExpression.cs | 5 +- .../Ast/Expressions/LambdaExpression.cs | 5 +- .../Expressions/MemberReferenceExpression.cs | 5 +- .../Ast/Expressions/ObjectCreateExpression.cs | 5 +- .../Expressions/PointerReferenceExpression.cs | 5 +- .../CSharp/Ast/Expressions/QueryExpression.cs | 6 +- .../CSharp/Ast/GeneralScope/Attribute.cs | 5 +- .../Ast/GeneralScope/AttributeSection.cs | 5 +- .../CSharp/Ast/GeneralScope/Constraint.cs | 5 +- .../Ast/GeneralScope/DelegateDeclaration.cs | 11 +- .../Ast/GeneralScope/NamespaceDeclaration.cs | 10 +- .../Ast/GeneralScope/TypeDeclaration.cs | 14 +- .../CSharp/Ast/MemberType.cs | 5 +- .../CSharp/Ast/SimpleType.cs | 5 +- .../CSharp/Ast/Statements/BlockStatement.cs | 5 +- .../CSharp/Ast/Statements/FixedStatement.cs | 5 +- .../CSharp/Ast/Statements/ForStatement.cs | 8 +- .../CSharp/Ast/Statements/SwitchStatement.cs | 11 +- .../Ast/Statements/TryCatchStatement.cs | 5 +- .../VariableDeclarationStatement.cs | 15 +- .../CSharp/Ast/TypeMembers/AttributedNode.cs | 3 +- .../Ast/TypeMembers/ConstructorDeclaration.cs | 8 +- .../Ast/TypeMembers/EventDeclaration.cs | 5 +- .../Ast/TypeMembers/FieldDeclaration.cs | 5 +- .../Ast/TypeMembers/IndexerDeclaration.cs | 5 +- .../Ast/TypeMembers/MethodDeclaration.cs | 11 +- .../Ast/TypeMembers/OperatorDeclaration.cs | 5 +- .../Ast/TypeMembers/ParameterDeclaration.cs | 5 +- .../CSharp/Parser/CSharpParser.cs | 4 +- .../ICSharpCode.NRefactory.csproj | 3 +- 47 files changed, 335 insertions(+), 236 deletions(-) create mode 100644 NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index d4c841fd1..807e7d78a 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -420,7 +420,7 @@ namespace Decompiler astMethod.AddAnnotation(methodDef); astMethod.Name = methodDef.Name; astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType); - astMethod.Parameters = MakeParameters(methodDef.Parameters); + astMethod.Parameters.AddRange(MakeParameters(methodDef.Parameters)); if (!methodDef.DeclaringType.IsInterface) { astMethod.Modifiers = ConvertModifiers(methodDef); astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); @@ -437,7 +437,7 @@ namespace Decompiler // don't show visibility for static ctors astMethod.Modifiers &= ~Modifiers.VisibilityMask; } - astMethod.Parameters = MakeParameters(methodDef.Parameters); + astMethod.Parameters.AddRange(MakeParameters(methodDef.Parameters)); astMethod.Body = AstMethodBodyBuilder.CreateMethodBody(methodDef, context); return astMethod; } diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index cbdc86518..c0fa88b57 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -193,27 +193,27 @@ namespace Decompiler ILSwitch ilSwitch = (ILSwitch)node; SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition.Arguments[0]) }; for (int i = 0; i < ilSwitch.CaseBlocks.Count; i++) { - switchStmt.AddChild(new SwitchSection() { - CaseLabels = new[] { new CaseLabel() { Expression = new PrimitiveExpression(i) } }, - Statements = new[] { TransformBlock(ilSwitch.CaseBlocks[i]) } - }, SwitchStatement.SwitchSectionRole); + SwitchSection section = new SwitchSection(); + section.CaseLabels.Add(new CaseLabel() { Expression = new PrimitiveExpression(i) }); + section.Statements.Add(TransformBlock(ilSwitch.CaseBlocks[i])); + switchStmt.SwitchSections.Add(section); } yield return switchStmt; } else if (node is ILTryCatchBlock) { ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node); - List catchClauses = new List(); + var tryCatchStmt = new Ast.TryCatchStatement(); + tryCatchStmt.TryBlock = TransformBlock(tryCatchNode.TryBlock); foreach (var catchClause in tryCatchNode.CatchBlocks) { - catchClauses.Add(new Ast.CatchClause { - Type = AstBuilder.ConvertType(catchClause.ExceptionType), - VariableName = "exception", - Body = TransformBlock(catchClause) - }); + tryCatchStmt.CatchClauses.Add( + new Ast.CatchClause { + Type = AstBuilder.ConvertType(catchClause.ExceptionType), + VariableName = "exception", + Body = TransformBlock(catchClause) + }); } - yield return new Ast.TryCatchStatement { - TryBlock = TransformBlock(tryCatchNode.TryBlock), - CatchClauses = catchClauses, - FinallyBlock = tryCatchNode.FinallyBlock != null ? TransformBlock(tryCatchNode.FinallyBlock) : null - }; + if (tryCatchNode.FinallyBlock != null) + tryCatchStmt.FinallyBlock = TransformBlock(tryCatchNode.FinallyBlock); + yield return tryCatchStmt; } else if (node is ILBlock) { yield return TransformBlock((ILBlock)node); } else { @@ -402,12 +402,12 @@ namespace Decompiler #endregion #region Arrays case Code.Newarr: - operandAsTypeRef = operandAsTypeRef.MakeArrayType(0); - return new Ast.ArrayCreateExpression { - Type = operandAsTypeRef, - Arguments = new Expression[] {arg1} - }; - + { + var ace = new Ast.ArrayCreateExpression(); + ace.Type = operandAsTypeRef; + ace.Arguments.Add(arg1); + return ace; + } case Code.Ldlen: return arg1.Member("Length"); @@ -532,7 +532,7 @@ namespace Decompiler { Cecil.MethodReference cecilMethod = ((MethodReference)operand); var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments = ConvertTypeArguments(cecilMethod); + expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldftn").Invoke(expr) .WithAnnotation(new Transforms.DelegateConstruction.Annotation(false)); @@ -541,7 +541,7 @@ namespace Decompiler { Cecil.MethodReference cecilMethod = ((MethodReference)operand); var expr = new Ast.IdentifierExpression(cecilMethod.Name); - expr.TypeArguments = ConvertTypeArguments(cecilMethod); + expr.TypeArguments.AddRange(ConvertTypeArguments(cecilMethod)); expr.AddAnnotation(cecilMethod); return new IdentifierExpression("ldvirtftn").Invoke(expr) .WithAnnotation(new Transforms.DelegateConstruction.Annotation(true)); @@ -634,18 +634,21 @@ namespace Decompiler case Code.Localloc: throw new NotImplementedException(); case Code.Mkrefany: throw new NotImplementedException(); case Code.Newobj: - Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; - // TODO: Ensure that the corrent overloaded constructor is called - if (declaringType is ArrayType) { + { + Cecil.TypeReference declaringType = ((MethodReference)operand).DeclaringType; + // TODO: Ensure that the corrent overloaded constructor is called + + /*if (declaringType is ArrayType) { shouldn't this be newarr? return new Ast.ArrayCreateExpression { Type = AstBuilder.ConvertType((ArrayType)declaringType), Arguments = args }; + }*/ + var oce = new Ast.ObjectCreateExpression(); + oce.Type = AstBuilder.ConvertType(declaringType); + oce.Arguments.AddRange(args); + return oce.WithAnnotation(operand); } - return new Ast.ObjectCreateExpression { - Type = AstBuilder.ConvertType(declaringType), - Arguments = args - }; case Code.No: throw new NotImplementedException(); case Code.Nop: return null; case Code.Pop: return arg1; @@ -667,10 +670,10 @@ namespace Decompiler ILVariable locVar = (ILVariable)operand; if (!definedLocalVars.Contains(locVar)) { definedLocalVars.Add(locVar); - return new Ast.VariableDeclarationStatement() { - Type = locVar.Type != null ? AstBuilder.ConvertType(locVar.Type) : new Ast.PrimitiveType("var"), - Variables = new[] { new Ast.VariableInitializer(locVar.Name, arg1) } - }; + return new Ast.VariableDeclarationStatement( + locVar.Type != null ? AstBuilder.ConvertType(locVar.Type) : new Ast.PrimitiveType("var"), + locVar.Name, + arg1); } else { return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name), arg1); } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs index caf6b4d91..aad95a607 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs @@ -28,9 +28,7 @@ namespace Decompiler.Transforms else return null; // Move arguments from invocation to initializer: - var arguments = invocation.Arguments.ToArray(); - invocation.Arguments = null; - ci.Arguments = arguments; + invocation.Arguments.MoveTo(ci.Arguments); // Add the initializer: constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation()); // Remove the statement: diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 15a098b9d..23c9fcd4c 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -74,15 +74,13 @@ namespace Decompiler.Transforms } } // now transform the identifier into a member reference - var typeArguments = methodIdent.TypeArguments.ToArray(); - methodIdent.TypeArguments = null; - MemberReferenceExpression mre = new MemberReferenceExpression { - Target = obj, - MemberName = methodIdent.Identifier, - TypeArguments = typeArguments - }; + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = obj; + mre.MemberName = methodIdent.Identifier; + methodIdent.TypeArguments.MoveTo(mre.TypeArguments); mre.AddAnnotation(method); - objectCreateExpression.Arguments = new [] { mre }; + objectCreateExpression.Arguments.Clear(); + objectCreateExpression.Arguments.Add(mre); return null; } } @@ -112,7 +110,7 @@ namespace Decompiler.Transforms ame.HasParameterList = false; } else { ame.HasParameterList = true; - ame.Parameters = AstBuilder.MakeParameters(method.Parameters); + ame.Parameters.AddRange(AstBuilder.MakeParameters(method.Parameters)); } ame.Body = body; // Replace all occurrences of 'this' in the method body with the delegate's target: @@ -193,7 +191,7 @@ namespace Decompiler.Transforms continue; VariableDeclarationStatement newVarDecl = new VariableDeclarationStatement(); newVarDecl.Type = AstBuilder.ConvertType(field.FieldType, field); - newVarDecl.Variables = new [] { new VariableInitializer(field.Name) }; + newVarDecl.Variables.Add(new VariableInitializer(field.Name)); blockStatement.InsertChildBefore(cur, newVarDecl, BlockStatement.StatementRole); dict[field.Name] = new IdentifierExpression(field.Name); } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs index 25215179f..05ad2063d 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs @@ -25,7 +25,7 @@ namespace Decompiler.Transforms // Reduce "String.Concat(a, b)" to "a + b" if (methodRef != null && methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2) { - invocationExpression.Arguments = null; // detach arguments from invocationExpression + invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression Expression expr = arguments[0]; for (int i = 1; i < arguments.Length; i++) { expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]); @@ -48,7 +48,7 @@ namespace Decompiler.Transforms BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(methodRef.Name); if (bop != null && arguments.Length == 2) { - invocationExpression.Arguments = null; // detach arguments from invocationExpression + invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression invocationExpression.ReplaceWith( new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).WithAnnotation(methodRef) ); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs b/ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs index ccd9f14fd..d177fc923 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs @@ -1,6 +1,6 @@ -using System; -using System.Linq; -using ICSharpCode.NRefactory.CSharp; +using System; +using System.Linq; +using ICSharpCode.NRefactory.CSharp; namespace Decompiler.Transforms.Ast { @@ -15,7 +15,7 @@ namespace Decompiler.Transforms.Ast VariableDeclarationStatement varDeclr = forStatement.PrevSibling as VariableDeclarationStatement; if (varDeclr != null) { varDeclr.ReplaceWith(Statement.Null); - forStatement.Initializers = new Statement[] { varDeclr }; + forStatement.Initializers.Add(varDeclr); } } @@ -63,7 +63,7 @@ namespace Decompiler.Transforms.Ast if (lastStmt != null && (lastStmt.Expression is AssignmentExpression || lastStmt.Expression is UnaryOperatorExpression)) { lastStmt.Remove(); - forStatement.Iterators = new Statement[] { lastStmt }; + forStatement.Iterators.Add(lastStmt); } } diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 67cf419f4..6bb41c459 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -157,9 +157,9 @@ namespace ICSharpCode.ILSpy base.VisitMemberType(memberType, data); SimpleType st = memberType.Target as SimpleType; if (st != null && !st.TypeArguments.Any()) { - var ta = memberType.TypeArguments.ToArray(); - memberType.TypeArguments = null; - memberType.ReplaceWith(new SimpleType { Identifier = memberType.MemberName, TypeArguments = ta }); + SimpleType newSt = new SimpleType(memberType.MemberName); + memberType.TypeArguments.MoveTo(newSt.TypeArguments); + memberType.ReplaceWith(newSt); } return null; } diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs index 5a81449d2..bfafd1633 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs @@ -1,4 +1,4 @@ -// +// // AstNode.cs // // Author: @@ -164,16 +164,9 @@ namespace ICSharpCode.NRefactory.CSharp return role.NullObject; } - public IEnumerable GetChildrenByRole(Role role) where T : AstNode + public AstNodeCollection GetChildrenByRole(Role role) where T : AstNode { - AstNode next; - for (AstNode cur = firstChild; cur != null; cur = next) { - // Remember next before yielding cur. - // This allows removing/replacing nodes while iterating through the list. - next = cur.nextSibling; - if (cur.role == role) - yield return (T)cur; - } + return new AstNodeCollection(this, role); } protected void SetChildByRole(Role role, T newChild) where T : AstNode @@ -185,24 +178,6 @@ namespace ICSharpCode.NRefactory.CSharp oldChild.ReplaceWith(newChild); } - protected void SetChildrenByRole(Role role, IEnumerable newChildren) where T : AstNode - { - // Evaluate 'newChildren' first, since it might change when we remove the old children - // Example: SetChildren(role, GetChildrenByRole(role)); - if (newChildren != null) - newChildren = newChildren.ToList(); - - // remove old children - foreach (AstNode node in GetChildrenByRole(role)) - node.Remove(); - // add new children - if (newChildren != null) { - foreach (T node in newChildren) { - AddChild(node, role); - } - } - } - public void AddChild(T child, Role role) where T : AstNode { if (role == null) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs new file mode 100644 index 000000000..89556dca8 --- /dev/null +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNodeCollection.cs @@ -0,0 +1,156 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace ICSharpCode.NRefactory.CSharp +{ + /// + /// Represents the children of an AstNode that have a specific role. + /// + public struct AstNodeCollection : ICollection where T : AstNode + { + readonly AstNode node; + readonly Role role; + + public AstNodeCollection(AstNode node, Role role) + { + if (node == null) + throw new ArgumentNullException("node"); + if (role == null) + throw new ArgumentNullException("role"); + this.node = node; + this.role = role; + } + + public int Count { + get { + var e = GetEnumerator(); + int count = 0; + while (e.MoveNext()) + count++; + return count; + } + } + + public void Add(T element) + { + node.AddChild(element, role); + } + + public void AddRange(IEnumerable nodes) + { + // Evaluate 'nodes' first, since it might change when we add the new children + // Example: collection.AddRange(collection); + if (nodes != null) { + foreach (T node in nodes.ToList()) + Add(node); + } + } + + public void AddRange(T[] nodes) + { + // Fast overload for arrays - we don't need to create a copy + if (nodes != null) { + foreach (T node in nodes) + Add(node); + } + } + + public void ReplaceWith(IEnumerable nodes) + { + // Evaluate 'nodes' first, since it might change when we call Clear() + // Example: collection.ReplaceWith(collection); + if (nodes != null) + nodes = nodes.ToList(); + Clear(); + foreach (T node in nodes) + Add(node); + } + + public void MoveTo(ICollection targetCollection) + { + foreach (T node in this) { + node.Remove(); + targetCollection.Add(node); + } + } + + public bool Contains(T element) + { + return element != null && element.Parent == node && element.Role == role; + } + + public bool Remove(T element) + { + if (Contains(element)) { + element.Remove(); + return true; + } else { + return false; + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + foreach (T item in this) + array[arrayIndex++] = item; + } + + public void Clear() + { + foreach (T item in this) + item.Remove(); + } + + bool ICollection.IsReadOnly { + get { return false; } + } + + public IEnumerator GetEnumerator() + { + AstNode next; + for (AstNode cur = node.FirstChild; cur != null; cur = next) { + // Remember next before yielding cur. + // This allows removing/replacing nodes while iterating through the list. + next = cur.NextSibling; + if (cur.Role == role) + yield return (T)cur; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #region Equals and GetHashCode implementation + public override bool Equals(object obj) + { + if (obj is AstNodeCollection) { + return ((AstNodeCollection)obj) == this; + } else { + return false; + } + } + + public override int GetHashCode() + { + return node.GetHashCode() ^ role.GetHashCode(); + } + + public static bool operator ==(AstNodeCollection left, AstNodeCollection right) + { + return left.role == right.role && left.node == right.node; + } + + public static bool operator !=(AstNodeCollection left, AstNodeCollection right) + { + return !(left.role == right.role && left.node == right.node); + } + #endregion + } +} diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs index 0a22debe1..e4316de30 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs @@ -67,9 +67,8 @@ namespace ICSharpCode.NRefactory.CSharp } } - public IEnumerable ArraySpecifiers { + public AstNodeCollection ArraySpecifiers { get { return GetChildrenByRole (ArraySpecifierRole); } - set { SetChildrenByRole (ArraySpecifierRole, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AnonymousMethodExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AnonymousMethodExpression.cs index 10d497eda..324bbff88 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AnonymousMethodExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/AnonymousMethodExpression.cs @@ -1,4 +1,4 @@ -// +// // AnonymousMethodExpression.cs // // Author: @@ -47,9 +47,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LPar); } } - public IEnumerable Parameters { + public AstNodeCollection Parameters { get { return GetChildrenByRole (Roles.Parameter); } - set { SetChildrenByRole (Roles.Parameter, value); } } public CSharpTokenNode RParToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArgListExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArgListExpression.cs index 533bf2fb8..d18d543f1 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArgListExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArgListExpression.cs @@ -1,4 +1,4 @@ -// +// // ArgListExpression.cs // // Author: @@ -45,9 +45,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LPar); } } - public IEnumerable Arguments { + public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } - set { SetChildrenByRole(Roles.Argument, value); } } public CSharpTokenNode RParToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArrayCreateExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArrayCreateExpression.cs index 7befd673e..e4122170e 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArrayCreateExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArrayCreateExpression.cs @@ -16,18 +16,16 @@ namespace ICSharpCode.NRefactory.CSharp set { SetChildByRole (Roles.Type, value); } } - public IEnumerable Arguments { + public AstNodeCollection Arguments { get { return GetChildrenByRole (Roles.Argument); } - set { SetChildrenByRole (Roles.Argument, value); } } /// /// Gets additional array ranks (those without size info). /// Empty for "new int[5,1]"; will contain a single element for "new int[5][]". /// - public IEnumerable AdditionalArraySpecifiers { + public AstNodeCollection AdditionalArraySpecifiers { get { return GetChildrenByRole(AdditionalArraySpecifierRole); } - set { SetChildrenByRole (AdditionalArraySpecifierRole, value); } } public ArrayInitializerExpression Initializer { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArrayInitializerExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArrayInitializerExpression.cs index 8f897048c..a654ecff7 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArrayInitializerExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ArrayInitializerExpression.cs @@ -1,4 +1,4 @@ -// +// // ArrayInitializerExpression.cs // // Author: @@ -55,9 +55,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LBrace); } } - public IEnumerable Elements { + public AstNodeCollection Elements { get { return GetChildrenByRole(Roles.Expression); } - set { SetChildrenByRole(Roles.Expression, value); } } public CSharpTokenNode RBraceToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs index 6a6e3d9a3..b82530464 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/Expression.cs @@ -66,7 +66,10 @@ namespace ICSharpCode.NRefactory.CSharp /// public IndexerExpression Indexer(IEnumerable arguments) { - return new IndexerExpression { Target = this, Arguments = arguments }; + IndexerExpression expr = new IndexerExpression(); + expr.Target = this; + expr.Arguments.AddRange(arguments); + return expr; } /// @@ -74,7 +77,10 @@ namespace ICSharpCode.NRefactory.CSharp /// public IndexerExpression Indexer(params Expression[] arguments) { - return new IndexerExpression { Target = this, Arguments = arguments }; + IndexerExpression expr = new IndexerExpression(); + expr.Target = this; + expr.Arguments.AddRange(arguments); + return expr; } /// @@ -98,14 +104,14 @@ namespace ICSharpCode.NRefactory.CSharp /// public InvocationExpression Invoke(string methodName, IEnumerable typeArguments, IEnumerable arguments) { - return new InvocationExpression { - Target = new MemberReferenceExpression { - Target = this, - MemberName = methodName, - TypeArguments = typeArguments - }, - Arguments = arguments - }; + InvocationExpression ie = new InvocationExpression(); + MemberReferenceExpression mre = new MemberReferenceExpression(); + mre.Target = this; + mre.MemberName = methodName; + mre.TypeArguments.AddRange(typeArguments); + ie.Target = mre; + ie.Arguments.AddRange(arguments); + return ie; } /// @@ -113,10 +119,10 @@ namespace ICSharpCode.NRefactory.CSharp /// public InvocationExpression Invoke(IEnumerable arguments) { - return new InvocationExpression { - Target = this, - Arguments = arguments - }; + InvocationExpression ie = new InvocationExpression(); + ie.Target = this; + ie.Arguments.AddRange(arguments); + return ie; } /// @@ -124,7 +130,10 @@ namespace ICSharpCode.NRefactory.CSharp /// public InvocationExpression Invoke(params Expression[] arguments) { - return Invoke(arguments.AsEnumerable()); + InvocationExpression ie = new InvocationExpression(); + ie.Target = this; + ie.Arguments.AddRange(arguments); + return ie; } public CastExpression CastTo(AstType type) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IdentifierExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IdentifierExpression.cs index c56b9e564..fce5f9c80 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IdentifierExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IdentifierExpression.cs @@ -1,4 +1,4 @@ -// +// // IdentifierExpression.cs // // Author: @@ -56,9 +56,8 @@ namespace ICSharpCode.NRefactory.CSharp } } - public IEnumerable TypeArguments { + public AstNodeCollection TypeArguments { get { return GetChildrenByRole (Roles.TypeArgument); } - set { SetChildrenByRole (Roles.TypeArgument, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IndexerExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IndexerExpression.cs index 71a8517f3..a5950bb0d 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IndexerExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IndexerExpression.cs @@ -1,4 +1,4 @@ -// +// // IndexerExpression.cs // // Author: @@ -42,9 +42,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LBracket); } } - public IEnumerable Arguments { + public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } - set { SetChildrenByRole(Roles.Argument, value); } } public CSharpTokenNode RBracketToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/InvocationExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/InvocationExpression.cs index 9efe86284..7b69440dd 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/InvocationExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/InvocationExpression.cs @@ -1,4 +1,4 @@ -// +// // InvocationExpression.cs // // Author: @@ -42,9 +42,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LPar); } } - public IEnumerable Arguments { + public AstNodeCollection Arguments { get { return GetChildrenByRole(Roles.Argument); } - set { SetChildrenByRole(Roles.Argument, value); } } public CSharpTokenNode RParToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/LambdaExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/LambdaExpression.cs index 06baa857c..dff50f074 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/LambdaExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/LambdaExpression.cs @@ -1,4 +1,4 @@ -// +// // LambdaExpression.cs // // Author: @@ -36,9 +36,8 @@ namespace ICSharpCode.NRefactory.CSharp public readonly static Role ArrowRole = new Role("Arrow", CSharpTokenNode.Null); public static readonly Role BodyRole = new Role("Body", AstNode.Null); - public IEnumerable Parameters { + public AstNodeCollection Parameters { get { return GetChildrenByRole (Roles.Parameter); } - set { SetChildrenByRole (Roles.Parameter, value); } } public CSharpTokenNode ArrowToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/MemberReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/MemberReferenceExpression.cs index c20d2ce07..43fa24714 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/MemberReferenceExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/MemberReferenceExpression.cs @@ -1,4 +1,4 @@ -// +// // MemberReferenceExpression.cs // // Author: @@ -51,9 +51,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LChevron); } } - public IEnumerable TypeArguments { + public AstNodeCollection TypeArguments { get { return GetChildrenByRole (Roles.TypeArgument); } - set { SetChildrenByRole (Roles.TypeArgument, value); } } public CSharpTokenNode RChevronToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ObjectCreateExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ObjectCreateExpression.cs index efccad91a..9b935ce48 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ObjectCreateExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/ObjectCreateExpression.cs @@ -1,4 +1,4 @@ -// +// // ObjectCreateExpression.cs // // Author: @@ -48,9 +48,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LPar); } } - public IEnumerable Arguments { + public AstNodeCollection Arguments { get { return GetChildrenByRole (Roles.Argument); } - set { SetChildrenByRole (Roles.Argument, value); } } public CSharpTokenNode RParToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PointerReferenceExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PointerReferenceExpression.cs index 17bd5b72c..e1ee93669 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PointerReferenceExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PointerReferenceExpression.cs @@ -1,4 +1,4 @@ -// +// // PointerReferenceExpression.cs // // Author: @@ -49,9 +49,8 @@ namespace ICSharpCode.NRefactory.CSharp } } - public IEnumerable TypeArguments { + public AstNodeCollection TypeArguments { get { return GetChildrenByRole (Roles.TypeArgument); } - set { SetChildrenByRole (Roles.TypeArgument, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/QueryExpression.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/QueryExpression.cs index d71be3d8d..b7f01a473 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/QueryExpression.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/QueryExpression.cs @@ -28,9 +28,8 @@ namespace ICSharpCode.NRefactory.CSharp } #endregion - public IEnumerable Clauses { + public AstNodeCollection Clauses { get { return GetChildrenByRole(ClauseRole); } - set { SetChildrenByRole(ClauseRole, value); } } public override S AcceptVisitor(IAstVisitor visitor, T data) @@ -260,9 +259,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.Keyword); } } - public IEnumerable Orderings { + public AstNodeCollection Orderings { get { return GetChildrenByRole (OrderingRole); } - set { SetChildrenByRole (OrderingRole, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Attribute.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Attribute.cs index 162a4e33b..5c08c1491 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Attribute.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Attribute.cs @@ -1,4 +1,4 @@ -// +// // Attribute.cs // // Author: @@ -44,9 +44,8 @@ namespace ICSharpCode.NRefactory.CSharp set { SetChildByRole (Roles.Type, value); } } - public IEnumerable Arguments { + public AstNodeCollection Arguments { get { return base.GetChildrenByRole (Roles.Argument); } - set { SetChildrenByRole (Roles.Argument, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/AttributeSection.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/AttributeSection.cs index 35d170dda..17c5e2f7f 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/AttributeSection.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/AttributeSection.cs @@ -1,4 +1,4 @@ -// +// // AttributeSection.cs // // Author: @@ -49,9 +49,8 @@ namespace ICSharpCode.NRefactory.CSharp set; } - public IEnumerable Attributes { + public AstNodeCollection Attributes { get { return base.GetChildrenByRole (AttributeRole); } - set { SetChildrenByRole (AttributeRole, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Constraint.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Constraint.cs index dae8d4e19..a2de49fff 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Constraint.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Constraint.cs @@ -1,4 +1,4 @@ -// +// // Constraint.cs // // Author: @@ -53,9 +53,8 @@ namespace ICSharpCode.NRefactory.CSharp // TODO: what about new(), struct and class constraints? - public IEnumerable BaseTypes { + public AstNodeCollection BaseTypes { get { return GetChildrenByRole (BaseTypeRole); } - set { SetChildrenByRole (BaseTypeRole, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/DelegateDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/DelegateDeclaration.cs index 1b71cd4c6..eccac9382 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/DelegateDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/DelegateDeclaration.cs @@ -1,4 +1,4 @@ -// +// // DelegateDeclaration.cs // // Author: @@ -54,27 +54,24 @@ namespace ICSharpCode.NRefactory.CSharp set { SetChildByRole (Roles.Type, value); } } - public IEnumerable TypeParameters { + public AstNodeCollection TypeParameters { get { return GetChildrenByRole (Roles.TypeParameter); } - set { SetChildrenByRole (Roles.TypeParameter, value); } } public CSharpTokenNode LParToken { get { return GetChildByRole (Roles.LPar); } } - public IEnumerable Parameters { + public AstNodeCollection Parameters { get { return GetChildrenByRole (Roles.Parameter); } - set { SetChildrenByRole (Roles.Parameter, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole (Roles.RPar); } } - public IEnumerable Constraints { + public AstNodeCollection Constraints { get { return GetChildrenByRole (Roles.Constraint); } - set { SetChildrenByRole (Roles.Constraint, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs index f082150b6..8111b5c3d 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs @@ -1,4 +1,4 @@ -// +// // NamespaceDeclaration.cs // // Author: @@ -54,13 +54,12 @@ namespace ICSharpCode.NRefactory.CSharp return builder.ToString (); } set { - SetChildrenByRole (Roles.Identifier, value.Split('.').Select(ident => new Identifier(ident, AstLocation.Empty))); + GetChildrenByRole(Roles.Identifier).ReplaceWith(value.Split('.').Select(ident => new Identifier(ident, AstLocation.Empty))); } } - public IEnumerable Identifiers { + public AstNodeCollection Identifiers { get { return GetChildrenByRole (Roles.Identifier); } - set { SetChildrenByRole (Roles.Identifier, value); } } /// @@ -79,9 +78,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LBrace); } } - public IEnumerable Members { + public AstNodeCollection Members { get { return GetChildrenByRole(MemberRole); } - set { SetChildrenByRole(MemberRole, value); } } public CSharpTokenNode RBraceToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeDeclaration.cs index 36381aafd..fd638f2da 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeDeclaration.cs @@ -1,4 +1,4 @@ -// +// // TypeDeclaration.cs // // Author: @@ -59,28 +59,24 @@ namespace ICSharpCode.NRefactory.CSharp } } - public IEnumerable TypeParameters { + public AstNodeCollection TypeParameters { get { return GetChildrenByRole (Roles.TypeParameter); } - set { SetChildrenByRole (Roles.TypeParameter, value); } } - public IEnumerable BaseTypes { + public AstNodeCollection BaseTypes { get { return GetChildrenByRole (BaseTypeRole); } - set { SetChildrenByRole (BaseTypeRole, value); } } - public IEnumerable Constraints { + public AstNodeCollection Constraints { get { return GetChildrenByRole (Roles.Constraint); } - set { SetChildrenByRole (Roles.Constraint, value); } } public CSharpTokenNode LBraceToken { get { return GetChildByRole (Roles.LBrace); } } - public IEnumerable Members { + public AstNodeCollection Members { get { return GetChildrenByRole (MemberRole); } - set { SetChildrenByRole (MemberRole, value); } } public CSharpTokenNode RBraceToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/MemberType.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/MemberType.cs index 8d600b575..cf5f1dd8f 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/MemberType.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/MemberType.cs @@ -1,4 +1,4 @@ -// +// // FullTypeName.cs // // Author: @@ -51,9 +51,8 @@ namespace ICSharpCode.NRefactory.CSharp } } - public IEnumerable TypeArguments { + public AstNodeCollection TypeArguments { get { return GetChildrenByRole (Roles.TypeArgument); } - set { SetChildrenByRole (Roles.TypeArgument, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/SimpleType.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/SimpleType.cs index ac3eb0210..ec6db27f1 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/SimpleType.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/SimpleType.cs @@ -1,4 +1,4 @@ -// +// // FullTypeName.cs // // Author: @@ -56,9 +56,8 @@ namespace ICSharpCode.NRefactory.CSharp } } - public IEnumerable TypeArguments { + public AstNodeCollection TypeArguments { get { return GetChildrenByRole (Roles.TypeArgument); } - set { SetChildrenByRole (Roles.TypeArgument, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/BlockStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/BlockStatement.cs index eb3a6f9a3..5d02ebea7 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/BlockStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/BlockStatement.cs @@ -1,4 +1,4 @@ -// +// // BlockStatement.cs // // Author: @@ -56,9 +56,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LBrace); } } - public IEnumerable Statements { + public AstNodeCollection Statements { get { return GetChildrenByRole (StatementRole); } - set { SetChildrenByRole (StatementRole, value); } } public CSharpTokenNode RBraceToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs index 5d44b66e0..d500c8dbf 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/FixedStatement.cs @@ -1,4 +1,4 @@ -// +// // FixedStatement.cs // // Author: @@ -46,9 +46,8 @@ namespace ICSharpCode.NRefactory.CSharp set { SetChildByRole (Roles.Type, value); } } - public IEnumerable Variables { + public AstNodeCollection Variables { get { return GetChildrenByRole (Roles.Variable); } - set { SetChildrenByRole (Roles.Variable, value); } } public CSharpTokenNode RParToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForStatement.cs index c667e6086..d50b0d3ff 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/ForStatement.cs @@ -1,4 +1,4 @@ -// +// // ForStatement.cs // // Author: @@ -49,9 +49,8 @@ namespace ICSharpCode.NRefactory.CSharp /// Note: this contains multiple statements for "for (a = 2, b = 1; a > b; a--)", but contains /// only a single statement for "for (int a = 2, b = 1; a > b; a--)" (a single VariableDeclarationStatement with two variables) /// - public IEnumerable Initializers { + public AstNodeCollection Initializers { get { return GetChildrenByRole (InitializerRole); } - set { SetChildrenByRole (InitializerRole, value); } } public Expression Condition { @@ -59,9 +58,8 @@ namespace ICSharpCode.NRefactory.CSharp set { SetChildByRole (Roles.Condition, value); } } - public IEnumerable Iterators { + public AstNodeCollection Iterators { get { return GetChildrenByRole (IteratorRole); } - set { SetChildrenByRole (IteratorRole, value); } } public CSharpTokenNode RParToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs index 99fb7009b..ed8a9df02 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/SwitchStatement.cs @@ -1,4 +1,4 @@ -// +// // SwitchStatement.cs // // Author: @@ -57,9 +57,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LBrace); } } - public IEnumerable SwitchSections { + public AstNodeCollection SwitchSections { get { return GetChildrenByRole (SwitchSectionRole); } - set { SetChildrenByRole (SwitchSectionRole, value); } } public CSharpTokenNode RBraceToken { @@ -82,14 +81,12 @@ namespace ICSharpCode.NRefactory.CSharp } } - public IEnumerable CaseLabels { + public AstNodeCollection CaseLabels { get { return GetChildrenByRole (CaseLabelRole); } - set { SetChildrenByRole (CaseLabelRole, value); } } - public IEnumerable Statements { + public AstNodeCollection Statements { get { return GetChildrenByRole (Roles.EmbeddedStatement); } - set { SetChildrenByRole (Roles.EmbeddedStatement, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/TryCatchStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/TryCatchStatement.cs index 70f48dd29..a50662dd4 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/TryCatchStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/TryCatchStatement.cs @@ -1,4 +1,4 @@ -// +// // TryCatchStatement.cs // // Author: @@ -49,9 +49,8 @@ namespace ICSharpCode.NRefactory.CSharp set { SetChildByRole (TryBlockRole, value); } } - public IEnumerable CatchClauses { + public AstNodeCollection CatchClauses { get { return GetChildrenByRole (CatchClauseRole); } - set { SetChildrenByRole (CatchClauseRole, value); } } public CSharpTokenNode FinallyToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/VariableDeclarationStatement.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/VariableDeclarationStatement.cs index 7be7b5a33..084ea12bf 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/VariableDeclarationStatement.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/Statements/VariableDeclarationStatement.cs @@ -1,4 +1,4 @@ -// +// // VariableDeclarationStatement.cs // // Author: @@ -33,6 +33,16 @@ namespace ICSharpCode.NRefactory.CSharp { public static readonly Role ModifierRole = AttributedNode.ModifierRole; + public VariableDeclarationStatement() + { + } + + public VariableDeclarationStatement(AstType type, string name, Expression initializer = null) + { + this.Type = type; + this.Variables.Add(new VariableInitializer(name, initializer)); + } + public Modifiers Modifiers { get { return AttributedNode.GetModifiers(this); } set { AttributedNode.SetModifiers(this, value); } @@ -43,9 +53,8 @@ namespace ICSharpCode.NRefactory.CSharp set { SetChildByRole (Roles.Type, value); } } - public IEnumerable Variables { + public AstNodeCollection Variables { get { return GetChildrenByRole (Roles.Variable); } - set { SetChildrenByRole (Roles.Variable, value); } } public CSharpTokenNode SemicolonToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs index a2734ae4f..1f8db8fce 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs @@ -11,9 +11,8 @@ namespace ICSharpCode.NRefactory.CSharp public static readonly Role AttributeRole = new Role("Attribute"); public static readonly Role ModifierRole = new Role("Modifier"); - public IEnumerable Attributes { + public AstNodeCollection Attributes { get { return base.GetChildrenByRole (AttributeRole); } - set { SetChildrenByRole (AttributeRole, value); } } public Modifiers Modifiers { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/ConstructorDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/ConstructorDeclaration.cs index 1097605c3..ec8101390 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/ConstructorDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/ConstructorDeclaration.cs @@ -1,4 +1,4 @@ -// +// // ConstructorDeclaration.cs // // Author: @@ -37,9 +37,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LPar); } } - public IEnumerable Parameters { + public AstNodeCollection Parameters { get { return GetChildrenByRole (Roles.Parameter); } - set { SetChildrenByRole (Roles.Parameter, value); } } public CSharpTokenNode RParToken { @@ -105,9 +104,8 @@ namespace ICSharpCode.NRefactory.CSharp set; } - public IEnumerable Arguments { + public AstNodeCollection Arguments { get { return GetChildrenByRole (Roles.Argument); } - set { SetChildrenByRole (Roles.Argument, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/EventDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/EventDeclaration.cs index dde192663..3a916782d 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/EventDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/EventDeclaration.cs @@ -1,4 +1,4 @@ -// +// // EventDeclaration.cs // // Author: @@ -30,9 +30,8 @@ namespace ICSharpCode.NRefactory.CSharp { public class EventDeclaration : MemberDeclaration { - public IEnumerable Variables { + public AstNodeCollection Variables { get { return GetChildrenByRole (Roles.Variable); } - set { SetChildrenByRole (Roles.Variable, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/FieldDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/FieldDeclaration.cs index 8969d50b0..3ca04772d 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/FieldDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/FieldDeclaration.cs @@ -1,4 +1,4 @@ -// +// // FieldDeclaration.cs // // Author: @@ -31,9 +31,8 @@ namespace ICSharpCode.NRefactory.CSharp { public class FieldDeclaration : MemberDeclaration { - public IEnumerable Variables { + public AstNodeCollection Variables { get { return GetChildrenByRole (Roles.Variable); } - set { SetChildrenByRole (Roles.Variable, value); } } public override S AcceptVisitor (IAstVisitor visitor, T data) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/IndexerDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/IndexerDeclaration.cs index 88d80ce50..5b63c9c4f 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/IndexerDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/IndexerDeclaration.cs @@ -1,4 +1,4 @@ -// +// // IndexerDeclaration.cs // // Author: @@ -35,9 +35,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LBracket); } } - public IEnumerable Parameters { + public AstNodeCollection Parameters { get { return GetChildrenByRole (Roles.Parameter); } - set { SetChildrenByRole (Roles.Parameter, value); } } public CSharpTokenNode RBracketToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/MethodDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/MethodDeclaration.cs index 84ff90819..ef740bd86 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/MethodDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/MethodDeclaration.cs @@ -1,4 +1,4 @@ -// +// // MethodDeclaration.cs // // Author: @@ -31,27 +31,24 @@ namespace ICSharpCode.NRefactory.CSharp { public class MethodDeclaration : MemberDeclaration { - public IEnumerable TypeParameters { + public AstNodeCollection TypeParameters { get { return GetChildrenByRole (Roles.TypeParameter); } - set { SetChildrenByRole (Roles.TypeParameter, value); } } public CSharpTokenNode LParToken { get { return GetChildByRole (Roles.LPar); } } - public IEnumerable Parameters { + public AstNodeCollection Parameters { get { return GetChildrenByRole (Roles.Parameter); } - set { SetChildrenByRole (Roles.Parameter, value); } } public CSharpTokenNode RParToken { get { return GetChildByRole (Roles.RPar); } } - public IEnumerable Constraints { + public AstNodeCollection Constraints { get { return GetChildrenByRole (Roles.Constraint); } - set { SetChildrenByRole (Roles.Constraint, value); } } public BlockStatement Body { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/OperatorDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/OperatorDeclaration.cs index 4b3a1693b..c8a326c49 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/OperatorDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/OperatorDeclaration.cs @@ -1,4 +1,4 @@ -// +// // OperatorDeclaration.cs // // Author: @@ -80,9 +80,8 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildByRole (Roles.LPar); } } - public IEnumerable Parameters { + public AstNodeCollection Parameters { get { return GetChildrenByRole (Roles.Parameter); } - set { SetChildrenByRole (Roles.Parameter, value); } } public CSharpTokenNode RParToken { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/ParameterDeclaration.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/ParameterDeclaration.cs index 21c1f644e..6ae4994e8 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/ParameterDeclaration.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/ParameterDeclaration.cs @@ -1,4 +1,4 @@ -// +// // ParameterDeclarationExpression.cs // // Author: @@ -49,9 +49,8 @@ namespace ICSharpCode.NRefactory.CSharp } } - public IEnumerable Attributes { + public AstNodeCollection Attributes { get { return GetChildrenByRole (AttributeRole); } - set { SetChildrenByRole (AttributeRole, value); } } public ParameterModifier ParameterModifier { diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs index a8cc6acac..bb447f4c6 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Parser/CSharpParser.cs @@ -122,9 +122,7 @@ namespace ICSharpCode.NRefactory.CSharp if (location != null) spec.AddChild (new CSharpTokenNode (Convert (location[0]), 1), FieldDeclaration.Roles.RBracket); - result.ArraySpecifiers = new ArraySpecifier[] { - spec - }; + result.ArraySpecifiers.Add(spec); } return result; } diff --git a/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 0f23db485..619f2070e 100644 --- a/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/NRefactory/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -1,4 +1,4 @@ - + {3B2A5653-EC97-4001-BB9B-D90F1AF2C371} @@ -56,6 +56,7 @@ + From d187b37fb5586d682246e63340a2b4080e593103 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 18 Feb 2011 21:47:53 +0100 Subject: [PATCH 17/27] use CreateInstanceFromAndUnwrap instead of CreatedInstanceAndUnwrap --- ILSpy/TreeNodes/ResourceEntryNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceEntryNode.cs index 1cff10836..af1c01baf 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceEntryNode.cs @@ -96,7 +96,7 @@ namespace ICSharpCode.ILSpy.TreeNodes // Create the second AppDomain. AppDomain bamlDecompilerAppDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup); - BamlDecompiler decompiler = (BamlDecompiler)bamlDecompilerAppDomain.CreateInstanceAndUnwrap(Assembly.GetEntryAssembly().FullName, typeof(BamlDecompiler).FullName); + BamlDecompiler decompiler = (BamlDecompiler)bamlDecompilerAppDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName); MemoryStream bamlStream = new MemoryStream(); value.Position = 0; From ac8d6d04208d70bdcebc236614b3fc8abe399b18 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 18 Feb 2011 23:17:42 +0100 Subject: [PATCH 18/27] fixing more exceptions with ResourceSets --- ILSpy/TreeNodes/ResourceListTreeNode.cs | 10 ++++------ README.txt | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index 9dcc0d377..cc38111c5 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -156,16 +156,14 @@ namespace ICSharpCode.ILSpy.TreeNodes if (er != null) { try { Stream s = er.GetResourceStream(); - FileType type = GuessFileType.DetectFileType(s); - s.Position = 0; - if (type == FileType.Binary) { + if (er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { ResourceSet set = new ResourceSet(s); foreach (DictionaryEntry entry in set.Cast().OrderBy(e => e.Key.ToString())) { - Children.Add(new ResourceEntryNode(entry.Key.ToString(), (Stream)entry.Value)); + if (entry.Value is Stream) + Children.Add(new ResourceEntryNode(entry.Key.ToString(), (Stream)entry.Value)); } } - } catch (Exception ex) { - MessageBox.Show(ex.ToString()); + } catch (ArgumentException) { } } } diff --git a/README.txt b/README.txt index 2143999f7..3c5629dfe 100644 --- a/README.txt +++ b/README.txt @@ -13,3 +13,4 @@ Included open-source libraries: ILSpy Contributors: Daniel Grunwald David Srbecky + Siegfried Pammer From f3f7d29c1225c631f931cda304eef39ddff8fc03 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 22:00:15 +0100 Subject: [PATCH 19/27] Fix lambda expression output bug. --- .../CSharp/OutputVisitor/OutputVisitor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs index 88b8b0488..397fe3072 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/OutputVisitor/OutputVisitor.cs @@ -675,10 +675,10 @@ namespace ICSharpCode.NRefactory.CSharp bool LambdaNeedsParenthesis(LambdaExpression lambdaExpression) { - if (lambdaExpression.Parameters.Count() != 1) + if (lambdaExpression.Parameters.Count != 1) return true; var p = lambdaExpression.Parameters.Single(); - return p.Type.IsNull && p.ParameterModifier == ParameterModifier.None; + return !(p.Type.IsNull && p.ParameterModifier == ParameterModifier.None); } public object VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data) From 24cdcd19583c810b5c30863bd282ef1c8cefbc93 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 22:00:37 +0100 Subject: [PATCH 20/27] Use lambda expressions for anonymous methods that contain only a single return statement. --- .../Ast/Transforms/DelegateConstruction.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 23c9fcd4c..4906cc97c 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -106,20 +106,34 @@ namespace Decompiler.Transforms body.AcceptVisitor(this, null); AnonymousMethodExpression ame = new AnonymousMethodExpression(); + bool isLambda = false; if (method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) { ame.HasParameterList = false; } else { ame.HasParameterList = true; ame.Parameters.AddRange(AstBuilder.MakeParameters(method.Parameters)); + if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) { + isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement); + } } - ame.Body = body; + // Replace all occurrences of 'this' in the method body with the delegate's target: foreach (AstNode node in body.Descendants) { if (node is ThisReferenceExpression) node.ReplaceWith(target.Clone()); } - objectCreateExpression.ReplaceWith(ame); + if (isLambda) { + LambdaExpression lambda = new LambdaExpression(); + ame.Parameters.MoveTo(lambda.Parameters); + Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression; + returnExpr.Remove(); + lambda.Body = returnExpr; + objectCreateExpression.ReplaceWith(lambda); + } else { + ame.Body = body; + objectCreateExpression.ReplaceWith(ame); + } return true; } From 8e696011842552e0969349932223764b02b78fba Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 23:48:19 +0100 Subject: [PATCH 21/27] Implement type analysis for a lot more opcodes. --- .../Ast/AstMethodBodyBuilder.cs | 3 +- .../ILAst/ILAstOptimizer.cs | 4 +- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 196 +++++++++++++++++- ILSpy/ILAstLanguage.cs | 5 +- 4 files changed, 191 insertions(+), 17 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index c0fa88b57..9513442ea 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -74,7 +74,7 @@ namespace Decompiler context.CancellationToken.ThrowIfCancellationRequested(); ILAstOptimizer bodyGraph = new ILAstOptimizer(); - bodyGraph.Optimize(methodDef, ilMethod); + bodyGraph.Optimize(context, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); @@ -410,7 +410,6 @@ namespace Decompiler } case Code.Ldlen: return arg1.Member("Length"); - case Code.Ldelem_I: case Code.Ldelem_I1: case Code.Ldelem_I2: diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index d7eba8e58..3e48aa4d9 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -24,7 +24,7 @@ namespace Decompiler.ControlFlow { Dictionary labelToCfNode = new Dictionary(); - public void Optimize(MethodDefinition cecilMethod, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) + public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None) { if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return; foreach(ILBlock block in method.GetSelfAndChildrenRecursive().ToList()) { @@ -57,7 +57,7 @@ namespace Decompiler.ControlFlow if (abortBeforeStep == ILAstOptimizationStep.RemoveDeadLabels) return; RemoveDeadLabels(method); if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return; - TypeAnalysis.Run(cecilMethod.Module.TypeSystem, method); + TypeAnalysis.Run(context, method); } class ILMoveableBlock: ILBlock diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 6a51a54a8..0b515fe22 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -20,14 +20,18 @@ namespace Decompiler /// public class TypeAnalysis { - public static void Run(TypeSystem typeSystem, ILNode node) + public static void Run(DecompilerContext context, ILNode node) { TypeAnalysis ta = new TypeAnalysis(); - ta.typeSystem = typeSystem; + ta.context = context; + ta.module = context.CurrentMethod.Module; + ta.typeSystem = ta.module.TypeSystem; ta.InferTypes(node); } + DecompilerContext context; TypeSystem typeSystem; + ModuleDefinition module; List storedToGeneratedVariables = new List(); void InferTypes(ILNode node) @@ -67,14 +71,25 @@ namespace Decompiler TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) { switch (expr.OpCode.Code) { + #region Variable load/store case Code.Stloc: if (forceInferChildren) InferTypeForExpression(expr.Arguments.Single(), ((ILVariable)expr.Operand).Type); return null; case Code.Ldloc: return ((ILVariable)expr.Operand).Type; + case Code.Starg: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments.Single(), ((ParameterReference)expr.Operand).ParameterType); + return null; case Code.Ldarg: - return ((ParameterDefinition)expr.Operand).ParameterType; + return ((ParameterReference)expr.Operand).ParameterType; + case Code.Ldloca: + return new ByReferenceType(((VariableDefinition)expr.Operand).VariableType); + case Code.Ldarga: + return new ByReferenceType(((ParameterReference)expr.Operand).ParameterType); + #endregion + #region Call / NewObj case Code.Call: case Code.Callvirt: { @@ -99,42 +114,201 @@ namespace Decompiler } return ctor.DeclaringType; } + #endregion + #region Load/Store Fields case Code.Ldfld: return UnpackModifiers(((FieldReference)expr.Operand).FieldType); case Code.Ldsfld: return UnpackModifiers(((FieldReference)expr.Operand).FieldType); + case Code.Ldflda: + return new ByReferenceType(UnpackModifiers(((FieldReference)expr.Operand).FieldType)); + case Code.Stfld: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments[1], ((FieldReference)expr.Operand).FieldType); + return null; + case Code.Stsfld: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).FieldType); + return null; + #endregion + #region Arithmetic instructions + case Code.Add: + case Code.Sub: + case Code.Mul: case Code.Or: - return InferArgumentsInBinaryOperator(expr); + case Code.And: + return InferArgumentsInBinaryOperator(expr, null); + case Code.Add_Ovf: + case Code.Sub_Ovf: + case Code.Mul_Ovf: + case Code.Div: + return InferArgumentsInBinaryOperator(expr, true); + case Code.Add_Ovf_Un: + case Code.Sub_Ovf_Un: + case Code.Mul_Ovf_Un: + case Code.Div_Un: + return InferArgumentsInBinaryOperator(expr, false); case Code.Shl: case Code.Shr: if (forceInferChildren) InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); return InferTypeForExpression(expr.Arguments[0], expectedType); + #endregion + #region Constant loading instructions + case Code.Ldnull: + return typeSystem.Object; + case Code.Ldstr: + return typeSystem.String; + case Code.Ldftn: + return typeSystem.IntPtr; case Code.Ldc_I4: return (IsIntegerOrEnum(expectedType) || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32; case Code.Ldc_I8: return (IsIntegerOrEnum(expectedType)) ? expectedType : typeSystem.Int64; + case Code.Ldc_R8: + return typeSystem.Double; + case Code.Ldtoken: + if (expr.Operand is TypeReference) + return new TypeReference("System", "RuntimeTypeHandle", module, module, true); + else if (expr.Operand is FieldReference) + return new TypeReference("System", "RuntimeFieldHandle", module, module, true); + else + return new TypeReference("System", "RuntimeMethodHandle", module, module, true); + #endregion + #region Array instructions + case Code.Newarr: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32); + return new ArrayType((TypeReference)expr.Operand); + case Code.Ldlen: + return typeSystem.Int32; + case Code.Ldelem_U1: + case Code.Ldelem_U2: + case Code.Ldelem_U4: + case Code.Ldelem_I1: + case Code.Ldelem_I2: + case Code.Ldelem_I4: + case Code.Ldelem_I8: + case Code.Ldelem_I: + case Code.Ldelem_Ref: + { + ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; + if (forceInferChildren) { + InferTypeForExpression(expr.Arguments[0], new ArrayType(typeSystem.Byte)); + InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); + } + return arrayType != null ? arrayType.ElementType : null; + } + case Code.Ldelema: + { + ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; + if (forceInferChildren) + InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); + return arrayType != null ? new ByReferenceType(arrayType.ElementType) : null; + } + case Code.Stelem_I: + case Code.Stelem_I1: + case Code.Stelem_I2: + case Code.Stelem_I4: + case Code.Stelem_I8: + case Code.Stelem_R4: + case Code.Stelem_R8: + case Code.Stelem_Ref: + case Code.Stelem_Any: + if (forceInferChildren) { + ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; + InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); + if (arrayType != null) { + InferTypeForExpression(expr.Arguments[2], arrayType.ElementType); + } + } + return null; + #endregion + #region Conversion instructions + case Code.Conv_I1: + return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == true) ? expectedType : typeSystem.SByte; + case Code.Conv_I2: + return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int16; + case Code.Conv_I4: + return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int32; case Code.Conv_I8: return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64; - case Code.Dup: - return InferTypeForExpression(expr.Arguments.Single(), expectedType); + case Code.Conv_U1: + return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == false) ? expectedType : typeSystem.Byte; + case Code.Conv_U2: + return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt16; + case Code.Conv_U4: + return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt32; + case Code.Conv_U8: + return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt64; + case Code.Castclass: + case Code.Isinst: + case Code.Unbox_Any: + return (TypeReference)expr.Operand; + case Code.Box: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments.Single(), (TypeReference)expr.Operand); + return (TypeReference)expr.Operand; + #endregion + #region Comparison instructions case Code.Ceq: + if (forceInferChildren) + InferArgumentsInBinaryOperator(expr, null); + return typeSystem.Boolean; case Code.Clt: + case Code.Cgt: if (forceInferChildren) - InferArgumentsInBinaryOperator(expr); + InferArgumentsInBinaryOperator(expr, true); return typeSystem.Boolean; + case Code.Clt_Un: + case Code.Cgt_Un: + if (forceInferChildren) + InferArgumentsInBinaryOperator(expr, false); + return typeSystem.Boolean; + #endregion + #region Branch instructions case Code.Beq: - case Code.Blt: + case Code.Bne_Un: if (forceInferChildren) - InferArgumentsInBinaryOperator(expr); + InferArgumentsInBinaryOperator(expr, null); return null; case Code.Brtrue: case Code.Brfalse: if (forceInferChildren) InferTypeForExpression(expr.Arguments.Single(), typeSystem.Boolean); return null; + case Code.Blt: + case Code.Ble: + case Code.Bgt: + case Code.Bge: + if (forceInferChildren) + InferArgumentsInBinaryOperator(expr, true); + return null; + case Code.Blt_Un: + case Code.Ble_Un: + case Code.Bgt_Un: + case Code.Bge_Un: + if (forceInferChildren) + InferArgumentsInBinaryOperator(expr, false); + return null; + case Code.Br: + case Code.Leave: + case Code.Endfinally: + case Code.Switch: + case Code.Throw: + case Code.Rethrow: + return null; + case Code.Ret: + if (forceInferChildren && expr.Arguments.Count == 1) + InferTypeForExpression(expr.Arguments[0], context.CurrentMethod.ReturnType); + return null; + #endregion + case Code.Pop: + return null; + case Code.Dup: + return InferTypeForExpression(expr.Arguments.Single(), expectedType); default: - //throw new NotImplementedException("Can't handle " + expr.OpCode.Name); + Debug.WriteLine("Type Inference: Can't handle " + expr.OpCode.Name); return null; } } @@ -146,7 +320,7 @@ namespace Decompiler return type; } - TypeReference InferArgumentsInBinaryOperator(ILExpression expr) + TypeReference InferArgumentsInBinaryOperator(ILExpression expr, bool? isSigned) { ILExpression left = expr.Arguments[0]; ILExpression right = expr.Arguments[1]; diff --git a/ILSpy/ILAstLanguage.cs b/ILSpy/ILAstLanguage.cs index 526567ba8..13f6af184 100644 --- a/ILSpy/ILAstLanguage.cs +++ b/ILSpy/ILAstLanguage.cs @@ -52,13 +52,14 @@ namespace ICSharpCode.ILSpy ilMethod.Body = astBuilder.Build(method, inlineVariables); if (abortBeforeStep != null) { - new ILAstOptimizer().Optimize(method, ilMethod, abortBeforeStep.Value); + DecompilerContext context = new DecompilerContext { CurrentType = method.DeclaringType, CurrentMethod = method }; + new ILAstOptimizer().Optimize(context, ilMethod, abortBeforeStep.Value); } var allVariables = astBuilder.Variables .Concat(ilMethod.GetSelfAndChildrenRecursive().Select(e => e.Operand as ILVariable).Where(v => v != null)).Distinct(); foreach (ILVariable v in allVariables) { - output.Write(v.Name); + output.WriteDefinition(v.Name, v); if (v.Type != null) { output.Write(" : "); v.Type.WriteTo(output, true, true); From 2c2801eab8b7f4aa8b4d1d0c99774a9520c13aa4 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 18 Feb 2011 23:53:10 +0100 Subject: [PATCH 22/27] Fix StackOverflowException in PushNegation. --- ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs index e8ee847d6..31ec559d3 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs @@ -83,7 +83,7 @@ namespace Decompiler.Transforms rightOperand = ((PrimitiveExpression)binaryOperatorExpression.Right).Value as bool?; if (op == BinaryOperatorType.Equality && rightOperand == true || op == BinaryOperatorType.InEquality && rightOperand == false) { // 'b == true' or 'b != false' is useless - binaryOperatorExpression.AcceptVisitor(this, data); + binaryOperatorExpression.Left.AcceptVisitor(this, data); binaryOperatorExpression.ReplaceWith(binaryOperatorExpression.Left); return null; } else if (op == BinaryOperatorType.Equality && rightOperand == false || op == BinaryOperatorType.InEquality && rightOperand == true) { From 7442f501b04021c5f90665ac4bc48168b9b379f7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 19 Feb 2011 01:09:16 +0100 Subject: [PATCH 23/27] Add support for more opcodes to type analysis: ldind*, stind*, conv_ovf*, and some others. --- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 96 +++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 0b515fe22..08f2ceb21 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -121,6 +121,7 @@ namespace Decompiler case Code.Ldsfld: return UnpackModifiers(((FieldReference)expr.Operand).FieldType); case Code.Ldflda: + case Code.Ldsflda: return new ByReferenceType(UnpackModifiers(((FieldReference)expr.Operand).FieldType)); case Code.Stfld: if (forceInferChildren) @@ -131,28 +132,76 @@ namespace Decompiler InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).FieldType); return null; #endregion + #region Reference/Pointer instructions + case Code.Ldind_I: + case Code.Ldind_I1: + case Code.Ldind_I2: + case Code.Ldind_I4: + case Code.Ldind_I8: + case Code.Ldind_U1: + case Code.Ldind_U2: + case Code.Ldind_U4: + case Code.Ldind_R4: + case Code.Ldind_R8: + case Code.Ldind_Ref: + return UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); + case Code.Stind_I1: + case Code.Stind_I2: + case Code.Stind_I4: + case Code.Stind_I8: + case Code.Stind_R4: + case Code.Stind_R8: + case Code.Stind_I: + case Code.Stind_Ref: + if (forceInferChildren) { + TypeReference elementType = UnpackPointer(InferTypeForExpression(expr.Arguments[0], null)); + InferTypeForExpression(expr.Arguments[1], elementType); + } + return null; + case Code.Ldobj: + return (TypeReference)expr.Operand; + case Code.Stobj: + if (forceInferChildren) { + InferTypeForExpression(expr.Arguments[1], (TypeReference)expr.Operand); + } + return null; + case Code.Initobj: + return null; + case Code.Localloc: + return typeSystem.IntPtr; + #endregion #region Arithmetic instructions + case Code.Not: // bitwise complement + case Code.Neg: + return InferTypeForExpression(expr.Arguments.Single(), expectedType); case Code.Add: case Code.Sub: case Code.Mul: case Code.Or: case Code.And: + case Code.Xor: return InferArgumentsInBinaryOperator(expr, null); case Code.Add_Ovf: case Code.Sub_Ovf: case Code.Mul_Ovf: case Code.Div: + case Code.Rem: return InferArgumentsInBinaryOperator(expr, true); case Code.Add_Ovf_Un: case Code.Sub_Ovf_Un: case Code.Mul_Ovf_Un: case Code.Div_Un: + case Code.Rem_Un: return InferArgumentsInBinaryOperator(expr, false); case Code.Shl: case Code.Shr: if (forceInferChildren) InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); - return InferTypeForExpression(expr.Arguments[0], expectedType); + return InferTypeForExpression(expr.Arguments[0], typeSystem.Int32); + case Code.Shr_Un: + if (forceInferChildren) + InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); + return InferTypeForExpression(expr.Arguments[0], typeSystem.UInt32); #endregion #region Constant loading instructions case Code.Ldnull: @@ -160,11 +209,14 @@ namespace Decompiler case Code.Ldstr: return typeSystem.String; case Code.Ldftn: + case Code.Ldvirtftn: return typeSystem.IntPtr; case Code.Ldc_I4: return (IsIntegerOrEnum(expectedType) || expectedType == typeSystem.Boolean) ? expectedType : typeSystem.Int32; case Code.Ldc_I8: return (IsIntegerOrEnum(expectedType)) ? expectedType : typeSystem.Int64; + case Code.Ldc_R4: + return typeSystem.Single; case Code.Ldc_R8: return typeSystem.Double; case Code.Ldtoken: @@ -174,6 +226,8 @@ namespace Decompiler return new TypeReference("System", "RuntimeFieldHandle", module, module, true); else return new TypeReference("System", "RuntimeMethodHandle", module, module, true); + case Code.Arglist: + return new TypeReference("System", "RuntimeArgumentHandle", module, module, true); #endregion #region Array instructions case Code.Newarr: @@ -199,6 +253,11 @@ namespace Decompiler } return arrayType != null ? arrayType.ElementType : null; } + case Code.Ldelem_Any: + if (forceInferChildren) { + InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); + } + return (TypeReference)expr.Operand; case Code.Ldelema: { ArrayType arrayType = InferTypeForExpression(expr.Arguments[0], null) as ArrayType; @@ -226,21 +285,41 @@ namespace Decompiler #endregion #region Conversion instructions case Code.Conv_I1: + case Code.Conv_Ovf_I1: return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == true) ? expectedType : typeSystem.SByte; case Code.Conv_I2: + case Code.Conv_Ovf_I2: return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int16; case Code.Conv_I4: + case Code.Conv_Ovf_I4: return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int32; case Code.Conv_I8: + case Code.Conv_Ovf_I8: return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == true) ? expectedType : typeSystem.Int64; case Code.Conv_U1: + case Code.Conv_Ovf_U1: return (GetInformationAmount(expectedType) == 8 && IsSigned(expectedType) == false) ? expectedType : typeSystem.Byte; case Code.Conv_U2: + case Code.Conv_Ovf_U2: return (GetInformationAmount(expectedType) == 16 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt16; case Code.Conv_U4: + case Code.Conv_Ovf_U4: return (GetInformationAmount(expectedType) == 32 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt32; case Code.Conv_U8: + case Code.Conv_Ovf_U8: return (GetInformationAmount(expectedType) == 64 && IsSigned(expectedType) == false) ? expectedType : typeSystem.UInt64; + case Code.Conv_I: + case Code.Conv_Ovf_I: + return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == true) ? expectedType : typeSystem.IntPtr; + case Code.Conv_U: + case Code.Conv_Ovf_U: + return (GetInformationAmount(expectedType) == nativeInt && IsSigned(expectedType) == false) ? expectedType : typeSystem.UIntPtr; + case Code.Conv_R4: + return typeSystem.Single; + case Code.Conv_R8: + return typeSystem.Double; + case Code.Conv_R_Un: + return (expectedType == typeSystem.Single) ? typeSystem.Single : typeSystem.Double; case Code.Castclass: case Code.Isinst: case Code.Unbox_Any: @@ -313,6 +392,17 @@ namespace Decompiler } } + TypeReference UnpackPointer(TypeReference pointerOrManagedReference) + { + ByReferenceType refType = pointerOrManagedReference as ByReferenceType; + if (refType != null) + return refType.ElementType; + PointerType ptrType = pointerOrManagedReference as PointerType; + if (ptrType != null) + return ptrType.ElementType; + return null; + } + static TypeReference UnpackModifiers(TypeReference type) { while (type is OptionalModifierType || type is RequiredModifierType) @@ -352,6 +442,8 @@ namespace Decompiler return GetInformationAmount(typeSystem, type); } + const int nativeInt = 33; // treat native int as between int32 and int64 + static int GetInformationAmount(TypeSystem typeSystem, TypeReference type) { if (type == null) @@ -373,7 +465,7 @@ namespace Decompiler else if (type == typeSystem.Int32 || type == typeSystem.UInt32) return 32; else if (type == typeSystem.IntPtr || type == typeSystem.UIntPtr) - return 33; // treat native int as between int32 and int64 + return nativeInt; else if (type == typeSystem.Int64 || type == typeSystem.UInt64) return 64; return 100; // we consider structs/objects to have more information than any primitives From a03d029d30edc81e16700c0b8a47f335e8933fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sat, 19 Feb 2011 10:47:48 +0000 Subject: [PATCH 24/27] Use custom enum to store OpCodes (so that we can add "virtual" opcodes) --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 2 +- .../Ast/AstMethodBodyBuilder.cs | 13 +- .../ICSharpCode.Decompiler.csproj | 5 +- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 76 +-- .../ILAst/ILAstOptimizer.cs | 15 +- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 17 +- ICSharpCode.Decompiler/ILAst/ILCodes.cs | 423 ++++++++++++++++ ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 6 +- .../Mono.Cecil.Rocks/Constants.cs | 15 - .../Mono.Cecil.Rocks/MethodBodyRocks.cs | 478 ------------------ .../Mono.Cecil.Rocks/MyRocks.cs | 118 ----- 11 files changed, 492 insertions(+), 676 deletions(-) create mode 100644 ICSharpCode.Decompiler/ILAst/ILCodes.cs delete mode 100644 ICSharpCode.Decompiler/Mono.Cecil.Rocks/Constants.cs delete mode 100644 ICSharpCode.Decompiler/Mono.Cecil.Rocks/MethodBodyRocks.cs delete mode 100644 ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 807e7d78a..e4978d823 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -148,7 +148,7 @@ namespace Decompiler } } else { // Base type - if (typeDef.BaseType != null && !typeDef.IsValueType && typeDef.BaseType.FullName != Constants.Object) { + if (typeDef.BaseType != null && !typeDef.IsValueType && typeDef.BaseType.FullName != "System.Object") { astType.AddChild(ConvertType(typeDef.BaseType), TypeDeclaration.BaseTypeRole); } foreach (var i in typeDef.Interfaces) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 9513442ea..5dfecb27b 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -8,7 +8,6 @@ using ICSharpCode.NRefactory.CSharp; using Cecil = Mono.Cecil; using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; using Decompiler.ControlFlow; namespace Decompiler @@ -80,7 +79,7 @@ namespace Decompiler List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); Dictionary typeNames = new Dictionary(); foreach(ILVariable varDef in astBuilder.Variables) { - if (varDef.Type.FullName == Constants.Int32 && intNames.Count > 0) { + if (varDef.Type.FullName == "System.Int32" && intNames.Count > 0) { varDef.Name = intNames[0]; intNames.RemoveAt(0); } else { @@ -243,7 +242,7 @@ namespace Decompiler Ast.Expression arg1 = args.Count >= 1 ? args[0] : null; Ast.Expression arg2 = args.Count >= 2 ? args[1] : null; TypeReference arg1Type = args.Count >= 1 ? expr.Arguments[0].InferredType : null; - switch(expr.OpCode.Code) { + switch((Code)expr.Code) { case Code.Brfalse: if (arg1Type == typeSystem.Boolean) return new Ast.UnaryOperatorExpression(UnaryOperatorType.Not, arg1); @@ -328,7 +327,7 @@ namespace Decompiler if (byteCode.Operand != null) { args.Insert(0, new IdentifierExpression(FormatByteCodeOperand(byteCode.Operand))); } - return new IdentifierExpression(byteCode.OpCode.Name).Invoke(args); + return new IdentifierExpression(byteCode.Code.GetName()).Invoke(args); } } @@ -361,7 +360,7 @@ namespace Decompiler { // throw new NotImplementedException(); - OpCode opCode = byteCode.OpCode; + ILCode opCode = byteCode.Code; object operand = byteCode.Operand; AstType operandAsTypeRef = AstBuilder.ConvertType(operand as Cecil.TypeReference); ILExpression operandAsByteCode = operand as ILExpression; @@ -375,7 +374,7 @@ namespace Decompiler branchCommand.AddStatement(new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)); } - switch(opCode.Code) { + switch((Code)opCode) { #region Arithmetic case Code.Add: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); case Code.Add_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Add, arg2); @@ -655,7 +654,7 @@ namespace Decompiler case Code.Refanytype: throw new NotImplementedException(); case Code.Refanyval: throw new NotImplementedException(); case Code.Ret: { - if (methodDef.ReturnType.FullName != Constants.Void) { + if (methodDef.ReturnType.FullName != "System.Void") { arg1 = Convert(arg1, methodDef.ReturnType); return new Ast.ReturnStatement { Expression = arg1 }; } else { diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index dfe2997d9..590030c50 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -89,11 +89,9 @@ + - - - @@ -114,7 +112,6 @@ - diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index bb0c9cf03..22c340a1d 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -4,9 +4,7 @@ using System.Linq; using System.Text; using Mono.Cecil; using Mono.Cecil.Cil; -using Mono.Cecil.Rocks; using Cecil = Mono.Cecil; -using Decompiler.Rocks; namespace Decompiler { @@ -33,7 +31,7 @@ namespace Decompiler public ILLabel Label; // Non-null only if needed public int Offset; public int EndOffset; - public OpCode OpCode; + public ILCode Code; public object Operand; public int? PopCount; // Null means pop all public int PushCount; @@ -59,7 +57,7 @@ namespace Decompiler public override string ToString() { StringBuilder sb = new StringBuilder(); - sb.AppendFormat("{0}:{1} {2} {3}", this.Name, this.Label != null ? " *" : "", this.OpCode, this.Operand); + sb.AppendFormat("{0}:{1} {2} {3}", this.Name, this.Label != null ? " *" : "", this.Code.GetName(), this.Operand); if (this.StackBefore != null) { sb.Append(" StackBefore = {"); bool first = true; @@ -116,13 +114,13 @@ namespace Decompiler // Create temporary structure for the stack analysis List body = new List(methodDef.Body.Instructions.Count); foreach(Instruction inst in methodDef.Body.Instructions) { - OpCode opCode = inst.OpCode; + ILCode code = (ILCode)inst.OpCode.Code; object operand = inst.Operand; - MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body); + ILCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body); ByteCode byteCode = new ByteCode() { Offset = inst.Offset, EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize, - OpCode = opCode, + Code = code, Operand = operand, PopCount = inst.GetPopCount(), PushCount = inst.GetPushCount() @@ -173,27 +171,25 @@ namespace Decompiler // Apply the state to any successors List branchTargets = new List(); - if (byteCode.OpCode.CanFallThough()) { + if (byteCode.Code.CanFallThough()) { branchTargets.Add(byteCode.Next); } - if (byteCode.OpCode.IsBranch()) { - if (byteCode.Operand is Instruction[]) { - foreach(Instruction inst in (Instruction[])byteCode.Operand) { - ByteCode target = instrToByteCode[inst]; - branchTargets.Add(target); - // The target of a branch must have label - if (target.Label == null) { - target.Label = new ILLabel() { Name = target.Name }; - } - } - } else { - ByteCode target = instrToByteCode[(Instruction)byteCode.Operand]; + if (byteCode.Operand is Instruction[]) { + foreach(Instruction inst in (Instruction[])byteCode.Operand) { + ByteCode target = instrToByteCode[inst]; branchTargets.Add(target); // The target of a branch must have label if (target.Label == null) { target.Label = new ILLabel() { Name = target.Name }; } } + } else if (byteCode.Operand is Instruction) { + ByteCode target = instrToByteCode[(Instruction)byteCode.Operand]; + branchTargets.Add(target); + // The target of a branch must have label + if (target.Label == null) { + target.Label = new ILLabel() { Name = target.Name }; + } } foreach (ByteCode branchTarget in branchTargets) { if (branchTarget.StackBefore == null) { @@ -255,12 +251,12 @@ namespace Decompiler int[] numReads = new int[Variables.Count]; int[] numWrites = new int[Variables.Count]; foreach(ByteCode byteCode in body) { - if (byteCode.OpCode == OpCodes.Ldloc) { + if (byteCode.Code == ILCode.Ldloc) { int index = ((VariableDefinition)byteCode.Operand).Index; byteCode.Operand = Variables[index]; numReads[index]++; } - if (byteCode.OpCode == OpCodes.Stloc) { + if (byteCode.Code == ILCode.Stloc) { int index = ((VariableDefinition)byteCode.Operand).Index; byteCode.Operand = Variables[index]; numWrites[index]++; @@ -361,12 +357,7 @@ namespace Decompiler // Convert stack-based IL code to ILAst tree foreach(ByteCode byteCode in body) { - OpCode opCode = byteCode.OpCode; - object operand = byteCode.Operand; - - MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body); - - ILExpression expr = new ILExpression(opCode, operand); + ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand); expr.ILRanges.Add(new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset }); // Label for this instruction @@ -379,10 +370,10 @@ namespace Decompiler for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { StackSlot slot = byteCode.StackBefore[i]; if (slot.PushedBy != null) { - ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, slot.LoadFrom); + ILExpression ldExpr = new ILExpression(ILCode.Ldloc, slot.LoadFrom); expr.Arguments.Add(ldExpr); } else { - ILExpression ldExpr = new ILExpression(OpCodes.Ldloc, new ILVariable() { Name = "ex", IsGenerated = true }); + ILExpression ldExpr = new ILExpression(ILCode.Ldloc, new ILVariable() { Name = "ex", IsGenerated = true }); expr.Arguments.Add(ldExpr); } } @@ -391,12 +382,12 @@ namespace Decompiler if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) { ast.Add(expr); } else if (byteCode.StoreTo.Count == 1) { - ast.Add(new ILExpression(OpCodes.Stloc, byteCode.StoreTo[0], expr)); + ast.Add(new ILExpression(ILCode.Stloc, byteCode.StoreTo[0], expr)); } else { ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true }; - ast.Add(new ILExpression(OpCodes.Stloc, tmpVar, expr)); + ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr)); foreach(ILVariable storeTo in byteCode.StoreTo) { - ast.Add(new ILExpression(OpCodes.Stloc, storeTo, new ILExpression(OpCodes.Ldloc, tmpVar))); + ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar))); } } } @@ -408,10 +399,10 @@ namespace Decompiler ILExpression currExpr = ast[i] as ILExpression; ILExpression nextExpr = ast[i + 1] as ILExpression; - if (currExpr != null && nextExpr != null && currExpr.OpCode.Code == Code.Stloc) { + if (currExpr != null && nextExpr != null && currExpr.Code == ILCode.Stloc) { // If the next expression is generated stloc, look inside - if (nextExpr.OpCode.Code == Code.Stloc && ((ILVariable)nextExpr.Operand).IsGenerated) { + if (nextExpr.Code == ILCode.Stloc && ((ILVariable)nextExpr.Operand).IsGenerated) { nextExpr = nextExpr.Arguments[0]; } @@ -421,7 +412,7 @@ namespace Decompiler // We are moving the expression evaluation past the other aguments. // It is ok to pass ldloc because the expression can not contain stloc and thus the ldcoc will still return the same value - if (arg.OpCode.Code == Code.Ldloc) { + if (arg.Code == ILCode.Ldloc) { bool canInline; allowInline.TryGetValue((ILVariable)arg.Operand, out canInline); if (arg.Operand == currExpr.Operand && canInline) { @@ -443,4 +434,17 @@ namespace Decompiler return ast; } } + + public static class ILAstBuilderExtensionMethods + { + public static List CutRange(this List list, int start, int count) + { + List ret = new List(count); + for (int i = 0; i < count; i++) { + ret.Add(list[start + i]); + } + list.RemoveRange(start, count); + return ret; + } + } } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 3e48aa4d9..fb1c3db78 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -4,7 +4,6 @@ using System.Linq; using ICSharpCode.Decompiler.FlowAnalysis; using Mono.Cecil; using Mono.Cecil.Cil; -using Decompiler.Rocks; namespace Decompiler.ControlFlow { @@ -76,7 +75,7 @@ namespace Decompiler.ControlFlow { // Remve no-ops // TODO: Assign the no-op range to someting - block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).OpCode == OpCodes.Nop)).ToList(); + block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).Code == ILCode.Nop)).ToList(); List moveableBlocks = new List(); @@ -96,8 +95,8 @@ namespace Decompiler.ControlFlow if ((currNode is ILLabel && !(lastNode is ILLabel)) || lastNode is ILTryCatchBlock || currNode is ILTryCatchBlock || - (lastNode is ILExpression) && ((ILExpression)lastNode).OpCode.IsBranch() || - (currNode is ILExpression) && ((ILExpression)currNode).OpCode.IsBranch()) + (lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() || + (currNode is ILExpression) && ((ILExpression)currNode).IsBranch()) { ILBlock lastBlock = moveableBlock; moveableBlock = new ILMoveableBlock() { OriginalOrder = (nextBlockIndex++) }; @@ -105,9 +104,9 @@ namespace Decompiler.ControlFlow // Explicit branch from one block to other // (unless the last expression was unconditional branch) - if (!(lastNode is ILExpression) || ((ILExpression)lastNode).OpCode.CanFallThough()) { + if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) { ILLabel blockLabel = new ILLabel() { Name = "Block_" + moveableBlock.OriginalOrder }; - lastBlock.Body.Add(new ILExpression(OpCodes.Br, blockLabel)); + lastBlock.Body.Add(new ILExpression(ILCode.Br, blockLabel)); moveableBlock.Body.Add(blockLabel); } } @@ -466,7 +465,7 @@ namespace Decompiler.ControlFlow if (block != null) { List flatBody = new List(); if (block.EntryPoint != null) { - flatBody.Add(new ILExpression(OpCodes.Br, block.EntryPoint)); + flatBody.Add(new ILExpression(ILCode.Br, block.EntryPoint)); block.EntryPoint = null; } foreach (ILNode child in block.Body) { @@ -496,7 +495,7 @@ namespace Decompiler.ControlFlow 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)) { + if (expr != null && (expr.Code == ILCode.Br)) { // 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; diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 89b3b9667..81341dbbd 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -160,7 +160,7 @@ namespace Decompiler public class ILExpression : ILNode { - public OpCode OpCode { get; set; } + public ILCode Code { get; set; } public object Operand { get; set; } public List Arguments { get; set; } // Mapping to the original instructions (useful for debugging) @@ -168,14 +168,19 @@ namespace Decompiler public TypeReference InferredType { get; set; } - public ILExpression(OpCode opCode, object operand, params ILExpression[] args) + public ILExpression(ILCode code, object operand, params ILExpression[] args) { - this.OpCode = opCode; + this.Code = code; this.Operand = operand; this.Arguments = new List(args); this.ILRanges = new List(1); } + public bool IsBranch() + { + return this.Operand is ILLabel || this.Operand is ILLabel[]; + } + public IEnumerable GetBranchTargets() { if (this.Operand is ILLabel) { @@ -216,12 +221,12 @@ namespace Decompiler public override void WriteTo(ITextOutput output) { if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) { - if (OpCode == OpCodes.Stloc && this.InferredType == null) { + if (Code == ILCode.Stloc && this.InferredType == null) { output.Write(((ILVariable)Operand).Name); output.Write(" = "); Arguments.First().WriteTo(output); return; - } else if (OpCode == OpCodes.Ldloc) { + } else if (Code == ILCode.Ldloc) { output.Write(((ILVariable)Operand).Name); if (this.InferredType != null) { output.Write(':'); @@ -231,7 +236,7 @@ namespace Decompiler } } - output.Write(OpCode.Name); + output.Write(Code.GetName()); if (this.InferredType != null) { output.Write(':'); this.InferredType.WriteTo(output, true, true); diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs new file mode 100644 index 000000000..62d7a1169 --- /dev/null +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -0,0 +1,423 @@ +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2010 Jb Evain +// +// 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 Mono.Cecil; +using Mono.Cecil.Cil; + +namespace Decompiler +{ + public enum ILCode + { + // For convenience, the start is exactly identical to Mono.Cecil.Cil.Code + // The macro instructions should never be used and are therefore prepended by __ + Nop, + Break, + __Ldarg_0, + __Ldarg_1, + __Ldarg_2, + __Ldarg_3, + __Ldloc_0, + __Ldloc_1, + __Ldloc_2, + __Ldloc_3, + __Stloc_0, + __Stloc_1, + __Stloc_2, + __Stloc_3, + __Ldarg_S, + __Ldarga_S, + __Starg_S, + __Ldloc_S, + __Ldloca_S, + __Stloc_S, + Ldnull, + __Ldc_I4_M1, + __Ldc_I4_0, + __Ldc_I4_1, + __Ldc_I4_2, + __Ldc_I4_3, + __Ldc_I4_4, + __Ldc_I4_5, + __Ldc_I4_6, + __Ldc_I4_7, + __Ldc_I4_8, + __Ldc_I4_S, + Ldc_I4, + Ldc_I8, + Ldc_R4, + Ldc_R8, + Dup, + Pop, + Jmp, + Call, + Calli, + Ret, + __Br_S, + __Brfalse_S, + __Brtrue_S, + __Beq_S, + __Bge_S, + __Bgt_S, + __Ble_S, + __Blt_S, + __Bne_Un_S, + __Bge_Un_S, + __Bgt_Un_S, + __Ble_Un_S, + __Blt_Un_S, + Br, + Brfalse, + Brtrue, + Beq, + Bge, + Bgt, + Ble, + Blt, + Bne_Un, + Bge_Un, + Bgt_Un, + Ble_Un, + Blt_Un, + Switch, + Ldind_I1, + Ldind_U1, + Ldind_I2, + Ldind_U2, + Ldind_I4, + Ldind_U4, + Ldind_I8, + Ldind_I, + Ldind_R4, + Ldind_R8, + Ldind_Ref, + Stind_Ref, + Stind_I1, + Stind_I2, + Stind_I4, + Stind_I8, + Stind_R4, + Stind_R8, + Add, + Sub, + Mul, + Div, + Div_Un, + Rem, + Rem_Un, + And, + Or, + Xor, + Shl, + Shr, + Shr_Un, + Neg, + Not, + Conv_I1, + Conv_I2, + Conv_I4, + Conv_I8, + Conv_R4, + Conv_R8, + Conv_U4, + Conv_U8, + Callvirt, + Cpobj, + Ldobj, + Ldstr, + Newobj, + Castclass, + Isinst, + Conv_R_Un, + Unbox, + Throw, + Ldfld, + Ldflda, + Stfld, + Ldsfld, + Ldsflda, + Stsfld, + Stobj, + Conv_Ovf_I1_Un, + Conv_Ovf_I2_Un, + Conv_Ovf_I4_Un, + Conv_Ovf_I8_Un, + Conv_Ovf_U1_Un, + Conv_Ovf_U2_Un, + Conv_Ovf_U4_Un, + Conv_Ovf_U8_Un, + Conv_Ovf_I_Un, + Conv_Ovf_U_Un, + Box, + Newarr, + Ldlen, + Ldelema, + Ldelem_I1, + Ldelem_U1, + Ldelem_I2, + Ldelem_U2, + Ldelem_I4, + Ldelem_U4, + Ldelem_I8, + Ldelem_I, + Ldelem_R4, + Ldelem_R8, + Ldelem_Ref, + Stelem_I, + Stelem_I1, + Stelem_I2, + Stelem_I4, + Stelem_I8, + Stelem_R4, + Stelem_R8, + Stelem_Ref, + Ldelem_Any, + Stelem_Any, + Unbox_Any, + Conv_Ovf_I1, + Conv_Ovf_U1, + Conv_Ovf_I2, + Conv_Ovf_U2, + Conv_Ovf_I4, + Conv_Ovf_U4, + Conv_Ovf_I8, + Conv_Ovf_U8, + Refanyval, + Ckfinite, + Mkrefany, + Ldtoken, + Conv_U2, + Conv_U1, + Conv_I, + Conv_Ovf_I, + Conv_Ovf_U, + Add_Ovf, + Add_Ovf_Un, + Mul_Ovf, + Mul_Ovf_Un, + Sub_Ovf, + Sub_Ovf_Un, + Endfinally, + Leave, + __Leave_S, + Stind_I, + Conv_U, + Arglist, + Ceq, + Cgt, + Cgt_Un, + Clt, + Clt_Un, + Ldftn, + Ldvirtftn, + Ldarg, + Ldarga, + Starg, + Ldloc, + Ldloca, + Stloc, + Localloc, + Endfilter, + Unaligned, + Volatile, + Tail, + Initobj, + Constrained, + Cpblk, + Initblk, + No, + Rethrow, + Sizeof, + Refanytype, + Readonly, + + // Virtual codes - defined for convenience + } + + public static class ILCodeUtil + { + public static string GetName(this ILCode code) + { + return code.ToString().ToLowerInvariant().Replace('_','.'); + } + + public static bool CanFallThough(this ILCode code) + { + switch(code) { + case ILCode.Br: + case ILCode.__Br_S: + case ILCode.Leave: + case ILCode.__Leave_S: + case ILCode.Ret: + case ILCode.Endfilter: + case ILCode.Endfinally: + case ILCode.Throw: + case ILCode.Rethrow: + return false; + default: + return true; + } + } + + public static int? GetPopCount(this Instruction inst) + { + switch(inst.OpCode.StackBehaviourPop) { + case StackBehaviour.Pop0: return 0; + case StackBehaviour.Pop1: return 1; + case StackBehaviour.Popi: return 1; + case StackBehaviour.Popref: return 1; + case StackBehaviour.Pop1_pop1: return 2; + case StackBehaviour.Popi_pop1: return 2; + case StackBehaviour.Popi_popi: return 2; + case StackBehaviour.Popi_popi8: return 2; + case StackBehaviour.Popi_popr4: return 2; + case StackBehaviour.Popi_popr8: return 2; + case StackBehaviour.Popref_pop1: return 2; + case StackBehaviour.Popref_popi: return 2; + case StackBehaviour.Popi_popi_popi: return 3; + case StackBehaviour.Popref_popi_popi: return 3; + case StackBehaviour.Popref_popi_popi8: return 3; + case StackBehaviour.Popref_popi_popr4: return 3; + case StackBehaviour.Popref_popi_popr8: return 3; + case StackBehaviour.Popref_popi_popref: return 3; + case StackBehaviour.PopAll: return null; + case StackBehaviour.Varpop: + switch(inst.OpCode.Code) { + case Code.Call: + case Code.Callvirt: + MethodReference cecilMethod = ((MethodReference)inst.Operand); + if (cecilMethod.HasThis) { + return cecilMethod.Parameters.Count + 1 /* this */; + } else { + return cecilMethod.Parameters.Count; + } + case Code.Calli: throw new NotImplementedException(); + case Code.Ret: return null; + case Code.Newobj: + MethodReference ctorMethod = ((MethodReference)inst.Operand); + return ctorMethod.Parameters.Count; + default: throw new Exception("Unknown Varpop opcode"); + } + default: throw new Exception("Unknown pop behaviour: " + inst.OpCode.StackBehaviourPop); + } + } + + public static int GetPushCount(this Instruction inst) + { + switch(inst.OpCode.StackBehaviourPush) { + case StackBehaviour.Push0: return 0; + case StackBehaviour.Push1: return 1; + case StackBehaviour.Push1_push1: return 2; + case StackBehaviour.Pushi: return 1; + case StackBehaviour.Pushi8: return 1; + case StackBehaviour.Pushr4: return 1; + case StackBehaviour.Pushr8: return 1; + case StackBehaviour.Pushref: return 1; + case StackBehaviour.Varpush: // Happens only for calls + switch(inst.OpCode.Code) { + case Code.Call: + case Code.Callvirt: + MethodReference cecilMethod = ((MethodReference)inst.Operand); + if (cecilMethod.ReturnType.FullName == "System.Void") { + return 0; + } else { + return 1; + } + case Code.Calli: throw new NotImplementedException(); + default: throw new Exception("Unknown Varpush opcode"); + } + default: throw new Exception("Unknown push behaviour: " + inst.OpCode.StackBehaviourPush); + } + } + + public static void ExpandMacro(ref ILCode code, ref object operand, MethodBody methodBody) + { + switch (code) { + case ILCode.__Ldarg_0: code = ILCode.Ldarg; operand = methodBody.GetParameter(0); break; + case ILCode.__Ldarg_1: code = ILCode.Ldarg; operand = methodBody.GetParameter(1); break; + case ILCode.__Ldarg_2: code = ILCode.Ldarg; operand = methodBody.GetParameter(2); break; + case ILCode.__Ldarg_3: code = ILCode.Ldarg; operand = methodBody.GetParameter(3); break; + case ILCode.__Ldloc_0: code = ILCode.Ldloc; operand = methodBody.Variables[0]; break; + case ILCode.__Ldloc_1: code = ILCode.Ldloc; operand = methodBody.Variables[1]; break; + case ILCode.__Ldloc_2: code = ILCode.Ldloc; operand = methodBody.Variables[2]; break; + case ILCode.__Ldloc_3: code = ILCode.Ldloc; operand = methodBody.Variables[3]; break; + case ILCode.__Stloc_0: code = ILCode.Stloc; operand = methodBody.Variables[0]; break; + case ILCode.__Stloc_1: code = ILCode.Stloc; operand = methodBody.Variables[1]; break; + case ILCode.__Stloc_2: code = ILCode.Stloc; operand = methodBody.Variables[2]; break; + case ILCode.__Stloc_3: code = ILCode.Stloc; operand = methodBody.Variables[3]; break; + case ILCode.__Ldarg_S: code = ILCode.Ldarg; break; + case ILCode.__Ldarga_S: code = ILCode.Ldarga; break; + case ILCode.__Starg_S: code = ILCode.Starg; break; + case ILCode.__Ldloc_S: code = ILCode.Ldloc; break; + case ILCode.__Ldloca_S: code = ILCode.Ldloca; break; + case ILCode.__Stloc_S: code = ILCode.Stloc; break; + case ILCode.__Ldc_I4_M1: code = ILCode.Ldc_I4; operand = -1; break; + case ILCode.__Ldc_I4_0: code = ILCode.Ldc_I4; operand = 0; break; + case ILCode.__Ldc_I4_1: code = ILCode.Ldc_I4; operand = 1; break; + case ILCode.__Ldc_I4_2: code = ILCode.Ldc_I4; operand = 2; break; + case ILCode.__Ldc_I4_3: code = ILCode.Ldc_I4; operand = 3; break; + case ILCode.__Ldc_I4_4: code = ILCode.Ldc_I4; operand = 4; break; + case ILCode.__Ldc_I4_5: code = ILCode.Ldc_I4; operand = 5; break; + case ILCode.__Ldc_I4_6: code = ILCode.Ldc_I4; operand = 6; break; + case ILCode.__Ldc_I4_7: code = ILCode.Ldc_I4; operand = 7; break; + case ILCode.__Ldc_I4_8: code = ILCode.Ldc_I4; operand = 8; break; + case ILCode.__Ldc_I4_S: code = ILCode.Ldc_I4; operand = (int) (sbyte) operand; break; + case ILCode.__Br_S: code = ILCode.Br; break; + case ILCode.__Brfalse_S: code = ILCode.Brfalse; break; + case ILCode.__Brtrue_S: code = ILCode.Brtrue; break; + case ILCode.__Beq_S: code = ILCode.Beq; break; + case ILCode.__Bge_S: code = ILCode.Bge; break; + case ILCode.__Bgt_S: code = ILCode.Bgt; break; + case ILCode.__Ble_S: code = ILCode.Ble; break; + case ILCode.__Blt_S: code = ILCode.Blt; break; + case ILCode.__Bne_Un_S: code = ILCode.Bne_Un; break; + case ILCode.__Bge_Un_S: code = ILCode.Bge_Un; break; + case ILCode.__Bgt_Un_S: code = ILCode.Bgt_Un; break; + case ILCode.__Ble_Un_S: code = ILCode.Ble_Un; break; + case ILCode.__Blt_Un_S: code = ILCode.Blt_Un; break; + case ILCode.__Leave_S: code = ILCode.Leave; break; + } + } + + public static ParameterDefinition GetParameter (this MethodBody self, int index) + { + var method = self.Method; + + if (method.HasThis) { + if (index == 0) + return self.ThisParameter; + + index--; + } + + var parameters = method.Parameters; + + if (index < 0 || index >= parameters.Count) + return null; + + return parameters [index]; + } + } +} diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 08f2ceb21..1a77cca4e 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -40,7 +40,7 @@ namespace Decompiler ILExpression expr = child as ILExpression; if (expr != null) { ILVariable v = expr.Operand as ILVariable; - if (v != null && v.IsGenerated && v.Type == null && expr.OpCode == OpCodes.Stloc) { + if (v != null && v.IsGenerated && v.Type == null && expr.Code == ILCode.Stloc) { // don't deal with this node or its children yet, // wait for the expected type to be inferred first storedToGeneratedVariables.Add(expr); @@ -70,7 +70,7 @@ namespace Decompiler TypeReference DoInferTypeForExpression(ILExpression expr, TypeReference expectedType, bool forceInferChildren = false) { - switch (expr.OpCode.Code) { + switch ((Code)expr.Code) { #region Variable load/store case Code.Stloc: if (forceInferChildren) @@ -387,7 +387,7 @@ namespace Decompiler case Code.Dup: return InferTypeForExpression(expr.Arguments.Single(), expectedType); default: - Debug.WriteLine("Type Inference: Can't handle " + expr.OpCode.Name); + Debug.WriteLine("Type Inference: Can't handle " + expr.Code.GetName()); return null; } } diff --git a/ICSharpCode.Decompiler/Mono.Cecil.Rocks/Constants.cs b/ICSharpCode.Decompiler/Mono.Cecil.Rocks/Constants.cs deleted file mode 100644 index 537d0a078..000000000 --- a/ICSharpCode.Decompiler/Mono.Cecil.Rocks/Constants.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) -// This code is distributed under MIT X11 license (for details please see \doc\license.txt) - -using System; - -namespace Decompiler -{ - public class Constants - { - public const string Object = "System.Object"; - public const string Int32 = "System.Int32"; - public const string Boolean = "System.Boolean"; - public const string Void = "System.Void"; - } -} diff --git a/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MethodBodyRocks.cs b/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MethodBodyRocks.cs deleted file mode 100644 index dbe95b085..000000000 --- a/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MethodBodyRocks.cs +++ /dev/null @@ -1,478 +0,0 @@ -// -// MethodBodyRocks.cs -// -// Author: -// Jb Evain (jbevain@gmail.com) -// -// Copyright (c) 2008 - 2010 Jb Evain -// -// 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 Mono.Cecil.Cil; - -namespace Mono.Cecil.Rocks { - -#if INSIDE_ROCKS - public -#endif - static class MethodBodyRocks { - - public static ParameterDefinition GetParameter (this MethodBody self, int index) - { - var method = self.Method; - - if (method.HasThis) { - if (index == 0) - return self.ThisParameter; - - index--; - } - - var parameters = method.Parameters; - - if (index < 0 || index >= parameters.Count) - return null; - - return parameters [index]; - } - - public static void SimplifyMacros (this MethodBody self) - { - if (self == null) - throw new ArgumentNullException ("self"); - - foreach (var instruction in self.Instructions) { - if (instruction.OpCode.OpCodeType != OpCodeType.Macro) - continue; - - switch (instruction.OpCode.Code) { - case Code.Ldarg_0: - ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (0)); - break; - case Code.Ldarg_1: - ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (1)); - break; - case Code.Ldarg_2: - ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (2)); - break; - case Code.Ldarg_3: - ExpandMacro (instruction, OpCodes.Ldarg, self.GetParameter (3)); - break; - case Code.Ldloc_0: - ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [0]); - break; - case Code.Ldloc_1: - ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [1]); - break; - case Code.Ldloc_2: - ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [2]); - break; - case Code.Ldloc_3: - ExpandMacro (instruction, OpCodes.Ldloc, self.Variables [3]); - break; - case Code.Stloc_0: - ExpandMacro (instruction, OpCodes.Stloc, self.Variables [0]); - break; - case Code.Stloc_1: - ExpandMacro (instruction, OpCodes.Stloc, self.Variables [1]); - break; - case Code.Stloc_2: - ExpandMacro (instruction, OpCodes.Stloc, self.Variables [2]); - break; - case Code.Stloc_3: - ExpandMacro (instruction, OpCodes.Stloc, self.Variables [3]); - break; - case Code.Ldarg_S: - instruction.OpCode = OpCodes.Ldarg; - break; - case Code.Ldarga_S: - instruction.OpCode = OpCodes.Ldarga; - break; - case Code.Starg_S: - instruction.OpCode = OpCodes.Starg; - break; - case Code.Ldloc_S: - instruction.OpCode = OpCodes.Ldloc; - break; - case Code.Ldloca_S: - instruction.OpCode = OpCodes.Ldloca; - break; - case Code.Stloc_S: - instruction.OpCode = OpCodes.Stloc; - break; - case Code.Ldc_I4_M1: - ExpandMacro (instruction, OpCodes.Ldc_I4, -1); - break; - case Code.Ldc_I4_0: - ExpandMacro (instruction, OpCodes.Ldc_I4, 0); - break; - case Code.Ldc_I4_1: - ExpandMacro (instruction, OpCodes.Ldc_I4, 1); - break; - case Code.Ldc_I4_2: - ExpandMacro (instruction, OpCodes.Ldc_I4, 2); - break; - case Code.Ldc_I4_3: - ExpandMacro (instruction, OpCodes.Ldc_I4, 3); - break; - case Code.Ldc_I4_4: - ExpandMacro (instruction, OpCodes.Ldc_I4, 4); - break; - case Code.Ldc_I4_5: - ExpandMacro (instruction, OpCodes.Ldc_I4, 5); - break; - case Code.Ldc_I4_6: - ExpandMacro (instruction, OpCodes.Ldc_I4, 6); - break; - case Code.Ldc_I4_7: - ExpandMacro (instruction, OpCodes.Ldc_I4, 7); - break; - case Code.Ldc_I4_8: - ExpandMacro (instruction, OpCodes.Ldc_I4, 8); - break; - case Code.Ldc_I4_S: - ExpandMacro (instruction, OpCodes.Ldc_I4, (int) (sbyte) instruction.Operand); - break; - case Code.Br_S: - instruction.OpCode = OpCodes.Br; - break; - case Code.Brfalse_S: - instruction.OpCode = OpCodes.Brfalse; - break; - case Code.Brtrue_S: - instruction.OpCode = OpCodes.Brtrue; - break; - case Code.Beq_S: - instruction.OpCode = OpCodes.Beq; - break; - case Code.Bge_S: - instruction.OpCode = OpCodes.Bge; - break; - case Code.Bgt_S: - instruction.OpCode = OpCodes.Bgt; - break; - case Code.Ble_S: - instruction.OpCode = OpCodes.Ble; - break; - case Code.Blt_S: - instruction.OpCode = OpCodes.Blt; - break; - case Code.Bne_Un_S: - instruction.OpCode = OpCodes.Bne_Un; - break; - case Code.Bge_Un_S: - instruction.OpCode = OpCodes.Bge_Un; - break; - case Code.Bgt_Un_S: - instruction.OpCode = OpCodes.Bgt_Un; - break; - case Code.Ble_Un_S: - instruction.OpCode = OpCodes.Ble_Un; - break; - case Code.Blt_Un_S: - instruction.OpCode = OpCodes.Blt_Un; - break; - case Code.Leave_S: - instruction.OpCode = OpCodes.Leave; - break; - } - } - } - - public static void ExpandMacro(ref OpCode opCode, ref object operand, MethodBody methodBody) - { - if (opCode.OpCodeType != OpCodeType.Macro) - return; - - switch (opCode.Code) { - case Code.Ldarg_0: opCode = OpCodes.Ldarg; operand = methodBody.GetParameter(0); break; - case Code.Ldarg_1: opCode = OpCodes.Ldarg; operand = methodBody.GetParameter(1); break; - case Code.Ldarg_2: opCode = OpCodes.Ldarg; operand = methodBody.GetParameter(2); break; - case Code.Ldarg_3: opCode = OpCodes.Ldarg; operand = methodBody.GetParameter(3); break; - case Code.Ldloc_0: opCode = OpCodes.Ldloc; operand = methodBody.Variables[0]; break; - case Code.Ldloc_1: opCode = OpCodes.Ldloc; operand = methodBody.Variables[1]; break; - case Code.Ldloc_2: opCode = OpCodes.Ldloc; operand = methodBody.Variables[2]; break; - case Code.Ldloc_3: opCode = OpCodes.Ldloc; operand = methodBody.Variables[3]; break; - case Code.Stloc_0: opCode = OpCodes.Stloc; operand = methodBody.Variables[0]; break; - case Code.Stloc_1: opCode = OpCodes.Stloc; operand = methodBody.Variables[1]; break; - case Code.Stloc_2: opCode = OpCodes.Stloc; operand = methodBody.Variables[2]; break; - case Code.Stloc_3: opCode = OpCodes.Stloc; operand = methodBody.Variables[3]; break; - case Code.Ldarg_S: opCode = OpCodes.Ldarg; break; - case Code.Ldarga_S: opCode = OpCodes.Ldarga; break; - case Code.Starg_S: opCode = OpCodes.Starg; break; - case Code.Ldloc_S: opCode = OpCodes.Ldloc; break; - case Code.Ldloca_S: opCode = OpCodes.Ldloca; break; - case Code.Stloc_S: opCode = OpCodes.Stloc; break; - case Code.Ldc_I4_M1: opCode = OpCodes.Ldc_I4; operand = -1; break; - case Code.Ldc_I4_0: opCode = OpCodes.Ldc_I4; operand = 0; break; - case Code.Ldc_I4_1: opCode = OpCodes.Ldc_I4; operand = 1; break; - case Code.Ldc_I4_2: opCode = OpCodes.Ldc_I4; operand = 2; break; - case Code.Ldc_I4_3: opCode = OpCodes.Ldc_I4; operand = 3; break; - case Code.Ldc_I4_4: opCode = OpCodes.Ldc_I4; operand = 4; break; - case Code.Ldc_I4_5: opCode = OpCodes.Ldc_I4; operand = 5; break; - case Code.Ldc_I4_6: opCode = OpCodes.Ldc_I4; operand = 6; break; - case Code.Ldc_I4_7: opCode = OpCodes.Ldc_I4; operand = 7; break; - case Code.Ldc_I4_8: opCode = OpCodes.Ldc_I4; operand = 8; break; - case Code.Ldc_I4_S: opCode = OpCodes.Ldc_I4; operand = (int) (sbyte) operand; break; - case Code.Br_S: opCode = OpCodes.Br; break; - case Code.Brfalse_S: opCode = OpCodes.Brfalse; break; - case Code.Brtrue_S: opCode = OpCodes.Brtrue; break; - case Code.Beq_S: opCode = OpCodes.Beq; break; - case Code.Bge_S: opCode = OpCodes.Bge; break; - case Code.Bgt_S: opCode = OpCodes.Bgt; break; - case Code.Ble_S: opCode = OpCodes.Ble; break; - case Code.Blt_S: opCode = OpCodes.Blt; break; - case Code.Bne_Un_S: opCode = OpCodes.Bne_Un; break; - case Code.Bge_Un_S: opCode = OpCodes.Bge_Un; break; - case Code.Bgt_Un_S: opCode = OpCodes.Bgt_Un; break; - case Code.Ble_Un_S: opCode = OpCodes.Ble_Un; break; - case Code.Blt_Un_S: opCode = OpCodes.Blt_Un; break; - case Code.Leave_S: opCode = OpCodes.Leave; break; - } - } - - static void ExpandMacro (Instruction instruction, OpCode opcode, object operand) - { - instruction.OpCode = opcode; - instruction.Operand = operand; - } - - static void MakeMacro (Instruction instruction, OpCode opcode) - { - instruction.OpCode = opcode; - instruction.Operand = null; - } - - public static void OptimizeMacros (this MethodBody self) - { - if (self == null) - throw new ArgumentNullException ("self"); - - var method = self.Method; - - foreach (var instruction in self.Instructions) { - int index; - switch (instruction.OpCode.Code) { - case Code.Ldarg: - index = ((ParameterDefinition) instruction.Operand).Index; - if (index == -1 && instruction.Operand == self.ThisParameter) - index = 0; - else if (method.HasThis) - index++; - - switch (index) { - case 0: - MakeMacro (instruction, OpCodes.Ldarg_0); - break; - case 1: - MakeMacro (instruction, OpCodes.Ldarg_1); - break; - case 2: - MakeMacro (instruction, OpCodes.Ldarg_2); - break; - case 3: - MakeMacro (instruction, OpCodes.Ldarg_3); - break; - default: - if (index < 256) - ExpandMacro (instruction, OpCodes.Ldarg_S, instruction.Operand); - break; - } - break; - case Code.Ldloc: - index = ((VariableDefinition) instruction.Operand).Index; - switch (index) { - case 0: - MakeMacro (instruction, OpCodes.Ldloc_0); - break; - case 1: - MakeMacro (instruction, OpCodes.Ldloc_1); - break; - case 2: - MakeMacro (instruction, OpCodes.Ldloc_2); - break; - case 3: - MakeMacro (instruction, OpCodes.Ldloc_3); - break; - default: - if (index < 256) - ExpandMacro (instruction, OpCodes.Ldloc_S, instruction.Operand); - break; - } - break; - case Code.Stloc: - index = ((VariableDefinition) instruction.Operand).Index; - switch (index) { - case 0: - MakeMacro (instruction, OpCodes.Stloc_0); - break; - case 1: - MakeMacro (instruction, OpCodes.Stloc_1); - break; - case 2: - MakeMacro (instruction, OpCodes.Stloc_2); - break; - case 3: - MakeMacro (instruction, OpCodes.Stloc_3); - break; - default: - if (index < 256) - ExpandMacro (instruction, OpCodes.Stloc_S, instruction.Operand); - break; - } - break; - case Code.Ldarga: - index = ((ParameterDefinition) instruction.Operand).Index; - if (index == -1 && instruction.Operand == self.ThisParameter) - index = 0; - else if (method.HasThis) - index++; - if (index < 256) - ExpandMacro (instruction, OpCodes.Ldarga_S, instruction.Operand); - break; - case Code.Ldloca: - if (((VariableDefinition) instruction.Operand).Index < 256) - ExpandMacro (instruction, OpCodes.Ldloca_S, instruction.Operand); - break; - case Code.Ldc_I4: - int i = (int) instruction.Operand; - switch (i) { - case -1: - MakeMacro (instruction, OpCodes.Ldc_I4_M1); - break; - case 0: - MakeMacro (instruction, OpCodes.Ldc_I4_0); - break; - case 1: - MakeMacro (instruction, OpCodes.Ldc_I4_1); - break; - case 2: - MakeMacro (instruction, OpCodes.Ldc_I4_2); - break; - case 3: - MakeMacro (instruction, OpCodes.Ldc_I4_3); - break; - case 4: - MakeMacro (instruction, OpCodes.Ldc_I4_4); - break; - case 5: - MakeMacro (instruction, OpCodes.Ldc_I4_5); - break; - case 6: - MakeMacro (instruction, OpCodes.Ldc_I4_6); - break; - case 7: - MakeMacro (instruction, OpCodes.Ldc_I4_7); - break; - case 8: - MakeMacro (instruction, OpCodes.Ldc_I4_8); - break; - default: - if (i >= -128 && i < 128) - ExpandMacro (instruction, OpCodes.Ldc_I4_S, (sbyte) i); - break; - } - break; - } - } - - OptimizeBranches (self); - } - - static void OptimizeBranches (MethodBody body) - { - ComputeOffsets (body); - - foreach (var instruction in body.Instructions) { - if (instruction.OpCode.OperandType != OperandType.InlineBrTarget) - continue; - - if (OptimizeBranch (instruction)) - ComputeOffsets (body); - } - } - - static bool OptimizeBranch (Instruction instruction) - { - var offset = ((Instruction) instruction.Operand).Offset - (instruction.Offset + instruction.OpCode.Size + 4); - if (!(offset >= -128 && offset <= 127)) - return false; - - switch (instruction.OpCode.Code) { - case Code.Br: - instruction.OpCode = OpCodes.Br_S; - break; - case Code.Brfalse: - instruction.OpCode = OpCodes.Brfalse_S; - break; - case Code.Brtrue: - instruction.OpCode = OpCodes.Brtrue_S; - break; - case Code.Beq: - instruction.OpCode = OpCodes.Beq_S; - break; - case Code.Bge: - instruction.OpCode = OpCodes.Bge_S; - break; - case Code.Bgt: - instruction.OpCode = OpCodes.Bgt_S; - break; - case Code.Ble: - instruction.OpCode = OpCodes.Ble_S; - break; - case Code.Blt: - instruction.OpCode = OpCodes.Blt_S; - break; - case Code.Bne_Un: - instruction.OpCode = OpCodes.Bne_Un_S; - break; - case Code.Bge_Un: - instruction.OpCode = OpCodes.Bge_Un_S; - break; - case Code.Bgt_Un: - instruction.OpCode = OpCodes.Bgt_Un_S; - break; - case Code.Ble_Un: - instruction.OpCode = OpCodes.Ble_Un_S; - break; - case Code.Blt_Un: - instruction.OpCode = OpCodes.Blt_Un_S; - break; - case Code.Leave: - instruction.OpCode = OpCodes.Leave_S; - break; - } - - return true; - } - - static void ComputeOffsets (MethodBody body) - { - var offset = 0; - foreach (var instruction in body.Instructions) { - instruction.Offset = offset; - offset += instruction.GetSize (); - } - } - } -} diff --git a/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs b/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs deleted file mode 100644 index 215051edf..000000000 --- a/ICSharpCode.Decompiler/Mono.Cecil.Rocks/MyRocks.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Created by SharpDevelop. - * User: User - * Date: 05/02/2011 - * Time: 10:10 - * - * To change this template use Tools | Options | Coding | Edit Standard Headers. - */ -using System; -using System.Collections.Generic; -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace Decompiler.Rocks -{ - static class MyRocks - { - public static List CutRange(this List list, int start, int count) - { - List ret = new List(count); - for (int i = 0; i < count; i++) { - ret.Add(list[start + i]); - } - list.RemoveRange(start, count); - return ret; - } - - public static bool CanFallThough(this OpCode opCode) - { - switch(opCode.FlowControl) { - case FlowControl.Branch: return false; - case FlowControl.Cond_Branch: return true; - case FlowControl.Next: return true; - case FlowControl.Call: return true; - case FlowControl.Return: return false; - case FlowControl.Throw: return false; - case FlowControl.Meta: return true; - default: throw new NotImplementedException(); - } - } - - public static bool IsBranch(this OpCode opCode) - { - return opCode.FlowControl == FlowControl.Branch || opCode.FlowControl == FlowControl.Cond_Branch; - } - - public static int? GetPopCount(this Instruction inst) - { - switch(inst.OpCode.StackBehaviourPop) { - case StackBehaviour.Pop0: return 0; - case StackBehaviour.Pop1: return 1; - case StackBehaviour.Popi: return 1; - case StackBehaviour.Popref: return 1; - case StackBehaviour.Pop1_pop1: return 2; - case StackBehaviour.Popi_pop1: return 2; - case StackBehaviour.Popi_popi: return 2; - case StackBehaviour.Popi_popi8: return 2; - case StackBehaviour.Popi_popr4: return 2; - case StackBehaviour.Popi_popr8: return 2; - case StackBehaviour.Popref_pop1: return 2; - case StackBehaviour.Popref_popi: return 2; - case StackBehaviour.Popi_popi_popi: return 3; - case StackBehaviour.Popref_popi_popi: return 3; - case StackBehaviour.Popref_popi_popi8: return 3; - case StackBehaviour.Popref_popi_popr4: return 3; - case StackBehaviour.Popref_popi_popr8: return 3; - case StackBehaviour.Popref_popi_popref: return 3; - case StackBehaviour.PopAll: return null; - case StackBehaviour.Varpop: - switch(inst.OpCode.Code) { - case Code.Call: - case Code.Callvirt: - MethodReference cecilMethod = ((MethodReference)inst.Operand); - if (cecilMethod.HasThis) { - return cecilMethod.Parameters.Count + 1 /* this */; - } else { - return cecilMethod.Parameters.Count; - } - case Code.Calli: throw new NotImplementedException(); - case Code.Ret: return null; - case Code.Newobj: - MethodReference ctorMethod = ((MethodReference)inst.Operand); - return ctorMethod.Parameters.Count; - default: throw new Exception("Unknown Varpop opcode"); - } - default: throw new Exception("Unknown pop behaviour: " + inst.OpCode.StackBehaviourPop); - } - } - - public static int GetPushCount(this Instruction inst) - { - switch(inst.OpCode.StackBehaviourPush) { - case StackBehaviour.Push0: return 0; - case StackBehaviour.Push1: return 1; - case StackBehaviour.Push1_push1: return 2; - case StackBehaviour.Pushi: return 1; - case StackBehaviour.Pushi8: return 1; - case StackBehaviour.Pushr4: return 1; - case StackBehaviour.Pushr8: return 1; - case StackBehaviour.Pushref: return 1; - case StackBehaviour.Varpush: // Happens only for calls - switch(inst.OpCode.Code) { - case Code.Call: - case Code.Callvirt: - MethodReference cecilMethod = ((MethodReference)inst.Operand); - if (cecilMethod.ReturnType.FullName == Constants.Void) { - return 0; - } else { - return 1; - } - case Code.Calli: throw new NotImplementedException(); - default: throw new Exception("Unknown Varpush opcode"); - } - default: throw new Exception("Unknown push behaviour: " + inst.OpCode.StackBehaviourPush); - } - } - } -} From 986e5e06cc2426c6e10b86f860a423c8bb677ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Srbeck=C3=BD?= Date: Sat, 19 Feb 2011 12:28:58 +0000 Subject: [PATCH 25/27] Proper support for catch block argument --- .../Ast/AstMethodBodyBuilder.cs | 2 +- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 59 ++++++++++++++----- ICSharpCode.Decompiler/ILAst/ILAstTypes.cs | 1 + ICSharpCode.Decompiler/ILAst/ILCodes.cs | 3 +- 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 5dfecb27b..769c1367c 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -206,7 +206,7 @@ namespace Decompiler tryCatchStmt.CatchClauses.Add( new Ast.CatchClause { Type = AstBuilder.ConvertType(catchClause.ExceptionType), - VariableName = "exception", + VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, Body = TransformBlock(catchClause) }); } diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 22c340a1d..e8a219e67 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -93,6 +93,9 @@ namespace Decompiler Dictionary instrToByteCode = new Dictionary(); Dictionary allowInline = new Dictionary(); + // Virtual instructions to load exception on stack + Dictionary ldexceptions = new Dictionary(); + public List Variables; public List Build(MethodDefinition methodDef, bool optimize) @@ -147,7 +150,14 @@ namespace Decompiler ByteCode handlerStart = instrToByteCode[ex.HandlerType == ExceptionHandlerType.Filter ? ex.FilterStart : ex.HandlerStart]; handlerStart.StackBefore = new List(); if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) { - handlerStart.StackBefore.Add(new StackSlot(null)); + ByteCode ldexception = new ByteCode() { + Code = ILCode.Ldexception, + Operand = ex.CatchType, + PopCount = 0, + PushCount = 1 + }; + ldexceptions[ex] = ldexception; + handlerStart.StackBefore.Add(new StackSlot(ldexception)); } agenda.Enqueue(handlerStart); @@ -231,13 +241,10 @@ namespace Decompiler ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true }; arg.LoadFrom = tmpVar; foreach(ByteCode pushedBy in arg.PushedBy) { - // TODO: Handle exception variables - if (pushedBy != null) { - if (pushedBy.StoreTo == null) { - pushedBy.StoreTo = new List(1); - } - pushedBy.StoreTo.Add(tmpVar); + if (pushedBy.StoreTo == null) { + pushedBy.StoreTo = new List(1); } + pushedBy.StoreTo.Add(tmpVar); } if (arg.PushedBy.Count == 1) { allowInline[tmpVar] = true; @@ -329,12 +336,38 @@ namespace Decompiler ehs.ExceptWith(nestedEHs); List handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs); if (eh.HandlerType == ExceptionHandlerType.Catch) { - tryCatchBlock.CatchBlocks.Add(new ILTryCatchBlock.CatchBlock() { + ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() { ExceptionType = eh.CatchType, Body = handlerAst - }); + }; + // Handle the automatically pushed exception on the stack + ByteCode ldexception = ldexceptions[eh]; + if (ldexception.StoreTo.Count == 0) { + throw new Exception("Exception should be consumed by something"); + } else if (ldexception.StoreTo.Count == 1) { + ILExpression first = catchBlock.Body[0] as ILExpression; + if (first != null && + first.Code == ILCode.Pop && + first.Arguments[0].Code == ILCode.Ldloc && + first.Arguments[0].Operand == ldexception.StoreTo[0]) + { + // The exception is just poped - optimize it all away; + catchBlock.ExceptionVariable = null; + catchBlock.Body.RemoveAt(0); + } else { + catchBlock.ExceptionVariable = ldexception.StoreTo[0]; + } + } else { + ILVariable exTemp = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true }; + catchBlock.ExceptionVariable = exTemp; + foreach(ILVariable storeTo in ldexception.StoreTo) { + catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp))); + } + } + tryCatchBlock.CatchBlocks.Add(catchBlock); } else if (eh.HandlerType == ExceptionHandlerType.Finally) { tryCatchBlock.FinallyBlock = new ILBlock(handlerAst); + // TODO: ldexception } else { // TODO } @@ -369,13 +402,7 @@ namespace Decompiler int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { StackSlot slot = byteCode.StackBefore[i]; - if (slot.PushedBy != null) { - ILExpression ldExpr = new ILExpression(ILCode.Ldloc, slot.LoadFrom); - expr.Arguments.Add(ldExpr); - } else { - ILExpression ldExpr = new ILExpression(ILCode.Ldloc, new ILVariable() { Name = "ex", IsGenerated = true }); - expr.Arguments.Add(ldExpr); - } + expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom)); } // Store the result to temporary variable(s) if needed diff --git a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs index 81341dbbd..143548b6d 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs @@ -88,6 +88,7 @@ namespace Decompiler public class CatchBlock: ILBlock { public TypeReference ExceptionType; + public ILVariable ExceptionVariable; public override void WriteTo(ITextOutput output) { diff --git a/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/ICSharpCode.Decompiler/ILAst/ILCodes.cs index 62d7a1169..7cb0abec5 100644 --- a/ICSharpCode.Decompiler/ILAst/ILCodes.cs +++ b/ICSharpCode.Decompiler/ILAst/ILCodes.cs @@ -253,7 +253,8 @@ namespace Decompiler Refanytype, Readonly, - // Virtual codes - defined for convenience + // Virtual codes - defined for convenience + Ldexception, // Operand holds the CatchType for catch handler, null for filter } public static class ILCodeUtil From 8b342ea469b9ed92dafbd6d9f218233ba4979c8b Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 19 Feb 2011 14:18:45 +0100 Subject: [PATCH 26/27] Use ILVariable as ldloca operand. --- ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs | 12 ++---------- ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs | 9 +++++++-- ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 769c1367c..00f05951a 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -608,17 +608,9 @@ namespace Decompiler AstBuilder.ConvertType(((FieldReference)operand).DeclaringType) .Member(((FieldReference)operand).Name).WithAnnotation(operand)); case Code.Ldloc: - if (operand is ILVariable) { - return new Ast.IdentifierExpression(((ILVariable)operand).Name); - } else { - return new Ast.IdentifierExpression(((VariableDefinition)operand).Name); - } + return new Ast.IdentifierExpression(((ILVariable)operand).Name); case Code.Ldloca: - if (operand is ILVariable) { - return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name)); - } else { - return MakeRef(new Ast.IdentifierExpression(((VariableDefinition)operand).Name)); - } + return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name)); case Code.Ldnull: return new Ast.NullReferenceExpression(); case Code.Ldobj: throw new NotImplementedException(); case Code.Ldstr: return new Ast.PrimitiveExpression(operand); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index e8a219e67..5dae13326 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -262,11 +262,16 @@ namespace Decompiler int index = ((VariableDefinition)byteCode.Operand).Index; byteCode.Operand = Variables[index]; numReads[index]++; - } - if (byteCode.Code == ILCode.Stloc) { + } else if (byteCode.Code == ILCode.Stloc) { int index = ((VariableDefinition)byteCode.Operand).Index; byteCode.Operand = Variables[index]; numWrites[index]++; + } else if (byteCode.Code == ILCode.Ldloca) { + int index = ((VariableDefinition)byteCode.Operand).Index; + byteCode.Operand = Variables[index]; + // ldloca leads to an unknown numbers of reads/writes, so ensure we don't inline the variable + numReads[index] += 2; + numWrites[index] += 2; } } diff --git a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs index 1a77cca4e..b5a17a782 100644 --- a/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs +++ b/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs @@ -85,7 +85,7 @@ namespace Decompiler case Code.Ldarg: return ((ParameterReference)expr.Operand).ParameterType; case Code.Ldloca: - return new ByReferenceType(((VariableDefinition)expr.Operand).VariableType); + return new ByReferenceType(((ILVariable)expr.Operand).Type); case Code.Ldarga: return new ByReferenceType(((ParameterReference)expr.Operand).ParameterType); #endregion From 4be02c001ff84f3f08be417c4ffcf331b8022df9 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 19 Feb 2011 15:12:16 +0100 Subject: [PATCH 27/27] Improve naming of local variables. --- ICSharpCode.Decompiler/Ast/AstBuilder.cs | 7 + .../Ast/AstMethodBodyBuilder.cs | 57 +------ ICSharpCode.Decompiler/Ast/NameVariables.cs | 158 ++++++++++++++++++ .../ICSharpCode.Decompiler.csproj | 1 + 4 files changed, 167 insertions(+), 56 deletions(-) create mode 100644 ICSharpCode.Decompiler/Ast/NameVariables.cs diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index e4978d823..2d775d770 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -198,6 +198,13 @@ namespace Decompiler .MakeArrayType((type as Mono.Cecil.ArrayType).Rank); } else if (type is GenericInstanceType) { GenericInstanceType gType = (GenericInstanceType)type; + if (gType.ElementType.Namespace == "System" && gType.ElementType.Name == "Nullable`1" && gType.GenericArguments.Count == 1) { + typeIndex++; + return new ComposedType { + BaseType = ConvertType(gType.GenericArguments[0], typeAttributes, ref typeIndex), + HasNullableSpecifier = true + }; + } AstType baseType = ConvertType(gType.ElementType, typeAttributes, ref typeIndex); foreach (var typeArgument in gType.GenericArguments) { typeIndex++; diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 00f05951a..33dcbcef9 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -45,23 +45,6 @@ namespace Decompiler } } - static readonly Dictionary typeNameToVariableNameDict = new Dictionary { - { "System.Boolean", "flag" }, - { "System.Byte", "b" }, - { "System.SByte", "b" }, - { "System.Int16", "num" }, - { "System.Int32", "num" }, - { "System.Int64", "num" }, - { "System.UInt16", "num" }, - { "System.UInt32", "num" }, - { "System.UInt64", "num" }, - { "System.Single", "num" }, - { "System.Double", "num" }, - { "System.Decimal", "num" }, - { "System.String", "text" }, - { "System.Object", "obj" }, - }; - public BlockStatement CreateMethodBody() { if (methodDef.Body == null) return null; @@ -76,45 +59,7 @@ namespace Decompiler bodyGraph.Optimize(context, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); - List intNames = new List(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"}); - Dictionary typeNames = new Dictionary(); - foreach(ILVariable varDef in astBuilder.Variables) { - if (varDef.Type.FullName == "System.Int32" && intNames.Count > 0) { - varDef.Name = intNames[0]; - intNames.RemoveAt(0); - } else { - string name; - if (varDef.Type.IsArray) { - name = "array"; - } else if (!typeNameToVariableNameDict.TryGetValue(varDef.Type.FullName, out name)) { - name = varDef.Type.Name; - // remove the 'I' for interfaces - if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2])) - name = name.Substring(1); - // remove the backtick (generics) - int pos = name.IndexOf('`'); - if (pos >= 0) - name = name.Substring(0, pos); - if (name.Length == 0) - name = "obj"; - else - name = char.ToLower(name[0]) + name.Substring(1); - } - if (!typeNames.ContainsKey(name)) { - typeNames.Add(name, 0); - } - int count = ++(typeNames[name]); - if (count > 1) { - name += count.ToString(); - } - varDef.Name = name; - } - -// Ast.VariableDeclaration astVar = new Ast.VariableDeclaration(varDef.Name); -// Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(astVar); -// astLocalVar.TypeReference = new Ast.TypeReference(varDef.VariableType.FullName); -// astBlock.Children.Add(astLocalVar); - } + NameVariables.AssignNamesToVariables(methodDef.Parameters.Select(p => p.Name), astBuilder.Variables, ilMethod); context.CancellationToken.ThrowIfCancellationRequested(); Ast.BlockStatement astBlock = TransformBlock(ilMethod); diff --git a/ICSharpCode.Decompiler/Ast/NameVariables.cs b/ICSharpCode.Decompiler/Ast/NameVariables.cs new file mode 100644 index 000000000..8797b43f1 --- /dev/null +++ b/ICSharpCode.Decompiler/Ast/NameVariables.cs @@ -0,0 +1,158 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under MIT X11 license (for details please see \doc\license.txt) + +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; + +namespace Decompiler +{ + public class NameVariables + { + static readonly Dictionary typeNameToVariableNameDict = new Dictionary { + { "System.Boolean", "flag" }, + { "System.Byte", "b" }, + { "System.SByte", "b" }, + { "System.Int16", "num" }, + { "System.Int32", "num" }, + { "System.Int64", "num" }, + { "System.UInt16", "num" }, + { "System.UInt32", "num" }, + { "System.UInt64", "num" }, + { "System.Single", "num" }, + { "System.Double", "num" }, + { "System.Decimal", "num" }, + { "System.String", "text" }, + { "System.Object", "obj" }, + }; + + + public static void AssignNamesToVariables(IEnumerable existingNames, IEnumerable variables, ILBlock methodBody) + { + NameVariables nv = new NameVariables(); + nv.AddExistingNames(existingNames); + foreach (ILVariable varDef in variables) { + nv.AssignNameToVariable(varDef, methodBody.GetSelfAndChildrenRecursive()); + } + } + + Dictionary typeNames = new Dictionary(); + + void AddExistingNames(IEnumerable existingNames) + { + foreach (string name in existingNames) { + if (string.IsNullOrEmpty(name)) + continue; + // First, identify whether the name already ends with a number: + int pos = name.Length; + while (pos > 0 && name[pos-1] >= '0' && name[pos-1] <= '9') + pos--; + if (pos < name.Length) { + int number; + if (int.TryParse(name.Substring(pos), out number)) { + string nameWithoutDigits = name.Substring(0, pos); + int existingNumber; + if (typeNames.TryGetValue(nameWithoutDigits, out existingNumber)) { + typeNames[nameWithoutDigits] = Math.Max(number, existingNumber); + } else { + typeNames.Add(nameWithoutDigits, number); + } + continue; + } + } + if (!typeNames.ContainsKey(name)) + typeNames.Add(name, 1); + } + } + + void AssignNameToVariable(ILVariable varDef, IEnumerable allExpressions) + { + string proposedName = null; + foreach (ILExpression expr in allExpressions) { + if (expr.Operand != varDef) + continue; + if (expr.Code == ILCode.Stloc) { + proposedName = GetNameFromExpression(expr.Arguments.Single()); + } + if (proposedName != null) + break; + } + if (proposedName == null) + proposedName = GetNameByType(varDef.Type); + + if (!typeNames.ContainsKey(proposedName)) { + typeNames.Add(proposedName, 0); + } + int count = ++typeNames[proposedName]; + if (count > 1) { + varDef.Name = proposedName + count.ToString(); + } else { + varDef.Name = proposedName; + } + } + + static string GetNameFromExpression(ILExpression expr) + { + 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)) + return ((FieldReference)expr.Operand).Name; + break; + case ILCode.Ldsfld: + return ((FieldReference)expr.Operand).Name; + case ILCode.Call: + case ILCode.Callvirt: + MethodReference mr = (MethodReference)expr.Operand; + if (mr.Name.StartsWith("get_", StringComparison.Ordinal)) + return CleanUpVariableName(mr.Name.Substring(4)); + else if (mr.Name.StartsWith("Get", StringComparison.Ordinal) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) + return CleanUpVariableName(mr.Name.Substring(3)); + break; + } + return null; + } + + string GetNameByType(TypeReference type) + { + GenericInstanceType git = type as GenericInstanceType; + if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) { + type = ((GenericInstanceType)type).GenericArguments[0]; + } + + if (type.FullName == "System.Int32") { + // try i,j,k, etc. + for (char c = 'i'; c <= 'n'; c++) { + if (!typeNames.ContainsKey(c.ToString())) + return c.ToString(); + } + } + string name; + if (type.IsArray) { + name = "array"; + } else if (type.IsPointer) { + name = "ptr"; + } else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) { + name = type.Name; + // remove the 'I' for interfaces + if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2])) + name = name.Substring(1); + name = CleanUpVariableName(name); + } + return name; + } + + static string CleanUpVariableName(string name) + { + // remove the backtick (generics) + int pos = name.IndexOf('`'); + if (pos >= 0) + name = name.Substring(0, pos); + if (name.Length == 0) + return "obj"; + else + return char.ToLower(name[0]) + name.Substring(1); + } + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 590030c50..13a0825f0 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -53,6 +53,7 @@ +