PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

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

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