/* -*- 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 "interp.h"
#include "objects.h"
#include "sig.h"
#include "compat.h"
#include "primitive-class.h"
#include "exceptions.h"
#include "log.h"
#include "util.h"
#include <string.h>
#include <stdlib.h>
#include <assert.h>

#define MYLOG "Native"

/**
 * WARNING: returned pointer must be freed by caller
 */
static char *
jstring2charptr(JNIEnv *env, jstring jstr)
{
  char *str_copy;
  const jbyte *str_chars;
  int str_length;
  str_chars = (*env)->GetStringUTFChars(env, jstr, NULL);
  str_length = (*env)->GetStringUTFLength(env, jstr) + 1;
  str_copy = (char*) malloc(str_length * sizeof(char));
  strncpy(str_copy, (char*)str_chars, str_length);
  (*env)->ReleaseStringUTFChars(env, jstr, str_chars);
  str_copy[str_length-1] = 0; /* Make sure the string is zero terminated */
  return str_copy;
}

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

  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)
{
  jobject clazz_loaded;
  char *cls_name = jstring2charptr(env, class_name);

  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)
{
  ClazzFile *cf;
  jmethodID constructor;

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

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

  if (!constructor)
    {
      char buf[1024];
      snprintf(buf, sizeof(buf), "in java.lang.Class.newInstance, for class %s",
	       getClassName(env, cf));

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

      throw_Exception(env, "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)
{
  ClazzFile *cf;
  jmethodID constructor;

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

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

  if (!constructor)
    {
      char buf[1024];
      snprintf(buf, sizeof(buf), "in java.lang.Class.newInstance, for class %s",
	       getClassName(env, cf));

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

      throw_Exception(env, "java/lang/IllegalAccessException", buf);

      return NULL;
    }

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

/* is this right? */
JNIEXPORT jboolean JNICALL
Java_java_lang_Class_isInstance(JNIEnv *env,
				jobject cls,
				jobject obj)
{
  return (*env)->IsInstanceOf(env, obj, cls);
}

JNIEXPORT jboolean JNICALL
Java_java_lang_Class_isAssignableFrom(JNIEnv *env,
				      jobject clazz,
				      jobject clazz2)
{
  return (*env)->IsAssignableFrom(env, clazz, clazz2);
}

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

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

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

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

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

  if (obj == NULL) return JNI_FALSE;

  clazz = jclass_to_clazzfile(env, obj);

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

JNIEXPORT jstring JNICALL
Java_java_lang_Class_getName(JNIEnv *env,
			     jobject obj)
{
  ClazzFile *cf = jclass_to_clazzfile(env, obj);
  char *cls_name = strdup(getClassName(env, cf));
  jstring str;
  
  if (obj == 0) return 0;

  slashes_to_dots(cls_name);

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

  free(cls_name);

  return str;
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getClassLoader(JNIEnv *env,
				    jobject obj)
{
  ClazzFile *cf = jclass_to_clazzfile(env, obj);
  return cf->classloader;
}

/* JDK1.2 */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getClassLoader0(JNIEnv *env,
				     jobject obj)
{
  ClazzFile *cf = jclass_to_clazzfile(env, obj);
  return cf->classloader;
}

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

  super = getSuperClass(env, clazz);

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

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getInterfaces(JNIEnv *env,
				   jobject clazz)
{
  ClazzFile *clazz_cf = jclass_to_clazzfile(env, clazz);
  jclass array_class = (*env)->FindClass(env, "[Ljava/lang/Class;");
  jobjectArray array;
  int array_length = 0;
  int array_index;
  ClazzFile *cur_class;

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

  array = (*env)->NewObjectArray(env, array_length,
				 array_class, 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->interfaces[i];
	  jobject interface_obj = clazzfile_to_jclass(env, interface_class);
		  
	  (*env)->SetObjectArrayElement(env, array,
					array_index,
					interface_obj);
	  array_index ++;
	}

      cur_class = getSuperClass(env, cur_class);
    }

  array = (*env)->NewGlobalRef(env, array);

  return array;
}

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

  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)
{
  ClazzFile *clazz = jclass_to_clazzfile(env, obj);

  return clazz->access_flags;
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getSigners(JNIEnv *env,
				jobject obj)
{
  (*env)->FatalError(env, "java.lang.Class.getSigners unimplemented");
  return NULL;
}

JNIEXPORT void JNICALL
Java_java_lang_Class_setSigners(JNIEnv *env,
				jobject obj,
				jobjectArray signers)
{
  (*env)->FatalError(env, "java.lang.Class.setSigners unimplemented");
}

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

  result = (jobject) createFakePrimitiveClass(env, (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));
}

/*
   XXX These should probably be obtained from java.lang.reflect.Member
   instead of being hard-coded here.
*/
#define MEMBER_PUBLIC 0
#define MEMBER_DECLARED 1

/**
 * Determine if the given field or method flag matches the
 * only_declared value that is being searched.
 */
static jboolean
access_is_ok(int field_flags, jboolean only_declared)
{
  return (only_declared || (field_flags & ACC_PUBLIC));
}

/**
 * Counts the number of fields in the specified
 * class.  If only_declared is JNI_FALSE, then all public fields
 * are counted including those inherited from super classes/interfaces.
 * If only_declared is JNI_TRUE, then all fields in the
 * specified class are counted (without counting super
 * classes/interfaces).
 */
static int
countFields(JNIEnv *env, ClazzFile *clazz, jboolean only_declared)
{
  int nflds = 0;
  int i;
  if (!only_declared) 
    {
      ClazzFile *superClass = getSuperClass(env, clazz);
      if (superClass != NULL) 
	{
	  nflds += countFields(env, superClass, only_declared);
	}
      for (i = 0; i < clazz->num_interfaces; ++i) 
	{
	  nflds += countFields(env, clazz->interfaces[i],
			       only_declared);
	}
    }
  for (i = 0; i < clazz->num_fields; ++i) 
    {
      if (access_is_ok(clazz->fields[i]->access_flags, only_declared)) 
	{
	  ++nflds;
	}
    }
  return nflds;
}

static int
fill_field_array(JNIEnv *env, ClazzFile *clazz, jboolean only_declared,
		 jclass field_class, jmethodID field_ctor,
		 jfieldID slot_field, jfieldID type_field, jfieldID name_field,
		 jfieldID clazz_field, jobjectArray array, int fldnum)
{
  int i;
  
  for (i = 0; i < clazz->num_fields; ++i) 
    {
      if (access_is_ok(clazz->fields[i]->access_flags, only_declared)) 
	{
	  jobject field = (*env)->ToReflectedField(env, clazzfile_to_jclass(env, clazz), clazz->fields[i]);
	  (*env)->SetObjectArrayElement(env, array, fldnum, field);
	  fldnum++;
	}
    }

  if (!only_declared) 
    {
      ClazzFile *superClass = getSuperClass(env, clazz);
      if (superClass != NULL) 
	{
	  fldnum = fill_field_array(env, superClass, only_declared,
				    field_class, field_ctor, slot_field, type_field,
				    name_field, clazz_field, array, fldnum);
	}
      for (i = 0; i < clazz->num_interfaces; ++i) 
	{
	  fldnum = fill_field_array(env, clazz->interfaces[i],
				    only_declared, field_class, field_ctor, slot_field,
				    type_field, name_field, clazz_field, array, fldnum);
	}
    }
  return fldnum;
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getFields0(JNIEnv *env,
				jobject obj,
				jint which)
{
  jclass member_class = (*env)->FindClass(env,
					  "java/lang/reflect/Member");
  jfieldID declared_field = (*env)->GetStaticFieldID(env, member_class,
						     "DECLARED", "I");
  jint declared_value = (*env)->GetStaticIntField(env, member_class,
						  declared_field);
  jboolean only_declared = (which == declared_value);
  ClazzFile *clazz = jclass_to_clazzfile(env, obj);
  jclass field_class = (*env)->FindClass(env, "java/lang/reflect/Field");
  jmethodID field_ctor;
  jobjectArray array;
  jfieldID slot_field = (*env)->GetFieldID(env, field_class, "slot", "I");
  jfieldID type_field = (*env)->GetFieldID(env, field_class, "type", "Ljava/lang/Class;");
  jfieldID name_field = (*env)->GetFieldID(env, field_class, "name", "Ljava/lang/String;");
  jfieldID clazz_field = (*env)->GetFieldID(env, field_class, "clazz", "Ljava/lang/Class;");
  int nflds = countFields(env, clazz, only_declared);
  array = (*env)->NewObjectArray(env, nflds, field_class, NULL);
  field_ctor = (*env)->GetMethodID(env, field_class, "<init>", "()V");

  fill_field_array(env, clazz, only_declared, field_class, field_ctor,
		   slot_field, type_field, name_field, clazz_field, array, 0);
  array = (*env)->NewGlobalRef(env, array);
  return array;
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getFields(JNIEnv *env,
			       jobject cls)
{
  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)
{
  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, MethodSigStruct *sig)
{
  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(env, 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)
{
  /*
    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(env, clazz, method_name, sig_str);
      if (method != NULL) 
	{
	  return JNI_TRUE;
	}
      clazz = getSuperClass(env, clazz);
    }
  return JNI_FALSE;
}

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

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

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

      if (access_is_ok(clazz->methods[i]->access_flags, only_declared) &&
	  (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,
		  jboolean only_declared, jclass class_class,
		  jclass method_class, jmethodID method_ctor, jfieldID clazz_field,
		  jfieldID slot_field, jfieldID name_field, jfieldID returnType_field,
		  jfieldID parameterTypes_field, jfieldID exceptionTypes_field,
		  jobjectArray array, int methodnum, jboolean constructors)
{
  int i;

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

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

      if (access_is_ok(clazz->methods[i]->access_flags, only_declared) &&
	  (constructors || !overridden(env, orig_clazz,
				       clazz->methods[i]->name, clazz->methods[i]->sig_str, clazz))) 
	{
	  jobject method = (*env)->ToReflectedMethod(env, clazzfile_to_jclass(env, clazz),
						     clazz->methods[i]);
	  (*env)->SetObjectArrayElement(env, array, methodnum, method);
	  methodnum++;
	}
    }
  
  if (!only_declared && !constructors) 
    {
      if (clazz->access_flags & ACC_INTERFACE) 
	{
	  for (i = 0; i < clazz->num_interfaces; ++i) 
	    {
	      methodnum = fill_method_array(env, orig_clazz,
					    clazz->interfaces[i],
					    only_declared, class_class, method_class, method_ctor,
					    clazz_field, slot_field, name_field, returnType_field,
					    parameterTypes_field, exceptionTypes_field,
					    array, methodnum, constructors);
	    }
	}
    }
  return methodnum;
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getMethods0(JNIEnv *env,
				 jobject obj,
				 jint which)
{
  jclass member_class = (*env)->FindClass(env,
					  "java/lang/reflect/Member");
  jfieldID declared_field = (*env)->GetStaticFieldID(env, member_class,
						     "DECLARED", "I");
  jint declared_value = (*env)->GetStaticIntField(env, member_class,
						  declared_field);
  jboolean only_declared = (which == declared_value);
  ClazzFile *clazz = jclass_to_clazzfile(env, obj);
  jclass class_class = (*env)->FindClass(env, "java/lang/Class");
  jclass method_class = (*env)->FindClass(env, "java/lang/reflect/Method");
  jobjectArray array;
  jfieldID clazz_field = (*env)->GetFieldID(env, method_class, "clazz", "Ljava/lang/Class;");
  jfieldID slot_field = (*env)->GetFieldID(env, method_class, "slot", "I");
  jfieldID name_field = (*env)->GetFieldID(env, method_class, "name", "Ljava/lang/String;");
  jfieldID returnType_field = (*env)->GetFieldID(env, method_class, "returnType", "Ljava/lang/Class;");
  jfieldID parameterTypes_field = (*env)->GetFieldID(env, method_class, "parameterTypes", "[Ljava/lang/Class;");
  jfieldID exceptionTypes_field = (*env)->GetFieldID(env, method_class, "exceptionTypes", "[Ljava/lang/Class;");
  jmethodID method_ctor = (*env)->GetMethodID(env, method_class, "<init>", "()V");
  int nmethods = countMethods(env, clazz, clazz, only_declared,
			      JNI_FALSE);
  array = (*env)->NewObjectArray(env, nmethods, method_class, NULL);
  
  fill_method_array(env, clazz, clazz, only_declared, class_class,
		    method_class, method_ctor, clazz_field, slot_field, name_field,
		    returnType_field, parameterTypes_field, exceptionTypes_field,
		    array, 0, JNI_FALSE);
  array = (*env)->NewGlobalRef(env, array);
  return array;
}

/* For Classpath, not JDK */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getMethods(JNIEnv *env,
				jobject obj)
{
  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)
{
  return(Java_java_lang_Class_getMethods0(env, obj, MEMBER_DECLARED));
}

JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getConstructors0(JNIEnv *env,
				      jobject obj,
				      jint which)
{
  jclass member_class = (*env)->FindClass(env,
					  "java/lang/reflect/Member");
  jfieldID declared_field = (*env)->GetStaticFieldID(env, member_class,
						     "DECLARED", "I");
  jint declared_value = (*env)->GetStaticIntField(env, member_class,
						  declared_field);
  jboolean only_declared = (which == declared_value);
  ClazzFile *clazz = jclass_to_clazzfile(env, obj);
  jclass class_class = (*env)->FindClass(env, "java/lang/Class");
  jclass method_class = (*env)->FindClass(env, "java/lang/reflect/Constructor");
  jobjectArray array;
  jfieldID clazz_field = (*env)->GetFieldID(env, method_class, "clazz", "Ljava/lang/Class;");
  jfieldID slot_field = (*env)->GetFieldID(env, method_class, "slot", "I");
  jfieldID parameterTypes_field = (*env)->GetFieldID(env, method_class, "parameterTypes", "[Ljava/lang/Class;");
  jfieldID exceptionTypes_field = (*env)->GetFieldID(env, method_class, "exceptionTypes", "[Ljava/lang/Class;");
  jmethodID method_ctor = (*env)->GetMethodID(env, method_class, "<init>", "()V");
  int nmethods = countMethods(env, clazz, clazz, only_declared,
			      JNI_TRUE);
  array = (*env)->NewObjectArray(env, nmethods, method_class, NULL);
  
  fill_method_array(env, clazz, clazz, only_declared, class_class,
		    method_class, method_ctor, clazz_field, slot_field, NULL,
		    NULL, parameterTypes_field, exceptionTypes_field,
		    array, 0, JNI_TRUE);
  array = (*env)->NewGlobalRef(env, array);
  return array;
}

/* For Classpath, not JDK */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getConstructors(JNIEnv *env,
				     jobject obj)
{
  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)
{
  return(Java_java_lang_Class_getConstructors0(env, obj, MEMBER_DECLARED));
}

static jobject
find_field(JNIEnv *env, ClazzFile *clazz,
	   jboolean only_declared, char *name)
{
  int i;
  jobject field = NULL;
  
  for (i = 0; i < clazz->num_fields; ++i) 
    {
      if (access_is_ok(clazz->fields[i]->access_flags, only_declared) &&
	  !strcmp(clazz->fields[i]->name, name)) 
	{
	  return (*env)->ToReflectedField(env, clazzfile_to_jclass(env, clazz),
					  clazz->fields[i]);
	}
    }
  
  if (!only_declared) 
    {
      ClazzFile *superClass = getSuperClass(env, clazz);
      if (superClass != NULL) 
	{
	  field = find_field(env, superClass, only_declared, name);
	  if (field != NULL) 
	    {
	      return field;
	    }
	}
      for (i = 0; i < clazz->num_interfaces; ++i) 
	{
	  field = find_field(env, clazz->interfaces[i],
			     only_declared, name);
	  if (field != NULL) 
	    {
	      return field;
	    }
	}
    }
  return NULL;
}

JNIEXPORT jobject JNICALL
Java_java_lang_Class_getField0(JNIEnv *env,
			       jobject cls,
			       jstring jname,
			       jint which)
{
  jclass member_class = (*env)->FindClass(env,
					  "java/lang/reflect/Member");
  jfieldID declared_field = (*env)->GetStaticFieldID(env, member_class,
						     "DECLARED", "I");
  jint declared_value = (*env)->GetStaticIntField(env, member_class,
						  declared_field);
  jboolean only_declared = (which == declared_value);
  ClazzFile *clazz = jclass_to_clazzfile(env, cls);
  char *name = jstring2charptr(env, jname);
  jobject field = find_field(env, clazz, only_declared, name);
  
  if (field == NULL) 
    {
      throw_Exception(env, "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, jboolean only_declared,
	    jclass class_class, jclass method_class, jfieldID clazz_field,
	    jfieldID slot_field, jfieldID name_field, jfieldID returnType_field,
	    jfieldID parameterTypes_field, jfieldID exceptionTypes_field,
	    jmethodID method_ctor, char *name, jobjectArray parameterTypes,
	    jboolean constructor)
{
  jobject method = NULL;
  int i;
  
  for (i = 0; i < clazz->num_methods; ++i) 
    {
      Signature *sig;

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

      sig = SIG_parseFromJavaSig(env, clazz->methods[i]->sig_str);

      if (access_is_ok(clazz->methods[i]->access_flags, only_declared) &&
	  params_match(env, parameterTypes,
		       &sig->method)) 
	{
	  SIG_free(env, sig);
	  return (*env)->ToReflectedMethod(env, clazzfile_to_jclass(env, clazz),
					   clazz->methods[i]);
	}
      SIG_free(env, sig);
    }
  
  if (!only_declared && !constructor) 
    {
      if (clazz->access_flags & ACC_INTERFACE) 
	{
	  for (i = 0; i < clazz->num_interfaces; ++i) 
	    {
	      method = find_method(env, clazz->interfaces[i],
				   only_declared, class_class, method_class,
				   clazz_field, slot_field, name_field, returnType_field,
				   parameterTypes_field, exceptionTypes_field,
				   method_ctor, name, parameterTypes, constructor);
	      if (method != NULL) 
		{
		  return method;
		}
	    }
	}
      else 
	{
	  ClazzFile *superClass = getSuperClass(env, clazz);
	  if (superClass != NULL) 
	    {
	      method = find_method(env, superClass, only_declared,
				   class_class, method_class, clazz_field, slot_field,
				   name_field, returnType_field, parameterTypes_field,
				   exceptionTypes_field, method_ctor, 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)
{
  jclass member_class = (*env)->FindClass(env,
					  "java/lang/reflect/Member");
  jfieldID declared_field = (*env)->GetStaticFieldID(env, member_class,
						     "DECLARED", "I");
  jint declared_value = (*env)->GetStaticIntField(env, member_class,
						  declared_field);
  jboolean only_declared = (which == declared_value);
  ClazzFile *clazz = jclass_to_clazzfile(env, cls);
  char *name = jstring2charptr(env, jname);
  jclass class_class = (*env)->FindClass(env, "java/lang/Class");
  jclass method_class = (*env)->FindClass(env,
					  "java/lang/reflect/Method");
  jfieldID clazz_field = (*env)->GetFieldID(env, method_class,
					    "clazz", "Ljava/lang/Class;");
  jfieldID slot_field = (*env)->GetFieldID(env, method_class,
					   "slot", "I");
  jfieldID name_field = (*env)->GetFieldID(env, method_class,
					   "name", "Ljava/lang/String;");
  jfieldID returnType_field = (*env)->GetFieldID(env, method_class,
						 "returnType", "Ljava/lang/Class;");
  jfieldID parameterTypes_field = (*env)->GetFieldID(env, method_class,
						     "parameterTypes", "[Ljava/lang/Class;");
  jfieldID exceptionTypes_field = (*env)->GetFieldID(env, method_class,
						     "exceptionTypes", "[Ljava/lang/Class;");
  jmethodID method_ctor = (*env)->GetMethodID(env, method_class,
					      "<init>", "()V");
  jobject method = find_method(env, clazz, only_declared,
			       class_class, method_class, clazz_field, slot_field, name_field,
			       returnType_field, parameterTypes_field, exceptionTypes_field,
			       method_ctor, name, parameterTypes, JNI_FALSE);
  
  if (method == NULL) 
    {
      throw_Exception(env, "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)
{
  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)
{
  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)
{
  jclass member_class = (*env)->FindClass(env,
					  "java/lang/reflect/Member");
  jfieldID declared_field = (*env)->GetStaticFieldID(env, member_class,
						     "DECLARED", "I");
  jint declared_value = (*env)->GetStaticIntField(env, member_class,
						  declared_field);
  jboolean only_declared = (which == declared_value);
  ClazzFile *clazz = jclass_to_clazzfile(env, cls);
  jclass class_class = (*env)->FindClass(env, "java/lang/Class");
  jclass method_class = (*env)->FindClass(env,
					  "java/lang/reflect/Constructor");
  jfieldID clazz_field = (*env)->GetFieldID(env, method_class,
					    "clazz", "Ljava/lang/Class;");
  jfieldID slot_field = (*env)->GetFieldID(env, method_class,
					   "slot", "I");
  jfieldID parameterTypes_field = (*env)->GetFieldID(env, method_class,
						     "parameterTypes", "[Ljava/lang/Class;");
  jfieldID exceptionTypes_field = (*env)->GetFieldID(env, method_class,
						     "exceptionTypes", "[Ljava/lang/Class;");
  jmethodID method_ctor = (*env)->GetMethodID(env, method_class,
					      "<init>", "()V");
  
  jobject method = find_method(env, clazz, only_declared,
			       class_class, method_class, clazz_field, slot_field, NULL,
			       NULL, parameterTypes_field, exceptionTypes_field,
			       method_ctor, NULL, parameterTypes, JNI_TRUE);
  
  if (method == NULL) 
    {
      throw_Exception(env, "java/lang/NoSuchMethodException", "<init>");
    }
  return method;
}

/* For Classpath, not JDK */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getConstructor(JNIEnv *env,
				    jobject cls,
				    jobjectArray parameterTypes)
{
  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)
{
  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)
{
  throw_Exception(env, "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)
{
  throw_Exception(env, "java/lang/UnsupportedOperationException",
                  "java.lang.Class.getDeclaredClasses()");
  return(0);
}

/* JDK1.2 */
JNIEXPORT jobjectArray JNICALL
Java_java_lang_Class_getDeclaredClasses0(JNIEnv *env,
					 jobject cls)
{
  (*env)->FatalError(env, "getDeclaredClasses0 not implemented yet.");
  return NULL;
}

/* JDK1.2 & Classpath */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getDeclaringClass(JNIEnv *env,
				       jobject cls)
{
  (*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)
{
  (*env)->FatalError(env, "setProtectionDomain not implemented yet.");
}

/* JDK1.2 */
JNIEXPORT jobject JNICALL
Java_java_lang_Class_getProtectionDomain0(JNIEnv *env,
					  jobject obj)
{
  (*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
}
