/*
 * $Id: jexc_dump.c,v 1.4 2000/09/06 13:33:55 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"

void print_code(FILE *fp,code_attr *cp, class_file *cfp);

void dump_class_file(FILE *fp,class_file *cfp)
{
    int k,i;

    fprintf(fp,"--- <constant pool: cp_count=%d > ---\n",cfp->cp_count);
    for(k = 1; k < cfp->cp_count; k++){
	fprintf(fp,"%5d:",k);
	print_cp_info(fp,&cfp->cp_info_table[k]);
	fprintf(fp,"\n");
    }

    fprintf(fp,"<access_flag=0x%x>\n",cfp->access_flag);
    fprintf(fp,"<this_class: %d, '%s'>\n",cfp->this_class,
	    get_class_name(cfp,cfp->this_class));
    fprintf(fp,"<super_class: %d, '%s'>\n",cfp->super_class,
	    get_class_name(cfp,cfp->super_class));

    fprintf(fp,"<interface: interface_count=%d>\n",cfp->interface_count);
    for(k = 0; k < cfp->interface_count; k++){
	fprintf(fp,"%5d:%d, '%s'\n",k,cfp->interfaces[k],
		get_class_name(cfp,cfp->interfaces[k]));
    }

    fprintf(fp,"--- <field: field_count=%d> ---\n",cfp->field_count);
    for(k = 0; k < cfp->field_count; k++){
	fprintf(fp,"%5d:",k);
	print_field_info(fp,&cfp->fields[k],cfp);
	fprintf(fp,"\n");
    }

    fprintf(fp,"--- <method: method_count=%d> --- \n",cfp->method_count);
    for(k = 0; k < cfp->method_count; k++){
	fprintf(fp,"%5d:",k);
	print_method_info(stdout,&cfp->methods[k],cfp);
	fprintf(fp,"\n");
	for(i = 0; i < cfp->methods[k].attr_count; i++){
	    fprintf(fp,"%s:\n",attr_name(cfp->methods[k].attrs[i].kind));
	    if(cfp->methods[k].attrs[i].kind == ATTR_CODE){
		print_code_attr(fp,(code_attr *)(cfp->methods[k].attrs[i].p),
				cfp);
	    }
	}
    }
    
    fprintf(fp,"--- <attr: attr_count=%d > --- \n",cfp->attr_count);
    for(k = 0; k < cfp->attr_count; k++){
	fprintf(fp,"%5d: %s\n",k,attr_name(cfp->attrs[k].kind));
    }
}

void print_cp_info(FILE *fp,cp_info *cp)
{
    switch(cp->tag){
    case CONSTANT_utf8:
	fprintf(fp,"CONSTANT_utf8(len=%d,\"%s\")",
		cp->info.utf8.len,cp->info.utf8.str);
	break;
    case CONSTANT_Integer:
	fprintf(fp,"CONSTANT_Integer(%d)",cp->info.i);
	break;
    case CONSTANT_Float:
	fprintf(fp,"CONSTANT_Float(0x%x)",cp->info.i);
	break;
    case CONSTANT_Long:
	fprintf(fp,"CONSTANT_Long(0x%08x:0x%08x)",
		cp->info.L.high,cp->info.L.low);
	break;
    case CONSTANT_Double:
	fprintf(fp,"CONSTANT_Double(%g)",cp->info.d);
	break;
    case CONSTANT_Class:
	fprintf(fp,"CONSTANT_Class(index=%d)",cp->info.class.name_index);
	break;
    case CONSTANT_String:
	fprintf(fp,"CONSTANT_String(index=%d)",cp->info.string.string_index);
	break;
    case CONSTANT_Fieldref:
	fprintf(fp,"CONSTANT_Fieldref(class_index=%d,name_and_type=%d)",
		cp->info.field.class_index,cp->info.field.name_index);
	break;
    case CONSTANT_Methodref:
	fprintf(fp,"CONSTANT_Methodref(class_index=%d,name_and_type=%d)",
		cp->info.method.class_index,cp->info.method.name_index);
	break;
    case CONSTANT_InterfaceMethodref:
	fprintf(fp,"CONSTANT_InterfMethodref(class_index=%d,name_and_type=%d)",
		cp->info.method.class_index,cp->info.method.name_index);
	break;
    case CONSTANT_NameAndType:
	fprintf(fp,"CONSTANT_NameAndType(name_index=%d,desc_index=%d)",
		cp->info.name.name_index,cp->info.name.desc_index);
	break;
    default:
	fprintf(fp,"CONSTANT_???");
    }
}

void print_field_info(FILE *fp,field_info *ip,class_file *cfp)
{
    int k;

    fprintf(fp,"<acc=0x%x,name(%d)='%s',desc(%d)='%s'",
	    ip->access_flag,ip->name_index,get_cp_str(cfp,ip->name_index),
	    ip->desc_index,get_cp_str(cfp,ip->desc_index));
    for(k = 0; k <ip->attr_count; k++){
	fprintf(fp,",%s",attr_name(ip->attrs[k].kind));
	if(ip->attrs[k].kind == ATTR_CONST_VAL){
	    print_cp_info(fp,&cfp->cp_info_table[ip->attrs[k].index]);
	}
    }
    fprintf(fp,">");
}

void print_method_info(FILE *fp,method_info *mp,class_file *cfp)
{
    fprintf(fp,"<acc=0x%x,name(%d)='%s',desc(%d)='%s'>",
	    mp->access_flag,mp->name_index,get_cp_str(cfp,mp->name_index),
	    mp->desc_index,get_cp_str(cfp,mp->desc_index));
}

void print_code_attr(FILE *fp,code_attr *cp, class_file *cfp)
{
    int k;
    exception_info *ep;

    fprintf(fp,"\tmax_stack=%d, max_locals=%d, len=%d\n",
	    cp->max_stack,cp->max_locals,cp->code_len);
    
    print_code(fp,cp,cfp);

    /* print attribute for code */
    if(cp->exception_table_len != 0){
	fprintf(fp,"ExceptionTable(%d):\n",cp->exception_table_len);
	for(k = 0; k < cp->exception_table_len; k++){
	    ep = &(cp->exception_table[k]);
	    fprintf(fp,"%5d %5d %5d %d <'%s'>\n",
		    ep->start_pc,ep->end_pc, ep->handler_pc,
		    ep->catch_type,
		    ep->catch_type == 0 ? "?" : 
		    get_class_name(cfp,ep->catch_type));
	}
    }
}

