java-callgraph

Convert processing loop to stream

10/23/2018 6:45:17 AM

Details

diff --git a/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java b/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java
index d5dad60..7e6d63d 100644
--- a/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java
+++ b/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java
@@ -36,6 +36,9 @@ import org.apache.bcel.classfile.Method;
 import org.apache.bcel.generic.ConstantPoolGen;
 import org.apache.bcel.generic.MethodGen;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * The simplest of class visitors, invokes the method visitor class for each
  * method found.
@@ -46,7 +49,8 @@ public class ClassVisitor extends EmptyVisitor {
     private ConstantPoolGen constants;
     private String classReferenceFormat;
     private final DynamicCallManager DCManager = new DynamicCallManager();
-    
+    private List<String> methodCalls = new ArrayList<>();
+
     public ClassVisitor(JavaClass jc) {
         clazz = jc;
         constants = new ConstantPoolGen(clazz.getConstantPool());
@@ -73,8 +77,7 @@ public class ClassVisitor extends EmptyVisitor {
             if (constant.getTag() == 7) {
                 String referencedClass = 
                     constantPool.constantToString(constant);
-                System.out.println(String.format(classReferenceFormat,
-                        referencedClass));
+                System.out.println(String.format(classReferenceFormat, referencedClass));
             }
         }
     }
@@ -82,10 +85,15 @@ public class ClassVisitor extends EmptyVisitor {
     public void visitMethod(Method method) {
         MethodGen mg = new MethodGen(method, clazz.getClassName(), constants);
         MethodVisitor visitor = new MethodVisitor(mg, clazz);
-        visitor.start(); 
+        methodCalls.addAll(visitor.start());
     }
 
-    public void start() {
+    public ClassVisitor start() {
         visitJavaClass(clazz);
+        return this;
+    }
+
+    public List<String> methodCalls() {
+        return this.methodCalls;
     }
 }
diff --git a/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java b/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
index 4b5422b..cb40dc9 100644
--- a/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
+++ b/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
@@ -28,25 +28,35 @@
 
 package gr.gousiosg.javacg.stat;
 
-import java.io.File;
-import java.io.IOException;
-import java.util.Enumeration;
+import java.io.*;
+import java.util.*;
+import java.util.function.Function;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import org.apache.bcel.classfile.ClassParser;
 
 /**
  * Constructs a callgraph out of a JAR archive. Can combine multiple archives
  * into a single call graph.
- * 
+ *
  * @author Georgios Gousios <gousiosg@gmail.com>
- * 
  */
 public class JCallGraph {
 
     public static void main(String[] args) {
-        ClassParser cp;
+
+        Function<ClassParser, ClassVisitor> getClassVisitor =
+                (ClassParser cp) -> {
+                    try {
+                        return new ClassVisitor(cp.parse());
+                    } catch (IOException e) {
+                        throw new UncheckedIOException(e);
+                    }
+                };
+
         try {
             for (String arg : args) {
 
@@ -57,24 +67,44 @@ public class JCallGraph {
                 }
 
                 try (JarFile jar = new JarFile(f)) {
-                	Enumeration<JarEntry> entries = jar.entries();
-                	while (entries.hasMoreElements()) {
-                		JarEntry entry = entries.nextElement();
-                		if (entry.isDirectory())
-                			continue;
-                		
-                		if (!entry.getName().endsWith(".class"))
-                			continue;
+                    Stream<JarEntry> entries = enumerationAsStream(jar.entries());
+
+                    String methodCalls = entries.
+                            flatMap(e -> {
+                                if (e.isDirectory() || !e.getName().endsWith(".class"))
+                                    return (new ArrayList<String>()).stream();
+
+                                ClassParser cp = new ClassParser(arg, e.getName());
+                                return getClassVisitor.apply(cp).start().methodCalls().stream();
+                            }).
+                            map(s -> s + "\n").
+                            reduce(new StringBuilder(),
+                                    StringBuilder::append,
+                                    StringBuilder::append).toString();
 
-                		cp = new ClassParser(arg,entry.getName());
-                		ClassVisitor visitor = new ClassVisitor(cp.parse());
-                		visitor.start();
-                	}
-				}
+                    BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
+                    log.write(methodCalls);
+                    log.close();
+                }
             }
         } catch (IOException e) {
             System.err.println("Error while processing jar: " + e.getMessage());
             e.printStackTrace();
         }
     }
+
+    public static <T> Stream<T> enumerationAsStream(Enumeration<T> e) {
+        return StreamSupport.stream(
+                Spliterators.spliteratorUnknownSize(
+                        new Iterator<T>() {
+                            public T next() {
+                                return e.nextElement();
+                            }
+
+                            public boolean hasNext() {
+                                return e.hasMoreElements();
+                            }
+                        },
+                        Spliterator.ORDERED), false);
+    }
 }
diff --git a/src/main/java/gr/gousiosg/javacg/stat/MethodVisitor.java b/src/main/java/gr/gousiosg/javacg/stat/MethodVisitor.java
index 4dd0821..2110042 100644
--- a/src/main/java/gr/gousiosg/javacg/stat/MethodVisitor.java
+++ b/src/main/java/gr/gousiosg/javacg/stat/MethodVisitor.java
@@ -31,6 +31,10 @@ package gr.gousiosg.javacg.stat;
 import org.apache.bcel.classfile.JavaClass;
 import org.apache.bcel.generic.*;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
 /**
  * The simplest of method visitors, prints any invoked method
  * signature for all method invocations.
@@ -43,6 +47,7 @@ public class MethodVisitor extends EmptyVisitor {
     private MethodGen mg;
     private ConstantPoolGen cp;
     private String format;
+    private List<String> methodCalls = new ArrayList<>();
 
     public MethodVisitor(MethodGen m, JavaClass jc) {
         visitedClass = jc;
@@ -63,9 +68,10 @@ public class MethodVisitor extends EmptyVisitor {
         return sb.toString();
     }
 
-    public void start() {
+    public List<String> start() {
         if (mg.isAbstract() || mg.isNative())
-            return;
+            return Collections.emptyList();
+
         for (InstructionHandle ih = mg.getInstructionList().getStart(); 
                 ih != null; ih = ih.getNext()) {
             Instruction i = ih.getInstruction();
@@ -73,6 +79,7 @@ public class MethodVisitor extends EmptyVisitor {
             if (!visitInstruction(i))
                 i.accept(this);
         }
+        return methodCalls;
     }
 
     private boolean visitInstruction(Instruction i) {
@@ -84,27 +91,27 @@ public class MethodVisitor extends EmptyVisitor {
 
     @Override
     public void visitINVOKEVIRTUAL(INVOKEVIRTUAL i) {
-        System.out.println(String.format(format,"M",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
+        methodCalls.add(String.format(format,"M",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
     }
 
     @Override
     public void visitINVOKEINTERFACE(INVOKEINTERFACE i) {
-        System.out.println(String.format(format,"I",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
+        methodCalls.add(String.format(format,"I",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
     }
 
     @Override
     public void visitINVOKESPECIAL(INVOKESPECIAL i) {
-        System.out.println(String.format(format,"O",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
+        methodCalls.add(String.format(format,"O",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
     }
 
     @Override
     public void visitINVOKESTATIC(INVOKESTATIC i) {
-        System.out.println(String.format(format,"S",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
+        methodCalls.add(String.format(format,"S",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
     }
 
     @Override
     public void visitINVOKEDYNAMIC(INVOKEDYNAMIC i) {
-        System.out.println(String.format(format,"D",i.getType(cp),i.getMethodName(cp),
+        methodCalls.add(String.format(format,"D",i.getType(cp),i.getMethodName(cp),
                 argumentList(i.getArgumentTypes(cp))));
     }
 }