*** empty log message ***
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / ast / EqualExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3  * All rights reserved. This program and the accompanying materials 
4  * are made available under the terms of the Common Public License v0.5 
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/cpl-v05.html
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
12
13 import net.sourceforge.phpdt.internal.compiler.IAbstractSyntaxTreeVisitor;
14 import net.sourceforge.phpdt.internal.compiler.codegen.CodeStream;
15 import net.sourceforge.phpdt.internal.compiler.codegen.Label;
16 import net.sourceforge.phpdt.internal.compiler.flow.FlowContext;
17 import net.sourceforge.phpdt.internal.compiler.flow.FlowInfo;
18 import net.sourceforge.phpdt.internal.compiler.impl.Constant;
19 import net.sourceforge.phpdt.internal.compiler.impl.NullConstant;
20 import net.sourceforge.phpdt.internal.compiler.lookup.ArrayBinding;
21 import net.sourceforge.phpdt.internal.compiler.lookup.BlockScope;
22 import net.sourceforge.phpdt.internal.compiler.lookup.MethodBinding;
23 import net.sourceforge.phpdt.internal.compiler.lookup.ReferenceBinding;
24 import net.sourceforge.phpdt.internal.compiler.lookup.Scope;
25 import net.sourceforge.phpdt.internal.compiler.lookup.TypeBinding;
26
27 public class EqualExpression extends BinaryExpression {
28
29 public EqualExpression(Expression left, Expression right,int operator) {
30         super(left,right,operator);
31 }
32 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
33         if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
34                 if ((left.constant != NotAConstant) && (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, flowInfo);
38                         } else { // false == anything
39                                 //  this is equivalent to the right argument inits negated
40                                 return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
41                         }
42                 }
43                 if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) {
44                         if (right.constant.booleanValue()) { //  anything == true
45                                 //  this is equivalent to the right argument inits 
46                                 return left.analyseCode(currentScope, flowContext, flowInfo);
47                         } else { // anything == false
48                                 //  this is equivalent to the right argument inits negated
49                                 return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
50                         }
51                 }
52                 return right.analyseCode(
53                         currentScope, flowContext, 
54                         left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits();
55         } else { //NOT_EQUAL :
56                 if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) {
57                         if (!left.constant.booleanValue()) { //  false != anything
58                                 //  this is equivalent to the right argument inits 
59                                 return right.analyseCode(currentScope, flowContext, flowInfo);
60                         } else { // true != anything
61                                 //  this is equivalent to the right argument inits negated
62                                 return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
63                         }
64                 }
65                 if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) {
66                         if (!right.constant.booleanValue()) { //  anything != false
67                                 //  this is equivalent to the right argument inits 
68                                 return left.analyseCode(currentScope, flowContext, flowInfo);
69                         } else { // anything != true
70                                 //  this is equivalent to the right argument inits negated
71                                 return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
72                         }
73                 }
74                 return right.analyseCode(
75                         currentScope, flowContext, 
76                         left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits();
77         }
78 }
79 public final boolean areTypesCastCompatible(BlockScope scope, TypeBinding castTb, TypeBinding expressionTb) {
80         //see specifications p.68
81         //A more complete version of this method is provided on
82         //CastExpression (it deals with constant and need runtime checkcast)
83
84
85         //========ARRAY===============
86         if (expressionTb.isArrayType()) {
87                 if (castTb.isArrayType()) { //------- (castTb.isArray) expressionTb.isArray -----------
88                         TypeBinding expressionEltTb = ((ArrayBinding) expressionTb).elementsType(scope);
89                         if (expressionEltTb.isBaseType())
90                                 // <---stop the recursion------- 
91                                 return ((ArrayBinding) castTb).elementsType(scope) == expressionEltTb;
92                         //recursivly on the elts...
93                         return areTypesCastCompatible(scope, ((ArrayBinding) castTb).elementsType(scope), expressionEltTb);
94                 }
95                 if (castTb.isBaseType()) {
96                         return false;
97                 }
98                 if (castTb.isClass()) { //------(castTb.isClass) expressionTb.isArray ---------------   
99                         if (scope.isJavaLangObject(castTb))
100                                 return true;
101                         return false;
102                 }
103                 if (castTb.isInterface()) { //------- (castTb.isInterface) expressionTb.isArray -----------
104                         if (scope.isJavaLangCloneable(castTb) || scope.isJavaIoSerializable(castTb)) {
105                                 return true;
106                         }
107                         return false;
108                 }
109
110                 return false;
111         }
112
113         //------------(castType) null--------------
114         if (expressionTb == NullBinding) {
115                 return !castTb.isBaseType();
116         }
117
118         //========BASETYPE==============
119         if (expressionTb.isBaseType()) {
120                 return false;
121         }
122
123
124         //========REFERENCE TYPE===================
125
126         if (expressionTb.isClass()) {
127                 if (castTb.isArrayType()) { // ---- (castTb.isArray) expressionTb.isClass -------
128                         if (scope.isJavaLangObject(expressionTb))
129                                 return true;
130                 }
131                 if (castTb.isBaseType()) {
132                         return false;
133                 }
134                 if (castTb.isClass()) { // ----- (castTb.isClass) expressionTb.isClass ------ 
135                         if (BlockScope.areTypesCompatible(expressionTb, castTb))
136                                 return true;
137                         else {
138                                 if (BlockScope.areTypesCompatible(castTb, expressionTb)) {
139                                         return true;
140                                 }
141                                 return false;
142                         }
143                 }
144                 if (castTb.isInterface()) { // ----- (castTb.isInterface) expressionTb.isClass -------  
145                         if (((ReferenceBinding) expressionTb).isFinal()) { //no subclass for expressionTb, thus compile-time check is valid
146                                 if (BlockScope.areTypesCompatible(expressionTb, castTb))
147                                         return true;
148                                 return false;
149                         } else {
150                                 return true;
151                         }
152                 }
153
154                 return false;
155         }
156         if (expressionTb.isInterface()) {
157                 if (castTb.isArrayType()) { // ----- (castTb.isArray) expressionTb.isInterface ------
158                         if (scope.isJavaLangCloneable(expressionTb) || scope.isJavaIoSerializable(expressionTb))
159                                 //potential runtime error
160                                 {
161                                 return true;
162                         }
163                         return false;
164                 }
165                 if (castTb.isBaseType()) {
166                         return false;
167                 }
168                 if (castTb.isClass()) { // ----- (castTb.isClass) expressionTb.isInterface --------
169                         if (scope.isJavaLangObject(castTb))
170                                 return true;
171                         if (((ReferenceBinding) castTb).isFinal()) { //no subclass for castTb, thus compile-time check is valid
172                                 if (BlockScope.areTypesCompatible(castTb, expressionTb)) {
173                                         return true;
174                                 }
175                                 return false;
176                         }
177                         return true;
178                 }
179                 if (castTb.isInterface()) { // ----- (castTb.isInterface) expressionTb.isInterface -------
180                         if (castTb != expressionTb && (Scope.compareTypes(castTb, expressionTb) == NotRelated)) {
181                                 MethodBinding[] castTbMethods = ((ReferenceBinding) castTb).methods();
182                                 int castTbMethodsLength = castTbMethods.length;
183                                 MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionTb).methods();
184                                 int expressionTbMethodsLength = expressionTbMethods.length;
185                                 for (int i = 0; i < castTbMethodsLength; i++) {
186                                         for (int j = 0; j < expressionTbMethodsLength; j++) {
187                                                 if (castTbMethods[i].selector == expressionTbMethods[j].selector) {
188                                                         if (castTbMethods[i].returnType != expressionTbMethods[j].returnType) {
189                                                                 if (castTbMethods[i].areParametersEqual(expressionTbMethods[j])) {
190                                                                         return false;
191                                                                 }
192                                                         }
193                                                 }
194                                         }
195                                 }
196                         }
197                         return true;
198                 }
199
200                 return false;
201         }
202
203         return false;
204 }
205 public final void computeConstant(TypeBinding leftTb, TypeBinding rightTb) {
206         if ((left.constant != NotAConstant) && (right.constant != NotAConstant)) {
207                 constant =
208                         Constant.computeConstantOperationEQUAL_EQUAL(
209                                 left.constant,
210                                 leftTb.id,
211                                 EQUAL_EQUAL,
212                                 right.constant,
213                                 rightTb.id);
214                 if (((bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
215                         constant = Constant.fromValue(!constant.booleanValue());
216         } else {
217                 constant = NotAConstant;
218         }
219 }
220 /**
221  * Normal == or != code generation.
222  *
223  * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
224  * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
225  * @param valueRequired boolean
226  */
227 public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
228
229         if (constant != NotAConstant) {
230                 int pc = codeStream.position;
231                 if (valueRequired) 
232                         codeStream.generateConstant(constant, implicitConversion);
233                 codeStream.recordPositionsFrom(pc, this.sourceStart);
234                 return;
235         }
236         Label falseLabel;
237         generateOptimizedBoolean(
238                 currentScope, 
239                 codeStream, 
240                 null, 
241                 falseLabel = new Label(codeStream), 
242                 valueRequired);
243         if (falseLabel.hasForwardReferences()) {
244                 if (valueRequired){
245                         // comparison is TRUE 
246                         codeStream.iconst_1();
247                         if ((bits & ValueForReturnMASK) != 0){
248                                 codeStream.ireturn();
249                                 // comparison is FALSE
250                                 falseLabel.place();
251                                 codeStream.iconst_0();
252                         } else {
253                                 Label endLabel = new Label(codeStream);
254                                 codeStream.goto_(endLabel);
255                                 codeStream.decrStackSize(1);
256                                 // comparison is FALSE
257                                 falseLabel.place();
258                                 codeStream.iconst_0();
259                                 endLabel.place();
260                         }
261                 } else {
262                         falseLabel.place();
263                 }       
264         }       
265 }
266 /**
267  * Boolean operator code generation
268  *      Optimized operations are: == and !=
269  */
270 public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
271         if ((constant != Constant.NotAConstant) && (constant.typeID() == T_boolean)) {
272                 super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
273                 return;
274         }
275         int pc = codeStream.position;
276         if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
277                 if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
278                         generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
279                 } else {
280                         generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
281                 }
282         } else {
283                 if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
284                         generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
285                 } else {
286                         generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
287                 }
288         }
289         codeStream.recordPositionsFrom(pc, this.sourceStart);
290 }
291 /**
292  * Boolean generation for == with boolean operands
293  *
294  * Note this code does not optimize conditional constants !!!!
295  */
296 public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
297         int pc = codeStream.position;
298         // optimized cases: true == x, false == x
299         if (left.constant != NotAConstant) {
300                 boolean inline = left.constant.booleanValue();
301                 right.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
302                 codeStream.recordPositionsFrom(pc, this.sourceStart);
303                 return;
304         } // optimized cases: x == true, x == false
305         if (right.constant != NotAConstant) {
306                 boolean inline = right.constant.booleanValue();
307                 left.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
308                 codeStream.recordPositionsFrom(pc, this.sourceStart);
309                 return;
310         }
311         // default case
312         left.generateCode(currentScope, codeStream, valueRequired);
313         right.generateCode(currentScope, codeStream, valueRequired);
314         if (valueRequired) {
315                 if (falseLabel == null) {
316                         if (trueLabel != null) {
317                                 // implicit falling through the FALSE case
318                                 codeStream.if_icmpeq(trueLabel);
319                         }
320                 } else {
321                         // implicit falling through the TRUE case
322                         if (trueLabel == null) {
323                                 codeStream.if_icmpne(falseLabel);
324                         } else {
325                                 // no implicit fall through TRUE/FALSE --> should never occur
326                         }
327                 }
328         }
329         codeStream.recordPositionsFrom(pc, this.sourceStart);
330 }
331 /**
332  * Boolean generation for == with non-boolean operands
333  *
334  */
335 public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
336         int pc = codeStream.position;
337         Constant inline;
338         if ((inline = right.constant) != NotAConstant) {
339                 // optimized case: x == null
340                 if (right.constant == NullConstant.Default) {
341                         left.generateCode(currentScope, codeStream, valueRequired);
342                         if (valueRequired) {
343                                 if (falseLabel == null) {
344                                         if (trueLabel != null) {
345                                                 // implicit falling through the FALSE case
346                                                 codeStream.ifnull(trueLabel);
347                                         }
348                                 } else {
349                                         // implicit falling through the TRUE case
350                                         if (trueLabel == null) {
351                                                 codeStream.ifnonnull(falseLabel);
352                                         } else {
353                                                 // no implicit fall through TRUE/FALSE --> should never occur
354                                         }
355                                 }
356                         }
357                         codeStream.recordPositionsFrom(pc, this.sourceStart);
358                         return;
359                 }
360                 // optimized case: x == 0
361                 if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() == 0)) {
362                         left.generateCode(currentScope, codeStream, valueRequired);
363                         if (valueRequired) {
364                                 if (falseLabel == null) {
365                                         if (trueLabel != null) {
366                                                 // implicit falling through the FALSE case
367                                                 codeStream.ifeq(trueLabel);
368                                         }
369                                 } else {
370                                         // implicit falling through the TRUE case
371                                         if (trueLabel == null) {
372                                                 codeStream.ifne(falseLabel);
373                                         } else {
374                                                 // no implicit fall through TRUE/FALSE --> should never occur
375                                         }
376                                 }
377                         }
378                         codeStream.recordPositionsFrom(pc, this.sourceStart);
379                         return;
380                 }
381         }
382         if ((inline = left.constant) != NotAConstant) {
383                 // optimized case: null == x
384                 if (left.constant == NullConstant.Default) {
385                         right.generateCode(currentScope, codeStream, valueRequired);
386                         if (valueRequired) {
387                                 if (falseLabel == null) {
388                                         if (trueLabel != null) {
389                                                 // implicit falling through the FALSE case
390                                                 codeStream.ifnull(trueLabel);
391                                         }
392                                 } else {
393                                         // implicit falling through the TRUE case
394                                         if (trueLabel == null) {
395                                                 codeStream.ifnonnull(falseLabel);
396                                         } else {
397                                                 // no implicit fall through TRUE/FALSE --> should never occur
398                                         }
399                                 }
400                         }
401                         codeStream.recordPositionsFrom(pc, this.sourceStart);
402                         return;
403                 }
404                 // optimized case: 0 == x
405                 if (((left.implicitConversion >> 4) == T_int)
406                         && (inline.intValue() == 0)) {
407                         right.generateCode(currentScope, codeStream, valueRequired);
408                         if (valueRequired) {
409                                 if (falseLabel == null) {
410                                         if (trueLabel != null) {
411                                                 // implicit falling through the FALSE case
412                                                 codeStream.ifeq(trueLabel);
413                                         }
414                                 } else {
415                                         // implicit falling through the TRUE case
416                                         if (trueLabel == null) {
417                                                 codeStream.ifne(falseLabel);
418                                         } else {
419                                                 // no implicit fall through TRUE/FALSE --> should never occur
420                                         }
421                                 }
422                         }
423                         codeStream.recordPositionsFrom(pc, this.sourceStart);
424                         return;
425                 }
426         }
427         // default case
428         left.generateCode(currentScope, codeStream, valueRequired);
429         right.generateCode(currentScope, codeStream, valueRequired);
430         if (valueRequired) {
431                 if (falseLabel == null) {
432                         if (trueLabel != null) {
433                                 // implicit falling through the FALSE case
434                                 switch (left.implicitConversion >> 4) { // operand runtime type
435                                         case T_int :
436                                                 codeStream.if_icmpeq(trueLabel);
437                                                 break;
438                                         case T_float :
439                                                 codeStream.fcmpl();
440                                                 codeStream.ifeq(trueLabel);
441                                                 break;
442                                         case T_long :
443                                                 codeStream.lcmp();
444                                                 codeStream.ifeq(trueLabel);
445                                                 break;
446                                         case T_double :
447                                                 codeStream.dcmpl();
448                                                 codeStream.ifeq(trueLabel);
449                                                 break;
450                                         default :
451                                                 codeStream.if_acmpeq(trueLabel);
452                                 }
453                         }
454                 } else {
455                         // implicit falling through the TRUE case
456                         if (trueLabel == null) {
457                                 switch (left.implicitConversion >> 4) { // operand runtime type
458                                         case T_int :
459                                                 codeStream.if_icmpne(falseLabel);
460                                                 break;
461                                         case T_float :
462                                                 codeStream.fcmpl();
463                                                 codeStream.ifne(falseLabel);
464                                                 break;
465                                         case T_long :
466                                                 codeStream.lcmp();
467                                                 codeStream.ifne(falseLabel);
468                                                 break;
469                                         case T_double :
470                                                 codeStream.dcmpl();
471                                                 codeStream.ifne(falseLabel);
472                                                 break;
473                                         default :
474                                                 codeStream.if_acmpne(falseLabel);
475                                 }
476                         } else {
477                                 // no implicit fall through TRUE/FALSE --> should never occur
478                         }
479                 }
480         }
481         codeStream.recordPositionsFrom(pc, this.sourceStart);
482 }
483 public boolean isCompactableOperation() {
484         return false;
485 }
486 public TypeBinding resolveType(BlockScope scope) {
487         // always return BooleanBinding
488         TypeBinding leftTb = left.resolveType(scope);
489         TypeBinding rightTb = right.resolveType(scope);
490         if (leftTb == null || rightTb == null){
491                 constant = NotAConstant;                
492                 return null;
493         }
494
495         // both base type
496         if (leftTb.isBaseType() && rightTb.isBaseType()) {
497                 // the code is an int
498                 // (cast)  left   == (cast)  rigth --> result
499                 //  0000   0000       0000   0000      0000
500                 //  <<16   <<12       <<8    <<4       <<0
501                 int result = ResolveTypeTables[EQUAL_EQUAL][ (leftTb.id << 4) + rightTb.id];
502                 left.implicitConversion = result >>> 12;
503                 right.implicitConversion = (result >>> 4) & 0x000FF;
504                 bits |= result & 0xF;           
505                 if ((result & 0x0000F) == T_undefined) {
506                         constant = Constant.NotAConstant;
507                         scope.problemReporter().invalidOperator(this, leftTb, rightTb);
508                         return null;
509                 }
510                 computeConstant(leftTb, rightTb);
511                 this.typeBinding = BooleanBinding;
512                 return BooleanBinding;
513         }
514
515         // Object references 
516         // spec 15.20.3
517         if (areTypesCastCompatible(scope, rightTb, leftTb) || areTypesCastCompatible(scope, leftTb, rightTb)) {
518                 // (special case for String)
519                 if ((rightTb.id == T_String) && (leftTb.id == T_String))
520                         computeConstant(leftTb, rightTb);
521                 else
522                         constant = NotAConstant;
523                 if (rightTb.id == T_String)
524                         right.implicitConversion = String2String;
525                 if (leftTb.id == T_String)
526                         left.implicitConversion = String2String;
527                 this.typeBinding = BooleanBinding;
528                 return BooleanBinding;
529         }
530         constant = NotAConstant;
531         scope.problemReporter().notCompatibleTypesError(this, leftTb, rightTb);
532         return null;
533 }
534 public void traverse(IAbstractSyntaxTreeVisitor visitor, BlockScope scope) {
535         if (visitor.visit(this, scope)) {
536                 left.traverse(visitor, scope);
537                 right.traverse(visitor, scope);
538         }
539         visitor.endVisit(this, scope);
540 }
541 }