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(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);
        }
Exemple #3
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);
        }