/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode:nil -*-
   sys_clsloader.c -- the system (primordial) classloader.
   Created: Chris Toshok <toshok@hungry.com>, 12-Oct-1999
 */
/*
  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"

void
initialize_system_classloader(HungryVM *hvm)
{
  if (hvm && NULL == hvm->_loaded_classes)
    hvm->_loaded_classes = PL_NewHashTable(213,
                                           PL_HashString, PL_CompareStrings,
                                           PL_CompareValues,
                                           NULL, NULL);

  PR_ASSERT(NULL != hvm->_loaded_classes);
}

void
destroy_system_classloader(HungryVM *hvm)
{
  if (hvm && hvm->_loaded_classes)
    PL_HashTableDestroy(hvm->_loaded_classes);
}

void
add_loaded_class (HungryEnv *henv, ClazzFile *classfile)
{
  PL_HashTableAdd(henv->vm->_loaded_classes,
                  getClassName(henv, classfile), classfile);
}

void
delete_loaded_class (HungryEnv *henv, char *name)
{
  PL_HashTableRemove(henv->vm->_loaded_classes, name);
}

PR_IMPLEMENT(ClazzFile*)
HVM_ClassFindLoaded (HungryEnv *henv, const char *name)
{
  return (ClazzFile*)PL_HashTableLookup(henv->vm->_loaded_classes, name);
}

static PRIntn PR_CALLBACK
num_enumerator(PLHashEntry *he, PRIntn i, void *arg)
{
  int *num = (int*)arg;
  (*num)++;
  return HT_ENUMERATE_NEXT;
}

PR_IMPLEMENT(PRUint32)
HVM_ClassGetNumLoaded(HungryEnv *henv)
{
  PRUint32 num = 0;
  PL_HashTableEnumerateEntries(henv->vm->_loaded_classes,
                               num_enumerator, &num);

  return num;
}

struct loaded {
  int num_classes;
  japhar_object** classes;
};

static PRIntn PR_CALLBACK
loaded_enumerator(PLHashEntry *he, PRIntn i, void *arg)
{
  struct loaded *loaded = (struct loaded*)arg;
  loaded->classes[ loaded->num_classes++ ] = he->value;
  return HT_ENUMERATE_NEXT;
}

PR_IMPLEMENT(void)
HVM_ClassGetLoaded(HungryEnv *henv, japhar_object** classes)
{
  struct loaded loaded;

  loaded.num_classes = 0;
  loaded.classes = classes;
  PL_HashTableEnumerateEntries(henv->vm->_loaded_classes,
                               loaded_enumerator, &loaded);
}

struct map_data {
  ClazzFileFunc func;
  void *arg;
};

static PRIntn PR_CALLBACK
map_enumerator(PLHashEntry *he, PRIntn i, void *arg)
{
  struct map_data *map_data = (struct map_data*) arg;

  (*map_data->func)(he->value, map_data->arg);
  return HT_ENUMERATE_NEXT;
}

PR_IMPLEMENT(void)
HVM_ClassForEachLoaded(HungryVM *vm,
                       ClazzFileFunc func,
                       void *arg)
{
  struct map_data map_data;

  map_data.func = func;
  map_data.arg = arg;

  PL_HashTableEnumerateEntries(vm->_loaded_classes,
                               map_enumerator, &map_data);
}

static PRIntn PR_CALLBACK
dump_enumerator(PLHashEntry *he, PRIntn i, void *arg)
{
  printf ("%d : ", i);
  printf ("%s\n", ((ClazzFile*)he->value)->class_name);
  return HT_ENUMERATE_NEXT;
}

PR_IMPLEMENT(void)
HVM_ClassDumpLoaded(HungryEnv *env)
{
  PL_HashTableEnumerateEntries(env->vm->_loaded_classes,
                               dump_enumerator, env);
}

ClazzFile*
find_class_on_classpath(HungryEnv *henv,
                        const char *dotted_class_name,
                        const char *class_name,
                        HVMClasspath *cp)
{
  char *full_class_name = NULL;
  int class_name_length = 0;
  ClazzFile *cf = NULL;
  int i;
  PRBool verbose_loading = PR_FALSE;

  if (henv && henv->vm->_verbose_flags & VERBOSE_CLASS)
    verbose_loading = PR_TRUE;

  for (i = 0; i < cp->num_entries; i ++)
    {
      int new_class_name_length = (PL_strlen(class_name)
                                   + PL_strlen(cp->entries[i].path)
                                   + PL_strlen(".class") + 2);
      if (!full_class_name)
        {
          full_class_name = (char*)PR_MALLOC(new_class_name_length);
          class_name_length = new_class_name_length;
        }
      else if (new_class_name_length > class_name_length)
        {
          PR_DELETE(full_class_name);
          full_class_name = (char*)PR_MALLOC(new_class_name_length);
          class_name_length = new_class_name_length;
        }

      if (cp->entries[i].type == eClasspathEntryZIP ||
          cp->entries[i].type == eClasspathEntryJAR)
        {
          ZipDirectory *dir;

          /* if there was an error reading the archive, pass over it. */
          if (NULL == cp->entries[i].zip.fd)
            continue;

          PL_strcpy(full_class_name, class_name);
          PL_strcat(full_class_name, ".class");

          {
            int first, last, middle;

            first = 0;
            last = cp->entries[i].zip.count - 1;

            do {
              int r;
              middle = (first + last) / 2;

              dir = cp->entries[i].zip.sorted_entries[middle];
              r = PL_strcmp(full_class_name, ZIPDIR_FILENAME(dir));
              if (r < 0)
                {
                  last = middle - 1;
                  continue;
                }
              else if (r > 0)
                {
                  first = middle + 1;
                  continue;
                }
              else
                {
                  PRUint8 *buf;
                  PRUint32 length;
                  PRBool malloced;

                  if (get_zipfile_entry(&cp->entries[i].zip,
                                        dir,
                                        &buf, &length,
                                        &malloced) != -1)
                    {
                      cf = HVM_ClassDefine(henv, buf, length);
                      if (verbose_loading)
                        printf("[Loaded %s from %s]\n", dotted_class_name, cp->entries[i].path);
                    }

                  free_zipfile_entry(dir, buf, length, malloced);

                  break;
                }
            } while (first <= last);
          }
        }
      else if (cp->entries[i].type == eClasspathEntryDirectory)
        {
          PL_strcpy(full_class_name, cp->entries[i].path);
          PL_strcat(full_class_name, "/");
          PL_strcat(full_class_name, class_name);
          PL_strcat(full_class_name, ".class");

          cf = parse_class (henv, full_class_name);

          if (cf && verbose_loading)
            printf("[Loaded %s from %s]\n", dotted_class_name, full_class_name);
        }

      if (cf != NULL)
        {
          break;
        }
    }

  if (cf != NULL)
    {
      add_loaded_class(henv, cf);
    }

  if (full_class_name)
    PR_DELETE(full_class_name);

  return cf;
}

PR_IMPLEMENT(ClazzFile*)
HVM_ClassFind(HungryEnv *henv, const char *class_name)
{
  char *real_class_name = NULL;
  int class_name_len, real_class_name_len;
  ClazzFile *result = NULL;

  PR_ASSERT(NULL != class_name);

  if (class_name[0] == '[')
    {
      /* for array classes, we just create a fake entry for them. */
      return createFakeArrayClass(henv, (const char*)class_name);
    }

  class_name_len = PL_strlen(class_name);
  if (class_name[0] == 'L' && class_name[class_name_len - 1] == ';')
    {
      real_class_name = PL_strdup(class_name + 1);

      real_class_name[class_name_len - 2] = 0;

      real_class_name_len = class_name_len - 2;
    }
  else /* normal case */
    {
      real_class_name = PL_strdup(class_name);
      real_class_name_len = class_name_len;
    }

  dots_to_slashes(real_class_name);

  if (!result)
    result = HVM_ClassFindLoaded(henv, (char*)real_class_name);

  if (!result)
    {
      result = find_class_on_classpath(henv, class_name,
                                       real_class_name,
                                                                           henv->vm->_cp);
    }

  PR_DELETE (real_class_name);

  return result;
}

