blob: bcd30772068b003b135f31caf6c92c541f305b54 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2013 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
* 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;
}
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 {
this.left.checkNPE(currentScope, flowContext, flowInfo);
flowInfo = this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) {
flowContext.expireNullCheckedFieldInfo();
}
this.right.checkNPE(currentScope, flowContext, flowInfo);
flowInfo = this.right.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
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);
}
}
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
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: <, <=, >, >=, &, |, ^
*/
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);
}
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);
}
}
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);
}
}
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() && ((ArrayBinding) rightType).elementsType() == TypeBinding.CHAR) {
scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.right);
}
}
if (rightTypeID == TypeIds.T_JavaLangString) {
this.right.computeConversion(scope, rightType, rightType);
if (leftType.isArrayType() && ((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)
}
}
}
}
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);
}
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() && ((ArrayBinding) rightType).elementsType() == TypeBinding.CHAR) {
scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.right);
}
}
if (rightTypeID == TypeIds.T_JavaLangString) {
this.right.computeConversion(scope, rightType, rightType);
if (leftType.isArrayType() && ((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;
}
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);
}
}