{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE RecordWildCards #-}
module Language.JVM.ClassFile
( ClassFile (..)
, cAccessFlags
, cFields
, cMethods
, cSignature
, cEnclosingMethod
, cInnerClasses
, ClassAttributes (..)
, emptyClassAttributes
, cBootstrapMethods
) where
import Data.Binary
import Data.Monoid
import Data.Set
import Language.JVM.AccessFlag
import Language.JVM.Attribute
import Language.JVM.Attribute.BootstrapMethods
import Language.JVM.Attribute.EnclosingMethod
import Language.JVM.Attribute.InnerClasses
import Language.JVM.Constant
import Language.JVM.ConstantPool as CP
import Language.JVM.Field (Field)
import Language.JVM.Method (Method)
import Language.JVM.Staged
import Language.JVM.Utils
data ClassFile r = ClassFile
{ cMagicNumber :: !Word32
, cMinorVersion :: !Word16
, cMajorVersion :: !Word16
, cConstantPool :: !(Choice (ConstantPool r) () r)
, cAccessFlags' :: !(BitSet16 CAccessFlag)
, cThisClass :: !(Ref ClassName r)
, cSuperClass :: !(Ref ClassName r)
, cInterfaces :: !(SizedList16 (Ref ClassName r))
, cFields' :: !(SizedList16 (Field r))
, cMethods' :: !(SizedList16 (Method r))
, cAttributes :: !(Attributes ClassAttributes r)
}
cAccessFlags :: ClassFile r -> Set CAccessFlag
cAccessFlags = toSet . cAccessFlags'
cFields :: ClassFile r -> [Field r]
cFields = unSizedList . cFields'
cMethods :: ClassFile r -> [Method r]
cMethods = unSizedList . cMethods'
cBootstrapMethods' :: ClassFile High -> Maybe (BootstrapMethods High)
cBootstrapMethods' =
firstOne . caBootstrapMethods . cAttributes
cBootstrapMethods :: ClassFile High -> [BootstrapMethod High]
cBootstrapMethods =
maybe [] methods . cBootstrapMethods'
cSignature :: ClassFile High -> Maybe (Signature High)
cSignature =
firstOne . caSignature . cAttributes
cEnclosingMethod :: ClassFile High -> Maybe (EnclosingMethod High)
cEnclosingMethod =
firstOne . caEnclosingMethod . cAttributes
cInnerClasses' :: ClassFile High -> Maybe (InnerClasses High)
cInnerClasses' =
firstOne . caInnerClasses . cAttributes
cInnerClasses :: ClassFile High -> [InnerClass High]
cInnerClasses =
maybe [] innerClasses . cInnerClasses'
data ClassAttributes r = ClassAttributes
{ caBootstrapMethods :: [ BootstrapMethods r]
, caSignature :: [ Signature r ]
, caEnclosingMethod :: [ EnclosingMethod r ]
, caInnerClasses :: [ InnerClasses r ]
, caVisibleAnnotations :: [ RuntimeVisibleAnnotations r ]
, caInvisibleAnnotations :: [ RuntimeInvisibleAnnotations r ]
, caVisibleTypeAnnotations ::
[ RuntimeVisibleTypeAnnotations ClassTypeAnnotation r ]
, caInvisibleTypeAnnotations ::
[ RuntimeInvisibleTypeAnnotations ClassTypeAnnotation r ]
, caOthers :: [ Attribute r ]
}
emptyClassAttributes :: ClassAttributes High
emptyClassAttributes =
ClassAttributes [] [] [] [] [] [] [] [] []
instance Staged ClassFile where
evolve cf = label "ClassFile" $ do
tci' <- link (cThisClass cf)
sci' <-
if tci' /= "java/lang/Object"
then do
link (cSuperClass cf)
else do
return $ "java/lang/Object"
cii' <- mapM link $ cInterfaces cf
cf' <- mapM evolve $ cFields' cf
cm' <- mapM evolve $ cMethods' cf
ca' <- fmap (`appEndo` emptyClassAttributes) . fromAttributes ClassAttribute (cAttributes cf)
$ collect
[ Attr $ \e ca -> ca {caSignature = e : caSignature ca}
, Attr $ \e ca -> ca {caEnclosingMethod = e : caEnclosingMethod ca}
, Attr $ \e ca -> ca {caBootstrapMethods = e : caBootstrapMethods ca}
, Attr $ \e ca -> ca {caVisibleAnnotations = e : caVisibleAnnotations ca}
, Attr $ \e ca -> ca {caInvisibleAnnotations = e : caInvisibleAnnotations ca}
, Attr $ \e ca -> ca {caVisibleTypeAnnotations = e : caVisibleTypeAnnotations ca}
, Attr $ \e ca -> ca {caInvisibleTypeAnnotations = e : caInvisibleTypeAnnotations ca}
, Attr $ \e ca -> ca {caInnerClasses = e : caInnerClasses ca}
]
(\e ca -> ca {caOthers = e : caOthers ca})
return $ cf
{ cConstantPool = ()
, cThisClass = tci'
, cSuperClass = sci'
, cInterfaces = cii'
, cFields' = cf'
, cMethods' = cm'
, cAttributes = ca'
}
devolve cf = do
tci' <- unlink (cThisClass cf)
sci' <-
if cThisClass cf /= "java/lang/Object" then
unlink (cSuperClass cf)
else
return $ 0
cii' <- mapM unlink $ cInterfaces cf
cf' <- mapM devolve $ cFields' cf
cm' <- mapM devolve $ cMethods' cf
ca' <- fromClassAttributes $ cAttributes cf
return $ cf
{ cConstantPool = CP.empty
, cThisClass = tci'
, cSuperClass = sci'
, cInterfaces = cii'
, cFields' = cf'
, cMethods' = cm'
, cAttributes = SizedList ca'
}
where
fromClassAttributes (ClassAttributes {..}) = do
concat <$> sequence
[ mapM toAttribute caBootstrapMethods
, mapM toAttribute caSignature
, mapM toAttribute caEnclosingMethod
, mapM toAttribute caInnerClasses
, mapM toAttribute caVisibleAnnotations
, mapM toAttribute caInvisibleAnnotations
, mapM toAttribute caVisibleTypeAnnotations
, mapM toAttribute caInvisibleTypeAnnotations
, mapM devolve caOthers
]
$(deriveBase ''ClassAttributes)
$(deriveBaseWithBinary ''ClassFile)