Browse Source

Add new Analyzer API (PoC)

pull/1030/head
Siegfried Pammer 7 years ago
parent
commit
a4bec3c8c5
  1. 92
      ILSpy/Analyzers/AnalyzeContextMenuEntry.cs
  2. 7
      ILSpy/Analyzers/AnalyzerEntityTreeNode.cs
  3. 78
      ILSpy/Analyzers/AnalyzerSearchTreeNode.cs
  4. 10
      ILSpy/Analyzers/AnalyzerTreeNode.cs
  5. 6
      ILSpy/Analyzers/AnalyzerTreeView.cs
  6. 117
      ILSpy/Analyzers/Builtin/FieldAccessAnalyzer.cs
  7. 66
      ILSpy/Analyzers/Builtin/MethodUsedByAnalyzer.cs
  8. 76
      ILSpy/Analyzers/Builtin/MethodUsesAnalyzer.cs
  9. 48
      ILSpy/Analyzers/IAnalyzer.cs
  10. 2
      ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs
  11. 269
      ILSpy/Analyzers/ScopedWhereUsedAnalyzer.cs
  12. 0
      ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs
  13. 28
      ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs
  14. 56
      ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs
  15. 0
      ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs
  16. 37
      ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs
  17. 20
      ILSpy/ILSpy.csproj
  18. 3
      ILSpy/Languages/CodeMappingInfo.cs
  19. 135
      ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs
  20. 145
      ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs
  21. 65
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs
  22. 96
      ILSpy/TreeNodes/Analyzer/Extensions.cs
  23. 144
      ILSpy/TreeNodes/Analyzer/Helpers.cs
  24. 350
      ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs
  25. 4
      ILSpy/TreeNodes/ThreadingSupport.cs

92
ILSpy/Analyzers/AnalyzeContextMenuEntry.cs

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
// 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.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.TreeNodes.Analyzer;
namespace ICSharpCode.ILSpy.Analyzers
{
[ExportContextMenuEntry(Header = "Analyze", Icon = "images/Search.png", Category = "Analyze", Order = 100)]
internal sealed class AnalyzeContextMenuEntry : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n.Parent.IsRoot))
return false;
if (context.SelectedTreeNodes == null)
return context.Reference != null && IsValidReference(context.Reference.Reference);
return context.SelectedTreeNodes.All(n => n is IMemberTreeNode);
}
public bool IsEnabled(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return context.Reference != null && context.Reference.Reference is IEntity;
foreach (IMemberTreeNode node in context.SelectedTreeNodes) {
if (!IsValidReference(node.Member))
return false;
}
return true;
}
bool IsValidReference(object reference)
{
return reference is IEntity;
}
public void Execute(TextViewContext context)
{
if (context.SelectedTreeNodes != null) {
foreach (IMemberTreeNode node in context.SelectedTreeNodes) {
Analyze(node.Member);
}
} else if (context.Reference != null && context.Reference.Reference is IEntity entity) {
Analyze(entity);
}
}
public static void Analyze(IEntity entity)
{
if (entity == null)
return;
switch (entity) {
case ITypeDefinition td:
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedTypeTreeNode(td));
break;
case IField fd:
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedFieldTreeNode(fd));
break;
case IMethod md:
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedMethodTreeNode(md));
break;
case IProperty pd:
//AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedPropertyTreeNode(pd));
break;
case IEvent ed:
//AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedEventTreeNode(ed));
break;
default:
throw new NotSupportedException();
}
}
}
}

7
ILSpy/TreeNodes/Analyzer/AnalyzerEntityTreeNode.cs → ILSpy/Analyzers/AnalyzerEntityTreeNode.cs

