1{-# LANGUAGE MagicHash, UnboxedTuples, KindSignatures, DataKinds #-}
2module ProjectM36.ScriptSession where
6import Control.Exception
10import Control.Monad.IO.Class
11import System.FilePath.Glob
13import System.Info (os, arch)
14import Data.Text (Text, unpack)
18import GHC.Paths (libdir)
19#if __GLASGOW_HASKELL__ >= 800
20import GHC.LanguageExtensions
25#if __GLASGOW_HASKELL__ >= 802
30import Outputable --hiding ((<>))
33#if __GLASGOW_HASKELL__ >= 802
35#elif __GLASGOW_HASKELL__ >= 710
36import Type hiding (pprTyThing)
40import GHC.Exts (addrToAny#)
41import GHC.Ptr (Ptr(..))
44data ScriptSession = ScriptSession {
46 atomFunctionBodyType :: Type,
47 dbcFunctionBodyType :: Type
50newtype ScriptSessionError = ScriptSessionLoadError GhcException
53-- | Configure a GHC environment/session which we will use for all script compilation.
54initScriptSession :: [String] -> IO (Either ScriptSessionError ScriptSession)
55initScriptSession ghcPkgPaths = do
56 --for the sake of convenience, for developers' builds, include the local cabal sandbox package database and the cabal new-build package database
57 eHomeDir <- tryJust (guard . isDoesNotExistError) getHomeDirectory
58 let homeDir = either (const "/") id eHomeDir
59 let excHandler exc = pure $ Left (ScriptSessionLoadError exc)
60 handleGhcException excHandler $ runGhc (Just libdir) $ do
61 dflags <- getSessionDynFlags
62 let ghcVersion = projectVersion dflags
64 sandboxPkgPaths <- liftIO $ concat <$> mapM glob [
65 "./dist-newstyle/packagedb/ghc-" ++ ghcVersion,
66 ".cabal-sandbox/*ghc-" ++ ghcVersion ++ "-packages.conf.d",
67 ".stack-work/install/*/*/" ++ ghcVersion ++ "/pkgdb",
68 ".stack-work/install/*/pkgdb/", --windows stack build
69 "C:/sr/snapshots/b201cfe6/pkgdb", --windows stack build- ideally, we could run `stack path --snapshot-pkg-db, but this is sufficient to pass CI
70 homeDir </> ".stack/snapshots/*/*/" ++ ghcVersion ++ "/pkgdb",
71 homeDir </> ".cabal/store/ghc-" ++ ghcVersion ++ "/package.db"
74 let localPkgPaths = map PkgConfFile (ghcPkgPaths ++ sandboxPkgPaths)
76 let dflags' = applyGopts . applyXopts $ dflags { hscTarget = HscInterpreted ,
77 ghcLink = LinkInMemory,
78 safeHaskell = Sf_Trustworthy,
82#if __GLASGOW_HASKELL__ >= 800
83 trustFlags = map TrustPackage required_packages,
85 packageFlags = packageFlags dflags ++ packages,
86#if __GLASGOW_HASKELL__ >= 802
87 packageDBFlags = map PackageDB localPkgPaths
89 extraPkgConfs = const (localPkgPaths ++ [UserPkgConf, GlobalPkgConf])
93 applyGopts flags = foldl gopt_set flags gopts
94 applyXopts flags = foldl xopt_set flags xopts
95#if __GLASGOW_HASKELL__ >= 800
96 xopts = [OverloadedStrings, ExtendedDefaultRules, ImplicitPrelude, ScopedTypeVariables]
98 xopts = [Opt_OverloadedStrings, Opt_ExtendedDefaultRules, Opt_ImplicitPrelude, Opt_ScopedTypeVariables]
100 gopts = [] --[Opt_DistrustAllPackages, Opt_PackageTrust]
101 required_packages = ["base",
105 "unordered-containers",
111 "vector-binary-instances",
115#if __GLASGOW_HASKELL__ >= 800
116 packages = map (\m -> ExposePackage ("-package " ++ m) (PackageArg m) (ModRenaming True [])) required_packages
118 packages = map TrustPackage required_packages
120 --liftIO $ traceShowM (showSDoc dflags' (ppr packages))
121 _ <- setSessionDynFlags dflags'
122 let safeImportDecl mn mQual = ImportDecl {
123#if __GLASGOW_HASKELL__ >= 802
124 ideclSourceSrc = NoSourceText,
126 ideclSourceSrc = Nothing,
129#if __GLASGOW_HASKELL__ >= 806
132 ideclName = noLoc mn,
133 ideclPkgQual = Nothing,
136 ideclImplicit = False,
137 ideclQualified = isJust mQual,
139 ideclHiding = Nothing
141#if __GLASGOW_HASKELL__ >= 806
142 :: ImportDecl (GhcPass (c :: Pass))
144 unqualifiedModules = map (\modn -> IIDecl $ safeImportDecl (mkModuleName modn) Nothing) [
148 "Data.Time.Calendar",
149 "Control.Monad.State",
151 "ProjectM36.Relation",
152 "ProjectM36.AtomFunctionError",
153 "ProjectM36.DatabaseContextFunctionError",
154 "ProjectM36.DatabaseContextFunctionUtils",
155 "ProjectM36.RelationalExpression"]
156#if __GLASGOW_HASKELL__ >= 802
157 mkModName = noLoc . mkModuleName
159 mkModName = mkModuleName
161 qualifiedModules = map (\(modn, qualNam) -> IIDecl $ safeImportDecl (mkModuleName modn) (Just (mkModName qualNam))) [
164 setContext (unqualifiedModules ++ qualifiedModules)
166 atomFuncType <- mkTypeForName "AtomFunctionBodyType"
167 dbcFuncType <- mkTypeForName "DatabaseContextFunctionBodyType"
168 pure (Right (ScriptSession env atomFuncType dbcFuncType))
170addImport :: String -> Ghc ()
171addImport moduleNam = do
173 setContext (IIDecl (simpleImportDecl (mkModuleName moduleNam)) : ctx)
175showType :: DynFlags -> Type -> String
176showType dflags ty = showSDocForUser dflags alwaysQualify (pprTypeForUser ty)
178mkTypeForName :: String -> Ghc Type
179mkTypeForName name = do
180 lBodyName <- parseName name
182 [] -> error ("failed to parse " ++ name)
183 _:_:_ -> error "too many name matches"
185 mThing <- lookupName bodyName
187 Nothing -> error ("failed to find " ++ name)
188 Just (ATyCon tyCon) -> case synTyConRhs_maybe tyCon of
190 Nothing -> error (name ++ " is not a type synonym")
191 Just _ -> error ("failed to find type synonym " ++ name)
193compileScript :: Type -> Text -> Ghc (Either ScriptCompilationError a)
194compileScript funcType script = do
195 let sScript = unpack script
196 mErr <- typeCheckScript funcType script
198 Just err -> pure (Left err)
200 --catch exception here
201 --we could potentially wrap the script with Atom pattern matching so that the script doesn't have to do it, but the change to an Atom ADT should make it easier. Still, it would be nice if the script didn't have to handle a list of arguments, for example.
202 -- we can't use dynCompileExpr here because
203 Right . unsafeCoerce <$> compileExpr sScript
205typeCheckScript :: Type -> Text -> Ghc (Maybe ScriptCompilationError)
206typeCheckScript expectedType inp = do
207 dflags <- getSessionDynFlags
208 --catch exception for SyntaxError
209#if __GLASGOW_HASKELL__ >= 802
210 funcType <- GHC.exprType TM_Inst (unpack inp)
212 funcType <- GHC.exprType (unpack inp)
214 --liftIO $ putStrLn $ showType dflags expectedType ++ ":::" ++ showType dflags funcType
215 if eqType funcType expectedType then
218 pure (Just (TypeCheckCompilationError (showType dflags expectedType) (showType dflags funcType)))
220mangleSymbol :: Maybe String -> String -> String -> String
221mangleSymbol pkg module' valsym =
223 maybe "" (\p -> zEncodeString p ++ "_") pkg ++
224 zEncodeString module' ++ "_" ++ zEncodeString valsym ++ "_closure"
227type FuncName = String
229data LoadSymbolError = LoadSymbolError
231loadFunction :: ModName -> FuncName -> FilePath -> IO (Either LoadSymbolError a)
232loadFunction modName funcName objPath = do
233#if __GLASGOW_HASKELL__ >= 802
234 initObjLinker RetainCAFs
240 ptr <- lookupSymbol (mangleSymbol Nothing modName funcName)
242 Nothing -> pure (Left LoadSymbolError)
243 Just (Ptr addr) -> case addrToAny# addr of
244 (# hval #) -> pure (Right hval)
246prefixUnderscore :: String
249 ("mingw32","x86_64") -> ""
250 ("cygwin","x86_64") -> ""
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list