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

org.checkerframework.checker.lock.LockStore Maven / Gradle / Ivy

Go to download

The Checker Framework enhances Java's type system to make it more powerful and useful. This lets software developers detect and prevent errors in their Java programs. The Checker Framework includes compiler plug-ins ("checkers") that find bugs or verify their absence. It also permits you to write your own compiler plug-ins.

The newest version!
package org.checkerframework.checker.lock;

import org.checkerframework.checker.lock.qual.LockHeld;
import org.checkerframework.checker.lock.qual.LockPossiblyHeld;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.visualize.CFGVisualizer;
import org.checkerframework.dataflow.expression.ArrayAccess;
import org.checkerframework.dataflow.expression.ClassName;
import org.checkerframework.dataflow.expression.FieldAccess;
import org.checkerframework.dataflow.expression.JavaExpression;
import org.checkerframework.dataflow.expression.LocalVariable;
import org.checkerframework.dataflow.expression.MethodCall;
import org.checkerframework.dataflow.expression.ThisReference;
import org.checkerframework.framework.flow.CFAbstractStore;
import org.checkerframework.framework.flow.CFValue;
import org.checkerframework.framework.type.GenericAnnotatedTypeFactory;
import org.checkerframework.framework.type.QualifierHierarchy;
import org.checkerframework.javacutil.AnnotationMirrorSet;
import org.checkerframework.javacutil.AnnotationUtils;

import java.util.ArrayList;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ExecutableElement;

/**
 * The Lock Store behaves like CFAbstractStore but requires the ability to insert exact annotations.
 * This is because we want to be able to insert @LockPossiblyHeld to replace @LockHeld, which
 * normally is not possible in CFAbstractStore since @LockHeld is more specific.
 */
public class LockStore extends CFAbstractStore {

    /**
     * If true, indicates that the store refers to a point in the code inside a constructor or
     * initializer. This is useful because constructors and initializers are special with regard to
     * the set of locks that is considered to be held. For example, 'this' is considered to be held
     * inside a constructor.
     */
    protected boolean inConstructorOrInitializer = false;

    /** The type factory to use. */
    private final LockAnnotatedTypeFactory atypeFactory;

    /**
     * Create a LockStore.
     *
     * @param analysis the analysis class this store belongs to
     * @param sequentialSemantics should the analysis use sequential Java semantics (i.e., assume
     *     that only one thread is running at all times)?
     */
    public LockStore(LockAnalysis analysis, boolean sequentialSemantics) {
        super(analysis, sequentialSemantics);
        this.atypeFactory = (LockAnnotatedTypeFactory) analysis.getTypeFactory();
    }

    /** Copy constructor. */
    public LockStore(LockAnalysis analysis, CFAbstractStore other) {
        super(other);
        this.inConstructorOrInitializer = ((LockStore) other).inConstructorOrInitializer;
        this.atypeFactory = ((LockStore) other).atypeFactory;
    }

    @Override
    public LockStore leastUpperBound(LockStore other) {
        LockStore newStore = super.leastUpperBound(other);

        // Least upper bound of a boolean
        newStore.inConstructorOrInitializer =
                this.inConstructorOrInitializer && other.inConstructorOrInitializer;

        return newStore;
    }

    /*
     * Insert an annotation exactly, without regard to whether an annotation was already present.
     * This is only done for @LockPossiblyHeld. This is not sound for other type qualifiers.
     */
    public void insertLockPossiblyHeld(JavaExpression je) {
        if (je.containsUnknown()) {
            // Expressions containing unknown expressions are not stored.
            return;
        }
        if (je instanceof LocalVariable) {
            LocalVariable localVar = (LocalVariable) je;
            CFValue current = localVariableValues.get(localVar);
            CFValue value = changeLockAnnoToTop(je, current);
            if (value != null) {
                localVariableValues.put(localVar, value);
            }
        } else if (je instanceof FieldAccess) {
            FieldAccess fieldAcc = (FieldAccess) je;
            CFValue current = fieldValues.get(fieldAcc);
            CFValue value = changeLockAnnoToTop(je, current);
            if (value != null) {
                fieldValues.put(fieldAcc, value);
            }
        } else if (je instanceof MethodCall) {
            MethodCall method = (MethodCall) je;
            CFValue current = methodValues.get(method);
            CFValue value = changeLockAnnoToTop(je, current);
            if (value != null) {
                methodValues.put(method, value);
            }
        } else if (je instanceof ArrayAccess) {
            ArrayAccess arrayAccess = (ArrayAccess) je;
            CFValue current = arrayValues.get(arrayAccess);
            CFValue value = changeLockAnnoToTop(je, current);
            if (value != null) {
                arrayValues.put(arrayAccess, value);
            }
        } else if (je instanceof ThisReference) {
            thisValue = changeLockAnnoToTop(je, thisValue);
        } else if (je instanceof ClassName) {
            ClassName className = (ClassName) je;
            CFValue current = classValues.get(className);
            CFValue value = changeLockAnnoToTop(je, current);
            if (value != null) {
                classValues.put(className, value);
            }
        } else {
            // No other types of expressions need to be stored.
        }
    }

