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

framework.src.org.checkerframework.framework.flow.CFAbstractStore 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.

There is a newer version: 3.42.0
Show newest version
package org.checkerframework.framework.flow;

/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/

import org.checkerframework.common.basetype.BaseTypeVisitor;
import org.checkerframework.dataflow.analysis.FlowExpressions;
import org.checkerframework.dataflow.analysis.FlowExpressions.ArrayAccess;
import org.checkerframework.dataflow.analysis.FlowExpressions.ClassName;
import org.checkerframework.dataflow.analysis.FlowExpressions.FieldAccess;
import org.checkerframework.dataflow.analysis.FlowExpressions.LocalVariable;
import org.checkerframework.dataflow.analysis.FlowExpressions.MethodCall;
import org.checkerframework.dataflow.analysis.FlowExpressions.Receiver;
import org.checkerframework.dataflow.analysis.Store;
import org.checkerframework.dataflow.cfg.CFGVisualizer;
import org.checkerframework.dataflow.cfg.node.ArrayAccessNode;
import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
import org.checkerframework.dataflow.cfg.node.LocalVariableNode;
import org.checkerframework.dataflow.cfg.node.MethodInvocationNode;
import org.checkerframework.dataflow.cfg.node.Node;
import org.checkerframework.dataflow.cfg.node.ThisLiteralNode;
import org.checkerframework.dataflow.qual.SideEffectFree;
import org.checkerframework.dataflow.util.PurityUtils;
import org.checkerframework.framework.qual.MonotonicQualifier;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.javacutil.AnnotationUtils;
import org.checkerframework.javacutil.Pair;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Name;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;

/**
 * A store for the checker framework analysis tracks the annotations of memory
 * locations such as local variables and fields.
 *
 * When adding a new field to track values for a code construct (similar to
 * {@code localVariableValues} and {@code thisValue}), it is important
 * to review all constructors and methods in this class for locations
 * where the new field must be handled (such as the copy constructor and
 * {@code clearValue}), as well as all constructors/methods in subclasses
 * of {code CFAbstractStore}. Note that this includes not only overridden
 * methods in the subclasses, but new methods in the subclasses as well.
 * Also check if BaseTypeVisitor#getFlowExpressionContextFromNode(Node)
 * needs to be updated. Failing to do so may result in silent failures that are
 * time consuming to debug.
 *
 * @author Charlie Garrett
 * @author Stefan Heule
 */
