# coding=utf-8

import abc
import ctypes
import struct

from runtime.thread import Frame, LocalVars
from runtime.jclass import ClassLoader, JString, JDouble, JLong, JClass, JFloat, JInteger
from runtime.jobject import JObject, JArray, JRef
from base.utils import print_utils, common_utils, error_handler
from interpreter.code_parser import CodeParser
from jthread.jthread import JThread

'''
以下指令没有测试
wide
dup2
dup_x1
dup_x2
dup2_x1
dup2_x2
pop2
if_xxx
swap
'''

'''
未实现指令
jsr
ret
invokeinterface	
monitorenter
monitorexit
multianewarray
jsr_w
breakpoint
impdep1
impdep2
'''


class InsUtils(object):
    TYPE_INT = 1
    TYPE_FLOAT = 2
    TYPE_LONG = 3
    TYPE_DOUBLE = 4
    TYPE_REF = 5
    TYPE_ARRAY = 6
    TYPE_UNKNOWN = -1

    @staticmethod
    def get_type_by_descriptor(desc):
        if desc == 'I' or desc == 'B' or desc == 'C' or desc == 'S' or desc == 'Z':
            return InsUtils.TYPE_INT
        elif desc == 'F':
            return InsUtils.TYPE_FLOAT
        elif desc == 'J':
            return InsUtils.TYPE_LONG
        elif desc == 'D':
            return InsUtils.TYPE_DOUBLE
        elif desc[0] == 'L':
            return InsUtils.TYPE_REF
        elif desc[0] == '[':
            return InsUtils.TYPE_ARRAY
        else:
            return InsUtils.TYPE_UNKNOWN

    @staticmethod
    def check_ref_null(ref):
        if JRef.check_null(ref):
            error_handler.rise_null_point_error()


class Instruction(object):
    code = None  # 字节码
    name = None  # 助记符

    def read_operands(self, code_parser):
        pass

    def execute_wrapper(self, frame):
        hex_code = '%x' % self.code
        print_utils.print_jvm_status('execute ins: ' + self.name + '  ' + hex_code + '   ' + str(frame) + '\n')
        self.execute(frame)

    @abc.abstractmethod
    def execute(self, frame):
        pass


# 不是真正的指令集,但是有和指令差不多的功能
class INNER_INS(Instruction):
    code = -1
    name = 'inner_ins'


class INNER_INVOKE_C_INIT(INNER_INS):
    def __init__(self, jclass):
        super(INNER_INVOKE_C_INIT, self).__init__()
        self.jclass = jclass

    def execute(self, frame):
        method = None
        for m in self.jclass.methods:
            if m.name == '<clinit>':
                method = m
                break
        if method is not None:
            n_frame = Frame(frame.thread, method)
            frame.thread.add_frame(n_frame)
            frame.pc = frame.thread.pc


class ACONST_NULL(Instruction):
    code = 0x1
    name = 'aconst_null'

    def execute(self, frame):
        frame.operand_stack.push(None)


class ALOAD(Instruction):
    code = 0x19
    name = 'aload'

    def __init__(self):
        super(ALOAD, self).__init__()
        self.index = -1

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def execute(self, frame):
        ref = frame.local_vars.get_ref(self.index)
        frame.operand_stack.push_ref(ref)


class ALOAD_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        ref = frame.local_vars.get_ref(self.get_index())
        frame.operand_stack.push_ref(ref)


class ALOAD_0(ALOAD_N):
    code = 0x2a
    name = 'aload_0'

    def get_index(self):
        return 0


class ALOAD_1(ALOAD_N):
    code = 0x2b
    name = 'aload_1'

    def get_index(self):
        return 1


class ALOAD_2(ALOAD_N):
    code = 0x2c
    name = 'aload_2'

    def get_index(self):
        return 2


class ALOAD_3(ALOAD_N):
    code = 0x2d
    name = 'aload_3'

    def get_index(self):
        return 3


class ARETURN(Instruction):
    code = 0xb0
    name = 'areturn'

    def execute(self, frame):
        frame.thread.pop_frame()
        r_value = frame.operand_stack.pop_ref()
        c_frame = frame.thread.top_frame()
        c_frame.operand_stack.push_ref(r_value)


class ASTORE(Instruction):
    code = 0x3a
    name = 'astore'

    def __init__(self):
        super(ASTORE, self).__init__()
        self.index = -1

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def execute(self, frame):
        ref = frame.operand_stack.pop_ref()
        frame.local_vars.add_ref(self.index, ref)


class ASTORE_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        ref = frame.operand_stack.pop_ref()
        frame.local_vars.add_ref(self.get_index(), ref)


class ASTORE_0(ASTORE_N):
    code = 0x4b
    name = 'astore_0'

    def get_index(self):
        return 0


class ASTORE_1(ASTORE_N):
    code = 0x4c
    name = 'astore_1'

    def get_index(self):
        return 1


class ASTORE_2(ASTORE_N):
    code = 0x4d
    name = 'astore_2'

    def get_index(self):
        return 2


class ASTORE_3(ASTORE_N):
    code = 0x4e
    name = 'astore_3'

    def get_index(self):
        return 3


class BIPUSH(Instruction):
    code = 0x10
    name = 'bipush'

    def __init__(self):
        super(BIPUSH, self).__init__()
        self.byte = 0

    def read_operands(self, code_parser):
        self.byte = code_parser.read_op()

    def execute(self, frame):
        frame.operand_stack.push_int(ctypes.c_byte(self.byte).value)


class DUP(Instruction):
    code = 0x59
    name = 'dup'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.top()
        if isinstance(value, JRef):
            value = value.clone()
        operand_stack.push(value)


class DUP_X1(Instruction):
    code = 0x5a
    name = 'dup_x1'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value1 = operand_stack.top()
        value2 = operand_stack.top(2)
        if isinstance(value1, JRef):
            value1 = value1.clone()
        if isinstance(value2, JRef):
            value1 = value2.clone()
        operand_stack.push(value2)
        operand_stack.push(value1)


class DUP_X2(Instruction):
    code = 0x5b
    name = 'dup_x2'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value1 = operand_stack.top()
        value2 = operand_stack.top(2)
        if operand_stack.is_top_type1(2):
            if isinstance(value1, JRef):
                value1 = value1.clone()
            operand_stack.push(value2)
            operand_stack.push(value1)
        else:
            value3 = operand_stack.top(3)
            if isinstance(value1, JRef):
                value1 = value1.clone()
            if isinstance(value2, JRef):
                value1 = value2.clone()
            if isinstance(value3, JRef):
                value1 = value3.clone()
            operand_stack.push(value3)
            operand_stack.push(value2)
            operand_stack.push(value1)


class DUP2(Instruction):
    code = 0x5c
    name = 'dup2'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.top()
        if operand_stack.is_top_type1():
            operand_stack.push(value)
            return

        value2 = operand_stack.top(2)
        if isinstance(value, JRef):
            value = value.clone()
        if isinstance(value2, JRef):
            value = value.clone()
        operand_stack.push(value2)
        operand_stack.push(value)


class DUP2_X1(Instruction):
    code = 0x5d
    name = 'dup2_x1'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value1 = operand_stack.top()
        value2 = operand_stack.top(2)
        if operand_stack.is_top_type1():
            if isinstance(value2, JRef):
                value1 = value2.clone()
            operand_stack.push(value2)
            operand_stack.push(value1)
        else:
            value3 = operand_stack.top(3)
            if isinstance(value1, JRef):
                value1 = value1.clone()
            if isinstance(value2, JRef):
                value1 = value2.clone()
            if isinstance(value3, JRef):
                value1 = value3.clone()
            operand_stack.push(value3)
            operand_stack.push(value2)
            operand_stack.push(value1)


class DUP2_X2(Instruction):
    code = 0x5e
    name = 'dup2_x2'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value1 = operand_stack.top()
        value2 = operand_stack.top(2)
        value3 = operand_stack.top(3)
        if operand_stack.is_top_type1() or operand_stack.is_top_type1(2) or operand_stack.is_top_type1(3):
            if isinstance(value1, JRef):
                value1 = value1.clone()
            if isinstance(value2, JRef):
                value1 = value2.clone()
            if isinstance(value3, JRef):
                value1 = value3.clone()
            operand_stack.push(value3)
            operand_stack.push(value2)
            operand_stack.push(value1)
        else:
            value4 = operand_stack.top(4)
            if isinstance(value1, JRef):
                value1 = value1.clone()
            if isinstance(value2, JRef):
                value1 = value2.clone()
            if isinstance(value3, JRef):
                value1 = value3.clone()
            if isinstance(value4, JRef):
                value1 = value4.clone()
            operand_stack.push(value4)
            operand_stack.push(value3)
            operand_stack.push(value2)
            operand_stack.push(value1)


