0caeb81a07a11c20ddffd3cdc792c11bd6014e25
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / EqualExpression.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
12
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.*;
19
20 public class EqualExpression extends BinaryExpression {
21
22 public EqualExpression(Expression left, Expression right,int operator) {
23         super(left,right,operator);
24 }
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();
34                         }
35                 }
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();
43                         }
44                 }
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();
56                         }
57                 }
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();
65                         }
66                 }
67                 return right.analyseCode(
68                         currentScope, flowContext, 
69                         left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits();
70         }
71 }
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)
76
77         if (castType == expressionType) return true;
78
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);
88                 }
89                 if (castType.isBaseType()) {
90                         return false;
91                 }
92                 if (castType.isClass()) { //------(castTb.isClass) expressionTb.isArray --------------- 
93                         if (castType.id == T_Object)
94                                 return true;
95                         return false;
96                 }
97                 if (castType.isInterface()) { //------- (castTb.isInterface) expressionTb.isArray -----------
98                         if (castType.id == T_JavaLangCloneable || castType.id == T_JavaIoSerializable) {
99                                 return true;
100                         }
101                         return false;
102                 }
103
104                 return false;
105         }
106
107         //------------(castType) null--------------
108         if (expressionType == NullBinding) {
109                 return !castType.isBaseType();
110         }
111
112         //========BASETYPE==============
113         if (expressionType.isBaseType()) {
114                 return false;
115         }
116
117
118         //========REFERENCE TYPE===================
119
120         if (expressionType.isClass()) {
121                 if (castType.isArrayType()) { // ---- (castTb.isArray) expressionTb.isClass -------
122                         if (expressionType.id == T_Object)
123                                 return true;
124                 }
125                 if (castType.isBaseType()) {
126                         return false;
127                 }
128                 if (castType.isClass()) { // ----- (castTb.isClass) expressionTb.isClass ------ 
129                         if (expressionType.isCompatibleWith(castType))
130                                 return true;
131                         else {
132                                 if (castType.isCompatibleWith(expressionType)) {
133                                         return true;
134                                 }
135                                 return false;
136                         }
137                 }
138                 if (castType.isInterface()) { // ----- (castTb.isInterface) expressionTb.isClass -------  
139                         if (expressionType.isCompatibleWith(castType))
140                                 return true;                
141                         if (!((ReferenceBinding) expressionType).isFinal()) {
142                                 return true;
143                         }
144                         //no subclass for expressionTb, thus compile-time check is valid                        
145                 }
146
147                 return false;
148         }
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
153                                 {
154                                 return true;
155                         }
156                         return false;
157                 }
158                 if (castType.isBaseType()) {
159                         return false;
160                 }
161                 if (castType.isClass()) { // ----- (castTb.isClass) expressionTb.isInterface --------
162                         if (castType.id == T_Object)
163                                 return true;
164                         if (((ReferenceBinding) castType).isFinal()) { //no subclass for castTb, thus compile-time check is valid
165                                 if (castType.isCompatibleWith(expressionType)) {
166                                         return true;
167                                 }
168                                 return false;
169                         }
170                         return true;
171                 }
172                 if (castType.isInterface()) { // ----- (castTb.isInterface) expressionTb.isInterface -------
173                         if (expressionType.isCompatibleWith(castType))
174                                 return true;
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])) {
185                                                                         return false;
186                                                                 }
187                                                         }
188                                                 }
189                                         }
190                                 }
191                         }
192                         return true;
193                 }
194                 return false;
195         }
196         return false;
197 }
198 public final void computeConstant(TypeBinding leftType, TypeBinding rightType) {
199         if ((this.left.constant != NotAConstant) && (this.right.constant != NotAConstant)) {
200                 this.constant =
201                         Constant.computeConstantOperationEQUAL_EQUAL(
202                                 left.constant,
203                                 leftType.id,
204                                 EQUAL_EQUAL,
205                                 right.constant,
206                                 rightType.id);
207                 if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
208                         constant = Constant.fromValue(!constant.booleanValue());
209         } else {
210                 this.constant = NotAConstant;
211                 // no optimization for null == null
212         }
213 }
214 /**
215  * Normal == or != code generation.
216  *
217  * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
218  * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
219  * @param valueRequired boolean
220  */
221 public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
222
223         if (constant != NotAConstant) {
224                 int pc = codeStream.position;
225                 if (valueRequired) 
226                         codeStream.generateConstant(constant, implicitConversion);
227                 codeStream.recordPositionsFrom(pc, this.sourceStart);
228                 return;
229         }
230         Label falseLabel;
231         bits |= OnlyValueRequiredMASK;
232         generateOptimizedBoolean(
233                 currentScope, 
234                 codeStream, 
235                 null, 
236                 falseLabel = new Label(codeStream), 
237                 valueRequired);
238         if (falseLabel.hasForwardReferences()) {
239                 if (valueRequired){
240                         // comparison is TRUE 
241                         codeStream.iconst_1();
242                         if ((bits & ValueForReturnMASK) != 0){
243                                 codeStream.ireturn();
244                                 // comparison is FALSE
245                                 falseLabel.place();
246                                 codeStream.iconst_0();
247                         } else {
248                                 Label endLabel = new Label(codeStream);
249                                 codeStream.goto_(endLabel);
250                                 codeStream.decrStackSize(1);
251                                 // comparison is FALSE
252                                 falseLabel.place();
253                                 codeStream.iconst_0();
254                                 endLabel.place();
255                         }
256                 } else {
257                         falseLabel.place();
258                 }       
259         }
260 }
261 /**
262  * Boolean operator code generation
263  *      Optimized operations are: == and !=
264  */
265 public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
266
267         if (constant != Constant.NotAConstant) {
268                 super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
269                 return;
270         }
271         if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
272                 if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
273                         generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
274                 } else {
275                         generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
276                 }
277         } else {
278                 if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
279                         generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
280                 } else {
281                         generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
282                 }
283         }
284 }
285 /**
286  * Boolean generation for == with boolean operands
287  *
288  * Note this code does not optimize conditional constants !!!!
289  */
290 public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
291
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);
296                 return;
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);
301                 return;
302         }
303         // default case
304         left.generateCode(currentScope, codeStream, valueRequired);
305         right.generateCode(currentScope, codeStream, valueRequired);
306         if (valueRequired) {
307                 if (falseLabel == null) {
308                         if (trueLabel != null) {
309                                 // implicit falling through the FALSE case
310                                 codeStream.if_icmpeq(trueLabel);
311                         }
312                 } else {
313                         // implicit falling through the TRUE case
314                         if (trueLabel == null) {
315                                 codeStream.if_icmpne(falseLabel);
316                         } else {
317                                 // no implicit fall through TRUE/FALSE --> should never occur
318                         }
319                 }
320         }
321         // reposition the endPC
322         codeStream.updateLastRecordedEndPC(codeStream.position);                                        
323 }
324 /**
325  * Boolean generation for == with non-boolean operands
326  *
327  */
328 public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
329
330         int pc = codeStream.position;
331         Constant inline;
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);
336                         if (valueRequired) {
337                                 if (falseLabel == null) {
338                                         if (trueLabel != null) {
339                                                 // implicit falling through the FALSE case
340                                                 codeStream.ifeq(trueLabel);
341                                         }
342                                 } else {
343                                         // implicit falling through the TRUE case
344                                         if (trueLabel == null) {
345                                                 codeStream.ifne(falseLabel);
346                                         } else {
347                                                 // no implicit fall through TRUE/FALSE --> should never occur
348                                         }
349                                 }
350                         }
351                         codeStream.recordPositionsFrom(pc, this.sourceStart);
352                         return;
353                 }
354         }
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);
360                         if (valueRequired) {
361                                 if (falseLabel == null) {
362                                         if (trueLabel != null) {
363                                                 // implicit falling through the FALSE case
364                                                 codeStream.ifeq(trueLabel);
365                                         }
366                                 } else {
367                                         // implicit falling through the TRUE case
368                                         if (trueLabel == null) {
369                                                 codeStream.ifne(falseLabel);
370                                         } else {
371                                                 // no implicit fall through TRUE/FALSE --> should never occur
372                                         }
373                                 }
374                         }
375                         codeStream.recordPositionsFrom(pc, this.sourceStart);
376                         return;
377                 }
378         }
379         // null cases
380         // optimized case: x == null
381         if (right instanceof NullLiteral) {
382                 if (left instanceof NullLiteral) {
383                         // null == null
384                         if (valueRequired) {
385                                         if ((bits & OnlyValueRequiredMASK) != 0) {
386                                                 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
387                                                         codeStream.iconst_1();
388                                                 } else {
389                                                         codeStream.iconst_0();
390                                                 }
391                                         } else {
392                                                 if (falseLabel == null) {
393                                                         // implicit falling through the FALSE case
394                                                         if (trueLabel != null) {
395                                                                 codeStream.goto_(trueLabel);
396                                                         }
397                                                 }
398                                 }
399                         }
400                 } else {
401                         left.generateCode(currentScope, codeStream, valueRequired);
402                         if (valueRequired) {
403                                 if (falseLabel == null) {
404                                         if (trueLabel != null) {
405                                                 // implicit falling through the FALSE case
406                                                 codeStream.ifnull(trueLabel);
407                                         }
408                                 } else {
409                                         // implicit falling through the TRUE case
410                                         if (trueLabel == null) {
411                                                 codeStream.ifnonnull(falseLabel);
412                                         } else {
413                                                 // no implicit fall through TRUE/FALSE --> should never occur
414                                         }
415                                 }
416                         }
417                 }
418                 codeStream.recordPositionsFrom(pc, this.sourceStart);
419                 return;
420         } else if (left instanceof NullLiteral) { // optimized case: null == x
421                 right.generateCode(currentScope, codeStream, valueRequired);
422                 if (valueRequired) {
423                         if (falseLabel == null) {
424                                 if (trueLabel != null) {
425                                         // implicit falling through the FALSE case
426                                         codeStream.ifnull(trueLabel);
427                                 }
428                         } else {
429                                 // implicit falling through the TRUE case
430                                 if (trueLabel == null) {
431                                         codeStream.ifnonnull(falseLabel);
432                                 } else {
433                                         // no implicit fall through TRUE/FALSE --> should never occur
434                                 }
435                         }
436                 }
437                 codeStream.recordPositionsFrom(pc, this.sourceStart);
438                 return;
439         }
440
441         // default case
442         left.generateCode(currentScope, codeStream, valueRequired);
443         right.generateCode(currentScope, codeStream, valueRequired);
444         if (valueRequired) {
445                 if (falseLabel == null) {
446                         if (trueLabel != null) {
447                                 // implicit falling through the FALSE case
448                                 switch (left.implicitConversion >> 4) { // operand runtime type
449                                         case T_int :
450                                                 codeStream.if_icmpeq(trueLabel);
451                                                 break;
452                                         case T_float :
453                                                 codeStream.fcmpl();
454                                                 codeStream.ifeq(trueLabel);
455                                                 break;
456                                         case T_long :
457                                                 codeStream.lcmp();
458                                                 codeStream.ifeq(trueLabel);
459                                                 break;
460                                         case T_double :
461                                                 codeStream.dcmpl();
462                                                 codeStream.ifeq(trueLabel);
463                                                 break;
464                                         default :
465                                                 codeStream.if_acmpeq(trueLabel);
466                                 }
467                         }
468                 } else {
469                         // implicit falling through the TRUE case
470                         if (trueLabel == null) {
471                                 switch (left.implicitConversion >> 4) { // operand runtime type
472                                         case T_int :
473                                                 codeStream.if_icmpne(falseLabel);
474                                                 break;
475                                         case T_float :
476                                                 codeStream.fcmpl();
477                                                 codeStream.ifne(falseLabel);
478                                                 break;
479                                         case T_long :
480                                                 codeStream.lcmp();
481                                                 codeStream.ifne(falseLabel);
482                                                 break;
483                                         case T_double :
484                                                 codeStream.dcmpl();
485                                                 codeStream.ifne(falseLabel);
486                                                 break;
487                                         default :
488                                                 codeStream.if_acmpne(falseLabel);
489                                 }
490                         } else {
491                                 // no implicit fall through TRUE/FALSE --> should never occur
492                         }
493                 }
494         }
495         codeStream.recordPositionsFrom(pc, this.sourceStart);
496 }
497 public boolean isCompactableOperation() {
498         return false;
499 }
500 public TypeBinding resolveType(BlockScope scope) {
501
502                 boolean leftIsCast, rightIsCast;
503                 if ((leftIsCast = left instanceof CastExpression) == true) left.bits |= IgnoreNeedForCastCheckMASK; // will check later on
504                 TypeBinding leftType = left.resolveType(scope);
505
506                 if ((rightIsCast = right instanceof CastExpression) == true) right.bits |= IgnoreNeedForCastCheckMASK; // will check later on
507                 TypeBinding rightType = right.resolveType(scope);
508
509         // always return BooleanBinding
510         if (leftType == null || rightType == null){
511                 constant = NotAConstant;                
512                 return null;
513         }
514
515         // both base type
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);
528                         return null;
529                 }
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);
533                 }
534                 computeConstant(leftType, rightType);
535                 return this.resolvedType = BooleanBinding;
536         }
537
538         // Object references 
539         // spec 15.20.3
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);
544                 } else {
545                         constant = NotAConstant;
546                 }
547                 if (rightType.id == T_String) {
548                         right.implicitConversion = String2String;
549                 }
550                 if (leftType.id == T_String) {
551                         left.implicitConversion = String2String;
552                 }
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);
563                         }
564                 }
565                 return this.resolvedType = BooleanBinding;
566         }
567         constant = NotAConstant;
568         scope.problemReporter().notCompatibleTypesError(this, leftType, rightType);
569         return null;
570 }
571 public void traverse(ASTVisitor visitor, BlockScope scope) {
572         if (visitor.visit(this, scope)) {
573                 left.traverse(visitor, scope);
574                 right.traverse(visitor, scope);
575         }
576         visitor.endVisit(this, scope);
577 }
578 }