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