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