Initial commit

This commit is contained in:
github-classroom[bot] 2023-11-15 17:32:20 +00:00 committed by GitHub
commit 266e5b7b6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 1080 additions and 0 deletions

24
.gitignore vendored Normal file
View file

@ -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/**

2
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
# Default ignored files
/workspace.xml

View file

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View file

@ -0,0 +1,19 @@
<component name="libraryTable">
<library name="asm-6.2.1">
<CLASSES>
<root url="jar://$PROJECT_DIR$/lib/asm-6.2.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/asm-tree-6.2.1.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/asm-util-6.2.1.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$PROJECT_DIR$/lib/asm-6.2.1-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/asm-tree-6.2.1-javadoc.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/asm-util-6.2.1-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$PROJECT_DIR$/lib/asm-6.2.1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/asm-tree-6.2.1-sources.jar!/" />
<root url="jar://$PROJECT_DIR$/lib/asm-util-6.2.1-sources.jar!/" />
</SOURCES>
</library>
</component>

13
.idea/misc.xml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ASMPluginConfiguration">
<asm skipDebug="false" skipFrames="false" skipCode="false" expandFrames="false" />
<groovy codeStyle="LEGACY" />
</component>
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_10" default="false" project-jdk-name="10" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/starter-lab-05-call-graph.iml" filepath="$PROJECT_DIR$/.idea/starter-lab-05-call-graph.iml" />
</modules>
</component>
</project>

View file

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

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="asm-6.2.1" level="project" />
</component>
</module>

124
.idea/uiDesigner.xml Normal file
View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

7
.idea/vcs.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

30
README.md Normal file
View file

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

Binary file not shown.

BIN
lib/asm-6.2.1-javadoc.jar Normal file

Binary file not shown.

BIN
lib/asm-6.2.1-sources.jar Normal file

Binary file not shown.

BIN
lib/asm-6.2.1.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/asm-tree-6.2.1.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/asm-util-6.2.1.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
lib/opentest4j-1.0.0.jar Normal file

Binary file not shown.

View file

@ -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...
* <p>
* java App test-input/pacman-src.jar
* <p>
* to produce ....
* Afterwards, go to the test-output folder, and call...
* <p>
* dot -Tpdf -ograph.pdf graph.dot
* <p>
* ...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<args.length; i++) {
scanner.scan(args[i]);
}
scanner.removeAnalyzer(classHierarchyBuilder);
// phase 2: add call sites and edges
final CallGraphBuilder callGraphBuilder = new CallGraphBuilder(classHierarchyBuilder.getClassHierarchy());
scanner.addAnalyzer(callGraphBuilder);
for (int i=0; i<args.length; i++) {
scanner.scan(args[i]);
}
// dump info about the call graph
new File("test-output").mkdirs(); // create output directory
new CallGraphRenderer().dumpDot(classHierarchyBuilder.getClassHierarchy(), "test-output/graph.dot");
}
}

View file

@ -0,0 +1,54 @@
package ch.usi.inf.sp.callgraph;
/**
* Represents the type of an array.
*
* @author Matthias.Hauswirth@usi.ch
*/
public final class ArrayType implements Type {
private final String internalName;
private boolean resolved;
private Type componentType;
public ArrayType(final String internalName) {
this.internalName = internalName;
}
public String getInternalName() {
return internalName;
}
public boolean isResolved() {
return resolved;
}
public String getSimpleName() {
if (!resolved) {
throw new IllegalStateException("Array type "+internalName+" not resolved yet");
}
return componentType.getSimpleName()+"[]";
}
public void resolve(final ClassHierarchy nameSpace) throws TypeInconsistencyException {
if (internalName.charAt(1)=='[') {
componentType = nameSpace.getOrCreateArrayType(internalName.substring(1));
} else if (internalName.charAt(1)=='L') {
componentType = nameSpace.getOrCreateClass(internalName.substring(2, internalName.length()-1));
} else {
componentType = nameSpace.getPrimitiveType(internalName.substring(1));
}
resolved = true;
}
public static void main(String[] args) throws TypeInconsistencyException {
ClassHierarchy ch = new ClassHierarchy();
ch.getOrCreateArrayType("[[LPacman;");
for (Type t : ch.getTypes()) {
System.out.println(t.getInternalName());
}
}
}

View file

@ -0,0 +1,45 @@
package ch.usi.inf.sp.callgraph;
import java.util.List;
import ch.usi.inf.sp.framework.ClassAnalyzer;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
/**
* Build a call graph (as part of the class hierarchy)
* consisting of CallSite nodes pointing to Method nodes.
*
* @author ?
* @author Matthias.Hauswirth@usi.ch
*/
public final class CallGraphBuilder implements ClassAnalyzer {
private final ClassHierarchy hierarchy;
public CallGraphBuilder(final ClassHierarchy hierarchy) {
this.hierarchy = hierarchy;
}
public void analyze(final String location, final ClassNode classNode) {
try {
final ClassType type = hierarchy.getOrCreateClass(classNode.name);
final List<MethodNode> methodNodes = (List<MethodNode>)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<instructions.size(); i++) {
// TODO implement this
}
}
} catch (final TypeInconsistencyException ex) {
System.err.println(ex);
}
}
}

View file

@ -0,0 +1,32 @@
package ch.usi.inf.sp.callgraph;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Dump out information about the given ClassHierarchy.
*
* @author ?
* @author Matthias.Hauswirth@usi.ch
*/
public final class CallGraphRenderer {
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;
// TODO implement this
}
}
pw.println("}");
pw.close();
}
}

View file

@ -0,0 +1,65 @@
package ch.usi.inf.sp.callgraph;
import java.util.Collection;
import java.util.HashSet;
/**
* A call site represents a call instruction in the body of a method.
*
* @author Matthias.Hauswirth@usi.ch
*/
public final class CallSite {
private final int opcode;
private final String declaredTargetClassName;
private final String targetMethodName;
private final String targetMethodDescriptor;
private final HashSet<ClassType> 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<ClassType>();
}
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<ClassType> getPossibleTargetClasses() {
return possibleTargetClasses;
}
}

View file

@ -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<String, Type> typeByInternalName;
public ClassHierarchy() {
typeByInternalName = new HashMap<String, Type>();
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<Type> getTypes() {
return typeByInternalName.values();
}
}

View file

@ -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);
}
}
}

View file

@ -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<ClassType> interfaces;
private ArrayList<Method> methods;
private ArrayList<ClassType> 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<ClassType>();
this.methods = new ArrayList<Method>();
this.subTypes = new ArrayList<ClassType>();
}
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<ClassType> getInterfaces() {
return interfaces;
}
/**
* Get all the currently known subtypes (interfaces and/or classes) of this interface or class.
*/
public Collection<ClassType> 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<Method> 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. "<init>" 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();
}
}

View file

@ -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<CallSite> callSites;
/**
*
* @param name The name of the method, "<init>" for constructor or instance initializer, "<clinit>" 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<CallSite>();
}
/**
* 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<CallSite> getCallSites() {
return callSites;
}
}

View file

@ -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;
}
}

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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<ClassAnalyzer> analyzers;
public ArchiveScanner() {
analyzers = new ArrayList<ClassAnalyzer>();
}
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);
}
}
}

View file

@ -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);
}

BIN
test-input/pacman-src.jar Normal file

Binary file not shown.