void print_code(FILE *fp,code_attr *cp, class_file *cfp)
{
    enum jopcode op;
    char *p,*code_begin,*code_end;
    int wide_flag;
    char opd_type,*opname;
    int i,ii,pc;

    code_begin = cp->code;
    code_end = &(cp->code[cp->code_len]);
    for(p = code_begin; p < code_end; ){
	pc = p - code_begin;
	op = ((*p++)&0xFF);
	wide_flag = FALSE;
	if(op == OPCODE_wide){
	    wide_flag = TRUE;
	    op = ((*p++)&0xFF);
	}
	opname = opcode_infos[op]->name;
	fprintf(fp,"%4d: %02x %s",pc,op,opname);
	fflush(fp);
	opd_type = *(opcode_infos[op]->opd_type);
	switch(opd_type){
	case 'n':
	    fprintf(fp,"\n");
	    break;
	case 'f':	/* field, u2 */
	    i = (*p++ & 0xFF);
	    i = ((i << 8)|(*p++ & 0xFF));
	    fprintf(fp," %d [%s <%s,%s>]\n",
		    i,get_field_class_name(cfp,i),
		    get_field_name(cfp,i),
		    get_field_desc(cfp,i));
	    break;
	case 'm':	/* method, u2 */
	    i = (*p++ & 0xFF);
	    i = ((i << 8)|(*p++ & 0xFF));
	    fprintf(fp," %d [%s <%s,%s>]\n",
		    i,get_method_class_name(cfp,i),
		    get_method_name(cfp,i),
		    get_method_desc(cfp,i));
	    break;
	case 'c':	/* class, u2 */
	    i = (*p++ & 0xFF);
	    i = ((i << 8)|(*p++ & 0xFF));
	    fprintf(fp," %d <%s>\n",
		    i,get_class_name(cfp,i));
	    break;
	case 'w':	/* constant, u1 */
	    i = (*p++ & 0xFF);
	    fprintf(fp," %d [",i);
	    print_cp_info(fp,&cfp->cp_info_table[i]);
	    fprintf(fp,"]\n");
	    break;
	case 'W':	/* constant, u2 */
	    i = (*p++ & 0xFF);
	    i = ((i << 8)|(*p++ & 0xFF));
	    fprintf(fp," %d [",i);
	    print_cp_info(fp,&cfp->cp_info_table[i]);
	    fprintf(fp,"]\n");
	    break;

	case 'b':
	    i = (*p++ & 0xFF);
	    fprintf(fp," 0x%x\n",i);
	    break;

	case 's':
	    i = (*p++ & 0xFF);
	    i = ((i << 8)|(*p++ & 0xFF));
	    fprintf(fp," 0x%x\n",i);
	    break;

	case 'v':
	    i = (*p++ & 0xFF);
	    if(wide_flag) i = ((i << 8)|(*p++ & 0xFF));
	    fprintf(fp," v(%d)\n",i);
	    break;

	case 'l':
	    i = *p++;
	    i = ((i << 8)|(*p++ & 0xFF));
	    fprintf(fp," %d <%d>\n",i,pc+i);
	    break;

	case 'L':
	    i = *p++;
	    i = ((i << 8)|(*p++ & 0xFF));
	    i = ((i << 8)|(*p++ & 0xFF));
	    i = ((i << 8)|(*p++ & 0xFF));
	    fprintf(fp," %d <%d>\n",i,pc+2+i);
	    break;
	case 'x':
	    switch(op){
	    case OPCODE_invokeinterface:  /* index(u2) n(u1) 0 */
		i = (*p++ & 0xFF);
		i = ((i << 8)|(*p++ & 0xFF));
		ii = (*p++ & 0xFF);
		p++;	/* skip 0 */
		fprintf(fp," %d [%s <%s,%s>] %d\n",
			i,get_method_class_name(cfp,i),
			get_method_name(cfp,i),
			get_method_desc(cfp,i),ii);
		break;
	    case OPCODE_iinc:	/* iinc v n */
		i = (*p++ & 0xFF);
		if(wide_flag) i = ((i << 8)|(*p++ & 0xFF));
		ii = *p++;
		if(wide_flag) ii = ((ii << 8)|(*p++ & 0xFF));
		fprintf(fp," v(%d) %d\n",i,ii);
		break;
	    case OPCODE_tableswitch:
	    {
		int low,high,def_offset,k;
		p = code_begin+((p-code_begin+3)&~3);
		i = *p++;
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
	        def_offset = i;
		i = *p++;
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
		low = i;
		i = *p++;
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
		high = i;
		fprintf(fp," %d <%d,%d> [",pc+def_offset,low,high);
		for(k = low; k <= high; k++){
		    i = *p++;
		    i = ((i << 8)|(*p++ & 0xFF));
		    i = ((i << 8)|(*p++ & 0xFF));
		    i = ((i << 8)|(*p++ & 0xFF));
		    fprintf(fp," %d",pc+i);
		}
		fprintf(fp,"]\n");
		break;
	    }
	    case OPCODE_lookupswitch:
	    {
		int n,def_offset,k;
		p = code_begin+((p-code_begin+3)&~3);
		i = *p++;
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
	        def_offset = i;
		i = *p++;
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
		i = ((i << 8)|(*p++ & 0xFF));
		n = i;
		fprintf(fp," %d %d [",pc+def_offset,n);
		for(k = 0; k < n; k++){
		    i = *p++;
		    i = ((i << 8)|(*p++ & 0xFF));
		    i = ((i << 8)|(*p++ & 0xFF));
		    i = ((i << 8)|(*p++ & 0xFF));
		    ii = *p++;
		    ii = ((ii << 8)|(*p++ & 0xFF));
		    ii = ((ii << 8)|(*p++ & 0xFF));
		    ii = ((ii << 8)|(*p++ & 0xFF));
		    fprintf(fp," <%d %d>",i,pc+ii);
		}
		fprintf(fp,"]\n");
		break;
	    }
	    case OPCODE_multianewarray: /* u2 u1 */
		i = (*p++ & 0xFF);
		i = ((i << 8)|(*p++ & 0xFF));
		ii = (*p++ & 0xFF);
		fprintf(fp,"%d <%s> %d\n",i,get_class_name(cfp,i),ii);
		break;
	    default: goto err;
	    }
	    break;
	err:
	default:
	    fprintf(stderr,"op='%s', unknown operand_type='%c'\n",
		    opname,opd_type);
	    exit(1);
	}
    }


}

