1 {-# LANGUAGE FlexibleContexts, TypeFamilies, OverloadedStrings, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, GeneralizedNewtypeDeriving, ScopedTypeVariables #-}
2 -- | This module defines Generate[IO] monad, which helps generating JVM code and
3 -- creating Java class constants pool.
5 -- Code generation could be done using one of two monads: Generate and GenerateIO.
6 -- Generate monad is pure (simply State monad), while GenerateIO is IO-related.
7 -- In GenerateIO additional actions are available, such as setting up ClassPath
8 -- and loading classes (from .class files or JAR archives).
10 module JVM.Builder.Monad
18 setStackSize, setMaxLocals,
20 getClassField, getClassMethod,
24 import Prelude hiding (catch)
25 import Control.Monad.State as St
26 import Control.Monad.Exception
27 import Control.Monad.Exception.Base
30 import qualified Data.Map as M
31 import qualified Data.Set as S
32 import qualified Data.ByteString.Lazy as B
41 data GState = GState {
42 generated :: [Instruction], -- ^ Already generated code (in current method)
43 currentPool :: Pool Direct, -- ^ Already generated constants pool
44 nextPoolIndex :: Word16, -- ^ Next index to be used in constants pool
45 doneMethods :: [Method Direct], -- ^ Already generated class methods
46 currentMethod :: Maybe (Method Direct), -- ^ Current method
47 stackSize :: Word16, -- ^ Maximum stack size for current method
48 locals :: Word16, -- ^ Maximum number of local variables for current method
49 classPath :: [Tree CPEntry]
53 -- | Empty generator state
55 emptyGState = GState {
57 currentPool = M.empty,
60 currentMethod = Nothing,
65 class (Monad (g e), MonadState GState (g e)) => Generator e g where
66 throwG :: (Exception x, Throws x e) => x -> g e a
69 newtype Generate e a = Generate {
70 runGenerate :: EMT e (State GState) a }
71 deriving (Monad, MonadState GState)
73 instance MonadState st (EMT e (StateT st IO)) where
75 put x = lift (St.put x)
77 instance MonadState st (EMT e (State st)) where
79 put x = lift (St.put x)
81 -- | IO version of Generate monad
82 newtype GenerateIO e a = GenerateIO {
83 runGenerateIO :: EMT e (StateT GState IO) a }
84 deriving (Monad, MonadState GState, MonadIO)
86 instance MonadIO (EMT e (StateT GState IO)) where
87 liftIO action = lift $ liftIO action
89 instance Generator e GenerateIO where
90 throwG e = GenerateIO (throw e)
92 instance (MonadState GState (EMT e (State GState))) => Generator e Generate where
93 throwG e = Generate (throw e)
95 execGenerateIO :: [Tree CPEntry]
96 -> GenerateIO (Caught SomeException NoExceptions) a
98 execGenerateIO cp (GenerateIO emt) = do
99 let caught = emt `catch` (\(e :: SomeException) -> fail $ show e)
100 execStateT (runEMT caught) (emptyGState {classPath = cp})
102 execGenerate :: [Tree CPEntry]
103 -> Generate (Caught SomeException NoExceptions) a
105 execGenerate cp (Generate emt) = do
106 let caught = emt `catch` (\(e :: SomeException) -> fail $ show e)
107 execState (runEMT caught) (emptyGState {classPath = cp})
109 -- | Update ClassPath
110 withClassPath :: ClassPath () -> GenerateIO e ()
111 withClassPath cp = do
112 res <- liftIO $ execClassPath cp
114 St.put $ st {classPath = res}
116 -- | Add a constant to pool
117 addItem :: (Generator e g) => Constant Direct -> g e Word16
119 pool <- St.gets currentPool
120 case lookupPool c pool of
123 i <- St.gets nextPoolIndex
124 let pool' = M.insert i c pool
129 St.put $ st {currentPool = pool',
133 -- | Lookup in a pool
134 lookupPool :: Constant Direct -> Pool Direct -> Maybe Word16
136 fromIntegral `fmap` mapFindIndex (== c) pool
138 addNT :: (Generator e g, HasSignature a) => NameType a -> g e Word16
139 addNT (NameType name sig) = do
140 let bsig = encode sig
141 x <- addItem (CNameType name bsig)
146 addSig :: (Generator e g) => MethodSignature -> g e Word16
147 addSig c@(MethodSignature args ret) = do
151 -- | Add a constant into pool
152 addToPool :: (Generator e g) => Constant Direct -> g e Word16
153 addToPool c@(CClass str) = do
156 addToPool c@(CField cls name) = do
157 addToPool (CClass cls)
160 addToPool c@(CMethod cls name) = do
161 addToPool (CClass cls)
164 addToPool c@(CIfaceMethod cls name) = do
165 addToPool (CClass cls)
168 addToPool c@(CString str) = do
169 addToPool (CUTF8 str)
171 addToPool c@(CNameType name sig) = do
175 addToPool c = addItem c
177 putInstruction :: (Generator e g) => Instruction -> g e ()
178 putInstruction instr = do
180 let code = generated st
181 St.put $ st {generated = code ++ [instr]}
183 -- | Generate one (zero-arguments) instruction
184 i0 :: (Generator e g) => Instruction -> g e ()
187 -- | Generate one one-argument instruction
188 i1 :: (Generator e g) => (Word16 -> Instruction) -> Constant Direct -> g e ()
193 -- | Generate one one-argument instruction
194 i8 :: (Generator e g) => (Word8 -> Instruction) -> Constant Direct -> g e ()
197 i0 (fn $ fromIntegral ix)
199 -- | Set maximum stack size for current method
200 setStackSize :: (Generator e g) => Word16 -> g e ()
203 St.put $ st {stackSize = n}
205 -- | Set maximum number of local variables for current method
206 setMaxLocals :: (Generator e g) => Word16 -> g e ()
209 St.put $ st {locals = n}
211 -- | Start generating new method
212 startMethod :: (Generator e g) => [AccessFlag] -> B.ByteString -> MethodSignature -> g e ()
213 startMethod flags name sig = do
214 addToPool (CString name)
219 let method = Method {
220 methodAccessFlags = S.fromList flags,
222 methodSignature = sig,
223 methodAttributesCount = 0,
224 methodAttributes = AR M.empty }
225 St.put $ st {generated = [],
226 currentMethod = Just method }
228 -- | End of method generation
229 endMethod :: (Generator e g, Throws UnexpectedEndMethod e) => g e ()
231 m <- St.gets currentMethod
232 code <- St.gets genCode
234 Nothing -> throwG UnexpectedEndMethod
236 let method' = method {methodAttributes = AR $ M.fromList [("Code", encodeMethod code)],
237 methodAttributesCount = 1}
239 St.put $ st {generated = [],
240 currentMethod = Nothing,
241 doneMethods = doneMethods st ++ [method']}
243 -- | Generate new method
244 newMethod :: (Generator e g, Throws UnexpectedEndMethod e)
245 => [AccessFlag] -- ^ Access flags for method (public, static etc)
246 -> B.ByteString -- ^ Method name
247 -> [ArgumentSignature] -- ^ Signatures of method arguments
248 -> ReturnSignature -- ^ Method return signature
249 -> g e () -- ^ Generator for method code
250 -> g e (NameType Method)
251 newMethod flags name args ret gen = do
252 let sig = MethodSignature args ret
253 startMethod flags name sig
256 return (NameType name sig)
258 -- | Get a class from current ClassPath
259 getClass :: (Throws ENotLoaded e, Throws ENotFound e)
260 => String -> GenerateIO e (Class Direct)
262 cp <- St.gets classPath
263 res <- liftIO $ getEntry cp name
265 Just (NotLoaded p) -> throwG (ClassFileNotLoaded p)
266 Just (Loaded _ c) -> return c
267 Just (NotLoadedJAR p c) -> throwG (JARNotLoaded p c)
268 Just (LoadedJAR _ c) -> return c
269 Nothing -> throwG (ClassNotFound name)
271 -- | Get class field signature from current ClassPath
272 getClassField :: (Throws ENotFound e, Throws ENotLoaded e)
273 => String -> B.ByteString -> GenerateIO e (NameType Field)
274 getClassField clsName fldName = do
275 cls <- getClass clsName
276 case lookupField fldName cls of
277 Just fld -> return (fieldNameType fld)
278 Nothing -> throwG (FieldNotFound clsName fldName)
280 -- | Get class method signature from current ClassPath
281 getClassMethod :: (Throws ENotFound e, Throws ENotLoaded e)
282 => String -> B.ByteString -> GenerateIO e (NameType Method)
283 getClassMethod clsName mName = do
284 cls <- getClass clsName
285 case lookupMethod mName cls of
286 Just m -> return (methodNameType m)
287 Nothing -> throwG (MethodNotFound clsName mName)
289 -- | Convert Generator state to method Code.
290 genCode :: GState -> Code
292 codeStackSize = stackSize st,
293 codeMaxLocals = locals st,
295 codeInstructions = generated st,
299 codeAttributes = AP [] }
301 len = fromIntegral $ B.length $ encodeInstructions (generated st)
303 -- | Start class generation.
304 initClass :: (Generator e g) => B.ByteString -> g e Word16
306 addToPool (CClass "java/lang/Object")
307 addToPool (CClass name)
308 addToPool (CString "Code")
310 -- | Generate a class
311 generateIO :: [Tree CPEntry]
313 -> GenerateIO (Caught SomeException NoExceptions) ()
315 generateIO cp name gen = do
319 res <- execGenerateIO cp generator
320 let code = genCode res
321 d = defaultClass :: Class Direct
323 constsPoolSize = fromIntegral $ M.size (currentPool res),
324 constsPool = currentPool res,
325 accessFlags = S.fromList [ACC_PUBLIC, ACC_STATIC],
327 superClass = "java/lang/Object",
328 classMethodsCount = fromIntegral $ length (doneMethods res),
329 classMethods = doneMethods res }
331 -- | Generate a class
332 generate :: [Tree CPEntry]
334 -> Generate (Caught SomeException NoExceptions) ()
336 generate cp name gen =
340 res = execGenerate cp generator
342 d = defaultClass :: Class Direct
344 constsPoolSize = fromIntegral $ M.size (currentPool res),
345 constsPool = currentPool res,
346 accessFlags = S.fromList [ACC_PUBLIC, ACC_STATIC],
348 superClass = "java/lang/Object",
349 classMethodsCount = fromIntegral $ length (doneMethods res),
350 classMethods = doneMethods res }