Browse Source

Fix nullable issues in AssemblyTreeModel

pull/3295/head
tom-englert 11 months ago
parent
commit
c301cd5743
  1. 125
      ILSpy/AssemblyTree/AssemblyTreeModel.cs

125
ILSpy/AssemblyTree/AssemblyTreeModel.cs

@ -54,6 +54,8 @@ using ICSharpCode.Decompiler;
using TomsToolbox.Essentials; using TomsToolbox.Essentials;
using TomsToolbox.Wpf; using TomsToolbox.Wpf;
#nullable enable
namespace ICSharpCode.ILSpy.AssemblyTree namespace ICSharpCode.ILSpy.AssemblyTree
{ {
[ExportToolPane] [ExportToolPane]
@ -63,8 +65,8 @@ namespace ICSharpCode.ILSpy.AssemblyTree
{ {
public const string PaneContentId = "assemblyListPane"; public const string PaneContentId = "assemblyListPane";
AssemblyListPane activeView; AssemblyListPane? activeView;
AssemblyListTreeNode assemblyListTreeNode; AssemblyListTreeNode? assemblyListTreeNode;
readonly NavigationHistory<NavigationState> history = new(); readonly NavigationHistory<NavigationState> history = new();
private bool isNavigatingHistory; private bool isNavigatingHistory;
@ -83,7 +85,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
SelectedItems.CollectionChanged += (_, _) => selectionChangeThrottle.Tick(); SelectedItems.CollectionChanged += (_, _) => selectionChangeThrottle.Tick();
} }
private void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) private void Settings_PropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
if (sender is SessionSettings sessionSettings) if (sender is SessionSettings sessionSettings)
{ {
@ -98,7 +100,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
DecompileSelectedNodes(DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState); DecompileSelectedNodes(DockWorkspace.Instance.ActiveTabPage.GetState() as DecompilerTextViewState);
break; break;
case nameof(SessionSettings.CurrentCulture): case nameof(SessionSettings.CurrentCulture):
MessageBox.Show(Properties.Resources.SettingsChangeRestartRequired, "ILSpy"); MessageBox.Show(Resources.SettingsChangeRestartRequired, "ILSpy");
break; break;
} }
} }
@ -116,23 +118,23 @@ namespace ICSharpCode.ILSpy.AssemblyTree
} }
} }
public AssemblyList AssemblyList { get; private set; } public AssemblyList? AssemblyList { get; private set; }
private SharpTreeNode root; private SharpTreeNode? root;
public SharpTreeNode Root { public SharpTreeNode? Root {
get => root; get => root;
set => SetProperty(ref root, value); set => SetProperty(ref root, value);
} }
private SharpTreeNode selectedItem; private SharpTreeNode? selectedItem;
public SharpTreeNode SelectedItem { public SharpTreeNode? SelectedItem {
get => selectedItem; get => selectedItem;
set => SetProperty(ref selectedItem, value); set => SetProperty(ref selectedItem, value);
} }
public ObservableCollection<SharpTreeNode> SelectedItems { get; } = []; public ObservableCollection<SharpTreeNode> SelectedItems { get; } = [];
public string[] SelectedPath => GetPathForNode(SelectedItem); public string[]? SelectedPath => GetPathForNode(SelectedItem);
readonly List<LoadedAssembly> commandLineLoadedAssemblies = []; readonly List<LoadedAssembly> commandLineLoadedAssemblies = [];
@ -148,7 +150,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
/// Called on startup or when passed arguments via WndProc from a second instance. /// Called on startup or when passed arguments via WndProc from a second instance.
/// In the format case, spySettings is non-null; in the latter it is null. /// In the format case, spySettings is non-null; in the latter it is null.
/// </summary> /// </summary>
public void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, ISettingsProvider spySettings = null) public void HandleCommandLineArgumentsAfterShowList(CommandLineArguments args, ISettingsProvider? spySettings = null)
{ {
var sessionSettings = SettingsService.Instance.SessionSettings; var sessionSettings = SettingsService.Instance.SessionSettings;
@ -184,7 +186,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
}); });
} }
public async void NavigateOnLaunch(string navigateTo, string[] activeTreeViewPath, ISettingsProvider spySettings, List<LoadedAssembly> relevantAssemblies) public async void NavigateOnLaunch(string? navigateTo, string[]? activeTreeViewPath, ISettingsProvider? spySettings, List<LoadedAssembly> relevantAssemblies)
{ {
var initialSelection = SelectedItem; var initialSelection = SelectedItem;
if (navigateTo != null) if (navigateTo != null)
@ -195,7 +197,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
string namespaceName = navigateTo.Substring(2); string namespaceName = navigateTo.Substring(2);
foreach (LoadedAssembly asm in relevantAssemblies) foreach (LoadedAssembly asm in relevantAssemblies)
{ {
AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(asm); var asmNode = assemblyListTreeNode?.FindAssemblyNode(asm);
if (asmNode != null) if (asmNode != null)
{ {
// FindNamespaceNode() blocks the UI if the assembly is not yet loaded, // FindNamespaceNode() blocks the UI if the assembly is not yet loaded,
@ -222,11 +224,11 @@ namespace ICSharpCode.ILSpy.AssemblyTree
} }
else else
{ {
IEntity mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies)); IEntity? mr = await Task.Run(() => FindEntityInRelevantAssemblies(navigateTo, relevantAssemblies));
// Make sure we wait for assemblies being loaded... // Make sure we wait for assemblies being loaded...
// BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal // BeginInvoke in LoadedAssembly.LookupReferencedAssemblyInternal
await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal); await Dispatcher.InvokeAsync(delegate { }, DispatcherPriority.Normal);
if (mr != null && mr.ParentModule?.MetadataFile != null) if (mr is { ParentModule.MetadataFile: not null })
{ {
found = true; found = true;
if (SelectedItem == initialSelection) if (SelectedItem == initialSelection)
@ -246,7 +248,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
{ {
// NavigateTo == null and an assembly was given on the command-line: // NavigateTo == null and an assembly was given on the command-line:
// Select the newly loaded assembly // Select the newly loaded assembly
AssemblyTreeNode asmNode = assemblyListTreeNode.FindAssemblyNode(relevantAssemblies[0]); var asmNode = assemblyListTreeNode?.FindAssemblyNode(relevantAssemblies[0]);
if (asmNode != null && SelectedItem == initialSelection) if (asmNode != null && SelectedItem == initialSelection)
{ {
SelectNode(asmNode); SelectNode(asmNode);
@ -254,8 +256,8 @@ namespace ICSharpCode.ILSpy.AssemblyTree
} }
else if (spySettings != null) else if (spySettings != null)
{ {
SharpTreeNode node = null; SharpTreeNode? node = null;
if (activeTreeViewPath?.Length > 0) if (activeTreeViewPath?.Length > 0 && AssemblyList != null)
{ {
foreach (var asm in AssemblyList.GetAssemblies()) foreach (var asm in AssemblyList.GetAssemblies())
{ {
@ -285,10 +287,10 @@ namespace ICSharpCode.ILSpy.AssemblyTree
} }
} }
public static IEntity FindEntityInRelevantAssemblies(string navigateTo, IEnumerable<LoadedAssembly> relevantAssemblies) public static IEntity? FindEntityInRelevantAssemblies(string navigateTo, IEnumerable<LoadedAssembly> relevantAssemblies)
{ {
ITypeReference typeRef; ITypeReference typeRef;
IMemberReference memberRef = null; IMemberReference? memberRef = null;
if (navigateTo.StartsWith("T:", StringComparison.Ordinal)) if (navigateTo.StartsWith("T:", StringComparison.Ordinal))
{ {
typeRef = IdStringProvider.ParseTypeName(navigateTo); typeRef = IdStringProvider.ParseTypeName(navigateTo);
@ -301,7 +303,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
foreach (LoadedAssembly asm in relevantAssemblies.ToList()) foreach (LoadedAssembly asm in relevantAssemblies.ToList())
{ {
var module = asm.GetMetadataFileOrNull(); var module = asm.GetMetadataFileOrNull();
if (CanResolveTypeInPEFile(module, typeRef, out var typeHandle)) if (module != null && CanResolveTypeInPEFile(module, typeRef, out var typeHandle))
{ {
ICompilation compilation = typeHandle.Kind == HandleKind.ExportedType ICompilation compilation = typeHandle.Kind == HandleKind.ExportedType
? new DecompilerTypeSystem(module, module.GetAssemblyResolver()) ? new DecompilerTypeSystem(module, module.GetAssemblyResolver())
@ -396,7 +398,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
{ {
AssemblyList list = SettingsService.Instance.AssemblyListManager.LoadList(name); AssemblyList list = SettingsService.Instance.AssemblyListManager.LoadList(name);
//Only load a new list when it is a different one //Only load a new list when it is a different one
if (list.ListName != AssemblyList.ListName) if (list.ListName != AssemblyList?.ListName)
{ {
ShowAssemblyList(list); ShowAssemblyList(list);
SelectNode(Root); SelectNode(Root);
@ -440,7 +442,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
#endif #endif
} }
void assemblyList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) void assemblyList_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{ {
if (e.Action == NotifyCollectionChangedAction.Reset) if (e.Action == NotifyCollectionChangedAction.Reset)
{ {
@ -459,6 +461,9 @@ namespace ICSharpCode.ILSpy.AssemblyTree
void LoadInitialAssemblies() void LoadInitialAssemblies()
{ {
if (AssemblyList is null)
return;
// Called when loading an empty assembly list; so that // Called when loading an empty assembly list; so that
// the user can see something initially. // the user can see something initially.
System.Reflection.Assembly[] initialAssemblies = { System.Reflection.Assembly[] initialAssemblies = {
@ -475,21 +480,21 @@ namespace ICSharpCode.ILSpy.AssemblyTree
AssemblyList.OpenAssembly(asm.Location); AssemblyList.OpenAssembly(asm.Location);
} }
public AssemblyTreeNode FindAssemblyNode(LoadedAssembly asm) public AssemblyTreeNode? FindAssemblyNode(LoadedAssembly asm)
{ {
return assemblyListTreeNode.FindAssemblyNode(asm); return assemblyListTreeNode?.FindAssemblyNode(asm);
} }
#region Node Selection #region Node Selection
public void SelectNode(SharpTreeNode node, bool inNewTabPage = false) public void SelectNode(SharpTreeNode? node, bool inNewTabPage = false)
{ {
if (node == null) if (node == null)
return; return;
if (node.AncestorsAndSelf().Any(item => item.IsHidden)) if (node.AncestorsAndSelf().Any(item => item.IsHidden))
{ {
MessageBox.Show(Properties.Resources.NavigationFailed, "ILSpy", MessageBoxButton.OK, MessageBoxImage.Exclamation); MessageBox.Show(Resources.NavigationFailed, "ILSpy", MessageBoxButton.OK, MessageBoxImage.Exclamation);
return; return;
} }
@ -532,7 +537,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
SelectedItems.Clear(); SelectedItems.Clear();
foreach (var node in nodesList) foreach (var node in nodesList)
{ {
activeView.ScrollIntoView(node); activeView?.ScrollIntoView(node);
SelectedItems.Add(node); SelectedItems.Add(node);
} }
} }
@ -543,7 +548,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
SelectedItems.Clear(); SelectedItems.Clear();
foreach (var node in nodesList) foreach (var node in nodesList)
{ {
activeView.ScrollIntoView(node); activeView?.ScrollIntoView(node);
SelectedItems.Add(node); SelectedItems.Add(node);
} }
}); });
@ -553,12 +558,12 @@ namespace ICSharpCode.ILSpy.AssemblyTree
/// <summary> /// <summary>
/// Retrieves a node using the .ToString() representations of its ancestors. /// Retrieves a node using the .ToString() representations of its ancestors.
/// </summary> /// </summary>
public SharpTreeNode FindNodeByPath(string[] path, bool returnBestMatch) public SharpTreeNode? FindNodeByPath(string[]? path, bool returnBestMatch)
{ {
if (path == null) if (path == null)
return null; return null;
SharpTreeNode node = Root; var node = Root;
SharpTreeNode bestMatch = node; var bestMatch = node;
foreach (var element in path) foreach (var element in path)
{ {
if (node == null) if (node == null)
@ -576,7 +581,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
/// <summary> /// <summary>
/// Gets the .ToString() representation of the node's ancestors. /// Gets the .ToString() representation of the node's ancestors.
/// </summary> /// </summary>
public static string[] GetPathForNode(SharpTreeNode node) public static string[]? GetPathForNode(SharpTreeNode? node)
{ {
if (node == null) if (node == null)
return null; return null;
@ -590,8 +595,11 @@ namespace ICSharpCode.ILSpy.AssemblyTree
return path.ToArray(); return path.ToArray();
} }
public ILSpyTreeNode FindTreeNode(object reference) public ILSpyTreeNode? FindTreeNode(object? reference)
{ {
if (assemblyListTreeNode == null)
return null;
switch (reference) switch (reference)
{ {
case LoadedAssembly lasm: case LoadedAssembly lasm:
@ -619,7 +627,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
} }
} }
private void JumpToReference(object sender, NavigateToReferenceEventArgs e) private void JumpToReference(object? sender, NavigateToReferenceEventArgs e)
{ {
JumpToReferenceAsync(e.Reference, e.InNewTabPage).HandleExceptions(); JumpToReferenceAsync(e.Reference, e.InNewTabPage).HandleExceptions();
} }
@ -631,7 +639,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
/// Returns a task that will signal completion when the decompilation of the jump target has finished. /// Returns a task that will signal completion when the decompilation of the jump target has finished.
/// The task will be marked as canceled if the decompilation is canceled. /// The task will be marked as canceled if the decompilation is canceled.
/// </returns> /// </returns>
private Task JumpToReferenceAsync(object reference, bool inNewTabPage = false) private Task JumpToReferenceAsync(object? reference, bool inNewTabPage = false)
{ {
var decompilationTask = Task.CompletedTask; var decompilationTask = Task.CompletedTask;
@ -641,6 +649,8 @@ namespace ICSharpCode.ILSpy.AssemblyTree
MainWindow.OpenLink(opCode.Link); MainWindow.OpenLink(opCode.Link);
break; break;
case EntityReference unresolvedEntity: case EntityReference unresolvedEntity:
if (AssemblyList is null)
break;
string protocol = unresolvedEntity.Protocol; string protocol = unresolvedEntity.Protocol;
var file = unresolvedEntity.ResolveAssembly(AssemblyList); var file = unresolvedEntity.ResolveAssembly(AssemblyList);
if (file == null) if (file == null)
@ -669,7 +679,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
} }
break; break;
default: default:
ILSpyTreeNode treeNode = FindTreeNode(reference); var treeNode = FindTreeNode(reference);
if (treeNode != null) if (treeNode != null)
SelectNode(treeNode, inNewTabPage); SelectNode(treeNode, inNewTabPage);
break; break;
@ -679,15 +689,19 @@ namespace ICSharpCode.ILSpy.AssemblyTree
#endregion #endregion
public void LoadAssemblies(IEnumerable<string> fileNames, List<LoadedAssembly> loadedAssemblies = null, bool focusNode = true) public void LoadAssemblies(IEnumerable<string> fileNames, List<LoadedAssembly>? loadedAssemblies = null, bool focusNode = true)
{ {
using (Keyboard.FocusedElement.PreserveFocus(!focusNode)) using (Keyboard.FocusedElement.PreserveFocus(!focusNode))
{ {
AssemblyTreeNode lastNode = null; AssemblyTreeNode? lastNode = null;
var assemblyList = AssemblyList;
if (assemblyList is null)
return;
foreach (string file in fileNames) foreach (string file in fileNames)
{ {
var assembly = AssemblyList.OpenAssembly(file); var assembly = assemblyList.OpenAssembly(file);
if (loadedAssemblies != null) if (loadedAssemblies != null)
{ {
@ -695,7 +709,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
} }
else else
{ {
var node = assemblyListTreeNode.FindAssemblyNode(assembly); var node = assemblyListTreeNode?.FindAssemblyNode(assembly);
if (node != null && focusNode) if (node != null && focusNode)
{ {
lastNode = node; lastNode = node;
@ -750,7 +764,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
return; return;
void ContextMenuClosed(object sender, EventArgs e) void ContextMenuClosed(object? sender, EventArgs e)
{ {
ContextMenuProvider.ContextMenuClosed -= ContextMenuClosed; ContextMenuProvider.ContextMenuClosed -= ContextMenuClosed;
@ -763,7 +777,7 @@ namespace ICSharpCode.ILSpy.AssemblyTree
} }
} }
private void DecompileSelectedNodes(DecompilerTextViewState newState = null) private void DecompileSelectedNodes(DecompilerTextViewState? newState = null)
{ {
var activeTabPage = DockWorkspace.Instance.ActiveTabPage; var activeTabPage = DockWorkspace.Instance.ActiveTabPage;
@ -849,11 +863,12 @@ namespace ICSharpCode.ILSpy.AssemblyTree
Title = e.Uri.AbsolutePath, Title = e.Uri.AbsolutePath,
EnableHyperlinks = true EnableHyperlinks = true
}; };
using (Stream s = typeof(App).Assembly.GetManifestResourceStream(typeof(App), e.Uri.AbsolutePath)) using (Stream? s = typeof(App).Assembly.GetManifestResourceStream(typeof(App), e.Uri.AbsolutePath))
{ {
using (StreamReader r = new StreamReader(s)) if (s != null)
{ {
string line; using StreamReader r = new StreamReader(s);
string? line;
while ((line = r.ReadLine()) != null) while ((line = r.ReadLine()) != null)
{ {
output.Write(line); output.Write(line);
@ -886,8 +901,13 @@ namespace ICSharpCode.ILSpy.AssemblyTree
using (Keyboard.FocusedElement.PreserveFocus()) using (Keyboard.FocusedElement.PreserveFocus())
{ {
var path = GetPathForNode(SelectedItem); var path = GetPathForNode(SelectedItem);
ShowAssemblyList(SettingsService.Instance.AssemblyListManager.LoadList(AssemblyList.ListName)); if (AssemblyList != null)
{
ShowAssemblyList(SettingsService.Instance.AssemblyListManager.LoadList(AssemblyList.ListName));
}
SelectNode(FindNodeByPath(path, true), inNewTabPage: false); SelectNode(FindNodeByPath(path, true), inNewTabPage: false);
if (DockWorkspace.Instance.ActiveTabPage?.GetState()?.DecompiledNodes?.Any() == true) if (DockWorkspace.Instance.ActiveTabPage?.GetState()?.DecompiledNodes?.Any() == true)
{ {
DecompileSelectedNodes(); DecompileSelectedNodes();
@ -917,14 +937,14 @@ namespace ICSharpCode.ILSpy.AssemblyTree
{ {
using (activeView?.LockUpdates()) using (activeView?.LockUpdates())
{ {
AssemblyList.Sort(AssemblyComparer.Instance); AssemblyList?.Sort(AssemblyComparer.Instance);
} }
} }
private class AssemblyComparer : IComparer<LoadedAssembly> private class AssemblyComparer : IComparer<LoadedAssembly>
{ {
public static readonly AssemblyComparer Instance = new(); public static readonly AssemblyComparer Instance = new();
int IComparer<LoadedAssembly>.Compare(LoadedAssembly x, LoadedAssembly y) int IComparer<LoadedAssembly>.Compare(LoadedAssembly? x, LoadedAssembly? y)
{ {
return string.Compare(x?.ShortName, y?.ShortName, StringComparison.CurrentCulture); return string.Compare(x?.ShortName, y?.ShortName, StringComparison.CurrentCulture);
} }
@ -932,14 +952,17 @@ namespace ICSharpCode.ILSpy.AssemblyTree
public void CollapseAll() public void CollapseAll()
{ {
using (activeView.LockUpdates()) using (activeView?.LockUpdates())
{ {
CollapseChildren(Root); CollapseChildren(Root);
} }
} }
static void CollapseChildren(SharpTreeNode node) private static void CollapseChildren(SharpTreeNode? node)
{ {
if (node is null)
return;
foreach (var child in node.Children) foreach (var child in node.Children)
{ {
if (!child.IsExpanded) if (!child.IsExpanded)

Loading…
Cancel
Save