/DLR/Microsoft.Scripting/Runtime/StringDictionaryExpando.cs

# · C# · 166 lines · 128 code · 20 blank · 18 comment · 2 complexity · 53d6873d23ba9009484ed4305ed6c03f 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. using System.Linq.Expressions;
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Dynamic;
  19. using Microsoft.Scripting.Utils;
  20. using System.Collections;
  21. namespace Microsoft.Scripting.Runtime {
  22. /// <summary>
  23. /// Exposes a IDictionary[string, object] as a dynamic object. Gets/sets/deletes turn
  24. /// into accesses on the underlying dictionary.
  25. /// </summary>
  26. public sealed class StringDictionaryExpando : IDynamicMetaObjectProvider {
  27. private readonly IDictionary<string, object> _data;
  28. internal static readonly object _getFailed = new object();
  29. public StringDictionaryExpando(IDictionary<string, object> data) {
  30. _data = data;
  31. }
  32. public IDictionary<string, object> Dictionary {
  33. get {
  34. return _data;
  35. }
  36. }
  37. private static object TryGetMember(object adapter, string name) {
  38. object result;
  39. if (((StringDictionaryExpando)adapter)._data.TryGetValue(name, out result)) {
  40. return result;
  41. }
  42. return _getFailed;
  43. }
  44. private static void TrySetMember(object adapter, string name, object value) {
  45. ((StringDictionaryExpando)adapter)._data[name] = value;
  46. }
  47. private static bool TryDeleteMember(object adapter, string name) {
  48. return ((StringDictionaryExpando)adapter)._data.Remove(name);
  49. }
  50. #region IDynamicMetaObjectProvider Members
  51. DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) {
  52. return new DictionaryExpandoMetaObject(parameter, this, _data.Keys, TryGetMember, TrySetMember, TryDeleteMember);
  53. }
  54. #endregion
  55. }
  56. internal sealed class DictionaryExpandoMetaObject : DynamicMetaObject {
  57. private readonly Func<object, string, object> _getMember;
  58. private readonly Action<object, string, object> _setMember;
  59. private readonly Func<object, string, bool> _deleteMember;
  60. private readonly IEnumerable _keys;
  61. public DictionaryExpandoMetaObject(Expression parameter, object storage, IEnumerable keys, Func<object, string, object> getMember, Action<object, string, object> setMember, Func<object, string, bool> deleteMember)
  62. : base(parameter, BindingRestrictions.Empty, storage) {
  63. _getMember = getMember;
  64. _setMember = setMember;
  65. _deleteMember = deleteMember;
  66. _keys = keys;
  67. }
  68. public override DynamicMetaObject BindGetMember(GetMemberBinder binder) {
  69. return DynamicTryGetMember(binder.Name,
  70. binder.FallbackGetMember(this).Expression,
  71. (tmp) => tmp
  72. );
  73. }
  74. public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) {
  75. return DynamicTryGetMember(binder.Name,
  76. binder.FallbackInvokeMember(this, args).Expression,
  77. (tmp) => binder.FallbackInvoke(new DynamicMetaObject(tmp, BindingRestrictions.Empty), args, null).Expression
  78. );
  79. }
  80. private DynamicMetaObject DynamicTryGetMember(string name, Expression fallback, Func<Expression, Expression> resultOp) {
  81. var tmp = Expression.Parameter(typeof(object));
  82. return new DynamicMetaObject(
  83. Expression.Block(
  84. new[] { tmp },
  85. Expression.Condition(
  86. Expression.NotEqual(
  87. Expression.Assign(
  88. tmp,
  89. Expression.Invoke(
  90. Expression.Constant(_getMember),
  91. Expression,
  92. Expression.Constant(name)
  93. )
  94. ),
  95. Expression.Constant(StringDictionaryExpando._getFailed)
  96. ),
  97. ExpressionUtils.Convert(resultOp(tmp), typeof(object)),
  98. ExpressionUtils.Convert(fallback, typeof(object))
  99. )
  100. ),
  101. GetRestrictions()
  102. );
  103. }
  104. private BindingRestrictions GetRestrictions() {
  105. return BindingRestrictions.GetTypeRestriction(Expression, Value.GetType());
  106. }
  107. public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) {
  108. return new DynamicMetaObject(
  109. Expression.Block(
  110. Expression.Invoke(
  111. Expression.Constant(_setMember),
  112. Expression,
  113. Expression.Constant(binder.Name),
  114. Expression.Convert(
  115. value.Expression,
  116. typeof(object)
  117. )
  118. ),
  119. value.Expression
  120. ),
  121. GetRestrictions()
  122. );
  123. }
  124. public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) {
  125. return new DynamicMetaObject(
  126. Expression.Condition(
  127. Expression.Invoke(
  128. Expression.Constant(_deleteMember),
  129. Expression,
  130. Expression.Constant(binder.Name)
  131. ),
  132. Expression.Default(binder.ReturnType),
  133. binder.FallbackDeleteMember(this).Expression
  134. ),
  135. GetRestrictions()
  136. );
  137. }
  138. public override IEnumerable<string> GetDynamicMemberNames() {
  139. foreach (object o in _keys) {
  140. if (o is string) {
  141. yield return (string)o;
  142. }
  143. }
  144. }
  145. }
  146. }