class PUTSTATIC(Instruction):
    code = 0xb3
    name = 'putstatic'

    def __init__(self):
        super(PUTSTATIC, self).__init__()
        self.index = -1

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        constant_pool = frame.method.jclass.constant_pool
        ref = constant_pool.constants[self.index]
        ref.resolve_field(frame.method.jclass.class_loader)
        ftype = InsUtils.get_type_by_descriptor(ref.descriptor)
        jclass = ref.cache_class
        if not jclass.has_inited:
            c_init = INNER_INVOKE_C_INIT(jclass)
            c_init.execute(frame)
            jclass.has_inited = True
            return
        slots = jclass.static_fields
        slot = slots[ref.field.name]
        operand_stack = frame.operand_stack
        if ftype == InsUtils.TYPE_INT:
            slot.num = operand_stack.pop_int()
        elif ftype == InsUtils.TYPE_FLOAT:
            slot.num = operand_stack.pop_float()
        elif ftype == InsUtils.TYPE_LONG:
            slot.num = operand_stack.pop_long()
        elif ftype == InsUtils.TYPE_DOUBLE:
            slot.num = operand_stack.pop_double()
        else:
            slot.ref = operand_stack.pop_ref()


class GETSTATIC(Instruction):
    code = 0xb2
    name = 'getstatic'

    def __init__(self):
        super(GETSTATIC, self).__init__()
        self.index = -1

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        constant_pool = frame.method.jclass.constant_pool
        ref = constant_pool.constants[self.index]
        ref.resolve_field(frame.method.jclass.class_loader)
        ftype = InsUtils.get_type_by_descriptor(ref.descriptor)
        jclass = ref.cache_class
        if not jclass.has_inited:
            c_init = INNER_INVOKE_C_INIT(jclass)
            c_init.execute(frame)
            jclass.has_inited = True
            return
        slots = jclass.static_fields
        val = slots[ref.field.name]
        if val is not None:
            if val.num is not None:
                if ftype == InsUtils.TYPE_INT:
                    frame.operand_stack.push_int(val.num)
                elif ftype == InsUtils.TYPE_FLOAT:
                    frame.operand_stack.push_float(val.num)
                elif ftype == InsUtils.TYPE_LONG:
                    frame.operand_stack.push_long(val.num)
                elif ftype == InsUtils.TYPE_DOUBLE:
                    frame.operand_stack.push_double(val.num)
            elif val.ref is not None:
                frame.operand_stack.push_ref(val.ref)
            else:
                frame.operand_stack.push(None)
        else:
            frame.operand_stack.push(None)


class JUMP_INC(Instruction):
    def jump_by(self, frame, offset):
        frame.pc = frame.thread.pc + offset

    def jump_to(self, frame, pc):
        frame.pc = pc


class GOTO(JUMP_INC):
    code = 0xa7
    name = 'goto'

    def __init__(self):
        super(GOTO, self).__init__()
        self.branch = 0

    def read_operands(self, code_parser):
        branch1 = code_parser.read_op()
        branch2 = code_parser.read_op()
        self.branch = common_utils.get_short_from_bytes(branch1, branch2)

    def execute(self, frame):
        self.jump_by(frame, self.branch)


class GOTO_W(JUMP_INC):
    code = 0xc8
    name = 'goto_w'

    def __init__(self):
        super(GOTO_W, self).__init__()
        self.branch = 0

    def read_operands(self, code_parser):
        branch1 = code_parser.read_op()
        branch2 = code_parser.read_op()
        branch3 = code_parser.read_op()
        branch4 = code_parser.read_op()
        self.branch = (branch1 << 24) | (branch2 << 16) | (branch3 << 8) | branch4

    def execute(self, frame):
        self.jump_by(frame, self.branch)


class IASTORE(Instruction):
    code = 0x4f
    name = 'iastore'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.pop_int()
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        aref.handler.obj.add_item(index, value)


class IALOAD(Instruction):
    code = 0x2e
    name = 'iaload'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        val = aref.handler.obj.get_item(index)
        operand_stack.push_int(val)


class CASTORE(Instruction):
    code = 0x55
    name = 'castore'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.pop_int()
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        aref.handler.obj.add_item(index, value)


class CALOAD(Instruction):
    code = 0x34
    name = 'caload'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        val = aref.handler.obj.get_item(index)
        operand_stack.push_int(val)


class DASTORE(Instruction):
    code = 0x52
    name = 'dastore'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.pop_double()
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        aref.handler.obj.add_item(index, value)


class DALOAD(Instruction):
    code = 0x31
    name = 'daload'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        val = aref.handler.obj.get_item(index)
        operand_stack.push_double(val)


class FASTORE(Instruction):
    code = 0x51
    name = 'fastore'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.pop_float()
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        aref.handler.obj.add_item(index, value)


class FALOAD(Instruction):
    code = 0x30
    name = 'faload'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        val = aref.handler.obj.get_item(index)
        operand_stack.push_float(val)


class LASTORE(Instruction):
    code = 0x50
    name = 'lastore'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.pop_long()
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        aref.handler.obj.add_item(index, value)


class LALOAD(Instruction):
    code = 0x2f
    name = 'laload'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        val = aref.handler.obj.get_item(index)
        operand_stack.push_long(val)


class SASTORE(Instruction):
    code = 0x56
    name = 'sastore'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.pop_int()
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        aref.handler.obj.add_item(index, value)


class SALOAD(Instruction):
    code = 0x35
    name = 'saload'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        val = aref.handler.obj.get_item(index)
        operand_stack.push_int(val)


class AASTORE(Instruction):
    code = 0x53
    name = 'aastore'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.pop_ref()
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        aref.handler.obj.add_ref_item(index, value)


class AALOAD(Instruction):
    code = 0x32
    name = 'aaload'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        val = aref.handler.obj.get_item(index)
        operand_stack.push_ref(val)


class BASTORE(Instruction):
    code = 0x54
    name = 'bastore'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        value = operand_stack.pop_int()
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        aref.handler.obj.add_item(index, value)


class BALOAD(Instruction):
    code = 0x33
    name = 'baload'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        index = operand_stack.pop_int()
        aref = operand_stack.pop_ref()
        InsUtils.check_ref_null(aref)
        val = aref.handler.obj.get_item(index)
        operand_stack.push_int(val)


class IINC(Instruction):
    code = 0x84
    name = 'iinc'

    def __init__(self):
        super(IINC, self).__init__()
        self.index = 0
        self.const = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()
        self.const = code_parser.read_op()

    def execute(self, frame):
        val = frame.local_vars.get_int(self.index)
        val += self.const
        frame.local_vars.add_int(self.index, val)


class I2L(Instruction):
    code = 0x85
    name = 'i2l'

    def execute(self, frame):
        val = frame.operand_stack.pop_int()
        frame.operand_stack.push_long(val)


class I2F(Instruction):
    code = 0x86
    name = 'i2f'

    def execute(self, frame):
        val = frame.operand_stack.pop_int()
        frame.operand_stack.push_float(ctypes.c_float(val).value)


class I2D(Instruction):
    code = 0x87
    name = 'i2d'

    def execute(self, frame):
        val = frame.operand_stack.pop_int()
        frame.operand_stack.push_double(ctypes.c_double(val).value)


class L2I(Instruction):
    code = 0x88
    name = 'l2i'

    def execute(self, frame):
        val = frame.operand_stack.pop_long()
        frame.operand_stack.push_int(ctypes.c_int32(val).value)


class L2F(Instruction):
    code = 0x89
    name = 'l2f'

    def execute(self, frame):
        val = frame.operand_stack.pop_long()
        frame.operand_stack.push_float(ctypes.c_float(val).value)


class L2D(Instruction):
    code = 0x8a
    name = 'l2d'

    def execute(self, frame):
        val = frame.operand_stack.pop_long()
        frame.operand_stack.push_double(ctypes.c_double(val).value)


class F2I(Instruction):
    code = 0x8b
    name = 'f2i'

    def execute(self, frame):
        val = frame.operand_stack.pop_float()
        frame.operand_stack.push_int(ctypes.c_int32(val).value)


class F2L(Instruction):
    code = 0x8c
    name = 'f2l'

    def execute(self, frame):
        val = frame.operand_stack.pop_float()
        frame.operand_stack.push_long(ctypes.c_long(val).value)


class F2D(Instruction):
    code = 0x8d
    name = 'f2d'

    def execute(self, frame):
        val = frame.operand_stack.pop_float()
        frame.operand_stack.push_double(ctypes.c_double(val).value)


class D2I(Instruction):
    code = 0x8e
    name = 'd2i'

    def execute(self, frame):
        val = frame.operand_stack.pop_double()
        frame.operand_stack.push_int(ctypes.c_int32(val).value)


class D2L(Instruction):
    code = 0x8f
    name = 'd2l'

    def execute(self, frame):
        val = frame.operand_stack.pop_double()
        frame.operand_stack.push_long(ctypes.c_long(val).value)


class D2F(Instruction):
    code = 0x90
    name = 'd2f'

    def execute(self, frame):
        val = frame.operand_stack.pop_double()
        frame.operand_stack.push_float(ctypes.c_float(val).value)


class I2B(Instruction):
    code = 0x91
    name = 'i2b'

    def execute(self, frame):
        val = frame.operand_stack.pop_int()
        frame.operand_stack.push_int(ctypes.c_byte(val).value)


