builder done
This commit is contained in:
parent
266e5b7b6b
commit
d2d364bc71
10 changed files with 160 additions and 90 deletions
6
.idea/jpa-buddy.xml
Normal file
6
.idea/jpa-buddy.xml
Normal 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>
|
|
@ -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>
|
|
@ -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>
|
24
README.md
24
README.md
|
@ -6,25 +6,25 @@ 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 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 manually checked that my implementation is correct by doing this:
|
||||||
- [ ] I studied the source code within test-input/pacman-src.jar
|
- [ ] I studied the source code within test-input/pacman-src.jar
|
||||||
- [ ] I ran App to produce the dot file (in test-output/)
|
- [ ] 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 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 manually verified that the PDF contains the correct call graph
|
||||||
- [ ] I committed my changes (at least one commit, but possibly many)
|
- [ ] I committed my changes (at least one commit, but possibly many)
|
||||||
- [ ] I pushed my commits to GitHub
|
- [ ] I pushed my commits to GitHub
|
||||||
|
|
|
@ -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");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +1,89 @@
|
||||||
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 {
|
||||||
|
|
||||||
private final ClassHierarchy hierarchy;
|
private final ClassHierarchy hierarchy;
|
||||||
|
|
||||||
|
|
||||||
public CallGraphBuilder(final ClassHierarchy hierarchy) {
|
public CallGraphBuilder(final ClassHierarchy hierarchy) {
|
||||||
this.hierarchy = hierarchy;
|
this.hierarchy = hierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
final InsnList instructions = methodNode.instructions;
|
Objects.requireNonNull(method, "method is null");
|
||||||
for (int i=0; i<instructions.size(); i++) {
|
final InsnList instructions = methodNode.instructions;
|
||||||
|
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);
|
||||||
} catch (final TypeInconsistencyException ex) {
|
method.addCallSite(callSite);
|
||||||
System.err.println(ex);
|
|
||||||
}
|
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) {
|
||||||
|
System.err.println(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,21 +8,31 @@ 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 {");
|
||||||
pw.println(" rankdir=\"BT\"");
|
pw.println(" rankdir=\"BT\"");
|
||||||
for (final Type type : hierarchy.getTypes()) {
|
for (final Type type : hierarchy.getTypes()) {
|
||||||
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("}");
|
||||||
|
|
|
@ -2,45 +2,56 @@ 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() {
|
||||||
|
this.classHierarchy = new ClassHierarchy();
|
||||||
|
}
|
||||||
|
|
||||||
public ClassHierarchyBuilder() {
|
public ClassHierarchy getClassHierarchy() {
|
||||||
this.classHierarchy = new ClassHierarchy();
|
return classHierarchy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassHierarchy getClassHierarchy() {
|
public void analyze(final String location, final ClassNode clazz) {
|
||||||
return classHierarchy;
|
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);
|
||||||
|
|
||||||
public void analyze(final String location, final ClassNode clazz) {
|
// extract modifiers
|
||||||
try {
|
classType.setModifiers(clazz.access);
|
||||||
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 superclass
|
||||||
|
classType.setSuperClass(classHierarchy.getOrCreateClass(clazz.superName));
|
||||||
|
|
||||||
// TODO extract modifiers, super class, interfaces, methods
|
// 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) {
|
ex.printStackTrace(System.err);
|
||||||
System.err.println(ex);
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Reference in a new issue