Copyright (c) 2000, 2011, 2015 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation Carmi Grushko - Bug 465048 - Binding is null for class literals in synchronized blocks
/******************************************************************************* * Copyright (c) 2000, 2011, 2015 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Carmi Grushko - Bug 465048 - Binding is null for class literals in synchronized blocks *******************************************************************************/
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(); //$NON-NLS-1$ // for local variables table attributes 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); // TODO (philippe) shouldn't it be protected by a check whether reachable statement ? // mark the synthetic variable as being used this.synchroVariable.useFlag = LocalVariableBinding.USED; // simple propagation to subnodes 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); // optimizing code gen if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) { this.bits |= ASTNode.BlockExit; } return flowInfo; } @Override public boolean isSubRoutineEscaping() { return false; }
Synchronized statement code generation
Params:
  • currentScope – org.eclipse.jdt.internal.compiler.lookup.BlockScope
  • codeStream – org.eclipse.jdt.internal.compiler.codegen.CodeStream
/** * Synchronized statement code generation * * @param currentScope org.eclipse.jdt.internal.compiler.lookup.BlockScope * @param codeStream org.eclipse.jdt.internal.compiler.codegen.CodeStream */
@Override public void generateCode(BlockScope currentScope, CodeStream codeStream) { if ((this.bits & IsReachable) == 0) { return; } // in case the labels needs to be reinitialized // when the code generation is restarted in wide mode this.anyExceptionLabel = null; int pc = codeStream.position; // generate the synchronization expression 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; } // only take the lock codeStream.monitorenter(); codeStream.monitorexit(); if (this.scope != currentScope) { codeStream.exitUserScope(this.scope); } } else { // enter the monitor codeStream.store(this.synchroVariable, true); codeStream.addVariable(this.synchroVariable); codeStream.monitorenter(); // generate the body of the synchronized block enterAnyExceptionHandler(codeStream); this.block.generateCode(this.scope, codeStream); if (this.scope != currentScope) { // close all locals defined in the synchronized block except the secret local 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); } // generate the body of the exception handler codeStream.pushExceptionOnStack(this.scope.getJavaLangThrowable()); if (this.preSynchronizedInitStateIndex != -1) { codeStream.removeNotDefinitelyAssignedVariables(currentScope, this.preSynchronizedInitStateIndex); } placeAllAnyExceptionHandler(); codeStream.load(this.synchroVariable); codeStream.monitorexit(); exitAnyExceptionHandler(); codeStream.athrow(); // May loose some local variable initializations : affecting the local variable attributes 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); }
See Also:
  • generateSubRoutineInvocation.generateSubRoutineInvocation(BlockScope, CodeStream, Object, int, LocalVariableBinding)
/** * @see SubRoutineStatement#generateSubRoutineInvocation(BlockScope, CodeStream, Object, int, LocalVariableBinding) */
@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) { // special scope for secret locals optimization. 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; } //continue even on errors in order to have the TC done into the statements this.synchroVariable = new LocalVariableBinding(SecretLocalDeclarationName, type, ClassFileConstants.AccDefault, false); this.scope.addLocalVariable(this.synchroVariable); this.synchroVariable.setConstant(Constant.NotAConstant); // not inlinable 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 ("); //$NON-NLS-1$ 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(); } }