/* -*- Mode: C; c-file-style: "gnu" -*-
   class.c -- native methods for java/lang/Class.
   Created: Chris Toshok <toshok@hungry.com>, 28-Jul-1997
 */
/*
  This file is part of Japhar, the GNU Virtual Machine for Java Bytecodes.
  Japhar is a project of The Hungry Programmers, GNU, and OryxSoft.

  Copyright (C) 1997, 1998, 1999 The Hungry Programmers

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "jniint.h"
#include "compat.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>

#define MYLOG "Native"

jclass jlClass = NULL;
jclass jlClassArray;
jclass jlrMember;
jclass jlrMethod;
jclass jlrConstructor;
jclass jlrField;

jfieldID Member_DECLARED;
jfieldID Member_PUBLIC;

jint MEMBER_PUBLIC;
jint MEMBER_DECLARED;

static void initids(JNIEnv *env)
{
  if (jlClass)
    return;

  jlClass = (*env)->FindClass(env, "java/lang/Class");
  jlClassArray = (*env)->FindClass(env, "[Ljava/lang/Class;");
  jlrMember = (*env)->FindClass(env, "java/lang/reflect/Member");
  jlrField = (*env)->FindClass(env, "java/lang/reflect/Field");
  jlrMethod = (*env)->FindClass(env, "java/lang/reflect/Method");
  jlrConstructor = (*env)->FindClass(env, "java/lang/reflect/Constructor");

  Member_DECLARED = (*env)->GetStaticFieldID(env, jlrMember, "DECLARED", "I");
  Member_PUBLIC = (*env)->GetStaticFieldID(env, jlrMember, "PUBLIC", "I");
  MEMBER_DECLARED = (*env)->GetStaticIntField(env, jlrMember, Member_DECLARED);
  MEMBER_PUBLIC = (*env)->GetStaticIntField(env, jlrMember, Member_PUBLIC);
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_forName(JNIEnv *env,
			     jclass cls,
			     jstring class_name)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jobject clazz_loaded;
  char *cls_name = HVM_StringToCString(henv, class_name);

  initids(env);

  clazz_loaded = (jobject)(*env)->FindClass(env, cls_name);

  free(cls_name);

  if ((*env)->ExceptionOccurred(env))
    return NULL;
  else
    return clazz_loaded;
}

/* JDK1.2 */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_forName0(JNIEnv *env,
			      jclass cls,
			      jstring class_name,
			      jboolean flag,
			      jobject classloader)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  char *cls_name = HVM_StringToCString(henv, class_name);
  jobject clazz_loaded;

  initids(env);

  clazz_loaded = (jobject)(*env)->FindClass(env, cls_name);

  free(cls_name);

  if ((*env)->ExceptionOccurred(env))
    return NULL;
  else
    return clazz_loaded;
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_newInstance(JNIEnv *env,
				 jobject clazz)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *cf;
  jmethodID constructor;

  assert(NULL != env);
  assert(NULL != clazz);

  initids(env);

  cf = jclass_to_clazzfile(henv, clazz);
  assert(NULL != cf);
  constructor = (*env)->GetMethodID(env, clazz, "<init>", "()V");

  if ((*env)->ExceptionOccurred(env))
    {
      char buf[1024];
      PR_snprintf(buf, sizeof(buf), "in java.lang.Class.newInstance, for class %s",
		  getClassName(env, cf));

      (*env)->ExceptionClear(env);

      HVM_ExceptionThrow(henv, "java/lang/InstantiationException", buf);
      
      return NULL;
    }
  
  /* probably need more checks here... */
  if (constructor->access_flags & ACC_PRIVATE)
    {
      char buf[1024];
      PR_snprintf(buf, sizeof(buf),
		  "in java.lang.Class.newInstance, "
		  "inaccessible constructor for class %s",
		  getClassName(env, cf));

      HVM_ExceptionThrow(henv, "java/lang/IllegalAccessException", buf);

      return NULL;
    }

  return (*env)->NewObject(env, clazz, constructor, NULL);
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_newInstance0(JNIEnv *env,
				  jobject clazz)
{
  initids(env);

  return Java_java_lang_Class_newInstance(env, clazz);
}

/* is this right? */
JNIEXPORT jboolean JNICALL
Java_java_lang_Class_isInstance(JNIEnv *env,
				jobject cls,
				jobject obj)
{
  initids(env);

  return (*env)->IsInstanceOf(env, obj, cls);
}

JNIEXPORT jboolean JNICALL
Java_java_lang_Class_isAssignableFrom(JNIEnv *env,
				      jobject clazz,
				      jobject clazz2)
{
  initids(env);

  return (*env)->IsAssignableFrom(env, clazz, clazz2);
}

JNIEXPORT jboolean JNICALL
Java_java_lang_Class_isInterface(JNIEnv *env,
				 jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, obj);

  initids(env);

  return (clazz->access_flags & ACC_INTERFACE) != 0;
}

JNIEXPORT jboolean JNICALL
Java_java_lang_Class_isArray(JNIEnv *env,
			     jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, obj);

  initids(env);

  return (clazz->access_flags & ACC_ARRAY) != 0;
}

JNIEXPORT jboolean JNICALL
Java_java_lang_Class_isPrimitive(JNIEnv *env,
				 jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz;

  initids(env);

  if (obj == NULL) return JNI_FALSE;

  clazz = jclass_to_clazzfile(henv, obj);

  return (clazz->access_flags & ACC_PRIMITIVE) != 0;
}

JNIEXPORT jstring JNICALL
Java_java_lang_Class_getName(JNIEnv *env,
			     jobject obj)
{ 
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *cf;
  char *cls_name;
  jstring str;

  initids(env);
  
  if (obj == NULL)
    return NULL;

  cf = jclass_to_clazzfile(henv, obj);

  cls_name = jstrdup(henv, getClassName(henv, cf));
  if (cls_name == NULL)
    return NULL;

  slashes_to_dots(cls_name);

  str = (*env)->NewStringUTF(env, cls_name);

  free(cls_name);

  return str;
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getClassLoader(JNIEnv *env,
				    jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *cf = jclass_to_clazzfile(henv, obj);

  initids(env);

  return cf->classloader;
}

/* JDK1.2 */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getClassLoader0(JNIEnv *env,
				     jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *cf = jclass_to_clazzfile(henv, obj);

  initids(env);

  return cf->classloader;
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getSuperclass(JNIEnv *env,
				   jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, obj);
  ClazzFile *super;

  initids(env);

  super = getSuperClass(henv, clazz);

  if (super)
    return (jobject)clazzfile_to_jclass(henv, super);
  else
    return NULL;
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getInterfaces(JNIEnv *env,
				   jobject clazz)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz_cf = jclass_to_clazzfile(henv, clazz);
  jobjectArray array;
  int array_length = 0;
  int array_index;
  ClazzFile *cur_class;

  initids(env);

  cur_class = clazz_cf;
  while (cur_class)
    {
      array_length += cur_class->num_interfaces;
      cur_class = getSuperClass(henv, cur_class);
    }

  array = (*env)->NewObjectArray(env, array_length,
				 jlClassArray, NULL);

  cur_class = clazz_cf;
  array_index = 0;
  while (cur_class)
    {
      int i;

      for (i = 0; i < cur_class->num_interfaces; i++)
	{
	  ClazzFile *interface_class = clazz_cf->interface_tuples[i].interface;
	  jobject interface_obj = clazzfile_to_jclass(henv, interface_class);
		  
	  (*env)->SetObjectArrayElement(env, array,
					array_index,
					interface_obj);
	  array_index ++;
	}

      cur_class = getSuperClass(henv, cur_class);
    }

  return array;
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getComponentType(JNIEnv *env,
				      jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, obj);
  jclass componentType;

  initids(env);

  if ((clazz->access_flags & ACC_ARRAY) == 0)
    return NULL;

  /* getClassName()'s result is incremented to skip leading '[' */
  componentType = (*env)->FindClass(env, getClassName(env, clazz) + 1);
  return componentType;
}

JNIEXPORT jint JNICALL
Java_java_lang_Class_getModifiers(JNIEnv *env,
				  jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, obj);

  initids(env);

  return clazz->access_flags;
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getSigners(JNIEnv *env,
				jobject obj)
{
  initids(env);

  (*env)->FatalError(env, "java.lang.Class.getSigners unimplemented");
  return NULL;
}

JNIEXPORT void JNICALL
Java_java_lang_Class_setSigners(JNIEnv *env,
				jobject obj,
				jobjectArray signers)
{
  initids(env);

  (*env)->FatalError(env, "java.lang.Class.setSigners unimplemented");
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getPrimitiveClass(JNIEnv *env,
				       jclass cls,
				       jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  const jbyte *bytes = (jbyte*)(*env)->GetStringUTFChars(env, obj, NULL);
  jobject result;

  initids(env);

  result = (jobject) HVM_ClassFindPrimitive(henv, (char*)bytes);

  (*env)->ReleaseStringUTFChars(env, obj, bytes);

  return result;
}

/*
 * For Classpath
 */
JNIEXPORT jclass JNICALL
Java_java_lang_VMClassLoader_getPrimitiveClass(JNIEnv *env,
                                               jclass cls,
                                               jstring className)
{
  return(Java_java_lang_Class_getPrimitiveClass(env, cls, className));
}

/**
 * Determine if the given method should be seen
 * based on whether the user is asking for public
 * or declared
 */
static jboolean
method_access_ok(MethodStruct * m, jint which, ClazzFile * clazz)
{
  return (which == MEMBER_DECLARED && m->clazz == clazz)
	  || (which == MEMBER_PUBLIC && (m->access_flags & ACC_PUBLIC));
}

/**
 * Determine if the given field should be seen
 * based on whether the user is asking for public
 * or declared
 */
static jboolean
field_access_ok(FieldStruct * f, jint which, ClazzFile * clazz)
{
  return (which == MEMBER_DECLARED && f->clazz == clazz)
	  || (which == MEMBER_PUBLIC && (f->access_flags & ACC_PUBLIC));
}

/**
 * Counts the number of fields in the specified
 * class.  If which is MEMBER_PUBLIC, then all public fields
 * are counted including those inherited from super classes/interfaces.
 * If which is MEMBER_DECLARED, then all fields in the
 * specified class are counted (without counting super
 * classes/interfaces).
 */
static int
countFields(JNIEnv *env, ClazzFile *clazz, jint which)
{
  int nflds = 0;
  int i;
  for (i = 0; i < clazz->num_fields; ++i) 
    {
      if (field_access_ok(clazz->fields[i], which, clazz))
	{
	  ++nflds;
	}
      
    }
  return nflds;
}

static void
fill_field_array(JNIEnv *env, ClazzFile *clazz, jint which,
		 jobjectArray array)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  int i;
  int fldnum = 0;

  for (i = 0; i < clazz->num_fields; ++i) 
    {
      if (field_access_ok(clazz->fields[i], which, clazz))
	{
	  jobject field = (*env)->ToReflectedField(env, clazzfile_to_jclass(henv, clazz), clazz->fields[i]);
	  (*env)->SetObjectArrayElement(env, array, fldnum++, field);
        }
    }
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getFields0(JNIEnv *env,
				jobject obj,
				jint which)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, obj);
  jmethodID field_ctor;
  jobjectArray array;
  int nflds;

  initids(env);

  nflds = countFields(env, clazz, which);
  array = (*env)->NewObjectArray(env, nflds, jlrField, NULL);

  if (0 < nflds)
    fill_field_array(env, clazz, which, array);

  return array;
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getFields(JNIEnv *env,
			       jobject cls)
{
  initids(env);

  return(Java_java_lang_Class_getFields0(env, cls, MEMBER_PUBLIC));
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getDeclaredFields(JNIEnv *env,
			      	       jobject cls)
{
  initids(env);

  return(Java_java_lang_Class_getFields0(env, cls, MEMBER_DECLARED));
}

/**
 * Returns whether the supplied Java Class array matches
 * the supplied method signature.
 */
static jboolean
params_match(JNIEnv *env, jobjectArray params, HVMMethodSigStruct *sig)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  int i;
  jsize size = 0;
  
  if (params != NULL) {
    size = (*env)->GetArrayLength(env, params);
  }
  
  /* Parameter counts do not match... no need to bother... */
  if (size != sig->num_params) {
    return JNI_FALSE;
  }
  
  for (i = 0; i < sig->num_params; ++i) {
    jclass sigClazz = sig_to_jclass(henv, sig->params[i]);
    jclass paramClazz = (jclass) (*env)->GetObjectArrayElement(env, params, i);
    if (paramClazz != sigClazz) {
      return JNI_FALSE;
    }
  }
  
  return JNI_TRUE;
}

/**
 * Returns whether the supplied method is a constructor or not.
 */
static jboolean
isConstructor(MethodStruct* method)
{
  /* XXX is there a better way to recognize constructors? */
  return strcmp(method->name, "<init>") == 0;
}

/**
 * Determines whether the method method_name (with supplied
 * signature) which was defined in definedClazz is overridden
 * in a descendant class (anywhere in the path from clazz up to
 * definedClazz).
 */
static jboolean
overridden(JNIEnv *env, ClazzFile *clazz, char *method_name,
	   char *sig_str, ClazzFile *definedClazz)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  /*
    Methods in interfaces are never overridden (this is the JDK 1.1.6
    behaviour, haven't checked specs)
  */
  if (definedClazz->access_flags & ACC_INTERFACE) 
    {
      return JNI_FALSE;
    }

  while (clazz != NULL && clazz != definedClazz) 
    {
      MethodStruct *method = GetMethodByNameAndSig(henv, clazz, method_name, sig_str);
      if (method != NULL) 
	{
	  return JNI_TRUE;
	}
      clazz = getSuperClass(henv, clazz);
    }
  return JNI_FALSE;
}

