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