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