LambdaCompiler.Expressions.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / Microsoft / Scripting / Compiler / LambdaCompiler.Expressions.cs / 1305376 / LambdaCompiler.Expressions.cs

                            /* **************************************************************************** 
 *
 * Copyright (c) Microsoft Corporation.
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If
 * you cannot locate the  Microsoft Public License, 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 Microsoft Public License.
 * 
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/ 

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 

namespace System.Linq.Expressions.Compiler { 
    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
        } 
 
        /// 
        /// Update the flag with a new EmitAsTailCall flag 
        /// 
        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;
        } 
 
        /// 
        /// Update the flag with a new EmitExpressionStart flag 
        /// 
        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;
        } 
 
        /// 
        /// Update the flag with a new EmitAsType flag 
        /// 
        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;
        } 
 
        /// 
        /// 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.
        /// 
        internal void EmitExpression(Expression node) { 
            EmitExpression(node, CompilationFlags.EmitAsNoTail | CompilationFlags.EmitExpressionStart);
        } 
 
        /// 
        /// Emits an expression and discards the result.  For some nodes this emits 
        /// more optimial code then EmitExpression/Pop
        /// 
        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 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 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;
        } 
 
        /// 
        /// Emits arguments to a call, and returns an array of writebacks that 
        /// should happen after the call.
        /// 
        private List EmitArguments(MethodBase method, IArgumentProvider args) {
            return EmitArguments(method, args, 0); 
        }
 
        ///  
        /// 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).
        /// 
        private List EmitArguments(MethodBase method, IArgumentProvider args, int skipParameters) {
            ParameterInfo[] pis = method.GetParametersCached(); 
            Debug.Assert(args.ArgumentCount + skipParameters == pis.Length);
 
            var writeBacks = new List(); 
            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 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 MICROSOFT_SCRIPTING_CORE
            // For 3.5, emit the temp as CallSite 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 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 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 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 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 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 variables, IList 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
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK