blob: cfb6a44120536690b81fd3355611a121fd47c611 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
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.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
public class BinaryExpression extends OperatorExpression {
public Expression left, right;
public Constant optimizedBooleanConstant;
public BinaryExpression(Expression left, Expression right, int operator) {
this.left = left;
this.right = right;
this.bits |= operator << OperatorSHIFT; // encode operator
this.sourceStart = left.sourceStart;
this.sourceEnd = right.sourceEnd;
}
public FlowInfo analyseCode(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo) {
return right
.analyseCode(
currentScope,
flowContext,
left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits())
.unconditionalInits();
}
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 & OperatorMASK) >> 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 & OperatorMASK) >> OperatorSHIFT,
rightId);
}
}
public Constant optimizedBooleanConstant() {
return this.optimizedBooleanConstant == null ? this.constant : this.optimizedBooleanConstant;
}
/**
* Code generation for a binary operation
*/
public void generateCode(
BlockScope currentScope,
CodeStream codeStream,
boolean valueRequired) {
int pc = codeStream.position;
Label falseLabel, endLabel;
if (constant != Constant.NotAConstant) {
if (valueRequired)
codeStream.generateConstant(constant, implicitConversion);
codeStream.recordPositionsFrom(pc, this.sourceStart);
return;
}
bits |= OnlyValueRequiredMASK;
switch ((bits & OperatorMASK) >> OperatorSHIFT) {
case PLUS :
switch (bits & ReturnTypeIDMASK) {
case T_JavaLangString :
codeStream.generateStringConcatenationAppend(currentScope, left, right);
if (!valueRequired)
codeStream.pop();
break;
case T_int :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.iadd();
break;
case T_long :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ladd();
break;
case T_double :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.dadd();
break;
case T_float :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.fadd();
break;
}
break;
case MINUS :
switch (bits & ReturnTypeIDMASK) {
case T_int :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.isub();
break;
case T_long :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lsub();
break;
case T_double :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.dsub();
break;
case T_float :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.fsub();
break;
}
break;
case MULTIPLY :
switch (bits & ReturnTypeIDMASK) {
case T_int :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.imul();
break;
case T_long :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lmul();
break;
case T_double :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.dmul();
break;
case T_float :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.fmul();
break;
}
break;
case DIVIDE :
switch (bits & ReturnTypeIDMASK) {
case T_int :
left.generateCode(currentScope, codeStream, true);
right.generateCode(currentScope, codeStream, true);
codeStream.idiv();
if (!valueRequired)
codeStream.pop();
break;
case T_long :
left.generateCode(currentScope, codeStream, true);
right.generateCode(currentScope, codeStream, true);
codeStream.ldiv();
if (!valueRequired)
codeStream.pop2();
break;
case T_double :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ddiv();
break;
case T_float :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.fdiv();
break;
}
break;
case REMAINDER :
switch (bits & ReturnTypeIDMASK) {
case T_int :
left.generateCode(currentScope, codeStream, true);
right.generateCode(currentScope, codeStream, true);
codeStream.irem();
if (!valueRequired)
codeStream.pop();
break;
case T_long :
left.generateCode(currentScope, codeStream, true);
right.generateCode(currentScope, codeStream, true);
codeStream.lrem();
if (!valueRequired)
codeStream.pop2();
break;
case T_double :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.drem();
break;
case T_float :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.frem();
break;
}
break;
case AND :
switch (bits & ReturnTypeIDMASK) {
case T_int :
// 0 & x
if ((left.constant != Constant.NotAConstant)
&& (left.constant.typeID() == T_int)
&& (left.constant.intValue() == 0)) {
right.generateCode(currentScope, codeStream, false);
if (valueRequired)
codeStream.iconst_0();
} else {
// x & 0
if ((right.constant != Constant.NotAConstant)
&& (right.constant.typeID() == T_int)
&& (right.constant.intValue() == 0)) {
left.generateCode(currentScope, codeStream, false);
if (valueRequired)
codeStream.iconst_0();
} else {
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.iand();
}
}
break;
case T_long :
// 0 & x
if ((left.constant != Constant.NotAConstant)
&& (left.constant.typeID() == T_long)
&& (left.constant.longValue() == 0L)) {
right.generateCode(currentScope, codeStream, false);
if (valueRequired)
codeStream.lconst_0();
} else {
// x & 0
if ((right.constant != Constant.NotAConstant)
&& (right.constant.typeID() == T_long)
&& (right.constant.longValue() == 0L)) {
left.generateCode(currentScope, codeStream, false);
if (valueRequired)
codeStream.lconst_0();
} else {
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.land();
}
}
break;
case T_boolean : // logical and
generateOptimizedLogicalAnd(
currentScope,
codeStream,
null,
(falseLabel = new Label(codeStream)),
valueRequired);
/* improving code gen for such a case: boolean b = i < 0 && false;
* since the label has never been used, we have the inlined value on the stack. */
if (falseLabel.hasForwardReferences()) {
if (valueRequired) {
codeStream.iconst_1();
if ((bits & ValueForReturnMASK) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new Label(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
} else {
falseLabel.place();
}
}
}
break;
case OR :
switch (bits & ReturnTypeIDMASK) {
case T_int :
// 0 | x
if ((left.constant != Constant.NotAConstant)
&& (left.constant.typeID() == T_int)
&& (left.constant.intValue() == 0)) {
right.generateCode(currentScope, codeStream, valueRequired);
} else {
// x | 0
if ((right.constant != Constant.NotAConstant)
&& (right.constant.typeID() == T_int)
&& (right.constant.intValue() == 0)) {
left.generateCode(currentScope, codeStream, valueRequired);
} else {
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ior();
}
}
break;
case T_long :
// 0 | x
if ((left.constant != Constant.NotAConstant)
&& (left.constant.typeID() == T_long)
&& (left.constant.longValue() == 0L)) {
right.generateCode(currentScope, codeStream, valueRequired);
} else {
// x | 0
if ((right.constant != Constant.NotAConstant)
&& (right.constant.typeID() == T_long)
&& (right.constant.longValue() == 0L)) {
left.generateCode(currentScope, codeStream, valueRequired);
} else {
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lor();
}
}
break;
case T_boolean : // logical or
generateOptimizedLogicalOr(
currentScope,
codeStream,
null,
(falseLabel = new Label(codeStream)),
valueRequired);
/* improving code gen for such a case: boolean b = i < 0 || true;
* since the label has never been used, we have the inlined value on the stack. */
if (falseLabel.hasForwardReferences()) {
if (valueRequired) {
codeStream.iconst_1();
if ((bits & ValueForReturnMASK) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new Label(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
} else {
falseLabel.place();
}
}
}
break;
case XOR :
switch (bits & ReturnTypeIDMASK) {
case T_int :
// 0 ^ x
if ((left.constant != Constant.NotAConstant)
&& (left.constant.typeID() == T_int)
&& (left.constant.intValue() == 0)) {
right.generateCode(currentScope, codeStream, valueRequired);
} else {
// x ^ 0
if ((right.constant != Constant.NotAConstant)
&& (right.constant.typeID() == T_int)
&& (right.constant.intValue() == 0)) {
left.generateCode(currentScope, codeStream, valueRequired);
} else {
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ixor();
}
}
break;
case T_long :
// 0 ^ x
if ((left.constant != Constant.NotAConstant)
&& (left.constant.typeID() == T_long)
&& (left.constant.longValue() == 0L)) {
right.generateCode(currentScope, codeStream, valueRequired);
} else {
// x ^ 0
if ((right.constant != Constant.NotAConstant)
&& (right.constant.typeID() == T_long)
&& (right.constant.longValue() == 0L)) {
left.generateCode(currentScope, codeStream, valueRequired);
} else {
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lxor();
}
}
break;
case T_boolean :
generateOptimizedLogicalXor(
currentScope,
codeStream,
null,
(falseLabel = new Label(codeStream)),
valueRequired);
/* improving code gen for such a case: boolean b = i < 0 ^ bool;
* since the label has never been used, we have the inlined value on the stack. */
if (falseLabel.hasForwardReferences()) {
if (valueRequired) {
codeStream.iconst_1();
if ((bits & ValueForReturnMASK) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new Label(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
} else {
falseLabel.place();
}
}
}
break;
case LEFT_SHIFT :
switch (bits & ReturnTypeIDMASK) {
case T_int :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ishl();
break;
case T_long :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lshl();
}
break;
case RIGHT_SHIFT :
switch (bits & ReturnTypeIDMASK) {
case T_int :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ishr();
break;
case T_long :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lshr();
}
break;
case UNSIGNED_RIGHT_SHIFT :
switch (bits & ReturnTypeIDMASK) {
case T_int :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.iushr();
break;
case T_long :
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lushr();
}
break;
case GREATER :
generateOptimizedGreaterThan(
currentScope,
codeStream,
null,
(falseLabel = new Label(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_1();
if ((bits & ValueForReturnMASK) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new Label(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
}
break;
case GREATER_EQUAL :
generateOptimizedGreaterThanOrEqual(
currentScope,
codeStream,
null,
(falseLabel = new Label(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_1();
if ((bits & ValueForReturnMASK) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new Label(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
}
break;
case LESS :
generateOptimizedLessThan(
currentScope,
codeStream,
null,
(falseLabel = new Label(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_1();
if ((bits & ValueForReturnMASK) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new Label(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
}
break;
case LESS_EQUAL :
generateOptimizedLessThanOrEqual(
currentScope,
codeStream,
null,
(falseLabel = new Label(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_1();
if ((bits & ValueForReturnMASK) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new Label(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
}
}
if (valueRequired) {
codeStream.generateImplicitConversion(implicitConversion);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
/**
* Boolean operator code generation
* Optimized operations are: <, <=, >, >=, &, |, ^
*/
public void generateOptimizedBoolean(
BlockScope currentScope,
CodeStream codeStream,
Label trueLabel,
Label falseLabel,
boolean valueRequired) {
if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean)) {
super.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
}
switch ((bits & OperatorMASK) >> 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,
Label trueLabel,
Label falseLabel,
boolean valueRequired) {
int promotedTypeID = (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4;
// both sides got promoted in the same way
if (promotedTypeID == T_int) {
// 0 > x
if ((left.constant != NotAConstant) && (left.constant.intValue() == 0)) {
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
}
// x > 0
if ((right.constant != NotAConstant) && (right.constant.intValue() == 0)) {
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
}
}
// default comparison
left.generateCode(currentScope, codeStream, valueRequired);
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.updateLastRecordedEndPC(currentScope, codeStream.position);
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
}
/**
* Boolean generation for >=
*/
public void generateOptimizedGreaterThanOrEqual(
BlockScope currentScope,
CodeStream codeStream,
Label trueLabel,
Label falseLabel,
boolean valueRequired) {
int promotedTypeID = (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4;
// both sides got promoted in the same way
if (promotedTypeID == T_int) {
// 0 >= x
if ((left.constant != NotAConstant) && (left.constant.intValue() == 0)) {
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
}
// x >= 0
if ((right.constant != NotAConstant) && (right.constant.intValue() == 0)) {
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
}
}
// default comparison
left.generateCode(currentScope, codeStream, valueRequired);
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.updateLastRecordedEndPC(currentScope, codeStream.position);
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
}
/**
* Boolean generation for <
*/
public void generateOptimizedLessThan(
BlockScope currentScope,
CodeStream codeStream,
Label trueLabel,
Label falseLabel,
boolean valueRequired) {
int promotedTypeID = (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4;
// both sides got promoted in the same way
if (promotedTypeID == T_int) {
// 0 < x
if ((left.constant != NotAConstant) && (left.constant.intValue() == 0)) {
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
}
// x < 0
if ((right.constant != NotAConstant) && (right.constant.intValue() == 0)) {
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
}
}
// default comparison
left.generateCode(currentScope, codeStream, valueRequired);
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.updateLastRecordedEndPC(currentScope, codeStream.position);
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
}
/**
* Boolean generation for <=
*/
public void generateOptimizedLessThanOrEqual(
BlockScope currentScope,
CodeStream codeStream,
Label trueLabel,
Label falseLabel,
boolean valueRequired) {
int promotedTypeID = (left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4;
// both sides got promoted in the same way
if (promotedTypeID == T_int) {
// 0 <= x
if ((left.constant != NotAConstant) && (left.constant.intValue() == 0)) {
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
}
// x <= 0
if ((right.constant != NotAConstant) && (right.constant.intValue() == 0)) {
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
}
}
// default comparison
left.generateCode(currentScope, codeStream, valueRequired);
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.updateLastRecordedEndPC(currentScope, codeStream.position);
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.updateLastRecordedEndPC(currentScope, codeStream.position);
return;
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
}
/**
* Boolean generation for &
*/
public void generateOptimizedLogicalAnd(
BlockScope currentScope,
CodeStream codeStream,
Label trueLabel,
Label falseLabel,
boolean valueRequired) {
Constant condConst;
if ((left.implicitConversion & COMPILE_TYPE_MASK) == T_boolean) {
if ((condConst = left.optimizedBooleanConstant()) != NotAConstant) {
if (condConst.booleanValue() == true) {
// <something equivalent to true> & x
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
if ((bits & OnlyValueRequiredMASK) != 0) {
right.generateCode(currentScope, codeStream, valueRequired);
} else {
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
}
} else {
// <something equivalent to false> & x
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
Label internalTrueLabel = new Label(codeStream);
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
internalTrueLabel.place();
if (valueRequired) {
if ((bits & OnlyValueRequiredMASK) != 0) {
codeStream.iconst_0();
} else {
if (falseLabel != null) {
// implicit falling through the TRUE case
codeStream.goto_(falseLabel);
}
}
}
// reposition the endPC
codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
}
return;
}
if ((condConst = right.optimizedBooleanConstant()) != NotAConstant) {
if (condConst.booleanValue() == true) {
// x & <something equivalent to true>
if ((bits & OnlyValueRequiredMASK) != 0) {
left.generateCode(currentScope, codeStream, valueRequired);
} else {
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
}
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
} else {
// x & <something equivalent to false>
Label internalTrueLabel = new Label(codeStream);
left.generateOptimizedBoolean(
currentScope,
codeStream,
internalTrueLabel,
falseLabel,
false);
internalTrueLabel.place();
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
if (valueRequired) {
if ((bits & OnlyValueRequiredMASK) != 0) {
codeStream.iconst_0();
} else {
if (falseLabel != null) {
// implicit falling through the TRUE case
codeStream.goto_(falseLabel);
}
}
}
// reposition the endPC
codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
}
return;
}
}
// default case
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
codeStream.iand();
if ((bits & OnlyValueRequiredMASK) == 0) {
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
}
}
}
}
// reposition the endPC
codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
}
/**
* Boolean generation for |
*/
public void generateOptimizedLogicalOr(
BlockScope currentScope,
CodeStream codeStream,
Label trueLabel,
Label falseLabel,
boolean valueRequired) {
Constant condConst;
if ((left.implicitConversion & COMPILE_TYPE_MASK) == T_boolean) {
if ((condConst = left.optimizedBooleanConstant()) != NotAConstant) {
if (condConst.booleanValue() == true) {
// <something equivalent to true> | x
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
Label internalFalseLabel = new Label(codeStream);
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
internalFalseLabel,
false);
internalFalseLabel.place();
if (valueRequired) {
if ((bits & OnlyValueRequiredMASK) != 0) {
codeStream.iconst_1();
} else {
if (trueLabel != null) {
codeStream.goto_(trueLabel);
}
}
}
// reposition the endPC
codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
} else {
// <something equivalent to false> | x
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
if ((bits & OnlyValueRequiredMASK) != 0) {
right.generateCode(currentScope, codeStream, valueRequired);
} else {
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
}
}
return;
}
if ((condConst = right.optimizedBooleanConstant()) != NotAConstant) {
if (condConst.booleanValue() == true) {
// x | <something equivalent to true>
Label internalFalseLabel = new Label(codeStream);
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
internalFalseLabel,
false);
internalFalseLabel.place();
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
if (valueRequired) {
if ((bits & OnlyValueRequiredMASK) != 0) {
codeStream.iconst_1();
} else {
if (trueLabel != null) {
codeStream.goto_(trueLabel);
}
}
}
// reposition the endPC
codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
} else {
// x | <something equivalent to false>
if ((bits & OnlyValueRequiredMASK) != 0) {
left.generateCode(currentScope, codeStream, valueRequired);
} else {
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
}
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
}
return;
}
}
// default case
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
codeStream.ior();
if ((bits & OnlyValueRequiredMASK) == 0) {
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
}
}
}
}
// reposition the endPC
codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
}
/**
* Boolean generation for ^
*/
public void generateOptimizedLogicalXor(
BlockScope currentScope,
CodeStream codeStream,
Label trueLabel,
Label falseLabel,
boolean valueRequired) {
Constant condConst;
if ((left.implicitConversion & COMPILE_TYPE_MASK) == T_boolean) {
if ((condConst = left.optimizedBooleanConstant()) != NotAConstant) {
if (condConst.booleanValue() == true) {
// <something equivalent to true> ^ x
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
right.generateOptimizedBoolean(
currentScope,
codeStream,
falseLabel,
trueLabel,
valueRequired);
} else {
// <something equivalent to false> ^ x
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
if ((bits & OnlyValueRequiredMASK) != 0) {
right.generateCode(currentScope, codeStream, valueRequired);
} else {
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
}
}
return;
}
if ((condConst = right.optimizedBooleanConstant()) != NotAConstant) {
if (condConst.booleanValue() == true) {
// x ^ <something equivalent to true>
left.generateOptimizedBoolean(
currentScope,
codeStream,
falseLabel,
trueLabel,
valueRequired);
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
} else {
// x ^ <something equivalent to false>
if ((bits & OnlyValueRequiredMASK) != 0) {
left.generateCode(currentScope, codeStream, valueRequired);
} else {
left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
}
right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
}
return;
}
}
// default case
left.generateCode(currentScope, codeStream, valueRequired);
right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
codeStream.ixor();
if ((bits & OnlyValueRequiredMASK) == 0) {
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
}
}
}
}
// reposition the endPC
codeStream.updateLastRecordedEndPC(currentScope, codeStream.position);
}
public void generateOptimizedStringConcatenation(
BlockScope blockScope,
CodeStream codeStream,
int typeID) {
/* 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 ((((bits & OperatorMASK) >> OperatorSHIFT) == PLUS)
&& ((bits & ReturnTypeIDMASK) == T_JavaLangString)) {
if (constant != NotAConstant) {
codeStream.generateConstant(constant, implicitConversion);
codeStream.invokeStringConcatenationAppendForType(implicitConversion & COMPILE_TYPE_MASK);
} else {
int pc = codeStream.position;
left.generateOptimizedStringConcatenation(
blockScope,
codeStream,
left.implicitConversion & COMPILE_TYPE_MASK);
codeStream.recordPositionsFrom(pc, left.sourceStart);
pc = codeStream.position;
right.generateOptimizedStringConcatenation(
blockScope,
codeStream,
right.implicitConversion & COMPILE_TYPE_MASK);
codeStream.recordPositionsFrom(pc, right.sourceStart);
}
} else {
super.generateOptimizedStringConcatenation(blockScope, codeStream, typeID);
}
}
public void generateOptimizedStringConcatenationCreation(
BlockScope blockScope,
CodeStream codeStream,
int typeID) {
/* 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 ((((bits & OperatorMASK) >> OperatorSHIFT) == PLUS)
&& ((bits & ReturnTypeIDMASK) == T_JavaLangString)) {
if (constant != NotAConstant) {
codeStream.newStringContatenation(); // new: java.lang.StringBuffer
codeStream.dup();
codeStream.ldc(constant.stringValue());
codeStream.invokeStringConcatenationStringConstructor();
// invokespecial: java.lang.StringBuffer.<init>(Ljava.lang.String;)V
} else {
int pc = codeStream.position;
left.generateOptimizedStringConcatenationCreation(
blockScope,
codeStream,
left.implicitConversion & COMPILE_TYPE_MASK);
codeStream.recordPositionsFrom(pc, left.sourceStart);
pc = codeStream.position;
right.generateOptimizedStringConcatenation(
blockScope,
codeStream,
right.implicitConversion & COMPILE_TYPE_MASK);
codeStream.recordPositionsFrom(pc, right.sourceStart);
}
} else {
super.generateOptimizedStringConcatenationCreation(blockScope, codeStream, typeID);
}
}
public boolean isCompactableOperation() {
return true;
}
public void optimizedBooleanConstant(int leftId, int operator, int rightId) {
switch (operator) {
case AND :
if ((leftId != T_boolean) || (rightId != T_boolean))
return;
case AND_AND :
Constant cst;
if ((cst = left.optimizedBooleanConstant()) != NotAConstant) {
if (cst.booleanValue() == false) { // left is equivalent to false
optimizedBooleanConstant = cst; // constant(false)
return;
} else { //left is equivalent to true
if ((cst = right.optimizedBooleanConstant()) != NotAConstant) {
optimizedBooleanConstant = cst;
// the conditional result is equivalent to the right conditional value
}
return;
}
}
if ((cst = right.optimizedBooleanConstant()) != NotAConstant) {
if (cst.booleanValue() == false) { // right is equivalent to false
optimizedBooleanConstant = cst; // constant(false)
}
}
return;
case OR :
if ((leftId != T_boolean) || (rightId != T_boolean))
return;
case OR_OR :
if ((cst = left.optimizedBooleanConstant()) != NotAConstant) {
if (cst.booleanValue() == true) { // left is equivalent to true
optimizedBooleanConstant = cst; // constant(true)
return;
} else { //left is equivalent to false
if ((cst = right.optimizedBooleanConstant()) != NotAConstant) {
optimizedBooleanConstant = cst;
}
return;
}
}
if ((cst = right.optimizedBooleanConstant()) != NotAConstant) {
if (cst.booleanValue() == true) { // right is equivalent to true
optimizedBooleanConstant = cst; // constant(true)
}
}
}
}
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
left.printExpression(indent, output).append(' ').append(operatorToString()).append(' ');
return right.printExpression(0, output);
}
public TypeBinding resolveType(BlockScope scope) {
boolean leftIsCast, rightIsCast;
if ((leftIsCast = left instanceof CastExpression) == true) left.bits |= IgnoreNeedForCastCheckMASK; // will check later on
TypeBinding leftType = left.resolveType(scope);
if ((rightIsCast = right instanceof CastExpression) == true) right.bits |= IgnoreNeedForCastCheckMASK; // will check later on
TypeBinding rightType = right.resolveType(scope);
// use the id of the type to navigate into the table
if (leftType == null || rightType == null) {
constant = Constant.NotAConstant;
return null;
}
int leftTypeID = leftType.id;
int rightTypeID = rightType.id;
// autoboxing support
LookupEnvironment env = scope.environment();
boolean use15specifics = env.options.sourceLevel >= JDK1_5;
if (use15specifics) {
if (!leftType.isBaseType() && rightTypeID != T_JavaLangString && rightTypeID != T_null) {
leftTypeID = env.computeBoxingType(leftType).id;
}
if (!rightType.isBaseType() && leftTypeID != T_JavaLangString && leftTypeID != T_null) {
rightTypeID = env.computeBoxingType(rightType).id;
}
}
if (leftTypeID > 15
|| rightTypeID > 15) { // must convert String + Object || Object + String
if (leftTypeID == T_JavaLangString) {
rightTypeID = T_JavaLangObject;
} else if (rightTypeID == T_JavaLangString) {
leftTypeID = T_JavaLangObject;
} else {
constant = Constant.NotAConstant;
scope.problemReporter().invalidOperator(this, leftType, rightType);
return null;
}
}
if (((bits & OperatorMASK) >> OperatorSHIFT) == PLUS) {
if (leftTypeID == T_JavaLangString) {
this.left.computeConversion(scope, leftType, leftType);
if (rightType.isArrayType() && ((ArrayBinding) rightType).elementsType() == CharBinding) {
scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(right);
}
}
if (rightTypeID == T_JavaLangString) {
this.right.computeConversion(scope, rightType, rightType);
if (leftType.isArrayType() && ((ArrayBinding) leftType).elementsType() == CharBinding) {
scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(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 = (bits & OperatorMASK) >> OperatorSHIFT;
int operatorSignature = OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID];
left.computeConversion( scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), leftType);
right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), rightType);
bits |= operatorSignature & 0xF;
switch (operatorSignature & 0xF) { // record the current ReturnTypeID
// only switch on possible result type.....
case T_boolean :
this.resolvedType = BooleanBinding;
break;
case T_byte :
this.resolvedType = ByteBinding;
break;
case T_char :
this.resolvedType = CharBinding;
break;
case T_double :
this.resolvedType = DoubleBinding;
break;
case T_float :
this.resolvedType = FloatBinding;
break;
case T_int :
this.resolvedType = IntBinding;
break;
case T_long :
this.resolvedType = LongBinding;
break;
case T_JavaLangString :
this.resolvedType = scope.getJavaLangString();
break;
default : //error........
constant = Constant.NotAConstant;
scope.problemReporter().invalidOperator(this, leftType, rightType);
return null;
}
// check need for operand cast
if (leftIsCast || rightIsCast) {
CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, left, leftTypeID, leftIsCast, right, rightTypeID, rightIsCast);
}
// compute the constant when valid
computeConstant(scope, leftTypeID, rightTypeID);
return this.resolvedType;
}
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
left.traverse(visitor, scope);
right.traverse(visitor, scope);
}
visitor.endVisit(this, scope);
}
}