PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/IronPython_Main/Runtime/Microsoft.Dynamic/Actions/NamespaceTracker.cs

#
C# | 519 lines | 362 code | 95 blank | 62 comment | 65 complexity | dd76e36849cc32d69d812c277f44de6c MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. #if !CLR2
  16. using System.Linq.Expressions;
  17. #else
  18. using Microsoft.Scripting.Ast;
  19. #endif
  20. using System;
  21. using System.Collections;
  22. using System.Collections.Generic;
  23. using System.Diagnostics;
  24. using System.Reflection;
  25. using Microsoft.Scripting.Utils;
  26. using System.Threading;
  27. using Microsoft.Contracts;
  28. using Microsoft.Scripting.Runtime;
  29. using AstUtils = Microsoft.Scripting.Ast.Utils;
  30. namespace Microsoft.Scripting.Actions {
  31. /// <summary>
  32. /// NamespaceTracker represent a CLS namespace.
  33. /// </summary>
  34. public class NamespaceTracker : MemberTracker, IMembersList, IEnumerable<KeyValuePair<string, object>> {
  35. // _dict contains all the currently loaded entries. However, there may be pending types that have
  36. // not yet been loaded in _typeNames
  37. internal Dictionary<string, MemberTracker> _dict = new Dictionary<string, MemberTracker>();
  38. internal readonly List<Assembly> _packageAssemblies = new List<Assembly>();
  39. internal readonly Dictionary<Assembly, TypeNames> _typeNames = new Dictionary<Assembly, TypeNames>();
  40. private readonly string _fullName; // null for the TopReflectedPackage
  41. private TopNamespaceTracker _topPackage;
  42. private int _id;
  43. private static int _masterId;
  44. #region Protected API Surface
  45. protected NamespaceTracker(string name) {
  46. UpdateId();
  47. _fullName = name;
  48. }
  49. [Confined]
  50. public override string ToString() {
  51. return base.ToString() + ":" + _fullName;
  52. }
  53. #endregion
  54. #region Internal API Surface
  55. internal NamespaceTracker GetOrMakeChildPackage(string childName, Assembly assem) {
  56. // lock is held when this is called
  57. Assert.NotNull(childName, assem);
  58. Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  59. Debug.Assert(_packageAssemblies.Contains(assem)); // Parent namespace must contain all the assemblies of the child
  60. MemberTracker ret;
  61. if (_dict.TryGetValue(childName, out ret)) {
  62. // If we have a module, then we add the assembly to the InnerModule
  63. // If it's not a module, we'll wipe it out below, eg "def System(): pass" then
  64. // "import System" will result in the namespace being visible.
  65. NamespaceTracker package = ret as NamespaceTracker;
  66. if (package != null) {
  67. if (!package._packageAssemblies.Contains(assem)) {
  68. package._packageAssemblies.Add(assem);
  69. package.UpdateSubtreeIds();
  70. }
  71. return package;
  72. }
  73. }
  74. return MakeChildPackage(childName, assem);
  75. }
  76. private NamespaceTracker MakeChildPackage(string childName, Assembly assem) {
  77. // lock is held when this is called
  78. Assert.NotNull(childName, assem);
  79. NamespaceTracker rp = new NamespaceTracker(GetFullChildName(childName));
  80. rp.SetTopPackage(_topPackage);
  81. rp._packageAssemblies.Add(assem);
  82. _dict[childName] = rp;
  83. return rp;
  84. }
  85. private string GetFullChildName(string childName) {
  86. Assert.NotNull(childName);
  87. Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  88. if (_fullName == null) {
  89. return childName;
  90. }
  91. return _fullName + Type.Delimiter + childName;
  92. }
  93. private static Type LoadType(Assembly assem, string fullTypeName) {
  94. Assert.NotNull(assem, fullTypeName);
  95. Type type = assem.GetType(fullTypeName);
  96. // We should ignore nested types. They will be loaded when the containing type is loaded
  97. Debug.Assert(type == null || !type.IsNested());
  98. return type;
  99. }
  100. internal void AddTypeName(string typeName, Assembly assem) {
  101. // lock is held when this is called
  102. Assert.NotNull(typeName, assem);
  103. Debug.Assert(typeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  104. if (!_typeNames.ContainsKey(assem)) {
  105. _typeNames[assem] = new TypeNames(assem, _fullName);
  106. }
  107. _typeNames[assem].AddTypeName(typeName);
  108. string normalizedTypeName = ReflectionUtils.GetNormalizedTypeName(typeName);
  109. if (_dict.ContainsKey(normalizedTypeName)) {
  110. // A similarly named type, namespace, or module already exists.
  111. Type newType = LoadType(assem, GetFullChildName(typeName));
  112. if (newType != null) {
  113. object existingValue = _dict[normalizedTypeName];
  114. TypeTracker existingTypeEntity = existingValue as TypeTracker;
  115. if (existingTypeEntity == null) {
  116. // Replace the existing namespace or module with the new type
  117. Debug.Assert(existingValue is NamespaceTracker);
  118. _dict[normalizedTypeName] = MemberTracker.FromMemberInfo(newType);
  119. } else {
  120. // Unify the new type with the existing type
  121. _dict[normalizedTypeName] = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType));
  122. }
  123. }
  124. }
  125. }
  126. /// <summary>
  127. /// Loads all the types from all assemblies that contribute to the current namespace (but not child namespaces)
  128. /// </summary>
  129. private void LoadAllTypes() {
  130. foreach (TypeNames typeNameList in _typeNames.Values) {
  131. foreach (string typeName in typeNameList.GetNormalizedTypeNames()) {
  132. object value;
  133. if (!TryGetValue(typeName, out value)) {
  134. Debug.Assert(false, "We should never get here as TryGetMember should raise a TypeLoadException");
  135. throw new TypeLoadException(typeName);
  136. }
  137. }
  138. }
  139. }
  140. #endregion
  141. public override string Name {
  142. get {
  143. return _fullName;
  144. }
  145. }
  146. protected void DiscoverAllTypes(Assembly assem) {
  147. // lock is held when this is called
  148. Assert.NotNull(assem);
  149. NamespaceTracker previousPackage = null;
  150. string previousFullNamespace = String.Empty; // Note that String.Empty is not a valid namespace
  151. foreach (TypeName typeName in AssemblyTypeNames.GetTypeNames(assem, _topPackage.DomainManager.Configuration.PrivateBinding)) {
  152. NamespaceTracker package;
  153. Debug.Assert(typeName.Namespace != String.Empty);
  154. if (typeName.Namespace == previousFullNamespace) {
  155. // We have a cache hit. We dont need to call GetOrMakePackageHierarchy (which generates
  156. // a fair amount of temporary substrings)
  157. package = previousPackage;
  158. } else {
  159. package = GetOrMakePackageHierarchy(assem, typeName.Namespace);
  160. previousFullNamespace = typeName.Namespace;
  161. previousPackage = package;
  162. }
  163. package.AddTypeName(typeName.Name, assem);
  164. }
  165. }
  166. /// <summary>
  167. /// Populates the tree with nodes for each part of the namespace
  168. /// </summary>
  169. /// <param name="assem"></param>
  170. /// <param name="fullNamespace">Full namespace name. It can be null (for top-level types)</param>
  171. /// <returns></returns>
  172. private NamespaceTracker GetOrMakePackageHierarchy(Assembly assem, string fullNamespace) {
  173. // lock is held when this is called
  174. Assert.NotNull(assem);
  175. if (fullNamespace == null) {
  176. // null is the top-level namespace
  177. return this;
  178. }
  179. NamespaceTracker ret = this;
  180. string[] pieces = fullNamespace.Split(Type.Delimiter);
  181. for (int i = 0; i < pieces.Length; i++) {
  182. ret = ret.GetOrMakeChildPackage(pieces[i], assem);
  183. }
  184. return ret;
  185. }
  186. /// <summary>
  187. /// As a fallback, so if the type does exist in any assembly. This would happen if a new type was added
  188. /// that was not in the hardcoded list of types.
  189. /// This code is not accurate because:
  190. /// 1. We dont deal with generic types (TypeCollision).
  191. /// 2. Previous calls to GetCustomMemberNames (eg. "from foo import *" in Python) would not have included this type.
  192. /// 3. This does not deal with new namespaces added to the assembly
  193. /// </summary>
  194. private MemberTracker CheckForUnlistedType(string nameString) {
  195. Assert.NotNull(nameString);
  196. string fullTypeName = GetFullChildName(nameString);
  197. foreach (Assembly assem in _packageAssemblies) {
  198. Type type = assem.GetType(fullTypeName, false);
  199. if (type == null || type.IsNested()) {
  200. continue;
  201. }
  202. bool publishType = type.IsPublic || _topPackage.DomainManager.Configuration.PrivateBinding;
  203. if (!publishType) {
  204. continue;
  205. }
  206. // We dont use TypeCollision.UpdateTypeEntity here because we do not handle generic type names
  207. return ReflectionCache.GetTypeTracker(type);
  208. }
  209. return null;
  210. }
  211. #region IAttributesCollection Members
  212. public bool TryGetValue(string name, out object value) {
  213. MemberTracker tmp;
  214. bool res = TryGetValue(name, out tmp);
  215. value = tmp;
  216. return res;
  217. }
  218. public bool TryGetValue(string name, out MemberTracker value) {
  219. lock (_topPackage.HierarchyLock) {
  220. LoadNamespaces();
  221. if (_dict.TryGetValue(name, out value)) {
  222. return true;
  223. }
  224. MemberTracker existingTypeEntity = null;
  225. if (name.IndexOf(Type.Delimiter) != -1) {
  226. value = null;
  227. return false;
  228. }
  229. // Look up the type names and load the type if its name exists
  230. foreach (KeyValuePair<Assembly, TypeNames> kvp in _typeNames) {
  231. if (!kvp.Value.Contains(name)) {
  232. continue;
  233. }
  234. existingTypeEntity = kvp.Value.UpdateTypeEntity((TypeTracker)existingTypeEntity, name);
  235. }
  236. if (existingTypeEntity == null) {
  237. existingTypeEntity = CheckForUnlistedType(name);
  238. }
  239. if (existingTypeEntity != null) {
  240. _dict[name] = existingTypeEntity;
  241. value = existingTypeEntity;
  242. return true;
  243. }
  244. return false;
  245. }
  246. }
  247. public bool ContainsKey(string name) {
  248. object dummy;
  249. return TryGetValue(name, out dummy);
  250. }
  251. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")]
  252. public object this[string name] {
  253. get {
  254. object res;
  255. if (TryGetValue(name, out res)) {
  256. return res;
  257. }
  258. throw new KeyNotFoundException();
  259. }
  260. }
  261. public int Count {
  262. get { return _dict.Count; }
  263. }
  264. public ICollection<string> Keys {
  265. get {
  266. LoadNamespaces();
  267. lock (_topPackage.HierarchyLock) {
  268. var res = new List<string>();
  269. return (ICollection<string>)AddKeys(res);
  270. }
  271. }
  272. }
  273. private IList AddKeys(IList res) {
  274. foreach (string s in _dict.Keys) {
  275. res.Add(s);
  276. }
  277. foreach (KeyValuePair<Assembly, TypeNames> kvp in _typeNames) {
  278. foreach (string typeName in kvp.Value.GetNormalizedTypeNames()) {
  279. if (!res.Contains(typeName)) {
  280. res.Add(typeName);
  281. }
  282. }
  283. }
  284. return res;
  285. }
  286. #endregion
  287. #region IEnumerable<KeyValuePair<string, object>> Members
  288. [Pure]
  289. public IEnumerator<KeyValuePair<string, object>> GetEnumerator() {
  290. foreach (var key in Keys) {
  291. yield return new KeyValuePair<string, object>(key, this[key]);
  292. }
  293. }
  294. #endregion
  295. [Pure]
  296. IEnumerator IEnumerable.GetEnumerator() {
  297. foreach (var key in Keys) {
  298. yield return new KeyValuePair<string, object>(key, this[key]);
  299. }
  300. }
  301. public IList<Assembly> PackageAssemblies {
  302. get {
  303. LoadNamespaces();
  304. return _packageAssemblies;
  305. }
  306. }
  307. protected virtual void LoadNamespaces() {
  308. if (_topPackage != null) {
  309. _topPackage.LoadNamespaces();
  310. }
  311. }
  312. protected void SetTopPackage(TopNamespaceTracker pkg) {
  313. Assert.NotNull(pkg);
  314. _topPackage = pkg;
  315. }
  316. /// <summary>
  317. /// This stores all the public non-nested type names in a single namespace and from a single assembly.
  318. /// This allows inspection of the namespace without eagerly loading all the types. Eagerly loading
  319. /// types slows down startup, increases working set, and is semantically incorrect as it can trigger
  320. /// TypeLoadExceptions sooner than required.
  321. /// </summary>
  322. internal class TypeNames {
  323. List<string> _simpleTypeNames = new List<string>();
  324. Dictionary<string, List<string>> _genericTypeNames = new Dictionary<string, List<string>>();
  325. Assembly _assembly;
  326. string _fullNamespace;
  327. internal TypeNames(Assembly assembly, string fullNamespace) {
  328. _assembly = assembly;
  329. _fullNamespace = fullNamespace;
  330. }
  331. internal bool Contains(string normalizedTypeName) {
  332. Debug.Assert(normalizedTypeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  333. Debug.Assert(ReflectionUtils.GetNormalizedTypeName(normalizedTypeName) == normalizedTypeName);
  334. return _simpleTypeNames.Contains(normalizedTypeName) || _genericTypeNames.ContainsKey(normalizedTypeName);
  335. }
  336. internal MemberTracker UpdateTypeEntity(TypeTracker existingTypeEntity, string normalizedTypeName) {
  337. Debug.Assert(normalizedTypeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  338. Debug.Assert(ReflectionUtils.GetNormalizedTypeName(normalizedTypeName) == normalizedTypeName);
  339. // Look for a non-generic type
  340. if (_simpleTypeNames.Contains(normalizedTypeName)) {
  341. Type newType = LoadType(_assembly, GetFullChildName(normalizedTypeName));
  342. if (newType != null) {
  343. existingTypeEntity = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType));
  344. }
  345. }
  346. // Look for generic types
  347. if (_genericTypeNames.ContainsKey(normalizedTypeName)) {
  348. List<string> actualNames = _genericTypeNames[normalizedTypeName];
  349. foreach (string actualName in actualNames) {
  350. Type newType = LoadType(_assembly, GetFullChildName(actualName));
  351. if (newType != null) {
  352. existingTypeEntity = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType));
  353. }
  354. }
  355. }
  356. return existingTypeEntity;
  357. }
  358. internal void AddTypeName(string typeName) {
  359. Debug.Assert(typeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  360. string normalizedName = ReflectionUtils.GetNormalizedTypeName(typeName);
  361. if (normalizedName == typeName) {
  362. _simpleTypeNames.Add(typeName);
  363. } else {
  364. List<string> actualNames;
  365. if (_genericTypeNames.ContainsKey(normalizedName)) {
  366. actualNames = _genericTypeNames[normalizedName];
  367. } else {
  368. actualNames = new List<string>();
  369. _genericTypeNames[normalizedName] = actualNames;
  370. }
  371. actualNames.Add(typeName);
  372. }
  373. }
  374. string GetFullChildName(string childName) {
  375. Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  376. if (_fullNamespace == null) {
  377. return childName;
  378. }
  379. return _fullNamespace + Type.Delimiter + childName;
  380. }
  381. internal ICollection<string> GetNormalizedTypeNames() {
  382. List<string> normalizedTypeNames = new List<string>();
  383. normalizedTypeNames.AddRange(_simpleTypeNames);
  384. normalizedTypeNames.AddRange(_genericTypeNames.Keys);
  385. return normalizedTypeNames;
  386. }
  387. }
  388. public int Id {
  389. get {
  390. return _id;
  391. }
  392. }
  393. #region IMembersList Members
  394. public IList<string> GetMemberNames() {
  395. LoadNamespaces();
  396. lock (_topPackage.HierarchyLock) {
  397. List<string> res = new List<string>();
  398. AddKeys(res);
  399. res.Sort();
  400. return res;
  401. }
  402. }
  403. #endregion
  404. public override TrackerTypes MemberType {
  405. get { return TrackerTypes.Namespace; }
  406. }
  407. public override Type DeclaringType {
  408. get { return null; }
  409. }
  410. private void UpdateId() {
  411. _id = Interlocked.Increment(ref _masterId);
  412. }
  413. protected void UpdateSubtreeIds() {
  414. // lock is held when this is called
  415. UpdateId();
  416. foreach (KeyValuePair<string, MemberTracker> kvp in _dict) {
  417. NamespaceTracker ns = kvp.Value as NamespaceTracker;
  418. if (ns != null) {
  419. ns.UpdateSubtreeIds();
  420. }
  421. }
  422. }
  423. }
  424. }