/******************************************************************************* * Copyright (c) 2000, 2011 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.IEqualExpression; 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.flow.UnconditionalFlowInfo; import org.eclipse.wst.jsdt.internal.compiler.impl.BooleanConstant; import org.eclipse.wst.jsdt.internal.compiler.impl.Constant; import org.eclipse.wst.jsdt.internal.compiler.lookup.BlockScope; import org.eclipse.wst.jsdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.wst.jsdt.internal.compiler.lookup.TagBits; import org.eclipse.wst.jsdt.internal.compiler.lookup.TypeBinding; public class EqualExpression extends BinaryExpression implements IEqualExpression { public EqualExpression(Expression left, Expression right,int operator) { super(left,right,operator); } private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { LocalVariableBinding local = this.left.localVariableBinding(); if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, right.nullStatus(flowInfo), this.left); } local = this.right.localVariableBinding(); if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, left.nullStatus(flowInfo), this.right); } } private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) { switch (nullStatus) { case FlowInfo.NULL : if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { flowContext.recordUsingNullReference(scope, local, reference, FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NULL, flowInfo); initsWhenTrue.markAsComparedEqualToNull(local); // from thereon it is set initsWhenFalse.markAsComparedEqualToNonNull(local); // from thereon it is set } else { flowContext.recordUsingNullReference(scope, local, reference, FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NON_NULL, flowInfo); initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set initsWhenFalse.markAsComparedEqualToNull(local); // from thereon it is set } break; case FlowInfo.NON_NULL : if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { flowContext.recordUsingNullReference(scope, local, reference, FlowContext.CAN_ONLY_NULL | FlowContext.IN_COMPARISON_NON_NULL, flowInfo); initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set } else { flowContext.recordUsingNullReference(scope, local, reference, FlowContext.CAN_ONLY_NULL | FlowContext.IN_COMPARISON_NULL, flowInfo); } break; } // we do not impact enclosing try context because this kind of protection // does not preclude the variable from being null in an enclosing scope } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { FlowInfo result; if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) { if (left.constant.booleanValue()) { // true == anything // this is equivalent to the right argument inits result = right.analyseCode(currentScope, flowContext, flowInfo); } else { // false == anything // this is equivalent to the right argument inits negated result = right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } } else if (right.constant != null && (right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { if (right.constant.booleanValue()) { // anything == true // this is equivalent to the left argument inits result = left.analyseCode(currentScope, flowContext, flowInfo); } else { // anything == false // this is equivalent to the right argument inits negated result = left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } } else { result = right.analyseCode( currentScope, flowContext, left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits(); } } else { //NOT_EQUAL : if ((left.constant != Constant.NotAConstant) && (left.constant.typeID() == T_boolean)) { if (!left.constant.booleanValue()) { // false != anything // this is equivalent to the right argument inits result = right.analyseCode(currentScope, flowContext, flowInfo); } else { // true != anything // this is equivalent to the right argument inits negated result = right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } } else if ((right.constant != Constant.NotAConstant) && (right.constant.typeID() == T_boolean)) { if (!right.constant.booleanValue()) { // anything != false // this is equivalent to the right argument inits result = left.analyseCode(currentScope, flowContext, flowInfo); } else { // anything != true // this is equivalent to the right argument inits negated result = left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } } else { result = right.analyseCode( currentScope, flowContext, left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()). /* unneeded since we flatten it: asNegatedCondition(). */ unconditionalInits(); } } if (result instanceof UnconditionalFlowInfo && (result.tagBits & FlowInfo.UNREACHABLE) == 0) { // the flow info is flat result = FlowInfo.conditional(result.copy(), result.copy()); // TODO (maxime) check, reintroduced copy } checkNullComparison(currentScope, flowContext, result, result.initsWhenTrue(), result.initsWhenFalse()); return result; } public final void computeConstant(TypeBinding leftType, TypeBinding rightType) { if ((this.left.constant != Constant.NotAConstant) && (this.right.constant != Constant.NotAConstant)) { this.constant = Constant.computeConstantOperationEQUAL_EQUAL( left.constant, leftType.id, right.constant, rightType.id); if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL) constant = BooleanConstant.fromValue(!constant.booleanValue()); } else { this.constant = Constant.NotAConstant; // no optimization for null == null } } public boolean isCompactableOperation() { return false; } public TypeBinding resolveType(BlockScope scope) { constant = Constant.NotAConstant; TypeBinding originalLeftType = left.resolveType(scope); TypeBinding originalRightType = right.resolveType(scope); // always return BooleanBinding if (originalLeftType == null || originalRightType == null){ constant = Constant.NotAConstant; return TypeBinding.BOOLEAN; } // autoboxing support boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5; TypeBinding leftType = originalLeftType, rightType = originalRightType; if (use15specifics) { if (leftType != TypeBinding.NULL && leftType.isBaseType()) { if (!rightType.isBaseType()) { rightType = scope.environment().computeBoxingType(rightType); } } else { if (rightType != TypeBinding.NULL && rightType.isBaseType()) { leftType = scope.environment().computeBoxingType(leftType); } } } // both base type if (leftType.isAnyType() || rightType.isAnyType()) { return TypeBinding.BOOLEAN; } if ( leftType.isBasicType() && rightType.isBasicType()) { int leftTypeID = leftType.id; int rightTypeID = rightType.id; // the code is an int // (cast) left == (cast) right --> result // 0000 0000 0000 0000 0000 // <<16 <<12 <<8 <<4 <<0 int operatorSignature = OperatorSignatures[EQUAL_EQUAL][ (leftTypeID << 4) + rightTypeID]; bits |= operatorSignature & 0xF; // fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=283663 if ((operatorSignature & 0x0000F) == T_undefined) { constant = Constant.NotAConstant; //scope.problemReporter().invalidOperator(this, leftType, rightType); return TypeBinding.BOOLEAN; } computeConstant(leftType, rightType); return this.resolvedType = TypeBinding.BOOLEAN; } // Object references // spec 15.20.3 if ((!leftType.isBaseType() || leftType == TypeBinding.NULL) // cannot compare: Object == (int)0 && (!rightType.isBaseType() || rightType == TypeBinding.NULL) && (this.checkCastTypesCompatibility(scope, leftType, rightType, null) || this.checkCastTypesCompatibility(scope, rightType, leftType, null))) { // (special case for String) if ((rightType.id == T_JavaLangString) && (leftType.id == T_JavaLangString)) { computeConstant(leftType, rightType); } else { constant = Constant.NotAConstant; } return this.resolvedType = TypeBinding.BOOLEAN; } constant = Constant.NotAConstant; return null; } public void traverse(ASTVisitor visitor, BlockScope scope) { if (visitor.visit(this, scope)) { left.traverse(visitor, scope); right.traverse(visitor, scope); } visitor.endVisit(this, scope); } public int getASTType() { return IASTNode.EQUAL_EXPRESSION; } }