blob: 9944cd98cca3d8732f7c08469b3a4bc5e16e39d3 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.internal.compiler.ast;
import org.eclipse.wst.jsdt.core.ast.IASTNode;
import org.eclipse.wst.jsdt.core.ast.IBinaryExpression;
import org.eclipse.wst.jsdt.internal.compiler.ASTVisitor;
import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.wst.jsdt.internal.compiler.flow.FlowContext;
import org.eclipse.wst.jsdt.internal.compiler.flow.FlowInfo;
import org.eclipse.wst.jsdt.internal.compiler.impl.Constant;
import org.eclipse.wst.jsdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope;
import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeIds;
public class BinaryExpression extends OperatorExpression implements IBinaryExpression {
/* 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 FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
FlowInfo flowInfo) {
// keep implementation in sync with CombinedBinaryExpression#analyseCode
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();
this.right.checkNPE(currentScope, flowContext, flowInfo);
return this.right.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits();
}
}
public void computeConstant(BlockScope scope, int leftId, int rightId) {
//compute the constant when valid
/* bchilds - Not sure about this change but left side was null (variable) and causing NPE further down */
if(this.left.constant==null) this.left.constant= Constant.NotAConstant;
if(this.right.constant==null) this.left.constant= Constant.NotAConstant;
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 (Exception 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;
}
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;
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;
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;
this.resolvedType=TypeBinding.ANY;
return null;
}
int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
int leftTypeID = leftType.id;
int rightTypeID = rightType.id;
if(operator==OperatorIds.INSTANCEOF) {
if ( rightTypeID>15)
rightTypeID= TypeIds.T_JavaLangObject;
if ( leftTypeID>15)
leftTypeID= TypeIds.T_JavaLangObject;
}
// 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 (rightType.isArrayType())
{
rightType=rightType.leafComponentType();
rightTypeID=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 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 = scope.getJavaLangNumber();
break;
case T_long :
this.resolvedType = TypeBinding.LONG;
break;
case T_JavaLangString :
this.resolvedType = scope.getJavaLangString();
break;
case T_any:
this.resolvedType = TypeBinding.UNKNOWN;
break;
case T_function:
this.resolvedType = scope.getJavaLangFunction();
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);
}
public int getASTType() {
return IASTNode.BINARY_EXPRESSION;
}
}