PageRenderTime 34ms CodeModel.GetById 21ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/Microsoft.Scripting.Core/Actions/ExpandoClass.cs

https://bitbucket.org/stefanrusek/xronos
C# | 197 lines | 112 code | 19 blank | 66 comment | 17 complexity | c516f2ebc18984f6224df8a0e6daf3dd 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
 16#if CODEPLEX_40
 17using System;
 18#else
 19using System; using Microsoft;
 20#endif
 21using System.Collections.Generic;
 22using System.Diagnostics;
 23#if CODEPLEX_40
 24using System.Linq.Expressions;
 25using System.Dynamic.Utils;
 26#else
 27using Microsoft.Linq.Expressions;
 28using Microsoft.Scripting.Utils;
 29#endif
 30using System.Text;
 31
 32#if CODEPLEX_40
 33namespace System.Dynamic {
 34#else
 35namespace Microsoft.Scripting {
 36#endif
 37    /// <summary>
 38    /// Represents a dynamically assigned class.  Expando objects which share the same 
 39    /// members will share the same class.  Classes are dynamically assigned as the
 40    /// expando object gains members.
 41    /// </summary>
 42    internal class ExpandoClass {
 43        private readonly string[] _keys;                            // list of names associated with each element in the data array, sorted
 44        private readonly int _hashCode;                             // pre-calculated hash code of all the keys the class contains
 45        private Dictionary<int, List<WeakReference>> _transitions;  // cached transitions
 46
 47        private const int EmptyHashCode = 6551;                     // hash code of the empty ExpandoClass.
 48
 49        internal static ExpandoClass Empty = new ExpandoClass();    // The empty Expando class - all Expando objects start off w/ this class.
 50        
 51        /// <summary>
 52        /// Constructs the empty ExpandoClass.  This is the class used when an
 53        /// empty Expando object is initially constructed.
 54        /// </summary>
 55        internal ExpandoClass() {
 56            _hashCode = EmptyHashCode;
 57            _keys = new string[0];
 58        }
 59
 60        /// <summary>
 61        /// Constructs a new ExpandoClass that can hold onto the specified keys.  The
 62        /// keys must be sorted ordinally.  The hash code must be precalculated for 
 63        /// the keys.
 64        /// </summary>
 65        internal ExpandoClass(string[] keys, int hashCode) {
 66            _hashCode = hashCode;
 67            _keys = keys;
 68        }
 69
 70        /// <summary>
 71        /// Finds or creates a new ExpandoClass given the existing set of keys
 72        /// in this ExpandoClass plus the new key to be added. Members in an
 73        /// ExpandoClass are always stored case sensitively.
 74        /// </summary>
 75        internal ExpandoClass FindNewClass(string newKey) {
 76            // just XOR the newKey hash code 
 77            int hashCode = _hashCode ^ newKey.GetHashCode();
 78
 79            lock (this) {
 80                List<WeakReference> infos = GetTransitionList(hashCode);
 81
 82                for (int i = 0; i < infos.Count; i++) {
 83                    ExpandoClass klass = infos[i].Target as ExpandoClass;
 84                    if (klass == null) {
 85                        infos.RemoveAt(i);
 86                        i--;
 87                        continue;
 88                    }
 89
 90                    if (string.Equals(klass._keys[klass._keys.Length - 1], newKey, StringComparison.Ordinal)) {
 91                        // the new key is the key we added in this transition
 92                        return klass;
 93                    }
 94                }
 95
 96                // no applicable transition, create a new one
 97                string[] keys = new string[_keys.Length + 1];
 98                Array.Copy(_keys, keys, _keys.Length);
 99                keys[_keys.Length] = newKey;
100                ExpandoClass ec = new ExpandoClass(keys, hashCode);
101
102                infos.Add(new WeakReference(ec));
103                return ec;
104            }
105        }
106
107        /// <summary>
108        /// Gets the lists of transitions that are valid from this ExpandoClass
109        /// to an ExpandoClass whos keys hash to the apporopriate hash code.
110        /// </summary>
111        private List<WeakReference> GetTransitionList(int hashCode) {
112            if (_transitions == null) {
113                _transitions = new Dictionary<int, List<WeakReference>>();
114            }
115
116            List<WeakReference> infos;
117            if (!_transitions.TryGetValue(hashCode, out infos)) {
118                _transitions[hashCode] = infos = new List<WeakReference>();
119            }
120
121            return infos;
122        }
123
124        /// <summary>
125        /// Gets the index at which the value should be stored for the specified name.
126        /// </summary>
127        internal int GetValueIndex(string name, bool caseInsensitive, ExpandoObject obj) {
128            if (caseInsensitive) {
129                return GetValueIndexCaseInsensitive(name, obj);
130            } else {
131                return GetValueIndexCaseSensitive(name);
132            }
133        }
134
135        /// <summary>
136        /// Gets the index at which the value should be stored for the specified name
137        /// case sensitively. Returns the index even if the member is marked as deleted.
138        /// </summary>
139        internal int GetValueIndexCaseSensitive(string name) {
140            for (int i = 0; i < _keys.Length; i++) {
141                if (string.Equals(
142                    _keys[i],
143                    name,
144                    StringComparison.Ordinal)) {
145                    return i;
146                }
147            }
148            return ExpandoObject.NoMatch;
149        }
150
151        /// <summary>
152        /// Gets the index at which the value should be stored for the specified name,
153        /// the method is only used in the case-insensitive case.
154        /// </summary>
155        /// <param name="name">the name of the member</param>
156        /// <param name="obj">The ExpandoObject associated with the class
157        /// that is used to check if a member has been deleted.</param>
158        /// <returns>
159        /// the exact match if there is one
160        /// if there is exactly one member with case insensitive match, return it
161        /// otherwise we throw AmbiguousMatchException.
162        /// </returns>
163        private int GetValueIndexCaseInsensitive(string name, ExpandoObject obj) {
164            int caseInsensitiveMatch = ExpandoObject.NoMatch; //the location of the case-insensitive matching member
165            lock (obj.LockObject) {
166                for (int i = _keys.Length - 1; i >= 0; i--) {
167                    if (string.Equals(
168                        _keys[i],
169                        name,
170                        StringComparison.OrdinalIgnoreCase)) {
171                        //if the matching member is deleted, continue searching
172                        if (!obj.IsDeletedMember(i)) {
173                            if (caseInsensitiveMatch == ExpandoObject.NoMatch) {
174                                caseInsensitiveMatch = i;
175                            } else {
176                                //Ambigous match, stop searching
177                                return ExpandoObject.AmbiguousMatchFound;
178                            }
179                        }
180                    }
181                }
182            }
183            //There is exactly one member with case insensitive match.
184            return caseInsensitiveMatch;
185        }
186
187        /// <summary>
188        /// Gets the names of the keys that can be stored in the Expando class.  The
189        /// list is sorted ordinally.
190        /// </summary>
191        internal string[] Keys {
192            get {
193                return _keys;
194            }
195        }
196    }
197}