/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package net.sourceforge.phpdt.internal.compiler.ast; import net.sourceforge.phpdt.core.compiler.ITerminalSymbols.TokenName; import net.sourceforge.phpdt.internal.compiler.ASTVisitor; import net.sourceforge.phpdt.internal.compiler.flow.FlowContext; import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo; import net.sourceforge.phpdt.internal.compiler.impl.Constant; import net.sourceforge.phpdt.internal.compiler.lookup.ArrayBinding; import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope; import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding; import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding; import net.sourceforge.phpdt.internal.compiler.lookup.Scope; import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding; public class EqualExpression extends BinaryExpression { public EqualExpression(Expression left, Expression right, int operator) { super (left, right, operator); } public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) { if (left.constant.booleanValue()) { // true == anything // this is equivalent to the right argument inits return right.analyseCode(currentScope, flowContext, flowInfo); } else { // false == anything // this is equivalent to the right argument inits negated return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } } if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) { if (right.constant.booleanValue()) { // anything == true // this is equivalent to the right argument inits return left .analyseCode(currentScope, flowContext, flowInfo); } else { // anything == false // this is equivalent to the right argument inits negated return left .analyseCode(currentScope, flowContext, flowInfo) .asNegatedCondition(); } } return right.analyseCode( currentScope, flowContext, left.analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits()).unconditionalInits(); } else { // NOT_EQUAL : if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) { if (!left.constant.booleanValue()) { // false != anything // this is equivalent to the right argument inits return right.analyseCode(currentScope, flowContext, flowInfo); } else { // true != anything // this is equivalent to the right argument inits negated return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition(); } } if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) { if (!right.constant.booleanValue()) { // anything != false // this is equivalent to the right argument inits return left .analyseCode(currentScope, flowContext, flowInfo); } else { // anything != true // this is equivalent to the right argument inits negated return left .analyseCode(currentScope, flowContext, flowInfo) .asNegatedCondition(); } } return right.analyseCode( currentScope, flowContext, left.analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits()).asNegatedCondition() .unconditionalInits(); } } public final boolean areTypesCastCompatible(BlockScope scope, TypeBinding castType, TypeBinding expressionType) { // see specifications 5.5 // A more complete version of this method is provided on // CastExpression (it deals with constant and need runtime checkcast) if (castType == expressionType) return true; // ========ARRAY=============== if (expressionType.isArrayType()) { if (castType.isArrayType()) { // ------- (castTb.isArray) // expressionTb.isArray ----------- TypeBinding expressionEltType = ((ArrayBinding) expressionType) .elementsType(scope); if (expressionEltType.isBaseType()) // <---stop the recursion------- return ((ArrayBinding) castType).elementsType(scope) == expressionEltType; // recursivly on the elts... return areTypesCastCompatible(scope, ((ArrayBinding) castType) .elementsType(scope), expressionEltType); } if (castType.isBaseType()) { return false; } if (castType.isClass()) { // ------(castTb.isClass) // expressionTb.isArray --------------- if (scope.isJavaLangObject(castType)) return true; return false; } if (castType.isInterface()) { // ------- (castTb.isInterface) // expressionTb.isArray ----------- if (scope.isJavaLangCloneable(castType) || scope.isJavaIoSerializable(castType)) { return true; } return false; } return false; } // ------------(castType) null-------------- if (expressionType == NullBinding) { return !castType.isBaseType(); } // ========BASETYPE============== if (expressionType.isBaseType()) { return false; } // ========REFERENCE TYPE=================== if (expressionType.isClass()) { if (castType.isArrayType()) { // ---- (castTb.isArray) // expressionTb.isClass ------- if (scope.isJavaLangObject(expressionType)) return true; } if (castType.isBaseType()) { return false; } if (castType.isClass()) { // ----- (castTb.isClass) // expressionTb.isClass ------ if (expressionType.isCompatibleWith(castType)) return true; else { if (castType.isCompatibleWith(expressionType)) { return true; } return false; } } if (castType.isInterface()) { // ----- (castTb.isInterface) // expressionTb.isClass ------- if (((ReferenceBinding) expressionType).isFinal()) { // no // subclass // for // expressionTb, // thus // compile-time // check // is // valid if (expressionType.isCompatibleWith(castType)) return true; return false; } else { return true; } } return false; } if (expressionType.isInterface()) { if (castType.isArrayType()) { // ----- (castTb.isArray) // expressionTb.isInterface ------ if (scope.isJavaLangCloneable(expressionType) || scope.isJavaIoSerializable(expressionType)) // potential runtime error { return true; } return false; } if (castType.isBaseType()) { return false; } if (castType.isClass()) { // ----- (castTb.isClass) // expressionTb.isInterface -------- if (scope.isJavaLangObject(castType)) return true; if (((ReferenceBinding) castType).isFinal()) { // no subclass // for castTb, // thus // compile-time // check is // valid if (castType.isCompatibleWith(expressionType)) { return true; } return false; } return true; } if (castType.isInterface()) { // ----- (castTb.isInterface) // expressionTb.isInterface ------- if (Scope.compareTypes(castType, expressionType) == NotRelated) { MethodBinding[] castTbMethods = ((ReferenceBinding) castType) .methods(); int castTbMethodsLength = castTbMethods.length; MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionType) .methods(); int expressionTbMethodsLength = expressionTbMethods.length; for (int i = 0; i < castTbMethodsLength; i++) { for (int j = 0; j < expressionTbMethodsLength; j++) { if (castTbMethods[i].selector == expressionTbMethods[j].selector) { if (castTbMethods[i].returnType != expressionTbMethods[j].returnType) { if (castTbMethods[i] .areParametersEqual(expressionTbMethods[j])) { return false; } } } } } } return true; } return false; } return false; } public final void computeConstant(TypeBinding leftType, TypeBinding rightType) { if ((this.left.constant != NotAConstant) && (this.right.constant != NotAConstant)) { this.constant = Constant.computeConstantOperationEQUAL_EQUAL( left.constant, leftType.id, EQUAL_EQUAL, right.constant, rightType.id); if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL) constant = Constant.fromValue(!constant.booleanValue()); } else { this.constant = NotAConstant; // no optimization for null == null } } /** * Normal == or != code generation. * * @param currentScope * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope * @param codeStream * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream * @param valueRequired * boolean */ // public void generateCode(BlockScope currentScope, CodeStream codeStream, // boolean valueRequired) { // // if (constant != NotAConstant) { // int pc = codeStream.position; // if (valueRequired) // codeStream.generateConstant(constant, implicitConversion); // codeStream.recordPositionsFrom(pc, this.sourceStart); // return; // } // Label falseLabel; // bits |= OnlyValueRequiredMASK; // generateOptimizedBoolean( // currentScope, // codeStream, // null, // falseLabel = new Label(codeStream), // valueRequired); // if (falseLabel.hasForwardReferences()) { // if (valueRequired){ // // comparison is TRUE // codeStream.iconst_1(); // if ((bits & ValueForReturnMASK) != 0){ // codeStream.ireturn(); // // comparison is FALSE // falseLabel.place(); // codeStream.iconst_0(); // } else { // Label endLabel = new Label(codeStream); // codeStream.goto_(endLabel); // codeStream.decrStackSize(1); // // comparison is FALSE // falseLabel.place(); // codeStream.iconst_0(); // endLabel.place(); // } // } else { // falseLabel.place(); // } // } // } /** * Boolean operator code generation Optimized operations are: == and != */ // public void generateOptimizedBoolean(BlockScope currentScope, CodeStream // codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) { // // if (constant != Constant.NotAConstant) { // super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, // falseLabel, valueRequired); // return; // } // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) { // generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, // falseLabel, valueRequired); // } else { // generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, // falseLabel, valueRequired); // } // } else { // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) { // generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, // trueLabel, valueRequired); // } else { // generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, // trueLabel, valueRequired); // } // } // } /** * Boolean generation for == with boolean operands * * Note this code does not optimize conditional constants !!!! */ // public void generateOptimizedBooleanEqual(BlockScope currentScope, // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean // valueRequired) { // // // optimized cases: true == x, false == x // if (left.constant != NotAConstant) { // boolean inline = left.constant.booleanValue(); // right.generateOptimizedBoolean(currentScope, codeStream, (inline ? // trueLabel : falseLabel), (inline ? falseLabel : trueLabel), // valueRequired); // return; // } // optimized cases: x == true, x == false // if (right.constant != NotAConstant) { // boolean inline = right.constant.booleanValue(); // left.generateOptimizedBoolean(currentScope, codeStream, (inline ? // trueLabel : falseLabel), (inline ? falseLabel : trueLabel), // valueRequired); // return; // } // // default case // left.generateCode(currentScope, codeStream, valueRequired); // right.generateCode(currentScope, codeStream, valueRequired); // if (valueRequired) { // if (falseLabel == null) { // if (trueLabel != null) { // // implicit falling through the FALSE case // codeStream.if_icmpeq(trueLabel); // } // } else { // // implicit falling through the TRUE case // if (trueLabel == null) { // codeStream.if_icmpne(falseLabel); // } else { // // no implicit fall through TRUE/FALSE --> should never occur // } // } // } // // reposition the endPC // codeStream.updateLastRecordedEndPC(codeStream.position); // } // /** // * Boolean generation for == with non-boolean operands // * // */ // public void generateOptimizedNonBooleanEqual(BlockScope currentScope, // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean // valueRequired) { // // int pc = codeStream.position; // Constant inline; // if ((inline = right.constant) != NotAConstant) { // // optimized case: x == 0 // if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() == // 0)) { // left.generateCode(currentScope, codeStream, valueRequired); // if (valueRequired) { // if (falseLabel == null) { // if (trueLabel != null) { // // implicit falling through the FALSE case // codeStream.ifeq(trueLabel); // } // } else { // // implicit falling through the TRUE case // if (trueLabel == null) { // codeStream.ifne(falseLabel); // } else { // // no implicit fall through TRUE/FALSE --> should never occur // } // } // } // codeStream.recordPositionsFrom(pc, this.sourceStart); // return; // } // } // if ((inline = left.constant) != NotAConstant) { // // optimized case: 0 == x // if (((left.implicitConversion >> 4) == T_int) // && (inline.intValue() == 0)) { // right.generateCode(currentScope, codeStream, valueRequired); // if (valueRequired) { // if (falseLabel == null) { // if (trueLabel != null) { // // implicit falling through the FALSE case // codeStream.ifeq(trueLabel); // } // } else { // // implicit falling through the TRUE case // if (trueLabel == null) { // codeStream.ifne(falseLabel); // } else { // // no implicit fall through TRUE/FALSE --> should never occur // } // } // } // codeStream.recordPositionsFrom(pc, this.sourceStart); // return; // } // } // // null cases // // optimized case: x == null // if (right instanceof NullLiteral) { // if (left instanceof NullLiteral) { // // null == null // if (valueRequired) { // if ((bits & OnlyValueRequiredMASK) != 0) { // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) { // codeStream.iconst_1(); // } else { // codeStream.iconst_0(); // } // } else { // if (falseLabel == null) { // // implicit falling through the FALSE case // if (trueLabel != null) { // codeStream.goto_(trueLabel); // } // } // } // } // } else { // left.generateCode(currentScope, codeStream, valueRequired); // if (valueRequired) { // if (falseLabel == null) { // if (trueLabel != null) { // // implicit falling through the FALSE case // codeStream.ifnull(trueLabel); // } // } else { // // implicit falling through the TRUE case // if (trueLabel == null) { // codeStream.ifnonnull(falseLabel); // } else { // // no implicit fall through TRUE/FALSE --> should never occur // } // } // } // } // codeStream.recordPositionsFrom(pc, this.sourceStart); // return; // } else if (left instanceof NullLiteral) { // optimized case: null == x // right.generateCode(currentScope, codeStream, valueRequired); // if (valueRequired) { // if (falseLabel == null) { // if (trueLabel != null) { // // implicit falling through the FALSE case // codeStream.ifnull(trueLabel); // } // } else { // // implicit falling through the TRUE case // if (trueLabel == null) { // codeStream.ifnonnull(falseLabel); // } else { // // no implicit fall through TRUE/FALSE --> should never occur // } // } // } // codeStream.recordPositionsFrom(pc, this.sourceStart); // return; // } // // // default case // left.generateCode(currentScope, codeStream, valueRequired); // right.generateCode(currentScope, codeStream, valueRequired); // if (valueRequired) { // if (falseLabel == null) { // if (trueLabel != null) { // // implicit falling through the FALSE case // switch (left.implicitConversion >> 4) { // operand runtime type // case T_int : // codeStream.if_icmpeq(trueLabel); // break; // case T_float : // codeStream.fcmpl(); // codeStream.ifeq(trueLabel); // break; // case T_long : // codeStream.lcmp(); // codeStream.ifeq(trueLabel); // break; // case T_double : // codeStream.dcmpl(); // codeStream.ifeq(trueLabel); // break; // default : // codeStream.if_acmpeq(trueLabel); // } // } // } else { // // implicit falling through the TRUE case // if (trueLabel == null) { // switch (left.implicitConversion >> 4) { // operand runtime type // case T_int : // codeStream.if_icmpne(falseLabel); // break; // case T_float : // codeStream.fcmpl(); // codeStream.ifne(falseLabel); // break; // case T_long : // codeStream.lcmp(); // codeStream.ifne(falseLabel); // break; // case T_double : // codeStream.dcmpl(); // codeStream.ifne(falseLabel); // break; // default : // codeStream.if_acmpne(falseLabel); // } // } else { // // no implicit fall through TRUE/FALSE --> should never occur // } // } // } // codeStream.recordPositionsFrom(pc, this.sourceStart); // } public boolean isCompactableOperation() { return false; } public TypeBinding resolveType(BlockScope scope) { // always return BooleanBinding TypeBinding leftType = left.resolveType(scope); TypeBinding rightType = right.resolveType(scope); if (leftType == null || rightType == null) { constant = NotAConstant; return null; } // both base type if (leftType.isBaseType() && rightType.isBaseType()) { // the code is an int // (cast) left == (cast) rigth --> result // 0000 0000 0000 0000 0000 // <<16 <<12 <<8 <<4 <<0 int result = ResolveTypeTables[EQUAL_EQUAL][(leftType.id << 4) + rightType.id]; left.implicitConversion = result >>> 12; right.implicitConversion = (result >>> 4) & 0x000FF; bits |= result & 0xF; if ((result & 0x0000F) == T_undefined) { constant = Constant.NotAConstant; scope.problemReporter().invalidOperator(this, leftType, rightType); return null; } computeConstant(leftType, rightType); this.resolvedType = BooleanBinding; return BooleanBinding; } // Object references // spec 15.20.3 if (areTypesCastCompatible(scope, rightType, leftType) || areTypesCastCompatible(scope, leftType, rightType)) { // (special case for String) if ((rightType.id == T_String) && (leftType.id == T_String)) computeConstant(leftType, rightType); else constant = NotAConstant; if (rightType.id == T_String) right.implicitConversion = String2String; if (leftType.id == T_String) left.implicitConversion = String2String; this.resolvedType = BooleanBinding; return BooleanBinding; } constant = NotAConstant; scope.problemReporter().notCompatibleTypesError(this, leftType, rightType); 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); } }