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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler.ast;
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;
24 public class EqualExpression extends BinaryExpression {
26 public EqualExpression(Expression left, Expression right, int operator) {
27 super(left, right, operator);
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,
39 } else { // false == anything
40 // this is equivalent to the right argument inits negated
41 return right.analyseCode(currentScope, flowContext,
42 flowInfo).asNegatedCondition();
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
50 .analyseCode(currentScope, flowContext, flowInfo);
51 } else { // anything == false
52 // this is equivalent to the right argument inits negated
54 .analyseCode(currentScope, flowContext, flowInfo)
55 .asNegatedCondition();
58 return right.analyseCode(
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,
70 } else { // true != anything
71 // this is equivalent to the right argument inits negated
72 return right.analyseCode(currentScope, flowContext,
73 flowInfo).asNegatedCondition();
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
81 .analyseCode(currentScope, flowContext, flowInfo);
82 } else { // anything != true
83 // this is equivalent to the right argument inits negated
85 .analyseCode(currentScope, flowContext, flowInfo)
86 .asNegatedCondition();
89 return right.analyseCode(
92 left.analyseCode(currentScope, flowContext, flowInfo)
93 .unconditionalInits()).asNegatedCondition()
94 .unconditionalInits();
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)
104 if (castType == expressionType)
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);
120 if (castType.isBaseType()) {
123 if (castType.isClass()) { // ------(castTb.isClass)
124 // expressionTb.isArray ---------------
125 if (scope.isJavaLangObject(castType))
129 if (castType.isInterface()) { // ------- (castTb.isInterface)
130 // expressionTb.isArray -----------
131 if (scope.isJavaLangCloneable(castType)
132 || scope.isJavaIoSerializable(castType)) {
141 // ------------(castType) null--------------
142 if (expressionType == NullBinding) {
143 return !castType.isBaseType();
146 // ========BASETYPE==============
147 if (expressionType.isBaseType()) {
151 // ========REFERENCE TYPE===================
153 if (expressionType.isClass()) {
154 if (castType.isArrayType()) { // ---- (castTb.isArray)
155 // expressionTb.isClass -------
156 if (scope.isJavaLangObject(expressionType))
159 if (castType.isBaseType()) {
162 if (castType.isClass()) { // ----- (castTb.isClass)
163 // expressionTb.isClass ------
164 if (expressionType.isCompatibleWith(castType))
167 if (castType.isCompatibleWith(expressionType)) {
173 if (castType.isInterface()) { // ----- (castTb.isInterface)
174 // expressionTb.isClass -------
175 if (((ReferenceBinding) expressionType).isFinal()) { // no
184 if (expressionType.isCompatibleWith(castType))
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
205 if (castType.isBaseType()) {
208 if (castType.isClass()) { // ----- (castTb.isClass)
209 // expressionTb.isInterface --------
210 if (scope.isJavaLangObject(castType))
212 if (((ReferenceBinding) castType).isFinal()) { // no subclass
218 if (castType.isCompatibleWith(expressionType)) {
225 if (castType.isInterface()) { // ----- (castTb.isInterface)
226 // expressionTb.isInterface -------
227 if (Scope.compareTypes(castType, expressionType) == NotRelated) {
228 MethodBinding[] castTbMethods = ((ReferenceBinding) castType)
230 int castTbMethodsLength = castTbMethods.length;
231 MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionType)
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) {
239 .areParametersEqual(expressionTbMethods[j])) {
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,
263 if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
264 constant = Constant.fromValue(!constant.booleanValue());
266 this.constant = NotAConstant;
267 // no optimization for null == null
272 * Normal == or != code generation.
274 * @param currentScope
275 * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
277 * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
278 * @param valueRequired
281 // public void generateCode(BlockScope currentScope, CodeStream codeStream,
282 // boolean valueRequired) {
284 // if (constant != NotAConstant) {
285 // int pc = codeStream.position;
286 // if (valueRequired)
287 // codeStream.generateConstant(constant, implicitConversion);
288 // codeStream.recordPositionsFrom(pc, this.sourceStart);
292 // bits |= OnlyValueRequiredMASK;
293 // generateOptimizedBoolean(
297 // falseLabel = new Label(codeStream),
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();
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();
318 // falseLabel.place();
323 * Boolean operator code generation Optimized operations are: == and !=
325 // public void generateOptimizedBoolean(BlockScope currentScope, CodeStream
326 // codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
328 // if (constant != Constant.NotAConstant) {
329 // super.generateOptimizedBoolean(currentScope, codeStream, trueLabel,
330 // falseLabel, valueRequired);
333 // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
334 // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
335 // generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel,
336 // falseLabel, valueRequired);
338 // generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel,
339 // falseLabel, valueRequired);
342 // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
343 // generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel,
344 // trueLabel, valueRequired);
346 // generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel,
347 // trueLabel, valueRequired);
352 * Boolean generation for == with boolean operands
354 * Note this code does not optimize conditional constants !!!!
356 // public void generateOptimizedBooleanEqual(BlockScope currentScope,
357 // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean
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),
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),
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);
385 // // implicit falling through the TRUE case
386 // if (trueLabel == null) {
387 // codeStream.if_icmpne(falseLabel);
389 // // no implicit fall through TRUE/FALSE --> should never occur
393 // // reposition the endPC
394 // codeStream.updateLastRecordedEndPC(codeStream.position);
397 // * Boolean generation for == with non-boolean operands
400 // public void generateOptimizedNonBooleanEqual(BlockScope currentScope,
401 // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean
404 // int pc = codeStream.position;
406 // if ((inline = right.constant) != NotAConstant) {
407 // // optimized case: x == 0
408 // if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() ==
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);
418 // // implicit falling through the TRUE case
419 // if (trueLabel == null) {
420 // codeStream.ifne(falseLabel);
422 // // no implicit fall through TRUE/FALSE --> should never occur
426 // codeStream.recordPositionsFrom(pc, this.sourceStart);
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);
442 // // implicit falling through the TRUE case
443 // if (trueLabel == null) {
444 // codeStream.ifne(falseLabel);
446 // // no implicit fall through TRUE/FALSE --> should never occur
450 // codeStream.recordPositionsFrom(pc, this.sourceStart);
455 // // optimized case: x == null
456 // if (right instanceof NullLiteral) {
457 // if (left instanceof NullLiteral) {
459 // if (valueRequired) {
460 // if ((bits & OnlyValueRequiredMASK) != 0) {
461 // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
462 // codeStream.iconst_1();
464 // codeStream.iconst_0();
467 // if (falseLabel == null) {
468 // // implicit falling through the FALSE case
469 // if (trueLabel != null) {
470 // codeStream.goto_(trueLabel);
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);
484 // // implicit falling through the TRUE case
485 // if (trueLabel == null) {
486 // codeStream.ifnonnull(falseLabel);
488 // // no implicit fall through TRUE/FALSE --> should never occur
493 // codeStream.recordPositionsFrom(pc, this.sourceStart);
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);
504 // // implicit falling through the TRUE case
505 // if (trueLabel == null) {
506 // codeStream.ifnonnull(falseLabel);
508 // // no implicit fall through TRUE/FALSE --> should never occur
512 // codeStream.recordPositionsFrom(pc, this.sourceStart);
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
525 // codeStream.if_icmpeq(trueLabel);
528 // codeStream.fcmpl();
529 // codeStream.ifeq(trueLabel);
532 // codeStream.lcmp();
533 // codeStream.ifeq(trueLabel);
536 // codeStream.dcmpl();
537 // codeStream.ifeq(trueLabel);
540 // codeStream.if_acmpeq(trueLabel);
544 // // implicit falling through the TRUE case
545 // if (trueLabel == null) {
546 // switch (left.implicitConversion >> 4) { // operand runtime type
548 // codeStream.if_icmpne(falseLabel);
551 // codeStream.fcmpl();
552 // codeStream.ifne(falseLabel);
555 // codeStream.lcmp();
556 // codeStream.ifne(falseLabel);
559 // codeStream.dcmpl();
560 // codeStream.ifne(falseLabel);
563 // codeStream.if_acmpne(falseLabel);
566 // // no implicit fall through TRUE/FALSE --> should never occur
570 // codeStream.recordPositionsFrom(pc, this.sourceStart);
572 public boolean isCompactableOperation() {
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;
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)
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,
602 computeConstant(leftType, rightType);
603 this.resolvedType = BooleanBinding;
604 return BooleanBinding;
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);
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;
623 constant = NotAConstant;
624 scope.problemReporter().notCompatibleTypesError(this, leftType,
629 public void traverse(ASTVisitor visitor, BlockScope scope) {
630 if (visitor.visit(this, scope)) {
631 left.traverse(visitor, scope);
632 right.traverse(visitor, scope);
634 visitor.endVisit(this, scope);