Compare commits

...

40 Commits

Author SHA1 Message Date
Christoph Wille 98ee6c3b84
Merge pull request #3731 from siegfriedpammer/runtime-async 3 days ago
Siegfried Pammer 391bf28f81 Drop the per-call predicate from FindFlagInitStore. 3 days ago
Siegfried Pammer a9a43f96a9 Reverse runtime-async multi-handler try-catch dispatched via an if-chain. 3 days ago
Siegfried Pammer 8e2e48f5cc Generalise runtime-async flag-based early-return for nested try-finally. 3 days ago
Siegfried Pammer 8a03ee246f Reverse runtime-async try-finally where the try body always throws. 3 days ago
Siegfried Pammer 8059fde539 Reverse runtime-async exception lowering for try-catch nested inside try-finally. 3 days ago
Siegfried Pammer fadcb3543c Cover ConfigureAwait and [MethodImpl(NoInlining)] in the Async pretty test fixture. 3 days ago
Siegfried Pammer c107c5a4bc Reverse runtime-async flag-based early-return across a try-finally. 3 days ago
Siegfried Pammer b29dd718a5 Recover the this-copy that runtime-async inserts at the entry of struct async methods. 3 days ago
Siegfried Pammer 1bfa3b821f Reduce runtime-async manual await pattern (non-Task awaitables) to Await. 3 days ago
Siegfried Pammer 7c39e9ea5e Reduce multi-handler runtime-async try-catch back to a multi-handler TryCatch. 3 days ago
Siegfried Pammer 58baac0c50 Normalize runtime-async catch filters to recover catch-with-when. 3 days ago
Siegfried Pammer 255e73f3a0 Reverse runtime-async lowering of try-finally and try-catch with await. 3 days ago
Siegfried Pammer d28ecf1b50 Add TransformAsyncHelpersAwaitToAwait to EarlyExpressionTransforms. 3 days ago
Siegfried Pammer 1a297bebb2 Handle non-Task task-likes (ValueTask, custom AsyncMethodBuilder) in ILReader async return-type unpacking. 3 days ago
Siegfried Pammer 82edef3bc6 Add scaffolding for the C# 15 runtime-async lowering. 3 days ago
Siegfried Pammer cf23d76a5e Add tests 3 days ago
Christoph Wille 00e21afd80 Use test-summary/action@ again (merge of contribution that we depended on in https://github.com/test-summary/action/releases/tag/v2.5) 6 days ago
Christoph Wille 1a9807079d Fix typos in cmd line options help (ILSpyCmd) 6 days ago
Christoph Wille b67ba16f69
Fix #3734: Search pane flickers when navigating to search result in large assembly 1 week ago
Christoph Wille bbbff59ce2
Merge pull request #3733 from icsharpcode/christophwille/updates 2 weeks ago
Christoph Wille 5833ee57a4 Updates for .NET Preview 4 2 weeks ago
Christoph Wille 6cd4131d59 Update NuGet packages (vulnerable and basic) 2 weeks ago
Christoph Wille 2826fff43a Installer NuGet updates (and link to Wix7 issue) 2 weeks ago
Christoph Wille a41156ff1d Update ILSpyCmd README.md 2 weeks ago
Siegfried Pammer dfc2bbb970
Merge pull request #3726 from icsharpcode/christophwille/3705 2 weeks ago
Siegfried Pammer 4c347ef6b9 Refactor RefreshInternal: HandleExceptions, FindAssembly, race-safe selection. 2 weeks ago
Christoph Wille 4c8e606a6a Fix #3705: Code window is empty when select a .baml and refresh 2 weeks ago
Siegfried Pammer 94f574ed9c
Merge pull request #3732 from siegfriedpammer/investigate-warnings 2 weeks ago
Siegfried Pammer eec031a220 Make SessionSettings.FromString robust against malformed saved values. 2 weeks ago
Siegfried Pammer fd6a81e7cb Suppress CA1063 / CA2213 on the ported ResXResourceWriter. 2 weeks ago
Siegfried Pammer a541fc0ddf Fix CS9336 precedence bug in CompoundAssignmentInstruction. 2 weeks ago
Siegfried Pammer 317e33b3b8 Fix five small analyzer warnings. 2 weeks ago
Siegfried Pammer 784379d012 Annotate intentional analyzer-violations with [SuppressMessage]. 2 weeks ago
Siegfried Pammer a8d841199d Lift IDisposable to MetadataFile and dispose evicted assemblies. 2 weeks ago
Siegfried Pammer 775ff371ac
Merge pull request #3730 from siegfriedpammer/ilspycmd-baml-resources 2 weeks ago
Siegfried Pammer d5e2e61a90 ilspycmd: route update-check messages to stderr 2 weeks ago
Siegfried Pammer 28fffb7e2c ilspycmd: add --list-resources, --resource, and --decompile-baml 2 weeks ago
Siegfried Pammer 50afc97e7b ilspycmd: add resource enumeration and BAML→XAML helpers 2 weeks ago
Siegfried Pammer d3e9121a32 ilspycmd: reference ICSharpCode.BamlDecompiler 2 weeks ago
  1. 4
      .github/workflows/build-ilspy.yml
  2. 14
      Directory.Packages.props
  3. 10
      ICSharpCode.BamlDecompiler/packages.lock.json
  4. 16
      ICSharpCode.Decompiler.Tests/Helpers/Tester.cs
  5. 40
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  6. 145
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs
  7. 3
      ICSharpCode.Decompiler/CSharp/CSharpLanguageVersion.cs
  8. 3
      ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs
  9. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs
  10. 28
      ICSharpCode.Decompiler/DecompilerSettings.cs
  11. 6
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  12. 59
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  13. 136
      ICSharpCode.Decompiler/IL/ControlFlow/AwaitInFinallyTransform.cs
  14. 1416
      ICSharpCode.Decompiler/IL/ControlFlow/RuntimeAsyncExceptionRewriteTransform.cs
  15. 177
      ICSharpCode.Decompiler/IL/ControlFlow/RuntimeAsyncManualAwaitTransform.cs
  16. 16
      ICSharpCode.Decompiler/IL/ILReader.cs
  17. 2
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  18. 33
      ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs
  19. 15
      ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs
  20. 15
      ICSharpCode.Decompiler/Metadata/MetadataFile.cs
  21. 8
      ICSharpCode.Decompiler/Metadata/PEFile.cs
  22. 8
      ICSharpCode.Decompiler/Metadata/WebCilFile.cs
  23. 11
      ICSharpCode.Decompiler/Output/PlainTextOutput.cs
  24. 4
      ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs
  25. 1
      ICSharpCode.Decompiler/SRMHacks.cs
  26. 7
      ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs
  27. 3
      ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs
  28. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs
  29. 22
      ICSharpCode.Decompiler/TypeSystem/TaskType.cs
  30. 3
      ICSharpCode.Decompiler/Util/EmptyList.cs
  31. 5
      ICSharpCode.Decompiler/Util/LongSet.cs
  32. 5
      ICSharpCode.Decompiler/Util/ResXResourceWriter.cs
  33. 2
      ICSharpCode.Decompiler/Util/ResourcesFile.cs
  34. 32
      ICSharpCode.Decompiler/packages.lock.json
  35. 95
      ICSharpCode.ILSpyCmd/BamlAwareWholeProjectDecompiler.cs
  36. 1
      ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj
  37. 136
      ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs
  38. 29
      ICSharpCode.ILSpyCmd/README.md
  39. 177
      ICSharpCode.ILSpyCmd/ResourceExtensions.cs
  40. 386
      ICSharpCode.ILSpyCmd/packages.lock.json
  41. 14
      ICSharpCode.ILSpyX/AssemblyList.cs
  42. 11
      ICSharpCode.ILSpyX/LoadedAssembly.cs
  43. 7
      ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs
  44. 46
      ICSharpCode.ILSpyX/packages.lock.json
  45. 2
      ILSpy.Installer/ILSpy.Installer.csproj
  46. 3
      ILSpy.Installer/setup.cs
  47. 3
      ILSpy.ReadyToRun/Properties/AssemblyInfo.cs
  48. 29
      ILSpy/AssemblyTree/AssemblyTreeModel.cs
  49. 3
      ILSpy/Languages/CSharpLanguage.cs
  50. 11
      ILSpy/Properties/Resources.Designer.cs
  51. 3
      ILSpy/Properties/Resources.resx
  52. 15
      ILSpy/Search/SearchPane.xaml.cs
  53. 7
      ILSpy/SessionSettings.cs

4
.github/workflows/build-ilspy.yml

@ -53,7 +53,7 @@ jobs: @@ -53,7 +53,7 @@ jobs:
- name: Install wix (locked version)
run: dotnet tool install --global wix --version 6.0.2
- name: Install wix extesnion (locked version)
- name: Install wix extension (locked version)
run: wix.exe extension add -g WixToolset.UI.wixext/6.0.2
- name: Get Version
@ -83,7 +83,7 @@ jobs: @@ -83,7 +83,7 @@ jobs:
path: 'test-results/${{ matrix.configuration }}/*.trx'
- name: Create Test Report
uses: icsharpcode/test-summary-action@dist
uses: test-summary/action@v2
if: always()
with:
paths: "test-results/${{ matrix.configuration }}/*.trx"

14
Directory.Packages.props

@ -4,9 +4,9 @@ @@ -4,9 +4,9 @@
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<PropertyGroup>
<RoslynVersion>5.7.0-1.26207.106</RoslynVersion>
<NetPackageVersion>10.0.6</NetPackageVersion>
<TomsToolboxVersion>2.23.0</TomsToolboxVersion>
<RoslynVersion>5.7.0-1.26230.115</RoslynVersion>
<NetPackageVersion>10.0.8</NetPackageVersion>
<TomsToolboxVersion>2.24.0</TomsToolboxVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AvalonEdit" Version="6.3.1.120" />
@ -33,16 +33,16 @@ @@ -33,16 +33,16 @@
<PackageVersion Include="Microsoft.NETCore.ILAsm" Version="$(NetPackageVersion)" />
<PackageVersion Include="Microsoft.NETCore.ILDAsm" Version="$(NetPackageVersion)" />
<PackageVersion Include="Microsoft.Sbom.Targets" Version="4.1.5" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.202" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.300" />
<PackageVersion Include="Microsoft.Testing.Extensions.TrxReport" Version="2.1.0" />
<PackageVersion Include="Microsoft.Testing.Extensions.VSTestBridge" Version="2.1.0" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.142" />
<PackageVersion Include="Mono.Cecil" Version="0.11.6" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" />
<PackageVersion Include="NUnit" Version="4.5.1" />
<PackageVersion Include="NUnit" Version="4.6.0" />
<PackageVersion Include="NUnit3TestAdapter" Version="6.2.0" />
<PackageVersion Include="NuGet.Protocol" Version="7.3.0" />
<PackageVersion Include="NuGet.Protocol" Version="7.6.0" />
<PackageVersion Include="PowerShellStandard.Library" Version="5.1.1" />
<PackageVersion Include="System.Composition.AttributedModel" Version="$(NetPackageVersion)" />
<PackageVersion Include="System.Collections.Immutable" Version="$(NetPackageVersion)" />
@ -55,7 +55,7 @@ @@ -55,7 +55,7 @@
<PackageVersion Include="TomsToolbox.Wpf.Composition" Version="$(TomsToolboxVersion)" />
<PackageVersion Include="TomsToolbox.Wpf.Composition.AttributedModel" Version="$(TomsToolboxVersion)" />
<PackageVersion Include="TomsToolbox.Wpf.Styles" Version="$(TomsToolboxVersion)" />
<PackageVersion Include="coverlet.MTP" Version="8.0.1" />
<PackageVersion Include="coverlet.MTP" Version="10.0.0" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Private.Uri" Version="4.3.2" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />

10
ICSharpCode.BamlDecompiler/packages.lock.json

@ -10,9 +10,9 @@ @@ -10,9 +10,9 @@
},
"TomsToolbox.Composition.Analyzer": {
"type": "Direct",
"requested": "[2.23.0, )",
"resolved": "2.23.0",
"contentHash": "WHvTkPV5AI9FQY7MB7PUWgIkm69NEiCmuG0A7cVXFF/wNQ0xI5YjS60a20vM1Z1Sg+mDDkZ4452hwFGaky0Txg=="
"requested": "[2.24.0, )",
"resolved": "2.24.0",
"contentHash": "dKHqW1MeAMnDIbtx8qDTsGwy/7LUiQ3ccdzHX0PzCh1r98Lgl/1deIky9+ojZO0K5HjeA7uE+eW9/h+v7EOIBA=="
},
"icsharpcode.decompiler": {
"type": "Project",
@ -23,13 +23,13 @@ @@ -23,13 +23,13 @@
},
"System.Collections.Immutable": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"requested": "[10.0.8, )",
"resolved": "9.0.0",
"contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w=="
},
"System.Reflection.Metadata": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"requested": "[10.0.8, )",
"resolved": "9.0.0",
"contentHash": "ANiqLu3DxW9kol/hMmTWbt3414t9ftdIuiIU7j80okq2YzAueo120M442xk1kDJWtmZTqWQn7wHDvMRipVOEOQ=="
}

16
ICSharpCode.Decompiler.Tests/Helpers/Tester.cs

@ -73,6 +73,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -73,6 +73,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
CheckForOverflowUnderflow = 0x20000,
ProcessXmlDoc = 0x40000,
UseRoslyn4_14_0 = 0x80000,
EnableRuntimeAsync = 0x100000,
UseMcsMask = UseMcs2_6_4 | UseMcs5_23,
UseRoslynMask = UseRoslyn1_3_2 | UseRoslyn2_10_0 | UseRoslyn3_11_0 | UseRoslyn4_14_0 | UseRoslynLatest
}
@ -96,7 +97,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers @@ -96,7 +97,7 @@ namespace ICSharpCode.Decompiler.Tests.Helpers
{
public const string CurrentNetCoreVersion = "11.0";
public const string CurrentNetCoreAppVersion = ".NETCoreApp,Version=v11.0";
public const string CurrentNetCoreRefAsmVersion = "11.0.0-preview.3.26207.106";
public const string CurrentNetCoreRefAsmVersion = "11.0.0-preview.4.26230.115";
public static readonly string TesterPath;
public static readonly string TestCasePath;
@ -500,6 +501,10 @@ namespace System.Runtime.CompilerServices @@ -500,6 +501,10 @@ namespace System.Runtime.CompilerServices
preprocessorSymbols.Add("LEGACY_CSC");
preprocessorSymbols.Add("LEGACY_VBC");
}
if (flags.HasFlag(CompilerOptions.EnableRuntimeAsync))
{
preprocessorSymbols.Add("RUNTIMEASYNC");
}
return preprocessorSymbols;
}
@ -605,13 +610,18 @@ namespace System.Runtime.CompilerServices @@ -605,13 +610,18 @@ namespace System.Runtime.CompilerServices
if (roslynVersion != "legacy")
{
otherOptions += "/shared ";
if (!targetNet40 && Version.Parse(RoslynToolset.SanitizeVersion(roslynVersion)).Major > 2)
var version = Version.Parse(RoslynToolset.SanitizeVersion(roslynVersion));
if (!targetNet40 && version.Major > 2)
{
if (flags.HasFlag(CompilerOptions.NullableEnable))
otherOptions += "/nullable+ ";
else
otherOptions += "/nullable- ";
}
if (!targetNet40 && roslynVersion == roslynLatestVersion && flags.HasFlag(CompilerOptions.EnableRuntimeAsync))
{
otherOptions += "/features:runtime-async=on ";
}
}
if (flags.HasFlag(CompilerOptions.Library))
@ -842,6 +852,8 @@ namespace System.Runtime.CompilerServices @@ -842,6 +852,8 @@ namespace System.Runtime.CompilerServices
suffix += ".mcs2";
if ((cscOptions & CompilerOptions.UseMcs5_23) != 0)
suffix += ".mcs5";
if ((cscOptions & CompilerOptions.EnableRuntimeAsync) != 0)
suffix += ".runtimeasync";
return suffix;
}

