diff --git a/.idea/jpa-buddy.xml b/.idea/jpa-buddy.xml
new file mode 100644
index 0000000..966d5f5
--- /dev/null
+++ b/.idea/jpa-buddy.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 02008bf..3048ea5 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,7 +7,10 @@
-
+
+
+
+
\ No newline at end of file
diff --git a/.idea/starter-lab-05-call-graph.iml b/.idea/starter-lab-05-call-graph.iml
index 5f84d43..0c9febe 100644
--- a/.idea/starter-lab-05-call-graph.iml
+++ b/.idea/starter-lab-05-call-graph.iml
@@ -8,5 +8,6 @@
+
\ No newline at end of file
diff --git a/README.md b/README.md
index c640d3b..ce86038 100644
--- a/README.md
+++ b/README.md
@@ -6,25 +6,25 @@ Go to [this Lab on iCorsi](https://www.icorsi.ch/course/view.php?id=16963).
## Submission Info
-Property | Value
------------- | -------------
-First Name | ...
-Last Name | ...
+| Property | Value |
+|------------|----------|
+| First Name | Claudio |
+| Last Name | Maggioni |
## Submission Checklist
Please complete this checklist (turn [ ] into [X]) before you submit:
-- [ ] I completed the above Submission Info
-- [ ] I built the project in IntelliJ (Build > Build Project)
-- [ ] I implemented the ClassHierarchyBuilder
+- [x] I completed the above Submission Info
+- [x] I built the project in IntelliJ (Build > Build Project)
+- [x] I implemented the ClassHierarchyBuilder
- [ ] I implemented the CallGraphBuilder
- [ ] I implemented the CallGraphRenderer
-- [ ] I wrote the source code myself and did not look at the source code of my class mates
+- [ ] I wrote the source code myself and did not look at the source code of my classmates
- [ ] I manually checked that my implementation is correct by doing this:
- - [ ] I studied the source code within test-input/pacman-src.jar
- - [ ] I ran App to produce the dot file (in test-output/)
- - [ ] I ran dot to turn the dot file in test-output into a PDF
- - [ ] I manually verified that the PDF contains the correct call graph
+ - [ ] I studied the source code within test-input/pacman-src.jar
+ - [ ] I ran App to produce the dot file (in test-output/)
+ - [ ] I ran dot to turn the dot file in test-output into a PDF
+ - [ ] I manually verified that the PDF contains the correct call graph
- [ ] I committed my changes (at least one commit, but possibly many)
- [ ] I pushed my commits to GitHub
diff --git a/src/ch/usi/inf/sp/callgraph/App.java b/src/ch/usi/inf/sp/callgraph/App.java
index 51a0080..5f7f210 100644
--- a/src/ch/usi/inf/sp/callgraph/App.java
+++ b/src/ch/usi/inf/sp/callgraph/App.java
@@ -12,7 +12,7 @@ import java.io.IOException;
* java App test-input/pacman-src.jar
*
* to produce ....
- * Afterwards, go to the test-output folder, and call...
+ * Afterward, go to the test-output folder, and call...
*
* dot -Tpdf -ograph.pdf graph.dot
*
@@ -32,21 +32,20 @@ public final class App {
// phase 1: build inheritance hierarchy
final ClassHierarchyBuilder classHierarchyBuilder = new ClassHierarchyBuilder();
scanner.addAnalyzer(classHierarchyBuilder);
- for (int i=0; i methodNodes = (List)classNode.methods;
- for (final MethodNode methodNode : methodNodes) {
- final Method method = type.getMethod(methodNode.name, methodNode.desc);
- final InsnList instructions = methodNode.instructions;
- for (int i=0; i methodNodes = classNode.methods;
+ for (final MethodNode methodNode : methodNodes) {
+ final Method method = type.getMethod(methodNode.name, methodNode.desc);
+ Objects.requireNonNull(method, "method is null");
+ final InsnList instructions = methodNode.instructions;
+ for (int i = 0; i < instructions.size(); i++) {
+ final AbstractInsnNode insn = instructions.get(i);
+
+ // invokedynamic deliberately ignored
+ if (insn.getType() == AbstractInsnNode.METHOD_INSN) {
+ final MethodInsnNode methodInsn = (MethodInsnNode) insn;
+ final int opCode = methodInsn.getOpcode();
+
+ final CallSite callSite = new CallSite(insn.getOpcode(), methodInsn.owner, methodInsn.name,
+ methodInsn.desc);
+ method.addCallSite(callSite);
+
+ final String targetClazz = callSite.getDeclaredTargetClassName();
+ final ClassType clazz = hierarchy.getOrCreateClass(targetClazz);
+ // ignoring if a class is unresolved on purpose, as analysis is constrained on user-defined
+ // classes (i.e. the contents of pacman-src.jar)
+
+ if (opCode == Opcodes.INVOKESTATIC || opCode == Opcodes.INVOKESPECIAL) {
+ // target is static, no fancy search needed
+ callSite.addPossibleTargetClass(clazz); // Cave Johnson, we're done here
+ } else if (opCode == Opcodes.INVOKEDYNAMIC) {
+ final Deque subClasses = new ArrayDeque<>();
+ subClasses.add(clazz);
+
+ // BFS over class and subclasses and add them all as possible targets
+ while (!subClasses.isEmpty()) {
+ final ClassType subClazz = subClasses.pop();
+ callSite.addPossibleTargetClass(subClazz);
+ subClasses.addAll(subClazz.getSubTypes());
+ }
+ } else { // opCode == Opcodes.INVOKEINTERFACE
+ // brute force our way through the type hierarchy to find compatible classes which may
+ // implement the interface
+
+ for (final Type aType : hierarchy.getTypes()) {
+ if (aType instanceof ClassType) {
+ final ClassType aClazz = (ClassType) aType;
+
+ if (aClazz.getInterfaces().contains(clazz)) {
+ callSite.addPossibleTargetClass(aClazz);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } catch (final TypeInconsistencyException ex) {
+ System.err.println(ex);
+ }
+ }
}
diff --git a/src/ch/usi/inf/sp/callgraph/CallGraphRenderer.java b/src/ch/usi/inf/sp/callgraph/CallGraphRenderer.java
index 9c51ee5..cf54513 100644
--- a/src/ch/usi/inf/sp/callgraph/CallGraphRenderer.java
+++ b/src/ch/usi/inf/sp/callgraph/CallGraphRenderer.java
@@ -8,21 +8,31 @@ import java.io.PrintWriter;
/**
* Dump out information about the given ClassHierarchy.
*
- * @author ?
+ * @author maggicl@usi.ch
* @author Matthias.Hauswirth@usi.ch
*/
public final class CallGraphRenderer {
+ private static String nodeName(ClassType type) {
+ return type.getInternalName().replaceAll("/", "_");
+ }
+
+ private static String nodeLabel(ClassType type) {
+ final String name = type.getInternalName().replaceAll("/", ".");
+ return name.substring(1, name.length() - 1);
+ }
+
public void dumpDot(final ClassHierarchy hierarchy, final String fileName) throws IOException {
final PrintWriter pw = new PrintWriter(new FileWriter(fileName));
pw.println("digraph CallGraph {");
pw.println(" rankdir=\"BT\"");
for (final Type type : hierarchy.getTypes()) {
if (type instanceof ClassType) {
- final ClassType classType = (ClassType)type;
+ final ClassType classType = (ClassType) type;
- // TODO implement this
-
+ pw.println(nodeName(classType) + " [shape=record,label=\"" + nodeLabel(classType) + "\"];");
+
+ // TODO: implement this
}
}
pw.println("}");
diff --git a/src/ch/usi/inf/sp/callgraph/ClassHierarchyBuilder.java b/src/ch/usi/inf/sp/callgraph/ClassHierarchyBuilder.java
index 877c7d2..dc5f912 100644
--- a/src/ch/usi/inf/sp/callgraph/ClassHierarchyBuilder.java
+++ b/src/ch/usi/inf/sp/callgraph/ClassHierarchyBuilder.java
@@ -2,45 +2,56 @@ package ch.usi.inf.sp.callgraph;
import ch.usi.inf.sp.framework.ClassAnalyzer;
import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
/**
* Build a class hierarchy (including methods).
- *
- * @author ?
+ *
+ * @author maggicl@usi.ch
* @author Matthias.Hauswirth@usi.ch
*/
public final class ClassHierarchyBuilder implements ClassAnalyzer {
- private final ClassHierarchy classHierarchy;
-
-
- public ClassHierarchyBuilder() {
- this.classHierarchy = new ClassHierarchy();
- }
-
- public ClassHierarchy getClassHierarchy() {
- return classHierarchy;
- }
-
- public void analyze(final String location, final ClassNode clazz) {
- try {
- final ClassType classType = classHierarchy.getOrCreateClass(clazz.name);
- if (classType.isResolved()) {
- System.err.println("WARNING: Class "+classType.getInternalName()+" defined multiple times");
- return;
- }
- classType.setLocation(location);
-
-
- // TODO extract modifiers, super class, interfaces, methods
+ private final ClassHierarchy classHierarchy;
+
+ public ClassHierarchyBuilder() {
+ this.classHierarchy = new ClassHierarchy();
+ }
+
+ public ClassHierarchy getClassHierarchy() {
+ return classHierarchy;
+ }
+
+ public void analyze(final String location, final ClassNode clazz) {
+ try {
+ final ClassType classType = classHierarchy.getOrCreateClass(clazz.name);
+ if (classType.isResolved()) {
+ System.err.println("WARNING: Class " + classType.getInternalName() + " defined multiple times");
+ return;
+ }
+ classType.setLocation(location);
+
+ // extract modifiers
+ classType.setModifiers(clazz.access);
+
+ // extract superclass
+ classType.setSuperClass(classHierarchy.getOrCreateClass(clazz.superName));
+
+ // extract interfaces
+ for (final String i : clazz.interfaces) {
+ classType.addInterface(classHierarchy.getOrCreateClass(i));
+ }
+
+ // extract methods
+ for (final MethodNode m : clazz.methods) {
+ classType.addMethod(new Method(clazz.name, m.name, m.desc, m.access));
+ }
+
+ classType.setResolved();
+ } catch (final TypeInconsistencyException ex) {
+ ex.printStackTrace(System.err);
+ }
+ }
-
-
- classType.setResolved();
- } catch (final TypeInconsistencyException ex) {
- System.err.println(ex);
- }
- }
-
}
diff --git a/src/ch/usi/inf/sp/callgraph/ClassType.java b/src/ch/usi/inf/sp/callgraph/ClassType.java
index 96b357b..9d63525 100644
--- a/src/ch/usi/inf/sp/callgraph/ClassType.java
+++ b/src/ch/usi/inf/sp/callgraph/ClassType.java
@@ -84,7 +84,6 @@ public final class ClassType implements Type {
/**
* Set the location (e.g. the name of the JAR file) this class was loaded from
* when you read in the class.
- * @param location
*/
public void setLocation(final String location) {
this.location = location;
@@ -92,7 +91,6 @@ public final class ClassType implements Type {
/**
* Get the location (e.g. the name of the JAR file) this class was loaded from.
- * @return
*/
public String getLocation() {
return location;
@@ -101,7 +99,6 @@ public final class ClassType implements Type {
/**
* Set this class (or interface's) super class
* when you read in the class.
- * @param superClass
*/
public void setSuperClass(final ClassType superClass) {
this.superClass = superClass;
diff --git a/src/ch/usi/inf/sp/callgraph/Method.java b/src/ch/usi/inf/sp/callgraph/Method.java
index 827bc45..9236822 100644
--- a/src/ch/usi/inf/sp/callgraph/Method.java
+++ b/src/ch/usi/inf/sp/callgraph/Method.java
@@ -87,7 +87,6 @@ public final class Method {
/**
* Add a CallSite to this method.
* Usually done only when really needed (e.g. by CallGraphBuilder).
- * @param callSite
*/
public void addCallSite(final CallSite callSite) {
callSites.add(callSite);