/**
 * Counts the number of methods (when constructors == JNI_FALSE) or
 * constructors (when constructors == JNI_TRUE) in the specified
 * class.  If which is MEMEBER_PUBLIC, then all public methods
 * are counted including those inherited from super classes/interfaces
 * If which is MEMBER_DECLARED, then all methods in the
 * specified class are counted (without counting super
 * classes/interfaces).
 */
static int
countMethods(JNIEnv *env, ClazzFile *orig_clazz, ClazzFile *clazz,
	     jint which, jboolean constructors)
{
  int nmethods = 0;
  int i;

  if (which != MEMBER_DECLARED && !constructors) 
    {
      if (clazz->access_flags & ACC_INTERFACE) 
	{
	  for (i = 0; i < clazz->num_interfaces; ++i) 
	    {
	      nmethods += countMethods(env, orig_clazz,
				       clazz->interface_tuples[i].interface,
				       which, constructors);
	    }
	}
    }
  for (i = 0; i < clazz->num_methods; ++i) 
    {
      if (which == MEMBER_DECLARED && clazz->methods[i].clazz != clazz)
	continue;

      if (isConstructor(&clazz->methods[i])) 
	{
	  if (!constructors) 
	    {
	      continue;
	    }
	}
      else 
	{
	  if (constructors) 
	    {
	      continue;
	    }
	}

      if (method_access_ok(&clazz->methods[i], which, clazz)
	  &&
	  (constructors || !overridden(env, orig_clazz,
				       clazz->methods[i].name, clazz->methods[i].sig_str, clazz)))
	{
	  ++nmethods;
	}
    }
  return nmethods;
}

static int
fill_method_array(JNIEnv *env, ClazzFile *orig_clazz, ClazzFile *clazz,
		  jint which, jclass class_class,
		  jclass method_class,
		  jobjectArray array, int methodnum, jboolean constructors)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  int i;

  for (i = 0; i < clazz->num_methods; ++i) 
    {
      if (which == MEMBER_DECLARED && clazz->methods[i].clazz != clazz)
	continue;

      if (isConstructor(&clazz->methods[i])) 
	{
	  if (!constructors) 
	    {
	      continue;
	    }
	}
      else 
	{
	  if (constructors) 
	    {
	      continue;
	    }
	}

      if (method_access_ok(&clazz->methods[i], which, clazz) &&
	  (constructors || !overridden(env, orig_clazz,
				       clazz->methods[i].name, clazz->methods[i].sig_str, clazz))) 
	{
	  jobject method = (*env)->ToReflectedMethod(env, clazzfile_to_jclass(henv, clazz),
						     &clazz->methods[i]);
	  (*env)->SetObjectArrayElement(env, array, methodnum, method);
	  methodnum++;
	}
    }
  
  if (which == MEMBER_PUBLIC && !constructors) 
    {
      if (clazz->access_flags & ACC_INTERFACE) 
	{
	  for (i = 0; i < clazz->num_interfaces; ++i) 
	    {
	      methodnum = fill_method_array(env, orig_clazz,
					    clazz->interface_tuples[i].interface,
					    which, class_class, method_class,
					    array, methodnum, constructors);
	    }
	}
    }
  return methodnum;
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getMethods0(JNIEnv *env,
				 jobject obj,
				 jint which)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, obj);
  jobjectArray array;
  int nmethods;

  initids(env);

  nmethods = countMethods(env, clazz, clazz, which,
			  JNI_FALSE);

  array = (*env)->NewObjectArray(env, nmethods, jlrMethod, NULL);
  
  fill_method_array(env, clazz, clazz, which, jlClass,
		    jlrMethod, array, 0, JNI_FALSE);
  return array;
}

