[ACCEPTED]-How can I find all the methods that call a given method in Java?-java
For analyzing bytecode, I would recommend 17 ASM. Given a list of Classes to analyze, a 16 visitor can be made which finds the method 15 calls you're interested in. One implementation 14 which analyses classes in a jar file is 13 below.
Note that ASM uses internalNames with '/' instead 12 of '.' as a separator. Specify the target 11 method as a standard declaration without modifiers.
For example, to 10 list methods that could be calling System.out.println("foo") in 9 the java runtime jar:
java -cp "classes;asm-3.1.jar;asm-commons-3.1.jar" App \
c:/java/jdk/jre/lib/rt.jar \
java/io/PrintStream "void println(String)"
Edit: source and line numbers 8 added: Note that this only indicates the 7 last target method invocation per calling 6 method - the original q only wanted to know 5 which methods. I leave it as an exercise for 4 the reader to show line numbers of the calling 3 method declaration, or the line numbers 2 of every target invocation, depending on 1 what you're actually after. :)
results in:
LogSupport.java:44 com/sun/activation/registries/LogSupport log (Ljava/lang/String;)V
LogSupport.java:50 com/sun/activation/registries/LogSupport log (Ljava/lang/String;Ljava/lang/Throwable;)V
...
Throwable.java:498 java/lang/Throwable printStackTraceAsCause (Ljava/io/PrintStream;[Ljava/lang/StackTraceElement;)V
--
885 methods invoke java/io/PrintStream println (Ljava/lang/String;)V
source:
public class App {
private String targetClass;
private Method targetMethod;
private AppClassVisitor cv;
private ArrayList<Callee> callees = new ArrayList<Callee>();
private static class Callee {
String className;
String methodName;
String methodDesc;
String source;
int line;
public Callee(String cName, String mName, String mDesc, String src, int ln) {
className = cName; methodName = mName; methodDesc = mDesc; source = src; line = ln;
}
}
private class AppMethodVisitor extends MethodAdapter {
boolean callsTarget;
int line;
public AppMethodVisitor() { super(new EmptyVisitor()); }
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(targetClass)
&& name.equals(targetMethod.getName())
&& desc.equals(targetMethod.getDescriptor())) {
callsTarget = true;
}
}
public void visitCode() {
callsTarget = false;
}
public void visitLineNumber(int line, Label start) {
this.line = line;
}
public void visitEnd() {
if (callsTarget)
callees.add(new Callee(cv.className, cv.methodName, cv.methodDesc,
cv.source, line));
}
}
private class AppClassVisitor extends ClassAdapter {
private AppMethodVisitor mv = new AppMethodVisitor();
public String source;
public String className;
public String methodName;
public String methodDesc;
public AppClassVisitor() { super(new EmptyVisitor()); }
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
className = name;
}
public void visitSource(String source, String debug) {
this.source = source;
}
public MethodVisitor visitMethod(int access, String name,
String desc, String signature,
String[] exceptions) {
methodName = name;
methodDesc = desc;
return mv;
}
}
public void findCallingMethodsInJar(String jarPath, String targetClass,
String targetMethodDeclaration) throws Exception {
this.targetClass = targetClass;
this.targetMethod = Method.getMethod(targetMethodDeclaration);
this.cv = new AppClassVisitor();
JarFile jarFile = new JarFile(jarPath);
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (entry.getName().endsWith(".class")) {
InputStream stream = new BufferedInputStream(jarFile.getInputStream(entry), 1024);
ClassReader reader = new ClassReader(stream);
reader.accept(cv, 0);
stream.close();
}
}
}
public static void main( String[] args ) {
try {
App app = new App();
app.findCallingMethodsInJar(args[0], args[1], args[2]);
for (Callee c : app.callees) {
System.out.println(c.source+":"+c.line+" "+c.className+" "+c.methodName+" "+c.methodDesc);
}
System.out.println("--\n"+app.callees.size()+" methods invoke "+
app.targetClass+" "+
app.targetMethod.getName()+" "+app.targetMethod.getDescriptor());
} catch(Exception x) {
x.printStackTrace();
}
}
}
Edit: the original question was edited to 9 indicate a runtime solution was needed - this 8 answer was given before that edit and only 7 indicates how to do it during development.
If 6 you are using Eclipse you can right click 5 the method and choose "Open call hierarchy" to 4 get this information.
Updated after reading 3 comments: Other IDEs support this as well 2 in a similar fashion (at least Netbeans 1 and IntelliJ do)
Annotate the method with @Deprecated ( or 5 tag it with @deprecated ), turn on deprecation 4 warnings, run your compile and see which 3 warnings get triggered.
The run your compile 2 bit can be done either by invoking an external 1 ant process or by using the Java 6 compiler API.
- right click on method
- Go to references and (depending on your requirement)
choose workspace/project/Hierarchy.
This pops up a panel that shows all references 1 to this functions. Eclipse FTW !
In eclipse, highlight the method name and 1 then Ctrl+Shift+G
There isn't a way to do this (programmatically) via 13 the Java reflection libraries - you can't 12 ask a java.lang.reflect.Method "which methods do you call?"
That 11 leaves two other options I can think of:
Static 10 analysis of the source code. I'm sure this 9 is what the Eclipse Java toolset does - you 8 could look at the Eclipse source behind 7 the JDT, and find what it does when you 6 ask Eclipse to "Find References" to 5 a method.
Bytecode analysis. You could inspect 4 the bytecode for calls to the method. I'm 3 not sure what libraries or examples are 2 out there to help with this - but I can't 1 imagine that something doesn't exist.
Yes, most modern IDE:s will let you either 9 search for usages of a method or variable. Alternatively, you 8 could use a debugger and set a trace point 7 on the method entry, printing a stack trace 6 or whatever every time the method is invoked. Finally, you 5 could use some simple shell util to just 4 grep for the method, such as
find . -name '*.java' -exec grep -H methodName {} ;
The only method 3 that will let you find invokations made 2 through some reflection method, though, would 1 be using the debugger.
I made a small example using @Chadwick's 3 one. It's a test that assesses if calls 2 to getDatabaseEngine() are made by methods 1 that implement @Transaction.
/**
* Ensures that methods that call {@link DatabaseProvider#getDatabaseEngine()}
* implement the {@link @Transaction} annotation.
*
* @throws Exception If something occurs while testing.
*/
@Test
public void ensure() throws Exception {
final Method method = Method.getMethod(
DatabaseEngine.class.getCanonicalName() + " getDatabaseEngine()");
final ArrayList<java.lang.reflect.Method> faultyMethods = Lists.newArrayList();
for (Path p : getAllClasses()) {
try (InputStream stream = new BufferedInputStream(Files.newInputStream(p))) {
ClassReader reader = new ClassReader(stream);
reader.accept(new ClassAdapter(new EmptyVisitor()) {
@Override
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
return new MethodAdapter(new EmptyVisitor()) {
@Override
public void visitMethodInsn(int opcode, String owner, String nameCode, String descCode) {
try {
final Class<?> klass = Class.forName(Type.getObjectType(owner).getClassName());
if (DatabaseProvider.class.isAssignableFrom(klass) &&
nameCode.equals(method.getName()) &&
descCode.equals(method.getDescriptor())) {
final java.lang.reflect.Method method = klass.getDeclaredMethod(name,
getParameters(desc).toArray(new Class[]{}));
for (Annotation annotation : method.getDeclaredAnnotations()) {
if (annotation.annotationType().equals(Transaction.class)) {
return;
}
}
faultyMethods.add(method);
}
} catch (Exception e) {
Throwables.propagate(e);
}
}
};
}
}, 0);
}
}
if (!faultyMethods.isEmpty()) {
fail("\n\nThe following methods must implement @Transaction because they're calling getDatabaseEngine().\n\n" + Joiner.on("\n").join
(faultyMethods) + "\n\n");
}
}
/**
* Gets all the classes from target.
*
* @return The list of classes.
* @throws IOException If something occurs while collecting those classes.
*/
private List<Path> getAllClasses() throws IOException {
final ImmutableList.Builder<Path> builder = new ImmutableList.Builder<>();
Files.walkFileTree(Paths.get("target", "classes"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
if (file.getFileName().toString().endsWith(".class")) {
builder.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return builder.build();
}
/**
* Gets the list of parameters given the description.
*
* @param desc The method description.
* @return The list of parameters.
* @throws Exception If something occurs getting the parameters.
*/
private List<Class<?>> getParameters(String desc) throws Exception {
ImmutableList.Builder<Class<?>> obj = new ImmutableList.Builder<>();
for (Type type : Type.getArgumentTypes(desc)) {
obj.add(ClassUtils.getClass(type.getClassName()));
}
return obj.build();
}
1)In eclipse it is ->right click on the 3 method and select open call hierarchy or 2 CLT+ALT+H
2)In jdeveloper it is -> right click on 1 the method and select calls or ALT+SHIFT+H
The closest that I could find was the method 2 described in this StackOverflow questions 1 selected answer.check this out
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.