diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libjava/java/lang/reflect/natMethod.cc | |
download | cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.bz2 cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libjava/java/lang/reflect/natMethod.cc')
-rw-r--r-- | libjava/java/lang/reflect/natMethod.cc | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/libjava/java/lang/reflect/natMethod.cc b/libjava/java/lang/reflect/natMethod.cc new file mode 100644 index 000000000..d95c92f84 --- /dev/null +++ b/libjava/java/lang/reflect/natMethod.cc @@ -0,0 +1,701 @@ +// 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; + } + + if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, 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; +} |