/* For Classpath, not JDK */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getMethods(JNIEnv *env,
				jobject obj)
{
  initids(env);

  return(Java_java_lang_Class_getMethods0(env, obj, MEMBER_PUBLIC));
}

/* For Classpath, not JDK */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getDeclaredMethods(JNIEnv *env,
					jobject obj)
{
  initids(env);

  return(Java_java_lang_Class_getMethods0(env, obj, MEMBER_DECLARED));
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getConstructors0(JNIEnv *env,
				      jobject obj,
				      jint which)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, obj);
  jobjectArray array;
  int nmethods;

  initids(env);
  
  nmethods = countMethods(env, clazz, clazz, which,
			  JNI_TRUE);

  array = (*env)->NewObjectArray(env, nmethods, jlrConstructor, NULL);

  fill_method_array(env, clazz, clazz, which, jlClass,
		    jlrConstructor, 
		    array, 0, JNI_TRUE);
  return array;
}

/* For Classpath, not JDK */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getConstructors(JNIEnv *env,
				     jobject obj)
{
  initids(env);

  return(Java_java_lang_Class_getConstructors0(env, obj, MEMBER_PUBLIC));
}

/* For Classpath, not JDK */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getDeclaredConstructors(JNIEnv *env,
				     	     jobject obj)
{
  initids(env);

  return(Java_java_lang_Class_getConstructors0(env, obj, MEMBER_DECLARED));
}

static jobject
find_field(JNIEnv *env, ClazzFile *clazz,
	   jint which, const char *name)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  int i;
  jobject field = NULL;
  
  for (i = 0; i < clazz->num_fields; ++i) 
    {
      if (field_access_ok(clazz->fields[i], which, clazz) &&
	  !strcmp(clazz->fields[i]->name, name)) 
	{
	  return (*env)->ToReflectedField(env, clazzfile_to_jclass(henv, clazz),
					  clazz->fields[i]);
	}
    }
  
  if (!(which==MEMBER_DECLARED)) 
    {
      ClazzFile *superClass = getSuperClass(henv, clazz);
      if (superClass != NULL) 
	{
	  field = find_field(env, superClass, which, name);
	  if (field != NULL) 
	    {
	      return field;
	    }
	}
      for (i = 0; i < clazz->num_interfaces; ++i) 
	{
	  field = find_field(env, clazz->interface_tuples[i].interface,
			     which, name);
	  if (field != NULL) 
	    {
	      return field;
	    }
	}
    }
  return NULL;
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getField0(JNIEnv *env,
			       jobject cls,
			       jstring jname,
			       jint which)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, cls);
  char *name = HVM_StringToCString(henv, jname);
  jobject field;

  initids(env);
  
  field = find_field(env, clazz, which, name);

  if (field == NULL) 
    {
      HVM_ExceptionThrow(henv, "java/lang/NoSuchFieldException", name);
    }
  free(name);
  return field;
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getField(JNIEnv *env,
			      jobject cls,
			      jstring jname)
{
  return(Java_java_lang_Class_getField0(env, cls, jname, MEMBER_PUBLIC));
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getDeclaredField(JNIEnv *env,
			      	      jobject cls,
			      	      jstring jname)
{
  return(Java_java_lang_Class_getField0(env, cls, jname, MEMBER_DECLARED));
}

static jobject
find_method(JNIEnv *env, ClazzFile *clazz, jint which,
	    jclass class_class, jclass method_class,
	    const char *name, jobjectArray parameterTypes,
	    jboolean constructor)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  jobject method = NULL;
  int i;
  
  for (i = 0; i < clazz->num_methods; ++i) 
    {
      HVMSignature *sig;

      if (isConstructor(&clazz->methods[i])) 
	{
	  if (!constructor) 
	    {
	      continue;
	    }
	}
      else 
	{
	  if (constructor || strcmp(clazz->methods[i].name, name) != 0) 
	    {
	      continue;
	    }
	}

      sig = HVM_SigParseFromJavaSig(henv, clazz->methods[i].sig_str);

      if (method_access_ok(&clazz->methods[i], which, clazz) &&
	  params_match(env, parameterTypes,
		       &sig->method)) 
	{
	  HVM_SigFree(henv, sig);
	  return (*env)->ToReflectedMethod(env, clazzfile_to_jclass(henv, clazz),
					   &clazz->methods[i]);
	}
      HVM_SigFree(henv, sig);
    }
  
  if (which == MEMBER_PUBLIC && !constructor) 
    {
      if (clazz->access_flags & ACC_INTERFACE) 
	{
	  for (i = 0; i < clazz->num_interfaces; ++i) 
	    {
	      method = find_method(env, clazz->interface_tuples[i].interface,
				   which, class_class, method_class,
				   name, parameterTypes, constructor);
	      if (method != NULL) 
		{
		  return method;
		}
	    }
	}
      else 
	{
	  ClazzFile *superClass = getSuperClass(henv, clazz);
	  if (superClass != NULL) 
	    {
	      method = find_method(env, superClass, which,
				   class_class, method_class,
				   name, parameterTypes,
				   constructor);
	      if (method != NULL) 
		{
		  return method;
		}
	    }
	}
    }
  return NULL;
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getMethod0(JNIEnv *env,
				jobject cls,
				jstring jname,
				jobjectArray parameterTypes,
				jint which)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, cls);
  char *name = HVM_StringToCString(henv, jname);
  jobject method;

  initids(env);
  
  method = find_method(env, clazz, which,
		       jlClass, jlrMethod, name, parameterTypes, JNI_FALSE);

  if (method == NULL) 
    {
      HVM_ExceptionThrow(henv, "java/lang/NoSuchMethodException", name);
    }

  free(name);

  return method;
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getMethod(JNIEnv *env,
			       jobject cls,
			       jstring jname,
		       	       jobjectArray parameterTypes)
{
  initids(env);

  return(Java_java_lang_Class_getMethod0(env, cls, jname, parameterTypes,
					 MEMBER_PUBLIC));
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getDeclaredMethod(JNIEnv *env,
			       	       jobject cls,
			       	       jstring jname,
		       	       	       jobjectArray parameterTypes)
{
  initids(env);

  return(Java_java_lang_Class_getMethod0(env, cls, jname, parameterTypes,
					 MEMBER_DECLARED));
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getConstructor0(JNIEnv *env,
				     jobject cls,
				     jobjectArray parameterTypes,
				     jint which)
{
  HungryEnv *henv = HVM_ThreadGetEnv();
  ClazzFile *clazz = jclass_to_clazzfile(henv, cls);

  jobject method;

  initids(env);

  method = find_method(env, clazz, which,
		       jlClass, jlrConstructor, NULL,
		       parameterTypes, JNI_TRUE);

  if (method == NULL) 
    {
      HVM_ExceptionThrow(henv, "java/lang/NoSuchMethodException", "<init>");
    }
  return method;
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getConstructor(JNIEnv *env,
				    jobject cls,
				    jobjectArray parameterTypes)
{
  initids(env);

  return(Java_java_lang_Class_getConstructor0(env, cls, parameterTypes,
					      MEMBER_PUBLIC));
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getDeclaredConstructor(JNIEnv *env,
				    	    jobject cls,
					    jobjectArray parameterTypes)
{
  initids(env);

  return(Java_java_lang_Class_getConstructor0(env, cls, parameterTypes,
					      MEMBER_DECLARED));
}

/* For Classpath, not JDK */
JNIEXPORT jclass JNICALL
Java_java_lang_Class_getClasses(JNIEnv *env, 
				jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();

  initids(env);

  HVM_ExceptionThrow(henv, "java/lang/UnsupportedOperationException",
                  "java.lang.Class.getClasses()");
  return(0);
}

/* For Classpath, not JDK */
JNIEXPORT jclass JNICALL
Java_java_lang_Class_getDeclaredClasses(JNIEnv *env, 
					jobject obj)
{
  HungryEnv *henv = HVM_ThreadGetEnv();

  initids(env);

  HVM_ExceptionThrow(henv, "java/lang/UnsupportedOperationException",
                  "java.lang.Class.getDeclaredClasses()");
  return(0);
}

/* JDK1.2 */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getDeclaredClasses0(JNIEnv *env,
					 jobject cls)
{
  initids(env);

  (*env)->FatalError(env, "getDeclaredClasses0 not implemented yet.");
  return NULL;
}

/* JDK1.2 & Classpath */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getDeclaringClass(JNIEnv *env,
				       jobject cls)
{
  initids(env);

  (*env)->FatalError(env, "getDeclaringClass not implemented yet.");
  return NULL;
}

/* JDK1.2 */
JNIEXPORT void JNICALL
Java_java_lang_Class_setProtectionDomain0(JNIEnv *env,
					  jobject cls,
					  jobject protection_domain)
{
  initids(env);

  (*env)->FatalError(env, "setProtectionDomain not implemented yet.");
}

/* JDK1.2 */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getProtectionDomain0(JNIEnv *env,
					  jobject obj)
{
  initids(env);

  (*env)->FatalError(env, "Java_java_lang_Class_getProtectionDomain0 not implemented");
  return NULL;
}

/* JDK 1.2 uses registration of native methods... */
static JNINativeMethod class_native_methods[] = {
  { "forName0",	"(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;", (void*)Java_java_lang_Class_forName0 },
  { "getClassLoader0",	"()Ljava/lang/ClassLoader;", (void*)Java_java_lang_Class_getClassLoader0 },
  { "getComponentType", "()Ljava/lang/Class;", (void*)Java_java_lang_Class_getComponentType },
  { "getConstructor0", "([Ljava/lang/Class;I)Ljava/lang/reflect/Constructor;", (void*)Java_java_lang_Class_getConstructor0 },
  { "getConstructors0", "(I)[Ljava/lang/reflect/Constructor;", (void*)Java_java_lang_Class_getConstructors0 },
  { "getDeclaredClasses0", "()[Ljava/lang/Class;", (void*)Java_java_lang_Class_getDeclaredClasses0 },
  { "getDeclaringClass", "()Ljava/lang/Class;",  (void*)Java_java_lang_Class_getDeclaringClass },
  { "getField0", "(Ljava/lang/String;I)Ljava/lang/reflect/Field;", (void*)Java_java_lang_Class_getField0 },
  { "getFields0", "(I)[Ljava/lang/reflect/Field;", (void*)Java_java_lang_Class_getFields0 },
  { "getInterfaces", "()[Ljava/lang/Class;", (void*)Java_java_lang_Class_getInterfaces },
  { "getMethod0", "(Ljava/lang/String;[Ljava/lang/Class;I)Ljava/lang/reflect/Method;", (void*)Java_java_lang_Class_getMethod0 },
  { "getMethods0", "(I)[Ljava/lang/reflect/Method;", (void*)Java_java_lang_Class_getMethods0 },
  { "getModifiers", "()I", (void*)Java_java_lang_Class_getModifiers },
  { "getName", "()Ljava/lang/String;", (void*)Java_java_lang_Class_getName },
  { "getPrimitiveClass", "(Ljava/lang/String;)Ljava/lang/Class;", (void*)Java_java_lang_Class_getPrimitiveClass },
  { "getProtectionDomain0", "()Ljava/security/ProtectionDomain;", (void*)Java_java_lang_Class_getProtectionDomain0 },
  { "getSigners", "()[Ljava/lang/Object;", (void*)Java_java_lang_Class_getSigners },
  { "getSuperclass", "()Ljava/lang/Class;", (void*)Java_java_lang_Class_getSuperclass },
  { "isArray", "()Z", (void*)Java_java_lang_Class_isArray },
  { "isAssignableFrom", "(Ljava/lang/Class;)Z", (void*)Java_java_lang_Class_isAssignableFrom },
  { "isInstance", "(Ljava/lang/Object;)Z", (void*)Java_java_lang_Class_isInstance },
  { "isInterface", "()Z", (void*)Java_java_lang_Class_isInterface },
  { "isPrimitive", "()Z", (void*)Java_java_lang_Class_isPrimitive },
  { "newInstance0", "()Ljava/lang/Object;", (void*)Java_java_lang_Class_newInstance0 },
  { "setProtectionDomain0", "(Ljava/security/ProtectionDomain;)V", (void*)Java_java_lang_Class_setProtectionDomain0 },
  { "setSigners", "([Ljava/lang/Object;)V", (void*)Java_java_lang_Class_setSigners }
};
static int num_class_native_methods = sizeof(class_native_methods) / sizeof(class_native_methods[0]);

JNIEXPORT void JNICALL
Java_java_lang_Class_registerNatives(JNIEnv *env,
				     jclass cls)
{
#ifdef HAVE_LIBFFI
  (*env)->RegisterNatives(env, cls,
			  class_native_methods,
			  num_class_native_methods);
#endif
}
