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 }