diff --git a/ILSpy/AssemblyList.cs b/ILSpy/AssemblyList.cs index c361b11df..46a9b9561 100644 --- a/ILSpy/AssemblyList.cs +++ b/ILSpy/AssemblyList.cs @@ -72,7 +72,7 @@ namespace ICSharpCode.ILSpy /// Loads an assembly list from XML. /// public AssemblyList(XElement listElement) - : this((string)listElement.Attribute("name")) + : this((string?)listElement.Attribute("name") ?? AssemblyListManager.DefaultListName) { foreach (var asm in listElement.Elements("Assembly")) { @@ -184,7 +184,7 @@ namespace ICSharpCode.ILSpy } } - void Assemblies_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + void Assemblies_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { Debug.Assert(Monitor.IsEntered(lockObj)); if (CollectionChangeHasEffectOnSave(e)) @@ -199,9 +199,9 @@ namespace ICSharpCode.ILSpy switch (e.Action) { case NotifyCollectionChangedAction.Add: - return e.NewItems.Cast().Any(asm => !asm.IsAutoLoaded); + return e.NewItems.EmptyIfNull().Cast().Any(asm => !asm.IsAutoLoaded); case NotifyCollectionChangedAction.Remove: - return e.OldItems.Cast().Any(asm => !asm.IsAutoLoaded); + return e.OldItems.EmptyIfNull().Cast().Any(asm => !asm.IsAutoLoaded); default: return true; } @@ -283,7 +283,7 @@ namespace ICSharpCode.ILSpy { bool isUIThread = App.Current.Dispatcher.Thread == Thread.CurrentThread; - LoadedAssembly asm; + LoadedAssembly? asm; lock (lockObj) { if (byFilename.TryGetValue(file, out asm)) @@ -319,7 +319,7 @@ namespace ICSharpCode.ILSpy file = Path.GetFullPath(file); lock (lockObj) { - if (!byFilename.TryGetValue(file, out LoadedAssembly target)) + if (!byFilename.TryGetValue(file, out LoadedAssembly? target)) return null; int index = this.assemblies.IndexOf(target); if (index < 0) diff --git a/ILSpy/AssemblyListSnapshot.cs b/ILSpy/AssemblyListSnapshot.cs index c61a7c9b5..64c98acf5 100644 --- a/ILSpy/AssemblyListSnapshot.cs +++ b/ILSpy/AssemblyListSnapshot.cs @@ -56,7 +56,7 @@ namespace ICSharpCode.ILSpy lookup = await CreateLoadedAssemblyLookupAsync(shortNames: isWinRT).ConfigureAwait(false); lookup = LazyInit.GetOrSet(ref isWinRT ? ref asmLookupByShortName : ref asmLookupByFullName, lookup); } - if (lookup.TryGetValue(key, out PEFile module)) + if (lookup.TryGetValue(key, out PEFile? module)) return module; return null; } diff --git a/ILSpy/ExtensionMethods.cs b/ILSpy/ExtensionMethods.cs index c34331e1d..de2bd4189 100644 --- a/ILSpy/ExtensionMethods.cs +++ b/ILSpy/ExtensionMethods.cs @@ -16,12 +16,17 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +#nullable enable + using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; +using ICSharpCode.Decompiler.Util; using ICSharpCode.ILSpy.Options; namespace ICSharpCode.ILSpy @@ -38,7 +43,7 @@ namespace ICSharpCode.ILSpy list.Add(item); } - public static T PeekOrDefault(this Stack stack) + public static T? PeekOrDefault(this Stack stack) { if (stack.Count == 0) return default(T); @@ -118,6 +123,11 @@ namespace ICSharpCode.ILSpy value = pair.Value; } + internal static IEnumerable EmptyIfNull(this IEnumerable? inst) => inst ?? Enumerable.Empty(); + internal static IEnumerable EmptyIfNull(this IEnumerable? inst) => inst ?? Enumerable.Empty(); + internal static IList EmptyIfNull(this IList? inst) => inst ?? EmptyList.Instance; + internal static IList EmptyIfNull(this IList? inst) => inst ?? Array.Empty(); + public static string ToSuffixString(this System.Reflection.Metadata.EntityHandle handle) { if (!DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokens) @@ -218,7 +228,7 @@ namespace ICSharpCode.ILSpy } #endregion - public static T FindVisualChild(this DependencyObject depObj) where T : DependencyObject + public static T? FindVisualChild(this DependencyObject? depObj) where T : DependencyObject { if (depObj != null) { @@ -230,7 +240,7 @@ namespace ICSharpCode.ILSpy return (T)child; } - T childItem = FindVisualChild(child); + T? childItem = FindVisualChild(child); if (childItem != null) return childItem; } @@ -238,7 +248,7 @@ namespace ICSharpCode.ILSpy return null; } - public static T GetParent(this DependencyObject depObj) where T : DependencyObject + public static T? GetParent(this DependencyObject? depObj) where T : DependencyObject { if (depObj == null) return null; diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs index f7b63e010..66e9c5b5f 100644 --- a/ILSpy/LoadedAssembly.cs +++ b/ILSpy/LoadedAssembly.cs @@ -218,7 +218,7 @@ namespace ICSharpCode.ILSpy return LazyInitializer.EnsureInitialized(ref this.typeSystem, () => { var module = GetPEFileOrNull(); if (module == null) - return null; + return null!; return new SimpleCompilation( module.WithOptions(TypeSystemOptions.Default | TypeSystemOptions.Uncached | TypeSystemOptions.KeepModifiers), MinimalCorlib.Instance); @@ -580,42 +580,44 @@ namespace ICSharpCode.ILSpy return module; } - - string file = Path.Combine(Path.GetDirectoryName(mainModule.FileName), moduleName); - if (File.Exists(file)) + string? directory = Path.GetDirectoryName(mainModule.FileName); + if (directory != null) { - // Load module from disk - LoadedAssembly? asm; - if (loadOnDemand) + string file = Path.Combine(directory, moduleName); + if (File.Exists(file)) { - asm = assemblyList.OpenAssembly(file, isAutoLoaded: true); - } - else - { - asm = assemblyList.FindAssembly(file); - } - if (asm != null) - { - return await asm.GetPEFileOrNullAsync().ConfigureAwait(false); + // Load module from disk + LoadedAssembly? asm; + if (loadOnDemand) + { + asm = assemblyList.OpenAssembly(file, isAutoLoaded: true); + } + else + { + asm = assemblyList.FindAssembly(file); + } + if (asm != null) + { + return await asm.GetPEFileOrNullAsync().ConfigureAwait(false); + } } } - else + + // Module does not exist on disk, look for one with a matching name in the assemblylist: + foreach (LoadedAssembly loaded in alreadyLoadedAssemblies.Assemblies) { - // Module does not exist on disk, look for one with a matching name in the assemblylist: - foreach (LoadedAssembly loaded in alreadyLoadedAssemblies.Assemblies) + var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false); + var reader = module?.Metadata; + if (reader == null || reader.IsAssembly) + continue; + var moduleDef = reader.GetModuleDefinition(); + if (moduleName.Equals(reader.GetString(moduleDef.Name), StringComparison.OrdinalIgnoreCase)) { - var module = await loaded.GetPEFileOrNullAsync().ConfigureAwait(false); - var reader = module?.Metadata; - if (reader == null || reader.IsAssembly) - continue; - var moduleDef = reader.GetModuleDefinition(); - if (moduleName.Equals(reader.GetString(moduleDef.Name), StringComparison.OrdinalIgnoreCase)) - { - referenceLoadInfo.AddMessageOnce(moduleName, MessageKind.Info, "Success - Found in Assembly List"); - return module; - } + referenceLoadInfo.AddMessageOnce(moduleName, MessageKind.Info, "Success - Found in Assembly List"); + return module; } } + return null; } } diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 2c70a15bf..2c17dd3df 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -170,7 +170,7 @@ namespace ICSharpCode.ILSpy.TextView #region Line margin - void CurrentDisplaySettings_PropertyChanged(object sender, PropertyChangedEventArgs e) + void CurrentDisplaySettings_PropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(DisplaySettings.ShowLineNumbers)) { @@ -216,7 +216,7 @@ namespace ICSharpCode.ILSpy.TextView int offset = textEditor.Document.GetOffset(position.Value.Location); if (referenceElementGenerator.References == null) return; - ReferenceSegment seg = referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault(); + ReferenceSegment? seg = referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault(); if (seg == null) return; object? content = GenerateTooltip(seg); @@ -363,7 +363,7 @@ namespace ICSharpCode.ILSpy.TextView TryCloseExistingPopup(true); } - void ToolTipClosed(object sender, EventArgs e) + void ToolTipClosed(object? sender, EventArgs e) { if (toolTip == sender) { @@ -373,7 +373,10 @@ namespace ICSharpCode.ILSpy.TextView { // Because popupToolTip instances are created by the tooltip provider, // they might be reused; so we should detach the event handler - popupToolTip.Closed -= ToolTipClosed; + if (popupToolTip != null) + { + popupToolTip.Closed -= ToolTipClosed; + } popupToolTip = null; } } @@ -515,7 +518,7 @@ namespace ICSharpCode.ILSpy.TextView #endregion #region Highlight brackets - void HighlightBrackets(object sender, EventArgs e) + void HighlightBrackets(object? sender, EventArgs e) { if (DisplaySettingsPanel.CurrentDisplaySettings.HighlightMatchingBraces) { @@ -569,7 +572,9 @@ namespace ICSharpCode.ILSpy.TextView currentCancellationTokenSource = myCancellationTokenSource; // cancel the previous only after current was set to the new one (avoid that the old one still finishes successfully) if (previousCancellationTokenSource != null) + { previousCancellationTokenSource.Cancel(); + } var tcs = new TaskCompletionSource(); Task task; @@ -1248,29 +1253,52 @@ namespace ICSharpCode.ILSpy.TextView { return other != null && ViewedUri == other.ViewedUri - && (DecompiledNodes == other.DecompiledNodes || DecompiledNodes?.SetEquals(other.DecompiledNodes) == true); + && NullSafeSetEquals(DecompiledNodes, other.DecompiledNodes); + + static bool NullSafeSetEquals(HashSet? a, HashSet? b) + { + if (a == b) + return true; + if (a == null || b == null) + return false; + return a.SetEquals(b); + } } } public class DecompilerTextViewState : ViewState { - private List>? ExpandedFoldings; + private List<(int StartOffset, int EndOffset)>? ExpandedFoldings; private int FoldingsChecksum; public double VerticalOffset; public double HorizontalOffset; public void SaveFoldingsState(IEnumerable foldings) { - ExpandedFoldings = foldings.Where(f => !f.IsFolded).Select(f => Tuple.Create(f.StartOffset, f.EndOffset)).ToList(); - FoldingsChecksum = unchecked(foldings.Select(f => f.StartOffset * 3 - f.EndOffset).DefaultIfEmpty().Aggregate((a, b) => a + b)); + ExpandedFoldings = foldings.Where(f => !f.IsFolded) + .Select(f => (f.StartOffset, f.EndOffset)).ToList(); + FoldingsChecksum = unchecked(foldings.Select(f => f.StartOffset * 3 - f.EndOffset) + .DefaultIfEmpty() + .Aggregate((a, b) => a + b)); } internal void RestoreFoldings(List list) { - var checksum = unchecked(list.Select(f => f.StartOffset * 3 - f.EndOffset).DefaultIfEmpty().Aggregate((a, b) => a + b)); + if (ExpandedFoldings == null) + return; + var checksum = unchecked(list.Select(f => f.StartOffset * 3 - f.EndOffset) + .DefaultIfEmpty() + .Aggregate((a, b) => a + b)); if (FoldingsChecksum == checksum) + { foreach (var folding in list) - folding.DefaultClosed = !ExpandedFoldings.Any(f => f.Item1 == folding.StartOffset && f.Item2 == folding.EndOffset); + { + folding.DefaultClosed = !ExpandedFoldings.Any( + f => f.StartOffset == folding.StartOffset + && f.EndOffset == folding.EndOffset + ); + } + } } public override bool Equals(ViewState? other) @@ -1301,17 +1329,21 @@ namespace ICSharpCode.ILSpy.TextView resourceName += ".xshd"; - manager.RegisterHighlighting( - name, extensions, - delegate { - using (Stream s = typeof(DecompilerTextView).Assembly.GetManifestResourceStream(typeof(DecompilerTextView), resourceName)) - { - using (XmlTextReader reader = new XmlTextReader(s)) + Stream? resourceStream = typeof(DecompilerTextView).Assembly + .GetManifestResourceStream(typeof(DecompilerTextView), resourceName); + + if (resourceStream != null) + { + manager.RegisterHighlighting( + name, extensions, + delegate { + using (resourceStream) + using (XmlTextReader reader = new XmlTextReader(resourceStream)) { return HighlightingLoader.Load(reader, manager); } - } - }); + }); + } } } }