mirror of
https://github.com/extism/extism.git
synced 2026-01-12 23:38:04 -05:00
Compare commits
16 Commits
owi
...
wasmtime-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7127759a6 | ||
|
|
ed5bad922a | ||
|
|
5af226219e | ||
|
|
b0e6387eef | ||
|
|
6a8e65eeb3 | ||
|
|
e8041dc5be | ||
|
|
433276274b | ||
|
|
a531e337fe | ||
|
|
ea29e080a6 | ||
|
|
c4312fe324 | ||
|
|
3ab6137ff1 | ||
|
|
fde222a6a0 | ||
|
|
ec4d45fb88 | ||
|
|
345d2d9f3b | ||
|
|
d8460d6aa2 | ||
|
|
ecc6463e6c |
5
Makefile
5
Makefile
@@ -4,12 +4,10 @@ AEXT=a
|
||||
FEATURES?=default
|
||||
DEFAULT_FEATURES?=yes
|
||||
RUST_TARGET?=
|
||||
EXTRA_LIBS=
|
||||
|
||||
UNAME := $(shell uname -s)
|
||||
ifeq ($(UNAME),Darwin)
|
||||
SOEXT=dylib
|
||||
EXTRA_LIBS=-framework Security
|
||||
endif
|
||||
|
||||
ifeq ($(DEFAULT_FEATURES),no)
|
||||
@@ -31,8 +29,7 @@ endif
|
||||
build:
|
||||
cargo build --release $(FEATURE_FLAGS) --manifest-path libextism/Cargo.toml $(TARGET_FLAGS)
|
||||
sed -e "s%@CMAKE_INSTALL_PREFIX@%$(DEST)%" libextism/extism.pc.in > libextism/extism.pc
|
||||
sed -e "s%@CMAKE_INSTALL_PREFIX@%$(DEST)%" \
|
||||
-e "s%Libs: %Libs: $(EXTRA_LIBS) %" libextism/extism-static.pc.in > libextism/extism-static.pc
|
||||
sed -e "s%@CMAKE_INSTALL_PREFIX@%$(DEST)%" libextism/extism-static.pc.in > libextism/extism-static.pc
|
||||
|
||||
bench:
|
||||
@(cargo criterion $(TARGET_FLAGS) || echo 'For nicer output use cargo-criterion: `cargo install cargo-criterion` - using `cargo bench`') && cargo bench $(TARGET_FLAGS)
|
||||
|
||||
@@ -51,7 +51,7 @@ started:
|
||||
| Java SDK | <img alt="Java SDK" src="https://extism.org/img/sdk-languages/java-android.svg" width="50px"/> | https://github.com/extism/java-sdk | [Sonatype](https://central.sonatype.com/artifact/org.extism.sdk/extism) |
|
||||
| .NET SDK | <img alt=".NET SDK" src="https://extism.org/img/sdk-languages/dotnet.svg" width="50px"/> | https://github.com/extism/dotnet-sdk <br/>(supports C# & F#!) | [Nuget](https://www.nuget.org/packages/Extism.Sdk) |
|
||||
| OCaml SDK | <img alt="OCaml SDK" src="https://extism.org/img/sdk-languages/ocaml.svg" width="50px"/> | https://github.com/extism/ocaml-sdk | [opam](https://opam.ocaml.org/packages/extism/) |
|
||||
| Perl SDK | <img alt="Perl SDK" src="https://extism.org/img/sdk-languages/perl.svg" width="50px"/> | https://github.com/extism/perl-sdk | [CPAN](https://metacpan.org/pod/Extism) |
|
||||
| Perl SDK | <img alt="Perl SDK" src="https://extism.org/img/sdk-languages/perl.svg" width="50px"/> | https://github.com/extism/perl-sdk | N/A |
|
||||
| PHP SDK | <img alt="PHP SDK" src="https://extism.org/img/sdk-languages/php.svg" width="50px"/> | https://github.com/extism/php-sdk | [Packagist](https://packagist.org/packages/extism/extism) |
|
||||
| Python SDK | <img alt="Python SDK" src="https://extism.org/img/sdk-languages/python.svg" width="50px"/> | https://github.com/extism/python-sdk | [PyPi](https://pypi.org/project/extism/) |
|
||||
| Ruby SDK | <img alt="Ruby SDK" src="https://extism.org/img/sdk-languages/ruby.svg" width="50px"/> | https://github.com/extism/ruby-sdk | [RubyGems](https://rubygems.org/gems/extism) |
|
||||
|
||||
@@ -59,6 +59,10 @@ pub trait ToBytes<'a> {
|
||||
|
||||
/// `to_bytes` converts a value into `Self::Bytes`
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error>;
|
||||
|
||||
fn to_vec(&self) -> Result<Vec<u8>, Error> {
|
||||
self.to_bytes().map(|x| x.as_ref().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for () {
|
||||
@@ -73,6 +77,10 @@ impl<'a> ToBytes<'a> for Vec<u8> {
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Result<Vec<u8>, Error> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for String {
|
||||
@@ -80,6 +88,10 @@ impl<'a> ToBytes<'a> for String {
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Result<Vec<u8>, Error> {
|
||||
self.to_bytes().map(|x| x.into_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for &'a [u8] {
|
||||
@@ -150,6 +162,10 @@ impl<'a, T: ToBytes<'a>> ToBytes<'a> for &'a T {
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
<T as ToBytes>::to_bytes(self)
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Result<Vec<u8>, Error> {
|
||||
<T as ToBytes>::to_vec(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ToBytes<'a>> ToBytes<'a> for Option<T> {
|
||||
@@ -161,6 +177,13 @@ impl<'a, T: ToBytes<'a>> ToBytes<'a> for Option<T> {
|
||||
None => Ok(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Result<Vec<u8>, Error> {
|
||||
match self {
|
||||
Some(x) => <T as ToBytes>::to_vec(x),
|
||||
None => Ok(vec![]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
3
kernel/.gitignore
vendored
3
kernel/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
owi-out
|
||||
*.wat
|
||||
proofs
|
||||
@@ -7,7 +7,6 @@ edition = "2021"
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.39"
|
||||
owi = {git = "https://github.com/dylibso/owi-rs"}
|
||||
|
||||
[features]
|
||||
default = ["bounds-checking"]
|
||||
@@ -16,4 +15,4 @@ bounds-checking = []
|
||||
[workspace]
|
||||
members = [
|
||||
"."
|
||||
]
|
||||
]
|
||||
@@ -1,7 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
export CARGO_FLAGS=""
|
||||
|
||||
while getopts d flag
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
use extism_runtime_kernel::*;
|
||||
use owi::*;
|
||||
|
||||
main!({
|
||||
let n = alloc(1024);
|
||||
|
||||
let x = u64();
|
||||
assume(x > 0);
|
||||
|
||||
let m = alloc(x);
|
||||
|
||||
// 1. Length should equal `x` while active
|
||||
assert(length(m) == x);
|
||||
|
||||
// 2. Length should equal `0` after free
|
||||
free(m); // Free the block
|
||||
assert(length(m) == 0);
|
||||
free(n);
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
use extism_runtime_kernel::*;
|
||||
use owi::*;
|
||||
|
||||
main!({
|
||||
let x = u64();
|
||||
let n = u64();
|
||||
let m = u64();
|
||||
assume(x > 0);
|
||||
assume(n > 0);
|
||||
assume(m > 0);
|
||||
|
||||
alloc(n);
|
||||
alloc(m);
|
||||
let n = alloc(x);
|
||||
assert(error_get() == 0);
|
||||
|
||||
error_set(n);
|
||||
assert(error_get() == n);
|
||||
alloc(m);
|
||||
error_set(0);
|
||||
assert(error_get() == 0);
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
use extism_runtime_kernel::*;
|
||||
use owi::*;
|
||||
|
||||
main!({
|
||||
let x = u64();
|
||||
assume(x > 0);
|
||||
|
||||
let m = alloc(x);
|
||||
assert(length(m) == x);
|
||||
for i in 0..x {
|
||||
store_u8(m + i, i as u8);
|
||||
assert(load_u8(m + i) == i as u8);
|
||||
}
|
||||
free(m);
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
use extism_runtime_kernel::*;
|
||||
use owi::*;
|
||||
|
||||
main!({
|
||||
let x = u64();
|
||||
assume(x > 0);
|
||||
|
||||
let y = u64();
|
||||
assume(y > 0);
|
||||
|
||||
let mut tmp = 0;
|
||||
|
||||
for _ in 0..y {
|
||||
let m = alloc(x);
|
||||
if tmp == 0 {
|
||||
tmp = m;
|
||||
} else {
|
||||
// Check that the existing block is being re-used
|
||||
assert(m == tmp);
|
||||
}
|
||||
|
||||
assert(length(m) == x);
|
||||
free(m); // Free the block
|
||||
}
|
||||
|
||||
let y = u64();
|
||||
assume(y == x + 1);
|
||||
let n = alloc(y);
|
||||
assert(n > tmp);
|
||||
|
||||
let z = u64();
|
||||
assume(z <= x);
|
||||
assume(x - z < 32);
|
||||
assume(z > 0);
|
||||
let p = alloc(z);
|
||||
assert(p == tmp);
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
pub use extism_runtime_kernel::*;
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ impl MemoryRoot {
|
||||
core::ptr::write_bytes(
|
||||
self.blocks.as_mut_ptr() as *mut u8,
|
||||
MemoryStatus::Unused as u8,
|
||||
self_position as usize - core::mem::size_of::<MemoryRoot>(),
|
||||
self_position as usize,
|
||||
);
|
||||
|
||||
// Clear extism runtime metadata
|
||||
@@ -638,15 +638,4 @@ mod test {
|
||||
|
||||
assert_eq!(last, 0);
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_sym() {
|
||||
unsafe {
|
||||
reset();
|
||||
let a = alloc(47);
|
||||
let b = alloc(20);
|
||||
assert_eq!(length(a), 47);
|
||||
assert_eq!(length(b), 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
OUT_DIR=./target/wasm32-unknown-unknown/release/examples/
|
||||
get_proof() {
|
||||
cp "$OUT_DIR/$1.wasm" "./proofs/$1.wasm"
|
||||
}
|
||||
|
||||
cargo build --examples --release --target wasm32-unknown-unknown --no-default-features
|
||||
|
||||
mkdir -p proofs
|
||||
get_proof alloc_length
|
||||
get_proof load_store
|
||||
get_proof reuse
|
||||
get_proof error
|
||||
|
||||
for proof in $(ls proofs/*.wasm); do
|
||||
echo "Checking $proof"
|
||||
owi conc "$proof" $@
|
||||
echo
|
||||
echo "---"
|
||||
done
|
||||
@@ -1,2 +0,0 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 2
|
||||
@@ -4,7 +4,7 @@ build:
|
||||
|
||||
.PHONY: static
|
||||
static:
|
||||
$(CC) -g -o example example.c -l:libextism.a -lm -lpthread
|
||||
$(CC) -g -o example example.c -l:libextism.a -lm
|
||||
|
||||
# if needed, set PKG_CONFIG_PATH= to the directory with extism*.pc installed
|
||||
LDFLAGS=`pkg-config --libs extism`
|
||||
|
||||
@@ -56,6 +56,7 @@ Since you may not have an Extism plug-in on hand to test, let's load a demo plug
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void print_plugin_output(ExtismPlugin *plugin, int32_t rc){
|
||||
if (rc != EXTISM_SUCCESS) {
|
||||
@@ -63,9 +64,9 @@ void print_plugin_output(ExtismPlugin *plugin, int32_t rc){
|
||||
return;
|
||||
}
|
||||
|
||||
ExtismSize outlen = extism_plugin_output_length(plugin);
|
||||
size_t outlen = extism_plugin_output_length(plugin);
|
||||
const uint8_t *out = extism_plugin_output_data(plugin);
|
||||
fwrite(out, 1, outlen, stdout);
|
||||
write(STDOUT_FILENO, out, outlen);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
@@ -105,9 +106,9 @@ if (rc != EXTISM_SUCCESS) {
|
||||
exit(2);
|
||||
}
|
||||
|
||||
ExtismSize outlen = extism_plugin_output_length(plugin);
|
||||
size_t outlen = extism_plugin_output_length(plugin);
|
||||
const uint8_t *out = extism_plugin_output_data(plugin);
|
||||
fwrite(out, 1, outlen, stdout);
|
||||
write(STDOUT_FILENO, out, outlen);
|
||||
```
|
||||
|
||||
Will print
|
||||
@@ -170,6 +171,7 @@ We want to expose two functions to our plugin, `kv_write(key: String, value: Byt
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// A stubbed out KV store
|
||||
typedef struct KVStore KVStore;
|
||||
@@ -182,14 +184,14 @@ extern const uint32_t fake_kv_store_get(KVStore *kv, const char *key,
|
||||
|
||||
// Our host functions to access the fake KV store
|
||||
void kv_get(ExtismCurrentPlugin *plugin, const ExtismVal *inputs,
|
||||
ExtismSize ninputs, ExtismVal *outputs, ExtismSize noutputs,
|
||||
size_t ninputs, ExtismVal *outputs, size_t noutputs,
|
||||
void *userdata) {
|
||||
// Cast the userdata pointer
|
||||
KVStore *kv = (KVStore *)userdata;
|
||||
|
||||
// Get the offset to the key in the plugin memory
|
||||
uint64_t offs = inputs[0].v.i64;
|
||||
ExtismSize keylen = extism_current_plugin_memory_length(plugin, offs);
|
||||
size_t keylen = extism_current_plugin_memory_length(plugin, offs);
|
||||
|
||||
// Allocate a new block to return
|
||||
uint64_t outoffs =
|
||||
@@ -203,23 +205,23 @@ void kv_get(ExtismCurrentPlugin *plugin, const ExtismVal *inputs,
|
||||
*(uint64_t *)(extism_current_plugin_memory(plugin) + outoffs) = value;
|
||||
|
||||
// Return the offset to our allocated block
|
||||
outputs[0].t = EXTISM_PTR;
|
||||
outputs[0].t = PTR;
|
||||
outputs[0].v.i64 = outoffs;
|
||||
}
|
||||
|
||||
void kv_set(ExtismCurrentPlugin *plugin, const ExtismVal *inputs,
|
||||
ExtismSize ninputs, ExtismVal *outputs, ExtismSize noutputs,
|
||||
size_t ninputs, ExtismVal *outputs, size_t noutputs,
|
||||
void *userdata) {
|
||||
// Cast the userdata pointer
|
||||
KVStore *kv = (KVStore *)userdata;
|
||||
|
||||
// Get the offset to the key in the plugin memory
|
||||
uint64_t keyoffs = inputs[0].v.i64;
|
||||
ExtismSize keylen = extism_current_plugin_memory_length(plugin, keyoffs);
|
||||
size_t keylen = extism_current_plugin_memory_length(plugin, keyoffs);
|
||||
|
||||
// Get the offset to the value in the plugin memory
|
||||
uint64_t valueoffs = inputs[1].v.i64;
|
||||
ExtismSize valuelen = extism_current_plugin_memory_length(plugin, valueoffs);
|
||||
size_t valuelen = extism_current_plugin_memory_length(plugin, valueoffs);
|
||||
|
||||
// Set key => value
|
||||
fake_kv_store_set(
|
||||
@@ -232,13 +234,13 @@ int main(void) {
|
||||
const char *manifest = "{\"wasm\": [{\"url\": "
|
||||
"\"https://github.com/extism/plugins/releases/latest/"
|
||||
"download/count_vowels_kvstore.wasm\"}]}";
|
||||
const ExtismValType kv_get_inputs[] = {EXTISM_PTR};
|
||||
const ExtismValType kv_get_outputs[] = {EXTISM_PTR};
|
||||
const ExtismValType kv_get_inputs[] = {PTR};
|
||||
const ExtismValType kv_get_outputs[] = {PTR};
|
||||
ExtismFunction *kv_get_fn = extism_function_new(
|
||||
"kv_get", kv_get_inputs, 1, kv_get_outputs, 1, kv_get, kv, NULL);
|
||||
|
||||
const ExtismValType kv_set_inputs[] = {EXTISM_PTR};
|
||||
const ExtismValType kv_set_outputs[] = {EXTISM_PTR};
|
||||
const ExtismValType kv_set_inputs[] = {PTR};
|
||||
const ExtismValType kv_set_outputs[] = {PTR};
|
||||
ExtismFunction *kv_set_fn = extism_function_new(
|
||||
"kv_set", kv_set_inputs, 1, kv_set_outputs, 1, kv_set, kv, NULL);
|
||||
const ExtismFunction *functions[] = {kv_get_fn};
|
||||
@@ -286,3 +288,5 @@ print_plugin_output(plugin, extism_plugin_call(plugin, "count_vowels",
|
||||
# => Writing value=6 from key=count-vowels"
|
||||
# => {"count": 3, "total": 6, "vowels": "aeiouAEIOU"}
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void log_handler(const char *line, ExtismSize length) {
|
||||
void log_handler(const char *line, uintptr_t length) {
|
||||
fwrite(line, length, 1, stderr);
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
size_t len = 0;
|
||||
uint8_t *data = read_file("../wasm/code-functions.wasm", &len);
|
||||
ExtismValType inputs[] = {EXTISM_PTR};
|
||||
ExtismValType outputs[] = {EXTISM_PTR};
|
||||
ExtismValType inputs[] = {PTR};
|
||||
ExtismValType outputs[] = {PTR};
|
||||
ExtismFunction *f =
|
||||
extism_function_new("hello_world", inputs, 1, outputs, 1, hello_world,
|
||||
"Hello, again!", free_data);
|
||||
|
||||
@@ -5,6 +5,6 @@ includedir=${prefix}/include
|
||||
Version: 1.0.0
|
||||
Name: Extism
|
||||
Description: The framework for building with WebAssembly (wasm).
|
||||
Libs: ${libdir}/libextism.a
|
||||
Libs.private: -lm -lpthread
|
||||
Libs: -L${libdir} -l:libextism.a
|
||||
Libs.private: -lm
|
||||
Cflags: -I${includedir}
|
||||
|
||||
@@ -6,5 +6,5 @@ Version: 1.0.0
|
||||
Name: Extism
|
||||
Description: The framework for building with WebAssembly (wasm).
|
||||
Libs: -L${libdir} -lextism
|
||||
Libs.private: -lm -lpthread
|
||||
Libs.private: -lm
|
||||
Cflags: -I${includedir}
|
||||
|
||||
@@ -10,14 +10,14 @@ version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
wasmtime = ">= 20.0.0, < 22.0.0"
|
||||
wasi-common = ">= 20.0.0, < 22.0.0"
|
||||
wasmtime-wasi = ">= 20.0.0, < 22.0.0"
|
||||
anyhow = "1"
|
||||
serde = {version = "1", features = ["derive"]}
|
||||
serde_json = "1"
|
||||
toml = "0.8"
|
||||
sha2 = "0.10"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = {version = "0.3.18", features = ["std", "env-filter", "fmt"]}
|
||||
tracing-subscriber = {version = "0.3", features = ["std", "env-filter", "fmt"]}
|
||||
url = "2"
|
||||
glob = "0.3"
|
||||
ureq = {version = "2.5", optional=true}
|
||||
|
||||
@@ -1,5 +1,88 @@
|
||||
use wasmtime_wasi::pipe::{MemoryInputPipe, MemoryOutputPipe};
|
||||
|
||||
use crate::*;
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct WasiConfig {
|
||||
pub args: Vec<String>,
|
||||
pub stdin: Option<wasmtime_wasi::pipe::MemoryInputPipe>,
|
||||
pub stdout: Option<wasmtime_wasi::pipe::MemoryOutputPipe>,
|
||||
pub stderr: Option<wasmtime_wasi::pipe::MemoryOutputPipe>,
|
||||
}
|
||||
|
||||
impl WasiConfig {
|
||||
pub fn new() -> WasiConfig {
|
||||
WasiConfig::default()
|
||||
}
|
||||
|
||||
pub fn with_arg(mut self, x: impl Into<String>) -> Self {
|
||||
self.args.push(x.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_args(mut self, x: impl IntoIterator<Item = impl Into<String>>) -> Self {
|
||||
let x: Vec<String> = x.into_iter().map(|x| x.into()).collect();
|
||||
self.args.extend(x);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_stdin<'a, T: ToBytes<'a>>(mut self, x: T) -> Result<Self, Error> {
|
||||
self.stdin = Some(MemoryInputPipe::new(x.to_vec()?));
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn with_stdout(mut self, size: usize) -> Self {
|
||||
self.stdout = Some(MemoryOutputPipe::new(size));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_stderr(mut self, size: usize) -> Self {
|
||||
self.stderr = Some(MemoryOutputPipe::new(size));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WasiOutput {
|
||||
pub(crate) return_code: i32,
|
||||
pub(crate) stdout: Option<wasmtime_wasi::pipe::MemoryOutputPipe>,
|
||||
pub(crate) stderr: Option<wasmtime_wasi::pipe::MemoryOutputPipe>,
|
||||
}
|
||||
|
||||
impl WasiOutput {
|
||||
pub fn return_code(&self) -> i32 {
|
||||
self.return_code
|
||||
}
|
||||
|
||||
pub fn stdout(&self) -> Vec<u8> {
|
||||
match &self.stdout {
|
||||
Some(x) => x.contents().to_vec(),
|
||||
None => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stderr(&self) -> Vec<u8> {
|
||||
match &self.stderr {
|
||||
Some(x) => x.contents().to_vec(),
|
||||
None => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_stdout<T: FromBytesOwned>(&self) -> Result<T, Error> {
|
||||
match &self.stdout {
|
||||
Some(x) => T::from_bytes(x.contents().as_ref()),
|
||||
None => anyhow::bail!("stdout not enabled"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_stderr<T: FromBytesOwned>(&self) -> Result<T, Error> {
|
||||
match &self.stderr {
|
||||
Some(x) => T::from_bytes(x.contents().as_ref()),
|
||||
None => anyhow::bail!("stdout not enabled"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CurrentPlugin stores data that is available to the caller in PDK functions, this should
|
||||
/// only be accessed from inside a host function
|
||||
pub struct CurrentPlugin {
|
||||
@@ -307,37 +390,73 @@ impl CurrentPlugin {
|
||||
pub(crate) fn new(
|
||||
manifest: extism_manifest::Manifest,
|
||||
wasi: bool,
|
||||
wasi_args: Option<WasiConfig>,
|
||||
available_pages: Option<u32>,
|
||||
id: uuid::Uuid,
|
||||
) -> Result<Self, Error> {
|
||||
let wasi = if wasi {
|
||||
let auth = wasi_common::sync::ambient_authority();
|
||||
let random = wasi_common::sync::random_ctx();
|
||||
let clocks = wasi_common::sync::clocks_ctx();
|
||||
let sched = wasi_common::sync::sched_ctx();
|
||||
let table = wasi_common::Table::new();
|
||||
let ctx = wasi_common::WasiCtx::new(random, clocks, sched, table);
|
||||
let mut ctx = wasmtime_wasi::WasiCtxBuilder::new();
|
||||
ctx.allow_ip_name_lookup(true)
|
||||
.allow_tcp(true)
|
||||
.allow_udp(true)
|
||||
.allow_blocking_current_thread(true);
|
||||
|
||||
if let Some(wasi_args) = wasi_args {
|
||||
ctx.args(&wasi_args.args);
|
||||
|
||||
if let Some(stdin) = wasi_args.stdin {
|
||||
ctx.stdin(stdin);
|
||||
}
|
||||
|
||||
if let Some(stdout) = wasi_args.stdout {
|
||||
ctx.stdout(stdout);
|
||||
}
|
||||
|
||||
if let Some(stderr) = wasi_args.stderr {
|
||||
ctx.stderr(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(a) = &manifest.allowed_paths {
|
||||
for (k, v) in a.iter() {
|
||||
let file = Box::new(wasi_common::sync::dir::Dir::from_cap_std(
|
||||
wasi_common::sync::Dir::open_ambient_dir(k, auth)?,
|
||||
));
|
||||
ctx.push_preopened_dir(file, v)?;
|
||||
ctx.preopened_dir(
|
||||
k,
|
||||
v.to_string_lossy(),
|
||||
wasmtime_wasi::DirPerms::READ | wasmtime_wasi::DirPerms::MUTATE,
|
||||
wasmtime_wasi::FilePerms::READ | wasmtime_wasi::FilePerms::WRITE,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(h) = &manifest.allowed_hosts {
|
||||
let h = h.clone();
|
||||
ctx.socket_addr_check(move |addr, _kind| {
|
||||
for host in h.iter() {
|
||||
let addrs = std::net::ToSocketAddrs::to_socket_addrs(&host);
|
||||
if let Ok(addrs) = addrs {
|
||||
for a in addrs.into_iter() {
|
||||
if addr == &a {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
// Enable WASI output, typically used for debugging purposes
|
||||
if std::env::var("EXTISM_ENABLE_WASI_OUTPUT").is_ok() {
|
||||
ctx.set_stderr(Box::new(wasi_common::sync::stdio::stderr()));
|
||||
ctx.set_stdout(Box::new(wasi_common::sync::stdio::stdout()));
|
||||
ctx.inherit_stdout().inherit_stderr();
|
||||
}
|
||||
|
||||
Some(Wasi { ctx })
|
||||
Some(Wasi {
|
||||
ctx: ctx.build_p1(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let memory_limiter = if let Some(pgs) = available_pages {
|
||||
let n = pgs as usize * 65536;
|
||||
Some(crate::current_plugin::MemoryLimiter {
|
||||
|
||||
@@ -176,8 +176,8 @@ pub struct Function {
|
||||
/// Module name
|
||||
pub(crate) namespace: Option<String>,
|
||||
|
||||
pub(crate) params: Vec<ValType>,
|
||||
pub(crate) results: Vec<ValType>,
|
||||
pub(crate) params: Vec<wasmtime::ValType>,
|
||||
pub(crate) results: Vec<wasmtime::ValType>,
|
||||
|
||||
/// Function handle
|
||||
pub(crate) f: Arc<FunctionInner>,
|
||||
@@ -203,8 +203,8 @@ impl Function {
|
||||
{
|
||||
let data = user_data.clone();
|
||||
let name = name.into();
|
||||
let params = params.into_iter().collect();
|
||||
let results = results.into_iter().collect();
|
||||
let params = params.into_iter().map(wasmtime::ValType::from).collect();
|
||||
let results = results.into_iter().map(wasmtime::ValType::from).collect();
|
||||
trace!("Creating function {name}: params={params:?}, results={results:?}");
|
||||
Function {
|
||||
name,
|
||||
@@ -225,19 +225,7 @@ impl Function {
|
||||
}
|
||||
|
||||
pub(crate) fn ty(&self, engine: &wasmtime::Engine) -> wasmtime::FuncType {
|
||||
wasmtime::FuncType::new(
|
||||
engine,
|
||||
self.params
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(wasmtime::ValType::from)
|
||||
.collect::<Vec<_>>(),
|
||||
self.results
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(wasmtime::ValType::from)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
wasmtime::FuncType::new(engine, self.params.clone(), self.results.clone())
|
||||
}
|
||||
|
||||
/// Host function name
|
||||
@@ -262,16 +250,6 @@ impl Function {
|
||||
self.set_namespace(namespace);
|
||||
self
|
||||
}
|
||||
|
||||
/// Get param types
|
||||
pub fn params(&self) -> &[ValType] {
|
||||
&self.params
|
||||
}
|
||||
|
||||
/// Get result types
|
||||
pub fn results(&self) -> &[ValType] {
|
||||
&self.results
|
||||
}
|
||||
}
|
||||
|
||||
/// The `host_fn` macro is used to define typed host functions
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::*;
|
||||
/// WASI context
|
||||
pub struct Wasi {
|
||||
/// wasi
|
||||
pub ctx: wasi_common::WasiCtx,
|
||||
pub ctx: wasmtime_wasi::preview1::WasiP1Ctx,
|
||||
}
|
||||
|
||||
/// InternalExt provides a unified way of acessing `memory`, `store` and `internal` values
|
||||
|
||||
@@ -23,7 +23,7 @@ mod timer;
|
||||
/// Extism C API
|
||||
pub mod sdk;
|
||||
|
||||
pub use current_plugin::CurrentPlugin;
|
||||
pub use current_plugin::{CurrentPlugin, WasiConfig, WasiOutput};
|
||||
pub use extism_convert::{FromBytes, FromBytesOwned, ToBytes};
|
||||
pub use extism_manifest::{Manifest, Wasm, WasmMetadata};
|
||||
pub use function::{Function, UserData, Val, ValType, PTR};
|
||||
|
||||
@@ -250,12 +250,12 @@ fn relink(
|
||||
|
||||
// If wasi is enabled then add it to the linker
|
||||
if with_wasi {
|
||||
wasi_common::sync::add_to_linker(&mut linker, |x: &mut CurrentPlugin| {
|
||||
wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |x: &mut CurrentPlugin| {
|
||||
&mut x.wasi.as_mut().unwrap().ctx
|
||||
})?;
|
||||
}
|
||||
|
||||
for f in imports {
|
||||
for f in imports.iter() {
|
||||
let name = f.name();
|
||||
let ns = f.namespace().unwrap_or(EXTISM_USER_MODULE);
|
||||
unsafe {
|
||||
@@ -338,13 +338,12 @@ impl Plugin {
|
||||
let id = uuid::Uuid::new_v4();
|
||||
let mut store = Store::new(
|
||||
&engine,
|
||||
CurrentPlugin::new(manifest, with_wasi, available_pages, id)?,
|
||||
CurrentPlugin::new(manifest, with_wasi, None, available_pages, id)?,
|
||||
);
|
||||
store.set_epoch_deadline(1);
|
||||
|
||||
let imports: Vec<Function> = imports.into_iter().collect();
|
||||
let (instance_pre, linker) = relink(&engine, &mut store, &imports, &modules, with_wasi)?;
|
||||
|
||||
let timer_tx = Timer::tx();
|
||||
let mut plugin = Plugin {
|
||||
modules,
|
||||
@@ -378,22 +377,24 @@ impl Plugin {
|
||||
pub(crate) fn reset_store(
|
||||
&mut self,
|
||||
instance_lock: &mut std::sync::MutexGuard<Option<Instance>>,
|
||||
wasi_args: Option<WasiConfig>,
|
||||
) -> Result<(), Error> {
|
||||
if self.store_needs_reset {
|
||||
if self.store_needs_reset || wasi_args.is_some() {
|
||||
let engine = self.store.engine().clone();
|
||||
let internal = self.current_plugin_mut();
|
||||
let with_wasi = internal.wasi.is_some();
|
||||
let with_wasi = internal.wasi.is_some() || wasi_args.is_some();
|
||||
self.store = Store::new(
|
||||
&engine,
|
||||
CurrentPlugin::new(
|
||||
internal.manifest.clone(),
|
||||
internal.wasi.is_some(),
|
||||
wasi_args,
|
||||
internal.available_pages,
|
||||
self.id,
|
||||
)?,
|
||||
);
|
||||
self.linker = Linker::new(&engine);
|
||||
self.store.set_epoch_deadline(1);
|
||||
|
||||
let (instance_pre, linker) = relink(
|
||||
&engine,
|
||||
&mut self.store,
|
||||
@@ -403,6 +404,7 @@ impl Plugin {
|
||||
)?;
|
||||
self.linker = linker;
|
||||
self.instance_pre = instance_pre;
|
||||
|
||||
let store = &mut self.store as *mut _;
|
||||
let linker = &mut self.linker as *mut _;
|
||||
let current_plugin = self.current_plugin_mut();
|
||||
@@ -702,16 +704,18 @@ impl Plugin {
|
||||
name: impl AsRef<str>,
|
||||
input: impl AsRef<[u8]>,
|
||||
host_context: Option<Rooted<ExternRef>>,
|
||||
wasi_args: Option<WasiConfig>,
|
||||
) -> Result<i32, (Error, i32)> {
|
||||
let name = name.as_ref();
|
||||
let input = input.as_ref();
|
||||
|
||||
if let Err(e) = self.reset_store(lock) {
|
||||
if let Err(e) = self.reset_store(lock, wasi_args) {
|
||||
error!(
|
||||
plugin = self.id.to_string(),
|
||||
"call to Plugin::reset_store failed: {e:?}"
|
||||
);
|
||||
}
|
||||
self.store_needs_reset = name == "_start";
|
||||
|
||||
self.instantiate(lock).map_err(|e| (e, -1))?;
|
||||
|
||||
@@ -756,7 +760,6 @@ impl Plugin {
|
||||
self.store
|
||||
.epoch_deadline_callback(|_| Ok(UpdateDeadline::Continue(1)));
|
||||
let _ = self.timer_tx.send(TimerAction::Stop { id: self.id });
|
||||
self.store_needs_reset = name == "_start";
|
||||
|
||||
// Get extism error
|
||||
self.get_output_after_call().map_err(|x| (x, -1))?;
|
||||
@@ -834,7 +837,7 @@ impl Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
let wasi_exit_code = e.downcast_ref::<wasi_common::I32Exit>().map(|e| e.0);
|
||||
let wasi_exit_code = e.downcast_ref::<wasmtime_wasi::I32Exit>().map(|e| e.0);
|
||||
if let Some(exit_code) = wasi_exit_code {
|
||||
debug!(
|
||||
plugin = self.id.to_string(),
|
||||
@@ -897,15 +900,9 @@ impl Plugin {
|
||||
let lock = self.instance.clone();
|
||||
let mut lock = lock.lock().unwrap();
|
||||
let data = input.to_bytes()?;
|
||||
self.raw_call(&mut lock, name, data, None)
|
||||
self.raw_call(&mut lock, name, data, None, None)
|
||||
.map_err(|e| e.0)
|
||||
.and_then(move |rc| {
|
||||
if rc != 0 {
|
||||
Err(Error::msg(format!("Returned non-zero exit code: {rc}")))
|
||||
} else {
|
||||
self.output()
|
||||
}
|
||||
})
|
||||
.and_then(move |_| self.output())
|
||||
}
|
||||
|
||||
pub fn call_with_host_context<'a, 'b, T, U, C>(
|
||||
@@ -922,12 +919,32 @@ impl Plugin {
|
||||
let lock = self.instance.clone();
|
||||
let mut lock = lock.lock().unwrap();
|
||||
let data = input.to_bytes()?;
|
||||
let ctx = ExternRef::new(&mut self.store, host_context)?;
|
||||
self.raw_call(&mut lock, name, data, Some(ctx))
|
||||
let r = ExternRef::new(&mut self.store, host_context)?;
|
||||
self.raw_call(&mut lock, name, data, Some(r), None)
|
||||
.map_err(|e| e.0)
|
||||
.and_then(move |_| self.output())
|
||||
}
|
||||
|
||||
/// Execute the `_start` function of a WASI command module, providing input/output and command-line arguments
|
||||
/// via `WasiConfig`
|
||||
pub fn run_wasi_command(&mut self, wasi_args: WasiConfig) -> Result<WasiOutput, Error> {
|
||||
let mut output = WasiOutput {
|
||||
return_code: 0,
|
||||
stdout: wasi_args.stdout.clone(),
|
||||
stderr: wasi_args.stderr.clone(),
|
||||
};
|
||||
let lock = self.instance.clone();
|
||||
let mut lock = lock.lock().unwrap();
|
||||
let res = self.raw_call(&mut lock, "_start", b"", None, Some(wasi_args));
|
||||
|
||||
output.return_code = match res {
|
||||
Ok(rc) => rc,
|
||||
Err((_, rc)) => rc,
|
||||
};
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// Similar to `Plugin::call`, but returns the Extism error code along with the
|
||||
/// `Error`. It is assumed if `Ok(_)` is returned that the error code was `0`.
|
||||
///
|
||||
@@ -942,7 +959,7 @@ impl Plugin {
|
||||
let lock = self.instance.clone();
|
||||
let mut lock = lock.lock().unwrap();
|
||||
let data = input.to_bytes().map_err(|e| (e, -1))?;
|
||||
self.raw_call(&mut lock, name, data, None)
|
||||
self.raw_call(&mut lock, name, data, None, None)
|
||||
.and_then(move |_| self.output().map_err(|e| (e, -1)))
|
||||
}
|
||||
|
||||
|
||||
@@ -218,10 +218,9 @@ pub unsafe extern "C" fn extism_function_new(
|
||||
output_types.clone(),
|
||||
user_data,
|
||||
move |plugin, inputs, outputs, user_data| {
|
||||
let store = &*plugin.store;
|
||||
let inputs: Vec<_> = inputs
|
||||
.iter()
|
||||
.map(|x| ExtismVal::from_val(x, store))
|
||||
.map(|x| ExtismVal::from_val(x, unsafe { &*plugin.store }))
|
||||
.collect();
|
||||
let mut output_tmp: Vec<_> = output_types
|
||||
.iter()
|
||||
@@ -522,7 +521,8 @@ pub unsafe extern "C" fn extism_plugin_call_with_host_context(
|
||||
Err(e) => return plugin.return_error(&mut lock, e, -1),
|
||||
Ok(x) => x,
|
||||
};
|
||||
let res = plugin.raw_call(&mut lock, name, input, Some(r));
|
||||
let res = plugin.raw_call(&mut lock, name, input, Some(r), None);
|
||||
|
||||
match res {
|
||||
Err((e, rc)) => plugin.return_error(&mut lock, e, rc),
|
||||
Ok(x) => x,
|
||||
|
||||
Reference in New Issue
Block a user