/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.runtime; import static org.jikesrvm.Configuration.BuildForSSE2Full; import static org.jikesrvm.VM.NOT_REACHED; import static org.jikesrvm.objectmodel.TIBLayoutConstants.TIB_FIRST_VIRTUAL_METHOD_INDEX; import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS; import org.jikesrvm.VM; import org.jikesrvm.classloader.RVMClass; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.classloader.TypeReference; import org.jikesrvm.compilers.common.CodeArray; import org.jikesrvm.compilers.common.CompiledMethod; import org.jikesrvm.scheduler.RVMThread; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.NoInline; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.WordArray; /** * Arch-independent portion of reflective method invoker. */ public class Reflection { /** Perform reflection using bytecodes (true) or out-of-line machine code (false) */ public static boolean bytecodeReflection = false; /** * Cache the reflective method invoker in JavaLangReflect? If this is true and * bytecodeReflection is false, then bytecode reflection will only be used for * java.lang.reflect objects. */ public static boolean cacheInvokerInJavaLangReflect = true; /* * Reflection uses an integer return from a function which logically * returns a triple. The values are packed in the integer return value * by the following masks. */ public static final int REFLECTION_GPRS_BITS = 5; public static final int REFLECTION_GPRS_MASK = (1 << REFLECTION_GPRS_BITS) - 1; public static final int REFLECTION_FPRS_BITS = 5; public static final int REFLECTION_FPRS_MASK = (1 << REFLECTION_FPRS_BITS) - 1; /** * Does the reflective method scheme need to check the arguments are valid? * Bytecode reflection doesn't need arguments checking as they are checking as * they are unwrapped * * @param invoker the invoker to use for the reflective invokation * @return whether the reflective method scheme needs to to be check * the arguments for validity */ @Inline public static boolean needsCheckArgs(ReflectionBase invoker) { // Only need to check the arguments when the user may be packaging them and // not using the bytecode based invoker (that checks them when they are unpacked) return !bytecodeReflection && !cacheInvokerInJavaLangReflect; } /** * Call a method. * @param method method to be called * @param invoker the invoker to use for the invocation, may be {@code null} in * certain cases * @param thisArg "this" argument (ignored if method is static) * @param otherArgs remaining arguments * @param isNonvirtual flag is {@code false} if the method of the real class of this * object is to be invoked; {@code true} if a method of a superclass may be invoked * @return return value (wrapped if primitive) * See also: java/lang/reflect/Method.invoke() */ @Inline public static Object invoke(RVMMethod method, ReflectionBase invoker, Object thisArg, Object[] otherArgs, boolean isNonvirtual) { // NB bytecode reflection doesn't care about isNonvirtual if (!bytecodeReflection && !cacheInvokerInJavaLangReflect) { return outOfLineInvoke(method, thisArg, otherArgs, isNonvirtual); } else if (!bytecodeReflection && cacheInvokerInJavaLangReflect) { if (invoker != null) { return invoker.invoke(method, thisArg, otherArgs); } else { return outOfLineInvoke(method, thisArg, otherArgs, isNonvirtual); } } else if (bytecodeReflection && !cacheInvokerInJavaLangReflect) { if (VM.VerifyAssertions) VM._assert(invoker == null); return method.getInvoker().invoke(method, thisArg, otherArgs); } else { // Even if we always generate an invoker this test is still necessary for // invokers that should have been created in the boot image if (invoker != null) { return invoker.invoke(method, thisArg, otherArgs); } else { return method.getInvoker().invoke(method, thisArg, otherArgs); } } } private static final WordArray emptyWordArray = WordArray.create(0); private static final double[] emptyDoubleArray = new double[0]; private static final byte[] emptyByteArray = new byte[0]; private static Object outOfLineInvoke(RVMMethod method, Object thisArg, Object[] otherArgs, boolean isNonvirtual) { // the class must be initialized before we can invoke a method // RVMClass klass = method.getDeclaringClass(); if (!klass.isInitialized()) { RuntimeEntrypoints.initializeClassForDynamicLink(klass); } // remember return type // Determine primitive type-ness early to avoid call (possible yield) // later while refs are possibly being held in int arrays. // TypeReference returnType = method.getReturnType(); boolean returnIsPrimitive = returnType.isPrimitiveType(); // decide how to pass parameters // int triple = 0; if (VM.BuildForIA32) { triple = org.jikesrvm.ia32.MachineReflection.countParameters(method); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); triple = org.jikesrvm.ppc.MachineReflection.countParameters(method); } int gprs = triple & REFLECTION_GPRS_MASK; WordArray GPRs = (gprs > 0) ? WordArray.create(gprs) : emptyWordArray; int fprs = (triple >> REFLECTION_GPRS_BITS) & 0x1F; double[] FPRs = (fprs > 0) ? new double[fprs] : emptyDoubleArray; byte[] FPRmeta; if (BuildForSSE2Full) { FPRmeta = (fprs > 0) ? new byte[fprs] : emptyByteArray; } else { FPRmeta = null; } int spillCount = triple >> (REFLECTION_GPRS_BITS + REFLECTION_FPRS_BITS); WordArray Spills = (spillCount > 0) ? WordArray.create(spillCount) : emptyWordArray; if (firstUse) { // force dynamic link sites in unwrappers to get resolved, // before disabling gc. // this is a bit silly, but I can't think of another way to do it [--DL] unwrapBoolean(wrapBoolean(0)); unwrapByte(wrapByte((byte) 0)); unwrapChar(wrapChar((char) 0)); unwrapShort(wrapShort((short) 0)); unwrapInt(wrapInt(0)); unwrapLong(wrapLong(0)); unwrapFloat(wrapFloat(0)); unwrapDouble(wrapDouble(0)); firstUse = false; } // choose actual method to be called // RVMMethod targetMethod; if (isNonvirtual || method.isStatic() || method.isObjectInitializer()) { targetMethod = method; } else { RVMClass C = Magic.getObjectType(thisArg).asClass(); if (!method.getDeclaringClass().isInterface()) { int tibIndex = method.getOffset().toInt() >>> LOG_BYTES_IN_ADDRESS; targetMethod = C.getVirtualMethods()[tibIndex - TIB_FIRST_VIRTUAL_METHOD_INDEX]; } else { RVMClass I = method.getDeclaringClass(); if (!RuntimeEntrypoints.isAssignableWith(I, C)) throw new IncompatibleClassChangeError(); targetMethod = C.findVirtualMethod(method.getName(), method.getDescriptor()); if (targetMethod == null) throw new IncompatibleClassChangeError(); } } // getCurrentCompiledMethod is synchronized but Unpreemptible. // Therefore there are no possible yieldpoints from the time // the compiledMethod is loaded in getCurrentCompiledMethod // to when we disable GC below. // We can't allow any yieldpoints between these points because of the way in which // we GC compiled code. Once a method is marked as obsolete, if it is not // executing on the stack of some thread, then the process of collecting the // code and meta-data might be initiated. targetMethod.compile(); CompiledMethod cm = targetMethod.getCurrentCompiledMethod(); while (cm == null) { targetMethod.compile(); cm = targetMethod.getCurrentCompiledMethod(); } RVMThread.getCurrentThread().disableYieldpoints(); CodeArray code = cm.getEntryCodeArray(); if (VM.BuildForIA32) { org.jikesrvm.ia32.MachineReflection.packageParameters(method, thisArg, otherArgs, GPRs, FPRs, FPRmeta, Spills); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); org.jikesrvm.ppc.MachineReflection.packageParameters(method, thisArg, otherArgs, GPRs, FPRs, FPRmeta, Spills); } // critical: no yieldpoints/GCpoints between here and the invoke of code! // We may have references hidden in the GPRs and Spills arrays!!! RVMThread.getCurrentThread().enableYieldpoints(); if (!returnIsPrimitive) { return Magic.invokeMethodReturningObject(code, GPRs, FPRs, FPRmeta, Spills); } if (returnType.isVoidType()) { Magic.invokeMethodReturningVoid(code, GPRs, FPRs, FPRmeta, Spills); return null; } if (returnType.isBooleanType()) { int x = Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills); return x == 1; } if (returnType.isByteType()) { int x = Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills); return (byte) x; } if (returnType.isShortType()) { int x = Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills); return (short) x; } if (returnType.isCharType()) { int x = Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills); return (char) x; } if (returnType.isIntType()) { return Magic.invokeMethodReturningInt(code, GPRs, FPRs, FPRmeta, Spills); } if (returnType.isLongType()) { return Magic.invokeMethodReturningLong(code, GPRs, FPRs, FPRmeta, Spills); } if (returnType.isFloatType()) { return Magic.invokeMethodReturningFloat(code, GPRs, FPRs, FPRmeta, Spills); } if (returnType.isDoubleType()) { return Magic.invokeMethodReturningDouble(code, GPRs, FPRs, FPRmeta, Spills); } if (VM.VerifyAssertions) VM._assert(NOT_REACHED); return null; } // Method parameter wrappers. // @NoInline public static Object wrapBoolean(int b) { return b == 1; } @NoInline public static Object wrapByte(byte b) { return b; } @NoInline public static Object wrapChar(char c) { return c; } @NoInline public static Object wrapShort(short s) { return s; } @NoInline public static Object wrapInt(int i) { return i; } @NoInline public static Object wrapLong(long l) { return l; } @NoInline public static Object wrapFloat(float f) { return f; } @NoInline public static Object wrapDouble(double d) { return d; } // Method parameter unwrappers. // @NoInline public static int unwrapBooleanAsInt(Object o) { if (unwrapBoolean(o)) { return 1; } else { return 0; } } @NoInline public static boolean unwrapBoolean(Object o) { return (Boolean) o; } @NoInline public static byte unwrapByte(Object o) { return (Byte) o; } @NoInline public static char unwrapChar(Object o) { return (Character) o; } @NoInline public static short unwrapShort(Object o) { return (Short) o; } @NoInline public static int unwrapInt(Object o) { return (Integer) o; } @NoInline public static long unwrapLong(Object o) { return (Long) o; } @NoInline public static float unwrapFloat(Object o) { return (Float) o; } @NoInline public static double unwrapDouble(Object o) { return (Double) o; } @NoInline public static Address unwrapObject(Object o) { return Magic.objectAsAddress(o); } private static boolean firstUse = true; }