| /******************************************************************************* |
| * 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 : |
| // ! <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 : |
| // - <num> |
| 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); |
| } |
| } |