/* -*- Mode: C; c-file-style: "gnu" -*-
   jnimeth.c -- Java Native Interface methods relating to instance methods.
   Created: Chris Toshok <toshok@hungry.com>, 26-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 "jni.h"
#include "jniint.h"
#include "interp.h"
#include "method.h"
#include "ClazzFile.h"
#include "objects.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "compat.h"

jmethodID
JNIFUNC(GetMethodID)(JNIEnv *env,
		     jclass clazz,
		     const char *name,
		     const char *sig)
{
  MethodStruct* method;

  method = find_method(env, clazz, name, sig);

  if (method == NULL)
    {
      jclass no_such_method = (*env)->FindClass(env, "java/lang/NoSuchMethodError");
      if (no_such_method)
	(*env)->ThrowNew(env, no_such_method, name);
      else
	(*env)->FatalError(env, "Could not load java/lang/NoSuchMethodError");
    }

  return method;
}

#define VIRTUAL_METHOD_FOO(jtype, JType, unionselector)  \
 \
jtype \
JNIFUNC(Call##JType##Method)(JNIEnv *env, \
			     jobject obj, \
			     jmethodID methodID, \
			     ...) \
{ \
  va_list varargs; \
  jtype result; \
\
 \
  va_start(varargs, methodID); \
  result = JNIFUNC(Call##JType##MethodV)(env, obj, methodID, varargs); \
  va_end(varargs); \
 \
  return result; \
} \
 \
jtype \
JNIFUNC(Call##JType##MethodA)(JNIEnv *env, \
			      jobject obj, \
			      jmethodID methodID, \
			      jvalue *args) \
{ \
  jvalue result = CallJavaMethod(env, \
				 (MethodStruct*)methodID, \
				 obj, args); \
   \
 \
  return result.unionselector; \
} \
 \
jtype \
JNIFUNC(Call##JType##MethodV)(JNIEnv *env, \
			      jobject obj, \
			      jmethodID methodID, \
			      va_list args) \
{ \
  jvalue *params; \
  MethodStruct *method = (MethodStruct*)methodID; \
  Signature *sig = SIG_parseFromJavaSig(env, method->sig_str); \
  int num_params = SIG_numParams(env, sig); \
  int i; \
  jtype result; \
 \
  params = (jvalue*)calloc(num_params, sizeof(jvalue)); \
 \
  for (i = 0; i < num_params; i ++) \
    { \
      if (sig->method.params[i]->any.tag != SIG_PRIM) /* XXX punt... */ \
	{ \
	  params[i].l = va_arg(args, void*); \
	} \
      else \
	{ \
	  switch (sig->method.params[i]->prim.type) \
	    { \
	    case SIG_JBOOLEAN: \
	    case SIG_JBYTE: \
	    case SIG_JCHAR: \
	    case SIG_JSHORT: \
	    case SIG_JINT: \
	    case SIG_JOBJECT: \
	    case SIG_JFLOAT: \
	      params[i].i = va_arg(args, jint); \
	      break; \
	    case SIG_JLONG: \
	    case SIG_JDOUBLE: \
	      params[i].j = va_arg(args, jlong); \
	      break; \
	    default: \
	      abort(); \
	    } \
	} \
    } \
 \
  SIG_free(env, sig); \
  result = JNIFUNC(Call##JType##MethodA)(env, obj, methodID, params); \
   \
  free(params); \
 \
  return result; \
}

VIRTUAL_METHOD_FOO(jboolean, Boolean, z)
VIRTUAL_METHOD_FOO(jbyte, Byte, b)
VIRTUAL_METHOD_FOO(jchar, Char, c)
VIRTUAL_METHOD_FOO(jshort, Short, s)
VIRTUAL_METHOD_FOO(jint, Int, i)
VIRTUAL_METHOD_FOO(jlong, Long, j)
VIRTUAL_METHOD_FOO(jobject, Object, l)
VIRTUAL_METHOD_FOO(jfloat, Float, f)
VIRTUAL_METHOD_FOO(jdouble, Double, d)

