This is the mail archive of the
java-patches@sourceware.cygnus.com
mailing list for the Java project.
Patch: even more JNI work
- To: Java Patch List <java-patches at sourceware dot cygnus dot com>
- Subject: Patch: even more JNI work
- From: Tom Tromey <tromey at cygnus dot com>
- Date: 04 Feb 2000 13:37:39 -0700
- Reply-To: tromey at cygnus dot com
I'm checking in this patch. It changes libgcj to look up JNI
functions in the libraries that have been loaded. This is done in an
extremely stupid way (we check for the symbol in every library). I
don't see any better way to do it though.
This patch also fixes a buglet in natClassLoader.cc (some globals that
should have been static were not).
2000-02-04 Tom Tromey <tromey@cygnus.com>
* defineclass.cc (handleMethodsBegin): Allocate _Jv_MethodBase
pointers.
(handleMethodsEnd): Fixed error messages. Create a _Jv_JNIMethod
if the method is native.
* resolve.cc (ncode): Don't handle native methods.
(_Jv_JNIMethod::ncode): New method.
(_Jv_PrepareClass): Handle native methods.
* jni.cc (call): Renamed from _Jv_JNI_conversion_call.
Include AbstractMethodError.h.
(add_char): New function.
(mangled_name): Likewise.
* include/java-interp.h (class _Jv_JNIMethod): New class.
(class _Jv_MethodBase): New class.
(class _Jv_InterpMethod): Derive from _Jv_MethodBase.
(_Jv_InterpClass): Changed `interpreted_methods' field to type
`_Jv_MethodBase'.
* include/jvm.h (_Jv_FindSymbolInExecutable): Declare.
* java/lang/natRuntime.cc (libraries_size, libraries_count,
libraries): New globals.
(add_library): New function.
(_Jv_FindSymbolInExecutable): New function.
* java/lang/natClassLoader.cc (initiated_classes, loaded_classes):
Now static.
Tom
Index: defineclass.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/defineclass.cc,v
retrieving revision 1.7
diff -u -r1.7 defineclass.cc
--- defineclass.cc 2000/01/20 17:32:26 1.7
+++ defineclass.cc 2000/02/04 20:26:27
@@ -1142,13 +1142,15 @@
-void _Jv_ClassReader::handleMethodsBegin (int count)
+void
+_Jv_ClassReader::handleMethodsBegin (int count)
{
def->methods = (_Jv_Method*)
_Jv_AllocBytesChecked (sizeof (_Jv_Method)*count);
- def->interpreted_methods = (_Jv_InterpMethod**)
- _Jv_AllocBytesChecked (sizeof (_Jv_InterpMethod*) * count);
+ def->interpreted_methods
+ = (_Jv_MethodBase **) _Jv_AllocBytesChecked (sizeof (_Jv_MethodBase *)
+ * count);
for (int i = 0; i < count; i++)
def->interpreted_methods[i] = 0;
@@ -1230,7 +1232,8 @@
(int method_index, int exc_index,
int start_pc, int end_pc, int handler_pc, int catch_type)
{
- _Jv_InterpMethod *method = def->interpreted_methods[method_index];
+ _Jv_InterpMethod *method = reinterpret_cast<_Jv_InterpMethod *>
+ (def->interpreted_methods[method_index]);
_Jv_InterpException *exc = method->exceptions ();
exc[exc_index].start_pc = start_pc;
@@ -1246,17 +1249,29 @@
for (int i = 0; i < def->method_count; i++)
{
_Jv_Method *method = &def->methods[i];
- if (method->accflags & (Modifier::NATIVE | Modifier::ABSTRACT))
+ if ((method->accflags & Modifier::NATIVE) != 0)
{
if (def->interpreted_methods[i] != 0)
- throw_class_format_error ("code provided "
- "for abstract or native method");
+ throw_class_format_error ("code provided for native method");
+ else
+ {
+ _Jv_JNIMethod *m = (_Jv_JNIMethod *)
+ _Jv_AllocBytesChecked (sizeof (_Jv_JNIMethod));
+ m->defining_class = def;
+ m->self = method;
+ m->function = NULL;
+ def->interpreted_methods[i] = m;
+ }
}
+ else if ((method->accflags & Modifier::ABSTRACT) != 0)
+ {
+ if (def->interpreted_methods[i] != 0)
+ throw_class_format_error ("code provided for abstract method");
+ }
else
{
if (def->interpreted_methods[i] == 0)
- throw_class_format_error ("abstract or native method "
- "with no code");
+ throw_class_format_error ("method with no code");
}
}
Index: jni.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/jni.cc,v
retrieving revision 1.9
diff -u -r1.9 jni.cc
--- jni.cc 2000/02/02 01:55:03 1.9
+++ jni.cc 2000/02/04 20:26:52
@@ -32,6 +32,7 @@
#include <java/lang/Throwable.h>
#include <java/lang/ArrayIndexOutOfBoundsException.h>
#include <java/lang/StringIndexOutOfBoundsException.h>
+#include <java/lang/AbstractMethodError.h>
#include <java/lang/InstantiationException.h>
#include <java/lang/NoSuchFieldError.h>
#include <java/lang/NoSuchMethodError.h>
@@ -1204,15 +1205,95 @@
+// Add a character to the buffer, encoding properly.
+static void
+add_char (char *buf, jchar c, int *here)
+{
+ if (c == '_')
+ {
+ buf[(*here)++] = '_';
+ buf[(*here)++] = '1';
+ }
+ else if (c == ';')
+ {
+ buf[(*here)++] = '_';
+ buf[(*here)++] = '2';
+ }
+ else if (c == '[')
+ {
+ buf[(*here)++] = '_';
+ buf[(*here)++] = '3';
+ }
+ else if (c == '/')
+ buf[(*here)++] = '_';
+ if ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z'))
+ buf[(*here)++] = (char) c;
+ else
+ {
+ // "Unicode" character.
+ buf[(*here)++] = '_';
+ buf[(*here)++] = '0';
+ // FIXME: hex. FIXME: use upper or lower case?
+ }
+}
+
+// Compute a mangled name for a native function. This computes the
+// long name, and also returns an index which indicates where a NUL
+// can be placed to create the short name. This function assumes that
+// the buffer is large enough for its results.
+static void
+mangled_name (jclass klass, _Jv_Utf8Const *func_name,
+ _Jv_Utf8Const *signature, char *buf, int *long_start)
+{
+ strcpy (buf, "Java_");
+ int here = 5;
+
+ // Add fully qualified class name.
+ jchar *chars = _Jv_GetStringChars (klass->getName ());
+ jint len = klass->getName ()->length ();
+ for (int i = 0; i < len; ++i)
+ add_char (buf, chars[i], &here);
+
+ // Don't use add_char because we need a literal `_'.
+ buf[here++] = '_';
+
+ const unsigned char *fn = (const unsigned char *) func_name->data;
+ const unsigned char *limit = fn + func_name->length;
+ for (int i = 0; ; ++i)
+ {
+ int ch = UTF8_GET (fn, limit);
+ if (ch < 0)
+ break;
+ add_char (buf, ch, &here);
+ }
+
+ // This is where the long signature begins.
+ *long_start = here;
+ buf[here++] = '_';
+ buf[here++] = '_';
+
+ const unsigned char *sig = (const unsigned char *) signature->data;
+ limit = sig + signature->length;
+ JvAssert (signature[0] == '(');
+ for (int i = 1; ; ++i)
+ {
+ int ch = UTF8_GET (sig, limit);
+ if (ch == ')' || ch < 0)
+ break;
+ add_char (buf, ch, &here);
+ }
+
+ buf[here] = '\0';
+}
+
// This function is the stub which is used to turn an ordinary (CNI)
// method call into a JNI call.
void
-_Jv_JNI_conversion_call (ffi_cif *cif,
- void *ret,
- ffi_raw *args,
- void *__this)
+_Jv_JNIMethod::call (ffi_cif *cif, void *ret, ffi_raw *args, void *__this)
{
- _Jv_InterpMethod* _this = (_Jv_InterpMethod*)__this;
+ _Jv_JNIMethod* _this = (_Jv_JNIMethod *) __this;
JNIEnv env;
_Jv_JNI_LocalFrame *frame
@@ -1233,11 +1314,34 @@
// FIXME: we should mark every reference parameter as a local. For
// now we assume a conservative GC, and we assume that the
// references are on the stack somewhere.
+
+ // We cache the value that we find, of course, but if we don't find
+ // a value we don't cache that fact -- we might subsequently load a
+ // library which finds the function in question.
+ if (_this->function == NULL)
+ {
+ char buf[10 + 6 * (_this->self->name->length
+ + _this->self->signature->length)];
+ int long_start;
+ mangled_name (_this->defining_class, _this->self->name,
+ _this->self->signature, buf, &long_start);
+ char c = buf[long_start];
+ buf[long_start] = '\0';
+ _this->function = _Jv_FindSymbolInExecutable (buf);
+ if (_this->function == NULL)
+ {
+ buf[long_start] = c;
+ _this->function = _Jv_FindSymbolInExecutable (buf);
+ if (_this->function == NULL)
+ {
+ jstring str = JvNewStringUTF (_this->self->name->data);
+ JvThrow (new java::lang::AbstractMethodError (str));
+ }
+ }
+ }
- ffi_raw_call (cif,
- NULL, // FIXME: function pointer.
- ret,
- args);
+ // The actual call to the JNI function.
+ ffi_raw_call (cif, (void (*) (...)) _this->function, ret, args);
do
{
Index: resolve.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/resolve.cc,v
retrieving revision 1.11
diff -u -r1.11 resolve.cc
--- resolve.cc 2000/02/02 01:55:03 1.11
+++ resolve.cc 2000/02/04 20:26:54
@@ -563,21 +563,21 @@
have code -- for static constructors. */
for (int i = 0; i < clz->method_count; i++)
{
- _Jv_InterpMethod *imeth = clz->interpreted_methods[i];
+ _Jv_MethodBase *imeth = clz->interpreted_methods[i];
- if (imeth != 0) // it could be abstract or native
+ if ((clz->methods[i].accflags & Modifier::NATIVE) != 0)
{
- clz->methods[i].ncode = imeth->ncode ();
+ // You might think we could use a virtual `ncode' method in
+ // the _Jv_MethodBase and unify the native and non-native
+ // cases. Well, we can't, because we don't allocate these
+ // objects using `new', and thus they don't get a vtable.
+ _Jv_JNIMethod *jnim = reinterpret_cast<_Jv_JNIMethod *> (imeth);
+ clz->methods[i].ncode = jnim->ncode ();
}
- else
+ else if (imeth != 0) // it could be abstract
{
- if ((clz->methods[i].accflags & Modifier::NATIVE) != 0)
- {
- JvThrow
- (new java::lang::VirtualMachineError
- (JvNewStringLatin1
- ("the interpreter does not support native methods")));
- }
+ _Jv_InterpMethod *im = reinterpret_cast<_Jv_InterpMethod *> (im);
+ clz->methods[i].ncode = im->ncode ();
}
}
@@ -588,13 +588,6 @@
return;
}
- /* FIXME: native methods for interpreted classes should be handled, I
- * dunno exactly how, but it seems that we should try to find them at
- * this point, and if we fail, try again after <clinit>, since it
- * could have caused additional code to be loaded. Interfaces cannot
- * have native methods (not even for static initialization). */
-
-
/* Now onto the actual job: vtable layout. First, count how many new
methods we have */
int new_method_count = 0;
@@ -1022,13 +1015,9 @@
args_raw_size = ffi_raw_size (&closure->cif);
- if ((self->accflags & Modifier::NATIVE) != 0)
- {
- // FIXME: for now we assume that all native methods for
- // interpreted code use JNI.
- fun = (ffi_closure_fun) &_Jv_JNI_conversion_call;
- }
- else if ((self->accflags & Modifier::SYNCHRONIZED) != 0)
+ JvAssert ((self->accflags & Modifier::NATIVE) == 0);
+
+ if ((self->accflags & Modifier::SYNCHRONIZED) != 0)
{
if (staticp)
fun = (ffi_closure_fun)&_Jv_InterpMethod::run_synch_class;
@@ -1040,10 +1029,49 @@
fun = (ffi_closure_fun)&_Jv_InterpMethod::run_normal;
}
+ ffi_prep_raw_closure (&closure->closure,
+ &closure->cif,
+ fun,
+ (void*) this);
+
+ self->ncode = (void*)closure;
+ return self->ncode;
+}
+
+
+void *
+_Jv_JNIMethod::ncode ()
+{
+ using namespace java::lang::reflect;
+
+ if (self->ncode != 0)
+ return self->ncode;
+
+ jboolean staticp = (self->accflags & Modifier::STATIC) != 0;
+ int arg_count = count_arguments (self->signature, staticp);
+
+ ncode_closure *closure =
+ (ncode_closure*)_Jv_AllocBytesChecked (sizeof (ncode_closure)
+ + arg_count * sizeof (ffi_type*));
+
+ init_cif (self->signature,
+ arg_count,
+ staticp,
+ &closure->cif,
+ &closure->arg_types[0]);
+
+ ffi_closure_fun fun;
+
+ JvAssert ((self->accflags & Modifier::NATIVE) != 0);
+
+ // FIXME: for now we assume that all native methods for
+ // interpreted code use JNI.
+ fun = (ffi_closure_fun) &_Jv_JNIMethod::call;
+
ffi_prep_raw_closure (&closure->closure,
- &closure->cif,
- fun,
- (void*)this);
+ &closure->cif,
+ fun,
+ (void*) this);
self->ncode = (void*)closure;
return self->ncode;
Index: include/java-interp.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/include/java-interp.h,v
retrieving revision 1.5
diff -u -r1.5 java-interp.h
--- java-interp.h 2000/02/02 01:55:03 1.5
+++ java-interp.h 2000/02/04 20:26:58
@@ -14,6 +14,18 @@
#include <jvm.h>
#include <java-cpool.h>
+// Base class for method representations. Subclasses are interpreted
+// and JNI methods.
+class _Jv_MethodBase
+{
+protected:
+ // The class which defined this method.
+ _Jv_InterpClass *defining_class;
+
+ // The method description.
+ _Jv_Method *self;
+};
+
#ifdef INTERPRETER
#pragma interface
@@ -65,9 +77,9 @@
friend class _Jv_ClassReader;
friend class _Jv_InterpMethod;
};
-
-class _Jv_InterpMethod {
+class _Jv_InterpMethod : public _Jv_MethodBase
+{
_Jv_ushort max_stack;
_Jv_ushort max_locals;
int code_length;
@@ -75,9 +87,6 @@
_Jv_ushort exc_count;
_Jv_ushort args_raw_size;
- _Jv_InterpClass *defining_class;
- _Jv_Method *self;
-
unsigned char* bytecode ()
{
return
@@ -121,9 +130,6 @@
friend class gnu::gcj::runtime::MethodInvocation;
friend void _Jv_PrepareClass(jclass);
-
- // This function is used when making a JNI call from the interpreter.
- friend void _Jv_JNI_conversion_call (ffi_cif *, void *, ffi_raw *, void *);
};
class _Jv_InterpMethodInvocation {
@@ -140,7 +146,7 @@
class _Jv_InterpClass : public java::lang::Class
{
- _Jv_InterpMethod **interpreted_methods;
+ _Jv_MethodBase **interpreted_methods;
_Jv_ushort *field_initializers;
friend class _Jv_ClassReader;
@@ -164,5 +170,20 @@
};
#endif /* INTERPRETER */
+
+class _Jv_JNIMethod : public _Jv_MethodBase
+{
+ // The underlying function. If NULL we have to look for the
+ // function.
+ void *function;
+
+ // This function is used when making a JNI call from the interpreter.
+ static void call (ffi_cif *, void *, ffi_raw *, void *);
+
+ void *ncode ();
+
+ friend class _Jv_ClassReader;
+ friend void _Jv_PrepareClass(jclass);
+};
#endif /* __JAVA_INTERP_H__ */
Index: include/jvm.h
===================================================================
RCS file: /cvs/java/libgcj/libjava/include/jvm.h,v
retrieving revision 1.14
diff -u -r1.14 jvm.h
--- jvm.h 2000/02/02 01:55:03 1.14
+++ jvm.h 2000/02/04 20:26:58
@@ -201,6 +201,9 @@
extern char *_Jv_ThisExecutable (void);
extern void _Jv_ThisExecutable (const char *);
+/* Return a pointer to a symbol in executable or loaded library. */
+void *_Jv_FindSymbolInExecutable (const char *);
+
/* Initialize JNI. */
extern void _Jv_JNI_Init (void);
Index: java/lang/natClassLoader.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/natClassLoader.cc,v
retrieving revision 1.15
diff -u -r1.15 natClassLoader.cc
--- natClassLoader.cc 2000/01/19 18:39:26 1.15
+++ natClassLoader.cc 2000/02/04 20:27:08
@@ -308,8 +308,12 @@
java::lang::ClassLoader *loader;
};
-_Jv_LoaderInfo *initiated_classes[HASH_LEN];
-jclass loaded_classes[HASH_LEN];
+static _Jv_LoaderInfo *initiated_classes[HASH_LEN];
+static jclass loaded_classes[HASH_LEN];
+
+// This is the root of a linked list of classes
+
+
jclass
_Jv_FindClassInCache (_Jv_Utf8Const *name, java::lang::ClassLoader *loader)
Index: java/lang/natRuntime.cc
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/lang/natRuntime.cc,v
retrieving revision 1.5
diff -u -r1.5 natRuntime.cc
--- natRuntime.cc 2000/01/19 18:39:26 1.5
+++ natRuntime.cc 2000/02/04 20:27:12
@@ -24,7 +24,52 @@
/* FIXME: we don't always need this. The next libtool will let us use
AC_LTDL_PREOPEN to see if we do. */
const lt_dlsymlist lt_preloaded_symbols[1] = { { 0, 0 } };
-#endif
+
+// We keep track of all the libraries loaded by this application. For
+// now we use them to look up symbols for JNI. `libraries_size' holds
+// the total size of the buffer. `libraries_count' is the number of
+// items which are in use.
+static int libraries_size;
+static int libraries_count;
+static lt_dlhandle *libraries;
+
+static void
+add_library (lt_dlhandle lib)
+{
+ if (libraries_count == libraries_size)
+ {
+ int ns = libraries_size * 2;
+ if (ns == 0)
+ ns = 10;
+ lt_dlhandle *n = (lt_dlhandle *) _Jv_Malloc (ns * sizeof (lt_dlhandle));
+ if (libraries)
+ {
+ memcpy (n, libraries, libraries_size * sizeof (lt_dlhandle));
+ _Jv_Free (libraries);
+ }
+ libraries = n;
+ libraries_size = ns;
+ for (int i = libraries_count; i < libraries_size; ++i)
+ libraries[i] = NULL;
+ }
+
+ libraries[libraries_count++] = lib;
+}
+
+void *
+_Jv_FindSymbolInExecutable (const char *symname)
+{
+ for (int i = 0; i < libraries_count; ++i)
+ {
+ void *r = lt_dlsym (libraries[i], symname);
+ if (r)
+ return r;
+ }
+
+ return lt_dlsym (NULL, symname);
+}
+
+#endif /* USE_LTDL */
void
java::lang::Runtime::exit (jint status)