1 {-# LANGUAGE FlexibleContexts, TypeFamilies, OverloadedStrings, TypeSynonymInstances, FlexibleInstances #-}
2 -- | This module defines Generate monad, which helps generating JVM code and
3 -- creating Java class constants pool.
4 module JVM.Builder.Monad
12 setStackSize, setMaxLocals,
14 getClassField, getClassMethod,
18 import Control.Monad.State as St
22 import qualified Data.Map as M
23 import qualified Data.Set as S
24 import qualified Data.ByteString.Lazy as B
26 import JVM.Common () -- import instances only
32 data GState = GState {
33 generated :: [Instruction], -- ^ Already generated code (in current method)
34 currentPool :: Pool Direct, -- ^ Already generated constants pool
35 doneMethods :: [Method Direct], -- ^ Already generated class methods
36 currentMethod :: Maybe (Method Direct), -- ^ Current method
37 stackSize :: Word16, -- ^ Maximum stack size for current method
38 locals :: Word16, -- ^ Maximum number of local variables for current method
39 classPath :: [Tree CPEntry]
43 -- | Empty generator state
45 emptyGState = GState {
47 currentPool = M.empty,
49 currentMethod = Nothing,
54 class (Monad m, MonadState GState m) => Generator m where
57 type GenerateIO a = StateT GState IO a
59 type Generate a = State GState a
61 instance Generator (StateT GState IO) where
63 instance Generator (State GState) where
66 withClassPath :: ClassPath () -> GenerateIO ()
68 res <- liftIO $ execClassPath cp
70 St.put $ st {classPath = res}
72 -- | Append a constant to pool
73 appendPool :: Constant Direct -> Pool Direct -> (Pool Direct, Word16)
75 let size = fromIntegral (M.size pool)
76 pool' = M.insert size c pool
79 -- | Add a constant to pool
80 addItem :: (Generator g) => Constant Direct -> g Word16
82 pool <- St.gets currentPool
83 case lookupPool c pool of
84 Just i -> return (i+1)
86 let (pool', i) = appendPool c pool
88 St.put $ st {currentPool = pool'}
92 lookupPool :: Constant Direct -> Pool Direct -> Maybe Word16
94 fromIntegral `fmap` findIndex (== c) (M.elems pool)
96 addNT :: (Generator g, HasSignature a) => NameType a -> g Word16
97 addNT (NameType name sig) = do
99 x <- addItem (CNameType name bsig)
104 addSig :: (Generator g) => MethodSignature -> g Word16
105 addSig c@(MethodSignature args ret) = do
109 -- | Add a constant into pool
110 addToPool :: (Generator g) => Constant Direct -> g Word16
111 addToPool c@(CClass str) = do
114 addToPool c@(CField cls name) = do
115 addToPool (CClass cls)
118 addToPool c@(CMethod cls name) = do
119 addToPool (CClass cls)
122 addToPool c@(CIfaceMethod cls name) = do
123 addToPool (CClass cls)
126 addToPool c@(CString str) = do
127 addToPool (CUTF8 str)
129 addToPool c@(CNameType name sig) = do
133 addToPool c = addItem c
135 putInstruction :: (Generator g) => Instruction -> g ()
136 putInstruction instr = do
138 let code = generated st
139 St.put $ st {generated = code ++ [instr]}
141 -- | Generate one (zero-arguments) instruction
142 i0 :: (Generator g) => Instruction -> g ()
145 -- | Generate one one-argument instruction
146 i1 :: (Generator g) => (Word16 -> Instruction) -> Constant Direct -> g ()
151 -- | Generate one one-argument instruction
152 i8 :: (Generator g) => (Word8 -> Instruction) -> Constant Direct -> g ()
155 i0 (fn $ fromIntegral ix)
157 -- | Set maximum stack size for current method
158 setStackSize :: (Generator g) => Word16 -> g ()
161 St.put $ st {stackSize = n}
163 -- | Set maximum number of local variables for current method
164 setMaxLocals :: (Generator g) => Word16 -> g ()
167 St.put $ st {locals = n}
169 -- | Start generating new method
170 startMethod :: (Generator g) => [AccessFlag] -> B.ByteString -> MethodSignature -> g ()
171 startMethod flags name sig = do
172 addToPool (CString name)
177 let method = Method {
178 methodAccessFlags = S.fromList flags,
180 methodSignature = sig,
181 methodAttributesCount = 0,
182 methodAttributes = AR M.empty }
183 St.put $ st {generated = [],
184 currentMethod = Just method }
186 -- | End of method generation
187 endMethod :: (Generator g) => g ()
189 m <- St.gets currentMethod
190 code <- St.gets genCode
192 Nothing -> fail "endMethod without startMethod!"
194 let method' = method {methodAttributes = AR $ M.fromList [("Code", encodeMethod code)],
195 methodAttributesCount = 1}
197 St.put $ st {generated = [],
198 currentMethod = Nothing,
199 doneMethods = doneMethods st ++ [method']}
201 -- | Generate new method
202 newMethod :: (Generator g)
203 => [AccessFlag] -- ^ Access flags for method (public, static etc)
204 -> B.ByteString -- ^ Method name
205 -> [ArgumentSignature] -- ^ Signatures of method arguments
206 -> ReturnSignature -- ^ Method return signature
207 -> g () -- ^ Generator for method code
208 -> g (NameType Method)
209 newMethod flags name args ret gen = do
210 let sig = MethodSignature args ret
211 startMethod flags name sig
214 return (NameType name sig)
216 -- | Get a class from current ClassPath
217 getClass :: String -> GenerateIO (Class Direct)
219 cp <- St.gets classPath
220 res <- liftIO $ getEntry cp name
222 Just (NotLoaded p) -> fail $ "Class file was not loaded: " ++ p
223 Just (Loaded _ c) -> return c
224 Just (NotLoadedJAR p c) -> fail $ "Class was not loaded from JAR " ++ p ++ ": " ++ c
225 Just (LoadedJAR _ c) -> return c
226 Nothing -> fail $ "No such class in ClassPath: " ++ name
228 -- | Get class field signature from current ClassPath
229 getClassField :: String -> B.ByteString -> GenerateIO (NameType Field)
230 getClassField clsName fldName = do
231 cls <- getClass clsName
232 case lookupField fldName cls of
233 Just fld -> return (fieldNameType fld)
234 Nothing -> fail $ "No such field in class " ++ clsName ++ ": " ++ toString fldName
236 -- | Get class method signature from current ClassPath
237 getClassMethod :: String -> B.ByteString -> GenerateIO (NameType Method)
238 getClassMethod clsName mName = do
239 cls <- getClass clsName
240 case lookupMethod mName cls of
241 Just m -> return (methodNameType m)
242 Nothing -> fail $ "No such method in class " ++ clsName ++ ": " ++ toString mName
244 -- | Convert Generator state to method Code.
245 genCode :: GState -> Code
247 codeStackSize = stackSize st,
248 codeMaxLocals = locals st,
250 codeInstructions = generated st,
254 codeAttributes = AP [] }
256 len = fromIntegral $ B.length $ encodeInstructions (generated st)
258 -- | Start class generation.
259 initClass :: (Generator g) => B.ByteString -> g Word16
261 addToPool (CClass "java/lang/Object")
262 addToPool (CClass name)
263 addToPool (CString "Code")
265 -- | Generate a class
266 generateIO :: [Tree CPEntry] -> B.ByteString -> GenerateIO () -> IO (Class Direct)
267 generateIO cp name gen = do
271 res <- execStateT generator (emptyGState {classPath = cp})
272 let code = genCode res
273 d = defaultClass :: Class Direct
275 constsPoolSize = fromIntegral $ M.size (currentPool res),
276 constsPool = currentPool res,
277 accessFlags = S.fromList [ACC_PUBLIC, ACC_STATIC],
279 superClass = "java/lang/Object",
280 classMethodsCount = fromIntegral $ length (doneMethods res),
281 classMethods = doneMethods res }
283 -- | Generate a class
284 generate :: [Tree CPEntry] -> B.ByteString -> Generate () -> Class Direct
285 generate cp name gen =
289 res = execState generator (emptyGState {classPath = cp})
291 d = defaultClass :: Class Direct
293 constsPoolSize = fromIntegral $ M.size (currentPool res),
294 constsPool = currentPool res,
295 accessFlags = S.fromList [ACC_PUBLIC, ACC_STATIC],
297 superClass = "java/lang/Object",
298 classMethodsCount = fromIntegral $ length (doneMethods res),
299 classMethods = doneMethods res }