123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707 |
- // natMethod.cc - Native code for Method class.
- /* Copyright (C) 1998, 1999, 2000, 2001 , 2002, 2003, 2004, 2005, 2006 Free Software Foundation
- This file is part of libgcj.
- This software is copyrighted work licensed under the terms of the
- Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
- details. */
- #include <config.h>
- #include <gcj/cni.h>
- #include <jvm.h>
- #include <jni.h>
- #include <java-stack.h>
- #include <java/lang/reflect/Method.h>
- #include <java/lang/reflect/Constructor.h>
- #include <java/lang/reflect/InvocationTargetException.h>
- #include <java/lang/reflect/Modifier.h>
- #include <java/lang/Void.h>
- #include <java/lang/Byte.h>
- #include <java/lang/Boolean.h>
- #include <java/lang/Character.h>
- #include <java/lang/Short.h>
- #include <java/lang/Integer.h>
- #include <java/lang/Long.h>
- #include <java/lang/Float.h>
- #include <java/lang/Double.h>
- #include <java/lang/IllegalAccessException.h>
- #include <java/lang/IllegalArgumentException.h>
- #include <java/lang/IncompatibleClassChangeError.h>
- #include <java/lang/NullPointerException.h>
- #include <java/lang/ArrayIndexOutOfBoundsException.h>
- #include <java/lang/VirtualMachineError.h>
- #include <java/lang/Class.h>
- #include <gcj/method.h>
- #include <gnu/gcj/RawData.h>
- #include <java/lang/NoClassDefFoundError.h>
- #include <stdlib.h>
- #if USE_LIBFFI
- #include <ffi.h>
- #else
- #include <java/lang/UnsupportedOperationException.h>
- #endif
- typedef JArray< ::java::lang::annotation::Annotation * > * anno_a_t;
- typedef JArray< JArray< ::java::lang::annotation::Annotation * > *> * anno_aa_t;
- struct cpair
- {
- jclass prim;
- jclass wrap;
- };
- // This is used to determine when a primitive widening conversion is
- // allowed.
- static cpair primitives[] =
- {
- #define BOOLEAN 0
- { JvPrimClass (boolean), &java::lang::Boolean::class$ },
- { JvPrimClass (byte), &java::lang::Byte::class$ },
- #define SHORT 2
- { JvPrimClass (short), &java::lang::Short::class$ },
- #define CHAR 3
- { JvPrimClass (char), &java::lang::Character::class$ },
- { JvPrimClass (int), &java::lang::Integer::class$ },
- { JvPrimClass (long), &java::lang::Long::class$ },
- { JvPrimClass (float), &java::lang::Float::class$ },
- { JvPrimClass (double), &java::lang::Double::class$ },
- { NULL, NULL }
- };
- static inline jboolean
- can_widen (jclass from, jclass to)
- {
- int fromx = -1, tox = -1;
- for (int i = 0; primitives[i].prim; ++i)
- {
- if (primitives[i].wrap == from)
- fromx = i;
- if (primitives[i].prim == to)
- tox = i;
- }
- // Can't handle a miss.
- if (fromx == -1 || tox == -1)
- return false;
- // Boolean arguments may not be widened.
- if (fromx == BOOLEAN && tox != BOOLEAN)
- return false;
- // Nothing promotes to char.
- if (tox == CHAR && fromx != CHAR)
- return false;
- return fromx <= tox;
- }
- #ifdef USE_LIBFFI
- static inline ffi_type *
- get_ffi_type (jclass klass)
- {
- // A special case.
- if (klass == NULL)
- return &ffi_type_pointer;
- ffi_type *r;
- if (klass == JvPrimClass (byte))
- r = &ffi_type_sint8;
- else if (klass == JvPrimClass (short))
- r = &ffi_type_sint16;
- else if (klass == JvPrimClass (int))
- r = &ffi_type_sint32;
- else if (klass == JvPrimClass (long))
- r = &ffi_type_sint64;
- else if (klass == JvPrimClass (float))
- r = &ffi_type_float;
- else if (klass == JvPrimClass (double))
- r = &ffi_type_double;
- else if (klass == JvPrimClass (boolean))
- {
- // On some platforms a bool is a byte, on others an int.
- if (sizeof (jboolean) == sizeof (jbyte))
- r = &ffi_type_sint8;
- else
- {
- JvAssert (sizeof (jboolean) == sizeof (jint));
- r = &ffi_type_sint32;
- }
- }
- else if (klass == JvPrimClass (char))
- r = &ffi_type_uint16;
- else
- {
- JvAssert (! klass->isPrimitive());
- r = &ffi_type_pointer;
- }
- return r;
- }
- #endif // USE_LIBFFI
- jobject
- java::lang::reflect::Method::invoke (jobject obj, jobjectArray args)
- {
- using namespace java::lang::reflect;
- jclass iface = NULL;
-
- if (parameter_types == NULL)
- getType ();
-
- jmethodID meth = _Jv_FromReflectedMethod (this);
- if (Modifier::isStatic(meth->accflags))
- {
- // We have to initialize a static class. It is safe to do this
- // here and not in _Jv_CallAnyMethodA because JNI initializes a
- // class whenever a method lookup is done.
- _Jv_InitClass (declaringClass);
- }
- else
- {
- jclass objClass = JV_CLASS (obj);
- if (! _Jv_IsAssignableFrom (objClass, declaringClass))
- throw new java::lang::IllegalArgumentException;
- }
- // Check accessibility, if required.
- if (! this->isAccessible())
- {
- if (! (Modifier::isPublic (meth->accflags)))
- {
- Class *caller = _Jv_StackTrace::GetCallingClass (&Method::class$);
- if (! _Jv_CheckAccess(caller, declaringClass, meth->accflags))
- throw new IllegalAccessException;
- }
- else
- // Method is public, check to see if class is accessible.
- {
- jint flags = (declaringClass->accflags
- & (Modifier::PUBLIC
- | Modifier::PROTECTED
- | Modifier::PRIVATE));
- if (flags == 0) // i.e. class is package private
- {
- Class *caller = _Jv_StackTrace::GetCallingClass (&Method::class$);
- if (! _Jv_ClassNameSamePackage (caller->name,
- declaringClass->name))
- throw new IllegalAccessException;
- }
- }
- }
- if (declaringClass->isInterface())
- iface = declaringClass;
- return _Jv_CallAnyMethodA (obj, return_type, meth, false,
- parameter_types, args, iface);
- }
- jint
- java::lang::reflect::Method::getModifiersInternal ()
- {
- return _Jv_FromReflectedMethod (this)->accflags;
- }
- jstring
- java::lang::reflect::Method::getSignature()
- {
- return declaringClass->getReflectionSignature (this);
- }
- jobject
- java::lang::reflect::Method::getDefaultValue()
- {
- return declaringClass->getMethodDefaultValue(this);
- }
- anno_a_t
- java::lang::reflect::Method::getDeclaredAnnotationsInternal()
- {
- return (anno_a_t) declaringClass->getDeclaredAnnotations(this, false);
- }
- anno_aa_t
- java::lang::reflect::Method::getParameterAnnotationsInternal()
- {
- return (anno_aa_t) declaringClass->getDeclaredAnnotations(this, true);
- }
- jstring
- java::lang::reflect::Method::getName ()
- {
- if (name == NULL)
- name = _Jv_NewStringUtf8Const (_Jv_FromReflectedMethod (this)->name);
- return name;
- }
- /* Internal method to set return_type and parameter_types fields. */
- void
- java::lang::reflect::Method::getType ()
- {
- _Jv_Method *method = _Jv_FromReflectedMethod (this);
- _Jv_GetTypesFromSignature (method,
- declaringClass,
- ¶meter_types,
- &return_type);
- int count = 0;
- if (method->throws != NULL)
- {
- while (method->throws[count] != NULL)
- ++count;
- }
- exception_types
- = (JArray<jclass> *) JvNewObjectArray (count, &java::lang::Class::class$,
- NULL);
- jclass *elts = elements (exception_types);
- for (int i = 0; i < count; ++i)
- elts[i] = _Jv_FindClass (method->throws[i],
- declaringClass->getClassLoaderInternal ());
- }
- void
- _Jv_GetTypesFromSignature (jmethodID method,
- jclass declaringClass,
- JArray<jclass> **arg_types_out,
- jclass *return_type_out)
- {
- _Jv_Utf8Const* sig = method->signature;
- java::lang::ClassLoader *loader = declaringClass->getClassLoaderInternal();
- char *ptr = sig->chars();
- int numArgs = 0;
- /* First just count the number of parameters. */
- // FIXME: should do some validation here, e.g., that there is only
- // one return type.
- for (; ; ptr++)
- {
- switch (*ptr)
- {
- case 0:
- case ')':
- case 'V':
- break;
- case '[':
- case '(':
- continue;
- case 'B':
- case 'C':
- case 'D':
- case 'F':
- case 'S':
- case 'I':
- case 'J':
- case 'Z':
- numArgs++;
- continue;
- case 'L':
- numArgs++;
- do
- ptr++;
- while (*ptr != ';' && ptr[1] != '\0');
- continue;
- }
- break;
- }
- JArray<jclass> *args = (JArray<jclass> *)
- JvNewObjectArray (numArgs, &java::lang::Class::class$, NULL);
- jclass* argPtr = elements (args);
- for (ptr = sig->chars(); *ptr != '\0'; ptr++)
- {
- if (*ptr == '(')
- continue;
- if (*ptr == ')')
- {
- argPtr = return_type_out;
- continue;
- }
- char *end_ptr;
- jclass type = _Jv_FindClassFromSignature (ptr, loader, &end_ptr);
- if (type == NULL)
- // FIXME: This isn't ideal.
- throw new java::lang::NoClassDefFoundError (sig->toString());
- // ARGPTR can be NULL if we are processing the return value of a
- // call from Constructor.
- if (argPtr)
- *argPtr++ = type;
- ptr = end_ptr;
- }
- *arg_types_out = args;
- }
- // This is a very rough analog of the JNI CallNonvirtual<type>MethodA
- // functions. It handles both Methods and Constructors, and it can
- // handle any return type. In the Constructor case, the `obj'
- // argument is unused and should be NULL; also, the `return_type' is
- // the class that the constructor will construct. RESULT is a pointer
- // to a `jvalue' (see jni.h); for a void method this should be NULL.
- // This function returns an exception (if one was thrown), or NULL if
- // the call went ok.
- void
- _Jv_CallAnyMethodA (jobject obj,
- jclass return_type,
- jmethodID meth,
- jboolean is_constructor,
- jboolean is_virtual_call,
- JArray<jclass> *parameter_types,
- const jvalue *args,
- jvalue *result,
- jboolean is_jni_call,
- jclass iface)
- {
- using namespace java::lang::reflect;
-
- #ifdef USE_LIBFFI
- JvAssert (! is_constructor || ! obj);
- JvAssert (! is_constructor || return_type);
- // See whether call needs an object as the first argument. A
- // constructor does need a `this' argument, but it is one we create.
- jboolean needs_this = false;
- if (is_constructor
- || ! Modifier::isStatic(meth->accflags))
- needs_this = true;
- int param_count = parameter_types->length;
- if (needs_this)
- ++param_count;
- ffi_type *rtype;
- // A constructor itself always returns void.
- if (is_constructor || return_type == JvPrimClass (void))
- rtype = &ffi_type_void;
- else
- rtype = get_ffi_type (return_type);
- ffi_type **argtypes = (ffi_type **) __builtin_alloca (param_count
- * sizeof (ffi_type *));
- jclass *paramelts = elements (parameter_types);
- // Special case for the `this' argument of a constructor. Note that
- // the JDK 1.2 docs specify that the new object must be allocated
- // before argument conversions are done.
- if (is_constructor)
- obj = _Jv_AllocObject (return_type);
- const int size_per_arg = sizeof(jvalue);
- ffi_cif cif;
- char *p = (char *) __builtin_alloca (param_count * size_per_arg);
- // Overallocate to get correct alignment.
- void **values = (void **)
- __builtin_alloca (param_count * sizeof (void *));
- int i = 0;
- if (needs_this)
- {
- // The `NULL' type is `Object'.
- argtypes[i] = get_ffi_type (NULL);
- values[i] = p;
- memcpy (p, &obj, sizeof (jobject));
- p += size_per_arg;
- ++i;
- }
- for (int arg = 0; i < param_count; ++i, ++arg)
- {
- int tsize;
- argtypes[i] = get_ffi_type (paramelts[arg]);
- if (paramelts[arg]->isPrimitive())
- tsize = paramelts[arg]->size();
- else
- tsize = sizeof (jobject);
- // Copy appropriate bits from the jvalue into the ffi array.
- // FIXME: we could do this copying all in one loop, above, by
- // over-allocating a bit.
- // How do we do this without breaking big-endian platforms?
- values[i] = p;
- memcpy (p, &args[arg], tsize);
- p += size_per_arg;
- }
- ffi_abi cabi = FFI_DEFAULT_ABI;
- #if defined (X86_WIN32) && !defined (__CYGWIN__)
- if (needs_this)
- cabi = FFI_THISCALL;
- #endif
- if (ffi_prep_cif (&cif, cabi, param_count,
- rtype, argtypes) != FFI_OK)
- throw new java::lang::VirtualMachineError(JvNewStringLatin1("internal error: ffi_prep_cif failed"));
- using namespace java::lang;
- using namespace java::lang::reflect;
- union
- {
- ffi_arg i;
- jobject o;
- jlong l;
- jfloat f;
- jdouble d;
- } ffi_result;
- switch (rtype->type)
- {
- case FFI_TYPE_VOID:
- break;
- case FFI_TYPE_SINT8:
- result->b = 0;
- break;
- case FFI_TYPE_SINT16:
- result->s = 0;
- break;
- case FFI_TYPE_UINT16:
- result->c = 0;
- break;
- case FFI_TYPE_SINT32:
- result->i = 0;
- break;
- case FFI_TYPE_SINT64:
- result->j = 0;
- break;
- case FFI_TYPE_FLOAT:
- result->f = 0;
- break;
- case FFI_TYPE_DOUBLE:
- result->d = 0;
- break;
- case FFI_TYPE_POINTER:
- result->l = 0;
- break;
- default:
- JvFail ("Unknown ffi_call return type");
- break;
- }
- void *ncode;
- // FIXME: If a vtable index is -1 at this point it is invalid, so we
- // have to use the ncode.
- //
- // This can happen because methods in final classes don't have
- // vtable entries, but _Jv_isVirtualMethod() doesn't know that. We
- // could solve this problem by allocating a vtable index for methods
- // in final classes.
- if (is_virtual_call
- && ! Modifier::isFinal (meth->accflags)
- && (_Jv_ushort)-1 != meth->index)
- {
- _Jv_VTable *vtable = *(_Jv_VTable **) obj;
- if (iface == NULL)
- {
- if (is_jni_call && Modifier::isAbstract (meth->accflags))
- {
- // With JNI we don't know if this is an interface call
- // or a call to an abstract method. Look up the method
- // by name, the slow way.
- _Jv_Method *concrete_meth
- = _Jv_LookupDeclaredMethod (vtable->clas,
- meth->name,
- meth->signature,
- NULL);
- if (concrete_meth == NULL
- || concrete_meth->ncode == NULL
- || Modifier::isAbstract(concrete_meth->accflags))
- throw new java::lang::IncompatibleClassChangeError
- (_Jv_GetMethodString (vtable->clas, meth));
- ncode = concrete_meth->ncode;
- }
- else
- ncode = vtable->get_method (meth->index);
- }
- else
- ncode = _Jv_LookupInterfaceMethodIdx (vtable->clas, iface,
- meth->index);
- }
- else
- {
- ncode = meth->ncode;
- }
- try
- {
- ffi_call (&cif, (void (*)()) ncode, &ffi_result, values);
- }
- catch (Throwable *ex)
- {
- // For JNI we just throw the real error. For reflection, we
- // wrap the underlying method's exception in an
- // InvocationTargetException.
- if (! is_jni_call)
- ex = new InvocationTargetException (ex);
- throw ex;
- }
- // Since ffi_call returns integer values promoted to a word, use
- // a narrowing conversion for jbyte, jchar, etc. results.
- // Note that boolean is handled either by the FFI_TYPE_SINT8 or
- // FFI_TYPE_SINT32 case.
- if (is_constructor)
- result->l = obj;
- else
- {
- switch (rtype->type)
- {
- case FFI_TYPE_VOID:
- break;
- case FFI_TYPE_SINT8:
- result->b = (jbyte)ffi_result.i;
- break;
- case FFI_TYPE_SINT16:
- result->s = (jshort)ffi_result.i;
- break;
- case FFI_TYPE_UINT16:
- result->c = (jchar)ffi_result.i;
- break;
- case FFI_TYPE_SINT32:
- result->i = (jint)ffi_result.i;
- break;
- case FFI_TYPE_SINT64:
- result->j = (jlong)ffi_result.l;
- break;
- case FFI_TYPE_FLOAT:
- result->f = (jfloat)ffi_result.f;
- break;
- case FFI_TYPE_DOUBLE:
- result->d = (jdouble)ffi_result.d;
- break;
- case FFI_TYPE_POINTER:
- result->l = (jobject)ffi_result.o;
- break;
- default:
- JvFail ("Unknown ffi_call return type");
- break;
- }
- }
- #else
- throw new java::lang::UnsupportedOperationException(JvNewStringLatin1("reflection not available in this build"));
- #endif // USE_LIBFFI
- }
- // This is another version of _Jv_CallAnyMethodA, but this one does
- // more checking and is used by the reflection (and not JNI) code.
- jobject
- _Jv_CallAnyMethodA (jobject obj,
- jclass return_type,
- jmethodID meth,
- jboolean is_constructor,
- JArray<jclass> *parameter_types,
- jobjectArray args,
- jclass iface)
- {
- if (parameter_types->length == 0 && args == NULL)
- {
- // The JDK accepts this, so we do too.
- }
- else if (parameter_types->length != args->length)
- throw new java::lang::IllegalArgumentException;
- int param_count = parameter_types->length;
- jclass *paramelts = elements (parameter_types);
- jobject *argelts = args == NULL ? NULL : elements (args);
- jvalue argvals[param_count];
- #define COPY(Where, What, Type) \
- do { \
- Type val = (What); \
- memcpy ((Where), &val, sizeof (Type)); \
- } while (0)
- for (int i = 0; i < param_count; ++i)
- {
- jclass k = argelts[i] ? argelts[i]->getClass() : NULL;
- if (paramelts[i]->isPrimitive())
- {
- if (! argelts[i]
- || ! k
- || ! can_widen (k, paramelts[i]))
- throw new java::lang::IllegalArgumentException;
-
- if (paramelts[i] == JvPrimClass (boolean))
- COPY (&argvals[i],
- ((java::lang::Boolean *) argelts[i])->booleanValue(),
- jboolean);
- else if (paramelts[i] == JvPrimClass (char))
- COPY (&argvals[i],
- ((java::lang::Character *) argelts[i])->charValue(),
- jchar);
- else
- {
- java::lang::Number *num = (java::lang::Number *) argelts[i];
- if (paramelts[i] == JvPrimClass (byte))
- COPY (&argvals[i], num->byteValue(), jbyte);
- else if (paramelts[i] == JvPrimClass (short))
- COPY (&argvals[i], num->shortValue(), jshort);
- else if (paramelts[i] == JvPrimClass (int))
- COPY (&argvals[i], num->intValue(), jint);
- else if (paramelts[i] == JvPrimClass (long))
- COPY (&argvals[i], num->longValue(), jlong);
- else if (paramelts[i] == JvPrimClass (float))
- COPY (&argvals[i], num->floatValue(), jfloat);
- else if (paramelts[i] == JvPrimClass (double))
- COPY (&argvals[i], num->doubleValue(), jdouble);
- }
- }
- else
- {
- if (argelts[i] && ! paramelts[i]->isAssignableFrom (k))
- throw new java::lang::IllegalArgumentException;
- COPY (&argvals[i], argelts[i], jobject);
- }
- }
- jvalue ret_value;
- _Jv_CallAnyMethodA (obj, return_type, meth, is_constructor,
- _Jv_isVirtualMethod (meth),
- parameter_types, argvals, &ret_value,
- false, iface);
- jobject r;
- #define VAL(Wrapper, Field) (new Wrapper (ret_value.Field))
- if (is_constructor)
- r = ret_value.l;
- else if (return_type == JvPrimClass (byte))
- r = VAL (java::lang::Byte, b);
- else if (return_type == JvPrimClass (short))
- r = VAL (java::lang::Short, s);
- else if (return_type == JvPrimClass (int))
- r = VAL (java::lang::Integer, i);
- else if (return_type == JvPrimClass (long))
- r = VAL (java::lang::Long, j);
- else if (return_type == JvPrimClass (float))
- r = VAL (java::lang::Float, f);
- else if (return_type == JvPrimClass (double))
- r = VAL (java::lang::Double, d);
- else if (return_type == JvPrimClass (boolean))
- r = VAL (java::lang::Boolean, z);
- else if (return_type == JvPrimClass (char))
- r = VAL (java::lang::Character, c);
- else if (return_type == JvPrimClass (void))
- r = NULL;
- else
- {
- JvAssert (return_type == NULL || ! return_type->isPrimitive());
- r = ret_value.l;
- }
- return r;
- }
|