mirror of https://github.com/icsharpcode/ILSpy.git
6 changed files with 239 additions and 6 deletions
@ -0,0 +1,148 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
using Mono.Cecil; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.Ast |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// ITypeResolveContext implementation that lazily loads types from Cecil.
|
||||||
|
/// </summary>
|
||||||
|
public class CecilTypeResolveContext : ISynchronizedTypeResolveContext, IProjectContent |
||||||
|
{ |
||||||
|
readonly ModuleDefinition module; |
||||||
|
readonly string[] namespaces; |
||||||
|
readonly CecilLoader loader; |
||||||
|
Dictionary<TypeDefinition, WeakReference> dict = new Dictionary<TypeDefinition, WeakReference>(); |
||||||
|
int countUntilNextCleanup = 4; |
||||||
|
|
||||||
|
public CecilTypeResolveContext(ModuleDefinition module) |
||||||
|
{ |
||||||
|
this.loader = new CecilLoader(); |
||||||
|
this.loader.IncludeInternalMembers = true; |
||||||
|
this.module = module; |
||||||
|
this.namespaces = module.Types.Select(t => t.Namespace).Distinct().ToArray(); |
||||||
|
|
||||||
|
List<IAttribute> assemblyAttributes = new List<IAttribute>(); |
||||||
|
foreach (var attr in module.Assembly.CustomAttributes) { |
||||||
|
assemblyAttributes.Add(loader.ReadAttribute(attr)); |
||||||
|
} |
||||||
|
this.AssemblyAttributes = assemblyAttributes.AsReadOnly(); |
||||||
|
} |
||||||
|
|
||||||
|
ITypeDefinition GetClass(TypeDefinition cecilType) |
||||||
|
{ |
||||||
|
lock (dict) { |
||||||
|
WeakReference wr; |
||||||
|
ITypeDefinition type; |
||||||
|
if (dict.TryGetValue(cecilType, out wr)) { |
||||||
|
type = (ITypeDefinition)wr.Target; |
||||||
|
} else { |
||||||
|
wr = null; |
||||||
|
type = null; |
||||||
|
} |
||||||
|
if (type == null) { |
||||||
|
type = loader.LoadType(cecilType, this); |
||||||
|
} |
||||||
|
if (wr == null) { |
||||||
|
if (--countUntilNextCleanup <= 0) |
||||||
|
CleanupDict(); |
||||||
|
wr = new WeakReference(type); |
||||||
|
dict.Add(cecilType, wr); |
||||||
|
} else { |
||||||
|
wr.Target = type; |
||||||
|
} |
||||||
|
return type; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void CleanupDict() |
||||||
|
{ |
||||||
|
List<TypeDefinition> deletedKeys = new List<TypeDefinition>(); |
||||||
|
foreach (var pair in dict) { |
||||||
|
if (!pair.Value.IsAlive) { |
||||||
|
deletedKeys.Add(pair.Key); |
||||||
|
} |
||||||
|
} |
||||||
|
foreach (var key in deletedKeys) { |
||||||
|
dict.Remove(key); |
||||||
|
} |
||||||
|
countUntilNextCleanup = dict.Count + 4; |
||||||
|
} |
||||||
|
|
||||||
|
public IList<IAttribute> AssemblyAttributes { get; private set; } |
||||||
|
|
||||||
|
public ITypeDefinition GetClass(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer) |
||||||
|
{ |
||||||
|
if (typeParameterCount > 0) |
||||||
|
name = name + "`" + typeParameterCount.ToString(); |
||||||
|
if (nameComparer == StringComparer.Ordinal) { |
||||||
|
TypeDefinition cecilType = module.GetType(nameSpace, name); |
||||||
|
if (cecilType != null) |
||||||
|
return GetClass(cecilType); |
||||||
|
else |
||||||
|
return null; |
||||||
|
} |
||||||
|
foreach (TypeDefinition cecilType in module.Types) { |
||||||
|
if (nameComparer.Equals(name, cecilType.Name) |
||||||
|
&& nameComparer.Equals(nameSpace, cecilType.Namespace) |
||||||
|
&& cecilType.GenericParameters.Count == typeParameterCount) |
||||||
|
{ |
||||||
|
return GetClass(cecilType); |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
public IEnumerable<ITypeDefinition> GetClasses() |
||||||
|
{ |
||||||
|
foreach (TypeDefinition cecilType in module.Types) { |
||||||
|
yield return GetClass(cecilType); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer) |
||||||
|
{ |
||||||
|
foreach (TypeDefinition cecilType in module.Types) { |
||||||
|
if (nameComparer.Equals(nameSpace, cecilType.Namespace)) |
||||||
|
yield return GetClass(cecilType); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public IEnumerable<string> GetNamespaces() |
||||||
|
{ |
||||||
|
return namespaces; |
||||||
|
} |
||||||
|
|
||||||
|
public string GetNamespace(string nameSpace, StringComparer nameComparer) |
||||||
|
{ |
||||||
|
foreach (string ns in namespaces) { |
||||||
|
if (nameComparer.Equals(ns, nameSpace)) |
||||||
|
return ns; |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
ICSharpCode.NRefactory.Utils.CacheManager ITypeResolveContext.CacheManager { |
||||||
|
get { |
||||||
|
// We don't support caching
|
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ISynchronizedTypeResolveContext ITypeResolveContext.Synchronize() |
||||||
|
{ |
||||||
|
// This class is logically immutable
|
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
void IDisposable.Dispose() |
||||||
|
{ |
||||||
|
// exit from Synchronize() block
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,87 @@ |
|||||||
|
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using ICSharpCode.NRefactory.CSharp; |
||||||
|
using Mono.Cecil; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.Ast.Transforms |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Introduces using declarations.
|
||||||
|
/// </summary>
|
||||||
|
public class IntroduceUsingDeclarations : DepthFirstAstVisitor<object, object>, IAstTransform |
||||||
|
{ |
||||||
|
DecompilerContext context; |
||||||
|
|
||||||
|
public IntroduceUsingDeclarations(DecompilerContext context) |
||||||
|
{ |
||||||
|
this.context = context; |
||||||
|
currentNamespace = context.CurrentType != null ? context.CurrentType.Namespace : string.Empty; |
||||||
|
} |
||||||
|
|
||||||
|
public void Run(AstNode compilationUnit) |
||||||
|
{ |
||||||
|
// Don't show using when decompiling a single method or nested types:
|
||||||
|
if (context.CurrentMethod != null || (context.CurrentType != null && context.CurrentType.IsNested)) |
||||||
|
return; |
||||||
|
|
||||||
|
// First determine all the namespaces that need to be imported:
|
||||||
|
compilationUnit.AcceptVisitor(this, null); |
||||||
|
|
||||||
|
importedNamespaces.Add("System"); // always import System, even when not necessary
|
||||||
|
|
||||||
|
// Now add using declarations for those namespaces:
|
||||||
|
foreach (string ns in importedNamespaces.OrderByDescending(n => n)) { |
||||||
|
// we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
|
||||||
|
// (always inserting at the start of the list)
|
||||||
|
string[] parts = ns.Split('.'); |
||||||
|
AstType nsType = new SimpleType(parts[0]); |
||||||
|
for (int i = 1; i < parts.Length; i++) { |
||||||
|
nsType = new MemberType { Target = nsType, MemberName = parts[i] }; |
||||||
|
} |
||||||
|
compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, CompilationUnit.MemberRole); |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: verify that the SimpleTypes refer to the correct type (no ambiguities)
|
||||||
|
} |
||||||
|
|
||||||
|
readonly HashSet<string> importedNamespaces = new HashSet<string>(); |
||||||
|
string currentNamespace; |
||||||
|
|
||||||
|
bool IsParentOfCurrentNamespace(string ns) |
||||||
|
{ |
||||||
|
if (ns.Length == 0) |
||||||
|
return true; |
||||||
|
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) { |
||||||
|
if (currentNamespace.Length == ns.Length) |
||||||
|
return true; |
||||||
|
if (currentNamespace[ns.Length] == '.') |
||||||
|
return true; |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
public override object VisitSimpleType(SimpleType simpleType, object data) |
||||||
|
{ |
||||||
|
TypeReference tr = simpleType.Annotation<TypeReference>(); |
||||||
|
if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) { |
||||||
|
importedNamespaces.Add(tr.Namespace); |
||||||
|
} |
||||||
|
return base.VisitSimpleType(simpleType, data); // also visit type arguments
|
||||||
|
} |
||||||
|
|
||||||
|
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) |
||||||
|
{ |
||||||
|
string oldNamespace = currentNamespace; |
||||||
|
foreach (Identifier ident in namespaceDeclaration.Identifiers) { |
||||||
|
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name); |
||||||
|
} |
||||||
|
base.VisitNamespaceDeclaration(namespaceDeclaration, data); |
||||||
|
currentNamespace = oldNamespace; |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue