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