/* -*- Mode: C; c-file-style: "gnu"; indent-tabs-mode:nil -*-
   gc.c -- garbage collection functions.
   Created: Chris Toshok <toshok@hungry.com>, 1999-11-02
 */
/*
  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

#ifdef WITH_SPORTSMODEL
#include "sm.h"
#include "smheap.h"
#include "smtrav.h"
#define K               1024
#define M               (K*K)
#define PAGE            (4*K)
#define MIN_HEAP_SIZE   (K*PAGE)
#define MAX_HEAP_SIZE   (128*M)
#endif

#include "runtimeint.h"

PR_IMPLEMENT( void* )
HVM_Malloc (PRSize size)
{
  void *result;
#if WITH_SPORTSMODEL
  result = SM_Malloc (size);
#else
  result = PR_Malloc (size);
#endif
#ifdef DEBUG
  if (result == NULL) printf ("HVM_Malloc(%d) failed\n", size);
#endif
  return result;
}

PR_IMPLEMENT( void* )
HVM_Calloc (PRSize nmemb, PRSize size)
{
  void *result;
#if WITH_SPORTSMODEL
  result = SM_Calloc (size, nmemb);
#else
  result = PR_Calloc (size, nmemb);
#endif
#ifdef DEBUG
  if (result == NULL) printf ("HVM_Calloc(%d,%d) failed\n", nmemb, size);
#endif
  return result;
}

PR_IMPLEMENT( void* )
HVM_Realloc (void *ptr, PRSize size)
{
  void *result;
#if WITH_SPORTSMODEL
  result = SM_Realloc (ptr, size);
#else
  result = PR_Realloc (ptr, size);
#endif
#ifdef DEBUG
  if (result == NULL) printf ("HVM_Realloc(%p,%d) failed\n", ptr, size);
#endif
  return result;
}

PR_IMPLEMENT( void* )
HVM_Strdup (void *ptr)
{
  void *result;
#if WITH_SPORTSMODEL
  /* Can't use SM_Strdup here since that requires that the source
     string be under sportsmodel control. */
  result = SM_Malloc (strlen (ptr) + 1);
  if (result)
    PL_strcpy (result, ptr);
#else
  result = PL_strdup (ptr);
#endif
#ifdef DEBUG
  if (result == NULL) printf ("HVM_Strdup(%s) failed\n", ptr);
#endif
  return result;
}

PR_IMPLEMENT( void )
HVM_Free (void *ptr)
{
#if WITH_SPORTSMODEL
  SM_Free (ptr);
#else
  PR_Free (ptr);
#endif
}

japhar_object*
HVM_GCAllocObject(HungryEnv *henv, ClazzFile *cf)
{
#if WITH_SPORTSMODEL && USE_BROKEN_ALLOCATION
  japhar_object *o;

  printf ("HVM_GCAllocObject (%s, cf = %p)\n", cf->class_name, cf);

  if (cf->gc_data == NULL)
    {
      int num_fields = (cf->superclass ? cf->superclass->num_fields : 0) + cf->num_fields;
      int num_collectable_fields = 0;
      int i;
      SMFieldDesc *fds;

      printf (" + initializing gc data\n");

      for (i = 0; i < num_fields; i ++)
        {
          if ((cf->fields[i]->access_flags && ACC_STATIC) == 0
              && (cf->fields[i]->sig_str[0] == 'L'
                  || cf->fields[i]->sig_str[0] == '['))
            {
              num_collectable_fields++;
            }
        }

      printf (" + %d collectable fields\n", num_collectable_fields);

      cf->gc_data = (SMObjectClass*)SM_Malloc(sizeof(SMObjectClass)
                                              + (num_collectable_fields * sizeof(SMFieldDesc)));

      SM_InitObjectClass (cf->gc_data, NULL, sizeof(SMObjectClass),
                          (object_size_without_fields()
                           /* for all the instance fields */
                           + cf->size_of_instance_field_block),
                          num_collectable_fields, 0);

      fds = SM_CLASS_GET_INST_FIELD_DESCS((SMClass*)cf->gc_data);

      for (i = 0; i < num_fields; i ++)
        {
          if ((cf->fields[i]->access_flags && ACC_STATIC) == 0
              && (cf->fields[i]->sig_str[0] == 'L'
                  || cf->fields[i]->sig_str[0] == '['))
            {
              num_collectable_fields--;
              fds[num_collectable_fields].offset = (object_size_without_fields()
                                                    + cf->fields[i]->field_offset) / sizeof (void*);
              fds[num_collectable_fields].count = 1;
            }
        }
    }

  o = (japhar_object*)SM_AllocObject(cf->gc_data);
  SM_SET_FIELD (japhar_object, o, hash_code, (PRUint32)SM_DECRYPT_OBJECT((SMObject*)o));
  SM_SET_FIELD (japhar_object, o, native_state, NULL);

  memset ((char*)SM_DECRYPT_OBJECT((SMObject*)o) + object_size_without_fields(), 0, cf->size_of_instance_field_block);

  /*  SM_DumpPageMap(stderr, SMDumpFlag_Detailed);*/
  return o;

#elif WITH_BOEHM
#else
  PRSize size_of_object;

  size_of_object = (object_size_without_fields()
                    /* for all the instance fields */
                    + cf->size_of_instance_field_block);

  return (japhar_object*)HVM_Calloc(1, size_of_object);
#endif
}

japhar_object*
HVM_GCCloneObject(HungryEnv *henv, japhar_object *old_obj)
{
  japhar_object* new_obj;

#if WITH_SPORTSMODEL
  /* XXX change this soon, k? :) */
  new_obj = HVM_ObjectNew(henv, old_obj->clazz);

  if (new_obj == NULL)
    return NULL;

  /* Copy fields */
  memcpy(((char*)new_obj + object_size_without_fields()),
         ((char*)old_obj + object_size_without_fields()),
         old_obj->clazz->size_of_instance_field_block);
#elif WITH_BOEHM
#else
  new_obj = HVM_ObjectNew(henv, old_obj->clazz);

  if (new_obj == NULL)
    return NULL;

  /* Copy fields */
  memcpy(((char*)new_obj + object_size_without_fields()),
         ((char*)old_obj + object_size_without_fields()),
         old_obj->clazz->size_of_instance_field_block);
#endif

  return new_obj;
}

PR_IMPLEMENT( void )
HVM_GCCollect(HungryVM *hvm)
{
#if WITH_SPORTSMODEL
  SM_Collect();
#elif WITH_BOEHM
  GC_gcollect ();
#endif
}

PR_IMPLEMENT( void )
HVM_GCRunFinalizers(HungryVM *hvm)
{
#if WITH_SPORTSMODEL
  SM_RunFinalization();
#elif WITH_BOEHM
  GC_invoke_finalizers ();
#endif
}

PR_IMPLEMENT( void )
HVM_GCAddRoot(HungryVM *hvm, japhar_object *root)
{
#if WITH_SPORTSMODEL
  PL_HashTableAdd(hvm->_roots, root, root);
#elif WITH_BOEHM
  GC_add_roots (/* XXX */);
#endif
}

PR_IMPLEMENT( void )
HVM_GCRemoveRoot(HungryVM *hvm, japhar_object *root)
{
#if WITH_SPORTSMODEL
  PL_HashTableRemove(hvm->_roots, root);
#elif WITH_BOEHM
  GC_remove_roots (/* XXX */);
#endif
}

PR_IMPLEMENT( PRUint64 )
HVM_GCGetFreeMemory(HungryVM *hvm)
{
#if WITH_SPORTSMODEL
  SMStats stats;

  SM_Stats(&stats);

  return (PRUint64)stats.total.amountFree;
#elif WITH_BOEHM
  return 0; /* XXX */
#else
  return 0;
#endif
}

PR_IMPLEMENT( PRUint64 )
HVM_GCGetTotalMemory(HungryVM *hvm)
{
#if WITH_SPORTSMODEL
  SMStats stats;

  SM_Stats(&stats);

  return (PRUint64)(stats.total.amountFree +
		    stats.total.amountUsed +
		    stats.total.overhead);
#elif WITH_BOEHM
  return 0; /* XXX */
#else
  return 0;
#endif
}

