"""
Things to be implemented:
* Process other Attribute types: e.g. "ConstantValue"
"""

import math
import instructions
import util

CP_INFO = {7: 'Class', 9: 'Fieldref', 10: 'Methodref',
           11: 'InterfaceMethodref', 8: 'String', 3: 'Integer',
           4: 'Float', 5: 'Long', 6: 'Double', 12: 'NameAndType',
           1: 'Utf8'}  

def generate_constant(class_file):
  """
  This returns a ConstantXxxInfo object of the right type (tag defines that).
  I have tried to use a Java-like static method to generate the object in a Constant class
  but it doesn't work. I don't know why.
  
  cp_info {
      u1 tag;
      u1 info[];
  }  
  """
  offset,tag,data = class_file.read('BYTE')
  assert(tag != 0) # there seems to be a problem with tag = 0, check specs
  # select right class
  tag_name = CP_INFO[tag]
  class_ = globals()['Constant%sInfo' % tag_name] # locate class
  return class_((offset, tag), class_file) 
  
class Disassembler(util.FileReader):
  """
  For reference: http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#80959
  
  ClassFile {
    	u4 magic;
    	u2 minor_version;
    	u2 major_version;
    	u2 Constantpool_count;
    	cp_info Constantpool[Constantpool_count-1];
    	u2 access_flags;
    	u2 this_class;
    	u2 super_class;
    	u2 interfaces_count;
    	u2 interfaces[interfaces_count];
    	u2 fields_count;
    	field_info fields[fields_count];
    	u2 methods_count;
    	method_info methods[methods_count];
    	u2 attributes_count;
    	attribute_info attributes[attributes_count];
  }
  """ 
  
  def __init__(self, filename):
    """
    Read class file to build its memory representation.
    """
    
    # open files using superclass constructor
    util.FileReader.__init__(self, filename)
    
    self.class_file = {}
    self.filename = filename
    
    # class_file structure
    self.class_file['magic'] = self.read('INTEGER')
    self.class_file['minor_version'] = self.read('SHORT')
    self.class_file['major_version'] = self.read('SHORT')
    self.class_file['constant_pool_count'] = self.read('SHORT')
    self.class_file['constant_pool'] = [None] # constant_pool starts at index 1
    while len(self.class_file['constant_pool']) < self.class_file['constant_pool_count'][1]:
      constant = generate_constant(self)
      self.class_file['constant_pool'].append(constant)
      if isinstance(constant, ConstantLongInfo) or isinstance(constant, ConstantDoubleInfo):
        # Long and Double reserve another entry in the constant_pool
        self.class_file['constant_pool'].append(None)      
    self.class_file['access_flags'] = self.read('SHORT')
    self.class_file['this_class'] = self.read('SHORT')
    self.class_file['super_class'] = self.read('SHORT')
    self.class_file['interfaces_count'] = self.read('SHORT')
    self.class_file['interfaces'] = [InterfaceInfo(self) for index in range(self.class_file['interfaces_count'][1])]
    self.class_file['fields_count'] = self.read('SHORT')
    self.class_file['fields'] = [FieldInfo(self) for index in range(self.class_file['fields_count'][1])]
    self.class_file['methods_count'] = self.read('SHORT')
    self.class_file['methods'] = [MethodInfo(self) for index in range(self.class_file['methods_count'][1])]
    self.class_file['attributes_count'] = self.read('SHORT')
    self.class_file['attributes'] = [AttributeInfo(self) for index in range(self.class_file['attributes_count'][1])]

  def get_constant(self, index):
    """
    Returns constant from pool.
    """
    
    return self.class_file['constant_pool'][index]

  def get_constant_value(self, index):
    """
    Returns value of the constant from pool.
    """
    
    constant = self.class_file['constant_pool'][index]
    return constant.get_value()

  def dump_line(self, value, type, tag):
    format = util.FileReader.FORMATS_DICT[type]
    if len(value) == 2:
      offset,pure_value = value
      self._output.write(('%08X ' + format + ' ; %s\n') % (offset, pure_value, tag))
    else:
      offset,pure_value,bytes = value
      self._output.write(('%08X %s ; %s\n') % (offset, util.bytes_to_hex_str(bytes), tag))
    
  def dump_msg(self, msg):
    self._output.write('; %s\n' % msg)
    
  def dump(self):
    """
    Generate output file:
    
      00000000 CAFEBABE ; magic
      00000004 0000 ; minor_version
      ...
    """
    
    # Output file
    self._output = open(self.filename + ".bc", 'w')
    
    # Dump structure
    self.dump_line(self.class_file['magic'], 'INTEGER', 'magic')
    self.dump_line(self.class_file['minor_version'], 'SHORT', 'minor_version')
    self.dump_line(self.class_file['major_version'], 'SHORT', 'major_version')
    # Constants
    self.dump_line(self.class_file['constant_pool_count'], 'SHORT', 'constant_pool_count')
    self.dump_msg('constant_pool_start')
    # constant_pool starts at index 1
    for constant_pool_index in range(1, len(self.class_file['constant_pool'])):
      self.dump_msg('constant_pool_index = 0x%x' % constant_pool_index)
      constant = self.class_file['constant_pool'][constant_pool_index]
      if constant != None:
        constant.dump()
      else:
        # extra entry for Long and Double
        self.dump_msg('long/double extension')
    self.dump_msg('constant_pool_end')
    # Flags
    self.dump_line(self.class_file['access_flags'], 'SHORT', 'access_flags')
    self.dump_line(self.class_file['this_class'], 'SHORT', 'this_class -> "%s"' % self.get_constant_value(self.class_file['this_class'][1]))
    self.dump_line(self.class_file['super_class'], 'SHORT', 'super_class -> "%s"' % self.get_constant_value(self.class_file['super_class'][1]))
    # Interfaces
    self.dump_line(self.class_file['interfaces_count'], 'SHORT', 'interfaces_count')
    self.dump_msg('interfaces_start')
    for interface_index in range(len(self.class_file['interfaces'])):
      self.dump_msg('interface_index = 0x%x' % interface_index)
      self.class_file['interfaces'][interface_index].dump()      
    self.dump_msg('interfaces_end')
    # Fields
    self.dump_line(self.class_file['fields_count'], 'SHORT', 'fields_count')
    self.dump_msg('fields_start')
    for field_index in range(len(self.class_file['fields'])):
      self.dump_msg('field_index = 0x%x' % field_index)
      self.class_file['fields'][field_index].dump()      
    self.dump_msg('fields_end')
    # Methods
    self.dump_line(self.class_file['methods_count'], 'SHORT', 'methods_count')
    self.dump_msg('methods_start')
    for method_index in range(len(self.class_file['methods'])):
      self.dump_msg('method_index = 0x%x' % method_index)
      self.class_file['methods'][method_index].dump()      
    self.dump_msg('methods_end')
    # Attributes
    self.dump_line(self.class_file['attributes_count'], 'SHORT', 'attributes_count')
    self.dump_msg('attributes_start')
    for attribute_index in range(len(self.class_file['attributes'])):
      self.dump_msg('attribute_index = 0x%x' % attribute_index)
      self.class_file['attributes'][attribute_index].dump()      
    self.dump_msg('attributes_end')
    
    # Close file
    self._output.close()
    

