/* -*- Mode: C; c-file-style: "gnu" -*-
   jnirefl.c -- Java Native Interface methods relating reflection
   Created: Hernan Otero <hernan_otero@bigfoot.com>, 10-Jul-1998
 */
/*
  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 "jni.h"
#include "jniint.h"
#include "primitive-class.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>

jmethodID
JNIFUNC(FromReflectedMethod)(JNIEnv *env, jobject method)
{
  /* Since a Method is only valid for a single Class and its
   * ancestors and descendants which have that method (and I'm
   * not even sure about the ancestors part), the jmethodID
   * returned will always be the same (jmethodIDs are likewise
   * valid for ancestors and descendants of a single Class).
   * Thus it is safe to cache the jmethodID.
   * Same argument is valid for Field/jfieldID.
   */
  return (jmethodID) NSA_GetNativeState(method);
}

jfieldID
JNIFUNC(FromReflectedField)(JNIEnv *env, jobject field)
{
  return (jfieldID) NSA_GetNativeState(field);
}

jclass
sig_to_jclass(JNIEnv *env, Signature *sig)
{
  jclass sig_cls = NULL;

  if (sig->any.tag == SIG_CLASS
      || sig->any.tag == SIG_ARRAY)
    {
      char *java_sig;
      java_sig = SIG_formatToJavaSig(env, sig);
      
      sig_cls = (*env)->FindClass(env, java_sig);
      
      free(java_sig);
    }
  else /* SIG_PRIM */
    {
      switch (sig->prim.type)
	{
	case SIG_JBOOLEAN:
	  sig_cls = createFakePrimitiveClass(env, "boolean");
	  break;
	case SIG_JBYTE:
	  sig_cls = createFakePrimitiveClass(env, "byte");
	  break;
	case SIG_JCHAR:
	  sig_cls = createFakePrimitiveClass(env, "char");
	  break;
	case SIG_JSHORT:
	  sig_cls = createFakePrimitiveClass(env, "short");
	  break;
	case SIG_JINT:
	  sig_cls = createFakePrimitiveClass(env, "int");
	  break;
	case SIG_JLONG:
	  sig_cls = createFakePrimitiveClass(env, "long");
	  break;
	case SIG_JFLOAT:
	  sig_cls = createFakePrimitiveClass(env, "float");
	  break;
	case SIG_JDOUBLE:
	  sig_cls = createFakePrimitiveClass(env, "double");
	  break;
	case SIG_JVOID:
	  sig_cls = createFakePrimitiveClass(env, "void");
	  break;
	default:
	  (*env)->FatalError(env, "Illegal primitive type.");
	  break;
	}
    }

  return sig_cls;
}

jobject
JNIFUNC(ToReflectedMethod)(JNIEnv *env, jclass cls, jmethodID method)
{
  static jclass class_class = NULL;
  static jclass method_class = NULL;
  static jclass ctor_class = NULL;
  static jfieldID clazz_field = NULL;
  static jfieldID slot_field = NULL;
  static jfieldID name_field = NULL;
  static jfieldID returnType_field = NULL;
  static jfieldID parameterTypes_field = NULL;
  static jfieldID exceptionTypes_field = NULL;
  static jmethodID method_ctor = NULL;

  jmethodID meth_cls;
  jfieldID used_name;
  jfieldID used_rettype;

  ClazzFile *clazz = method->clazz;
  jobject rmethod;
  Signature *sig = SIG_parseFromJavaSig(env, method->sig_str);
  int num_params = sig->method.num_params;
  int num_exceptions = method->num_throwable_exceptions;
  jobjectArray exception_array;
  jobjectArray param_array;
  int p;
  int slot;

  jboolean is_ctor;

  if (!class_class)
    {
      class_class = (*env)->FindClass(env, "java/lang/Class");
      ctor_class = (*env)->FindClass(env,
				     "java/lang/reflect/Constructor");
      method_class = (*env)->FindClass(env,
				       "java/lang/reflect/Method");
      clazz_field = (*env)->GetFieldID(env, method_class,
				       "clazz", "Ljava/lang/Class;");
      slot_field = (*env)->GetFieldID(env, method_class,
				      "slot", "I");
      name_field = (*env)->GetFieldID(env, method_class,
				      "name", "Ljava/lang/String;");
      returnType_field = (*env)->GetFieldID(env, method_class,
					    "returnType", "Ljava/lang/Class;");
      parameterTypes_field = (*env)->GetFieldID(env, method_class,
						"parameterTypes", "[Ljava/lang/Class;");
      exceptionTypes_field = (*env)->GetFieldID(env, method_class,
						"exceptionTypes", "[Ljava/lang/Class;");
      method_ctor = (*env)->GetMethodID(env, method_class,
					"<init>", "()V");
    }

  rmethod = (*env)->NewObject(env, method_class, method_ctor);
  is_ctor = (strcmp(method->name, "<init>") == 0);
  if (is_ctor)
    {
      meth_cls = ctor_class;
      used_name = NULL;
      used_rettype = NULL;
    }
  else
    {
      meth_cls = method_class;
      used_name = name_field;
      used_rettype = returnType_field;
    }


  /* Determine method's slot inside class */
  for (slot = 0; slot < clazz->num_methods; ++slot) 
    {
      if (clazz->methods[slot] == method) 
	{
	  break;
	}
    }

  /*
   * We use the NULL-ness of name_field to determine if we are
   * working with a Constructor or a Method
   */
  if (used_name)
    {
      jstring mname = (*env)->NewStringUTF(env, method->name);
      jclass returntype = sig_to_jclass(env,
					sig->method.return_type);
      (*env)->SetObjectField(env, rmethod, used_rettype,
			     returntype);
      mname = (*env)->NewGlobalRef(env, mname);
      (*env)->SetObjectField(env, rmethod, used_name, mname);
    }
  
  /* Create the declared exceptions array */
  exception_array = (*env)->NewObjectArray(env, num_exceptions,
					   class_class, NULL);
  for (p = 0; p < num_exceptions; p ++)
    {
      jobject exc;
      ClazzFile *exc_type;
      
      exc_type = getThrowableException(env, method, p);
      
      exc = clazzfile_to_jclass(env, exc_type);
      
      (*env)->SetObjectArrayElement(env, exception_array, p, exc);
    }
  exception_array = (*env)->NewGlobalRef(env, exception_array);
  
  /* Create the parameter types array */
  param_array = (*env)->NewObjectArray(env, num_params,
				       class_class, NULL);
  for (p = 0; p < num_params; p ++)
    {
      jclass param;
      
      param = sig_to_jclass(env, sig->method.params[p]);
      
      (*env)->SetObjectArrayElement(env, param_array, p, param);
    }

  param_array = (*env)->NewGlobalRef(env, param_array);
  
  (*env)->SetIntField(env, rmethod, slot_field, slot);
  (*env)->SetObjectField(env, rmethod, clazz_field,
			 clazzfile_to_jclass(env, clazz));
  (*env)->SetObjectField(env, rmethod, exceptionTypes_field,
			 exception_array);
  (*env)->SetObjectField(env, rmethod, parameterTypes_field,
			 param_array);

  SIG_free(env, sig);

  NSA_SetNativeState(rmethod, method);

  return (*env)->NewGlobalRef(env, rmethod);
}

jobject
JNIFUNC(ToReflectedField)(JNIEnv *env, jclass cls, jfieldID field)
{
  static jclass field_class = NULL;
  static jfieldID clazz_field = NULL;
  static jfieldID slot_field = NULL;
  static jfieldID name_field = NULL;
  static jfieldID type_field = NULL;
  static jmethodID field_ctor = NULL;
  int slot;
  ClazzFile *clazz = field->clazz;
  jobject rfield;
  jobject type;
  jstring name;
  Signature *sig;

  if (!field_class)
    {
      field_class = (*env)->FindClass(env,
				      "java/lang/reflect/Field");
      clazz_field = (*env)->GetFieldID(env, field_class,
				       "clazz", "Ljava/lang/Class;");
      slot_field = (*env)->GetFieldID(env, field_class,
				      "slot", "I");
      name_field = (*env)->GetFieldID(env, field_class,
				      "name", "Ljava/lang/String;");
      type_field = (*env)->GetFieldID(env, field_class,
				      "type", "Ljava/lang/Class;");
      field_ctor = (*env)->GetMethodID(env, field_class,
				       "<init>", "()V");
    }

  /* Determine field's slot inside class */
  for (slot = 0; slot < clazz->num_fields; ++slot) 
    {
      if (clazz->fields[slot] == field) 
	{
	  break;
	}
    }
  
  rfield = (*env)->NewObject(env, field_class, field_ctor);
  name = (*env)->NewStringUTF(env, field->name);

  sig = SIG_parseFromJavaSig(env, field->sig_str);
  type = sig_to_jclass(env, sig);
  SIG_free(env, sig);

  name = (*env)->NewGlobalRef(env, name);
  (*env)->SetObjectField(env, rfield, name_field, name);
  (*env)->SetIntField(env, rfield, slot_field, slot);
  (*env)->SetObjectField(env, rfield, clazz_field,
			 clazzfile_to_jclass(env, clazz));
  (*env)->SetObjectField(env, rfield, type_field, type);

  NSA_SetNativeState(rfield, field);

  return (*env)->NewGlobalRef(env, rfield);
}
