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