7

My objective is to look at some lines of codes of an external file and count the number of functions of a class are called then.

For example, if I have the following code:

import java.io.BufferedReader;
import whatever.MyClass;
import java.util.ArrayList;
...
...
public void example(){
    InputStreamReader isr = new InputStreamReader (whatever);
    MyClass object = new MyClass();
    someArrayList.add(whatever2)
    someArrayList.add(whatever3)
}

In this case, BufferedReader and MyClass functions were called once, and ArrayList functions were called twice.

My solution for that is get a list of all methods inside the used classes and try to match with some string of my code. For classes created in my project, I can do the following:

jar -tf jarPath

which returns me the list of classes inside a JAR . And doing:

javap -cp jarPath className

I can get a list of all methods inside a JAR whit a specific class name. However, what can I do to get a external methods names, like add(...) of an "external" class java.util.ArrayList? I can't access the .jar file of java.util.ArrayList correct? Anyone have another suggestion to reach the objective?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Lucas Pace
  • 139
  • 9
  • 2
    Why do you need that? What are you trying to do? – Robert Sep 29 '21 at 20:31
  • `javap` by default lists only _public_ methods (and fields if any); to get all use `-private` (or `-p`). It doesn't actually work on a jar; it works on a loaded class, from anywhere the classloader loads from, and although in java 9 up standard libarary classes like `java.util.ArrayList` are no longer in a jar, `javap` still works on them. However, this only tells you what methods (and fields) _exist_; it doesn't tell you how many times they are used or if they are used at all. Note one method name can be and is used in many classes. – dave_thompson_085 Sep 29 '21 at 20:40
  • Does this answer your question? [Java : parse java source code, extract methods](https://stackoverflow.com/questions/2206065/java-parse-java-source-code-extract-methods) – Progman Sep 29 '21 at 20:42
  • @Progman actually it is a next step of my work. I will parse a java file to find the methods but before i need to know where the functions come from – Lucas Pace Sep 29 '21 at 21:33
  • @user16320675 i'm analyzing a "external" java file, the file doenst belong to my project. To use that i need to import all libraries of "external" java file and run that code. I understand correctly? I will try if this is viable – Lucas Pace Sep 29 '21 at 21:37

2 Answers2

4

The compiler doesn't put the imports into the object file. It throws them away. Import is just a shorthand to the compiler.(Imports are a compile-time feature ).

first step :

use Qdox https://github.com/paul-hammant/qdox to get all the imports in a class :

String fileFullPath = "Your\\java\\ file \\full\\path";
JavaDocBuilder builder = new JavaDocBuilder();
builder.addSource(new FileReader( fileFullPath  ));

JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();

for ( String imp : imports )
{
    System.out.println(imp);
}

second step : inspire from that code , loop through your imports (String array) and apply the same code and you will get the methods .

 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;

  public class Tes {
  public static void main(String[] args) {
    Class c;
    try {
        c = Class.forName("java.util.ArrayList");
        Arrays.stream(getAccessibleMethods(c)).
                              forEach(System.out::println);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

public static Method[] getAccessibleMethods(Class clazz) {
    List<Method> result = new ArrayList<Method>();
    while (clazz != null) {
        for (Method method : clazz.getDeclaredMethods()) {
            result.add(method);
        }
        clazz = clazz.getSuperclass();
    }
    return result.toArray(new Method[result.size()]);
}
}

Output :

  public void java.util.ArrayList.add(int,java.lang.Object)
  public boolean java.util.ArrayList.add(java.lang.Object)
  public boolean java.util.ArrayList.remove(java.lang.Object)
  public java.lang.Object java.util.ArrayList.remove(int)
  public java.lang.Object java.util.ArrayList.get(int)
  public java.lang.Object java.util.ArrayList.clone()
  public int java.util.ArrayList.indexOf(java.lang.Object)
  public void java.util.ArrayList.clear()
  .
  .
  .

All the code - one class :

  import java.io.FileNotFoundException;
  import java.io.FileReader;
  import java.lang.reflect.Method;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;

  import com.thoughtworks.qdox.JavaDocBuilder;
  import com.thoughtworks.qdox.model.JavaSource;

  public class Tester {
  public static void main(String[] args) {
  // put your .java file path
  // CyclicB is a class within another project in my pc
    String fileFullPath =
      "C:\\Users\\OUSSEMA\\Desktop\\dev\\OCP_Preparation\\src\\w\\CyclicB.java";
JavaDocBuilder builder = new JavaDocBuilder();
try {
    builder.addSource(new FileReader( fileFullPath  ));
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

JavaSource src = builder.getSources()[0];
String[] imports = src.getImports();

for ( String imp : imports )
{
    Class c;
    try {
        c = Class.forName(imp);
        Arrays.stream(getAccessibleMethods(c)).
                              forEach(System.out::println);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
 }
 }
  public static Method[] getAccessibleMethods(Class clazz) {
  List<Method> result = new ArrayList<Method>();
  while (clazz != null) {
    for (Method method : clazz.getDeclaredMethods()) {
        result.add(method);
    }
    clazz = clazz.getSuperclass();
  }
return result.toArray(new Method[result.size()]);
}
}

Output all the methods within the classes imported in the file CyclicB.java :

  private void java.lang.Throwable.printStackTrace(java.lang.Throwable$PrintStreamOrWriter)
  public void java.lang.Throwable.printStackTrace(java.io.PrintStream)
  public void java.lang.Throwable.printStackTrace()
  public void java.lang.Throwable.printStackTrace(java.io.PrintWriter)
  public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace()
  .
  .
  .
Oussama ZAGHDOUD
  • 1,767
  • 5
  • 15
  • Thanks for the answer. I would like to analyze a file external to my project. I wouldn't necessarily have the imports in my current project. That way I would have to inject this second code into the file I'm analyzing at runtime and run it. Any idea how to do this? – Lucas Pace Sep 30 '21 at 17:12
  • Change the File path in the first step with your java file path , and it will work as expected . You can print all methods from external projects ( put the path of any java file ) , i have tested the code and it works fine . i will put the example in another answer. Thanks – Oussama ZAGHDOUD Sep 30 '21 at 19:29
  • Answer updated , hope that is what you need – Oussama ZAGHDOUD Sep 30 '21 at 19:41
  • sorry but i still having a some problem. If CyclicB has a a import like `com.google.api-client` or any other external library i cant get the methods i only can get the methods if `com.google.api-client` was included on my project – Lucas Pace Oct 01 '21 at 05:10
  • If the external library is a Jar file for example , you cant access the source code so i think that you can't know about imports , you need to use a decompiler ( Jar file can contains source java code and bytecode files ), after decompiling your Jar , try to find the file that you are looking for ( the file mentionned in the import ) and use the answer .Decompiling jar to java files can be special and depends on your OS and the tool used , i think that you can find a switable solution ( using command ligne from the same java program will be a great choice ) . – Oussama ZAGHDOUD Oct 04 '21 at 11:13
  • and if projects with use some dependency management like maven? i still can get the Jar file? – Lucas Pace Oct 05 '21 at 15:10
  • maven will download the Jar from internet , just you should know the path where the Jar will be downloaded within your disk , by default maven put jars in m2 directory within the user home but you can change this default location from the pom xml file . – Oussama ZAGHDOUD Oct 05 '21 at 15:25
  • is there any way to extract the jar independent of the dependency manager? – Lucas Pace Oct 07 '21 at 17:16
  • I didnt understand the question but i hope that this is what are you looking for by extracting the Jar : http://www.devx.com/tips/Tip/22124 – Oussama ZAGHDOUD Oct 08 '21 at 08:40
0

You may look into OpenJDK project that has a Java compiler. Learn to build the modified versions. Investigate the syntax analysis layer of this compiler and find where the method calls are handled. Put the logging into these locations and now you only need to build your java file with the modified compiler to get the information about the calls.

The build is complex, but you will likely only need a careful editing in a few files. It is not exactly very low hanging fruit but I think it should be possible to discover these files and make changes in them, and still may be a simpler/cleaner approach than to implement the own Java syntax parser (also doable with JavaCC).

If you also need to track calls from the external libraries, build them with the modified compiler as well and you will have the needed records.

GNU Classpath is another open source project where you can do the similar thing, and it may be easier to build. However, unlike OpenJDK, GNU Classpath java system library is not complete.

This approach may not discover some methods called during reflection. But it would discover that reflection framework methods have been called. If it is a security - related project, the simplest would be to agree that reflection is not allowed. It is uncommon to use reflection in a normal Java application that is not a framework.

Audrius Meškauskas
  • 20,936
  • 12
  • 75
  • 93