/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.jni.ia32;
import java.lang.reflect.Constructor;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.MemberReference;
import org.jikesrvm.classloader.MethodReference;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.TypeReference;
import static org.jikesrvm.ia32.BaselineConstants.WORDSIZE;
import org.jikesrvm.jni.JNIEnvironment;
import org.jikesrvm.jni.JNIGenericHelpers;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.runtime.SysCall;
import org.jikesrvm.scheduler.RVMThread;
import org.vmmagic.pragma.NoInline;
import org.vmmagic.pragma.NoOptCompile;
import org.vmmagic.unboxed.Address;
/**
* Platform dependent utility functions called from JNIFunctions
* (cannot be placed in JNIFunctions because methods
* there are specially compiled to be called from native).
*
* @see org.jikesrvm.jni.JNIFunctions
*/
public abstract class JNIHelpers extends JNIGenericHelpers {
/**
* Common code shared by the JNI functions NewObjectA, NewObjectV, NewObject
* (object creation)
* @param cls class whose constructor is to be invoked
* @param methodID the method ID for a constructor
* @param argAddress where to find the arguments for the constructor
* @param isJvalue {@code true} if parameters are passed as a jvalue array
* @param isDotDotStyle {@code true} if the method uses varargs
* @return a new object created by the specified constructor
* @throws Exception when the reflective invocation of the constructor fails
*/
public static Object invokeInitializer(Class<?> cls, int methodID, Address argAddress, boolean isJvalue,
boolean isDotDotStyle) throws Exception {
// get the parameter list as Java class
MemberReference mr = MemberReference.getMemberRef(methodID);
TypeReference tr = java.lang.JikesRVMSupport.getTypeForClass(cls).getTypeRef();
MethodReference methodRef = MemberReference.findOrCreate(tr, mr.getName(), mr.getDescriptor()).asMethodReference();
RVMMethod mth = methodRef.resolve();
Constructor<?> constMethod = java.lang.reflect.JikesRVMSupport.createConstructor(mth);
if (!mth.isPublic()) {
constMethod.setAccessible(true);
}
// Package the parameters for the constructor
Address varargAddress;
if (isDotDotStyle) {
// flag is false because this JNI function has 3 args before the var args
varargAddress = getVarArgAddress(false);
} else {
varargAddress = argAddress;
}
Object[] argObjs;
if (isJvalue) {
argObjs = packageParametersFromJValuePtr(methodRef, argAddress);
} else {
argObjs = packageParameterFromVarArg(methodRef, varargAddress);
}
// construct the new object
return constMethod.newInstance(argObjs);
}
/**
* Common code shared by the JNI functions CallStatic<type>Method
* (static method invocation)
* @param methodID the method ID
* @param expectReturnType the return type of the method to be invoked
* @return an object that may be the return object or a wrapper for the primitive return value
* @throws Exception if the return type doesn't match the expected return type
*/
@NoInline
@NoOptCompile
// expect a certain stack frame structure
public static Object invokeWithDotDotVarArg(int methodID, TypeReference expectReturnType) throws Exception {
MethodReference mr = MemberReference.getMethodRef(methodID);
Address varargAddress = getVarArgAddress(false);
Object[] argObjectArray = packageParameterFromVarArg(mr, varargAddress);
return callMethod(null, mr, argObjectArray, expectReturnType, true);
}
/**
* Common code shared by the JNI functions Call<type>Method
* (virtual method invocation)
* @param obj the object instance
* @param methodID the method ID
* @param expectReturnType the return type for checking purpose
* @param skip4Args true if the calling JNI Function takes 4 args before the vararg
* false if the calling JNI Function takes 3 args before the vararg
* @return an object that may be the return object or a wrapper for the primitive return value
* @throws Exception if the return type doesn't match the expected return type
*/
@NoInline
@NoOptCompile
// expect a certain stack frame structure
public static Object invokeWithDotDotVarArg(Object obj, int methodID, TypeReference expectReturnType,
boolean skip4Args) throws Exception {
MethodReference mr = MemberReference.getMethodRef(methodID);
Address varargAddress = getVarArgAddress(skip4Args);
Object[] argObjectArray = packageParameterFromVarArg(mr, varargAddress);
return callMethod(obj, mr, argObjectArray, expectReturnType, skip4Args);
}
/**
* This method supports var args passed from C.<p>
*
* In the Linux Intel C convention, the caller places the args immediately above the
* saved return address, starting with the first arg. <br>
*
* For the JNI functions that takes var args, their prolog code will save the
* var arg in the glue frame because the values in the register may be lost by
* subsequent calls. <br>
*
* This method copies the var arg values that were saved earlier in glue frame into
* the spill area of the original caller, thereby doing the work that the callee
* normally performs in the AIX C convention. <br>
*
* NOTE: This method contains internal stack pointer.
* For now we assume that the stack will not be relocatable while native code is running
* because native code can hold an address into the stack, so this code is OK,
* but this is an issue to be resolved later. <br>
*
* NOTE: this method assumes that it is immediately above the
* invokeWithDotDotVarArg frame, the JNI frame, the glue frame and
* the C caller frame in the respective order.
* Therefore, this method will not work if called from anywhere else.
*
* <pre>
* low address
*
* | fp | <- JNIEnvironment.getVarArgAddress
* | mid |
* | |
* | |
* |------|
* | fp | <- JNIEnvironment.invokeWithDotDotVarArg frame
* | mid |
* | ... |
* | |
* | |
* |------|
* | fp | <- JNI method frame
* | mid |
* | ... |
* | arg 0| args copied by JNI prolog (3 for static, nonvirtual,
* | arg 1| or 4 for virtual)
* | arg 2|
* | |
* | |
* |------|
* | fp | <- Native C caller frame
* |return|
* | arg 0|
* | arg 1|
* | arg 2|
* | arg 3|
* | arg 4|
* | arg 5|
* | arg 6|
* | arg 7|
* | arg 8|
* | arg 9|
* | |
* | |
* | |
*
*
* high address
* </pre>
*
* @param skip4Args if true, the calling JNI function has 4 args before the vararg
* if false, the calling JNI function has 3 args before the vararg
* @return the starting address of the vararg in the caller stack frame
*/
@NoInline
private static Address getVarArgAddress(boolean skip4Args) {
Address fp = Magic.getFramePointer();
fp = fp.loadAddress();
fp = fp.loadAddress();
return (fp.plus(2 * WORDSIZE + (skip4Args ? 4 * WORDSIZE : 3 * WORDSIZE)));
}
/**
* Common code shared by the JNI functions CallStatic<type>MethodV
* @param methodID the method ID
* @param argAddress a raw address for the variable argument list
* @param expectReturnType the return type of the method to be invoked
* @return an object that may be the return object or a wrapper for the primitive return value
* @throws Exception if the return type doesn't match the expected return type
*/
public static Object invokeWithVarArg(int methodID, Address argAddress, TypeReference expectReturnType)
throws Exception {
MethodReference mr = MemberReference.getMethodRef(methodID);
Object[] argObjectArray = packageParameterFromVarArg(mr, argAddress);
return callMethod(null, mr, argObjectArray, expectReturnType, true);
}
/**
* Common code shared by the JNI functions Call<type>MethodV
* @param obj the object instance
* @param methodID the method ID
* @param argAddress a raw address for the variable argument list
* @param expectReturnType the return type for checking purpose
* @param skip4Args received from the JNI function, passed on to Reflection.invoke()
* @return an object that may be the return object or a wrapper for the primitive return value
* @throws Exception if the return type doesn't match the expected return type
*/
public static Object invokeWithVarArg(Object obj, int methodID, Address argAddress, TypeReference expectReturnType,
boolean skip4Args) throws Exception {
MethodReference mr = MemberReference.getMethodRef(methodID);
Object[] argObjectArray = packageParameterFromVarArg(mr, argAddress);
return callMethod(obj, mr, argObjectArray, expectReturnType, skip4Args);
}
/**
* Repackage the arguments passed as a variable argument list into an array of Object,
* used by the JNI functions CallStatic<type>MethodV
* @param targetMethod The target {@link RVMMethod}
* @param argAddress an address into the C space for the array of jvalue unions;
* each element is 2-word and holds the argument of the appropriate type
* @return an Object array holding the arguments wrapped at Objects
*/
static Object[] packageParameterFromVarArg(MethodReference targetMethod, Address argAddress) {
TypeReference[] argTypes = targetMethod.getParameterTypes();
int argCount = argTypes.length;
Object[] argObjectArray = new Object[argCount];
Address vaListCopy = SysCall.sysCall.sysVaCopy(argAddress);
JNIEnvironment env = RVMThread.getCurrentThread().getJNIEnv();
for (int i = 0; i < argCount; i++) {
// convert and wrap the argument according to the expected type
if (argTypes[i].isReferenceType()) {
argObjectArray[i] = env.getJNIRef(SysCall.sysCall.sysVaArgJobject(vaListCopy));
} else if (argTypes[i].isIntType()) {
argObjectArray[i] = SysCall.sysCall.sysVaArgJint(vaListCopy);
} else if (argTypes[i].isLongType()) {
argObjectArray[i] = SysCall.sysCall.sysVaArgJlong(vaListCopy);
} else if (argTypes[i].isBooleanType()) {
argObjectArray[i] = SysCall.sysCall.sysVaArgJboolean(vaListCopy);
} else if (argTypes[i].isByteType()) {
argObjectArray[i] = SysCall.sysCall.sysVaArgJbyte(vaListCopy);
} else if (argTypes[i].isCharType()) {
argObjectArray[i] = SysCall.sysCall.sysVaArgJchar(vaListCopy);
} else if (argTypes[i].isShortType()) {
argObjectArray[i] = SysCall.sysCall.sysVaArgJshort(vaListCopy);
} else if (argTypes[i].isFloatType()) {
argObjectArray[i] = SysCall.sysCall.sysVaArgJfloat(vaListCopy);
} else {
if (VM.VerifyAssertions) VM._assert(argTypes[i].isDoubleType());
argObjectArray[i] = SysCall.sysCall.sysVaArgJdouble(vaListCopy);
}
}
SysCall.sysCall.sysVaEnd(vaListCopy);
return argObjectArray;
}
}