Browse Source

Merge pull request #2643 from icsharpcode/analyzer-opt

pull/2650/head
Siegfried Pammer 4 years ago committed by GitHub
parent
commit
5dd0fe2fb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  3. 39
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs
  4. 35
      ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs
  5. 154
      ICSharpCode.Decompiler/Metadata/FindTypeDecoder.cs
  6. 54
      ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs
  7. 15
      ICSharpCode.Decompiler/Metadata/PEFile.cs
  8. 16
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  9. 160
      ICSharpCode.Decompiler/SRMExtensions.cs
  10. 15
      ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs
  11. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs
  12. 6
      ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs
  13. 7
      ILSpy/Analyzers/AnalyzerContext.cs
  14. 79
      ILSpy/Analyzers/AnalyzerHelpers.cs
  15. 37
      ILSpy/Analyzers/AnalyzerScope.cs
  16. 7
      ILSpy/Analyzers/AnalyzerSearchTreeNode.cs
  17. 60
      ILSpy/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs
  18. 158
      ILSpy/Analyzers/Builtin/FindTypeInAttributeDecoder.cs
  19. 43
      ILSpy/Analyzers/Builtin/MethodUsedByAnalyzer.cs
  20. 453
      ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs
  21. 56
      ILSpy/AssemblyList.cs
  22. 199
      ILSpy/AssemblyListSnapshot.cs
  23. 2
      ILSpy/Languages/Language.cs
  24. 90
      ILSpy/LoadedAssembly.cs
  25. 5
      ILSpy/LoadedAssemblyExtensions.cs

16
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -633,7 +633,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -633,7 +633,7 @@ namespace ICSharpCode.Decompiler.CSharp
/// </summary>
public static CodeMappingInfo GetCodeMappingInfo(PEFile module, EntityHandle member)
{
var declaringType = member.GetDeclaringType(module.Metadata);
var declaringType = (TypeDefinitionHandle)member.GetDeclaringType(module.Metadata);
if (declaringType.IsNil && member.Kind == HandleKind.TypeDefinition)
{
@ -835,20 +835,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -835,20 +835,8 @@ namespace ICSharpCode.Decompiler.CSharp
return (TypeDefinitionHandle)memberRef.Parent;
case HandleKind.TypeSpecification:
var ts = module.Metadata.GetTypeSpecification((TypeSpecificationHandle)memberRef.Parent);
if (ts.Signature.IsNil)
return default;
// Do a quick scan using BlobReader
var signature = module.Metadata.GetBlobReader(ts.Signature);
// When dealing with FSM implementations, we can safely assume that if it's a type spec,
// it must be a generic type instance.
if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance)
return default;
// Skip over the rawTypeKind: value type or class
var rawTypeKind = signature.ReadCompressedInteger();
if (rawTypeKind < 17 || rawTypeKind > 18)
return default;
// Only read the generic type, ignore the type arguments
var genericType = signature.ReadTypeHandle();
var genericType = ts.GetGenericType(module.Metadata);
// Again, we assume this is a type def, because we are only looking at nested types
if (genericType.Kind != HandleKind.TypeDefinition)
return default;

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -112,6 +112,7 @@ @@ -112,6 +112,7 @@
<Compile Include="IL\Transforms\PatternMatchingTransform.cs" />
<Compile Include="IL\Transforms\RemoveInfeasiblePathTransform.cs" />
<Compile Include="Instrumentation\DecompilerEventSource.cs" />
<Compile Include="Metadata\FindTypeDecoder.cs" />
<Compile Include="Metadata\ReferenceLoadInfo.cs" />
<Compile Include="Semantics\OutVarResolveResult.cs" />
<Compile Include="SingleFileBundle.cs" />

39
ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

@ -827,7 +827,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -827,7 +827,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (!IsLocalFunctionMethod(module, method, context))
continue;
var md = metadata.GetMethodDefinition(method);
if (md.DecodeSignature(new FindTypeDecoder(typeHandle), default).ParameterTypes.Any())
if (md.DecodeSignature(new FindTypeDecoder(typeHandle, module), default).ParameterTypes.Any())
return true;
}
@ -852,43 +852,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -852,43 +852,6 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return match.Success;
}
struct FindTypeDecoder : ISignatureTypeProvider<bool, Unit>
{
readonly TypeDefinitionHandle handle;
public FindTypeDecoder(TypeDefinitionHandle handle)
{
this.handle = handle;
}
public bool GetArrayType(bool elementType, ArrayShape shape) => elementType;
public bool GetByReferenceType(bool elementType) => elementType;
public bool GetFunctionPointerType(MethodSignature<bool> signature) => false;
public bool GetGenericInstantiation(bool genericType, ImmutableArray<bool> typeArguments) => genericType;
public bool GetGenericMethodParameter(Unit genericContext, int index) => false;
public bool GetGenericTypeParameter(Unit genericContext, int index) => false;
public bool GetModifiedType(bool modifier, bool unmodifiedType, bool isRequired) => unmodifiedType;
public bool GetPinnedType(bool elementType) => elementType;
public bool GetPointerType(bool elementType) => elementType;
public bool GetPrimitiveType(PrimitiveTypeCode typeCode) => false;
public bool GetSZArrayType(bool elementType) => false;
public bool GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
return this.handle == handle;
}
public bool GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
return false;
}
public bool GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext);
}
}
class FindRefStructParameters : ISignatureTypeProvider<TypeDefinitionHandle, Unit>
{
public readonly List<TypeDefinitionHandle> RefStructTypes = new List<TypeDefinitionHandle>();

35
ICSharpCode.Decompiler/Metadata/AssemblyReferences.cs

@ -222,29 +222,40 @@ namespace ICSharpCode.Decompiler.Metadata @@ -222,29 +222,40 @@ namespace ICSharpCode.Decompiler.Metadata
public bool IsWindowsRuntime => (entry.Flags & AssemblyFlags.WindowsRuntime) != 0;
public bool IsRetargetable => (entry.Flags & AssemblyFlags.Retargetable) != 0;
string? name;
string? fullName;
public string Name {
get {
try
{
return Metadata.GetString(entry.Name);
}
catch (BadImageFormatException)
if (name == null)
{
return $"AR:{Handle}";
try
{
name = Metadata.GetString(entry.Name);
}
catch (BadImageFormatException)
{
name = $"AR:{Handle}";
}
}
return name;
}
}
public string FullName {
get {
try
{
return entry.GetFullAssemblyName(Metadata);
}
catch (BadImageFormatException)
if (fullName == null)
{
return $"fullname(AR:{Handle})";
try
{
fullName = entry.GetFullAssemblyName(Metadata);
}
catch (BadImageFormatException)
{
fullName = $"fullname(AR:{Handle})";
}
}
return fullName;
}
}

154
ICSharpCode.Decompiler/Metadata/FindTypeDecoder.cs

@ -0,0 +1,154 @@ @@ -0,0 +1,154 @@
// Copyright (c) 2022 Siegfried Pammer
//
// 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.
#nullable enable
using System;
using System.Collections.Immutable;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.Metadata
{
public class FindTypeDecoder : ISignatureTypeProvider<bool, Unit>
{
readonly PEFile declaringModule;
readonly MetadataModule? currentModule;
readonly TypeDefinitionHandle handle;
readonly string? typeName;
readonly string? namespaceName;
readonly PrimitiveTypeCode primitiveType;
/// <summary>
/// Constructs a FindTypeDecoder that finds uses of a specific type-definition handle.
/// This assumes that the module we are search in is the same as the module containing the type-definiton.
/// </summary>
internal FindTypeDecoder(TypeDefinitionHandle handle, PEFile declaringModule)
{
this.handle = handle;
this.declaringModule = declaringModule;
this.primitiveType = 0;
this.currentModule = null;
}
/// <summary>
/// Constructs a FindTypeDecoder that can be used to find <paramref name="type"/> in signatures from <paramref name="currentModule"/>.
/// </summary>
public FindTypeDecoder(MetadataModule currentModule, ITypeDefinition type)
{
this.currentModule = currentModule;
this.declaringModule = type.ParentModule.PEFile ?? throw new InvalidOperationException("Cannot use MetadataModule without PEFile as context.");
this.handle = (TypeDefinitionHandle)type.MetadataToken;
this.primitiveType = type.KnownTypeCode == KnownTypeCode.None ? 0 : type.KnownTypeCode.ToPrimitiveTypeCode();
this.typeName = type.MetadataName;
this.namespaceName = type.Namespace;
}
public bool GetArrayType(bool elementType, ArrayShape shape) => elementType;
public bool GetByReferenceType(bool elementType) => elementType;
public bool GetFunctionPointerType(MethodSignature<bool> signature)
{
return AnyInMethodSignature(signature);
}
public static bool AnyInMethodSignature(MethodSignature<bool> signature)
{
if (signature.ReturnType)
return true;
foreach (bool type in signature.ParameterTypes)
{
if (type)
return true;
}
return false;
}
public bool GetGenericInstantiation(bool genericType, ImmutableArray<bool> typeArguments)
{
if (genericType)
return true;
foreach (bool ta in typeArguments)
{
if (ta)
return true;
}
return false;
}
public bool GetGenericMethodParameter(Unit genericContext, int index) => false;
public bool GetGenericTypeParameter(Unit genericContext, int index) => false;
public bool GetModifiedType(bool modifier, bool unmodifiedType, bool isRequired) => unmodifiedType || modifier;
public bool GetPinnedType(bool elementType) => elementType;
public bool GetPointerType(bool elementType) => elementType;
public bool GetPrimitiveType(PrimitiveTypeCode typeCode)
{
return typeCode == primitiveType;
}
public bool GetSZArrayType(bool elementType) => elementType;
public bool GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
return this.handle == handle && reader == declaringModule.Metadata;
}
public bool GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
if (currentModule == null || typeName == null || namespaceName == null)
return false;
var tr = reader.GetTypeReference(handle);
if (!reader.StringComparer.Equals(tr.Name, typeName))
return false;
if (!((tr.Namespace.IsNil && namespaceName.Length == 0) || reader.StringComparer.Equals(tr.Namespace, namespaceName)))
return false;
var t = currentModule.ResolveType(handle, default);
var td = t.GetDefinition();
if (td == null)
return false;
return td.MetadataToken == this.handle && td.ParentModule.PEFile == declaringModule;
}
public bool GetTypeFromSpecification(MetadataReader reader, Unit genericContext, TypeSpecificationHandle handle, byte rawTypeKind)
{
return reader.GetTypeSpecification(handle).DecodeSignature(this, genericContext);
}
public bool GetTypeFromEntity(MetadataReader reader, EntityHandle handle, Unit genericContext = default, byte rawTypeKind = 0)
{
switch (handle.Kind)
{
case HandleKind.TypeReference:
return GetTypeFromReference(reader, (TypeReferenceHandle)handle, rawTypeKind);
case HandleKind.TypeDefinition:
return GetTypeFromDefinition(reader, (TypeDefinitionHandle)handle, rawTypeKind);
case HandleKind.TypeSpecification:
return GetTypeFromSpecification(reader, genericContext, (TypeSpecificationHandle)handle, rawTypeKind);
default:
return false;
}
}
}
}

