Browse Source

Refactor Solution decompilation to use Language instead of AssemblyTreeNode.

pull/1550/head
Siegfried Pammer 6 years ago
parent
commit
a63e94e5b4
  1. 1
      ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs
  2. 4
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  3. 56
      ICSharpCode.Decompiler/Solution/ProjectId.cs
  4. 55
      ICSharpCode.Decompiler/Solution/ProjectItem.cs
  5. 70
      ICSharpCode.Decompiler/Solution/SolutionCreator.cs
  6. 8
      ILSpy/Languages/CSharpLanguage.cs
  7. 7
      ILSpy/Languages/ILLanguage.cs
  8. 8
      ILSpy/Languages/Language.cs
  9. 75
      ILSpy/MainWindow.xaml.cs
  10. 128
      ILSpy/SolutionWriter.cs
  11. 4
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  12. 4
      ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs
  13. 12
      ILSpy/TreeNodes/AssemblyTreeNode.cs
  14. 3
      ILSpy/TreeNodes/BaseTypesEntryNode.cs
  15. 4
      ILSpy/TreeNodes/BaseTypesTreeNode.cs
  16. 3
      ILSpy/TreeNodes/DerivedTypesEntryNode.cs
  17. 3
      ILSpy/TreeNodes/DerivedTypesTreeNode.cs
  18. 3
      ILSpy/TreeNodes/EventTreeNode.cs
  19. 3
      ILSpy/TreeNodes/FieldTreeNode.cs
  20. 2
      ILSpy/TreeNodes/ILSpyTreeNode.cs
  21. 3
      ILSpy/TreeNodes/MethodTreeNode.cs
  22. 3
      ILSpy/TreeNodes/ModuleReferenceTreeNode.cs
  23. 3
      ILSpy/TreeNodes/NamespaceTreeNode.cs
  24. 3
      ILSpy/TreeNodes/PropertyTreeNode.cs
  25. 3
      ILSpy/TreeNodes/ReferenceFolderTreeNode.cs
  26. 4
      ILSpy/TreeNodes/ResourceListTreeNode.cs
  27. 3
      ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs
  28. 3
      ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
  29. 4
      ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs
  30. 4
      ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs
  31. 6
      ILSpy/TreeNodes/ThreadingSupport.cs
  32. 3
      ILSpy/TreeNodes/TypeTreeNode.cs

1
ICSharpCode.Decompiler/CSharp/WholeProjectDecompiler.cs

@ -35,6 +35,7 @@ using System.Reflection.Metadata; @@ -35,6 +35,7 @@ using System.Reflection.Metadata;
using static ICSharpCode.Decompiler.Metadata.DotNetCorePathFinderExtensions;
using static ICSharpCode.Decompiler.Metadata.MetadataExtensions;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
namespace ICSharpCode.Decompiler.CSharp
{

4
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -61,7 +61,9 @@ @@ -61,7 +61,9 @@
<Compile Include="CSharp\CSharpLanguageVersion.cs" />
<Compile Include="CSharp\RequiredNamespaceCollector.cs" />
<Compile Include="CSharp\SequencePointBuilder.cs" />
<Compile Include="CSharp\SolutionCreator.cs" />
<Compile Include="Solution\ProjectId.cs" />
<Compile Include="Solution\ProjectItem.cs" />
<Compile Include="Solution\SolutionCreator.cs" />
<Compile Include="CSharp\Syntax\AstNode.cs" />
<Compile Include="CSharp\Syntax\AstNodeCollection.cs" />
<Compile Include="CSharp\Syntax\AstType.cs" />

56
ICSharpCode.Decompiler/Solution/ProjectId.cs

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
// Copyright (c) 2019 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;
namespace ICSharpCode.Decompiler.Solution
{
/// <summary>
/// A container class that holds platform and GUID information about a Visual Studio project.
/// </summary>
public class ProjectId
{
/// <summary>
/// Initializes a new instance of the <see cref="ProjectId"/> class.
/// </summary>
/// <param name="projectPlatform">The project platform.</param>
/// <param name="projectGuid">The project GUID.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="projectFile"/>
/// or <paramref name="projectPlatform"/> is null or empty.</exception>
public ProjectId(string projectPlatform, Guid projectGuid)
{
if (string.IsNullOrWhiteSpace(projectPlatform)) {
throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform));
}
Guid = projectGuid;
PlatformName = projectPlatform;
}
/// <summary>
/// Gets the GUID of this project.
/// </summary>
public Guid Guid { get; }
/// <summary>
/// Gets the platform name of this project. Only single platform per project is supported.
/// </summary>
public string PlatformName { get; }
}
}

