Browse Source

TypeUsedByAnalyzer: Refactoring and optimization, we now use raw metadata information whereever possible.

pull/2643/head
Siegfried Pammer 3 years ago
parent
commit
c525b86da4
  1. 7
      ILSpy/Analyzers/AnalyzerScope.cs
  2. 158
      ILSpy/Analyzers/Builtin/FindTypeInAttributeDecoder.cs
  3. 297
      ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs

7
ILSpy/Analyzers/AnalyzerScope.cs

@ -84,6 +84,11 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -84,6 +84,11 @@ namespace ICSharpCode.ILSpy.Analyzers
.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)
@ -97,7 +102,7 @@ namespace ICSharpCode.ILSpy.Analyzers @@ -97,7 +102,7 @@ namespace ICSharpCode.ILSpy.Analyzers
{
foreach (var module in GetModulesInScope(ct))
{
var typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver(assemblyListSnapshot, loadOnDemand: false));
var typeSystem = ConstructTypeSystem(module);
foreach (var type in typeSystem.MainModule.TypeDefinitions)
{
yield return type;

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;
}
}

297
ILSpy/Analyzers/Builtin/TypeUsedByAnalyzer.cs

@ -20,8 +20,10 @@ using System; @@ -20,8 +20,10 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
@ -39,188 +41,213 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin @@ -39,188 +41,213 @@ namespace ICSharpCode.ILSpy.Analyzers.Builtin
Debug.Assert(analyzedSymbol is ITypeDefinition);
var analyzedType = (ITypeDefinition)analyzedSymbol;
var scope = context.GetScopeOf(analyzedType);
foreach (var type in scope.GetTypesInScope(context.CancellationToken))
context.SortResults = true;
return scope.GetModulesInScope(context.CancellationToken)
.AsParallel().AsOrdered()
.SelectMany(AnalyzeModuleAndFilter);
IEnumerable<ISymbol> AnalyzeModuleAndFilter(PEFile module)
{
foreach (var result in ScanType(analyzedType, 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);
}
var visitor = new TypeDefinitionUsedVisitor(analyzedEntity, topLevelOnly: false);
found |= FindUsesInGenericConstraints(metadata, td.GetGenericParameters(), decoder);
if (found)
yield return typeSystem.MainModule.GetDefinition(h);
}
foreach (var h in metadata.MethodDefinitions)
{
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;
}
}
foreach (var bt in type.DirectBaseTypes)
foreach (var h in metadata.FieldDefinitions)
{
bt.AcceptVisitor(visitor);
var fd = metadata.GetFieldDefinition(h);
if (fd.DecodeSignature(decoder, default))
yield return typeSystem.MainModule.GetDefinition(h);
}
if (visitor.Found || ScanAttributes(visitor, type.GetAttributes()))
yield return type;
foreach (var h in metadata.PropertyDefinitions)
{
var pd = metadata.GetPropertyDefinition(h);
var psig = pd.DecodeSignature(decoder, default);
if (FindTypeDecoder.AnyInMethodSignature(psig))
yield return typeSystem.MainModule.GetDefinition(h);
}
foreach (var member in type.Members)
foreach (var h in metadata.EventDefinitions)
{
visitor.Found = false;
VisitMember(visitor, member, context);
if (visitor.Found)
yield return member;
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)
{
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 (!visitor.Found)
visitor.Found |= ScanMethodBody(visitor.TypeDefinition, method, context.GetMethodBody(method));
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 (!visitor.Found && property.CanGet)
else
{
ScanAttributes(visitor, property.Getter.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, property.Getter.GetReturnTypeAttributes());
if (!visitor.Found)
visitor.Found |= ScanMethodBody(visitor.TypeDefinition, property.Getter, context.GetMethodBody(property.Getter));
var parent = AnalyzerHelpers.GetParentEntity(typeSystem, customAttribute);
if (parent != null)
yield return parent;
}
if (!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())
{
ScanAttributes(visitor, property.Setter.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, property.Setter.GetReturnTypeAttributes());
if (!visitor.Found)
visitor.Found |= ScanMethodBody(visitor.TypeDefinition, property.Setter, context.GetMethodBody(property.Setter));
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 (!visitor.Found && @event.CanAdd)
{
ScanAttributes(visitor, @event.AddAccessor.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, @event.AddAccessor.GetReturnTypeAttributes());
if (!visitor.Found)
visitor.Found |= ScanMethodBody(visitor.TypeDefinition, @event.AddAccessor, context.GetMethodBody(@event.AddAccessor));
}
private static bool AnalyzeCustomAttributeValue(CustomAttributeValue<TokenSearchResult> attribute)
{
foreach (var fa in attribute.FixedArguments)
{
if (CheckAttributeValue(fa.Value))
return true;
}
if (!visitor.Found && @event.CanRemove)
{
ScanAttributes(visitor, @event.RemoveAccessor.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, @event.RemoveAccessor.GetReturnTypeAttributes());
if (!visitor.Found)
visitor.Found |= ScanMethodBody(visitor.TypeDefinition, @event.RemoveAccessor, context.GetMethodBody(@event.RemoveAccessor));
}
foreach (var na in attribute.NamedArguments)
{
if (CheckAttributeValue(na.Value))
return true;
}
return false;
if (!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)
{
ScanAttributes(visitor, @event.InvokeAccessor.GetAttributes());
if (!visitor.Found)
ScanAttributes(visitor, @event.InvokeAccessor.GetReturnTypeAttributes());
if (!visitor.Found)
visitor.Found |= ScanMethodBody(visitor.TypeDefinition, @event.InvokeAccessor, context.GetMethodBody(@event.InvokeAccessor));
if (CheckAttributeValue(element.Value))
return true;
}
}
break;
return false;
}
}
bool ScanMethodBody(ITypeDefinition analyzedType, IMethod method, MethodBodyBlock methodBody)
static bool ScanMethodBody(ITypeDefinition analyzedType, PEFile module, in MethodDefinition md, FindTypeDecoder decoder)
{
if (methodBody == null)
if (!md.HasBody())
return false;
var module = (MetadataModule)method.ParentModule;
var metadata = module.PEFile.Metadata;
var decoder = new FindTypeDecoder(module, analyzedType);
var methodBody = module.Reader.GetMethodBody(md.RelativeVirtualAddress);
var metadata = module.Metadata;
if (!methodBody.LocalSignature.IsNil)
{

Loading…
Cancel
Save