{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
module Language.JVM.Attribute.Base
( Attribute (..)
, aInfo
, toAttribute
, toBCAttribute
, devolveAttribute
, fromAttribute'
, toAttribute'
, IsAttribute (..)
, Attributes
, fromAttributes
, collect
, collectBC
, AttributeCollector (..)
, ByteCodeAttributeCollector (..)
, firstOne
, Const (..)
) where
import Data.Monoid
import Control.Monad
import Control.Applicative
import Data.Maybe
import Data.Bifunctor
import qualified Data.List as List
import Data.Binary
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as Text
import Language.JVM.Staged
import Language.JVM.ByteCode
import Language.JVM.Utils
firstOne :: [a] -> Maybe a
firstOne as = fst <$> List.uncons as
data Attribute r = Attribute
{ aName :: ! (Ref Text.Text r)
, aInfo' :: ! SizedByteString32
}
aInfo :: Attribute r -> BS.ByteString
aInfo = unSizedByteString . aInfo'
instance Staged Attribute where
evolve (Attribute an ai) = do
an' <- link an
return $ Attribute an' ai
devolve (Attribute an ai) = do
an' <- unlink an
return $ Attribute an' ai
$(deriveBaseWithBinary ''Attribute)
type Attributes b r = Choice (SizedList16 (Attribute r)) (b r) r
class (Binary a) => IsAttribute a where
attrName :: Const Text.Text a
fromAttribute' :: IsAttribute a => Attribute r -> Either String a
fromAttribute' = readFromStrict
toAttribute' :: forall a. IsAttribute a => a -> Attribute High
toAttribute' a =
let name = getConst (attrName :: Const Text.Text a)
bytes = encode a
in Attribute name (SizedByteString . BL.toStrict $ bytes)
toAttribute :: (IsAttribute (a Low), Staged a, DevolveM m) => a High -> m (Attribute Low)
toAttribute =
devolveAttribute devolve
toBCAttribute ::
(IsAttribute (a Low), ByteCodeStaged a, DevolveM m)
=> (ByteCodeIndex -> m ByteCodeOffset)
-> a High
-> m (Attribute Low)
toBCAttribute bcde =
devolveAttribute (devolveBC bcde)
devolveAttribute :: (IsAttribute (a Low), DevolveM m) => (a High -> m (a Low)) -> a High -> m (Attribute Low)
devolveAttribute f a = do
a' <- f a
devolve $ toAttribute' a'
fromAttribute ::
forall a m. (IsAttribute (a Low), Staged a, EvolveM m)
=> Attribute High
-> Maybe (m (a High))
fromAttribute as =
if aName as == getConst (attrName :: Const Text.Text (a Low))
then Just . label (Text.unpack $ aName as) . either evolveError evolve $ fromAttribute' as
else Nothing
fromBCAttribute ::
forall a m. (IsAttribute (a Low), ByteCodeStaged a, EvolveM m)
=> (ByteCodeOffset -> m ByteCodeIndex)
-> Attribute High
-> Maybe (m (a High))
fromBCAttribute fn as =
if aName as == getConst (attrName :: Const Text.Text (a Low))
then Just . label (Text.unpack $ aName as) . either evolveError (evolveBC fn) $ fromAttribute' as
else Nothing
collect ::
forall c m. (EvolveM m)
=> [AttributeCollector c]
-> (Attribute High -> c -> c)
-> Attribute High
-> m (Endo c)
collect options def attr =
fromMaybe (return $ Endo (def attr))
. msum
$ (\(Attr fn) -> fmap (Endo . fn) <$> fromAttribute attr) <$> options
data AttributeCollector c
= forall a. (IsAttribute (a Low), Staged a)
=> Attr (a High -> c -> c)
collectBC ::
forall c m. (EvolveM m)
=> (ByteCodeOffset -> m ByteCodeIndex)
-> [ByteCodeAttributeCollector c]
-> (Attribute High -> c -> c)
-> Attribute High
-> m (Endo c)
collectBC evolvefn options def attr =
fromMaybe (return $ Endo (def attr))
. msum
$ (\(BCAttr fn) -> fmap (Endo . fn) <$> fromBCAttribute evolvefn attr) <$> options
data ByteCodeAttributeCollector c
= forall a. (IsAttribute (a Low), ByteCodeStaged a)
=> BCAttr (a High -> c -> c)
fromAttributes ::
(Foldable f, EvolveM m, Monoid a)
=> AttributeLocation
-> f (Attribute Low)
-> (Attribute High -> m a)
-> m a
fromAttributes al attrs f = do
afilter <- attributeFilter
Prelude.foldl (g afilter) (return mempty) attrs
where
g afilter m a' = do
b <- m
ah <- evolve a'
if afilter (al, aName ah)
then do
x <- f ah
return $ b `mappend` x
else return b
readFromStrict :: Binary a => Attribute r -> Either String a
readFromStrict =
bimap trd trd . decodeOrFail . BL.fromStrict . aInfo