1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/
using System;
using System.Diagnostics;
using System.Dynamic.Utils;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Threading;
#if !FEATURE_CORE_DLR
namespace Microsoft.Scripting.Ast.Compiler {
#else
namespace System.Linq.Expressions.Compiler {
#endif
/// <summary>
/// Dynamic Language Runtime Compiler.
/// This part compiles lambdas.
/// </summary>
partial class LambdaCompiler {
private static int _Counter;
internal void EmitConstantArray<T>(T[] array) {
// Emit as runtime constant if possible
// if not, emit into IL
if (_method is DynamicMethod) {
EmitConstant(array, typeof(T[]));
#if FEATURE_REFEMIT
} else if(_typeBuilder != null) {
// store into field in our type builder, we will initialize
// the value only once.
FieldBuilder fb = CreateStaticField("ConstantArray", typeof(T[]));
Label l = _ilg.DefineLabel();
_ilg.Emit(OpCodes.Ldsfld, fb);
_ilg.Emit(OpCodes.Ldnull);
_ilg.Emit(OpCodes.Bne_Un, l);
_ilg.EmitArray(array);
_ilg.Emit(OpCodes.Stsfld, fb);
_ilg.MarkLabel(l);
_ilg.Emit(OpCodes.Ldsfld, fb);
#endif
} else {
_ilg.EmitArray(array);
}
}
private void EmitClosureCreation(LambdaCompiler inner) {
bool closure = inner._scope.NeedsClosure;
bool boundConstants = inner._boundConstants.Count > 0;
if (!closure && !boundConstants) {
_ilg.EmitNull();
return;
}
// new Closure(constantPool, currentHoistedLocals)
if (boundConstants) {
_boundConstants.EmitConstant(this, inner._boundConstants.ToArray(), typeof(object[]));
} else {
_ilg.EmitNull();
}
if (closure) {
_scope.EmitGet(_scope.NearestHoistedLocals.SelfVariable);
} else {
_ilg.EmitNull();
}
_ilg.EmitNew(typeof(Closure).GetConstructor(new Type[] { typeof(object[]), typeof(object[]) }));
}
/// <summary>
/// Emits code which creates new instance of the delegateType delegate.
///
/// Since the delegate is getting closed over the "Closure" argument, this
/// cannot be used with virtual/instance methods (inner must be static method)
/// </summary>
private void EmitDelegateConstruction(LambdaCompiler inner) {
Type delegateType = inner._lambda.Type;
DynamicMethod dynamicMethod = inner._method as DynamicMethod;
if (dynamicMethod != null) {
// dynamicMethod.CreateDelegate(delegateType, closure)
_boundConstants.EmitConstant(this, dynamicMethod, typeof(DynamicMethod));
_ilg.EmitType(delegateType);
EmitClosureCreation(inner);
_ilg.Emit(OpCodes.Callvirt, typeof(DynamicMethod).GetMethod("CreateDelegate", new Type[] { typeof(Type), typeof(object) }));
_ilg.Emit(OpCodes.Castclass, delegateType);
} else {
// new DelegateType(closure)
EmitClosureCreation(inner);
_ilg.Emit(OpCodes.Ldftn, (MethodInfo)inner._method);
_ilg.Emit(OpCodes.Newobj, (ConstructorInfo)(delegateType.GetMember(".ctor")[0]));
}
}
/// <summary>
/// Emits a delegate to the method generated for the LambdaExpression.
/// May end up creating a wrapper to match the requested delegate type.
/// </summary>
/// <param name="lambda">Lambda for which to generate a delegate</param>
///
private void EmitDelegateConstruction(LambdaExpression lambda) {
// 1. Create the new compiler
LambdaCompiler impl = CreateCompiler(lambda);
// 2. emit the lambda
// Since additional ILs are always emitted after the lambda's body, should not emit with tail call optimization.
impl.EmitLambdaBody(_scope, false, CompilationFlags.EmitAsNoTail);
// 3. emit the delegate creation in the outer lambda
EmitDelegateConstruction(impl);
}
private LambdaCompiler CreateCompiler(LambdaExpression lambda) {
#if FEATURE_REFEMIT
if (!(_method is DynamicMethod)) {
// When the lambda does not have a name or the name is empty, generate a unique name for it.
string name = String.IsNullOrEmpty(lambda.Name) ? GetUniqueMethodName() : lambda.Name;
MethodBuilder mb = _typeBuilder.DefineMethod(name, MethodAttributes.Private | MethodAttributes.Static);
return new LambdaCompiler(_tree, lambda, mb);
}
#endif
return new LambdaCompiler(_tree, lambda);
}
private static Type[] GetParameterTypes(LambdaExpression lambda) {
return lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type);
}
private static string GetUniqueMethodName() {
return "<ExpressionCompilerImplementationDetails>{" + Interlocked.Increment(ref _Counter) + "}lambda_method";
}
private void EmitLambdaBody() {
// The lambda body is the "last" expression of the lambda
CompilationFlags tailCallFlag = _lambda.TailCall ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail;
EmitLambdaBody(null, false, tailCallFlag);
}
/// <summary>
/// Emits the lambda body. If inlined, the parameters should already be
/// pushed onto the IL stack.
/// </summary>
/// <param name="parent">The parent scope.</param>
/// <param name="inlined">true if the lambda is inlined; false otherwise.</param>
/// <param name="flags">
/// The emum to specify if the lambda is compiled with the tail call optimization.
/// </param>
private void EmitLambdaBody(CompilerScope parent, bool inlined, CompilationFlags flags) {
_scope.Enter(this, parent);
if (inlined) {
// The arguments were already pushed onto the IL stack.
// Store them into locals, popping in reverse order.
//
// If any arguments were ByRef, the address is on the stack and
// we'll be storing it into the variable, which has a ref type.
for (int i = _lambda.Parameters.Count - 1; i >= 0; i--) {
_scope.EmitSet(_lambda.Parameters[i]);
}
}
// Need to emit the expression start for the lambda body
flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
if (_lambda.ReturnType == typeof(void)) {
EmitExpressionAsVoid(_lambda.Body, flags);
} else {
EmitExpression(_lambda.Body, flags);
}
// Return must be the last instruction in a CLI method.
// But if we're inlining the lambda, we want to leave the return
// value on the IL stack.
if (!inlined) {
_ilg.Emit(OpCodes.Ret);
}
_scope.Exit();
// Validate labels
Debug.Assert(_labelBlock.Parent == null && _labelBlock.Kind == LabelScopeKind.Lambda);
foreach (LabelInfo label in _labelInfo.Values) {
label.ValidateFinish();
}
}
}
}
|