/* -*- Mode: C; c-file-style: "gnu" -*-
   clparse.c -- class file parsing.
   Created: Chris Toshok <toshok@hungry.com>, 23-Jul-97
 */
/*
  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 "ClazzFile.h"
#include "objects.h"
#include "class-repository.h"
#include "resolve.h"
#include "native-threads.h"
#include "log.h"
#include "gc.h"
#include "jniint.h"
#include "compat.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <assert.h>

/* linux doesn't have a problem with this. */
#ifndef linux
#undef HAVE_MMAN_H
#endif

#ifdef HAVE_MMAN_H
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#endif

#define MYLOG "ParseClass"

static inline u1
extract_u1(unsigned char buf[], int *pos)
{
  return buf[(*pos)++];
}
static inline u2
extract_u2(unsigned char buf[], int *pos)
{
  return (u2)(extract_u1(buf, pos) << 8) | extract_u1(buf, pos);
}
static inline u4
extract_u4(unsigned char buf[], int *pos)
{
  return ((u4)(extract_u1(buf, pos) << 24)
	  | extract_u1(buf, pos) << 16
	  | extract_u1(buf, pos) << 8
	  | extract_u1(buf, pos));
}

static unsigned char *
load_class_file(char *file_name, int *length_return, jboolean *malloced)
{
  unsigned char *buf;
  FILE *fp;
  int file_size;
  struct stat stbuf;

  file_size = stat(file_name, &stbuf);

  if (file_size == -1)
    {
      JAVARLOG1(MYLOG, 2, 
                "load_file_to_memory failed for file '%s'\n", file_name);
      return NULL;
    }

  file_size = stbuf.st_size;

  fp = fopen(file_name, "rb");

  if (fp == NULL)
    {
      JAVARLOG1(MYLOG, 2, "couldn't open '%s'\n", file_name);
      return NULL;
    }

#ifdef HAVE_MMAN_H
  {
    JAVARLOG1(MYLOG, 2, "load_class_file reading from '%s'\n", file_name);

    buf = (unsigned char*)mmap(0, file_size, PROT_READ, MAP_SHARED, fileno(fp), 0);

    assert(MAP_FAILED != buf); /* XXX should be handled more gracefully */

    *malloced = JNI_FALSE;
  }
#else
  {
    int retval;
    int feofmark;

    *malloced = JNI_TRUE;

    buf = (unsigned char *) malloc ( (size_t) file_size );
    if (NULL == buf)
      return NULL; /* XXX Should also throw ? */

    JAVARLOG1(MYLOG, 2, "load_class_file reading from '%s'\n", file_name);

    retval = fread ((void *) buf, sizeof(unsigned char), (size_t) file_size, fp);
    feofmark = feof(fp);
    clearerr(fp);

    if ((retval < file_size && retval >= 0) && feofmark == 0)
      {
	JAVARLOG2(MYLOG, 2, "load_file_to_memory: only got %d out of %d bytes\n",
		  retval, file_size);
	JAVARLOG0(MYLOG, 2, "fread failed.\n");
	return NULL;
      }

    if (retval < 0)
      {
	JAVARLOG0(MYLOG, 2, "fread failed.\n");
	return NULL;
      }
  }
#endif

  fclose(fp);

  *length_return = file_size;
  return buf;
}

ConstantPoolEntry *
get_constant(ClazzFile *clazz, int index)
{
  assert(NULL != clazz);
  assert(index > 0);
  assert(index < clazz->constant_pool_count);

  return &clazz->constants[index];
}

static void
parse_field_attribute(JNIEnv *env, unsigned char *buf, int *buf_pos, FieldStruct *field)
{
  u2 attribute_name;
  u4 attribute_length;
  char *att_name;

  attribute_name = extract_u2(buf, buf_pos);
  attribute_length = extract_u4(buf, buf_pos);

  att_name = ResolveUtf8(env, field->clazz,
			 get_constant(field->clazz, attribute_name));

  if (!strcmp("ConstantValue", att_name))
    {
      u2 index;

      index = extract_u2(buf, buf_pos);

      field->has_constant_value = 1;
      field->constant_value_index = index;
      field->constant_value.l = (void*)VALUE_UNRESOLVED;
    }
  else if (!strcmp("Deprecated", att_name))
    {
      field->flags |= FLAG_DEPRECATED;
    }
  else if (!strcmp("Synthetic", att_name))
    {
      field->flags |= FLAG_SYNTHETIC;
    }
  else
    {
#ifdef DEBUG
      printf ("Unhandled field attribute type '%s'\n", att_name);
#endif
      *buf_pos += attribute_length;
    }
}

static void
parse_field(JNIEnv *env, unsigned char *buf, int *buf_pos, ClazzFile *clazz,
	    FieldStruct *new_field)
{
  u2 tmp_u2;
  Signature *new_sig;

  new_field->clazz = clazz;
  new_field->field_offset = VALUE_UNRESOLVED;
  new_field->java_type = VALUE_UNRESOLVED;

  new_field->access_flags = extract_u2(buf, buf_pos);

  if (new_field->access_flags & ACC_STATIC)
    clazz->num_static_fields ++;
  else
    clazz->num_instance_fields ++;

  /* get the name of the field */
  tmp_u2 = extract_u2(buf, buf_pos);

  new_field->name = ResolveUtf8(env, clazz, get_constant(clazz,tmp_u2));

  /* get the signature of the field */
  tmp_u2 = extract_u2(buf, buf_pos);
  new_field->sig_str = ResolveUtf8(env, clazz, get_constant(clazz,tmp_u2));
  new_sig = SIG_parseFromJavaSig(env, new_field->sig_str);
  new_field->sig_size_in_bytes = SIG_sizeInBytes(env, new_sig);

  if (new_sig->any.tag == SIG_CLASS || new_sig->any.tag == SIG_ARRAY)
    new_field->java_type = SIG_JOBJECT;
  else
    new_field->java_type = new_sig->prim.type;

  SIG_free(env, new_sig);

  /* parse the attributes of this field */
  tmp_u2 = extract_u2(buf, buf_pos);
  if (tmp_u2)
    {
      int att_num;

      for (att_num = 0;
	   att_num < tmp_u2;
	   att_num ++)
	parse_field_attribute(env, buf, buf_pos, new_field);
    }
}