void
JNIFUNC(CallVoidMethod)(JNIEnv *env,
			jobject obj,
			jmethodID methodID,
			...)
{
  va_list varargs;

  va_start(varargs, methodID);
  JNIFUNC(CallVoidMethodV)(env, obj, methodID, varargs);
  va_end(varargs);
}

void
JNIFUNC(CallVoidMethodA)(JNIEnv *env,
			 jobject obj,
			 jmethodID methodID,
			 jvalue *args)
{
  CallJavaMethod(env,
		 (MethodStruct*)methodID,
		 obj,
		 args);
}

void
JNIFUNC(CallVoidMethodV)(JNIEnv *env,
			 jobject obj,
			 jmethodID methodID,
			 va_list args)
{
  jvalue *params = NULL;
  MethodStruct *method = (MethodStruct*)methodID;
  Signature *sig = SIG_parseFromJavaSig(env, method->sig_str);
  int num_params = SIG_numParams(env, sig);
  int i;

  if (0 < num_params)
    params = (jvalue*)calloc(num_params, sizeof(jvalue));

  for (i = 0; i < num_params; i ++)
    {
      if (sig->method.params[i]->any.tag != SIG_PRIM) /* XXX punt... */
	{
	  params[i].l = va_arg(args, void*);
	}
      else
	{
	  switch (sig->method.params[i]->prim.type)
	    {
	    case SIG_JBOOLEAN:
	    case SIG_JBYTE:
	    case SIG_JCHAR:
	    case SIG_JSHORT:
	    case SIG_JINT:
	    case SIG_JOBJECT:
	    case SIG_JFLOAT:
	      params[i].i = va_arg(args, jint);
	      break;
	    case SIG_JLONG:
	    case SIG_JDOUBLE:
	      params[i].j = va_arg(args, jlong);
	      break;
	    default:
	      abort();
	    }
	}
    }

  SIG_free(env, sig);

  JNIFUNC(CallVoidMethodA)(env, obj, methodID, params);

  if (NULL != params)
    free(params);
}

#define NONVIRTUAL_METHOD_FOO(jtype, JType, unionselector)  \
 \
