protected override bool EmitInternal(DefaultExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
 {
     resultType = node.Type;
     if (node.Type != typeof(void))
     {
         context.EmitLoadDefaultValue(node.Type);
     }
     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);
        }
        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(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);
        }
        protected override bool EmitInternal(ConstantExpression node, EmittingContext context, GroboIL.Label returnDefaultValueLabel, ResultType whatReturn, bool extend, out Type resultType)
        {
            if (node.Value == null)
            {
                context.EmitLoadDefaultValue(node.Type);
            }
            else
            {
                var typeCode = Type.GetTypeCode(node.Type.IsNullable() ? node.Type.GetGenericArguments()[0] : node.Type);
                switch (typeCode)
                {
                case TypeCode.Boolean:
                    context.Il.Ldc_I4(((bool)node.Value) ? 1 : 0);
                    break;

                case TypeCode.Byte:
                    context.Il.Ldc_I4((byte)node.Value);
                    break;

                case TypeCode.Char:
                    context.Il.Ldc_I4((char)node.Value);
                    break;

                case TypeCode.Int16:
                    context.Il.Ldc_I4((short)node.Value);
                    break;

                case TypeCode.Int32:
                    context.Il.Ldc_I4((int)node.Value);
                    break;

                case TypeCode.SByte:
                    context.Il.Ldc_I4((sbyte)node.Value);
                    break;

                case TypeCode.UInt16:
                    context.Il.Ldc_I4((ushort)node.Value);
                    break;

                case TypeCode.UInt32:
                    unchecked
                    {
                        context.Il.Ldc_I4((int)(uint)node.Value);
                    }
                    break;

                case TypeCode.Int64:
                    context.Il.Ldc_I8((long)node.Value);
                    break;

                case TypeCode.UInt64:
                    unchecked
                    {
                        context.Il.Ldc_I8((long)(ulong)node.Value);
                    }
                    break;

                case TypeCode.Single:
                    context.Il.Ldc_R4((float)node.Value);
                    break;

                case TypeCode.Double:
                    context.Il.Ldc_R8((double)node.Value);
                    break;

                case TypeCode.String:
                    context.Il.Ldstr((string)node.Value);
                    break;

                default:
                    throw new NotSupportedException("Constant of type '" + node.Type + "' is not supported");
                }
                if (node.Type.IsNullable())
                {
                    context.Il.Newobj(node.Type.GetConstructor(new[] { node.Type.GetGenericArguments()[0] }));
                }
            }
            resultType = node.Type;
            return(false);
        }