/*
 * $Id: read_class_file.c,v 1.4 2000/09/06 18:30:59 m-hirano 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"

#define INPUT_BUF_SIZE  4096

extern char *xmalloc(int size);
extern char *intern(char *name);
#define STR_EQ(s1,s2) (strcmp(s1,s2) == 0)

void read_cp_info(cp_info *cp);
void read_method_info(method_info *mp);
void read_field_info(field_info *fp);
void *read_code_attr();
void *read_exceptions_attr();
int read_u4();
int read_u2();
int read_u1();
void read_bytes(char *p,int nbytes);

class_file *current_class;

FILE *input_fp;
char input_buf[INPUT_BUF_SIZE];

class_file *read_class_file(char *file_name)
{
    int k,index,len;
    char *atr;
    class_file *cfp;

    fprintf(stderr,"%s:\n",file_name);

    if((input_fp = fopen(file_name,"r")) == NULL){
	fprintf(stderr,"cannot open '%s'\n",file_name);
	exit(1);
    }

    cfp = (class_file *)xmalloc(sizeof(class_file));
    current_class = cfp;

    cfp->magic = read_u4();
    cfp->major_version = read_u2();
    cfp->minor_version = read_u2();

    if(cfp->magic != MAGIC) 
	fatal("bad magic number in class file(0x%x)",cfp->magic);

    cfp->cp_count = read_u2();
    cfp->cp_info_table = (cp_info *)xmalloc(sizeof(cp_info)*cfp->cp_count);

    for(k = 1; k < cfp->cp_count; k++){
	read_cp_info(&cfp->cp_info_table[k]);
	/*debug*//*
	printf("%d:",k); 
	print_cp_info(stdout,&cfp->cp_info_table[k]);
	printf("\n");
	*/
	if(cfp->cp_info_table[k].tag == CONSTANT_Long ||
	   cfp->cp_info_table[k].tag == CONSTANT_Double) k++;
    }

    cfp->access_flag = read_u2();
    cfp->this_class = read_u2();
    cfp->super_class = read_u2();

    cfp->interface_count = read_u2();
    if(cfp->interface_count != 0){
	cfp->interfaces = (int *)xmalloc(sizeof(int)*cfp->interface_count);
	for(k = 0; k < cfp->interface_count; k++)
	    cfp->interfaces[k] = read_u2();
    }

    cfp->field_count = read_u2();
    /* read field_info */
    if(cfp->field_count != 0){
	cfp->fields = (field_info *)
	    xmalloc(sizeof(field_info)*cfp->field_count);
	for(k = 0; k < cfp->field_count; k++)
	    read_field_info(&cfp->fields[k]);
    }

    cfp->method_count = read_u2();
    if(cfp->method_count != 0){
	cfp->methods = (method_info *)
	    xmalloc(sizeof(method_info)*cfp->method_count);
	for(k = 0; k < cfp->method_count; k++){
	    read_method_info(&cfp->methods[k]);
	}
    }

    cfp->attr_count = read_u2();
    if(cfp->attr_count != 0){
	cfp->attrs = (attr_info *)xmalloc(sizeof(attr_info)*cfp->attr_count);
	for(k = 0; k < cfp->attr_count; k++){
	    index = read_u2();
	    len = read_u4();
	    atr = get_cp_str(current_class,index);
	    if(STR_EQ(atr,"SourceFile")){
		cfp->attrs[k].kind = ATTR_SOURCE_FILE;
		cfp->attrs[k].index = read_u2();
		continue;
	    } else if(STR_EQ(atr,"InnerClasses")){
		cfp->attrs[k].kind = ATTR_INNER_CLASS;
		read_bytes(input_buf,len); /* skip */
#ifdef DoDepre
	    } else if(STR_EQ(atr,"Deprecated")){
		cfp->attrs[k].kind = ATTR_NONE;
		read_bytes(input_buf,len); /* skip */
#endif
	    } else {
		fatal("bad attr in class (index=%d,%s)\n",
		      index,atr);
		exit(1);
	    }
	}
    }

    fclose(input_fp);
    return cfp;
}


void read_cp_info(cp_info *cp)
{
    int l;

    cp->tag = (enum cp_tag)read_u1();
    switch(cp->tag){
    case CONSTANT_utf8:
	l = read_u2();
	if(l >= MAX_STR_LEN){
	    fatal("too long string\n");
	}
	read_bytes(input_buf,l);
	input_buf[l] = '\0';
	cp->info.utf8.len = l;
	cp->info.utf8.str = intern(input_buf);
	break;
    case CONSTANT_Integer:
    case CONSTANT_Float:
	cp->info.i = read_u4();
	break;
    case CONSTANT_Long:
    case CONSTANT_Double:
	cp->info.L.high = read_u4();
	cp->info.L.low = read_u4();
	/* swap ??? */
	break;
    case CONSTANT_Class:
	cp->info.class.name_index = read_u2();
	break;
    case CONSTANT_String:
	cp->info.string.string_index = read_u2();
	break;
    case CONSTANT_Fieldref:
	cp->info.field.class_index = read_u2();
	cp->info.field.name_index = read_u2();
	break;
    case CONSTANT_Methodref:
    case CONSTANT_InterfaceMethodref:
	cp->info.method.class_index = read_u2();
	cp->info.method.name_index = read_u2();
	break;
    case CONSTANT_NameAndType:
	cp->info.name.name_index = read_u2();
	cp->info.name.desc_index = read_u2();
	break;
    default:
	fatal("bad cp_info tag %d\n",cp->tag);
    }

}

