@ -12,14 +12,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
@@ -12,14 +12,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
/// <summary>
/// Introduces using declarations.
/// </summary>
public class IntroduceUsingDeclarations : DepthFirstAstVisitor < object , object > , IAstTransform
public class IntroduceUsingDeclarations : IAstTransform
{
DecompilerContext context ;
public IntroduceUsingDeclarations ( DecompilerContext context )
{
this . context = context ;
currentNamespace = context . CurrentType ! = null ? context . CurrentType . Namespace : string . Empty ;
}
public void Run ( AstNode compilationUnit )
@ -28,7 +27,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
@@ -28,7 +27,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return ;
// 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
@ -55,48 +54,60 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
@@ -55,48 +54,60 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
// 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 > 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 > ambiguousTypeNames = new HashSet < string > ( ) ;
string currentNamespace ;
bool IsParentOfCurrentNamespace ( string ns )
sealed class FindRequiredImports : DepthFirstAstVisitor < object , object >
{
if ( ns . Length = = 0 )
return true ;
if ( currentNamespace . StartsWith ( ns , StringComparison . Ordinal ) ) {
if ( currentNamespace . Length = = ns . Length )
return true ;
if ( currentNamespace [ ns . Length ] = = '.' )
readonly IntroduceUsingDeclarations transform ;
string currentNamespace ;
public FindRequiredImports ( IntroduceUsingDeclarations transform )
{
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 ;
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 )
{
TypeReference tr = simpleType . Annotation < TypeReference > ( ) ;
if ( tr ! = null & & ! IsParentOfCurrentNamespace ( tr . Namespace ) ) {
importedNamespaces . Add ( tr . Namespace ) ;
public override object VisitSimpleType ( SimpleType simpleType , object data )
{
TypeReference tr = simpleType . Annotation < TypeReference > ( ) ;
if ( tr ! = null & & ! IsParentOfCurrentNamespace ( tr . Namespace ) ) {
transform . 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 )
@ -111,17 +122,176 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
@@ -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 > ( ) ;
if ( tr ! = null & & ambiguousTypeNames . Contains ( tr . Name ) ) {
// 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 ( '.' ) ;
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 + + ) {
ns = new MemberType { Target = ns , MemberName = parts [ i ] } ;
}
@ -134,6 +304,41 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
@@ -134,6 +304,41 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
simpleType . TypeArguments . MoveTo ( mt . TypeArguments ) ;
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 ) ;
}
}
}