package s3.services.simplejit.x86;

import ovm.util.ByteBuffer;
import ovm.util.HTString2int;
import s3.services.simplejit.CodeLinker;

/**
 * @author Hiroshi Yamauchi
 **/
public class CodeLinkerImpl extends CodeLinker {

    public CodeLinkerImpl(int[] bytecodePC2NativePC) {
	super(bytecodePC2NativePC);
    }

    protected RelativeJump makeRelativeJump(int branchPC,
                        int shift,
                        int bits,
					    int targetBytecodePC) {
	return new RelativeJumpImpl(branchPC, shift, bits,
				    targetBytecodePC);
    }
	
    protected AbsoluteRef2BC makeAbsoluteRef2BC(int refererPC,
						int refereeBytecodePC) {
	return new AbsoluteRef2BCImpl(refererPC,
				      refereeBytecodePC);
    }

    protected AbsoluteRef makeAbsoluteRef(int refererPC,
					  int refereePC) {
	return new AbsoluteRefImpl(refererPC,
				   refereePC);
    }

    protected Call makeCall(int callSitePC,
			    String functionSymbol) {
	return new CallImpl(callSitePC,
			    functionSymbol);
    }

    private static void writeInt(byte[] data, int index, int value) {
	byte lowest =   (byte)((value >>> 0) & 0xff);
	byte low =      (byte)((value >>> 8) & 0xff);
	byte high =     (byte)((value >>> 16) & 0xff);
	byte highest =  (byte)((value >>> 24) & 0xff);
	data[index + 0] = lowest;
	data[index + 1] = low;
	data[index + 2] = high;
	data[index + 3] = highest;
    }

    public static class RelativeJumpImpl extends RelativeJump {
	private RelativeJumpImpl(int branchPC, int shift, int bits,
				 int targetBytecodePC) {
	    super(branchPC, shift, bits, targetBytecodePC);
	}
	public void patch(byte[] code, int[] bytecodePC2NativePC) {
	    int targetNativePC = bytecodePC2NativePC[targetBytecodePC];
	    writeInt(code, branchPC, targetNativePC - branchPC - 4);
	}
	public void patch(ByteBuffer code, int[] bytecodePC2NativePC) {
	    int targetNativePC = bytecodePC2NativePC[targetBytecodePC];
        // shift and bits are ignored for now under x86
	    code.putInt(branchPC, targetNativePC - branchPC - 4);
	}
    }

    public static class AbsoluteRef2BCImpl
	extends AbsoluteRef2BC {
	private AbsoluteRef2BCImpl(int refererPC,
				   int refereeBytecodePC) {
	    super(refererPC,
		  refereeBytecodePC);
	}

	public void patch(byte[] code, int baseAddress, int[] bytecodePC2NativePC) {
	    int refereeNativePC = bytecodePC2NativePC[refereeBytecodePC];
	    writeInt(code, refererPC, refereeNativePC + baseAddress);
	}
	public void patch(ByteBuffer code, int baseAddress, int[] bytecodePC2NativePC) {
	    int refereeNativePC = bytecodePC2NativePC[refereeBytecodePC];
	    code.putInt(refererPC, refereeNativePC + baseAddress);
	}
    }

    public static class AbsoluteRefImpl
	extends AbsoluteRef {
	private AbsoluteRefImpl(int refererPC,
				int refereePC) {
	    super(refererPC,
		  refereePC);
	}

	public void patch(byte[] code, int baseAddress) {
	    writeInt(code, refererPC, refereePC + baseAddress);
	}
	public void patch(ByteBuffer code, int baseAddress) {
	    code.putInt(refererPC, refereePC + baseAddress);
	}
    }

    public static class CallImpl
	extends Call {
	private CallImpl(int callSitePC,
			 String functionSymbol) {
	    super(callSitePC, functionSymbol);
	}
	public void patch(byte[] code, int baseAddress, HTString2int functionPointers) {
	    int addr = functionPointers.get(functionSymbol);
	    writeInt(code, callSitePC, addr - (callSitePC + baseAddress) - 4);
	}
	public void patch(ByteBuffer code, int baseAddress, HTString2int functionPointers) {
	    int addr = functionPointers.get(functionSymbol);
	    code.putInt(callSitePC, addr - (callSitePC + baseAddress) - 4);
	}
    }

}