40
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -537,6 +537,46 @@ namespace ICSharpCode.Decompiler.Tests @@ -537,6 +537,46 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task RuntimeAsync([ValueSource(nameof(roslyn5OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary("Async", cscOptions: cscOptions | CompilerOptions.EnableRuntimeAsync | CompilerOptions.Preview);
}
[Test]
public async Task RuntimeAsyncForeach([ValueSource(nameof(roslyn5OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary("AsyncForeach", cscOptions: cscOptions | CompilerOptions.EnableRuntimeAsync | CompilerOptions.Preview | CompilerOptions.GeneratePdb);
}
[Test]
public async Task RuntimeAsyncMain([ValueSource(nameof(roslyn5OrNewerOptions))] CompilerOptions cscOptions)
{
await Run("AsyncMain", cscOptions: cscOptions | CompilerOptions.EnableRuntimeAsync | CompilerOptions.Preview);
}
[Test]
public async Task RuntimeAsyncStreams([ValueSource(nameof(roslyn5OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary("AsyncStreams", cscOptions: cscOptions | CompilerOptions.EnableRuntimeAsync | CompilerOptions.Preview);
}
[Test]
public async Task RuntimeAsyncUsing([ValueSource(nameof(roslyn5OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(
"AsyncUsing",
cscOptions: cscOptions | CompilerOptions.EnableRuntimeAsync | CompilerOptions.Preview,
configureDecompiler: settings => { settings.UseEnhancedUsing = false; }
);
}
[Test]
public async Task RuntimeAsyncCustomTaskType([ValueSource(nameof(roslyn5OrNewerOptions))] CompilerOptions cscOptions)
{
await RunForLibrary("CustomTaskType", cscOptions: cscOptions | CompilerOptions.EnableRuntimeAsync | CompilerOptions.Preview);
}
[Test]
public async Task NullableRefTypes([ValueSource(nameof(roslyn3OrNewerOptions))] CompilerOptions cscOptions)
{

145
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Async.cs

@ -74,6 +74,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -74,6 +74,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
Console.WriteLine("After");
}
[MethodImpl(MethodImplOptions.NoInlining)]
public async Task NoInliningTaskMethod()
{
await Task.Yield();
}
public async Task TaskMethodWithoutAwait()
{
Console.WriteLine("No Await");
@ -115,6 +121,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -115,6 +121,24 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
public async Task AwaitConfigureAwaitFalse(Task<int> task)
{
#if ROSLYN2
Console.WriteLine(await task.ConfigureAwait(continueOnCapturedContext: false));
#else
Console.WriteLine(await task.ConfigureAwait(false));
#endif
}
public async Task<int> AwaitConfigureAwaitMixed(Task<int> task1, Task<int> task2)
{
#if ROSLYN2
return await task1.ConfigureAwait(continueOnCapturedContext: false) + await task2.ConfigureAwait(continueOnCapturedContext: true);
#else
return await task1.ConfigureAwait(false) + await task2.ConfigureAwait(true);
#endif
}
#if CS60
public async Task AwaitInCatch(bool b, Task<int> task1, Task<int> task2)
{
@ -359,6 +383,127 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -359,6 +383,127 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
return new object();
}
public async Task TryCatchFinallyAllAwait()
{
try
{
await Task.CompletedTask;
Console.WriteLine("try");
}
catch (Exception)
{
await Task.CompletedTask;
Console.WriteLine("catch");
}
finally
{
await Task.CompletedTask;
Console.WriteLine("finally");
}
}
public async Task ThrowInsideTryFinally()
{
try
{
throw new InvalidOperationException();
}
finally
{
await Task.Yield();
}
}
public async Task HeterogeneousMultiCatch1()
{
try
{
await Task.Yield();
}
catch (InvalidOperationException ex)
{
await Task.Yield();
Console.WriteLine(ex.Message);
}
catch (ArgumentException ex2)
{
await Task.Yield();
Console.WriteLine(ex2.Message);
}
}
public async Task HeterogeneousMultiCatch2()
{
try
{
await Task.Yield();
}
catch (InvalidOperationException ex)
{
await Task.Yield();
Console.WriteLine(ex.Message);
}
catch
{
await Task.Yield();
Console.WriteLine("other");
}
}
public async Task HeterogeneousMultiCatch3()
{
try
{
await Task.Yield();
}
catch (InvalidOperationException ex)
{
await Task.Yield();
Console.WriteLine(ex.Message);
}
catch (Exception)
{
await Task.Yield();
throw;
}
}
#if RUNTIMEASYNC
// The state-machine async lowering doesn't recognize return-from-try-with-await-in-finally
// and decompiles these as `int result; try { ... } finally { ... } return result;`. The
// runtime-async exception rewrite recovers the source-level form. Gate these tests so the
// (state-machine) Async test doesn't run them against the more aggressive output.
public async Task<int> ReturnFromTryFinally()
{
try
{
return 42;
}
finally
{
await Task.CompletedTask;
}
}
public async Task<int> ReturnFromInsideNestedTryFinally()
{
try
{
try
{
return 42;
}
finally
{
await Task.CompletedTask;
}
}
finally
{
await Task.CompletedTask;
}
}
#endif
#endif
public static async Task<int> GetIntegerSumAsync(IEnumerable<int> items)

3
ICSharpCode.Decompiler/CSharp/CSharpLanguageVersion.cs

@ -37,7 +37,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -37,7 +37,8 @@ namespace ICSharpCode.Decompiler.CSharp
CSharp12_0 = 1200,
CSharp13_0 = 1300,
CSharp14_0 = 1400,
Preview = 1400,
CSharp15_0 = 1500,
Preview = 1500,
Latest = 0x7FFFFFFF
}
}

3
ICSharpCode.Decompiler/CSharp/Syntax/IAnnotatable.cs

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
@ -115,6 +116,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -115,6 +116,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
}
[SuppressMessage("Reliability", "CA2002:Do not lock on objects with weak identity",
Justification = "AnnotationList is a private nested type — the surrounding Annotatable class deliberately locks on the AnnotationList instance to serialize annotation reads/writes; external code cannot obtain a reference to it.")]
public object Clone()
{
lock (this)

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/ExtensionDeclaration.cs

@ -24,7 +24,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -24,7 +24,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{
public readonly static TokenRole ExtensionKeywordRole = new TokenRole("extension");
public override SymbolKind SymbolKind => throw new System.NotImplementedException();
public override SymbolKind SymbolKind => SymbolKind.TypeDefinition;
public AstNodeCollection<TypeParameterDeclaration> TypeParameters {
get { return GetChildrenByRole(Roles.TypeParameter); }

28
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -177,10 +177,16 @@ namespace ICSharpCode.Decompiler @@ -177,10 +177,16 @@ namespace ICSharpCode.Decompiler
extensionMembers = false;
firstClassSpanTypes = false;
}
if (languageVersion < CSharp.LanguageVersion.CSharp15_0)
{
runtimeAsync = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (runtimeAsync)
return CSharp.LanguageVersion.CSharp15_0;
if (extensionMembers || firstClassSpanTypes)
return CSharp.LanguageVersion.CSharp14_0;
if (paramsCollections)
@ -2167,7 +2173,7 @@ namespace ICSharpCode.Decompiler @@ -2167,7 +2173,7 @@ namespace ICSharpCode.Decompiler
/// <summary>
/// Gets/Sets whether C# 14.0 extension members should be transformed.
/// </summary>
[Category("C# 14.0 / VS 202x.yy")]
[Category("C# 14.0 / VS 2026")]
[Description("DecompilerSettings.ExtensionMembers")]
public bool ExtensionMembers {
get { return extensionMembers; }
@ -2185,7 +2191,7 @@ namespace ICSharpCode.Decompiler @@ -2185,7 +2191,7 @@ namespace ICSharpCode.Decompiler
/// <summary>
/// Gets/Sets whether (ReadOnly)Span&lt;T&gt; should be treated like built-in types.
/// </summary>
[Category("C# 14.0 / VS 202x.yy")]
[Category("C# 14.0 / VS 2026")]
[Description("DecompilerSettings.FirstClassSpanTypes")]
public bool FirstClassSpanTypes {
get { return firstClassSpanTypes; }
@ -2198,6 +2204,24 @@ namespace ICSharpCode.Decompiler @@ -2198,6 +2204,24 @@ namespace ICSharpCode.Decompiler
}
}
bool runtimeAsync = true;
/// <summary>
/// Gets/Sets whether runtime async should be used.
/// </summary>
[Category("C# 15.0 / VS 202x.yy")]
[Description("DecompilerSettings.RuntimeAsync")]
public bool RuntimeAsync {
get { return runtimeAsync; }
set {
if (runtimeAsync != value)
{
runtimeAsync = value;
OnPropertyChanged();
}
}
}
bool separateLocalVariableDeclarations = false;
/// <summary>

6
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -85,11 +85,11 @@ @@ -85,11 +85,11 @@
</PackageReference>
<PackageReference Include="System.Collections.Immutable" Version="9.0.0" />
<PackageReference Include="System.Reflection.Metadata" Version="9.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.202">
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.300">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.202">
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="10.0.300">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
@ -149,6 +149,8 @@ @@ -149,6 +149,8 @@
<Compile Include="Disassembler\DisassemblerSignatureTypeProvider.cs" />
<Compile Include="Documentation\XmlDocumentationElement.cs" />
<Compile Include="IL\ControlFlow\AwaitInFinallyTransform.cs" />
<Compile Include="IL\ControlFlow\RuntimeAsyncExceptionRewriteTransform.cs" />
<Compile Include="IL\ControlFlow\RuntimeAsyncManualAwaitTransform.cs" />
<Compile Include="IL\Transforms\InterpolatedStringTransform.cs" />
<Compile Include="IL\Transforms\IntroduceNativeIntTypeOnLocals.cs" />
<Compile Include="IL\Transforms\LdLocaDupInitObjTransform.cs" />

59
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -121,7 +121,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -121,7 +121,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
awaitDebugInfos.Clear();
moveNextLeaves.Clear();
if (!MatchTaskCreationPattern(function) && !MatchAsyncEnumeratorCreationPattern(function))
{
if (function.IsAsync && context.Settings.RuntimeAsync)
{
TranslateThisCopyAccess(function);
RuntimeAsyncManualAwaitTransform.Run(function, context);
RuntimeAsyncExceptionRewriteTransform.Run(function, context);
}
return;
}
try
{
AnalyzeMoveNext();
@ -174,6 +182,57 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -174,6 +182,57 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
function.AsyncDebugInfo = new AsyncDebugInfo(catchHandlerOffset, awaitDebugInfos.ToImmutableArray());
}
// Runtime-async analog of fieldToParameterMap's `<>4__this` capture: in a struct method,
// Roslyn lowers `this` references to a single ldobj at function entry stored into a local.
// Match the entry instruction `stloc V_X(ldobj T(ldloc this))` and rewrite every later
// `ldloc V_X` / `ldloca V_X` to go through the `this` parameter again, so AST emission
// renders accesses as plain `this.field` / `field` rather than `<copy>.field`.
static bool TranslateThisCopyAccess(ILFunction function)
{
// Only applies to struct methods — that's the only shape where Roslyn copies *this
// at entry to keep the managed ref off the async resume path.
if (function.Method?.DeclaringTypeDefinition?.Kind != TypeKind.Struct)
return false;
var thisParam = function.Variables.FirstOrDefault(v => v.Kind == VariableKind.Parameter && v.Index == -1);
if (thisParam == null)
return false;
if (thisParam.Type is not ByReferenceType thisRef)
return false;
if (function.Body is not BlockContainer body || body.Blocks.Count == 0)
return false;
var entry = body.EntryPoint;
if (entry.Instructions.Count == 0)
return false;
if (!entry.Instructions[0].MatchStLoc(out var copyVar, out var copyValue))
return false;
if (copyVar.Kind != VariableKind.Local)
return false;
if (copyVar.StoreInstructions.Count != 1)
return false;
if (copyValue is not LdObj ldobj)
return false;
if (!ldobj.Target.MatchLdLoc(thisParam))
return false;
if (!copyVar.Type.Equals(ldobj.Type) || !thisRef.ElementType.Equals(ldobj.Type))
return false;
foreach (var ldloc in function.Descendants.OfType<LdLoc>().ToArray())
{
if (ldloc.Variable != copyVar)
continue;
ldloc.ReplaceWith(new LdObj(new LdLoc(thisParam), ldobj.Type).WithILRange(ldloc));
}
foreach (var ldloca in function.Descendants.OfType<LdLoca>().ToArray())
{
if (ldloca.Variable != copyVar)
continue;
ldloca.ReplaceWith(new LdLoc(thisParam).WithILRange(ldloca));
}
entry.Instructions.RemoveAt(0);
return true;
}
private void CleanUpBodyOfMoveNext(ILFunction function)
{
context.StepStartGroup("CleanUpBodyOfMoveNext", function);

136
ICSharpCode.Decompiler/IL/ControlFlow/AwaitInFinallyTransform.cs

@ -78,31 +78,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -78,31 +78,11 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// [stloc V_3(ldloc E_100) - copy exception variable to a temporary]
// stloc V_6(ldloc V_3) - store exception in 'global' object variable
// br IL_0075 - jump out of catch block to the head of the finallyBlock
var catchBlockEntry = catchBlockContainer.EntryPoint;
ILVariable objectVariable;
switch (catchBlockEntry.Instructions.Count)
if (!MatchObjectStoreCatchHandler(catchBlockContainer, exceptionVariable,
out var objectVariable, out var entryPointOfFinally))
{
case 2:
if (!catchBlockEntry.Instructions[0].MatchStLoc(out objectVariable, out var value))
continue;
if (!value.MatchLdLoc(exceptionVariable))
continue;
break;
case 3:
if (!catchBlockEntry.Instructions[0].MatchStLoc(out var temporaryVariable, out value))
continue;
if (!value.MatchLdLoc(exceptionVariable))
continue;
if (!catchBlockEntry.Instructions[1].MatchStLoc(out objectVariable, out value))
continue;
if (!value.MatchLdLoc(temporaryVariable))
continue;
break;
default:
continue;
}
if (!catchBlockEntry.Instructions[catchBlockEntry.Instructions.Count - 1].MatchBranch(out var entryPointOfFinally))
continue;
}
// globalCopyVar should only be used once, at the end of the finally-block
if (objectVariable.LoadCount != 1 || objectVariable.StoreCount > 2)
continue;
@ -136,7 +116,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -136,7 +116,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
tryCatch.ReplaceWith(new TryFinally(tryCatch.TryBlock, finallyContainer).WithILRange(tryCatch.TryBlock));
context.Step("Move blocks into finally", finallyContainer);
MoveDominatedBlocksToContainer(entryPointOfFinally, beforeExceptionCaptureBlock, cfg, finallyContainer);
MoveDominatedBlocksToContainer(entryPointOfFinally, beforeExceptionCaptureBlock, cfg, finallyContainer, context);
SimplifyEndOfFinally(context, objectVariable, beforeExceptionCaptureBlock, objectVariableCopy, finallyContainer);
@ -193,38 +173,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -193,38 +173,6 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
}
void MoveDominatedBlocksToContainer(Block newEntryPoint, Block endBlock, ControlFlowGraph graph,
BlockContainer targetContainer)
{
var node = graph.GetNode(newEntryPoint);
var endNode = endBlock == null ? null : graph.GetNode(endBlock);
MoveBlock(newEntryPoint, targetContainer);
foreach (var n in graph.cfg)
{
Block block = (Block)n.UserData;
if (node.Dominates(n))
{
if (endNode != null && endNode != n && endNode.Dominates(n))
continue;
if (block.Parent == targetContainer)
continue;
MoveBlock(block, targetContainer);
}
}
}
void MoveBlock(Block block, BlockContainer target)
{
context.Step($"Move {block.Label} to container at IL_{target.StartILOffset:x4}", target);
block.Remove();
target.Blocks.Add(block);
}
static void SimplifyEndOfFinally(ILTransformContext context, ILVariable objectVariable, Block beforeExceptionCaptureBlock, ILVariable objectVariableCopy, BlockContainer finallyContainer)
{
if (beforeExceptionCaptureBlock.Instructions.Count >= 3
@ -281,6 +229,82 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -281,6 +229,82 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
return true;
}
/// <summary>
/// Matches a catch handler that stores its exception in an "object"-typed local and then
/// branches out of the catch:
/// [stloc tmp(ldloc exceptionVariable);]
/// stloc objectVariable(ldloc tmp_or_exceptionVariable)
/// br branchTarget
/// Used by the state-machine async lowering AND by the runtime-async try-finally lowering.
/// </summary>
internal static bool MatchObjectStoreCatchHandler(BlockContainer catchBlockContainer,
ILVariable exceptionVariable, out ILVariable objectVariable, out Block branchTarget)
{
objectVariable = null;
branchTarget = null;
var entry = catchBlockContainer.EntryPoint;
ILInstruction value;
switch (entry.Instructions.Count)
{
case 2:
if (!entry.Instructions[0].MatchStLoc(out objectVariable, out value))
return false;
if (!value.MatchLdLoc(exceptionVariable))
return false;
break;
case 3:
if (!entry.Instructions[0].MatchStLoc(out var temporaryVariable, out value))
return false;
if (!value.MatchLdLoc(exceptionVariable))
return false;
if (!entry.Instructions[1].MatchStLoc(out objectVariable, out value))
return false;
if (!value.MatchLdLoc(temporaryVariable))
return false;
break;
default:
return false;
}
return entry.Instructions[entry.Instructions.Count - 1].MatchBranch(out branchTarget);
}
/// <summary>
/// Move <paramref name="newEntryPoint"/> and every block it dominates (excluding any block
/// dominated by <paramref name="endBlock"/> other than <paramref name="endBlock"/> itself)
/// from their current container into <paramref name="targetContainer"/>.
/// </summary>
internal static void MoveDominatedBlocksToContainer(Block newEntryPoint, Block endBlock,
ControlFlowGraph graph, BlockContainer targetContainer, ILTransformContext context)
{
var node = graph.GetNode(newEntryPoint);
var endNode = endBlock == null ? null : graph.GetNode(endBlock);
MoveBlock(newEntryPoint, targetContainer, context);
foreach (var n in graph.cfg)
{
Block block = (Block)n.UserData;
if (node.Dominates(n))
{
if (endNode != null && endNode != n && endNode.Dominates(n))
continue;
if (block.Parent == targetContainer)
continue;
MoveBlock(block, targetContainer, context);
}
}
}
static void MoveBlock(Block block, BlockContainer target, ILTransformContext context)
{
context.Step($"Move {block.Label} to container at IL_{target.StartILOffset:x4}", target);
block.Remove();
target.Blocks.Add(block);
}
static (Block, Block, ILVariable) FindBlockAfterFinally(ILTransformContext context, Block block, ILVariable objectVariable)
{
// Block IL_0327 (incoming: 2) {

1416
ICSharpCode.Decompiler/IL/ControlFlow/RuntimeAsyncExceptionRewriteTransform.cs

File diff suppressed because it is too large Load Diff

177
ICSharpCode.Decompiler/IL/ControlFlow/RuntimeAsyncManualAwaitTransform.cs

@ -0,0 +1,177 @@ @@ -0,0 +1,177 @@
// Copyright (c) 2026 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 System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
namespace ICSharpCode.Decompiler.IL.ControlFlow
{
/// <summary>
/// Collapses the runtime-async manual await pattern (GetAwaiter / get_IsCompleted /
/// AsyncHelpers.UnsafeAwaitAwaiter / GetResult across three blocks) into a single IL
/// Await instruction, matching what the state-machine async pipeline emits.
/// </summary>
static class RuntimeAsyncManualAwaitTransform
{
public static void Run(ILFunction function, ILTransformContext context)
{
bool changed = false;
foreach (var block in function.Descendants.OfType<Block>().ToArray())
{
if (DetectRuntimeAsyncManualAwait(block, context))
changed = true;
}
if (changed)
{
foreach (var c in function.Body.Descendants.OfType<BlockContainer>())
c.SortBlocks(deleteUnreachableBlocks: true);
}
}
// Block X (head) {
// [stloc awaitable(<value>);] (optional: when GetAwaiter takes an address)
// stloc awaiter(call GetAwaiter(<expr>))
// if (call get_IsCompleted(ldloca awaiter)) br completedBlock
// br pauseBlock
// }
// Block pauseBlock {
// call AsyncHelpers.UnsafeAwaitAwaiter[<T>](ldloc awaiter)
// br completedBlock
// }
// Block completedBlock {
// call GetResult(ldloca awaiter) (possibly inlined into a containing expression)
// ...
// }
// =>
// Block X { ...; br completedBlock }
// Block completedBlock { <Await(value)>; ... }
static bool DetectRuntimeAsyncManualAwait(Block block, ILTransformContext context)
{
if (block.Instructions.Count < 3)
return false;
if (!block.Instructions[^3].MatchStLoc(out var awaiterVar, out var awaiterValue))
return false;
if (awaiterValue is not CallInstruction getAwaiterCall)
return false;
if (getAwaiterCall.Method.Name != "GetAwaiter" || getAwaiterCall.Arguments.Count != 1)
return false;
if (block.Instructions[^2] is not IfInstruction ifInst)
return false;
var condition = ifInst.Condition;
if (condition.MatchLogicNot(out var negated))
condition = negated;
if (condition is not CallInstruction isCompletedCall)
return false;
if (isCompletedCall.Method.Name != "get_IsCompleted" || isCompletedCall.Arguments.Count != 1)
return false;
if (!isCompletedCall.Arguments[0].MatchLdLoca(awaiterVar))
return false;
Block completedBlock, pauseBlock;
if (ifInst.TrueInst.MatchBranch(out var trueBranchTarget) && block.Instructions[^1].MatchBranch(out var falseBranchTarget))
{
if (negated != null)
{
// if (!IsCompleted) br pauseBlock; br completedBlock — flipped
pauseBlock = trueBranchTarget;
completedBlock = falseBranchTarget;
}
else
{
// if (IsCompleted) br completedBlock; br pauseBlock — canonical
completedBlock = trueBranchTarget;
pauseBlock = falseBranchTarget;
}
}
else
{
return false;
}
// Block pauseBlock { call AsyncHelpers.UnsafeAwaitAwaiter(ldloc awaiter); br completedBlock }
if (pauseBlock.Instructions.Count != 2)
return false;
if (pauseBlock.Instructions[0] is not Call pauseCall)
return false;
if (!EarlyExpressionTransforms.IsAsyncHelpersMethod(pauseCall.Method, "UnsafeAwaitAwaiter") || pauseCall.Arguments.Count != 1)
return false;
if (!pauseCall.Arguments[0].MatchLdLoc(awaiterVar))
return false;
if (!pauseBlock.Instructions[1].MatchBranch(out var pauseTail) || pauseTail != completedBlock)
return false;
// completedBlock starts with: GetResult(ldloca awaiter), possibly inlined.
if (completedBlock.Instructions.Count < 1)
return false;
var getResultCall = ILInlining.FindFirstInlinedCall(completedBlock.Instructions[0]);
if (getResultCall == null)
return false;
if (getResultCall.Method.Name != "GetResult" || getResultCall.Arguments.Count != 1)
return false;
if (!getResultCall.Arguments[0].MatchLdLoca(awaiterVar))
return false;
// Determine the awaitable expression: the original GetAwaiter argument (often `ldloca tmp`)
// or, if `tmp` is a fresh temporary set immediately above, the value it was set to.
ILInstruction awaitable;
bool removeTemporaryStore = false;
if (getAwaiterCall.Arguments[0].MatchLdLoca(out var tmpVar)
&& block.Instructions.Count >= 4
&& block.Instructions[^4].MatchStLoc(tmpVar, out var tmpValue)
&& tmpVar.StoreCount == 1
&& tmpVar.LoadCount == 0
&& tmpVar.AddressCount == 1)
{
awaitable = tmpValue;
removeTemporaryStore = true;
}
else
{
awaitable = getAwaiterCall.Arguments[0];
}
context.Step("Reduce manual await pattern to Await", block);
// Capture IL ranges before we remove the suspend machinery. The new Await stands in
// for the whole pattern (temp store + GetAwaiter + IsCompleted check + UnsafeAwaitAwaiter
// + GetResult), and the new `br completedBlock` replaces the original `br pauseBlock`.
var oldTailBranch = block.Instructions[^1];
var awaitInst = new Await(awaitable.Clone()).WithILRange(getResultCall);
if (removeTemporaryStore)
awaitInst.AddILRange(block.Instructions[^4]);
awaitInst.AddILRange(block.Instructions[^3]);
awaitInst.AddILRange(block.Instructions[^2]);
foreach (var inst in pauseBlock.Instructions)
awaitInst.AddILRange(inst);
awaitInst.GetAwaiterMethod = getAwaiterCall.Method;
awaitInst.GetResultMethod = getResultCall.Method;
// Remove the trailing 3 (or 4) instructions of the head block; replace with `br completedBlock`.
int removeCount = removeTemporaryStore ? 4 : 3;
block.Instructions.RemoveRange(block.Instructions.Count - removeCount, removeCount);
block.Instructions.Add(new Branch(completedBlock).WithILRange(oldTailBranch));
getResultCall.ReplaceWith(awaitInst);
return true;
}
}
}

16
ICSharpCode.Decompiler/IL/ILReader.cs

@ -159,6 +159,7 @@ namespace ICSharpCode.Decompiler.IL @@ -159,6 +159,7 @@ namespace ICSharpCode.Decompiler.IL
BitSet isBranchTarget = null!;
BlockContainer mainContainer = null!;
int currentInstructionStart;
bool isRuntimeAsync;
Dictionary<int, ImportedBlock> blocksByOffset = new Dictionary<int, ImportedBlock>();
Queue<ImportedBlock> importQueue = new Queue<ImportedBlock>();
@ -172,6 +173,8 @@ namespace ICSharpCode.Decompiler.IL @@ -172,6 +173,8 @@ namespace ICSharpCode.Decompiler.IL
if (methodDefinitionHandle.IsNil)
throw new ArgumentException("methodDefinitionHandle.IsNil");
this.method = module.GetDefinition(methodDefinitionHandle);
this.isRuntimeAsync = (module.TypeSystemOptions & TypeSystemOptions.RuntimeAsync) != 0
&& (module.metadata.GetMethodDefinition(methodDefinitionHandle).ImplAttributes & SRMExtensions.MethodImplAsync) != 0;
if (genericContext.ClassTypeParameters == null && genericContext.MethodTypeParameters == null)
{
// no generic context specified: use the method's own type parameters
@ -187,7 +190,14 @@ namespace ICSharpCode.Decompiler.IL @@ -187,7 +190,14 @@ namespace ICSharpCode.Decompiler.IL
this.reader = body.GetILReader();
this.currentStack = ImmutableStack<ILVariable>.Empty;
this.expressionStack.Clear();
this.methodReturnStackType = method.ReturnType.GetStackType();
if (isRuntimeAsync)
{
this.methodReturnStackType = TaskType.UnpackAnyTask(compilation, method.ReturnType).GetStackType();
}
else
{
this.methodReturnStackType = method.ReturnType.GetStackType();
}
InitParameterVariables();
localVariables = InitLocalVariables();
foreach (var v in localVariables)
@ -715,6 +725,10 @@ namespace ICSharpCode.Decompiler.IL @@ -715,6 +725,10 @@ namespace ICSharpCode.Decompiler.IL
var blockBuilder = new BlockBuilder(body, variableByExceptionHandler, compilation);
blockBuilder.CreateBlocks(mainContainer, blocksByOffset.Values.Select(ib => ib.Block), cancellationToken);
var function = new ILFunction(this.method, body.GetCodeSize(), this.genericContext, mainContainer, kind);
if (isRuntimeAsync)
{
function.AsyncReturnType = TaskType.UnpackAnyTask(compilation, this.method.ReturnType);
}
function.Variables.AddRange(parameterVariables);
function.Variables.AddRange(localVariables);
function.LocalVariableSignatureLength = localVariables.Length;

2
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -215,7 +215,7 @@ namespace ICSharpCode.Decompiler.IL @@ -215,7 +215,7 @@ namespace ICSharpCode.Decompiler.IL
return false; // operator not supported on pointer types
}
}
else if ((type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) && type.Kind is not TypeKind.NInt or TypeKind.NUInt)
else if ((type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) && type.Kind is not (TypeKind.NInt or TypeKind.NUInt))
{
// If the LHS is C# 9 IntPtr (but not nint or C# 11 IntPtr):
// "target.intptr *= 2;" is compiler error, but

33
ICSharpCode.Decompiler/IL/Transforms/EarlyExpressionTransforms.cs

@ -173,6 +173,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -173,6 +173,39 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
protected internal override void VisitCall(Call inst)
{
base.VisitCall(inst);
TransformAsyncHelpersAwaitToAwait(inst, context);
}
// runtime-async lowering emits `call AsyncHelpers.Await(value)` in place of the IL Await
// instruction. Convert it back so downstream transforms (UsingTransform's MatchDisposeBlock
// and friends pattern-match on Await via UnwrapAwait) see the canonical shape that the
// state-machine async pipeline also produces. Gate on the current function actually being
// a runtime-async method — ILReader sets IsAsync iff the MethodImpl.Async bit is present —
// so a non-async method that calls the helper directly (legal, just unusual) doesn't get
// rewritten into IL that the surrounding non-async control flow can't represent.
internal static bool TransformAsyncHelpersAwaitToAwait(Call inst, ILTransformContext context)
{
if (!context.Settings.RuntimeAsync || !context.Function.IsAsync)
return false;
if (!inst.Method.IsStatic || inst.Arguments.Count != 1 || !IsAsyncHelpersMethod(inst.Method, "Await"))
return false;
context.Step("call AsyncHelpers.Await(value) => await(value)", inst);
var awaitInst = new Await(inst.Arguments[0]).WithILRange(inst);
awaitInst.GetAwaiterMethod = null;
awaitInst.GetResultMethod = inst.Method;
inst.ReplaceWith(awaitInst);
return true;
}
internal static bool IsAsyncHelpersMethod(IMethod method, string name)
{
return method.Name == name
&& method.DeclaringType?.FullName == "System.Runtime.CompilerServices.AsyncHelpers";
}
protected internal override void VisitNewObj(NewObj inst)
{
if (TransformDecimalCtorToConstant(inst, out LdcDecimal decimalConstant))

15
ICSharpCode.Decompiler/Metadata/DotNetCorePathFinder.cs

@ -318,7 +318,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -318,7 +318,7 @@ namespace ICSharpCode.Decompiler.Metadata
fixed (byte* input = bytes)
{
byte* output = GetRealPath(input, null);
byte* output = NativeMethods.GetRealPath(input, null);
if (output == null)
{
return null;
@ -330,15 +330,18 @@ namespace ICSharpCode.Decompiler.Metadata @@ -330,15 +330,18 @@ namespace ICSharpCode.Decompiler.Metadata
}
byte[] result = new byte[len];
Marshal.Copy((IntPtr)output, result, 0, result.Length);
Free(output);
NativeMethods.Free(output);
return encoding.GetString(result);
}
}
[DllImport("libc", EntryPoint = "realpath")]
static extern unsafe byte* GetRealPath(byte* path, byte* resolvedPath);
static class NativeMethods
{
[DllImport("libc", EntryPoint = "realpath")]
internal static extern unsafe byte* GetRealPath(byte* path, byte* resolvedPath);
[DllImport("libc", EntryPoint = "free")]
static extern unsafe void Free(void* ptr);
[DllImport("libc", EntryPoint = "free")]
internal static extern unsafe void Free(void* ptr);
}
}
}

15
ICSharpCode.Decompiler/Metadata/MetadataFile.cs

@ -22,6 +22,7 @@ using System; @@ -22,6 +22,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
@ -45,7 +46,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -45,7 +46,7 @@ namespace ICSharpCode.Decompiler.Metadata
/// decompiled type systems.
/// </remarks>
[DebuggerDisplay("{Kind}: {FileName}")]
public class MetadataFile
public class MetadataFile : IDisposable
{
public enum MetadataFileKind
{
@ -285,6 +286,8 @@ namespace ICSharpCode.Decompiler.Metadata @@ -285,6 +286,8 @@ namespace ICSharpCode.Decompiler.Metadata
throw new BadImageFormatException("This metadata file does not support sections.");
}
[SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations",
Justification = "Throw signals that this MetadataFileKind has no PE sections; derived PE-like kinds override.")]
public virtual ImmutableArray<SectionHeader> SectionHeaders => throw new BadImageFormatException("This metadata file does not support sections.");
/// <summary>
@ -297,6 +300,16 @@ namespace ICSharpCode.Decompiler.Metadata @@ -297,6 +300,16 @@ namespace ICSharpCode.Decompiler.Metadata
return new MetadataFileWithOptions(this, options);
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
}
private class MetadataFileWithOptions : IModuleReference
{
readonly MetadataFile peFile;

8
ICSharpCode.Decompiler/Metadata/PEFile.cs

@ -31,7 +31,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -31,7 +31,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Metadata
{
[DebuggerDisplay("{FileName}")]
public class PEFile : MetadataFile, IDisposable, IModuleReference
public sealed class PEFile : MetadataFile, IModuleReference
{
public PEReader Reader { get; }
@ -55,9 +55,11 @@ namespace ICSharpCode.Decompiler.Metadata @@ -55,9 +55,11 @@ namespace ICSharpCode.Decompiler.Metadata
public override int MetadataOffset => Reader.PEHeaders.MetadataStartOffset;
public override bool IsMetadataOnly => false;
public void Dispose()
protected override void Dispose(bool disposing)
{
Reader.Dispose();
if (disposing)
Reader.Dispose();
base.Dispose(disposing);
}
IModule TypeSystem.IModuleReference.Resolve(ITypeResolveContext context)

8
ICSharpCode.Decompiler/Metadata/WebCilFile.cs

@ -32,7 +32,7 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -32,7 +32,7 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.Metadata
{
public class WebCilFile : MetadataFile, IDisposable, IModuleReference
public sealed class WebCilFile : MetadataFile, IModuleReference
{
readonly MemoryMappedViewAccessor view;
readonly long webcilOffset;
@ -245,9 +245,11 @@ namespace ICSharpCode.Decompiler.Metadata @@ -245,9 +245,11 @@ namespace ICSharpCode.Decompiler.Metadata
return new MetadataModule(context.Compilation, this, TypeSystemOptions.Default);
}
public void Dispose()
protected override void Dispose(bool disposing)
{
view.Dispose();
if (disposing)
view.Dispose();
base.Dispose(disposing);
}
public struct WebcilHeader

11
ICSharpCode.Decompiler/Output/PlainTextOutput.cs

@ -28,9 +28,10 @@ using ICSharpCode.Decompiler.TypeSystem; @@ -28,9 +28,10 @@ using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler
{
public sealed class PlainTextOutput : ITextOutput
public sealed class PlainTextOutput : ITextOutput, IDisposable
{
readonly TextWriter writer;
readonly bool ownsWriter;
int indent;
bool needsIndent;
@ -44,11 +45,19 @@ namespace ICSharpCode.Decompiler @@ -44,11 +45,19 @@ namespace ICSharpCode.Decompiler
if (writer == null)
throw new ArgumentNullException(nameof(writer));
this.writer = writer;
this.ownsWriter = false;
}
public PlainTextOutput()
{
this.writer = new StringWriter();
this.ownsWriter = true;
}
public void Dispose()
{
if (ownsWriter)
writer.Dispose();
}
public TextLocation Location {

4
ICSharpCode.Decompiler/Properties/DecompilerVersionInfo.template.cs

@ -1,8 +1,8 @@ @@ -1,8 +1,8 @@
public static class DecompilerVersionInfo
{
public const string Major = "10";
public const string Minor = "0";
public const string Build = "1";
public const string Minor = "1";
public const string Build = "0";
public const string Revision = "$INSERTREVISION$";
public const string VersionName = null;

1
ICSharpCode.Decompiler/SRMHacks.cs

@ -15,6 +15,7 @@ namespace ICSharpCode.Decompiler @@ -15,6 +15,7 @@ namespace ICSharpCode.Decompiler
public static partial class SRMExtensions
{
internal const GenericParameterAttributes AllowByRefLike = (GenericParameterAttributes)0x0020;
internal const MethodImplAttributes MethodImplAsync = (MethodImplAttributes)0x2000;
public static ImmutableArray<MethodImplementationHandle> GetMethodImplementations(
this MethodDefinitionHandle handle, MetadataReader reader)

7
ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs

@ -154,12 +154,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -154,12 +154,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// </summary>
ExtensionMembers = 0x80000,
/// <summary>
/// If this option is active, methods with the MethodImplAttribute(MethodImplOptions.Async) are treated as async methods.
/// </summary>
RuntimeAsync = 0x100000,
/// <summary>
/// Default settings: typical options for the decompiler, with all C# language features enabled.
/// </summary>
Default = Dynamic | Tuple | ExtensionMethods | DecimalConstants | ReadOnlyStructsAndParameters
| RefStructs | UnmanagedConstraints | NullabilityAnnotations | ReadOnlyMethods
| NativeIntegers | FunctionPointers | ScopedRef | NativeIntegersWithoutAttribute
| RefReadOnlyParameters | ParamsCollections | FirstClassSpanTypes | ExtensionMembers
| RuntimeAsync
}
/// <summary>
@ -207,6 +212,8 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -207,6 +212,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
typeSystemOptions |= TypeSystemOptions.FirstClassSpanTypes;
if (settings.ExtensionMembers)
typeSystemOptions |= TypeSystemOptions.ExtensionMembers;
if (settings.RuntimeAsync)
typeSystemOptions |= TypeSystemOptions.RuntimeAsync;
return typeSystemOptions;
}

3
ICSharpCode.Decompiler/TypeSystem/Implementation/CustomAttribute.cs

@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -40,6 +40,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
CustomAttributeValue<IType> value;
bool valueDecoded;
bool hasDecodeErrors;
readonly object syncRoot = new object();
internal CustomAttribute(MetadataModule module, IMethod attrCtor, CustomAttributeHandle handle)
{
@ -76,7 +77,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -76,7 +77,7 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
void DecodeValue()
{
lock (this)
lock (syncRoot)
{
try
{

4
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs

@ -350,6 +350,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation @@ -350,6 +350,10 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var metadata = module.metadata;
var def = metadata.GetMethodDefinition(handle);
MethodImplAttributes implAttributes = def.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
if ((module.TypeSystemOptions & TypeSystemOptions.RuntimeAsync) != 0)
{
implAttributes &= ~SRMExtensions.MethodImplAsync;
}
int methodCodeType = (int)(def.ImplAttributes & MethodImplAttributes.CodeTypeMask);
#region DllImportAttribute

22
ICSharpCode.Decompiler/TypeSystem/TaskType.cs

@ -40,6 +40,28 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -40,6 +40,28 @@ namespace ICSharpCode.Decompiler.TypeSystem
return type.TypeArguments[0];
}
/// <summary>
/// Gets the element type of any task-like type (Task, Task&lt;T&gt;, ValueTask,
/// ValueTask&lt;T&gt;, or any [AsyncMethodBuilder]-attributed custom task type).
/// Returns void for non-generic task-likes. Any other type is returned unmodified.
/// </summary>
public static IType UnpackAnyTask(ICompilation compilation, IType type)
{
if (IsTask(type))
{
return type.TypeParameterCount == 0
? compilation.FindType(KnownTypeCode.Void)
: type.TypeArguments[0];
}
if (IsCustomTask(type, out _))
{
return type.TypeParameterCount == 0
? compilation.FindType(KnownTypeCode.Void)
: type.TypeArguments[0];
}
return type;
}
/// <summary>
/// Gets whether the specified type is Task or Task&lt;T&gt;.
/// </summary>

3
ICSharpCode.Decompiler/Util/EmptyList.cs

@ -20,6 +20,7 @@ @@ -20,6 +20,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace ICSharpCode.Decompiler.Util
{
@ -99,6 +100,8 @@ namespace ICSharpCode.Decompiler.Util @@ -99,6 +100,8 @@ namespace ICSharpCode.Decompiler.Util
get { throw new NotSupportedException(); }
}
[SuppressMessage("Usage", "CA1063:Implement IDisposable Correctly",
Justification = "Explicit IDisposable implementation for IEnumerator<T>; intentional no-op for the singleton.")]
void IDisposable.Dispose()
{
}

5
ICSharpCode.Decompiler/Util/LongSet.cs

@ -21,6 +21,7 @@ using System; @@ -21,6 +21,7 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace ICSharpCode.Decompiler.Util
@ -28,6 +29,8 @@ namespace ICSharpCode.Decompiler.Util @@ -28,6 +29,8 @@ namespace ICSharpCode.Decompiler.Util
/// <summary>
/// An immutable set of longs, that is implemented as a list of intervals.
/// </summary>
[SuppressMessage("Usage", "CA2231:Overload operator equals on overriding value type Equals",
Justification = "Equality on LongSet is intentionally only available via SetEquals — the IEquatable<LongSet>.Equals overload is itself [Obsolete] in favor of SetEquals.")]
public struct LongSet : IEquatable<LongSet>
{
/// <summary>
@ -362,6 +365,8 @@ namespace ICSharpCode.Decompiler.Util @@ -362,6 +365,8 @@ namespace ICSharpCode.Decompiler.Util
return obj is LongSet && SetEquals((LongSet)obj);
}
[SuppressMessage("Design", "CA1065:Do not raise exceptions in unexpected locations",
Justification = "Throw is an explicit guard against using LongSet in hash-based containers; use SetEquals for comparison.")]
public override int GetHashCode()
{
throw new NotImplementedException();

5
ICSharpCode.Decompiler/Util/ResXResourceWriter.cs

@ -31,6 +31,7 @@ @@ -31,6 +31,7 @@
// includes code by Mike Krüger and Lluis Sanchez
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text;
@ -40,6 +41,10 @@ using ICSharpCode.Decompiler.Metadata; @@ -40,6 +41,10 @@ using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.Decompiler.Util
{
[SuppressMessage("Usage", "CA1063:Implement IDisposable Correctly",
Justification = "Ported from the Mono ResXResourceWriter implementation; preserved verbatim for fidelity with the upstream.")]
[SuppressMessage("Usage", "CA2213:Disposable fields should be disposed",
Justification = "By design: the writer doesn't own the underlying stream/textwriter passed in by the caller. Mono behavior preserved.")]
#if INSIDE_SYSTEM_WEB
internal
#else

2
ICSharpCode.Decompiler/Util/ResourcesFile.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.Util @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.Util
/// <summary>
/// .resources file.
/// </summary>
public class ResourcesFile : IEnumerable<KeyValuePair<string, object?>>, IDisposable
public sealed class ResourcesFile : IEnumerable<KeyValuePair<string, object?>>, IDisposable
{
sealed class MyBinaryReader : BinaryReader
{

32
ICSharpCode.Decompiler/packages.lock.json

@ -4,9 +4,9 @@ @@ -4,9 +4,9 @@
".NETStandard,Version=v2.0": {
"Microsoft.CodeAnalysis.NetAnalyzers": {
"type": "Direct",
"requested": "[10.0.202, )",
"resolved": "10.0.202",
"contentHash": "ZC4WsiOnzyUHIZ3uGN4popaYPh2NgYcXZ/CpMpAb3mVF/HvBGlBtlredzZMKgcHqlm+ygqJh6qfhUSc/OJ0hWQ=="
"requested": "[10.0.300, )",
"resolved": "10.0.300",
"contentHash": "WsPMVkfrmoZ0qKSasuVtYaxnd/MvqPOz7ZXld5NzyF8/kwwtPGjtmEKp4xDHG+pj4QqETpSVfkmKa0UFu/n1GQ=="
},
"Microsoft.Sbom.Targets": {
"type": "Direct",
@ -16,13 +16,13 @@ @@ -16,13 +16,13 @@
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[10.0.202, )",
"resolved": "10.0.202",
"contentHash": "vRm3w0AWndnqt8X73qoWTdD/qp7o+bYMCufl7hSiQUSqeI3WKdqA/ElE6MCUscy84FX8HCMcK2EJCtTm+4bdbw==",
"requested": "[10.0.300, )",
"resolved": "10.0.300",
"contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "10.0.202",
"Microsoft.SourceLink.Common": "10.0.202",
"System.IO.Hashing": "10.0.6"
"Microsoft.Build.Tasks.Git": "10.0.300",
"Microsoft.SourceLink.Common": "10.0.300",
"System.IO.Hashing": "10.0.8"
}
},
"NETStandard.Library": {
@ -62,10 +62,10 @@ @@ -62,10 +62,10 @@
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "10.0.202",
"contentHash": "HXAW7dToPLi6lHN4TJz1FfxLYfPS3EF20YrrZxQ9xbwbjLkFXJ+SwkuuILoAdCWTNHhHfa5oqOsrOEQX5s+eMg==",
"resolved": "10.0.300",
"contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==",
"dependencies": {
"System.IO.Hashing": "10.0.6"
"System.IO.Hashing": "10.0.8"
}
},
"Microsoft.NETCore.Platforms": {
@ -75,8 +75,8 @@ @@ -75,8 +75,8 @@
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "10.0.202",
"contentHash": "g8E67huS0Slt6iJiUG1uj2N4NBo/q68Q1y1H4nFu1Cn4AOp6S5D8AeGLhGMzBT+JmNCjiGTvKco2qkbe4F31tQ=="
"resolved": "10.0.300",
"contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ=="
},
"System.Buffers": {
"type": "Transitive",
@ -85,8 +85,8 @@ @@ -85,8 +85,8 @@
},
"System.IO.Hashing": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "lbMLkqFekDR4DeFd26eEfrG2HlvixIfs22uk/e2+9/NJ7WxMycVVakcQpuJvvqgc9XxwEgSd/Td+dZA+TjDDwA==",
"resolved": "10.0.8",
"contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg==",
"dependencies": {
"System.Buffers": "4.6.1",
"System.Memory": "4.6.3"

95
ICSharpCode.ILSpyCmd/BamlAwareWholeProjectDecompiler.cs

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
//
// 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 System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using ICSharpCode.BamlDecompiler;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.ILSpyCmd
{
sealed class BamlAwareWholeProjectDecompiler : WholeProjectDecompiler
{
readonly BamlDecompilerTypeSystem bamlTypeSystem;
readonly BamlDecompilerSettings bamlSettings;
public BamlAwareWholeProjectDecompiler(
DecompilerSettings settings,
IAssemblyResolver assemblyResolver,
AssemblyReferenceClassifier assemblyReferenceClassifier,
IDebugInfoProvider debugInfoProvider,
BamlDecompilerTypeSystem bamlTypeSystem,
BamlDecompilerSettings bamlSettings)
: base(settings, assemblyResolver, projectWriter: null, assemblyReferenceClassifier, debugInfoProvider)
{
this.bamlTypeSystem = bamlTypeSystem ?? throw new ArgumentNullException(nameof(bamlTypeSystem));
this.bamlSettings = bamlSettings ?? throw new ArgumentNullException(nameof(bamlSettings));
}
public CancellationToken CancellationToken { get; set; }
protected override IEnumerable<ProjectItemInfo> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
{
if (!fileName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase))
return base.WriteResourceToFile(fileName, resourceName, entryStream);
var decompiler = new XamlDecompiler(bamlTypeSystem, bamlSettings) {
CancellationToken = CancellationToken
};
var result = decompiler.Decompile(entryStream);
string xamlFileName;
PartialTypeInfo partialTypeInfo = null;
var typeDefinition = result.TypeName.HasValue
? bamlTypeSystem.MainModule.GetTypeDefinition(result.TypeName.Value.TopLevelTypeName)
: null;
if (typeDefinition != null)
{
xamlFileName = SanitizeFileName(typeDefinition.ReflectionName + ".xaml");
partialTypeInfo = new PartialTypeInfo(typeDefinition);
foreach (var member in result.GeneratedMembers)
partialTypeInfo.AddDeclaredMember(member);
}
else
{
xamlFileName = Path.ChangeExtension(fileName, ".xaml");
}
string fullPath = Path.Combine(TargetDirectory, xamlFileName);
string directory = Path.GetDirectoryName(fullPath);
if (!string.IsNullOrEmpty(directory))
Directory.CreateDirectory(directory);
result.Xaml.Save(fullPath);
var item = new ProjectItemInfo("Page", xamlFileName)
.With("Generator", "MSBuild:Compile")
.With("SubType", "Designer");
if (partialTypeInfo != null)
item.PartialTypes = new List<PartialTypeInfo> { partialTypeInfo };
return new[] { item };
}
}
}

1
ICSharpCode.ILSpyCmd/ICSharpCode.ILSpyCmd.csproj

@ -56,6 +56,7 @@ @@ -56,6 +56,7 @@
<ItemGroup>
<ProjectReference Include="..\ICSharpCode.ILSpyX\ICSharpCode.ILSpyX.csproj" />
<ProjectReference Include="..\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj" />
<ProjectReference Include="..\ICSharpCode.BamlDecompiler\ICSharpCode.BamlDecompiler.csproj" />
</ItemGroup>
<ItemGroup>

136
ICSharpCode.ILSpyCmd/IlspyCmdProgram.cs

@ -10,6 +10,7 @@ using System.Reflection.PortableExecutable; @@ -10,6 +10,7 @@ using System.Reflection.PortableExecutable;
using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.BamlDecompiler;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler;
@ -52,6 +53,15 @@ Examples: @@ -52,6 +53,15 @@ Examples:
Generate a HTML diagrammer containing filtered type info into a custom output folder
(including types in the LightJson namespace while excluding types in nested LightJson.Serialization namespace)
ilspycmd sample.dll --generate-diagrammer -o c:\diagrammer --generate-diagrammer-include LightJson\\..+ --generate-diagrammer-exclude LightJson\\.Serialization\\..+
List all embedded resources in a WPF assembly (including BAML entries inside .g.resources containers).
ilspycmd sample.dll --list-resources
Extract a single resource. If the name ends with .baml, the output is decompiled XAML; otherwise raw bytes.
ilspycmd sample.dll --resource sample.g.resources/mainwindow.baml -o c:\decompiled
Decompile assembly as a compilable project and convert all BAML resources to XAML Page items.
ilspycmd sample.dll -p -o c:\decompiled --decompile-baml
")]
[HelpOption("-h|--help")]
[ProjectOptionRequiresOutputDirectoryValidation]
@ -93,6 +103,15 @@ Examples: @@ -93,6 +103,15 @@ Examples:
[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)]
public string[] EntityTypes { get; } = Array.Empty<string>();
[Option("--list-resources", "Lists all embedded resources in the assembly. Entries inside .resources containers are listed individually as '<container>/<entry>'.", CommandOptionType.NoValue)]
public bool ListResourcesFlag { get; }
[Option("--resource <name>", "Extract a single resource by name (as printed by --list-resources). Resources whose name ends with '.baml' are decompiled to XAML.", CommandOptionType.SingleValue)]
public string ResourceName { get; }
[Option("--decompile-baml", "When used with -p, decompile BAML resources to XAML files (Page items) instead of leaving them as raw byte streams.", CommandOptionType.NoValue)]
public bool DecompileBamlFlag { get; }
public string DecompilerVersion => "ilspycmd: " + typeof(ILSpyCmdProgram).Assembly.GetName().Version.ToString() +
Environment.NewLine
+ "ICSharpCode.Decompiler: " +
@ -152,8 +171,8 @@ Examples: @@ -152,8 +171,8 @@ Examples:
public bool ReportExcludedTypes { get; set; }
[Option(generateDiagrammerCmd + "-docs", "The path or file:// URI of the XML file containing the target assembly's documentation comments." +
" You only need to set this if a) you want your diagrams annotated with them and b) the file name differs from that of the assmbly." +
" To enable XML documentation output for your assmbly, see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/#create-xml-documentation-output",
" You only need to set this if a) you want your diagrams annotated with them and b) the file name differs from that of the assembly." +
" To enable XML documentation output for your assembly, see https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/#create-xml-documentation-output",
CommandOptionType.SingleValue)]
public string XmlDocs { get; set; }
@ -257,8 +276,8 @@ Examples: @@ -257,8 +276,8 @@ Examples:
var checkResult = await updateCheckTask;
if (null != checkResult && checkResult.UpdateRecommendation)
{
Console.WriteLine("You are not using the latest version of the tool, please update.");
Console.WriteLine($"Latest version is '{checkResult.LatestVersion}' (yours is '{checkResult.RunningVersion}')");
app.Error.WriteLine("You are not using the latest version of the tool, please update.");
app.Error.WriteLine($"Latest version is '{checkResult.LatestVersion}' (yours is '{checkResult.RunningVersion}')");
}
}
}
@ -306,6 +325,20 @@ Examples: @@ -306,6 +325,20 @@ Examples:
{
return DumpPackageAssemblies(fileName, outputDirectory, app);
}
else if (ListResourcesFlag)
{
if (outputDirectory != null)
{
string outputName = Path.GetFileNameWithoutExtension(fileName);
output = File.CreateText(Path.Combine(outputDirectory, outputName) + ".resources.txt");
}
return ListResources(fileName, output);
}
else if (ResourceName != null)
{
return ExtractResource(fileName, ResourceName, output, outputDirectory, app);
}
else
{
if (outputDirectory != null)
@ -430,6 +463,85 @@ Examples: @@ -430,6 +463,85 @@ Examples:
return 0;
}
int ListResources(string assemblyFileName, TextWriter output)
{
var module = new PEFile(assemblyFileName);
foreach (var path in ResourceExtensions.EnumerateResourcePaths(module))
{
output.WriteLine(path);
}
return 0;
}
int ExtractResource(string assemblyFileName, string resourceName, TextWriter output, string outputDirectory, CommandLineApplication app)
{
var module = new PEFile(assemblyFileName);
if (!ResourceExtensions.TryGetResource(module, resourceName, out object value))
{
app.Error.WriteLine($"Resource '{resourceName}' not found.");
app.Error.WriteLine("Available resources:");
foreach (var p in ResourceExtensions.EnumerateResourcePaths(module))
app.Error.WriteLine(" " + p);
return ProgramExitCodes.EX_DATAERR;
}
bool isBaml = resourceName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase);
if (isBaml && value is byte[] bamlBytes)
{
var resolver = new UniversalAssemblyResolver(assemblyFileName, false, module.Metadata.DetectTargetFrameworkId());
foreach (var path in (ReferencePaths ?? Array.Empty<string>()))
resolver.AddSearchDirectory(path);
var bamlSettings = new BamlDecompilerSettings {
ThrowOnAssemblyResolveErrors = GetSettings(module).ThrowOnAssemblyResolveErrors
};
using var bamlStream = new MemoryStream(bamlBytes);
var xaml = ResourceExtensions.DecompileBaml(module, resolver, bamlStream, bamlSettings, CancellationToken.None);
if (outputDirectory != null)
{
string xamlFile = WholeProjectDecompiler.SanitizeFileName(Path.GetFileNameWithoutExtension(resourceName) + ".xaml");
string fullPath = Path.Combine(outputDirectory, xamlFile);
xaml.Save(fullPath);
}
else
{
output.Write(xaml.ToString());
}
return 0;
}
if (value is byte[] binary)
{
if (outputDirectory != null)
{
string fileName = WholeProjectDecompiler.SanitizeFileName(Path.GetFileName(resourceName));
string fullPath = Path.Combine(outputDirectory, fileName);
File.WriteAllBytes(fullPath, binary);
}
else
{
var stdout = Console.OpenStandardOutput();
stdout.Write(binary, 0, binary.Length);
stdout.Flush();
}
}
else
{
string text = value as string ?? value?.ToString() ?? string.Empty;
if (outputDirectory != null)
{
string fileName = WholeProjectDecompiler.SanitizeFileName(Path.GetFileName(resourceName));
string fullPath = Path.Combine(outputDirectory, fileName);
File.WriteAllText(fullPath, text);
}
else
{
output.Write(text);
}
}
return 0;
}
int ShowIL(string assemblyFileName, TextWriter output)
{
var module = new PEFile(assemblyFileName);
@ -450,7 +562,21 @@ Examples: @@ -450,7 +562,21 @@ Examples:
{
resolver.AddSearchDirectory(path);
}
var decompiler = new WholeProjectDecompiler(GetSettings(module), resolver, null, resolver, TryLoadPDB(module));
var settings = GetSettings(module);
var debugInfo = TryLoadPDB(module);
WholeProjectDecompiler decompiler;
if (DecompileBamlFlag)
{
var bamlTypeSystem = new BamlDecompilerTypeSystem(module, resolver);
var bamlSettings = new BamlDecompilerSettings {
ThrowOnAssemblyResolveErrors = settings.ThrowOnAssemblyResolveErrors
};
decompiler = new BamlAwareWholeProjectDecompiler(settings, resolver, resolver, debugInfo, bamlTypeSystem, bamlSettings);
}
else
{
decompiler = new WholeProjectDecompiler(settings, resolver, null, resolver, debugInfo);
}
using (var projectFileWriter = new StreamWriter(File.OpenWrite(projectFileName)))
return decompiler.DecompileProject(module, Path.GetDirectoryName(projectFileName), projectFileWriter);
}

29
ICSharpCode.ILSpyCmd/README.md

@ -9,8 +9,8 @@ dotnet tool install --global ilspycmd @@ -9,8 +9,8 @@ dotnet tool install --global ilspycmd
Help output (`ilspycmd --help`):
```
ilspycmd: 9.0.0.7847
ICSharpCode.Decompiler: 9.0.0.7847
ilspycmd: 10.1.0.8361
ICSharpCode.Decompiler: 10.1.0.8361
dotnet tool for decompiling .NET assemblies and generating portable PDBs
@ -32,13 +32,21 @@ Options: @@ -32,13 +32,21 @@ Options:
-usepdb|--use-varnames-from-pdb Use variable names from PDB.
-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)
--list-resources Lists all embedded resources in the assembly. Entries inside .resources
containers are listed individually as '<container>/<entry>'.
--resource <name> Extract a single resource by name (as printed by --list-resources). Resources
whose name ends with '.baml' are decompiled to XAML.
--decompile-baml When used with -p, decompile BAML resources to XAML files (Page items) instead
of leaving them as raw byte streams.
-lv|--languageversion <version> 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
CSharp11_0, CSharp12_0, CSharp13_0, Preview or Latest
Allowed values are: CSharp1, CSharp2, CSharp3, CSharp4, CSharp5, CSharp6,
CSharp7, CSharp7_1, CSharp7_2, CSharp7_3, CSharp8_0, CSharp9_0, CSharp10_0,
CSharp11_0, Preview, CSharp12_0, Latest.
CSharp11_0, CSharp12_0, CSharp13_0, CSharp14_0, Preview, Latest.
Default value is: Latest.
--ilspy-settingsfile <path> Path to an ILSpy settings file.
-ds|--decompiler-setting <value> Set a decompiler setting. Use multiple times to set multiple settings.
-r|--referencepath <path> Path to a directory containing dependencies of the assembly that is being
decompiled.
--no-dead-code Remove dead code.
@ -62,8 +70,8 @@ Options: @@ -62,8 +70,8 @@ Options:
your regular expressions.
--generate-diagrammer-docs The path or file:// URI of the XML file containing the target assembly's
documentation comments. You only need to set this if a) you want your diagrams
annotated with them and b) the file name differs from that of the assmbly. To
enable XML documentation output for your assmbly, see
annotated with them and b) the file name differs from that of the assembly. To
enable XML documentation output for your assembly, see
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/xmldoc/#create-xml-documentation-output
--generate-diagrammer-strip-namespaces Optional space-separated namespace names that are removed for brevity from XML
documentation comments. Note that the order matters: e.g. replace
@ -92,6 +100,15 @@ Examples: @@ -92,6 +100,15 @@ Examples:
Generate a HTML diagrammer containing filtered type info into a custom output folder
(including types in the LightJson namespace while excluding types in nested LightJson.Serialization namespace)
ilspycmd sample.dll --generate-diagrammer -o c:\diagrammer --generate-diagrammer-include LightJson\\..+ --generate-diagrammer-exclude LightJson\\.Serialization\\..+
List all embedded resources in a WPF assembly (including BAML entries inside .g.resources containers).
ilspycmd sample.dll --list-resources
Extract a single resource. If the name ends with .baml, the output is decompiled XAML; otherwise raw bytes.
ilspycmd sample.dll --resource sample.g.resources/mainwindow.baml -o c:\decompiled
Decompile assembly as a compilable project and convert all BAML resources to XAML Page items.
ilspycmd sample.dll -p -o c:\decompiled --decompile-baml
```
## Generate HTML diagrammers

177
ICSharpCode.ILSpyCmd/ResourceExtensions.cs

@ -0,0 +1,177 @@ @@ -0,0 +1,177 @@
// Copyright (c) 2026 AlphaSierraPapa for the SharpDevelop Team
//
// 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 System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using ICSharpCode.BamlDecompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.ILSpyCmd
{
static class ResourceExtensions
{
public static IEnumerable<string> EnumerateResourcePaths(MetadataFile module)
{
foreach (var r in module.Resources.Where(r => r.ResourceType == ResourceType.Embedded))
{
IReadOnlyList<string> entries;
if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)
&& TryReadResourcesEntryNames(r, out entries))
{
foreach (var name in entries)
yield return r.Name + "/" + name;
}
else
{
yield return r.Name;
}
}
}
public static bool TryGetResource(MetadataFile module, string resourcePath, out object value)
{
foreach (var r in module.Resources.Where(r => r.ResourceType == ResourceType.Embedded))
{
if (string.Equals(resourcePath, r.Name, StringComparison.OrdinalIgnoreCase))
{
using Stream stream = r.TryOpenStream();
if (stream == null)
{
value = null;
return false;
}
stream.Position = 0;
var ms = new MemoryStream();
stream.CopyTo(ms);
value = ms.ToArray();
return true;
}
string prefix = r.Name + "/";
if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)
&& resourcePath.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
{
string entryName = resourcePath.Substring(prefix.Length);
if (TryReadResourcesEntry(r, entryName, out value))
return true;
}
}
value = null;
return false;
}
public static XDocument DecompileBaml(MetadataFile module, IAssemblyResolver resolver, Stream bamlStream, BamlDecompilerSettings settings, CancellationToken cancellationToken)
{
var typeSystem = new BamlDecompilerTypeSystem(module, resolver);
var decompiler = new XamlDecompiler(typeSystem, settings) {
CancellationToken = cancellationToken
};
return decompiler.Decompile(bamlStream).Xaml;
}
static bool TryReadResourcesEntryNames(Resource r, out IReadOnlyList<string> names)
{
using Stream stream = r.TryOpenStream();
if (stream == null)
{
names = Array.Empty<string>();
return false;
}
stream.Position = 0;
ResourcesFile resourcesFile;
try
{
resourcesFile = new ResourcesFile(stream);
}
catch (Exception ex) when (ex is BadImageFormatException || ex is EndOfStreamException)
{
names = null;
return false;
}
using (resourcesFile)
{
var list = new List<string>(resourcesFile.ResourceCount);
for (int i = 0; i < resourcesFile.ResourceCount; i++)
list.Add(resourcesFile.GetResourceName(i));
names = list;
return true;
}
}
static bool TryReadResourcesEntry(Resource r, string entryName, out object value)
{
using Stream stream = r.TryOpenStream();
if (stream == null)
{
value = null;
return false;
}
stream.Position = 0;
ResourcesFile resourcesFile;
try
{
resourcesFile = new ResourcesFile(stream);
}
catch (Exception ex) when (ex is BadImageFormatException || ex is EndOfStreamException)
{
value = null;
return false;
}
using (resourcesFile)
{
for (int i = 0; i < resourcesFile.ResourceCount; i++)
{
if (!string.Equals(resourcesFile.GetResourceName(i), entryName, StringComparison.OrdinalIgnoreCase))
continue;
object entryValue = resourcesFile.GetResourceValue(i);
if (entryValue is ResourceSerializedObject serialized)
{
value = serialized.GetBytes();
}
else if (entryValue is Stream s)
{
var ms = new MemoryStream();
s.Position = 0;
s.CopyTo(ms);
value = ms.ToArray();
}
else
{
value = entryValue;
}
return true;
}
}
value = null;
return false;
}
}
}

386
ICSharpCode.ILSpyCmd/packages.lock.json

@ -15,279 +15,279 @@ @@ -15,279 +15,279 @@
},
"Microsoft.Extensions.Hosting": {
"type": "Direct",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "ygrWasQx4OgbUfJpA2PQHon+c5yQWSoIpG2+f2uyEGs8ciTRoyn+Ne12e9zp6VZ2GNNb8CnUoxq1ika66tjVCA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.6",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6",
"Microsoft.Extensions.Configuration.Binder": "10.0.6",
"Microsoft.Extensions.Configuration.CommandLine": "10.0.6",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.6",
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.6",
"Microsoft.Extensions.Configuration.Json": "10.0.6",
"Microsoft.Extensions.Configuration.UserSecrets": "10.0.6",
"Microsoft.Extensions.DependencyInjection": "10.0.6",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Diagnostics": "10.0.6",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.6",
"Microsoft.Extensions.FileProviders.Physical": "10.0.6",
"Microsoft.Extensions.Hosting.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging": "10.0.6",
"Microsoft.Extensions.Logging.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging.Configuration": "10.0.6",
"Microsoft.Extensions.Logging.Console": "10.0.6",
"Microsoft.Extensions.Logging.Debug": "10.0.6",
"Microsoft.Extensions.Logging.EventLog": "10.0.6",
"Microsoft.Extensions.Logging.EventSource": "10.0.6",
"Microsoft.Extensions.Options": "10.0.6"
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "VfEyM2BipThcSd0GG/FS2ZPCVCTiosVq2zLKEDsfeMIg78sOVZPEmS7CgWlb+dqTlgXvLSL4OG2q6sM4xRhHNg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
"Microsoft.Extensions.Configuration.Binder": "10.0.8",
"Microsoft.Extensions.Configuration.CommandLine": "10.0.8",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.8",
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.8",
"Microsoft.Extensions.Configuration.Json": "10.0.8",
"Microsoft.Extensions.Configuration.UserSecrets": "10.0.8",
"Microsoft.Extensions.DependencyInjection": "10.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Diagnostics": "10.0.8",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.8",
"Microsoft.Extensions.FileProviders.Physical": "10.0.8",
"Microsoft.Extensions.Hosting.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging": "10.0.8",
"Microsoft.Extensions.Logging.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging.Configuration": "10.0.8",
"Microsoft.Extensions.Logging.Console": "10.0.8",
"Microsoft.Extensions.Logging.Debug": "10.0.8",
"Microsoft.Extensions.Logging.EventLog": "10.0.8",
"Microsoft.Extensions.Logging.EventSource": "10.0.8",
"Microsoft.Extensions.Options": "10.0.8"
}
},
"NuGet.Protocol": {
"type": "Direct",
"requested": "[7.3.0, )",
"resolved": "7.3.0",
"contentHash": "QWx4Fko06Act+gVhB9UUc8Hzt0fnA8qQhD5SFn/xEis2ZZVzmatHmMdsc0SV7tyvCUlfG8DzQzYLF7fF4LvTyA==",
"requested": "[7.6.0, )",
"resolved": "7.6.0",
"contentHash": "Ccb9dJG9hW0FdHFjXoHmhJBBJRYSCeSJArLdZjyZj6/FEAIKezLn75KFQNrTPE5UiAp4HVrjBizufZ/0IXhfKQ==",
"dependencies": {
"NuGet.Packaging": "7.3.0"
"NuGet.Packaging": "7.6.0"
}
},
"System.Security.Cryptography.Pkcs": {
"type": "Direct",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "88tquaGJ1htm4DHWS6x9jwER7sFET2SVRN7HqO1FYZwE0diDcUmz0ajhVa8ZD2HGhDJBueSPjP/gqyP3gXtT2A=="
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "2wOycCqMyg9Tu+SDP03FFwDEWBni/3xOKgt0bRXplOvyeIcUJmWO7m3gTCF2mIdtQLROLtOP5VwWRT8YBwP/bA=="
},
"TomsToolbox.Composition.Analyzer": {
"type": "Direct",
"requested": "[2.23.0, )",
"resolved": "2.23.0",
"contentHash": "WHvTkPV5AI9FQY7MB7PUWgIkm69NEiCmuG0A7cVXFF/wNQ0xI5YjS60a20vM1Z1Sg+mDDkZ4452hwFGaky0Txg=="
"requested": "[2.24.0, )",
"resolved": "2.24.0",
"contentHash": "dKHqW1MeAMnDIbtx8qDTsGwy/7LUiQ3ccdzHX0PzCh1r98Lgl/1deIky9+ojZO0K5HjeA7uE+eW9/h+v7EOIBA=="
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "a7bA7IT3ngIgcOMb/2MVH5CcfSxUCeQ6QXWS1Vt6oFpzLTH3U1+J2Xtc64Uw3whX9akYG8eR/UQeEzxo64zZLg==",
"resolved": "10.0.8",
"contentHash": "I63esIFbL3h5pSt7gXpXOlmcwDmYBUoYNEglKfDPFUqtYvSV84f2l28hO2lfVXsV0wdlplgAM7IVz16matapSg==",
"dependencies": {
"Microsoft.Extensions.Primitives": "10.0.6"
"Microsoft.Extensions.Primitives": "10.0.8"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "h22Fm4VxRmr4ty9rxJaW0i51xD56Bl5QhQ2hsGY2vl+6FioWmBhkpg3B78XQaK25N+hE41gZLZuYKGQS+OGbdw==",
"resolved": "10.0.8",
"contentHash": "R3NN1X+kVu14uoxLEW6sBSQyhogDSbaOQzILnCtuXxBN4hx22AgjWPwZX6v/suERFkEDgU1lk12AglHTrUxhlw==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.6",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6"
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8"
}
},
"Microsoft.Extensions.Configuration.CommandLine": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "QIhi6cJMfeGBGs36DGc+3k5yYFAc9TAk3TN3WaommALXVv+syLSIkFwDgXDtrXvAgvFwOrRjxWpzJ88TLD1uhA==",
"resolved": "10.0.8",
"contentHash": "nQXq1a4MiInYh+0VF9fguxAl06q2ftmOyYQ+5e933s4rk57xjgkbTjUdFUySzjrcrvDeWsSqlZB+TE8+TbM2HA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.6",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6"
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8"
}
},
"Microsoft.Extensions.Configuration.EnvironmentVariables": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "ZqkqIq6AXCBrLHqLGpjv0otGo0Dx1rF1UdDuVWDiog8jXuRwb3IH59fDONIxUschwDcYaD5xftrPCWdH1YD6lQ==",
"resolved": "10.0.8",
"contentHash": "bVGqctAfPGfTxJvNp8pMshtvpsUj6r6JkeiCNVIGVYO5gBxuxdN0Lbr25kEvE/zXdctkEc44g8HssnPgDnFGVA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.6",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6"
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8"
}
},
"Microsoft.Extensions.Configuration.FileExtensions": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "hils30RkqBbtQVupvgUr7sgxJUYPc6YMEDge1QAXGTOhbRlqk2I0OH+BWMSsQjYnbGX2Ytl6EkrLgu9im6vE0w==",
"resolved": "10.0.8",
"contentHash": "1g9mzuu8gIHkjYb0jLxOTQVl/QDG5nn0b0JzgT/gbgNKr6gXZzxOHRAsdYRc1eDApB7LdHR8uK5vQrNjIQdRrQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.6",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.6",
"Microsoft.Extensions.FileProviders.Physical": "10.0.6",
"Microsoft.Extensions.Primitives": "10.0.6"
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.8",
"Microsoft.Extensions.FileProviders.Physical": "10.0.8",
"Microsoft.Extensions.Primitives": "10.0.8"
}
},
"Microsoft.Extensions.Configuration.UserSecrets": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "DBuOHuzQitvowdS06xaHaOQl1Tcy8D+vU/FNAClkMPB23skPDbmN14t0ijJlQUGC9o10u+x+xVEsQk30ywYFtQ==",
"resolved": "10.0.8",
"contentHash": "6XTfFOnf27WY8kEeZkTZ4YNn0t+imgvdQ0YaAdR4vgURKATo9bCaVJ1KB71IOJAQtJP7Elb53VHlTNXg2CtSsA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6",
"Microsoft.Extensions.Configuration.Json": "10.0.6",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.6",
"Microsoft.Extensions.FileProviders.Physical": "10.0.6"
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
"Microsoft.Extensions.Configuration.Json": "10.0.8",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.8",
"Microsoft.Extensions.FileProviders.Physical": "10.0.8"
}
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "poUvwtf92bEs8uBH3aRRs/ZgiAw+Z485EU7TtVPBt//MmD0uMPERe7+v3Ur7lpD8XgIEDL9sDoTBcW1LMG97CQ==",
"resolved": "10.0.8",
"contentHash": "daf62xHIrq8pnE709hgaZZN9tSam9TGGepWe1+bE6V3GEuVwJiMs6ib+38lfMCyAJAHiX0vapxBhsuMSV7U+cg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6"
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8"
}
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "vbigpAOKX+Bbm2uJQ/AqXqONEPPB3ZYkynRT24vo5ZWF1rzKPtVjpkQkJx5qTGp2dqNV5In9QqboayqmKdvGUA==",
"resolved": "10.0.8",
"contentHash": "uduyw9d3Fi+sbredO5drA1S44AQS2FRNFyn72UmB2vmQIO1qaXprpp1U/2lYhYi8yFdVERfY9sy/pxw/qPOU9w==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.6",
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.6",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.6"
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.8"
}
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "L98Xe5C+xyKytLNLiEyQ0rcY8GNXAeAn1xKsE0YDxPx/mXBYYtRoj8pC2cnbSFQUlOzBkyO90ivMSV22SRETFg==",
"resolved": "10.0.8",
"contentHash": "+f4C5g78QCGNyxzUfrTYsB7qYx06Zca0e88s3qFlea9/lQhgPImYdNprlgzl1uHhRU3fVHLfmbijayU2sJEZ6w==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Options": "10.0.6"
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Options": "10.0.8"
}
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "iU/lPyrjHVA4jJ7Bl/VpXvgsAD4qJWc4oPSVJjMBeZjmv7IIo8wBKxnOUoXdZcSCUJ6MeBMs3WpXNfncO7OzRg==",
"resolved": "10.0.8",
"contentHash": "U+oquaPxFdY8lYeEIWO/AD7jDIl9sPW6aVWMQRHU/pZ/SWpLcOrAj2fcLe1HwXl4sYw1ONI56K/eELT3xr4RRQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "10.0.6"
"Microsoft.Extensions.Primitives": "10.0.8"
}
},
"Microsoft.Extensions.FileProviders.Physical": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "t6T7umdzTKkSBOUMe5RYk826cTCsDU0hne9lPN5RGOSb3Kq0Xw8OEErM4zJ4dgZWV3G0ObK1Hf1IVU88uIKe6A==",
"resolved": "10.0.8",
"contentHash": "GkPvQe6IdidLu6Q3Lw6+B8NJpW8feW8czZ5mBKt5rXM/x8MvZfEp5WvAsjznzDGd23chIDrW0b2mmt+ScnEgiw==",
"dependencies": {
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.6",
"Microsoft.Extensions.FileSystemGlobbing": "10.0.6",
"Microsoft.Extensions.Primitives": "10.0.6"
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.8",
"Microsoft.Extensions.FileSystemGlobbing": "10.0.8",
"Microsoft.Extensions.Primitives": "10.0.8"
}
},
"Microsoft.Extensions.FileSystemGlobbing": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "EG9GuYJlj1o1G8maSpKceZdj88OehKFRWaWp8BWUQWlvIJDWD8N0sIYDoRMGL/yX85H8KbVYPR9+dH/UjPEiKw=="
"resolved": "10.0.8",
"contentHash": "IUQet3SY51xIFcFZKtAB6a54/Zdxs7T3SQ84kJtOD6yeXfZgiOMksACWD5qtTmXGQGFH4QYGBOT0KIO8Uy/dJw=="
},
"Microsoft.Extensions.Hosting.Abstractions": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "x3C8tgsX+xWvV5u76LFm24/U7sSnCRjuudBkbFsMV/DIqCA85te7YGg6dpa7lBToDhi4Lry9E7Arpy0laUw5AQ==",
"resolved": "10.0.8",
"contentHash": "MoOWFPT88/pDfmWpbU9PydKRX/rJFQkliowE/L9wbQcl94IicUphb5BFgepkWiDkYYxPnuEqjN4buzOGW4vJpQ==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.6",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging.Abstractions": "10.0.6"
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.8",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging.Abstractions": "10.0.8"
}
},
"Microsoft.Extensions.Logging": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "ZjpnbMD88IcZQE2pE9lcGv3mkH2mlApPWNh88ya1wJpcxZLp7p4aN7twI2FpawGPAsXNpmMgtKaz3o796YWKWQ==",
"resolved": "10.0.8",
"contentHash": "K60JhWC2hN/Gi7TP68tBxSzk5ACWOs7lkmPzsfA8Bcf/IXTajujt2ORMf9rSMk1bsng6Lv4Y3fuxp3bm1+15ug==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "10.0.6",
"Microsoft.Extensions.Logging.Abstractions": "10.0.6",
"Microsoft.Extensions.Options": "10.0.6"
"Microsoft.Extensions.DependencyInjection": "10.0.8",
"Microsoft.Extensions.Logging.Abstractions": "10.0.8",
"Microsoft.Extensions.Options": "10.0.8"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "1YgBO3wAy0dlpQyVTKWBSPND/t0yZHsvd3shGpbeEwH8JSb2hnFI2pNFrOOUi/stsp+T/dqwqmRIGh47ibo9bw==",
"resolved": "10.0.8",
"contentHash": "fdVadZmsC8jRP0KvKy8mO8f6GV/HyBvElfcSxEhd+5FM5boAw/01iSaCto5G3G37ApJira4A3pNaVvBv8cUiLQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6"
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8"
}
},
"Microsoft.Extensions.Logging.Configuration": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "YXyeFL/MFNuy7k4zCIxldXdyyK7hpW3wPnqyS5HxOJ+BkMkaT7cYVmpWYNnRaiEM6a98vjVjvIRHiUUsTJfc6g==",
"resolved": "10.0.8",
"contentHash": "rxSLTO7xTbcC3DuEJHNEijBr8g14Jj62zQ+DeFu68bsoTYoU8jLcMhc1735PV21bESXsATlL5LsfaWH71FOWAg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.6",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6",
"Microsoft.Extensions.Configuration.Binder": "10.0.6",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging": "10.0.6",
"Microsoft.Extensions.Logging.Abstractions": "10.0.6",
"Microsoft.Extensions.Options": "10.0.6",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.6"
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
"Microsoft.Extensions.Configuration.Binder": "10.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging": "10.0.8",
"Microsoft.Extensions.Logging.Abstractions": "10.0.8",
"Microsoft.Extensions.Options": "10.0.8",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.8"
}
},
"Microsoft.Extensions.Logging.Console": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "i6yclZFcPCX3MWphzPEbnBXpgT9vjZQppS4mFFvzSVols9JvvZPVeMe1ufv1bWC0/NwrBY5C+xKX4Joq+8HCkg==",
"resolved": "10.0.8",
"contentHash": "6cv53sHsPnFS56PJw8X4GbNcjeX1KGyFJRxJWvxOgK63cnqeSB1k1eRwjUdkse0tBhwlH6qc9EOYDlan+CYTuw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging": "10.0.6",
"Microsoft.Extensions.Logging.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging.Configuration": "10.0.6",
"Microsoft.Extensions.Options": "10.0.6"
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging": "10.0.8",
"Microsoft.Extensions.Logging.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging.Configuration": "10.0.8",
"Microsoft.Extensions.Options": "10.0.8"
}
},
"Microsoft.Extensions.Logging.Debug": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "CS6sPOCtu4NZo7fy4+475DPyqP0Yty2lj14yGZBC6JRdLQKuy+698gcZpKlCEzfr/0mqnbuBlrLRr/LgI7u/4g==",
"resolved": "10.0.8",
"contentHash": "4HW3M1lGHHDwEYcDZHRNptBQ48LCI2yW+XV4vuxdfQUqafTpVT8j9RqAsez08krZKhIiaArWu8iQq5uRKZ9Ffg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging": "10.0.6",
"Microsoft.Extensions.Logging.Abstractions": "10.0.6"
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging": "10.0.8",
"Microsoft.Extensions.Logging.Abstractions": "10.0.8"
}
},
"Microsoft.Extensions.Logging.EventLog": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "E/EI8sRdfbdLfHsmtdVwvX2ygoyCvP0l8Bk95QS00nw7ZHuEIibalafSTNMGrIz34+Wriyivl6unQ56g634QPQ==",
"resolved": "10.0.8",
"contentHash": "kK/C3SLIoGrcZvddYQw4eMm6YaROiSYBO7YgUR5Hdv5l+GIjBmbvQK5cST2FqjeubiAOPqFEimBT2N/8wVI+3A==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging": "10.0.6",
"Microsoft.Extensions.Logging.Abstractions": "10.0.6",
"Microsoft.Extensions.Options": "10.0.6",
"System.Diagnostics.EventLog": "10.0.6"
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging": "10.0.8",
"Microsoft.Extensions.Logging.Abstractions": "10.0.8",
"Microsoft.Extensions.Options": "10.0.8",
"System.Diagnostics.EventLog": "10.0.8"
}
},
"Microsoft.Extensions.Logging.EventSource": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "On5ERRSmspe7/rCoiy+gaWmNI2hriIBTQS/2jtakeKE9MR7iDhOOjVjzjWapzZW3BlzAi4xCkocNqFl2AYQN3g==",
"resolved": "10.0.8",
"contentHash": "HX2M0MgzwQM8jpLe3AYAEMd0YsUfOP5RgGrDuk+Ki9n7HSuMbvLm9TEV3qRI3Pg9aqxc56GfgK/KdMRBhfWwKw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Logging": "10.0.6",
"Microsoft.Extensions.Logging.Abstractions": "10.0.6",
"Microsoft.Extensions.Options": "10.0.6",
"Microsoft.Extensions.Primitives": "10.0.6"
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Logging": "10.0.8",
"Microsoft.Extensions.Logging.Abstractions": "10.0.8",
"Microsoft.Extensions.Options": "10.0.8",
"Microsoft.Extensions.Primitives": "10.0.8"
}
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "v5RTWm+3Gdub21ADJeRG5bunOOxutFNBZk6qGH6Az4L5nyRZoLe3Kse7jfAyUcdEoiKp72XpNw/wGR+9wP+MtQ==",
"resolved": "10.0.8",
"contentHash": "VBD+131DpTNCNDfA4kIyKTiCySvJGNhwibdWBSdFRu7GMfXLXcXODkgA+KStKbbhzraLglZWUN4nXyHgW4JIRA==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Primitives": "10.0.6"
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Primitives": "10.0.8"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "O7xt1vYMxku2+/WpFkh6X8RzUtYbKR+XCt0KOO0W9TbRbFeQdfb9Nry/CdVq57kOyOKS3Z4qD1xqV/8LpJQ0Xw==",
"resolved": "10.0.8",
"contentHash": "VOapXeO3lhBH0zYoyAH7tjapuo4V5pTHlevPpiSHueEquAajqd5nF0mttm+h/uE/exwAEuM5s26SzOJtletE3w==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6",
"Microsoft.Extensions.Configuration.Binder": "10.0.6",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.6",
"Microsoft.Extensions.Options": "10.0.6",
"Microsoft.Extensions.Primitives": "10.0.6"
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
"Microsoft.Extensions.Configuration.Binder": "10.0.8",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.8",
"Microsoft.Extensions.Options": "10.0.8",
"Microsoft.Extensions.Primitives": "10.0.8"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "L8P21mqaG+CXvPheLndean/cHCOcItJqH8nx+0YQnK7wAiOR0G1IOC418ZSzTMD2D6Gmo0f2M5WR70XtpX2B8g=="
"resolved": "10.0.8",
"contentHash": "OBPo4nYhMyIbtueoC10CBm6AGAbo/A9IV8QQ/6ryZS7VvmqpGT7hunazeHLxFawRzn3oLOq4jhqhpBX4tfswWQ=="
},
"Newtonsoft.Json": {
"type": "Transitive",
@ -296,52 +296,58 @@ @@ -296,52 +296,58 @@
},
"NuGet.Common": {
"type": "Transitive",
"resolved": "7.3.0",
"contentHash": "y+7cQuzc6zePfo3GdueKgFAfE06pyNv1EGeordAMXotm6WEJw/m7UHv7GSLnib2HEPpzUk4Wvxgn9VgWkIe6Yw==",
"resolved": "7.6.0",
"contentHash": "uyXLqkbbZmkMvdHOR23l1EHW2hRmULtzoG3Ocj84VpGptnNfkODVboHGJfDfcn9Gi9oUNDs8/VjL4FZcST6zVg==",
"dependencies": {
"NuGet.Frameworks": "7.3.0"
"NuGet.Frameworks": "7.6.0"
}
},
"NuGet.Configuration": {
"type": "Transitive",
"resolved": "7.3.0",
"contentHash": "9qNpnZP73pfGm1MYET78OCW/HNnKNZpTfWPB2z/EyxQnfWtJBANeFyogX8s7S+oWMbR+EG2w6jbDVKdvVJfCXA==",
"resolved": "7.6.0",
"contentHash": "+bNj+YneC5CNg1vR+WZjLAakscJlsi0KhADZUgIJPn4pwh8/jeCUMC5ik5cpET/i+QOplDy7aJVTGkpdfrlPww==",
"dependencies": {
"NuGet.Common": "7.3.0",
"NuGet.Common": "7.6.0",
"System.Security.Cryptography.ProtectedData": "8.0.0"
}
},
"NuGet.Frameworks": {
"type": "Transitive",
"resolved": "7.3.0",
"contentHash": "AnhSFUOrCrWp7pGtqhfOxa5HKm4rfpQxuatGDKzpGtTczasj6OcGVtB74PyqW7osdu9/BtkjnkAYvNk5iT82Gg=="
"resolved": "7.6.0",
"contentHash": "rJ7QtKN45XzLXCrMATve6eFLiUyUGEkA1rFSb6U6Fw6laM4hEAcKOrcdbgWlcFUlCK2158qP1LF00hg/ivF3nw=="
},
"NuGet.Packaging": {
"type": "Transitive",
"resolved": "7.3.0",
"contentHash": "yvmJ8LUPUVMm2DAKSK3+QfJKoCxQR1NuyPYLHqFHb1zmUcGwq+IvSl/4JjUivJDztXnI9GSVlE/SjQLsRJPoYw==",
"resolved": "7.6.0",
"contentHash": "TDp+qHzRBy1zjwiJGCbfpdO0jMG5hH/bk7p1EABLKv9p5SIykDPGnbuYXm2iZO0QJ/H+hOI/vo5LfqM17Q+G0w==",
"dependencies": {
"Newtonsoft.Json": "13.0.3",
"NuGet.Configuration": "7.3.0",
"NuGet.Versioning": "7.3.0",
"NuGet.Configuration": "7.6.0",
"NuGet.Versioning": "7.6.0",
"System.Security.Cryptography.Pkcs": "8.0.1"
}
},
"NuGet.Versioning": {
"type": "Transitive",
"resolved": "7.3.0",
"contentHash": "iOQPAdnVgj2U+K3AcdGGGcdS1tWyh3nvr64eqTDFCsDuHySBXSHRhl7eR8hdc0BZHxDjacRstbuaJKCurt2oPw=="
"resolved": "7.6.0",
"contentHash": "TpZxfOoQBQk/0r/2uc1A1qNYIKHkJGgOrWP+ax3nsNAUN/1BOQMDrgmGADogSA4hOXH1ZJiyeYg4Ca+vUW0sEg=="
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "RMe4gRBwSVd1O6HVRjNwLgcH2jjrT8sHyNRJegZLX68voA+HzMf1xZPvFxMMDpyW86B9U2pYslgl4DFCE61WyA=="
"resolved": "10.0.8",
"contentHash": "+Ro7WgIom+BDNH+YhTuZKL6QJ0ctfOpTyfUG/h3aU5KwXt3OaNf0wYWrTvoBUj+34Dy5V8dN9yCco1hAJQ4txw=="
},
"System.Security.Cryptography.ProtectedData": {
"type": "Transitive",
"resolved": "8.0.0",
"contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg=="
},
"icsharpcode.bamldecompiler": {
"type": "Project",
"dependencies": {
"ICSharpCode.Decompiler": "[8.0.0-noversion, )"
}
},
"icsharpcode.decompiler": {
"type": "Project",
"dependencies": {
@ -355,8 +361,8 @@ @@ -355,8 +361,8 @@
"ICSharpCode.Decompiler": "[8.0.0-noversion, )",
"K4os.Compression.LZ4": "[1.3.8, )",
"Mono.Cecil": "[0.11.6, )",
"System.Composition.AttributedModel": "[10.0.6, )",
"System.Reflection.Metadata": "[10.0.6, )",
"System.Composition.AttributedModel": "[10.0.8, )",
"System.Reflection.Metadata": "[10.0.8, )",
"System.Runtime.CompilerServices.Unsafe": "[6.1.2, )"
}
},
@ -374,31 +380,31 @@ @@ -374,31 +380,31 @@
},
"Microsoft.Extensions.Configuration": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "lYQ9S1FGXIWIU7243RimdAXQYsFDeLhSSZvbSDwbeI/kCzZ4MIYXpp3kMQ+bDJXwl9pzMRIYkd4f9zGqcYxfAQ==",
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "ehZcoPbjzWzS4XFvuz7R3V55SmpdkyMqFURLH3yXaN9NtXd9tR6CGB7pd49HYtCkenl+G7ctXSFLhNI08xLfRg==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6",
"Microsoft.Extensions.Primitives": "10.0.6"
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
"Microsoft.Extensions.Primitives": "10.0.8"
}
},
"Microsoft.Extensions.Configuration.Json": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "o/IG5ywTfT5U1ANCAC4w1vKtXapdL/OlunywrWboySYJB79eX0+mw7qxqNRkq1WMZOJoSyjPjbyZ17l3LS7A6Q==",
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "KLtAZ6A38s1pIfCO2ns6aG14NNGMYNZ4PBYfFK4M+R4A+xuSc6oklhqDcpHZxvDpyBWeFtR5C8iQBw2ng8tUHQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.6",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.6",
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.6",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.6"
"Microsoft.Extensions.Configuration": "10.0.8",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.8",
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.8",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.8"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "w+dX4SIr1X9yegX2yX2dU1XtP4JAUVNdvOG/Evn+H+ndn96YzfIPX52FALXChrRNWFR9l77FQyg1mB7WQo6iOA=="
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "21nbDV60SRPWGIivsyl6lqBeEJNG1sginhhfWgRrr3Ais7aQ12To25OAHQxgoiJkjqy1aQ6RxpZBGYuTi7Ge6A=="
},
"Mono.Cecil": {
"type": "CentralTransitive",
@ -408,21 +414,21 @@ @@ -408,21 +414,21 @@
},
"System.Collections.Immutable": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"requested": "[10.0.8, )",
"resolved": "9.0.0",
"contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w=="
},
"System.Composition.AttributedModel": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "sdv1Uk4auk8lc/0hsuAwE8Hpgg3tgbZDk4yRANIpfgk13f6Ssh+sKQviJchygk0ZR/fnFV4LtMyqzwZS8g7UaQ=="
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "qkcb2x773qUgOcrNk+AENDEtkYobPEM46jyYelB/Va3hWWeuSIFXHD7NdvP/L+H6OmexwV06T1OKr3ONl04kZw=="
},
"System.Reflection.Metadata": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "JQ3LKu6fdgnSqrgD5Uwfjvngi40lKjxWpUsaUnjQCgF4LWAjs+55ytZx11U+09FRByMS07LrFv89KXViUhlWTQ=="
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "Ap8JPUYLRnwQPk/rpyhxSoD+55/a7zJfZsmSD0maudVJT9p/xR+xegyT8gQIwh1XMC/lRowQJM06EdQ0dfiMkA=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "CentralTransitive",

