/*
 * $Id: jexc_runtime.c,v 1.6 2000/09/29 09:59:56 msato Exp $
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */

#include "jexc.h"
#include "jexc_runtime.h"

jexc_class **jexc_class_table;
jexc_string **jexc_string_table;

jexc_class *jexc_object_class; 	/* root */
jexc_class *jexc_string_class;	/* string */
jexc_class *jexc_integer_class;	/* for hash table */

jexc_word *Top,*jexc_stack;

jexc_catch_info *jexc_catch_p = NULL;
jexc_object *jexc_throw_object;

jexc_string *jexc_string_hash_table[STR_HASH_SIZE];

char *system_property_names[MAX_SYS_PROP];
char *system_property_vals[MAX_SYS_PROP];
int  n_system_property;

void jexc_run_main(int argc,char *argv[],
		   int main_class_id,int method_disp)
{
    jexc_class *main_class;
    jexc_word *sp;
    jexc_array *args;
    int i;
    char *s1,*s2;

    jexc_runtime_init();

    argc--;
    argv++;

    n_system_property = 0;
    while(argc > 0 && strncmp(argv[0],"--D",3) == 0){
	s1 = argv[0]+3;
	s2 = strchr(s1,'=');
	if(s2 == NULL){
	    fprintf(stderr,"bad --D argument");
	    exit(1);
	}
	*s2 = '\0';
	system_property_names[n_system_property] = s1;
	system_property_vals[n_system_property] = s2+1;
	n_system_property++;
	argc--;
	argv++;
    }

    args = jexc_new_array(argc);
    for(i = 0; i < argc; i++) 
	args->elem[i] = (jexc_word)jexc_new_string(argv[i]);

    main_class = jexc_class_table[main_class_id];
    sp = Top;
    sp[0] = (jexc_word)args;
    (*main_class->methods[method_disp])(&sp);
    exit(0);
}

void jexc_runtime_init()
{
    int i;
    jexc_word *sp;

    jexc_class_table = (jexc_class **)xmalloc(sizeof(jexc_class *)*n_class);
    jexc_string_table = 
	(jexc_string **)xmalloc(sizeof(jexc_string *)*n_string_constant);

    /* pre-allocate class, no static fields */
    jexc_object_class = (jexc_class *)xmalloc(sizeof(jexc_class));
    jexc_string_class = (jexc_class *)xmalloc(sizeof(jexc_class));

    /* make string table */
    for(i = 0; i < n_string_constant; i++)
	jexc_string_table[i] = 
	    jexc_intern_string(jexc_new_string(string_constants[i]));

    /* initialize class */
    jexc_class_initialize();

    /* for hash table */
    jexc_integer_class = jexc_find_class("java/lang/Integer");

    /* setup stack */
    Top = jexc_stack = (jexc_word *)xmalloc(STACK_SIZE);

    /* initialize class */
    sp = Top;
    for(i = 0; i < n_class; i++){
	(*jexc_class_table[i]->methods[0])(&sp);
    }

    jexc_init_Lang_class();
}

/* non-GCable memory */
char *xmalloc(int size)
{
    char *p;
    p = (char *)malloc(size);
    if(p == NULL) jexc_error("xmalloc: out of memory, size=%d",size);
    bzero(p,size);
    return p;
}

#ifdef not
jexc_mem *jexc_malloc(int size)
{
    jexc_mem *p;
    p = (jexc_mem *)malloc(size);
    if(p == NULL) jexc_error("jexc_malloc: out of memory, size=%d",size);
    bzero(p,size);
    p->size = size;
    return p;
}
#endif

