/mcs/class/dlr/Runtime/Microsoft.Scripting.Core/Compiler/LambdaCompiler.Expressions.cs
C# | 1085 lines | 805 code | 143 blank | 137 comment | 195 complexity | 81cc89fbe6384e7192e174787f67e658 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
- /* ****************************************************************************
- *
- * 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.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Dynamic.Utils;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Runtime.CompilerServices;
- #if !FEATURE_CORE_DLR
- namespace Microsoft.Scripting.Ast.Compiler {
- #else
- namespace System.Linq.Expressions.Compiler {
- #endif
- partial class LambdaCompiler {
- [Flags]
- internal enum CompilationFlags {
- EmitExpressionStart = 0x0001,
- EmitNoExpressionStart = 0x0002,
- EmitAsDefaultType = 0x0010,
- EmitAsVoidType = 0x0020,
- EmitAsTail = 0x0100, // at the tail position of a lambda, tail call can be safely emitted
- EmitAsMiddle = 0x0200, // in the middle of a lambda, tail call can be emitted if it is in a return
- EmitAsNoTail = 0x0400, // neither at the tail or in a return, or tail call is not turned on, no tail call is emitted
- EmitExpressionStartMask = 0x000f,
- EmitAsTypeMask = 0x00f0,
- EmitAsTailCallMask = 0x0f00
- }
- /// <summary>
- /// Update the flag with a new EmitAsTailCall flag
- /// </summary>
- private static CompilationFlags UpdateEmitAsTailCallFlag(CompilationFlags flags, CompilationFlags newValue) {
- Debug.Assert(newValue == CompilationFlags.EmitAsTail || newValue == CompilationFlags.EmitAsMiddle || newValue == CompilationFlags.EmitAsNoTail);
- var oldValue = flags & CompilationFlags.EmitAsTailCallMask;
- return flags ^ oldValue | newValue;
- }
- /// <summary>
- /// Update the flag with a new EmitExpressionStart flag
- /// </summary>
- private static CompilationFlags UpdateEmitExpressionStartFlag(CompilationFlags flags, CompilationFlags newValue) {
- Debug.Assert(newValue == CompilationFlags.EmitExpressionStart || newValue == CompilationFlags.EmitNoExpressionStart);
- var oldValue = flags & CompilationFlags.EmitExpressionStartMask;
- return flags ^ oldValue | newValue;
- }
- /// <summary>
- /// Update the flag with a new EmitAsType flag
- /// </summary>
- private static CompilationFlags UpdateEmitAsTypeFlag(CompilationFlags flags, CompilationFlags newValue) {
- Debug.Assert(newValue == CompilationFlags.EmitAsDefaultType || newValue == CompilationFlags.EmitAsVoidType);
- var oldValue = flags & CompilationFlags.EmitAsTypeMask;
- return flags ^ oldValue | newValue;
- }
- /// <summary>
- /// Generates code for this expression in a value position.
- /// This method will leave the value of the expression
- /// on the top of the stack typed as Type.
- /// </summary>
- internal void EmitExpression(Expression node) {
- EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitExpressionStart);
- }
- /// <summary>
- /// Emits an expression and discards the result. For some nodes this emits
- /// more optimial code then EmitExpression/Pop
- /// </summary>
- private void EmitExpressionAsVoid(Expression node) {
- EmitExpressionAsVoid(node, CompilationFlags.EmitAsNoTail);
- }
- private void EmitExpressionAsVoid(Expression node, CompilationFlags flags) {
- Debug.Assert(node != null);
- CompilationFlags startEmitted = EmitExpressionStart(node);
- switch (node.NodeType) {
- case ExpressionType.Assign:
- EmitAssign((BinaryExpression)node, CompilationFlags.EmitAsVoidType);
- break;
- case ExpressionType.Block:
- Emit((BlockExpression)node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
- break;
- case ExpressionType.Throw:
- EmitThrow((UnaryExpression)node, CompilationFlags.EmitAsVoidType);
- break;
- case ExpressionType.Goto:
- EmitGotoExpression(node, UpdateEmitAsTypeFlag(flags, CompilationFlags.EmitAsVoidType));
- break;
- case ExpressionType.Constant:
- case ExpressionType.Default:
- case ExpressionType.Parameter:
- // no-op
- break;
- default:
- if (node.Type == typeof(void)) {
- EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitNoExpressionStart));
- } else {
- EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitNoExpressionStart);
- _ilg.Emit(OpCodes.Pop);
- }
- break;
- }
- EmitExpressionEnd(startEmitted);
- }
- private void EmitExpressionAsType(Expression node, Type type, CompilationFlags flags) {
- if (type == typeof(void)) {
- EmitExpressionAsVoid(node, flags);
- } else {
- // if the node is emitted as a different type, CastClass IL is emitted at the end,
- // should not emit with tail calls.
- if (!TypeUtils.AreEquivalent(node.Type, type)) {
- EmitExpression(node);
- Debug.Assert(TypeUtils.AreReferenceAssignable(type, node.Type));
- _ilg.Emit(OpCodes.Castclass, type);
- } else {
- // emit the with the flags and emit emit expression start
- EmitExpression(node, UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart));
- }
- }
- }
- #region label block tracking
- private CompilationFlags EmitExpressionStart(Expression node) {
- if (TryPushLabelBlock(node)) {
- return CompilationFlags.EmitExpressionStart;
- }
- return CompilationFlags.EmitNoExpressionStart;
- }
- private void EmitExpressionEnd(CompilationFlags flags) {
- if ((flags & CompilationFlags.EmitExpressionStartMask) == CompilationFlags.EmitExpressionStart) {
- PopLabelBlock(_labelBlock.Kind);
- }
- }
- #endregion
- #region InvocationExpression
- private void EmitInvocationExpression(Expression expr, CompilationFlags flags) {
- InvocationExpression node = (InvocationExpression)expr;
- // Optimization: inline code for literal lambda's directly
- //
- // This is worth it because otherwise we end up with a extra call
- // to DynamicMethod.CreateDelegate, which is expensive.
- //
- if (node.LambdaOperand != null) {
- EmitInlinedInvoke(node, flags);
- return;
- }
- expr = node.Expression;
- if (typeof(LambdaExpression).IsAssignableFrom(expr.Type)) {
- // if the invoke target is a lambda expression tree, first compile it into a delegate
- expr = Expression.Call(expr, expr.Type.GetMethod("Compile", new Type[0]));
- }
- expr = Expression.Call(expr, expr.Type.GetMethod("Invoke"), node.Arguments);
- EmitExpression(expr);
- }
- private void EmitInlinedInvoke(InvocationExpression invoke, CompilationFlags flags) {
- var lambda = invoke.LambdaOperand;
- // This is tricky: we need to emit the arguments outside of the
- // scope, but set them inside the scope. Fortunately, using the IL
- // stack it is entirely doable.
- // 1. Emit invoke arguments
- List<WriteBack> wb = EmitArguments(lambda.Type.GetMethod("Invoke"), invoke);
- // 2. Create the nested LambdaCompiler
- var inner = new LambdaCompiler(this, lambda);
- // 3. Emit the body
- // if the inlined lambda is the last expression of the whole lambda,
- // tail call can be applied.
- if (wb.Count != 0) {
- flags = UpdateEmitAsTailCallFlag(flags, CompilationFlags.EmitAsNoTail);
- }
- inner.EmitLambdaBody(_scope, true, flags);
- // 4. Emit writebacks if needed
- EmitWriteBack(wb);
- }
- #endregion
- #region IndexExpression
- private void EmitIndexExpression(Expression expr) {
- var node = (IndexExpression)expr;
- // Emit instance, if calling an instance method
- Type objectType = null;
- if (node.Object != null) {
- EmitInstance(node.Object, objectType = node.Object.Type);
- }
- // Emit indexes. We don't allow byref args, so no need to worry
- // about writebacks or EmitAddress
- foreach (var arg in node.Arguments) {
- EmitExpression(arg);
- }
- EmitGetIndexCall(node, objectType);
- }
- private void EmitIndexAssignment(BinaryExpression node, CompilationFlags flags) {
- var index = (IndexExpression)node.Left;
- var emitAs = flags & CompilationFlags.EmitAsTypeMask;
- // Emit instance, if calling an instance method
- Type objectType = null;
- if (index.Object != null) {
- EmitInstance(index.Object, objectType = index.Object.Type);
- }
- // Emit indexes. We don't allow byref args, so no need to worry
- // about writebacks or EmitAddress
- foreach (var arg in index.Arguments) {
- EmitExpression(arg);
- }
- // Emit value
- EmitExpression(node.Right);
- // Save the expression value, if needed
- LocalBuilder temp = null;
- if (emitAs != CompilationFlags.EmitAsVoidType) {
- _ilg.Emit(OpCodes.Dup);
- _ilg.Emit(OpCodes.Stloc, temp = GetLocal(node.Type));
- }
- EmitSetIndexCall(index, objectType);
- // Restore the value
- if (emitAs != CompilationFlags.EmitAsVoidType) {
- _ilg.Emit(OpCodes.Ldloc, temp);
- FreeLocal(temp);
- }
- }
- private void EmitGetIndexCall(IndexExpression node, Type objectType) {
- if (node.Indexer != null) {
- // For indexed properties, just call the getter
- var method = node.Indexer.GetGetMethod(true);
- EmitCall(objectType, method);
- } else if (node.Arguments.Count != 1) {
- // Multidimensional arrays, call get
- _ilg.Emit(OpCodes.Call, node.Object.Type.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance));
- } else {
- // For one dimensional arrays, emit load
- _ilg.EmitLoadElement(node.Type);
- }
- }
- private void EmitSetIndexCall(IndexExpression node, Type objectType) {
- if (node.Indexer != null) {
- // For indexed properties, just call the setter
- var method = node.Indexer.GetSetMethod(true);
- EmitCall(objectType, method);
- } else if (node.Arguments.Count != 1) {
- // Multidimensional arrays, call set
- _ilg.Emit(OpCodes.Call, node.Object.Type.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance));
- } else {
- // For one dimensional arrays, emit store
- _ilg.EmitStoreElement(node.Type);
- }
- }
- #endregion
- #region MethodCallExpression
- private void EmitMethodCallExpression(Expression expr, CompilationFlags flags) {
- MethodCallExpression node = (MethodCallExpression)expr;
- EmitMethodCall(node.Object, node.Method, node, flags);
- }
- private void EmitMethodCallExpression(Expression expr) {
- EmitMethodCallExpression(expr, CompilationFlags.EmitAsNoTail);
- }
- private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr) {
- EmitMethodCall(obj, method, methodCallExpr, CompilationFlags.EmitAsNoTail);
- }
- private void EmitMethodCall(Expression obj, MethodInfo method, IArgumentProvider methodCallExpr, CompilationFlags flags) {
- // Emit instance, if calling an instance method
- Type objectType = null;
- if (!method.IsStatic) {
- EmitInstance(obj, objectType = obj.Type);
- }
- // if the obj has a value type, its address is passed to the method call so we cannot destroy the
- // stack by emitting a tail call
- if (obj != null && obj.Type.IsValueType) {
- EmitMethodCall(method, methodCallExpr, objectType);
- } else {
- EmitMethodCall(method, methodCallExpr, objectType, flags);
- }
- }
- // assumes 'object' of non-static call is already on stack
- private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType) {
- EmitMethodCall(mi, args, objectType, CompilationFlags.EmitAsNoTail);
- }
- // assumes 'object' of non-static call is already on stack
- private void EmitMethodCall(MethodInfo mi, IArgumentProvider args, Type objectType, CompilationFlags flags) {
- // Emit arguments
- List<WriteBack> wb = EmitArguments(mi, args);
- // Emit the actual call
- OpCode callOp = UseVirtual(mi) ? OpCodes.Callvirt : OpCodes.Call;
- if (callOp == OpCodes.Callvirt && objectType.IsValueType) {
- // This automatically boxes value types if necessary.
- _ilg.Emit(OpCodes.Constrained, objectType);
- }
- // The method call can be a tail call if
- // 1) the method call is the last instruction before Ret
- // 2) the method does not have any ByRef parameters, refer to ECMA-335 Partition III Section 2.4.
- // "Verification requires that no managed pointers are passed to the method being called, since
- // it does not track pointers into the current frame."
- if ((flags & CompilationFlags.EmitAsTailCallMask) == CompilationFlags.EmitAsTail && !MethodHasByRefParameter(mi)) {
- _ilg.Emit(OpCodes.Tailcall);
- }
- if (mi.CallingConvention == CallingConventions.VarArgs) {
- _ilg.EmitCall(callOp, mi, args.Map(a => a.Type));
- } else {
- _ilg.Emit(callOp, mi);
- }
- // Emit writebacks for properties passed as "ref" arguments
- EmitWriteBack(wb);
- }
- private static bool MethodHasByRefParameter(MethodInfo mi) {
- foreach (var pi in mi.GetParametersCached()) {
- if (pi.IsByRefParameter()) {
- return true;
- }
- }
- return false;
- }
- private void EmitCall(Type objectType, MethodInfo method) {
- if (method.CallingConvention == CallingConventions.VarArgs) {
- throw Error.UnexpectedVarArgsCall(method);
- }
- OpCode callOp = UseVirtual(method) ? OpCodes.Callvirt : OpCodes.Call;
- if (callOp == OpCodes.Callvirt && objectType.IsValueType) {
- _ilg.Emit(OpCodes.Constrained, objectType);
- }
- _ilg.Emit(callOp, method);
- }
- private static bool UseVirtual(MethodInfo mi) {
- // There are two factors: is the method static, virtual or non-virtual instance?
- // And is the object ref or value?
- // The cases are:
- //
- // static, ref: call
- // static, value: call
- // virtual, ref: callvirt
- // virtual, value: call -- eg, double.ToString must be a non-virtual call to be verifiable.
- // instance, ref: callvirt -- this looks wrong, but is verifiable and gives us a free null check.
- // instance, value: call
- //
- // We never need to generate a nonvirtual call to a virtual method on a reference type because
- // expression trees do not support "base.Foo()" style calling.
- //
- // We could do an optimization here for the case where we know that the object is a non-null
- // reference type and the method is a non-virtual instance method. For example, if we had
- // (new Foo()).Bar() for instance method Bar we don't need the null check so we could do a
- // call rather than a callvirt. However that seems like it would not be a very big win for
- // most dynamically generated code scenarios, so let's not do that for now.
- if (mi.IsStatic) {
- return false;
- }
- if (mi.DeclaringType.IsValueType) {
- return false;
- }
- return true;
- }
- /// <summary>
- /// Emits arguments to a call, and returns an array of writebacks that
- /// should happen after the call.
- /// </summary>
- private List<WriteBack> EmitArguments(MethodBase method, IArgumentProvider args) {
- return EmitArguments(method, args, 0);
- }
- /// <summary>
- /// Emits arguments to a call, and returns an array of writebacks that
- /// should happen after the call. For emitting dynamic expressions, we
- /// need to skip the first parameter of the method (the call site).
- /// </summary>
- private List<WriteBack> EmitArguments(MethodBase method, IArgumentProvider args, int skipParameters) {
- ParameterInfo[] pis = method.GetParametersCached();
- Debug.Assert(args.ArgumentCount + skipParameters == pis.Length);
- var writeBacks = new List<WriteBack>();
- for (int i = skipParameters, n = pis.Length; i < n; i++) {
- ParameterInfo parameter = pis[i];
- Expression argument = args.GetArgument(i - skipParameters);
- Type type = parameter.ParameterType;
- if (type.IsByRef) {
- type = type.GetElementType();
- WriteBack wb = EmitAddressWriteBack(argument, type);
- if (wb != null) {
- writeBacks.Add(wb);
- }
- } else {
- EmitExpression(argument);
- }
- }
- return writeBacks;
- }
- private static void EmitWriteBack(IList<WriteBack> writeBacks) {
- foreach (WriteBack wb in writeBacks) {
- wb();
- }
- }
- #endregion
- private void EmitConstantExpression(Expression expr) {
- ConstantExpression node = (ConstantExpression)expr;
- EmitConstant(node.Value, node.Type);
- }
- private void EmitConstant(object value, Type type) {
- // Try to emit the constant directly into IL
- if (ILGen.CanEmitConstant(value, type)) {
- _ilg.EmitConstant(value, type);
- return;
- }
- _boundConstants.EmitConstant(this, value, type);
- }
- private void EmitDynamicExpression(Expression expr) {
- if (!(_method is DynamicMethod)) {
- throw Error.CannotCompileDynamic();
- }
- var node = (DynamicExpression)expr;
- var site = CallSite.Create(node.DelegateType, node.Binder);
- Type siteType = site.GetType();
- var invoke = node.DelegateType.GetMethod("Invoke");
- // site.Target.Invoke(site, args)
- EmitConstant(site, siteType);
- // Emit the temp as type CallSite so we get more reuse
- _ilg.Emit(OpCodes.Dup);
- #if !FEATURE_CORE_DLR
- // For 3.5, emit the temp as CallSite<T> to work around a Jit32
- // verifier issue (fixed in 3.5 sp1)
- var siteTemp = GetLocal(siteType);
- #else
- var siteTemp = GetLocal(typeof(CallSite));
- #endif
- _ilg.Emit(OpCodes.Stloc, siteTemp);
- _ilg.Emit(OpCodes.Ldfld, siteType.GetField("Target"));
- _ilg.Emit(OpCodes.Ldloc, siteTemp);
- FreeLocal(siteTemp);
- List<WriteBack> wb = EmitArguments(invoke, node, 1);
- _ilg.Emit(OpCodes.Callvirt, invoke);
- EmitWriteBack(wb);
- }
- private void EmitNewExpression(Expression expr) {
- NewExpression node = (NewExpression)expr;
- if (node.Constructor != null) {
- List<WriteBack> wb = EmitArguments(node.Constructor, node);
- _ilg.Emit(OpCodes.Newobj, node.Constructor);
- EmitWriteBack(wb);
- } else {
- Debug.Assert(node.Arguments.Count == 0, "Node with arguments must have a constructor.");
- Debug.Assert(node.Type.IsValueType, "Only value type may have constructor not set.");
- LocalBuilder temp = GetLocal(node.Type);
- _ilg.Emit(OpCodes.Ldloca, temp);
- _ilg.Emit(OpCodes.Initobj, node.Type);
- _ilg.Emit(OpCodes.Ldloc, temp);
- FreeLocal(temp);
- }
- }
- private void EmitTypeBinaryExpression(Expression expr) {
- TypeBinaryExpression node = (TypeBinaryExpression)expr;
- if (node.NodeType == ExpressionType.TypeEqual) {
- EmitExpression(node.ReduceTypeEqual());
- return;
- }
- Type type = node.Expression.Type;
- // Try to determine the result statically
- AnalyzeTypeIsResult result = ConstantCheck.AnalyzeTypeIs(node);
- if (result == AnalyzeTypeIsResult.KnownTrue ||
- result == AnalyzeTypeIsResult.KnownFalse) {
- // Result is known statically, so just emit the expression for
- // its side effects and return the result
- EmitExpressionAsVoid(node.Expression);
- _ilg.EmitBoolean(result == AnalyzeTypeIsResult.KnownTrue);
- return;
- }
- if (result == AnalyzeTypeIsResult.KnownAssignable) {
- // We know the type can be assigned, but still need to check
- // for null at runtime
- if (type.IsNullableType()) {
- EmitAddress(node.Expression, type);
- _ilg.EmitHasValue(type);
- return;
- }
- Debug.Assert(!type.IsValueType);
- EmitExpression(node.Expression);
- _ilg.Emit(OpCodes.Ldnull);
- _ilg.Emit(OpCodes.Ceq);
- _ilg.Emit(OpCodes.Ldc_I4_0);
- _ilg.Emit(OpCodes.Ceq);
- return;
- }
- Debug.Assert(result == AnalyzeTypeIsResult.Unknown);
- // Emit a full runtime "isinst" check
- EmitExpression(node.Expression);
- if (type.IsValueType) {
- _ilg.Emit(OpCodes.Box, type);
- }
- _ilg.Emit(OpCodes.Isinst, node.TypeOperand);
- _ilg.Emit(OpCodes.Ldnull);
- _ilg.Emit(OpCodes.Cgt_Un);
- }
- private void EmitVariableAssignment(BinaryExpression node, CompilationFlags flags) {
- var variable = (ParameterExpression)node.Left;
- var emitAs = flags & CompilationFlags.EmitAsTypeMask;
- EmitExpression(node.Right);
- if (emitAs != CompilationFlags.EmitAsVoidType) {
- _ilg.Emit(OpCodes.Dup);
- }
- if (variable.IsByRef) {
- // Note: the stloc/ldloc pattern is a bit suboptimal, but it
- // saves us from having to spill stack when assigning to a
- // byref parameter. We already make this same tradeoff for
- // hoisted variables, see ElementStorage.EmitStore
- LocalBuilder value = GetLocal(variable.Type);
- _ilg.Emit(OpCodes.Stloc, value);
- _scope.EmitGet(variable);
- _ilg.Emit(OpCodes.Ldloc, value);
- FreeLocal(value);
- _ilg.EmitStoreValueIndirect(variable.Type);
- } else {
- _scope.EmitSet(variable);
- }
- }
- private void EmitAssignBinaryExpression(Expression expr) {
- EmitAssign((BinaryExpression)expr, CompilationFlags.EmitAsDefaultType);
- }
- private void EmitAssign(BinaryExpression node, CompilationFlags emitAs) {
- switch (node.Left.NodeType) {
- case ExpressionType.Index:
- EmitIndexAssignment(node, emitAs);
- return;
- case ExpressionType.MemberAccess:
- EmitMemberAssignment(node, emitAs);
- return;
- case ExpressionType.Parameter:
- EmitVariableAssignment(node, emitAs);
- return;
- default:
- throw Error.InvalidLvalue(node.Left.NodeType);
- }
- }
- private void EmitParameterExpression(Expression expr) {
- ParameterExpression node = (ParameterExpression)expr;
- _scope.EmitGet(node);
- if (node.IsByRef) {
- _ilg.EmitLoadValueIndirect(node.Type);
- }
- }
- private void EmitLambdaExpression(Expression expr) {
- LambdaExpression node = (LambdaExpression)expr;
- EmitDelegateConstruction(node);
- }
- private void EmitRuntimeVariablesExpression(Expression expr) {
- RuntimeVariablesExpression node = (RuntimeVariablesExpression)expr;
- _scope.EmitVariableAccess(this, node.Variables);
- }
- private void EmitMemberAssignment(BinaryExpression node, CompilationFlags flags) {
- MemberExpression lvalue = (MemberExpression)node.Left;
- MemberInfo member = lvalue.Member;
- // emit "this", if any
- Type objectType = null;
- if (lvalue.Expression != null) {
- EmitInstance(lvalue.Expression, objectType = lvalue.Expression.Type);
- }
- // emit value
- EmitExpression(node.Right);
- LocalBuilder temp = null;
- var emitAs = flags & CompilationFlags.EmitAsTypeMask;
- if (emitAs != CompilationFlags.EmitAsVoidType) {
- // save the value so we can return it
- _ilg.Emit(OpCodes.Dup);
- _ilg.Emit(OpCodes.Stloc, temp = GetLocal(node.Type));
- }
- switch (member.MemberType) {
- case MemberTypes.Field:
- _ilg.EmitFieldSet((FieldInfo)member);
- break;
- case MemberTypes.Property:
- EmitCall(objectType, ((PropertyInfo)member).GetSetMethod(true));
- break;
- default:
- throw Error.InvalidMemberType(member.MemberType);
- }
- if (emitAs != CompilationFlags.EmitAsVoidType) {
- _ilg.Emit(OpCodes.Ldloc, temp);
- FreeLocal(temp);
- }
- }
- private void EmitMemberExpression(Expression expr) {
- MemberExpression node = (MemberExpression)expr;
- // emit "this", if any
- Type instanceType = null;
- if (node.Expression != null) {
- EmitInstance(node.Expression, instanceType = node.Expression.Type);
- }
- EmitMemberGet(node.Member, instanceType);
- }
- // assumes instance is already on the stack
- private void EmitMemberGet(MemberInfo member, Type objectType) {
- switch (member.MemberType) {
- case MemberTypes.Field:
- FieldInfo fi = (FieldInfo)member;
- if (fi.IsLiteral) {
- EmitConstant(fi.GetRawConstantValue(), fi.FieldType);
- } else {
- _ilg.EmitFieldGet(fi);
- }
- break;
- case MemberTypes.Property:
- EmitCall(objectType, ((PropertyInfo)member).GetGetMethod(true));
- break;
- default:
- throw ContractUtils.Unreachable;
- }
- }
- private void EmitInstance(Expression instance, Type type) {
- if (instance != null) {
- if (type.IsValueType) {
- EmitAddress(instance, type);
- } else {
- EmitExpression(instance);
- }
- }
- }
- private void EmitNewArrayExpression(Expression expr) {
- NewArrayExpression node = (NewArrayExpression)expr;
- if (node.NodeType == ExpressionType.NewArrayInit) {
- _ilg.EmitArray(
- node.Type.GetElementType(),
- node.Expressions.Count,
- delegate(int index) {
- EmitExpression(node.Expressions[index]);
- }
- );
- } else {
- ReadOnlyCollection<Expression> bounds = node.Expressions;
- for (int i = 0; i < bounds.Count; i++) {
- Expression x = bounds[i];
- EmitExpression(x);
- _ilg.EmitConvertToType(x.Type, typeof(int), true);
- }
- _ilg.EmitArray(node.Type);
- }
- }
- private void EmitDebugInfoExpression(Expression expr) {
- if (!EmitDebugSymbols) {
- return;
- }
- var node = (DebugInfoExpression)expr;
- if (node.IsClear && _sequencePointCleared) {
- // Emitting another clearance after one clearance does not
- // have any effect, so we can save it.
- return;
- }
- _tree.DebugInfoGenerator.MarkSequencePoint(_lambda, _method, _ilg, node);
- _ilg.Emit(OpCodes.Nop);
- _sequencePointCleared = node.IsClear;
- }
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "expr")]
- private static void EmitExtensionExpression(Expression expr) {
- throw Error.ExtensionNotReduced();
- }
- #region ListInit, MemberInit
- private void EmitListInitExpression(Expression expr) {
- EmitListInit((ListInitExpression)expr);
- }
- private void EmitMemberInitExpression(Expression expr) {
- EmitMemberInit((MemberInitExpression)expr);
- }
- private void EmitBinding(MemberBinding binding, Type objectType) {
- switch (binding.BindingType) {
- case MemberBindingType.Assignment:
- EmitMemberAssignment((MemberAssignment)binding, objectType);
- break;
- case MemberBindingType.ListBinding:
- EmitMemberListBinding((MemberListBinding)binding);
- break;
- case MemberBindingType.MemberBinding:
- EmitMemberMemberBinding((MemberMemberBinding)binding);
- break;
- default:
- throw Error.UnknownBindingType();
- }
- }
- private void EmitMemberAssignment(MemberAssignment binding, Type objectType) {
- EmitExpression(binding.Expression);
- FieldInfo fi = binding.Member as FieldInfo;
- if (fi != null) {
- _ilg.Emit(OpCodes.Stfld, fi);
- } else {
- PropertyInfo pi = binding.Member as PropertyInfo;
- if (pi != null) {
- EmitCall(objectType, pi.GetSetMethod(true));
- } else {
- throw Error.UnhandledBinding();
- }
- }
- }
- private void EmitMemberMemberBinding(MemberMemberBinding binding) {
- Type type = GetMemberType(binding.Member);
- if (binding.Member is PropertyInfo && type.IsValueType) {
- throw Error.CannotAutoInitializeValueTypeMemberThroughProperty(binding.Member);
- }
- if (type.IsValueType) {
- EmitMemberAddress(binding.Member, binding.Member.DeclaringType);
- } else {
- EmitMemberGet(binding.Member, binding.Member.DeclaringType);
- }
- EmitMemberInit(binding.Bindings, false, type);
- }
- private void EmitMemberListBinding(MemberListBinding binding) {
- Type type = GetMemberType(binding.Member);
- if (binding.Member is PropertyInfo && type.IsValueType) {
- throw Error.CannotAutoInitializeValueTypeElementThroughProperty(binding.Member);
- }
- if (type.IsValueType) {
- EmitMemberAddress(binding.Member, binding.Member.DeclaringType);
- } else {
- EmitMemberGet(binding.Member, binding.Member.DeclaringType);
- }
- EmitListInit(binding.Initializers, false, type);
- }
- private void EmitMemberInit(MemberInitExpression init) {
- EmitExpression(init.NewExpression);
- LocalBuilder loc = null;
- if (init.NewExpression.Type.IsValueType && init.Bindings.Count > 0) {
- loc = _ilg.DeclareLocal(init.NewExpression.Type);
- _ilg.Emit(OpCodes.Stloc, loc);
- _ilg.Emit(OpCodes.Ldloca, loc);
- }
- EmitMemberInit(init.Bindings, loc == null, init.NewExpression.Type);
- if (loc != null) {
- _ilg.Emit(OpCodes.Ldloc, loc);
- }
- }
- // This method assumes that the instance is on the stack and is expected, based on "keepOnStack" flag
- // to either leave the instance on the stack, or pop it.
- private void EmitMemberInit(ReadOnlyCollection<MemberBinding> bindings, bool keepOnStack, Type objectType) {
- int n = bindings.Count;
- if (n == 0) {
- // If there are no initializers and instance is not to be kept on the stack, we must pop explicitly.
- if (!keepOnStack) {
- _ilg.Emit(OpCodes.Pop);
- }
- } else {
- for (int i = 0; i < n; i++) {
- if (keepOnStack || i < n - 1) {
- _ilg.Emit(OpCodes.Dup);
- }
- EmitBinding(bindings[i], objectType);
- }
- }
- }
- private void EmitListInit(ListInitExpression init) {
- EmitExpression(init.NewExpression);
- LocalBuilder loc = null;
- if (init.NewExpression.Type.IsValueType) {
- loc = _ilg.DeclareLocal(init.NewExpression.Type);
- _ilg.Emit(OpCodes.Stloc, loc);
- _ilg.Emit(OpCodes.Ldloca, loc);
- }
- EmitListInit(init.Initializers, loc == null, init.NewExpression.Type);
- if (loc != null) {
- _ilg.Emit(OpCodes.Ldloc, loc);
- }
- }
- // This method assumes that the list instance is on the stack and is expected, based on "keepOnStack" flag
- // to either leave the list instance on the stack, or pop it.
- private void EmitListInit(ReadOnlyCollection<ElementInit> initializers, bool keepOnStack, Type objectType) {
- int n = initializers.Count;
- if (n == 0) {
- // If there are no initializers and instance is not to be kept on the stack, we must pop explicitly.
- if (!keepOnStack) {
- _ilg.Emit(OpCodes.Pop);
- }
- } else {
- for (int i = 0; i < n; i++) {
- if (keepOnStack || i < n - 1) {
- _ilg.Emit(OpCodes.Dup);
- }
- EmitMethodCall(initializers[i].AddMethod, initializers[i], objectType);
- // Aome add methods, ArrayList.Add for example, return non-void
- if (initializers[i].AddMethod.ReturnType != typeof(void)) {
- _ilg.Emit(OpCodes.Pop);
- }
- }
- }
- }
- private static Type GetMemberType(MemberInfo member) {
- FieldInfo fi = member as FieldInfo;
- if (fi != null) return fi.FieldType;
- PropertyInfo pi = member as PropertyInfo;
- if (pi != null) return pi.PropertyType;
- throw Error.MemberNotFieldOrProperty(member);
- }
- #endregion
- #region Expression helpers
- internal static void ValidateLift(IList<ParameterExpression> variables, IList<Expression> arguments) {
- System.Diagnostics.Debug.Assert(variables != null);
- System.Diagnostics.Debug.Assert(arguments != null);
- if (variables.Count != arguments.Count) {
- throw Error.IncorrectNumberOfIndexes();
- }
- for (int i = 0, n = variables.Count; i < n; i++) {
- if (!TypeUtils.AreReferenceAssignable(variables[i].Type, TypeUtils.GetNonNullableType(arguments[i].Type))) {
- throw Error.ArgumentTypesMustMatch();
- }
- }
- }
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
- private void EmitLift(ExpressionType nodeType, Type resultType, MethodCallExpression mc, ParameterExpression[] paramList, Expression[] argList) {
- Debug.Assert(TypeUtils.AreEquivalent(TypeUtils.GetNonNullableType(resultType), TypeUtils.GetNonNullableType(mc.Type)));
- switch (nodeType) {
- default:
- case ExpressionType.LessThan:
- case ExpressionType.LessThanOrEqual:
- case ExpressionType.GreaterThan:
- case ExpressionType.GreaterThanOrEqual: {
- Label exit = _ilg.DefineLabel();
- Label exitNull = _ilg.DefineLabel();
- LocalBuilder anyNull = _ilg.DeclareLocal(typeof(bool));
- for (int i = 0, n = paramList.Length; i < n; i++) {
- ParameterExpression v = paramList[i];
- Expression arg = argList[i];
- if (TypeUtils.IsNullableType(arg.Type)) {
- _scope.AddLocal(this, v);
- EmitAddress(arg, arg.Type);
- _ilg.Emit(OpCodes.Dup);
- _ilg.EmitHasValue(arg.Type);
- _ilg.Emit(OpCodes.Ldc_I4_0);
- _ilg.Emit(OpCodes.Ceq);
- _ilg.Emit(OpCodes.Stloc, anyNull);
- _ilg.EmitGetValueOrDefault(arg.Type);
- _scope.EmitSet(v);
- } else {
- _scope.AddLocal(this, v);
- EmitExpression(arg);
- if (!arg.Type.IsValueType) {
- _ilg.Emit(OpCodes.Dup);
- _ilg.Emit(OpCodes.Ldnull);
- _ilg.Emit(OpCodes.Ceq);
- _ilg.Emit(OpCodes.Stloc, anyNull);
- }
- _scope.EmitSet(v);
- }
- _ilg.Emit(OpCodes.Ldloc, anyNull);
- _ilg.Emit(OpCodes.Brtrue, exitNull);
- }
- EmitMethodCallExpression(mc);
- if (TypeUtils.IsNullableType(resultType) && !TypeUtils.AreEquivalent(resultType, mc.Type)) {
- ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type });
- _ilg.Emit(OpCodes.Newobj, ci);
- }
- _ilg.Emit(OpCodes.Br_S, exit);
- _ilg.MarkLabel(exitNull);
- if (TypeUtils.AreEquivalent(resultType, TypeUtils.GetNullableType(mc.Type))) {
- if (resultType.IsValueType) {
- LocalBuilder result = GetLocal(resultType);
- _ilg.Emit(OpCodes.Ldloca, result);
- _ilg.Emit(OpCodes.Initobj, resultType);
- _ilg.Emit(OpCodes.Ldloc, result);
- FreeLocal(result);
- } else {
- _ilg.Emit(OpCodes.Ldnull);
- }
- } else {
- switch (nodeType) {
- case ExpressionType.LessThan:
- case ExpressionType.LessThanOrEqual:
- case ExpressionType.GreaterThan:
- case ExpressionType.GreaterThanOrEqual:
- _ilg.Emit(OpCodes.Ldc_I4_0);
- break;
- default:
- throw Error.UnknownLiftType(nodeType);
- }
- }
- _ilg.MarkLabel(exit);
- return;
- }
- case ExpressionType.Equal:
- case ExpressionType.NotEqual: {
- if (TypeUtils.AreEquivalent(resultType, TypeUtils.GetNullableType(mc.Type))) {
- goto default;
- }
- Label exit = _ilg.DefineLabel();
- Label exitAllNull = _ilg.DefineLabel();
- Label exitAnyNull = _ilg.DefineLabel();
- LocalBuilder anyNull = _ilg.DeclareLocal(typeof(bool));
- LocalBuilder allNull = _ilg.DeclareLocal(typeof(bool));
- _ilg.Emit(OpCodes.Ldc_I4_0);
- _ilg.Emit(OpCodes.Stloc, anyNull);
- _ilg.Emit(OpCodes.Ldc_I4_1);
- _ilg.Emit(OpCodes.Stloc, allNull);
- for (int i = 0, n = paramList.Length; i < n; i++) {
- ParameterExpression v = paramList[i];
- Expression arg = argList[i];
- _scope.AddLocal(this, v);
- if (TypeUtils.IsNullableType(arg.Type)) {
- EmitAddress(arg, arg.Type);
- _ilg.Emit(OpCodes.Dup);
- _ilg.EmitHasValue(arg.Type);
- _ilg.Emit(OpCodes.Ldc_I4_0);
- _ilg.Emit(OpCodes.Ceq);
- _ilg.Emit(OpCodes.Dup);
- _ilg.Emit(OpCodes.Ldloc, anyNull);
- _ilg.Emit(OpCodes.Or);
- _ilg.Emit(OpCodes.Stloc, anyNull);
- _ilg.Emit(OpCodes.Ldloc, allNull);
- _ilg.Emit(OpCodes.And);
- _ilg.Emit(OpCodes.Stloc, allNull);
- _ilg.EmitGetValueOrDefault(arg.Type);
- } else {
- EmitExpression(arg);
- if (!arg.Type.IsValueType) {
- _ilg.Emit(OpCodes.Dup);
- _ilg.Emit(OpCodes.Ldnull);
- _ilg.Emit(OpCodes.Ceq);
- _ilg.Emit(OpCodes.Dup);
- _ilg.Emit(OpCodes.Ldloc, anyNull);
- _ilg.Emit(OpCodes.Or);
- _ilg.Emit(OpCodes.Stloc, anyNull);
- _ilg.Emit(OpCodes.Ldloc, allNull);
- _ilg.Emit(OpCodes.And);
- _ilg.Emit(OpCodes.Stloc, allNull);
- } else {
- _ilg.Emit(OpCodes.Ldc_I4_0);
- _ilg.Emit(OpCodes.Stloc, allNull);
- }
- }
- _scope.EmitSet(v);
- }
- _ilg.Emit(OpCodes.Ldloc, allNull);
- _ilg.Emit(OpCodes.Brtrue, exitAllNull);
- _ilg.Emit(OpCodes.Ldloc, anyNull);
- _ilg.Emit(OpCodes.Brtrue, exitAnyNull);
- EmitMethodCallExpression(mc);
- if (TypeUtils.IsNullableType(resultType) && !TypeUtils.AreEquivalent(resultType, mc.Type)) {
- ConstructorInfo ci = resultType.GetConstructor(new Type[] { mc.Type });
- _ilg.Emit(OpCodes.Newobj, ci);
- }
- _ilg.Emit(OpCodes.Br_S, exit);
- _ilg.MarkLabel(exitAllNull);
- _ilg.EmitBoolean(nodeType == ExpressionType.Equal);
- _ilg.Emit(OpCodes.Br_S, exit);
- _ilg.MarkLabel(exitAnyNull);
- _ilg.EmitBoolean(nodeType == ExpressionType.NotEqual);
- _ilg.MarkLabel(exit);
- return;
- }
- }
- }
- #endregion
- }
- }