55
ICSharpCode.Decompiler/Solution/ProjectItem.cs

@ -0,0 +1,55 @@ @@ -0,0 +1,55 @@
// Copyright (c) 2019 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.IO;
namespace ICSharpCode.Decompiler.Solution
{
/// <summary>
/// A container class that holds information about a Visual Studio project.
/// </summary>
public sealed class ProjectItem : ProjectId
{
/// <summary>
/// Initializes a new instance of the <see cref="ProjectItem"/> class.
/// </summary>
/// <param name="projectFile">The full path of the project file.</param>
/// <param name="projectPlatform">The project platform.</param>
/// <param name="projectGuid">The project GUID.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="projectFile"/>
/// or <paramref name="projectPlatform"/> is null or empty.</exception>
public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid)
: base(projectPlatform, projectGuid)
{
ProjectName = Path.GetFileNameWithoutExtension(projectFile);
FilePath = projectFile;
}
/// <summary>
/// Gets the name of the project.
/// </summary>
public string ProjectName { get; }
/// <summary>
/// Gets the full path to the project file.
/// </summary>
public string FilePath { get; }
}
}

70
ICSharpCode.Decompiler/CSharp/SolutionCreator.cs → ICSharpCode.Decompiler/Solution/SolutionCreator.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
// Copyright (c) 2016 Daniel Grunwald
// Copyright (c) 2019 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
@ -22,74 +22,8 @@ using System.IO; @@ -22,74 +22,8 @@ using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace ICSharpCode.Decompiler.CSharp
namespace ICSharpCode.Decompiler.Solution
{
/// <summary>
/// A container class that holds information about a Visual Studio project.
/// </summary>
public sealed class ProjectItem : ProjectId
{
/// <summary>
/// Initializes a new instance of the <see cref="ProjectItem"/> class.
/// </summary>
/// <param name="projectFile">The full path of the project file.</param>
/// <param name="projectPlatform">The project platform.</param>
/// <param name="projectGuid">The project GUID.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="projectFile"/>
/// or <paramref name="projectPlatform"/> is null or empty.</exception>
public ProjectItem(string projectFile, string projectPlatform, Guid projectGuid)
: base(projectPlatform, projectGuid)
{
ProjectName = Path.GetFileNameWithoutExtension(projectFile);
FilePath = projectFile;
}
/// <summary>
/// Gets the name of the project.
/// </summary>
public string ProjectName { get; }
/// <summary>
/// Gets the full path to the project file.
/// </summary>
public string FilePath { get; }
}
/// <summary>
/// A container class that holds platform and GUID information about a Visual Studio project.
/// </summary>
public class ProjectId
{
/// <summary>
/// Initializes a new instance of the <see cref="ProjectId"/> class.
/// </summary>
/// <param name="projectPlatform">The project platform.</param>
/// <param name="projectGuid">The project GUID.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="projectFile"/>
/// or <paramref name="projectPlatform"/> is null or empty.</exception>
public ProjectId(string projectPlatform, Guid projectGuid)
{
if (string.IsNullOrWhiteSpace(projectPlatform)) {
throw new ArgumentException("The platform cannot be null or empty.", nameof(projectPlatform));
}
Guid = projectGuid;
PlatformName = projectPlatform;
}
/// <summary>
/// Gets the GUID of this project.
/// </summary>
public Guid Guid { get; }
/// <summary>
/// Gets the platform name of this project. Only single platform per project is supported.
/// </summary>
public string PlatformName { get; }
}
/// <summary>
/// A helper class that can write a Visual Studio Solution file for the provided projects.
/// </summary>

8
ILSpy/Languages/CSharpLanguage.cs

@ -34,6 +34,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax; @@ -34,6 +34,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Output;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.TreeNodes;
@ -343,7 +344,7 @@ namespace ICSharpCode.ILSpy @@ -343,7 +344,7 @@ namespace ICSharpCode.ILSpy
}
}
public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
var module = assembly.GetPEFileOrNull();
if (options.FullDecompilation && options.SaveAsProjectDirectory != null) {
@ -389,7 +390,7 @@ namespace ICSharpCode.ILSpy @@ -389,7 +390,7 @@ namespace ICSharpCode.ILSpy
}
if (metadata.IsAssembly) {
var asm = metadata.GetAssemblyDefinition();
if (asm.HashAlgorithm != System.Reflection.AssemblyHashAlgorithm.None)
if (asm.HashAlgorithm != AssemblyHashAlgorithm.None)
output.WriteLine("// Hash algorithm: " + asm.HashAlgorithm.ToString().ToUpper());
if (!asm.PublicKey.IsNil) {
output.Write("// Public key: ");
@ -415,8 +416,7 @@ namespace ICSharpCode.ILSpy @@ -415,8 +416,7 @@ namespace ICSharpCode.ILSpy
}
WriteCode(output, options.DecompilerSettings, st, decompiler.TypeSystem);
}
return true;
return null;
}
}

