{-# LANGUAGE DeriveGeneric #-}
module Data.Dwarf.OP where

import           Control.Applicative (Applicative(..), (<$>))
import           Data.Binary.Get (getWord8, Get)
import qualified Data.ByteString as B
import           Data.Dwarf.Reader
import           Data.Dwarf.Utils
import           Data.Int (Int8, Int16, Int32, Int64)
import           Data.Word (Word8, Word16, Word32, Word64)
import           GHC.Generics (Generic)
import           TextShow (TextShow(..))
import           TextShow.Generic (genericShowbPrec)

data DW_OP
    = DW_OP_addr Word64
    | DW_OP_deref
    | DW_OP_const1u Word8
    | DW_OP_const1s Int8
    | DW_OP_const2u Word16
    | DW_OP_const2s Int16
    | DW_OP_const4u Word32
    | DW_OP_const4s Int32
    | DW_OP_const8u Word64
    | DW_OP_const8s Int64
    | DW_OP_constu  Word64
    | DW_OP_consts  Int64
    | DW_OP_dup
    | DW_OP_drop
    | DW_OP_over
    | DW_OP_pick Word8
    | DW_OP_swap
    | DW_OP_rot
    | DW_OP_xderef
    | DW_OP_abs
    | DW_OP_and
    | DW_OP_div
    | DW_OP_minus
    | DW_OP_mod
    | DW_OP_mul
    | DW_OP_neg
    | DW_OP_not
    | DW_OP_or
    | DW_OP_plus
    | DW_OP_plus_uconst Word64
    | DW_OP_shl
    | DW_OP_shr
    | DW_OP_shra
    | DW_OP_xor
    | DW_OP_skip Int16
    | DW_OP_bra Int16
    | DW_OP_eq
    | DW_OP_ge
    | DW_OP_gt
    | DW_OP_le
    | DW_OP_lt
    | DW_OP_ne
    | DW_OP_lit Int
    | DW_OP_reg Int
    | DW_OP_breg Int Int64
    | DW_OP_regx Word64
    | DW_OP_fbreg Int64
    | DW_OP_bregx Word64 Int64
    | DW_OP_piece Word64
    | DW_OP_deref_size Word8
    | DW_OP_xderef_size Word8
    | DW_OP_nop
    | DW_OP_push_object_address
    | DW_OP_call2 Word16
    | DW_OP_call4 Word32
    | DW_OP_call_ref Word64
    | DW_OP_form_tls_address
    | DW_OP_call_frame_cfa
    | DW_OP_bit_piece Word64 Word64
    deriving (Eq, Ord, Read, Show, Generic)

instance TextShow DW_OP where showbPrec = genericShowbPrec

-- | Parse a ByteString into a DWARF opcode. This will be needed for further decoding of DIE attributes.
parseDW_OP :: Reader -> B.ByteString -> DW_OP
parseDW_OP = strictGet . getDW_OP
getDW_OP :: Reader -> Get DW_OP
getDW_OP dr = getWord8 >>= getDW_OP_
  where
    getDW_OP_ :: Word8 -> Get DW_OP
    getDW_OP_ 0x03 = pure DW_OP_addr <*> drGetTargetAddress dr
    getDW_OP_ 0x06 = pure DW_OP_deref
    getDW_OP_ 0x08 = pure DW_OP_const1u <*>                  getWord8
    getDW_OP_ 0x09 = pure DW_OP_const1s <*> fromIntegral <$> getWord8
    getDW_OP_ 0x0a = pure DW_OP_const2u <*>                  drGetW16 dr
    getDW_OP_ 0x0b = pure DW_OP_const2s <*> fromIntegral <$> drGetW16 dr
    getDW_OP_ 0x0c = pure DW_OP_const4u <*>                  drGetW32 dr
    getDW_OP_ 0x0d = pure DW_OP_const4s <*> fromIntegral <$> drGetW32 dr
    getDW_OP_ 0x0e = pure DW_OP_const8u <*> drGetW64 dr
    getDW_OP_ 0x0f = pure DW_OP_const8s <*> fromIntegral <$> drGetW64 dr
    getDW_OP_ 0x10 = pure DW_OP_constu  <*> getULEB128
    getDW_OP_ 0x11 = pure DW_OP_consts  <*> getSLEB128
    getDW_OP_ 0x12 = pure DW_OP_dup
    getDW_OP_ 0x13 = pure DW_OP_drop
    getDW_OP_ 0x14 = pure DW_OP_over
    getDW_OP_ 0x15 = pure DW_OP_pick <*> getWord8
    getDW_OP_ 0x16 = pure DW_OP_swap
    getDW_OP_ 0x17 = pure DW_OP_rot
    getDW_OP_ 0x18 = pure DW_OP_xderef
    getDW_OP_ 0x19 = pure DW_OP_abs
    getDW_OP_ 0x1a = pure DW_OP_and
    getDW_OP_ 0x1b = pure DW_OP_div
    getDW_OP_ 0x1c = pure DW_OP_minus
    getDW_OP_ 0x1d = pure DW_OP_mod
    getDW_OP_ 0x1e = pure DW_OP_mul
    getDW_OP_ 0x1f = pure DW_OP_neg
    getDW_OP_ 0x20 = pure DW_OP_not
    getDW_OP_ 0x21 = pure DW_OP_or
    getDW_OP_ 0x22 = pure DW_OP_plus
    getDW_OP_ 0x23 = pure DW_OP_plus_uconst <*> getULEB128
    getDW_OP_ 0x24 = pure DW_OP_shl
    getDW_OP_ 0x25 = pure DW_OP_shr
    getDW_OP_ 0x26 = pure DW_OP_shra
    getDW_OP_ 0x27 = pure DW_OP_xor
    getDW_OP_ 0x2f = pure DW_OP_skip <*> fromIntegral <$> drGetW16 dr
    getDW_OP_ 0x28 = pure DW_OP_bra  <*> fromIntegral <$> drGetW16 dr
    getDW_OP_ 0x29 = pure DW_OP_eq
    getDW_OP_ 0x2a = pure DW_OP_ge
    getDW_OP_ 0x2b = pure DW_OP_gt
    getDW_OP_ 0x2c = pure DW_OP_le
    getDW_OP_ 0x2d = pure DW_OP_lt
    getDW_OP_ 0x2e = pure DW_OP_ne
    getDW_OP_ 0x30 = pure $ DW_OP_lit 0
    getDW_OP_ 0x31 = pure $ DW_OP_lit 1
    getDW_OP_ 0x32 = pure $ DW_OP_lit 2
    getDW_OP_ 0x33 = pure $ DW_OP_lit 3
    getDW_OP_ 0x34 = pure $ DW_OP_lit 4
    getDW_OP_ 0x35 = pure $ DW_OP_lit 5
    getDW_OP_ 0x36 = pure $ DW_OP_lit 6
    getDW_OP_ 0x37 = pure $ DW_OP_lit 7
    getDW_OP_ 0x38 = pure $ DW_OP_lit 8
    getDW_OP_ 0x39 = pure $ DW_OP_lit 9
    getDW_OP_ 0x3a = pure $ DW_OP_lit 10
    getDW_OP_ 0x3b = pure $ DW_OP_lit 11
    getDW_OP_ 0x3c = pure $ DW_OP_lit 12
    getDW_OP_ 0x3d = pure $ DW_OP_lit 13
    getDW_OP_ 0x3e = pure $ DW_OP_lit 14
    getDW_OP_ 0x3f = pure $ DW_OP_lit 15
    getDW_OP_ 0x40 = pure $ DW_OP_lit 16
    getDW_OP_ 0x41 = pure $ DW_OP_lit 17
    getDW_OP_ 0x42 = pure $ DW_OP_lit 18
    getDW_OP_ 0x43 = pure $ DW_OP_lit 19
    getDW_OP_ 0x44 = pure $ DW_OP_lit 20
    getDW_OP_ 0x45 = pure $ DW_OP_lit 21
    getDW_OP_ 0x46 = pure $ DW_OP_lit 22
    getDW_OP_ 0x47 = pure $ DW_OP_lit 23
    getDW_OP_ 0x48 = pure $ DW_OP_lit 24
    getDW_OP_ 0x49 = pure $ DW_OP_lit 25
    getDW_OP_ 0x4a = pure $ DW_OP_lit 26
    getDW_OP_ 0x4b = pure $ DW_OP_lit 27
    getDW_OP_ 0x4c = pure $ DW_OP_lit 28
    getDW_OP_ 0x4d = pure $ DW_OP_lit 29
    getDW_OP_ 0x4e = pure $ DW_OP_lit 30
    getDW_OP_ 0x4f = pure $ DW_OP_lit 31
    getDW_OP_ 0x50 = pure $ DW_OP_reg 0
    getDW_OP_ 0x51 = pure $ DW_OP_reg 1
    getDW_OP_ 0x52 = pure $ DW_OP_reg 2
    getDW_OP_ 0x53 = pure $ DW_OP_reg 3
    getDW_OP_ 0x54 = pure $ DW_OP_reg 4
    getDW_OP_ 0x55 = pure $ DW_OP_reg 5
    getDW_OP_ 0x56 = pure $ DW_OP_reg 6
    getDW_OP_ 0x57 = pure $ DW_OP_reg 7
    getDW_OP_ 0x58 = pure $ DW_OP_reg 8
    getDW_OP_ 0x59 = pure $ DW_OP_reg 9
    getDW_OP_ 0x5a = pure $ DW_OP_reg 10
    getDW_OP_ 0x5b = pure $ DW_OP_reg 11
    getDW_OP_ 0x5c = pure $ DW_OP_reg 12
    getDW_OP_ 0x5d = pure $ DW_OP_reg 13
    getDW_OP_ 0x5e = pure $ DW_OP_reg 14
    getDW_OP_ 0x5f = pure $ DW_OP_reg 15
    getDW_OP_ 0x60 = pure $ DW_OP_reg 16
    getDW_OP_ 0x61 = pure $ DW_OP_reg 17
    getDW_OP_ 0x62 = pure $ DW_OP_reg 18
    getDW_OP_ 0x63 = pure $ DW_OP_reg 19
    getDW_OP_ 0x64 = pure $ DW_OP_reg 20
    getDW_OP_ 0x65 = pure $ DW_OP_reg 21
    getDW_OP_ 0x66 = pure $ DW_OP_reg 22
    getDW_OP_ 0x67 = pure $ DW_OP_reg 23
    getDW_OP_ 0x68 = pure $ DW_OP_reg 24
    getDW_OP_ 0x69 = pure $ DW_OP_reg 25
    getDW_OP_ 0x6a = pure $ DW_OP_reg 26
    getDW_OP_ 0x6b = pure $ DW_OP_reg 27
    getDW_OP_ 0x6c = pure $ DW_OP_reg 28
    getDW_OP_ 0x6d = pure $ DW_OP_reg 29
    getDW_OP_ 0x6e = pure $ DW_OP_reg 30
    getDW_OP_ 0x6f = pure $ DW_OP_reg 31
    getDW_OP_ 0x70 = pure (DW_OP_breg 0)  <*> getSLEB128
    getDW_OP_ 0x71 = pure (DW_OP_breg 1)  <*> getSLEB128
    getDW_OP_ 0x72 = pure (DW_OP_breg 2)  <*> getSLEB128
    getDW_OP_ 0x73 = pure (DW_OP_breg 3)  <*> getSLEB128
    getDW_OP_ 0x74 = pure (DW_OP_breg 4)  <*> getSLEB128
    getDW_OP_ 0x75 = pure (DW_OP_breg 5)  <*> getSLEB128
    getDW_OP_ 0x76 = pure (DW_OP_breg 6)  <*> getSLEB128
    getDW_OP_ 0x77 = pure (DW_OP_breg 7)  <*> getSLEB128
    getDW_OP_ 0x78 = pure (DW_OP_breg 8)  <*> getSLEB128
    getDW_OP_ 0x79 = pure (DW_OP_breg 9)  <*> getSLEB128
    getDW_OP_ 0x7a = pure (DW_OP_breg 10) <*> getSLEB128
    getDW_OP_ 0x7b = pure (DW_OP_breg 11) <*> getSLEB128
    getDW_OP_ 0x7c = pure (DW_OP_breg 12) <*> getSLEB128
    getDW_OP_ 0x7d = pure (DW_OP_breg 13) <*> getSLEB128
    getDW_OP_ 0x7e = pure (DW_OP_breg 14) <*> getSLEB128
    getDW_OP_ 0x7f = pure (DW_OP_breg 15) <*> getSLEB128
    getDW_OP_ 0x80 = pure (DW_OP_breg 16) <*> getSLEB128
    getDW_OP_ 0x81 = pure (DW_OP_breg 17) <*> getSLEB128
    getDW_OP_ 0x82 = pure (DW_OP_breg 18) <*> getSLEB128
    getDW_OP_ 0x83 = pure (DW_OP_breg 19) <*> getSLEB128
    getDW_OP_ 0x84 = pure (DW_OP_breg 20) <*> getSLEB128
    getDW_OP_ 0x85 = pure (DW_OP_breg 21) <*> getSLEB128
    getDW_OP_ 0x86 = pure (DW_OP_breg 22) <*> getSLEB128
    getDW_OP_ 0x87 = pure (DW_OP_breg 23) <*> getSLEB128
    getDW_OP_ 0x88 = pure (DW_OP_breg 24) <*> getSLEB128
    getDW_OP_ 0x89 = pure (DW_OP_breg 25) <*> getSLEB128
    getDW_OP_ 0x8a = pure (DW_OP_breg 26) <*> getSLEB128
    getDW_OP_ 0x8b = pure (DW_OP_breg 27) <*> getSLEB128
    getDW_OP_ 0x8c = pure (DW_OP_breg 28) <*> getSLEB128
    getDW_OP_ 0x8d = pure (DW_OP_breg 29) <*> getSLEB128
    getDW_OP_ 0x8e = pure (DW_OP_breg 30) <*> getSLEB128
    getDW_OP_ 0x8f = pure (DW_OP_breg 31) <*> getSLEB128
    getDW_OP_ 0x90 = pure DW_OP_regx   <*> getULEB128
    getDW_OP_ 0x91 = pure DW_OP_fbreg  <*> getSLEB128
    getDW_OP_ 0x92 = pure DW_OP_bregx  <*> getULEB128 <*> getSLEB128
    getDW_OP_ 0x93 = pure DW_OP_piece  <*> getULEB128
    getDW_OP_ 0x94 = pure DW_OP_deref_size <*> getWord8
    getDW_OP_ 0x95 = pure DW_OP_xderef_size <*> getWord8
    getDW_OP_ 0x96 = pure DW_OP_nop
    getDW_OP_ 0x97 = pure DW_OP_push_object_address
    getDW_OP_ 0x98 = pure DW_OP_call2 <*> drGetW16 dr
    getDW_OP_ 0x99 = pure DW_OP_call4 <*> drGetW32 dr
    getDW_OP_ 0x9a = pure DW_OP_call_ref <*> drGetTargetAddress dr
    getDW_OP_ 0x9b = pure DW_OP_form_tls_address
    getDW_OP_ 0x9c = pure DW_OP_call_frame_cfa
    getDW_OP_ 0x9d = pure DW_OP_bit_piece <*> getULEB128 <*> getULEB128
    getDW_OP_ n | 0xe0 <= n && n <= 0xff = fail $ "User DW_OP data requires extension of parser for code " ++ show n
    getDW_OP_ n = fail $ "Unrecognized DW_OP code " ++ show n