/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode:nil -*-
   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 "jniint.h"
#include "ClazzFile.h"
#include "compat.h"

jmethodID
JNIFUNC(GetMethodID)(JNIEnv *env,
                     jclass clazz,
                     const char *name,
                     const char *sig)
{
  HungryEnv *henv = JNIEnvToHEnv(env);
  ClazzFile *cf = jclass_to_clazzfile(henv, clazz);
  MethodStruct* method;

  method = HVM_MethodFind(henv, cf, 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) \
{ \
  HungryEnv *henv = JNIEnvToHEnv(env); \
  InterpValue result = HVM_MethodCall(henv, \
                                      (MethodStruct*)methodID, \
                                      obj, args); \
   \
 \
  return result.unionselector; \
} \
 \
jtype \
JNIFUNC(Call##JType##MethodV)(JNIEnv *env, \
                              jobject obj, \
                              jmethodID methodID, \
                              va_list args) \
{ \
  HungryEnv *henv = JNIEnvToHEnv(env); \
  jvalue *params; \
  MethodStruct *method = (MethodStruct*)methodID; \
  HVMSignature *sig = HVM_SigParseFromJavaSig(henv, method->sig_str); \
  int num_params = HVM_SigNumParams(henv, sig); \
  int i; \
  jtype result; \
 \
  params = (jvalue*)PR_Calloc(num_params, sizeof(jvalue)); \
 \
  for (i = 0; i < num_params; i ++) \
    { \
      switch (HVM_SigSizeofStrInWords(henv, sig->method.params[i])) \
        { \
        case 1: \
          params[i].i = va_arg(args, jint); \
          break; \
        case 2: \
          params[i].j = va_arg(args, jlong); \
          break; \
        } \
    } \
 \
  HVM_SigFree(henv, sig); \
  result = JNIFUNC(Call##JType##MethodA)(env, obj, methodID, params); \
   \
  PR_DELETE(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)
{
  HungryEnv *henv = JNIEnvToHEnv(env);
  HVM_MethodCallA(henv,
                  (MethodStruct*)methodID,
                  obj,
                  (InterpValue*)args);
}

void
JNIFUNC(CallVoidMethodV)(JNIEnv *env,
                         jobject obj,
                         jmethodID methodID,
                         va_list args)
{
  HungryEnv *henv = JNIEnvToHEnv(env);
  jvalue *params = NULL;
  MethodStruct *method = (MethodStruct*)methodID;
  HVMSignature *sig = HVM_SigParseFromJavaSig(henv, method->sig_str);
  int num_params = HVM_SigNumParams(henv, sig);
  int i;

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

  for (i = 0; i < num_params; i ++)
    {
      switch (HVM_SigSizeofStrInWords(henv, sig->method.params[i]))
        {
        case 1:
          params[i].i = va_arg(args, jint);
          break;
        case 2:
          params[i].j = va_arg(args, jlong);
          break;
        }
    }
  
  HVM_SigFree(henv, sig);
  
  JNIFUNC(CallVoidMethodA)(env, obj, methodID, params);

  if (NULL != params)
    PR_DELETE(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) \
{ \
  HungryEnv *henv = JNIEnvToHEnv(env); \
  if (((japhar_object*)obj)->clazz == methodID->clazz) \
    { \
      InterpValue value = HVM_MethodCallA(henv,\
                                          (MethodStruct*)methodID, \
                                          (japhar_object*)obj, \
                                          (InterpValue*)args); \
       \
      return value.unionselector; \
    } \
  else \
    { \
      char buf[1024]; \
      jclass exception = (*env)->FindClass(env, \
                                           java_lang_ClassCastException); \
       \
      PR_snprintf(buf, sizeof(buf), \
               "JNI CallNonvirtual" #JType "Method*, calling method '%s', which doesn't exist in class '%s'", \
               methodID->name, \
               getClassName(env, ((japhar_object*)obj)->clazz)); \
 \
      (*env)->ThrowNew(env, exception, buf); \
      return (jtype)0; \
    } \
} \
 \
jtype \
JNIFUNC(CallNonvirtual##JType##MethodV)(JNIEnv *env, \
                                        jobject obj, \
                                        jmethodID methodID, \
                                        va_list args) \
{ \
  HungryEnv *henv = JNIEnvToHEnv(env); \
  jvalue *params = NULL; \
  MethodStruct *method = (MethodStruct*)methodID; \
  HVMSignature *sig = HVM_SigParseFromJavaSig(henv, method->sig_str); \
  int num_params = HVM_SigNumParams(henv, sig); \
  int i; \
  jtype result; \
 \
  if (0 < num_params) \
    params = (jvalue*)PR_Calloc(num_params, sizeof(jvalue)); \
 \
  for (i = 0; i < num_params; i ++) \
    { \
      switch (HVM_SigSizeofStrInWords(henv, sig->method.params[i])) \
        { \
        case 1: \
          params[i].i = va_arg(args, jint); \
          break; \
        case 2: \
          params[i].j = va_arg(args, jlong); \
          break; \
        } \
    } \
 \
  result = JNIFUNC(CallNonvirtual##JType##MethodA)(env, obj, methodID, params); \
 \
  if (NULL != params) \
    PR_DELETE(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)
{
  HungryEnv *henv = JNIEnvToHEnv(env);
  if (((japhar_object*)obj)->clazz == methodID->clazz)
    {
      HVM_MethodCallA(henv,
                      (MethodStruct*)methodID,
                      obj,
                      (InterpValue*)args);
    }
  else
    {
      char buf[1024];
      jclass exception = (*env)->FindClass(env,
                                           java_lang_ClassCastException);
      
      PR_snprintf(buf, sizeof(buf),
               "JNI CallNonvirtualVoidMethod*, calling method '%s', which doesn't exist in class '%s'",
               methodID->name,
               getClassName(env, ((japhar_object*)obj)->clazz));

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

void
JNIFUNC(CallNonvirtualVoidMethodV)(JNIEnv *env,
                                   jobject obj,
                                   jmethodID methodID,
                                   va_list args)
{
  HungryEnv *henv = JNIEnvToHEnv(env);
  jvalue *params = NULL;
  MethodStruct *method = (MethodStruct*)methodID;
  HVMSignature *sig = HVM_SigParseFromJavaSig(henv, method->sig_str);
  int num_params = HVM_SigNumParams(henv, sig);
  int i;

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

  for (i = 0; i < num_params; i ++)
    {
      switch (HVM_SigSizeofStrInWords(henv, sig->method.params[i]))
        {
        case 1:
          params[i].i = va_arg(args, jint);
          break;
        case 2:
          params[i].j = va_arg(args, jlong);
          break;
        }
    }

  HVM_SigFree(henv, sig);

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

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