{- Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree. -} {-# LANGUAGE ApplicativeDo, TypeApplications #-} -- | Reversing DeclarationTargets into DeclarationSources -- -- * Load the DeclarationTargets which hold (source, [target]) -- into vector of (target, source) -- -- * Sort them by target -- -- * Group them by target -- -- * convert the group into (target, [source]) -- -- * Make this into DeclarationSources facts -- -- * chunk the facts and write to Glean module Derive.CxxDeclarationSources ( deriveCxxDeclarationSources ) where import Data.Int (Int64) import qualified Data.Vector.Algorithms.Intro as VSort import qualified Data.Vector.Unboxed as VU import qualified Data.Vector.Unboxed.Mutable as VUM import Data.Word (Word8) import Util.Log (logInfo) import Glean import qualified Glean.Schema.Cxx1.Types as Cxx import Glean.Util.Declarations (getDeclId) import qualified Glean.Util.ValueBuffer as Buffer import Derive.Types -- ----------------------------------------------------------------------------- -- | This represents which 'Cxx.Declaration' constructor is being -- used. This means we will not depend on each predicate -- having a non-overlapping space of fact Ids. type DeclTag x = x -- | Get the branch index. toDeclTag :: Cxx.Declaration -> DeclTag Word8 toDeclTag = \case Cxx.Declaration_namespace_{} -> 0 Cxx.Declaration_usingDeclaration{} -> 1 Cxx.Declaration_usingDirective{} -> 2 Cxx.Declaration_record_{} -> 3 Cxx.Declaration_enum_{} -> 4 Cxx.Declaration_function_{} -> 5 Cxx.Declaration_variable{} -> 6 Cxx.Declaration_objcContainer{} -> 7 Cxx.Declaration_objcMethod{} -> 8 Cxx.Declaration_objcProperty{} -> 9 Cxx.Declaration_typeAlias{} -> 10 Cxx.Declaration_namespaceAlias{} -> 11 Cxx.Declaration_EMPTY -> error "unknown Cxx.Declaration" -- | Reconstruct the 'Cxx.Declaration' from the branch index (creatged by -- 'toDeclTag') and the fact id. fromDeclTag :: DeclTag Word8 -> Int64 -> Cxx.Declaration fromDeclTag n i = case n of 0 -> Cxx.Declaration_namespace_ $! justId (IdOf (Fid i)) 1 -> Cxx.Declaration_usingDeclaration $! justId (IdOf (Fid i)) 2 -> Cxx.Declaration_usingDirective $! justId (IdOf (Fid i)) 3 -> Cxx.Declaration_record_ $! justId (IdOf (Fid i)) 4 -> Cxx.Declaration_enum_ $! justId (IdOf (Fid i)) 5 -> Cxx.Declaration_function_ $! justId (IdOf (Fid i)) 6 -> Cxx.Declaration_variable $! justId (IdOf (Fid i)) 7 -> Cxx.Declaration_objcContainer $! justId (IdOf (Fid i)) 8 -> Cxx.Declaration_objcMethod $! justId (IdOf (Fid i)) 9 -> Cxx.Declaration_objcProperty $! justId (IdOf (Fid i)) 10 -> Cxx.Declaration_typeAlias $! justId (IdOf (Fid i)) 11 -> Cxx.Declaration_namespaceAlias $! justId (IdOf (Fid i)) _ -> error "impossible fromDeclTag" -- | Decompose the declaration into pieces, and it can be --reconstructed by 'fromDecN' fromDecl :: Cxx.Declaration -> (DeclTag Word8, Int64) fromDecl d = (toDeclTag d, getDeclId d) -- | Represent an edge from target to source. Put the target's Id -- first so that sorting will only need the first component of -- the tuple. -- -- > (targetId, targetTag, sourceId, sourceTag) type TargetSource = (Int64, DeclTag Word8, Int64, DeclTag Word8) type TargetSourcesBuffer = Buffer.IOBuffer VU.Vector TargetSource type TargetSourcesM = VUM.IOVector TargetSource type TargetSources = VU.Vector TargetSource -- | Start with a small 'ValueBuffer', it will grow by doubling as needed. initialSize :: Int initialSize = 2^(10 :: Int) -- ----------------------------------------------------------------------------- addDeclarationTarget :: TargetSourcesBuffer -> () -> Cxx.DeclarationTargets_key -> IO () addDeclarationTarget buffer () Cxx.DeclarationTargets_key{..} = mapM_ (Buffer.push buffer) [ (targetId, targetTag, sourceId, sourceTag) | let (sourceTag, sourceId) = fromDecl declarationTargets_key_source , target <- declarationTargets_key_targets , let (targetTag, targetId) = fromDecl target ] -- | Create lazy list of facts to write toDeclarationSources :: TargetSources -> [Cxx.DeclarationSources_key] toDeclarationSources = loop where loop v | VU.null v = [] | otherwise = let front = VU.head v -- head is safe due to null check above (matching, next) = VU.span (eqTarget front) v this = Cxx.DeclarationSources_key { declarationSources_key_target = targetOf front , declarationSources_key_sources = map sourceOf (VU.toList matching) } in this : loop next eqTarget (x1, x2, _, _) (y1, y2, _, _) = x1 == y1 && x2 == y2 targetOf (targetId, targetTag, _, _) = fromDeclTag targetTag targetId sourceOf (_, _, sourceId, sourceTag) = fromDeclTag sourceTag sourceId -- | Write all the 'Cxx.DeclarationSources' facts to Glean writeDeclSources :: Writer -> TargetSources -> IO () writeDeclSources writer targetSources = do logInfo "writeDeclSources" mapM_ (\f -> writeFacts writer (makeFact_ @Cxx.DeclarationSources f)) (toDeclarationSources targetSources) logInfo "writeDeclSources done" deriveCxxDeclarationSources :: Backend e => e -> Config -> Writer -> IO () deriveCxxDeclarationSources e cfg writer = do logInfo "deriveCxxDeclarationSources" let q :: Query Cxx.DeclarationTargets_key q = keys $ maybe id limit (cfgMaxQueryFacts cfg) $ limitBytes (cfgMaxQuerySize cfg) (allFacts :: Query Cxx.DeclarationTargets) (targetSourcesM :: TargetSourcesM) <- do (buffer :: TargetSourcesBuffer) <- Buffer.new initialSize runQueryEach e (cfgRepo cfg) q () (addDeclarationTarget buffer) Buffer.get buffer logInfo "query complete, got targetSourcesM" VSort.sort targetSourcesM logInfo "sorted targetSourcesM" (targetSources :: TargetSources) <- VU.unsafeFreeze targetSourcesM writeDeclSources writer (VU.uniq targetSources) logInfo "deriveCxxDeclarationSources done"