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.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;
25 public class EqualExpression extends BinaryExpression {
27 public EqualExpression(Expression left, Expression right, int operator) {
28 super (left, right, operator);
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,
40 } else { // false == anything
41 // this is equivalent to the right argument inits negated
42 return right.analyseCode(currentScope, flowContext,
43 flowInfo).asNegatedCondition();
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
51 .analyseCode(currentScope, flowContext, flowInfo);
52 } else { // anything == false
53 // this is equivalent to the right argument inits negated
55 .analyseCode(currentScope, flowContext, flowInfo)
56 .asNegatedCondition();
59 return right.analyseCode(
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,
71 } else { // true != anything
72 // this is equivalent to the right argument inits negated
73 return right.analyseCode(currentScope, flowContext,
74 flowInfo).asNegatedCondition();
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
82 .analyseCode(currentScope, flowContext, flowInfo);
83 } else { // anything != true
84 // this is equivalent to the right argument inits negated
86 .analyseCode(currentScope, flowContext, flowInfo)
87 .asNegatedCondition();
90 return right.analyseCode(
93 left.analyseCode(currentScope, flowContext, flowInfo)
94 .unconditionalInits()).asNegatedCondition()
95 .unconditionalInits();
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)
105 if (castType == expressionType)
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);
121 if (castType.isBaseType()) {
124 if (castType.isClass()) { // ------(castTb.isClass)
125 // expressionTb.isArray ---------------
126 if (scope.isJavaLangObject(castType))
130 if (castType.isInterface()) { // ------- (castTb.isInterface)
131 // expressionTb.isArray -----------
132 if (scope.isJavaLangCloneable(castType)
133 || scope.isJavaIoSerializable(castType)) {
142 // ------------(castType) null--------------
143 if (expressionType == NullBinding) {
144 return !castType.isBaseType();
147 // ========BASETYPE==============
148 if (expressionType.isBaseType()) {
152 // ========REFERENCE TYPE===================
154 if (expressionType.isClass()) {
155 if (castType.isArrayType()) { // ---- (castTb.isArray)
156 // expressionTb.isClass -------
157 if (scope.isJavaLangObject(expressionType))
160 if (castType.isBaseType()) {
163 if (castType.isClass()) { // ----- (castTb.isClass)
164 // expressionTb.isClass ------
165 if (expressionType.isCompatibleWith(castType))
168 if (castType.isCompatibleWith(expressionType)) {
174 if (castType.isInterface()) { // ----- (castTb.isInterface)
175 // expressionTb.isClass -------
176 if (((ReferenceBinding) expressionType).isFinal()) { // no
185 if (expressionType.isCompatibleWith(castType))
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
206 if (castType.isBaseType()) {
209 if (castType.isClass()) { // ----- (castTb.isClass)
210 // expressionTb.isInterface --------
211 if (scope.isJavaLangObject(castType))
213 if (((ReferenceBinding) castType).isFinal()) { // no subclass
219 if (castType.isCompatibleWith(expressionType)) {
226 if (castType.isInterface()) { // ----- (castTb.isInterface)
227 // expressionTb.isInterface -------
228 if (Scope.compareTypes(castType, expressionType) == NotRelated) {
229 MethodBinding[] castTbMethods = ((ReferenceBinding) castType)
231 int castTbMethodsLength = castTbMethods.length;
232 MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionType)
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) {
240 .areParametersEqual(expressionTbMethods[j])) {
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,
264 if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
265 constant = Constant.fromValue(!constant.booleanValue());
267 this.constant = NotAConstant;
268 // no optimization for null == null
273 * Normal == or != code generation.
275 * @param currentScope
276 * net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
278 * net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
279 * @param valueRequired
282 // public void generateCode(BlockScope currentScope, CodeStream codeStream,
283 // boolean valueRequired) {
285 // if (constant != NotAConstant) {
286 // int pc = codeStream.position;
287 // if (valueRequired)
288 // codeStream.generateConstant(constant, implicitConversion);
289 // codeStream.recordPositionsFrom(pc, this.sourceStart);
293 // bits |= OnlyValueRequiredMASK;
294 // generateOptimizedBoolean(
298 // falseLabel = new Label(codeStream),
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();
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();
319 // falseLabel.place();
324 * Boolean operator code generation Optimized operations are: == and !=
326 // public void generateOptimizedBoolean(BlockScope currentScope, CodeStream
327 // codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
329 // if (constant != Constant.NotAConstant) {
330 // super.generateOptimizedBoolean(currentScope, codeStream, trueLabel,
331 // falseLabel, valueRequired);
334 // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
335 // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
336 // generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel,
337 // falseLabel, valueRequired);
339 // generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel,
340 // falseLabel, valueRequired);
343 // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
344 // generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel,
345 // trueLabel, valueRequired);
347 // generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel,
348 // trueLabel, valueRequired);
353 * Boolean generation for == with boolean operands
355 * Note this code does not optimize conditional constants !!!!
357 // public void generateOptimizedBooleanEqual(BlockScope currentScope,
358 // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean
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),
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),
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);
386 // // implicit falling through the TRUE case
387 // if (trueLabel == null) {
388 // codeStream.if_icmpne(falseLabel);
390 // // no implicit fall through TRUE/FALSE --> should never occur
394 // // reposition the endPC
395 // codeStream.updateLastRecordedEndPC(codeStream.position);
398 // * Boolean generation for == with non-boolean operands
401 // public void generateOptimizedNonBooleanEqual(BlockScope currentScope,
402 // CodeStream codeStream, Label trueLabel, Label falseLabel, boolean
405 // int pc = codeStream.position;
407 // if ((inline = right.constant) != NotAConstant) {
408 // // optimized case: x == 0
409 // if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() ==
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);
419 // // implicit falling through the TRUE case
420 // if (trueLabel == null) {
421 // codeStream.ifne(falseLabel);
423 // // no implicit fall through TRUE/FALSE --> should never occur
427 // codeStream.recordPositionsFrom(pc, this.sourceStart);
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);
443 // // implicit falling through the TRUE case
444 // if (trueLabel == null) {
445 // codeStream.ifne(falseLabel);
447 // // no implicit fall through TRUE/FALSE --> should never occur
451 // codeStream.recordPositionsFrom(pc, this.sourceStart);
456 // // optimized case: x == null
457 // if (right instanceof NullLiteral) {
458 // if (left instanceof NullLiteral) {
460 // if (valueRequired) {
461 // if ((bits & OnlyValueRequiredMASK) != 0) {
462 // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
463 // codeStream.iconst_1();
465 // codeStream.iconst_0();
468 // if (falseLabel == null) {
469 // // implicit falling through the FALSE case
470 // if (trueLabel != null) {
471 // codeStream.goto_(trueLabel);
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);
485 // // implicit falling through the TRUE case
486 // if (trueLabel == null) {
487 // codeStream.ifnonnull(falseLabel);
489 // // no implicit fall through TRUE/FALSE --> should never occur
494 // codeStream.recordPositionsFrom(pc, this.sourceStart);
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);
505 // // implicit falling through the TRUE case
506 // if (trueLabel == null) {
507 // codeStream.ifnonnull(falseLabel);
509 // // no implicit fall through TRUE/FALSE --> should never occur
513 // codeStream.recordPositionsFrom(pc, this.sourceStart);
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
526 // codeStream.if_icmpeq(trueLabel);
529 // codeStream.fcmpl();
530 // codeStream.ifeq(trueLabel);
533 // codeStream.lcmp();
534 // codeStream.ifeq(trueLabel);
537 // codeStream.dcmpl();
538 // codeStream.ifeq(trueLabel);
541 // codeStream.if_acmpeq(trueLabel);
545 // // implicit falling through the TRUE case
546 // if (trueLabel == null) {
547 // switch (left.implicitConversion >> 4) { // operand runtime type
549 // codeStream.if_icmpne(falseLabel);
552 // codeStream.fcmpl();
553 // codeStream.ifne(falseLabel);
556 // codeStream.lcmp();
557 // codeStream.ifne(falseLabel);
560 // codeStream.dcmpl();
561 // codeStream.ifne(falseLabel);
564 // codeStream.if_acmpne(falseLabel);
567 // // no implicit fall through TRUE/FALSE --> should never occur
571 // codeStream.recordPositionsFrom(pc, this.sourceStart);
573 public boolean isCompactableOperation() {
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;
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)
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,
603 computeConstant(leftType, rightType);
604 this.resolvedType = BooleanBinding;
605 return BooleanBinding;
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);
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;
624 constant = NotAConstant;
625 scope.problemReporter().notCompatibleTypesError(this, leftType,
630 public void traverse(ASTVisitor visitor, BlockScope scope) {
631 if (visitor.visit(this, scope)) {
632 left.traverse(visitor, scope);
633 right.traverse(visitor, scope);
635 visitor.endVisit(this, scope);