7
ILSpy/Languages/ILLanguage.cs

@ -27,6 +27,8 @@ using System.Reflection.Metadata.Ecma335; @@ -27,6 +27,8 @@ using System.Reflection.Metadata.Ecma335;
using System.Linq;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.Decompiler.Solution;
namespace ICSharpCode.ILSpy
{
@ -150,7 +152,7 @@ namespace ICSharpCode.ILSpy @@ -150,7 +152,7 @@ namespace ICSharpCode.ILSpy
dis.DisassembleNamespace(nameSpace, module, types.Select(t => (TypeDefinitionHandle)t.MetadataToken));
}
public override object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
public override ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
output.WriteLine("// " + assembly.FileName);
output.WriteLine();
@ -174,8 +176,7 @@ namespace ICSharpCode.ILSpy @@ -174,8 +176,7 @@ namespace ICSharpCode.ILSpy
dis.WriteModuleContents(module);
}
}
return true;
return null;
}
}
}

8
ILSpy/Languages/Language.cs

@ -23,6 +23,7 @@ using System.Reflection.PortableExecutable; @@ -23,6 +23,7 @@ using System.Reflection.PortableExecutable;
using System.Text;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
@ -131,11 +132,11 @@ namespace ICSharpCode.ILSpy @@ -131,11 +132,11 @@ namespace ICSharpCode.ILSpy
WriteCommentLine(output, nameSpace);
}
public virtual object DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
public virtual ProjectId DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options)
{
WriteCommentLine(output, assembly.FileName);
var asm = assembly.GetPEFileOrNull();
if (asm == null) return false;
if (asm == null) return null;
var metadata = asm.Metadata;
if (metadata.IsAssembly) {
var name = metadata.GetAssemblyDefinition();
@ -147,8 +148,7 @@ namespace ICSharpCode.ILSpy @@ -147,8 +148,7 @@ namespace ICSharpCode.ILSpy
} else {
WriteCommentLine(output, metadata.GetString(metadata.GetModuleDefinition().Name));
}
return true;
return null;
}
public virtual void WriteCommentLine(ITextOutput output, string comment)