jexc_class *jexc_define_class(char *name,int id,int super,int i_static,
			  int n_static,int i_var,int n_var,int n_method)
{
    int i;
    jexc_class *cp,*super_cp;

    if(strcmp(name,"java/lang/Object") == 0)
	cp = jexc_object_class;
    else if(strcmp(name,"java/lang/String") == 0)
	cp = jexc_string_class;
    else 
	cp = (jexc_class *)xmalloc(sizeof(jexc_class)+
				   sizeof(jexc_word)*(n_static-1)+i_static);
    cp->name = name;
    cp->i_static = i_static;
    cp->n_static = n_static;
    cp->i_var = i_var;
    cp->n_var = n_var;
    cp->n_method = n_method;
    cp->methods = (jexc_c_func *)xmalloc(sizeof(jexc_c_func)*n_method);

    if(super >= 0){
	super_cp = jexc_class_table[super];
	cp->super = super_cp;

	/* copy super's methods */
	for(i = 0; i < super_cp->n_method; i++)
	    cp->methods[i] = super_cp->methods[i];
    }
    
    /* set i_static_base */
    cp->i_static_base = (char *)(cp->n_static_base + cp->n_static);

    /* set size */
    cp->obj_size = sizeof(jexc_object)+sizeof(jexc_word)*(n_var-1)+i_var;

    /* set this in class_table */
    jexc_class_table[id] = cp;
    
    return cp;
}

void jexc_add_interface(jexc_class *cp,int m_id,int disp)
{
    jexc_interf *ip;
    ip = (jexc_interf *)xmalloc(sizeof(jexc_interf));
    ip->method_id = m_id;
    ip->f = cp->methods[disp];
    ip->link = cp->interfaces;
    cp->interfaces = ip;
}

jexc_c_func jexc_lookup_interface(jexc_object *p,int m_id)
{
    jexc_interf *ip;
    for(ip = p->class->interfaces; ip != NULL; ip = ip->link){
	if(ip->method_id == m_id) return ip->f;
    }
    jexc_error("cannot find interface");
    return NULL;
}

jexc_class *jexc_find_class(char *name)
{
    int i;
    for(i = 0; i < n_class; i++){
	if(strcmp(jexc_class_table[i]->name,name) == 0){
	    return jexc_class_table[i];
	}
    }
    jexc_error("'%s' is not found",name);
    return NULL;
}


jexc_object *jexc_new_object(jexc_class *cp)
{
    jexc_object *p;
    p = (jexc_object *)jexc_malloc(cp->obj_size);
    p->class = cp;
    p->i_var_base = (char *)(p->n_var_base + cp->n_var);
    return p;
}

jexc_opaque *jexc_alloc_opaque(int size)
{
    jexc_opaque *op;
    op = (jexc_opaque *)jexc_malloc(sizeof(jexc_opaque)+size-1);
    op->class = NULL;
    return op;
}

jexc_string_opaque *jexc_alloc_string_opaque(int l,char *str)
{
    jexc_string_opaque *op;
    op = (jexc_string_opaque *)jexc_malloc(sizeof(jexc_string_opaque)+l);
    op->class = NULL;
    op->length = l;
    if(str != NULL) memcpy(op->str,str,l);
    op->str[l+1] = '\0';  /* +1, terminate */
    return op;
}

jexc_string *jexc_new_string(char *str)
{
    int l;
    jexc_string *p;
    p = (jexc_string *)jexc_malloc(sizeof(jexc_string));
    p->class = jexc_string_class;
    if(str != NULL){
	l = strlen(str);
	p->op = jexc_alloc_string_opaque(l,str);
    }
    return p;
}

jexc_string *jexc_new_string_n(char *str,int len)
{
    jexc_string *p;
    p = (jexc_string *)jexc_malloc(sizeof(jexc_string));
    p->class = jexc_string_class;
    p->op = jexc_alloc_string_opaque(len,str);
    return p;
}

static int gen_hcode_str(char *str,int len)
{
    int i;
    int c = 0;
    if(len > 10) len = 10;
    for(i = 0; i < len; i++) c = (c << 1)+str[i];
    return c;
}

jexc_string *jexc_intern_string(jexc_string *sp)
{
    jexc_string *sq;
    char *str;
    int hcode,len;

    if(sp->link != NULL) return sp;
    str = sp->op->str;
    len = sp->op->length;
    hcode = gen_hcode_str(str,len) & STR_HASH_MASK;
    for(sq = jexc_string_hash_table[hcode]; sq != NULL; sq = sq->link){
	if(sq->class != jexc_string_class) abort();
	if(sq->op->length == len &&
	   strncmp(sq->op->str,str,len) == 0) return sq;
    }
    /* not found */
    sp->link = jexc_string_hash_table[hcode];
    jexc_string_hash_table[hcode] = sp;
    return sp;
}

jexc_array *jexc_new_array(int len)
{
    jexc_array *ap;
    ap = (jexc_array *)jexc_malloc(sizeof(jexc_array)+
				   sizeof(jexc_word)*(len-1));
    ap->class = ARRAY_OBJECT;
    ap->length = len;
    return ap;
}

jexc_array *jexc_new_multiarray(int ndim,jexc_word *sp)
{
    jexc_array *ap;
    int i,len;
    
    if(ndim <= 1) return jexc_new_array(*((jexc_int *)sp));
    else {
	len = *((jexc_int *)sp);
	ap = jexc_new_array(len);
	for(i = 0; i < len; i++)
	    ap->elem[i] = (jexc_word)jexc_new_multiarray(ndim-1,sp+1);
	return ap;
    }
}

jexc_iarray *jexc_new_iarray(int len,int kind)
{
    jexc_iarray *ap;
    int size = 0;

    switch(kind){
    case IARRAY_BOOLEAN:
    case IARRAY_BYTE:
	size = 1;
    case IARRAY_SHORT:
    case IARRAY_CHAR:
	size = 2;
	break;
    case IARRAY_FLOAT:
    case IARRAY_INT:
	size = 4;
	break;
    case IARRAY_DOUBLE:
    case IARRAY_LONG:
	size = 8;
	break;
    default:
	jexc_error("jexc_new_iarray: bad type %d",kind);
    }

    ap = (jexc_iarray *)jexc_malloc(sizeof(jexc_iarray)+size*len-1);
    ap->class = IARRAY_OBJECT;
    ap->length = len;
    return ap;
}

/* 
 * catch & throw
 */
int jexc_catch_match(int pc,int start,int end,int id)
{
    jexc_class *cp,*cq;

    if(pc < start) return FALSE;
    if(pc >= end) return FALSE;

    /* check throw object */
    if(id < 0) return TRUE;
    cq = jexc_class_table[id];
    for(cp = jexc_throw_object->class; cp != NULL; cp = cp->super){
	if(cp == cq) return TRUE;
    }
    return FALSE;
}

void jexc_throw(jexc_object *p)
{
    if(p != NULL) jexc_throw_object = p;
    if(jexc_catch_p == NULL) jexc_error("jexc_throw: no catch");
    longjmp(jexc_catch_p->env,1);
    jexc_error("jexc_throw: longjmp failed");
}

/* checkcast and instanceof */
void jexc_checkcast(jexc_object *p,jexc_class *cp)
{
    if(p == NULL) return;
    if(!jexc_instanceof(p,cp)) jexc_error("ClassCastExecption");
}

int jexc_instanceof(jexc_object *p,jexc_class *cp)
{
    jexc_class *cpp;
    for(cpp = p->class; cpp != NULL; cpp = cpp->super)
	if(cpp == cp) return TRUE;
    return FALSE;
}

/* nonfatal error message */
/* VARARGS0 */
void
jexc_error EXC_VARARGS_DEF(char *, fmt)
{ 
    va_list args;

    fprintf(stderr,"Error:");
    EXC_VARARGS_START(char *, fmt, args);
    vfprintf(stderr, fmt, args);
    va_end(args);
    fprintf(stderr, "\n" );
    fflush(stderr);
    abort(); /* for debug */
}



