001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.jikesrvm.jni.ia32; 014 015 import java.lang.reflect.Constructor; 016 import org.jikesrvm.VM; 017 import org.jikesrvm.classloader.MemberReference; 018 import org.jikesrvm.classloader.RVMMethod; 019 import org.jikesrvm.classloader.TypeReference; 020 import static org.jikesrvm.ia32.BaselineConstants.WORDSIZE; 021 import org.jikesrvm.jni.JNIEnvironment; 022 import org.jikesrvm.jni.JNIFunctions; 023 import org.jikesrvm.jni.JNIGenericHelpers; 024 import org.jikesrvm.runtime.Magic; 025 import org.jikesrvm.runtime.Reflection; 026 import org.jikesrvm.scheduler.RVMThread; 027 import org.vmmagic.pragma.NoInline; 028 import org.vmmagic.pragma.NoOptCompile; 029 import org.vmmagic.unboxed.Address; 030 031 /** 032 * Platform dependent utility functions called from JNIFunctions 033 * (cannot be placed in JNIFunctions because methods 034 * there are specially compiled to be called from native). 035 * 036 * @see JNIFunctions 037 */ 038 public abstract class JNIHelpers extends JNIGenericHelpers { 039 040 /** 041 * Common code shared by the JNI functions NewObjectA, NewObjectV, NewObject 042 * (object creation) 043 * @param methodID the method ID for a constructor 044 * @return a new object created by the specified constructor 045 */ 046 public static Object invokeInitializer(Class<?> cls, int methodID, Address argAddress, boolean isJvalue, 047 boolean isDotDotStyle) throws Exception { 048 049 // get the parameter list as Java class 050 MemberReference mr = MemberReference.getMemberRef(methodID); 051 TypeReference tr = java.lang.JikesRVMSupport.getTypeForClass(cls).getTypeRef(); 052 RVMMethod mth = MemberReference.findOrCreate(tr, mr.getName(), mr.getDescriptor()).asMethodReference().resolve(); 053 054 Constructor<?> constMethod = java.lang.reflect.JikesRVMSupport.createConstructor(mth); 055 if (!mth.isPublic()) { 056 constMethod.setAccessible(true); 057 } 058 059 // Package the parameters for the constructor 060 Address varargAddress; 061 if (isDotDotStyle) { 062 // flag is false because this JNI function has 3 args before the var args 063 varargAddress = getVarArgAddress(false); 064 } else { 065 varargAddress = argAddress; 066 } 067 068 Object[] argObjs; 069 if (isJvalue) { 070 argObjs = packageParameterFromJValue(mth, argAddress); 071 } else { 072 argObjs = packageParameterFromVarArg(mth, varargAddress); 073 } 074 075 // construct the new object 076 return constMethod.newInstance(argObjs); 077 } 078 079 /** 080 * Common code shared by the JNI functions CallStatic<type>Method 081 * (static method invocation) 082 * @param methodID the method ID 083 * @param expectReturnType the return type of the method to be invoked 084 * @return an object that may be the return object or a wrapper for the primitive return value 085 */ 086 @NoInline 087 @NoOptCompile 088 // expect a certain stack frame structure 089 public static Object invokeWithDotDotVarArg(int methodID, TypeReference expectReturnType) throws Exception { 090 Address varargAddress = getVarArgAddress(false); 091 return packageAndInvoke(null, methodID, varargAddress, expectReturnType, false, true); 092 } 093 094 /** 095 * Common code shared by the JNI functions Call<type>Method 096 * (virtual method invocation) 097 * @param obj the object instance 098 * @param methodID the method ID 099 * @param expectReturnType the return type for checking purpose 100 * @param skip4Args true if the calling JNI Function takes 4 args before the vararg 101 * false if the calling JNI Function takes 3 args before the vararg 102 * @return an object that may be the return object or a wrapper for the primitive return value 103 */ 104 @NoInline 105 @NoOptCompile 106 // expect a certain stack frame structure 107 public static Object invokeWithDotDotVarArg(Object obj, int methodID, TypeReference expectReturnType, 108 boolean skip4Args) throws Exception { 109 110 Address varargAddress = getVarArgAddress(skip4Args); 111 return packageAndInvoke(obj, methodID, varargAddress, expectReturnType, skip4Args, true); 112 } 113 114 /** 115 * This method supports var args passed from C.<p> 116 * 117 * In the Linux Intel C convention, the caller places the args immediately above the 118 * saved return address, starting with the first arg. <br> 119 * 120 * For the JNI functions that takes var args, their prolog code will save the 121 * var arg in the glue frame because the values in the register may be lost by 122 * subsequent calls. <br> 123 * 124 * This method copies the var arg values that were saved earlier in glue frame into 125 * the spill area of the original caller, thereby doing the work that the callee 126 * normally performs in the AIX C convention. <br> 127 * 128 * NOTE: This method contains internal stack pointer. 129 * For now we assume that the stack will not be relocatable while native code is running 130 * because native code can hold an address into the stack, so this code is OK, 131 * but this is an issue to be resolved later. <br> 132 * 133 * NOTE: this method assumes that it is immediately above the 134 * invokeWithDotDotVarArg frame, the JNI frame, the glue frame and 135 * the C caller frame in the respective order. 136 * Therefore, this method will not work if called from anywhere else. 137 * 138 * <pre> 139 * low address 140 * 141 * | fp | <- JNIEnvironment.getVarArgAddress 142 * | mid | 143 * | | 144 * | | 145 * |------| 146 * | fp | <- JNIEnvironment.invokeWithDotDotVarArg frame 147 * | mid | 148 * | ... | 149 * | | 150 * | | 151 * |------| 152 * | fp | <- JNI method frame 153 * | mid | 154 * | ... | 155 * | arg 0| args copied by JNI prolog (3 for static, nonvirtual, 156 * | arg 1| or 4 for virtual) 157 * | arg 2| 158 * | | 159 * | | 160 * |------| 161 * | fp | <- Native C caller frame 162 * |return| 163 * | arg 0| 164 * | arg 1| 165 * | arg 2| 166 * | arg 3| 167 * | arg 4| 168 * | arg 5| 169 * | arg 6| 170 * | arg 7| 171 * | arg 8| 172 * | arg 9| 173 * | | 174 * | | 175 * | | 176 * 177 * 178 * high address 179 * </pre> 180 * 181 * @param skip4Args if true, the calling JNI function has 4 args before the vararg 182 * if false, the calling JNI function has 3 args before the vararg 183 * @return the starting address of the vararg in the caller stack frame 184 */ 185 @NoInline 186 private static Address getVarArgAddress(boolean skip4Args) { 187 Address fp = Magic.getFramePointer(); 188 fp = fp.loadAddress(); 189 fp = fp.loadAddress(); 190 return (fp.plus(2 * WORDSIZE + (skip4Args ? 4 * WORDSIZE : 3 * WORDSIZE))); 191 } 192 193 /** 194 * Common code shared by the JNI functions CallStatic<type>MethodV 195 * @param methodID the method ID 196 * @param argAddress a raw address for the variable argument list 197 * @return an object that may be the return object or a wrapper for the primitive return value 198 */ 199 public static Object invokeWithVarArg(int methodID, Address argAddress, TypeReference expectReturnType) 200 throws Exception { 201 return packageAndInvoke(null, methodID, argAddress, expectReturnType, false, true); 202 } 203 204 /** 205 * Common code shared by the JNI functions Call<type>MethodV 206 * @param obj the object instance 207 * @param methodID the method ID 208 * @param argAddress a raw address for the variable argument list 209 * @param expectReturnType the return type for checking purpose 210 * @param skip4Args received from the JNI function, passed on to Reflection.invoke() 211 * @return an object that may be the return object or a wrapper for the primitive return value 212 */ 213 public static Object invokeWithVarArg(Object obj, int methodID, Address argAddress, TypeReference expectReturnType, 214 boolean skip4Args) throws Exception { 215 return packageAndInvoke(obj, methodID, argAddress, expectReturnType, skip4Args, true); 216 } 217 218 /** 219 * Common code shared by the JNI functions CallStatic<type>MethodA 220 * @param methodID id of MemberReference 221 * @param argAddress a raw address for the argument array 222 * @return an object that may be the return object or a wrapper for the primitive return value 223 */ 224 public static Object invokeWithJValue(int methodID, Address argAddress, TypeReference expectReturnType) 225 throws Exception { 226 return packageAndInvoke(null, methodID, argAddress, expectReturnType, false, false); 227 } 228 229 /** 230 * Common code shared by the JNI functions Call<type>MethodA 231 * @param obj the object instance 232 * @param methodID id of MemberReference 233 * @param argAddress a raw address for the argument array 234 * @param expectReturnType the return type for checking purpose 235 * @param skip4Args received from the JNI function, passed on to Reflection.invoke() 236 * @return an object that may be the return object or a wrapper for the primitive return value 237 */ 238 public static Object invokeWithJValue(Object obj, int methodID, Address argAddress, TypeReference expectReturnType, 239 boolean skip4Args) throws Exception { 240 return packageAndInvoke(obj, methodID, argAddress, expectReturnType, skip4Args, false); 241 } 242 243 /** 244 * Common code shared by invokeWithJValue, invokeWithVarArg and invokeWithDotDotVarArg 245 * @param obj the object instance 246 * @param methodID id of MemberReference 247 * @param argAddress a raw address for the argument array 248 * @param expectReturnType the return type for checking purpose 249 * @param skip4Args This flag is received from the JNI function and passed directly to 250 * Reflection.invoke(). 251 * It is true if the actual method is to be invoked, which could be 252 * from the superclass. 253 * It is false if the method from the real class of the object 254 * is to be invoked, which may not be the actual method specified by methodID 255 * @param isVarArg This flag describes whether the array of parameters is in var arg format or 256 * jvalue format 257 * @return an object that may be the return object or a wrapper for the primitive return value 258 */ 259 @NoInline 260 @NoOptCompile 261 // expect a certain stack frame structure 262 static Object packageAndInvoke(Object obj, int methodID, Address argAddress, TypeReference expectReturnType, 263 boolean skip4Args, boolean isVarArg) throws Exception { 264 265 RVMMethod targetMethod = MemberReference.getMemberRef(methodID).asMethodReference().resolve(); 266 TypeReference returnType = targetMethod.getReturnType(); 267 268 if (JNIFunctions.traceJNI) { 269 VM.sysWrite("JNI CallXXXMethod: (mid " + 270 methodID + 271 ") " + 272 targetMethod.getDeclaringClass().toString() + 273 "." + 274 targetMethod.getName().toString() + 275 "\n"); 276 } 277 278 if (expectReturnType == null) { // for reference return type 279 if (!returnType.isReferenceType()) { 280 throw new Exception("Wrong return type for method: expect reference type instead of " + returnType); 281 } 282 } else { // for primitive return type 283 if (!returnType.definitelySame(expectReturnType)) { 284 throw new Exception("Wrong return type for method: expect " + expectReturnType + " instead of " + returnType); 285 } 286 } 287 288 // Repackage the arguments into an array of objects based on the signature of this method 289 Object[] argObjectArray; 290 if (isVarArg) { 291 argObjectArray = packageParameterFromVarArg(targetMethod, argAddress); 292 } else { 293 argObjectArray = packageParameterFromJValue(targetMethod, argAddress); 294 } 295 296 // now invoke the method 297 return Reflection.invoke(targetMethod, null, obj, argObjectArray, skip4Args); 298 } 299 300 /** 301 * Repackage the arguments passed as a variable argument list into an array of Object, 302 * used by the JNI functions CallStatic<type>MethodV 303 * @param targetMethod The target {@link RVMMethod} 304 * @param argAddress an address into the C space for the array of jvalue unions; 305 * each element is 2-word and holds the argument of the appropriate type 306 * @return an Object array holding the arguments wrapped at Objects 307 */ 308 static Object[] packageParameterFromVarArg(RVMMethod targetMethod, Address argAddress) { 309 TypeReference[] argTypes = targetMethod.getParameterTypes(); 310 int argCount = argTypes.length; 311 Object[] argObjectArray = new Object[argCount]; 312 JNIEnvironment env = RVMThread.getCurrentThread().getJNIEnv(); 313 314 Address addr = argAddress; 315 for (int i = 0; i < argCount; i++) { 316 // convert and wrap the argument according to the expected type 317 if (argTypes[i].isReferenceType()) { 318 // for object, the arg is a JREF index, dereference to get the real object 319 argObjectArray[i] = env.getJNIRef(addr.loadInt()); 320 addr = addr.plus(WORDSIZE); 321 } else if (argTypes[i].isIntType()) { 322 argObjectArray[i] = addr.loadInt(); 323 addr = addr.plus(WORDSIZE); 324 } else if (argTypes[i].isLongType()) { 325 argObjectArray[i] = addr.loadLong(); 326 addr = addr.plus(2*WORDSIZE); 327 } else if (argTypes[i].isBooleanType()) { 328 // the 0/1 bit is stored in the high byte 329 argObjectArray[i] = addr.loadByte() != 0; 330 addr = addr.plus(WORDSIZE); 331 } else if (argTypes[i].isByteType()) { 332 // the target byte is stored in the high byte 333 argObjectArray[i] = addr.loadByte(); 334 addr = addr.plus(WORDSIZE); 335 } else if (argTypes[i].isCharType()) { 336 // char is stored in the high 2 bytes 337 argObjectArray[i] = addr.loadChar(); 338 addr = addr.plus(WORDSIZE); 339 } else if (argTypes[i].isShortType()) { 340 // short is stored in the high 2 bytes 341 argObjectArray[i] = addr.loadShort(); 342 addr = addr.plus(WORDSIZE); 343 } else if (argTypes[i].isFloatType()) { 344 // NOTE: in VarArg convention, C compiler will expand a float to a double that occupy 2 words 345 // so we have to extract it as a double and convert it back to a float 346 argObjectArray[i] = (float) addr.loadDouble(); 347 addr = addr.plus(2*WORDSIZE); 348 } else { 349 if (VM.VerifyAssertions) VM._assert(argTypes[i].isDoubleType()); 350 argObjectArray[i] = addr.loadDouble(); 351 addr = addr.plus(2*WORDSIZE); 352 } 353 } 354 return argObjectArray; 355 } 356 357 /** 358 * Repackage the arguments passed as an array of jvalue into an array of Object, 359 * used by the JNI functions CallStatic<type>MethodA 360 * @param targetMethod The target {@link RVMMethod} 361 * @param argAddress an address into the C space for the array of jvalue unions; 362 * each element is 2-word and holds the argument of the appropriate type 363 * @return an Object array holding the arguments wrapped at Objects 364 */ 365 static Object[] packageParameterFromJValue(RVMMethod targetMethod, Address argAddress) { 366 TypeReference[] argTypes = targetMethod.getParameterTypes(); 367 int argCount = argTypes.length; 368 Object[] argObjectArray = new Object[argCount]; 369 370 // get the JNIEnvironment for this thread in case we need to dereference any object arg 371 JNIEnvironment env = RVMThread.getCurrentThread().getJNIEnv(); 372 373 Address addr = argAddress; 374 for (int i = 0; i < argCount; i++, addr = addr.plus(2*WORDSIZE)) { 375 // convert and wrap the argument according to the expected type 376 if (argTypes[i].isReferenceType()) { 377 // for object, the arg is a JREF index, dereference to get the real object 378 argObjectArray[i] = env.getJNIRef(addr.loadInt()); 379 } else if (argTypes[i].isIntType()) { 380 argObjectArray[i] = addr.loadInt(); 381 } else if (argTypes[i].isLongType()) { 382 argObjectArray[i] = addr.loadLong(); 383 } else if (argTypes[i].isBooleanType()) { 384 // the 0/1 bit is stored in the high byte 385 argObjectArray[i] = addr.loadByte() != 0; 386 } else if (argTypes[i].isByteType()) { 387 // the target byte is stored in the high byte 388 argObjectArray[i] = addr.loadByte(); 389 } else if (argTypes[i].isCharType()) { 390 // char is stored in the high 2 bytes 391 argObjectArray[i] = addr.loadChar(); 392 } else if (argTypes[i].isShortType()) { 393 // short is stored in the high 2 bytes 394 argObjectArray[i] = addr.loadShort(); 395 } else if (argTypes[i].isFloatType()) { 396 argObjectArray[i] = addr.loadFloat(); 397 } else { 398 if (VM.VerifyAssertions) VM._assert(argTypes[i].isDoubleType()); 399 argObjectArray[i] = addr.loadDouble(); 400 } 401 } 402 return argObjectArray; 403 } 404 }