2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ForeignFunctionInterface #-}
5 module Mate.ClassPool (
13 getInterfaceMethodOffset,
21 import qualified Data.Map as M
22 import qualified Data.Set as S
23 import qualified Data.ByteString.Lazy as B
34 import Foreign.C.Types
35 import Foreign.Storable
38 import System.IO.Unsafe
39 import System.Directory
43 import Java.ClassPath hiding (Directory)
46 import Mate.BasicBlocks
47 import {-# SOURCE #-} Mate.MethodPool
50 import Mate.GarbageAlloc
52 getClassInfo :: B.ByteString -> IO ClassInfo
53 getClassInfo path = do
54 class_map <- getClassMap
55 case M.lookup path class_map of
56 Nothing -> loadAndInitClass path
59 getClassFile :: B.ByteString -> IO (Class Direct)
60 getClassFile path = do
61 ci <- getClassInfo path
64 getStaticFieldOffset :: B.ByteString -> B.ByteString -> IO CUInt
65 getStaticFieldOffset path field = do
66 ci <- getClassInfo path
67 return $ fromIntegral $ ciStaticMap ci M.! field
69 getFieldOffset :: B.ByteString -> B.ByteString -> IO Int32
70 getFieldOffset path field = do
71 ci <- getClassInfo path
72 return $ ciFieldMap ci M.! field
74 -- method + signature plz!
75 getMethodOffset :: B.ByteString -> B.ByteString -> IO Word32
76 getMethodOffset path method = do
77 ci <- getClassInfo path
78 -- (4+) one slot for "interface-table-ptr"
79 return $ (+4) $ fromIntegral $ ciMethodMap ci M.! method
81 getMethodTable :: B.ByteString -> IO Word32
82 getMethodTable path = do
83 ci <- getClassInfo path
84 return $ ciMethodBase ci
86 getObjectSize :: B.ByteString -> IO Word32
87 getObjectSize path = do
88 ci <- getClassInfo path
89 -- TODO(bernhard): correct sizes for different types...
90 let fsize = fromIntegral $ M.size $ ciFieldMap ci
91 -- one slot for "method-table-ptr"
92 return $ (1 + fsize) * 4
94 getStaticFieldAddr :: CUInt -> IO CUInt
95 getStaticFieldAddr from = do
97 let w32_from = fromIntegral from
98 let sfi = trapmap M.! w32_from
100 (SFI (StaticFieldInfo cls field)) -> getStaticFieldOffset cls field
101 _ -> error "getFieldAddr: no trapInfo. abort"
103 -- interface + method + signature plz!
104 getInterfaceMethodOffset :: B.ByteString -> B.ByteString -> B.ByteString -> IO Word32
105 getInterfaceMethodOffset ifname meth sig = do
107 ifmmap <- getInterfaceMethodMap
108 let k = ifname `B.append` meth `B.append` sig
109 case M.lookup k ifmmap of
110 Just w32 -> return $ w32 + 4
111 Nothing -> error "getInterfaceMethodOffset: no offset set"
114 readClass :: B.ByteString -> IO ClassInfo
116 cfile <- readClassFile $ toString path
120 -- load all interfaces, which are implemented by this class
121 sequence_ [ loadInterface i | i <- interfaces cfile ]
122 superclass <- if path /= "java/lang/Object"
124 sc <- readClass $ superClass cfile
128 (staticmap, fieldmap) <- calculateFields cfile superclass
129 (methodmap, mbase) <- calculateMethodMap cfile superclass
130 immap <- getInterfaceMethodMap
132 -- allocate interface offset table for this class
133 -- TODO(bernhard): we have some duplicates in immap (i.e. some
134 -- entries have the same offset), so we could
135 -- save some memory here.
136 iftable <- mallocClassData ((4*) $ M.size immap)
137 let w32_iftable = fromIntegral $ ptrToIntPtr iftable :: Word32
138 -- store interface-table at offset 0 in method-table
139 pokeElemOff (intPtrToPtr $ fromIntegral mbase) 0 w32_iftable
140 printfCp "staticmap: %s @ %s\n" (show staticmap) (toString path)
141 printfCp "fieldmap: %s @ %s\n" (show fieldmap) (toString path)
142 printfCp "methodmap: %s @ %s\n" (show methodmap) (toString path)
143 printfCp "mbase: 0x%08x\n" mbase
144 printfCp "interfacemethod: %s @ %s\n" (show immap) (toString path)
145 printfCp "iftable: 0x%08x\n" w32_iftable
146 virtual_map <- getVirtualMap
147 setVirtualMap $ M.insert mbase path virtual_map
149 class_map <- getClassMap
150 let new_ci = ClassInfo path cfile staticmap fieldmap methodmap mbase False
151 setClassMap $ M.insert path new_ci class_map
155 loadInterface :: B.ByteString -> IO ()
156 loadInterface path = do
157 imap <- getInterfaceMap
158 -- interface already loaded?
159 case M.lookup path imap of
162 printfCp "interface: loading \"%s\"\n" $ toString path
163 cfile <- readClassFile $ toString path
164 -- load "superinterfaces" first
165 sequence_ [ loadInterface i | i <- interfaces cfile ]
166 immap <- getInterfaceMethodMap
168 -- load map again, because there could be new entries now
169 -- due to loading superinterfaces
170 imap' <- getInterfaceMap
171 let max_off = fromIntegral $ M.size immap * 4
172 -- create index of methods by this interface
173 let mm = zipbase max_off (classMethods cfile)
175 -- create for each method from *every* superinterface a entry to,
176 -- but just put in the same offset as it is already in the map
177 let (ifnames, methodnames) = unzip $ concat
178 [ zip (repeat ifname) (classMethods $ imap' M.! ifname)
179 | ifname <- interfaces cfile ]
180 let sm = zipWith (\x y -> (entry y, immap M.! getname x y)) ifnames methodnames
182 -- merge all offset tables
183 setInterfaceMethodMap $ M.fromList sm `M.union` M.fromList mm `M.union` immap
184 setInterfaceMap $ M.insert path cfile imap'
186 zipbase base = zipWith (\x y -> (entry y, x + base)) [0,4..]
188 getname p y = p `B.append` methodName y `B.append` encode (methodSignature y)
191 calculateFields :: Class Direct -> Maybe ClassInfo -> IO (FieldMap, FieldMap)
192 calculateFields cf superclass = do
193 -- TODO(bernhard): correct sizes. int only atm
195 let (sfields, ifields) = span (S.member ACC_STATIC . fieldAccessFlags) (classFields cf)
197 staticbase <- mallocClassData $ fromIntegral (length sfields) * 4
198 let i_sb = fromIntegral $ ptrToIntPtr staticbase
199 let sm = zipbase i_sb sfields
200 let sc_sm = getsupermap superclass ciStaticMap
201 -- new fields "overwrite" old ones, if they have the same name
202 let staticmap = M.fromList sm `M.union` sc_sm
204 let sc_im = getsupermap superclass ciFieldMap
205 -- "+ 4" for the method table pointer
206 let max_off = (4+) $ fromIntegral $ M.size sc_im * 4
207 let im = zipbase max_off ifields
208 -- new fields "overwrite" old ones, if they have the same name
209 let fieldmap = M.fromList im `M.union` sc_im
211 return (staticmap, fieldmap)
213 zipbase base = zipWith (\x y -> (fieldName y, x + base)) [0,4..]
216 getsupermap :: Maybe ClassInfo -> (ClassInfo -> FieldMap) -> FieldMap
217 getsupermap superclass getter = case superclass of Just x -> getter x; Nothing -> M.empty
220 calculateMethodMap :: Class Direct -> Maybe ClassInfo -> IO (FieldMap, Word32)
221 calculateMethodMap cf superclass = do
223 (\x -> (not . S.member ACC_STATIC . methodAccessFlags) x &&
224 ((/=) "<init>" . methodName) x)
226 let sc_mm = getsupermap superclass ciMethodMap
227 let max_off = fromIntegral $ M.size sc_mm * 4
228 let mm = zipbase max_off methods
229 let methodmap = M.fromList mm `M.union` sc_mm
231 -- (+1): one slot for the interface-table-ptr
232 methodbase <- mallocClassData (((+1) $ fromIntegral $ M.size methodmap) * 4)
233 return (methodmap, fromIntegral $ ptrToIntPtr methodbase)
234 where zipbase base = zipWith (\x y -> (entry y, x + base)) [0,4..]
235 where entry y = methodName y `B.append` encode (methodSignature y)
238 loadAndInitClass :: B.ByteString -> IO ClassInfo
239 loadAndInitClass path = do
240 class_map <- getClassMap
241 ci <- case M.lookup path class_map of
242 Nothing -> readClass path
245 -- first try to execute class initializer of superclass
246 when (path /= "java/lang/Object") (void $ loadAndInitClass $ superClass $ ciFile ci)
248 -- execute class initializer
249 case lookupMethod "<clinit>" (ciFile ci) of
251 hmap <- parseMethod (ciFile ci) "<clinit>"
254 let mi = MethodInfo "<clinit>" path (methodSignature m)
255 entry <- compileBB hmap' mi
256 addMethodRef entry mi [path]
257 printfCp "executing static initializer from %s now\n" (toString path)
259 printfCp "static initializer from %s done\n" (toString path)
260 Nothing -> error "readClass: static initializer not found (WTF?). abort"
263 class_map' <- getClassMap
264 let new_ci = ci { ciInitDone = True }
265 setClassMap $ M.insert path new_ci class_map'
269 readClassFile :: String -> IO (Class Direct)
270 readClassFile path = readIORef classPaths >>= rcf
272 rcf :: [MClassPath] -> IO (Class Direct)
273 rcf [] = error $ "readClassFile: Class \"" ++ (show path) ++ "\" not found."
274 rcf ((Directory pre):xs) = do
275 let cf = pre ++ path ++ ".class"
276 b <- doesFileExist cf
278 then parseClassFile cf
280 rcf ((JAR p):xs) = do
281 entry <- getEntry p path
283 Just (LoadedJAR _ cls) -> return cls
285 _ -> error $ "readClassFile: Class \"" ++ show path ++ "\" in JAR not found. #1"
291 classPaths :: IORef [MClassPath]
292 {-# NOINLINE classPaths #-}
293 classPaths = unsafePerformIO $ newIORef []
295 addClassPath :: String -> IO ()
297 cps <- readIORef classPaths
298 writeIORef classPaths (Directory x:cps)
300 addClassPathJAR :: String -> IO ()
301 addClassPathJAR x = do
302 cps <- readIORef classPaths
303 t <- execClassPath $ addJAR x
304 writeIORef classPaths (JAR t:cps)