/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode:nil -*-
   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 "runtimeint.h"
#include "arch.h"
#include "compat.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

extern PRLogModuleInfo* parseClassLm;

static inline PRUint8
extract_u1(const PRUint8 *buf, int *pos)
{
  return buf[(*pos)++];
}
static inline PRUint16
extract_u2(const PRUint8 *buf, int *pos)
{
  return (PRUint16)(extract_u1(buf, pos) << 8) | extract_u1(buf, pos);
}
static inline PRUint32
extract_u4(const PRUint8* buf, int *pos)
{
  return ((PRUint32)(extract_u1(buf, pos) << 24)
          | extract_u1(buf, pos) << 16
          | extract_u1(buf, pos) << 8
          | extract_u1(buf, pos));
}

static PRUint8 *
load_class_file(char *file_name, PRUint32 *length_return, PRBool *malloced)
{
  PRUint8 *buf;
  PRFileInfo finfo;
  FILE *fp;
  int file_size;

  if (PR_GetFileInfo(file_name, &finfo) == PR_FAILURE)
    {
      PR_LOG(parseClassLm, PR_LOG_DEBUG, 
             ("load_file_to_memory failed for file '%s'\n", file_name));
      return NULL;
    }

  file_size = finfo.size;

  fp = fopen(file_name, "rb");

  if (fp == NULL)
    {
      PR_LOG(parseClassLm, PR_LOG_DEBUG,
             ("couldn't open '%s'\n", file_name));
      return NULL;
    }

#ifdef HAVE_MMAN_H
  {
    PR_LOG(parseClassLm, PR_LOG_DEBUG,
           ("load_class_file reading from '%s'\n", file_name));

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

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

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

    *malloced = PR_TRUE;

    buf = (PRUint8 *) HVM_Malloc ( file_size );
    if (NULL == buf)
      return NULL; /* XXX Should also throw ? */

    PR_LOG(parseClassLm, PR_LOG_DEBUG,
           ("load_class_file reading from '%s'\n", file_name));

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

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

    if (retval < 0)
      {
        PR_LOG(parseClassLm, PR_LOG_DEBUG, ("fread failed.\n"));
        return NULL;
      }
  }
#endif

  fclose(fp);

  *length_return = file_size;
  return buf;
}

/**
 * Make sure the supplied constant pool entry is resolved.
 */
static void
ensure_resolved(ClazzFile *clazz, ConstantPoolEntry *entry)
{
  HungryEnv *henv;
  int resolved = entry->generic.tag & CONSTANT_RESOLVED;
  if (resolved) {
    return;
  }
  henv = HVM_ThreadGetEnv();
  switch(entry->generic.tag)
    {
    case CONSTANT_Integer:
      /* nothing to do to resolve an Integer */
      break;
    case CONSTANT_Float:
      entry->res_float_info.value = *(float*)&entry->float_info.bytes;
      break;
    case CONSTANT_Long:
      LL_ISHL(entry->res_long_info.value, entry->long_info.high_bytes, 32);
      LL_OR2(entry->res_long_info.value, entry->long_info.low_bytes);
      break;
    case CONSTANT_String:
      ResolveString(henv, clazz, entry);
      break;
    case CONSTANT_Double:
      {
        /* XXX only works where PRInt64 == long long */
        PRInt64 j;
        LL_ISHL(j, entry->double_info.high_bytes, 32);
        LL_OR2(j, entry->double_info.low_bytes);
        entry->res_double_info.value = *(double*)&j;
        break;
      }
    default:
      /* sorry, not yet implemented */
      PR_LOG(parseClassLm, PR_LOG_DEBUG, ("constant tag '%d' is not yet supported\n",
                                          entry->generic.tag));
      PR_ASSERT(0);
      break;
    }
  entry->generic.tag |= CONSTANT_RESOLVED;
}

static void
get_constant_field(FieldStruct *field)
{
  ConstantPoolEntry *entry;

  entry = &field->clazz->constants[ field->constant_value_index ];
  ensure_resolved(field->clazz, entry);
  switch(entry->generic.tag & ~CONSTANT_RESOLVED)
    {
    case CONSTANT_Integer:
      field->constant_value.i = entry->integer_info.bytes;
      break;
    case CONSTANT_Double:
      field->constant_value.d = entry->res_double_info.value;
      break;
    case CONSTANT_Float:
      field->constant_value.f = entry->res_float_info.value;
      break;
    case CONSTANT_Long:
      field->constant_value.j = entry->res_long_info.value;
      break;
    case CONSTANT_String:
      field->constant_value.l = entry->res_string_info.string;
      break;
    default:
      /* sorry, not yet implemented */
      PR_LOG(parseClassLm, PR_LOG_DEBUG, ("constant tag %d is not yet supported\n",
                                          entry->generic.tag & ~CONSTANT_RESOLVED));
      PR_ASSERT(0);
      break;
    }
}

static void
parse_field_attribute(HungryEnv *henv, const PRUint8 *buf, PRUint32 *buf_pos, FieldStruct *field)
{
  PRUint16 attribute_name;
  PRUint32 attribute_length;
  char *att_name;

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

  att_name = ResolveUtf8(henv, field->clazz,
                         &field->clazz->constants[attribute_name]);

  if (!PL_strcmp("ConstantValue", att_name))
    {
      PRUint16 index;

      index = extract_u2(buf, buf_pos);

      field->has_constant_value = 1;
      field->constant_value_index = index;
      get_constant_field(field);
    }
  else if (!PL_strcmp("Deprecated", att_name))
    {
      field->flags |= FLAG_DEPRECATED;
    }
  else if (!PL_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(HungryEnv *henv, const PRUint8 *buf, PRUint32 *buf_pos, ClazzFile *clazz,
            FieldStruct *new_field)
{
  PRUint16 tmp_u2;

  new_field->clazz = clazz;
  new_field->field_offset = 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(henv, clazz, &clazz->constants[tmp_u2]);

  /* get the signature of the field */
  tmp_u2 = extract_u2(buf, buf_pos);
  new_field->sig_str = ResolveUtf8(henv, clazz, &clazz->constants[tmp_u2]);
  new_field->sig_size_in_bytes = HVM_SigSizeofStrInWords(henv, new_field->sig_str);

  /* 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(henv, buf, buf_pos, new_field);
    }
}

static void
parse_method_attribute(HungryEnv *henv, const PRUint8 *buf, PRUint32 *buf_pos, MethodStruct *method)
{
  PRUint16 attribute_name;
  PRUint32 attribute_length;
  char *att_name;

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

  att_name = ResolveUtf8(henv,method->clazz,
                         &method->clazz->constants[attribute_name]);

  if (!PL_strcmp("Code", att_name))
    {
      PRUint16 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 = (PRUint8*)HVM_Malloc(method->code_length);

          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*)HVM_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(henv, buf, buf_pos, method);
        }
    }
  else if (!PL_strcmp("Exceptions", att_name))
    {
      int i;

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

      for (i = 0; i < method->num_throwable_exceptions; i ++) {
        method->throwable_exception_indices[i] = extract_u2(buf, buf_pos);
      }
    }
  else if (!PL_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*)HVM_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 (!PL_strcmp("LocalVariableTable", att_name))
    {
      method->num_local_variables = extract_u2(buf, buf_pos);

      if (method->num_local_variables)
        {
          int i;
          PRUint16 tmp_u2;
          method->local_variables = (LocalVariableEntry*)HVM_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(henv, method->clazz,
                            &method->clazz->constants[tmp_u2]);

              tmp_u2 = extract_u2(buf, buf_pos);
              method->local_variables[i].sig_str = 
                                    ResolveUtf8(henv, method->clazz, 
                                                &method->clazz->constants[tmp_u2]);

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

    }
  else if (!PL_strcmp("Deprecated", att_name))
    {
      method->flags |= FLAG_DEPRECATED;
    }
  else if (!PL_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(HungryEnv *henv, const PRUint8 *buf, PRUint32 *buf_pos, ClazzFile *clazz,
             MethodStruct *new_method)
{
  PRUint16 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(henv, clazz, &clazz->constants[tmp_u2]);

  /* get the signature of the method */
  tmp_u2 = extract_u2(buf, buf_pos);
  new_method->sig_str = ResolveUtf8(henv, clazz, &clazz->constants[tmp_u2]);

  if (!PL_strcmp(new_method->name, "<clinit>")
      && !PL_strcmp(ResolveUtf8(henv, clazz, &clazz->constants[tmp_u2]), "()V")
      && new_method->access_flags & ACC_STATIC)
    {
      clazz->access_flags |= HAS_CLINIT;
    }

  PR_LOG(parseClassLm, PR_LOG_DEBUG,
         ("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.... */
    PRUint32 i;

    HVMSignature *sig = HVM_SigParseFromJavaSig(henv, new_method->sig_str);

    new_method->num_params = HVM_SigNumParams(henv, sig);
    new_method->num_param_words = 0;
    for (i = 0; i < new_method->num_params; i ++)
      new_method->num_param_words += HVM_SigSizeofStrInWords(henv, sig->method.params[i]);

    HVM_SigFree(henv, 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(henv, buf, buf_pos, new_method);
    }
  new_method->native_func = NULL;

  PR_LOG(parseClassLm, PR_LOG_DEBUG,
         ("Done parsing method %s\n", new_method->name));
}

static void
parse_class_attribute(HungryEnv *henv, const PRUint8 *buf, PRUint32 *buf_pos, ClazzFile *clazz)
{
  PRUint16 attribute_name;
  PRUint32 attribute_length;
  char *att_name;

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

  att_name = ResolveUtf8(henv, clazz, &clazz->constants[attribute_name]);

  if (!PL_strcmp("SourceFile", att_name))
    {
      PRUint16 source_file_index;
      source_file_index = extract_u2(buf, buf_pos);

      clazz->source_filename = ResolveUtf8(henv,clazz, &clazz->constants[source_file_index]);
    }
  else if (!PL_strcmp("InnerClasses", att_name))
    {
      PRUint16 i;

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

      if (clazz->num_innerclasses)
        {
          clazz->innerclass_indices = (PRUint16*)HVM_Calloc(clazz->num_innerclasses, sizeof(PRUint16));
          clazz->innerclasses = (ClazzFile**)HVM_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 (!PL_strcmp("AbsoluteSourcePath", att_name))
    {
      PRUint16 tmp;
      /* don't do anything...  should this just override SourcePath if present? */
      tmp = extract_u2(buf, buf_pos);
    }
  else if (!PL_strcmp("Deprecated", att_name))
    {
      clazz->flags |= FLAG_DEPRECATED;
    }
  else if (!PL_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(const PRUint8 *buf, PRUint32 *buf_pos, ConstantPoolEntry *entry)
{
  PRUint8 tmp_u1;

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

  PR_LOG(parseClassLm, PR_LOG_DEBUG,
         (" 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 = (PRUint8*)HVM_Malloc(entry->utf8_info.length *
                                                        sizeof(PRUint8));

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

        } else {
          entry->utf8_info.bytes = NULL;
          PR_LOG(parseClassLm, PR_LOG_DEBUG,
                 (" 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 = (PRUint16*)HVM_Malloc(sizeof(PRUint16) *
                                                          entry->utf8_info.length);
        PR_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:
      PR_LOG(parseClassLm, PR_LOG_ERROR,
             ("invalid constant tag: %d, pos is %d\n",
              entry->generic.tag, *buf_pos));
      PR_ASSERT(0);
    }
}

ClazzFile *
HVM_ClassDefine(HungryEnv *henv, const PRUint8 *buf, PRUint32 buf_length)
{
  HungryVM *vm = NULL;
  int buf_pos = 0;
  ClazzFile *clazz;
  PRUint32 tmp_u4;
  PRUint16 tmp_u2;
  int constant_num;
  PRUint16 thisclass_index;
  ClazzFile *superclass;

  if (henv)
    vm = henv->vm;

  tmp_u4 = extract_u4(buf, &buf_pos);

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

  clazz = (ClazzFile*)HVM_Calloc(1, sizeof(ClazzFile));

  if (NULL == clazz)
    return NULL; /* XXX Should also throw ? */

  clazz->minor = extract_u2(buf, &buf_pos);
  PR_LOG(parseClassLm, PR_LOG_DEBUG, ("minor_version is %d\n", clazz->minor));

  clazz->major = extract_u2(buf, &buf_pos);
  PR_LOG(parseClassLm, PR_LOG_DEBUG, ("major_version is %d\n", clazz->major));

  clazz->constant_pool_count = extract_u2(buf, &buf_pos);
  PR_LOG(parseClassLm, PR_LOG_DEBUG, 
         ("%d constants in constant_pool_count\n",
          clazz->constant_pool_count));

  clazz->constants = (ConstantPoolEntry*) HVM_Calloc (clazz->constant_pool_count + 1, /* fuckers. */
                                                  sizeof(ConstantPoolEntry));
  
  for (constant_num = 1;
       constant_num < clazz->constant_pool_count;
       constant_num ++)
    {
      ConstantPoolEntry *constant = &clazz->constants[constant_num];
      PR_LOG(parseClassLm, PR_LOG_DEBUG,
             ("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(henv, clazz);

  /* fill in the name of this class. */
  clazz->class_name = ResolveClassName(henv, clazz, &clazz->constants[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;
        PR_LOG(parseClassLm, PR_LOG_DEBUG,
               ("%d interfaces to read\n", clazz->num_interfaces));
        
        clazz->interface_tuples = (InterfaceTuple*)HVM_Malloc(
                                                  (clazz->num_interfaces
                                                   + num_superclass_interfaces)
                                                  * sizeof(InterfaceTuple));
        
        for (interface_num = 0;
             interface_num < clazz->num_interfaces;
             interface_num ++)
          {
            PRUint16 interface_index;

            interface_index = extract_u2(buf, &buf_pos);
            clazz->interface_tuples[ interface_num ].interface =
              ResolveClass(henv, clazz,
                           &clazz->constants[interface_index]);
            clazz->interface_tuples[ interface_num ].methods =
              HVM_Malloc(clazz->interface_tuples[ interface_num ].interface->num_instance_methods
                     * sizeof(MethodStruct*));
          }

        /*
        ** 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->interface_tuples[ clazz->num_interfaces ++ ] =
                  superclass->interface_tuples[ 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;
        
        PR_LOG(parseClassLm, PR_LOG_DEBUG,
               ("%d (0x%x) fields to parse (pos=%d)\n",
                clazz->num_fields, clazz->num_fields, buf_pos));
        clazz->fields = (FieldStruct**)HVM_Malloc((clazz->num_fields
                                               + num_superclass_fields) 
                                              * sizeof(FieldStruct*));

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

        for (field_index = 0;
             field_index < clazz->num_fields;
             field_index ++)
          {
            PR_LOG(parseClassLm, PR_LOG_DEBUG,
                   ("parsing field #%d\n", field_index));
            parse_field(henv, 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_instance_methods = 0;
    int num_superclass_methods = 0;
    int method_num;
        
    if (superclass)
      {
        num_superclass_instance_methods = superclass->num_instance_methods;
        num_superclass_methods = superclass->num_methods;
      }
    
    clazz->num_methods = extract_u2 (buf, &buf_pos);
    if (clazz->num_methods > 0)
      {
        clazz->methods = (MethodStruct*)HVM_Calloc(clazz->num_methods + num_superclass_methods,
                                               sizeof(MethodStruct));

        for (method_num = 0;
             method_num < clazz->num_methods;
             method_num ++)
          {
            parse_method(henv, buf, &buf_pos,
                         clazz, &clazz->methods[ method_num ]);
            if ( clazz->methods[ method_num ].access_flags & ACC_STATIC )
              clazz->num_static_methods ++;
            else if (PL_strcmp(clazz->methods[ method_num ].name, "<init>"))
              clazz->num_instance_methods ++;
          }

        for (method_num = 0;
             method_num < num_superclass_methods;
             method_num ++)
          {
            clazz->methods[method_num + clazz->num_methods] = superclass->methods[method_num];
          }
      }

    if (clazz->access_flags & ACC_INTERFACE)
      num_superclass_instance_methods = 0;
    else
      clazz->num_instance_methods += num_superclass_instance_methods;

    if (0 < clazz->num_methods
        || 0 < num_superclass_instance_methods)
      {
        /* 
        ** we're guaranteed that we can't have more instance methods
        ** than current_class->num_instance_methods +
        ** superclass->num_instance_methods, and many times we'll have
        ** less.
        */
        if (0 < clazz->num_instance_methods)
          clazz->vmethods = (MethodStruct**)HVM_Malloc(clazz->num_instance_methods
                                                   * sizeof(MethodStruct*));

        if (0 < clazz->num_static_methods)
          clazz->smethods = (MethodStruct**)HVM_Malloc(clazz->num_static_methods
                                                   * sizeof(MethodStruct*));


        /*
        ** populate the virtual method table with the parent class's
        ** virtual methods.
        */
        for (method_num = 0;
             method_num < num_superclass_instance_methods;
             method_num++)
          {
            clazz->vmethods[ method_num ] = superclass->vmethods[ method_num ];
          }

        /*
        ** loop through our methods, either adding them to the virtual
        ** method table or the static method table
        */
        {
          int last_static = 0;
          int last_virtual = num_superclass_instance_methods;

          for (method_num = 0;
               method_num < clazz->num_methods;
               method_num ++)
            {
              MethodStruct *our_m = &clazz->methods[ method_num ];
              if ( our_m->access_flags & ACC_STATIC )
                {
                  clazz->smethods[ last_static ++ ] = our_m;
                }
              else if (PL_strcmp(our_m->name, "<init>"))
                {
                  int sm;
                  PRBool found = PR_FALSE;

                  /*
                  ** since the method is virtual, we loop through the
                  ** virtual methods supplied by the superclass,
                  ** replacing the virtual method in our table with
                  ** the new one if they match.  we always also not
                  ** the index where we put the method.
                  */

                  for (sm = 0; sm < num_superclass_instance_methods; sm ++)
                    {
                      MethodStruct *sup_m = superclass->vmethods[ sm ];
                      if (!PL_strcmp(our_m->name, sup_m->name)
                          && !PL_strcmp(our_m->sig_str, sup_m->sig_str))
                        {
                          /* XXX what if superclass method is final? */
                          clazz->vmethods[ sm ] = our_m;
                          our_m->index = sm;
                          found = PR_TRUE;
                          clazz->num_instance_methods --;
                        }
                    }
                  if (!found)
                    {
                      clazz->vmethods[ last_virtual ] = our_m;
                      our_m->index = last_virtual ++;
                    }
                }
            }
        }
        clazz->num_methods += num_superclass_methods;
      }
  }

  /*
  ** now that we've parsed the methods of the class, we need to hook
  ** up the interface method tables.
  */
  {
    int i;

    for (i = 0; i < clazz->num_interfaces; i ++)
      {
        int j;
        ClazzFile *ii = clazz->interface_tuples[i].interface;

        for (j = 0; j < ii->num_instance_methods; j ++)
          {
            int m;
            for (m = 0; m < clazz->num_instance_methods; m ++)
              if (!PL_strcmp(clazz->vmethods[m]->name, ii->vmethods[j]->name) &&
                  !PL_strcmp(clazz->vmethods[m]->sig_str, ii->vmethods[j]->sig_str))
                {
                  clazz->interface_tuples[i].methods[j] = clazz->vmethods[m];
                  break;
                }
          }
      }
  }

  /* 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(henv, buf, &buf_pos, clazz);
        }
    }

  return clazz;
}

ClazzFile *
parse_class(HungryEnv *henv, char *full_class_name)
{
  PRUint8 *buf = NULL;
  PRUint32 buf_length = 0;
  PRBool malloced;
  ClazzFile *result;

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

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

  result = HVM_ClassDefine(henv, buf, buf_length);

  if (malloced) HVM_Free(buf);

  return result;
}

void
class_finalize(ClazzFile *clazz)
{
  int i;
  HungryEnv *henv = HVM_ThreadGetEnv();
  printf ("Finalizing class (%s)\n", getClassName(henv, clazz));

  delete_loaded_class(henv, getClassName(henv, clazz));

  if (clazz->vmethods)
    HVM_Free(clazz->vmethods);
  if (clazz->smethods)
    HVM_Free(clazz->smethods);
  if (clazz->methods)
    HVM_Free(clazz->methods);

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

  if (clazz->innerclasses)
    HVM_Free(clazz->innerclasses);
  if (clazz->interface_tuples)
    HVM_Free(clazz->interface_tuples);

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

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

  HVM_Free(clazz);
}

ClazzFile *
ExceptionBlock_getHandlerClazz(HungryEnv *henv, 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 = HVM_ClassFind(henv, java_lang_Object);
        }
      else
        {
          block->catch_clazz = ResolveClass(henv, clazz, &clazz->constants[block->catch_clazz_index]);
        }
    }
  return block->catch_clazz;
}

char *
ResolveClassName(HungryEnv *henv, ClazzFile *clazz, ConstantPoolEntry *constant)
{
  PR_ASSERT(constant->generic.tag & CONSTANT_Class);

  if (!(constant->generic.tag & CONSTANT_RESOLVED))
    {
      PRUint16 name_index = constant->clazz_info.name_index;
      
      constant->generic.tag |= CONSTANT_RESOLVED;
      constant->res_clazz_info.name = ResolveUtf8(henv, clazz, &clazz->constants[name_index]);
      constant->res_clazz_info.clazz = NULL;
    }
  
  return constant->res_clazz_info.name;
}

ClazzFile *
ResolveClass(HungryEnv *henv, ClazzFile *clazz, ConstantPoolEntry *constant)
{
  PR_ASSERT(constant->generic.tag & CONSTANT_Class);

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

      constant->generic.tag |= CONSTANT_RESOLVED;
      constant->res_clazz_info.name = ResolveUtf8(henv, clazz, &clazz->constants[name_index]);
    }

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

  return constant->res_clazz_info.clazz;
}

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

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

#if 0
      if (NULL != constant->utf8_info.bytes)
        HVM_Free(constant->utf8_info.bytes);
#endif

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

      return value;
    }
#else
  char *value;

  value = (char*)HVM_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(HungryEnv *henv, ClazzFile *clazz, ConstantPoolEntry *entry)
{
  PR_ASSERT(entry->generic.tag & CONSTANT_Methodref);

  if (entry->res_method_info.nonvirtual_method == NULL)
    {
      int i;
      MethodStruct *method = NULL;
      ClazzFile *method_clazz;
      char *method_name;
      char *sig_str;
      
      if (entry->generic.tag & CONSTANT_RESOLVED)
        {
          method_clazz = entry->res_method_info.clazz;
          method_name = entry->res_method_info.name;
          sig_str = entry->res_method_info.sig_str;
        }
      else
        {
          method_clazz = ResolveClass(henv, clazz, &clazz->constants[entry->methodref_info.class_index]);
          method_name = ResolveUtf8(henv, clazz, 
                                    &clazz->constants[clazz->constants[entry->methodref_info.name_and_type_index].nameandtype_info.name_index]);
          
          sig_str = ResolveUtf8(henv, clazz,
                                &clazz->constants[
                       clazz->constants[
                        entry->methodref_info.name_and_type_index].
                       nameandtype_info.signature_index]);

          entry->generic.tag |= CONSTANT_RESOLVED;
          entry->res_method_info.clazz = method_clazz;
          entry->res_method_info.name = method_name;
          entry->res_method_info.sig_str = sig_str;
        }
      
      while (method_clazz != NULL)
        {
          for (i = 0; i < method_clazz->num_methods; i ++)
            {
              if (!PL_strcmp(method_clazz->methods[i].name, method_name)
                  && !PL_strcmp(method_clazz->methods[i].sig_str, sig_str))
                {
                  method = &method_clazz->methods[i];
                  goto found;
                }
            }
          method_clazz = method_clazz->superclass;
        }
      
    found:
       entry->res_method_info.nonvirtual_method = method;
    }
  
  return entry->res_method_info.nonvirtual_method;
}

MethodStruct *
ResolveStaticMethodRef(HungryEnv *henv, ClazzFile *clazz, ConstantPoolEntry *entry)
{
  PR_ASSERT(entry->generic.tag & CONSTANT_Methodref);
  
  if (entry->res_method_info.static_method == NULL)
    {
      int i;
      MethodStruct *method = NULL;
      char *method_name;
      char *sig_str;
      ClazzFile *method_clazz;

      if (entry->generic.tag & CONSTANT_RESOLVED)
        {
          method_clazz = entry->res_method_info.clazz;
          method_name = entry->res_method_info.name;
          sig_str = entry->res_method_info.sig_str;
        }
      else
        {
          method_clazz = ResolveClass(henv, clazz,
                              &clazz->constants[entry->methodref_info.class_index]);

          method_name = ResolveUtf8(henv, clazz, 
                            &clazz->constants[
                              clazz->constants[
                               entry->methodref_info.name_and_type_index].
                              nameandtype_info.name_index]);

          sig_str = ResolveUtf8(henv, clazz,
                        &clazz->constants[
                          clazz->constants[
                           entry->methodref_info.name_and_type_index].
                          nameandtype_info.signature_index]);

          entry->res_method_info.clazz = method_clazz;
          entry->res_method_info.name = method_name;
          entry->res_method_info.sig_str = sig_str;
        }


      for (i = 0; i < method_clazz->num_static_methods; i ++)
        {
          if (!PL_strcmp(method_clazz->smethods[i]->name, method_name)
              && !PL_strcmp(method_clazz->smethods[i]->sig_str, sig_str))
            {
              method = method_clazz->smethods[i];
              break;
            }
        }

      PR_ASSERT(method != NULL);

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

  return entry->res_method_info.static_method;
}

MethodStruct *
GetMethodByNameAndSig(HungryEnv *henv, ClazzFile *clazz, char *method_name, char *sig_str)
{
  int i;

  for (i = 0; i < clazz->num_instance_methods; i ++)
    if (!PL_strcmp(clazz->vmethods[i]->name, method_name)
        && !PL_strcmp(clazz->vmethods[i]->sig_str, sig_str))
      {
        return clazz->vmethods[i];
      }

  return NULL;
}

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

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

        num_found ++;
      }

  return num_found;
}

MethodStruct *
ResolveVirtualMethod(HungryEnv *henv, ClazzFile *clazz,
                     ConstantPoolEntry *constant,
                     char **name)
{
  PR_ASSERT(constant->generic.tag & CONSTANT_Methodref);

  if (constant->res_method_info.virtual_method == NULL)
    {
      int i;
      MethodStruct *method = NULL;
      char *method_name;
      char *sig_str;
      ClazzFile *method_clazz;

      if (constant->generic.tag & CONSTANT_RESOLVED)
        {
          method_clazz = constant->res_method_info.clazz;
          method_name = constant->res_method_info.name;
          sig_str = constant->res_method_info.sig_str;
        }
      else
        {
          method_clazz = ResolveClass(henv, clazz,
                              &clazz->constants[constant->methodref_info.class_index]);

          method_name = ResolveUtf8(henv, clazz, 
                            &clazz->constants[
                              clazz->constants[
                               constant->methodref_info.name_and_type_index].
                              nameandtype_info.name_index]);

          sig_str = ResolveUtf8(henv, clazz,
                        &clazz->constants[
                          clazz->constants[
                           constant->methodref_info.name_and_type_index].
                          nameandtype_info.signature_index]);

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

      for (i = 0; i < method_clazz->num_instance_methods; i ++)
        {
          if (!PL_strcmp(method_clazz->vmethods[i]->name, method_name)
              && !PL_strcmp(method_clazz->vmethods[i]->sig_str, sig_str))
            {
              method = method_clazz->vmethods[i];
              break;
            }
        }

      constant->res_method_info.virtual_method = method;
    }

  *name = constant->res_method_info.name;
  return constant->res_method_info.virtual_method;
}

MethodStruct *
GetMethodForVirtualMethod(HungryEnv *henv, ClazzFile *clazz,
                          MethodStruct *virtual_method)
{
  return clazz->vmethods[virtual_method->index];
}

static MethodStruct *
internalResolveInterfaceMethod(ClazzFile * method_clazz,
                         const char* name_str, const char* sig_str) {
  int i;
  for (i = 0; i < method_clazz->num_methods; i ++)
    {
      if (!PL_strcmp(method_clazz->methods[i].name, name_str)
          && !PL_strcmp(method_clazz->methods[i].sig_str, sig_str))
        {
          return &method_clazz->methods[i];
        }
    }
      
  for (i = 0; i < method_clazz->num_interfaces; i++)
    {
      MethodStruct *method = internalResolveInterfaceMethod
        (method_clazz->interface_tuples[i].interface, name_str, sig_str);
      if (method != NULL)
        return method;
    }
  return NULL;
}

MethodStruct *
ResolveInterfaceMethod(HungryEnv *henv, ClazzFile *clazz,
                       ConstantPoolEntry *constant,
                       char **name)
{
  PR_ASSERT(constant->generic.tag & CONSTANT_Methodref);
  if (!(constant->generic.tag & CONSTANT_RESOLVED))
    {
      int i;
      MethodStruct *method = NULL;
      
      ClazzFile *method_clazz =
        ResolveClass(henv, clazz,
                     &clazz->constants[constant->interfacemethodref_info.class_index]);
      char *sig_str =
        ResolveUtf8(henv, clazz,
          &clazz->constants[
            clazz->constants[
             constant->interfacemethodref_info.name_and_type_index].nameandtype_info.signature_index]);
      char *name_str =
        ResolveUtf8(henv, clazz,
           &clazz->constants[
             clazz->constants[
              constant->interfacemethodref_info.name_and_type_index].nameandtype_info.name_index]);
      
      method = internalResolveInterfaceMethod(method_clazz, name_str, sig_str);
      PR_ASSERT(method != NULL);

      constant->res_interfacemethodref_info.tag |= CONSTANT_RESOLVED;
      constant->res_interfacemethodref_info.method = method;
      constant->res_interfacemethodref_info.name = name_str;
      constant->res_interfacemethodref_info.sig_str = sig_str;
    }
  
  *name = constant->res_interfacemethodref_info.name;
  return constant->res_interfacemethodref_info.method;
}

MethodStruct *
GetMethodForInterfaceMethod(HungryEnv *henv, ClazzFile *clazz,
                            MethodStruct *interface_method)
{
  int i;
#if 0
  ClazzFile *interface = interface_method->clazz;
  for (i = 0; i < clazz->num_interfaces; i ++)
    {
      if (interface == clazz->interface_tuples[i].interface)
        return clazz->interface_tuples[i].methods[interface_method->index];
    }
#else
  for (i = 0; i < clazz->num_instance_methods; i ++)
    if (!PL_strcmp(interface_method->name, clazz->vmethods[i]->name)
        && !PL_strcmp(interface_method->sig_str, clazz->vmethods[i]->sig_str))
      return clazz->vmethods[i];
#endif

  return NULL;
}

FieldStruct *
ResolveFieldRef(HungryEnv *henv, ClazzFile *clazz, ConstantPoolEntry *entry)
{
  PR_ASSERT(entry->generic.tag & CONSTANT_Fieldref);

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

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

      PR_ASSERT(field != NULL);

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

  return entry->res_fieldref_info.field;
}

void *
ResolveString(HungryEnv *henv, ClazzFile *clazz, ConstantPoolEntry *constant)
{
  PR_ASSERT(NULL != henv);
  PR_ASSERT(NULL != clazz);
  PR_ASSERT(NULL != constant);
  PR_ASSERT(constant->generic.tag & CONSTANT_String);
  
  if (!(constant->generic.tag & CONSTANT_RESOLVED))
    {
      char *text;
          
      text = ResolveUtf8(henv, clazz, &clazz->constants[ constant->string_info.string_index ]);

      constant->generic.tag |= CONSTANT_RESOLVED;
      constant->res_string_info.string = HVM_StringFromCString(henv, text);
    }

  return constant->res_string_info.string;
}

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

  if (tag & CONSTANT_Unicode)
    {
      PR_LOG (parseClassLm, PR_LOG_ALWAYS, 
              ("getting unicode string as C string... do you *really* want to do this?\n"));
      abort();
    }
  else if (tag & CONSTANT_Utf8)
    {
      return ResolveUtf8(henv, clazz, constant);
    }
  else
    PR_ASSERT(0);

  return 0;
}

char *
getSuperClassName(HungryEnv *henv, ClazzFile *clazz)
{
  PR_ASSERT(NULL != clazz);
  if (clazz->superclass_index == 0)
    return NULL;
  else
    return ResolveClassName(henv, clazz, &clazz->constants[ clazz->superclass_index ]);
}

ClazzFile *
getSuperClass(HungryEnv *henv, ClazzFile *clazz)
{
  if (clazz->superclass_index == 0)
    return NULL;
  else
    return ResolveClass(henv, clazz, &clazz->constants[ clazz->superclass_index ]);
}

ClazzFile *
getInnerclass(HungryEnv *henv, ClazzFile *clazz, int innerclass_index)
{
  if (clazz->innerclasses[innerclass_index] == NULL)
    {
      clazz->innerclasses[innerclass_index] =
        ResolveClass(henv, clazz, &clazz->constants[ clazz->innerclass_indices[innerclass_index]]);
    }

  return clazz->innerclasses[innerclass_index];
}

ClazzFile *
getThrowableException(HungryEnv *henv, MethodStruct *method,
                      int index)
{
  if (method->throwable_exceptions[index] == NULL)
    {
      method->throwable_exceptions[index] =
        ResolveClass(henv, method->clazz,
                     &method->clazz->constants[method->throwable_exception_indices[index]]);
    }
  return method->throwable_exceptions[index];
}

