/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 */
#include <stdio.h>
#include "java_lang_reflect_Method.h"
#include "java_lang_Class.h"

#include "JavaVM.h"
#include "SysCallsRuntime.h"
#include "StackWalker.h"

static inline void throwMethodException(RuntimeError err)
{
  // FIXME Need to check for illegalAccess, which we currently do not enforce
  // Also need to throw an InvocationTargetException
  if (err.cause == RuntimeError::nullPointer)
    sysThrowNullPointerException();
  else if (err.cause == RuntimeError::illegalArgument)
    sysThrowNamedException("java/lang/IllegalArgumentException");
  else
    trespass("Unknown Runtime Error in throwMethodException()");

}

// Make sure that the class that called this method can access this field.
// Throw an IllegalAccessException if it can't.
static inline void checkCallingClass(const Method &method)
{
    Frame thisFrame;
    Method &callerMethod = thisFrame.getCallingJavaMethod();
    
    if (callerMethod.getDeclaringClass() != method.getDeclaringClass()) {
	if ((method.getModifiers() & CR_METHOD_PRIVATE)) 
	    sysThrowNamedException("java/lang/IllegalAccessException");
	else if ((method.getModifiers() & CR_METHOD_PROTECTED)) {
	    if (!method.getDeclaringClass()->isAssignableFrom(*(const Type *) callerMethod.getDeclaringClass()))
	        sysThrowNamedException("java/lang/IllegalAccessException");
	}
    }
}

extern "C" {

/* XXX Routines returning strings will have to be redone once the VM supports 
 * unicode strings directly
 */

static inline Method &toMethod(Java_java_lang_reflect_Method *inMethod)
{
  if (!inMethod)
    sysThrowNullPointerException();

  return *(Method *) inMethod;
}



/*
 * Class : java/lang/reflect/Method
 * Method : equals
 * Signature : (Ljava/lang/Object;)Z
 */
NS_EXPORT NS_NATIVECALL(uint32 /* bool */)
Netscape_Java_java_lang_reflect_Method_equals(Java_java_lang_reflect_Method *inMethod, 
					      Java_java_lang_Object *inObj)
{
  Method &method = toMethod(inMethod);
  JavaObject *obj = (JavaObject *) inObj;

  return obj && method.equals(*obj);  
}  


/*
 * Class : java/lang/reflect/Method
 * Method : getDeclaringClass
 * Signature : ()Ljava/lang/Class;
 */
NS_EXPORT NS_NATIVECALL(Java_java_lang_Class *)
Netscape_Java_java_lang_reflect_Method_getDeclaringClass(Java_java_lang_reflect_Method *inMethod)
{
  Method &method = toMethod(inMethod);
  
  return ((Java_java_lang_Class *) method.getDeclaringClass());
}


/*
 * Class : java/lang/reflect/Method
 * Method : getExceptionTypes
 * Signature : ()[Ljava/lang/Class;
 */
NS_EXPORT NS_NATIVECALL(ArrayOf_Java_java_lang_Class *)
Netscape_Java_java_lang_reflect_Method_getExceptionTypes(Java_java_lang_reflect_Method *)
{
  printf("Netscape_Java_java_lang_reflect_Method_getExceptionTypes() not implemented\n");
  return 0;
}


/*
 * Class : java/lang/reflect/Method
 * Method : getModifiers
 * Signature : ()I
 */
NS_EXPORT NS_NATIVECALL(int32)
Netscape_Java_java_lang_reflect_Method_getModifiers(Java_java_lang_reflect_Method *inMethod)
{
  Method &method = toMethod(inMethod);

  return method.getModifiers();
}



/*
 * Class : java/lang/reflect/Method
 * Method : getName
 * Signature : ()Ljava/lang/String;
 */
NS_EXPORT NS_NATIVECALL(Java_java_lang_String *)
Netscape_Java_java_lang_reflect_Method_getName(Java_java_lang_reflect_Method *inMethod)
{
  Method &method = toMethod(inMethod);

  const char *name = method.getName();

  return ((Java_java_lang_String *) &VM::intern(name));
}


/*
 * Class : java/lang/reflect/Method
 * Method : getParameterTypes
 * Signature : ()[Ljava/lang/Class;
 */
NS_EXPORT NS_NATIVECALL(ArrayOf_Java_java_lang_Class *)
Netscape_Java_java_lang_reflect_Method_getParameterTypes(Java_java_lang_reflect_Method *inMethod)
{
  Method &method = toMethod(inMethod);
  const Type **params;
  Int32 numParams;

  numParams = method.getParameterTypes(params);

  void *arr = sysNewObjectArray(&VM::getStandardClass(cClass), numParams);

  ArrayOf_Java_java_lang_Class *clazzArray = (ArrayOf_Java_java_lang_Class *) arr;
  for (Int32 i = 0; i < numParams; i++)
    clazzArray->elements[i] = (Java_java_lang_Class *) params[i];

  return clazzArray;
}



/*
 * Class : java/lang/reflect/Method
 * Method : getReturnType
 * Signature : ()Ljava/lang/Class;
 */
NS_EXPORT NS_NATIVECALL(Java_java_lang_Class *)
Netscape_Java_java_lang_reflect_Method_getReturnType(Java_java_lang_reflect_Method *inMethod)
{
  Method &method = toMethod(inMethod);
  
  return ((Java_java_lang_Class *) &method.getReturnType());
}


/*
 * Class : java/lang/reflect/Method
 * Method : invoke
 * Signature : (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;
 */
NS_EXPORT NS_NATIVECALL(Java_java_lang_Object *)
Netscape_Java_java_lang_reflect_Method_invoke(Java_java_lang_reflect_Method *inMethod,
					      Java_java_lang_Object *inObj, 
					      ArrayOf_Java_java_lang_Object *inParams)
{
  Method &method = toMethod(inMethod);

  checkCallingClass(method);

  if (!(method.getModifiers() & CR_METHOD_STATIC) && !inObj)
    sysThrowNullPointerException();

  JavaObject *obj = (JavaObject *) inObj;
  JavaObject **params;
  Int32 numParams;

  if (inParams) {
    params = (JavaObject **) inParams->elements;
    numParams = inParams->length;
  } else {
    JavaObject *dummy[1];
    
    // It is permissible for params to be non-null only if 
    // the method takes no arguments
    params = (JavaObject **) dummy;
    numParams = 0;
  }
  
  try {
    return ((Java_java_lang_Object *) method.invoke(obj, params, numParams));
  } catch (RuntimeError err) {
    throwMethodException(err);
  }
}


/*
 * Class : java/lang/reflect/Method
 * Method : toString
 * Signature : ()Ljava/lang/String;
 */
NS_EXPORT NS_NATIVECALL(Java_java_lang_String *)
Netscape_Java_java_lang_reflect_Method_toString(Java_java_lang_reflect_Method *inMethod)
{
  Method &method = toMethod(inMethod);

  const char *str = method.toString();

  return ((Java_java_lang_String *) &VM::intern(str));
}



}