void read_field_info(field_info *fp)
{
    int k,index,len;
    char *atr;

    fp->access_flag = read_u2();
    fp->name_index = read_u2();
    fp->desc_index = read_u2();
    fp->attr_count = read_u2();
    if(fp->attr_count != 0){
	fp->attrs = (attr_info *)xmalloc(sizeof(attr_info)*fp->attr_count);
	for(k = 0; k < fp->attr_count; k++){
	    index = read_u2();
	    len = read_u4();	/* discard */
	    atr = get_cp_str(current_class,index);
	    if(STR_EQ(atr,"ConstantValue")){
		fp->attrs[k].kind = ATTR_CONST_VAL;
		fp->attrs[k].index = read_u2();
	    } else if(STR_EQ(atr,"Synthetic")){
		fp->attrs[k].kind = ATTR_SYNTHETIC;
#ifdef DoDepre
	    } else if(STR_EQ(atr,"Deprecated")){
		fp->attrs[k].kind = ATTR_NONE;
#endif
	    } else {
		fatal("bad attr in field_info(index=%d,%s)\n",
		      index,atr);
	    }
	}
    }
}

void read_method_info(method_info *mp)
{
    int k,index,len;
    char *atr;

    mp->access_flag = read_u2();
    mp->name_index = read_u2();
    mp->desc_index = read_u2();
    mp->attr_count = read_u2();

    if(mp->attr_count != 0){
	mp->attrs = (attr_info *)xmalloc(sizeof(attr_info)*mp->attr_count);
	for(k = 0; k < mp->attr_count; k++){
	    index = read_u2();
	    len = read_u4();	/* discard */
	    atr = get_cp_str(current_class,index);
	    if(STR_EQ(atr,"Code")){
		mp->attrs[k].kind = ATTR_CODE;
		mp->attrs[k].p = read_code_attr();
	    } else if(STR_EQ(atr,"Exceptions")){
		mp->attrs[k].kind = ATTR_EXCEPTIONS;
		mp->attrs[k].p = read_exceptions_attr();
	    } else if(STR_EQ(atr,"Synthetic")){
		mp->attrs[k].kind = ATTR_SYNTHETIC;
#ifdef DoDepre
	    } else if(STR_EQ(atr,"Deprecated")){
		mp->attrs[k].kind = ATTR_NONE;
#endif
	    } else {
		fatal("bad attr in method_info(index=%d,%s)\n",
			index,atr);
	    }
	}
    }
}

void *read_code_attr()
{
    code_attr *cp;
    exception_info *ep;
    char *atr;
    int k,index,len;

    cp = (code_attr *)xmalloc(sizeof(code_attr));
    cp->max_stack = read_u2();
    cp->max_locals = read_u2();
    cp->code_len = read_u4();
    cp->code = (char *)xmalloc(cp->code_len);
    read_bytes(cp->code,cp->code_len);

    cp->exception_table_len = read_u2();
    if(cp->exception_table_len != 0){
	ep = (exception_info *)
	    xmalloc(sizeof(exception_info)*cp->exception_table_len);
	cp->exception_table = ep;
	for(k = 0; k < cp->exception_table_len; k++){
	    ep->start_pc = read_u2();
	    ep->end_pc = read_u2();
	    ep->handler_pc = read_u2();
	    ep->catch_type = read_u2();
	    ep++;
	}
    }
    cp->attr_count = read_u2();
    if(cp->attr_count != 0){
	cp->attrs = (attr_info *)xmalloc(sizeof(attr_info)*cp->attr_count);
	for(k = 0; k < cp->attr_count; k++){
	    index = read_u2();
	    len = read_u4();
	    atr = get_cp_str(current_class,index);
	    if(STR_EQ(atr,"LineNumberTable")){
		cp->attrs[k].kind = ATTR_LINE_NO;
		read_bytes(input_buf,len); /* skip */
		continue;
	    } else if(STR_EQ(atr,"LocalVariableTable")){
		cp->attrs[k].kind = ATTR_LOCAL_VAR;
		read_bytes(input_buf,len); /* skip */
	    } else {
		fatal("bad attr in code_attr(index=%d,%s)\n",
		      index,atr);
	    }
	}
    }
    return cp;
}

void *read_exceptions_attr()
{
    exception_attr *ep;
    int k;

    ep = (exception_attr *)xmalloc(sizeof(exception_attr));
    ep->n_exceptions = read_u2();
    if(ep->n_exceptions != 0){
	ep->exception_index_table = 
	    (int *)xmalloc(sizeof(int)*ep->n_exceptions);
	for(k = 0; k < ep->n_exceptions; k++)
	    ep->exception_index_table[k] = read_u2();
    }
    return (void *)ep;
}

int read_u4()
{
    int u,x,i;
    u = 0;
    for(i = 0; i < 4; i++){
	x = fgetc(input_fp);
	u = ((u << 8)|(0xff&x));
    }
    return u;
}

int read_u2()
{
    int u,x,i;
    u = 0;
    for(i = 0; i < 2; i++){
	x = fgetc(input_fp);
	u = ((u << 8)|(0xFF&x));
    }
    return u;
}

int read_u1()
{
    int u;
    u = fgetc(input_fp);
    return (0xFF & u);
}

void read_bytes(char *p,int nbytes)
{
    if(nbytes > INPUT_BUF_SIZE) fatal("read_bytes: too large record");
    fread(p,nbytes,1,input_fp);
}


