PageRenderTime 76ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/DLR/Microsoft.Dynamic/Actions/ComboBinder.cs

#
C# | 316 lines | 229 code | 52 blank | 35 comment | 47 complexity | d52427ed3b291052e0bd16cfa04666cc MD5 | raw file
Possible License(s): BSD-3-Clause
  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 System.Runtime.CompilerServices;
  20. using System.Text;
  21. using Microsoft.Scripting.Utils;
  22. namespace Microsoft.Scripting.Actions {
  23. /// <summary>
  24. /// A binder which can combine multiple binders into a single dynamic site. The creator
  25. /// of this needs to perform the mapping of parameters, constants, and sub-site expressions
  26. /// and provide a List of BinderMappingInfo representing this data. From there the ComboBinder
  27. /// just processes the list to create the resulting code.
  28. /// </summary>
  29. public class ComboBinder : DynamicMetaObjectBinder {
  30. private readonly BinderMappingInfo[] _metaBinders;
  31. public ComboBinder(params BinderMappingInfo[] binders)
  32. : this((ICollection<BinderMappingInfo>)binders) {
  33. }
  34. public ComboBinder(ICollection<BinderMappingInfo> binders) {
  35. Assert.NotNullItems(binders);
  36. _metaBinders = ArrayUtils.ToArray(binders);
  37. }
  38. public override DynamicMetaObject Bind(DynamicMetaObject target, params DynamicMetaObject[] args) {
  39. args = ArrayUtils.Insert(target, args);
  40. List<DynamicMetaObject> results = new List<DynamicMetaObject>(_metaBinders.Length);
  41. List<Expression> steps = new List<Expression>();
  42. List<ParameterExpression> temps = new List<ParameterExpression>();
  43. BindingRestrictions restrictions = BindingRestrictions.Empty;
  44. for (int i = 0; i < _metaBinders.Length; i++) {
  45. BinderMappingInfo curBinder = _metaBinders[i];
  46. DynamicMetaObject[] tmpargs = GetArguments(args, results, i);
  47. DynamicMetaObject next = curBinder.Binder.Bind(tmpargs[0], ArrayUtils.RemoveFirst(tmpargs));
  48. if (i != 0) {
  49. // If the rule contains an embedded "update", replace it with a defer
  50. var visitor = new ReplaceUpdateVisitor { Binder = curBinder.Binder, Arguments = tmpargs };
  51. next = new DynamicMetaObject(visitor.Visit(next.Expression), next.Restrictions);
  52. }
  53. restrictions = restrictions.Merge(next.Restrictions);
  54. if (next.Expression.NodeType == ExpressionType.Throw) {
  55. // end of the line... the expression is throwing, none of the other
  56. // binders will have an opportunity to run.
  57. steps.Add(next.Expression);
  58. break;
  59. }
  60. ParameterExpression tmp = Expression.Variable(next.Expression.Type, "comboTemp" + i.ToString());
  61. temps.Add(tmp);
  62. steps.Add(Expression.Assign(tmp, next.Expression));
  63. results.Add(new DynamicMetaObject(tmp, next.Restrictions));
  64. }
  65. return new DynamicMetaObject(
  66. Expression.Block(
  67. temps.ToArray(),
  68. steps.ToArray()
  69. ),
  70. restrictions
  71. );
  72. }
  73. public override Type ReturnType {
  74. get {
  75. return _metaBinders[_metaBinders.Length - 1].Binder.ReturnType;
  76. }
  77. }
  78. private sealed class ReplaceUpdateVisitor : ExpressionVisitor {
  79. internal DynamicMetaObjectBinder Binder;
  80. internal DynamicMetaObject[] Arguments;
  81. protected override Expression VisitGoto(GotoExpression node) {
  82. if (node.Target == CallSiteBinder.UpdateLabel) {
  83. return Binder.Defer(Arguments).Expression;
  84. }
  85. return base.Visit(node);
  86. }
  87. }
  88. private DynamicMetaObject[] GetArguments(DynamicMetaObject[] args, IList<DynamicMetaObject> results, int metaBinderIndex) {
  89. BinderMappingInfo indices = _metaBinders[metaBinderIndex];
  90. DynamicMetaObject[] res = new DynamicMetaObject[indices.MappingInfo.Count];
  91. for (int i = 0; i < res.Length; i++) {
  92. ParameterMappingInfo mappingInfo = indices.MappingInfo[i];
  93. if (mappingInfo.IsAction) {
  94. // input is the result of a previous bind
  95. res[i] = results[mappingInfo.ActionIndex];
  96. } else if (mappingInfo.IsParameter) {
  97. // input is one of the original arguments
  98. res[i] = args[mappingInfo.ParameterIndex];
  99. } else {
  100. // input is a constant
  101. res[i] = new DynamicMetaObject(
  102. mappingInfo.Constant,
  103. BindingRestrictions.Empty,
  104. mappingInfo.Constant.Value
  105. );
  106. }
  107. }
  108. return res;
  109. }
  110. public override int GetHashCode() {
  111. int res = 6551;
  112. foreach (BinderMappingInfo metaBinder in _metaBinders) {
  113. res ^= metaBinder.Binder.GetHashCode();
  114. foreach (ParameterMappingInfo mapInfo in metaBinder.MappingInfo) {
  115. res ^= mapInfo.GetHashCode();
  116. }
  117. }
  118. return res;
  119. }
  120. public override bool Equals(object obj) {
  121. ComboBinder other = obj as ComboBinder;
  122. if (other != null) {
  123. if (_metaBinders.Length != other._metaBinders.Length) {
  124. return false;
  125. }
  126. for (int i = 0; i < _metaBinders.Length; i++) {
  127. BinderMappingInfo self = _metaBinders[i];
  128. BinderMappingInfo otherBinders = other._metaBinders[i];
  129. if (!self.Binder.Equals(otherBinders.Binder) ||
  130. self.MappingInfo.Count != otherBinders.MappingInfo.Count) {
  131. return false;
  132. }
  133. for (int j = 0; j < self.MappingInfo.Count; j++) {
  134. if (!self.MappingInfo[j].Equals(otherBinders.MappingInfo[j])) {
  135. return false;
  136. }
  137. }
  138. }
  139. return true;
  140. }
  141. return false;
  142. }
  143. }
  144. /// <summary>
  145. /// Provides a mapping for inputs of combo action expressions. The input can map
  146. /// to either an input of the new dynamic site, an input of a previous DynamicExpression,
  147. /// or a ConstantExpression which has been pulled out of the dynamic site arguments.
  148. /// </summary>
  149. public class ParameterMappingInfo {
  150. private readonly int _parameterIndex;
  151. private readonly int _actionIndex;
  152. private ConstantExpression _fixedInput;
  153. private ParameterMappingInfo(int param, int action, ConstantExpression fixedInput) {
  154. _parameterIndex = param;
  155. _actionIndex = action;
  156. _fixedInput = fixedInput;
  157. }
  158. public static ParameterMappingInfo Parameter(int index) {
  159. return new ParameterMappingInfo(index, -1, null);
  160. }
  161. public static ParameterMappingInfo Action(int index) {
  162. return new ParameterMappingInfo(-1, index, null);
  163. }
  164. public static ParameterMappingInfo Fixed(ConstantExpression e) {
  165. return new ParameterMappingInfo(-1, -1, e);
  166. }
  167. public int ParameterIndex {
  168. get {
  169. return _parameterIndex;
  170. }
  171. }
  172. public int ActionIndex {
  173. get {
  174. return _actionIndex;
  175. }
  176. }
  177. public ConstantExpression Constant {
  178. get {
  179. return _fixedInput;
  180. }
  181. }
  182. public bool IsParameter {
  183. get {
  184. return _parameterIndex != -1;
  185. }
  186. }
  187. public bool IsAction {
  188. get {
  189. return _actionIndex != -1;
  190. }
  191. }
  192. public bool IsConstant {
  193. get {
  194. return _fixedInput != null;
  195. }
  196. }
  197. public override bool Equals(object obj) {
  198. ParameterMappingInfo pmi = obj as ParameterMappingInfo;
  199. if (pmi == null) {
  200. return false;
  201. }
  202. if (pmi.ParameterIndex == ParameterIndex && pmi.ActionIndex == ActionIndex) {
  203. if (Constant != null) {
  204. if (pmi.Constant == null) {
  205. return false;
  206. }
  207. return Constant.Value == pmi.Constant.Value;
  208. } else {
  209. return pmi.Constant == null;
  210. }
  211. }
  212. return false;
  213. }
  214. public override int GetHashCode() {
  215. int res = ParameterIndex.GetHashCode() ^ ActionIndex.GetHashCode();
  216. if (Constant != null) {
  217. if (Constant.Value != null) {
  218. res ^= Constant.Value.GetHashCode();
  219. }
  220. }
  221. return res;
  222. }
  223. public override string ToString() {
  224. if (IsAction) {
  225. return "Action" + ActionIndex.ToString();
  226. } else if (IsParameter) {
  227. return "Parameter" + ParameterIndex.ToString();
  228. } else {
  229. object value = Constant.Value;
  230. if (value == null) {
  231. return "(null)";
  232. }
  233. return value.ToString();
  234. }
  235. }
  236. }
  237. /// <summary>
  238. /// Contains the mapping information for a single Combo Binder. This includes the original
  239. /// meta-binder and the mapping of parameters, sub-sites, and constants into the binding.
  240. /// </summary>
  241. public class BinderMappingInfo {
  242. public DynamicMetaObjectBinder Binder;
  243. public IList<ParameterMappingInfo> MappingInfo;
  244. public BinderMappingInfo(DynamicMetaObjectBinder binder, IList<ParameterMappingInfo> mappingInfo) {
  245. Binder = binder;
  246. MappingInfo = mappingInfo;
  247. }
  248. public BinderMappingInfo(DynamicMetaObjectBinder binder, params ParameterMappingInfo[] mappingInfos)
  249. : this(binder, (IList<ParameterMappingInfo>)mappingInfos) {
  250. }
  251. public override string ToString() {
  252. StringBuilder res = new StringBuilder();
  253. res.Append(Binder.ToString());
  254. res.Append(" ");
  255. string comma = "";
  256. foreach (ParameterMappingInfo info in MappingInfo) {
  257. res.Append(comma);
  258. res.Append(info.ToString());
  259. comma = ", ";
  260. }
  261. return res.ToString();
  262. }
  263. }
  264. }