Milán Major

netscape-revival

1 branch
Code

sun-java/runtime/jrijdk.c

/* -*- Mode: C; tab-width: 4; -*- */
/*******************************************************************************
 * Java Runtime Interface -- JDK Implementation
 * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved.
 ******************************************************************************/

/*
** Define this first so that the first file that pulls in jri_md.h
** defines the GUIDs for JRI:
*/
#define INITGUID

#include "oobj.h"
#include "interpreter.h"
#include "javaThreads.h"
#include "exceptions.h"
#include "opcodes.h"
#include "java_lang_Throwable.h"
#include "java_lang_Thread.h"
#include "java_lang_ThreadGroup.h"
#include "java_lang_ClassLoader.h"
#include "jio.h"
#include "utf.h"
#if defined(NETSCAPE) && defined(XP_UNIX)
#include "prmon.h"
#endif

/*
** Don't include jriext.h first because we need to make sure the proper
** symbols are defined to get the right long long definitions. Since
** jri.h (and consequently jri.h and jri_md.h) are stand-alone headers,
** this doesn't happen automatically.
*/
#include "jriext.h"

#include <sys/stat.h>
#include <fcntl.h>

extern HClass* java_lang_Object_getClass(HObject *p); /* from object.c */

/*******************************************************************************
 * RefTable
 ******************************************************************************/

#define GLOBAL_TABLE_INITIAL_SIZE	256
#define GLOBAL_TABLE_INCREMENT_SIZE	256

typedef JHandle** RefCell;

static jbool JRI_CALLBACK
RefTable_init(RefTable* table)
{
    RefCell elements;
    elements = (RefCell)sysMalloc(GLOBAL_TABLE_INITIAL_SIZE
                                  * sizeof(JHandle*));
    if (elements == NULL)
        return JRIFalse;
	
    table->element = elements;
    table->size = GLOBAL_TABLE_INITIAL_SIZE;
    table->top = 0;
	
    return JRITrue;
}

/*
** JHandle* RefTable_deref(RefTable* table, jref ref)
**
** This dereferencing operator doesn't check for NULL.
*/
#define RefTable_deref(table, ref)		(((table)->element[((jsize)ref)-1]))

#define RefTable_refIsValid(table, ref)	\
	sysAssert(/*(uint32_t)(ref)-1 >= 0 &&*/ (uint32_t)(ref)-1 < (table)->top)
		
#ifdef CHECK_ON_PUSH
/* This version checks on every push, potentially growing the table */

/*
** jref RefTable_push(RefTable* table, JHandle handle) 
**
** Table should be a variable.
*/
#define RefTable_push(table, handle)     \
	((((table)->top >= (table)->size)    \
	  ? RefTable_grow(table) : JRITrue) \
	 ? RefTable_push1(table, handle)     \
     : NULL)                             \

/*
** jref RefTable_push1(RefTable* table, JHandle* handle) 
**
** This pushes without checking whether to grow the table. Table should
** be a variable.
*/
#define RefTable_push1(table, handle)           \
	(sysAssert((table)->top < (table)->size),   \
	 (table)->element[(table)->top] = (handle), \
	 (jref)(++(table)->top))                    \
    
static jbool JRI_CALLBACK
RefTable_grow(RefTable* table) 
{
	if (table->top >= table->size) {
		RefCell elements = (RefCell)
			sysRealloc(table->element, 
					   (table->size + GLOBAL_TABLE_INCREMENT_SIZE)
					   * sizeof(JHandle*));
		if (elements == NULL) {
			return JRIFalse;
		}
		table->element = elements;
		table->size += GLOBAL_TABLE_INCREMENT_SIZE;
	}
	return JRITrue;
}

#else /* !CHECK_ON_PUSH */

/*
** jref RefTable_push(RefTable* table, JHandle* handle) 
**
** This pushes without checking whether to grow the table. Table should
** be a variable.
*/
#define RefTable_push(table, handle)            \
	(sysAssert((table)->top < (table)->size),   \
	 (table)->element[(table)->top] = (handle), \
	 (jref)(++(table)->top))                    \
	
/*
** jbool RefTable_ensure(RefTable* table, jsize refCount)
**
** Table and count should be variables.
*/
#define RefTable_ensure(table, refCount)		  \
	(((table)->top + refCount >= (table)->size)	  \
	 ? RefTable_grow(table, refCount) : JRITrue) \

#ifndef MAX
#define MAX(x, y)	(((x) > (y)) ? (x) : (y))
#endif

static jbool JRI_CALLBACK
RefTable_grow(RefTable* table, jsize refCount) 
{
    if (table->top + refCount >= table->size) {
		RefCell elements = (RefCell)
			sysRealloc(table->element, 
					   (size_t)(table->size + 
						MAX(GLOBAL_TABLE_INCREMENT_SIZE, refCount))
					   * sizeof(JHandle*));
		if (elements == NULL) {
			return JRIFalse;
		}
		table->element = elements;
		table->size += GLOBAL_TABLE_INCREMENT_SIZE;
    }
    return JRITrue;
}

#endif  /* !CHECK_ON_PUSH */

/*
** JHandle* RefTable_pop(RefTable* table)
*/
#define RefTable_pop(table)		((table)->element[--(table)->top])
	
/*
** JHandle* RefTable_top(RefTable* table)
*/
#define RefTable_top(table)		((table)->element[(table)->top - 1])
	
/*
** jrefFrame RefTable_beginFrame(RefTable* table)
*/
#define RefTable_beginFrame(table)	((table)->top)
	
/*
** void RefTable_endFrame(RefTable* table, int32 frame)
*/
#define RefTable_endFrame(table, frame)	((table)->top = (frame))

#define EMPTY		((JHandle*)-1)	/* must not be == NULL */

static jref JRI_CALLBACK
RefTable_insert(RefTable* table, JHandle* handle)
{
    RefCell elements = table->element;
    uint32_t top = table->top;
    int32_t i;
    /* First, just push (if there's room). The table's allocated anyway. */
    if (top < table->size) {
#if CHECK_ON_PUSH
		return RefTable_push1(table, handle);
#else /* !CHECK_ON_PUSH */
		return RefTable_push(table, handle);
#endif /* !CHECK_ON_PUSH */
    }
    /*
    ** Next, look for an already deleted (empty) cell. Assume youngest
    ** die young.
    */
    for (i = (int32_t) top - 1; i >= 0; i--) {
		if (elements[i] == EMPTY) {
			elements[i] = handle;
			return (jref)i;
		}
    }
    /* Finally, if the table's completely full, let it grow. */
#if CHECK_ON_PUSH
    return RefTable_push(table, handle);
#else /* !CHECK_ON_PUSH */
    RefTable_ensure(table, 1);
    return RefTable_push(table, handle);
#endif /* !CHECK_ON_PUSH */
}

/*
** void RefTable_set(RefTable* table, RefCell ref, JHandle* value)
*/
#define RefTable_set(table, ref, value)	\
	(RefTable_refIsValid(table, ref), RefTable_deref(table, ref) = (value))

/*
** void RefTable_drop(RefTable* table, RefCell ref)
*/
#define RefTable_drop(table, ref)		RefTable_set(table, ref, EMPTY)

#if 0
static int
RefTable_isInTable(RefTable* table, RefCell ref)
{
    RefCell elements = table->element;
    RefCell index = (RefCell)ref;
    return elements <= index && index < (elements + table->top);
}
#endif

/*
** bool_t RefTable_isLocation(RefCell ref)
*/
#define RefTable_isLocation(ref)	((ref) != NULL)

/*
** JHandle* RefTable_get(RefTable* table, RefCell ref)
*/
#define RefTable_get(table, ref)									 \
	(RefTable_isLocation(ref)										 \
	 ? (RefTable_refIsValid(table, ref), RefTable_deref(table, ref)) \
	 : (JHandle*)(ref))												 \

#ifdef DEBUG
void
RefTable_describe(RefTable* refTable) 
{
	int i;
	fprintf(stderr, "RefTable 0x%p:\n", refTable);
	for (i = 0; i < refTable->top; i++) {
	    extern ClassClass* classJavaLangClass;
	    JHandle* h = refTable->element[i];
	    HClass* hc;
	    if (h == NULL || h == (JHandle*)-1) 
		continue;
	    hc = java_lang_Object_getClass(h);
	    if (unhand(hc) == classJavaLangClass) {
		fprintf(stderr,
			"\t%d. %s @ 0x%p (%s)\n", i, unhand(hc)->name, h,
			unhand((HClass*)h)->name);
	    }
	    else {
		fprintf(stderr,
			"\t%d. %s @ 0x%p\n", i, unhand(hc)->name, h);
	    }
	}
}
#endif /* DEBUG */

/*******************************************************************************
 * Manipulating Interfaces
 ******************************************************************************/

#define NaEnv2EE(env)		\
	((ExecEnv*)((char*)(env) - offsetof(ExecEnv, nativeInterface)))
#define RtEnv2EE(env)		\
	((ExecEnv*)((char*)(env) - offsetof(ExecEnv, runtimeInterface)))
#define RfEnv2EE(env)		\
	((ExecEnv*)((char*)(env) - offsetof(ExecEnv, reflectionInterface)))
#define DbEnv2EE(env)		\
	((ExecEnv*)((char*)(env) - offsetof(ExecEnv, debuggerInterface)))
#define CpEnv2EE(env)		\
	((ExecEnv*)((char*)(env) - offsetof(ExecEnv, compilerInterface)))
#define ExEnv2EE(env)		\
	((ExecEnv*)((char*)(env) - offsetof(ExecEnv, exprInterface)))

#define EE2NaEnv(ee)		(&(ee)->nativeInterface)
#define EE2RtEnv(ee)		(&(ee)->runtimeInterface)
#define EE2RfEnv(ee)		(&(ee)->reflectionInterface)
#define EE2DbEnv(ee)		(&(ee)->debuggerInterface)
#define EE2CpEnv(ee)		(&(ee)->compilerInterface)
#define EE2ExEnv(ee)		(&(ee)->exprInterface)

/*******************************************************************************
 * Local References
 ******************************************************************************/

#if 0 /* Last minute decision -- no non-conservative gc. */

#define JRIEnv_ref(env, handle)                  \
	RefTable_push(&Env2EE(env)->refStack, handle) \

#define JRIEnv_deref(env, ref)                             \
	((JHandle*)RefTable_get(&Env2EE(env)->refStack, ref)) \

#else

/* Local references in the jdk are just JHandles */

#define JRIEnv_ref(env, handle)        ((jref)handle)
#define JRIEnv_deref(env, ref)         ((JHandle*)ref)

#endif

/*******************************************************************************
 * General Stuff
 ******************************************************************************/

/* This implementation only allows one: */
extern const JRIRuntimeInterface jri_RuntimeInterface;
JRIRuntimeInstance jriRuntimeInstance = &jri_RuntimeInterface;
#define jriRuntime		&jriRuntimeInstance

extern ClassClass* classJavaLangClass;
extern ClassClass* classJavaLangString;

static ExecEnv _defaultEE;
static jbool jdkInitialized = (jbool)JRIFalse;
static ClassClass* classNullPointerException;
static ClassClass* classThrowable;

static ClassClass* JRI_CALLBACK
jri_Ref2Class(ExecEnv* ee, jref clazz)
{
    JHandle* handle = JRIEnv_deref(ee, clazz);
    if (handle == NULL) {
		SignalError(ee, JAVAPKG "NullPointerException", NULL);
		return NULL;
    }
	if (!is_instance_of(handle, classJavaLangClass, ee)) {
		SignalError(ee, JAVAPKG "IllegalAccessError",
					"native method specified something that wasn't a class");
		return NULL;
    }
    return unhand((HClass*)handle);
}

static struct fieldblock* JRI_CALLBACK
jri_FindFieldBlock(ExecEnv* ee, ClassClass* cb, hash_t fieldID, jsize *index)
{
	struct fieldblock** fields;
	struct fieldblock* fb = NULL;
    int i, nFields;

 	if (makeslottable(cb) == NULL)
		return NULL;
    nFields = cbSlotTableSize(cb);
	fields = cbSlotTable(cb);
    for (i = nFields - 1; i >= 0; i--) {
		fb = fields[i];
    	if (fb->ID == fieldID)
			break;
	}
	if (i == -1) {
		SignalError(ee, JAVAPKG "NoSuchFieldError",
					"native method specified an invalid field");
		return NULL;
    }
    else {
		if (index != NULL)
			*index = (jsize)i;
		return fb;
    }
}

