mirror of https://github.com/icsharpcode/ILSpy.git
25 changed files with 841 additions and 1013 deletions
@ -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(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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; } |
||||||
|
} |
||||||
|
} |
@ -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,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; |
||||||
|
} |
||||||
|
} |
@ -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; |
|
||||||
} |
|
||||||
*/ |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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); |
|
||||||
} |
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -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; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue