| File: Linker\TypeNameResolver.cs | Web Access |
| Project: src\runtime\src\tools\illink\src\linker\Mono.Linker.csproj (illink) |
// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Mono.Cecil; using AssemblyNameInfo = System.Reflection.Metadata.AssemblyNameInfo; using TypeName = System.Reflection.Metadata.TypeName; using TypeNameParseOptions = System.Reflection.Metadata.TypeNameParseOptions; #nullable enable namespace Mono.Linker { internal sealed partial class TypeNameResolver { readonly ITryResolveMetadata _metadataResolver; readonly ITryResolveAssemblyName _assemblyResolver; private static readonly TypeNameParseOptions s_typeNameParseOptions = new() { MaxNodes = int.MaxValue }; public readonly record struct TypeResolutionRecord(AssemblyDefinition ReferringAssembly, TypeDefinition ResolvedType); public TypeNameResolver(ITryResolveMetadata metadataResolver, ITryResolveAssemblyName assemblyNameResolver) { _metadataResolver = metadataResolver; _assemblyResolver = assemblyNameResolver; } public bool TryResolveTypeName( AssemblyDefinition assembly, string typeNameString, bool fallbackToCoreLib, [NotNullWhen(true)] out TypeReference? typeReference, [NotNullWhen(true)] out List<TypeResolutionRecord>? typeResolutionRecords) { typeResolutionRecords = new List<TypeResolutionRecord>(); if (!TypeName.TryParse(typeNameString, out TypeName? parsedTypeName, s_typeNameParseOptions)) { typeReference = null; return false; } // Assembly.GetType (signaled by !fallbackToCoreLib) rejects top-level assembly-qualified // names at runtime (Argument_AssemblyGetTypeCannotSpecifyAssembly). if (!fallbackToCoreLib && parsedTypeName.AssemblyName is not null) { typeReference = null; typeResolutionRecords = null; return false; } typeReference = ResolveTypeName(assembly, parsedTypeName, typeResolutionRecords, fallbackToCoreLib); if (typeReference == null) typeResolutionRecords = null; return typeReference != null; } TypeReference? ResolveTypeName(AssemblyDefinition originalAssembly, TypeName? typeName, List<TypeResolutionRecord> typeResolutionRecords, bool fallbackToCoreLib) { if (typeName == null) return null; AssemblyDefinition? assembly = originalAssembly; if (typeName.AssemblyName is AssemblyNameInfo assemblyName) // In this case we ignore the assembly parameter since the type name has assembly in it assembly = _assemblyResolver.TryResolve(assemblyName.Name); if (assembly == null) return null; if (typeName.IsConstructedGenericType) { var genericTypeRef = ResolveTypeName(assembly, typeName.GetGenericTypeDefinition(), typeResolutionRecords, fallbackToCoreLib); if (genericTypeRef == null) return null; Debug.Assert(genericTypeRef is TypeDefinition); var genericInstanceType = new GenericInstanceType(genericTypeRef); foreach (var arg in typeName.GetGenericArguments()) { var genericArgument = ResolveTypeName(assembly, arg, typeResolutionRecords, fallbackToCoreLib); if (genericArgument == null) return null; genericInstanceType.GenericArguments.Add(genericArgument); } return genericInstanceType; } else if (typeName.IsArray || typeName.IsPointer || typeName.IsByRef) { var elementType = ResolveTypeName(assembly, typeName.GetElementType(), typeResolutionRecords, fallbackToCoreLib); if (elementType == null) return null; if (typeName.IsArray) return typeName.IsSZArray ? new ArrayType(elementType) : new ArrayType(elementType, typeName.GetArrayRank()); if (typeName.IsByRef) return new ByReferenceType(elementType); if (typeName.IsPointer) return new PointerType(elementType); Debug.Fail("Unreachable"); return null; } Debug.Assert(typeName.IsSimple); TypeName topLevelTypeName = typeName; while (topLevelTypeName.IsNested) topLevelTypeName = topLevelTypeName.DeclaringType; Debug.Assert(topLevelTypeName.AssemblyName == typeName.AssemblyName); TypeDefinition? resolvedType = GetSimpleTypeFromModule(typeName, assembly.MainModule); // True type references (like generics and arrays) don't count as actually resolved types, they're just wrappers // so only record type resolutions for types which are actually resolved. if (resolvedType != null) { return resolvedType; } if (!fallbackToCoreLib) return null; // If it didn't resolve and wasn't assembly-qualified, we also try core library var coreLibrary = _metadataResolver.TryResolve(originalAssembly.MainModule.TypeSystem.Object)?.Module.Assembly; if (coreLibrary is null) return null; if (topLevelTypeName.AssemblyName == null && assembly != coreLibrary) { resolvedType = GetSimpleTypeFromModule(typeName, coreLibrary.MainModule); if (resolvedType != null) { return resolvedType; } } return null; TypeDefinition? GetSimpleTypeFromModule(TypeName typeName, ModuleDefinition module) { int initialResolutionRecordCount = typeResolutionRecords.Count; TypeDefinition? resolvedType; if (typeName.IsNested) { TypeDefinition? declaringType = GetSimpleTypeFromModule(typeName.DeclaringType, module); if (declaringType == null) return null; resolvedType = GetNestedType(declaringType, TypeName.Unescape(typeName.Name)); } else { resolvedType = module.ResolveType(TypeName.Unescape(typeName.FullName), _metadataResolver); } if (resolvedType != null) { typeResolutionRecords.Add(new(module.Assembly, resolvedType)); } else if (typeResolutionRecords.Count != initialResolutionRecordCount) { typeResolutionRecords.RemoveRange(initialResolutionRecordCount, typeResolutionRecords.Count - initialResolutionRecordCount); } return resolvedType; } TypeDefinition? GetNestedType(TypeDefinition type, string nestedTypeName) { if (!type.HasNestedTypes) return null; foreach (var nestedType in type.NestedTypes) { if (nestedType.Name == nestedTypeName) return nestedType; } return null; } } } }