#ifdef XP_MAC
#ifndef DEBUG
#pragma global_optimizer on 
#pragma optimization_level 2 
#endif
#endif /* XP_MAC */

static struct methodblock* JRI_CALLBACK
jri_GetMethodBlock(ExecEnv* ee, ClassClass* cb, jint methodID,
				   bool_t isStaticCall, bool_t isConstructor)
{
    hash_t id = (hash_t)methodID;
    int32_t i, nMethods;
    struct methodblock** methods;
    struct methodblock* mb = NULL;

	/* First search in the virtual method table: */
	if (!CCIs(cb, Resolved)) {
		char* detail;
		char* exception = ResolveClass(cb, &detail);
		if (exception != NULL) {
			SignalError(ee, exception, detail);
			return NULL;
		}
	}
    nMethods = cbMethodTableSize(cb);
    methods = cbMethodTable(cb)->methods;
    for (i = 0; i < nMethods; i++) {
		mb = methods[i];
#ifdef DEBUG
		if (getenv("METHOD_LOOKUP") != 0 && mb) {
			fprintf(stderr, "%d: %s.%s%s (%d)", id, classname(cb),
					mb->fb.name, mb->fb.signature, mb->fb.ID);
			if (mb != NULL && mb->fb.ID == id)
				fprintf(stderr, " => found");
			fprintf(stderr, "\n");
		}
#endif
		if (mb != NULL && mb->fb.ID == id)
			return mb;
	}
    if (i == nMethods) {
		/* If not found yet, search in this class' methods: */
		nMethods = cb->methods_count;
		mb = cb->methods;
		for (i = 0; i < nMethods; i++, mb++) {
#ifdef DEBUG
			if (getenv("METHOD_LOOKUP") != 0 && mb) {
				fprintf(stderr, "%di: %s.%s%s (%d)", id, classname(cb),
						mb->fb.name, mb->fb.signature, mb->fb.ID);
				if (mb != NULL && mb->fb.ID == id)
					fprintf(stderr, " => found");
				fprintf(stderr, "\n");
			}
#endif
			if (mb != NULL && mb->fb.ID == id)
				return mb;
		}
		/* not found anywhere */
		SignalError(ee, JAVAPKG "NoSuchMethodError",
					"native method specified an invalid method");
		return NULL;
    }
    return mb;
}

#ifdef XP_MAC
#ifndef DEBUG
#pragma global_optimizer reset 
#endif
#endif /* XP_MAC */

#if defined(NETSCAPE) && defined(XP_UNIX)

#define WITH_LOCKED_GUI_THREAD								\
	{														\
		int _xWasLocked_ = PR_InMonitor(_pr_rusty_lock);	\
		if (_xWasLocked_) PR_XUnlock();						\

#define END_WITH_LOCKED_GUI_THREAD				\
		if (_xWasLocked_) PR_XLock();			\
	}											\

#else 

#define WITH_LOCKED_GUI_THREAD 
#define END_WITH_LOCKED_GUI_THREAD

#endif

typedef union JRI_PushArgsType {
	va_list		v;
	JRIValue*	a;
} JRI_PushArgsType;

typedef char*
(*JRI_PushArguments_t)(ExecEnv* ee, char* method_signature, char* method_name,
					   JavaFrame *current_frame, JRI_PushArgsType args);

static char* JRI_CALLBACK
jri_PushArgumentsVararg(ExecEnv* ee, char* method_name, char* method_signature,
					   JavaFrame *current_frame, JRI_PushArgsType a)
{
    bool_t shortFloats = FALSE;
    char *p;
    Java8 tdub;
	va_list args = a.v;

    for (p = method_signature + 1; *p != SIGNATURE_ENDFUNC; p++) {
		switch (*p) {
		  case SIGNATURE_BOOLEAN:
		  case SIGNATURE_SHORT:
		  case SIGNATURE_BYTE:
		  case SIGNATURE_CHAR:
		  case SIGNATURE_INT:
			(current_frame->optop++)->i = va_arg(args, long);
			break;
		  case SIGNATURE_FLOAT:
			if (shortFloats) 
				(current_frame->optop++)->i = va_arg(args, long);
			else 
				(current_frame->optop++)->f = (float)va_arg(args, double);
			break;
		  case SIGNATURE_CLASS:
		  {
			  jref ref = va_arg(args, jref);	/* don't put va_arg in the macro */
			  (current_frame->optop++)->h = JRIEnv_deref(EE2NaEnv(ee), ref);
		  }
		  while (*p != SIGNATURE_ENDCLASS) p++;
		  break;
		  case SIGNATURE_ARRAY:
			while ((*p == SIGNATURE_ARRAY)) p++;
			if (*p == SIGNATURE_CLASS) 
	        { while (*p != SIGNATURE_ENDCLASS) p++; } 
			{
				jref ref = va_arg(args, jref);	/* don't put va_arg in the macro */
				(current_frame->optop++)->p = (void*)JRIEnv_deref(EE2NaEnv(ee), ref);
			}
			break;
		  case SIGNATURE_LONG:
			SET_INT64(tdub, current_frame->optop, va_arg(args, int64_t));
			current_frame->optop += 2;
			break;
		  case SIGNATURE_DOUBLE:
			SET_DOUBLE(tdub, current_frame->optop, va_arg(args, double));
			current_frame->optop += 2;
			break;
		  default:
			fprintf(stderr, "Invalid method signature '%s'\n", method_name);
			return NULL;
		}
    }
	return p;
}

static char* JRI_CALLBACK
jri_PushArgumentsArray(ExecEnv* ee, char* method_name, char* method_signature,
					   JavaFrame *current_frame, JRI_PushArgsType a)
{
    bool_t shortFloats = FALSE;
    char *p;
    JRI_JDK_Java8 tdub;
	JRIValue* args = a.a;

    for (p = method_signature + 1; *p != SIGNATURE_ENDFUNC; p++) {
		switch (*p) {
		  case SIGNATURE_BOOLEAN:
		  case SIGNATURE_SHORT:
		  case SIGNATURE_BYTE:
		  case SIGNATURE_CHAR:
		  case SIGNATURE_INT:
			(current_frame->optop++)->i = (*args++).i;
			break;
		  case SIGNATURE_FLOAT:
			if (shortFloats) 
				(current_frame->optop++)->i = (*args++).i;
			else 
				(current_frame->optop++)->f = (float)(*args++).d;
			break;
		  case SIGNATURE_CLASS:
		  {
			  jref ref = (*args++).r;	/* don't put this expr in the macro */
			  (current_frame->optop++)->h = JRIEnv_deref(EE2NaEnv(ee), ref);
		  }
		  while (*p != SIGNATURE_ENDCLASS) p++;
		  break;
		  case SIGNATURE_ARRAY:
			while ((*p == SIGNATURE_ARRAY)) p++;
			if (*p == SIGNATURE_CLASS) 
	        { while (*p != SIGNATURE_ENDCLASS) p++; } 
			{
				jref ref = (*args++).r;	/* don't put this expr in the macro */
				(current_frame->optop++)->p = (void*)JRIEnv_deref(EE2NaEnv(ee), ref);
			}
			break;
		  case SIGNATURE_LONG:
			JRI_SET_INT64(tdub, current_frame->optop, (*args++).l);
			current_frame->optop += 2;
			break;
		  case SIGNATURE_DOUBLE:
			JRI_SET_DOUBLE(tdub, current_frame->optop, (*args++).d);
			current_frame->optop += 2;
			break;
		  default:
			fprintf(stderr, "Invalid method signature '%s'\n", method_name);
			return NULL;
		}
    }
	return p;
}

extern bool_t ExecuteJava(unsigned char*, ExecEnv* ee);

static JRIValue JRI_CALLBACK
jri_Invoke(ExecEnv* ee, jref self, JRIMethodID methodID,
		   JRI_PushArguments_t pushArguments, JRI_PushArgsType args,
		   bool_t isStaticCall, bool_t isConstructor)
{
    JRIValue result;
    void* obj;
    ClassClass* cb;
    struct methodblock* mb;
    char* method_name;
    char* method_signature;
    JavaFrame *current_frame, *previous_frame;
    JavaStack *current_stack;
    char *p;

    /* zero result */
    result.l = jlong_ZERO;
    
    if (isStaticCall) {
		cb = jri_Ref2Class(ee, self);
		if (cb == NULL) return result;
		obj = cb;
    }
    else {
		obj = JRIEnv_deref(ee, self);
		if (obj == NULL) {
			SignalError(ee, JAVAPKG "NullPointerException", NULL);
			return result;
		}
		cb = obj_classblock((JHandle*)obj);
    }
    /*
    ** We have to look up the method block or we may get security
    ** violations when we invoke the method. The JRI can access any
    ** method.
    */
	sysAssert(methodID != JRIUninitialized);
    mb = jri_GetMethodBlock(ee, cb, methodID, isStaticCall, isConstructor);
    if (mb == NULL) {
		/* exception already raised */
		return result;
    }
    method_name = mb->fb.name;
    method_signature = mb->fb.signature;

    /*
    ** The following is stolen from do_execute_java_method_vararg, and
    ** tweaked to grab jrefs off the stack instead of JHandle*s
    */
    previous_frame = ee->current_frame;
    if (previous_frame == 0) {	
		/* bottommost frame on this Exec Env. */
		current_stack = ee->initial_stack;
		current_frame = (JavaFrame *)(current_stack->data); /* no vars */
    } else {
		int args_size = mb ? mb->args_size 
			/* We don't need the +1 if a static call, but it's
			 * okay to make args_size too big.  */
			: (Signature2ArgsSize(method_signature) + 1);
		current_stack = previous_frame->javastack; /* assume same stack */
		if (previous_frame->current_method) {
			int size = previous_frame->current_method->maxstack;
			current_frame = (JavaFrame *)(&previous_frame->ostack[size]);
		} else {
			/* The only frames that don't have a mb are pseudo frames like 
			 * this one and they don't really touch their stack. */
			current_frame = (JavaFrame *)(previous_frame->optop + 3);
		}
		if (current_frame->ostack + args_size > current_stack->end_data) {
			/* Ooops.  The current stack isn't big enough.  */
			if (current_stack->next != 0) {
				current_stack = current_stack->next;
			} else {
				current_stack = CreateNewJavaStack(ee, current_stack);
				if (current_stack == 0) {
					SignalError(ee, JAVAPKG "OutOfMemoryError", 0);
					return result;
				}
			}	
			current_frame = (JavaFrame *)(current_stack->data); /* no vars */
		} 
    }
    ee->current_frame = current_frame;

    current_frame->prev = previous_frame;
    current_frame->javastack = current_stack;
    current_frame->optop = current_frame->ostack;
    current_frame->vars = 0;	/* better not reference any! */
    current_frame->monitor = 0;	/* not monitoring anything */
    current_frame->annotation = 0;
    current_frame->current_method = 0; /* not part of a method. */
    
    /* Push the target object, if not a static call */
    if (!isStaticCall) 
        (current_frame->optop++)->p = obj;
	
	p = pushArguments(ee, method_name, method_signature, current_frame, args);
	if (p == NULL)
		return result;	/* argument error */

    {
		ClassClass *cb = (isStaticCall || mb != 0) ? obj 
			: obj_array_classblock((JHandle *)obj);
		unsigned char pc[6];
		cp_item_type  constant_pool[10];
		unsigned char cpt[10];
		bool_t isInitializer = (method_name[0] == '<'  &&  /* fast test */
								!isStaticCall && 
								!strcmp(method_name, "<init>"));
	
		constant_pool[CONSTANT_POOL_TYPE_TABLE_INDEX].p = cpt;
		cpt[0] = CONSTANT_POOL_ENTRY_RESOLVED;

		if (isStaticCall || mb) {
			pc[0] = isStaticCall ? (mb ? opc_invokestatic_quick 
									: opc_invokestatic) 
				: opc_invokenonvirtual_quick;
			pc[1] = 0; pc[2] = 1;	/* constant pool entry #1 */
			pc[3] = opc_return;
		} else {
			int nargs = (current_frame->optop - current_frame->ostack);
			pc[0] = isInitializer ? opc_invokenonvirtual 
				: opc_invokeinterface;
			pc[1] = 0; pc[2] = 1;	/* constant pool entry #1 */
			pc[3] = nargs;
			pc[4] = 0;			/* padding needed for guess */
			pc[5] = opc_return;
		}

		if (mb) {
			constant_pool[1].p = mb;
			cpt[1] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Methodref;
		} else {
			constant_pool[1].i = (2 << 16) + 3; /* Class, NameAndType */
			cpt[1] = (isStaticCall || isInitializer)
		        ? CONSTANT_Methodref : CONSTANT_InterfaceMethodref; 
			constant_pool[2].p = cb; /* class */
			cpt[2] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Class;
			constant_pool[3].i = (4 << 16) + 5;	/* Name, Type */
			cpt[3] = CONSTANT_NameAndType;
			constant_pool[4].cp = method_name;
			cpt[4] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Utf8;
			constant_pool[5].cp = method_signature;
			cpt[5] = CONSTANT_POOL_ENTRY_RESOLVED | CONSTANT_Utf8;	    
		}

		current_frame->constant_pool = constant_pool;

		WITH_LOCKED_GUI_THREAD {

		/* Run the byte codes outside the lock and catch any exceptions. */
		ee->exceptionKind = EXCKIND_NONE;
		if (ExecuteJava(pc, ee)) {
			switch (p[1]) {
			  case SIGNATURE_VOID:
				break;
			  case SIGNATURE_BOOLEAN:
				result.z = (jbool)current_frame->optop[-1].i;
				break;
			  case SIGNATURE_BYTE:
				result.b = (jbyte)current_frame->optop[-1].i;
				break;
			  case SIGNATURE_CHAR:
				result.c = (jchar)current_frame->optop[-1].i;
				break;
			  case SIGNATURE_SHORT:
				result.s = (jshort)current_frame->optop[-1].i;
				break;
			  case SIGNATURE_LONG: {
				JRI_JDK_Java8 tmp;
				result.l = JRI_GET_INT64(tmp, &current_frame->optop[-2].i);
				break;
			  }
			  case SIGNATURE_DOUBLE: {
				JRI_JDK_Java8 tmp;
				result.d = JRI_GET_DOUBLE(tmp, &current_frame->optop[-2].i);
				break;
			  }
			  default:	/* int, object and array */
				result.i = (jint)current_frame->optop[-1].i;
				break;
			}
		}
		/* Our caller can look at ee->exceptionKind and ee->exception. */
		ee->current_frame = previous_frame;

		} END_WITH_LOCKED_GUI_THREAD;
    }
    return result;
}

