1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Collections.Generic; 6 using System.Diagnostics; 7 using System.Dynamic.Utils; 8 using System.Reflection; 9 using System.Reflection.Emit; 10 using System.Runtime.CompilerServices; 11 12 namespace System.Linq.Expressions.Compiler 13 { 14 internal interface ILocalCache 15 { GetLocal(Type type)16 LocalBuilder GetLocal(Type type); 17 FreeLocal(LocalBuilder local)18 void FreeLocal(LocalBuilder local); 19 } 20 21 /// <summary> 22 /// LambdaCompiler is responsible for compiling individual lambda (LambdaExpression). The complete tree may 23 /// contain multiple lambdas, the Compiler class is responsible for compiling the whole tree, individual 24 /// lambdas are then compiled by the LambdaCompiler. 25 /// </summary> 26 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] 27 internal sealed partial class LambdaCompiler : ILocalCache 28 { WriteBack(LambdaCompiler compiler)29 private delegate void WriteBack(LambdaCompiler compiler); 30 31 // Information on the entire lambda tree currently being compiled 32 private readonly AnalyzedTree _tree; 33 34 private readonly ILGenerator _ilg; 35 36 #if FEATURE_COMPILE_TO_METHODBUILDER 37 // The TypeBuilder backing this method, if any 38 private readonly TypeBuilder _typeBuilder; 39 #endif 40 41 private readonly MethodInfo _method; 42 43 // Currently active LabelTargets and their mapping to IL labels 44 private LabelScopeInfo _labelBlock = new LabelScopeInfo(null, LabelScopeKind.Lambda); 45 // Mapping of labels used for "long" jumps (jumping out and into blocks) 46 private readonly Dictionary<LabelTarget, LabelInfo> _labelInfo = new Dictionary<LabelTarget, LabelInfo>(); 47 48 // The currently active variable scope 49 private CompilerScope _scope; 50 51 // The lambda we are compiling 52 private readonly LambdaExpression _lambda; 53 54 // True if the method's first argument is of type Closure 55 private readonly bool _hasClosureArgument; 56 57 // Runtime constants bound to the delegate 58 private readonly BoundConstants _boundConstants; 59 60 // Free list of locals, so we reuse them rather than creating new ones 61 private readonly KeyedStack<Type, LocalBuilder> _freeLocals = new KeyedStack<Type, LocalBuilder>(); 62 63 /// <summary> 64 /// Creates a lambda compiler that will compile to a dynamic method 65 /// </summary> LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda)66 private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda) 67 { 68 Type[] parameterTypes = GetParameterTypes(lambda, typeof(Closure)); 69 70 var method = new DynamicMethod(lambda.Name ?? "lambda_method", lambda.ReturnType, parameterTypes, true); 71 72 _tree = tree; 73 _lambda = lambda; 74 _method = method; 75 76 // In a Win8 immersive process user code is not allowed to access non-W8P framework APIs through 77 // reflection or RefEmit. Framework code, however, is given an exemption. 78 // This is to make sure that user code cannot access non-W8P framework APIs via ExpressionTree. 79 80 // TODO: This API is not available, is there an alternative way to achieve the same. 81 // method.ProfileAPICheck = true; 82 83 _ilg = method.GetILGenerator(); 84 85 _hasClosureArgument = true; 86 87 // These are populated by AnalyzeTree/VariableBinder 88 _scope = tree.Scopes[lambda]; 89 _boundConstants = tree.Constants[lambda]; 90 91 InitializeMethod(); 92 } 93 94 #if FEATURE_COMPILE_TO_METHODBUILDER 95 /// <summary> 96 /// Creates a lambda compiler that will compile into the provided MethodBuilder 97 /// </summary> LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method)98 private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method) 99 { 100 var scope = tree.Scopes[lambda]; 101 var hasClosureArgument = scope.NeedsClosure; 102 103 Type[] paramTypes = GetParameterTypes(lambda, hasClosureArgument ? typeof(Closure) : null); 104 105 method.SetReturnType(lambda.ReturnType); 106 method.SetParameters(paramTypes); 107 var parameters = lambda.Parameters; 108 // parameters are index from 1, with closure argument we need to skip the first arg 109 int startIndex = hasClosureArgument ? 2 : 1; 110 for (int i = 0, n = parameters.Count; i < n; i++) 111 { 112 method.DefineParameter(i + startIndex, ParameterAttributes.None, parameters[i].Name); 113 } 114 115 _tree = tree; 116 _lambda = lambda; 117 _typeBuilder = (TypeBuilder)method.DeclaringType; 118 _method = method; 119 _hasClosureArgument = hasClosureArgument; 120 121 _ilg = method.GetILGenerator(); 122 123 // These are populated by AnalyzeTree/VariableBinder 124 _scope = scope; 125 _boundConstants = tree.Constants[lambda]; 126 127 InitializeMethod(); 128 } 129 #endif 130 131 /// <summary> 132 /// Creates a lambda compiler for an inlined lambda 133 /// </summary> LambdaCompiler( LambdaCompiler parent, LambdaExpression lambda, InvocationExpression invocation)134 private LambdaCompiler( 135 LambdaCompiler parent, 136 LambdaExpression lambda, 137 InvocationExpression invocation) 138 { 139 _tree = parent._tree; 140 _lambda = lambda; 141 _method = parent._method; 142 _ilg = parent._ilg; 143 _hasClosureArgument = parent._hasClosureArgument; 144 #if FEATURE_COMPILE_TO_METHODBUILDER 145 _typeBuilder = parent._typeBuilder; 146 #endif 147 // inlined scopes are associated with invocation, not with the lambda 148 _scope = _tree.Scopes[invocation]; 149 _boundConstants = parent._boundConstants; 150 } 151 InitializeMethod()152 private void InitializeMethod() 153 { 154 // See if we can find a return label, so we can emit better IL 155 AddReturnLabel(_lambda); 156 _boundConstants.EmitCacheConstants(this); 157 } 158 159 internal ILGenerator IL => _ilg; 160 161 internal IParameterProvider Parameters => _lambda; 162 163 #if FEATURE_COMPILE_TO_METHODBUILDER 164 internal bool CanEmitBoundConstants => _method is DynamicMethod; 165 #endif 166 167 #region Compiler entry points 168 169 /// <summary> 170 /// Compiler entry point 171 /// </summary> 172 /// <param name="lambda">LambdaExpression to compile.</param> 173 /// <returns>The compiled delegate.</returns> Compile(LambdaExpression lambda)174 internal static Delegate Compile(LambdaExpression lambda) 175 { 176 lambda.ValidateArgumentCount(); 177 178 // 1. Bind lambda 179 AnalyzedTree tree = AnalyzeLambda(ref lambda); 180 181 // 2. Create lambda compiler 182 LambdaCompiler c = new LambdaCompiler(tree, lambda); 183 184 // 3. Emit 185 c.EmitLambdaBody(); 186 187 // 4. Return the delegate. 188 return c.CreateDelegate(); 189 } 190 191 #if FEATURE_COMPILE_TO_METHODBUILDER 192 /// <summary> 193 /// Mutates the MethodBuilder parameter, filling in IL, parameters, 194 /// and return type. 195 /// 196 /// (probably shouldn't be modifying parameters/return type...) 197 /// </summary> Compile(LambdaExpression lambda, MethodBuilder method)198 internal static void Compile(LambdaExpression lambda, MethodBuilder method) 199 { 200 // 1. Bind lambda 201 AnalyzedTree tree = AnalyzeLambda(ref lambda); 202 203 // 2. Create lambda compiler 204 LambdaCompiler c = new LambdaCompiler(tree, lambda, method); 205 206 // 3. Emit 207 c.EmitLambdaBody(); 208 } 209 #endif 210 211 #endregion 212 AnalyzeLambda(ref LambdaExpression lambda)213 private static AnalyzedTree AnalyzeLambda(ref LambdaExpression lambda) 214 { 215 // Spill the stack for any exception handling blocks or other 216 // constructs which require entering with an empty stack 217 lambda = StackSpiller.AnalyzeLambda(lambda); 218 219 // Bind any variable references in this lambda 220 return VariableBinder.Bind(lambda); 221 } 222 223 public LocalBuilder GetLocal(Type type) => _freeLocals.TryPop(type) ?? _ilg.DeclareLocal(type); 224 FreeLocal(LocalBuilder local)225 public void FreeLocal(LocalBuilder local) 226 { 227 Debug.Assert(local != null); 228 _freeLocals.Push(local.LocalType, local); 229 } 230 231 /// <summary> 232 /// Gets the argument slot corresponding to the parameter at the given 233 /// index. Assumes that the method takes a certain number of prefix 234 /// arguments, followed by the real parameters stored in Parameters 235 /// </summary> GetLambdaArgument(int index)236 internal int GetLambdaArgument(int index) 237 { 238 return index + (_hasClosureArgument ? 1 : 0) + (_method.IsStatic ? 0 : 1); 239 } 240 241 /// <summary> 242 /// Returns the index-th argument. This method provides access to the actual arguments 243 /// defined on the lambda itself, and excludes the possible 0-th closure argument. 244 /// </summary> EmitLambdaArgument(int index)245 internal void EmitLambdaArgument(int index) 246 { 247 _ilg.EmitLoadArg(GetLambdaArgument(index)); 248 } 249 EmitClosureArgument()250 internal void EmitClosureArgument() 251 { 252 Debug.Assert(_hasClosureArgument, "must have a Closure argument"); 253 Debug.Assert(_method.IsStatic, "must be a static method"); 254 _ilg.EmitLoadArg(0); 255 } 256 CreateDelegate()257 private Delegate CreateDelegate() 258 { 259 Debug.Assert(_method is DynamicMethod); 260 261 return _method.CreateDelegate(_lambda.Type, new Closure(_boundConstants.ToArray(), null)); 262 } 263 264 #if FEATURE_COMPILE_TO_METHODBUILDER CreateStaticField(string name, Type type)265 private FieldBuilder CreateStaticField(string name, Type type) 266 { 267 // We are emitting into someone else's type. We don't want name 268 // conflicts, so choose a long name that is unlikely to conflict. 269 // Naming scheme chosen here is similar to what the C# compiler 270 // uses. 271 return _typeBuilder.DefineField("<ExpressionCompilerImplementationDetails>{" + System.Threading.Interlocked.Increment(ref s_counter) + "}" + name, type, FieldAttributes.Static | FieldAttributes.Private); 272 } 273 #endif 274 275 /// <summary> 276 /// Creates an uninitialized field suitable for private implementation details 277 /// Works with DynamicMethods or TypeBuilders. 278 /// </summary> CreateLazyInitializedField(string name)279 private MemberExpression CreateLazyInitializedField<T>(string name) 280 { 281 #if FEATURE_COMPILE_TO_METHODBUILDER 282 if (_method is DynamicMethod) 283 #else 284 Debug.Assert(_method is DynamicMethod); 285 #endif 286 { 287 return Expression.Field(Expression.Constant(new StrongBox<T>(default(T))), "Value"); 288 } 289 #if FEATURE_COMPILE_TO_METHODBUILDER 290 else 291 { 292 return Expression.Field(null, CreateStaticField(name, typeof(T))); 293 } 294 #endif 295 } 296 } 297 } 298