1 package edu
.berkeley
.cs
.bodik
.svelte
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
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
;
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.
34 public class CGNodeUtils
{
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>
41 * The method with code to search through.
43 * The method name, irrespective of class it is defined in, whether it is static, etc.
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
) {
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
);
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.
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
);
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
92 * The method containing statements to look thru.
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
);
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.
113 * The method containing statements to look thru.
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
++ ) {
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.
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
;
160 public int getFirstCol() {
165 public int getFirstLine() {
170 public InputStream
getInputStream() throws IOException
{
175 public int getLastCol() {
180 public int getLastLine() {
185 public URL
getURL() {
190 public int compareTo(Object o
) {
195 public int getFirstOffset() {
200 public int getLastOffset() {
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
) {
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
) {
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.
227 * The CGNode of the method.
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
);
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.)
250 * @param instructionIndex
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
) {
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
) {