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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
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.*;
19 public class EqualExpression extends BinaryExpression {
21 public EqualExpression(Expression left, Expression right,int operator) {
22 super(left,right,operator);
24 public void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {
26 LocalVariableBinding local = this.left.localVariableBinding();
28 checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, right.nullStatus(flowInfo), this.left);
30 local = this.right.localVariableBinding();
32 checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, left.nullStatus(flowInfo), this.right);
35 private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) {
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
43 initsWhenTrue.markAsDefinitelyNonNull(local); // from thereon it is set
44 initsWhenFalse.markAsDefinitelyNull(local); // from thereon it is set
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
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();
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();
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();
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();
98 return right.analyseCode(
99 currentScope, flowContext,
100 left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits();
104 public final void computeConstant(TypeBinding leftType, TypeBinding rightType) {
105 if ((this.left.constant != NotAConstant) && (this.right.constant != NotAConstant)) {
107 Constant.computeConstantOperationEQUAL_EQUAL(
112 if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
113 constant = Constant.fromValue(!constant.booleanValue());
115 this.constant = NotAConstant;
116 // no optimization for null == null
120 * Normal == or != code generation.
122 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
123 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
124 * @param valueRequired boolean
126 public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
128 if (constant != NotAConstant) {
129 int pc = codeStream.position;
131 codeStream.generateConstant(constant, implicitConversion);
132 codeStream.recordPositionsFrom(pc, this.sourceStart);
136 bits |= OnlyValueRequiredMASK;
137 generateOptimizedBoolean(
141 falseLabel = new Label(codeStream),
143 if (falseLabel.hasForwardReferences()) {
145 // comparison is TRUE
146 codeStream.iconst_1();
147 if ((bits & ValueForReturnMASK) != 0){
148 codeStream.ireturn();
149 // comparison is FALSE
151 codeStream.iconst_0();
153 Label endLabel = new Label(codeStream);
154 codeStream.goto_(endLabel);
155 codeStream.decrStackSize(1);
156 // comparison is FALSE
158 codeStream.iconst_0();
161 codeStream.generateImplicitConversion(implicitConversion);
168 * Boolean operator code generation
169 * Optimized operations are: == and !=
171 public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
173 if (constant != Constant.NotAConstant) {
174 super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
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);
181 generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
184 if ((left.implicitConversion & COMPILE_TYPE_MASK) /*compile-time*/ == T_boolean) {
185 generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
187 generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
192 * Boolean generation for == with boolean operands
194 * Note this code does not optimize conditional constants !!!!
196 public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
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);
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);
210 left.generateCode(currentScope, codeStream, valueRequired);
211 right.generateCode(currentScope, codeStream, valueRequired);
213 if (falseLabel == null) {
214 if (trueLabel != null) {
215 // implicit falling through the FALSE case
216 codeStream.if_icmpeq(trueLabel);
219 // implicit falling through the TRUE case
220 if (trueLabel == null) {
221 codeStream.if_icmpne(falseLabel);
223 // no implicit fall through TRUE/FALSE --> should never occur
227 // reposition the endPC
228 codeStream.updateLastRecordedEndPC(codeStream.position);
231 * Boolean generation for == with non-boolean operands
234 public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
236 int pc = codeStream.position;
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);
243 if (falseLabel == null) {
244 if (trueLabel != null) {
245 // implicit falling through the FALSE case
246 codeStream.ifeq(trueLabel);
249 // implicit falling through the TRUE case
250 if (trueLabel == null) {
251 codeStream.ifne(falseLabel);
253 // no implicit fall through TRUE/FALSE --> should never occur
257 codeStream.recordPositionsFrom(pc, this.sourceStart);
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);
267 if (falseLabel == null) {
268 if (trueLabel != null) {
269 // implicit falling through the FALSE case
270 codeStream.ifeq(trueLabel);
273 // implicit falling through the TRUE case
274 if (trueLabel == null) {
275 codeStream.ifne(falseLabel);
277 // no implicit fall through TRUE/FALSE --> should never occur
281 codeStream.recordPositionsFrom(pc, this.sourceStart);
286 // optimized case: x == null
287 if (right instanceof NullLiteral) {
288 if (left instanceof NullLiteral) {
291 if ((bits & OnlyValueRequiredMASK) != 0) {
292 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
293 codeStream.iconst_1();
295 codeStream.iconst_0();
298 if (falseLabel == null) {
299 // implicit falling through the FALSE case
300 if (trueLabel != null) {
301 codeStream.goto_(trueLabel);
307 left.generateCode(currentScope, codeStream, valueRequired);
309 if (falseLabel == null) {
310 if (trueLabel != null) {
311 // implicit falling through the FALSE case
312 codeStream.ifnull(trueLabel);
315 // implicit falling through the TRUE case
316 if (trueLabel == null) {
317 codeStream.ifnonnull(falseLabel);
319 // no implicit fall through TRUE/FALSE --> should never occur
324 codeStream.recordPositionsFrom(pc, this.sourceStart);
326 } else if (left instanceof NullLiteral) { // optimized case: null == x
327 right.generateCode(currentScope, codeStream, valueRequired);
329 if (falseLabel == null) {
330 if (trueLabel != null) {
331 // implicit falling through the FALSE case
332 codeStream.ifnull(trueLabel);
335 // implicit falling through the TRUE case
336 if (trueLabel == null) {
337 codeStream.ifnonnull(falseLabel);
339 // no implicit fall through TRUE/FALSE --> should never occur
343 codeStream.recordPositionsFrom(pc, this.sourceStart);
348 left.generateCode(currentScope, codeStream, valueRequired);
349 right.generateCode(currentScope, codeStream, 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
356 codeStream.if_icmpeq(trueLabel);
360 codeStream.ifeq(trueLabel);
364 codeStream.ifeq(trueLabel);
368 codeStream.ifeq(trueLabel);
371 codeStream.if_acmpeq(trueLabel);
375 // implicit falling through the TRUE case
376 if (trueLabel == null) {
377 switch ((left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type
379 codeStream.if_icmpne(falseLabel);
383 codeStream.ifne(falseLabel);
387 codeStream.ifne(falseLabel);
391 codeStream.ifne(falseLabel);
394 codeStream.if_acmpne(falseLabel);
397 // no implicit fall through TRUE/FALSE --> should never occur
401 codeStream.recordPositionsFrom(pc, this.sourceStart);
403 public boolean isCompactableOperation() {
406 public TypeBinding resolveType(BlockScope scope) {
408 boolean leftIsCast, rightIsCast;
409 if ((leftIsCast = left instanceof CastExpression) == true) left.bits |= IgnoreNeedForCastCheckMASK; // will check later on
410 TypeBinding originalLeftType = left.resolveType(scope);
412 if ((rightIsCast = right instanceof CastExpression) == true) right.bits |= IgnoreNeedForCastCheckMASK; // will check later on
413 TypeBinding originalRightType = right.resolveType(scope);
415 // always return BooleanBinding
416 if (originalLeftType == null || originalRightType == null){
417 constant = NotAConstant;
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);
431 if (rightType != NullBinding && rightType.isBaseType()) {
432 leftType = env.computeBoxingType(leftType);
437 if (leftType.isBaseType() && rightType.isBaseType()) {
438 int leftTypeID = leftType.id;
439 int rightTypeID = rightType.id;
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);
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);
458 computeConstant(leftType, rightType);
459 return this.resolvedType = BooleanBinding;
464 if (this.checkCastTypesCompatibility(scope, leftType, rightType, null)
465 || this.checkCastTypesCompatibility(scope, rightType, leftType, null)) {
467 // (special case for String)
468 if ((rightType.id == T_JavaLangString) && (leftType.id == T_JavaLangString)) {
469 computeConstant(leftType, rightType);
471 constant = NotAConstant;
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);
488 return this.resolvedType = BooleanBinding;
490 constant = NotAConstant;
491 scope.problemReporter().notCompatibleTypesError(this, leftType, rightType);
494 public void traverse(ASTVisitor visitor, BlockScope scope) {
495 if (visitor.visit(this, scope)) {
496 left.traverse(visitor, scope);
497 right.traverse(visitor, scope);
499 visitor.endVisit(this, scope);