54
ICSharpCode.Decompiler/Metadata/MetadataExtensions.cs

@ -94,25 +94,39 @@ namespace ICSharpCode.Decompiler.Metadata @@ -94,25 +94,39 @@ namespace ICSharpCode.Decompiler.Metadata
public static string GetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader)
{
string publicKey = "null";
if (!reference.PublicKeyOrToken.IsNil)
StringBuilder builder = new StringBuilder();
builder.Append(reader.GetString(reference.Name));
builder.Append(", Version=");
builder.Append(reference.Version);
builder.Append(", Culture=");
if (reference.Culture.IsNil)
{
if ((reference.Flags & AssemblyFlags.PublicKey) != 0)
{
publicKey = CalculatePublicKeyToken(reference.PublicKeyOrToken, reader);
}
else
{
publicKey = reader.GetBlobBytes(reference.PublicKeyOrToken).ToHexString(8);
}
builder.Append("neutral");
}
else
{
builder.Append(reader.GetString(reference.Culture));
}
if (reference.PublicKeyOrToken.IsNil)
{
builder.Append(", PublicKeyToken=null");
}
else if ((reference.Flags & AssemblyFlags.PublicKey) != 0)
{
builder.Append(", PublicKeyToken=");
builder.Append(CalculatePublicKeyToken(reference.PublicKeyOrToken, reader));
}
else
{
builder.Append(", PublicKeyToken=");
builder.AppendHexString(reader.GetBlobReader(reference.PublicKeyOrToken));
}
string properties = "";
if ((reference.Flags & AssemblyFlags.Retargetable) != 0)
properties = ", Retargetable=true";
return $"{reader.GetString(reference.Name)}, " +
$"Version={reference.Version}, " +
$"Culture={(reference.Culture.IsNil ? "neutral" : reader.GetString(reference.Culture))}, " +
$"PublicKeyToken={publicKey}{properties}";
{
builder.Append(", Retargetable=true");
}
return builder.ToString();
}
public static bool TryGetFullAssemblyName(this SRM.AssemblyReference reference, MetadataReader reader, out string assemblyName)
@ -140,6 +154,14 @@ namespace ICSharpCode.Decompiler.Metadata @@ -140,6 +154,14 @@ namespace ICSharpCode.Decompiler.Metadata
return sb.ToString();
}
public static void AppendHexString(this StringBuilder builder, BlobReader reader)
{
for (int i = 0; i < reader.Length; i++)
{
builder.AppendFormat("{0:x2}", reader.ReadByte());
}
}
public static string ToHexString(this BlobReader reader)
{
StringBuilder sb = new StringBuilder(reader.Length * 3);

15
ICSharpCode.Decompiler/Metadata/PEFile.cs

@ -128,7 +128,20 @@ namespace ICSharpCode.Decompiler.Metadata @@ -128,7 +128,20 @@ namespace ICSharpCode.Decompiler.Metadata
}
}
public ImmutableArray<AssemblyReference> AssemblyReferences => Metadata.AssemblyReferences.Select(r => new AssemblyReference(this, r)).ToImmutableArray();
ImmutableArray<AssemblyReference> assemblyReferences;
public ImmutableArray<AssemblyReference> AssemblyReferences {
get {
var value = assemblyReferences;
if (value.IsDefault)
{
value = Metadata.AssemblyReferences.Select(r => new AssemblyReference(this, r)).ToImmutableArray();
assemblyReferences = value;
}
return value;
}
}
public ImmutableArray<Resource> Resources => GetResources().ToImmutableArray();
IEnumerable<Resource> GetResources()

16
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -676,7 +676,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -676,7 +676,7 @@ namespace ICSharpCode.Decompiler.Metadata
{
var gac = Path.Combine(gac_paths[i], gacs[j]);
var file = GetAssemblyFile(reference, prefixes[i], gac);
if (Directory.Exists(gac) && File.Exists(file))
if (File.Exists(file))
return file;
}
}
@ -689,16 +689,10 @@ namespace ICSharpCode.Decompiler.Metadata @@ -689,16 +689,10 @@ namespace ICSharpCode.Decompiler.Metadata
var gac_folder = new StringBuilder()
.Append(prefix)
.Append(reference.Version);
if (reference.PublicKeyToken != null)
{
gac_folder.Append("__");
for (int i = 0; i < reference.PublicKeyToken.Length; i++)
gac_folder.Append(reference.PublicKeyToken[i].ToString("x2"));
}
return Path.Combine(
Path.Combine(
Path.Combine(gac, reference.Name), gac_folder.ToString()),
reference.Name + ".dll");
gac_folder.Append("__");
for (int i = 0; i < reference.PublicKeyToken!.Length; i++)
gac_folder.Append(reference.PublicKeyToken[i].ToString("x2"));
return Path.Combine(gac, reference.Name, gac_folder.ToString(), reference.Name + ".dll");
}
/// <summary>

160
ICSharpCode.Decompiler/SRMExtensions.cs

