commit 266e5b7b6b52e54947c7305195adafb35d3dcf15
Author: github-classroom[bot] <66690702+github-classroom[bot]@users.noreply.github.com>
Date: Wed Nov 15 17:32:20 2023 +0000
Initial commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..752376f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,24 @@
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+test-output/**
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..5c98b42
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,2 @@
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..a55e7a1
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/asm_6_2_1.xml b/.idea/libraries/asm_6_2_1.xml
new file mode 100644
index 0000000..fdbbc4b
--- /dev/null
+++ b/.idea/libraries/asm_6_2_1.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..02008bf
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..5d0d716
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sonarlint/issuestore/1/d/1d626f79e495ba1c9dcf29a43a14e76b4a6095ac b/.idea/sonarlint/issuestore/1/d/1d626f79e495ba1c9dcf29a43a14e76b4a6095ac
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/3/f/3fa6b31f8e29ac7b8e005caf4b878f80c9fc3e4b b/.idea/sonarlint/issuestore/3/f/3fa6b31f8e29ac7b8e005caf4b878f80c9fc3e4b
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/6/d/6d4ba865d940befa40ed49e8650c29be6a5b0682 b/.idea/sonarlint/issuestore/6/d/6d4ba865d940befa40ed49e8650c29be6a5b0682
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d b/.idea/sonarlint/issuestore/8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/a/5/a5cc2925ca8258af241be7e5b0381edf30266302 b/.idea/sonarlint/issuestore/a/5/a5cc2925ca8258af241be7e5b0381edf30266302
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/a/d/ad21bb9fca84ba24aa77efa1907f02adf7e9d563 b/.idea/sonarlint/issuestore/a/d/ad21bb9fca84ba24aa77efa1907f02adf7e9d563
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/sonarlint/issuestore/index.pb b/.idea/sonarlint/issuestore/index.pb
new file mode 100644
index 0000000..1d33be9
--- /dev/null
+++ b/.idea/sonarlint/issuestore/index.pb
@@ -0,0 +1,14 @@
+
+a
+1src/ch/usi/inf/sp/callgraph/CallGraphBuilder.java,a/d/ad21bb9fca84ba24aa77efa1907f02adf7e9d563
+f
+6src/ch/usi/inf/sp/callgraph/ClassHierarchyBuilder.java,3/f/3fa6b31f8e29ac7b8e005caf4b878f80c9fc3e4b
+:
+
+.gitignore,a/5/a5cc2925ca8258af241be7e5b0381edf30266302
+b
+2src/ch/usi/inf/sp/callgraph/CallGraphRenderer.java,1/d/1d626f79e495ba1c9dcf29a43a14e76b4a6095ac
+9
+ README.md,8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d
+T
+$src/ch/usi/inf/sp/callgraph/App.java,6/d/6d4ba865d940befa40ed49e8650c29be6a5b0682
\ No newline at end of file
diff --git a/.idea/starter-lab-05-call-graph.iml b/.idea/starter-lab-05-call-graph.iml
new file mode 100644
index 0000000..5f84d43
--- /dev/null
+++ b/.idea/starter-lab-05-call-graph.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..e96534f
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..8306744
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c640d3b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,30 @@
+# Lab 5 - Software Peformance 2023
+
+This is Lab 5 of the **Software Performance** course at USI.
+
+Go to [this Lab on iCorsi](https://www.icorsi.ch/course/view.php?id=16963).
+
+## Submission Info
+
+Property | Value
+------------ | -------------
+First Name | ...
+Last Name | ...
+
+## 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
+- [ ] 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 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 committed my changes (at least one commit, but possibly many)
+- [ ] I pushed my commits to GitHub
diff --git a/lib/apiguardian-api-1.0.0.jar b/lib/apiguardian-api-1.0.0.jar
new file mode 100644
index 0000000..6cbff70
Binary files /dev/null and b/lib/apiguardian-api-1.0.0.jar differ
diff --git a/lib/asm-6.2.1-javadoc.jar b/lib/asm-6.2.1-javadoc.jar
new file mode 100644
index 0000000..2bb31c4
Binary files /dev/null and b/lib/asm-6.2.1-javadoc.jar differ
diff --git a/lib/asm-6.2.1-sources.jar b/lib/asm-6.2.1-sources.jar
new file mode 100644
index 0000000..669d99a
Binary files /dev/null and b/lib/asm-6.2.1-sources.jar differ
diff --git a/lib/asm-6.2.1.jar b/lib/asm-6.2.1.jar
new file mode 100644
index 0000000..cc00792
Binary files /dev/null and b/lib/asm-6.2.1.jar differ
diff --git a/lib/asm-tree-6.2.1-javadoc.jar b/lib/asm-tree-6.2.1-javadoc.jar
new file mode 100644
index 0000000..067a8f0
Binary files /dev/null and b/lib/asm-tree-6.2.1-javadoc.jar differ
diff --git a/lib/asm-tree-6.2.1-sources.jar b/lib/asm-tree-6.2.1-sources.jar
new file mode 100644
index 0000000..70e1273
Binary files /dev/null and b/lib/asm-tree-6.2.1-sources.jar differ
diff --git a/lib/asm-tree-6.2.1.jar b/lib/asm-tree-6.2.1.jar
new file mode 100644
index 0000000..9b17711
Binary files /dev/null and b/lib/asm-tree-6.2.1.jar differ
diff --git a/lib/asm-util-6.2.1-javadoc.jar b/lib/asm-util-6.2.1-javadoc.jar
new file mode 100644
index 0000000..a350e55
Binary files /dev/null and b/lib/asm-util-6.2.1-javadoc.jar differ
diff --git a/lib/asm-util-6.2.1-sources.jar b/lib/asm-util-6.2.1-sources.jar
new file mode 100644
index 0000000..1883b76
Binary files /dev/null and b/lib/asm-util-6.2.1-sources.jar differ
diff --git a/lib/asm-util-6.2.1.jar b/lib/asm-util-6.2.1.jar
new file mode 100644
index 0000000..4930a39
Binary files /dev/null and b/lib/asm-util-6.2.1.jar differ
diff --git a/lib/junit-jupiter-api-5.0.0.jar b/lib/junit-jupiter-api-5.0.0.jar
new file mode 100644
index 0000000..5e0d9ce
Binary files /dev/null and b/lib/junit-jupiter-api-5.0.0.jar differ
diff --git a/lib/junit-platform-commons-1.0.0.jar b/lib/junit-platform-commons-1.0.0.jar
new file mode 100644
index 0000000..3210003
Binary files /dev/null and b/lib/junit-platform-commons-1.0.0.jar differ
diff --git a/lib/opentest4j-1.0.0.jar b/lib/opentest4j-1.0.0.jar
new file mode 100644
index 0000000..1b5f425
Binary files /dev/null and b/lib/opentest4j-1.0.0.jar differ
diff --git a/src/ch/usi/inf/sp/callgraph/App.java b/src/ch/usi/inf/sp/callgraph/App.java
new file mode 100644
index 0000000..51a0080
--- /dev/null
+++ b/src/ch/usi/inf/sp/callgraph/App.java
@@ -0,0 +1,52 @@
+package ch.usi.inf.sp.callgraph;
+
+import ch.usi.inf.sp.framework.ArchiveScanner;
+
+import java.io.File;
+import java.io.IOException;
+
+
+/**
+ * Invoke like this...
+ *
+ * java App test-input/pacman-src.jar
+ *
+ * to produce ....
+ * Afterwards, go to the test-output folder, and call...
+ *
+ * dot -Tpdf -ograph.pdf graph.dot
+ *
+ * ...to produce a file graph.pdf of the call graph.
+ *
+ * MAKE SURE YOU MANUALLY VERIFY FOR EACH METHOD THAT
+ * THE CALL GRAPH IT'S ACTUALLY CORRECT.
+ */
+public final class App {
+
+ public static void main(final String[] args) throws IOException {
+ for (final String arg : args) {
+ System.out.println(arg);
+ }
+ final ArchiveScanner scanner = new ArchiveScanner();
+
+ // 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 possibleTargetClasses;
+
+
+ /**
+ * Create a CallSite given the info taken from an ASM MethodInsnNode.
+ *
+ * @param opcode from MethodInsnNode.getOpcode()
+ * @param declaredTargetClassName from MethodInsnNode.owner
+ * @param targetMethodName from MethodInsnNode.name
+ * @param targetMethodDescriptor from MethodInsnNode.desc
+ */
+ public CallSite(final int opcode, final String declaredTargetClassName, final String targetMethodName, final String targetMethodDescriptor) {
+ this.opcode = opcode;
+ this.declaredTargetClassName = declaredTargetClassName;
+ this.targetMethodName = targetMethodName;
+ this.targetMethodDescriptor = targetMethodDescriptor;
+ possibleTargetClasses = new HashSet();
+ }
+
+ public int getOpcode() {
+ return opcode;
+ }
+
+ public String getDeclaredTargetClassName() {
+ return declaredTargetClassName;
+ }
+
+ public String getTargetMethodName() {
+ return targetMethodName;
+ }
+
+ public String getTargetMethodDescriptor() {
+ return targetMethodDescriptor;
+ }
+
+ /**
+ * Use this method to add a possible target during Class Hierarchy Analysis.
+ * @param targetClass
+ */
+ public void addPossibleTargetClass(final ClassType targetClass) {
+ possibleTargetClasses.add(targetClass);
+ }
+
+ public Collection getPossibleTargetClasses() {
+ return possibleTargetClasses;
+ }
+
+}
diff --git a/src/ch/usi/inf/sp/callgraph/ClassHierarchy.java b/src/ch/usi/inf/sp/callgraph/ClassHierarchy.java
new file mode 100644
index 0000000..daf4ff2
--- /dev/null
+++ b/src/ch/usi/inf/sp/callgraph/ClassHierarchy.java
@@ -0,0 +1,69 @@
+package ch.usi.inf.sp.callgraph;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+
+/**
+ * The name space containing all known types.
+ *
+ * @author Matthias.Hauswirth@usi.ch
+ */
+public final class ClassHierarchy {
+
+ private HashMap typeByInternalName;
+
+
+ public ClassHierarchy() {
+ typeByInternalName = new HashMap();
+ add(PrimitiveType.BYTE);
+ add(PrimitiveType.SHORT);
+ add(PrimitiveType.CHAR);
+ add(PrimitiveType.INT);
+ add(PrimitiveType.LONG);
+ add(PrimitiveType.FLOAT);
+ add(PrimitiveType.DOUBLE);
+ add(PrimitiveType.BOOLEAN);
+ }
+
+ private final void add(final Type type) {
+ typeByInternalName.put(type.getInternalName(), type);
+ }
+
+ public ClassType getOrCreateClass(final String internalName) throws TypeInconsistencyException {
+ Type type = typeByInternalName.get(internalName);
+ if (type==null) {
+ type = new ClassType(internalName);
+ typeByInternalName.put(internalName, type);
+ } else if (!(type instanceof ClassType)) {
+ throw new TypeInconsistencyException("Expected class, got "+type);
+ }
+ return (ClassType)type;
+ }
+
+ public ArrayType getOrCreateArrayType(final String internalName) throws TypeInconsistencyException {
+ Type type = typeByInternalName.get(internalName);
+ if (type==null) {
+ final ArrayType arrayType = new ArrayType(internalName);
+ typeByInternalName.put(internalName, arrayType);
+ arrayType.resolve(this);
+ type = arrayType;
+ } else if (!(type instanceof ArrayType)) {
+ throw new TypeInconsistencyException("Expected array type, got "+type);
+ }
+ return (ArrayType)type;
+ }
+
+ public PrimitiveType getPrimitiveType(final String internalName) throws TypeInconsistencyException {
+ final Type type = typeByInternalName.get(internalName);
+ if (!(type instanceof PrimitiveType)) {
+ throw new TypeInconsistencyException("Expected primitive type, got "+type);
+ }
+ return (PrimitiveType)type;
+ }
+
+ public Collection getTypes() {
+ return typeByInternalName.values();
+ }
+
+}
diff --git a/src/ch/usi/inf/sp/callgraph/ClassHierarchyBuilder.java b/src/ch/usi/inf/sp/callgraph/ClassHierarchyBuilder.java
new file mode 100644
index 0000000..877c7d2
--- /dev/null
+++ b/src/ch/usi/inf/sp/callgraph/ClassHierarchyBuilder.java
@@ -0,0 +1,46 @@
+package ch.usi.inf.sp.callgraph;
+
+import ch.usi.inf.sp.framework.ClassAnalyzer;
+import org.objectweb.asm.tree.ClassNode;
+
+
+/**
+ * Build a class hierarchy (including methods).
+ *
+ * @author ?
+ * @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
+
+
+
+ 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
new file mode 100644
index 0000000..96b357b
--- /dev/null
+++ b/src/ch/usi/inf/sp/callgraph/ClassType.java
@@ -0,0 +1,210 @@
+package ch.usi.inf.sp.callgraph;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.objectweb.asm.Opcodes;
+
+
+/**
+ * A ClassType represents a class or an interface.
+ *
+ * @author Matthias.Hauswirth@usi.ch
+ */
+public final class ClassType implements Type {
+
+ private final String internalName;
+ private boolean resolved;
+ private String location;
+ private int modifiers;
+ private ClassType superClass;
+ private ArrayList interfaces;
+ private ArrayList methods;
+ private ArrayList subTypes;
+
+ /**
+ * Create a ClassType given an internal name (without "L" prefix or ";" suffix).
+ * @param internalName the internal name of the class,
+ * e.g. "java/lang/Object" (class Object in package java.lang)
+ * or "java/awt/geom/Point2D$Double" (class Double in class Point2D in package java.awt.geom)
+ * or "TypeInDefaultPackage" (class TypeInDefaultPackage in the default package).
+ */
+ public ClassType(final String internalName) {
+ this.internalName = internalName;
+ this.interfaces = new ArrayList();
+ this.methods = new ArrayList();
+ this.subTypes = new ArrayList();
+ }
+
+ public String getInternalName() {
+ return internalName;
+ }
+
+ /**
+ * Returns the simple name of the underlying class as given in the source code.
+ * Returns an empty string if the underlying class is anonymous.
+ * The simple name of an array is the simple name of the component type with "[]" appended.
+ * In particular the simple name of an array whose component type is anonymous is "[]".
+ */
+ public String getSimpleName() {
+ final int dollar = internalName.lastIndexOf('$');
+ if (dollar>-1) {
+ final String n = internalName.substring(dollar);
+ if (n.matches("[0-9]+")) {
+ // anonymous inner classes have numbers as their names
+ return "";
+ } else {
+ // inner/nested class
+ return n;
+ }
+ } else {
+ final int slash = internalName.lastIndexOf('/');
+ if (slash>-1) {
+ // class in package
+ return internalName.substring(slash).replace('/', '.');
+ } else {
+ // class in default package
+ return internalName;
+ }
+ }
+ }
+
+ /**
+ * Do this after you have completed reading this class
+ * (classes that were never read, but referenced by other classes, will appear as not resolved)
+ */
+ public void setResolved() {
+ resolved = true;
+ }
+
+ public boolean isResolved() {
+ return resolved;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Get the location (e.g. the name of the JAR file) this class was loaded from.
+ * @return
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ /**
+ * 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;
+ // automatically maintain subtypes
+ superClass.subTypes.add(this);
+ }
+
+ /**
+ * Get this class (or interface's) super class.
+ */
+ public ClassType getSuperClass() {
+ return superClass;
+ }
+
+ /**
+ * Add interfa to the list of this class (or interface's) interfaces
+ * when you read in the class.
+ * @param interfa The interface implemented by this class, resp. extended by this interface.
+ */
+ public void addInterface(final ClassType interfa) {
+ interfaces.add(interfa);
+ // automatically maintain subtypes
+ interfa.subTypes.add(this);
+ }
+
+ /**
+ * Get all the interfaces implemented by this class resp. extended by this interface.
+ */
+ public Collection getInterfaces() {
+ return interfaces;
+ }
+
+ /**
+ * Get all the currently known subtypes (interfaces and/or classes) of this interface or class.
+ */
+ public Collection getSubTypes() {
+ return subTypes;
+ }
+
+ /**
+ * Add a method to this class
+ * when you read in the clas.
+ * The class should contain all the methods it explicitly declares
+ * (including abstract methods).
+ */
+ public void addMethod(final Method method) {
+ methods.add(method);
+ }
+
+ /**
+ * Get all the methods this class declares.
+ */
+ public Collection getMethods() {
+ return methods;
+ }
+
+ /**
+ * Get the method with the given name and descriptor, if such a method is declared in this class.
+ * @param name e.g. "" or "main"
+ * @param descriptor E.g. "()V" or "([Ljava/lang/String;)V"
+ * @return the Method or null
+ */
+ public Method getMethod(final String name, final String descriptor) {
+ for (final Method method : methods) {
+ if (method.getName().equals(name) && method.getDescriptor().equals(descriptor)) {
+ return method;
+ }
+ }
+ // no such method declared in this class
+ // (one still could be declared in a superclass or interface!)
+ return null;
+ }
+
+ /**
+ * Set the modifiers when you read in the class.
+ * @param modifiers Modifiers coming from ASM's ClassNode.access
+ */
+ public void setModifiers(final int modifiers) {
+ this.modifiers = modifiers;
+ }
+
+ public int getModifiers() {
+ return modifiers;
+ }
+
+ public boolean isInterface() {
+ return (modifiers & Opcodes.ACC_INTERFACE) != 0;
+ }
+
+ public boolean isAbstract() {
+ return (modifiers & Opcodes.ACC_ABSTRACT) != 0;
+ }
+
+ public boolean isFinal() {
+ return (modifiers & Opcodes.ACC_FINAL) != 0;
+ }
+
+ public boolean isEnum() {
+ return (modifiers & Opcodes.ACC_ENUM) != 0;
+ }
+
+ public String toString() {
+ return (isInterface()?"interface ":(isEnum()?"enum ":"class "))+getInternalName();
+ }
+
+}
diff --git a/src/ch/usi/inf/sp/callgraph/Method.java b/src/ch/usi/inf/sp/callgraph/Method.java
new file mode 100644
index 0000000..827bc45
--- /dev/null
+++ b/src/ch/usi/inf/sp/callgraph/Method.java
@@ -0,0 +1,104 @@
+package ch.usi.inf.sp.callgraph;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.objectweb.asm.Opcodes;
+
+
+/**
+ * A method declared in a class or interface. May be abstract.
+ * A method can contain several CallSites
+ * (information about CallSites may or may not be available,
+ * e.g. usually CallSites are added to Method objects only by the CallGraphBuilder).
+ *
+ * @author Matthias.Hauswirth@usi.ch
+ */
+public final class Method {
+
+ private final String declaringClassName;
+ private final String name;
+ private final String descriptor;
+ private final int modifiers;
+ private final ArrayList callSites;
+
+
+ /**
+ *
+ * @param name The name of the method, "" for constructor or instance initializer, "" for static initializer.
+ * @param descriptor The descriptor showing argument and return types
+ * (e.g. "(IJ)V" means the method takes two arguments, an int and a long, and its return type is void)
+ * @param modifiers
+ */
+ public Method(final String declaringClassName, final String name, final String descriptor, final int modifiers) {
+ this.declaringClassName = declaringClassName;
+ this.name = name;
+ this.descriptor = descriptor;
+ this.modifiers = modifiers;
+ this.callSites = new ArrayList();
+ }
+
+ /**
+ * Get the internal name of the class declaring this method.
+ */
+ public String getDeclaringClassName() {
+ return declaringClassName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * Get the modifiers (access flags, ...) as an int, as taken from ASM.
+ */
+ public int getModifiers() {
+ return modifiers;
+ }
+
+ public boolean isStatic() {
+ return (Opcodes.ACC_STATIC & modifiers) != 0;
+ }
+
+ public boolean isPublic() {
+ return (Opcodes.ACC_PUBLIC & modifiers) != 0;
+ }
+
+ public boolean isProtected() {
+ return (Opcodes.ACC_PROTECTED & modifiers) != 0;
+ }
+
+ public boolean isPrivate() {
+ return (Opcodes.ACC_PRIVATE & modifiers) != 0;
+ }
+
+ public boolean isAbstract() {
+ return (Opcodes.ACC_ABSTRACT & modifiers) !=0;
+ }
+
+ public boolean isFinal() {
+ return (Opcodes.ACC_FINAL & modifiers) !=0;
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Get all CallSites in this method
+ * (will return an empty collection if no CallSites were added).
+ */
+ public Collection getCallSites() {
+ return callSites;
+ }
+
+}
diff --git a/src/ch/usi/inf/sp/callgraph/PrimitiveType.java b/src/ch/usi/inf/sp/callgraph/PrimitiveType.java
new file mode 100644
index 0000000..94eac34
--- /dev/null
+++ b/src/ch/usi/inf/sp/callgraph/PrimitiveType.java
@@ -0,0 +1,42 @@
+package ch.usi.inf.sp.callgraph;
+
+
+/**
+ * Represents a primitive type.
+
+ * @author Matthias.Hauswirth@usi.ch
+ */
+public enum PrimitiveType implements Type {
+
+ BYTE("B", "byte"),
+ SHORT("S", "short"),
+ CHAR("C", "char"),
+ INT("I", "int"),
+ LONG("J", "long"),
+ FLOAT("F", "float"),
+ DOUBLE("D", "double"),
+ BOOLEAN("Z", "boolean"),
+ VOID("V", "void");
+
+ private final String internalName;
+ private final String simpleName;
+
+
+ private PrimitiveType(final String internalName, final String simpleName) {
+ this.internalName = internalName;
+ this.simpleName = simpleName;
+ }
+
+ public String getInternalName() {
+ return internalName;
+ }
+
+ public boolean isResolved() {
+ return true;
+ }
+
+ public String getSimpleName() {
+ return simpleName;
+ }
+
+}
diff --git a/src/ch/usi/inf/sp/callgraph/Type.java b/src/ch/usi/inf/sp/callgraph/Type.java
new file mode 100644
index 0000000..857ed72
--- /dev/null
+++ b/src/ch/usi/inf/sp/callgraph/Type.java
@@ -0,0 +1,15 @@
+package ch.usi.inf.sp.callgraph;
+
+
+/**
+ * A (primitive, array, or class) type in Java.
+ *
+ * @author Matthias.Hauswirth@usi.ch
+ */
+public interface Type {
+
+ public String getInternalName();
+ public boolean isResolved();
+ public String getSimpleName();
+
+}
diff --git a/src/ch/usi/inf/sp/callgraph/TypeInconsistencyException.java b/src/ch/usi/inf/sp/callgraph/TypeInconsistencyException.java
new file mode 100644
index 0000000..0500f28
--- /dev/null
+++ b/src/ch/usi/inf/sp/callgraph/TypeInconsistencyException.java
@@ -0,0 +1,15 @@
+package ch.usi.inf.sp.callgraph;
+
+
+/**
+ * Thrown in cases of type inconsistencies while building the ClassHierarchy
+ *
+ * @author Matthias.Hauswirth@usi.ch
+ */
+public class TypeInconsistencyException extends Exception {
+
+ public TypeInconsistencyException(final String message) {
+ super(message);
+ }
+
+}
diff --git a/src/ch/usi/inf/sp/framework/ArchiveScanner.java b/src/ch/usi/inf/sp/framework/ArchiveScanner.java
new file mode 100644
index 0000000..c97c403
--- /dev/null
+++ b/src/ch/usi/inf/sp/framework/ArchiveScanner.java
@@ -0,0 +1,58 @@
+package ch.usi.inf.sp.framework;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+
+
+/**
+ * Scans a JAR archive containing Java class files, uses ASM to load each class,
+ * and for each class invokes Analyzer.analyze() on each registered Analyzer.
+ *
+ * @author Matthias.Hauswirth@usi.ch
+ */
+public final class ArchiveScanner {
+
+ private final ArrayList analyzers;
+
+
+ public ArchiveScanner() {
+ analyzers = new ArrayList();
+ }
+
+ public void addAnalyzer(final ClassAnalyzer analyzer) {
+ analyzers.add(analyzer);
+ }
+
+ public void removeAnalyzer(final ClassAnalyzer analyzer) {
+ analyzers.remove(analyzer);
+ }
+
+ public void scan(final String archiveName) throws IOException {
+ final ZipFile zipFile = new ZipFile(archiveName);
+ final Enumeration extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ final ZipEntry zipEntry = entries.nextElement();
+ if (zipEntry.getName().toLowerCase().endsWith(".class"))
+ analyzeClass(zipFile, zipEntry);
+ }
+ }
+
+ private void analyzeClass(final ZipFile zipFile, final ZipEntry zipEntry) throws IOException {
+ final String location = zipFile.getName();
+ final ClassReader classReader = new ClassReader(zipFile.getInputStream(zipEntry));
+ // create an empty ClassNode (in-memory representation of a class)
+ final ClassNode classNode = new ClassNode();
+ // have the ClassReader read the class file and populate the ClassNode with the corresponding information
+ classReader.accept(classNode, 0);
+ for (final ClassAnalyzer analyzer : analyzers) {
+ analyzer.analyze(location, classNode);
+ }
+ }
+
+}
diff --git a/src/ch/usi/inf/sp/framework/ClassAnalyzer.java b/src/ch/usi/inf/sp/framework/ClassAnalyzer.java
new file mode 100644
index 0000000..080d66d
--- /dev/null
+++ b/src/ch/usi/inf/sp/framework/ClassAnalyzer.java
@@ -0,0 +1,15 @@
+package ch.usi.inf.sp.framework;
+
+import org.objectweb.asm.tree.ClassNode;
+
+
+/**
+ * Implement this interface if you want to be called by an ArchiveScanner.
+ *
+ * @author Matthias.Hauswirth@usi.ch
+ */
+public interface ClassAnalyzer {
+
+ public void analyze(String location, ClassNode clazz);
+
+}
diff --git a/test-input/pacman-src.jar b/test-input/pacman-src.jar
new file mode 100644
index 0000000..949f774
Binary files /dev/null and b/test-input/pacman-src.jar differ