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))));
}
}