14
ICSharpCode.ILSpyX/AssemblyList.cs

@ -340,6 +340,8 @@ namespace ICSharpCode.ILSpyX @@ -340,6 +340,8 @@ namespace ICSharpCode.ILSpyX
{
VerifyAccess();
file = Path.GetFullPath(file);
LoadedAssembly evicted;
LoadedAssembly newAsm;
lock (lockObj)
{
if (!byFilename.TryGetValue(file, out LoadedAssembly? target))
@ -348,7 +350,7 @@ namespace ICSharpCode.ILSpyX @@ -348,7 +350,7 @@ namespace ICSharpCode.ILSpyX
if (index < 0)
return null;
var newAsm = new LoadedAssembly(this, file, stream: Task.FromResult<Stream?>(stream),
newAsm = new LoadedAssembly(this, file, stream: Task.FromResult<Stream?>(stream),
fileLoaders: manager?.LoaderRegistry,
applyWinRTProjections: ApplyWinRTProjections, useDebugSymbols: UseDebugSymbols);
newAsm.IsAutoLoaded = target.IsAutoLoaded;
@ -356,8 +358,10 @@ namespace ICSharpCode.ILSpyX @@ -356,8 +358,10 @@ namespace ICSharpCode.ILSpyX
Debug.Assert(newAsm.FileName == file);
byFilename[file] = newAsm;
this.assemblies[index] = newAsm;
return newAsm;
evicted = target;
}
evicted.Dispose();
return newAsm;
}
public LoadedAssembly? ReloadAssembly(string file)
@ -387,6 +391,7 @@ namespace ICSharpCode.ILSpyX @@ -387,6 +391,7 @@ namespace ICSharpCode.ILSpyX
this.assemblies.Remove(target);
this.assemblies.Insert(index, newAsm);
}
target.Dispose();
return newAsm;
}
@ -398,16 +403,21 @@ namespace ICSharpCode.ILSpyX @@ -398,16 +403,21 @@ namespace ICSharpCode.ILSpyX
assemblies.Remove(assembly);
byFilename.Remove(assembly.FileName);
}
assembly.Dispose();
}
public void Clear()
{
VerifyAccess();
LoadedAssembly[] removed;
lock (lockObj)
{
removed = assemblies.ToArray();
assemblies.Clear();
byFilename.Clear();
}
foreach (var asm in removed)
asm.Dispose();
}
public void Sort(IComparer<LoadedAssembly> comparer)
{

11
ICSharpCode.ILSpyX/LoadedAssembly.cs

@ -52,7 +52,7 @@ namespace ICSharpCode.ILSpyX @@ -52,7 +52,7 @@ namespace ICSharpCode.ILSpyX
/// * a file that is still being loaded in the background
/// </summary>
[DebuggerDisplay("[LoadedAssembly {shortName}]")]
public sealed class LoadedAssembly
public sealed class LoadedAssembly : IDisposable
{
/// <summary>
/// Maps from MetadataFile (successfully loaded .NET module) back to the LoadedAssembly instance
@ -652,5 +652,14 @@ namespace ICSharpCode.ILSpyX @@ -652,5 +652,14 @@ namespace ICSharpCode.ILSpyX
}
UniversalAssemblyResolver? universalResolver;
public void Dispose()
{
if (loadingTask.Status == TaskStatus.RanToCompletion)
{
loadingTask.Result.MetadataFile?.Dispose();
}
(debugInfoProvider as IDisposable)?.Dispose();
}
}
}

7
ICSharpCode.ILSpyX/PdbProvider/PortableDebugInfoProvider.cs

@ -32,7 +32,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataFile; @@ -32,7 +32,7 @@ using static ICSharpCode.Decompiler.Metadata.MetadataFile;
namespace ICSharpCode.ILSpyX.PdbProvider
{
public class PortableDebugInfoProvider : IDebugInfoProvider
public sealed class PortableDebugInfoProvider : IDebugInfoProvider, IDisposable
{
string? pdbFileName;
string moduleFileName;
@ -252,5 +252,10 @@ namespace ICSharpCode.ILSpyX.PdbProvider @@ -252,5 +252,10 @@ namespace ICSharpCode.ILSpyX.PdbProvider
var kind = IsEmbedded || Path.GetExtension(SourceFileName).Equals(".pdb", StringComparison.OrdinalIgnoreCase) ? MetadataFileKind.ProgramDebugDatabase : MetadataFileKind.Metadata;
return new MetadataFile(kind, SourceFileName, provider, options, 0, IsEmbedded);
}
public void Dispose()
{
provider.Dispose();
}
}
}

46
ICSharpCode.ILSpyX/packages.lock.json

@ -16,13 +16,13 @@ @@ -16,13 +16,13 @@
},
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[10.0.202, )",
"resolved": "10.0.202",
"contentHash": "vRm3w0AWndnqt8X73qoWTdD/qp7o+bYMCufl7hSiQUSqeI3WKdqA/ElE6MCUscy84FX8HCMcK2EJCtTm+4bdbw==",
"requested": "[10.0.300, )",
"resolved": "10.0.300",
"contentHash": "QzCtLkXVb3l4IxcpvJCbzUwMLihAmLN6vVLjQGSzYSF8d2dvXxqJAZk83RV3gYnp2egz8jRMgSR2woY3vOahTA==",
"dependencies": {
"Microsoft.Build.Tasks.Git": "10.0.202",
"Microsoft.SourceLink.Common": "10.0.202",
"System.IO.Hashing": "10.0.6"
"Microsoft.Build.Tasks.Git": "10.0.300",
"Microsoft.SourceLink.Common": "10.0.300",
"System.IO.Hashing": "10.0.8"
}
},
"Mono.Cecil": {
@ -33,15 +33,15 @@ @@ -33,15 +33,15 @@
},
"System.Composition.AttributedModel": {
"type": "Direct",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "sdv1Uk4auk8lc/0hsuAwE8Hpgg3tgbZDk4yRANIpfgk13f6Ssh+sKQviJchygk0ZR/fnFV4LtMyqzwZS8g7UaQ=="
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "qkcb2x773qUgOcrNk+AENDEtkYobPEM46jyYelB/Va3hWWeuSIFXHD7NdvP/L+H6OmexwV06T1OKr3ONl04kZw=="
},
"System.Reflection.Metadata": {
"type": "Direct",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "JQ3LKu6fdgnSqrgD5Uwfjvngi40lKjxWpUsaUnjQCgF4LWAjs+55ytZx11U+09FRByMS07LrFv89KXViUhlWTQ=="
"requested": "[10.0.8, )",
"resolved": "10.0.8",
"contentHash": "Ap8JPUYLRnwQPk/rpyhxSoD+55/a7zJfZsmSD0maudVJT9p/xR+xegyT8gQIwh1XMC/lRowQJM06EdQ0dfiMkA=="
},
"System.Runtime.CompilerServices.Unsafe": {
"type": "Direct",
@ -51,27 +51,27 @@ @@ -51,27 +51,27 @@
},
"TomsToolbox.Composition.Analyzer": {
"type": "Direct",
"requested": "[2.23.0, )",
"resolved": "2.23.0",
"contentHash": "WHvTkPV5AI9FQY7MB7PUWgIkm69NEiCmuG0A7cVXFF/wNQ0xI5YjS60a20vM1Z1Sg+mDDkZ4452hwFGaky0Txg=="
"requested": "[2.24.0, )",
"resolved": "2.24.0",
"contentHash": "dKHqW1MeAMnDIbtx8qDTsGwy/7LUiQ3ccdzHX0PzCh1r98Lgl/1deIky9+ojZO0K5HjeA7uE+eW9/h+v7EOIBA=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "10.0.202",
"contentHash": "HXAW7dToPLi6lHN4TJz1FfxLYfPS3EF20YrrZxQ9xbwbjLkFXJ+SwkuuILoAdCWTNHhHfa5oqOsrOEQX5s+eMg==",
"resolved": "10.0.300",
"contentHash": "P0kaQwVZx4xIUe2FtrLyBadYNXuAljttJUPvjBYRuHhPE8L77L42KakLDkaADRiUrGspoLcMwayjrbQhYTr0zA==",
"dependencies": {
"System.IO.Hashing": "10.0.6"
"System.IO.Hashing": "10.0.8"
}
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "10.0.202",
"contentHash": "g8E67huS0Slt6iJiUG1uj2N4NBo/q68Q1y1H4nFu1Cn4AOp6S5D8AeGLhGMzBT+JmNCjiGTvKco2qkbe4F31tQ=="
"resolved": "10.0.300",
"contentHash": "0jlkXaUGjYlWTIVPve5MftjKHnT3SlAtq9BCLV4J9IjdPrxV/+4rMlBSjfr1khG8/GC6KGojjola8E1VvWF0qQ=="
},
"System.IO.Hashing": {
"type": "Transitive",
"resolved": "10.0.6",
"contentHash": "lbMLkqFekDR4DeFd26eEfrG2HlvixIfs22uk/e2+9/NJ7WxMycVVakcQpuJvvqgc9XxwEgSd/Td+dZA+TjDDwA=="
"resolved": "10.0.8",
"contentHash": "+dJsbPJ3FyUbTZNplFj0RCKePFizmv6ewDV46JE9q/IVH4c3xTCftHfHelLsAKf0jryIPqgMb5GpS0x7TAY3mg=="
},
"icsharpcode.decompiler": {
"type": "Project",
@ -82,7 +82,7 @@ @@ -82,7 +82,7 @@
},
"System.Collections.Immutable": {
"type": "CentralTransitive",
"requested": "[10.0.6, )",
"requested": "[10.0.8, )",
"resolved": "9.0.0",
"contentHash": "QhkXUl2gNrQtvPmtBTQHb0YsUrDiDQ2QS09YbtTTiSjGcf7NBqtYbrG/BE06zcBPCKEwQGzIv13IVdXNOSub2w=="
}

