1 /*******************************************************************************
2  * Copyright (c) 2000, 2013 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *     Stephan Herrmann - Contribution for
11  *								bug 383368 - [compiler][null] syntactic null analysis for field references
12  *								bug 403086 - [compiler][null] include the effect of 'assert' in syntactic null analysis for fields
13  *******************************************************************************/
14 package org.eclipse.jdt.internal.compiler.ast;
15 
16 import org.eclipse.jdt.internal.compiler.ASTVisitor;
17 import org.eclipse.jdt.internal.compiler.impl.*;
18 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
19 import org.eclipse.jdt.internal.compiler.codegen.*;
20 import org.eclipse.jdt.internal.compiler.flow.*;
21 import org.eclipse.jdt.internal.compiler.lookup.*;
22 
23 public class UnaryExpression extends OperatorExpression {
24 
25 	public Expression expression;
26 	public Constant optimizedBooleanConstant;
27 
UnaryExpression(Expression expression, int operator)28 	public UnaryExpression(Expression expression, int operator) {
29 		this.expression = expression;
30 		this.bits |= operator << OperatorSHIFT; // encode operator
31 	}
32 
analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)33 public FlowInfo analyseCode(
34 		BlockScope currentScope,
35 		FlowContext flowContext,
36 		FlowInfo flowInfo) {
37 	this.expression.checkNPE(currentScope, flowContext, flowInfo);
38 	if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
39 		flowContext.tagBits ^= FlowContext.INSIDE_NEGATION;
40 		flowInfo = this.expression.
41 			analyseCode(currentScope, flowContext, flowInfo).
42 			asNegatedCondition();
43 		flowContext.tagBits ^= FlowContext.INSIDE_NEGATION;
44 		return flowInfo;
45 	} else {
46 		return this.expression.
47 			analyseCode(currentScope, flowContext, flowInfo);
48 	}
49 }
50 
optimizedBooleanConstant()51 	public Constant optimizedBooleanConstant() {
52 
53 		return this.optimizedBooleanConstant == null
54 				? this.constant
55 				: this.optimizedBooleanConstant;
56 	}
57 
58 	/**
59 	 * Code generation for an unary operation
60 	 *
61 	 * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
62 	 * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
63 	 * @param valueRequired boolean
64 	 */
generateCode( BlockScope currentScope, CodeStream codeStream, boolean valueRequired)65 	public void generateCode(
66 		BlockScope currentScope,
67 		CodeStream codeStream,
68 		boolean valueRequired) {
69 
70 		int pc = codeStream.position;
71 		BranchLabel falseLabel, endifLabel;
72 		if (this.constant != Constant.NotAConstant) {
73 			// inlined value
74 			if (valueRequired) {
75 				codeStream.generateConstant(this.constant, this.implicitConversion);
76 			}
77 			codeStream.recordPositionsFrom(pc, this.sourceStart);
78 			return;
79 		}
80 		switch ((this.bits & OperatorMASK) >> OperatorSHIFT) {
81 			case NOT :
82 				switch ((this.expression.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4) /* runtime type */ {
83 					case T_boolean :
84 						// ! <boolean>
85 						// Generate code for the condition
86 						this.expression.generateOptimizedBoolean(
87 							currentScope,
88 							codeStream,
89 							null,
90 							(falseLabel = new BranchLabel(codeStream)),
91 							valueRequired);
92 						if (valueRequired) {
93 							codeStream.iconst_0();
94 							if (falseLabel.forwardReferenceCount() > 0) {
95 								codeStream.goto_(endifLabel = new BranchLabel(codeStream));
96 								codeStream.decrStackSize(1);
97 								falseLabel.place();
98 								codeStream.iconst_1();
99 								endifLabel.place();
100 							}
101 						} else { // 6596: if (!(a && b)){} - must still place falseLabel
102 							falseLabel.place();
103 						}
104 						break;
105 				}
106 				break;
107 			case TWIDDLE :
108 				switch ((this.expression.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4 /* runtime */) {
109 					case T_int :
110 						// ~int
111 						this.expression.generateCode(currentScope, codeStream, valueRequired);
112 						if (valueRequired) {
113 							codeStream.iconst_m1();
114 							codeStream.ixor();
115 						}
116 						break;
117 					case T_long :
118 						this.expression.generateCode(currentScope, codeStream, valueRequired);
119 						if (valueRequired) {
120 							codeStream.ldc2_w(-1L);
121 							codeStream.lxor();
122 						}
123 				}
124 				break;
125 			case MINUS :
126 				// - <num>
127 				if (this.constant != Constant.NotAConstant) {
128 					if (valueRequired) {
129 						switch ((this.expression.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4){ /* runtime */
130 							case T_int :
131 								codeStream.generateInlinedValue(this.constant.intValue() * -1);
132 								break;
133 							case T_float :
134 								codeStream.generateInlinedValue(this.constant.floatValue() * -1.0f);
135 								break;
136 							case T_long :
137 								codeStream.generateInlinedValue(this.constant.longValue() * -1L);
138 								break;
139 							case T_double :
140 								codeStream.generateInlinedValue(this.constant.doubleValue() * -1.0);
141 						}
142 					}
143 				} else {
144 					this.expression.generateCode(currentScope, codeStream, valueRequired);
145 					if (valueRequired) {
146 						switch ((this.expression.implicitConversion & IMPLICIT_CONVERSION_MASK) >> 4){ /* runtime type */
147 							case T_int :
148 								codeStream.ineg();
149 								break;
150 							case T_float :
151 								codeStream.fneg();
152 								break;
153 							case T_long :
154 								codeStream.lneg();
155 								break;
156 							case T_double :
157 								codeStream.dneg();
158 						}
159 					}
160 				}
161 				break;
162 			case PLUS :
163 				this.expression.generateCode(currentScope, codeStream, valueRequired);
164 		}
165 		if (valueRequired) {
166 			codeStream.generateImplicitConversion(this.implicitConversion);
167 		}
168 		codeStream.recordPositionsFrom(pc, this.sourceStart);
169 	}
170 
171 	/**
172 	 * Boolean operator code generation
173 	 *	Optimized operations are: &&, ||, <, <=, >, >=, &, |, ^
174 	 */
generateOptimizedBoolean( BlockScope currentScope, CodeStream codeStream, BranchLabel trueLabel, BranchLabel falseLabel, boolean valueRequired)175 	public void generateOptimizedBoolean(
176 		BlockScope currentScope,
177 		CodeStream codeStream,
178 		BranchLabel trueLabel,
179 		BranchLabel falseLabel,
180 		boolean valueRequired) {
181 
182 		if ((this.constant != Constant.NotAConstant) && (this.constant.typeID() == T_boolean)) {
183 			super.generateOptimizedBoolean(
184 				currentScope,
185 				codeStream,
186 				trueLabel,
187 				falseLabel,
188 				valueRequired);
189 			return;
190 		}
191 		if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
192 			this.expression.generateOptimizedBoolean(
193 				currentScope,
194 				codeStream,
195 				falseLabel,
196 				trueLabel,
197 				valueRequired);
198 		} else {
199 			super.generateOptimizedBoolean(
200 				currentScope,
201 				codeStream,
202 				trueLabel,
203 				falseLabel,
204 				valueRequired);
205 		}
206 	}
207 
printExpressionNoParenthesis(int indent, StringBuffer output)208 	public StringBuffer printExpressionNoParenthesis(int indent, StringBuffer output) {
209 
210 		output.append(operatorToString()).append(' ');
211 		return this.expression.printExpression(0, output);
212 	}
213 
resolveType(BlockScope scope)214 	public TypeBinding resolveType(BlockScope scope) {
215 		boolean expressionIsCast;
216 		if ((expressionIsCast = this.expression instanceof CastExpression) == true) this.expression.bits |= DisableUnnecessaryCastCheck; // will check later on
217 		TypeBinding expressionType = this.expression.resolveType(scope);
218 		if (expressionType == null) {
219 			this.constant = Constant.NotAConstant;
220 			return null;
221 		}
222 		int expressionTypeID = expressionType.id;
223 		// autoboxing support
224 		boolean use15specifics = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
225 		if (use15specifics) {
226 			if (!expressionType.isBaseType()) {
227 				expressionTypeID = scope.environment().computeBoxingType(expressionType).id;
228 			}
229 		}
230 		if (expressionTypeID > 15) {
231 			this.constant = Constant.NotAConstant;
232 			scope.problemReporter().invalidOperator(this, expressionType);
233 			return null;
234 		}
235 
236 		int tableId;
237 		switch ((this.bits & OperatorMASK) >> OperatorSHIFT) {
238 			case NOT :
239 				tableId = AND_AND;
240 				break;
241 			case TWIDDLE :
242 				tableId = LEFT_SHIFT;
243 				break;
244 			default :
245 				tableId = MINUS;
246 		} //+ and - cases
247 
248 		// the code is an int
249 		// (cast)  left   Op (cast)  rigth --> result
250 		//  0000   0000       0000   0000      0000
251 		//  <<16   <<12       <<8    <<4       <<0
252 		int operatorSignature = OperatorSignatures[tableId][(expressionTypeID << 4) + expressionTypeID];
253 		this.expression.computeConversion(scope, TypeBinding.wellKnownType(scope, (operatorSignature >>> 16) & 0x0000F), expressionType);
254 		this.bits |= operatorSignature & 0xF;
255 		switch (operatorSignature & 0xF) { // only switch on possible result type.....
256 			case T_boolean :
257 				this.resolvedType = TypeBinding.BOOLEAN;
258 				break;
259 			case T_byte :
260 				this.resolvedType = TypeBinding.BYTE;
261 				break;
262 			case T_char :
263 				this.resolvedType = TypeBinding.CHAR;
264 				break;
265 			case T_double :
266 				this.resolvedType = TypeBinding.DOUBLE;
267 				break;
268 			case T_float :
269 				this.resolvedType = TypeBinding.FLOAT;
270 				break;
271 			case T_int :
272 				this.resolvedType = TypeBinding.INT;
273 				break;
274 			case T_long :
275 				this.resolvedType = TypeBinding.LONG;
276 				break;
277 			default : //error........
278 				this.constant = Constant.NotAConstant;
279 				if (expressionTypeID != T_undefined)
280 					scope.problemReporter().invalidOperator(this, expressionType);
281 				return null;
282 		}
283 		// compute the constant when valid
284 		if (this.expression.constant != Constant.NotAConstant) {
285 			this.constant =
286 				Constant.computeConstantOperation(
287 					this.expression.constant,
288 					expressionTypeID,
289 					(this.bits & OperatorMASK) >> OperatorSHIFT);
290 		} else {
291 			this.constant = Constant.NotAConstant;
292 			if (((this.bits & OperatorMASK) >> OperatorSHIFT) == NOT) {
293 				Constant cst = this.expression.optimizedBooleanConstant();
294 				if (cst != Constant.NotAConstant)
295 					this.optimizedBooleanConstant = BooleanConstant.fromValue(!cst.booleanValue());
296 			}
297 		}
298 		if (expressionIsCast) {
299 		// check need for operand cast
300 			CastExpression.checkNeedForArgumentCast(scope, tableId, operatorSignature, this.expression, expressionTypeID);
301 		}
302 		return this.resolvedType;
303 	}
304 
traverse( ASTVisitor visitor, BlockScope blockScope)305 	public void traverse(
306     		ASTVisitor visitor,
307     		BlockScope blockScope) {
308 
309 		if (visitor.visit(this, blockScope)) {
310 			this.expression.traverse(visitor, blockScope);
311 		}
312 		visitor.endVisit(this, blockScope);
313 	}
314 }
315