package s3.services.simplejit.ir;

import ovm.core.repository.TypeCodes;
import ovm.services.bytecode.Instruction;
import ovm.services.bytecode.InstructionBuffer;
import ovm.services.bytecode.analysis.AbstractValue;
import ovm.services.bytecode.analysis.Frame;
import ovm.services.bytecode.analysis.WorkList;
import ovm.util.HashSet;
import s3.core.domain.S3Method;
import s3.services.bytecode.verifier.VerificationFixpointIterator;

public class SimpleJITBC2IRFixpointIterator 
    extends VerificationFixpointIterator {

    protected S3Method s3Method;
    protected SimpleJITBC2IR bc2ir;
    protected HashSet visitedBlocks;

    public SimpleJITBC2IRFixpointIterator(AbstractValue.Factory avf,
					  SimpleJITBC2IR bc2ir,
					  S3Method me,
					  InstructionBuffer code) {
	super(avf,
	      bc2ir,
	      me.getRepositoryMethod(),
	      code);
	this.bc2ir = bc2ir;
	this.s3Method = me;
	visitedBlocks = new HashSet();
	visitedBlocks.add(new Integer(0));
    }


    // Visit each block exactly once
    private void add(int pc, SimpleJITOperand.Stack ops) {
	Integer wpc = new Integer(pc);
	if (visitedBlocks.contains(wpc)) {
	    return;
	}
	visitedBlocks.add(wpc);
	add(pc, null, WorkList.NOMERGE);
	SimpleJITBasicBlock b = bc2ir.getBlockAt(pc);
	b.setOpStack(ops);
    }
    private void add(int pc) {
	Integer wpc = new Integer(pc);
	if (visitedBlocks.contains(wpc)) {
	    return;
	}
	visitedBlocks.add(wpc);
	add(pc, null, WorkList.NOMERGE);
    }

    public Instruction next() {
        WorkList.Entry curr = null;
	if (worklist.hasNext()) {
	    curr = worklist.remove();
	    bc2ir.setPC(curr.pc);
	    if (bc2ir.isBlockStart(curr.pc)) {
		bc2ir.setCurrentBlockAndOpStackByPC(curr.pc);
	    }
	    return buf.get(curr.pc);
	} else {
	    return null;
	}
    }

    public Frame makeInitialFrame() {
	return null;
    }

    public MergeStrategyVisitor makeStrategyVisitor() {
        return new SimpleJITBC2IRMergeStrategyVisitor();
    }

    private void addSuccessors() {
	SimpleJITBasicBlock b = 
	    bc2ir.getCurrentBlock();
	SimpleJITOperand.Stack op_stack =
	    bc2ir.getCurrentOpStack();
	SimpleJITBasicBlock[] succ =
	    b.getSuccessors();
	for(int i = 0; i < succ.length; i++) {
	    if (succ[i].isHandlerBlock()) {
		SimpleJITOperand.Stack ops = op_stack.copy();
		ops.clear();
		ops.push(bc2ir
			 .makeStackTemporary(TypeCodes.REFERENCE, 0));
		add(succ[i].getEntryPC(), ops);
	    } else
		add(succ[i].getEntryPC(), op_stack.copy());
	}
    }

    public class SimpleJITBC2IRMergeStrategyVisitor 
	extends MergeStrategyVisitor {

        public void visit(Instruction i) {
	    int next_pc = ai.getPC() + i.size(ai.getPosition());
	    if (bc2ir.isBlockStart(next_pc))
		addSuccessors();
	    else
		add(next_pc);
        }

        public void visit(Instruction.FlowEnd i) {
        }

        public void visit(Instruction.ATHROW i) {
	    addSuccessors();
        }

        public void visit(Instruction.UnconditionalJump i) {
            addSuccessors();
        }

        public void visit(Instruction.ConditionalJump i) {
	    addSuccessors();
        }

        public void visit(Instruction.Switch i) {
	    addSuccessors();
        }

        public void visit(Instruction.RET i) {
	    addSuccessors();
        }
    }
}
