package s3.services.simplejit;

import ovm.core.domain.Blueprint;
import ovm.core.domain.Code;
import ovm.core.domain.Oop;
import ovm.core.execution.Activation;
import ovm.core.execution.Context;
import ovm.core.execution.ExceptionTableActivation;
import ovm.core.execution.NativeInterface;
import ovm.core.services.io.BasicIO;
import ovm.core.services.memory.VM_Address;
import s3.util.PragmaNoInline;

public class JitActivation extends ExceptionTableActivation {
    static final class Native implements NativeInterface {
	/* architecture-specific library routines */
	native static void setCallerPC(int a, VM_Address pc);

	/** Gets the ABSOLUTE pc (not relative to the code fragment).  **/
	// Warning: this PC actually points to the next instruction to
	// execute in SimpleJIT
	native static VM_Address getCallerPC(int a);

	native static int getCaller(int a);

	native static int getCurrentActivation(int skipCount);

	/* simplejit-specific support routines */
	static native VM_Address jit_getLocalAddress(int ctx, int act,
						     char index);
	static native VM_Address jit_getOperandAddress(int ctx, int act,
						       char index);
    
	native static void jit_setOpstackDepth(int ctx,
					       int act,
					       int newDepth);

	native static VM_Address jit_getCode(int a);

	static native int jit_topFrame(int nc);

	static native int jit_bottomFrame(int nc);

	static native void jit_cutToActivation(int nc, int callee);
    }
    int nc;
    int f;
    int callee;
    VM_Address absPC;

    public int getNativeHandle() { return f; }

    public Code getCode() {
	return (Code) Native.jit_getCode(f).asAnyObject();
    }

    public int getPC() {
	byte[] codeb = getCode().getBytes();
	VM_Address code = VM_Address.fromObject(codeb);
	Blueprint.Array bp = (Blueprint.Array)code.asOop().getBlueprint();
	    
	return absPC.diff( code).sub( bp.getFixedSize()).asInt();
    }

    protected void setToCurrent(int nc) throws PragmaNoInline {
	this.nc = nc;
	if (Context.getCurrentContext().nativeContextHandle_ == nc) {
	    // skip invoke_native()
	    this.f = Native.getCurrentActivation(1);
	    caller(this);
	} else {
	    // jit_topActivation returns ovmrt_run()
	    this.f = Native.jit_topFrame(nc);
	    caller(this);
	}
    }
    
    public Activation caller(Activation _act) {
	JitActivation act = (JitActivation) _act;
	act.callee = this.f;
	act.absPC = Native.getCallerPC(f);
	act.f = Native.getCaller(f);
	if (act.f == Native.jit_bottomFrame(nc))
	    return null;
	return act;
    }

    public Oop getLocal(int idx) {
	VM_Address addr = Native.jit_getLocalAddress(nc, f, (char) idx);
	return addr.getAddress().asOop();
    }

    public void setOperand(int idx, Oop value) {
	VM_Address addr = Native.jit_getOperandAddress(nc, f, (char) idx);
	addr.setAddress(VM_Address.fromObject(value));
    }

    public void setOperandStackDepth(int depth) {
        Native.jit_setOpstackDepth(nc, f, depth);
    }

    public void setPC(int pc) {
	byte[] codeb = getCode().getBytes();
        if (codeb == null)
            BasicIO.err.println("NULL code!");

        VM_Address addr = VM_Address.fromObject(codeb);
        Blueprint.Array bp = (Blueprint.Array)addr.asOop().getBlueprint();
        addr = addr.add( pc + bp.getFixedSize());
	absPC = addr;
	Native.setCallerPC(callee, absPC);
    }

    public void cutTo() {
	Native.jit_cutToActivation(nc, callee);
    }

    public static class Factory extends Activation.Factory {
	public Activation make() { return new JitActivation(); }
    }
}
