jvm-binary-0.1.0: src/Language/JVM/ClassFile.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-|
Module : Language.JVM.ClassFile
Copyright : (c) Christian Gram Kalhauge, 2017
License : MIT
Maintainer : kalhuage@cs.ucla.edu
The class file is described in this module.
-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
module Language.JVM.ClassFile
( ClassFile (..)
, cAccessFlags
, cFields
, cMethods
, cSignature
-- * Attributes
, ClassAttributes (..)
, 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.Signature
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
-- | A 'ClassFile' as described
-- [here](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html).
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)
}
-- | Get the set of access flags
cAccessFlags :: ClassFile r -> Set CAccessFlag
cAccessFlags = toSet . cAccessFlags'
-- | Get a list of 'Field's of a ClassFile.
cFields :: ClassFile r -> [Field r]
cFields = unSizedList . cFields'
-- | Get a list of 'Method's of a ClassFile.
cMethods :: ClassFile r -> [Method r]
cMethods = unSizedList . cMethods'
-- | Fetch the 'BootstrapMethods' attribute.
-- There can only one bootstrap methods per class, but there might not be
-- one.
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
data ClassAttributes r = ClassAttributes
{ caBootstrapMethods :: [ BootstrapMethods r]
, caSignature :: [ Signature r ]
, caOthers :: [ Attribute r ]
}
instance Staged ClassFile where
evolve cf = label "ClassFile" $ do
tci' <- link (cThisClass cf)
sci' <-
if tci' /= ClassName "java/lang/Object"
then do
link (cSuperClass cf)
else do
return $ ClassName "java/lang/Object"
cii' <- mapM link $ cInterfaces cf
cf' <- mapM evolve $ cFields' cf
cm' <- mapM evolve $ cMethods' cf
ca' <- fromCollector <$> fromAttributes collect' (cAttributes cf)
return $ cf
{ cConstantPool = ()
, cThisClass = tci'
, cSuperClass = sci'
, cInterfaces = cii'
, cFields' = cf'
, cMethods' = cm'
, cAttributes = ca'
}
where
fromCollector (a, b, c) =
ClassAttributes (appEndo a []) (appEndo b []) (appEndo c [])
collect' attr =
collect (mempty, mempty, Endo (attr:)) attr
[ toC $ \e -> (Endo (e:), mempty, mempty)
, toC $ \e -> (mempty, Endo (e:), mempty)]
devolve cf = do
tci' <- unlink (cThisClass cf)
sci' <-
if cThisClass cf /= ClassName "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
-- We cannot yet set the constant pool
, cThisClass = tci'
, cSuperClass = sci'
, cInterfaces = cii'
, cFields' = cf'
, cMethods' = cm'
, cAttributes = SizedList ca'
}
where
fromClassAttributes (ClassAttributes cm cs at) = do
cm' <- mapM toAttribute cm
cs' <- mapM toAttribute cs
at' <- mapM devolve at
return (cm' ++ cs' ++ at')
$(deriveBase ''ClassAttributes)
$(deriveBaseWithBinary ''ClassFile)