Initial commit
This commit is contained in:
commit
74a433f602
73 changed files with 2719 additions and 0 deletions
2
.idea/.gitignore
vendored
Normal file
2
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Default ignored files
|
||||
/workspace.xml
|
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
19
.idea/libraries/asm_util_7_1.xml
Normal file
19
.idea/libraries/asm_util_7_1.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<component name="libraryTable">
|
||||
<library name="asm-util-7.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-util-7.1.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-tree-7.1.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-7.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-util-7.1-javadoc.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-tree-7.1-javadoc.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-7.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-util-7.1-sources.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-tree-7.1-sources.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-7.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
10
.idea/misc.xml
Normal file
10
.idea/misc.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?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="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="10" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/starter-lab-04-dominator-analysis.iml" filepath="$PROJECT_DIR$/starter-lab-04-dominator-analysis.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
16
.idea/runConfigurations/App.xml
Normal file
16
.idea/runConfigurations/App.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="App" type="Application" factoryName="Application" nameIsGenerated="true">
|
||||
<option name="MAIN_CLASS_NAME" value="ch.usi.inf.sp.cfg.App" />
|
||||
<module name="starter-lab-04-dominator-analysis" />
|
||||
<option name="PROGRAM_PARAMETERS" value="test-input/java10/ExampleClass.class test-output" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="ch.usi.inf.sp.graph.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
16
.idea/runConfigurations/Disassembler.xml
Normal file
16
.idea/runConfigurations/Disassembler.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Disassembler" type="Application" factoryName="Application" nameIsGenerated="true">
|
||||
<option name="MAIN_CLASS_NAME" value="ch.usi.inf.sp.bytecode.Disassembler" />
|
||||
<module name="starter-lab-03-control-flow-graph" />
|
||||
<option name="PROGRAM_PARAMETERS" value="test-input/java10/ExampleClass.class" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="ch.usi.inf.sp.bytecode.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
3
.idea/sonarlint/issuestore/index.pb
Normal file
3
.idea/sonarlint/issuestore/index.pb
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
9
|
||||
README.md,8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d
|
124
.idea/uiDesigner.xml
Normal file
124
.idea/uiDesigner.xml
Normal 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>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
34
README.md
Normal file
34
README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Lab 4 - Software Peformance 2023
|
||||
|
||||
This is Lab 4 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 (re)implemented the ControlFlowGraphBuilder (copy from Lab 3)
|
||||
- [ ] I (re)implemented the ControlFlowGraphRenderer (copy from Lab 3)
|
||||
- [ ] I implemented the Traversal
|
||||
- [ ] I implemented the DominatorAnalyzer
|
||||
- [ ] I wrote the source code myself and did not look at the source code of my class mates
|
||||
- [ ] I ran all the JUnit tests and verified that they all pass
|
||||
- [ ] I manually checked that my implementation is correct by doing this:
|
||||
- [ ] I studied the test-input/ExampleClass.java source code
|
||||
- [ ] I ran App to produce the dot files (in test-output/)
|
||||
- [ ] I ran dot to turn the dot files in test-output into a PDF
|
||||
- [ ] I manually verified that the PDF contains a set of pages per method of ExampleClass
|
||||
- [ ] I manually verified that the CFGs in the PDFs correspond to the methods' source code
|
||||
- [ ] I manually verified that the dominator trees in the PDFs correspond to the method's source code
|
||||
- [ ] I committed my changes (at least one commit, but possibly many)
|
||||
- [ ] I pushed my commits to GitHub
|
BIN
lib/asm-7.1-javadoc.jar
Normal file
BIN
lib/asm-7.1-javadoc.jar
Normal file
Binary file not shown.
BIN
lib/asm-7.1-sources.jar
Normal file
BIN
lib/asm-7.1-sources.jar
Normal file
Binary file not shown.
BIN
lib/asm-7.1.jar
Normal file
BIN
lib/asm-7.1.jar
Normal file
Binary file not shown.
BIN
lib/asm-tree-7.1-javadoc.jar
Normal file
BIN
lib/asm-tree-7.1-javadoc.jar
Normal file
Binary file not shown.
BIN
lib/asm-tree-7.1-sources.jar
Normal file
BIN
lib/asm-tree-7.1-sources.jar
Normal file
Binary file not shown.
BIN
lib/asm-tree-7.1.jar
Normal file
BIN
lib/asm-tree-7.1.jar
Normal file
Binary file not shown.
BIN
lib/asm-util-7.1-javadoc.jar
Normal file
BIN
lib/asm-util-7.1-javadoc.jar
Normal file
Binary file not shown.
BIN
lib/asm-util-7.1-sources.jar
Normal file
BIN
lib/asm-util-7.1-sources.jar
Normal file
Binary file not shown.
BIN
lib/asm-util-7.1.jar
Normal file
BIN
lib/asm-util-7.1.jar
Normal file
Binary file not shown.
265
src/ch/usi/inf/sp/bytecode/Disassembler.java
Normal file
265
src/ch/usi/inf/sp/bytecode/Disassembler.java
Normal file
|
@ -0,0 +1,265 @@
|
|||
package ch.usi.inf.sp.bytecode;
|
||||
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.*;
|
||||
import org.objectweb.asm.util.Printer;
|
||||
|
||||
|
||||
/**
|
||||
* A Disassembler can disassemble Java class files.
|
||||
* It presents an output similar to javap -c.
|
||||
* Given the name of the class file as a command line argument,
|
||||
* it prints the name of the class, a list of all methods,
|
||||
* and for each method, the list of all Java bytecode instructions.
|
||||
* <p>
|
||||
* The format of the disassembled bytecode includes the opcodes
|
||||
* (in the form of mnemonics such as "ILOAD") and all the operands.
|
||||
* Some operands can be printed as simple integers, while others have to be printed in a more understandable form
|
||||
* (e.g. method or field names and descriptors).
|
||||
* Operands of branch instructions are shown as an "id" of the targeted instruction.
|
||||
* For this, all instructions of a method, including ASM's pseudo-instructions (LABEL, LINE, FRAME),
|
||||
* are numbered, starting at 0.
|
||||
* The instruction id allows you to look up the corresponding instruction object in the instruction list:
|
||||
* AbstractInsnNode target = instructionList.get(targetId);
|
||||
* <p>
|
||||
* An example output:
|
||||
*
|
||||
* <pre>
|
||||
* Class: ExampleClass
|
||||
* ...
|
||||
* Method: switchMethod2(I)I
|
||||
* 0: // label
|
||||
* 1: // line number information
|
||||
* 2: ICONST_0
|
||||
* 3: ISTORE 2
|
||||
* 4: // label
|
||||
* 5: // line number information
|
||||
* 6: ILOAD 1
|
||||
* 7: LOOKUPSWITCH 0: 8, 1000: 13, 2000: 18, default: 23
|
||||
* 8: // label
|
||||
* 9: // line number information
|
||||
* 10: ICONST_0
|
||||
* 11: ISTORE 2
|
||||
* 12: GOTO 27
|
||||
* 13: // label
|
||||
* 14: // line number information
|
||||
* 15: ICONST_1
|
||||
* 16: ISTORE 2
|
||||
* 17: GOTO 27
|
||||
* 18: // label
|
||||
* 19: // line number information
|
||||
* 20: ICONST_2
|
||||
* 21: ISTORE 2
|
||||
* 22: GOTO 27
|
||||
* 23: // label
|
||||
* 24: // line number information
|
||||
* 25: ICONST_M1
|
||||
* 26: ISTORE 2
|
||||
* 27: // label
|
||||
* 28: // line number information
|
||||
* 29: ILOAD 2
|
||||
* 30: IRETURN
|
||||
* 31: // label
|
||||
* </pre>
|
||||
*
|
||||
* @author Matthias.Hauswirth@usi.ch
|
||||
*/
|
||||
public class Disassembler {
|
||||
|
||||
public static void main(final String[] args) throws Exception {
|
||||
// create a ClassReader that loads the Java .class file specified as the command line argument
|
||||
final String classFileName = args[0];
|
||||
final ClassReader cr = new ClassReader(new FileInputStream(classFileName));
|
||||
// create an empty ClassNode (in-memory representation of a class)
|
||||
final ClassNode clazz = new ClassNode();
|
||||
// have the ClassReader read the class file and populate the ClassNode with the corresponding information
|
||||
cr.accept(clazz, 0);
|
||||
// create a dumper and have it dump the given ClassNode
|
||||
System.out.println(disassembleClass(clazz));
|
||||
}
|
||||
|
||||
public static String disassembleClass(final ClassNode clazz) {
|
||||
final StringBuffer sb = new StringBuffer("Class: ");
|
||||
sb.append(clazz.name);
|
||||
sb.append('\n');
|
||||
// get the list of all methods in that class
|
||||
final List<MethodNode> methods = clazz.methods;
|
||||
for (int m = 0; m < methods.size(); m++) {
|
||||
final MethodNode method = methods.get(m);
|
||||
sb.append(disassembleMethod(method));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String disassembleMethod(final MethodNode method) {
|
||||
final StringBuffer sb = new StringBuffer(" Method: ");
|
||||
sb.append(method.name);
|
||||
sb.append(method.desc);
|
||||
sb.append('\n');
|
||||
// get the list of all instructions in that method
|
||||
final InsnList instructions = method.instructions;
|
||||
for (int i = 0; i < instructions.size(); i++) {
|
||||
final AbstractInsnNode instruction = instructions.get(i);
|
||||
sb.append(" ");
|
||||
sb.append(Disassembler.disassembleInstruction(instruction, i, instructions));
|
||||
sb.append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hint: Check out org.objectweb.asm.MethodVisitor to determine which instructions (opcodes)
|
||||
* have which instruction types (subclasses of AbstractInsnNode).
|
||||
*
|
||||
* @see org.objectweb.asm.MethodVisitor
|
||||
* <p>
|
||||
* E.g. the comment in org.objectweb.asm.MethodVisitor.visitIntInsn(int opcode, int operand)
|
||||
* shows the list of all opcodes that are represented as instructions of type IntInsnNode.
|
||||
* That list e.g. includes the BIPUSH opcode.
|
||||
*/
|
||||
public static String disassembleInstruction(final AbstractInsnNode instruction, final int i, final InsnList instructions) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
final int opcode = instruction.getOpcode();
|
||||
final String mnemonic = opcode == -1 ? "" : Printer.OPCODES[instruction.getOpcode()];
|
||||
sb.append(i + ":\t" + mnemonic + " ");
|
||||
// There are different subclasses of AbstractInsnNode.
|
||||
// AbstractInsnNode.getType() represents the subclass as an int.
|
||||
// Note:
|
||||
// to check the subclass of an instruction node, we can either use:
|
||||
// if (instruction.getType()==AbstractInsnNode.LABEL)
|
||||
// or we can use:
|
||||
// if (instruction instanceof LabelNode)
|
||||
// They give the same result, but the first one can be used in a switch statement.
|
||||
switch (instruction.getType()) {
|
||||
case AbstractInsnNode.LABEL:
|
||||
// pseudo-instruction (branch or exception target)
|
||||
sb.append("// label");
|
||||
break;
|
||||
case AbstractInsnNode.FRAME:
|
||||
// pseudo-instruction (stack frame map)
|
||||
sb.append("// stack frame map");
|
||||
break;
|
||||
case AbstractInsnNode.LINE:
|
||||
// pseudo-instruction (line number information)
|
||||
sb.append("// line number information");
|
||||
case AbstractInsnNode.INSN:
|
||||
// Opcodes: NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2,
|
||||
// ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0,
|
||||
// FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD,
|
||||
// DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE,
|
||||
// DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP,
|
||||
// DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD, FADD,
|
||||
// DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV,
|
||||
// FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL,
|
||||
// LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR,
|
||||
// I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B,
|
||||
// I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN,
|
||||
// FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW,
|
||||
// MONITORENTER, or MONITOREXIT.
|
||||
// zero operands, nothing to print
|
||||
break;
|
||||
case AbstractInsnNode.INT_INSN:
|
||||
// Opcodes: NEWARRAY, BIPUSH, SIPUSH.
|
||||
if (instruction.getOpcode() == Opcodes.NEWARRAY) {
|
||||
// NEWARRAY
|
||||
sb.append(Printer.TYPES[((IntInsnNode) instruction).operand]);
|
||||
} else {
|
||||
// BIPUSH or SIPUSH
|
||||
sb.append(((IntInsnNode) instruction).operand);
|
||||
}
|
||||
break;
|
||||
case AbstractInsnNode.JUMP_INSN:
|
||||
// Opcodes: IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ,
|
||||
// IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ,
|
||||
// IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
|
||||
{
|
||||
final LabelNode targetInstruction = ((JumpInsnNode) instruction).label;
|
||||
final int targetId = instructions.indexOf(targetInstruction);
|
||||
sb.append(targetId);
|
||||
break;
|
||||
}
|
||||
case AbstractInsnNode.LDC_INSN:
|
||||
// Opcodes: LDC.
|
||||
sb.append(((LdcInsnNode) instruction).cst);
|
||||
break;
|
||||
case AbstractInsnNode.IINC_INSN:
|
||||
// Opcodes: IINC.
|
||||
sb.append(((IincInsnNode) instruction).var);
|
||||
sb.append(" ");
|
||||
sb.append(((IincInsnNode) instruction).incr);
|
||||
break;
|
||||
case AbstractInsnNode.TYPE_INSN:
|
||||
// Opcodes: NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
|
||||
sb.append(((TypeInsnNode) instruction).desc);
|
||||
break;
|
||||
case AbstractInsnNode.VAR_INSN:
|
||||
// Opcodes: ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE,
|
||||
// LSTORE, FSTORE, DSTORE, ASTORE or RET.
|
||||
sb.append(((VarInsnNode) instruction).var);
|
||||
break;
|
||||
case AbstractInsnNode.FIELD_INSN:
|
||||
// Opcodes: GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
|
||||
sb.append(((FieldInsnNode) instruction).owner);
|
||||
sb.append(".");
|
||||
sb.append(((FieldInsnNode) instruction).name);
|
||||
sb.append(" ");
|
||||
sb.append(((FieldInsnNode) instruction).desc);
|
||||
break;
|
||||
case AbstractInsnNode.METHOD_INSN:
|
||||
// Opcodes: INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC,
|
||||
// INVOKEINTERFACE or INVOKEDYNAMIC.
|
||||
sb.append(((MethodInsnNode) instruction).owner);
|
||||
sb.append(".");
|
||||
sb.append(((MethodInsnNode) instruction).name);
|
||||
sb.append(" ");
|
||||
sb.append(((MethodInsnNode) instruction).desc);
|
||||
break;
|
||||
case AbstractInsnNode.MULTIANEWARRAY_INSN:
|
||||
// Opcodes: MULTIANEWARRAY.
|
||||
sb.append(((MultiANewArrayInsnNode) instruction).desc);
|
||||
sb.append(" ");
|
||||
sb.append(((MultiANewArrayInsnNode) instruction).dims);
|
||||
break;
|
||||
case AbstractInsnNode.LOOKUPSWITCH_INSN:
|
||||
// Opcodes: LOOKUPSWITCH.
|
||||
{
|
||||
final List keys = ((LookupSwitchInsnNode) instruction).keys;
|
||||
final List labels = ((LookupSwitchInsnNode) instruction).labels;
|
||||
for (int t = 0; t < keys.size(); t++) {
|
||||
final int key = (Integer) keys.get(t);
|
||||
final LabelNode targetInstruction = (LabelNode) labels.get(t);
|
||||
final int targetId = instructions.indexOf(targetInstruction);
|
||||
sb.append(key + ": " + targetId + ", ");
|
||||
}
|
||||
final LabelNode defaultTargetInstruction = ((LookupSwitchInsnNode) instruction).dflt;
|
||||
final int defaultTargetId = instructions.indexOf(defaultTargetInstruction);
|
||||
sb.append("default: " + defaultTargetId);
|
||||
break;
|
||||
}
|
||||
case AbstractInsnNode.TABLESWITCH_INSN:
|
||||
// Opcodes: TABLESWITCH.
|
||||
{
|
||||
final int minKey = ((TableSwitchInsnNode) instruction).min;
|
||||
final List labels = ((TableSwitchInsnNode) instruction).labels;
|
||||
for (int t = 0; t < labels.size(); t++) {
|
||||
final int key = minKey + t;
|
||||
final LabelNode targetInstruction = (LabelNode) labels.get(t);
|
||||
final int targetId = instructions.indexOf(targetInstruction);
|
||||
sb.append(key + ": " + targetId + ", ");
|
||||
}
|
||||
final LabelNode defaultTargetInstruction = ((TableSwitchInsnNode) instruction).dflt;
|
||||
final int defaultTargetId = instructions.indexOf(defaultTargetInstruction);
|
||||
sb.append("default: " + defaultTargetId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
99
src/ch/usi/inf/sp/cfg/App.java
Normal file
99
src/ch/usi/inf/sp/cfg/App.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ch.usi.inf.sp.bytecode.Disassembler;
|
||||
import ch.usi.inf.sp.dom.DominatorAnalyzer;
|
||||
import ch.usi.inf.sp.dom.DominatorTree;
|
||||
import ch.usi.inf.sp.dom.DominatorTreeRenderer;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
|
||||
public final class App {
|
||||
|
||||
/**
|
||||
* Invoke like this...
|
||||
* <p>
|
||||
* java App test-input/java10/ExampleClass.class test-output
|
||||
* <p>
|
||||
* to produce one disassembly, one CFG, one dominator tree,
|
||||
* and one combined graph (CFG with additional dotted dominance edges)
|
||||
* for each method in ExampleClass.
|
||||
* Afterwards, go to the test-output folder, and call...
|
||||
* <p>
|
||||
* dot -Tpdf -oall.pdf *.dot
|
||||
* <p>
|
||||
* ...to produce a file all.pdf containing one page for each graph, or...
|
||||
* <p>
|
||||
* dot -Tpdf -oall.combined.pdf *.combined.pdf
|
||||
* </p>
|
||||
* ...to produce a file all.combined.pdf with just the combined graphs.
|
||||
*
|
||||
* MAKE SURE YOU MANUALLY VERIFY FOR EACH METHOD THAT THE DOMINATORS ARE CORRECT.
|
||||
*/
|
||||
public static void main(final String[] args) throws IOException {
|
||||
final File classFile = new File(args[0]);
|
||||
final File outputDirectory = new File(args[1]);
|
||||
final App app = new App(classFile, outputDirectory);
|
||||
app.execute();
|
||||
}
|
||||
|
||||
private final File classFile;
|
||||
private final File outputDirectory;
|
||||
|
||||
|
||||
public App(final File classFile, final File outputDirectory) {
|
||||
this.classFile = classFile;
|
||||
this.outputDirectory = outputDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the given contents into a file with the given fileName in the outputDirectory.
|
||||
*/
|
||||
private void save(final String fileName, final String contents) throws IOException {
|
||||
if (!outputDirectory.exists()) {
|
||||
outputDirectory.mkdirs();
|
||||
}
|
||||
final File file = new File(outputDirectory, fileName);
|
||||
final FileWriter writer = new FileWriter(file);
|
||||
writer.write(contents);
|
||||
writer.close();
|
||||
}
|
||||
|
||||
public void execute() throws IOException {
|
||||
final ClassReader cr = new ClassReader(new FileInputStream(classFile));
|
||||
// create an empty ClassNode (in-memory representation of a class)
|
||||
final ClassNode cn = new ClassNode();
|
||||
// have the ClassReader read the class file and populate the ClassNode with the corresponding information
|
||||
cr.accept(cn, 0);
|
||||
// disassemble and perform control-flow analysis
|
||||
processClass(cn);
|
||||
}
|
||||
|
||||
private void processClass(final ClassNode cn) throws IOException {
|
||||
System.out.println("Class: " + cn.name);
|
||||
// get the list of all methods in that class
|
||||
final List<MethodNode> methods = cn.methods;
|
||||
for (int m = 0; m < methods.size(); m++) {
|
||||
final MethodNode method = methods.get(m);
|
||||
processMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
private void processMethod(final MethodNode method) throws IOException {
|
||||
System.out.println(" Method: " + method.name + method.desc);
|
||||
save(method.name + ".asm.txt", Disassembler.disassembleMethod(method));
|
||||
final ControlFlowGraph cfg = ControlFlowGraphBuilder.createControlFlowGraph(method);
|
||||
save(method.name + ".cfg.dot", ControlFlowGraphRenderer.renderControlFlowGraph(method.name, cfg));
|
||||
final DominatorTree dt = DominatorAnalyzer.analyze(cfg);
|
||||
save(method.name + ".dt.dot", DominatorTreeRenderer.renderDominatorTree(method.name, dt));
|
||||
save(method.name + ".combined.dot", DominatorTreeRenderer.renderCombined(method.name, cfg, dt));
|
||||
}
|
||||
|
||||
}
|
44
src/ch/usi/inf/sp/cfg/BasicBlock.java
Normal file
44
src/ch/usi/inf/sp/cfg/BasicBlock.java
Normal file
|
@ -0,0 +1,44 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import ch.usi.inf.sp.graph.Node;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
public final class BasicBlock extends Node<ControlFlowEdge> {
|
||||
|
||||
private final int id;
|
||||
private final ArrayList<String> instructions;
|
||||
|
||||
|
||||
public BasicBlock(final int id) {
|
||||
this.id = id;
|
||||
instructions = new ArrayList<String>();
|
||||
}
|
||||
|
||||
public void appendInstruction(final String s) {
|
||||
instructions.add(s);
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getInstructionCount() {
|
||||
return instructions.size();
|
||||
}
|
||||
|
||||
public String getInstruction(int i) {
|
||||
return instructions.get(i);
|
||||
}
|
||||
|
||||
public Iterable<String> getInstructions() {
|
||||
return instructions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "\""+id+"\"";
|
||||
}
|
||||
|
||||
}
|
19
src/ch/usi/inf/sp/cfg/ControlFlowEdge.java
Normal file
19
src/ch/usi/inf/sp/cfg/ControlFlowEdge.java
Normal file
|
@ -0,0 +1,19 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
|
||||
import ch.usi.inf.sp.graph.Edge;
|
||||
|
||||
public final class ControlFlowEdge extends Edge<BasicBlock> {
|
||||
|
||||
private final String label;
|
||||
|
||||
|
||||
public ControlFlowEdge(final String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
}
|
110
src/ch/usi/inf/sp/cfg/ControlFlowGraph.java
Normal file
110
src/ch/usi/inf/sp/cfg/ControlFlowGraph.java
Normal file
|
@ -0,0 +1,110 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import ch.usi.inf.sp.graph.DiGraph;
|
||||
|
||||
|
||||
public final class ControlFlowGraph extends DiGraph<BasicBlock, ControlFlowEdge> {
|
||||
|
||||
private BasicBlock entry;
|
||||
private BasicBlock exit;
|
||||
|
||||
|
||||
public ControlFlowGraph() {
|
||||
entry = new BasicBlock(-1);
|
||||
addNode(entry);
|
||||
exit = new BasicBlock(-2);
|
||||
addNode(exit);
|
||||
}
|
||||
|
||||
private final void checkContains(BasicBlock block, String name) {
|
||||
if (!getNodes().contains(block)) {
|
||||
throw new IllegalStateException("Control flow graph does not contain the given " + name + " block.");
|
||||
}
|
||||
}
|
||||
|
||||
public ControlFlowEdge addEntryEdge(BasicBlock firstBlock) {
|
||||
if (entry.getOutEdges().size() > 0) {
|
||||
throw new IllegalStateException("Control flow graph already has an entry edge. It can only have one.");
|
||||
}
|
||||
checkContains(firstBlock, "firstBlock");
|
||||
ControlFlowEdge edge = new ControlFlowEdge("");
|
||||
addEdge(edge);
|
||||
connect(entry, edge, firstBlock);
|
||||
return edge;
|
||||
}
|
||||
|
||||
public ControlFlowEdge addExitEdge(BasicBlock returnBlock) {
|
||||
checkContains(returnBlock, "returnBlock");
|
||||
ControlFlowEdge edge = new ControlFlowEdge("");
|
||||
addEdge(edge);
|
||||
connect(returnBlock, edge, exit);
|
||||
return edge;
|
||||
}
|
||||
|
||||
public ControlFlowEdge addFallthroughEdge(BasicBlock fromBlock, BasicBlock toBlock) {
|
||||
checkContains(fromBlock, "fromBlock");
|
||||
checkContains(toBlock, "toBlock");
|
||||
ControlFlowEdge edge = new ControlFlowEdge("");
|
||||
addEdge(edge);
|
||||
connect(fromBlock, edge, toBlock);
|
||||
return edge;
|
||||
}
|
||||
|
||||
public ControlFlowEdge addBranchTakenEdge(BasicBlock fromBlock, BasicBlock toBlock) {
|
||||
checkContains(fromBlock, "fromBlock");
|
||||
checkContains(toBlock, "toBlock");
|
||||
ControlFlowEdge edge = new ControlFlowEdge("T");
|
||||
addEdge(edge);
|
||||
connect(fromBlock, edge, toBlock);
|
||||
return edge;
|
||||
}
|
||||
|
||||
public ControlFlowEdge addCaseEdge(BasicBlock fromBlock, BasicBlock toBlock, int key) {
|
||||
checkContains(fromBlock, "fromBlock");
|
||||
checkContains(toBlock, "toBlock");
|
||||
ControlFlowEdge edge = new ControlFlowEdge("" + key);
|
||||
addEdge(edge);
|
||||
connect(fromBlock, edge, toBlock);
|
||||
return edge;
|
||||
}
|
||||
|
||||
public ControlFlowEdge addDefaultEdge(BasicBlock fromBlock, BasicBlock toBlock) {
|
||||
checkContains(fromBlock, "fromBlock");
|
||||
checkContains(toBlock, "toBlock");
|
||||
ControlFlowEdge edge = new ControlFlowEdge("default");
|
||||
addEdge(edge);
|
||||
connect(fromBlock, edge, toBlock);
|
||||
return edge;
|
||||
}
|
||||
|
||||
public BasicBlock getEntry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public BasicBlock getExit() {
|
||||
return exit;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("digraph CFG {\n");
|
||||
for (final BasicBlock node : getNodes()) {
|
||||
if (node==entry) {
|
||||
sb.append(" " + node + " [shape=circle,style=filled,label=e]\n");
|
||||
} else if (node==exit) {
|
||||
sb.append(" " + node + " [shape=circle,style=filled,label=x]\n");
|
||||
} else {
|
||||
sb.append(" " + node + " [shape=rectangle]\n");
|
||||
}
|
||||
}
|
||||
for (final ControlFlowEdge edge : getEdges()) {
|
||||
if (edge.getLabel().length()>0) {
|
||||
sb.append(" " + edge + " [label=" + edge.getLabel() + "]\n");
|
||||
} else {
|
||||
sb.append(" " + edge + "\n");
|
||||
}
|
||||
}
|
||||
sb.append("}\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
23
src/ch/usi/inf/sp/cfg/ControlFlowGraphBuilder.java
Normal file
23
src/ch/usi/inf/sp/cfg/ControlFlowGraphBuilder.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ch.usi.inf.sp.bytecode.Disassembler;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.JumpInsnNode;
|
||||
import org.objectweb.asm.tree.LabelNode;
|
||||
import org.objectweb.asm.tree.LookupSwitchInsnNode;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
import org.objectweb.asm.tree.TableSwitchInsnNode;
|
||||
|
||||
|
||||
public class ControlFlowGraphBuilder {
|
||||
|
||||
public static ControlFlowGraph createControlFlowGraph(final MethodNode method) {
|
||||
//TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
11
src/ch/usi/inf/sp/cfg/ControlFlowGraphRenderer.java
Normal file
11
src/ch/usi/inf/sp/cfg/ControlFlowGraphRenderer.java
Normal file
|
@ -0,0 +1,11 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
|
||||
public class ControlFlowGraphRenderer {
|
||||
|
||||
public static String renderControlFlowGraph(final String label, final ControlFlowGraph cfg) {
|
||||
//TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
8
src/ch/usi/inf/sp/dom/DominanceEdge.java
Normal file
8
src/ch/usi/inf/sp/dom/DominanceEdge.java
Normal file
|
@ -0,0 +1,8 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
import ch.usi.inf.sp.graph.Edge;
|
||||
|
||||
|
||||
public class DominanceEdge extends Edge<DominatorTreeNode> {
|
||||
|
||||
}
|
52
src/ch/usi/inf/sp/dom/DominatorAnalyzer.java
Normal file
52
src/ch/usi/inf/sp/dom/DominatorAnalyzer.java
Normal file
|
@ -0,0 +1,52 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
import ch.usi.inf.sp.cfg.BasicBlock;
|
||||
import ch.usi.inf.sp.cfg.ControlFlowGraph;
|
||||
import ch.usi.inf.sp.graph.Traversal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public class DominatorAnalyzer {
|
||||
|
||||
/**
|
||||
* Cooper et al.'s "Engineered Algorithm".
|
||||
* ================================================================
|
||||
* for all nodes, b // initialize the dominators array
|
||||
* doms[b] ← Undefined
|
||||
* doms[entryNode] ← entryNode
|
||||
* Changed ← true
|
||||
* while (Changed)
|
||||
* Changed ← false
|
||||
* for all nodes, b, in reverse postorder (except entryNode)
|
||||
* newidom ← first (processed) predecessor of b // (pick one)
|
||||
* for all other predecessors, p, of b
|
||||
* if doms[p] != Undefined // i.e., if doms[p] already calculated
|
||||
* newidom ← intersect(p, newidom)
|
||||
* if doms[b] != newidom
|
||||
* doms[b] ← newidom
|
||||
* Changed ← true
|
||||
*
|
||||
* function intersect(b1, b2) returns node
|
||||
* finger1 ← b1
|
||||
* finger2 ← b2
|
||||
* while (finger1 != finger2)
|
||||
* while (finger1 < finger2)
|
||||
* finger1 = doms[finger1]
|
||||
* while (finger2 < finger1)
|
||||
* finger2 = doms[finger2]
|
||||
* return finger1
|
||||
* ================================================================
|
||||
* Figure 3 of Cooper, Harvey, Kennedy
|
||||
*/
|
||||
public static DominatorTree analyze(final ControlFlowGraph cfg) {
|
||||
//TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
// probably add a method intersect(...)
|
||||
|
||||
}
|
80
src/ch/usi/inf/sp/dom/DominatorTree.java
Normal file
80
src/ch/usi/inf/sp/dom/DominatorTree.java
Normal file
|
@ -0,0 +1,80 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
import ch.usi.inf.sp.cfg.BasicBlock;
|
||||
import ch.usi.inf.sp.graph.DiGraph;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
public final class DominatorTree extends DiGraph<DominatorTreeNode,DominanceEdge> {
|
||||
|
||||
private Map<BasicBlock,DominatorTreeNode> nodeForBlock;
|
||||
private DominatorTreeNode rootNode;
|
||||
|
||||
|
||||
public DominatorTree() {
|
||||
nodeForBlock = new HashMap<>();
|
||||
}
|
||||
|
||||
public DominatorTreeNode getNodeForBlock(final BasicBlock block) {
|
||||
return nodeForBlock.get(block);
|
||||
}
|
||||
|
||||
public DominatorTreeNode setRootBlock(BasicBlock rootBlock) {
|
||||
DominatorTreeNode newRootNode = nodeForBlock.get(rootBlock);
|
||||
if (newRootNode==null) {
|
||||
newRootNode = new DominatorTreeNode(rootBlock);
|
||||
nodeForBlock.put(rootBlock, newRootNode);
|
||||
}
|
||||
if (!getNodes().contains(newRootNode)) {
|
||||
addNode(newRootNode);
|
||||
}
|
||||
rootNode = newRootNode;
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
public DominatorTreeNode getRoot() {
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
public DominanceEdge addDominanceEdge(BasicBlock idom, BasicBlock child) {
|
||||
DominatorTreeNode idomNode = nodeForBlock.get(idom);
|
||||
if (idomNode==null) {
|
||||
idomNode = new DominatorTreeNode(idom);
|
||||
nodeForBlock.put(idom, idomNode);
|
||||
}
|
||||
if (!getNodes().contains(idomNode)) {
|
||||
addNode(idomNode);
|
||||
}
|
||||
DominatorTreeNode childNode = nodeForBlock.get(child);
|
||||
if (childNode==null) {
|
||||
childNode = new DominatorTreeNode(child);
|
||||
nodeForBlock.put(child, childNode);
|
||||
}
|
||||
if (!getNodes().contains(childNode)) {
|
||||
addNode(childNode);
|
||||
}
|
||||
final DominanceEdge edge = new DominanceEdge();
|
||||
addEdge(edge);
|
||||
connect(idomNode, edge, childNode);
|
||||
return edge;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("digraph DOM {\n");
|
||||
for (final DominatorTreeNode node : getNodes()) {
|
||||
if (node==rootNode) {
|
||||
sb.append(" " + node + " [style=filled]\n");
|
||||
} else {
|
||||
sb.append(" " + node + "\n");
|
||||
}
|
||||
}
|
||||
for (final DominanceEdge edge : getEdges()) {
|
||||
sb.append(" "+edge+"\n");
|
||||
}
|
||||
sb.append("}\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
30
src/ch/usi/inf/sp/dom/DominatorTreeNode.java
Normal file
30
src/ch/usi/inf/sp/dom/DominatorTreeNode.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
import ch.usi.inf.sp.cfg.BasicBlock;
|
||||
import ch.usi.inf.sp.graph.Node;
|
||||
|
||||
|
||||
public class DominatorTreeNode extends Node<DominanceEdge> {
|
||||
|
||||
private final BasicBlock block;
|
||||
|
||||
|
||||
public DominatorTreeNode(final BasicBlock block) {
|
||||
this.block = block;
|
||||
}
|
||||
|
||||
public BasicBlock getBlock() {
|
||||
return block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof DominatorTreeNode && block==((DominatorTreeNode)other).block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "\"D("+block.getId()+")\"";
|
||||
}
|
||||
|
||||
}
|
59
src/ch/usi/inf/sp/dom/DominatorTreeRenderer.java
Normal file
59
src/ch/usi/inf/sp/dom/DominatorTreeRenderer.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
|
||||
import ch.usi.inf.sp.cfg.BasicBlock;
|
||||
import ch.usi.inf.sp.cfg.ControlFlowEdge;
|
||||
import ch.usi.inf.sp.cfg.ControlFlowGraph;
|
||||
|
||||
public class DominatorTreeRenderer {
|
||||
|
||||
public static String renderDominatorTree(final String label, final DominatorTree tree) {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
sb.append("digraph dominatorTree {\n");
|
||||
sb.append(" label=\"" + label + "\"\n");
|
||||
for (final DominatorTreeNode node : tree.getNodes()) {
|
||||
if (node == tree.getRoot()) {
|
||||
sb.append(" " + node.toString() + " [style=filled]\n");
|
||||
} else {
|
||||
sb.append(" " + node.toString() + "\n");
|
||||
}
|
||||
}
|
||||
for (final DominanceEdge edge : tree.getEdges()) {
|
||||
sb.append(" " + edge.getFrom().toString() + " -> " + edge.getTo().toString()+"\n");
|
||||
}
|
||||
sb.append("}\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String renderCombined(final String label, final ControlFlowGraph cfg, final DominatorTree dt) {
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
sb.append("digraph combined {\n");
|
||||
sb.append(" label=\"" + label + "\"\n");
|
||||
// render basic blocks
|
||||
for (final BasicBlock block : cfg.getNodes()) {
|
||||
if (block == cfg.getEntry()) {
|
||||
sb.append(" " + block.getId() + " [shape=record,style=filled,label=\""+block.getId()+"|entry\"]\n");
|
||||
} else if (block == cfg.getExit()) {
|
||||
sb.append(" " + block.getId() + " [shape=record,label=\""+block.getId()+"|exit\"]\n");
|
||||
} else {
|
||||
sb.append(" " + block.getId() + " [shape=record,label=\""+block.getId()+"|{");
|
||||
// use \l to tell dot to left-align each line
|
||||
sb.append(String.join("\\l|", block.getInstructions()));
|
||||
sb.append("\\l}\"]\n");
|
||||
}
|
||||
}
|
||||
// render control flow edges (solid)
|
||||
for (final ControlFlowEdge edge : cfg.getEdges()) {
|
||||
sb.append(" " + edge.getFrom().getId() + " -> " + edge.getTo().getId());
|
||||
sb.append(" [label=\"" + edge.getLabel() + "\"]\n");
|
||||
}
|
||||
// render dominance edges (dotted)
|
||||
for (final DominanceEdge edge : dt.getEdges()) {
|
||||
sb.append(" " + edge.getFrom().getBlock().getId() + " -> " + edge.getTo().getBlock().getId()+" [style=dotted]\n");
|
||||
}
|
||||
|
||||
sb.append("}\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
69
src/ch/usi/inf/sp/graph/DiGraph.java
Normal file
69
src/ch/usi/inf/sp/graph/DiGraph.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
package ch.usi.inf.sp.graph;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class DiGraph<N extends Node<E>, E extends Edge<N>> {
|
||||
|
||||
private final ArrayList<N> nodes;
|
||||
private final ArrayList<E> edges;
|
||||
|
||||
|
||||
public DiGraph() {
|
||||
nodes = new ArrayList<N>();
|
||||
edges = new ArrayList<E>();
|
||||
}
|
||||
|
||||
public void addNode(N node) {
|
||||
nodes.add(node);
|
||||
}
|
||||
|
||||
public void addEdge(E edge) {
|
||||
edges.add(edge);
|
||||
}
|
||||
|
||||
public void connect(N from, E edge, N to) {
|
||||
if (!nodes.contains(from)) {
|
||||
throw new IllegalStateException("Graph does not contain from node.");
|
||||
}
|
||||
if (!nodes.contains(to)) {
|
||||
throw new IllegalStateException("Graph does not contain to node.");
|
||||
}
|
||||
if (!edges.contains(edge)) {
|
||||
throw new IllegalStateException("Graph does not contain edge.");
|
||||
}
|
||||
if (from.getOutEdges().contains(edge)) {
|
||||
throw new IllegalStateException("From node already has this edge as out edge");
|
||||
}
|
||||
if (to.getInEdges().contains(edge)) {
|
||||
throw new IllegalStateException("To node already has this edge as in edge");
|
||||
}
|
||||
edge.setFrom(from);
|
||||
edge.setTo(to);
|
||||
from.addOutEdge(edge);
|
||||
to.addInEdge(edge);
|
||||
}
|
||||
|
||||
public List<E> getEdges() {
|
||||
return Collections.unmodifiableList(edges);
|
||||
}
|
||||
|
||||
public List<N> getNodes() {
|
||||
return Collections.unmodifiableList(nodes);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
final StringBuffer sb = new StringBuffer("digraph G {\n");
|
||||
for (final Node node : nodes) {
|
||||
sb.append(" "+node+"\n");
|
||||
}
|
||||
for (final Edge edge : edges) {
|
||||
sb.append(" "+edge+"\n");
|
||||
}
|
||||
sb.append("}\n");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
32
src/ch/usi/inf/sp/graph/Edge.java
Normal file
32
src/ch/usi/inf/sp/graph/Edge.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package ch.usi.inf.sp.graph;
|
||||
|
||||
|
||||
public class Edge<N extends Node> {
|
||||
|
||||
private N from;
|
||||
private N to;
|
||||
|
||||
public Edge() {
|
||||
}
|
||||
|
||||
public void setFrom(N node) {
|
||||
from = node;
|
||||
}
|
||||
|
||||
public N getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public void setTo(N node) {
|
||||
to = node;
|
||||
}
|
||||
|
||||
public N getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return from.toString()+" -> "+to.toString();
|
||||
}
|
||||
|
||||
}
|
38
src/ch/usi/inf/sp/graph/Node.java
Normal file
38
src/ch/usi/inf/sp/graph/Node.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
package ch.usi.inf.sp.graph;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class Node<E extends Edge> {
|
||||
|
||||
private final ArrayList<E> inEdges;
|
||||
private final ArrayList<E> outEdges;
|
||||
|
||||
public Node() {
|
||||
inEdges = new ArrayList<E>();
|
||||
outEdges = new ArrayList<E>();
|
||||
}
|
||||
|
||||
public void addInEdge(E edge) {
|
||||
inEdges.add(edge);
|
||||
}
|
||||
|
||||
public void addOutEdge(E edge) {
|
||||
outEdges.add(edge);
|
||||
}
|
||||
|
||||
public List<E> getInEdges() {
|
||||
return Collections.unmodifiableList(inEdges);
|
||||
}
|
||||
|
||||
public List<E> getOutEdges() {
|
||||
return Collections.unmodifiableList(outEdges);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return '"'+super.toString()+'"';
|
||||
}
|
||||
|
||||
}
|
39
src/ch/usi/inf/sp/graph/Traversal.java
Normal file
39
src/ch/usi/inf/sp/graph/Traversal.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
package ch.usi.inf.sp.graph;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public class Traversal {
|
||||
|
||||
public static <G extends DiGraph<N, E>, N extends Node<E>, E extends Edge<N>>
|
||||
List<N> getNodesInReversePostOrder(final DiGraph<N, E> graph, final N entryNode) {
|
||||
final List<N> orderedNodes = getNodesInPostOrder(graph, entryNode);
|
||||
Collections.reverse(orderedNodes);
|
||||
return orderedNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* From: https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/
|
||||
*
|
||||
* def postorder(graph, root):
|
||||
* """Return a post-order ordering of nodes in the graph."""
|
||||
* visited = set()
|
||||
* order = []
|
||||
* def dfs_walk(node):
|
||||
* visited.add(node)
|
||||
* for succ in graph.successors(node):
|
||||
* if not succ in visited:
|
||||
* dfs_walk(succ)
|
||||
* order.append(node)
|
||||
* dfs_walk(root)
|
||||
* return order
|
||||
*/
|
||||
public static <G extends DiGraph<N, E>, N extends Node<E>, E extends Edge<N>>
|
||||
List<N> getNodesInPostOrder(final DiGraph<N, E> graph, final N entryNode) {
|
||||
//TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
// probably add a method dfsWalk(...)
|
||||
|
||||
}
|
25
starter-lab-04-dominator-analysis.iml
Normal file
25
starter-lab-04-dominator-analysis.iml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?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" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library name="JUnit5.2">
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/jupiter/junit-jupiter-api/5.3.1/junit-jupiter-api-5.3.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.0.0/apiguardian-api-1.0.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.1.1/opentest4j-1.1.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-commons/1.3.1/junit-platform-commons-1.3.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="library" name="asm-util-7.1" level="project" />
|
||||
</component>
|
||||
</module>
|
288
test-input/ExampleClass.java
Normal file
288
test-input/ExampleClass.java
Normal file
|
@ -0,0 +1,288 @@
|
|||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* This class contains a set of methods that are useful for testing
|
||||
* Java disassemblers and control-flow graph generators.
|
||||
*
|
||||
* @author Matthias.Hauswirth@usi.ch
|
||||
*/
|
||||
public class ExampleClass {
|
||||
|
||||
public void emptyMethod() {
|
||||
return;
|
||||
}
|
||||
|
||||
//--- conditionals
|
||||
public int ifMethod(int i) {
|
||||
int j = 0;
|
||||
if (i<0) {
|
||||
j = 1;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
public int ifElseMethod(int i) {
|
||||
int j = 0;
|
||||
if (i>0) {
|
||||
j = 0;
|
||||
} else {
|
||||
j = i;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
public int switchMethod(int i) {
|
||||
int j = 0;
|
||||
switch (i) {
|
||||
case 0: j = 0; break;
|
||||
case 1: j = 1; break;
|
||||
case 2: j = 2; break;
|
||||
default: j = -1;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
public int switchMethod2(int i) {
|
||||
int j = 0;
|
||||
switch (i) {
|
||||
case 0: j = 0; break;
|
||||
case 1000: j = 1; break;
|
||||
case 2000: j = 2; break;
|
||||
default: j = -1;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--- loops
|
||||
public int forMethod(int i) {
|
||||
int sum = 0;
|
||||
for (int j=0; j<i; i++) {
|
||||
sum += j;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int whileMethod(int i) {
|
||||
int sum = 0;
|
||||
while (i>0) {
|
||||
sum +=i;
|
||||
i--;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int doWhileMethod(int i) {
|
||||
int sum = 0;
|
||||
do {
|
||||
sum += i;
|
||||
i--;
|
||||
} while (i>0);
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int forEachArrayMethod(String[] a) {
|
||||
int sum = 0;
|
||||
for (String s : a) {
|
||||
sum++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int forEachCollectionMethod(Set<String> a) {
|
||||
int sum = 0;
|
||||
for (String s : a) {
|
||||
sum++;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int forWithBreakMethod(int n) {
|
||||
int sum = 0;
|
||||
for (int i=0; i<n; i++) {
|
||||
if (i==10) {
|
||||
break;
|
||||
}
|
||||
sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int forWithContinueMethod(int n) {
|
||||
int sum = 0;
|
||||
for (int i=0; i<n; i++) {
|
||||
if (i==10) {
|
||||
continue;
|
||||
}
|
||||
sum += i;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
public int whileTrueMethod(int n) {
|
||||
while (true) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
public int doWhileTrue(int n) {
|
||||
do {
|
||||
n++;
|
||||
} while (true);
|
||||
}
|
||||
|
||||
public int forEver(int n) {
|
||||
for (int i=0; true; i++) {
|
||||
}
|
||||
}
|
||||
|
||||
public int nestedFor(int n) {
|
||||
int sum = 0;
|
||||
for (int i=0; i<n; i++) {
|
||||
for (int j=0; j<i; j++) {
|
||||
sum += j;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--- calls
|
||||
public int staticCallMethod(int i) {
|
||||
staticCallTarget();
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int instanceCallMethod(ExampleClass i) {
|
||||
i.instanceCallTarget();
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int privateInstanceCallMethod(ExampleClass i) {
|
||||
i.privateInstanceCallTarget();
|
||||
return 2;
|
||||
}
|
||||
|
||||
public int interfaceCallMethod(Interface i) {
|
||||
i.interfaceCallTarget();
|
||||
return 2;
|
||||
}
|
||||
|
||||
static interface Interface {
|
||||
public void interfaceCallTarget();
|
||||
}
|
||||
|
||||
static class Implementation implements Interface {
|
||||
public void interfaceCallTarget() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static void staticCallTarget() {
|
||||
}
|
||||
|
||||
public void instanceCallTarget() {
|
||||
}
|
||||
|
||||
private void privateInstanceCallTarget() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--- field and array accesses
|
||||
private String field;
|
||||
|
||||
public String fieldReadMethod() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public void fieldWriteMethod(String s) {
|
||||
field = s;
|
||||
}
|
||||
|
||||
private static String staticField;
|
||||
|
||||
public String staticFieldReadMethod() {
|
||||
return staticField;
|
||||
}
|
||||
|
||||
public void staticFieldWriteMethod(String s) {
|
||||
staticField = s;
|
||||
}
|
||||
|
||||
public int arrayLengthMethod(String[] a) {
|
||||
return a.length;
|
||||
}
|
||||
|
||||
public String arrayReadMethod(String[] a) {
|
||||
return a[0];
|
||||
}
|
||||
|
||||
public void arrayWriteMethod(String[] a, String s) {
|
||||
a[0] = s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--- allocation
|
||||
public Object allocObjectMethod() {
|
||||
return new Object();
|
||||
}
|
||||
|
||||
public int[] allocIntArrayMethod() {
|
||||
return new int[3];
|
||||
}
|
||||
|
||||
public Object[] allocObjectArrayMethod() {
|
||||
return new Object[3];
|
||||
}
|
||||
|
||||
public int[][] alloc2dArrayMethod() {
|
||||
return new int[2][3];
|
||||
}
|
||||
|
||||
public int[][] allocIncomplete2dArrayMethod() {
|
||||
return new int[2][];
|
||||
}
|
||||
|
||||
public int[][][] alloc2Of3dArrayMethod() {
|
||||
return new int[2][3][];
|
||||
}
|
||||
|
||||
public int[] allocAndInitIntArrayMethod() {
|
||||
return new int[] {1, 2};
|
||||
}
|
||||
|
||||
public Object[] allocAndInitObjectArrayMethod() {
|
||||
return new Object[] {"1", "2"};
|
||||
}
|
||||
|
||||
public int[][] allocAndInit2dArrayMethod() {
|
||||
return new int[][] {{1}};
|
||||
}
|
||||
|
||||
|
||||
|
||||
//--- more conditionals
|
||||
public int condMethod(int a, int b) {
|
||||
return a>b?a:b;
|
||||
}
|
||||
|
||||
public int shortCircuitMethod(int i, int j, int k) {
|
||||
if (i>j && i<k) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int nonShortCircuitMethod(int i, int j, int k) {
|
||||
if (i>j & i<k) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
9
test-input/Mini.java
Normal file
9
test-input/Mini.java
Normal file
|
@ -0,0 +1,9 @@
|
|||
// A minimal class, with one "method" (the constructor)
|
||||
public class Mini {
|
||||
|
||||
public Mini() {
|
||||
super();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
16
test-input/compile-example.sh
Executable file
16
test-input/compile-example.sh
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Compile the given Java file for the different versions of Java
|
||||
# E.g., if you use the ASM Viewer plugin for IntelliJ IDEA,
|
||||
# that plugin can't deal with Java 10.
|
||||
# It can read Java class files for older versions of Java, though.
|
||||
# So, here we compile it for all versions of Java supported by the Java 10 javac compiler.
|
||||
CLASS=$1
|
||||
|
||||
VERSIONS=( 6 7 8 9 10 )
|
||||
for VERSION in "${VERSIONS[@]}"
|
||||
do
|
||||
echo Version $VERSION
|
||||
mkdir -p java$VERSION
|
||||
javac -d java$VERSION --release $VERSION $CLASS
|
||||
done
|
BIN
test-input/java10/ExampleClass$Implementation.class
Normal file
BIN
test-input/java10/ExampleClass$Implementation.class
Normal file
Binary file not shown.
BIN
test-input/java10/ExampleClass$Interface.class
Normal file
BIN
test-input/java10/ExampleClass$Interface.class
Normal file
Binary file not shown.
BIN
test-input/java10/ExampleClass.class
Normal file
BIN
test-input/java10/ExampleClass.class
Normal file
Binary file not shown.
BIN
test-input/java10/Mini.class
Normal file
BIN
test-input/java10/Mini.class
Normal file
Binary file not shown.
BIN
test-input/java6/ExampleClass$Implementation.class
Normal file
BIN
test-input/java6/ExampleClass$Implementation.class
Normal file
Binary file not shown.
BIN
test-input/java6/ExampleClass$Interface.class
Normal file
BIN
test-input/java6/ExampleClass$Interface.class
Normal file
Binary file not shown.
BIN
test-input/java6/ExampleClass.class
Normal file
BIN
test-input/java6/ExampleClass.class
Normal file
Binary file not shown.
BIN
test-input/java6/Mini.class
Normal file
BIN
test-input/java6/Mini.class
Normal file
Binary file not shown.
BIN
test-input/java7/ExampleClass$Implementation.class
Normal file
BIN
test-input/java7/ExampleClass$Implementation.class
Normal file
Binary file not shown.
BIN
test-input/java7/ExampleClass$Interface.class
Normal file
BIN
test-input/java7/ExampleClass$Interface.class
Normal file
Binary file not shown.
BIN
test-input/java7/ExampleClass.class
Normal file
BIN
test-input/java7/ExampleClass.class
Normal file
Binary file not shown.
BIN
test-input/java7/Mini.class
Normal file
BIN
test-input/java7/Mini.class
Normal file
Binary file not shown.
BIN
test-input/java8/ExampleClass$Implementation.class
Normal file
BIN
test-input/java8/ExampleClass$Implementation.class
Normal file
Binary file not shown.
BIN
test-input/java8/ExampleClass$Interface.class
Normal file
BIN
test-input/java8/ExampleClass$Interface.class
Normal file
Binary file not shown.
BIN
test-input/java8/ExampleClass.class
Normal file
BIN
test-input/java8/ExampleClass.class
Normal file
Binary file not shown.
BIN
test-input/java8/Mini.class
Normal file
BIN
test-input/java8/Mini.class
Normal file
Binary file not shown.
BIN
test-input/java9/ExampleClass$Implementation.class
Normal file
BIN
test-input/java9/ExampleClass$Implementation.class
Normal file
Binary file not shown.
BIN
test-input/java9/ExampleClass$Interface.class
Normal file
BIN
test-input/java9/ExampleClass$Interface.class
Normal file
Binary file not shown.
BIN
test-input/java9/ExampleClass.class
Normal file
BIN
test-input/java9/ExampleClass.class
Normal file
Binary file not shown.
BIN
test-input/java9/Mini.class
Normal file
BIN
test-input/java9/Mini.class
Normal file
Binary file not shown.
28
test/ch/usi/inf/sp/cfg/BasicBlockTest.java
Normal file
28
test/ch/usi/inf/sp/cfg/BasicBlockTest.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class BasicBlockTest {
|
||||
|
||||
@Test
|
||||
void newBasicBlock() {
|
||||
BasicBlock bb = new BasicBlock(1);
|
||||
assertEquals(1, bb.getId());
|
||||
assertFalse(bb.getInstructions().iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
void appendInstruction() {
|
||||
BasicBlock bb = new BasicBlock(1);
|
||||
bb.appendInstruction("i1");
|
||||
bb.appendInstruction("i2");
|
||||
Iterator<String> it = bb.getInstructions().iterator();
|
||||
assertEquals("i1", it.next());
|
||||
assertEquals("i2", it.next());
|
||||
}
|
||||
|
||||
}
|
15
test/ch/usi/inf/sp/cfg/ControlFlowEdgeTest.java
Normal file
15
test/ch/usi/inf/sp/cfg/ControlFlowEdgeTest.java
Normal file
|
@ -0,0 +1,15 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ControlFlowEdgeTest {
|
||||
|
||||
@Test
|
||||
void newControlFlowGraphEdge() {
|
||||
ControlFlowEdge e = new ControlFlowEdge("e1");
|
||||
assertEquals("e1", e.getLabel());
|
||||
}
|
||||
|
||||
}
|
84
test/ch/usi/inf/sp/cfg/ControlFlowGraphBuilderTest.java
Normal file
84
test/ch/usi/inf/sp/cfg/ControlFlowGraphBuilderTest.java
Normal file
|
@ -0,0 +1,84 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import ch.usi.inf.sp.bytecode.Disassembler;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.objectweb.asm.tree.InsnList;
|
||||
import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* This tests the ControlFlowGraphBuilder by running it on the compiled ExampleClass.
|
||||
* It loads the ExampleClass.class (as compiled by Java10's javac).
|
||||
* The different test methods test the CFGB on different inputs
|
||||
* (different methods in class ExampleClass).
|
||||
* The test is VERY WEAK.
|
||||
* That is, there are many bugs it will not discover.
|
||||
*/
|
||||
class ControlFlowGraphBuilderTest {
|
||||
|
||||
private static ClassNode classNode;
|
||||
|
||||
private static MethodNode getMethod(String name, String desc) {
|
||||
for (MethodNode methodNode : classNode.methods) {
|
||||
if (methodNode.name.equals(name) && methodNode.desc.equals(desc)) {
|
||||
return methodNode;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Error in test harness: method "+name+" not found!");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws IOException {
|
||||
// Use ASM to read a class file (from test-input) and create a ClassNode
|
||||
File classFile = new File("test-input/java10/ExampleClass.class");
|
||||
final ClassReader cr = new ClassReader(new FileInputStream(classFile));
|
||||
// create an empty ClassNode (in-memory representation of a class)
|
||||
classNode = new ClassNode();
|
||||
// have the ClassReader read the class file and populate the ClassNode with the corresponding information
|
||||
cr.accept(classNode, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createControlFlowGraphOfEmptyMethod() {
|
||||
MethodNode methodNode = getMethod("emptyMethod", "()V");
|
||||
ControlFlowGraph g = ControlFlowGraphBuilder.createControlFlowGraph(methodNode);
|
||||
assertEquals(3, g.getNodes().size(), "CFG should contain three basic blocks: entry, exit, and one with code");
|
||||
BasicBlock bb = ((ControlFlowEdge)g.getEntry().getOutEdges().get(0)).getTo();
|
||||
final InsnList asmInstructions = methodNode.instructions;
|
||||
assertEquals(asmInstructions.size(), bb.getInstructionCount(), "basic block's instruction count differs from number of instructions in method node");
|
||||
for (int i = 0; i < asmInstructions.size(); i++) {
|
||||
AbstractInsnNode asmInstruction = asmInstructions.get(i);
|
||||
String asmInstructionAsString = Disassembler.disassembleInstruction(asmInstruction, i, asmInstructions);
|
||||
String bbInstruction = bb.getInstruction(i);
|
||||
assertEquals(asmInstructionAsString, bbInstruction, "basic block's instruction string differs from disassembled ASM instruction");
|
||||
//System.out.println(asmInstructionAsString+" == "+bbInstruction);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createControlFlowGraphOfIfMethod() {
|
||||
MethodNode methodNode = getMethod("ifMethod", "(I)I");
|
||||
ControlFlowGraph g = ControlFlowGraphBuilder.createControlFlowGraph(methodNode);
|
||||
assertEquals(5, g.getNodes().size(), "CFG should contain five basic blocks: entry, exit, before if, then, after if");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createControlFlowGraphOfIfElseMethod() {
|
||||
MethodNode methodNode = getMethod("ifElseMethod", "(I)I");
|
||||
ControlFlowGraph g = ControlFlowGraphBuilder.createControlFlowGraph(methodNode);
|
||||
assertEquals(6, g.getNodes().size(), "CFG should contain five basic blocks: entry, exit, before if, then, else, after if");
|
||||
}
|
||||
|
||||
}
|
98
test/ch/usi/inf/sp/cfg/ControlFlowGraphRendererTest.java
Normal file
98
test/ch/usi/inf/sp/cfg/ControlFlowGraphRendererTest.java
Normal file
|
@ -0,0 +1,98 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
||||
/**
|
||||
* Tests ControlFlowGraphRenderer by
|
||||
* asking it to render simple, artificially generated CFGs.
|
||||
* The test is VERY WEAK.
|
||||
* That is, there are many bugs it will not discover.
|
||||
*/
|
||||
class ControlFlowGraphRendererTest {
|
||||
|
||||
@Test
|
||||
public void renderEmptyControlFlowGraph() {
|
||||
ControlFlowGraph cfg = new ControlFlowGraph();
|
||||
String result = ControlFlowGraphRenderer.renderControlFlowGraph("THELABEL", cfg);
|
||||
//System.out.println(result);
|
||||
assertTrue(result.contains("THELABEL"), "must include the given label");
|
||||
assertTrue(result.startsWith("digraph "));
|
||||
assertTrue(result.contains("{"));
|
||||
assertTrue(result.contains("}"));
|
||||
assertTrue(result.contains("["), "must set properties of entry/exit nodes");
|
||||
assertTrue(result.contains("]"), "must set properties of entry/exit nodes");
|
||||
assertTrue(result.contains("label"), "must set label for graph, and for nodes");
|
||||
assertTrue(result.contains("-1"), "must contain entry node's ID: -1");
|
||||
assertTrue(result.contains("-2"), "must contain exit node's ID: -2");
|
||||
assertTrue(result.contains("shape"), "must set shape for entry/exit nodes");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void renderSingleBlockControlFlowGraph() {
|
||||
ControlFlowGraph cfg = new ControlFlowGraph();
|
||||
BasicBlock bb = new BasicBlock(99);
|
||||
bb.appendInstruction("THEINSTRUCTION");
|
||||
cfg.addNode(bb);
|
||||
cfg.addEntryEdge(bb);
|
||||
cfg.addExitEdge(bb);
|
||||
String result = ControlFlowGraphRenderer.renderControlFlowGraph("THELABEL", cfg);
|
||||
//System.out.println(result);
|
||||
assertTrue(result.contains("THELABEL"), "must include the given label");
|
||||
assertTrue(result.startsWith("digraph "));
|
||||
assertTrue(result.contains("{"));
|
||||
assertTrue(result.contains("}"));
|
||||
assertTrue(result.contains("["), "must set properties of entry/exit nodes");
|
||||
assertTrue(result.contains("]"), "must set properties of entry/exit nodes");
|
||||
assertTrue(result.contains("label"), "must set label for graph, and for nodes");
|
||||
assertTrue(result.contains("-1"), "must contain entry node's ID: -1");
|
||||
assertTrue(result.contains("-2"), "must contain exit node's ID: -2");
|
||||
assertTrue(result.contains("shape"), "must set shape for entry/exit nodes");
|
||||
|
||||
assertTrue(result.contains("->"), "must contain an edge (->)");
|
||||
assertTrue(result.contains("99"), "must include the basic block's ID (99)");
|
||||
assertTrue(result.contains("THEINSTRUCTION"), "must contain THEINSTRUCTION in the label of the basic block");
|
||||
assertTrue(result.contains("record"), "must use a record shape for the basic block");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void renderTwoBlockControlFlowGraph() {
|
||||
ControlFlowGraph cfg = new ControlFlowGraph();
|
||||
BasicBlock bb1 = new BasicBlock(11);
|
||||
bb1.appendInstruction("I11.1");
|
||||
bb1.appendInstruction("I11.2");
|
||||
cfg.addNode(bb1);
|
||||
BasicBlock bb2 = new BasicBlock(22);
|
||||
bb2.appendInstruction("I22.1");
|
||||
bb2.appendInstruction("I22.2");
|
||||
cfg.addNode(bb2);
|
||||
cfg.addEntryEdge(bb1);
|
||||
cfg.addFallthroughEdge(bb1, bb2);
|
||||
cfg.addExitEdge(bb2);
|
||||
String result = ControlFlowGraphRenderer.renderControlFlowGraph("THELABEL", cfg);
|
||||
//System.out.println(result);
|
||||
assertTrue(result.contains("THELABEL"), "must include the given label");
|
||||
assertTrue(result.startsWith("digraph "));
|
||||
assertTrue(result.contains("{"));
|
||||
assertTrue(result.contains("}"));
|
||||
assertTrue(result.contains("["), "must set properties of entry/exit nodes");
|
||||
assertTrue(result.contains("]"), "must set properties of entry/exit nodes");
|
||||
assertTrue(result.contains("label"), "must set label for graph, and for nodes");
|
||||
assertTrue(result.contains("-1"), "must contain entry node's ID: -1");
|
||||
assertTrue(result.contains("-2"), "must contain exit node's ID: -2");
|
||||
assertTrue(result.contains("shape"), "must set shape for entry/exit nodes");
|
||||
|
||||
assertTrue(result.contains("->"), "must contain an edge (->)");
|
||||
assertTrue(result.contains("11"), "must include the first basic block's ID (11)");
|
||||
assertTrue(result.contains("22"), "must include the second basic block's ID (22)");
|
||||
assertTrue(result.contains("I11.1"), "must contain instruction I11.1 in the label of the first basic block");
|
||||
assertTrue(result.contains("I11.2"), "must contain instruction I11.2 in the label of the first basic block");
|
||||
assertTrue(result.contains("I22.1"), "must contain instruction I22.1 in the label of the first basic block");
|
||||
assertTrue(result.contains("I22.2"), "must contain instruction I22.2 in the label of the first basic block");
|
||||
assertTrue(result.contains("record"), "must use a record shape for the basic block");
|
||||
}
|
||||
|
||||
}
|
171
test/ch/usi/inf/sp/cfg/ControlFlowGraphTest.java
Normal file
171
test/ch/usi/inf/sp/cfg/ControlFlowGraphTest.java
Normal file
|
@ -0,0 +1,171 @@
|
|||
package ch.usi.inf.sp.cfg;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ControlFlowGraphTest {
|
||||
|
||||
private final void assertConnected(BasicBlock from, ControlFlowEdge e, BasicBlock to) {
|
||||
assertTrue(from.getOutEdges().contains(e));
|
||||
assertTrue(to.getInEdges().contains(e));
|
||||
assertSame(from, e.getFrom());
|
||||
assertSame(to, e.getTo());
|
||||
}
|
||||
|
||||
@Test
|
||||
void newControlFlowGraph() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
assertNotNull(g.getEntry());
|
||||
assertNotNull(g.getExit());
|
||||
assertNotSame(g.getEntry(), g.getExit());
|
||||
assertEquals(2, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(g.getEntry()));
|
||||
assertTrue(g.getNodes().contains(g.getExit()));
|
||||
assertEquals(0, g.getEdges().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addEntryEdge() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock bb = new BasicBlock(1);
|
||||
g.addNode(bb);
|
||||
ControlFlowEdge e = g.addEntryEdge(bb);
|
||||
assertNotNull(e);
|
||||
assertEquals(3, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(bb));
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertTrue(g.getEdges().contains(g.getEntry().getOutEdges().get(0)));
|
||||
assertSame(bb, g.getEntry().getOutEdges().get(0).getTo());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addEntryEdgeToNonContainedBasicBlock() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock bb = new BasicBlock(1);
|
||||
assertThrows(IllegalStateException.class, () -> g.addEntryEdge(bb));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addTwoEntryEdges() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock bb = new BasicBlock(1);
|
||||
g.addNode(bb);
|
||||
g.addEntryEdge(bb);
|
||||
assertThrows(IllegalStateException.class, () -> g.addEntryEdge(bb));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addExitEdge() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock bb = new BasicBlock(1);
|
||||
g.addNode(bb);
|
||||
ControlFlowEdge e = g.addExitEdge(bb);
|
||||
assertNotNull(e);
|
||||
assertEquals(3, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(bb));
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertTrue(g.getEdges().contains(g.getExit().getInEdges().get(0)));
|
||||
assertSame(bb, g.getExit().getInEdges().get(0).getFrom());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addExitEdgeFromNonContainedBasicBlock() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock bb = new BasicBlock(1);
|
||||
assertThrows(IllegalStateException.class, () -> g.addExitEdge(bb));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addTwoExitEdges() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock bb = new BasicBlock(1);
|
||||
g.addNode(bb);
|
||||
g.addExitEdge(bb);
|
||||
g.addExitEdge(bb);
|
||||
assertEquals(3, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(bb));
|
||||
assertEquals(2, g.getEdges().size());
|
||||
assertTrue(g.getEdges().contains(g.getExit().getInEdges().get(0)));
|
||||
assertTrue(g.getEdges().contains(g.getExit().getInEdges().get(1)));
|
||||
assertSame(bb, g.getExit().getInEdges().get(0).getFrom());
|
||||
assertSame(bb, g.getExit().getInEdges().get(1).getFrom());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addFallthroughEdge() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock from = new BasicBlock(1);
|
||||
BasicBlock to = new BasicBlock(2);
|
||||
g.addNode(from);
|
||||
g.addNode(to);
|
||||
ControlFlowEdge e = g.addFallthroughEdge(from, to);
|
||||
assertNotNull(e);
|
||||
assertEquals("", e.getLabel());
|
||||
assertEquals(4, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(from));
|
||||
assertTrue(g.getNodes().contains(to));
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertTrue(g.getEdges().contains(e));
|
||||
assertConnected(from, e, to);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addBranchTakenEdge() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock from = new BasicBlock(1);
|
||||
BasicBlock to = new BasicBlock(2);
|
||||
g.addNode(from);
|
||||
g.addNode(to);
|
||||
ControlFlowEdge e = g.addBranchTakenEdge(from, to);
|
||||
assertNotNull(e);
|
||||
assertEquals("T", e.getLabel());
|
||||
assertEquals(4, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(from));
|
||||
assertTrue(g.getNodes().contains(to));
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertTrue(g.getEdges().contains(e));
|
||||
assertConnected(from, e, to);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addCaseEdge() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock from = new BasicBlock(1);
|
||||
BasicBlock to = new BasicBlock(2);
|
||||
g.addNode(from);
|
||||
g.addNode(to);
|
||||
ControlFlowEdge e = g.addCaseEdge(from, to, 99);
|
||||
assertNotNull(e);
|
||||
assertEquals("99", e.getLabel());
|
||||
assertEquals(4, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(from));
|
||||
assertTrue(g.getNodes().contains(to));
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertTrue(g.getEdges().contains(e));
|
||||
assertConnected(from, e, to);
|
||||
}
|
||||
|
||||
@Test
|
||||
void addDefaultEdge() {
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock from = new BasicBlock(1);
|
||||
BasicBlock to = new BasicBlock(2);
|
||||
g.addNode(from);
|
||||
g.addNode(to);
|
||||
ControlFlowEdge e = g.addDefaultEdge(from, to);
|
||||
assertNotNull(e);
|
||||
assertEquals("default", e.getLabel());
|
||||
assertEquals(4, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(from));
|
||||
assertTrue(g.getNodes().contains(to));
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertTrue(g.getEdges().contains(e));
|
||||
assertConnected(from, e, to);
|
||||
}
|
||||
|
||||
}
|
208
test/ch/usi/inf/sp/dom/DominatorAnalyzerTest.java
Normal file
208
test/ch/usi/inf/sp/dom/DominatorAnalyzerTest.java
Normal file
|
@ -0,0 +1,208 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
import ch.usi.inf.sp.cfg.BasicBlock;
|
||||
import ch.usi.inf.sp.cfg.ControlFlowGraph;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static ch.usi.inf.sp.dom.DominatorTreeAssertions.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class DominatorAnalyzerTest {
|
||||
|
||||
@Test
|
||||
void analyzeEntryExit() {
|
||||
ControlFlowGraph cfg = new ControlFlowGraph();
|
||||
cfg.addFallthroughEdge(cfg.getEntry(), cfg.getExit());
|
||||
System.out.println(cfg);
|
||||
|
||||
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||||
System.out.println(t);
|
||||
assertAllNodesMapToTheirBlocks(t);
|
||||
assertNodesHaveCorrectNumberOfParents(t);
|
||||
assertChildrenPointToParent(t);
|
||||
assertPathToRoot(t, Arrays.asList(cfg.getExit(), cfg.getEntry()));
|
||||
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||||
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||||
assertTrue(t.getRoot().getOutEdges().stream().map(e->e.getTo()).anyMatch(d -> d==t.getNodeForBlock(cfg.getExit())), "Root must have child holding exit block");
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeEntryNodeExit() {
|
||||
ControlFlowGraph cfg = new ControlFlowGraph();
|
||||
BasicBlock b0 = new BasicBlock(0);
|
||||
cfg.addNode(b0);
|
||||
cfg.addFallthroughEdge(cfg.getEntry(), b0);
|
||||
cfg.addFallthroughEdge(b0, cfg.getExit());
|
||||
System.out.println(cfg);
|
||||
|
||||
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||||
System.out.println(t);
|
||||
assertAllNodesMapToTheirBlocks(t);
|
||||
assertNodesHaveCorrectNumberOfParents(t);
|
||||
assertChildrenPointToParent(t);
|
||||
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||||
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||||
assertSame(b0, t.getRoot().getOutEdges().get(0).getTo().getBlock(), "Root must have child holding b0 block");
|
||||
assertEquals(1, t.getRoot().getOutEdges().get(0).getTo().getOutEdges().size(), "Root must have child that has one out edge");
|
||||
assertSame(cfg.getExit(), t.getRoot().getOutEdges().get(0).getTo().getOutEdges().get(0).getTo().getBlock(), "Root must have child that has child holding exit block");
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeIfThen() {
|
||||
ControlFlowGraph cfg = new ControlFlowGraph();
|
||||
BasicBlock bIf = new BasicBlock(0);
|
||||
cfg.addNode(bIf);
|
||||
BasicBlock bThen = new BasicBlock(1);
|
||||
cfg.addNode(bThen);
|
||||
cfg.addFallthroughEdge(cfg.getEntry(), bIf);
|
||||
cfg.addFallthroughEdge(bIf, cfg.getExit());
|
||||
cfg.addBranchTakenEdge(bIf, bThen);
|
||||
cfg.addFallthroughEdge(bThen, cfg.getExit());
|
||||
System.out.println(cfg);
|
||||
|
||||
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||||
System.out.println(t);
|
||||
assertAllNodesMapToTheirBlocks(t);
|
||||
assertNodesHaveCorrectNumberOfParents(t);
|
||||
assertChildrenPointToParent(t);
|
||||
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||||
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||||
assertSame(bIf, t.getRoot().getOutEdges().get(0).getTo().getBlock(), "Root must have child holding bIf block");
|
||||
assertEquals(2, t.getRoot().getOutEdges().get(0).getTo().getOutEdges().size(), "Root must have child that has two out edges");
|
||||
assertTrue(t.getRoot().getOutEdges().get(0).getTo().getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==bThen), "Root must have child that has child holding bThen block");
|
||||
assertTrue(t.getRoot().getOutEdges().get(0).getTo().getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==cfg.getExit()), "Root must have child that has child holding exit block");
|
||||
assertEquals(0, t.getNodeForBlock(bThen).getOutEdges().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeIfThenElse() {
|
||||
ControlFlowGraph cfg = new ControlFlowGraph();
|
||||
BasicBlock bIf = new BasicBlock(0);
|
||||
cfg.addNode(bIf);
|
||||
BasicBlock bThen = new BasicBlock(1);
|
||||
cfg.addNode(bThen);
|
||||
BasicBlock bElse = new BasicBlock(2);
|
||||
cfg.addNode(bElse);
|
||||
cfg.addFallthroughEdge(cfg.getEntry(), bIf);
|
||||
cfg.addBranchTakenEdge(bIf, bElse);
|
||||
cfg.addFallthroughEdge(bIf, bThen);
|
||||
cfg.addFallthroughEdge(bElse, cfg.getExit());
|
||||
cfg.addFallthroughEdge(bThen, cfg.getExit());
|
||||
System.out.println(cfg);
|
||||
|
||||
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||||
System.out.println(t);
|
||||
assertAllNodesMapToTheirBlocks(t);
|
||||
assertNodesHaveCorrectNumberOfParents(t);
|
||||
assertChildrenPointToParent(t);
|
||||
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||||
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||||
assertSame(bIf, t.getRoot().getOutEdges().get(0).getTo().getBlock(), "Root must have child holding bIf block");
|
||||
assertEquals(3, t.getNodeForBlock(bIf).getOutEdges().size(), "bIf must have three out edges");
|
||||
assertTrue(t.getNodeForBlock(bIf).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==bThen), "bIf must have child holding bThen block");
|
||||
assertTrue(t.getNodeForBlock(bIf).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==bElse), "bIf must have child holding bElse block");
|
||||
assertTrue(t.getNodeForBlock(bIf).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==cfg.getExit()), "bIf must have child holding exit block");
|
||||
assertEquals(0, t.getNodeForBlock(bThen).getOutEdges().size());
|
||||
assertEquals(0, t.getNodeForBlock(bElse).getOutEdges().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeLoop() {
|
||||
ControlFlowGraph cfg = new ControlFlowGraph();
|
||||
BasicBlock bHeader = new BasicBlock(0);
|
||||
cfg.addNode(bHeader);
|
||||
BasicBlock bBody = new BasicBlock(1);
|
||||
cfg.addNode(bBody);
|
||||
cfg.addFallthroughEdge(cfg.getEntry(), bHeader);
|
||||
cfg.addBranchTakenEdge(bHeader, bBody);
|
||||
cfg.addFallthroughEdge(bHeader, cfg.getExit());
|
||||
cfg.addFallthroughEdge(bBody, bHeader);
|
||||
System.out.println(cfg);
|
||||
|
||||
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||||
System.out.println(t);
|
||||
assertAllNodesMapToTheirBlocks(t);
|
||||
assertNodesHaveCorrectNumberOfParents(t);
|
||||
assertChildrenPointToParent(t);
|
||||
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||||
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||||
assertSame(bHeader, t.getRoot().getOutEdges().get(0).getTo().getBlock(), "Root must have child holding bHeader block");
|
||||
assertEquals(2, t.getNodeForBlock(bHeader).getOutEdges().size(), "bHeader must have two out edges");
|
||||
assertTrue(t.getNodeForBlock(bHeader).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==bBody), "bHeader must have child holding bBody block");
|
||||
assertTrue(t.getNodeForBlock(bHeader).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==cfg.getExit()), "bHeader must have child holding exit block");
|
||||
assertEquals(0, t.getNodeForBlock(bBody).getOutEdges().size(), "bBody must have zero out edges");
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeCooperFigure2() {
|
||||
// This is an irreducible graph
|
||||
// (and it doesn't have an exit edge, so we "abuse" the CFG a bit, which is fine)
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock b5 = g.getEntry();
|
||||
BasicBlock b4 = new BasicBlock(4);
|
||||
g.addNode(b4);
|
||||
BasicBlock b3 = new BasicBlock(3);
|
||||
g.addNode(b3);
|
||||
BasicBlock b2 = new BasicBlock(2);
|
||||
g.addNode(b2);
|
||||
BasicBlock b1 = g.getExit();
|
||||
g.addFallthroughEdge(b5, b3);
|
||||
g.addFallthroughEdge(b5, b4);
|
||||
g.addFallthroughEdge(b3, b2);
|
||||
g.addFallthroughEdge(b4, b1);
|
||||
g.addFallthroughEdge(b2, b1);
|
||||
g.addFallthroughEdge(b1, b2);
|
||||
System.out.println(g);
|
||||
|
||||
DominatorTree t = DominatorAnalyzer.analyze(g);
|
||||
System.out.println(t);
|
||||
assertAllNodesMapToTheirBlocks(t);
|
||||
assertNodesHaveCorrectNumberOfParents(t);
|
||||
assertChildrenPointToParent(t);
|
||||
assertPathToRoot(t, Arrays.asList(b4, b5));
|
||||
assertPathToRoot(t, Arrays.asList(b3, b5));
|
||||
assertPathToRoot(t, Arrays.asList(b2, b5));
|
||||
assertPathToRoot(t, Arrays.asList(b1, b5));
|
||||
}
|
||||
|
||||
@Test
|
||||
void analyzeCooperFigure4() {
|
||||
// This is an irreducible graph
|
||||
// (and it doesn't have an exit edge, so we "abuse" the CFG a bit, which is fine)
|
||||
ControlFlowGraph g = new ControlFlowGraph();
|
||||
BasicBlock b6 = g.getEntry();
|
||||
BasicBlock b5 = new BasicBlock(5);
|
||||
g.addNode(b5);
|
||||
BasicBlock b4 = new BasicBlock(4);
|
||||
g.addNode(b4);
|
||||
BasicBlock b3 = new BasicBlock(3);
|
||||
g.addNode(b3);
|
||||
BasicBlock b2 = new BasicBlock(2);
|
||||
g.addNode(b2);
|
||||
BasicBlock b1 = g.getExit();
|
||||
g.addFallthroughEdge(b6, b4);
|
||||
g.addFallthroughEdge(b6, b5);
|
||||
g.addFallthroughEdge(b4, b3);
|
||||
g.addFallthroughEdge(b4, b2);
|
||||
g.addFallthroughEdge(b3, b2);
|
||||
g.addFallthroughEdge(b2, b1);
|
||||
g.addFallthroughEdge(b2, b3);
|
||||
g.addFallthroughEdge(b1, b2);
|
||||
g.addFallthroughEdge(b5, b1);
|
||||
System.out.println(g);
|
||||
|
||||
DominatorTree t = DominatorAnalyzer.analyze(g);
|
||||
System.out.println(t);
|
||||
assertAllNodesMapToTheirBlocks(t);
|
||||
assertNodesHaveCorrectNumberOfParents(t);
|
||||
assertChildrenPointToParent(t);
|
||||
assertPathToRoot(t, Arrays.asList(b5, b6));
|
||||
assertPathToRoot(t, Arrays.asList(b4, b6));
|
||||
assertPathToRoot(t, Arrays.asList(b3, b6));
|
||||
assertPathToRoot(t, Arrays.asList(b2, b6));
|
||||
assertPathToRoot(t, Arrays.asList(b1, b6));
|
||||
}
|
||||
|
||||
}
|
51
test/ch/usi/inf/sp/dom/DominatorTreeAssertions.java
Normal file
51
test/ch/usi/inf/sp/dom/DominatorTreeAssertions.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
import ch.usi.inf.sp.cfg.BasicBlock;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* These are static methods used in unit tests to assert properties on DominatorTrees.
|
||||
*/
|
||||
public class DominatorTreeAssertions {
|
||||
|
||||
public static void assertAllNodesMapToTheirBlocks(DominatorTree t) {
|
||||
for (DominatorTreeNode n : t.getNodes()) {
|
||||
assertSame(n, t.getNodeForBlock(n.getBlock()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNodesHaveCorrectNumberOfParents(DominatorTree t) {
|
||||
for (DominatorTreeNode node : t.getNodes()) {
|
||||
if (node==t.getRoot()) {
|
||||
assertEquals(0, node.getInEdges().size());
|
||||
} else {
|
||||
assertEquals(1, node.getInEdges().size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertChildrenPointToParent(DominatorTree t) {
|
||||
for (DominatorTreeNode parent : t.getNodes()) {
|
||||
for (DominanceEdge e : parent.getOutEdges()) {
|
||||
DominatorTreeNode child = e.getTo();
|
||||
assertSame(parent, child.getInEdges().get(0).getFrom());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertPathToRoot(DominatorTree t, List<BasicBlock> pathToRoot) {
|
||||
DominatorTreeNode child = null;
|
||||
for (BasicBlock b: pathToRoot) {
|
||||
DominatorTreeNode node = t.getNodeForBlock(b);
|
||||
assertSame(b, node.getBlock());
|
||||
if (child!=null) {
|
||||
assertEquals(b, child.getInEdges().get(0).getFrom().getBlock());
|
||||
}
|
||||
child = node;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
41
test/ch/usi/inf/sp/dom/DominatorTreeTest.java
Normal file
41
test/ch/usi/inf/sp/dom/DominatorTreeTest.java
Normal file
|
@ -0,0 +1,41 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
import ch.usi.inf.sp.cfg.BasicBlock;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class DominatorTreeTest {
|
||||
|
||||
@Test
|
||||
void newDominatorTree() {
|
||||
DominatorTree t = new DominatorTree();
|
||||
assertNull(t.getRoot());
|
||||
assertEquals(0, t.getNodes().size());
|
||||
assertEquals(0, t.getEdges().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRoot() {
|
||||
DominatorTree t = new DominatorTree();
|
||||
BasicBlock b = new BasicBlock(0);
|
||||
t.setRootBlock(b);
|
||||
assertSame(b, t.getRoot().getBlock());
|
||||
assertEquals(1, t.getNodes().size());
|
||||
assertSame(b, t.getNodes().get(0).getBlock());
|
||||
assertEquals(0, t.getEdges().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addDominanceEdge() {
|
||||
DominatorTree t = new DominatorTree();
|
||||
BasicBlock rootBlock = new BasicBlock(1);
|
||||
BasicBlock childBlock = new BasicBlock(2);
|
||||
t.setRootBlock(rootBlock);
|
||||
DominanceEdge edge = t.addDominanceEdge(rootBlock, childBlock);
|
||||
assertSame(rootBlock, t.getRoot().getBlock());
|
||||
assertEquals(1, t.getRoot().getOutEdges().size());
|
||||
assertSame(edge, t.getRoot().getOutEdges().get(0));
|
||||
}
|
||||
|
||||
}
|
147
test/ch/usi/inf/sp/graph/DiGraphTest.java
Normal file
147
test/ch/usi/inf/sp/graph/DiGraphTest.java
Normal file
|
@ -0,0 +1,147 @@
|
|||
package ch.usi.inf.sp.graph;
|
||||
|
||||
import ch.usi.inf.sp.graph.DiGraph;
|
||||
import ch.usi.inf.sp.graph.Edge;
|
||||
import ch.usi.inf.sp.graph.Node;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class DiGraphTest {
|
||||
|
||||
@Test
|
||||
void newDiGraph() {
|
||||
DiGraph g = new DiGraph();
|
||||
assertEquals(0, g.getNodes().size());
|
||||
assertEquals(0, g.getEdges().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringEmpty() {
|
||||
DiGraph g = new DiGraph();
|
||||
assertEquals("digraph G {\n}\n", g.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringOneEdge() {
|
||||
DiGraph g = new DiGraph();
|
||||
Node n1 = new Node();
|
||||
Node n2 = new Node();
|
||||
Edge e = new Edge();
|
||||
g.addNode(n1);
|
||||
g.addNode(n2);
|
||||
g.addEdge(e);
|
||||
g.connect(n1, e, n2);
|
||||
assertEquals("digraph G {\n"+
|
||||
" "+n1.toString()+"\n"+
|
||||
" "+n2.toString()+"\n"+
|
||||
" "+e.toString()+"\n"+
|
||||
"}\n", g.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addNode() {
|
||||
DiGraph g = new DiGraph();
|
||||
Node n = new Node();
|
||||
g.addNode(n);
|
||||
assertEquals(1, g.getNodes().size());
|
||||
assertSame(n, g.getNodes().get(0));
|
||||
assertEquals(0, g.getEdges().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addEdge() {
|
||||
DiGraph g = new DiGraph();
|
||||
Edge e = new Edge();
|
||||
g.addEdge(e);
|
||||
assertEquals(0, g.getNodes().size());
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertSame(e, g.getEdges().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void connect() {
|
||||
DiGraph g = new DiGraph();
|
||||
Edge e = new Edge();
|
||||
Node n1 = new Node();
|
||||
Node n2 = new Node();
|
||||
g.addNode(n1);
|
||||
g.addNode(n2);
|
||||
g.addEdge(e);
|
||||
g.connect(n1, e, n2);
|
||||
assertEquals(2, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(n1));
|
||||
assertTrue(g.getNodes().contains(n2));
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertSame(e, g.getEdges().get(0));
|
||||
assertSame(e, n1.getOutEdges().get(0));
|
||||
assertSame(e, n2.getInEdges().get(0));
|
||||
assertSame(n1, e.getFrom());
|
||||
assertSame(n2, e.getTo());
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectEdgeNotContained() {
|
||||
DiGraph g = new DiGraph();
|
||||
Edge e = new Edge();
|
||||
Node n1 = new Node();
|
||||
Node n2 = new Node();
|
||||
g.addNode(n1);
|
||||
g.addNode(n2);
|
||||
assertThrows(IllegalStateException.class, () -> g.connect(n1, e, n2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectFromNodeNotContained() {
|
||||
DiGraph g = new DiGraph();
|
||||
Edge e = new Edge();
|
||||
Node n1 = new Node();
|
||||
Node n2 = new Node();
|
||||
g.addNode(n2);
|
||||
g.addEdge(e);
|
||||
assertThrows(IllegalStateException.class, () -> g.connect(n1, e, n2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectToNodeNotContained() {
|
||||
DiGraph g = new DiGraph();
|
||||
Edge e = new Edge();
|
||||
Node n1 = new Node();
|
||||
Node n2 = new Node();
|
||||
g.addNode(n1);
|
||||
g.addEdge(e);
|
||||
assertThrows(IllegalStateException.class, () -> g.connect(n1, e, n2));
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectSelfLoop() {
|
||||
DiGraph g = new DiGraph();
|
||||
Edge e = new Edge();
|
||||
Node n = new Node();
|
||||
g.addNode(n);
|
||||
g.addEdge(e);
|
||||
g.connect(n, e, n);
|
||||
assertEquals(1, g.getNodes().size());
|
||||
assertTrue(g.getNodes().contains(n));
|
||||
assertEquals(1, g.getEdges().size());
|
||||
assertSame(e, g.getEdges().get(0));
|
||||
assertSame(e, n.getOutEdges().get(0));
|
||||
assertSame(e, n.getInEdges().get(0));
|
||||
assertSame(n, e.getFrom());
|
||||
assertSame(n, e.getTo());
|
||||
}
|
||||
|
||||
@Test
|
||||
void connectSameEdgeTwice() {
|
||||
DiGraph g = new DiGraph();
|
||||
Edge e = new Edge();
|
||||
Node n1 = new Node();
|
||||
Node n2 = new Node();
|
||||
g.addNode(n1);
|
||||
g.addNode(n2);
|
||||
g.addEdge(e);
|
||||
g.connect(n1, e, n2);
|
||||
assertThrows(IllegalStateException.class, () -> g.connect(n1, e, n2));
|
||||
}
|
||||
|
||||
}
|
46
test/ch/usi/inf/sp/graph/EdgeTest.java
Normal file
46
test/ch/usi/inf/sp/graph/EdgeTest.java
Normal file
|
@ -0,0 +1,46 @@
|
|||
package ch.usi.inf.sp.graph;
|
||||
|
||||
import ch.usi.inf.sp.graph.Edge;
|
||||
import ch.usi.inf.sp.graph.Node;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class EdgeTest {
|
||||
|
||||
@Test
|
||||
void newEdge() {
|
||||
Edge e = new Edge();
|
||||
assertNull(e.getFrom());
|
||||
assertNull(e.getTo());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringTest() {
|
||||
Node n1 = new Node();
|
||||
Node n2 = new Node();
|
||||
Edge e = new Edge();
|
||||
e.setFrom(n1);
|
||||
e.setTo(n2);
|
||||
assertEquals(n1.toString()+" -> "+n2.toString(), e.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setFrom() {
|
||||
Edge e = new Edge();
|
||||
Node n = new Node();
|
||||
e.setFrom(n);
|
||||
assertEquals(n, e.getFrom());
|
||||
assertNull(e.getTo());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setTo() {
|
||||
Edge e = new Edge();
|
||||
Node n = new Node();
|
||||
e.setTo(n);
|
||||
assertEquals(n, e.getTo());
|
||||
assertNull(e.getFrom());
|
||||
}
|
||||
|
||||
}
|
42
test/ch/usi/inf/sp/graph/NodeTest.java
Normal file
42
test/ch/usi/inf/sp/graph/NodeTest.java
Normal file
|
@ -0,0 +1,42 @@
|
|||
package ch.usi.inf.sp.graph;
|
||||
|
||||
import ch.usi.inf.sp.graph.Edge;
|
||||
import ch.usi.inf.sp.graph.Node;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class NodeTest {
|
||||
|
||||
@Test
|
||||
void newNode() {
|
||||
Node n = new Node();
|
||||
assertEquals(0, n.getInEdges().size());
|
||||
assertEquals(0, n.getOutEdges().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringTest() {
|
||||
Node n = new Node();
|
||||
assertEquals('"'+n.getClass().getName()+"@"+Integer.toHexString(n.hashCode())+'"', n.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void addInEdge() {
|
||||
Node n = new Node();
|
||||
Edge e = new Edge();
|
||||
n.addInEdge(e);
|
||||
assertEquals(1, n.getInEdges().size());
|
||||
assertSame(e, n.getInEdges().get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void addOutEdge() {
|
||||
Node n = new Node();
|
||||
Edge e = new Edge();
|
||||
n.addOutEdge(e);
|
||||
assertEquals(1, n.getOutEdges().size());
|
||||
assertSame(e, n.getOutEdges().get(0));
|
||||
}
|
||||
|
||||
}
|
229
test/ch/usi/inf/sp/graph/TraversalTest.java
Normal file
229
test/ch/usi/inf/sp/graph/TraversalTest.java
Normal file
|
@ -0,0 +1,229 @@
|
|||
package ch.usi.inf.sp.graph;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class TraversalTest {
|
||||
|
||||
//--- simple DiGraph, Node, and Edge implementations just for testing
|
||||
private static class TG extends DiGraph<TN, TE> {
|
||||
private TN root;
|
||||
public TG(TN root) { this.root = root; }
|
||||
public TN getRoot() { return root; }
|
||||
public void edge(TN from, TN to) {
|
||||
if (!getNodes().contains(from)) addNode(from);
|
||||
if (!getNodes().contains(to)) addNode(to);
|
||||
TE edge = new TE();
|
||||
addEdge(edge);
|
||||
connect(from, edge, to);
|
||||
}
|
||||
}
|
||||
private static class TN extends Node<TE> {
|
||||
private String label;
|
||||
public TN(String label) { this.label = label; }
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
private static class TE extends Edge<TN> {}
|
||||
|
||||
|
||||
@Test
|
||||
void getNodesInPostOrderTwoNodes() {
|
||||
TN a = new TN("A");
|
||||
TN b = new TN("B");
|
||||
TG graph = new TG(a);
|
||||
graph.edge(a, b);
|
||||
//System.out.print(graph);
|
||||
|
||||
List<TN> postOrderNodes = Traversal.getNodesInPostOrder(graph, graph.getRoot());
|
||||
//System.out.println("post order: "+postOrderNodes);
|
||||
assertEquals(2, postOrderNodes.size());
|
||||
assertIterableEquals(Arrays.asList(b, a), postOrderNodes);
|
||||
|
||||
List<TN> reversePostOrderNodes = Traversal.getNodesInReversePostOrder(graph, graph.getRoot());
|
||||
//System.out.println("reverse post order: "+reversePostOrderNodes);
|
||||
assertEquals(2, reversePostOrderNodes.size());
|
||||
assertIterableEquals(Arrays.asList(a, b), reversePostOrderNodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNodesInPostOrderThreeNodes() {
|
||||
TN a = new TN("A");
|
||||
TN b = new TN("B");
|
||||
TN c = new TN("C");
|
||||
TG graph = new TG(a);
|
||||
graph.edge(a, b);
|
||||
graph.edge(b, c);
|
||||
//System.out.print(graph);
|
||||
|
||||
List<TN> postOrderNodes = Traversal.getNodesInPostOrder(graph, graph.getRoot());
|
||||
//System.out.println("post order: "+postOrderNodes);
|
||||
assertEquals(3, postOrderNodes.size());
|
||||
assertIterableEquals(Arrays.asList(new TN[] {c,b,a}), postOrderNodes);
|
||||
|
||||
List<TN> reversePostOrderNodes = Traversal.getNodesInReversePostOrder(graph, graph.getRoot());
|
||||
//System.out.println("reverse post order: "+reversePostOrderNodes);
|
||||
assertEquals(3, reversePostOrderNodes.size());
|
||||
assertIterableEquals(Arrays.asList(new TN[] {a,b,c}), reversePostOrderNodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNodesInPostOrderDiamond() {
|
||||
TN a = new TN("A");
|
||||
TN b = new TN("B");
|
||||
TN c = new TN("C");
|
||||
TN d = new TN("D");
|
||||
TG graph = new TG(a);
|
||||
graph.edge(a, b);
|
||||
graph.edge(a, c);
|
||||
graph.edge(b, d);
|
||||
graph.edge(c, d);
|
||||
//System.out.print(graph);
|
||||
|
||||
List<TN> postOrderNodes = Traversal.getNodesInPostOrder(graph, graph.getRoot());
|
||||
//System.out.println("post order: "+postOrderNodes);
|
||||
assertEquals(4, postOrderNodes.size());
|
||||
assertSame(d, postOrderNodes.get(0));
|
||||
assertSame(a, postOrderNodes.get(3));
|
||||
|
||||
List<TN> reversePostOrderNodes = Traversal.getNodesInReversePostOrder(graph, graph.getRoot());
|
||||
//System.out.println("reverse post order: "+reversePostOrderNodes);
|
||||
assertEquals(4, reversePostOrderNodes.size());
|
||||
assertSame(d, reversePostOrderNodes.get(3));
|
||||
assertSame(a, reversePostOrderNodes.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNodesInPostOrderExample() {
|
||||
// example graph from
|
||||
// https://eli.thegreenplace.net/2015/directed-graph-traversal-orderings-and-applications-to-data-flow-analysis/
|
||||
TN a = new TN("A");
|
||||
TN b = new TN("B");
|
||||
TN c = new TN("C");
|
||||
TN d = new TN("D");
|
||||
TN e = new TN("E");
|
||||
TN t = new TN("T");
|
||||
TG graph = new TG(a);
|
||||
graph.edge(a, c);
|
||||
graph.edge(a, b);
|
||||
graph.edge(a, t);
|
||||
graph.edge(c, b);
|
||||
graph.edge(t, b);
|
||||
graph.edge(c, e);
|
||||
graph.edge(e, d);
|
||||
graph.edge(b, d);
|
||||
//System.out.print(graph);
|
||||
|
||||
List<TN> postOrderNodes = Traversal.getNodesInPostOrder(graph, graph.getRoot());
|
||||
// this assertion is overly strict: it assumes the traversal algorithm iterates forward through the out edges
|
||||
//System.out.println("post order: "+postOrderNodes);
|
||||
assertEquals(6, postOrderNodes.size());
|
||||
assertIterableEquals(Arrays.asList(new TN[] {d,b,e,c,t,a}), postOrderNodes);
|
||||
|
||||
List<TN> reversePostOrderNodes = Traversal.getNodesInReversePostOrder(graph, graph.getRoot());
|
||||
// this assertion is overly strict: it assumes the traversal algorithm iterates forward through the out edges
|
||||
//System.out.println("reverse post order: "+reversePostOrderNodes);
|
||||
assertEquals(6, reversePostOrderNodes.size());
|
||||
assertIterableEquals(Arrays.asList(new TN[] {a,t,c,e,b,d}), reversePostOrderNodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNodesInPostOrderExample2() {
|
||||
// example graph from
|
||||
// Christensen's MS thesis, Figure 2.2
|
||||
TN a = new TN("A");
|
||||
TN b = new TN("B");
|
||||
TN c = new TN("C");
|
||||
TN d = new TN("D");
|
||||
TN e = new TN("E");
|
||||
TN f = new TN("F");
|
||||
TG graph = new TG(a);
|
||||
graph.edge(a, b);
|
||||
graph.edge(a, d);
|
||||
graph.edge(b, c);
|
||||
graph.edge(d, e);
|
||||
graph.edge(d, f);
|
||||
graph.edge(e, b);
|
||||
graph.edge(f, e);
|
||||
//System.out.print(graph);
|
||||
|
||||
List<TN> postOrderNodes = Traversal.getNodesInPostOrder(graph, graph.getRoot());
|
||||
// this assertion is overly strict: it assumes the traversal algorithm iterates forward through the out edges
|
||||
//System.out.println("post order: "+postOrderNodes);
|
||||
assertIterableEquals(Arrays.asList(new TN[] {c,b,e,f,d,a}), postOrderNodes);
|
||||
|
||||
List<TN> reversePostOrderNodes = Traversal.getNodesInReversePostOrder(graph, graph.getRoot());
|
||||
// this assertion is overly strict: it assumes the traversal algorithm iterates forward through the out edges
|
||||
//System.out.println("reverse post order: "+reversePostOrderNodes);
|
||||
assertIterableEquals(Arrays.asList(new TN[] {a,d,f,e,b,c}), reversePostOrderNodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNodesInPostOrderExample3() {
|
||||
// example graph from
|
||||
// Cooper et al., Figure 2
|
||||
TN n1 = new TN("1");
|
||||
TN n2 = new TN("2");
|
||||
TN n3 = new TN("3");
|
||||
TN n4 = new TN("4");
|
||||
TN n5 = new TN("5");
|
||||
TG graph = new TG(n5);
|
||||
graph.edge(n5, n3);
|
||||
graph.edge(n5, n4);
|
||||
graph.edge(n3, n2);
|
||||
graph.edge(n2, n1);
|
||||
graph.edge(n1, n2);
|
||||
graph.edge(n4, n1);
|
||||
//System.out.print(graph);
|
||||
|
||||
List<TN> postOrderNodes = Traversal.getNodesInPostOrder(graph, graph.getRoot());
|
||||
// this assertion is overly strict: it assumes the traversal algorithm iterates forward through the out edges
|
||||
//System.out.println("post order: "+postOrderNodes);
|
||||
assertIterableEquals(Arrays.asList(new TN[] {n1,n2,n3,n4,n5}), postOrderNodes);
|
||||
|
||||
List<TN> reversePostOrderNodes = Traversal.getNodesInReversePostOrder(graph, graph.getRoot());
|
||||
// this assertion is overly strict: it assumes the traversal algorithm iterates forward through the out edges
|
||||
//System.out.println("reverse post order: "+reversePostOrderNodes);
|
||||
assertIterableEquals(Arrays.asList(new TN[] {n5,n4,n3,n2,n1}), reversePostOrderNodes);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNodesInPostOrderExample4() {
|
||||
// example graph from
|
||||
// Cooper et al., Figure 4
|
||||
TN n1 = new TN("1");
|
||||
TN n2 = new TN("2");
|
||||
TN n3 = new TN("3");
|
||||
TN n4 = new TN("4");
|
||||
TN n5 = new TN("5");
|
||||
TN n6 = new TN("6");
|
||||
TG graph = new TG(n6);
|
||||
graph.edge(n6, n4);
|
||||
graph.edge(n6, n5);
|
||||
graph.edge(n4, n3);
|
||||
graph.edge(n4, n2);
|
||||
graph.edge(n2, n1);
|
||||
graph.edge(n2, n3);
|
||||
graph.edge(n3, n2);
|
||||
graph.edge(n5, n1);
|
||||
graph.edge(n1, n2);
|
||||
//System.out.print(graph);
|
||||
|
||||
List<TN> postOrderNodes = Traversal.getNodesInPostOrder(graph, graph.getRoot());
|
||||
// this assertion is overly strict: it assumes the traversal algorithm iterates forward through the out edges
|
||||
//System.out.println("post order: "+postOrderNodes);
|
||||
assertIterableEquals(Arrays.asList(new TN[] {n1,n2,n3,n4,n5,n6}), postOrderNodes);
|
||||
|
||||
List<TN> reversePostOrderNodes = Traversal.getNodesInReversePostOrder(graph, graph.getRoot());
|
||||
// this assertion is overly strict: it assumes the traversal algorithm iterates forward through the out edges
|
||||
//System.out.println("reverse post order: "+reversePostOrderNodes);
|
||||
assertIterableEquals(Arrays.asList(new TN[] {n6,n5,n4,n3,n2,n1}), reversePostOrderNodes);
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue