mirror of https://github.com/icsharpcode/ILSpy.git
Browse Source
Replaces the NotImplementedDialog stubs in FileCommands.cs with real implementations. Both are DEBUG-only File-menu entries for decompiler dev: Assisted-by: Claude:claude-opus-4-7:Claude Codepull/3755/head
2 changed files with 167 additions and 13 deletions
@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
|
||||
// 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.
|
||||
|
||||
#if DEBUG
|
||||
using System; |
||||
using System.Collections.Concurrent; |
||||
using System.Composition; |
||||
using System.Diagnostics; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Threading.Tasks; |
||||
|
||||
using ICSharpCode.Decompiler; |
||||
using ICSharpCode.ILSpy.Properties; |
||||
using ICSharpCode.ILSpyX; |
||||
|
||||
using ILSpy.AssemblyTree; |
||||
using ILSpy.Docking; |
||||
using ILSpy.Languages; |
||||
using ILSpy.TextView; |
||||
using ILSpy.TreeNodes; |
||||
|
||||
namespace ILSpy.Commands |
||||
{ |
||||
/// <summary>
|
||||
/// DEBUG-only File-menu stress test: decompile every loaded assembly to
|
||||
/// <c>c:\temp\decompiled\<ShortName>.cs</c> in parallel and report per-assembly
|
||||
/// timing in a results tab. Hard-coded output path matches WPF — the command's
|
||||
/// CanExecute gates on that directory existing so it's effectively opt-in.
|
||||
/// </summary>
|
||||
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile), MenuCategory = nameof(Resources.Open), MenuOrder = 2.5)] |
||||
[Shared] |
||||
sealed class DecompileAllCommand : SimpleCommand |
||||
{ |
||||
const string OutputDir = @"c:\temp\decompiled"; |
||||
|
||||
readonly AssemblyTreeModel assemblyTreeModel; |
||||
readonly DockWorkspace dockWorkspace; |
||||
|
||||
[ImportingConstructor] |
||||
public DecompileAllCommand(AssemblyTreeModel assemblyTreeModel, DockWorkspace dockWorkspace) |
||||
{ |
||||
this.assemblyTreeModel = assemblyTreeModel; |
||||
this.dockWorkspace = dockWorkspace; |
||||
} |
||||
|
||||
public override bool CanExecute(object? parameter) => Directory.Exists(OutputDir); |
||||
|
||||
public override void Execute(object? parameter) => _ = ExecuteAsync(); |
||||
|
||||
async Task ExecuteAsync() |
||||
{ |
||||
try |
||||
{ |
||||
var report = await dockWorkspace.RunWithCancellation(token => Task.Run(() => { |
||||
var output = new AvaloniaEditTextOutput { Title = "Decompile All" }; |
||||
var bag = new ConcurrentBag<string>(); |
||||
Parallel.ForEach( |
||||
Partitioner.Create(assemblyTreeModel.AssemblyList!.GetAssemblies(), loadBalance: true), |
||||
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount, CancellationToken = token }, |
||||
asm => { |
||||
if (asm.HasLoadError) |
||||
return; |
||||
var watch = Stopwatch.StartNew(); |
||||
Exception? ex = null; |
||||
var path = Path.Combine(OutputDir, asm.ShortName + ".cs"); |
||||
try |
||||
{ |
||||
using var writer = new StreamWriter(path); |
||||
var options = new DecompilationOptions { |
||||
CancellationToken = token, |
||||
FullDecompilation = true, |
||||
}; |
||||
new CSharpLanguage().DecompileAssembly(asm, new PlainTextOutput(writer), options); |
||||
} |
||||
catch (Exception caught) |
||||
{ |
||||
ex = caught; |
||||
} |
||||
watch.Stop(); |
||||
bag.Add(asm.ShortName + " - " + watch.Elapsed + (ex != null ? " - " + ex.GetType().Name : "")); |
||||
}); |
||||
foreach (var line in bag.OrderBy(s => s, StringComparer.OrdinalIgnoreCase)) |
||||
{ |
||||
output.Write(line); |
||||
output.WriteLine(); |
||||
} |
||||
return output; |
||||
}, token), "Decompiling all assemblies…"); |
||||
dockWorkspace.ShowText(report); |
||||
} |
||||
catch (OperationCanceledException) { } |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// DEBUG-only stress test: re-decompile the current selection 100 times in series
|
||||
/// and report average wall-clock time. Used for tracking decompilation perf
|
||||
/// regressions across changes.
|
||||
/// </summary>
|
||||
[ExportMainMenuCommand(ParentMenuID = nameof(Resources._File), Header = nameof(Resources.DEBUGDecompile100x), MenuCategory = nameof(Resources.Open), MenuOrder = 2.6)] |
||||
[Shared] |
||||
sealed class Decompile100TimesCommand : SimpleCommand |
||||
{ |
||||
const int NumRuns = 100; |
||||
|
||||
readonly AssemblyTreeModel assemblyTreeModel; |
||||
readonly LanguageService languageService; |
||||
readonly DockWorkspace dockWorkspace; |
||||
|
||||
[ImportingConstructor] |
||||
public Decompile100TimesCommand(AssemblyTreeModel assemblyTreeModel, LanguageService languageService, DockWorkspace dockWorkspace) |
||||
{ |
||||
this.assemblyTreeModel = assemblyTreeModel; |
||||
this.languageService = languageService; |
||||
this.dockWorkspace = dockWorkspace; |
||||
} |
||||
|
||||
public override void Execute(object? parameter) => _ = ExecuteAsync(); |
||||
|
||||
async Task ExecuteAsync() |
||||
{ |
||||
var language = languageService.CurrentLanguage; |
||||
var nodes = assemblyTreeModel.SelectedItems.OfType<ILSpyTreeNode>().ToArray(); |
||||
if (nodes.Length == 0) |
||||
return; |
||||
try |
||||
{ |
||||
var report = await dockWorkspace.RunWithCancellation(token => Task.Run(() => { |
||||
var watch = Stopwatch.StartNew(); |
||||
var options = new DecompilationOptions { CancellationToken = token }; |
||||
for (int i = 0; i < NumRuns; i++) |
||||
{ |
||||
foreach (var node in nodes) |
||||
node.Decompile(language, new PlainTextOutput(), options); |
||||
} |
||||
watch.Stop(); |
||||
var output = new AvaloniaEditTextOutput { Title = "Decompile 100×" }; |
||||
var msPerRun = watch.Elapsed.TotalMilliseconds / NumRuns; |
||||
output.Write($"Average time: {msPerRun:f1}ms"); |
||||
output.WriteLine(); |
||||
return output; |
||||
}, token), "Decompiling 100×…"); |
||||
dockWorkspace.ShowText(report); |
||||
} |
||||
catch (OperationCanceledException) { } |
||||
} |
||||
} |
||||
} |
||||
#endif
|
||||
Loading…
Reference in new issue