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