Commit Graph

28 Commits

Author SHA1 Message Date
Chris Dickinson
212e28bec3 fix: give extism_log_drain's param a named type (#638)
This fixes a test failure on the python-sdk [1]. (See also [2]). In
particular, while maturin creates bindings for `extism_log_drain` on
clang platforms, it seems that MSVC cannot generate the required binding
information for `ffi.py`. This results in an `extism_sys` wheel whose
shared object (in this case, a DLL) contains the `extism_log_drain`, but
whose Python FFI bindings don't contain a definition for that function.

Naming the function pointer type parameter resolves the issue. What a
strange issue!

[1]:
https://github.com/extism/python-sdk/actions/runs/7172934060/job/19669775001#step:9:35
[2]:
https://github.com/extism/python-sdk/pull/18#issuecomment-1850892835
2023-12-15 15:40:54 -08:00
zach
a5edf58747 fix(kernel): improve performance after large allocations, add extism_plugin_reset to give users more control when dealing with large allocations (#627)
See https://github.com/extism/cpp-sdk/issues/15

- Limits a call to memset in the kernel to the size of the current
memory offset instead of the total size of memory.
- Adds `extism_plugin_reset` to the C API and `extism::Plugin::reset` to
Rust

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: zshipko <zshipko@users.noreply.github.com>
2023-12-12 13:56:34 -08:00
Gavin Hayes
0285f64ecf build: add c++ compat to extism.h (#625)
Before `extern "C"` was needed to include the header in C++ code. Now
that's included inside, so it's easier and cleaner to include in c++
projects like the `cpp-sdk`.
2023-12-06 15:34:09 -05:00
zach
2a24d13c9b refactor!: use tracing crate for logging, add extism_log_callback function (#578)
- Uses `tracing` instead of `log` crate
- Uses `tracing-subscriber` instead of `fern`
- This allows us to automatically capture `log` events using
`tracing-subscriber`
- Breaking: Makes `extism::set_log_file` private and only used through
the C API, Rust users should use `tracing-subscriber` to determine which
filters/levels to log.
- Adds `extism::set_log_callback` function to set a callback that can be
used for custom logging from Rust.
- Adds `bool extism_log_custom(const char *level)` and
`extism_log_drain(void (*fn)(const char *s, size_t length)` to the C API
to enable custom sinks in other SDKs
2023-11-16 10:35:22 -08:00
zach
215d6838e9 cleanup: add PTR as an alias for I64 in C API (#560)
Similar to #558 but for libextism
2023-11-02 16:52:46 -07:00
zach
0e65c3afae refactor!: Cleanup UserData implementation, update example in README (#550)
Fixes #545 

- Adds a type parameter to `UserData` type to avoid dynamic typing
issues
- Adds KV example from README to `examples/readme.rs` to make sure it
stays in-sync
- Implement `Default` for `UserData<T>` when `T` implements `Default`
- Make `UserData` argument non-optional in `Function::new`,
`UserData::default()` can be used instead.
2023-10-26 11:19:48 -07:00
zach
728ee84b73 doc: add runtime/README.md and libextism/README.md (#526)
C: https://github.com/extism/extism/blob/new-readmes/libextism/README.md
Rust:
https://github.com/extism/extism/blob/new-readmes/runtime/README.md

Also includes some small usability improvements I stumbled across when
working on the docs
2023-10-20 10:30:10 -07:00
zach
cb55e52506 feat: Add extism-convert crate and use it for input/output to plugin calls (#443)
- Adds `extism-convert` crate with `ToBytes`, `FromBytes` and
`FromBytesOwned` traits
- This serves as a single interface for reading/writing rich types from
WebAssembly linear memory.
- Supports `Json` and `Msgpack` and `Protobuf` encodings out-of-the-box
- Updates `Plugin::call` to take `ToBytes` as the input argument and
return a `FromBytes` value
- Adds `host_fn!` macro to simplify host function creation
- Cleans up generated documentation a little
- PR for the Rust PDK: https://github.com/extism/rust-pdk/pull/31
- Adds a `typed_plugin!` macro to implement type-safe wrappers around
`Plugin`
- After this we should focus on adding similar type-conversion helpers
to the SDKs and other PDKs to make it easier to use across languages.
For example, a Python host running a Rust plugin using Msgpack encoded
types.

## Examples

### Calling a function

Instead of the untyped, bytes-only `call` function:

```rust
let output = plugin.call("func_name", "my data").unwrap();
let output: MyType = serde_json::from_slice(&output).unwrap();
```
We can now use richer types to encode/decode our values directly when
using `call`:

```rust
let Json(output) = plugin.call::<_, Json<MyType>>("func_name", "my data").unwrap();
```

### Allocating inside of a host function

The same interface works for host functions, so instead of:

```rust
fn hello_world(
    plugin: &mut CurrentPlugin,
    inputs: &[Val],
    outputs: &mut [Val],
    _user_data: UserData,
) -> Result<(), Error> {
    let handle = plugin.memory_handle_val(&inputs[0])?;
    let input = plugin.memory_read_str(handle)?;
    let output = plugin.memory_alloc_bytes(&input).unwrap();
    outputs[0] = output.into();
    Ok(())
}
```

Becomes:

```rust
fn hello_world(
    plugin: &mut CurrentPlugin,
    inputs: &[Val],
    outputs: &mut [Val],
    _user_data: UserData,
) -> Result<(), Error> {
    let my_value: String = plugin.memory_get_val(&inputs[0])?;
    let output = plugin.memory_new(&my_value)?;
    outputs[0] = plugin.memory_to_val(output);
    Ok(())
}
```

Although this isn't much of an improvement, using the `host_fn` macro,
we can really begin to see how the above function is really just an
identity function:
```rust
host_fn!(hello_world(a: String) -> String {
    a
});
```

### typed_plugin!

`typed_plugin!` is used to make a typed wrapper around a Plugin:

```rust
/// Create the typed plugin
typed_plugin!(Testing {
    count_vowels(&str) -> Json<Count>
});

/// Create the `Plugin` and convert it to `Testing` wrapper
let mut plugin: Testing = Plugin::new(WASM, [f], true).unwrap().into();

/// Call the `count_vowels` function:
let Json(output0): Json<Count> = plugin.count_vowels("abc123")?;
```

It could make sense to convert `host_fn` and/or `typed_plugin` to
proc-macros at some point, but for now they work and provide some
flexibility in experimenting with the interfaces. Another future update
could be to figure out a nice way to make it so input can be written in
multiple chunks, so the entire input doesn't have to get copied into
memory at once.
2023-09-14 12:32:38 -07:00
zach
ddcbeec3de refactor!: Remove context, unify extism-runtime and extism crates (#421)
- Removes the `ExtismContext` type from runtime and all SDKs
- Removed SDK functions: `extism_context_new`, `extism_context_reset`,
`extism_context_free`
  - All SDKs have been updated, but there are still some TODOs below 
- Removes `extism_plugin_update`
- Plugins can no longer be updated - a new plugin should be created
instead
- Adds `extism_plugin_id` to uniquely identify plugins
- Merges the `extism-runtime` and `extism` crates (there is no longer an
`extism-runtime` crate)
- Makes `extism::Manifest` an alias for `extism_manifest::Manifest`
instead of a distinct type
- Adds `MemoryHandle` type to SDKs to refer to blocks of Extism memory
that can be accessed in host functions
- Improves thread-safety of Plugins, adds C++ test to call a single
plugin from multiple threads.
- Expands wasmtime bounds to include 12.0
2023-08-29 13:57:17 -07:00
Benjamin Eckel
67eb8c1571 release: Bump the rest of the SDKs (#430) 2023-08-24 12:41:31 -05:00
zach
baa060d062 feat: Haskell host functions (#400) 2023-07-31 16:52:01 -07:00
zach
2e5f5ef716 fix(rust): fix lifetime of data returned from Plugin::call (#369) 2023-06-09 12:07:14 -07:00
zach
0f8954c203 feat!: add ability to create plugins without an existing Context (#335)
EIP: https://github.com/extism/proposals/pull/8

This PR makes minor breaking changes to several SDKs, but not to runtime
C API. The threadsafety updates in the Rust SDK are kind of specific to
Rust, I'm not sure if it makes sense to add the locks to all the other
SDKs at this point. For the most part the `Context` and `Plugin` types
in the SDKs should be safe to use protected by a mutex but they aren't
inherently threadsafe. That kind of locking should probably be done by
the user.

- Runtime 
  - improve thread safety
  - reinstantiates less
- fixes a potential resource exhaustion bug from re-instantiating using
the same store too many times
- Rust SDK
  - adds `Send` and `Sync` implementations for `Context`
  - adds test sharing a context between threads
- adds `Plugin::call_map` to call a plugin and handle the output with
the lock held
  - adds testing sharing an `Arc<Mutex<Plugin>>` between threads
- adds `Plugin::create` and `Plugin::create_from_manifest` to create a
plugin without a `Context`
- Python
  - BREAKING
- changes `Plugin` constructor to take `context` as an optional named
argument, to update use `Plugin(data, context=context)` instead
 - Ruby
   - BREAKING
- changes `Plugin` constructor to take `context` as an optional named
argument, to update use `Plugin.new(data, context=context)` instead
 - Go
   - adds `NewPlugin` and `NewPluginFromManifest` functions
 - Node
   - BREAKING
- changes `Plugin` constructor to take `context` as an optional named
argument, to update use `new Plugin(data, wasi, config, host, context)`
instead of `new Plugin(context, data, wasi, functions, config)` (most
people are probably using `context.plugin` instead of the Plugin
constructor anyway)
 - OCaml
   - BREAKING
- changes `Plugin.create` and `Plugin.of_manifest` to take `context` as
an optional named argument, to update `Plugin.create ~context data` and
`Plugin.of_manifest ~context data` instead
- Haskell
  - adds `createPlugin` and `createPluginFromManifest` functions
 - Elixir
- adds `Plugin.new` to make a plugin without going through
`Context.new_plugin`
 - Java
   - adds new `Plugin` constructors without a `Context` argument
- C++
  - BREAKING
- Updates `Plugin` constructor to take an optional context as the last
argument, instead of requiring it to be the first argument
- Use `Plugin(wasm, wasi, functions, ctx)` instead of `Plugin(ctx, wasm,
wasi, functions)`
 - Zig
- Adds `Plugin.create` and `Plugin.createWithManifest` to create plugins
in their own context.

---------

Co-authored-by: zach <zach@dylib.so>
Co-authored-by: Benjamin Eckel <bhelx@simst.im>
2023-05-17 11:35:16 -07:00
zach
94d5bd98c8 feat: Add plugin cancellation (#270)
This PR adds the ability to cancel running plugins from another thread
in the host.

- All SDKs have been updated except  .NET
- Adds a new SDK type: `ExtismCancelHandle`
- Adds 2 new SDK functions: `extism_plugin_cancel_handle` and
`extism_plugin_cancel`
- `extism_plugin_cancel_handle` returns a pointer to
`ExtismCancelHandle`, which can be passed to `extism_plugin_cancel` to
stop plugin execution.
- One thing that's worth noting is when plugin is executing a host
function it cannot be cancelled until after the host function returns.

---------

Co-authored-by: Etienne ANNE <etienne.anne@icloud.com>
2023-03-08 17:44:40 -08:00
zach
a44124bdb0 feat: add ability to set host function namespace (#246)
- Adds `extism_function_set_namespace` to SDK
- Updates SDKs with host function support to allow setting the namespace
for a function
2023-02-01 11:04:15 -08:00
zach
aa04fd3e5c test: add more host function tests, cleanup tests to use wasm/code.wasm when possible (#219) 2023-01-19 10:09:16 -06:00
zach
668ef5c3c0 fix: use cgo.Handle to protect Go pointers being passed to C callbacks (#220)
See
https://discord.com/channels/1011124058408112148/1050087851443888138/1064977577837592739
2023-01-17 15:38:16 -08:00
zach
dc3d54e260 feat: Add C API for host functions + support for C++, Python, Go, Node, OCaml (#195)
- New types:
  - `ExtismValType` - Enum of WebAssembly types
  - `ExtismValUnion` - A union of the possible WebAssembly types
  - `ExtismVal` - A struct with `ExtismValType` and `ExtismValUnion`
  - `ExtismFunction` - The host function wrapper type
  - `ExtismFunctionType` - The type of the host function callback
- `ExtismCurrentPlugin` - Provides access to the currently running
plugin from inside a host function

- New functions:
  - `extism_function_new` - Create a new `ExtismFunction`
  - `extism_function_free` - Free an `ExtismFunction`
- `extism_current_plugin_memory`, `extism_current_plugin_memory_alloc`,
`extism_current_plugin_memory_free`,
`extism_current_plugin_memory_length` - Manage plugin memory from inside
a host functions

- Updated functions
- `extism_plugin_new` and `extsim_plugin_update` - now accept two extra
parameters for `ExtismFunction*` array and length of that array

## Notes

- Host functions take a user-data argument, which is owned by the
resulting `ExtismFunction` and will be cleaned up when
`extism_function_free` is called (if a cleanup function was passed in
with the user data)
- Host functions in every SDK require working with `ExtismVal` arguments
directly, this is pretty low-level for what is kind of a high-level
feature. We could work on adding some types to the SDKs that make
working with pointers to plugin data more accessible, maybe something
similar to how the Rust PDK handes input/output data.
- In each language the host functions more-or-less share a signature:
`(CurrentPlugin plugin, Val inputs[], Val outputs[], userData)`
- C, C++, OCaml and Go take a single userData argument but Python and
Node take a "rest" argument which allows passing any number of user-data
values
- Go requires the host function to be exported:
f9eb5ed839/go/main.go (L13-L26)
- Zig and Ruby should be relatively simple to add host functions to next
but I haven't really looked into Elixir, .NET or Java yet.
- Also closes #20
2023-01-10 12:04:40 -08:00
Benjamin Eckel
0816e15a42 fix docstring comment 2022-10-17 13:46:59 -05:00
Benjamin Eckel
d27f430184 Change pointer type for go 2022-10-17 12:37:35 -05:00
Benjamin Eckel
b89745c519 Change header definition 2022-10-17 12:22:04 -05:00
Benjamin Eckel
85b3aece6f Add version to Host SDK and all clients 2022-10-17 09:25:22 -05:00
zach
87be73570d Add ExtismContext to SDK + better errors for failed register/update (#19)
- Adds `ExtismContext` instead of global `PLUGINS` registry
- Adds `extism_context_new`, `extism_context_free` and
`extism_context_reset`
- Requires updating nearly every SDK function to add context parameter
- Renames some SDK functions to follow better naming conventions
   - `extism_plugin_register` -> `extism_plugin_new`
   - `extism_output_get` -> `extism_plugin_output_data`
   - `extism_output_length` -> `extism_plugin_output_length`
   - `extism_call` -> `extism_plugin_call`
- Updates `extism_error` to return the context error when -1 issued for
the plug-in ID
- Adds `extism_plugin_free` to remove an existing plugin
- Updates SDKs to include these functions
- Updates SDK examples and comments

Co-authored-by: Steve Manuel <steve@dylib.so>
2022-09-20 14:53:15 -06:00
Steve Manuel
f9e9ff28d9 chore: bump versions for release, minimize c sdk imports (#15)
Co-authored-by: zach <zachshipko@gmail.com>
2022-09-13 13:27:58 -06:00
zach
8bc608d1e9 feat: access input/output buffers directly (#11)
This PR updates `extism_output_get` to return an actual pointer to the
output value (`const uint8_t* extism_output_get(PluginIndex plugin)`
instead of `void extism_output_get(PluginIndex plugin, uint8_t *buffer,
uint64_t length)`), this pointer will only be valid until the next call,
but it makes it possible to access the output data without copying.

The input buffer is also not copied and the same issue applies: 
the input buffer must not change during `call`.

Co-authored-by: Steve Manuel <steve@dylib.so>
2022-09-12 09:34:50 -06:00
zach
7b27d4f883 feat: add extism_plugin_update (#6)
This gives host the ability to re-use plugin descriptors instead of
loading a new plugin each time. The plugin memory and everything is
reset, so no state is shared with the newly loaded plugin.

Co-authored-by: Steve Manuel <steve@dylib.so>
2022-09-09 00:39:57 -06:00
zach
64856207d0 refactor: port over missing changes 2022-08-25 20:26:36 -07:00
Steve Manuel
e27fae9193 v0.0.1 alpha
Co-authored-by: Zach Shipko <zach@dylib.so>
2022-08-25 14:36:47 -06:00