75
ILSpy/MainWindow.xaml.cs

@ -914,27 +914,78 @@ namespace ICSharpCode.ILSpy @@ -914,27 +914,78 @@ namespace ICSharpCode.ILSpy
void SaveCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
var selectedNodes = SelectedNodes.ToList();
if (selectedNodes.Count > 1) {
var assemblyNodes = selectedNodes
.OfType<AssemblyTreeNode>()
.Where(n => n.Language is CSharpLanguage)
.ToList();
if (assemblyNodes.Count == selectedNodes.Count) {
var initialPath = Path.GetDirectoryName(assemblyNodes[0].LoadedAssembly.FileName);
var selectedPath = SolutionWriter.SelectSolutionFile(initialPath);
if (selectedNodes.Count == 1) {
// if there's only one treenode selected
// we will invoke the custom Save logic
if (selectedNodes[0].Save(TextView))
return;
} else if (selectedNodes.All(n => n is AssemblyTreeNode)) {
var initialPath = Path.GetDirectoryName(((AssemblyTreeNode)selectedNodes[0]).LoadedAssembly.FileName);
var selectedPath = SelectSolutionFile(initialPath);
if (!string.IsNullOrEmpty(selectedPath)) {
SolutionWriter.CreateSolution(TextView, selectedPath, assemblyNodes);
var assemblies = selectedNodes.OfType<AssemblyTreeNode>()
.Select(n => n.LoadedAssembly)
.Where(a => a != null).ToArray();
SolutionWriter.CreateSolution(TextView, selectedPath, CurrentLanguage, assemblies);
}
return;
}
}
if (selectedNodes.Count != 1 || !selectedNodes[0].Save(TextView)) {
// Fallback: if nobody was able to handle the request, use default behavior.
// try to save all nodes to disk.
var options = new DecompilationOptions() { FullDecompilation = true };
TextView.SaveToDisk(CurrentLanguage, selectedNodes, options);
}
/// <summary>
/// Shows a File Selection dialog where the user can select the target file for the solution.
/// </summary>
/// <param name="path">The initial path to show in the dialog. If not specified, the 'Documents' directory
/// will be used.</param>
///
/// <returns>The full path of the selected target file, or <c>null</c> if the user canceled.</returns>
string SelectSolutionFile(string path)
{
const string SolutionExtension = ".sln";
const string DefaultSolutionName = "Solution";
if (string.IsNullOrWhiteSpace(path)) {
path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
}
SaveFileDialog dlg = new SaveFileDialog();
dlg.InitialDirectory = path;
dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension);
dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension;
bool targetInvalid;
do {
if (dlg.ShowDialog() != true) {
return null;
}
string selectedPath = Path.GetDirectoryName(dlg.FileName);
try {
targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any();
} catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is System.Security.SecurityException) {
MessageBox.Show(
"The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.",
"Solution directory not accessible",
MessageBoxButton.OK, MessageBoxImage.Error);
targetInvalid = true;
continue;
}
if (targetInvalid) {
MessageBox.Show(
"The directory is not empty. Please select an empty directory.",
"Solution directory not empty",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
} while (targetInvalid);
return dlg.FileName;
}
public void RefreshDecompiledView()

128
ILSpy/SolutionWriter.cs

@ -22,15 +22,12 @@ using System.Collections.Generic; @@ -22,15 +22,12 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
using Microsoft.Win32;
namespace ICSharpCode.ILSpy
{
@ -38,73 +35,23 @@ namespace ICSharpCode.ILSpy @@ -38,73 +35,23 @@ namespace ICSharpCode.ILSpy
/// An utility class that creates a Visual Studio solution containing projects for the
/// decompiled assemblies.
/// </summary>
internal static class SolutionWriter
internal class SolutionWriter
{
private const string SolutionExtension = ".sln";
private const string DefaultSolutionName = "Solution";
/// <summary>
/// Shows a File Selection dialog where the user can select the target file for the solution.
/// </summary>
/// <param name="path">The initial path to show in the dialog. If not specified, the 'Documents' directory
/// will be used.</param>
///
/// <returns>The full path of the selected target file, or <c>null</c> if the user canceled.</returns>
public static string SelectSolutionFile(string path)
{
if (string.IsNullOrWhiteSpace(path)) {
path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
}
SaveFileDialog dlg = new SaveFileDialog();
dlg.InitialDirectory = path;
dlg.FileName = Path.Combine(path, DefaultSolutionName + SolutionExtension);
dlg.Filter = "Visual Studio Solution file|*" + SolutionExtension;
bool targetInvalid;
do {
if (dlg.ShowDialog() != true) {
return null;
}
string selectedPath = Path.GetDirectoryName(dlg.FileName);
try {
targetInvalid = Directory.EnumerateFileSystemEntries(selectedPath).Any();
} catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is SecurityException) {
MessageBox.Show(
"The directory cannot be accessed. Please ensure it exists and you have sufficient rights to access it.",
"Solution directory not accessible",
MessageBoxButton.OK, MessageBoxImage.Error);
targetInvalid = true;
continue;
}
if (targetInvalid) {
MessageBox.Show(
"The directory is not empty. Please select an empty directory.",
"Solution directory not empty",
MessageBoxButton.OK, MessageBoxImage.Warning);
}
} while (targetInvalid);
return dlg.FileName;
}
/// <summary>
/// Creates a Visual Studio solution that contains projects with decompiled code
/// of the specified <paramref name="assemblyNodes"/>. The solution file will be saved
/// of the specified <paramref name="assemblies"/>. The solution file will be saved
/// to the <paramref name="solutionFilePath"/>. The directory of this file must either
/// be empty or not exist.
/// </summary>
/// <param name="textView">A reference to the <see cref="DecompilerTextView"/> instance.</param>
/// <param name="solutionFilePath">The target file path of the solution file.</param>
/// <param name="assemblyNodes">The assembly nodes to decompile.</param>
/// <param name="assemblies">The assembly nodes to decompile.</param>
///
/// <exception cref="ArgumentException">Thrown when <paramref name="solutionFilePath"/> is null,
/// an empty or a whitespace string.</exception>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="textView"/>> or
/// <paramref name="assemblyNodes"/> is null.</exception>
public static void CreateSolution(DecompilerTextView textView, string solutionFilePath, IEnumerable<AssemblyTreeNode> assemblyNodes)
/// <paramref name="assemblies"/> is null.</exception>
public static void CreateSolution(DecompilerTextView textView, string solutionFilePath, Language language, IEnumerable<LoadedAssembly> assemblies)
{
if (textView == null) {
throw new ArgumentNullException(nameof(textView));
@ -114,29 +61,37 @@ namespace ICSharpCode.ILSpy @@ -114,29 +61,37 @@ namespace ICSharpCode.ILSpy
throw new ArgumentException("The solution file path cannot be null or empty.", nameof(solutionFilePath));
}
if (assemblyNodes == null) {
throw new ArgumentNullException(nameof(assemblyNodes));
if (assemblies == null) {
throw new ArgumentNullException(nameof(assemblies));
}
var writer = new SolutionWriter(solutionFilePath);
textView
.RunWithCancellation(ct => CreateSolution(solutionFilePath, assemblyNodes, ct))
.RunWithCancellation(ct => writer.CreateSolution(assemblies, language, ct))
.Then(output => textView.ShowText(output))
.HandleExceptions();
}
private static async Task<AvalonEditTextOutput> CreateSolution(
string solutionFilePath,
IEnumerable<AssemblyTreeNode> assemblyNodes,
CancellationToken ct)
readonly string solutionFilePath;
readonly string solutionDirectory;
readonly ConcurrentBag<ProjectItem> projects;
readonly ConcurrentBag<string> statusOutput;
SolutionWriter(string solutionFilePath)
{
var solutionDirectory = Path.GetDirectoryName(solutionFilePath);
var statusOutput = new ConcurrentBag<string>();
var projects = new ConcurrentBag<ProjectItem>();
this.solutionFilePath = solutionFilePath;
solutionDirectory = Path.GetDirectoryName(solutionFilePath);
statusOutput = new ConcurrentBag<string>();
projects = new ConcurrentBag<ProjectItem>();
}
async Task<AvalonEditTextOutput> CreateSolution(IEnumerable<LoadedAssembly> assemblies, Language language, CancellationToken ct)
{
var result = new AvalonEditTextOutput();
var duplicates = new HashSet<string>();
if (assemblyNodes.Any(n => !duplicates.Add(n.LoadedAssembly.ShortName))) {
if (assemblies.Any(asm => !duplicates.Add(asm.ShortName))) {
result.WriteLine("Duplicate assembly names selected, cannot generate a solution.");
return result;
}
@ -144,7 +99,7 @@ namespace ICSharpCode.ILSpy @@ -144,7 +99,7 @@ namespace ICSharpCode.ILSpy
Stopwatch stopwatch = Stopwatch.StartNew();
try {
await Task.Run(() => Parallel.ForEach(assemblyNodes, n => WriteProject(n, solutionDirectory, statusOutput, projects, ct)))
await Task.Run(() => Parallel.ForEach(assemblies, n => WriteProject(n, language, solutionDirectory, ct)))
.ConfigureAwait(false);
await Task.Run(() => SolutionCreator.WriteSolutionFile(solutionFilePath, projects))
@ -172,13 +127,13 @@ namespace ICSharpCode.ILSpy @@ -172,13 +127,13 @@ namespace ICSharpCode.ILSpy
if (statusOutput.Count == 0) {
result.WriteLine("Successfully decompiled the following assemblies into Visual Studio projects:");
foreach (var item in assemblyNodes.Select(n => n.Text.ToString())) {
foreach (var item in assemblies.Select(n => n.Text.ToString())) {
result.WriteLine(item);
}
result.WriteLine();
if (assemblyNodes.Count() == projects.Count) {
if (assemblies.Count() == projects.Count) {
result.WriteLine("Created the Visual Studio Solution file.");
}
@ -191,17 +146,10 @@ namespace ICSharpCode.ILSpy @@ -191,17 +146,10 @@ namespace ICSharpCode.ILSpy
return result;
}
private static void WriteProject(
AssemblyTreeNode assemblyNode,
string targetDirectory,
ConcurrentBag<string> statusOutput,
ConcurrentBag<ProjectItem> targetContainer,
CancellationToken ct)
void WriteProject(LoadedAssembly loadedAssembly, Language language, string targetDirectory, CancellationToken ct)
{
var loadedAssembly = assemblyNode.LoadedAssembly;
targetDirectory = Path.Combine(targetDirectory, loadedAssembly.ShortName);
string projectFileName = Path.Combine(targetDirectory, loadedAssembly.ShortName + assemblyNode.Language.ProjectFileExtension);
string projectFileName = Path.Combine(targetDirectory, loadedAssembly.ShortName + language.ProjectFileExtension);
if (!Directory.Exists(targetDirectory)) {
try {
@ -218,17 +166,15 @@ namespace ICSharpCode.ILSpy @@ -218,17 +166,15 @@ namespace ICSharpCode.ILSpy
var options = new DecompilationOptions() {
FullDecompilation = true,
CancellationToken = ct,
SaveAsProjectDirectory = targetDirectory };
SaveAsProjectDirectory = targetDirectory
};
if (assemblyNode.Decompile(assemblyNode.Language, projectFileOutput, options) is ProjectId projectInfo) {
targetContainer.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid));
}
}
var projectInfo = language.DecompileAssembly(loadedAssembly, projectFileOutput, options);
if (projectInfo != null) {
projects.Add(new ProjectItem(projectFileName, projectInfo.PlatformName, projectInfo.Guid));
}
catch (OperationCanceledException) {
throw;
}
catch (Exception e) {
} catch (Exception e) when (!(e is OperationCanceledException)) {
statusOutput.Add($"Failed to decompile the assembly '{loadedAssembly.FileName}':{Environment.NewLine}{e}");
}
}

4
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public Action<SharpTreeNode> Select = delegate { };
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, "List: " + assemblyList.ListName);
output.WriteLine();
@ -150,8 +150,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -150,8 +150,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
output.WriteLine();
asm.Decompile(language, output, options);
}
return true;
}
#region Find*Node

4
ILSpy/TreeNodes/AssemblyReferenceTreeNode.cs

@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -79,7 +79,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
var loaded = parentAssembly.LoadedAssembly.LoadedAssemblyReferencesInfo.TryGetInfo(r.FullName, out var info);
if (r.IsWindowsRuntime) {
@ -98,8 +98,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -98,8 +98,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
output.Unindent();
output.WriteLine();
}
return true;
}
}
}

