{-# LANGUAGE CPP, UnboxedTuples, MagicHash, ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
module GHCi.ObjLink
( initObjLinker, ShouldRetainCAFs(..)
, loadDLL
, loadArchive
, loadObj
, unloadObj
, purgeObj
, lookupSymbol
, lookupSymbolInDLL
, lookupClosure
, resolveObjs
, addLibrarySearchPath
, removeLibrarySearchPath
, findSystemLibrary
) where
import Prelude
import GHCi.RemoteTypes
import GHCi.Message (LoadedDLL)
import Control.Exception (throwIO, ErrorCall(..))
import Control.Monad ( when )
import Foreign.C
import Foreign.Marshal.Alloc ( alloca, free )
import Foreign ( nullPtr, peek )
import GHC.Exts
import System.Posix.Internals ( CFilePath, withFilePath, peekFilePath )
import System.FilePath ( dropExtension, normalise )
#if defined(wasm32_HOST_ARCH)
import Control.Exception (catch, evaluate)
import GHC.Wasm.Prim
#endif
data ShouldRetainCAFs
= RetainCAFs
| DontRetainCAFs
#if defined(wasm32_HOST_ARCH)
initObjLinker :: ShouldRetainCAFs -> IO ()
initObjLinker _ = pure ()
loadDLL :: String -> IO (Either String (Ptr LoadedDLL))
loadDLL f =
m `catch` \(err :: JSException) ->
pure $ Left $ "loadDLL failed for " <> f <> ": " <> show err
where
m = do
evaluate =<< js_loadDLL (toJSString f)
pure $ Right nullPtr
foreign import javascript safe "__exports.__dyld.loadDLL($1)"
js_loadDLL :: JSString -> IO ()
loadArchive :: String -> IO ()
loadArchive f = throwIO $ ErrorCall $ "loadArchive: unsupported on wasm for " <> f
loadObj :: String -> IO ()
loadObj f = throwIO $ ErrorCall $ "loadObj: unsupported on wasm for " <> f
unloadObj :: String -> IO ()
unloadObj f = throwIO $ ErrorCall $ "unloadObj: unsupported on wasm for " <> f
purgeObj :: String -> IO ()
purgeObj f = throwIO $ ErrorCall $ "purgeObj: unsupported on wasm for " <> f
lookupSymbol :: String -> IO (Maybe (Ptr a))
lookupSymbol sym = do
r <- js_lookupSymbol $ toJSString sym
evaluate $ if r == nullPtr then Nothing else Just r
foreign import javascript unsafe "__exports.__dyld.lookupSymbol($1)"
js_lookupSymbol :: JSString -> IO (Ptr a)
lookupSymbolInDLL :: Ptr LoadedDLL -> String -> IO (Maybe (Ptr a))
lookupSymbolInDLL _ sym =
throwIO $ ErrorCall $ "lookupSymbolInDLL: unsupported on wasm for " <> sym
resolveObjs :: IO Bool
resolveObjs = pure True
addLibrarySearchPath :: String -> IO (Ptr ())
addLibrarySearchPath p = do
evaluate =<< js_addLibrarySearchPath (toJSString p)
pure nullPtr
foreign import javascript safe "__exports.__dyld.addLibrarySearchPath($1)"
js_addLibrarySearchPath :: JSString -> IO ()
removeLibrarySearchPath :: Ptr () -> IO Bool
removeLibrarySearchPath _ = pure True
findSystemLibrary :: String -> IO (Maybe String)
findSystemLibrary f = m `catch` \(_ :: JSException) -> pure Nothing
where
m = do
p' <- js_findSystemLibrary (toJSString f)
p <- evaluate $ fromJSString p'
pure $ Just p
foreign import javascript safe "__exports.__dyld.findSystemLibrary($1)"
js_findSystemLibrary :: JSString -> IO JSString
#else
initObjLinker :: ShouldRetainCAFs -> IO ()
initObjLinker :: ShouldRetainCAFs -> IO ()
initObjLinker ShouldRetainCAFs
RetainCAFs = CInt -> IO ()
c_initLinker_ CInt
1
initObjLinker ShouldRetainCAFs
_ = CInt -> IO ()
c_initLinker_ CInt
0
lookupSymbol :: String -> IO (Maybe (Ptr a))
lookupSymbol :: forall a. String -> IO (Maybe (Ptr a))
lookupSymbol String
str_in = do
let str :: String
str = String -> String
prefixUnderscore String
str_in
String -> (CFilePath -> IO (Maybe (Ptr a))) -> IO (Maybe (Ptr a))
forall a. String -> (CFilePath -> IO a) -> IO a
withCAString String
str ((CFilePath -> IO (Maybe (Ptr a))) -> IO (Maybe (Ptr a)))
-> (CFilePath -> IO (Maybe (Ptr a))) -> IO (Maybe (Ptr a))
forall a b. (a -> b) -> a -> b
$ \CFilePath
c_str -> do
addr <- CFilePath -> IO (Ptr a)
forall a. CFilePath -> IO (Ptr a)
c_lookupSymbol CFilePath
c_str
if addr == nullPtr
then return Nothing
else return (Just addr)
lookupSymbolInDLL :: Ptr LoadedDLL -> String -> IO (Maybe (Ptr a))
lookupSymbolInDLL :: forall a. Ptr LoadedDLL -> String -> IO (Maybe (Ptr a))
lookupSymbolInDLL Ptr LoadedDLL
dll String
str_in = do
let str :: String
str = String -> String
prefixUnderscore String
str_in
String -> (CFilePath -> IO (Maybe (Ptr a))) -> IO (Maybe (Ptr a))
forall a. String -> (CFilePath -> IO a) -> IO a
withCAString String
str ((CFilePath -> IO (Maybe (Ptr a))) -> IO (Maybe (Ptr a)))
-> (CFilePath -> IO (Maybe (Ptr a))) -> IO (Maybe (Ptr a))
forall a b. (a -> b) -> a -> b
$ \CFilePath
c_str -> do
addr <- Ptr LoadedDLL -> CFilePath -> IO (Ptr a)
forall a. Ptr LoadedDLL -> CFilePath -> IO (Ptr a)
c_lookupSymbolInNativeObj Ptr LoadedDLL
dll CFilePath
c_str
if addr == nullPtr
then return Nothing
else return (Just addr)
prefixUnderscore :: String -> String
prefixUnderscore :: String -> String
prefixUnderscore
| Bool
cLeadingUnderscore = (Char
'_'Char -> String -> String
forall a. a -> [a] -> [a]
:)
| Bool
otherwise = String -> String
forall a. a -> a
id
loadDLL :: String -> IO (Either String (Ptr LoadedDLL))
loadDLL :: String -> IO (Either String (Ptr LoadedDLL))
loadDLL String
str0 = do
let
str :: String
str | Bool
isWindowsHost = String -> String
dropExtension String
str0
| Bool
otherwise = String
str0
(maybe_handle, maybe_errmsg) <- String
-> (CFilePath -> IO (Ptr LoadedDLL, CFilePath))
-> IO (Ptr LoadedDLL, CFilePath)
forall a. String -> (CFilePath -> IO a) -> IO a
withFilePath (String -> String
normalise String
str) ((CFilePath -> IO (Ptr LoadedDLL, CFilePath))
-> IO (Ptr LoadedDLL, CFilePath))
-> (CFilePath -> IO (Ptr LoadedDLL, CFilePath))
-> IO (Ptr LoadedDLL, CFilePath)
forall a b. (a -> b) -> a -> b
$ \CFilePath
dll ->
(Ptr CFilePath -> IO (Ptr LoadedDLL, CFilePath))
-> IO (Ptr LoadedDLL, CFilePath)
forall a b. Storable a => (Ptr a -> IO b) -> IO b
alloca ((Ptr CFilePath -> IO (Ptr LoadedDLL, CFilePath))
-> IO (Ptr LoadedDLL, CFilePath))
-> (Ptr CFilePath -> IO (Ptr LoadedDLL, CFilePath))
-> IO (Ptr LoadedDLL, CFilePath)
forall a b. (a -> b) -> a -> b
$ \Ptr CFilePath
errmsg_ptr -> (,)
(Ptr LoadedDLL -> CFilePath -> (Ptr LoadedDLL, CFilePath))
-> IO (Ptr LoadedDLL)
-> IO (CFilePath -> (Ptr LoadedDLL, CFilePath))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CFilePath -> Ptr CFilePath -> IO (Ptr LoadedDLL)
c_loadNativeObj CFilePath
dll Ptr CFilePath
errmsg_ptr
IO (CFilePath -> (Ptr LoadedDLL, CFilePath))
-> IO CFilePath -> IO (Ptr LoadedDLL, CFilePath)
forall a b. IO (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Ptr CFilePath -> IO CFilePath
forall a. Storable a => Ptr a -> IO a
peek Ptr CFilePath
errmsg_ptr
if maybe_handle == nullPtr
then do str <- peekCString maybe_errmsg
free maybe_errmsg
return (Left str)
else return (Right maybe_handle)
loadArchive :: String -> IO ()
loadArchive :: String -> IO ()
loadArchive String
str = do
String -> (CFilePath -> IO ()) -> IO ()
forall a. String -> (CFilePath -> IO a) -> IO a
withFilePath String
str ((CFilePath -> IO ()) -> IO ()) -> (CFilePath -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \CFilePath
c_str -> do
r <- CFilePath -> IO Int
c_loadArchive CFilePath
c_str
when (r == 0) (throwIO (ErrorCall ("loadArchive " ++ show str ++ ": failed")))
loadObj :: String -> IO ()
loadObj :: String -> IO ()
loadObj String
str = do
String -> (CFilePath -> IO ()) -> IO ()
forall a. String -> (CFilePath -> IO a) -> IO a
withFilePath String
str ((CFilePath -> IO ()) -> IO ()) -> (CFilePath -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \CFilePath
c_str -> do
r <- CFilePath -> IO Int
c_loadObj CFilePath
c_str
when (r == 0) (throwIO (ErrorCall ("loadObj " ++ show str ++ ": failed")))
unloadObj :: String -> IO ()
unloadObj :: String -> IO ()
unloadObj String
str =
String -> (CFilePath -> IO ()) -> IO ()
forall a. String -> (CFilePath -> IO a) -> IO a
withFilePath String
str ((CFilePath -> IO ()) -> IO ()) -> (CFilePath -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \CFilePath
c_str -> do
r <- CFilePath -> IO Int
c_unloadObj CFilePath
c_str
when (r == 0) (throwIO (ErrorCall ("unloadObj " ++ show str ++ ": failed")))
purgeObj :: String -> IO ()
purgeObj :: String -> IO ()
purgeObj String
str =
String -> (CFilePath -> IO ()) -> IO ()
forall a. String -> (CFilePath -> IO a) -> IO a
withFilePath String
str ((CFilePath -> IO ()) -> IO ()) -> (CFilePath -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \CFilePath
c_str -> do
r <- CFilePath -> IO Int
c_purgeObj CFilePath
c_str
when (r == 0) (throwIO (ErrorCall ("purgeObj " ++ show str ++ ": failed")))
addLibrarySearchPath :: String -> IO (Ptr ())
addLibrarySearchPath :: String -> IO (Ptr ())
addLibrarySearchPath String
str =
String -> (CFilePath -> IO (Ptr ())) -> IO (Ptr ())
forall a. String -> (CFilePath -> IO a) -> IO a
withFilePath String
str CFilePath -> IO (Ptr ())
c_addLibrarySearchPath
removeLibrarySearchPath :: Ptr () -> IO Bool
removeLibrarySearchPath :: Ptr () -> IO Bool
removeLibrarySearchPath = Ptr () -> IO Bool
c_removeLibrarySearchPath
findSystemLibrary :: String -> IO (Maybe String)
findSystemLibrary :: String -> IO (Maybe String)
findSystemLibrary String
str = do
result <- String -> (CFilePath -> IO CFilePath) -> IO CFilePath
forall a. String -> (CFilePath -> IO a) -> IO a
withFilePath String
str CFilePath -> IO CFilePath
c_findSystemLibrary
case result == nullPtr of
Bool
True -> Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing
Bool
False -> do path <- CFilePath -> IO String
peekFilePath CFilePath
result
free result
return $ Just path
resolveObjs :: IO Bool
resolveObjs :: IO Bool
resolveObjs = do
r <- IO Int
c_resolveObjs
return (r /= 0)
foreign import ccall unsafe "loadNativeObj" c_loadNativeObj :: CFilePath -> Ptr CString -> IO (Ptr LoadedDLL)
foreign import ccall unsafe "lookupSymbolInNativeObj" c_lookupSymbolInNativeObj :: Ptr LoadedDLL -> CString -> IO (Ptr a)
foreign import ccall unsafe "initLinker_" c_initLinker_ :: CInt -> IO ()
foreign import ccall unsafe "lookupSymbol" c_lookupSymbol :: CString -> IO (Ptr a)
foreign import ccall unsafe "loadArchive" c_loadArchive :: CFilePath -> IO Int
foreign import ccall unsafe "loadObj" c_loadObj :: CFilePath -> IO Int
foreign import ccall unsafe "purgeObj" c_purgeObj :: CFilePath -> IO Int
foreign import ccall unsafe "unloadObj" c_unloadObj :: CFilePath -> IO Int
foreign import ccall unsafe "resolveObjs" c_resolveObjs :: IO Int
foreign import ccall unsafe "addLibrarySearchPath" c_addLibrarySearchPath :: CFilePath -> IO (Ptr ())
foreign import ccall unsafe "findSystemLibrary" c_findSystemLibrary :: CFilePath -> IO CFilePath
foreign import ccall unsafe "removeLibrarySearchPath" c_removeLibrarySearchPath :: Ptr() -> IO Bool
#include "ghcautoconf.h"
cLeadingUnderscore :: Bool
#if defined(LEADING_UNDERSCORE)
cLeadingUnderscore = True
#else
cLeadingUnderscore :: Bool
cLeadingUnderscore = Bool
False
#endif
isWindowsHost :: Bool
#if defined(mingw32_HOST_OS)
isWindowsHost = True
#else
isWindowsHost :: Bool
isWindowsHost = Bool
False
#endif
#endif
lookupClosure :: String -> IO (Maybe HValueRef)
lookupClosure :: String -> IO (Maybe HValueRef)
lookupClosure String
str = do
m <- String -> IO (Maybe (Ptr (ZonkAny 0)))
forall a. String -> IO (Maybe (Ptr a))
lookupSymbol String
str
case m of
Maybe (Ptr (ZonkAny 0))
Nothing -> Maybe HValueRef -> IO (Maybe HValueRef)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe HValueRef
forall a. Maybe a
Nothing
Just (Ptr Addr#
addr) -> case Addr# -> (# Any #)
forall a. Addr# -> (# a #)
addrToAny# Addr#
addr of
(# Any
a #) -> HValueRef -> Maybe HValueRef
forall a. a -> Maybe a
Just (HValueRef -> Maybe HValueRef)
-> IO HValueRef -> IO (Maybe HValueRef)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HValue -> IO HValueRef
forall a. a -> IO (RemoteRef a)
mkRemoteRef (Any -> HValue
HValue Any
a)