PageRenderTime 114ms CodeModel.GetById 61ms app.highlight 10ms RepoModel.GetById 40ms app.codeStats 0ms

/Microsoft.Scripting/Generation/ToDiskRewriter.cs

https://bitbucket.org/stefanrusek/xronos
C# | 227 lines | 149 code | 38 blank | 40 comment | 28 complexity | b2bdab2d1be643d51268dfc1ad8732f2 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;
 29using System.Reflection.Emit;
 30using System.Runtime.CompilerServices;
 31#if !CODEPLEX_40
 32using Microsoft.Runtime.CompilerServices;
 33#endif
 34
 35using System.Threading;
 36using Microsoft.Scripting.Ast;
 37using Microsoft.Scripting.Runtime;
 38using Microsoft.Scripting.Utils;
 39using AstUtils = Microsoft.Scripting.Ast.Utils;
 40
 41namespace Microsoft.Scripting.Generation {
 42
 43    /// <summary>
 44    /// Serializes constants and dynamic sites so the code can be saved to disk
 45    /// </summary>
 46    internal sealed class ToDiskRewriter : ExpressionVisitor {
 47        private static int _uniqueNameId;
 48        private List<Expression> _constants;
 49        private ParameterExpression _constantPool;
 50        private Dictionary<Type, Type> _delegateTypes;
 51        private int _depth;
 52        private readonly TypeGen _typeGen;
 53
 54        internal ToDiskRewriter(TypeGen typeGen) {
 55            _typeGen = typeGen;
 56        }
 57
 58        public LambdaExpression RewriteLambda(LambdaExpression lambda) {
 59            return (LambdaExpression)Visit(lambda);
 60        }
 61
 62        protected override Expression VisitLambda<T>(Expression<T> node) {
 63            _depth++;
 64            try {
 65
 66                // Visit the lambda first, so we walk the tree and find any
 67                // constants we need to rewrite.
 68                node = (Expression<T>)base.VisitLambda(node);
 69
 70                if (_depth != 1) {
 71                    return node;
 72                }
 73
 74                var body = node.Body;
 75
 76                if (_constants != null) {
 77                    // Rewrite the constants, they can contain embedded
 78                    // CodeContextExpressions
 79                    for (int i = 0; i < _constants.Count; i++) {
 80                        _constants[i] = Visit(_constants[i]);
 81                    }
 82
 83                    // Add the consant pool variable to the top lambda
 84                    body = AstUtils.AddScopedVariable(
 85                        body,
 86                        _constantPool,
 87                        Expression.NewArrayInit(typeof(object), _constants)
 88                    );
 89                }
 90
 91                // Rewrite the lambda
 92                return Expression.Lambda<T>(
 93                    body,
 94                    node.Name + "$" + Interlocked.Increment(ref _uniqueNameId),
 95                    node.Parameters
 96                );
 97
 98            } finally {
 99                _depth--;
100            }
101        }
102
103        protected override Expression VisitExtension(Expression node) {
104            if (node.NodeType == ExpressionType.Dynamic) {
105                // the node was dynamic, the dynamic nodes were removed,
106                // we now need to rewrite any call sites.
107                return VisitDynamic((DynamicExpression)node);
108            }
109
110            return base.VisitExtension(node);
111        }
112
113        protected override Expression VisitConstant(ConstantExpression node) {
114            var site = node.Value as CallSite;
115            if (site != null) {
116                return RewriteCallSite(site);
117            }
118
119            var exprSerializable = node.Value as IExpressionSerializable;
120            if (exprSerializable != null) {
121                return Visit(exprSerializable.CreateExpression());
122            }
123
124            var symbols = node.Value as SymbolId[];
125            if (symbols != null) {
126                return Expression.NewArrayInit(
127                     typeof(SymbolId),
128                     new ReadOnlyCollection<Expression>(
129                         symbols.Map(s => SymbolConstantExpression.GetExpression(s))
130                     )
131                 );
132            }
133
134            return base.VisitConstant(node);
135        }
136
137        // If the DynamicExpression uses a transient (in-memory) type for its
138        // delegate, we need to replace it with a new delegate type that can be
139        // saved to disk
140        protected override Expression VisitDynamic(DynamicExpression node) {
141            Type delegateType;
142            if (RewriteDelegate(node.DelegateType, out delegateType)) {
143                node = Expression.MakeDynamic(delegateType, node.Binder, node.Arguments);
144            }
145
146            // Reduce dynamic expression so that the lambda can be emitted as a non-dynamic method.
147            return Visit(CompilerHelpers.Reduce(node));
148        }
149
150        private bool RewriteDelegate(Type delegateType, out Type newDelegateType) {
151            if (!ShouldRewriteDelegate(delegateType)) {
152                newDelegateType = null;
153                return false;
154            }
155
156            if (_delegateTypes == null) {
157                _delegateTypes = new Dictionary<Type, Type>();
158            }
159
160            // TODO: should caching move to AssemblyGen?
161            if (!_delegateTypes.TryGetValue(delegateType, out newDelegateType)) {
162                MethodInfo invoke = delegateType.GetMethod("Invoke");
163
164                newDelegateType = _typeGen.AssemblyGen.MakeDelegateType(
165                    delegateType.Name,
166                    invoke.GetParameters().Map(p => p.ParameterType),
167                    invoke.ReturnType
168                );
169
170                _delegateTypes[delegateType] = newDelegateType;
171            }
172
173            return true;
174        }
175
176        private bool ShouldRewriteDelegate(Type delegateType) {
177            // We need to replace a transient delegateType with one stored in
178            // the assembly we're saving to disk.
179            //
180            // One complication:
181            // SaveAssemblies mode prevents us from detecting the module as
182            // transient. If that option is turned on, always replace delegates
183            // that live in another AssemblyBuilder
184
185            var module = delegateType.Module as ModuleBuilder;
186            if (module == null) {
187                return false;
188            }
189
190            if (module.IsTransient()) {
191                return true;
192            }
193
194            if (Snippets.Shared.SaveSnippets && module.Assembly != _typeGen.AssemblyGen.AssemblyBuilder) {
195                return true;
196            }
197
198            return false;
199        }
200
201        private Expression RewriteCallSite(CallSite site) {
202            IExpressionSerializable serializer = site.Binder as IExpressionSerializable;
203            if (serializer == null) {
204                throw Error.GenNonSerializableBinder();
205            }
206
207            // add the initialization code that we'll generate later into the outermost
208            // lambda and then return an index into the array we'll be creating.
209            if (_constantPool == null) {
210                _constantPool = Expression.Variable(typeof(object[]), "$constantPool");
211                _constants = new List<Expression>();
212            }
213
214            Type siteType = site.GetType();
215
216            _constants.Add(Expression.Call(siteType.GetMethod("Create"), serializer.CreateExpression()));
217
218            // rewrite the node...
219            return Visit(
220                AstUtils.Convert(
221                    Expression.ArrayAccess(_constantPool, AstUtils.Constant(_constants.Count - 1)),
222                    siteType
223                )
224            );
225        }
226    }
227}