added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / src / org / eclipse / jdt / internal / compiler / ast / EqualExpression.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 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 org.eclipse.jdt.internal.compiler.ast;
12
13 import org.eclipse.jdt.internal.compiler.ASTVisitor;
14 import org.eclipse.jdt.internal.compiler.impl.*;
15 import org.eclipse.jdt.internal.compiler.codegen.*;
16 import org.eclipse.jdt.internal.compiler.flow.*;
17 import org.eclipse.jdt.internal.compiler.lookup.*;
18
19 public class EqualExpression extends BinaryExpression {
20
21         public EqualExpression(Expression left, Expression right,int operator) {
22                 super(left,right,operator);
23         }
24         public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
25                 
26                 LocalVariableBinding local = this.left.localVariableBinding();
27                 if (local != null) {
28                         checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, right.nullStatus(flowInfo), this.left);
29                 }
30                 local = this.right.localVariableBinding();
31                 if (local != null) {
32                         checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, left.nullStatus(flowInfo), this.right);
33                 }
34         }
35         private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) {
36                 switch (nullStatus) {
37                         case FlowInfo.NULL :
38                                 flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NULL, flowInfo);
39                                 if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
40                                         initsWhenTrue.markAsDefinitelyNull(local); // from thereon it is set
41                                         initsWhenFalse.markAsDefinitelyNonNull(local); // from thereon it is set
42                                 } else {
43                                         initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set
44                                         initsWhenFalse.markAsDefinitelyNull(local); // from thereon it is set
45                                 }
46                                 break;
47                         case FlowInfo.NON_NULL :
48                                 flowContext.recordUsingNullReference(scope, local, reference, FlowInfo.NON_NULL, flowInfo);
49                                 if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
50                                         initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set
51                                 }
52                                 break;
53                 }       
54         }
55         
56         public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
57                 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
58                         if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) {
59                                 if (left.constant.booleanValue()) { //  true == anything
60                                         //  this is equivalent to the right argument inits 
61                                         return right.analyseCode(currentScope, flowContext, flowInfo);
62                                 } else { // false == anything
63                                         //  this is equivalent to the right argument inits negated
64                                         return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
65                                 }
66                         }
67                         if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) {
68                                 if (right.constant.booleanValue()) { //  anything == true
69                                         //  this is equivalent to the right argument inits 
70                                         return left.analyseCode(currentScope, flowContext, flowInfo);
71                                 } else { // anything == false
72                                         //  this is equivalent to the right argument inits negated
73                                         return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
74                                 }
75                         }
76                         return right.analyseCode(
77                                 currentScope, flowContext, 
78                                 left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits();
79                 } else { //NOT_EQUAL :
80                         if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) {
81                                 if (!left.constant.booleanValue()) { //  false != anything
82                                         //  this is equivalent to the right argument inits 
83                                         return right.analyseCode(currentScope, flowContext, flowInfo);
84                                 } else { // true != anything
85                                         //  this is equivalent to the right argument inits negated
86                                         return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
87                                 }
88                         }
89                         if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) {
90                                 if (!right.constant.booleanValue()) { //  anything != false
91                                         //  this is equivalent to the right argument inits 
92                                         return left.analyseCode(currentScope, flowContext, flowInfo);
93                                 } else { // anything != true
94                                         //  this is equivalent to the right argument inits negated
95                                         return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
96                                 }
97                         }
98                         return right.analyseCode(
99                                 currentScope, flowContext, 
100                                 left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits();
101                 }
102         }
103         
104         public final void computeConstant(TypeBinding leftType, TypeBinding rightType) {
105                 if ((this.left.constant != NotAConstant) && (this.right.constant != NotAConstant)) {
106                         this.constant =
107                                 Constant.computeConstantOperationEQUAL_EQUAL(
108                                         left.constant,
109                                         leftType.id,
110                                         right.constant,
111                                         rightType.id);
112                         if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
113                                 constant = Constant.fromValue(!constant.booleanValue());
114                 } else {
115                         this.constant = NotAConstant;
116                         // no optimization for null == null
117                 }
118         }
119         /**
120          * Normal == or != code generation.
121          *
122          * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
123          * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
124          * @param valueRequired boolean
125          */
126         public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
127         
128                 if (constant != NotAConstant) {
129                         int pc = codeStream.position;
130                         if (valueRequired) 
131                                 codeStream.generateConstant(constant, implicitConversion);
132                         codeStream.recordPositionsFrom(pc, this.sourceStart);
133                         return;
134                 }
135                 Label falseLabel;
136                 bits |= OnlyValueRequiredMASK;
137                 generateOptimizedBoolean(
138                         currentScope, 
139                         codeStream, 
140                         null, 
141                         falseLabel = new Label(codeStream), 
142                         valueRequired);
143                 if (falseLabel.hasForwardReferences()) {
144                         if (valueRequired){
145                                 // comparison is TRUE 
146                                 codeStream.iconst_1();
147                                 if ((bits & ValueForReturnMASK) != 0){
148                                         codeStream.ireturn();
149                                         // comparison is FALSE
150                                         falseLabel.place();
151                                         codeStream.iconst_0();
152                                 } else {
153                                         Label endLabel = new Label(codeStream);
154                                         codeStream.goto_(endLabel);
155                                         codeStream.decrStackSize(1);
156                                         // comparison is FALSE
157                                         falseLabel.place();
158                                         codeStream.iconst_0();
159                                         endLabel.place();
160                                 }
161                                 codeStream.generateImplicitConversion(implicitConversion);
162                         } else {
163                                 falseLabel.place();
164                         }       
165                 }
166         }
167         /**
168          * Boolean operator code generation
169          *      Optimized operations are: == and !=
170          */
171         public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
172         
173                 if (constant != Constant.NotAConstant) {
174                         super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
175                         return;
176                 }
177                 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
178                         if ((left.implicitConversion & COMPILE_TYPE_MASK) /*compile-time*/ == T_boolean) {
179                                 generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
180                         } else {
181                                 generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
182                         }
183                 } else {
184                         if ((left.implicitConversion & COMPILE_TYPE_MASK) /*compile-time*/ == T_boolean) {
185                                 generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
186                         } else {
187                                 generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
188                         }
189                 }
190         }
191         /**
192          * Boolean generation for == with boolean operands
193          *
194          * Note this code does not optimize conditional constants !!!!
195          */
196         public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
197         
198                 // optimized cases: true == x, false == x
199                 if (left.constant != NotAConstant) {
200                         boolean inline = left.constant.booleanValue();
201                         right.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
202                         return;
203                 } // optimized cases: x == true, x == false
204                 if (right.constant != NotAConstant) {
205                         boolean inline = right.constant.booleanValue();
206                         left.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
207                         return;
208                 }
209                 // default case
210                 left.generateCode(currentScope, codeStream, valueRequired);
211                 right.generateCode(currentScope, codeStream, valueRequired);
212                 if (valueRequired) {
213                         if (falseLabel == null) {
214                                 if (trueLabel != null) {
215                                         // implicit falling through the FALSE case
216                                         codeStream.if_icmpeq(trueLabel);
217                                 }
218                         } else {
219                                 // implicit falling through the TRUE case
220                                 if (trueLabel == null) {
221                                         codeStream.if_icmpne(falseLabel);
222                                 } else {
223                                         // no implicit fall through TRUE/FALSE --> should never occur
224                                 }
225                         }
226                 }
227                 // reposition the endPC
228                 codeStream.updateLastRecordedEndPC(codeStream.position);                                        
229         }
230         /**
231          * Boolean generation for == with non-boolean operands
232          *
233          */
234         public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
235         
236                 int pc = codeStream.position;
237                 Constant inline;
238                 if ((inline = right.constant) != NotAConstant) {
239                         // optimized case: x == 0
240                         if ((((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_int) && (inline.intValue() == 0)) {
241                                 left.generateCode(currentScope, codeStream, valueRequired);
242                                 if (valueRequired) {
243                                         if (falseLabel == null) {
244                                                 if (trueLabel != null) {
245                                                         // implicit falling through the FALSE case
246                                                         codeStream.ifeq(trueLabel);
247                                                 }
248                                         } else {
249                                                 // implicit falling through the TRUE case
250                                                 if (trueLabel == null) {
251                                                         codeStream.ifne(falseLabel);
252                                                 } else {
253                                                         // no implicit fall through TRUE/FALSE --> should never occur
254                                                 }
255                                         }
256                                 }
257                                 codeStream.recordPositionsFrom(pc, this.sourceStart);
258                                 return;
259                         }
260                 }
261                 if ((inline = left.constant) != NotAConstant) {
262                         // optimized case: 0 == x
263                         if ((((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_int)
264                                 && (inline.intValue() == 0)) {
265                                 right.generateCode(currentScope, codeStream, valueRequired);
266                                 if (valueRequired) {
267                                         if (falseLabel == null) {
268                                                 if (trueLabel != null) {
269                                                         // implicit falling through the FALSE case
270                                                         codeStream.ifeq(trueLabel);
271                                                 }
272                                         } else {
273                                                 // implicit falling through the TRUE case
274                                                 if (trueLabel == null) {
275                                                         codeStream.ifne(falseLabel);
276                                                 } else {
277                                                         // no implicit fall through TRUE/FALSE --> should never occur
278                                                 }
279                                         }
280                                 }
281                                 codeStream.recordPositionsFrom(pc, this.sourceStart);
282                                 return;
283                         }
284                 }
285                 // null cases
286                 // optimized case: x == null
287                 if (right instanceof NullLiteral) {
288                         if (left instanceof NullLiteral) {
289                                 // null == null
290                                 if (valueRequired) {
291                                                 if ((bits & OnlyValueRequiredMASK) != 0) {
292                                                         if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
293                                                                 codeStream.iconst_1();
294                                                         } else {
295                                                                 codeStream.iconst_0();
296                                                         }
297                                                 } else {
298                                                         if (falseLabel == null) {
299                                                                 // implicit falling through the FALSE case
300                                                                 if (trueLabel != null) {
301                                                                         codeStream.goto_(trueLabel);
302                                                                 }
303                                                         }
304                                         }
305                                 }
306                         } else {
307                                 left.generateCode(currentScope, codeStream, valueRequired);
308                                 if (valueRequired) {
309                                         if (falseLabel == null) {
310                                                 if (trueLabel != null) {
311                                                         // implicit falling through the FALSE case
312                                                         codeStream.ifnull(trueLabel);
313                                                 }
314                                         } else {
315                                                 // implicit falling through the TRUE case
316                                                 if (trueLabel == null) {
317                                                         codeStream.ifnonnull(falseLabel);
318                                                 } else {
319                                                         // no implicit fall through TRUE/FALSE --> should never occur
320                                                 }
321                                         }
322                                 }
323                         }
324                         codeStream.recordPositionsFrom(pc, this.sourceStart);
325                         return;
326                 } else if (left instanceof NullLiteral) { // optimized case: null == x
327                         right.generateCode(currentScope, codeStream, valueRequired);
328                         if (valueRequired) {
329                                 if (falseLabel == null) {
330                                         if (trueLabel != null) {
331                                                 // implicit falling through the FALSE case
332                                                 codeStream.ifnull(trueLabel);
333                                         }
334                                 } else {
335                                         // implicit falling through the TRUE case
336                                         if (trueLabel == null) {
337                                                 codeStream.ifnonnull(falseLabel);
338                                         } else {
339                                                 // no implicit fall through TRUE/FALSE --> should never occur
340                                         }
341                                 }
342                         }
343                         codeStream.recordPositionsFrom(pc, this.sourceStart);
344                         return;
345                 }
346         
347                 // default case
348                 left.generateCode(currentScope, codeStream, valueRequired);
349                 right.generateCode(currentScope, codeStream, valueRequired);
350                 if (valueRequired) {
351                         if (falseLabel == null) {
352                                 if (trueLabel != null) {
353                                         // implicit falling through the FALSE case
354                                         switch ((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type
355                                                 case T_int :
356                                                         codeStream.if_icmpeq(trueLabel);
357                                                         break;
358                                                 case T_float :
359                                                         codeStream.fcmpl();
360                                                         codeStream.ifeq(trueLabel);
361                                                         break;
362                                                 case T_long :
363                                                         codeStream.lcmp();
364                                                         codeStream.ifeq(trueLabel);
365                                                         break;
366                                                 case T_double :
367                                                         codeStream.dcmpl();
368                                                         codeStream.ifeq(trueLabel);
369                                                         break;
370                                                 default :
371                                                         codeStream.if_acmpeq(trueLabel);
372                                         }
373                                 }
374                         } else {
375                                 // implicit falling through the TRUE case
376                                 if (trueLabel == null) {
377                                         switch ((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type
378                                                 case T_int :
379                                                         codeStream.if_icmpne(falseLabel);
380                                                         break;
381                                                 case T_float :
382                                                         codeStream.fcmpl();
383                                                         codeStream.ifne(falseLabel);
384                                                         break;
385                                                 case T_long :
386                                                         codeStream.lcmp();
387                                                         codeStream.ifne(falseLabel);
388                                                         break;
389                                                 case T_double :
390                                                         codeStream.dcmpl();
391                                                         codeStream.ifne(falseLabel);
392                                                         break;
393                                                 default :
394                                                         codeStream.if_acmpne(falseLabel);
395                                         }
396                                 } else {
397                                         // no implicit fall through TRUE/FALSE --> should never occur
398                                 }
399                         }
400                 }
401                 codeStream.recordPositionsFrom(pc, this.sourceStart);
402         }
403         public boolean isCompactableOperation() {
404                 return false;
405         }
406         public TypeBinding resolveType(BlockScope scope) {
407         
408                         boolean leftIsCast, rightIsCast;
409                         if ((leftIsCast = left instanceof CastExpression) == true) left.bits |= IgnoreNeedForCastCheckMASK; // will check later on
410                         TypeBinding originalLeftType = left.resolveType(scope);
411         
412                         if ((rightIsCast = right instanceof CastExpression) == true) right.bits |= IgnoreNeedForCastCheckMASK; // will check later on
413                         TypeBinding originalRightType = right.resolveType(scope);
414         
415                 // always return BooleanBinding
416                 if (originalLeftType == null || originalRightType == null){
417                         constant = NotAConstant;                
418                         return null;
419                 }
420         
421                 // autoboxing support
422                 LookupEnvironment env = scope.environment();
423                 boolean use15specifics = env.options.sourceLevel >= JDK1_5;
424                 TypeBinding leftType = originalLeftType, rightType = originalRightType;
425                 if (use15specifics) {
426                         if (leftType != NullBinding && leftType.isBaseType()) {
427                                 if (!rightType.isBaseType()) {
428                                         rightType = env.computeBoxingType(rightType);
429                                 }
430                         } else {
431                                 if (rightType != NullBinding && rightType.isBaseType()) {
432                                         leftType = env.computeBoxingType(leftType);
433                                 }
434                         }
435                 }
436                 // both base type
437                 if (leftType.isBaseType() && rightType.isBaseType()) {
438                         int leftTypeID = leftType.id;
439                         int rightTypeID = rightType.id;
440         
441                         // the code is an int
442                         // (cast)  left   == (cast)  right --> result
443                         //  0000   0000       0000   0000      0000
444                         //  <<16   <<12       <<8    <<4       <<0
445                         int operatorSignature = OperatorSignatures[EQUAL_EQUAL][ (leftTypeID << 4) + rightTypeID];
446                         left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), originalLeftType);
447                         right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), originalRightType);
448                         bits |= operatorSignature & 0xF;                
449                         if ((operatorSignature & 0x0000F) == T_undefined) {
450                                 constant = Constant.NotAConstant;
451                                 scope.problemReporter().invalidOperator(this, leftType, rightType);
452                                 return null;
453                         }
454                         // check need for operand cast
455                         if (leftIsCast || rightIsCast) {
456                                 CastExpression.checkNeedForArgumentCasts(scope, EQUAL_EQUAL, operatorSignature, left, leftType.id, leftIsCast, right, rightType.id, rightIsCast);
457                         }
458                         computeConstant(leftType, rightType);
459                         return this.resolvedType = BooleanBinding;
460                 }
461         
462                 // Object references 
463                 // spec 15.20.3
464                 if (this.checkCastTypesCompatibility(scope, leftType, rightType, null) 
465                                 || this.checkCastTypesCompatibility(scope, rightType, leftType, null)) {
466
467                         // (special case for String)
468                         if ((rightType.id == T_JavaLangString) && (leftType.id == T_JavaLangString)) {
469                                 computeConstant(leftType, rightType);
470                         } else {
471                                 constant = NotAConstant;
472                         }
473                         TypeBinding objectType = scope.getJavaLangObject();
474                         left.computeConversion(scope, objectType, leftType);
475                         right.computeConversion(scope, objectType, rightType);
476                         // check need for operand cast
477                         boolean unnecessaryLeftCast = (left.bits & UnnecessaryCastMask) != 0;
478                         boolean unnecessaryRightCast = (right.bits & UnnecessaryCastMask) != 0;
479                         if (unnecessaryLeftCast || unnecessaryRightCast) {
480                                 TypeBinding alternateLeftType = unnecessaryLeftCast ? ((CastExpression)left).expression.resolvedType : leftType;
481                                 TypeBinding alternateRightType = unnecessaryRightCast ? ((CastExpression)right).expression.resolvedType : rightType;
482                                 if (this.checkCastTypesCompatibility(scope, alternateLeftType, alternateRightType, null) 
483                                                 || this.checkCastTypesCompatibility(scope, alternateRightType, alternateLeftType, null)) {
484                                         if (unnecessaryLeftCast) scope.problemReporter().unnecessaryCast((CastExpression)left); 
485                                         if (unnecessaryRightCast) scope.problemReporter().unnecessaryCast((CastExpression)right);
486                                 }
487                         }
488                         return this.resolvedType = BooleanBinding;
489                 }
490                 constant = NotAConstant;
491                 scope.problemReporter().notCompatibleTypesError(this, leftType, rightType);
492                 return null;
493         }
494         public void traverse(ASTVisitor visitor, BlockScope scope) {
495                 if (visitor.visit(this, scope)) {
496                         left.traverse(visitor, scope);
497                         right.traverse(visitor, scope);
498                 }
499                 visitor.endVisit(this, scope);
500         }
501 }