class I2C(Instruction):
    code = 0x92
    name = 'i2c'

    def execute(self, frame):
        val = frame.operand_stack.pop_int()
        frame.operand_stack.push_int(ctypes.c_char(val).value)


class I2S(Instruction):
    code = 0x93
    name = 'i2s'

    def execute(self, frame):
        val = frame.operand_stack.pop_int()
        frame.operand_stack.push_int(ctypes.c_short(val).value)


class LCMP(Instruction):
    code = 0x94
    name = 'lcmp'

    def execute(self, frame):
        value2 = frame.operand_stack.pop_long()
        value1 = frame.operand_stack.pop_long()
        if value1 > value2:
            frame.operand_stack.push_int(1)
        elif value1 < value2:
            frame.operand_stack.push_int(-1)
        else:
            frame.operand_stack.push_int(0)


class FCMPOP(Instruction):
    # TODO: 对 fcmpl 和 fcmpd 没有做区分
    def execute(self, frame):
        value2 = frame.operand_stack.pop_float()
        value1 = frame.operand_stack.pop_float()
        if value1 > value2:
            frame.operand_stack.push_int(1)
        elif value1 < value2:
            frame.operand_stack.push_int(-1)
        else:
            frame.operand_stack.push_int(0)


class FCMPL(FCMPOP):
    code = 0x95
    name = 'fcmpl'


class FCMPD(FCMPOP):
    code = 0x96
    name = 'fcmpd'


class DCMPOP(Instruction):
    # TODO: 对 dcmpl 和 dcmpd 没有做区分
    def execute(self, frame):
        value2 = frame.operand_stack.pop_double()
        value1 = frame.operand_stack.pop_double()
        if value1 > value2:
            frame.operand_stack.push_int(1)
        elif value1 < value2:
            frame.operand_stack.push_int(-1)
        else:
            frame.operand_stack.push_int(0)


class DCMPL(DCMPOP):
    code = 0x97
    name = 'dcmpl'


class DCMPD(FCMPOP):
    code = 0x98
    name = 'dcmpd'