/*******************************************************************************
 * MCOM
 ******************************************************************************/

JRI_DEFINE_GUID(JRINativePkgID, 0xb79cd9f0, 0x8c2f, 0x11cf);
JRI_DEFINE_GUID(JRIRuntimePkgID, 0xb8342180, 0x8c2f, 0x11cf);
JRI_DEFINE_GUID(JRIReflectionPkgID, 0xb89c0a40, 0x8c2f, 0x11cf);
JRI_DEFINE_GUID(JRIDebuggerPkgID, 0xb8fac7e0, 0x8c2f, 0x11cf);
JRI_DEFINE_GUID(JRICompilerPkgID, 0xb9567720, 0x8c2f, 0x11cf);
JRI_DEFINE_GUID(JRIExprPkgID, 0xb9af1800, 0x8c2f, 0x11cf);

static struct java_lang_Object* JRI_CALLBACK
jri_GetInterface(ExecEnv* self, jint interfaceID)
{
	GUID* id = (GUID*)interfaceID;
	if (memcmp(id, &JRINativePkgID, sizeof(GUID)) == 0)
		return (struct java_lang_Object*)&self->nativeInterface;
	else if (memcmp(id, &JRIRuntimePkgID, sizeof(GUID)) == 0)
		return (struct java_lang_Object*)&self->runtimeInterface;
	else if (memcmp(id, &JRIReflectionPkgID, sizeof(GUID)) == 0)
		return (struct java_lang_Object*)&self->reflectionInterface;
	else if (memcmp(id, &JRIDebuggerPkgID, sizeof(GUID)) == 0)
		return (struct java_lang_Object*)&self->debuggerInterface;
	else if (memcmp(id, &JRICompilerPkgID, sizeof(GUID)) == 0)
		return (struct java_lang_Object*)&self->compilerInterface;
	else if (memcmp(id, &JRIExprPkgID, sizeof(GUID)) == 0)
		return (struct java_lang_Object*)&self->exprInterface;
	else
		return NULL;
}

static long JRI_CALLBACK
jri_AddRef(void* self)
{
	return 0;	/* ExecEnvs are not reference counted (yet) */
}

static long JRI_CALLBACK
jri_Release(void* self)
{
	return 0;	/* ExecEnvs are not reference counted (yet) */
}

/*******************************************************************************
 * Environments
 ******************************************************************************/

typedef void	JavaObject; 

static JavaObject* JRI_CALLBACK
jri_NativeGetInterface(JRIEnv* self, jint id)
{
	return jri_GetInterface(NaEnv2EE(self), id);
}

/* !!! This is a non-jump-table entry point: */
JRI_PUBLIC_API(const JRIEnvInterface**)
JRI_GetCurrentEnv(void)
{
	if (!jdkInitialized)
		return NULL;
	return EE2NaEnv(EE());
}

/* Defining Classes */

static struct java_lang_Class* JRI_CALLBACK
jri_DefineClass(JRIEnv* env, jint op, struct java_lang_ClassLoader* classLoader,
				jbyte* buf, jsize bufLen)
{
	ExecEnv* ee = NaEnv2EE(env);
    ClassClass* cb;
    char* ename;
    char* detail;

    java_lang_ClassLoader_checkInitialized((struct Hjava_lang_ClassLoader*)
										   classLoader);
    if (exceptionOccurred(ee))
		return NULL;

    cb = AllocClass();
    if (cb == NULL) {
		SignalError(ee, JAVAPKG "OutOfMemoryError", NULL);
		return NULL;
    }

    cb->loader = (HObject*)classLoader;
	lock_classes();
    if (!createInternalClass((unsigned char*)buf,
							 (unsigned char*)buf + bufLen,
							 cb)) {
		if (!exceptionOccurred(ee))
			SignalError(ee, JAVAPKG "ClassFormatError", NULL);
		unlock_classes();
        return NULL;
    }
    AddBinClass(cb);
    /*
    ** Clear the CCF_IsResolving bit for the class since it is fully 
    ** initialized now...
    */
    sysAssert(CCIs(cb, Resolving));
    CCClear(cb, Resolving);
	unlock_classes();

    /*
    ** InitializeClass will lock the class and initialize it if
    ** necessary. Note that we need to hold the CCF_Locked across the
    ** call to prevent the class unloader from unloading this currently
    ** unreferenced class!
    */
    if ((ename = InitializeClass(cb, &detail)) != 0) {
		SignalError(ee, ename, detail);
		return 0;
    }
    return JRIEnv_ref(ee, (HObject*)cbHandle(cb));
}

static struct java_lang_Class* JRI_CALLBACK
jri_FindClass(JRIEnv* env, jint op, const char* name)
{
	ExecEnv* ee = NaEnv2EE(env);
    ClassClass* cb;
	
    cb = FindClass(ee, (char*)name, TRUE);
    if (cb != NULL)
    	return JRIEnv_ref(ee, (HObject*)cbHandle(cb));
    else
    	return NULL;
}

/* Working with Exceptions */

static void JRI_CALLBACK
jri_Throw(JRIEnv* env, jint op, struct java_lang_Throwable* obj)
{
	ExecEnv* ee = NaEnv2EE(env);
    HObject* ret;

    ret = JRIEnv_deref(ee, obj);
    if (!is_instance_of(ret, classThrowable, ee)) {
		SignalError(ee, JAVAPKG "IllegalAccessError", 
					"object is not an instance of java.lang.Throwable");
		return;
    }
    fillInStackTrace((struct Hjava_lang_Throwable*)ret, ee);
    exceptionThrow(ee, ret);
}

static void JRI_CALLBACK
jri_ThrowNew(JRIEnv* env, jint op, struct java_lang_Class* clazz, const char* message)
{
	ExecEnv* ee = NaEnv2EE(env);
    ClassClass* cb;
    
    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return;
    if (!is_subclass_of(cb, classThrowable, ee)) {
		SignalError(ee, JAVAPKG "IllegalAccessError", 
					"class is not a subclass of java.lang.Throwable");
		return;
    }
    SignalError1(ee, cb, (char*)message);
}

static struct java_lang_Throwable* JRI_CALLBACK
jri_ExceptionOccurred(JRIEnv* env, jint op)
{
	ExecEnv* ee = NaEnv2EE(env);
    if (exceptionOccurred(ee))
		return (struct java_lang_Throwable*)
			JRIEnv_ref(ee, ee->exception.exc);
    else
		return NULL;
}

#if 0
static void
jri_ExceptionDescribe(JRIEnv* env, jint op, JavaWriterProc writer,
					   void* dest, jsize destLen)
{
	ExecEnv* ee = NaEnv2EE(env);
    exception_t exc;
    
    ee = Env2EE(env);
    exc = ee->exception.exc;
    if (!is_instance_of(exc, classJavaLangThreadDeath, ee)
		&& is_instance_of(exc, classJavaLangThrowable, ee)) {
		(void)printStackTrace((struct Hjava_lang_Throwable*)exc,
							  (StackPrinter)writer, dest, destLen);
    }
}
#else
/*
** Describe to stderr -- yuck!
*/
static void JRI_CALLBACK
jri_ExceptionDescribe(JRIEnv* env, jint op)
{
	ExecEnv* ee = NaEnv2EE(env);
	WITH_LOCKED_GUI_THREAD {
		exceptionDescribe(ee);
	} END_WITH_LOCKED_GUI_THREAD;
}
#endif

static void JRI_CALLBACK
jri_ExceptionClear(JRIEnv* env, jint op)
{
	ExecEnv* ee = NaEnv2EE(env);
    exceptionClear(ee);
}

/*******************************************************************************
 * References
 ******************************************************************************/

/*
** jglobal is equivalent to RefCell (JHandle**), whereas
** jref is equivalent to JHandle* 
*/

static jglobal JRI_CALLBACK
jri_NewGlobalRef(JRIEnv* env, jint op, JavaObject* ref)
{
	ExecEnv* ee = NaEnv2EE(env);
    jint result;
	RefTable* globalRefs;

	globalRefs = &ee->globalRefs;
	
	if (globalRefs->element == NULL) {
		if (!RefTable_init(globalRefs)) {
			SignalError(ee, JAVAPKG "OutOfMemory", NULL);
			return 0;
		}
	}

    result = (jint)RefTable_insert(globalRefs, JRIEnv_deref(ee, ref));
    if (result == 0) {
		SignalError(ee, JAVAPKG "OutOfMemory", NULL);
	}
    return (jglobal)result;
}

static void JRI_CALLBACK
jri_DisposeGlobalRef(JRIEnv* env, jint op, jglobal ref)
{
	ExecEnv* ee = NaEnv2EE(env);
	if (ref == 0) {
		SignalError(ee, JAVAPKG "NullPointerException", NULL);
		return;
	}
	RefTable_drop(&ee->globalRefs, (RefCell)ref);
}

static JavaObject* JRI_CALLBACK
jri_GetGlobalRef(JRIEnv* env, jint op, jglobal globalRef)
{
	ExecEnv* ee = NaEnv2EE(env);
    jref result;
    result = JRIEnv_ref(ee, RefTable_get(&ee->globalRefs,
										 (RefCell)globalRef));
    return result;
}

static void JRI_CALLBACK
jri_SetGlobalRef(JRIEnv* env, jint op, jglobal globalRef, JavaObject* value)
{
	ExecEnv* ee = NaEnv2EE(env);
	if (globalRef == NULL) {
		SignalError(ee, JAVAPKG "NullPointerException", NULL);
		return;
	}
	RefTable_set(&ee->globalRefs, (RefCell)globalRef,
				 JRIEnv_deref(ee, value));
}

static jbool JRI_CALLBACK
jri_IsSameObject(JRIEnv* env, jint op, JavaObject* r1, JavaObject* r2)
{
    return JRIEnv_deref(NaEnv2EE(env), r1) == JRIEnv_deref(NaEnv2EE(env), r2);
}

/*******************************************************************************
 * Identifiers
 ******************************************************************************/

