diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 5116658a5..d3480ca42 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -337,6 +337,8 @@ namespace Decompiler Modifiers ConvertModifiers(MethodDefinition methodDef) { + if (methodDef == null) + return Modifiers.None; Modifiers modifiers = Modifiers.None; if (methodDef.IsPrivate) modifiers |= Modifiers.Private; @@ -431,7 +433,7 @@ namespace Decompiler PropertyDeclaration CreateProperty(PropertyDefinition propDef) { PropertyDeclaration astProp = new PropertyDeclaration(); - astProp.Modifiers = ConvertModifiers(propDef.GetMethod); + astProp.Modifiers = ConvertModifiers(propDef.GetMethod ?? propDef.SetMethod); astProp.Name = propDef.Name; astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); if (propDef.GetMethod != null) { @@ -469,9 +471,16 @@ namespace Decompiler FieldDeclaration CreateField(FieldDefinition fieldDef) { FieldDeclaration astField = new FieldDeclaration(); - astField.AddChild(new VariableInitializer(fieldDef.Name), FieldDeclaration.Roles.Variable); + VariableInitializer initializer = new VariableInitializer(fieldDef.Name); + astField.AddChild(initializer, FieldDeclaration.Roles.Variable); astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef); astField.Modifiers = ConvertModifiers(fieldDef); + if (fieldDef.HasConstant) { + if (fieldDef.Constant == null) + initializer.Initializer = new NullReferenceExpression(); + else + initializer.Initializer = new PrimitiveExpression(fieldDef.Constant); + } return astField; } diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 1f131d3b5..b80aa09ac 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -21,7 +21,17 @@ namespace Decompiler { AstMethodBodyBuilder builder = new AstMethodBodyBuilder(); builder.methodDef = methodDef; - return builder.CreateMethodBody(); + if (Debugger.IsAttached) { + return builder.CreateMethodBody(); + } else { + try { + return builder.CreateMethodBody(); + } catch (OperationCanceledException) { + throw; + } catch (Exception ex) { + throw new ICSharpCode.Decompiler.DecompilerException(methodDef, ex); + } + } } static readonly Dictionary typeNameToVariableNameDict = new Dictionary { diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index c853ebdd1..f489d57eb 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -14,6 +14,7 @@ namespace Decompiler { readonly ITextOutput output; readonly Stack nodeStack = new Stack(); + int braceLevelWithinType = -1; public TextOutputFormatter(ITextOutput output) { @@ -64,8 +65,11 @@ namespace Decompiler public void OpenBrace(BraceStyle style) { - if (nodeStack.OfType().Count() <= 1) - output.MarkFoldStart(); + if (braceLevelWithinType >= 0 || nodeStack.Peek() is TypeDeclaration) + braceLevelWithinType++; + if (nodeStack.OfType().Count() <= 1) { + output.MarkFoldStart(defaultCollapsed: braceLevelWithinType == 1); + } output.WriteLine(); output.WriteLine("{"); output.Indent(); @@ -77,6 +81,8 @@ namespace Decompiler output.Write('}'); if (nodeStack.OfType().Count() <= 1) output.MarkFoldEnd(); + if (braceLevelWithinType >= 0) + braceLevelWithinType--; } public void Indent() diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 4f3786120..c2a4f7245 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -74,10 +74,12 @@ 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 = methodIdent.TypeArguments + TypeArguments = typeArguments }; mre.AddAnnotation(method); objectCreateExpression.Arguments = new [] { mre }; diff --git a/ICSharpCode.Decompiler/DecompilerException.cs b/ICSharpCode.Decompiler/DecompilerException.cs new file mode 100644 index 000000000..c7316f775 --- /dev/null +++ b/ICSharpCode.Decompiler/DecompilerException.cs @@ -0,0 +1,27 @@ +// 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.Runtime.Serialization; +using Mono.Cecil; + +namespace ICSharpCode.Decompiler +{ + /// + /// Desctiption of DecompilerException. + /// + public class DecompilerException : Exception, ISerializable + { + public MethodDefinition DecompiledMethod { get; set; } + + public DecompilerException(MethodDefinition decompiledMethod, Exception innerException) + : base("Error decompiling " + decompiledMethod.FullName + Environment.NewLine, innerException) + { + } + + // This constructor is needed for serialization. + protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index 77a18ab6b..0c45d730f 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -64,6 +64,7 @@ + diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 9b6ffed6f..bb0c9cf03 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -106,7 +106,7 @@ namespace Decompiler List body = StackAnalysis(methodDef); - List ast = ConvertToAst(body, methodDef.Body.ExceptionHandlers); + List ast = ConvertToAst(body, new HashSet(methodDef.Body.ExceptionHandlers)); return ast; } @@ -292,7 +292,7 @@ namespace Decompiler return body; } - List ConvertToAst(List body, IEnumerable ehs) + List ConvertToAst(List body, HashSet ehs) { List ast = new List(); @@ -313,7 +313,8 @@ namespace Decompiler // Cut the try block { - List nestedEHs = ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)).ToList(); + HashSet nestedEHs = new HashSet(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd))); + ehs.ExceptWith(nestedEHs); int tryEndIdx; for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++); tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs)); @@ -328,7 +329,8 @@ namespace Decompiler // Note that the end(exclusiove) instruction may not necessarly be in our body for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++); int count = 1 + endInclusiveIndex - startIndex; - List nestedEHs = ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset)).ToList(); + HashSet nestedEHs = new HashSet(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset))); + ehs.ExceptWith(nestedEHs); List handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs); if (eh.HandlerType == ExceptionHandlerType.Catch) { tryCatchBlock.CatchBlocks.Add(new ILTryCatchBlock.CatchBlock() { @@ -342,7 +344,7 @@ namespace Decompiler } } - ehs = ehs.Where(eh => eh.TryStart.Offset > tryEnd).ToList(); + ehs.ExceptWith(handlers); ast.Add(tryCatchBlock); } diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 3368f1594..e8800f8b0 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -101,6 +101,20 @@ namespace ICSharpCode.ILSpy codeDomBuilder.GenerateCode(output, transformAbortCondition); } + public override void DecompileAssembly(AssemblyDefinition assembly, string fileName, ITextOutput output, DecompilationOptions options) + { + if (options.FullDecompilation) { + foreach (TypeDefinition type in assembly.MainModule.Types) { + AstBuilder codeDomBuilder = new AstBuilder(); + codeDomBuilder.AddType(type); + codeDomBuilder.GenerateCode(output, transformAbortCondition); + output.WriteLine(); + } + } else { + base.DecompileAssembly(assembly, fileName, output, options); + } + } + public override string TypeToString(TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes) { AstType astType = AstBuilder.ConvertType(type, typeAttributes); diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 1e0e974a7..4dc675245 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -482,7 +482,7 @@ namespace ICSharpCode.ILSpy } decompilerTextView.SaveToDisk(sessionSettings.FilterSettings.Language, treeView.GetTopLevelSelection().OfType(), - new DecompilationOptions()); + new DecompilationOptions() { FullDecompilation = true }); } #endregion diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 28bf7ddd0..2966cebc7 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -240,7 +240,7 @@ namespace ICSharpCode.ILSpy.TextView RunWithCancellation( delegate (CancellationToken ct) { // creation of the background task context.Options.CancellationToken = ct; - return RunDecompiler(context, outputLengthLimit); + return DecompileAsync(context, outputLengthLimit); }, delegate (Task task) { // handling the result try { @@ -269,7 +269,7 @@ namespace ICSharpCode.ILSpy.TextView }); } - static Task RunDecompiler(DecompilationContext context, int outputLengthLimit) + static Task DecompileAsync(DecompilationContext context, int outputLengthLimit) { Debug.WriteLine("Start decompilation of {0} tree nodes", context.TreeNodes.Length); @@ -293,10 +293,11 @@ namespace ICSharpCode.ILSpy.TextView } catch (AggregateException ex) { tcs.SetException(ex); } catch (OperationCanceledException ex) { + tcs.SetException(ex); #else } catch (Exception ex) { - #endif tcs.SetException(ex); + #endif } })); thread.Start(); @@ -411,29 +412,7 @@ namespace ICSharpCode.ILSpy.TextView RunWithCancellation( delegate (CancellationToken ct) { context.Options.CancellationToken = ct; - return Task.Factory.StartNew( - delegate { - using (StreamWriter w = new StreamWriter(fileName)) { - try { - DecompileNodes(context, new PlainTextOutput(w)); - } catch (OperationCanceledException) { - w.WriteLine(); - w.WriteLine("Decompiled was cancelled."); - throw; - } - } - AvalonEditTextOutput output = new AvalonEditTextOutput(); - output.WriteLine("Decompilation complete."); - output.WriteLine(); - output.AddButton( - null, "Open Explorer", - delegate { - Process.Start("explorer", "/select,\"" + fileName + "\""); - } - ); - output.WriteLine(); - return output; - }, TaskCreationOptions.LongRunning); + return SaveToDiskAsync(context, fileName); }, delegate (Task task) { try { @@ -452,6 +431,42 @@ namespace ICSharpCode.ILSpy.TextView } }); } + + Task SaveToDiskAsync(DecompilationContext context, string fileName) + { + TaskCompletionSource tcs = new TaskCompletionSource(); + Thread thread = new Thread(new ThreadStart( + delegate { + try { + using (StreamWriter w = new StreamWriter(fileName)) { + try { + DecompileNodes(context, new PlainTextOutput(w)); + } catch (OperationCanceledException) { + w.WriteLine(); + w.WriteLine("Decompiled was cancelled."); + throw; + } + } + AvalonEditTextOutput output = new AvalonEditTextOutput(); + output.WriteLine("Decompilation complete."); + output.WriteLine(); + output.AddButton(null, "Open Explorer", delegate { Process.Start("explorer", "/select,\"" + fileName + "\""); }); + output.WriteLine(); + tcs.SetResult(output); + #if DEBUG + } catch (OperationCanceledException ex) { + tcs.SetException(ex); + } catch (AggregateException ex) { + tcs.SetException(ex); + #else + } catch (Exception ex) { + tcs.SetException(ex); + #endif + } + })); + thread.Start(); + return tcs.Task; + } /// /// Cleans up a node name for use as a file name. diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index eb24ce5a1..457fdbc04 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -97,10 +97,10 @@ namespace ICSharpCode.ILSpy.TreeNodes this.Children.Add(new BaseTypesTreeNode(type)); if (!type.IsSealed) this.Children.Add(new DerivedTypesTreeNode(parentAssemblyNode.AssemblyList, type)); - foreach (TypeDefinition nestedType in type.NestedTypes) { + foreach (TypeDefinition nestedType in type.NestedTypes.OrderBy(m => m.Name)) { this.Children.Add(new TypeTreeNode(nestedType, parentAssemblyNode)); } - foreach (FieldDefinition field in type.Fields) { + foreach (FieldDefinition field in type.Fields.OrderBy(m => m.Name)) { this.Children.Add(new FieldTreeNode(field)); } @@ -112,14 +112,14 @@ namespace ICSharpCode.ILSpy.TreeNodes defaultMemberName = defaultMemberAttribute.ConstructorArguments[0].Value as string; } - foreach (PropertyDefinition property in type.Properties) { + foreach (PropertyDefinition property in type.Properties.OrderBy(m => m.Name)) { this.Children.Add(new PropertyTreeNode(property, property.Name == defaultMemberName)); } - foreach (EventDefinition ev in type.Events) { + foreach (EventDefinition ev in type.Events.OrderBy(m => m.Name)) { this.Children.Add(new EventTreeNode(ev)); } HashSet accessorMethods = type.GetAccessorMethods(); - foreach (MethodDefinition method in type.Methods) { + foreach (MethodDefinition method in type.Methods.OrderBy(m => m.Name)) { if (!accessorMethods.Contains(method)) { this.Children.Add(new MethodTreeNode(method)); } diff --git a/Mono.Cecil/Mono.Cecil/ModuleDefinition.cs b/Mono.Cecil/Mono.Cecil/ModuleDefinition.cs index 7c793f5a2..ea84209d7 100644 --- a/Mono.Cecil/Mono.Cecil/ModuleDefinition.cs +++ b/Mono.Cecil/Mono.Cecil/ModuleDefinition.cs @@ -757,18 +757,22 @@ namespace Mono.Cecil { { return Read (this, (_, reader) => reader.LookupToken (token)); } + + readonly object module_lock = new object(); internal TRet Read (TItem item, Func read) { - var position = reader.position; - var context = reader.context; + lock (module_lock) { + var position = reader.position; + var context = reader.context; - var ret = read (item, reader); + var ret = read (item, reader); - reader.position = position; - reader.context = context; + reader.position = position; + reader.context = context; - return ret; + return ret; + } } void ProcessDebugHeader () diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs index 3b48cdf59..6b2b12959 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpModifierToken.cs @@ -25,6 +25,7 @@ // THE SOFTWARE. using System; using System.Collections.Generic; +using System.Linq; namespace ICSharpCode.NRefactory.CSharp { @@ -36,33 +37,40 @@ namespace ICSharpCode.NRefactory.CSharp public Modifiers Modifier { get { return modifier; } set { - modifier = value; - if (!lengthTable.TryGetValue (modifier, out tokenLength)) - throw new InvalidOperationException ("Modifier " + modifier + " is invalid."); + for (int i = 0; i < lengthTable.Count; i++) { + if (lengthTable[i].Key == value) { + this.modifier = value; + this.tokenLength = lengthTable[i].Value; + return; + } + } + throw new ArgumentException ("Modifier " + value + " is invalid."); } } - static Dictionary lengthTable = new Dictionary () { - { Modifiers.Public, "public".Length }, - { Modifiers.Protected, "protected".Length }, - { Modifiers.Private, "private".Length }, - { Modifiers.Internal, "internal".Length }, - { Modifiers.New, "new".Length }, - { Modifiers.Unsafe, "unsafe".Length }, - { Modifiers.Abstract, "abstract".Length }, - { Modifiers.Virtual, "virtual".Length }, - { Modifiers.Sealed, "sealed".Length }, - { Modifiers.Static, "static".Length }, - { Modifiers.Override, "override".Length }, - { Modifiers.Readonly, "readonly".Length }, - { Modifiers.Volatile, "volatile".Length }, - { Modifiers.Extern, "extern".Length }, - { Modifiers.Partial, "partial".Length }, - { Modifiers.Const, "const".Length }, + // Not worth using a dictionary for such few elements. + // This table is sorted in the order that modifiers should be output when generating code. + static readonly List> lengthTable = new List> () { + new KeyValuePair(Modifiers.Public, "public".Length), + new KeyValuePair(Modifiers.Protected, "protected".Length), + new KeyValuePair(Modifiers.Private, "private".Length), + new KeyValuePair(Modifiers.Internal, "internal".Length), + new KeyValuePair(Modifiers.New, "new".Length), + new KeyValuePair(Modifiers.Unsafe, "unsafe".Length), + new KeyValuePair(Modifiers.Abstract, "abstract".Length), + new KeyValuePair(Modifiers.Virtual, "virtual".Length), + new KeyValuePair(Modifiers.Sealed, "sealed".Length), + new KeyValuePair(Modifiers.Static, "static".Length), + new KeyValuePair(Modifiers.Override, "override".Length), + new KeyValuePair(Modifiers.Readonly, "readonly".Length), + new KeyValuePair(Modifiers.Volatile, "volatile".Length), + new KeyValuePair(Modifiers.Extern, "extern".Length), + new KeyValuePair(Modifiers.Partial, "partial".Length), + new KeyValuePair(Modifiers.Const, "const".Length) }; - public static ICollection AllModifiers { - get { return lengthTable.Keys; } + public static IEnumerable AllModifiers { + get { return lengthTable.Select(p => p.Key); } } public CSharpModifierToken (AstLocation location, Modifiers modifier) : base (location, 0) diff --git a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs index 99d8af8ec..a2734ae4f 100644 --- a/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs +++ b/NRefactory/ICSharpCode.NRefactory/CSharp/Ast/TypeMembers/AttributedNode.cs @@ -42,7 +42,9 @@ namespace ICSharpCode.NRefactory.CSharp if ((m & newValue) != 0) { if ((m & oldValue) == 0) { // Modifier was added - node.InsertChildAfter(insertionPos, new CSharpModifierToken(AstLocation.Empty, m), ModifierRole); + var newToken = new CSharpModifierToken(AstLocation.Empty, m); + node.InsertChildAfter(insertionPos, newToken, ModifierRole); + insertionPos = newToken; } else { // Modifier already exists insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m); diff --git a/SharpTreeView/TreeFlattener.cs b/SharpTreeView/TreeFlattener.cs index eb6895084..332031dfd 100644 --- a/SharpTreeView/TreeFlattener.cs +++ b/SharpTreeView/TreeFlattener.cs @@ -81,7 +81,7 @@ namespace ICSharpCode.TreeView public int IndexOf(object item) { SharpTreeNode node = item as SharpTreeNode; - if (node != null && node.GetListRoot() == root) { + if (node != null && node.IsVisible && node.GetListRoot() == root) { if (includeRoot) return SharpTreeNode.GetVisibleIndexForNode(node); else