/*
 *      Interactive disassembler (IDA).
 *      Version 3.05
 *      Copyright (c) 1990-95 by Ilfak Guilfanov.
 *      ALL RIGHTS RESERVED.
 *                              FIDO:   2:5020/209
 *                              E-mail: ig@estar.msk.su
 *
 */

#include "java.hpp"
#include "ins.hpp"
#include "classfil.hpp"

//----------------------------------------------------------------------
static int LoadIndex(void)
{
  register ushort top;

  cmd.Op1.type = o_mem;
//  cmd.Op1.ref = 0;
  cmd.Op1.offb = cmd.size;
  cmd.Op1.addr = top = cmd.wid ? ua_next_word() : ua_next_byte();
  if(((cmd.Op1.dtyp == dt_qword || cmd.Op1.dtyp == dt_double) && !(++top)) ||
    top >= curSeg.DataSize) {
    if(!debugmode) return(0);
    ++cmd.Op1.ref;
  }
  return(1);
}

//----------------------------------------------------------------------
enum CIC_param {
                 C_4byte = 0,
                 C_8byte,
                 C_Field,
                 C_Method,
                 C_Interface,
                 C_Class,
                 C_Sign,
                 C_SignName,
               };

#define NORM_FIELD (HAS_CLASS_NAME | SUB_FM_NAME | SUB_SIGNATURE)
#define NORM_METOD (HAS_CLASS_NAME | SUB_FM_NAME | SUB_PARAMSIGN)

static int ConstLoad(CIC_param ctype)
{
  ConstOpis *cntopis;
  ushort i;

  cmd.Op1.type = o_cpool;
//  cmd.Op1.ref = 0;

  if(!cmd.Op1.cp_ind) goto dmpchk;  //NULL Ptr

  if((cntopis = LoadOpis(cmd.Op1.cp_ind)) == 0) goto dmpchk;

#ifdef __BORLANDC__
#if (offsetof(ConstOpis,flag) != (offsetof(ConstOpis,type) + sizeof(uchar)) \
     || (sizeof(cntopis->type) != sizeof(uchar))      \
     || (sizeof(cntopis->flag) != sizeof(uchar))      \
     || (sizeof(cmd.Op1.cp_type) < (2*sizeof(uchar))) \
     || (sizeof(ushort) != sizeof(cmd.Op1.cp_type)))
#error
#endif
#endif
  cmd.Op1.cp_type = *((ushort *)&cntopis->type);
  i = cntopis->type;

  switch(ctype) {
    case C_4byte:
      if(i == CONSTANT_Integer ||
         i == CONSTANT_Float   ||
         i == CONSTANT_String) {
load1:
        cmd.Op1.value = cntopis->value;    // for string index to Utf8
        return(1);                         // or TWO index for other
      }
      break;

    case C_8byte:
      if(i == CONSTANT_Long ||
         i == CONSTANT_Double) {
load2:
        cmd.Op1.addr = cntopis->value2;
        cmd.Op1.value = cntopis->value;    // for string index to Utf8
#ifdef __EA64__
        cmd.Op1.value = make_ulonglong(cmd.Op1.value, cmd.Op1.addr);
#endif        
        return 1;
      }
      break;

    case C_Field:
      if(i != CONSTANT_Fieldref) break;
      if((cntopis->flag & NORM_FIELD) != NORM_FIELD) goto wrnret;
loadref2:
      cmd.xtrn_ip = cntopis->ref_ip;
      goto load2; //load 3 ind.

    case C_Interface:
      if(i != CONSTANT_InterfaceMethodref) break;
      goto methodchk;
    case C_Method:
      if(i != CONSTANT_Methodref) break;
methodchk:
      if((cntopis->flag & NORM_METOD) != NORM_METOD) goto wrnret;
      goto loadref2; //load 3 ind. & xtrn_ref

    case C_Class:
      if(i != CONSTANT_Class) break;
      if(!(cntopis->flag & HAS_CLASS_NAME)) goto wrnret;
      cmd.Op1.addr = 0x10001ul * (ushort)fmt_fullname;
loadref1:
      cmd.xtrn_ip = cntopis->ref_ip;
      goto load1; //load 1 ind.

    case C_Sign:
      if(i != CONSTANT_Class) break;
      if(!(cntopis->flag & HAS_SIGNATURE)) goto wrnret;
      cmd.Op1.addr = ((ulong)fmt_sign << 16) | (ushort)fmt_signclass;
      goto loadref1; //load 1 ind.

    case C_SignName:
      if(i != CONSTANT_Class) break;
      if(!(cntopis->flag & (HAS_SIGNATURE | HAS_CLASS_NAME))) goto wrnret;
      cmd.Op1.addr = ((ulong)fmt_cast << 16) | (ushort)
                                        ((cntopis->flag & HAS_CLASS_NAME) ?
                                               fmt_fullname : fmt_signclass);
      goto loadref1; //load 1 ind.

    default:
      {
        static char diastr[] = "Illegal CIC call (%x)\n";
        warning(diastr, ctype);
      }
      return(0);
  }