static jint
jri_GetMethodID(JRIEnv* env, jint op, struct java_lang_Class* clazz, 
				const char* name, const char* sig)
{
	ExecEnv* ee = NaEnv2EE(env);
    ClassClass* cb;
    hash_t id;
    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) 
		return -1;

    /* Passing in cb has the effect of never failing a call because
     * the ClassLoaders of the caller and cb are different.
     * Users of the JRI are responsible for making sure this is typesafe. */
	id = NameAndTypeToHash((char*)name, (char*)sig, cb);
	return (jint)id;
}

/*******************************************************************************
 * Object Operations
 ******************************************************************************/

static JavaObject* JRI_CALLBACK
jri_Construct(JRIEnv* env, struct java_lang_Class* clazz, jint methodID,
			  JRI_PushArguments_t pushArguments, JRI_PushArgsType args)
{
	ExecEnv* ee = NaEnv2EE(env);
    HObject* obj;
    ClassClass* cb;
    JavaObject* result;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return NULL;
    
    /* Find the class.  Make sure we can allocate an object of that class */
    if (cbAccess(cb) & (ACC_INTERFACE | ACC_ABSTRACT)) {
		SignalError(ee, JAVAPKG "InstantiationException",
					classname(cb));
        return NULL;
    }

    /* Allocate the object and call the constructor */
	obj = newobject(cb, 0, ee);
    if (obj == NULL) { 
		SignalError(ee, JAVAPKG "OutOfMemory", NULL);
        return NULL;
    }
    result = (JavaObject*)JRIEnv_ref(ee, obj);
    jri_Invoke(ee, result, methodID, pushArguments, args, FALSE, TRUE);
    return result;
}

static JavaObject* JRI_CALLBACK
jri_NewObjectA(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint methodID, JRIValue* args)
{
	JRI_PushArgsType a;
	a.a = args;
	return jri_Construct(env, clazz, methodID,
						 (JRI_PushArguments_t)jri_PushArgumentsArray, a);
}

static JavaObject* JRI_CALLBACK
jri_NewObjectV(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint methodID, va_list args)
{
	JRI_PushArgsType a;
	a.v = args;
	return jri_Construct(env, clazz, methodID,
						 (JRI_PushArguments_t)jri_PushArgumentsVararg, a);
}

static JavaObject* JRI_CALLBACK
jri_NewObject(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint methodID, ...)
{
    jref result;
    va_list args;
	JRI_PushArgsType a;
    va_start(args, methodID);
	a.v = args;
	result = jri_Construct(env, clazz, methodID,
						   (JRI_PushArguments_t)jri_PushArgumentsVararg, a);
    va_end(args);
    return result;
}

static struct java_lang_Class* JRI_CALLBACK
jri_GetObjectClass(JRIEnv* env, jint op, JavaObject* obj)
{
	JHandle* o = JRIEnv_deref(NaEnv2EE(env), obj);
	HClass* hc = java_lang_Object_getClass(o);
	return JRIEnv_ref(NaEnv2EE(env), (HObject*)hc);
}

static jbool JRI_CALLBACK
jri_IsInstanceOf(JRIEnv* env, jint op, JavaObject* obj,
				 struct java_lang_Class* clazz)
{
	ExecEnv* ee = NaEnv2EE(env);
    ClassClass* cb;

    cb = jri_Ref2Class(ee, clazz);
    return cb != NULL
        && is_instance_of(JRIEnv_deref(ee, obj), cb, ee);
}

#if 0
static void
jri_PrintObject(JRIEnv* env, jint op, JavaObject* obj,
				JavaWriterProc writer, void* dest, jsize destLen)
{
	ExecEnv* ee = NaEnv2EE(env);
    char* str;
    int len;

    str = Object2CString(JRIEnv_deref(ee, obj));	/* static buffer */
    len = strlen(str);
    (void)writer(env, dest, destLen, str, len);
}
#endif

/******************************************************************************/
/* Accessing Fields by Index */

static jint
jri_GetFieldID(JRIEnv* env, jint op, struct java_lang_Class* clazz, 
			   const char* name, const char* sig)
{
	ExecEnv* ee = NaEnv2EE(env);
	hash_t id;
	jsize index;
	ClassClass* cb;
	struct fieldblock* fb;
	cb = jri_Ref2Class(ee, clazz);
	if (cb == NULL) 
		return -1;

    /* Passing in cb has the effect of never failing a call because
     * the ClassLoaders of the caller and cb are different.
     * Users of the JRI are responsible for making sure this is typesafe. */
	id = NameAndTypeToHash((char*)name, (char*)sig, cb);
	fb = jri_FindFieldBlock(ee, cb, id, &index);
	if (fb == NULL)
		return -1;
	if (fb->access & ACC_STATIC) {
		SignalError(ee, JAVAPKG "NoSuchFieldError",
					"static field accessed with non-static accessor");
		return -1;
	}
	return (jint)fb->u.offset;
}

#define obj_Getoffsetaddr(o, off) ((OBJECT*)((char *)unhand(o)+(off)))
#define obj_Getoffset(o, off)	  (*obj_Getoffsetaddr(o, off))
#define obj_Setoffset(o, off, v)  (*obj_Getoffsetaddr(o, off) = (v))

static JavaObject* JRI_CALLBACK
jri_GetField_ref(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
    JHandle* value;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
	value = (JHandle*)obj_Getoffset(o, fieldID);
    return (JavaObject*)JRIEnv_ref(NaEnv2EE(env), value);
}

static jbool JRI_CALLBACK
jri_GetField_boolean(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    return (jbool)obj_Getoffset(o, fieldID);
}

static jbyte JRI_CALLBACK
jri_GetField_byte(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    return (jbyte)obj_Getoffset(o, fieldID);
}

static jchar JRI_CALLBACK
jri_GetField_char(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    return (jchar)obj_Getoffset(o, fieldID);
}

static jshort JRI_CALLBACK
jri_GetField_short(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    return (jshort)obj_Getoffset(o, fieldID);
}

static jint JRI_CALLBACK
jri_GetField_int(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    return (jint)obj_Getoffset(o, fieldID);
}

static jlong JRI_CALLBACK
jri_GetField_long(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
	OBJECT* slot;
    JRI_JDK_Java8 tmp;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    slot = obj_Getoffsetaddr(o, fieldID);
    return JRI_GET_INT64(tmp, slot);
}

static jfloat JRI_CALLBACK
jri_GetField_float(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
	OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    slot = obj_Getoffsetaddr(o, fieldID);
    return *(jfloat*)slot;
}

static jdouble JRI_CALLBACK
jri_GetField_double(JRIEnv* env, jint op, JavaObject* obj, jint fieldID)
{
    JHandle* o;
	OBJECT* slot;
    JRI_JDK_Java8 tmp;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    slot = obj_Getoffsetaddr(o, fieldID);
    return JRI_GET_DOUBLE(tmp, slot);
}

/******************************************************************************/

static void JRI_CALLBACK
jri_SetField_ref(JRIEnv* env, jint op, JavaObject* obj, jint fieldID, 
				 JavaObject* value)
{
    JHandle* o = JRIEnv_deref(NaEnv2EE(env), obj);
    JHandle* v = JRIEnv_deref(NaEnv2EE(env), value);
    obj_Setoffset(o, fieldID, (OBJECT)v);
}

static void JRI_CALLBACK
jri_SetField_boolean(JRIEnv* env, jint op, JavaObject* obj, jint fieldID,
					  jbool value)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    obj_Setoffset(o, fieldID, value);
}

static void JRI_CALLBACK
jri_SetField_byte(JRIEnv* env, jint op, JavaObject* obj, jint fieldID,
				   jbyte value)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    obj_Setoffset(o, fieldID, value);
}

static void JRI_CALLBACK
jri_SetField_char(JRIEnv* env, jint op, JavaObject* obj, jint fieldID,
				   jchar value)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    obj_Setoffset(o, fieldID, value);
}

static void JRI_CALLBACK
jri_SetField_short(JRIEnv* env, jint op, JavaObject* obj, jint fieldID,
					jshort value)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    obj_Setoffset(o, fieldID, value);
}

static void JRI_CALLBACK
jri_SetField_int(JRIEnv* env, jint op, JavaObject* obj, jint fieldID,
				  jint value)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    obj_Setoffset(o, fieldID, value);
}

static void JRI_CALLBACK
jri_SetField_long(JRIEnv* env, jint op, JavaObject* obj, jint fieldID,
				   jlong value)
{
    JHandle* o;
	OBJECT* slot;
    JRI_JDK_Java8 tmp;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    slot = obj_Getoffsetaddr(o, fieldID);
    JRI_SET_INT64(tmp, slot, value);
}

static void JRI_CALLBACK
jri_SetField_float(JRIEnv* env, jint op, JavaObject* obj, jint fieldID,
					jfloat value)
{
    JHandle* o;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    obj_Setoffset(o, fieldID, (OBJECT)value);
}

static void JRI_CALLBACK
jri_SetField_double(JRIEnv* env, jint op, JavaObject* obj, jint fieldID,
					 jdouble value)
{
    JHandle* o;
	OBJECT* slot;
    Java8 tmp;
	sysAssert(fieldID != JRIUninitialized);
	o = JRIEnv_deref(NaEnv2EE(env), obj);
    slot = obj_Getoffsetaddr(o, fieldID);
    SET_DOUBLE(tmp, slot, value);
}

/******************************************************************************/

/* Calling Methods */

#define DEFINE_CALLMETHOD(ResultType, suffix, unionType)				 \
static ResultType JRI_CALLBACK														 \
jri_CallMethod##suffix(JRIEnv* env, jint op, JavaObject* obj,		 \
						 jint methodID, ...)							 \
{																		 \
	ExecEnv* ee = NaEnv2EE(env);										 \
	ResultType result;													 \
	va_list args;														 \
	JRI_PushArgsType a;													 \
																		 \
	va_start(args, methodID);											 \
	a.v = args;															 \
	result = jri_Invoke(ee, obj, methodID,								 \
						(JRI_PushArguments_t)jri_PushArgumentsVararg, a, \
						FALSE, FALSE).unionType;						 \
	va_end(args);														 \
	return result;														 \
}																		 \
																		 \
static ResultType JRI_CALLBACK														 \
jri_CallMethod##suffix##V(JRIEnv* env, jint op, JavaObject* obj,	 \
						  jint methodID, va_list args)					 \
{																		 \
	ExecEnv* ee = NaEnv2EE(env);										 \
	ResultType result;													 \
	JRI_PushArgsType a;													 \
	a.v = args;															 \
																		 \
	result = jri_Invoke(ee, obj, methodID,								 \
						(JRI_PushArguments_t)jri_PushArgumentsVararg, a, \
						FALSE, FALSE).unionType;						 \
	return result;														 \
}																		 \
																		 \
static ResultType JRI_CALLBACK														 \
jri_CallMethod##suffix##A(JRIEnv* env, jint op, JavaObject* obj,	 \
						  jint methodID, JRIValue* args)				 \
{																		 \
	ExecEnv* ee = NaEnv2EE(env);										 \
	ResultType result;													 \
	JRI_PushArgsType a;													 \
	a.a = args;															 \
	result = jri_Invoke(ee, obj, methodID,								 \
						(JRI_PushArguments_t)jri_PushArgumentsArray, a,	 \
						FALSE, FALSE).unionType;						 \
	return result;														 \
}																		 \

DEFINE_CALLMETHOD(JavaObject*,	_ref, r)
DEFINE_CALLMETHOD(jbool,					_boolean, z)
DEFINE_CALLMETHOD(jbyte,					_byte, b)
DEFINE_CALLMETHOD(jchar,					_char, c)
DEFINE_CALLMETHOD(jshort,					_short, s)
DEFINE_CALLMETHOD(jint,						_int, i)
DEFINE_CALLMETHOD(jlong,					_long, l)
DEFINE_CALLMETHOD(jfloat,					_float, f)
DEFINE_CALLMETHOD(jdouble,					_double, d)

#define DEFINE_CALLSTATICMETHOD(ResultType, suffix, unionType)				\
static ResultType JRI_CALLBACK															\
jri_CallStaticMethod##suffix(JRIEnv* env, jint op, struct java_lang_Class* clazz,	\
							   jint methodID, ...)							\
{																			\
	ExecEnv* ee = NaEnv2EE(env);											\
	ResultType result;														\
	va_list args;															\
	JRI_PushArgsType a;														\
	va_start(args, methodID);												\
	a.v = args;																\
	result = jri_Invoke(ee, clazz, methodID,								\
						(JRI_PushArguments_t)jri_PushArgumentsVararg, a,	\
						TRUE, FALSE).unionType;								\
	va_end(args);															\
	return result;															\
}																			\
																			\
static ResultType JRI_CALLBACK															\
jri_CallStaticMethod##suffix##V(JRIEnv* env, jint op, struct java_lang_Class* clazz,	\
								jint methodID, va_list args)				\
{																			\
	ExecEnv* ee = NaEnv2EE(env);											\
	ResultType result;														\
	JRI_PushArgsType a;														\
	a.v = args;																\
	result = jri_Invoke(ee, clazz, methodID,								\
						(JRI_PushArguments_t)jri_PushArgumentsVararg, a,	\
						TRUE, FALSE).unionType;								\
	return result;															\
}																			\
																			\
static ResultType JRI_CALLBACK															\
jri_CallStaticMethod##suffix##A(JRIEnv* env, jint op, struct java_lang_Class* clazz,	\
								jint methodID, JRIValue* args)				\
{																			\
	ExecEnv* ee = NaEnv2EE(env);											\
	ResultType result;														\
	JRI_PushArgsType a;														\
	a.a = args;																\
	result = jri_Invoke(ee, clazz, methodID,								\
						(JRI_PushArguments_t)jri_PushArgumentsArray, a,		\
						TRUE, FALSE).unionType;								\
	return result;															\
}																			\

DEFINE_CALLSTATICMETHOD(JavaObject*,	_ref, r)
DEFINE_CALLSTATICMETHOD(jbool,						_boolean, z)
DEFINE_CALLSTATICMETHOD(jbyte,						_byte, b)
DEFINE_CALLSTATICMETHOD(jchar,						_char, c)
DEFINE_CALLSTATICMETHOD(jshort,						_short, s)
DEFINE_CALLSTATICMETHOD(jint,						_int, i)
DEFINE_CALLSTATICMETHOD(jlong,						_long, l)
DEFINE_CALLSTATICMETHOD(jfloat,						_float, f)
DEFINE_CALLSTATICMETHOD(jdouble,					_double, d)

/*******************************************************************************
 * Class Operations
 ******************************************************************************/

static jbool JRI_CALLBACK
jri_IsSubclassOf(JRIEnv* env, jint op, struct java_lang_Class* clazz, 
				 struct java_lang_Class* super)
{
	ExecEnv* ee = NaEnv2EE(env);
    ClassClass* cb;
    ClassClass* scb;

    cb = jri_Ref2Class(ee, clazz);
    scb = jri_Ref2Class(ee, super);
    return cb != NULL
        && scb != NULL
        && is_subclass_of(cb, scb, ee);
}

/* Accessing Static Fields */

static jint JRI_CALLBACK
jri_GetStaticFieldID(JRIEnv* env, jint op, struct java_lang_Class* clazz, 
					 const char* name, const char* sig)
{
	ExecEnv* ee = NaEnv2EE(env);
	hash_t id;
	jsize index;
	ClassClass* cb;
	struct fieldblock* fb;
	cb = jri_Ref2Class(ee, clazz);
	if (cb == NULL) 
		return -1;

    /* Passing in cb has the effect of never failing a call because
     * the ClassLoaders of the caller and cb are different.
     * Users of the JRI are responsible for making sure this is typesafe. */
	id = NameAndTypeToHash((char*)name, (char*)sig, cb);
	fb = jri_FindFieldBlock(ee, cb, id, &index);
	if (fb == NULL)
		return -1;
	if (!(fb->access & ACC_STATIC)) {
		SignalError(ee, JAVAPKG "NoSuchFieldError",
					"non-static field accessed with static accessor");
		return -1;
	}
	return (jint)((sig[0] == SIGNATURE_LONG || sig[0] == SIGNATURE_DOUBLE)
				  ? (void*)twoword_static_address(fb)
				  : (void*)normal_static_address(fb));
}

static JavaObject* JRI_CALLBACK
jri_GetStaticField_ref(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
	JHandle* value;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
	value = (JHandle*)*slot;
    return (JavaObject*)JRIEnv_ref(ee, value);
}

static jbool JRI_CALLBACK
jri_GetStaticField_boolean(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    return (jbool)*slot;
}

static jbyte JRI_CALLBACK
jri_GetStaticField_byte(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    return (jbyte)*slot;
}

static jchar JRI_CALLBACK
jri_GetStaticField_char(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    return (jchar)*slot;
}

static jshort JRI_CALLBACK
jri_GetStaticField_short(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    return (jshort)*slot;
}

static jint JRI_CALLBACK
jri_GetStaticField_int(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    return *slot;
}

static jlong JRI_CALLBACK
jri_GetStaticField_long(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
    JRI_JDK_Java8 tmp;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    return JRI_GET_INT64(tmp, slot);
}

static jfloat JRI_CALLBACK
jri_GetStaticField_float(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    return *(float*)slot;
}

static jdouble JRI_CALLBACK
jri_GetStaticField_double(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID)
{
    OBJECT* slot;
    JRI_JDK_Java8 tmp;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    return JRI_GET_DOUBLE(tmp, slot);
}

/******************************************************************************/

static void JRI_CALLBACK
jri_SetStaticField_ref(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
					   JavaObject* value)
{
    OBJECT* slot;
	JHandle* v;
	sysAssert(fieldID != JRIUninitialized);
    v = JRIEnv_deref(ee, value);
	slot = (OBJECT*)fieldID;
    *slot = (OBJECT)v;
}

static void JRI_CALLBACK
jri_SetStaticField_boolean(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
							jbool value)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    *slot = (OBJECT)value;
}

static void JRI_CALLBACK
jri_SetStaticField_byte(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
						 jbyte value)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    *slot = (OBJECT)value;
}

static void JRI_CALLBACK
jri_SetStaticField_char(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
						 jchar value)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    *slot = (OBJECT)value;
}

static void JRI_CALLBACK
jri_SetStaticField_short(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
						  jshort value)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    *slot = (OBJECT)value;
}

static void JRI_CALLBACK
jri_SetStaticField_int(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
						jint value)
{
    OBJECT* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    *slot = (OBJECT)value;
}

static void JRI_CALLBACK
jri_SetStaticField_long(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
						 jlong value)
{
    OBJECT* slot;
    JRI_JDK_Java8 tmp;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    JRI_SET_INT64(tmp, slot, value);
}

static void JRI_CALLBACK
jri_SetStaticField_float(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
						  jfloat value)
{
    float* slot;
	sysAssert(fieldID != JRIUninitialized);
	slot = (float*)fieldID;
    *slot = value;
}

static void JRI_CALLBACK
jri_SetStaticField_double(JRIEnv* env, jint op, struct java_lang_Class* clazz, jint fieldID,
						   jdouble value)
{
    OBJECT* slot;
    JRI_JDK_Java8 tmp;
	sysAssert(fieldID != JRIUninitialized);
	slot = (OBJECT*)fieldID;
    JRI_SET_DOUBLE(tmp, slot, value);
}

/*******************************************************************************
 * String Operations
 ******************************************************************************/

/* Unicode Interface */

static struct java_lang_String* JRI_CALLBACK
jri_NewString(JRIEnv* env, jint op, const jchar* unicodeChars, jint len)
{
	ExecEnv* ee = NaEnv2EE(env);
    HArrayOfChar* val = (HArrayOfChar *) ArrayAlloc(T_CHAR, len);
    unicode* p;
    struct Hjava_lang_String* result;
    
    if (val == NULL) {
		SignalError(0, JAVAPKG "OutOfMemoryError", 0);
		return NULL;
    }
    p = unhand(val)->body;
    if (unicodeChars) memcpy(p, unicodeChars, (size_t) (len << 1L));
    result = (struct Hjava_lang_String*)
		execute_java_constructor(ee, 0, classJavaLangString, "([C)", val);
    return (struct java_lang_String*)JRIEnv_ref(ee, (JHandle*)result);
}

static Hjava_lang_String* JRI_CALLBACK
jri_GetString(JRIEnv* env, struct java_lang_String* string)
{
	ExecEnv* ee = NaEnv2EE(env);
    struct Hjava_lang_String* s =
		(struct Hjava_lang_String*)JRIEnv_deref(ee, string);
	if (s == NULL) {
		SignalError(ee, JAVAPKG "NullPointerException", NULL);
	}
	else if (obj_classblock(s) != classJavaLangString) {
		SignalError(ee, JAVAPKG "IllegalAccessError", "object is not a String");
	}
	return s;
}

static jint JRI_CALLBACK
jri_GetStringLength(JRIEnv* env, jint op, struct java_lang_String* string)
{
    struct Hjava_lang_String* s = jri_GetString(env, string);
	if (s == NULL) return 0;
    return javaStringLength(s);
}

static const jchar* JRI_CALLBACK
jri_GetStringChars(JRIEnv* env, jint op, struct java_lang_String* string)
{
	ExecEnv* ee = NaEnv2EE(env);
    Classjava_lang_String* str;
    HArrayOfChar* hac;
    unicode* body;
    struct Hjava_lang_String* s = jri_GetString(env, string);
	if (s == NULL) return NULL;
	str = unhand(s);
	hac = (HArrayOfChar*)str->value;
    if (hac == NULL) {	/* XXX can this really happen? */
		SignalError(ee, JAVAPKG "NullPointerException", NULL);
		return NULL;
	}
    body = unhand(hac)->body;
    return (const jchar*)body;
}

/* UTF Interface */

static struct java_lang_String* JRI_CALLBACK
jri_NewStringUTF(JRIEnv* env, jint op, const jbyte* bytes, jint length)
{
    struct Hjava_lang_String* s =
		makeJavaString((char*)bytes, (size_t)length);
    return JRIEnv_ref(NaEnv2EE(env), (JHandle*)s);
}

static jint JRI_CALLBACK
jri_GetStringUTFLength(JRIEnv* env, jint op, struct java_lang_String* string)
{
	ExecEnv* ee = NaEnv2EE(env);
    Classjava_lang_String* str;
    HArrayOfChar* hac;
    unicode* body;
    struct Hjava_lang_String* s = jri_GetString(env, string);
	if (s == NULL) return 0;
	str = unhand(s);
	hac = (HArrayOfChar*)str->value;
    if (hac == NULL) {	/* XXX can this really happen? */
		SignalError(ee, JAVAPKG "NullPointerException", NULL);
		return 0;
	}
    body = unhand(hac)->body;
    return unicode2utfstrlen(body, str->count);
}

static const jbyte* JRI_CALLBACK
jri_GetStringUTFChars(JRIEnv* env, jint op, struct java_lang_String* string)
{
    struct Hjava_lang_String* s = jri_GetString(env, string);
	if (s == NULL) return NULL;
    return (const jbyte*)makeCString(s);
}

/*******************************************************************************
 * Byte Array Operations
 ******************************************************************************/

static JavaObject* JRI_CALLBACK
jri_NewScalarArray(JRIEnv* env, jint op, jint length, const char* elementSig,
				   const jbyte* initialElements)
{
	ExecEnv* ee = NaEnv2EE(env);
    HArrayOfByte* array;
    char* body;
	int elementType;
    
	switch (elementSig[0]) {
	  case SIGNATURE_BYTE:		elementType = T_BYTE; break;
	  case SIGNATURE_CHAR:		elementType = T_CHAR; break;
	  case SIGNATURE_SHORT:		elementType = T_SHORT; break;
	  case SIGNATURE_INT:		elementType = T_INT; break;
	  case SIGNATURE_LONG:		elementType = T_LONG; break;
	  case SIGNATURE_FLOAT:		elementType = T_FLOAT; break;
	  case SIGNATURE_DOUBLE:	elementType = T_DOUBLE; break;
	  default:
		SignalError(ee, JAVAPKG "InstantiationError",
					"invalid scalar array element signature");
		return NULL;
	}
	array = (HArrayOfByte*)ArrayAlloc(elementType, length);
    if (array == NULL) {
		SignalError(ee, JAVAPKG "OutOfMemory", NULL);
		return NULL;
    }

    /* Give the array a "type" */
    body = unhand(array)->body;
    /* Initialize the array */
    if (initialElements != NULL) {
		memcpy(body, initialElements, (size_t)length);
    }
    return (JavaObject*)JRIEnv_ref(ee, (JHandle*)array);
}

