org.eclipse.jdt.internal.compiler.ast.BinaryExpression Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2000, 2020 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contribution for
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 383368 - [compiler][null] syntactic null analysis for field references
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
public class BinaryExpression extends OperatorExpression {
/* Tracking helpers
* The following are used to elaborate realistic statistics about binary
* expressions. This must be neutralized in the released code.
* Search the keyword BE_INSTRUMENTATION to reenable.
* An external device must install a suitable probe so as to monitor the
* emission of events and publish the results.
public interface Probe {
public void ping(int depth);
}
public int depthTracker;
public static Probe probe;
*/
public Expression left, right;
public Constant optimizedBooleanConstant;
public BinaryExpression(Expression left, Expression right, int operator) {
this.left = left;
this.right = right;
this.bits |= operator << ASTNode.OperatorSHIFT; // encode operator
this.sourceStart = left.sourceStart;
this.sourceEnd = right.sourceEnd;
// BE_INSTRUMENTATION: neutralized in the released code
// if (left instanceof BinaryExpression &&
// ((left.bits & OperatorMASK) ^ (this.bits & OperatorMASK)) == 0) {
// this.depthTracker = ((BinaryExpression)left).depthTracker + 1;
// } else {
// this.depthTracker = 1;
// }
}
public BinaryExpression(BinaryExpression expression) {
this.left = expression.left;
this.right = expression.right;
this.bits = expression.bits;
this.sourceStart = expression.sourceStart;
this.sourceEnd = expression.sourceEnd;
}
@Override
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
// keep implementation in sync with CombinedBinaryExpression#analyseCode
try {
if (this.resolvedType.id == TypeIds.T_JavaLangString) {
return this.right.analyseCode(
currentScope, flowContext,
this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits())
.unconditionalInits();
} else {
flowInfo = this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
this.left.checkNPE(currentScope, flowContext, flowInfo);
if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) {
flowContext.expireNullCheckedFieldInfo();
}
flowInfo = this.right.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
this.right.checkNPE(currentScope, flowContext, flowInfo);
if (((this.bits & OperatorMASK) >> OperatorSHIFT) != AND) {
flowContext.expireNullCheckedFieldInfo();
}
return flowInfo;
}
} finally {
// account for exception possibly thrown by arithmetics
flowContext.recordAbruptExit();
}
}
@Override
protected void updateFlowOnBooleanResult(FlowInfo flowInfo, boolean result) {
int operator = (this.bits & OperatorMASK) >> OperatorSHIFT;
if (result ? operator == AND_AND : operator == OR_OR) {
this.left.updateFlowOnBooleanResult(flowInfo, result);
this.right.updateFlowOnBooleanResult(flowInfo, result);
}
}
public void computeConstant(BlockScope scope, int leftId, int rightId) {
//compute the constant when valid
if ((this.left.constant != Constant.NotAConstant)
&& (this.right.constant != Constant.NotAConstant)) {
try {
this.constant =
Constant.computeConstantOperation(
this.left.constant,
leftId,
(this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT,
this.right.constant,
rightId);
} catch (ArithmeticException e) {
this.constant = Constant.NotAConstant;
// 1.2 no longer throws an exception at compile-time
//scope.problemReporter().compileTimeConstantThrowsArithmeticException(this);
}
} else {
this.constant = Constant.NotAConstant;
//add some work for the boolean operators & |
this.optimizedBooleanConstant(
leftId,
(this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT,
rightId);
}
}
@Override
public Constant optimizedBooleanConstant() {
return this.optimizedBooleanConstant == null ? this.constant : this.optimizedBooleanConstant;
}
/**
* Code generation for a binary operation
*/
// given the current focus of CombinedBinaryExpression on strings concatenation,
// we do not provide a general, non-recursive implementation of generateCode,
// but rely upon generateOptimizedStringConcatenationCreation instead
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
int pc = codeStream.position;
if (this.constant != Constant.NotAConstant) {
if (valueRequired)
codeStream.generateConstant(this.constant, this.implicitConversion);
codeStream.recordPositionsFrom(pc, this.sourceStart);
return;
}
switch ((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) {
case PLUS :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_JavaLangString :
// BE_INSTRUMENTATION: neutralized in the released code
// if (probe != null) {
// probe.ping(this.depthTracker);
// }
codeStream.generateStringConcatenationAppend(currentScope, this.left, this.right);
if (!valueRequired)
codeStream.pop();
break;
case T_int :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.iadd();
break;
case T_long :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ladd();
break;
case T_double :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.dadd();
break;
case T_float :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.fadd();
break;
}
break;
case MINUS :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.isub();
break;
case T_long :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lsub();
break;
case T_double :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.dsub();
break;
case T_float :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.fsub();
break;
}
break;
case MULTIPLY :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.imul();
break;
case T_long :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lmul();
break;
case T_double :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.dmul();
break;
case T_float :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.fmul();
break;
}
break;
case DIVIDE :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
this.left.generateCode(currentScope, codeStream, true);
this.right.generateCode(currentScope, codeStream, true);
codeStream.idiv();
if (!valueRequired)
codeStream.pop();
break;
case T_long :
this.left.generateCode(currentScope, codeStream, true);
this.right.generateCode(currentScope, codeStream, true);
codeStream.ldiv();
if (!valueRequired)
codeStream.pop2();
break;
case T_double :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ddiv();
break;
case T_float :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.fdiv();
break;
}
break;
case REMAINDER :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
this.left.generateCode(currentScope, codeStream, true);
this.right.generateCode(currentScope, codeStream, true);
codeStream.irem();
if (!valueRequired)
codeStream.pop();
break;
case T_long :
this.left.generateCode(currentScope, codeStream, true);
this.right.generateCode(currentScope, codeStream, true);
codeStream.lrem();
if (!valueRequired)
codeStream.pop2();
break;
case T_double :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.drem();
break;
case T_float :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.frem();
break;
}
break;
case AND :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
// 0 & x
if ((this.left.constant != Constant.NotAConstant)
&& (this.left.constant.typeID() == TypeIds.T_int)
&& (this.left.constant.intValue() == 0)) {
this.right.generateCode(currentScope, codeStream, false);
if (valueRequired)
codeStream.iconst_0();
} else {
// x & 0
if ((this.right.constant != Constant.NotAConstant)
&& (this.right.constant.typeID() == TypeIds.T_int)
&& (this.right.constant.intValue() == 0)) {
this.left.generateCode(currentScope, codeStream, false);
if (valueRequired)
codeStream.iconst_0();
} else {
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.iand();
}
}
break;
case T_long :
// 0 & x
if ((this.left.constant != Constant.NotAConstant)
&& (this.left.constant.typeID() == TypeIds.T_long)
&& (this.left.constant.longValue() == 0L)) {
this.right.generateCode(currentScope, codeStream, false);
if (valueRequired)
codeStream.lconst_0();
} else {
// x & 0
if ((this.right.constant != Constant.NotAConstant)
&& (this.right.constant.typeID() == TypeIds.T_long)
&& (this.right.constant.longValue() == 0L)) {
this.left.generateCode(currentScope, codeStream, false);
if (valueRequired)
codeStream.lconst_0();
} else {
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.land();
}
}
break;
case T_boolean : // logical and
generateLogicalAnd(currentScope, codeStream, valueRequired);
break;
}
break;
case OR :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
// 0 | x
if ((this.left.constant != Constant.NotAConstant)
&& (this.left.constant.typeID() == TypeIds.T_int)
&& (this.left.constant.intValue() == 0)) {
this.right.generateCode(currentScope, codeStream, valueRequired);
} else {
// x | 0
if ((this.right.constant != Constant.NotAConstant)
&& (this.right.constant.typeID() == TypeIds.T_int)
&& (this.right.constant.intValue() == 0)) {
this.left.generateCode(currentScope, codeStream, valueRequired);
} else {
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ior();
}
}
break;
case T_long :
// 0 | x
if ((this.left.constant != Constant.NotAConstant)
&& (this.left.constant.typeID() == TypeIds.T_long)
&& (this.left.constant.longValue() == 0L)) {
this.right.generateCode(currentScope, codeStream, valueRequired);
} else {
// x | 0
if ((this.right.constant != Constant.NotAConstant)
&& (this.right.constant.typeID() == TypeIds.T_long)
&& (this.right.constant.longValue() == 0L)) {
this.left.generateCode(currentScope, codeStream, valueRequired);
} else {
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lor();
}
}
break;
case T_boolean : // logical or
generateLogicalOr(currentScope, codeStream, valueRequired);
break;
}
break;
case XOR :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
// 0 ^ x
if ((this.left.constant != Constant.NotAConstant)
&& (this.left.constant.typeID() == TypeIds.T_int)
&& (this.left.constant.intValue() == 0)) {
this.right.generateCode(currentScope, codeStream, valueRequired);
} else {
// x ^ 0
if ((this.right.constant != Constant.NotAConstant)
&& (this.right.constant.typeID() == TypeIds.T_int)
&& (this.right.constant.intValue() == 0)) {
this.left.generateCode(currentScope, codeStream, valueRequired);
} else {
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ixor();
}
}
break;
case T_long :
// 0 ^ x
if ((this.left.constant != Constant.NotAConstant)
&& (this.left.constant.typeID() == TypeIds.T_long)
&& (this.left.constant.longValue() == 0L)) {
this.right.generateCode(currentScope, codeStream, valueRequired);
} else {
// x ^ 0
if ((this.right.constant != Constant.NotAConstant)
&& (this.right.constant.typeID() == TypeIds.T_long)
&& (this.right.constant.longValue() == 0L)) {
this.left.generateCode(currentScope, codeStream, valueRequired);
} else {
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lxor();
}
}
break;
case T_boolean :
generateLogicalXor(currentScope, codeStream, valueRequired);
break;
}
break;
case LEFT_SHIFT :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ishl();
break;
case T_long :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lshl();
}
break;
case RIGHT_SHIFT :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.ishr();
break;
case T_long :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lshr();
}
break;
case UNSIGNED_RIGHT_SHIFT :
switch (this.bits & ASTNode.ReturnTypeIDMASK) {
case T_int :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.iushr();
break;
case T_long :
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired)
codeStream.lushr();
}
break;
case GREATER :
BranchLabel falseLabel, endLabel;
generateOptimizedGreaterThan(
currentScope,
codeStream,
null,
(falseLabel = new BranchLabel(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_1();
if ((this.bits & ASTNode.IsReturnedValue) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new BranchLabel(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
}
break;
case GREATER_EQUAL :
generateOptimizedGreaterThanOrEqual(
currentScope,
codeStream,
null,
(falseLabel = new BranchLabel(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_1();
if ((this.bits & ASTNode.IsReturnedValue) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new BranchLabel(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
}
break;
case LESS :
generateOptimizedLessThan(
currentScope,
codeStream,
null,
(falseLabel = new BranchLabel(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_1();
if ((this.bits & ASTNode.IsReturnedValue) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new BranchLabel(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
}
break;
case LESS_EQUAL :
generateOptimizedLessThanOrEqual(
currentScope,
codeStream,
null,
(falseLabel = new BranchLabel(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_1();
if ((this.bits & ASTNode.IsReturnedValue) != 0) {
codeStream.generateImplicitConversion(this.implicitConversion);
codeStream.generateReturnBytecode(this);
falseLabel.place();
codeStream.iconst_0();
} else {
codeStream.goto_(endLabel = new BranchLabel(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_0();
endLabel.place();
}
}
}
if (valueRequired) {
codeStream.generateImplicitConversion(this.implicitConversion);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
/**
* Boolean operator code generation
* Optimized operations are: <, <=, >, >=, &, |, ^
*/
@Override
public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {
if ((this.constant != Constant.NotAConstant) && (this.constant.typeID() == TypeIds.T_boolean)) {
super.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
}
switch ((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) {
case LESS :
generateOptimizedLessThan(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
case LESS_EQUAL :
generateOptimizedLessThanOrEqual(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
case GREATER :
generateOptimizedGreaterThan(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
case GREATER_EQUAL :
generateOptimizedGreaterThanOrEqual(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
case AND :
generateOptimizedLogicalAnd(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
case OR :
generateOptimizedLogicalOr(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
case XOR :
generateOptimizedLogicalXor(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
}
super.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
}
/**
* Boolean generation for >
*/
public void generateOptimizedGreaterThan(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {
int promotedTypeID = (this.left.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
// both sides got promoted in the same way
if (promotedTypeID == TypeIds.T_int) {
// 0 > x
if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.intValue() == 0)) {
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicitly falling through the FALSE case
codeStream.iflt(trueLabel);
}
} else {
if (trueLabel == null) {
// implicitly falling through the TRUE case
codeStream.ifge(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
// x > 0
if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.intValue() == 0)) {
this.left.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicitly falling through the FALSE case
codeStream.ifgt(trueLabel);
}
} else {
if (trueLabel == null) {
// implicitly falling through the TRUE case
codeStream.ifle(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
}
// default comparison
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicit falling through the FALSE case
switch (promotedTypeID) {
case T_int :
codeStream.if_icmpgt(trueLabel);
break;
case T_float :
codeStream.fcmpl();
codeStream.ifgt(trueLabel);
break;
case T_long :
codeStream.lcmp();
codeStream.ifgt(trueLabel);
break;
case T_double :
codeStream.dcmpl();
codeStream.ifgt(trueLabel);
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
} else {
if (trueLabel == null) {
// implicit falling through the TRUE case
switch (promotedTypeID) {
case T_int :
codeStream.if_icmple(falseLabel);
break;
case T_float :
codeStream.fcmpl();
codeStream.ifle(falseLabel);
break;
case T_long :
codeStream.lcmp();
codeStream.ifle(falseLabel);
break;
case T_double :
codeStream.dcmpl();
codeStream.ifle(falseLabel);
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
}
/**
* Boolean generation for >=
*/
public void generateOptimizedGreaterThanOrEqual(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {
int promotedTypeID = (this.left.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
// both sides got promoted in the same way
if (promotedTypeID == TypeIds.T_int) {
// 0 >= x
if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.intValue() == 0)) {
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicitly falling through the FALSE case
codeStream.ifle(trueLabel);
}
} else {
if (trueLabel == null) {
// implicitly falling through the TRUE case
codeStream.ifgt(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
// x >= 0
if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.intValue() == 0)) {
this.left.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicitly falling through the FALSE case
codeStream.ifge(trueLabel);
}
} else {
if (trueLabel == null) {
// implicitly falling through the TRUE case
codeStream.iflt(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
}
// default comparison
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicit falling through the FALSE case
switch (promotedTypeID) {
case T_int :
codeStream.if_icmpge(trueLabel);
break;
case T_float :
codeStream.fcmpl();
codeStream.ifge(trueLabel);
break;
case T_long :
codeStream.lcmp();
codeStream.ifge(trueLabel);
break;
case T_double :
codeStream.dcmpl();
codeStream.ifge(trueLabel);
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
} else {
if (trueLabel == null) {
// implicit falling through the TRUE case
switch (promotedTypeID) {
case T_int :
codeStream.if_icmplt(falseLabel);
break;
case T_float :
codeStream.fcmpl();
codeStream.iflt(falseLabel);
break;
case T_long :
codeStream.lcmp();
codeStream.iflt(falseLabel);
break;
case T_double :
codeStream.dcmpl();
codeStream.iflt(falseLabel);
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
}
/**
* Boolean generation for <
*/
public void generateOptimizedLessThan(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {
int promotedTypeID = (this.left.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
// both sides got promoted in the same way
if (promotedTypeID == TypeIds.T_int) {
// 0 < x
if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.intValue() == 0)) {
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicitly falling through the FALSE case
codeStream.ifgt(trueLabel);
}
} else {
if (trueLabel == null) {
// implicitly falling through the TRUE case
codeStream.ifle(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
// x < 0
if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.intValue() == 0)) {
this.left.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicitly falling through the FALSE case
codeStream.iflt(trueLabel);
}
} else {
if (trueLabel == null) {
// implicitly falling through the TRUE case
codeStream.ifge(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
}
// default comparison
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicit falling through the FALSE case
switch (promotedTypeID) {
case T_int :
codeStream.if_icmplt(trueLabel);
break;
case T_float :
codeStream.fcmpg();
codeStream.iflt(trueLabel);
break;
case T_long :
codeStream.lcmp();
codeStream.iflt(trueLabel);
break;
case T_double :
codeStream.dcmpg();
codeStream.iflt(trueLabel);
}
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
} else {
if (trueLabel == null) {
// implicit falling through the TRUE case
switch (promotedTypeID) {
case T_int :
codeStream.if_icmpge(falseLabel);
break;
case T_float :
codeStream.fcmpg();
codeStream.ifge(falseLabel);
break;
case T_long :
codeStream.lcmp();
codeStream.ifge(falseLabel);
break;
case T_double :
codeStream.dcmpg();
codeStream.ifge(falseLabel);
}
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
}
/**
* Boolean generation for <=
*/
public void generateOptimizedLessThanOrEqual(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {
int promotedTypeID = (this.left.implicitConversion & TypeIds.IMPLICIT_CONVERSION_MASK) >> 4;
// both sides got promoted in the same way
if (promotedTypeID == TypeIds.T_int) {
// 0 <= x
if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.intValue() == 0)) {
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicitly falling through the FALSE case
codeStream.ifge(trueLabel);
}
} else {
if (trueLabel == null) {
// implicitly falling through the TRUE case
codeStream.iflt(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
// x <= 0
if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.intValue() == 0)) {
this.left.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicitly falling through the FALSE case
codeStream.ifle(trueLabel);
}
} else {
if (trueLabel == null) {
// implicitly falling through the TRUE case
codeStream.ifgt(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
}
// default comparison
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
if (falseLabel == null) {
if (trueLabel != null) {
// implicit falling through the FALSE case
switch (promotedTypeID) {
case T_int :
codeStream.if_icmple(trueLabel);
break;
case T_float :
codeStream.fcmpg();
codeStream.ifle(trueLabel);
break;
case T_long :
codeStream.lcmp();
codeStream.ifle(trueLabel);
break;
case T_double :
codeStream.dcmpg();
codeStream.ifle(trueLabel);
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
}
} else {
if (trueLabel == null) {
// implicit falling through the TRUE case
switch (promotedTypeID) {
case T_int :
codeStream.if_icmpgt(falseLabel);
break;
case T_float :
codeStream.fcmpg();
codeStream.ifgt(falseLabel);
break;
case T_long :
codeStream.lcmp();
codeStream.ifgt(falseLabel);
break;
case T_double :
codeStream.dcmpg();
codeStream.ifgt(falseLabel);
}
// reposition the endPC
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
return;
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
}
/**
* Boolean generation for &
*/
public void generateLogicalAnd(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
Constant condConst;
if ((this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK) == TypeIds.T_boolean) {
if ((condConst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
if (condConst.booleanValue() == true) {
// & x
this.left.generateCode(currentScope, codeStream, false);
this.right.generateCode(currentScope, codeStream, valueRequired);
} else {
// & 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 &
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, false);
} else {
// 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;
}
}
// 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) {
// | 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 {
// | 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 |
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 |
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) {
// ^ 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 {
// ^ 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 ^
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 ^
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) {
// & x
this.left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
this.right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
} else {
// & 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 &
this.left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
this.right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
} else {
// x &
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) {
// | 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 {
// | 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 |
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 |
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) {
// ^ x
this.left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
this.right.generateOptimizedBoolean(
currentScope,
codeStream,
falseLabel, // negating
trueLabel,
valueRequired);
} else {
// ^ 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 ^
this.left.generateOptimizedBoolean(
currentScope,
codeStream,
falseLabel, // negating
trueLabel,
valueRequired);
this.right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
} else {
// x ^
this.left.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
this.right.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
false);
}
return;
}
}
// default case
this.left.generateCode(currentScope, codeStream, valueRequired);
this.right.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
codeStream.ixor();
if (falseLabel == null) {
if (trueLabel != null) {
// implicit falling through the FALSE case
codeStream.ifne(trueLabel);
}
} else {
// implicit falling through the TRUE case
if (trueLabel == null) {
codeStream.ifeq(falseLabel);
} else {
// no implicit fall through TRUE/FALSE --> should never occur
}
}
}
codeStream.recordPositionsFrom(codeStream.position, this.sourceEnd);
}
@Override
public void generateOptimizedStringConcatenation(BlockScope blockScope, CodeStream codeStream, int typeID) {
// keep implementation in sync with CombinedBinaryExpression
// #generateOptimizedStringConcatenation
/* In the case trying to make a string concatenation, there is no need to create a new
* string buffer, thus use a lower-level API for code generation involving only the
* appending of arguments to the existing StringBuffer
*/
if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS)
&& ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) {
if (this.constant != Constant.NotAConstant) {
codeStream.generateConstant(this.constant, this.implicitConversion);
codeStream.invokeStringConcatenationAppendForType(this.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
} else {
int pc = codeStream.position;
this.left.generateOptimizedStringConcatenation(
blockScope,
codeStream,
this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
codeStream.recordPositionsFrom(pc, this.left.sourceStart);
pc = codeStream.position;
this.right.generateOptimizedStringConcatenation(
blockScope,
codeStream,
this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
codeStream.recordPositionsFrom(pc, this.right.sourceStart);
}
} else {
super.generateOptimizedStringConcatenation(blockScope, codeStream, typeID);
}
}
@Override
public void generateOptimizedStringConcatenationCreation(BlockScope blockScope, CodeStream codeStream, int typeID) {
// keep implementation in sync with CombinedBinaryExpression
// #generateOptimizedStringConcatenationCreation
/* In the case trying to make a string concatenation, there is no need to create a new
* string buffer, thus use a lower-level API for code generation involving only the
* appending of arguments to the existing StringBuffer
*/
if ((((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS)
&& ((this.bits & ASTNode.ReturnTypeIDMASK) == TypeIds.T_JavaLangString)) {
if (this.constant != Constant.NotAConstant) {
codeStream.newStringContatenation(); // new: java.lang.StringBuffer
codeStream.dup();
codeStream.ldc(this.constant.stringValue());
codeStream.invokeStringConcatenationStringConstructor();
// invokespecial: java.lang.StringBuffer.(Ljava.lang.String;)V
} else {
int pc = codeStream.position;
this.left.generateOptimizedStringConcatenationCreation(
blockScope,
codeStream,
this.left.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
codeStream.recordPositionsFrom(pc, this.left.sourceStart);
pc = codeStream.position;
this.right.generateOptimizedStringConcatenation(
blockScope,
codeStream,
this.right.implicitConversion & TypeIds.COMPILE_TYPE_MASK);
codeStream.recordPositionsFrom(pc, this.right.sourceStart);
}
} else {
super.generateOptimizedStringConcatenationCreation(blockScope, codeStream, typeID);
}
}
@Override
public boolean isCompactableOperation() {
return true;
}
/**
* Separates into a reusable method the subpart of {@link
* #resolveType(BlockScope)} that needs to be executed while climbing up the
* chain of expressions of this' leftmost branch. For use by {@link
* CombinedBinaryExpression#resolveType(BlockScope)}.
* @param scope the scope within which the resolution occurs
*/
void nonRecursiveResolveTypeUpwards(BlockScope scope) {
// keep implementation in sync with BinaryExpression#resolveType
boolean leftIsCast, rightIsCast;
TypeBinding leftType = this.left.resolvedType;
if ((rightIsCast = this.right instanceof CastExpression) == true) {
this.right.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
}
TypeBinding rightType = this.right.resolveType(scope);
// use the id of the type to navigate into the table
if (leftType == null || rightType == null) {
this.constant = Constant.NotAConstant;
return;
}
int leftTypeID = leftType.id;
int rightTypeID = rightType.id;
// autoboxing support
boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
if (use15specifics) {
if (!leftType.isBaseType() && rightTypeID != TypeIds.T_JavaLangString && rightTypeID != TypeIds.T_null) {
leftTypeID = scope.environment().computeBoxingType(leftType).id;
}
if (!rightType.isBaseType() && leftTypeID != TypeIds.T_JavaLangString && leftTypeID != TypeIds.T_null) {
rightTypeID = scope.environment().computeBoxingType(rightType).id;
}
}
if (leftTypeID > 15
|| rightTypeID > 15) { // must convert String + Object || Object + String
if (leftTypeID == TypeIds.T_JavaLangString) {
rightTypeID = TypeIds.T_JavaLangObject;
} else if (rightTypeID == TypeIds.T_JavaLangString) {
leftTypeID = TypeIds.T_JavaLangObject;
} else {
this.constant = Constant.NotAConstant;
scope.problemReporter().invalidOperator(this, leftType, rightType);
return;
}
}
if (((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS) {
if (leftTypeID == TypeIds.T_JavaLangString) {
this.left.computeConversion(scope, leftType, leftType);
if (rightType.isArrayType() && TypeBinding.equalsEquals(((ArrayBinding) rightType).elementsType(), TypeBinding.CHAR)) {
scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.right);
}
}
if (rightTypeID == TypeIds.T_JavaLangString) {
this.right.computeConversion(scope, rightType, rightType);
if (leftType.isArrayType() && TypeBinding.equalsEquals(((ArrayBinding) leftType).elementsType(), TypeBinding.CHAR)) {
scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.left);
}
}
}
// the code is an int
// (cast) left Op (cast) right --> result
// 0000 0000 0000 0000 0000
// <<16 <<12 <<8 <<4 <<0
// Don't test for result = 0. If it is zero, some more work is done.
// On the one hand when it is not zero (correct code) we avoid doing the test
int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
int operatorSignature = OperatorExpression.OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID];
this.left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), leftType);
this.right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), rightType);
this.bits |= operatorSignature & 0xF;
switch (operatorSignature & 0xF) { // record the current ReturnTypeID
// only switch on possible result type.....
case T_boolean :
this.resolvedType = TypeBinding.BOOLEAN;
break;
case T_byte :
this.resolvedType = TypeBinding.BYTE;
break;
case T_char :
this.resolvedType = TypeBinding.CHAR;
break;
case T_double :
this.resolvedType = TypeBinding.DOUBLE;
break;
case T_float :
this.resolvedType = TypeBinding.FLOAT;
break;
case T_int :
this.resolvedType = TypeBinding.INT;
break;
case T_long :
this.resolvedType = TypeBinding.LONG;
break;
case T_JavaLangString :
this.resolvedType = scope.getJavaLangString();
break;
default : //error........
this.constant = Constant.NotAConstant;
scope.problemReporter().invalidOperator(this, leftType, rightType);
return;
}
// check need for operand cast
if ((leftIsCast = (this.left instanceof CastExpression)) == true ||
rightIsCast) {
CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, this.left, leftTypeID, leftIsCast, this.right, rightTypeID, rightIsCast);
}
// compute the constant when valid
computeConstant(scope, leftTypeID, rightTypeID);
}
public void optimizedBooleanConstant(int leftId, int operator, int rightId) {
switch (operator) {
case AND :
if ((leftId != TypeIds.T_boolean) || (rightId != TypeIds.T_boolean))
return;
//$FALL-THROUGH$
case AND_AND :
Constant cst;
if ((cst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
if (cst.booleanValue() == false) { // left is equivalent to false
this.optimizedBooleanConstant = cst; // constant(false)
return;
} else { //left is equivalent to true
if ((cst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
this.optimizedBooleanConstant = cst;
// the conditional result is equivalent to the right conditional value
}
return;
}
}
if ((cst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
if (cst.booleanValue() == false) { // right is equivalent to false
this.optimizedBooleanConstant = cst; // constant(false)
}
}
return;
case OR :
if ((leftId != TypeIds.T_boolean) || (rightId != TypeIds.T_boolean))
return;
//$FALL-THROUGH$
case OR_OR :
if ((cst = this.left.optimizedBooleanConstant()) != Constant.NotAConstant) {
if (cst.booleanValue() == true) { // left is equivalent to true
this.optimizedBooleanConstant = cst; // constant(true)
return;
} else { //left is equivalent to false
if ((cst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
this.optimizedBooleanConstant = cst;
}
return;
}
}
if ((cst = this.right.optimizedBooleanConstant()) != Constant.NotAConstant) {
if (cst.booleanValue() == true) { // right is equivalent to true
this.optimizedBooleanConstant = cst; // constant(true)
}
}
}
}
@Override
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
// keep implementation in sync with
// CombinedBinaryExpression#printExpressionNoParenthesis
this.left.printExpression(indent, output).append(' ').append(operatorToString()).append(' ');
return this.right.printExpression(0, output);
}
@Override
public void addPatternVariables(BlockScope scope, CodeStream codeStream) {
this.left.addPatternVariables(scope, codeStream);
this.right.addPatternVariables(scope, codeStream);
}
@Override
public boolean containsPatternVariable() {
return this.left.containsPatternVariable() || this.right.containsPatternVariable();
}
@Override
public TypeBinding resolveType(BlockScope scope) {
// keep implementation in sync with CombinedBinaryExpression#resolveType
// and nonRecursiveResolveTypeUpwards
boolean leftIsCast, rightIsCast;
if ((leftIsCast = this.left instanceof CastExpression) == true) this.left.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
TypeBinding leftType = this.left.resolveType(scope);
if ((rightIsCast = this.right instanceof CastExpression) == true) this.right.bits |= ASTNode.DisableUnnecessaryCastCheck; // will check later on
TypeBinding rightType = this.right.resolveType(scope);
// use the id of the type to navigate into the table
if (leftType == null || rightType == null) {
this.constant = Constant.NotAConstant;
return null;
}
int leftTypeID = leftType.id;
int rightTypeID = rightType.id;
// autoboxing support
boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
if (use15specifics) {
if (!leftType.isBaseType() && rightTypeID != TypeIds.T_JavaLangString && rightTypeID != TypeIds.T_null) {
leftTypeID = scope.environment().computeBoxingType(leftType).id;
}
if (!rightType.isBaseType() && leftTypeID != TypeIds.T_JavaLangString && leftTypeID != TypeIds.T_null) {
rightTypeID = scope.environment().computeBoxingType(rightType).id;
}
}
if (leftTypeID > 15
|| rightTypeID > 15) { // must convert String + Object || Object + String
if (leftTypeID == TypeIds.T_JavaLangString) {
rightTypeID = TypeIds.T_JavaLangObject;
} else if (rightTypeID == TypeIds.T_JavaLangString) {
leftTypeID = TypeIds.T_JavaLangObject;
} else {
this.constant = Constant.NotAConstant;
scope.problemReporter().invalidOperator(this, leftType, rightType);
return null;
}
}
if (((this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT) == OperatorIds.PLUS) {
if (leftTypeID == TypeIds.T_JavaLangString) {
this.left.computeConversion(scope, leftType, leftType);
if (rightType.isArrayType() && TypeBinding.equalsEquals(((ArrayBinding) rightType).elementsType(), TypeBinding.CHAR)) {
scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.right);
}
}
if (rightTypeID == TypeIds.T_JavaLangString) {
this.right.computeConversion(scope, rightType, rightType);
if (leftType.isArrayType() && TypeBinding.equalsEquals(((ArrayBinding) leftType).elementsType(), TypeBinding.CHAR)) {
scope.problemReporter().signalNoImplicitStringConversionForCharArrayExpression(this.left);
}
}
}
// the code is an int
// (cast) left Op (cast) right --> result
// 0000 0000 0000 0000 0000
// <<16 <<12 <<8 <<4 <<0
// Don't test for result = 0. If it is zero, some more work is done.
// On the one hand when it is not zero (correct code) we avoid doing the test
int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
int operatorSignature = OperatorExpression.OperatorSignatures[operator][(leftTypeID << 4) + rightTypeID];
this.left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), leftType);
this.right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), rightType);
this.bits |= operatorSignature & 0xF;
switch (operatorSignature & 0xF) { // record the current ReturnTypeID
// only switch on possible result type.....
case T_boolean :
this.resolvedType = TypeBinding.BOOLEAN;
break;
case T_byte :
this.resolvedType = TypeBinding.BYTE;
break;
case T_char :
this.resolvedType = TypeBinding.CHAR;
break;
case T_double :
this.resolvedType = TypeBinding.DOUBLE;
break;
case T_float :
this.resolvedType = TypeBinding.FLOAT;
break;
case T_int :
this.resolvedType = TypeBinding.INT;
break;
case T_long :
this.resolvedType = TypeBinding.LONG;
break;
case T_JavaLangString :
this.resolvedType = scope.getJavaLangString();
break;
default : //error........
this.constant = Constant.NotAConstant;
scope.problemReporter().invalidOperator(this, leftType, rightType);
return null;
}
// check need for operand cast
if (leftIsCast || rightIsCast) {
CastExpression.checkNeedForArgumentCasts(scope, operator, operatorSignature, this.left, leftTypeID, leftIsCast, this.right, rightTypeID, rightIsCast);
}
// compute the constant when valid
computeConstant(scope, leftTypeID, rightTypeID);
return this.resolvedType;
}
@Override
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
this.left.traverse(visitor, scope);
this.right.traverse(visitor, scope);
}
visitor.endVisit(this, scope);
}
}