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