2
ILSpy.Installer/ILSpy.Installer.csproj

@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="WixSharp_wix4" Version="2.12.2" />
<PackageReference Include="WixSharp_wix4" Version="2.14.0" />
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">

3
ILSpy.Installer/setup.cs

@ -36,6 +36,9 @@ namespace ILSpy.Installer @@ -36,6 +36,9 @@ namespace ILSpy.Installer
new Files(Path.Combine(buildOutputDir, "ILSpy.resources.dll")),
new Files(Path.Combine(buildOutputDir, "ILSpy.ReadyToRun.Plugin.resources.dll"))));
// https://github.com/oleg-shilo/wixsharp/issues/1900 in addition to the pipeline dotnet tool install command
WixExtension.UI.PreferredVersion = "6.0.2";
#if ARM64
project.Platform = Platform.arm64;
#else

3
ILSpy.ReadyToRun/Properties/AssemblyInfo.cs

@ -8,7 +8,8 @@ using System.Runtime.Versioning; @@ -8,7 +8,8 @@ using System.Runtime.Versioning;
[assembly: TargetPlatform("Windows10.0")]
[assembly: SupportedOSPlatform("Windows7.0")]
// General Information about an assembly is controlled through the following
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: AssemblyVersion("1.0.0.0")]

29
ILSpy/AssemblyTree/AssemblyTreeModel.cs

