From 4c8ea69ddb7129fd72d4064497469be73d299138 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Tue, 24 May 2011 13:37:24 +1000 Subject: [PATCH] Added rudimentary event raised-by analyzer. Only works for 'standard' events (ie events without custom add/remove methods). Only implementations are analyzed, so event declarations on interfaces are out as well. --- ILSpy/ILSpy.csproj | 1 + .../Analyzer/AnalyzedEventFiredByTreeNode.cs | 144 ++++++++++++++++++ .../Analyzer/AnalyzedEventTreeNode.cs | 6 + 3 files changed, 151 insertions(+) create mode 100644 ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 8c90134d6..b3ad7a9be 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -144,6 +144,7 @@ + diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs new file mode 100644 index 000000000..89e892d12 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs @@ -0,0 +1,144 @@ +// 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 = analyzedEvent.DeclaringType.Fields.First( + fd => (fd.Name == analyzedEvent.Name || fd.Name == (analyzedEvent.Name + "Event")) && + fd.FieldType.FullName == analyzedEvent.EventType.FullName); + 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()) { + yield return child; + } + + foundMethods = null; + } + + private IEnumerable FindReferencesInType() + { + foreach (MethodDefinition method in analyzedEvent.DeclaringType.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); + } + + + public static bool CanShow(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 true; + } + return false; + } + } +} 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)); }