|
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