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.core.compiler.ITerminalSymbols.TokenName;
 
  14 import net.sourceforge.phpdt.internal.compiler.ASTVisitor;
 
  15 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
 
  16 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
 
  17 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
 
  18 import net.sourceforge.phpdt.internal.compiler.lookup.ArrayBinding;
 
  19 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
 
  20 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
 
  21 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
 
  22 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
 
  23 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
 
  25 public class EqualExpression extends BinaryExpression {
 
  27         public EqualExpression(Expression left, Expression right, int operator) {
 
  28                 super (left, right, operator);
 
  31         public FlowInfo analyseCode(BlockScope currentScope,
 
  32                         FlowContext flowContext, FlowInfo flowInfo) {
 
  33                 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
 
  34                         if ((left.constant != NotAConstant)
 
  35                                         && (left.constant.typeID() == T_boolean)) {
 
  36                                 if (left.constant.booleanValue()) { // true == anything
 
  37                                         // this is equivalent to the right argument inits
 
  38                                         return right.analyseCode(currentScope, flowContext,
 
  40                                 } else { // false == anything
 
  41                                         // this is equivalent to the right argument inits negated
 
  42                                         return right.analyseCode(currentScope, flowContext,
 
  43                                                         flowInfo).asNegatedCondition();
 
  46                         if ((right.constant != NotAConstant)
 
  47                                         && (right.constant.typeID() == T_boolean)) {
 
  48                                 if (right.constant.booleanValue()) { // anything == true
 
  49                                         // this is equivalent to the right argument inits
 
  51                                                         .analyseCode(currentScope, flowContext, flowInfo);
 
  52                                 } else { // anything == false
 
  53                                         // this is equivalent to the right argument inits negated
 
  55                                                         .analyseCode(currentScope, flowContext, flowInfo)
 
  56                                                         .asNegatedCondition();
 
  59                         return right.analyseCode(
 
  62                                         left.analyseCode(currentScope, flowContext, flowInfo)
 
  63                                                         .unconditionalInits()).unconditionalInits();
 
  64                 } else { // NOT_EQUAL :
 
  65                         if ((left.constant != NotAConstant)
 
  66                                         && (left.constant.typeID() == T_boolean)) {
 
  67                                 if (!left.constant.booleanValue()) { // false != anything
 
  68                                         // this is equivalent to the right argument inits
 
  69                                         return right.analyseCode(currentScope, flowContext,
 
  71                                 } else { // true != anything
 
  72                                         // this is equivalent to the right argument inits negated
 
  73                                         return right.analyseCode(currentScope, flowContext,
 
  74                                                         flowInfo).asNegatedCondition();
 
  77                         if ((right.constant != NotAConstant)
 
  78                                         && (right.constant.typeID() == T_boolean)) {
 
  79                                 if (!right.constant.booleanValue()) { // anything != false
 
  80                                         // this is equivalent to the right argument inits
 
  82                                                         .analyseCode(currentScope, flowContext, flowInfo);
 
  83                                 } else { // anything != true
 
  84                                         // this is equivalent to the right argument inits negated
 
  86                                                         .analyseCode(currentScope, flowContext, flowInfo)
 
  87                                                         .asNegatedCondition();
 
  90                         return right.analyseCode(
 
  93                                         left.analyseCode(currentScope, flowContext, flowInfo)
 
  94                                                         .unconditionalInits()).asNegatedCondition()
 
  95                                         .unconditionalInits();
 
  99         public final boolean areTypesCastCompatible(BlockScope scope,
 
 100                         TypeBinding castType, TypeBinding expressionType) {
 
 101                 // see specifications 5.5
 
 102                 // A more complete version of this method is provided on
 
 103                 // CastExpression (it deals with constant and need runtime checkcast)
 
 105                 if (castType == expressionType)
 
 108                 // ========ARRAY===============
 
 109                 if (expressionType.isArrayType()) {
 
 110                         if (castType.isArrayType()) { // ------- (castTb.isArray)
 
 111                                                                                         // expressionTb.isArray -----------
 
 112                                 TypeBinding expressionEltType = ((ArrayBinding) expressionType)
 
 113                                                 .elementsType(scope);
 
 114                                 if (expressionEltType.isBaseType())
 
 115                                         // <---stop the recursion-------
 
 116                                         return ((ArrayBinding) castType).elementsType(scope) == expressionEltType;
 
 117                                 // recursivly on the elts...
 
 118                                 return areTypesCastCompatible(scope, ((ArrayBinding) castType)
 
 119                                                 .elementsType(scope), expressionEltType);
 
 121                         if (castType.isBaseType()) {
 
 124                         if (castType.isClass()) { // ------(castTb.isClass)
 
 125                                                                                 // expressionTb.isArray ---------------
 
 126                                 if (scope.isJavaLangObject(castType))
 
 130                         if (castType.isInterface()) { // ------- (castTb.isInterface)
 
 131                                                                                         // expressionTb.isArray -----------
 
 132                                 if (scope.isJavaLangCloneable(castType)
 
 133                                                 || scope.isJavaIoSerializable(castType)) {
 
 142                 // ------------(castType) null--------------
 
 143                 if (expressionType == NullBinding) {
 
 144                         return !castType.isBaseType();
 
 147                 // ========BASETYPE==============
 
 148                 if (expressionType.isBaseType()) {
 
 152                 // ========REFERENCE TYPE===================
 
 154                 if (expressionType.isClass()) {
 
 155                         if (castType.isArrayType()) { // ---- (castTb.isArray)
 
 156                                                                                         // expressionTb.isClass -------
 
 157                                 if (scope.isJavaLangObject(expressionType))
 
 160                         if (castType.isBaseType()) {
 
 163                         if (castType.isClass()) { // ----- (castTb.isClass)
 
 164                                                                                 // expressionTb.isClass ------
 
 165                                 if (expressionType.isCompatibleWith(castType))
 
 168                                         if (castType.isCompatibleWith(expressionType)) {
 
 174                         if (castType.isInterface()) { // ----- (castTb.isInterface)
 
 175                                                                                         // expressionTb.isClass -------
 
 176                                 if (((ReferenceBinding) expressionType).isFinal()) { // no
 
 185                                         if (expressionType.isCompatibleWith(castType))
 
 195                 if (expressionType.isInterface()) {
 
 196                         if (castType.isArrayType()) { // ----- (castTb.isArray)
 
 197                                                                                         // expressionTb.isInterface ------
 
 198                                 if (scope.isJavaLangCloneable(expressionType)
 
 199                                                 || scope.isJavaIoSerializable(expressionType))
 
 200                                 // potential runtime error
 
 206                         if (castType.isBaseType()) {
 
 209                         if (castType.isClass()) { // ----- (castTb.isClass)
 
 210                                                                                 // expressionTb.isInterface --------
 
 211                                 if (scope.isJavaLangObject(castType))
 
 213                                 if (((ReferenceBinding) castType).isFinal()) { // no subclass
 
 219                                         if (castType.isCompatibleWith(expressionType)) {
 
 226                         if (castType.isInterface()) { // ----- (castTb.isInterface)
 
 227                                                                                         // expressionTb.isInterface -------
 
 228                                 if (Scope.compareTypes(castType, expressionType) == NotRelated) {
 
 229                                         MethodBinding[] castTbMethods = ((ReferenceBinding) castType)
 
 231                                         int castTbMethodsLength = castTbMethods.length;
 
 232                                         MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionType)
 
 234                                         int expressionTbMethodsLength = expressionTbMethods.length;
 
 235                                         for (int i = 0; i < castTbMethodsLength; i++) {
 
 236                                                 for (int j = 0; j < expressionTbMethodsLength; j++) {
 
 237                                                         if (castTbMethods[i].selector == expressionTbMethods[j].selector) {
 
 238                                                                 if (castTbMethods[i].returnType != expressionTbMethods[j].returnType) {
 
 240                                                                                         .areParametersEqual(expressionTbMethods[j])) {
 
 257         public final void computeConstant(TypeBinding leftType,
 
 258                         TypeBinding rightType) {
 
 259                 if ((this.left.constant != NotAConstant)
 
 260                                 && (this.right.constant != NotAConstant)) {
 
 261                         this.constant = Constant.computeConstantOperationEQUAL_EQUAL(
 
 262                                         left.constant, leftType.id, EQUAL_EQUAL, right.constant,
 
 264                         if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
 
 265                                 constant = Constant.fromValue(!constant.booleanValue());
 
 267                         this.constant = NotAConstant;
 
 268                         // no optimization for null == null
 
 273          * Normal == or != code generation.
 
 275          * @param currentScope
 
 276          *            net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
 
 278          *            net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
 
 279          * @param valueRequired
 
 282         // public void generateCode(BlockScope currentScope, CodeStream codeStream,
 
 283         // boolean valueRequired) {
 
 285         // if (constant != NotAConstant) {
 
 286         // int pc = codeStream.position;
 
 287         // if (valueRequired)
 
 288         // codeStream.generateConstant(constant, implicitConversion);
 
 289         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 293         // bits |= OnlyValueRequiredMASK;
 
 294         // generateOptimizedBoolean(
 
 298         // falseLabel = new Label(codeStream),
 
 300         // if (falseLabel.hasForwardReferences()) {
 
 301         // if (valueRequired){
 
 302         // // comparison is TRUE
 
 303         // codeStream.iconst_1();
 
 304         // if ((bits & ValueForReturnMASK) != 0){
 
 305         // codeStream.ireturn();
 
 306         // // comparison is FALSE
 
 307         // falseLabel.place();
 
 308         // codeStream.iconst_0();
 
 310         // Label endLabel = new Label(codeStream);
 
 311         // codeStream.goto_(endLabel);
 
 312         // codeStream.decrStackSize(1);
 
 313         // // comparison is FALSE
 
 314         // falseLabel.place();
 
 315         // codeStream.iconst_0();
 
 319         // falseLabel.place();
 
 324          * Boolean operator code generation Optimized operations are: == and !=
 
 326         // public void generateOptimizedBoolean(BlockScope currentScope, CodeStream
 
 327         // codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
 
 329         // if (constant != Constant.NotAConstant) {
 
 330         // super.generateOptimizedBoolean(currentScope, codeStream, trueLabel,
 
 331         // falseLabel, valueRequired);
 
 334         // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
 
 335         // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
 
 336         // generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel,
 
 337         // falseLabel, valueRequired);
 
 339         // generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel,
 
 340         // falseLabel, valueRequired);
 
 343         // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
 
 344         // generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel,
 
 345         // trueLabel, valueRequired);
 
 347         // generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel,
 
 348         // trueLabel, valueRequired);
 
 353          * Boolean generation for == with boolean operands
 
 355          * Note this code does not optimize conditional constants !!!!
 
 357         // public void generateOptimizedBooleanEqual(BlockScope currentScope,
 
 358         // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean
 
 361         // // optimized cases: true == x, false == x
 
 362         // if (left.constant != NotAConstant) {
 
 363         // boolean inline = left.constant.booleanValue();
 
 364         // right.generateOptimizedBoolean(currentScope, codeStream, (inline ?
 
 365         // trueLabel : falseLabel), (inline ? falseLabel : trueLabel),
 
 368         // } // optimized cases: x == true, x == false
 
 369         // if (right.constant != NotAConstant) {
 
 370         // boolean inline = right.constant.booleanValue();
 
 371         // left.generateOptimizedBoolean(currentScope, codeStream, (inline ?
 
 372         // trueLabel : falseLabel), (inline ? falseLabel : trueLabel),
 
 377         // left.generateCode(currentScope, codeStream, valueRequired);
 
 378         // right.generateCode(currentScope, codeStream, valueRequired);
 
 379         // if (valueRequired) {
 
 380         // if (falseLabel == null) {
 
 381         // if (trueLabel != null) {
 
 382         // // implicit falling through the FALSE case
 
 383         // codeStream.if_icmpeq(trueLabel);
 
 386         // // implicit falling through the TRUE case
 
 387         // if (trueLabel == null) {
 
 388         // codeStream.if_icmpne(falseLabel);
 
 390         // // no implicit fall through TRUE/FALSE --> should never occur
 
 394         // // reposition the endPC
 
 395         // codeStream.updateLastRecordedEndPC(codeStream.position);
 
 398         // * Boolean generation for == with non-boolean operands
 
 401         // public void generateOptimizedNonBooleanEqual(BlockScope currentScope,
 
 402         // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean
 
 405         // int pc = codeStream.position;
 
 407         // if ((inline = right.constant) != NotAConstant) {
 
 408         // // optimized case: x == 0
 
 409         // if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() ==
 
 411         // left.generateCode(currentScope, codeStream, valueRequired);
 
 412         // if (valueRequired) {
 
 413         // if (falseLabel == null) {
 
 414         // if (trueLabel != null) {
 
 415         // // implicit falling through the FALSE case
 
 416         // codeStream.ifeq(trueLabel);
 
 419         // // implicit falling through the TRUE case
 
 420         // if (trueLabel == null) {
 
 421         // codeStream.ifne(falseLabel);
 
 423         // // no implicit fall through TRUE/FALSE --> should never occur
 
 427         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 431         // if ((inline = left.constant) != NotAConstant) {
 
 432         // // optimized case: 0 == x
 
 433         // if (((left.implicitConversion >> 4) == T_int)
 
 434         // && (inline.intValue() == 0)) {
 
 435         // right.generateCode(currentScope, codeStream, valueRequired);
 
 436         // if (valueRequired) {
 
 437         // if (falseLabel == null) {
 
 438         // if (trueLabel != null) {
 
 439         // // implicit falling through the FALSE case
 
 440         // codeStream.ifeq(trueLabel);
 
 443         // // implicit falling through the TRUE case
 
 444         // if (trueLabel == null) {
 
 445         // codeStream.ifne(falseLabel);
 
 447         // // no implicit fall through TRUE/FALSE --> should never occur
 
 451         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 456         // // optimized case: x == null
 
 457         // if (right instanceof NullLiteral) {
 
 458         // if (left instanceof NullLiteral) {
 
 460         // if (valueRequired) {
 
 461         // if ((bits & OnlyValueRequiredMASK) != 0) {
 
 462         // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
 
 463         // codeStream.iconst_1();
 
 465         // codeStream.iconst_0();
 
 468         // if (falseLabel == null) {
 
 469         // // implicit falling through the FALSE case
 
 470         // if (trueLabel != null) {
 
 471         // codeStream.goto_(trueLabel);
 
 477         // left.generateCode(currentScope, codeStream, valueRequired);
 
 478         // if (valueRequired) {
 
 479         // if (falseLabel == null) {
 
 480         // if (trueLabel != null) {
 
 481         // // implicit falling through the FALSE case
 
 482         // codeStream.ifnull(trueLabel);
 
 485         // // implicit falling through the TRUE case
 
 486         // if (trueLabel == null) {
 
 487         // codeStream.ifnonnull(falseLabel);
 
 489         // // no implicit fall through TRUE/FALSE --> should never occur
 
 494         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 496         // } else if (left instanceof NullLiteral) { // optimized case: null == x
 
 497         // right.generateCode(currentScope, codeStream, valueRequired);
 
 498         // if (valueRequired) {
 
 499         // if (falseLabel == null) {
 
 500         // if (trueLabel != null) {
 
 501         // // implicit falling through the FALSE case
 
 502         // codeStream.ifnull(trueLabel);
 
 505         // // implicit falling through the TRUE case
 
 506         // if (trueLabel == null) {
 
 507         // codeStream.ifnonnull(falseLabel);
 
 509         // // no implicit fall through TRUE/FALSE --> should never occur
 
 513         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 518         // left.generateCode(currentScope, codeStream, valueRequired);
 
 519         // right.generateCode(currentScope, codeStream, valueRequired);
 
 520         // if (valueRequired) {
 
 521         // if (falseLabel == null) {
 
 522         // if (trueLabel != null) {
 
 523         // // implicit falling through the FALSE case
 
 524         // switch (left.implicitConversion >> 4) { // operand runtime type
 
 526         // codeStream.if_icmpeq(trueLabel);
 
 529         // codeStream.fcmpl();
 
 530         // codeStream.ifeq(trueLabel);
 
 533         // codeStream.lcmp();
 
 534         // codeStream.ifeq(trueLabel);
 
 537         // codeStream.dcmpl();
 
 538         // codeStream.ifeq(trueLabel);
 
 541         // codeStream.if_acmpeq(trueLabel);
 
 545         // // implicit falling through the TRUE case
 
 546         // if (trueLabel == null) {
 
 547         // switch (left.implicitConversion >> 4) { // operand runtime type
 
 549         // codeStream.if_icmpne(falseLabel);
 
 552         // codeStream.fcmpl();
 
 553         // codeStream.ifne(falseLabel);
 
 556         // codeStream.lcmp();
 
 557         // codeStream.ifne(falseLabel);
 
 560         // codeStream.dcmpl();
 
 561         // codeStream.ifne(falseLabel);
 
 564         // codeStream.if_acmpne(falseLabel);
 
 567         // // no implicit fall through TRUE/FALSE --> should never occur
 
 571         // codeStream.recordPositionsFrom(pc, this.sourceStart);
 
 573         public boolean isCompactableOperation() {
 
 577         public TypeBinding resolveType(BlockScope scope) {
 
 578                 // always return BooleanBinding
 
 579                 TypeBinding leftType = left.resolveType(scope);
 
 580                 TypeBinding rightType = right.resolveType(scope);
 
 581                 if (leftType == null || rightType == null) {
 
 582                         constant = NotAConstant;
 
 587                 if (leftType.isBaseType() && rightType.isBaseType()) {
 
 588                         // the code is an int
 
 589                         // (cast) left == (cast) rigth --> result
 
 590                         // 0000 0000 0000 0000 0000
 
 591                         // <<16 <<12 <<8 <<4 <<0
 
 592                         int result = ResolveTypeTables[EQUAL_EQUAL][(leftType.id << 4)
 
 594                         left.implicitConversion = result >>> 12;
 
 595                         right.implicitConversion = (result >>> 4) & 0x000FF;
 
 596                         bits |= result & 0xF;
 
 597                         if ((result & 0x0000F) == T_undefined) {
 
 598                                 constant = Constant.NotAConstant;
 
 599                                 scope.problemReporter().invalidOperator(this, leftType,
 
 603                         computeConstant(leftType, rightType);
 
 604                         this.resolvedType = BooleanBinding;
 
 605                         return BooleanBinding;
 
 610                 if (areTypesCastCompatible(scope, rightType, leftType)
 
 611                                 || areTypesCastCompatible(scope, leftType, rightType)) {
 
 612                         // (special case for String)
 
 613                         if ((rightType.id == T_String) && (leftType.id == T_String))
 
 614                                 computeConstant(leftType, rightType);
 
 616                                 constant = NotAConstant;
 
 617                         if (rightType.id == T_String)
 
 618                                 right.implicitConversion = String2String;
 
 619                         if (leftType.id == T_String)
 
 620                                 left.implicitConversion = String2String;
 
 621                         this.resolvedType = BooleanBinding;
 
 622                         return BooleanBinding;
 
 624                 constant = NotAConstant;
 
 625                 scope.problemReporter().notCompatibleTypesError(this, leftType,
 
 630         public void traverse(ASTVisitor visitor, BlockScope scope) {
 
 631                 if (visitor.visit(this, scope)) {
 
 632                         left.traverse(visitor, scope);
 
 633                         right.traverse(visitor, scope);
 
 635                 visitor.endVisit(this, scope);