{-# LANGUAGE DeriveAnyClass     #-}
{-# LANGUAGE DeriveGeneric      #-}
{-# LANGUAGE FlexibleInstances  #-}
{-# LANGUAGE OverloadedStrings  #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell    #-}
{-|
Module      : Language.JVM.Attribute.LineNumberTable
Copyright   : (c) Christian Gram Kalhauge, 2018
License     : MIT
Maintainer  : kalhuage@cs.ucla.edu

Based on the LineNumberTable Attribute,
as documented [here](http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.12).
-}

module Language.JVM.Attribute.LineNumberTable
  ( LineNumberTable (..)
  , LineNumber

  , BinaryFormat
  -- * Helper functions
  , linenumber
  , linenumber'
  ) where

import           Control.DeepSeq             (NFData)
import           Data.Binary
import qualified Data.IntMap as IM
import           GHC.Generics                (Generic)
import           Language.JVM.Attribute.Base
import           Language.JVM.ByteCode
import           Language.JVM.Utils
import           Language.JVM.Staged

-- | 'Signature' is an Attribute.
instance IsAttribute (LineNumberTable Low) where
  attrName :: Const Text (LineNumberTable Low)
attrName = Text -> Const Text (LineNumberTable Low)
forall k a (b :: k). a -> Const a b
Const Text
"LineNumberTable"

type LineNumber = Word16

-- | The 'LineNumberTable' is just a mapping from offsets to linenumbers.
newtype LineNumberTable r = LineNumberTable
  { LineNumberTable r -> IntMap LineNumber
lineNumberTable :: IM.IntMap LineNumber
  } deriving (Int -> LineNumberTable r -> ShowS
[LineNumberTable r] -> ShowS
LineNumberTable r -> String
(Int -> LineNumberTable r -> ShowS)
-> (LineNumberTable r -> String)
-> ([LineNumberTable r] -> ShowS)
-> Show (LineNumberTable r)
forall r. Int -> LineNumberTable r -> ShowS
forall r. [LineNumberTable r] -> ShowS
forall r. LineNumberTable r -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LineNumberTable r] -> ShowS
$cshowList :: forall r. [LineNumberTable r] -> ShowS
show :: LineNumberTable r -> String
$cshow :: forall r. LineNumberTable r -> String
showsPrec :: Int -> LineNumberTable r -> ShowS
$cshowsPrec :: forall r. Int -> LineNumberTable r -> ShowS
Show, LineNumberTable r -> LineNumberTable r -> Bool
(LineNumberTable r -> LineNumberTable r -> Bool)
-> (LineNumberTable r -> LineNumberTable r -> Bool)
-> Eq (LineNumberTable r)
forall r. LineNumberTable r -> LineNumberTable r -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: LineNumberTable r -> LineNumberTable r -> Bool
$c/= :: forall r. LineNumberTable r -> LineNumberTable r -> Bool
== :: LineNumberTable r -> LineNumberTable r -> Bool
$c== :: forall r. LineNumberTable r -> LineNumberTable r -> Bool
Eq, Eq (LineNumberTable r)
Eq (LineNumberTable r)
-> (LineNumberTable r -> LineNumberTable r -> Ordering)
-> (LineNumberTable r -> LineNumberTable r -> Bool)
-> (LineNumberTable r -> LineNumberTable r -> Bool)
-> (LineNumberTable r -> LineNumberTable r -> Bool)
-> (LineNumberTable r -> LineNumberTable r -> Bool)
-> (LineNumberTable r -> LineNumberTable r -> LineNumberTable r)
-> (LineNumberTable r -> LineNumberTable r -> LineNumberTable r)
-> Ord (LineNumberTable r)
LineNumberTable r -> LineNumberTable r -> Bool
LineNumberTable r -> LineNumberTable r -> Ordering
LineNumberTable r -> LineNumberTable r -> LineNumberTable r
forall r. Eq (LineNumberTable r)
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall r. LineNumberTable r -> LineNumberTable r -> Bool
forall r. LineNumberTable r -> LineNumberTable r -> Ordering
forall r.
LineNumberTable r -> LineNumberTable r -> LineNumberTable r
min :: LineNumberTable r -> LineNumberTable r -> LineNumberTable r
$cmin :: forall r.
LineNumberTable r -> LineNumberTable r -> LineNumberTable r
max :: LineNumberTable r -> LineNumberTable r -> LineNumberTable r
$cmax :: forall r.
LineNumberTable r -> LineNumberTable r -> LineNumberTable r
>= :: LineNumberTable r -> LineNumberTable r -> Bool
$c>= :: forall r. LineNumberTable r -> LineNumberTable r -> Bool
> :: LineNumberTable r -> LineNumberTable r -> Bool
$c> :: forall r. LineNumberTable r -> LineNumberTable r -> Bool
<= :: LineNumberTable r -> LineNumberTable r -> Bool
$c<= :: forall r. LineNumberTable r -> LineNumberTable r -> Bool
< :: LineNumberTable r -> LineNumberTable r -> Bool
$c< :: forall r. LineNumberTable r -> LineNumberTable r -> Bool
compare :: LineNumberTable r -> LineNumberTable r -> Ordering
$ccompare :: forall r. LineNumberTable r -> LineNumberTable r -> Ordering
$cp1Ord :: forall r. Eq (LineNumberTable r)
Ord, (forall x. LineNumberTable r -> Rep (LineNumberTable r) x)
-> (forall x. Rep (LineNumberTable r) x -> LineNumberTable r)
-> Generic (LineNumberTable r)
forall x. Rep (LineNumberTable r) x -> LineNumberTable r
forall x. LineNumberTable r -> Rep (LineNumberTable r) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall r x. Rep (LineNumberTable r) x -> LineNumberTable r
forall r x. LineNumberTable r -> Rep (LineNumberTable r) x
$cto :: forall r x. Rep (LineNumberTable r) x -> LineNumberTable r
$cfrom :: forall r x. LineNumberTable r -> Rep (LineNumberTable r) x
Generic, LineNumberTable r -> ()
(LineNumberTable r -> ()) -> NFData (LineNumberTable r)
forall r. LineNumberTable r -> ()
forall a. (a -> ()) -> NFData a
rnf :: LineNumberTable r -> ()
$crnf :: forall r. LineNumberTable r -> ()
NFData)


instance ByteCodeStaged LineNumberTable where
  evolveBC :: (LineNumber -> m Int)
-> LineNumberTable Low -> m (LineNumberTable High)
evolveBC LineNumber -> m Int
f (LineNumberTable IntMap LineNumber
mp) =
    IntMap LineNumber -> LineNumberTable High
forall r. IntMap LineNumber -> LineNumberTable r
LineNumberTable
    (IntMap LineNumber -> LineNumberTable High)
-> ([(Int, LineNumber)] -> IntMap LineNumber)
-> [(Int, LineNumber)]
-> LineNumberTable High
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Int, LineNumber)] -> IntMap LineNumber
forall a. [(Int, a)] -> IntMap a
IM.fromList ([(Int, LineNumber)] -> LineNumberTable High)
-> m [(Int, LineNumber)] -> m (LineNumberTable High)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (((Int, LineNumber) -> m (Int, LineNumber))
-> [(Int, LineNumber)] -> m [(Int, LineNumber)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (\(Int
x, LineNumber
y) -> do Int
x' <- LineNumber -> m Int
f (Int -> LineNumber
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
x); (Int, LineNumber) -> m (Int, LineNumber)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int
x', LineNumber
y)) ([(Int, LineNumber)] -> m [(Int, LineNumber)])
-> (IntMap LineNumber -> [(Int, LineNumber)])
-> IntMap LineNumber
-> m [(Int, LineNumber)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IntMap LineNumber -> [(Int, LineNumber)]
forall a. IntMap a -> [(Int, a)]
IM.toList) IntMap LineNumber
mp

  devolveBC :: (Int -> m LineNumber)
-> LineNumberTable High -> m (LineNumberTable Low)
devolveBC Int -> m LineNumber
f (LineNumberTable IntMap LineNumber
mp) =
    IntMap LineNumber -> LineNumberTable Low
forall r. IntMap LineNumber -> LineNumberTable r
LineNumberTable
    (IntMap LineNumber -> LineNumberTable Low)
-> ([(Int, LineNumber)] -> IntMap LineNumber)
-> [(Int, LineNumber)]
-> LineNumberTable Low
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Int, LineNumber)] -> IntMap LineNumber
forall a. [(Int, a)] -> IntMap a
IM.fromList ([(Int, LineNumber)] -> LineNumberTable Low)
-> m [(Int, LineNumber)] -> m (LineNumberTable Low)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (((Int, LineNumber) -> m (Int, LineNumber))
-> [(Int, LineNumber)] -> m [(Int, LineNumber)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM (\(Int
x, LineNumber
y) -> do LineNumber
x' <- Int -> m LineNumber
f Int
x; (Int, LineNumber) -> m (Int, LineNumber)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (LineNumber -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral LineNumber
x', LineNumber
y)) ([(Int, LineNumber)] -> m [(Int, LineNumber)])
-> (IntMap LineNumber -> [(Int, LineNumber)])
-> IntMap LineNumber
-> m [(Int, LineNumber)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IntMap LineNumber -> [(Int, LineNumber)]
forall a. IntMap a -> [(Int, a)]
IM.toList) IntMap LineNumber
mp

-- | Returns the line number of an offset.
linenumber :: Int -> LineNumberTable r -> Maybe LineNumber
linenumber :: Int -> LineNumberTable r -> Maybe LineNumber
linenumber Int
i (LineNumberTable IntMap LineNumber
t) =
  (Int, LineNumber) -> LineNumber
forall a b. (a, b) -> b
snd ((Int, LineNumber) -> LineNumber)
-> Maybe (Int, LineNumber) -> Maybe LineNumber
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int -> IntMap LineNumber -> Maybe (Int, LineNumber)
forall a. Int -> IntMap a -> Maybe (Int, a)
IM.lookupLE Int
i IntMap LineNumber
t

-- | Returns the line number of an offset. Helper function that also
-- does the conversion from integral to int.
linenumber' :: Integral a => a -> LineNumberTable r -> Maybe LineNumber
linenumber' :: a -> LineNumberTable r -> Maybe LineNumber
linenumber' a
i = Int -> LineNumberTable r -> Maybe LineNumber
forall r. Int -> LineNumberTable r -> Maybe LineNumber
linenumber (a -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
i)

type BinaryFormat = SizedList16 (Word16, LineNumber)

instance Binary (LineNumberTable Low) where
  get :: Get (LineNumberTable Low)
get = do
    IntMap LineNumber -> LineNumberTable Low
forall r. IntMap LineNumber -> LineNumberTable r
LineNumberTable (IntMap LineNumber -> LineNumberTable Low)
-> (SizedList LineNumber (LineNumber, LineNumber)
    -> IntMap LineNumber)
-> SizedList LineNumber (LineNumber, LineNumber)
-> LineNumberTable Low
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Int, LineNumber)] -> IntMap LineNumber
forall a. [(Int, a)] -> IntMap a
IM.fromList ([(Int, LineNumber)] -> IntMap LineNumber)
-> (SizedList LineNumber (LineNumber, LineNumber)
    -> [(Int, LineNumber)])