12
ILSpy/TreeNodes/AssemblyTreeNode.cs

@ -240,7 +240,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -240,7 +240,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Recurse;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
void HandleException(Exception ex, string message)
{
@ -259,21 +259,21 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -259,21 +259,21 @@ namespace ICSharpCode.ILSpy.TreeNodes
switch (ex.InnerException) {
case BadImageFormatException badImage:
HandleException(badImage, "This file does not contain a managed assembly.");
return null;
return;
case FileNotFoundException fileNotFound:
HandleException(fileNotFound, "The file was not found.");
return null;
return;
case DirectoryNotFoundException dirNotFound:
HandleException(dirNotFound, "The directory was not found.");
return null;
return;
case PEFileNotSupportedException notSupported:
HandleException(notSupported, notSupported.Message);
return null;
return;
default:
throw;
}
}
return language.DecompileAssembly(LoadedAssembly, output, options);
language.DecompileAssembly(LoadedAssembly, output, options);
}
public override bool Save(DecompilerTextView textView)

3
ILSpy/TreeNodes/BaseTypesEntryNode.cs

@ -97,10 +97,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -97,10 +97,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return false;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true));
return true;
}
IEntity IMemberTreeNode.Member {

4
ILSpy/TreeNodes/BaseTypesTreeNode.cs

@ -69,14 +69,12 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -69,14 +69,12 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
foreach (ILSpyTreeNode child in this.Children) {
child.Decompile(language, output, options);
}
return true;
}
}
}

