1 /*******************************************************************************
 
   2  * Copyright (c) 2000, 2003 IBM Corporation and others.
 
   3  * All rights reserved. This program and the accompanying materials 
 
   4  * are made available under the terms of the Common Public License v1.0
 
   5  * which accompanies this distribution, and is available at
 
   6  * http://www.eclipse.org/legal/cpl-v10.html
 
   9  *     IBM Corporation - initial API and implementation
 
  10  *******************************************************************************/
 
  11 package net.sourceforge.phpdt.internal.compiler.ast;
 
  13 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
 
  14 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
 
  15 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
 
  16 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
 
  17 import net.sourceforge.phpdt.internal.compiler.lookup.ArrayBinding;
 
  18 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
 
  19 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
 
  20 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
 
  21 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
 
  22 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
 
  24 public class EqualExpression extends BinaryExpression {
 
  26         public EqualExpression(Expression left, Expression right, int operator) {
 
  27                 super(left, right, operator);
 
  30         public FlowInfo analyseCode(BlockScope currentScope,
 
  31                         FlowContext flowContext, FlowInfo flowInfo) {
 
  32                 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
 
  33                         if ((left.constant != NotAConstant)
 
  34                                         && (left.constant.typeID() == T_boolean)) {
 
  35                                 if (left.constant.booleanValue()) { // true == anything
 
  36                                         // this is equivalent to the right argument inits
 
  37                                         return right.analyseCode(currentScope, flowContext,
 
  39                                 } else { // false == anything
 
  40                                         // this is equivalent to the right argument inits negated
 
  41                                         return right.analyseCode(currentScope, flowContext,
 
  42                                                         flowInfo).asNegatedCondition();
 
  45                         if ((right.constant != NotAConstant)
 
  46                                         && (right.constant.typeID() == T_boolean)) {
 
  47                                 if (right.constant.booleanValue()) { // anything == true
 
  48                                         // this is equivalent to the right argument inits
 
  50                                                         .analyseCode(currentScope, flowContext, flowInfo);
 
  51                                 } else { // anything == false
 
  52                                         // this is equivalent to the right argument inits negated
 
  54                                                         .analyseCode(currentScope, flowContext, flowInfo)
 
  55                                                         .asNegatedCondition();
 
  58                         return right.analyseCode(
 
  61                                         left.analyseCode(currentScope, flowContext, flowInfo)
 
  62                                                         .unconditionalInits()).unconditionalInits();
 
  63                 } else { // NOT_EQUAL :
 
  64                         if ((left.constant != NotAConstant)
 
  65                                         && (left.constant.typeID() == T_boolean)) {
 
  66                                 if (!left.constant.booleanValue()) { // false != anything
 
  67                                         // this is equivalent to the right argument inits
 
  68                                         return right.analyseCode(currentScope, flowContext,
 
  70                                 } else { // true != anything
 
  71                                         // this is equivalent to the right argument inits negated
 
  72                                         return right.analyseCode(currentScope, flowContext,
 
  73                                                         flowInfo).asNegatedCondition();
 
  76                         if ((right.constant != NotAConstant)
 
  77                                         && (right.constant.typeID() == T_boolean)) {
 
  78                                 if (!right.constant.booleanValue()) { // anything != false
 
  79                                         // this is equivalent to the right argument inits
 
  81                                                         .analyseCode(currentScope, flowContext, flowInfo);
 
  82                                 } else { // anything != true
 
  83                                         // this is equivalent to the right argument inits negated
 
  85                                                         .analyseCode(currentScope, flowContext, flowInfo)
 
  86                                                         .asNegatedCondition();
 
  89                         return right.analyseCode(
 
  92                                         left.analyseCode(currentScope, flowContext, flowInfo)
 
  93                                                         .unconditionalInits()).asNegatedCondition()
 
  94                                         .unconditionalInits();
 
  98         public final boolean areTypesCastCompatible(BlockScope scope,
 
  99                         TypeBinding castType, TypeBinding expressionType) {
 
 100                 // see specifications 5.5
 
 101                 // A more complete version of this method is provided on
 
 102                 // CastExpression (it deals with constant and need runtime checkcast)
 
 104                 if (castType == expressionType)
 
 107                 // ========ARRAY===============
 
 108                 if (expressionType.isArrayType()) {
 
 109                         if (castType.isArrayType()) { // ------- (castTb.isArray)
 
 110                                                                                         // expressionTb.isArray -----------
 
 111                                 TypeBinding expressionEltType = ((ArrayBinding) expressionType)
 
 112                                                 .elementsType(scope);
 
 113                                 if (expressionEltType.isBaseType())
 
 114                                         // <---stop the recursion-------
 
 115                                         return ((ArrayBinding) castType).elementsType(scope) == expressionEltType;
 
 116                                 // recursivly on the elts...
 
 117                                 return areTypesCastCompatible(scope, ((ArrayBinding) castType)
 
 118                                                 .elementsType(scope), expressionEltType);
 
 120                         if (castType.isBaseType()) {
 
 123                         if (castType.isClass()) { // ------(castTb.isClass)
 
 124                                                                                 // expressionTb.isArray ---------------
 
 125                                 if (scope.isJavaLangObject(castType))
 
 129                         if (castType.isInterface()) { // ------- (castTb.isInterface)
 
 130                                                                                         // expressionTb.isArray -----------
 
 131                                 if (scope.isJavaLangCloneable(castType)
 
 132                                                 || scope.isJavaIoSerializable(castType)) {
 
 141                 // ------------(castType) null--------------
 
 142                 if (expressionType == NullBinding) {
 
 143                         return !castType.isBaseType();
 
 146                 // ========BASETYPE==============
 
 147                 if (expressionType.isBaseType()) {
 
 151                 // ========REFERENCE TYPE===================
 
 153                 if (expressionType.isClass()) {
 
 154                         if (castType.isArrayType()) { // ---- (castTb.isArray)
 
 155                                                                                         // expressionTb.isClass -------
 
 156                                 if (scope.isJavaLangObject(expressionType))
 
 159                         if (castType.isBaseType()) {
 
 162                         if (castType.isClass()) { // ----- (castTb.isClass)
 
 163                                                                                 // expressionTb.isClass ------
 
 164                                 if (expressionType.isCompatibleWith(castType))
 
 167                                         if (castType.isCompatibleWith(expressionType)) {
 
 173                         if (castType.isInterface()) { // ----- (castTb.isInterface)
 
 174                                                                                         // expressionTb.isClass -------
 
 175                                 if (((ReferenceBinding) expressionType).isFinal()) { // no
 
 184                                         if (expressionType.isCompatibleWith(castType))
 
 194                 if (expressionType.isInterface()) {
 
 195                         if (castType.isArrayType()) { // ----- (castTb.isArray)
 
 196                                                                                         // expressionTb.isInterface ------
 
 197                                 if (scope.isJavaLangCloneable(expressionType)
 
 198                                                 || scope.isJavaIoSerializable(expressionType))
 
 199                                 // potential runtime error
 
 205                         if (castType.isBaseType()) {
 
 208                         if (castType.isClass()) { // ----- (castTb.isClass)
 
 209                                                                                 // expressionTb.isInterface --------
 
 210                                 if (scope.isJavaLangObject(castType))
 
 212                                 if (((ReferenceBinding) castType).isFinal()) { // no subclass
 
 218                                         if (castType.isCompatibleWith(expressionType)) {
 
 225                         if (castType.isInterface()) { // ----- (castTb.isInterface)
 
 226                                                                                         // expressionTb.isInterface -------
 
 227                                 if (Scope.compareTypes(castType, expressionType) == NotRelated) {
 
 228                                         MethodBinding[] castTbMethods = ((ReferenceBinding) castType)
 
 230                                         int castTbMethodsLength = castTbMethods.length;
 
 231                                         MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionType)
 
 233                                         int expressionTbMethodsLength = expressionTbMethods.length;
 
 234                                         for (int i = 0; i < castTbMethodsLength; i++) {
 
 235                                                 for (int j = 0; j < expressionTbMethodsLength; j++) {
 
 236                                                         if (castTbMethods[i].selector == expressionTbMethods[j].selector) {
 
 237                                                                 if (castTbMethods[i].returnType != expressionTbMethods[j].returnType) {
 
 239                                                                                         .areParametersEqual(expressionTbMethods[j])) {
 
 256         public final void computeConstant(TypeBinding leftType,
 
 257                         TypeBinding rightType) {
 
 258                 if ((this.left.constant != NotAConstant)
 
 259                                 && (this.right.constant != NotAConstant)) {
 
 260                         this.constant = Constant.computeConstantOperationEQUAL_EQUAL(
 
 261                                         left.constant, leftType.id, EQUAL_EQUAL, right.constant,
 
 263                         if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
 
 264                                 constant = Constant.fromValue(!constant.booleanValue());
 
 266                         this.constant = NotAConstant;
 
 267                         // no optimization for null == null
 
 272          * Normal == or != code generation.
 
 274          * @param currentScope
 
 275          *            net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
 
 277          *            net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
 
 278          * @param valueRequired
 
 281         // public void generateCode(BlockScope currentScope, CodeStream codeStream,
 
 282         // boolean valueRequired) {
 
 284         // if (constant != NotAConstant) {
 
 285         // int pc = codeStream.position;
 
 286         // if (valueRequired)
 
 287         // codeStream.generateConstant(constant, implicitConversion);
 
 288         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 292         // bits |= OnlyValueRequiredMASK;
 
 293         // generateOptimizedBoolean(
 
 297         // falseLabel = new Label(codeStream),
 
 299         // if (falseLabel.hasForwardReferences()) {
 
 300         // if (valueRequired){
 
 301         // // comparison is TRUE
 
 302         // codeStream.iconst_1();
 
 303         // if ((bits & ValueForReturnMASK) != 0){
 
 304         // codeStream.ireturn();
 
 305         // // comparison is FALSE
 
 306         // falseLabel.place();
 
 307         // codeStream.iconst_0();
 
 309         // Label endLabel = new Label(codeStream);
 
 310         // codeStream.goto_(endLabel);
 
 311         // codeStream.decrStackSize(1);
 
 312         // // comparison is FALSE
 
 313         // falseLabel.place();
 
 314         // codeStream.iconst_0();
 
 318         // falseLabel.place();
 
 323          * Boolean operator code generation Optimized operations are: == and !=
 
 325         // public void generateOptimizedBoolean(BlockScope currentScope, CodeStream
 
 326         // codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
 
 328         // if (constant != Constant.NotAConstant) {
 
 329         // super.generateOptimizedBoolean(currentScope, codeStream, trueLabel,
 
 330         // falseLabel, valueRequired);
 
 333         // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
 
 334         // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
 
 335         // generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel,
 
 336         // falseLabel, valueRequired);
 
 338         // generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel,
 
 339         // falseLabel, valueRequired);
 
 342         // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
 
 343         // generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel,
 
 344         // trueLabel, valueRequired);
 
 346         // generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel,
 
 347         // trueLabel, valueRequired);
 
 352          * Boolean generation for == with boolean operands
 
 354          * Note this code does not optimize conditional constants !!!!
 
 356         // public void generateOptimizedBooleanEqual(BlockScope currentScope,
 
 357         // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean
 
 360         // // optimized cases: true == x, false == x
 
 361         // if (left.constant != NotAConstant) {
 
 362         // boolean inline = left.constant.booleanValue();
 
 363         // right.generateOptimizedBoolean(currentScope, codeStream, (inline ?
 
 364         // trueLabel : falseLabel), (inline ? falseLabel : trueLabel),
 
 367         // } // optimized cases: x == true, x == false
 
 368         // if (right.constant != NotAConstant) {
 
 369         // boolean inline = right.constant.booleanValue();
 
 370         // left.generateOptimizedBoolean(currentScope, codeStream, (inline ?
 
 371         // trueLabel : falseLabel), (inline ? falseLabel : trueLabel),
 
 376         // left.generateCode(currentScope, codeStream, valueRequired);
 
 377         // right.generateCode(currentScope, codeStream, valueRequired);
 
 378         // if (valueRequired) {
 
 379         // if (falseLabel == null) {
 
 380         // if (trueLabel != null) {
 
 381         // // implicit falling through the FALSE case
 
 382         // codeStream.if_icmpeq(trueLabel);
 
 385         // // implicit falling through the TRUE case
 
 386         // if (trueLabel == null) {
 
 387         // codeStream.if_icmpne(falseLabel);
 
 389         // // no implicit fall through TRUE/FALSE --> should never occur
 
 393         // // reposition the endPC
 
 394         // codeStream.updateLastRecordedEndPC(codeStream.position);
 
 397         // * Boolean generation for == with non-boolean operands
 
 400         // public void generateOptimizedNonBooleanEqual(BlockScope currentScope,
 
 401         // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean
 
 404         // int pc = codeStream.position;
 
 406         // if ((inline = right.constant) != NotAConstant) {
 
 407         // // optimized case: x == 0
 
 408         // if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() ==
 
 410         // left.generateCode(currentScope, codeStream, valueRequired);
 
 411         // if (valueRequired) {
 
 412         // if (falseLabel == null) {
 
 413         // if (trueLabel != null) {
 
 414         // // implicit falling through the FALSE case
 
 415         // codeStream.ifeq(trueLabel);
 
 418         // // implicit falling through the TRUE case
 
 419         // if (trueLabel == null) {
 
 420         // codeStream.ifne(falseLabel);
 
 422         // // no implicit fall through TRUE/FALSE --> should never occur
 
 426         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 430         // if ((inline = left.constant) != NotAConstant) {
 
 431         // // optimized case: 0 == x
 
 432         // if (((left.implicitConversion >> 4) == T_int)
 
 433         // && (inline.intValue() == 0)) {
 
 434         // right.generateCode(currentScope, codeStream, valueRequired);
 
 435         // if (valueRequired) {
 
 436         // if (falseLabel == null) {
 
 437         // if (trueLabel != null) {
 
 438         // // implicit falling through the FALSE case
 
 439         // codeStream.ifeq(trueLabel);
 
 442         // // implicit falling through the TRUE case
 
 443         // if (trueLabel == null) {
 
 444         // codeStream.ifne(falseLabel);
 
 446         // // no implicit fall through TRUE/FALSE --> should never occur
 
 450         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 455         // // optimized case: x == null
 
 456         // if (right instanceof NullLiteral) {
 
 457         // if (left instanceof NullLiteral) {
 
 459         // if (valueRequired) {
 
 460         // if ((bits & OnlyValueRequiredMASK) != 0) {
 
 461         // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
 
 462         // codeStream.iconst_1();
 
 464         // codeStream.iconst_0();
 
 467         // if (falseLabel == null) {
 
 468         // // implicit falling through the FALSE case
 
 469         // if (trueLabel != null) {
 
 470         // codeStream.goto_(trueLabel);
 
 476         // left.generateCode(currentScope, codeStream, valueRequired);
 
 477         // if (valueRequired) {
 
 478         // if (falseLabel == null) {
 
 479         // if (trueLabel != null) {
 
 480         // // implicit falling through the FALSE case
 
 481         // codeStream.ifnull(trueLabel);
 
 484         // // implicit falling through the TRUE case
 
 485         // if (trueLabel == null) {
 
 486         // codeStream.ifnonnull(falseLabel);
 
 488         // // no implicit fall through TRUE/FALSE --> should never occur
 
 493         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 495         // } else if (left instanceof NullLiteral) { // optimized case: null == x
 
 496         // right.generateCode(currentScope, codeStream, valueRequired);
 
 497         // if (valueRequired) {
 
 498         // if (falseLabel == null) {
 
 499         // if (trueLabel != null) {
 
 500         // // implicit falling through the FALSE case
 
 501         // codeStream.ifnull(trueLabel);
 
 504         // // implicit falling through the TRUE case
 
 505         // if (trueLabel == null) {
 
 506         // codeStream.ifnonnull(falseLabel);
 
 508         // // no implicit fall through TRUE/FALSE --> should never occur
 
 512         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 517         // left.generateCode(currentScope, codeStream, valueRequired);
 
 518         // right.generateCode(currentScope, codeStream, valueRequired);
 
 519         // if (valueRequired) {
 
 520         // if (falseLabel == null) {
 
 521         // if (trueLabel != null) {
 
 522         // // implicit falling through the FALSE case
 
 523         // switch (left.implicitConversion >> 4) { // operand runtime type
 
 525         // codeStream.if_icmpeq(trueLabel);
 
 528         // codeStream.fcmpl();
 
 529         // codeStream.ifeq(trueLabel);
 
 532         // codeStream.lcmp();
 
 533         // codeStream.ifeq(trueLabel);
 
 536         // codeStream.dcmpl();
 
 537         // codeStream.ifeq(trueLabel);
 
 540         // codeStream.if_acmpeq(trueLabel);
 
 544         // // implicit falling through the TRUE case
 
 545         // if (trueLabel == null) {
 
 546         // switch (left.implicitConversion >> 4) { // operand runtime type
 
 548         // codeStream.if_icmpne(falseLabel);
 
 551         // codeStream.fcmpl();
 
 552         // codeStream.ifne(falseLabel);
 
 555         // codeStream.lcmp();
 
 556         // codeStream.ifne(falseLabel);
 
 559         // codeStream.dcmpl();
 
 560         // codeStream.ifne(falseLabel);
 
 563         // codeStream.if_acmpne(falseLabel);
 
 566         // // no implicit fall through TRUE/FALSE --> should never occur
 
 570         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 572         public boolean isCompactableOperation() {
 
 576         public TypeBinding resolveType(BlockScope scope) {
 
 577                 // always return BooleanBinding
 
 578                 TypeBinding leftType = left.resolveType(scope);
 
 579                 TypeBinding rightType = right.resolveType(scope);
 
 580                 if (leftType == null || rightType == null) {
 
 581                         constant = NotAConstant;
 
 586                 if (leftType.isBaseType() && rightType.isBaseType()) {
 
 587                         // the code is an int
 
 588                         // (cast) left == (cast) rigth --> result
 
 589                         // 0000 0000 0000 0000 0000
 
 590                         // <<16 <<12 <<8 <<4 <<0
 
 591                         int result = ResolveTypeTables[EQUAL_EQUAL][(leftType.id << 4)
 
 593                         left.implicitConversion = result >>> 12;
 
 594                         right.implicitConversion = (result >>> 4) & 0x000FF;
 
 595                         bits |= result & 0xF;
 
 596                         if ((result & 0x0000F) == T_undefined) {
 
 597                                 constant = Constant.NotAConstant;
 
 598                                 scope.problemReporter().invalidOperator(this, leftType,
 
 602                         computeConstant(leftType, rightType);
 
 603                         this.resolvedType = BooleanBinding;
 
 604                         return BooleanBinding;
 
 609                 if (areTypesCastCompatible(scope, rightType, leftType)
 
 610                                 || areTypesCastCompatible(scope, leftType, rightType)) {
 
 611                         // (special case for String)
 
 612                         if ((rightType.id == T_String) && (leftType.id == T_String))
 
 613                                 computeConstant(leftType, rightType);
 
 615                                 constant = NotAConstant;
 
 616                         if (rightType.id == T_String)
 
 617                                 right.implicitConversion = String2String;
 
 618                         if (leftType.id == T_String)
 
 619                                 left.implicitConversion = String2String;
 
 620                         this.resolvedType = BooleanBinding;
 
 621                         return BooleanBinding;
 
 623                 constant = NotAConstant;
 
 624                 scope.problemReporter().notCompatibleTypesError(this, leftType,
 
 629         public void traverse(ASTVisitor visitor, BlockScope scope) {
 
 630                 if (visitor.visit(this, scope)) {
 
 631                         left.traverse(visitor, scope);
 
 632                         right.traverse(visitor, scope);
 
 634                 visitor.endVisit(this, scope);