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 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