From 6dbd8429b750d7abda512701ffe75f7beb11d8b1 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 23 May 2026 19:43:07 +0200 Subject: [PATCH] Port ILSpy.ReadyToRun plugin R2R was the only legacy plugin still keeping the solution-level build red (27 errors against the Avalonia tree). Port mirrors the four-corner substitution used for the rest of the rewrite: Assisted-by: Claude:claude-opus-4-7:Claude Code --- ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj | 15 ++++--- ILSpy.ReadyToRun/ReadyToRunDisassembler.cs | 3 +- ILSpy.ReadyToRun/ReadyToRunLanguage.cs | 15 ++++--- ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml | 29 ------------ ILSpy.ReadyToRun/ReadyToRunOptions.cs | 6 +-- ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml | 33 ++++++++++++++ .../ReadyToRunOptionsView.axaml.cs | 33 ++++++++++++++ ....xaml.cs => ReadyToRunOptionsViewModel.cs} | 44 +++++++------------ ILSpy.sln | 15 +++++++ ILSpy/ViewLocator.cs | 16 ++++++- 10 files changed, 134 insertions(+), 75 deletions(-) delete mode 100644 ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml create mode 100644 ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml create mode 100644 ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml.cs rename ILSpy.ReadyToRun/{ReadyToRunOptionPage.xaml.cs => ReadyToRunOptionsViewModel.cs} (66%) diff --git a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj index 0232533b1..a1f50dce8 100644 --- a/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj +++ b/ILSpy.ReadyToRun/ILSpy.ReadyToRun.csproj @@ -3,14 +3,13 @@ ILSpy.ReadyToRun.Plugin - net10.0-windows - win-x64;win-arm64 + net10.0 False en-US true - true false - true + disable + true @@ -31,16 +30,18 @@ + + + + So ensure NuGet uses consistent versions (from Directory.Packages.props) for these. --> diff --git a/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs b/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs index 5acffa251..d70485ca4 100644 --- a/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs +++ b/ILSpy.ReadyToRun/ReadyToRunDisassembler.cs @@ -26,11 +26,12 @@ using Iced.Intel; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.Metadata; -using ICSharpCode.ILSpy.Util; using ILCompiler.Reflection.ReadyToRun; using ILCompiler.Reflection.ReadyToRun.Amd64; +using ILSpy; + namespace ICSharpCode.ILSpy.ReadyToRun { internal class ReadyToRunDisassembler diff --git a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs index 2b7d084eb..fe7db176f 100644 --- a/ILSpy.ReadyToRun/ReadyToRunLanguage.cs +++ b/ILSpy.ReadyToRun/ReadyToRunLanguage.cs @@ -30,18 +30,20 @@ using System.Reflection.PortableExecutable; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using ICSharpCode.AvalonEdit.Highlighting; +using AvaloniaEdit.Highlighting; + using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.Disassembler; using ICSharpCode.Decompiler.Metadata; +using ICSharpCode.Decompiler.Output; using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.TypeSystem; -using ICSharpCode.ILSpy.Util; using ICSharpCode.ILSpyX; using ILCompiler.Reflection.ReadyToRun; -using TomsToolbox.Composition; +using ILSpy; +using ILSpy.Languages; using MetadataReader = System.Reflection.Metadata.MetadataReader; @@ -108,7 +110,10 @@ namespace ICSharpCode.ILSpy.ReadyToRun [Export(typeof(Language))] [Shared] - internal class ReadyToRunLanguage(SettingsService settingsService, IExportProvider exportProvider) : Language + [method: ImportingConstructor] + // LanguageService is taken Lazy because it imports Language[] -- a direct LanguageService + // dependency creates a constructor-injection cycle that System.Composition refuses. + internal class ReadyToRunLanguage(SettingsService settingsService, Lazy languageService) : Language { private static readonly ConditionalWeakTable readyToRunReaders = new ConditionalWeakTable(); @@ -229,7 +234,7 @@ namespace ICSharpCode.ILSpy.ReadyToRun public override RichText GetRichTextTooltip(IEntity entity) { - return exportProvider.GetExportedValue().ILLanguage.GetRichTextTooltip(entity); + return languageService.Value.GetLanguage("IL").GetRichTextTooltip(entity); } private ReadyToRunReaderCacheEntry GetReader(LoadedAssembly assembly, MetadataFile file) diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml b/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml deleted file mode 100644 index 39aa35451..000000000 --- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ILSpy.ReadyToRun/ReadyToRunOptions.cs b/ILSpy.ReadyToRun/ReadyToRunOptions.cs index c2b94929c..28c7418a6 100644 --- a/ILSpy.ReadyToRun/ReadyToRunOptions.cs +++ b/ILSpy.ReadyToRun/ReadyToRunOptions.cs @@ -18,13 +18,13 @@ using System.Xml.Linq; -using ICSharpCode.ILSpyX.Settings; +using CommunityToolkit.Mvvm.ComponentModel; -using TomsToolbox.Wpf; +using ICSharpCode.ILSpyX.Settings; namespace ICSharpCode.ILSpy.ReadyToRun { - internal partial class ReadyToRunOptions : ObservableObjectBase, ISettingsSection + internal partial class ReadyToRunOptions : ObservableObject, ISettingsSection { private static readonly XNamespace ns = "http://www.ilspy.net/ready-to-run"; diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml b/ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml new file mode 100644 index 000000000..eafd12fa6 --- /dev/null +++ b/ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml.cs b/ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml.cs new file mode 100644 index 000000000..d2be1a370 --- /dev/null +++ b/ILSpy.ReadyToRun/ReadyToRunOptionsView.axaml.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2018 Siegfried Pammer +// +// 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 global::Avalonia.Controls; +using global::Avalonia.Markup.Xaml; + +namespace ICSharpCode.ILSpy.ReadyToRun +{ + public partial class ReadyToRunOptionsView : UserControl + { + public ReadyToRunOptionsView() + { + InitializeComponent(); + } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); + } +} diff --git a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs b/ILSpy.ReadyToRun/ReadyToRunOptionsViewModel.cs similarity index 66% rename from ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs rename to ILSpy.ReadyToRun/ReadyToRunOptionsViewModel.cs index bb4c6d440..b25eee314 100644 --- a/ILSpy.ReadyToRun/ReadyToRunOptionPage.xaml.cs +++ b/ILSpy.ReadyToRun/ReadyToRunOptionsViewModel.cs @@ -1,14 +1,14 @@ // Copyright (c) 2018 Siegfried Pammer -// +// // 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 @@ -17,46 +17,32 @@ // DEALINGS IN THE SOFTWARE. using System.Composition; +using System.Xml.Linq; -using ICSharpCode.ILSpy.Options; -using ICSharpCode.ILSpy.Util; +using CommunityToolkit.Mvvm.ComponentModel; -using TomsToolbox.Wpf; -using TomsToolbox.Wpf.Composition.AttributedModel; +using ILSpy; +using ILSpy.Options; namespace ICSharpCode.ILSpy.ReadyToRun { - [DataTemplate(typeof(ReadyToRunOptionsViewModel))] - [NonShared] - partial class ReadyToRunOptionPage - { - public ReadyToRunOptionPage() - { - InitializeComponent(); - } - } - [ExportOptionPage(Order = 40)] - [NonShared] - class ReadyToRunOptionsViewModel : ObservableObjectBase, IOptionPage + [Shared] + internal sealed partial class ReadyToRunOptionsViewModel : ObservableObject, IOptionPage { - private ReadyToRunOptions options; - - public ReadyToRunOptions Options { - get => options; - set => SetProperty(ref options, value); - } + [ObservableProperty] + ReadyToRunOptions options = null!; public string Title => global::ILSpy.ReadyToRun.Properties.Resources.ReadyToRun; - public void Load(SettingsSnapshot snapshot) + public void Load(SettingsService service) { - Options = snapshot.GetSettings(); + Options = service.GetSettings(); } public void LoadDefaults() { - Options.LoadFromXml(new("empty")); + Options.LoadFromXml(new XElement("ReadyToRunOptions")); } } -} \ No newline at end of file +} diff --git a/ILSpy.sln b/ILSpy.sln index 954caa09e..6f7d6b923 100644 --- a/ILSpy.sln +++ b/ILSpy.sln @@ -1,3 +1,4 @@ + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 VisualStudioVersion = 18.4.11620.152 @@ -38,6 +39,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.Tests", "ILSpy.Tests\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.Tests.Windows", "ILSpy.Tests.Windows\ILSpy.Tests.Windows.csproj", "{20AF56FB-DC0F-4048-B75D-83FEE0D669B2}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpy.ReadyToRun", "ILSpy.ReadyToRun\ILSpy.ReadyToRun.csproj", "{AF43A431-AAF7-4758-A180-42C378AE0FB2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -168,6 +171,18 @@ Global {20AF56FB-DC0F-4048-B75D-83FEE0D669B2}.Release|x64.Build.0 = Release|Any CPU {20AF56FB-DC0F-4048-B75D-83FEE0D669B2}.Release|x86.ActiveCfg = Release|Any CPU {20AF56FB-DC0F-4048-B75D-83FEE0D669B2}.Release|x86.Build.0 = Release|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Debug|x64.ActiveCfg = Debug|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Debug|x64.Build.0 = Debug|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Debug|x86.ActiveCfg = Debug|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Debug|x86.Build.0 = Debug|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Release|Any CPU.Build.0 = Release|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Release|x64.ActiveCfg = Release|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Release|x64.Build.0 = Release|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Release|x86.ActiveCfg = Release|Any CPU + {AF43A431-AAF7-4758-A180-42C378AE0FB2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ILSpy/ViewLocator.cs b/ILSpy/ViewLocator.cs index ecdb22c2d..6c9287a92 100644 --- a/ILSpy/ViewLocator.cs +++ b/ILSpy/ViewLocator.cs @@ -28,6 +28,7 @@ using Dock.Model.Core; using ILSpy.Analyzers; using ILSpy.AssemblyTree; using ILSpy.Compare; +using ILSpy.Options; using ILSpy.Search; using ILSpy.TextView; using ILSpy.ViewModels; @@ -77,9 +78,21 @@ namespace ILSpy return null; if (s_views.TryGetValue(param.GetType(), out var ctor)) return ctor(); - // Convention fallback for ViewModelBase-derived VMs not in the explicit map. + // Convention fallback: SomeViewModel -> SomeView. Type.GetType only searches the + // calling assembly + corelib, so a plugin viewmodel (loaded into the AppDomain + // at runtime by AppComposition.LoadPlugins) wouldn't be found via that path + // alone. Walk every loaded assembly to find the matching View type. var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal); var type = Type.GetType(name); + if (type == null) + { + foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) + { + type = asm.GetType(name); + if (type != null) + break; + } + } if (type != null) return (Control)Activator.CreateInstance(type)!; return new TextBlock { Text = "Not Found: " + name }; @@ -91,6 +104,7 @@ namespace ILSpy return false; return data is IDockable || data is ViewModelBase + || data is IOptionPage || s_views.ContainsKey(data.GetType()); } }