PageRenderTime 84ms CodeModel.GetById 40ms app.highlight 15ms RepoModel.GetById 25ms app.codeStats 1ms

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