mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Five threads share infrastructure here, so they land together: Assisted-by: Claude:claude-opus-4-7:Claude Codepull/3755/head
28 changed files with 727 additions and 22 deletions
@ -0,0 +1,80 @@ |
|||||||
|
// Copyright (c) 2026 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.
|
||||||
|
|
||||||
|
using ICSharpCode.Decompiler; |
||||||
|
using ICSharpCode.Decompiler.Output; |
||||||
|
using ICSharpCode.Decompiler.TypeSystem; |
||||||
|
using ICSharpCode.ILSpyX.TreeView; |
||||||
|
using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; |
||||||
|
|
||||||
|
using ILSpy.AppEnv; |
||||||
|
using ILSpy.AssemblyTree; |
||||||
|
using ILSpy.Languages; |
||||||
|
|
||||||
|
namespace ILSpy.TreeNodes |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Single base-class / interface entry under a <see cref="BaseTypesTreeNode"/>. Activating
|
||||||
|
/// the entry navigates the assembly-tree selection to the corresponding <see cref="TypeTreeNode"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BaseTypesEntryNode : ILSpyTreeNode, IMemberTreeNode |
||||||
|
{ |
||||||
|
readonly ITypeDefinition type; |
||||||
|
|
||||||
|
public BaseTypesEntryNode(ITypeDefinition type) |
||||||
|
{ |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
public override object Text => Language.TypeToString(type, ConversionFlags.None); |
||||||
|
|
||||||
|
public override object? NavigationText => $"{Text} ({ICSharpCode.ILSpy.Properties.Resources.BaseTypes})"; |
||||||
|
|
||||||
|
public override object Icon => type.Kind == TypeKind.Interface |
||||||
|
? Images.Images.Interface |
||||||
|
: Images.Images.Class; |
||||||
|
|
||||||
|
public override void ActivateItem(IPlatformRoutedEventArgs e) |
||||||
|
{ |
||||||
|
e.Handled = ActivateItem(this, type); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shared activate helper used by <see cref="BaseTypesEntryNode"/> and
|
||||||
|
/// <see cref="DerivedTypesEntryNode"/>. Mirrors WPF's matching helper. The
|
||||||
|
/// <paramref name="node"/> parameter is unused on Avalonia (navigation routes through
|
||||||
|
/// the MEF-resolved <see cref="AssemblyTreeModel"/> instead of walking up the tree to
|
||||||
|
/// the assembly-list node), but the signature stays in lock-step with WPF so any future
|
||||||
|
/// caller that needs the originating node has it on hand.
|
||||||
|
/// </summary>
|
||||||
|
internal static bool ActivateItem(SharpTreeNode node, ITypeDefinition? def) |
||||||
|
{ |
||||||
|
if (def == null) |
||||||
|
return false; |
||||||
|
var atm = AppComposition.Current.GetExport<AssemblyTreeModel>(); |
||||||
|
return atm.JumpToType(def); |
||||||
|
} |
||||||
|
|
||||||
|
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) |
||||||
|
{ |
||||||
|
language.WriteCommentLine(output, language.TypeToString(type, ConversionFlags.None)); |
||||||
|
} |
||||||
|
|
||||||
|
IEntity IMemberTreeNode.Member => type; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,85 @@ |
|||||||
|
// Copyright (c) 2026 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.
|
||||||
|
|
||||||
|
using System.Linq; |
||||||
|
using System.Reflection.Metadata; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler; |
||||||
|
using ICSharpCode.Decompiler.Metadata; |
||||||
|
using ICSharpCode.Decompiler.TypeSystem; |
||||||
|
using ICSharpCode.ILSpyX; |
||||||
|
using ICSharpCode.ILSpyX.TreeView; |
||||||
|
|
||||||
|
using ILSpy.Languages; |
||||||
|
|
||||||
|
namespace ILSpy.TreeNodes |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Lists the base types of a class — the inheritance chain plus implemented interfaces, in
|
||||||
|
/// type-itself-first → most-distant-ancestor order. Lazy: children are only resolved when
|
||||||
|
/// the user expands the node.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BaseTypesTreeNode : ILSpyTreeNode |
||||||
|
{ |
||||||
|
readonly MetadataFile module; |
||||||
|
readonly ITypeDefinition type; |
||||||
|
|
||||||
|
public BaseTypesTreeNode(MetadataFile module, ITypeDefinition type) |
||||||
|
{ |
||||||
|
this.module = module; |
||||||
|
this.type = type; |
||||||
|
LazyLoading = true; |
||||||
|
} |
||||||
|
|
||||||
|
public override object Text => ICSharpCode.ILSpy.Properties.Resources.BaseTypes; |
||||||
|
|
||||||
|
public override object? NavigationText => $"{Text} ({Language.TypeToString(type)})"; |
||||||
|
|
||||||
|
public override object Icon => Images.Images.SuperTypes; |
||||||
|
|
||||||
|
protected override void LoadChildren() |
||||||
|
{ |
||||||
|
AddBaseTypes(Children, module, type); |
||||||
|
} |
||||||
|
|
||||||
|
internal static void AddBaseTypes(SharpTreeNodeCollection children, MetadataFile module, ITypeDefinition typeDefinition) |
||||||
|
{ |
||||||
|
// Re-resolve the type with an Uncached type system so we get a fresh inheritance
|
||||||
|
// chain (some Decompiler-flag toggles affect how interfaces fold in). Mirrors WPF.
|
||||||
|
var handle = (TypeDefinitionHandle)typeDefinition.MetadataToken; |
||||||
|
var typeSystem = new DecompilerTypeSystem(module, module.GetAssemblyResolver(), |
||||||
|
TypeSystemOptions.Default | TypeSystemOptions.Uncached); |
||||||
|
if (typeSystem.MainModule.ResolveEntity(handle) is not ITypeDefinition t) |
||||||
|
return; |
||||||
|
// GetAllBaseTypeDefinitions returns [furthest-ancestor, ..., direct-base, self]; we
|
||||||
|
// want everything except self, in walk-up order — so reverse and skip(1).
|
||||||
|
foreach (var td in t.GetAllBaseTypeDefinitions().Reverse().Skip(1)) |
||||||
|
{ |
||||||
|
if (t.Kind != TypeKind.Interface || t.Kind == td.Kind) |
||||||
|
children.Add(new BaseTypesEntryNode(td)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) |
||||||
|
{ |
||||||
|
EnsureLazyChildren(); |
||||||
|
foreach (var child in Children.OfType<ILSpyTreeNode>()) |
||||||
|
child.Decompile(language, output, options); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,97 @@ |
|||||||
|
// Copyright (c) 2026 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.
|
||||||
|
|
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler; |
||||||
|
using ICSharpCode.Decompiler.Output; |
||||||
|
using ICSharpCode.Decompiler.TypeSystem; |
||||||
|
using ICSharpCode.ILSpyX; |
||||||
|
using ICSharpCode.ILSpyX.Abstractions; |
||||||
|
using ICSharpCode.ILSpyX.TreeView.PlatformAbstractions; |
||||||
|
|
||||||
|
using ILSpy.Languages; |
||||||
|
|
||||||
|
namespace ILSpy.TreeNodes |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Single derived-type entry under a <see cref="DerivedTypesTreeNode"/>. Activating jumps
|
||||||
|
/// to the corresponding <see cref="TypeTreeNode"/>. Itself lazy-recursive — expanding a
|
||||||
|
/// derived-type entry walks one more level, surfacing types derived from <em>it</em>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DerivedTypesEntryNode : ILSpyTreeNode, IMemberTreeNode |
||||||
|
{ |
||||||
|
readonly AssemblyList list; |
||||||
|
readonly ITypeDefinition type; |
||||||
|
|
||||||
|
public DerivedTypesEntryNode(AssemblyList list, ITypeDefinition type) |
||||||
|
{ |
||||||
|
this.list = list; |
||||||
|
this.type = type; |
||||||
|
LazyLoading = true; |
||||||
|
} |
||||||
|
|
||||||
|
public override bool ShowExpander => !type.IsSealed && base.ShowExpander; |
||||||
|
|
||||||
|
public override object Text => Language.TypeToString(type, ConversionFlags.None); |
||||||
|
|
||||||
|
public override object? NavigationText => $"{Text} ({ICSharpCode.ILSpy.Properties.Resources.DerivedTypes})"; |
||||||
|
|
||||||
|
public override object Icon => type.Kind == TypeKind.Interface |
||||||
|
? Images.Images.Interface |
||||||
|
: Images.Images.Class; |
||||||
|
|
||||||
|
protected override void LoadChildren() |
||||||
|
{ |
||||||
|
foreach (var entry in DerivedTypesTreeNode.FindDerivedTypes(list, LanguageService.CurrentLanguage, type, CancellationToken.None)) |
||||||
|
Children.Add(entry); |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsPublicAPI => type.Accessibility switch { |
||||||
|
Accessibility.Public or Accessibility.Internal or Accessibility.ProtectedOrInternal => true, |
||||||
|
_ => false, |
||||||
|
}; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Mirrors WPF's filter — drop non-public entries under PublicOnly visibility, otherwise
|
||||||
|
/// recurse so the user can drill into derived chains. The WPF overload also reads
|
||||||
|
/// <c>SearchTermMatches</c> (a <see cref="LanguageSettings"/> helper that's not yet in
|
||||||
|
/// the Avalonia port) to surface only entries whose name matches the active search term;
|
||||||
|
/// reinstate that branch when the search infrastructure lands.
|
||||||
|
/// </summary>
|
||||||
|
public override FilterResult Filter(LanguageSettings settings) |
||||||
|
{ |
||||||
|
if (settings.ShowApiLevel == ApiVisibility.PublicOnly && !IsPublicAPI) |
||||||
|
return FilterResult.Hidden; |
||||||
|
return FilterResult.Recurse; |
||||||
|
} |
||||||
|
|
||||||
|
public override void ActivateItem(IPlatformRoutedEventArgs e) |
||||||
|
{ |
||||||
|
e.Handled = BaseTypesEntryNode.ActivateItem(this, type); |
||||||
|
} |
||||||
|
|
||||||
|
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) |
||||||
|
{ |
||||||
|
language.WriteCommentLine(output, language.TypeToString(type, ConversionFlags.None)); |
||||||
|
} |
||||||
|
|
||||||
|
IEntity IMemberTreeNode.Member => type; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,94 @@ |
|||||||
|
// Copyright (c) 2026 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.
|
||||||
|
|
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Threading; |
||||||
|
|
||||||
|
using ICSharpCode.Decompiler; |
||||||
|
using ICSharpCode.Decompiler.TypeSystem; |
||||||
|
using ICSharpCode.ILSpyX; |
||||||
|
using ICSharpCode.ILSpyX.Analyzers; |
||||||
|
|
||||||
|
using ILSpy.Languages; |
||||||
|
|
||||||
|
namespace ILSpy.TreeNodes |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Lists the derived types of a class — every loaded type whose direct base list contains
|
||||||
|
/// our target. Lazy: the assembly-list scan only runs when the user expands the node. The
|
||||||
|
/// scan is synchronous; small assembly lists complete in milliseconds. A future task can
|
||||||
|
/// switch to a background-loaded variant when this proves slow under real workloads.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DerivedTypesTreeNode : ILSpyTreeNode |
||||||
|
{ |
||||||
|
readonly AssemblyList list; |
||||||
|
readonly ITypeDefinition type; |
||||||
|
|
||||||
|
public DerivedTypesTreeNode(AssemblyList list, ITypeDefinition type) |
||||||
|
{ |
||||||
|
this.list = list; |
||||||
|
this.type = type; |
||||||
|
LazyLoading = true; |
||||||
|
} |
||||||
|
|
||||||
|
public override object Text => ICSharpCode.ILSpy.Properties.Resources.DerivedTypes; |
||||||
|
|
||||||
|
public override object? NavigationText => $"{Text} ({Language.TypeToString(type)})"; |
||||||
|
|
||||||
|
public override object Icon => Images.Images.SubTypes; |
||||||
|
|
||||||
|
protected override void LoadChildren() |
||||||
|
{ |
||||||
|
foreach (var entry in FindDerivedTypes(list, LanguageService.CurrentLanguage, type, CancellationToken.None)) |
||||||
|
Children.Add(entry); |
||||||
|
} |
||||||
|
|
||||||
|
internal static IEnumerable<DerivedTypesEntryNode> FindDerivedTypes(AssemblyList list, Language language, ITypeDefinition type, CancellationToken cancellationToken) |
||||||
|
{ |
||||||
|
var context = new AnalyzerContext { |
||||||
|
CancellationToken = cancellationToken, |
||||||
|
Language = language, |
||||||
|
AssemblyList = list, |
||||||
|
}; |
||||||
|
var scope = context.GetScopeOf(type); |
||||||
|
|
||||||
|
foreach (var td in scope.GetTypesInScope(cancellationToken)) |
||||||
|
{ |
||||||
|
cancellationToken.ThrowIfCancellationRequested(); |
||||||
|
foreach (var baseType in td.DirectBaseTypes) |
||||||
|
{ |
||||||
|
if (baseType.FullName == type.FullName && baseType.TypeParameterCount == type.TypeParameterCount) |
||||||
|
{ |
||||||
|
yield return new DerivedTypesEntryNode(list, td); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options) |
||||||
|
{ |
||||||
|
EnsureLazyChildren(); |
||||||
|
foreach (var child in Children) |
||||||
|
{ |
||||||
|
if (child is ILSpyTreeNode node) |
||||||
|
node.Decompile(language, output, options); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue