From a683ba51ff3bd40edd0ebfa75a86441f08936925 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Sat, 21 May 2011 23:07:39 +1000 Subject: [PATCH 1/4] Removed redundant parameter. --- ILSpy/TreeNodes/Analyzer/Helpers.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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); From eba91caf6cd84e99c41f0885e268354723adec3e Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Sat, 21 May 2011 23:12:35 +1000 Subject: [PATCH 2/4] Analyzer: further improvements to where-used analysis for virtual methods. --- .../AnalyzedVirtualMethodUsedByTreeNode.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) 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; + } } } } From 4c8ea69ddb7129fd72d4064497469be73d299138 Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Tue, 24 May 2011 13:37:24 +1000 Subject: [PATCH 3/4] 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)); } From 5d7ff43a8473191cf41fe25e9bb70105f365319b Mon Sep 17 00:00:00 2001 From: Ed Harvey Date: Tue, 24 May 2011 15:29:22 +1000 Subject: [PATCH 4/4] Analyzer: code refactoring in event raised-by analyzer. --- .../Analyzer/AnalyzedEventFiredByTreeNode.cs | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs index 89e892d12..02320cc09 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs @@ -45,9 +45,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer 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.eventBackingField = GetBackingField(analyzedEvent); this.eventFiringMethod = analyzedEvent.EventType.Resolve().Methods.First(md => md.Name == "Invoke"); } @@ -79,16 +77,20 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { foundMethods = new ConcurrentDictionary(); - foreach (var child in FindReferencesInType()) { + foreach (var child in FindReferencesInType(analyzedEvent.DeclaringType)) { yield return child; } foundMethods = null; } - private IEnumerable FindReferencesInType() + private IEnumerable FindReferencesInType(TypeDefinition type) { - foreach (MethodDefinition method in analyzedEvent.DeclaringType.Methods) { + // 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) @@ -126,8 +128,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return !foundMethods.TryAdd(method, 0); } - - public static bool CanShow(EventDefinition ev) + // 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"; @@ -136,9 +138,16 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer foreach (var fd in ev.DeclaringType.Fields) { if (fd.Name == fieldName || fd.Name == vbStyleFieldName) if (fd.FieldType.FullName == fieldType.FullName) - return true; + return fd; } - return false; + + return null; + } + + + public static bool CanShow(EventDefinition ev) + { + return GetBackingField(ev) != null; } } }