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.runtime;
014
015import static org.jikesrvm.Configuration.BuildForSSE2Full;
016import static org.jikesrvm.VM.NOT_REACHED;
017import static org.jikesrvm.objectmodel.TIBLayoutConstants.TIB_FIRST_VIRTUAL_METHOD_INDEX;
018import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
019
020import org.jikesrvm.VM;
021import org.jikesrvm.classloader.RVMClass;
022import org.jikesrvm.classloader.RVMMethod;
023import org.jikesrvm.classloader.TypeReference;
024import org.jikesrvm.compilers.common.CodeArray;
025import org.jikesrvm.compilers.common.CompiledMethod;
026import org.jikesrvm.scheduler.RVMThread;
027import org.vmmagic.pragma.Inline;
028import org.vmmagic.pragma.NoInline;
029import org.vmmagic.unboxed.Address;
030import org.vmmagic.unboxed.WordArray;
031
032/**
033 * Arch-independent portion of reflective method invoker.
034 */
035public class Reflection {
036
037  /** Perform reflection using bytecodes (true) or out-of-line machine code (false) */
038  public static boolean bytecodeReflection = false;
039  /**
040   * Cache the reflective method invoker in JavaLangReflect? If this is true and
041   * bytecodeReflection is false, then bytecode reflection will only be used for
042   * java.lang.reflect objects.
043   */
044  public static boolean cacheInvokerInJavaLangReflect = true;
045  /*
046   * Reflection uses an integer return from a function which logically
047   * returns a triple.  The values are packed in the integer return value
048   * by the following masks.
049   */
050  public static final int REFLECTION_GPRS_BITS = 5;
051  public static final int REFLECTION_GPRS_MASK = (1 << REFLECTION_GPRS_BITS) - 1;
052  public static final int REFLECTION_FPRS_BITS = 5;
053  public static final int REFLECTION_FPRS_MASK = (1 << REFLECTION_FPRS_BITS) - 1;
054
055  /**
056   * Does the reflective method scheme need to check the arguments are valid?
057   * Bytecode reflection doesn't need arguments checking as they are checking as
058   * they are unwrapped
059   *
060   * @param invoker the invoker to use for the reflective invokation
061   * @return whether the reflective method scheme needs to to be check
062   *  the arguments for validity
063   */
064  @Inline
065  public static boolean needsCheckArgs(ReflectionBase invoker) {
066    // Only need to check the arguments when the user may be packaging them and
067    // not using the bytecode based invoker (that checks them when they are unpacked)
068    return !bytecodeReflection && !cacheInvokerInJavaLangReflect;
069  }
070  /**
071   * Call a method.
072   * @param method method to be called
073   * @param invoker the invoker to use for the invocation, may be {@code null} in
074   *  certain cases
075   * @param thisArg "this" argument (ignored if method is static)
076   * @param otherArgs remaining arguments
077   * @param isNonvirtual flag is {@code false} if the method of the real class of this
078   * object is to be invoked; {@code true} if a method of a superclass may be invoked
079   * @return return value (wrapped if primitive)
080   * See also: java/lang/reflect/Method.invoke()
081   */
082  @Inline
083  public static Object invoke(RVMMethod method, ReflectionBase invoker,
084                              Object thisArg, Object[] otherArgs,
085                              boolean isNonvirtual) {
086    // NB bytecode reflection doesn't care about isNonvirtual
087    if (!bytecodeReflection && !cacheInvokerInJavaLangReflect) {
088      return outOfLineInvoke(method, thisArg, otherArgs, isNonvirtual);
089    } else if (!bytecodeReflection && cacheInvokerInJavaLangReflect) {
090      if (invoker != null) {
091        return invoker.invoke(method, thisArg, otherArgs);
092      } else {
093        return outOfLineInvoke(method, thisArg, otherArgs, isNonvirtual);
094      }
095    } else if (bytecodeReflection && !cacheInvokerInJavaLangReflect) {
096      if (VM.VerifyAssertions) VM._assert(invoker == null);
097      return method.getInvoker().invoke(method, thisArg, otherArgs);
098    } else {
099      // Even if we always generate an invoker this test is still necessary for
100      // invokers that should have been created in the boot image
101      if (invoker != null) {
102        return invoker.invoke(method, thisArg, otherArgs);
103      } else {
104        return method.getInvoker().invoke(method, thisArg, otherArgs);
105      }
106    }
107  }
108
109  private static final WordArray emptyWordArray = WordArray.create(0);
110  private static final double[] emptyDoubleArray = new double[0];
111  private static final byte[] emptyByteArray = new byte[0];
112
113  private static Object outOfLineInvoke(RVMMethod method, Object thisArg, Object[] otherArgs, boolean isNonvirtual) {
114
115    // the class must be initialized before we can invoke a method
116    //
117    RVMClass klass = method.getDeclaringClass();
118    if (!klass.isInitialized()) {
119      RuntimeEntrypoints.initializeClassForDynamicLink(klass);
120    }
121
122    // remember return type
123    // Determine primitive type-ness early to avoid call (possible yield)
124    // later while refs are possibly being held in int arrays.
125    //
126    TypeReference returnType = method.getReturnType();
127    boolean returnIsPrimitive = returnType.isPrimitiveType();
128
129    // decide how to pass parameters
130    //
131    int triple = 0;
132    if (VM.BuildForIA32) {
133      triple = org.jikesrvm.ia32.MachineReflection.countParameters(method);
134    } else {
135      if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
136      triple = org.jikesrvm.ppc.MachineReflection.countParameters(method);
137    }
138    int gprs = triple & REFLECTION_GPRS_MASK;
139    WordArray GPRs = (gprs > 0) ? WordArray.create(gprs) : emptyWordArray;
140    int fprs = (triple >> REFLECTION_GPRS_BITS) & 0x1F;
141    double[] FPRs = (fprs > 0) ? new double[fprs] : emptyDoubleArray;
142    byte[] FPRmeta;
143    if (BuildForSSE2Full) {
144      FPRmeta = (fprs > 0) ? new byte[fprs] : emptyByteArray;
145    } else {
146      FPRmeta = null;
147    }
148
149    int spillCount = triple >> (REFLECTION_GPRS_BITS + REFLECTION_FPRS_BITS);
150
151    WordArray Spills = (spillCount > 0) ? WordArray.create(spillCount) : emptyWordArray;
152
153    if (firstUse) {
154      // force dynamic link sites in unwrappers to get resolved,
155      // before disabling gc.
156      // this is a bit silly, but I can't think of another way to do it [--DL]
157      unwrapBoolean(wrapBoolean(0));
158      unwrapByte(wrapByte((byte) 0));
159      unwrapChar(wrapChar((char) 0));
160      unwrapShort(wrapShort((short) 0));
161      unwrapInt(wrapInt(0));
162      unwrapLong(wrapLong(0));
163      unwrapFloat(wrapFloat(0));
164      unwrapDouble(wrapDouble(0));
165      firstUse = false;
166    }
167
168    // choose actual method to be called
169    //
170    RVMMethod targetMethod;
171    if (isNonvirtual || method.isStatic() || method.isObjectInitializer()) {
172      targetMethod = method;
173    } else {
174      RVMClass C = Magic.getObjectType(thisArg).asClass();
175      if (!method.getDeclaringClass().isInterface()) {
176        int tibIndex = method.getOffset().toInt() >>> LOG_BYTES_IN_ADDRESS;
177        targetMethod = C.getVirtualMethods()[tibIndex - TIB_FIRST_VIRTUAL_METHOD_INDEX];
178      } else {
179        RVMClass I = method.getDeclaringClass();
180        if (!RuntimeEntrypoints.isAssignableWith(I, C))
181          throw new IncompatibleClassChangeError();
182        targetMethod = C.findVirtualMethod(method.getName(), method.getDescriptor());
183        if (targetMethod == null)
184          throw new IncompatibleClassChangeError();
185      }
186    }
187
188    // getCurrentCompiledMethod is synchronized but Unpreemptible.
189    // Therefore there are no possible yieldpoints from the time
190    // the compiledMethod is loaded in getCurrentCompiledMethod
191    // to when we disable GC below.
192    // We can't allow any yieldpoints between these points because of the way in which
193    // we GC compiled code.  Once a method is marked as obsolete, if it is not
194    // executing on the stack of some thread, then the process of collecting the
195    // code and meta-data might be initiated.
196    targetMethod.compile();
197    CompiledMethod cm = targetMethod.getCurrentCompiledMethod();
198    while (cm == null) {
199      targetMethod.compile();
200      cm = targetMethod.getCurrentCompiledMethod();
201    }
202
203    RVMThread.getCurrentThread().disableYieldpoints();
204
205    CodeArray code = cm.getEntryCodeArray();
206    if (VM.BuildForIA32) {
207      org.jikesrvm.ia32.MachineReflection.packageParameters(method, thisArg, otherArgs, GPRs, FPRs, FPRmeta, Spills);
208    } else {
209      if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
210      org.jikesrvm.ppc.MachineReflection.packageParameters(method, thisArg, otherArgs, GPRs, FPRs, FPRmeta, Spills);
211    }
212
213    // critical: no yieldpoints/GCpoints between here and the invoke of code!
214    //           We may have references hidden in the GPRs and Spills arrays!!!
215    RVMThread.getCurrentThread().enableYieldpoints();
216
217    if (!returnIsPrimitive) {
218      return Magic.invokeMethodReturningObject(code, GPRs, FPRs, FPRmeta, Spills);
219    }
220
221    if (returnType.isVoidType()) {
222      Magic.invokeMethodReturningVoid(code, GPRs, FPRs, FPRmeta, Spills);
223      return null;
224    }
225
226    if (returnType.isBooleanType()) {
227      int x = Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills);
228      return x == 1;
229    }
230
231    if (returnType.isByteType()) {
232      int x = Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills);
233      return (byte) x;
234    }
235
236    if (returnType.isShortType()) {
237      int x = Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills);
238      return (short) x;
239    }
240
241    if (returnType.isCharType()) {
242      int x = Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills);
243      return (char) x;
244    }
245
246    if (returnType.isIntType()) {
247      return Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills);
248    }
249
250    if (returnType.isLongType()) {
251      return Magic.invokeMethodReturningLong(code, GPRs, FPRs, FPRmeta, Spills);
252    }
253
254    if (returnType.isFloatType()) {
255      return Magic.invokeMethodReturningFloat(code, GPRs, FPRs, FPRmeta, Spills);
256    }
257
258    if (returnType.isDoubleType()) {
259      return Magic.invokeMethodReturningDouble(code, GPRs, FPRs, FPRmeta, Spills);
260    }
261
262    if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
263    return null;
264  }
265
266  // Method parameter wrappers.
267  //
268  @NoInline
269  public static Object wrapBoolean(int b) {
270    return b == 1;
271  }
272
273  @NoInline
274  public static Object wrapByte(byte b) {
275    return b;
276  }
277
278  @NoInline
279  public static Object wrapChar(char c) {
280    return c;
281  }
282
283  @NoInline
284  public static Object wrapShort(short s) {
285    return s;
286  }
287
288  @NoInline
289  public static Object wrapInt(int i) {
290    return i;
291  }
292
293  @NoInline
294  public static Object wrapLong(long l) {
295    return l;
296  }
297
298  @NoInline
299  public static Object wrapFloat(float f) {
300    return f;
301  }
302
303  @NoInline
304  public static Object wrapDouble(double d) {
305    return d;
306  }
307
308  // Method parameter unwrappers.
309  //
310  @NoInline
311  public static int unwrapBooleanAsInt(Object o) {
312    if (unwrapBoolean(o)) {
313      return 1;
314    } else {
315      return 0;
316    }
317  }
318
319  @NoInline
320  public static boolean unwrapBoolean(Object o) {
321    return (Boolean) o;
322  }
323
324  @NoInline
325  public static byte unwrapByte(Object o) {
326    return (Byte) o;
327  }
328
329  @NoInline
330  public static char unwrapChar(Object o) {
331    return (Character) o;
332  }
333
334  @NoInline
335  public static short unwrapShort(Object o) {
336    return (Short) o;
337  }
338
339  @NoInline
340  public static int unwrapInt(Object o) {
341    return (Integer) o;
342  }
343
344  @NoInline
345  public static long unwrapLong(Object o) {
346    return (Long) o;
347  }
348
349  @NoInline
350  public static float unwrapFloat(Object o) {
351    return (Float) o;
352  }
353
354  @NoInline
355  public static double unwrapDouble(Object o) {
356    return (Double) o;
357  }
358
359  @NoInline
360  public static Address unwrapObject(Object o) {
361    return Magic.objectAsAddress(o);
362  }
363
364  private static boolean firstUse = true;
365}