Search code examples
javabytecodejavassistcglibbyte-buddy

how to find all methods called in a methods including Lamda?


I would like to list all methods called by a method.

void create() throws MyException {
    System.out.println("TEST");
    of("String").map(String::valueOf).get();
}

In this method i would like to list

  • System.out.println
  • of
  • map
  • String::valueOf
  • get();

I have used to following code , from how to find all methods called in a method?

public class MethodFinder {

  public static void main(String[] args) throws Throwable {
    ClassPool cp = ClassPool.getDefault();
    CtClass ctClass = cp.get("MyClass");
    CtMethod method = ctClass.getDeclaredMethod("getItem1");
    method.instrument(
        new ExprEditor() {
            public void edit(MethodCall m)
                          throws CannotCompileException
            {
                System.out.println(m.getClassName() + "." + m.getMethodName() + " " + m.getSignature());
            }
        });
  }
}

And get all methods except the String::valueOf

It dosn't matter if this is solved by any other framework.


Solution

  • Since String.valueOf is not actually invoked by the method, I’d use a broader term, e.g. talk about referenced methods which includes invoked methods. One way to gather all of them using ASM() is:

    import java.io.IOException;
    import java.util.Optional;
    import org.objectweb.asm.*;
    
    public class FindAllReferencedMethods {
        class Example {
            void create() {
                System.out.println("TEST");
                Optional.of("String").map(String::valueOf).get();
            }
        }
        public static void main(String[] args) throws IOException {
            ClassReader r = new ClassReader(Example.class.getName());
            r.accept(new ClassVisitor(Opcodes.ASM5) {
                @Override
                public MethodVisitor visitMethod(
                       int access, String name, String desc, String sig, String[] ex) {
                    return name.equals("create")? new MethodRefCollector(): null;
                }
    
            }, ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
        }
    
        static void referencedMethod(String owner, String name, String desc) {
            System.out.println(
                Type.getObjectType(owner).getClassName() + "." + name + " " + desc);
        }
    
        static class MethodRefCollector extends MethodVisitor {
            public MethodRefCollector() {
                super(Opcodes.ASM5);
            }
    
            @Override
            public void visitMethodInsn(
                        int opcode, String owner, String name, String desc, boolean itf) {
                referencedMethod(owner, name, desc);
            }
    
            @Override
            public void visitInvokeDynamicInsn(
                        String name, String desc, Handle bsm, Object... bsmArgs) {
                if(bsm.getOwner().equals("java/lang/invoke/LambdaMetafactory")
                && bsm.getDesc().equals(bsm.getName().equals("altMetafactory")?
                                        ALT_SIG: MF_SIG)) {
                    Handle target = (Handle)bsmArgs[1];
                    referencedMethod(target.getOwner(), target.getName(), target.getDesc());
                }
            }
        }
        static String MF_SIG = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;"
            +"Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/"
            +"MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;";
        static String ALT_SIG = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;"
            +"Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;";
    }