static void
parse_method_attribute(JNIEnv *env, unsigned char *buf, int *buf_pos, MethodStruct *method)
{
  u2 attribute_name;
  u4 attribute_length;
  char *att_name;

  attribute_name = extract_u2(buf, buf_pos);
  attribute_length = extract_u4(buf, buf_pos);

  att_name = ResolveUtf8(env,method->clazz,
			 get_constant(method->clazz, attribute_name));

  if (!strcmp("Code", att_name))
    {
      u2 tmp_u2;

      method->max_stack = extract_u2(buf, buf_pos);
      method->max_locals = extract_u2(buf, buf_pos);
      method->code_length = extract_u4(buf, buf_pos);

      if (method->code_length)
	{
	  method->code = (u1*)calloc(method->code_length,
				     sizeof(u1));

	  memcpy(method->code, buf + *buf_pos, method->code_length);
	  *buf_pos += method->code_length;
	}

      method->num_exception_blocks = extract_u2(buf, buf_pos);
      
      if (method->num_exception_blocks)
	{
	  int i;
	  method->exceptions = (ExceptionBlock*)calloc(method->num_exception_blocks,
						       sizeof(ExceptionBlock));

	  for (i = 0;
	       i < method->num_exception_blocks;
	       i ++)
	    {
	      method->exceptions[i].start_pc = extract_u2(buf, buf_pos);
	      method->exceptions[i].end_pc = extract_u2(buf, buf_pos);
	      method->exceptions[i].handler_pc = extract_u2(buf, buf_pos);
	      method->exceptions[i].catch_clazz_index = extract_u2(buf, buf_pos);
	    }
	}

      tmp_u2 = extract_u2(buf, buf_pos);
      if (tmp_u2)
	{
	  int att_num;

	  for (att_num = 0; att_num < tmp_u2; att_num ++)
	    parse_method_attribute(env, buf, buf_pos, method);
	}
    }
  else if (!strcmp("Exceptions", att_name))
    {
      int i;

      method->num_throwable_exceptions = extract_u2(buf, buf_pos);
      method->throwable_exceptions =
	(ClazzFile**) calloc(method->num_throwable_exceptions,
			     sizeof(ClazzFile*));
      method->throwable_exception_indices =
	(u2*) calloc(method->num_throwable_exceptions,
		     sizeof(u2));

      for (i = 0; i < method->num_throwable_exceptions; i ++) {
	method->throwable_exception_indices[i] = extract_u2(buf, buf_pos);
      }
    }
  else if (!strcmp("LineNumberTable", att_name))
    {
      method->num_line_number_blocks = extract_u2(buf, buf_pos);

      if (method->num_line_number_blocks)
	{
	  int i;

	  method->line_numbers = (LineNumberBlock*)calloc(method->num_line_number_blocks,
							  sizeof(LineNumberBlock));
	  
	  for (i = 0; i < method->num_line_number_blocks; i ++)
	    {
	      method->line_numbers[i].start_pc = extract_u2(buf, buf_pos);
	      method->line_numbers[i].line_number = extract_u2(buf, buf_pos);
	    }
	}
    }
  else if (!strcmp("LocalVariableTable", att_name))
    {
      method->num_local_variables = extract_u2(buf, buf_pos);

      if (method->num_local_variables)
	{
	  int i;
	  u2 tmp_u2;
	  method->local_variables = (LocalVariableEntry*)calloc(method->num_local_variables,
								sizeof(LocalVariableEntry));

	  for (i = 0; i < method->num_local_variables; i ++)
	    {
	      method->local_variables[i].start_pc = extract_u2(buf, buf_pos);
	      tmp_u2 = extract_u2(buf, buf_pos);
	      method->local_variables[i].end_pc = method->local_variables[i].start_pc + tmp_u2;

	      tmp_u2 = extract_u2(buf, buf_pos);
	      method->local_variables[i].name =
		ResolveUtf8(env, method->clazz,
			    get_constant(method->clazz, tmp_u2));

	      tmp_u2 = extract_u2(buf, buf_pos);
	      method->local_variables[i].sig =
		SIG_parseFromJavaSig(env,
				     ResolveUtf8(env, method->clazz, 
						 get_constant(method->clazz, tmp_u2)));

	      method->local_variables[i].slot = extract_u2(buf, buf_pos);
	    }
	}

    }
  else if (!strcmp("Deprecated", att_name))
    {
      method->flags |= FLAG_DEPRECATED;
    }
  else if (!strcmp("Synthetic", att_name))
    {
      method->flags |= FLAG_SYNTHETIC;
    }
  else
    {
#ifdef DEBUG
      printf ("Unhandled method attribute type '%s'\n", att_name);
#endif
      *buf_pos += attribute_length;
    }
}

