diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 85ac9da1e..0e431e3f8 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -136,6 +136,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Create AnonymousMethodExpression and prepare parameters AnonymousMethodExpression ame = new AnonymousMethodExpression(); + ame.CopyAnnotationsFrom(objectCreateExpression); // copy ILRanges etc. + ame.RemoveAnnotations(); // remove reference to delegate ctor + ame.AddAnnotation(method); // add reference to anonymous method ame.Parameters.AddRange(AstBuilder.MakeParameters(method, isLambda: true)); ame.HasParameterList = true; @@ -180,6 +183,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } if (isLambda) { LambdaExpression lambda = new LambdaExpression(); + lambda.CopyAnnotationsFrom(ame); ame.Parameters.MoveTo(lambda.Parameters); Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression; returnExpr.Remove(); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index ef6ab4e54..c4447f4d4 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -575,16 +575,37 @@ namespace ICSharpCode.Decompiler.ILAst // This ensures that a single IL variable is a single C# variable (gets assigned only one name) // The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes. Dictionary dict = new Dictionary(); - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) { - ILVariable v = expr.Operand as ILVariable; - if (v != null && v.OriginalVariable != null) { + ReplaceVariables( + method, + delegate(ILVariable v) { + if (v.OriginalVariable == null) + return v; ILVariable combinedVariable; if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) { dict.Add(v.OriginalVariable, v); combinedVariable = v; } - expr.Operand = combinedVariable; + return combinedVariable; + }); + } + + public static void ReplaceVariables(ILNode node, Func variableMapping) + { + ILExpression expr = node as ILExpression; + if (expr != null) { + ILVariable v = expr.Operand as ILVariable; + if (v != null) + expr.Operand = variableMapping(v); + foreach (ILExpression child in expr.Arguments) + ReplaceVariables(child, variableMapping); + } else { + var catchBlock = node as ILTryCatchBlock.CatchBlock; + if (catchBlock != null && catchBlock.ExceptionVariable != null) { + catchBlock.ExceptionVariable = variableMapping(catchBlock.ExceptionVariable); } + + foreach (ILNode child in node.GetChildren()) + ReplaceVariables(child, variableMapping); } } diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index f9e596514..88900a73b 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -523,8 +523,8 @@ namespace ICSharpCode.Decompiler.ILAst if (recombineVariable) { // Split local variable, unsplit these two instances - foreach (var ilExpression in method.GetSelfAndChildrenRecursive(expression => expression.Operand == nextExpr.Operand)) - ilExpression.Operand = exprInit.Operand; + // replace nextExpr.Operand with exprInit.Operand + ReplaceVariables(method, oldVar => oldVar == nextExpr.Operand ? (ILVariable)exprInit.Operand : oldVar); } switch (loadInstruction) { diff --git a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs index 45b5d72ca..0db35e357 100644 --- a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs +++ b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs @@ -105,4 +105,24 @@ public class ExceptionHandling cancellationTokenSource = new CancellationTokenSource(); } } + + public void TwoCatchBlocksWithSameVariable() + { + try + { + Console.WriteLine("Try1"); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + try + { + Console.WriteLine("Try2"); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } } diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 8c43c0a4b..e8264a3e7 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -161,6 +161,7 @@ + @@ -171,7 +172,12 @@ - + + + + + + @@ -230,9 +236,9 @@ - + - + @@ -324,6 +330,11 @@ ICSharpCode.TreeView + + + + + diff --git a/ILSpy/Images/Images.cs b/ILSpy/Images/Images.cs index 8755e772e..8e8f2b08d 100644 --- a/ILSpy/Images/Images.cs +++ b/ILSpy/Images/Images.cs @@ -59,6 +59,9 @@ namespace ICSharpCode.ILSpy public static readonly BitmapImage Resource = LoadBitmap("Resource"); public static readonly BitmapImage ResourceImage = LoadBitmap("ResourceImage"); public static readonly BitmapImage ResourceResourcesFile = LoadBitmap("ResourceResourcesFile"); + public static readonly BitmapImage ResourceXml = LoadBitmap("ResourceXml"); + public static readonly BitmapImage ResourceXsd = LoadBitmap("ResourceXsd"); + public static readonly BitmapImage ResourceXslt = LoadBitmap("ResourceXslt"); public static readonly BitmapImage Class = LoadBitmap("Class"); public static readonly BitmapImage Struct = LoadBitmap("Struct"); diff --git a/ILSpy/Images/ResourceXml.png b/ILSpy/Images/ResourceXml.png new file mode 100644 index 000000000..157feead5 Binary files /dev/null and b/ILSpy/Images/ResourceXml.png differ diff --git a/ILSpy/Images/ResourceXsd.png b/ILSpy/Images/ResourceXsd.png new file mode 100644 index 000000000..4422be59e Binary files /dev/null and b/ILSpy/Images/ResourceXsd.png differ diff --git a/ILSpy/Images/ResourceXslt.png b/ILSpy/Images/ResourceXslt.png new file mode 100644 index 000000000..107c9f2a6 Binary files /dev/null and b/ILSpy/Images/ResourceXslt.png differ diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs new file mode 100644 index 000000000..02320cc09 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs @@ -0,0 +1,153 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using ICSharpCode.TreeView; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + internal sealed class AnalyzedEventFiredByTreeNode : AnalyzerTreeNode + { + private readonly EventDefinition analyzedEvent; + private readonly FieldDefinition eventBackingField; + private readonly MethodDefinition eventFiringMethod; + + private readonly ThreadingSupport threading; + private ConcurrentDictionary foundMethods; + + public AnalyzedEventFiredByTreeNode(EventDefinition analyzedEvent) + { + if (analyzedEvent == null) + throw new ArgumentNullException("analyzedEvent"); + + this.analyzedEvent = analyzedEvent; + this.threading = new ThreadingSupport(); + this.LazyLoading = true; + + this.eventBackingField = GetBackingField(analyzedEvent); + this.eventFiringMethod = analyzedEvent.EventType.Resolve().Methods.First(md => md.Name == "Invoke"); + } + + public override object Text + { + get { return "Raised By"; } + } + + public override object Icon + { + get { return Images.Search; } + } + + protected override void LoadChildren() + { + threading.LoadChildren(this, FetchChildren); + } + + protected override void OnCollapsing() + { + if (threading.IsRunning) { + this.LazyLoading = true; + threading.Cancel(); + this.Children.Clear(); + } + } + + private IEnumerable FetchChildren(CancellationToken ct) + { + foundMethods = new ConcurrentDictionary(); + + foreach (var child in FindReferencesInType(analyzedEvent.DeclaringType)) { + yield return child; + } + + foundMethods = null; + } + + private IEnumerable FindReferencesInType(TypeDefinition type) + { + // HACK: in lieu of proper flow analysis, I'm going to use a simple heuristic + // If the method accesses the event's backing field, and calls invoke on a delegate + // with the same signature, then it is (most likely) raise the given event. + + foreach (MethodDefinition method in type.Methods) { + bool readBackingField = false; + bool found = false; + if (!method.HasBody) + continue; + foreach (Instruction instr in method.Body.Instructions) { + Code code = instr.OpCode.Code; + if (code == Code.Ldfld || code == Code.Ldflda) { + FieldReference fr = instr.Operand as FieldReference; + if (fr != null && fr.Name == eventBackingField.Name && fr == eventBackingField) { + readBackingField = true; + } + } + if (readBackingField && (code == Code.Callvirt || code == Code.Call)) { + MethodReference mr = instr.Operand as MethodReference; + if (mr != null && mr.Name == eventFiringMethod.Name && mr.Resolve() == eventFiringMethod) { + found = true; + break; + } + } + } + + method.Body = null; + + if (found) { + MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; + if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { + yield return new AnalyzedMethodTreeNode(codeLocation); + } + } + } + } + + private bool HasAlreadyBeenFound(MethodDefinition method) + { + return !foundMethods.TryAdd(method, 0); + } + + // HACK: we should probably examine add/remove methods to determine this + private static FieldDefinition GetBackingField(EventDefinition ev) + { + var fieldName = ev.Name; + var vbStyleFieldName = fieldName + "Event"; + var fieldType = ev.EventType; + + foreach (var fd in ev.DeclaringType.Fields) { + if (fd.Name == fieldName || fd.Name == vbStyleFieldName) + if (fd.FieldType.FullName == fieldType.FullName) + return fd; + } + + return null; + } + + + public static bool CanShow(EventDefinition ev) + { + return GetBackingField(ev) != null; + } + } +} diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs index 89903abd4..84a937942 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs @@ -59,13 +59,19 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { if (analyzedEvent.AddMethod != null) this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.AddMethod, "add")); + if (analyzedEvent.RemoveMethod != null) this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.RemoveMethod, "remove")); + foreach (var accessor in analyzedEvent.OtherMethods) this.Children.Add(new AnalyzedEventAccessorTreeNode(accessor, null)); + if (AnalyzedEventFiredByTreeNode.CanShow(analyzedEvent)) + this.Children.Add(new AnalyzedEventFiredByTreeNode(analyzedEvent)); + if (AnalyzedEventOverridesTreeNode.CanShow(analyzedEvent)) this.Children.Add(new AnalyzedEventOverridesTreeNode(analyzedEvent)); + if (AnalyzedInterfaceEventImplementedByTreeNode.CanShow(analyzedEvent)) this.Children.Add(new AnalyzedInterfaceEventImplementedByTreeNode(analyzedEvent)); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs index 993ef36d7..26f5e4c65 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs @@ -89,7 +89,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer var BaseMethods = TypesHierarchyHelpers.FindBaseMethods(analyzedMethod).ToArray(); if (BaseMethods.Length > 0) { baseMethod = BaseMethods[BaseMethods.Length - 1]; - } + } else + baseMethod = analyzedMethod; possibleTypes = new List(); @@ -119,15 +120,24 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer MethodReference mr = instr.Operand as MethodReference; if (mr != null && mr.Name == name) { // explicit call to the requested method - if (Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) { + if (instr.OpCode.Code == Code.Call + && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) + && mr.Resolve() == analyzedMethod) { found = true; prefix = "(as base) "; break; } // virtual call to base method - if (instr.OpCode.Code == Code.Callvirt && Helpers.IsReferencedBy(baseMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == baseMethod) { - found = true; - break; + if (instr.OpCode.Code == Code.Callvirt) { + MethodDefinition md = mr.Resolve(); + if (md == null) { + // cannot resolve the operand, so ignore this method + break; + } + if (md == baseMethod) { + found = true; + break; + } } } } diff --git a/ILSpy/TreeNodes/Analyzer/Helpers.cs b/ILSpy/TreeNodes/Analyzer/Helpers.cs index bef4e486a..b773ad875 100644 --- a/ILSpy/TreeNodes/Analyzer/Helpers.cs +++ b/ILSpy/TreeNodes/Analyzer/Helpers.cs @@ -67,11 +67,11 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return FindMethodUsageInType(method.DeclaringType, method) ?? method; } - var typeUsage = GetOriginalCodeLocation(method.DeclaringType, method); + var typeUsage = GetOriginalCodeLocation(method.DeclaringType); return typeUsage ?? method; } - public static MethodDefinition GetOriginalCodeLocation(TypeDefinition type, MethodDefinition method) + public static MethodDefinition GetOriginalCodeLocation(TypeDefinition type) { if (type != null && type.DeclaringType != null && type.IsCompilerGenerated()) { MethodDefinition constructor = GetTypeConstructor(type); diff --git a/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs new file mode 100644 index 000000000..3170f0992 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/CursorResourceEntryNode.cs @@ -0,0 +1,95 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using ICSharpCode.ILSpy.TextView; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + [Export(typeof(IResourceNodeFactory))] + sealed class CursorResourceNodeFactory : IResourceNodeFactory + { + static readonly string[] imageFileExtensions = { ".cur" }; + + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null) { + return CreateNode(er.Name, er.GetResourceStream()); + } + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + foreach (string fileExt in imageFileExtensions) { + if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) + return new CursorResourceEntryNode(key, data); + } + return null; + } + } + + sealed class CursorResourceEntryNode : ResourceEntryNode + { + public CursorResourceEntryNode(string key, Stream data) + : base(key, data) + { + } + + public override object Icon + { + get { return Images.ResourceImage; } + } + + public override bool View(DecompilerTextView textView) + { + try { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + Data.Position = 0; + BitmapImage image = new BitmapImage(); + + //HACK: windows imaging does not understand that .cur files have the same layout as .ico + // so load to data, and modify the ResourceType in the header to make look like an icon... + byte[] curData = ((MemoryStream)Data).ToArray(); + curData[2] = 1; + using (Stream stream = new MemoryStream(curData)) { + image.BeginInit(); + image.StreamSource = stream; + image.EndInit(); + } + + output.AddUIElement(() => new Image { Source = image }); + output.WriteLine(); + output.AddButton(Images.Save, "Save", delegate { + Save(null); + }); + textView.ShowNode(output, this, null); + return true; + } + catch (Exception) { + return false; + } + } + } +} diff --git a/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs b/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs new file mode 100644 index 000000000..49d5f4893 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/IResourceNodeFactory.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.IO; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + /// + /// This interface allows plugins to create custom nodes for resources. + /// + public interface IResourceNodeFactory + { + ILSpyTreeNode CreateNode(Resource resource); + ILSpyTreeNode CreateNode(string key, Stream data); + } +} diff --git a/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs new file mode 100644 index 000000000..94e894961 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/ImageResourceEntryNode.cs @@ -0,0 +1,87 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using ICSharpCode.ILSpy.TextView; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + [Export(typeof(IResourceNodeFactory))] + sealed class ImageResourceNodeFactory : IResourceNodeFactory + { + static readonly string[] imageFileExtensions = { ".png", ".gif", ".bmp", ".jpg", ".ico" }; + + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null) { + return CreateNode(er.Name, er.GetResourceStream()); + } + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + foreach (string fileExt in imageFileExtensions) { + if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) + return new ImageResourceEntryNode(key, data); + } + return null; + } + } + + sealed class ImageResourceEntryNode : ResourceEntryNode + { + public ImageResourceEntryNode(string key, Stream data) + : base(key, data) + { + } + + public override object Icon + { + get { return Images.ResourceImage; } + } + + public override bool View(DecompilerTextView textView) + { + try { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + Data.Position = 0; + BitmapImage image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = Data; + image.EndInit(); + output.AddUIElement(() => new Image { Source = image }); + output.WriteLine(); + output.AddButton(Images.Save, "Save", delegate { + Save(null); + }); + textView.ShowNode(output, this, null); + return true; + } + catch (Exception) { + return false; + } + } + } +} diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs similarity index 64% rename from ILSpy/TreeNodes/ResourceEntryNode.cs rename to ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs index cddab8326..fa2cff797 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs @@ -17,14 +17,10 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; using System.ComponentModel.Composition; +using System.IO; using System.Windows.Controls; using System.Windows.Media.Imaging; -using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.TextView; using Microsoft.Win32; @@ -95,61 +91,4 @@ namespace ICSharpCode.ILSpy.TreeNodes return true; } } - - [Export(typeof(IResourceNodeFactory))] - sealed class ImageResourceNodeFactory : IResourceNodeFactory - { - static readonly string[] imageFileExtensions = { ".png", ".gif", ".bmp", ".jpg", ".ico" }; - - public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) - { - EmbeddedResource er = resource as EmbeddedResource; - if (er != null) { - return CreateNode(er.Name, er.GetResourceStream()); - } - return null; - } - - public ILSpyTreeNode CreateNode(string key, Stream data) - { - foreach (string fileExt in imageFileExtensions) { - if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) - return new ImageResourceEntryNode(key, data); - } - return null; - } - } - - sealed class ImageResourceEntryNode : ResourceEntryNode - { - public ImageResourceEntryNode(string key, Stream data) - : base(key, data) - { - } - - public override object Icon - { - get { return Images.ResourceImage; } - } - - public override bool View(DecompilerTextView textView) - { - try { - AvalonEditTextOutput output = new AvalonEditTextOutput(); - Data.Position = 0; - BitmapImage image = new BitmapImage(); - image.BeginInit(); - image.StreamSource = Data; - image.EndInit(); - output.AddUIElement(() => new Image { Source = image }); - output.WriteLine(); - output.AddButton(Images.Save, "Save", delegate { Save(null); }); - textView.ShowNode(output, this, null); - return true; - } - catch (Exception) { - return false; - } - } - } } diff --git a/ILSpy/TreeNodes/ResourceTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs similarity index 74% rename from ILSpy/TreeNodes/ResourceTreeNode.cs rename to ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs index 1c1a1f08a..5fa470d5a 100644 --- a/ILSpy/TreeNodes/ResourceTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs @@ -34,6 +34,10 @@ using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes { + /// + /// This is the default resource entry tree node, which is used if no specific + /// exists for the given resource type. + /// public class ResourceTreeNode : ILSpyTreeNode { Resource r; @@ -132,63 +136,4 @@ namespace ICSharpCode.ILSpy.TreeNodes return result ?? new ResourceTreeNode(resource); } } - - /// - /// This interface allows plugins to create custom nodes for resources. - /// - public interface IResourceNodeFactory - { - ILSpyTreeNode CreateNode(Resource resource); - ILSpyTreeNode CreateNode(string key, Stream data); - } - - [Export(typeof(IResourceNodeFactory))] - sealed class ResourcesFileTreeNodeFactory : IResourceNodeFactory - { - public ILSpyTreeNode CreateNode(Resource resource) - { - EmbeddedResource er = resource as EmbeddedResource; - if (er != null && er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { - return new ResourcesFileTreeNode(er); - } - return null; - } - - public ILSpyTreeNode CreateNode(string key, Stream data) - { - return null; - } - } - - sealed class ResourcesFileTreeNode : ResourceTreeNode - { - public ResourcesFileTreeNode(EmbeddedResource er) : base(er) - { - this.LazyLoading = true; - } - - public override object Icon - { - get { return Images.ResourceResourcesFile; } - } - - protected override void LoadChildren() - { - EmbeddedResource er = this.Resource as EmbeddedResource; - if (er != null) { - Stream s = er.GetResourceStream(); - s.Position = 0; - ResourceReader reader; - try { - reader = new ResourceReader(s); - } catch (ArgumentException) { - return; - } - foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { - if (entry.Value is Stream) - Children.Add(ResourceEntryNode.Create(entry.Key.ToString(), (Stream)entry.Value)); - } - } - } - } } diff --git a/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs new file mode 100644 index 000000000..a99e2a989 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs @@ -0,0 +1,82 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections; +using System.ComponentModel.Composition; +using System.IO; +using System.Linq; +using System.Resources; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes +{ + [Export(typeof(IResourceNodeFactory))] + sealed class ResourcesFileTreeNodeFactory : IResourceNodeFactory + { + public ILSpyTreeNode CreateNode(Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null && er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { + return new ResourcesFileTreeNode(er); + } + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + return null; + } + } + + sealed class ResourcesFileTreeNode : ResourceTreeNode + { + public ResourcesFileTreeNode(EmbeddedResource er) + : base(er) + { + this.LazyLoading = true; + } + + public override object Icon + { + get { return Images.ResourceResourcesFile; } + } + + protected override void LoadChildren() + { + EmbeddedResource er = this.Resource as EmbeddedResource; + if (er != null) { + Stream s = er.GetResourceStream(); + s.Position = 0; + ResourceReader reader; + try { + reader = new ResourceReader(s); + } + catch (ArgumentException) { + return; + } + foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { + if (entry.Value is Stream) + Children.Add(ResourceEntryNode.Create(entry.Key.ToString(), (Stream)entry.Value)); + else if (entry.Value is byte[]) + Children.Add(ResourceEntryNode.Create(entry.Key.ToString(), new MemoryStream((byte[])entry.Value))); + } + } + } + } +} diff --git a/ILSpy/TreeNodes/XamlResourceNode.cs b/ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs similarity index 100% rename from ILSpy/TreeNodes/XamlResourceNode.cs rename to ILSpy/TreeNodes/ResourceNodes/XamlResourceNode.cs diff --git a/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs b/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs new file mode 100644 index 000000000..ce2f1e1d4 --- /dev/null +++ b/ILSpy/TreeNodes/ResourceNodes/XmlResourceNode.cs @@ -0,0 +1,106 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Threading.Tasks; + +using ICSharpCode.AvalonEdit.Highlighting; +using ICSharpCode.ILSpy.TextView; +using ICSharpCode.ILSpy.TreeNodes; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.Xaml +{ + [Export(typeof(IResourceNodeFactory))] + sealed class XmlResourceNodeFactory : IResourceNodeFactory + { + private readonly static string[] xmlFileExtensions = { ".xml", ".xsd", ".xslt" }; + + public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource) + { + EmbeddedResource er = resource as EmbeddedResource; + if (er != null) + return CreateNode(er.Name, er.GetResourceStream()); + return null; + } + + public ILSpyTreeNode CreateNode(string key, Stream data) + { + foreach (string fileExt in xmlFileExtensions) + if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase)) + return new XmlResourceEntryNode(key, data); + return null; + } + } + + sealed class XmlResourceEntryNode : ResourceEntryNode + { + string xml; + + public XmlResourceEntryNode(string key, Stream data) + : base(key, data) + { + } + + public override object Icon + { + get + { + string text = (string)Text; + if (text.EndsWith(".xml", StringComparison.OrdinalIgnoreCase)) + return Images.ResourceXml; + else if (text.EndsWith(".xsd", StringComparison.OrdinalIgnoreCase)) + return Images.ResourceXsd; + else if (text.EndsWith(".xslt", StringComparison.OrdinalIgnoreCase)) + return Images.ResourceXslt; + else + return Images.Resource; + } + } + + public override bool View(DecompilerTextView textView) + { + AvalonEditTextOutput output = new AvalonEditTextOutput(); + IHighlightingDefinition highlighting = null; + + textView.RunWithCancellation( + token => Task.Factory.StartNew( + () => { + try { + // cache read XAML because stream will be closed after first read + if (xml == null) { + using (var reader = new StreamReader(Data)) { + xml = reader.ReadToEnd(); + } + } + output.Write(xml); + highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml"); + } + catch (Exception ex) { + output.Write(ex.ToString()); + } + return output; + }), + t => textView.ShowNode(t.Result, this, highlighting) + ); + return true; + } + } +}