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