class ConstantClassInfo:
  """
  ConstantClass_info {
    u1 tag;
    u2 name_index;
  }
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.name_index = class_file.read('SHORT')
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.name_index, 'SHORT', 'name_index -> "%s"' % self.get_value())

  def get_value(self):
    return self.class_file.get_constant_value(self.name_index[1])
  
class ConstantMethodrefInfo:
  """
  ConstantMethodref_info {
      u1 tag;
      u2 class_index;
      u2 name_and_type_index;
  } 
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.class_index = class_file.read('SHORT')
    self.name_and_type_index = class_file.read('SHORT')
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.class_index, 'SHORT', 'class_index -> "%s"' % self.class_file.get_constant_value(self.class_index[1]))
    self.class_file.dump_line(self.name_and_type_index, 'SHORT', 'name_and_type_index -> "%s"' % self.class_file.get_constant_value(self.name_and_type_index[1]))

class ConstantFieldrefInfo:
  """
  ConstantFieldref_info {
      u1 tag;
      u2 class_index;
      u2 name_and_type_index;
  } 
  """

  def __init__(self, tag, class_file):
    self.tag = tag
    self.class_index = class_file.read('SHORT')
    self.name_and_type_index = class_file.read('SHORT')
    self.class_file = class_file
    
  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.class_index, 'SHORT', 'class_index -> "%s"' % self.class_file.get_constant_value(self.class_index[1]))
    self.class_file.dump_line(self.name_and_type_index, 'SHORT', 'name_and_type_index -> "%s"' % self.class_file.get_constant_value(self.name_and_type_index[1]))
         
