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