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.
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 }