PageRenderTime 23ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/Microsoft.Scripting.Core/Actions/BindingRestrictions.cs

https://bitbucket.org/stefanrusek/xronos
C# | 317 lines | 203 code | 40 blank | 74 comment | 33 complexity | 6433af9aad2448356f3a6783ff7f9f81 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. using System; using Microsoft;
  16. using System.Collections.Generic;
  17. #if CODEPLEX_40
  18. using System.Dynamic.Utils;
  19. using System.Linq.Expressions;
  20. #else
  21. using Microsoft.Scripting.Utils;
  22. using Microsoft.Linq.Expressions;
  23. #endif
  24. #if CODEPLEX_40
  25. namespace System.Dynamic {
  26. #else
  27. namespace Microsoft.Scripting {
  28. #endif
  29. /// <summary>
  30. /// Represents a set of binding restrictions on the <see cref="DynamicMetaObject"/>under which the dynamic binding is valid.
  31. /// </summary>
  32. public sealed class BindingRestrictions {
  33. private class Restriction {
  34. internal enum RestrictionKind {
  35. Type,
  36. Instance,
  37. Custom
  38. };
  39. private readonly RestrictionKind _kind;
  40. // Simplification ... for now just one kind of restriction rather than hierarchy.
  41. private readonly Expression _expression;
  42. private readonly Type _type;
  43. // We hold onto the instance here, but that's okay, because
  44. // we're binding. Once we generate the instance test however, we
  45. // should store the instance as a WeakReference
  46. private readonly object _instance;
  47. internal RestrictionKind Kind {
  48. get { return _kind; }
  49. }
  50. internal Expression Expression {
  51. get { return _expression; }
  52. }
  53. internal Type Type {
  54. get { return _type; }
  55. }
  56. internal object Instance {
  57. get { return _instance; }
  58. }
  59. internal Restriction(Expression parameter, Type type) {
  60. _kind = RestrictionKind.Type;
  61. _expression = parameter;
  62. _type = type;
  63. }
  64. internal Restriction(Expression parameter, object instance) {
  65. _kind = RestrictionKind.Instance;
  66. _expression = parameter;
  67. _instance = instance;
  68. }
  69. internal Restriction(Expression expression) {
  70. _kind = RestrictionKind.Custom;
  71. _expression = expression;
  72. }
  73. public override bool Equals(object obj) {
  74. Restriction other = obj as Restriction;
  75. if (other == null) {
  76. return false;
  77. }
  78. if (other.Kind != Kind ||
  79. other.Expression != Expression) {
  80. return false;
  81. }
  82. switch (other.Kind) {
  83. case RestrictionKind.Instance:
  84. return other.Instance == Instance;
  85. case RestrictionKind.Type:
  86. return other.Type == Type;
  87. default:
  88. return false;
  89. }
  90. }
  91. public override int GetHashCode() {
  92. // lots of collisions but we don't hash Restrictions ever
  93. return (int)Kind ^ Expression.GetHashCode();
  94. }
  95. }
  96. private readonly Restriction[] _restrictions;
  97. private BindingRestrictions(params Restriction[] restrictions) {
  98. _restrictions = restrictions;
  99. }
  100. private bool IsEmpty {
  101. get {
  102. return _restrictions.Length == 0;
  103. }
  104. }
  105. /// <summary>
  106. /// Merges the set of binding restrictions with the current binding restrictions.
  107. /// </summary>
  108. /// <param name="restrictions">The set of restrictions with which to merge the current binding restrictions.</param>
  109. /// <returns>The new set of binding restrictions.</returns>
  110. public BindingRestrictions Merge(BindingRestrictions restrictions) {
  111. if (restrictions.IsEmpty) {
  112. return this;
  113. } else if (IsEmpty) {
  114. return restrictions;
  115. } else {
  116. List<Restriction> res = new List<Restriction>(_restrictions.Length + restrictions._restrictions.Length);
  117. AddRestrictions(_restrictions, res);
  118. AddRestrictions(restrictions._restrictions, res);
  119. return new BindingRestrictions(res.ToArray());
  120. }
  121. }
  122. /// <summary>
  123. /// Adds unique restrictions and doesn't add restrictions which are alerady present
  124. /// </summary>
  125. private static void AddRestrictions(Restriction[] list, List<Restriction> res) {
  126. foreach (Restriction r in list) {
  127. bool found = false;
  128. for (int j = 0; j < res.Count; j++) {
  129. if (res[j].Equals(r)) {
  130. found = true;
  131. }
  132. }
  133. if (!found) {
  134. res.Add(r);
  135. }
  136. }
  137. }
  138. /// <summary>
  139. /// Represents an empty set of binding restrictions. This field is read only.
  140. /// </summary>
  141. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2104:DoNotDeclareReadOnlyMutableReferenceTypes")]
  142. public static readonly BindingRestrictions Empty = new BindingRestrictions();
  143. /// <summary>
  144. /// Creates the binding restriction that check the expression for runtime type identity.
  145. /// </summary>
  146. /// <param name="expression">The expression to test.</param>
  147. /// <param name="type">The exact type to test.</param>
  148. /// <returns>The new binding restrictions.</returns>
  149. public static BindingRestrictions GetTypeRestriction(Expression expression, Type type) {
  150. ContractUtils.RequiresNotNull(expression, "expression");
  151. ContractUtils.RequiresNotNull(type, "type");
  152. return new BindingRestrictions(new Restriction(expression, type));
  153. }
  154. /// <summary>
  155. /// The method takes a DynamicMetaObject, and returns an instance restriction for testing null if the object
  156. /// holds a null value, otherwise returns a type restriction.
  157. /// </summary>
  158. internal static BindingRestrictions GetTypeRestriction(DynamicMetaObject obj) {
  159. if (obj.Value == null && obj.HasValue) {
  160. return BindingRestrictions.GetInstanceRestriction(obj.Expression, null);
  161. } else {
  162. return BindingRestrictions.GetTypeRestriction(obj.Expression, obj.LimitType);
  163. }
  164. }
  165. /// <summary>
  166. /// Creates the binding restriction that checks the expression for object instance identity.
  167. /// </summary>
  168. /// <param name="expression">The expression to test.</param>
  169. /// <param name="instance">The exact object instance to test.</param>
  170. /// <returns>The new binding restrictions.</returns>
  171. public static BindingRestrictions GetInstanceRestriction(Expression expression, object instance) {
  172. ContractUtils.RequiresNotNull(expression, "expression");
  173. return new BindingRestrictions(new Restriction(expression, instance));
  174. }
  175. /// <summary>
  176. /// Creates the binding restriction that checks the expression for arbitrary immutable properties.
  177. /// </summary>
  178. /// <param name="expression">The expression expression the restrictions.</param>
  179. /// <returns>The new binding restrictions.</returns>
  180. /// <remarks>
  181. /// By convention, the general restrictions created by this method must only test
  182. /// immutable object properties.
  183. /// </remarks>
  184. public static BindingRestrictions GetExpressionRestriction(Expression expression) {
  185. ContractUtils.RequiresNotNull(expression, "expression");
  186. ContractUtils.Requires(expression.Type == typeof(bool), "expression");
  187. return new BindingRestrictions(new Restriction(expression));
  188. }
  189. /// <summary>
  190. /// Combines binding restrictions from the list of <see cref="DynamicMetaObject"/> instances into one set of restrictions.
  191. /// </summary>
  192. /// <param name="contributingObjects">The list of <see cref="DynamicMetaObject"/> instances from which to combine restrictions.</param>
  193. /// <returns>The new set of binding restrictions.</returns>
  194. public static BindingRestrictions Combine(IList<DynamicMetaObject> contributingObjects) {
  195. BindingRestrictions res = BindingRestrictions.Empty;
  196. if (contributingObjects != null) {
  197. foreach (DynamicMetaObject mo in contributingObjects) {
  198. if (mo != null) {
  199. res = res.Merge(mo.Restrictions);
  200. }
  201. }
  202. }
  203. return res;
  204. }
  205. /// <summary>
  206. /// Creates the <see cref="Expression"/> representing the binding restrictions.
  207. /// </summary>
  208. /// <returns>The expression tree representing the restrictions.</returns>
  209. public Expression ToExpression() {
  210. // We could optimize this better, e.g. common subexpression elimination
  211. // But for now, it's good enough.
  212. Expression test = null;
  213. foreach (Restriction r in _restrictions) {
  214. Expression one;
  215. switch (r.Kind) {
  216. case Restriction.RestrictionKind.Type:
  217. one = CreateTypeRestriction(r.Expression, r.Type);
  218. break;
  219. case Restriction.RestrictionKind.Instance:
  220. one = CreateInstanceRestriction(r.Expression, r.Instance);
  221. break;
  222. case Restriction.RestrictionKind.Custom:
  223. one = r.Expression;
  224. break;
  225. default:
  226. throw new InvalidOperationException();
  227. }
  228. if (one != null) {
  229. if (test == null) {
  230. test = one;
  231. } else {
  232. test = Expression.AndAlso(test, one);
  233. }
  234. }
  235. }
  236. return test ?? Expression.Constant(true);
  237. }
  238. /// <summary>
  239. /// Creates one type identity test
  240. /// </summary>
  241. private static Expression CreateTypeRestriction(Expression expression, Type rt) {
  242. return Expression.TypeEqual(expression, rt);
  243. }
  244. private static Expression CreateInstanceRestriction(Expression expression, object value) {
  245. if (value == null) {
  246. return Expression.Equal(
  247. Expression.Convert(expression, typeof(object)),
  248. Expression.Constant(null)
  249. );
  250. }
  251. // TODO: need to add special cases for valuetypes and nullables
  252. ParameterExpression temp = Expression.Parameter(typeof(object), null);
  253. Expression init = Expression.Assign(
  254. temp,
  255. Expression.Property(
  256. Expression.Constant(new WeakReference(value)),
  257. typeof(WeakReference).GetProperty("Target")
  258. )
  259. );
  260. return Expression.Block(
  261. new ParameterExpression[] { temp },
  262. init,
  263. Expression.AndAlso(
  264. //check that WeekReference was not collected.
  265. Expression.NotEqual(
  266. temp,
  267. Expression.Constant(null)
  268. ),
  269. Expression.Equal(
  270. temp,
  271. Expression.Convert(expression, typeof(object))
  272. )
  273. )
  274. );
  275. }
  276. }
  277. }