extism 0.3.0 → 0.5.0
raw patch · 6 files changed
+304/−51 lines, 6 filesdep ~basedep ~bytestringdep ~extism-manifestPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, bytestring, extism-manifest, json
API changes (from Hackage documentation)
- Extism: data CancelHandle
+ Extism: ExternRef :: ValType
+ Extism: F32 :: ValType
+ Extism: F64 :: ValType
+ Extism: FuncRef :: ValType
+ Extism: Function :: ForeignPtr ExtismFunction -> StablePtr () -> Function
+ Extism: I32 :: ValType
+ Extism: I64 :: ValType
+ Extism: V128 :: ValType
+ Extism: ValF32 :: Float -> Val
+ Extism: ValF64 :: Double -> Val
+ Extism: ValI32 :: Int32 -> Val
+ Extism: ValI64 :: Int64 -> Val
+ Extism: data Function
+ Extism: data Val
+ Extism: data ValType
+ Extism: fromF32 :: Val -> Maybe Float
+ Extism: fromF64 :: Val -> Maybe Double
+ Extism: fromI32 :: Integral a => Val -> Maybe a
+ Extism: fromI64 :: Integral a => Val -> Maybe a
+ Extism: hostFunction :: String -> [ValType] -> [ValType] -> (CurrentPlugin -> [Val] -> a -> IO [Val]) -> a -> IO Function
+ Extism: newtype CancelHandle
+ Extism: toF32 :: Float -> Val
+ Extism: toF64 :: Double -> Val
+ Extism: toI32 :: Integral a => a -> Val
+ Extism: toI64 :: Integral a => a -> Val
+ Extism: type CurrentPlugin = Ptr ExtismCurrentPlugin
+ Extism: withPlugin :: (Plugin -> IO a) -> Plugin -> IO a
+ Extism.CurrentPlugin: allocBytes :: CurrentPlugin -> ByteString -> IO Word64
+ Extism.CurrentPlugin: memory :: CurrentPlugin -> IO (Ptr Word8)
+ Extism.CurrentPlugin: memoryAlloc :: CurrentPlugin -> Word64 -> IO Word64
+ Extism.CurrentPlugin: memoryBytes :: CurrentPlugin -> Word64 -> IO ByteString
+ Extism.CurrentPlugin: memoryFree :: CurrentPlugin -> Word64 -> IO ()
+ Extism.CurrentPlugin: memoryLength :: CurrentPlugin -> Word64 -> IO Word64
+ Extism.CurrentPlugin: memoryOffset :: CurrentPlugin -> Word64 -> IO (Ptr Word8)
- Extism: Plugin :: Context -> Int32 -> Plugin
+ Extism: Plugin :: Context -> Int32 -> [Function] -> Plugin
- Extism: createPlugin :: ByteString -> Bool -> IO (Result Plugin)
+ Extism: createPlugin :: ByteString -> [Function] -> Bool -> IO (Result Plugin)
- Extism: createPluginFromManifest :: Manifest -> Bool -> IO (Result Plugin)
+ Extism: createPluginFromManifest :: Manifest -> [Function] -> Bool -> IO (Result Plugin)
- Extism: plugin :: Context -> ByteString -> Bool -> IO (Result Plugin)
+ Extism: plugin :: Context -> ByteString -> [Function] -> Bool -> IO (Result Plugin)
- Extism: pluginFromManifest :: Context -> Manifest -> Bool -> IO (Result Plugin)
+ Extism: pluginFromManifest :: Context -> Manifest -> [Function] -> Bool -> IO (Result Plugin)
- Extism: update :: Plugin -> ByteString -> Bool -> IO (Result ())
+ Extism: update :: Plugin -> ByteString -> [Function] -> Bool -> IO (Result Plugin)
- Extism: updateManifest :: Plugin -> Manifest -> Bool -> IO (Result ())
+ Extism: updateManifest :: Plugin -> Manifest -> [Function] -> Bool -> IO (Result Plugin)
Files
- Example.hs +14/−5
- extism.cabal +7/−7
- src/Extism.hs +126/−36
- src/Extism/Bindings.hs +92/−0
- src/Extism/CurrentPlugin.hs +48/−0
- test/Test.hs +17/−3
Example.hs view
@@ -1,15 +1,24 @@ module Main where import Extism+import Extism.CurrentPlugin import Extism.Manifest(manifest, wasmFile) unwrap (Right x) = x unwrap (Left (ExtismError msg)) = do error msg +hello plugin params msg = do+ putStrLn "Hello from Haskell!"+ putStrLn msg+ offs <- allocBytes plugin (toByteString "{\"count\": 999}")+ return [toI64 offs]+ main = do- let m = manifest [wasmFile "../wasm/code.wasm"]- plugin <- unwrap <$> Extism.createPluginFromManifest m False- res <- unwrap <$> Extism.call plugin "count_vowels" (Extism.toByteString "this is a test")- putStrLn (Extism.fromByteString res)- Extism.free plugin+ setLogFile "stdout" Error+ let m = manifest [wasmFile "../wasm/code-functions.wasm"]+ f <- hostFunction "hello_world" [I64] [I64] hello "Hello, again"+ plugin <- unwrap <$> createPluginFromManifest m [f] True+ res <- unwrap <$> call plugin "count_vowels" (toByteString "this is a test")+ putStrLn (fromByteString res)+ free plugin
extism.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: extism-version: 0.3.0+version: 0.5.0 license: BSD-3-Clause maintainer: oss@extism.org author: Extism authors@@ -8,10 +8,10 @@ synopsis: Extism bindings description: Bindings to Extism, the universal plugin system category: Plugins, WebAssembly-extra-source-files: CHANGELOG.md+extra-doc-files: CHANGELOG.md library- exposed-modules: Extism+ exposed-modules: Extism Extism.CurrentPlugin reexported-modules: Extism.Manifest hs-source-dirs: src other-modules: Extism.Bindings@@ -19,10 +19,10 @@ extra-libraries: extism extra-lib-dirs: /usr/local/lib build-depends:- base >= 4.16.1 && < 4.19.0,- bytestring >= 0.11.3 && < 0.12,- json >= 0.10 && < 0.11,- extism-manifest >= 0.0.0 && < 0.3.0+ base >= 4.16.1 && < 5,+ bytestring >= 0.11.3 && <= 0.12,+ json >= 0.10 && <= 0.11,+ extism-manifest >= 0.0.0 && < 0.4.0 test-suite extism-example type: exitcode-stdio-1.0
src/Extism.hs view
@@ -1,10 +1,21 @@-module Extism (module Extism, module Extism.Manifest) where+module Extism (+ module Extism,+ module Extism.Manifest,+ ValType(..),+ Val(..)+) where+ import Data.Int import Data.Word import Control.Monad (void) import Foreign.ForeignPtr import Foreign.C.String import Foreign.Ptr+import Foreign.Marshal.Array+import Foreign.Storable+import Foreign.StablePtr+import Foreign.Concurrent+import Foreign.Marshal.Utils (copyBytes, moveBytes) import Data.ByteString as B import Data.ByteString.Internal (c2w, w2c) import Data.ByteString.Unsafe (unsafeUseAsCString)@@ -16,11 +27,18 @@ -- | Context for managing plugins newtype Context = Context (ForeignPtr ExtismContext) +-- | Host function+data Function = Function (ForeignPtr ExtismFunction) (StablePtr ())+ -- | Plugins can be used to call WASM function-data Plugin = Plugin Context Int32+data Plugin = Plugin Context Int32 [Function] -data CancelHandle = CancelHandle (Ptr ExtismCancelHandle)+-- | Cancellation handle for Plugins+newtype CancelHandle = CancelHandle (Ptr ExtismCancelHandle) +-- | Access the plugin that is currently executing from inside a host function+type CurrentPlugin = Ptr ExtismCurrentPlugin+ -- | Log level data LogLevel = Error | Warn | Info | Debug | Trace deriving (Show) @@ -53,80 +71,94 @@ newContext :: IO Context newContext = do ptr <- extism_context_new- fptr <- newForeignPtr extism_context_free ptr+ fptr <- Foreign.ForeignPtr.newForeignPtr extism_context_free ptr return (Context fptr)- + -- | Execute a function with a new 'Context' that is destroyed when it returns withContext :: (Context -> IO a) -> IO a withContext f = do ctx <- newContext f ctx +-- | Execute a function with the provided 'Plugin' as a parameter, then frees the 'Plugin'+-- | before returning the result.+withPlugin :: (Plugin -> IO a) -> Plugin -> IO a+withPlugin f plugin = do+ res <- f plugin+ free plugin+ return res+ -- | Create a 'Plugin' from a WASM module, `useWasi` determines if WASI should -- | be linked-plugin :: Context -> B.ByteString -> Bool -> IO (Result Plugin)-plugin c wasm useWasi =+plugin :: Context -> B.ByteString -> [Function] -> Bool -> IO (Result Plugin)+plugin c wasm functions useWasi =+ let nfunctions = fromIntegral (Prelude.length functions) in let length = fromIntegral (B.length wasm) in let wasi = fromInteger (if useWasi then 1 else 0) in let Context ctx = c in do+ funcs <- Prelude.mapM (\(Function ptr _) -> withForeignPtr ptr (\x -> do return x)) functions withForeignPtr ctx (\ctx -> do p <- unsafeUseAsCString wasm (\s ->- extism_plugin_new ctx (castPtr s) length nullPtr 0 wasi )+ withArray funcs (\funcs ->+ extism_plugin_new ctx (castPtr s) length funcs nfunctions wasi )) if p < 0 then do err <- extism_error ctx (-1) e <- peekCString err return $ Left (ExtismError e) else- return $ Right (Plugin c p))- + return $ Right (Plugin c p functions))+ -- | Create a 'Plugin' with its own 'Context'-createPlugin :: B.ByteString -> Bool -> IO (Result Plugin)-createPlugin c useWasi = do+createPlugin :: B.ByteString -> [Function] -> Bool -> IO (Result Plugin)+createPlugin c functions useWasi = do ctx <- newContext- plugin ctx c useWasi+ plugin ctx c functions useWasi -- | Create a 'Plugin' from a 'Manifest'-pluginFromManifest :: Context -> Manifest -> Bool -> IO (Result Plugin)-pluginFromManifest ctx manifest useWasi =+pluginFromManifest :: Context -> Manifest -> [Function] -> Bool -> IO (Result Plugin)+pluginFromManifest ctx manifest functions useWasi = let wasm = toByteString $ toString manifest in- plugin ctx wasm useWasi+ plugin ctx wasm functions useWasi -- | Create a 'Plugin' with its own 'Context' from a 'Manifest'-createPluginFromManifest :: Manifest -> Bool -> IO (Result Plugin)-createPluginFromManifest manifest useWasi = do+createPluginFromManifest :: Manifest -> [Function] -> Bool -> IO (Result Plugin)+createPluginFromManifest manifest functions useWasi = do ctx <- newContext- pluginFromManifest ctx manifest useWasi+ pluginFromManifest ctx manifest functions useWasi -- | Update a 'Plugin' with a new WASM module-update :: Plugin -> B.ByteString -> Bool -> IO (Result ())-update (Plugin (Context ctx) id) wasm useWasi =+update :: Plugin -> B.ByteString -> [Function] -> Bool -> IO (Result Plugin)+update (Plugin (Context ctx) id _) wasm functions useWasi =+ let nfunctions = fromIntegral (Prelude.length functions) in let length = fromIntegral (B.length wasm) in let wasi = fromInteger (if useWasi then 1 else 0) in do- withForeignPtr ctx (\ctx -> do+ funcs <- Prelude.mapM (\(Function ptr _ ) -> withForeignPtr ptr (\x -> do return x)) functions+ withForeignPtr ctx (\ctx' -> do b <- unsafeUseAsCString wasm (\s ->- extism_plugin_update ctx id (castPtr s) length nullPtr 0 wasi)+ withArray funcs (\funcs ->+ extism_plugin_update ctx' id (castPtr s) length funcs nfunctions wasi)) if b <= 0 then do- err <- extism_error ctx (-1)+ err <- extism_error ctx' (-1) e <- peekCString err return $ Left (ExtismError e) else- return (Right ()))+ return (Right (Plugin (Context ctx) id functions))) -- | Update a 'Plugin' with a new 'Manifest'-updateManifest :: Plugin -> Manifest -> Bool -> IO (Result ())-updateManifest plugin manifest useWasi =+updateManifest :: Plugin -> Manifest -> [Function] -> Bool -> IO (Result Plugin)+updateManifest plugin manifest functions useWasi = let wasm = toByteString $ toString manifest in- update plugin wasm useWasi+ update plugin wasm functions useWasi -- | Check if a 'Plugin' is valid isValid :: Plugin -> Bool-isValid (Plugin _ p) = p >= 0+isValid (Plugin _ p _) = p >= 0 -- | Set configuration values for a plugin setConfig :: Plugin -> [(String, Maybe String)] -> IO Bool-setConfig (Plugin (Context ctx) plugin) x =+setConfig (Plugin (Context ctx) plugin _) x = if plugin < 0 then return False else@@ -155,14 +187,14 @@ -- | Check if a function exists in the given plugin functionExists :: Plugin -> String -> IO Bool-functionExists (Plugin (Context ctx) plugin) name = do+functionExists (Plugin (Context ctx) plugin _) name = do withForeignPtr ctx (\ctx -> do b <- withCString name (extism_plugin_function_exists ctx plugin) if b == 1 then return True else return False) --- | Call a function provided by the given plugin call :: Plugin -> String -> B.ByteString -> IO (Result B.ByteString)-call (Plugin (Context ctx) plugin) name input =+call (Plugin (Context ctx) plugin _) name input = let length = fromIntegral (B.length input) in do withForeignPtr ctx (\ctx -> do@@ -184,15 +216,73 @@ -- | Free a 'Plugin', this will automatically be called for every plugin -- | associated with a 'Context' when that 'Context' is freed free :: Plugin -> IO ()-free (Plugin (Context ctx) plugin) =+free (Plugin (Context ctx) plugin _) = withForeignPtr ctx (`extism_plugin_free` plugin) +-- | Create a new 'CancelHandle' that can be used to cancel a running plugin+-- | from another thread. cancelHandle :: Plugin -> IO CancelHandle-cancelHandle (Plugin (Context ctx) plugin) = do- handle <- withForeignPtr ctx (\ctx -> extism_plugin_cancel_handle ctx plugin)+cancelHandle (Plugin (Context ctx) plugin _) = do+ handle <- withForeignPtr ctx (`extism_plugin_cancel_handle` plugin) return (CancelHandle handle) +-- | Cancel a running plugin using a 'CancelHandle' cancel :: CancelHandle -> IO Bool-cancel (CancelHandle handle) = +cancel (CancelHandle handle) = extism_plugin_cancel handle ++-- | Create a new 'Function' that can be called from a 'Plugin'+hostFunction :: String -> [ValType] -> [ValType] -> (CurrentPlugin -> [Val] -> a -> IO [Val]) -> a -> IO Function+hostFunction name params results f v =+ let nparams = fromIntegral $ Prelude.length params in+ let nresults = fromIntegral $ Prelude.length results in+ do+ cb <- callbackWrap (callback f :: CCallback)+ free <- freePtrWrap freePtr+ userData <- newStablePtr (v, free, cb)+ let userDataPtr = castStablePtrToPtr userData+ x <- withCString name (\name -> do+ withArray params (\params ->+ withArray results (\results -> do+ extism_function_new name params nparams results nresults cb userDataPtr free)))+ let freeFn = extism_function_free x+ fptr <- Foreign.Concurrent.newForeignPtr x freeFn+ return $ Function fptr (castPtrToStablePtr userDataPtr)+++-- | Create a new I32 'Val'+toI32 :: Integral a => a -> Val+toI32 x = ValI32 (fromIntegral x)++-- | Create a new I64 'Val'+toI64 :: Integral a => a -> Val+toI64 x = ValI64 (fromIntegral x)++-- | Create a new F32 'Val'+toF32 :: Float -> Val+toF32 = ValF32++-- | Create a new F64 'Val'+toF64 :: Double -> Val+toF64 = ValF64++-- | Get I32 'Val'+fromI32 :: Integral a => Val -> Maybe a+fromI32 (ValI32 x) = Just (fromIntegral x)+fromI32 _ = Nothing++-- | Get I64 'Val'+fromI64 :: Integral a => Val -> Maybe a+fromI64 (ValI64 x) = Just (fromIntegral x)+fromI64 _ = Nothing++-- | Get F32 'Val'+fromF32 :: Val -> Maybe Float+fromF32 (ValF32 x) = Just x+fromF32 _ = Nothing++-- | Get F64 'Val'+fromF64 :: Val -> Maybe Double+fromF64 (ValF64 x) = Just x+fromF64 _ = Nothing
src/Extism/Bindings.hs view
@@ -7,11 +7,78 @@ import Foreign.C.String import Data.Int import Data.Word+import Foreign.Storable+import Foreign.Marshal.Array+import Foreign.StablePtr +type FreeCallback = Ptr () -> IO ()+ newtype ExtismContext = ExtismContext () deriving Show newtype ExtismFunction = ExtismFunction () deriving Show newtype ExtismCancelHandle = ExtismCancelHandle () deriving Show+newtype ExtismCurrentPlugin = ExtismCurrentPlugin () deriving Show+data ValType = I32 | I64 | F32 | F64 | V128 | FuncRef | ExternRef deriving (Show, Eq)+data Val = ValI32 Int32 | ValI64 Int64 | ValF32 Float | ValF64 Double deriving (Show, Eq) +typeOfVal (ValI32 _) = I32+typeOfVal (ValI64 _) = I64+typeOfVal (ValF32 _) = F32+typeOfVal (ValF64 _) = F64++type CCallback = Ptr ExtismCurrentPlugin -> Ptr Val -> Word64 -> Ptr Val -> Word64 -> Ptr () -> IO ()++_32Bit = sizeOf (undefined :: Int) == 4++instance Storable Val where+ sizeOf _ =+ if _32Bit then 12 else 16+ alignment _ = 1+ peek ptr = do+ let offs = if _32Bit then 4 else 8+ t <- valTypeOfInt <$> peekByteOff ptr 0+ case t of+ I32 -> ValI32 <$> peekByteOff ptr offs+ I64 -> ValI64 <$> peekByteOff ptr offs+ F32 -> ValF32 <$> peekByteOff ptr offs+ F64 -> ValF64 <$> peekByteOff ptr offs+ poke ptr x = do+ let offs = if _32Bit then 4 else 8+ pokeByteOff ptr 0 (typeOfVal x)+ case x of+ ValI32 x -> pokeByteOff ptr offs x+ ValI64 x -> pokeByteOff ptr offs x+ ValF32 x -> pokeByteOff ptr offs x+ ValF64 x -> pokeByteOff ptr offs x+++intOfValType :: ValType -> CInt+intOfValType I32 = 0+intOfValType I64 = 1+intOfValType F32 = 2+intOfValType F64 = 3+intOfValType V128 = 4+intOfValType FuncRef = 5+intOfValType ExternRef = 6++valTypeOfInt :: CInt -> ValType+valTypeOfInt 0 = I32+valTypeOfInt 1 = I64+valTypeOfInt 2 = F32+valTypeOfInt 3 = F64+valTypeOfInt 4 = V128+valTypeOfInt 5 = FuncRef+valTypeOfInt 6 = ExternRef+valTypeOfInt _ = error "Invalid ValType"++instance Storable ValType where+ sizeOf _ = 4+ alignment _ = 1+ peek ptr = do+ x <- peekByteOff ptr 0+ return $ valTypeOfInt (x :: CInt)+ poke ptr x = do+ pokeByteOff ptr 0 (intOfValType x)+ foreign import ccall safe "extism.h extism_context_new" extism_context_new :: IO (Ptr ExtismContext) foreign import ccall safe "extism.h &extism_context_free" extism_context_free :: FunPtr (Ptr ExtismContext -> IO ()) foreign import ccall safe "extism.h extism_plugin_new" extism_plugin_new :: Ptr ExtismContext -> Ptr Word8 -> Word64 -> Ptr (Ptr ExtismFunction) -> Word64 -> CBool -> IO Int32@@ -28,3 +95,28 @@ foreign import ccall safe "extism.h extism_version" extism_version :: IO CString foreign import ccall safe "extism.h extism_plugin_cancel_handle" extism_plugin_cancel_handle :: Ptr ExtismContext -> Int32 -> IO (Ptr ExtismCancelHandle) foreign import ccall safe "extism.h extism_plugin_cancel" extism_plugin_cancel :: Ptr ExtismCancelHandle -> IO Bool++foreign import ccall safe "extism.h extism_function_new" extism_function_new :: CString -> Ptr ValType -> Word64 -> Ptr ValType -> Word64 -> FunPtr CCallback -> Ptr () -> FunPtr FreeCallback -> IO (Ptr ExtismFunction)+foreign import ccall safe "extism.h extism_function_free" extism_function_free :: Ptr ExtismFunction -> IO ()+foreign import ccall safe "extism.h extism_current_plugin_memory" extism_current_plugin_memory :: Ptr ExtismCurrentPlugin -> IO (Ptr Word8)+foreign import ccall safe "extism.h extism_current_plugin_memory_alloc" extism_current_plugin_memory_alloc :: Ptr ExtismCurrentPlugin -> Word64 -> IO Word64+foreign import ccall safe "extism.h extism_current_plugin_memory_length" extism_current_plugin_memory_length :: Ptr ExtismCurrentPlugin -> Word64 -> IO Word64+foreign import ccall safe "extism.h extism_current_plugin_memory_free" extism_current_plugin_memory_free :: Ptr ExtismCurrentPlugin -> Word64 -> IO ()++freePtr ptr = do+ let s = castPtrToStablePtr ptr+ (a, b, c) <- deRefStablePtr s+ freeHaskellFunPtr b+ freeHaskellFunPtr c+ freeStablePtr s++foreign import ccall "wrapper" freePtrWrap :: FreeCallback -> IO (FunPtr FreeCallback)++foreign import ccall "wrapper" callbackWrap :: CCallback -> IO (FunPtr CCallback)++callback :: (Ptr ExtismCurrentPlugin -> [Val] -> a -> IO [Val]) -> (Ptr ExtismCurrentPlugin -> Ptr Val -> Word64 -> Ptr Val -> Word64 -> Ptr () -> IO ())+callback f plugin params nparams results nresults ptr = do+ p <- peekArray (fromIntegral nparams) params+ (userData, _, _) <- deRefStablePtr (castPtrToStablePtr ptr)+ res <- f plugin p userData+ pokeArray results res
+ src/Extism/CurrentPlugin.hs view
@@ -0,0 +1,48 @@+module Extism.CurrentPlugin where++import Extism+import Extism.Bindings+import Data.Word+import Data.ByteString as B+import Foreign.Ptr+import Foreign.Marshal.Array++-- | Allocate a new handle of the given size+memoryAlloc :: CurrentPlugin -> Word64 -> IO Word64+memoryAlloc = extism_current_plugin_memory_alloc++-- | Get the length of a handle, returns 0 if the handle is invalid+memoryLength :: CurrentPlugin -> Word64 -> IO Word64+memoryLength = extism_current_plugin_memory_length++-- | Free allocated memory+memoryFree :: CurrentPlugin -> Word64 -> IO ()+memoryFree = extism_current_plugin_memory_free++-- | Access a pointer to the entire memory region+memory :: CurrentPlugin -> IO (Ptr Word8)+memory = extism_current_plugin_memory++-- | Access a pointer the a specific offset in memory+memoryOffset :: CurrentPlugin -> Word64 -> IO (Ptr Word8)+memoryOffset plugin offs = do+ x <- extism_current_plugin_memory plugin+ return $ plusPtr x (fromIntegral offs)++-- | Access the data associated with a handle as a 'ByteString'+memoryBytes :: CurrentPlugin -> Word64 -> IO B.ByteString+memoryBytes plugin offs = do+ ptr <- memoryOffset plugin offs+ len <- memoryLength plugin offs+ arr <- peekArray (fromIntegral len) ptr+ return $ B.pack arr++-- | Allocate memory and copy an existing 'ByteString' into it+allocBytes :: CurrentPlugin -> B.ByteString -> IO Word64+allocBytes plugin s = do+ let length = B.length s+ offs <- memoryAlloc plugin (fromIntegral length)+ ptr <- memoryOffset plugin offs+ pokeArray ptr (B.unpack s)+ return offs+
test/Test.hs view
@@ -1,6 +1,7 @@ import Test.HUnit import Extism import Extism.Manifest+import Extism.CurrentPlugin unwrap (Right x) = return x@@ -8,12 +9,13 @@ assertFailure msg defaultManifest = manifest [wasmFile "../../wasm/code.wasm"]+hostFunctionManifest = manifest [wasmFile "../../wasm/code-functions.wasm"] initPlugin :: Maybe Context -> IO Plugin initPlugin Nothing =- Extism.createPluginFromManifest defaultManifest False >>= unwrap+ Extism.createPluginFromManifest defaultManifest [] False >>= unwrap initPlugin (Just ctx) =- Extism.pluginFromManifest ctx defaultManifest False >>= unwrap+ Extism.pluginFromManifest ctx defaultManifest [] False >>= unwrap pluginFunctionExists = do p <- initPlugin Nothing@@ -30,6 +32,17 @@ p <- initPlugin Nothing checkCallResult p ++hello plugin params () = do+ putStrLn "Hello from Haskell!"+ offs <- allocBytes plugin (toByteString "{\"count\": 999}")+ return [toI64 offs]++pluginCallHostFunction = do+ p <- Extism.createPluginFromManifest hostFunctionManifest [] False >>= unwrap+ res <- call p "count_vowels" (toByteString "this is a test") >>= unwrap+ assertEqual "count vowels output" "{\"count\": 999}" (fromByteString res)+ pluginMultiple = do withContext(\ctx -> do p <- initPlugin (Just ctx)@@ -42,7 +55,7 @@ pluginUpdate = do withContext (\ctx -> do p <- initPlugin (Just ctx)- updateManifest p defaultManifest True >>= unwrap+ updateManifest p defaultManifest [] True >>= unwrap checkCallResult p) pluginConfig = do@@ -62,6 +75,7 @@ [ t "Plugin.FunctionExists" pluginFunctionExists , t "Plugin.Call" pluginCall+ , t "Plugin.CallHostFunction" pluginCallHostFunction , t "Plugin.Multiple" pluginMultiple , t "Plugin.Update" pluginUpdate , t "Plugin.Config" pluginConfig