    /**
     * Makes a new CFValue with the same annotations as currentValue except that the annotation in
     * the LockPossiblyHeld hierarchy is set to LockPossiblyHeld. If currentValue is null, then a
     * new value is created where the annotation set is LockPossiblyHeld and GuardedByUnknown
     */
    private CFValue changeLockAnnoToTop(JavaExpression je, @Nullable CFValue currentValue) {
        if (currentValue == null) {
            AnnotationMirrorSet set = new AnnotationMirrorSet();
            set.add(atypeFactory.GUARDEDBYUNKNOWN);
            set.add(atypeFactory.LOCKPOSSIBLYHELD);
            return analysis.createAbstractValue(set, je.getType());
        }

        QualifierHierarchy qualHierarchy = atypeFactory.getQualifierHierarchy();
        AnnotationMirrorSet currentSet = currentValue.getAnnotations();
        AnnotationMirror gb =
                qualHierarchy.findAnnotationInHierarchy(currentSet, atypeFactory.GUARDEDBYUNKNOWN);
        AnnotationMirrorSet newSet = new AnnotationMirrorSet();
        newSet.add(atypeFactory.LOCKPOSSIBLYHELD);
        if (gb != null) {
            newSet.add(gb);
        }
        return analysis.createAbstractValue(newSet, currentValue.getUnderlyingType());
    }

    public void setInConstructorOrInitializer() {
        inConstructorOrInitializer = true;
    }

    @Override
    public @Nullable CFValue getValue(JavaExpression expr) {

        if (inConstructorOrInitializer) {
            // 'this' is automatically considered as being held in a constructor or initializer.
            // The class name, however, is not.
            if (expr instanceof ThisReference) {
                initializeThisValue(atypeFactory.LOCKHELD, expr.getType());
            } else if (expr instanceof FieldAccess) {
                FieldAccess fieldAcc = (FieldAccess) expr;
                if (!fieldAcc.isStatic() && fieldAcc.getReceiver() instanceof ThisReference) {
                    insertValue(fieldAcc.getReceiver(), atypeFactory.LOCKHELD);
                }
            }
        }

        return super.getValue(expr);
    }

    @Override
    protected String internalVisualize(CFGVisualizer viz) {
        return viz.visualizeStoreKeyVal("inConstructorOrInitializer", inConstructorOrInitializer)
                + viz.getSeparator()
                + super.internalVisualize(viz);
    }

    @Override
    public void updateForMethodCall(
            MethodInvocationNode n,
            GenericAnnotatedTypeFactory atypeFactory,
            CFValue val) {
        super.updateForMethodCall(n, atypeFactory, val);
        ExecutableElement method = n.getTarget().getMethod();
        // The following behavior is similar to setting the sideEffectsUnrefineAliases field of
        // Lockannotatedtypefactory, but it affects only the LockPosssiblyHeld type hierarchy (not
        // the @GuardedBy hierarchy), so it cannot use that logic.
        if (!atypeFactory.isSideEffectFree(method)) {
            // After the call to super.updateForMethodCall, only final fields are left in
            // fieldValues (if the method called is side-effecting). For the LockPossiblyHeld
            // hierarchy, even a final field might be locked or unlocked by a side-effecting method.
            // So, final fields must be set to @LockPossiblyHeld, but the annotation in the
            // GuardedBy hierarchy should not be changed.
            for (FieldAccess field : new ArrayList<>(fieldValues.keySet())) {
                CFValue newValue = changeLockAnnoToTop(field, fieldValues.get(field));
                if (newValue != null) {
                    fieldValues.put(field, newValue);
                } else {
                    fieldValues.remove(field);
                }
            }

            // Local variables could also be unlocked via an alias
            for (LocalVariable var : new ArrayList<>(localVariableValues.keySet())) {
                CFValue newValue = changeLockAnnoToTop(var, localVariableValues.get(var));
                if (newValue != null) {
                    localVariableValues.put(var, newValue);
                }
            }

            if (thisValue != null) {
                thisValue = changeLockAnnoToTop(null, thisValue);
            }
        }
    }

    /**
     * Whether the specified value has the {@link LockHeld} annotation.
     *
     * @param value the value to check.
     * @return whether the {@code value} has the {@link LockHeld} annotation
     */
    boolean hasLockHeld(CFValue value) {
        return AnnotationUtils.containsSame(value.getAnnotations(), atypeFactory.LOCKHELD);
    }

    /**
     * Whether the specified value has the {@link LockPossiblyHeld} annotation.
     *
     * @param value the value to check.
     * @return whether the {@code value} has the {@link LockPossiblyHeld} annotation
     */
    boolean hasLockPossiblyHeld(CFValue value) {
        return AnnotationUtils.containsSame(value.getAnnotations(), atypeFactory.LOCKPOSSIBLYHELD);
    }

    @Override
    public void insertValue(
            JavaExpression je, @Nullable CFValue value, boolean permitNondeterministic) {
        if (!shouldInsert(je, value, permitNondeterministic)) {
            return;
        }

        // Even with concurrent semantics enabled, a @LockHeld value must always be
        // stored for fields and @Pure method calls. This is sound because:
        //  * Another thread can never release the lock on the current thread, and
        //  * Locks are assumed to be effectively final, hence another thread will not
        //    side effect the lock expression that has value @LockHeld.
        if (hasLockHeld(value)) {
            if (je instanceof FieldAccess) {
                FieldAccess fieldAcc = (FieldAccess) je;
                CFValue oldValue = fieldValues.get(fieldAcc);
                CFValue newValue = value.mostSpecific(oldValue, null);
                if (newValue != null) {
                    fieldValues.put(fieldAcc, newValue);
                }
            } else if (je instanceof MethodCall) {
                MethodCall method = (MethodCall) je;
                CFValue oldValue = methodValues.get(method);
                CFValue newValue = value.mostSpecific(oldValue, null);
                if (newValue != null) {
                    methodValues.put(method, newValue);
                }
            }
        }

        super.insertValue(je, value, permitNondeterministic);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy