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 */
013package org.jikesrvm.jni.ia32;
014
015import java.lang.reflect.Constructor;
016
017import org.jikesrvm.VM;
018import org.jikesrvm.classloader.MemberReference;
019import org.jikesrvm.classloader.MethodReference;
020import org.jikesrvm.classloader.RVMMethod;
021import org.jikesrvm.classloader.TypeReference;
022
023import static org.jikesrvm.ia32.BaselineConstants.WORDSIZE;
024
025import org.jikesrvm.jni.JNIEnvironment;
026import org.jikesrvm.jni.JNIGenericHelpers;
027import org.jikesrvm.runtime.Magic;
028import org.jikesrvm.runtime.SysCall;
029import org.jikesrvm.scheduler.RVMThread;
030import org.vmmagic.pragma.NoInline;
031import org.vmmagic.pragma.NoOptCompile;
032import org.vmmagic.unboxed.Address;
033
034/**
035 * Platform dependent utility functions called from JNIFunctions
036 * (cannot be placed in JNIFunctions because methods
037 * there are specially compiled to be called from native).
038 *
039 * @see org.jikesrvm.jni.JNIFunctions
040 */
041public abstract class JNIHelpers extends JNIGenericHelpers {
042
043  /**
044   * Common code shared by the JNI functions NewObjectA, NewObjectV, NewObject
045   * (object creation)
046   * @param cls class whose constructor is to be invoked
047   * @param methodID the method ID for a constructor
048   * @param argAddress where to find the arguments for the constructor
049   * @param isJvalue {@code true} if parameters are passed as a jvalue array
050   * @param isDotDotStyle {@code true} if the method uses varargs
051   * @return a new object created by the specified constructor
052   * @throws Exception when the reflective invocation of the constructor fails
053   */
054  public static Object invokeInitializer(Class<?> cls, int methodID, Address argAddress, boolean isJvalue,
055                                         boolean isDotDotStyle) throws Exception {
056    // get the parameter list as Java class
057    MemberReference mr = MemberReference.getMemberRef(methodID);
058    TypeReference tr = java.lang.JikesRVMSupport.getTypeForClass(cls).getTypeRef();
059    MethodReference methodRef = MemberReference.findOrCreate(tr, mr.getName(), mr.getDescriptor()).asMethodReference();
060    RVMMethod mth = methodRef.resolve();
061
062    Constructor<?> constMethod = java.lang.reflect.JikesRVMSupport.createConstructor(mth);
063    if (!mth.isPublic()) {
064      constMethod.setAccessible(true);
065    }
066
067    // Package the parameters for the constructor
068    Address varargAddress;
069    if (isDotDotStyle) {
070      // flag is false because this JNI function has 3 args before the var args
071      varargAddress = getVarArgAddress(false);
072    } else {
073      varargAddress = argAddress;
074    }
075
076    Object[] argObjs;
077    if (isJvalue) {
078      argObjs = packageParametersFromJValuePtr(methodRef, argAddress);
079    } else {
080      argObjs = packageParameterFromVarArg(methodRef, varargAddress);
081    }
082
083    // construct the new object
084    return constMethod.newInstance(argObjs);
085  }
086
087  /**
088   * Common code shared by the JNI functions CallStatic&lt;type&gt;Method
089   * (static method invocation)
090   * @param methodID the method ID
091   * @param expectReturnType the return type of the method to be invoked
092   * @return an object that may be the return object or a wrapper for the primitive return value
093   * @throws Exception if the return type doesn't match the expected return type
094   */
095  @NoInline
096  @NoOptCompile
097  // expect a certain stack frame structure
098  public static Object invokeWithDotDotVarArg(int methodID, TypeReference expectReturnType) throws Exception {
099    MethodReference mr = MemberReference.getMethodRef(methodID);
100    Address varargAddress = getVarArgAddress(false);
101    Object[] argObjectArray = packageParameterFromVarArg(mr, varargAddress);
102    return callMethod(null, mr, argObjectArray, expectReturnType, true);
103  }
104
105  /**
106   * Common code shared by the JNI functions Call&lt;type&gt;Method
107   * (virtual method invocation)
108   * @param obj the object instance
109   * @param methodID the method ID
110   * @param expectReturnType the return type for checking purpose
111   * @param skip4Args  true if the calling JNI Function takes 4 args before the vararg
112   *                   false if the calling JNI Function takes 3 args before the vararg
113   * @return an object that may be the return object or a wrapper for the primitive return value
114   * @throws Exception if the return type doesn't match the expected return type
115   */
116  @NoInline
117  @NoOptCompile
118  // expect a certain stack frame structure
119  public static Object invokeWithDotDotVarArg(Object obj, int methodID, TypeReference expectReturnType,
120                                              boolean skip4Args) throws Exception {
121    MethodReference mr = MemberReference.getMethodRef(methodID);
122    Address varargAddress = getVarArgAddress(skip4Args);
123    Object[] argObjectArray = packageParameterFromVarArg(mr, varargAddress);
124    return callMethod(obj, mr, argObjectArray, expectReturnType, skip4Args);
125  }
126
127  /**
128   * This method supports var args passed from C.<p>
129   *
130   * In the Linux Intel C convention, the caller places the args immediately above the
131   * saved return address, starting with the first arg. <br>
132   *
133   * For the JNI functions that takes var args, their prolog code will save the
134   * var arg in the glue frame because the values in the register may be lost by
135   * subsequent calls. <br>
136   *
137   * This method copies the var arg values that were saved earlier in glue frame into
138   * the spill area of the original caller, thereby doing the work that the callee
139   * normally performs in the AIX C convention. <br>
140   *
141   * NOTE: This method contains internal stack pointer.
142   * For now we assume that the stack will not be relocatable while native code is running
143   * because native code can hold an address into the stack, so this code is OK,
144   * but this is an issue to be resolved later. <br>
145   *
146   * NOTE:  this method assumes that it is immediately above the
147   * invokeWithDotDotVarArg frame, the JNI frame, the glue frame and
148   * the C caller frame in the respective order.
149   * Therefore, this method will not work if called from anywhere else.
150   *
151   * <pre>
152   *  low address
153   *
154   *   |  fp  | &lt;- JNIEnvironment.getVarArgAddress
155   *   | mid  |
156   *   |      |
157   *   |      |
158   *   |------|
159   *   |  fp  | &lt;- JNIEnvironment.invokeWithDotDotVarArg frame
160   *   | mid  |
161   *   | ...  |
162   *   |      |
163   *   |      |
164   *   |------|
165   *   |  fp  | &lt;- JNI method frame
166   *   | mid  |
167   *   | ...  |
168   *   | arg 0|    args copied by JNI prolog (3 for static, nonvirtual,
169   *   | arg 1|    or 4 for virtual)
170   *   | arg 2|
171   *   |      |
172   *   |      |
173   *   |------|
174   *   | fp   | &lt;- Native C caller frame
175   *   |return|
176   *   | arg 0|
177   *   | arg 1|
178   *   | arg 2|
179   *   | arg 3|
180   *   | arg 4|
181   *   | arg 5|
182   *   | arg 6|
183   *   | arg 7|
184   *   | arg 8|
185   *   | arg 9|
186   *   |      |
187   *   |      |
188   *   |      |
189   *
190   *
191   *   high address
192   * </pre>
193   *
194   * @param skip4Args if true, the calling JNI function has 4 args before the vararg
195   *                  if false, the calling JNI function has 3 args before the vararg
196   * @return the starting address of the vararg in the caller stack frame
197   */
198  @NoInline
199  private static Address getVarArgAddress(boolean skip4Args) {
200    Address fp = Magic.getFramePointer();
201    fp = fp.loadAddress();
202    fp = fp.loadAddress();
203    return (fp.plus(2 * WORDSIZE + (skip4Args ? 4 * WORDSIZE : 3 * WORDSIZE)));
204  }
205
206  /**
207   * Common code shared by the JNI functions CallStatic&lt;type&gt;MethodV
208   * @param methodID the method ID
209   * @param argAddress a raw address for the variable argument list
210   * @param expectReturnType the return type of the method to be invoked
211   * @return an object that may be the return object or a wrapper for the primitive return value
212   * @throws Exception if the return type doesn't match the expected return type
213   */
214  public static Object invokeWithVarArg(int methodID, Address argAddress, TypeReference expectReturnType)
215      throws Exception {
216    MethodReference mr = MemberReference.getMethodRef(methodID);
217    Object[] argObjectArray = packageParameterFromVarArg(mr, argAddress);
218    return callMethod(null, mr, argObjectArray, expectReturnType, true);
219  }
220
221  /**
222   * Common code shared by the JNI functions Call&lt;type&gt;MethodV
223   * @param obj the object instance
224   * @param methodID the method ID
225   * @param argAddress a raw address for the variable argument list
226   * @param expectReturnType the return type for checking purpose
227   * @param skip4Args received from the JNI function, passed on to Reflection.invoke()
228   * @return an object that may be the return object or a wrapper for the primitive return value
229   * @throws Exception if the return type doesn't match the expected return type
230   */
231  public static Object invokeWithVarArg(Object obj, int methodID, Address argAddress, TypeReference expectReturnType,
232                                        boolean skip4Args) throws Exception {
233    MethodReference mr = MemberReference.getMethodRef(methodID);
234    Object[] argObjectArray = packageParameterFromVarArg(mr, argAddress);
235    return callMethod(obj, mr, argObjectArray, expectReturnType, skip4Args);
236  }
237
238  /**
239   * Repackage the arguments passed as a variable argument list into an array of Object,
240   * used by the JNI functions CallStatic&lt;type&gt;MethodV
241   * @param targetMethod   The target {@link RVMMethod}
242   * @param argAddress an address into the C space for the array of jvalue unions;
243   *                   each element is 2-word and holds the argument of the appropriate type
244   * @return an Object array holding the arguments wrapped at Objects
245   */
246  static Object[] packageParameterFromVarArg(MethodReference targetMethod, Address argAddress) {
247    TypeReference[] argTypes = targetMethod.getParameterTypes();
248    int argCount = argTypes.length;
249    Object[] argObjectArray = new Object[argCount];
250    Address vaListCopy = SysCall.sysCall.sysVaCopy(argAddress);
251    JNIEnvironment env = RVMThread.getCurrentThread().getJNIEnv();
252
253    for (int i = 0; i < argCount; i++) {
254      // convert and wrap the argument according to the expected type
255      if (argTypes[i].isReferenceType()) {
256        argObjectArray[i] = env.getJNIRef(SysCall.sysCall.sysVaArgJobject(vaListCopy));
257      } else if (argTypes[i].isIntType()) {
258        argObjectArray[i] = SysCall.sysCall.sysVaArgJint(vaListCopy);
259      } else if (argTypes[i].isLongType()) {
260        argObjectArray[i] = SysCall.sysCall.sysVaArgJlong(vaListCopy);
261      } else if (argTypes[i].isBooleanType()) {
262        argObjectArray[i] = SysCall.sysCall.sysVaArgJboolean(vaListCopy);
263      } else if (argTypes[i].isByteType()) {
264        argObjectArray[i] = SysCall.sysCall.sysVaArgJbyte(vaListCopy);
265      } else if (argTypes[i].isCharType()) {
266        argObjectArray[i] = SysCall.sysCall.sysVaArgJchar(vaListCopy);
267      } else if (argTypes[i].isShortType()) {
268        argObjectArray[i] = SysCall.sysCall.sysVaArgJshort(vaListCopy);
269      } else if (argTypes[i].isFloatType()) {
270        argObjectArray[i] = SysCall.sysCall.sysVaArgJfloat(vaListCopy);
271      } else {
272        if (VM.VerifyAssertions) VM._assert(argTypes[i].isDoubleType());
273        argObjectArray[i] = SysCall.sysCall.sysVaArgJdouble(vaListCopy);
274      }
275    }
276    SysCall.sysCall.sysVaEnd(vaListCopy);
277    return argObjectArray;
278  }
279}