package s3.services.transactions;
import ovm.core.repository.JavaNames;
import ovm.services.bytecode.Instruction;
import ovm.services.bytecode.InstructionBuffer;
import ovm.services.bytecode.editor.CodeFragmentEditor;
import ovm.services.bytecode.editor.Cursor;
import ovm.services.bytecode.editor.Marker;
import ovm.services.bytecode.editor.CodeFragmentEditor.ExceptionHandlerList;
import s3.core.domain.S3Blueprint;
import s3.services.bootimage.IRewriter;
/**
 * This rewriter takes a method annotated as Preemptible Atomic and adds
 * a prologue that starts a transaction and an epilogue the commits it.
 */
public class PARMethodRewriter extends IRewriter.RewriteVisitor {

    public PARMethodRewriter(IRewriter ir, S3Blueprint bp,  CodeFragmentEditor c) {
	ir.super(bp, false, false); cfe = c;
    }
  
    Marker startofmeth;
    Marker endofmeth;
    int lastPC;
    Marker startofexception;
    Instruction.ReturnInstruction returnI;
    /**
     * Returns are transformed into gotos to the epilogue.
     */
    public void visit(Instruction.ReturnInstruction r) {
	Cursor c = cfe.getCursorBeforeMarker(getPC());
	cfe.replaceInstruction().addGoto(endofmeth);
	returnI = r;
    }
    /**
     * Insert a Transaction.start() before the first instruction of the method.
     * The jump target Marker is before the method call, the exception range
     * starts after.
     */
    public void beginEditing(InstructionBuffer buf, CodeFragmentEditor cfe) {
	super.beginEditing(buf, cfe);
	// FIXME: there has to be a better way to get the last PC
	while (buf.hasRemaining()) buf.get();
	lastPC = buf.getPC();
	buf.rewind();
	Cursor c = cfe.getCursorBeforeMarker(0);
	startofmeth = c.addMarker();
	c.addINVOKESTATIC(Transaction.Transaction_start);
	startofexception = c.addMarker();
	endcursor = cfe.getCursorAfterMarker(lastPC);	
	endofmeth = endcursor.makeUnboundMarker();
    }
    Cursor endcursor;
    /**
     * Add a backbranch, followed by a call to commit and a return of the 
     * proper kind. The jump is the handler for any aborted exception.     
     */
    public void endEditing() {
	Cursor c = endcursor;
	Marker jump = c.addMarker();
	c.addSimpleInstruction(POP);
	c.addGoto(startofmeth);	
	Marker finallyMark = c.addMarker();
	c.addINVOKESTATIC(Transaction.Transaction_commit);	
	c.addSimpleInstruction(ATHROW);
	c.bindMarker(endofmeth);
	c.addINVOKESTATIC(Transaction.Transaction_commit);	
	
	c.addSimpleInstruction((returnI!=null)?returnI.getOpcode():RETURN);	
	
	ExceptionHandlerList handlers = cfe.getExceptionHandlers();
	handlers.insertBefore(startofexception, jump, jump, Transaction.ABORTED_EXCEPTION);
	handlers.insertBefore(startofexception, jump, finallyMark, JavaNames.java_lang_Throwable);
	super.endEditing();
    }
}