All Downloads are FREE. Search and download functionalities are using the official Maven repository.

checker.src.org.checkerframework.checker.lock.LockTransfer Maven / Gradle / Ivy

package org.checkerframework.checker.lock;

import java.util.List;

import org.checkerframework.javacutil.InternalUtils;
import org.checkerframework.javacutil.TreeUtils;

import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.type.TypeMirror;

import org.checkerframework.framework.flow.CFAbstractTransfer;
import org.checkerframework.framework.flow.CFValue;

import com.sun.source.tree.ClassTree;
import com.sun.source.tree.MethodTree;

import org.checkerframework.dataflow.analysis.FlowExpressions;
import org.checkerframework.dataflow.analysis.FlowExpressions.Receiver;
import org.checkerframework.dataflow.analysis.TransferInput;
import org.checkerframework.dataflow.analysis.TransferResult;
import org.checkerframework.dataflow.cfg.UnderlyingAST;
import org.checkerframework.dataflow.cfg.UnderlyingAST.CFGMethod;
import org.checkerframework.dataflow.cfg.UnderlyingAST.Kind;
import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.cfg.node.SynchronizedNode;

/*
 * LockTransfer handles constructors, initializers, synchronized methods, and synchronized blocks.
 */
public class LockTransfer extends
    CFAbstractTransfer {

    /** Type-specific version of super.analysis. */
    protected LockAnalysis analysis;
    protected LockChecker checker;
    private LockAnnotatedTypeFactory atypeFactory;

    public LockTransfer(LockAnalysis analysis, LockChecker checker) {
        // Always run the Lock Checker with -AconcurrentSemantics turned on.
        super(analysis, true /* useConcurrentSemantics */);
        // This assignment is needed (even though the super constructor is called) because
        // LockTransfer.analysis shadows CFAbstractTransfer.analysis,
        this.analysis = analysis;
        this.checker = checker;
        this.atypeFactory = (LockAnnotatedTypeFactory) analysis.getTypeFactory();
    }

    /**
     * Sets a given {@link Node} to @LockHeld in the given {@code store}.
     */
    protected void makeLockHeld(LockStore store, Node node) {
        Receiver internalRepr = FlowExpressions.internalReprOf(
                atypeFactory, node);
        store.insertValue(internalRepr, atypeFactory.LOCKHELD);
    }

    /**
     * Sets a given {@link Node} to @LockPossiblyHeld in the given {@code store}.
     */
    protected void makeLockPossiblyHeld(LockStore store, Node node) {
        Receiver internalRepr = FlowExpressions.internalReprOf(
                atypeFactory, node);

        // insertValue cannot change an annotation to a less
        // specific type (e.g. LockHeld to LockPossiblyHeld),
        // so insertLockPossiblyHeld is called.
        store.insertLockPossiblyHeld(internalRepr);
    }

    /**
     * Sets a given {@link Node} {@code node} to LockHeld in the given
     * {@link TransferResult}.
     */
    protected void makeLockHeld(
            TransferResult result, Node node) {
        if (result.containsTwoStores()) {
            makeLockHeld(result.getThenStore(), node);
            makeLockHeld(result.getElseStore(), node);
        } else {
            makeLockHeld(result.getRegularStore(), node);
        }
    }

    /**
     * Sets a given {@link Node} {@code node} to LockPossiblyHeld in the given
     * {@link TransferResult}.
     */
    protected void makeLockPossiblyHeld(
            TransferResult result, Node node) {
        if (result.containsTwoStores()) {
            makeLockPossiblyHeld(result.getThenStore(), node);
            makeLockPossiblyHeld(result.getElseStore(), node);
        } else {
            makeLockPossiblyHeld(result.getRegularStore(), node);
        }
    }

    @Override
    public LockStore initialStore(UnderlyingAST underlyingAST,
            /*@Nullable */ List parameters) {

        LockStore store = super.initialStore(underlyingAST, parameters);

        Kind astKind = underlyingAST.getKind();

        // Methods with the 'synchronized' modifier are
        // holding the 'this' lock.

        // There is a subtle difference between synchronized methods
        // and constructors/initializers. A synchronized method is only
        // taking the intrinsic lock of the current object. It says nothing
        // about any fields of the current object.

        // Furthermore, since the current object already exists,
        // other objects may be guarded by the current object. So
        // a synchronized method can affect the locking behavior of other
        // objects.

        // A constructor/initializer behaves as if the current object
        // and all its non-static fields were held as locks. But in
        // reality no locks are held.

        // Furthermore, since the current object is being constructed,
        // no other object can be guarded by it or any of its non-static
        // fields.

        // Handle synchronized methods and constructors.
        if (astKind == Kind.METHOD) {
            CFGMethod method = (CFGMethod) underlyingAST;
            MethodTree methodTree = method.getMethod();

            ExecutableElement methodElement = TreeUtils.elementFromDeclaration(methodTree);

            if (methodElement.getModifiers().contains(Modifier.SYNCHRONIZED)) {
                final ClassTree classTree = method.getClassTree();
                TypeMirror classType = InternalUtils.typeOf(classTree);

                if (methodElement.getModifiers().contains(Modifier.STATIC)) {
                    store.insertValue(new FlowExpressions.ClassName(classType), atypeFactory.LOCKHELD);
                } else {
                    store.insertThisValue(atypeFactory.LOCKHELD, classType);
                }
            } else if (methodElement.getKind() == ElementKind.CONSTRUCTOR) {
                store.setInConstructorOrInitializer();
            }
        } else if (astKind == Kind.ARBITRARY_CODE) { // Handle initializers
            store.setInConstructorOrInitializer();
        }

        return store;
    }

    @Override
    public TransferResult visitSynchronized(SynchronizedNode n,
            TransferInput p) {

        TransferResult result = super.visitSynchronized(n,
                p);

        // Handle the entering and leaving of the synchronized block
        if (n.getIsStartOfBlock()) {
            makeLockHeld(result, n.getExpression());
        } else {
            makeLockPossiblyHeld(result, n.getExpression());
        }

        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy