org.eclipse.jdt.internal.compiler.ast.UnaryExpression Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ecj Show documentation
Show all versions of ecj Show documentation
Eclipse Compiler for Java(TM)
/*******************************************************************************
* Copyright (c) 2000, 2021 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 - Contribution for
* bug 383368 - [compiler][null] syntactic null analysis for field references
* bug 403086 - [compiler][null] include the effect of 'assert' in syntactic null analysis for fields
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.impl.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
public class UnaryExpression extends OperatorExpression {
public Expression expression;
public Constant optimizedBooleanConstant;
public UnaryExpression(Expression expression, int operator) {
this.expression = expression;
this.bits |= operator << OperatorSHIFT; // encode operator
}
@Override
public FlowInfo analyseCode(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo) {
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
flowContext.tagBits ^= FlowContext.INSIDE_NEGATION;
flowInfo = this.expression.
analyseCode(currentScope, flowContext, flowInfo).
asNegatedCondition();
flowContext.tagBits ^= FlowContext.INSIDE_NEGATION;
} else {
flowInfo = this.expression.
analyseCode(currentScope, flowContext, flowInfo);
}
this.expression.checkNPE(currentScope, flowContext, flowInfo);
return flowInfo;
}
@Override
protected void updateFlowOnBooleanResult(FlowInfo flowInfo, boolean result) {
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT)
this.expression.updateFlowOnBooleanResult(flowInfo, !result);
}
@Override
public Constant optimizedBooleanConstant() {
return this.optimizedBooleanConstant == null
? this.constant
: this.optimizedBooleanConstant;
}
/**
* Code generation for an unary operation
*
* @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;
BranchLabel falseLabel, endifLabel;
if (this.constant != Constant.NotAConstant) {
// inlined value
if (valueRequired) {
codeStream.generateConstant(this.constant, this.implicitConversion);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
return;
}
switch ((this.bits & OperatorMASK) >> OperatorSHIFT) {
case NOT :
switch ((this.expression.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) /* runtime type */ {
case T_boolean :
// !
// Generate code for the condition
this.expression.generateOptimizedBoolean(
currentScope,
codeStream,
null,
(falseLabel = new BranchLabel(codeStream)),
valueRequired);
if (valueRequired) {
codeStream.iconst_0();
if (falseLabel.forwardReferenceCount() > 0) {
codeStream.goto_(endifLabel = new BranchLabel(codeStream));
codeStream.decrStackSize(1);
falseLabel.place();
codeStream.iconst_1();
endifLabel.place();
}
} else { // 6596: if (!(a && b)){} - must still place falseLabel
falseLabel.place();
}
break;
}
break;
case TWIDDLE :
switch ((this.expression.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4 /* runtime */) {
case T_int :
// ~int
this.expression.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
codeStream.iconst_m1();
codeStream.ixor();
}
break;
case T_long :
this.expression.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
codeStream.ldc2_w(-1L);
codeStream.lxor();
}
}
break;
case MINUS :
// -
if (this.constant != Constant.NotAConstant) {
if (valueRequired) {
switch ((this.expression.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4){ /* runtime */
case T_int :
codeStream.generateInlinedValue(this.constant.intValue() * -1);
break;
case T_float :
codeStream.generateInlinedValue(this.constant.floatValue() * -1.0f);
break;
case T_long :
codeStream.generateInlinedValue(this.constant.longValue() * -1L);
break;
case T_double :
codeStream.generateInlinedValue(this.constant.doubleValue() * -1.0);
}
}
} else {
this.expression.generateCode(currentScope, codeStream, valueRequired);
if (valueRequired) {
switch ((this.expression.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4){ /* runtime type */
case T_int :
codeStream.ineg();
break;
case T_float :
codeStream.fneg();
break;
case T_long :
codeStream.lneg();
break;
case T_double :
codeStream.dneg();
}
}
}
break;
case PLUS :
this.expression.generateCode(currentScope, codeStream, valueRequired);
}
if (valueRequired) {
codeStream.generateImplicitConversion(this.implicitConversion);
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
/**
* Boolean operator code generation
* Optimized operations are: &&, ||, <, <=, >, >=, &, |, ^
*/
@Override
public void generateOptimizedBoolean(
BlockScope currentScope,
CodeStream codeStream,
BranchLabel trueLabel,
BranchLabel falseLabel,
boolean valueRequired) {
if ((this.constant != Constant.NotAConstant) && (this.constant.typeID() == T_boolean)) {
super.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
return;
}
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
this.expression.generateOptimizedBoolean(
currentScope,
codeStream,
falseLabel,
trueLabel,
valueRequired);
} else {
super.generateOptimizedBoolean(
currentScope,
codeStream,
trueLabel,
falseLabel,
valueRequired);
}
}
@Override
public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
output.append(operatorToString()).append(' ');
return this.expression.printExpression(0, output);
}
@Override
public void collectPatternVariablesToScope(LocalVariableBinding[] variables, BlockScope scope) {
this.expression.collectPatternVariablesToScope(variables, scope);
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
variables = this.expression.getPatternVariablesWhenTrue();
if (variables != null)
this.addPatternVariablesWhenFalse(variables);
variables = this.expression.getPatternVariablesWhenFalse();
if (variables != null)
this.addPatternVariablesWhenTrue(variables);
} else {
variables = this.expression.getPatternVariablesWhenTrue();
this.addPatternVariablesWhenTrue(variables);
variables = this.expression.getPatternVariablesWhenFalse();
this.addPatternVariablesWhenFalse(variables);
}
}
@Override
public TypeBinding resolveType(BlockScope scope) {
boolean expressionIsCast;
if ((expressionIsCast = this.expression instanceof CastExpression) == true) this.expression.bits |= DisableUnnecessaryCastCheck; // will check later on
TypeBinding expressionType = this.expression.resolveType(scope);
if (expressionType == null) {
this.constant = Constant.NotAConstant;
return null;
}
int expressionTypeID = expressionType.id;
// autoboxing support
boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
if (use15specifics) {
if (!expressionType.isBaseType()) {
expressionTypeID = scope.environment().computeBoxingType(expressionType).id;
}
}
if (expressionTypeID > 15) {
this.constant = Constant.NotAConstant;
scope.problemReporter().invalidOperator(this, expressionType);
return null;
}
int tableId;
switch ((this.bits & OperatorMASK) >> OperatorSHIFT) {
case NOT :
tableId = AND_AND;
break;
case TWIDDLE :
tableId = LEFT_SHIFT;
break;
default :
tableId = MINUS;
} //+ and - cases
// the code is an int
// (cast) left Op (cast) rigth --> result
// 0000 0000 0000 0000 0000
// <<16 <<12 <<8 <<4 <<0
int operatorSignature = OperatorSignatures[tableId][(expressionTypeID << 4) + expressionTypeID];
this.expression.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), expressionType);
this.bits |= operatorSignature & 0xF;
switch (operatorSignature & 0xF) { // only switch on possible result type.....
case T_boolean :
this.resolvedType = TypeBinding.BOOLEAN;
break;
case T_byte :
this.resolvedType = TypeBinding.BYTE;
break;
case T_char :
this.resolvedType = TypeBinding.CHAR;
break;
case T_double :
this.resolvedType = TypeBinding.DOUBLE;
break;
case T_float :
this.resolvedType = TypeBinding.FLOAT;
break;
case T_int :
this.resolvedType = TypeBinding.INT;
break;
case T_long :
this.resolvedType = TypeBinding.LONG;
break;
default : //error........
this.constant = Constant.NotAConstant;
if (expressionTypeID != T_undefined)
scope.problemReporter().invalidOperator(this, expressionType);
return null;
}
// compute the constant when valid
if (this.expression.constant != Constant.NotAConstant) {
this.constant =
Constant.computeConstantOperation(
this.expression.constant,
expressionTypeID,
(this.bits & OperatorMASK) >> OperatorSHIFT);
} else {
this.constant = Constant.NotAConstant;
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
Constant cst = this.expression.optimizedBooleanConstant();
if (cst != Constant.NotAConstant)
this.optimizedBooleanConstant = BooleanConstant.fromValue(!cst.booleanValue());
}
}
if (expressionIsCast) {
// check need for operand cast
CastExpression.checkNeedForArgumentCast(scope, tableId, operatorSignature, this.expression, expressionTypeID);
}
return this.resolvedType;
}
@Override
public boolean containsPatternVariable() {
return this.expression.containsPatternVariable();
}
@Override
public LocalDeclaration getPatternVariableIntroduced() {
return this.expression.getPatternVariableIntroduced();
}
@Override
public void traverse(
ASTVisitor visitor,
BlockScope blockScope) {
if (visitor.visit(this, blockScope)) {
this.expression.traverse(visitor, blockScope);
}
visitor.endVisit(this, blockScope);
}
}