From d2d364bc71277fee9c50f7a1a188cc1844867e91 Mon Sep 17 00:00:00 2001 From: Claudio Maggioni Date: Wed, 15 Nov 2023 22:51:30 +0100 Subject: [PATCH] builder done --- .idea/jpa-buddy.xml | 6 ++ .idea/misc.xml | 5 +- .idea/starter-lab-05-call-graph.iml | 1 + README.md | 24 ++--- src/ch/usi/inf/sp/callgraph/App.java | 15 ++- .../inf/sp/callgraph/CallGraphBuilder.java | 102 +++++++++++++----- .../inf/sp/callgraph/CallGraphRenderer.java | 18 +++- .../sp/callgraph/ClassHierarchyBuilder.java | 75 +++++++------ src/ch/usi/inf/sp/callgraph/ClassType.java | 3 - src/ch/usi/inf/sp/callgraph/Method.java | 1 - 10 files changed, 160 insertions(+), 90 deletions(-) create mode 100644 .idea/jpa-buddy.xml 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);