PageRenderTime 59ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/Microsoft.Scripting/Actions/ComboBinder.cs

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