
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}

{-
(c) The University of Glasgow 2006
(c) The AQUA Project, Glasgow University, 1998
-}

-- | Desugaring foreign declarations
module GHC.HsToCore.Foreign.Decl
  ( dsForeigns
  )
where

import GHC.Prelude
import GHC.Data.FastString

import GHC.Tc.Utils.Monad        -- temp

import GHC.HsToCore.Foreign.C
import GHC.HsToCore.Foreign.JavaScript
import GHC.HsToCore.Foreign.Utils
import GHC.HsToCore.Monad

import GHC.Hs
import GHC.Types.Id
import GHC.Types.ForeignStubs
import GHC.Unit.Module
import GHC.Core.Coercion

import GHC.Cmm.CLabel
import GHC.Types.ForeignCall
import GHC.Types.SrcLoc
import GHC.Utils.Outputable
import GHC.Driver.DynFlags
import GHC.Platform
import GHC.Data.OrdList
import GHC.Driver.Hooks

import Data.List (unzip4)

{-
Desugaring of @foreign@ declarations is naturally split up into
parts, an @import@ and an @export@  part. A @foreign import@
declaration
\begin{verbatim}
  foreign import cc nm f :: prim_args -> IO prim_res
\end{verbatim}
is the same as
\begin{verbatim}
  f :: prim_args -> IO prim_res
  f a1 ... an = _ccall_ nm cc a1 ... an
\end{verbatim}
so we reuse the desugaring code in @GHC.HsToCore.Foreign.Call@ to deal with these.
-}

dsForeigns :: [LForeignDecl GhcTc] -> DsM (ForeignStubs, OrdList Binding)
dsForeigns fos = do
    hooks <- getHooks
    case dsForeignsHook hooks of
        Nothing -> dsForeigns' fos
        Just h  -> h fos

dsForeigns' :: [LForeignDecl GhcTc]
            -> DsM (ForeignStubs, OrdList Binding)
dsForeigns' []
  = return (NoStubs, nilOL)
dsForeigns' fos = do
    mod <- getModule
    platform <- targetPlatform <$> getDynFlags
    fives <- mapM do_ldecl fos
    let
        (hs, cs, idss, bindss) = unzip4 fives
        fe_ids = concat idss
        fe_init_code = foreignExportsInitialiser platform mod fe_ids
    --
    return (ForeignStubs
             (mconcat hs)
             (mconcat cs `mappend` fe_init_code),
            foldr (appOL . toOL) nilOL bindss)
  where
   do_ldecl (L loc decl) = putSrcSpanDs (locA loc) (do_decl decl)

   do_decl :: ForeignDecl GhcTc -> DsM (CHeader, CStub, [Id], [Binding])
   do_decl (ForeignImport { fd_name = id, fd_i_ext = co, fd_fi = spec }) = do
      traceIf (text "fi start" <+> ppr id)
      let id' = unLoc id
      (bs, h, c) <- dsFImport id' co spec
      traceIf (text "fi end" <+> ppr id)
      return (h, c, [], bs)

   do_decl (ForeignExport { fd_name = L _ id
                          , fd_e_ext = co
                          , fd_fe = CExport _
                              (L _ (CExportStatic _ ext_nm cconv)) }) = do
      (h, c, _, _) <- dsFExport id co ext_nm cconv False
      return (h, c, [id], [])

{-
************************************************************************
*                                                                      *
\subsection{Foreign import}
*                                                                      *
************************************************************************

Desugaring foreign imports is just the matter of creating a binding
that on its RHS unboxes its arguments, performs the external call
(using the @CCallOp@ primop), before boxing the result up and returning it.

However, we create a worker/wrapper pair, thus:

        foreign import f :: Int -> IO Int
==>
        f x = IO ( \s -> case x of { I# x# ->
                         case fw s x# of { (# s1, y# #) ->
                         (# s1, I# y# #)}})

        fw s x# = ccall f s x#

The strictness/CPR analyser won't do this automatically because it doesn't look
inside returned tuples; but inlining this wrapper is a Really Good Idea
because it exposes the boxing to the call site.
-}

dsFImport :: Id
          -> Coercion
          -> ForeignImport (GhcPass p)
          -> DsM ([Binding], CHeader, CStub)
dsFImport id co (CImport _ cconv safety mHeader spec) = do
  platform <- getPlatform
  case platformArch platform of
    ArchJavaScript -> dsJsImport id co spec (unLoc cconv) (unLoc safety) mHeader
    _              -> dsCImport  id co spec (unLoc cconv) (unLoc safety) mHeader

{-
************************************************************************
*                                                                      *
\subsection{Foreign export}
*                                                                      *
************************************************************************

The function that does most of the work for `@foreign export@' declarations.
(see below for the boilerplate code a `@foreign export@' declaration expands
 into.)

For each `@foreign export foo@' in a module M we generate:
\begin{itemize}
\item a C function `@foo@', which calls
\item a Haskell stub `@M.\$ffoo@', which calls
\end{itemize}
the user-written Haskell function `@M.foo@'.
-}

dsFExport :: Id                 -- Either the exported Id,
                                -- or the foreign-export-dynamic constructor
          -> Coercion           -- Coercion between the Haskell type callable
                                -- from C, and its representation type
          -> CLabelString       -- The name to export to C land
          -> CCallConv
          -> Bool               -- True => foreign export dynamic
                                --         so invoke IO action that's hanging off
                                --         the first argument's stable pointer
          -> DsM ( CHeader      -- contents of Module_stub.h
                 , CStub        -- contents of Module_stub.c
                 , String       -- string describing type to pass to createAdj.
                 , Int          -- size of args to stub function
                 )
dsFExport fn_id co ext_name cconv is_dyn = do
  platform <- getPlatform
  case platformArch platform of
    ArchJavaScript -> dsJsFExport fn_id co ext_name cconv is_dyn
    _              -> dsCFExport  fn_id co ext_name cconv is_dyn


foreignExportsInitialiser :: Platform -> Module -> [Id] -> CStub
foreignExportsInitialiser _        _   []     = mempty
foreignExportsInitialiser platform mod hs_fns =
   -- Initialise foreign exports by registering a stable pointer from an
   -- __attribute__((constructor)) function.
   -- The alternative is to do this from stginit functions generated in
   -- codeGen/CodeGen.hs; however, stginit functions have a negative impact
   -- on binary sizes and link times because the static linker will think that
   -- all modules that are imported directly or indirectly are actually used by
   -- the program.
   -- (this is bad for big umbrella modules like Graphics.Rendering.OpenGL)
   --
   -- See Note [Tracking foreign exports] in rts/ForeignExports.c
   initializerCStub platform fn_nm list_decl fn_body
  where
    fn_nm       = mkInitializerStubLabel mod (fsLit "fexports")
    mod_str     = pprModuleName (moduleName mod)
    fn_body     = text "registerForeignExports" <> parens (char '&' <> list_symbol) <> semi
    list_symbol = text "stg_exports_" <> mod_str
    list_decl   = text "static struct ForeignExportsList" <+> list_symbol <+> equals
         <+> braces (
           text ".exports = " <+> export_list <> comma <+>
           text ".n_entries = " <+> ppr (length hs_fns))
         <> semi

    export_list = braces $ pprWithCommas closure_ptr hs_fns

    closure_ptr :: Id -> SDoc
    closure_ptr fn = text "(StgPtr) &" <> ppr fn <> text "_closure"

