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