PageRenderTime 274ms CodeModel.GetById 100ms app.highlight 98ms RepoModel.GetById 63ms app.codeStats 0ms

/Microsoft.Scripting.Core/Actions/AutoRuleTemplate.cs

https://bitbucket.org/stefanrusek/xronos
C# | 214 lines | 110 code | 37 blank | 67 comment | 11 complexity | f4f3c89e1e320f760009995682b44f90 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 * ***************************************************************************/
 15using System; using Microsoft;
 16
 17
 18using System.Collections.Generic;
 19#if CODEPLEX_40
 20using System.Dynamic.Utils;
 21using System.Linq.Expressions;
 22#else
 23using Microsoft.Scripting.Utils;
 24using Microsoft.Linq.Expressions;
 25#endif
 26using System.Runtime.CompilerServices;
 27#if !CODEPLEX_40
 28using Microsoft.Runtime.CompilerServices;
 29#endif
 30
 31
 32#if CODEPLEX_40
 33namespace System.Dynamic {
 34#else
 35namespace Microsoft.Scripting {
 36#endif
 37    /// <summary>
 38    /// Handles auto-templating of rules.  There are three important actions this performs:
 39    ///     1. Detects if templating is possible between two rules
 40    ///     2. Re-writes a non-templated rule into templated form
 41    ///     3. Extracts the constants from a non-templated rule which is compatible with a 
 42    ///         templated rule so that they can be used by the existing generated code.
 43    ///         
 44    /// Auto-templating is currently only used for serially monomorphic call sites where we
 45    /// can easily avoid code gen.  It is not used for polymorphic call sites although such
 46    /// a feature could be enabled in the future.
 47    /// </summary>
 48    internal static class AutoRuleTemplate {
 49        /// <summary>
 50        /// The entry point into auto-rule tempating.  This consumes the monomorphic rule which is currently
 51        /// stored in the cache as well as the rule that was just produced by the binder.  
 52        /// </summary>
 53        /// <param name="from">The original rule that is currently stored in the cache.  This rule may
 54        /// or may not be a templated rule.</param>
 55        /// <param name="to">The new rule produced by a binder.</param>
 56        internal static CallSiteRule<T> CopyOrCreateTemplatedRule<T>(CallSiteRule<T> from, Expression to) where T : class {
 57            List<KeyValuePair<ConstantExpression, int>> replacementList;
 58            
 59            // we only templaet off of L2 and L2 CallSiteRules. They always have targets, but Bindings could be null.
 60            if (from.Binding == null) {
 61                return null;
 62            }
 63
 64            TreeCompareResult tc = TreeComparer.CompareTrees(to, from.Binding, from.TemplatedConsts, out replacementList);
 65
 66            if (tc == TreeCompareResult.Incompatible) {
 67                return null;
 68            }
 69
 70            // We have 2 rules which are compatible.  We should create a new template or 
 71            // re-use the existing one.
 72
 73            TemplateData<T> template;
 74            object[] values = GetConstantValues(replacementList);
 75
 76            if (tc == TreeCompareResult.TooSpecific || from.Template == null) {
 77                // create a new one - either we are going from a non-templated rule to using a templated rule, 
 78                // or we are further generalizing an existing rule.  We need to re-write the incoming tree 
 79                // to be templated over the necessary constants and return the new rule bound to the template.
 80
 81                Expression<Func<Object[], T>> templateExpr = TemplateRuleRewriter.MakeTemplate<T>(CallSiteBinder.Stitch<T>(to), replacementList);
 82
 83#if !MICROSOFT_SCRIPTING_CORE
 84                // We cannot compile rules in the heterogeneous app domains since they
 85                // may come from less trusted sources
 86                if (!AppDomain.CurrentDomain.IsHomogenous) {
 87                    throw Error.HomogenousAppDomainRequired();
 88                }
 89#endif
 90                Func<Object[], T> templateFunction = templateExpr.Compile();
 91                Set<int> consts = new Set<int>(replacementList.Select(pair => pair.Value));
 92                template = new TemplateData<T>(templateFunction, consts);
 93            } else {
 94                template = from.Template;
 95            }
 96
 97            T newBody = template.TemplateFunction(values);
 98            return new CallSiteRule<T>(to, newBody, template);
 99        }
100
101        private static object[] GetConstantValues(List<KeyValuePair<ConstantExpression,int>> newConstants) {
102            object[] res = new object[newConstants.Count];
103            int index = 0;
104            foreach (KeyValuePair<ConstantExpression,int> ce in newConstants) {
105                res[index++] = ce.Key.Value;
106            }
107            return res;
108        }
109
110
111        internal class TemplateRuleRewriter : ExpressionVisitor {
112            private Dictionary<int, ParameterExpression> _map;
113            private int _curConstNum;
114
115            private TemplateRuleRewriter(Dictionary<int, ParameterExpression> map) {
116                _map = map;
117                _curConstNum = -1;
118            }
119
120            /// <summary>
121            /// Creates a higher order factory expression that can produce
122            /// instances of original expresion bound to given set of constant values.
123            /// </summary>
124            /// <typeparam name="T"></typeparam>
125            /// <param name="origTree">Original expresion.</param>
126            /// <param name="constToTemplate">Which constants should be parameterised.</param>
127            /// <returns>Factory expression.</returns>
128            internal static Expression<Func<Object[], T>> MakeTemplate<T>(
129                    Expression<T> origTree, 
130                    List<KeyValuePair<ConstantExpression,int>> constToTemplate) {
131
132
133                // The goal is to produce a nested lambda 
134                // which is the same as original, but with specified constants re-bound to variables
135                // that we will initialize to given values.
136
137                ParameterExpression constsArg = Expression.Parameter(typeof(object[]), null);
138
139                // these are the variables that would act as constant replacements. 
140                // they will be hoisted into a closure so every produced rule will get its own version.
141                List<ParameterExpression> locals = new List<ParameterExpression>();
142
143                // maping of constants to locals to tell rewriter what to do.
144                Dictionary<int, ParameterExpression> map = new Dictionary<int, ParameterExpression>();
145
146                List<Expression> statements = new List<Expression>();
147
148                int i = 0;
149                foreach(var cur in constToTemplate) {
150                    Type curConstType = TypeUtils.GetConstantType(cur.Key.Type);
151                    var local = Expression.Parameter(curConstType, null);
152                    locals.Add(local);
153                    statements.Add(
154                        Expression.Assign(
155                            local,
156                            Expression.Convert(
157                                Expression.ArrayAccess(constsArg, Expression.Constant(i)),
158                                curConstType
159                            )
160                        )
161                    );
162                    map.Add(cur.Value, local);
163                    i++;
164                }
165
166                // remap original lambda
167                TemplateRuleRewriter rewriter = new TemplateRuleRewriter(map);
168                Expression<T> templatedTree = (Expression<T>)rewriter.Visit(origTree);
169
170                statements.Add(templatedTree);
171
172                
173                //  T template(object[] constArg){
174                //      local0 = constArg[0];
175                //      local1 = constArg[1];
176                //      ...
177                //      localN = constArg[N];
178                //
179                //      return {original lambda, but with selected consts bound to locals}      
180                //  }
181
182                Expression<Func<Object[], T>> template = Expression.Lambda<Func<Object[], T>>(
183                    Expression.Block(
184                        locals,
185                        statements
186                    ),
187                    constsArg
188                );
189
190                // Need to compile with forceDynamic because T could be invisible,
191                // or one of the argument types could be invisible
192                return template;
193            }
194
195            protected internal override Expression VisitConstant(ConstantExpression node) {
196                _curConstNum++;
197
198                if (_map.ContainsKey(_curConstNum)) {
199                    return _map[_curConstNum];
200                }
201
202                return base.VisitConstant(node);
203            }
204
205            // Extensions may contain constants too. 
206            // We visit them in TreeCompare and so we do here.
207            protected internal override Expression VisitExtension(Expression node) {
208                return Visit(node.ReduceExtensions());
209            }
210
211        }
212    }
213}
214