|
|
|
|
@ -20,7 +20,12 @@ using System;
@@ -20,7 +20,12 @@ using System;
|
|
|
|
|
using System.Collections.Generic; |
|
|
|
|
using System.Linq; |
|
|
|
|
using ICSharpCode.NRefactory.CSharp; |
|
|
|
|
using ICSharpCode.NRefactory.CSharp.Refactoring; |
|
|
|
|
using ICSharpCode.NRefactory.CSharp.Resolver; |
|
|
|
|
using ICSharpCode.NRefactory.CSharp.TypeSystem; |
|
|
|
|
using ICSharpCode.NRefactory.Semantics; |
|
|
|
|
using ICSharpCode.NRefactory.TypeSystem; |
|
|
|
|
using Mono.Cecil; |
|
|
|
|
|
|
|
|
|
namespace ICSharpCode.Decompiler.CSharp.Transforms |
|
|
|
|
{ |
|
|
|
|
@ -39,6 +44,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
@@ -39,6 +44,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
|
|
|
|
|
|
|
|
|
|
requiredImports.ImportedNamespaces.Add("System"); // always import System, even when not necessary
|
|
|
|
|
|
|
|
|
|
var usingScope = new UsingScope(); |
|
|
|
|
|
|
|
|
|
// Now add using declarations for those namespaces:
|
|
|
|
|
foreach (string ns in requiredImports.ImportedNamespaces.OrderByDescending(n => n)) { |
|
|
|
|
// we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
|
|
|
|
|
@ -48,28 +55,32 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
@@ -48,28 +55,32 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
|
|
|
|
|
for (int i = 1; i < parts.Length; i++) { |
|
|
|
|
nsType = new MemberType { Target = nsType, MemberName = parts[i] }; |
|
|
|
|
} |
|
|
|
|
if (FullyQualifyAmbiguousTypeNames) { |
|
|
|
|
var reference = nsType.ToTypeReference(NameLookupMode.TypeInUsingDeclaration) as TypeOrNamespaceReference; |
|
|
|
|
if (reference != null) |
|
|
|
|
usingScope.Usings.Add(reference); |
|
|
|
|
} |
|
|
|
|
compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!FullyQualifyAmbiguousTypeNames) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
FindAmbiguousTypeNames(context.CurrentModule, internalsVisible: true); |
|
|
|
|
foreach (AssemblyNameReference r in context.CurrentModule.AssemblyReferences) { |
|
|
|
|
AssemblyDefinition d = context.CurrentModule.AssemblyResolver.Resolve(r); |
|
|
|
|
if (d != null) |
|
|
|
|
FindAmbiguousTypeNames(d.MainModule, internalsVisible: false); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var astBuilder = CreateAstBuilderWithUsingScope(context, usingScope); |
|
|
|
|
|
|
|
|
|
// verify that the SimpleTypes refer to the correct type (no ambiguities)
|
|
|
|
|
compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);*/ |
|
|
|
|
compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(astBuilder), null); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
TypeSystemAstBuilder CreateAstBuilderWithUsingScope(TransformContext context, UsingScope usingScope) |
|
|
|
|
{ |
|
|
|
|
var resolveContext = new CSharpTypeResolveContext(context.TypeSystem.MainAssembly, usingScope.Resolve(context.TypeSystem.Compilation), context.DecompiledTypeDefinition, context.DecompiledMember); |
|
|
|
|
var resolver = new CSharpResolver(resolveContext); |
|
|
|
|
return new TypeSystemAstBuilder(resolver) { |
|
|
|
|
AddResolveResultAnnotations = true, |
|
|
|
|
UseAliases = true |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Note that we store type names with `n suffix, so we automatically disambiguate based on number of type parameters.
|
|
|
|
|
//readonly HashSet<string> availableTypeNames = new HashSet<string>();
|
|
|
|
|
//readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
|
|
|
|
|
|
|
|
|
|
sealed class FindRequiredImports : DepthFirstAstVisitor |
|
|
|
|
{ |
|
|
|
|
@ -117,237 +128,41 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
@@ -117,237 +128,41 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible) |
|
|
|
|
{ |
|
|
|
|
foreach (TypeDefinition type in module.Types) { |
|
|
|
|
if (internalsVisible || type.IsPublic) { |
|
|
|
|
if (importedNamespaces.Contains(type.Namespace) || declaredNamespaces.Contains(type.Namespace)) { |
|
|
|
|
if (!availableTypeNames.Add(type.Name)) |
|
|
|
|
ambiguousTypeNames.Add(type.Name); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object> |
|
|
|
|
{ |
|
|
|
|
readonly IntroduceUsingDeclarations transform; |
|
|
|
|
string currentNamespace; |
|
|
|
|
HashSet<string> currentMemberTypes; |
|
|
|
|
Dictionary<string, MemberReference> currentMembers; |
|
|
|
|
bool isWithinTypeReferenceExpression; |
|
|
|
|
readonly TypeSystemAstBuilder astBuilder; |
|
|
|
|
|
|
|
|
|
public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform) |
|
|
|
|
public FullyQualifyAmbiguousTypeNamesVisitor(TypeSystemAstBuilder astBuilder) |
|
|
|
|
{ |
|
|
|
|
this.transform = transform; |
|
|
|
|
this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) |
|
|
|
|
{ |
|
|
|
|
HashSet<string> oldMemberTypes = currentMemberTypes; |
|
|
|
|
currentMemberTypes = currentMemberTypes != null ? new HashSet<string>(currentMemberTypes) : new HashSet<string>(); |
|
|
|
|
|
|
|
|
|
Dictionary<string, MemberReference> oldMembers = currentMembers; |
|
|
|
|
currentMembers = new Dictionary<string, MemberReference>(); |
|
|
|
|
|
|
|
|
|
TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>(); |
|
|
|
|
bool privateMembersVisible = true; |
|
|
|
|
ModuleDefinition internalMembersVisibleInModule = typeDef.Module; |
|
|
|
|
while (typeDef != null) { |
|
|
|
|
foreach (GenericParameter gp in typeDef.GenericParameters) { |
|
|
|
|
currentMemberTypes.Add(gp.Name); |
|
|
|
|
} |
|
|
|
|
foreach (TypeDefinition t in typeDef.NestedTypes) { |
|
|
|
|
if (privateMembersVisible || IsVisible(t, internalMembersVisibleInModule)) |
|
|
|
|
currentMemberTypes.Add(t.Name.Substring(t.Name.LastIndexOf('+') + 1)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
foreach (MethodDefinition method in typeDef.Methods) { |
|
|
|
|
if (privateMembersVisible || IsVisible(method, internalMembersVisibleInModule)) |
|
|
|
|
AddCurrentMember(method); |
|
|
|
|
} |
|
|
|
|
foreach (PropertyDefinition property in typeDef.Properties) { |
|
|
|
|
if (privateMembersVisible || IsVisible(property.GetMethod, internalMembersVisibleInModule) || IsVisible(property.SetMethod, internalMembersVisibleInModule)) |
|
|
|
|
AddCurrentMember(property); |
|
|
|
|
} |
|
|
|
|
foreach (EventDefinition ev in typeDef.Events) { |
|
|
|
|
if (privateMembersVisible || IsVisible(ev.AddMethod, internalMembersVisibleInModule) || IsVisible(ev.RemoveMethod, internalMembersVisibleInModule)) |
|
|
|
|
AddCurrentMember(ev); |
|
|
|
|
} |
|
|
|
|
foreach (FieldDefinition f in typeDef.Fields) { |
|
|
|
|
if (privateMembersVisible || IsVisible(f, internalMembersVisibleInModule)) |
|
|
|
|
AddCurrentMember(f); |
|
|
|
|
} |
|
|
|
|
// repeat with base class:
|
|
|
|
|
if (typeDef.BaseType != null) |
|
|
|
|
typeDef = typeDef.BaseType.Resolve(); |
|
|
|
|
else |
|
|
|
|
typeDef = null; |
|
|
|
|
privateMembersVisible = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Now add current members from outer classes:
|
|
|
|
|
if (oldMembers != null) { |
|
|
|
|
foreach (var pair in oldMembers) { |
|
|
|
|
// add members from outer classes only if the inner class doesn't define the member
|
|
|
|
|
if (!currentMembers.ContainsKey(pair.Key)) |
|
|
|
|
currentMembers.Add(pair.Key, pair.Value); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
base.VisitTypeDeclaration(typeDeclaration, data); |
|
|
|
|
currentMembers = oldMembers; |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void AddCurrentMember(MemberReference m) |
|
|
|
|
{ |
|
|
|
|
MemberReference existingMember; |
|
|
|
|
if (currentMembers.TryGetValue(m.Name, out existingMember)) { |
|
|
|
|
// We keep the existing member assignment if it was from another class (=from a derived class),
|
|
|
|
|
// because members in derived classes have precedence over members in base classes.
|
|
|
|
|
if (existingMember != null && existingMember.DeclaringType == m.DeclaringType) { |
|
|
|
|
// Use null as value to signalize multiple members with the same name
|
|
|
|
|
currentMembers[m.Name] = null; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
currentMembers.Add(m.Name, m); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsVisible(MethodDefinition m, ModuleDefinition internalMembersVisibleInModule) |
|
|
|
|
{ |
|
|
|
|
if (m == null) |
|
|
|
|
return false; |
|
|
|
|
switch (m.Attributes & MethodAttributes.MemberAccessMask) { |
|
|
|
|
case MethodAttributes.FamANDAssem: |
|
|
|
|
case MethodAttributes.Assembly: |
|
|
|
|
return m.Module == internalMembersVisibleInModule; |
|
|
|
|
case MethodAttributes.Family: |
|
|
|
|
case MethodAttributes.FamORAssem: |
|
|
|
|
case MethodAttributes.Public: |
|
|
|
|
return true; |
|
|
|
|
default: |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsVisible(FieldDefinition f, ModuleDefinition internalMembersVisibleInModule) |
|
|
|
|
{ |
|
|
|
|
if (f == null) |
|
|
|
|
return false; |
|
|
|
|
switch (f.Attributes & FieldAttributes.FieldAccessMask) { |
|
|
|
|
case FieldAttributes.FamANDAssem: |
|
|
|
|
case FieldAttributes.Assembly: |
|
|
|
|
return f.Module == internalMembersVisibleInModule; |
|
|
|
|
case FieldAttributes.Family: |
|
|
|
|
case FieldAttributes.FamORAssem: |
|
|
|
|
case FieldAttributes.Public: |
|
|
|
|
return true; |
|
|
|
|
default: |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsVisible(TypeDefinition t, ModuleDefinition internalMembersVisibleInModule) |
|
|
|
|
{ |
|
|
|
|
if (t == null) |
|
|
|
|
return false; |
|
|
|
|
switch (t.Attributes & TypeAttributes.VisibilityMask) { |
|
|
|
|
case TypeAttributes.NotPublic: |
|
|
|
|
case TypeAttributes.NestedAssembly: |
|
|
|
|
case TypeAttributes.NestedFamANDAssem: |
|
|
|
|
return t.Module == internalMembersVisibleInModule; |
|
|
|
|
case TypeAttributes.NestedFamily: |
|
|
|
|
case TypeAttributes.NestedFamORAssem: |
|
|
|
|
case TypeAttributes.NestedPublic: |
|
|
|
|
case TypeAttributes.Public: |
|
|
|
|
return true; |
|
|
|
|
default: |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
this.astBuilder = astBuilder; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override object VisitSimpleType(SimpleType simpleType, object data) |
|
|
|
|
{ |
|
|
|
|
// Handle type arguments first, so that the fixed-up type arguments get moved over to the MemberType,
|
|
|
|
|
// if we're also creating one here.
|
|
|
|
|
base.VisitSimpleType(simpleType, data); |
|
|
|
|
TypeReference tr = simpleType.Annotation<TypeReference>(); |
|
|
|
|
// Fully qualify any ambiguous type names.
|
|
|
|
|
if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) { |
|
|
|
|
AstType ns; |
|
|
|
|
if (string.IsNullOrEmpty(tr.Namespace)) { |
|
|
|
|
ns = new SimpleType("global"); |
|
|
|
|
} else { |
|
|
|
|
string[] parts = tr.Namespace.Split('.'); |
|
|
|
|
if (IsAmbiguous(string.Empty, parts[0])) { |
|
|
|
|
// conflict between namespace and type name/member name
|
|
|
|
|
ns = new MemberType { Target = new SimpleType("global"), IsDoubleColon = true, MemberName = parts[0] }; |
|
|
|
|
} else { |
|
|
|
|
ns = new SimpleType(parts[0]); |
|
|
|
|
} |
|
|
|
|
for (int i = 1; i < parts.Length; i++) { |
|
|
|
|
ns = new MemberType { Target = ns, MemberName = parts[i] }; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
MemberType mt = new MemberType(); |
|
|
|
|
mt.Target = ns; |
|
|
|
|
mt.IsDoubleColon = string.IsNullOrEmpty(tr.Namespace); |
|
|
|
|
mt.MemberName = simpleType.Identifier; |
|
|
|
|
mt.CopyAnnotationsFrom(simpleType); |
|
|
|
|
simpleType.TypeArguments.MoveTo(mt.TypeArguments); |
|
|
|
|
simpleType.ReplaceWith(mt); |
|
|
|
|
} |
|
|
|
|
TypeResolveResult rr; |
|
|
|
|
if (simpleType.Ancestors.OfType<UsingDeclaration>().Any() || (rr = simpleType.Annotation<TypeResolveResult>()) == null) |
|
|
|
|
return base.VisitSimpleType(simpleType, data); |
|
|
|
|
simpleType.ReplaceWith(astBuilder.ConvertType(rr.Type)); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data) |
|
|
|
|
public override object VisitMemberType(MemberType memberType, object data) |
|
|
|
|
{ |
|
|
|
|
isWithinTypeReferenceExpression = true; |
|
|
|
|
base.VisitTypeReferenceExpression(typeReferenceExpression, data); |
|
|
|
|
isWithinTypeReferenceExpression = false; |
|
|
|
|
TypeResolveResult rr; |
|
|
|
|
if (memberType.Ancestors.OfType<UsingDeclaration>().Any() || (rr = memberType.Annotation<TypeResolveResult>()) == null) |
|
|
|
|
return base.VisitMemberType(memberType, data); |
|
|
|
|
memberType.ReplaceWith(astBuilder.ConvertType(rr.Type)); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool IsAmbiguous(string ns, string name) |
|
|
|
|
public override object VisitComposedType(ComposedType composedType, object data) |
|
|
|
|
{ |
|
|
|
|
// If the type name conflicts with an inner class/type parameter, we need to fully-qualify it:
|
|
|
|
|
if (currentMemberTypes != null && currentMemberTypes.Contains(name)) |
|
|
|
|
return true; |
|
|
|
|
// If the type name conflicts with a field/property etc. on the current class, we need to fully-qualify it,
|
|
|
|
|
// if we're inside an expression.
|
|
|
|
|
if (isWithinTypeReferenceExpression && currentMembers != null) { |
|
|
|
|
MemberReference mr; |
|
|
|
|
if (currentMembers.TryGetValue(name, out mr)) { |
|
|
|
|
// However, in the special case where the member is a field or property with the same type
|
|
|
|
|
// as is requested, then we can use the short name (if it's not otherwise ambiguous)
|
|
|
|
|
PropertyDefinition prop = mr as PropertyDefinition; |
|
|
|
|
FieldDefinition field = mr as FieldDefinition; |
|
|
|
|
if (!(prop != null && prop.PropertyType.Namespace == ns && prop.PropertyType.Name == name) |
|
|
|
|
&& !(field != null && field.FieldType.Namespace == ns && field.FieldType.Name == name)) |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// If the type is defined in the current namespace,
|
|
|
|
|
// then we can use the short name even if we imported type with same name from another namespace.
|
|
|
|
|
if (ns == currentNamespace && !string.IsNullOrEmpty(ns)) |
|
|
|
|
return false; |
|
|
|
|
return transform.ambiguousTypeNames.Contains(name); |
|
|
|
|
TypeResolveResult rr; |
|
|
|
|
if (composedType.Ancestors.OfType<UsingDeclaration>().Any() || (rr = composedType.Annotation<TypeResolveResult>()) == null) |
|
|
|
|
return base.VisitComposedType(composedType, data); |
|
|
|
|
composedType.ReplaceWith(astBuilder.ConvertType(rr.Type)); |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
}*/ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|