class ConstantInterfaceMethodrefInfo:
  """
  ConstantInterfaceMethodref_info {
      u1 tag;
      u2 class_index;
      u2 name_and_type_index;
  } 
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.class_index = class_file.read('SHORT')
    self.name_and_type_index = class_file.read('SHORT')
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.class_index, 'SHORT', 'class_index -> "%s"' % self.class_file.get_constant_value(self.class_index[1]))
    self.class_file.dump_line(self.name_and_type_index, 'SHORT', 'name_and_type_index -> "%s"' % self.class_file.get_constant_value(self.name_and_type_index[1]))

class ConstantStringInfo:
  """
  ConstantString_info {
      u1 tag;
      u2 string_index;
  }  
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.string_index = class_file.read('SHORT')
    self.class_file = class_file
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.string_index, 'SHORT', 'string_index -> "%s"' % self.get_value())
    
  def get_value(self):
    return util.get_printable_string(self.class_file.get_constant(self.string_index[1]).bytes[1])

class ConstantIntegerInfo:
  """
  ConstantInteger_info {
      u1 tag;
      u4 bytes;
  }
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.bytes = class_file.read('INTEGER')
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.bytes, 'INTEGER', 'bytes = %d' % self.get_value())

  def get_value(self):
    return self.bytes[1]

class ConstantFloatInfo:
  """
  ConstantFloat_info {
      u1 tag;
      u4 bytes;
  }  
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    #self.bytes = class_file.read('FLOAT')
    self.bytes = class_file.read_raw(4) # it seems that float is only reading 2 bytes instead of 4
    self.class_file = class_file
    
  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    #class_file.dump_line(self.bytes, 'FLOAT', 'bytes')
    self.class_file.dump_line((self.bytes[0], util.bytes_to_hex_str(self.bytes[1])), 'BYTES', 'bytes')
                      
  def get_value(self):
    #return self.bytes[1]
    return '<FLOAT_VALUE_SHOULD_BE_HERE>'
  
class ConstantLongInfo:
  """
    ConstantLong_info {
      u1 tag;
      u4 high_bytes;
      u4 low_bytes;
  } 
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.high_bytes = class_file.read('INTEGER')    
    self.low_bytes = class_file.read('INTEGER')
    self.class_file = class_file
    
  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.high_bytes, 'INTEGER', 'high_bytes')
    self.class_file.dump_line(self.low_bytes, 'INTEGER', 'low_bytes')

class ConstantDoubleInfo:
  """
  ConstantDouble_info {
      u1 tag;
      u4 high_bytes;
      u4 low_bytes;
  } 
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.high_bytes = class_file.read('INTEGER')    
    self.low_bytes = class_file.read('INTEGER')
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.high_bytes, 'INTEGER', 'high_bytes')
    self.class_file.dump_line(self.low_bytes, 'INTEGER', 'low_bytes')

class ConstantNameAndTypeInfo:
  """
  ConstantNameAndType_info {
      u1 tag;
      u2 name_index;
      u2 descriptor_index;
  }
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.name_index = class_file.read('SHORT')    
    self.descriptor_index = class_file.read('SHORT')
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.name_index, 'SHORT', 'name_index -> "%s"' % self.class_file.get_constant_value(self.name_index[1]))
    self.class_file.dump_line(self.descriptor_index, 'SHORT', 'descriptor_index -> "%s"' % self.class_file.get_constant_value(self.descriptor_index[1]))

  def get_value(self):
    return '%s%s' % (self.class_file.get_constant_value(self.name_index[1]), self.class_file.get_constant_value(self.descriptor_index[1]))

class ConstantUtf8Info:
  """
  ConstantUtf8_info {
      u1 tag;
      u2 length;
      u1 bytes[length];
  }
  """

  def __init__(self, tag, class_file): 
    self.tag = tag
    self.length = class_file.read('SHORT')    
    self.bytes = class_file.read_raw(self.length[1])
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.tag, 'BYTE', 'tag = %s' % CP_INFO[self.tag[1]])
    self.class_file.dump_line(self.length, 'SHORT', 'length')
    self.class_file.dump_line((self.bytes[0],util.bytes_to_hex_str(self.bytes[1])), 'BYTES', 'bytes = "%s"' % util.get_printable_string(self.bytes[1]))

  def get_value(self):
    return util.get_printable_string(self.bytes[1])
  
class FieldInfo:
  """
  field_info {
    u2 access_flags;
    u2 name_index;
    u2 descriptor_index;
    u2 attributes_count;
    attribute_info attributes[attributes_count];
  }
  """

  def __init__(self, class_file): 
    self.access_flags = class_file.read('SHORT')
    self.name_index = class_file.read('SHORT')
    self.descriptor_index = class_file.read('SHORT')
    self.attributes_count = class_file.read('SHORT')
    self.attributes = [AttributeInfo(class_file) for index in range(self.attributes_count[1])]
    self.class_file = class_file

  def dump(self):
    self.class_file.dump_line(self.access_flags, 'SHORT', 'access_flags')
    name = self.class_file.get_constant_value(self.name_index[1]) # get name_index from constant_pool
    self.class_file.dump_line(self.name_index, 'SHORT', 'name_index -> "%s"' % name)
    descriptor = self.class_file.get_constant_value(self.descriptor_index[1]) # get descriptor_index from constant_pool
    self.class_file.dump_line(self.descriptor_index, 'SHORT', 'descriptor_index -> "%s"' % descriptor)
    # Attributes
    self.class_file.dump_line(self.attributes_count, 'SHORT', 'attributes_count')
    for attribute_index in range(len(self.attributes)):
      self.class_file.dump_msg('attribute_index = 0x%x' % attribute_index)
      self.attributes[attribute_index].dump()

class AttributeInfo:
  """
  attribute_info {
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
  }
  """

  def __init__(self, class_file): 
    self.attribute_name_index = class_file.read('SHORT')
    attribute_name = class_file.get_constant(self.attribute_name_index[1]).bytes[1] # get attribute_name_index from constant_pool
    self.attribute_length = class_file.read('INTEGER')
    if attribute_name == 'Code':
      self.info = Code(class_file)
    else:
      self.info = class_file.read_raw(self.attribute_length[1])
    self.class_file = class_file

  def dump(self):
    attribute_name = self.class_file.get_constant(self.attribute_name_index[1]).bytes[1] # get attribute_name_index from constant_pool
    self.class_file.dump_line(self.attribute_name_index, 'SHORT', 'attribute_name_index = "%s"' % attribute_name)
    self.class_file.dump_line(self.attribute_length, 'INTEGER', 'attribute_length')
    if attribute_name == 'Code':
      self.info.dump()
    else:
      self.class_file.dump_line((self.info[0], util.bytes_to_hex_str(self.info[1])), 'BYTES', 'info')

class Code:
  """
  Process code instructions.  

  Code_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {
            u2 start_pc;
            u2 end_pc;
            u2 handler_pc;
            u2 catch_type;
    }	exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
  }
  """

  def __init__(self, class_file):
    self.class_file = class_file
    # 
    self.max_stack = class_file.read('SHORT')
    self.max_locals = class_file.read('SHORT')
    self.code_length = class_file.read('INTEGER')
    # code
    self.code = []
    start_offset = class_file._input.tell()
    end_offset = start_offset + self.code_length[1]
    while class_file._input.tell() < end_offset: # process code_length bytes
      instruction = Instruction(class_file, start_offset)
      self.code.append(instruction)
    #exception_table
    self.exception_table_length = class_file.read('SHORT')
    self.exceptions = [ExceptionTable(class_file) for index in range(self.exception_table_length[1])]
    # attributes
    self.attributes_count = class_file.read('SHORT')
    self.attributes = [AttributeInfo(class_file) for index in range(self.attributes_count[1])]
    
  def dump(self):
    self.class_file.dump_msg('code_start ************************************************* ')
    self.class_file.dump_line(self.max_stack, 'SHORT', 'start_pc')
    self.class_file.dump_line(self.max_locals, 'SHORT', 'max_locals')
    # code
    self.class_file.dump_line(self.code_length, 'INTEGER', 'code_length')
    for instruction in self.code:
      instruction.dump()
    # exception table
    self.class_file.dump_line(self.exception_table_length, 'SHORT', 'exception_table_length')
    for exception_index in range(len(self.exceptions)):
      self.class_file.dump_msg('exception_index = 0x%x' % exception_index)
      self.exceptions[exception_index].dump()
    # attributes
    self.class_file.dump_line(self.attributes_count, 'SHORT', 'attributes_count')
    for attribute_index in range(len(self.attributes)):
      self.class_file.dump_msg('attribute_index = 0x%x' % attribute_index)
      self.attributes[attribute_index].dump()
    self.class_file.dump_msg('code_end *************************************************** ')

class Instruction:
  """
    Reference: http://java.sun.com/docs/books/vmspec/2nd-edition/html/Instructions.doc.html
    
    There can be several types of instruction and they are all mixed
    here with different attributes so BE CAREFUL.
  """
  
  def __init__(self, class_file, start_offset):
    self.class_file = class_file
    self.offset,self.opcode,self.bytes = class_file.read('BYTE')    # opcode
    self.opname, self.length = instructions.INSTR_DICT[self.opcode] # opname, length
    if self.length == -1:
      # special cases
      assert(self.opname == 'TABLESWITCH' or self.opname == 'LOOKUPSWITCH' or self.opname == 'WIDE')
      if self.opname == 'WIDE':
        # http://java.sun.com/docs/books/vmspec/2nd-edition/html/Instructions2.doc15.html
        self.opcode2 = class_file.read('BYTE')    # opcode
        self.opname2,self.length2 = instructions.INSTR_DICT[self.opcode2[1]]
        if self.opname2 == 'IINC':
          self.index = class_file.read('INTEGER')
          self.const = class_file.read('INTEGER')
        else:
          self.index = class_file.read('INTEGER')
      else:
        # TABLESWITCH or LOOKUPSWITCH
        index = self.offset - start_offset
        pad_size = int(math.ceil((index+1)/4.0))*4-(index+1) # 4-byte aligment
        # Pad
        self.pad_bytes = class_file.read_raw(pad_size)
        # Default, low and high
        self.default = class_file.read('INTEGER')
        if self.opname == 'TABLESWITCH':
          self.low = class_file.read('SIGNEDINTEGER')
          self.high = class_file.read('SIGNEDINTEGER')
          n = self.high[1] - self.low[1] + 1;  # number of offsets
          # offset labels
          self.labels = [class_file.read('INTEGER') for offset_index in range(n)]
        elif self.opname == 'LOOKUPSWITCH':
          self.npairs = class_file.read('INTEGER')
          # match - offset pairs
          self.pairs = [(class_file.read('INTEGER'), class_file.read('INTEGER')) for npair_index in range(self.npairs[1])]
        else:
          raise StandardError(), "Unknown opname: %s" % self.opname
    else:
      if self.length == 1:
        self.operand = class_file.read_and_raw('BYTE')
      elif self.length == 2:
        self.operand = class_file.read_and_raw('SHORT')
      elif self.length == 4:
        self.operand = class_file.read_and_raw('INTEGER')
      else:
        self.operand_bytes = class_file.read_raw(self.length)
  
  def dump(self):
    opname, length = instructions.INSTR_DICT[self.opcode]   # opname, length
    if opname == 'TABLESWITCH':
      self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode) + self.pad_bytes[1])), 'BYTES', '%s + pad' % opname)
      self.class_file.dump_line(self.default, 'INTEGER', 'default')
      self.class_file.dump_line(self.low, 'SIGNEDINTEGER', 'low')
      self.class_file.dump_line(self.high, 'SIGNEDINTEGER', 'high')
      for offset_index in range(len(self.labels)):
        self.class_file.dump_line(self.labels[offset_index], 'INTEGER', 'offset_index = 0x%x' % offset_index)
    elif opname == 'LOOKUPSWITCH':
      self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode) + self.pad_bytes[1])), 'BYTES', '%s + pad' % opname)
      self.class_file.dump_line(self.default, 'INTEGER', 'default')
      self.class_file.dump_line(self.npairs, 'INTEGER', 'npairs')
      for match,offset in self.pairs:
        self.class_file.dump_line(match, 'INTEGER', 'match')
        self.class_file.dump_line(offset, 'INTEGER', 'offset')      
    elif opname == 'WIDE':
      self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode))), 'BYTES', '%s' % opname)
      self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode2[1]))), 'BYTES', '%s' % self.opname2)
      self.class_file.dump_line(self.index, 'INTEGER', 'index')
      if hasattr(self, 'const'):
        self.class_file.dump_line(self.const, 'INTEGER', 'const')      
    elif (opname.startswith('INVOKE') or opname.startswith('GETSTATIC') or opname.startswith('GETFIELD') or opname.startswith('PUTFIELD')) and length == 2:
      constant_methodref_info = self.class_file.get_constant(self.operand[1])
      constant_class_info = self.class_file.get_constant(constant_methodref_info.class_index[1])
      class_constant_utf8_info = self.class_file.get_constant(constant_class_info.name_index[1])
      name_and_type = self.class_file.get_constant(constant_methodref_info.name_and_type_index[1])
      name_constant_utf8_info = self.class_file.get_constant(name_and_type.name_index[1])
      #descriptor_constant_utf8_info = class_file.get_constant(name_and_type.descriptor_index[1])
      self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode) + self.operand[2])), 'BYTES', '%s %s.%s' % (opname, class_constant_utf8_info.bytes[1], name_constant_utf8_info.bytes[1])) 
    elif opname == 'NEW' or opname.startswith('CHECKCAST'):
      constant_class_info = self.class_file.get_constant(self.operand[1])
      class_constant_utf8_info = self.class_file.get_constant(constant_class_info.name_index[1])
      self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode) + self.operand[2])), 'BYTES', '%s %s' % (opname, class_constant_utf8_info.bytes[1]))       
    elif opname == 'LDC':
      info = self.class_file.get_constant(self.operand[1])
      if isinstance(info, ConstantInterfaceMethodrefInfo) or isinstance(info, ConstantMethodrefInfo):
        info.dump(self.class_file)
      else:
        value = info.get_value()
        if type(value) == str:
          value = '"%s"' % value # put strings between quotes
        self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode) + self.operand[2])), 'BYTES', '%s %s' % (opname, str(value))) 
    else:
      # normal cases
      if hasattr(self, 'operand'):
        self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode) + self.operand[2])), 'BYTES', '%s %s' % (opname, str(self.operand[1]))) 
      else:
        self.class_file.dump_line((self.offset, util.bytes_to_hex_str(chr(self.opcode) + self.operand_bytes[1])), 'BYTES', '%s %s' % (opname, util.bytes_to_hex_str(self.operand_bytes[1]))) 
  
class ExceptionTable:
  """
  {       
    u2 start_pc;
    u2 end_pc;
    u2 handler_pc;
    u2 catch_type;
  }  exception_table;
  """

  def __init__(self, class_file):
    self.start_pc = class_file.read('SHORT')
    self.end_pc = class_file.read('SHORT')
    self.handler_pc = class_file.read('SHORT')
    self.catch_type = class_file.read('SHORT')
    self.class_file = class_file
      
  def dump(self):
    self.class_file.dump_line(self.start_pc, 'SHORT', 'start_pc')
    self.class_file.dump_line(self.end_pc, 'SHORT', 'end_pc')
    self.class_file.dump_line(self.handler_pc, 'SHORT', 'handler_pc')
    self.class_file.dump_line(self.catch_type, 'SHORT', 'catch_type')
  
class MethodInfo:
  """
  method_info {
      u2 access_flags;
      u2 name_index;
      u2 descriptor_index;
      u2 attributes_count;
      attribute_info attributes[attributes_count];
  }
  """

  def __init__(self, class_file): 
    self.access_flags = class_file.read('SHORT')
    self.name_index = class_file.read('SHORT')
    self.descriptor_index = class_file.read('SHORT')
    self.attributes_count = class_file.read('SHORT')
    self.attributes = [AttributeInfo(class_file) for index in range(self.attributes_count[1])]
    self.class_file = class_file
    
  def dump(self):
    self.class_file.dump_line(self.access_flags, 'SHORT', 'access_flags')
    name = self.class_file.get_constant(self.name_index[1]).bytes[1] # get name_index from constant_pool
    self.class_file.dump_line(self.name_index, 'SHORT', 'name_index = "%s"' % name)
    descriptor = self.class_file.get_constant(self.descriptor_index[1]).bytes[1] # get descriptor_index from constant_pool
    self.class_file.dump_line(self.descriptor_index, 'SHORT', 'descriptor_index = "%s"' % descriptor)
    # attributes
    self.class_file.dump_line(self.attributes_count, 'SHORT', 'attributes_count')
    for attribute_index in range(len(self.attributes)):
      self.class_file.dump_msg('attribute_index = 0x%x' % attribute_index)
      self.attributes[attribute_index].dump()

class InterfaceInfo:
  """
  interface_info {
      u2 contant_pool_index;
  } 
  """

  def __init__(self, class_file): 
    self.contant_pool_index = class_file.read('SHORT')
    self.class_file = class_file
    
  def dump(self):
    self.class_file.dump_line(self.contant_pool_index, 'SHORT', 'contant_pool_index -> "%s"' % self.class_file.get_constant_value(self.contant_pool_index[1]))
      
