|
|
|
@ -13,6 +13,7 @@ using ICSharpCode.Core;
@@ -13,6 +13,7 @@ using ICSharpCode.Core;
|
|
|
|
|
using ICSharpCode.Decompiler; |
|
|
|
|
using ICSharpCode.Decompiler.Ast; |
|
|
|
|
using ICSharpCode.NRefactory.TypeSystem; |
|
|
|
|
using ICSharpCode.SharpDevelop; |
|
|
|
|
using ICSharpCode.SharpDevelop.Parser; |
|
|
|
|
using Mono.Cecil; |
|
|
|
|
|
|
|
|
@ -23,45 +24,46 @@ namespace ICSharpCode.ILSpyAddIn
@@ -23,45 +24,46 @@ namespace ICSharpCode.ILSpyAddIn
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static class ILSpyDecompilerService |
|
|
|
|
{ |
|
|
|
|
class AssemblyCacheInfo |
|
|
|
|
class ModuleCacheInfo |
|
|
|
|
{ |
|
|
|
|
public readonly DateTime LastUpdateTime; |
|
|
|
|
public readonly WeakReference<AssemblyDefinition> Assembly; |
|
|
|
|
public readonly WeakReference<ModuleDefinition> Module; |
|
|
|
|
|
|
|
|
|
public AssemblyCacheInfo(DateTime lastUpdateTime, AssemblyDefinition assembly) |
|
|
|
|
public ModuleCacheInfo(DateTime lastUpdateTime, ModuleDefinition assembly) |
|
|
|
|
{ |
|
|
|
|
if (assembly == null) |
|
|
|
|
throw new ArgumentNullException("assembly"); |
|
|
|
|
this.LastUpdateTime = lastUpdateTime; |
|
|
|
|
this.Assembly = new WeakReference<AssemblyDefinition>(assembly); |
|
|
|
|
this.Module = new WeakReference<ModuleDefinition>(assembly); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static readonly Dictionary<FileName, AssemblyCacheInfo> assemblyCache = new Dictionary<FileName, AssemblyCacheInfo>(); |
|
|
|
|
static readonly Dictionary<FileName, ModuleCacheInfo> moduleCache = new Dictionary<FileName, ModuleCacheInfo>(); |
|
|
|
|
|
|
|
|
|
static AssemblyDefinition GetAssemblyDefinitionFromCache(FileName file, ReaderParameters parameters = null) |
|
|
|
|
static ModuleDefinition GetModuleDefinitionFromCache(FileName file) |
|
|
|
|
{ |
|
|
|
|
if (file == null) return null; |
|
|
|
|
if (parameters == null) parameters = new ReaderParameters(); |
|
|
|
|
ReaderParameters parameters = new ReaderParameters(); |
|
|
|
|
var resolver = new ILSpyAssemblyResolver(file); |
|
|
|
|
var lastUpdateTime = File.GetLastWriteTimeUtc(file); |
|
|
|
|
lock (assemblyCache) { |
|
|
|
|
AssemblyCacheInfo info; |
|
|
|
|
AssemblyDefinition asm; |
|
|
|
|
if (!assemblyCache.TryGetValue(file, out info)) { |
|
|
|
|
asm = AssemblyDefinition.ReadAssembly(file, parameters); |
|
|
|
|
assemblyCache.Add(file, new AssemblyCacheInfo(lastUpdateTime, asm)); |
|
|
|
|
return asm; |
|
|
|
|
lock (moduleCache) { |
|
|
|
|
ModuleCacheInfo info; |
|
|
|
|
ModuleDefinition module; |
|
|
|
|
if (!moduleCache.TryGetValue(file, out info)) { |
|
|
|
|
module = ModuleDefinition.ReadModule(file, parameters); |
|
|
|
|
moduleCache.Add(file, new ModuleCacheInfo(lastUpdateTime, module)); |
|
|
|
|
return module; |
|
|
|
|
} else if (info.LastUpdateTime < lastUpdateTime) { |
|
|
|
|
assemblyCache.Remove(file); |
|
|
|
|
asm = AssemblyDefinition.ReadAssembly(file, parameters); |
|
|
|
|
assemblyCache.Add(file, new AssemblyCacheInfo(lastUpdateTime, asm)); |
|
|
|
|
return asm; |
|
|
|
|
moduleCache.Remove(file); |
|
|
|
|
module = ModuleDefinition.ReadModule(file, parameters); |
|
|
|
|
moduleCache.Add(file, new ModuleCacheInfo(lastUpdateTime, module)); |
|
|
|
|
return module; |
|
|
|
|
} else { |
|
|
|
|
if (info.Assembly.TryGetTarget(out asm)) |
|
|
|
|
return asm; |
|
|
|
|
asm = AssemblyDefinition.ReadAssembly(file, parameters); |
|
|
|
|
info.Assembly.SetTarget(asm); |
|
|
|
|
return asm; |
|
|
|
|
if (info.Module.TryGetTarget(out module)) |
|
|
|
|
return module; |
|
|
|
|
module = ModuleDefinition.ReadModule(file, parameters); |
|
|
|
|
info.Module.SetTarget(module); |
|
|
|
|
return module; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -77,14 +79,19 @@ namespace ICSharpCode.ILSpyAddIn
@@ -77,14 +79,19 @@ namespace ICSharpCode.ILSpyAddIn
|
|
|
|
|
{ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Used to remember the referenced assemblies for the WeakReference cache policy.
|
|
|
|
|
/// </summary>
|
|
|
|
|
readonly ISet<AssemblyDefinition> resolvedAssemblies = new HashSet<AssemblyDefinition>(); |
|
|
|
|
|
|
|
|
|
AssemblyDefinition Resolve(DomAssemblyName name, ReaderParameters parameters) |
|
|
|
|
{ |
|
|
|
|
var assemblyDefinition = GetAssemblyDefinitionFromCache(FindAssembly(name), parameters); |
|
|
|
|
if (assemblyDefinition != null) |
|
|
|
|
resolvedAssemblies.Add(assemblyDefinition); |
|
|
|
|
return assemblyDefinition; |
|
|
|
|
var moduleDefinition = GetModuleDefinitionFromCache(FindAssembly(name)); |
|
|
|
|
if (moduleDefinition != null) { |
|
|
|
|
resolvedAssemblies.Add(moduleDefinition.Assembly); |
|
|
|
|
return moduleDefinition.Assembly; |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public AssemblyDefinition Resolve(AssemblyNameReference name) |
|
|
|
@ -108,31 +115,24 @@ namespace ICSharpCode.ILSpyAddIn
@@ -108,31 +115,24 @@ namespace ICSharpCode.ILSpyAddIn
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static ILSpyUnresolvedFile DecompileType(DecompiledTypeReference name) |
|
|
|
|
public static ILSpyFullParseInformation DecompileType(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) |
|
|
|
|
{ |
|
|
|
|
if (name == null) |
|
|
|
|
throw new ArgumentNullException("entity"); |
|
|
|
|
return DoDecompile(name); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static async Task<ILSpyUnresolvedFile> DecompileTypeAsync(DecompiledTypeReference name, CancellationToken cancellationToken) |
|
|
|
|
{ |
|
|
|
|
return await Task.Run( |
|
|
|
|
delegate() { return DoDecompile(name, cancellationToken); }, |
|
|
|
|
cancellationToken); |
|
|
|
|
throw new ArgumentNullException("name"); |
|
|
|
|
var astBuilder = CreateAstBuilder(name, cancellationToken); |
|
|
|
|
return new ILSpyFullParseInformation(ILSpyUnresolvedFile.Create(name, astBuilder), null, astBuilder.SyntaxTree); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static AstBuilder CreateAstBuilder(DecompiledTypeReference name, out AssemblyDefinition[] neededAssemblies, CancellationToken cancellationToken = default(CancellationToken)) |
|
|
|
|
static AstBuilder CreateAstBuilder(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) |
|
|
|
|
{ |
|
|
|
|
ReaderParameters readerParameters = new ReaderParameters(); |
|
|
|
|
// Use new assembly resolver instance so that the AssemblyDefinitions
|
|
|
|
|
// can be garbage-collected once the code is decompiled.
|
|
|
|
|
var resolver = new ILSpyAssemblyResolver(name.AssemblyFile); |
|
|
|
|
readerParameters.AssemblyResolver = resolver; |
|
|
|
|
AssemblyDefinition asm = GetAssemblyDefinitionFromCache(name.AssemblyFile, readerParameters); |
|
|
|
|
if (asm == null) |
|
|
|
|
ModuleDefinition module = GetModuleDefinitionFromCache(name.AssemblyFile); |
|
|
|
|
if (module == null) |
|
|
|
|
throw new InvalidOperationException("Could not find assembly file"); |
|
|
|
|
ModuleDefinition module = asm.MainModule; |
|
|
|
|
TypeDefinition typeDefinition = module.GetType(name.Type.ReflectionName); |
|
|
|
|
if (typeDefinition == null) |
|
|
|
|
throw new InvalidOperationException("Could not find type"); |
|
|
|
@ -140,30 +140,21 @@ namespace ICSharpCode.ILSpyAddIn
@@ -140,30 +140,21 @@ namespace ICSharpCode.ILSpyAddIn
|
|
|
|
|
context.CancellationToken = cancellationToken; |
|
|
|
|
AstBuilder astBuilder = new AstBuilder(context); |
|
|
|
|
astBuilder.AddType(typeDefinition); |
|
|
|
|
neededAssemblies = resolver.ResolvedAssemblies.ToArray(); |
|
|
|
|
return astBuilder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static ILSpyUnresolvedFile DoDecompile(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) |
|
|
|
|
{ |
|
|
|
|
AssemblyDefinition[] assemblies; |
|
|
|
|
return ILSpyUnresolvedFile.Create(name, CreateAstBuilder(name, out assemblies, cancellationToken)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static ILSpyFullParseInformation ParseDecompiledType(DecompiledTypeReference name, CancellationToken cancellationToken = default(CancellationToken)) |
|
|
|
|
{ |
|
|
|
|
AssemblyDefinition[] assemblies; |
|
|
|
|
var astBuilder = CreateAstBuilder(name, out assemblies, cancellationToken); |
|
|
|
|
return new ILSpyFullParseInformation(ILSpyUnresolvedFile.Create(name, astBuilder), null, astBuilder.SyntaxTree, assemblies); |
|
|
|
|
return ILSpyUnresolvedFile.Create(name, CreateAstBuilder(name, cancellationToken)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public class DecompiledTypeReference : IEquatable<DecompiledTypeReference> |
|
|
|
|
{ |
|
|
|
|
public FileName AssemblyFile { get; private set; } |
|
|
|
|
public FullTypeName Type { get; private set; } |
|
|
|
|
public TopLevelTypeName Type { get; private set; } |
|
|
|
|
|
|
|
|
|
public DecompiledTypeReference(FileName assemblyFile, FullTypeName type) |
|
|
|
|
public DecompiledTypeReference(FileName assemblyFile, TopLevelTypeName type) |
|
|
|
|
{ |
|
|
|
|
this.AssemblyFile = assemblyFile; |
|
|
|
|
this.Type = type; |
|
|
|
@ -185,7 +176,16 @@ namespace ICSharpCode.ILSpyAddIn
@@ -185,7 +176,16 @@ namespace ICSharpCode.ILSpyAddIn
|
|
|
|
|
asm = match.Groups[1].Value; |
|
|
|
|
typeName = UnescapeTypeName(match.Groups[2].Value); |
|
|
|
|
|
|
|
|
|
return new DecompiledTypeReference(new FileName(asm), new FullTypeName(typeName)); |
|
|
|
|
return new DecompiledTypeReference(new FileName(asm), new TopLevelTypeName(typeName)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static DecompiledTypeReference FromTypeDefinition(ITypeDefinition definition) |
|
|
|
|
{ |
|
|
|
|
FileName assemblyLocation = definition.ParentAssembly.GetRuntimeAssemblyLocation(); |
|
|
|
|
if (assemblyLocation != null && SD.FileSystem.FileExists(assemblyLocation)) { |
|
|
|
|
return new DecompiledTypeReference(assemblyLocation, definition.FullTypeName.TopLevelTypeName); |
|
|
|
|
} |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public static string EscapeTypeName(string typeName) |
|
|
|
@ -204,9 +204,7 @@ namespace ICSharpCode.ILSpyAddIn
@@ -204,9 +204,7 @@ namespace ICSharpCode.ILSpyAddIn
|
|
|
|
|
{ |
|
|
|
|
if (typeName == null) |
|
|
|
|
throw new ArgumentNullException("typeName"); |
|
|
|
|
foreach (var ch in Path.GetInvalidFileNameChars().Concat(new[] { '_' })) { |
|
|
|
|
typeName = unescapeRegex.Replace(typeName, m => ((char)int.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber)).ToString()); |
|
|
|
|
} |
|
|
|
|
typeName = unescapeRegex.Replace(typeName, m => ((char)int.Parse(m.Groups[1].Value, System.Globalization.NumberStyles.HexNumber)).ToString()); |
|
|
|
|
return typeName; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|