/* -*- Mode: C; c-file-style: "gnu" -*-
   jvmdimth.c -- JVMDI Method Information Functions
   Created: Hernan Otero <hernan_otero@bigfoot.com>, 18-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) 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 "jvmdiint.h"
#include "interp.h"
#include "objects.h"
#include "log.h"

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

#define MYLOG "JVMDI"

JNIEXPORT jvmdiError JNICALL
JVMDI_GetMethodName(JNIEnv *env,
		    jclass clazz,
		    jmethodID method, 
		    jstring *namePtr,
		    jstring *signaturePtr)
{
  jstring name = NULL, signature = NULL;

  /* the usual NULL check. */
  if (env == NULL
      || namePtr == NULL
      || signaturePtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  name = (*env)->NewStringUTF(env, method->name);
  signature = (*env)->NewStringUTF(env, method->sig_str);

  if (name == NULL
      || signature == NULL)
    {
      /* how do we free things?  it doesn't make sense to have the
	 same thread local objects for jvmdi methods like the JNI spec
	 does for native methods, does it?  I suppose, since these
	 objects were not changed into global refs, then they should
	 be collected the next time around, so we shouldn't worry. */
      return JVMDI_ERROR_OUT_OF_MEMORY;
    }

  *namePtr = name;
  *signaturePtr = signature;
  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetMethodDeclaringClass(JNIEnv *env,
			      jclass clazz,
			      jmethodID method,
			      jclass *definingClassPtr)
{
  /* the usual NULL check. */
  if (env == NULL
      || definingClassPtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *definingClassPtr = method->clazz;
  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetMethodModifiers(JNIEnv *env, jclass clazz, jmethodID method,
                         jint *modifiersPtr)
{
  /* the usual NULL check. */
  if (env == NULL
      || modifiersPtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *modifiersPtr = method->access_flags;
  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetMaxStack(JNIEnv *env, jclass clazz, jmethodID method,
                  jint *maxPtr)
{
  /* the usual NULL check. */
  if (env == NULL
      || maxPtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *maxPtr = method->max_stack;
  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetMaxLocals(JNIEnv *env, jclass clazz, jmethodID method,
		   jint *maxPtr)
{
  /* the usual NULL check. */
  if (env == NULL
      || maxPtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *maxPtr = method->max_locals;
  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetArgumentsSize(JNIEnv *env, jclass clazz, jmethodID method,
		       jint *sizePtr)
{
  /* the usual NULL check. */
  if (env == NULL
      || sizePtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  /* XXX is this correct? */
  *sizePtr = method->num_param_words * 2;
  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetLineNumberTable(JNIEnv *env, jclass clazz, jmethodID method,
 			 jint *entryCountPtr, 
			 JVMDI_line_number_entry **tablePtr)
{
  int i;

  /* the usual NULL check. */
  if (env == NULL
      || entryCountPtr == NULL
      || tablePtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *entryCountPtr = method->num_line_number_blocks;
  if (JVMDI_Allocate(env,
		     method->num_line_number_blocks * sizeof(JVMDI_line_number_entry),
		     (jbyte**) tablePtr) == JVMDI_ERROR_OUT_OF_MEMORY) {
    return JVMDI_ERROR_OUT_OF_MEMORY;
  }

  for (i = 0; i < method->num_line_number_blocks; ++i) {
    JVMDI_line_number_entry *jvmdi_entry = &(*tablePtr)[i];
    LineNumberBlock *japhar_entry = &method->line_numbers[i];
    jvmdi_entry->start_location = japhar_entry->start_pc;
    jvmdi_entry->line_number = japhar_entry->line_number;
  }

  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetMethodLocation(JNIEnv *env, jclass clazz, jmethodID method,
                        jlocation *startLocationPtr, 
                        jlocation *endLocationPtr)
{
  /* the usual NULL check. */
  if (env == NULL
      || startLocationPtr == NULL
      || endLocationPtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *startLocationPtr = 0;
  *endLocationPtr = method->code_length - 1;

  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetLocalVariableTable(JNIEnv *env, jclass clazz, jmethodID method,
                            jint *entryCountPtr, 
                            JVMDI_local_variable_entry **tablePtr)
{
  int i;

  /* the usual NULL check. */
  if (env == NULL
      || entryCountPtr == NULL
      || tablePtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *entryCountPtr = method->num_local_variables;
  if (JVMDI_Allocate(env,
		     method->num_local_variables * sizeof(JVMDI_local_variable_entry),
		     (jbyte**) tablePtr) == JVMDI_ERROR_OUT_OF_MEMORY) {
    return JVMDI_ERROR_OUT_OF_MEMORY;
  }

  for (i = 0; i < method->num_local_variables; ++i) {
    JVMDI_local_variable_entry *jvmdi_entry = &(*tablePtr[i]);
    LocalVariableEntry *japhar_entry = &method->local_variables[i];
    jvmdi_entry->start_location = japhar_entry->start_pc;
    jvmdi_entry->length = japhar_entry->end_pc - japhar_entry->start_pc;
    /* XXX should we strdup()? docs say nothing about this */
    jvmdi_entry->name = japhar_entry->name;
    /* XXX should we strdup()? docs say nothing about this */
    jvmdi_entry->signature = SIG_formatToJavaSig(env, japhar_entry->sig);
    jvmdi_entry->slot = japhar_entry->slot;
  }

  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetExceptionHandlerTable(JNIEnv *env, jclass clazz, jmethodID method,
                               jint *entryCountPtr, 
                               JVMDI_exception_handler_entry **tablePtr)
{
  int i;

  /* the usual NULL check. */
  if (env == NULL
      || entryCountPtr == NULL
      || tablePtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *entryCountPtr = method->num_exception_blocks;
  if (JVMDI_Allocate(env,
		     method->num_exception_blocks * sizeof(JVMDI_exception_handler_entry),
		     (jbyte**) tablePtr) == JVMDI_ERROR_OUT_OF_MEMORY) {
    return JVMDI_ERROR_OUT_OF_MEMORY;
  }

  for (i = 0; i < method->num_exception_blocks; ++i) {
    JVMDI_exception_handler_entry *jvmdi_entry = &(*tablePtr[i]);
    ExceptionBlock *japhar_entry = &method->exceptions[i];
    jvmdi_entry->start_location = japhar_entry->start_pc;
    jvmdi_entry->end_location = japhar_entry->end_pc;
    jvmdi_entry->handler_location = japhar_entry->handler_pc;
    jvmdi_entry->exception =
      (jclass) ExceptionBlock_getHandlerClazz(env,
					      clazz, japhar_entry);
  }

  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetThrownExceptions(JNIEnv *env, jclass clazz, jmethodID method,
                          jint *exceptionCountPtr, jclass **exceptionsPtr)
{
  int i;

  /* the usual NULL check. */
  if (env == NULL
      || exceptionCountPtr == NULL
      || exceptionsPtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *exceptionCountPtr = method->num_throwable_exceptions;
  if (JVMDI_Allocate(env,
		     method->num_throwable_exceptions * sizeof(jclass),
		     (jbyte**) exceptionsPtr) == JVMDI_ERROR_OUT_OF_MEMORY) {
    return JVMDI_ERROR_OUT_OF_MEMORY;
  }

  for (i = 0; i < method->num_throwable_exceptions; ++i) {
    (*exceptionsPtr[i]) = (jclass) getThrowableException(env, method, i);
  }

  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_GetBytecodes(JNIEnv *env, jclass clazz, jmethodID method,
		   jint *bytecodeCountPtr,
		   jbyte **bytecodesPtr)
{
  /* the usual NULL check. */
  if (env == NULL
      || bytecodeCountPtr == NULL
      || bytecodesPtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *bytecodeCountPtr = method->code_length;
  if (JVMDI_Allocate(env, method->code_length, bytecodesPtr) ==
      JVMDI_ERROR_OUT_OF_MEMORY) {
    return JVMDI_ERROR_OUT_OF_MEMORY;
  }
  memcpy(*bytecodesPtr, method->code, method->code_length);

  return JVMDI_ERROR_NONE;
}

JNIEXPORT jvmdiError JNICALL
JVMDI_IsMethodNative(JNIEnv *env,
		     jclass clazz,
		     jmethodID method,
		     jboolean *isNativePtr)
{
  /* the usual NULL checks. */
  if (env == NULL
      || isNativePtr == NULL)
    return JVMDI_ERROR_NULL_POINTER;

  /* check for invalid class. */
  if (clazz == NULL)
    return JVMDI_ERROR_INVALID_CLASS;

  /* check for invalid method id */
  if (method == NULL)
    return JVMDI_ERROR_INVALID_METHODID;

  *isNativePtr = (method->access_flags & ACC_NATIVE) != 0;
  return JVMDI_ERROR_NONE;
}