static void
parse_method(JNIEnv *env, unsigned char *buf, int *buf_pos, ClazzFile *clazz,
	     MethodStruct *new_method)
{
  u2 tmp_u2;

  new_method->clazz = clazz;

  new_method->access_flags = extract_u2(buf, buf_pos);

  /* get the name of the method */
  tmp_u2 = extract_u2(buf, buf_pos);
  new_method->name = ResolveUtf8(env, clazz, get_constant(clazz,tmp_u2));

  /* get the signature of the method */
  tmp_u2 = extract_u2(buf, buf_pos);
  new_method->sig_str = ResolveUtf8(env, clazz, get_constant(clazz,tmp_u2));

  if (!strcmp(new_method->name, "<clinit>")
      && !strcmp(ResolveUtf8(env, clazz, get_constant(clazz, tmp_u2)), "()V")
      && new_method->access_flags & ACC_STATIC)
    {
      clazz->access_flags |= HAS_CLINIT;
    }

  JAVARLOG2(MYLOG, 2, "Parsing method %s %s\n", new_method->name,
	    new_method->access_flags & ACC_NATIVE ? "(native)" : "");

  {
    /* figure out how many words need to be there to call this method. */
    /* this should perhaps be moved off until we resolve the method.... */
    int i;

    Signature *sig = SIG_parseFromJavaSig(env, new_method->sig_str);

    new_method->num_param_words = 0;
    for (i = 0; i < SIG_numParams(env, sig); i ++)
      new_method->num_param_words += SIG_sizeInWords(env, sig->method.params[i]);

    SIG_free(env, sig);
  }
  
  /* parse the attributes of this method */
  tmp_u2 = extract_u2(buf, buf_pos);
  if (tmp_u2)
    {
      int att_num;

      for (att_num = 0;
	   att_num < tmp_u2;
	   att_num ++)
	parse_method_attribute(env, buf, buf_pos, new_method);
    }
  new_method->native_func = NULL;

  JAVARLOG1(MYLOG, 2, "Done parsing method %s\n", new_method->name);
}

static void
parse_class_attribute(JNIEnv *env, unsigned char *buf, int *buf_pos, ClazzFile *clazz)
{
  u2 attribute_name;
  u4 attribute_length;
  char *att_name;

  attribute_name = extract_u2(buf, buf_pos);
  attribute_length = extract_u4(buf, buf_pos);

  att_name = ResolveUtf8(env, clazz, get_constant(clazz,attribute_name));

  if (!strcmp("SourceFile", att_name))
    {
      u2 source_file_index;
      source_file_index = extract_u2(buf, buf_pos);

      clazz->source_filename = ResolveUtf8(env,clazz, get_constant(clazz,source_file_index));
    }
  else if (!strcmp("InnerClasses", att_name))
    {
      u2 i;

      clazz->num_innerclasses = extract_u2(buf, buf_pos);

      if (clazz->num_innerclasses)
	{
	  clazz->innerclass_indices = (u2*)calloc(clazz->num_innerclasses, sizeof(u2));
	  clazz->innerclasses = (ClazzFile**)calloc(clazz->num_innerclasses, sizeof(ClazzFile*));
	}

      for (i = 0; i < clazz->num_innerclasses; i ++)
	{
	  clazz->innerclass_indices[i] = extract_u2(buf, buf_pos);
	  (void)extract_u2(buf, buf_pos); /* outer_class_info_index */
	  (void)extract_u2(buf, buf_pos); /* inner_name_index */
	  (void)extract_u2(buf, buf_pos); /* inner_class_access_flags */
	}
    }
  else if (!strcmp("AbsoluteSourcePath", att_name))
    {
      u2 tmp;
      /* don't do anything...  should this just override SourcePath if present? */
      tmp = extract_u2(buf, buf_pos);
    }
  else if (!strcmp("Deprecated", att_name))
    {
      clazz->flags |= FLAG_DEPRECATED;
    }
  else if (!strcmp("Synthetic", att_name))
    {
      clazz->flags |= FLAG_SYNTHETIC;
    }
  else
    {
#ifdef DEBUG
      printf ("Unknown class attribute type '%s'\n", att_name);
#endif
      *buf_pos += attribute_length;
    }
}

static void
parse_constant(unsigned char *buf, int *buf_pos, ConstantPoolEntry *entry)
{
  u1 tmp_u1;

  tmp_u1 = extract_u1(buf, buf_pos);
  entry->generic.tag = tmp_u1;

  JAVARLOG1(MYLOG, 2, " parsing constant type %d\n", tmp_u1);
  switch (entry->generic.tag)
    {
    case CONSTANT_Utf8:
      {
	/* this is just screaming for optimization... */

	entry->utf8_info.length = extract_u2(buf, buf_pos);
	if (0 < entry->utf8_info.length) {
	  entry->utf8_info.bytes = (u1*)malloc(entry->utf8_info.length * sizeof(u1));

	  memcpy(entry->utf8_info.bytes, buf + *buf_pos, entry->utf8_info.length);
	  *buf_pos += entry->utf8_info.length;

	} else {
	  entry->utf8_info.bytes = NULL;
	  JAVARLOG0(MYLOG, 1, " Extracting CONSTANT_UTF8 of zero length!\n");
	}
	break;
      }
    case CONSTANT_Unicode:
      {
	int i;

	entry->unicode_info.length = extract_u2(buf, buf_pos);
	entry->unicode_info.bytes = (u2*)malloc(sizeof(u2) * entry->utf8_info.length);
	assert(NULL != entry->unicode_info.bytes);

	for (i  =0; i < entry->utf8_info.length; i ++)
	  entry->unicode_info.bytes[i] = extract_u2(buf, buf_pos);
	break;
      }
    case CONSTANT_Integer:
      entry->integer_info.bytes = extract_u4(buf, buf_pos);
      break;
    case CONSTANT_Float:
      entry->float_info.bytes = extract_u4(buf, buf_pos);
      break;
    case CONSTANT_Long:
      entry->long_info.high_bytes = extract_u4(buf, buf_pos);
      entry->long_info.low_bytes = extract_u4(buf, buf_pos);
      break;
    case CONSTANT_Double:
      entry->double_info.high_bytes = extract_u4(buf, buf_pos);
      entry->double_info.low_bytes = extract_u4(buf, buf_pos);
      break;
    case CONSTANT_Class:
      entry->clazz_info.name_index = extract_u2(buf, buf_pos);
      break;
    case CONSTANT_String:
      entry->string_info.string_index = extract_u2(buf, buf_pos);
      break;
    case CONSTANT_Fieldref:
      entry->fieldref_info.class_index = extract_u2(buf, buf_pos);
      entry->fieldref_info.name_and_type_index = extract_u2(buf, buf_pos);
      break;
    case CONSTANT_Methodref:
      entry->methodref_info.class_index = extract_u2(buf, buf_pos);
      entry->methodref_info.name_and_type_index = extract_u2(buf, buf_pos);
      break;
    case CONSTANT_InterfaceMethodref:
      entry->interfacemethodref_info.class_index = extract_u2(buf, buf_pos);
      entry->interfacemethodref_info.name_and_type_index = extract_u2(buf, buf_pos);
      break;
    case CONSTANT_NameAndType:
      entry->nameandtype_info.name_index = extract_u2(buf, buf_pos);
      entry->nameandtype_info.signature_index = extract_u2(buf, buf_pos);
      break;
    default:
      fprintf (stderr, "invalid constant tag: %d, pos is %d\n",
	       entry->generic.tag, *buf_pos);
      abort();
    }
}

#if notyet
static int
method_compare(const void *a, const void*b)
{
  MethodStruct *ma = (MethodStruct*)a;
  MethodStruct *mb = (MethodStruct*)b;
  int comp;

  /* use name first. */
  comp = strcmp(ma->name, mb->name);
  if (comp != 0) return comp;

  /* then signature */
  comp = strcmp(ma->sig_str, mb->sig_str);
  if (comp != 0) return comp;

  /* then by static/non-static */
  if ((ma->access_flags & ACC_STATIC) && !(mb->access_flags & ACC_STATIC))
    return 1;
  else if ((mb->access_flags & ACC_STATIC) && !(ma->access_flags & ACC_STATIC))
    return -1;

  /* and lastly inheritance */
  /* we know they can't be equal - the methods would be identical in
     that case.  so, we just choose ma's clazz and go up the tree
     until we hit the root.  if we find mb's clazz, return -1.
     otherwise, return 1. */
  {
    JNIEnv *env = THREAD_getEnv();
    ClazzFile *cur = ma->clazz;

    while (cur)
      {
	if (cur == mb->clazz) return -1;
	cur = getSuperClass(env, cur);
      }
  }
  return 1;
}
#endif

static void
collapse_methods(ClazzFile *clazz)
{
#if 0
  int i;

  qsort(clazz->methods, clazz->num_methods, sizeof(MethodStruct*),
	method_compare);

  for (i = 0; i < clazz->num_methods; i++)
    {
      int j;

      char *method_name = clazz->methods[i]->name;
      char *method_sig = clazz->methods[i]->sig_str;

      for (j = i+1; j < clazz->num_methods; j++)
	{
	  int num = 0;

	  if (!strcmp(method_name, clazz->methods[j]->name)
	      && !strcmp(method_sig, clazz->methods[j]->sig_str))
	    {
	      num++;
	      continue;
	    }
	  else if (num)
	    {
	      int k;
	      for (k = i + 1; k < clazz->num_methods - num; k++)
		clazz->methods[k] = clazz->methods[k + num];

	      clazz->num_methods -= num;
	    }
	}
    }
#endif
}

ClazzFile *
define_class(JNIEnv *env, unsigned char *buf, int buf_length)
{
  HungryJavaVM *vm = NULL;
  int buf_pos = 0;
  ClazzFile *clazz;
  u4 tmp_u4;
  u2 tmp_u2;
  int constant_num;
  u2 thisclass_index;
  ClazzFile *superclass;

  if (env)
    vm = ((HungryJNIEnv*)env)->_vm;

  tmp_u4 = extract_u4(buf, &buf_pos);

  if (tmp_u4 != 0xCAFEBABE)
    {
      fprintf (stderr, "magic is wrong -- not a java class..\n");
      return NULL;
    }

  clazz = (ClazzFile*)JGC_allocClazzFile((JavaVM*)vm, sizeof(ClazzFile));
  if (NULL == clazz)
    return NULL; /* XXX Should also throw ? */

  clazz->minor = extract_u2(buf, &buf_pos);
  JAVARLOG1(MYLOG, 2, "minor_version is %d\n", clazz->minor);

  clazz->major = extract_u2(buf, &buf_pos);
  JAVARLOG1(MYLOG, 2, "major_version is %d\n", clazz->major);

  clazz->constant_pool_count = extract_u2(buf, &buf_pos);
  JAVARLOG1(MYLOG, 2, "%d constants in constant_pool_count\n", clazz->constant_pool_count);

  clazz->constants = (ConstantPoolEntry*) calloc (clazz->constant_pool_count + 1, /* fuckers. */
						  sizeof(ConstantPoolEntry));
  
  for (constant_num = 1;
       constant_num < clazz->constant_pool_count;
       constant_num ++)
    {
      ConstantPoolEntry *constant = get_constant(clazz,constant_num);
      JAVARLOG1(MYLOG, 2, "getting constant #%d\n", constant_num);

      parse_constant(buf, &buf_pos, constant);

      if (constant->generic.tag == CONSTANT_Double ||
	  constant->generic.tag == CONSTANT_Long)
	constant_num ++;
    }

  clazz->access_flags = extract_u2 (buf, &buf_pos);
  thisclass_index = extract_u2 (buf, &buf_pos);
  clazz->superclass_index = extract_u2 (buf, &buf_pos);
  superclass = getSuperClass(env, clazz);

  /* fill in the name of this class. */
  clazz->class_name = ResolveClassName(env, clazz, get_constant(clazz,thisclass_index));

  /* build up this class's interface table by collapsing down the
     interface from its parent class, as well as parsing new ones. */
  {
    int num_superclass_interfaces = 0;
    
    if (superclass)
      num_superclass_interfaces = superclass->num_interfaces;

    clazz->num_interfaces = extract_u2 (buf, &buf_pos);
    if (clazz->num_interfaces > 0
	|| num_superclass_interfaces > 0)
      {
	int interface_num;
	JAVARLOG1(MYLOG, 2, "%d interfaces to read\n", clazz->num_interfaces);
	
	clazz->interfaces = (ClazzFile**)malloc((clazz->num_interfaces
						 + num_superclass_interfaces)
						* sizeof(ClazzFile*));
	
	for (interface_num = 0;
	     interface_num < clazz->num_interfaces;
	     interface_num ++)
	  {
	    u2 interface_index;

	    interface_index = extract_u2(buf, &buf_pos);
	    clazz->interfaces[ interface_num ] =
	      ResolveClass(env, clazz,
			   get_constant(clazz,
					interface_index));
	  }

	/*
	** now loop through the parent class's interfaces and add the ones we don't override.
	** (would it hurt to just add all the parent interfaces onto the end of the list of
	**  interfaces?  if they're overridden by this class they would appear earlier in the list
	**  so we'd never find the wrong interface...)
	*/
	if (superclass)
	  {
	    for (interface_num = 0;
		 interface_num < num_superclass_interfaces;
		 interface_num ++)
	      {
		clazz->interfaces[ clazz->num_interfaces ++ ] =
		  superclass->interfaces[ interface_num ];
	      }
	  }
      }
  }

  /* build up this class's field table by collapsing down the
     fields of its parent classes. */
  {
    int num_superclass_fields = 0;
    
    if (superclass)
      num_superclass_fields = superclass->num_fields;

    clazz->num_fields = extract_u2 (buf, &buf_pos);
    if (clazz->num_fields > 0
	|| num_superclass_fields)
      {
	int field_index;
	
	JAVARLOG3(MYLOG, 2, "%d (0x%x) fields to parse (pos=%d)\n",
		  clazz->num_fields, clazz->num_fields, buf_pos);
	clazz->fields = (FieldStruct**)malloc((clazz->num_fields
					       + num_superclass_fields) 
					      * sizeof(FieldStruct*));

	clazz->field_pool = (FieldStruct*)calloc(clazz->num_fields,
						 sizeof(FieldStruct));

	for (field_index = 0;
	     field_index < clazz->num_fields;
	     field_index ++)
	  {
	    JAVARLOG1(MYLOG, 2, "parsing field #%d\n", field_index);
	    parse_field(env, buf, &buf_pos, clazz, &clazz->field_pool[ field_index ]);
	    clazz->fields[ field_index ] = &clazz->field_pool[ field_index ];
	  }

	/*
	** now loop through the parent class's fields and add the ones we don't override.
	** (would it hurt to just add all the parent methods onto the end of the list of
	**  methods?  if they're overridden by this class they would appear earlier in the list
	**  so we'd never find the wrong method...)
	*/
	if (superclass)
	  {
	    for (field_index = 0;
		 field_index < num_superclass_fields;
		 field_index ++)
	      {
		/*		if (!(superclass->fields[ field_index ]->access_flags & ACC_PRIVATE))*/
		clazz->fields[ clazz->num_fields ++ ] = superclass->fields[ field_index ];
	      }
	  }
      }
  }

  /* build up this class's method table by collapsing down the
     methods of its parent classes. */
  {
    int num_superclass_methods = 0;
    
    if (superclass)
      num_superclass_methods = superclass->num_methods;
    
    clazz->num_methods = extract_u2 (buf, &buf_pos);
    if (clazz->num_methods > 0
	|| num_superclass_methods > 0)
      {
	int method_num;
	
	/* 
	** we're guaranteed that we can't have more methods than
	** current_class->num_methods + superclass->num_methods, and many
	** times we'll have less.
	*/
	clazz->methods = (MethodStruct**)malloc((clazz->num_methods
						 + num_superclass_methods)
						* sizeof(MethodStruct*));

	clazz->method_pool = (MethodStruct*)calloc(clazz->num_methods,
						   sizeof(MethodStruct));

	for (method_num = 0;
	     method_num < clazz->num_methods;
	     method_num ++)
	  {
	    parse_method(env, buf, &buf_pos, clazz, &clazz->method_pool[ method_num ]);
	    clazz->methods[ method_num ] = &clazz->method_pool[ method_num ];
	  }
	
	/*
	** now loop through the parent class's methods and add the ones we don't override.
	** (would it hurt to just add all the parent methods onto the end of the list of
	**  methods?  if they're overridden by this class they would appear earlier in the list
	**  so we'd never find the wrong method...)
	*/
	if (superclass)
	  {
	    for (method_num = 0;
		 method_num < num_superclass_methods;
		 method_num ++)
	      {
		/*		if (!(superclass->methods[ method_num ]->access_flags & ACC_PRIVATE))*/
		clazz->methods[ clazz->num_methods ++ ] = superclass->methods[ method_num ];
	      }
	  }

	collapse_methods(clazz);
      }
  }

  /* parse through the attributes of this class */
  tmp_u2 = extract_u2 (buf, &buf_pos);
  if (tmp_u2)
    {
      int att_num;
      for (att_num = 0;
	   att_num < tmp_u2;
	   att_num ++)
	{
	  parse_class_attribute(env, buf, &buf_pos, clazz);
	}
    }

  return clazz;
}

ClazzFile *
parse_class(JNIEnv *env, char *full_class_name)
{
  unsigned char *buf = NULL;
  int buf_length = 0;
  jboolean malloced;
  ClazzFile *result;

  buf = load_class_file(full_class_name, &buf_length, &malloced);

  if (buf == NULL
      || buf_length == 0)
    {
      return NULL;
    }

  result = define_class(env, buf, buf_length);

  if (malloced) free(buf);

  return result;
}

void
class_finalize(ClazzFile *clazz)
{
  int i;
  JNIEnv *env = THREAD_getEnv();
  printf ("Finalizing class (%s)\n", getClassName(env, clazz));

  delete_class_from_repository(env, getClassName(env, clazz));

  /* free the method structs */
  for (i = 0; i < clazz->num_methods; i ++)
    {
      free(clazz->methods[i]);
    }
  if (clazz->methods)
    free(clazz->methods);

  /* free the field structs */
  for (i = 0; i < clazz->num_fields; i ++)
    {
      free(clazz->fields[i]);
    }
  if (clazz->fields)
    free(clazz->fields);

  if (clazz->innerclasses)
    free(clazz->innerclasses);
  if (clazz->interfaces)
    free(clazz->interfaces);

  /* free our constants. */
  for (i = 0; i < clazz->constant_pool_count; i ++)
    {
    }
  free(clazz->constants);

  if (clazz->static_fields)
    free(clazz->static_fields);
  if (clazz->class_name)
    free(clazz->class_name);
  if (clazz->source_filename)
    free(clazz->source_filename);

  free(clazz);
}

ClazzFile *
ExceptionBlock_getHandlerClazz(JNIEnv *env, ClazzFile *clazz, ExceptionBlock *block)
{
  if (block->catch_clazz == NULL)
    {
      if (block->catch_clazz_index == 0)
	{
	  /* this seems to be used to catch any type of exception. */
	  /* as in:
	   
	     Exception table:
	     from   to  target type
	     8   103   108   any


	     -- actually, this is used to implement finally blocks.
	  */
	  block->catch_clazz = find_class(env, "java/lang/Object");
	}
      else
	{
	  block->catch_clazz = ResolveClass(env, clazz, get_constant(clazz,block->catch_clazz_index));
	}
    }
  return block->catch_clazz;
}

char *
ResolveClassName(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *constant)
{
  assert(constant->generic.tag & CONSTANT_Class);

  if (!(constant->generic.tag & CONSTANT_RESOLVED))
    {
      u2 name_index = constant->clazz_info.name_index;
      
      constant->generic.tag |= CONSTANT_RESOLVED;
      constant->res_clazz_info.name = ResolveUtf8(env, clazz, get_constant(clazz,name_index));
      constant->res_clazz_info.clazz = NULL;
    }
  
  return constant->res_clazz_info.name;
}

ClazzFile *
ResolveClass(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *constant)
{
  assert(constant->generic.tag & CONSTANT_Class);

  if (!(constant->generic.tag & CONSTANT_RESOLVED))
    {
      u2 name_index = constant->clazz_info.name_index;

      constant->generic.tag |= CONSTANT_RESOLVED;
      constant->res_clazz_info.name = ResolveUtf8(env, clazz, get_constant(clazz,name_index));
    }

  if (constant->res_clazz_info.clazz == NULL)
    {
      constant->res_clazz_info.clazz =
	find_class(env, constant->res_clazz_info.name);
    }

  return constant->res_clazz_info.clazz;
}

char *
ResolveUtf8(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *constant)
{
  /* If string length is 0 then bytes == NULL and we make value = "" */
#if 1
  assert(constant->generic.tag & CONSTANT_Utf8);
  if (constant->generic.tag & CONSTANT_RESOLVED)
    return ((ResolvedUtf8Constant*)constant)->value;
  else
    {
      char *value;

      value = (char*)malloc(constant->utf8_info.length + 1);
      assert(NULL != value);
      memcpy(value, constant->utf8_info.bytes, constant->utf8_info.length);
      value[constant->utf8_info.length] = 0;

      if (NULL != constant->utf8_info.bytes)
	free(constant->utf8_info.bytes);

      constant->res_utf8_info.tag |= CONSTANT_RESOLVED;
      constant->res_utf8_info.value = value;

      return value;
    }
#else
  char *value;

  value = (char*)malloc(constant->utf8_info.length + 1);
  memcpy(value, constant->utf8_info.bytes, constant->utf8_info.length);
  value[constant->utf8_info.length] = 0;
      
  return value;
#endif
}

MethodStruct *
ResolveNonVirtualMethodRef(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *entry)
{
  assert(entry->generic.tag & CONSTANT_Methodref);

  if (!(entry->generic.tag & CONSTANT_RESOLVED)
      || entry->res_method_info.nonvirtual_method == NULL)
    {
      int i;
      MethodStruct *method = NULL;
      ClazzFile *method_clazz;
      char *method_name;
      /*Signature *sig;*/
      char *sig;

      if (entry->generic.tag & CONSTANT_RESOLVED)
	{
	  method_clazz = entry->res_method_info.clazz;
	  method_name = entry->res_method_info.name;
	  sig = entry->res_method_info.sig_str;
	}
      else
	{
	  char *method_sig;
		  
	  method_clazz = ResolveClass(env, clazz, get_constant(clazz,entry->methodref_info.class_index));
	  method_name = ResolveUtf8(env, clazz, 
				    get_constant(clazz,
						 get_constant(clazz,
							      entry->methodref_info.name_and_type_index)->nameandtype_info.name_index));
	  method_sig = ResolveUtf8(env, clazz,
				   get_constant(clazz,
						get_constant(clazz,entry->methodref_info.name_and_type_index)->nameandtype_info.signature_index));

	  /*sig = SIG_parseFromJavaSig(env, method_sig);*/
	  sig = method_sig;
	  entry->res_method_info.clazz = method_clazz;
	  entry->res_method_info.name = method_name;
	  entry->res_method_info.sig_str = method_sig;
	}

      for (i = 0; i < method_clazz->num_methods; i ++)
	{
	  if (!strcmp(method_clazz->methods[i]->name, method_name)
	      && !strcmp(method_clazz->methods[i]->sig_str, sig)
	      /*&& SIG_equal(env, method_clazz->methods[i]->sig, sig)*/)
	    {
	      method = method_clazz->methods[i];
	      break;
	    }
	}

      assert(method != NULL);

      entry->generic.tag |= CONSTANT_RESOLVED;
      entry->res_method_info.nonvirtual_method = method;
    }

  return entry->res_method_info.nonvirtual_method;
}

MethodStruct *
ResolveStaticMethodRef(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *entry)
{
  assert(entry->generic.tag & CONSTANT_Methodref);

  if (!(entry->generic.tag & CONSTANT_RESOLVED)
      || entry->res_method_info.static_method == NULL)
    {
      int i;
      MethodStruct *method = NULL;
      char *method_name;
      /*      Signature *sig;*/
      char *sig;
      ClazzFile *method_clazz;

      if (entry->generic.tag & CONSTANT_RESOLVED)
	{
	  method_clazz = entry->res_method_info.clazz;
	  method_name = entry->res_method_info.name;
	  sig = entry->res_method_info.sig_str;
	}
      else
	{
	  char *method_sig;

	  method_clazz = ResolveClass(env, clazz,
				      get_constant(clazz,entry->methodref_info.class_index));

	  method_name = ResolveUtf8(env, clazz, 
				    get_constant(clazz,
						 get_constant(clazz,
							      entry->methodref_info.name_and_type_index)->nameandtype_info.name_index));
	  method_sig = ResolveUtf8(env, clazz,
				   get_constant(clazz,
						get_constant(clazz,entry->methodref_info.name_and_type_index)->nameandtype_info.signature_index));
	  /*	  sig = SIG_parseFromJavaSig(env, method_sig);*/
	  sig = method_sig;
	  entry->res_method_info.clazz = method_clazz;
	  entry->res_method_info.name = method_name;
	  entry->res_method_info.sig_str = sig;
	}

      for (i = 0; i < method_clazz->num_methods; i ++)
	{
	  if (!strcmp(method_clazz->methods[i]->name, method_name)
	      && !strcmp(method_clazz->methods[i]->sig_str, sig)
	      /*&& SIG_equal(env, method_clazz->methods[i]->sig, sig)*/
	      && method_clazz->methods[i]->access_flags & ACC_STATIC)
	    {
	      method = method_clazz->methods[i];
	      break;
	    }
	}

      assert(method != NULL);

      entry->generic.tag |= CONSTANT_RESOLVED;
      entry->res_method_info.static_method = method;
    }

  return entry->res_method_info.static_method;
}

MethodStruct *
GetMethodByNameAndSig(JNIEnv *env, ClazzFile *clazz, char *method_name, char *sig_str)
{
  int i;
  MethodStruct *result = NULL;

  for (i = 0; i < clazz->num_methods; i ++)
    if (!strcmp(clazz->methods[i]->name, method_name)
	&& !strcmp(clazz->methods[i]->sig_str, sig_str))
      {
	result = clazz->methods[i];
	break;
      }

  return result;
}

int
GetMethodByName(JNIEnv *env, ClazzFile *clazz, char *method_name, MethodStruct ***methods)
{
  int num_found = 0;
  int i;

  for (i = 0; i < clazz->num_methods; i ++)
    if (!strcmp(clazz->methods[i]->name, method_name))
      {
	if (methods)
	  (*methods[num_found]) = clazz->methods[i];
		
	num_found ++;
      }

  return num_found;
}

void
ResolveMethod(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *constant,
	      char **name, char **sig_str_ret)
{
  assert(constant->generic.tag & CONSTANT_Methodref);
  if (constant->generic.tag & CONSTANT_RESOLVED)
    {
      *sig_str_ret = constant->res_method_info.sig_str;
      *name = constant->res_method_info.name;
    }
  else
    {
      ClazzFile *method_clazz = ResolveClass(env, clazz,
					     get_constant(clazz,constant->methodref_info.class_index));
      char *sig_str = ResolveUtf8(env, clazz,
				  get_constant(clazz,
					       get_constant(clazz,
							    constant->methodref_info.name_and_type_index)->nameandtype_info.signature_index));
      char *name_str = ResolveUtf8(env, clazz,
				   get_constant(clazz,
						get_constant(clazz,
							     constant->methodref_info.name_and_type_index)->nameandtype_info.name_index));

      constant->res_method_info.tag |= CONSTANT_RESOLVED;
      constant->res_method_info.sig_str = sig_str;
      constant->res_method_info.name = name_str;
      constant->res_method_info.clazz = method_clazz;

      *sig_str_ret = constant->res_method_info.sig_str;
      *name = constant->res_method_info.name;
    }
}

void
ResolveInterfaceMethod(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *constant,
		       char **name, char **sig_str_ret)
{
  assert(constant->generic.tag & CONSTANT_Methodref);
  if (constant->generic.tag & CONSTANT_RESOLVED)
    {
      *sig_str_ret = constant->res_interfacemethodref_info.sig_str;
      *name = constant->res_interfacemethodref_info.name;
    }
  else
    {
      char *sig_str = ResolveUtf8(env, clazz,
				  get_constant(clazz,
					       get_constant(clazz,
							    constant->interfacemethodref_info.name_and_type_index)->nameandtype_info.signature_index));
      char *name_str = ResolveUtf8(env, clazz,
				   get_constant(clazz,
						get_constant(clazz,
							     constant->interfacemethodref_info.name_and_type_index)->nameandtype_info.name_index));

      constant->res_interfacemethodref_info.tag |= CONSTANT_RESOLVED;
      constant->res_interfacemethodref_info.name = name_str;
      constant->res_interfacemethodref_info.sig_str = sig_str;

      *sig_str_ret = constant->res_interfacemethodref_info.sig_str;
      *name = constant->res_interfacemethodref_info.name;
    }
}

FieldStruct *
ResolveFieldRef(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *entry)
{
  assert(entry->generic.tag & CONSTANT_Fieldref);

  if (!(entry->generic.tag & CONSTANT_RESOLVED))
    {
      int i;
      FieldStruct *field = NULL;
      ClazzFile *field_clazz = ResolveClass(env, clazz,
					    get_constant(clazz, entry->fieldref_info.class_index));
      char *field_name = ResolveUtf8(env, clazz,
				     get_constant(clazz, get_constant(clazz, entry->fieldref_info.name_and_type_index)->nameandtype_info.name_index));
      char *field_sig = ResolveUtf8(env, clazz,
				    get_constant(clazz, get_constant(clazz, entry->fieldref_info.name_and_type_index)->nameandtype_info.signature_index));

      for (i = 0; i < field_clazz->num_fields; i ++)
	{
	  if (!strcmp(field_clazz->fields[i]->name, field_name)
	      && !strcmp(field_clazz->fields[i]->sig_str, field_sig))
	    {
	      field = field_clazz->fields[i];
	      break;
	    }
	}

      assert(field != NULL);

      entry->generic.tag |= CONSTANT_RESOLVED;
      entry->res_fieldref_info.field = field;
    }

  return entry->res_fieldref_info.field;
}

void *
ResolveString(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *constant)
{
  assert(NULL != env);
  assert(NULL != clazz);
  assert(NULL != constant);
  assert(constant->generic.tag & CONSTANT_String);
  
  if (!(constant->generic.tag & CONSTANT_RESOLVED))
    {
      char *text;
	  
      text = ResolveUtf8(env, clazz, get_constant(clazz, constant->string_info.string_index));

      constant->generic.tag |= CONSTANT_RESOLVED;
      constant->res_string_info.string = (void*)(*env)->NewStringUTF(env, text);
      constant->res_string_info.string = (void*)(*env)->NewGlobalRef(env, constant->res_string_info.string);
    }

  return constant->res_string_info.string;
}

char *
ResolveStringAsCString(JNIEnv *env, ClazzFile *clazz, ConstantPoolEntry *constant)
{
  int tag = constant->generic.tag;

  if (tag & CONSTANT_Unicode)
    {
      fprintf (stderr, "getting unicode string as C string... do you *really* want to do this?\n");
      abort();
    }
  else if (tag & CONSTANT_Utf8)
    {
      return ResolveUtf8(env, clazz, constant);
    }
  else
    assert(0);

  return 0;
}

char *
getSuperClassName(JNIEnv *env, ClazzFile *clazz)
{
  assert(NULL != clazz);
  if (clazz->superclass_index == 0)
    return NULL;
  else
    return ResolveClassName(env, clazz, get_constant(clazz, clazz->superclass_index));
}

ClazzFile *
getSuperClass(JNIEnv *env, ClazzFile *clazz)
{
  if (clazz->superclass_index == 0)
    return NULL;
  else
    return ResolveClass(env, clazz, get_constant(clazz, clazz->superclass_index));
}

ClazzFile *
getInnerclass(JNIEnv *env, ClazzFile *clazz, int innerclass_index)
{
  if (clazz->innerclasses[innerclass_index] == NULL)
    {
      clazz->innerclasses[innerclass_index] =
	ResolveClass(env, clazz, get_constant(clazz, clazz->innerclass_indices[innerclass_index]));
    }

  return clazz->innerclasses[innerclass_index];
}

ClazzFile *
getThrowableException(JNIEnv *env, MethodStruct *method,
		      int index)
{
  if (method->throwable_exceptions[index] == NULL)
    {
      method->throwable_exceptions[index] =
	ResolveClass(env, method->clazz,
		     get_constant(method->clazz,
				  method->throwable_exception_indices[index]));
    }
  return method->throwable_exceptions[index];
}

