1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.ast;
13 import org.eclipse.jdt.core.compiler.CharOperation;
14 import org.eclipse.jdt.internal.compiler.ASTVisitor;
15 import org.eclipse.jdt.internal.compiler.impl.*;
16 import org.eclipse.jdt.internal.compiler.codegen.*;
17 import org.eclipse.jdt.internal.compiler.flow.*;
18 import org.eclipse.jdt.internal.compiler.lookup.*;
20 public class EqualExpression extends BinaryExpression {
22 public EqualExpression(Expression left, Expression right,int operator) {
23 super(left,right,operator);
25 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
26 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
27 if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) {
28 if (left.constant.booleanValue()) { // true == anything
29 // this is equivalent to the right argument inits
30 return right.analyseCode(currentScope, flowContext, flowInfo);
31 } else { // false == anything
32 // this is equivalent to the right argument inits negated
33 return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
36 if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) {
37 if (right.constant.booleanValue()) { // anything == true
38 // this is equivalent to the right argument inits
39 return left.analyseCode(currentScope, flowContext, flowInfo);
40 } else { // anything == false
41 // this is equivalent to the right argument inits negated
42 return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
45 return right.analyseCode(
46 currentScope, flowContext,
47 left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits();
48 } else { //NOT_EQUAL :
49 if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) {
50 if (!left.constant.booleanValue()) { // false != anything
51 // this is equivalent to the right argument inits
52 return right.analyseCode(currentScope, flowContext, flowInfo);
53 } else { // true != anything
54 // this is equivalent to the right argument inits negated
55 return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
58 if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) {
59 if (!right.constant.booleanValue()) { // anything != false
60 // this is equivalent to the right argument inits
61 return left.analyseCode(currentScope, flowContext, flowInfo);
62 } else { // anything != true
63 // this is equivalent to the right argument inits negated
64 return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
67 return right.analyseCode(
68 currentScope, flowContext,
69 left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits();
72 public final boolean areTypesCastCompatible(BlockScope scope, TypeBinding castType, TypeBinding expressionType) {
73 //see specifications 5.5
74 //A more complete version of this method is provided on
75 //CastExpression (it deals with constant and need runtime checkcast)
77 if (castType == expressionType) return true;
79 //========ARRAY===============
80 if (expressionType.isArrayType()) {
81 if (castType.isArrayType()) { //------- (castTb.isArray) expressionTb.isArray -----------
82 TypeBinding expressionEltType = ((ArrayBinding) expressionType).elementsType(scope);
83 if (expressionEltType.isBaseType())
84 // <---stop the recursion-------
85 return ((ArrayBinding) castType).elementsType(scope) == expressionEltType;
86 //recursivly on the elts...
87 return areTypesCastCompatible(scope, ((ArrayBinding) castType).elementsType(scope), expressionEltType);
89 if (castType.isBaseType()) {
92 if (castType.isClass()) { //------(castTb.isClass) expressionTb.isArray ---------------
93 if (castType.id == T_Object)
97 if (castType.isInterface()) { //------- (castTb.isInterface) expressionTb.isArray -----------
98 if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) {
107 //------------(castType) null--------------
108 if (expressionType == NullBinding) {
109 return !castType.isBaseType();
112 //========BASETYPE==============
113 if (expressionType.isBaseType()) {
118 //========REFERENCE TYPE===================
120 if (expressionType.isClass()) {
121 if (castType.isArrayType()) { // ---- (castTb.isArray) expressionTb.isClass -------
122 if (expressionType.id == T_Object)
125 if (castType.isBaseType()) {
128 if (castType.isClass()) { // ----- (castTb.isClass) expressionTb.isClass ------
129 if (expressionType.isCompatibleWith(castType))
132 if (castType.isCompatibleWith(expressionType)) {
138 if (castType.isInterface()) { // ----- (castTb.isInterface) expressionTb.isClass -------
139 if (expressionType.isCompatibleWith(castType))
141 if (!((ReferenceBinding) expressionType).isFinal()) {
144 //no subclass for expressionTb, thus compile-time check is valid
149 if (expressionType.isInterface()) {
150 if (castType.isArrayType()) { // ----- (castTb.isArray) expressionTb.isInterface ------
151 if (expressionType.id == T_JavaLangCloneable || expressionType.id == T_JavaIoSerializable)
152 //potential runtime error
158 if (castType.isBaseType()) {
161 if (castType.isClass()) { // ----- (castTb.isClass) expressionTb.isInterface --------
162 if (castType.id == T_Object)
164 if (((ReferenceBinding) castType).isFinal()) { //no subclass for castTb, thus compile-time check is valid
165 if (castType.isCompatibleWith(expressionType)) {
172 if (castType.isInterface()) { // ----- (castTb.isInterface) expressionTb.isInterface -------
173 if (expressionType.isCompatibleWith(castType))
175 if (!castType.isCompatibleWith(expressionType)) {
176 MethodBinding[] castTbMethods = ((ReferenceBinding) castType).methods();
177 int castTbMethodsLength = castTbMethods.length;
178 MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionType).methods();
179 int expressionTbMethodsLength = expressionTbMethods.length;
180 for (int i = 0; i < castTbMethodsLength; i++) {
181 for (int j = 0; j < expressionTbMethodsLength; j++) {
182 if (CharOperation.equals(castTbMethods[i].selector, expressionTbMethods[j].selector)) {
183 if (castTbMethods[i].returnType != expressionTbMethods[j].returnType) {
184 if (castTbMethods[i].areParametersEqual(expressionTbMethods[j])) {
198 public final void computeConstant(TypeBinding leftType, TypeBinding rightType) {
199 if ((this.left.constant != NotAConstant) && (this.right.constant != NotAConstant)) {
201 Constant.computeConstantOperationEQUAL_EQUAL(
207 if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
208 constant = Constant.fromValue(!constant.booleanValue());
210 this.constant = NotAConstant;
211 // no optimization for null == null
215 * Normal == or != code generation.
217 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
218 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
219 * @param valueRequired boolean
221 public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
223 if (constant != NotAConstant) {
224 int pc = codeStream.position;
226 codeStream.generateConstant(constant, implicitConversion);
227 codeStream.recordPositionsFrom(pc, this.sourceStart);
231 bits |= OnlyValueRequiredMASK;
232 generateOptimizedBoolean(
236 falseLabel = new Label(codeStream),
238 if (falseLabel.hasForwardReferences()) {
240 // comparison is TRUE
241 codeStream.iconst_1();
242 if ((bits & ValueForReturnMASK) != 0){
243 codeStream.ireturn();
244 // comparison is FALSE
246 codeStream.iconst_0();
248 Label endLabel = new Label(codeStream);
249 codeStream.goto_(endLabel);
250 codeStream.decrStackSize(1);
251 // comparison is FALSE
253 codeStream.iconst_0();
262 * Boolean operator code generation
263 * Optimized operations are: == and !=
265 public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
267 if (constant != Constant.NotAConstant) {
268 super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
271 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
272 if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
273 generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
275 generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
278 if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
279 generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
281 generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
286 * Boolean generation for == with boolean operands
288 * Note this code does not optimize conditional constants !!!!
290 public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
292 // optimized cases: true == x, false == x
293 if (left.constant != NotAConstant) {
294 boolean inline = left.constant.booleanValue();
295 right.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
297 } // optimized cases: x == true, x == false
298 if (right.constant != NotAConstant) {
299 boolean inline = right.constant.booleanValue();
300 left.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
304 left.generateCode(currentScope, codeStream, valueRequired);
305 right.generateCode(currentScope, codeStream, valueRequired);
307 if (falseLabel == null) {
308 if (trueLabel != null) {
309 // implicit falling through the FALSE case
310 codeStream.if_icmpeq(trueLabel);
313 // implicit falling through the TRUE case
314 if (trueLabel == null) {
315 codeStream.if_icmpne(falseLabel);
317 // no implicit fall through TRUE/FALSE --> should never occur
321 // reposition the endPC
322 codeStream.updateLastRecordedEndPC(codeStream.position);
325 * Boolean generation for == with non-boolean operands
328 public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
330 int pc = codeStream.position;
332 if ((inline = right.constant) != NotAConstant) {
333 // optimized case: x == 0
334 if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() == 0)) {
335 left.generateCode(currentScope, codeStream, valueRequired);
337 if (falseLabel == null) {
338 if (trueLabel != null) {
339 // implicit falling through the FALSE case
340 codeStream.ifeq(trueLabel);
343 // implicit falling through the TRUE case
344 if (trueLabel == null) {
345 codeStream.ifne(falseLabel);
347 // no implicit fall through TRUE/FALSE --> should never occur
351 codeStream.recordPositionsFrom(pc, this.sourceStart);
355 if ((inline = left.constant) != NotAConstant) {
356 // optimized case: 0 == x
357 if (((left.implicitConversion >> 4) == T_int)
358 && (inline.intValue() == 0)) {
359 right.generateCode(currentScope, codeStream, valueRequired);
361 if (falseLabel == null) {
362 if (trueLabel != null) {
363 // implicit falling through the FALSE case
364 codeStream.ifeq(trueLabel);
367 // implicit falling through the TRUE case
368 if (trueLabel == null) {
369 codeStream.ifne(falseLabel);
371 // no implicit fall through TRUE/FALSE --> should never occur
375 codeStream.recordPositionsFrom(pc, this.sourceStart);
380 // optimized case: x == null
381 if (right instanceof NullLiteral) {
382 if (left instanceof NullLiteral) {
385 if ((bits & OnlyValueRequiredMASK) != 0) {
386 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
387 codeStream.iconst_1();
389 codeStream.iconst_0();
392 if (falseLabel == null) {
393 // implicit falling through the FALSE case
394 if (trueLabel != null) {
395 codeStream.goto_(trueLabel);
401 left.generateCode(currentScope, codeStream, valueRequired);
403 if (falseLabel == null) {
404 if (trueLabel != null) {
405 // implicit falling through the FALSE case
406 codeStream.ifnull(trueLabel);
409 // implicit falling through the TRUE case
410 if (trueLabel == null) {
411 codeStream.ifnonnull(falseLabel);
413 // no implicit fall through TRUE/FALSE --> should never occur
418 codeStream.recordPositionsFrom(pc, this.sourceStart);
420 } else if (left instanceof NullLiteral) { // optimized case: null == x
421 right.generateCode(currentScope, codeStream, valueRequired);
423 if (falseLabel == null) {
424 if (trueLabel != null) {
425 // implicit falling through the FALSE case
426 codeStream.ifnull(trueLabel);
429 // implicit falling through the TRUE case
430 if (trueLabel == null) {
431 codeStream.ifnonnull(falseLabel);
433 // no implicit fall through TRUE/FALSE --> should never occur
437 codeStream.recordPositionsFrom(pc, this.sourceStart);
442 left.generateCode(currentScope, codeStream, valueRequired);
443 right.generateCode(currentScope, codeStream, valueRequired);
445 if (falseLabel == null) {
446 if (trueLabel != null) {
447 // implicit falling through the FALSE case
448 switch (left.implicitConversion >> 4) { // operand runtime type
450 codeStream.if_icmpeq(trueLabel);
454 codeStream.ifeq(trueLabel);
458 codeStream.ifeq(trueLabel);
462 codeStream.ifeq(trueLabel);
465 codeStream.if_acmpeq(trueLabel);
469 // implicit falling through the TRUE case
470 if (trueLabel == null) {
471 switch (left.implicitConversion >> 4) { // operand runtime type
473 codeStream.if_icmpne(falseLabel);
477 codeStream.ifne(falseLabel);
481 codeStream.ifne(falseLabel);
485 codeStream.ifne(falseLabel);
488 codeStream.if_acmpne(falseLabel);
491 // no implicit fall through TRUE/FALSE --> should never occur
495 codeStream.recordPositionsFrom(pc, this.sourceStart);
497 public boolean isCompactableOperation() {
500 public TypeBinding resolveType(BlockScope scope) {
502 boolean leftIsCast, rightIsCast;
503 if ((leftIsCast = left instanceof CastExpression) == true) left.bits |= IgnoreNeedForCastCheckMASK; // will check later on
504 TypeBinding leftType = left.resolveType(scope);
506 if ((rightIsCast = right instanceof CastExpression) == true) right.bits |= IgnoreNeedForCastCheckMASK; // will check later on
507 TypeBinding rightType = right.resolveType(scope);
509 // always return BooleanBinding
510 if (leftType == null || rightType == null){
511 constant = NotAConstant;
516 if (leftType.isBaseType() && rightType.isBaseType()) {
517 // the code is an int
518 // (cast) left == (cast) right --> result
519 // 0000 0000 0000 0000 0000
520 // <<16 <<12 <<8 <<4 <<0
521 int operatorSignature = OperatorSignatures[EQUAL_EQUAL][ (leftType.id << 4) + rightType.id];
522 left.implicitConversion = operatorSignature >>> 12;
523 right.implicitConversion = (operatorSignature >>> 4) & 0x000FF;
524 bits |= operatorSignature & 0xF;
525 if ((operatorSignature & 0x0000F) == T_undefined) {
526 constant = Constant.NotAConstant;
527 scope.problemReporter().invalidOperator(this, leftType, rightType);
530 // check need for operand cast
531 if (leftIsCast || rightIsCast) {
532 CastExpression.checkNeedForArgumentCasts(scope, EQUAL_EQUAL, operatorSignature, left, leftType.id, leftIsCast, right, rightType.id, rightIsCast);
534 computeConstant(leftType, rightType);
535 return this.resolvedType = BooleanBinding;
540 if (areTypesCastCompatible(scope, rightType, leftType) || areTypesCastCompatible(scope, leftType, rightType)) {
541 // (special case for String)
542 if ((rightType.id == T_String) && (leftType.id == T_String)) {
543 computeConstant(leftType, rightType);
545 constant = NotAConstant;
547 if (rightType.id == T_String) {
548 right.implicitConversion = String2String;
550 if (leftType.id == T_String) {
551 left.implicitConversion = String2String;
553 // check need for operand cast
554 boolean unnecessaryLeftCast = (left.bits & UnnecessaryCastMask) != 0;
555 boolean unnecessaryRightCast = (right.bits & UnnecessaryCastMask) != 0;
556 if (unnecessaryLeftCast || unnecessaryRightCast) {
557 TypeBinding alternateLeftType = unnecessaryLeftCast ? ((CastExpression)left).expression.resolvedType : leftType;
558 TypeBinding alternateRightType = unnecessaryRightCast ? ((CastExpression)right).expression.resolvedType : rightType;
559 if (areTypesCastCompatible(scope, alternateLeftType, alternateRightType)
560 || areTypesCastCompatible(scope, alternateRightType, alternateLeftType)) {
561 if (unnecessaryLeftCast) scope.problemReporter().unnecessaryCast((CastExpression)left);
562 if (unnecessaryRightCast) scope.problemReporter().unnecessaryCast((CastExpression)right);
565 return this.resolvedType = BooleanBinding;
567 constant = NotAConstant;
568 scope.problemReporter().notCompatibleTypesError(this, leftType, rightType);
571 public void traverse(ASTVisitor visitor, BlockScope scope) {
572 if (visitor.visit(this, scope)) {
573 left.traverse(visitor, scope);
574 right.traverse(visitor, scope);
576 visitor.endVisit(this, scope);