1{-# LANGUAGE MagicHash, UnboxedTuples #-}
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,
128 ideclName = noLoc mn,
129 ideclPkgQual = Nothing,
132 ideclImplicit = False,
133 ideclQualified = isJust mQual,
135 ideclHiding = Nothing
137 unqualifiedModules = map (\modn -> IIDecl $ safeImportDecl (mkModuleName modn) Nothing) [
141 "Data.Time.Calendar",
142 "Control.Monad.State",
144 "ProjectM36.Relation",
145 "ProjectM36.AtomFunctionError",
146 "ProjectM36.DatabaseContextFunctionError",
147 "ProjectM36.DatabaseContextFunctionUtils",
148 "ProjectM36.RelationalExpression"]
149#if __GLASGOW_HASKELL__ >= 802
150 mkModName = noLoc . mkModuleName
152 mkModName = mkModuleName
154 qualifiedModules = map (\(modn, qualNam) -> IIDecl $ safeImportDecl (mkModuleName modn) (Just (mkModName qualNam))) [
157 setContext (unqualifiedModules ++ qualifiedModules)
159 atomFuncType <- mkTypeForName "AtomFunctionBodyType"
160 dbcFuncType <- mkTypeForName "DatabaseContextFunctionBodyType"
161 pure (Right (ScriptSession env atomFuncType dbcFuncType))
163addImport :: String -> Ghc ()
164addImport moduleNam = do
166 setContext ( (IIDecl $ simpleImportDecl (mkModuleName moduleNam)) : ctx )
168showType :: DynFlags -> Type -> String
169showType dflags ty = showSDocForUser dflags alwaysQualify (pprTypeForUser ty)
171mkTypeForName :: String -> Ghc Type
172mkTypeForName name = do
173 lBodyName <- parseName name
175 [] -> error ("failed to parse " ++ name)
176 _:_:_ -> error "too many name matches"
178 mThing <- lookupName bodyName
180 Nothing -> error ("failed to find " ++ name)
181 Just (ATyCon tyCon) -> case synTyConRhs_maybe tyCon of
183 Nothing -> error (name ++ " is not a type synonym")
184 Just _ -> error ("failed to find type synonym " ++ name)
186compileScript :: Type -> Text -> Ghc (Either ScriptCompilationError a)
187compileScript funcType script = do
188 let sScript = unpack script
189 mErr <- typeCheckScript funcType script
191 Just err -> pure (Left err)
193 --catch exception here
194 --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.
195 -- we can't use dynCompileExpr here because
196 Right . unsafeCoerce <$> compileExpr sScript
198typeCheckScript :: Type -> Text -> Ghc (Maybe ScriptCompilationError)
199typeCheckScript expectedType inp = do
200 dflags <- getSessionDynFlags
201 --catch exception for SyntaxError
202#if __GLASGOW_HASKELL__ >= 802
203 funcType <- GHC.exprType TM_Inst (unpack inp)
205 funcType <- GHC.exprType (unpack inp)
207 --liftIO $ putStrLn $ showType dflags expectedType ++ ":::" ++ showType dflags funcType
208 if eqType funcType expectedType then
211 pure (Just (TypeCheckCompilationError (showType dflags expectedType) (showType dflags funcType)))
213mangleSymbol :: Maybe String -> String -> String -> String
214mangleSymbol pkg module' valsym =
216 maybe "" (\p -> zEncodeString p ++ "_") pkg ++
217 zEncodeString module' ++ "_" ++ zEncodeString valsym ++ "_closure"
220type FuncName = String
222data LoadSymbolError = LoadSymbolError
224loadFunction :: ModName -> FuncName -> FilePath -> IO (Either LoadSymbolError a)
225loadFunction modName funcName objPath = do
226#if __GLASGOW_HASKELL__ >= 802
227 initObjLinker RetainCAFs
233 ptr <- lookupSymbol (mangleSymbol Nothing modName funcName)
235 Nothing -> pure (Left LoadSymbolError)
236 Just (Ptr addr) -> case addrToAny# addr of
237 (# hval #) -> pure (Right hval)
239prefixUnderscore :: String
242 ("mingw32","x86_64") -> ""
243 ("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