{-# LANGUAGE DefaultSignatures #-}

-- | Types and type synonyms for raw JVM data
module JVM.Data.Raw.Types where

import Data.Binary
import Data.Bits (shiftR)
import Data.Text (Text)

type U1 = Word8

type U2 = Word16

type U4 = Word32

type U8 = Word64

type JVMInt = U4

type JVMFloat = Float

type JVMLong = U8

type JVMDouble = Double

type JVMString = Text

-- | An offset for an instruction, used for jumps
type InstOffsetBytes = U2

{- | Converts an 'InstOffsetBytes' to a pair of bytes, encoding it a way that the JVM will correctly interpret it
 In other words, this function does the opposite of @(branchbyte1 << 8) | branchbyte2@
 >>> instOffsetBytesToU2 39
 (0,39)
-}
instOffsetBytesToU2 :: InstOffsetBytes -> (U1, U1)
instOffsetBytesToU2 :: InstOffsetBytes -> (U1, U1)
instOffsetBytesToU2 InstOffsetBytes
value = (InstOffsetBytes -> U1
forall a b. (Integral a, Num b) => a -> b
fromIntegral (InstOffsetBytes
value InstOffsetBytes -> Int -> InstOffsetBytes
forall a. Bits a => a -> Int -> a
`shiftR` Int
8), InstOffsetBytes -> U1
forall a b. (Integral a, Num b) => a -> b
fromIntegral InstOffsetBytes
value)

-- | A constant pool index
type ConstantPoolIndex = U2

-- | An "array type code" for the `newarray` instruction
type ArrayType = U1

-- | Conversions between numbers that will never overflow or underflow
class SafeNumConvert a b where
    safeNumConvert :: a -> b

instance SafeNumConvert U1 Int where
    safeNumConvert :: U1 -> Int
safeNumConvert = U1 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance SafeNumConvert U2 Int where
    safeNumConvert :: InstOffsetBytes -> Int
safeNumConvert = InstOffsetBytes -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance SafeNumConvert U4 Int where
    safeNumConvert :: U4 -> Int
safeNumConvert = U4 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance SafeNumConvert U1 U2 where
    safeNumConvert :: U1 -> InstOffsetBytes
safeNumConvert = U1 -> InstOffsetBytes
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance SafeNumConvert U2 U4 where
    safeNumConvert :: InstOffsetBytes -> U4
safeNumConvert = InstOffsetBytes -> U4
forall a b. (Integral a, Num b) => a -> b
fromIntegral

instance SafeNumConvert U1 U4 where
    safeNumConvert :: U1 -> U4
safeNumConvert = U1 -> U4
forall a b. (Integral a, Num b) => a -> b
fromIntegral

class (SafeNumConvert b a, Integral a, Integral b) => UnsafeNumConvert a b where
    unsafeNumConvert :: a -> Maybe b
    default unsafeNumConvert :: (Bounded b) => a -> Maybe b
    unsafeNumConvert a
x
        | forall a b. (Integral a, Num b) => a -> b
fromIntegral @a @Integer a
x Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
>= b -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (b
forall a. Bounded a => a
minBound :: b)
            Bool -> Bool -> Bool
&& forall a b. (Integral a, Num b) => a -> b
fromIntegral @a @Integer a
x
                Integer -> Integer -> Bool
forall a. Ord a => a -> a -> Bool
<= b -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral (b
forall a. Bounded a => a
maxBound :: b) =
            b -> Maybe b
forall a. a -> Maybe a
Just (a -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
x)
        | Bool
otherwise = Maybe b
forall a. Maybe a
Nothing

instance UnsafeNumConvert Int U1

instance UnsafeNumConvert Int U2

instance UnsafeNumConvert U2 U1