All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.dev.compiler.java.ConditionalExpression Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */


package com.tangosol.dev.compiler.java;


import com.tangosol.dev.assembler.CodeAttribute;
import com.tangosol.dev.assembler.Goto;
import com.tangosol.dev.assembler.Label;
import com.tangosol.dev.assembler.Ifeq;

import com.tangosol.dev.compiler.CompilerException;
import com.tangosol.dev.compiler.Context;

import com.tangosol.dev.component.DataType;

import com.tangosol.util.ErrorList;

import java.util.Set;
import java.util.Map;
import java.util.HashSet;


/**
* Implements the Conditional (? :) expression.
*
* @version 1.00, 10/05/98
* @author  Cameron Purdy
*/
public class ConditionalExpression extends Expression
    {
    // ----- construction ---------------------------------------------------

    /**
    * Construct a ConditionalExpression.
    *
    * @param test       the test expression
    * @param operator   the conditional operator
    * @param iftrue     the true expression
    * @param separator  the colon separating the true expression from the false
    * @param iffalse    the false expression
    */
    public ConditionalExpression(Expression test, Token operator, Expression iftrue, Token separator, Expression iffalse)
        {
        super(test.getBlock(), test.getStartToken(), iffalse.getEndToken());

        this.test      = test;
        this.operator  = operator;
        this.iftrue    = iftrue;
        this.separator = separator;
        this.iffalse   = iffalse;
        }


    // ----- code generation ------------------------------------------------

    /**
    * Perform semantic checks, parse tree re-organization, name binding,
    * and optimizations.
    *
    * @param ctx        the compiler context
    * @param setUVars   the set of potentially unassigned variables
    * @param setFVars   the set of potentially assigned final variables
    * @param mapThrown  the set of potentially thrown checked exceptions
    * @param errlist    the error list
    *
    * @return the resulting language element (typically this)
    *
    * @exception CompilerException  thrown if an error occurs that should
    *            stop the compilation process
    */
    protected Element precompile(Context ctx, DualSet setUVars, DualSet setFVars, Map mapThrown, ErrorList errlist)
            throws CompilerException
        {
        // get the sub-expressions
        Expression test    = this.test;
        Expression iftrue  = this.iftrue;
        Expression iffalse = this.iffalse;

        // JLS 16.1.11 The Boolean Operator ?:
        // Suppose that b and c are boolean-valued expressions.
        //  1)  V is definitely assigned after a ? b : c when true iff both
        //      of the following are true:
        //      1)  V is definitely assigned before b or V is definitely
        //          assigned after b when true.
        //      2)  V is definitely assigned before c or V is definitely
        //          assigned after c when true.
        //  2)  V is definitely assigned after a ? b : c when false iff both
        //      of the following are true:
        //      1)  V is definitely assigned before b or V is definitely
        //          assigned after b when false.
        //      2)  V is definitely assigned before c or V is definitely
        //          assigned after c when false.
        //  3)  V is definitely assigned before a iff V is definitely
        //      assigned before a ? b : c.
        //  4)  V is definitely assigned before b iff V is definitely
        //      assigned after a when true.
        //  5)  V is definitely assigned before c iff V is definitely
        //      assigned after a when false.
        //
        // JLS 16.1.12 The Conditional Operator ?:
        // Suppose that b and c are expressions that are not boolean-valued.
        //  1)  V is definitely assigned after a ? b : c iff both of the
        //      following are true:
        //      1)  V is definitely assigned after b.
        //      2)  V is definitely assigned after c.
        //  2)  V is definitely assigned before a iff V is definitely
        //      assigned before a ? b : c.
        //  3)  V is definitely assigned before b iff V is definitely
        //      assigned after a when true.
        //  4)  V is definitely assigned before c iff V is definitely
        //      assigned after a when false.
        //
        // Translation for definite assignment:
        //  1)  Precompile the test
        //  2)  Precompile the if-true expression using the when-true
        //      variables from the test precompilation
        //  3)  Precompile the if-false expression using the when-false
        //      variables from the test precompilation
        //  4)  Intersect the assigned variables from the if-true and
        //      if-false expressions; those are the variables assigned by
        //      the expression (in addition to those assigned by the test)
        //  5)  For if-true and if-false expressions that are boolean typed,
        //      determine the results both for "when true" and "when false"
        //
        // Translation for definite un-assignment (final vars):
        //  1)  Precompile the test
        //  2)  Precompile the if-true expression using the when-true
        //      variables from the test precompilation
        //  3)  Precompile the if-false expression using the when-false
        //      variables from the test precompilation
        //  4)  Union the potentially assigned variables from the if-true
        //      and if-false expressions; those are the variables potentially
        //      assigned by the expression (in addition to those potentially
        //      assigned by the test)
        //  5)  For if-true and if-false expressions that are boolean typed,
        //      determine the results both for "when true" and "when false"

        // pre-compile the test
        this.test = test = (Expression) test.precompile(ctx, setUVars, setFVars, mapThrown, errlist);
        test.checkBoolean(errlist);

        // pre-compile the if-true sub-expression
        DualSet setUTrue = new DualSet(setUVars.getTrueSet());
        DualSet setFTrue = new DualSet(setFVars.getTrueSet());
        this.iftrue = iftrue = (Expression) iftrue.precompile(ctx, setUTrue, setFVars, mapThrown, errlist);

        // pre-compile the if-false sub-expression
        DualSet setUFalse = new DualSet(setUVars.getFalseSet());
        DualSet setFFalse = new DualSet(setFVars.getFalseSet());
        this.iffalse = iffalse = (Expression) iffalse.precompile(ctx, setUTrue, setFVars, mapThrown, errlist);

        // determine type of expression
        DataType dtTrue   = iftrue .getType();
        DataType dtFalse  = iffalse.getType();
        DataType dtResult = UNKNOWN;

        // sub-expressions may both be boolean
        if (dtTrue == BOOLEAN)
            {
            // definite assignment
            if (setUTrue.isModified() && setUFalse.isModified())
                {
                Set setLeftTrue   = setUTrue .getTrueSet ().getRemoved();
                Set setLeftFalse  = setUTrue .getFalseSet().getRemoved();
                Set setRightTrue  = setUFalse.getTrueSet ().getRemoved();
                Set setRightFalse = setUFalse.getFalseSet().getRemoved();

                Set setAssigned = new HashSet(setLeftTrue);
                setAssigned.retainAll(setRightTrue);
                setUVars.getTrueSet().removeAll(setAssigned);

                setAssigned = new HashSet(setLeftFalse);
                setAssigned.retainAll(setRightFalse);
                setUVars.getFalseSet().removeAll(setAssigned);
                }

            // definite un-assignment (potential assignment)
            if (setFTrue.isModified() && setFFalse.isModified())
                {
                Set setLeftTrue   = setFTrue .getTrueSet ().getAdded();
                Set setLeftFalse  = setFTrue .getFalseSet().getAdded();
                Set setRightTrue  = setFFalse.getTrueSet ().getAdded();
                Set setRightFalse = setFFalse.getFalseSet().getAdded();

                Set setAssigned = new HashSet(setLeftTrue);
                setAssigned.addAll(setRightTrue);
                setUVars.getTrueSet().addAll(setAssigned);

                setAssigned = new HashSet(setLeftFalse);
                setAssigned.addAll(setRightFalse);
                setUVars.getFalseSet().addAll(setAssigned);
                }

            iffalse.checkBoolean(errlist);
            dtResult = BOOLEAN;
            }
        else
            {
            // definite assignment/definite unassignment:
            // when true/when false processing is only for boolean types;
            // just merge/commit the changes (which in effect intersects the
            // variables definitely assigned by if-true and if-false)
            setUTrue .resolve();
            setUFalse.resolve();
            setFTrue .resolve();
            setFFalse.resolve();

            // sub-expressions may both be reference types
            if (dtTrue.isReference())
                {
                final DataType NULL   = this.NULL;

                // JLS 15.2 Conditional Operator ?:
                //  - If the second and third operands have the same type
                //    (which may be the null type), then that is the type
                //    of the conditional expression.
                //  - If one of the second and third operands is of the
                //    null type and the type of the other is a reference
                //    type, then the type of the conditional expression
                //    is that reference type.
                //  - If the second and third operands are of different
                //    reference types, then it must be possible to
                //    convert one of the types to the other type (call
                //    this latter type T) by assignment conversion
                //    (5.2); the type of the conditional expression is
                //    T. It is a compile-time error if neither type is
                //    assignment compatible with the other type.
                if (dtTrue == dtFalse || !iffalse.checkReference(errlist))
                    {
                    dtResult = dtTrue;
                    }
                else if (dtTrue == NULL)
                    {
                    dtResult = dtFalse;
                    }
                else if (dtFalse == NULL)
                    {
                    dtResult = dtTrue;
                    }
                else if (iffalse.checkAssignable(ctx, dtTrue, null))
                    {
                    iffalse  = iffalse.convertAssignable(ctx, dtTrue);
                    dtResult = dtTrue;
                    }
                else if (iftrue.checkAssignable(ctx, dtFalse, errlist))
                    {
                    iftrue   = iftrue.convertAssignable(ctx, dtFalse);
                    dtResult = dtFalse;
                    }
                else
                    {
                    // error
                    dtResult = NULL;
                    }
                }
            // otherwise they must both be numeric
            else if (iftrue.checkNumeric(errlist) & iffalse.checkNumeric(errlist))
                {
                final DataType BYTE  = this.BYTE;
                final DataType CHAR  = this.CHAR;
                final DataType SHORT = this.SHORT;
                final DataType INT   = this.INT;

                // JLS 15.2 Conditional Operator ?:
                //  - If the second and third operands have the same type
                //    (which may be the null type), then that is the type
                //    of the conditional expression.
                //  - If one of the operands is of type byte and the other
                //    is of type short, then the type of the conditional
                //    expression is short.
                //  - If one of the operands is of type T where T is byte,
                //    short, or char, and the other operand is a constant
                //    expression of type int whose value is representable
                //    in type T, then the type of the conditional expression
                //    is T.
                //  - Otherwise, binary numeric promotion (5.6.2) is applied
                //    to the operand types, and the type of the conditional
                //    expression is the promoted type of the second and third
                //    operands.

                if (dtTrue == dtFalse)
                    {
                    dtResult = dtTrue;
                    }
                else if ((dtTrue  == BYTE || dtTrue  == SHORT)
                      && (dtFalse == BYTE || dtFalse == SHORT))
                    {
                    dtResult = SHORT;
                    }
                else if ((dtTrue == BYTE || dtTrue == SHORT || dtTrue == CHAR)
                        && dtFalse == INT && iffalse.isConstant()
                        && iffalse.checkAssignable(ctx, dtTrue, null))
                    {
                    iffalse  = iffalse.convertAssignable(ctx, dtTrue);
                    dtResult = dtTrue;
                    }
                else if ((dtFalse == BYTE || dtFalse == SHORT || dtFalse == CHAR)
                        && dtTrue == INT && iftrue.isConstant()
                        && iftrue.checkAssignable(ctx, dtFalse, null))
                    {
                    iftrue   = iftrue.convertAssignable(ctx, dtFalse);
                    dtResult = dtFalse;
                    }
                else
                    {
                    // binary numeric promotion
                    iftrue   = iftrue .promoteNumeric(iffalse);
                    iffalse  = iffalse.promoteNumeric(iftrue );
                    dtResult = iftrue.getType();
                    }
                }
            }

        // store expressions
        this.test    = test;
        this.iftrue  = iftrue;
        this.iffalse = iffalse;

        // store type
        setType(dtResult);

        return this;
        }

    /**
    * Perform final optimizations and code generation.
    *
    * @param ctx       the compiler context
    * @param code      the assembler code attribute to compile to
    * @param fReached  true if this language element is reached (JLS 14.19)
    * @param errlist   the error list to log errors to
    *
    * @return true if the element can complete normally (JLS 14.1)
    *
    * @exception CompilerException  thrown if an error occurs that should
    *            stop the compilation process
    */
    protected boolean compile(Context ctx, CodeAttribute code, boolean fReached, ErrorList errlist)
            throws CompilerException
        {
        if (!ctx.isDebug() && isConstant())
            {
            // Expression has compiler implementation for constant values
            return super.compile(ctx, code, fReached, errlist);
            }

        // get the sub-expressions
        Expression test    = this.test;
        Expression iftrue  = this.iftrue;
        Expression iffalse = this.iffalse;

        // compilation of " ?  : ":
        //
        //          [expr]
        //          ifeq    else
        //          [iftrue]
        //          goto    exit
        //  else:
        //          [iffalse]
        //  exit:

        if (test instanceof BooleanExpression || !ctx.isDebug() && test.isConstant())
            {
            Expression result = ((Boolean) test.getValue()).booleanValue() ? iftrue : iffalse;
            result.compile(ctx, code, fReached, errlist);
            }
        else
            {
            Label lblElse = new Label();
            Label lblExit = new Label();

            test.compile(ctx, code, fReached, errlist);
            code.add(new Ifeq(lblElse));
            iftrue.compile(ctx, code, fReached, errlist);
            code.add(new Goto(lblExit));
            code.add(lblElse);
            iffalse.compile(ctx, code, fReached, errlist);
            code.add(lblExit);
            }

        return fReached;
        }


    // ----- Expression methods ---------------------------------------------

    /**
    * Determine if the expression has a constant value.
    *
    * @return true if the expression results in a constant value
    */
    public boolean isConstant()
        {
        // constant iff test is constant boolean and result is constant
        Expression test = this.test;
        if (test.getType() == BOOLEAN && test.isConstant())
            {
            return (((Boolean) test.getValue()).booleanValue() ? iftrue : iffalse).isConstant();
            }

        return false;
        }

    /**
    * Determine the constant value of the expression.
    *
    * @return the constant value of the expression
    */
    public Object getValue()
        {
        return (((Boolean) test.getValue()).booleanValue() ? iftrue : iffalse).getValue();
        }


    // ----- accessors ------------------------------------------------------

    /**
    * Get the conditional test.
    *
    * @return the conditional test
    */
    public Expression getTest()
        {
        return test;
        }

    /**
    * Set the conditional test.
    *
    * @param test  the new conditional test
    */
    protected void setTest(Expression test)
        {
        this.test = test;
        }

    /**
    * Get the expression which is evaluated if the test evaluates to true.
    *
    * @return the "true" expression
    */
    public Expression getTrueExpression()
        {
        return iftrue;
        }

    /**
    * Set the expression which is evaluated if the test evaluates to true.
    *
    * @param iftrue  the new "true" expression
    */
    public void setTrueExpression(Expression iftrue)
        {
        this.iftrue = iftrue;
        }

    /**
    * Get the expression which is evaluated if the test evaluates to false.
    *
    * @return the "false" expression
    */
    public Expression getFalseExpression()
        {
        return iffalse;
        }

    /**
    * Set the expression which is evaluated if the test evaluates to false.
    *
    * @param iffalse  the new "false" expression
    */
    public void setFalseExpression(Expression iffalse)
        {
        this.iffalse = iffalse;
        }


    // ----- Element methods ------------------------------------------------

    /**
    * Print the element information.
    *
    * @param sIndent
    */
    public void print(String sIndent)
        {
        out(sIndent + toString());

        out(sIndent + "  test:");
        test.print(sIndent + "    ");

        out(sIndent + "  value if true:");
        iftrue.print(sIndent + "    ");

        out(sIndent + "  value if false:");
        iffalse.print(sIndent + "    ");
        }


    // ----- data members ---------------------------------------------------

    /**
    * The class name.
    */
    private static final String CLASS = "ConditionalExpression";

    /**
    * Unknown data type (caused by compiler error).
    */
    private static final DataType UNKNOWN = DataType.UNKNOWN;

    /**
    * Null data type.
    */
    private static final DataType NULL = DataType.NULL;

    /**
    * Boolean data type.
    */
    private static final DataType BOOLEAN = DataType.BOOLEAN;

    /**
    * Byte data type.
    */
    private static final DataType BYTE = DataType.BYTE;

    /**
    * Char data type.
    */
    private static final DataType CHAR = DataType.CHAR;

    /**
    * Short data type.
    */
    private static final DataType SHORT = DataType.SHORT;

    /**
    * Int data type.
    */
    private static final DataType INT = DataType.INT;

    /**
    * The test expression.
    */
    private Expression test;

    /**
    * The conditional operator.
    */
    private Token operator;

    /**
    * The true expression.
    */
    private Expression iftrue;

    /**
    * The colon separator.
    */
    private Token separator;

    /**
    * The false expression.
    */
    private Expression iffalse;
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy