From bf5249be2b83dd77b04ff9297da4508f455b23af Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 6 Jul 2025 10:30:43 +0200 Subject: [PATCH] Add --ilspy-settingsfile and -ds|--decompiler-setting name=value options --- ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs | 88 +++++++++++++++++-- .../Settings/DecompilerSettings.cs | 15 ++++ 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs index 2879b2d89..3a641d50c 100644 --- a/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs +++ b/ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs @@ -91,7 +91,7 @@ Examples: public (bool IsSet, string Value) InputPDBFile { get; } [Option("-l|--list ", "Lists all entities of the specified type(s). Valid types: c(lass), i(nterface), s(truct), d(elegate), e(num)", CommandOptionType.MultipleValue)] - public string[] EntityTypes { get; } = new string[0]; + public string[] EntityTypes { get; } = Array.Empty(); public string DecompilerVersion => "ilspycmd: " + typeof(ILSpyCmdProgram).Assembly.GetName().Version.ToString() + Environment.NewLine @@ -100,9 +100,16 @@ Examples: [Option("-lv|--languageversion ", "C# Language version: CSharp1, CSharp2, CSharp3, " + "CSharp4, CSharp5, CSharp6, CSharp7, CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0, CSharp9_0, " + - "CSharp10_0, Preview or Latest", CommandOptionType.SingleValue)] + "CSharp10_0, CSharp11_0, CSharp12_0, CSharp13_0, Preview or Latest", CommandOptionType.SingleValue)] public LanguageVersion LanguageVersion { get; } = LanguageVersion.Latest; + [FileExists] + [Option("--ilspy-settingsfile ", "Path to an ILSpy settings file.", CommandOptionType.SingleValue)] + public string ILSpySettingsFile { get; } + + [Option("-ds|--decompiler-setting =", "Set a decompiler setting. Use multiple times to set multiple settings.", CommandOptionType.MultipleValue)] + public string[] DecompilerSettingOverrides { get; set; } = Array.Empty(); + [DirectoryExists] [Option("-r|--referencepath ", "Path to a directory containing dependencies of the assembly that is being decompiled.", CommandOptionType.MultipleValue)] public string[] ReferencePaths { get; } @@ -325,13 +332,76 @@ Examples: DecompilerSettings GetSettings(PEFile module) { - return new DecompilerSettings(LanguageVersion) { - ThrowOnAssemblyResolveErrors = false, - RemoveDeadCode = RemoveDeadCode, - RemoveDeadStores = RemoveDeadStores, - UseSdkStyleProjectFormat = WholeProjectDecompiler.CanUseSdkStyleProjectFormat(module), - UseNestedDirectoriesForNamespaces = NestedDirectories, - }; + DecompilerSettings decompilerSettings = null; + + if (ILSpySettingsFile != null) + { + try + { + ILSpyX.Settings.ILSpySettings.SettingsFilePathProvider = new ILSpyX.Settings.DefaultSettingsFilePathProvider(ILSpySettingsFile); + var settingsService = new ILSpyX.Settings.SettingsServiceBase(ILSpyX.Settings.ILSpySettings.Load()); + decompilerSettings = settingsService.GetSettings(); + } + catch (Exception ex) + { + Console.Error.WriteLine($"Error loading ILSpy settings file '{ILSpySettingsFile}': {ex.Message}"); + } + } + + if (decompilerSettings == null) + { + decompilerSettings = new DecompilerSettings(LanguageVersion) { + ThrowOnAssemblyResolveErrors = false, + RemoveDeadCode = RemoveDeadCode, + RemoveDeadStores = RemoveDeadStores, + UseSdkStyleProjectFormat = WholeProjectDecompiler.CanUseSdkStyleProjectFormat(module), + UseNestedDirectoriesForNamespaces = NestedDirectories, + }; + } + + if (DecompilerSettingOverrides is { Length: > 0 }) + { + foreach (var entry in DecompilerSettingOverrides) + { + int equals = entry.IndexOf('='); + if (equals <= 0) + { + Console.Error.WriteLine($"Decompiler setting '{entry}' is invalid; use '='"); + continue; + } + + string name = entry[..equals].Trim(); + string value = entry[(equals + 1)..].Trim(); + + if (!ILSpyX.Settings.DecompilerSettings.IsKnownOption(name, out var property)) + { + Console.Error.WriteLine($"Decompiler setting '{name}' is unknown."); + continue; + } + + object typedValue; + + try + { + typedValue = Convert.ChangeType(value, property.PropertyType); + } + catch (Exception) + { + Console.Error.WriteLine($"Decompiler setting '{name}': Value '{value}' could not be converted to '{property.PropertyType.FullName}'."); + continue; + } + + if (typedValue == null && property.PropertyType.IsValueType) + { + Console.Error.WriteLine($"Decompiler setting '{name}': Value '{value}' could not be converted to '{property.PropertyType.FullName}'."); + continue; + } + + property.SetValue(decompilerSettings, typedValue); + } + } + + return decompilerSettings; } CSharpDecompiler GetDecompiler(string assemblyFileName) diff --git a/ICSharpCode.ILSpyX/Settings/DecompilerSettings.cs b/ICSharpCode.ILSpyX/Settings/DecompilerSettings.cs index 817f002a0..76d437ed8 100644 --- a/ICSharpCode.ILSpyX/Settings/DecompilerSettings.cs +++ b/ICSharpCode.ILSpyX/Settings/DecompilerSettings.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Xml.Linq; @@ -57,5 +58,19 @@ namespace ICSharpCode.ILSpyX.Settings { return (DecompilerSettings)base.Clone(); } + + public static bool IsKnownOption(string name, [NotNullWhen(true)] out PropertyInfo? property) + { + property = null; + foreach (var item in properties) + { + if (item.Name != name) + continue; + property = item; + return true; + } + + return false; + } } }