From 1ef6bc6f4eb380b30e070d852ec9b18772ee4397 Mon Sep 17 00:00:00 2001
From: Marcus Comstedt <marcus@mc.pp.se>
Date: Tue, 2 Mar 1999 23:07:13 +0100
Subject: [PATCH] Java module imported from pike-modules.

Rev: src/modules/Java/.cvsignore:1.1
Rev: src/modules/Java/Makefile.in:1.1
Rev: src/modules/Java/acconfig.h:1.1
Rev: src/modules/Java/configure.in:1.1
Rev: src/modules/Java/jvm.c:1.1
Rev: src/modules/Java/module.pmod.in.in:1.1
Rev: src/modules/Java/testsuite.in:1.1
---
 .gitattributes                     |    4 +
 src/modules/Java/.cvsignore        |   14 +
 src/modules/Java/.gitignore        |   14 +
 src/modules/Java/Makefile.in       |   18 +
 src/modules/Java/acconfig.h        |   30 +
 src/modules/Java/configure.in      |  216 +++
 src/modules/Java/jvm.c             | 2871 ++++++++++++++++++++++++++++
 src/modules/Java/module.pmod.in.in |  574 ++++++
 src/modules/Java/testsuite.in      |    8 +
 9 files changed, 3749 insertions(+)
 create mode 100644 src/modules/Java/.cvsignore
 create mode 100644 src/modules/Java/.gitignore
 create mode 100644 src/modules/Java/Makefile.in
 create mode 100644 src/modules/Java/acconfig.h
 create mode 100644 src/modules/Java/configure.in
 create mode 100644 src/modules/Java/jvm.c
 create mode 100644 src/modules/Java/module.pmod.in.in
 create mode 100644 src/modules/Java/testsuite.in

diff --git a/.gitattributes b/.gitattributes
index f05075de30..342f40d5d6 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -194,6 +194,10 @@ testfont binary
 /src/modules/Image/polyfill.c foreign_ident
 /src/modules/Image/togif.c foreign_ident
 /src/modules/Image/x.c foreign_ident
+/src/modules/Java/Makefile.in foreign_ident
+/src/modules/Java/acconfig.h foreign_ident
+/src/modules/Java/jvm.c foreign_ident
+/src/modules/Java/testsuite.in foreign_ident
 /src/modules/MIME/Makefile.in foreign_ident
 /src/modules/MIME/acconfig.h foreign_ident
 /src/modules/MIME/configure.in foreign_ident
