source: trunk/gcc/libjava/java/lang/reflect/natMethod.cc

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 15.2 KB
Line 
1// natMethod.cc - Native code for Method class.
2
3/* Copyright (C) 1998, 1999, 2000, 2001 , 2002 Free Software Foundation
4
5 This file is part of libgcj.
6
7This software is copyrighted work licensed under the terms of the
8Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
9details. */
10
11#include <config.h>
12
13#include <gcj/cni.h>
14#include <jvm.h>
15#include <jni.h>
16
17#include <java/lang/reflect/Method.h>
18#include <java/lang/reflect/Constructor.h>
19#include <java/lang/reflect/InvocationTargetException.h>
20#include <java/lang/reflect/Modifier.h>
21
22#include <java/lang/Void.h>
23#include <java/lang/Byte.h>
24#include <java/lang/Boolean.h>
25#include <java/lang/Character.h>
26#include <java/lang/Short.h>
27#include <java/lang/Integer.h>
28#include <java/lang/Long.h>
29#include <java/lang/Float.h>
30#include <java/lang/Double.h>
31#include <java/lang/IllegalArgumentException.h>
32#include <java/lang/NullPointerException.h>
33#include <java/lang/Class.h>
34#include <gcj/method.h>
35#include <gnu/gcj/RawData.h>
36
37#include <stdlib.h>
38
39#if USE_LIBFFI
40#include <ffi.h>
41#else
42#include <java/lang/UnsupportedOperationException.h>
43#endif
44
45struct cpair
46{
47 jclass prim;
48 jclass wrap;
49};
50
51// This is used to determine when a primitive widening conversion is
52// allowed.
53static cpair primitives[] =
54{
55#define BOOLEAN 0
56 { JvPrimClass (boolean), &java::lang::Boolean::class$ },
57 { JvPrimClass (byte), &java::lang::Byte::class$ },
58#define SHORT 2
59 { JvPrimClass (short), &java::lang::Short::class$ },
60#define CHAR 3
61 { JvPrimClass (char), &java::lang::Character::class$ },
62 { JvPrimClass (int), &java::lang::Integer::class$ },
63 { JvPrimClass (long), &java::lang::Long::class$ },
64 { JvPrimClass (float), &java::lang::Float::class$ },
65 { JvPrimClass (double), &java::lang::Double::class$ },
66 { NULL, NULL }
67};
68
69static inline jboolean
70can_widen (jclass from, jclass to)
71{
72 int fromx = -1, tox = -1;
73
74 for (int i = 0; primitives[i].prim; ++i)
75 {
76 if (primitives[i].wrap == from)
77 fromx = i;
78 if (primitives[i].prim == to)
79 tox = i;
80 }
81
82 // Can't handle a miss.
83 if (fromx == -1 || tox == -1)
84 return false;
85 // Boolean arguments may not be widened.
86 if (fromx == BOOLEAN && tox != BOOLEAN)
87 return false;
88 // Nothing promotes to char.
89 if (tox == CHAR && fromx != CHAR)
90 return false;
91
92 return fromx <= tox;
93}
94
95#ifdef USE_LIBFFI
96static inline ffi_type *
97get_ffi_type (jclass klass)
98{
99 // A special case.
100 if (klass == NULL)
101 return &ffi_type_pointer;
102
103 ffi_type *r;
104 if (klass == JvPrimClass (byte))
105 r = &ffi_type_sint8;
106 else if (klass == JvPrimClass (short))
107 r = &ffi_type_sint16;
108 else if (klass == JvPrimClass (int))
109 r = &ffi_type_sint32;
110 else if (klass == JvPrimClass (long))
111 r = &ffi_type_sint64;
112 else if (klass == JvPrimClass (float))
113 r = &ffi_type_float;
114 else if (klass == JvPrimClass (double))
115 r = &ffi_type_double;
116 else if (klass == JvPrimClass (boolean))
117 {
118 // On some platforms a bool is a byte, on others an int.
119 if (sizeof (jboolean) == sizeof (jbyte))
120 r = &ffi_type_sint8;
121 else
122 {
123 JvAssert (sizeof (jboolean) == sizeof (jint));
124 r = &ffi_type_sint32;
125 }
126 }
127 else if (klass == JvPrimClass (char))
128 r = &ffi_type_uint16;
129 else
130 {
131 JvAssert (! klass->isPrimitive());
132 r = &ffi_type_pointer;
133 }
134
135 return r;
136}
137#endif // USE_LIBFFI
138
139jobject
140java::lang::reflect::Method::invoke (jobject obj, jobjectArray args)
141{
142 if (parameter_types == NULL)
143 getType ();
144
145 jmethodID meth = _Jv_FromReflectedMethod (this);
146 if (! java::lang::reflect::Modifier::isStatic(meth->accflags))
147 {
148 jclass k = obj ? obj->getClass() : NULL;
149 if (! obj)
150 throw new java::lang::NullPointerException;
151 if (! declaringClass->isAssignableFrom(k))
152 throw new java::lang::IllegalArgumentException;
153 // FIXME: access checks.
154
155 // Find the possibly overloaded method based on the runtime type
156 // of the object.
157 meth = _Jv_LookupDeclaredMethod (k, meth->name, meth->signature);
158 }
159 else
160 {
161 // We have to initialize a static class. It is safe to do this
162 // here and not in _Jv_CallAnyMethodA because JNI initializes a
163 // class whenever a method lookup is done.
164 _Jv_InitClass (declaringClass);
165 }
166
167 return _Jv_CallAnyMethodA (obj, return_type, meth, false,
168 parameter_types, args);
169}
170
171jint
172java::lang::reflect::Method::getModifiers ()
173{
174 // Ignore all unknown flags.
175 return _Jv_FromReflectedMethod (this)->accflags & Modifier::ALL_FLAGS;
176}
177
178jstring
179java::lang::reflect::Method::getName ()
180{
181 if (name == NULL)
182 name = _Jv_NewStringUtf8Const (_Jv_FromReflectedMethod (this)->name);
183 return name;
184}
185
186/* Internal method to set return_type and parameter_types fields. */
187
188void
189java::lang::reflect::Method::getType ()
190{
191 _Jv_Method *method = _Jv_FromReflectedMethod (this);
192 _Jv_GetTypesFromSignature (method,
193 declaringClass,
194 &parameter_types,
195 &return_type);
196
197 int count = 0;
198 if (method->throws != NULL)
199 {
200 while (method->throws[count] != NULL)
201 ++count;
202 }
203
204 exception_types
205 = (JArray<jclass> *) JvNewObjectArray (count, &java::lang::Class::class$,
206 NULL);
207 jclass *elts = elements (exception_types);
208 for (int i = 0; i < count; ++i)
209 elts[i] = _Jv_FindClass (method->throws[i],
210 declaringClass->getClassLoader ());
211}
212
213void
214_Jv_GetTypesFromSignature (jmethodID method,
215 jclass declaringClass,
216 JArray<jclass> **arg_types_out,
217 jclass *return_type_out)
218{
219
220 _Jv_Utf8Const* sig = method->signature;
221 java::lang::ClassLoader *loader = declaringClass->getClassLoader();
222 char *ptr = sig->data;
223 int numArgs = 0;
224 /* First just count the number of parameters. */
225 for (; ; ptr++)
226 {
227 switch (*ptr)
228 {
229 case 0:
230 case ')':
231 case 'V':
232 break;
233 case '[':
234 case '(':
235 continue;
236 case 'B':
237 case 'C':
238 case 'D':
239 case 'F':
240 case 'S':
241 case 'I':
242 case 'J':
243 case 'Z':
244 numArgs++;
245 continue;
246 case 'L':
247 numArgs++;
248 do
249 ptr++;
250 while (*ptr != ';' && ptr[1] != '\0');
251 continue;
252 }
253 break;
254 }
255
256 JArray<jclass> *args = (JArray<jclass> *)
257 JvNewObjectArray (numArgs, &java::lang::Class::class$, NULL);
258 jclass* argPtr = elements (args);
259 for (ptr = sig->data; *ptr != '\0'; ptr++)
260 {
261 int num_arrays = 0;
262 jclass type;
263 for (; *ptr == '['; ptr++)
264 num_arrays++;
265 switch (*ptr)
266 {
267 default:
268 return;
269 case ')':
270 argPtr = return_type_out;
271 continue;
272 case '(':
273 continue;
274 case 'V':
275 case 'B':
276 case 'C':
277 case 'D':
278 case 'F':
279 case 'S':
280 case 'I':
281 case 'J':
282 case 'Z':
283 type = _Jv_FindClassFromSignature(ptr, loader);
284 break;
285 case 'L':
286 type = _Jv_FindClassFromSignature(ptr, loader);
287 do
288 ptr++;
289 while (*ptr != ';' && ptr[1] != '\0');
290 break;
291 }
292
293 // FIXME: 2'nd argument should be "current loader"
294 while (--num_arrays >= 0)
295 type = _Jv_GetArrayClass (type, 0);
296 // ARGPTR can be NULL if we are processing the return value of a
297 // call from Constructor.
298 if (argPtr)
299 *argPtr++ = type;
300 }
301 *arg_types_out = args;
302}
303
304// This is a very rough analog of the JNI CallNonvirtual<type>MethodA
305// functions. It handles both Methods and Constructors, and it can
306// handle any return type. In the Constructor case, the `obj'
307// argument is unused and should be NULL; also, the `return_type' is
308// the class that the constructor will construct. RESULT is a pointer
309// to a `jvalue' (see jni.h); for a void method this should be NULL.
310// This function returns an exception (if one was thrown), or NULL if
311// the call went ok.
312jthrowable
313_Jv_CallAnyMethodA (jobject obj,
314 jclass return_type,
315 jmethodID meth,
316 jboolean is_constructor,
317 JArray<jclass> *parameter_types,
318 jvalue *args,
319 jvalue *result)
320{
321#ifdef USE_LIBFFI
322 JvAssert (! is_constructor || ! obj);
323 JvAssert (! is_constructor || return_type);
324
325 // See whether call needs an object as the first argument. A
326 // constructor does need a `this' argument, but it is one we create.
327 jboolean needs_this = false;
328 if (is_constructor
329 || ! java::lang::reflect::Modifier::isStatic(meth->accflags))
330 needs_this = true;
331
332 int param_count = parameter_types->length;
333 if (needs_this)
334 ++param_count;
335
336 ffi_type *rtype;
337 // A constructor itself always returns void.
338 if (is_constructor || return_type == JvPrimClass (void))
339 rtype = &ffi_type_void;
340 else
341 rtype = get_ffi_type (return_type);
342 ffi_type **argtypes = (ffi_type **) __builtin_alloca (param_count
343 * sizeof (ffi_type *));
344
345 jclass *paramelts = elements (parameter_types);
346
347 // FIXME: at some point the compiler is going to add extra arguments
348 // to some functions. In particular we are going to do this for
349 // handling access checks in reflection. We must add these hidden
350 // arguments here.
351
352 // Special case for the `this' argument of a constructor. Note that
353 // the JDK 1.2 docs specify that the new object must be allocated
354 // before argument conversions are done.
355 if (is_constructor)
356 {
357 // FIXME: must special-case String, arrays, maybe others here.
358 obj = JvAllocObject (return_type);
359 }
360
361 const int size_per_arg = sizeof(jvalue);
362 ffi_cif cif;
363
364 char *p = (char *) __builtin_alloca (param_count * size_per_arg);
365 // Overallocate to get correct alignment.
366 void **values = (void **)
367 __builtin_alloca (param_count * sizeof (void *));
368
369 int i = 0;
370 if (needs_this)
371 {
372 // The `NULL' type is `Object'.
373 argtypes[i] = get_ffi_type (NULL);
374 values[i] = p;
375 memcpy (p, &obj, sizeof (jobject));
376 p += size_per_arg;
377 ++i;
378 }
379
380 for (int arg = 0; i < param_count; ++i, ++arg)
381 {
382 int tsize;
383
384 argtypes[i] = get_ffi_type (paramelts[arg]);
385 if (paramelts[arg]->isPrimitive())
386 tsize = paramelts[arg]->size();
387 else
388 tsize = sizeof (jobject);
389
390 // Copy appropriate bits from the jvalue into the ffi array.
391 // FIXME: we could do this copying all in one loop, above, by
392 // over-allocating a bit.
393 // How do we do this without breaking big-endian platforms?
394 values[i] = p;
395 memcpy (p, &args[arg], tsize);
396 p += size_per_arg;
397 }
398
399 if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, param_count,
400 rtype, argtypes) != FFI_OK)
401 {
402 // FIXME: throw some kind of VirtualMachineError here.
403 }
404
405 using namespace java::lang;
406 using namespace java::lang::reflect;
407
408 Throwable *ex = NULL;
409
410 union
411 {
412 ffi_arg i;
413 jobject o;
414 jlong l;
415 jfloat f;
416 jdouble d;
417 } ffi_result;
418
419 try
420 {
421 ffi_call (&cif, (void (*)()) meth->ncode, &ffi_result, values);
422 }
423 catch (Throwable *ex2)
424 {
425 // FIXME: this is wrong for JNI. But if we just return the
426 // exception, then the non-JNI cases won't be able to
427 // distinguish it from exceptions we might generate ourselves.
428 // Sigh.
429 ex = new InvocationTargetException (ex2);
430 }
431
432 // Since ffi_call returns integer values promoted to a word, use
433 // a narrowing conversion for jbyte, jchar, etc. results.
434 // Note that boolean is handled either by the FFI_TYPE_SINT8 or
435 // FFI_TYPE_SINT32 case.
436 if (is_constructor)
437 result->l = obj;
438 else
439 {
440 switch (rtype->type)
441 {
442 case FFI_TYPE_VOID:
443 break;
444 case FFI_TYPE_SINT8:
445 result->b = (jbyte)ffi_result.i;
446 break;
447 case FFI_TYPE_SINT16:
448 result->s = (jshort)ffi_result.i;
449 break;
450 case FFI_TYPE_UINT16:
451 result->c = (jchar)ffi_result.i;
452 break;
453 case FFI_TYPE_SINT32:
454 result->i = (jint)ffi_result.i;
455 break;
456 case FFI_TYPE_SINT64:
457 result->j = (jlong)ffi_result.l;
458 break;
459 case FFI_TYPE_FLOAT:
460 result->f = (jfloat)ffi_result.f;
461 break;
462 case FFI_TYPE_DOUBLE:
463 result->d = (jdouble)ffi_result.d;
464 break;
465 case FFI_TYPE_POINTER:
466 result->l = (jobject)ffi_result.o;
467 break;
468 default:
469 JvFail ("Unknown ffi_call return type");
470 break;
471 }
472 }
473
474 return ex;
475#else
476 throw new java::lang::UnsupportedOperationException;
477 return 0;
478#endif // USE_LIBFFI
479}
480
481// This is another version of _Jv_CallAnyMethodA, but this one does
482// more checking and is used by the reflection (and not JNI) code.
483jobject
484_Jv_CallAnyMethodA (jobject obj,
485 jclass return_type,
486 jmethodID meth,
487 jboolean is_constructor,
488 JArray<jclass> *parameter_types,
489 jobjectArray args)
490{
491 // FIXME: access checks.
492
493 if (parameter_types->length == 0 && args == NULL)
494 {
495 // The JDK accepts this, so we do too.
496 }
497 else if (parameter_types->length != args->length)
498 throw new java::lang::IllegalArgumentException;
499
500 int param_count = parameter_types->length;
501
502 jclass *paramelts = elements (parameter_types);
503 jobject *argelts = args == NULL ? NULL : elements (args);
504 jvalue argvals[param_count];
505
506#define COPY(Where, What, Type) \
507 do { \
508 Type val = (What); \
509 memcpy ((Where), &val, sizeof (Type)); \
510 } while (0)
511
512 for (int i = 0; i < param_count; ++i)
513 {
514 jclass k = argelts[i] ? argelts[i]->getClass() : NULL;
515 if (paramelts[i]->isPrimitive())
516 {
517 if (! argelts[i]
518 || ! k
519 || ! can_widen (k, paramelts[i]))
520 throw new java::lang::IllegalArgumentException;
521
522 if (paramelts[i] == JvPrimClass (boolean))
523 COPY (&argvals[i],
524 ((java::lang::Boolean *) argelts[i])->booleanValue(),
525 jboolean);
526 else if (paramelts[i] == JvPrimClass (char))
527 COPY (&argvals[i],
528 ((java::lang::Character *) argelts[i])->charValue(),
529 jchar);
530 else
531 {
532 java::lang::Number *num = (java::lang::Number *) argelts[i];
533 if (paramelts[i] == JvPrimClass (byte))
534 COPY (&argvals[i], num->byteValue(), jbyte);
535 else if (paramelts[i] == JvPrimClass (short))
536 COPY (&argvals[i], num->shortValue(), jshort);
537 else if (paramelts[i] == JvPrimClass (int))
538 COPY (&argvals[i], num->intValue(), jint);
539 else if (paramelts[i] == JvPrimClass (long))
540 COPY (&argvals[i], num->longValue(), jlong);
541 else if (paramelts[i] == JvPrimClass (float))
542 COPY (&argvals[i], num->floatValue(), jfloat);
543 else if (paramelts[i] == JvPrimClass (double))
544 COPY (&argvals[i], num->doubleValue(), jdouble);
545 }
546 }
547 else
548 {
549 if (argelts[i] && ! paramelts[i]->isAssignableFrom (k))
550 throw new java::lang::IllegalArgumentException;
551 COPY (&argvals[i], argelts[i], jobject);
552 }
553 }
554
555 jvalue ret_value;
556 java::lang::Throwable *ex = _Jv_CallAnyMethodA (obj,
557 return_type,
558 meth,
559 is_constructor,
560 parameter_types,
561 argvals,
562 &ret_value);
563
564 if (ex)
565 throw ex;
566
567 jobject r;
568#define VAL(Wrapper, Field) (new Wrapper (ret_value.Field))
569 if (is_constructor)
570 r = ret_value.l;
571 else if (return_type == JvPrimClass (byte))
572 r = VAL (java::lang::Byte, b);
573 else if (return_type == JvPrimClass (short))
574 r = VAL (java::lang::Short, s);
575 else if (return_type == JvPrimClass (int))
576 r = VAL (java::lang::Integer, i);
577 else if (return_type == JvPrimClass (long))
578 r = VAL (java::lang::Long, j);
579 else if (return_type == JvPrimClass (float))
580 r = VAL (java::lang::Float, f);
581 else if (return_type == JvPrimClass (double))
582 r = VAL (java::lang::Double, d);
583 else if (return_type == JvPrimClass (boolean))
584 r = VAL (java::lang::Boolean, z);
585 else if (return_type == JvPrimClass (char))
586 r = VAL (java::lang::Character, c);
587 else if (return_type == JvPrimClass (void))
588 r = NULL;
589 else
590 {
591 JvAssert (return_type == NULL || ! return_type->isPrimitive());
592 r = ret_value.l;
593 }
594
595 return r;
596}
Note: See TracBrowser for help on using the repository browser.