PageRenderTime 35ms CodeModel.GetById 14ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 1ms

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

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