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.phpeclipse.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);
29 public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
30 if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
31 if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) {
32 if (left.constant.booleanValue()) { // true == anything
33 // this is equivalent to the right argument inits
34 return right.analyseCode(currentScope, flowContext, flowInfo);
35 } else { // false == anything
36 // this is equivalent to the right argument inits negated
37 return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
40 if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) {
41 if (right.constant.booleanValue()) { // anything == true
42 // this is equivalent to the right argument inits
43 return left.analyseCode(currentScope, flowContext, flowInfo);
44 } else { // anything == false
45 // this is equivalent to the right argument inits negated
46 return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
49 return right.analyseCode(
50 currentScope, flowContext,
51 left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits();
52 } else { //NOT_EQUAL :
53 if ((left.constant != NotAConstant) && (left.constant.typeID() == T_boolean)) {
54 if (!left.constant.booleanValue()) { // false != anything
55 // this is equivalent to the right argument inits
56 return right.analyseCode(currentScope, flowContext, flowInfo);
57 } else { // true != anything
58 // this is equivalent to the right argument inits negated
59 return right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
62 if ((right.constant != NotAConstant) && (right.constant.typeID() == T_boolean)) {
63 if (!right.constant.booleanValue()) { // anything != false
64 // this is equivalent to the right argument inits
65 return left.analyseCode(currentScope, flowContext, flowInfo);
66 } else { // anything != true
67 // this is equivalent to the right argument inits negated
68 return left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
71 return right.analyseCode(
72 currentScope, flowContext,
73 left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).asNegatedCondition().unconditionalInits();
76 public final boolean areTypesCastCompatible(BlockScope scope, TypeBinding castType, TypeBinding expressionType) {
77 //see specifications 5.5
78 //A more complete version of this method is provided on
79 //CastExpression (it deals with constant and need runtime checkcast)
81 if (castType == expressionType) return true;
83 //========ARRAY===============
84 if (expressionType.isArrayType()) {
85 if (castType.isArrayType()) { //------- (castTb.isArray) expressionTb.isArray -----------
86 TypeBinding expressionEltType = ((ArrayBinding) expressionType).elementsType(scope);
87 if (expressionEltType.isBaseType())
88 // <---stop the recursion-------
89 return ((ArrayBinding) castType).elementsType(scope) == expressionEltType;
90 //recursivly on the elts...
91 return areTypesCastCompatible(scope, ((ArrayBinding) castType).elementsType(scope), expressionEltType);
93 if (castType.isBaseType()) {
96 if (castType.isClass()) { //------(castTb.isClass) expressionTb.isArray ---------------
97 if (scope.isJavaLangObject(castType))
101 if (castType.isInterface()) { //------- (castTb.isInterface) expressionTb.isArray -----------
102 if (scope.isJavaLangCloneable(castType) || scope.isJavaIoSerializable(castType)) {
111 //------------(castType) null--------------
112 if (expressionType == NullBinding) {
113 return !castType.isBaseType();
116 //========BASETYPE==============
117 if (expressionType.isBaseType()) {
122 //========REFERENCE TYPE===================
124 if (expressionType.isClass()) {
125 if (castType.isArrayType()) { // ---- (castTb.isArray) expressionTb.isClass -------
126 if (scope.isJavaLangObject(expressionType))
129 if (castType.isBaseType()) {
132 if (castType.isClass()) { // ----- (castTb.isClass) expressionTb.isClass ------
133 if (expressionType.isCompatibleWith(castType))
136 if (castType.isCompatibleWith(expressionType)) {
142 if (castType.isInterface()) { // ----- (castTb.isInterface) expressionTb.isClass -------
143 if (((ReferenceBinding) expressionType).isFinal()) { //no subclass for expressionTb, thus compile-time check is valid
144 if (expressionType.isCompatibleWith(castType))
154 if (expressionType.isInterface()) {
155 if (castType.isArrayType()) { // ----- (castTb.isArray) expressionTb.isInterface ------
156 if (scope.isJavaLangCloneable(expressionType) || scope.isJavaIoSerializable(expressionType))
157 //potential runtime error
163 if (castType.isBaseType()) {
166 if (castType.isClass()) { // ----- (castTb.isClass) expressionTb.isInterface --------
167 if (scope.isJavaLangObject(castType))
169 if (((ReferenceBinding) castType).isFinal()) { //no subclass for castTb, thus compile-time check is valid
170 if (castType.isCompatibleWith(expressionType)) {
177 if (castType.isInterface()) { // ----- (castTb.isInterface) expressionTb.isInterface -------
178 if (Scope.compareTypes(castType, expressionType) == NotRelated) {
179 MethodBinding[] castTbMethods = ((ReferenceBinding) castType).methods();
180 int castTbMethodsLength = castTbMethods.length;
181 MethodBinding[] expressionTbMethods = ((ReferenceBinding) expressionType).methods();
182 int expressionTbMethodsLength = expressionTbMethods.length;
183 for (int i = 0; i < castTbMethodsLength; i++) {
184 for (int j = 0; j < expressionTbMethodsLength; j++) {
185 if (castTbMethods[i].selector == expressionTbMethods[j].selector) {
186 if (castTbMethods[i].returnType != expressionTbMethods[j].returnType) {
187 if (castTbMethods[i].areParametersEqual(expressionTbMethods[j])) {
203 public final void computeConstant(TypeBinding leftType, TypeBinding rightType) {
204 if ((this.left.constant != NotAConstant) && (this.right.constant != NotAConstant)) {
206 Constant.computeConstantOperationEQUAL_EQUAL(
212 if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
213 constant = Constant.fromValue(!constant.booleanValue());
215 this.constant = NotAConstant;
216 // no optimization for null == null
220 * Normal == or != code generation.
222 * @param currentScope net.sourceforge.phpdt.internal.compiler.lookup.BlockScope
223 * @param codeStream net.sourceforge.phpdt.internal.compiler.codegen.CodeStream
224 * @param valueRequired boolean
226 //public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
228 // if (constant != NotAConstant) {
229 // int pc = codeStream.position;
230 // if (valueRequired)
231 // codeStream.generateConstant(constant, implicitConversion);
232 // codeStream.recordPositionsFrom(pc, this.sourceStart);
236 // bits |= OnlyValueRequiredMASK;
237 // generateOptimizedBoolean(
241 // falseLabel = new Label(codeStream),
243 // if (falseLabel.hasForwardReferences()) {
244 // if (valueRequired){
245 // // comparison is TRUE
246 // codeStream.iconst_1();
247 // if ((bits & ValueForReturnMASK) != 0){
248 // codeStream.ireturn();
249 // // comparison is FALSE
250 // falseLabel.place();
251 // codeStream.iconst_0();
253 // Label endLabel = new Label(codeStream);
254 // codeStream.goto_(endLabel);
255 // codeStream.decrStackSize(1);
256 // // comparison is FALSE
257 // falseLabel.place();
258 // codeStream.iconst_0();
262 // falseLabel.place();
267 * Boolean operator code generation
268 * Optimized operations are: == and !=
270 //public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
272 // if (constant != Constant.NotAConstant) {
273 // super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
276 // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
277 // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
278 // generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
280 // generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
283 // if ((left.implicitConversion & 0xF) /*compile-time*/ == T_boolean) {
284 // generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
286 // generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
291 * Boolean generation for == with boolean operands
293 * Note this code does not optimize conditional constants !!!!
295 //public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
297 // // optimized cases: true == x, false == x
298 // if (left.constant != NotAConstant) {
299 // boolean inline = left.constant.booleanValue();
300 // right.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
302 // } // optimized cases: x == true, x == false
303 // if (right.constant != NotAConstant) {
304 // boolean inline = right.constant.booleanValue();
305 // left.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
309 // left.generateCode(currentScope, codeStream, valueRequired);
310 // right.generateCode(currentScope, codeStream, valueRequired);
311 // if (valueRequired) {
312 // if (falseLabel == null) {
313 // if (trueLabel != null) {
314 // // implicit falling through the FALSE case
315 // codeStream.if_icmpeq(trueLabel);
318 // // implicit falling through the TRUE case
319 // if (trueLabel == null) {
320 // codeStream.if_icmpne(falseLabel);
322 // // no implicit fall through TRUE/FALSE --> should never occur
326 // // reposition the endPC
327 // codeStream.updateLastRecordedEndPC(codeStream.position);
330 // * Boolean generation for == with non-boolean operands
333 //public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, Label trueLabel, Label falseLabel, boolean valueRequired) {
335 // int pc = codeStream.position;
337 // if ((inline = right.constant) != NotAConstant) {
338 // // optimized case: x == 0
339 // if (((left.implicitConversion >> 4) == T_int) && (inline.intValue() == 0)) {
340 // left.generateCode(currentScope, codeStream, valueRequired);
341 // if (valueRequired) {
342 // if (falseLabel == null) {
343 // if (trueLabel != null) {
344 // // implicit falling through the FALSE case
345 // codeStream.ifeq(trueLabel);
348 // // implicit falling through the TRUE case
349 // if (trueLabel == null) {
350 // codeStream.ifne(falseLabel);
352 // // no implicit fall through TRUE/FALSE --> should never occur
356 // codeStream.recordPositionsFrom(pc, this.sourceStart);
360 // if ((inline = left.constant) != NotAConstant) {
361 // // optimized case: 0 == x
362 // if (((left.implicitConversion >> 4) == T_int)
363 // && (inline.intValue() == 0)) {
364 // right.generateCode(currentScope, codeStream, valueRequired);
365 // if (valueRequired) {
366 // if (falseLabel == null) {
367 // if (trueLabel != null) {
368 // // implicit falling through the FALSE case
369 // codeStream.ifeq(trueLabel);
372 // // implicit falling through the TRUE case
373 // if (trueLabel == null) {
374 // codeStream.ifne(falseLabel);
376 // // no implicit fall through TRUE/FALSE --> should never occur
380 // codeStream.recordPositionsFrom(pc, this.sourceStart);
385 // // optimized case: x == null
386 // if (right instanceof NullLiteral) {
387 // if (left instanceof NullLiteral) {
389 // if (valueRequired) {
390 // if ((bits & OnlyValueRequiredMASK) != 0) {
391 // if (((bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
392 // codeStream.iconst_1();
394 // codeStream.iconst_0();
397 // if (falseLabel == null) {
398 // // implicit falling through the FALSE case
399 // if (trueLabel != null) {
400 // codeStream.goto_(trueLabel);
406 // left.generateCode(currentScope, codeStream, valueRequired);
407 // if (valueRequired) {
408 // if (falseLabel == null) {
409 // if (trueLabel != null) {
410 // // implicit falling through the FALSE case
411 // codeStream.ifnull(trueLabel);
414 // // implicit falling through the TRUE case
415 // if (trueLabel == null) {
416 // codeStream.ifnonnull(falseLabel);
418 // // no implicit fall through TRUE/FALSE --> should never occur
423 // codeStream.recordPositionsFrom(pc, this.sourceStart);
425 // } else if (left instanceof NullLiteral) { // optimized case: null == x
426 // right.generateCode(currentScope, codeStream, valueRequired);
427 // if (valueRequired) {
428 // if (falseLabel == null) {
429 // if (trueLabel != null) {
430 // // implicit falling through the FALSE case
431 // codeStream.ifnull(trueLabel);
434 // // implicit falling through the TRUE case
435 // if (trueLabel == null) {
436 // codeStream.ifnonnull(falseLabel);
438 // // no implicit fall through TRUE/FALSE --> should never occur
442 // codeStream.recordPositionsFrom(pc, this.sourceStart);
447 // left.generateCode(currentScope, codeStream, valueRequired);
448 // right.generateCode(currentScope, codeStream, valueRequired);
449 // if (valueRequired) {
450 // if (falseLabel == null) {
451 // if (trueLabel != null) {
452 // // implicit falling through the FALSE case
453 // switch (left.implicitConversion >> 4) { // operand runtime type
455 // codeStream.if_icmpeq(trueLabel);
458 // codeStream.fcmpl();
459 // codeStream.ifeq(trueLabel);
462 // codeStream.lcmp();
463 // codeStream.ifeq(trueLabel);
466 // codeStream.dcmpl();
467 // codeStream.ifeq(trueLabel);
470 // codeStream.if_acmpeq(trueLabel);
474 // // implicit falling through the TRUE case
475 // if (trueLabel == null) {
476 // switch (left.implicitConversion >> 4) { // operand runtime type
478 // codeStream.if_icmpne(falseLabel);
481 // codeStream.fcmpl();
482 // codeStream.ifne(falseLabel);
485 // codeStream.lcmp();
486 // codeStream.ifne(falseLabel);
489 // codeStream.dcmpl();
490 // codeStream.ifne(falseLabel);
493 // codeStream.if_acmpne(falseLabel);
496 // // no implicit fall through TRUE/FALSE --> should never occur
500 // codeStream.recordPositionsFrom(pc, this.sourceStart);
502 public boolean isCompactableOperation() {
505 public TypeBinding resolveType(BlockScope scope) {
506 // always return BooleanBinding
507 TypeBinding leftType = left.resolveType(scope);
508 TypeBinding rightType = right.resolveType(scope);
509 if (leftType == null || rightType == null){
510 constant = NotAConstant;
515 if (leftType.isBaseType() && rightType.isBaseType()) {
516 // the code is an int
517 // (cast) left == (cast) rigth --> result
518 // 0000 0000 0000 0000 0000
519 // <<16 <<12 <<8 <<4 <<0
520 int result = ResolveTypeTables[EQUAL_EQUAL][ (leftType.id << 4) + rightType.id];
521 left.implicitConversion = result >>> 12;
522 right.implicitConversion = (result >>> 4) & 0x000FF;
523 bits |= result & 0xF;
524 if ((result & 0x0000F) == T_undefined) {
525 constant = Constant.NotAConstant;
526 scope.problemReporter().invalidOperator(this, leftType, rightType);
529 computeConstant(leftType, rightType);
530 this.resolvedType = BooleanBinding;
531 return BooleanBinding;
536 if (areTypesCastCompatible(scope, rightType, leftType) || areTypesCastCompatible(scope, leftType, rightType)) {
537 // (special case for String)
538 if ((rightType.id == T_String) && (leftType.id == T_String))
539 computeConstant(leftType, rightType);
541 constant = NotAConstant;
542 if (rightType.id == T_String)
543 right.implicitConversion = String2String;
544 if (leftType.id == T_String)
545 left.implicitConversion = String2String;
546 this.resolvedType = BooleanBinding;
547 return BooleanBinding;
549 constant = NotAConstant;
550 scope.problemReporter().notCompatibleTypesError(this, leftType, rightType);
553 public void traverse(ASTVisitor visitor, BlockScope scope) {
554 if (visitor.visit(this, scope)) {
555 left.traverse(visitor, scope);
556 right.traverse(visitor, scope);
558 visitor.endVisit(this, scope);