/*
 * $Id: jexc_trans_code.c,v 1.5 2000/09/27 04:36:51 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"

#define MAX_LABEL 1000
#define MAX_RET 100

int label_count = 0;
int label_table[MAX_LABEL];
int next_label_index = 0;

int jsr_ret_count = 0;
int jsr_ret_table[MAX_RET];

void define_label(int pc);
void sort_label_table();
int check_label(int pc);

void output_method_code(class_file *cfp,method_info *mp,FILE *fp);
char *output_xxx_code(FILE *fp,class_file *cfp, 
		      enum jopcode op,char *opname,
		      char *code_begin,char *p,int pc);

void output_methods(class_file *cfp,FILE *fp)
{
    int k;
    method_info *mp;
    
    for(k = 0; k < cfp->method_count; k++){
	mp = &cfp->methods[k];
	if(mp->cname != NULL){
	    fprintf(fp,"/* sys_class_method=%s %s.%s %s narg=%d, disp=%d */\n",
		    mp->cname, cfp->this_class_name,
		    get_cp_str(cfp,mp->name_index),
		    get_cp_str(cfp,mp->desc_index),
		    mp->n_args,
		    mp->disp);
	    continue;
	}
	    
	fprintf(fp,"DEFINE_METHOD(method_%d) /* %s.%s %s disp=%d */\n",
		mp->id,cfp->this_class_name,
		get_cp_str(cfp,mp->name_index),
		get_cp_str(cfp,mp->desc_index),
		mp->disp);

	output_method_code(cfp,mp,fp);
	
	fprintf(fp,"END_METHOD /* %d */\n",mp->id);
    }
}

