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