PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Microsoft.Scripting/Actions/RuleBuilder.cs

https://bitbucket.org/stefanrusek/xronos
C# | 307 lines | 214 code | 38 blank | 55 comment | 36 complexity | 24ee23809d41a5595fabfed10d45dcb9 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.Collections.ObjectModel;
  22. #if CODEPLEX_40
  23. using System.Linq.Expressions;
  24. #else
  25. using Microsoft.Linq.Expressions;
  26. #endif
  27. using System.Reflection;
  28. #if CODEPLEX_40
  29. using System.Dynamic;
  30. #else
  31. using Microsoft.Scripting;
  32. #endif
  33. using Microsoft.Contracts;
  34. using Microsoft.Scripting.Generation;
  35. using Microsoft.Scripting.Runtime;
  36. using Microsoft.Scripting.Utils;
  37. using AstUtils = Microsoft.Scripting.Ast.Utils;
  38. namespace Microsoft.Scripting.Actions {
  39. #if CODEPLEX_40
  40. using Ast = System.Linq.Expressions.Expression;
  41. #else
  42. using Ast = Microsoft.Linq.Expressions.Expression;
  43. #endif
  44. /// <summary>
  45. /// Rule Builder
  46. ///
  47. /// A rule is the mechanism that LanguageBinders use to specify both what code to execute (the Target)
  48. /// for a particular action on a particular set of objects, but also a Test that guards the Target.
  49. /// Whenver the Test returns true, it is assumed that the Target will be the correct action to
  50. /// take on the arguments.
  51. ///
  52. /// In the current design, a RuleBuilder is also used to provide a mini binding scope for the
  53. /// parameters and temporary variables that might be needed by the Test and Target. This will
  54. /// probably change in the future as we unify around the notion of Lambdas.
  55. ///
  56. /// TODO: remove once everyone is converted over to MetaObjects
  57. /// </summary>
  58. public sealed class RuleBuilder {
  59. internal Expression _test; // the test that determines if the rule is applicable for its parameters
  60. internal Expression _target; // the target that executes if the rule is true
  61. internal readonly Expression _context; // CodeContext, if any.
  62. private bool _error; // true if the rule represents an error
  63. internal List<ParameterExpression> _temps; // temporaries allocated by the rule
  64. // the parameters which the rule is processing
  65. internal readonly IList<Expression> _parameters;
  66. // the return label of the rule
  67. internal readonly LabelTarget _return;
  68. /// <summary>
  69. /// Completed rule
  70. /// </summary>
  71. private Expression _binding;
  72. public RuleBuilder(ReadOnlyCollection<ParameterExpression> parameters, LabelTarget returnLabel) {
  73. if (parameters.Count > 0 && typeof(CodeContext).IsAssignableFrom(parameters[0].Type)) {
  74. _context = parameters[0];
  75. var p = ArrayUtils.RemoveAt(parameters, 0);
  76. _parameters = p;
  77. } else {
  78. // TODO: remove the copy when we have covariant IEnumerable<T>
  79. _parameters = parameters.ToArray();
  80. }
  81. _return = returnLabel;
  82. }
  83. public void Clear() {
  84. _test = null;
  85. _target = null;
  86. _error = false;
  87. _temps = null;
  88. }
  89. /// <summary>
  90. /// An expression that should return true if and only if Target should be executed
  91. /// </summary>
  92. public Expression Test {
  93. get { return _test; }
  94. set {
  95. ContractUtils.RequiresNotNull(value, "value");
  96. ContractUtils.Requires(TypeUtils.IsBool(value.Type), "value", Strings.TypeOfTestMustBeBool);
  97. _test = value;
  98. }
  99. }
  100. /// <summary>
  101. /// The code to execute if the Test is true.
  102. /// </summary>
  103. public Expression Target {
  104. get { return _target; }
  105. set { _target = value; }
  106. }
  107. public Expression Context {
  108. get {
  109. return _context;
  110. }
  111. }
  112. /// <summary>
  113. /// Gets the logical parameters to the dynamic site in the form of Expressions.
  114. /// </summary>
  115. public IList<Expression> Parameters {
  116. get {
  117. return _parameters;
  118. }
  119. }
  120. public LabelTarget ReturnLabel {
  121. get { return _return; }
  122. }
  123. /// <summary>
  124. /// Allocates a temporary variable for use during the rule.
  125. /// </summary>
  126. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  127. public ParameterExpression GetTemporary(Type type, string name) {
  128. ParameterExpression t = Expression.Variable(type, name);
  129. AddTemporary(t);
  130. return t;
  131. }
  132. public void AddTemporary(ParameterExpression variable) {
  133. ContractUtils.RequiresNotNull(variable, "variable");
  134. if (_temps == null) {
  135. _temps = new List<ParameterExpression>();
  136. }
  137. _temps.Add(variable);
  138. }
  139. public Expression MakeReturn(ActionBinder binder, Expression expr) {
  140. // we create a temporary here so that ConvertExpression doesn't need to (because it has no way to declare locals).
  141. if (expr.Type != typeof(void)) {
  142. ParameterExpression variable = GetTemporary(expr.Type, "$retVal");
  143. Expression conv = binder.ConvertExpression(variable, ReturnType, ConversionResultKind.ExplicitCast, Context);
  144. if (conv == variable) return MakeReturn(expr);
  145. return MakeReturn(Ast.Block(Ast.Assign(variable, expr), conv));
  146. }
  147. return MakeReturn(binder.ConvertExpression(expr, ReturnType, ConversionResultKind.ExplicitCast, Context));
  148. }
  149. private Expression MakeReturn(Expression expression) {
  150. return Ast.Return(_return, AstUtils.Convert(expression, _return.Type));
  151. }
  152. public Expression MakeError(Expression expr) {
  153. if (expr != null) {
  154. // TODO: Change to ConvertHelper
  155. if (!TypeUtils.CanAssign(typeof(Exception), expr.Type)) {
  156. expr = Ast.Convert(expr, typeof(Exception));
  157. }
  158. }
  159. _error = true;
  160. return Ast.Throw(expr);
  161. }
  162. public bool IsError {
  163. get {
  164. return _error;
  165. }
  166. set {
  167. _error = value;
  168. }
  169. }
  170. public void AddTest(Expression expression) {
  171. ContractUtils.RequiresNotNull(expression, "expression");
  172. ContractUtils.Requires(TypeUtils.IsBool(expression.Type), "expression", Strings.TypeOfExpressionMustBeBool);
  173. if (_test == null) {
  174. _test = expression;
  175. } else {
  176. _test = Ast.AndAlso(_test, expression);
  177. }
  178. }
  179. public void MakeTest(params Type[] types) {
  180. _test = MakeTestForTypes(types, 0);
  181. }
  182. public static Expression MakeTypeTestExpression(Type t, Expression expr) {
  183. // we must always check for non-sealed types explicitly - otherwise we end up
  184. // doing fast-path behavior on a subtype which overrides behavior that wasn't
  185. // present for the base type.
  186. //TODO there's a question about nulls here
  187. if (CompilerHelpers.IsSealed(t) && t == expr.Type) {
  188. if (t.IsValueType) {
  189. return AstUtils.Constant(true);
  190. }
  191. return Ast.NotEqual(expr, AstUtils.Constant(null));
  192. }
  193. return Ast.AndAlso(
  194. Ast.NotEqual(
  195. expr,
  196. AstUtils.Constant(null)),
  197. Ast.Equal(
  198. Ast.Call(
  199. AstUtils.Convert(expr, typeof(object)),
  200. typeof(object).GetMethod("GetType")
  201. ),
  202. AstUtils.Constant(t)
  203. )
  204. );
  205. }
  206. public Expression MakeTestForTypes(Type[] types, int index) {
  207. Expression test = MakeTypeTest(types[index], index);
  208. if (index < types.Length - 1) {
  209. Expression nextTests = MakeTestForTypes(types, index + 1);
  210. if (ConstantCheck.Check(test, true)) {
  211. return nextTests;
  212. } else if (ConstantCheck.Check(nextTests, true)) {
  213. return test;
  214. } else {
  215. return Ast.AndAlso(test, nextTests);
  216. }
  217. } else {
  218. return test;
  219. }
  220. }
  221. public Expression MakeTypeTest(Type type, int index) {
  222. return MakeTypeTest(type, Parameters[index]);
  223. }
  224. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
  225. public Expression MakeTypeTest(Type type, Expression tested) {
  226. if (type == null || type == typeof(DynamicNull)) {
  227. return Ast.Equal(tested, AstUtils.Constant(null));
  228. }
  229. return MakeTypeTestExpression(type, tested);
  230. }
  231. /// <summary>
  232. /// Gets the number of logical parameters the dynamic site is provided with.
  233. /// </summary>
  234. public int ParameterCount {
  235. get {
  236. return _parameters.Count;
  237. }
  238. }
  239. public Expression MakeTypeTestExpression(Type t, int param) {
  240. return MakeTypeTestExpression(t, Parameters[param]);
  241. }
  242. [Confined]
  243. public override string ToString() {
  244. return string.Format("RuleBuilder({0})", _target);
  245. }
  246. public Type ReturnType {
  247. get { return _return.Type; }
  248. }
  249. public Expression CreateRule() {
  250. if (_binding == null) {
  251. if (_test == null) {
  252. throw Error.MissingTest();
  253. }
  254. if (_target == null) {
  255. throw Error.MissingTarget();
  256. }
  257. _binding = Expression.Block(
  258. _temps != null ? _temps.ToArray() : new ParameterExpression[0],
  259. Expression.Condition(
  260. _test,
  261. AstUtils.Convert(_target, typeof(void)),
  262. AstUtils.Empty()
  263. )
  264. );
  265. }
  266. return _binding;
  267. }
  268. }
  269. }