static HArrayOfByte* JRI_CALLBACK
jri_GetScalarArray(JRIEnv* env, JavaObject* array)
{
	ExecEnv* ee = NaEnv2EE(env);
    HArrayOfByte* arr = (HArrayOfByte*)JRIEnv_deref(NaEnv2EE(env), array);
	int flags;
	if (arr == NULL) {
		SignalError(ee, JAVAPKG "NullPointerException", NULL);
	}
	else if ((flags = obj_flags(arr), flags == T_NORMAL_OBJECT)
			 || !T_ISNUMERIC(flags)) {
		SignalError(ee, JAVAPKG "IllegalAccessError", "object is not a scalar array");
	}
	return arr;
}

static jint JRI_CALLBACK
jri_GetScalarArrayLength(JRIEnv* env, jint op, JavaObject* array)
{
    HArrayOfByte* arr = jri_GetScalarArray(env, array);
#if defined(XP_PC) && !defined(_WIN32)
    /*
    ** XXX:
    **     This unsed temporary variable is necessary to avoid an intermal compiler error
    **     with MSVC 1.52c
    */
    unsigned long tmp = 0;
#endif
    if (arr == NULL) return 0;
    return obj_length(arr);
}

static jbyte* JRI_CALLBACK
jri_GetScalarArrayElements(JRIEnv* env, jint op, JavaObject* array)
{
    char* body;
    HArrayOfByte* arr = jri_GetScalarArray(env, array);
    if (arr == NULL) return NULL;
    body = unhand(arr)->body;
    return (char*)body;
}

/*******************************************************************************
 * Object Array Operations
 ******************************************************************************/

static JavaObject* JRI_CALLBACK
jri_NewObjectArray(JRIEnv* env, jint op, jint length, struct java_lang_Class* elementClass,
				   JavaObject* initialElement)
{
	ExecEnv* ee = NaEnv2EE(env);
    HArrayOfObject* array;
    ClassClass* cb;
    JHandle** body;

    cb = jri_Ref2Class(ee, elementClass);
    if (cb == NULL) return NULL;

    array = (HArrayOfObject*)ArrayAlloc(T_CLASS, length);
    if (array == NULL) {
		SignalError(ee, JAVAPKG "OutOfMemory", NULL);
		return NULL;
    }

    /* Give the array a "type" */
    body = unhand((HArrayOfObject*)array)->body;
    body[length] = (JHandle*)cb;
    /* Initialize the array */
    if (initialElement != NULL) {
		JHandle* elt = JRIEnv_deref(ee, initialElement);
		jsize i;
		if (!is_instance_of(elt, cb, ee)) {
			SignalError(ee, JAVAPKG "ArrayStoreException", NULL);
			return NULL;
		}
		for (i = 0; i < length; i++) {
			body[i] = elt;
		}
    }
    return (JavaObject*)JRIEnv_ref(ee, (JHandle*)array);
}

static HArrayOfObject* JRI_CALLBACK
jri_GetObjectArray(JRIEnv* env, JavaObject* array)
{
	ExecEnv* ee = NaEnv2EE(env);
    HArrayOfObject* arr = (HArrayOfObject*)JRIEnv_deref(ee, array);
	int flags;
	if (arr == NULL) {
		SignalError(ee, JAVAPKG "NullPointerException", NULL);
	}
	else if ((flags = obj_flags(arr), flags == T_NORMAL_OBJECT)
			 || flags != T_CLASS) {
		SignalError(ee, JAVAPKG "IllegalAccessError", "object is not an Object array");
	}
	return arr;
}

static jint JRI_CALLBACK
jri_GetObjectArrayLength(JRIEnv* env, jint op, JavaObject* array)
{
    HArrayOfObject* arr = jri_GetObjectArray(env, array);
#if defined(XP_PC) && !defined(_WIN32)
    /*
    ** XXX:
    **     This unsed temporary variable is necessary to avoid an intermal compiler error
    **     with MSVC 1.52c
    */
    unsigned long tmp = 0;
#endif
    if (arr == NULL) return 0;
    return obj_length(arr);
}

static JavaObject* JRI_CALLBACK
jri_GetObjectArrayElement(JRIEnv* env, jint op, JavaObject* array, jint index)
{
	ExecEnv* ee = NaEnv2EE(env);
    HArrayOfObject* arr = jri_GetObjectArray(env, array);
    if (arr == NULL) return NULL;
    if (index >= obj_length(arr)) {
		SignalError(ee, JAVAPKG "ArrayIndexOutOfBoundsException", NULL);
		return NULL;
    }
    return (JavaObject*)JRIEnv_ref(ee, unhand(arr)->body[index]);
}

static void JRI_CALLBACK
jri_SetObjectArrayElement(JRIEnv* env, jint op, JavaObject* array, jint index,
						  JavaObject* value)
{
	ExecEnv* ee = NaEnv2EE(env);
	HObject** body;
	JHandle* val;
	ClassClass* elementClass;
	jsize len;
    HArrayOfObject* arr = jri_GetObjectArray(env, array);
    if (arr == NULL) return;
	len = obj_length(arr);
    if (index >= len) {
		SignalError(ee, JAVAPKG "ArrayIndexOutOfBoundsException", NULL);
		return;
    }

	body = unhand(arr)->body;
	elementClass = (ClassClass*)body[len];
	val = JRIEnv_deref(ee, value);
	if (!is_instance_of(val, elementClass, ee)) {
		SignalError(ee, JAVAPKG "ArrayStoreException", NULL);
		return;
	}
    body[index] = val;
}

/*******************************************************************************
 * Native Bootstrap
 ******************************************************************************/

static void JRI_CALLBACK
jri_RegisterNatives(JRIEnv* env, jint op, struct java_lang_Class* clazz,
					char** nameAndSigArray, void** nativeProcArray)
{
	ExecEnv* ee = NaEnv2EE(env);
	ClassClass* cb;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return;

	/* Register Natives */
    for (; *nameAndSigArray != NULL; nameAndSigArray++, nativeProcArray++) {
		struct methodblock* mb;
		jint methodID;
		char* nameAndSig = *nameAndSigArray;
#if 0
		void* nativeProc = *nativeProcArray;
#endif
		char name[256];
		char* n = name;
		char* sig;
		while ((*n++ = *nameAndSig++) != SIGNATURE_FUNC);
		*--n = '\0';
		sig = --nameAndSig;

        /* Passing in cb allows registering of non-universal methods.
         * Users of the JRI are responsible for making sure this is
         * typesafe. */
        methodID = NameAndTypeToHash(name, sig, cb);
		mb = jri_GetMethodBlock(ee, cb, methodID, FALSE, FALSE);
		if (mb == NULL) return;
		if ((mb->fb.access & ACC_NATIVE) != ACC_NATIVE) {
			SignalError(ee, JAVAPKG "NoSuchMethodError", 
						"tried to register undeclared native method");
			return;
		}
        /*
        ** Right now we don't do much with the native procs that were
        ** supplied to us. We still use the jdk dynamic linker strategy
        ** to find the stub routines that invoke the natives.  In the
        ** future, the idea is to emulate the calling conventions on all
        ** the architectures we care about and call these procs
        ** directly. Ambitious yes, but it'll be fast.
        */
		/*mb->code = nativeProc;*/
    }
}

static void JRI_CALLBACK
jri_UnregisterNatives(JRIEnv* env, jint op, struct java_lang_Class* clazz)
{
#if 0 /* Since we don't really register methods now, don't do this either: */
	int nslots;
	struct methodblock* mb;
	ClassClass* cb;

    cb = jri_Ref2Class(NaEnv2EE(env), clazz);
    if (cb == NULL) return;
	for (nslots = 0, mb = cbMethods(cb); nslots < cb->methods_count; nslots++, mb++) {
		mb->code = NULL;
	}
#endif
}

/*******************************************************************************
 * Optional Interfaces
 ******************************************************************************/

/*******************************************************************************
 * Optional Embedding
 ******************************************************************************/

static JavaObject* JRI_CALLBACK
jri_RuntimeGetInterface(void* self, jint id)
{
	return jri_GetInterface(RtEnv2EE(self), id);
}

/* Runtime */

/* !!! This is a non-jump-table entry point: */
JRI_PUBLIC_API(JRIRuntimeInstance*)
JRI_NewRuntime(JRIRuntimeInitargs* initargs)
{
    HThread* self;
    char* errmsg;
    ClassClass * volatile tmpClass;
	
    /* The JDK JRI can only support one runtime instance because its
       state is a bunch of global variables: */
    if (jdkInitialized) return NULL;

    intrInit();

    monitorRegistryInit();
    monitorCacheInit();
	
    /*
    ** We need an ExecEnv to be able to load and run clinit methods in
    ** the classes that the runtime loads during initialization...
    */
    InitializeExecEnv(&_defaultEE, 0);  /* Call before InitializeMem() */
	
    InitializeAlloc(initargs->maxHeapSize, initargs->initialHeapSize);
	
    /* Call before InitializeClassThread() */
    InitializeInterpreter();
	
    self = InitializeClassThread(&_defaultEE, &errmsg);
    if (self == NULL) return NULL;
	
    /* Finish initialization of the main thread */
    InitializeMainThread();
	
    /*
    ** This code is necessary for preemptive threading environments.  Since the 
    ** classes are being stored into global variables, they DO NOT show up on the
    ** C-Stack and are vulnerable to GC until they are made sticky...
    **
    ** Use the volatile stack temporary tmpClass for GC safety...
    */
    tmpClass = FindClass(&_defaultEE, JAVAPKG "NullPointerException", TRUE);
    if (tmpClass == NULL) return NULL;
    tmpClass->flags |= CCF_Sticky;
    classNullPointerException = tmpClass;

    tmpClass = FindClass(&_defaultEE, JAVAPKG "Throwable", TRUE);
    if (tmpClass == NULL) return NULL;
    tmpClass->flags |= CCF_Sticky;
    classThrowable = tmpClass;

    verifyclasses = initargs->verifyMode;
	
    jdkInitialized = JRITrue;
    return jriRuntime;
}

static void JRI_CALLBACK
jri_DisposeRuntime(JRIRuntimeInstance* runtime)
{
    sysAssert(runtime == jriRuntime && jdkInitialized);
    /* JDK does no shutdown stuff (yet). */
    jdkInitialized = (jbool)JRIFalse;
}

static void JRI_CALLBACK
jri_SetIOMode(JRIRuntimeInstance* runtime, JRIIOModeFlags mode)
{
    sysAssert(runtime == jriRuntime && jdkInitialized);
    set_java_io_mode((int)mode);    
}

static void JRI_CALLBACK
jri_SetFSMode(JRIRuntimeInstance* runtime, JRIFSModeFlags mode)
{
    sysAssert(runtime == jriRuntime && jdkInitialized);
    set_java_fs_mode((JavaFSMode)mode);
}

static void JRI_CALLBACK
jri_SetRTMode(JRIRuntimeInstance* runtime, JRIRTModeFlags mode)
{
    sysAssert(runtime == jriRuntime && jdkInitialized);
    set_java_rt_mode((JavaRTMode)mode);
}

/* Environments */

/* from threadruntime.c: */
extern ClassClass* Thread_classblock;
extern HThreadGroup *maingroup;
static int envCount = 0;

static JRIEnv* JRI_CALLBACK
jri_NewEnv(JRIRuntimeInstance* runtime, void* systhread)
{
	ExecEnv* ee;
    struct Classjava_lang_Thread* tid;
    char name[64];
    HThread* jthread;

    if (!(runtime == jriRuntime && jdkInitialized)) return NULL;
    
    jthread = sysThreadGetBackPtr(systhread);
    if (jthread != NULL) 
		ee = (ExecEnv*)unhand(jthread)->eetop;
    else {
		ee = (ExecEnv*)sysMalloc(sizeof(struct execenv));
		if (ee == NULL) return NULL;
	
		/* create the java thread */
		jthread = (HThread*) ObjAlloc(Thread_classblock, 0);
		if (jthread == NULL) {
			sysFree(ee);
			return NULL;
		}
		tid = THREAD(jthread);
	
		/* Need to set unhand(jthread)->eetop before threadBootstrap() */
		unhand(jthread)->eetop = (long)ee;
		unhand(jthread)->group = maingroup;
	
		/* make jthread and systhread point to each other */
		unhand(jthread)->PrivateInfo = (long)systhread;
		sysThreadSetBackPtr(systhread, (void*)jthread);
	
		unhand(jthread)->priority = NormalPriority;
		threadSetPriority(jthread, NormalPriority);
	
		/* Hack alert -- this NULL is supposed to be the stack top, but I know
		   this routine is a no-op (now). */
		threadInit(jthread, NULL);
	
		/* initialize the ExecEnv before invoking the init method */
		InitializeExecEnv(ee, (JHandle*)jthread);

		jio_snprintf(name, sizeof(name), "JRIEnv-%d\0", ++envCount);
	
		execute_java_dynamic_method(ee, (HObject*)jthread, "init", 
									"(Ljava/lang/ThreadGroup;Ljava/lang/Runnable;Ljava/lang/String;)V", 
									maingroup, /* ThreadGroup */
									NULL,      /* runnable */
									makeJavaString(name, strlen(name)));
		if (exceptionOccurred(ee)) {
#ifdef DEBUG
			exceptionDescribe(ee);
#endif
			DeleteExecEnv(ee, (JHandle*)jthread);
			sysFree(ee);
			return NULL;
		}
    }
    return EE2NaEnv(ee);
}

