Updates for call graph slice view (latest is click constructor fix) and
[eclipsethinslicer.git] / Svelte / src / edu / berkeley / cs / bodik / svelte / CGNodeUtils.java
blob3c73dc35b5aada0ba1aeec394b94596f4456e24e
1 package edu.berkeley.cs.bodik.svelte;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.net.URL;
6 import java.util.ArrayList;
7 import java.util.HashSet;
8 import java.util.Iterator;
10 import com.ibm.wala.cast.java.loader.JavaSourceLoaderImpl.ConcreteJavaMethod;
11 import com.ibm.wala.cast.tree.CAstSourcePositionMap.Position;
12 import com.ibm.wala.classLoader.IMethod;
13 import com.ibm.wala.classLoader.ShrikeBTMethod;
14 import com.ibm.wala.ipa.callgraph.CGNode;
15 import com.ibm.wala.ipa.slicer.NormalStatement;
16 import com.ibm.wala.ipa.slicer.Statement;
17 import com.ibm.wala.shrikeCT.InvalidClassFileException;
18 import com.ibm.wala.ssa.IR;
19 import com.ibm.wala.ssa.SSAInstruction;
20 import com.ibm.wala.ssa.SSAInvokeInstruction;
21 import com.ibm.wala.util.collections.HashSetFactory;
22 import com.ibm.wala.util.debug.Assertions;
23 import com.ibm.wala.util.intset.IntSet;
25 /**
26 * Utilities dealing with Call Graph Nodes (which are essentially methods)
27 * These would be handy in the CGNode itself.
28 * Most of these are only useful for methods coming from a real .java
29 * or .class file we are analyzing.
31 * @author evan
34 public class CGNodeUtils {
36 /**
37 * Do a brute-force search thru the statements of the Call Graph and find
38 * the lexically first statement which calls a method called <code>methodName</code>
40 * @param n
41 * The method with code to search through.
42 * @param methodName
43 * The method name, irrespective of class it is defined in, whether it is static, etc.
44 * @return
45 * The statement found (i.e. an invokestatic or invokevirtual statement), or null if none found.
47 public static Statement findCallTo(CGNode n, String methodName) {
48 IR ir = n.getIR();
49 for (Iterator<SSAInstruction> it = ir.iterateAllInstructions(); it.hasNext();) {
50 SSAInstruction s = it.next();
51 if (s instanceof SSAInvokeInstruction) {
52 SSAInvokeInstruction call = (SSAInvokeInstruction) s;
53 if (call.getCallSite().getDeclaredTarget().getName().toString().equals(methodName)) {
54 IntSet indices = ir.getCallInstructionIndices(((SSAInvokeInstruction) s).getCallSite());
55 Assertions.productionAssertion(indices.size() == 1, "expected 1 but got " + indices.size());
56 return new NormalStatement(n, indices.intIterator().next());
60 Assertions.UNREACHABLE("failed to find call to " + methodName + " in " + n);
61 return null;
64 /**
65 * Find a particular SSA instruction in the method's instructions (by a simple brute force search),
66 * and create a NormalStatement from this. A NormalStatement contains the CGNode and the
67 * index of the instruction in the method's instructions.
69 * @param node
70 * @param instr
71 * @return
72 * A NormalStatement representing the instruction, or <code>null</code> if the
73 * instruction was not found in the method.
75 public static NormalStatement findStatementFromSSAInstruction(CGNode node, SSAInstruction instr) {
76 SSAInstruction instrs[] = node.getIR().getInstructions();
77 for ( int i = 0; i < instrs.length; i++ ) {
78 if ( instrs[i] == instr )
79 return new NormalStatement(node, i);
81 return null;
84 /**
85 * Given a CGNode and a line number, does a brute force search for statements in the CGNode
86 * which have that line number in the given source code. There's probably a better way to do this.
87 * If not, this should at least be a binary search. Note that you have to have the CGNode for the
88 * method to do this first off. Returns the FIRST statement only (until I can figure out how
89 * to use Set)
91 * @param node
92 * The method containing statements to look thru.
94 * @param linenumber
95 * The original source line number to look for.
97 public static Statement getStatementFromLineNumber(CGNode node, int linenumber) {
98 SSAInstruction[] instrs = node.getIR().getInstructions();
99 for ( int i = 0; i < instrs.length; i++ ) {
100 if (((ConcreteJavaMethod) node.getMethod()).getLineNumber(i) == linenumber)
101 return new NormalStatement(node, i);
103 return null;
107 * Given a CGNode and a line number, does a brute force search for statements in the CGNode
108 * which have that line number in the given source code. There's probably a better way to do this.
109 * If not, this should at least be a binary search. Note that you have to have the CGNode for the
110 * method to do this first off. Returns all statements.
112 * @param node
113 * The method containing statements to look thru.
115 * @param linenumber
116 * The original source line number to look for.
118 public static ArrayList<Statement> getStatementsFromLineNumber(CGNode node, int linenumber) {
119 ArrayList<Statement> result = new ArrayList<Statement>();
120 SSAInstruction[] instrs = node.getIR().getInstructions();
123 for ( int i = 0; i < instrs.length; i++ ) {
124 SourcePosition pos;
126 // FIXME: may fail for Shrike instrs on multiple lines
127 // if (instrs[i] != null && ((pos = getSourcePositionOfInstructionIndex(node,i)) != null))
128 // System.out.println("looking for "+linenumber+" it's not " +pos.lineStart);
129 // else if (instrs[i] != null)
130 // System.out.println("couldn't get pos!");
132 if (instrs[i] != null && ((pos = getSourcePositionOfInstructionIndex(node,i)) != null) && linenumber == pos.lineStart ) {
133 // FIXME: instructions like conditional branches, switches, and gotos seem to encompass many lines, ... adding seeds we don't really want.
134 // for a conditional, we probably want the first line. for other things, this may not work as well.... analyze elements, not statements?
135 // linenumber >= pos.lineStart && linenumber <= pos.lineEnd
136 result.add(new NormalStatement(node, i));
138 // NOTE: instrs[i] could be null if the instrcution
139 // has been optimized out. this could be useful later on.
143 return result;
149 // used only to represent start and end lines for a Shrike method.
150 private static class ShrikeBTMethodPosition implements Position {
151 private int firstline;
152 private int lastline;
154 public ShrikeBTMethodPosition(int firstline, int lastline) {
155 this.firstline = firstline;
156 this.lastline = lastline;
159 @Override
160 public int getFirstCol() {
161 return -1;
164 @Override
165 public int getFirstLine() {
166 return firstline;
169 @Override
170 public InputStream getInputStream() throws IOException {
171 return null;
174 @Override
175 public int getLastCol() {
176 return -1;
179 @Override
180 public int getLastLine() {
181 return lastline;
184 @Override
185 public URL getURL() {
186 return null;
189 @Override
190 public int compareTo(Object o) {
191 return 0;
194 @Override
195 public int getFirstOffset() {
196 return -1;
199 @Override
200 public int getLastOffset() {
201 return -1;
206 * Find lexically first and last instruction in the method and return a Position
207 * with their line numbers.
209 public static Position getSourcePosition(ShrikeBTMethod met) {
210 try {
211 int firstline = met.getLineNumber(met.getBytecodeIndex(0));
212 int lastinstr = met.getInstructions().length - 1;
213 int lastline = met.getLineNumber(met.getBytecodeIndex(lastinstr));
214 return new ShrikeBTMethodPosition(firstline,lastline);
215 } catch (InvalidClassFileException e) {
216 e.printStackTrace();
218 return null;
222 * Return a Position representing the position of the method in the original source file.
223 * In the case of Shrike methods, these will be the line numbers of the first and last
224 * instruction that show up in the IR of the method.
226 * @param cgn
227 * The CGNode of the method.
228 * @return
229 * The position, or <code>null</code> it the method is neither a ConcreteJavaMethod or ShrikeBTMethod.
231 public static Position getSourcePosition(CGNode cgn) {
232 IMethod met = cgn.getMethod();
233 if ( met instanceof ConcreteJavaMethod ) {
234 return ((ConcreteJavaMethod) met).getSourcePosition();
235 } else if ( met instanceof ShrikeBTMethod ) {
236 return getSourcePosition((ShrikeBTMethod)met);
237 } else
238 return null;
242 * Gets as much information as possible about where in the original source a
243 * particular IR instruction is in the method. For a method originating from CAst,
244 * this will be start line and column information, as well as an absolute path.
245 * For a method originating from Shrike, this will be a line number (first line and
246 * last line are equal), and the name of the class (the filename must then be looked
247 * up from the class.)
249 * @param cgn
250 * @param instructionIndex
251 * @return
253 public static SourcePosition getSourcePositionOfInstructionIndex(CGNode cgn, int instructionIndex) {
254 IMethod met = cgn.getMethod();
255 if ( met instanceof ConcreteJavaMethod ) {
256 Position pos = ((ConcreteJavaMethod) met).getSourcePosition(instructionIndex);
257 return new SourcePosition(pos.getFirstLine(),pos.getLastLine(),
258 pos.getFirstOffset(),pos.getLastOffset(),
259 met.getDeclaringClass().getSourceFileName(),
260 met.getDeclaringClass().getName().toString() );
261 } else if ( met instanceof ShrikeBTMethod ) {
262 try {
263 int bcIndex = ((ShrikeBTMethod) met).getBytecodeIndex(instructionIndex);
264 int linenum = met.getLineNumber(bcIndex);
265 return new SourcePosition ( linenum, linenum, -1, -1,
266 met.getDeclaringClass().getSourceFileName(), // this will probably be null so pass in the classname
267 met.getDeclaringClass().getName().toString());
268 } catch (InvalidClassFileException e) {
269 e.printStackTrace();
272 return null;