fix merge conflict

This commit is contained in:
Steve Manuel
2022-09-01 16:43:06 -06:00
19 changed files with 536 additions and 64 deletions

View File

@@ -57,11 +57,11 @@ jobs:
python-version: '3.9'
check-latest: true
- name: Load cached Poetry installation
uses: actions/cache@v2
with:
path: ~/.local
key: poetry-0
# - name: Load cached Poetry installation
# uses: actions/cache@v2
# with:
# path: ~/.local
# key: poetry-0
- name: Install Poetry
uses: snok/install-poetry@v1
@@ -107,5 +107,11 @@ jobs:
# opam install -y .
# cd ocaml
# opam exec -- dune exec extism
- name: Setup Haskell env
uses: haskell/actions/setup@v2
- name: Test Haskell SDK
run: |
cd haskell
LD_LIBRARY_PATH=/usr/local/lib cabal test

2
.gitignore vendored
View File

@@ -28,3 +28,5 @@ ocaml/duniverse
ocaml/_build
wasm/rust-pdk/target
php/Extism.php
dist-newstyle
.stack-work

View File

@@ -4,7 +4,7 @@ The universal plug-in system. Run WebAssembly extensions inside your app. Use id
[Ruby](https://extism.org/docs/integrate-into-your-codebase/ruby-host-sdk), [Python](https://extism.org/docs/integrate-into-your-codebase/python-host-sdk),
[Node](https://extism.org/docs/integrate-into-your-codebase/node-host-sdk), [Rust](https://extism.org/docs/integrate-into-your-codebase/rust-host-sdk),
[C](https://extism.org/docs/integrate-into-your-codebase/c-host-sdk), [C++](https://extism.org/docs/integrate-into-your-codebase/cpp-host-sdk),
[OCaml](https://extism.org/docs/integrate-into-your-codebase/ocaml-host-sdk) & more (others coming soon).
[OCaml](https://extism.org/docs/integrate-into-your-codebase/ocaml-host-sdk), [Haskell](https://extism.org/docs/integrate-into-your-codebase/haskell-host-sdk) & more (others coming soon).
Plug-in development kits (PDK) for plug-in authors supported in Rust, AssemblyScript, Go, C/C++.
@@ -48,4 +48,4 @@ Extism is an open-source product from the team at:
<a href="https://dylib.so" _target="blanks"><img width="200px" src="https://dylib.so/assets/dylibso-logo.svg"/></a>
</p>
_Reach out and tell us what you're building! We'd love to help._
_Reach out and tell us what you're building! We'd love to help._

View File

@@ -7,20 +7,18 @@
(source
(github extism/extism))
(authors "Author Name")
(authors "Extism Authors <oss@extism.org>")
(maintainers "Maintainer Name")
(maintainers "Extism Authors <oss@extism.org>")
(license LICENSE)
(license BSD-3)
(documentation https://url/to/documentation)
(documentation https://github.com/extism/extism)
(package
(name extism)
(synopsis "A short synopsis")
(description "A longer description")
(synopsis "Extism bindings")
(description "Bindings to Extism, the universal plugin system")
(depends ocaml dune ctypes-foreign bigstringaf ppx_yojson_conv base64)
(tags
(topics "to describe" your project)))
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/dune-files.html#dune-project
(topics wasm plugin)))

View File

@@ -1,13 +1,13 @@
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
synopsis: "A short synopsis"
description: "A longer description"
maintainer: ["Maintainer Name"]
authors: ["Author Name"]
license: "LICENSE"
tags: ["topics" "to describe" "your" "project"]
synopsis: "Extism bindings"
description: "Bindings to Extism, the universal plugin system"
maintainer: ["Extism Authors <oss@extism.org>"]
authors: ["Extism Authors <oss@extism.org>"]
license: "BSD-3"
tags: ["topics" "wasm" "plugin"]
homepage: "https://github.com/extism/extism"
doc: "https://url/to/documentation"
doc: "https://github.com/extism/extism"
bug-reports: "https://github.com/extism/extism/issues"
depends: [
"ocaml"
@@ -15,6 +15,7 @@ depends: [
"ctypes-foreign"
"bigstringaf"
"ppx_yojson_conv"
"base64"
"odoc" {with-doc}
]
build: [

5
haskell/CHANGELOG.md Normal file
View File

@@ -0,0 +1,5 @@
# Revision history for extism
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

17
haskell/Example.hs Normal file
View File

@@ -0,0 +1,17 @@
module Main where
import System.Exit (exitFailure, exitSuccess)
import qualified Data.ByteString as B
import Extism
import Extism.Manifest
main = do
plugin <- Extism.registerManifest (manifest [wasmFile "../wasm/code.wasm"]) False
res <- Extism.call plugin "count_vowels" (Extism.toByteString "this is a test")
case res of
Right (Error msg) -> do
_ <- putStrLn msg
exitFailure
Left bs -> do
_ <- putStrLn (Extism.fromByteString bs)
exitSuccess

47
haskell/extism.cabal Normal file
View File

@@ -0,0 +1,47 @@
cabal-version: 2.4
name: extism
version: 0.0.1.0
-- A short (one-line) description of the package.
synopsis: Extism bindings
-- A longer description of the package.
description: Bindings to Extism, the universal plugin system
-- A URL where users can report bugs.
bug-reports: https://github.com/extism/extism
-- The license under which the package is released.
license: BSD-3-Clause
author: Extism authors
maintainer: oss@extism.org
-- A copyright notice.
-- copyright:
category: Plugins
extra-source-files: CHANGELOG.md
library
exposed-modules: Extism Extism.Manifest
-- Modules included in this library but not exported.
other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
build-depends:
base ^>=4.16.1.0
, bytestring
, base64-bytestring
, json
hs-source-dirs: src
default-language: Haskell2010
extra-libraries: extism
extra-lib-dirs: /usr/local/lib
Test-Suite extism-example
type: exitcode-stdio-1.0
main-is: Example.hs
build-depends: base, extism, bytestring
default-language: Haskell2010

91
haskell/src/Extism.hs Normal file
View File

@@ -0,0 +1,91 @@
{-# LANGUAGE ForeignFunctionInterface #-}
module Extism (module Extism, module Extism.Manifest) where
import GHC.Int
import GHC.Word
import Foreign.C.Types
import Foreign.Ptr
import Foreign.C.String
import Control.Monad (void)
import Data.ByteString as B
import Data.ByteString.Internal (c2w, w2c)
import Data.ByteString.Unsafe (unsafeUseAsCString)
import Text.JSON (JSON, toJSObject, encode)
import Extism.Manifest (Manifest, toString)
foreign import ccall unsafe "extism.h extism_plugin_register" extism_plugin_register :: Ptr Word8 -> Word64 -> CBool -> IO Int32
foreign import ccall unsafe "extism.h extism_call" extism_call :: Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32
foreign import ccall unsafe "extism.h extism_function_exists" extism_function_exists :: Int32 -> CString -> IO CBool
foreign import ccall unsafe "extism.h extism_error" extism_error :: Int32 -> IO CString
foreign import ccall unsafe "extism.h extism_output_length" extism_output_length :: Int32 -> IO Word64
foreign import ccall unsafe "extism.h extism_output_get" extism_output_get :: Int32 -> Ptr Word8 -> Word64 -> IO ()
foreign import ccall unsafe "extism.h extism_log_file" extism_log_file :: CString -> CString -> IO CBool
foreign import ccall unsafe "extism.h extism_plugin_config" extism_plugin_config :: Int32 -> Ptr Word8 -> Int64 -> IO CBool
newtype Plugin = Plugin Int32 deriving Show
newtype Error = Error String deriving Show
toByteString :: String -> ByteString
toByteString x = B.pack (Prelude.map c2w x)
fromByteString :: ByteString -> String
fromByteString bs = Prelude.map w2c $ B.unpack bs
register :: B.ByteString -> Bool -> IO Plugin
register wasm useWasi =
let length = fromIntegral (B.length wasm) in
let wasi = fromInteger (if useWasi then 1 else 0) in
do
p <- unsafeUseAsCString wasm (\s ->
extism_plugin_register (castPtr s) length wasi)
return $ Plugin p
registerManifest :: Manifest -> Bool -> IO Plugin
registerManifest manifest useWasi =
let wasm = toByteString $ toString manifest in
register wasm useWasi
isValid :: Plugin -> Bool
isValid (Plugin p) = p >= 0
setConfig :: Plugin -> [(String, String)] -> IO ()
setConfig (Plugin plugin) x =
if plugin < 0
then return ()
else
let obj = toJSObject x in
let bs = toByteString (encode obj) in
let length = fromIntegral (B.length bs) in
unsafeUseAsCString bs (\s -> do
void $ extism_plugin_config plugin (castPtr s) length)
setLogFile :: String -> String -> IO ()
setLogFile filename level =
withCString filename (\f ->
withCString level (\l -> do
void $ extism_log_file f l))
functionExists :: Plugin -> String -> IO Bool
functionExists (Plugin plugin) name = do
b <- withCString name (extism_function_exists plugin)
if b == 1 then return True else return False
call :: Plugin -> String -> B.ByteString -> IO (Either B.ByteString Error)
call (Plugin plugin) name input =
let length = fromIntegral (B.length input) in
do
rc <- withCString name (\name ->
unsafeUseAsCString input (\input ->
extism_call plugin name (castPtr input) length))
err <- extism_error plugin
if err /= nullPtr
then do e <- peekCString err
return $ Right (Error e)
else if rc == 0
then do
length <- extism_output_length plugin
let output = B.replicate (fromIntegral length) 0
() <- unsafeUseAsCString output (\a ->
extism_output_get plugin (castPtr a) length)
return $ Left output
else return $ Right (Error "Call failed")

View File

@@ -0,0 +1,168 @@
module Extism.Manifest where
import Text.JSON
(
JSValue(JSNull, JSString, JSArray),
toJSString, showJSON, makeObj, encode
)
import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as B64
import qualified Data.ByteString.Char8 as BS (unpack)
valueOrNull f Nothing = JSNull
valueOrNull f (Just x) = f x
makeString s = JSString (toJSString s)
stringOrNull = valueOrNull makeString
makeArray f x = JSArray [f a | a <- x]
filterNulls obj = [(a, b) | (a, b) <- obj, not (isNull b)]
mapObj f x = makeObj (filterNulls [(a, f b) | (a, b) <- x])
isNull JSNull = True
isNull _ = False
newtype Memory = Memory
{
memoryMax :: Maybe Int
}
class JSONValue a where
toJSONValue :: a -> JSValue
instance JSONValue Memory where
toJSONValue x =
case memoryMax x of
Nothing -> makeObj []
Just max -> makeObj [("max", showJSON max)]
data HttpRequest = HttpRequest
{
url :: String
, header :: [(String, String)]
, method :: Maybe String
}
requestObj x =
let meth = stringOrNull $ method x in
let h = mapObj makeString $ header x in
filterNulls [
("url", makeString $ url x),
("header", h),
("method", meth)
]
instance JSONValue HttpRequest where
toJSONValue x =
makeObj $ requestObj x
data WasmFile = WasmFile
{
filePath :: String
, fileName :: Maybe String
, fileHash :: Maybe String
}
instance JSONValue WasmFile where
toJSONValue x =
let path = makeString $ filePath x in
let name = stringOrNull $ fileName x in
let hash = stringOrNull $ fileHash x in
makeObj $ filterNulls [
("path", path),
("name", name),
("hash", hash)
]
data WasmCode = WasmCode
{
codeBytes :: B.ByteString
, codeName :: Maybe String
, codeHash :: Maybe String
}
instance JSONValue WasmCode where
toJSONValue x =
let bytes = makeString $ BS.unpack $ B64.encode $ codeBytes x in
let name = stringOrNull $ codeName x in
let hash = stringOrNull $ codeHash x in
makeObj $ filterNulls [
("data", bytes),
("name", name),
("hash", hash)
]
data WasmURL = WasmURL
{
req :: HttpRequest
, urlName :: Maybe String
, urlHash :: Maybe String
}
instance JSONValue WasmURL where
toJSONValue x =
let request = requestObj $ req x in
let name = stringOrNull $ urlName x in
let hash = stringOrNull $ urlHash x in
makeObj $ filterNulls $ ("name", name) : ("hash", hash) : request
data Wasm = File WasmFile | Code WasmCode | URL WasmURL
instance JSONValue Wasm where
toJSONValue x =
case x of
File f -> toJSONValue f
Code d -> toJSONValue d
URL u -> toJSONValue u
wasmFile :: String -> Wasm
wasmFile path =
File WasmFile { filePath = path, fileName = Nothing, fileHash = Nothing}
wasmURL :: String -> String -> Wasm
wasmURL method url =
let r = HttpRequest { url = url, header = [], method = Just method } in
URL WasmURL { req = r, urlName = Nothing, urlHash = Nothing }
wasmCode :: B.ByteString -> Wasm
wasmCode code =
Code WasmCode { codeBytes = code, codeName = Nothing, codeHash = Nothing }
withName :: Wasm -> String -> Wasm
withName (Code code) name = Code code { codeName = Just name }
withName (URL url) name = URL url { urlName = Just name }
withName (File f) name = File f { fileName = Just name }
withHash :: Wasm -> String -> Wasm
withHash (Code code) hash = Code code { codeHash = Just hash }
withHash (URL url) hash = URL url { urlHash = Just hash }
withHash (File f) hash = File f { fileHash = Just hash }
data Manifest = Manifest
{
wasm :: [Wasm]
, memory :: Maybe Memory
, config :: [(String, String)]
}
manifest :: [Wasm] -> Manifest
manifest wasm = Manifest {wasm = wasm, memory = Nothing, config = []}
withConfig :: Manifest -> [(String, String)] -> Manifest
withConfig m config =
m { config = config }
instance JSONValue Manifest where
toJSONValue x =
let w = makeArray toJSONValue $ wasm x in
let mem = valueOrNull toJSONValue $ memory x in
let c = mapObj makeString $ config x in
makeObj $ filterNulls [
("wasm", w),
("memory", mem),
("config", c)
]
toString :: Manifest -> String
toString manifest =
encode (toJSONValue manifest)

69
haskell/stack.yaml Normal file
View File

@@ -0,0 +1,69 @@
# This file was automatically generated by 'stack init'
#
# Some commonly used options have been documented as comments in this file.
# For advanced use and comprehensive documentation of the format, please see:
# https://docs.haskellstack.org/en/stable/yaml_configuration/
# Resolver to choose a 'specific' stackage snapshot or a compiler version.
# A snapshot resolver dictates the compiler version and the set of packages
# to be used for project dependencies. For example:
#
# resolver: lts-3.5
# resolver: nightly-2015-09-21
# resolver: ghc-7.10.2
#
# The location of a snapshot can be provided as a file or url. Stack assumes
# a snapshot provided as a file might change, whereas a url resource does not.
#
# resolver: ./custom-snapshot.yaml
# resolver: https://example.com/snapshots/2018-01-01.yaml
resolver:
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/8/30.yaml
# User packages to be built.
# Various formats can be used as shown in the example below.
#
# packages:
# - some-directory
# - https://example.com/foo/bar/baz-0.0.2.tar.gz
# subdirs:
# - auto-update
# - wai
packages:
- .
- dist-newstyle/tmp/src-3036517/semialign-1.2.0.1
- dist-newstyle/tmp/src-3036516/witherable-0.4.2
# Dependency packages to be pulled from upstream that are not in the resolver.
# These entries can reference officially published versions as well as
# forks / in-progress versions pinned to a git hash. For example:
#
# extra-deps:
# - acme-missiles-0.3
# - git: https://github.com/commercialhaskell/stack.git
# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
#
# extra-deps: []
# Override default flag values for local packages and extra-deps
# flags: {}
# Extra package databases containing global packages
# extra-package-dbs: []
# Control whether we use the GHC we find on the path
# system-ghc: true
#
# Require a specific version of stack, using version ranges
# require-stack-version: -any # Default
# require-stack-version: ">=2.7"
#
# Override the architecture used by stack, especially useful on Windows
# arch: i386
# arch: x86_64
#
# Extra directories used by stack for building
# extra-include-dirs: [/path/to/dir]
# extra-lib-dirs: [/path/to/dir]
#
# Allow a newer minor version of GHC than the snapshot specifies
# compiler-check: newer-minor

13
haskell/stack.yaml.lock Normal file
View File

@@ -0,0 +1,13 @@
# This file was autogenerated by Stack.
# You should not edit this file by hand.
# For more information, please see the documentation at:
# https://docs.haskellstack.org/en/stable/lock_files
packages: []
snapshots:
- completed:
size: 632828
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/8/30.yaml
sha256: 5b02c2ce430ac62843fb884126765628da2ca2280bb9de0c6635c723e32a9f6b
original:
url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/8/30.yaml

View File

@@ -10,4 +10,16 @@ description = "Extism plug-in manifest crate"
[dependencies]
serde = {version = "1", features=["derive"]}
base64 = "0.20.0-alpha"
base64 = "0.20.0-alpha"
schemars = {version = "0.8", optional=true}
[features]
json_schema = ["schemars"]
[dev-dependencies]
serde_json = "1"
[[example]]
name = "json_schema"
required-features = ["json_schema"]

View File

@@ -15,8 +15,8 @@ crate-type = ["cdylib"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
wasmtime = "0.39.1"
wasmtime-wasi = "0.39.1"
wasmtime = "0.40.0"
wasmtime-wasi = "0.40.0"
anyhow = "1"
serde = { version = "1", features=["derive"] }
toml = "0.5"
@@ -26,14 +26,13 @@ log = "0.4"
log4rs = "1.1"
ureq = {version = "2.5", optional=true}
extism-manifest = { version = "0.0.1-alpha", path = "../manifest" }
pretty-hex = { version = "0.3", optional = true }
pretty-hex = { version = "0.3" }
[features]
default = ["http", "register-http", "register-filesystem"]
register-http = ["ureq"] # enables wasm to be downloaded using http
register-filesystem = [] # enables wasm to be loaded from disk
http = ["ureq"] # enables extism_http_request
debug = ["pretty-hex"]
[build-dependencies]
cbindgen = "0.24"

View File

@@ -16,4 +16,4 @@ pub use plugin_ref::PluginRef;
pub type Size = u64;
pub type PluginIndex = i32;
pub(crate) use log::{debug, error, info};
pub(crate) use log::{debug, error, info, trace};

View File

@@ -2,7 +2,6 @@ use std::collections::BTreeMap;
use crate::*;
#[cfg(feature = "debug")]
use pretty_hex::PrettyHex;
/// Handles memory for plugins
@@ -32,6 +31,7 @@ impl PluginMemory {
}
pub(crate) fn store_u8(&mut self, offs: usize, data: u8) -> Result<(), MemoryAccessError> {
trace!("store_u8: {data:x} at offset {offs}");
if offs >= self.size() {
// This should raise MemoryAccessError
let buf = &mut [0];
@@ -44,6 +44,7 @@ impl PluginMemory {
/// Read from memory
pub(crate) fn load_u8(&self, offs: usize) -> Result<u8, MemoryAccessError> {
trace!("load_u8: offset {offs}");
if offs >= self.size() {
// This should raise MemoryAccessError
let buf = &mut [0];
@@ -54,6 +55,7 @@ impl PluginMemory {
}
pub(crate) fn store_u32(&mut self, offs: usize, data: u32) -> Result<(), MemoryAccessError> {
trace!("store_u32: {data:x} at offset {offs}");
let handle = MemoryBlock {
offset: offs,
length: 4,
@@ -64,6 +66,7 @@ impl PluginMemory {
/// Read from memory
pub(crate) fn load_u32(&self, offs: usize) -> Result<u32, MemoryAccessError> {
trace!("load_u32: offset {offs}");
let mut buf = [0; 4];
let handle = MemoryBlock {
@@ -75,6 +78,7 @@ impl PluginMemory {
}
pub(crate) fn store_u64(&mut self, offs: usize, data: u64) -> Result<(), MemoryAccessError> {
trace!("store_u64: {data:x} at offset {offs}");
let handle = MemoryBlock {
offset: offs,
length: 8,
@@ -84,6 +88,7 @@ impl PluginMemory {
}
pub(crate) fn load_u64(&self, offs: usize) -> Result<u64, MemoryAccessError> {
trace!("load_u64: offset {offs}");
let mut buf = [0; 8];
let handle = MemoryBlock {
offset: offs,
@@ -222,11 +227,10 @@ impl PluginMemory {
}
}
#[cfg(feature = "debug")]
pub fn dump(&self) {
let data = self.memory.data(&self.store);
println!("{:?}", data[..self.position].hex_dump());
trace!("{:?}", data[..self.position].hex_dump());
}
/// Reset memory

View File

@@ -152,6 +152,7 @@ impl Plugin {
/// Set `last_error` field
pub fn set_error(&mut self, e: impl std::fmt::Debug) {
debug!("Set error: {:?}", e);
let x = format!("{:?}", e).into_bytes();
let x = if x[0] == b'"' && x[x.len() - 1] == b'"' {
x[1..x.len() - 1].to_vec()
@@ -181,7 +182,6 @@ impl Plugin {
internal.plugin = ptr;
}
#[cfg(feature = "debug")]
pub fn dump_memory(&self) {
self.memory.dump();
}

View File

@@ -2,12 +2,17 @@ use crate::*;
// PluginRef is used to access a plugin from the global plugin registry
pub struct PluginRef<'a> {
pub id: PluginIndex,
pub plugins: std::sync::MutexGuard<'a, Vec<Plugin>>,
plugin: *mut Plugin,
}
impl<'a> PluginRef<'a> {
pub fn init(mut self) -> Self {
trace!(
"Resetting memory and clearing error message for plugin {}",
self.id,
);
// Initialize
self.as_mut().clear_error();
self.as_mut().memory.reset();
@@ -17,20 +22,26 @@ impl<'a> PluginRef<'a> {
/// # Safety
///
/// This function is used to access the static `PLUGINS` registry
pub unsafe fn new(plugin: PluginIndex) -> Self {
pub unsafe fn new(plugin_id: PluginIndex) -> Self {
let mut plugins = match PLUGINS.lock() {
Ok(p) => p,
Err(e) => e.into_inner(),
};
if plugin < 0 || plugin as usize >= plugins.len() {
trace!("Loading plugin {plugin_id}");
if plugin_id < 0 || plugin_id as usize >= plugins.len() {
drop(plugins);
panic!("Invalid PluginIndex {plugin}");
panic!("Invalid PluginIndex {plugin_id}");
}
let plugin = plugins.get_unchecked_mut(plugin as usize) as *mut _;
let plugin = plugins.get_unchecked_mut(plugin_id as usize) as *mut _;
PluginRef { plugins, plugin }
PluginRef {
id: plugin_id,
plugins,
plugin,
}
}
}
@@ -48,6 +59,7 @@ impl<'a> AsMut<Plugin> for PluginRef<'a> {
impl<'a> Drop for PluginRef<'a> {
fn drop(&mut self) {
trace!("Dropping plugin {}", self.id);
// Cleanup?
}
}

View File

@@ -11,6 +11,10 @@ pub unsafe extern "C" fn extism_plugin_register(
wasm_size: Size,
with_wasi: bool,
) -> PluginIndex {
trace!(
"Call to extism_plugin_register with wasm pointer {:?}",
wasm
);
let data = std::slice::from_raw_parts(wasm, wasm_size as usize);
let plugin = match Plugin::new(data, with_wasi) {
Ok(x) => x,
@@ -40,6 +44,12 @@ pub unsafe extern "C" fn extism_plugin_config(
) -> bool {
let mut plugin = PluginRef::new(plugin);
trace!(
"Call to extism_plugin_config for {} with json pointer {:?}",
plugin.id,
json
);
let data = std::slice::from_raw_parts(json, json_size as usize);
let json: std::collections::BTreeMap<String, String> = match serde_json::from_slice(data) {
Ok(x) => x,
@@ -53,6 +63,7 @@ pub unsafe extern "C" fn extism_plugin_config(
let wasi = &mut plugin.memory.store.data_mut().wasi;
let config = &mut plugin.manifest.as_mut().config;
for (k, v) in json.into_iter() {
trace!("Config, adding {k}");
let _ = wasi.push_env(&k, &v);
config.insert(k, v);
}
@@ -107,7 +118,6 @@ pub unsafe extern "C" fn extism_call(
Err(e) => return plugin.error(e.context("Unable to allocate bytes"), -1),
};
#[cfg(feature = "debug")]
plugin.dump_memory();
// Always needs to be called before `func.call()`
@@ -118,14 +128,12 @@ pub unsafe extern "C" fn extism_call(
match func.call(&mut plugin.memory.store, &[], results.as_mut_slice()) {
Ok(r) => r,
Err(e) => {
#[cfg(feature = "debug")]
plugin.dump_memory();
error!("Call: {e:?}");
return plugin.error(e.context("Call failed"), -1);
}
};
#[cfg(feature = "debug")]
plugin.dump_memory();
// Return result to caller
@@ -134,22 +142,31 @@ pub unsafe extern "C" fn extism_call(
#[no_mangle]
pub unsafe extern "C" fn extism_error(plugin: PluginIndex) -> *const c_char {
trace!("Call to extism_error for plugin {plugin}");
let plugin = PluginRef::new(plugin);
match &plugin.as_ref().last_error {
Some(e) => e.as_ptr() as *const _,
None => std::ptr::null(),
None => {
trace!("Error is NULL");
std::ptr::null()
}
}
}
#[no_mangle]
pub unsafe extern "C" fn extism_output_length(plugin: PluginIndex) -> Size {
trace!("Call to extism_output_length for plugin {plugin}");
let plugin = PluginRef::new(plugin);
plugin.as_ref().memory.store.data().output_length as Size
let len = plugin.as_ref().memory.store.data().output_length as Size;
trace!("Output length: {len}");
len
}
#[no_mangle]
pub unsafe extern "C" fn extism_output_get(plugin: PluginIndex, buf: *mut u8, len: Size) {
trace!("Call to extism_output_get for plugin {plugin}, length {len}");
let plugin = PluginRef::new(plugin);
let data = plugin.as_ref().memory.store.data();
@@ -170,16 +187,21 @@ pub unsafe extern "C" fn extism_log_file(
log_level: *const c_char,
) -> bool {
use log::LevelFilter;
use log4rs::append::console::ConsoleAppender;
use log4rs::append::file::FileAppender;
use log4rs::config::{Appender, Config, Root};
use log4rs::config::{Appender, Config, Logger, Root};
use log4rs::encode::pattern::PatternEncoder;
let file = std::ffi::CStr::from_ptr(filename);
let file = match file.to_str() {
Ok(x) => x,
Err(_) => {
return false;
let file = if !filename.is_null() {
let file = std::ffi::CStr::from_ptr(filename);
match file.to_str() {
Ok(x) => x,
Err(_) => {
return false;
}
}
} else {
"-"
};
let level = if log_level.is_null() {
@@ -201,28 +223,34 @@ pub unsafe extern "C" fn extism_log_file(
}
};
let logfile = match FileAppender::builder()
.encoder(Box::new(PatternEncoder::new("{d}: {l} - {m}\n")))
.build(file)
{
Ok(x) => x,
Err(e) => {
error!("Unable to set up log encoder: {e:?}");
return false;
let encoder = Box::new(PatternEncoder::new("{t} {l} {d} (({f}:{L})) - {m}\n"));
let logfile: Box<dyn log4rs::append::Append> = if file == "-" {
let console = ConsoleAppender::builder().encoder(encoder);
Box::new(console.build())
} else {
match FileAppender::builder().encoder(encoder).build(file) {
Ok(x) => Box::new(x),
Err(e) => {
error!("Unable to set up log encoder: {e:?}");
return false;
}
}
};
let config = match Config::builder()
.appender(Appender::builder().build("logfile", Box::new(logfile)))
.build(Root::builder().appender("logfile").build(level))
.appender(Appender::builder().build("logfile", logfile))
.logger(Logger::builder().appender("logfile").build("extism", level))
.build(Root::builder().build(LevelFilter::Off))
{
Ok(x) => x,
Err(e) => {
error!("Unable to configure log file: {e:?}");
Err(_) => {
return false;
}
};
log4rs::init_config(config).expect("Log initialization failed");
if log4rs::init_config(config).is_err() {
return false;
}
true
}