All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aspectj.org.eclipse.jdt.internal.compiler.ast.EqualExpression Maven / Gradle / Ivy

Go to download

AspectJ tools most notably contains the AspectJ compiler (AJC). AJC applies aspects to Java classes during compilation, fully replacing Javac for plain Java classes and also compiling native AspectJ or annotation-based @AspectJ syntax. Furthermore, AJC can weave aspects into existing class files in a post-compile binary weaving step. This library is a superset of AspectJ weaver and hence also of AspectJ runtime.

There is a newer version: 1.9.22.1
Show newest version
/*******************************************************************************
 * Copyright (c) 2000, 2020 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contributions for
 *								bug 186342 - [compiler][null] Using annotations for null checking
 *								bug 331649 - [compiler][null] consider null annotations for fields
 *								bug 383368 - [compiler][null] syntactic null analysis for field references
 *								bug 382069 - [null] Make the null analysis consider JUnit's assertNotNull similarly to assertions
 *								bug 403086 - [compiler][null] include the effect of 'assert' in syntactic null analysis for fields
 *******************************************************************************/
package org.aspectj.org.eclipse.jdt.internal.compiler.ast;

import org.aspectj.org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.flow.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.*;

public class EqualExpression extends BinaryExpression {

	public EqualExpression(Expression left, Expression right,int operator) {
		super(left,right,operator);
	}
	private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) {

		// collect null status of child nodes:
		int rightStatus = this.right.nullStatus(flowInfo, flowContext);
		int leftStatus = this.left.nullStatus(flowInfo, flowContext);

		boolean leftNonNullChecked = false;
		boolean rightNonNullChecked = false;

		// check if either is a non-local expression known to be nonnull and compared to null, candidates are
		// - method/field annotated @NonNull
		// - allocation expression, some literals, this reference (see inside expressionNonNullComparison(..))
		// these checks do not leverage the flowInfo.
		boolean checkEquality = ((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL;
		if ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING_MASK) == 0) {
			if (leftStatus == FlowInfo.NON_NULL && rightStatus == FlowInfo.NULL) {
				leftNonNullChecked = scope.problemReporter().expressionNonNullComparison(this.left, checkEquality);
			} else if (leftStatus == FlowInfo.NULL && rightStatus == FlowInfo.NON_NULL) {
				rightNonNullChecked = scope.problemReporter().expressionNonNullComparison(this.right, checkEquality);
			}
		}

		boolean contextualCheckEquality = checkEquality ^ ((flowContext.tagBits & FlowContext.INSIDE_NEGATION) != 0);
		// perform flowInfo-based checks for variables and record info for syntactic null analysis for fields:
		if (!leftNonNullChecked) {
			LocalVariableBinding local = this.left.localVariableBinding();
			if (local != null) {
				if ((local.type.tagBits & TagBits.IsBaseType) == 0) {
					checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, rightStatus, this.left);
				}
			} else if (this.left instanceof Reference
							&& ((contextualCheckEquality ? rightStatus == FlowInfo.NON_NULL : rightStatus == FlowInfo.NULL))
							&& scope.compilerOptions().enableSyntacticNullAnalysisForFields)
			{
				FieldBinding field = ((Reference)this.left).lastFieldBinding();
				if (field != null && (field.type.tagBits & TagBits.IsBaseType) == 0) {
					flowContext.recordNullCheckedFieldReference((Reference) this.left, 1);
				}
			}
		}
		if (!rightNonNullChecked) {
			LocalVariableBinding local = this.right.localVariableBinding();
			if (local != null) {
				if ((local.type.tagBits & TagBits.IsBaseType) == 0) {
					checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, leftStatus, this.right);
				}
			} else if (this.right instanceof Reference
							&& ((contextualCheckEquality ? leftStatus == FlowInfo.NON_NULL : leftStatus == FlowInfo.NULL))
							&& scope.compilerOptions().enableSyntacticNullAnalysisForFields)
			{
				FieldBinding field = ((Reference)this.right).lastFieldBinding();
				if (field != null && (field.type.tagBits & TagBits.IsBaseType) == 0) {
					flowContext.recordNullCheckedFieldReference((Reference) this.right, 1);
				}
			}
		}

		// handle reachability:
		if (leftNonNullChecked || rightNonNullChecked) {
			// above checks have not propagated unreachable into the corresponding branch, do it now:
			if (checkEquality) {
				initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
			} else {
				initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
			}
		}
	}
	private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) {
		switch (nullStatus) {
			case FlowInfo.NULL :
				if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
					flowContext.recordUsingNullReference(scope, local, reference,
							FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NULL, flowInfo);
					initsWhenTrue.markAsComparedEqualToNull(local); // from thereon it is set
					initsWhenFalse.markAsComparedEqualToNonNull(local); // from thereon it is set
				} else {
					flowContext.recordUsingNullReference(scope, local, reference,
							FlowContext.CAN_ONLY_NULL_NON_NULL | FlowContext.IN_COMPARISON_NON_NULL, flowInfo);
					initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set
					initsWhenFalse.markAsComparedEqualToNull(local); // from thereon it is set
				}
				break;
			case FlowInfo.NON_NULL :
				if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
					flowContext.recordUsingNullReference(scope, local, reference,
							FlowContext.CAN_ONLY_NULL | FlowContext.IN_COMPARISON_NON_NULL, flowInfo);
					initsWhenTrue.markAsComparedEqualToNonNull(local); // from thereon it is set
				} else {
					flowContext.recordUsingNullReference(scope, local, reference,
							FlowContext.CAN_ONLY_NULL | FlowContext.IN_COMPARISON_NULL, flowInfo);
				}
				break;
		}
		// we do not impact enclosing try context because this kind of protection
		// does not preclude the variable from being null in an enclosing scope
	}
	private void analyzeLocalVariable(Expression exp, FlowInfo flowInfo) {
		if (exp instanceof SingleNameReference && (exp.bits & Binding.LOCAL) != 0 ) {
			LocalVariableBinding localBinding = (LocalVariableBinding) ((SingleNameReference ) exp).binding;
			if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) {
				localBinding.useFlag = LocalVariableBinding.USED;
			} else if (localBinding.useFlag == LocalVariableBinding.UNUSED ) {
				localBinding.useFlag = LocalVariableBinding.FAKE_USED;
			}
	}
	}
	@Override
	public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
		FlowInfo result;
		if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
			if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.typeID() == T_boolean)) {
				if (this.left.constant.booleanValue()) { //  true == anything
					//  this is equivalent to the right argument inits
					result = this.right.analyseCode(currentScope, flowContext, flowInfo);
				} else { // false == anything
					//  this is equivalent to the right argument inits negated
					result = this.right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
					analyzeLocalVariable(this.left, flowInfo);
				}
			}
			else if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.typeID() == T_boolean)) {
				if (this.right.constant.booleanValue()) { //  anything == true
					//  this is equivalent to the left argument inits
					result = this.left.analyseCode(currentScope, flowContext, flowInfo);
				} else { // anything == false
					//  this is equivalent to the right argument inits negated
					result = this.left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
					analyzeLocalVariable(this.right, flowInfo);
				}
			}
			else {
				result = this.right.analyseCode(
					currentScope, flowContext,
					this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).unconditionalInits();
			}
		} else { //NOT_EQUAL :
			if ((this.left.constant != Constant.NotAConstant) && (this.left.constant.typeID() == T_boolean)) {
				if (!this.left.constant.booleanValue()) { //  false != anything
					//  this is equivalent to the right argument inits
					result = this.right.analyseCode(currentScope, flowContext, flowInfo);
					analyzeLocalVariable(this.left, flowInfo);
				} else { // true != anything
					//  this is equivalent to the right argument inits negated
					result = this.right.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
				}
			}
			else if ((this.right.constant != Constant.NotAConstant) && (this.right.constant.typeID() == T_boolean)) {
				if (!this.right.constant.booleanValue()) { //  anything != false
					//  this is equivalent to the right argument inits
					result = this.left.analyseCode(currentScope, flowContext, flowInfo);
					analyzeLocalVariable(this.right, flowInfo);
				} else { // anything != true
					//  this is equivalent to the right argument inits negated
					result = this.left.analyseCode(currentScope, flowContext, flowInfo).asNegatedCondition();
				}
			}
			else {
				result = this.right.analyseCode(
					currentScope, flowContext,
					this.left.analyseCode(currentScope, flowContext, flowInfo).unconditionalInits()).
					/* unneeded since we flatten it: asNegatedCondition(). */
					unconditionalInits();
			}
		}
		if (result instanceof UnconditionalFlowInfo &&
				(result.tagBits & FlowInfo.UNREACHABLE) == 0) { // the flow info is flat
			result = FlowInfo.conditional(result.copy(), result.copy());
			// TODO (maxime) check, reintroduced copy
		}
	  checkNullComparison(currentScope, flowContext, result, result.initsWhenTrue(), result.initsWhenFalse());
	  return result;
	}

	public final void computeConstant(TypeBinding leftType, TypeBinding rightType) {
		if ((this.left.constant != Constant.NotAConstant) && (this.right.constant != Constant.NotAConstant)) {
			this.constant =
				Constant.computeConstantOperationEQUAL_EQUAL(
					this.left.constant,
					leftType.id,
					this.right.constant,
					rightType.id);
			if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT_EQUAL)
				this.constant = BooleanConstant.fromValue(!this.constant.booleanValue());
		} else {
			this.constant = Constant.NotAConstant;
			// no optimization for null == null
		}
	}
	/**
	 * Normal == or != code generation.
	 *
	 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
	 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
	 * @param valueRequired boolean
	 */
	@Override
	public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {

		int pc = codeStream.position;
		if (this.constant != Constant.NotAConstant) {
			if (valueRequired)
				codeStream.generateConstant(this.constant, this.implicitConversion);
			codeStream.recordPositionsFrom(pc, this.sourceStart);
			return;
		}

		if ((this.left.implicitConversion & COMPILE_TYPE_MASK) /*compile-time*/ == T_boolean) {
			generateBooleanEqual(currentScope, codeStream, valueRequired);
		} else {
			generateNonBooleanEqual(currentScope, codeStream, valueRequired);
		}
		if (valueRequired) {
			codeStream.generateImplicitConversion(this.implicitConversion);
		}
		codeStream.recordPositionsFrom(pc, this.sourceStart);
	}
	/**
	 * Boolean operator code generation
	 *	Optimized operations are: == and !=
	 */
	@Override
	public void generateOptimizedBoolean(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {

		if (this.constant != Constant.NotAConstant) {
			super.generateOptimizedBoolean(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
			return;
		}
		if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
			if ((this.left.implicitConversion & COMPILE_TYPE_MASK) /*compile-time*/ == T_boolean) {
				generateOptimizedBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
			} else {
				generateOptimizedNonBooleanEqual(currentScope, codeStream, trueLabel, falseLabel, valueRequired);
			}
		} else {
			if ((this.left.implicitConversion & COMPILE_TYPE_MASK) /*compile-time*/ == T_boolean) {
				generateOptimizedBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
			} else {
				generateOptimizedNonBooleanEqual(currentScope, codeStream, falseLabel, trueLabel, valueRequired);
			}
		}
	}

	/**
	 * Boolean generation for == with boolean operands
	 *
	 * Note this code does not optimize conditional constants !!!!
	 */
	public void generateBooleanEqual(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {

		// optimized cases:  == x,  == x,
		// optimized cases:  != x,  != x,
		boolean isEqualOperator = ((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL;
		Constant cst = this.left.optimizedBooleanConstant();
		if (cst != Constant.NotAConstant) {
			Constant rightCst = this.right.optimizedBooleanConstant();
			if (rightCst != Constant.NotAConstant) {
				//  == ,  != 
				//  == ,  != 
				this.left.generateCode(currentScope, codeStream, false);
				this.right.generateCode(currentScope, codeStream, false);
				if (valueRequired) {
					boolean leftBool = cst.booleanValue();
					boolean rightBool = rightCst.booleanValue();
					if (isEqualOperator) {
						if (leftBool == rightBool) {
							codeStream.iconst_1();
						} else {
							codeStream.iconst_0();
						}
					} else {
						if (leftBool != rightBool) {
							codeStream.iconst_1();
						} else {
							codeStream.iconst_0();
						}
					}
				}
			} else if (cst.booleanValue() == isEqualOperator) {
				//  == x,  != x
				this.left.generateCode(currentScope, codeStream, false);
				this.right.generateCode(currentScope, codeStream, valueRequired);
			} else {
				//  == x,  != x
				if (valueRequired) {
					BranchLabel falseLabel = new BranchLabel(codeStream);
					this.left.generateCode(currentScope, codeStream, false);
					this.right.generateOptimizedBoolean(currentScope, codeStream, null, falseLabel, valueRequired);
					// comparison is TRUE
					codeStream.iconst_0();
					if ((this.bits & IsReturnedValue) != 0){
						codeStream.generateImplicitConversion(this.implicitConversion);
						codeStream.generateReturnBytecode(this);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_1();
					} else {
						BranchLabel endLabel = new BranchLabel(codeStream);
						codeStream.goto_(endLabel);
						codeStream.decrStackSize(1);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_1();
						endLabel.place();
					}
				} else {
					this.left.generateCode(currentScope, codeStream, false);
					this.right.generateCode(currentScope, codeStream, false);
				}
//				left.generateCode(currentScope, codeStream, false);
//				right.generateCode(currentScope, codeStream, valueRequired);
//				if (valueRequired) {
//					codeStream.iconst_1();
//					codeStream.ixor(); // negate
//				}
			}
			return;
		}
		cst = this.right.optimizedBooleanConstant();
		if (cst != Constant.NotAConstant) {
			if (cst.booleanValue() == isEqualOperator) {
				// x == , x != 
				this.left.generateCode(currentScope, codeStream, valueRequired);
				this.right.generateCode(currentScope, codeStream, false);
			} else {
				// x == , x != 
				if (valueRequired) {
					BranchLabel falseLabel = new BranchLabel(codeStream);
					this.left.generateOptimizedBoolean(currentScope, codeStream, null, falseLabel, valueRequired);
					this.right.generateCode(currentScope, codeStream, false);
					// comparison is TRUE
					codeStream.iconst_0();
					if ((this.bits & IsReturnedValue) != 0){
						codeStream.generateImplicitConversion(this.implicitConversion);
						codeStream.generateReturnBytecode(this);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_1();
					} else {
						BranchLabel endLabel = new BranchLabel(codeStream);
						codeStream.goto_(endLabel);
						codeStream.decrStackSize(1);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_1();
						endLabel.place();
					}
				} else {
					this.left.generateCode(currentScope, codeStream, false);
					this.right.generateCode(currentScope, codeStream, false);
				}
//				left.generateCode(currentScope, codeStream, valueRequired);
//				right.generateCode(currentScope, codeStream, false);
//				if (valueRequired) {
//					codeStream.iconst_1();
//					codeStream.ixor(); // negate
//				}
			}
			return;
		}
		// default case
		this.left.generateCode(currentScope, codeStream, valueRequired);
		this.right.generateCode(currentScope, codeStream, valueRequired);

		if (valueRequired) {
			if (isEqualOperator) {
				BranchLabel falseLabel;
				codeStream.if_icmpne(falseLabel = new BranchLabel(codeStream));
				// comparison is TRUE
				codeStream.iconst_1();
				if ((this.bits & IsReturnedValue) != 0){
					codeStream.generateImplicitConversion(this.implicitConversion);
					codeStream.generateReturnBytecode(this);
					// comparison is FALSE
					falseLabel.place();
					codeStream.iconst_0();
				} else {
					BranchLabel endLabel = new BranchLabel(codeStream);
					codeStream.goto_(endLabel);
					codeStream.decrStackSize(1);
					// comparison is FALSE
					falseLabel.place();
					codeStream.iconst_0();
					endLabel.place();
				}
			} else {
				codeStream.ixor();
			}
		}
	}

	/**
	 * Boolean generation for == with boolean operands
	 *
	 * Note this code does not optimize conditional constants !!!!
	 */
	public void generateOptimizedBooleanEqual(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {

		// optimized cases: true == x, false == x
		if (this.left.constant != Constant.NotAConstant) {
			boolean inline = this.left.constant.booleanValue();
			this.right.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
			return;
		} // optimized cases: x == true, x == false
		if (this.right.constant != Constant.NotAConstant) {
			boolean inline = this.right.constant.booleanValue();
			this.left.generateOptimizedBoolean(currentScope, codeStream, (inline ? trueLabel : falseLabel), (inline ? falseLabel : trueLabel), valueRequired);
			return;
		}
		// default case
		this.left.generateCode(currentScope, codeStream, valueRequired);
		this.right.generateCode(currentScope, codeStream, valueRequired);
		int pc = codeStream.position;
		if (valueRequired) {
			if (falseLabel == null) {
				if (trueLabel != null) {
					// implicit falling through the FALSE case
					codeStream.if_icmpeq(trueLabel);
				}
			} else {
				// implicit falling through the TRUE case
				if (trueLabel == null) {
					codeStream.if_icmpne(falseLabel);
				} else {
					// no implicit fall through TRUE/FALSE --> should never occur
				}
			}
		}
		codeStream.recordPositionsFrom(pc, this.sourceEnd);
	}
	/**
	 * Boolean generation for == with non-boolean operands
	 *
	 */
	public void generateNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {

		boolean isEqualOperator = ((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL;
		if (((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_int) {
			Constant cst;
			if ((cst = this.left.constant) != Constant.NotAConstant && cst.intValue() == 0) {
				// optimized case: 0 == x, 0 != x
				this.right.generateCode(currentScope, codeStream, valueRequired);
				if (valueRequired) {
					BranchLabel falseLabel = new BranchLabel(codeStream);
					if (isEqualOperator) {
						codeStream.ifne(falseLabel);
					} else {
						codeStream.ifeq(falseLabel);
					}
					// comparison is TRUE
					codeStream.iconst_1();
					if ((this.bits & IsReturnedValue) != 0){
						codeStream.generateImplicitConversion(this.implicitConversion);
						codeStream.generateReturnBytecode(this);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_0();
					} else {
						BranchLabel endLabel = new BranchLabel(codeStream);
						codeStream.goto_(endLabel);
						codeStream.decrStackSize(1);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_0();
						endLabel.place();
					}
				}
				return;
			}
			if ((cst = this.right.constant) != Constant.NotAConstant && cst.intValue() == 0) {
				// optimized case: x == 0, x != 0
				this.left.generateCode(currentScope, codeStream, valueRequired);
				if (valueRequired) {
					BranchLabel falseLabel = new BranchLabel(codeStream);
					if (isEqualOperator) {
						codeStream.ifne(falseLabel);
					} else {
						codeStream.ifeq(falseLabel);
					}
					// comparison is TRUE
					codeStream.iconst_1();
					if ((this.bits & IsReturnedValue) != 0){
						codeStream.generateImplicitConversion(this.implicitConversion);
						codeStream.generateReturnBytecode(this);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_0();
					} else {
						BranchLabel endLabel = new BranchLabel(codeStream);
						codeStream.goto_(endLabel);
						codeStream.decrStackSize(1);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_0();
						endLabel.place();
					}
				}
				return;
			}
		}

		// null cases
		if (this.right instanceof NullLiteral) {
			if (this.left instanceof NullLiteral) {
				// null == null, null != null
				if (valueRequired) {
					if (isEqualOperator) {
						codeStream.iconst_1();
					} else {
						codeStream.iconst_0();
					}
				}
			} else {
				// x == null, x != null
				this.left.generateCode(currentScope, codeStream, valueRequired);
				if (valueRequired) {
					BranchLabel falseLabel = new BranchLabel(codeStream);
					if (isEqualOperator) {
						codeStream.ifnonnull(falseLabel);
					} else {
						codeStream.ifnull(falseLabel);
					}
					// comparison is TRUE
					codeStream.iconst_1();
					if ((this.bits & IsReturnedValue) != 0){
						codeStream.generateImplicitConversion(this.implicitConversion);
						codeStream.generateReturnBytecode(this);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_0();
					} else {
						BranchLabel endLabel = new BranchLabel(codeStream);
						codeStream.goto_(endLabel);
						codeStream.decrStackSize(1);
						// comparison is FALSE
						falseLabel.place();
						codeStream.iconst_0();
						endLabel.place();
					}
				}
			}
			return;
		} else if (this.left instanceof NullLiteral) {
			// null = x, null != x
			this.right.generateCode(currentScope, codeStream, valueRequired);
			if (valueRequired) {
				BranchLabel falseLabel = new BranchLabel(codeStream);
				if (isEqualOperator) {
					codeStream.ifnonnull(falseLabel);
				} else {
					codeStream.ifnull(falseLabel);
				}
				// comparison is TRUE
				codeStream.iconst_1();
				if ((this.bits & IsReturnedValue) != 0){
					codeStream.generateImplicitConversion(this.implicitConversion);
					codeStream.generateReturnBytecode(this);
					// comparison is FALSE
					falseLabel.place();
					codeStream.iconst_0();
				} else {
					BranchLabel endLabel = new BranchLabel(codeStream);
					codeStream.goto_(endLabel);
					codeStream.decrStackSize(1);
					// comparison is FALSE
					falseLabel.place();
					codeStream.iconst_0();
					endLabel.place();
				}
			}
			return;
		}

		// default case
		this.left.generateCode(currentScope, codeStream, valueRequired);
		this.right.generateCode(currentScope, codeStream, valueRequired);
		if (valueRequired) {
			BranchLabel falseLabel = new BranchLabel(codeStream);
			if (isEqualOperator) {
				switch ((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type
					case T_int :
						codeStream.if_icmpne(falseLabel);
						break;
					case T_float :
						codeStream.fcmpl();
						codeStream.ifne(falseLabel);
						break;
					case T_long :
						codeStream.lcmp();
						codeStream.ifne(falseLabel);
						break;
					case T_double :
						codeStream.dcmpl();
						codeStream.ifne(falseLabel);
						break;
					default :
						codeStream.if_acmpne(falseLabel);
				}
			} else {
				switch ((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type
					case T_int :
						codeStream.if_icmpeq(falseLabel);
						break;
					case T_float :
						codeStream.fcmpl();
						codeStream.ifeq(falseLabel);
						break;
					case T_long :
						codeStream.lcmp();
						codeStream.ifeq(falseLabel);
						break;
					case T_double :
						codeStream.dcmpl();
						codeStream.ifeq(falseLabel);
						break;
					default :
						codeStream.if_acmpeq(falseLabel);
				}
			}
			// comparison is TRUE
			codeStream.iconst_1();
			if ((this.bits & IsReturnedValue) != 0){
				codeStream.generateImplicitConversion(this.implicitConversion);
				codeStream.generateReturnBytecode(this);
				// comparison is FALSE
				falseLabel.place();
				codeStream.iconst_0();
			} else {
				BranchLabel endLabel = new BranchLabel(codeStream);
				codeStream.goto_(endLabel);
				codeStream.decrStackSize(1);
				// comparison is FALSE
				falseLabel.place();
				codeStream.iconst_0();
				endLabel.place();
			}
		}
	}

	/**
	 * Boolean generation for == with non-boolean operands
	 *
	 */
	public void generateOptimizedNonBooleanEqual(BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired) {

		int pc = codeStream.position;
		Constant inline;
		if ((inline = this.right.constant) != Constant.NotAConstant) {
			// optimized case: x == 0
			if ((((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_int) && (inline.intValue() == 0)) {
				this.left.generateCode(currentScope, codeStream, valueRequired);
				if (valueRequired) {
					if (falseLabel == null) {
						if (trueLabel != null) {
							// implicit falling through the FALSE case
							codeStream.ifeq(trueLabel);
						}
					} else {
						// implicit falling through the TRUE case
						if (trueLabel == null) {
							codeStream.ifne(falseLabel);
						} else {
							// no implicit fall through TRUE/FALSE --> should never occur
						}
					}
				}
				codeStream.recordPositionsFrom(pc, this.sourceStart);
				return;
			}
		}
		if ((inline = this.left.constant) != Constant.NotAConstant) {
			// optimized case: 0 == x
			if ((((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) == T_int)
				&& (inline.intValue() == 0)) {
				this.right.generateCode(currentScope, codeStream, valueRequired);
				if (valueRequired) {
					if (falseLabel == null) {
						if (trueLabel != null) {
							// implicit falling through the FALSE case
							codeStream.ifeq(trueLabel);
						}
					} else {
						// implicit falling through the TRUE case
						if (trueLabel == null) {
							codeStream.ifne(falseLabel);
						} else {
							// no implicit fall through TRUE/FALSE --> should never occur
						}
					}
				}
				codeStream.recordPositionsFrom(pc, this.sourceStart);
				return;
			}
		}
		// null cases
		// optimized case: x == null
		if (this.right instanceof NullLiteral) {
			if (this.left instanceof NullLiteral) {
				// null == null
				if (valueRequired) {
					if (falseLabel == null) {
						// implicit falling through the FALSE case
						if (trueLabel != null) {
							codeStream.goto_(trueLabel);
						}
					}
				}
			} else {
				this.left.generateCode(currentScope, codeStream, valueRequired);
				if (valueRequired) {
					if (falseLabel == null) {
						if (trueLabel != null) {
							// implicit falling through the FALSE case
							codeStream.ifnull(trueLabel);
						}
					} else {
						// implicit falling through the TRUE case
						if (trueLabel == null) {
							codeStream.ifnonnull(falseLabel);
						} else {
							// no implicit fall through TRUE/FALSE --> should never occur
						}
					}
				}
			}
			codeStream.recordPositionsFrom(pc, this.sourceStart);
			return;
		} else if (this.left instanceof NullLiteral) { // optimized case: null == x
			this.right.generateCode(currentScope, codeStream, valueRequired);
			if (valueRequired) {
				if (falseLabel == null) {
					if (trueLabel != null) {
						// implicit falling through the FALSE case
						codeStream.ifnull(trueLabel);
					}
				} else {
					// implicit falling through the TRUE case
					if (trueLabel == null) {
						codeStream.ifnonnull(falseLabel);
					} else {
						// no implicit fall through TRUE/FALSE --> should never occur
					}
				}
			}
			codeStream.recordPositionsFrom(pc, this.sourceStart);
			return;
		}

		// default case
		this.left.generateCode(currentScope, codeStream, valueRequired);
		this.right.generateCode(currentScope, codeStream, valueRequired);
		if (valueRequired) {
			if (falseLabel == null) {
				if (trueLabel != null) {
					// implicit falling through the FALSE case
					switch ((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type
						case T_int :
							codeStream.if_icmpeq(trueLabel);
							break;
						case T_float :
							codeStream.fcmpl();
							codeStream.ifeq(trueLabel);
							break;
						case T_long :
							codeStream.lcmp();
							codeStream.ifeq(trueLabel);
							break;
						case T_double :
							codeStream.dcmpl();
							codeStream.ifeq(trueLabel);
							break;
						default :
							codeStream.if_acmpeq(trueLabel);
					}
				}
			} else {
				// implicit falling through the TRUE case
				if (trueLabel == null) {
					switch ((this.left.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) { // operand runtime type
						case T_int :
							codeStream.if_icmpne(falseLabel);
							break;
						case T_float :
							codeStream.fcmpl();
							codeStream.ifne(falseLabel);
							break;
						case T_long :
							codeStream.lcmp();
							codeStream.ifne(falseLabel);
							break;
						case T_double :
							codeStream.dcmpl();
							codeStream.ifne(falseLabel);
							break;
						default :
							codeStream.if_acmpne(falseLabel);
					}
				} else {
					// no implicit fall through TRUE/FALSE --> should never occur
				}
			}
		}
		codeStream.recordPositionsFrom(pc, this.sourceStart);
	}
	@Override
	public boolean isCompactableOperation() {
		return false;
	}

	@Override
	protected Constant optimizedNullComparisonConstant() {
		int operator = (this.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
		if (operator == OperatorIds.EQUAL_EQUAL) {
			if (this.left instanceof NullLiteral && this.right instanceof NullLiteral) {
				return BooleanConstant.fromValue(true);
			}
		} else if (operator == OperatorIds.NOT_EQUAL) {
			if (this.left instanceof NullLiteral && this.right instanceof NullLiteral) {
				return BooleanConstant.fromValue(false);
			}
		}
		return Constant.NotAConstant;
	}

	@Override
	public TypeBinding resolveType(BlockScope scope) {

			boolean leftIsCast, rightIsCast;
			if ((leftIsCast = this.left instanceof CastExpression) == true) this.left.bits |= DisableUnnecessaryCastCheck; // will check later on
			TypeBinding originalLeftType = this.left.resolveType(scope);

			if ((rightIsCast = this.right instanceof CastExpression) == true) this.right.bits |= DisableUnnecessaryCastCheck; // will check later on
			TypeBinding originalRightType = this.right.resolveType(scope);

		// always return BooleanBinding
		if (originalLeftType == null || originalRightType == null){
			this.constant = Constant.NotAConstant;
			return null;
		}

		final CompilerOptions compilerOptions = scope.compilerOptions();
		if (compilerOptions.complainOnUninternedIdentityComparison && originalRightType.hasTypeBit(TypeIds.BitUninternedType) && originalLeftType.hasTypeBit(TypeIds.BitUninternedType))
			scope.problemReporter().uninternedIdentityComparison(this, originalLeftType, originalRightType, scope.referenceCompilationUnit());

		// autoboxing support
		boolean use15specifics = compilerOptions.sourceLevel >= ClassFileConstants.JDK1_5;
		TypeBinding leftType = originalLeftType, rightType = originalRightType;
		if (use15specifics) {
			if (leftType != TypeBinding.NULL && leftType.isBaseType()) {
				if (!rightType.isBaseType()) {
					rightType = scope.environment().computeBoxingType(rightType);
				}
			} else {
				if (rightType != TypeBinding.NULL && rightType.isBaseType()) {
					leftType = scope.environment().computeBoxingType(leftType);
				}
			}
		}
		// both base type
		if (leftType.isBaseType() && rightType.isBaseType()) {
			int leftTypeID = leftType.id;
			int rightTypeID = rightType.id;

			// the code is an int
			// (cast)  left   == (cast)  right --> result
			//  0000   0000       0000   0000      0000
			//  <<16   <<12       <<8    <<4       <<0
			int operatorSignature = OperatorSignatures[EQUAL_EQUAL][ (leftTypeID << 4) + rightTypeID];
			this.left.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), originalLeftType);
			this.right.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 8) & 0x0000F), originalRightType);
			this.bits |= operatorSignature & 0xF;
			if ((operatorSignature & 0x0000F) == T_undefined) {
				this.constant = Constant.NotAConstant;
				scope.problemReporter().invalidOperator(this, leftType, rightType);
				return null;
			}
			// check need for operand cast
			if (leftIsCast || rightIsCast) {
				CastExpression.checkNeedForArgumentCasts(scope, EQUAL_EQUAL, operatorSignature, this.left, leftType.id, leftIsCast, this.right, rightType.id, rightIsCast);
			}
			computeConstant(leftType, rightType);

			// check whether comparing identical expressions
			Binding leftDirect = Expression.getDirectBinding(this.left);
			if (leftDirect != null && leftDirect == Expression.getDirectBinding(this.right)) {
				if (leftTypeID != TypeIds.T_double && leftTypeID != TypeIds.T_float
						&&(!(this.right instanceof Assignment))) // https://bugs.eclipse.org/bugs/show_bug.cgi?id=281776
					scope.problemReporter().comparingIdenticalExpressions(this);
			} else if (this.constant != Constant.NotAConstant) {
				// https://bugs.eclipse.org/bugs/show_bug.cgi?id=276740
				int operator = (this.bits & OperatorMASK) >> OperatorSHIFT;
				if ((operator == EQUAL_EQUAL && this.constant == BooleanConstant.fromValue(true))
						|| (operator == NOT_EQUAL && this.constant == BooleanConstant.fromValue(false)))
					scope.problemReporter().comparingIdenticalExpressions(this);
			}
			return this.resolvedType = TypeBinding.BOOLEAN;
		}

		// Object references
		// spec 15.20.3
		if ((!leftType.isBaseType() || leftType == TypeBinding.NULL) // cannot compare: Object == (int)0
				&& (!rightType.isBaseType() || rightType == TypeBinding.NULL)
				&& (checkCastTypesCompatibility(scope, leftType, rightType, null, true)
						|| checkCastTypesCompatibility(scope, rightType, leftType, null, true))) {

			// (special case for String)
			if ((rightType.id == T_JavaLangString) && (leftType.id == T_JavaLangString)) {
				computeConstant(leftType, rightType);
			} else {
				this.constant = Constant.NotAConstant;
			}
			TypeBinding objectType = scope.getJavaLangObject();
			this.left.computeConversion(scope, objectType, leftType);
			this.right.computeConversion(scope, objectType, rightType);
			// check need for operand cast
			boolean unnecessaryLeftCast = (this.left.bits & UnnecessaryCast) != 0;
			boolean unnecessaryRightCast = (this.right.bits & UnnecessaryCast) != 0;
			if (unnecessaryLeftCast || unnecessaryRightCast) {
				TypeBinding alternateLeftType = unnecessaryLeftCast ? ((CastExpression)this.left).expression.resolvedType : leftType;
				TypeBinding alternateRightType = unnecessaryRightCast ? ((CastExpression)this.right).expression.resolvedType : rightType;
				// Bug 543727 - check if either cast is really needed
				if (!isCastNeeded(alternateLeftType, alternateRightType)) {
					if (checkCastTypesCompatibility(scope, alternateLeftType, alternateRightType, null, false)
							|| checkCastTypesCompatibility(scope, alternateRightType, alternateLeftType, null, false)) {
						if (unnecessaryLeftCast) scope.problemReporter().unnecessaryCast((CastExpression)this.left);
						if (unnecessaryRightCast) scope.problemReporter().unnecessaryCast((CastExpression)this.right);
					}
				}
			}
			// check whether comparing identical expressions
			Binding leftDirect = Expression.getDirectBinding(this.left);
			if (leftDirect != null && leftDirect == Expression.getDirectBinding(this.right)) {
				if (!(this.right instanceof Assignment)) {
					scope.problemReporter().comparingIdenticalExpressions(this);
				}
			}
			return this.resolvedType = TypeBinding.BOOLEAN;
		}
		this.constant = Constant.NotAConstant;
		scope.problemReporter().notCompatibleTypesError(this, leftType, rightType);
		return null;
	}

	private boolean isCastNeeded(TypeBinding leftType, TypeBinding rightType) {
		// Bug 543727 - if either type is parameterized and the other is a base type,
		// a cast is necessary, even if boxing the base type will result in a compatible
		// type.
		if (leftType.isParameterizedType()) {
			return rightType.isBaseType();
		}
		if (rightType.isParameterizedType()) {
			return leftType.isBaseType();
		}
		return false;
	}

	@Override
	public void traverse(ASTVisitor visitor, BlockScope scope) {
		if (visitor.visit(this, scope)) {
			this.left.traverse(visitor, scope);
			this.right.traverse(visitor, scope);
		}
		visitor.endVisit(this, scope);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy