mirror of https://github.com/icsharpcode/ILSpy.git
3 changed files with 207 additions and 6 deletions
@ -0,0 +1,203 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using System.Text; |
||||||
|
using System.Threading; |
||||||
|
using ICSharpCode.NRefactory.Utils; |
||||||
|
using ICSharpCode.TreeView; |
||||||
|
using Mono.Cecil; |
||||||
|
|
||||||
|
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Searches for overrides of the analyzed method.
|
||||||
|
/// </summary>
|
||||||
|
class AnalyzerMethodOverridesTreeNode : AnalyzerTreeNode |
||||||
|
{ |
||||||
|
readonly MethodDefinition analyzedMethod; |
||||||
|
readonly ThreadingSupport threading; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls whether overrides of already overriden method should be included.
|
||||||
|
/// </summary>
|
||||||
|
readonly bool onlyDirectOverrides = false; |
||||||
|
|
||||||
|
public AnalyzerMethodOverridesTreeNode(MethodDefinition analyzedMethod) |
||||||
|
{ |
||||||
|
if (analyzedMethod == null) |
||||||
|
throw new ArgumentNullException("analyzedMethod"); |
||||||
|
|
||||||
|
this.analyzedMethod = analyzedMethod; |
||||||
|
this.threading = new ThreadingSupport(); |
||||||
|
this.LazyLoading = true; |
||||||
|
} |
||||||
|
|
||||||
|
public override object Text |
||||||
|
{ |
||||||
|
get { return "Overrided 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<SharpTreeNode> FetchChildren(CancellationToken ct) |
||||||
|
{ |
||||||
|
return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct); |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly[] assemblies, CancellationToken ct) |
||||||
|
{ |
||||||
|
// use parallelism only on the assembly level (avoid locks within Cecil)
|
||||||
|
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct)); |
||||||
|
} |
||||||
|
|
||||||
|
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct) |
||||||
|
{ |
||||||
|
string asmName = asm.AssemblyDefinition.Name.Name; |
||||||
|
string name = analyzedMethod.Name; |
||||||
|
string declTypeName = analyzedMethod.DeclaringType.FullName; |
||||||
|
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) |
||||||
|
{ |
||||||
|
ct.ThrowIfCancellationRequested(); |
||||||
|
|
||||||
|
if (!IsDerived(type, analyzedMethod.DeclaringType)) |
||||||
|
continue; |
||||||
|
|
||||||
|
foreach (MethodDefinition method in type.Methods) |
||||||
|
{ |
||||||
|
ct.ThrowIfCancellationRequested(); |
||||||
|
|
||||||
|
if (HasCompatibleSpecification(method) && !method.IsNewSlot && DoesOverrideCorrectMethod(method)) |
||||||
|
{ |
||||||
|
yield return new AnalyzedMethodTreeNode(method); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests whether the method could override analyzed method by comparing its name, return type and parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">The method to test.</param>
|
||||||
|
/// <returns>true if the method has the same specyfication as analyzed method, otherwise false.</returns>
|
||||||
|
private bool HasCompatibleSpecification(MethodDefinition method) |
||||||
|
{ |
||||||
|
return method.Name == analyzedMethod.Name |
||||||
|
&& method.IsVirtual |
||||||
|
&& AreSameType(method.ReturnType, analyzedMethod.ReturnType) |
||||||
|
&& HaveTheSameParameters(method); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether between given and analyzed method are overrides with <code>new</code> (newSlot) modifier.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">The method to test.</param>
|
||||||
|
/// <returns>true if the method overrides analyzed method, false if it overrides some other method that hides analyzed method.</returns>
|
||||||
|
private bool DoesOverrideCorrectMethod(MethodDefinition method) |
||||||
|
{ |
||||||
|
var type = method.DeclaringType.BaseType.Resolve(); |
||||||
|
while (type != analyzedMethod.DeclaringType) |
||||||
|
{ |
||||||
|
var parentOverride = type.Methods.Where(m => HasCompatibleSpecification(m)).SingleOrDefault(); |
||||||
|
if (parentOverride != null) |
||||||
|
{ |
||||||
|
if (parentOverride.IsNewSlot) |
||||||
|
return false; |
||||||
|
else |
||||||
|
return !onlyDirectOverrides; |
||||||
|
} |
||||||
|
type = type.BaseType.Resolve(); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether one type derives (directly or indirectly) from base type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="derivedType">The possible derived type.</param>
|
||||||
|
/// <param name="baseType">The base type.</param>
|
||||||
|
/// <returns>true if <paramref name="derivedType"/> derives from <paramref name="baseType"/>, overwise false.</returns>
|
||||||
|
private static bool IsDerived(TypeDefinition derivedType, TypeDefinition baseType) |
||||||
|
{ |
||||||
|
while (derivedType != null && derivedType.BaseType != null) |
||||||
|
{ |
||||||
|
if (AreSameType(derivedType.BaseType, baseType)) |
||||||
|
return true; |
||||||
|
derivedType = derivedType.BaseType.Resolve(); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether both <see cref="TypeReference"/> instances references the same type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ref1">The first type reference.</param>
|
||||||
|
/// <param name="ref2">The second type reference.</param>
|
||||||
|
/// <returns>true if both instances references the same type, overwise false.</returns>
|
||||||
|
private static bool AreSameType(TypeReference ref1, TypeReference ref2) |
||||||
|
{ |
||||||
|
if (ref1 == ref2) |
||||||
|
return true; |
||||||
|
|
||||||
|
if (ref1.Name != ref2.Name || ref1.FullName != ref2.FullName) |
||||||
|
return false; |
||||||
|
|
||||||
|
return ref1.Resolve() == ref2.Resolve(); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checkes whether the given method and the analyzed one has identical lists of parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">The method to test.</param>
|
||||||
|
/// <returns>true if both methods has the same parameters, otherwise false.</returns>
|
||||||
|
private bool HaveTheSameParameters(MethodDefinition method) |
||||||
|
{ |
||||||
|
if (analyzedMethod.HasParameters) |
||||||
|
{ |
||||||
|
return CompareParameterLists(analyzedMethod.Parameters, method.Parameters); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
return !method.HasParameters; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Compares the list of method's parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="coll1">The first list to compare.</param>
|
||||||
|
/// <param name="coll2">The second list to copare.</param>
|
||||||
|
/// <returns>true if both list have parameters of the same types at the same positions.</returns>
|
||||||
|
private static bool CompareParameterLists(Mono.Collections.Generic.Collection<ParameterDefinition> coll1, Mono.Collections.Generic.Collection<ParameterDefinition> coll2) |
||||||
|
{ |
||||||
|
if (coll1.Count != coll2.Count) |
||||||
|
return false; |
||||||
|
|
||||||
|
for (int index = 0; index < coll1.Count; index++) |
||||||
|
{ |
||||||
|
var param1 = coll1[index]; |
||||||
|
var param2 = coll2[index]; |
||||||
|
if (param1.Attributes != param2.Attributes || !AreSameType(param1.ParameterType, param2.ParameterType)) |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue