Skip to content

Commit 96cba4a

Browse files
Merge pull request icsharpcode#3505 from icsharpcode/fix/3494/ilspycmd-settings
Add dynamic ILSpy settings options to ILSpyCmd
2 parents 45a6212 + bf5249b commit 96cba4a

File tree

12 files changed

+183
-70
lines changed

12 files changed

+183
-70
lines changed

ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs

Lines changed: 79 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class ILSpyCmdProgram
9191
public (bool IsSet, string Value) InputPDBFile { get; }
9292

9393
[Option("-l|--list <entity-type(s)>", "Lists all entities of the specified type(s). Valid types: c(lass), i(nterface), s(truct), d(elegate), e(num)", CommandOptionType.MultipleValue)]
94-
public string[] EntityTypes { get; } = new string[0];
94+
public string[] EntityTypes { get; } = Array.Empty<string>();
9595

9696
public string DecompilerVersion => "ilspycmd: " + typeof(ILSpyCmdProgram).Assembly.GetName().Version.ToString() +
9797
Environment.NewLine
@@ -100,9 +100,16 @@ class ILSpyCmdProgram
100100

101101
[Option("-lv|--languageversion <version>", "C# Language version: CSharp1, CSharp2, CSharp3, " +
102102
"CSharp4, CSharp5, CSharp6, CSharp7, CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0, CSharp9_0, " +
103-
"CSharp10_0, Preview or Latest", CommandOptionType.SingleValue)]
103+
"CSharp10_0, CSharp11_0, CSharp12_0, CSharp13_0, Preview or Latest", CommandOptionType.SingleValue)]
104104
public LanguageVersion LanguageVersion { get; } = LanguageVersion.Latest;
105105

106+
[FileExists]
107+
[Option("--ilspy-settingsfile <path>", "Path to an ILSpy settings file.", CommandOptionType.SingleValue)]
108+
public string ILSpySettingsFile { get; }
109+
110+
[Option("-ds|--decompiler-setting <name>=<value>", "Set a decompiler setting. Use multiple times to set multiple settings.", CommandOptionType.MultipleValue)]
111+
public string[] DecompilerSettingOverrides { get; set; } = Array.Empty<string>();
112+
106113
[DirectoryExists]
107114
[Option("-r|--referencepath <path>", "Path to a directory containing dependencies of the assembly that is being decompiled.", CommandOptionType.MultipleValue)]
108115
public string[] ReferencePaths { get; }
@@ -325,13 +332,76 @@ private static string ResolveOutputDirectory(string outputDirectory)
325332

326333
DecompilerSettings GetSettings(PEFile module)
327334
{
328-
return new DecompilerSettings(LanguageVersion) {
329-
ThrowOnAssemblyResolveErrors = false,
330-
RemoveDeadCode = RemoveDeadCode,
331-
RemoveDeadStores = RemoveDeadStores,
332-
UseSdkStyleProjectFormat = WholeProjectDecompiler.CanUseSdkStyleProjectFormat(module),
333-
UseNestedDirectoriesForNamespaces = NestedDirectories,
334-
};
335+
DecompilerSettings decompilerSettings = null;
336+
337+
if (ILSpySettingsFile != null)
338+
{
339+
try
340+
{
341+
ILSpyX.Settings.ILSpySettings.SettingsFilePathProvider = new ILSpyX.Settings.DefaultSettingsFilePathProvider(ILSpySettingsFile);
342+
var settingsService = new ILSpyX.Settings.SettingsServiceBase(ILSpyX.Settings.ILSpySettings.Load());
343+
decompilerSettings = settingsService.GetSettings<ILSpyX.Settings.DecompilerSettings>();
344+
}
345+
catch (Exception ex)
346+
{
347+
Console.Error.WriteLine($"Error loading ILSpy settings file '{ILSpySettingsFile}': {ex.Message}");
348+
}
349+
}
350+
351+
if (decompilerSettings == null)
352+
{
353+
decompilerSettings = new DecompilerSettings(LanguageVersion) {
354+
ThrowOnAssemblyResolveErrors = false,
355+
RemoveDeadCode = RemoveDeadCode,
356+
RemoveDeadStores = RemoveDeadStores,
357+
UseSdkStyleProjectFormat = WholeProjectDecompiler.CanUseSdkStyleProjectFormat(module),
358+
UseNestedDirectoriesForNamespaces = NestedDirectories,
359+
};
360+
}
361+
362+
if (DecompilerSettingOverrides is { Length: > 0 })
363+
{
364+
foreach (var entry in DecompilerSettingOverrides)
365+
{
366+
int equals = entry.IndexOf('=');
367+
if (equals <= 0)
368+
{
369+
Console.Error.WriteLine($"Decompiler setting '{entry}' is invalid; use '<Name>=<Value>'");
370+
continue;
371+
}
372+
373+
string name = entry[..equals].Trim();
374+
string value = entry[(equals + 1)..].Trim();
375+
376+
if (!ILSpyX.Settings.DecompilerSettings.IsKnownOption(name, out var property))
377+
{
378+
Console.Error.WriteLine($"Decompiler setting '{name}' is unknown.");
379+
continue;
380+
}
381+
382+
object typedValue;
383+
384+
try
385+
{
386+
typedValue = Convert.ChangeType(value, property.PropertyType);
387+
}
388+
catch (Exception)
389+
{
390+
Console.Error.WriteLine($"Decompiler setting '{name}': Value '{value}' could not be converted to '{property.PropertyType.FullName}'.");
391+
continue;
392+
}
393+
394+
if (typedValue == null && property.PropertyType.IsValueType)
395+
{
396+
Console.Error.WriteLine($"Decompiler setting '{name}': Value '{value}' could not be converted to '{property.PropertyType.FullName}'.");
397+
continue;
398+
}
399+
400+
property.SetValue(decompilerSettings, typedValue);
401+
}
402+
}
403+
404+
return decompilerSettings;
335405
}
336406

337407
CSharpDecompiler GetDecompiler(string assemblyFileName)

ILSpy/Options/DecompilerSettings.cs renamed to ICSharpCode.ILSpyX/Settings/DecompilerSettings.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2024 Tom Englert for the SharpDevelop Team
1+
// Copyright (c) 2024 Tom Englert
22
//
33
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
44
// software and associated documentation files (the "Software"), to deal in the Software
@@ -17,13 +17,12 @@
1717
// DEALINGS IN THE SOFTWARE.
1818

1919
using System.ComponentModel;
20+
using System.Diagnostics.CodeAnalysis;
2021
using System.Linq;
2122
using System.Reflection;
2223
using System.Xml.Linq;
2324

24-
#nullable enable
25-
26-
namespace ICSharpCode.ILSpy.Options
25+
namespace ICSharpCode.ILSpyX.Settings
2726
{
2827
public class DecompilerSettings : Decompiler.DecompilerSettings, ISettingsSection
2928
{
@@ -59,5 +58,19 @@ public override DecompilerSettings Clone()
5958
{
6059
return (DecompilerSettings)base.Clone();
6160
}
61+
62+
public static bool IsKnownOption(string name, [NotNullWhen(true)] out PropertyInfo? property)
63+
{
64+
property = null;
65+
foreach (var item in properties)
66+
{
67+
if (item.Name != name)
68+
continue;
69+
property = item;
70+
return true;
71+
}
72+
73+
return false;
74+
}
6275
}
6376
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) 2024 Tom Englert
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4+
// software and associated documentation files (the "Software"), to deal in the Software
5+
// without restriction, including without limitation the rights to use, copy, modify, merge,
6+
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7+
// to whom the Software is furnished to do so, subject to the following conditions:
8+
//
9+
// The above copyright notice and this permission notice shall be included in all copies or
10+
// substantial portions of the Software.
11+
//
12+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13+
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14+
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15+
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17+
// DEALINGS IN THE SOFTWARE.
18+
19+
using System;
20+
using System.Collections.Concurrent;
21+
using System.ComponentModel;
22+
using System.Xml.Linq;
23+
24+
namespace ICSharpCode.ILSpyX.Settings
25+
{
26+
public interface IChildSettings
27+
{
28+
ISettingsSection Parent { get; }
29+
}
30+
31+
public interface ISettingsSection : INotifyPropertyChanged
32+
{
33+
XName SectionName { get; }
34+
35+
void LoadFromXml(XElement section);
36+
37+
XElement SaveToXml();
38+
}
39+
40+
public class SettingsServiceBase(ISettingsProvider spySettings)
41+
{
42+
protected readonly ConcurrentDictionary<Type, ISettingsSection> sections = new();
43+
44+
protected ISettingsProvider SpySettings { get; set; } = spySettings;
45+
46+
public T GetSettings<T>() where T : ISettingsSection, new()
47+
{
48+
return (T)sections.GetOrAdd(typeof(T), _ => {
49+
T section = new T();
50+
51+
var sectionElement = SpySettings[section.SectionName];
52+
53+
section.LoadFromXml(sectionElement);
54+
section.PropertyChanged += Section_PropertyChanged;
55+
56+
return section;
57+
});
58+
}
59+
60+
protected static void SaveSection(ISettingsSection section, XElement root)
61+
{
62+
var element = section.SaveToXml();
63+
64+
var existingElement = root.Element(section.SectionName);
65+
if (existingElement != null)
66+
existingElement.ReplaceWith(element);
67+
else
68+
root.Add(element);
69+
}
70+
71+
protected virtual void Section_PropertyChanged(object? sender, PropertyChangedEventArgs e)
72+
{
73+
}
74+
}
75+
}

ILSpy.ReadyToRun/ReadyToRunOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
using System.Xml.Linq;
2020

21-
using ICSharpCode.ILSpy.Util;
21+
using ICSharpCode.ILSpyX.Settings;
2222

2323
using TomsToolbox.Wpf;
2424

ILSpy/DecompilationOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
using ICSharpCode.ILSpy.Options;
2424
using ICSharpCode.ILSpyX;
2525

26-
using DecompilerSettings = ICSharpCode.ILSpy.Options.DecompilerSettings;
26+
using DecompilerSettings = ICSharpCode.ILSpyX.Settings.DecompilerSettings;
2727

2828
namespace ICSharpCode.ILSpy
2929
{

ILSpy/LanguageSettings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Xml.Linq;
2121

2222
using ICSharpCode.ILSpyX;
23+
using ICSharpCode.ILSpyX.Settings;
2324

2425
using TomsToolbox.Wpf;
2526

ILSpy/Options/DecompilerSettingsViewModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
using ICSharpCode.ILSpy.Properties;
2626
using ICSharpCode.ILSpy.TreeNodes;
27+
using ICSharpCode.ILSpyX.Settings;
2728

2829
using TomsToolbox.Wpf;
2930

ILSpy/Options/DisplaySettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
using System.Windows.Media;
2020
using System.Xml.Linq;
2121

22+
using ICSharpCode.ILSpyX.Settings;
23+
2224
using TomsToolbox.Wpf;
2325

2426
namespace ICSharpCode.ILSpy.Options

ILSpy/SessionSettings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
using ICSharpCode.ILSpy.Docking;
3131
using ICSharpCode.ILSpy.Themes;
3232
using ICSharpCode.ILSpyX.Search;
33+
using ICSharpCode.ILSpyX.Settings;
3334

3435
namespace ICSharpCode.ILSpy
3536
{

ILSpy/Updates/UpdateSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
using System;
2020
using System.Xml.Linq;
2121

22+
using ICSharpCode.ILSpyX.Settings;
23+
2224
using TomsToolbox.Wpf;
2325

2426
namespace ICSharpCode.ILSpy.Updates

0 commit comments

Comments
 (0)