diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj
index 3eaca83ce..4e99f674b 100644
--- a/ILSpy/ILSpy.csproj
+++ b/ILSpy/ILSpy.csproj
@@ -148,6 +148,7 @@
+
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);