Initial commit
This commit is contained in:
commit
74a433f602
|
@ -0,0 +1,2 @@
|
|||
# Default ignored files
|
||||
/workspace.xml
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
|
@ -0,0 +1,19 @@
|
|||
<component name="libraryTable">
|
||||
<library name="asm-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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
9
|
||||
README.md,8/e/8ec9a00bfd09b3190ac6b22251dbb1aa95a0579d
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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+"\"";
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package ch.usi.inf.sp.dom;
|
||||
|
||||
import ch.usi.inf.sp.graph.Edge;
|
||||
|
||||
|
||||
public class DominanceEdge extends Edge<DominatorTreeNode> {
|
||||
|
||||
}
|
|
@ -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(...)
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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()+")\"";
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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()+'"';
|
||||
}
|
||||
|
||||
}
|
|
@ -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(...)
|
||||
|
||||
}
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
// A minimal class, with one "method" (the constructor)
|
||||
public class Mini {
|
||||
|
||||
public Mini() {
|
||||
super();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue