/* Native routines to allow Java code to manipulate classes */
#include <stddef.h>

#include "toba.h"

#include "java_lang_ClassLoader.h"
#include "java_lang_String.h"
#include "java_lang_Class.h"

#include "toba_runtime_ClassRT.h"
#include "toba_classfile_ClassData.h"
#include "toba_classfile_Field.h"
#include "toba_classfile_Names.h"
#include "toba_classfile_Method.h"
#include "classfile_ClassData.h"

#include "runtime.h"

static void *longToAddress(Long l)
{
    return (void *)(long)l;
}
static Long addressToLong(void *v)
{
    return (Long)(long)v;
}


/* Return the byte offset in the class structure for the start of the method 
 * table */
int methodTableOffset() 
{
    int cl_size;

    /* Class structures start with a class descriptor structure */
    cl_size = offsetof(struct cl_generic, M);

    return cl_size;
}

static int classVariableOffset(int mtlen, int cvalign)
{
    int cl_size;
    int curralign;
    int cvpadding;

    /* Classes start with a class descriptor and a method table */
    cl_size = methodTableOffset();
    cl_size += mtlen * sizeof(struct mt_generic);

    /* Next, compute class variable table alignment if necessary */
    if (cvalign > 0) {
	curralign = 8 - (cl_size % 8);
	cvpadding = curralign % cvalign;

	cl_size += cvpadding;
    }
    return cl_size;
}

static int classStructSize(int mtlen, int cvspace, int cvalign) 
{
    int cl_size;
    int curralign;
    int structpadding;

    /* Classes start with a class descriptor, a method table, and a class
       variable table */
    cl_size = classVariableOffset(mtlen, cvalign);
    cl_size += cvspace;

    /* Now, pad the whole structure out to an 8 byte boundary just
     * to be nice. (I could pad it smaller, but what the hell.) */
    curralign = 8 - (cl_size % 8);
    structpadding = curralign % 8;
    cl_size += structpadding;

    return cl_size;
}

/* Function to turn an array of longs that represent classes into 
 * a C-array of class pointers */
static void setClassArray(Class **dest, int *length, struct larray *source)
{
    int i;
    *length = source->length;
    *dest = allocate(sizeof(Class) * (*length));

    /* Copy the addresses. This is necessary since we don't know the size of 
     * addresses on the java side. This means java has to treat them as 
     * longs, which could be wrong. This copy could be optimized away on 
     * 64-bit machines. */
    for (i = 0; i < (*length); i++) {
	/* Since others is either the same size or smaller than the data,
	 * we don't worry about sign extension problems */
	(*dest)[i] = longToAddress(source->data[i]);
    }
    return;
}


/* Allocate all of the native data structures associated with a class */
Object
allocateNativeClass_Ciii_q3OuE(Object arg1, 
			       int nimethods, 
			       int cvspace, int cvalignment)
{
    struct in_toba_classfile_ClassData * cd = arg1;
    Class cl;
    int cl_size;
    struct cl_generic *clg;
    
    /* Oh heck, let's make sure this is initialized */
    initclass (&cl_toba_runtime_ClassRT.C);

    /* Figure out how big the class should be, and allocate space for it. */
    cl_size = classStructSize(nimethods, cvspace, cvalignment);
    clg = allocate(cl_size);

    /* Most things are set up later, but we assign whatever we can now. */
    cl = &clg->C;
    cl->needinit = 1;           /* yes, it needs to be initialized */
    cl->access = cd->access;    /* access mask */
    cl->flags = 0;              /* runtime flags; why is this different? */
    if (cl->access & ACC_INTERFACE) {
       cl->flags |= IS_INTERFACE;
    }
    cl->name = cd->name;        /* class name (j.l.String) */
    cl->classclass.class = cl;  /* j.l.Class instance */
    cl->classclass.monitor = NULL;
    /* Following all set later */
    cl->instsize = -1;
    cl->nimethods = -1;
    cl->nsmethods = -1;
    cl->nivars = -1;
    cl->ncvars = -1;
    cl->nsupers = -1;
    cl->ninters = -1;
    cl->ndinters = -1;
    cl->inters = NULL;
    cl->hashmask = -1;
    cl->htable = NULL;
    cl->nothers = -1;
    cl->arrayclass = NULL;
    cl->elemclass = NULL;
    cl->strpool = NULL;
    cl->strlist = NULL;
    cl->initializer = NULL; /* throwInstantiationException; */
    cl->constructor = throwInstantiationException;
    cl->finalizer = throwInstantiationException;
    cl->nimethods = nimethods;
    cl->classclass.class = &cl_java_lang_Class.C;
    cl->clsformat = TOBA_CLASSSTRUCT_VERSION;
    /* cl->access set above */
    cl->cldata = cd;
    cl->clref = cd->myRef;
    cl->ivars = NULL;
    cl->cvars = NULL;
    cl->smethods = NULL;

    return &cl->classclass;
}


Object getName_C_SSVyw(Object arg) 
{
    Class native;
    
    initclass(&cl_toba_runtime_ClassRT.C);
    native = find_class(arg);
    return native->name;
}

Void setName_CS_3bu6E(Object arg1, Object n) 
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);
    c->name = n;
    return;
}

Int getFlags_C_qjUQo(Object arg1) {
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);
    return c->flags;
}

Void setFlags_Ci_yMDL3(Object arg1, Int f) 
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);
    c->flags = f;
    return;
}

Long getNativeClass_C_Hx1ge(Object arg1)
{
    initclass(&cl_toba_runtime_ClassRT.C);

    return addressToLong(find_class(arg1));
}

Object getElementClass_C_HlJzC(Object arg1)
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);
    return find_classclass(c->elemclass);
}

Object getArrayClass_C_Mzo2C(Object arg1)
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);

    if (!c->arrayclass)
	c->arrayclass = arrayclassof(c);

    return find_classclass(c->arrayclass);
}

Object getSuperclass_C_3Rbmg(Object arg1)
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);

    if (c->nsupers < 2)
	return 0;
    return find_classclass(c->supers[1]);
}

Object getClassDataInternal_C_6jsmq(Object arg1)
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);

    return c->cldata;
}

Void initClass_C_2WWoD(Object arg1)
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(c);

    return;
}

void setClassData_CC_P9Q3H(Object arg1, Object arg2)
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);

    c->cldata = arg2;
}

Void setClassLoader_CC_rIhpG(Object arg1, Object arg2) 
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    initclass(&cl_toba_runtime_ClassRT.C);
    c->classloader = arg2;
    return;
}

Void setInstanceSize_Ci_LTnNf(Object arg1, Int arg2) 
{
    struct in_java_lang_Class *inst = arg1;
    Class c = find_class(inst);
    c->instsize = arg2;
    return;
}

Void setInterfaces_Cali_eWv1l(Object arg1, Object arg2, Int ndinters)
{
    struct in_java_lang_Class *inst = arg1;
    struct larray *inters = arg2;
    Class cl = find_class(inst);

    cl->ndinters = ndinters;

    setClassArray((Class **)&cl->inters, &cl->ninters, inters);
    return;
}

void setReferencedClasses_Cal_u89Wz(Object arg1, Object refs)
{
    struct larray *array = refs;
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);

    setClassArray((Class **)&cl->others, &cl->nothers, array);
    return;
}

void setSuperclasses_Cal_YPMqb(Object arg1, Object supers)
{
    struct larray *array = supers;
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);

    setClassArray((Class **)&cl->supers, &cl->nsupers, array);
    return;
}

/** Given a java.lang.String object, assign the pointers to the Unicode
  * character data, and the string length. */
static void
setCharLen (Object ob,          /* jlS object */
            const Char ** cpp,  /* where to store pointer to string */
            int * szp)          /* where to store string length */
{
   struct in_java_lang_String * jls = (struct in_java_lang_String *) ob;
   struct carray * ca = (struct carray *) jls->value;

   *cpp = ca->data + jls->offset;
   *szp = jls->count;
   return;
}

void setInstanceVariable_CaFai_kBgUZ(Object arg1, Object arg2, Object arg3)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);
    struct aarray *fields = arg2;
    struct iarray *offsets = arg3;
    int i;
    struct vt_generic *ivtable;

    ivtable = allocate(fields->length * sizeof(struct vt_generic));
    for (i = 0; i < fields->length; i++) {
	struct in_toba_classfile_Field *f = fields->data[i];
	struct vt_generic * ivp;

	ivp = ivtable + f->tableslot;
	ivp->offset = offsets->data[i];
	ivp->addr = NULL;       /* Not a class variable */
        setCharLen (f->name, &ivp->name_chars, &ivp->name_len);
        setCharLen (f->signature, &ivp->sig_chars, &ivp->sig_len);
	/* Field is local iff it's in the array of classdata fields.
	 * We should have assigned the cldata pointer when we built this
	 * native class structure. */
	if (NULL == cl->cldata) {
	   throwInternalError ("Class definition: missing ClassData structure");
	}
	ivp->localp = isInArray_aF_ppXbl (f, cl->cldata->fields);
	ivp->access = f->access;
    }
    cl->ivars = ivtable;
    cl->nivars = fields->length;
    return;
}


void setConstructor_Cl_4WHPv(Object arg1, Long l)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);

    cl->constructor = longToAddress(l);
}
void setFinalizer_Cl_QzTuj(Object arg1, Long l)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);

    cl->finalizer = longToAddress(l);
}
void setClassInitializer_Cl_IC0rH(Object arg1, Long l)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);

    cl->initializer = longToAddress(l);
}


Void setClassVariables_CaFaii_u9TNg(Object arg1, Object arg2, Object arg3, 
				    Int cvalign)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);
    struct aarray *fields = arg2;
    struct iarray *offsets = arg3;
    int i;
    struct vt_generic *cvtable;
    char *cvbase; 

     /* The space for the class variables comes just after the 
      * method table in the class structure */

    cvtable = allocate(fields->length * sizeof(struct vt_generic));
    cvbase = (char *)cl + classVariableOffset(cl->nimethods, cvalign);

    for (i = 0; i < fields->length; i++) {
	struct in_toba_classfile_Field *f = fields->data[i];
	struct vt_generic * cvp;

	cvp = cvtable + f->tableslot;
	cvp->offset = 0;	/* Not an instance variable */
	cvp->addr = cvbase + offsets->data[i];
        setCharLen (f->name, &cvp->name_chars, &cvp->name_len);
        setCharLen (f->signature, &cvp->sig_chars, &cvp->sig_len);
	/* Field is local iff it's in the array of classdata fields.
	 * We should have assigned the cldata pointer when we built this
	 * native class structure. */
	if (NULL == cl->cldata) {
	   throwInternalError ("Class definition: missing ClassData structure");
	}
	cvp->localp = isInArray_aF_ppXbl (f, cl->cldata->fields);
	cvp->access = f->access;
    }

    cl->cvars = cvtable;
    cl->ncvars = fields->length;

    return;
}


Void setInstanceMethods_CaFal_C8R2B(Object arg1, Object arg2, Object arg3)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);
    struct aarray *methods = arg2;
    struct larray *addresses = arg3;
    int i;
    struct mt_generic *mtable = 
	(struct mt_generic *)((char *)cl + methodTableOffset());
    
    for (i = 0; i < methods->length; i++) {
	struct in_toba_classfile_Field *f = methods->data[i];
	struct mt_generic * mtp;
	
	mtp = mtable + i;
	mtp->itype = TMIT_native_code;
	mtp->f = longToAddress(addresses->data[i]);
        setCharLen (f->name, &mtp->name_chars, &mtp->name_len);
        setCharLen (f->signature, &mtp->sig_chars, &mtp->sig_len);
	/* Field is local iff it's in the array of classdata fields.
	 * We should have assigned the cldata pointer when we built this
	 * native class structure. */
	if (NULL == cl->cldata) {
	   throwInternalError ("Class definition: missing ClassData structure");
	}
	mtp->localp = isInArray_aF_ppXbl (f, cl->cldata->fields);
	mtp->access = f->access;
    }
    return;
}

Void setStaticMethods_CaFal_HxsKh(Object arg1, Object arg2, Object arg3)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);
    struct aarray *methods = arg2;
    struct larray *addresses = arg3;
    int i;
    struct mt_generic *stable;

    stable = allocate(sizeof(struct mt_generic) * methods->length);
    
    for (i = 0; i < methods->length; i++) {
	struct in_toba_classfile_Field *f = methods->data[i];
	struct mt_generic * stp;
	
	stp = stable + i;
	stp->itype = TMIT_native_code;
	stp->f = longToAddress(addresses->data[i]);
        setCharLen (f->name, &stp->name_chars, &stp->name_len);
        setCharLen (f->signature, &stp->sig_chars, &stp->sig_len);
	/* Field is local iff it's in the array of classdata fields.
	 * We should have assigned the cldata pointer when we built this
	 * native class structure. */
	if (NULL == cl->cldata) {
	   throwInternalError ("Class definition: missing ClassData structure");
	}
	stp->localp = isInArray_aF_ppXbl (f, cl->cldata->fields);
	stp->access = f->access;
    }

    cl->smethods = stable;
    cl->nsmethods = methods->length;
    return;
}

Void setStringPool_CaS_eXgDr(Object arg1, Object arg2)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);
    struct aarray *strings = arg2;
    int i;

    cl->strlist = allocate(sizeof(Object) * (strings->length + 1));
    for (i = 0; i < strings->length; i++) {
	cl->strlist[i] = strings->data[i];
    }
    cl->strlist[strings->length] = 0;

}

Void setHashTable_CiaFal_qCtif(Object arg1, Int arg2, Object arg3, Object arg4)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);
    int mask = arg2;
    struct aarray *fields = arg3;
    struct larray *addrs = arg4;
    struct ihash *hashtable = allocate(sizeof(struct ihash) * fields->length);
    struct in_toba_classfile_Field *f;
    int i;
    for (i = 0; i < fields->length; i++) {
	f = fields->data[i];
	if (f != 0) {
	    hashtable[i].n = f->hashcode;
	    hashtable[i].mt = longToAddress(addrs->data[i]);
	} else {
	    hashtable[i].n = 0;
	    hashtable[i].mt = 0;
	}
    }

    cl->hashmask = mask;
    cl->htable = hashtable;
}

Int
getInstanceVarOffset_Ci_BWHS7(Object arg1, Int index)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);

    initclass(&cl_toba_runtime_ClassRT.C);

    if (0 == cl->ivars) {
	/* XXX */;
    }

    return cl->ivars[index].offset;
}

Long
getClassVarAddr_Ci_PQTNy(Object arg1, Int index)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);

    initclass(&cl_toba_runtime_ClassRT.C);

    if (0 == cl->cvars) {
	/* XXX */;
    }

    return addressToLong(cl->cvars[index].addr);
}

/* Return the address of the struct mt_generic entry defining the
 * method that's in the index'th slot in either the static or instance
 * method table of arg1's class. */
Long
getMethodTEnt_Czi_ph2B5 (Object arg1,
                         Boolean instancep,
                         Int index)
{
    struct in_java_lang_Class *inst = arg1;
    Class cl = find_class(inst);
    struct mt_generic *mtable;

    initclass(&cl_toba_runtime_ClassRT.C);

    if (instancep) {
       mtable = (struct mt_generic *)((char *)cl + methodTableOffset());
       if (index >= cl->nimethods) {
          /* throw error */
       }
       return addressToLong(mtable + index);
    } else {
       if ((NULL == cl->smethods) ||
           (index >= cl->nsmethods)) {
          /* throw error */
       }
       return addressToLong (cl->smethods + index);
    }
}

Long
getMTEntFuncPtr_l_lUIsL (Long arg1)
{
   struct mt_generic * mtp;

   mtp = longToAddress (arg1);
   if (NULL == mtp) {
      /* throw NPE */
   }
   return addressToLong (mtp->f);
}

