/* -*- 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 "NativeMethodDispatcher.h"

#include <ctype.h>

#include "prio.h"
#include "plstr.h"
#include "prprf.h"
#include "prmem.h"

#include "CUtils.h"
#include "StringUtils.h"
#include "Address.h"
#include "NameMangler.h"

/* Each of these manglers must be registered using 
 * NativeMethodDispatcher::register
 */
#include "JNIManglers.h"
#include "NetscapeManglers.h"

Vector<NameMangler *> NativeMethodDispatcher::manglers;

/*
 * create a string for the native function name by adding the
 * appropriate system-specific decorations.
 *
 * On Win32, "__stdcall" functions are exported differently, depending
 * on the compiler. In MSVC 4.0, they are decorated with a "_" in the 
 * beginning, and @nnn in the end, where nnn is the number of bytes in 
 * the arguments (in decimal). Borland C++ exports undecorated names.
 *
 * buildFunName handles different encodings depending on the value 
 * of callType. It returns false when handed a callType it can't handle
 * or which does not apply to the current platform.
 */
bool NativeMethodDispatcher::buildFunName(char *name, Int32 nameMax, 
					  Method &m,
					  NativeCallingConvention callType,
					  NameMangler &mangler)
{
#if defined(XP_PC)
  if (callType == nativeCallingConventionStdCallMSVC) {
    /* For Microsoft MSVC 4.0/5.0 */
    char suffix[6];    /* This is enough since Java never has more than 
			  256 words of arguments. */
    int realArgsSize;
    int nameLen;
    int i;
    
    realArgsSize = m.getArgsSize() + 
      mangler.getExtraArgsSize((m.getModifiers() & CR_METHOD_STATIC) != 0);
    
    PR_snprintf(suffix, sizeof(suffix), "@%d", realArgsSize);
    
    nameLen = PL_strlen(name);
  
    if (nameLen >= nameMax - 7)
      return false;

    for(i = nameLen; i > 0; i--)
      name[i] = name[i-1];
    name[0] = '_';

    PR_snprintf(name + nameLen + 1, sizeof(suffix), "%s", suffix);
    return true;
  }   
#endif

#if defined(GLOBALS_NEED_UNDERSCORE)
  int nameLen;
  int i;
  
  nameLen = PL_strlen(name);
  
  for(i = nameLen; i > 0; i--)
    name[i] = name[i-1];
  name[0] = '_';
#endif

  if (callType == nativeCallingConventionDefault)
    return true;
  else
    return false;
}

addr NativeMethodDispatcher::resolve(Method &method)
{
  const char *methodName = method.getName();
  const char *className = method.getDeclaringClass()->getName();

  const char *signature = method.getSignatureString();

  /* XXX This is not a correct upper-bound on the length */
  Int32 len = PL_strlen(methodName) + PL_strlen(className)*2 +
    PL_strlen(signature)*2 + sizeof("Netscape_Java___");
  
  TemporaryBuffer buf(len);
  char *name = buf;

  /* Try all the name-mangling schemes at our disposal, one after another..*/
  for (Uint32 i = 0; i < manglers.size(); i++) {
    if (!manglers[i]->mangle(className, methodName, signature, name, len))
      continue;
    
    Int32 numConventions = manglers[i]->numNativeConventions();
    const NativeCallingConvention *conventions = 
      manglers[i]->getNativeConventions();
    
    /* Try all the calling conventions that this mangling scheme supports */
    for (Int32 j = 0; j < numConventions; j++) {
      if (!buildFunName(name, len, method, conventions[j], 
			*manglers[i]))
	continue;
      
      void *address;
      PRLibrary *lib;

      if ((address = PR_FindSymbolAndLibrary(name, &lib)) != NULL)
	return functionAddress((void (*)())(manglers[i]->getCode(method, address)));
    }
  }

  return functionAddress((void (*)()) 0);
}
  

void NativeMethodDispatcher::staticInit()
{
  registerMangler(*new JNIShortMangler());
  registerMangler(*new JNILongMangler());
  registerMangler(*new NetscapeShortMangler());
  registerMangler(*new NetscapeLongMangler());
}

PRLibrary *NativeMethodDispatcher::loadLibrary(const char *simpleLibName)
{
  /* For now, we parse the path *each* time we load a library. In the future, 
   * NativeMethodDispatcher must be capable of storing a set of paths, and
   * each path could potentially be a directory, zip or jar file.
   */
  char *libPath = PR_GetLibraryPath();
  char *libName = 0;
  PRLibrary *lib = 0;
#if defined(XP_PC)
  static const char* PATH_SEPARATOR = ";";
#else
  static const char* PATH_SEPARATOR = ":";
#endif
  
  if (libPath) {
    Strtok tok(libPath);
    for (const char *token = tok.get(PATH_SEPARATOR); token; token = tok.get(PATH_SEPARATOR))
      if ((libName = PR_GetLibraryName(token, simpleLibName)) != 0) {
	lib = PR_LoadLibrary(libName);
	//free(libName);
	libName = 0;

	if (lib)
	  break;
      }
  } 
  
  /* Always search in the current directory */
  if (!lib && (libName = PR_GetLibraryName(".", simpleLibName)) != 0) {
    lib = PR_LoadLibrary(libName);
    //free(libName);
  }

  if (libPath) PR_DELETE(libPath);

  return lib;
}


#if 0
char *NativeMethodDispatcher::mangle(const char *className,
				     const char *methodName,
				     const char *signature,
				     char *buffer, Int32 len,
			       NativeMethodDispatcher::MangleType mangleType)
{
  char *bufptr = buffer;
  char *bufend = buffer + len; 

  /* For internal (Netscape-style) mangling, prefix a Netscape_ to the JNI-
   * style mangled name
   */
  if (mangleType == mangleTypeNetscapeShort || 
      mangleType == mangleTypeNetscapeLong) {
    PR_snprintf(bufptr, bufend - bufptr, "Netscape_");
    bufptr += PL_strlen(bufptr);
    mangleType = (mangleType == mangleTypeNetscapeShort) ? 
      mangleTypeJNIShort: mangleTypeJNILong;
      
  } else
    *bufptr = 0;
  
  /* Copy over Java_ */
  PL_strcat(bufptr, "Java_");
  bufptr += PL_strlen(bufptr);

  bufptr += NativeMethodDispatcher::mangleUTFString(className, bufptr, 
						    bufend - bufptr, 
						    mangleUTFJNI);

  if (bufend - bufptr > 1) 
    *bufptr++ = '_';
  
  bufptr += NativeMethodDispatcher::mangleUTFString(methodName, bufptr, 
						    bufend - bufptr, 
						    mangleUTFJNI);

  /* JNI long name: includes a mangled signature. */
  if (mangleType == mangleTypeJNILong) {
    char sig[1024];
    int i;
    if (bufend - bufptr > 2) {
      *bufptr++ = '_';
      *bufptr++ = '_';
    }

    for (i = 0; i<1023; i++)
      if ((sig[i] = signature[i]) == ')')
	break;
    
    sig[i] = 0;
    bufptr += NativeMethodDispatcher::mangleUTFString(sig, bufptr, 
						       bufend - bufptr, 
						       mangleUTFJNI);
  }

  return buffer;
}
#endif







