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