1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 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
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler.ast;
12 
13 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
14 import org.eclipse.jdt.internal.compiler.codegen.*;
15 import org.eclipse.jdt.internal.compiler.flow.*;
16 import org.eclipse.jdt.internal.compiler.impl.Constant;
17 import org.eclipse.jdt.internal.compiler.lookup.*;
18 import org.eclipse.jdt.internal.compiler.ASTVisitor;
19 
20 public class AssertStatement extends Statement {
21 
22 	public Expression assertExpression, exceptionArgument;
23 
24 	// for local variable attribute
25 	int preAssertInitStateIndex = -1;
26 	private FieldBinding assertionSyntheticFieldBinding;
27 
AssertStatement( Expression exceptionArgument, Expression assertExpression, int startPosition)28 	public AssertStatement(
29 		Expression exceptionArgument,
30 		Expression assertExpression,
31 		int startPosition) {
32 
33 		this.assertExpression = assertExpression;
34 		this.exceptionArgument = exceptionArgument;
35 		sourceStart = startPosition;
36 		sourceEnd = exceptionArgument.sourceEnd;
37 	}
38 
AssertStatement(Expression assertExpression, int startPosition)39 	public AssertStatement(Expression assertExpression, int startPosition) {
40 
41 		this.assertExpression = assertExpression;
42 		sourceStart = startPosition;
43 		sourceEnd = assertExpression.sourceEnd;
44 	}
45 
analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)46 	public FlowInfo analyseCode(
47 		BlockScope currentScope,
48 		FlowContext flowContext,
49 		FlowInfo flowInfo) {
50 
51 		preAssertInitStateIndex = currentScope.methodScope().recordInitializationStates(flowInfo);
52 
53 		Constant cst = this.assertExpression.optimizedBooleanConstant();
54 		boolean isOptimizedTrueAssertion = cst != NotAConstant && cst.booleanValue() == true;
55 		boolean isOptimizedFalseAssertion = cst != NotAConstant && cst.booleanValue() == false;
56 
57 		FlowInfo assertInfo = flowInfo.copy();
58 		if (isOptimizedTrueAssertion) {
59 			assertInfo.setReachMode(FlowInfo.UNREACHABLE);
60 		}
61 		assertInfo = assertExpression.analyseCode(currentScope, flowContext, assertInfo).unconditionalInits();
62 
63 		if (exceptionArgument != null) {
64 			// only gets evaluated when escaping - results are not taken into account
65 			FlowInfo exceptionInfo = exceptionArgument.analyseCode(currentScope, flowContext, assertInfo.copy());
66 
67 			if (!isOptimizedTrueAssertion){
68 				flowContext.checkExceptionHandlers(
69 					currentScope.getJavaLangAssertionError(),
70 					this,
71 					exceptionInfo,
72 					currentScope);
73 			}
74 		}
75 
76 		if (!isOptimizedTrueAssertion){
77 			// add the assert support in the clinit
78 			manageSyntheticAccessIfNecessary(currentScope, flowInfo);
79 		}
80 		if (isOptimizedFalseAssertion) {
81 			return flowInfo; // if assertions are enabled, the following code will be unreachable
82 		} else {
83 			return flowInfo.mergedWith(assertInfo.unconditionalInits());
84 		}
85 	}
86 
generateCode(BlockScope currentScope, CodeStream codeStream)87 	public void generateCode(BlockScope currentScope, CodeStream codeStream) {
88 
89 		if ((bits & IsReachableMASK) == 0) {
90 			return;
91 		}
92 		int pc = codeStream.position;
93 
94 		if (this.assertionSyntheticFieldBinding != null) {
95 			Label assertionActivationLabel = new Label(codeStream);
96 			codeStream.getstatic(this.assertionSyntheticFieldBinding);
97 			codeStream.ifne(assertionActivationLabel);
98 
99 			Label falseLabel = new Label(codeStream);
100 			this.assertExpression.generateOptimizedBoolean(currentScope, codeStream, (falseLabel = new Label(codeStream)), null , true);
101 			codeStream.newJavaLangAssertionError();
102 			codeStream.dup();
103 			if (exceptionArgument != null) {
104 				exceptionArgument.generateCode(currentScope, codeStream, true);
105 				codeStream.invokeJavaLangAssertionErrorConstructor(exceptionArgument.implicitConversion & 0xF);
106 			} else {
107 				codeStream.invokeJavaLangAssertionErrorDefaultConstructor();
108 			}
109 			codeStream.athrow();
110 			falseLabel.place();
111 			assertionActivationLabel.place();
112 		}
113 
114 		// May loose some local variable initializations : affecting the local variable attributes
115 		if (preAssertInitStateIndex != -1) {
116 			codeStream.removeNotDefinitelyAssignedVariables(currentScope, preAssertInitStateIndex);
117 		}
118 		codeStream.recordPositionsFrom(pc, this.sourceStart);
119 	}
120 
resolve(BlockScope scope)121 	public void resolve(BlockScope scope) {
122 
123 		assertExpression.resolveTypeExpecting(scope, BooleanBinding);
124 		if (exceptionArgument != null) {
125 			TypeBinding exceptionArgumentType = exceptionArgument.resolveType(scope);
126 			if (exceptionArgumentType != null){
127 			    int id = exceptionArgumentType.id;
128 			    switch(id) {
129 					case T_void :
130 						scope.problemReporter().illegalVoidExpression(exceptionArgument);
131 					default:
132 					    id = T_Object;
133 					case T_boolean :
134 					case T_byte :
135 					case T_char :
136 					case T_short :
137 					case T_double :
138 					case T_float :
139 					case T_int :
140 					case T_long :
141 					case T_String :
142 						exceptionArgument.implicitConversion = (id << 4) + id;
143 				}
144 			}
145 		}
146 	}
147 
traverse(ASTVisitor visitor, BlockScope scope)148 	public void traverse(ASTVisitor visitor, BlockScope scope) {
149 
150 		if (visitor.visit(this, scope)) {
151 			assertExpression.traverse(visitor, scope);
152 			if (exceptionArgument != null) {
153 				exceptionArgument.traverse(visitor, scope);
154 			}
155 		}
156 		visitor.endVisit(this, scope);
157 	}
158 
manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo)159 	public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
160 
161 		if (!flowInfo.isReachable()) return;
162 
163 		// need assertion flag: $assertionsDisabled on outer most source clas
164 		// (in case of static member of interface, will use the outermost static member - bug 22334)
165 		SourceTypeBinding outerMostClass = currentScope.enclosingSourceType();
166 		while (outerMostClass.isLocalType()){
167 			ReferenceBinding enclosing = outerMostClass.enclosingType();
168 			if (enclosing == null || enclosing.isInterface()) break;
169 			outerMostClass = (SourceTypeBinding) enclosing;
170 		}
171 
172 		this.assertionSyntheticFieldBinding = outerMostClass.addSyntheticField(this, currentScope);
173 
174 		// find <clinit> and enable assertion support
175 		TypeDeclaration typeDeclaration = outerMostClass.scope.referenceType();
176 		AbstractMethodDeclaration[] methods = typeDeclaration.methods;
177 		for (int i = 0, max = methods.length; i < max; i++) {
178 			AbstractMethodDeclaration method = methods[i];
179 			if (method.isClinit()) {
180 				((Clinit) method).setAssertionSupport(assertionSyntheticFieldBinding, currentScope.environment().options.sourceLevel < ClassFileConstants.JDK1_5);
181 				break;
182 			}
183 		}
184 	}
185 
printStatement(int tab, StringBuffer output)186 	public StringBuffer printStatement(int tab, StringBuffer output) {
187 
188 		printIndent(tab, output);
189 		output.append("assert "); //$NON-NLS-1$
190 		this.assertExpression.printExpression(0, output);
191 		if (this.exceptionArgument != null) {
192 			output.append(": "); //$NON-NLS-1$
193 			this.exceptionArgument.printExpression(0, output);
194 		}
195 		return output.append(';');
196 	}
197 
198 }
199