static void JRI_CALLBACK
jri_DisposeEnv(JRIEnv* env)
{
	ExecEnv* ee = NaEnv2EE(env);
    DeleteExecEnv(ee, ee->thread);
    sysFree(ee);
}

static JRIRuntimeInstance* JRI_CALLBACK
jri_GetRuntime(JRIEnv* env)
{
    return jriRuntime;	/* only one */
}

static void* JRI_CALLBACK
jri_GetThread(JRIEnv* env)
{
	ExecEnv* ee = NaEnv2EE(env);
    HThread* thread = (HThread*)ee->thread;
    return (void*)unhand(thread)->PrivateInfo;
}

static void
jri_SetClassLoader(JRIEnv* env, jref classLoader)
{
}

/*******************************************************************************
 * Optional Reflection
 ******************************************************************************/

static JavaObject* JRI_CALLBACK
jri_ReflectionGetInterface(void* self, jint id)
{
	return jri_GetInterface(RfEnv2EE(self), id);
}

static jsize JRI_CALLBACK
jri_GetClassCount(JRIReflectionEnv* env)
{
    return get_nbinclasses();
}

static jref JRI_CALLBACK
jri_GetClass(JRIReflectionEnv* env, jsize index)
{
	ExecEnv* ee = RfEnv2EE(env);
    jsize nclasses;
    ClassClass** cbs;
	jref result = NULL;

	lock_classes();
    nclasses = get_nbinclasses();
    if (index >= nclasses) {
		SignalError(ee, JAVAPKG "ArrayIndexOutOfBoundsException", 
					"no class found at that index");
		goto done;
    }
    cbs = get_binclasses();
    result = JRIEnv_ref(ee, (JHandle*)cbHandle(cbs[index]));
  done:
	unlock_classes();
	return result;
}

static const char* JRI_CALLBACK
jri_GetClassName(JRIReflectionEnv* env, struct java_lang_Class* clazz)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;
    char buf[128];

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return NULL;
    classname2string(classname(cb), buf, sizeof(buf));
    return strdup(buf);
}

extern bool_t VerifyClass(ClassClass *cb);

static jbool JRI_CALLBACK
jri_VerifyClass(JRIReflectionEnv* env, struct java_lang_Class* clazz)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return JRIFalse;
    return VerifyClass(cb) ? JRITrue : JRIFalse;
}

static jref JRI_CALLBACK
jri_GetClassSuperclass(JRIReflectionEnv* env, struct java_lang_Class* clazz)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return NULL;
    return JRIEnv_ref(ee, (JHandle*)cbSuperclass(cb));
}

static jsize JRI_CALLBACK
jri_GetClassInterfaceCount(JRIReflectionEnv* env, struct java_lang_Class* clazz)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return 0;
    return cb->implements_count;
}

static jref JRI_CALLBACK
jri_GetClassInterface(JRIReflectionEnv* env, struct java_lang_Class* clazz, jsize index)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;
    short intf;
    struct Hjava_lang_Class* ch;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return NULL;
    if (index >= cb->implements_count) {
		SignalError(ee, JAVAPKG "ArrayIndexOutOfBoundsException", 
					"native method tried to access interface at illegal index");
		return NULL;
    }
    intf = cbImplements(cb)[index];
    ResolveClassConstant(cb->constantpool, intf, ee,
						 1 << CONSTANT_Class);
    ch = cbHandle((ClassClass *)(cb->constantpool[intf].p));
    return JRIEnv_ref(ee, (JHandle*)ch);
}

static jsize JRI_CALLBACK
jri_GetClassFieldCount(JRIReflectionEnv* env, struct java_lang_Class* clazz)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return 0;
 	if (makeslottable(cb) == NULL)
		return 0;
    return cbSlotTableSize(cb);
}

static void JRI_CALLBACK
jri_GetClassFieldInfo(JRIReflectionEnv* env, struct java_lang_Class* clazz, jsize fieldIndex,
					  char* *fieldName, char* *fieldSig, 
					  JRIAccessFlags *fieldAccess, jref *fieldClass)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;
    jsize nslots;
    struct fieldblock* fb;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return;
    nslots = cbSlotTableSize(cb);
    if (fieldIndex >= nslots) {
		SignalError(ee, JAVAPKG "ArrayIndexOutOfBoundsException", 
					"native method tried to access field at illegal index");
		return;
    }
    fb = cbSlotTable(cb)[fieldIndex];
    if (fb == NULL) {	/* XXX can this really happen? */
		SignalError(ee, JAVAPKG "NoSuchFieldError", NULL);
		return;
    }
    if (fieldName != NULL)
		*fieldName = strdup(fieldname(fb));
    if (fieldSig != NULL)
		*fieldSig = strdup(fieldsig(fb));
#if 0
    if (fieldID != NULL)
		*fieldID = (JavaIdent)fb->ID;
#endif
    if (fieldAccess != NULL)
		*fieldAccess = (JRIAccessFlags)fb->access;
    if (fieldClass != NULL) {
		ClassClass* cb = fieldclass(fb);
		*fieldClass = JRIEnv_ref(ee, (JHandle*)cbHandle(cb));
    }
}

static jsize JRI_CALLBACK
jri_GetClassMethodCount(JRIReflectionEnv* env, struct java_lang_Class* clazz)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return 0;
    return cb->methods_count;
}

static void JRI_CALLBACK
jri_GetClassMethodInfo(JRIReflectionEnv* env, struct java_lang_Class* clazz, jsize methodIndex,
					   char* *methodName, char* *methodSig, 
					   JRIAccessFlags *methodAccess,
					   jref *methodClass, void* *methodNativeProc)
{
	ExecEnv* ee = RfEnv2EE(env);
    ClassClass* cb;
    jsize nmeths;
    struct methodblock* mb;
    struct fieldblock* fb;

    cb = jri_Ref2Class(ee, clazz);
    if (cb == NULL) return;
    nmeths = cb->methods_count;
    if (methodIndex >= nmeths) {
		SignalError(ee, JAVAPKG "ArrayIndexOutOfBoundsException", 
					"native method tried to access method at illegal index");
		return;
    }
    mb = &cbMethods(cb)[methodIndex];
    if (mb == NULL) {	/* XXX can this really happen? */
		SignalError(ee, JAVAPKG "NoSuchMethodError", NULL);
		return;
    }
    fb = &mb->fb;
    if (methodName != NULL)
		*methodName = strdup(fieldname(fb));
    if (methodSig != NULL)
		*methodSig = strdup(fieldsig(fb));
#if 0
    if (methodID != NULL)
		*methodID = strdup(fieldsig(fb));
#endif
    if (methodAccess != NULL)
		*methodAccess = (JRIAccessFlags)fb->access;
    if (methodClass != NULL)
		*methodClass = JRIEnv_ref(ee, (JHandle*)cbHandle(fieldclass(fb)));
    if (methodNativeProc != NULL)
		*methodNativeProc = mb->code;
}

/*******************************************************************************
 * Optional Debugger
 ******************************************************************************/

static JavaObject* JRI_CALLBACK
jri_DebuggerGetInterface(void* self, jint id)
{
	return jri_GetInterface(DbEnv2EE(self), id);
}

/* Manipulating Stacks */

static jsize JRI_CALLBACK
jri_GetFrameCount(JRIDebuggerEnv* env)
{
	ExecEnv* ee = DbEnv2EE(env);
    jsize count = 0;
    JavaFrame* jframe;

    jframe = ee->current_frame;
    while (jframe) {
		jframe = jframe->prev;
        if (jframe->current_method != 0)
			count++;
    }
    return count;
}

static JavaFrame* JRI_CALLBACK
jri_FindFrame(ExecEnv* ee, jsize frameIndex)
{
    JavaFrame* jframe;
    int fr = (int)frameIndex;

    jframe = ee->current_frame;
    while (jframe) {
        /* skip C stack frames */
        if (jframe->current_method == 0) {
            jframe = jframe->prev;
        }
        if (--fr >= 0 && jframe) {
            jframe = jframe->prev;
        } else {
            break;
        }
    }
    if (jframe == NULL) {
		SignalError(ee, JAVAPKG "ArrayIndexOutOfBoundsException", 
					"native method tried to access frame at illegal index");
	}
    return jframe;
}

static jbool JRI_CALLBACK
jri_GetFrameInfo(JRIDebuggerEnv* env, jsize frameIndex,
				  jref *methodClass, jsize *methodIndex,
				  jsize *pc, jsize *varsCount)
{
	ExecEnv* ee = DbEnv2EE(env);
    JavaFrame* frame;
    struct methodblock* mb;

    frame = jri_FindFrame(ee, frameIndex);
    if (frame == NULL) return JRIFalse;
    mb = frame->current_method;
    if (mb != NULL) {
		/* a normal method */
		ClassClass *cb = fieldclass(&mb->fb);
		if (methodClass != NULL) {
			*methodClass = JRIEnv_ref(ee, (JHandle*)cbHandle(cb));
		}
		if (methodIndex != NULL) {
			/* XXX the method index should get cached on mb to avoid
			   this loop */
			int count = cb->methods_count;
			struct methodblock* mbs = cbMethods(cb);
			int i;
			for (i = 0; i < count; i++) {
				if (mb->fb.ID == mbs->fb.ID)
					break;
			}
			sysAssert(i < count);
			*methodIndex = i;
		}
		if (pc != NULL)
			*pc = frame->lastpc - mb->code;
		if (varsCount != NULL)
			*varsCount = mb->localvar_table_length;
		return JRITrue;
    }
    else {
		/* a native method */
		return JRIFalse;
    }
}

static void JRI_CALLBACK
jri_GetSourceInfo(JRIDebuggerEnv* env, jsize frameIndex,
				   const char* *filename, jsize *lineNumber)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static struct localvar* JRI_CALLBACK
jri_FindLocalVar(ExecEnv* ee, JavaFrame* frame, jsize varIndex,
			 struct methodblock* *mbptr)
{
    struct methodblock* mb;

    mb = frame->current_method;
    if (mb == NULL) {
		SignalError(ee, JAVAPKG "IllegalAccessError",
					"native method tried to access a variable of a native method");
		return NULL;
    }
    if (mbptr != NULL)
		*mbptr = mb;
    if (varIndex >= mb->localvar_table_length) {
		SignalError(ee, JAVAPKG "ArrayIndexOutOfBoundsException", 
					"native method tried to access variable at illegal index");
		return NULL;
    }
    return &mb->localvar_table[varIndex];
}

static void JRI_CALLBACK
jri_GetVarInfo(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
			   char* *name, char* *signature, jbool *isArgument,
			   jsize *startScope, jsize *endScope)
{
	ExecEnv* ee = DbEnv2EE(env);
    JavaFrame* frame;
    struct methodblock* mb;
    struct localvar* lv;

    frame = jri_FindFrame(ee, frameIndex);
    if (frame == NULL) return;
    lv = jri_FindLocalVar(ee, frame, varIndex, &mb);
    if (lv == NULL) return;
#if 0
    if (id != NULL) {
		/*
		** The jdk doesn't really have identifiers for stack variables,
		** but we'll fabricate one for consistency with the way object
		** field info is returned.
		**
        ** DJH: NameAndTypeToHash now takes the class in which names are
        ** to be resolved as a parameter. What should we use in this case?
        ** A debugging environment should probably have a ClassLoader
        ** associated with it.
        ** Doesn't matter for 3.01 because this is #if'd out.
        */
		*id = (JavaIdent)
			NameAndTypeToHash(frame->constant_pool[lv->nameoff].cp,
							  frame->constant_pool[lv->sigoff].cp,
                              /* what class should we put here? */);
    }
#endif
    if (name != NULL)
		*name = strdup(frame->constant_pool[lv->nameoff].cp);
    if (signature != NULL)
		*signature = strdup(frame->constant_pool[lv->sigoff].cp);
    if (isArgument != NULL)
		*isArgument = (lv->slot < mb->args_size);
    if (startScope != NULL)
		*startScope = lv->pc0;
    if (endScope != NULL)
		*endScope = lv->pc0 + lv->length;
}

static long JRI_CALLBACK
jri_GetVarAddr(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
    JavaFrame* frame;
    struct localvar* lv;
    jsize currentPC;
    long slot = -1;

    frame = jri_FindFrame(ee, frameIndex);
    if (frame == NULL) return -1;
    lv = jri_FindLocalVar(ee, frame, varIndex, NULL);
    if (lv == NULL) return -1;
    currentPC = (long)frame->lastpc - (long)frame->current_method->code;
    if (currentPC >= lv->pc0 && currentPC <= (lv->pc0 + lv->length)) {
		slot = lv->slot;
    }
    return slot;
}

static jref JRI_CALLBACK
jri_GetVar(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	long slot = jri_GetVarAddr(env, frameIndex, varIndex);
    return JRIEnv_ref(ee, *(JHandle**)slot);
}

static jbool JRI_CALLBACK
jri_GetVar_boolean(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
	return (jbool)JRIFalse;
}

static jbyte JRI_CALLBACK
jri_GetVar_byte(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
	return 0;
}

static jchar JRI_CALLBACK
jri_GetVar_char(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
	return 0;
}

static jshort JRI_CALLBACK
jri_GetVar_short(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
	return 0;
}

static jint JRI_CALLBACK
jri_GetVar_int(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
	return 0;
}

static jlong JRI_CALLBACK
jri_GetVar_long(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
	return jlong_ZERO;
}

static jfloat JRI_CALLBACK
jri_GetVar_float(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
	return 0.0f;
}

static jdouble JRI_CALLBACK
jri_GetVar_double(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
	return 0.0;
}

/******************************************************************************/

static void JRI_CALLBACK
jri_SetVar(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
			jref value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_SetVar_boolean(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
					  jbool value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_SetVar_byte(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
				   jbyte value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_SetVar_char(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
				   jchar value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_SetVar_short(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
					jshort value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_SetVar_int(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
				  jint value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_SetVar_long(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
				   jlong value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_SetVar_float(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
					jfloat value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_SetVar_double(JRIDebuggerEnv* env, jsize frameIndex, jsize varIndex,
					 jdouble value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

/******************************************************************************/

/* Controlling Execution */

static void JRI_CALLBACK
jri_StepOver(JRIDebuggerEnv* env)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_StepIn(JRIDebuggerEnv* env)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_StepOut(JRIDebuggerEnv* env)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_Continue(JRIDebuggerEnv* env)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

static void JRI_CALLBACK
jri_Return(JRIDebuggerEnv* env, jsize frameIndex, JRIValue value)
{
	ExecEnv* ee = DbEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

/*******************************************************************************
 * Optional Compiling
 ******************************************************************************/

static JavaObject* JRI_CALLBACK
jri_CompilerGetInterface(void* self, jint id)
{
	ExecEnv* ee = CpEnv2EE(self);
	return jri_GetInterface(ee, id);
}

static void JRI_CALLBACK
jri_CompileClass(JRICompilerEnv* env, const char* classSrc, jsize classSrcLen,
				 jbyte* *resultingClassData, jsize *classDataLen)
{
	ExecEnv* ee = CpEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
}

/*******************************************************************************
 * Optional Expression Evaluation
 ******************************************************************************/

static JavaObject* JRI_CALLBACK
jri_ExprGetInterface(void* self, jint id)
{
	return jri_GetInterface(ExEnv2EE(self), id);
}

static jref JRI_CALLBACK
jri_CompileExpr(JRIExprEnv* env, const char* exprSrc, jsize exprSrcLen)
{
	ExecEnv* ee = ExEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
    return NULL;
}

static jref JRI_CALLBACK
jri_EvalExpr(JRIExprEnv* env, jref expr)
{
	ExecEnv* ee = ExEnv2EE(env);
	SignalError(ee, JAVAPKG "InternalError", "not implemented");
    return NULL;
}

/*******************************************************************************
 * JDK Specific Things
 ******************************************************************************/

#ifndef HAVE_LONG_LONG
#ifdef IS_LITTLE_ENDIAN
jlong jlong_MAXINT = { 0xffffffff, 0x7fffffff };
jlong jlong_MININT = { 0x00000000, 0x80000000 };
#else
jlong jlong_MAXINT = { 0x7fffffff, 0xffffffff };
jlong jlong_MININT = { 0x80000000, 0x00000000 };
#endif
jlong jlong_ZERO   = { 0x00000000, 0x00000000 };

JRI_PUBLIC_API(void)
jlong_udivmod(julong *qp, julong *rp, julong a, julong b)
{
    sysAssert(0);	/* XXX */
}

#endif /* !HAVE_LONG_LONG */

/******************************************************************************/

const JRIEnvInterface jri_NativeInterface = {
	NULL,
	(void*)jri_NativeGetInterface,
	NULL,
	NULL,
	jri_FindClass,
	jri_Throw,
	jri_ThrowNew,
	jri_ExceptionOccurred,
	jri_ExceptionDescribe,
	jri_ExceptionClear,
	jri_NewGlobalRef,
	jri_DisposeGlobalRef,
	jri_GetGlobalRef,
	jri_SetGlobalRef,
	jri_IsSameObject,
	jri_NewObject,
	jri_NewObjectV,
	jri_NewObjectA,
	jri_GetObjectClass,
	jri_IsInstanceOf,
	jri_GetMethodID,
	jri_CallMethod_ref,
	jri_CallMethod_refV,
	jri_CallMethod_refA,
	jri_CallMethod_boolean,
	jri_CallMethod_booleanV,
	jri_CallMethod_booleanA,
	jri_CallMethod_byte,
	jri_CallMethod_byteV,
	jri_CallMethod_byteA,
	jri_CallMethod_char,
	jri_CallMethod_charV,
	jri_CallMethod_charA,
	jri_CallMethod_short,
	jri_CallMethod_shortV,
	jri_CallMethod_shortA,
	jri_CallMethod_int,
	jri_CallMethod_intV,
	jri_CallMethod_intA,
	jri_CallMethod_long,
	jri_CallMethod_longV,
	jri_CallMethod_longA,
	jri_CallMethod_float,
	jri_CallMethod_floatV,
	jri_CallMethod_floatA,
	jri_CallMethod_double,
	jri_CallMethod_doubleV,
	jri_CallMethod_doubleA,
	jri_GetFieldID,
	jri_GetField_ref,
	jri_GetField_boolean,
	jri_GetField_byte,
	jri_GetField_char,
	jri_GetField_short,
	jri_GetField_int,
	jri_GetField_long,
	jri_GetField_float,
	jri_GetField_double,
	jri_SetField_ref,
	jri_SetField_boolean,
	jri_SetField_byte,
	jri_SetField_char,
	jri_SetField_short,
	jri_SetField_int,
	jri_SetField_long,
	jri_SetField_float,
	jri_SetField_double,
	jri_IsSubclassOf,
	jri_GetMethodID,
	jri_CallStaticMethod_ref,
	jri_CallStaticMethod_refV,
	jri_CallStaticMethod_refA,
	jri_CallStaticMethod_boolean,
	jri_CallStaticMethod_booleanV,
	jri_CallStaticMethod_booleanA,
	jri_CallStaticMethod_byte,
	jri_CallStaticMethod_byteV,
	jri_CallStaticMethod_byteA,
	jri_CallStaticMethod_char,
	jri_CallStaticMethod_charV,
	jri_CallStaticMethod_charA,
	jri_CallStaticMethod_short,
	jri_CallStaticMethod_shortV,
	jri_CallStaticMethod_shortA,
	jri_CallStaticMethod_int,
	jri_CallStaticMethod_intV,
	jri_CallStaticMethod_intA,
	jri_CallStaticMethod_long,
	jri_CallStaticMethod_longV,
	jri_CallStaticMethod_longA,
	jri_CallStaticMethod_float,
	jri_CallStaticMethod_floatV,
	jri_CallStaticMethod_floatA,
	jri_CallStaticMethod_double,
	jri_CallStaticMethod_doubleV,
	jri_CallStaticMethod_doubleA,
	jri_GetStaticFieldID,
	jri_GetStaticField_ref,
	jri_GetStaticField_boolean,
	jri_GetStaticField_byte,
	jri_GetStaticField_char,
	jri_GetStaticField_short,
	jri_GetStaticField_int,
	jri_GetStaticField_long,
	jri_GetStaticField_float,
	jri_GetStaticField_double,
	jri_SetStaticField_ref,
	jri_SetStaticField_boolean,
	jri_SetStaticField_byte,
	jri_SetStaticField_char,
	jri_SetStaticField_short,
	jri_SetStaticField_int,
	jri_SetStaticField_long,
	jri_SetStaticField_float,
	jri_SetStaticField_double,
	jri_NewString,
	jri_GetStringLength,
	jri_GetStringChars,
	jri_NewStringUTF,
	jri_GetStringUTFLength,
	jri_GetStringUTFChars,
	jri_NewScalarArray,
	jri_GetScalarArrayLength,
	jri_GetScalarArrayElements,
	jri_NewObjectArray,
	jri_GetObjectArrayLength,
	jri_GetObjectArrayElement,
	jri_SetObjectArrayElement,
	jri_RegisterNatives,
	jri_UnregisterNatives,
	jri_DefineClass
};

const JRIRuntimeInterface jri_RuntimeInterface = {
	(void*)jri_RuntimeGetInterface,
	(void*)jri_AddRef,
	(void*)jri_Release,
	NULL,
	jri_DisposeRuntime,
	jri_SetIOMode,
	jri_SetFSMode,
	jri_SetRTMode,
	jri_NewEnv,
	jri_DisposeEnv,
	jri_GetRuntime,
	jri_GetThread,
	jri_SetClassLoader
};

const JRIReflectionInterface jri_ReflectionInterface = {
	(void*)jri_ReflectionGetInterface,
	(void*)jri_AddRef,
	(void*)jri_Release,
	NULL,
	jri_GetClassCount,
	jri_GetClass,
	jri_GetClassName,
	jri_VerifyClass,
	jri_GetClassSuperclass,
	jri_GetClassInterfaceCount,
	jri_GetClassInterface,
	jri_GetClassFieldCount,
	jri_GetClassFieldInfo,
	jri_GetClassMethodCount,
	jri_GetClassMethodInfo
};

const JRIDebuggerInterface jri_DebuggerInterface = {
	(void*)jri_DebuggerGetInterface,
	(void*)jri_AddRef,
	(void*)jri_Release,
	NULL,
	jri_GetFrameCount,
	jri_GetFrameInfo,
	jri_GetVarInfo,
	jri_GetSourceInfo,
	jri_GetVar,
	jri_GetVar_boolean,
	jri_GetVar_byte,
	jri_GetVar_char,
	jri_GetVar_short,
	jri_GetVar_int,
	jri_GetVar_long,
	jri_GetVar_float,
	jri_GetVar_double,
	jri_SetVar,
	jri_SetVar_boolean,
	jri_SetVar_byte,
	jri_SetVar_char,
	jri_SetVar_short,
	jri_SetVar_int,
	jri_SetVar_long,
	jri_SetVar_float,
	jri_SetVar_double,
	jri_StepOver,
	jri_StepIn,
	jri_StepOut,
	jri_Continue,
	jri_Return
};

const JRICompilerInterface jri_CompilerInterface = {
	(void*)jri_CompilerGetInterface,
	(void*)jri_AddRef,
	(void*)jri_Release,
	NULL,
	jri_CompileClass
};

const JRIExprInterface jri_ExprInterface = {
	(void*)jri_ExprGetInterface,
	(void*)jri_AddRef,
	(void*)jri_Release,
	NULL,
	jri_CompileExpr,
	jri_EvalExpr
};

/******************************************************************************/