This is the mail archive of the
java-patches@sourceware.cygnus.com
mailing list for the Java project.
Patch: reflection support
- To: Java Patch List <java-patches at sourceware dot cygnus dot com>
- Subject: Patch: reflection support
- From: Tom Tromey <tromey at cygnus dot com>
- Date: 04 Jan 2000 01:46:20 -0700
- Reply-To: tromey at cygnus dot com
I'm checking in this jumbo patch. It:
* Implements most of java.lang.reflect (perhaps all of it)
* Implements nearly all of the reflection methods in java.lang.Class
* Fixes a couple bugs in the existing reflection code
* Fixes a couple minor portability bugs (define _GNU_SOURCE always,
remove the `#pragma's from array.h -- this last one allows "-g"
builds to work again for me)
This code compiles, and at least partly works, but it is largely
untested. It currently fails on the one Mauve Method.invoke test, but
I've been having major problems debugging (compiler and debugger
failures on alternate days), so I'm just committing it. As far as I
know it won't break anything that already works.
In case this seeming instability worries you, my plan for the next few
days is to write Mauve tests for the new functionality, and get the
debugging done too.
2000-01-04 Tom Tromey <tromey@cygnus.com>
* 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 <gnu/gcj/RawData.h>.
(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 <per@bothner.com>
* java/lang/natClass.cc (getDeclaredMethods): Correctly compute
offset in new Method.
Tom
Index: Makefile.am
===================================================================
RCS file: /cvs/java/libgcj/libjava/Makefile.am,v
retrieving revision 1.39
diff -u -r1.39 Makefile.am
--- Makefile.am 1999/12/20 02:18:11 1.39
+++ Makefile.am 2000/01/04 08:39:01
@@ -74,8 +74,10 @@
EH_COMMON_INCLUDE = @EH_COMMON_INCLUDE@
WARNINGS = -W -Wall
+## We need _GNU_SOURCE defined for some Linux builds. It doesn't hurt
+## to always define it.
AM_CXXFLAGS = -fno-rtti -fvtable-thunks @LIBGCJ_CXXFLAGS@ @EXCEPTIONSPEC@ \
- $(WARNINGS)
+ $(WARNINGS) -D_GNU_SOURCE
if USING_GCC
AM_CFLAGS = @LIBGCJ_CFLAGS@ $(WARNINGS)
else
@@ -229,6 +231,12 @@
-friend 'jstring _Jv_AllocString (jsize);' \
$(basename $<)
+java/lang/reflect/Constructor.h: java/lang/reflect/Constructor.class libgcj.zip
+ $(GCJH) -classpath $(top_builddir) \
+ -friend 'jmethodID _Jv_FromReflectedConstructor (java::lang::reflect::Constructor *);' \
+ -friend 'java::lang::Class;' \
+ $(basename $<)
+
java/lang/reflect/Field.h: java/lang/reflect/Field.class libgcj.zip
$(GCJH) -classpath $(top_builddir) \
-friend 'jfieldID _Jv_FromReflectedField (java::lang::reflect::Field *);' \
@@ -797,6 +805,7 @@
java/lang/natSystem.cc \
java/lang/natThread.cc \
java/lang/reflect/natArray.cc \
+java/lang/reflect/natConstructor.cc \
java/lang/reflect/natField.cc \
java/lang/reflect/natMethod.cc \
java/net/natInetAddress.cc \
Index: prims.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/prims.cc,v
retrieving revision 1.14
diff -u -r1.14 prims.cc
--- prims.cc 1999/11/25 00:36:51 1.14
+++ prims.cc 2000/01/04 08:39:14
@@ -16,8 +16,6 @@
#include <string.h>
#include <signal.h>
-#pragma implementation "gcj/array.h"
-
#include <gcj/cni.h>
#include <jvm.h>
#include <java-signal.h>
@@ -133,6 +131,26 @@
register unsigned char* ptr = (unsigned char*) a->data;
register unsigned char* limit = ptr + a->length;
for (;; i++, sptr++)
+ {
+ int ch = UTF8_GET (ptr, limit);
+ if (i == len)
+ return ch < 0;
+ if (ch != *sptr)
+ return false;
+ }
+ return true;
+}
+
+/* Like _Jv_equal, but stop after N characters. */
+jboolean
+_Jv_equaln (Utf8Const *a, jstring str, jint n)
+{
+ jint len = str->length();
+ jint i = 0;
+ jchar *sptr = _Jv_GetStringChars (str);
+ register unsigned char* ptr = (unsigned char*) a->data;
+ register unsigned char* limit = ptr + a->length;
+ for (; n-- > 0; i++, sptr++)
{
int ch = UTF8_GET (ptr, limit);
if (i == len)
Index: resolve.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/resolve.cc,v
retrieving revision 1.6
diff -u -r1.6 resolve.cc
--- resolve.cc 1999/09/10 22:03:05 1.6
+++ resolve.cc 2000/01/04 08:39:24
@@ -321,21 +321,6 @@
}
}
-_Jv_Method*
-_Jv_LookupDeclaredMethod (jclass klass, _Jv_Utf8Const *name,
- _Jv_Utf8Const *signature)
-{
- for (; klass; klass = klass->getSuperclass())
- {
- _Jv_Method *meth = _Jv_GetMethodLocal (klass, name, signature);
-
- if (meth)
- return meth;
- }
-
- return NULL;
-}
-
/** FIXME: this is a terribly inefficient algorithm! It would improve
things if compiled classes to know vtable offset, and _Jv_Method had
a field for this.
Index: gcj/array.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/gcj/array.h,v
retrieving revision 1.2
diff -u -r1.2 array.h
--- array.h 1999/10/22 19:43:40 1.2
+++ array.h 2000/01/04 08:39:28
@@ -11,8 +11,6 @@
#ifndef __GCJ_ARRAY_H__
#define __GCJ_ARRAY_H__
-#pragma interface
-
#include <java/lang/Object.h>
extern "Java" {
Index: gcj/method.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/gcj/method.h,v
retrieving revision 1.1
diff -u -r1.1 method.h
--- method.h 1999/09/10 22:03:06 1.1
+++ method.h 2000/01/04 08:39:28
@@ -18,4 +18,11 @@
((char *) method->declaringClass->methods + method->offset);
}
+extern inline jmethodID
+_Jv_FromReflectedConstructor (java::lang::reflect::Constructor *constructor)
+{
+ return (jmethodID)
+ ((char *) constructor->declaringClass->methods + constructor->offset);
+}
+
#endif /* __GCJ_METHOD_H__ */
Index: include/jvm.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/include/jvm.h,v
retrieving revision 1.8
diff -u -r1.8 jvm.h
--- jvm.h 1999/12/06 06:33:56 1.8
+++ jvm.h 2000/01/04 08:39:32
@@ -53,6 +53,7 @@
_Jv_Utf8Const *_Jv_makeUtf8Const (jstring string);
extern jboolean _Jv_equalUtf8Consts (_Jv_Utf8Const *, _Jv_Utf8Const *);
extern jboolean _Jv_equal (_Jv_Utf8Const *, jstring, jint);
+extern jboolean _Jv_equaln (_Jv_Utf8Const *, jstring, jint);
#define StringClass _CL_Q34java4lang6String
extern java::lang::Class StringClass;
@@ -161,6 +162,13 @@
java::lang::ClassLoader *loader);
extern jclass _Jv_FindClassFromSignature (char *,
java::lang::ClassLoader *loader);
+extern void _Jv_GetTypesFromSignature (jmethodID method,
+ jclass declaringClass,
+ JArray<jclass> **arg_types_out,
+ jclass *return_type_out);
+extern jobject _Jv_CallNonvirtualMethodA (jobject, jclass,
+ jmethodID, jboolean,
+ JArray<jclass> *, jobjectArray);
extern jobject _Jv_NewMultiArray (jclass, jint ndims, jint* dims)
__attribute__((__malloc__));
Index: java/lang/Class.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/Class.h,v
retrieving revision 1.7
diff -u -r1.7 Class.h
--- Class.h 1999/09/10 22:03:08 1.7
+++ Class.h 2000/01/04 08:39:37
@@ -1,6 +1,6 @@
// Class.h - Header file for java.lang.Class. -*- c++ -*-
-/* Copyright (C) 1998, 1999 Cygnus Solutions
+/* Copyright (C) 1998, 1999, 2000 Cygnus Solutions
This file is part of libgcj.
@@ -94,12 +94,16 @@
java::lang::reflect::Field *getField (jstring);
private:
+ jint _getFields (JArray<java::lang::reflect::Field *> *result, jint offset);
+ JArray<java::lang::reflect::Constructor *> *_getConstructors (jboolean);
java::lang::reflect::Field *getField (jstring, jint);
public:
JArray<java::lang::reflect::Field *> *getFields (void);
JArray<jclass> *getInterfaces (void);
+ void getSignature (java::lang::StringBuffer *buffer);
+ static jstring getSignature (JArray<jclass> *);
java::lang::reflect::Method *getMethod (jstring, JArray<jclass> *);
JArray<java::lang::reflect::Method *> *getMethods (void);
@@ -156,6 +160,8 @@
// Friend functions implemented in natClass.cc.
friend _Jv_Method *_Jv_GetMethodLocal (jclass klass, _Jv_Utf8Const *name,
_Jv_Utf8Const *signature);
+ friend _Jv_Method* _Jv_LookupDeclaredMethod (jclass, _Jv_Utf8Const *,
+ _Jv_Utf8Const*);
friend void _Jv_InitClass (jclass klass);
friend jfieldID JvGetFirstInstanceField (jclass);
@@ -166,6 +172,7 @@
friend jobject _Jv_JNI_ToReflectedField (_Jv_JNIEnv *, jclass, jfieldID);
friend jfieldID _Jv_FromReflectedField (java::lang::reflect::Field *);
friend jmethodID _Jv_FromReflectedMethod (java::lang::reflect::Method *);
+ friend jmethodID _Jv_FromReflectedConstructor (java::lang::reflect::Constructor *);
friend class _Jv_PrimClass;
@@ -190,8 +197,6 @@
#ifdef INTERPRETER
friend jboolean _Jv_IsInterpretedClass (jclass);
friend void _Jv_InitField (jobject, jclass, _Jv_Field*);
- friend _Jv_Method* _Jv_LookupDeclaredMethod (jclass, _Jv_Utf8Const *,
- _Jv_Utf8Const*);
friend int _Jv_DetermineVTableIndex (jclass, _Jv_Utf8Const *,
_Jv_Utf8Const*);
friend void _Jv_InitField (jobject, jclass, int);
Index: java/lang/Class.java
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/Class.java,v
retrieving revision 1.3
diff -u -r1.3 Class.java
--- Class.java 1999/08/18 14:16:41 1.3
+++ Class.java 2000/01/04 08:39:37
@@ -1,6 +1,6 @@
// Class.java - Representation of a Java class.
-/* Copyright (C) 1998, 1999 Cygnus Solutions
+/* Copyright (C) 1998, 1999, 2000 Cygnus Solutions
This file is part of libgcj.
@@ -36,14 +36,27 @@
public native Constructor getConstructor (Class[] parameterTypes)
throws NoSuchMethodException, SecurityException;
- public native Constructor[] getConstructors () throws SecurityException;
- public native Class[] getDeclaredClasses () throws SecurityException;
+ // This is used to implement getConstructors and
+ // getDeclaredConstructors.
+ private native Constructor[] _getConstructors (boolean declared)
+ throws SecurityException;
+ public Constructor[] getConstructors () throws SecurityException
+ {
+ return _getConstructors (false);
+ }
+
public native Constructor getDeclaredConstructor (Class[] parameterTypes)
throws NoSuchMethodException, SecurityException;
- public native Constructor[] getDeclaredConstructors ()
- throws SecurityException;
+
+ public native Class[] getDeclaredClasses () throws SecurityException;
+
+ public Constructor[] getDeclaredConstructors () throws SecurityException
+ {
+ return _getConstructors (true);
+ }
+
public native Field getDeclaredField (String fieldName)
throws NoSuchFieldException, SecurityException;
public native Field[] getDeclaredFields () throws SecurityException;
@@ -69,9 +82,14 @@
throw new NoSuchFieldException(fieldName);
return fld;
}
+
+ private native Field[] _getFields (Field[] result, int offset);
public native Field[] getFields () throws SecurityException;
public native Class[] getInterfaces ();
+
+ private final native void getSignature (StringBuffer buffer);
+ private static final native String getSignature (Class[] parameterTypes);
public native Method getMethod (String methodName, Class[] parameterTypes)
throws NoSuchMethodException, SecurityException;
Index: java/lang/natClass.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/natClass.cc,v
retrieving revision 1.8
diff -u -r1.8 natClass.cc
--- natClass.cc 1999/11/18 07:19:00 1.8
+++ natClass.cc 2000/01/04 08:39:41
@@ -1,6 +1,6 @@
// natClass.cc - Implementation of java.lang.Class native methods.
-/* Copyright (C) 1998, 1999 Cygnus Solutions
+/* Copyright (C) 1998, 1999, 2000 Cygnus Solutions
This file is part of libgcj.
@@ -38,6 +38,7 @@
#include <java/lang/NullPointerException.h>
#include <java/lang/System.h>
#include <java/lang/SecurityManager.h>
+#include <java/lang/StringBuffer.h>
#include <java-cpool.h>
@@ -55,6 +56,8 @@
extern java::lang::Class MethodClass;
#define FieldClass _CL_Q44java4lang7reflect5Field
extern java::lang::Class FieldClass;
+#define ConstructorClass _CL_Q44java4lang7reflect11Constructor
+extern java::lang::Class ConstructorClass;
// Some constants we use to look up the class initializer.
static _Jv_Utf8Const *void_signature = _Jv_makeUtf8Const ("()V", 3);
@@ -96,27 +99,95 @@
}
java::lang::reflect::Constructor *
-java::lang::Class::getConstructor (JArray<jclass> *)
+java::lang::Class::getConstructor (JArray<jclass> *param_types)
{
- JvFail ("java::lang::Class::getConstructor not implemented");
+ jstring partial_sig = getSignature (param_types);
+ jint hash = partial_sig->hashCode ();
+
+ int i = isPrimitive () ? 0 : method_count;
+ while (--i >= 0)
+ {
+ // FIXME: access checks.
+ if (_Jv_equalUtf8Consts (methods[i].name, init_name)
+ && _Jv_equal (methods[i].signature, partial_sig, hash))
+ {
+ // Found it. For getConstructor, the constructor must be
+ // public.
+ using namespace java::lang::reflect;
+ if (Modifier::isPublic(methods[i].accflags))
+ break;
+ Constructor *cons = new Constructor ();
+ cons->offset = (char *) (&methods[i]) - (char *) methods;
+ cons->declaringClass = this;
+ return cons;
+ }
+ }
+ JvThrow (new java::lang::NoSuchMethodException);
}
JArray<java::lang::reflect::Constructor *> *
-java::lang::Class::getConstructors (void)
+java::lang::Class::_getConstructors (jboolean declared)
{
- JvFail ("java::lang::Class::getConstructors not implemented");
+ // FIXME: this method needs access checks.
+
+ int numConstructors = 0;
+ int max = isPrimitive () ? 0 : method_count;
+ int i;
+ for (i = max; --i >= 0; )
+ {
+ _Jv_Method *method = &methods[i];
+ if (method->name == NULL
+ && ! _Jv_equalUtf8Consts (method->name, init_name))
+ continue;
+ if (declared
+ && ! java::lang::reflect::Modifier::isPublic(method->accflags))
+ continue;
+ numConstructors++;
+ }
+ JArray<java::lang::reflect::Constructor *> *result
+ = (JArray<java::lang::reflect::Constructor *> *)
+ JvNewObjectArray (numConstructors, &ConstructorClass, NULL);
+ java::lang::reflect::Constructor** cptr = elements (result);
+ for (i = 0; i < max; i++)
+ {
+ _Jv_Method *method = &methods[i];
+ if (method->name == NULL
+ && ! _Jv_equalUtf8Consts (method->name, init_name))
+ continue;
+ if (declared
+ && ! java::lang::reflect::Modifier::isPublic(method->accflags))
+ continue;
+ java::lang::reflect::Constructor *cons
+ = new java::lang::reflect::Constructor ();
+ cons->offset = (char *) method - (char *) methods;
+ cons->declaringClass = this;
+ *cptr++ = cons;
+ }
+ return result;
}
java::lang::reflect::Constructor *
-java::lang::Class::getDeclaredConstructor (JArray<jclass> *)
+java::lang::Class::getDeclaredConstructor (JArray<jclass> *param_types)
{
- JvFail ("java::lang::Class::getDeclaredConstructor not implemented");
-}
+ jstring partial_sig = getSignature (param_types);
+ jint hash = partial_sig->hashCode ();
-JArray<java::lang::reflect::Constructor *> *
-java::lang::Class::getDeclaredConstructors (void)
-{
- JvFail ("java::lang::Class::getDeclaredConstructors not implemented");
+ int i = isPrimitive () ? 0 : method_count;
+ while (--i >= 0)
+ {
+ // FIXME: access checks.
+ if (_Jv_equalUtf8Consts (methods[i].name, init_name)
+ && _Jv_equal (methods[i].signature, partial_sig, hash))
+ {
+ // Found it.
+ using namespace java::lang::reflect;
+ Constructor *cons = new Constructor ();
+ cons->offset = (char *) (&methods[i]) - (char *) methods;
+ cons->declaringClass = this;
+ return cons;
+ }
+ }
+ JvThrow (new java::lang::NoSuchMethodException);
}
java::lang::reflect::Field *
@@ -187,18 +258,67 @@
return result;
}
+void
+java::lang::Class::getSignature (java::lang::StringBuffer *buffer)
+{
+ if (isPrimitive())
+ buffer->append((jchar) method_count);
+ else
+ {
+ jstring name = getName();
+ if (name->charAt(0) != '[')
+ buffer->append((jchar) 'L');
+ buffer->append(name);
+ if (name->charAt(0) != '[')
+ buffer->append((jchar) ';');
+ }
+}
+
+// This doesn't have to be native. It is an implementation detail
+// only called from the C++ code, though, so maybe this is clearer.
+jstring
+java::lang::Class::getSignature (JArray<jclass> *param_types)
+{
+ java::lang::StringBuffer *buf = new java::lang::StringBuffer ();
+ buf->append((jchar) '(');
+ jclass *v = elements (param_types);
+ for (int i = 0; i < param_types->length; ++i)
+ v[i]->getSignature(buf);
+ buf->append((jchar) ')');
+ return buf->toString();
+}
+
java::lang::reflect::Method *
-java::lang::Class::getDeclaredMethod (jstring, JArray<jclass> *)
+java::lang::Class::getDeclaredMethod (jstring name,
+ JArray<jclass> *param_types)
{
- JvFail ("java::lang::Class::getDeclaredMethod not implemented");
+ jstring partial_sig = getSignature (param_types);
+ jint p_len = partial_sig->length();
+ _Jv_Utf8Const *utf_name = _Jv_makeUtf8Const (name);
+ int i = isPrimitive () ? 0 : method_count;
+ while (--i >= 0)
+ {
+ // FIXME: access checks.
+ if (_Jv_equalUtf8Consts (methods[i].name, utf_name)
+ && _Jv_equaln (methods[i].signature, partial_sig, p_len))
+ {
+ // Found it.
+ using namespace java::lang::reflect;
+ Method *rmethod = new Method ();
+ rmethod->offset = (char*) (&methods[i]) - (char*) methods;
+ rmethod->declaringClass = this;
+ }
+ }
+ JvThrow (new java::lang::NoSuchMethodException);
}
JArray<java::lang::reflect::Method *> *
java::lang::Class::getDeclaredMethods (void)
{
int numMethods = 0;
+ int max = isPrimitive () ? 0 : method_count;
int i;
- for (i = method_count; --i >= 0; )
+ for (i = max; --i >= 0; )
{
_Jv_Method *method = &methods[i];
if (method->name == NULL
@@ -211,15 +331,16 @@
= (JArray<java::lang::reflect::Method *> *)
JvNewObjectArray (numMethods, &MethodClass, NULL);
java::lang::reflect::Method** mptr = elements (result);
- for (i = 0; i < method_count; i++)
+ for (i = 0; i < max; i++)
{
_Jv_Method *method = &methods[i];
if (method->name == NULL
|| _Jv_equalUtf8Consts (method->name, clinit_name)
|| _Jv_equalUtf8Consts (method->name, init_name))
continue;
- java::lang::reflect::Method* rmethod = new java::lang::reflect::Method ();
- rmethod->offset = (char*) mptr - (char*) elements (result);
+ java::lang::reflect::Method* rmethod
+ = new java::lang::reflect::Method ();
+ rmethod->offset = (char*) method - (char*) methods;
rmethod->declaringClass = this;
*mptr++ = rmethod;
}
@@ -258,10 +379,58 @@
return NULL; // Placate compiler.
}
+jint
+java::lang::Class::_getFields (JArray<java::lang::reflect::Field *> *result,
+ jint offset)
+{
+ int count = 0;
+ for (int i = 0; i < field_count; i++)
+ {
+ _Jv_Field *field = &fields[i];
+ if (! (field->getModifiers() & java::lang::reflect::Modifier::PUBLIC))
+ continue;
+ ++count;
+
+ if (result != NULL)
+ {
+ java::lang::reflect::Field *rfield
+ = new java::lang::reflect::Field ();
+ rfield->offset = (char *) field - (char *) fields;
+ rfield->declaringClass = this;
+ rfield->name = _Jv_NewStringUtf8Const (field->name);
+ (elements (result))[offset + i] = rfield;
+ }
+ }
+ jclass superclass = getSuperclass();
+ if (superclass != NULL)
+ {
+ int s_count = superclass->_getFields (result, offset);
+ count += s_count;
+ offset += s_count;
+ }
+ for (int i = 0; i < interface_count; ++i)
+ {
+ int f_count = interfaces[i]->_getFields (result, offset);
+ count += f_count;
+ offset += f_count;
+ }
+ return count;
+}
+
JArray<java::lang::reflect::Field *> *
java::lang::Class::getFields (void)
{
- JvFail ("java::lang::Class::getFields not implemented");
+ using namespace java::lang::reflect;
+
+ int count = _getFields (NULL, 0);
+
+ JArray<java::lang::reflect::Field *> *result
+ = ((JArray<java::lang::reflect::Field *> *)
+ JvNewObjectArray (count, &FieldClass, NULL));
+
+ _getFields (result, 0);
+
+ return result;
}
JArray<jclass> *
@@ -275,9 +444,30 @@
}
java::lang::reflect::Method *
-java::lang::Class::getMethod (jstring, JArray<jclass> *)
+java::lang::Class::getMethod (jstring name, JArray<jclass> *param_types)
{
- JvFail ("java::lang::Class::getMethod not implemented");
+ jstring partial_sig = getSignature (param_types);
+ jint p_len = partial_sig->length();
+ _Jv_Utf8Const *utf_name = _Jv_makeUtf8Const (name);
+ for (Class *klass = this; klass; klass = klass->getSuperclass())
+ {
+ int i = klass->isPrimitive () ? 0 : klass->method_count;
+ while (--i >= 0)
+ {
+ // FIXME: access checks.
+ if (_Jv_equalUtf8Consts (klass->methods[i].name, utf_name)
+ && _Jv_equaln (klass->methods[i].signature, partial_sig, p_len))
+ {
+ // Found it.
+ using namespace java::lang::reflect;
+ Method *rmethod = new Method ();
+ rmethod->offset = (char*) (&klass->methods[i]) - (char*) methods;
+ rmethod->declaringClass = klass;
+ return rmethod;
+ }
+ }
+ }
+ JvThrow (new java::lang::NoSuchMethodException);
}
JArray<java::lang::reflect::Method *> *
@@ -494,6 +684,8 @@
// Some class-related convenience functions.
//
+// Find a method declared in the class. If it is not declared locally
+// (or if it is inherited), return NULL.
_Jv_Method *
_Jv_GetMethodLocal (jclass klass, _Jv_Utf8Const *name,
_Jv_Utf8Const *signature)
@@ -507,6 +699,21 @@
return NULL;
}
+_Jv_Method *
+_Jv_LookupDeclaredMethod (jclass klass, _Jv_Utf8Const *name,
+ _Jv_Utf8Const *signature)
+{
+ for (; klass; klass = klass->getSuperclass())
+ {
+ _Jv_Method *meth = _Jv_GetMethodLocal (klass, name, signature);
+
+ if (meth)
+ return meth;
+ }
+
+ return NULL;
+}
+
// NOTE: MCACHE_SIZE should be a power of 2 minus one.
#define MCACHE_SIZE 1023
@@ -553,16 +760,6 @@
_Jv_LookupInterfaceMethod (jclass klass, _Jv_Utf8Const *name,
_Jv_Utf8Const *signature)
{
- // FIXME: can't do this until we have a working class loader.
- // This probably isn't the right thing to do anyway, since we can't
- // call a method of a class until the class is linked. But this
- // captures the general idea.
- // klass->getClassLoader()->resolveClass(klass);
- //
- // KKT: This is unnessecary, exactly for the reason you present:
- // _Jv_LookupInterfaceMethod is only called on object instances, and
- // such have already been initialized (which includes resolving).
-
void *ncode = _Jv_FindMethodInCache (klass, name, signature);
if (ncode != 0)
return ncode;
Index: java/lang/reflect/Constructor.java
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/reflect/Constructor.java,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 Constructor.java
--- Constructor.java 1999/04/07 14:52:39 1.1.1.1
+++ Constructor.java 2000/01/04 08:39:41
@@ -28,12 +28,12 @@
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 @@
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();
}
+
+ // Update cached values from method descriptor in class.
+ private native void getType ();
- // FIXME: this must be native. Should share implementation with
- // Method.invoke.
- public Object newInstance (Object[] args)
+ 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 @@
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;
}
Index: java/lang/reflect/Method.java
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/reflect/Method.java,v
retrieving revision 1.3
diff -u -r1.3 Method.java
--- Method.java 1999/12/20 23:05:21 1.3
+++ Method.java 2000/01/04 08:39:46
@@ -10,6 +10,8 @@
package java.lang.reflect;
+import gnu.gcj.RawData;
+
/**
* @author Tom Tromey <tromey@cygnus.com>
* @date December 12, 1998
@@ -17,7 +19,7 @@
/* 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
@@ -65,6 +67,30 @@
// FIXME.
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,
Index: java/lang/reflect/natConstructor.cc
===================================================================
RCS file: natConstructor.cc
diff -N natConstructor.cc
--- /dev/null Tue May 5 13:32:27 1998
+++ natConstructor.cc Tue Jan 4 00:39:46 2000
@@ -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 <config.h>
+
+#include <gcj/cni.h>
+#include <jvm.h>
+
+#include <java/lang/reflect/Constructor.h>
+#include <java/lang/reflect/Method.h>
+#include <java/lang/reflect/InvocationTargetException.h>
+#include <java/lang/reflect/Modifier.h>
+#include <java/lang/InstantiationException.h>
+#include <gcj/method.h>
+
+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);
+}
Index: java/lang/reflect/natMethod.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/reflect/natMethod.cc,v
retrieving revision 1.3
diff -u -r1.3 natMethod.cc
--- natMethod.cc 1999/09/10 22:03:08 1.3
+++ natMethod.cc 2000/01/04 08:39:46
@@ -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 @@
Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
details. */
-// This is about 90% done. Search for FIXME to see what remains.
-
#include <config.h>
#include <gcj/cni.h>
#include <jvm.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>
@@ -32,14 +31,15 @@
#include <java/lang/NullPointerException.h>
#include <java/lang/Class.h>
#include <gcj/method.h>
+#include <gnu/gcj/RawData.h>
+#define ObjectClass _CL_Q34java4lang6Object
+extern java::lang::Class ObjectClass;
#define ClassClass _CL_Q34java4lang5Class
extern java::lang::Class ClassClass;
#include <stdlib.h>
-#if 0
-
#include <ffi.h>
#define VoidClass _CL_Q34java4lang4Void
@@ -144,38 +144,226 @@
return r;
}
+
+// 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);
+}
-// FIXME: the body of this method should be a separate function so
-// that Constructor can use it too.
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<jclass> **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<jclass> *args = (JArray<jclass> *)
+ 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 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.
+jobject
+_Jv_CallNonvirtualMethodA (jobject obj,
+ jclass return_type,
+ jmethodID meth,
+ jboolean is_constructor,
+ JArray<jclass> *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 @@
}
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,8 +400,15 @@
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))
COPY (p, num->byteValue(), jbyte);
@@ -228,7 +423,8 @@
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
@@ -237,12 +433,18 @@
COPY (p, argelts[i], jobject);
}
}
+
+ // FIXME: initialize class here.
- // FIXME: exception handling.
+ // 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 @@
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<jclass> *args = (JArray<jclass> *)
- 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;
}