3
ILSpy/TreeNodes/DerivedTypesEntryNode.cs

@ -90,10 +90,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -90,10 +90,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
e.Handled = BaseTypesEntryNode.ActivateItem(this, type);
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, language.TypeToString(type, includeNamespace: true));
return true;
}
IEntity IMemberTreeNode.Member => type;

3
ILSpy/TreeNodes/DerivedTypesTreeNode.cs

@ -92,10 +92,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -92,10 +92,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return typeRef.GetFullTypeName(referenceMetadata) == typeDef.GetFullTypeName(definitionMetadata);
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
threading.Decompile(language, output, options, EnsureLazyChildren);
return true;
}
}
}

3
ILSpy/TreeNodes/EventTreeNode.cs

@ -67,10 +67,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -67,10 +67,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.DecompileEvent(EventDefinition, output, options);
return true;
}
public override bool IsPublicAPI {

3
ILSpy/TreeNodes/FieldTreeNode.cs

@ -68,10 +68,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -68,10 +68,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.DecompileField(FieldDefinition, output, options);
return true;
}
public override bool IsPublicAPI {

2
ILSpy/TreeNodes/ILSpyTreeNode.cs

@ -57,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -57,7 +57,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden;
}
public abstract object Decompile(Language language, ITextOutput output, DecompilationOptions options);
public abstract void Decompile(Language language, ITextOutput output, DecompilationOptions options);
/// <summary>
/// Used to implement special view logic for some items.

