Browse Source

Fix #2807: Detect when a compiler-generated/hidden type needs to be decompiled in WholeProjectDecompiler.

pull/2828/head
Siegfried Pammer 3 years ago
parent
commit
71e4d51786
  1. 130
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

130
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -32,6 +32,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax; @@ -32,6 +32,7 @@ using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Transforms;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Semantics;
using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
@ -206,58 +207,99 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler @@ -206,58 +207,99 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
IEnumerable<(string itemType, string fileName)> WriteCodeFilesInProject(Metadata.PEFile module, IList<PartialTypeInfo> partialTypes, CancellationToken cancellationToken)
{
var metadata = module.Metadata;
var files = module.Metadata.GetTopLevelTypeDefinitions().Where(td => IncludeTypeWhenDecompilingProject(module, td)).GroupBy(
delegate (TypeDefinitionHandle h) {
var type = metadata.GetTypeDefinition(h);
string file = CleanUpFileName(metadata.GetString(type.Name)) + ".cs";
string ns = metadata.GetString(type.Namespace);
if (string.IsNullOrEmpty(ns))
{
return file;
}
else
{
string dir = Settings.UseNestedDirectoriesForNamespaces ? CleanUpPath(ns) : CleanUpDirectoryName(ns);
if (directories.Add(dir))
Directory.CreateDirectory(Path.Combine(TargetDirectory, dir));
return Path.Combine(dir, file);
}
}, StringComparer.OrdinalIgnoreCase).ToList();
var files = module.Metadata.GetTopLevelTypeDefinitions().Where(td => IncludeTypeWhenDecompilingProject(module, td))
.GroupBy(GetFileFileNameForHandle, StringComparer.OrdinalIgnoreCase).ToList();
var progressReporter = ProgressIndicator;
var progress = new DecompilationProgress { TotalUnits = files.Count, Title = "Exporting project..." };
DecompilerTypeSystem ts = new DecompilerTypeSystem(module, AssemblyResolver, Settings);
Parallel.ForEach(
Partitioner.Create(files, loadBalance: true),
new ParallelOptions {
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
CancellationToken = cancellationToken
},
delegate (IGrouping<string, TypeDefinitionHandle> file) {
using (StreamWriter w = new StreamWriter(Path.Combine(TargetDirectory, file.Key)))
{
try
var workList = new HashSet<TypeDefinitionHandle>();
var processedTypes = new HashSet<TypeDefinitionHandle>();
ProcessFiles(files);
while (workList.Count > 0)
{
var additionalFiles = workList
.GroupBy(GetFileFileNameForHandle, StringComparer.OrdinalIgnoreCase).ToList();
workList.Clear();
ProcessFiles(additionalFiles);
files.AddRange(additionalFiles);
progress.TotalUnits = files.Count;
}
return files.Select(f => ("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken));
string GetFileFileNameForHandle(TypeDefinitionHandle h)
{
var type = metadata.GetTypeDefinition(h);
string file = CleanUpFileName(metadata.GetString(type.Name)) + ".cs";
string ns = metadata.GetString(type.Namespace);
if (string.IsNullOrEmpty(ns))
{
return file;
}
else
{
string dir = Settings.UseNestedDirectoriesForNamespaces ? CleanUpPath(ns) : CleanUpDirectoryName(ns);
if (directories.Add(dir))
Directory.CreateDirectory(Path.Combine(TargetDirectory, dir));
return Path.Combine(dir, file);
}
}
void ProcessFiles(List<IGrouping<string, TypeDefinitionHandle>> files)
{
processedTypes.AddRange(files.SelectMany(f => f));
Parallel.ForEach(
Partitioner.Create(files, loadBalance: true),
new ParallelOptions {
MaxDegreeOfParallelism = this.MaxDegreeOfParallelism,
CancellationToken = cancellationToken
},
delegate (IGrouping<string, TypeDefinitionHandle> file) {
using (StreamWriter w = new StreamWriter(Path.Combine(TargetDirectory, file.Key)))
{
CSharpDecompiler decompiler = CreateDecompiler(ts);
try
{
CSharpDecompiler decompiler = CreateDecompiler(ts);
foreach (var partialType in partialTypes)
foreach (var partialType in partialTypes)
{
decompiler.AddPartialTypeDefinition(partialType);
}
decompiler.CancellationToken = cancellationToken;
var declaredTypes = file.ToArray();
var syntaxTree = decompiler.DecompileTypes(declaredTypes);
foreach (var node in syntaxTree.Descendants)
{
var td = (node.GetResolveResult() as TypeResolveResult)?.Type.GetDefinition();
if (td?.ParentModule != ts.MainModule)
continue;
while (td?.DeclaringTypeDefinition != null)
{
td = td.DeclaringTypeDefinition;
}
if (td != null && td.MetadataToken is { IsNil: false } token && !processedTypes.Contains((TypeDefinitionHandle)token))
{
lock (workList)
{
workList.Add((TypeDefinitionHandle)token);
}
}
}
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
}
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))
{
decompiler.AddPartialTypeDefinition(partialType);
throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException);
}
decompiler.CancellationToken = cancellationToken;
var syntaxTree = decompiler.DecompileTypes(file.ToArray());
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
}
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))
{
throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException);
}
}
progress.Status = file.Key;
Interlocked.Increment(ref progress.UnitsCompleted);
progressReporter?.Report(progress);
});
return files.Select(f => ("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken));
progress.Status = file.Key;
Interlocked.Increment(ref progress.UnitsCompleted);
progressReporter?.Report(progress);
});
}
}
#endregion

Loading…
Cancel
Save