diff --git a/ICSharpCode.Decompiler/Documentation/GetPotentiallyNestedClassTypeReference.cs b/ICSharpCode.Decompiler/Documentation/GetPotentiallyNestedClassTypeReference.cs index 4394157f0..46b11db6c 100644 --- a/ICSharpCode.Decompiler/Documentation/GetPotentiallyNestedClassTypeReference.cs +++ b/ICSharpCode.Decompiler/Documentation/GetPotentiallyNestedClassTypeReference.cs @@ -18,6 +18,8 @@ using System; using System.Linq; +using System.Reflection.Metadata; +using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem.Implementation; @@ -30,7 +32,7 @@ namespace ICSharpCode.Decompiler.Documentation /// The type parameter count only applies to the innermost type, all outer types must be non-generic. /// [Serializable] - class GetPotentiallyNestedClassTypeReference : ITypeReference + public class GetPotentiallyNestedClassTypeReference : ITypeReference { readonly string typeName; readonly int typeParameterCount; @@ -67,5 +69,49 @@ namespace ICSharpCode.Decompiler.Documentation // give back a guessed namespace/type name return new UnknownType(typeName.Substring(0, idx), typeName.Substring(idx + 1), typeParameterCount); } + + /// + /// Resolves the type reference within the context of the given PE file. + /// + /// Either TypeDefinitionHandle, if the type is defined in the module or ExportedTypeHandle, + /// if the module contains a type forwarder. Returns a nil handle, if the type was not found. + public EntityHandle ResolveInPEFile(PEFile module) + { + string[] parts = typeName.Split('.'); + for (int i = parts.Length - 1; i >= 0; i--) { + string ns = string.Join(".", parts, 0, i); + string name = parts[i]; + int topLevelTPC = (i == parts.Length - 1 ? typeParameterCount : 0); + var topLevelName = new TopLevelTypeName(ns, name, topLevelTPC); + var typeHandle = module.GetTypeDefinition(topLevelName); + + for (int j = i + 1; j < parts.Length && !typeHandle.IsNil; j++) { + int tpc = (j == parts.Length - 1 ? typeParameterCount : 0); + var typeDef = module.Metadata.GetTypeDefinition(typeHandle); + string lookupName = parts[j] + (tpc > 0 ? "`" + tpc : ""); + typeHandle = typeDef.GetNestedTypes().FirstOrDefault(n => IsEqualShortName(n, module.Metadata, lookupName)); + } + + if (!typeHandle.IsNil) + return typeHandle; + FullTypeName typeName = topLevelName; + for (int j = i + 1; j < parts.Length; j++) { + int tpc = (j == parts.Length - 1 ? typeParameterCount : 0); + typeName = typeName.NestedType(parts[j], tpc); + } + + var exportedType = module.GetTypeForwarder(typeName); + if (!exportedType.IsNil) + return exportedType; + } + + return default; + + bool IsEqualShortName(TypeDefinitionHandle h, MetadataReader metadata, string name) + { + var nestedType = metadata.GetTypeDefinition(h); + return metadata.StringComparer.Equals(nestedType.Name, name); + } + } } } diff --git a/ICSharpCode.Decompiler/SRMExtensions.cs b/ICSharpCode.Decompiler/SRMExtensions.cs index cb43e3691..07600038a 100644 --- a/ICSharpCode.Decompiler/SRMExtensions.cs +++ b/ICSharpCode.Decompiler/SRMExtensions.cs @@ -241,9 +241,14 @@ namespace ICSharpCode.Decompiler public static FullTypeName GetFullTypeName(this ExportedType type, MetadataReader metadata) { - string ns = type.Namespace.IsNil ? "" : metadata.GetString(type.Namespace); string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(metadata.GetString(type.Name), out int typeParameterCount); - return new TopLevelTypeName(ns, name, typeParameterCount); + if (type.Implementation.Kind == HandleKind.ExportedType) { + var outerType = metadata.GetExportedType((ExportedTypeHandle)type.Implementation); + return outerType.GetFullTypeName(metadata).NestedType(name, typeParameterCount); + } else { + string ns = type.Namespace.IsNil ? "" : metadata.GetString(type.Namespace); + return new TopLevelTypeName(ns, name, typeParameterCount); + } } public static bool IsAnonymousType(this TypeDefinition type, MetadataReader metadata) diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs index 0b0add19a..d5cbbddbd 100644 --- a/ILSpy/MainWindow.xaml.cs +++ b/ILSpy/MainWindow.xaml.cs @@ -23,6 +23,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; +using System.Reflection.Metadata; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -287,12 +288,25 @@ namespace ICSharpCode.ILSpy } } } else { + ITypeReference typeRef = null; + IMemberReference memberRef = null; + if (args.NavigateTo.StartsWith("T:", StringComparison.Ordinal)) { + typeRef = IdStringProvider.ParseTypeName(args.NavigateTo); + } else { + memberRef = IdStringProvider.ParseMemberIdString(args.NavigateTo); + typeRef = memberRef.DeclaringTypeReference; + } foreach (LoadedAssembly asm in commandLineLoadedAssemblies) { - var def = asm.GetPEFileOrNull(); - if (def != null) { - var compilation = new SimpleCompilation(def, MinimalCorlib.Instance); - var mr = IdStringProvider.FindEntity(args.NavigateTo, new SimpleTypeResolveContext(compilation)); - if (mr != null) { + var module = asm.GetPEFileOrNull(); + if (CanResolveTypeInPEFile(module, typeRef, out var typeHandle)) { + IEntity mr = null; + ICompilation compilation = typeHandle.Kind == HandleKind.ExportedType + ? new DecompilerTypeSystem(module, module.GetAssemblyResolver()) + : new SimpleCompilation(module, MinimalCorlib.Instance); + mr = memberRef == null + ? typeRef.Resolve(new SimpleTypeResolveContext(compilation)) as ITypeDefinition + : (IEntity)memberRef.Resolve(new SimpleTypeResolveContext(compilation)); + if (mr != null && mr.ParentModule.PEFile != null) { found = true; // Defer JumpToReference call to allow an assembly that was loaded while // resolving a type-forwarder in FindMemberByKey to appear in the assembly list. @@ -320,6 +334,30 @@ namespace ICSharpCode.ILSpy commandLineLoadedAssemblies.Clear(); // clear references once we don't need them anymore } + private bool CanResolveTypeInPEFile(PEFile module, ITypeReference typeRef, out EntityHandle typeHandle) + { + switch (typeRef) { + case GetPotentiallyNestedClassTypeReference topLevelType: + typeHandle = topLevelType.ResolveInPEFile(module); + return !typeHandle.IsNil; + case NestedTypeReference nestedType: + if (!CanResolveTypeInPEFile(module, nestedType.DeclaringTypeReference, out typeHandle)) + return false; + if (typeHandle.Kind == HandleKind.ExportedType) + return true; + var typeDef = module.Metadata.GetTypeDefinition((TypeDefinitionHandle)typeHandle); + typeHandle = typeDef.GetNestedTypes().FirstOrDefault(t => { + var td = module.Metadata.GetTypeDefinition(t); + var typeName = ReflectionHelper.SplitTypeParameterCountFromReflectionName(module.Metadata.GetString(td.Name), out int typeParameterCount); + return nestedType.AdditionalTypeParameterCount == typeParameterCount && nestedType.Name == typeName; + }); + return !typeHandle.IsNil; + default: + typeHandle = default; + return false; + } + } + void MainWindow_Loaded(object sender, RoutedEventArgs e) { ILSpySettings spySettings = this.spySettings;