¿Cómo puedo encontrar todos los métodos que llaman a un método dado en Java?

Necesito obtener una lista de todos los métodos de llamada para un método de interés para mí en Java. ¿Hay alguna herramienta que pueda ayudarme con esto?

Editar: Olvidé mencionar que necesito hacer esto desde un progtwig. Estoy usando Java Pathfinder y quiero ejecutarlo con todos los métodos que llaman mi método de interés.

Para analizar bytecode, recomendaría ASM . Dada una lista de Clases para analizar, se puede crear un visitante que encuentre las llamadas a los métodos que le interesan. A continuación se incluye una implementación que analiza las clases en un archivo jar.

Tenga en cuenta que ASM usa internalNames con ‘/’ en lugar de ‘.’ como un separador Especifique el método de destino como una statement estándar sin modificadores.

Por ejemplo, para enumerar los métodos que podrían estar llamando a System.out.println (“foo”) en el jar de tiempo de ejecución de java:

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)" 

Editar : fuente y números de línea agregados: tenga en cuenta que esto solo indica la última invocación al método de destino por método de llamada: la q original solo quería saber qué métodos. Lo dejo como un ejercicio para que el lector muestre los números de línea de la statement del método de llamada, o los números de línea de cada invocación de destino, dependiendo de lo que realmente está buscando. 🙂

resultados en:

 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 

fuente:

 public class App { private String targetClass; private Method targetMethod; private AppClassVisitor cv; private ArrayList callees = new ArrayList(); 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 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(); } } } 

Editar: la pregunta original fue editada para indicar que se necesitaba una solución en tiempo de ejecución; esta respuesta se dio antes de esa edición y solo indica cómo hacerlo durante el desarrollo.

Si está utilizando Eclipse, puede hacer clic con el botón derecho en el método y seleccionar “Abrir jerarquía de llamadas” para obtener esta información.

Actualizado después de leer los comentarios: otros IDEs también lo soportan de manera similar (al menos Netbeans e IntelliJ lo hacen)

Anota el método con @Deprecated (o etiquétalo con @deprecated), activa las advertencias de degradación, ejecuta tu comstackción y observa qué advertencias se activan.

La ejecución de su bit de comstackción se puede realizar invocando un proceso externo y utilizando la API del comstackdor Java 6 .

  1. haga clic derecho en el método
  2. Ir a referencias y (dependiendo de su requerimiento)
    elige workspace / project / Hierarchy.

Aparece un panel que muestra todas las referencias a estas funciones. Eclipse FTW!

No hay una forma de hacer esto (mediante progtwigción) a través de las bibliotecas de reflexión de Java; no puede solicitar un método java.lang.reflect.Method “¿a qué métodos llama?”

Eso deja otras dos opciones que puedo pensar:

  1. Análisis estático del código fuente. Estoy seguro de que esto es lo que hace el conjunto de herramientas de Eclipse Java: puede ver el origen de Eclipse detrás del JDT y encontrar qué hace cuando le pide a Eclipse que “Encuentre referencias” para un método.

  2. Análisis de bytecode Puede inspeccionar el bytecode para las llamadas al método. No estoy seguro de qué bibliotecas o ejemplos hay disponibles para ayudar con esto, pero no puedo imaginar que algo no exista.

En eclipse, resalta el nombre del método y luego Ctrl + Shift + G

Sí, la mayoría de los IDE modernos te permitirán buscar usos de un método o variable. Alternativamente, puede usar un depurador y establecer un punto de rastreo en la entrada del método, imprimiendo un seguimiento de stack o lo que sea cada vez que se invoca el método. Finalmente, puede usar una utilidad simple de shell para grep para el método, como

 find . -name '*.java' -exec grep -H methodName {} ; 

Sin embargo, el único método que le permitirá encontrar invocaciones hechas a través de algún método de reflexión sería usar el depurador.

1) En eclipse está -> haga clic derecho en el método y seleccione abrir jerarquía de llamadas o CLT+ALT+H

2) En jdeveloper está -> haga clic derecho en el método y seleccione llamadas o ALT+SHIFT+H

Lo más cerca que pude encontrar fue el método descrito en esta respuesta seleccionada StackOverflow preguntas. mira esto

Hice un pequeño ejemplo usando el de @ Chadwick. Es una prueba que evalúa si las llamadas a getDatabaseEngine () se realizan mediante métodos que implementan @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 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 getAllClasses() throws IOException { final ImmutableList.Builder builder = new ImmutableList.Builder<>(); Files.walkFileTree(Paths.get("target", "classes"), new SimpleFileVisitor() { @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> getParameters(String desc) throws Exception { ImmutableList.Builder> obj = new ImmutableList.Builder<>(); for (Type type : Type.getArgumentTypes(desc)) { obj.add(ClassUtils.getClass(type.getClassName())); } return obj.build(); } 

Puede hacer esto con algo en su IDE como “Buscar usos” (que es como se llama en Netbeans y JDeveloper). Un par de cosas a anotar:

  1. Si su método implementa un método de una interfaz o clase base, solo puede saber que su método es POSIBLEMENTE llamado.
  2. Muchos frameworks de Java usan Reflection para llamar a su método (IE Spring, Hibernate, JSF, etc.), así que tenga cuidado con eso.
  3. En la misma nota, su método podría ser llamado por algún marco, reflexivo o no, así que de nuevo tenga cuidado.