-> SizedList LineNumber (LineNumber, LineNumber)
-> IntMap LineNumber
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((LineNumber, LineNumber) -> (Int, LineNumber))
-> [(LineNumber, LineNumber)] -> [(Int, LineNumber)]
forall a b. (a -> b) -> [a] -> [b]
map (LineNumber, LineNumber) -> (Int, LineNumber)
forall a a b. (Integral a, Num a) => (a, b) -> (a, b)
f ([(LineNumber, LineNumber)] -> [(Int, LineNumber)])
-> (SizedList LineNumber (LineNumber, LineNumber)
    -> [(LineNumber, LineNumber)])
-> SizedList LineNumber (LineNumber, LineNumber)
-> [(Int, LineNumber)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SizedList LineNumber (LineNumber, LineNumber)
-> [(LineNumber, LineNumber)]
forall w a. SizedList w a -> [a]
unSizedList (SizedList LineNumber (LineNumber, LineNumber)
 -> LineNumberTable Low)
-> Get (SizedList LineNumber (LineNumber, LineNumber))
-> Get (LineNumberTable Low)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Get (SizedList LineNumber (LineNumber, LineNumber))
forall t. Binary t => Get t
get :: Get BinaryFormat)
    where
      f :: (a, b) -> (a, b)
f (a
a,b
b) = (a -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
a, b
b)
  put :: LineNumberTable Low -> Put
put (LineNumberTable IntMap LineNumber
x) = do
    SizedList LineNumber (LineNumber, LineNumber) -> Put
forall t. Binary t => t -> Put
put SizedList LineNumber (LineNumber, LineNumber)
sl
    where
      sl :: BinaryFormat
      sl :: SizedList LineNumber (LineNumber, LineNumber)
sl = [(LineNumber, LineNumber)]
-> SizedList LineNumber (LineNumber, LineNumber)
forall w a. [a] -> SizedList w a
SizedList ([(LineNumber, LineNumber)]
 -> SizedList LineNumber (LineNumber, LineNumber))
-> (IntMap LineNumber -> [(LineNumber, LineNumber)])
-> IntMap LineNumber
-> SizedList LineNumber (LineNumber, LineNumber)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, LineNumber) -> (LineNumber, LineNumber))
-> [(Int, LineNumber)] -> [(LineNumber, LineNumber)]
forall a b. (a -> b) -> [a] -> [b]
map (Int, LineNumber) -> (LineNumber, LineNumber)
forall a a b. (Integral a, Num a) => (a, b) -> (a, b)
f ([(Int, LineNumber)] -> [(LineNumber, LineNumber)])
-> (IntMap LineNumber -> [(Int, LineNumber)])
-> IntMap LineNumber
-> [(LineNumber, LineNumber)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. IntMap LineNumber -> [(Int, LineNumber)]
forall a. IntMap a -> [(Int, a)]
IM.toList (IntMap LineNumber
 -> SizedList LineNumber (LineNumber, LineNumber))
-> IntMap LineNumber
-> SizedList LineNumber (LineNumber, LineNumber)
forall a b. (a -> b) -> a -> b
$ IntMap LineNumber
x
      f :: (a, b) -> (a, b)
f (a
a,b
b) = (a -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
a, b
b)