package sds.assemble.controlflow;

import sds.assemble.LineInstructions;
import sds.classfile.bytecode.BranchOpcode;

import static sds.assemble.controlflow.DominatorNodeSearcher.searchCommon;

/**
 * This builder class is for control flow graph.<br>
 * This class is designed singleton.
 * @author inagaki
 */
public class CFGBuilder {
	private static CFGBuilder builder = null;

	/**
	 * returns own instance.
	 * @return instance
	 */
	public static CFGBuilder getInstance() {
		if(builder == null) {
			builder = new CFGBuilder();
		}
		return builder;
	}

	/**
	 * returns control flow graph.
	 * @param inst method's instructions
	 * @return control flow graph
	 */
	public CFNode[] build(LineInstructions[] inst) {
		/** create nodes **/
		CFNode[] nodes = new CFNode[inst.length];
		for(int i = 0; i < nodes.length; i++) {
			nodes[i] = new CFNode(inst[i]);
		}

		/** set parent and child **/
		int index = 0;
		for(CFNode n : nodes) {
			if(index > 0) {
				if(nodes[index-1].getType() != CFNodeType.Exit
				&& nodes[index-1].getType() != CFNodeType.LoopExit) {
					n.addParent(nodes[index-1]);
					nodes[index-1].addChild(n);
				}
			} else if(index == nodes.length-1) {
				if(n.getType() != CFNodeType.Exit && n.getType() != CFNodeType.LoopExit) {
					n.addChild(nodes[index]);
					nodes[index].addParent(n);
				}
			}
			index++;
		}

		/** set jump point node **/
		index = 0;
		for(CFNode n : nodes) {
			if(n.getEnd() instanceof BranchOpcode) {
				BranchOpcode op = (BranchOpcode)n.getEnd();
				int jumpPoint = op.getBranch() + op.getPc();
				if(n.getType() == CFNodeType.Exit
				|| n.getType() == CFNodeType.Entry
				|| n.getType() == CFNodeType.LoopEntry) {
					for(int i = index; i < nodes.length; i++) {
						if(nodes[i].isInPcRange(jumpPoint)) {
							n.addChild(nodes[i]);
							nodes[i].addParent(n);
							break;
						}
					}
				} else if(n.getType() == CFNodeType.LoopExit) {
					System.out.println(jumpPoint);
					for(int i = 0; i <= index; i++) {
						if(nodes[i].isInPcRange(jumpPoint)) {
							nodes[i].nodeType = CFNodeType.LoopEntry;
							n.addChild(nodes[i]);
							break;
						}
					}
				}
			}
			index++;
		}

		/** decide dominator **/
		for(int i = nodes.length - 1; i > 0; i--) {
			if(nodes[i].getParents().size() == 1) { // immediate dominator
				CFNode n = nodes[i].getParents().iterator().next().getDest();
				nodes[i].setImmediateDominator(n);
			} else if(nodes[i].getParents().size() > 1) { // dominator
				CFEdge[] parents = nodes[i].getParents().toArray(new CFEdge[0]);
				CFNode cand = null;
				for(int j = 0; j < parents.length; j++) {
					if(j < parents.length - 1) {
						if(cand == null) {
							cand = searchCommon(parents[j], parents[j+1]);
						} else {
							cand = searchCommon(cand, parents[j+1]);
						}
					}
				}
				nodes[i].setDominator(cand);
			}
		}
		return nodes;
	}
}

@@ -1422,150 +1422,8 @@
 ) %7B%0A
-%09%09%09if(n.getEnd() instanceof BranchOpcode) %7B%0A%09%09%09%09BranchOpcode op = (BranchOpcode)n.getEnd();%0A%09%09%09%09int jumpPoint = op.getBranch() + op.getPc();%0A%09
 %09%09%09i
@@ -1454,25 +1454,24 @@
 ype.Exit%0A%09%09%09
-%09
 %7C%7C n.getType
@@ -1493,17 +1493,16 @@
 e.Entry%0A
-%09
 %09%09%09%7C%7C n.
@@ -1534,25 +1534,24 @@
 oopEntry) %7B%0A
-%09
 %09%09%09%09for(int 
@@ -1583,33 +1583,32 @@
 th; i++) %7B%0A%09%09%09%09%09
-%09
 if(nodes%5Bi%5D.isIn
@@ -1611,33 +1611,38 @@
 isInPcRange(
-j
+n.getJ
 umpPoint
 )) %7B%0A%09%09%09%09%09%09%09
@@ -1625,30 +1625,31 @@
 getJumpPoint
+()
 )) %7B%0A
-%09
 %09%09%09%09%09%09n.addC
@@ -1666,25 +1666,24 @@
 %5Bi%5D);%0A%09%09%09%09%09%09
-%09
 nodes%5Bi%5D.add
@@ -1695,33 +1695,31 @@
 t(n);%0A%09%09%09%09%09%09
-%09
 break;%0A
-%09
 %09%09%09%09%09%7D%0A%09%09%09%09%09
@@ -1713,28 +1713,26 @@
 %0A%09%09%09%09%09%7D%0A%09%09%09%09
-%09
 %7D%0A
-%09
 %09%09%09%7D else if
@@ -1774,45 +1774,8 @@
 ) %7B%0A
-%09%09%09%09%09System.out.println(jumpPoint);%0A%09
 %09%09%09%09
@@ -1792,17 +1792,16 @@
 = 0; i %3C
-=
  index; 
@@ -1803,25 +1803,24 @@
 dex; i++) %7B%0A
-%09
 %09%09%09%09%09if(node
@@ -1840,17 +1840,22 @@
 nge(
-j
+n.getJ
 umpPoint
 )) %7B
@@ -1850,22 +1850,23 @@
 umpPoint
+()
 )) %7B%0A
-%09
 %09%09%09%09%09%09no
@@ -1907,25 +1907,24 @@
 ntry;%0A%09%09%09%09%09%09
-%09
 n.addChild(n
@@ -1943,24 +1943,15 @@
 %09%09%09%09
-%09
 break;%0A
-%09%09%09%09%09%09%7D%0A
 %09%09%09%09

fix #10.