]>
Commit | Line | Data |
---|---|---|
97b8365c TT |
1 | // natVMProxy.cc -- Implementation of VMProxy methods. |
2 | ||
18fa3240 | 3 | /* Copyright (C) 2006, 2007 |
97b8365c TT |
4 | Free Software Foundation |
5 | ||
6 | This file is part of libgcj. | |
7 | ||
8 | This software is copyrighted work licensed under the terms of the | |
9 | Libgcj License. Please consult the file "LIBGCJ_LICENSE" for | |
10 | details. */ | |
11 | ||
12 | // The idea of behind this code is to utilize libffi's ability to | |
13 | // create closures to provide a fast "cut-through" way to generate | |
14 | // proxy classes. Instead of generating bytecode and then | |
15 | // interpreting that, we copy the method definitions for each of the | |
16 | // methods we're supposed to be prxying and generate a libffi closure | |
17 | // for each one. | |
18 | ||
19 | #include <config.h> | |
20 | #include <platform.h> | |
21 | #include <sysdep/descriptor.h> | |
22 | ||
23 | #include <limits.h> | |
24 | #include <string.h> | |
25 | #include <stddef.h> | |
26 | #include <stdio.h> | |
27 | ||
28 | #include <gcj/cni.h> | |
29 | #include <gcj/javaprims.h> | |
30 | #include <jvm.h> | |
31 | #include <jni.h> | |
32 | #include <java-threads.h> | |
33 | #include <java-interp.h> | |
34 | #include <ffi.h> | |
35 | #include <execution.h> | |
36 | #include <gcj/method.h> | |
37 | ||
38 | #include <gnu/gcj/runtime/BootClassLoader.h> | |
39 | #include <java/lang/Class.h> | |
40 | #include <java/lang/ClassCastException.h> | |
41 | #include <java/lang/Error.h> | |
42 | #include <java/lang/IllegalArgumentException.h> | |
43 | #include <java/lang/Integer.h> | |
44 | #include <java/lang/StringBuffer.h> | |
45 | #include <java/lang/VMClassLoader.h> | |
46 | #include <java/lang/VMCompiler.h> | |
47 | #include <java/lang/reflect/InvocationHandler.h> | |
48 | #include <java/lang/reflect/Method.h> | |
49 | #include <java/lang/reflect/Proxy$ClassFactory.h> | |
50 | #include <java/lang/reflect/Proxy$ProxyData.h> | |
51 | #include <java/lang/reflect/Proxy.h> | |
52 | #include <java/lang/reflect/UndeclaredThrowableException.h> | |
53 | #include <java/lang/reflect/VMProxy.h> | |
54 | ||
55 | #include <java/lang/Byte.h> | |
56 | #include <java/lang/Short.h> | |
57 | #include <java/lang/Integer.h> | |
58 | #include <java/lang/Long.h> | |
59 | #include <java/lang/Float.h> | |
60 | #include <java/lang/Double.h> | |
61 | #include <java/lang/Boolean.h> | |
62 | #include <java/lang/Character.h> | |
63 | ||
64 | ||
65 | using namespace java::lang::reflect; | |
66 | using namespace java::lang; | |
67 | ||
68 | typedef void (*closure_fun) (ffi_cif*, void*, void**, void*); | |
18fa3240 | 69 | static void *ncode (jclass klass, _Jv_Method *self, closure_fun fun); |
97b8365c TT |
70 | static void run_proxy (ffi_cif*, void*, void**, void*); |
71 | ||
72 | typedef jobject invoke_t (jobject, Proxy *, Method *, JArray< jobject > *); | |
73 | ||
74 | // True if pc points to a proxy frame. | |
75 | ||
76 | bool | |
77 | _Jv_is_proxy (void *pc) | |
78 | { | |
79 | return pc == UNWRAP_FUNCTION_DESCRIPTOR ((void*)&run_proxy); | |
80 | } | |
81 | ||
82 | // Generate a proxy class by using libffi closures for each entry | |
83 | // point. | |
84 | ||
85 | jclass | |
86 | java::lang::reflect::VMProxy::generateProxyClass | |
87 | (ClassLoader *loader, Proxy$ProxyData *d) | |
88 | { | |
89 | // If we're precompiling, generate bytecode and allow VMCompiler to | |
90 | // precompile it. | |
91 | if (VMCompiler::precompiles ()) | |
92 | return (new Proxy$ClassFactory(d))->generate(loader); | |
93 | ||
94 | jclass klass = new Class (); | |
95 | klass->superclass = &Proxy::class$; | |
96 | klass->engine = &_Jv_soleIndirectCompiledEngine; | |
97 | klass->size_in_bytes = Proxy::class$.size_in_bytes; | |
98 | klass->vtable_method_count = -1; | |
99 | ||
100 | // Synchronize on the class, so that it is not attempted initialized | |
101 | // until we're done. | |
102 | JvSynchronize sync (klass); | |
103 | ||
104 | // Record the defining loader. For the bootstrap class loader, | |
105 | // we record NULL. | |
106 | if (loader != VMClassLoader::bootLoader) | |
107 | klass->loader = loader; | |
108 | ||
109 | { | |
110 | StringBuffer *sb = new StringBuffer(); | |
111 | sb->append(JvNewStringLatin1 ("$Proxy")); | |
112 | sb->append(Integer::toString (d->id)); | |
113 | klass->name = _Jv_makeUtf8Const (sb->toString()); | |
114 | } | |
115 | ||
116 | // Allocate space for the interfaces. | |
117 | klass->interface_count = d->interfaces->length; | |
118 | klass->interfaces = (jclass*) _Jv_AllocRawObj (klass->interface_count | |
119 | *sizeof (jclass)); | |
120 | for (int i = 0; i < klass->interface_count; i++) | |
121 | klass->interfaces[i] = elements(d->interfaces)[i]; | |
122 | ||
123 | size_t count = d->methods->length; | |
124 | ||
125 | { | |
126 | size_t total_count = count + Proxy::class$.method_count + 1; | |
127 | if (total_count >= 65536) | |
128 | throw new IllegalArgumentException (); | |
129 | // Allocate space for the methods. This is a worst case | |
130 | // estimate. | |
131 | klass->methods | |
132 | = (_Jv_Method *) _Jv_AllocRawObj (sizeof (_Jv_Method) | |
133 | * total_count); | |
134 | } | |
135 | ||
136 | jshort &method_count = klass->method_count; | |
137 | ||
138 | // Copy all reachable methods from Proxy. | |
139 | for (int i = 0; i < Proxy::class$.method_count; i++) | |
140 | { | |
141 | if (_Jv_CheckAccess (klass, &Proxy::class$, | |
142 | Proxy::class$.methods[i].accflags)) | |
143 | { | |
144 | klass->methods[method_count] = Proxy::class$.methods[i]; | |
145 | method_count++; | |
146 | } | |
147 | } | |
148 | ||
149 | _Jv_Method *init_method | |
150 | = (_Jv_Linker::search_method_in_class | |
151 | (klass, klass, | |
152 | _Jv_makeUtf8Const ("<init>"), | |
153 | _Jv_makeUtf8Const ("(Ljava.lang.reflect.InvocationHandler;)V"), | |
154 | false)); | |
155 | init_method->accflags |= Modifier::PUBLIC; | |
156 | ||
157 | // Create the methods for all of the interfaces. | |
158 | for (size_t i = 0; i < count; i++) | |
159 | { | |
160 | _Jv_Method &method = klass->methods[method_count++]; | |
161 | const _Jv_Method &imethod = *_Jv_FromReflectedMethod (elements(d->methods)[i]); | |
162 | // We use a shallow copy of IMETHOD rather than a deep copy; | |
163 | // this means that the pointer fields of METHOD point into the | |
164 | // interface. As long as this subclass of Proxy is reachable, | |
165 | // the interfaces of which it is a proxy will also be reachable, | |
166 | // so this is safe. | |
167 | method = imethod; | |
18fa3240 | 168 | method.ncode = ncode (klass, &method, run_proxy); |
97b8365c TT |
169 | method.accflags &= ~Modifier::ABSTRACT; |
170 | } | |
171 | ||
172 | _Jv_Linker::layout_vtable_methods (klass); | |
173 | _Jv_RegisterInitiatingLoader (klass, klass->loader); | |
174 | ||
175 | return klass; | |
176 | } | |
177 | ||
178 | ||
179 | // Box things with primitive types. | |
180 | static inline jobject | |
181 | box (void *thing, jclass klass, FFI_TYPE type) | |
182 | { | |
183 | jobject o; | |
184 | ||
185 | switch (type) | |
186 | { | |
187 | case FFI_TYPE_VOID: | |
188 | return NULL; | |
189 | ||
190 | case FFI_TYPE_POINTER: | |
191 | o = *(jobject*)thing; | |
192 | return o; | |
193 | ||
194 | default: | |
195 | ; | |
196 | } | |
197 | ||
198 | if (klass == JvPrimClass (byte)) | |
199 | o = new Byte (*(jbyte*)thing); | |
200 | else if (klass == JvPrimClass (short)) | |
201 | o = new Short (*(jshort*)thing); | |
202 | else if (klass == JvPrimClass (int)) | |
203 | o = new Integer (*(jint*)thing); | |
204 | else if (klass == JvPrimClass (long)) | |
205 | o = new Long (*(jlong*)thing); | |
206 | else if (klass == JvPrimClass (float)) | |
207 | o = new Float (*(jfloat*)thing); | |
208 | else if (klass == JvPrimClass (double)) | |
209 | o = new Double (*(jdouble*)thing); | |
210 | else if (klass == JvPrimClass (boolean)) | |
211 | o = new Boolean (*(jboolean*)thing); | |
212 | else if (klass == JvPrimClass (char)) | |
213 | o = new Character (*(jchar*)thing); | |
214 | else | |
215 | JvFail ("Bad ffi type in proxy"); | |
216 | ||
217 | return o; | |
218 | } | |
219 | ||
220 | ||
221 | // Unbox things with primitive types. | |
222 | static inline void | |
223 | unbox (jobject o, jclass klass, void *rvalue, FFI_TYPE type) | |
224 | { | |
225 | switch (type) | |
226 | { | |
227 | case FFI_TYPE_VOID: | |
228 | return; | |
229 | ||
230 | case FFI_TYPE_POINTER: | |
231 | _Jv_CheckCast (klass, o); | |
232 | *(jobject*)rvalue = o; | |
233 | return; | |
234 | ||
235 | default: | |
236 | ; | |
237 | } | |
238 | ||
239 | // If the value returned ... is null and the interface method's | |
240 | // return type is primitive, then a NullPointerException will be | |
241 | // thrown ... | |
242 | if (klass == JvPrimClass (byte)) | |
243 | { | |
244 | _Jv_CheckCast (&Byte::class$, o); | |
245 | *(jbyte*)rvalue = ((Byte*)o)->byteValue(); | |
246 | } | |
247 | else if (klass == JvPrimClass (short)) | |
248 | { | |
249 | _Jv_CheckCast (&Short::class$, o); | |
250 | *(jshort*)rvalue = ((Short*)o)->shortValue(); | |
251 | } | |
252 | else if (klass == JvPrimClass (int)) | |
253 | { | |
254 | _Jv_CheckCast (&Integer::class$, o); | |
255 | *(jint*)rvalue = ((Integer*)o)->intValue(); | |
256 | } | |
257 | else if (klass == JvPrimClass (long)) | |
258 | { | |
259 | _Jv_CheckCast (&Long::class$, o); | |
260 | *(jlong*)rvalue = ((Long*)o)->longValue(); | |
261 | } | |
262 | else if (klass == JvPrimClass (float)) | |
263 | { | |
264 | _Jv_CheckCast (&Float::class$, o); | |
265 | *(jfloat*)rvalue = ((Float*)o)->floatValue(); | |
266 | } | |
267 | else if (klass == JvPrimClass (double)) | |
268 | { | |
269 | _Jv_CheckCast (&Double::class$, o); | |
270 | *(jdouble*)rvalue = ((Double*)o)->doubleValue(); | |
271 | } | |
272 | else if (klass == JvPrimClass (boolean)) | |
273 | { | |
274 | _Jv_CheckCast (&Boolean::class$, o); | |
275 | *(jboolean*)rvalue = ((Boolean*)o)->booleanValue(); | |
276 | } | |
277 | else if (klass == JvPrimClass (char)) | |
278 | { | |
279 | _Jv_CheckCast (&Character::class$, o); | |
280 | *(jchar*)rvalue = ((Character*)o)->charValue(); | |
281 | } | |
282 | else | |
283 | JvFail ("Bad ffi type in proxy"); | |
284 | } | |
285 | ||
97b8365c TT |
286 | // run_proxy is the entry point for all proxy methods. It boxes up |
287 | // all the arguments and then invokes the invocation handler's invoke() | |
288 | // method. Exceptions are caught and propagated. | |
289 | ||
290 | typedef struct { | |
291 | ffi_closure closure; | |
18fa3240 | 292 | _Jv_ClosureList list; |
97b8365c | 293 | ffi_cif cif; |
97b8365c TT |
294 | _Jv_Method *self; |
295 | ffi_type *arg_types[0]; | |
296 | } ncode_closure; | |
297 | ||
298 | static void | |
299 | run_proxy (ffi_cif *cif, | |
300 | void *rvalue, | |
301 | void **args, | |
302 | void*user_data) | |
303 | { | |
304 | Proxy *proxy = *(Proxy**)args[0]; | |
305 | ncode_closure *self = (ncode_closure *) user_data; | |
306 | ||
307 | // FRAME_DESC registers this particular invocation as the top-most | |
308 | // interpreter frame. This lets the stack tracing code (for | |
309 | // Throwable) print information about the Proxy being run rather | |
310 | // than about Proxy.class itself. FRAME_DESC has a destructor so it | |
311 | // cleans up automatically when this proxy invocation returns. | |
312 | Thread *thread = Thread::currentThread(); | |
313 | _Jv_InterpFrame frame_desc (self->self, thread, proxy->getClass()); | |
314 | ||
a29114a3 AH |
315 | Method *meth = _Jv_GetReflectedMethod (proxy->getClass(), |
316 | self->self->name, | |
317 | self->self->signature); | |
318 | JArray<jclass> *parameter_types = meth->internalGetParameterTypes (); | |
319 | JArray<jclass> *exception_types = meth->internalGetExceptionTypes (); | |
320 | ||
97b8365c TT |
321 | InvocationHandler *handler = proxy->h; |
322 | void *poo | |
a29114a3 | 323 | = _Jv_NewObjectArray (parameter_types->length, &Object::class$, NULL); |
97b8365c TT |
324 | JArray<jobject> *argsArray = (JArray<jobject> *) poo; |
325 | jobject *jargs = elements(argsArray); | |
326 | ||
327 | // FIXME: It must be possible to use fast interface dispatch here, | |
328 | // but I've not quite figured out how to do it. | |
329 | invoke_t *invoke | |
330 | = (invoke_t *)(_Jv_LookupInterfaceMethod | |
331 | (handler->getClass (), | |
332 | _Jv_makeUtf8Const ("invoke"), | |
333 | (_Jv_makeUtf8Const | |
334 | ("(Ljava.lang.Object;Ljava.lang.reflect.Method;[Ljava.lang.Object;)" | |
335 | "Ljava.lang.Object;")))); | |
336 | ||
337 | // Copy and box all the args. | |
338 | int index = 1; | |
a29114a3 AH |
339 | for (int i = 0; i < parameter_types->length; i++, index++) |
340 | jargs[i] = box (args[index], elements(parameter_types)[i], | |
97b8365c TT |
341 | cif->arg_types[index]->type); |
342 | ||
343 | jobject ret; | |
344 | try | |
345 | { | |
a29114a3 | 346 | ret = invoke (handler, proxy, meth, argsArray); |
97b8365c TT |
347 | } |
348 | catch (Throwable *t) | |
349 | { | |
350 | if (_Jv_IsInstanceOf (t, &RuntimeException::class$) | |
351 | || _Jv_IsInstanceOf (t, &Error::class$)) | |
352 | throw t; | |
353 | ||
a29114a3 AH |
354 | Class **throwables = elements (exception_types); |
355 | for (int i = 0; i < exception_types->length; i++) | |
97b8365c TT |
356 | if (_Jv_IsInstanceOf (t, throwables[i])) |
357 | throw t; | |
358 | ||
359 | throw new UndeclaredThrowableException (t); | |
360 | } | |
361 | ||
a29114a3 | 362 | unbox (ret, meth->return_type, rvalue, cif->rtype->type); |
97b8365c TT |
363 | } |
364 | ||
365 | ||
366 | // Given a method and a closure function, create libffi CIF and return | |
367 | // the address of its closure. | |
368 | ||
369 | static void * | |
18fa3240 | 370 | ncode (jclass klass, _Jv_Method *self, closure_fun fun) |
97b8365c TT |
371 | { |
372 | using namespace java::lang::reflect; | |
373 | ||
374 | jboolean staticp = (self->accflags & Modifier::STATIC) != 0; | |
375 | int arg_count = _Jv_count_arguments (self->signature, staticp); | |
376 | ||
18fa3240 | 377 | void *code; |
97b8365c | 378 | ncode_closure *closure = |
18fa3240 AO |
379 | (ncode_closure*)ffi_closure_alloc (sizeof (ncode_closure) |
380 | + arg_count * sizeof (ffi_type*), | |
381 | &code); | |
382 | closure->list.registerClosure (klass, closure); | |
97b8365c TT |
383 | |
384 | _Jv_init_cif (self->signature, | |
385 | arg_count, | |
386 | staticp, | |
387 | &closure->cif, | |
388 | &closure->arg_types[0], | |
389 | NULL); | |
97b8365c TT |
390 | closure->self = self; |
391 | ||
392 | JvAssert ((self->accflags & Modifier::NATIVE) == 0); | |
393 | ||
18fa3240 AO |
394 | ffi_prep_closure_loc (&closure->closure, |
395 | &closure->cif, | |
396 | fun, | |
397 | code, | |
398 | code); | |
97b8365c | 399 | |
18fa3240 | 400 | self->ncode = code; |
97b8365c TT |
401 | return self->ncode; |
402 | } |