package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
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.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.*;
public class SynchronizedStatement extends SubRoutineStatement {
public Expression expression;
public Block block;
public BlockScope scope;
public LocalVariableBinding synchroVariable;
static final char[] SecretLocalDeclarationName = " syncValue".toCharArray();
int preSynchronizedInitStateIndex = -1;
int mergedSynchronizedInitStateIndex = -1;
public SynchronizedStatement(
Expression expression,
Block statement,
int s,
int e) {
this.expression = expression;
this.block = statement;
this.sourceEnd = e;
this.sourceStart = s;
}
@Override
public FlowInfo analyseCode(
BlockScope currentScope,
FlowContext flowContext,
FlowInfo flowInfo) {
this.preSynchronizedInitStateIndex =
currentScope.methodScope().recordInitializationStates(flowInfo);
this.synchroVariable.useFlag = LocalVariableBinding.USED;
FlowInfo expressionFlowInfo = this.expression.analyseCode(this.scope, flowContext, flowInfo);
this.expression.checkNPE(currentScope, flowContext, expressionFlowInfo, 1);
flowInfo =
this.block.analyseCode(
this.scope,
new InsideSubRoutineFlowContext(flowContext, this),
expressionFlowInfo);
this.mergedSynchronizedInitStateIndex =
currentScope.methodScope().recordInitializationStates(flowInfo);
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) {
this.bits |= ASTNode.BlockExit;
}
return flowInfo;
}
@Override
public boolean isSubRoutineEscaping() {
return false;
}
@Override
public void generateCode(BlockScope currentScope, CodeStream codeStream) {
if ((this.bits & IsReachable) == 0) {
return;
}
this.anyExceptionLabel = null;
int pc = codeStream.position;
this.expression.generateCode(this.scope, codeStream, true);
if (this.block.isEmptyBlock()) {
switch(this.synchroVariable.type.id) {
case TypeIds.T_long :
case TypeIds.T_double :
codeStream.dup2();
break;
default :
codeStream.dup();
break;
}
codeStream.monitorenter();
codeStream.monitorexit();
if (this.scope != currentScope) {
codeStream.exitUserScope(this.scope);
}
} else {
codeStream.store(this.synchroVariable, true);
codeStream.addVariable(this.synchroVariable);
codeStream.monitorenter();
enterAnyExceptionHandler(codeStream);
this.block.generateCode(this.scope, codeStream);
if (this.scope != currentScope) {
codeStream.exitUserScope(this.scope, this.synchroVariable);
}
BranchLabel endLabel = new BranchLabel(codeStream);
if ((this.bits & ASTNode.BlockExit) == 0) {
codeStream.load(this.synchroVariable);
codeStream.monitorexit();
exitAnyExceptionHandler();
codeStream.goto_(endLabel);
enterAnyExceptionHandler(codeStream);
}
codeStream.pushExceptionOnStack(this.scope.getJavaLangThrowable());
if (this.preSynchronizedInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preSynchronizedInitStateIndex);
}
placeAllAnyExceptionHandler();
codeStream.load(this.synchroVariable);
codeStream.monitorexit();
exitAnyExceptionHandler();
codeStream.athrow();
if (this.mergedSynchronizedInitStateIndex != -1) {
codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.mergedSynchronizedInitStateIndex);
codeStream.addDefinitelyAssignedVariables(currentScope, this.mergedSynchronizedInitStateIndex);
}
if (this.scope != currentScope) {
codeStream.removeVariable(this.synchroVariable);
}
if ((this.bits & ASTNode.BlockExit) == 0) {
endLabel.place();
}
}
codeStream.recordPositionsFrom(pc, this.sourceStart);
}
@Override
public boolean generateSubRoutineInvocation(BlockScope currentScope, CodeStream codeStream, Object targetLocation, int stateIndex, LocalVariableBinding secretLocal) {
codeStream.load(this.synchroVariable);
codeStream.monitorexit();
exitAnyExceptionHandler();
return false;
}
@Override
public void resolve(BlockScope upperScope) {
this.scope = new BlockScope(upperScope);
TypeBinding type = this.expression.resolveType(this.scope);
if (type != null) {
switch (type.id) {
case T_boolean :
case T_char :
case T_float :
case T_double :
case T_byte :
case T_short :
case T_int :
case T_long :
this.scope.problemReporter().invalidTypeToSynchronize(this.expression, type);
break;
case T_void :
this.scope.problemReporter().illegalVoidExpression(this.expression);
break;
case T_null :
this.scope.problemReporter().invalidNullToSynchronize(this.expression);
break;
}
this.synchroVariable = new LocalVariableBinding(SecretLocalDeclarationName, type, ClassFileConstants.AccDefault, false);
this.scope.addLocalVariable(this.synchroVariable);
this.synchroVariable.setConstant(Constant.NotAConstant);
this.expression.computeConversion(this.scope, type, type);
}
this.block.resolveUsing(this.scope);
}
@Override
public StringBuffer printStatement(int indent, StringBuffer output) {
printIndent(indent, output);
output.append("synchronized (");
this.expression.printExpression(0, output).append(')');
output.append('\n');
return this.block.printStatement(indent + 1, output);
}
@Override
public void traverse(ASTVisitor visitor, BlockScope blockScope) {
if (visitor.visit(this, blockScope)) {
this.expression.traverse(visitor, this.scope);
this.block.traverse(visitor, this.scope);
}
visitor.endVisit(this, blockScope);
}
@Override
public boolean doesNotCompleteNormally() {
return this.block.doesNotCompleteNormally();
}
@Override
public boolean completesByContinue() {
return this.block.completesByContinue();
}
}