diff --git a/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java b/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java
index e03d72c6..d3ca0ae0 100644
--- a/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java
+++ b/src/main/java/gr/gousiosg/javacg/stat/ClassVisitor.java
@@ -28,58 +28,182 @@
package gr.gousiosg.javacg.stat;
-import org.apache.bcel.classfile.Constant;
-import org.apache.bcel.classfile.ConstantPool;
-import org.apache.bcel.classfile.EmptyVisitor;
-import org.apache.bcel.classfile.JavaClass;
-import org.apache.bcel.classfile.Method;
+import org.apache.bcel.classfile.*;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.MethodGen;
+import java.io.PrintStream;
+
/**
* The simplest of class visitors, invokes the method visitor class for each
* method found.
*/
public class ClassVisitor extends EmptyVisitor {
+ private static final String OBJECT = Object.class.getName();
+ private static final String ENUMERATION = Enum.class.getName();
private JavaClass clazz;
private ConstantPoolGen constants;
private String classReferenceFormat;
-
- public ClassVisitor(JavaClass jc) {
+ private PrintStream output;
+ private FormatEnumaration format;
+ private ConstantPool currentConstantPool;
+
+ public ClassVisitor(JavaClass jc, PrintStream outputStream, FormatEnumaration format) {
clazz = jc;
+ this.format = format;
+ this.output = outputStream;
constants = new ConstantPoolGen(clazz.getConstantPool());
- classReferenceFormat = "C:" + clazz.getClassName() + " %s";
+ switch (format) {
+ case TXT:
+ classReferenceFormat = "C:" + clazz.getClassName() + " %s";
+ break;
+ case XML:
+ classReferenceFormat = "%s";
+ break;
+ default:
+ throw new RuntimeException("Unsupported format "+format);
+ }
+
}
public void visitJavaClass(JavaClass jc) {
+ switch (format) {
+ case XML:
+ output.println("");
+ String[] names = jc.getInterfaceNames();
+ if (names != null && names.length>0) {
+ output.println("");
+ for (String name:names) {
+ output.print("");
+ output.print(name);
+ output.println("");
+ }
+ output.println("");
+ }
+ if (OBJECT.equals(jc.getSuperclassName())==false) {
+ output.print("");
+ output.print(jc.getSuperclassName());
+ output.println("");
+ }
+ break;
+ }
jc.getConstantPool().accept(this);
Method[] methods = jc.getMethods();
- for (int i = 0; i < methods.length; i++)
+ for (int i = 0; i < methods.length; i++) {
methods[i].accept(this);
+ }
+ switch (format) {
+ case XML:
+ output.println("");
+ break;
+ }
}
public void visitConstantPool(ConstantPool constantPool) {
- for (int i = 0; i < constantPool.getLength(); i++) {
- Constant constant = constantPool.getConstant(i);
- if (constant == null)
- continue;
- if (constant.getTag() == 7) {
- String referencedClass =
- constantPool.constantToString(constant);
- System.out.println(String.format(classReferenceFormat,
- referencedClass));
- }
+ switch (format) {
+ case TXT:
+ for (int i = 0; i < constantPool.getLength(); i++) {
+ Constant constant = constantPool.getConstant(i);
+ if (constant == null)
+ continue;
+ if (constant.getTag() == 7) {
+ String referencedClass =
+ constantPool.constantToString(constant);
+ output.println(String.format(classReferenceFormat,
+ referencedClass));
+ }
+ }
+ case XML:
+ for (int i = 0; i < constantPool.getLength(); i++) {
+ Constant constant = constantPool.getConstant(i);
+ if (constant == null)
+ continue;
+ if (constant.getTag() == 7) {
+ output.print("");
+ String referencedClass =
+ constantPool.constantToString(constant);
+ output.print(xmlEscapeText(referencedClass));
+ output.println("");
+ }
+ }
}
}
public void visitMethod(Method method) {
MethodGen mg = new MethodGen(method, clazz.getClassName(), constants);
- MethodVisitor visitor = new MethodVisitor(mg, clazz);
+ MethodVisitor visitor = new MethodVisitor(mg, clazz,output,format);
visitor.start();
}
public void start() {
visitJavaClass(clazz);
}
+
+ /**
+ * Encode special charaters for XML
+ * @param t
+ * @return
+ */
+ static String xmlEscapeText(String t) {
+ StringBuilder sb = null;
+ for(int i = 0; i < t.length(); i++){
+ char c = t.charAt(i);
+ switch(c){
+ case '<':
+ if (sb==null) {
+ sb = new StringBuilder(t.length()+10);
+ sb.append(t.substring(0,i));
+ }
+ sb.append("<");
+ break;
+ case '>':
+ if (sb==null) {
+ sb = new StringBuilder(t.length()+10);
+ sb.append(t.substring(0,i));
+ }
+ sb.append(">");
+ break;
+ case '\"':
+ if (sb==null) {
+ sb = new StringBuilder(t.length()+10);
+ sb.append(t.substring(0,i));
+ }
+ sb.append(""");
+ break;
+ case '&':
+ if (sb==null) {
+ sb = new StringBuilder(t.length()+10);
+ sb.append(t.substring(0,i));
+ }
+ sb.append("&");
+ break;
+ case '\'':
+ if (sb==null) {
+ sb = new StringBuilder(t.length()+10);
+ sb.append(t.substring(0,i));
+ }
+ sb.append("'");
+ break;
+ default:
+ if(c>0x7e || c<0x20) {
+ if (sb==null) {
+ sb = new StringBuilder(t.length()+10);
+ sb.append(t.substring(0,i));
+ }
+ sb.append(""+((int)c)+";");
+ }else if (sb != null) {
+ sb.append(c);
+ }
+ }
+ }
+ if (sb==null) {
+ //re-use original string
+ return t;
+ } else {
+ return sb.toString();
+ }
+ }
}
diff --git a/src/main/java/gr/gousiosg/javacg/stat/FormatEnumaration.java b/src/main/java/gr/gousiosg/javacg/stat/FormatEnumaration.java
new file mode 100644
index 00000000..c9afc34a
--- /dev/null
+++ b/src/main/java/gr/gousiosg/javacg/stat/FormatEnumaration.java
@@ -0,0 +1,5 @@
+package gr.gousiosg.javacg.stat;
+
+public enum FormatEnumaration {
+ TXT,XML
+}
diff --git a/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java b/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
index 4d19408d..88af3917 100644
--- a/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
+++ b/src/main/java/gr/gousiosg/javacg/stat/JCallGraph.java
@@ -29,8 +29,12 @@
package gr.gousiosg.javacg.stat;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -46,9 +50,90 @@
public class JCallGraph {
public static void main(String[] args) {
+ FormatEnumaration format = FormatEnumaration.TXT;
+ PrintStream outputStream = System.out;
+ List jars = new ArrayList<>(args.length);
+ if (args.length==0) {
+ printHelp();
+ }
+ for (int i =0;i");
+ outputStream.println("");
+ break;
+ }
+ for (String arg : jars) {
File f = new File(arg);
@@ -68,13 +153,27 @@ public static void main(String[] args) {
continue;
cp = new ClassParser(arg,entry.getName());
- ClassVisitor visitor = new ClassVisitor(cp.parse());
+ ClassVisitor visitor = new ClassVisitor(cp.parse(),outputStream,format);
visitor.start();
}
}
+ switch (format) {
+ case XML:
+ outputStream.println("");
+ break;
+ }
} catch (IOException e) {
System.err.println("Error while processing jar: " + e.getMessage());
e.printStackTrace();
}
}
+
+ private static void printHelp() {
+ System.out.println("Constructs a callgraph out of a JAR archive. Can combine multiple archives into a single call graph");
+ System.out.println("See https://github.com/gousiosg/java-callgraph for more details");
+ System.out.println("Options:");
+ System.out.println("-h, -help, -? This help message");
+ System.out.println("-f, -format Set output format. Possible values 'xml' or 'txt' (default)");
+ System.out.println("-o, -out File for output. Default - to console");
+ }
}
diff --git a/src/main/java/gr/gousiosg/javacg/stat/MethodVisitor.java b/src/main/java/gr/gousiosg/javacg/stat/MethodVisitor.java
index 4dd0821b..9280414c 100644
--- a/src/main/java/gr/gousiosg/javacg/stat/MethodVisitor.java
+++ b/src/main/java/gr/gousiosg/javacg/stat/MethodVisitor.java
@@ -31,6 +31,8 @@
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.*;
+import java.io.PrintStream;
+
/**
* The simplest of method visitors, prints any invoked method
* signature for all method invocations.
@@ -43,22 +45,47 @@ public class MethodVisitor extends EmptyVisitor {
private MethodGen mg;
private ConstantPoolGen cp;
private String format;
+ private PrintStream output;
+ private FormatEnumaration outputFormat;
- public MethodVisitor(MethodGen m, JavaClass jc) {
+ public MethodVisitor(MethodGen m, JavaClass jc, PrintStream output, FormatEnumaration format) {
visitedClass = jc;
mg = m;
cp = mg.getConstantPool();
- format = "M:" + visitedClass.getClassName() + ":" + mg.getName() + "(" + argumentList(mg.getArgumentTypes()) + ")"
- + " " + "(%s)%s:%s(%s)";
+ this.outputFormat = format;
+ this.output = output;
+ switch (outputFormat) {
+ case TXT:
+ this.format = "M:" + visitedClass.getClassName() + ":" + mg.getName() + "(" + argumentList(mg.getArgumentTypes(),false) + ")"
+ + " " + "(%s)%s:%s(%s)";
+ break;
+ case XML:
+ this.format = "";
+ break;
+ default:
+ throw new RuntimeException("Unsupported format "+outputFormat);
+ }
}
- private String argumentList(Type[] arguments) {
+ private String argumentList(Type[] arguments,boolean flat) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < arguments.length; i++) {
- if (i != 0) {
- sb.append(",");
+ if (flat || outputFormat==FormatEnumaration.TXT) {
+ if (i != 0) {
+ sb.append(",");
+ }
+ sb.append(arguments[i].toString());
+ } else {
+ switch (outputFormat) {
+ case XML:
+ sb.append("");
+ sb.append(arguments[i].getSignature());
+ sb.append("");
+ break;
+ default:
+ throw new RuntimeException("Unsupported format "+outputFormat);
+ }
}
- sb.append(arguments[i].toString());
}
return sb.toString();
}
@@ -66,13 +93,35 @@ private String argumentList(Type[] arguments) {
public void start() {
if (mg.isAbstract() || mg.isNative())
return;
- for (InstructionHandle ih = mg.getInstructionList().getStart();
+ switch (outputFormat) {
+ case XML:
+ output.print("");
+ break;
+ }
+ for (InstructionHandle ih = mg.getInstructionList().getStart();
ih != null; ih = ih.getNext()) {
Instruction i = ih.getInstruction();
-
+// if (outputFormat== FormatEnumaration.XML && i instanceof ConstantPushInstruction) {
+// ConstantPushInstruction pushInstruction = (ConstantPushInstruction)i;
+// output.println("");
+// output.println(pushInstruction.getValue());
+// output.println("");
+// }
if (!visitInstruction(i))
i.accept(this);
}
+ switch (outputFormat) {
+ case XML:
+ output.println("");
+ break;
+ }
+ }
+
+ @Override
+ public void visitLCONST(LCONST obj) {
+ super.visitLCONST(obj);
}
private boolean visitInstruction(Instruction i) {
@@ -82,29 +131,38 @@ private boolean visitInstruction(Instruction i) {
&& !(i instanceof ReturnInstruction));
}
+// @Override
+// public void visitLocalVariableInstruction(LocalVariableInstruction obj) {
+// switch (outputFormat) {
+// case XML:
+// output.println(""+obj.toString(true)+"");
+// break;
+// }
+// }
+
@Override
public void visitINVOKEVIRTUAL(INVOKEVIRTUAL i) {
- System.out.println(String.format(format,"M",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
+ output.println(String.format(format,"M",i.getReferenceType(cp), ClassVisitor.xmlEscapeText(i.getMethodName(cp)),argumentList(i.getArgumentTypes(cp),true)));
}
@Override
public void visitINVOKEINTERFACE(INVOKEINTERFACE i) {
- System.out.println(String.format(format,"I",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
+ output.println(String.format(format,"I",i.getReferenceType(cp),ClassVisitor.xmlEscapeText(i.getMethodName(cp)),argumentList(i.getArgumentTypes(cp),true)));
}
@Override
public void visitINVOKESPECIAL(INVOKESPECIAL i) {
- System.out.println(String.format(format,"O",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
+ output.println(String.format(format,"O",i.getReferenceType(cp),ClassVisitor.xmlEscapeText(i.getMethodName(cp)),argumentList(i.getArgumentTypes(cp),true)));
}
@Override
public void visitINVOKESTATIC(INVOKESTATIC i) {
- System.out.println(String.format(format,"S",i.getReferenceType(cp),i.getMethodName(cp),argumentList(i.getArgumentTypes(cp))));
+ output.println(String.format(format,"S",i.getReferenceType(cp),ClassVisitor.xmlEscapeText(i.getMethodName(cp)),argumentList(i.getArgumentTypes(cp),true)));
}
@Override
public void visitINVOKEDYNAMIC(INVOKEDYNAMIC i) {
- System.out.println(String.format(format,"D",i.getType(cp),i.getMethodName(cp),
- argumentList(i.getArgumentTypes(cp))));
+ output.println(String.format(format,"D",i.getType(cp),ClassVisitor.xmlEscapeText(i.getMethodName(cp)),
+ argumentList(i.getArgumentTypes(cp),true)));
}
}