/main/contrib/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
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}