1 {-# LANGUAGE FlexibleContexts, TypeFamilies, OverloadedStrings #-}
2 -- | This module defines Generate monad, which helps generating JVM code and
3 -- creating Java class constants pool.
4 module JVM.Builder.Monad
11 setStackSize, setMaxLocals,
15 import Control.Monad.State as St
19 import qualified Data.Map as M
20 import qualified Data.Set as S
21 import qualified Data.ByteString.Lazy as B
23 import JVM.Common () -- import instances only
28 data GState = GState {
29 generated :: [Instruction], -- ^ Already generated code (in current method)
30 currentPool :: Pool Direct, -- ^ Already generated constants pool
31 doneMethods :: [Method Direct], -- ^ Already generated class methods
32 currentMethod :: Maybe (Method Direct), -- ^ Current method
33 stackSize :: Word16, -- ^ Maximum stack size for current method
34 locals :: Word16 -- ^ Maximum number of local variables for current method
38 -- | Empty generator state
40 emptyGState = GState {
42 currentPool = M.empty,
44 currentMethod = Nothing,
49 type Generate a = State GState a
51 -- | Append a constant to pool
52 appendPool :: Constant Direct -> Pool Direct -> (Pool Direct, Word16)
54 let size = fromIntegral (M.size pool)
55 pool' = M.insert size c pool
58 -- | Add a constant to pool
59 addItem :: Constant Direct -> Generate Word16
61 pool <- St.gets currentPool
62 case lookupPool c pool of
63 Just i -> return (i+1)
65 let (pool', i) = appendPool c pool
67 St.put $ st {currentPool = pool'}
71 lookupPool :: Constant Direct -> Pool Direct -> Maybe Word16
73 fromIntegral `fmap` findIndex (== c) (M.elems pool)
75 addNT :: Binary (Signature a) => NameType a -> Generate Word16
76 addNT (NameType name sig) = do
78 x <- addItem (CNameType name bsig)
83 addSig :: MethodSignature -> Generate Word16
84 addSig c@(MethodSignature args ret) = do
88 -- | Add a constant into pool
89 addToPool :: Constant Direct -> Generate Word16
90 addToPool c@(CClass str) = do
93 addToPool c@(CField cls name) = do
94 addToPool (CClass cls)
97 addToPool c@(CMethod cls name) = do
98 addToPool (CClass cls)
101 addToPool c@(CIfaceMethod cls name) = do
102 addToPool (CClass cls)
105 addToPool c@(CString str) = do
106 addToPool (CUTF8 str)
108 addToPool c@(CNameType name sig) = do
112 addToPool c = addItem c
114 putInstruction :: Instruction -> Generate ()
115 putInstruction instr = do
117 let code = generated st
118 St.put $ st {generated = code ++ [instr]}
120 -- | Generate one (zero-arguments) instruction
121 i0 :: Instruction -> Generate ()
124 -- | Generate one one-argument instruction
125 i1 :: (Word16 -> Instruction) -> Constant Direct -> Generate ()
130 -- | Generate one one-argument instruction
131 i8 :: (Word8 -> Instruction) -> Constant Direct -> Generate ()
134 i0 (fn $ fromIntegral ix)
136 -- | Set maximum stack size for current method
137 setStackSize :: Word16 -> Generate ()
140 St.put $ st {stackSize = n}
142 -- | Set maximum number of local variables for current method
143 setMaxLocals :: Word16 -> Generate ()
146 St.put $ st {locals = n}
148 -- | Start generating new method
149 startMethod :: [AccessFlag] -> B.ByteString -> MethodSignature -> Generate ()
150 startMethod flags name sig = do
151 addToPool (CString name)
156 let method = Method {
157 methodAccessFlags = S.fromList flags,
159 methodSignature = sig,
160 methodAttributesCount = 0,
161 methodAttributes = AR M.empty }
162 St.put $ st {generated = [],
163 currentMethod = Just method }
165 -- | End of method generation
166 endMethod :: Generate ()
168 m <- St.gets currentMethod
169 code <- St.gets genCode
171 Nothing -> fail "endMethod without startMethod!"
173 let method' = method {methodAttributes = AR $ M.fromList [("Code", encodeMethod code)],
174 methodAttributesCount = 1}
176 St.put $ st {generated = [],
177 currentMethod = Nothing,
178 doneMethods = doneMethods st ++ [method']}
180 -- | Generate new method
181 newMethod :: [AccessFlag] -- ^ Access flags for method (public, static etc)
182 -> B.ByteString -- ^ Method name
183 -> [ArgumentSignature] -- ^ Signatures of method arguments
184 -> ReturnSignature -- ^ Method return signature
185 -> Generate () -- ^ Generator for method code
186 -> Generate (NameType Method)
187 newMethod flags name args ret gen = do
188 let sig = MethodSignature args ret
189 startMethod flags name sig
192 return (NameType name sig)
194 -- | Convert Generator state to method Code.
195 genCode :: GState -> Code
197 codeStackSize = stackSize st,
198 codeMaxLocals = locals st,
200 codeInstructions = generated st,
204 codeAttributes = AP [] }
206 len = fromIntegral $ B.length $ encodeInstructions (generated st)
208 -- | Start class generation.
209 initClass :: B.ByteString -> Generate Word16
211 addToPool (CClass "java/lang/Object")
212 addToPool (CClass name)
213 addToPool (CString "Code")
215 -- | Generate a class
216 generate :: B.ByteString -> Generate () -> Class Direct
221 res = execState generator emptyGState
227 constsPoolSize = fromIntegral $ M.size (currentPool res),
228 constsPool = currentPool res,
229 accessFlags = S.fromList [ACC_PUBLIC, ACC_STATIC],
231 superClass = "java/lang/Object",
234 classFieldsCount = 0,
236 classMethodsCount = fromIntegral $ length (doneMethods res),
237 classMethods = doneMethods res,
238 classAttributesCount = 0,
239 classAttributes = AR M.empty }