dmpchk:
  if(!debugmode) return(0);
  ++cmd.Op1.ref;
wrnret:
  ++cmd.Op1.ref;
  cmd.Op1.addr_shorts.low = cmd.Op1.cp_ind;    // for dmp out
  return(1);
}
//----------------------------------------------------------------------

int ana(void) {
  CIC_param ctype;
  register segment_t *s = getMySeg(cmd.ea);

  if(s->type != SEG_CODE || cmd.ip >= curSeg.CodeSize) {
    static char illc[] = "Can't decode non-code fragment!";
    warning(illc);
    return(0);
  }

  cmd.Op1.dtyp = dt_void;
  cmd.wid = cmd.swit = 0;
//
  cmd.Op1.ref = 0;

  if((cmd.itype = ua_next_byte()) == j_wide) {
    if((cmd.itype = ua_next_byte()) == j_iinc           ||
       (cmd.itype >= j_iload && cmd.itype <= j_aload)   ||
       (cmd.itype >= j_istore && cmd.itype <= j_astore) ||
       cmd.itype == j_ret) cmd.wid = 1; //_w
    else if(!debugmode) return(0);
          else {
            cmd.size = 1;
            cmd.itype = j_wide;
         }
  }

  if(cmd.itype >= j_lastnorm) {
    if(!debugmode) return(0);
    if(cmd.itype < j_quick_last) {
      static uchar redefcmd[j_quick_last - j_lastnorm] = {
            j_ldc1,                   //j_ldc_quick
            j_ldc2,                   //j_ldc2_quick
            j_ldc2w,                  //j_ldc2w_quick
            j_getfield,               //j_getfield_quick
            j_putfield,               //j_putfield_quick
            j_getfield,               //j_getfield2_quick
            j_putfield,               //j_putfield2_quick
            j_getstatic,              //j_getstatic_quick
            j_putstatic,              //j_putstatic_quick
            j_getstatic,              //j_getstatic2_quick
            j_putstatic,              //j_putstatic2_quick
            j_invokevirtual,          //j_invokevirtual_quick
            j_invokenonvirtual,       //j_invokenonvirtual_quick
            j_a_invokesuper,          //j_invokesuper_quick
            j_invokestatic,           //j_invokestatic_quick
            j_invokeinterface,        //j_invokeinterface_quick
            j_a_invokevirtualobject,  //j_invokevirtualobject_quick
            j_a_invokeignored,        //j_invokeignored_quick
            j_new,                    //j_new_quick
            j_anewarray,              //j_anewarray_quick
            j_multianewarray,         //j_multianewarray_quick
            j_checkcast,              //j_checkcast_quick
            j_instanceof,             //j_instanceof_quick
            j_invokevirtual,          //j_invokevirtual_quick_w
            j_getfield,               //j_getfield_quick_w
            j_putfield                //j_putfield_quick_w
        };

      cmd.wid = 2; //_quick;
      switch(cmd.itype) {
        case j_getstatic2_quick:
        case j_putstatic2_quick:
        case j_getfield2_quick:
        case j_putfield2_quick:
          cmd.wid = 3;  //2_quick
          break;
        case j_invokevirtual_quick_w:
        case j_getfield_quick_w:
        case j_putfield_quick_w:
          cmd.wid = 4;  //_quick_w
          break;
        default:
          break;
      }
      cmd.itype = redefcmd[cmd.itype - j_lastnorm];
    } else if(cmd.itype < j_software) return(0);
           else cmd.itype -= (j_software - j_a_software);
  }
//---
  switch(cmd.itype) {
    default:
      {
        register ushort refs, ref2f;

        if(cmd.itype >= j_iload_0 && cmd.itype <= j_aload_3) {
          refs = (cmd.itype - j_iload_0) % 4;
          ref2f = (cmd.itype - j_iload_0) / 4;
          ref2f = (ref2f == ((j_lload_0 - j_iload_0) / 4) ||
                   ref2f == ((j_dload_0 - j_iload_0) / 4));
          goto refer;
        }
        if(cmd.itype >= j_istore_0 && cmd.itype <= j_astore_3) {
          refs = (cmd.itype - j_istore_0) % 4;
          ref2f = (cmd.itype - j_istore_0) / 4;
          ref2f = (ref2f == ((j_lstore_0 - j_istore_0) / 4) ||
                   ref2f == ((j_dstore_0 - j_istore_0) / 4));
refer:
          cmd.Op1.addr = curSeg.DataBase + refs;
          cmd.Op1.ref = ref2f + 1;
          if((refs + ref2f) >= curSeg.DataSize) cmd.Op1.ref |= 0x80;
          break;
        }
      } // end refs/refx
      if(cmd.itype < j_ifeq || cmd.itype > j_jsr) break;
    case j_ifnull:
    case j_ifnonnull:
      cmd.Op1.addr = (short)ua_next_word();
b_near:
      cmd.Op1.type = o_near;
      cmd.Op1.offb = 1;
      cmd.Op1.addr += cmd.ip;
//      cmd.Op1.ref = 0;
      if(cmd.Op1.addr >= curSeg.CodeSize) {
        if(!debugmode) return(0);
        else ++cmd.Op1.ref;
      }
      break;

    case j_goto_w:
    case j_jsr_w:
      cmd.Op1.addr = ua_next_long();
      goto b_near;

    case j_xxxunusedxxx:
      if(!debugmode) return(0);
      break;

    case j_bipush:
      cmd.Op1.dtyp = dt_byte;
      cmd.Op1.value = (char)ua_next_byte();
      goto setdat;
    case j_sipush:
      cmd.Op1.dtyp = dt_word;
      cmd.Op1.value = (short)ua_next_word();
setdat:
      cmd.Op1.type = o_imm;
//      cmd.Op1.ref = 0;
      cmd.Op1.offb = 1;
      break;

    case j_ldc1:
      cmd.Op1.cp_ind = ua_next_byte();
      ctype = C_4byte;
      goto constchk;
    case j_ldc2:
      ctype = C_4byte;
      goto const2w;
    case j_ldc2w:
      ctype = C_8byte;
const2w:
      cmd.Op1.cp_ind = ua_next_word();
constchk:
      if(!ConstLoad(ctype)) return(0);
      break;

    case j_getstatic:
    case j_putstatic:
    case j_getfield:
    case j_putfield:
      if(cmd.wid > 1) {     //_quick form
        cmd.Op1.type = o_imm;
        cmd.Op1.ref = 2;        //#data
        cmd.Op1.offb = 1;
        if(cmd.wid == 4) { //???
          cmd.Op1.dtyp = dt_word;
          cmd.Op1.value = ua_next_word();
        } else {
          cmd.Op1.dtyp = dt_byte;
          cmd.Op1.value = ua_next_byte();
          ++cmd.size;           // SKIP
        }
        break;
      }
      ctype = C_Field;
      goto const2w;

    case j_new:
      ctype = C_Class;
      goto const2w;

    case j_anewarray:
//\\ ?/
    case j_checkcast:
    case j_instanceof:
      ctype = C_SignName;
      goto const2w;

    case j_a_invokesuper:
    case j_a_invokeignored:
      goto fictarg;
    case j_invokevirtual:
    case j_a_invokevirtualobject:
      cmd.Op2.dtyp = dt_void;
      if(cmd.wid > 1) {
        if(cmd.wid == 4) {
fictarg:
          cmd.Op1.value = ua_next_word(); //???
          cmd.Op1.dtyp = dt_word;
        } else {
          cmd.Op2.type = o_imm;
          cmd.Op1.ref = 2;        //#data
          cmd.Op1.dtyp = cmd.Op2.dtyp = dt_byte;
          cmd.Op1.value = ua_next_byte();
          cmd.Op2.offb = 2;
          cmd.Op2.value = ua_next_byte();
        }
        cmd.Op1.offb = 1;
        cmd.Op1.type = o_imm;
        cmd.Op1.ref = 2;        //#data
        break;
      }
    case j_invokenonvirtual:
    case j_invokestatic:
      ctype = C_Method;
      goto const2w;
    case j_invokeinterface:
      ctype = C_Interface;
      cmd.Op1.cp_ind = ua_next_word();
      cmd.Op2.type = o_imm;
      cmd.Op2.ref = 1;          //not sign
      cmd.Op2.dtyp = dt_byte;
      cmd.Op2.value = ua_next_byte();
      if(cmd.wid > 1) {
        cmd.Op3.type = o_imm;
        cmd.Op3.ref = 2;        //#data
        cmd.Op3.value = ua_next_byte();
        cmd.Op3.offb = 4;
        cmd.Op3.dtyp = dt_byte;
      } else {
        ++cmd.size;  //reserved
        cmd.Op3.dtyp = dt_void;
      }
      goto constchk;

    case j_multianewarray:
      cmd.Op1.cp_ind = ua_next_word();
      cmd.Op2.type = o_imm;
      cmd.Op2.ref = 1;         // not sign
      cmd.Op2.dtyp = dt_byte;
      if((cmd.Op2.value = ua_next_byte()) == 0 && !debugmode) return(0);
      ctype = C_Sign;
      goto constchk;

    case j_iinc:
    case j_iload:
    case j_istore:
      cmd.Op1.dtyp = dt_dword;
      goto memref;
    case j_lload:
    case j_lstore:
      cmd.Op1.dtyp = dt_qword;
      goto memref;
    case j_fload:
    case j_fstore:
      cmd.Op1.dtyp = dt_float;
      goto memref;
    case j_dload:
    case j_dstore:
      cmd.Op1.dtyp = dt_double;
      goto memref;
    case j_aload:
    case j_astore:
      cmd.Op1.dtyp = dt_string;
      goto memref;
    case j_ret:
      cmd.Op1.dtyp = dt_code;
memref:
      if(!LoadIndex()) return(0);
      if(cmd.itype == j_iinc) {
        cmd.Op2.type = o_imm;
        cmd.Op2.ref = 0;
        cmd.Op2.offb = cmd.size;
//\\??? Это надо???
        if(cmd.wid) {
          cmd.Op2.dtyp = dt_word;
          cmd.Op2.value = (short)ua_next_word();
        } else {
          cmd.Op2.dtyp = dt_byte;
          cmd.Op2.value = (char)ua_next_byte();
        }
      }
      break;

    case j_tableswitch:
    case j_lookupswitch:
      {
        long count;
        register ulong top;

        cmd.swit = 1;
        for(top = (4  - ((cmd.ip + cmd.size) % 4)) & 3; top; top--)
          if(ua_next_byte()) if(!debugmode) return(0);
                            else cmd.swit |= 0100;
        cmd.Op3.type = o_near;
        cmd.Op3.offb = cmd.size;
        cmd.Op3.addr = ua_next_long();
        cmd.Op3.addr += cmd.ip;
        cmd.Op3.ref = 0;

        if(cmd.Op3.addr >= curSeg.CodeSize) {
          if(!debugmode) return(0);
          else ++cmd.Op3.ref;
        }

        cmd.Op1.type = o_imm;   //lokupswitch for user manipulation...
        cmd.Op1.dtyp = dt_dword;
//        cmd.Op1.ref =  0;

        count = ua_next_long();
        if(cmd.itype == j_tableswitch) {
          cmd.Op2.type = o_imm;
          cmd.Op2.ref = 0;
          cmd.Op2.dtyp = dt_dword;
          cmd.Op1.value = count;
          count = ((cmd.Op2.value = ua_next_long()) - count + 1);
        }
        cmd.Op3.value = count;
        cmd.Op2.addr = cmd.ip + cmd.size;
        top = curSeg.CodeSize - cmd.ip;
        while(count--) {
          if(cmd.itype == j_lookupswitch) ua_next_long(); // skip pairs;
          if((cmd.ip + ua_next_long()) >= curSeg.CodeSize) {
            if(!debugmode) return(0);
            else           cmd.swit |= 0200;
          }
          if((ulong)cmd.size >= top) return(0);
        }
      }
      break;

    case j_newarray:
      cmd.Op1.type = o_array;       // type!
      cmd.Op1.offb = 1;
//      cmd.Op1.ref = 0;
      if((cmd.Op1.cp_type = ua_next_byte()) < T_BOOLEAN ||
         (uchar)cmd.Op1.cp_type > T_LONG) {

        if(!debugmode) return(0);
        ++cmd.Op1.ref;
      }
      break;

  } //end switch

  return(cmd.size);
}
//----------------------------------------------------------------------
bool can_have_type(op_t &x)
{
  if(x.type == o_cpool) return((uchar)x.cp_type == CONSTANT_Integer ||
                               (uchar)x.cp_type == CONSTANT_Long);
  return(x.type == o_imm);
}
//----------------------------------------------------------------------
