/******************************************************************************* * Copyright (c) 2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package com.ibm.wala.ipa.modref; import java.util.Collection; import java.util.Iterator; import java.util.Map; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IField; import com.ibm.wala.dataflow.graph.BitVectorSolver; import com.ibm.wala.fixpoint.BitVectorVariable; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.CallGraph; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis; import com.ibm.wala.ipa.callgraph.propagation.PointerKey; import com.ibm.wala.ipa.cha.ClassHierarchyException; import com.ibm.wala.ipa.slicer.HeapExclusions; import com.ibm.wala.ipa.slicer.PDG; import com.ibm.wala.ssa.IR; import com.ibm.wala.ssa.SSAArrayLengthInstruction; import com.ibm.wala.ssa.SSAArrayLoadInstruction; import com.ibm.wala.ssa.SSAArrayStoreInstruction; import com.ibm.wala.ssa.SSAGetInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSANewInstruction; import com.ibm.wala.ssa.SSAPutInstruction; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.debug.Assertions; import com.ibm.wala.util.graph.impl.GraphInverter; import com.ibm.wala.util.intset.OrdinalSet; import com.ibm.wala.util.warnings.WarningSet; /** * Mod-ref analysis for heap locations. * * For each call graph node, what heap locations (as determined by a heap model) * may it read or write, including it's callees transitively * * @author sjfink * */ public class ModRef { /** * For each call graph node, what heap locations (as determined by a heap * model) may it write, including its callees transitively * */ public static Map> computeMod(CallGraph cg, PointerAnalysis pa, HeapExclusions heapExclude) { Map> scan = scanForMod(cg, pa, heapExclude); return transitiveClosure(cg, scan); } /** * For each call graph node, what heap locations (as determined by a heap * model) may it read, including its callees transitively * */ public static Map> computeRef(CallGraph cg, PointerAnalysis pa, HeapExclusions heapExclude) { Map> scan = scanForRef(cg, pa, heapExclude); return transitiveClosure(cg, scan); } /** * For each call graph node, what heap locations (as determined by a heap * model) may it write, including its callees transitively * */ public static Map> computeMod(CallGraph cg, PointerAnalysis pa) { return computeMod(cg, pa, null); } /** * For each call graph node, what heap locations (as determined by a heap * model) may it read, including its callees transitively * */ public static Map> computeRef(CallGraph cg, PointerAnalysis pa) { return computeRef(cg, pa, null); } private static Map> transitiveClosure(CallGraph cg, Map> scan) { GenReach gr = new GenReach(GraphInverter.invert(cg), scan); BitVectorSolver solver = new BitVectorSolver(gr); solver.solve(); Map> result = HashMapFactory.make(); for (Iterator it = cg.iterator(); it.hasNext();) { CGNode n = it.next(); BitVectorVariable bv = (BitVectorVariable) solver.getOut(n); result.put(n, new OrdinalSet(bv.getValue(), gr.getLatticeValues())); } return result; } /** * For each call graph node, what heap locations (as determined by a heap * model) may it write, NOT including its callees transitively * * @param heapExclude */ private static Map> scanForMod(CallGraph cg, PointerAnalysis pa, HeapExclusions heapExclude) { Map> result = HashMapFactory.make(); for (Iterator it = cg.iterator(); it.hasNext();) { CGNode n = it.next(); result.put(n, scanNodeForMod(n, pa, heapExclude)); } return result; } /** * For each call graph node, what heap locations (as determined by a heap * model) may it read, NOT including its callees transitively * * @param heapExclude */ private static Map> scanForRef(CallGraph cg, PointerAnalysis pa, HeapExclusions heapExclude) { Map> result = HashMapFactory.make(); for (Iterator it = cg.iterator(); it.hasNext();) { CGNode n = it.next(); result.put(n, scanNodeForRef(n, pa, heapExclude)); } return result; } /** * For a call graph node, what heap locations (as determined by a heap model) * may it write, NOT including it's callees transitively * * @param heapExclude */ private static Collection scanNodeForMod(final CGNode n, final PointerAnalysis pa, HeapExclusions heapExclude) { Collection result = HashSetFactory.make(); final ExtendedHeapModel h = new DelegatingExtendedHeapModel(pa.getHeapModel()); SSAInstruction.Visitor v = new ModVisitor(n, result, h, pa); IR ir = n.getIR(new WarningSet()); if (ir != null) { for (Iterator it = ir.iterateNormalInstructions(); it.hasNext();) { it.next().visit(v); } } if (heapExclude != null) { result = heapExclude.filter(result); } return result; } /** * For a call graph node, what heap locations (as determined by a heap model) * may it read, NOT including it's callees transitively */ private static Collection scanNodeForRef(final CGNode n, final PointerAnalysis pa, HeapExclusions heapExclude) { Collection result = HashSetFactory.make(); final ExtendedHeapModel h = new DelegatingExtendedHeapModel(pa.getHeapModel()); SSAInstruction.Visitor v = new RefVisitor(n, result, pa, h); IR ir = n.getIR(new WarningSet()); if (ir != null) { for (Iterator it = ir.iterateNormalInstructions(); it.hasNext();) { it.next().visit(v); } } if (heapExclude != null) { result = heapExclude.filter(result); } return result; } private static final class RefVisitor extends SSAInstruction.Visitor { private final CGNode n; private final Collection result; private final PointerAnalysis pa; private final ExtendedHeapModel h; private RefVisitor(CGNode n, Collection result, PointerAnalysis pa, ExtendedHeapModel h) { this.n = n; this.result = result; this.pa = pa; this.h = h; } @Override public void visitArrayLength(SSAArrayLengthInstruction instruction) { PointerKey ref = h.getPointerKeyForLocal(n, instruction.getArrayRef()); for (InstanceKey i : pa.getPointsToSet(ref)) { result.add(h.getPointerKeyForArrayLength(i)); } } @Override public void visitArrayLoad(SSAArrayLoadInstruction instruction) { PointerKey ref = h.getPointerKeyForLocal(n, instruction.getArrayRef()); for (InstanceKey i : pa.getPointsToSet(ref)) { result.add(h.getPointerKeyForArrayContents(i)); } } @Override public void visitGet(SSAGetInstruction instruction) { IField f = pa.getClassHierarchy().resolveField(instruction.getDeclaredField()); if (f != null) { if (instruction.isStatic()) { result.add(h.getPointerKeyForStaticField(f)); } else { PointerKey ref = h.getPointerKeyForLocal(n, instruction.getRef()); for (InstanceKey i : pa.getPointsToSet(ref)) { result.add(h.getPointerKeyForInstanceField(i, f)); } } } } } private static final class ModVisitor extends SSAInstruction.Visitor { private final CGNode n; private final Collection result; private final ExtendedHeapModel h; private final PointerAnalysis pa; private ModVisitor(CGNode n, Collection result, ExtendedHeapModel h, PointerAnalysis pa) { this.n = n; this.result = result; this.h = h; this.pa = pa; } @Override public void visitNew(SSANewInstruction instruction) { if (instruction.getConcreteType().isArrayType()) { int dim = instruction.getConcreteType().getDimensionality(); if (dim > 1) { for (int d = 0; d < dim - 1; d++) { InstanceKey i = h.getInstanceKeyForMultiNewArray(n, instruction.getNewSite(), d); PointerKey pk = h.getPointerKeyForArrayContents(i); if (pk == null) { h.getPointerKeyForArrayContents(i); } assert pk != null; result.add(pk); pk = h.getPointerKeyForArrayLength(i); assert pk != null; result.add(pk); } } else { // allocation of 1D arr "writes" the contents of the array and the // length field InstanceKey i = h.getInstanceKeyForAllocation(n, instruction.getNewSite()); PointerKey pk = h.getPointerKeyForArrayContents(i); if (pk == null) { h.getPointerKeyForArrayContents(i); } assert pk != null; result.add(pk); pk = h.getPointerKeyForArrayLength(i); assert pk != null; result.add(pk); } } else { if (!PDG.IGNORE_ALLOC_HEAP_DEFS) { // allocation of a scalar "writes" all fields in the scalar InstanceKey i = h.getInstanceKeyForAllocation(n, instruction.getNewSite()); if (i != null) { IClass type = i.getConcreteType(); try { for (IField f : type.getAllInstanceFields()) { PointerKey pk = h.getPointerKeyForInstanceField(i, f); assert pk != null; result.add(pk); } } catch (ClassHierarchyException e) { Assertions.UNREACHABLE(); e.printStackTrace(); } } } } } @Override public void visitArrayStore(SSAArrayStoreInstruction instruction) { PointerKey ref = h.getPointerKeyForLocal(n, instruction.getArrayRef()); for (InstanceKey i : pa.getPointsToSet(ref)) { result.add(h.getPointerKeyForArrayContents(i)); } } @Override public void visitPut(SSAPutInstruction instruction) { IField f = pa.getClassHierarchy().resolveField(instruction.getDeclaredField()); if (f != null) { if (instruction.isStatic()) { result.add(h.getPointerKeyForStaticField(f)); } else { PointerKey ref = h.getPointerKeyForLocal(n, instruction.getRef()); for (InstanceKey i : pa.getPointsToSet(ref)) { result.add(h.getPointerKeyForInstanceField(i, f)); } } } } } public static Collection getMod(CGNode n, ExtendedHeapModel h, PointerAnalysis pa, SSAInstruction s, HeapExclusions hexcl) { Collection result = HashSetFactory.make(2); ModVisitor v = new ModVisitor(n, result, h, pa); s.visit(v); return hexcl == null ? result : hexcl.filter(result); } public static Collection getRef(CGNode n, ExtendedHeapModel h, PointerAnalysis pa, SSAInstruction s, HeapExclusions hexcl) { Collection result = HashSetFactory.make(2); RefVisitor v = new RefVisitor(n, result, pa, h); s.visit(v); return hexcl == null ? result : hexcl.filter(result); } }