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 }