org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.compiler.ast.SynchronizedStatement.java

Source

/*******************************************************************************
 * 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
     *
     * @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 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();
    }
}