
soot.jimple.toolkits.annotation.nullcheck.NullnessAssumptionAnalysis Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of soot Show documentation
Show all versions of soot Show documentation
A Java Optimization Framework
package soot.jimple.toolkits.annotation.nullcheck;
/*-
* #%L
* Soot - a J*va Optimization Framework
* %%
* Copyright (C) 2006 Richard L. Halpert
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import soot.Immediate;
import soot.Local;
import soot.RefLikeType;
import soot.Unit;
import soot.Value;
import soot.jimple.ArrayRef;
import soot.jimple.DefinitionStmt;
import soot.jimple.FieldRef;
import soot.jimple.InstanceFieldRef;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.MonitorStmt;
import soot.jimple.Stmt;
import soot.jimple.internal.JCastExpr;
import soot.toolkits.graph.UnitGraph;
import soot.toolkits.scalar.BackwardFlowAnalysis;
/**
* An intraprocedural nullness assumption analysis that computes for each location and each value in a method if the value
* (before or after that location) is treated as definitely null, definitely non-null or neither. This information could be
* useful in deciding whether or not to insert code that accesses a potentially null object. If the original program assumes
* a value is non-null, then adding a use of that value will not introduce any NEW nullness errors into the program. This
* code may be buggy, or just plain wrong. It has not been checked.
*
* @author Richard L. Halpert Adapted from Eric Bodden's NullnessAnalysis
*/
public class NullnessAssumptionAnalysis extends BackwardFlowAnalysis {
protected final static Object BOTTOM = new Object() {
public String toString() {
return "bottom";
}
};
protected final static Object NULL = new Object() {
public String toString() {
return "null";
}
};
protected final static Object NON_NULL = new Object() {
public String toString() {
return "non-null";
}
};
// TOP IS MEANINGLESS FOR THIS ANALYSIS: YOU CAN'T ASSUME A VALUE IS NULL AND NON_NULL. BOTTOM IS USED FOR THAT CASE
protected final static Object TOP = new Object() {
public String toString() {
return "top";
}
};
/**
* Creates a new analysis for the given graph/
*
* @param graph
* any unit graph
*/
public NullnessAssumptionAnalysis(UnitGraph graph) {
super(graph);
doAnalysis();
}
/**
* {@inheritDoc}
*/
protected void flowThrough(Object inValue, Object unit, Object outValue)
// protected void flowThrough(Object flowin, Unit u, List fallOut, List branchOuts)
{
AnalysisInfo in = (AnalysisInfo) inValue;
AnalysisInfo out = new AnalysisInfo(in);
Stmt s = (Stmt) unit;
// in case of an if statement, we neet to compute the branch-flow;
// e.g. for a statement "if(x!=null) goto s" we have x==null for the fallOut and
// x!=null for the branchOut
// or for an instanceof expression
// if(s instanceof JIfStmt) {
// JIfStmt ifStmt = (JIfStmt) s;
// handleIfStmt(ifStmt, in, out, outBranch);
// }
// in case of a monitor statement, we know that the programmer assumes we have a non-null value
if (s instanceof MonitorStmt) {
MonitorStmt monitorStmt = (MonitorStmt) s;
out.put(monitorStmt.getOp(), NON_NULL);
}
// if we have an array ref, set the info for this ref to TOP,
// cause we need to be conservative here
if (s.containsArrayRef()) {
ArrayRef arrayRef = s.getArrayRef();
handleArrayRef(arrayRef, out);
}
// same for field refs, but also set the receiver object to non-null, if there is one
if (s.containsFieldRef()) {
FieldRef fieldRef = s.getFieldRef();
handleFieldRef(fieldRef, out);
}
// same for invoke expr., also set the receiver object to non-null, if there is one
if (s.containsInvokeExpr()) {
InvokeExpr invokeExpr = s.getInvokeExpr();
handleInvokeExpr(invokeExpr, out);
}
// allow sublasses to define certain values as always-non-null
for (Iterator outIter = out.entrySet().iterator(); outIter.hasNext();) {
Entry entry = (Entry) outIter.next();
Value v = (Value) entry.getKey();
if (isAlwaysNonNull(v)) {
entry.setValue(NON_NULL);
}
}
// if we have a definition (assignment) statement to a ref-like type, handle it,
if (s instanceof DefinitionStmt) {
// need to copy the current out set because we need to assign under this assumption;
// so this copy becomes the in-set to handleRefTypeAssignment
AnalysisInfo temp = new AnalysisInfo(out);
DefinitionStmt defStmt = (DefinitionStmt) s;
if (defStmt.getLeftOp().getType() instanceof RefLikeType) {
handleRefTypeAssignment(defStmt, temp, out);
}
}
// save memory by only retaining information about locals
for (Iterator outIter = out.keySet().iterator(); outIter.hasNext();) {
Value v = (Value) outIter.next();
if (!(v instanceof Local)) {
outIter.remove();
}
}
// for (Iterator outBranchIter = outBranch.keySet().iterator(); outBranchIter.hasNext();) {
// Value v = (Value) outBranchIter.next();
// if(!(v instanceof Local)) {
// outBranchIter.remove();
// }
// }
// now copy the computed info to out
copy(out, outValue);
}
/**
* This can be overridden by sublasses to mark a certain value as constantly non-null.
*
* @param v
* any value
* @return true if it is known that this value (e.g. a method return value) is never null
*/
protected boolean isAlwaysNonNull(Value v) {
return false;
}
private void handleArrayRef(ArrayRef arrayRef, AnalysisInfo out) {
Value array = arrayRef.getBase();
// here we know that the array must point to an object, but the array value might be anything
out.put(array, NON_NULL);
// out.put(arrayRef, TOP);
}
private void handleFieldRef(FieldRef fieldRef, AnalysisInfo out) {
if (fieldRef instanceof InstanceFieldRef) {
InstanceFieldRef instanceFieldRef = (InstanceFieldRef) fieldRef;
// here we know that the receiver must point to an object
Value base = instanceFieldRef.getBase();
out.put(base, NON_NULL);
}
// but the referenced object might point to everything
// out.put(fieldRef, TOP);
}
private void handleInvokeExpr(InvokeExpr invokeExpr, AnalysisInfo out) {
if (invokeExpr instanceof InstanceInvokeExpr) {
InstanceInvokeExpr instanceInvokeExpr = (InstanceInvokeExpr) invokeExpr;
// here we know that the receiver must point to an object
Value base = instanceInvokeExpr.getBase();
out.put(base, NON_NULL);
}
// but the returned object might point to everything
// out.put(invokeExpr, TOP);
}
private void handleRefTypeAssignment(DefinitionStmt assignStmt, AnalysisInfo rhsInfo, AnalysisInfo out) {
Value left = assignStmt.getLeftOp();
Value right = assignStmt.getRightOp();
// unbox casted value
if (right instanceof JCastExpr) {
JCastExpr castExpr = (JCastExpr) right;
right = castExpr.getOp();
}
// An assignment invalidates any assumptions of null/non-null for lhs
// We COULD be more accurate by assigning those assumptions to the rhs prior to this statement
rhsInfo.put(right, BOTTOM);
// assign from rhs to lhs
out.put(left, rhsInfo.get(right));
}
/**
* {@inheritDoc}
*/
protected void copy(Object source, Object dest) {
Map s = (Map) source;
Map d = (Map) dest;
d.clear();
d.putAll(s);
}
/**
* {@inheritDoc}
*/
protected Object entryInitialFlow() {
return new AnalysisInfo();
}
/**
* {@inheritDoc}
*/
protected void merge(Object in1, Object in2, Object out) {
AnalysisInfo left = (AnalysisInfo) in1;
AnalysisInfo right = (AnalysisInfo) in2;
AnalysisInfo res = (AnalysisInfo) out;
Set values = new HashSet();
values.addAll(left.keySet());
values.addAll(right.keySet());
res.clear();
for (Iterator keyIter = values.iterator(); keyIter.hasNext();) {
Value v = (Value) keyIter.next();
Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy