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

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

https://github.com/jfcantin/monodevelop
C# | 360 lines | 283 code | 34 blank | 43 comment | 90 complexity | a72251301aef63a437597086268f02c3 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			if (!context.Settings.UsingDeclarations)
 42				return;
 43			
 44			// First determine all the namespaces that need to be imported:
 45			compilationUnit.AcceptVisitor(new FindRequiredImports(this), null);
 46			
 47			importedNamespaces.Add("System"); // always import System, even when not necessary
 48			
 49			// Now add using declarations for those namespaces:
 50			foreach (string ns in importedNamespaces.OrderByDescending(n => n)) {
 51				// we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
 52				// (always inserting at the start of the list)
 53				string[] parts = ns.Split('.');
 54				AstType nsType = new SimpleType(parts[0]);
 55				for (int i = 1; i < parts.Length; i++) {
 56					nsType = new MemberType { Target = nsType, MemberName = parts[i] };
 57				}
 58				compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, CompilationUnit.MemberRole);
 59			}
 60			
 61			if (!context.Settings.FullyQualifyAmbiguousTypeNames)
 62				return;
 63			
 64			FindAmbiguousTypeNames(context.CurrentModule, internalsVisible: true);
 65			foreach (AssemblyNameReference r in context.CurrentModule.AssemblyReferences) {
 66				AssemblyDefinition d = context.CurrentModule.AssemblyResolver.Resolve(r);
 67				if (d != null)
 68					FindAmbiguousTypeNames(d.MainModule, internalsVisible: false);
 69			}
 70			
 71			// verify that the SimpleTypes refer to the correct type (no ambiguities)
 72			compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);
 73		}
 74		
 75		readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty };
 76		readonly HashSet<string> importedNamespaces = new HashSet<string>();
 77		
 78		// Note that we store type names with `n suffix, so we automatically disambiguate based on number of type parameters.
 79		readonly HashSet<string> availableTypeNames = new HashSet<string>();
 80		readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
 81		
 82		sealed class FindRequiredImports : DepthFirstAstVisitor<object, object>
 83		{
 84			readonly IntroduceUsingDeclarations transform;
 85			string currentNamespace;
 86			
 87			public FindRequiredImports(IntroduceUsingDeclarations transform)
 88			{
 89				this.transform = transform;
 90				this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
 91			}
 92			
 93			bool IsParentOfCurrentNamespace(string ns)
 94			{
 95				if (ns.Length == 0)
 96					return true;
 97				if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
 98					if (currentNamespace.Length == ns.Length)
 99						return true;
100					if (currentNamespace[ns.Length] == '.')
101						return true;
102				}
103				return false;
104			}
105			
106			public override object VisitSimpleType(SimpleType simpleType, object data)
107			{
108				TypeReference tr = simpleType.Annotation<TypeReference>();
109				if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
110					transform.importedNamespaces.Add(tr.Namespace);
111				}
112				return base.VisitSimpleType(simpleType, data); // also visit type arguments
113			}
114			
115			public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
116			{
117				string oldNamespace = currentNamespace;
118				foreach (Identifier ident in namespaceDeclaration.Identifiers) {
119					currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
120					transform.declaredNamespaces.Add(currentNamespace);
121				}
122				base.VisitNamespaceDeclaration(namespaceDeclaration, data);
123				currentNamespace = oldNamespace;
124				return null;
125			}
126		}
127		
128		void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible)
129		{
130			foreach (TypeDefinition type in module.Types) {
131				if (internalsVisible || type.IsPublic) {
132					if (importedNamespaces.Contains(type.Namespace) || declaredNamespaces.Contains(type.Namespace)) {
133						if (!availableTypeNames.Add(type.Name))
134							ambiguousTypeNames.Add(type.Name);
135					}
136				}
137			}
138		}
139		
140		sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object>
141		{
142			readonly IntroduceUsingDeclarations transform;
143			string currentNamespace;
144			HashSet<string> currentMemberTypes;
145			Dictionary<string, MemberReference> currentMembers;
146			bool isWithinTypeReferenceExpression;
147			
148			public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform)
149			{
150				this.transform = transform;
151				this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
152			}
153			
154			public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
155			{
156				string oldNamespace = currentNamespace;
157				foreach (Identifier ident in namespaceDeclaration.Identifiers) {
158					currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
159				}
160				base.VisitNamespaceDeclaration(namespaceDeclaration, data);
161				currentNamespace = oldNamespace;
162				return null;
163			}
164			
165			public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
166			{
167				HashSet<string> oldMemberTypes = currentMemberTypes;
168				currentMemberTypes = currentMemberTypes != null ? new HashSet<string>(currentMemberTypes) : new HashSet<string>();
169				
170				Dictionary<string, MemberReference> oldMembers = currentMembers;
171				currentMembers = new Dictionary<string, MemberReference>();
172				
173				TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>();
174				bool privateMembersVisible = true;
175				ModuleDefinition internalMembersVisibleInModule = typeDef.Module;
176				while (typeDef != null) {
177					foreach (GenericParameter gp in typeDef.GenericParameters) {
178						currentMemberTypes.Add(gp.Name);
179					}
180					foreach (TypeDefinition t in typeDef.NestedTypes) {
181						if (privateMembersVisible || IsVisible(t, internalMembersVisibleInModule))
182							currentMemberTypes.Add(t.Name.Substring(t.Name.LastIndexOf('+') + 1));
183					}
184					
185					foreach (MethodDefinition method in typeDef.Methods) {
186						if (privateMembersVisible || IsVisible(method, internalMembersVisibleInModule))
187							AddCurrentMember(method);
188					}
189					foreach (PropertyDefinition property in typeDef.Properties) {
190						if (privateMembersVisible || IsVisible(property.GetMethod, internalMembersVisibleInModule) || IsVisible(property.SetMethod, internalMembersVisibleInModule))
191							AddCurrentMember(property);
192					}
193					foreach (EventDefinition ev in typeDef.Events) {
194						if (privateMembersVisible || IsVisible(ev.AddMethod, internalMembersVisibleInModule) || IsVisible(ev.RemoveMethod, internalMembersVisibleInModule))
195							AddCurrentMember(ev);
196					}
197					foreach (FieldDefinition f in typeDef.Fields) {
198						if (privateMembersVisible || IsVisible(f, internalMembersVisibleInModule))
199							AddCurrentMember(f);
200					}
201					// repeat with base class:
202					if (typeDef.BaseType != null)
203						typeDef = typeDef.BaseType.Resolve();
204					else
205						typeDef = null;
206					privateMembersVisible = false;
207				}
208				
209				// Now add current members from outer classes:
210				if (oldMembers != null) {
211					foreach (var pair in oldMembers) {
212						// add members from outer classes only if the inner class doesn't define the member
213						if (!currentMembers.ContainsKey(pair.Key))
214							currentMembers.Add(pair.Key, pair.Value);
215					}
216				}
217				
218				base.VisitTypeDeclaration(typeDeclaration, data);
219				currentMembers = oldMembers;
220				return null;
221			}
222			
223			void AddCurrentMember(MemberReference m)
224			{
225				MemberReference existingMember;
226				if (currentMembers.TryGetValue(m.Name, out existingMember)) {
227					// We keep the existing member assignment if it was from another class (=from a derived class),
228					// because members in derived classes have precedence over members in base classes.
229					if (existingMember != null && existingMember.DeclaringType == m.DeclaringType) {
230						// Use null as value to signalize multiple members with the same name
231						currentMembers[m.Name] = null;
232					}
233				} else {
234					currentMembers.Add(m.Name, m);
235				}
236			}
237			
238			bool IsVisible(MethodDefinition m, ModuleDefinition internalMembersVisibleInModule)
239			{
240				if (m == null)
241					return false;
242				switch (m.Attributes & MethodAttributes.MemberAccessMask) {
243					case MethodAttributes.FamANDAssem:
244					case MethodAttributes.Assembly:
245						return m.Module == internalMembersVisibleInModule;
246					case MethodAttributes.Family:
247					case MethodAttributes.FamORAssem:
248					case MethodAttributes.Public:
249						return true;
250					default:
251						return false;
252				}
253			}
254			
255			bool IsVisible(FieldDefinition f, ModuleDefinition internalMembersVisibleInModule)
256			{
257				if (f == null)
258					return false;
259				switch (f.Attributes & FieldAttributes.FieldAccessMask) {
260					case FieldAttributes.FamANDAssem:
261					case FieldAttributes.Assembly:
262						return f.Module == internalMembersVisibleInModule;
263					case FieldAttributes.Family:
264					case FieldAttributes.FamORAssem:
265					case FieldAttributes.Public:
266						return true;
267					default:
268						return false;
269				}
270			}
271			
272			bool IsVisible(TypeDefinition t, ModuleDefinition internalMembersVisibleInModule)
273			{
274				if (t == null)
275					return false;
276				switch (t.Attributes & TypeAttributes.VisibilityMask) {
277					case TypeAttributes.NotPublic:
278					case TypeAttributes.NestedAssembly:
279					case TypeAttributes.NestedFamANDAssem:
280						return t.Module == internalMembersVisibleInModule;
281					case TypeAttributes.NestedFamily:
282					case TypeAttributes.NestedFamORAssem:
283					case TypeAttributes.NestedPublic:
284					case TypeAttributes.Public:
285						return true;
286					default:
287						return false;
288				}
289			}
290			
291			public override object VisitSimpleType(SimpleType simpleType, object data)
292			{
293				// Handle type arguments first, so that the fixed-up type arguments get moved over to the MemberType,
294				// if we're also creating one here.
295				base.VisitSimpleType(simpleType, data);
296				TypeReference tr = simpleType.Annotation<TypeReference>();
297				// Fully qualify any ambiguous type names.
298				if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) {
299					AstType ns;
300					if (string.IsNullOrEmpty(tr.Namespace)) {
301						ns = new SimpleType("global");
302					} else {
303						string[] parts = tr.Namespace.Split('.');
304						if (IsAmbiguous(string.Empty, parts[0])) {
305							// conflict between namespace and type name/member name
306							ns = new MemberType { Target = new SimpleType("global"), IsDoubleColon = true, MemberName = parts[0] };
307						} else {
308							ns = new SimpleType(parts[0]);
309						}
310						for (int i = 1; i < parts.Length; i++) {
311							ns = new MemberType { Target = ns, MemberName = parts[i] };
312						}
313					}
314					MemberType mt = new MemberType();
315					mt.Target = ns;
316					mt.IsDoubleColon = string.IsNullOrEmpty(tr.Namespace);
317					mt.MemberName = simpleType.Identifier;
318					mt.CopyAnnotationsFrom(simpleType);
319					simpleType.TypeArguments.MoveTo(mt.TypeArguments);
320					simpleType.ReplaceWith(mt);
321				}
322				return null;
323			}
324			
325			public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data)
326			{
327				isWithinTypeReferenceExpression = true;
328				base.VisitTypeReferenceExpression(typeReferenceExpression, data);
329				isWithinTypeReferenceExpression = false;
330				return null;
331			}
332			
333			bool IsAmbiguous(string ns, string name)
334			{
335				// If the type name conflicts with an inner class/type parameter, we need to fully-qualify it:
336				if (currentMemberTypes != null && currentMemberTypes.Contains(name))
337					return true;
338				// If the type name conflicts with a field/property etc. on the current class, we need to fully-qualify it,
339				// if we're inside an expression.
340				if (isWithinTypeReferenceExpression && currentMembers != null) {
341					MemberReference mr;
342					if (currentMembers.TryGetValue(name, out mr)) {
343						// However, in the special case where the member is a field or property with the same type
344						// as is requested, then we can use the short name (if it's not otherwise ambiguous)
345						PropertyDefinition prop = mr as PropertyDefinition;
346						FieldDefinition field = mr as FieldDefinition;
347						if (!(prop != null && prop.PropertyType.Namespace == ns && prop.PropertyType.Name == name)
348						    && !(field != null && field.FieldType.Namespace == ns && field.FieldType.Name == name))
349							return true;
350					}
351				}
352				// If the type is defined in the current namespace,
353				// then we can use the short name even if we imported type with same name from another namespace.
354				if (ns == currentNamespace && !string.IsNullOrEmpty(ns))
355					return false;
356				return transform.ambiguousTypeNames.Contains(name);
357			}
358		}
359	}
360}