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

com.ibm.wala.analysis.arraybounds.ArrayBoundsGraph Maven / Gradle / Ivy

package com.ibm.wala.analysis.arraybounds;

import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperEdge;
import com.ibm.wala.analysis.arraybounds.hypergraph.DirectedHyperGraph;
import com.ibm.wala.analysis.arraybounds.hypergraph.HyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.SoftFinalHyperNode;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.Weight.Type;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.AdditiveEdgeWeight;
import com.ibm.wala.analysis.arraybounds.hypergraph.weight.edgeweights.EdgeWeight;
import com.ibm.wala.util.collections.Pair;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * Some thoughts about implementation details, not mentioned in [1]:
 *
 * 

As it is written The paper describes, that the distance is equal to the shortest hyper path. * But what if we don't know anything about a variable (i.e. it is returned by a method)? There will * be no path at all, the distance should be unlimited. * *

Initializing all nodes with -infinity instead of infinity, seems to work at first glance, as * we also have hyper edges with more than one source, which cause the maximum to be propagated * instead of minimum. However, this will not work, as loops will not get updated properly. * *

We need to make sure, that only nodes, which are not connected to the source of shortest path * computation are set to infinity. To do so, it is enough to set nodes, which don't have a * predecessor to infinity. (Nodes in cycles will always have an ancestor, which is not part of the * cycle. So all nodes are either connected to the source, or a node with no predecessor.) * *