void output_method_code(class_file *cfp,method_info *mp,FILE *fp)
{
    code_attr *cp;
    enum jopcode op;
    char *p,*code_begin,*code_end;
    int wide_flag;
    char opd_type;
    char *opname = NULL;
    int i,ii,pc,k;
    exception_info *ep;
    int exception_count;
    char type_c;

    cp = NULL;
    for(i = 0; i < mp->attr_count; i++){
	if(mp->attrs[i].kind == ATTR_CODE){
	    if(cp != NULL){
		error("internal class is not supported");
		return;
	    }
	    cp = (code_attr *)(mp->attrs[i].p);
	}
    }

    if(cp == NULL){
	/* if(!cfp->is_interface) fatal("empty code in method"); */
	fprintf(fp,"CALL_INTERFACE(%d)\n",mp->id);
	return;
    }
    
    /* 
     * scan for label 
     */
    label_count = 0;
    jsr_ret_count = 0;
    exception_count = cp->exception_table_len;
    if(exception_count > 0){
	for(k = 0; k < cp->exception_table_len; k++){
	    ep = &(cp->exception_table[k]);
	    define_label(ep->handler_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);
	}
	opd_type = *(opcode_infos[op]->opd_type);
	switch(opd_type){
	case 'n':
	    break;
	case 'f':	/* field, u2 */
	case 'm':	/* method, u2 */
	case 'c':	/* class, u2 */
	    p += 2;
	    break;
	case 'w':	/* constant, u1 */
	    p += 1;
	    break;
	case 'W':	/* constant, u2 */
	    p += 2;
	    break;

	case 'b':
	    p += 1;
	    break;

	case 's':
	    p += 2;
	    break;

	case 'v':
	    if(wide_flag) p += 1;
	    p += 1;	/* check wide */
	    break;

	case 'l':
	    i = *p++;
	    i = ((i << 8)|(*p++ & 0xFF));
	    define_label(pc+i);
	    break;

	case 'L':
	    i = *p++;
	    i = ((i << 8)|(*p++ & 0xFF));
	    i = ((i << 8)|(*p++ & 0xFF));
	    i = ((i << 8)|(*p++ & 0xFF));
	    define_label(pc+2+i);
	    break;

	case 'x':
	default:
	    switch(op){
	    case OPCODE_invokeinterface:  /* index(u2) n(u1) 0 */
		p += 4;
		break;
	    case OPCODE_iinc:	/* iinc v n */
		if(wide_flag) p += 1;
		p += 2;	
		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;
		define_label(def_offset+pc);
		for(k = low; k <= high; k++){
		    i = *p++;
		    i = ((i << 8)|(*p++ & 0xFF));
		    i = ((i << 8)|(*p++ & 0xFF));
		    i = ((i << 8)|(*p++ & 0xFF));
		    define_label(i+pc);
		}
		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;
		define_label(def_offset+pc);
		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));
		    define_label(ii+pc);
		}
		break;
	    }
	    case OPCODE_multianewarray: /* u2 u1 */
		p += 3;
		break;
	    default: goto bad_code;
	    }
	}
    }

    sort_label_table();

    /* 
     * generate codes 
     */
    fprintf(fp,"INIT_METHOD(%d,%d,%d)\n",cp->max_stack,cp->max_locals,
	    mp->n_args);

    if(exception_count != 0){
	fprintf(fp,"EXCEPTION_TABLE_BEGIN\n");
	for(k = 0; k < exception_count; k++){
	    ep = &(cp->exception_table[k]);
	    fprintf(fp,"EXECPTION_DEF(%d,%d,L%d,%d) /* '%s' */\n",
		    ep->start_pc,ep->end_pc, ep->handler_pc,
		    (ep->catch_type == 0)? (-1):
		    get_class(cfp,ep->catch_type)->id,
		    (ep->catch_type == 0) ? "?":
		    get_class_name(cfp,ep->catch_type));
	}
	fprintf(fp,"EXCEPTION_TABLE_END\n");
    }
    
    code_begin = cp->code;
    code_end = &(cp->code[cp->code_len]);
    for(p = code_begin; p < code_end; ){
	pc = p - code_begin;
	if(check_label(pc)) fprintf(fp,"L%d:;\n",pc);
	if(exception_count > 0) fprintf(fp,"CHECK_PC(%d);",pc);
	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 */",pc,op);
	opd_type = *(opcode_infos[op]->opd_type);
	switch(opd_type){
	case 'n':
	    fprintf(fp,"OP_%s();\n",opname);
	    break;
	case 'f':	/* field, u2 */
	    i = (*p++ & 0xFF);
	    i = ((i << 8)|(*p++ & 0xFF));
	    type_c = *get_field_desc(cfp,i);
	    if(type_c == '[') type_c = 'L';
	    fprintf(fp,"OP_%s_%c(%d,%d);",opname,type_c,
		    get_field_class(cfp,i)->id,
		    get_field(cfp,i)->disp);
	    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,"OP_%s(%d,%d,%d);",opname,
		    get_method_class(cfp,i)->id,
		    get_method(cfp,i)->disp,
		    get_method(cfp,i)->n_args);
	    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,"OP_%s(%d);",opname,get_class(cfp,i)->id);
	    fprintf(fp,"/* %d <%s> */\n",
		    i,get_class_name(cfp,i));
	    break;

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

	case 'l': /* s2 */
	    i = *p++;
	    i = ((i << 8)|(*p++ & 0xFF));
	    if(op == OPCODE_jsr){
		fprintf(fp," OP_%s(L%d,%d);/* %d */\n",
			opname,pc+i,pc,i);
		fprintf(fp,"R%d:;\n",pc);
		jsr_ret_table[jsr_ret_count++] = pc;
	    } else {
		fprintf(fp," OP_%s(L%d);/* %d */\n",
			opname,pc+i,i);
	    }
	    break;
	case 'L': /* s4 */
	    i = *p++;
	    i = ((i << 8)|(*p++ & 0xFF));
	    i = ((i << 8)|(*p++ & 0xFF));
	    i = ((i << 8)|(*p++ & 0xFF));
	    if(op == OPCODE_jsr_w){
		fprintf(fp,"OP_%s(L%d,%d);/* %d */\n",
			opname,pc+2+i,pc,i);
		fprintf(fp,"R%d:;\n",pc);
		jsr_ret_table[jsr_ret_count++] = pc;
	    } else {
		fprintf(fp,"OP_%s(L%d);/* %d */\n",
			opname,pc+2+i,i);
	    }
	    break;

	default:
	    switch(op){
	    case OPCODE_newarray:
		i = *p++;
		fprintf(fp,"OP_newarray(%d);\n",i);
		break;
	    case OPCODE_bipush:
		i = *p++;
		fprintf(fp,"OP_PUSH_INT(%d);\n",i);
		break;
	    case OPCODE_sipush:
		i = *p++;
		i = ((i << 8)|(*p++ & 0xFF));
		fprintf(fp,"OP_PUSH_INT(%d);\n",i);
		break;
	    case OPCODE_iinc:	/* iinc v n */
		i = (*p++ & 0xFF);
		if(wide_flag) i = ((i << 8)|(*p++ & 0xFF));
		ii = *p++; /* s1 */
		if(wide_flag) ii = ((ii << 8)|(*p++ & 0xFF));
		fprintf(fp,"OP_%s(%d,%d); /*v(%d) %d*/\n",
			opname,i,ii,i,ii);
		break;
	    default:
		p = output_xxx_code(fp,cfp,op,opname,code_begin,p,pc);
	    }
	}
    }

    if(jsr_ret_count > 0){
	fprintf(fp,"RET_TABLE_BEGIN\n");
	for(k = 0; k < jsr_ret_count; k++)
	    fprintf(fp,"RET_LABEL(%d,R%d)\n",
		    jsr_ret_table[k],jsr_ret_table[k]);
	fprintf(fp,"RET_TABLE_END\n");
    }

    return;

bad_code:
    fatal("op='%s', unknown operand_type='%c'\n", 
	  opcode_infos[op]->name,opd_type);
}

char *output_xxx_code(FILE *fp,class_file *cfp, 
		      enum jopcode op,char *opname,
		      char *code_begin,char *p, int pc)
{
    int i,ii;

    switch(op){
    case OPCODE_ldc:
	i = (*p++ & 0xFF);
	goto op_ldc;
    case OPCODE_ldc_w:
    case OPCODE_ldc2_w:
	i = (*p++ & 0xFF);
	i = ((i << 8)|(*p++ & 0xFF));
    op_ldc:
	switch(cfp->cp_info_table[i].tag){
	case CONSTANT_Integer:
	    fprintf(fp,"OP_PUSH_INT(0x%x); /* ldc Integer (%d) */\n",
		    cfp->cp_info_table[i].info.i,
		    cfp->cp_info_table[i].info.i);
	    break;
	case CONSTANT_Float:
	    fprintf(fp,"OP_PUSH_INT(0x%x);/* ldc Float */\n",
		    cfp->cp_info_table[i].info.i);
	    break;
	case CONSTANT_Long:
	case CONSTANT_Double:
	    fprintf(fp,"OP_PUSH_LONG(0x%x,0x%x);/* ldc Long */\n",
		    cfp->cp_info_table[i].info.L.low,
		    cfp->cp_info_table[i].info.L.high);
	    break;
	case CONSTANT_String:
	    fprintf(fp,"OP_PUSH_STRING(%d); /* ldc String */\n",
		    cfp->cp_info_table[i].info.string.id);
	    break;
	default:
	    fatal("bad ldc operand (tag=%d)",
		  cfp->cp_info_table[i].tag);
	}
	break;
	
    case OPCODE_invokeinterface:  /* index(u2) n(u1) 0 */
	i = (*p++ & 0xFF);
	i = ((i << 8)|(*p++ & 0xFF));
	ii = (*p++ & 0xFF);
	p++;	/* skip 0 */
	fprintf(fp,"OP_%s(%d,%d,%d);",opname,
		get_method_class(cfp,i)->id,
		get_method(cfp,i)->disp,ii);
	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_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,"OP_SWITCH_BEGIN(L%d); /* %s <%d,%d> */\n",
		def_offset+pc,opname,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,"CASE_GOTO(%d,L%d);\n",k,i+pc);
	}
	fprintf(fp,"OP_SWITCH_END;\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,"OP_SWITCH_BEGIN(L%d); /* %s %d */ ",
		def_offset+pc,opname,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,"CASE_GOTO(%d,L%d);\n",i,ii+pc);
	}
	fprintf(fp,"OP_SWITCH_END;\n");
	break;
    }
    case OPCODE_multianewarray: /* u2 u1 */
	i = (*p++ & 0xFF);
	i = ((i << 8)|(*p++ & 0xFF));
	ii = (*p++ & 0xFF);
	fprintf(fp,"OP_%s(%d,%d);",opname,get_class(cfp,i)->id,ii);
	fprintf(fp,"/* %d <%s> %d */\n",i,get_class_name(cfp,i),ii);
	break;
    default: break;
    }
    return p;
}

void define_label(int pc)
{
    int i;
    if(label_count >= MAX_LABEL){
	fatal("too many label (>= %d)",MAX_LABEL);
    }
    for(i = 0; i < label_count; i++)
	if(label_table[i] == pc) return;
    /* else put label table */
    label_table[label_count++] = pc;
}

void sort_label_table()
{
    int i,j,t;
    for(i = 0; i < label_count; i++){
	for(j = i+1; j < label_count; j++){
	    if(label_table[i] > label_table[j]){
		t = label_table[i];
		label_table[i] = label_table[j];
		label_table[j] = t;
	    }
	}
    }
    next_label_index = 0;
}

int check_label(int pc)
{
    if(next_label_index < label_count &&
       label_table[next_label_index] == pc){
	next_label_index++;
	return TRUE;
    }
    return FALSE;
}

/*EOF*/
