builder done

This commit is contained in:
Claudio Maggioni 2023-11-15 22:51:30 +01:00
parent 266e5b7b6b
commit d2d364bc71
10 changed files with 160 additions and 90 deletions

6
.idea/jpa-buddy.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JpaBuddyIdeaProjectConfig">
<option name="renamerInitialized" value="true" />
</component>
</project>

View file

@ -7,7 +7,10 @@
<component name="JavaScriptSettings"> <component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" /> <option name="languageLevel" value="ES6" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_10" default="false" project-jdk-name="10" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_10" project-jdk-name="10" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
<component name="ProjectType">
<option name="id" value="jpab" />
</component>
</project> </project>

View file

@ -8,5 +8,6 @@
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="asm-6.2.1" level="project" /> <orderEntry type="library" name="asm-6.2.1" level="project" />
<orderEntry type="library" name="pacman-src" level="application" />
</component> </component>
</module> </module>

View file

@ -6,18 +6,18 @@ Go to [this Lab on iCorsi](https://www.icorsi.ch/course/view.php?id=16963).
## Submission Info ## Submission Info
Property | Value | Property | Value |
------------ | ------------- |------------|----------|
First Name | ... | First Name | Claudio |
Last Name | ... | Last Name | Maggioni |
## Submission Checklist ## Submission Checklist
Please complete this checklist (turn [ ] into [X]) before you submit: Please complete this checklist (turn [ ] into [X]) before you submit:
- [ ] I completed the above Submission Info - [x] I completed the above Submission Info
- [ ] I built the project in IntelliJ (Build > Build Project) - [x] I built the project in IntelliJ (Build > Build Project)
- [ ] I implemented the ClassHierarchyBuilder - [x] I implemented the ClassHierarchyBuilder
- [ ] I implemented the CallGraphBuilder - [ ] I implemented the CallGraphBuilder
- [ ] I implemented the CallGraphRenderer - [ ] I implemented the CallGraphRenderer
- [ ] I wrote the source code myself and did not look at the source code of my classmates - [ ] I wrote the source code myself and did not look at the source code of my classmates

View file

@ -12,7 +12,7 @@ import java.io.IOException;
* java App test-input/pacman-src.jar * java App test-input/pacman-src.jar
* <p> * <p>
* to produce .... * to produce ....
* Afterwards, go to the test-output folder, and call... * Afterward, go to the test-output folder, and call...
* <p> * <p>
* dot -Tpdf -ograph.pdf graph.dot * dot -Tpdf -ograph.pdf graph.dot
* <p> * <p>
@ -32,21 +32,20 @@ public final class App {
// phase 1: build inheritance hierarchy // phase 1: build inheritance hierarchy
final ClassHierarchyBuilder classHierarchyBuilder = new ClassHierarchyBuilder(); final ClassHierarchyBuilder classHierarchyBuilder = new ClassHierarchyBuilder();
scanner.addAnalyzer(classHierarchyBuilder); scanner.addAnalyzer(classHierarchyBuilder);
for (int i=0; i<args.length; i++) { for (String arg : args) {
scanner.scan(args[i]); scanner.scan(arg);
} }
scanner.removeAnalyzer(classHierarchyBuilder); scanner.removeAnalyzer(classHierarchyBuilder);
// phase 2: add call sites and edges // phase 2: add call sites and edges
final CallGraphBuilder callGraphBuilder = new CallGraphBuilder(classHierarchyBuilder.getClassHierarchy()); final CallGraphBuilder callGraphBuilder = new CallGraphBuilder(classHierarchyBuilder.getClassHierarchy());
scanner.addAnalyzer(callGraphBuilder); scanner.addAnalyzer(callGraphBuilder);
for (int i=0; i<args.length; i++) { for (String arg : args) {
scanner.scan(args[i]); scanner.scan(arg);
} }
// dump info about the call graph // dump info about the call graph
new File("test-output").mkdirs(); // create output directory new File("test-output").mkdirs(); // create output directory
new CallGraphRenderer().dumpDot(classHierarchyBuilder.getClassHierarchy(), "test-output/graph.dot"); new CallGraphRenderer().dumpDot(classHierarchyBuilder.getClassHierarchy(), "test-output/graph.dot");
} }
} }

View file

@ -1,18 +1,20 @@
package ch.usi.inf.sp.callgraph; package ch.usi.inf.sp.callgraph;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List; import java.util.List;
import java.util.Objects;
import ch.usi.inf.sp.framework.ClassAnalyzer; import ch.usi.inf.sp.framework.ClassAnalyzer;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.MethodNode;
/** /**
* Build a call graph (as part of the class hierarchy) * Build a call graph (as part of the class hierarchy)
* consisting of CallSite nodes pointing to Method nodes. * consisting of CallSite nodes pointing to Method nodes.
* *
* @author ? * @author maggicl@usi.ch
* @author Matthias.Hauswirth@usi.ch * @author Matthias.Hauswirth@usi.ch
*/ */
public final class CallGraphBuilder implements ClassAnalyzer { public final class CallGraphBuilder implements ClassAnalyzer {
@ -27,14 +29,56 @@ public final class CallGraphBuilder implements ClassAnalyzer {
public void analyze(final String location, final ClassNode classNode) { public void analyze(final String location, final ClassNode classNode) {
try { try {
final ClassType type = hierarchy.getOrCreateClass(classNode.name); final ClassType type = hierarchy.getOrCreateClass(classNode.name);
final List<MethodNode> methodNodes = (List<MethodNode>)classNode.methods; final List<MethodNode> methodNodes = classNode.methods;
for (final MethodNode methodNode : methodNodes) { for (final MethodNode methodNode : methodNodes) {
final Method method = type.getMethod(methodNode.name, methodNode.desc); final Method method = type.getMethod(methodNode.name, methodNode.desc);
Objects.requireNonNull(method, "method is null");
final InsnList instructions = methodNode.instructions; final InsnList instructions = methodNode.instructions;
for (int i = 0; i < instructions.size(); i++) { for (int i = 0; i < instructions.size(); i++) {
final AbstractInsnNode insn = instructions.get(i);
// TODO implement this // 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<ClassType> 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) { } catch (final TypeInconsistencyException ex) {

View file

@ -8,11 +8,20 @@ import java.io.PrintWriter;
/** /**
* Dump out information about the given ClassHierarchy. * Dump out information about the given ClassHierarchy.
* *
* @author ? * @author maggicl@usi.ch
* @author Matthias.Hauswirth@usi.ch * @author Matthias.Hauswirth@usi.ch
*/ */
public final class CallGraphRenderer { 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 { public void dumpDot(final ClassHierarchy hierarchy, final String fileName) throws IOException {
final PrintWriter pw = new PrintWriter(new FileWriter(fileName)); final PrintWriter pw = new PrintWriter(new FileWriter(fileName));
pw.println("digraph CallGraph {"); pw.println("digraph CallGraph {");
@ -21,8 +30,9 @@ public final class CallGraphRenderer {
if (type instanceof ClassType) { 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("}"); pw.println("}");

View file

@ -2,19 +2,19 @@ package ch.usi.inf.sp.callgraph;
import ch.usi.inf.sp.framework.ClassAnalyzer; import ch.usi.inf.sp.framework.ClassAnalyzer;
import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/** /**
* Build a class hierarchy (including methods). * Build a class hierarchy (including methods).
* *
* @author ? * @author maggicl@usi.ch
* @author Matthias.Hauswirth@usi.ch * @author Matthias.Hauswirth@usi.ch
*/ */
public final class ClassHierarchyBuilder implements ClassAnalyzer { public final class ClassHierarchyBuilder implements ClassAnalyzer {
private final ClassHierarchy classHierarchy; private final ClassHierarchy classHierarchy;
public ClassHierarchyBuilder() { public ClassHierarchyBuilder() {
this.classHierarchy = new ClassHierarchy(); this.classHierarchy = new ClassHierarchy();
} }
@ -32,14 +32,25 @@ public final class ClassHierarchyBuilder implements ClassAnalyzer {
} }
classType.setLocation(location); classType.setLocation(location);
// extract modifiers
classType.setModifiers(clazz.access);
// TODO extract modifiers, super class, interfaces, methods // 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(); classType.setResolved();
} catch (final TypeInconsistencyException ex) { } catch (final TypeInconsistencyException ex) {
System.err.println(ex); ex.printStackTrace(System.err);
} }
} }

View file

@ -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 * Set the location (e.g. the name of the JAR file) this class was loaded from
* when you read in the class. * when you read in the class.
* @param location
*/ */
public void setLocation(final String location) { public void setLocation(final String location) {
this.location = 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. * Get the location (e.g. the name of the JAR file) this class was loaded from.
* @return
*/ */
public String getLocation() { public String getLocation() {
return location; return location;
@ -101,7 +99,6 @@ public final class ClassType implements Type {
/** /**
* Set this class (or interface's) super class * Set this class (or interface's) super class
* when you read in the class. * when you read in the class.
* @param superClass
*/ */
public void setSuperClass(final ClassType superClass) { public void setSuperClass(final ClassType superClass) {
this.superClass = superClass; this.superClass = superClass;

View file

@ -87,7 +87,6 @@ public final class Method {
/** /**
* Add a CallSite to this method. * Add a CallSite to this method.
* Usually done only when really needed (e.g. by CallGraphBuilder). * Usually done only when really needed (e.g. by CallGraphBuilder).
* @param callSite
*/ */
public void addCallSite(final CallSite callSite) { public void addCallSite(final CallSite callSite) {
callSites.add(callSite); callSites.add(callSite);