PageRenderTime 36ms CodeModel.GetById 20ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://github.com/icsharpcode/ILSpy
C# | 359 lines | 283 code | 33 blank | 43 comment | 90 complexity | 00adab88d14f07f06bc5d6feb68ab019 MD5 | raw file
  1// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2// 
  3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4// software and associated documentation files (the "Software"), to deal in the Software
  5// without restriction, including without limitation the rights to use, copy, modify, merge,
  6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7// to whom the Software is furnished to do so, subject to the following conditions:
  8// 
  9// The above copyright notice and this permission notice shall be included in all copies or
 10// substantial portions of the Software.
 11// 
 12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
 15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 17// DEALINGS IN THE SOFTWARE.
 18
 19using System;
 20using System.Collections.Generic;
 21using System.Linq;
 22using ICSharpCode.NRefactory.CSharp;
 23using Mono.Cecil;
 24
 25namespace ICSharpCode.Decompiler.Ast.Transforms
 26{
 27	/// <summary>
 28	/// Introduces using declarations.
 29	/// </summary>
 30	public class IntroduceUsingDeclarations : IAstTransform
 31	{
 32		DecompilerContext context;
 33		
 34		public IntroduceUsingDeclarations(DecompilerContext context)
 35		{
 36			this.context = context;
 37		}
 38		
 39		public void Run(AstNode compilationUnit)
 40		{
 41			// First determine all the namespaces that need to be imported:
 42			compilationUnit.AcceptVisitor(new FindRequiredImports(this), null);
 43			
 44			importedNamespaces.Add("System"); // always import System, even when not necessary
 45			
 46			if (context.Settings.UsingDeclarations) {
 47				// Now add using declarations for those namespaces:
 48				foreach (string ns in importedNamespaces.OrderByDescending(n => n)) {
 49					// we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
 50					// (always inserting at the start of the list)
 51					string[] parts = ns.Split('.');
 52					AstType nsType = new SimpleType(parts[0]);
 53					for (int i = 1; i < parts.Length; i++) {
 54						nsType = new MemberType { Target = nsType, MemberName = parts[i] };
 55					}
 56					compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, SyntaxTree.MemberRole);
 57				}
 58			}
 59			
 60			if (!context.Settings.FullyQualifyAmbiguousTypeNames)
 61				return;
 62			
 63			FindAmbiguousTypeNames(context.CurrentModule, internalsVisible: true);
 64			foreach (AssemblyNameReference r in context.CurrentModule.AssemblyReferences) {
 65				AssemblyDefinition d = context.CurrentModule.AssemblyResolver.Resolve(r);
 66				if (d != null)
 67					FindAmbiguousTypeNames(d.MainModule, internalsVisible: false);
 68			}
 69			
 70			// verify that the SimpleTypes refer to the correct type (no ambiguities)
 71			compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);
 72		}
 73		
 74		readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty };
 75		readonly HashSet<string> importedNamespaces = new HashSet<string>();
 76		
 77		// Note that we store type names with `n suffix, so we automatically disambiguate based on number of type parameters.
 78		readonly HashSet<string> availableTypeNames = new HashSet<string>();
 79		readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
 80		
 81		sealed class FindRequiredImports : DepthFirstAstVisitor<object, object>
 82		{
 83			readonly IntroduceUsingDeclarations transform;
 84			string currentNamespace;
 85			
 86			public FindRequiredImports(IntroduceUsingDeclarations transform)
 87			{
 88				this.transform = transform;
 89				this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
 90			}
 91			
 92			bool IsParentOfCurrentNamespace(string ns)
 93			{
 94				if (ns.Length == 0)
 95					return true;
 96				if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
 97					if (currentNamespace.Length == ns.Length)
 98						return true;
 99					if (currentNamespace[ns.Length] == '.')
100						return true;
101				}
102				return false;
103			}
104			
105			public override object VisitSimpleType(SimpleType simpleType, object data)
106			{
107				TypeReference tr = simpleType.Annotation<TypeReference>();
108				if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
109					transform.importedNamespaces.Add(tr.Namespace);
110				}
111				return base.VisitSimpleType(simpleType, data); // also visit type arguments
112			}
113			
114			public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
115			{
116				string oldNamespace = currentNamespace;
117				foreach (string ident in namespaceDeclaration.Identifiers) {
118					currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident);
119					transform.declaredNamespaces.Add(currentNamespace);
120				}
121				base.VisitNamespaceDeclaration(namespaceDeclaration, data);
122				currentNamespace = oldNamespace;
123				return null;
124			}
125		}
126		
127		void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible)
128		{
129			foreach (TypeDefinition type in module.Types) {
130				if (internalsVisible || type.IsPublic) {
131					if (importedNamespaces.Contains(type.Namespace) || declaredNamespaces.Contains(type.Namespace)) {
132						if (!availableTypeNames.Add(type.Name))
133							ambiguousTypeNames.Add(type.Name);
134					}
135				}
136			}
137		}
138		
139		sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object>
140		{
141			readonly IntroduceUsingDeclarations transform;
142			string currentNamespace;
143			HashSet<string> currentMemberTypes;
144			Dictionary<string, MemberReference> currentMembers;
145			bool isWithinTypeReferenceExpression;
146			
147			public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform)
148			{
149				this.transform = transform;
150				this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
151			}
152			
153			public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
154			{
155				string oldNamespace = currentNamespace;
156				foreach (string ident in namespaceDeclaration.Identifiers) {
157					currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident);
158				}
159				base.VisitNamespaceDeclaration(namespaceDeclaration, data);
160				currentNamespace = oldNamespace;
161				return null;
162			}
163			
164			public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
165			{
166				HashSet<string> oldMemberTypes = currentMemberTypes;
167				currentMemberTypes = currentMemberTypes != null ? new HashSet<string>(currentMemberTypes) : new HashSet<string>();
168				
169				Dictionary<string, MemberReference> oldMembers = currentMembers;
170				currentMembers = new Dictionary<string, MemberReference>();
171				
172				TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>();
173				bool privateMembersVisible = true;
174				ModuleDefinition internalMembersVisibleInModule = typeDef.Module;
175				while (typeDef != null) {
176					foreach (GenericParameter gp in typeDef.GenericParameters) {
177						currentMemberTypes.Add(gp.Name);
178					}
179					foreach (TypeDefinition t in typeDef.NestedTypes) {
180						if (privateMembersVisible || IsVisible(t, internalMembersVisibleInModule))
181							currentMemberTypes.Add(t.Name.Substring(t.Name.LastIndexOf('+') + 1));
182					}
183					
184					foreach (MethodDefinition method in typeDef.Methods) {
185						if (privateMembersVisible || IsVisible(method, internalMembersVisibleInModule))
186							AddCurrentMember(method);
187					}
188					foreach (PropertyDefinition property in typeDef.Properties) {
189						if (privateMembersVisible || IsVisible(property.GetMethod, internalMembersVisibleInModule) || IsVisible(property.SetMethod, internalMembersVisibleInModule))
190							AddCurrentMember(property);
191					}
192					foreach (EventDefinition ev in typeDef.Events) {
193						if (privateMembersVisible || IsVisible(ev.AddMethod, internalMembersVisibleInModule) || IsVisible(ev.RemoveMethod, internalMembersVisibleInModule))
194							AddCurrentMember(ev);
195					}
196					foreach (FieldDefinition f in typeDef.Fields) {
197						if (privateMembersVisible || IsVisible(f, internalMembersVisibleInModule))
198							AddCurrentMember(f);
199					}
200					// repeat with base class:
201					if (typeDef.BaseType != null)
202						typeDef = typeDef.BaseType.Resolve();
203					else
204						typeDef = null;
205					privateMembersVisible = false;
206				}
207				
208				// Now add current members from outer classes:
209				if (oldMembers != null) {
210					foreach (var pair in oldMembers) {
211						// add members from outer classes only if the inner class doesn't define the member
212						if (!currentMembers.ContainsKey(pair.Key))
213							currentMembers.Add(pair.Key, pair.Value);
214					}
215				}
216				
217				base.VisitTypeDeclaration(typeDeclaration, data);
218				currentMembers = oldMembers;
219				return null;
220			}
221			
222			void AddCurrentMember(MemberReference m)
223			{
224				MemberReference existingMember;
225				if (currentMembers.TryGetValue(m.Name, out existingMember)) {
226					// We keep the existing member assignment if it was from another class (=from a derived class),
227					// because members in derived classes have precedence over members in base classes.
228					if (existingMember != null && existingMember.DeclaringType == m.DeclaringType) {
229						// Use null as value to signalize multiple members with the same name
230						currentMembers[m.Name] = null;
231					}
232				} else {
233					currentMembers.Add(m.Name, m);
234				}
235			}
236			
237			bool IsVisible(MethodDefinition m, ModuleDefinition internalMembersVisibleInModule)
238			{
239				if (m == null)
240					return false;
241				switch (m.Attributes & MethodAttributes.MemberAccessMask) {
242					case MethodAttributes.FamANDAssem:
243					case MethodAttributes.Assembly:
244						return m.Module == internalMembersVisibleInModule;
245					case MethodAttributes.Family:
246					case MethodAttributes.FamORAssem:
247					case MethodAttributes.Public:
248						return true;
249					default:
250						return false;
251				}
252			}
253			
254			bool IsVisible(FieldDefinition f, ModuleDefinition internalMembersVisibleInModule)
255			{
256				if (f == null)
257					return false;
258				switch (f.Attributes & FieldAttributes.FieldAccessMask) {
259					case FieldAttributes.FamANDAssem:
260					case FieldAttributes.Assembly:
261						return f.Module == internalMembersVisibleInModule;
262					case FieldAttributes.Family:
263					case FieldAttributes.FamORAssem:
264					case FieldAttributes.Public:
265						return true;
266					default:
267						return false;
268				}
269			}
270			
271			bool IsVisible(TypeDefinition t, ModuleDefinition internalMembersVisibleInModule)
272			{
273				if (t == null)
274					return false;
275				switch (t.Attributes & TypeAttributes.VisibilityMask) {
276					case TypeAttributes.NotPublic:
277					case TypeAttributes.NestedAssembly:
278					case TypeAttributes.NestedFamANDAssem:
279						return t.Module == internalMembersVisibleInModule;
280					case TypeAttributes.NestedFamily:
281					case TypeAttributes.NestedFamORAssem:
282					case TypeAttributes.NestedPublic:
283					case TypeAttributes.Public:
284						return true;
285					default:
286						return false;
287				}
288			}
289			
290			public override object VisitSimpleType(SimpleType simpleType, object data)
291			{
292				// Handle type arguments first, so that the fixed-up type arguments get moved over to the MemberType,
293				// if we're also creating one here.
294				base.VisitSimpleType(simpleType, data);
295				TypeReference tr = simpleType.Annotation<TypeReference>();
296				// Fully qualify any ambiguous type names.
297				if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) {
298					AstType ns;
299					if (string.IsNullOrEmpty(tr.Namespace)) {
300						ns = new SimpleType("global");
301					} else {
302						string[] parts = tr.Namespace.Split('.');
303						if (IsAmbiguous(string.Empty, parts[0])) {
304							// conflict between namespace and type name/member name
305							ns = new MemberType { Target = new SimpleType("global"), IsDoubleColon = true, MemberName = parts[0] };
306						} else {
307							ns = new SimpleType(parts[0]);
308						}
309						for (int i = 1; i < parts.Length; i++) {
310							ns = new MemberType { Target = ns, MemberName = parts[i] };
311						}
312					}
313					MemberType mt = new MemberType();
314					mt.Target = ns;
315					mt.IsDoubleColon = string.IsNullOrEmpty(tr.Namespace);
316					mt.MemberName = simpleType.Identifier;
317					mt.CopyAnnotationsFrom(simpleType);
318					simpleType.TypeArguments.MoveTo(mt.TypeArguments);
319					simpleType.ReplaceWith(mt);
320				}
321				return null;
322			}
323			
324			public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data)
325			{
326				isWithinTypeReferenceExpression = true;
327				base.VisitTypeReferenceExpression(typeReferenceExpression, data);
328				isWithinTypeReferenceExpression = false;
329				return null;
330			}
331			
332			bool IsAmbiguous(string ns, string name)
333			{
334				// If the type name conflicts with an inner class/type parameter, we need to fully-qualify it:
335				if (currentMemberTypes != null && currentMemberTypes.Contains(name))
336					return true;
337				// If the type name conflicts with a field/property etc. on the current class, we need to fully-qualify it,
338				// if we're inside an expression.
339				if (isWithinTypeReferenceExpression && currentMembers != null) {
340					MemberReference mr;
341					if (currentMembers.TryGetValue(name, out mr)) {
342						// However, in the special case where the member is a field or property with the same type
343						// as is requested, then we can use the short name (if it's not otherwise ambiguous)
344						PropertyDefinition prop = mr as PropertyDefinition;
345						FieldDefinition field = mr as FieldDefinition;
346						if (!(prop != null && prop.PropertyType.Namespace == ns && prop.PropertyType.Name == name)
347						    && !(field != null && field.FieldType.Namespace == ns && field.FieldType.Name == name))
348							return true;
349					}
350				}
351				// If the type is defined in the current namespace,
352				// then we can use the short name even if we imported type with same name from another namespace.
353				if (ns == currentNamespace && !string.IsNullOrEmpty(ns))
354					return false;
355				return transform.ambiguousTypeNames.Contains(name);
356			}
357		}
358	}
359}