/* -*- Mode: C; c-file-style: "gnu" -*-
   stubs.c -- special code for outputting stubs for japharh.
   Created: Chris Toshok <toshok@hungry.com>
 */
/*
  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 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
*/
#include "config.h"
#include "ClazzFile.h"
#include "japharh.h"
#include "log.h"
#include "sig.h"
#include "util.h"

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

#define MYLOG "Javah"

extern char *javah_dest_dir;
extern JNIEnv *the_env;

static void
output_method_stub(FILE *fp,
		   char *class_name,
		   char *method_name,
		   char *sig_str,
		   int isStatic)
{
  int initial_param;
  JNIEnv *env = NULL;
  Signature *sig = SIG_parseFromJavaSig(env, sig_str);
  char *hacked_method = SIG_formatStringToNativeName(the_env, method_name);
  char *hacked_class = SIG_formatStringToNativeName(the_env, class_name);
  JAVARLOG0(MYLOG, 1, " in output_method_stub()\n");

  JAVARLOG3(MYLOG, 2, "   class_name = '%s' method_name = '%s' method_sig = '%s'\n",
	    class_name, method_name, SIG_formatToC(env, sig));

  fprintf(fp, "/* \"%s/%s()\",\n   Java_%s_%s_stub  */\n",
	  class_name, method_name,
	  hacked_class, hacked_method);
  
  fprintf(fp, "JNIEXPORT jvalue * JNICALL\n");
  fprintf(fp, "Java_%s_%s_stub ( JNIEnv *env, jvalue *p )\n",
	  hacked_class, hacked_method);
  fprintf(fp, "{\n");
  output_method_prototype(fp, "  ", class_name,
			  method_name, sig_str, isStatic);

  /* now we actually output the call to the function. 
     the return value (if there is one) is stored in p[0] */

  fprintf (fp, "  ");

  if (!SIG_isVoid(env, sig->method.return_type))
    fprintf (fp, "p[0].%c = ", 
	     SIG_toUnionSelector(env, sig->method.return_type));

  fprintf (fp, "Java_%s_%s( env, p[1].l", hacked_class, hacked_method);

  initial_param = 2;

  if (SIG_numParams(env, sig) > 0)
    {
      int i;
      
      fprintf(fp, ", ");
	  
      for (i = 0; i < SIG_numParams(env, sig); i ++)
	{
	  fprintf (fp, "p[%d].%c%s",
		   i + initial_param , 
		   SIG_toUnionSelector(env, sig->method.params[i]),
		   i < SIG_numParams(env, sig) - 1 ? ", " : "");
	}
    }
  fprintf (fp, " );\n\n");

  fprintf (fp, "  return p;\n");

  fprintf(fp, "}\n\n");
  
  SIG_free(env, sig);
}

static void
spew_clazz_to_stubs(ClazzFile *cf, FILE *fp)
{
  int i;

  for (i = 0; i < cf->num_methods; i ++)
    if (cf->methods[i]->clazz == cf
	&& cf->methods[i]->access_flags & ACC_NATIVE)
      output_method_stub(fp,
			 cf->class_name,
			 cf->methods[i]->name,
			 cf->methods[i]->sig_str,
			 (cf->methods[i]->access_flags & ACC_STATIC));

  /* now we do the inner classes */
  for (i = 0; i < cf->num_innerclasses; i++)
    {
      ClazzFile *inner_class = getInnerclass(the_env, cf, i);
      if (cf == inner_class) /* ??? */
	continue; 

      spew_clazz_to_stubs (getInnerclass(the_env, cf, i), fp);
    }

}

void
spew_stubs(ClazzFile *cf, char *bar_name)
{
  char full_name[100];
  FILE *fp;

  dots_to_underscores(bar_name);

  strcpy(full_name, javah_dest_dir);
  if (javah_dest_dir[strlen(javah_dest_dir) - 1] != '/')
    strcat(full_name, "/");
  strcat(full_name, bar_name);
  strcat(full_name, "_stubs.c");

  fp = fopen(full_name, "w");
  if (NULL == fp) {
    fprintf(stderr, "Unable to open %s for writing.\n", full_name);
    return;
  }

  fprintf (fp, "/* DO NOT EDIT THIS FILE - it is machine generated by japharh */\n");
  fprintf (fp, "#include \"jni.h\"\n\n");

  fprintf (fp, "/* Stubs for class '%s' */\n\n", bar_name);

  fprintf(fp, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n");

  spew_clazz_to_stubs(cf, fp);

  fprintf(fp, "#ifdef __cplusplus\n};\n#endif\n");
  fclose(fp);
}