diff --git a/src/modules/Java/.cvsignore b/src/modules/Java/.cvsignore
new file mode 100644
index 0000000000..c03762d717
--- /dev/null
+++ b/src/modules/Java/.cvsignore
@@ -0,0 +1,14 @@
+.pure
+Makefile
+config.h
+config.h.in
+config.log
+config.status
+configure
+dependencies
+linker_options
+modlist_headers
+modlist_segment
+module_testsuite
+stamp-h
+stamp-h.in
diff --git a/src/modules/Java/.gitignore b/src/modules/Java/.gitignore
new file mode 100644
index 0000000000..a5518ba7a2
--- /dev/null
+++ b/src/modules/Java/.gitignore
@@ -0,0 +1,14 @@
+/.pure
+/Makefile
+/config.h
+/config.h.in
+/config.log
+/config.status
+/configure
+/dependencies
+/linker_options
+/modlist_headers
+/modlist_segment
+/module_testsuite
+/stamp-h
+/stamp-h.in
diff --git a/src/modules/Java/Makefile.in b/src/modules/Java/Makefile.in
new file mode 100644
index 0000000000..c720abfd18
--- /dev/null
+++ b/src/modules/Java/Makefile.in
@@ -0,0 +1,18 @@
+#
+# $Id: Makefile.in,v 1.1 1999/03/02 22:07:10 marcus Exp $
+#
+
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+MODULE_CPPFLAGS=@DEFS@ @CPPFLAGS@
+OBJS=jvm.o
+MODULE_LDFLAGS=@LDFLAGS@ @JAVA_LIBS@
+
+DUMMY=module.pmod.in
+
+@dynamic_module_makefile@
+
+module.pmod.in: $(SRCDIR)/module.pmod.in.in config.status
+	CONFIG_FILES=module.pmod.in CONFIG_HEADERS="" ./config.status
+
+@dependencies@
diff --git a/src/modules/Java/acconfig.h b/src/modules/Java/acconfig.h
new file mode 100644
index 0000000000..0d0f9c6d52
--- /dev/null
+++ b/src/modules/Java/acconfig.h
@@ -0,0 +1,30 @@
+/*
+ * $Id: acconfig.h,v 1.1 1999/03/02 22:07:10 marcus Exp $
+ *
+ * Config-file for the Pike Java module.
+ *
+ * Marcus Comstedt
+ */
+
+#ifndef PIKE_JAVA_CONFIG_H
+#define PIKE_JAVA_CONFIG_H
+
+@TOP@
+@BOTTOM@
+
+/* Define if you have Java */
+#undef HAVE_JAVA
+
+/* Define to home of Java */
+#undef JAVA_HOME
+
+/* Define to the library path for Java libraries */
+#undef JAVA_LIBPATH
+
+/* Define if you have a Sparc CPU */
+#undef HAVE_SPARC_CPU
+
+/* Define if you have an x86 CPU */
+#undef HAVE_X86_CPU
+
+#endif /* PIKE_JAVA_CONFIG_H */
diff --git a/src/modules/Java/configure.in b/src/modules/Java/configure.in
new file mode 100644
index 0000000000..283852eb51
--- /dev/null
+++ b/src/modules/Java/configure.in
@@ -0,0 +1,216 @@
+AC_INIT(jvm.c)
+AC_CONFIG_HEADER(config.h)
+
+sinclude(../module_configure.in)
+
+OLD_LIBS=$LIBS
+OLD_LDFLAGS=$LDFLAGS
+OLD_CPPFLAGS=$CPPFLAGS
+JAVA_LIBS=""
+JAVA_LIBPATH=""
+JAVA_AVAILABLE=0
+
+AC_ARG_WITH(java,  [  --without-java       no support for Java],[],[with_java=yes])
+
+if test "x$with_java" = xyes; then
+
+  AC_MSG_CHECKING(which architecture we're using)
+  AC_CACHE_VAL(pike_cv_java_arch, [
+    if pike_cv_java_arch="`uname -p`"; then :; else pike_cv_java_arch=no; fi
+  ])
+  AC_MSG_RESULT($pike_cv_java_arch)
+
+  AC_MSG_CHECKING(which operating system we're using)
+  AC_CACHE_VAL(pike_cv_java_sysos, [
+    pike_cv_java_sysos="`uname`"
+    case "$pike_cv_java_sysos" in
+      SunOS)
+        case "`uname -r`" in
+          5.*|6.*|7.*) pike_cv_java_sysos="solaris";
+        esac
+      ;;
+    esac
+  ])
+  AC_MSG_RESULT($pike_cv_java_sysos)
+
+  AC_MSG_CHECKING(what kind of threads to use)
+  AC_CACHE_VAL(pike_cv_java_threads_type, [
+    if test "x$THREADS_FLAG" = x; then
+      pike_cv_java_threads_type=native_threads
+    else
+      pike_cv_java_threads_type="${THREADS_FLAG}_threads"
+    fi
+  ])
+  AC_MSG_RESULT($pike_cv_java_threads_type)
+
+  AC_MSG_CHECKING(for JAVA_HOME)
+  AC_CACHE_VAL(pike_cv_java_java_home, [
+    if test "x$JAVA_HOME" = x; then
+      pike_cv_java_java_home=no
+      for tmp_java_home in /usr/java /usr/local/java /usr/local/jdk* /usr/java*/jre /usr/local/java*/jre /usr/local/jdk*/jre /usr/local/jre*; do
+        if test -d $tmp_java_home/.; then
+          if ls "$tmp_java_home/lib/$pike_cv_java_arch/$pike_cv_java_threads_type"/libjvm* >/dev/null 2>&1 || \
+	    ls "$tmp_java_home/lib/$pike_cv_java_arch"/libjvm* >/dev/null 2>&1 || \
+	    ls "$tmp_java_home/lib/$pike_cv_java_arch"/classic/libjvm* >/dev/null 2>&1; then
+	      pike_cv_java_java_home="$tmp_java_home"
+	  else
+	    :
+	  fi
+        else
+          :
+        fi
+      done
+    else
+      pike_cv_java_java_home="$JAVA_HOME"
+    fi
+  ])
+  AC_MSG_RESULT($pike_cv_java_java_home)
+
+  AC_MSG_CHECKING(for Java libraries)
+  AC_CACHE_VAL(pike_cv_java_lib_dir, [
+    pike_cv_java_lib_dir=""
+    if test -d "$pike_cv_java_java_home/lib/$pike_cv_java_arch/$pike_cv_java_threads_type" ; then
+      pike_cv_java_lib_dir="$pike_cv_java_lib_dir$pike_cv_java_java_home/lib/$pike_cv_java_arch/$pike_cv_java_threads_type "
+    else
+      :
+    fi
+    if test -d "$pike_cv_java_java_home/lib/$pike_cv_java_arch/classic" ; then
+      pike_cv_java_lib_dir="$pike_cv_java_lib_dir$pike_cv_java_java_home/lib/$pike_cv_java_arch/classic "
+    else
+      :
+    fi
+    if test -d "$pike_cv_java_java_home/lib/$pike_cv_java_arch" ; then
+      pike_cv_java_lib_dir="$pike_cv_java_lib_dir$pike_cv_java_java_home/lib/$pike_cv_java_arch "
+    else
+      :
+    fi
+    if test -z "$pike_cv_java_lib_dir" ; then pike_cv_java_lib_dir=no; else
+      :
+    fi
+  ])
+  AC_MSG_RESULT($pike_cv_java_lib_dir)
+
+  if test "x$pike_cv_java_lib_dir" = xno; then :; else
+    echo Adding $pike_cv_java_lib_dir to the library search path.
+    for i in $pike_cv_java_lib_dir; do
+      LDFLAGS="-L$i -R$i ${LDFLAGS}"
+      JAVA_LIBPATH="${JAVA_LIBPATH}${JAVA_LIBPATH:+:}$i"
+    done
+  fi
+
+  AC_MSG_CHECKING(for the Java include directory)
+  AC_CACHE_VAL(pike_cv_java_include_dir, [
+    pike_cv_java_include_dir=no
+    for tmp_java_incdir in /usr/java* /usr/local/java* /usr/local/jdk* /usr/local/jre* "$pike_cv_java_java_home"; do
+      if test -d $tmp_java_incdir/. -a -d $tmp_java_incdir/include/$pike_cv_java_sysos/.; then
+        if ls "$tmp_java_incdir/include/jni.h" >/dev/null 2>&1 ; then
+	  pike_cv_java_include_dir="$tmp_java_incdir/include"
+	else
+	  :
+	fi
+      else
+        :
+      fi
+    done
+  ])
+  AC_MSG_RESULT($pike_cv_java_include_dir)
+
+  if test "x$pike_cv_java_include_dir" = xno; then :; else
+    echo Adding $pike_cv_java_include_dir and $pike_cv_java_include_dir/$pike_cv_java_sysos to the include search path.
+    CPPFLAGS="-I$pike_cv_java_include_dir -I$pike_cv_java_include_dir/$pike_cv_java_sysos ${CPPFLAGS}"
+  fi
+
+  AC_CHECK_HEADERS(jni.h)
+
+  pike_cv_java=no
+
+  if test "$ac_cv_header_jni_h" = yes; then
+
+    AC_MSG_CHECKING([for JNI_CreateJavaVM in -ljvm])
+    AC_CACHE_VAL(ac_cv_lib_jvm_JNI_CreateJavaVM, [
+      ac_save_LIBS="$LIBS"
+      LIBS="-ljvm $LIBS"
+      AC_TRY_LINK([#include <jni.h>], dnl
+	[JNI_CreateJavaVM((JavaVM**)0,(void**)0,(void*)0);], dnl
+	[ac_cv_lib_jvm_JNI_CreateJavaVM=yes], dnl
+	[ac_cv_lib_jvm_JNI_CreateJavaVM=no])
+      LIBS="$ac_save_LIBS"
+    ])
+    if test x"$ac_cv_lib_jvm_JNI_CreateJavaVM" = xyes; then
+      LIBS="-ljvm $LIBS"
+      JAVA_LIBS="-ljvm ${JAVA_LIBS}"
+      pike_cv_java=yes;
+      AC_MSG_RESULT(yes)
+    else
+      AC_MSG_RESULT(no)
+    fi
+
+    if test "$pike_cv_java" = yes; then
+
+      AC_CHECK_LIB(java, Java_java_lang_Class_isInstance, [
+        LIBS="-ljava $LIBS"
+        JAVA_LIBS="-ljava ${JAVA_LIBS}"
+      ], [])
+
+      AC_CHECK_LIB(zip, Java_java_util_zip_Inflater_inflateBytes, [
+        LIBS="-lzip $LIBS"
+        JAVA_LIBS="-lzip ${JAVA_LIBS}"
+      ], [])
+
+      AC_CHECK_LIB(hpi, sysOpen, [
+        LIBS="$LIBS -lhpi"
+        JAVA_LIBS="${JAVA_LIBS} -lhpi"
+      ], [])
+
+      AC_CHECK_LIB(thread, thr_create, [
+        LIBS="$LIBS -lthread"
+        JAVA_LIBS="${JAVA_LIBS} -lthread"
+      ], [])
+
+    else
+      :
+    fi
+  else
+    :
+  fi
+
+  if test "x$pike_cv_java" = xno; then
+    LIBS="$OLD_LIBS"
+    CPPFLAGS="$OLD_CPPFLAGS"
+    LDFLAGS="$OLD_LDFLAGS"
+    JAVA_LIBS=""
+  else
+    AC_DEFINE(HAVE_JAVA)
+    JAVA_AVAILABLE=1
+    if test "x$pike_cv_java_java_home" = x; then :; else
+      AC_DEFINE_UNQUOTED(JAVA_HOME, "${pike_cv_java_java_home}")
+    fi
+  fi
+
+  AC_MSG_CHECKING(for known machine language)
+  AC_CACHE_VAL(pike_cv_java_cpu, [
+    case $pike_cv_java_arch  in
+      sparc) pike_cv_java_cpu=sparc;;
+      i386|x86) pike_cv_java_cpu=x86;;
+      *) pike_cv_java_cpu=no;;
+    esac
+  ])
+  AC_MSG_RESULT($pike_cv_java_cpu)
+
+  case $pike_cv_java_cpu in
+    sparc) AC_DEFINE(HAVE_SPARC_CPU);;
+    x86) AC_DEFINE(HAVE_X86_CPU);;
+  esac
+
+  if test "x$JAVA_LIBPATH" = x; then :; else
+    AC_DEFINE_UNQUOTED(JAVA_LIBPATH, "${JAVA_LIBPATH}")
+  fi
+
+else
+  :
+fi
+
+AC_SUBST(JAVA_LIBS)
+AC_SUBST(JAVA_AVAILABLE)
+
+AC_OUTPUT(Makefile module.pmod.in,echo FOO >stamp-h )
diff --git a/src/modules/Java/jvm.c b/src/modules/Java/jvm.c
new file mode 100644
index 0000000000..d95b2e1502
--- /dev/null
+++ b/src/modules/Java/jvm.c
@@ -0,0 +1,2871 @@
+/*
+ * $Id: jvm.c,v 1.1 1999/03/02 22:07:11 marcus Exp $
+ *
+ * Pike interface to Java Virtual Machine
+ *
+ * Marcus Comstedt
+ */
+
+/*
+ * Includes
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "global.h"
+RCSID("$Id: jvm.c,v 1.1 1999/03/02 22:07:11 marcus Exp $");
+#include "program.h"
+#include "interpret.h"
+#include "stralloc.h"
+#include "object.h"
+#include "mapping.h"
+#include "builtin_functions.h"
+#include "error.h"
+#include "module_support.h"
+#include "pike_memory.h"
+#include "gc.h"
+#include "threads.h"
+#include "operators.h"
+
+#ifdef HAVE_JAVA
+
+#ifdef HAVE_JNI_H
+#include <jni.h>
+#endif /* HAVE_JNI_H */
+
+#ifdef _REENTRANT
+#if defined(HAVE_SPARC_CPU) || defined(HAVE_X86_CPU)
+#define SUPPORT_NATIVE_METHODS
+#endif /* HAVE_SPARC_CPU || HAVE_X86_CPU */
+#endif /* _REENTRANT */
+
+static struct program *jvm_program = NULL;
+static struct program *jobj_program = NULL, *jclass_program = NULL;
+static struct program *jthrowable_program = NULL, *jarray_program = NULL;
+static struct program *method_program = NULL, *static_method_program = NULL;
+static struct program *field_program = NULL, *static_field_program = NULL;
+static struct program *natives_program = NULL, *attachment_program = NULL;
+static SIZE_T jarray_stor_offs = 0;
+
+struct jvm_storage {
+  JavaVM *jvm;			/* Denotes a Java VM */
+  JNIEnv *env;			/* pointer to native method interface */
+  JavaVMInitArgs vm_args;       /* JDK 1.2 VM initialization arguments */
+  JavaVMOption vm_options[4];	/* Should be large enough to hold all opts. */
+  struct pike_string *classpath_string;
+  jclass class_object, class_class, class_string, class_throwable;
+  jclass class_runtimex, class_system;
+  jmethodID method_hash, method_tostring, method_arraycopy;
+  jmethodID method_getcomponenttype, method_isarray;
+  jmethodID method_getname, method_charat;
+#ifdef _REENTRANT
+  struct object *tl_env;
+#endif /* _REENTRANT */
+};
+
+struct jobj_storage {
+  struct object *jvm;
+  jobject jobj;
+};
+
+struct jarray_storage {
+  int ty;
+};
+
+struct method_storage {
+  struct object *class;
+  struct pike_string *name, *sig;
+  jmethodID method;
+  INT32 nargs;
+  char rettype, subtype;
+};
+
+struct field_storage {
+  struct object *class;
+  struct pike_string *name, *sig;
+  jfieldID field;
+  char type, subtype;
+};
+
+#ifdef _REENTRANT
+
+struct att_storage {
+  struct object *jvm;
+  struct svalue thr;
+  JavaVMAttachArgs args;
+  JNIEnv *env;
+  THREAD_T tid;
+};
+
+#endif /* _REENTRANT */
+
+
+#define THIS_JVM ((struct jvm_storage *)(fp->current_storage))
+#define THAT_JOBJ(o) ((struct jobj_storage *)get_storage((o),jobj_program))
+#define THIS_JOBJ ((struct jobj_storage *)(fp->current_storage))
+#define THIS_JARRAY ((struct jarray_storage *)(fp->current_storage+jarray_stor_offs))
+#define THIS_METHOD ((struct method_storage *)(fp->current_storage))
+#define THIS_FIELD ((struct field_storage *)(fp->current_storage))
+
+#define THIS_NATIVES ((struct natives_storage *)(fp->current_storage))
+#ifdef _REENTRANT
+#define THIS_ATT ((struct att_storage *)(fp->current_storage))
+#endif /* _REENTRANT */
+
+
+/*
+
+TODO(?):
+
+DefineClass
+*Reflected*
+GetObjectClass
+
+Array stuff
+
+MonitorEnter
+MonitorExit
+
+fatal x_clear x_descr f_cre f_fld_cre f_mtd_cre f_javath f_throw_new
+
+array input to make_jargs
+
+ */
+
+
+
+/* Attach foo */
+
+static JNIEnv *jvm_procure_env(struct object *jvm)
+{
+  struct jvm_storage *j =
+    (struct jvm_storage *)get_storage(jvm, jvm_program);
+  if(j) {
+
+#ifdef _REENTRANT
+    JNIEnv *env;
+
+    if(JNI_OK == (*j->jvm)->GetEnv(j->jvm, (void **)&env, JNI_VERSION_1_2))
+      return env;
+
+    if(j->tl_env != NULL && j->tl_env->prog != NULL) {
+      safe_apply(j->tl_env, "get", 0);
+      if(sp[-1].type != T_OBJECT)
+	pop_n_elems(1);
+      else {
+	env = ((struct att_storage *)((sp[-1].u.object)->storage))->env;
+	pop_n_elems(1);
+	return env;
+      }
+    }
+
+    ref_push_object(jvm);
+    push_object(clone_object(attachment_program, 1));
+    if(sp[-1].type != T_OBJECT || sp[-1].u.object == NULL) {
+      pop_n_elems(1);
+      return NULL;
+    }
+
+    env = ((struct att_storage *)((sp[-1].u.object)->storage))->env;
+
+    if(j->tl_env != NULL && j->tl_env->prog != NULL)
+      safe_apply(j->tl_env, "set", 1);
+
+    pop_n_elems(1);
+    return env;
+#else
+    return j->env;
+#endif /* _REENTRANT */
+
+  } else
+    return NULL;
+}
+
+static void jvm_vacate_env(struct object *jvm, JNIEnv *env)
+{
+}
+
+
+/* Global object references */
+
+static void push_java_class(jclass c, struct object *jvm, JNIEnv *env)
+{
+  struct object *oo;
+  struct jobj_storage *jo;
+  jobject c2;
+
+  if(!c) {
+    push_int(0);
+    return;
+  }
+  c2 = (*env)->NewGlobalRef(env, c);
+  (*env)->DeleteLocalRef(env, c);
+  push_object(oo=clone_object(jclass_program, 0));
+  jo = (struct jobj_storage *)(oo->storage);
+  jo->jvm = jvm;
+  jo->jobj = c2;
+  jvm->refs++;
+}
+
+static void push_java_throwable(jthrowable t, struct object *jvm, JNIEnv *env)
+{
+  struct object *oo;
+  struct jobj_storage *jo;
+  jobject t2;
+
+  if(!t) {
+    push_int(0);
+    return;
+  }
+  t2 = (*env)->NewGlobalRef(env, t);
+  (*env)->DeleteLocalRef(env, t);
+  push_object(oo=clone_object(jthrowable_program, 0));
+  jo = (struct jobj_storage *)(oo->storage);
+  jo->jvm = jvm;
+  jo->jobj = t2;
+  jvm->refs++;
+}
+
+static void push_java_array(jarray t, struct object *jvm, JNIEnv *env, int ty)
+{
+  struct object *oo;
+  struct jobj_storage *jo;
+  struct jarray_storage *a;
+  jobject t2;
+
+  if(!t) {
+    push_int(0);
+    return;
+  }
+  t2 = (*env)->NewGlobalRef(env, t);
+  (*env)->DeleteLocalRef(env, t);
+  push_object(oo=clone_object(jarray_program, 0));
+  jo = (struct jobj_storage *)(oo->storage);
+  jo->jvm = jvm;
+  jo->jobj = t2;
+  a = (struct jarray_storage *)(oo->storage+jarray_stor_offs);
+  a->ty = ty;
+  jvm->refs++;
+}
+
+static void push_java_anyobj(jobject o, struct object *jvm, JNIEnv *env)
+{
+  struct object *oo;
+  struct jobj_storage *jo;
+  struct jvm_storage *j =
+    (struct jvm_storage *)get_storage(jvm, jvm_program);
+  jobject o2;
+
+  if((!j)||(!o)) {
+    push_int(0);
+    return;
+  }
+  o2 = (*env)->NewGlobalRef(env, o);
+  (*env)->DeleteLocalRef(env, o);
+  if((*env)->IsInstanceOf(env, o2, j->class_class))
+    push_object(oo=clone_object(jclass_program, 0));
+  else if((*env)->IsInstanceOf(env, o2, j->class_throwable))
+    push_object(oo=clone_object(jthrowable_program, 0));
+  else {
+    jclass cc = (*env)->GetObjectClass(env, o2);
+    if((*env)->CallBooleanMethod(env, cc, j->method_isarray)) {
+      jstring ets = (jstring)(*env)->CallObjectMethod(env, cc,
+						      j->method_getname);
+      push_object(oo=clone_object(jarray_program, 0));
+      ((struct jarray_storage *)(oo->storage+jarray_stor_offs))->ty =
+	(*env)->CallCharMethod(env, ets, j->method_charat, 1);
+      (*env)->DeleteLocalRef(env, ets);
+    } else
+      push_object(oo=clone_object(jobj_program, 0));
+    (*env)->DeleteLocalRef(env, cc);
+  }
+  jo = (struct jobj_storage *)(oo->storage);
+  jo->jvm = jvm;
+  jo->jobj = o2;
+  jvm->refs++;
+}
+
+static void init_jobj_struct(struct object *o)
+{
+  struct jobj_storage *j = THIS_JOBJ;
+  j->jvm = NULL;
+  j->jobj = 0;
+}
+
+static void exit_jobj_struct(struct object *o)
+{
+  JNIEnv *env;
+  struct jobj_storage *j = THIS_JOBJ;
+  if(j->jvm) {
+    if(j->jobj && (env = jvm_procure_env(j->jvm)) != NULL) {
+      (*env)->DeleteGlobalRef(env, j->jobj);
+      jvm_vacate_env(j->jvm, env);
+    }
+    free_object(j->jvm);
+  }
+}
+
+static void jobj_gc_check(struct object *o)
+{
+  struct jobj_storage *j = THIS_JOBJ;
+
+  if(j->jvm)
+    gc_check(j->jvm);
+}
+
+static void jobj_gc_mark(struct object *o)
+{
+  struct jobj_storage *j = THIS_JOBJ;
+
+  if(j->jvm)
+    gc_mark_object_as_referenced(j->jvm);
+}
+
+static void f_jobj_cast(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  struct jvm_storage *j =
+    (struct jvm_storage *)get_storage(jo->jvm, jvm_program);
+  JNIEnv *env;
+  jstring jstr;
+
+  if(args < 1)
+    error("cast() called without arguments.\n");
+  if(sp[-args].type != T_STRING)
+    error("Bad argument 1 to cast().\n");
+
+  if(!strcmp(sp[-args].u.string->str, "object")) {
+    pop_n_elems(args);
+    push_object(this_object());
+  }
+
+  if(strcmp(sp[-args].u.string->str, "string"))
+    error("cast() to other type than string.\n");
+
+  pop_n_elems(args);
+  if((env=jvm_procure_env(jo->jvm))) {
+    jsize l;
+    const jchar *wstr;
+
+    jstr = (*env)->CallObjectMethod(env, jo->jobj, j->method_tostring);
+    wstr = (*env)->GetStringChars(env, jstr, NULL);
+    l = (*env)->GetStringLength(env, jstr);
+    push_string(make_shared_binary_string1((INT16*)wstr, l));
+    (*env)->ReleaseStringChars(env, jstr, wstr);
+    jvm_vacate_env(jo->jvm, env);
+  } else
+    push_int(0);
+}
+
+static void f_jobj_eq(INT32 args)
+{
+  struct jobj_storage *jo2, *jo = THIS_JOBJ;
+  JNIEnv *env;
+  jboolean res;
+
+  if(args<1 || sp[-args].type != T_OBJECT || 
+     (jo2 = (struct jobj_storage *)get_storage(sp[-args].u.object,
+					       jobj_program))==NULL) {
+    pop_n_elems(args);
+    push_int(0);
+    return;
+  }
+
+  if((env=jvm_procure_env(jo->jvm))) {
+    res = (*env)->IsSameObject(env, jo->jobj, jo2->jobj);
+    jvm_vacate_env(jo->jvm, env);
+  } else
+    res = 0;
+
+  pop_n_elems(args);
+  push_int((res? 1:0));
+}
+
+static void f_jobj_hash(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  JNIEnv *env;
+  struct jvm_storage *j =
+    (struct jvm_storage *)get_storage(jo->jvm, jvm_program);
+
+  pop_n_elems(args);
+  if((env=jvm_procure_env(jo->jvm))) {
+    push_int((*env)->CallIntMethod(env, jo->jobj, j->method_hash));
+    jvm_vacate_env(jo->jvm, env);
+  }
+  else push_int(0);
+}
+
+static void f_jobj_instance(INT32 args)
+{
+  struct jobj_storage *c, *jo = THIS_JOBJ;
+  JNIEnv *env;
+  struct jvm_storage *j =
+    (struct jvm_storage *)get_storage(jo->jvm, jvm_program);
+  struct object *cls;
+  int n=0;
+
+  get_all_args("Java.obj->is_instance_of()", args, "%o", &cls);
+
+  if((c = (struct jobj_storage *)get_storage(cls, jclass_program)) == NULL)
+    error("Bad argument 1 to is_instance_of().\n");
+
+  if((env=jvm_procure_env(jo->jvm))) {
+    if((*env)->IsInstanceOf(env, jo->jobj, c->jobj))
+      n = 1;
+    jvm_vacate_env(jo->jvm, env);
+  }
+
+  pop_n_elems(args);
+  push_int(n);
+}
+
+
+/* Methods */
+
+static void init_method_struct(struct object *o)
+{
+  struct method_storage *m=THIS_METHOD;
+
+  m->class = NULL;
+  m->name = NULL;
+  m->sig = NULL;
+}
+
+static void exit_method_struct(struct object *o)
+{
+  struct method_storage *m=THIS_METHOD;
+
+  if(m->sig != NULL)
+    free_string(m->sig);
+  if(m->name != NULL)
+    free_string(m->name);
+  if(m->class != NULL)
+    free_object(m->class);
+}
+
+static void method_gc_check(struct object *o)
+{
+  struct method_storage *m = THIS_METHOD;
+
+  if(m->class)
+    gc_check(m->class);
+}
+
+static void method_gc_mark(struct object *o)
+{
+  struct method_storage *m = THIS_METHOD;
+
+  if(m->class)
+    gc_mark_object_as_referenced(m->class);
+}
+
+static void f_method_create(INT32 args)
+{
+  struct method_storage *m=THIS_METHOD;
+  struct jobj_storage *c;
+  struct object *class;
+  struct pike_string *name, *sig;
+  JNIEnv *env;
+  char *p;
+
+  get_all_args("Java.method->create()", args, "%S%S%o", &name, &sig, &class);
+
+  if((c = (struct jobj_storage *)get_storage(class, jclass_program)) == NULL)
+    error("Bad argument 3 to create().\n");
+
+  if((env = jvm_procure_env(c->jvm))==NULL) {
+    pop_n_elems(args);
+    destruct(fp->current_object);
+    return;
+  }
+
+  m->method = (fp->current_object->prog==static_method_program?
+	       (*env)->GetStaticMethodID(env, c->jobj, name->str, sig->str):
+	       (*env)->GetMethodID(env, c->jobj, name->str, sig->str));
+
+  jvm_vacate_env(c->jvm, env);
+
+  if(m->method == 0) {
+    pop_n_elems(args);
+    destruct(fp->current_object);
+    return;
+  }
+
+  m->class = class;
+  m->name = name;
+  m->sig = sig;
+  class->refs++;
+  name->refs++;
+  sig->refs++;
+  pop_n_elems(args);
+  push_int(0);
+
+  m->nargs = 0;
+  m->rettype = 0;
+
+  p = sig->str;
+  if(*p++ != '(')
+    return;
+
+  while(*p && *p != ')') {
+    if(*p != '[')
+      m->nargs ++;
+    if(*p++ == 'L')
+      while(*p && *p++ != ';');
+  }
+  if(*p)
+    if((m->rettype = *++p)=='[')
+      m->subtype = *++p;
+}
+
+static void jargs_error(struct object *jvm, JNIEnv *env)
+{
+  jvm_vacate_env(jvm, env);
+  error("incompatible types passed to method.\n");
+}
+
+static void make_jargs(jvalue *jargs, INT32 args, char *sig,
+		       struct object *jvm, JNIEnv *env)
+{
+  INT32 i;
+  struct jobj_storage *jo;
+
+  if(args==-1)
+      args=1;
+  else
+    if(*sig=='(')
+      sig++;
+    else
+      return;
+  for(i=0; i<args; i++) {
+    struct svalue *sv = &sp[i-args];
+    switch(sv->type) {
+    case T_INT:
+      switch(*sig++) {
+      case 'L':
+	while(*sig && *sig++!=';');
+	if(sv->u.integer!=0)
+	  jargs_error(jvm, env);
+	jargs->l = 0;
+	break;
+      case '[':
+	while(*sig=='[') sig++;
+	if(*sig && *sig++=='L')
+	  while(*sig && *sig++!=';');
+	if(sv->u.integer!=0)
+	  jargs_error(jvm, env);
+	jargs->l = 0;
+	break;
+      case 'Z':
+	jargs->z = sv->u.integer!=0;
+	break;
+      case 'B':
+	jargs->b = sv->u.integer;
+	break;
+      case 'C':
+	jargs->c = sv->u.integer;
+	break;
+      case 'S':
+	jargs->s = sv->u.integer;
+	break;
+      case 'I':
+	jargs->i = sv->u.integer;
+	break;
+      case 'J':
+	jargs->j = sv->u.integer;
+	break;
+      case 'F':
+	jargs->f = sv->u.integer;
+	break;
+      case 'D':
+	jargs->d = sv->u.integer;
+	break;
+      default:
+	jargs_error(jvm, env);
+      }
+      break;
+    case T_FLOAT:
+      switch(*sig++) {
+      case 'Z':
+	jargs->z = sv->u.float_number!=0;
+	break;
+      case 'B':
+	jargs->b = sv->u.float_number;
+	break;
+      case 'C':
+	jargs->c = sv->u.float_number;
+	break;
+      case 'S':
+	jargs->s = sv->u.float_number;
+	break;
+      case 'I':
+	jargs->i = sv->u.float_number;
+	break;
+      case 'J':
+	jargs->j = sv->u.float_number;
+	break;
+      case 'F':
+	jargs->f = sv->u.float_number;
+	break;
+      case 'D':
+	jargs->d = sv->u.float_number;
+	break;
+      default:
+	jargs_error(jvm, env);
+      }
+      break;
+    case T_STRING:
+      if(*sig++!='L')
+	jargs_error(jvm, env);
+      while(*sig && *sig++!=';');
+      switch(sv->u.string->size_shift) {
+      case 0:
+	{
+	  jchar *newstr = alloca(2*sv->u.string->len);
+	  INT32 i;
+	  p_wchar0 *p = STR0(sv->u.string);
+	  for(i=sv->u.string->len; --i>=0; )
+	    newstr[i]=(jchar)(unsigned char)p[i];
+	  jargs->l = (*env)->NewString(env, newstr, sv->u.string->len);
+	}
+	break;
+      case 1:
+	jargs->l = (*env)->NewString(env, (jchar*)STR1(sv->u.string),
+				     sv->u.string->len);
+	break;
+      case 2:
+	{
+	  /* FIXME?: Does not make surrogates for plane 1-16 in group 0... */
+	  jchar *newstr = alloca(2*sv->u.string->len);
+	  INT32 i;
+	  p_wchar2 *p = STR2(sv->u.string);
+	  for(i=sv->u.string->len; --i>=0; )
+	    newstr[i]=(jchar)(p[i]>0xffff? 0xfffd : p[i]);
+	  jargs->l = (*env)->NewString(env, newstr, sv->u.string->len);
+	}
+	break;
+      }
+      break;
+    case T_OBJECT:
+      if(*sig=='[') {
+	if(!(jo=(struct jobj_storage *)get_storage(sv->u.object,jobj_program)))
+	  jargs_error(jvm, env);
+	else {
+	  while(*sig=='[') sig++;
+	  if(*sig && *sig++=='L')
+	    while(*sig && *sig++!=';');
+	  jargs->l = jo->jobj;
+	}
+      } else {
+	if(*sig++!='L' ||
+	   !(jo=(struct jobj_storage *)get_storage(sv->u.object,jobj_program)))
+	  jargs_error(jvm, env);
+	else {
+	  while(*sig && *sig++!=';');
+	  jargs->l = jo->jobj;
+	}
+      }
+      break;
+    default:
+      jargs_error(jvm, env);
+    }
+    jargs++;
+  }
+}
+
+static void free_jargs(jvalue *jargs, INT32 args, char *sig)
+{
+  if(jargs == NULL)
+    return;
+  free(jargs);
+}
+
+static void f_call_static(INT32 args)
+{
+  struct method_storage *m=THIS_METHOD;
+  jvalue *jargs = (m->nargs>0?(jvalue *)xalloc(m->nargs*sizeof(jvalue)):NULL);
+
+  JNIEnv *env;
+  struct jobj_storage *co = THAT_JOBJ(m->class);
+  jclass class = co->jobj;
+  jobject jjo; FLOAT_TYPE jjf; INT32 jji;
+
+  if(args != m->nargs)
+    error("wrong number of arguments for method.\n");
+
+  if((env = jvm_procure_env(co->jvm))==NULL) {
+    pop_n_elems(args);
+    push_int(0);
+    return;
+  }
+
+  make_jargs(jargs, args, m->sig->str, co->jvm, env);
+
+  switch(m->rettype) {
+  case 'Z':
+    jji = (*env)->CallStaticBooleanMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'B':
+    jji = (*env)->CallStaticByteMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'C':
+    jji = (*env)->CallStaticCharMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'S':
+    jji = (*env)->CallStaticShortMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'I':
+    jji = (*env)->CallStaticIntMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'J':
+    jji = (*env)->CallStaticLongMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'F':
+    jjf = (*env)->CallStaticFloatMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_float(jjf);
+    break;
+  case 'D':
+    jjf = (*env)->CallStaticDoubleMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_float(jjf);
+    break;
+  case 'L':
+  case '[':
+    jjo = (*env)->CallStaticObjectMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    if(m->rettype=='[')
+      push_java_array(jjo, co->jvm, env, m->subtype);
+    else
+      push_java_anyobj(jjo, co->jvm, env);
+    break;
+  case 'V':
+  default:
+    (*env)->CallStaticVoidMethodA(env, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(0);
+    break;
+  }
+
+  free_jargs(jargs, args, m->sig->str);
+
+  jvm_vacate_env(co->jvm, env);
+}
+
+static void f_call_virtual(INT32 args)
+{
+  struct method_storage *m=THIS_METHOD;
+  jvalue *jargs = (m->nargs>0?(jvalue *)xalloc(m->nargs*sizeof(jvalue)):NULL);
+
+  JNIEnv *env;
+  struct jobj_storage *co = THAT_JOBJ(m->class);
+  jclass class = co->jobj;
+  jobject jjo; FLOAT_TYPE jjf; INT32 jji;
+  struct jobj_storage *jo;
+
+  if(args != 1+m->nargs)
+    error("wrong number of arguments for method.\n");
+
+  if(sp[-args].type != T_OBJECT || 
+     (jo = (struct jobj_storage *)get_storage(sp[-args].u.object,
+					      jobj_program))==NULL)
+    error("Bad argument 1 to `().\n");
+
+  if((env = jvm_procure_env(co->jvm))==NULL) {
+    pop_n_elems(args);
+    push_int(0);
+    return;
+  }
+
+  make_jargs(jargs, args-1, m->sig->str, co->jvm, env);
+
+  switch(m->rettype) {
+  case 'Z':
+    jji = (*env)->CallBooleanMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'B':
+    jji = (*env)->CallByteMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'C':
+    jji = (*env)->CallCharMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'S':
+    jji = (*env)->CallShortMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'I':
+    jji = (*env)->CallIntMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'J':
+    jji = (*env)->CallLongMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'F':
+    jjf = (*env)->CallFloatMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_float(jjf);
+    break;
+  case 'D':
+    jjf = (*env)->CallDoubleMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_float(jjf);
+    break;
+  case 'L':
+  case '[':
+    jjo = (*env)->CallObjectMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    if(m->rettype=='[')
+      push_java_array(jjo, co->jvm, env, m->subtype);
+    else
+      push_java_anyobj(jjo, co->jvm, env);
+    break;
+  case 'V':
+  default:
+    (*env)->CallVoidMethodA(env, jo->jobj, m->method, jargs);
+    pop_n_elems(args);
+    push_int(0);
+    break;
+  }
+
+  free_jargs(jargs, args, m->sig->str);
+
+  jvm_vacate_env(co->jvm, env);
+}
+
+static void f_call_nonvirtual(INT32 args)
+{
+  struct method_storage *m=THIS_METHOD;
+  jvalue *jargs = (m->nargs>0?(jvalue *)xalloc(m->nargs*sizeof(jvalue)):NULL);
+
+  JNIEnv *env;
+  struct jobj_storage *co = THAT_JOBJ(m->class);
+  jclass class = co->jobj;
+  jobject jjo; FLOAT_TYPE jjf; INT32 jji;
+  struct jobj_storage *jo;
+
+  if(args != 1+m->nargs)
+    error("wrong number of arguments for method.\n");
+
+  if(sp[-args].type != T_OBJECT || 
+     (jo = (struct jobj_storage *)get_storage(sp[-args].u.object,
+					      jobj_program))==NULL)
+    error("Bad argument 1 to call_nonvirtual.\n");
+
+  if((env = jvm_procure_env(co->jvm))==NULL) {
+    pop_n_elems(args);
+    push_int(0);
+    return;
+  }
+
+  make_jargs(jargs, args-1, m->sig->str, co->jvm, env);
+
+  switch(m->rettype) {
+  case 'Z':
+    jji = (*env)->CallNonvirtualBooleanMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'B':
+    jji = (*env)->CallNonvirtualByteMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'C':
+    jji = (*env)->CallNonvirtualCharMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'S':
+    jji = (*env)->CallNonvirtualShortMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'I':
+    jji = (*env)->CallNonvirtualIntMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'J':
+    jji = (*env)->CallNonvirtualLongMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'F':
+    jjf = (*env)->CallNonvirtualFloatMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_float(jjf);
+    break;
+  case 'D':
+    jjf = (*env)->CallNonvirtualDoubleMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_float(jjf);
+    break;
+  case 'L':
+  case '[':
+    jjo = (*env)->CallNonvirtualObjectMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    if(m->rettype=='[')
+      push_java_array(jjo, co->jvm, env, m->subtype);
+    else
+      push_java_anyobj(jjo, co->jvm, env);
+    break;
+  case 'V':
+  default:
+    (*env)->CallNonvirtualVoidMethodA(env, jo->jobj, class, m->method, jargs);
+    pop_n_elems(args);
+    push_int(0);
+    break;
+  }
+
+  free_jargs(jargs, args, m->sig->str);
+
+  jvm_vacate_env(co->jvm, env);
+}
+
+
+/* Fields */
+
+static void init_field_struct(struct object *o)
+{
+  struct field_storage *f=THIS_FIELD;
+
+  f->class = NULL;
+  f->name = NULL;
+  f->sig = NULL;
+}
+
+static void exit_field_struct(struct object *o)
+{
+  struct field_storage *f=THIS_FIELD;
+
+  if(f->sig != NULL)
+    free_string(f->sig);
+  if(f->name != NULL)
+    free_string(f->name);
+  if(f->class != NULL)
+    free_object(f->class);
+}
+
+static void field_gc_check(struct object *o)
+{
+  struct field_storage *f = THIS_FIELD;
+
+  if(f->class)
+    gc_check(f->class);
+}
+
+static void field_gc_mark(struct object *o)
+{
+  struct field_storage *f = THIS_FIELD;
+
+  if(f->class)
+    gc_mark_object_as_referenced(f->class);
+}
+
+static void f_field_create(INT32 args)
+{
+  struct field_storage *f=THIS_FIELD;
+  struct jobj_storage *c;
+  struct object *class;
+  struct pike_string *name, *sig;
+  JNIEnv *env;
+
+  if(args==1) {
+    get_all_args("Java.field->create()", args, "%o", &class);
+    name = NULL;
+    sig = NULL;
+  } else
+    get_all_args("Java.field->create()", args, "%S%S%o", &name, &sig, &class);
+
+  if((c = (struct jobj_storage *)get_storage(class, jclass_program)) == NULL)
+    error("Bad argument 3 to create().\n");
+
+  f->field = 0;
+
+  if(name == NULL || sig == NULL) {
+    f->class = class;
+    class->refs++;
+    pop_n_elems(args);
+    f->type = 0;
+    return;
+  }
+
+  if((env = jvm_procure_env(c->jvm))) {
+
+    f->field = (fp->current_object->prog==static_field_program?
+		(*env)->GetStaticFieldID(env, c->jobj, name->str, sig->str):
+		(*env)->GetFieldID(env, c->jobj, name->str, sig->str));
+
+    jvm_vacate_env(c->jvm, env);
+  }
+
+  if(f->field == 0) {
+    pop_n_elems(args);
+    destruct(fp->current_object);
+    return;
+  }
+
+  f->class = class;
+  f->name = name;
+  f->sig = sig;
+  class->refs++;
+  name->refs++;
+  sig->refs++;
+  pop_n_elems(args);
+  push_int(0);
+
+  if((f->type = sig->str[0])=='[')
+    f->subtype = sig->str[1];
+}
+
+static void f_field_set(INT32 args)
+{
+  struct field_storage *f=THIS_FIELD;
+  JNIEnv *env;
+  struct jobj_storage *co = THAT_JOBJ(f->class);
+  struct jobj_storage *jo;
+  jvalue v;
+
+  if(args!=2)
+    error("Incorrect number of arguments to set.\n");
+
+  if(sp[-args].type != T_OBJECT || 
+     (jo = (struct jobj_storage *)get_storage(sp[-args].u.object,
+					      jobj_program))==NULL)
+    error("Bad argument 1 to set.\n");
+
+  if((env = jvm_procure_env(co->jvm))==NULL) {
+    pop_n_elems(args);
+    push_int(0);
+    return;
+  }
+
+  make_jargs(&v, -1, f->sig->str, co->jvm, env);
+  switch(f->type) {
+  case 'Z':
+    (*env)->SetBooleanField(env, jo->jobj, f->field, v.z);
+    break;
+  case 'B':
+    (*env)->SetByteField(env, jo->jobj, f->field, v.b);
+    break;
+  case 'C':
+    (*env)->SetCharField(env, jo->jobj, f->field, v.c);
+    break;
+  case 'S':
+    (*env)->SetShortField(env, jo->jobj, f->field, v.s);
+    break;
+  case 'I':
+    (*env)->SetIntField(env, jo->jobj, f->field, v.i);
+    break;
+  case 'J':
+    (*env)->SetLongField(env, jo->jobj, f->field, v.j);
+    break;
+  case 'F':
+    (*env)->SetFloatField(env, jo->jobj, f->field, v.f);
+    break;
+  case 'D':
+    (*env)->SetDoubleField(env, jo->jobj, f->field, v.d);
+    break;
+  case 'L':
+  case '[':
+    (*env)->SetObjectField(env, jo->jobj, f->field, v.l);
+    break;
+  }
+
+  jvm_vacate_env(co->jvm, env);
+
+  pop_n_elems(args);
+  push_int(0);
+}
+
+static void f_field_get(INT32 args)
+{
+  struct field_storage *f=THIS_FIELD;
+  JNIEnv *env;
+  struct jobj_storage *co = THAT_JOBJ(f->class);
+  jclass class = co->jobj;
+  jobject jjo; FLOAT_TYPE jjf; INT32 jji;
+  struct jobj_storage *jo;
+
+  if(sp[-args].type != T_OBJECT || 
+     (jo = (struct jobj_storage *)get_storage(sp[-args].u.object,
+					      jobj_program))==NULL)
+    error("Bad argument 1 to get.\n");
+
+  if((env = jvm_procure_env(co->jvm))==NULL) {
+    pop_n_elems(args);
+    push_int(0);
+    return;
+  }
+
+  switch(f->type) {
+  case 'Z':
+    jji = (*env)->GetBooleanField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'B':
+    jji = (*env)->GetByteField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'C':
+    jji = (*env)->GetCharField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'S':
+    jji = (*env)->GetShortField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'I':
+    jji = (*env)->GetIntField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'J':
+    jji = (*env)->GetLongField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    push_int(jji);
+    break;
+  case 'F':
+    jjf = (*env)->GetFloatField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    push_float(jjf);
+    break;
+  case 'D':
+    jjf = (*env)->GetDoubleField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    push_float(jjf);
+    break;
+  case 'L':
+  case '[':
+    jjo = (*env)->GetObjectField(env, jo->jobj, f->field);
+    pop_n_elems(args);
+    if(f->type=='[')
+      push_java_array(jjo, co->jvm, env, f->subtype);
+    else
+      push_java_anyobj(jjo, co->jvm, env);
+    break;
+  default:
+    pop_n_elems(args);
+    push_int(0);
+    break;
+  }
+
+  jvm_vacate_env(co->jvm, env);
+}
+
+static void f_static_field_set(INT32 args)
+{
+  struct field_storage *f=THIS_FIELD;
+  JNIEnv *env;
+  struct jobj_storage *co = THAT_JOBJ(f->class);
+  jclass class = co->jobj;
+  jvalue v;
+
+  if(args!=1)
+    error("Incorrect number of arguments to set.\n");
+
+  if((env = jvm_procure_env(co->jvm))==NULL) {
+    pop_n_elems(args);
+    push_int(0);
+    return;
+  }
+
+  make_jargs(&v, -1, f->sig->str, co->jvm, env);
+  switch(f->type) {
+  case 'Z':
+    (*env)->SetStaticBooleanField(env, class, f->field, v.z);
+    break;
+  case 'B':
+    (*env)->SetStaticByteField(env, class, f->field, v.b);
+    break;
+  case 'C':
+    (*env)->SetStaticCharField(env, class, f->field, v.c);
+    break;
+  case 'S':
+    (*env)->SetStaticShortField(env, class, f->field, v.s);
+    break;
+  case 'I':
+    (*env)->SetStaticIntField(env, class, f->field, v.i);
+    break;
+  case 'J':
+    (*env)->SetStaticLongField(env, class, f->field, v.j);
+    break;
+  case 'F':
+    (*env)->SetStaticFloatField(env, class, f->field, v.f);
+    break;
+  case 'D':
+    (*env)->SetStaticDoubleField(env, class, f->field, v.d);
+    break;
+  case 'L':
+  case '[':
+    (*env)->SetStaticObjectField(env, class, f->field, v.l);
+    break;
+  }
+
+  jvm_vacate_env(co->jvm, env);
+
+  pop_n_elems(args);
+  push_int(0);
+}
+
+static void f_static_field_get(INT32 args)
+{
+  struct field_storage *f=THIS_FIELD;
+  JNIEnv *env;
+  struct jobj_storage *co = THAT_JOBJ(f->class);
+  jclass class = co->jobj;
+  jobject jjo; FLOAT_TYPE jjf; INT32 jji;
+
+  if((env = jvm_procure_env(co->jvm))==NULL) {
+    pop_n_elems(args);
+    push_int(0);
+    return;
+  }
+
+  pop_n_elems(args);
+  switch(f->type) {
+  case 'Z':
+    jji = (*env)->GetStaticBooleanField(env, class, f->field);
+    push_int(jji);
+    break;
+  case 'B':
+    jji = (*env)->GetStaticByteField(env, class, f->field);
+    push_int(jji);
+    break;
+  case 'C':
+    jji = (*env)->GetStaticCharField(env, class, f->field);
+    push_int(jji);
+    break;
+  case 'S':
+    jji = (*env)->GetStaticShortField(env, class, f->field);
+    push_int(jji);
+    break;
+  case 'I':
+    jji = (*env)->GetStaticIntField(env, class, f->field);
+    push_int(jji);
+    break;
+  case 'J':
+    jji = (*env)->GetStaticLongField(env, class, f->field);
+    push_int(jji);
+    break;
+  case 'F':
+    jjf = (*env)->GetStaticFloatField(env, class, f->field);
+    push_float(jjf);
+    break;
+  case 'D':
+    jjf = (*env)->GetStaticDoubleField(env, class, f->field);
+    push_float(jjf);
+    break;
+  case 'L':
+  case '[':
+    jjo = (*env)->GetStaticObjectField(env, class, f->field);
+    if(f->type=='[')
+      push_java_array(jjo, co->jvm, env, f->subtype);
+    else
+      push_java_anyobj(jjo, co->jvm, env);
+    break;
+  default:
+    push_int(0);
+    break;
+  }
+
+  jvm_vacate_env(co->jvm, env);
+}
+
+
+/* Classes */
+
+
+#ifdef SUPPORT_NATIVE_METHODS
+
+struct native_method_context;
+
+#ifdef HAVE_SPARC_CPU
+
+struct cpu_context {
+  unsigned INT32 code[19];
+};
+
+static void *low_make_stub(struct cpu_context *ctx, void *data, int statc,
+			   void (*dispatch)())
+{
+  unsigned INT32 *p = ctx->code;
+
+  if(!statc)
+    *p++ = 0xd223a048;  /* st  %o1, [ %sp + 0x48 ] */
+  *p++ = 0xd423a04c;  /* st  %o2, [ %sp + 0x4c ] */
+  *p++ = 0xd623a050;  /* st  %o3, [ %sp + 0x50 ] */
+  *p++ = 0xd823a054;  /* st  %o4, [ %sp + 0x54 ] */
+  *p++ = 0xda23a058;  /* st  %o5, [ %sp + 0x58 ] */
+  *p++ = 0x9de3bf90;  /* save  %sp, -112, %sp    */
+
+  *p++ = 0x11000000|(((unsigned INT32)data)>>10);
+                      /* sethi  %hi(data), %o0   */
+  *p++ = 0x90122000|(((unsigned INT32)data)&0x3ff);
+                      /* or  %o0, %lo(data), %o0 */
+
+  *p++ = 0x92162000;  /* mov  %i0, %o1           */
+  if(statc) {
+    *p++ = 0x94100019;  /* mov  %i1, %o2           */
+    *p++ = 0x9607a04c;  /* add  %fp, 0x4c, %o3     */
+  } else {
+    *p++ = 0x94100000;  /* mov  %g0, %o2           */
+    *p++ = 0x9607a048;  /* add  %fp, 0x48, %o3     */
+  }
+
+  *p++ = 0x19000000|(((unsigned INT32)(void *)dispatch)>>10);
+                      /* sethi  %hi(dispatch), %o4   */
+  *p++ = 0x98132000|(((unsigned INT32)(void *)dispatch)&0x3ff);
+                      /* or  %o4, %lo(dispatch), %o4 */
+
+  *p++ = 0x9fc30000;  /* call  %o4               */
+  *p++ = 0x01000000;  /* nop                     */
+  *p++ = 0xb0100008;  /* mov %o0,%i0             */
+  *p++ = 0xb2100009;  /* mov %o1,%i1             */
+  *p++ = 0x81c7e008;  /* ret                     */
+  *p++ = 0x81e80000;  /* restore                 */
+
+  return ctx->code;
+}
+
+#else
+#ifdef HAVE_X86_CPU
+
+struct cpu_context {
+  unsigned char code[32];
+};
+
+static void *low_make_stub(struct cpu_context *ctx, void *data, int statc,
+			   void (*dispatch)())
+{
+  unsigned char *p = ctx->code;
+
+  *p++ = 0x55;               /* pushl  %ebp       */
+  *p++ = 0x8b; *p++ = 0xec;  /* movl  %esp, %ebp  */
+  *p++ = 0x8d; *p++ = 0x45;  /* lea  n(%ebp),%eax */
+  if(statc)
+    *p++ = 16;
+  else
+    *p++ = 12;
+  *p++ = 0x50;               /* pushl  %eax       */
+  if(statc) {
+    *p++ = 0xff; *p++ = 0x75; *p++ = 0x0c;  /* pushl  12(%ebp) */
+  } else {
+    *p++ = 0x6a; *p++ = 0x00;               /* pushl  $0x0     */
+  }
+  *p++ = 0xff; *p++ = 0x75; *p++ = 0x08;  /* pushl  8(%ebp)  */
+  *p++ = 0x68;               /* pushl  $data          */
+  *((unsigned INT32 *)p) = (unsigned INT32)data; p+=4;
+  *p++ = 0xb8;               /* movl  $dispatch, %eax */
+  *((unsigned INT32 *)p) = (unsigned INT32)dispatch; p+=4;
+  *p++ = 0xff; *p++ = 0xd0;  /* call  *%eax          */
+  *p++ = 0x8b; *p++ = 0xe5;  /* movl  %ebp, %esp     */
+  *p++ = 0x5d;               /* popl  %ebp           */
+  *p++ = 0xc3;               /* ret                  */
+
+  return ctx->code;
+}
+
+#else
+#error How did you get here?  It should never happen.
+#endif /* HAVE_X86_CPU */
+#endif /* HAVE_SPARC_CPU */
+
+struct natives_storage;
+
+struct native_method_context {
+  struct svalue callback;
+  struct pike_string *name, *sig;
+  struct natives_storage *nat;
+  struct cpu_context cpu;
+};
+
+struct natives_storage {
+
+  struct object *jvm, *cls;
+  int num_methods;
+  struct native_method_context *cons;
+  JNINativeMethod *jnms;
+
+};
+
+static void make_java_exception(struct object *jvm, JNIEnv *env,
+				struct svalue *v)
+{
+  union anything *a;
+  struct jvm_storage *j =
+    (struct jvm_storage *)get_storage(jvm, jvm_program);
+
+  if(!j)
+    return;
+
+  if(v->type == T_ARRAY && v->u.array->size &&
+     (a=low_array_get_item_ptr(v->u.array, 0, T_STRING))!=NULL) {
+    (*env)->ThrowNew(env, j->class_runtimex, a->string->str);
+  } else {
+    (*env)->ThrowNew(env, j->class_runtimex,
+		     "Nonstandard pike exception thrown.");
+  }
+}
+
+#define GET_NATIVE_ARG(ty) (((args)=((ty *)(args))+1),(((ty *)(args))[-1]))
+
+static void do_native_dispatch(struct native_method_context *ctx,
+			       JNIEnv *env, jclass cls, void *args,
+			       jvalue *rc)
+{
+  JMP_BUF recovery;
+  struct svalue *osp = sp;
+  int nargs = 0;
+  char *p;
+
+  if (SETJMP(recovery)) {
+    make_java_exception(ctx->nat->jvm, env, &throw_value);
+    pop_n_elems(sp-osp);
+    UNSETJMP(recovery);
+    return;
+  }
+
+  if(!cls) {
+    push_java_anyobj(GET_NATIVE_ARG(jobject), ctx->nat->jvm, env);
+    nargs++;
+  }
+
+  p = ctx->sig->str;
+
+  if(*p == '(')
+    p++;
+
+  while(*p && *p!=')') {
+    switch(*p++) {
+    case 'Z':
+    case 'B':
+    case 'C':
+    case 'S':
+    case 'I':
+    default:
+      push_int(GET_NATIVE_ARG(jint));
+      break;
+      
+    case 'J':
+      push_int(GET_NATIVE_ARG(jlong));
+      break;
+      
+    case 'F':
+      push_float(GET_NATIVE_ARG(jfloat));
+      break;
+      
+    case 'D':
+      push_float(GET_NATIVE_ARG(jdouble));
+      break;
+      
+    case 'L':
+      push_java_anyobj(GET_NATIVE_ARG(jobject), ctx->nat->jvm, env);
+      while(*p && *p++!=';') ;
+      break;
+      
+    case '[':
+      push_java_array(GET_NATIVE_ARG(jarray), ctx->nat->jvm, env, *p);
+      while(*p == '[')
+	p++;
+      if(*p++ == 'L')
+	while(*p && *p++!=';') ;
+      break;
+    }
+    nargs ++;
+  }
+
+  if(*p == ')')
+    p++;
+
+  apply_svalue(&ctx->callback, nargs);
+
+  memset(rc, 0, sizeof(*rc));
+
+  if(*p != 'V')
+    make_jargs(rc, -1, p, ctx->nat->jvm, env);
+
+  pop_n_elems(sp-osp);
+  UNSETJMP(recovery);
+}
+
+static void native_dispatch(struct native_method_context *ctx,
+			    JNIEnv *env, jclass cls, void *args,
+			    jvalue *rc)
+{
+  extern struct program *thread_id_prog;
+  struct thread_state *state;
+
+  if((state = thread_state_for_id(th_self()))!=NULL) {
+    /* This is a pike thread.  Do we have the interpreter lock? */
+    if(!state->swapped) {
+      /* Yes.  Go for it... */
+      do_native_dispatch(ctx, env, cls, args, rc);
+    } else {
+      /* Nope, let's get it... */
+      mt_lock(&interpreter_lock);
+      SWAP_IN_THREAD(state);
+
+      do_native_dispatch(ctx, env, cls, args, rc);
+
+      /* Restore */
+      SWAP_OUT_THREAD(state);
+      mt_unlock(&interpreter_lock);
+    }
+  } else {
+    /* Not a pike thread.  Create a temporary thread_id... */
+    mt_lock(&interpreter_lock);
+    init_interpreter();
+    thread_id=clone_object(thread_id_prog,0);
+    SWAP_OUT_THREAD((struct thread_state *)thread_id->storage);
+    ((struct thread_state *)thread_id->storage)->swapped=0;
+    ((struct thread_state *)thread_id->storage)->id=th_self();
+    num_threads++;
+    thread_table_insert(thread_id);
+    do_native_dispatch(ctx, env, cls, args, rc);
+    ((struct thread_state *)(thread_id->storage))->status=THREAD_EXITED;
+    co_signal(& ((struct thread_state *)(thread_id->storage))->status_change);
+    thread_table_delete(thread_id);
+    free_object(thread_id);
+    thread_id=NULL;
+    cleanup_interpret();
+    num_threads--;
+    mt_unlock(&interpreter_lock);
+  }
+}
+
+static jboolean native_dispatch_z(struct native_method_context *ctx,
+				  JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.z;
+}
+
+static jbyte native_dispatch_b(struct native_method_context *ctx,
+			       JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.b;
+}
+
+static jchar native_dispatch_c(struct native_method_context *ctx,
+			       JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.c;
+}
+
+static jshort native_dispatch_s(struct native_method_context *ctx,
+				JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.s;
+}
+
+static jint native_dispatch_i(struct native_method_context *ctx,
+			      JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.i;
+}
+
+static jlong native_dispatch_j(struct native_method_context *ctx,
+			       JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.j;
+}
+
+static jfloat native_dispatch_f(struct native_method_context *ctx,
+				JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.f;
+}
+
+static jdouble native_dispatch_d(struct native_method_context *ctx,
+				 JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.d;
+}
+
+static jobject native_dispatch_l(struct native_method_context *ctx,
+				 JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+  return v.l;
+}
+
+static void native_dispatch_v(struct native_method_context *ctx,
+			      JNIEnv *env, jobject obj, void *args)
+{
+  jvalue v;
+  native_dispatch(ctx, env, obj, args, &v);
+}
+
+static void make_stub(struct cpu_context *ctx, void *data, int statc, int rt)
+{
+  void *disp = native_dispatch_v;
+
+  switch(rt) {
+  case 'Z': disp = native_dispatch_z; break;
+  case 'B': disp = native_dispatch_b; break;
+  case 'C': disp = native_dispatch_c; break;
+  case 'S': disp = native_dispatch_s; break;
+  case 'I': disp = native_dispatch_i; break;
+  case 'J': disp = native_dispatch_j; break;
+  case 'F': disp = native_dispatch_f; break;
+  case 'D': disp = native_dispatch_d; break;
+  case '[':
+  case 'L': disp = native_dispatch_l; break;
+  default:
+    disp = native_dispatch_v;
+  }
+
+  low_make_stub(ctx, data, statc, disp);
+}
+
+static void build_native_entry(JNIEnv *env, jclass cls,
+			       struct native_method_context *con,
+			       JNINativeMethod *jnm,
+			       struct pike_string *name,
+			       struct pike_string *sig)
+{
+  int statc;
+  char *p = sig->str;
+
+  if((*env)->GetMethodID(env, cls, name->str, sig->str))
+    statc = 0;
+  else {
+    (*env)->ExceptionClear(env);
+    if((*env)->GetStaticMethodID(env, cls, name->str, sig->str))
+      statc = 1;
+    else {
+      (*env)->ExceptionClear(env);
+      error("trying to register nonexistant function\n");
+    }
+  }
+
+  con->name = name;
+  con->sig = sig;
+  name->refs++;
+  sig->refs++;
+  jnm->name = name->str;
+  jnm->signature = sig->str;
+  jnm->fnPtr = (void*)&con->cpu;
+  while(*p && *p++ != ')');
+  make_stub(&con->cpu, con, statc, *p);
+}
+
+static void init_natives_struct(struct object *o)
+{
+  struct natives_storage *n = THIS_NATIVES;
+
+  n->jvm = NULL;
+  n->cls = NULL;
+  n->num_methods = 0;
+  n->cons = NULL;
+  n->jnms = NULL;
+}
+
+static void exit_natives_struct(struct object *o)
+{
+  JNIEnv *env;
+  struct natives_storage *n = THIS_NATIVES;
+  
+  if(n->jvm) {
+    if(n->cls) {
+      if((env = jvm_procure_env(n->jvm)) != NULL) {
+	(*env)->UnregisterNatives(env, THAT_JOBJ(n->cls)->jobj);
+	jvm_vacate_env(n->jvm, env);
+      }
+      free_object(n->cls);
+    }
+    free_object(n->jvm);
+  }
+  if(n->jnms)
+    free(n->jnms);
+  if(n->cons) {
+    int i;
+    for(i=0; i<n->num_methods; i++) {
+      free_svalue(&n->cons[i].callback);
+      if(n->cons[i].name)
+	free_string(n->cons[i].name);
+      if(n->cons[i].sig)
+	free_string(n->cons[i].sig);
+    }
+    free(n->cons);
+  }
+}
+
+static void natives_gc_check(struct object *o)
+{
+  struct natives_storage *n = THIS_NATIVES;
+
+  if(n->jvm)
+    gc_check(n->jvm);
+  if(n->cls)
+    gc_check(n->cls);
+  if(n->cons) {
+    int i;
+    for(i=0; i<n->num_methods; i++)
+      gc_check_svalues(&n->cons[i].callback, 1);
+  }
+}
+
+static void natives_gc_mark(struct object *o)
+{
+  struct natives_storage *n = THIS_NATIVES;
+
+  if(n->jvm)
+    gc_mark_object_as_referenced(n->jvm);
+  if(n->cls)
+    gc_mark_object_as_referenced(n->cls);
+  if(n->cons) {
+    int i;
+    for(i=0; i<n->num_methods; i++)
+      gc_mark_svalues(&n->cons[i].callback, 1);
+  }
+}
+
+static void f_natives_create(INT32 args)
+{
+  struct natives_storage *n = THIS_NATIVES;
+  struct jobj_storage *c;
+  struct object *cls;
+  struct array *arr;
+  int i, rc=-1;
+  JNIEnv *env;
+
+  get_all_args("Java.natives->create()", args, "%a%o", &arr, &cls);
+
+  if((c = (struct jobj_storage *)get_storage(cls, jclass_program)) == NULL)
+    error("Bad argument 2 to create().\n");
+
+  if(n->num_methods)
+    error("create() called twice in Java.natives object.\n");
+
+  if(!arr->size) {
+    pop_n_elems(args);
+    return;
+  }
+
+  if((env = jvm_procure_env(c->jvm))) {
+
+    n->cons = (struct native_method_context *)
+      xalloc(arr->size * sizeof(struct native_method_context));
+    n->jnms = (JNINativeMethod *)
+      xalloc(arr->size * sizeof(JNINativeMethod));
+
+    for(i=0; i<arr->size; i++) {
+      struct array *nm;
+      if(ITEM(arr)[i].type != T_ARRAY || ITEM(arr)[i].u.array->size != 3)
+	error("Bad argument 1 to create().\n");
+      nm = ITEM(arr)[i].u.array;
+      if(ITEM(nm)[0].type != T_STRING || ITEM(nm)[1].type != T_STRING)
+	error("Bad argument 1 to create().\n");
+      assign_svalue_no_free(&n->cons[i].callback, &ITEM(nm)[2]);
+      n->cons[i].nat = n;
+      n->num_methods++;
+      
+      build_native_entry(env, c->jobj, &n->cons[i], &n->jnms[i],
+			 ITEM(nm)[0].u.string, ITEM(nm)[1].u.string);
+    }
+    
+    n->jvm = c->jvm;
+    n->cls = cls;
+    n->jvm->refs++;
+    n->cls->refs++;
+
+    rc = (*env)->RegisterNatives(env, c->jobj, n->jnms, n->num_methods);
+    jvm_vacate_env(c->jvm, env);
+  }
+
+  pop_n_elems(args);
+
+  if(rc<0)
+    destruct(fp->current_object);
+}
+
+#endif /* SUPPORT_NATIVE_METHODS */
+
+static void f_super_class(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  JNIEnv *env;
+  
+  pop_n_elems(args);
+  if((env = jvm_procure_env(jo->jvm))) {
+    push_java_class((*env)->GetSuperclass(env, jo->jobj), jo->jvm, env);
+    jvm_vacate_env(jo->jvm, env);
+  } else
+    push_int(0);
+}
+
+static void f_is_assignable_from(INT32 args)
+{
+  struct jobj_storage *jc, *jo = THIS_JOBJ;
+  JNIEnv *env;
+  jboolean iaf;
+
+  if(args<1 || sp[-args].type != T_OBJECT ||
+     (jc = (struct jobj_storage *)get_storage(sp[-args].u.object,
+					      jclass_program))==NULL)
+    error("illegal argument 1 to is_assignable_from\n");
+
+  if((env = jvm_procure_env(jo->jvm))) {
+    iaf = (*env)->IsAssignableFrom(env, jo->jobj, jc->jobj);
+    jvm_vacate_env(jo->jvm, env);
+  } else
+    iaf = 0;
+
+  pop_n_elems(args);
+  push_int((iaf? 1:0));
+}
+
+static void f_throw_new(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  struct jvm_storage *jj =
+    (struct jvm_storage *)get_storage(jo->jvm, jvm_program);
+  JNIEnv *env;
+  char *cn;
+
+  get_all_args("throw_new", args, "%s", &cn);
+
+  if((env = jvm_procure_env(jo->jvm))) {
+
+    if(!(*env)->IsAssignableFrom(env, jo->jobj, jj->class_throwable)) {
+      jvm_vacate_env(jo->jvm, env);
+      error("throw_new called in a class that doesn't inherit java.lang.Throwable!\n");
+    }
+
+    if((*env)->ThrowNew(env, jo->jobj, cn)<0) {
+      jvm_vacate_env(jo->jvm, env);
+      error("throw_new failed!\n");
+    }
+
+    jvm_vacate_env(jo->jvm, env);
+  }
+
+  pop_n_elems(args);
+  push_int(0);
+}
+
+static void f_alloc(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  JNIEnv *env;
+  
+  pop_n_elems(args);
+
+  if((env = jvm_procure_env(jo->jvm))) {
+    push_java_anyobj((*env)->AllocObject(env, jo->jobj), jo->jvm, env);
+    jvm_vacate_env(jo->jvm, env);
+  } else push_int(0);
+}
+
+static void f_get_method(INT32 args)
+{
+  struct object *oo;
+
+  check_all_args("get_method", args, BIT_STRING, BIT_STRING, 0);
+
+  push_object(this_object());
+  oo=clone_object(method_program, args+1);
+  if(oo->prog!=NULL)
+    push_object(oo);
+  else {
+    free_object(oo);
+    push_int(0);
+  }
+}
+
+static void f_get_static_method(INT32 args)
+{
+  struct object *oo;
+
+  check_all_args("get_static_method", args, BIT_STRING, BIT_STRING, 0);
+
+  push_object(this_object());
+  oo=clone_object(static_method_program, args+1);
+  if(oo->prog!=NULL)
+    push_object(oo);
+  else {
+    free_object(oo);
+    push_int(0);
+  }
+}
+
+static void f_get_field(INT32 args)
+{
+  struct object *oo;
+
+  check_all_args("get_field", args, BIT_STRING, BIT_STRING, 0);
+
+  push_object(this_object());
+  oo=clone_object(field_program, args+1);
+  if(oo->prog!=NULL)
+    push_object(oo);
+  else {
+    free_object(oo);
+    push_int(0);
+  }
+}
+
+static void f_get_static_field(INT32 args)
+{
+  struct object *oo;
+
+  check_all_args("get_static_field", args, BIT_STRING, BIT_STRING, 0);
+
+  push_object(this_object());
+  oo=clone_object(static_field_program, args+1);
+  if(oo->prog!=NULL)
+    push_object(oo);
+  else {
+    free_object(oo);
+    push_int(0);
+  }
+}
+
+#ifdef SUPPORT_NATIVE_METHODS
+static void f_register_natives(INT32 args)
+{
+  struct object *oo;
+  check_all_args("register_natives", args, BIT_ARRAY, 0);
+  push_object(this_object());
+  oo=clone_object(natives_program, args+1);
+  if(oo->prog!=NULL)
+    push_object(oo);
+  else {
+    free_object(oo);
+    push_int(0);
+  } 
+}
+#endif /* SUPPORT_NATIVE_METHODS */
+
+
+/* Throwables */
+
+
+static void f_javathrow(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  JNIEnv *env;
+
+  pop_n_elems(args);
+
+  if((env = jvm_procure_env(jo->jvm))) {
+    if((*env)->Throw(env, jo->jobj)<0) {
+      jvm_vacate_env(jo->jvm, env);
+      error("throw failed!\n");
+    }
+    jvm_vacate_env(jo->jvm, env);
+  }
+  push_int(0);
+}
+
+
+/* Arrays */
+
+static void f_javaarray_sizeof(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  JNIEnv *env;
+
+  pop_n_elems(args);
+
+  if((env = jvm_procure_env(jo->jvm))) {
+    push_int((*env)->GetArrayLength(env, jo->jobj));  
+    jvm_vacate_env(jo->jvm, env);
+  } else
+    push_int(0);
+}
+
+static void javaarray_subarray(struct object *jvm, struct object *oo,
+			       jobject jobj, int ty, INT32 e1, INT32 e2)
+{
+  JNIEnv *env;
+  jobject jobj2;
+  jclass jocls, eltcls;
+  struct jvm_storage *j;
+
+  if((j = (struct jvm_storage *)get_storage(jvm, jvm_program))==NULL) {
+    push_int(0);
+    return;
+  }
+
+  if((env = jvm_procure_env(jvm))) {
+    jsize size = (*env)->GetArrayLength(env, jobj);
+
+    if(e1<0)
+      e1=0;
+    if(e1>size)
+      e1=size;
+    if(e2>=size)
+      e2=size-1;
+    if(e2<e1)
+      e2=0;
+    else
+      e2-=e1-1;
+
+    if(e2==size) {
+      /* Entire array selected */
+      jvm_vacate_env(jvm, env);
+      oo->refs++;
+      push_object(oo);
+      return;
+    }
+
+    switch(ty) {
+    case 'Z':
+      jobj2 = (*env)->NewBooleanArray(env, e2);
+      break;
+    case 'B':
+      jobj2 = (*env)->NewByteArray(env, e2);
+      break;
+    case 'C':
+      jobj2 = (*env)->NewCharArray(env, e2);
+      break;
+    case 'S':
+      jobj2 = (*env)->NewShortArray(env, e2);
+      break;
+    case 'I':
+      jobj2 = (*env)->NewIntArray(env, e2);
+      break;
+    case 'J':
+      jobj2 = (*env)->NewLongArray(env, e2);
+      break;
+    case 'F':
+      jobj2 = (*env)->NewFloatArray(env, e2);
+      break;
+    case 'D':
+      jobj2 = (*env)->NewDoubleArray(env, e2);
+      break;
+    case 'L':
+    case '[':
+    default:
+      jocls = (*env)->GetObjectClass(env, jobj);
+      eltcls = (jclass)(*env)->CallObjectMethod(env, jocls,
+						j->method_getcomponenttype);
+      jobj2 = (*env)->NewObjectArray(env, e2, eltcls, 0);
+      (*env)->DeleteLocalRef(env, eltcls);
+      (*env)->DeleteLocalRef(env, jocls);
+      break;
+    }
+
+    if(!jobj2) {
+      jvm_vacate_env(jvm, env);
+      push_int(0);
+      return;
+    }
+
+    if(e2)
+      (*env)->CallStaticVoidMethod(env, j->class_system, j->method_arraycopy,
+				   jobj, (jint)e1, jobj2, (jint)0, (jint)e2);
+    push_java_array(jobj2, jvm, env, ty);
+    jvm_vacate_env(jvm, env);
+  } else
+    push_int(0);
+}
+
+static void f_javaarray_getelt(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  struct jarray_storage *ja = THIS_JARRAY;
+  JNIEnv *env;
+  INT32 n;
+  jvalue jjv;
+
+  if(args<1 || sp[-args].type != T_INT || (args>1 && sp[1-args].type != T_INT))
+    error("Bad args to `[].");
+
+  n = sp[-args].u.integer;
+
+  if(args>1) {
+    INT32 m = sp[1-args].u.integer;
+    pop_n_elems(args);
+    javaarray_subarray(jo->jvm, fp->current_object, jo->jobj, ja->ty, n, m);
+    return;
+  }
+
+  pop_n_elems(args);
+
+  if((env = jvm_procure_env(jo->jvm))) {
+
+    if(n<0) {
+      /* Count backwards... */
+      n += (*env)->GetArrayLength(env, jo->jobj);
+    }
+
+    switch(ja->ty) {
+    case 'Z':
+      (*env)->GetBooleanArrayRegion(env, jo->jobj, n, 1, &jjv.z);
+      push_int(jjv.z);
+      break;
+    case 'B':
+      (*env)->GetByteArrayRegion(env, jo->jobj, n, 1, &jjv.b);
+      push_int(jjv.b);
+      break;
+    case 'C':
+      (*env)->GetCharArrayRegion(env, jo->jobj, n, 1, &jjv.c);
+      push_int(jjv.c);
+      break;
+    case 'S':
+      (*env)->GetShortArrayRegion(env, jo->jobj, n, 1, &jjv.s);
+      push_int(jjv.s);
+      break;
+    case 'I':
+      (*env)->GetIntArrayRegion(env, jo->jobj, n, 1, &jjv.i);
+      push_int(jjv.i);
+      break;
+    case 'J':
+      (*env)->GetLongArrayRegion(env, jo->jobj, n, 1, &jjv.j);
+      push_int(jjv.j);
+      break;
+    case 'F':
+      (*env)->GetFloatArrayRegion(env, jo->jobj, n, 1, &jjv.f);
+      push_float(jjv.f);
+      break;
+    case 'D':
+      (*env)->GetDoubleArrayRegion(env, jo->jobj, n, 1, &jjv.d);
+      push_float(jjv.d);
+      break;
+    case 'L':
+    case '[':
+      push_java_anyobj((*env)->GetObjectArrayElement(env, jo->jobj, n),
+		       jo->jvm, env);
+      break;
+    default:
+      push_int(0);
+    }
+    jvm_vacate_env(jo->jvm, env);
+  } else
+    push_int(0);
+}
+
+static void f_javaarray_indices(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  JNIEnv *env;
+  INT32 size;
+  struct array *a;
+
+  if((env = jvm_procure_env(jo->jvm))) {
+    size = (*env)->GetArrayLength(env, jo->jobj);
+    jvm_vacate_env(jo->jvm, env);
+  } else
+    size = 0;
+  a = allocate_array_no_init(size,0);
+  a->type_field=BIT_INT;
+  while(--size>=0) {
+    ITEM(a)[size].type=T_INT;
+    ITEM(a)[size].subtype=NUMBER_NUMBER;
+    ITEM(a)[size].u.integer=size;
+  }
+  pop_n_elems(args);
+  push_array(a);
+}
+
+static void f_javaarray_values(INT32 args)
+{
+  struct jobj_storage *jo = THIS_JOBJ;
+  struct jarray_storage *ja = THIS_JARRAY;
+  JNIEnv *env;
+
+  if((env = jvm_procure_env(jo->jvm))) {
+    INT32 i, size = (*env)->GetArrayLength(env, jo->jobj);
+    pop_n_elems(args);
+    if(ja->ty == 'L' || ja->ty == '[') {
+      for(i=0; i<size; i++)
+	push_java_anyobj((*env)->GetObjectArrayElement(env, jo->jobj, i),
+			 jo->jvm, env);
+      f_aggregate(size);
+    } else {
+      struct array *ar = allocate_array_no_init(size, 0);
+      if(ar == NULL)
+	push_int(0);
+      else {
+	void *a = (*env)->GetPrimitiveArrayCritical(env, jo->jobj, 0);
+	if(a == NULL) {
+	  free_array(ar);
+	  push_int(0);
+	} else {
+	  switch(ja->ty) {
+	  case 'Z':
+	    ar->type_field=BIT_INT;
+	    for(i=0; i<size; i++) {
+	      ITEM(ar)[i].type = T_INT;
+	      ITEM(ar)[i].subtype = NUMBER_NUMBER;
+	      ITEM(ar)[i].u.integer = ((jboolean*)a)[i];
+	    }
+	    break;
+	  case 'B':
+	    ar->type_field=BIT_INT;
+	    for(i=0; i<size; i++) {
+	      ITEM(ar)[i].type = T_INT;
+	      ITEM(ar)[i].subtype = NUMBER_NUMBER;
+	      ITEM(ar)[i].u.integer = ((jbyte*)a)[i];
+	    }
+	    break;
+	  case 'C':
+	    ar->type_field=BIT_INT;
+	    for(i=0; i<size; i++) {
+	      ITEM(ar)[i].type = T_INT;
+	      ITEM(ar)[i].subtype = NUMBER_NUMBER;
+	      ITEM(ar)[i].u.integer = ((jchar*)a)[i];
+	    }
+	    break;
+	  case 'S':
+	    ar->type_field=BIT_INT;
+	    for(i=0; i<size; i++) {
+	      ITEM(ar)[i].type = T_INT;
+	      ITEM(ar)[i].subtype = NUMBER_NUMBER;
+	      ITEM(ar)[i].u.integer = ((jshort*)a)[i];
+	    }
+	    break;
+	  case 'I':
+	  default:
+	    ar->type_field=BIT_INT;
+	    for(i=0; i<size; i++) {
+	      ITEM(ar)[i].type = T_INT;
+	      ITEM(ar)[i].subtype = NUMBER_NUMBER;
+	      ITEM(ar)[i].u.integer = ((jint*)a)[i];
+	    }
+	    break;
+	  case 'J':
+	    ar->type_field=BIT_INT;
+	    for(i=0; i<size; i++) {
+	      ITEM(ar)[i].type = T_INT;
+	      ITEM(ar)[i].subtype = NUMBER_NUMBER;
+	      ITEM(ar)[i].u.integer = ((jlong*)a)[i];
+	    }
+	    break;
+	  case 'F':
+	    ar->type_field=BIT_FLOAT;
+	    for(i=0; i<size; i++) {
+	      ITEM(ar)[i].type = T_FLOAT;
+	      ITEM(ar)[i].u.float_number = ((jfloat*)a)[i];
+	    }
+	    break;
+	  case 'D':
+	    ar->type_field=BIT_FLOAT;
+	    for(i=0; i<size; i++) {
+	      ITEM(ar)[i].type = T_FLOAT;
+	      ITEM(ar)[i].u.float_number = ((jdouble*)a)[i];
+	    }
+	    break;	    
+	  }
+	  (*env)->ReleasePrimitiveArrayCritical(env, jo->jobj, a, 0);
+	  push_array(ar);
+	}
+      }
+    }
+    jvm_vacate_env(jo->jvm, env);
+  } else {
+    pop_n_elems(args);
+    push_int(0);
+  }
+}
+
+
+/* Attachment */
+
+#ifdef _REENTRANT
+
+static void init_att_struct(struct object *o)
+{
+  struct att_storage *att = THIS_ATT;
+  att->jvm = NULL;
+  att->env = NULL;
+  clear_svalues(&att->thr, 1);
+}
+
+static void exit_att_struct(struct object *o)
+{
+  struct att_storage *att = THIS_ATT;
+
+  if(att->jvm) {
+    struct jvm_storage *j =
+      (struct jvm_storage *)get_storage(att->jvm, jvm_program);
+    if(att->env) {
+      THREAD_T me = th_self();
+      if(!th_equal(me, att->tid))
+	/* Hum hum.  This should (hopefully) only happen at exit time
+	   when we're destructing all objects.  In this case it should be
+	   safe just to ignore detaching, as the JVM itself will be destroyed
+	   within moments... */
+	;
+      else
+	(*j->jvm)->DetachCurrentThread(j->jvm);
+    }
+    free_object(att->jvm);
+  }
+  free_svalue(&att->thr);
+}
+
+static void att_gc_check(struct object *o)
+{
+  struct att_storage *att = THIS_ATT;
+
+  if(att->jvm)
+    gc_check(att->jvm);
+  gc_check_svalues(&att->thr, 1);
+}
+
+static void att_gc_mark(struct object *o)
+{
+  struct att_storage *att = THIS_ATT;
+
+  if(att->jvm)
+    gc_mark_object_as_referenced(att->jvm);
+  gc_mark_svalues(&att->thr, 1);
+}
+
+static void f_att_create(INT32 args)
+{
+  struct object *j;
+  struct jvm_storage *jvm;
+  struct att_storage *att = THIS_ATT;
+
+  get_all_args("Java.attachment->create()", args, "%o", &j);
+
+  if((jvm = (struct jvm_storage *)get_storage(j, jvm_program))==NULL)
+    error("Bad argument 1 to create().\n");
+
+  att->jvm = j;
+  j->refs++;
+  pop_n_elems(args);
+  f_this_thread(0);
+  assign_svalue(&att->thr, &sp[-1]);
+  pop_n_elems(1);
+  att->args.version = JNI_VERSION_1_2;
+  att->args.name = NULL;
+  att->args.group = NULL;
+
+  att->tid = th_self();
+  if((*jvm->jvm)->AttachCurrentThread(jvm->jvm,
+				      (void **)&att->env, &att->args)<0)
+    destruct(fp->current_object);
+}
+
+#endif /* _REENTRANT */
+
+
+/* JVM */
+
+
+static void f_create(INT32 args)
+{
+  struct jvm_storage *j = THIS_JVM;
+  char *classpath = NULL;
+  jclass cls;
+
+  if(j->jvm)
+    (*j->jvm)->DestroyJavaVM(j->jvm);
+
+  j->vm_args.version = 0x00010002; /* Java 1.2. */
+  j->vm_args.nOptions = 0;
+  j->vm_args.options = j->vm_options;
+  j->vm_args.ignoreUnrecognized = JNI_TRUE;
+
+  /* Set classpath */
+  if(args>0 && sp[-args].type == T_STRING) {
+    classpath = sp[-args].u.string->str;
+    sp[-args].u.string->refs++;
+    j->classpath_string = sp[-args].u.string;
+  } else {
+    if(getenv("CLASSPATH"))
+      classpath = getenv("CLASSPATH");
+#if 0
+#ifdef JAVA_HOME
+    else
+      classpath = ".:"JAVA_HOME"/classes:"JAVA_HOME"/lib/classes.zip:"
+	JAVA_HOME"/lib/rt.jar:"JAVA_HOME"/lib/i18n.jar";
+#endif /* JAVA_HOME */
+#else
+    else
+      classpath = ".";
+#endif
+    if(classpath != NULL)
+      j->classpath_string = make_shared_string(classpath);
+  }
+  if(classpath != NULL) {
+    push_text("-Djava.class.path=");
+    push_string(j->classpath_string);
+    j->classpath_string = NULL;
+    f_add(2);
+    sp[-1].u.string->refs++;
+    j->classpath_string = sp[-1].u.string;
+    pop_n_elems(1);
+    j->vm_args.options[j->vm_args.nOptions].optionString =
+      j->classpath_string->str;
+    j->vm_args.options[j->vm_args.nOptions].extraInfo = NULL;
+    j->vm_args.nOptions++;
+  }
+#ifdef JAVA_LIBPATH
+  j->vm_args.options[j->vm_args.nOptions].optionString =
+    "-Djava.library.path="JAVA_LIBPATH;
+  j->vm_args.options[j->vm_args.nOptions].extraInfo = NULL;
+  j->vm_args.nOptions++;
+#endif
+
+  /* load and initialize a Java VM, return a JNI interface 
+   * pointer in env */
+  if(JNI_CreateJavaVM(&j->jvm, (void**)&j->env, &j->vm_args))
+    error( "Failed to create virtual machine\n" );
+
+  cls = (*j->env)->FindClass(j->env, "java/lang/Object");
+  j->class_object = (*j->env)->NewGlobalRef(j->env, cls);
+  (*j->env)->DeleteLocalRef(j->env, cls);
+  cls = (*j->env)->FindClass(j->env, "java/lang/Class");
+  j->class_class = (*j->env)->NewGlobalRef(j->env, cls);
+  (*j->env)->DeleteLocalRef(j->env, cls);
+  cls = (*j->env)->FindClass(j->env, "java/lang/String");
+  j->class_string = (*j->env)->NewGlobalRef(j->env, cls);
+  (*j->env)->DeleteLocalRef(j->env, cls);
+  cls = (*j->env)->FindClass(j->env, "java/lang/Throwable");
+  j->class_throwable = (*j->env)->NewGlobalRef(j->env, cls);
+  (*j->env)->DeleteLocalRef(j->env, cls);
+  cls = (*j->env)->FindClass(j->env, "java/lang/RuntimeException");
+  j->class_runtimex = (*j->env)->NewGlobalRef(j->env, cls);
+  (*j->env)->DeleteLocalRef(j->env, cls);
+  cls = (*j->env)->FindClass(j->env, "java/lang/System");
+  j->class_system = (*j->env)->NewGlobalRef(j->env, cls);
+  (*j->env)->DeleteLocalRef(j->env, cls);
+
+  j->method_hash =
+    (*j->env)->GetMethodID(j->env, j->class_object, "hashCode", "()I");
+  j->method_tostring =
+    (*j->env)->GetMethodID(j->env, j->class_object, "toString",
+			   "()Ljava/lang/String;");
+  j->method_arraycopy =
+    (*j->env)->GetStaticMethodID(j->env, j->class_system, "arraycopy",
+				 "(Ljava/lang/Object;ILjava/lang/Object;II)V");
+  j->method_getcomponenttype =
+    (*j->env)->GetMethodID(j->env, j->class_class, "getComponentType",
+			   "()Ljava/lang/Class;");
+  j->method_isarray =
+    (*j->env)->GetMethodID(j->env, j->class_class, "isArray", "()Z");
+  j->method_getname =
+    (*j->env)->GetMethodID(j->env, j->class_class, "getName",
+			   "()Ljava/lang/String;");
+  j->method_charat =
+    (*j->env)->GetMethodID(j->env, j->class_string, "charAt", "(I)C");
+
+#ifdef _REENTRANT
+  f_thread_local(0);
+  if(sp[-1].type == T_OBJECT) {
+    j->tl_env = sp[-1].u.object;
+    j->tl_env->refs ++;
+  }
+  pop_n_elems(args+1);
+#else
+  pop_n_elems(args);
+#endif /* _REENTRANT */
+  push_int(0);
+}
+
+static void init_jvm_struct(struct object *o)
+{
+  struct jvm_storage *j = THIS_JVM;
+
+#ifdef SUPPORT_NATIVE_METHODS
+  num_threads++;
+#endif /* SUPPORT_NATIVE_METHODS */
+  j->jvm = NULL;
+  j->classpath_string = NULL;
+  j->class_object = 0;
+  j->class_class = 0;
+  j->class_string = 0;
+  j->class_throwable = 0;
+  j->class_runtimex = 0;
+  j->class_system = 0;
+#ifdef _REENTRANT
+  j->tl_env = NULL;
+#endif /* _REENTRANT */
+}
+
+static void exit_jvm_struct(struct object *o)
+{
+  struct jvm_storage *j = THIS_JVM;
+  JNIEnv *env;
+
+  if(j->jvm != NULL && (env = jvm_procure_env(fp->current_object))) {
+    if(j->class_system)
+      (*env)->DeleteGlobalRef(env, j->class_system);
+    if(j->class_runtimex)
+      (*env)->DeleteGlobalRef(env, j->class_runtimex);
+    if(j->class_throwable)
+      (*env)->DeleteGlobalRef(env, j->class_throwable);
+    if(j->class_string)
+      (*env)->DeleteGlobalRef(env, j->class_string);
+    if(j->class_class)
+      (*env)->DeleteGlobalRef(env, j->class_class);
+    if(j->class_object)
+      (*env)->DeleteGlobalRef(env, j->class_object);
+    jvm_vacate_env(fp->current_object, env);
+  }
+
+  if(j->jvm) {
+    (*j->jvm)->AttachCurrentThread(j->jvm, (void **)&env, NULL);
+    (*j->jvm)->DestroyJavaVM(j->jvm);
+    j->jvm = NULL;
+  }
+  if(j->classpath_string)
+    free_string(j->classpath_string);
+#ifdef _REENTRANT
+  if(j->tl_env != NULL)
+    free_object(j->tl_env);
+#endif /* _REENTRANT */
+#ifdef SUPPORT_NATIVE_METHODS
+  num_threads--;
+#endif /* SUPPORT_NATIVE_METHODS */
+}
+
+#ifdef _REENTRANT
+static void jvm_gc_check(struct object *o)
+{
+  struct jvm_storage *j = THIS_JVM;
+
+  if(j->tl_env)
+    gc_check(j->tl_env);
+}
+
+static void jvm_gc_mark(struct object *o)
+{
+  struct jvm_storage *j = THIS_JVM;
+
+  if(j->tl_env)
+    gc_mark_object_as_referenced(j->tl_env);
+}
+#endif /* _REENTRANT */
+
+
+static void f_get_version(INT32 args)
+{
+  JNIEnv *env;
+  pop_n_elems(args);
+  if((env = jvm_procure_env(fp->current_object))) {
+    push_int((*env)->GetVersion(env));
+    jvm_vacate_env(fp->current_object, env);
+  } else
+    push_int(0);
+}
+
+static void f_find_class(INT32 args)
+{
+  JNIEnv *env;
+  char *cn;
+  jclass c;
+
+  get_all_args("find_class", args, "%s", &cn);
+  if((env = jvm_procure_env(fp->current_object))) {
+    c = (*env)->FindClass(env, cn);
+    pop_n_elems(args);
+    push_java_class(c, fp->current_object, env);
+    jvm_vacate_env(fp->current_object, env);
+  } else {
+    pop_n_elems(args);
+    push_int(0);
+  }
+}
+
+static void f_exception_occurred(INT32 args)
+{
+  JNIEnv *env;
+
+  pop_n_elems(args);
+  if((env = jvm_procure_env(fp->current_object))) {
+    push_java_throwable((*env)->ExceptionOccurred(env),
+			fp->current_object, env);
+    jvm_vacate_env(fp->current_object, env);
+  } else
+    push_int(0);
+}
+
+static void f_exception_describe(INT32 args)
+{
+  JNIEnv *env;
+
+  pop_n_elems(args);
+  if((env = jvm_procure_env(fp->current_object))) {
+    (*env)->ExceptionDescribe(env);
+    jvm_vacate_env(fp->current_object, env);
+  }
+  push_int(0);
+}
+
+static void f_exception_clear(INT32 args)
+{
+  JNIEnv *env;
+
+  pop_n_elems(args);
+  if((env = jvm_procure_env(fp->current_object))) {
+    (*env)->ExceptionClear(env);
+    jvm_vacate_env(fp->current_object, env);
+  }
+  push_int(0);
+}
+
+static void f_javafatal(INT32 args)
+{
+  JNIEnv *env;
+  char *msg;
+
+  get_all_args("fatal", args, "%s", &msg);
+  if((env = jvm_procure_env(fp->current_object))) {
+    (*env)->FatalError(env, msg);
+    jvm_vacate_env(fp->current_object, env);
+  }
+  pop_n_elems(args);
+  push_int(0);
+}
+
+
+#endif /* HAVE_JAVA */
+
+void pike_module_init(void)
+{
+#ifdef HAVE_JAVA
+  struct svalue prog;
+  prog.type = T_PROGRAM;
+  prog.subtype = 0;
+
+  start_new_program();
+  ADD_STORAGE(struct jobj_storage);
+  add_function("cast", f_jobj_cast, "function(string:mixed)", 0);
+  add_function("`==", f_jobj_eq, "function(mixed:int)", 0);
+  add_function("__hash", f_jobj_hash, "function(:int)", 0);
+  add_function("is_instance_of", f_jobj_instance, "function(object:int)", 0);
+  set_init_callback(init_jobj_struct);
+  set_exit_callback(exit_jobj_struct);
+  set_gc_check_callback(jobj_gc_check);
+  set_gc_mark_callback(jobj_gc_mark);
+  jobj_program = end_program();
+  jobj_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+
+  start_new_program();
+  prog.u.program = jobj_program;
+  do_inherit(&prog, 0, NULL);
+  add_function("super_class", f_super_class, "function(:object)", 0);
+  add_function("is_assignable_from", f_is_assignable_from,
+	       "function(object:int)", 0);
+  add_function("throw_new", f_throw_new, "function(string:void)", 0);
+  add_function("alloc", f_alloc, "function(:object)", 0);
+  add_function("get_method", f_get_method, "function(string,string:object)", 0);
+  add_function("get_static_method", f_get_static_method, "function(string,string:object)", 0);
+  add_function("get_field", f_get_field, "function(string,string:object)", 0);
+  add_function("get_static_field", f_get_static_field, "function(string,string:object)", 0);
+#ifdef SUPPORT_NATIVE_METHODS
+  add_function("register_natives", f_register_natives, "function(array(array(string|function)):object)", 0);
+#endif /* SUPPORT_NATIVE_METHODS */
+  jclass_program = end_program();
+  jclass_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+
+  start_new_program();
+  do_inherit(&prog, 0, NULL);
+  add_function("throw", f_javathrow, "function(:void)", 0);
+  jthrowable_program = end_program();
+  jthrowable_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+
+  start_new_program();
+  do_inherit(&prog, 0, NULL);
+  jarray_stor_offs = ADD_STORAGE(struct jarray_storage);
+  add_function("_sizeof", f_javaarray_sizeof, "function(:int)", 0);
+  add_function("`[]", f_javaarray_getelt, "function(int:mixed)", 0);
+  add_function("_indices", f_javaarray_indices, "function(:array(int))", 0);
+  add_function("_values", f_javaarray_values, "function(:array(mixed))", 0);
+  jarray_program = end_program();
+  jarray_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+
+  start_new_program();
+  ADD_STORAGE(struct method_storage);
+  add_function("create", f_method_create,
+	       "function(string,string,object:void)", 0);
+  add_function("`()", f_call_static, "function(mixed...:mixed)", 0);
+  set_init_callback(init_method_struct);
+  set_exit_callback(exit_method_struct);
+  set_gc_check_callback(method_gc_check);
+  set_gc_mark_callback(method_gc_mark);
+  static_method_program = end_program();
+  static_method_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+
+  start_new_program();
+  ADD_STORAGE(struct method_storage);
+  add_function("create", f_method_create,
+	       "function(string,string,object:void)", 0);
+  add_function("`()", f_call_virtual, "function(object,mixed...:mixed)", 0);
+  add_function("call_nonvirtual", f_call_nonvirtual,
+	       "function(object,mixed...:mixed)", 0);
+  set_init_callback(init_method_struct);
+  set_exit_callback(exit_method_struct);
+  set_gc_check_callback(method_gc_check);
+  set_gc_mark_callback(method_gc_mark);
+  method_program = end_program();
+  method_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+
+  start_new_program();
+  ADD_STORAGE(struct field_storage);
+  add_function("create", f_field_create,
+	       "function(string,string,object:void)", 0);
+  add_function("set", f_field_set, "function(object,mixed:mixed)", 0);
+  add_function("get", f_field_get, "function(object:mixed)", 0);
+  set_init_callback(init_field_struct);
+  set_exit_callback(exit_field_struct);
+  set_gc_check_callback(field_gc_check);
+  set_gc_mark_callback(field_gc_mark);
+  field_program = end_program();
+  field_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+
+  start_new_program();
+  ADD_STORAGE(struct field_storage);
+  add_function("create", f_field_create,
+	       "function(string,string,object:void)", 0);
+  add_function("set", f_static_field_set, "function(mixed:mixed)", 0);
+  add_function("get", f_static_field_get, "function(:mixed)", 0);
+  set_init_callback(init_field_struct);
+  set_exit_callback(exit_field_struct);
+  set_gc_check_callback(field_gc_check);
+  set_gc_mark_callback(field_gc_mark);
+  static_field_program = end_program();
+  static_field_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+
+#ifdef SUPPORT_NATIVE_METHODS
+  start_new_program();
+  ADD_STORAGE(struct natives_storage);
+  add_function("create", f_natives_create,
+	       "function(array(array(string|function)),object:void)", 0);
+  set_init_callback(init_natives_struct);
+  set_exit_callback(exit_natives_struct);
+  set_gc_check_callback(natives_gc_check);
+  set_gc_mark_callback(natives_gc_mark);
+  natives_program = end_program();
+  natives_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+#endif /* SUPPORT_NATIVE_METHODS */
+
+#ifdef _REENTRANT
+  start_new_program();
+  ADD_STORAGE(struct att_storage);
+  add_function("create", f_att_create, "function(object:void)", 0);
+  set_init_callback(init_att_struct);
+  set_exit_callback(exit_att_struct);
+  set_gc_check_callback(att_gc_check);
+  set_gc_mark_callback(att_gc_mark);
+  attachment_program = end_program();
+  attachment_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+#endif /* _REENTRANT */
+
+  start_new_program();
+  ADD_STORAGE(struct jvm_storage);
+  add_function("create", f_create, "function(string|void:void)", 0);
+  add_function("get_version", f_get_version, "function(:int)", 0);
+  add_function("find_class", f_find_class, "function(string:object)", 0);
+  add_function("exception_occurred", f_exception_occurred,
+	       "function(:object)", 0);
+  add_function("exception_describe", f_exception_describe,
+	       "function(:void)", 0);
+  add_function("exception_clear", f_exception_clear, "function(:void)", 0);
+  add_function("fatal", f_javafatal, "function(string:void)", 0);
+  set_init_callback(init_jvm_struct);
+  set_exit_callback(exit_jvm_struct);
+#ifdef _REENTRANT
+  set_gc_check_callback(jvm_gc_check);
+  set_gc_mark_callback(jvm_gc_mark);
+#endif /* _REENTRANT */
+  add_program_constant("jvm", jvm_program = end_program(), 0);
+  jvm_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
+#endif /* HAVE_JAVA */
+}
+
+void pike_module_exit(void)
+{
+#ifdef HAVE_JAVA
+  if(jarray_program) {
+    free_program(jarray_program);
+    jarray_program=NULL;
+  }
+  if(jthrowable_program) {
+    free_program(jthrowable_program);
+    jthrowable_program=NULL;
+  }
+  if(jclass_program) {
+    free_program(jclass_program);
+    jobj_program=NULL;
+  }
+  if(jobj_program) {
+    free_program(jobj_program);
+    jobj_program=NULL;
+  }
+  if(static_field_program) {
+    free_program(static_field_program);
+    static_field_program=NULL;
+  }
+  if(field_program) {
+    free_program(field_program);
+    field_program=NULL;
+  }
+  if(static_method_program) {
+    free_program(static_method_program);
+    static_method_program=NULL;
+  }
+  if(method_program) {
+    free_program(method_program);
+    method_program=NULL;
+  }
+  if(natives_program) {
+    free_program(natives_program);
+    natives_program=NULL;
+  }
+  if(attachment_program) {
+    free_program(attachment_program);
+    attachment_program=NULL;
+  }
+  if(jvm_program) {
+    free_program(jvm_program);
+    jvm_program=NULL;
+  }
+#endif /* HAVE_JAVA */
+}
+
diff --git a/src/modules/Java/module.pmod.in.in b/src/modules/Java/module.pmod.in.in
new file mode 100644
index 0000000000..a9e424c1d9
--- /dev/null
+++ b/src/modules/Java/module.pmod.in.in
@@ -0,0 +1,574 @@
+
+#if @JAVA_AVAILABLE@
+
+inherit @module@;
+
+object machine = jvm();
+
+static string make_sig(object o, mapping(string:mixed) info, object|void p)
+{
+  if(p)
+    return "("+Array.map(values(p), make_sig, info)*""+")"+make_sig(o, info);
+  else {
+    if(info->classisprimitive_method(o))
+      return info->primitives[o];
+    else if(info->classisarray_method(o))
+      return replace((string)info->classgetname_method(o), ".", "/");
+    else
+      return "L"+replace((string)info->classgetname_method(o), ".", "/")+";";
+  }
+}
+
+static void check_exception(mapping(string:mixed) info)
+{
+  object e = info->jvm->exception_occurred();
+  if(e) {
+    object sw = info->stringwriter_class->alloc();
+    info->stringwriter_init(sw);
+    object pw = info->printwriter_class->alloc();
+    info->printwriter_init(pw, sw);
+    info->throwable_printstacktrace(e, pw);
+    info->printwriter_flush(pw);
+    info->jvm->exception_clear();
+    array bt = backtrace();
+    throw(({(string)sw, bt[..sizeof(bt)-3]}));
+  }
+}
+
+static mixed wrap_result(mixed x, mapping(string:mixed) info)
+{
+  check_exception(info);
+  if(objectp(x)) {
+    if(x->_values) {
+      return jarray(x, info);
+    } else {
+      object cls = info->getclass_method(x);
+      return jobject(info->classes[cls] || jclass(cls, info), x, info);
+    }
+  } else
+    return x;
+}
+
+static array unwrap_args(array a)
+{
+  return Array.map(a, lambda(mixed x) {
+			return (objectp(x)? x->_obj||x:x);
+		      });
+}
+
+static class jmethod {
+
+  static object obj;
+  static mapping(string:mixed) info;
+  static mapping(string:object) protos;
+
+  static int is_applicable(string sig, array(mixed) args)
+  {
+    int sp=0, ap=0, na=sizeof(args);
+    if(sig[sp++]!='(')
+      return 0;
+    while(ap<na) {
+      switch(sig[sp++]) {
+      case 'B':
+      case 'C':
+      case 'I':
+      case 'J':
+      case 'S':
+      case 'Z':
+	if(!intp(args[ap]))
+	  return 0;
+	break;
+      case 'D':
+      case 'F':
+	if((!intp(args[ap])) && (!floatp(args[ap])))
+	  return 0;
+	break;
+      case '[':
+	if((!arrayp(args[ap])) && (!objectp(args[ap])))
+	  return 0;
+	while(sig[sp]=='[')
+	  sp++;
+	if(sig[sp++]=='L')
+	  while(sig[sp++]!=';')
+	    ;
+	break;
+      case 'L':
+	if((!objectp(args[ap])) && (!stringp(args[ap])))
+	  return 0;
+	while(sig[sp++]!=';')
+	  ;
+	break;
+      default:
+	return 0;
+	break;
+      }
+      ap++;
+    }
+    return sig[sp]==')';
+  }
+
+  static object select_proto(array(mixed) args)
+  {
+    array(string) applicable =
+      Array.filter(indices(protos), is_applicable, args);
+
+    if(sizeof(applicable)==1)
+      return protos[applicable[0]];
+
+    if(!sizeof(applicable))
+      throw(({"No method signatures are applicable.  Resolution incomplete.\n",
+	      backtrace()}));
+
+    throw(({"Multiple method signatures apply.  Resolution incomplete.\n",
+	    backtrace()}));
+  }
+
+  mixed `()(mixed ... args)
+  {
+    object mm;
+    if(sizeof(protos)==1)
+      mm = values(protos)[0];
+    else
+      mm = select_proto(args);
+    if(obj)
+      return wrap_result(mm(obj, @unwrap_args(args)), info);
+    else
+      return wrap_result(mm(@unwrap_args(args)), info);
+  }
+
+  void create(mapping(string:mixed) i, object o, mapping(string:object) p)
+  {
+    info = i;
+    obj = o;
+    protos = p;
+  }
+
+  void add_proto(string p, object o)
+  {
+    protos[p] = o;
+  }
+
+  object for_object(object o)
+  {
+    return jmethod(info, o, protos);
+  }
+
+  object for_proto(string p)
+  {
+    return protos[p];
+  }
+
+};
+
+static class jobject {
+
+  static object obj;
+  static object cls;
+  mapping(string:mixed) info;
+
+  static mixed _wrap_result(mixed x)
+  {
+    return cls->_wrap_result(x);
+  }
+
+  mixed cast(mixed ... args)
+  {
+    return obj->cast(@unwrap_args(args));
+  }
+
+  mixed `[](string n)
+  {
+    object f = cls->_fields[n];
+    if(f)
+      return _wrap_result(f->get(obj));
+    object m = cls->_methods[n];
+    if(m)
+      return m->for_object(obj);
+    return ([])[0];
+  }
+
+  array(string) _indices()
+  {
+    return indices(cls->_fields)|indices(cls->_methods);
+  }
+
+  array(mixed) _values()
+  {
+    return rows(this_object(), _indices());
+  }
+
+  static object method(string n, string sig)
+  {
+    object m = cls->_methods[n];
+    if(m) {
+      object m2 = m->for_proto(sig);
+      if(m2)
+	return jmethod(info, obj, ([sig:m2]));
+    }
+    return ([])[0];
+  }
+
+  mixed `->(string n)
+  {
+    if(sizeof(n) && n[0]=='_')
+      switch(n) {
+      case "_method":
+	return method;
+      case "_obj":
+	return obj;
+      default:
+	return `[](n);
+      } else
+	return `[](n);
+  }
+
+  void create(object c, object o, mapping(string:mixed) i)
+  {
+    obj = o;
+    cls = c;
+    info = i;
+  }
+
+};
+
+static class jarray {
+
+  static object obj;
+  mapping(string:mixed) info;
+
+  mixed cast(mixed ... args)
+  {
+    return obj->cast(@unwrap_args(args));
+  }
+
+  mixed `[](int ... n)
+  {
+    return wrap_result(obj->`[](@n), info);
+  }
+
+  array(mixed) _indices()
+  {
+    return indices(obj);
+  }
+
+  array(mixed) _values()
+  {
+    return Array.map(values(obj), wrap_result, info);
+  }
+
+  int _sizeof()
+  {
+    return sizeof(obj);
+  }
+
+  mixed `->(string n)
+  {
+    if(n == "length")
+      return sizeof(obj);
+    else if(n == "_obj")
+      return obj;
+    else
+      return ([])[0];
+  }
+
+  void create(object o, mapping(string:mixed) i)
+  {
+    obj = o;
+    info = i;
+  }
+
+};
+
+static class jconstructor {
+
+  static object cls, con;
+  static mapping(string:mixed) info;
+  
+  object `()(mixed ... args)
+  {
+    object o = cls->_alloc();
+    if(!o)
+      return 0;
+    con(o, @unwrap_args(args));
+    return jobject(cls, o, info);
+  }
+
+  void create(object c, object o, mapping(string:mixed) i)
+  {
+    cls = c;
+    con = o;
+    info = i;
+  }
+
+}
+
+static class jclass {
+
+  static object obj;
+  static mapping(string:object) fields = ([]);
+  static mapping(string:object) static_fields = ([]);
+  static mapping(string:object) methods = ([]);
+  static mapping(string:object) static_methods = ([]);
+  static object constructor;
+  static mapping(string:mixed) info;
+
+  static mixed _wrap_result(mixed x)
+  {
+    return wrap_result(x, info);
+  }
+
+  void create(object o, mapping(string:mixed) i)
+  {
+    obj = o;
+    info = i;
+    info->classes[o] = this_object();
+    foreach(values(i->getfields_method(o)), object f)
+      if((i->fieldgetmodifiers_method(f)) & i->modifier_static) {
+	string name = (string)i->fieldgetname_method(f);
+	static_fields[name] =
+	  o->get_static_field(name, make_sig(i->fieldgettype_method(f), i));
+      } else {
+	string name = (string)i->fieldgetname_method(f);
+	fields[name] =
+	  o->get_field(name, make_sig(i->fieldgettype_method(f), i));
+      }
+    foreach(values(i->getmethods_method(o)), object m)
+      if((i->methodgetmodifiers_method(m)) & i->modifier_static) {
+	string name = (string)i->methodgetname_method(m);
+	string sig = make_sig(i->methodgetreturntype_method(m), i,
+			      i->methodgetparametertypes_method(m));
+	object oo = o->get_static_method(name, sig);
+	if(static_methods[name])
+	  static_methods[name]->add_proto(sig, oo);
+	else
+	  static_methods[name] = jmethod(info, 0, ([sig:oo]));
+      } else {
+	string name = (string)i->methodgetname_method(m);
+	string sig = make_sig(i->methodgetreturntype_method(m), i,
+			      i->methodgetparametertypes_method(m));
+	object oo = o->get_method(name, sig);
+	if(methods[name])
+	  methods[name]->add_proto(sig, oo);
+	else
+	  methods[name] = jmethod(info, 0, ([sig:oo]));
+      }
+    foreach(values(i->getconstructors_method(o)), object c) {
+      string sig = make_sig(i->voidtype, i,
+			    i->constructorgetparametertypes_method(c));
+      object oo = o->get_method("<init>", sig);
+      if(constructor)
+	constructor->add_proto(sig, oo);
+      else
+	constructor = jmethod(info, 0, ([sig:oo]));
+    }
+  }
+
+  mixed `[](string n)
+  {
+    object f = static_fields[n];
+    if(f)
+      return _wrap_result(f->get());
+    return static_methods[n];
+  }
+
+  array(string) _indices()
+  {
+    return indices(static_fields)|indices(static_methods);
+  }
+
+  array(mixed) _values()
+  {
+    return rows(this_object(), _indices());
+  }
+
+  static object method(string n, string sig)
+  {
+    object m = static_methods[n];
+    if(m) {
+      object m2 = m->for_proto(sig);
+      if(m2)
+	return jmethod(info, 0, ([sig:m2]));
+    }
+    return 0;
+  }
+
+  static object make_constructor(string sig)
+  {
+    if(sizeof(sig) && sig[-1]!='V')
+      sig += "V";
+    if(constructor) {
+      object c = constructor->for_proto(sig);
+      if(c)
+	return jconstructor(this_object(), c, info);
+    }
+    return 0;
+  }
+
+  mixed `->(string n)
+  {
+    if(sizeof(n) && n[0]=='_')
+      switch(n) {
+      case "_fields":
+	return fields;
+      case "_static_fields":
+	return static_fields;
+      case "_methods":
+	return methods;
+      case "_static_methods":
+	return static_methods;
+      case "_wrap_result":
+	return _wrap_result;
+      case "_method":
+	return method;
+      case "_constructor":
+	return make_constructor;
+      case "_alloc":
+	return obj->alloc;
+      case "_register_natives":
+	return obj->register_natives;
+      default:
+	return `[](n);
+      } else
+	return `[](n);
+  }
+
+  object `()(mixed ... args)
+  {
+    object o = obj->alloc();
+    if(!o)
+      return 0;
+    if(constructor)
+      constructor->for_object(o)(@unwrap_args(args));
+    return jobject(this_object(), o, info);
+  }
+
+};
+
+static class package {
+
+  static mapping(string:mixed) info;
+  static mapping(string:object) subpackages;
+  static string name;
+  static object pkg;
+
+  object `[](string n)
+  {
+    object p = subpackages[n];
+    if(p)
+      return p;
+    if(zero_type(p)) {
+      p = info->jvm->find_class(name==""?n:replace(name, ".", "/")+"/"+n);
+      if(info->jvm->exception_occurred())
+	check_exception(info);
+        /* info->jvm->exception_clear(); */
+      if(p)
+	p = info->classes[p] || jclass(p, info);
+      return p;
+    }
+    p = object_program(this_object())((name==""?n:name+"."+n), info);
+    if(p)
+      subpackages[n] = p;
+    return p;
+  }
+
+  void create(string n, mapping(string:mixed) i)
+  {
+    name = n;
+    info = i;
+    if(!i->getpackage_method) {
+      object cls = i->jvm->find_class("java/lang/Package");
+      i->getpackage_method =
+	cls->get_static_method("getPackage",
+			       "(Ljava/lang/String;)Ljava/lang/Package;");
+      object gap =
+	cls->get_static_method("getPackages",
+			       "()[Ljava/lang/Package;");
+      i->packages =
+	Array.map(gap(), lambda(object o, object nm) {
+			   return (string)nm(o);
+			 }, cls->get_method("getName","()Ljava/lang/String;"));
+
+      cls = i->jvm->find_class("java/lang/Object");
+      i->getclass_method = cls->get_method("getClass",
+					   "()Ljava/lang/Class;");
+
+      cls = i->jvm->find_class("java/lang/Class");
+      i->getfields_method = cls->get_method("getFields",
+					    "()[Ljava/lang/reflect/Field;");
+      i->getmethods_method = cls->get_method("getMethods",
+					     "()[Ljava/lang/reflect/Method;");
+      i->getconstructors_method =
+	cls->get_method("getConstructors",
+			"()[Ljava/lang/reflect/Constructor;");
+
+      i->classisprimitive_method = cls->get_method("isPrimitive", "()Z");
+      i->classisarray_method = cls->get_method("isArray", "()Z");
+      i->classgetname_method = cls->get_method("getName",
+					       "()Ljava/lang/String;");
+
+      cls = i->jvm->find_class("java/lang/reflect/Field");
+      i->fieldgetname_method = cls->get_method("getName",
+					       "()Ljava/lang/String;"); 
+      i->fieldgettype_method = cls->get_method("getType",
+					       "()Ljava/lang/Class;"); 
+      i->fieldgetmodifiers_method = cls->get_method("getModifiers", "()I");
+
+      cls = i->jvm->find_class("java/lang/reflect/Method");
+      i->methodgetname_method = cls->get_method("getName",
+						"()Ljava/lang/String;"); 
+      i->methodgetreturntype_method = cls->get_method("getReturnType",
+						      "()Ljava/lang/Class;"); 
+      i->methodgetparametertypes_method =
+	cls->get_method("getParameterTypes", "()[Ljava/lang/Class;"); 
+      i->methodgetmodifiers_method = cls->get_method("getModifiers", "()I");
+
+      cls = i->jvm->find_class("java/lang/reflect/Constructor");
+      i->constructorgetparametertypes_method =
+	cls->get_method("getParameterTypes", "()[Ljava/lang/Class;");
+
+      cls = i->jvm->find_class("java/lang/reflect/Modifier");
+      object fld = cls->get_static_field("STATIC", "I");
+      i->modifier_static = fld->get();
+
+      i->primitives =
+	mkmapping(Array.map(({"Byte", "Character", "Double", "Float",
+			      "Integer", "Long", "Short", "Boolean", "Void"}),
+			    lambda(string n, function(string:object) fc) {
+			      return fc("java/lang/"+n)->
+				get_static_field("TYPE", "Ljava/lang/Class;")->
+				get();
+			    }, i->jvm->find_class),
+		  ({"B", "C", "D", "F", "I", "J", "S", "Z", "V"}));
+
+      i->classes = ([]);
+
+      i->voidtype = search(i->primitives, "V");
+
+      cls = i->stringwriter_class = i->jvm->find_class("java/io/StringWriter");
+      i->stringwriter_init = cls->get_method("<init>", "()V");
+
+      cls = i->printwriter_class = i->jvm->find_class("java/io/PrintWriter");
+      i->printwriter_init = cls->get_method("<init>", "(Ljava/io/Writer;)V");
+      i->printwriter_flush = cls->get_method("flush", "()V");
+
+      cls = i->jvm->find_class("java/lang/Throwable");
+      i->throwable_printstacktrace =
+	cls->get_method("printStackTrace", "(Ljava/io/PrintWriter;)V");
+
+    }
+    pkg = i->getpackage_method(name);
+    array(string) subs = Array.map(i->packages, lambda(string p, string n){
+						  string sn;
+						  if(sscanf(p,n+"%[^.]",sn)==1)
+						    return sn;
+						  else
+						    return 0;
+						}, (name==""?n:n+"."))-({0});
+    subpackages = mkmapping(subs, allocate(sizeof(subs)));
+  }
+
+};
+
+object pkg = package("", (["jvm":machine]));
+
+#endif
diff --git a/src/modules/Java/testsuite.in b/src/modules/Java/testsuite.in
new file mode 100644
index 0000000000..03c3851977
--- /dev/null
+++ b/src/modules/Java/testsuite.in
@@ -0,0 +1,8 @@
+// $Id: testsuite.in,v 1.1 1999/03/02 22:07:13 marcus Exp $
+
+test_true([[objectp(Java)]])
+test_true([[objectp(Java.pkg)]])
+test_true([[objectp(Java.pkg.java.lang.String)]])
+test_eq([[(string)Java.pkg.java.lang.String()->concat("FOO")->toLowerCase()]],
+	"foo")
+test_eq([[(string)Java.pkg.java.lang.Character(4711)]],"\11147")
-- 
GitLab