From 0f918fea8b46618edfb2d302c9a920ceb64c22d0 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Tue, 4 Jan 2000 08:46:52 +0000 Subject: [multiple changes] 2000-01-04 Tom Tromey * java/lang/reflect/natConstructor.cc (newInstance): Pass declaring class as return_type argument to _Jv_CallNonvirtualMethodA. * java/lang/reflect/natMethod.cc (_Jv_CallNonvirtualMethodA): In constructor case, create object and use it as `this' argument. * java/lang/Class.h (_getConstructors): Declare. (_getFields): Declare. * java/lang/Class.java (getConstructors): Wrote. (_getConstructors): New native method. (getDeclaredConstructors): Wrote. (_getFields): Declare new native method. * java/lang/natClass.cc (_Jv_LookupInterfaceMethod): Removed incorrect comment. (getMethod): Work correctly when class is primitive. (getDeclaredMethods): Likewise. Compute offset using `method', not `mptr'. (getDeclaredMethod): Likewise. (getConstructor): Wrote. (ConstructorClass): New define. (getDeclaredConstructor): Wrote. (_getConstructors): New method. (_getFields): New method. (getFields): Wrote. * Makefile.in: Rebuilt. * Makefile.am (AM_CXXFLAGS): Added -D_GNU_SOURCE. * prims.cc: Remove `#pragma implementation'. * gcj/array.h: Remove `#pragma interface'. * prims.cc (_Jv_equaln): New function. * java/lang/Class.java (getSignature): Declare. * resolve.cc (_Jv_LookupDeclaredMethod): Moved to natClass.cc. * java/lang/natClass.cc (_Jv_LookupDeclaredMethod): Moved from resolve.cc. (getSignature): New method. (getDeclaredMethod): Wrote. (getMethod): Wrote. Include StringBuffer.h. * java/lang/Class.h (Class): Added _Jv_FromReflectedConstructor as a friend. Unconditionally declare _Jv_LookupDeclaredMethod as a friend. (getSignature): Declare. * include/jvm.h (_Jv_GetTypesFromSignature): Declare. (_Jv_equaln): Declare. (_Jv_CallNonvirtualMethodA): Declare. * Makefile.in: Rebuilt. * Makefile.am (nat_source_files): Added natConstructor.cc. (java/lang/reflect/Constructor.h): New target. * java/lang/reflect/natConstructor.cc: New file. * java/lang/reflect/Constructor.java (newInstance): Now native. (declaringClass): Renamed from decl_class. (offset): Renamed from index. (getType): New native method. (getModifiers): Now native. (getParameterTypes): Call getType if required. (hashCode): Include hash code from declaring class. (modifiers): Removed. (toString): Call getType if required. * gcj/method.h (_Jv_FromReflectedConstructor): New function. * java/lang/reflect/natMethod.cc (hack_call): New method. Removed `#if 0' around FFI code. Include . (invoke): Use _Jv_CallNonvirtualMethodA. Throw IllegalArgumentException when argument object and class disagree. (_Jv_GetTypesFromSignature): New function. (getType): Use it. (ObjectClass): New define. (_Jv_CallNonvirtualMethodA): New function. * java/lang/reflect/Method.java (hack_trampoline): New method. (hack_call): New native method. 1999-12-21 Per Bothner * java/lang/natClass.cc (getDeclaredMethods): Correctly compute offset in new Method. From-SVN: r31199 --- libjava/java/lang/reflect/Constructor.java | 47 ++-- libjava/java/lang/reflect/Method.java | 28 ++- libjava/java/lang/reflect/natConstructor.cc | 53 ++++ libjava/java/lang/reflect/natMethod.cc | 364 +++++++++++++++++----------- 4 files changed, 331 insertions(+), 161 deletions(-) create mode 100644 libjava/java/lang/reflect/natConstructor.cc (limited to 'libjava/java/lang/reflect') diff --git a/libjava/java/lang/reflect/Constructor.java b/libjava/java/lang/reflect/Constructor.java index 466c120..7815d7c 100644 --- a/libjava/java/lang/reflect/Constructor.java +++ b/libjava/java/lang/reflect/Constructor.java @@ -28,12 +28,12 @@ public final class Constructor extends AccessibleObject implements Member if (! (obj instanceof Constructor)) return false; Constructor c = (Constructor) obj; - return decl_class == c.decl_class && index == c.index; + return declaringClass == c.declaringClass && offset == c.offset; } public Class getDeclaringClass () { - return decl_class; + return declaringClass; } public Class[] getExceptionTypes () @@ -41,40 +41,39 @@ public final class Constructor extends AccessibleObject implements Member return (Class[]) exception_types.clone(); } - public int getModifiers () - { - return modifiers; - } + public native int getModifiers (); public String getName () - { - return decl_class.getName(); - } + { + return declaringClass.getName(); + } public Class[] getParameterTypes () { + if (parameter_types == null) + getType (); return (Class[]) parameter_types.clone(); } public int hashCode () { // FIXME. - return getName().hashCode(); + return getName().hashCode() + declaringClass.getName().hashCode(); } - // FIXME: this must be native. Should share implementation with - // Method.invoke. - public Object newInstance (Object[] args) + // Update cached values from method descriptor in class. + private native void getType (); + + public native Object newInstance (Object[] args) throws InstantiationException, IllegalAccessException, - IllegalArgumentException, InvocationTargetException - { - return null; - } + IllegalArgumentException, InvocationTargetException; public String toString () { + if (parameter_types == null) + getType (); StringBuffer b = new StringBuffer (); - b.append(Modifier.toString(modifiers)); + b.append(Modifier.toString(getModifiers())); b.append(" "); b.append(getName()); b.append("("); @@ -88,19 +87,19 @@ public final class Constructor extends AccessibleObject implements Member return b.toString(); } - // Can't create these. FIXME. + // Can't create these. private Constructor () { } // Declaring class. - private Class decl_class; + private Class declaringClass; + // Exception types. private Class[] exception_types; - // Modifiers. - private int modifiers; // Parameter types. private Class[] parameter_types; - // Index of this method in declaring class' method table. - private int index; + + // Offset in bytes from the start of declaringClass's methods array. + private int offset; } diff --git a/libjava/java/lang/reflect/Method.java b/libjava/java/lang/reflect/Method.java index e0571f8..dd87816 100644 --- a/libjava/java/lang/reflect/Method.java +++ b/libjava/java/lang/reflect/Method.java @@ -10,6 +10,8 @@ details. */ package java.lang.reflect; +import gnu.gcj.RawData; + /** * @author Tom Tromey * @date December 12, 1998 @@ -17,7 +19,7 @@ package java.lang.reflect; /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 * "The Java Language Specification", ISBN 0-201-63451-1 * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. - * Status: Incomplete: invoke() needs to be finished. + * Status: Complete, but not correct: access checks aren't done. */ public final class Method extends AccessibleObject implements Member @@ -66,6 +68,30 @@ public final class Method extends AccessibleObject implements Member return name.hashCode() + declaringClass.getName().hashCode(); } + // This is used to perform an actual method call via ffi. + private static final native void hack_call (RawData cif, + RawData method, + RawData ret_value, + RawData values); + + // Perform an ffi call while capturing exceptions. We have to do + // this because we can't catch Java exceptions from C++. + static final Throwable hack_trampoline (RawData cif, + RawData method, + RawData ret_value, + RawData values) + { + try + { + hack_call (cif, method, ret_value, values); + } + catch (Throwable x) + { + return x; + } + return null; + } + public native Object invoke (Object obj, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; diff --git a/libjava/java/lang/reflect/natConstructor.cc b/libjava/java/lang/reflect/natConstructor.cc new file mode 100644 index 0000000..1197e68 --- /dev/null +++ b/libjava/java/lang/reflect/natConstructor.cc @@ -0,0 +1,53 @@ +// natConstructor.cc - Native code for Constructor class. + +/* Copyright (C) 1999, 2000 Cygnus Solutions + + 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 + +#include +#include + +#include +#include +#include +#include +#include +#include + +jint +java::lang::reflect::Constructor::getModifiers () +{ + return _Jv_FromReflectedConstructor (this)->accflags; +} + +void +java::lang::reflect::Constructor::getType () +{ + _Jv_GetTypesFromSignature (_Jv_FromReflectedConstructor (this), + declaringClass, + ¶meter_types, + NULL); +} + +jobject +java::lang::reflect::Constructor::newInstance (jobjectArray args) +{ + if (parameter_types == NULL) + getType (); + + using namespace java::lang::reflect; + if (Modifier::isAbstract (declaringClass->getModifiers())) + JvThrow (new InstantiationException); + + jmethodID meth = _Jv_FromReflectedConstructor (this); + // In the constructor case the return type is the type of the + // constructor. + return _Jv_CallNonvirtualMethodA (NULL, declaringClass, meth, true, + parameter_types, args); +} diff --git a/libjava/java/lang/reflect/natMethod.cc b/libjava/java/lang/reflect/natMethod.cc index 0130ac4..5635b9f 100644 --- a/libjava/java/lang/reflect/natMethod.cc +++ b/libjava/java/lang/reflect/natMethod.cc @@ -1,6 +1,6 @@ // natMethod.cc - Native code for Method class. -/* Copyright (C) 1998, 1999 Cygnus Solutions +/* Copyright (C) 1998, 1999, 2000 Cygnus Solutions This file is part of libgcj. @@ -8,14 +8,13 @@ This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ -// This is about 90% done. Search for FIXME to see what remains. - #include #include #include #include +#include #include #include @@ -32,14 +31,15 @@ details. */ #include #include #include +#include +#define ObjectClass _CL_Q34java4lang6Object +extern java::lang::Class ObjectClass; #define ClassClass _CL_Q34java4lang5Class extern java::lang::Class ClassClass; #include -#if 0 - #include #define VoidClass _CL_Q34java4lang4Void @@ -145,37 +145,225 @@ get_ffi_type (jclass klass) return r; } -// FIXME: the body of this method should be a separate function so -// that Constructor can use it too. +// Actually perform an FFI call. +void +java::lang::reflect::Method::hack_call (gnu::gcj::RawData *rcif, + gnu::gcj::RawData *rmethod, + gnu::gcj::RawData *rret_value, + gnu::gcj::RawData *rvalues) +{ + ffi_cif *cif = (ffi_cif *) rcif; + void (*method) (...) = (void (*) (...)) rmethod; + void *ret_value = (void *) rret_value; + void **values = (void **) rvalues; + + ffi_call (cif, method, ret_value, values); +} + jobject -java::lang::reflect::Method::invoke (jobject obj, - jobjectArray args) +java::lang::reflect::Method::invoke (jobject obj, jobjectArray args) { - // FIXME: we need to be a friend of Class here. - _Jv_Method *meth = decl_class->methods[index]; - if (! java::lang::reflect::Modifier::isStatic(modifiers)) + if (parameter_types == NULL) + getType (); + + jmethodID meth = _Jv_FromReflectedMethod (this); + if (! java::lang::reflect::Modifier::isStatic(meth->accflags)) { jclass k = obj ? obj->getClass() : NULL; - if (! obj || ! decl_class->isAssignableFrom(k)) + if (! obj) JvThrow (new java::lang::NullPointerException); + if (! declaringClass->isAssignableFrom(k)) + JvThrow (new java::lang::IllegalArgumentException); // FIXME: access checks. - meth = _Jv_LookupMethod (k, meth->name, meth->signature); + + // Find the possibly overloaded method based on the runtime type + // of the object. + meth = _Jv_LookupDeclaredMethod (k, meth->name, meth->signature); } + return _Jv_CallNonvirtualMethodA (obj, return_type, meth, false, + parameter_types, args); +} + +jint +java::lang::reflect::Method::getModifiers () +{ + return _Jv_FromReflectedMethod (this)->accflags; +} + +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_GetTypesFromSignature (_Jv_FromReflectedMethod (this), + declaringClass, + ¶meter_types, + &return_type); +} + +void +_Jv_GetTypesFromSignature (jmethodID method, + jclass declaringClass, + JArray **arg_types_out, + jclass *return_type_out) +{ + + _Jv_Utf8Const* sig = method->signature; + java::lang::ClassLoader *loader = declaringClass->getClassLoader(); + char *ptr = sig->data; + int numArgs = 0; + /* First just count the number of parameters. */ + 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 *args = (JArray *) + JvNewObjectArray (numArgs, &ClassClass, NULL); + jclass* argPtr = elements (args); + for (ptr = sig->data; *ptr != '\0'; ptr++) + { + int num_arrays = 0; + jclass type; + for (; *ptr == '['; ptr++) + num_arrays++; + switch (*ptr) + { + default: + return; + case ')': + argPtr = return_type_out; + continue; + case '(': + continue; + case 'V': + case 'B': + case 'C': + case 'D': + case 'F': + case 'S': + case 'I': + case 'J': + case 'Z': + type = _Jv_FindClassFromSignature(ptr, loader); + break; + case 'L': + type = _Jv_FindClassFromSignature(ptr, loader); + do + ptr++; + while (*ptr != ';' && ptr[1] != '\0'); + break; + } + + // FIXME: 2'nd argument should be "current loader" + while (--num_arrays >= 0) + type = _Jv_FindArrayClass (type, 0); + // ARGPTR can be NULL if we are processing the return value of a + // call from Constructor. + if (argPtr) + *argPtr++ = type; + } + *arg_types_out = args; +} + +// This is a very rough analog of the JNI CallNonvirtualMethodA +// 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. +jobject +_Jv_CallNonvirtualMethodA (jobject obj, + jclass return_type, + jmethodID meth, + jboolean is_constructor, + JArray *parameter_types, + jobjectArray args) +{ + JvAssert (! is_constructor || ! obj); + JvAssert (! is_constructor || ! return_type); + // FIXME: access checks. if (parameter_types->length != args->length) JvThrow (new java::lang::IllegalArgumentException); + // 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 + || ! java::lang::reflect::Modifier::isStatic(meth->accflags)) + needs_this = true; + + int param_count = parameter_types->length; + if (needs_this) + ++param_count; + ffi_type *rtype = get_ffi_type (return_type); - ffi_type **argtypes = (ffi_type **) alloca (parameter_types->length + ffi_type **argtypes = (ffi_type **) alloca (param_count * sizeof (ffi_type *)); - jobject *paramelts = elements (parameter_types); + jclass *paramelts = elements (parameter_types); jobject *argelts = elements (args); + // FIXME: at some point the compiler is going to add extra arguments + // to some functions. In particular we are going to do this for + // handling access checks in reflection. We must add these hidden + // arguments here. + + // 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) + { + // FIXME: must special-case String, arrays, maybe others here. + obj = JvAllocObject (return_type); + } + + int i = 0; int size = 0; - for (int i = 0; i < parameter_types->length; ++i) + if (needs_this) + { + // The `NULL' type is `Object'. + argtypes[i++] = get_ffi_type (NULL); + size += sizeof (jobject); + } + + for (; i < param_count; ++i) { jclass k = argelts[i] ? argelts[i]->getClass() : NULL; argtypes[i] = get_ffi_type (k); @@ -196,7 +384,7 @@ java::lang::reflect::Method::invoke (jobject obj, } ffi_cif cif; - if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, parameter_types->length, + if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, param_count, rtype, argtypes) != FFI_OK) { // FIXME: throw some kind of VirtualMachineError here. @@ -212,7 +400,14 @@ java::lang::reflect::Method::invoke (jobject obj, Where += sizeof (Type); \ } while (0) - for (int i = 0; i < parameter_types->length; ++i) + i = 0; + if (needs_this) + { + COPY (p, obj, jobject); + ++i; + } + + for (; i < param_count; ++i) { java::lang::Number *num = (java::lang::Number *) paramelts[i]; if (paramelts[i] == JvPrimClass (byte)) @@ -228,7 +423,8 @@ java::lang::reflect::Method::invoke (jobject obj, else if (paramelts[i] == JvPrimClass (double)) COPY (p, num->doubleValue(), jdouble); else if (paramelts[i] == JvPrimClass (boolean)) - COPY (p, ((java::lang::Boolean *) argelts[i])->booleanValue(), jboolean); + COPY (p, ((java::lang::Boolean *) argelts[i])->booleanValue(), + jboolean); else if (paramelts[i] == JvPrimClass (char)) COPY (p, ((java::lang::Character *) argelts[i])->charValue(), jchar); else @@ -238,11 +434,17 @@ java::lang::reflect::Method::invoke (jobject obj, } } - // FIXME: exception handling. + // FIXME: initialize class here. + + // Largest possible value. Hopefully it is aligned! + jdouble ret_value; java::lang::Throwable *ex; - jdouble ret_value; // Largest possible value. Hopefully - // it is aligned! - ex = TRAMP_CALL (ffi_call (&cif, meth->ncode, &ret_value, (void *) values)); + using namespace java::lang; + using namespace java::lang::reflect; + ex = Method::hack_trampoline ((gnu::gcj::RawData *) &cif, + (gnu::gcj::RawData *) meth->ncode, + (gnu::gcj::RawData *) &ret_value, + (gnu::gcj::RawData *) values); if (ex) JvThrow (new InvocationTargetException (ex)); @@ -269,119 +471,9 @@ java::lang::reflect::Method::invoke (jobject obj, r = NULL; else { - JvAssert (! return_type->isPrimitive()); - r = VAL (java::lang::Object, jobject); + JvAssert (return_type == NULL || ! return_type->isPrimitive()); + r = * (Object **) &ret_value; } return r; } - -#else /* 0 */ - -jobject -java::lang::reflect::Method::invoke (jobject, jobjectArray) -{ - JvFail ("not enabled yet"); -} - -#endif /* 0 */ - -jint -java::lang::reflect::Method::getModifiers () -{ - return _Jv_FromReflectedMethod (this)->accflags; -} - -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_Utf8Const* sig = _Jv_FromReflectedMethod (this)->signature; - java::lang::ClassLoader *loader = declaringClass->getClassLoader(); - char *ptr = sig->data; - int numArgs = 0; - /* First just count the number of parameters. */ - 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 *args = (JArray *) - JvNewObjectArray (numArgs, &ClassClass, NULL); - jclass* argPtr = elements (args); - for (ptr = sig->data; *ptr != '\0'; ptr++) - { - int num_arrays = 0; - jclass type; - for (; *ptr == '['; ptr++) - num_arrays++; - switch (*ptr) - { - default: - return; - case ')': - argPtr = &return_type; - continue; - case '(': - continue; - case 'V': - case 'B': - case 'C': - case 'D': - case 'F': - case 'S': - case 'I': - case 'J': - case 'Z': - type = _Jv_FindClassFromSignature(ptr, loader); - break; - case 'L': - type = _Jv_FindClassFromSignature(ptr, loader); - do - ptr++; - while (*ptr != ';' && ptr[1] != '\0'); - break; - } - - // FIXME: 2'nd argument should be "current loader" - while (--num_arrays >= 0) - type = _Jv_FindArrayClass (type, 0); - *argPtr++ = type; - } - parameter_types = args; -} -- cgit v1.1