From e86aee752dfc594fc675be407aab965b71840c9b Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Fri, 22 Apr 2011 15:27:24 +1000 Subject: [PATCH] Added Event analysis. --- .../Ast/TypesHierarchyHelpers.cs | 52 +++++++++++ ILSpy/ILSpy.csproj | 3 + .../Analyzer/AnalyzeContextMenuEntry.cs | 6 +- .../AnalyzedEventAccessorsTreeNode.cs | 58 ++++++++++++ .../AnalyzedEventOverridesTreeNode.cs | 93 +++++++++++++++++++ .../Analyzer/AnalyzedEventTreeNode.cs | 81 ++++++++++++++++ ILSpy/TreeNodes/EventTreeNode.cs | 27 +++++- 7 files changed, 314 insertions(+), 6 deletions(-) create mode 100644 ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs create mode 100644 ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs create mode 100644 ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs diff --git a/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs b/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs index c9105d4e7..5104ea7ba 100644 --- a/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs +++ b/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs @@ -61,6 +61,14 @@ namespace ICSharpCode.Decompiler.Ast return FindBaseProperties(childProperty).Any(m => m == parentProperty); } + public static bool IsBaseEvent(EventDefinition parentEvent, EventDefinition childEvent) + { + if (parentEvent.Name != childEvent.Name) + return false; + + return FindBaseEvents(childEvent).Any(m => m == parentEvent); + } + public static IEnumerable FindBaseMethods(MethodDefinition method) { if (method == null) @@ -97,6 +105,25 @@ namespace ICSharpCode.Decompiler.Ast } + public static IEnumerable FindBaseEvents(EventDefinition eventDef) + { + if (eventDef == null) + throw new ArgumentNullException("eventDef"); + + var typeContext = CreateGenericContext(eventDef.DeclaringType); + var gEvent = typeContext.ApplyTo(eventDef); + + foreach (var baseType in BaseTypes(eventDef.DeclaringType)) + foreach (var baseEvent in baseType.Item.Events) + if (MatchEvent(baseType.ApplyTo(baseEvent), gEvent) && IsVisbleFrom(baseEvent, eventDef)) { + yield return baseEvent; + var anyEventAccessor = baseEvent.AddMethod ?? baseEvent.RemoveMethod; + if (!(anyEventAccessor.IsNewSlot ^ anyEventAccessor.IsVirtual)) + yield break; + } + + } + private static bool IsVisbleFrom(MethodDefinition baseCandidate, MethodDefinition method) { if (baseCandidate.IsPrivate) @@ -115,6 +142,15 @@ namespace ICSharpCode.Decompiler.Ast return false; } + private static bool IsVisbleFrom(EventDefinition baseCandidate, EventDefinition eventDef) + { + if (baseCandidate.AddMethod != null && eventDef.AddMethod != null && IsVisbleFrom(baseCandidate.AddMethod, eventDef.AddMethod)) + return true; + if (baseCandidate.RemoveMethod != null && eventDef.RemoveMethod != null && IsVisbleFrom(baseCandidate.RemoveMethod, eventDef.RemoveMethod)) + return true; + return false; + } + private static bool MatchMethod(GenericContext candidate, GenericContext method) { var mCandidate = candidate.Item; @@ -172,6 +208,22 @@ namespace ICSharpCode.Decompiler.Ast return true; } + private static bool MatchEvent(GenericContext candidate, GenericContext ev) + { + var mCandidate = candidate.Item; + var mEvent = ev.Item; + if (mCandidate.Name != mEvent.Name) + return false; + + if ((mCandidate.AddMethod ?? mCandidate.RemoveMethod).HasOverrides) + return false; + + if (!IsSameType(candidate.ResolveWithContext(mCandidate.EventType), ev.ResolveWithContext(mEvent.EventType))) + return false; + + return true; + } + private static bool MatchParameters(GenericContext baseParameterType, GenericContext parameterType) { var baseParam = baseParameterType.ResolveWithContext(baseParameterType.Item.ParameterType); diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 115c6a6a7..e04a90c6d 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -131,6 +131,9 @@ + + + diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs b/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs index c7c4e0266..fa2c5bf29 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs @@ -36,7 +36,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foreach (IMemberTreeNode node in selectedNodes) { if (!(node.Member is FieldDefinition || node.Member is MethodDefinition - || Analyzer.AnalyzedPropertyTreeNode.CanShow(node.Member))) + || Analyzer.AnalyzedPropertyTreeNode.CanShow(node.Member) + || Analyzer.AnalyzedEventTreeNode.CanShow(node.Member))) return false; } return true; @@ -56,6 +57,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer var propertyAnalyzer = Analyzer.AnalyzedPropertyTreeNode.TryCreateAnalyzer(node.Member); if(propertyAnalyzer != null) MainWindow.Instance.AddToAnalyzer(propertyAnalyzer); + var eventAnalyzer = Analyzer.AnalyzedEventTreeNode.TryCreateAnalyzer(node.Member); + if (eventAnalyzer != null) + MainWindow.Instance.AddToAnalyzer(eventAnalyzer); } } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs new file mode 100644 index 000000000..8bf0285fb --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs @@ -0,0 +1,58 @@ +using System; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + /// + /// Description of AnalyzedEventAccessorsTreeNode. + /// + public class AnalyzedEventAccessorsTreeNode : AnalyzerTreeNode + { + EventDefinition analyzedEvent; + + public AnalyzedEventAccessorsTreeNode(EventDefinition analyzedEvent) + { + if (analyzedEvent == null) + throw new ArgumentNullException("analyzedEvent"); + this.analyzedEvent = analyzedEvent; + + 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)); + } + + public override object Icon + { + get { return Images.Search; } + } + + public override object Text + { + get { return "Accessors"; } + } + + public static bool CanShow(EventDefinition property) + { + return !MainWindow.Instance.CurrentLanguage.ShowMember(property.AddMethod ?? property.RemoveMethod); + } + + class AnalyzedEventAccessorTreeNode : AnalyzedMethodTreeNode + { + string name; + + public AnalyzedEventAccessorTreeNode(MethodDefinition analyzedMethod, string name) + : base(analyzedMethod) + { + this.name = name; + } + + public override object Text + { + get { return name ?? base.Text; } + } + } + } +} diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs new file mode 100644 index 000000000..f406b45e0 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using ICSharpCode.Decompiler.Ast; +using ICSharpCode.NRefactory.Utils; +using ICSharpCode.TreeView; +using Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + class AnalyzedEventOverridesTreeNode : AnalyzerTreeNode + { + readonly EventDefinition analyzedEvent; + readonly ThreadingSupport threading; + + public AnalyzedEventOverridesTreeNode(EventDefinition analyzedEvent) + { + if (analyzedEvent == null) + throw new ArgumentNullException("analyzedEvent"); + + this.analyzedEvent = analyzedEvent; + this.threading = new ThreadingSupport(); + this.LazyLoading = true; + } + + public override object Text + { + get { return "Overriden 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(); + } + } + + IEnumerable FetchChildren(CancellationToken ct) + { + return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct); + } + + IEnumerable FindReferences(IEnumerable assemblies, CancellationToken ct) + { + assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null); + // use parallelism only on the assembly level (avoid locks within Cecil) + return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct)); + } + + IEnumerable FindReferences(LoadedAssembly asm, CancellationToken ct) + { + string asmName = asm.AssemblyDefinition.Name.Name; + string name = analyzedEvent.Name; + string declTypeName = analyzedEvent.DeclaringType.FullName; + foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) { + ct.ThrowIfCancellationRequested(); + + if (!TypesHierarchyHelpers.IsBaseType(analyzedEvent.DeclaringType, type, resolveTypeArguments: false)) + continue; + + foreach (EventDefinition eventDef in type.Events) { + ct.ThrowIfCancellationRequested(); + + if (TypesHierarchyHelpers.IsBaseEvent(analyzedEvent, eventDef)) { + MethodDefinition anyAccessor = eventDef.AddMethod ?? eventDef.RemoveMethod; + bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot; + yield return new AnalyzedEventTreeNode(eventDef, hidesParent ? "(hides) " : ""); + } + } + } + } + + public static bool CanShowAnalyzer(EventDefinition property) + { + var accessor = property.AddMethod ?? property.RemoveMethod; + return accessor.IsVirtual && !accessor.IsFinal && !accessor.DeclaringType.IsInterface; + } + } +} diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs new file mode 100644 index 000000000..e2db2aa67 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs @@ -0,0 +1,81 @@ +// 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 Mono.Cecil; +using ICSharpCode.Decompiler; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + class AnalyzedEventTreeNode : AnalyzerTreeNode + { + EventDefinition analyzedEvent; + string prefix; + + public AnalyzedEventTreeNode(EventDefinition analyzedEvent, string prefix = "") + { + if (analyzedEvent == null) + throw new ArgumentNullException("analyzedMethod"); + this.analyzedEvent = analyzedEvent; + this.prefix = prefix; + this.LazyLoading = true; + } + + public override object Icon { + get { return EventTreeNode.GetIcon(analyzedEvent); } + } + + public override object Text { + get { + // TODO: This way of formatting is not suitable for events which explicitly implement interfaces. + return prefix + Language.TypeToString(analyzedEvent.DeclaringType, true) + "." + EventTreeNode.GetText(analyzedEvent, Language); } + } + + public override void ActivateItem(System.Windows.RoutedEventArgs e) + { + e.Handled = true; + MainWindow.Instance.JumpToReference(analyzedEvent); + } + + protected override void LoadChildren() + { + if(AnalyzedEventAccessorsTreeNode.CanShow(analyzedEvent)) + this.Children.Add(new AnalyzedEventAccessorsTreeNode(analyzedEvent)); + if (AnalyzedEventOverridesTreeNode.CanShowAnalyzer(analyzedEvent)) + this.Children.Add(new AnalyzedEventOverridesTreeNode(analyzedEvent)); + } + + public static AnalyzerTreeNode TryCreateAnalyzer(MemberReference member) + { + if (CanShow(member)) + return new AnalyzedEventTreeNode(member as EventDefinition); + else + return null; + } + + public static bool CanShow(MemberReference member) + { + var property = member as EventDefinition; + if (property == null) + return false; + + return AnalyzedEventAccessorsTreeNode.CanShow(property) + || AnalyzedEventOverridesTreeNode.CanShowAnalyzer(property); + } + } +} diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 94cba230e..55e6432e9 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -47,17 +47,33 @@ namespace ICSharpCode.ILSpy.TreeNodes } } - public EventDefinition EventDefinition { + public EventDefinition EventDefinition + { get { return ev; } } - public override object Text { - get { return HighlightSearchMatch(ev.Name, " : " + this.Language.TypeToString(ev.EventType, false, ev)); } + public override object Text + { + get { return GetText(ev, this.Language); } + } + + public static object GetText(EventDefinition eventDef, Language language) + { + return HighlightSearchMatch(eventDef.Name, " : " + language.TypeToString(eventDef.EventType, false, eventDef)); } public override object Icon { - get { return Images.GetIcon(MemberIcon.Event, GetOverlayIcon(ev.AddMethod.Attributes), ev.AddMethod.IsStatic); } + get { return GetIcon(ev); } + } + + public static object GetIcon(EventDefinition eventDef) + { + MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod; + if (accessor != null) + return Images.GetIcon(MemberIcon.Event, GetOverlayIcon(eventDef.AddMethod.Attributes), eventDef.AddMethod.IsStatic); + else + return Images.GetIcon(MemberIcon.Event, AccessOverlayIcon.Public, false); } private static AccessOverlayIcon GetOverlayIcon(MethodAttributes methodAttributes) @@ -91,7 +107,8 @@ namespace ICSharpCode.ILSpy.TreeNodes language.DecompileEvent(ev, output, options); } - MemberReference IMemberTreeNode.Member { + MemberReference IMemberTreeNode.Member + { get { return ev; } } }