mirror of
https://github.com/icsharpcode/ILSpy.git
synced 2026-10-12 11:09:54 +00:00
Backfills the standard MIT X11 header on hand-written files that never got one, attributing each to its first-commit author and year from git history. Code vendored from dotnet/runtime and Humanizr/Humanizer gets its origin's license lines and a provenance note instead. Generated files (Resources.Designer.cs, the version-info template), tool-managed suppression files, and BAML test-case fixtures intentionally stay header-less. Assisted-by: Claude:claude-fable-5:Claude Code
437 lines
13 KiB
C#
437 lines
13 KiB
C#
// 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;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using System.Reflection.Metadata;
|
|
|
|
using ICSharpCode.Decompiler.Disassembler;
|
|
using ICSharpCode.Decompiler.Metadata;
|
|
using ICSharpCode.Decompiler.TypeSystem;
|
|
using ICSharpCode.Decompiler.TypeSystem.Implementation;
|
|
|
|
using static ICSharpCode.Decompiler.Metadata.ILOpCodeExtensions;
|
|
|
|
#nullable enable
|
|
|
|
namespace ICSharpCode.Decompiler.CSharp
|
|
{
|
|
class RequiredNamespaceCollector
|
|
{
|
|
static readonly Decompiler.TypeSystem.GenericContext genericContext = default;
|
|
|
|
readonly HashSet<string> namespaces;
|
|
readonly HashSet<IType> visitedTypes = new HashSet<IType>();
|
|
|
|
public RequiredNamespaceCollector(HashSet<string> namespaces)
|
|
{
|
|
this.namespaces = namespaces;
|
|
for (int i = 0; i < KnownTypeReference.KnownTypeCodeCount; i++)
|
|
{
|
|
var ktr = KnownTypeReference.Get((KnownTypeCode)i);
|
|
if (ktr == null)
|
|
continue;
|
|
namespaces.Add(ktr.Namespace);
|
|
}
|
|
}
|
|
|
|
public static void CollectNamespaces(MetadataModule module, HashSet<string> namespaces)
|
|
{
|
|
var collector = new RequiredNamespaceCollector(namespaces);
|
|
foreach (var type in module.TypeDefinitions)
|
|
{
|
|
collector.CollectNamespaces(type, module, (CodeMappingInfo?)null);
|
|
}
|
|
collector.HandleAttributes(module.GetAssemblyAttributes());
|
|
collector.HandleAttributes(module.GetModuleAttributes());
|
|
}
|
|
|
|
public static void CollectAttributeNamespaces(MetadataModule module, HashSet<string> namespaces)
|
|
{
|
|
var collector = new RequiredNamespaceCollector(namespaces);
|
|
collector.HandleAttributes(module.GetAssemblyAttributes());
|
|
collector.HandleAttributes(module.GetModuleAttributes());
|
|
}
|
|
|
|
public static void CollectNamespaces(IEntity? entity, MetadataModule module, HashSet<string> namespaces)
|
|
{
|
|
var collector = new RequiredNamespaceCollector(namespaces);
|
|
collector.CollectNamespaces(entity, module);
|
|
}
|
|
|
|
void CollectNamespaces(IEntity? entity, MetadataModule module, CodeMappingInfo? mappingInfo = null)
|
|
{
|
|
if (entity == null || entity.MetadataToken.IsNil || module.MetadataFile is not MetadataFile corFile)
|
|
return;
|
|
if (mappingInfo == null)
|
|
mappingInfo = CSharpDecompiler.GetCodeMappingInfo(corFile, entity.MetadataToken);
|
|
switch (entity)
|
|
{
|
|
case ITypeDefinition td:
|
|
var extensionInfo = td.DeclaringTypeDefinition?.ExtensionInfo ?? td.DeclaringTypeDefinition?.DeclaringTypeDefinition?.ExtensionInfo;
|
|
if (extensionInfo?.IsExtensionMarkerType(td, out _) == true || extensionInfo?.IsExtensionGroupType(td) == true)
|
|
{
|
|
td = extensionInfo.Container;
|
|
}
|
|
|
|
namespaces.Add(td.Namespace);
|
|
HandleAttributes(td.GetAttributes());
|
|
HandleTypeParameters(td.TypeParameters);
|
|
|
|
foreach (var baseType in td.DirectBaseTypes)
|
|
{
|
|
CollectNamespacesForTypeReference(baseType);
|
|
}
|
|
|
|
foreach (var nestedType in td.NestedTypes)
|
|
{
|
|
bool isGroupOrMarker = extensionInfo?.IsExtensionGroupType(nestedType) == true || extensionInfo?.IsExtensionMarkerType(nestedType, out _) == true;
|
|
if (isGroupOrMarker)
|
|
continue;
|
|
CollectNamespaces(nestedType, module, mappingInfo);
|
|
}
|
|
|
|
foreach (var field in td.Fields)
|
|
{
|
|
CollectNamespaces(field, module, mappingInfo);
|
|
}
|
|
|
|
foreach (var property in td.Properties)
|
|
{
|
|
CollectNamespaces(property, module, mappingInfo);
|
|
}
|
|
|
|
foreach (var @event in td.Events)
|
|
{
|
|
CollectNamespaces(@event, module, mappingInfo);
|
|
}
|
|
|
|
foreach (var method in td.Methods)
|
|
{
|
|
CollectNamespaces(method, module, mappingInfo);
|
|
}
|
|
break;
|
|
case IField field:
|
|
HandleAttributes(field.GetAttributes());
|
|
CollectNamespacesForTypeReference(field.ReturnType);
|
|
break;
|
|
case IMethod method:
|
|
var parts = mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken).ToList();
|
|
foreach (var part in parts)
|
|
{
|
|
var partMethod = module.ResolveMethod(part, genericContext);
|
|
HandleAttributes(partMethod.GetAttributes());
|
|
HandleAttributes(partMethod.GetReturnTypeAttributes());
|
|
CollectNamespacesForTypeReference(partMethod.ReturnType);
|
|
foreach (var param in partMethod.Parameters)
|
|
{
|
|
HandleAttributes(param.GetAttributes());
|
|
CollectNamespacesForTypeReference(param.Type);
|
|
}
|
|
HandleTypeParameters(partMethod.TypeParameters);
|
|
HandleOverrides(part.GetMethodImplementations(module.metadata), module);
|
|
var methodDef = module.metadata.GetMethodDefinition(part);
|
|
if (method.HasBody)
|
|
{
|
|
MethodBodyBlock body;
|
|
try
|
|
{
|
|
body = module.MetadataFile.GetMethodBody(methodDef.RelativeVirtualAddress);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
continue;
|
|
}
|
|
CollectNamespacesFromMethodBody(body, module);
|
|
}
|
|
}
|
|
break;
|
|
case IProperty property:
|
|
HandleAttributes(property.GetAttributes());
|
|
CollectNamespacesForTypeReference(property.ReturnType);
|
|
CollectNamespaces(property.Getter, module, mappingInfo);
|
|
CollectNamespaces(property.Setter, module, mappingInfo);
|
|
break;
|
|
case IEvent @event:
|
|
HandleAttributes(@event.GetAttributes());
|
|
CollectNamespacesForTypeReference(@event.ReturnType);
|
|
CollectNamespaces(@event.AddAccessor, module, mappingInfo);
|
|
CollectNamespaces(@event.RemoveAccessor, module, mappingInfo);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HandleOverrides(ImmutableArray<MethodImplementationHandle> immutableArray, MetadataModule module)
|
|
{
|
|
foreach (var h in immutableArray)
|
|
{
|
|
var methodImpl = module.metadata.GetMethodImplementation(h);
|
|
CollectNamespacesForTypeReference(module.ResolveType(methodImpl.Type, genericContext));
|
|
CollectNamespacesForMemberReference(module.ResolveMethod(methodImpl.MethodBody, genericContext));
|
|
CollectNamespacesForMemberReference(module.ResolveMethod(methodImpl.MethodDeclaration, genericContext));
|
|
}
|
|
}
|
|
|
|
void CollectNamespacesForTypeReference(IType type)
|
|
{
|
|
if (!visitedTypes.Add(type))
|
|
return;
|
|
switch (type)
|
|
{
|
|
case ParameterizedType parameterizedType:
|
|
namespaces.Add(parameterizedType.Namespace);
|
|
CollectNamespacesForTypeReference(parameterizedType.GenericType);
|
|
foreach (var arg in parameterizedType.TypeArguments)
|
|
CollectNamespacesForTypeReference(arg);
|
|
return; // no need to collect base types again
|
|
case TypeWithElementType typeWithElementType:
|
|
CollectNamespacesForTypeReference(typeWithElementType.ElementType);
|
|
break;
|
|
case TupleType tupleType:
|
|
foreach (var elementType in tupleType.ElementTypes)
|
|
{
|
|
CollectNamespacesForTypeReference(elementType);
|
|
}
|
|
break;
|
|
case FunctionPointerType fnPtrType:
|
|
CollectNamespacesForTypeReference(fnPtrType.ReturnType);
|
|
foreach (var paramType in fnPtrType.ParameterTypes)
|
|
{
|
|
CollectNamespacesForTypeReference(paramType);
|
|
}
|
|
break;
|
|
default:
|
|
namespaces.Add(type.Namespace);
|
|
break;
|
|
}
|
|
foreach (var baseType in type.GetAllBaseTypes())
|
|
{
|
|
namespaces.Add(baseType.Namespace);
|
|
}
|
|
}
|
|
|
|
public static void CollectNamespaces(EntityHandle entity, MetadataModule module, HashSet<string> namespaces)
|
|
{
|
|
if (entity.IsNil)
|
|
return;
|
|
CollectNamespaces(module.ResolveEntity(entity, genericContext), module, namespaces);
|
|
}
|
|
|
|
void HandleAttributes(IEnumerable<IAttribute> attributes)
|
|
{
|
|
foreach (var attr in attributes)
|
|
{
|
|
namespaces.Add(attr.AttributeType.Namespace);
|
|
foreach (var arg in attr.FixedArguments)
|
|
{
|
|
HandleAttributeValue(arg.Type, arg.Value);
|
|
}
|
|
foreach (var arg in attr.NamedArguments)
|
|
{
|
|
HandleAttributeValue(arg.Type, arg.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void HandleAttributeValue(IType type, object? value)
|
|
{
|
|
CollectNamespacesForTypeReference(type);
|
|
if (value is IType typeofType)
|
|
CollectNamespacesForTypeReference(typeofType);
|
|
if (value is ImmutableArray<CustomAttributeTypedArgument<IType>> arr)
|
|
{
|
|
foreach (var element in arr)
|
|
{
|
|
HandleAttributeValue(element.Type, element.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void HandleTypeParameters(IEnumerable<ITypeParameter> typeParameters)
|
|
{
|
|
foreach (var typeParam in typeParameters)
|
|
{
|
|
HandleAttributes(typeParam.GetAttributes());
|
|
|
|
foreach (var constraint in typeParam.DirectBaseTypes)
|
|
{
|
|
CollectNamespacesForTypeReference(constraint);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CollectNamespacesFromMethodBody(MethodBodyBlock method, MetadataModule module)
|
|
{
|
|
var metadata = module.metadata;
|
|
var instructions = method.GetILReader();
|
|
|
|
if (!method.LocalSignature.IsNil)
|
|
{
|
|
ImmutableArray<IType> localSignature;
|
|
try
|
|
{
|
|
localSignature = module.DecodeLocalSignature(method.LocalSignature, genericContext);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
// Issue #1211: ignore invalid local signatures
|
|
localSignature = ImmutableArray<IType>.Empty;
|
|
}
|
|
foreach (var type in localSignature)
|
|
CollectNamespacesForTypeReference(type);
|
|
}
|
|
|
|
foreach (var region in method.ExceptionRegions)
|
|
{
|
|
if (region.CatchType.IsNil)
|
|
continue;
|
|
IType ty;
|
|
try
|
|
{
|
|
ty = module.ResolveType(region.CatchType, genericContext);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
continue;
|
|
}
|
|
CollectNamespacesForTypeReference(ty);
|
|
}
|
|
|
|
while (instructions.RemainingBytes > 0)
|
|
{
|
|
ILOpCode opCode;
|
|
try
|
|
{
|
|
opCode = instructions.DecodeOpCode();
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
return;
|
|
}
|
|
switch (opCode.GetOperandType())
|
|
{
|
|
case OperandType.Field:
|
|
case OperandType.Method:
|
|
case OperandType.Sig:
|
|
case OperandType.Tok:
|
|
case OperandType.Type:
|
|
EntityHandle handle;
|
|
try
|
|
{
|
|
handle = MetadataTokenHelpers.EntityHandleOrNil(instructions.ReadInt32());
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
return;
|
|
}
|
|
if (handle.IsNil)
|
|
break;
|
|
switch (handle.Kind)
|
|
{
|
|
case HandleKind.TypeDefinition:
|
|
case HandleKind.TypeReference:
|
|
case HandleKind.TypeSpecification:
|
|
IType type;
|
|
try
|
|
{
|
|
type = module.ResolveType(handle, genericContext);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
break;
|
|
}
|
|
CollectNamespacesForTypeReference(type);
|
|
break;
|
|
case HandleKind.FieldDefinition:
|
|
case HandleKind.MethodDefinition:
|
|
case HandleKind.MethodSpecification:
|
|
case HandleKind.MemberReference:
|
|
IMember? member;
|
|
try
|
|
{
|
|
member = module.ResolveEntity(handle, genericContext) as IMember;
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
break;
|
|
}
|
|
CollectNamespacesForMemberReference(member);
|
|
break;
|
|
case HandleKind.StandaloneSignature:
|
|
StandaloneSignature sig;
|
|
try
|
|
{
|
|
sig = metadata.GetStandaloneSignature((StandaloneSignatureHandle)handle);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
break;
|
|
}
|
|
if (sig.GetKind() == StandaloneSignatureKind.Method)
|
|
{
|
|
FunctionPointerType fpt;
|
|
try
|
|
{
|
|
(_, fpt) = module.DecodeMethodSignature((StandaloneSignatureHandle)handle, genericContext);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
break;
|
|
}
|
|
CollectNamespacesForTypeReference(fpt);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
try
|
|
{
|
|
instructions.SkipOperand(opCode);
|
|
}
|
|
catch (BadImageFormatException)
|
|
{
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CollectNamespacesForMemberReference(IMember? member)
|
|
{
|
|
switch (member)
|
|
{
|
|
case IField field:
|
|
CollectNamespacesForTypeReference(field.DeclaringType);
|
|
CollectNamespacesForTypeReference(field.ReturnType);
|
|
break;
|
|
case IMethod method:
|
|
CollectNamespacesForTypeReference(method.DeclaringType);
|
|
CollectNamespacesForTypeReference(method.ReturnType);
|
|
foreach (var param in method.Parameters)
|
|
CollectNamespacesForTypeReference(param.Type);
|
|
foreach (var arg in method.TypeArguments)
|
|
CollectNamespacesForTypeReference(arg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|