|
1 | 1 | {- |
2 | 2 | Binary serialization for .hie files. |
3 | 3 | -} |
4 | | -{- HLINT ignore -} |
5 | | -{-# LANGUAGE ScopedTypeVariables #-} |
6 | | -{-# LANGUAGE BangPatterns #-} |
7 | 4 |
|
8 | | -module Compat.HieBin |
9 | | - ( readHieFile |
10 | | - , readHieFileWithVersion |
11 | | - , HieHeader |
12 | | - , writeHieFile |
13 | | - , HieName(..) |
14 | | - , toHieName |
15 | | - , HieFileResult(..) |
16 | | - , hieMagic |
17 | | - , hieNameOcc |
18 | | - , NameCacheUpdater(..) |
19 | | - ) |
| 5 | +module Compat.HieBin ( module GHC.Iface.Ext.Binary) |
20 | 6 | where |
21 | 7 |
|
22 | | -import GHC.Settings.Utils ( maybeRead ) |
23 | | -import GHC.Settings.Config ( cProjectVersion ) |
24 | | --- import GHC.Prelude |
25 | | -import GHC.Utils.Binary |
26 | | -import GHC.Iface.Binary ( getDictFastString ) |
27 | | -import GHC.Data.FastMutInt |
28 | | -import GHC.Data.FastString ( FastString ) |
29 | | -import GHC.Types.Name |
30 | | -import GHC.Types.Name.Cache |
31 | | -import GHC.Utils.Outputable |
32 | | -import GHC.Builtin.Utils |
33 | | -import GHC.Types.SrcLoc as SrcLoc |
34 | | -import GHC.Types.Unique.Supply ( takeUniqFromSupply ) |
35 | | -import GHC.Types.Unique |
36 | | -import GHC.Types.Unique.FM |
37 | | -import GHC.Iface.Env (NameCacheUpdater(..)) |
38 | | --- import IfaceEnv |
39 | | - |
40 | | -import qualified Data.Array as A |
41 | | -import Data.IORef |
42 | | -import Data.ByteString ( ByteString ) |
43 | | -import qualified Data.ByteString as BS |
44 | | -import qualified Data.ByteString.Char8 as BSC |
45 | | -import Data.List ( mapAccumR ) |
46 | | -import Data.Word ( Word8, Word32 ) |
47 | | -import Control.Monad ( replicateM, when ) |
48 | | -import System.Directory ( createDirectoryIfMissing ) |
49 | | -import System.FilePath ( takeDirectory ) |
50 | | - |
51 | | -import GHC.Iface.Ext.Types |
52 | | - |
53 | | -data HieSymbolTable = HieSymbolTable |
54 | | - { hie_symtab_next :: !FastMutInt |
55 | | - , hie_symtab_map :: !(IORef (UniqFM Name (Int, HieName))) |
56 | | - } |
57 | | - |
58 | | -data HieDictionary = HieDictionary |
59 | | - { hie_dict_next :: !FastMutInt -- The next index to use |
60 | | - , hie_dict_map :: !(IORef (UniqFM FastString (Int,FastString))) -- indexed by FastString |
61 | | - } |
62 | | - |
63 | | -initBinMemSize :: Int |
64 | | -initBinMemSize = 1024*1024 |
65 | | - |
66 | | --- | The header for HIE files - Capital ASCII letters \"HIE\". |
67 | | -hieMagic :: [Word8] |
68 | | -hieMagic = [72,73,69] |
69 | | - |
70 | | -hieMagicLen :: Int |
71 | | -hieMagicLen = length hieMagic |
72 | | - |
73 | | -ghcVersion :: ByteString |
74 | | -ghcVersion = BSC.pack cProjectVersion |
75 | | - |
76 | | -putBinLine :: BinHandle -> ByteString -> IO () |
77 | | -putBinLine bh xs = do |
78 | | - mapM_ (putByte bh) $ BS.unpack xs |
79 | | - putByte bh 10 -- newline char |
80 | | - |
81 | | --- | Write a `HieFile` to the given `FilePath`, with a proper header and |
82 | | --- symbol tables for `Name`s and `FastString`s |
83 | | -writeHieFile :: FilePath -> HieFile -> IO () |
84 | | -writeHieFile hie_file_path hiefile = do |
85 | | - bh0 <- openBinMem initBinMemSize |
86 | | - |
87 | | - -- Write the header: hieHeader followed by the |
88 | | - -- hieVersion and the GHC version used to generate this file |
89 | | - mapM_ (putByte bh0) hieMagic |
90 | | - putBinLine bh0 $ BSC.pack $ show hieVersion |
91 | | - putBinLine bh0 $ ghcVersion |
92 | | - |
93 | | - -- remember where the dictionary pointer will go |
94 | | - dict_p_p <- tellBin bh0 |
95 | | - put_ bh0 dict_p_p |
96 | | - |
97 | | - -- remember where the symbol table pointer will go |
98 | | - symtab_p_p <- tellBin bh0 |
99 | | - put_ bh0 symtab_p_p |
100 | | - |
101 | | - -- Make some initial state |
102 | | - symtab_next <- newFastMutInt |
103 | | - writeFastMutInt symtab_next 0 |
104 | | - symtab_map <- newIORef emptyUFM :: IO (IORef (UniqFM Name (Int, HieName))) |
105 | | - let hie_symtab = HieSymbolTable { |
106 | | - hie_symtab_next = symtab_next, |
107 | | - hie_symtab_map = symtab_map } |
108 | | - dict_next_ref <- newFastMutInt |
109 | | - writeFastMutInt dict_next_ref 0 |
110 | | - dict_map_ref <- newIORef emptyUFM |
111 | | - let hie_dict = HieDictionary { |
112 | | - hie_dict_next = dict_next_ref, |
113 | | - hie_dict_map = dict_map_ref } |
114 | | - |
115 | | - -- put the main thing |
116 | | - let bh = setUserData bh0 $ newWriteState (putName hie_symtab) |
117 | | - (putName hie_symtab) |
118 | | - (putFastString hie_dict) |
119 | | - put_ bh hiefile |
120 | | - |
121 | | - -- write the symtab pointer at the front of the file |
122 | | - symtab_p <- tellBin bh |
123 | | - putAt bh symtab_p_p symtab_p |
124 | | - seekBin bh symtab_p |
125 | | - |
126 | | - -- write the symbol table itself |
127 | | - symtab_next' <- readFastMutInt symtab_next |
128 | | - symtab_map' <- readIORef symtab_map |
129 | | - putSymbolTable bh symtab_next' symtab_map' |
130 | | - |
131 | | - -- write the dictionary pointer at the front of the file |
132 | | - dict_p <- tellBin bh |
133 | | - putAt bh dict_p_p dict_p |
134 | | - seekBin bh dict_p |
135 | | - |
136 | | - -- write the dictionary itself |
137 | | - dict_next <- readFastMutInt dict_next_ref |
138 | | - dict_map <- readIORef dict_map_ref |
139 | | - putDictionary bh dict_next dict_map |
140 | | - |
141 | | - -- and send the result to the file |
142 | | - createDirectoryIfMissing True (takeDirectory hie_file_path) |
143 | | - writeBinMem bh hie_file_path |
144 | | - return () |
145 | | - |
146 | | -data HieFileResult |
147 | | - = HieFileResult |
148 | | - { hie_file_result_version :: Integer |
149 | | - , hie_file_result_ghc_version :: ByteString |
150 | | - , hie_file_result :: HieFile |
151 | | - } |
152 | | - |
153 | | -type HieHeader = (Integer, ByteString) |
154 | | - |
155 | | --- | Read a `HieFile` from a `FilePath`. Can use |
156 | | --- an existing `NameCache`. Allows you to specify |
157 | | --- which versions of hieFile to attempt to read. |
158 | | --- `Left` case returns the failing header versions. |
159 | | -readHieFileWithVersion :: (HieHeader -> Bool) -> NameCacheUpdater -> FilePath -> IO (Either HieHeader HieFileResult) |
160 | | -readHieFileWithVersion readVersion ncu file = do |
161 | | - bh0 <- readBinMem file |
162 | | - |
163 | | - (hieVersion, ghcVersion) <- readHieFileHeader file bh0 |
164 | | - |
165 | | - if readVersion (hieVersion, ghcVersion) |
166 | | - then do |
167 | | - hieFile <- readHieFileContents bh0 ncu |
168 | | - return $ Right (HieFileResult hieVersion ghcVersion hieFile) |
169 | | - else return $ Left (hieVersion, ghcVersion) |
170 | | - |
171 | | - |
172 | | --- | Read a `HieFile` from a `FilePath`. Can use |
173 | | --- an existing `NameCache`. |
174 | | -readHieFile :: NameCacheUpdater -> FilePath -> IO HieFileResult |
175 | | -readHieFile ncu file = do |
176 | | - |
177 | | - bh0 <- readBinMem file |
178 | | - |
179 | | - (readHieVersion, ghcVersion) <- readHieFileHeader file bh0 |
180 | | - |
181 | | - -- Check if the versions match |
182 | | - when (readHieVersion /= hieVersion) $ |
183 | | - panic $ unwords ["readHieFile: hie file versions don't match for file:" |
184 | | - , file |
185 | | - , "Expected" |
186 | | - , show hieVersion |
187 | | - , "but got", show readHieVersion |
188 | | - ] |
189 | | - hieFile <- readHieFileContents bh0 ncu |
190 | | - return $ HieFileResult hieVersion ghcVersion hieFile |
191 | | - |
192 | | -readBinLine :: BinHandle -> IO ByteString |
193 | | -readBinLine bh = BS.pack . reverse <$> loop [] |
194 | | - where |
195 | | - loop acc = do |
196 | | - char <- get bh :: IO Word8 |
197 | | - if char == 10 -- ASCII newline '\n' |
198 | | - then return acc |
199 | | - else loop (char : acc) |
200 | | - |
201 | | -readHieFileHeader :: FilePath -> BinHandle -> IO HieHeader |
202 | | -readHieFileHeader file bh0 = do |
203 | | - -- Read the header |
204 | | - magic <- replicateM hieMagicLen (get bh0) |
205 | | - version <- BSC.unpack <$> readBinLine bh0 |
206 | | - case maybeRead version of |
207 | | - Nothing -> |
208 | | - panic $ unwords ["readHieFileHeader: hieVersion isn't an Integer:" |
209 | | - , show version |
210 | | - ] |
211 | | - Just readHieVersion -> do |
212 | | - ghcVersion <- readBinLine bh0 |
213 | | - |
214 | | - -- Check if the header is valid |
215 | | - when (magic /= hieMagic) $ |
216 | | - panic $ unwords ["readHieFileHeader: headers don't match for file:" |
217 | | - , file |
218 | | - , "Expected" |
219 | | - , show hieMagic |
220 | | - , "but got", show magic |
221 | | - ] |
222 | | - return (readHieVersion, ghcVersion) |
223 | | - |
224 | | -readHieFileContents :: BinHandle -> NameCacheUpdater -> IO HieFile |
225 | | -readHieFileContents bh0 ncu = do |
226 | | - |
227 | | - dict <- get_dictionary bh0 |
228 | | - |
229 | | - -- read the symbol table so we are capable of reading the actual data |
230 | | - bh1 <- do |
231 | | - let bh1 = setUserData bh0 $ newReadState (error "getSymtabName") |
232 | | - (getDictFastString dict) |
233 | | - symtab <- get_symbol_table bh1 |
234 | | - let bh1' = setUserData bh1 |
235 | | - $ newReadState (getSymTabName symtab) |
236 | | - (getDictFastString dict) |
237 | | - return bh1' |
238 | | - |
239 | | - -- load the actual data |
240 | | - hiefile <- get bh1 |
241 | | - return hiefile |
242 | | - where |
243 | | - get_dictionary bin_handle = do |
244 | | - dict_p <- get bin_handle |
245 | | - data_p <- tellBin bin_handle |
246 | | - seekBin bin_handle dict_p |
247 | | - dict <- getDictionary bin_handle |
248 | | - seekBin bin_handle data_p |
249 | | - return dict |
250 | | - |
251 | | - get_symbol_table bh1 = do |
252 | | - symtab_p <- get bh1 |
253 | | - data_p' <- tellBin bh1 |
254 | | - seekBin bh1 symtab_p |
255 | | - symtab <- getSymbolTable bh1 ncu |
256 | | - seekBin bh1 data_p' |
257 | | - return symtab |
258 | | - |
259 | | -putFastString :: HieDictionary -> BinHandle -> FastString -> IO () |
260 | | -putFastString HieDictionary { hie_dict_next = j_r, |
261 | | - hie_dict_map = out_r} bh f |
262 | | - = do |
263 | | - out <- readIORef out_r |
264 | | - let !unique = getUnique f |
265 | | - case lookupUFM_Directly out unique of |
266 | | - Just (j, _) -> put_ bh (fromIntegral j :: Word32) |
267 | | - Nothing -> do |
268 | | - j <- readFastMutInt j_r |
269 | | - put_ bh (fromIntegral j :: Word32) |
270 | | - writeFastMutInt j_r (j + 1) |
271 | | - writeIORef out_r $! addToUFM_Directly out unique (j, f) |
272 | | - |
273 | | -putSymbolTable :: BinHandle -> Int -> UniqFM Name (Int,HieName) -> IO () |
274 | | -putSymbolTable bh next_off symtab = do |
275 | | - put_ bh next_off |
276 | | - let names = A.elems (A.array (0,next_off-1) (nonDetEltsUFM symtab)) |
277 | | - mapM_ (putHieName bh) names |
278 | | - |
279 | | -getSymbolTable :: BinHandle -> NameCacheUpdater -> IO SymbolTable |
280 | | -getSymbolTable bh ncu = do |
281 | | - sz <- get bh |
282 | | - od_names <- replicateM sz (getHieName bh) |
283 | | - updateNameCache ncu $ \nc -> |
284 | | - let arr = A.listArray (0,sz-1) names |
285 | | - (nc', names) = mapAccumR fromHieName nc od_names |
286 | | - in (nc',arr) |
287 | | - |
288 | | -getSymTabName :: SymbolTable -> BinHandle -> IO Name |
289 | | -getSymTabName st bh = do |
290 | | - i :: Word32 <- get bh |
291 | | - return $ st A.! (fromIntegral i) |
292 | | - |
293 | | -putName :: HieSymbolTable -> BinHandle -> Name -> IO () |
294 | | -putName (HieSymbolTable next ref) bh name = do |
295 | | - symmap <- readIORef ref |
296 | | - case lookupUFM symmap name of |
297 | | - Just (off, ExternalName mod occ (UnhelpfulSpan _)) |
298 | | - | isGoodSrcSpan (nameSrcSpan name) -> do |
299 | | - let hieName = ExternalName mod occ (nameSrcSpan name) |
300 | | - writeIORef ref $! addToUFM symmap name (off, hieName) |
301 | | - put_ bh (fromIntegral off :: Word32) |
302 | | - Just (off, LocalName _occ span) |
303 | | - | notLocal (toHieName name) || nameSrcSpan name /= span -> do |
304 | | - writeIORef ref $! addToUFM symmap name (off, toHieName name) |
305 | | - put_ bh (fromIntegral off :: Word32) |
306 | | - Just (off, _) -> put_ bh (fromIntegral off :: Word32) |
307 | | - Nothing -> do |
308 | | - off <- readFastMutInt next |
309 | | - writeFastMutInt next (off+1) |
310 | | - writeIORef ref $! addToUFM symmap name (off, toHieName name) |
311 | | - put_ bh (fromIntegral off :: Word32) |
312 | | - |
313 | | - where |
314 | | - notLocal :: HieName -> Bool |
315 | | - notLocal LocalName{} = False |
316 | | - notLocal _ = True |
317 | | - |
318 | | - |
319 | | --- ** Converting to and from `HieName`'s |
320 | | - |
321 | | -fromHieName :: NameCache -> HieName -> (NameCache, Name) |
322 | | -fromHieName nc (ExternalName mod occ span) = |
323 | | - let cache = nsNames nc |
324 | | - in case lookupOrigNameCache cache mod occ of |
325 | | - Just name |
326 | | - | nameSrcSpan name == span -> (nc, name) |
327 | | - | otherwise -> |
328 | | - let name' = setNameLoc name span |
329 | | - new_cache = extendNameCache cache mod occ name' |
330 | | - in ( nc{ nsNames = new_cache }, name' ) |
331 | | - Nothing -> |
332 | | - let (uniq, us) = takeUniqFromSupply (nsUniqs nc) |
333 | | - name = mkExternalName uniq mod occ span |
334 | | - new_cache = extendNameCache cache mod occ name |
335 | | - in ( nc{ nsUniqs = us, nsNames = new_cache }, name ) |
336 | | -fromHieName nc (LocalName occ span) = |
337 | | - let (uniq, us) = takeUniqFromSupply (nsUniqs nc) |
338 | | - name = mkInternalName uniq occ span |
339 | | - in ( nc{ nsUniqs = us }, name ) |
340 | | -fromHieName nc (KnownKeyName u) = case lookupKnownKeyName u of |
341 | | - Nothing -> pprPanic "fromHieName:unknown known-key unique" |
342 | | - (ppr (unpkUnique u)) |
343 | | - Just n -> (nc, n) |
344 | | - |
345 | | --- ** Reading and writing `HieName`'s |
346 | | - |
347 | | -putHieName :: BinHandle -> HieName -> IO () |
348 | | -putHieName bh (ExternalName mod occ span) = do |
349 | | - putByte bh 0 |
350 | | - put_ bh (mod, occ, span) |
351 | | -putHieName bh (LocalName occName span) = do |
352 | | - putByte bh 1 |
353 | | - put_ bh (occName, span) |
354 | | -putHieName bh (KnownKeyName uniq) = do |
355 | | - putByte bh 2 |
356 | | - put_ bh $ unpkUnique uniq |
357 | | - |
358 | | -getHieName :: BinHandle -> IO HieName |
359 | | -getHieName bh = do |
360 | | - t <- getByte bh |
361 | | - case t of |
362 | | - 0 -> do |
363 | | - (modu, occ, span) <- get bh |
364 | | - return $ ExternalName modu occ span |
365 | | - 1 -> do |
366 | | - (occ, span) <- get bh |
367 | | - return $ LocalName occ span |
368 | | - 2 -> do |
369 | | - (c,i) <- get bh |
370 | | - return $ KnownKeyName $ mkUnique c i |
371 | | - _ -> panic "GHC.Iface.Ext.Binary.getHieName: invalid tag" |
| 8 | +import GHC.Iface.Ext.Binary |
0 commit comments