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    }