Changes between Version 1 and Version 2 of Ghc/Hooks


Ignore:
Timestamp:
Sep 4, 2013 10:08:17 PM (2 years ago)
Author:
nominolo
Comment:

Add example code and implementation sketch

Legend:

Unmodified
Added
Removed
Modified
  • Ghc/Hooks

    v1 v2  
    1313Instead we identify "interesting" places inside the compiler and allow users of the GHC API to specify a call-back that gets invoked when execution reaches that place.  For example, instead of calling {{{typecheckRename}}} function directly, we first look up whether there is a hook specified for and if so call that function instead.  The hook may choose to perform its own renaming and type checking passes (unlikely) or call the GHC's {{{typecheckRename}}} function and inspect its output and generate additional files on disk.
    1414
     15== Example ==
     16
     17{{{
     18...
     19  withGhc libdir $ do
     20    dflags0 <- getSessionDynFlags
     21    let dflags1 = dflags0{ hooks = insertHook LocateLibHook myLocateLib
     22                                 . insertHook LinkDynLibHook myLinkDynLibHook
     23                                 $ hooks dflags0 }
     24    setSessionDynFlags dflags1
     25...
     26
     27myLocateLib :: DynFlags -> Bool -> [FilePath] -> String -> IO LibrarySpec
     28myLocateLib ...
     29
     30myLinkDynLibHook :: DynFlags -> [FilePath] -> [PackageId] -> IO ()
     31myLinkDynLibHook dflags paths ids = do
     32
     33}}}                                                .
     34
     35The two functions will be called whenever GHC needs to locate or link a dynamically loaded library.
     36
    1537== The Hook datatype ==
    1638
    1739Each hook has a potentially different type from all the other hooks. Additionally, we need to be able to communicate hooks to all the locations where they may be invoked. This is achieved by storing the list of hooks in the {{{DynFlags}}}.  This, however, means that hooks cannot be defined as an ADT, as that would lead to huge cyclic imports (the data types used by the hooks will depend on {{{DynFlags}}}, but the {{{DynFlags}}} will depend on the hook data type.  Instead we {{{Hooks}}} is an untyped key-value store.  The keys are single constructor types and the {{{Hooks}}} map is indexed by their {{{TypeRep}}}.  We recover the hook type via a type family:
    1840
     41{{{
     42--- Implementation Sketch -----------------------
     43data Hook = forall a. Hook TypeRep a
     44
     45type Hooks = Map TypeRep Hook
     46
     47type family HookType a :: *
     48
     49insertHook :: forall a. Typeable a => a -> HookType a -> Hooks -> Hooks
     50insertHook key value hooks =
     51  let hook = Hook (typeOf key) value in
     52  Map.insert (typeOf key) hook hooks
     53
     54lookupHook :: forall a. Typeable a => Hooks -> Maybe (HookType a)
     55lookupHook hooks =
     56  let key = typeOf (undefined :: a) in
     57  case Map.lookup key of
     58     Nothing -> Nothing
     59     Just (Hook _ h) -> Just (unsafeCoerce h :: HookType a)  -- the tricky bit
     60}}}
     61
     62{{{
     63-- usage:
     64data LocateLibHook = LocateLibHook deriving Typeable
     65
     66type instance HookType LocateLibHook = DynFlags -> Bool -> [FilePath] -> String -> IO LibrarySpec
     67}}}
     68
     69== TODO: List all currently available hooks ==
     70