Browse Source

Constrain where-used searches to accessibility domain.

pull/138/head
Ed Harvey 15 years ago
parent
commit
1aaa720cc4
  1. 1
      ILSpy/ILSpy.csproj
  2. 66
      ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs
  3. 62
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs
  4. 250
      ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs

1
ILSpy/ILSpy.csproj

@ -132,6 +132,7 @@ @@ -132,6 +132,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzeContextMenuEntry.cs" />
<Compile Include="TreeNodes\Analyzer\Helpers.cs" />
<Compile Include="TreeNodes\Analyzer\ScopedWhereUsedAnalyzer.cs" />
<Compile Include="TreeNodes\IMemberTreeNode.cs" />
<Compile Include="TreeNodes\XamlResourceNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedPropertyAccessorsTreeNode.cs" />

66
ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs

@ -18,31 +18,33 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -18,31 +18,33 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
readonly bool showWrites; // true: show writes; false: show read access
readonly FieldDefinition analyzedField;
readonly ThreadingSupport threading;
public AnalyzedFieldAccessNode(FieldDefinition analyzedField, bool showWrites)
{
if (analyzedField == null)
throw new ArgumentNullException("analyzedField");
this.analyzedField = analyzedField;
this.showWrites = showWrites;
this.threading = new ThreadingSupport();
this.LazyLoading = true;
}
public override object Text {
public override object Text
{
get { return showWrites ? "Assigned By" : "Read By"; }
}
public override object Icon {
public override object Icon
{
get { return Images.Search; }
}
protected override void LoadChildren()
{
threading.LoadChildren(this, FetchChildren);
}
protected override void OnCollapsing()
{
if (threading.IsRunning) {
@ -51,44 +53,36 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -51,44 +53,36 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
this.Children.Clear();
}
}
IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct);
}
IEnumerable<SharpTreeNode> FindReferences(IEnumerable<LoadedAssembly> 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));
var analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedField, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
{
string name = analyzedField.Name;
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (MethodDefinition method in type.Methods) {
ct.ThrowIfCancellationRequested();
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
if (CanBeReference(instr.OpCode.Code)) {
FieldReference fr = instr.Operand as FieldReference;
if (fr != null && fr.Name == name && Helpers.IsReferencedBy(analyzedField.DeclaringType, fr.DeclaringType) && fr.Resolve() == analyzedField) {
found = true;
break;
}
string declTypeName = analyzedField.DeclaringType.FullName;
foreach (MethodDefinition method in type.Methods) {
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
if (CanBeReference(instr.OpCode.Code)) {
FieldReference fr = instr.Operand as FieldReference;
if (fr != null && fr.Name == name && Helpers.IsReferencedBy(analyzedField.DeclaringType, fr.DeclaringType) && fr.Resolve() == analyzedField) {
found = true;
break;
}
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
}
bool CanBeReference(Code code)
{
switch (code) {

62
ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs

@ -32,30 +32,32 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -32,30 +32,32 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
MethodDefinition analyzedMethod;
ThreadingSupport threading;
public AnalyzedMethodUsedByTreeNode(MethodDefinition analyzedMethod)
{
if (analyzedMethod == null)
throw new ArgumentNullException("analyzedMethod");
this.analyzedMethod = analyzedMethod;
this.threading = new ThreadingSupport();
this.LazyLoading = true;
}
public override object Text {
public override object Text
{
get { return "Used By"; }
}
public override object Icon {
public override object Icon
{
get { return Images.Search; }
}
protected override void LoadChildren()
{
threading.LoadChildren(this, FetchChildren);
}
protected override void OnCollapsing()
{
if (threading.IsRunning) {
@ -64,39 +66,31 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -64,39 +66,31 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
this.Children.Clear();
}
}
IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct);
}
IEnumerable<SharpTreeNode> FindReferences(IEnumerable<LoadedAssembly> 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));
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
{
string name = analyzedMethod.Name;
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (MethodDefinition method in type.Methods) {
ct.ThrowIfCancellationRequested();
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
MethodReference mr = instr.Operand as MethodReference;
if (mr != null && mr.Name == name && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) {
found = true;
break;
}
foreach (MethodDefinition method in type.Methods) {
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
MethodReference mr = instr.Operand as MethodReference;
if (mr != null && mr.Name == name && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) {
found = true;
break;
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
}
}

250
ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs

@ -0,0 +1,250 @@ @@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Mono.Cecil;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
/// <summary>
/// Determines the accessibility domain of a member for where-used analysis.
/// </summary>
internal class ScopedWhereUsedScopeAnalyzer<T>
{
private AssemblyDefinition assemblyScope;
private TypeDefinition typeScope;
private Accessibility memberAccessibility = Accessibility.Public;
private Accessibility typeAccessibility = Accessibility.Public;
private Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction;
private ScopedWhereUsedScopeAnalyzer(TypeDefinition type, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
{
this.typeScope = type;
this.assemblyScope = type.Module.Assembly;
this.typeAnalysisFunction = typeAnalysisFunction;
}
public ScopedWhereUsedScopeAnalyzer(MethodDefinition method, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
: this(method.DeclaringType, typeAnalysisFunction)
{
switch (method.Attributes & MethodAttributes.MemberAccessMask) {
case MethodAttributes.Private:
default:
memberAccessibility = Accessibility.Private;
break;
case MethodAttributes.FamANDAssem:
memberAccessibility = Accessibility.FamilyAndInternal;
break;
case MethodAttributes.Family:
memberAccessibility = Accessibility.Family;
break;
case MethodAttributes.Assembly:
memberAccessibility = Accessibility.Internal;
break;
case MethodAttributes.FamORAssem:
memberAccessibility = Accessibility.FamilyOrInternal;
break;
case MethodAttributes.Public:
memberAccessibility = Accessibility.Public;
break;
}
}
public ScopedWhereUsedScopeAnalyzer(FieldDefinition field, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
: this(field.DeclaringType, typeAnalysisFunction)
{
switch (field.Attributes & FieldAttributes.FieldAccessMask) {
case FieldAttributes.Private:
default:
memberAccessibility = Accessibility.Private;
break;
case FieldAttributes.FamANDAssem:
memberAccessibility = Accessibility.FamilyAndInternal;
break;
case FieldAttributes.Assembly:
memberAccessibility = Accessibility.Internal;
break;
case FieldAttributes.Family:
memberAccessibility = Accessibility.Family;
break;
case FieldAttributes.FamORAssem:
memberAccessibility = Accessibility.FamilyOrInternal;
break;
case FieldAttributes.Public:
memberAccessibility = Accessibility.Public;
break;
}
}
public IEnumerable<T> PerformAnalysis(CancellationToken ct)
{
if (memberAccessibility == Accessibility.Private) {
return FindReferencesInTypeScope(ct);
}
DetermineTypeAccessibility();
if (typeAccessibility == Accessibility.Private) {
return FindReferencesInTypeScope(ct);
}
if (memberAccessibility == Accessibility.Internal ||
memberAccessibility == Accessibility.FamilyAndInternal ||
typeAccessibility == Accessibility.Internal ||
typeAccessibility == Accessibility.FamilyAndInternal)
return FindReferencesInAssemblyAndFriends(ct);
return FindReferencesGlobal(ct);
}
private void DetermineTypeAccessibility()
{
while (typeScope.IsNested) {
Accessibility accessibility = GetNestedTypeAccessibility(typeScope);
if ((int)typeAccessibility > (int)accessibility) {
typeAccessibility = accessibility;
if (typeAccessibility == Accessibility.Private)
return;
}
typeScope = typeScope.DeclaringType;
}
if (typeScope.IsNotPublic &&
((int)typeAccessibility > (int)Accessibility.Internal)) {
typeAccessibility = Accessibility.Internal;
}
}
private Accessibility GetNestedTypeAccessibility(TypeDefinition type)
{
Accessibility result;
switch (type.Attributes & TypeAttributes.VisibilityMask) {
case TypeAttributes.NestedPublic:
result = Accessibility.Public;
break;
case TypeAttributes.NestedPrivate:
result = Accessibility.Private;
break;
case TypeAttributes.NestedFamily:
result = Accessibility.Family;
break;
case TypeAttributes.NestedAssembly:
result = Accessibility.Internal;
break;
case TypeAttributes.NestedFamANDAssem:
result = Accessibility.FamilyAndInternal;
break;
case TypeAttributes.NestedFamORAssem:
result = Accessibility.FamilyOrInternal;
break;
default:
throw new InvalidOperationException();
}
return result;
}
/// <summary>
/// The effective accessibility of a member
/// </summary>
private enum Accessibility
{
Private,
FamilyAndInternal,
Internal,
Family,
FamilyOrInternal,
Public
}
IEnumerable<T> FindReferencesInAssemblyAndFriends(CancellationToken ct)
{
var assemblies = GetAssemblyAndAnyFriends(assemblyScope, ct);
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((AssemblyDefinition a) => FindReferencesInAssembly(a, ct));
}
IEnumerable<T> FindReferencesGlobal(CancellationToken ct)
{
var assemblies = GetReferencingAssemblies(assemblyScope, ct);
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((AssemblyDefinition asm) => FindReferencesInAssembly(asm, ct));
}
IEnumerable<T> FindReferencesInAssembly(AssemblyDefinition asm, CancellationToken ct)
{
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (var result in typeAnalysisFunction(type)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<T> FindReferencesInTypeScope(CancellationToken ct)
{
foreach (TypeDefinition type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (var result in typeAnalysisFunction(type)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<AssemblyDefinition> GetReferencingAssemblies(AssemblyDefinition asm, CancellationToken ct)
{
yield return asm;
string requiredAssemblyFullName = asm.FullName;
IEnumerable<LoadedAssembly> assemblies = MainWindow.Instance.CurrentAssemblyList.GetAssemblies().Where(assy => assy.AssemblyDefinition != null);
foreach (var assembly in assemblies) {
ct.ThrowIfCancellationRequested();
bool found = false;
foreach (var reference in assembly.AssemblyDefinition.MainModule.AssemblyReferences) {
if (requiredAssemblyFullName == reference.FullName) {
found = true;
break;
}
}
if (found)
yield return assembly.AssemblyDefinition;
}
}
IEnumerable<AssemblyDefinition> GetAssemblyAndAnyFriends(AssemblyDefinition asm, CancellationToken ct)
{
yield return asm;
if (asm.HasCustomAttributes) {
var attributes = asm.CustomAttributes
.Where(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute");
var friendAssemblies = new HashSet<string>();
foreach (var attribute in attributes) {
string assemblyName = attribute.ConstructorArguments[0].Value as string;
assemblyName = assemblyName.Split(',')[0]; // strip off any public key info
friendAssemblies.Add(assemblyName);
}
if (friendAssemblies.Count > 0) {
IEnumerable<LoadedAssembly> assemblies = MainWindow.Instance.CurrentAssemblyList.GetAssemblies();
foreach (var assembly in assemblies) {
ct.ThrowIfCancellationRequested();
if (friendAssemblies.Contains(assembly.ShortName)) {
yield return assembly.AssemblyDefinition;
}
}
}
}
}
}
}
Loading…
Cancel
Save