@ -140,31 +140,61 @@ namespace ICSharpCode.Decompiler @@ -140,31 +140,61 @@ namespace ICSharpCode.Decompiler
return accessors.Raiser;
}
public static TypeDefinitionHandle GetDeclaringType(this EntityHandle entity, MetadataReader metadata)
public static EntityHandle GetGenericType(this in TypeSpecification ts, MetadataReader metadata)
{
if (ts.Signature.IsNil)
return default;
// Do a quick scan using BlobReader
var signature = metadata.GetBlobReader(ts.Signature);
// When dealing with FSM implementations, we can safely assume that if it's a type spec,
// it must be a generic type instance.
if (signature.ReadByte() != (byte)SignatureTypeCode.GenericTypeInstance)
return default;
// Skip over the rawTypeKind: value type or class
var rawTypeKind = signature.ReadCompressedInteger();
if (rawTypeKind < 17 || rawTypeKind > 18)
return default;
// Only read the generic type, ignore the type arguments
return signature.ReadTypeHandle();
}
public static EntityHandle GetDeclaringType(this EntityHandle entity, MetadataReader metadata)
{
switch (entity.Kind)
{
case HandleKind.TypeDefinition:
var td = metadata.GetTypeDefinition((TypeDefinitionHandle)entity);
return td.GetDeclaringType();
case HandleKind.TypeReference:
var tr = metadata.GetTypeReference((TypeReferenceHandle)entity);
return tr.GetDeclaringType();
case HandleKind.TypeSpecification:
var ts = metadata.GetTypeSpecification((TypeSpecificationHandle)entity);
return ts.GetGenericType(metadata).GetDeclaringType(metadata);
case HandleKind.FieldDefinition:
var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)entity);
return fd.GetDeclaringType();
case HandleKind.MethodDefinition:
var md = metadata.GetMethodDefinition((MethodDefinitionHandle)entity);
return md.GetDeclaringType();
case HandleKind.MemberReference:
var mr = metadata.GetMemberReference((MemberReferenceHandle)entity);
return mr.Parent;
case HandleKind.EventDefinition:
var ed = metadata.GetEventDefinition((EventDefinitionHandle)entity);
return metadata.GetMethodDefinition(ed.GetAccessors().GetAny()).GetDeclaringType();
case HandleKind.PropertyDefinition:
var pd = metadata.GetPropertyDefinition((PropertyDefinitionHandle)entity);
return metadata.GetMethodDefinition(pd.GetAccessors().GetAny()).GetDeclaringType();
case HandleKind.MethodSpecification:
var ms = metadata.GetMethodSpecification((MethodSpecificationHandle)entity);
return ms.Method.GetDeclaringType(metadata);
default:
throw new ArgumentOutOfRangeException();
}
}
public static TypeReferenceHandle GetDeclaringType(this TypeReference tr)
public static TypeReferenceHandle GetDeclaringType(this in TypeReference tr)
{
switch (tr.ResolutionScope.Kind)
{
@ -195,15 +225,133 @@ namespace ICSharpCode.Decompiler @@ -195,15 +225,133 @@ namespace ICSharpCode.Decompiler
public static bool IsKnownType(this EntityHandle handle, MetadataReader reader,
KnownTypeCode knownType)
{
return !handle.IsNil
&& GetFullTypeName(handle, reader) == KnownTypeReference.Get(knownType).TypeName;
return IsKnownType(handle, reader, KnownTypeReference.Get(knownType).TypeName);
}
internal static bool IsKnownType(this EntityHandle handle, MetadataReader reader,
KnownAttribute knownType)
{
return !handle.IsNil
&& GetFullTypeName(handle, reader) == knownType.GetTypeName();
return IsKnownType(handle, reader, knownType.GetTypeName());
}
private static bool IsKnownType(EntityHandle handle, MetadataReader reader, TopLevelTypeName knownType)
{
if (handle.IsNil)
return false;
StringHandle nameHandle, namespaceHandle;
switch (handle.Kind)
{
case HandleKind.TypeReference:
var tr = reader.GetTypeReference((TypeReferenceHandle)handle);
// ignore exported and nested types
if (tr.ResolutionScope.IsNil || tr.ResolutionScope.Kind == HandleKind.TypeReference)
return false;
nameHandle = tr.Name;
namespaceHandle = tr.Namespace;
break;
case HandleKind.TypeDefinition:
var td = reader.GetTypeDefinition((TypeDefinitionHandle)handle);
if (td.IsNested)
return false;
nameHandle = td.Name;
namespaceHandle = td.Namespace;
break;
case HandleKind.TypeSpecification:
var ts = reader.GetTypeSpecification((TypeSpecificationHandle)handle);
var blob = reader.GetBlobReader(ts.Signature);
return SignatureIsKnownType(reader, knownType, ref blob);
default:
return false;
}
if (knownType.TypeParameterCount == 0)
{
if (!reader.StringComparer.Equals(nameHandle, knownType.Name))
return false;
}
else
{
string name = reader.GetString(nameHandle);
name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name, out int typeParameterCount);
if (typeParameterCount != knownType.TypeParameterCount || name != knownType.Name)
return false;
}
if (namespaceHandle.IsNil)
{
return knownType.Namespace.Length == 0;
}
else
{
return reader.StringComparer.Equals(namespaceHandle, knownType.Namespace);
}
}
private static bool SignatureIsKnownType(MetadataReader reader, TopLevelTypeName knownType, ref BlobReader blob)
{
if (!blob.TryReadCompressedInteger(out int typeCode))
return false;
switch (typeCode)
{
case 0x1: // ELEMENT_TYPE_VOID
return knownType.IsKnownType(KnownTypeCode.Void);
case 0x2: // ELEMENT_TYPE_BOOLEAN
return knownType.IsKnownType(KnownTypeCode.Boolean);
case 0x3: // ELEMENT_TYPE_CHAR
return knownType.IsKnownType(KnownTypeCode.Char);
case 0x4: // ELEMENT_TYPE_I1
return knownType.IsKnownType(KnownTypeCode.SByte);
case 0x5: // ELEMENT_TYPE_U1
return knownType.IsKnownType(KnownTypeCode.Byte);
case 0x6: // ELEMENT_TYPE_I2
return knownType.IsKnownType(KnownTypeCode.Int16);
case 0x7: // ELEMENT_TYPE_U2
return knownType.IsKnownType(KnownTypeCode.UInt16);
case 0x8: // ELEMENT_TYPE_I4
return knownType.IsKnownType(KnownTypeCode.Int32);
case 0x9: // ELEMENT_TYPE_U4
return knownType.IsKnownType(KnownTypeCode.UInt32);
case 0xA: // ELEMENT_TYPE_I8
return knownType.IsKnownType(KnownTypeCode.Int64);
case 0xB: // ELEMENT_TYPE_U8
return knownType.IsKnownType(KnownTypeCode.UInt64);
case 0xC: // ELEMENT_TYPE_R4
return knownType.IsKnownType(KnownTypeCode.Single);
case 0xD: // ELEMENT_TYPE_R8
return knownType.IsKnownType(KnownTypeCode.Double);
case 0xE: // ELEMENT_TYPE_STRING
return knownType.IsKnownType(KnownTypeCode.String);
case 0x16: // ELEMENT_TYPE_TYPEDBYREF
return knownType.IsKnownType(KnownTypeCode.TypedReference);
case 0x18: // ELEMENT_TYPE_I
return knownType.IsKnownType(KnownTypeCode.IntPtr);
case 0x19: // ELEMENT_TYPE_U
return knownType.IsKnownType(KnownTypeCode.UIntPtr);
case 0x1C: // ELEMENT_TYPE_OBJECT
return knownType.IsKnownType(KnownTypeCode.Object);
case 0xF: // ELEMENT_TYPE_PTR
case 0x10: // ELEMENT_TYPE_BYREF
case 0x45: // ELEMENT_TYPE_PINNED
case 0x1D: // ELEMENT_TYPE_SZARRAY
case 0x1B: // ELEMENT_TYPE_FNPTR
case 0x14: // ELEMENT_TYPE_ARRAY
return false;
case 0x1F: // ELEMENT_TYPE_CMOD_REQD
case 0x20: // ELEMENT_TYPE_CMOD_OPT
// modifier
blob.ReadTypeHandle(); // skip modifier
return SignatureIsKnownType(reader, knownType, ref blob);
case 0x15: // ELEMENT_TYPE_GENERICINST
// generic type
return SignatureIsKnownType(reader, knownType, ref blob);
case 0x13: // ELEMENT_TYPE_VAR
case 0x1E: // ELEMENT_TYPE_MVAR
// index
return false;
case 0x11: // ELEMENT_TYPE_VALUETYPE
case 0x12: // ELEMENT_TYPE_CLASS
return IsKnownType(blob.ReadTypeHandle(), reader, knownType);
default:
return false;
}
}
public static FullTypeName GetFullTypeName(this TypeSpecificationHandle handle, MetadataReader reader)

15
ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs

@ -43,9 +43,9 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -43,9 +43,9 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// <summary>
/// For enums: returns the underlying primitive type.
/// For all other types: returns <see cref="SpecialType.UnknownType"/>.
/// For all other types: returns <see langword="null"/>.
/// </summary>
IType EnumUnderlyingType { get; }
IType? EnumUnderlyingType { get; }
/// <summary>
/// For structs: returns whether this is a readonly struct.
@ -58,6 +58,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -58,6 +58,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
FullTypeName FullTypeName { get; }
/// <summary>
/// Gets the short type name as stored in metadata.
/// That is, the short type name including the generic arity (`N) appended.
/// </summary>
/// <remarks>
/// "Int32" for int
/// "List`1" for List&lt;T&gt;
/// "List`1" for List&lt;string&gt;
/// </remarks>
string MetadataName { get; }
/// <summary>
/// Gets/Sets the declaring type (incl. type arguments, if any).
/// This property will return null for top-level types.

3
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs

@ -70,6 +70,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -70,6 +70,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var td = metadata.GetTypeDefinition(handle);
this.attributes = td.Attributes;
this.fullTypeName = td.GetFullTypeName(metadata);
this.MetadataName = metadata.GetString(td.Name);
// Find DeclaringType + KnownTypeCode:
if (fullTypeName.IsNested)
{
@ -349,7 +350,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -349,7 +350,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
}
public EntityHandle MetadataToken => handle;
public string MetadataName { get; }
public FullTypeName FullTypeName => fullTypeName;
public string Name => fullTypeName.Name;

6
ICSharpCode.Decompiler/TypeSystem/Implementation/MinimalCorlib.cs

@ -148,7 +148,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -148,7 +148,9 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
{
this.corlib = corlib;
this.typeCode = typeCode;
this.typeKind = KnownTypeReference.Get(typeCode).typeKind;
KnownTypeReference ktr = KnownTypeReference.Get(typeCode);
this.typeKind = ktr.typeKind;
this.MetadataName = ktr.Name + (ktr.TypeParameterCount > 0 ? "`" + ktr.TypeParameterCount : "");
}
IReadOnlyList<ITypeDefinition> ITypeDefinition.NestedTypes => EmptyList<ITypeDefinition>.Instance;
@ -164,6 +166,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -164,6 +166,8 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
public FullTypeName FullTypeName => KnownTypeReference.Get(typeCode).TypeName;
public string MetadataName { get; }
ITypeDefinition IEntity.DeclaringTypeDefinition => null;
IType ITypeDefinition.DeclaringType => null;
IType IType.DeclaringType => null;

7
ILSpy/Analyzers/AnalyzerContext.cs

@ -43,6 +43,13 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -43,6 +43,13 @@ namespace ICSharpCode.ILSpy.Analyzers
/// </summary>
public Language Language { get; internal set; }
/// <summary>
/// Allows the analyzer to control whether the tree nodes will be sorted.
/// Must be set within <see cref="IAnalyzer.Analyze(ISymbol, AnalyzerContext)"/>
/// before the results are enumerated.
/// </summary>
public bool SortResults { get; set; }
public MethodBodyBlock GetMethodBody(IMethod method)
{
if (!method.HasBody || method.MetadataToken.IsNil)

79
ILSpy/Analyzers/AnalyzerHelpers.cs

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
// Copyright (c) 2018 Siegfried Pammer
//
// 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.Reflection.Metadata;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.Analyzers
{
internal static class AnalyzerHelpers
{
public static bool IsPossibleReferenceTo(EntityHandle member, PEFile module, IMethod analyzedMethod)
{
if (member.IsNil)
return false;
MetadataReader metadata = module.Metadata;
switch (member.Kind)
{
case HandleKind.MethodDefinition:
return member == analyzedMethod.MetadataToken
&& module == analyzedMethod.ParentModule.PEFile;
case HandleKind.MemberReference:
var mr = metadata.GetMemberReference((MemberReferenceHandle)member);
if (mr.GetKind() != MemberReferenceKind.Method)
return false;
return metadata.StringComparer.Equals(mr.Name, analyzedMethod.Name);
case HandleKind.MethodSpecification:
var ms = metadata.GetMethodSpecification((MethodSpecificationHandle)member);
return IsPossibleReferenceTo(ms.Method, module, analyzedMethod);
default:
return false;
}
}
public static ISymbol GetParentEntity(DecompilerTypeSystem ts, CustomAttribute customAttribute)
{
var metadata = ts.MainModule.PEFile.Metadata;
switch (customAttribute.Parent.Kind)
{
case HandleKind.MethodDefinition:
IMethod parent = (IMethod)ts.MainModule.ResolveEntity(customAttribute.Parent);
return parent?.AccessorOwner ?? parent;
case HandleKind.FieldDefinition:
case HandleKind.PropertyDefinition:
case HandleKind.EventDefinition:
case HandleKind.TypeDefinition:
return ts.MainModule.ResolveEntity(customAttribute.Parent);
case HandleKind.AssemblyDefinition:
case HandleKind.ModuleDefinition:
return ts.MainModule;
case HandleKind.GenericParameterConstraint:
var gpc = metadata.GetGenericParameterConstraint((GenericParameterConstraintHandle)customAttribute.Parent);
var gp = metadata.GetGenericParameter(gpc.Parameter);
return ts.MainModule.ResolveEntity(gp.Parent);
case HandleKind.GenericParameter:
gp = metadata.GetGenericParameter((GenericParameterHandle)customAttribute.Parent);
return ts.MainModule.ResolveEntity(gp.Parent);
default:
return null;
}
}
}
}

37
ILSpy/Analyzers/AnalyzerScope.cs

@ -32,6 +32,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -32,6 +32,7 @@ namespace ICSharpCode.ILSpy.Analyzers
public class AnalyzerScope
{
readonly ITypeDefinition typeScope;
readonly AssemblyListSnapshot assemblyListSnapshot;
/// <summary>
/// Returns whether this scope is local, i.e., AnalyzedSymbol is only reachable
@ -40,6 +41,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -40,6 +41,7 @@ namespace ICSharpCode.ILSpy.Analyzers
public bool IsLocal { get; }
public AssemblyList AssemblyList { get; }
public ISymbol AnalyzedSymbol { get; }
public ITypeDefinition TypeScope => typeScope;
@ -49,11 +51,12 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -49,11 +51,12 @@ namespace ICSharpCode.ILSpy.Analyzers
public AnalyzerScope(AssemblyList assemblyList, IEntity entity)
{
AssemblyList = assemblyList;
assemblyListSnapshot = assemblyList.GetSnapshot();
AnalyzedSymbol = entity;
if (entity is ITypeDefinition type)
{
typeScope = type;
effectiveAccessibility = DetermineEffectiveAccessibility(ref typeScope);
effectiveAccessibility = DetermineEffectiveAccessibility(ref typeScope, Accessibility.Public);
}
else
{
@ -76,11 +79,16 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -76,11 +79,16 @@ namespace ICSharpCode.ILSpy.Analyzers
public IEnumerable<PEFile> GetAllModules()
{
return AssemblyList.GetAllAssemblies().GetAwaiter().GetResult()
return assemblyListSnapshot.GetAllAssembliesAsync().GetAwaiter().GetResult()
.Select(asm => asm.GetPEFileOrNull())
.Where(x => x != null);
}
public DecompilerTypeSystem ConstructTypeSystem(PEFile module)
{
return new DecompilerTypeSystem(module, module.GetAssemblyResolver(assemblyListSnapshot, loadOnDemand: false));
}
public IEnumerable<ITypeDefinition> GetTypesInScope(CancellationToken ct)
{
if (IsLocal)
@ -94,7 +102,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -94,7 +102,7 @@ namespace ICSharpCode.ILSpy.Analyzers
{
foreach (var module in GetModulesInScope(ct))
{
var typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver());
var typeSystem = ConstructTypeSystem(module);
foreach (var type in typeSystem.MainModule.TypeDefinitions)
{
yield return type;
@ -103,13 +111,15 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -103,13 +111,15 @@ namespace ICSharpCode.ILSpy.Analyzers
}
}
static Accessibility DetermineEffectiveAccessibility(ref ITypeDefinition typeScope, Accessibility memberAccessibility = Accessibility.Public)
static Accessibility DetermineEffectiveAccessibility(ref ITypeDefinition typeScope, Accessibility memberAccessibility)
{
Accessibility accessibility = memberAccessibility;
while (typeScope.DeclaringTypeDefinition != null && !accessibility.LessThanOrEqual(Accessibility.Private))
var ts = typeScope;
while (ts != null && !accessibility.LessThanOrEqual(Accessibility.Private))
{
accessibility = accessibility.Intersect(typeScope.Accessibility);
typeScope = typeScope.DeclaringTypeDefinition;
accessibility = accessibility.Intersect(ts.Accessibility);
typeScope = ts;
ts = ts.DeclaringTypeDefinition;
}
// Once we reach a private entity, we leave the loop with typeScope set to the class that
// contains the private entity = the scope that needs to be searched.
@ -131,7 +141,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -131,7 +141,7 @@ namespace ICSharpCode.ILSpy.Analyzers
toWalkFiles.Push(self);
checkedFiles.Add(self);
IList<LoadedAssembly> assemblies = AssemblyList.GetAllAssemblies().GetAwaiter().GetResult();
IList<LoadedAssembly> assemblies = assemblyListSnapshot.GetAllAssembliesAsync().GetAwaiter().GetResult();
do
{
@ -145,7 +155,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -145,7 +155,7 @@ namespace ICSharpCode.ILSpy.Analyzers
continue;
if (checkedFiles.Contains(module))
continue;
var resolver = assembly.GetAssemblyResolver(loadOnDemand: false);
var resolver = assembly.GetAssemblyResolver(assemblyListSnapshot, loadOnDemand: false);
foreach (var reference in module.AssemblyReferences)
{
if (resolver.Resolve(reference) == curFile)
@ -184,7 +194,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -184,7 +194,7 @@ namespace ICSharpCode.ILSpy.Analyzers
if (friendAssemblies.Count > 0)
{
IEnumerable<LoadedAssembly> assemblies = AssemblyList.GetAllAssemblies()
IEnumerable<LoadedAssembly> assemblies = assemblyListSnapshot.GetAllAssembliesAsync()
.GetAwaiter().GetResult();
foreach (var assembly in assemblies)
@ -208,7 +218,8 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -208,7 +218,8 @@ namespace ICSharpCode.ILSpy.Analyzers
foreach (var h in metadata.TypeReferences)
{
var typeRef = metadata.GetTypeReference(h);
if (metadata.StringComparer.Equals(typeRef.Name, typeScopeName) && metadata.StringComparer.Equals(typeRef.Namespace, typeScopeNamespace))
if (metadata.StringComparer.Equals(typeRef.Name, typeScopeName)
&& metadata.StringComparer.Equals(typeRef.Namespace, typeScopeNamespace))
{
hasRef = true;
break;
@ -223,7 +234,9 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -223,7 +234,9 @@ namespace ICSharpCode.ILSpy.Analyzers
foreach (var h in metadata.ExportedTypes)
{
var exportedType = metadata.GetExportedType(h);
if (exportedType.IsForwarder && metadata.StringComparer.Equals(exportedType.Name, typeScopeName) && metadata.StringComparer.Equals(exportedType.Namespace, typeScopeNamespace))
if (exportedType.IsForwarder
&& metadata.StringComparer.Equals(exportedType.Name, typeScopeName)
&& metadata.StringComparer.Equals(exportedType.Namespace, typeScopeNamespace))
{
hasForward = true;
break;

7
ILSpy/Analyzers/AnalyzerSearchTreeNode.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ICSharpCode.Decompiler.TypeSystem;
@ -60,10 +61,12 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -60,10 +61,12 @@ namespace ICSharpCode.ILSpy.Analyzers
Language = Language,
AssemblyList = MainWindow.Instance.CurrentAssemblyList
};
foreach (var result in analyzer.Analyze(symbol, context))
var results = analyzer.Analyze(symbol, context).Select(SymbolTreeNodeFactory);
if (context.SortResults)
{
yield return SymbolTreeNodeFactory(result);
results = results.OrderBy(tn => tn.Text?.ToString(), NaturalStringComparer.Instance);
}
return results;
}
else
{

60
ILSpy/Analyzers/Builtin/AttributeAppliedToAnalyzer.cs

@ -16,14 +16,13 @@ @@ -16,14 +16,13 @@
// 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.Runtime.InteropServices;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -41,11 +40,11 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -41,11 +40,11 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
// TODO: DeclSecurity attributes are not supported.
if (!IsBuiltinAttribute(attributeType, out var knownAttribute))
{
return HandleCustomAttribute(attributeType, scope);
return HandleCustomAttribute(attributeType, scope, context.CancellationToken).Distinct();
}
else
{
return HandleBuiltinAttribute(knownAttribute, scope).SelectMany(s => s);
return HandleBuiltinAttribute(knownAttribute, scope, context.CancellationToken).SelectMany(s => s);
}
}
@ -74,7 +73,7 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -74,7 +73,7 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
}
}
IEnumerable<IEnumerable<ISymbol>> HandleBuiltinAttribute(KnownAttribute attribute, AnalyzerScope scope)
IEnumerable<IEnumerable<ISymbol>> HandleBuiltinAttribute(KnownAttribute attribute, AnalyzerScope scope, CancellationToken ct)
{
IEnumerable<ISymbol> ScanTypes(DecompilerTypeSystem ts)
{
@ -112,9 +111,10 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -112,9 +111,10 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
.Select(m => m.AccessorOwner ?? m);
}
foreach (Decompiler.Metadata.PEFile module in scope.GetAllModules())
foreach (Decompiler.Metadata.PEFile module in scope.GetModulesInScope(ct))
{
var ts = new DecompilerTypeSystem(module, module.GetAssemblyResolver());
var ts = scope.ConstructTypeSystem(module);
ct.ThrowIfCancellationRequested();
switch (attribute)
{
@ -148,21 +148,21 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -148,21 +148,21 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
}
}
IEnumerable<ISymbol> HandleCustomAttribute(ITypeDefinition attributeType, AnalyzerScope scope)
IEnumerable<ISymbol> HandleCustomAttribute(ITypeDefinition attributeType, AnalyzerScope scope, CancellationToken ct)
{
var genericContext = new GenericContext(); // type arguments do not matter for this analyzer.
var genericContext = new Decompiler.TypeSystem.GenericContext(); // type arguments do not matter for this analyzer.
foreach (var module in scope.GetAllModules())
foreach (var module in scope.GetModulesInScope(ct))
{
var ts = new DecompilerTypeSystem(module, module.GetAssemblyResolver());
var ts = scope.ConstructTypeSystem(module);
ct.ThrowIfCancellationRequested();
var decoder = new FindTypeDecoder(ts.MainModule, attributeType);
var referencedParameters = new HashSet<ParameterHandle>();
foreach (var h in module.Metadata.CustomAttributes)
{
var customAttribute = module.Metadata.GetCustomAttribute(h);
var attributeCtor = ts.MainModule.ResolveMethod(customAttribute.Constructor, genericContext);
if (attributeCtor.DeclaringTypeDefinition != null
&& attributeCtor.ParentModule.PEFile == attributeType.ParentModule.PEFile
&& attributeCtor.DeclaringTypeDefinition.MetadataToken == attributeType.MetadataToken)
if (IsCustomAttributeOfType(customAttribute.Constructor, module.Metadata, decoder))
{
if (customAttribute.Parent.Kind == HandleKind.Parameter)
{
@ -170,7 +170,7 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -170,7 +170,7 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
}
else
{
var parent = GetParentEntity(ts, customAttribute);
var parent = AnalyzerHelpers.GetParentEntity(ts, customAttribute);
if (parent != null)
yield return parent;
}
@ -201,30 +201,10 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -201,30 +201,10 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
}
}
ISymbol GetParentEntity(DecompilerTypeSystem ts, CustomAttribute customAttribute)
internal static bool IsCustomAttributeOfType(EntityHandle customAttributeCtor, MetadataReader metadata, FindTypeDecoder decoder)
{
var metadata = ts.MainModule.PEFile.Metadata;
switch (customAttribute.Parent.Kind)
{
case HandleKind.MethodDefinition:
case HandleKind.FieldDefinition:
case HandleKind.PropertyDefinition:
case HandleKind.EventDefinition:
case HandleKind.TypeDefinition:
return ts.MainModule.ResolveEntity(customAttribute.Parent);
case HandleKind.AssemblyDefinition:
case HandleKind.ModuleDefinition:
return ts.MainModule;
case HandleKind.GenericParameterConstraint:
var gpc = metadata.GetGenericParameterConstraint((GenericParameterConstraintHandle)customAttribute.Parent);
var gp = metadata.GetGenericParameter(gpc.Parameter);
return ts.MainModule.ResolveEntity(gp.Parent);
case HandleKind.GenericParameter:
gp = metadata.GetGenericParameter((GenericParameterHandle)customAttribute.Parent);
return ts.MainModule.ResolveEntity(gp.Parent);
default:
return null;
}
var declaringAttributeType = customAttributeCtor.GetDeclaringType(metadata);
return decoder.GetTypeFromEntity(metadata, declaringAttributeType);
}
public bool Show(ISymbol symbol)

158
ILSpy/Analyzers/Builtin/FindTypeInAttributeDecoder.cs

@ -0,0 +1,158 @@ @@ -0,0 +1,158 @@
// Copyright (c) 2022 Siegfried Pammer
//
// 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.
#nullable enable
using System;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.ILSpy.Analyzers.Builtin
{
public enum TokenSearchResult : byte
{
NoResult = 0,
Byte = PrimitiveTypeCode.Byte,
SByte = PrimitiveTypeCode.SByte,
Int16 = PrimitiveTypeCode.Int16,
UInt16 = PrimitiveTypeCode.UInt16,
Int32 = PrimitiveTypeCode.Int32,
UInt32 = PrimitiveTypeCode.UInt32,
Int64 = PrimitiveTypeCode.Int64,
UInt64 = PrimitiveTypeCode.UInt64,
IntPtr = PrimitiveTypeCode.IntPtr,
UIntPtr = PrimitiveTypeCode.UIntPtr,
// lowest PrimitiveTypeCode is 1
// highest PrimitiveTypeCode is 28 (0b0001_1100)
// TokenSearchResult with a PrimitiveTypeCode set is only used when decoding an enum-type.
// It is used for GetUnderlyingEnumType and should be masked out in all other uses.
// MSB = Found
// 127 = System.Type
TypeCodeMask = 0b0111_1111,
Found = 0b1000_0000,
SystemType = 127,
}
class FindTypeInAttributeDecoder : ICustomAttributeTypeProvider<TokenSearchResult>
{
readonly PEFile declaringModule;
readonly MetadataModule currentModule;
readonly TypeDefinitionHandle handle;
readonly PrimitiveTypeCode primitiveType;
/// <summary>
/// Constructs a FindTypeInAttributeDecoder that can be used to find <paramref name="type"/> in signatures from <paramref name="currentModule"/>.
/// </summary>
public FindTypeInAttributeDecoder(MetadataModule currentModule, ITypeDefinition type)
{
this.currentModule = currentModule;
this.declaringModule = type.ParentModule.PEFile ?? throw new InvalidOperationException("Cannot use MetadataModule without PEFile as context.");
this.handle = (TypeDefinitionHandle)type.MetadataToken;
this.primitiveType = type.KnownTypeCode == KnownTypeCode.None ? 0 : type.KnownTypeCode.ToPrimitiveTypeCode();
}
public TokenSearchResult GetPrimitiveType(PrimitiveTypeCode typeCode)
{
return typeCode == primitiveType ? TokenSearchResult.Found : 0;
}
public TokenSearchResult GetSystemType() => TokenSearchResult.SystemType;
public TokenSearchResult GetSZArrayType(TokenSearchResult elementType) => elementType & TokenSearchResult.Found;
public TokenSearchResult GetTypeFromDefinition(MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind)
{
TokenSearchResult result = TokenSearchResult.NoResult;
if (handle.IsEnum(reader, out PrimitiveTypeCode underlyingType))
{
result = (TokenSearchResult)underlyingType;
}
else if (((EntityHandle)handle).IsKnownType(reader, KnownTypeCode.Type))
{
result = TokenSearchResult.SystemType;
}
if (this.handle == handle && reader == declaringModule.Metadata)
{
result |= TokenSearchResult.Found;
}
return result;
}
public TokenSearchResult GetTypeFromReference(MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind)
{
var t = currentModule.ResolveType(handle, default);
return GetResultFromResolvedType(t);
}
public TokenSearchResult GetTypeFromSerializedName(string name)
{
if (name == null)
{
return TokenSearchResult.NoResult;
}
try
{
IType type = ReflectionHelper.ParseReflectionName(name)
.Resolve(new SimpleTypeResolveContext(currentModule));
return GetResultFromResolvedType(type);
}
catch (ReflectionNameParseException)
{
return TokenSearchResult.NoResult;
}
}
private TokenSearchResult GetResultFromResolvedType(IType type)
{
var td = type.GetDefinition();
if (td == null)
return TokenSearchResult.NoResult;
TokenSearchResult result = TokenSearchResult.NoResult;
var underlyingType = td.EnumUnderlyingType?.GetDefinition();
if (underlyingType != null)
{
result = (TokenSearchResult)underlyingType.KnownTypeCode.ToPrimitiveTypeCode();
}
else if (td.KnownTypeCode == KnownTypeCode.Type)
{
result = TokenSearchResult.SystemType;
}
if (td.MetadataToken == this.handle && td.ParentModule.PEFile == declaringModule)
{
result |= TokenSearchResult.Found;
}
return result;
}
public PrimitiveTypeCode GetUnderlyingEnumType(TokenSearchResult type)
{
TokenSearchResult typeCode = type & TokenSearchResult.TypeCodeMask;
if (typeCode == 0 || typeCode == TokenSearchResult.SystemType)
throw new EnumUnderlyingTypeResolveException();
return (PrimitiveTypeCode)typeCode;
}
public bool IsSystemType(TokenSearchResult type) => (type & TokenSearchResult.TypeCodeMask) == TokenSearchResult.SystemType;
}
}

43
ILSpy/Analyzers/Builtin/MethodUsedByAnalyzer.cs

@ -45,6 +45,7 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -45,6 +45,7 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
Debug.Assert(analyzedSymbol is IMethod);
var analyzedMethod = (IMethod)analyzedSymbol;
var analyzedBaseMethod = (IMethod)InheritanceHelper.GetBaseMember(analyzedMethod);
var mapping = context.Language
.GetCodeMappingInfo(analyzedMethod.ParentModule.PEFile,
analyzedMethod.DeclaringTypeDefinition.MetadataToken);
@ -57,12 +58,13 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -57,12 +58,13 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
foreach (var type in scope.GetTypesInScope(context.CancellationToken))
{
var parentModule = (MetadataModule)type.ParentModule;
mapping = context.Language.GetCodeMappingInfo(parentModule.PEFile, type.MetadataToken);
mapping = null;
var methods = type.GetMembers(m => m is IMethod, Options).OfType<IMethod>();
foreach (var method in methods)
{
if (IsUsedInMethod((IMethod)analyzedSymbol, method, context))
if (IsUsedInMethod(analyzedMethod, analyzedBaseMethod, method, context))
{
mapping ??= context.Language.GetCodeMappingInfo(parentModule.PEFile, type.MetadataToken);
var parent = mapping.GetParentMethod((MethodDefinitionHandle)method.MetadataToken);
yield return parentModule.GetDefinition(parent);
}
@ -70,12 +72,12 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -70,12 +72,12 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
foreach (var property in type.Properties)
{
if (property.CanGet && IsUsedInMethod((IMethod)analyzedSymbol, property.Getter, context))
if (property.CanGet && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, property.Getter, context))
{
yield return property;
continue;
}
if (property.CanSet && IsUsedInMethod((IMethod)analyzedSymbol, property.Setter, context))
if (property.CanSet && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, property.Setter, context))
{
yield return property;
continue;
@ -84,17 +86,17 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -84,17 +86,17 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
foreach (var @event in type.Events)
{
if (@event.CanAdd && IsUsedInMethod((IMethod)analyzedSymbol, @event.AddAccessor, context))
if (@event.CanAdd && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, @event.AddAccessor, context))
{
yield return @event;
continue;
}
if (@event.CanRemove && IsUsedInMethod((IMethod)analyzedSymbol, @event.RemoveAccessor, context))
if (@event.CanRemove && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, @event.RemoveAccessor, context))
{
yield return @event;
continue;
}
if (@event.CanInvoke && IsUsedInMethod((IMethod)analyzedSymbol, @event.InvokeAccessor, context))
if (@event.CanInvoke && IsUsedInMethod(analyzedMethod, analyzedBaseMethod, @event.InvokeAccessor, context))
{
yield return @event;
continue;
@ -103,12 +105,12 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -103,12 +105,12 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
}
}
bool IsUsedInMethod(IMethod analyzedEntity, IMethod method, AnalyzerContext context)
bool IsUsedInMethod(IMethod analyzedEntity, IMethod analyzedBaseMethod, IMethod method, AnalyzerContext context)
{
return ScanMethodBody(analyzedEntity, method, context.GetMethodBody(method));
return ScanMethodBody(analyzedEntity, method, analyzedBaseMethod, context.GetMethodBody(method));
}
static bool ScanMethodBody(IMethod analyzedMethod, IMethod method, MethodBodyBlock methodBody)
static bool ScanMethodBody(IMethod analyzedMethod, IMethod method, IMethod analyzedBaseMethod, MethodBodyBlock methodBody)
{
if (methodBody == null)
return false;
@ -116,7 +118,6 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -116,7 +118,6 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
var mainModule = (MetadataModule)method.ParentModule;
var blob = methodBody.GetILReader();
var baseMethod = InheritanceHelper.GetBaseMember(analyzedMethod);
var genericContext = new Decompiler.TypeSystem.GenericContext(); // type parameters don't matter for this analyzer
while (blob.RemainingBytes > 0)
@ -136,8 +137,13 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -136,8 +137,13 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
return false; // unexpected end of blob
}
var member = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (member.IsNil || !member.Kind.IsMemberKind())
continue;
if (!AnalyzerHelpers.IsPossibleReferenceTo(member, mainModule.PEFile, analyzedMethod))
{
if (analyzedBaseMethod == null || !AnalyzerHelpers.IsPossibleReferenceTo(member, mainModule.PEFile, analyzedBaseMethod))
{
continue;
}
}
IMember m;
try
@ -151,19 +157,16 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -151,19 +157,16 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
if (m == null)
continue;
if (opCode == ILOpCode.Callvirt && baseMethod != null)
if (opCode == ILOpCode.Callvirt && analyzedBaseMethod != null)
{
if (IsSameMember(baseMethod, m))
if (IsSameMember(analyzedBaseMethod, m))
{
return true;
}
}
else
if (IsSameMember(analyzedMethod, m))
{
if (IsSameMember(analyzedMethod, m))
{
return true;
}
return true;
}
}

453
ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler;
@ -38,220 +39,235 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -38,220 +39,235 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
public IEnumerable<ISymbol> Analyze(ISymbol analyzedSymbol, AnalyzerContext context)
{
Debug.Assert(analyzedSymbol is ITypeDefinition);
var scope = context.GetScopeOf((ITypeDefinition)analyzedSymbol);
foreach (var type in scope.GetTypesInScope(context.CancellationToken))
var analyzedType = (ITypeDefinition)analyzedSymbol;
var scope = context.GetScopeOf(analyzedType);
context.SortResults = true;
return scope.GetModulesInScope(context.CancellationToken)
.AsParallel().AsOrdered()
.SelectMany(AnalyzeModuleAndFilter);
IEnumerable<ISymbol> AnalyzeModuleAndFilter(PEFile module)
{
foreach (var result in ScanType((ITypeDefinition)analyzedSymbol, type, context))
yield return result;
return AnalyzeModule(analyzedType, scope, module)
.Distinct()
.Where(s => !analyzedType.Equals((s as IEntity)?.DeclaringTypeDefinition));
}
}
IEnumerable<IEntity> ScanType(ITypeDefinition analyzedEntity, ITypeDefinition type, AnalyzerContext context)
static IEnumerable<ISymbol> AnalyzeModule(ITypeDefinition analyzedType, AnalyzerScope scope, PEFile module)
{
if (analyzedEntity.ParentModule.PEFile == type.ParentModule.PEFile
&& analyzedEntity.MetadataToken == type.MetadataToken)
yield break;
var metadata = module.Metadata;
var typeSystem = scope.ConstructTypeSystem(module);
var decoder = new FindTypeDecoder(typeSystem.MainModule, analyzedType);
//// resolve type refs
//int rowCount = metadata.GetTableRowCount(TableIndex.TypeRef);
//BitSet typeReferences = new BitSet(rowCount);
//for (int row = 0; row < rowCount; row++)
//{
// var h = MetadataTokens.TypeReferenceHandle(row + 1);
// typeReferences[row] = decoder.GetTypeFromReference(metadata, h, 0);
//}
//// resolve type specs
//rowCount = metadata.GetTableRowCount(TableIndex.TypeSpec);
//BitSet typeSpecifications = new BitSet(rowCount);
//for (int row = 0; row < rowCount; row++)
//{
// var h = MetadataTokens.TypeSpecificationHandle(row + 1);
// typeSpecifications[row] = decoder.GetTypeFromSpecification(metadata, default, h, 0);
//}
foreach (ISymbol result in FindUsesInAttributes(typeSystem, metadata, decoder, analyzedType))
yield return result;
foreach (var h in metadata.TypeDefinitions)
{
var td = metadata.GetTypeDefinition(h);
bool found = decoder.GetTypeFromEntity(metadata, td.GetBaseTypeOrNil());
foreach (var ih in td.GetInterfaceImplementations())
{
var ii = metadata.GetInterfaceImplementation(ih);
found |= decoder.GetTypeFromEntity(metadata, ii.Interface);
}
found |= FindUsesInGenericConstraints(metadata, td.GetGenericParameters(), decoder);
var visitor = new TypeDefinitionUsedVisitor(analyzedEntity, topLevelOnly: false);
if (found)
yield return typeSystem.MainModule.GetDefinition(h);
}
foreach (var bt in type.DirectBaseTypes)
foreach (var h in metadata.MethodDefinitions)
{
bt.AcceptVisitor(visitor);
var md = metadata.GetMethodDefinition(h);
var msig = md.DecodeSignature(decoder, default);
bool found = FindTypeDecoder.AnyInMethodSignature(msig);
found |= FindUsesInGenericConstraints(metadata, md.GetGenericParameters(), decoder);
if (found || ScanMethodBody(analyzedType, module, md, decoder))
{
var method = typeSystem.MainModule.GetDefinition(h);
yield return method?.AccessorOwner ?? method;
}
}
if (visitor.Found || ScanAttributes(visitor, type.GetAttributes()))
yield return type;
foreach (var h in metadata.FieldDefinitions)
{
var fd = metadata.GetFieldDefinition(h);
if (fd.DecodeSignature(decoder, default))
yield return typeSystem.MainModule.GetDefinition(h);
}
foreach (var member in type.Members)
foreach (var h in metadata.PropertyDefinitions)
{
visitor.Found = false;
VisitMember(visitor, member, context, scanBodies: true);
if (visitor.Found)
yield return member;
var pd = metadata.GetPropertyDefinition(h);
var psig = pd.DecodeSignature(decoder, default);
if (FindTypeDecoder.AnyInMethodSignature(psig))
yield return typeSystem.MainModule.GetDefinition(h);
}
foreach (var h in metadata.EventDefinitions)
{
var ed = metadata.GetEventDefinition(h);
if (decoder.GetTypeFromEntity(metadata, ed.Type))
yield return typeSystem.MainModule.GetDefinition(h);
}
}
bool ScanAttributes(TypeDefinitionUsedVisitor visitor, IEnumerable<IAttribute> attributes)
static bool FindUsesInGenericConstraints(MetadataReader metadata, GenericParameterHandleCollection collection, FindTypeDecoder decoder)
{
foreach (var attribute in attributes)
foreach (var h in collection)
{
foreach (var fa in attribute.FixedArguments)
{
CheckAttributeValue(fa.Value);
if (visitor.Found)
return true;
}
foreach (var na in attribute.NamedArguments)
var gp = metadata.GetGenericParameter(h);
foreach (var hc in gp.GetConstraints())
{
CheckAttributeValue(na.Value);
if (visitor.Found)
var gc = metadata.GetGenericParameterConstraint(hc);
if (decoder.GetTypeFromEntity(metadata, gc.Type))
return true;
}
}
return false;
}
void CheckAttributeValue(object value)
static IEnumerable<ISymbol> FindUsesInAttributes(DecompilerTypeSystem typeSystem, MetadataReader metadata, FindTypeDecoder decoder, ITypeDefinition analyzedType)
{
var attrDecoder = new FindTypeInAttributeDecoder(typeSystem.MainModule, analyzedType);
var referencedParameters = new HashSet<ParameterHandle>();
foreach (var h in metadata.CustomAttributes)
{
if (value is IType typeofType)
var customAttribute = metadata.GetCustomAttribute(h);
CustomAttributeValue<TokenSearchResult> value;
try
{
typeofType.AcceptVisitor(visitor);
value = customAttribute.DecodeValue(attrDecoder);
}
else if (value is ImmutableArray<CustomAttributeTypedArgument<IType>> arr)
catch (EnumUnderlyingTypeResolveException)
{
foreach (var element in arr)
{
CheckAttributeValue(element.Value);
}
continue;
}
}
}
void VisitMember(TypeDefinitionUsedVisitor visitor, IMember member, AnalyzerContext context, bool scanBodies = false)
{
member.DeclaringType.AcceptVisitor(visitor);
switch (member)
{
case IField field:
field.ReturnType.AcceptVisitor(visitor);
if (!visitor.Found)
ScanAttributes(visitor, field.GetAttributes());
break;
case IMethod method:
foreach (var p in method.Parameters)
{
p.Type.AcceptVisitor(visitor);
if (!visitor.Found)
ScanAttributes(visitor, p.GetAttributes());
}
if (!visitor.Found)
ScanAttributes(visitor, method.GetAttributes());
method.ReturnType.AcceptVisitor(visitor);
if (!visitor.Found)
ScanAttributes(visitor, method.GetReturnTypeAttributes());
foreach (var t in method.TypeArguments)
{
t.AcceptVisitor(visitor);
}
foreach (var t in method.TypeParameters)
{
t.AcceptVisitor(visitor);
if (!visitor.Found)
ScanAttributes(visitor, t.GetAttributes());
}
if (scanBodies && !visitor.Found)
ScanMethodBody(visitor, method, context.GetMethodBody(method), context);
break;
case IProperty property:
foreach (var p in property.Parameters)
if (AttributeAppliedToAnalyzer.IsCustomAttributeOfType(customAttribute.Constructor, metadata, decoder)
|| AnalyzeCustomAttributeValue(value))
{
if (customAttribute.Parent.Kind == HandleKind.Parameter)
{
p.Type.AcceptVisitor(visitor);
referencedParameters.Add((ParameterHandle)customAttribute.Parent);
}
if (!visitor.Found)
ScanAttributes(visitor, property.GetAttributes());
property.ReturnType.AcceptVisitor(visitor);
if (scanBodies && !visitor.Found && property.CanGet)
else
{
if (!visitor.Found)
ScanAttributes(visitor, property.Getter.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, property.Getter.GetReturnTypeAttributes());
ScanMethodBody(visitor, property.Getter, context.GetMethodBody(property.Getter), context);
var parent = AnalyzerHelpers.GetParentEntity(typeSystem, customAttribute);
if (parent != null)
yield return parent;
}
if (scanBodies && !visitor.Found && property.CanSet)
}
}
if (referencedParameters.Count > 0)
{
foreach (var h in metadata.MethodDefinitions)
{
var md = metadata.GetMethodDefinition(h);
foreach (var p in md.GetParameters())
{
if (!visitor.Found)
ScanAttributes(visitor, property.Setter.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, property.Setter.GetReturnTypeAttributes());
ScanMethodBody(visitor, property.Setter, context.GetMethodBody(property.Setter), context);
if (referencedParameters.Contains(p))
{
var method = typeSystem.MainModule.ResolveMethod(h, default);
if (method != null)
{
if (method.IsAccessor)
yield return method.AccessorOwner;
else
yield return method;
}
break;
}
}
}
}
break;
case IEvent @event:
@event.ReturnType.AcceptVisitor(visitor);
if (scanBodies && !visitor.Found && @event.CanAdd)
{
if (!visitor.Found)
ScanAttributes(visitor, @event.AddAccessor.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, @event.AddAccessor.GetReturnTypeAttributes());
ScanMethodBody(visitor, @event.AddAccessor, context.GetMethodBody(@event.AddAccessor), context);
}
}
if (scanBodies && !visitor.Found && @event.CanRemove)
{
if (!visitor.Found)
ScanAttributes(visitor, @event.RemoveAccessor.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, @event.RemoveAccessor.GetReturnTypeAttributes());
private static bool AnalyzeCustomAttributeValue(CustomAttributeValue<TokenSearchResult> attribute)
{
foreach (var fa in attribute.FixedArguments)
{
if (CheckAttributeValue(fa.Value))
return true;
}
ScanMethodBody(visitor, @event.RemoveAccessor, context.GetMethodBody(@event.RemoveAccessor), context);
}
foreach (var na in attribute.NamedArguments)
{
if (CheckAttributeValue(na.Value))
return true;
}
return false;
if (scanBodies && !visitor.Found && @event.CanInvoke)
bool CheckAttributeValue(object value)
{
if (value is TokenSearchResult typeofType)
{
if ((typeofType & TokenSearchResult.Found) != 0)
return true;
}
else if (value is ImmutableArray<CustomAttributeTypedArgument<IType>> arr)
{
foreach (var element in arr)
{
if (!visitor.Found)
ScanAttributes(visitor, @event.InvokeAccessor.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, @event.InvokeAccessor.GetReturnTypeAttributes());
ScanMethodBody(visitor, @event.InvokeAccessor, context.GetMethodBody(@event.InvokeAccessor), context);
if (CheckAttributeValue(element.Value))
return true;
}
}
break;
return false;
}
}
void ScanMethodBody(TypeDefinitionUsedVisitor visitor, IMethod method, MethodBodyBlock methodBody, AnalyzerContext context)
static bool ScanMethodBody(ITypeDefinition analyzedType, PEFile module, in MethodDefinition md, FindTypeDecoder decoder)
{
if (methodBody == null)
return;
if (!md.HasBody())
return false;
var module = (MetadataModule)method.ParentModule;
var genericContext = new Decompiler.TypeSystem.GenericContext(); // type parameters don't matter for this analyzer
var methodBody = module.Reader.GetMethodBody(md.RelativeVirtualAddress);
var metadata = module.Metadata;
if (!methodBody.LocalSignature.IsNil)
{
ImmutableArray<IType> localSignature;
try
{
localSignature = module.DecodeLocalSignature(methodBody.LocalSignature, genericContext);
var ss = metadata.GetStandaloneSignature(methodBody.LocalSignature);
if (HandleStandaloneSignature(ss))
{
return true;
}
}
catch (BadImageFormatException)
{
// Issue #2197: ignore invalid local signatures
localSignature = ImmutableArray<IType>.Empty;
}
foreach (var type in localSignature)
{
type.AcceptVisitor(visitor);
if (visitor.Found)
return;
}
}
var blob = methodBody.GetILReader();
while (!visitor.Found && blob.RemainingBytes > 0)
while (blob.RemainingBytes > 0)
{
var opCode = blob.DecodeOpCode();
switch (opCode.GetOperandType())
@ -261,49 +277,118 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -261,49 +277,118 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
case OperandType.Sig:
case OperandType.Tok:
case OperandType.Type:
var member = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (member.IsNil)
continue;
switch (member.Kind)
{
case HandleKind.TypeReference:
case HandleKind.TypeSpecification:
case HandleKind.TypeDefinition:
module.ResolveType(member, genericContext).AcceptVisitor(visitor);
if (visitor.Found)
return;
break;
if (HandleMember(MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32())))
return true;
break;
default:
blob.SkipOperand(opCode);
break;
}
}
case HandleKind.FieldDefinition:
case HandleKind.MethodDefinition:
case HandleKind.MemberReference:
case HandleKind.MethodSpecification:
VisitMember(visitor, module.ResolveEntity(member, genericContext) as IMember, context);
return false;
if (visitor.Found)
return;
bool HandleMember(EntityHandle member)
{
if (member.IsNil)
return false;
switch (member.Kind)
{
case HandleKind.TypeReference:
return decoder.GetTypeFromReference(metadata, (TypeReferenceHandle)member, 0);
case HandleKind.TypeSpecification:
return decoder.GetTypeFromSpecification(metadata, default, (TypeSpecificationHandle)member, 0);
case HandleKind.TypeDefinition:
return decoder.GetTypeFromDefinition(metadata, (TypeDefinitionHandle)member, 0);
case HandleKind.FieldDefinition:
var fd = metadata.GetFieldDefinition((FieldDefinitionHandle)member);
return HandleMember(fd.GetDeclaringType()) || fd.DecodeSignature(decoder, default);
case HandleKind.MethodDefinition:
var md = metadata.GetMethodDefinition((MethodDefinitionHandle)member);
if (HandleMember(md.GetDeclaringType()))
return true;
var msig = md.DecodeSignature(decoder, default);
if (msig.ReturnType)
return true;
foreach (var t in msig.ParameterTypes)
{
if (t)
return true;
}
break;
case HandleKind.MemberReference:
var mr = metadata.GetMemberReference((MemberReferenceHandle)member);
if (HandleMember(mr.Parent))
return true;
switch (mr.GetKind())
{
case MemberReferenceKind.Method:
msig = mr.DecodeMethodSignature(decoder, default);
if (msig.ReturnType)
return true;
foreach (var t in msig.ParameterTypes)
{
if (t)
return true;
}
break;
case MemberReferenceKind.Field:
return mr.DecodeFieldSignature(decoder, default);
}
break;
case HandleKind.StandaloneSignature:
var (_, fpt) = module.DecodeMethodSignature((StandaloneSignatureHandle)member, genericContext);
fpt.AcceptVisitor(visitor);
case HandleKind.MethodSpecification:
var ms = metadata.GetMethodSpecification((MethodSpecificationHandle)member);
if (HandleMember(ms.Method))
return true;
var mssig = ms.DecodeSignature(decoder, default);
foreach (var t in mssig)
{
if (t)
return true;
}
break;
if (visitor.Found)
return;
break;
case HandleKind.StandaloneSignature:
var ss = metadata.GetStandaloneSignature((StandaloneSignatureHandle)member);
return HandleStandaloneSignature(ss);
}
return false;
}
default:
break;
bool HandleStandaloneSignature(StandaloneSignature signature)
{
switch (signature.GetKind())
{
case StandaloneSignatureKind.Method:
var msig = signature.DecodeMethodSignature(decoder, default);
if (msig.ReturnType)
return true;
foreach (var t in msig.ParameterTypes)
{
if (t)
return true;
}
break;
default:
blob.SkipOperand(opCode);
case StandaloneSignatureKind.LocalVariables:
var sig = signature.DecodeLocalSignature(decoder, default);
foreach (var t in sig)
{
if (t)
return true;
}
break;
}
return false;
}
}
public bool Show(ISymbol symbol) => symbol is ITypeDefinition entity;
public bool Show(ISymbol symbol) => symbol is ITypeDefinition;
}
class TypeDefinitionUsedVisitor : TypeVisitor

56
ILSpy/AssemblyList.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
@ -118,55 +119,20 @@ namespace ICSharpCode.ILSpy @@ -118,55 +119,20 @@ namespace ICSharpCode.ILSpy
}
}
/// <summary>
/// Gets all loaded assemblies recursively, including assemblies found in bundles or packages.
/// </summary>
public async Task<IList<LoadedAssembly>> GetAllAssemblies()
internal AssemblyListSnapshot GetSnapshot()
{
var assemblies = GetAssemblies();
var results = new List<LoadedAssembly>(assemblies.Length);
foreach (var asm in assemblies)
{
LoadedAssembly.LoadResult result;
try
{
result = await asm.GetLoadResultAsync();
}
catch
{
results.Add(asm);
continue;
}
if (result.Package != null)
{
AddDescendants(result.Package.RootFolder);
}
else if (result.PEFile != null)
{
results.Add(asm);
}
}
void AddDescendants(PackageFolder folder)
lock (lockObj)
{
foreach (var subFolder in folder.Folders)
{
AddDescendants(subFolder);
}
foreach (var entry in folder.Entries)
{
if (!entry.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
continue;
var asm = folder.ResolveFileName(entry.Name);
if (asm == null)
continue;
results.Add(asm);
}
return new AssemblyListSnapshot(assemblies.ToImmutableArray());
}
}
return results;
/// <summary>
/// Gets all loaded assemblies recursively, including assemblies found in bundles or packages.
/// </summary>
public Task<IList<LoadedAssembly>> GetAllAssemblies()
{
return GetSnapshot().GetAllAssembliesAsync();
}
public int Count {

199
ILSpy/AssemblyListSnapshot.cs

@ -0,0 +1,199 @@ @@ -0,0 +1,199 @@
// 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.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.ILSpy
{
class AssemblyListSnapshot
{
readonly ImmutableArray<LoadedAssembly> assemblies;
Dictionary<string, PEFile>? asmLookupByFullName;
Dictionary<string, PEFile>? asmLookupByShortName;
Dictionary<string, List<(PEFile module, Version version)>>? asmLookupByShortNameGrouped;
public ImmutableArray<LoadedAssembly> Assemblies => assemblies;
public AssemblyListSnapshot(ImmutableArray<LoadedAssembly> assemblies)
{
this.assemblies = assemblies;
}
public async Task<PEFile?> TryGetModuleAsync(IAssemblyReference reference, string tfm)
{
bool isWinRT = reference.IsWindowsRuntime;
if (tfm.StartsWith(".NETFramework,Version=v4.", StringComparison.Ordinal))
{
tfm = ".NETFramework,Version=v4";
}
string key = tfm + ";" + (isWinRT ? reference.Name : reference.FullName);
var lookup = LazyInit.VolatileRead(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName);
if (lookup == null)
{
lookup = await CreateLoadedAssemblyLookupAsync(shortNames: isWinRT).ConfigureAwait(false);
lookup = LazyInit.GetOrSet(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName, lookup);
}
if (lookup.TryGetValue(key, out PEFile module))
return module;
return null;
}
public async Task<PEFile?> TryGetSimilarModuleAsync(IAssemblyReference reference)
{
var lookup = LazyInit.VolatileRead(ref asmLookupByShortNameGrouped);
if (lookup == null)
{
lookup = await CreateLoadedAssemblyShortNameGroupLookupAsync().ConfigureAwait(false);
lookup = LazyInit.GetOrSet(ref asmLookupByShortNameGrouped, lookup);
}
if (!lookup.TryGetValue(reference.Name, out var candidates))
return null;
return candidates.FirstOrDefault(c => c.version >= reference.Version).module ?? candidates.Last().module;
}
private async Task<Dictionary<string, PEFile>> CreateLoadedAssemblyLookupAsync(bool shortNames)
{
var result = new Dictionary<string, PEFile>(StringComparer.OrdinalIgnoreCase);
foreach (LoadedAssembly loaded in assemblies)
{
try
{
var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
if (module == null)
continue;
var reader = module.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
string tfm = await loaded.GetTargetFrameworkIdAsync().ConfigureAwait(false);
if (tfm.StartsWith(".NETFramework,Version=v4.", StringComparison.Ordinal))
{
tfm = ".NETFramework,Version=v4";
}
string key = tfm + ";"
+ (shortNames ? module.Name : module.FullName);
if (!result.ContainsKey(key))
{
result.Add(key, module);
}
}
catch (BadImageFormatException)
{
continue;
}
}
return result;
}
private async Task<Dictionary<string, List<(PEFile module, Version version)>>> CreateLoadedAssemblyShortNameGroupLookupAsync()
{
var result = new Dictionary<string, List<(PEFile module, Version version)>>(StringComparer.OrdinalIgnoreCase);
foreach (LoadedAssembly loaded in assemblies)
{
try
{
var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
var asmDef = reader.GetAssemblyDefinition();
var asmDefName = reader.GetString(asmDef.Name);
var line = (module!, version: asmDef.Version);
if (!result.TryGetValue(asmDefName, out var existing))
{
existing = new List<(PEFile module, Version version)>();
result.Add(asmDefName, existing);
existing.Add(line);
continue;
}
int index = existing.BinarySearch(line.version, l => l.version);
index = index < 0 ? ~index : index + 1;
existing.Insert(index, line);
}
catch (BadImageFormatException)
{
continue;
}
}
return result;
}
/// <summary>
/// Gets all loaded assemblies recursively, including assemblies found in bundles or packages.
/// </summary>
public async Task<IList<LoadedAssembly>> GetAllAssembliesAsync()
{
var results = new List<LoadedAssembly>(assemblies.Length);
foreach (var asm in assemblies)
{
LoadedAssembly.LoadResult result;
try
{
result = await asm.GetLoadResultAsync().ConfigureAwait(false);
}
catch
{
results.Add(asm);
continue;
}
if (result.Package != null)
{
AddDescendants(result.Package.RootFolder);
}
else if (result.PEFile != null)
{
results.Add(asm);
}
}
void AddDescendants(PackageFolder folder)
{
foreach (var subFolder in folder.Folders)
{
AddDescendants(subFolder);
}
foreach (var entry in folder.Entries)
{
if (!entry.Name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
continue;
var asm = folder.ResolveFileName(entry.Name);
if (asm == null)
continue;
results.Add(asm);
}
}
return results;
}
}
}

2
ILSpy/Languages/Language.cs

@ -541,7 +541,7 @@ namespace ICSharpCode.ILSpy @@ -541,7 +541,7 @@ namespace ICSharpCode.ILSpy
public virtual CodeMappingInfo GetCodeMappingInfo(PEFile module, SRM.EntityHandle member)
{
var declaringType = member.GetDeclaringType(module.Metadata);
var declaringType = (SRM.TypeDefinitionHandle)member.GetDeclaringType(module.Metadata);
if (declaringType.IsNil && member.Kind == SRM.HandleKind.TypeDefinition)
{

90
ILSpy/LoadedAssembly.cs

@ -470,11 +470,11 @@ namespace ICSharpCode.ILSpy @@ -470,11 +470,11 @@ namespace ICSharpCode.ILSpy
readonly IAssemblyResolver? providedAssemblyResolver;
readonly AssemblyList assemblyList;
readonly LoadedAssembly[] alreadyLoadedAssemblies;
readonly AssemblyListSnapshot alreadyLoadedAssemblies;
readonly Task<string> tfmTask;
readonly ReferenceLoadInfo referenceLoadInfo;
public MyAssemblyResolver(LoadedAssembly parent, bool loadOnDemand)
public MyAssemblyResolver(LoadedAssembly parent, AssemblyListSnapshot assemblyListSnapshot, bool loadOnDemand)
{
this.parent = parent;
this.loadOnDemand = loadOnDemand;
@ -484,7 +484,7 @@ namespace ICSharpCode.ILSpy @@ -484,7 +484,7 @@ namespace ICSharpCode.ILSpy
// Note: we cache a copy of the assembly list in the constructor, so that the
// resolve calls only search-by-asm-name in the assemblies that were already loaded
// at the time of the GetResolver() call.
this.alreadyLoadedAssemblies = assemblyList.GetAssemblies();
this.alreadyLoadedAssemblies = assemblyListSnapshot;
// If we didn't do this, we'd also search in the assemblies that we just started to load
// in previous Resolve() calls; but we don't want to wait for those to be loaded.
this.tfmTask = parent.GetTargetFrameworkIdAsync();
@ -496,9 +496,6 @@ namespace ICSharpCode.ILSpy @@ -496,9 +496,6 @@ namespace ICSharpCode.ILSpy
return ResolveAsync(reference).GetAwaiter().GetResult();
}
Dictionary<string, PEFile>? asmLookupByFullName;
Dictionary<string, PEFile>? asmLookupByShortName;
/// <summary>
/// 0) if we're inside a package, look for filename.dll in parent directories
/// 1) try to find exact match by tfm + full asm name in loaded assemblies
@ -524,17 +521,9 @@ namespace ICSharpCode.ILSpy @@ -524,17 +521,9 @@ namespace ICSharpCode.ILSpy
string tfm = await tfmTask.ConfigureAwait(false);
bool isWinRT = reference.IsWindowsRuntime;
string key = tfm + ";" + (isWinRT ? reference.Name : reference.FullName);
// 1) try to find exact match by tfm + full asm name in loaded assemblies
var lookup = LazyInit.VolatileRead(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName);
if (lookup == null)
{
lookup = await CreateLoadedAssemblyLookupAsync(shortNames: isWinRT).ConfigureAwait(false);
lookup = LazyInit.GetOrSet(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName, lookup);
}
if (lookup.TryGetValue(key, out module))
module = await alreadyLoadedAssemblies.TryGetModuleAsync(reference, tfm).ConfigureAwait(false);
if (module != null)
{
referenceLoadInfo.AddMessageOnce(reference.FullName, MessageKind.Info, "Success - Found in Assembly List");
return module;
@ -556,7 +545,7 @@ namespace ICSharpCode.ILSpy @@ -556,7 +545,7 @@ namespace ICSharpCode.ILSpy
}
if (asm != null)
{
referenceLoadInfo.AddMessage(reference.ToString(), MessageKind.Info, "Success - Loading from: " + file);
referenceLoadInfo.AddMessage(reference.FullName, MessageKind.Info, "Success - Loading from: " + file);
return await asm.GetPEFileOrNullAsync().ConfigureAwait(false);
}
return null;
@ -564,63 +553,17 @@ namespace ICSharpCode.ILSpy @@ -564,63 +553,17 @@ namespace ICSharpCode.ILSpy
else
{
// Assembly not found; try to find a similar-enough already-loaded assembly
var candidates = new List<(LoadedAssembly assembly, Version version)>();
foreach (LoadedAssembly loaded in alreadyLoadedAssemblies)
{
module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
var reader = module?.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
var asmDef = reader.GetAssemblyDefinition();
var asmDefName = reader.GetString(asmDef.Name);
if (reference.Name.Equals(asmDefName, StringComparison.OrdinalIgnoreCase))
{
candidates.Add((loaded, asmDef.Version));
}
}
if (candidates.Count == 0)
module = await alreadyLoadedAssemblies.TryGetSimilarModuleAsync(reference).ConfigureAwait(false);
if (module == null)
{
referenceLoadInfo.AddMessageOnce(reference.ToString(), MessageKind.Error, "Could not find reference: " + reference);
return null;
referenceLoadInfo.AddMessageOnce(reference.FullName, MessageKind.Error, "Could not find reference: " + reference.FullName);
}
candidates.SortBy(c => c.version);
var bestCandidate = candidates.FirstOrDefault(c => c.version >= reference.Version).assembly ?? candidates.Last().assembly;
referenceLoadInfo.AddMessageOnce(reference.ToString(), MessageKind.Info, "Success - Found in Assembly List with different TFM or version: " + bestCandidate.fileName);
return await bestCandidate.GetPEFileOrNullAsync().ConfigureAwait(false);
}
}
private async Task<Dictionary<string, PEFile>> CreateLoadedAssemblyLookupAsync(bool shortNames)
{
var result = new Dictionary<string, PEFile>(StringComparer.OrdinalIgnoreCase);
foreach (LoadedAssembly loaded in alreadyLoadedAssemblies)
{
try
{
var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
if (module == null)
continue;
var reader = module.Metadata;
if (reader == null || !reader.IsAssembly)
continue;
string tfm = await loaded.GetTargetFrameworkIdAsync().ConfigureAwait(false);
string key = tfm + ";"
+ (shortNames ? module.Name : module.FullName);
if (!result.ContainsKey(key))
{
result.Add(key, module);
}
}
catch (BadImageFormatException)
else
{
continue;
referenceLoadInfo.AddMessageOnce(reference.FullName, MessageKind.Info, "Success - Found in Assembly List with different TFM or version: " + module.FileName);
}
return module;
}
return result;
}
public PEFile? ResolveModule(PEFile mainModule, string moduleName)
@ -659,7 +602,7 @@ namespace ICSharpCode.ILSpy @@ -659,7 +602,7 @@ namespace ICSharpCode.ILSpy
else
{
// Module does not exist on disk, look for one with a matching name in the assemblylist:
foreach (LoadedAssembly loaded in alreadyLoadedAssemblies)
foreach (LoadedAssembly loaded in alreadyLoadedAssemblies.Assemblies)
{
var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false);
var reader = module?.Metadata;
@ -679,7 +622,12 @@ namespace ICSharpCode.ILSpy @@ -679,7 +622,12 @@ namespace ICSharpCode.ILSpy
public IAssemblyResolver GetAssemblyResolver(bool loadOnDemand = true)
{
return new MyAssemblyResolver(this, loadOnDemand);
return new MyAssemblyResolver(this, AssemblyList.GetSnapshot(), loadOnDemand);
}
internal IAssemblyResolver GetAssemblyResolver(AssemblyListSnapshot snapshot, bool loadOnDemand = true)
{
return new MyAssemblyResolver(this, snapshot, loadOnDemand);
}
private UniversalAssemblyResolver GetUniversalResolver()

5
ILSpy/LoadedAssemblyExtensions.cs

@ -31,6 +31,11 @@ namespace ICSharpCode.ILSpy @@ -31,6 +31,11 @@ namespace ICSharpCode.ILSpy
return GetLoadedAssembly(file).GetAssemblyResolver(loadOnDemand);
}
internal static IAssemblyResolver GetAssemblyResolver(this PEFile file, AssemblyListSnapshot snapshot, bool loadOnDemand = true)
{
return GetLoadedAssembly(file).GetAssemblyResolver(snapshot, loadOnDemand);
}
public static IDebugInfoProvider GetDebugInfoOrNull(this PEFile file)
{
return GetLoadedAssembly(file).GetDebugInfoOrNull();

Loading…
Cancel
Save