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

/Microsoft.Scripting/Actions/NamespaceTracker.cs

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