// TODO: this class should be split into parts that are reusable generally, and
// parts specific to the checker framework
public abstract class CFAbstractStore, S extends CFAbstractStore>
        implements Store {

    /**
     * The analysis class this store belongs to.
     */
    protected final CFAbstractAnalysis analysis;

    /**
     * Information collected about local variables (including method arguments).
     */
    protected final Map localVariableValues;

    /**
     * Information collected about the current object.
     */
    protected V thisValue;

    /**
     * Information collected about fields, using the internal representation
     * {@link FieldAccess}.
     */
    protected Map fieldValues;

    /**
     * Information collected about arrays, using the internal representation
     * {@link ArrayAccess}.
     */
    protected Map arrayValues;

    /**
     * Information collected about method calls, using the internal
     * representation {@link MethodCall}.
     */
    protected Map methodValues;

    /**
     * Information collected about <class name>.class values,
     * using the internal representation {@link ClassName}.
     */
    protected Map classValues;

    /**
     * Should the analysis use sequential Java semantics (i.e., assume that only
     * one thread is running at all times)?
     */
    protected final boolean sequentialSemantics;

    /* --------------------------------------------------------- */
    /* Initialization */
    /* --------------------------------------------------------- */

    public CFAbstractStore(CFAbstractAnalysis analysis,
            boolean sequentialSemantics) {
        this.analysis = analysis;
        localVariableValues = new HashMap<>();
        thisValue = null;
        fieldValues = new HashMap<>();
        methodValues = new HashMap<>();
        arrayValues = new HashMap<>();
        classValues = new HashMap<>();
        this.sequentialSemantics = sequentialSemantics;
    }

    /** Copy constructor. */
    protected CFAbstractStore(CFAbstractStore other) {
        this.analysis = other.analysis;
        localVariableValues = new HashMap<>(other.localVariableValues);
        thisValue = other.thisValue;
        fieldValues = new HashMap<>(other.fieldValues);
        methodValues = new HashMap<>(other.methodValues);
        arrayValues = new HashMap<>(other.arrayValues);
        classValues = new HashMap<>(other.classValues);
        sequentialSemantics = other.sequentialSemantics;
    }

    /**
     * Set the abstract value of a method parameter (only adds the information
     * to the store, does not remove any other knowledge). Any previous
     * information is erased; this method should only be used to initialize the
     * abstract value.
     */
    public void initializeMethodParameter(LocalVariableNode p, /*@Nullable*/ V value) {
        if (value != null) {
            localVariableValues.put(new FlowExpressions.LocalVariable(p.getElement()), value);
        }
    }

    /**
     * Set the value of the current object. Any previous information is erased;
     * this method should only be used to initialize the value.
     */
    public void initializeThisValue(AnnotationMirror a, TypeMirror underlyingType) {
        if (a != null) {
            thisValue = analysis.createSingleAnnotationValue(a, underlyingType);
        }
    }

    /*
     * Indicates whether the given method is side-effect-free as far as the
     * current store is concerned.
     * In some cases, a store for a checker allows for other mechanisms to specify
     * whether a method is side-effect-free. For example, unannotated methods may
     * be considered side-effect-free by default.
     *
     * @param atypeFactory     the type factory used to retrieve annotations on the method element
     * @param method           the method element
     *
     * @return whether the method is side-effect-free
     */
    protected boolean isSideEffectFree(AnnotatedTypeFactory atypeFactory, ExecutableElement method) {
        return PurityUtils.isSideEffectFree(atypeFactory, method);
    }

    /* --------------------------------------------------------- */
    /* Handling of fields */
    /* --------------------------------------------------------- */

    /**
     * Remove any information that might not be valid any more after a method
     * call, and add information guaranteed by the method.
     *
     * 
    *
  1. If the method is side-effect-free (as indicated by * {@link org.checkerframework.dataflow.qual.SideEffectFree} or {@link org.checkerframework.dataflow.qual.Pure}), * then no information needs to be removed. *
  2. Otherwise, all information about field accesses {@code a.f} needs to * be removed, except if the method {@code n} cannot modify {@code a.f} * (e.g., if {@code a} is a local variable or {@code this}, and {@code f} is * final). *
  3. Furthermore, if the field has a monotonic annotation, then its * information can also be kept. *
* * Furthermore, if the method is deterministic, we store its result * {@code val} in the store. */ public void updateForMethodCall(MethodInvocationNode n, AnnotatedTypeFactory atypeFactory, V val) { ExecutableElement method = n.getTarget().getMethod(); // case 1: remove information if necessary if (!(analysis.checker.hasOption("assumeSideEffectFree") || isSideEffectFree(atypeFactory, method))) { // update field values Map newFieldValues = new HashMap<>(); for (Entry e : fieldValues.entrySet()) { FlowExpressions.FieldAccess fieldAccess = e.getKey(); V otherVal = e.getValue(); // case 3: List> fieldAnnotations = atypeFactory .getAnnotationWithMetaAnnotation( fieldAccess.getField(), MonotonicQualifier.class); V newOtherVal = null; for (Pair fieldAnnotation : fieldAnnotations) { AnnotationMirror monotonicAnnotation = fieldAnnotation.second; Name annotation = AnnotationUtils.getElementValueClassName( monotonicAnnotation, "value", false); AnnotationMirror target = AnnotationUtils.fromName( atypeFactory.getElementUtils(), annotation); AnnotationMirror anno = otherVal.getType() .getAnnotationInHierarchy(target); // Make sure the 'target' annotation is present. if (anno != null && AnnotationUtils.areSame(anno, target)) { newOtherVal = analysis.createSingleAnnotationValue( target, otherVal.getType().getUnderlyingType()) .mostSpecific(newOtherVal, null); } } if (newOtherVal != null) { // keep information for all hierarchies where we had a // monotone annotation. newFieldValues.put(fieldAccess, newOtherVal); continue; } // case 2: if (!fieldAccess.isUnmodifiableByOtherCode()) { continue; // remove information completely } // keep information newFieldValues.put(fieldAccess, otherVal); } fieldValues = newFieldValues; // update method values methodValues.clear(); arrayValues.clear(); } // store information about method call if possible Receiver methodCall = FlowExpressions.internalReprOf( analysis.getTypeFactory(), n); replaceValue(methodCall, val); } /** * Add the annotation {@code a} for the expression {@code r} (correctly * deciding where to store the information depending on the type of the * expression {@code r}). * *

* This method does not take care of removing other information that might * be influenced by changes to certain parts of the state. * *

* If there is already a value {@code v} present for {@code r}, then the * stronger of the new and old value are taken (according to the lattice). * Note that this happens per hierarchy, and if the store already contains * information about a hierarchy other than {@code a}s hierarchy, that * information is preserved. */ public void insertValue(FlowExpressions.Receiver r, AnnotationMirror a) { insertValue(r, analysis.createSingleAnnotationValue(a, r.getType())); } /** * Returns true if the receiver {@code r} can be stored in this store. */ public static boolean canInsertReceiver(Receiver r) { if (r instanceof FlowExpressions.FieldAccess || r instanceof FlowExpressions.ThisReference || r instanceof FlowExpressions.LocalVariable || r instanceof FlowExpressions.MethodCall || r instanceof FlowExpressions.ArrayAccess) { return !r.containsUnknown(); } return false; } /** * Add the abstract value {@code value} for the expression {@code r} * (correctly deciding where to store the information depending on the type * of the expression {@code r}). * *

* This method does not take care of removing other information that might * be influenced by changes to certain parts of the state. * *

* If there is already a value {@code v} present for {@code r}, then the * stronger of the new and old value are taken (according to the lattice). * Note that this happens per hierarchy, and if the store already contains * information about a hierarchy for which {@code value} does not contain * information, then that information is preserved. */ public void insertValue(FlowExpressions.Receiver r, /*@Nullable*/ V value) { if (value == null) { // No need to insert a null abstract value because it represents // top and top is also the default value. return; } if (r.containsUnknown()) { // Expressions containing unknown expressions are not stored. return; } if (r instanceof FlowExpressions.LocalVariable) { FlowExpressions.LocalVariable localVar = (FlowExpressions.LocalVariable) r; V oldValue = localVariableValues.get(localVar); V newValue = value.mostSpecific(oldValue, null); if (newValue != null) { localVariableValues.put(localVar, newValue); } } else if (r instanceof FlowExpressions.FieldAccess) { FlowExpressions.FieldAccess fieldAcc = (FlowExpressions.FieldAccess) r; // Only store information about final fields (where the receiver is // also fixed) if concurrent semantics are enabled. boolean isMonotonic = isMonotonicUpdate(fieldAcc, value); if (sequentialSemantics || isMonotonic || fieldAcc.isUnmodifiableByOtherCode()) { V oldValue = fieldValues.get(fieldAcc); V newValue = value.mostSpecific(oldValue, null); if (newValue != null) { fieldValues.put(fieldAcc, newValue); } } } else if (r instanceof FlowExpressions.MethodCall) { FlowExpressions.MethodCall method = (FlowExpressions.MethodCall) r; // Don't store any information if concurrent semantics are enabled. if (sequentialSemantics) { V oldValue = methodValues.get(method); V newValue = value.mostSpecific(oldValue, null); if (newValue != null) { methodValues.put(method, newValue); } } } else if (r instanceof FlowExpressions.ArrayAccess) { FlowExpressions.ArrayAccess arrayAccess = (ArrayAccess) r; if (sequentialSemantics) { V oldValue = arrayValues.get(arrayAccess); V newValue = value.mostSpecific(oldValue, null); if (newValue != null) { arrayValues.put(arrayAccess, newValue); } } } else if (r instanceof FlowExpressions.ThisReference) { FlowExpressions.ThisReference thisRef = (FlowExpressions.ThisReference) r; if (sequentialSemantics || thisRef.isUnmodifiableByOtherCode()) { V oldValue = thisValue; V newValue = value.mostSpecific(oldValue, null); if (newValue != null) { thisValue = newValue; } } } else if (r instanceof FlowExpressions.ClassName) { FlowExpressions.ClassName className = (FlowExpressions.ClassName) r; if (sequentialSemantics || className.isUnmodifiableByOtherCode()) { V oldValue = classValues.get(className); V newValue = value.mostSpecific(oldValue, null); if (newValue != null) { classValues.put(className, newValue); } } } else { // No other types of expressions need to be stored. } } /** * @return true if fieldAcc is an update of a monotonic qualifier to its target qualifier. * (e.g. @MonotonicNonNull to @NonNull). Always returns false if sequentialSemantics is true. */ protected boolean isMonotonicUpdate(FieldAccess fieldAcc, V value) { boolean isMonotonic = false; // TODO: this check for !sequentialSemantics is an optimization that breaks the contract of the method, // since the method name and documentation say nothing about sequential semantics. // This check should be performed by callers of this method when needed. // TODO: Update the javadoc of this method when the above to-do item is addressed. if (!sequentialSemantics) { // only compute if necessary AnnotatedTypeFactory atypeFactory = this.analysis.atypeFactory; List> fieldAnnotations = atypeFactory.getAnnotationWithMetaAnnotation( fieldAcc.getField(), MonotonicQualifier.class); for (Pair fieldAnnotation : fieldAnnotations) { AnnotationMirror monotonicAnnotation = fieldAnnotation.second; Name annotation = AnnotationUtils.getElementValueClassName( monotonicAnnotation, "value", false); AnnotationMirror target = AnnotationUtils.fromName( atypeFactory.getElementUtils(), annotation); AnnotationMirror valueAM = value.getType().getAnnotationInHierarchy(target); // Make sure the 'target' annotation is present. if (valueAM != null && AnnotationUtils.areSame(valueAM, target)) { isMonotonic = true; break; } } } return isMonotonic; } public void insertThisValue(AnnotationMirror a, TypeMirror underlyingType) { if (a == null) { return; } V value = analysis.createSingleAnnotationValue(a, underlyingType); V oldValue = thisValue; V newValue = value.mostSpecific(oldValue, null); if (newValue != null) { thisValue = newValue; } } /** * Completely replaces the abstract value {@code value} for the expression * {@code r} (correctly deciding where to store the information depending on * the type of the expression {@code r}). Any previous information is * discarded. * *

* This method does not take care of removing other information that might * be influenced by changes to certain parts of the state. */ public void replaceValue(FlowExpressions.Receiver r, /*@Nullable*/ V value) { clearValue(r); insertValue(r, value); } /** * Remove any knowledge about the expression {@code r} (correctly deciding * where to remove the information depending on the type of the expression * {@code r}). */ public void clearValue(FlowExpressions.Receiver r) { if (r.containsUnknown()) { // Expressions containing unknown expressions are not stored. return; } if (r instanceof FlowExpressions.LocalVariable) { FlowExpressions.LocalVariable localVar = (FlowExpressions.LocalVariable) r; localVariableValues.remove(localVar); } else if (r instanceof FlowExpressions.FieldAccess) { FlowExpressions.FieldAccess fieldAcc = (FlowExpressions.FieldAccess) r; fieldValues.remove(fieldAcc); } else if (r instanceof FlowExpressions.MethodCall) { MethodCall method = (MethodCall) r; methodValues.remove(method); } else if (r instanceof FlowExpressions.ArrayAccess) { ArrayAccess a = (ArrayAccess) r; arrayValues.remove(a); } else if (r instanceof FlowExpressions.ClassName) { FlowExpressions.ClassName c = (FlowExpressions.ClassName) r; classValues.remove(c); } else { // thisValue ... // No other types of expressions are stored. } } /** * @return current abstract value of a flow expression, or {@code null} if * no information is available. */ public /*@Nullable*/ V getValue(FlowExpressions.Receiver expr) { if (expr instanceof FlowExpressions.LocalVariable) { FlowExpressions.LocalVariable localVar = (FlowExpressions.LocalVariable) expr; return localVariableValues.get(localVar); } else if (expr instanceof FlowExpressions.ThisReference) { return thisValue; } else if (expr instanceof FlowExpressions.FieldAccess) { FlowExpressions.FieldAccess fieldAcc = (FlowExpressions.FieldAccess) expr; return fieldValues.get(fieldAcc); } else if (expr instanceof FlowExpressions.MethodCall) { FlowExpressions.MethodCall method = (FlowExpressions.MethodCall) expr; return methodValues.get(method); } else if (expr instanceof FlowExpressions.ArrayAccess) { FlowExpressions.ArrayAccess a = (FlowExpressions.ArrayAccess) expr; return arrayValues.get(a); } else if (expr instanceof FlowExpressions.ClassName) { FlowExpressions.ClassName c = (FlowExpressions.ClassName) expr; return classValues.get(c); } else { assert false; return null; } } /** * @return current abstract value of a field access, or {@code null} if no * information is available. */ public /*@Nullable*/ V getValue(FieldAccessNode n) { FlowExpressions.FieldAccess fieldAccess = FlowExpressions .internalReprOfFieldAccess(analysis.getTypeFactory(), n); return fieldValues.get(fieldAccess); } /** * @return current abstract value of a method call, or {@code null} if no * information is available. */ public /*@Nullable*/ V getValue(MethodInvocationNode n) { Receiver method = FlowExpressions.internalReprOf(analysis.getTypeFactory(), n, true); if (method == null) { return null; } return methodValues.get(method); } /** * @return current abstract value of a field access, or {@code null} if no * information is available. */ public /*@Nullable*/ V getValue(ArrayAccessNode n) { FlowExpressions.ArrayAccess arrayAccess = FlowExpressions .internalReprOfArrayAccess(analysis.getTypeFactory(), n); return arrayValues.get(arrayAccess); } /** * Update the information in the store by considering an assignment with * target {@code n}. */ public void updateForAssignment(Node n, /*@Nullable*/ V val) { Receiver receiver = FlowExpressions.internalReprOf( analysis.getTypeFactory(), n); if (receiver instanceof ArrayAccess) { updateForArrayAssignment((ArrayAccess) receiver, val); } else if (receiver instanceof FieldAccess) { updateForFieldAccessAssignment((FieldAccess) receiver, val); } else if (receiver instanceof LocalVariable) { updateForLocalVariableAssignment((LocalVariable) receiver, val); } else { assert false : "Unexpected receiver of class " + receiver.getClass(); } } /** * Update the information in the store by considering a field assignment * with target {@code n}, where the right hand side has the abstract value * {@code val}. * * @param val * The abstract value of the value assigned to {@code n} (or * {@code null} if the abstract value is not known). */ protected void updateForFieldAccessAssignment(FieldAccess fieldAccess, /*@Nullable*/ V val) { removeConflicting(fieldAccess, val); if (!fieldAccess.containsUnknown() && val != null) { // Only store information about final fields (where the receiver is // also fixed) if concurrent semantics are enabled. boolean isMonotonic = isMonotonicUpdate(fieldAccess, val); if (sequentialSemantics || isMonotonic || fieldAccess.isUnmodifiableByOtherCode()) { fieldValues.put(fieldAccess, val); } } } /** * Update the information in the store by considering an assignment with * target {@code n}, where the target is an array access. * * See {@link CFAbstractStore#removeConflicting(ArrayAccess,CFAbstractValue)}, as it is called first * by this method. */ protected void updateForArrayAssignment(ArrayAccess arrayAccess, /*@Nullable*/ V val) { removeConflicting(arrayAccess, val); if (!arrayAccess.containsUnknown() && val != null) { // Only store information about final fields (where the receiver is // also fixed) if concurrent semantics are enabled. if (sequentialSemantics) { arrayValues.put(arrayAccess, val); } } } /** * Set the abstract value of a local variable in the store. Overwrites any * value that might have been available previously. * * @param val * The abstract value of the value assigned to {@code n} (or * {@code null} if the abstract value is not known). */ protected void updateForLocalVariableAssignment(LocalVariable receiver, /*@Nullable*/ V val) { removeConflicting(receiver); if (val != null) { localVariableValues.put(receiver, val); } } /** * Remove any information in this store that might not be true any more * after {@code fieldAccess} has been assigned a new value (with the * abstract value {@code val}). This includes the following steps (assume * that {@code fieldAccess} is of the form a.f for some a. * *

    *
  1. Update the abstract value of other field accesses * b.g where the field is equal (that is, f=g), and the * receiver b might alias the receiver of {@code fieldAccess}, * a. This update will raise the abstract value for such field * accesses to at least {@code val} (or the old value, if that was less * precise). However, this is only necessary if the field g is not * final. *
  2. Remove any abstract values for field accesses b.g * where {@code fieldAccess} might alias any expression in the receiver * b. *
  3. Remove any information about method calls. *
  4. Remove any abstract values an arrary access b[i] * where {@code fieldAccess} might alias any expression in the receiver * a or index i. *
* * @param val * The abstract value of the value assigned to {@code n} (or * {@code null} if the abstract value is not known). */ protected void removeConflicting(FlowExpressions.FieldAccess fieldAccess, /*@Nullable*/ V val) { Map newFieldValues = new HashMap<>(); for (Entry e : fieldValues.entrySet()) { FlowExpressions.FieldAccess otherFieldAccess = e.getKey(); V otherVal = e.getValue(); // case 2: if (otherFieldAccess.getReceiver().containsModifiableAliasOf(this, fieldAccess)) { continue; // remove information completely } // case 1: if (fieldAccess.getField().equals(otherFieldAccess.getField())) { if (canAlias(fieldAccess.getReceiver(), otherFieldAccess.getReceiver())) { if (!otherFieldAccess.isFinal()) { if (val != null) { V newVal = val.leastUpperBound(otherVal); newFieldValues.put(otherFieldAccess, newVal); } else { // remove information completely } continue; } } } // information is save to be carried over newFieldValues.put(otherFieldAccess, otherVal); } fieldValues = newFieldValues; Map newArrayValues = new HashMap<>(); for (Entry e : arrayValues.entrySet()) { FlowExpressions.ArrayAccess otherArrayAccess = e.getKey(); V otherVal = e.getValue(); if (otherArrayAccess.containsModifiableAliasOf(this, fieldAccess)) { // remove information completely continue; } newArrayValues.put(otherArrayAccess, otherVal); } arrayValues = newArrayValues; // case 3: methodValues = new HashMap<>(); } /** * Remove any information in the store that might not be true any more after * {@code arrayAccess} has been assigned a new value (with the abstract * value {@code val}). This includes the following steps (assume that * {@code arrayAccess} is of the form a[i] for some a. * *
    *
  1. Remove any abstract value for other array access * b[j] where a and b can be aliases, or where * either b or j contains a modifiable alias of * a[i]. *
  2. Remove any abstract values for field accesses b.g * where a[i] might alias any expression in the receiver b * and there is an array expression somewhere in the receiver. *
  3. Remove any information about method calls. *
* * @param val * The abstract value of the value assigned to {@code n} (or * {@code null} if the abstract value is not known). */ protected void removeConflicting(FlowExpressions.ArrayAccess arrayAccess, /*@Nullable*/ V val) { Map newArrayValues = new HashMap<>(); for (Entry e : arrayValues.entrySet()) { FlowExpressions.ArrayAccess otherArrayAccess = e.getKey(); V otherVal = e.getValue(); // case 1: if (otherArrayAccess.containsModifiableAliasOf(this, arrayAccess)) { continue; // remove information completely } if (canAlias(arrayAccess.getReceiver(), otherArrayAccess.getReceiver())) { // TODO: one could be less strict here, and only raise the // abstract value // for all array expressions with potentially aliasing receivers continue; // remove information completely } // information is save to be carried over newArrayValues.put(otherArrayAccess, otherVal); } arrayValues = newArrayValues; // case 2: Map newFieldValues = new HashMap<>(); for (Entry e : fieldValues.entrySet()) { FlowExpressions.FieldAccess otherFieldAccess = e.getKey(); V otherVal = e.getValue(); Receiver receiver = otherFieldAccess.getReceiver(); if (receiver.containsModifiableAliasOf(this, arrayAccess) && receiver.containsOfClass(ArrayAccess.class)) { // remove information completely continue; } newFieldValues.put(otherFieldAccess, otherVal); } fieldValues = newFieldValues; // case 3: methodValues = new HashMap<>(); } /** * Remove any information in this store that might not be true any more * after {@code localVar} has been assigned a new value. This includes the * following steps: * *
    *
  1. Remove any abstract values for field accesses b.g * where {@code localVar} might alias any expression in the receiver * b. *
  2. Remove any abstract values for array accesses a[i] * where {@code localVar} might alias the receiver a. *
  3. Remove any information about method calls where the * receiver or any of the parameters contains {@code localVar}. *
*/ protected void removeConflicting(LocalVariable var) { Map newFieldValues = new HashMap<>(); for (Entry e : fieldValues.entrySet()) { FlowExpressions.FieldAccess otherFieldAccess = e.getKey(); // case 1: if (otherFieldAccess.containsSyntacticEqualReceiver(var)) { continue; } newFieldValues.put(otherFieldAccess, e.getValue()); } fieldValues = newFieldValues; Map newArrayValues = new HashMap<>(); for (Entry e : arrayValues.entrySet()) { FlowExpressions.ArrayAccess otherArrayAccess = e.getKey(); // case 2: if (otherArrayAccess.containsSyntacticEqualReceiver(var)) { continue; } newArrayValues.put(otherArrayAccess, e.getValue()); } arrayValues = newArrayValues; Map newMethodValues = new HashMap<>(); for (Entry e : methodValues .entrySet()) { FlowExpressions.MethodCall otherMethodAccess = e.getKey(); // case 3: if (otherMethodAccess.containsSyntacticEqualReceiver(var) || otherMethodAccess.containsSyntacticEqualParameter(var)) { continue; } newMethodValues.put(otherMethodAccess, e.getValue()); } methodValues = newMethodValues; } /** * Can the objects {@code a} and {@code b} be aliases? Returns a * conservative answer (i.e., returns {@code true} if not enough information * is available to determine aliasing). */ @Override public boolean canAlias(FlowExpressions.Receiver a, FlowExpressions.Receiver b) { TypeMirror tb = b.getType(); TypeMirror ta = a.getType(); Types types = analysis.getTypes(); return types.isSubtype(ta, tb) || types.isSubtype(tb, ta); } /* --------------------------------------------------------- */ /* Handling of local variables */ /* --------------------------------------------------------- */ /** * @return current abstract value of a local variable, or {@code null} if no * information is available. */ public /*@Nullable*/ V getValue(LocalVariableNode n) { Element el = n.getElement(); return localVariableValues.get(new FlowExpressions.LocalVariable(el)); } /* --------------------------------------------------------- */ /* Handling of the current object */ /* --------------------------------------------------------- */ /** * @return current abstract value of the current object, or {@code null} if no * information is available. */ public /*@Nullable*/ V getValue(ThisLiteralNode n) { return thisValue; } /* --------------------------------------------------------- */ /* Helper and miscellaneous methods */ /* --------------------------------------------------------- */ @SuppressWarnings("unchecked") @Override public S copy() { return analysis.createCopiedStore((S) this); } @Override public S leastUpperBound(S other) { S newStore = analysis.createEmptyStore(sequentialSemantics); for (Entry e : other.localVariableValues.entrySet()) { // local variables that are only part of one store, but not the // other are discarded, as one of store implicitly contains 'top' // for that variable. FlowExpressions.LocalVariable localVar = e.getKey(); if (localVariableValues.containsKey(localVar)) { V otherVal = e.getValue(); V thisVal = localVariableValues.get(localVar); V mergedVal = thisVal.leastUpperBound(otherVal); if (mergedVal != null) { newStore.localVariableValues.put(localVar, mergedVal); } } } // information about the current object { V otherVal = other.thisValue; V myVal = thisValue; V mergedVal = myVal == null ? null : myVal.leastUpperBound(otherVal); if (mergedVal != null) { newStore.thisValue = mergedVal; } } for (Entry e : other.fieldValues .entrySet()) { // information about fields that are only part of one store, but not // the other are discarded, as one store implicitly contains 'top' // for that field. FlowExpressions.FieldAccess el = e.getKey(); if (fieldValues.containsKey(el)) { V otherVal = e.getValue(); V thisVal = fieldValues.get(el); V mergedVal = thisVal.leastUpperBound(otherVal); if (mergedVal != null) { newStore.fieldValues.put(el, mergedVal); } } } for (Entry e : other.arrayValues .entrySet()) { // information about arrays that are only part of one store, but not // the other are discarded, as one store implicitly contains 'top' // for that array access. FlowExpressions.ArrayAccess el = e.getKey(); if (arrayValues.containsKey(el)) { V otherVal = e.getValue(); V thisVal = arrayValues.get(el); V mergedVal = thisVal.leastUpperBound(otherVal); if (mergedVal != null) { newStore.arrayValues.put(el, mergedVal); } } } for (Entry e : other.methodValues.entrySet()) { // information about methods that are only part of one store, but // not the other are discarded, as one store implicitly contains // 'top' for that field. FlowExpressions.MethodCall el = e.getKey(); if (methodValues.containsKey(el)) { V otherVal = e.getValue(); V thisVal = methodValues.get(el); V mergedVal = thisVal.leastUpperBound(otherVal); if (mergedVal != null) { newStore.methodValues.put(el, mergedVal); } } } for (Entry e : other.classValues .entrySet()) { FlowExpressions.ClassName el = e.getKey(); if (classValues.containsKey(el)) { V otherVal = e.getValue(); V thisVal = classValues.get(el); V mergedVal = thisVal.leastUpperBound(otherVal); if (mergedVal != null) { newStore.classValues.put(el, mergedVal); } } } return newStore; } /** * Returns true iff this {@link CFAbstractStore} contains a superset of the * map entries of the argument {@link CFAbstractStore}. Note that we test * the entry keys and values by Java equality, not by any subtype * relationship. This method is used primarily to simplify the equals * predicate. */ protected boolean supersetOf(CFAbstractStore other) { for (Entry e : other.localVariableValues.entrySet()) { FlowExpressions.LocalVariable key = e.getKey(); if (!localVariableValues.containsKey(key) || !localVariableValues.get(key).equals(e.getValue())) { return false; } } for (Entry e : other.fieldValues .entrySet()) { FlowExpressions.FieldAccess key = e.getKey(); if (!fieldValues.containsKey(key) || !fieldValues.get(key).equals(e.getValue())) { return false; } } for (Entry e : other.arrayValues .entrySet()) { FlowExpressions.ArrayAccess key = e.getKey(); if (!arrayValues.containsKey(key) || !arrayValues.get(key).equals(e.getValue())) { return false; } } for (Entry e : other.methodValues.entrySet()) { FlowExpressions.MethodCall key = e.getKey(); if (!methodValues.containsKey(key) || !methodValues.get(key).equals(e.getValue())) { return false; } } for (Entry e : other.classValues .entrySet()) { FlowExpressions.ClassName key = e.getKey(); if (!classValues.containsKey(key) || !classValues.get(key).equals(e.getValue())) { return false; } } return true; } @Override public boolean equals(Object o) { if (o != null && o instanceof CFAbstractStore) { @SuppressWarnings("unchecked") CFAbstractStore other = (CFAbstractStore) o; return this.supersetOf(other) && other.supersetOf(this); } else { return false; } } @SideEffectFree @Override public String toString() { return "Use a CFGVisualizer to see the Store: " + this.hashCode(); } @Override public void visualize(CFGVisualizer viz) { /* This cast is guaranteed to be safe, as long as the CFGVisualizer is created by * CFGVisualizer createCFGVisualizer() of GenericAnnotatedTypeFactory */ @SuppressWarnings("unchecked") CFGVisualizer casted_viz = (CFGVisualizer ) viz; casted_viz.visualizeStoreHeader( this.getClass().getCanonicalName()); internalVisualize(casted_viz); casted_viz.visualizeStoreFooter(); } /** * Adds a representation of the internal information of this store to * visualizer {@code viz}. */ protected void internalVisualize(CFGVisualizer viz) { for (Entry entry : localVariableValues.entrySet()) { viz.visualizeStoreLocalVar(entry.getKey(), entry.getValue()); } if (thisValue != null) { viz.visualizeStoreThisVal(thisValue); } for (Entry entry : fieldValues.entrySet()) { viz.visualizeStoreFieldVals(entry.getKey(), entry.getValue()); } for (Entry entry : arrayValues.entrySet()) { viz.visualizeStoreArrayVal(entry.getKey(), entry.getValue()); } for (Entry entry : methodValues.entrySet()) { viz.visualizeStoreMethodVals(entry.getKey(), entry.getValue()); } for (Entry entry : classValues.entrySet()) { viz.visualizeStoreClassVals(entry.getKey(), entry.getValue()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy