org.eclipse.jdt.internal.compiler.ast.BinaryExpression.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.ast.BinaryExpression.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2013 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contribution for
 *                        bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
 *                        bug 383368 - [compiler][null] syntactic null analysis for field references
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;

public class BinaryExpression extends OperatorExpression {

    /* Tracking helpers
     * The following are used to elaborate realistic statistics about binary
     * expressions. This must be neutralized in the released code.
     * Search the keyword BE_INSTRUMENTATION to reenable.
     * An external device must install a suitable probe so as to monitor the
     * emission of events and publish the results.
       public interface Probe {
          public void ping(int depth);
       }
       public int depthTracker;
       public static Probe probe;
     */

    public Expression left, right;
    public Constant optimizedBooleanConstant;

    public BinaryExpression(Expression left, Expression right, int operator) {
        this.left = left;
        this.right = right;
        this.bits |= operator << ASTNode.OperatorSHIFT; // encode operator
        this.sourceStart = left.sourceStart;
        this.sourceEnd = right.sourceEnd;
        // BE_INSTRUMENTATION: neutralized in the released code
        //   if (left instanceof BinaryExpression &&
        //         ((left.bits & OperatorMASK) ^ (this.bits & OperatorMASK)) == 0) {
        //      this.depthTracker = ((BinaryExpression)left).depthTracker + 1;
        //   } else {
        //      this.depthTracker = 1;
        //   }
    }

    public BinaryExpression(BinaryExpression expression) {
        this.left = expression.left;
        this.right = expression.right;
        this.bits = expression.bits;
        this.sourceStart = expression.sourceStart;
        this.sourceEnd = expression.sourceEnd;
    }

    @Override
    public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
        // keep implementation in sync with CombinedBinaryExpression#analyseCode
        try {
            if (this.resolvedType.id == TypeIds.T_JavaLangString) {
                return this.right
                        .analyseCode(currentScope, flowContext,
                                this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits())
                        .unconditionalInits();
            } else {
                flowInfo = this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
                this.left.checkNPE(currentScope, flowContext, flowInfo);
                if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) {
                    flowContext.expireNullCheckedFieldInfo();
                }
                flowInfo = this.right.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
                this.right.checkNPE(currentScope, flowContext, flowInfo);
                if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) {
                    flowContext.expireNullCheckedFieldInfo();
                }
                return flowInfo;
            }
        } finally {
            // account for exception possibly thrown by arithmetics
            flowContext.recordAbruptExit();
        }
    }

    public void computeConstant(BlockScope scope, int leftId, int rightId) {
        //compute the constant when valid
        if ((this.left.constant != Constant.NotAConstant) && (this.right.constant != Constant.NotAConstant)) {
            try {
                this.constant = Constant.computeConstantOperation(this.left.constant, leftId,
                        (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT, this.right.constant, rightId);
            } catch (ArithmeticException e) {
                this.constant = Constant.NotAConstant;
                // 1.2 no longer throws an exception at compile-time
                //scope.problemReporter().compileTimeConstantThrowsArithmeticException(this);
            }
        } else {
            this.constant = Constant.NotAConstant;
            //add some work for the boolean operators & |
            this.optimizedBooleanConstant(leftId, (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT,
                    rightId);
        }
    }

    @Override
    public Constant optimizedBooleanConstant() {
        return this.optimizedBooleanConstant == null ? this.constant : this.optimizedBooleanConstant;
    }

    /**
     * Code generation for a binary operation
     */
    // given the current focus of CombinedBinaryExpression on strings concatenation,
    // we do not provide a general, non-recursive implementation of generateCode,
    // but rely upon generateOptimizedStringConcatenationCreation instead
    @Override
    public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
        int pc = codeStream.position;
        if (this.constant != Constant.NotAConstant) {
            if (valueRequired)
                codeStream.generateConstant(this.constant, this.implicitConversion);
            codeStream.recordPositionsFrom(pc, this.sourceStart);
            return;
        }
        switch ((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) {
        case PLUS:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_JavaLangString:
                // BE_INSTRUMENTATION: neutralized in the released code
                //               if (probe != null) {
                //                  probe.ping(this.depthTracker);
                //               }
                codeStream.generateStringConcatenationAppend(currentScope, this.left, this.right);
                if (!valueRequired)
                    codeStream.pop();
                break;
            case T_int:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.iadd();
                break;
            case T_long:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.ladd();
                break;
            case T_double:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.dadd();
                break;
            case T_float:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.fadd();
                break;
            }
            break;
        case MINUS:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.isub();
                break;
            case T_long:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.lsub();
                break;
            case T_double:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.dsub();
                break;
            case T_float:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.fsub();
                break;
            }
            break;
        case MULTIPLY:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.imul();
                break;
            case T_long:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.lmul();
                break;
            case T_double:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.dmul();
                break;
            case T_float:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.fmul();
                break;
            }
            break;
        case DIVIDE:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                this.left.generateCode(currentScope, codeStream, true);
                this.right.generateCode(currentScope, codeStream, true);
                codeStream.idiv();
                if (!valueRequired)
                    codeStream.pop();
                break;
            case T_long:
                this.left.generateCode(currentScope, codeStream, true);
                this.right.generateCode(currentScope, codeStream, true);
                codeStream.ldiv();
                if (!valueRequired)
                    codeStream.pop2();
                break;
            case T_double:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.ddiv();
                break;
            case T_float:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.fdiv();
                break;
            }
            break;
        case REMAINDER:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                this.left.generateCode(currentScope, codeStream, true);
                this.right.generateCode(currentScope, codeStream, true);
                codeStream.irem();
                if (!valueRequired)
                    codeStream.pop();
                break;
            case T_long:
                this.left.generateCode(currentScope, codeStream, true);
                this.right.generateCode(currentScope, codeStream, true);
                codeStream.lrem();
                if (!valueRequired)
                    codeStream.pop2();
                break;
            case T_double:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.drem();
                break;
            case T_float:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.frem();
                break;
            }
            break;
        case AND:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                // 0 & x
                if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.typeID() == TypeIds.T_int)
                        && (this.left.constant.intValue() == 0)) {
                    this.right.generateCode(currentScope, codeStream, false);
                    if (valueRequired)
                        codeStream.iconst_0();
                } else {
                    // x & 0
                    if ((this.right.constant != Constant.NotAConstant)
                            && (this.right.constant.typeID() == TypeIds.T_int)
                            && (this.right.constant.intValue() == 0)) {
                        this.left.generateCode(currentScope, codeStream, false);
                        if (valueRequired)
                            codeStream.iconst_0();
                    } else {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                        this.right.generateCode(currentScope, codeStream, valueRequired);
                        if (valueRequired)
                            codeStream.iand();
                    }
                }
                break;
            case T_long:
                // 0 & x
                if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.typeID() == TypeIds.T_long)
                        && (this.left.constant.longValue() == 0L)) {
                    this.right.generateCode(currentScope, codeStream, false);
                    if (valueRequired)
                        codeStream.lconst_0();
                } else {
                    // x & 0
                    if ((this.right.constant != Constant.NotAConstant)
                            && (this.right.constant.typeID() == TypeIds.T_long)
                            && (this.right.constant.longValue() == 0L)) {
                        this.left.generateCode(currentScope, codeStream, false);
                        if (valueRequired)
                            codeStream.lconst_0();
                    } else {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                        this.right.generateCode(currentScope, codeStream, valueRequired);
                        if (valueRequired)
                            codeStream.land();
                    }
                }
                break;
            case T_boolean: // logical and
                generateLogicalAnd(currentScope, codeStream, valueRequired);
                break;
            }
            break;
        case OR:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                // 0 | x
                if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.typeID() == TypeIds.T_int)
                        && (this.left.constant.intValue() == 0)) {
                    this.right.generateCode(currentScope, codeStream, valueRequired);
                } else {
                    // x | 0
                    if ((this.right.constant != Constant.NotAConstant)
                            && (this.right.constant.typeID() == TypeIds.T_int)
                            && (this.right.constant.intValue() == 0)) {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                    } else {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                        this.right.generateCode(currentScope, codeStream, valueRequired);
                        if (valueRequired)
                            codeStream.ior();
                    }
                }
                break;
            case T_long:
                // 0 | x
                if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.typeID() == TypeIds.T_long)
                        && (this.left.constant.longValue() == 0L)) {
                    this.right.generateCode(currentScope, codeStream, valueRequired);
                } else {
                    // x | 0
                    if ((this.right.constant != Constant.NotAConstant)
                            && (this.right.constant.typeID() == TypeIds.T_long)
                            && (this.right.constant.longValue() == 0L)) {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                    } else {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                        this.right.generateCode(currentScope, codeStream, valueRequired);
                        if (valueRequired)
                            codeStream.lor();
                    }
                }
                break;
            case T_boolean: // logical or
                generateLogicalOr(currentScope, codeStream, valueRequired);
                break;
            }
            break;
        case XOR:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                // 0 ^ x
                if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.typeID() == TypeIds.T_int)
                        && (this.left.constant.intValue() == 0)) {
                    this.right.generateCode(currentScope, codeStream, valueRequired);
                } else {
                    // x ^ 0
                    if ((this.right.constant != Constant.NotAConstant)
                            && (this.right.constant.typeID() == TypeIds.T_int)
                            && (this.right.constant.intValue() == 0)) {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                    } else {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                        this.right.generateCode(currentScope, codeStream, valueRequired);
                        if (valueRequired)
                            codeStream.ixor();
                    }
                }
                break;
            case T_long:
                // 0 ^ x
                if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.typeID() == TypeIds.T_long)
                        && (this.left.constant.longValue() == 0L)) {
                    this.right.generateCode(currentScope, codeStream, valueRequired);
                } else {
                    // x ^ 0
                    if ((this.right.constant != Constant.NotAConstant)
                            && (this.right.constant.typeID() == TypeIds.T_long)
                            && (this.right.constant.longValue() == 0L)) {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                    } else {
                        this.left.generateCode(currentScope, codeStream, valueRequired);
                        this.right.generateCode(currentScope, codeStream, valueRequired);
                        if (valueRequired)
                            codeStream.lxor();
                    }
                }
                break;
            case T_boolean:
                generateLogicalXor(currentScope, codeStream, valueRequired);
                break;
            }
            break;
        case LEFT_SHIFT:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.ishl();
                break;
            case T_long:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.lshl();
            }
            break;
        case RIGHT_SHIFT:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.ishr();
                break;
            case T_long:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.lshr();
            }
            break;
        case UNSIGNED_RIGHT_SHIFT:
            switch (this.bits & ASTNode.ReturnTypeIDMASK) {
            case T_int:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.iushr();
                break;
            case T_long:
                this.left.generateCode(currentScope, codeStream, valueRequired);
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired)
                    codeStream.lushr();
            }
            break;
        case GREATER:
            BranchLabel falseLabel, endLabel;
            generateOptimizedGreaterThan(currentScope, codeStream, null, (falseLabel = new BranchLabel(codeStream)),
                    valueRequired);
            if (valueRequired) {
                codeStream.iconst_1();
                if ((this.bits & ASTNode.IsReturnedValue) != 0) {
                    codeStream.generateImplicitConversion(this.implicitConversion);
                    codeStream.generateReturnBytecode(this);
                    falseLabel.place();
                    codeStream.iconst_0();
                } else {
                    codeStream.goto_(endLabel = new BranchLabel(codeStream));
                    codeStream.decrStackSize(1);
                    falseLabel.place();
                    codeStream.iconst_0();
                    endLabel.place();
                }
            }
            break;
        case GREATER_EQUAL:
            generateOptimizedGreaterThanOrEqual(currentScope, codeStream, null,
                    (falseLabel = new BranchLabel(codeStream)), valueRequired);
            if (valueRequired) {
                codeStream.iconst_1();
                if ((this.bits & ASTNode.IsReturnedValue) != 0) {
                    codeStream.generateImplicitConversion(this.implicitConversion);
                    codeStream.generateReturnBytecode(this);
                    falseLabel.place();
                    codeStream.iconst_0();
                } else {
                    codeStream.goto_(endLabel = new BranchLabel(codeStream));
                    codeStream.decrStackSize(1);
                    falseLabel.place();
                    codeStream.iconst_0();
                    endLabel.place();
                }
            }
            break;
        case LESS:
            generateOptimizedLessThan(currentScope, codeStream, null, (falseLabel = new BranchLabel(codeStream)),
                    valueRequired);
            if (valueRequired) {
                codeStream.iconst_1();
                if ((this.bits & ASTNode.IsReturnedValue) != 0) {
                    codeStream.generateImplicitConversion(this.implicitConversion);
                    codeStream.generateReturnBytecode(this);
                    falseLabel.place();
                    codeStream.iconst_0();
                } else {
                    codeStream.goto_(endLabel = new BranchLabel(codeStream));
                    codeStream.decrStackSize(1);
                    falseLabel.place();
                    codeStream.iconst_0();
                    endLabel.place();
                }
            }
            break;
        case LESS_EQUAL:
            generateOptimizedLessThanOrEqual(currentScope, codeStream, null,
                    (falseLabel = new BranchLabel(codeStream)), valueRequired);
            if (valueRequired) {
                codeStream.iconst_1();
                if ((this.bits & ASTNode.IsReturnedValue) != 0) {
                    codeStream.generateImplicitConversion(this.implicitConversion);
                    codeStream.generateReturnBytecode(this);
                    falseLabel.place();
                    codeStream.iconst_0();
                } else {
                    codeStream.goto_(endLabel = new BranchLabel(codeStream));
                    codeStream.decrStackSize(1);
                    falseLabel.place();
                    codeStream.iconst_0();
                    endLabel.place();
                }
            }
        }
        if (valueRequired) {
            codeStream.generateImplicitConversion(this.implicitConversion);
        }
        codeStream.recordPositionsFrom(pc, this.sourceStart);
    }

    /**
     * Boolean operator code generation
     *   Optimized operations are: <, <=, >, >=, &, |, ^
     */
    @Override
    public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
            BranchLabel falseLabel, boolean valueRequired) {
        if ((this.constant != Constant.NotAConstant) && (this.constant.typeID() == TypeIds.T_boolean)) {
            super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
            return;
        }
        switch ((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) {
        case LESS:
            generateOptimizedLessThan(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
            return;
        case LESS_EQUAL:
            generateOptimizedLessThanOrEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
            return;
        case GREATER:
            generateOptimizedGreaterThan(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
            return;
        case GREATER_EQUAL:
            generateOptimizedGreaterThanOrEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
            return;
        case AND:
            generateOptimizedLogicalAnd(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
            return;
        case OR:
            generateOptimizedLogicalOr(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
            return;
        case XOR:
            generateOptimizedLogicalXor(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
            return;
        }
        super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
    }

    /**
     * Boolean generation for >
     */
    public void generateOptimizedGreaterThan(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
            BranchLabel falseLabel, boolean valueRequired) {
        int promotedTypeID = (this.left.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
        // both sides got promoted in the same way
        if (promotedTypeID == TypeIds.T_int) {
            // 0 > x
            if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.intValue() == 0)) {
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired) {
                    if (falseLabel == null) {
                        if (trueLabel != null) {
                            // implicitly falling through the FALSE case
                            codeStream.iflt(trueLabel);
                        }
                    } else {
                        if (trueLabel == null) {
                            // implicitly falling through the TRUE case
                            codeStream.ifge(falseLabel);
                        } else {
                            // no implicit fall through TRUE/FALSE --> should never occur
                        }
                    }
                }
                // reposition the endPC
                codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                return;
            }
            // x > 0
            if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.intValue() == 0)) {
                this.left.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired) {
                    if (falseLabel == null) {
                        if (trueLabel != null) {
                            // implicitly falling through the FALSE case
                            codeStream.ifgt(trueLabel);
                        }
                    } else {
                        if (trueLabel == null) {
                            // implicitly falling through the TRUE case
                            codeStream.ifle(falseLabel);
                        } else {
                            // no implicit fall through TRUE/FALSE --> should never occur
                        }
                    }
                }
                // reposition the endPC
                codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                return;
            }
        }
        // default comparison
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            if (falseLabel == null) {
                if (trueLabel != null) {
                    // implicit falling through the FALSE case
                    switch (promotedTypeID) {
                    case T_int:
                        codeStream.if_icmpgt(trueLabel);
                        break;
                    case T_float:
                        codeStream.fcmpl();
                        codeStream.ifgt(trueLabel);
                        break;
                    case T_long:
                        codeStream.lcmp();
                        codeStream.ifgt(trueLabel);
                        break;
                    case T_double:
                        codeStream.dcmpl();
                        codeStream.ifgt(trueLabel);
                    }
                    // reposition the endPC
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    return;
                }
            } else {
                if (trueLabel == null) {
                    // implicit falling through the TRUE case
                    switch (promotedTypeID) {
                    case T_int:
                        codeStream.if_icmple(falseLabel);
                        break;
                    case T_float:
                        codeStream.fcmpl();
                        codeStream.ifle(falseLabel);
                        break;
                    case T_long:
                        codeStream.lcmp();
                        codeStream.ifle(falseLabel);
                        break;
                    case T_double:
                        codeStream.dcmpl();
                        codeStream.ifle(falseLabel);
                    }
                    // reposition the endPC
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    return;
                } else {
                    // no implicit fall through TRUE/FALSE --> should never occur
                }
            }
        }
    }

    /**
     * Boolean generation for >=
     */
    public void generateOptimizedGreaterThanOrEqual(BlockScope currentScope, CodeStream codeStream,
            BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {
        int promotedTypeID = (this.left.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
        // both sides got promoted in the same way
        if (promotedTypeID == TypeIds.T_int) {
            // 0 >= x
            if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.intValue() == 0)) {
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired) {
                    if (falseLabel == null) {
                        if (trueLabel != null) {
                            // implicitly falling through the FALSE case
                            codeStream.ifle(trueLabel);
                        }
                    } else {
                        if (trueLabel == null) {
                            // implicitly falling through the TRUE case
                            codeStream.ifgt(falseLabel);
                        } else {
                            // no implicit fall through TRUE/FALSE --> should never occur
                        }
                    }
                }
                // reposition the endPC
                codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                return;
            }
            // x >= 0
            if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.intValue() == 0)) {
                this.left.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired) {
                    if (falseLabel == null) {
                        if (trueLabel != null) {
                            // implicitly falling through the FALSE case
                            codeStream.ifge(trueLabel);
                        }
                    } else {
                        if (trueLabel == null) {
                            // implicitly falling through the TRUE case
                            codeStream.iflt(falseLabel);
                        } else {
                            // no implicit fall through TRUE/FALSE --> should never occur
                        }
                    }
                }
                // reposition the endPC
                codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                return;
            }
        }
        // default comparison
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            if (falseLabel == null) {
                if (trueLabel != null) {
                    // implicit falling through the FALSE case
                    switch (promotedTypeID) {
                    case T_int:
                        codeStream.if_icmpge(trueLabel);
                        break;
                    case T_float:
                        codeStream.fcmpl();
                        codeStream.ifge(trueLabel);
                        break;
                    case T_long:
                        codeStream.lcmp();
                        codeStream.ifge(trueLabel);
                        break;
                    case T_double:
                        codeStream.dcmpl();
                        codeStream.ifge(trueLabel);
                    }
                    // reposition the endPC
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    return;
                }
            } else {
                if (trueLabel == null) {
                    // implicit falling through the TRUE case
                    switch (promotedTypeID) {
                    case T_int:
                        codeStream.if_icmplt(falseLabel);
                        break;
                    case T_float:
                        codeStream.fcmpl();
                        codeStream.iflt(falseLabel);
                        break;
                    case T_long:
                        codeStream.lcmp();
                        codeStream.iflt(falseLabel);
                        break;
                    case T_double:
                        codeStream.dcmpl();
                        codeStream.iflt(falseLabel);
                    }
                    // reposition the endPC
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    return;
                } else {
                    // no implicit fall through TRUE/FALSE --> should never occur
                }
            }
        }
    }

    /**
     * Boolean generation for <
     */
    public void generateOptimizedLessThan(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
            BranchLabel falseLabel, boolean valueRequired) {
        int promotedTypeID = (this.left.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
        // both sides got promoted in the same way
        if (promotedTypeID == TypeIds.T_int) {
            // 0 < x
            if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.intValue() == 0)) {
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired) {
                    if (falseLabel == null) {
                        if (trueLabel != null) {
                            // implicitly falling through the FALSE case
                            codeStream.ifgt(trueLabel);
                        }
                    } else {
                        if (trueLabel == null) {
                            // implicitly falling through the TRUE case
                            codeStream.ifle(falseLabel);
                        } else {
                            // no implicit fall through TRUE/FALSE --> should never occur
                        }
                    }
                }
                codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                return;
            }
            // x < 0
            if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.intValue() == 0)) {
                this.left.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired) {
                    if (falseLabel == null) {
                        if (trueLabel != null) {
                            // implicitly falling through the FALSE case
                            codeStream.iflt(trueLabel);
                        }
                    } else {
                        if (trueLabel == null) {
                            // implicitly falling through the TRUE case
                            codeStream.ifge(falseLabel);
                        } else {
                            // no implicit fall through TRUE/FALSE --> should never occur
                        }
                    }
                }
                codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                return;
            }
        }
        // default comparison
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            if (falseLabel == null) {
                if (trueLabel != null) {
                    // implicit falling through the FALSE case
                    switch (promotedTypeID) {
                    case T_int:
                        codeStream.if_icmplt(trueLabel);
                        break;
                    case T_float:
                        codeStream.fcmpg();
                        codeStream.iflt(trueLabel);
                        break;
                    case T_long:
                        codeStream.lcmp();
                        codeStream.iflt(trueLabel);
                        break;
                    case T_double:
                        codeStream.dcmpg();
                        codeStream.iflt(trueLabel);
                    }
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    return;
                }
            } else {
                if (trueLabel == null) {
                    // implicit falling through the TRUE case
                    switch (promotedTypeID) {
                    case T_int:
                        codeStream.if_icmpge(falseLabel);
                        break;
                    case T_float:
                        codeStream.fcmpg();
                        codeStream.ifge(falseLabel);
                        break;
                    case T_long:
                        codeStream.lcmp();
                        codeStream.ifge(falseLabel);
                        break;
                    case T_double:
                        codeStream.dcmpg();
                        codeStream.ifge(falseLabel);
                    }
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    return;
                } else {
                    // no implicit fall through TRUE/FALSE --> should never occur
                }
            }
        }
    }

    /**
     * Boolean generation for <=
     */
    public void generateOptimizedLessThanOrEqual(BlockScope currentScope, CodeStream codeStream,
            BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {
        int promotedTypeID = (this.left.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
        // both sides got promoted in the same way
        if (promotedTypeID == TypeIds.T_int) {
            // 0 <= x
            if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.intValue() == 0)) {
                this.right.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired) {
                    if (falseLabel == null) {
                        if (trueLabel != null) {
                            // implicitly falling through the FALSE case
                            codeStream.ifge(trueLabel);
                        }
                    } else {
                        if (trueLabel == null) {
                            // implicitly falling through the TRUE case
                            codeStream.iflt(falseLabel);
                        } else {
                            // no implicit fall through TRUE/FALSE --> should never occur
                        }
                    }
                }
                // reposition the endPC
                codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                return;
            }
            // x <= 0
            if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.intValue() == 0)) {
                this.left.generateCode(currentScope, codeStream, valueRequired);
                if (valueRequired) {
                    if (falseLabel == null) {
                        if (trueLabel != null) {
                            // implicitly falling through the FALSE case
                            codeStream.ifle(trueLabel);
                        }
                    } else {
                        if (trueLabel == null) {
                            // implicitly falling through the TRUE case
                            codeStream.ifgt(falseLabel);
                        } else {
                            // no implicit fall through TRUE/FALSE --> should never occur
                        }
                    }
                }
                // reposition the endPC
                codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                return;
            }
        }
        // default comparison
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            if (falseLabel == null) {
                if (trueLabel != null) {
                    // implicit falling through the FALSE case
                    switch (promotedTypeID) {
                    case T_int:
                        codeStream.if_icmple(trueLabel);
                        break;
                    case T_float:
                        codeStream.fcmpg();
                        codeStream.ifle(trueLabel);
                        break;
                    case T_long:
                        codeStream.lcmp();
                        codeStream.ifle(trueLabel);
                        break;
                    case T_double:
                        codeStream.dcmpg();
                        codeStream.ifle(trueLabel);
                    }
                    // reposition the endPC
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    return;
                }
            } else {
                if (trueLabel == null) {
                    // implicit falling through the TRUE case
                    switch (promotedTypeID) {
                    case T_int:
                        codeStream.if_icmpgt(falseLabel);
                        break;
                    case T_float:
                        codeStream.fcmpg();
                        codeStream.ifgt(falseLabel);
                        break;
                    case T_long:
                        codeStream.lcmp();
                        codeStream.ifgt(falseLabel);
                        break;
                    case T_double:
                        codeStream.dcmpg();
                        codeStream.ifgt(falseLabel);
                    }
                    // reposition the endPC
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    return;
                } else {
                    // no implicit fall through TRUE/FALSE --> should never occur
                }
            }
        }
    }

    /**
     * Boolean generation for &
     */
    public void generateLogicalAnd(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
        Constant condConst;
        if ((this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK) == TypeIds.T_boolean) {
            if ((condConst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // <something equivalent to true> & x
                    this.left.generateCode(currentScope, codeStream, false);
                    this.right.generateCode(currentScope, codeStream, valueRequired);
                } else {
                    // <something equivalent to false> & x
                    this.left.generateCode(currentScope, codeStream, false);
                    this.right.generateCode(currentScope, codeStream, false);
                    if (valueRequired) {
                        codeStream.iconst_0();
                    }
                    // reposition the endPC
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                }
                return;
            }
            if ((condConst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // x & <something equivalent to true>
                    this.left.generateCode(currentScope, codeStream, valueRequired);
                    this.right.generateCode(currentScope, codeStream, false);
                } else {
                    // x & <something equivalent to false>
                    this.left.generateCode(currentScope, codeStream, false);
                    this.right.generateCode(currentScope, codeStream, false);
                    if (valueRequired) {
                        codeStream.iconst_0();
                    }
                    // reposition the endPC
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                }
                return;
            }
        }
        // default case
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            codeStream.iand();
        }
        codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
    }

    /**
     * Boolean generation for |
     */
    public void generateLogicalOr(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
        Constant condConst;
        if ((this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK) == TypeIds.T_boolean) {
            if ((condConst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // <something equivalent to true> | x
                    this.left.generateCode(currentScope, codeStream, false);
                    this.right.generateCode(currentScope, codeStream, false);
                    if (valueRequired) {
                        codeStream.iconst_1();
                    }
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                } else {
                    // <something equivalent to false> | x
                    this.left.generateCode(currentScope, codeStream, false);
                    this.right.generateCode(currentScope, codeStream, valueRequired);
                }
                return;
            }
            if ((condConst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // x | <something equivalent to true>
                    this.left.generateCode(currentScope, codeStream, false);
                    this.right.generateCode(currentScope, codeStream, false);
                    if (valueRequired) {
                        codeStream.iconst_1();
                    }
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                } else {
                    // x | <something equivalent to false>
                    this.left.generateCode(currentScope, codeStream, valueRequired);
                    this.right.generateCode(currentScope, codeStream, false);
                }
                return;
            }
        }
        // default case
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            codeStream.ior();
        }
        codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
    }

    /**
     * Boolean generation for ^
     */
    public void generateLogicalXor(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
        Constant condConst;
        if ((this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK) == TypeIds.T_boolean) {
            if ((condConst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // <something equivalent to true> ^ x
                    this.left.generateCode(currentScope, codeStream, false);
                    if (valueRequired) {
                        codeStream.iconst_1();
                    }
                    this.right.generateCode(currentScope, codeStream, valueRequired);
                    if (valueRequired) {
                        codeStream.ixor(); // negate
                        codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    }
                } else {
                    // <something equivalent to false> ^ x
                    this.left.generateCode(currentScope, codeStream, false);
                    this.right.generateCode(currentScope, codeStream, valueRequired);
                }
                return;
            }
            if ((condConst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // x ^ <something equivalent to true>
                    this.left.generateCode(currentScope, codeStream, valueRequired);
                    this.right.generateCode(currentScope, codeStream, false);
                    if (valueRequired) {
                        codeStream.iconst_1();
                        codeStream.ixor(); // negate
                        codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                    }
                } else {
                    // x ^ <something equivalent to false>
                    this.left.generateCode(currentScope, codeStream, valueRequired);
                    this.right.generateCode(currentScope, codeStream, false);
                }
                return;
            }
        }
        // default case
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            codeStream.ixor();
        }
        codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
    }

    /**
     * Boolean generation for &
     */
    public void generateOptimizedLogicalAnd(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
            BranchLabel falseLabel, boolean valueRequired) {
        Constant condConst;
        if ((this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK) == TypeIds.T_boolean) {
            if ((condConst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // <something equivalent to true> & x
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel,
                            valueRequired);
                } else {
                    // <something equivalent to false> & x
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    if (valueRequired) {
                        if (falseLabel != null) {
                            // implicit falling through the TRUE case
                            codeStream.goto_(falseLabel);
                        }
                    }
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                }
                return;
            }
            if ((condConst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // x & <something equivalent to true>
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel,
                            valueRequired);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                } else {
                    // x & <something equivalent to false>
                    BranchLabel internalTrueLabel = new BranchLabel(codeStream);
                    this.left.generateOptimizedBoolean(currentScope, codeStream, internalTrueLabel, falseLabel,
                            false);
                    internalTrueLabel.place();
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    if (valueRequired) {
                        if (falseLabel != null) {
                            // implicit falling through the TRUE case
                            codeStream.goto_(falseLabel);
                        }
                    }
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                }
                return;
            }
        }
        // default case
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            codeStream.iand();
            if (falseLabel == null) {
                if (trueLabel != null) {
                    // implicit falling through the FALSE case
                    codeStream.ifne(trueLabel);
                }
            } else {
                // implicit falling through the TRUE case
                if (trueLabel == null) {
                    codeStream.ifeq(falseLabel);
                } else {
                    // no implicit fall through TRUE/FALSE --> should never occur
                }
            }
        }
        codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
    }

    /**
     * Boolean generation for |
     */
    public void generateOptimizedLogicalOr(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
            BranchLabel falseLabel, boolean valueRequired) {
        Constant condConst;
        if ((this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK) == TypeIds.T_boolean) {
            if ((condConst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // <something equivalent to true> | x
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    BranchLabel internalFalseLabel = new BranchLabel(codeStream);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, internalFalseLabel,
                            false);
                    internalFalseLabel.place();
                    if (valueRequired) {
                        if (trueLabel != null) {
                            codeStream.goto_(trueLabel);
                        }
                    }
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                } else {
                    // <something equivalent to false> | x
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel,
                            valueRequired);
                }
                return;
            }
            if ((condConst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // x | <something equivalent to true>
                    BranchLabel internalFalseLabel = new BranchLabel(codeStream);
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, internalFalseLabel,
                            false);
                    internalFalseLabel.place();
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    if (valueRequired) {
                        if (trueLabel != null) {
                            codeStream.goto_(trueLabel);
                        }
                    }
                    codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
                } else {
                    // x | <something equivalent to false>
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel,
                            valueRequired);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                }
                return;
            }
        }
        // default case
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            codeStream.ior();
            if (falseLabel == null) {
                if (trueLabel != null) {
                    // implicit falling through the FALSE case
                    codeStream.ifne(trueLabel);
                }
            } else {
                // implicit falling through the TRUE case
                if (trueLabel == null) {
                    codeStream.ifeq(falseLabel);
                } else {
                    // no implicit fall through TRUE/FALSE --> should never occur
                }
            }
        }
        codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
    }

    /**
     * Boolean generation for ^
     */
    public void generateOptimizedLogicalXor(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel,
            BranchLabel falseLabel, boolean valueRequired) {
        Constant condConst;
        if ((this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK) == TypeIds.T_boolean) {
            if ((condConst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // <something equivalent to true> ^ x
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, falseLabel, // negating
                            trueLabel, valueRequired);
                } else {
                    // <something equivalent to false> ^ x
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel,
                            valueRequired);
                }
                return;
            }
            if ((condConst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (condConst.booleanValue() == true) {
                    // x ^ <something equivalent to true>
                    this.left.generateOptimizedBoolean(currentScope, codeStream, falseLabel, // negating
                            trueLabel, valueRequired);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                } else {
                    // x ^ <something equivalent to false>
                    this.left.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel,
                            valueRequired);
                    this.right.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, false);
                }
                return;
            }
        }
        // default case
        this.left.generateCode(currentScope, codeStream, valueRequired);
        this.right.generateCode(currentScope, codeStream, valueRequired);
        if (valueRequired) {
            codeStream.ixor();
            if (falseLabel == null) {
                if (trueLabel != null) {
                    // implicit falling through the FALSE case
                    codeStream.ifne(trueLabel);
                }
            } else {
                // implicit falling through the TRUE case
                if (trueLabel == null) {
                    codeStream.ifeq(falseLabel);
                } else {
                    // no implicit fall through TRUE/FALSE --> should never occur
                }
            }
        }
        codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
    }

    @Override
    public void generateOptimizedStringConcatenation(BlockScope blockScope, CodeStream codeStream, int typeID) {
        // keep implementation in sync with CombinedBinaryExpression
        // #generateOptimizedStringConcatenation
        /* In the case trying to make a string concatenation, there is no need to create a new
         * string buffer, thus use a lower-level API for code generation involving only the
         * appending of arguments to the existing StringBuffer
         */

        if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS)
                && ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) {
            if (this.constant != Constant.NotAConstant) {
                codeStream.generateConstant(this.constant, this.implicitConversion);
                codeStream.invokeStringConcatenationAppendForType(
                        this.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
            } else {
                int pc = codeStream.position;
                this.left.generateOptimizedStringConcatenation(blockScope, codeStream,
                        this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
                codeStream.recordPositionsFrom(pc, this.left.sourceStart);
                pc = codeStream.position;
                this.right.generateOptimizedStringConcatenation(blockScope, codeStream,
                        this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
                codeStream.recordPositionsFrom(pc, this.right.sourceStart);
            }
        } else {
            super.generateOptimizedStringConcatenation(blockScope, codeStream, typeID);
        }
    }

    @Override
    public void generateOptimizedStringConcatenationCreation(BlockScope blockScope, CodeStream codeStream,
            int typeID) {
        // keep implementation in sync with CombinedBinaryExpression
        // #generateOptimizedStringConcatenationCreation
        /* In the case trying to make a string concatenation, there is no need to create a new
         * string buffer, thus use a lower-level API for code generation involving only the
         * appending of arguments to the existing StringBuffer
         */
        if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS)
                && ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) {
            if (this.constant != Constant.NotAConstant) {
                codeStream.newStringContatenation(); // new: java.lang.StringBuffer
                codeStream.dup();
                codeStream.ldc(this.constant.stringValue());
                codeStream.invokeStringConcatenationStringConstructor();
                // invokespecial: java.lang.StringBuffer.<init>(Ljava.lang.String;)V
            } else {
                int pc = codeStream.position;
                this.left.generateOptimizedStringConcatenationCreation(blockScope, codeStream,
                        this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
                codeStream.recordPositionsFrom(pc, this.left.sourceStart);
                pc = codeStream.position;
                this.right.generateOptimizedStringConcatenation(blockScope, codeStream,
                        this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
                codeStream.recordPositionsFrom(pc, this.right.sourceStart);
            }
        } else {
            super.generateOptimizedStringConcatenationCreation(blockScope, codeStream, typeID);
        }
    }

    @Override
    public boolean isCompactableOperation() {
        return true;
    }

    /**
     * Separates into a reusable method the subpart of {@link
     * #resolveType(BlockScope)} that needs to be executed while climbing up the
     * chain of expressions of this' leftmost branch. For use by {@link
     * CombinedBinaryExpression#resolveType(BlockScope)}.
     * @param scope the scope within which the resolution occurs
     */
    void nonRecursiveResolveTypeUpwards(BlockScope scope) {
        // keep implementation in sync with BinaryExpression#resolveType
        boolean leftIsCast, rightIsCast;
        TypeBinding leftType = this.left.resolvedType;

        if ((rightIsCast = this.right instanceof CastExpression) == true) {
            this.right.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
        }
        TypeBinding rightType = this.right.resolveType(scope);

        // use the id of the type to navigate into the table
        if (leftType == null || rightType == null) {
            this.constant = Constant.NotAConstant;
            return;
        }

        int leftTypeID = leftType.id;
        int rightTypeID = rightType.id;

        // autoboxing support
        boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
        if (use15specifics) {
            if (!leftType.isBaseType() && rightTypeID != TypeIds.T_JavaLangString
                    && rightTypeID != TypeIds.T_null) {
                leftTypeID = scope.environment().computeBoxingType(leftType).id;
            }
            if (!rightType.isBaseType() && leftTypeID != TypeIds.T_JavaLangString && leftTypeID != TypeIds.T_null) {
                rightTypeID = scope.environment().computeBoxingType(rightType).id;
            }
        }
        if (leftTypeID > 15 || rightTypeID > 15) { // must convert String + Object || Object + String
            if (leftTypeID == TypeIds.T_JavaLangString) {
                rightTypeID = TypeIds.T_JavaLangObject;
            } else if (rightTypeID == TypeIds.T_JavaLangString) {
                leftTypeID = TypeIds.T_JavaLangObject;
            } else {
                this.constant = Constant.NotAConstant;
                scope.problemReporter().invalidOperator(this, leftType, rightType);
                return;
            }
        }
        if (((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS) {
            if (leftTypeID == TypeIds.T_JavaLangString) {
                this.left.computeConversion(scope, leftType, leftType);
                if (rightType.isArrayType()
                        && TypeBinding.equalsEquals(((ArrayBinding) rightType).elementsType(), TypeBinding.CHAR)) {
                    scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.right);
                }
            }
            if (rightTypeID == TypeIds.T_JavaLangString) {
                this.right.computeConversion(scope, rightType, rightType);
                if (leftType.isArrayType()
                        && TypeBinding.equalsEquals(((ArrayBinding) leftType).elementsType(), TypeBinding.CHAR)) {
                    scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.left);
                }
            }
        }

        // the code is an int
        // (cast)  left   Op (cast)  right --> result
        //  0000   0000       0000   0000      0000
        //  <<16   <<12       <<8    <<4       <<0

        // Don't test for result = 0. If it is zero, some more work is done.
        // On the one hand when it is not zero (correct code) we avoid doing the test
        int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
        int operatorSignature = OperatorExpression.OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID];

        this.left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F),
                leftType);
        this.right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F),
                rightType);
        this.bits |= operatorSignature & 0xF;
        switch (operatorSignature & 0xF) { // record the current ReturnTypeID
        // only switch on possible result type.....
        case T_boolean:
            this.resolvedType = TypeBinding.BOOLEAN;
            break;
        case T_byte:
            this.resolvedType = TypeBinding.BYTE;
            break;
        case T_char:
            this.resolvedType = TypeBinding.CHAR;
            break;
        case T_double:
            this.resolvedType = TypeBinding.DOUBLE;
            break;
        case T_float:
            this.resolvedType = TypeBinding.FLOAT;
            break;
        case T_int:
            this.resolvedType = TypeBinding.INT;
            break;
        case T_long:
            this.resolvedType = TypeBinding.LONG;
            break;
        case T_JavaLangString:
            this.resolvedType = scope.getJavaLangString();
            break;
        default: //error........
            this.constant = Constant.NotAConstant;
            scope.problemReporter().invalidOperator(this, leftType, rightType);
            return;
        }

        // check need for operand cast
        if ((leftIsCast = (this.left instanceof CastExpression)) == true || rightIsCast) {
            CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, this.left, leftTypeID,
                    leftIsCast, this.right, rightTypeID, rightIsCast);
        }
        // compute the constant when valid
        computeConstant(scope, leftTypeID, rightTypeID);
    }

    public void optimizedBooleanConstant(int leftId, int operator, int rightId) {
        switch (operator) {
        case AND:
            if ((leftId != TypeIds.T_boolean) || (rightId != TypeIds.T_boolean))
                return;
            //$FALL-THROUGH$
        case AND_AND:
            Constant cst;
            if ((cst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (cst.booleanValue() == false) { // left is equivalent to false
                    this.optimizedBooleanConstant = cst; // constant(false)
                    return;
                } else { //left is equivalent to true
                    if ((cst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                        this.optimizedBooleanConstant = cst;
                        // the conditional result is equivalent to the right conditional value
                    }
                    return;
                }
            }
            if ((cst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (cst.booleanValue() == false) { // right is equivalent to false
                    this.optimizedBooleanConstant = cst; // constant(false)
                }
            }
            return;
        case OR:
            if ((leftId != TypeIds.T_boolean) || (rightId != TypeIds.T_boolean))
                return;
            //$FALL-THROUGH$
        case OR_OR:
            if ((cst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (cst.booleanValue() == true) { // left is equivalent to true
                    this.optimizedBooleanConstant = cst; // constant(true)
                    return;
                } else { //left is equivalent to false
                    if ((cst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                        this.optimizedBooleanConstant = cst;
                    }
                    return;
                }
            }
            if ((cst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
                if (cst.booleanValue() == true) { // right is equivalent to true
                    this.optimizedBooleanConstant = cst; // constant(true)
                }
            }
        }
    }

    @Override
    public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
        // keep implementation in sync with
        // CombinedBinaryExpression#printExpressionNoParenthesis
        this.left.printExpression(indent, output).append(' ').append(operatorToString()).append(' ');
        return this.right.printExpression(0, output);
    }

    @Override
    public TypeBinding resolveType(BlockScope scope) {
        // keep implementation in sync with CombinedBinaryExpression#resolveType
        // and nonRecursiveResolveTypeUpwards
        boolean leftIsCast, rightIsCast;
        if ((leftIsCast = this.left instanceof CastExpression) == true)
            this.left.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
        TypeBinding leftType = this.left.resolveType(scope);

        if ((rightIsCast = this.right instanceof CastExpression) == true)
            this.right.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
        TypeBinding rightType = this.right.resolveType(scope);

        // use the id of the type to navigate into the table
        if (leftType == null || rightType == null) {
            this.constant = Constant.NotAConstant;
            return null;
        }

        int leftTypeID = leftType.id;
        int rightTypeID = rightType.id;

        // autoboxing support
        boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
        if (use15specifics) {
            if (!leftType.isBaseType() && rightTypeID != TypeIds.T_JavaLangString
                    && rightTypeID != TypeIds.T_null) {
                leftTypeID = scope.environment().computeBoxingType(leftType).id;
            }
            if (!rightType.isBaseType() && leftTypeID != TypeIds.T_JavaLangString && leftTypeID != TypeIds.T_null) {
                rightTypeID = scope.environment().computeBoxingType(rightType).id;
            }
        }
        if (leftTypeID > 15 || rightTypeID > 15) { // must convert String + Object || Object + String
            if (leftTypeID == TypeIds.T_JavaLangString) {
                rightTypeID = TypeIds.T_JavaLangObject;
            } else if (rightTypeID == TypeIds.T_JavaLangString) {
                leftTypeID = TypeIds.T_JavaLangObject;
            } else {
                this.constant = Constant.NotAConstant;
                scope.problemReporter().invalidOperator(this, leftType, rightType);
                return null;
            }
        }
        if (((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS) {
            if (leftTypeID == TypeIds.T_JavaLangString) {
                this.left.computeConversion(scope, leftType, leftType);
                if (rightType.isArrayType()
                        && TypeBinding.equalsEquals(((ArrayBinding) rightType).elementsType(), TypeBinding.CHAR)) {
                    scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.right);
                }
            }
            if (rightTypeID == TypeIds.T_JavaLangString) {
                this.right.computeConversion(scope, rightType, rightType);
                if (leftType.isArrayType()
                        && TypeBinding.equalsEquals(((ArrayBinding) leftType).elementsType(), TypeBinding.CHAR)) {
                    scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.left);
                }
            }
        }

        // the code is an int
        // (cast)  left   Op (cast)  right --> result
        //  0000   0000       0000   0000      0000
        //  <<16   <<12       <<8    <<4       <<0

        // Don't test for result = 0. If it is zero, some more work is done.
        // On the one hand when it is not zero (correct code) we avoid doing the test
        int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
        int operatorSignature = OperatorExpression.OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID];

        this.left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F),
                leftType);
        this.right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F),
                rightType);
        this.bits |= operatorSignature & 0xF;
        switch (operatorSignature & 0xF) { // record the current ReturnTypeID
        // only switch on possible result type.....
        case T_boolean:
            this.resolvedType = TypeBinding.BOOLEAN;
            break;
        case T_byte:
            this.resolvedType = TypeBinding.BYTE;
            break;
        case T_char:
            this.resolvedType = TypeBinding.CHAR;
            break;
        case T_double:
            this.resolvedType = TypeBinding.DOUBLE;
            break;
        case T_float:
            this.resolvedType = TypeBinding.FLOAT;
            break;
        case T_int:
            this.resolvedType = TypeBinding.INT;
            break;
        case T_long:
            this.resolvedType = TypeBinding.LONG;
            break;
        case T_JavaLangString:
            this.resolvedType = scope.getJavaLangString();
            break;
        default: //error........
            this.constant = Constant.NotAConstant;
            scope.problemReporter().invalidOperator(this, leftType, rightType);
            return null;
        }

        // check need for operand cast
        if (leftIsCast || rightIsCast) {
            CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, this.left, leftTypeID,
                    leftIsCast, this.right, rightTypeID, rightIsCast);
        }
        // compute the constant when valid
        computeConstant(scope, leftTypeID, rightTypeID);
        return this.resolvedType;
    }

    @Override
    public void traverse(ASTVisitor visitor, BlockScope scope) {
        if (visitor.visit(this, scope)) {
            this.left.traverse(visitor, scope);
            this.right.traverse(visitor, scope);
        }
        visitor.endVisit(this, scope);
    }
}