@ -946,12 +946,41 @@ namespace ICSharpCode.ILSpy.AssemblyTree @@ -946,12 +946,41 @@ namespace ICSharpCode.ILSpy.AssemblyTree
}
private void RefreshInternal()
{
RefreshInternalAsync().HandleExceptions();
}
private async Task RefreshInternalAsync()
{
using (Keyboard.FocusedElement.PreserveFocus())
{
var path = GetPathForNode(SelectedItem);
ShowAssemblyList(settingsService.AssemblyListManager.LoadList(AssemblyList.ListName));
// Ensure the assembly is loaded before FindNodeByPath, so lazy-loaded
// resource nodes (e.g. .baml entries) are present in the tree.
if (path?.Length > 0)
{
var rootAssembly = AssemblyList.FindAssembly(path[0]);
if (rootAssembly != null)
{
// FindNodeByPath() blocks the UI if the assembly is not yet loaded,
// so use an async wait instead.
var preAwaitSelection = SelectedItem;
await rootAssembly.GetMetadataFileAsync().Catch<Exception>(_ => { });
// If the user navigated to a different node while the assembly
// was loading, respect that — don't restore the pre-refresh path.
// A change to null counts too (e.g. user cleared the selection).
if (!ReferenceEquals(SelectedItem, preAwaitSelection))
{
RefreshDecompiledView();
return;
}
}
}
SelectNode(FindNodeByPath(path, true), inNewTabPage: false);
RefreshDecompiledView();

3
ILSpy/Languages/CSharpLanguage.cs

@ -24,8 +24,6 @@ using System.Linq; @@ -24,8 +24,6 @@ using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Windows;
using System.Windows.Controls;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Decompiler;
@ -119,6 +117,7 @@ namespace ICSharpCode.ILSpy @@ -119,6 +117,7 @@ namespace ICSharpCode.ILSpy
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp12_0.ToString(), "C# 12.0 / VS 2022.8"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp13_0.ToString(), "C# 13.0 / VS 2022.12"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp14_0.ToString(), "C# 14.0 / VS 2026"),
new LanguageVersion(Decompiler.CSharp.LanguageVersion.CSharp15_0.ToString(), "C# 15.0 / VS 202x.yy"),
};
}
return versions;