@ -19,16 +19,17 @@ @@ -19,16 +19,17 @@
using System.Collections.Generic;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
namespace ICSharpCode.ILSpy.Analyzers
{
/// <summary>
/// Base class for entity nodes.
/// </summary>
public abstract class AnalyzerEntityTreeNode : AnalyzerTreeNode, IMemberTreeNode
{
public abstract IMetadataEntity Member { get; }
public abstract IEntity Member { get; }
public override void ActivateItem(System.Windows.RoutedEventArgs e)
{
@ -39,7 +40,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -39,7 +40,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
public override bool HandleAssemblyListChanged(ICollection<LoadedAssembly> removedAssemblies, ICollection<LoadedAssembly> addedAssemblies)
{
foreach (LoadedAssembly asm in removedAssemblies) {
if (this.Member.Module == asm.GetPEFileOrNull())
if (this.Member.ParentAssembly.PEFile == asm.GetPEFileOrNull())
return false; // remove this node
}
this.Children.RemoveAll(

78
ILSpy/TreeNodes/Analyzer/AnalyzerSearchTreeNode.cs → ILSpy/Analyzers/AnalyzerSearchTreeNode.cs

@ -19,33 +19,81 @@ @@ -19,33 +19,81 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.ILSpy.TreeNodes.Analyzer;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
namespace ICSharpCode.ILSpy.Analyzers
{
/// <summary>
/// Base class for analyzer nodes that perform a search.
/// </summary>
public abstract class AnalyzerSearchTreeNode : AnalyzerTreeNode
class AnalyzerSearchTreeNode<T> : AnalyzerTreeNode where T : IEntity
{
private readonly ThreadingSupport threading = new ThreadingSupport();
protected AnalyzerSearchTreeNode()
readonly T analyzedEntity;
readonly IAnalyzer<T> analyzer;
public AnalyzerSearchTreeNode(T entity, IAnalyzer<T> analyzer)
{
this.analyzedEntity = entity;
this.analyzer = analyzer ?? throw new ArgumentNullException(nameof(analyzer));
this.LazyLoading = true;
}
public override object Icon
{
get { return Images.Search; }
}
public override object Text => analyzer.Text;
public override object Icon => Images.Search;
protected override void LoadChildren()
{
threading.LoadChildren(this, FetchChildren);
}
protected abstract IEnumerable<AnalyzerTreeNode> FetchChildren(CancellationToken ct);
protected IEnumerable<AnalyzerTreeNode> FetchChildren(CancellationToken ct)
{
if (analyzer is IEntityAnalyzer<T> entityAnalyzer) {
var module = analyzedEntity.ParentAssembly.PEFile;
var ts = new DecompilerTypeSystem(module, module.GetAssemblyResolver());
var context = new AnalyzerContext(ts) {
CancellationToken = ct,
CodeMappingInfo = Language.GetCodeMappingInfo(module, analyzedEntity.MetadataToken)
};
foreach (var result in entityAnalyzer.Analyze(analyzedEntity, context)) {
yield return EntityTreeNodeFactory(result);
}
} else {
foreach (var result in new ScopedWhereUsedAnalyzer<T>(Language, analyzedEntity, analyzer).PerformAnalysis(ct)) {
yield return EntityTreeNodeFactory(result);
}
}
}
AnalyzerTreeNode EntityTreeNodeFactory(IEntity result)
{
switch (result) {
case ITypeDefinition td:
return new AnalyzedTypeTreeNode(td) {
Language = this.Language
};
case IField fd:
return new AnalyzedFieldTreeNode(fd) {
Language = this.Language
};
case IMethod md:
return new AnalyzedMethodTreeNode(md) {
Language = this.Language
};
//case IProperty pd:
// return new AnalyzedPropertyTreeNode(pd) {
//Language = this.Language
//};
//case IEvent ed:
// return new AnalyzedEventTreeNode(ed) {
//Language = this.Language
//};
default:
throw new NotSupportedException();
}
}
protected override void OnIsVisibleChanged()
{
base.OnIsVisibleChanged();

10
ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs → ILSpy/Analyzers/AnalyzerTreeNode.cs

@ -21,17 +21,15 @@ using System.Collections.Specialized; @@ -21,17 +21,15 @@ using System.Collections.Specialized;
using System.Linq;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
namespace ICSharpCode.ILSpy.Analyzers
{
public abstract class AnalyzerTreeNode : SharpTreeNode
{
private Language language;
public Language Language
{
public Language Language {
get { return language; }
set
{
set {
if (language != value) {
language = value;
foreach (var child in this.Children.OfType<AnalyzerTreeNode>())
@ -63,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -63,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
}
base.OnChildrenChanged(e);
}
/// <summary>
/// Handles changes to the assembly list.
/// </summary>

6
ILSpy/AnalyzerTreeView.cs → ILSpy/Analyzers/AnalyzerTreeView.cs

@ -23,7 +23,7 @@ using System.Windows; @@ -23,7 +23,7 @@ using System.Windows;
using ICSharpCode.ILSpy.TreeNodes.Analyzer;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy
namespace ICSharpCode.ILSpy.Analyzers
{
/// <summary>
/// Analyzer tree view.
@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy @@ -86,7 +86,7 @@ namespace ICSharpCode.ILSpy
public void ShowOrFocus(AnalyzerTreeNode node)
{
/*if (node is AnalyzerEntityTreeNode) {
if (node is AnalyzerEntityTreeNode) {
var an = node as AnalyzerEntityTreeNode;
var found = this.Root.Children.OfType<AnalyzerEntityTreeNode>().FirstOrDefault(n => n.Member == an.Member);
if (found != null) {
@ -98,7 +98,7 @@ namespace ICSharpCode.ILSpy @@ -98,7 +98,7 @@ namespace ICSharpCode.ILSpy
return;
}
}
Show(node);*/
Show(node);
}
void IPane.Closed()

117
ILSpy/Analyzers/Builtin/FieldAccessAnalyzer.cs

@ -0,0 +1,117 @@ @@ -0,0 +1,117 @@
// 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;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Analyzers;
using ILOpCode = System.Reflection.Metadata.ILOpCode;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
/// <summary>
/// Finds methods where this field is read.
/// </summary>
[Export(typeof(IAnalyzer<IField>))]
class AssignedByFieldAccessAnalyzer : FieldAccessAnalyzer
{
public AssignedByFieldAccessAnalyzer() : base(true) { }
}
/// <summary>
/// Finds methods where this field is written.
/// </summary>
[Export(typeof(IAnalyzer<IField>))]
class ReadByFieldAccessAnalyzer : FieldAccessAnalyzer
{
public ReadByFieldAccessAnalyzer() : base(false) { }
}
/// <summary>
/// Finds methods where this field is read or written.
/// </summary>
class FieldAccessAnalyzer : IMethodBodyAnalyzer<IField>
{
readonly bool showWrites; // true: show writes; false: show read access
public string Text => showWrites ? "Assigned By" : "Read By";
public FieldAccessAnalyzer(bool showWrites)
{
this.showWrites = showWrites;
}
public bool Show(IField field)
{
return !showWrites || !field.IsConst;
}
public IEnumerable<IEntity> Analyze(IField analyzedField, IMethod method, MethodBodyBlock methodBody, AnalyzerContext context)
{
bool found = false;
var blob = methodBody.GetILReader();
while (!found && blob.RemainingBytes > 0) {
var opCode = blob.DecodeOpCode();
if (!CanBeReference(opCode)) {
blob.SkipOperand(opCode);
continue;
}
EntityHandle fieldHandle = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (!fieldHandle.Kind.IsMemberKind())
continue;
var field = context.TypeSystem.ResolveAsField(fieldHandle);
if (field == null)
continue;
found = field.MetadataToken == analyzedField.MetadataToken
&& field.ParentAssembly.PEFile == analyzedField.ParentAssembly.PEFile;
}
if (found) {
yield return method;
}
}
bool CanBeReference(ILOpCode code)
{
switch (code) {
case ILOpCode.Ldfld:
case ILOpCode.Ldsfld:
return !showWrites;
case ILOpCode.Stfld:
case ILOpCode.Stsfld:
return showWrites;
case ILOpCode.Ldflda:
case ILOpCode.Ldsflda:
return true; // always show address-loading
default:
return false;
}
}
}
}

66
ILSpy/Analyzers/Builtin/MethodUsedByAnalyzer.cs

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.Analyzers.Builtin
{
/// <summary>
/// Shows entities that are used by a method.
/// </summary>
[Export(typeof(IAnalyzer<IMethod>))]
class MethodUsedByAnalyzer : IMethodBodyAnalyzer<IMethod>
{
public string Text => "Used By";
public bool Show(IMethod entity) => true;
public IEnumerable<IEntity> Analyze(IMethod analyzedMethod, IMethod method, MethodBodyBlock methodBody, AnalyzerContext context)
{
var blob = methodBody.GetILReader();
while (blob.RemainingBytes > 0) {
var opCode = blob.DecodeOpCode();
switch (opCode.GetOperandType()) {
case OperandType.Field:
case OperandType.Method:
case OperandType.Sig:
case OperandType.Tok:
var member = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (member.IsNil) continue;
switch (member.Kind) {
case HandleKind.MethodDefinition:
case HandleKind.MethodSpecification:
case HandleKind.MemberReference:
var m = context.TypeSystem.ResolveAsMember(member)?.MemberDefinition;
if (m.MetadataToken == analyzedMethod.MetadataToken && m.ParentAssembly.PEFile == analyzedMethod.ParentAssembly.PEFile) {
yield return method;
yield break;
}
break;
}
break;
default:
ILParser.SkipOperand(ref blob, opCode);
break;
}
}
}
bool ScanMethodBody(PEFile module, int rva)
{
var blob = module.Reader.GetMethodBody(rva).GetILReader();
while (blob.RemainingBytes > 0) {
}
return false;
}
}
}

76
ILSpy/Analyzers/Builtin/MethodUsesAnalyzer.cs

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.Analyzers.Builtin
{
/// <summary>
/// Shows entities that are used by a method.
/// </summary>
[Export(typeof(IAnalyzer<IMethod>))]
class MethodUsesAnalyzer : IEntityAnalyzer<IMethod>
{
public string Text => "Uses";
public bool Show(IMethod entity) => true;
public IEnumerable<IEntity> Analyze(IMethod analyzedMethod, AnalyzerContext context)
{
return context.CodeMappingInfo.GetMethodParts((MethodDefinitionHandle)analyzedMethod.MetadataToken)
.SelectMany(h => ScanMethod(analyzedMethod, h, context)).Distinct();
}
IEnumerable<IEntity> ScanMethod(IMethod analyzedMethod, MethodDefinitionHandle handle, AnalyzerContext context)
{
var module = analyzedMethod.ParentAssembly.PEFile;
var md = module.Metadata.GetMethodDefinition(handle);
if (!md.HasBody()) yield break;
var blob = module.Reader.GetMethodBody(md.RelativeVirtualAddress).GetILReader();
while (blob.RemainingBytes > 0) {
var opCode = blob.DecodeOpCode();
switch (opCode.GetOperandType()) {
case OperandType.Field:
case OperandType.Method:
case OperandType.Sig:
case OperandType.Tok:
var member = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (member.IsNil) continue;
switch (member.Kind) {
case HandleKind.StandaloneSignature:
break;
case HandleKind.TypeDefinition:
case HandleKind.TypeReference:
case HandleKind.TypeSpecification:
var type = context.TypeSystem.ResolveAsType(member).GetDefinition();
if (type != null)
yield return type;
break;
case HandleKind.MethodDefinition:
case HandleKind.MethodSpecification:
case HandleKind.MemberReference:
case HandleKind.FieldDefinition:
var m = context.TypeSystem.ResolveAsMember(member);
if (m != null)
yield return m;
break;
}
break;
default:
ILParser.SkipOperand(ref blob, opCode);
break;
}
}
}
}
}

48
ILSpy/Analyzers/IAnalyzer.cs

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.Analyzers
{
/// <summary>
/// This is the common interface for all analyzers. Note: implementing this interface alone will not provide a full analyzer.
/// You must implement either the <see cref="ITypeDefinitionAnalyzer{T}"/>, <see cref="IEntityAnalyzer{T}"/> or <see cref="IMethodBodyAnalyzer{T}"/> interfaces.
/// </summary>
public interface IAnalyzer<T> where T : IEntity
{
string Text { get; }
bool Show(T entity);
}
public interface IEntityAnalyzer<T> : IAnalyzer<T> where T : IEntity
{
IEnumerable<IEntity> Analyze(T analyzedEntity, AnalyzerContext context);
}
public interface ITypeDefinitionAnalyzer<T> : IAnalyzer<T> where T : IEntity
{
IEnumerable<IEntity> Analyze(T analyzedEntity, ITypeDefinition type, AnalyzerContext context);
}
public interface IMethodBodyAnalyzer<T> : IAnalyzer<T> where T : IEntity
{
IEnumerable<IEntity> Analyze(T analyzedEntity, IMethod method, MethodBodyBlock methodBody, AnalyzerContext context);
}
public class AnalyzerContext
{
public AnalyzerContext(IDecompilerTypeSystem typeSystem)
{
this.TypeSystem = typeSystem;
}
public IDecompilerTypeSystem TypeSystem { get; }
public CancellationToken CancellationToken { get; internal set; }
public CodeMappingInfo CodeMappingInfo { get; internal set; }
}
}

2
ILSpy/TreeNodes/Analyzer/RemoveAnalyzeContextMenuEntry.cs → ILSpy/Analyzers/RemoveAnalyzeContextMenuEntry.cs

@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
using System.Linq;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
namespace ICSharpCode.ILSpy.Analyzers
{
[ExportContextMenuEntry(Header = "Remove", Icon = "images/Delete.png", Category = "Analyze", Order = 200)]
internal sealed class RemoveAnalyzeContextMenuEntry : IContextMenuEntry

269
ILSpy/Analyzers/ScopedWhereUsedAnalyzer.cs

@ -0,0 +1,269 @@ @@ -0,0 +1,269 @@
// 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.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.Analyzers;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
/// <summary>
/// Determines the accessibility domain of a member for where-used analysis.
/// </summary>
class ScopedWhereUsedAnalyzer<T> where T : IEntity
{
readonly Language language;
readonly IAnalyzer<T> analyzer;
readonly IAssembly assemblyScope;
readonly T analyzedEntity;
ITypeDefinition typeScope;
readonly Accessibility memberAccessibility = Accessibility.Public;
Accessibility typeAccessibility = Accessibility.Public;
public ScopedWhereUsedAnalyzer(Language language, T analyzedEntity, IAnalyzer<T> analyzer)
{
this.language = language ?? throw new ArgumentNullException(nameof(language));
this.analyzer = analyzer ?? throw new ArgumentNullException(nameof(analyzer));
this.analyzedEntity = analyzedEntity ;
this.assemblyScope = analyzedEntity.ParentAssembly;
if (analyzedEntity is ITypeDefinition type) {
this.typeScope = type;
this.typeAccessibility = type.Accessibility;
} else {
this.typeScope = analyzedEntity.DeclaringTypeDefinition;
this.typeAccessibility = this.typeScope.Accessibility;
this.memberAccessibility = analyzedEntity.Accessibility;
}
}
public IEnumerable<IEntity> PerformAnalysis(CancellationToken ct)
{
if (memberAccessibility == Accessibility.Private) {
return FindReferencesInTypeScope(ct);
}
DetermineTypeAccessibility();
if (typeAccessibility == Accessibility.Private && typeScope.DeclaringType != null) {
return FindReferencesInEnclosingTypeScope(ct);
}
if (memberAccessibility == Accessibility.Internal ||
memberAccessibility == Accessibility.ProtectedOrInternal ||
typeAccessibility == Accessibility.Internal ||
typeAccessibility == Accessibility.ProtectedAndInternal)
return FindReferencesInAssemblyAndFriends(ct);
return FindReferencesGlobal(ct);
}
void DetermineTypeAccessibility()
{
while (typeScope.DeclaringType != null) {
Accessibility accessibility = typeScope.Accessibility;
if ((int)typeAccessibility > (int)accessibility) {
typeAccessibility = accessibility;
if (typeAccessibility == Accessibility.Private)
return;
}
typeScope = typeScope.DeclaringTypeDefinition;
}
if ((int)typeAccessibility > (int)Accessibility.Internal) {
typeAccessibility = Accessibility.Internal;
}
}
IEnumerable<IEntity> FindReferencesInAssemblyAndFriends(CancellationToken ct)
{
var assemblies = GetAssemblyAndAnyFriends(assemblyScope.PEFile, ct);
return assemblies.AsParallel().WithCancellation(ct).SelectMany(a => FindReferencesInAssembly(a, ct));
}
IEnumerable<IEntity> FindReferencesGlobal(CancellationToken ct)
{
var assemblies = GetReferencingAssemblies(assemblyScope.PEFile, ct);
return assemblies.AsParallel().WithCancellation(ct).SelectMany(asm => FindReferencesInAssembly(asm, ct));
}
IEnumerable<IEntity> FindReferencesInAssembly(PEFile module, CancellationToken ct)
{
var ts = new DecompilerTypeSystem(module, module.GetAssemblyResolver());
var context = new AnalyzerContext(ts) { CancellationToken = ct };
foreach (var type in ts.MainAssembly.TypeDefinitions) {
ct.ThrowIfCancellationRequested();
if (type.MetadataToken.IsNil) continue;
context.CodeMappingInfo = language.GetCodeMappingInfo(module, type.MetadataToken);
foreach (var result in RunAnalysisOn(module, type, context)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<IEntity> FindReferencesInTypeScope(CancellationToken ct)
{
var ts = new DecompilerTypeSystem(assemblyScope.PEFile, assemblyScope.PEFile.GetAssemblyResolver());
var context = new AnalyzerContext(ts) { CancellationToken = ct };
var types = TreeTraversal.PreOrder(typeScope,
t => t.GetNestedTypes(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions)
.Select(td => td.GetDefinition()));
foreach (var type in types) {
ct.ThrowIfCancellationRequested();
if (type.MetadataToken.IsNil) continue;
context.CodeMappingInfo = language.GetCodeMappingInfo(assemblyScope.PEFile, type.MetadataToken);
foreach (var result in RunAnalysisOn(assemblyScope.PEFile, type, context)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<IEntity> FindReferencesInEnclosingTypeScope(CancellationToken ct)
{
var ts = new DecompilerTypeSystem(assemblyScope.PEFile, assemblyScope.PEFile.GetAssemblyResolver());
var context = new AnalyzerContext(ts) { CancellationToken = ct };
var types = TreeTraversal.PreOrder(typeScope.DeclaringTypeDefinition,
t => t.GetNestedTypes(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions)
.Select(td => td.GetDefinition()));
foreach (var type in types) {
ct.ThrowIfCancellationRequested();
if (type.MetadataToken.IsNil) continue;
context.CodeMappingInfo = language.GetCodeMappingInfo(assemblyScope.PEFile, type.MetadataToken);
foreach (var result in RunAnalysisOn(assemblyScope.PEFile, type, context)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<PEFile> GetReferencingAssemblies(PEFile asm, CancellationToken ct)
{
yield return asm;
IEnumerable<LoadedAssembly> assemblies = MainWindow.Instance.CurrentAssemblyList.GetAssemblies();
foreach (var assembly in assemblies) {
ct.ThrowIfCancellationRequested();
bool found = false;
var module = assembly.GetPEFileOrNull();
if (module == null || !module.IsAssembly)
continue;
var resolver = assembly.GetAssemblyResolver();
foreach (var reference in module.AssemblyReferences) {
using (LoadedAssembly.DisableAssemblyLoad()) {
if (resolver.Resolve(reference) == asm) {
found = true;
break;
}
}
}
if (found && AssemblyReferencesScopeType(module.Metadata, typeScope.Name, typeScope.Namespace))
yield return module;
}
}
IEnumerable<PEFile> GetAssemblyAndAnyFriends(PEFile asm, CancellationToken ct)
{
yield return asm;
var typeProvider = MetadataExtensions.MinimalAttributeTypeProvider;
var attributes = asm.Metadata.CustomAttributes.Select(h => asm.Metadata.GetCustomAttribute(h))
.Where(ca => ca.GetAttributeType(asm.Metadata).GetFullTypeName(asm.Metadata).ToString() == "System.Runtime.CompilerServices.InternalsVisibleToAttribute");
var friendAssemblies = new HashSet<string>();
foreach (var attribute in attributes) {
string assemblyName = attribute.DecodeValue(typeProvider).FixedArguments[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)) {
var module = assembly.GetPEFileOrNull();
if (module == null)
continue;
if (AssemblyReferencesScopeType(module.Metadata, typeScope.Name, typeScope.Namespace))
yield return module;
}
}
}
}
bool AssemblyReferencesScopeType(MetadataReader metadata, string typeScopeName, string typeScopeNamespace)
{
bool hasRef = false;
foreach (var h in metadata.TypeReferences) {
var typeRef = metadata.GetTypeReference(h);
if (metadata.StringComparer.Equals(typeRef.Name, typeScopeName) && metadata.StringComparer.Equals(typeRef.Namespace, typeScopeNamespace)) {
hasRef = true;
break;
}
}
return hasRef;
}
IEnumerable<IEntity> RunAnalysisOn(PEFile module, ITypeDefinition type, AnalyzerContext context)
{
if (analyzer is IMethodBodyAnalyzer<T> methodAnalyzer) {
var reader = module.Reader;
foreach (var method in type.GetMethods(options: GetMemberOptions.IgnoreInheritedMembers)) {
if (!method.HasBody || method.MetadataToken.IsNil) continue;
var md = module.Metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
var body = reader.GetMethodBody(md.RelativeVirtualAddress);
foreach (var result in methodAnalyzer.Analyze(analyzedEntity, method, body, context))
yield return GetParentEntity(context, result);
}
foreach (var method in type.GetAccessors(options: GetMemberOptions.IgnoreInheritedMembers)) {
if (!method.HasBody || method.MetadataToken.IsNil) continue;
var md = module.Metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
var body = reader.GetMethodBody(md.RelativeVirtualAddress);
foreach (var result in methodAnalyzer.Analyze(analyzedEntity, method, body, context))
yield return GetParentEntity(context, result);
}
}
if (analyzer is ITypeDefinitionAnalyzer<T> typeDefinitionAnalyzer) {
foreach (var result in typeDefinitionAnalyzer.Analyze(analyzedEntity, type, context))
yield return GetParentEntity(context, result);
}
}
private IEntity GetParentEntity(AnalyzerContext context, IEntity entity)
{
if (entity.MetadataToken.Kind == HandleKind.MethodDefinition && !entity.MetadataToken.IsNil) {
var parentHandle = context.CodeMappingInfo.GetParentMethod((MethodDefinitionHandle)entity.MetadataToken);
var method = context.TypeSystem.ResolveAsMethod(parentHandle);
if (method != null) {
return method.AccessorOwner ?? method;
}
}
return entity;
}
}
}

0
ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs → ILSpy/Analyzers/TreeNodes/AnalyzedEventTreeNode.cs

28
ILSpy/TreeNodes/Analyzer/AnalyzedFieldTreeNode.cs → ILSpy/Analyzers/TreeNodes/AnalyzedFieldTreeNode.cs

@ -19,35 +19,35 @@ @@ -19,35 +19,35 @@
using System;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Analyzers;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
class AnalyzedFieldTreeNode : AnalyzerEntityTreeNode
{
readonly Decompiler.Metadata.PEFile module;
readonly FieldDefinitionHandle analyzedField;
readonly IField analyzedField;
public AnalyzedFieldTreeNode(Decompiler.Metadata.PEFile module, FieldDefinitionHandle analyzedField)
public AnalyzedFieldTreeNode(IField analyzedField)
{
if (analyzedField.IsNil)
throw new ArgumentNullException(nameof(analyzedField));
this.module = module;
this.analyzedField = analyzedField;
this.analyzedField = analyzedField ?? throw new ArgumentNullException(nameof(analyzedField));
this.LazyLoading = true;
}
public override object Icon => FieldTreeNode.GetIcon(new Decompiler.Metadata.FieldDefinition(module, analyzedField));
public override object Icon => FieldTreeNode.GetIcon(analyzedField);
public override object Text => Language.FieldToString(new Decompiler.Metadata.FieldDefinition(module, analyzedField), true, true);
public override object Text => Language.FieldToString(analyzedField, true, true);
protected override void LoadChildren()
{
this.Children.Add(new AnalyzedFieldAccessTreeNode(module, analyzedField, false));
var fd = module.Metadata.GetFieldDefinition(analyzedField);
if (!fd.HasFlag(System.Reflection.FieldAttributes.Literal))
this.Children.Add(new AnalyzedFieldAccessTreeNode(module, analyzedField, true));
foreach (var lazy in App.ExportProvider.GetExports<IAnalyzer<IField>>()) {
var analyzer = lazy.Value;
if (analyzer.Show(analyzedField)) {
this.Children.Add(new AnalyzerSearchTreeNode<IField>(analyzedField, analyzer));
}
}
}
public override Decompiler.Metadata.IMetadataEntity Member => new Decompiler.Metadata.FieldDefinition(module, analyzedField);
public override IEntity Member => analyzedField;
}
}

56
ILSpy/Analyzers/TreeNodes/AnalyzedMethodTreeNode.cs

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
// 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.Reflection;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Analyzers;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal class AnalyzedMethodTreeNode : AnalyzerEntityTreeNode
{
readonly IMethod analyzedMethod;
readonly string prefix;
public AnalyzedMethodTreeNode(IMethod analyzedMethod, string prefix = "")
{
this.analyzedMethod = analyzedMethod ?? throw new ArgumentNullException(nameof(analyzedMethod));
this.prefix = prefix;
this.LazyLoading = true;
}
public override object Icon => MethodTreeNode.GetIcon(analyzedMethod);
public override object Text => prefix + Language.MethodToString(analyzedMethod, true, true);
protected override void LoadChildren()
{
foreach (var lazy in App.ExportProvider.GetExports<IAnalyzer<IMethod>>()) {
var analyzer = lazy.Value;
if (analyzer.Show(analyzedMethod)) {
this.Children.Add(new AnalyzerSearchTreeNode<IMethod>(analyzedMethod, analyzer));
}
}
}
public override IEntity Member => analyzedMethod;
}
}

0
ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs → ILSpy/Analyzers/TreeNodes/AnalyzedPropertyTreeNode.cs

37
ILSpy/TreeNodes/Analyzer/AnalyzedTypeTreeNode.cs → ILSpy/Analyzers/TreeNodes/AnalyzedTypeTreeNode.cs

@ -17,23 +17,18 @@ @@ -17,23 +17,18 @@
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.Decompiler.Metadata;
using SRM = System.Reflection.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Analyzers;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal class AnalyzedTypeTreeNode : AnalyzerEntityTreeNode
{
readonly TypeDefinition analyzedType;
readonly SRM.TypeDefinition td;
readonly ITypeDefinition analyzedType;
public AnalyzedTypeTreeNode(TypeDefinition analyzedType)
public AnalyzedTypeTreeNode(ITypeDefinition analyzedType)
{
if (analyzedType.IsNil)
throw new ArgumentNullException(nameof(analyzedType));
this.analyzedType = analyzedType;
this.td = analyzedType.Module.Metadata.GetTypeDefinition(analyzedType.Handle);
this.analyzedType = analyzedType ?? throw new ArgumentNullException(nameof(analyzedType));
this.LazyLoading = true;
}
@ -43,22 +38,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -43,22 +38,14 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
protected override void LoadChildren()
{
//if (AnalyzedAttributeAppliedToTreeNode.CanShow(analyzedType))
// this.Children.Add(new AnalyzedAttributeAppliedToTreeNode(analyzedType));
if (AnalyzedTypeInstantiationsTreeNode.CanShow(analyzedType.Module.Metadata, analyzedType.Handle))
this.Children.Add(new AnalyzedTypeInstantiationsTreeNode(analyzedType.Module, analyzedType.Handle));
if (AnalyzedTypeUsedByTreeNode.CanShow(analyzedType.Module, analyzedType.Handle))
this.Children.Add(new AnalyzedTypeUsedByTreeNode(analyzedType.Module, analyzedType.Handle));
/*
if (AnalyzedTypeExposedByTreeNode.CanShow(analyzedType.Module.Metadata, analyzedType.Handle))
this.Children.Add(new AnalyzedTypeExposedByTreeNode(analyzedType));
if (AnalyzedTypeExtensionMethodsTreeNode.CanShow(analyzedType.Module.Metadata, analyzedType.Handle))
this.Children.Add(new AnalyzedTypeExtensionMethodsTreeNode(analyzedType));*/
foreach (var lazy in App.ExportProvider.GetExports<IAnalyzer<ITypeDefinition>>()) {
var analyzer = lazy.Value;
if (analyzer.Show(analyzedType)) {
this.Children.Add(new AnalyzerSearchTreeNode<ITypeDefinition>(analyzedType, analyzer));
}
}
}
public override IMetadataEntity Member => analyzedType;
public override IEntity Member => analyzedType;
}
}

20
ILSpy/ILSpy.csproj

@ -66,9 +66,14 @@ @@ -66,9 +66,14 @@
<ItemGroup>
<Compile Include="AboutPage.cs" />
<Compile Include="AnalyzerTreeView.cs">
<Compile Include="Analyzers\Builtin\MethodUsedByAnalyzer.cs" />
<Compile Include="Analyzers\Builtin\MethodUsesAnalyzer.cs" />
<Compile Include="Analyzers\IAnalyzer.cs" />
<Compile Include="Analyzers\AnalyzerTreeView.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Analyzers\TreeNodes\AnalyzedMethodTreeNode.cs" />
<Compile Include="Analyzers\TreeNodes\AnalyzedTypeTreeNode.cs" />
<Compile Include="App.xaml.cs">
<SubType>Code</SubType>
<DependentUpon>App.xaml</DependentUpon>
@ -189,12 +194,15 @@ @@ -189,12 +194,15 @@
<Compile Include="TextView\EditorCommands.cs" />
<Compile Include="TextView\FoldingCommands.cs" />
<Compile Include="TextView\XmlDocRenderer.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzeContextMenuEntry.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzerSearchTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzerTreeNode.cs" />
<Compile Include="Analyzers\AnalyzeContextMenuEntry.cs" />
<Compile Include="Analyzers\Builtin\FieldAccessAnalyzer.cs" />
<Compile Include="Analyzers\TreeNodes\AnalyzedFieldTreeNode.cs" />
<Compile Include="Analyzers\AnalyzerEntityTreeNode.cs" />
<Compile Include="Analyzers\AnalyzerSearchTreeNode.cs" />
<Compile Include="Analyzers\AnalyzerTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\Extensions.cs" />
<Compile Include="TreeNodes\Analyzer\RemoveAnalyzeContextMenuEntry.cs" />
<Compile Include="TreeNodes\Analyzer\ScopedWhereUsedAnalyzer.cs" />
<Compile Include="Analyzers\RemoveAnalyzeContextMenuEntry.cs" />
<Compile Include="Analyzers\ScopedWhereUsedAnalyzer.cs" />
<Compile Include="TreeNodes\BaseTypesEntryNode.cs" />
<Compile Include="TreeNodes\CopyFullyQualifiedNameContextMenuEntry.cs" />
<Compile Include="TreeNodes\DerivedTypesEntryNode.cs" />

3
ILSpy/Languages/CodeMappingInfo.cs

@ -4,9 +4,6 @@ using System.Diagnostics; @@ -4,9 +4,6 @@ using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.ILSpy
{

135
ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs

@ -1,135 +0,0 @@ @@ -1,135 +0,0 @@
// 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.Linq;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
[ExportContextMenuEntry(Header = "Analyze", Icon = "images/Search.png", Category = "Analyze", Order = 100)]
internal sealed class AnalyzeContextMenuEntry : IContextMenuEntry
{
public bool IsVisible(TextViewContext context)
{
if (context.TreeView is AnalyzerTreeView && context.SelectedTreeNodes != null && context.SelectedTreeNodes.All(n => n.Parent.IsRoot))
return false;
if (context.SelectedTreeNodes == null)
return context.Reference != null && IsValidReference(context.Reference.Reference);
return context.SelectedTreeNodes.All(n => n is IMemberTreeNode);
}
public bool IsEnabled(TextViewContext context)
{
if (context.SelectedTreeNodes == null)
return context.Reference != null && context.Reference.Reference is IEntity;
foreach (IMemberTreeNode node in context.SelectedTreeNodes) {
if (!IsValidReference(node.Member))
return false;
}
return true;
}
bool IsValidReference(object reference)
{
return reference is IEntity;
}
public void Execute(TextViewContext context)
{
if (context.SelectedTreeNodes != null) {
foreach (IMemberTreeNode node in context.SelectedTreeNodes) {
Analyze(node.Member);
}
} else if (context.Reference != null && IsValidReference(context.Reference.Reference)) {
Analyze(context.Reference.Reference);
}
}
public static void Analyze(object member)
{/*
switch (member) {
case IMetadataEntity entity:
switch (entity) {
case TypeDefinition td:
if (!td.IsNil)
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedTypeTreeNode(td));
break;
case FieldDefinition fd:
if (!fd.IsNil)
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedFieldTreeNode(fd.Module, fd.Handle));
break;
case MethodDefinition md:
if (!md.IsNil)
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedMethodTreeNode(md.Module, md.Handle));
break;
case PropertyDefinition pd:
//if (!pd.IsNil)
// AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedPropertyTreeNode(pd));
break;
case EventDefinition ed:
//if (!ed.IsNil)
// AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedEventTreeNode(ed));
break;
default:
throw new NotSupportedException();
}
break;
case TypeReference tr:
var resolved = tr.Handle.Resolve(new SimpleMetadataResolveContext(tr.Module));
if (!resolved.IsNil)
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedTypeTreeNode(resolved));
break;
case TypeSpecification ts:
resolved = ts.Handle.Resolve(new SimpleMetadataResolveContext(ts.Module));
if (!resolved.IsNil)
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedTypeTreeNode(resolved));
break;
case MemberReference mr:
var resolvedMember = mr.Handle.Resolve(new SimpleMetadataResolveContext(mr.Module));
if (!resolvedMember.IsNil) {
switch (resolvedMember) {
case FieldDefinition fd:
//AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedFieldTreeNode(fd));
break;
case MethodDefinition md:
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedMethodTreeNode(md.Module, md.Handle));
break;
default:
throw new NotSupportedException();
}
}
break;
case MethodSpecification ms:
resolvedMember = ms.Handle.Resolve(new SimpleMetadataResolveContext(ms.Module));
if (!resolvedMember.IsNil) {
switch (resolvedMember) {
case MethodDefinition md:
AnalyzerTreeView.Instance.ShowOrFocus(new AnalyzedMethodTreeNode(md.Module, md.Handle));
break;
default:
throw new NotSupportedException();
}
}
break;
}
*/
}
}
}

145
ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessTreeNode.cs

@ -1,145 +0,0 @@ @@ -1,145 +0,0 @@
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.TypeSystem;
using ILOpCode = System.Reflection.Metadata.ILOpCode;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
/// <summary>
/// Finds methods where this field is read or written.
/// </summary>
sealed class AnalyzedFieldAccessTreeNode : AnalyzerSearchTreeNode
{
readonly bool showWrites; // true: show writes; false: show read access
readonly Decompiler.Metadata.PEFile module;
readonly FieldDefinitionHandle analyzedField;
readonly FullTypeName analyzedTypeName;
readonly string analyzedFieldName;
Lazy<HashSet<Decompiler.Metadata.MethodDefinition>> foundMethods;
readonly object hashLock = new object();
public AnalyzedFieldAccessTreeNode(Decompiler.Metadata.PEFile module, FieldDefinitionHandle analyzedField, bool showWrites)
{
if (analyzedField.IsNil)
throw new ArgumentNullException(nameof(analyzedField));
this.module = module;
this.analyzedField = analyzedField;
var fd = module.Metadata.GetFieldDefinition(analyzedField);
this.analyzedTypeName = fd.GetDeclaringType().GetFullTypeName(module.Metadata);
this.analyzedFieldName = module.Metadata.GetString(fd.Name);
this.showWrites = showWrites;
}
public override object Text => showWrites ? "Assigned By" : "Read By";
protected override IEnumerable<AnalyzerTreeNode> FetchChildren(CancellationToken ct)
{
foundMethods = new Lazy<HashSet<Decompiler.Metadata.MethodDefinition>>(LazyThreadSafetyMode.ExecutionAndPublication);
var analyzer = new ScopedWhereUsedAnalyzer<AnalyzerTreeNode>(this.Language, module, analyzedField, provideTypeSystem: false, FindReferencesInType);
foreach (var child in analyzer.PerformAnalysis(ct).OrderBy(n => n.Text)) {
yield return child;
}
foundMethods = null;
}
private IEnumerable<AnalyzerTreeNode> FindReferencesInType(Decompiler.Metadata.PEFile module, TypeDefinitionHandle type, CodeMappingInfo codeMapping, IDecompilerTypeSystem typeSystem)
{
var td = module.Metadata.GetTypeDefinition(type);
foreach (var h in td.GetMethods()) {
bool found = false;
var method = module.Metadata.GetMethodDefinition(h);
if (!method.HasBody())
continue;
var blob = module.Reader.GetMethodBody(method.RelativeVirtualAddress).GetILReader();
while (!found && blob.RemainingBytes > 0) {
var opCode = blob.DecodeOpCode();
if (!CanBeReference(opCode)) {
blob.SkipOperand(opCode);
continue;
}
var member = MetadataTokens.EntityHandle(blob.ReadInt32());
switch (member.Kind) {
case HandleKind.FieldDefinition:
// check whether we're looking at the defining assembly
found = member == analyzedField && module == this.module;
break;
case HandleKind.MemberReference:
var mr = module.Metadata.GetMemberReference((MemberReferenceHandle)member);
// safety-check: should always be a field
if (mr.GetKind() != MemberReferenceKind.Field)
break;
if (!module.Metadata.StringComparer.Equals(mr.Name, analyzedFieldName))
break;
var typeName = mr.Parent.GetFullTypeName(module.Metadata);
found = typeName == analyzedTypeName;
break;
}
}
if (found) {
var md = new Decompiler.Metadata.MethodDefinition(module, h);
if (IsNewResult(md)) {
var node = new AnalyzedMethodTreeNode(module, h);
node.Language = this.Language;
yield return node;
}
}
}
}
bool CanBeReference(ILOpCode code)
{
switch (code) {
case ILOpCode.Ldfld:
case ILOpCode.Ldsfld:
return !showWrites;
case ILOpCode.Stfld:
case ILOpCode.Stsfld:
return showWrites;
case ILOpCode.Ldflda:
case ILOpCode.Ldsflda:
return true; // always show address-loading
default:
return false;
}
}
bool IsNewResult(Decompiler.Metadata.MethodDefinition method)
{
var hashSet = foundMethods.Value;
lock (hashLock) {
return hashSet.Add(method);
}
}
}
}

65
ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs

@ -1,65 +0,0 @@ @@ -1,65 +0,0 @@
// 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.Reflection;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal class AnalyzedMethodTreeNode : AnalyzerEntityTreeNode
{
readonly Decompiler.Metadata.PEFile module;
readonly MethodDefinitionHandle analyzedMethod;
readonly string prefix;
public AnalyzedMethodTreeNode(Decompiler.Metadata.PEFile module, MethodDefinitionHandle analyzedMethod, string prefix = "")
{
if (analyzedMethod.IsNil)
throw new ArgumentNullException(nameof(analyzedMethod));
this.module = module;
this.analyzedMethod = analyzedMethod;
this.prefix = prefix;
this.LazyLoading = true;
}
public override object Icon => MethodTreeNode.GetIcon(new Decompiler.Metadata.MethodDefinition(module, analyzedMethod));
public override object Text => prefix + Language.MethodToString(new Decompiler.Metadata.MethodDefinition(module, analyzedMethod), true, true);
protected override void LoadChildren()
{
if (module.Metadata.GetMethodDefinition(analyzedMethod).HasBody())
this.Children.Add(new AnalyzedMethodUsesTreeNode(module, analyzedMethod));
/*if (analyzedMethod.HasFlag(MethodAttributes.Virtual) && !(analyzedMethod.HasFlag(MethodAttributes.NewSlot) && analyzedMethod.HasFlag(MethodAttributes.Final)))
this.Children.Add(new AnalyzedVirtualMethodUsedByTreeNode(analyzedMethod));
else*/
this.Children.Add(new AnalyzedMethodUsedByTreeNode(module, analyzedMethod));
/*if (AnalyzedMethodOverridesTreeNode.CanShow(analyzedMethod))
this.Children.Add(new AnalyzedMethodOverridesTreeNode(analyzedMethod));
if (AnalyzedInterfaceMethodImplementedByTreeNode.CanShow(analyzedMethod))
this.Children.Add(new AnalyzedInterfaceMethodImplementedByTreeNode(analyzedMethod));*/
}
public override Decompiler.Metadata.IMetadataEntity Member => new Decompiler.Metadata.MethodDefinition(module, analyzedMethod);
}
}

96
ILSpy/TreeNodes/Analyzer/Extensions.cs

@ -1,96 +0,0 @@ @@ -1,96 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Threading.Tasks;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
public static class Helpers
{
public static TypeDefinitionHandle GetDeclaringType(this MethodDefinitionHandle method, MetadataReader metadata)
{
if (method.IsNil)
throw new ArgumentNullException(nameof(method));
return metadata.GetMethodDefinition(method).GetDeclaringType();
}
public static TypeDefinitionHandle GetDeclaringType(this FieldDefinitionHandle field, MetadataReader metadata)
{
if (field.IsNil)
throw new ArgumentNullException(nameof(field));
return metadata.GetFieldDefinition(field).GetDeclaringType();
}
public static TypeDefinitionHandle GetDeclaringType(this PropertyDefinitionHandle property, MetadataReader metadata)
{
if (property.IsNil)
throw new ArgumentNullException(nameof(property));
var accessor = metadata.GetPropertyDefinition(property).GetAccessors().GetAny();
return metadata.GetMethodDefinition(accessor).GetDeclaringType();
}
public static TypeDefinitionHandle GetDeclaringType(this EventDefinitionHandle @event, MetadataReader metadata)
{
if (@event.IsNil)
throw new ArgumentNullException(nameof(@event));
var accessor = metadata.GetEventDefinition(@event).GetAccessors().GetAny();
return metadata.GetMethodDefinition(accessor).GetDeclaringType();
}
public static bool IsSameMethod(MethodDefinitionHandle method, PEFile definitionModule, EntityHandle entity, PEFile entityModule)
{
if (method.IsNil || entity.IsNil)
return false;
var md = definitionModule.Metadata.GetMethodDefinition(method);
if (entity.Kind == HandleKind.MethodSpecification) {
var ms = entityModule.Metadata.GetMethodSpecification((MethodSpecificationHandle)entity);
entity = ms.Method;
}
switch (entity.Kind) {
case HandleKind.MethodDefinition:
return method == entity && definitionModule == entityModule;
case HandleKind.MemberReference:
var mr = entityModule.Metadata.GetMemberReference((MemberReferenceHandle)entity);
if (mr.GetKind() != MemberReferenceKind.Method)
return false;
if (!entityModule.Metadata.StringComparer.Equals(mr.Name, definitionModule.Metadata.GetString(md.Name)))
return false;
if (mr.Parent.Kind.IsTypeKind()) {
if (!IsSameType(md.GetDeclaringType(), definitionModule, mr.Parent, entityModule))
return false;
} else {
// TODO ...
}
return SignatureBlobComparer.EqualsMethodSignature(
definitionModule.Metadata.GetBlobReader(md.Signature),
entityModule.Metadata.GetBlobReader(mr.Signature),
definitionModule.Metadata, entityModule.Metadata);
default:
return false;
}
}
public static bool IsSameType(TypeDefinitionHandle type, PEFile module, EntityHandle entity, PEFile entityModule)
{
if (type.IsNil || entity.IsNil)
return false;
var td = module.Metadata.GetTypeDefinition(type);
if (entity.Kind == HandleKind.TypeSpecification) {
var ts = entityModule.Metadata.GetTypeSpecification((TypeSpecificationHandle)entity);
entity = ts.DecodeSignature(new Unspecializer(), default);
}
switch (entity.Kind) {
case HandleKind.TypeDefinition:
return type == entity && module == entityModule;
case HandleKind.TypeReference:
return type.GetFullTypeName(module.Metadata) == entity.GetFullTypeName(entityModule.Metadata);
default:
return false;
}
}
}
}

144
ILSpy/TreeNodes/Analyzer/Helpers.cs

@ -1,144 +0,0 @@ @@ -1,144 +0,0 @@
// 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.Linq;
using ICSharpCode.Decompiler;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
internal static class Helpers
{
public static bool IsSameType(TypeDefinition type, TypeReference typeRef)
{
// TODO: move it to a better place after adding support for more cases.
if (type == null)
throw new ArgumentNullException(nameof(type));
if (typeRef == null)
throw new ArgumentNullException(nameof(typeRef));
if (type == typeRef)
return true;
if (type.Name != typeRef.Name)
return false;
if (type.Namespace != typeRef.Namespace)
return false;
if (type.DeclaringType != null || typeRef.DeclaringType != null) {
if (type.DeclaringType == null || typeRef.DeclaringType == null)
return false;
if (!IsSameType(type.DeclaringType, typeRef.DeclaringType))
return false;
}
return true;
}
public static MemberReference GetOriginalCodeLocation(MemberReference member)
{
if (member is MethodDefinition)
return GetOriginalCodeLocation((MethodDefinition)member);
return member;
}
public static MethodDefinition GetOriginalCodeLocation(MethodDefinition method)
{
if (method.IsCompilerGenerated()) {
return FindMethodUsageInType(method.DeclaringType, method) ?? method;
}
var typeUsage = GetOriginalCodeLocation(method.DeclaringType);
return typeUsage ?? method;
}
/// <summary>
/// Given a compiler-generated type, returns the method where that type is used.
/// Used to detect the 'parent method' for a lambda/iterator/async state machine.
/// </summary>
public static MethodDefinition GetOriginalCodeLocation(TypeDefinition type)
{
if (type != null && type.DeclaringType != null && type.IsCompilerGenerated()) {
if (type.IsValueType) {
// Value types might not have any constructor; but they must be stored in a local var
// because 'initobj' (or 'call .ctor') expects a managed ref.
return FindVariableOfTypeUsageInType(type.DeclaringType, type);
} else {
MethodDefinition constructor = GetTypeConstructor(type);
if (constructor == null)
return null;
return FindMethodUsageInType(type.DeclaringType, constructor);
}
}
return null;
}
private static MethodDefinition GetTypeConstructor(TypeDefinition type)
{
return type.Methods.FirstOrDefault(method => !method.IsStatic && method.IsConstructor);
}
private static MethodDefinition FindMethodUsageInType(TypeDefinition type, MethodDefinition analyzedMethod)
{
string name = analyzedMethod.Name;
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 &&
IsSameType(analyzedMethod.DeclaringType, mr.DeclaringType) &&
mr.Resolve() == analyzedMethod) {
found = true;
break;
}
}
method.Body = null;
if (found)
return method;
}
return null;
}
private static MethodDefinition FindVariableOfTypeUsageInType(TypeDefinition type, TypeDefinition variableType)
{
foreach (MethodDefinition method in type.Methods) {
bool found = false;
if (!method.HasBody)
continue;
foreach (var v in method.Body.Variables) {
if (v.VariableType.ResolveWithinSameModule() == variableType) {
found = true;
break;
}
}
method.Body = null;
if (found)
return method;
}
return null;
}
}
}

350
ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs

@ -1,350 +0,0 @@ @@ -1,350 +0,0 @@
// 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.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
/// <summary>
/// Determines the accessibility domain of a member for where-used analysis.
/// </summary>
class ScopedWhereUsedAnalyzer<T>
{
readonly Language language;
readonly Decompiler.Metadata.PEFile assemblyScope;
readonly bool provideTypeSystem;
TypeDefinitionHandle typeScopeHandle;
TypeDefinition typeScope;
readonly Accessibility memberAccessibility = Accessibility.Public;
Accessibility typeAccessibility = Accessibility.Public;
readonly Func<Decompiler.Metadata.PEFile, TypeDefinitionHandle, CodeMappingInfo, IDecompilerTypeSystem, IEnumerable<T>> typeAnalysisFunction;
public ScopedWhereUsedAnalyzer(Language language, Decompiler.Metadata.PEFile module, TypeDefinitionHandle type, bool provideTypeSystem, Func<Decompiler.Metadata.PEFile, TypeDefinitionHandle, CodeMappingInfo, IDecompilerTypeSystem, IEnumerable<T>> typeAnalysisFunction)
{
this.language = language;
this.typeScopeHandle = type;
this.assemblyScope = module;
this.typeScope = module.Metadata.GetTypeDefinition(typeScopeHandle);
this.provideTypeSystem = provideTypeSystem;
this.typeAnalysisFunction = typeAnalysisFunction;
}
public ScopedWhereUsedAnalyzer(Language language, Decompiler.Metadata.PEFile module, MethodDefinitionHandle method, bool provideTypeSystem, Func<Decompiler.Metadata.PEFile, TypeDefinitionHandle, CodeMappingInfo, IDecompilerTypeSystem, IEnumerable<T>> typeAnalysisFunction)
: this(language, module, method.GetDeclaringType(module.Metadata), provideTypeSystem, typeAnalysisFunction)
{
this.memberAccessibility = GetMethodAccessibility(module.Metadata, method);
}
public ScopedWhereUsedAnalyzer(Language language, Decompiler.Metadata.PEFile module, PropertyDefinitionHandle property, bool provideTypeSystem, Func<Decompiler.Metadata.PEFile, TypeDefinitionHandle, CodeMappingInfo, IDecompilerTypeSystem, IEnumerable<T>> typeAnalysisFunction)
: this(language, module, property.GetDeclaringType(module.Metadata), provideTypeSystem, typeAnalysisFunction)
{
var pd = module.Metadata.GetPropertyDefinition(property);
var accessors = pd.GetAccessors();
Accessibility getterAccessibility = (accessors.Getter.IsNil) ? Accessibility.Private : GetMethodAccessibility(module.Metadata, accessors.Getter);
Accessibility setterAccessibility = (accessors.Setter.IsNil) ? Accessibility.Private : GetMethodAccessibility(module.Metadata, accessors.Setter);
this.memberAccessibility = (Accessibility)Math.Max((int)getterAccessibility, (int)setterAccessibility);
}
public ScopedWhereUsedAnalyzer(Language language, Decompiler.Metadata.PEFile module, EventDefinitionHandle eventDef, bool provideTypeSystem, Func<Decompiler.Metadata.PEFile, TypeDefinitionHandle, CodeMappingInfo, IDecompilerTypeSystem, IEnumerable<T>> typeAnalysisFunction)
: this(language, module, eventDef.GetDeclaringType(module.Metadata), provideTypeSystem, typeAnalysisFunction)
{
// we only have to check the accessibility of the the get method
// [CLS Rule 30: The accessibility of an event and of its accessors shall be identical.]
var ed = module.Metadata.GetEventDefinition(eventDef);
this.memberAccessibility = GetMethodAccessibility(module.Metadata, ed.GetAccessors().Adder);
}
public ScopedWhereUsedAnalyzer(Language language, Decompiler.Metadata.PEFile module, FieldDefinitionHandle field, bool provideTypeSystem, Func<Decompiler.Metadata.PEFile, TypeDefinitionHandle, CodeMappingInfo, IDecompilerTypeSystem, IEnumerable<T>> typeAnalysisFunction)
: this(language, module, field.GetDeclaringType(module.Metadata), provideTypeSystem, typeAnalysisFunction)
{
var fd = module.Metadata.GetFieldDefinition(field);
switch (fd.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;
}
}
Accessibility GetMethodAccessibility(MetadataReader metadata, MethodDefinitionHandle method)
{
Accessibility accessibility;
var methodInfo = metadata.GetMethodDefinition(method);
switch (methodInfo.Attributes & MethodAttributes.MemberAccessMask) {
case MethodAttributes.Private:
default:
accessibility = Accessibility.Private;
break;
case MethodAttributes.FamANDAssem:
accessibility = Accessibility.FamilyAndInternal;
break;
case MethodAttributes.Family:
accessibility = Accessibility.Family;
break;
case MethodAttributes.Assembly:
accessibility = Accessibility.Internal;
break;
case MethodAttributes.FamORAssem:
accessibility = Accessibility.FamilyOrInternal;
break;
case MethodAttributes.Public:
accessibility = Accessibility.Public;
break;
}
return accessibility;
}
public IEnumerable<T> PerformAnalysis(CancellationToken ct)
{
if (memberAccessibility == Accessibility.Private) {
return FindReferencesInTypeScope(ct);
}
DetermineTypeAccessibility();
if (typeAccessibility == Accessibility.Private) {
return FindReferencesInEnclosingTypeScope(ct);
}
if (memberAccessibility == Accessibility.Internal ||
memberAccessibility == Accessibility.FamilyAndInternal ||
typeAccessibility == Accessibility.Internal ||
typeAccessibility == Accessibility.FamilyAndInternal)
return FindReferencesInAssemblyAndFriends(ct);
return FindReferencesGlobal(ct);
}
void DetermineTypeAccessibility()
{
while (!typeScope.GetDeclaringType().IsNil) {
Accessibility accessibility = GetNestedTypeAccessibility(typeScope);
if ((int)typeAccessibility > (int)accessibility) {
typeAccessibility = accessibility;
if (typeAccessibility == Accessibility.Private)
return;
}
typeScopeHandle = typeScope.GetDeclaringType();
typeScope = assemblyScope.Metadata.GetTypeDefinition(typeScopeHandle);
}
if ((typeScope.Attributes & TypeAttributes.VisibilityMask) == TypeAttributes.NotPublic &&
((int)typeAccessibility > (int)Accessibility.Internal)) {
typeAccessibility = Accessibility.Internal;
}
}
static 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>
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(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(asm => FindReferencesInAssembly(asm, ct));
}
IEnumerable<T> FindReferencesInAssembly(Decompiler.Metadata.PEFile module, CancellationToken ct)
{
IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(module, module.GetAssemblyResolver()) : null;
var metadata = module.Metadata;
foreach (var type in TreeTraversal.PreOrder(metadata.TypeDefinitions, t => metadata.GetTypeDefinition(t).GetNestedTypes())) {
ct.ThrowIfCancellationRequested();
var codeMappingInfo = language.GetCodeMappingInfo(assemblyScope, type);
foreach (var result in typeAnalysisFunction(module, type, codeMappingInfo, ts)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<T> FindReferencesInTypeScope(CancellationToken ct)
{
IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(assemblyScope, assemblyScope.GetAssemblyResolver()) : null;
foreach (var type in TreeTraversal.PreOrder(typeScopeHandle, t => assemblyScope.Metadata.GetTypeDefinition(t).GetNestedTypes())) {
ct.ThrowIfCancellationRequested();
var codeMappingInfo = language.GetCodeMappingInfo(assemblyScope, type);
foreach (var result in typeAnalysisFunction(assemblyScope, type, codeMappingInfo, ts)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<T> FindReferencesInEnclosingTypeScope(CancellationToken ct)
{
IDecompilerTypeSystem ts = provideTypeSystem ? new DecompilerTypeSystem(assemblyScope, assemblyScope.GetAssemblyResolver()) : null;
var codeMappingInfo = language.GetCodeMappingInfo(assemblyScope, typeScope.GetDeclaringType());
foreach (var type in TreeTraversal.PreOrder(typeScope.GetDeclaringType(), t => assemblyScope.Metadata.GetTypeDefinition(t).GetNestedTypes())) {
ct.ThrowIfCancellationRequested();
foreach (var result in typeAnalysisFunction(assemblyScope, type, codeMappingInfo, ts)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<Decompiler.Metadata.PEFile> GetReferencingAssemblies(Decompiler.Metadata.PEFile asm, CancellationToken ct)
{
yield return asm;
string typeScopeNamespace = asm.Metadata.GetString(typeScope.Namespace);
string typeScopeName = asm.Metadata.GetString(typeScope.Name);
IEnumerable<LoadedAssembly> assemblies = MainWindow.Instance.CurrentAssemblyList.GetAssemblies().Where(assy => assy.GetPEFileOrNull()?.IsAssembly == true);
foreach (var assembly in assemblies) {
ct.ThrowIfCancellationRequested();
bool found = false;
var module = assembly.GetPEFileOrNull();
if (module == null)
continue;
var resolver = assembly.GetAssemblyResolver();
var metadata = module.Metadata;
foreach (var reference in module.AssemblyReferences) {
using (LoadedAssembly.DisableAssemblyLoad()) {
if (resolver.Resolve(reference) == asm) {
found = true;
break;
}
}
}
if (found && AssemblyReferencesScopeType(metadata, typeScopeName, typeScopeNamespace))
yield return module;
}
}
IEnumerable<Decompiler.Metadata.PEFile> GetAssemblyAndAnyFriends(Decompiler.Metadata.PEFile asm, CancellationToken ct)
{
yield return asm;
var metadata = asm.Metadata;
string typeScopeNamespace = metadata.GetString(typeScope.Namespace);
string typeScopeName = metadata.GetString(typeScope.Name);
var typeProvider = Decompiler.Metadata.MetadataExtensions.MinimalAttributeTypeProvider;
var attributes = metadata.CustomAttributes.Select(h => metadata.GetCustomAttribute(h)).Where(ca => ca.GetAttributeType(metadata).GetFullTypeName(metadata).ToString() == "System.Runtime.CompilerServices.InternalsVisibleToAttribute");
var friendAssemblies = new HashSet<string>();
foreach (var attribute in attributes) {
string assemblyName = attribute.DecodeValue(typeProvider).FixedArguments[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)) {
var module = assembly.GetPEFileOrNull();
if (module == null)
continue;
if (AssemblyReferencesScopeType(module.Metadata, typeScopeName, typeScopeNamespace))
yield return module;
}
}
}
}
bool AssemblyReferencesScopeType(MetadataReader metadata, string typeScopeName, string typeScopeNamespace)
{
bool hasRef = false;
foreach (var h in metadata.TypeReferences) {
var typeRef = metadata.GetTypeReference(h);
if (metadata.StringComparer.Equals(typeRef.Name, typeScopeName) && metadata.StringComparer.Equals(typeRef.Namespace, typeScopeNamespace)) {
hasRef = true;
break;
}
}
return hasRef;
}
}
}

4
ILSpy/TreeNodes/ThreadingSupport.cs

@ -65,8 +65,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -65,8 +65,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
result.Add(child);
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action<SharpTreeNode>(
delegate (SharpTreeNode newChild) {
// don't access "child" here the
// background thread might already be running the next loop iteration
// don't access "child" here,
// the background thread might already be running the next loop iteration
if (loadChildrenTask == thisTask) {
node.Children.Insert(node.Children.Count - 1, newChild);
}

Loading…
Cancel
Save