package org.checkerframework.framework.flow; /*>>> import org.checkerframework.checker.nullness.qual.Nullable; */ 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; 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; /** * A store for the checker framework analysis tracks the annotations of memory locations such as * local variables and fields. * * <p>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<V extends CFAbstractValue<V>, S extends CFAbstractStore<V, S>> implements Store<S> { /** The analysis class this store belongs to. */ protected final CFAbstractAnalysis<V, S, ?> analysis; /** Information collected about local variables (including method arguments). */ protected final Map<FlowExpressions.LocalVariable, V> localVariableValues; /** Information collected about the current object. */ protected V thisValue; /** * Information collected about fields, using the internal representation {@link FieldAccess}. */ protected Map<FlowExpressions.FieldAccess, V> fieldValues; /** * Information collected about arrays, using the internal representation {@link ArrayAccess}. */ protected Map<FlowExpressions.ArrayAccess, V> arrayValues; /** * Information collected about method calls, using the internal representation {@link * MethodCall}. */ protected Map<FlowExpressions.MethodCall, V> methodValues; /** * Information collected about <i>classname</i>.class values, using the internal representation * {@link ClassName}. */ protected Map<FlowExpressions.ClassName, V> 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<V, S, ?> 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<V, S> 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. * * <ol> * <li>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. * <li>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). * <li>Furthermore, if the field has a monotonic annotation, then its information can also be * kept. * </ol> * * 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<FlowExpressions.FieldAccess, V> newFieldValues = new HashMap<>(); for (Entry<FlowExpressions.FieldAccess, V> e : fieldValues.entrySet()) { FlowExpressions.FieldAccess fieldAccess = e.getKey(); V otherVal = e.getValue(); // case 3: List<Pair<AnnotationMirror, AnnotationMirror>> fieldAnnotations = atypeFactory.getAnnotationWithMetaAnnotation( fieldAccess.getField(), MonotonicQualifier.class); V newOtherVal = null; for (Pair<AnnotationMirror, AnnotationMirror> fieldAnnotation : fieldAnnotations) { AnnotationMirror monotonicAnnotation = fieldAnnotation.second; Name annotation = AnnotationUtils.getElementValueClassName( monotonicAnnotation, "value", false); AnnotationMirror target = AnnotationUtils.fromName(atypeFactory.getElementUtils(), annotation); // Make sure the 'target' annotation is present. if (AnnotationUtils.containsSame(otherVal.getAnnotations(), target)) { newOtherVal = analysis.createSingleAnnotationValue( target, otherVal.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}). * * <p>This method does not take care of removing other information that might be influenced by * changes to certain parts of the state. * * <p>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}). * * <p>This method does not take care of removing other information that might be influenced by * changes to certain parts of the state. * * <p>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<Pair<AnnotationMirror, AnnotationMirror>> fieldAnnotations = atypeFactory.getAnnotationWithMetaAnnotation( fieldAcc.getField(), MonotonicQualifier.class); for (Pair<AnnotationMirror, AnnotationMirror> fieldAnnotation : fieldAnnotations) { AnnotationMirror monotonicAnnotation = fieldAnnotation.second; Name annotation = AnnotationUtils.getElementValueClassName( monotonicAnnotation, "value", false); AnnotationMirror target = AnnotationUtils.fromName(atypeFactory.getElementUtils(), annotation); // Make sure the 'target' annotation is present. if (AnnotationUtils.containsSame(value.getAnnotations(), 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. * * <p>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. * * <p>See {@link #removeConflicting(FlowExpressions.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 <em>a.f</em> for * some <em>a</em>. * * <ol> * <li value="1">Update the abstract value of other field accesses <em>b.g</em> where the * field is equal (that is, <em>f=g</em>), and the receiver <em>b</em> might alias the * receiver of {@code fieldAccess}, <em>a</em>. 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 <em>g</em> is not final. * <li value="2">Remove any abstract values for field accesses <em>b.g</em> where {@code * fieldAccess} might alias any expression in the receiver <em>b</em>. * <li value="3">Remove any information about method calls. * <li value="4">Remove any abstract values an array access <em>b[i]</em> where {@code * fieldAccess} might alias any expression in the receiver <em>a</em> or index <em>i</em>. * </ol> * * @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<FlowExpressions.FieldAccess, V> newFieldValues = new HashMap<>(); for (Entry<FlowExpressions.FieldAccess, V> 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<FlowExpressions.ArrayAccess, V> newArrayValues = new HashMap<>(); for (Entry<ArrayAccess, V> 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 <em>a[i]</em> for some * <em>a</em>. * * <ol> * <li value="1">Remove any abstract value for other array access <em>b[j]</em> where * <em>a</em> and <em>b</em> can be aliases, or where either <em>b</em> or <em>j</em> * contains a modifiable alias of <em>a[i]</em>. * <li value="2">Remove any abstract values for field accesses <em>b.g</em> where * <em>a[i]</em> might alias any expression in the receiver <em>b</em> and there is an * array expression somewhere in the receiver. * <li value="3">Remove any information about method calls. * </ol> * * @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<FlowExpressions.ArrayAccess, V> newArrayValues = new HashMap<>(); for (Entry<FlowExpressions.ArrayAccess, V> 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<FlowExpressions.FieldAccess, V> newFieldValues = new HashMap<>(); for (Entry<FieldAccess, V> 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: * * <ol> * <li value="1">Remove any abstract values for field accesses <em>b.g</em> where {@code * localVar} might alias any expression in the receiver <em>b</em>. * <li value="2">Remove any abstract values for array accesses <em>a[i]</em> where {@code * localVar} might alias the receiver <em>a</em>. * <li value="3">Remove any information about method calls where the receiver or any of the * parameters contains {@code localVar}. * </ol> */ protected void removeConflicting(LocalVariable var) { Map<FlowExpressions.FieldAccess, V> newFieldValues = new HashMap<>(); for (Entry<FlowExpressions.FieldAccess, V> e : fieldValues.entrySet()) { FlowExpressions.FieldAccess otherFieldAccess = e.getKey(); // case 1: if (otherFieldAccess.containsSyntacticEqualReceiver(var)) { continue; } newFieldValues.put(otherFieldAccess, e.getValue()); } fieldValues = newFieldValues; Map<FlowExpressions.ArrayAccess, V> newArrayValues = new HashMap<>(); for (Entry<FlowExpressions.ArrayAccess, V> e : arrayValues.entrySet()) { FlowExpressions.ArrayAccess otherArrayAccess = e.getKey(); // case 2: if (otherArrayAccess.containsSyntacticEqualReceiver(var)) { continue; } newArrayValues.put(otherArrayAccess, e.getValue()); } arrayValues = newArrayValues; Map<FlowExpressions.MethodCall, V> newMethodValues = new HashMap<>(); for (Entry<FlowExpressions.MethodCall, V> 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) { return upperBound(other, false); } @Override public S widenUpperBound(S other) { return upperBound(other, true); } private S upperBound(S other, boolean shouldWiden) { S newStore = analysis.createEmptyStore(sequentialSemantics); for (Entry<FlowExpressions.LocalVariable, V> 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 = upperBoundOfValues(otherVal, thisVal, shouldWiden); 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 : upperBoundOfValues(otherVal, myVal, shouldWiden); if (mergedVal != null) { newStore.thisValue = mergedVal; } } for (Entry<FlowExpressions.FieldAccess, V> 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 = upperBoundOfValues(otherVal, thisVal, shouldWiden); if (mergedVal != null) { newStore.fieldValues.put(el, mergedVal); } } } for (Entry<FlowExpressions.ArrayAccess, V> 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 = upperBoundOfValues(otherVal, thisVal, shouldWiden); if (mergedVal != null) { newStore.arrayValues.put(el, mergedVal); } } } for (Entry<MethodCall, V> 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 = upperBoundOfValues(otherVal, thisVal, shouldWiden); if (mergedVal != null) { newStore.methodValues.put(el, mergedVal); } } } for (Entry<FlowExpressions.ClassName, V> e : other.classValues.entrySet()) { FlowExpressions.ClassName el = e.getKey(); if (classValues.containsKey(el)) { V otherVal = e.getValue(); V thisVal = classValues.get(el); V mergedVal = upperBoundOfValues(otherVal, thisVal, shouldWiden); if (mergedVal != null) { newStore.classValues.put(el, mergedVal); } } } return newStore; } private V upperBoundOfValues(V otherVal, V thisVal, boolean shouldWiden) { return shouldWiden ? thisVal.widenUpperBound(otherVal) : thisVal.leastUpperBound(otherVal); } /** * 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<V, S> other) { for (Entry<FlowExpressions.LocalVariable, V> e : other.localVariableValues.entrySet()) { FlowExpressions.LocalVariable key = e.getKey(); if (!localVariableValues.containsKey(key) || !localVariableValues.get(key).equals(e.getValue())) { return false; } } for (Entry<FlowExpressions.FieldAccess, V> e : other.fieldValues.entrySet()) { FlowExpressions.FieldAccess key = e.getKey(); if (!fieldValues.containsKey(key) || !fieldValues.get(key).equals(e.getValue())) { return false; } } for (Entry<FlowExpressions.ArrayAccess, V> e : other.arrayValues.entrySet()) { FlowExpressions.ArrayAccess key = e.getKey(); if (!arrayValues.containsKey(key) || !arrayValues.get(key).equals(e.getValue())) { return false; } } for (Entry<MethodCall, V> e : other.methodValues.entrySet()) { FlowExpressions.MethodCall key = e.getKey(); if (!methodValues.containsKey(key) || !methodValues.get(key).equals(e.getValue())) { return false; } } for (Entry<FlowExpressions.ClassName, V> 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<V, S> other = (CFAbstractStore<V, S>) 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<?, S, ?> viz) { /* This cast is guaranteed to be safe, as long as the CFGVisualizer is created by * CFGVisualizer<Value, Store, TransferFunction> createCFGVisualizer() of GenericAnnotatedTypeFactory */ @SuppressWarnings("unchecked") CFGVisualizer<V, S, ?> casted_viz = (CFGVisualizer<V, S, ?>) 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<V, S, ?> viz) { for (Entry<FlowExpressions.LocalVariable, V> entry : localVariableValues.entrySet()) { viz.visualizeStoreLocalVar(entry.getKey(), entry.getValue()); } if (thisValue != null) { viz.visualizeStoreThisVal(thisValue); } for (Entry<FlowExpressions.FieldAccess, V> entry : fieldValues.entrySet()) { viz.visualizeStoreFieldVals(entry.getKey(), entry.getValue()); } for (Entry<FlowExpressions.ArrayAccess, V> entry : arrayValues.entrySet()) { viz.visualizeStoreArrayVal(entry.getKey(), entry.getValue()); } for (Entry<MethodCall, V> entry : methodValues.entrySet()) { viz.visualizeStoreMethodVals(entry.getKey(), entry.getValue()); } for (Entry<FlowExpressions.ClassName, V> entry : classValues.entrySet()) { viz.visualizeStoreClassVals(entry.getKey(), entry.getValue()); } } }