Initial commit
This commit is contained in:
commit
266e5b7b6b
|
@ -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/**
|
|
@ -0,0 +1,2 @@
|
|||
# Default ignored files
|
||||
/workspace.xml
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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");
|
||||
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
Binary file not shown.
Loading…
Reference in New Issue