11
ILSpy/Properties/Resources.Designer.cs generated

@ -1360,7 +1360,16 @@ namespace ICSharpCode.ILSpy.Properties { @@ -1360,7 +1360,16 @@ namespace ICSharpCode.ILSpy.Properties {
return ResourceManager.GetString("DecompilerSettings.RequiredMembers", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Decompile runtime-async (System.Runtime.CompilerServices.AsyncHelpers) methods.
/// </summary>
public static string DecompilerSettings_RuntimeAsync {
get {
return ResourceManager.GetString("DecompilerSettings.RuntimeAsync", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &apos;scoped&apos; lifetime annotation.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -474,6 +474,9 @@ Are you sure you want to continue?</value> @@ -474,6 +474,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.RequiredMembers" xml:space="preserve">
<value>Required members</value>
</data>
<data name="DecompilerSettings.RuntimeAsync" xml:space="preserve">
<value>Decompile runtime-async (System.Runtime.CompilerServices.AsyncHelpers) methods</value>
</data>
<data name="DecompilerSettings.ScopedRef" xml:space="preserve">
<value>'scoped' lifetime annotation</value>
</data>

15
ILSpy/Search/SearchPane.xaml.cs

@ -19,6 +19,7 @@ @@ -19,6 +19,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Composition;
@ -75,14 +76,24 @@ namespace ICSharpCode.ILSpy.Search @@ -75,14 +76,24 @@ namespace ICSharpCode.ILSpy.Search
InitializeComponent();
ContextMenuProvider.Add(listBox);
MessageBus<CurrentAssemblyListChangedEventArgs>.Subscribers += (sender, e) => CurrentAssemblyList_Changed();
MessageBus<CurrentAssemblyListChangedEventArgs>.Subscribers += (sender, e) => CurrentAssemblyList_Changed(e);
MessageBus<SettingsChangedEventArgs>.Subscribers += (sender, e) => Settings_PropertyChanged(sender, e);
CompositionTarget.Rendering += UpdateResults;
}
void CurrentAssemblyList_Changed()
void CurrentAssemblyList_Changed(CurrentAssemblyListChangedEventArgs e)
{
// Don't restart the search when only auto-loaded (dependency) assemblies are added;
// this avoids flickering when navigating to a result in a large assembly.
// Explicit variable assignment necessary bc implicit operator T on WrappedEventArgs<T>
System.Collections.Specialized.NotifyCollectionChangedEventArgs collectionChange = e;
if (collectionChange.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add
&& collectionChange.NewItems?.Cast<LoadedAssembly>().All(asm => asm.IsAutoLoaded) == true)
{
return;
}
if (IsVisible)
{
StartSearch(this.SearchTerm);

7
ILSpy/SessionSettings.cs

@ -162,15 +162,18 @@ namespace ICSharpCode.ILSpy @@ -162,15 +162,18 @@ namespace ICSharpCode.ILSpy
static T FromString<T>(string s, T defaultValue)
{
if (s == null)
if (string.IsNullOrEmpty(s))
return defaultValue;
try
{
TypeConverter c = TypeDescriptor.GetConverter(typeof(T));
return (T)c.ConvertFromInvariantString(s);
}
catch (FormatException)
catch (Exception)
{
// TypeConverters for WPF types (e.g. Rect) throw InvalidOperationException, not
// FormatException, on malformed input. Treat any conversion failure as "use the
// default" so a single bad saved value can't crash startup.
return defaultValue;
}
}

Loading…
Cancel
Save