#if WITH_SPORTSMODEL
static void
before_gc_hook(SMGenNum collectingGenNum, PRUword collectCount,
               PRBool copyCycle, void* closure)
{
  printf ("before_gc_book (%d, %d, %d, %p)\n",
          collectingGenNum, collectCount, copyCycle, closure);
}

static void
after_gc_hook(SMGenNum collectingGenNum, PRUword collectCount,
              PRBool copyCycle, void* closure)
{
  printf ("after_gc_book (%d, %d, %d, %p)\n",
          collectingGenNum, collectCount, copyCycle, closure);
}

static PRIntn
mark_root_enumerator (PLHashEntry *he, PRIntn i, void *arg)
{
  SM_MarkRoots ((SMObject**)&he->value, 1, PR_FALSE);

  return HT_ENUMERATE_NEXT;
}

static PR_CALLBACK void
mark_clazz (ClazzFile *cf, void *arg)
{
  int i;

  printf ("Marking class %s (%p)\n", cf->class_name, cf->jcls);

  if (cf->jcls)
    {
      printf (" + marking java class\n");
      SM_MarkRoots ((SMObject**)&cf->jcls, 1, PR_TRUE);
      printf (" + marked\n");
    }

  if (cf->num_static_fields == 0) return;

  printf ("+ marking static fields\n");
  for (i = 0; i < cf->num_fields; i ++) {
    if ((cf->fields[i]->access_flags & ACC_STATIC)
        && cf->fields[i]->clazz == cf
        && (cf->fields[i]->sig_str[0] == 'L'
            || cf->fields[i]->sig_str[0] == '['))
      {
        printf (" + marking field %d\n", i);
        SM_MarkRoots ((SMObject**)((char*)cf->static_fields + cf->fields[i]->field_offset),
                      1, PR_TRUE);
        printf (" + marked\n");
      }
  }
}

static void
mark_roots(SMGenNum collectingGenNum, PRBool copyCycle,
           void* closure)
{
  HungryVM *hvm = (HungryVM*)closure;
  HungryEnv *henv;

  printf ("+ marking thread stacks conservatively\n");

  /* this must be called first, as per the sportsmodel docs. */
  SM_MarkThreadsConservatively (collectingGenNum, copyCycle, NULL);

  /* mark our thread call and operand stacks */
  for (henv = hvm->_envs; henv; henv = henv->next)
    {
      StackFrame *f;
      printf ("+ marking frames\n");
      for (f = henv->top_frame; f < henv->stack_highwater; f = f->parent)
        {
          int i;

          SM_MarkRoots ((SMObject**)&f->this_pointer, 1, PR_TRUE);

          for (i = 0; i < f->method->max_locals; i ++)
            SM_MarkRoots ((SMObject**)&f->vars[i].value.l, 1, PR_TRUE);
        }

      printf ("+ marking operand stack\n");
      SM_MarkRoots ((SMObject**)henv->op_stack->stack_bottom,
                    henv->op_stack->stack_top - henv->op_stack->stack_bottom,
                    PR_TRUE);
    }

  /* now enumerate our loaded classes so we can mark their static object fields */
  printf ("+ marking loaded classes\n");
  HVM_ClassForEachLoaded (hvm, mark_clazz, NULL);

  /* enumerate our hashtable of registered roots */
  printf ("+ marking registered roots\n");
  PL_HashTableEnumerateEntries (hvm->_roots, mark_root_enumerator, NULL);
}
#endif

static PLHashNumber
hashValue(const void *key)
{
  return (PLHashNumber)key;
}

void
init_collector(HungryVM *hvm)
{
#if WITH_SPORTSMODEL
  hvm->_roots = PL_NewHashTable(213,
				hashValue,
				PL_CompareValues,
				PL_CompareValues,
				NULL, NULL);

  SM_InitGC(MIN_HEAP_SIZE, MAX_HEAP_SIZE,
            before_gc_hook, hvm,
            after_gc_hook, hvm,
            mark_roots, hvm);
#elif WITH_BOEHM
  GC_INIT();
#endif
}

void
shutdown_collector(HungryVM *hvm,
		   PRBool finalize)
{
#if WITH_SPORTSMODEL
  if (hvm->_verbose_flags & VERBOSE_GC)
    SM_DumpStats (stderr, PR_TRUE);
  SM_CleanupGC(finalize);
#endif
}