jtype  \
JNIFUNC(CallNonvirtual##JType##Method)(JNIEnv *env,  \
				       jobject obj,  \
				       jmethodID methodID,  \
				       ...) \
{ \
  va_list varargs; \
  jtype result; \
 \
  va_start(varargs, methodID); \
  result = JNIFUNC(CallNonvirtual##JType##MethodV)(env, obj, methodID, varargs); \
  va_end(varargs); \
 \
  return result; \
} \
 \
jtype \
JNIFUNC(CallNonvirtual##JType##MethodA)(JNIEnv *env, \
					jobject obj, \
					jmethodID methodID, \
					jvalue *args) \
{ \
  if (*(japhar_obj)obj == methodID->clazz) \
    { \
      jvalue value = CallJavaMethod(env,\
				    (MethodStruct*)methodID, \
				    obj, \
				    args); \
       \
      return value.unionselector; \
    } \
  else \
    { \
      char buf[1024]; \
      jclass exception = (*env)->FindClass(env, \
					   "java/lang/ClassCastException"); \
       \
      snprintf(buf, sizeof(buf), \
	       "JNI CallNonvirtual" #JType "Method*, calling method '%s', which doesn't exist in class '%s'", \
	       methodID->name, \
	       getClassName(env, *(japhar_obj)obj)); \
 \
      (*env)->ThrowNew(env, exception, buf); \
      return (jtype)0; \
    } \
} \
 \
jtype \
JNIFUNC(CallNonvirtual##JType##MethodV)(JNIEnv *env, \
					jobject obj, \
					jmethodID methodID, \
					va_list args) \
{ \
  jvalue *params = NULL; \
  MethodStruct *method = (MethodStruct*)methodID; \
  Signature *sig = SIG_parseFromJavaSig(env, method->sig_str); \
  int num_params = SIG_numParams(env, sig); \
  int i; \
  jtype result; \
 \
  if (0 < num_params) \
    params = (jvalue*)calloc(num_params, sizeof(jvalue)); \
 \
  for (i = 0; i < num_params; i ++) \
    { \
      if (sig->method.params[i]->any.tag != SIG_PRIM) /* XXX punt... */ \
	{ \
	  params[i].l = va_arg(args, void*); \
	} \
      else \
	{ \
	  switch (sig->method.params[i]->prim.type) \
	    { \
	    case SIG_JBOOLEAN: \
	    case SIG_JBYTE: \
	    case SIG_JCHAR: \
	    case SIG_JSHORT: \
	    case SIG_JINT: \
	    case SIG_JOBJECT: \
	    case SIG_JFLOAT: \
	      params[i].i = va_arg(args, jint); \
	      break; \
	    case SIG_JLONG: \
	    case SIG_JDOUBLE: \
	      params[i].j = va_arg(args, jlong); \
	      break; \
	    default: \
	      abort(); \
	    } \
	} \
    } \
 \
  result = JNIFUNC(CallNonvirtual##JType##MethodA)(env, obj, methodID, params); \
 \
  if (NULL != params) \
    free(params); \
 \
  return result; \
}


NONVIRTUAL_METHOD_FOO(jboolean, Boolean, z)
NONVIRTUAL_METHOD_FOO(jbyte, Byte, b)
NONVIRTUAL_METHOD_FOO(jchar, Char, c)
NONVIRTUAL_METHOD_FOO(jshort, Short, s)
NONVIRTUAL_METHOD_FOO(jint, Int, i)
NONVIRTUAL_METHOD_FOO(jlong, Long, j)
NONVIRTUAL_METHOD_FOO(jobject, Object, l)
NONVIRTUAL_METHOD_FOO(jfloat, Float, f)
NONVIRTUAL_METHOD_FOO(jdouble, Double, d)

void
JNIFUNC(CallNonvirtualVoidMethod)(JNIEnv *env,
				  jobject obj,
				  jmethodID methodID,
				  ...)
{
  va_list varargs;

  va_start(varargs, methodID);
  JNIFUNC(CallNonvirtualVoidMethodV)(env, obj, methodID, varargs);
  va_end(varargs);
}

void
JNIFUNC(CallNonvirtualVoidMethodA)(JNIEnv *env,
				   jobject obj,
				   jmethodID methodID,
				   jvalue *args)
{
  if (*(japhar_obj)obj == methodID->clazz)
    {
      CallJavaMethod(env,
		     (MethodStruct*)methodID,
		     obj,
		     args);
    }
  else
    {
      char buf[1024];
      jclass exception = (*env)->FindClass(env,
					   "java/lang/ClassCastException");
      
      snprintf(buf, sizeof(buf),
	       "JNI CallNonvirtualVoidMethod*, calling method '%s', which doesn't exist in class '%s'",
	       methodID->name,
	       getClassName(env, *(japhar_obj)obj));

      (*env)->ThrowNew(env, exception, buf);
    }
}

void
JNIFUNC(CallNonvirtualVoidMethodV)(JNIEnv *env,
				   jobject obj,
				   jmethodID methodID,
				   va_list args)
{
  jvalue *params = NULL;
  MethodStruct *method = (MethodStruct*)methodID;
  Signature *sig = SIG_parseFromJavaSig(env, method->sig_str);
  int num_params = SIG_numParams(env, sig);
  int i;

  if (0 < num_params)
    params = (jvalue*)calloc(num_params, sizeof(jvalue));

  for (i = 0; i < num_params; i ++)
    {
      if (sig->method.params[i]->any.tag != SIG_PRIM) /* XXX punt... */
	{
	  params[i].l = va_arg(args, void*);
	}
      else
	{
	  switch (sig->method.params[i]->prim.type)
	    {
	    case SIG_JBOOLEAN:
	    case SIG_JBYTE:
	    case SIG_JCHAR:
	    case SIG_JSHORT:
	    case SIG_JINT:
	    case SIG_JOBJECT:
	    case SIG_JFLOAT:
	      params[i].i = va_arg(args, jint);
	      break;
	    case SIG_JLONG:
	    case SIG_JDOUBLE:
	      params[i].j = va_arg(args, jlong);
	      break;
	    default:
	      abort();
	    }
	}
    }

  SIG_free(env, sig);

  JNIFUNC(CallNonvirtualVoidMethodA)(env, obj, methodID, params);

  if (NULL != params)
    free(params);
}
