/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * 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/cpl1.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.VM_MemberReference; import org.jikesrvm.classloader.VM_Method; import org.jikesrvm.classloader.VM_TypeReference; import org.jikesrvm.jni.VM_JNIEnvironment; import org.jikesrvm.jni.VM_JNIFunctions; import org.jikesrvm.jni.VM_JNIGenericHelpers; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.runtime.VM_Reflection; import org.jikesrvm.scheduler.VM_Scheduler; import org.vmmagic.pragma.NoInline; import org.vmmagic.pragma.NoOptCompile; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.LocalAddress; /** * Platform dependent utility functions called from VM_JNIFunctions * (cannot be placed in VM_JNIFunctions because methods * there are specially compiled to be called from native). * * @see VM_JNIFunctions */ public abstract class VM_JNIHelpers extends VM_JNIGenericHelpers { /** * Common code shared by the JNI functions NewObjectA, NewObjectV, NewObject * (object creation) * @param methodID the method ID for a constructor * @return a new object created by the specified constructor */ public static Object invokeInitializer(Class<?> cls, int methodID, Address argAddress, boolean isJvalue, boolean isDotDotStyle) throws Exception { // get the parameter list as Java class VM_Method mth = VM_MemberReference.getMemberRef(methodID).asMethodReference().resolve(false); 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 = packageParameterFromJValue(mth, argAddress); } else { argObjs = packageParameterFromVarArg(mth, 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 */ @NoInline @NoOptCompile // expect a certain stack frame structure public static Object invokeWithDotDotVarArg(int methodID, VM_TypeReference expectReturnType) throws Exception { Address varargAddress = getVarArgAddress(false); return packageAndInvoke(null, methodID, varargAddress, expectReturnType, false, 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 */ @NoInline @NoOptCompile // expect a certain stack frame structure public static Object invokeWithDotDotVarArg(Object obj, int methodID, VM_TypeReference expectReturnType, boolean skip4Args) throws Exception { Address varargAddress = getVarArgAddress(skip4Args); return packageAndInvoke(obj, methodID, varargAddress, expectReturnType, skip4Args, true); } /** * This method supports var args passed from C. * * 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 | <- VM_JNIEnvironment.getVarArgAddress * | mid | * | | * | | * |------| * | fp | <- VM_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) { LocalAddress fp = VM_Magic.getFramePointer(); fp = fp.loadLocalAddress(); fp = fp.loadLocalAddress(); return VM_Magic.localAddressAsAddress((fp.plus(2 * 4 + (skip4Args ? 4 * 4 : 3 * 4)))); } /** * 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 * @return an object that may be the return object or a wrapper for the primitive return value */ public static Object invokeWithVarArg(int methodID, Address argAddress, VM_TypeReference expectReturnType) throws Exception { return packageAndInvoke(null, methodID, argAddress, expectReturnType, false, 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 VM_Reflection.invoke() * @return an object that may be the return object or a wrapper for the primitive return value */ public static Object invokeWithVarArg(Object obj, int methodID, Address argAddress, VM_TypeReference expectReturnType, boolean skip4Args) throws Exception { return packageAndInvoke(obj, methodID, argAddress, expectReturnType, skip4Args, true); } /** * Common code shared by the JNI functions CallStatic<type>MethodA * @param methodID id of VM_MemberReference * @param argAddress a raw address for the argument array * @return an object that may be the return object or a wrapper for the primitive return value */ public static Object invokeWithJValue(int methodID, Address argAddress, VM_TypeReference expectReturnType) throws Exception { return packageAndInvoke(null, methodID, argAddress, expectReturnType, false, false); } /** * Common code shared by the JNI functions Call<type>MethodA * @param obj the object instance * @param methodID id of VM_MemberReference * @param argAddress a raw address for the argument array * @param expectReturnType the return type for checking purpose * @param skip4Args received from the JNI function, passed on to VM_Reflection.invoke() * @return an object that may be the return object or a wrapper for the primitive return value */ public static Object invokeWithJValue(Object obj, int methodID, Address argAddress, VM_TypeReference expectReturnType, boolean skip4Args) throws Exception { return packageAndInvoke(obj, methodID, argAddress, expectReturnType, skip4Args, false); } /** * Common code shared by invokeWithJValue, invokeWithVarArg and invokeWithDotDotVarArg * @param obj the object instance * @param methodID id of VM_MemberReference * @param argAddress a raw address for the argument array * @param expectReturnType the return type for checking purpose * @param skip4Args This flag is received from the JNI function and passed directly to * VM_Reflection.invoke(). * It is true if the actual method is to be invoked, which could be * from the superclass. * It is false if the method from the real class of the object * is to be invoked, which may not be the actual method specified by methodID * @param isVarArg This flag describes whether the array of parameters is in var arg format or * jvalue format * @return an object that may be the return object or a wrapper for the primitive return value */ @NoInline @NoOptCompile // expect a certain stack frame structure static Object packageAndInvoke(Object obj, int methodID, Address argAddress, VM_TypeReference expectReturnType, boolean skip4Args, boolean isVarArg) throws Exception { VM_Method targetMethod = VM_MemberReference.getMemberRef(methodID).asMethodReference().resolve(false); VM_TypeReference returnType = targetMethod.getReturnType(); if (VM_JNIFunctions.traceJNI) { VM.sysWrite("JNI CallXXXMethod: (mid " + methodID + ") " + targetMethod.getDeclaringClass().toString() + "." + targetMethod.getName().toString() + "\n"); } if (expectReturnType == null) { // for reference return type if (!returnType.isReferenceType()) { throw new Exception("Wrong return type for method: expect reference type instead of " + returnType); } } else { // for primitive return type if (!returnType.definitelySame(expectReturnType)) { throw new Exception("Wrong return type for method: expect " + expectReturnType + " instead of " + returnType); } } // Repackage the arguments into an array of objects based on the signature of this method Object[] argObjectArray; if (isVarArg) { argObjectArray = packageParameterFromVarArg(targetMethod, argAddress); } else { argObjectArray = packageParameterFromJValue(targetMethod, argAddress); } // now invoke the method return VM_Reflection.invoke(targetMethod, obj, argObjectArray, 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 VM_Method} * @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(VM_Method targetMethod, Address argAddress) { VM_TypeReference[] argTypes = targetMethod.getParameterTypes(); int argCount = argTypes.length; Object[] argObjectArray = new Object[argCount]; Address addr = argAddress; for (int i = 0; i < argCount; i++) { int loword = addr.loadInt(); addr = addr.plus(4); // convert and wrap the argument according to the expected type if (argTypes[i].isFloatType()) { // NOTE: in VarArg convention, C compiler will expand a float to a double that occupy 2 words // so we have to extract it as a double and convert it back to a float int hiword = addr.loadInt(); addr = addr.plus(4); long doubleBits = (((long) hiword) << 32) | (loword & 0xFFFFFFFFL); argObjectArray[i] = VM_Reflection.wrapFloat((float) (Double.longBitsToDouble(doubleBits))); } else if (argTypes[i].isDoubleType()) { int hiword = addr.loadInt(); addr = addr.plus(4); long doubleBits = (((long) hiword) << 32) | (loword & 0xFFFFFFFFL); argObjectArray[i] = VM_Reflection.wrapDouble(Double.longBitsToDouble(doubleBits)); } else if (argTypes[i].isLongType()) { int hiword = addr.loadInt(); addr = addr.plus(4); long longValue = (((long) hiword) << 32) | (loword & 0xFFFFFFFFL); argObjectArray[i] = VM_Reflection.wrapLong(longValue); } else if (argTypes[i].isBooleanType()) { // the 0/1 bit is stored in the high byte argObjectArray[i] = VM_Reflection.wrapBoolean(loword); } else if (argTypes[i].isByteType()) { // the target byte is stored in the high byte argObjectArray[i] = VM_Reflection.wrapByte((byte) loword); } else if (argTypes[i].isCharType()) { // char is stored in the high 2 bytes argObjectArray[i] = VM_Reflection.wrapChar((char) loword); } else if (argTypes[i].isShortType()) { // short is stored in the high 2 bytes argObjectArray[i] = VM_Reflection.wrapShort((short) loword); } else if (argTypes[i].isReferenceType()) { // for object, the arg is a JREF index, dereference to get the real object VM_JNIEnvironment env = VM_Scheduler.getCurrentThread().getJNIEnv(); argObjectArray[i] = env.getJNIRef(loword); } else if (argTypes[i].isIntType()) { argObjectArray[i] = VM_Reflection.wrapInt(loword); } else { return null; } } return argObjectArray; } /** * Repackage the arguments passed as an array of jvalue into an array of Object, * used by the JNI functions CallStatic<type>MethodA * @param targetMethod The target {@link VM_Method} * @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[] packageParameterFromJValue(VM_Method targetMethod, Address argAddress) { VM_TypeReference[] argTypes = targetMethod.getParameterTypes(); int argCount = argTypes.length; Object[] argObjectArray = new Object[argCount]; // get the VM_JNIEnvironment for this thread in case we need to dereference any object arg VM_JNIEnvironment env = VM_Scheduler.getCurrentThread().getJNIEnv(); Address addr = argAddress; for (int i = 0; i < argCount; i++, addr = addr.plus(8)) { int loword = addr.loadInt(); int hiword; // convert and wrap the argument according to the expected type if (argTypes[i].isFloatType()) { argObjectArray[i] = VM_Reflection.wrapFloat(Float.intBitsToFloat(loword)); } else if (argTypes[i].isDoubleType()) { hiword = addr.plus(4).loadInt(); long doubleBits = (((long) hiword) << 32) | (loword & 0xFFFFFFFFL); argObjectArray[i] = VM_Reflection.wrapDouble(Double.longBitsToDouble(doubleBits)); } else if (argTypes[i].isLongType()) { hiword = addr.plus(4).loadInt(); long longValue = (((long) hiword) << 32) | (loword & 0xFFFFFFFFL); argObjectArray[i] = VM_Reflection.wrapLong(longValue); } else if (argTypes[i].isBooleanType()) { // the 0/1 bit is stored in the high byte argObjectArray[i] = VM_Reflection.wrapBoolean(loword & 0x000000FF); } else if (argTypes[i].isByteType()) { // the target byte is stored in the high byte argObjectArray[i] = VM_Reflection.wrapByte((byte) (loword & 0x000000FF)); } else if (argTypes[i].isCharType()) { // char is stored in the high 2 bytes argObjectArray[i] = VM_Reflection.wrapChar((char) (loword & 0x0000FFFF)); } else if (argTypes[i].isShortType()) { // short is stored in the high 2 bytes argObjectArray[i] = VM_Reflection.wrapShort((short) (loword & 0x0000FFFF)); } else if (argTypes[i].isReferenceType()) { // for object, the arg is a JREF index, dereference to get the real object argObjectArray[i] = env.getJNIRef(loword); } else if (argTypes[i].isIntType()) { argObjectArray[i] = VM_Reflection.wrapInt(loword); } else { return null; } } return argObjectArray; } }