Exemple #1
0
        protected override bool EmitInternal(ConditionalExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var test                = node.Test;
            var ifTrue              = node.IfTrue;
            var ifFalse             = node.IfFalse;
            var ifTrueBranchIsEmpty = ifTrue.NodeType == ExpressionType.Default && ifTrue.Type == typeof(void);

            if (ifTrueBranchIsEmpty)
            {
                test = Expression.Not(test);
                var temp = ifTrue;
                ifTrue  = ifFalse;
                ifFalse = temp;
            }
            var  result          = false;
            var  il              = context.Il;
            var  testIsNullLabel = il.DefineLabel("testIsNull");
            Type testType;
            var  testIsNullLabelUsed = ExpressionEmittersCollection.Emit(test, context, testIsNullLabel, out testType);

            if (testType == typeof(bool?))
            {
                context.ConvertFromNullableBoolToBool();
            }
            var ifFalseLabel = il.DefineLabel("ifFalse");

            il.Brfalse(ifFalseLabel);
            Type ifTrueType;

            result |= ExpressionEmittersCollection.Emit(ifTrue, context, returnDefaultValueLabel, whatReturn, extend, out ifTrueType);
            if (node.Type == typeof(void) && ifTrueType != typeof(void))
            {
                using (var temp = context.DeclareLocal(ifTrueType))
                    il.Stloc(temp);
            }
            var doneLabel = il.DefineLabel("done");

            il.Br(doneLabel);
            if (testIsNullLabelUsed)
            {
                context.MarkLabelAndSurroundWithSP(testIsNullLabel);
                il.Pop();
            }
            context.MarkLabelAndSurroundWithSP(ifFalseLabel);
            Type ifFalseType;

            result |= ExpressionEmittersCollection.Emit(ifFalse, context, returnDefaultValueLabel, whatReturn, extend, out ifFalseType);
            if (node.Type == typeof(void) && ifFalseType != typeof(void))
            {
                using (var temp = context.DeclareLocal(ifFalseType))
                    il.Stloc(temp);
            }
            context.MarkLabelAndSurroundWithSP(doneLabel);
            if (ifTrueType != typeof(void) && ifFalseType != typeof(void) && ifTrueType != ifFalseType)
            {
                throw new InvalidOperationException(string.Format("ifTrue type '{0}' is not equal to ifFalse type '{1}'", ifTrueType, ifFalseType));
            }
            resultType = node.Type == typeof(void) ? typeof(void) : ifTrueType;
            return(result);
        }
        protected override bool EmitInternal(NewExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            context.EmitLoadArguments(node.Arguments.ToArray());
            // note ich: баг решарпера
            // ReSharper disable ConditionIsAlwaysTrueOrFalse
            // ReSharper disable HeuristicUnreachableCode
            GroboIL il = context.Il;

            if (node.Constructor != null)
            {
                il.Newobj(node.Constructor);
            }
            else
            {
                if (node.Type.IsValueType)
                {
                    using (var temp = context.DeclareLocal(node.Type))
                    {
                        il.Ldloca(temp);
                        il.Initobj(node.Type);
                        il.Ldloc(temp);
                    }
                }
                else
                {
                    throw new InvalidOperationException("Missing constructor for type '" + node.Type + "'");
                }
            }
            resultType = node.Type;
            // ReSharper restore ConditionIsAlwaysTrueOrFalse
            // ReSharper restore HeuristicUnreachableCode
            return(false);
        }
        // todo обобщить
        private static void EmitExpressionAsVoid(Expression node, EmittingContext context)
        {
            var il = context.Il;

            GroboIL.Label skipLabel = context.CanReturn ? il.DefineLabel("skip") : null;
            Type          resultType;
            bool          skipLabelUsed = ExpressionEmittersCollection.Emit(node, context, skipLabel, ResultType.Void, false, out resultType);

            if (skipLabelUsed)
            {
                var endLabel = il.DefineLabel("end");
                if (resultType != typeof(void))
                {
                    if (!resultType.IsStruct())
                    {
                        il.Pop();
                    }
                    else
                    {
                        using (var temp = context.DeclareLocal(resultType))
                            il.Stloc(temp);
                    }
                }
                il.Br(endLabel);
                context.MarkLabelAndSurroundWithSP(skipLabel);
                il.Pop();
                context.MarkLabelAndSurroundWithSP(endLabel);
            }
        }
        protected override bool EmitInternal(MemberInitExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            ExpressionEmittersCollection.Emit(node.NewExpression, context, out resultType); // stack: [new obj(args)]
            GroboIL il = context.Il;

            if (!node.Type.IsValueType)
            {
                foreach (MemberAssignment assignment in node.Bindings)
                {
                    il.Dup();
                    context.EmitLoadArguments(assignment.Expression);
                    context.EmitMemberAssign(node.Type, assignment.Member);
                }
            }
            else
            {
                using (var temp = context.DeclareLocal(node.Type))
                {
                    il.Stloc(temp);
                    il.Ldloca(temp);
                    foreach (MemberAssignment assignment in node.Bindings)
                    {
                        il.Dup();
                        context.EmitLoadArguments(assignment.Expression);
                        context.EmitMemberAssign(node.Type, assignment.Member);
                    }
                    // todo или il.Ldobj()?
                    il.Pop();
                    il.Ldloc(temp);
                }
            }
            return(false);
        }
        protected override bool EmitInternal(ListInitExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var il     = context.Il;
            var result = ExpressionEmittersCollection.Emit(node.NewExpression, context, returnDefaultValueLabel, out resultType);

            if (!resultType.IsValueType)
            {
                foreach (var initializer in node.Initializers)
                {
                    il.Dup();
                    context.EmitLoadArguments(initializer.Arguments.ToArray());
                    il.Call(initializer.AddMethod, resultType);
                }
            }
            else
            {
                if (node.Initializers.Count > 0)
                {
                    using (var temp = context.DeclareLocal(resultType))
                    {
                        il.Stloc(temp);
                        foreach (var initializer in node.Initializers)
                        {
                            il.Ldloca(temp);
                            context.EmitLoadArguments(initializer.Arguments.ToArray());
                            il.Call(initializer.AddMethod, resultType);
                        }
                        il.Ldloc(temp);
                    }
                }
            }
            return(result);
        }
        protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            if (node.Conversion != null)
            {
                throw new NotSupportedException("Coalesce with conversion is not supported");
            }
            // note ich: баг решарпера
            // ReSharper disable HeuristicUnreachableCode
            var     left  = node.Left;
            var     right = node.Right;
            GroboIL il    = context.Il;

            GroboIL.Label valueIsNullLabel = il.DefineLabel("valueIsNull");
            Type          leftType;
            bool          labelUsed = ExpressionEmittersCollection.Emit(left, context, valueIsNullLabel, out leftType);

            if (left.Type.IsValueType)
            {
                using (var temp = context.DeclareLocal(left.Type))
                {
                    il.Stloc(temp);
                    il.Ldloca(temp);
                }
            }
            labelUsed |= context.EmitNullChecking(left.Type, valueIsNullLabel);
            if (left.Type.IsValueType)
            {
                if (!left.Type.IsNullable())
                {
                    throw new InvalidOperationException("Type '" + left.Type + "' cannot be null");
                }
                if (node.Type != left.Type)
                {
                    context.EmitValueAccess(left.Type);
                }
                else
                {
                    il.Ldobj(left.Type);
                }
            }
            var valueIsNotNullLabel = il.DefineLabel("valueIsNotNull");

            il.Br(valueIsNotNullLabel);
            if (labelUsed)
            {
                context.MarkLabelAndSurroundWithSP(valueIsNullLabel);
                il.Pop();
            }
            Type rightType;
            var  result = ExpressionEmittersCollection.Emit(right, context, returnDefaultValueLabel, out rightType);

            context.MarkLabelAndSurroundWithSP(valueIsNotNullLabel);
            resultType = node.Type;
            return(result);
            // ReSharper restore HeuristicUnreachableCode
        }
        protected override bool EmitInternal(LoopExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var il = context.Il;

            GroboIL.Label continueLabel;
            if (node.ContinueLabel == null)
            {
                continueLabel = context.Il.DefineLabel("continue");
            }
            else
            {
                if (!context.Labels.TryGetValue(node.ContinueLabel, out continueLabel))
                {
                    context.Labels.Add(node.ContinueLabel, continueLabel = context.Il.DefineLabel(string.IsNullOrEmpty(node.ContinueLabel.Name) ? "continue" : node.ContinueLabel.Name));
                }
            }
            context.MarkLabelAndSurroundWithSP(continueLabel);
            GroboIL.Label breakLabel;
            if (node.BreakLabel == null)
            {
                breakLabel = null;
            }
            else
            {
                if (!context.Labels.TryGetValue(node.BreakLabel, out breakLabel))
                {
                    context.Labels.Add(node.BreakLabel, breakLabel = context.Il.DefineLabel(string.IsNullOrEmpty(node.BreakLabel.Name) ? "break" : node.BreakLabel.Name));
                }
            }
            ExpressionEmittersCollection.Emit(node.Body, context, out resultType);
            if (resultType != typeof(void))
            {
                if (resultType.IsStruct())
                {
                    using (var temp = context.DeclareLocal(resultType))
                        context.Il.Stloc(temp);
                }
                else
                {
                    context.Il.Pop();
                }
            }
            il.Br(continueLabel);
            if (breakLabel != null)
            {
                context.MarkLabelAndSurroundWithSP(breakLabel);
            }
            if (node.BreakLabel != null)
            {
                resultType = node.BreakLabel.Type;
            }
            return(false);
        }
        protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            if (node.Type != typeof(bool) && node.Type != typeof(bool?))
            {
                return(ExpressionEmittersCollection.Emit(Expression.OnesComplement(node.Operand, node.Method), context, returnDefaultValueLabel, whatReturn, extend, out resultType));
            }
            GroboIL il = context.Il;

            if (node.Method != null)
            {
                throw new NotSupportedException("Custom operator '" + node.NodeType + "' is not supported");
            }
            var operand = node.Operand;

            context.EmitLoadArgument(operand, false, out resultType);
            if (resultType == typeof(bool))
            {
                il.Ldc_I4(1);
                il.Xor();
            }
            else if (resultType == typeof(bool?))
            {
                using (var value = context.DeclareLocal(typeof(bool?)))
                {
                    il.Stloc(value);
                    il.Ldloca(value);
                    context.EmitHasValueAccess(typeof(bool?));
                    var returnLabel = il.DefineLabel("return");
                    il.Brfalse(returnLabel);
                    il.Ldloca(value);
                    context.EmitValueAccess(typeof(bool?));
                    il.Ldc_I4(1);
                    il.Xor();
                    il.Newobj(nullableBoolConstructor);
                    il.Stloc(value);
                    context.MarkLabelAndSurroundWithSP(returnLabel);
                    il.Ldloc(value);
                }
            }
            else
            {
                throw new InvalidOperationException("Cannot perform '" + node.NodeType + "' operator on type '" + resultType + "'");
            }
            return(false);
        }
        protected override bool EmitInternal(TypeBinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            bool    result;
            GroboIL il = context.Il;

            if (!node.Expression.Type.IsAssignableFrom(node.TypeOperand))
            {
                il.Ldc_I4(0);
                result = false;
            }
            else if (node.Expression.Type == node.TypeOperand && node.TypeOperand.IsValueType)
            {
                il.Ldc_I4(1);
                result = false;
            }
            else
            {
                Type operandType;
                result = ExpressionEmittersCollection.Emit(node.Expression, context, returnDefaultValueLabel, ResultType.Value, extend, out operandType);
                if (operandType.IsValueType)
                {
                    using (var temp = context.DeclareLocal(operandType))
                    {
                        il.Stloc(temp);
                        il.Ldloca(temp);
                    }
                }
                var returnFalseLabel = il.DefineLabel("returnFalse");
                il.Dup();
                il.Brfalse(returnFalseLabel);
                il.Call(typeof(object).GetMethod("GetType"), operandType);
                il.Ldtoken(node.TypeOperand);
                il.Call(typeof(Type).GetMethod("GetTypeFromHandle"));
                il.Ceq();
                var doneLabel = il.DefineLabel("done");
                il.Br(doneLabel);
                context.MarkLabelAndSurroundWithSP(returnFalseLabel);
                il.Pop();
                il.Ldc_I4(0);
                context.MarkLabelAndSurroundWithSP(doneLabel);
            }
            resultType = typeof(bool);
            return(result);
        }
        protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var  left = node.Left;
            var  right = node.Right;
            Type leftType, rightType;

            context.EmitLoadArgument(left, false, out leftType);
            context.EmitLoadArgument(right, false, out rightType);
            var il = context.Il;

            if (node.Method != null)
            {
                if (!leftType.IsNullable() && !rightType.IsNullable())
                {
                    il.Call(node.Method);
                }
                else
                {
                    using (var localLeft = context.DeclareLocal(leftType))
                        using (var localRight = context.DeclareLocal(rightType))
                        {
                            il.Stloc(localRight);
                            il.Stloc(localLeft);
                            var returnNullLabel = il.DefineLabel("returnNull");
                            if (leftType.IsNullable())
                            {
                                il.Ldloca(localLeft);
                                context.EmitHasValueAccess(leftType);
                                il.Brfalse(returnNullLabel);
                            }
                            if (rightType.IsNullable())
                            {
                                il.Ldloca(localRight);
                                context.EmitHasValueAccess(rightType);
                                il.Brfalse(returnNullLabel);
                            }
                            if (!leftType.IsNullable())
                            {
                                il.Ldloc(localLeft);
                            }
                            else
                            {
                                il.Ldloca(localLeft);
                                context.EmitValueAccess(leftType);
                            }
                            if (!rightType.IsNullable())
                            {
                                il.Ldloc(localRight);
                            }
                            else
                            {
                                il.Ldloca(localRight);
                                context.EmitValueAccess(rightType);
                            }
                            il.Call(node.Method);

                            var doneLabel = il.DefineLabel("done");
                            il.Br(doneLabel);
                            context.MarkLabelAndSurroundWithSP(returnNullLabel);
                            context.EmitLoadDefaultValue(node.Type);
                            context.MarkLabelAndSurroundWithSP(doneLabel);
                        }
                }
                resultType = node.Method.ReturnType;
            }
            else
            {
                var type = leftType;
                if (type != rightType)
                {
                    throw new InvalidOperationException("Cannot compare objects of different types '" + leftType + "' and '" + rightType + "'");
                }
                if (!type.IsNullable())
                {
                    switch (node.NodeType)
                    {
                    case ExpressionType.GreaterThan:
                        il.Cgt(type.Unsigned());
                        break;

                    case ExpressionType.LessThan:
                        il.Clt(type.Unsigned());
                        break;

                    case ExpressionType.GreaterThanOrEqual:
                        il.Clt(type.Unsigned());
                        il.Ldc_I4(1);
                        il.Xor();
                        break;

                    case ExpressionType.LessThanOrEqual:
                        il.Cgt(type.Unsigned());
                        il.Ldc_I4(1);
                        il.Xor();
                        break;

                    default:
                        throw new InvalidOperationException();
                    }
                    resultType = typeof(bool);
                }
                else
                {
                    if (!context.Options.HasFlag(CompilerOptions.UseTernaryLogic))
                    {
                        using (var localLeft = context.DeclareLocal(type))
                            using (var localRight = context.DeclareLocal(type))
                            {
                                il.Stloc(localRight);
                                il.Stloc(localLeft);
                                il.Ldloca(localLeft);
                                context.EmitValueAccess(type);
                                il.Ldloca(localRight);
                                context.EmitValueAccess(type);
                                var returnFalseLabel = il.DefineLabel("returnFalse");

                                var argument = type.GetGenericArguments()[0];
                                switch (node.NodeType)
                                {
                                case ExpressionType.GreaterThan:
                                    il.Ble(returnFalseLabel, argument.Unsigned());
                                    break;

                                case ExpressionType.LessThan:
                                    il.Bge(returnFalseLabel, argument.Unsigned());
                                    break;

                                case ExpressionType.GreaterThanOrEqual:
                                    il.Blt(returnFalseLabel, argument.Unsigned());
                                    break;

                                case ExpressionType.LessThanOrEqual:
                                    il.Bgt(returnFalseLabel, argument.Unsigned());
                                    break;

                                default:
                                    throw new InvalidOperationException();
                                }
                                il.Ldloca(localLeft);
                                context.EmitHasValueAccess(type);
                                il.Ldloca(localRight);
                                context.EmitHasValueAccess(type);
                                il.And();
                                var doneLabel = il.DefineLabel("done");
                                il.Br(doneLabel);
                                context.MarkLabelAndSurroundWithSP(returnFalseLabel);
                                il.Ldc_I4(0);
                                context.MarkLabelAndSurroundWithSP(doneLabel);
                                resultType = typeof(bool);
                            }
                    }
                    else
                    {
                        using (var localLeft = context.DeclareLocal(type))
                            using (var localRight = context.DeclareLocal(type))
                            {
                                il.Stloc(localRight);
                                il.Stloc(localLeft);
                                il.Ldloca(localLeft);
                                context.EmitHasValueAccess(type);
                                il.Ldloca(localRight);
                                context.EmitHasValueAccess(type);
                                il.And();
                                var returnNullLabel = il.DefineLabel("returnNull");
                                il.Brfalse(returnNullLabel);
                                il.Ldloca(localLeft);
                                context.EmitValueAccess(type);
                                il.Ldloca(localRight);
                                context.EmitValueAccess(type);
                                var argumentType = type.GetGenericArguments()[0];

                                switch (node.NodeType)
                                {
                                case ExpressionType.GreaterThan:
                                    il.Cgt(argumentType.Unsigned());
                                    break;

                                case ExpressionType.LessThan:
                                    il.Clt(argumentType.Unsigned());
                                    break;

                                case ExpressionType.GreaterThanOrEqual:
                                    il.Clt(argumentType.Unsigned());
                                    il.Ldc_I4(1);
                                    il.Xor();
                                    break;

                                case ExpressionType.LessThanOrEqual:
                                    il.Cgt(argumentType.Unsigned());
                                    il.Ldc_I4(1);
                                    il.Xor();
                                    break;

                                default:
                                    throw new InvalidOperationException();
                                }
                                il.Newobj(nullableBoolConstructor);

                                var doneLabel = il.DefineLabel("done");
                                il.Br(doneLabel);
                                context.MarkLabelAndSurroundWithSP(returnNullLabel);
                                context.EmitLoadDefaultValue(typeof(bool?));
                                context.MarkLabelAndSurroundWithSP(doneLabel);
                                resultType = typeof(bool?);
                            }
                    }
                }
            }
            return(false);
        }
        public static bool Emit(Expression zarr, Expression zindex, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var arrayType = zarr.Type;
            var isArray   = arrayType.IsArray;

            if (!isArray && !arrayType.IsList())
            {
                throw new InvalidOperationException("Unable to perform array index operator to type '" + arrayType + "'");
            }
            var     itemType = isArray ? arrayType.GetElementType() : arrayType.GetGenericArguments()[0];
            GroboIL il       = context.Il;

            EmittingContext.LocalHolder arrayIndex = null;
            bool extendArray        = extend && (CanAssign(zarr) || !isArray);
            bool extendArrayElement = extend && itemType.IsClass;
            var  result             = false;

            if (!extendArray)
            {
                result |= ExpressionEmittersCollection.Emit(zarr, context, returnDefaultValueLabel, ResultType.Value, extend, out arrayType); // stack: [array]
                if (context.Options.HasFlag(CompilerOptions.CheckNullReferences))
                {
                    result = true;
                    il.Dup();                              // stack: [array, array]
                    il.Brfalse(returnDefaultValueLabel);   // if(array == null) goto returnDefaultValue; stack: [array]
                }
                EmitLoadIndex(zindex, context, arrayType); // stack: [array, arrayIndex]
                if (context.Options.HasFlag(CompilerOptions.CheckArrayIndexes))
                {
                    result     = true;
                    arrayIndex = context.DeclareLocal(typeof(int));
                    il.Stloc(arrayIndex); // arrayIndex = index; stack: [array]
                    il.Dup();             // stack: [array, array]
                    if (isArray)
                    {
                        il.Ldlen(); // stack: [array, array.Length]
                    }
                    else
                    {
                        EmitLoadField(context, arrayType, arrayType.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic));
                    }
                    il.Ldloc(arrayIndex);                   // stack: [array, array.Length, arrayIndex]
                    il.Ble(returnDefaultValueLabel, false); // if(array.Length <= arrayIndex) goto returnDefaultValue; stack: [array]
                    il.Ldloc(arrayIndex);                   // stack: [array, arrayIndex]
                    il.Ldc_I4(0);                           // stack: [array, arrayIndex, 0]
                    il.Blt(returnDefaultValueLabel, false); // if(arrayIndex < 0) goto returnDefaultValue; stack: [array]
                }
                else if (extendArrayElement || !isArray)
                {
                    arrayIndex = context.DeclareLocal(typeof(int));
                    il.Stloc(arrayIndex); // arrayIndex = index; stack: [array]
                }
            }
            else
            {
                EmittingContext.LocalHolder arrayOwner = null;
                switch (zarr.NodeType)
                {
                case ExpressionType.Parameter:
                case ExpressionType.ArrayIndex:
                case ExpressionType.Index:
                    Type type;
                    ExpressionEmittersCollection.Emit(zarr, context, returnDefaultValueLabel, ResultType.ByRefAll, true, out type); // stack: [ref array]
                    arrayOwner = context.DeclareLocal(type);
                    il.Dup();                                                                                                       // stack: [ref array, ref array]
                    il.Stloc(arrayOwner);                                                                                           // arrayOwner = ref array; stack: [ref array]
                    il.Ldind(zarr.Type);                                                                                            // stack: [array]
                    break;

                case ExpressionType.MemberAccess:
                    var  memberExpression = (MemberExpression)zarr;
                    Type memberType;
                    context.EmitMemberAccess(memberExpression, returnDefaultValueLabel, context.Options.HasFlag(CompilerOptions.CheckNullReferences), true, ResultType.ByRefValueTypesOnly, out memberType, out arrayOwner); // stack: [array]
                    break;

                default:
                    throw new InvalidOperationException("Cannot extend array for expression with node type '" + zarr.NodeType + "'");
                }
                if (context.Options.HasFlag(CompilerOptions.CheckNullReferences))
                {
                    il.Dup();                            // stack: [array, array]
                    il.Brfalse(returnDefaultValueLabel); // if(array == null) goto returnDefaultValue; stack: [array]
                }
                EmitLoadIndex(zindex, context, arrayType);
                result     = true;
                arrayIndex = context.DeclareLocal(typeof(int));
                il.Stloc(arrayIndex);                   // arrayIndex = index; stack: [array]
                il.Ldloc(arrayIndex);                   // stack: [array, arrayIndex]
                il.Ldc_I4(0);                           // stack: [array, arrayIndex, 0]
                il.Blt(returnDefaultValueLabel, false); // if(arrayIndex < 0) goto returnDefaultValue; stack: [array]
                il.Dup();                               // stack: [array, array]
                if (isArray)
                {
                    il.Ldlen(); // stack: [array, array.Length]
                }
                else
                {
                    EmitLoadField(context, arrayType, arrayType.GetField("_size", BindingFlags.Instance | BindingFlags.NonPublic));
                }
                il.Ldloc(arrayIndex);          // stack: [array, array.Length, arrayIndex]
                var bigEnoughLabel = il.DefineLabel("bigEnough");
                il.Bgt(bigEnoughLabel, false); // if(array.Length > arrayIndex) goto bigEnough; stack: [array]
                using (var array = context.DeclareLocal(arrayType))
                {
                    il.Stloc(array); // stack: []
                    if (!isArray)
                    {
                        EnsureCount(context, array, arrayIndex, arrayType);
                    }
                    else
                    {
                        il.Ldloca(array);                                                         // stack: [ref array]
                        il.Ldloc(arrayIndex);                                                     // stack: [ref array, arrayIndex]
                        il.Ldc_I4(1);                                                             // stack: [ref array, arrayIndex, 1]
                        il.Add();                                                                 // stack: [ref array, arrayIndex + 1]
                        il.Call(arrayResizeMethod.MakeGenericMethod(arrayType.GetElementType())); // Array.Resize(ref array, 1 + arrayIndex); stack: []

                        switch (zarr.NodeType)
                        {
                        case ExpressionType.Parameter:
                        case ExpressionType.ArrayIndex:
                        case ExpressionType.Index:
                            il.Ldloc(arrayOwner); // stack: [ref parameter]
                            il.Ldloc(array);      // stack: [ref parameter, array]
                            il.Stind(arrayType);  // parameter = array; stack: []
                            break;

                        case ExpressionType.MemberAccess:
                            var memberExpression = (MemberExpression)zarr;
                            if (memberExpression.Expression != null)
                            {
                                il.Ldloc(arrayOwner);
                            }
                            il.Ldloc(array);
                            switch (memberExpression.Member.MemberType)
                            {
                            case MemberTypes.Field:
                                il.Stfld((FieldInfo)memberExpression.Member);
                                break;

                            case MemberTypes.Property:
                                var propertyInfo = (PropertyInfo)memberExpression.Member;
                                var setter       = propertyInfo.GetSetMethod(context.SkipVisibility);
                                if (setter == null)
                                {
                                    throw new MissingMethodException(propertyInfo.ReflectedType.ToString(), "set_" + propertyInfo.Name);
                                }
                                il.Call(setter, memberExpression.Expression == null ? null : memberExpression.Expression.Type);
                                break;

                            default:
                                throw new NotSupportedException("Member type '" + memberExpression.Member.MemberType + "' is not supported");
                            }
                            break;

                        default:
                            throw new InvalidOperationException("Unable to assign array to an expression with node type '" + zarr.NodeType);
                        }
                    }
                    il.Ldloc(array);
                    context.MarkLabelAndSurroundWithSP(bigEnoughLabel);
                }
            }

            if (!isArray)
            {
                // TODO: это злобно, лист при всех операциях меняет _version, а мы нет
                EmitLoadField(context, arrayType, arrayType.GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic));
                arrayType = itemType.MakeArrayType();
            }

            if (extendArrayElement)
            {
                // stack: [array]
                var constructor = itemType.GetConstructor(Type.EmptyTypes);
                if (itemType.IsArray || constructor != null)
                {
                    using (var array = context.DeclareLocal(arrayType))
                    {
                        il.Dup();             // stack: [array, array]
                        il.Stloc(array);      // stack: [array]
                        il.Ldloc(arrayIndex); // stack: [array, arrayIndex]
                        il.Ldelem(itemType);  // stack: [array[arrayIndex]]
                        var elementIsNotNullLabel = il.DefineLabel("elementIsNotNull");
                        il.Brtrue(elementIsNotNullLabel);
                        il.Ldloc(array);
                        il.Ldloc(arrayIndex);
                        context.Create(itemType);
                        il.Stelem(itemType);
                        context.MarkLabelAndSurroundWithSP(elementIsNotNullLabel);
                        il.Ldloc(array);
                    }
                }
            }
            if (arrayIndex != null)
            {
                il.Ldloc(arrayIndex);
                arrayIndex.Dispose();
            }
            switch (whatReturn)
            {
            case ResultType.ByRefAll:
                il.Ldelema(itemType);
                resultType = itemType.MakeByRefType();
                break;

            case ResultType.ByRefValueTypesOnly:
                if (itemType.IsValueType)
                {
                    il.Ldelema(itemType);
                    resultType = itemType.MakeByRefType();
                }
                else
                {
                    il.Ldelem(itemType); // stack: [array[arrayIndex]]
                    resultType = itemType;
                }
                break;

            default:
                il.Ldelem(itemType); // stack: [array[arrayIndex]]
                resultType = itemType;
                break;
            }
            return(result);
        }
        private static void EmitAssign(AssigneeKind assigneeKind, Expression node, EmittingContext context, object[] arguments)
        {
            var il = context.Il;

            switch (assigneeKind)
            {
            case AssigneeKind.Parameter:
                var index = Array.IndexOf(context.Parameters, node);
                if (index >= 0)
                {
                    il.Starg(index);
                }
                else
                {
                    GroboIL.Local variable;
                    if (context.VariablesToLocals.TryGetValue((ParameterExpression)node, out variable))
                    {
                        il.Stloc(variable);
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown parameter " + node);
                    }
                }
                break;

            case AssigneeKind.SimpleArray:
                il.Stind(node.Type);
                break;

            case AssigneeKind.InstanceField:
            case AssigneeKind.StaticField:
                il.Stfld((FieldInfo)((MemberExpression)node).Member);
                break;

            case AssigneeKind.InstanceProperty:
            case AssigneeKind.StaticProperty:
                var memberExpression = (MemberExpression)node;
                il.Call(((PropertyInfo)memberExpression.Member).GetSetMethod(context.SkipVisibility), memberExpression.Expression == null ? null : memberExpression.Expression.Type);
                break;

            case AssigneeKind.IndexedProperty:
            {
                using (var temp = context.DeclareLocal(node.Type))
                {
                    il.Stloc(temp);
                    var indexExpression = (IndexExpression)node;
                    if (arguments == null)
                    {
                        context.EmitLoadArguments(indexExpression.Arguments.ToArray());
                    }
                    else
                    {
                        foreach (var argument in arguments)
                        {
                            if (argument is Expression)
                            {
                                context.EmitLoadArguments((Expression)argument);
                            }
                            else
                            {
                                var local = (EmittingContext.LocalHolder)argument;
                                il.Ldloc(local);
                                local.Dispose();
                            }
                        }
                    }
                    il.Ldloc(temp);
                    MethodInfo setter = indexExpression.Indexer.GetSetMethod(context.SkipVisibility);
                    if (setter == null)
                    {
                        throw new MissingMethodException(indexExpression.Indexer.ReflectedType.ToString(), "set_" + indexExpression.Indexer.Name);
                    }
                    context.Il.Call(setter, indexExpression.Object.Type);
                }
            }
            break;

            case AssigneeKind.MultiDimensionalArray:
            {
                using (var temp = context.DeclareLocal(node.Type))
                {
                    il.Stloc(temp);
                    var  indexExpression = (IndexExpression)node;
                    Type arrayType       = indexExpression.Object.Type;
                    if (!arrayType.IsArray)
                    {
                        throw new InvalidOperationException("An array expected");
                    }
                    int rank = arrayType.GetArrayRank();
                    if (rank != indexExpression.Arguments.Count)
                    {
                        throw new InvalidOperationException("Incorrect number of indeces '" + indexExpression.Arguments.Count + "' provided to access an array with rank '" + rank + "'");
                    }
                    Type indexType = indexExpression.Arguments.First().Type;
                    if (indexType != typeof(int))
                    {
                        throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed");
                    }
                    if (arguments == null)
                    {
                        context.EmitLoadArguments(indexExpression.Arguments.ToArray());
                    }
                    else
                    {
                        foreach (var argument in arguments)
                        {
                            if (argument is Expression)
                            {
                                context.EmitLoadArguments((Expression)argument);
                            }
                            else
                            {
                                var local = (EmittingContext.LocalHolder)argument;
                                il.Ldloc(local);
                                local.Dispose();
                            }
                        }
                    }
                    il.Ldloc(temp);
                    MethodInfo setMethod = arrayType.GetMethod("Set");
                    if (setMethod == null)
                    {
                        throw new MissingMethodException(arrayType.ToString(), "Set");
                    }
                    context.Il.Call(setMethod, arrayType);
                }
            }
            break;
            }
        }
        private static object[] EmitAccess(AssigneeKind assigneeKind, Expression node, EmittingContext context)
        {
            object[] arguments = null;
            var      il        = context.Il;

            switch (assigneeKind)
            {
            case AssigneeKind.Parameter:
                var index = Array.IndexOf(context.Parameters, node);
                if (index >= 0)
                {
                    il.Ldarg(index);
                }
                else
                {
                    GroboIL.Local variable;
                    if (context.VariablesToLocals.TryGetValue((ParameterExpression)node, out variable))
                    {
                        il.Ldloc(variable);
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown parameter " + node);
                    }
                }
                break;

            case AssigneeKind.InstanceField:
            case AssigneeKind.StaticField:
                il.Ldfld((FieldInfo)((MemberExpression)node).Member);
                break;

            case AssigneeKind.InstanceProperty:
            case AssigneeKind.StaticProperty:
                var memberExpression = (MemberExpression)node;
                il.Call(((PropertyInfo)memberExpression.Member).GetGetMethod(context.SkipVisibility), memberExpression.Expression == null ? null : memberExpression.Expression.Type);
                break;

            case AssigneeKind.SimpleArray:
                il.Ldind(node.Type);
                break;

            case AssigneeKind.IndexedProperty:
            {
                var indexExpression = (IndexExpression)node;
                var args            = new List <object>();
                foreach (var argument in indexExpression.Arguments)
                {
                    context.EmitLoadArguments(argument);
                    if (argument.NodeType == ExpressionType.Constant || (argument.NodeType == ExpressionType.MemberAccess && ((MemberExpression)argument).Member.MemberType == MemberTypes.Field && ((FieldInfo)((MemberExpression)argument).Member).IsStatic))
                    {
                        args.Add(argument);
                    }
                    else
                    {
                        var local = context.DeclareLocal(argument.Type);
                        args.Add(local);
                        il.Stloc(local);
                        il.Ldloc(local);
                    }
                }
                arguments = args.ToArray();
                MethodInfo getter = indexExpression.Indexer.GetGetMethod(context.SkipVisibility);
                if (getter == null)
                {
                    throw new MissingMethodException(indexExpression.Indexer.ReflectedType.ToString(), "get_" + indexExpression.Indexer.Name);
                }
                context.Il.Call(getter, indexExpression.Object.Type);
            }
            break;

            case AssigneeKind.MultiDimensionalArray:
            {
                var  indexExpression = (IndexExpression)node;
                Type arrayType       = indexExpression.Object.Type;
                if (!arrayType.IsArray)
                {
                    throw new InvalidOperationException("An array expected");
                }
                int rank = arrayType.GetArrayRank();
                if (rank != indexExpression.Arguments.Count)
                {
                    throw new InvalidOperationException("Incorrect number of indeces '" + indexExpression.Arguments.Count + "' provided to access an array with rank '" + rank + "'");
                }
                Type indexType = indexExpression.Arguments.First().Type;
                if (indexType != typeof(int))
                {
                    throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed");
                }
                var args = new List <object>();
                foreach (var argument in indexExpression.Arguments)
                {
                    context.EmitLoadArguments(argument);
                    if (argument.NodeType == ExpressionType.Constant || (argument.NodeType == ExpressionType.MemberAccess && ((MemberExpression)argument).Member.MemberType == MemberTypes.Field && ((FieldInfo)((MemberExpression)argument).Member).IsStatic))
                    {
                        args.Add(argument);
                    }
                    else
                    {
                        var local = context.DeclareLocal(argument.Type);
                        args.Add(local);
                        il.Stloc(local);
                        il.Ldloc(local);
                    }
                }
                arguments = args.ToArray();
                MethodInfo getMethod = arrayType.GetMethod("Get");
                if (getMethod == null)
                {
                    throw new MissingMethodException(arrayType.ToString(), "Get");
                }
                context.Il.Call(getMethod, arrayType);
            }
            break;
            }
            return(arguments);
        }
        protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            GroboIL      il      = context.Il;
            var          result  = false;
            var          operand = node.Operand;
            Type         assigneeType;
            AssigneeKind assigneeKind;
            bool         checkNullReferences = context.Options.HasFlag(CompilerOptions.CheckNullReferences);

            extend |= context.Options.HasFlag(CompilerOptions.ExtendOnAssign);

            GroboIL.Label assigneeIsNullLabel     = null;
            bool          assigneeIsNullLabelUsed = false;

            switch (operand.NodeType)
            {
            case ExpressionType.Parameter:
                assigneeType        = null;
                assigneeKind        = AssigneeKind.Parameter;
                checkNullReferences = false;
                break;

            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression)operand;
                if (memberExpression.Expression == null)
                {
                    assigneeType        = null;
                    assigneeKind        = memberExpression.Member is FieldInfo ? AssigneeKind.StaticField : AssigneeKind.StaticProperty;
                    checkNullReferences = false;
                }
                else
                {
                    bool closureAssign = memberExpression.Expression == context.ParsedLambda.ClosureParameter || memberExpression.Expression.Type.IsStaticClosure();
                    checkNullReferences &= !closureAssign;
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ExpressionEmittersCollection.Emit(memberExpression.Expression, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    else
                    {
                        assigneeIsNullLabel     = !closureAssign && context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(memberExpression.Expression, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    assigneeKind = memberExpression.Member is FieldInfo ? AssigneeKind.InstanceField : AssigneeKind.InstanceProperty;
                }
                break;

            case ExpressionType.Index:
                var indexExpression = (IndexExpression)operand;
                if (indexExpression.Object == null)
                {
                    throw new InvalidOperationException("Indexing of null object is invalid");
                }
                if (indexExpression.Object.Type.IsArray && indexExpression.Object.Type.GetArrayRank() == 1)
                {
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ExpressionEmittersCollection.Emit(Expression.ArrayIndex(indexExpression.Object, indexExpression.Arguments.Single()), context, returnDefaultValueLabel, ResultType.ByRefAll, extend, out assigneeType);
                        checkNullReferences = false;
                    }
                    else
                    {
                        assigneeIsNullLabel     = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(Expression.ArrayIndex(indexExpression.Object, indexExpression.Arguments.Single()), context, assigneeIsNullLabel, ResultType.ByRefAll, extend, out assigneeType);
                    }
                    assigneeKind = AssigneeKind.SimpleArray;
                }
                else
                {
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ExpressionEmittersCollection.Emit(indexExpression.Object, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    else
                    {
                        assigneeIsNullLabel     = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(indexExpression.Object, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    assigneeKind = indexExpression.Indexer != null ? AssigneeKind.IndexedProperty : AssigneeKind.MultiDimensionalArray;
                }
                break;

            default:
                throw new InvalidOperationException("Unable to assign to an expression of type '" + operand.NodeType + "'");
            }
            if (assigneeType != null && assigneeType.IsValueType)
            {
                using (var temp = context.DeclareLocal(assigneeType))
                {
                    il.Stloc(temp);
                    il.Ldloca(temp);
                }
                assigneeType = assigneeType.MakeByRefType();
            }
            if (assigneeIsNullLabelUsed)
            {
                context.EmitReturnDefaultValue(assigneeType, assigneeIsNullLabel, il.DefineLabel("assigneeIsNotNull"));
            }

            if (checkNullReferences)
            {
                il.Dup();
                il.Brfalse(returnDefaultValueLabel);
                result = true;
            }

            if (assigneeType != null)
            {
                il.Dup();
            }
            object[] arguments = EmitAccess(assigneeKind, operand, context);
            if (!operand.Type.IsNullable())
            {
                if (whatReturn == ResultType.Void)
                {
                    EmitOp(node.NodeType, node.Method, node.Type, context);
                    EmitAssign(assigneeKind, operand, context, arguments);
                }
                else
                {
                    if (node.NodeType == ExpressionType.PostDecrementAssign || node.NodeType == ExpressionType.PostIncrementAssign)
                    {
                        using (var assignmentResult = context.DeclareLocal(operand.Type))
                        {
                            il.Stloc(assignmentResult);
                            il.Ldloc(assignmentResult);
                            EmitOp(node.NodeType, node.Method, node.Type, context);
                            EmitAssign(assigneeKind, operand, context, arguments);
                            il.Ldloc(assignmentResult);
                        }
                    }
                    else
                    {
                        EmitOp(node.NodeType, node.Method, node.Type, context);
                        using (var assignmentResult = context.DeclareLocal(operand.Type))
                        {
                            il.Stloc(assignmentResult);
                            EmitAssign(assigneeKind, operand, context, arguments, assignmentResult);
                            il.Ldloc(assignmentResult);
                        }
                    }
                }
            }
            else
            {
                using (var value = context.DeclareLocal(operand.Type))
                {
                    il.Stloc(value);
                    il.Ldloca(value);
                    context.EmitHasValueAccess(operand.Type);
                    var returnNullLabel = il.DefineLabel("returnNull");
                    il.Brfalse(returnNullLabel);
                    il.Ldloca(value);
                    context.EmitValueAccess(operand.Type);
                    Type            argumentType = operand.Type.GetGenericArguments()[0];
                    ConstructorInfo constructor  = operand.Type.GetConstructor(new[] { argumentType });
                    if (whatReturn == ResultType.Void)
                    {
                        EmitOp(node.NodeType, node.Method, argumentType, context);
                        il.Newobj(constructor);
                        EmitAssign(assigneeKind, operand, context, arguments);
                    }
                    else
                    {
                        if (node.NodeType == ExpressionType.PostDecrementAssign || node.NodeType == ExpressionType.PostIncrementAssign)
                        {
                            EmitOp(node.NodeType, node.Method, argumentType, context);
                            il.Newobj(constructor);
                            EmitAssign(assigneeKind, operand, context, arguments);
                            il.Ldloc(value);
                        }
                        else
                        {
                            EmitOp(node.NodeType, node.Method, argumentType, context);
                            il.Newobj(constructor);
                            using (var assignmentResult = context.DeclareLocal(operand.Type))
                            {
                                il.Stloc(assignmentResult);
                                EmitAssign(assigneeKind, operand, context, arguments, assignmentResult);
                                il.Ldloc(assignmentResult);
                            }
                        }
                    }
                    var doneLabel = il.DefineLabel("done");
                    il.Br(doneLabel);
                    context.MarkLabelAndSurroundWithSP(returnNullLabel);
                    if (assigneeType != null)
                    {
                        il.Pop();
                    }
                    if (whatReturn != ResultType.Void)
                    {
                        il.Ldloc(value);
                    }
                    context.MarkLabelAndSurroundWithSP(doneLabel);
                }
            }
            resultType = whatReturn == ResultType.Void ? typeof(void) : operand.Type;
            return(result);
        }
        protected override bool EmitInternal(IndexExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            if (node.Object == null)
            {
                throw new InvalidOperationException("Indexing of null object is invalid");
            }
            if (node.Object.Type.IsArray && node.Object.Type.GetArrayRank() == 1)
            {
                return(ExpressionEmittersCollection.Emit(Expression.ArrayIndex(node.Object, node.Arguments.Single()), context, returnDefaultValueLabel, whatReturn, extend, out resultType));
            }
            if (node.Object.Type.IsList())
            {
                return(ArrayIndexExpressionEmitter.Emit(node.Object, node.Arguments.Single(), context, returnDefaultValueLabel, whatReturn, extend, out resultType));
            }
            Type objectType;
            bool result = ExpressionEmittersCollection.Emit(node.Object, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out objectType);

            if (objectType.IsValueType)
            {
                using (var temp = context.DeclareLocal(objectType))
                {
                    context.Il.Stloc(temp);
                    context.Il.Ldloca(temp);
                }
            }
            if (context.Options.HasFlag(CompilerOptions.CheckNullReferences))
            {
                result |= context.EmitNullChecking(objectType, returnDefaultValueLabel);
            }
            if (node.Indexer != null)
            {
                context.EmitLoadArguments(node.Arguments.ToArray());
                MethodInfo getter = node.Indexer.GetGetMethod(context.SkipVisibility);
                if (getter == null)
                {
                    throw new MissingMethodException(node.Indexer.ReflectedType.ToString(), "get_" + node.Indexer.Name);
                }
                GroboIL.Label doneLabel = null;
                if (node.Object.Type.IsDictionary())
                {
                    var valueType   = node.Object.Type.GetGenericArguments()[1];
                    var constructor = valueType.GetConstructor(Type.EmptyTypes);
                    extend   &= (valueType.IsClass && constructor != null) || valueType.IsArray || valueType.IsValueType || valueType == typeof(string);
                    doneLabel = context.Il.DefineLabel("done");
                    using (var dict = context.DeclareLocal(node.Object.Type))
                        using (var key = context.DeclareLocal(node.Arguments.Single().Type))
                            using (var value = context.DeclareLocal(valueType))
                            {
                                context.Il.Stloc(key);    // key = arg0; stack: [dict]
                                context.Il.Dup();         // stack: [dict, dict]
                                context.Il.Stloc(dict);   // stack: [dict]
                                context.Il.Ldloc(key);    // stack: [dict, key]
                                context.Il.Ldloca(value); // stack: [dict, key, ref value]
                                var tryGetValueMethod = node.Object.Type.GetMethod("TryGetValue", BindingFlags.Public | BindingFlags.Instance);
                                if (tryGetValueMethod == null)
                                {
                                    throw new MissingMethodException(node.Object.Type.Name, "TryGetValue");
                                }
                                context.Il.Call(tryGetValueMethod, node.Object.Type); // stack: [dict.TryGetValue(key, out value)]
                                var loadResultLabel = context.Il.DefineLabel("loadResult");
                                context.Il.Brtrue(loadResultLabel);                   // if(dict.TryGetValue(key, out result)) goto loadResult; stack: []
                                if (valueType.IsValueType)
                                {
                                    context.Il.Ldloca(value);
                                    context.Il.Initobj(valueType); // value = default(valueType)
                                    context.Il.Ldloc(value);       // stack: [default(valueType)]
                                }
                                else if (valueType == typeof(string))
                                {
                                    context.Il.Ldstr("");
                                }
                                else
                                {
                                    context.Create(valueType);
                                }
                                context.Il.Stloc(value);
                                if (extend)
                                {
                                    context.Il.Ldloc(dict);  // stack: [dict]
                                    context.Il.Ldloc(key);   // stack: [dict, key]
                                    context.Il.Ldloc(value); // stack: [dict, key, value]
                                    var setter = node.Indexer.GetSetMethod(context.SkipVisibility);
                                    if (setter == null)
                                    {
                                        throw new MissingMethodException(node.Indexer.ReflectedType.ToString(), "set_" + node.Indexer.Name);
                                    }
                                    context.Il.Call(setter, node.Object.Type); // dict.set_Item(key, default(valueType)); stack: []
                                }
                                context.MarkLabelAndSurroundWithSP(loadResultLabel);
                                context.Il.Ldloc(value);
                            }
                }
                if (doneLabel != null)
                {
                    context.MarkLabelAndSurroundWithSP(doneLabel);
                }
                else
                {
                    context.Il.Call(getter, node.Object.Type);
                }
            }
            else
            {
                Type arrayType = node.Object.Type;
                if (!arrayType.IsArray)
                {
                    throw new InvalidOperationException("An array expected");
                }
                int rank = arrayType.GetArrayRank();
                if (rank != node.Arguments.Count)
                {
                    throw new InvalidOperationException("Incorrect number of indeces '" + node.Arguments.Count + "' provided to access an array with rank '" + rank + "'");
                }
                Type indexType = node.Arguments.First().Type;
                if (indexType != typeof(int))
                {
                    throw new InvalidOperationException("Indexing array with an index of type '" + indexType + "' is not allowed");
                }
                context.EmitLoadArguments(node.Arguments.ToArray());
                MethodInfo getMethod = arrayType.GetMethod("Get");
                if (getMethod == null)
                {
                    throw new MissingMethodException(arrayType.ToString(), "Get");
                }
                context.Il.Call(getMethod, arrayType);
            }
            resultType = node.Type;
            return(result);
        }
        protected override bool EmitInternal(SwitchExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            GroboIL il           = context.Il;
            var     defaultLabel = il.DefineLabel("default");
            var     caseLabels   = new GroboIL.Label[node.Cases.Count];

            GroboIL.Label switchValueIsNullLabel = null;
            for (int index = 0; index < node.Cases.Count; index++)
            {
                caseLabels[index] = il.DefineLabel("case#" + index);
            }
            context.EmitLoadArguments(node.SwitchValue);
            using (var switchValue = context.DeclareLocal(node.SwitchValue.Type))
            {
                il.Stloc(switchValue);
                Tuple <int, int, int> switchCase;
                if (context.ParsedLambda.ParsedSwitches.TryGetValue(node, out switchCase))
                {
                    // use simplified hashtable to locate the proper case
                    var labels = new List <GroboIL.Label>();
                    for (int index = 0; index < node.Cases.Count; index++)
                    {
                        foreach (var testValue in node.Cases[index].TestValues)
                        {
                            if (((ConstantExpression)testValue).Value != null)
                            {
                                labels.Add(caseLabels[index]);
                            }
                            else
                            {
                                switchValueIsNullLabel = caseLabels[index];
                            }
                        }
                    }
                    if (switchValueIsNullLabel != null)
                    {
                        if (!node.SwitchValue.Type.IsNullable())
                        {
                            il.Ldloc(switchValue);
                        }
                        else
                        {
                            il.Ldloca(switchValue);
                            context.EmitHasValueAccess(node.SwitchValue.Type);
                        }
                        il.Brfalse(switchValueIsNullLabel);
                    }
                    EmittingContext.LocalHolder pureSwitchValue = switchValue;
                    if (node.SwitchValue.Type.IsNullable())
                    {
                        pureSwitchValue = context.DeclareLocal(node.SwitchValue.Type.GetGenericArguments()[0]);
                        il.Ldloca(switchValue);
                        context.EmitValueAccess(node.SwitchValue.Type);
                        il.Stloc(pureSwitchValue);
                    }
                    Type temp;
                    ExpressionEmittersCollection.Emit(context.ParsedLambda.ConstantsBuilder.MakeAccess(context.ParsedLambda.ConstantsParameter, switchCase.Item1), context, out temp);
                    var type     = node.SwitchValue.Type.IsNullable() ? node.SwitchValue.Type.GetGenericArguments()[0] : node.SwitchValue.Type;
                    var typeCode = Type.GetTypeCode(type);
                    switch (typeCode)
                    {
                    case TypeCode.Byte:
                    case TypeCode.Char:
                    case TypeCode.Int16:
                    case TypeCode.Int32:
                    case TypeCode.Int64:
                    case TypeCode.SByte:
                    case TypeCode.UInt16:
                    case TypeCode.UInt32:
                    case TypeCode.UInt64:
                        il.Ldloc(pureSwitchValue);
                        break;

                    default:
                        if (type.IsValueType)
                        {
                            il.Ldloca(pureSwitchValue);
                        }
                        else
                        {
                            il.Ldloc(pureSwitchValue);
                        }
                        il.Call(typeof(object).GetMethod("GetHashCode"), type);
                        break;
                    }
                    using (var index = context.DeclareLocal(typeof(int)))
                    {
                        if (typeCode == TypeCode.Int64 || typeCode == TypeCode.UInt64)
                        {
                            il.Ldc_I8(switchCase.Item3);
                        }
                        else
                        {
                            il.Ldc_I4(switchCase.Item3);
                        }
                        il.Rem(true);
                        if (typeCode == TypeCode.Int64 || typeCode == TypeCode.UInt64)
                        {
                            il.Conv <int>();
                        }
                        il.Stloc(index);
                        il.Ldloc(index);
                        il.Ldelem(type);
                        il.Ldloc(pureSwitchValue);
                        if (node.Comparison != null)
                        {
                            il.Call(node.Comparison);
                        }
                        else
                        {
                            il.Ceq();
                        }
                        il.Brfalse(defaultLabel);
                        ExpressionEmittersCollection.Emit(context.ParsedLambda.ConstantsBuilder.MakeAccess(context.ParsedLambda.ConstantsParameter, switchCase.Item2), context, out temp);
                        il.Ldloc(index);
                        il.Ldelem(typeof(int));
                        il.Switch(labels.ToArray());
                    }
                    if (pureSwitchValue != switchValue)
                    {
                        pureSwitchValue.Dispose();
                    }
                }
                else
                {
                    // use a number of if/else branches to locate the proper case
                    EmittingContext.LocalHolder pureSwitchValue   = switchValue;
                    EmittingContext.LocalHolder switchValueIsNull = null;
                    if (node.SwitchValue.Type.IsNullable())
                    {
                        pureSwitchValue   = context.DeclareLocal(node.SwitchValue.Type.GetGenericArguments()[0]);
                        switchValueIsNull = context.DeclareLocal(typeof(bool));
                        il.Ldloca(switchValue);
                        il.Dup();
                        context.EmitValueAccess(node.SwitchValue.Type);
                        il.Stloc(pureSwitchValue);
                        context.EmitHasValueAccess(node.SwitchValue.Type);
                        il.Stloc(switchValueIsNull);
                    }
                    for (int index = 0; index < node.Cases.Count; index++)
                    {
                        var caSe  = node.Cases[index];
                        var label = caseLabels[index];
                        foreach (var testValue in caSe.TestValues)
                        {
                            context.EmitLoadArguments(testValue);
                            GroboIL.Label elseLabel = null;
                            if (testValue.Type.IsNullable())
                            {
                                elseLabel = il.DefineLabel("else");
                                using (var temp = context.DeclareLocal(testValue.Type))
                                {
                                    il.Stloc(temp);
                                    il.Ldloca(temp);
                                    context.EmitHasValueAccess(testValue.Type);
                                    if (switchValueIsNull != null)
                                    {
                                        il.Ldloc(switchValueIsNull);
                                        il.Or();
                                        il.Brfalse(label);
                                        il.Ldloca(temp);
                                        context.EmitHasValueAccess(testValue.Type);
                                        il.Ldloc(switchValueIsNull);
                                        il.And();
                                    }
                                    il.Brfalse(elseLabel);
                                    il.Ldloca(temp);
                                    context.EmitValueAccess(testValue.Type);
                                }
                            }
                            il.Ldloc(pureSwitchValue);
                            if (node.Comparison != null)
                            {
                                il.Call(node.Comparison);
                            }
                            else
                            {
                                il.Ceq();
                            }
                            il.Brtrue(label);
                            if (elseLabel != null)
                            {
                                context.MarkLabelAndSurroundWithSP(elseLabel);
                            }
                        }
                    }
                }
            }
            context.MarkLabelAndSurroundWithSP(defaultLabel);
            var doneLabel = il.DefineLabel("done");

            context.EmitLoadArguments(node.DefaultBody);
            il.Br(doneLabel);
            for (int index = 0; index < node.Cases.Count; ++index)
            {
                context.MarkLabelAndSurroundWithSP(caseLabels[index]);
                context.EmitLoadArguments(node.Cases[index].Body);
                if (index < node.Cases.Count - 1)
                {
                    il.Br(doneLabel);
                }
            }
            context.MarkLabelAndSurroundWithSP(doneLabel);
            resultType = node.Type;
            return(false);
        }
Exemple #17
0
        protected override bool EmitInternal(MethodCallExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var        result = false;
            GroboIL    il     = context.Il;
            var        method = node.Method;
            Expression obj;
            IEnumerable <Expression>    arguments;
            IEnumerable <ParameterInfo> parameters;
            bool isStatic = method.IsStatic;

            if (!isStatic)
            {
                obj        = node.Object;
                arguments  = node.Arguments;
                parameters = method.GetParameters();
            }
            else if (method.DeclaringType == typeof(Enumerable))
            {
                obj        = node.Arguments[0];
                arguments  = node.Arguments.Skip(1);
                parameters = method.GetParameters().Skip(1);
            }
            else
            {
                obj        = null;
                arguments  = node.Arguments;
                parameters = method.GetParameters();
            }
            Type type = obj == null ? null : obj.Type;

            if (obj != null)
            {
                Type actualType;
                result |= ExpressionEmittersCollection.Emit(obj, context, returnDefaultValueLabel, isStatic ? ResultType.Value : ResultType.ByRefValueTypesOnly, extend, out actualType); // stack: [obj]
                if (actualType == typeof(void))
                {
                    throw new InvalidOperationException("Unable to call method on void");
                }
                if (actualType.IsValueType && !isStatic)
                {
                    using (var temp = context.DeclareLocal(actualType))
                    {
                        il.Stloc(temp);
                        il.Ldloca(temp);
                    }
                    actualType = actualType.MakeByRefType();
                }
                if (context.Options.HasFlag(CompilerOptions.CheckNullReferences) && !actualType.IsValueType)
                {
                    if (method.DeclaringType != typeof(Enumerable))
                    {
                        result |= context.EmitNullChecking(type, returnDefaultValueLabel);
                    }
                    else
                    {
                        var arrIsNotNullLabel = il.DefineLabel("arrIsNotNull");
                        il.Dup();
                        il.Brtrue(arrIsNotNullLabel);
                        il.Pop();
                        il.Ldc_I4(0);
                        il.Newarr(GetElementType(type));
                        context.MarkLabelAndSurroundWithSP(arrIsNotNullLabel);
                    }
                }
            }

            var parametersArray = parameters.ToArray();
            var argumentsArray  = arguments.ToArray();

            for (int i = 0; i < argumentsArray.Length; i++)
            {
                var argument  = argumentsArray[i];
                var parameter = parametersArray[i];
                if (parameter.ParameterType.IsByRef)
                {
                    Type argumentType;
                    var  options = context.Options;
                    context.Options = CompilerOptions.None;
                    ExpressionEmittersCollection.Emit(argument, context, null, ResultType.ByRefAll, false, out argumentType);
                    context.Options = options;
                    if (!argumentType.IsByRef)
                    {
                        throw new InvalidOperationException("Expected type by reference");
                    }
                }
                else
                {
                    Type argumentType;
                    context.EmitLoadArgument(argument, true, out argumentType);
                }
            }
            il.Call(method, type);
            resultType = node.Type;
            return(result);
        }
        protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            Expression left = node.Left;
            Expression right = node.Right;
            Type       leftType, rightType;

            context.EmitLoadArgument(left, false, out leftType);
            context.EmitLoadArgument(right, false, out rightType);
            GroboIL il = context.Il;

            if (!leftType.IsNullable() && !rightType.IsNullable())
            {
                if (node.Method != null)
                {
                    il.Call(node.Method);
                }
                else
                {
                    if (leftType.IsStruct() || rightType.IsStruct())
                    {
                        throw new InvalidOperationException("Cannot compare structs");
                    }
                    il.Ceq();
                    if (node.NodeType == ExpressionType.NotEqual)
                    {
                        il.Ldc_I4(1);
                        il.Xor();
                    }
                }
            }
            else
            {
                var type = leftType;
                if (type != rightType)
                {
                    throw new InvalidOperationException("Cannot compare objects of different types '" + leftType + "' and '" + rightType + "'");
                }
                using (var localLeft = context.DeclareLocal(type))
                    using (var localRight = context.DeclareLocal(type))
                    {
                        il.Stloc(localRight);
                        il.Stloc(localLeft);
                        if (node.Method != null)
                        {
                            il.Ldloca(localLeft);             // stack: [&left]
                            context.EmitHasValueAccess(type); // stack: [left.HasValue]
                            il.Dup();                         // stack: [left.HasValue, left.HasValue]
                            il.Ldloca(localRight);            // stack: [left.HasValue, left.HasValue, &right]
                            context.EmitHasValueAccess(type); // stack: [left.HasValue, left.HasValue, right.HasValue]
                            var notEqualLabel = il.DefineLabel("notEqual");
                            il.Bne_Un(notEqualLabel);         // stack: [left.HasValue]
                            var equalLabel = il.DefineLabel("equal");
                            il.Brfalse(equalLabel);
                            il.Ldloca(localLeft);
                            context.EmitValueAccess(type);
                            il.Ldloca(localRight);
                            context.EmitValueAccess(type);
                            il.Call(node.Method);
                            var doneLabel = il.DefineLabel("done");
                            il.Br(doneLabel);
                            context.MarkLabelAndSurroundWithSP(notEqualLabel);
                            il.Pop();
                            il.Ldc_I4(node.NodeType == ExpressionType.Equal ? 0 : 1);
                            il.Br(doneLabel);
                            context.MarkLabelAndSurroundWithSP(equalLabel);
                            il.Ldc_I4(node.NodeType == ExpressionType.Equal ? 1 : 0);
                            context.MarkLabelAndSurroundWithSP(doneLabel);
                        }
                        else
                        {
                            il.Ldloca(localLeft);
                            context.EmitValueAccess(type);
                            il.Ldloca(localRight);
                            context.EmitValueAccess(type);
                            var notEqualLabel = il.DefineLabel("notEqual");
                            il.Bne_Un(notEqualLabel);
                            il.Ldloca(localLeft);
                            context.EmitHasValueAccess(type);
                            il.Ldloca(localRight);
                            context.EmitHasValueAccess(type);
                            il.Ceq();
                            var doneLabel = il.DefineLabel("done");
                            il.Br(doneLabel);
                            context.MarkLabelAndSurroundWithSP(notEqualLabel);
                            il.Ldc_I4(0);
                            context.MarkLabelAndSurroundWithSP(doneLabel);
                            if (node.NodeType == ExpressionType.NotEqual)
                            {
                                il.Ldc_I4(1);
                                il.Xor();
                            }
                        }
                    }
            }
            resultType = typeof(bool);
            return(false);
        }
        protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var il = context.Il;

            GroboIL.Label operandIsNullLabel     = context.CanReturn ? il.DefineLabel("operandIsNull") : null;
            var           operandIsNullLabelUsed = ExpressionEmittersCollection.Emit(node.Operand, context, operandIsNullLabel, ResultType.Value, extend, out resultType); // stack: [obj]

            if (operandIsNullLabelUsed)
            {
                context.EmitReturnDefaultValue(resultType, operandIsNullLabel, il.DefineLabel("operandIsNotNull"));
            }
            if (resultType != node.Type && !(context.Options.HasFlag(CompilerOptions.UseTernaryLogic) && resultType == typeof(bool?) && node.Type == typeof(bool)))
            {
                if (node.Method != null)
                {
                    if (!resultType.IsNullable())
                    {
                        il.Call(node.Method);
                        if (node.Type.IsNullable())
                        {
                            il.Newobj(node.Type.GetConstructor(new[] { node.Method.ReturnType }));
                        }
                    }
                    else
                    {
                        using (var temp = context.DeclareLocal(resultType))
                        {
                            il.Stloc(temp);
                            il.Ldloca(temp);
                            il.Dup();
                            context.EmitHasValueAccess(resultType);
                            var valueIsNullLabel = operandIsNullLabelUsed ? operandIsNullLabel : il.DefineLabel("valueIsNull");
                            il.Brfalse(valueIsNullLabel);
                            context.EmitValueAccess(resultType);
                            il.Call(node.Method);
                            il.Newobj(node.Type.GetConstructor(new[] { node.Method.ReturnType }));
                            if (!operandIsNullLabelUsed)
                            {
                                context.EmitReturnDefaultValue(node.Type, valueIsNullLabel, il.DefineLabel("valueIsNotNull"));
                            }
                        }
                    }
                }
                else
                {
                    switch (node.NodeType)
                    {
                    case ExpressionType.Convert:
                        context.EmitConvert(resultType, node.Type); // stack: [(type)obj]
                        break;

                    case ExpressionType.ConvertChecked:
                        context.EmitConvert(resultType, node.Type, true); // stack: [(type)obj]
                        break;

                    default:
                        throw new InvalidOperationException("Node type '" + node.NodeType + "' is not valid at this point");
                    }
                }
                resultType = node.Type;
            }
            return(false);
        }
        protected override bool EmitInternal(BlockExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var variables = node.Variables.Where(variable => !context.VariablesToLocals.ContainsKey(variable)).ToArray();

            foreach (var variable in variables)
            {
                var local = string.IsNullOrEmpty(variable.Name)
                                ? context.Il.DeclareLocal(variable.Type)
                                : context.Il.DeclareLocal(variable.Type, variable.Name, appendUniquePrefix: false);
                if (context.DebugInfoGenerator != null)
                {
                    local.SetLocalSymInfo(local.Name);
                }
                context.VariablesToLocals.Add(variable, local);
                context.Variables.Push(variable);
            }
            resultType = typeof(void);
            for (var index = 0; index < node.Expressions.Count; index++)
            {
                var expression       = node.Expressions[index];
                var il               = context.Il;
                var valueIsNullLabel = il.DefineLabel("valueIsNull");
                var labelUsed        = ExpressionEmittersCollection.Emit(expression, context, valueIsNullLabel, index < node.Expressions.Count - 1 ? ResultType.Void : whatReturn, extend, out resultType);
                if (resultType != typeof(void) && index < node.Expressions.Count - 1)
                {
                    // eat results of all expressions except the last one
                    if (resultType.IsStruct())
                    {
                        using (var temp = context.DeclareLocal(resultType))
                            context.Il.Stloc(temp);
                    }
                    else
                    {
                        context.Il.Pop();
                    }
                }
                if (labelUsed)
                {
                    var doneLabel = il.DefineLabel("done");
                    il.Br(doneLabel);
                    context.MarkLabelAndSurroundWithSP(valueIsNullLabel);
                    il.Pop();
                    if (resultType != typeof(void) && index == node.Expressions.Count - 1)
                    {
                        // return default value for the last expression in the block
                        context.EmitLoadDefaultValue(resultType);
                    }
                    context.MarkLabelAndSurroundWithSP(doneLabel);
                }
            }
            if (node.Type == typeof(bool) && resultType == typeof(bool?))
            {
                resultType = typeof(bool);
                context.ConvertFromNullableBoolToBool();
            }
            else if (node.Type == typeof(void) && resultType != typeof(void))
            {
                // eat result of the last expression if the result of block is void
                if (resultType.IsStruct())
                {
                    using (var temp = context.DeclareLocal(resultType))
                        context.Il.Stloc(temp);
                }
                else
                {
                    context.Il.Pop();
                }
                resultType = typeof(void);
            }
            foreach (var variable in variables)
            {
                context.VariablesToLocals.Remove(variable);
                context.Variables.Pop();
            }
            return(false);
        }
        protected override bool EmitInternal(UnaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            Type    operandType;
            var     result = ExpressionEmittersCollection.Emit(node.Operand, context, returnDefaultValueLabel, ResultType.Value, extend, out operandType);
            GroboIL il     = context.Il;

            if (node.NodeType == ExpressionType.IsTrue || node.NodeType == ExpressionType.IsFalse)
            {
                if (!operandType.IsNullable())
                {
                    if (node.Method != null)
                    {
                        il.Call(node.Method);
                    }
                    else if (operandType == typeof(bool))
                    {
                        if (node.NodeType == ExpressionType.IsFalse)
                        {
                            il.Ldc_I4(1);
                            il.Xor();
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Cannot perform operation '" + node.NodeType + "' to a type '" + operandType + "'");
                    }
                }
                else
                {
                    using (var temp = context.DeclareLocal(operandType))
                    {
                        il.Stloc(temp);
                        il.Ldloca(temp);
                        context.EmitHasValueAccess(operandType);
                        var returnFalseLabel = il.DefineLabel("returnFalse");
                        il.Brfalse(returnFalseLabel);
                        il.Ldloca(temp);
                        context.EmitValueAccess(operandType);
                        if (node.Method != null)
                        {
                            il.Call(node.Method);
                        }
                        else if (operandType == typeof(bool?))
                        {
                            if (node.NodeType == ExpressionType.IsFalse)
                            {
                                il.Ldc_I4(1);
                                il.Xor();
                            }
                        }
                        else
                        {
                            throw new InvalidOperationException("Cannot perform operation '" + node.NodeType + "' to a type '" + operandType + "'");
                        }
                        var doneLabel = il.DefineLabel("done");
                        il.Br(doneLabel);
                        context.MarkLabelAndSurroundWithSP(returnFalseLabel);
                        il.Ldc_I4(0);
                        context.MarkLabelAndSurroundWithSP(doneLabel);
                    }
                }
            }
            else
            {
                if (!operandType.IsNullable())
                {
                    if (node.Method != null)
                    {
                        il.Call(node.Method);
                    }
                    else
                    {
                        if (operandType.IsStruct())
                        {
                            throw new InvalidOperationException("Cannot perform operation '" + node.NodeType + "' to a struct '" + operandType + "'");
                        }
                        switch (node.NodeType)
                        {
                        case ExpressionType.UnaryPlus:
                            break;

                        case ExpressionType.Negate:
                            il.Neg();
                            break;

                        case ExpressionType.NegateChecked:
                            using (var temp = context.DeclareLocal(operandType))
                            {
                                il.Stloc(temp);
                                il.Ldc_I4(0);
                                context.EmitConvert(typeof(int), operandType);
                                il.Ldloc(temp);
                                il.Sub_Ovf(operandType.Unsigned());
                            }
                            break;

                        case ExpressionType.Increment:
                            il.Ldc_I4(1);
                            context.EmitConvert(typeof(int), operandType);
                            il.Add();
                            break;

                        case ExpressionType.Decrement:
                            il.Ldc_I4(1);
                            context.EmitConvert(typeof(int), operandType);
                            il.Sub();
                            break;

                        case ExpressionType.OnesComplement:
                            il.Not();
                            break;

                        default:
                            throw new InvalidOperationException("Node type '" + node.NodeType + "' invalid at this point");
                        }
                    }
                }
                else
                {
                    using (var temp = context.DeclareLocal(operandType))
                    {
                        il.Stloc(temp);
                        il.Ldloca(temp);
                        context.EmitHasValueAccess(operandType);
                        var returnNullLabel = il.DefineLabel("returnLabel");
                        il.Brfalse(returnNullLabel);
                        Type argumentType = operandType.GetGenericArguments()[0];
                        if (node.Method != null)
                        {
                            il.Ldloca(temp);
                            context.EmitValueAccess(operandType);
                            il.Call(node.Method);
                        }
                        else
                        {
                            switch (node.NodeType)
                            {
                            case ExpressionType.UnaryPlus:
                                il.Ldloca(temp);
                                context.EmitValueAccess(operandType);
                                break;

                            case ExpressionType.Negate:
                                il.Ldloca(temp);
                                context.EmitValueAccess(operandType);
                                il.Neg();
                                break;

                            case ExpressionType.NegateChecked:
                                il.Ldc_I4(0);
                                context.EmitConvert(typeof(int), argumentType);
                                il.Ldloca(temp);
                                context.EmitValueAccess(operandType);
                                il.Sub_Ovf(argumentType.Unsigned());
                                break;

                            case ExpressionType.Increment:
                                il.Ldloca(temp);
                                context.EmitValueAccess(operandType);
                                il.Ldc_I4(1);
                                context.EmitConvert(typeof(int), argumentType);
                                il.Add();
                                break;

                            case ExpressionType.Decrement:
                                il.Ldloca(temp);
                                context.EmitValueAccess(operandType);
                                il.Ldc_I4(1);
                                context.EmitConvert(typeof(int), argumentType);
                                il.Sub();
                                break;

                            case ExpressionType.OnesComplement:
                                il.Ldloca(temp);
                                context.EmitValueAccess(operandType);
                                il.Not();
                                break;

                            default:
                                throw new InvalidOperationException("Node type '" + node.NodeType + "' invalid at this point");
                            }
                        }
                        il.Newobj(operandType.GetConstructor(new[] { argumentType }));
                        var doneLabel = il.DefineLabel("done");
                        il.Br(doneLabel);
                        context.MarkLabelAndSurroundWithSP(returnNullLabel);
                        context.EmitLoadDefaultValue(operandType);
                        context.MarkLabelAndSurroundWithSP(doneLabel);
                    }
                }
            }
            resultType = node.Type;
            return(result);
        }
Exemple #22
0
        protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var          il     = context.Il;
            var          left   = node.Left;
            var          right  = node.Right;
            bool         result = false;
            Type         assigneeType;
            AssigneeKind assigneeKind;
            bool         checkNullReferences = context.Options.HasFlag(CompilerOptions.CheckNullReferences);

            extend |= context.Options.HasFlag(CompilerOptions.ExtendOnAssign);

            GroboIL.Label assigneeIsNullLabel     = null;
            bool          assigneeIsNullLabelUsed = false;

            switch (left.NodeType)
            {
            case ExpressionType.Parameter:
                assigneeType        = null;
                assigneeKind        = AssigneeKind.Parameter;
                checkNullReferences = false;
                break;

            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression)left;
                if (memberExpression.Expression == null)
                {
                    assigneeType        = null;
                    assigneeKind        = memberExpression.Member is FieldInfo ? AssigneeKind.StaticField : AssigneeKind.StaticProperty;
                    checkNullReferences = false;
                }
                else
                {
                    bool closureAssign = memberExpression.Expression == context.ParsedLambda.ClosureParameter || memberExpression.Expression.Type.IsStaticClosure();
                    checkNullReferences &= !closureAssign;
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ExpressionEmittersCollection.Emit(memberExpression.Expression, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    else
                    {
                        assigneeIsNullLabel     = !closureAssign && context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(memberExpression.Expression, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    assigneeKind = memberExpression.Member is FieldInfo ? AssigneeKind.InstanceField : AssigneeKind.InstanceProperty;
                }
                break;

            case ExpressionType.Index:
                var indexExpression = (IndexExpression)left;
                if (indexExpression.Object == null)
                {
                    throw new InvalidOperationException("Indexing of null object is invalid");
                }
                if ((indexExpression.Object.Type.IsArray && indexExpression.Object.Type.GetArrayRank() == 1) || indexExpression.Object.Type.IsList())
                {
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ArrayIndexExpressionEmitter.Emit(indexExpression.Object, indexExpression.Arguments.Single(), context, returnDefaultValueLabel, ResultType.ByRefAll, extend, out assigneeType);
                        checkNullReferences = false;
                    }
                    else
                    {
                        assigneeIsNullLabel     = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ArrayIndexExpressionEmitter.Emit(indexExpression.Object, indexExpression.Arguments.Single(), context, assigneeIsNullLabel, ResultType.ByRefAll, extend, out assigneeType);
                    }
                    assigneeKind = AssigneeKind.SimpleArray;
                }
                else
                {
                    if (node.NodeType != ExpressionType.Assign && context.CanReturn)
                    {
                        result |= ExpressionEmittersCollection.Emit(indexExpression.Object, context, returnDefaultValueLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    else
                    {
                        assigneeIsNullLabel     = context.CanReturn ? il.DefineLabel("assigneeIsNull") : null;
                        assigneeIsNullLabelUsed = ExpressionEmittersCollection.Emit(indexExpression.Object, context, assigneeIsNullLabel, ResultType.ByRefValueTypesOnly, extend, out assigneeType);
                    }
                    assigneeKind = indexExpression.Indexer != null ? AssigneeKind.IndexedProperty : AssigneeKind.MultiDimensionalArray;
                }
                break;

            default:
                throw new InvalidOperationException("Unable to assign to an expression of type '" + left.NodeType + "'");
            }
            if (assigneeType != null && assigneeType.IsValueType)
            {
                using (var temp = context.DeclareLocal(assigneeType))
                {
                    il.Stloc(temp);
                    il.Ldloca(temp);
                }
                assigneeType = assigneeType.MakeByRefType();
            }

            if (node.NodeType == ExpressionType.Assign)
            {
                if (!checkNullReferences)
                {
                    if (whatReturn == ResultType.Void)
                    {
                        EmitAssign(assigneeKind, left, context, null, right);
                    }
                    else
                    {
                        if (assigneeKind == AssigneeKind.Parameter)
                        {
                            EmitAssign(assigneeKind, left, context, null, right);
                            EmitAccess(assigneeKind, left, context);
                        }
                        else
                        {
                            context.EmitLoadArguments(right);
                            using (var value = context.DeclareLocal(right.Type))
                            {
                                il.Stloc(value);
                                EmitAssign(assigneeKind, left, context, null, value);
                                il.Ldloc(value);
                            }
                        }
                    }
                }
                else
                {
                    if (whatReturn == ResultType.Void)
                    {
                        il.Dup();
                        var skipAssigneeLabel = il.DefineLabel("skipAssignee");
                        il.Brfalse(skipAssigneeLabel);
                        EmitAssign(assigneeKind, left, context, null, right);
                        var returnLabel = il.DefineLabel("return");
                        il.Br(returnLabel);
                        context.MarkLabelAndSurroundWithSP(skipAssigneeLabel);
                        il.Pop();
                        context.MarkLabelAndSurroundWithSP(returnLabel);
                    }
                    else
                    {
                        // load value
                        var  rightIsNullLabel = context.CanReturn ? il.DefineLabel("rightIsNull") : null;
                        Type valueType;
                        bool labelUsed = ExpressionEmittersCollection.Emit(right, context, rightIsNullLabel, out valueType); // stack: [address, value]
                        if (right.Type == typeof(bool) && valueType == typeof(bool?))
                        {
                            context.ConvertFromNullableBoolToBool();
                        }
                        if (labelUsed && context.CanReturn)
                        {
                            context.EmitReturnDefaultValue(right.Type, rightIsNullLabel, il.DefineLabel("rightIsNotNull"));
                        }
                        using (var value = context.DeclareLocal(right.Type))
                        {
                            il.Stloc(value);
                            il.Dup();
                            var skipAssigneeLabel = il.DefineLabel("skipAssignee");
                            il.Brfalse(skipAssigneeLabel);
                            EmitAssign(assigneeKind, left, context, null, value);
                            var returnValueLabel = il.DefineLabel("returnValue");
                            il.Br(returnValueLabel);
                            context.MarkLabelAndSurroundWithSP(skipAssigneeLabel);
                            il.Pop();
                            context.MarkLabelAndSurroundWithSP(returnValueLabel);
                            il.Ldloc(value);
                        }
                    }
                }
            }
            else
            {
                if (checkNullReferences)
                {
                    il.Dup();
                    il.Brfalse(returnDefaultValueLabel);
                    result = true;
                }
                if (assigneeType != null)
                {
                    il.Dup();
                }
                object[] arguments = EmitAccess(assigneeKind, left, context);
                context.EmitLoadArguments(right);
                context.EmitArithmeticOperation(GetOp(node.NodeType), left.Type, left.Type, right.Type, node.Method);
                if (whatReturn == ResultType.Void)
                {
                    EmitAssign(assigneeKind, left, context, arguments);
                }
                else
                {
                    if (assigneeKind == AssigneeKind.Parameter)
                    {
                        EmitAssign(assigneeKind, left, context, arguments);
                        EmitAccess(assigneeKind, left, context);
                    }
                    else
                    {
                        using (var temp = context.DeclareLocal(left.Type))
                        {
                            il.Stloc(temp);
                            EmitAssign(assigneeKind, left, context, arguments, temp);
                            il.Ldloc(temp);
                        }
                    }
                }
            }
            resultType = whatReturn == ResultType.Void ? typeof(void) : left.Type;
            if (assigneeIsNullLabelUsed)
            {
                context.EmitReturnDefaultValue(resultType, assigneeIsNullLabel, il.DefineLabel("assigneeIsNotNull"));
            }
            return(result);
        }
        protected override bool EmitInternal(TryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            var il = context.Il;

            il.BeginExceptionBlock();
            returnDefaultValueLabel = context.CanReturn ? il.DefineLabel("returnDefaultValue") : null;
            bool returnDefaultValueLabelUsed = ExpressionEmittersCollection.Emit(node.Body, context, returnDefaultValueLabel, whatReturn, extend, out resultType);

            EmittingContext.LocalHolder retValue = null;
            var doneLabel = il.DefineLabel("done");

            if (resultType == typeof(void))
            {
                if (returnDefaultValueLabelUsed)
                {
                    context.MarkLabelAndSurroundWithSP(returnDefaultValueLabel);
                }
                il.Leave(doneLabel);
            }
            else
            {
                retValue = context.DeclareLocal(resultType);
                il.Stloc(retValue);
                il.Leave(doneLabel);
                if (returnDefaultValueLabelUsed)
                {
                    context.MarkLabelAndSurroundWithSP(returnDefaultValueLabel);
                    if (resultType.IsValueType)
                    {
                        il.Ldloca(retValue);
                        il.Initobj(resultType);
                    }
                    else
                    {
                        il.Ldnull();
                        il.Stloc(retValue);
                    }
                    il.Leave(doneLabel);
                }
            }
            foreach (var catchBlock in node.Handlers)
            {
                bool disposeVariable = false;
                var  variable        = catchBlock.Variable;
                if (catchBlock.Filter == null)
                {
                    il.BeginCatchBlock(catchBlock.Test);
                    if (variable == null)
                    {
                        il.Pop();
                    }
                    else
                    {
                        // todo вызвать ф-цию из AssignExpressionEmitter
                        var index = Array.IndexOf(context.Parameters, variable);
                        if (index >= 0)
                        {
                            il.Starg(index);
                        }
                        else
                        {
                            GroboIL.Local local;
                            if (!context.VariablesToLocals.TryGetValue(variable, out local))
                            {
                                local = context.DeclareLocal(variable.Type);
                                context.VariablesToLocals.Add(variable, local);
                                context.Variables.Push(variable);
                                disposeVariable = true;
                            }
                            il.Stloc(local);
                        }
                    }
                }
                else
                {
                    il.BeginExceptFilterBlock();
                    il.Isinst(catchBlock.Test);
                    il.Dup();
                    var rightTypeLabel = il.DefineLabel("rightType");
                    il.Brtrue(rightTypeLabel);
                    il.Pop();
                    il.Ldc_I4(0);
                    var endFilterLabel = il.DefineLabel("endFilter");
                    il.Br(endFilterLabel);
                    context.MarkLabelAndSurroundWithSP(rightTypeLabel);
                    if (variable == null)
                    {
                        il.Pop();
                    }
                    else
                    {
                        // todo вызвать ф-цию из AssignExpressionEmitter
                        var index = Array.IndexOf(context.Parameters, variable);
                        if (index >= 0)
                        {
                            il.Starg(index);
                        }
                        else
                        {
                            GroboIL.Local local;
                            if (!context.VariablesToLocals.TryGetValue(variable, out local))
                            {
                                local = string.IsNullOrEmpty(variable.Name)
                                                ? context.Il.DeclareLocal(variable.Type)
                                                : context.Il.DeclareLocal(variable.Type, variable.Name, appendUniquePrefix: false);
                                if (context.DebugInfoGenerator != null)
                                {
                                    local.SetLocalSymInfo(local.Name);
                                }
                                context.VariablesToLocals.Add(variable, local);
                                context.Variables.Push(variable);
                                disposeVariable = true;
                            }
                            il.Stloc(local);
                        }
                    }
                    GroboIL.Label returnFalseLabel = context.CanReturn ? il.DefineLabel("returnFalse") : null;
                    Type          filterResultType;
                    bool          returnFalseLabelUsed = ExpressionEmittersCollection.Emit(catchBlock.Filter, context, returnFalseLabel, out filterResultType);
                    if (returnFalseLabelUsed)
                    {
                        il.Br(endFilterLabel);
                        context.MarkLabelAndSurroundWithSP(returnFalseLabel);
                        il.Pop();
                        il.Ldc_I4(0);
                    }
                    context.MarkLabelAndSurroundWithSP(endFilterLabel);
                    il.BeginCatchBlock(null);
                    il.Pop();
                }

                context.EmitLoadArguments(catchBlock.Body);
                if (catchBlock.Body.Type != typeof(void))
                {
                    il.Stloc(retValue);
                }

                if (disposeVariable)
                {
                    context.VariablesToLocals.Remove(variable);
                    context.Variables.Pop();
                }
            }

            if (node.Fault != null)
            {
                il.BeginFaultBlock();
                EmitExpressionAsVoid(node.Fault, context);
            }

            if (node.Finally != null)
            {
                il.BeginFinallyBlock();
                EmitExpressionAsVoid(node.Finally, context);
            }

            il.EndExceptionBlock();

            context.MarkLabelAndSurroundWithSP(doneLabel);
            if (retValue != null)
            {
                il.Ldloc(retValue);
                retValue.Dispose();
            }
            return(false);
        }
        protected override bool EmitInternal(BinaryExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            GroboIL il = context.Il;

            if (node.Method != null)
            {
                throw new NotSupportedException("Custom operator '" + node.NodeType + "' is not supported");
            }
            Expression left  = node.Left;
            Expression right = node.Right;
            Type       leftType;

            context.EmitLoadArgument(left, false, out leftType); // stack: [left]
            if (leftType == typeof(bool))
            {
                switch (node.NodeType)
                {
                case ExpressionType.AndAlso:
                {
                    var returnFalseLabel = il.DefineLabel("returnFalse");
                    il.Brfalse(returnFalseLabel);                          // if(left == false) return false; stack: []
                    Type rightType;
                    context.EmitLoadArgument(right, false, out rightType); // stack: [right]
                    var doneLabel = il.DefineLabel("done");
                    il.Br(doneLabel);                                      // goto done; stack: [right]
                    context.MarkLabelAndSurroundWithSP(returnFalseLabel);
                    il.Ldc_I4(0);                                          // stack: [false]
                    if (rightType == typeof(bool?))
                    {
                        il.Newobj(nullableBoolConstructor);     // stack: [new bool?(false)]
                    }
                    context.MarkLabelAndSurroundWithSP(doneLabel);
                    resultType = rightType;
                    break;
                }

                case ExpressionType.OrElse:
                {
                    var returnTrueLabel = il.DefineLabel("returnTrue");
                    il.Brtrue(returnTrueLabel);                            // if(left == true) return true; stack: []
                    Type rightType;
                    context.EmitLoadArgument(right, false, out rightType); // stack: [right]
                    var doneLabel = il.DefineLabel("done");
                    il.Br(doneLabel);                                      // goto done; stack: [right]
                    context.MarkLabelAndSurroundWithSP(returnTrueLabel);
                    il.Ldc_I4(1);                                          // stack: [true]
                    if (rightType == typeof(bool?))
                    {
                        il.Newobj(nullableBoolConstructor);     // stack: [new bool?(true)]
                    }
                    context.MarkLabelAndSurroundWithSP(doneLabel);
                    resultType = rightType;
                    break;
                }

                default:
                    throw new NotSupportedException("Node type '" + node.NodeType + "' is not supported");
                }
            }
            else // bool?
            {
                switch (node.NodeType)
                {
                case ExpressionType.AndAlso:
                {
                    /*
                     * +-------+-------+-------+-------+
                     * |  &&   | null  | false | true  |
                     * +-------+-------+-------+-------+
                     * | null  | null  | false | null  |
                     * +-------+-------+-------+-------+
                     * | false | false | false | false |
                     * +-------+-------+-------+-------+
                     * | true  | null  | false | true  |
                     * +-------+-------+-------+-------+
                     */
                    using (var localLeft = context.DeclareLocal(typeof(bool?)))
                    {
                        il.Stloc(localLeft);                                   // localLeft = left; stack: []
                        il.Ldloca(localLeft);                                  // stack: [&localLeft]
                        context.EmitHasValueAccess(typeof(bool?));             // stack: [localLeft.HasValue]
                        il.Ldc_I4(1);                                          // stack: [localLeft.HasValue, true]
                        il.Xor();                                              // stack: [!localLeft.HasValue]
                        il.Ldloca(localLeft);                                  // stack: [!localLeft.HasValue, &localLeft]
                        context.EmitValueAccess(typeof(bool?));                // stack: [!localLeft.HasValue, localLeft.Value]
                        il.Or();                                               // stack: [!localLeft.HasValue || localLeft.Value]
                        var returnFalseLabel = il.DefineLabel("returnFalse");
                        il.Brfalse(returnFalseLabel);                          // if(localLeft == false) goto returnFalse; stack: []
                        Type rightType;
                        context.EmitLoadArgument(right, false, out rightType); // stack: [right]
                        using (var localRight = context.DeclareLocal(rightType))
                        {
                            il.Stloc(localRight);     // localRight = right; stack: []
                            if (rightType == typeof(bool))
                            {
                                il.Ldloc(localRight);     // stack: [localRight]
                            }
                            else
                            {
                                il.Ldloca(localRight);                     // stack: [&localRight]
                                context.EmitHasValueAccess(typeof(bool?)); // stack: [localRight.HasValue]
                                il.Ldc_I4(1);                              // stack: [localRight.HasValue, true]
                                il.Xor();                                  // stack: [!localRight.HasValue]
                                il.Ldloca(localRight);                     // stack: [!localRight.HasValue, &localRight]
                                context.EmitValueAccess(typeof(bool?));    // stack: [!localRight.HasValue, localRight.Value]
                                il.Or();                                   // stack: [!localRight.HasValue || localRight.Value]
                            }
                            il.Brfalse(returnFalseLabel);                  // if(localRight == false) goto returnFalse;
                            if (rightType == typeof(bool))
                            {
                                il.Ldloc(localRight);     // stack: [localRight]
                            }
                            else
                            {
                                il.Ldloca(localRight);                     // stack: [&localRight]
                                context.EmitHasValueAccess(typeof(bool?)); // stack: [localRight.HasValue]
                                il.Ldloca(localRight);                     // stack: [localRight.HasValue, &localRight]
                                context.EmitValueAccess(typeof(bool?));    // stack: [localRight.HasValue, localRight.Value]
                                il.And();                                  // stack: [localRight.HasValue && localRight.Value]
                            }
                            var returnLeftLabel = il.DefineLabel("returnLeft");
                            il.Brtrue(returnLeftLabel); // if(localRight == true) goto returnLeft;
                            il.Ldloca(localLeft);       // stack: [&localLeft]
                            il.Initobj(typeof(bool?));  // localLeft = default(bool?); stack: []
                            context.MarkLabelAndSurroundWithSP(returnLeftLabel);
                            il.Ldloc(localLeft);        // stack: [localLeft]
                            var doneLabel = il.DefineLabel("done");
                            il.Br(doneLabel);
                            context.MarkLabelAndSurroundWithSP(returnFalseLabel);
                            il.Ldc_I4(0);                       // stack: [false]
                            il.Newobj(nullableBoolConstructor); // new bool?(false)
                            context.MarkLabelAndSurroundWithSP(doneLabel);
                            resultType = typeof(bool?);
                        }
                    }
                    break;
                }

                case ExpressionType.OrElse:
                {
                    /*
                     * +-------+-------+-------+-------+
                     * |  ||   | null  | false | true  |
                     * +-------+-------+-------+-------+
                     * | null  | null  | null  | true  |
                     * +-------+-------+-------+-------+
                     * | false | null  | false | true  |
                     * +-------+-------+-------+-------+
                     * | true  | true  | true  | true  |
                     * +-------+-------+-------+-------+
                     */
                    using (var localLeft = context.DeclareLocal(typeof(bool?)))
                    {
                        il.Stloc(localLeft);                                   // localLeft = left; stack: []
                        il.Ldloca(localLeft);                                  // stack: [&localLeft]
                        context.EmitHasValueAccess(typeof(bool?));             // stack: [localLeft.HasValue]
                        il.Ldloca(localLeft);                                  // stack: [localLeft.HasValue, &localLeft]
                        context.EmitValueAccess(typeof(bool?));                // stack: [localLeft.HasValue, localLeft.Value]
                        il.And();                                              // stack: [localLeft.HasValue && localLeft.Value]
                        var returnTrueLabel = il.DefineLabel("returnTrue");
                        il.Brtrue(returnTrueLabel);                            // if(localLeft == true) goto returnTrue; stack: []
                        Type rightType;
                        context.EmitLoadArgument(right, false, out rightType); // stack: [right]
                        using (var localRight = context.DeclareLocal(rightType))
                        {
                            il.Stloc(localRight);     // localRight = right; stack: []
                            if (rightType == typeof(bool))
                            {
                                il.Ldloc(localRight);     // stack: [localRight]
                            }
                            else
                            {
                                il.Ldloca(localRight);                     // stack: [&localRight]
                                context.EmitHasValueAccess(typeof(bool?)); // stack: [localRight.HasValue]
                                il.Ldloca(localRight);                     // stack: [localRight.HasValue, &localRight]
                                context.EmitValueAccess(typeof(bool?));    // stack: [localRight.HasValue, localRight.Value]
                                il.And();                                  // stack: [localRight.HasValue && localRight.Value]
                            }
                            il.Brtrue(returnTrueLabel);                    // if(localRight == true) goto returnTrue; stack: []
                            if (rightType == typeof(bool))
                            {
                                il.Ldloc(localRight);     // stack: [localRight]
                            }
                            else
                            {
                                il.Ldloca(localRight);                     // stack: [&localRight]
                                context.EmitHasValueAccess(typeof(bool?)); // stack: [localRight.HasValue]
                                il.Ldc_I4(1);                              // stack: [localRight.HasValue, true]
                                il.Xor();                                  // stack: [!localRight.HasValue]
                                il.Ldloca(localRight);                     // stack: [!localRight.HasValue, &localRight]
                                context.EmitValueAccess(typeof(bool?));    // stack: [!localRight.HasValue, localRight.Value]
                                il.Or();                                   // stack: [!localRight.HasValue || localRight.Value]
                            }
                            var returnLeftLabel = il.DefineLabel("returnLeft");
                            il.Brfalse(returnLeftLabel); // if(localRight == false) goto returnLeft;
                            il.Ldloca(localLeft);        // stack: [&localLeft]
                            il.Initobj(typeof(bool?));   // localLeft = default(bool?); stack: []
                            context.MarkLabelAndSurroundWithSP(returnLeftLabel);
                            il.Ldloc(localLeft);         // stack: [localLeft]
                            var doneLabel = il.DefineLabel("done");
                            il.Br(doneLabel);
                            context.MarkLabelAndSurroundWithSP(returnTrueLabel);
                            il.Ldc_I4(1);                       // stack: [true]
                            il.Newobj(nullableBoolConstructor); // new bool?(true)
                            context.MarkLabelAndSurroundWithSP(doneLabel);
                            resultType = typeof(bool?);
                        }
                    }
                    break;
                }

                default:
                    throw new NotSupportedException("Node type '" + node.NodeType + "' is not supported");
                }
            }
            return(false);
        }