File: Microsoft\Scripting\Compiler\LambdaCompiler.Expressions.cs
Project: ndp\fx\src\Core\System.Core.csproj (System.Core)
/* ****************************************************************************
 *
 * 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 SILVERLIGHT
using System.Core;
#endif
 
#if CLR2
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 CLR2
            // 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
    }
}