|
|
|
|
@ -17,11 +17,15 @@
@@ -17,11 +17,15 @@
|
|
|
|
|
// DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
using System; |
|
|
|
|
using System.Collections; |
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
using System.ComponentModel.Composition; |
|
|
|
|
using System.IO; |
|
|
|
|
using System.Linq; |
|
|
|
|
using System.Resources; |
|
|
|
|
using System.Text; |
|
|
|
|
using System.Threading.Tasks; |
|
|
|
|
using System.Xml; |
|
|
|
|
|
|
|
|
|
using ICSharpCode.Decompiler; |
|
|
|
|
using ICSharpCode.Decompiler.Ast; |
|
|
|
|
@ -67,10 +71,10 @@ namespace ICSharpCode.ILSpy.VB
@@ -67,10 +71,10 @@ namespace ICSharpCode.ILSpy.VB
|
|
|
|
|
public override void DecompileAssembly(LoadedAssembly assembly, ITextOutput output, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
if (options.FullDecompilation && options.SaveAsProjectDirectory != null) { |
|
|
|
|
// HashSet<string> directories = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
// var files = WriteCodeFilesInProject(assembly.AssemblyDefinition, options, directories).ToList();
|
|
|
|
|
// files.AddRange(WriteResourceFilesInProject(assembly, options, directories));
|
|
|
|
|
// WriteProjectFile(new TextOutputWriter(output), files, assembly.AssemblyDefinition.MainModule);
|
|
|
|
|
HashSet<string> directories = new HashSet<string>(StringComparer.OrdinalIgnoreCase); |
|
|
|
|
var files = WriteCodeFilesInProject(assembly.AssemblyDefinition, options, directories).ToList(); |
|
|
|
|
files.AddRange(WriteResourceFilesInProject(assembly, options, directories)); |
|
|
|
|
WriteProjectFile(new TextOutputWriter(output), files, assembly.AssemblyDefinition.MainModule); |
|
|
|
|
} else { |
|
|
|
|
base.DecompileAssembly(assembly, output, options); |
|
|
|
|
output.WriteLine(); |
|
|
|
|
@ -123,6 +127,253 @@ namespace ICSharpCode.ILSpy.VB
@@ -123,6 +127,253 @@ namespace ICSharpCode.ILSpy.VB
|
|
|
|
|
OnDecompilationFinished(null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#region WriteProjectFile
|
|
|
|
|
void WriteProjectFile(TextWriter writer, IEnumerable<Tuple<string, string>> files, ModuleDefinition module) |
|
|
|
|
{ |
|
|
|
|
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; |
|
|
|
|
string platformName; |
|
|
|
|
switch (module.Architecture) { |
|
|
|
|
case TargetArchitecture.I386: |
|
|
|
|
if ((module.Attributes & ModuleAttributes.Required32Bit) == ModuleAttributes.Required32Bit) |
|
|
|
|
platformName = "x86"; |
|
|
|
|
else |
|
|
|
|
platformName = "AnyCPU"; |
|
|
|
|
break; |
|
|
|
|
case TargetArchitecture.AMD64: |
|
|
|
|
platformName = "x64"; |
|
|
|
|
break; |
|
|
|
|
case TargetArchitecture.IA64: |
|
|
|
|
platformName = "Itanium"; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new NotSupportedException("Invalid value for TargetArchitecture"); |
|
|
|
|
} |
|
|
|
|
using (XmlTextWriter w = new XmlTextWriter(writer)) { |
|
|
|
|
w.Formatting = Formatting.Indented; |
|
|
|
|
w.WriteStartDocument(); |
|
|
|
|
w.WriteStartElement("Project", ns); |
|
|
|
|
w.WriteAttributeString("ToolsVersion", "4.0"); |
|
|
|
|
w.WriteAttributeString("DefaultTargets", "Build"); |
|
|
|
|
|
|
|
|
|
w.WriteStartElement("PropertyGroup"); |
|
|
|
|
w.WriteElementString("ProjectGuid", Guid.NewGuid().ToString().ToUpperInvariant()); |
|
|
|
|
|
|
|
|
|
w.WriteStartElement("Configuration"); |
|
|
|
|
w.WriteAttributeString("Condition", " '$(Configuration)' == '' "); |
|
|
|
|
w.WriteValue("Debug"); |
|
|
|
|
w.WriteEndElement(); // </Configuration>
|
|
|
|
|
|
|
|
|
|
w.WriteStartElement("Platform"); |
|
|
|
|
w.WriteAttributeString("Condition", " '$(Platform)' == '' "); |
|
|
|
|
w.WriteValue(platformName); |
|
|
|
|
w.WriteEndElement(); // </Platform>
|
|
|
|
|
|
|
|
|
|
switch (module.Kind) { |
|
|
|
|
case ModuleKind.Windows: |
|
|
|
|
w.WriteElementString("OutputType", "WinExe"); |
|
|
|
|
break; |
|
|
|
|
case ModuleKind.Console: |
|
|
|
|
w.WriteElementString("OutputType", "Exe"); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
w.WriteElementString("OutputType", "Library"); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
w.WriteElementString("AssemblyName", module.Assembly.Name.Name); |
|
|
|
|
switch (module.Runtime) { |
|
|
|
|
case TargetRuntime.Net_1_0: |
|
|
|
|
w.WriteElementString("TargetFrameworkVersion", "v1.0"); |
|
|
|
|
break; |
|
|
|
|
case TargetRuntime.Net_1_1: |
|
|
|
|
w.WriteElementString("TargetFrameworkVersion", "v1.1"); |
|
|
|
|
break; |
|
|
|
|
case TargetRuntime.Net_2_0: |
|
|
|
|
w.WriteElementString("TargetFrameworkVersion", "v2.0"); |
|
|
|
|
// TODO: Detect when .NET 3.0/3.5 is required
|
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
w.WriteElementString("TargetFrameworkVersion", "v4.0"); |
|
|
|
|
// TODO: Detect TargetFrameworkProfile
|
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
w.WriteElementString("WarningLevel", "4"); |
|
|
|
|
|
|
|
|
|
w.WriteEndElement(); // </PropertyGroup>
|
|
|
|
|
|
|
|
|
|
w.WriteStartElement("PropertyGroup"); // platform-specific
|
|
|
|
|
w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' "); |
|
|
|
|
w.WriteElementString("PlatformTarget", platformName); |
|
|
|
|
w.WriteEndElement(); // </PropertyGroup> (platform-specific)
|
|
|
|
|
|
|
|
|
|
w.WriteStartElement("PropertyGroup"); // Debug
|
|
|
|
|
w.WriteAttributeString("Condition", " '$(Configuration)' == 'Debug' "); |
|
|
|
|
w.WriteElementString("OutputPath", "bin\\Debug\\"); |
|
|
|
|
w.WriteElementString("DebugSymbols", "true"); |
|
|
|
|
w.WriteElementString("DebugType", "full"); |
|
|
|
|
w.WriteElementString("Optimize", "false"); |
|
|
|
|
w.WriteEndElement(); // </PropertyGroup> (Debug)
|
|
|
|
|
|
|
|
|
|
w.WriteStartElement("PropertyGroup"); // Release
|
|
|
|
|
w.WriteAttributeString("Condition", " '$(Configuration)' == 'Release' "); |
|
|
|
|
w.WriteElementString("OutputPath", "bin\\Release\\"); |
|
|
|
|
w.WriteElementString("DebugSymbols", "true"); |
|
|
|
|
w.WriteElementString("DebugType", "pdbonly"); |
|
|
|
|
w.WriteElementString("Optimize", "true"); |
|
|
|
|
w.WriteEndElement(); // </PropertyGroup> (Release)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w.WriteStartElement("ItemGroup"); // References
|
|
|
|
|
foreach (AssemblyNameReference r in module.AssemblyReferences) { |
|
|
|
|
if (r.Name != "mscorlib") { |
|
|
|
|
w.WriteStartElement("Reference"); |
|
|
|
|
w.WriteAttributeString("Include", r.Name); |
|
|
|
|
// TODO: RequiredTargetFramework
|
|
|
|
|
w.WriteEndElement(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
w.WriteEndElement(); // </ItemGroup> (References)
|
|
|
|
|
|
|
|
|
|
foreach (IGrouping<string, string> gr in (from f in files group f.Item2 by f.Item1 into g orderby g.Key select g)) { |
|
|
|
|
w.WriteStartElement("ItemGroup"); |
|
|
|
|
foreach (string file in gr.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)) { |
|
|
|
|
w.WriteStartElement(gr.Key); |
|
|
|
|
w.WriteAttributeString("Include", file); |
|
|
|
|
w.WriteEndElement(); |
|
|
|
|
} |
|
|
|
|
w.WriteEndElement(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
w.WriteStartElement("Import"); |
|
|
|
|
w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); // TODO change to VB!
|
|
|
|
|
w.WriteEndElement(); |
|
|
|
|
|
|
|
|
|
w.WriteEndDocument(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region WriteCodeFilesInProject
|
|
|
|
|
bool IncludeTypeWhenDecompilingProject(TypeDefinition type, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
if (type.Name == "<Module>" || AstBuilder.MemberIsHidden(type, options.DecompilerSettings)) |
|
|
|
|
return false; |
|
|
|
|
if (type.Namespace == "XamlGeneratedNamespace" && type.Name == "GeneratedInternalTypeHelper") |
|
|
|
|
return false; |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IEnumerable<Tuple<string, string>> WriteCodeFilesInProject(AssemblyDefinition assembly, DecompilationOptions options, HashSet<string> directories) |
|
|
|
|
{ |
|
|
|
|
var files = assembly.MainModule.Types.Where(t => IncludeTypeWhenDecompilingProject(t, options)).GroupBy( |
|
|
|
|
delegate(TypeDefinition type) { |
|
|
|
|
string file = TextView.DecompilerTextView.CleanUpName(type.Name) + this.FileExtension; |
|
|
|
|
if (string.IsNullOrEmpty(type.Namespace)) { |
|
|
|
|
return file; |
|
|
|
|
} else { |
|
|
|
|
string dir = TextView.DecompilerTextView.CleanUpName(type.Namespace); |
|
|
|
|
if (directories.Add(dir)) |
|
|
|
|
Directory.CreateDirectory(Path.Combine(options.SaveAsProjectDirectory, dir)); |
|
|
|
|
return Path.Combine(dir, file); |
|
|
|
|
} |
|
|
|
|
}, StringComparer.OrdinalIgnoreCase).ToList(); |
|
|
|
|
AstMethodBodyBuilder.ClearUnhandledOpcodes(); |
|
|
|
|
Parallel.ForEach( |
|
|
|
|
files, |
|
|
|
|
new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, |
|
|
|
|
delegate(IGrouping<string, TypeDefinition> file) { |
|
|
|
|
using (StreamWriter w = new StreamWriter(Path.Combine(options.SaveAsProjectDirectory, file.Key))) { |
|
|
|
|
AstBuilder codeDomBuilder = CreateAstBuilder(options, currentModule: assembly.MainModule); |
|
|
|
|
foreach (TypeDefinition type in file) { |
|
|
|
|
codeDomBuilder.AddType(type); |
|
|
|
|
} |
|
|
|
|
codeDomBuilder.RunTransformations(transformAbortCondition); |
|
|
|
|
RunTransformsAndGenerateCode(codeDomBuilder, new PlainTextOutput(w), options); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
AstMethodBodyBuilder.PrintNumberOfUnhandledOpcodes(); |
|
|
|
|
return files.Select(f => Tuple.Create("Compile", f.Key)); |
|
|
|
|
} |
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region WriteResourceFilesInProject
|
|
|
|
|
IEnumerable<Tuple<string, string>> WriteResourceFilesInProject(LoadedAssembly assembly, DecompilationOptions options, HashSet<string> directories) |
|
|
|
|
{ |
|
|
|
|
AppDomain bamlDecompilerAppDomain = null; |
|
|
|
|
try { |
|
|
|
|
foreach (EmbeddedResource r in assembly.AssemblyDefinition.MainModule.Resources.OfType<EmbeddedResource>()) { |
|
|
|
|
string fileName; |
|
|
|
|
Stream s = r.GetResourceStream(); |
|
|
|
|
s.Position = 0; |
|
|
|
|
if (r.Name.EndsWith(".g.resources", StringComparison.OrdinalIgnoreCase)) { |
|
|
|
|
IEnumerable<DictionaryEntry> rs = null; |
|
|
|
|
try { |
|
|
|
|
rs = new ResourceSet(s).Cast<DictionaryEntry>(); |
|
|
|
|
} |
|
|
|
|
catch (ArgumentException) { |
|
|
|
|
} |
|
|
|
|
if (rs != null && rs.All(e => e.Value is Stream)) { |
|
|
|
|
foreach (var pair in rs) { |
|
|
|
|
fileName = Path.Combine(((string)pair.Key).Split('/').Select(p => TextView.DecompilerTextView.CleanUpName(p)).ToArray()); |
|
|
|
|
string dirName = Path.GetDirectoryName(fileName); |
|
|
|
|
if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) { |
|
|
|
|
Directory.CreateDirectory(Path.Combine(options.SaveAsProjectDirectory, dirName)); |
|
|
|
|
} |
|
|
|
|
Stream entryStream = (Stream)pair.Value; |
|
|
|
|
entryStream.Position = 0; |
|
|
|
|
if (fileName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) { |
|
|
|
|
MemoryStream ms = new MemoryStream(); |
|
|
|
|
entryStream.CopyTo(ms); |
|
|
|
|
// TODO implement extension point
|
|
|
|
|
// var decompiler = Baml.BamlResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assembly.FileName);
|
|
|
|
|
// string xaml = null;
|
|
|
|
|
// try {
|
|
|
|
|
// xaml = decompiler.DecompileBaml(ms, assembly.FileName, new ConnectMethodDecompiler(assembly), new AssemblyResolver(assembly));
|
|
|
|
|
// }
|
|
|
|
|
// catch (XamlXmlWriterException) { } // ignore XAML writer exceptions
|
|
|
|
|
// if (xaml != null) {
|
|
|
|
|
// File.WriteAllText(Path.Combine(options.SaveAsProjectDirectory, Path.ChangeExtension(fileName, ".xaml")), xaml);
|
|
|
|
|
// yield return Tuple.Create("Page", Path.ChangeExtension(fileName, ".xaml"));
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
} |
|
|
|
|
using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) { |
|
|
|
|
entryStream.CopyTo(fs); |
|
|
|
|
} |
|
|
|
|
yield return Tuple.Create("Resource", fileName); |
|
|
|
|
} |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
fileName = GetFileNameForResource(r.Name, directories); |
|
|
|
|
using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) { |
|
|
|
|
s.CopyTo(fs); |
|
|
|
|
} |
|
|
|
|
yield return Tuple.Create("EmbeddedResource", fileName); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
finally { |
|
|
|
|
if (bamlDecompilerAppDomain != null) |
|
|
|
|
AppDomain.Unload(bamlDecompilerAppDomain); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
string GetFileNameForResource(string fullName, HashSet<string> directories) |
|
|
|
|
{ |
|
|
|
|
string[] splitName = fullName.Split('.'); |
|
|
|
|
string fileName = TextView.DecompilerTextView.CleanUpName(fullName); |
|
|
|
|
for (int i = splitName.Length - 1; i > 0; i--) { |
|
|
|
|
string ns = string.Join(".", splitName, 0, i); |
|
|
|
|
if (directories.Contains(ns)) { |
|
|
|
|
string name = string.Join(".", splitName, i, splitName.Length - i); |
|
|
|
|
fileName = Path.Combine(ns, TextView.DecompilerTextView.CleanUpName(name)); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return fileName; |
|
|
|
|
} |
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
public override void DecompileMethod(MethodDefinition method, ITextOutput output, DecompilationOptions options) |
|
|
|
|
{ |
|
|
|
|
WriteCommentLine(output, TypeToString(method.DeclaringType, includeNamespace: true)); |
|
|
|
|
|