class ICONST_I(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        index = self.get_index()
        stack = frame.operand_stack
        stack.push_int(index)


class ICONST_M1(ICONST_I):
    code = 0x2
    name = 'iconst_m1'

    def get_index(self):
        return -1


class ICONST_0(ICONST_I):
    code = 0x3
    name = 'iconst_0'

    def get_index(self):
        return 0


class ICONST_1(ICONST_I):
    code = 0x4
    name = 'iconst_1'

    def get_index(self):
        return 1


class ICONST_2(ICONST_I):
    code = 0x5
    name = 'iconst_2'

    def get_index(self):
        return 2


class ICONST_3(ICONST_I):
    code = 0x6
    name = 'iconst_3'

    def get_index(self):
        return 3


class ICONST_4(ICONST_I):
    code = 0x7
    name = 'iconst_4'

    def get_index(self):
        return 4


class ICONST_5(ICONST_I):
    code = 0x8
    name = 'iconst_5'

    def get_index(self):
        return 5


class LCONST_L(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        index = self.get_index()
        stack = frame.operand_stack
        stack.push_long(index)


class LCONST_0(LCONST_L):
    code = 0x9
    name = 'lconst_0'

    def get_index(self):
        return 0


class LCONST_1(LCONST_L):
    code = 0xa
    name = 'lconst_1'

    def get_index(self):
        return 1


class FCONST_L(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        index = self.get_index()
        stack = frame.operand_stack
        stack.push_float(index)


class FCONST_0(FCONST_L):
    code = 0xb
    name = 'fconst_0'

    def get_index(self):
        return 0.0


class FCONST_1(FCONST_L):
    code = 0xc
    name = 'fconst_1'

    def get_index(self):
        return 1.0


class FCONST_2(FCONST_L):
    code = 0xd
    name = 'fconst_2'

    def get_index(self):
        return 2.0


class DCONST_L(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        index = self.get_index()
        stack = frame.operand_stack
        stack.push_double(index)


class DCONST_0(FCONST_L):
    code = 0xe
    name = 'dconst_0'

    def get_index(self):
        return 0.0


class DCONST_1(FCONST_L):
    code = 0xf
    name = 'dconst_1'

    def get_index(self):
        return 1.0


class ILOAD(Instruction):
    code = 0x15
    name = 'iload'

    def __init__(self):
        super(ILOAD, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def execute(self, frame):
        local_vars = frame.local_vars
        operand_stack = frame.operand_stack
        value = local_vars.get_int(self.index)
        operand_stack.push_int(value)


class ILOAD_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        index = self.get_index()
        local_vars = frame.local_vars
        operand_stack = frame.operand_stack
        value = local_vars.get_int(index)
        operand_stack.push_int(value)


class ILOAD_0(ILOAD_N):
    code = 0x1a
    name = 'iload_0'

    def get_index(self):
        return 0


class ILOAD_1(ILOAD_N):
    code = 0x1b
    name = 'iload_1'

    def get_index(self):
        return 1


class ILOAD_2(ILOAD_N):
    code = 0x1c
    name = 'iload_2'

    def get_index(self):
        return 2


class ILOAD_3(ILOAD_N):
    code = 0x1d
    name = 'iload_3'

    def get_index(self):
        return 3


class DLOAD_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        index = self.get_index()
        local_vars = frame.local_vars
        operand_stack = frame.operand_stack
        value = local_vars.get_double(index)
        operand_stack.push_double(value)


class DLOAD(DLOAD_N):
    code = 0x18
    name = 'dload'

    def __init__(self):
        super(DLOAD, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def get_index(self):
        return self.index


class DLOAD_0(DLOAD_N):
    code = 0x26
    name = 'dload_0'

    def get_index(self):
        return 0


class DLOAD_1(DLOAD_N):
    code = 0x27
    name = 'dload_1'

    def get_index(self):
        return 1


class DLOAD_2(DLOAD_N):
    code = 0x28
    name = 'dload_2'

    def get_index(self):
        return 2


class DLOAD_3(DLOAD_N):
    code = 0x29
    name = 'dload_3'

    def get_index(self):
        return 3


class FLOAD_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        index = self.get_index()
        local_vars = frame.local_vars
        operand_stack = frame.operand_stack
        value = local_vars.get_float(index)
        operand_stack.push_float(value)


class FLOAD(FLOAD_N):
    code = 0x17
    name = 'fload'

    def __init__(self):
        super(FLOAD, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def get_index(self):
        return self.index


class FLOAD_0(FLOAD_N):
    code = 0x22
    name = 'fload_0'

    def get_index(self):
        return 0


class FLOAD_1(FLOAD_N):
    code = 0x23
    name = 'fload_1'

    def get_index(self):
        return 1


class FLOAD_2(FLOAD_N):
    code = 0x24
    name = 'fload_2'

    def get_index(self):
        return 2


class FLOAD_3(FLOAD_N):
    code = 0x25
    name = 'fload_3'

    def get_index(self):
        return 3


class LLOAD_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        index = self.get_index()
        local_vars = frame.local_vars
        operand_stack = frame.operand_stack
        value = local_vars.get_long(index)
        operand_stack.push_long(value)


class LLOAD(LLOAD_N):
    code = 0x16
    name = 'lload'

    def __init__(self):
        super(LLOAD, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def get_index(self):
        return self.index


class LLOAD_0(LLOAD_N):
    code = 0x1e
    name = 'lload_0'

    def get_index(self):
        return 0


class LLOAD_1(LLOAD_N):
    code = 0x1f
    name = 'lload_1'

    def get_index(self):
        return 1


class LLOAD_2(LLOAD_N):
    code = 0x20
    name = 'lload_2'

    def get_index(self):
        return 2


class LLOAD_3(LLOAD_N):
    code = 0x21
    name = 'lload_3'

    def get_index(self):
        return 3


class IADD(Instruction):
    code = 0x60
    name = 'iadd'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_int()
        val2 = operand_stack.pop_int()
        res = val1 + val2
        operand_stack.push_int(res)


class LADD(Instruction):
    code = 0x61
    name = 'ladd'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_long()
        val2 = operand_stack.pop_long()
        res = val1 + val2
        operand_stack.push_long(res)


class FADD(Instruction):
    code = 0x62
    name = 'fadd'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_float()
        val2 = operand_stack.pop_float()
        res = val1 + val2
        operand_stack.push_float(res)


class DADD(Instruction):
    code = 0x63
    name = 'dadd'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_double()
        val2 = operand_stack.pop_double()
        res = val1 + val2
        operand_stack.push_double(res)


class ISUB(Instruction):
    code = 0x64
    name = 'isub'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_int()
        val2 = operand_stack.pop_int()
        res = val1 - val2
        operand_stack.push_int(res)


class LSUB(Instruction):
    code = 0x65
    name = 'lsub'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_long()
        val2 = operand_stack.pop_long()
        res = val1 - val2
        operand_stack.push_long(res)


class FSUB(Instruction):
    code = 0x66
    name = 'fsub'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_float()
        val2 = operand_stack.pop_float()
        res = val1 - val2
        operand_stack.push_float(res)


class DSUB(Instruction):
    code = 0x67
    name = 'dsub'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_double()
        val2 = operand_stack.pop_double()
        res = val1 - val2
        operand_stack.push_double(res)


class IMUL(Instruction):
    code = 0x68
    name = 'imul'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_int()
        val2 = operand_stack.pop_int()
        res = val1 * val2
        operand_stack.push_int(res)


class LMUL(Instruction):
    code = 0x69
    name = 'lmul'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_long()
        val2 = operand_stack.pop_long()
        res = val1 * val2
        operand_stack.push_long(res)


class FMUL(Instruction):
    code = 0x6a
    name = 'fmul'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_float()
        val2 = operand_stack.pop_float()
        res = val1 * val2
        operand_stack.push_float(res)


class DMUL(Instruction):
    code = 0x6b
    name = 'dmul'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_double()
        val2 = operand_stack.pop_double()
        res = val1 * val2
        operand_stack.push_double(res)


class IDIV(Instruction):
    code = 0x6c
    name = 'idiv'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_int()
        val2 = operand_stack.pop_int()
        if val2 == 0:
            error_handler.rise_runtime_error('java.lang.ArithmeticException: x / 0')
        res = val1 / val2
        operand_stack.push_int(res)


class LDIV(Instruction):
    code = 0x6d
    name = 'ldiv'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_long()
        val2 = operand_stack.pop_long()
        if val2 == 0:
            error_handler.rise_runtime_error('java.lang.ArithmeticException: x / 0')
        res = val1 / val2
        operand_stack.push_long(res)


class FDIV(Instruction):
    code = 0x6e
    name = 'fdiv'

    def execute(self, frame):
        # TODO: IEEE 规范中规定的运算规则
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_float()
        val2 = operand_stack.pop_float()
        res = val1 / val2
        operand_stack.push_float(res)


class DDIV(Instruction):
    code = 0x6f
    name = 'ddiv'

    def execute(self, frame):
        # TODO: IEEE 规范中规定的运算规则
        operand_stack = frame.operand_stack
        val1 = operand_stack.pop_double()
        val2 = operand_stack.pop_double()
        res = val1 / val2
        operand_stack.push_double(res)


class IREM(Instruction):
    code = 0x70
    name = 'irem'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val2 = operand_stack.pop_int()
        val1 = operand_stack.pop_int()
        if val2 == 0:
            error_handler.rise_runtime_error('java.lang.ArithmeticException: x / 0')
        # res = val1 - int(val1 / val2) * val2
        res = val1 % val2
        operand_stack.push_int(res)


class LREM(Instruction):
    code = 0x71
    name = 'lrem'

    def execute(self, frame):
        operand_stack = frame.operand_stack
        val2 = operand_stack.pop_long()
        val1 = operand_stack.pop_long()
        if val2 == 0:
            error_handler.rise_runtime_error('java.lang.ArithmeticException: x / 0')
        # res = val1 - int(val1 / val2) * val2
        res = val1 % val2
        operand_stack.push_long(res)


class FREM(Instruction):
    code = 0x72
    name = 'frem'

    def execute(self, frame):
        # TODO: IEEE 规范中规定的运算规则
        operand_stack = frame.operand_stack
        val2 = operand_stack.pop_float()
        val1 = operand_stack.pop_float()
        res = val1 % val2
        operand_stack.push_float(res)


class DREM(Instruction):
    code = 0x73
    name = 'drem'

    def execute(self, frame):
        # TODO: IEEE 规范中规定的运算规则
        operand_stack = frame.operand_stack
        val2 = operand_stack.pop_double()
        val1 = operand_stack.pop_double()
        res = val1 % val2
        operand_stack.push_double(res)


class INEG(Instruction):
    code = 0x74
    name = 'ineg'

    def execute(self, frame):
        val = frame.operand_stack.pop_int()
        frame.operand_stack.push_int(-val)


class LNEG(Instruction):
    code = 0x75
    name = 'lneg'

    def execute(self, frame):
        val = frame.operand_stack.pop_long()
        frame.operand_stack.push_long(-val)


class FNEG(Instruction):
    code = 0x76
    name = 'fneg'

    def execute(self, frame):
        val = frame.operand_stack.pop_float()
        frame.operand_stack.push_float(-val)


class DNEG(Instruction):
    code = 0x77
    name = 'dneg'

    def execute(self, frame):
        val = frame.operand_stack.pop_double()
        frame.operand_stack.push_double(-val)


class ISHL(Instruction):
    code = 0x78
    name = 'ishl'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_int()
        val1 = frame.operand_stack.pop_int()
        s = val2 & 0x1f
        res = val1 << s
        frame.operand_stack.push_int(res)


class LSHL(Instruction):
    code = 0x79
    name = 'lshl'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_long()
        val1 = frame.operand_stack.pop_long()
        s = val2 & 0x3f
        res = val1 << s
        frame.operand_stack.push_long(res)


class ISHR(Instruction):
    code = 0x7a
    name = 'ishr'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_int()
        val1 = frame.operand_stack.pop_int()
        s = val2 & 0x1f
        res = val1 >> s
        frame.operand_stack.push_int(res)


class LSHR(Instruction):
    code = 0x7b
    name = 'lshr'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_long()
        val1 = frame.operand_stack.pop_long()
        s = val2 & 0x3f
        res = val1 >> s
        frame.operand_stack.push_long(res)


class IUSHR(Instruction):
    code = 0x7c
    name = 'iushr'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_int()
        val1 = frame.operand_stack.pop_int()
        s = val2 & 0x1f
        val = ctypes.c_uint32(val1).value
        res = ctypes.c_int32(val >> s).value
        frame.operand_stack.push_int(res)


class LUSHR(Instruction):
    code = 0x7d
    name = 'lushr'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_long()
        val1 = frame.operand_stack.pop_long()
        s = val2 & 0x3f
        val = ctypes.c_uint64(val1).value
        res = ctypes.c_int64(val >> s).value
        frame.operand_stack.push_int(res)


class IAND(Instruction):
    code = 0x7e
    name = 'iand'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_int()
        val1 = frame.operand_stack.pop_int()
        res = val1 & val2
        frame.operand_stack.push_int(res)


class LAND(Instruction):
    code = 0x7f
    name = 'land'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_long()
        val1 = frame.operand_stack.pop_long()
        res = val1 & val2
        frame.operand_stack.push_long(res)


class IOR(Instruction):
    code = 0x80
    name = 'ior'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_int()
        val1 = frame.operand_stack.pop_int()
        res = val1 | val2
        frame.operand_stack.push_int(res)


class LOR(Instruction):
    code = 0x81
    name = 'lor'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_long()
        val1 = frame.operand_stack.pop_long()
        res = val1 | val2
        frame.operand_stack.push_long(res)


class IXOR(Instruction):
    code = 0x82
    name = 'ixor'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_int()
        val1 = frame.operand_stack.pop_int()
        res = val1 ^ val2
        frame.operand_stack.push_int(res)


class LXOR(Instruction):
    code = 0x83
    name = 'lxor'

    def execute(self, frame):
        val2 = frame.operand_stack.pop_long()
        val1 = frame.operand_stack.pop_long()
        res = val1 ^ val2
        frame.operand_stack.push_long(res)


class INVOKESPECIAL(Instruction):
    code = 0xb7
    name = 'invokespecial'

    def __init__(self):
        super(INVOKESPECIAL, self).__init__()
        self.index = -1

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        # TODO: 方法校验
        constant_pool = frame.method.jclass.constant_pool.constants
        n_method_ref = constant_pool[self.index]
        # TODO: 先忽略基础类的所有方法
        if n_method_ref.class_name == 'java/lang/Object':
            return
        print_utils.print_jvm_status('invokespecial: ' + n_method_ref.name)
        n_method = n_method_ref.resolve_method(frame.method.jclass.class_loader)
        n_frame = Frame(frame.thread, n_method)
        frame.thread.add_frame(n_frame)
        arg_desc = n_method.arg_desc
        i = len(arg_desc)
        for arg in arg_desc:
            if arg == 'I' or arg == 'S' or arg == 'Z' or arg == 'C':
                value = frame.operand_stack.pop_int()
                n_frame.local_vars.add_int(i, value)
            elif arg == 'J':
                value = frame.operand_stack.pop_long()
                n_frame.local_vars.add_long(i, value)
            elif arg == 'F':
                value = frame.operand_stack.pop_float()
                n_frame.local_vars.add_float(i, value)
            elif arg == 'D':
                value = frame.operand_stack.pop_double()
                n_frame.local_vars.add_double(i, value)
            elif arg[0] == 'L':
                jref = frame.operand_stack.pop_ref()
                n_frame.local_vars.add_ref(i, jref)
            i -= 1
        n_frame.local_vars.add_ref(0, frame.operand_stack.pop())


class INVOKEVIRTUAL(Instruction):
    code = 0xb6
    name = 'invokevirtual'

    def __init__(self):
        super(INVOKEVIRTUAL, self).__init__()
        self.index = -1

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        # TODO: 方法校验
        constant_pool = frame.method.jclass.constant_pool.constants
        n_method_ref = constant_pool[self.index]
        # TODO: 先忽略基类的所有方法
        if n_method_ref.class_name == 'java/lang/Object':
            return
        print_utils.print_jvm_status('invokevirtual: ' + n_method_ref.name)
        # 主要是为了获取参数个数
        n_method = n_method_ref.resolve_method_with_super(frame.method.jclass.class_loader)
        arg_desc = n_method.arg_desc
        ref = frame.operand_stack.top(len(arg_desc))
        if ref is not None:
            real_class_name = ref.handler.obj.jclass.name
            if n_method_ref.class_name != real_class_name:
                n_method = n_method_ref.re_resolve_method_with_super_by_class_name(frame.method.jclass.class_loader,
                                                                                   real_class_name)
        n_frame = Frame(frame.thread, n_method)
        frame.thread.add_frame(n_frame)
        i = len(arg_desc)
        for arg in arg_desc:
            if arg == 'I' or arg == 'S' or arg == 'Z' or arg == 'C' or arg == 'B':
                value = frame.operand_stack.pop_int()
                n_frame.local_vars.add_int(i, value)
            elif arg == 'J':
                value = frame.operand_stack.pop_long()
                n_frame.local_vars.add_long(i, value)
            elif arg == 'F':
                value = frame.operand_stack.pop_float()
                n_frame.local_vars.add_float(i, value)
            elif arg == 'D':
                value = frame.operand_stack.pop_double()
                n_frame.local_vars.add_double(i, value)
            elif arg[0] == 'L':
                jref = frame.operand_stack.pop_ref()
                n_frame.local_vars.add_ref(i, jref)
            i -= 1
        n_frame.local_vars.add_ref(0, frame.operand_stack.pop())
        self.__hack(n_frame, n_method)

    # 暂时做一些 hack 处理,比如输出
    def __hack(self, n_frame, method):
        # 处理 print
        self.__hack_println(n_frame, method)
        # 处理 thread
        self.__hack_thread(n_frame, method)

    def __hack_thread(self, n_frame, method):
        jthis = n_frame.local_vars.get_ref(0)
        if method.name == 'start' and jthis.handler.obj.jclass.super_class_name == 'java/lang/Thread':
            for m in jthis.handler.obj.jclass.methods:
                if m.name == 'run':
                    JThread.start_new_thread(m)

    def __hack_println(self, n_frame, method):
        if method.name == 'println' and method.jclass.name == 'java/io/PrintStream':
            if method.descriptor == '(Ljava/lang/String;)Ljava/io/PrintStream;':
                jref = n_frame.local_vars.get_ref(1)
                print_utils.StreamPrinter.append_msg(n_frame.thread, jref.data)
            elif method.descriptor == '(I)Ljava/io/PrintStream;':
                jint = n_frame.local_vars.get_int(1)
                print_utils.StreamPrinter.append_msg(n_frame.thread, jint)
            elif method.descriptor == '(S)Ljava/io/PrintStream;':
                jint = n_frame.local_vars.get_int(1)
                print_utils.StreamPrinter.append_msg(n_frame.thread, jint)
            elif method.descriptor == '(B)Ljava/io/PrintStream;':
                jint = n_frame.local_vars.get_int(1)
                print_utils.StreamPrinter.append_msg(n_frame.thread, jint)
            elif method.descriptor == '(C)Ljava/io/PrintStream;':
                jint = n_frame.local_vars.get_int(1)
                print_utils.StreamPrinter.append_msg(n_frame.thread, chr(jint))
            elif method.descriptor == '(J)Ljava/io/PrintStream;':
                jlong = n_frame.local_vars.get_long(1)
                print_utils.StreamPrinter.append_msg(n_frame.thread, jlong)
            elif method.descriptor == '(D)Ljava/io/PrintStream;':
                jdouble = n_frame.local_vars.get_double(1)
                print_utils.StreamPrinter.append_msg(n_frame.thread, jdouble)
            elif method.descriptor == '(F)Ljava/io/PrintStream;':
                jfloat = n_frame.local_vars.get_float(1)
                print_utils.StreamPrinter.append_msg(n_frame.thread, jfloat)
            elif method.descriptor == '(Z)Ljava/io/PrintStream;':
                jint = n_frame.local_vars.get_int(1)
                if jint == 0:  # False
                    print_utils.StreamPrinter.append_msg(n_frame.thread, 'false')
                elif jint == 1:  # True
                    print_utils.StreamPrinter.append_msg(n_frame.thread, 'true')
                else:
                    error_handler.rise_runtime_error('runtime error: boolean not true or false')


class INVOKESTATIC(Instruction):
    code = 0xb8
    name = 'invokestatic'

    def __init__(self):
        super(INVOKESTATIC, self).__init__()
        self.index = -1

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        # TODO: 方法校验
        constant_pool = frame.method.jclass.constant_pool.constants
        n_method_ref = constant_pool[self.index]
        print_utils.print_jvm_status('invokestatic: ' + n_method_ref.name)
        n_method = n_method_ref.resolve_method(frame.method.jclass.class_loader)
        n_frame = Frame(frame.thread, n_method)
        frame.thread.add_frame(n_frame)
        arg_desc = n_method.arg_desc
        i = len(arg_desc)
        for arg in arg_desc:
            i -= 1
            if arg == 'I' or arg == 'S' or arg == 'Z' or arg == 'C':
                value = frame.operand_stack.pop_int()
                n_frame.local_vars.add_int(i, value)
            elif arg == 'J':
                value = frame.operand_stack.pop_long()
                n_frame.local_vars.add_long(i, value)
            elif arg == 'F':
                value = frame.operand_stack.pop_float()
                n_frame.local_vars.add_float(i, value)
            elif arg == 'D':
                value = frame.operand_stack.pop_double()
                n_frame.local_vars.add_double(i, value)
            elif arg[0] == 'L':
                jref = frame.operand_stack.pop_ref()
                n_frame.local_vars.add_ref(i, jref)


class IRETURN(Instruction):
    code = 0xac
    name = 'ireturn'

    def execute(self, frame):
        frame.thread.pop_frame()
        r_value = frame.operand_stack.pop_int()
        c_frame = frame.thread.top_frame()
        c_frame.operand_stack.push_int(r_value)


class LRETURN(Instruction):
    code = 0xad
    name = 'lreturn'

    def execute(self, frame):
        frame.thread.pop_frame()
        r_value = frame.operand_stack.pop_long()
        c_frame = frame.thread.top_frame()
        c_frame.operand_stack.push_long(r_value)


class FRETURN(Instruction):
    code = 0xae
    name = 'freturn'

    def execute(self, frame):
        frame.thread.pop_frame()
        r_value = frame.operand_stack.pop_float()
        c_frame = frame.thread.top_frame()
        c_frame.operand_stack.push_float(r_value)


class DRETURN(Instruction):
    code = 0xaf
    name = 'dreturn'

    def execute(self, frame):
        frame.thread.pop_frame()
        r_value = frame.operand_stack.pop_double()
        c_frame = frame.thread.top_frame()
        c_frame.operand_stack.push_double(r_value)


class ARETURN(Instruction):
    code = 0xb0
    name = 'areturn'

    def execute(self, frame):
        frame.thread.pop_frame()
        r_value = frame.operand_stack.pop_ref()
        c_frame = frame.thread.top_frame()
        c_frame.operand_stack.push_ref(r_value)


class ISTORE_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        value = frame.operand_stack.pop_int()
        index = self.get_index()
        frame.local_vars.add_int(index, value)


class ISTORE(ISTORE_N):
    code = 0x36
    name = 'istore'

    def __init__(self):
        super(ISTORE, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def get_index(self):
        return self.index


class ISTORE_0(ISTORE_N):
    code = 0x3b
    name = 'istore_0'

    def get_index(self):
        return 0


class ISTORE_1(ISTORE_N):
    code = 0x3c
    name = 'istore_1'

    def get_index(self):
        return 1


class ISTORE_2(ISTORE_N):
    code = 0x3d
    name = 'istore_2'

    def get_index(self):
        return 2


class ISTORE_3(ISTORE_N):
    code = 0x3e
    name = 'istore_3'

    def get_index(self):
        return 3


class DSTORE_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        value = frame.operand_stack.pop_double()
        index = self.get_index()
        frame.local_vars.add_double(index, value)


class DSTORE(DSTORE_N):
    code = 0x39
    name = 'dstore'

    def __init__(self):
        super(DSTORE, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def get_index(self):
        return self.index


class DSTORE_0(DSTORE_N):
    code = 0x47
    name = 'dstore_0'

    def get_index(self):
        return 0


class DSTORE_1(DSTORE_N):
    code = 0x48
    name = 'dstore_1'

    def get_index(self):
        return 1


class DSTORE_2(DSTORE_N):
    code = 0x49
    name = 'dstore_2'

    def get_index(self):
        return 2


class DSTORE_3(DSTORE_N):
    code = 0x4a
    name = 'dstore_3'

    def get_index(self):
        return 3


class FSTORE_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        value = frame.operand_stack.pop_float()
        index = self.get_index()
        frame.local_vars.add_float(index, value)


class FSTORE(FSTORE_N):
    code = 0x38
    name = 'fstore'

    def __init__(self):
        super(FSTORE, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def get_index(self):
        return self.index


class FSTORE_0(FSTORE_N):
    code = 0x43
    name = 'fstore_0'

    def get_index(self):
        return 0


class FSTORE_1(FSTORE_N):
    code = 0x44
    name = 'fstore_1'

    def get_index(self):
        return 1


class FSTORE_2(FSTORE_N):
    code = 0x45
    name = 'fstore_2'

    def get_index(self):
        return 2


class FSTORE_3(FSTORE_N):
    code = 0x46
    name = 'fstore_3'

    def get_index(self):
        return 3


class LSTORE_N(Instruction):
    @abc.abstractmethod
    def get_index(self):
        pass

    def execute(self, frame):
        value = frame.operand_stack.pop_long()
        index = self.get_index()
        frame.local_vars.add_long(index, value)


class LSTORE(LSTORE_N):
    code = 0x37
    name = 'lstore'

    def __init__(self):
        super(LSTORE, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def get_index(self):
        return self.index


class LSTORE_0(LSTORE_N):
    code = 0x3f
    name = 'lstore_0'

    def get_index(self):
        return 0


class LSTORE_1(LSTORE_N):
    code = 0x40
    name = 'lstore_1'

    def get_index(self):
        return 1


class LSTORE_2(LSTORE_N):
    code = 0x41
    name = 'lstore_2'

    def get_index(self):
        return 2


class LSTORE_3(LSTORE_N):
    code = 0x42
    name = 'lstore_3'

    def get_index(self):
        return 3


class LDC(Instruction):
    code = 0x12
    name = 'ldc'

    def __init__(self):
        super(LDC, self).__init__()
        self.index = None

    def read_operands(self, code_parser):
        self.index = code_parser.read_op()

    def execute(self, frame):
        constants = frame.method.jclass.constant_pool.constants
        ref = constants[self.index]
        operand_stack = frame.operand_stack
        if isinstance(ref, JInteger):
            operand_stack.push_int(ref.data)
        elif isinstance(ref, JFloat):
            operand_stack.push_float(ref.data)
        elif isinstance(ref, JString):
            operand_stack.push_ref(ref)
        elif isinstance(ref, JRef):
            operand_stack.push_ref(ref)
        else:
            operand_stack.push(None)


class LDC_W(LDC):
    code = 0x13
    name = 'ldc_w'

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2


class LDC2_W(Instruction):
    code = 0x14
    name = 'ldc2_w'

    def __init__(self):
        super(LDC2_W, self).__init__()
        self.index = None

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        constants = frame.method.jclass.constant_pool.constants
        ref = constants[self.index]
        operand_stack = frame.operand_stack
        if isinstance(ref, JLong):
            operand_stack.push_long(ref.data)
        elif isinstance(ref, JDouble):
            operand_stack.push_double(ref.data)
        elif isinstance(ref, JString):
            operand_stack.push_ref(ref)
        else:  # TODO: Class 对象
            pass


class NOP(Instruction):
    code = 0x00
    name = 'nop'

    def execute(self, frame):
        pass


class IF_COND(JUMP_INC):
    def __init__(self):
        super(IF_COND, self).__init__()
        self.branch = 0

    @abc.abstractmethod
    def compare(self, value):
        pass

    def read_operands(self, code_parser):
        branch1 = code_parser.read_op()
        branch2 = code_parser.read_op()
        self.branch = common_utils.get_short_from_bytes(branch1, branch2)

    def execute(self, frame):
        value = frame.operand_stack.pop_int()
        if self.compare(value):
            self.jump_by(frame, self.branch)


class IFEQ(IF_COND):
    code = 0x99
    name = 'ifeq'

    def compare(self, value):
        return value == 0


class IFNE(IF_COND):
    code = 0x9a
    name = 'ifne'

    def compare(self, value):
        return value != 0


class IFLT(IF_COND):
    code = 0x9b
    name = 'iflt'

    def compare(self, value):
        return value < 0


class IFGE(IF_COND):
    code = 0x9c
    name = 'ifge'

    def compare(self, value):
        return value >= 0


class IFGT(IF_COND):
    code = 0x9d
    name = 'ifgt'

    def compare(self, value):
        return value > 0


class IFLE(IF_COND):
    code = 0x9e
    name = 'ifle'

    def compare(self, value):
        return value <= 0


class IF_ICMP_COND(JUMP_INC):
    def __init__(self):
        super(IF_ICMP_COND, self).__init__()
        self.branch = 0

    @abc.abstractmethod
    def compare(self, value1, value2):
        pass

    def read_operands(self, code_parser):
        branch1 = code_parser.read_op()
        branch2 = code_parser.read_op()
        self.branch = common_utils.get_short_from_bytes(branch1, branch2)

    def execute(self, frame):
        value2 = frame.operand_stack.pop_int()
        value1 = frame.operand_stack.pop_int()
        if self.compare(value1, value2):
            self.jump_by(frame, self.branch)


class IF_ICMPEQ(IF_ICMP_COND):
    code = 0x9f
    name = 'if_icmpeq'

    def compare(self, value1, value2):
        return value1 == value2


class IF_ICMPNE(IF_ICMP_COND):
    code = 0xa0
    name = 'if_icmpne'

    def compare(self, value1, value2):
        return value1 != value2


class IF_ICMPLT(IF_ICMP_COND):
    code = 0xa1
    name = 'if_icmplt'

    def compare(self, value1, value2):
        return value1 < value2


class IF_ICMPGE(IF_ICMP_COND):
    code = 0xa2
    name = 'if_icmpge'

    def compare(self, value1, value2):
        return value1 >= value2


class IF_ICMPGT(IF_ICMP_COND):
    code = 0xa3
    name = 'if_icmpgt'

    def compare(self, value1, value2):
        return value1 > value2


class IF_ICMPLE(IF_ICMP_COND):
    code = 0xa4
    name = 'if_icmple'

    def compare(self, value1, value2):
        return value1 <= value2


class IF_ACMP_COND(JUMP_INC):
    def __init__(self):
        super(IF_ACMP_COND, self).__init__()
        self.branch = 0

    @abc.abstractmethod
    def compare(self, ref1, ref2):
        pass

    def read_operands(self, code_parser):
        branch1 = code_parser.read_op()
        branch2 = code_parser.read_op()
        self.branch = common_utils.get_short_from_bytes(branch1, branch2)

    def execute(self, frame):
        # TODO: ref 怎么做比较?
        value2 = frame.operand_stack.pop_ref()
        value1 = frame.operand_stack.pop_ref()
        if self.compare(value1, value2):
            self.jump_by(frame, self.branch)


class IF_ACMPEQ(IF_ACMP_COND):
    code = 0xa5
    name = 'if_acmpeq'

    def compare(self, ref1, ref2):
        return ref1 == ref2


class IF_ACMPNE(IF_ACMP_COND):
    code = 0xa6
    name = 'if_acmpne'

    def compare(self, ref1, ref2):
        return ref1 != ref2


# impdep1 impdep2 breakpoint 为保留操作符
class IMPDEP1(Instruction):
    code = 0xfe
    name = 'impdep1'

    def execute(self, frame):
        pass


class IMPDEP2(Instruction):
    code = 0xff
    name = 'impdep2'

    def execute(self, frame):
        pass


class Breakpoint(Instruction):
    code = 0xca
    name = 'breakpoint'

    def execute(self, frame):
        pass


class NEW(Instruction):
    code = 0xbb
    name = 'new'

    def __init__(self):
        super(NEW, self).__init__()
        self.index = -1

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        class_loader = frame.method.jclass.class_loader
        if class_loader is None:
            class_loader = ClassLoader()
            frame.method.jclass.class_loader = class_loader
        constant_pool = frame.method.jclass.constant_pool
        ref = constant_pool.constants[self.index]
        jclass = class_loader.load_class(ref.class_name)
        ref = JRef.new_object(jclass)  # JObject.new_object() 返回的是真实实例对象  JRef.new_object() 返回的是引用,并且会吧 object 放入 gc 堆
        frame.operand_stack.push_ref(ref)


class NEWARRAY(Instruction):
    code = 0xbc
    name = 'newarray'

    def __init__(self):
        self.atype = None

    def read_operands(self, code_parser):
        self.atype = code_parser.read_op()

    def execute(self, frame):
        count = frame.operand_stack.pop_int()
        class_loader = frame.method.jclass.class_loader
        if class_loader is None:
            class_loader = ClassLoader()
            frame.method.jclass.class_loader = class_loader
        jclass = class_loader.load_class(JArray.get_array_jclass_name(self.atype))
        jref = JRef.new_array(jclass, self.atype, count)
        frame.operand_stack.push_ref(jref)


class ANEWARRAY(Instruction):
    code = 0xbd
    name = 'anewarray'

    def __init__(self):
        super(ANEWARRAY, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        count = frame.operand_stack.pop_int()
        class_loader = frame.method.jclass.class_loader
        if class_loader is None:
            class_loader = ClassLoader()
            frame.method.jclass.class_loader = class_loader
        clref = frame.method.jclass.constant_pool.constants[self.index]
        jclass = class_loader.load_class(JArray.get_ref_array_jclass_name(clref.class_name))
        jref = JRef.new_ref_array(jclass, clref, count)
        frame.operand_stack.push_ref(jref)


class ARRAY_LENGTH(Instruction):
    code = 0xbe
    name = 'arraylength'

    def execute(self, frame):
        ref = frame.operand_stack.pop_ref()
        InsUtils.check_ref_null(ref)
        if ref.handler.obj is None:
            error_handler.rise_null_point_error()
        obj = ref.handler.obj
        if not isinstance(ref.handler.obj, JArray):
            error_handler.rise_runtime_error('!!! not array !!!')
        frame.operand_stack.push_int(obj.length)


class POP(Instruction):
    code = 0x57
    name = 'pop'

    def execute(self, frame):
        frame.operand_stack.pop_raw()


class POP2(Instruction):
    code = 0x58
    name = 'pop2'

    def execute(self, frame):
        frame.operand_stack.pop()
        if frame.operand_stack.is_top_type1():
            frame.operand_stack.pop()


class PUTFIELD(Instruction):
    code = 0xb5
    name = 'putfield'

    def __init__(self):
        super(PUTFIELD, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    @staticmethod
    def check_state(frame, field_ref, cl_ref):
        if field_ref.is_static():
            error_handler.rise_runtime_error('java.lang.IncompatibleClassChangeError: put field to static value')
        if field_ref.is_final() and frame.method.name != '<init>':
            error_handler.rise_runtime_error('java.lang.IllegalAccessError: val is final')
        if cl_ref is None or cl_ref.handler is None or cl_ref.handler.obj is None:
            error_handler.rise_runtime_error('java.lang.NullPointerException ref is null')

    def execute(self, frame):
        # TODO: field 验证
        field_ref = frame.method.jclass.constant_pool.constants[self.index]
        val = frame.operand_stack.pop()
        cl_ref = frame.operand_stack.pop_ref()
        PUTFIELD.check_state(frame, field_ref, cl_ref)
        if isinstance(val, JRef):
            InsUtils.check_ref_null(cl_ref)
            cl_ref.handler.obj.put_ref_field(field_ref.name, val)
        else:
            cl_ref.handler.obj.put_field(field_ref.name, val)


class GETFIELD(Instruction):
    code = 0xb4
    name = 'getfield'

    def __init__(self):
        super(GETFIELD, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        fieldref = frame.method.jclass.constant_pool.constants[self.index]
        clref = frame.operand_stack.pop_ref()
        InsUtils.check_ref_null(clref)
        InsUtils.check_ref_null(clref)
        val = clref.handler.obj.get_field(fieldref.name)
        ftype = InsUtils.get_type_by_descriptor(fieldref.descriptor)
        if ftype == InsUtils.TYPE_INT:
            frame.operand_stack.push_int(val)
        elif ftype == InsUtils.TYPE_FLOAT:
            frame.operand_stack.push_float(val)
        elif ftype == InsUtils.TYPE_LONG:
            frame.operand_stack.push_long(val)
        elif ftype == InsUtils.TYPE_DOUBLE:
            frame.operand_stack.push_double(val)
        elif ftype == InsUtils.TYPE_REF or ftype == InsUtils.TYPE_ARRAY:
            frame.operand_stack.push_ref(val)


class RETURN(Instruction):
    code = 0xb1
    name = 'return'

    def __init__(self):
        super(RETURN, self).__init__()

    def execute(self, frame):
        frame.thread.pop_frame()


class SIPUSH(Instruction):
    code = 0x11
    name = 'sipush'

    def __init__(self):
        super(SIPUSH, self).__init__()
        self.byte = 0

    def read_operands(self, code_parser):
        byte1 = code_parser.read_op()
        byte2 = code_parser.read_op()
        self.byte = (byte1 << 8) | byte2

    def execute(self, frame):
        frame.operand_stack.push_int(ctypes.c_short(self.byte).value)


class SWAP(Instruction):
    code = 0x5f
    name = 'swap'

    def execute(self, frame):
        value1 = frame.operand_stack.pop()


class TABLE_SWITCH(JUMP_INC):
    code = 0xaa
    name = 'tableswitch'

    def __init__(self):
        super(TABLE_SWITCH, self).__init__()
        self.default = 0
        self.low = 0
        self.high = 0
        self.count = 0
        self.offsets = []

    def read_operands(self, code_parser):
        code_parser.skip_padding()

        self.default = code_parser.read_4byte()

        self.low = code_parser.read_4byte()

        self.high = code_parser.read_4byte()

        self.count = self.high - self.low + 1

        for i in range(self.count):
            offset = code_parser.read_4byte()
            self.offsets.append(offset)

    def execute(self, frame):
        index = frame.operand_stack.pop_int()
        if index < self.low or index > self.high:
            self.jump_by(frame, self.default)
        else:
            self.jump_by(frame, self.offsets[index - self.low])


class LOOK_UP_SWITCH(JUMP_INC):
    code = 0xab
    name = 'lookupswitch'

    def __init__(self):
        super(LOOK_UP_SWITCH, self).__init__()
        self.default = 0
        self.npairs = 0
        self.pairs = {}

    def read_operands(self, code_parser):
        code_parser.skip_padding()

        self.default = code_parser.read_4byte()
        self.npairs = code_parser.read_4byte()

        for i in range(self.npairs):
            match = code_parser.read_4byte()
            offset = code_parser.read_4byte()
            self.pairs[match] = offset

    def execute(self, frame):
        pairs = self.pairs
        key = frame.operand_stack.pop_int()
        if key in pairs:
            self.jump_by(frame, pairs[key])
        else:
            self.jump_by(frame, self.default)


class CHECK_CAST(Instruction):
    code = 0xc0
    name = 'checkcast'

    def __init__(self):
        super(CHECK_CAST, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        # TODO: interface 实现
        ref = frame.operand_stack.top()
        if not isinstance(ref, JRef):
            error_handler.rise_runtime_error('checkcast param must be ref')
        cl_ref = frame.method.jclass.constant_pool.constants[self.index]
        name = cl_ref.class_name
        cast_class = ref.handler.obj.jclass
        while cast_class is not None:
            if cast_class.name == name:
                return
            cast_class = cast_class.super_class
        error_handler.rise_class_cast_error()


class INSTANCE_OF(Instruction):
    code = 0xc1
    name = 'instanceof'

    def __init__(self):
        super(INSTANCE_OF, self).__init__()
        self.index = 0

    def read_operands(self, code_parser):
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2

    def execute(self, frame):
        # TODO: interface 实现
        ref = frame.operand_stack.top()
        if not isinstance(ref, JRef):
            error_handler.rise_runtime_error('instance param must be ref')
        cl_ref = frame.method.jclass.constant_pool.constants[self.index]
        name = cl_ref.class_name
        if ref.handler is None:
            frame.operand_stack.push_int(0)
            return
        cast_class = ref.handler.obj.jclass
        while cast_class is not None:
            if cast_class.name == name:
                frame.operand_stack.push_int(1)
                return
            cast_class = cast_class.super_class
        frame.operand_stack.push_int(0)


class WIDE(Instruction):
    code = 0xc4
    name = 'wide'

    def __init__(self):
        super(WIDE, self).__init__()
        self.code = None
        self.index = 0
        self.const = 0
        self.ins = None
        self.type1_code_map = {
            ILOAD.code: ILOAD,
            FLOAD.code: FLOAD,
            ALOAD.code: ALOAD,
            LLOAD.code: LLOAD,
            DLOAD.code: DLOAD,
            ISTORE.code: ISTORE,
            FSTORE.code: FSTORE,
            ASTORE.code: ASTORE,
            LSTORE.code: LSTORE,
            DSTORE.code: DSTORE,
        }
        self.type2_code_map = {
            IINC.code: IINC
        }

    def read_operands(self, code_parser):
        self.code = code_parser.read_op()
        index1 = code_parser.read_op()
        index2 = code_parser.read_op()
        self.index = (index1 << 8) | index2
        if self.code in self.type1_code_map:
            ins = self.type1_code_map[self.code]()
            ins.index = self.index
            self.ins = ins
        elif self.code in self.type2_code_map:
            ins = self.type2_code_map[self.code]()
            ins.index = self.index
            const1 = code_parser.read_op()
            const2 = code_parser.read_op()
            self.const = (const1 << 8) | const2
            ins.const = self.const
            self.ins = ins

    def execute(self, frame):
        self.ins.execute(frame)


class IF_NULL(JUMP_INC):
    code = 0xc6
    name = 'ifnull'

    def __init__(self):
        super(IF_NULL, self).__init__()
        self.branch = 0

    def read_operands(self, code_parser):
        branch1 = code_parser.read_op()
        branch2 = code_parser.read_op()
        self.branch = (branch1 << 8) | branch2

    def execute(self, frame):
        ref = frame.operand_stack.pop_ref()
        if ref is None or ref.handler is None or ref.handler.obj is None:
            self.jump_by(frame, self.branch)


class IF_NON_NULL(JUMP_INC):
    code = 0xc7
    name = 'ifnonnull'

    def __init__(self):
        super(IF_NON_NULL, self).__init__()
        self.branch = 0

    def read_operands(self, code_parser):
        branch1 = code_parser.read_op()
        branch2 = code_parser.read_op()
        self.branch = (branch1 << 8) | branch2

    def execute(self, frame):
        ref = frame.operand_stack.pop_ref()
        if ref is not None and ref.handler is not None and ref.handler.obj is not None:
            self.jump_by(frame, self.branch)


class ATHROW(JUMP_INC):
    code = 0xbf
    name = 'athrow'

    def execute(self, frame):
        thread = frame.thread
        ref = frame.operand_stack.pop_ref()
        exceptions = frame.method.exceptions
        InsUtils.check_ref_null(ref)
        catched = False
        while exceptions is not None:
            for ex in exceptions:
                cl_ref = frame.method.jclass.constant_pool.constants[ex.catch_type]
                jclass = ref.handler.obj.jclass
                while jclass is not None and jclass.name != 'java/lang/Object':
                    if cl_ref.class_name == jclass.name:
                        frame.operand_stack.clear()
                        frame.operand_stack.push_ref(ref)
                        self.jump_to(frame, ex.handler_pc)
                        catched = True
                        break
                    jclass = jclass.super_class
                if catched:
                    break
            if catched:
                break
            thread.pop_frame()
            if not thread.has_frame():
                break
            frame = thread.top_frame()
            exceptions = frame.method.exceptions
        if not catched:
            error_handler.rise_runtime_error('none catched exception: ' + ref.handler.obj.jclass.name)


instruction_cache = dict()


def register_instruction(ins):
    instruction_cache[ins.code] = ins


register_instruction(NOP)
register_instruction(ACONST_NULL)
register_instruction(ALOAD)
register_instruction(ALOAD_0)
register_instruction(ALOAD_1)
register_instruction(ALOAD_2)
register_instruction(ALOAD_3)
register_instruction(ASTORE)
register_instruction(ASTORE_0)
register_instruction(ASTORE_1)
register_instruction(ASTORE_2)
register_instruction(ASTORE_3)
register_instruction(ARETURN)
register_instruction(BIPUSH)
register_instruction(DUP)
register_instruction(DUP_X1)
register_instruction(DUP_X2)
register_instruction(DUP2)
register_instruction(DUP2_X1)
register_instruction(DUP2_X2)
register_instruction(GETSTATIC)
register_instruction(PUTSTATIC)
register_instruction(GOTO)
register_instruction(GOTO_W)
register_instruction(IADD)
register_instruction(LADD)
register_instruction(FADD)
register_instruction(DADD)
register_instruction(ISUB)
register_instruction(FSUB)
register_instruction(LSUB)
register_instruction(DSUB)
register_instruction(IMUL)
register_instruction(LMUL)
register_instruction(FMUL)
register_instruction(DMUL)
register_instruction(IDIV)
register_instruction(LDIV)
register_instruction(FDIV)
register_instruction(DDIV)
register_instruction(IREM)
register_instruction(LREM)
register_instruction(FREM)
register_instruction(DREM)
register_instruction(INEG)
register_instruction(LNEG)
register_instruction(FNEG)
register_instruction(DNEG)
register_instruction(ISHL)
register_instruction(LSHL)
register_instruction(ISHR)
register_instruction(LSHR)
register_instruction(IUSHR)
register_instruction(LUSHR)
register_instruction(IAND)
register_instruction(LAND)
register_instruction(IOR)
register_instruction(LOR)
register_instruction(IXOR)
register_instruction(LXOR)
register_instruction(IINC)
register_instruction(I2L)
register_instruction(I2F)
register_instruction(I2D)
register_instruction(L2I)
register_instruction(L2F)
register_instruction(L2D)
register_instruction(F2I)
register_instruction(F2L)
register_instruction(F2D)
register_instruction(D2I)
register_instruction(D2L)
register_instruction(D2F)
register_instruction(I2B)
register_instruction(I2C)
register_instruction(I2S)
register_instruction(LCMP)
register_instruction(FCMPL)
register_instruction(FCMPD)
register_instruction(DCMPL)
register_instruction(DCMPD)
register_instruction(IFEQ)
register_instruction(IFNE)
register_instruction(IFLT)
register_instruction(IFGE)
register_instruction(IFGT)
register_instruction(IFLE)
register_instruction(ICONST_M1)
register_instruction(ICONST_0)
register_instruction(ICONST_1)
register_instruction(ICONST_2)
register_instruction(ICONST_3)
register_instruction(ICONST_4)
register_instruction(ICONST_5)
register_instruction(LCONST_0)
register_instruction(LCONST_1)
register_instruction(FCONST_0)
register_instruction(FCONST_1)
register_instruction(FCONST_2)
register_instruction(DCONST_0)
register_instruction(DCONST_1)
register_instruction(IF_ICMPEQ)
register_instruction(IF_ICMPGE)
register_instruction(IF_ICMPGT)
register_instruction(IF_ICMPLE)
register_instruction(IF_ICMPLT)
register_instruction(IF_ICMPNE)
register_instruction(IF_ACMPEQ)
register_instruction(IF_ACMPNE)
register_instruction(DLOAD)
register_instruction(DLOAD_0)
register_instruction(DLOAD_1)
register_instruction(DLOAD_2)
register_instruction(DLOAD_3)
register_instruction(FLOAD)
register_instruction(FLOAD_0)
register_instruction(FLOAD_1)
register_instruction(FLOAD_2)
register_instruction(FLOAD_3)
register_instruction(ILOAD)
register_instruction(ILOAD_0)
register_instruction(ILOAD_1)
register_instruction(ILOAD_2)
register_instruction(ILOAD_3)
register_instruction(LLOAD)
register_instruction(LLOAD_0)
register_instruction(LLOAD_1)
register_instruction(LLOAD_2)
register_instruction(LLOAD_3)
register_instruction(DSTORE)
register_instruction(DSTORE_0)
register_instruction(DSTORE_1)
register_instruction(DSTORE_2)
register_instruction(DSTORE_3)
register_instruction(FSTORE)
register_instruction(FSTORE_0)
register_instruction(FSTORE_1)
register_instruction(FSTORE_2)
register_instruction(FSTORE_3)
register_instruction(IASTORE)
register_instruction(IALOAD)
register_instruction(CASTORE)
register_instruction(CALOAD)
register_instruction(DASTORE)
register_instruction(DALOAD)
register_instruction(FASTORE)
register_instruction(FALOAD)
register_instruction(LASTORE)
register_instruction(LALOAD)
register_instruction(SASTORE)
register_instruction(SALOAD)
register_instruction(AASTORE)
register_instruction(AALOAD)
register_instruction(BASTORE)
register_instruction(BALOAD)
register_instruction(ISTORE)
register_instruction(ISTORE_0)
register_instruction(ISTORE_1)
register_instruction(ISTORE_2)
register_instruction(ISTORE_3)
register_instruction(LSTORE)
register_instruction(LSTORE_0)
register_instruction(LSTORE_1)
register_instruction(LSTORE_2)
register_instruction(LSTORE_3)
register_instruction(IMPDEP1)
register_instruction(IMPDEP2)
register_instruction(INVOKESPECIAL)
register_instruction(INVOKESTATIC)
register_instruction(INVOKEVIRTUAL)
register_instruction(RETURN)
register_instruction(IRETURN)
register_instruction(LRETURN)
register_instruction(FRETURN)
register_instruction(DRETURN)
register_instruction(ARETURN)
register_instruction(LDC)
register_instruction(LDC_W)
register_instruction(LDC2_W)
register_instruction(NEW)
register_instruction(NEWARRAY)
register_instruction(ANEWARRAY)
register_instruction(ARRAY_LENGTH)
register_instruction(POP)
register_instruction(POP2)
register_instruction(PUTFIELD)
register_instruction(GETFIELD)
register_instruction(SIPUSH)
register_instruction(SWAP)
register_instruction(TABLE_SWITCH)
register_instruction(LOOK_UP_SWITCH)
register_instruction(CHECK_CAST)
register_instruction(INSTANCE_OF)
register_instruction(WIDE)
register_instruction(IF_NULL)
register_instruction(IF_NON_NULL)
register_instruction(ATHROW)


def get_instruction(code):
    if code in instruction_cache:
        return instruction_cache[code]()
    error_handler.rise_runtime_error('sorry! code %x is not realized!' % code)


if __name__ == '__main__':
    print(sorted(instruction_cache))
    for code in range(255):
        if code not in instruction_cache:
            print('%x' % code)