Java tutorial
/******************************************************************************* * 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); } }