PageRenderTime 65ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_2_0/Src/Microsoft.Scripting/Actions/NamespaceTracker.cs

#
C# | 616 lines | 438 code | 115 blank | 63 comment | 64 complexity | 251d4e40615f74980c08ee017cd63211 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 Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System; using Microsoft;
  16. using System.Collections;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using Microsoft.Linq.Expressions;
  20. using System.Reflection;
  21. using Microsoft.Scripting.Utils;
  22. using System.Threading;
  23. using Microsoft.Contracts;
  24. using Microsoft.Scripting.Runtime;
  25. namespace Microsoft.Scripting.Actions {
  26. public class AssemblyLoadedEventArgs : EventArgs {
  27. private Assembly _assembly;
  28. public AssemblyLoadedEventArgs(Assembly assembly) {
  29. _assembly = assembly;
  30. }
  31. public Assembly Assembly {
  32. get {
  33. return _assembly;
  34. }
  35. }
  36. }
  37. /// <summary>
  38. /// NamespaceTracker represent a CLS namespace.
  39. /// </summary>
  40. public class NamespaceTracker : MemberTracker, IOldDynamicObject, IAttributesCollection, IMembersList {
  41. // _dict contains all the currently loaded entries. However, there may be pending types that have
  42. // not yet been loaded in _typeNames
  43. internal Dictionary<string, MemberTracker> _dict = new Dictionary<string, MemberTracker>();
  44. internal readonly List<Assembly> _packageAssemblies = new List<Assembly>();
  45. internal readonly Dictionary<Assembly, TypeNames> _typeNames = new Dictionary<Assembly, TypeNames>();
  46. private string _fullName; // null for the TopReflectedPackage
  47. private TopNamespaceTracker _topPackage;
  48. private int _id;
  49. private static int _masterId;
  50. #region Protected API Surface
  51. private NamespaceTracker() {
  52. UpdateId();
  53. }
  54. [Confined]
  55. public override string ToString() {
  56. return base.ToString() + ":" + _fullName;
  57. }
  58. protected NamespaceTracker(string name)
  59. : this() {
  60. _fullName = name;
  61. }
  62. #endregion
  63. #region Internal API Surface
  64. internal NamespaceTracker GetOrMakeChildPackage(string childName, Assembly assem) {
  65. // lock is held when this is called
  66. Assert.NotNull(childName, assem);
  67. Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  68. Debug.Assert(_packageAssemblies.Contains(assem)); // Parent namespace must contain all the assemblies of the child
  69. MemberTracker ret;
  70. if (_dict.TryGetValue(childName, out ret)) {
  71. // If we have a module, then we add the assembly to the InnerModule
  72. // If it's not a module, we'll wipe it out below, eg "def System(): pass" then
  73. // "import System" will result in the namespace being visible.
  74. NamespaceTracker package = ret as NamespaceTracker;
  75. if (package != null) {
  76. if (!package._packageAssemblies.Contains(assem)) {
  77. package._packageAssemblies.Add(assem);
  78. package.UpdateId();
  79. }
  80. return package;
  81. }
  82. }
  83. return MakeChildPackage(childName, assem);
  84. }
  85. private NamespaceTracker MakeChildPackage(string childName, Assembly assem) {
  86. // lock is held when this is called
  87. Assert.NotNull(childName, assem);
  88. NamespaceTracker rp = new NamespaceTracker();
  89. rp.SetTopPackage(_topPackage);
  90. rp._packageAssemblies.Add(assem);
  91. rp._fullName = GetFullChildName(childName);
  92. _dict[childName] = rp;
  93. return rp;
  94. }
  95. private string GetFullChildName(string childName) {
  96. Assert.NotNull(childName);
  97. Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  98. if (_fullName == null) {
  99. return childName;
  100. }
  101. return _fullName + Type.Delimiter + childName;
  102. }
  103. private static Type LoadType(Assembly assem, string fullTypeName) {
  104. Assert.NotNull(assem, fullTypeName);
  105. Type type = assem.GetType(fullTypeName);
  106. // We should ignore nested types. They will be loaded when the containing type is loaded
  107. Debug.Assert(!ReflectionUtils.IsNested(type));
  108. return type;
  109. }
  110. internal void AddTypeName(string typeName, Assembly assem) {
  111. // lock is held when this is called
  112. Assert.NotNull(typeName, assem);
  113. Debug.Assert(typeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  114. if (!_typeNames.ContainsKey(assem)) {
  115. _typeNames[assem] = new TypeNames(assem, _fullName);
  116. }
  117. _typeNames[assem].AddTypeName(typeName);
  118. string normalizedTypeName = ReflectionUtils.GetNormalizedTypeName(typeName);
  119. if (_dict.ContainsKey(normalizedTypeName)) {
  120. // A similarly named type, namespace, or module already exists.
  121. Type newType = LoadType(assem, GetFullChildName(typeName));
  122. object existingValue = _dict[normalizedTypeName];
  123. TypeTracker existingTypeEntity = existingValue as TypeTracker;
  124. if (existingTypeEntity == null) {
  125. // Replace the existing namespace or module with the new type
  126. Debug.Assert(existingValue is NamespaceTracker);
  127. _dict[normalizedTypeName] = MemberTracker.FromMemberInfo(newType);
  128. } else {
  129. // Unify the new type with the existing type
  130. _dict[normalizedTypeName] = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType));
  131. }
  132. return;
  133. }
  134. }
  135. /// <summary>
  136. /// Loads all the types from all assemblies that contribute to the current namespace (but not child namespaces)
  137. /// </summary>
  138. private void LoadAllTypes() {
  139. foreach (TypeNames typeNameList in _typeNames.Values) {
  140. foreach (string typeName in typeNameList.GetNormalizedTypeNames()) {
  141. object value;
  142. if (!TryGetValue(SymbolTable.StringToId(typeName), out value)) {
  143. Debug.Assert(false, "We should never get here as TryGetMember should raise a TypeLoadException");
  144. throw new TypeLoadException(typeName);
  145. }
  146. }
  147. }
  148. }
  149. #endregion
  150. public override string Name {
  151. get {
  152. return _fullName;
  153. }
  154. }
  155. protected void DiscoverAllTypes(Assembly assem) {
  156. // lock is held when this is called
  157. Assert.NotNull(assem);
  158. NamespaceTracker previousPackage = null;
  159. string previousFullNamespace = String.Empty; // Note that String.Empty is not a valid namespace
  160. foreach (TypeName typeName in AssemblyTypeNames.GetTypeNames(assem, _topPackage.DomainManager.Configuration.PrivateBinding)) {
  161. NamespaceTracker package;
  162. Debug.Assert(typeName.Namespace != String.Empty);
  163. if (typeName.Namespace == previousFullNamespace) {
  164. // We have a cache hit. We dont need to call GetOrMakePackageHierarchy (which generates
  165. // a fair amount of temporary substrings)
  166. package = previousPackage;
  167. } else {
  168. package = GetOrMakePackageHierarchy(assem, typeName.Namespace);
  169. previousFullNamespace = typeName.Namespace;
  170. previousPackage = package;
  171. }
  172. package.AddTypeName(typeName.Name, assem);
  173. }
  174. }
  175. /// <summary>
  176. /// Populates the tree with nodes for each part of the namespace
  177. /// </summary>
  178. /// <param name="assem"></param>
  179. /// <param name="fullNamespace">Full namespace name. It can be null (for top-level types)</param>
  180. /// <returns></returns>
  181. private NamespaceTracker GetOrMakePackageHierarchy(Assembly assem, string fullNamespace) {
  182. // lock is held when this is called
  183. Assert.NotNull(assem);
  184. if (fullNamespace == null) {
  185. // null is the top-level namespace
  186. return this;
  187. }
  188. NamespaceTracker ret = this;
  189. string[] pieces = fullNamespace.Split(Type.Delimiter);
  190. for (int i = 0; i < pieces.Length; i++) {
  191. ret = ret.GetOrMakeChildPackage(pieces[i], assem);
  192. }
  193. return ret;
  194. }
  195. /// <summary>
  196. /// As a fallback, so if the type does exist in any assembly. This would happen if a new type was added
  197. /// that was not in the hardcoded list of types.
  198. /// This code is not accurate because:
  199. /// 1. We dont deal with generic types (TypeCollision).
  200. /// 2. Previous calls to GetCustomMemberNames (eg. "from foo import *" in Python) would not have included this type.
  201. /// 3. This does not deal with new namespaces added to the assembly
  202. /// </summary>
  203. private MemberTracker CheckForUnlistedType(string nameString) {
  204. Assert.NotNull(nameString);
  205. string fullTypeName = GetFullChildName(nameString);
  206. foreach (Assembly assem in _packageAssemblies) {
  207. Type type = assem.GetType(fullTypeName, false);
  208. if (type == null || ReflectionUtils.IsNested(type)) {
  209. continue;
  210. }
  211. bool publishType = type.IsPublic || _topPackage.DomainManager.Configuration.PrivateBinding;
  212. if (!publishType) {
  213. continue;
  214. }
  215. // We dont use TypeCollision.UpdateTypeEntity here because we do not handle generic type names
  216. return ReflectionCache.GetTypeTracker(type);
  217. }
  218. return null;
  219. }
  220. #region IAttributesCollection Members
  221. public void Add(SymbolId name, object value) {
  222. throw new InvalidOperationException();
  223. }
  224. public bool TryGetValue(SymbolId name, out object value) {
  225. MemberTracker tmp;
  226. bool res = TryGetValue(name, out tmp);
  227. value = tmp;
  228. return res;
  229. }
  230. public bool TryGetValue(SymbolId name, out MemberTracker value) {
  231. lock (this) {
  232. LoadNamespaces();
  233. if (_dict.TryGetValue(SymbolTable.IdToString(name), out value)) {
  234. return true;
  235. }
  236. MemberTracker existingTypeEntity = null;
  237. string nameString = SymbolTable.IdToString(name);
  238. if (nameString.IndexOf(Type.Delimiter) != -1) {
  239. value = null;
  240. return false;
  241. }
  242. // Look up the type names and load the type if its name exists
  243. foreach (KeyValuePair<Assembly, TypeNames> kvp in _typeNames) {
  244. if (!kvp.Value.Contains(nameString)) {
  245. continue;
  246. }
  247. existingTypeEntity = kvp.Value.UpdateTypeEntity((TypeTracker)existingTypeEntity, nameString);
  248. }
  249. if (existingTypeEntity == null) {
  250. existingTypeEntity = CheckForUnlistedType(nameString);
  251. }
  252. if (existingTypeEntity != null) {
  253. _dict[SymbolTable.IdToString(name)] = existingTypeEntity;
  254. value = existingTypeEntity;
  255. return true;
  256. }
  257. return false;
  258. }
  259. }
  260. public bool Remove(SymbolId name) {
  261. throw new InvalidOperationException();
  262. }
  263. public bool ContainsKey(SymbolId name) {
  264. object dummy;
  265. return TryGetValue(name, out dummy);
  266. }
  267. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")]
  268. public object this[SymbolId name] {
  269. get {
  270. object res;
  271. if (TryGetValue(name, out res)) {
  272. return res;
  273. }
  274. throw new KeyNotFoundException();
  275. }
  276. set {
  277. throw new InvalidOperationException();
  278. }
  279. }
  280. public IDictionary<SymbolId, object> SymbolAttributes {
  281. get {
  282. LoadNamespaces();
  283. Dictionary<SymbolId, object> res = new Dictionary<SymbolId, object>();
  284. foreach (KeyValuePair<object, object> kvp in this) {
  285. string strkey = kvp.Key as string;
  286. if (strkey != null) {
  287. res[SymbolTable.StringToId(strkey)] = kvp.Value;
  288. }
  289. }
  290. return res;
  291. }
  292. }
  293. public void AddObjectKey(object name, object value) {
  294. throw new InvalidOperationException();
  295. }
  296. public bool TryGetObjectValue(object name, out object value) {
  297. string str = name as string;
  298. if (str != null) {
  299. return TryGetValue(SymbolTable.StringToId(str), out value);
  300. }
  301. value = null;
  302. return false;
  303. }
  304. public bool RemoveObjectKey(object name) {
  305. throw new InvalidOperationException();
  306. }
  307. public bool ContainsObjectKey(object name) {
  308. object dummy;
  309. return TryGetObjectValue(name, out dummy);
  310. }
  311. public IDictionary<object, object> AsObjectKeyedDictionary() {
  312. LoadNamespaces();
  313. lock (this) {
  314. Dictionary<object, object> res = new Dictionary<object, object>();
  315. foreach (KeyValuePair<string, MemberTracker> kvp in _dict) {
  316. res[kvp.Key] = kvp.Value;
  317. }
  318. return res;
  319. }
  320. }
  321. public int Count {
  322. get { return _dict.Count; }
  323. }
  324. public ICollection<object> Keys {
  325. get {
  326. LoadNamespaces();
  327. lock (this) {
  328. List<object> res = new List<object>();
  329. foreach (string s in _dict.Keys) res.Add(s);
  330. foreach (KeyValuePair<Assembly, TypeNames> kvp in _typeNames) {
  331. foreach (string typeName in kvp.Value.GetNormalizedTypeNames()) {
  332. if (!res.Contains(typeName)) {
  333. res.Add(typeName);
  334. }
  335. }
  336. }
  337. res.Sort();
  338. return res;
  339. }
  340. }
  341. }
  342. #endregion
  343. #region IEnumerable<KeyValuePair<object,object>> Members
  344. [Pure]
  345. public IEnumerator<KeyValuePair<object, object>> GetEnumerator() {
  346. foreach (object key in Keys) {
  347. yield return new KeyValuePair<object, object>(key, this[SymbolTable.StringToId((string)key)]);
  348. }
  349. }
  350. #endregion
  351. #region IEnumerable Members
  352. [Pure]
  353. IEnumerator IEnumerable.GetEnumerator() {
  354. foreach (object key in Keys) {
  355. yield return new KeyValuePair<object, object>(key, this[SymbolTable.StringToId((string)key)]);
  356. }
  357. }
  358. #endregion
  359. public IList<Assembly> PackageAssemblies {
  360. get {
  361. LoadNamespaces();
  362. return _packageAssemblies;
  363. }
  364. }
  365. protected virtual void LoadNamespaces() {
  366. if (_topPackage != null) {
  367. _topPackage.LoadNamespaces();
  368. }
  369. }
  370. protected void SetTopPackage(TopNamespaceTracker pkg) {
  371. Assert.NotNull(pkg);
  372. _topPackage = pkg;
  373. }
  374. /// <summary>
  375. /// This stores all the public non-nested type names in a single namespace and from a single assembly.
  376. /// This allows inspection of the namespace without eagerly loading all the types. Eagerly loading
  377. /// types slows down startup, increases working set, and is semantically incorrect as it can trigger
  378. /// TypeLoadExceptions sooner than required.
  379. /// </summary>
  380. internal class TypeNames {
  381. List<string> _simpleTypeNames = new List<string>();
  382. Dictionary<string, List<string>> _genericTypeNames = new Dictionary<string, List<string>>();
  383. Assembly _assembly;
  384. string _fullNamespace;
  385. internal TypeNames(Assembly assembly, string fullNamespace) {
  386. _assembly = assembly;
  387. _fullNamespace = fullNamespace;
  388. }
  389. internal bool Contains(string normalizedTypeName) {
  390. Debug.Assert(normalizedTypeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  391. Debug.Assert(ReflectionUtils.GetNormalizedTypeName(normalizedTypeName) == normalizedTypeName);
  392. return _simpleTypeNames.Contains(normalizedTypeName) || _genericTypeNames.ContainsKey(normalizedTypeName);
  393. }
  394. internal MemberTracker UpdateTypeEntity(TypeTracker existingTypeEntity, string normalizedTypeName) {
  395. Debug.Assert(normalizedTypeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  396. Debug.Assert(ReflectionUtils.GetNormalizedTypeName(normalizedTypeName) == normalizedTypeName);
  397. // Look for a non-generic type
  398. if (_simpleTypeNames.Contains(normalizedTypeName)) {
  399. Type newType = LoadType(_assembly, GetFullChildName(normalizedTypeName));
  400. existingTypeEntity = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType));
  401. }
  402. // Look for generic types
  403. if (_genericTypeNames.ContainsKey(normalizedTypeName)) {
  404. List<string> actualNames = _genericTypeNames[normalizedTypeName];
  405. foreach (string actualName in actualNames) {
  406. Type newType = LoadType(_assembly, GetFullChildName(actualName));
  407. existingTypeEntity = TypeGroup.UpdateTypeEntity(existingTypeEntity, ReflectionCache.GetTypeTracker(newType));
  408. }
  409. }
  410. return existingTypeEntity;
  411. }
  412. internal void AddTypeName(string typeName) {
  413. Debug.Assert(typeName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  414. string normalizedName = ReflectionUtils.GetNormalizedTypeName(typeName);
  415. if (normalizedName == typeName) {
  416. _simpleTypeNames.Add(typeName);
  417. } else {
  418. List<string> actualNames;
  419. if (_genericTypeNames.ContainsKey(normalizedName)) {
  420. actualNames = _genericTypeNames[normalizedName];
  421. } else {
  422. actualNames = new List<string>();
  423. _genericTypeNames[normalizedName] = actualNames;
  424. }
  425. actualNames.Add(typeName);
  426. }
  427. }
  428. string GetFullChildName(string childName) {
  429. Debug.Assert(childName.IndexOf(Type.Delimiter) == -1); // This is the simple name, not the full name
  430. if (_fullNamespace == null) {
  431. return childName;
  432. }
  433. return _fullNamespace + Type.Delimiter + childName;
  434. }
  435. internal ICollection<string> GetNormalizedTypeNames() {
  436. List<string> normalizedTypeNames = new List<string>();
  437. normalizedTypeNames.AddRange(_simpleTypeNames);
  438. normalizedTypeNames.AddRange(_genericTypeNames.Keys);
  439. return normalizedTypeNames;
  440. }
  441. }
  442. #region IOldDynamicObject Members
  443. public RuleBuilder<T> GetRule<T>(OldDynamicAction action, CodeContext context, object[] args) where T : class {
  444. if (action.Kind == DynamicActionKind.GetMember) {
  445. return MakeGetMemberRule<T>((OldGetMemberAction)action, context);
  446. }
  447. return null;
  448. }
  449. private RuleBuilder<T> MakeGetMemberRule<T>(OldGetMemberAction action, CodeContext context) where T : class {
  450. object value;
  451. if (TryGetValue(action.Name, out value)) {
  452. Debug.Assert(value is MemberTracker);
  453. MemberTracker memValue = (MemberTracker)value;
  454. RuleBuilder<T> rule = new RuleBuilder<T>();
  455. rule.MakeTest(typeof(NamespaceTracker));
  456. rule.AddTest(
  457. Expression.Equal(
  458. Expression.Property(
  459. Expression.Convert(rule.Parameters[0], typeof(NamespaceTracker)),
  460. typeof(NamespaceTracker).GetProperty("Id")
  461. ),
  462. Expression.Constant(Id)
  463. )
  464. );
  465. Expression target = context.LanguageContext.Binder.ReturnMemberTracker(memValue.DeclaringType, memValue);
  466. rule.Target = rule.MakeReturn(context.LanguageContext.Binder, target);
  467. return rule;
  468. }
  469. return null;
  470. }
  471. #endregion
  472. public int Id {
  473. get {
  474. return _id;
  475. }
  476. }
  477. #region IMembersList Members
  478. public IList<object> GetMemberNames(CodeContext context) {
  479. return (IList<object>)Keys;
  480. }
  481. #endregion
  482. public override TrackerTypes MemberType {
  483. get { return TrackerTypes.Namespace; }
  484. }
  485. public override Type DeclaringType {
  486. get { return null; }
  487. }
  488. protected void UpdateId() {
  489. _id = Interlocked.Increment(ref _masterId);
  490. foreach (KeyValuePair<string, MemberTracker> kvp in _dict) {
  491. NamespaceTracker ns = kvp.Value as NamespaceTracker;
  492. if (ns == null) continue;
  493. lock (ns) {
  494. // namespace trackers are trees so we always take this
  495. // in order
  496. ns.UpdateId();
  497. }
  498. }
  499. }
  500. }
  501. }