1 /*******************************************************************************
2  * Copyright (c) 2000, 2011, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Carmi Grushko - Bug 465048 - Binding is null for class literals in synchronized blocks
14  *******************************************************************************/
15 package org.eclipse.jdt.internal.compiler.ast;
16 
17 import org.eclipse.jdt.internal.compiler.ASTVisitor;
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.impl.Constant;
22 import org.eclipse.jdt.internal.compiler.lookup.*;
23 
24 public class SynchronizedStatement extends SubRoutineStatement {
25 
26 	public Expression expression;
27 	public Block block;
28 	public BlockScope scope;
29 	public LocalVariableBinding synchroVariable;
30 	static final char[] SecretLocalDeclarationName = " syncValue".toCharArray(); //$NON-NLS-1$
31 
32 	// for local variables table attributes
33 	int preSynchronizedInitStateIndex = -1;
34 	int mergedSynchronizedInitStateIndex = -1;
35 
SynchronizedStatement( Expression expression, Block statement, int s, int e)36 public SynchronizedStatement(
37 	Expression expression,
38 	Block statement,
39 	int s,
40 	int e) {
41 
42 	this.expression = expression;
43 	this.block = statement;
44 	this.sourceEnd = e;
45 	this.sourceStart = s;
46 }
47 
48 @Override
analyseCode( BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo)49 public FlowInfo analyseCode(
50 	BlockScope currentScope,
51 	FlowContext flowContext,
52 	FlowInfo flowInfo) {
53 
54 	this.preSynchronizedInitStateIndex =
55 		currentScope.methodScope().recordInitializationStates(flowInfo);
56     // TODO (philippe) shouldn't it be protected by a check whether reachable statement ?
57 
58 	// mark the synthetic variable as being used
59 	this.synchroVariable.useFlag = LocalVariableBinding.USED;
60 
61 	// simple propagation to subnodes
62 	FlowInfo expressionFlowInfo = this.expression.analyseCode(this.scope, flowContext, flowInfo);
63 
64 	this.expression.checkNPE(currentScope, flowContext, expressionFlowInfo, 1);
65 
66 	flowInfo =
67 		this.block.analyseCode(
68 			this.scope,
69 			new InsideSubRoutineFlowContext(flowContext, this),
70 			expressionFlowInfo);
71 
72 	this.mergedSynchronizedInitStateIndex =
73 		currentScope.methodScope().recordInitializationStates(flowInfo);
74 
75 	// optimizing code gen
76 	if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) {
77 		this.bits |= ASTNode.BlockExit;
78 	}
79 
80 	return flowInfo;
81 }
82 
83 @Override
isSubRoutineEscaping()84 public boolean isSubRoutineEscaping() {
85 	return false;
86 }
87 
88 /**
89  * Synchronized statement code generation
90  *
91  * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope
92  * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream
93  */
94 @Override
generateCode(BlockScope currentScope, CodeStream codeStream)95 public void generateCode(BlockScope currentScope, CodeStream codeStream) {
96 	if ((this.bits & IsReachable) == 0) {
97 		return;
98 	}
99 	// in case the labels needs to be reinitialized
100 	// when the code generation is restarted in wide mode
101 	this.anyExceptionLabel = null;
102 
103 	int pc = codeStream.position;
104 
105 	// generate the synchronization expression
106 	this.expression.generateCode(this.scope, codeStream, true);
107 	if (this.block.isEmptyBlock()) {
108 		switch(this.synchroVariable.type.id) {
109 			case TypeIds.T_long :
110 			case TypeIds.T_double :
111 				codeStream.dup2();
112 				break;
113 			default :
114 				codeStream.dup();
115 				break;
116 		}
117 		// only take the lock
118 		codeStream.monitorenter();
119 		codeStream.monitorexit();
120 		if (this.scope != currentScope) {
121 			codeStream.exitUserScope(this.scope);
122 		}
123 	} else {
124 		// enter the monitor
125 		codeStream.store(this.synchroVariable, true);
126 		codeStream.addVariable(this.synchroVariable);
127 		codeStream.monitorenter();
128 
129 		// generate  the body of the synchronized block
130 		enterAnyExceptionHandler(codeStream);
131 		this.block.generateCode(this.scope, codeStream);
132 		if (this.scope != currentScope) {
133 			// close all locals defined in the synchronized block except the secret local
134 			codeStream.exitUserScope(this.scope, this.synchroVariable);
135 		}
136 
137 		BranchLabel endLabel = new BranchLabel(codeStream);
138 		if ((this.bits & ASTNode.BlockExit) == 0) {
139 			codeStream.load(this.synchroVariable);
140 			codeStream.monitorexit();
141 			exitAnyExceptionHandler();
142 			codeStream.goto_(endLabel);
143 			enterAnyExceptionHandler(codeStream);
144 		}
145 		// generate the body of the exception handler
146 		codeStream.pushExceptionOnStack(this.scope.getJavaLangThrowable());
147 		if (this.preSynchronizedInitStateIndex != -1) {
148 			codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preSynchronizedInitStateIndex);
149 		}
150 		placeAllAnyExceptionHandler();
151 		codeStream.load(this.synchroVariable);
152 		codeStream.monitorexit();
153 		exitAnyExceptionHandler();
154 		codeStream.athrow();
155 		// May loose some local variable initializations : affecting the local variable attributes
156 		if (this.mergedSynchronizedInitStateIndex != -1) {
157 			codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedSynchronizedInitStateIndex);
158 			codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedSynchronizedInitStateIndex);
159 		}
160 		if (this.scope != currentScope) {
161 			codeStream.removeVariable(this.synchroVariable);
162 		}
163 		if ((this.bits & ASTNode.BlockExit) == 0) {
164 			endLabel.place();
165 		}
166 	}
167 	codeStream.recordPositionsFrom(pc, this.sourceStart);
168 }
169 
170 /**
171  * @see SubRoutineStatement#generateSubRoutineInvocation(BlockScope, CodeStream, Object, int, LocalVariableBinding)
172  */
173 @Override
generateSubRoutineInvocation(BlockScope currentScope, CodeStream codeStream, Object targetLocation, int stateIndex, LocalVariableBinding secretLocal)174 public boolean generateSubRoutineInvocation(BlockScope currentScope, CodeStream codeStream, Object targetLocation, int stateIndex, LocalVariableBinding secretLocal) {
175 	codeStream.load(this.synchroVariable);
176 	codeStream.monitorexit();
177 	exitAnyExceptionHandler();
178 	return false;
179 }
180 
181 @Override
resolve(BlockScope upperScope)182 public void resolve(BlockScope upperScope) {
183 	// special scope for secret locals optimization.
184 	this.scope = new BlockScope(upperScope);
185 	TypeBinding type = this.expression.resolveType(this.scope);
186 	if (type != null) {
187 		switch (type.id) {
188 			case T_boolean :
189 			case T_char :
190 			case T_float :
191 			case T_double :
192 			case T_byte :
193 			case T_short :
194 			case T_int :
195 			case T_long :
196 				this.scope.problemReporter().invalidTypeToSynchronize(this.expression, type);
197 				break;
198 			case T_void :
199 				this.scope.problemReporter().illegalVoidExpression(this.expression);
200 				break;
201 			case T_null :
202 				this.scope.problemReporter().invalidNullToSynchronize(this.expression);
203 				break;
204 			}
205 			//continue even on errors in order to have the TC done into the statements
206 			this.synchroVariable = new LocalVariableBinding(SecretLocalDeclarationName, type, ClassFileConstants.AccDefault, false);
207 			this.scope.addLocalVariable(this.synchroVariable);
208 			this.synchroVariable.setConstant(Constant.NotAConstant); // not inlinable
209 			this.expression.computeConversion(this.scope, type, type);
210 	}
211 	this.block.resolveUsing(this.scope);
212 }
213 
214 @Override
printStatement(int indent, StringBuffer output)215 public StringBuffer printStatement(int indent, StringBuffer output) {
216 	printIndent(indent, output);
217 	output.append("synchronized ("); //$NON-NLS-1$
218 	this.expression.printExpression(0, output).append(')');
219 	output.append('\n');
220 	return this.block.printStatement(indent + 1, output);
221 }
222 
223 @Override
traverse(ASTVisitor visitor, BlockScope blockScope)224 public void traverse(ASTVisitor visitor, BlockScope blockScope) {
225 	if (visitor.visit(this, blockScope)) {
226 		this.expression.traverse(visitor, this.scope);
227 		this.block.traverse(visitor, this.scope);
228 	}
229 	visitor.endVisit(this, blockScope);
230 }
231 
232 @Override
doesNotCompleteNormally()233 public boolean doesNotCompleteNormally() {
234 	return this.block.doesNotCompleteNormally();
235 }
236 @Override
237 
completesByContinue()238 public boolean completesByContinue() {
239 	return this.block.completesByContinue();
240 }
241 
242 @Override
canCompleteNormally()243 public boolean canCompleteNormally() {
244 	return this.block.canCompleteNormally();
245 }
246 
247 @Override
continueCompletes()248 public boolean continueCompletes() {
249 	return this.block.continueCompletes();
250 }
251 }
252