In this implementation this is done, by adding an infinity node and connect all lose ends to * it (see {@link ArrayBoundsGraphBuilder#bundleDeadEnds(ArrayBoundsGraph)}). Note, that array * length and the zero node are dead ends, if they are not the source of a shortest path * computation. To prevent changing the inequality graph, depending on which source is used, a kind * of trap door construct is used (See {@link ArrayBoundsGraph#createSourceVar(Integer)}). * *

There are some variations, but these are minor changes to improve results: * *

    *
  • handling of constants (see {@link ArrayBoundsGraph#addConstant(Integer, Integer)}) *
  • pi nodes (see {@link ArrayBoundsGraph#addPhi(Integer)}) *
  • array length nodes (see {@link ArrayBoundsGraph#arrayLength}) *
* * [1] Bodík, Rastislav, Rajiv Gupta, and Vivek Sarkar. "ABCD: eliminating array bounds checks on * demand." ACM SIGPLAN Notices. Vol. 35. No. 5. ACM, 2000. * * @author Stephan Gocht {@code } */ @SuppressWarnings({"JavadocReference", "javadoc"}) public class ArrayBoundsGraph extends DirectedHyperGraph { /** * We need a ssa variable representing zero. So we just use an integer, which is never produced by * ssa generation */ public static final Integer ZERO = -1; public static final Integer ZERO_HELPER = -3; /** * We need a ssa variable representing unlimited (values we don't know anything about). So we just * use an integer, which is never produced by ssa generation */ public static final Integer UNLIMITED = -2; /** * Maps each array variable to a set of variables, which are used as Index for accessing that * array */ private final HashMap> arrayAccess; /** * Maps each array variable to a node which is parent to all variables, that contain the array * length */ private final HashMap arrayLength; private final HashSet phis; private final HashMap> constants; /** * For simplicity we introduce extra variables, for arrayLength, to have a unique node * representing the array length, even if the length is accessed more than once in the code. * *

Start with -3 so it is unequal to other constants */ private Integer arrayCounter = -4; public ArrayBoundsGraph() { this.arrayAccess = new HashMap<>(); this.arrayLength = new HashMap<>(); this.constants = new HashMap<>(); this.phis = new HashSet<>(); this.addNode(UNLIMITED); this.phis.add(UNLIMITED); this.createSourceVar(ZERO); this.addNode(ZERO_HELPER); this.addEdge(ZERO, ZERO_HELPER); // this.phis.add(ZERO_HELPER); } public void postProcessConstants() { for (Map.Entry> entry : constants.entrySet()) { HyperNode constantNode = this.getNodes().get(entry.getKey()); final Pair value = entry.getValue(); HyperNode helper1 = this.getNodes().get(value.fst); HyperNode helper2 = this.getNodes().get(value.snd); for (DirectedHyperEdge edge : constantNode.getOutEdges()) { if (!edge.getDestination().contains(helper2)) { edge.getSource().remove(constantNode); edge.getSource().add(helper1); } } } } public void addAdditionEdge(Integer src, Integer dst, Integer value) { this.addNode(src); final HyperNode srcNode = this.getNodes().get(src); this.addNode(dst); final HyperNode dstNode = this.getNodes().get(dst); Weight weight; if (value == 0) { weight = Weight.ZERO; } else { weight = new Weight(Type.NUMBER, value); } final EdgeWeight edgeWeight = new AdditiveEdgeWeight(weight); final DirectedHyperEdge edge = new DirectedHyperEdge<>(); edge.getDestination().add(dstNode); edge.getSource().add(srcNode); edge.setWeight(edgeWeight); this.getEdges().add(edge); } public void addArray(Integer array) { this.getArrayNode(array); } /** * Add variable as constant with value value. * *

This will create the following construct: [zero] -(value)-> [h1] -0- > [variable] * -(-value)-> [h2] -0-> [zero]. * *

The bidirectional linking, allows things like * *

   * int[] a = new int[2]();
   * a[0] = 1;
   * 
* * to work properly. h1, h2 are helper nodes: [zero] and [variable] may have other predecessors, * this will cause their in edges to be merged to a single hyper edge with weight zero. The helper * nodes are inserted to keep the proper distance from [zero]. */ public void addConstant(Integer variable, Integer value) { final Integer helper1 = this.generateNewVar(); final Integer helper2 = this.generateNewVar(); this.addAdditionEdge(ZERO_HELPER, helper1, value); // this.addEdge(helper1, variable); this.addAdditionEdge(variable, helper2, -value); this.addEdge(helper2, ZERO_HELPER); this.constants.put(variable, Pair.make(helper1, helper2)); } public void addEdge(Integer src, Integer dst) { this.addAdditionEdge(src, dst, 0); } public HyperNode addNode(Integer value) { HyperNode result; if (!this.getNodes().containsKey(value)) { result = new HyperNode<>(value); this.getNodes().put(value, result); } else { result = this.getNodes().get(value); } return result; } public void addPhi(Integer dst) { this.phis.add(dst); } public void addPi(Integer dst, Integer src1, Integer src2) { this.addEdge(src1, dst); this.addEdge(src2, dst); } /** * Adds var as source var. A source var is a variable, which can be used as source for shortest * path computation. * *

This will create the following construct: [unlimited] -> [var] -> [var] * -(unlimited)-> [unlimited] * *

This is a trap door construct: if [var] is not set to 0 it will get the value unlimited, if * [var] is set to 0 it will stay 0. */ public void createSourceVar(Integer var) { if (this.getNodes().containsKey(var)) { throw new AssertionError("Source variables should only be created once."); } SoftFinalHyperNode node = new SoftFinalHyperNode<>(var); this.getNodes().put(var, node); // final HyperNode varNode = this.getNodes().get(var); // final HyperNode unlimitedNode = this.getNodes().get(UNLIMITED); // final DirectedHyperEdge edge = new DirectedHyperEdge<>(); // edge.setWeight(new AdditiveEdgeWeight(Weight.UNLIMITED)); // edge.getSource().add(varNode); // edge.getDestination().add(unlimitedNode); // this.getEdges().add(edge); this.addEdge(UNLIMITED, var); // this.addEdge(var, var); } public Integer generateNewVar() { final int result = this.arrayCounter; this.arrayCounter -= 1; return result; } public HashMap> getArrayAccess() { return this.arrayAccess; } public HashMap getArrayLength() { return this.arrayLength; } public Integer getArrayNode(Integer array) { Integer arrayVar; if (!this.arrayLength.containsKey(array)) { arrayVar = this.generateNewVar(); this.arrayLength.put(array, arrayVar); this.createSourceVar(arrayVar); } else { arrayVar = this.arrayLength.get(array); } return arrayVar; } public HashSet getPhis() { return this.phis; } public void markAsArrayAccess(Integer array, Integer index) { Set indices; if (!this.arrayAccess.containsKey(array)) { indices = new HashSet<>(); this.arrayAccess.put(array, indices); } else { indices = this.arrayAccess.get(array); } indices.add(index); this.addArray(array); } /** Mark variable as length for array. */ public void markAsArrayLength(Integer array, Integer variable) { this.addEdge(this.getArrayNode(array), variable); } public void markAsDeadEnd(Integer variable) { this.addEdge(UNLIMITED, variable); } public Weight getVariableWeight(Integer variable) { if (constants.containsKey(variable)) { variable = constants.get(variable).fst; } return this.getNodes().get(variable).getWeight(); } @Override public void reset() { super.reset(); this.getNodes().get(UNLIMITED).setWeight(Weight.UNLIMITED); this.getNodes().get(UNLIMITED).setNewWeight(Weight.UNLIMITED); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy