/*
 * object.c
 * Handle create and subsequent garbage collection of objects.
 *
 * Copyright (c) 1996 T. J. Wilkinson & Associates, London, UK.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * Written by Tim Wilkinson <tim@tjwassoc.demon.co.uk>, 1996.
 */

#define	DBG(s)
#define	ADBG(s)

#include "config.h"
#include "config-std.h"
#include "config-mem.h"
#include "gtypes.h"
#include "access.h"
#include "object.h"
#include "constants.h"
#include "classMethod.h"
#include "lookup.h"
#include "itypes.h"
#include "baseClasses.h"
#include "errors.h"
#include "exception.h"
#include "itypes.h"
#include "md.h"
#include "external.h"
#include "gc.h"

extern Hjava_lang_Class* ThreadClass;

extern gcFuncs gcFinalizeObject;
extern gcFuncs gcNormalObject;
extern gcFuncs gcRefArray;
extern gcFuncs gcClassObject;

/*
 * Create a new Java object.
 * If the object is a Class then we make it a root (for the moment).
 * Otherwise, if the object has a finalize method we look it up and store
 * it with the object for it's later use.  Otherwise the object is normal.
 */
Hjava_lang_Object*
newObject(Hjava_lang_Class* class)
{
	Hjava_lang_Object* obj;
	gcFuncs* type;
	int sz;

	if (class == &ClassClass) {
		sz = sizeof(Hjava_lang_Class);
		type = &gcClassObject;
	}
	else {
		sz = CLASS_FSIZE(class);
		if (class->final == false) {
			type = &gcNormalObject;
		}
		else {
			type = &gcFinalizeObject;
		}
	}

	obj = gc_malloc(sz, type);

        /* Fill in object information */
        obj->dtable = class->dtable;

ADBG(	printf("newObject %x class %s\n", obj,
		(class ? class->name->data : "<none>"));
		fflush(stdout);						)

        return (obj);
}

/* Allocate a new array whose elements are ELCLASS (a primitive type),
   and that has COUNT elements. */
Hjava_lang_Object*
newPrimArray(Hjava_lang_Class* elclass, int count)
{
	int size;
	Hjava_lang_Class* class;
	Hjava_lang_Object* obj;

	/* Calculate object size */
	size = (count * TYPE_SIZE(elclass)) + sizeof(Array);
	class = lookupArray(elclass);

	obj = gc_malloc(size, &gcPrimArray);
	obj->dtable = class->dtable;
	ARRAY_SIZE(obj) = count;

	return (obj);
}

/* Allocate a new array whose elements are ELCLASS (an Object type),
   and that has COUNT elements. */
Hjava_lang_Object*
newRefArray(Hjava_lang_Class* elclass, int count)
{
	int size;
	Hjava_lang_Class* class;
	Hjava_lang_Object* obj;

	size = (count * PTR_TYPE_SIZE) + sizeof(Array);
	class = lookupArray(elclass);

	obj = gc_malloc(size, &gcRefArray);
	obj->dtable = class->dtable;
	ARRAY_SIZE(obj) = count;

	return (obj);
}

Hjava_lang_Object*
newMultiArray(Hjava_lang_Class* clazz, int* dims)
{
	Hjava_lang_Object* obj;
	Hjava_lang_Object** array;
	int i;
	
	obj = newArray(CLASS_ELEMENT_TYPE(clazz), dims[0]);
	if (dims[1] > 0) {
		array = OBJARRAY_DATA(obj);
		for (i = 0; i < dims[0]; i++) {
			array[i] = newMultiArray(CLASS_ELEMENT_TYPE(clazz), &dims[1]);
		}
	}

	return (obj);
}

/*
 * Allocate a new array, of whatever types.
 */
Hjava_lang_Object*
newArray(Hjava_lang_Class* eltype, int count)
{
	if (CLASS_IS_PRIMITIVE(eltype)) {
		return (newPrimArray(eltype, count));
	}
	else {
		return (newRefArray(eltype, count));
	}
}

/*
 * Walk an array object objects.
 */
void
walkRefArray(void* base, uint32 size)
{
	Hjava_lang_Object* arr;
	int i;
	Hjava_lang_Object** ptr;
#ifdef notyet
	markObject (.. class ..);
#endif
	gcStats.markedmem += size;
	arr = (Hjava_lang_Object*)base;
	ptr = OBJARRAY_DATA(arr);
	for (i = ARRAY_SIZE(arr); --i>= 0; ) {
		Hjava_lang_Object* el = *ptr++;
		if (el != NULL) {
			markObject(el);
		}
	}
}

/*
 * Object requires finalising - find the method and call it.
 */
void
finalizeObject(void* ob)
{
	Method* final;

	final = findMethod(OBJECT_CLASS((Hjava_lang_Object*)ob), final_name, void_signature);
	CALL_KAFFE_METHOD(final, (Hjava_lang_Object*)ob);
}

/*
 * Walk a class object.
 */
void
walkClass(void* base, uint32 size)
{
	Hjava_lang_Class* class;
	Field* fld;
	int n;

	gcStats.markedmem += size;
	class = (Hjava_lang_Class*)base;

	markObject(class->name);
	if (class->state == CSTATE_PREPARED) {
		markObject(class->superclass);
	}
	markObject(class->constants.data);
	markObject(class->methods);
	markObject(class->fields);
	if (!CLASS_IS_PRIMITIVE(class)) {
		markObject(class->dtable);
	}
	markObject(class->interfaces);
	markObject(class->loader);

	/* Walk the static data elements */
        fld = CLASS_SFIELDS(class);
        n = CLASS_NSFIELDS(class);
        for (; --n >= 0; fld++) {
		if (FIELD_ISREF(fld)) {
			markObject(FIELD_ADDRESS(fld));
		}
        }
}
