/*  alloc.c -- memory allocation routines */

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include "toba.h"
#include "runtime.h"
#include "sthreads.h"
#include "../gc/gc.h"
void GC_finalize_all(), GC_register_finalizer_no_order();

#include "java_lang_Class.h"
#include "java_lang_Object.h"
#include "java_lang_String.h"
#include "java_lang_StringBuffer.h"
#include "java_lang_OutOfMemoryError.h"



static Object mkarray(Class c, int ndim, int dlist[]);

static Object memerror;			/* out-of-memory Error exception */


static void null_warn_proc(char *msg, GC_word arg) /*ARGSUSED*/ {}

/* 
 *  membootstrap - basic pre-everything memory system initialization.
 */
void membootstrap(void)
{
    GC_set_warn_proc(null_warn_proc);
}

/*
 *  meminit -- initialize memory interface
 *
 * These environment variables configure the memory allocation scheme:
 *
 * Name			Value		Meaning
 * ---------------	------------	-----------------------------
 * TOBA_HEAP		integer > 0     initial heap size
 * TOBA_NOCOLLECT	- 		if set, don't collect garbage
 */		

void meminit(void)
{
#ifndef SCOUT
    /* Scout doesn't support environment; skip this stuff */
    size_t min_heap_size = DEFAULT_HEAP;
    size_t curr_heap_size;
    char *user_heap_size;

    if (getenv("TOBA_NOCOLLECT"))
	GC_dont_gc++;

    user_heap_size = getenv("TOBA_HEAP");
    if (user_heap_size)
	min_heap_size = atoi(user_heap_size);

    user_heap_size = getenv("TOBA_HEAP_MAX");
    if (user_heap_size) {
        size_t max_heap_size = atoi(user_heap_size);
        GC_set_max_heap_size (max_heap_size);
    }

    curr_heap_size = GC_get_heap_size();
    if (curr_heap_size < min_heap_size)
        GC_expand_hp(min_heap_size - curr_heap_size);
#endif /* ! SCOUT */

    /* construct exception now so that it's ready when needed */
    memerror = construct(&cl_java_lang_OutOfMemoryError.C);

    return;
}



/*  memexit -- shut down memory interface  */

void memexit(void)
{
    GC_finalize_all();
    return;
}



/*  allocate(n) -- allocate an object of n bytes  */

void *allocate(int n_bytes)
{
    void *addr;
    struct mythread *thr = mythread();

    /* GC_malloc allocates garbage-collected zero-filled memory */
    sthread_dontkill_start(thr);
    addr = GC_malloc(n_bytes);
    sthread_dontkill_end(thr);

    /* Possibly run some finalizers. Only do so if we're in a 
     * real java thread. */
    if (thr)
        run_some_finalizers(thr, 0);

    if (addr)
	return addr;
 
    athrow(memerror);			/* throw exception built earlier */
    /*NOTREACHED*/
    return NULL;
}


/*  allocateuncol(n) -- allocate an object of n bytes in uncollectable memory  */

void *allocateuncol(int n_bytes)
{
    void *addr;
    struct mythread *thr = mythread();

    /* GC_malloc_uncollectable allocates memory which may contain tracked
     * pointers, but which is itself not collected.  E.g., pointers which
     * are encoded into generated instructions by the JIT should be allocated
     * by this beast. */
    sthread_dontkill_start(thr);
    addr = GC_malloc_uncollectable (n_bytes);
    sthread_dontkill_end(thr);

    /* possibly run some finalizers */
    run_some_finalizers(thr, 0);

    if (addr)
	return addr;
 
    athrow(memerror);			/* throw exception built earlier */
    /*NOTREACHED*/
    return NULL;
}



/*  finalizer(o, f) -- register finalizer f for object o  */
void finalizer(Object o, void (*func) (void *)) 
{
    /* The "this" pointer gets passed implicitly by the collector already. */

    /* pre-allocate a queue node for the finalizer queue */
    finalizer_queue_prealloc();

    /* 
     * we actually register a function that enqueues the garbage
     * for a separate finalizer thread to run 
     */
    GC_register_finalizer_no_order((void *)o, needs_finalization, 
        func, NULL, NULL);
} 



/*  construct(c) -- create a new instance and call the default constructor  */

Object construct(Class c)
{
    Object inst;

    inst = new(c);
    c->constructor(inst);
    return inst;
}



/*  new(c) --  create new instance of class c  */

Object new(Class c)
{
    Object o;

    if (c->needinit)
	initclass(c);
    o = allocate(c->instsize);
    ((struct in_generic *)o)->class = c;

    /*
     *  Register a finalizer only if not inherited from java.lang.Object.
     *  Finalizers incur a large overhead, but the Object finalizer does
     *  nothing.
     */
    if (c->finalizer != finalize__UKxhs)
	finalizer(o, c->finalizer);

    return o;
}



/*  anewarray(c, n) -- create new array of objects or primitives  */

Object anewarray(Class c, int nelem)
{
    Class a;
    struct aarray *o;

    if (nelem < 0)
	throwNegativeArraySizeException(nelem);

    a = c->arrayclass;			/* get class of new object */
    if (!a)				/* if not yet initialized */
	a = c->arrayclass = arrayclassof(c);

    o = allocate(offsetof(struct aarray, data[0]) + nelem * a->instsize);
    o->class = a;
    o->length = nelem;
    return o;
}



/*  arrayclassof(c) -- create a class struct for an array of objects  */

static CARRAY(1) c_lbrack = {&acl_char, 0, 1, 0, {'['}};
static struct in_java_lang_String s_lbrack =
    { &cl_java_lang_String, 0, &c_lbrack, 0, 1 };

Class arrayclassof(Class c)
{
    Object o;
    Class a = allocate(sizeof (struct cl_java_lang_Object));
    static Class objptr = &cl_java_lang_Object.C;

    /* make class name */
    o = new(&cl_java_lang_StringBuffer.C);	/* new StringBuffer */
    init_S_a8OuK(o, &s_lbrack);			/* init to "[" */
    if ((c->flags & IS_ARRAY) == 0)
	append_c_PKutk(o, 'L');			/* append 'L' */
    append_S_6tRW4(o, c->name);			/* append class name */
    if ((c->flags & IS_ARRAY) == 0)
	append_c_PKutk(o, ';');			/* append ';' */
    a->name = toString__GjBaS(o);		/* convert to string */

    a->flags = IS_ARRAY;
    a->classclass.class = &cl_java_lang_Class.C;
    a->instsize = sizeof(Object);
    a->nimethods = cl_java_lang_Object.C.nimethods;
    a->nsmethods = cl_java_lang_Object.C.nsmethods;
    a->nivars    = cl_java_lang_Object.C.nivars;
    a->ncvars    = cl_java_lang_Object.C.ncvars;
    a->smethods  = cl_java_lang_Object.C.smethods;
    a->ivars     = cl_java_lang_Object.C.ivars;
    a->cvars     = cl_java_lang_Object.C.cvars;
    a->nsupers = 1;
    a->supers = &objptr;
    a->elemclass = c;
    a->constructor = cl_java_lang_Object.C.constructor;
    a->finalizer = cl_java_lang_Object.C.finalizer;

    memcpy(((struct cl_generic *)a)->M, &OBJ_METHODS, sizeof(OBJ_METHODS));
    return a;
}



Object mnewarray(Class c, int arraydim, int ndim, int *dlist)
{
    int i;

    /* make Class structs out to final dimension */
    for (i = 0; i < arraydim; i++) {
	if (!c->arrayclass)
	    c->arrayclass = arrayclassof(c);
	c = c->arrayclass;
    }
    /* recursively create the requested number of dimensions */
    return mkarray(c->elemclass, ndim, dlist);

}

/*  mnewarray(c, arraydim, ndim, d1, d2, ...) -- implement multianewarray  */

Object vmnewarray(Class c, int arraydim, int ndim, /*d1,d2,*/...)
{
    va_list ap;
    int dlist[256];		/* bytecode limits dimensions to 256 max */
    int i;

    /* store dimensions from vararg list in local array */
    va_start(ap, ndim);
    for (i = 0; i < ndim; i++)
	dlist[i] = va_arg(ap, int);
    va_end(ap);

    return mnewarray(c, arraydim, ndim, dlist);
}

static Object mkarray(Class c, int ndim, int *dlist)  /* make ndim-array of c */
{
    int i;
    Object o;

    if (0 > *dlist) {
       /* Have to throw an error here. */
       throwNegativeArraySizeException(*dlist);
    }
    o = anewarray(c, *dlist);
    if (ndim > 1)
	for (i = 0; i < *dlist; i++)
	    ((struct aarray *)o)->data[i] =
		mkarray(c->elemclass, ndim - 1, dlist + 1);
    return o;
}
