Browse Source

Improved disambiguation of type references.

pull/144/head
Daniel Grunwald 14 years ago
parent
commit
094f42ac83
  1. 279
      ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

279
ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

@ -12,14 +12,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
/// <summary> /// <summary>
/// Introduces using declarations. /// Introduces using declarations.
/// </summary> /// </summary>
public class IntroduceUsingDeclarations : DepthFirstAstVisitor<object, object>, IAstTransform public class IntroduceUsingDeclarations : IAstTransform
{ {
DecompilerContext context; DecompilerContext context;
public IntroduceUsingDeclarations(DecompilerContext context) public IntroduceUsingDeclarations(DecompilerContext context)
{ {
this.context = context; this.context = context;
currentNamespace = context.CurrentType != null ? context.CurrentType.Namespace : string.Empty;
} }
public void Run(AstNode compilationUnit) public void Run(AstNode compilationUnit)
@ -28,7 +27,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return; return;
// First determine all the namespaces that need to be imported: // First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(this, null); compilationUnit.AcceptVisitor(new FindRequiredImports(this), null);
importedNamespaces.Add("System"); // always import System, even when not necessary importedNamespaces.Add("System"); // always import System, even when not necessary
@ -55,48 +54,60 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
// verify that the SimpleTypes refer to the correct type (no ambiguities) // verify that the SimpleTypes refer to the correct type (no ambiguities)
FullyQualifyAmbiguousTypeNames(compilationUnit); compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);
} }
readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty }; readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty };
readonly HashSet<string> importedNamespaces = new HashSet<string>(); readonly HashSet<string> importedNamespaces = new HashSet<string>();
// 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> availableTypeNames = new HashSet<string>();
readonly HashSet<string> ambiguousTypeNames = new HashSet<string>(); readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
string currentNamespace;
bool IsParentOfCurrentNamespace(string ns) sealed class FindRequiredImports : DepthFirstAstVisitor<object, object>
{ {
if (ns.Length == 0) readonly IntroduceUsingDeclarations transform;
return true; string currentNamespace;
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
if (currentNamespace.Length == ns.Length) public FindRequiredImports(IntroduceUsingDeclarations transform)
return true; {
if (currentNamespace[ns.Length] == '.') this.transform = transform;
this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
}
bool IsParentOfCurrentNamespace(string ns)
{
if (ns.Length == 0)
return true; return true;
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
if (currentNamespace.Length == ns.Length)
return true;
if (currentNamespace[ns.Length] == '.')
return true;
}
return false;
} }
return false;
} public override object VisitSimpleType(SimpleType simpleType, object data)
{
public override object VisitSimpleType(SimpleType simpleType, object data) TypeReference tr = simpleType.Annotation<TypeReference>();
{ if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
TypeReference tr = simpleType.Annotation<TypeReference>(); transform.importedNamespaces.Add(tr.Namespace);
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);
transform.declaredNamespaces.Add(currentNamespace);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
} }
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);
declaredNamespaces.Add(currentNamespace);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
} }
void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible) void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible)
@ -111,17 +122,176 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
} }
void FullyQualifyAmbiguousTypeNames(AstNode compilationUnit) sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object>
{ {
foreach (SimpleType simpleType in compilationUnit.Descendants.OfType<SimpleType>()) { readonly IntroduceUsingDeclarations transform;
string currentNamespace;
HashSet<string> currentMemberTypes;
Dictionary<string, MemberReference> currentMembers;
bool isWithinTypeReferenceExpression;
public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform)
{
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;
}
}
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>(); TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && ambiguousTypeNames.Contains(tr.Name)) { // Fully qualify any ambiguous type names.
if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) {
AstType ns; AstType ns;
if (string.IsNullOrEmpty(tr.Namespace)) { if (string.IsNullOrEmpty(tr.Namespace)) {
ns = new SimpleType("global"); ns = new SimpleType("global");
} else { } else {
string[] parts = tr.Namespace.Split('.'); string[] parts = tr.Namespace.Split('.');
ns = new SimpleType(parts[0]); 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++) { for (int i = 1; i < parts.Length; i++) {
ns = new MemberType { Target = ns, MemberName = parts[i] }; ns = new MemberType { Target = ns, MemberName = parts[i] };
} }
@ -134,6 +304,41 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
simpleType.TypeArguments.MoveTo(mt.TypeArguments); simpleType.TypeArguments.MoveTo(mt.TypeArguments);
simpleType.ReplaceWith(mt); simpleType.ReplaceWith(mt);
} }
return null;
}
public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data)
{
isWithinTypeReferenceExpression = true;
base.VisitTypeReferenceExpression(typeReferenceExpression, data);
isWithinTypeReferenceExpression = false;
return null;
}
bool IsAmbiguous(string ns, string name)
{
// 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);
} }
} }
} }

Loading…
Cancel
Save