3
ILSpy/TreeNodes/MethodTreeNode.cs

@ -83,10 +83,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -83,10 +83,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.DecompileMethod(MethodDefinition, output, options);
return true;
}
public override FilterResult Filter(FilterSettings settings)

3
ILSpy/TreeNodes/ModuleReferenceTreeNode.cs

@ -72,11 +72,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -72,11 +72,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, moduleName);
language.WriteCommentLine(output, containsMetadata ? "contains metadata" : "contains no metadata");
return true;
}
}
}

3
ILSpy/TreeNodes/NamespaceTreeNode.cs

@ -56,10 +56,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -56,10 +56,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Recurse;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.DecompileNamespace(name, this.Children.OfType<TypeTreeNode>().Select(t => t.TypeDefinition), output, options);
return true;
}
}
}

3
ILSpy/TreeNodes/PropertyTreeNode.cs

@ -74,10 +74,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -74,10 +74,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.DecompileProperty(PropertyDefinition, output, options);
return true;
}
public override bool IsPublicAPI {

3
ILSpy/TreeNodes/ReferenceFolderTreeNode.cs

@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
this.Children.Add(new ModuleReferenceTreeNode(parentAssembly, r, metadata));
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, $"Detected Target-Framework-Id: {parentAssembly.LoadedAssembly.GetTargetFrameworkIdAsync().Result}");
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
@ -86,7 +86,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -86,7 +86,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
output.Unindent();
output.WriteLine();
}
return true;
}
}
}

4
ILSpy/TreeNodes/ResourceListTreeNode.cs

@ -64,15 +64,13 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -64,15 +64,13 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Recurse;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
App.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(EnsureLazyChildren));
foreach (ILSpyTreeNode child in this.Children) {
child.Decompile(language, output, options);
output.WriteLine();
}
return true;
}
}
}

3
ILSpy/TreeNodes/ResourceNodes/ImageListResourceEntryNode.cs

@ -75,10 +75,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -75,10 +75,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
EnsureLazyChildren();
return true;
}
}
}

3
ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs

@ -73,10 +73,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -73,10 +73,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
return result;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, string.Format("{0} = {1}", key, data));
return true;
}
public override bool Save(DecompilerTextView textView)

4
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -67,7 +67,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Hidden;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes));
@ -76,8 +76,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -76,8 +76,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
smartOutput.AddButton(Images.Save, Resources.Save, delegate { Save(MainWindow.Instance.TextView); });
output.WriteLine();
}
return true;
}
public override bool View(DecompilerTextView textView)

4
ILSpy/TreeNodes/ResourceNodes/ResourcesFileTreeNode.cs

@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -141,7 +141,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return true;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
EnsureLazyChildren();
base.Decompile(language, output, options);
@ -168,8 +168,6 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -168,8 +168,6 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
output.WriteLine();
}
return true;
}
internal class SerializedObjectRepresentation

6
ILSpy/TreeNodes/ThreadingSupport.cs

@ -128,9 +128,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -128,9 +128,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Match;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
return false;
}
}
@ -152,9 +151,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -152,9 +151,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
return FilterResult.Match;
}
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
return false;
}
}

3
ILSpy/TreeNodes/TypeTreeNode.cs

@ -103,10 +103,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -103,10 +103,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
public override bool CanExpandRecursively => true;
public override object Decompile(Language language, ITextOutput output, DecompilationOptions options)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.DecompileType(TypeDefinition, output, options);
return true;
}
public override object Icon => GetIcon(TypeDefinition);

Loading…
Cancel
Save