mirror of
https://github.com/extism/extism.git
synced 2026-01-12 15:28:05 -05:00
Compare commits
74 Commits
stable
...
add-protob
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3341eb238 | ||
|
|
2d01efb833 | ||
|
|
f36d0f60cc | ||
|
|
215d6838e9 | ||
|
|
1aff1ce69c | ||
|
|
ce5d43c9ae | ||
|
|
c5b3af2963 | ||
|
|
f3447a538c | ||
|
|
fab44c5db8 | ||
|
|
0e65c3afae | ||
|
|
fea4e6c293 | ||
|
|
c2910bdc60 | ||
|
|
1f72c2a1ce | ||
|
|
4bd0ed6d03 | ||
|
|
dba0b67cd3 | ||
|
|
abb31ee7de | ||
|
|
30322dba23 | ||
|
|
8ff9b9a015 | ||
|
|
728ee84b73 | ||
|
|
2c82b928ab | ||
|
|
eb05381297 | ||
|
|
c5fce439d1 | ||
|
|
f0f2d59d8f | ||
|
|
8cd8135414 | ||
|
|
94f42413ab | ||
|
|
2ef701ae00 | ||
|
|
18b01b5d16 | ||
|
|
6e0fd3baa1 | ||
|
|
09961010f6 | ||
|
|
c893faede4 | ||
|
|
6f4b43bedc | ||
|
|
a9835da614 | ||
|
|
64fd246b73 | ||
|
|
59e8335f17 | ||
|
|
703cacda91 | ||
|
|
bc56d7303a | ||
|
|
e7ffc1dd6b | ||
|
|
d994f081f9 | ||
|
|
583befbaef | ||
|
|
4c9dc6f6e4 | ||
|
|
08db39c153 | ||
|
|
75a8495772 | ||
|
|
be646480b9 | ||
|
|
8426e1a0a6 | ||
|
|
00ccb5be9d | ||
|
|
6e8c28b0e9 | ||
|
|
98a4244915 | ||
|
|
326a4c86a8 | ||
|
|
1ec4fda8fc | ||
|
|
e7452a6f44 | ||
|
|
d815b31352 | ||
|
|
28074af2b9 | ||
|
|
016e9158ef | ||
|
|
d040c8b8a8 | ||
|
|
ab458ebd44 | ||
|
|
cb55e52506 | ||
|
|
4377cbd7a0 | ||
|
|
e1a5b01ede | ||
|
|
de19168540 | ||
|
|
2bf5ac75c0 | ||
|
|
3e92b05db0 | ||
|
|
4caee2de1a | ||
|
|
5339f3ca1e | ||
|
|
26f70e3a30 | ||
|
|
3ca661546d | ||
|
|
d85bae6187 | ||
|
|
8a19142524 | ||
|
|
ddcbeec3de | ||
|
|
aa073b8acc | ||
|
|
8c2125cdcd | ||
|
|
3b89b51b62 | ||
|
|
92a7eecf69 | ||
|
|
65231cada5 | ||
|
|
d13301a346 |
8
.github/workflows/browser-ci.yml
vendored
8
.github/workflows/browser-ci.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-node.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- browser/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Browser CI
|
||||
|
||||
8
.github/workflows/ci-cpp.yml
vendored
8
.github/workflows/ci-cpp.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-cpp.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- cpp/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: C++ CI
|
||||
|
||||
28
.github/workflows/ci-d.yml
vendored
Normal file
28
.github/workflows/ci-d.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: D CI
|
||||
|
||||
jobs:
|
||||
d:
|
||||
name: D
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
# TODO: Use multiple versions once stable
|
||||
d_version: [ldc-1.33.0]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Setup D environment
|
||||
uses: dlang-community/setup-dlang@v1
|
||||
with:
|
||||
compiler: ${{ matrix.d_version }}
|
||||
|
||||
- name: Test D Host SDK
|
||||
run: |
|
||||
dub --version
|
||||
LD_LIBRARY_PATH=/usr/local/lib dub test
|
||||
8
.github/workflows/ci-dotnet.yml
vendored
8
.github/workflows/ci-dotnet.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-dotnet.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- dotnet/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: .NET CI
|
||||
|
||||
8
.github/workflows/ci-elixir.yml
vendored
8
.github/workflows/ci-elixir.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-elixir.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- rust/**
|
||||
- elixir/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Elixir CI
|
||||
|
||||
12
.github/workflows/ci-go.yml
vendored
12
.github/workflows/ci-go.yml
vendored
@@ -1,16 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-go.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- extism.go
|
||||
- extism_test.go
|
||||
- go.mod
|
||||
- libextism.pc
|
||||
- go/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Go CI
|
||||
|
||||
8
.github/workflows/ci-haskell.yml
vendored
8
.github/workflows/ci-haskell.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-haskell.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- haskell/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Haskell CI
|
||||
|
||||
8
.github/workflows/ci-java.yml
vendored
8
.github/workflows/ci-java.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-java.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- java/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Java CI
|
||||
|
||||
8
.github/workflows/ci-node.yml
vendored
8
.github/workflows/ci-node.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-node.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- node/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Node CI
|
||||
|
||||
10
.github/workflows/ci-ocaml.yml
vendored
10
.github/workflows/ci-ocaml.yml
vendored
@@ -1,14 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-ocaml.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- ocaml/**
|
||||
- dune-project
|
||||
- extism.opam
|
||||
workflow_dispatch:
|
||||
|
||||
name: OCaml CI
|
||||
|
||||
10
.github/workflows/ci-php.yml
vendored
10
.github/workflows/ci-php.yml
vendored
@@ -1,14 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-php.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- php/**
|
||||
- composer.json
|
||||
- composer.lock
|
||||
workflow_dispatch:
|
||||
|
||||
name: PHP CI
|
||||
|
||||
8
.github/workflows/ci-python.yml
vendored
8
.github/workflows/ci-python.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-python.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- python/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Python CI
|
||||
|
||||
8
.github/workflows/ci-ruby.yml
vendored
8
.github/workflows/ci-ruby.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-ruby.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- ruby/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Ruby CI
|
||||
|
||||
40
.github/workflows/ci-rust.yml
vendored
40
.github/workflows/ci-rust.yml
vendored
@@ -3,6 +3,7 @@ on:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-rust.yml
|
||||
- convert/**
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- rust/**
|
||||
@@ -12,9 +13,8 @@ on:
|
||||
name: Rust CI
|
||||
|
||||
env:
|
||||
RUNTIME_CRATE: extism-runtime
|
||||
RUNTIME_CRATE: extism
|
||||
LIBEXTISM_CRATE: libextism
|
||||
RUST_SDK_CRATE: extism
|
||||
|
||||
jobs:
|
||||
lib:
|
||||
@@ -40,13 +40,13 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/release/libextism.*
|
||||
key: ${{ runner.os }}-libextism-${{ hashFiles('runtime/**') }}-${{ hashFiles('manifest/**') }}
|
||||
key: ${{ runner.os }}-libextism-${{ hashFiles('runtime/**') }}-${{ hashFiles('manifest/**') }}-${{ hashFiles('convert/**') }}
|
||||
- name: Cache target
|
||||
id: cache-target
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/**
|
||||
key: ${{ runner.os }}-target-${{ env.GITHUB_SHA }}
|
||||
key: ${{ runner.os }}-target-${{ github.sha }}
|
||||
- name: Build
|
||||
if: steps.cache-libextism.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
@@ -80,26 +80,40 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/**
|
||||
key: ${{ runner.os }}-target-${{ env.GITHUB_SHA }}
|
||||
key: ${{ runner.os }}-target-${{ github.sha }}
|
||||
- name: Format
|
||||
run: cargo fmt --check -p ${{ env.RUNTIME_CRATE }}
|
||||
- name: Lint
|
||||
run: cargo clippy --release --all-features --no-deps -p ${{ env.RUNTIME_CRATE }}
|
||||
- name: Test
|
||||
run: cargo test --release -p ${{ env.RUNTIME_CRATE }}
|
||||
- name: Test all features
|
||||
run: cargo test --all-features --release -p ${{ env.RUNTIME_CRATE }}
|
||||
|
||||
rust:
|
||||
name: Rust
|
||||
needs: lib
|
||||
- name: Test no features
|
||||
run: cargo test --no-default-features --release -p ${{ env.RUNTIME_CRATE }}
|
||||
bench:
|
||||
name: Benchmarking
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
os: [ubuntu-latest]
|
||||
rust:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/extism
|
||||
- name: Test Rust Host SDK
|
||||
run: LD_LIBRARY_PATH=/usr/local/lib cargo test --release -p ${{ env.RUST_SDK_CRATE }}
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
- name: Cache Rust environment
|
||||
uses: Swatinem/rust-cache@v1
|
||||
- name: Cache target
|
||||
id: cache-target
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: target/**
|
||||
key: ${{ runner.os }}-target-${{ github.sha }}
|
||||
- run: cargo install cargo-criterion
|
||||
- run: cargo criterion
|
||||
8
.github/workflows/ci-zig.yml
vendored
8
.github/workflows/ci-zig.yml
vendored
@@ -1,12 +1,4 @@
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/actions/extism/**
|
||||
- .github/workflows/ci-zig.yml
|
||||
- manifest/**
|
||||
- runtime/**
|
||||
- libextism/**
|
||||
- zig/**
|
||||
workflow_dispatch:
|
||||
|
||||
name: Zig CI
|
||||
|
||||
47
.github/workflows/kernel.yml
vendored
Normal file
47
.github/workflows/kernel.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- kernel/**
|
||||
|
||||
name: Kernel
|
||||
|
||||
jobs:
|
||||
kernel:
|
||||
name: Build extism-runtime.wasm
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
target: wasm32-unknown-unknown
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Install deps
|
||||
run: |
|
||||
sudo apt install wabt --yes
|
||||
|
||||
- name: Build kernel
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
run: |
|
||||
cd kernel
|
||||
sh build.sh
|
||||
git diff --exit-code
|
||||
export GIT_EXIT_CODE=$?
|
||||
|
||||
- uses: peter-evans/create-pull-request@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
if: ${{ env.GIT_EXIT_CODE }} != 0
|
||||
with:
|
||||
title: "update(kernel): extism-runtime.wasm in ${{ github.event.pull_request.head.ref }}"
|
||||
body: "Automated PR to update `runtime/src/extism-runtime.wasm` in PR #${{ github.event.number }}"
|
||||
base: "${{ github.event.pull_request.head.ref }}"
|
||||
branch: "update-kernel--${{ github.event.pull_request.head.ref }}"
|
||||
commit-message: "update(kernel): extism-runtime.wasm in ${{ github.event.pull_request.head.ref }}"
|
||||
delete-branch: true
|
||||
26
.github/workflows/release-convert.yaml
vendored
Normal file
26
.github/workflows/release-convert.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Release extism-convert
|
||||
|
||||
jobs:
|
||||
release-convert:
|
||||
name: release-extism-convert
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Rust env
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Release Rust Convert Crate
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_TOKEN }}
|
||||
run: |
|
||||
cargo publish --manifest-path convert/Cargo.toml
|
||||
29
.github/workflows/release-dotnet-native.yaml
vendored
29
.github/workflows/release-dotnet-native.yaml
vendored
@@ -10,6 +10,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
fetch-tags: true
|
||||
|
||||
- name: Setup .NET Core SDK
|
||||
uses: actions/setup-dotnet@v3.0.3
|
||||
with:
|
||||
@@ -20,10 +22,25 @@ jobs:
|
||||
name: release-artifacts
|
||||
- name: Extract Archive
|
||||
run: |
|
||||
tar -xvzf libextism-x86_64-pc-windows-msvc-v*.tar.gz --directory dotnet/nuget/runtimes
|
||||
mv dotnet/nuget/runtimes/extism.dll dotnet/nuget/runtimes/win-x64.dll
|
||||
- name: Publish win-x64
|
||||
mkdir -p dotnet/nuget/runtimes/win-x64/native/
|
||||
tar -xvzf libextism-x86_64-pc-windows-msvc-main.tar.gz -C dotnet/nuget/runtimes/win-x64/native/
|
||||
|
||||
mkdir -p dotnet/nuget/runtimes/osx-arm64/native/
|
||||
tar -xvzf libextism-aarch64-apple-darwin-main.tar.gz -C dotnet/nuget/runtimes/osx-arm64/native/
|
||||
|
||||
mkdir -p dotnet/nuget/runtimes/osx-x64/native/
|
||||
tar -xvzf libextism-x86_64-apple-darwin-main.tar.gz -C dotnet/nuget/runtimes/osx-x64/native/
|
||||
|
||||
mkdir -p dotnet/nuget/runtimes/linux-x64/native/
|
||||
tar -xvzf libextism-x86_64-unknown-linux-gnu-main.tar.gz -C dotnet/nuget/runtimes/linux-x64/native/
|
||||
|
||||
mkdir -p dotnet/nuget/runtimes/linux-arm64/native/
|
||||
tar -xvzf libextism-aarch64-unknown-linux-gnu-main.tar.gz -C dotnet/nuget/runtimes/linux-arm64/native/
|
||||
|
||||
mkdir -p dotnet/nuget/runtimes/linux-musl-arm64/native/
|
||||
tar -xvzf libextism-aarch64-unknown-linux-musl-main.tar.gz -C dotnet/nuget/runtimes/linux-musl-arm64/native/
|
||||
|
||||
- name: Publish NuGet packages
|
||||
run: |
|
||||
cd dotnet/nuget
|
||||
dotnet pack -o dist
|
||||
dotnet nuget push --source https://api.nuget.org/v3/index.json ./dist/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }}
|
||||
dotnet pack .\dotnet\nuget\Nuget.sln -o release-artifacts
|
||||
dotnet nuget push --source https://api.nuget.org/v3/index.json ./release-artifacts/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }}
|
||||
7
.github/workflows/release-elixir.yaml
vendored
7
.github/workflows/release-elixir.yaml
vendored
@@ -10,6 +10,13 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Install extism shared library
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p /home/runner/.local/bin/
|
||||
export PATH="/home/runner/.local/bin/:$PATH"
|
||||
curl https://raw.githubusercontent.com/extism/cli/main/install.sh | sh
|
||||
extism --sudo --prefix /usr/local install
|
||||
- name: Setup Elixir Host SDK
|
||||
uses: erlef/setup-beam@v1
|
||||
with:
|
||||
|
||||
26
.github/workflows/release-manifest.yaml
vendored
Normal file
26
.github/workflows/release-manifest.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Release extism-manifest
|
||||
|
||||
jobs:
|
||||
release-manifest:
|
||||
name: release-extism-manifest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Rust env
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Release Rust Manifest Crate
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_TOKEN }}
|
||||
run: |
|
||||
cargo publish --manifest-path manifest/Cargo.toml
|
||||
49
.github/workflows/release-python.yaml
vendored
49
.github/workflows/release-python.yaml
vendored
@@ -1,8 +1,9 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Release Python SDK
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published, edited]
|
||||
|
||||
jobs:
|
||||
release-sdks:
|
||||
name: release-python
|
||||
@@ -11,26 +12,30 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python env
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.9"
|
||||
check-latest: true
|
||||
- name: Run image
|
||||
uses: abatilo/actions-poetry@v2
|
||||
- name: Install poetry
|
||||
run: pipx install poetry
|
||||
|
||||
- name: Build Python Host SDK
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: install twine
|
||||
run: |
|
||||
cd python
|
||||
cp ../LICENSE .
|
||||
make clean
|
||||
poetry install --no-dev
|
||||
poetry build
|
||||
pip install twine
|
||||
|
||||
- name: Release Python Host SDK
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: ${{ secrets.PYPI_API_USER }}
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
packages_dir: python/dist/
|
||||
- name: download release
|
||||
run: |
|
||||
tag='${{ github.ref }}'
|
||||
tag="${tag/refs\/tags\//}"
|
||||
mkdir dist
|
||||
cd dist
|
||||
gh release download "$tag" -p 'extism_sys-*'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: upload release
|
||||
run: |
|
||||
twine upload dist/*
|
||||
env:
|
||||
TWINE_USERNAME: ${{ secrets.PYPI_API_USER }}
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
||||
|
||||
25
.github/workflows/release-rust.yaml
vendored
25
.github/workflows/release-rust.yaml
vendored
@@ -1,16 +1,16 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: Release Rust SDK
|
||||
name: Release Runtime/Rust SDK
|
||||
|
||||
jobs:
|
||||
release-sdks:
|
||||
release-runtime:
|
||||
name: release-rust
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Setup Rust env
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
@@ -19,23 +19,8 @@ jobs:
|
||||
override: true
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Release Rust Manifest Crate
|
||||
if: always()
|
||||
- name: Release Runtime
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_TOKEN }}
|
||||
run: |
|
||||
# order of crate publication matter: manifest, runtime, rust
|
||||
cargo publish --manifest-path manifest/Cargo.toml
|
||||
# allow for crates.io to update so dependant crates can locate extism-manifest
|
||||
sleep 5
|
||||
|
||||
- name: Release Rust Host SDK
|
||||
if: always()
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_TOKEN }}
|
||||
run: |
|
||||
|
||||
cargo publish --manifest-path runtime/Cargo.toml --no-verify
|
||||
cargo publish --manifest-path rust/Cargo.toml
|
||||
|
||||
|
||||
cargo publish --manifest-path runtime/Cargo.toml
|
||||
|
||||
277
.github/workflows/release.yml
vendored
277
.github/workflows/release.yml
vendored
@@ -1,7 +1,9 @@
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ main ]
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
name: Release
|
||||
|
||||
@@ -11,20 +13,60 @@ env:
|
||||
RUSTFLAGS: -C target-feature=-crt-static
|
||||
ARTIFACT_DIR: release-artifacts
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release-linux:
|
||||
name: linux
|
||||
runs-on: ubuntu-latest
|
||||
release:
|
||||
name: ${{ matrix.os }} ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
[
|
||||
aarch64-unknown-linux-gnu,
|
||||
aarch64-unknown-linux-musl,
|
||||
x86_64-unknown-linux-gnu,
|
||||
]
|
||||
# i686-unknown-linux-gnu,
|
||||
if: always()
|
||||
include:
|
||||
- os: 'macos'
|
||||
target: 'x86_64-apple-darwin'
|
||||
artifact: 'libextism.dylib'
|
||||
static-artifact: 'libextism.a'
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'macos'
|
||||
target: 'aarch64-apple-darwin'
|
||||
artifact: 'libextism.dylib'
|
||||
static-artifact: 'libextism.a'
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'ubuntu'
|
||||
target: 'aarch64-unknown-linux-gnu'
|
||||
artifact: 'libextism.so'
|
||||
static-artifact: 'libextism.a'
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'ubuntu'
|
||||
target: 'aarch64-unknown-linux-musl'
|
||||
artifact: 'libextism.so'
|
||||
static-artifact: 'libextism.a'
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'ubuntu'
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
artifact: 'libextism.so'
|
||||
static-artifact: 'libextism.a'
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'windows'
|
||||
target: 'x86_64-pc-windows-gnu'
|
||||
artifact: 'extism.dll'
|
||||
static-artifact: 'libextism.a'
|
||||
pc-in: 'extism.pc.in'
|
||||
static-pc-in: 'extism-static.pc.in'
|
||||
- os: 'windows'
|
||||
target: 'x86_64-pc-windows-msvc'
|
||||
artifact: 'extism.dll'
|
||||
static-artifact: 'extism.lib'
|
||||
pc-in: ''
|
||||
static-pc-in: ''
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -37,148 +79,68 @@ jobs:
|
||||
override: true
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Build Target (${{ matrix.target }})
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
prefix-key: "${{matrix.os}}-${{matrix.target}}"
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
cache-on-failure: "true"
|
||||
|
||||
- name: Build Target (${{ matrix.os }} ${{ matrix.target }})
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
use-cross: ${{ matrix.os != 'windows' }}
|
||||
command: build
|
||||
args: --release --target ${{ matrix.target }} -p ${{ env.RUNTIME_CRATE }}
|
||||
|
||||
- name: Prepare Artifact
|
||||
- name: set extism-maturin version
|
||||
shell: bash
|
||||
run: |
|
||||
EXT=so
|
||||
SRC_DIR=target/${{ matrix.target }}/release
|
||||
DEST_DIR=${{ env.ARTIFACT_DIR }}
|
||||
RELEASE_NAME=libextism-${{ matrix.target }}-${{ github.ref_name }}
|
||||
ARCHIVE=${RELEASE_NAME}.tar.gz
|
||||
CHECKSUM=${RELEASE_NAME}.checksum.txt
|
||||
pyproject="$(cat extism-maturin/pyproject.toml)"
|
||||
version="${{ github.ref }}"
|
||||
if [[ "$version" = "refs/heads/main" ]]; then
|
||||
version="0.0.0-dev"
|
||||
else
|
||||
version="${version/refs\/tags\/v/}"
|
||||
fi
|
||||
|
||||
# compress the shared library & create checksum
|
||||
cp runtime/extism.h ${SRC_DIR}
|
||||
cp LICENSE ${SRC_DIR}
|
||||
tar -C ${SRC_DIR} -czvf ${ARCHIVE} libextism.${EXT} extism.h
|
||||
ls -ll ${ARCHIVE}
|
||||
shasum -a 256 ${ARCHIVE} > ${CHECKSUM}
|
||||
<<<"$pyproject" >extism-maturin/pyproject.toml sed -e 's/^version = "0.0.0.replaced-by-ci"/version = "'"$version"'"/g'
|
||||
|
||||
# copy archive and checksum into release artifact directory
|
||||
mkdir -p ${DEST_DIR}
|
||||
cp ${ARCHIVE} ${DEST_DIR}
|
||||
cp ${CHECKSUM} ${DEST_DIR}
|
||||
|
||||
ls ${DEST_DIR}
|
||||
|
||||
- name: Upload Artifact to Summary
|
||||
uses: actions/upload-artifact@v3
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_DIR }}
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.txt
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Upload Artifact to Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
# maturin's cffi integration struggles with gnu headers on windows.
|
||||
# there's partial work towards fixing this in `extism-maturin/build.rs`, but it's
|
||||
# not sufficient to get it to work. omit it for now!
|
||||
if: ${{ matrix.target != 'x86_64-pc-windows-gnu' && matrix.target != 'aarch64-unknown-linux-gnu' }}
|
||||
with:
|
||||
files: |
|
||||
*.tar.gz
|
||||
*.txt
|
||||
|
||||
release-macos:
|
||||
name: macos
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [x86_64-apple-darwin, aarch64-apple-darwin]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist --find-interpreter -m extism-maturin/Cargo.toml
|
||||
sccache: 'true'
|
||||
manylinux: auto
|
||||
|
||||
- name: Build Target (${{ matrix.target }})
|
||||
uses: actions-rs/cargo@v1
|
||||
- name: Build GNU Linux wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
# One of our deps, "ring", needs a newer sysroot than what "manylinux: auto" provides.
|
||||
if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
|
||||
with:
|
||||
use-cross: true
|
||||
command: build
|
||||
args: --release --target ${{ matrix.target }} -p ${{ env.RUNTIME_CRATE }}
|
||||
target: ${{ matrix.target }}
|
||||
args: --release --out dist --find-interpreter -m extism-maturin/Cargo.toml
|
||||
sccache: 'true'
|
||||
manylinux: 2_28
|
||||
|
||||
- name: Prepare Artifact
|
||||
- name: Add pkg-config files except on MSVC
|
||||
if: ${{ matrix.target != 'x86_64-pc-windows-msvc' }}
|
||||
shell: bash
|
||||
run: |
|
||||
EXT=dylib
|
||||
SRC_DIR=target/${{ matrix.target }}/release
|
||||
DEST_DIR=${{ env.ARTIFACT_DIR }}
|
||||
RELEASE_NAME=libextism-${{ matrix.target }}-${{ github.ref_name }}
|
||||
ARCHIVE=${RELEASE_NAME}.tar.gz
|
||||
CHECKSUM=${RELEASE_NAME}.checksum.txt
|
||||
|
||||
# compress the shared library & create checksum
|
||||
cp runtime/extism.h ${SRC_DIR}
|
||||
cp LICENSE ${SRC_DIR}
|
||||
tar -C ${SRC_DIR} -czvf ${ARCHIVE} libextism.${EXT} extism.h
|
||||
ls -ll ${ARCHIVE}
|
||||
shasum -a 256 ${ARCHIVE} > ${CHECKSUM}
|
||||
|
||||
# copy archive and checksum into release artifact directory
|
||||
mkdir -p ${DEST_DIR}
|
||||
cp ${ARCHIVE} ${DEST_DIR}
|
||||
cp ${CHECKSUM} ${DEST_DIR}
|
||||
|
||||
ls ${DEST_DIR}
|
||||
|
||||
- name: Upload Artifact to Summary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_DIR }}
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.txt
|
||||
|
||||
- name: Upload Artifact to Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
*.tar.gz
|
||||
*.txt
|
||||
|
||||
release-windows:
|
||||
name: windows
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
[x86_64-pc-windows-gnu, x86_64-pc-windows-msvc]
|
||||
# i686-pc-windows-gnu,
|
||||
# i686-pc-windows-msvc,
|
||||
# aarch64-pc-windows-msvc
|
||||
if: always()
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
override: true
|
||||
target: ${{ matrix.target }}
|
||||
|
||||
- name: Build Target (${{ matrix.target }})
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release --target ${{ matrix.target }} -p ${{ env.RUNTIME_CRATE }}
|
||||
cp libextism/extism*.pc.in ${SRC_DIR}
|
||||
|
||||
- name: Prepare Artifact
|
||||
shell: bash
|
||||
run: |
|
||||
EXT=dll
|
||||
SRC_DIR=target/${{ matrix.target }}/release
|
||||
DEST_DIR=${{ env.ARTIFACT_DIR }}
|
||||
RELEASE_NAME=libextism-${{ matrix.target }}-${{ github.ref_name }}
|
||||
@@ -188,29 +150,62 @@ jobs:
|
||||
# compress the shared library & create checksum
|
||||
cp runtime/extism.h ${SRC_DIR}
|
||||
cp LICENSE ${SRC_DIR}
|
||||
tar -C ${SRC_DIR} -czvf ${ARCHIVE} extism.${EXT} extism.h
|
||||
tar -C ${SRC_DIR} -czvf ${ARCHIVE} extism.h \
|
||||
${{ matrix.artifact }} ${{ matrix.static-artifact }} \
|
||||
${{ matrix.pc-in }} ${{ matrix.static-pc-in }}
|
||||
ls -ll ${ARCHIVE}
|
||||
|
||||
certutil -hashfile ${ARCHIVE} SHA256 >${CHECKSUM}
|
||||
if &>/dev/null which shasum; then
|
||||
shasum -a 256 ${ARCHIVE} > ${CHECKSUM}
|
||||
else
|
||||
# windows doesn't have shasum available, so we use certutil instead.
|
||||
certutil -hashfile ${ARCHIVE} SHA256 >${CHECKSUM}
|
||||
fi
|
||||
|
||||
# copy archive and checksum into release artifact directory
|
||||
mkdir -p ${DEST_DIR}
|
||||
cp ${ARCHIVE} ${DEST_DIR}
|
||||
cp ${CHECKSUM} ${DEST_DIR}
|
||||
|
||||
ls ${DEST_DIR}
|
||||
# copy any built wheels.
|
||||
if [ -e dist/*.whl ]; then
|
||||
cp dist/*.whl ${DEST_DIR}
|
||||
fi
|
||||
|
||||
ls -ll ${DEST_DIR}
|
||||
|
||||
- name: Upload Artifact to Summary
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_DIR }}
|
||||
path: |
|
||||
*.tar.gz
|
||||
*.txt
|
||||
path: ${{ env.ARTIFACT_DIR }}
|
||||
|
||||
- name: Upload Artifact to Release
|
||||
- name: Upload Artifact to Draft Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
draft: true
|
||||
files: |
|
||||
${{ env.ARTIFACT_DIR }}/*
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
|
||||
release-latest:
|
||||
name: create latest release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [release]
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ${{ env.ARTIFACT_DIR }}
|
||||
|
||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
automatic_release_tag: "latest"
|
||||
prerelease: true
|
||||
title: "Development Build"
|
||||
files: |
|
||||
*.tar.gz
|
||||
*.txt
|
||||
*.whl
|
||||
if: github.ref == 'refs/heads/main'
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -15,6 +15,8 @@ python/poetry.lock
|
||||
c/main
|
||||
cpp/test/test
|
||||
cpp/example
|
||||
.dub
|
||||
dub.selections.json
|
||||
go/main
|
||||
ruby/.bundle/
|
||||
ruby/.yardoc
|
||||
@@ -41,4 +43,7 @@ java/*.iml
|
||||
java/*.log
|
||||
java/.idea
|
||||
java/.DS_Store
|
||||
|
||||
extism-maturin/src/extism.h
|
||||
runtime/*.log
|
||||
libextism/example
|
||||
libextism/extism*.pc
|
||||
@@ -1 +1 @@
|
||||
version = 0.24.1
|
||||
version = 0.26.0
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"extism-maturin",
|
||||
"manifest",
|
||||
"runtime",
|
||||
"rust",
|
||||
"libextism",
|
||||
"convert"
|
||||
]
|
||||
exclude = ["kernel"]
|
||||
|
||||
14
Makefile
14
Makefile
@@ -1,5 +1,6 @@
|
||||
DEST?=/usr/local
|
||||
SOEXT=so
|
||||
AEXT=a
|
||||
FEATURES?=default
|
||||
DEFAULT_FEATURES?=yes
|
||||
|
||||
@@ -20,6 +21,11 @@ endif
|
||||
|
||||
build:
|
||||
cargo build --release $(FEATURE_FLAGS) --manifest-path libextism/Cargo.toml
|
||||
sed -e "s%PREFIX%$(DEST)%" libextism/extism.pc.in > libextism/extism.pc
|
||||
sed -e "s%PREFIX%$(DEST)%" libextism/extism-static.pc.in > libextism/extism-static.pc
|
||||
|
||||
bench:
|
||||
@(cargo criterion || echo 'For nicer output use cargo-criterion: `cargo install cargo-criterion` - using `cargo bench`') && cargo bench
|
||||
|
||||
.PHONY: kernel
|
||||
kernel:
|
||||
@@ -32,11 +38,15 @@ debug:
|
||||
RUSTFLAGS=-g $(MAKE) build
|
||||
|
||||
install:
|
||||
mkdir -p $(DEST)/lib $(DEST)/include
|
||||
mkdir -p $(DEST)/lib $(DEST)/include $(DEST)/lib/pkgconfig
|
||||
install runtime/extism.h $(DEST)/include/extism.h
|
||||
install target/release/libextism.$(SOEXT) $(DEST)/lib/libextism.$(SOEXT)
|
||||
install target/release/libextism.$(AEXT) $(DEST)/lib/libextism.$(AEXT)
|
||||
install libextism/extism.pc $(DEST)/lib/pkgconfig/extism.pc
|
||||
install libextism/extism-static.pc $(DEST)/lib/pkgconfig/extism-static.pc
|
||||
|
||||
uninstall:
|
||||
rm -f $(DEST)/include/extism.h $(DEST)/lib/libextism.$(SOEXT)
|
||||
rm -f $(DEST)/include/extism.h $(DEST)/lib/libextism.$(SOEXT) $(DEST)/lib/libextism.$(AEXT) \
|
||||
$(DEST)/lib/pkgconfig/extism*.pc
|
||||
|
||||
|
||||
|
||||
31
README.md
31
README.md
@@ -1,6 +1,6 @@
|
||||
### _Welcome!_
|
||||
|
||||
**Please note:** This project still under active development and APIs may change until we hit v1.0.
|
||||
**Please note:** This project still under active development and APIs are changing as we hit 1.0. Currently, the main branch has many breaking changes. Our current release is 0.5.x. Until we release 1.0, we are cutting these releases from the [stable](https://github.com/extism/extism/tree/stable) branch.
|
||||
|
||||
If you're interested in working on or building with Extism, please join our [Discord](https://discord.gg/cx3usBCWnc) and let us know - we are happy to help get you started.
|
||||
|
||||
@@ -8,19 +8,24 @@ If you're interested in working on or building with Extism, please join our [Dis
|
||||
|
||||
# [Extism](https://extism.org)
|
||||
|
||||
The universal plug-in system. Run WebAssembly extensions inside your app. Use idiomatic Host SDKs for [Go](https://extism.org/docs/integrate-into-your-codebase/go-host-sdk),
|
||||
[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),
|
||||
[Haskell](https://extism.org/docs/integrate-into-your-codebase/haskell-host-sdk),
|
||||
[PHP](https://extism.org/docs/integrate-into-your-codebase/php-host-sdk),
|
||||
[Elixir/Erlang](https://extism.org/docs/integrate-into-your-codebase/elixir-or-erlang-host-sdk),
|
||||
[.NET](https://extism.org/docs/integrate-into-your-codebase/dotnet-host-sdk),
|
||||
[Java](https://extism.org/docs/integrate-into-your-codebase/java-host-sdk),
|
||||
[Zig](https://extism.org/docs/integrate-into-your-codebase/zig-host-sdk) & more (others coming soon).
|
||||
The universal plug-in system. Run WebAssembly extensions inside your app. Use idiomatic Host SDKs for [Go](https://github.com/extism/go-sdk#readme),
|
||||
[Ruby](https://github.com/extism/ruby-sdk#readme),
|
||||
[Python](https://github.com/extism/python-sdk#readme),
|
||||
[JavaScript](https://github.com/extism/js-sdk#readme),
|
||||
[Rust](/runtime/#readme),
|
||||
[C](libextism/#readme),
|
||||
[C++](https://github.com/extism/cpp-sdk/#readme),
|
||||
[OCaml](https://github.com/extism/ocaml-sdk#readme),
|
||||
[Haskell](https://github.com/extism/haskell-sdk#readme),
|
||||
[PHP](https://github.com/extism/php-sdk#readme),
|
||||
[Elixir](https://github.com/extism/elixir-sdk#readme),
|
||||
[.NET](https://github.com/extism/dotnet-sdk#readme),
|
||||
[Java](https://github.com/extism/java-sdk#readme),
|
||||
[Zig](https://github.com/extism/zig-sdk#readme),
|
||||
[D](https://github.com/extism/d-sdk#readme),
|
||||
& more (others coming soon).
|
||||
|
||||
Plug-in development kits (PDK) for plug-in authors supported in [Rust](https://github.com/extism/rust-pdk), [AssemblyScript](https://github.com/extism/assemblyscript-pdk), [Go](https://github.com/extism/go-pdk), [C/C++](https://github.com/extism/c-pdk), [Haskell](https://github.com/extism/haskell-pdk), and [Zig](https://github.com/extism/zig-pdk).
|
||||
Plug-in development kits (PDK) for plug-in authors supported in [Rust](https://github.com/extism/rust-pdk#readme), [AssemblyScript](https://github.com/extism/assemblyscript-pdk#readme), [Go](https://github.com/extism/go-pdk#readme), [C/C++](https://github.com/extism/c-pdk#readme), [Haskell](https://github.com/extism/haskell-pdk#readme), [JavaScript](https://github.com/extism/js-pdk#readme), [C#](https://github.com/extism/dotnet-pdk#readme), [F#](https://github.com/extism/dotnet-pdk#readme) and [Zig](https://github.com/extism/zig-pdk#readme).
|
||||
|
||||
<p align="center">
|
||||
<img style="width: 70%;" src="https://user-images.githubusercontent.com/7517515/210286900-39b144fd-1b26-4dd0-b7a9-2b5755bc174d.png" alt="Extism embedded SDK language support"/>
|
||||
|
||||
4
browser/README.md
Normal file
4
browser/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Browser Host SDK
|
||||
|
||||
This contains the `0.x` version of the SDK. This library is deprecated and all new integrations should use the new [Universal JavaScript library](https://github.com/extism/js-sdk#readme).
|
||||
|
||||
Binary file not shown.
248
browser/package-lock.json
generated
248
browser/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@extism/runtime-browser",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@extism/runtime-browser",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@bjorn3/browser_wasi_shim": "^0.2.7"
|
||||
@@ -39,12 +39,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
||||
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/highlight": "^7.18.6"
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -99,13 +100,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/generator": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz",
|
||||
"integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.20.7",
|
||||
"@babel/types": "^7.23.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -154,34 +156,34 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.18.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
|
||||
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-function-name": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
|
||||
"integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.18.10",
|
||||
"@babel/types": "^7.19.0"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-hoist-variables": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
|
||||
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
||||
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.18.6"
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -240,30 +242,30 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
|
||||
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.18.6"
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.19.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
|
||||
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
|
||||
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
|
||||
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -293,13 +295,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/highlight": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
|
||||
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.18.6",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -307,9 +309,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/parser": {
|
||||
"version": "7.20.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz",
|
||||
"integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"parser": "bin/babel-parser.js"
|
||||
@@ -513,33 +515,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
||||
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/parser": "^7.20.7",
|
||||
"@babel/types": "^7.20.7"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/traverse": {
|
||||
"version": "7.20.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz",
|
||||
"integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/generator": "^7.20.7",
|
||||
"@babel/helper-environment-visitor": "^7.18.9",
|
||||
"@babel/helper-function-name": "^7.19.0",
|
||||
"@babel/helper-hoist-variables": "^7.18.6",
|
||||
"@babel/helper-split-export-declaration": "^7.18.6",
|
||||
"@babel/parser": "^7.20.13",
|
||||
"@babel/types": "^7.20.7",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
},
|
||||
@@ -548,13 +550,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/types": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
|
||||
"integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.19.4",
|
||||
"@babel/helper-validator-identifier": "^7.19.1",
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -8288,12 +8290,13 @@
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
||||
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
|
||||
"version": "7.22.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
|
||||
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.18.6"
|
||||
"@babel/highlight": "^7.22.13",
|
||||
"chalk": "^2.4.2"
|
||||
}
|
||||
},
|
||||
"@babel/compat-data": {
|
||||
@@ -8334,13 +8337,14 @@
|
||||
}
|
||||
},
|
||||
"@babel/generator": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.7.tgz",
|
||||
"integrity": "sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
|
||||
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.20.7",
|
||||
"@babel/types": "^7.23.0",
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"@jridgewell/trace-mapping": "^0.3.17",
|
||||
"jsesc": "^2.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -8378,28 +8382,28 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.18.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
|
||||
"integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
"integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-function-name": {
|
||||
"version": "7.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz",
|
||||
"integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
|
||||
"integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/template": "^7.18.10",
|
||||
"@babel/types": "^7.19.0"
|
||||
"@babel/template": "^7.22.15",
|
||||
"@babel/types": "^7.23.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-hoist-variables": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
|
||||
"integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
|
||||
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.18.6"
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-imports": {
|
||||
@@ -8443,24 +8447,24 @@
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
|
||||
"integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.18.6"
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-string-parser": {
|
||||
"version": "7.19.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
|
||||
"integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
|
||||
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-identifier": {
|
||||
"version": "7.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
|
||||
"integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
|
||||
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-validator-option": {
|
||||
@@ -8481,20 +8485,20 @@
|
||||
}
|
||||
},
|
||||
"@babel/highlight": {
|
||||
"version": "7.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
|
||||
"integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
|
||||
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.18.6",
|
||||
"chalk": "^2.0.0",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"chalk": "^2.4.2",
|
||||
"js-tokens": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/parser": {
|
||||
"version": "7.20.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.13.tgz",
|
||||
"integrity": "sha512-gFDLKMfpiXCsjt4za2JA9oTMn70CeseCehb11kRZgvd7+F67Hih3OHOK24cRrWECJ/ljfPGac6ygXAs/C8kIvw==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
|
||||
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/plugin-syntax-async-generators": {
|
||||
@@ -8635,42 +8639,42 @@
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
||||
"integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==",
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
"integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/parser": "^7.20.7",
|
||||
"@babel/types": "^7.20.7"
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/parser": "^7.22.15",
|
||||
"@babel/types": "^7.22.15"
|
||||
}
|
||||
},
|
||||
"@babel/traverse": {
|
||||
"version": "7.20.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.13.tgz",
|
||||
"integrity": "sha512-kMJXfF0T6DIS9E8cgdLCSAL+cuCK+YEZHWiLK0SXpTo8YRj5lpJu3CDNKiIBCne4m9hhTIqUg6SYTAI39tAiVQ==",
|
||||
"version": "7.23.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz",
|
||||
"integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.18.6",
|
||||
"@babel/generator": "^7.20.7",
|
||||
"@babel/helper-environment-visitor": "^7.18.9",
|
||||
"@babel/helper-function-name": "^7.19.0",
|
||||
"@babel/helper-hoist-variables": "^7.18.6",
|
||||
"@babel/helper-split-export-declaration": "^7.18.6",
|
||||
"@babel/parser": "^7.20.13",
|
||||
"@babel/types": "^7.20.7",
|
||||
"@babel/code-frame": "^7.22.13",
|
||||
"@babel/generator": "^7.23.0",
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-function-name": "^7.23.0",
|
||||
"@babel/helper-hoist-variables": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"@babel/parser": "^7.23.0",
|
||||
"@babel/types": "^7.23.0",
|
||||
"debug": "^4.1.0",
|
||||
"globals": "^11.1.0"
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.7.tgz",
|
||||
"integrity": "sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==",
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
|
||||
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-string-parser": "^7.19.4",
|
||||
"@babel/helper-validator-identifier": "^7.19.1",
|
||||
"@babel/helper-string-parser": "^7.22.5",
|
||||
"@babel/helper-validator-identifier": "^7.22.20",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@extism/runtime-browser",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"description": "Extism runtime in the browser",
|
||||
"scripts": {
|
||||
"build": "node build.js && tsc --emitDeclarationOnly --outDir dist",
|
||||
@@ -36,4 +36,4 @@
|
||||
"dependencies": {
|
||||
"@bjorn3/browser_wasi_shim": "^0.2.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { ExtismPlugin } from './plugin';
|
||||
* Can be a {@link Manifest} or just the raw bytes of the WASM module as an ArrayBuffer.
|
||||
* We recommend using {@link Manifest}
|
||||
*/
|
||||
type ManifestData = Manifest | ArrayBuffer;
|
||||
type ManifestData = Manifest | ArrayBuffer | WebAssembly.Module;
|
||||
|
||||
/**
|
||||
* A Context is needed to create plugins. The Context
|
||||
@@ -21,8 +21,8 @@ export default class ExtismContext {
|
||||
* @returns A new Plugin scoped to this Context
|
||||
*/
|
||||
async newPlugin(manifest: ManifestData, functions: Record<string, any> = {}, config?: PluginConfig) {
|
||||
let moduleData: ArrayBuffer | null = null;
|
||||
if (manifest instanceof ArrayBuffer) {
|
||||
let moduleData: ArrayBuffer | WebAssembly.Module | null = null;
|
||||
if (manifest instanceof ArrayBuffer || manifest instanceof WebAssembly.Module) {
|
||||
moduleData = manifest;
|
||||
} else if ((manifest as Manifest).wasm) {
|
||||
const wasmData = (manifest as Manifest).wasm;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { WASI, Fd } from '@bjorn3/browser_wasi_shim';
|
||||
export type ExtismFunction = any;
|
||||
|
||||
export class ExtismPlugin {
|
||||
moduleData: ArrayBuffer;
|
||||
moduleData: ArrayBuffer | WebAssembly.Module;
|
||||
allocator: Allocator;
|
||||
config?: PluginConfig;
|
||||
vars: Record<string, Uint8Array>;
|
||||
@@ -14,7 +14,7 @@ export class ExtismPlugin {
|
||||
module?: WebAssembly.WebAssemblyInstantiatedSource;
|
||||
functions: Record<string, ExtismFunction>;
|
||||
|
||||
constructor(moduleData: ArrayBuffer, functions: Record<string, ExtismFunction> = {}, config?: PluginConfig) {
|
||||
constructor(moduleData: ArrayBuffer | WebAssembly.Module, functions: Record<string, ExtismFunction> = {}, config?: PluginConfig) {
|
||||
this.moduleData = moduleData;
|
||||
this.allocator = new Allocator(1024 * 1024);
|
||||
this.config = config;
|
||||
@@ -78,11 +78,25 @@ export class ExtismPlugin {
|
||||
wasi_snapshot_preview1: wasi.wasiImport,
|
||||
env: environment,
|
||||
};
|
||||
this.module = await WebAssembly.instantiate(this.moduleData, env);
|
||||
|
||||
if (this.moduleData instanceof WebAssembly.Module) {
|
||||
const instance = new WebAssembly.Instance(this.moduleData, env);
|
||||
this.module = {
|
||||
instance,
|
||||
module: this.moduleData
|
||||
}
|
||||
//@ts-ignore
|
||||
wasi.inst = instance;
|
||||
}
|
||||
else {
|
||||
this.module = await WebAssembly.instantiate(this.moduleData, env);
|
||||
//@ts-ignore
|
||||
wasi.inst = this.module.instance;
|
||||
}
|
||||
if (!this.module) throw Error("Unable to instantiate module");
|
||||
|
||||
// normally we would call wasi.start here but it doesn't respect when there is
|
||||
// no _start function
|
||||
//@ts-ignore
|
||||
wasi.inst = this.module.instance;
|
||||
if (this.module.instance.exports._start) {
|
||||
//@ts-ignore
|
||||
this.module.instance.exports._start();
|
||||
|
||||
24
composer.lock
generated
24
composer.lock
generated
@@ -12,12 +12,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ircmaxell/FFIMe.git",
|
||||
"reference": "5f648f95ecf23262a2e58f4e4c9001bd1b5f9c98"
|
||||
"reference": "431a3c13d9906b974d50b13bf8295097ea000c5e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ircmaxell/FFIMe/zipball/5f648f95ecf23262a2e58f4e4c9001bd1b5f9c98",
|
||||
"reference": "5f648f95ecf23262a2e58f4e4c9001bd1b5f9c98",
|
||||
"url": "https://api.github.com/repos/ircmaxell/FFIMe/zipball/431a3c13d9906b974d50b13bf8295097ea000c5e",
|
||||
"reference": "431a3c13d9906b974d50b13bf8295097ea000c5e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -50,7 +50,7 @@
|
||||
"issues": "https://github.com/ircmaxell/FFIMe/issues",
|
||||
"source": "https://github.com/ircmaxell/FFIMe/tree/master"
|
||||
},
|
||||
"time": "2022-09-01T18:56:19+00:00"
|
||||
"time": "2023-04-03T00:43:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ircmaxell/php-c-parser",
|
||||
@@ -58,12 +58,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ircmaxell/php-c-parser.git",
|
||||
"reference": "55e0a4fdf88d6e955d928860e1e107a68492c1cf"
|
||||
"reference": "29e0223704e4ee00c66f43506f5f52db151b3517"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ircmaxell/php-c-parser/zipball/55e0a4fdf88d6e955d928860e1e107a68492c1cf",
|
||||
"reference": "55e0a4fdf88d6e955d928860e1e107a68492c1cf",
|
||||
"url": "https://api.github.com/repos/ircmaxell/php-c-parser/zipball/29e0223704e4ee00c66f43506f5f52db151b3517",
|
||||
"reference": "29e0223704e4ee00c66f43506f5f52db151b3517",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -99,7 +99,7 @@
|
||||
"issues": "https://github.com/ircmaxell/php-c-parser/issues",
|
||||
"source": "https://github.com/ircmaxell/php-c-parser/tree/master"
|
||||
},
|
||||
"time": "2022-08-27T17:37:14+00:00"
|
||||
"time": "2023-03-23T10:58:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ircmaxell/php-object-symbolresolver",
|
||||
@@ -107,12 +107,12 @@
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ircmaxell/php-object-symbolresolver.git",
|
||||
"reference": "3734df2b22d7c8273ee6f6f2155fddde6056d057"
|
||||
"reference": "dfe1b1aa6c15b198bdef50fff8485e98e89f2a09"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ircmaxell/php-object-symbolresolver/zipball/3734df2b22d7c8273ee6f6f2155fddde6056d057",
|
||||
"reference": "3734df2b22d7c8273ee6f6f2155fddde6056d057",
|
||||
"url": "https://api.github.com/repos/ircmaxell/php-object-symbolresolver/zipball/dfe1b1aa6c15b198bdef50fff8485e98e89f2a09",
|
||||
"reference": "dfe1b1aa6c15b198bdef50fff8485e98e89f2a09",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -144,7 +144,7 @@
|
||||
"issues": "https://github.com/ircmaxell/php-object-symbolresolver/issues",
|
||||
"source": "https://github.com/ircmaxell/php-object-symbolresolver/tree/master"
|
||||
},
|
||||
"time": "2022-08-14T19:30:20+00:00"
|
||||
"time": "2022-09-15T18:21:50+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
||||
28
convert/Cargo.toml
Normal file
28
convert/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "extism-convert"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
authors = ["The Extism Authors", "oss@extism.org"]
|
||||
license = "BSD-3-Clause"
|
||||
readme = "./README.md"
|
||||
homepage = "https://extism.org"
|
||||
repository = "https://github.com/extism/extism"
|
||||
description = "Traits to make Rust types usable with Extism"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
base64 = "0.21.3"
|
||||
prost = { version = "0.12.0", optional = true }
|
||||
protobuf = { version = "3.3.0", optional = true }
|
||||
rmp-serde = { version = "1.1.2", optional = true }
|
||||
serde = "1.0.186"
|
||||
serde_json = "1.0.105"
|
||||
|
||||
[dev-dependencies]
|
||||
serde = { version = "1.0.186", features = ["derive"] }
|
||||
|
||||
[features]
|
||||
default = ["msgpack", "prost"]
|
||||
msgpack = ["rmp-serde"]
|
||||
prost = ["prost"]
|
||||
protobuf = ["protobuf"]
|
||||
12
convert/README.md
Normal file
12
convert/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# extism-convert
|
||||
|
||||
The [extism-convert](https://crates.io/crates/extism-convert) crate is used by the [Rust SDK](https://crates.io/crates/extism) and [Rust PDK](https://crates.io/crates/extism-pdk) to provide a shared interface for
|
||||
encoding and decoding values that can be passed to Extism function calls.
|
||||
|
||||
A set of types (Json, Msgpack, Protobuf) that can be used to specify a serde encoding are also provided. These are
|
||||
similar to [axum extractors](https://docs.rs/axum/latest/axum/extract/index.html#intro) - they are
|
||||
implemented as a tuple struct with a single field that is meant to be extracted using pattern matching.
|
||||
|
||||
## Documentation
|
||||
|
||||
See [extism-convert on docs.rs](https://docs.rs/extism-convert/latest/extism_convert/) for in-depth documentation.
|
||||
140
convert/src/encoding.rs
Normal file
140
convert/src/encoding.rs
Normal file
@@ -0,0 +1,140 @@
|
||||
use crate::*;
|
||||
|
||||
use base64::Engine;
|
||||
|
||||
/// The `encoding` macro can be used to create newtypes that implement a particular encoding for the
|
||||
/// inner value.
|
||||
///
|
||||
/// For example, the following line creates a new JSON encoding using serde_json:
|
||||
///
|
||||
/// ```
|
||||
/// extism_convert::encoding!(MyJson, serde_json::to_vec, serde_json::from_slice);
|
||||
/// ```
|
||||
///
|
||||
/// This will create a struct `struct MyJson<T>(pub T)` and implement `ToBytes` using `serde_json::to_vec`
|
||||
/// and `FromBytesOwned` using `serde_json::from_vec`
|
||||
#[macro_export]
|
||||
macro_rules! encoding {
|
||||
($name:ident, $to_vec:expr, $from_slice:expr) => {
|
||||
#[doc = concat!(stringify!($name), " encoding")]
|
||||
pub struct $name<T>(pub T);
|
||||
|
||||
impl<T> $name<T> {
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: serde::de::DeserializeOwned> $crate::FromBytesOwned for $name<T> {
|
||||
fn from_bytes_owned(data: &[u8]) -> std::result::Result<Self, $crate::Error> {
|
||||
let x = $from_slice(data)?;
|
||||
std::result::Result::Ok($name(x))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: serde::Serialize> $crate::ToBytes<'a> for $name<T> {
|
||||
type Bytes = Vec<u8>;
|
||||
|
||||
fn to_bytes(&self) -> std::result::Result<Self::Bytes, $crate::Error> {
|
||||
let enc = $to_vec(&self.0)?;
|
||||
std::result::Result::Ok(enc)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
encoding!(Json, serde_json::to_vec, serde_json::from_slice);
|
||||
|
||||
#[cfg(feature = "msgpack")]
|
||||
encoding!(Msgpack, rmp_serde::to_vec, rmp_serde::from_slice);
|
||||
|
||||
impl<'a> ToBytes<'a> for serde_json::Value {
|
||||
type Bytes = Vec<u8>;
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(serde_json::to_vec(self)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for serde_json::Value {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(serde_json::from_slice(data)?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Base64 conversion
|
||||
///
|
||||
/// When using `Base64` with `ToBytes` any type that implement `AsRef<[T]>` may be used as the inner value,
|
||||
/// but only `Base64<String>` and `Base64<Vec>` may be used with `FromBytes`
|
||||
///
|
||||
/// A value wrapped in `Base64` will automatically be encoded/decoded using base64, the inner value should not
|
||||
/// already be base64 encoded.
|
||||
pub struct Base64<T: AsRef<[u8]>>(pub T);
|
||||
|
||||
impl<'a, T: AsRef<[u8]>> ToBytes<'a> for Base64<T> {
|
||||
type Bytes = String;
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(base64::engine::general_purpose::STANDARD.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for Base64<Vec<u8>> {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Base64(
|
||||
base64::engine::general_purpose::STANDARD.decode(data)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for Base64<String> {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Base64(String::from_utf8(
|
||||
base64::engine::general_purpose::STANDARD.decode(data)?,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Protobuf encoding
|
||||
///
|
||||
/// Allows for `prost` Protobuf messages to be used as arguments to Extism plugin calls
|
||||
#[cfg(feature = "prost")]
|
||||
pub struct Protobuf<T: prost::Message>(pub T);
|
||||
|
||||
#[cfg(feature = "prost")]
|
||||
impl<'a, T: prost::Message> ToBytes<'a> for Protobuf<T> {
|
||||
type Bytes = Vec<u8>;
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.0.encode_to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "prost")]
|
||||
impl<T: Default + prost::Message> FromBytesOwned for Protobuf<T> {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Protobuf(T::decode(data)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Protobuf encoding
|
||||
///
|
||||
/// Allows for `protobuf` Protobuf messages to be used as arguments to Extism plugin calls
|
||||
#[cfg(feature = "protobuf")]
|
||||
pub struct Protobuf<T: protobuf::Message>(pub T);
|
||||
|
||||
#[cfg(feature = "protobuf")]
|
||||
impl<'a, T: protobuf::Message> ToBytes<'a> for Protobuf<T> {
|
||||
type Bytes = Vec<u8>;
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.0.write_to_bytes()?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "protobuf")]
|
||||
impl<'a, T: Default + protobuf::Message> FromBytesOwned for Protobuf<T> {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Protobuf(T::parse_from_bytes(data)?))
|
||||
}
|
||||
}
|
||||
100
convert/src/from_bytes.rs
Normal file
100
convert/src/from_bytes.rs
Normal file
@@ -0,0 +1,100 @@
|
||||
use crate::*;
|
||||
|
||||
/// `FromBytes` is used to define how a type should be decoded when working with
|
||||
/// Extism memory. It is used for plugin output and host function input.
|
||||
pub trait FromBytes<'a>: Sized {
|
||||
/// Decode a value from a slice of bytes
|
||||
fn from_bytes(data: &'a [u8]) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
/// `FromBytesOwned` is similar to `FromBytes` but it doesn't borrow from the input slice.
|
||||
/// `FromBytes` is automatically implemented for all types that implement `FromBytesOwned`
|
||||
pub trait FromBytesOwned: Sized {
|
||||
/// Decode a value from a slice of bytes, the resulting value should not borrow the input
|
||||
/// data.
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
impl<'a> FromBytes<'a> for &'a [u8] {
|
||||
fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromBytes<'a> for &'a str {
|
||||
fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
|
||||
Ok(std::str::from_utf8(data)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: FromBytesOwned> FromBytes<'a> for T {
|
||||
fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
|
||||
T::from_bytes_owned(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for Box<[u8]> {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(data.to_vec().into_boxed_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for Vec<u8> {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(data.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for String {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(std::str::from_utf8(data)?.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for f64 {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self::from_le_bytes(data.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for f32 {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self::from_le_bytes(data.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for i64 {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self::from_le_bytes(data.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for i32 {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self::from_le_bytes(data.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for u64 {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self::from_le_bytes(data.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for u32 {
|
||||
fn from_bytes_owned(data: &[u8]) -> Result<Self, Error> {
|
||||
Ok(Self::from_le_bytes(data.try_into()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBytesOwned for () {
|
||||
fn from_bytes_owned(_: &[u8]) -> Result<Self, Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: FromBytes<'a>> FromBytes<'a> for std::io::Cursor<T> {
|
||||
fn from_bytes(data: &'a [u8]) -> Result<Self, Error> {
|
||||
Ok(std::io::Cursor::new(T::from_bytes(data)?))
|
||||
}
|
||||
}
|
||||
29
convert/src/lib.rs
Normal file
29
convert/src/lib.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
//! The [extism-convert](https://crates.io/crates/extism-convert) crate is used by the [Rust SDK](https://crates.io/crates/extism) and [Rust PDK](https://crates.io/crates/extism-pdk) to provide a shared interface for
|
||||
//! encoding and decoding values that can be passed to Extism function calls.
|
||||
//!
|
||||
//! A set of types (Json, Msgpack) that can be used to specify a serde encoding are also provided. These are
|
||||
//! similar to [axum extractors](https://docs.rs/axum/latest/axum/extract/index.html#intro) - they are
|
||||
//! implemented as a tuple struct with a single field that is meant to be extracted using pattern matching.
|
||||
|
||||
pub use anyhow::Error;
|
||||
|
||||
mod encoding;
|
||||
|
||||
mod from_bytes;
|
||||
mod memory_handle;
|
||||
mod to_bytes;
|
||||
|
||||
pub use encoding::{Base64, Json};
|
||||
|
||||
#[cfg(feature = "msgpack")]
|
||||
pub use encoding::Msgpack;
|
||||
|
||||
#[cfg(feature = "protobuf")]
|
||||
pub use encoding::Protobuf;
|
||||
|
||||
pub use from_bytes::{FromBytes, FromBytesOwned};
|
||||
pub use memory_handle::MemoryHandle;
|
||||
pub use to_bytes::ToBytes;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
42
convert/src/memory_handle.rs
Normal file
42
convert/src/memory_handle.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
/// `MemoryHandle` describes where in memory a block of data is stored
|
||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub struct MemoryHandle {
|
||||
/// The offset of the region in Extism linear memory
|
||||
pub offset: u64,
|
||||
|
||||
/// The length of the memory region
|
||||
pub length: u64,
|
||||
}
|
||||
|
||||
impl MemoryHandle {
|
||||
/// Create a new `MemoryHandle` from an offset in memory and length
|
||||
///
|
||||
/// # Safety
|
||||
/// This function is unsafe because the specified memory region may not be valid.
|
||||
pub unsafe fn new(offset: u64, length: u64) -> MemoryHandle {
|
||||
MemoryHandle { offset, length }
|
||||
}
|
||||
|
||||
/// `NULL` equivalent
|
||||
pub fn null() -> MemoryHandle {
|
||||
MemoryHandle {
|
||||
offset: 0,
|
||||
length: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the offset of a memory handle
|
||||
pub fn offset(&self) -> u64 {
|
||||
self.offset
|
||||
}
|
||||
|
||||
/// Get the length of the memory region
|
||||
pub fn len(&self) -> usize {
|
||||
self.length as usize
|
||||
}
|
||||
|
||||
/// Returns `true` when the length is 0
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.length == 0
|
||||
}
|
||||
}
|
||||
39
convert/src/tests.rs
Normal file
39
convert/src/tests.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use crate::*;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
|
||||
struct Testing {
|
||||
a: String,
|
||||
b: i64,
|
||||
c: f32,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_json() {
|
||||
let x = Testing {
|
||||
a: "foobar".to_string(),
|
||||
b: 123,
|
||||
c: 456.7,
|
||||
};
|
||||
let bytes = Json(&x).to_bytes().unwrap();
|
||||
let Json(y): Json<Testing> = FromBytes::from_bytes(&bytes).unwrap();
|
||||
assert_eq!(x, y);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_msgpack() {
|
||||
let x = Testing {
|
||||
a: "foobar".to_string(),
|
||||
b: 123,
|
||||
c: 456.7,
|
||||
};
|
||||
let bytes = Msgpack(&x).to_bytes().unwrap();
|
||||
let Msgpack(y): Msgpack<Testing> = FromBytes::from_bytes(&bytes).unwrap();
|
||||
assert_eq!(x, y);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn roundtrip_base64() {
|
||||
let bytes = Base64("this is a test").to_bytes().unwrap();
|
||||
let Base64(s): Base64<String> = FromBytes::from_bytes(bytes.as_bytes()).unwrap();
|
||||
assert_eq!(s, "this is a test");
|
||||
}
|
||||
102
convert/src/to_bytes.rs
Normal file
102
convert/src/to_bytes.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use crate::*;
|
||||
|
||||
/// `ToBytes` is used to define how a type should be encoded when working with
|
||||
/// Extism memory. It is used for plugin input and host function output.
|
||||
pub trait ToBytes<'a> {
|
||||
/// A configurable byte slice representation, allows any type that implements `AsRef<[u8]>`
|
||||
type Bytes: AsRef<[u8]>;
|
||||
|
||||
/// `to_bytes` converts a value into `Self::Bytes`
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error>;
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for () {
|
||||
type Bytes = [u8; 0];
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok([])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for Vec<u8> {
|
||||
type Bytes = Vec<u8>;
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for String {
|
||||
type Bytes = String;
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for &'a [u8] {
|
||||
type Bytes = &'a [u8];
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for &'a str {
|
||||
type Bytes = &'a str;
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for f64 {
|
||||
type Bytes = [u8; 8];
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for f32 {
|
||||
type Bytes = [u8; 4];
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for i64 {
|
||||
type Bytes = [u8; 8];
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for i32 {
|
||||
type Bytes = [u8; 4];
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for u64 {
|
||||
type Bytes = [u8; 8];
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToBytes<'a> for u32 {
|
||||
type Bytes = [u8; 4];
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
Ok(self.to_le_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ToBytes<'a>> ToBytes<'a> for &'a T {
|
||||
type Bytes = T::Bytes;
|
||||
|
||||
fn to_bytes(&self) -> Result<Self::Bytes, Error> {
|
||||
<T as ToBytes>::to_bytes(self)
|
||||
}
|
||||
}
|
||||
183
cpp/extism.hpp
183
cpp/extism.hpp
@@ -221,6 +221,7 @@ public:
|
||||
typedef ExtismValType ValType;
|
||||
typedef ExtismValUnion ValUnion;
|
||||
typedef ExtismVal Val;
|
||||
typedef uint64_t MemoryHandle;
|
||||
|
||||
class CurrentPlugin {
|
||||
ExtismCurrentPlugin *pointer;
|
||||
@@ -229,16 +230,18 @@ public:
|
||||
CurrentPlugin(ExtismCurrentPlugin *p) : pointer(p) {}
|
||||
|
||||
uint8_t *memory() { return extism_current_plugin_memory(this->pointer); }
|
||||
ExtismSize memory_length(uint64_t offs) {
|
||||
uint8_t *memory(MemoryHandle offs) { return this->memory() + offs; }
|
||||
|
||||
ExtismSize memoryLength(MemoryHandle offs) {
|
||||
return extism_current_plugin_memory_length(this->pointer, offs);
|
||||
}
|
||||
|
||||
uint64_t alloc(ExtismSize size) {
|
||||
MemoryHandle alloc(ExtismSize size) {
|
||||
return extism_current_plugin_memory_alloc(this->pointer, size);
|
||||
}
|
||||
|
||||
void free(uint64_t offs) {
|
||||
extism_current_plugin_memory_free(this->pointer, offs);
|
||||
void free(MemoryHandle handle) {
|
||||
extism_current_plugin_memory_free(this->pointer, handle);
|
||||
}
|
||||
|
||||
void returnString(Val &output, const std::string &s) {
|
||||
@@ -256,7 +259,7 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
if (length != nullptr) {
|
||||
*length = this->memory_length(inp.v.i64);
|
||||
*length = this->memoryLength(inp.v.i64);
|
||||
}
|
||||
return this->memory() + inp.v.i64;
|
||||
}
|
||||
@@ -318,7 +321,7 @@ public:
|
||||
this->func = std::shared_ptr<ExtismFunction>(ptr, extism_function_free);
|
||||
}
|
||||
|
||||
void set_namespace(std::string s) {
|
||||
void setNamespace(std::string s) {
|
||||
extism_function_set_namespace(this->func.get(), s.c_str());
|
||||
}
|
||||
|
||||
@@ -336,111 +339,51 @@ public:
|
||||
};
|
||||
|
||||
class Plugin {
|
||||
std::shared_ptr<ExtismContext> context;
|
||||
ExtismPlugin plugin;
|
||||
std::vector<Function> functions;
|
||||
|
||||
public:
|
||||
ExtismPlugin *plugin;
|
||||
// Create a new plugin
|
||||
Plugin(const uint8_t *wasm, ExtismSize length, bool with_wasi = false,
|
||||
std::vector<Function> functions = std::vector<Function>(),
|
||||
std::shared_ptr<ExtismContext> ctx = std::shared_ptr<ExtismContext>(
|
||||
extism_context_new(), extism_context_free))
|
||||
std::vector<Function> functions = std::vector<Function>())
|
||||
: functions(functions) {
|
||||
std::vector<const ExtismFunction *> ptrs;
|
||||
for (auto i : this->functions) {
|
||||
ptrs.push_back(i.get());
|
||||
}
|
||||
this->plugin = extism_plugin_new(ctx.get(), wasm, length, ptrs.data(),
|
||||
ptrs.size(), with_wasi);
|
||||
if (this->plugin < 0) {
|
||||
const char *err = extism_error(ctx.get(), -1);
|
||||
throw Error(err == nullptr ? "Unable to load plugin" : err);
|
||||
|
||||
char *errmsg = nullptr;
|
||||
this->plugin = extism_plugin_new(wasm, length, ptrs.data(), ptrs.size(),
|
||||
with_wasi, &errmsg);
|
||||
if (this->plugin == nullptr) {
|
||||
std::string s(errmsg);
|
||||
extism_plugin_new_error_free(errmsg);
|
||||
throw Error(s);
|
||||
}
|
||||
this->context = ctx;
|
||||
}
|
||||
|
||||
Plugin(const std::string &str, bool with_wasi = false,
|
||||
std::vector<Function> functions = {},
|
||||
std::shared_ptr<ExtismContext> ctx = std::shared_ptr<ExtismContext>(
|
||||
extism_context_new(), extism_context_free))
|
||||
: Plugin((const uint8_t *)str.c_str(), str.size(), with_wasi, functions,
|
||||
ctx) {}
|
||||
std::vector<Function> functions = {})
|
||||
: Plugin((const uint8_t *)str.c_str(), str.size(), with_wasi, functions) {
|
||||
}
|
||||
|
||||
Plugin(const std::vector<uint8_t> &data, bool with_wasi = false,
|
||||
std::vector<Function> functions = {},
|
||||
std::shared_ptr<ExtismContext> ctx = std::shared_ptr<ExtismContext>(
|
||||
extism_context_new(), extism_context_free))
|
||||
: Plugin(data.data(), data.size(), with_wasi, functions, ctx) {}
|
||||
std::vector<Function> functions = {})
|
||||
: Plugin(data.data(), data.size(), with_wasi, functions) {}
|
||||
|
||||
CancelHandle cancel_handle() {
|
||||
return CancelHandle(
|
||||
extism_plugin_cancel_handle(this->context.get(), this->id()));
|
||||
CancelHandle cancelHandle() {
|
||||
return CancelHandle(extism_plugin_cancel_handle(this->plugin));
|
||||
}
|
||||
|
||||
#ifndef EXTISM_NO_JSON
|
||||
// Create a new plugin from Manifest
|
||||
Plugin(const Manifest &manifest, bool with_wasi = false,
|
||||
std::vector<Function> functions = {},
|
||||
std::shared_ptr<ExtismContext> ctx = std::shared_ptr<ExtismContext>(
|
||||
extism_context_new(), extism_context_free)) {
|
||||
std::vector<const ExtismFunction *> ptrs;
|
||||
for (auto i : this->functions) {
|
||||
ptrs.push_back(i.get());
|
||||
}
|
||||
|
||||
auto buffer = manifest.json();
|
||||
this->plugin =
|
||||
extism_plugin_new(ctx.get(), (const uint8_t *)buffer.c_str(),
|
||||
buffer.size(), ptrs.data(), ptrs.size(), with_wasi);
|
||||
if (this->plugin < 0) {
|
||||
const char *err = extism_error(ctx.get(), -1);
|
||||
throw Error(err == nullptr ? "Unable to load plugin from manifest" : err);
|
||||
}
|
||||
this->context = ctx;
|
||||
}
|
||||
#endif
|
||||
std::vector<Function> functions = {})
|
||||
: Plugin(manifest.json().c_str(), with_wasi, functions) {}
|
||||
|
||||
~Plugin() {
|
||||
extism_plugin_free(this->context.get(), this->plugin);
|
||||
this->plugin = -1;
|
||||
}
|
||||
|
||||
ExtismPlugin id() const { return this->plugin; }
|
||||
|
||||
ExtismContext *get_context() const { return this->context.get(); }
|
||||
|
||||
void update(const uint8_t *wasm, size_t length, bool with_wasi = false,
|
||||
std::vector<Function> functions = {}) {
|
||||
this->functions = functions;
|
||||
std::vector<const ExtismFunction *> ptrs;
|
||||
for (auto i : this->functions) {
|
||||
ptrs.push_back(i.get());
|
||||
}
|
||||
bool b = extism_plugin_update(this->context.get(), this->plugin, wasm,
|
||||
length, ptrs.data(), ptrs.size(), with_wasi);
|
||||
if (!b) {
|
||||
const char *err = extism_error(this->context.get(), -1);
|
||||
throw Error(err == nullptr ? "Unable to update plugin" : err);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef EXTISM_NO_JSON
|
||||
void update(const Manifest &manifest, bool with_wasi = false,
|
||||
std::vector<Function> functions = {}) {
|
||||
this->functions = functions;
|
||||
std::vector<const ExtismFunction *> ptrs;
|
||||
for (auto i : this->functions) {
|
||||
ptrs.push_back(i.get());
|
||||
}
|
||||
auto buffer = manifest.json();
|
||||
bool b = extism_plugin_update(
|
||||
this->context.get(), this->plugin, (const uint8_t *)buffer.c_str(),
|
||||
buffer.size(), ptrs.data(), ptrs.size(), with_wasi);
|
||||
if (!b) {
|
||||
const char *err = extism_error(this->context.get(), -1);
|
||||
throw Error(err == nullptr ? "Unable to update plugin" : err);
|
||||
}
|
||||
extism_plugin_free(this->plugin);
|
||||
this->plugin = nullptr;
|
||||
}
|
||||
|
||||
void config(const Config &data) {
|
||||
@@ -457,10 +400,9 @@ public:
|
||||
#endif
|
||||
|
||||
void config(const char *json, size_t length) {
|
||||
bool b = extism_plugin_config(this->context.get(), this->plugin,
|
||||
(const uint8_t *)json, length);
|
||||
bool b = extism_plugin_config(this->plugin, (const uint8_t *)json, length);
|
||||
if (!b) {
|
||||
const char *err = extism_error(this->context.get(), this->plugin);
|
||||
const char *err = extism_plugin_error(this->plugin);
|
||||
throw Error(err == nullptr ? "Unable to update plugin config" : err);
|
||||
}
|
||||
}
|
||||
@@ -472,10 +414,10 @@ public:
|
||||
// Call a plugin
|
||||
Buffer call(const std::string &func, const uint8_t *input,
|
||||
ExtismSize input_length) const {
|
||||
int32_t rc = extism_plugin_call(this->context.get(), this->plugin,
|
||||
func.c_str(), input, input_length);
|
||||
int32_t rc =
|
||||
extism_plugin_call(this->plugin, func.c_str(), input, input_length);
|
||||
if (rc != 0) {
|
||||
const char *error = extism_error(this->context.get(), this->plugin);
|
||||
const char *error = extism_plugin_error(this->plugin);
|
||||
if (error == nullptr) {
|
||||
throw Error("extism_call failed");
|
||||
}
|
||||
@@ -483,10 +425,8 @@ public:
|
||||
throw Error(error);
|
||||
}
|
||||
|
||||
ExtismSize length =
|
||||
extism_plugin_output_length(this->context.get(), this->plugin);
|
||||
const uint8_t *ptr =
|
||||
extism_plugin_output_data(this->context.get(), this->plugin);
|
||||
ExtismSize length = extism_plugin_output_length(this->plugin);
|
||||
const uint8_t *ptr = extism_plugin_output_data(this->plugin);
|
||||
return Buffer(ptr, length);
|
||||
}
|
||||
|
||||
@@ -503,56 +443,13 @@ public:
|
||||
}
|
||||
|
||||
// Returns true if the specified function exists
|
||||
bool function_exists(const std::string &func) const {
|
||||
return extism_plugin_function_exists(this->context.get(), this->plugin,
|
||||
func.c_str());
|
||||
bool functionExists(const std::string &func) const {
|
||||
return extism_plugin_function_exists(this->plugin, func.c_str());
|
||||
}
|
||||
};
|
||||
|
||||
class Context {
|
||||
public:
|
||||
std::shared_ptr<ExtismContext> pointer;
|
||||
|
||||
// Create a new context;
|
||||
Context() {
|
||||
this->pointer = std::shared_ptr<ExtismContext>(extism_context_new(),
|
||||
extism_context_free);
|
||||
}
|
||||
|
||||
// Create plugin from uint8_t*
|
||||
Plugin plugin(const uint8_t *wasm, size_t length, bool with_wasi = false,
|
||||
std::vector<Function> functions = {}) const {
|
||||
return Plugin(wasm, length, with_wasi, functions, this->pointer);
|
||||
}
|
||||
|
||||
// Create plugin from std::string
|
||||
Plugin plugin(const std::string &str, bool with_wasi = false,
|
||||
std::vector<Function> functions = {}) const {
|
||||
return Plugin((const uint8_t *)str.c_str(), str.size(), with_wasi,
|
||||
functions, this->pointer);
|
||||
}
|
||||
|
||||
// Create plugin from uint8_t vector
|
||||
Plugin plugin(const std::vector<uint8_t> &data, bool with_wasi = false,
|
||||
std::vector<Function> functions = {}) const {
|
||||
return Plugin(data.data(), data.size(), with_wasi, functions,
|
||||
this->pointer);
|
||||
}
|
||||
|
||||
#ifndef EXTISM_NO_JSON
|
||||
// Create plugin from Manifest
|
||||
Plugin plugin(const Manifest &manifest, bool with_wasi = false,
|
||||
std::vector<Function> functions = {}) const {
|
||||
return Plugin(manifest, with_wasi, functions, this->pointer);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Remove all plugins
|
||||
void reset() { extism_context_reset(this->pointer.get()); }
|
||||
};
|
||||
|
||||
// Set global log file for plugins
|
||||
inline bool set_log_file(const char *filename, const char *level) {
|
||||
inline bool setLogFile(const char *filename, const char *level) {
|
||||
return extism_log_file(filename, level);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "../extism.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -15,16 +16,10 @@ const std::string code = "../../wasm/code.wasm";
|
||||
namespace {
|
||||
using namespace extism;
|
||||
|
||||
TEST(Context, Basic) {
|
||||
Context context;
|
||||
ASSERT_NE(context.pointer, nullptr);
|
||||
}
|
||||
|
||||
TEST(Plugin, Manifest) {
|
||||
Manifest manifest = Manifest::path(code);
|
||||
manifest.set_config("a", "1");
|
||||
|
||||
ASSERT_NO_THROW(Plugin plugin(manifest));
|
||||
Plugin plugin(manifest);
|
||||
|
||||
Buffer buf = plugin.call("count_vowels", "this is a test");
|
||||
@@ -37,19 +32,17 @@ TEST(Plugin, BadManifest) {
|
||||
}
|
||||
|
||||
TEST(Plugin, Bytes) {
|
||||
Context context;
|
||||
auto wasm = read(code.c_str());
|
||||
ASSERT_NO_THROW(Plugin plugin = context.plugin(wasm));
|
||||
Plugin plugin = context.plugin(wasm);
|
||||
ASSERT_NO_THROW(Plugin plugin(wasm));
|
||||
Plugin plugin(wasm);
|
||||
|
||||
Buffer buf = plugin.call("count_vowels", "this is another test");
|
||||
ASSERT_EQ(buf.string(), "{\"count\": 6}");
|
||||
}
|
||||
|
||||
TEST(Plugin, UpdateConfig) {
|
||||
Context context;
|
||||
auto wasm = read(code.c_str());
|
||||
Plugin plugin = context.plugin(wasm);
|
||||
Plugin plugin(wasm);
|
||||
|
||||
Config config;
|
||||
config["abc"] = "123";
|
||||
@@ -57,12 +50,11 @@ TEST(Plugin, UpdateConfig) {
|
||||
}
|
||||
|
||||
TEST(Plugin, FunctionExists) {
|
||||
Context context;
|
||||
auto wasm = read(code.c_str());
|
||||
Plugin plugin = context.plugin(wasm);
|
||||
Plugin plugin(wasm);
|
||||
|
||||
ASSERT_FALSE(plugin.function_exists("bad_function"));
|
||||
ASSERT_TRUE(plugin.function_exists("count_vowels"));
|
||||
ASSERT_FALSE(plugin.functionExists("bad_function"));
|
||||
ASSERT_TRUE(plugin.functionExists("count_vowels"));
|
||||
}
|
||||
|
||||
TEST(Plugin, HostFunction) {
|
||||
@@ -85,6 +77,38 @@ TEST(Plugin, HostFunction) {
|
||||
ASSERT_EQ((std::string)buf, "test");
|
||||
}
|
||||
|
||||
void callThread(Plugin *plugin) {
|
||||
auto buf = plugin->call("count_vowels", "aaa").string();
|
||||
ASSERT_EQ(buf.size(), 10);
|
||||
ASSERT_EQ(buf, "testing123");
|
||||
}
|
||||
|
||||
TEST(Plugin, MultipleThreads) {
|
||||
auto wasm = read("../../wasm/code-functions.wasm");
|
||||
auto t = std::vector<ValType>{ValType::I64};
|
||||
Function hello_world =
|
||||
Function("hello_world", t, t,
|
||||
[](CurrentPlugin plugin, const std::vector<Val> ¶ms,
|
||||
std::vector<Val> &results, void *user_data) {
|
||||
auto offs = plugin.alloc(10);
|
||||
memcpy(plugin.memory() + offs, "testing123", 10);
|
||||
results[0].v.i64 = (int64_t)offs;
|
||||
});
|
||||
auto functions = std::vector<Function>{
|
||||
hello_world,
|
||||
};
|
||||
Plugin plugin(wasm, true, functions);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
threads.push_back(std::thread(callThread, &plugin));
|
||||
}
|
||||
|
||||
for (auto &th : threads) {
|
||||
th.join();
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
7
d/.gitignore
vendored
Normal file
7
d/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Generated by host system's C preprocessor
|
||||
# See https://dlang.org/spec/importc.html#manual-cpp
|
||||
runtime.i
|
||||
|
||||
docs.json
|
||||
extism-test-*
|
||||
*.lst
|
||||
4
d/README.md
Normal file
4
d/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# D Host SDK
|
||||
|
||||
Development of this library has moved to [this repo](https://github.com/extism/d-sdk#readme).
|
||||
|
||||
17
d/examples/hello/.gitignore
vendored
Normal file
17
d/examples/hello/.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
.dub
|
||||
docs.json
|
||||
__dummy.html
|
||||
docs/
|
||||
/extism_hello
|
||||
/hello
|
||||
hello.so
|
||||
hello.dylib
|
||||
hello.dll
|
||||
hello.a
|
||||
hello.lib
|
||||
hello-test-*
|
||||
*.exe
|
||||
*.pdb
|
||||
*.o
|
||||
*.obj
|
||||
*.lst
|
||||
15
d/examples/hello/dub.json
Normal file
15
d/examples/hello/dub.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "hello",
|
||||
"description": "A minimal Extism usage example",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
"Chance Snow <git@chancesnow.me>",
|
||||
"Extism contributors"
|
||||
],
|
||||
"copyright": "Copyright © 2023, Extism contributors",
|
||||
"dependencies": {
|
||||
"extism": {
|
||||
"path": "../../../"
|
||||
}
|
||||
}
|
||||
}
|
||||
33
d/examples/hello/source/app.d
Normal file
33
d/examples/hello/source/app.d
Normal file
@@ -0,0 +1,33 @@
|
||||
import std.conv: castFrom, to;
|
||||
import std.file;
|
||||
import std.functional: toDelegate;
|
||||
import std.stdio;
|
||||
import std.string: representation;
|
||||
import std.typecons : Yes;
|
||||
|
||||
import extism;
|
||||
|
||||
void main() {
|
||||
auto wasm = cast(ubyte[]) read("wasm/code-functions.wasm");
|
||||
// FIXME: Creating the plugin results in EXC_BAD_ACCESS (segfault?) from `extism_plugin_new`
|
||||
auto plugin = new Plugin(wasm, [
|
||||
Function!string(
|
||||
"hello_world", /* Inputs */ [ValType.i64], /* Outputs */ [ValType.i64], toDelegate(&helloWorld), "Hello, again!"
|
||||
),
|
||||
], Yes.withWasi);
|
||||
|
||||
auto input = "aeiou";
|
||||
plugin.call("count_vowels", cast(ubyte[]) input.representation);
|
||||
writeln(plugin.outputData);
|
||||
}
|
||||
|
||||
///
|
||||
void helloWorld(CurrentPlugin plugin, const Val[] inputs, Val[] outputs, void *data) {
|
||||
writeln("Hello from D!");
|
||||
writeln(data);
|
||||
|
||||
ExtismSize ptr_offs = inputs[0].v.i64;
|
||||
auto buf = plugin.memory(ptr_offs);
|
||||
writeln(buf);
|
||||
outputs[0].v.i64 = inputs[0].v.i64;
|
||||
}
|
||||
258
d/extism.d
Normal file
258
d/extism.d
Normal file
@@ -0,0 +1,258 @@
|
||||
module extism;
|
||||
|
||||
import std.conv: castFrom, to;
|
||||
import std.meta: Alias;
|
||||
import std.string: fromStringz, toStringz;
|
||||
|
||||
import runtime;
|
||||
|
||||
/// A list of all possible value types in WebAssembly.
|
||||
enum ValType {
|
||||
i32 = I32,
|
||||
i64 = I64,
|
||||
f32 = F32,
|
||||
f64 = F64,
|
||||
v128 = V128,
|
||||
funcRef = FuncRef,
|
||||
externRef = ExternRef,
|
||||
}
|
||||
|
||||
// Opaque Pointers
|
||||
///
|
||||
alias CancelHandle = Alias!(void*);
|
||||
///
|
||||
alias CurrentPlugin = Alias!(void*);
|
||||
|
||||
///
|
||||
alias ExtismSize = ulong;
|
||||
|
||||
/// A union type for host function argument/return values.
|
||||
union ValUnion {
|
||||
///
|
||||
int i32;
|
||||
///
|
||||
long i64;
|
||||
///
|
||||
float f32;
|
||||
///
|
||||
double f64;
|
||||
}
|
||||
|
||||
/// Holds the type and value of a function argument/return.
|
||||
struct Val {
|
||||
///
|
||||
ValType t;
|
||||
///
|
||||
ValUnion v;
|
||||
}
|
||||
|
||||
/// Host function signature.
|
||||
///
|
||||
/// Used to register host functions with plugins.
|
||||
/// Params:
|
||||
/// plugin: the currently executing plugin from within a host function
|
||||
/// inputs: argument values
|
||||
/// outputs: return values
|
||||
/// data: user data associated with the host function
|
||||
alias FunctionType = void delegate(
|
||||
CurrentPlugin plugin, const Val[] inputs, Val[] outputs, void* data
|
||||
);
|
||||
|
||||
/// Returns: A slice of an allocated memory block of the currently running plugin.
|
||||
ubyte[] memory(CurrentPlugin plugin, ulong n) {
|
||||
auto length = extism_current_plugin_memory_length(cast(ExtismCurrentPlugin*) plugin, n);
|
||||
return extism_current_plugin_memory(cast(ExtismCurrentPlugin*) plugin)[n .. (n + length)];
|
||||
}
|
||||
|
||||
/// Allocate a memory block in the currently running plugin.
|
||||
ulong memoryAlloc(CurrentPlugin plugin, ulong n) {
|
||||
return extism_current_plugin_memory_alloc(cast(ExtismCurrentPlugin*) plugin, n);
|
||||
}
|
||||
|
||||
/// Free an allocated memory block.
|
||||
void memoryFree(CurrentPlugin plugin, ulong ptr) {
|
||||
extism_current_plugin_memory_free(cast(ExtismCurrentPlugin*) plugin, ptr);
|
||||
}
|
||||
|
||||
/// A host function, where `T` is the type of its user data.
|
||||
struct Function(T) {
|
||||
/// Managed pointer.
|
||||
ExtismFunction* func;
|
||||
alias func this;
|
||||
private string _namespace = null;
|
||||
|
||||
/// Create a new host function.
|
||||
/// Params:
|
||||
/// name: function name, this should be valid UTF-8
|
||||
/// inputs: argument types
|
||||
/// outputs: return types
|
||||
/// func: the function to call
|
||||
/// userData: a pointer that will be passed to the function when it's called. This value should live as long as the function exists.
|
||||
/// freeUserData: a callback to release the `userData`` value when the resulting `Function` is freed.
|
||||
this(
|
||||
string name, const ValType[] inputs, const ValType[] outputs, FunctionType func,
|
||||
T userData, void function(T userData) freeUserData = null
|
||||
) {
|
||||
import std.functional: toDelegate;
|
||||
|
||||
// Bind the given host function with C linkage
|
||||
auto funcClosure = ((
|
||||
ExtismCurrentPlugin* plugin,
|
||||
const ExtismVal* inputs, ulong numInputs, ExtismVal* outputs, ulong numOutputs,
|
||||
void* data
|
||||
) {
|
||||
func(plugin, (cast(const Val*) inputs)[0 .. numInputs], (cast(Val*) outputs)[0 .. numOutputs], data);
|
||||
}).toDelegate.bindDelegate;
|
||||
|
||||
// Bind the `freeUserData` function with C linkage
|
||||
auto freeUserDataHandler = freeUserData is null ? null : ((void* userDataPtr) {
|
||||
if (userDataPtr is null) return;
|
||||
freeUserData((&userDataPtr).to!T);
|
||||
}).toDelegate.bindDelegate;
|
||||
|
||||
this.func = extism_function_new(
|
||||
name.toStringz,// See https://dlang.org/spec/importc.html#enums
|
||||
// See https://forum.dlang.org/post/qmidcpaxctbuphcyvkdc@forum.dlang.org
|
||||
castFrom!(const(ValType)*)
|
||||
.to!(typeof(ExtismVal.t)*)(inputs.ptr), inputs.length,
|
||||
castFrom!(const(ValType)*).to!(typeof(ExtismVal.t)*)(outputs.ptr), outputs.length,
|
||||
funcClosure,
|
||||
&userData,
|
||||
freeUserDataHandler,
|
||||
);
|
||||
}
|
||||
|
||||
~this() {
|
||||
extism_function_free(func);
|
||||
}
|
||||
|
||||
/// Get the namespace of this function.
|
||||
string namespace() {
|
||||
return this._namespace;
|
||||
}
|
||||
/// Set the namespace of this function.
|
||||
void namespace(string value) {
|
||||
this._namespace = value;
|
||||
extism_function_set_namespace(func, value.toStringz);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
struct Plugin {
|
||||
import std.uuid: UUID;
|
||||
|
||||
/// Managed pointer.
|
||||
ExtismPlugin* plugin;
|
||||
alias plugin this;
|
||||
|
||||
/// Create a new plugin.
|
||||
/// Params:
|
||||
/// wasm: is a WASM module (wat or wasm) or a JSON encoded manifest
|
||||
/// functions: is an array of ExtismFunction*
|
||||
/// withWasi: enables/disables WASI
|
||||
/// Throws: `Exception` if the plugin could not be created.
|
||||
this(const ubyte[] wasm, const(ExtismFunction)*[] functions, bool withWasi = false) {
|
||||
char* errorMsgPtr = null;
|
||||
plugin = extism_plugin_new(
|
||||
wasm.ptr, wasm.length, cast(ExtismFunction**) functions.ptr, functions.length, withWasi, &errorMsgPtr
|
||||
);
|
||||
// See https://github.com/extism/extism/blob/ddcbeec3debe787293a9957c8be88f80a64b7c22/c/main.c#L67
|
||||
// Instead of terminating the host process, throw.
|
||||
if (plugin !is null)
|
||||
return;
|
||||
auto errorMsg = errorMsgPtr.fromStringz.idup;
|
||||
extism_plugin_new_error_free(errorMsgPtr);
|
||||
// TODO: Subclass `Exception` for better error handling
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
|
||||
~this() {
|
||||
extism_plugin_free(plugin);
|
||||
}
|
||||
|
||||
/// Get a plugin's ID.
|
||||
UUID id() {
|
||||
return UUID(extism_plugin_id(plugin)[0 .. 16]);
|
||||
}
|
||||
|
||||
/// Get the error associated with this `Plugin`.
|
||||
string error() {
|
||||
return extism_error(plugin).fromStringz.idup;
|
||||
}
|
||||
|
||||
/// Update plugin config values, this will merge with the existing values.
|
||||
bool config(ubyte[] json) {
|
||||
return extism_plugin_config(plugin, json.ptr, json.length);
|
||||
}
|
||||
|
||||
/// See_Also: `cancel`
|
||||
const(CancelHandle) cancelHandle() {
|
||||
return extism_plugin_cancel_handle(plugin);
|
||||
}
|
||||
|
||||
/// Cancel a running plugin.
|
||||
/// See_Also: `cancelHandle`
|
||||
bool cancel(const CancelHandle handle) {
|
||||
return extism_plugin_cancel(cast(ExtismCancelHandle*) handle);
|
||||
}
|
||||
|
||||
/// Returns: Whether a function with `name` exists.
|
||||
bool functionExists(string name) {
|
||||
return extism_plugin_function_exists(plugin, name.toStringz);
|
||||
}
|
||||
|
||||
/// Call a function.
|
||||
/// Params:
|
||||
/// funcName: is the function to call
|
||||
/// data: is the input data
|
||||
void call(string funcName, ubyte[] data = null) {
|
||||
// Returns `0` if the call was successful, otherwise `-1`.
|
||||
if (extism_plugin_call(plugin, funcName.toStringz, data.ptr, data.length) != 0)
|
||||
throw new Exception(this.error);
|
||||
}
|
||||
|
||||
/// Get the plugin's output data.
|
||||
///
|
||||
/// Note: This copies the data into a managed buffer.
|
||||
/// Remarks: Use `outputData.length` to retrieve size of plugin output.
|
||||
ubyte[] outputData() {
|
||||
import std.algorithm: copy;
|
||||
|
||||
auto outputLength = extism_plugin_output_length(plugin);
|
||||
auto outputData = extism_plugin_output_data(plugin)[0 .. outputLength];
|
||||
auto buffer = new ubyte[outputLength];
|
||||
assert(
|
||||
outputData.copy(buffer).length == 0,
|
||||
"Output data was not completely copied into buffer, i.e. buffer was not filled."
|
||||
);
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the error associated with a `Context` or `Plugin`, if plugin is -1 then the context error will be returned.
|
||||
|
||||
/// Set log file and level.
|
||||
bool setLogFile(string filename, string logLevel) {
|
||||
return extism_log_file(filename.toStringz, logLevel.toStringz);
|
||||
}
|
||||
|
||||
/// Get the Extism version string.
|
||||
string version_() {
|
||||
return extism_version().fromStringz.idup;
|
||||
}
|
||||
|
||||
import std.traits: isDelegate;
|
||||
|
||||
/// Transform the given delegate into a static function pointer with C linkage.
|
||||
/// See_Also: <a href="https://stackoverflow.com/a/22845722/1363247">stackoverflow.com/a/22845722/1363247</a>
|
||||
package auto bindDelegate(Func)(Func f) if (isDelegate!Func) {
|
||||
import std.traits: ParameterTypeTuple, ReturnType;
|
||||
|
||||
static Func delegate_;
|
||||
delegate_ = f;
|
||||
extern (C) static ReturnType!Func func(ParameterTypeTuple!Func args) {
|
||||
return delegate_(args);
|
||||
}
|
||||
|
||||
return &func;
|
||||
}
|
||||
1
d/runtime.c
Normal file
1
d/runtime.c
Normal file
@@ -0,0 +1 @@
|
||||
#include "runtime/extism.h"
|
||||
4
dotnet/README.md
Normal file
4
dotnet/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Dotnet Host SDK
|
||||
|
||||
This contains the `0.x` version of the SDK. Development of this library has moved to [this repo](https://github.com/extism/dotnet-sdk#readme).
|
||||
|
||||
28
dotnet/nuget/Directory.Build.props
Normal file
28
dotnet/nuget/Directory.Build.props
Normal file
@@ -0,0 +1,28 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Authors>Extism Contributors</Authors>
|
||||
<PackageTags>extism, wasm, plugin</PackageTags>
|
||||
<PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<PackageProjectUrl>https://extism.org</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/your/repository.git</RepositoryUrl>
|
||||
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NoBuild>true</NoBuild>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="README.md" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MinVer" Version="4.3.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
14
dotnet/nuget/Extism.runtime.all.csproj
Normal file
14
dotnet/nuget/Extism.runtime.all.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<NuspecFile>Extism.runtime.all.nuspec</NuspecFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="Replace" BeforeTargets="GenerateNuspec">
|
||||
<WriteLinesToFile
|
||||
File="$(NuspecFile)"
|
||||
Lines="$([System.IO.File]::ReadAllText($(NuspecFile)).Replace('$Version','$(MinVerVersion)'))"
|
||||
Overwrite="true"/>
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
27
dotnet/nuget/Extism.runtime.all.nuspec
Normal file
27
dotnet/nuget/Extism.runtime.all.nuspec
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0"?>
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Extism.runtime.all</id>
|
||||
<version>$Version</version>
|
||||
<authors>Extism Contributors</authors>
|
||||
<owners>Extism Contributors</owners>
|
||||
<description>Internal implementation package for Extism</description>
|
||||
<tags>extism wasm plugin</tags>
|
||||
<license type="expression">BSD-3-Clause</license>
|
||||
<projectUrl>https://github.com/extism/extism</projectUrl>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<copyright>Copyright © 2023 Extism Contributors</copyright>
|
||||
<readme>README.md</readme>
|
||||
<dependencies>
|
||||
<dependency id="Extism.runtime.linux-arm64" version="$Version" />
|
||||
<dependency id="Extism.runtime.linux-musl-arm64" version="$Version" />
|
||||
<dependency id="Extism.runtime.linux-x64" version="$Version" />
|
||||
<dependency id="Extism.runtime.osx-arm64" version="$Version" />
|
||||
<dependency id="Extism.runtime.osx-x64" version="$Version" />
|
||||
<dependency id="Extism.runtime.win-x64" version="$Version" />
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
<file src="README.md" target="" />
|
||||
</files>
|
||||
</package>
|
||||
15
dotnet/nuget/Extism.runtime.linux-arm64.csproj
Normal file
15
dotnet/nuget/Extism.runtime.linux-arm64.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.runtime.linux-arm64</PackageId>
|
||||
<Description>Internal implementation package for Extism to work on Linux ARM64</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes\linux-arm64\native\libextism.so"
|
||||
CopyToOutputDirectory="Always"
|
||||
Pack="true"
|
||||
PackagePath="runtimes\linux-arm64\native\libextism.so" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
16
dotnet/nuget/Extism.runtime.linux-musl-arm64.csproj
Normal file
16
dotnet/nuget/Extism.runtime.linux-musl-arm64.csproj
Normal file
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.runtime.linux-musl-arm64</PackageId>
|
||||
<Description>Internal implementation package for Extism to work on Linux Musl ARM64</Description>
|
||||
<IsPackable>true</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes\linux-musl-arm64\native\libextism.so"
|
||||
CopyToOutputDirectory="Always"
|
||||
Pack="true"
|
||||
PackagePath="runtimes\linux-musl-arm64\native\libextism.so" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
15
dotnet/nuget/Extism.runtime.linux-x64.csproj
Normal file
15
dotnet/nuget/Extism.runtime.linux-x64.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.runtime.linux-x64</PackageId>
|
||||
<Description>Internal implementation package for Extism to work on Linux x64</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes\linux-x64\native\libextism.so"
|
||||
CopyToOutputDirectory="Always"
|
||||
Pack="true"
|
||||
PackagePath="runtimes\linux-x64\native\libextism.so" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
15
dotnet/nuget/Extism.runtime.osx-arm64.csproj
Normal file
15
dotnet/nuget/Extism.runtime.osx-arm64.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.runtime.osx-arm64</PackageId>
|
||||
<Description>Internal implementation package for Extism to work on macOS ARM64</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes\osx-arm64\native\libextism.dylib"
|
||||
CopyToOutputDirectory="Always"
|
||||
Pack="true"
|
||||
PackagePath="runtimes\osx-arm64\native\libextism.dylib" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
15
dotnet/nuget/Extism.runtime.osx-x64.csproj
Normal file
15
dotnet/nuget/Extism.runtime.osx-x64.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.runtime.osx-x64</PackageId>
|
||||
<Description>Internal implementation package for Extism to work on macOS x64</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes\osx-x64\native\libextism.dylib"
|
||||
CopyToOutputDirectory="Always"
|
||||
Pack="true"
|
||||
PackagePath="runtimes\osx-x64\native\libextism.dylib" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
15
dotnet/nuget/Extism.runtime.win-x64.csproj
Normal file
15
dotnet/nuget/Extism.runtime.win-x64.csproj
Normal file
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.runtime.win-x64</PackageId>
|
||||
<Description>Internal implementation package for Extism to work on Windows x64</Description>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes\win-x64\native\extism.dll"
|
||||
CopyToOutputDirectory="Always"
|
||||
Pack="true"
|
||||
PackagePath="runtimes\win-x64\native\extism.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,24 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<NoBuild>true</NoBuild>
|
||||
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PackageId>Extism.runtime.win-x64</PackageId>
|
||||
<Version>0.7.0</Version>
|
||||
<Authors>Extism Contributors</Authors>
|
||||
<Description>Internal implementation package for Extism to work on Windows x64</Description>
|
||||
<Tags>extism, wasm, plugin</Tags>
|
||||
<PackageLicenseExpression>BSD-3-Clause</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="runtimes/win-x64.dll"
|
||||
CopyToOutputDirectory="Always"
|
||||
Pack="true"
|
||||
PackagePath="runtimes\win-x64\native\extism.dll" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
66
dotnet/nuget/Nuget.sln
Normal file
66
dotnet/nuget/Nuget.sln
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.runtime.linux-arm64", "Extism.runtime.linux-arm64.csproj", "{F1CF6818-43C4-4CD6-A3AD-748EFC83C21B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.runtime.linux-musl-arm64", "Extism.runtime.linux-musl-arm64.csproj", "{2E563F73-9FD5-42B2-9368-12BBF5819148}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.runtime.linux-x64", "Extism.runtime.linux-x64.csproj", "{A2E24D65-8AE2-4C09-82CB-5AEBA0539973}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.runtime.osx-arm64", "Extism.runtime.osx-arm64.csproj", "{66461A9C-140C-48C1-B658-BE540220460D}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.runtime.osx-x64", "Extism.runtime.osx-x64.csproj", "{BE23CF82-F668-4166-8579-0FF2B20EA095}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.runtime.win-x64", "Extism.runtime.win-x64.csproj", "{639F9EFE-AC2A-43A7-A9BB-EDAEB306455F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Extism.runtime.all", "Extism.runtime.all.csproj", "{88DF54A7-845B-409D-93FE-64CF2864FFDE}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{2E563F73-9FD5-42B2-9368-12BBF5819148} = {2E563F73-9FD5-42B2-9368-12BBF5819148}
|
||||
{639F9EFE-AC2A-43A7-A9BB-EDAEB306455F} = {639F9EFE-AC2A-43A7-A9BB-EDAEB306455F}
|
||||
{66461A9C-140C-48C1-B658-BE540220460D} = {66461A9C-140C-48C1-B658-BE540220460D}
|
||||
{A2E24D65-8AE2-4C09-82CB-5AEBA0539973} = {A2E24D65-8AE2-4C09-82CB-5AEBA0539973}
|
||||
{BE23CF82-F668-4166-8579-0FF2B20EA095} = {BE23CF82-F668-4166-8579-0FF2B20EA095}
|
||||
{F1CF6818-43C4-4CD6-A3AD-748EFC83C21B} = {F1CF6818-43C4-4CD6-A3AD-748EFC83C21B}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F1CF6818-43C4-4CD6-A3AD-748EFC83C21B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F1CF6818-43C4-4CD6-A3AD-748EFC83C21B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F1CF6818-43C4-4CD6-A3AD-748EFC83C21B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F1CF6818-43C4-4CD6-A3AD-748EFC83C21B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2E563F73-9FD5-42B2-9368-12BBF5819148}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2E563F73-9FD5-42B2-9368-12BBF5819148}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2E563F73-9FD5-42B2-9368-12BBF5819148}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2E563F73-9FD5-42B2-9368-12BBF5819148}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A2E24D65-8AE2-4C09-82CB-5AEBA0539973}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A2E24D65-8AE2-4C09-82CB-5AEBA0539973}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A2E24D65-8AE2-4C09-82CB-5AEBA0539973}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A2E24D65-8AE2-4C09-82CB-5AEBA0539973}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{66461A9C-140C-48C1-B658-BE540220460D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{66461A9C-140C-48C1-B658-BE540220460D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{66461A9C-140C-48C1-B658-BE540220460D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{66461A9C-140C-48C1-B658-BE540220460D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BE23CF82-F668-4166-8579-0FF2B20EA095}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BE23CF82-F668-4166-8579-0FF2B20EA095}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BE23CF82-F668-4166-8579-0FF2B20EA095}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BE23CF82-F668-4166-8579-0FF2B20EA095}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{639F9EFE-AC2A-43A7-A9BB-EDAEB306455F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{639F9EFE-AC2A-43A7-A9BB-EDAEB306455F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{639F9EFE-AC2A-43A7-A9BB-EDAEB306455F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{639F9EFE-AC2A-43A7-A9BB-EDAEB306455F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{88DF54A7-845B-409D-93FE-64CF2864FFDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{88DF54A7-845B-409D-93FE-64CF2864FFDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{88DF54A7-845B-409D-93FE-64CF2864FFDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{88DF54A7-845B-409D-93FE-64CF2864FFDE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
2
dotnet/nuget/README.md
Normal file
2
dotnet/nuget/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# Extism
|
||||
The cross-language framework for building with WebAssembly (wasm).
|
||||
@@ -4,8 +4,6 @@ using Extism.Sdk.Native;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
var context = new Context();
|
||||
|
||||
var userData = Marshal.StringToHGlobalAnsi("Hello again!");
|
||||
|
||||
using var helloWorld = new HostFunction(
|
||||
@@ -30,7 +28,7 @@ void HelloWorld(CurrentPlugin plugin, Span<ExtismVal> inputs, Span<ExtismVal> ou
|
||||
}
|
||||
|
||||
var wasm = File.ReadAllBytes("./code-functions.wasm");
|
||||
using var plugin = context.CreatePlugin(wasm, new[] { helloWorld }, withWasi: true);
|
||||
using var plugin = new Plugin(wasm, new[] { helloWorld }, withWasi: true);
|
||||
|
||||
var output = Encoding.UTF8.GetString(
|
||||
plugin.CallFunction("count_vowels", Encoding.UTF8.GetBytes("Hello World!"))
|
||||
|
||||
@@ -1,191 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Extism.Sdk.Native;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an Extism context through which you can load <see cref="Plugin"/>s.
|
||||
/// </summary>
|
||||
public unsafe class Context : IDisposable
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, Plugin> _plugins = new ConcurrentDictionary<int, Plugin>();
|
||||
|
||||
private const int DisposedMarker = 1;
|
||||
|
||||
private int _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a new Extism Context.
|
||||
/// </summary>
|
||||
public Context()
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
NativeHandle = LibExtism.extism_context_new();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Native pointer to the Extism Context.
|
||||
/// </summary>
|
||||
internal LibExtism.ExtismContext* NativeHandle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Loads an Extism <see cref="Plugin"/>.
|
||||
/// </summary>
|
||||
/// <param name="wasm">A WASM module (wat or wasm) or a JSON encoded manifest.</param>
|
||||
/// <param name="functions">List of host functions expected by the plugin.</param>
|
||||
/// <param name="withWasi">Enable/Disable WASI.</param>
|
||||
public Plugin CreatePlugin(ReadOnlySpan<byte> wasm, HostFunction[] functions, bool withWasi)
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
var functionHandles = functions.Select(f => f.NativeHandle).ToArray();
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* wasmPtr = wasm)
|
||||
fixed (IntPtr* functionsPtr = functionHandles)
|
||||
{
|
||||
var index = LibExtism.extism_plugin_new(NativeHandle, wasmPtr, wasm.Length, functionsPtr, functions.Length, withWasi);
|
||||
if (index == -1)
|
||||
{
|
||||
var errorMsg = GetError();
|
||||
if (errorMsg != null)
|
||||
{
|
||||
throw new ExtismException(errorMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ExtismException("Failed to create plugin.");
|
||||
}
|
||||
}
|
||||
|
||||
return _plugins[index] = new Plugin(this, functions, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a plugin by index.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of plugin.</param>
|
||||
/// <returns></returns>
|
||||
public Plugin GetPlugin(int index)
|
||||
{
|
||||
return _plugins[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all plugins from this <see cref="Context"/>'s registry.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
LibExtism.extism_context_reset(NativeHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get this this <see cref="Context"/>'s last error.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal string? GetError()
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
var result = LibExtism.extism_error(NativeHandle, -1);
|
||||
return Marshal.PtrToStringUTF8(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees all resources held by this Context.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (Interlocked.Exchange(ref _disposed, DisposedMarker) == DisposedMarker)
|
||||
{
|
||||
// Already disposed.
|
||||
return;
|
||||
}
|
||||
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throw an appropriate exception if the plugin has been disposed.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException"></exception>
|
||||
protected void CheckNotDisposed()
|
||||
{
|
||||
Interlocked.MemoryBarrier();
|
||||
if (_disposed == DisposedMarker)
|
||||
{
|
||||
ThrowDisposedException();
|
||||
}
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
private static void ThrowDisposedException()
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(Context));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees all resources held by this Context.
|
||||
/// </summary>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Free up any managed resources here
|
||||
}
|
||||
|
||||
foreach (var plugin in _plugins.Values)
|
||||
{
|
||||
plugin.Dispose();
|
||||
}
|
||||
|
||||
// Free up unmanaged resources
|
||||
LibExtism.extism_context_free(NativeHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destructs the current Context and frees all resources used by it.
|
||||
/// </summary>
|
||||
~Context()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Extism version string.
|
||||
/// </summary>
|
||||
public static string GetExtismVersion()
|
||||
{
|
||||
var pointer = LibExtism.extism_version();
|
||||
return Marshal.PtrToStringUTF8(pointer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set Extism's log file and level. This is applied for all <see cref="Context"/>s.
|
||||
/// </summary>
|
||||
/// <param name="logPath">Log file; can be 'stdout' or 'stderr' to write logs to the console.</param>
|
||||
/// <param name="level">The log level to write at.</param>
|
||||
public static bool SetExtismLogFile(string logPath, LogLevel level)
|
||||
{
|
||||
var logLevel = level switch
|
||||
{
|
||||
LogLevel.Error => LibExtism.LogLevels.Error,
|
||||
LogLevel.Warning => LibExtism.LogLevels.Warn,
|
||||
LogLevel.Info => LibExtism.LogLevels.Info,
|
||||
LogLevel.Debug => LibExtism.LogLevels.Debug,
|
||||
LogLevel.Trace => LibExtism.LogLevels.Trace,
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
return LibExtism.extism_log_file(logPath, logLevel);
|
||||
}
|
||||
}
|
||||
@@ -97,10 +97,10 @@ public struct ExtismVal
|
||||
internal static class LibExtism
|
||||
{
|
||||
/// <summary>
|
||||
/// A `Context` is used to store and manage plugins.
|
||||
/// An Extism Plugin
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct ExtismContext { }
|
||||
internal struct ExtismPlugin { }
|
||||
|
||||
/// <summary>
|
||||
/// Host function signature
|
||||
@@ -180,24 +180,9 @@ internal static class LibExtism
|
||||
[DllImport("extism", EntryPoint = "extism_function_free")]
|
||||
internal static extern void extism_function_free(IntPtr ptr);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new context.
|
||||
/// </summary>
|
||||
/// <returns>A pointer to the newly created context.</returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern ExtismContext* extism_context_new();
|
||||
|
||||
/// <summary>
|
||||
/// Remove a context from the registry and free associated memory.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern void extism_context_free(ExtismContext* context);
|
||||
|
||||
/// <summary>
|
||||
/// Load a WASM plugin.
|
||||
/// </summary>
|
||||
/// <param name="context">Pointer to the context the plugin will be associated with.</param>
|
||||
/// <param name="wasm">A WASM module (wat or wasm) or a JSON encoded manifest.</param>
|
||||
/// <param name="wasmSize">The length of the `wasm` parameter.</param>
|
||||
/// <param name="functions">Array of host function pointers.</param>
|
||||
@@ -205,98 +190,68 @@ internal static class LibExtism
|
||||
/// <param name="withWasi">Enables/disables WASI.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern int extism_plugin_new(ExtismContext* context, byte* wasm, int wasmSize, IntPtr* functions, int nFunctions, bool withWasi);
|
||||
|
||||
/// <summary>
|
||||
/// Update a plugin, keeping the existing ID.
|
||||
/// Similar to <see cref="extism_plugin_new"/> but takes an `plugin` argument to specify which plugin to update.
|
||||
/// Memory for this plugin will be reset upon update.
|
||||
/// </summary>
|
||||
/// <param name="context">Pointer to the context the plugin is associated with.</param>
|
||||
/// <param name="plugin">Pointer to the plugin you want to update.</param>
|
||||
/// <param name="wasm">A WASM module (wat or wasm) or a JSON encoded manifest.</param>
|
||||
/// <param name="wasmSize">The length of the `wasm` parameter.</param>
|
||||
/// <param name="functions">Array of host function pointers.</param>
|
||||
/// <param name="nFunctions">Number of host functions.</param>
|
||||
/// <param name="withWasi">Enables/disables WASI.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern bool extism_plugin_update(ExtismContext* context, int plugin, byte* wasm, long wasmSize, Span<IntPtr> functions, long nFunctions, bool withWasi);
|
||||
unsafe internal static extern ExtismPlugin* extism_plugin_new(byte* wasm, int wasmSize, IntPtr* functions, int nFunctions, bool withWasi, IntPtr* errmsg);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a plugin from the registry and free associated memory.
|
||||
/// </summary>
|
||||
/// <param name="context">Pointer to the context the plugin is associated with.</param>
|
||||
/// <param name="plugin">Pointer to the plugin you want to free.</param>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern void extism_plugin_free(ExtismContext* context, int plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Remove all plugins from the registry.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern void extism_context_reset(ExtismContext* context);
|
||||
unsafe internal static extern void extism_plugin_free(ExtismPlugin* plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Update plugin config values, this will merge with the existing values.
|
||||
/// </summary>
|
||||
/// <param name="context">Pointer to the context the plugin is associated with.</param>
|
||||
/// <param name="plugin">Pointer to the plugin you want to update the configurations for.</param>
|
||||
/// <param name="json">The configuration JSON encoded in UTF8.</param>
|
||||
/// <param name="jsonLength">The length of the `json` parameter.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern bool extism_plugin_config(ExtismContext* context, int plugin, byte* json, int jsonLength);
|
||||
unsafe internal static extern bool extism_plugin_config(ExtismPlugin* plugin, byte* json, int jsonLength);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if funcName exists.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <param name="funcName"></param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern bool extism_plugin_function_exists(ExtismContext* context, int plugin, string funcName);
|
||||
unsafe internal static extern bool extism_plugin_function_exists(ExtismPlugin* plugin, string funcName);
|
||||
|
||||
/// <summary>
|
||||
/// Call a function.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <param name="funcName">The function to call.</param>
|
||||
/// <param name="data">Input data.</param>
|
||||
/// <param name="dataLen">The length of the `data` parameter.</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern int extism_plugin_call(ExtismContext* context, int plugin, string funcName, byte* data, int dataLen);
|
||||
unsafe internal static extern int extism_plugin_call(ExtismPlugin* plugin, string funcName, byte* data, int dataLen);
|
||||
|
||||
/// <summary>
|
||||
/// Get the error associated with a Context or Plugin, if plugin is -1 then the context error will be returned.
|
||||
/// Get the error associated with a Plugin
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin">A plugin pointer, or -1 for the context error.</param>
|
||||
/// <param name="plugin">A plugin pointer</param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern IntPtr extism_error(ExtismContext* context, nint plugin);
|
||||
unsafe internal static extern IntPtr extism_plugin_error(ExtismPlugin* plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Get the length of a plugin's output data.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern long extism_plugin_output_length(ExtismContext* context, int plugin);
|
||||
unsafe internal static extern long extism_plugin_output_length(ExtismPlugin* plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Get the plugin's output data.
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism")]
|
||||
unsafe internal static extern IntPtr extism_plugin_output_data(ExtismContext* context, int plugin);
|
||||
unsafe internal static extern IntPtr extism_plugin_output_data(ExtismPlugin* plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Set log file and level.
|
||||
@@ -308,11 +263,11 @@ internal static class LibExtism
|
||||
internal static extern bool extism_log_file(string filename, string logLevel);
|
||||
|
||||
/// <summary>
|
||||
/// Get the Extism version string.
|
||||
/// Get the Extism Plugin ID, a 16-bit UUID in host order
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DllImport("extism", EntryPoint = "extism_version")]
|
||||
internal static extern IntPtr extism_version();
|
||||
// [DllImport("extism")]
|
||||
// unsafe internal static extern IntPtr extism_plugin_id(ExtismPlugin* plugin);
|
||||
|
||||
/// <summary>
|
||||
/// Extism Log Levels
|
||||
|
||||
@@ -6,51 +6,43 @@ namespace Extism.Sdk.Native;
|
||||
/// <summary>
|
||||
/// Represents a WASM Extism plugin.
|
||||
/// </summary>
|
||||
public class Plugin : IDisposable
|
||||
public unsafe class Plugin : IDisposable
|
||||
{
|
||||
private const int DisposedMarker = 1;
|
||||
|
||||
private readonly Context _context;
|
||||
private readonly HostFunction[] _functions;
|
||||
private int _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Native pointer to the Extism Plugin.
|
||||
/// </summary>
|
||||
internal LibExtism.ExtismPlugin* NativeHandle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a and load a plug-in
|
||||
/// Using this constructor will give the plug-in it's own internal Context
|
||||
/// </summary>
|
||||
/// <param name="wasm">A WASM module (wat or wasm) or a JSON encoded manifest.</param>
|
||||
/// <param name="functions">List of host functions expected by the plugin.</param>
|
||||
/// <param name="withWasi">Enable/Disable WASI.</param>
|
||||
public static Plugin Create(ReadOnlySpan<byte> wasm, HostFunction[] functions, bool withWasi) {
|
||||
var context = new Context();
|
||||
return context.CreatePlugin(wasm, functions, withWasi);
|
||||
}
|
||||
|
||||
internal Plugin(Context context, HostFunction[] functions, int index)
|
||||
{
|
||||
_context = context;
|
||||
public Plugin(ReadOnlySpan<byte> wasm, HostFunction[] functions, bool withWasi) {
|
||||
_functions = functions;
|
||||
Index = index;
|
||||
}
|
||||
var functionHandles = functions.Select(f => f.NativeHandle).ToArray();
|
||||
|
||||
/// <summary>
|
||||
/// A pointer to the native Plugin struct.
|
||||
/// </summary>
|
||||
internal int Index { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Update a plugin, keeping the existing ID.
|
||||
/// </summary>
|
||||
/// <param name="wasm">The plugin WASM bytes.</param>
|
||||
/// <param name="withWasi">Enable/Disable WASI.</param>
|
||||
unsafe public bool Update(ReadOnlySpan<byte> wasm, bool withWasi)
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
var functions = _functions.Select(f => f.NativeHandle).ToArray();
|
||||
fixed (byte* wasmPtr = wasm)
|
||||
unsafe
|
||||
{
|
||||
return LibExtism.extism_plugin_update(_context.NativeHandle, Index, wasmPtr, wasm.Length, functions, 0, withWasi);
|
||||
fixed (byte* wasmPtr = wasm)
|
||||
fixed (IntPtr* functionsPtr = functionHandles)
|
||||
{
|
||||
NativeHandle = LibExtism.extism_plugin_new(wasmPtr, wasm.Length, functionsPtr, functions.Length, withWasi, null);
|
||||
if (NativeHandle == null)
|
||||
{
|
||||
throw new ExtismException("Unable to create plugin");
|
||||
// TODO: handle error
|
||||
// var s = Marshal.PtrToStringUTF8(result);
|
||||
// LibExtism.extism_plugin_new_error_free(errmsg);
|
||||
// throw new ExtismException(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +56,7 @@ public class Plugin : IDisposable
|
||||
|
||||
fixed (byte* jsonPtr = json)
|
||||
{
|
||||
return LibExtism.extism_plugin_config(_context.NativeHandle, Index, jsonPtr, json.Length);
|
||||
return LibExtism.extism_plugin_config(NativeHandle, jsonPtr, json.Length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,7 +67,7 @@ public class Plugin : IDisposable
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
return LibExtism.extism_plugin_function_exists(_context.NativeHandle, Index, name);
|
||||
return LibExtism.extism_plugin_function_exists(NativeHandle, name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -93,7 +85,7 @@ public class Plugin : IDisposable
|
||||
|
||||
fixed (byte* dataPtr = data)
|
||||
{
|
||||
int response = LibExtism.extism_plugin_call(_context.NativeHandle, Index, functionName, dataPtr, data.Length);
|
||||
int response = LibExtism.extism_plugin_call(NativeHandle, functionName, dataPtr, data.Length);
|
||||
if (response == 0)
|
||||
{
|
||||
return OutputData();
|
||||
@@ -121,7 +113,7 @@ public class Plugin : IDisposable
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
return (int)LibExtism.extism_plugin_output_length(_context.NativeHandle, Index);
|
||||
return (int)LibExtism.extism_plugin_output_length(NativeHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -135,7 +127,7 @@ public class Plugin : IDisposable
|
||||
|
||||
unsafe
|
||||
{
|
||||
var ptr = LibExtism.extism_plugin_output_data(_context.NativeHandle, Index).ToPointer();
|
||||
var ptr = LibExtism.extism_plugin_output_data(NativeHandle).ToPointer();
|
||||
return new Span<byte>(ptr, length);
|
||||
}
|
||||
}
|
||||
@@ -148,7 +140,7 @@ public class Plugin : IDisposable
|
||||
{
|
||||
CheckNotDisposed();
|
||||
|
||||
var result = LibExtism.extism_error(_context.NativeHandle, Index);
|
||||
var result = LibExtism.extism_plugin_error(NativeHandle);
|
||||
return Marshal.PtrToStringUTF8(result);
|
||||
}
|
||||
|
||||
@@ -197,7 +189,7 @@ public class Plugin : IDisposable
|
||||
}
|
||||
|
||||
// Free up unmanaged resources
|
||||
LibExtism.extism_plugin_free(_context.NativeHandle, Index);
|
||||
LibExtism.extism_plugin_free(NativeHandle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -10,25 +10,12 @@ namespace Extism.Sdk.Tests;
|
||||
|
||||
public class BasicTests
|
||||
{
|
||||
[Fact]
|
||||
public void CountHelloWorldVowelsWithoutContext()
|
||||
{
|
||||
var binDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
var wasm = File.ReadAllBytes(Path.Combine(binDirectory, "code.wasm"));
|
||||
using var plugin = Plugin.Create(wasm, Array.Empty<HostFunction>(), withWasi: true);
|
||||
|
||||
var response = plugin.CallFunction("count_vowels", Encoding.UTF8.GetBytes("Hello World"));
|
||||
Assert.Equal("{\"count\": 3}", Encoding.UTF8.GetString(response));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CountHelloWorldVowels()
|
||||
{
|
||||
using var context = new Context();
|
||||
|
||||
var binDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
var wasm = File.ReadAllBytes(Path.Combine(binDirectory, "code.wasm"));
|
||||
using var plugin = context.CreatePlugin(wasm, Array.Empty<HostFunction>(), withWasi: true);
|
||||
using var plugin = new Plugin(wasm, Array.Empty<HostFunction>(), withWasi: true);
|
||||
|
||||
var response = plugin.CallFunction("count_vowels", Encoding.UTF8.GetBytes("Hello World"));
|
||||
Assert.Equal("{\"count\": 3}", Encoding.UTF8.GetString(response));
|
||||
@@ -37,8 +24,6 @@ public class BasicTests
|
||||
[Fact]
|
||||
public void CountVowelsHostFunctions()
|
||||
{
|
||||
using var context = new Context();
|
||||
|
||||
var userData = Marshal.StringToHGlobalAnsi("Hello again!");
|
||||
|
||||
using var helloWorld = new HostFunction(
|
||||
@@ -51,7 +36,7 @@ public class BasicTests
|
||||
|
||||
var binDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
|
||||
var wasm = File.ReadAllBytes(Path.Combine(binDirectory, "code-functions.wasm"));
|
||||
using var plugin = context.CreatePlugin(wasm, new[] { helloWorld }, withWasi: true);
|
||||
using var plugin = new Plugin(wasm, new[] { helloWorld }, withWasi: true);
|
||||
|
||||
var response = plugin.CallFunction("count_vowels", Encoding.UTF8.GetBytes("Hello World"));
|
||||
Assert.Equal("{\"count\": 3}", Encoding.UTF8.GetString(response));
|
||||
|
||||
116
dscanner.ini
Normal file
116
dscanner.ini
Normal file
@@ -0,0 +1,116 @@
|
||||
; Configure which static analysis checks are enabled
|
||||
[analysis.config.StaticAnalysisConfig]
|
||||
; Check variable, class, struct, interface, union, and function names against t
|
||||
; he Phobos style guide
|
||||
style_check="enabled"
|
||||
; Check for array literals that cause unnecessary allocation
|
||||
enum_array_literal_check="enabled"
|
||||
; Check for poor exception handling practices
|
||||
exception_check="enabled"
|
||||
; Check for use of the deprecated 'delete' keyword
|
||||
delete_check="enabled"
|
||||
; Check for use of the deprecated floating point operators
|
||||
float_operator_check="enabled"
|
||||
; Check number literals for readability
|
||||
number_style_check="enabled"
|
||||
; Checks that opEquals, opCmp, toHash, and toString are either const, immutable
|
||||
; , or inout.
|
||||
object_const_check="enabled"
|
||||
; Checks for .. expressions where the left side is larger than the right.
|
||||
backwards_range_check="enabled"
|
||||
; Checks for if statements whose 'then' block is the same as the 'else' block
|
||||
if_else_same_check="enabled"
|
||||
; Checks for some problems with constructors
|
||||
constructor_check="enabled"
|
||||
; Checks for unused variables
|
||||
unused_variable_check="enabled"
|
||||
; Checks for unused labels
|
||||
unused_label_check="enabled"
|
||||
; Checks for unused function parameters
|
||||
unused_parameter_check="enabled"
|
||||
; Checks for duplicate attributes
|
||||
duplicate_attribute="enabled"
|
||||
; Checks that opEquals and toHash are both defined or neither are defined
|
||||
opequals_tohash_check="enabled"
|
||||
; Checks for subtraction from .length properties
|
||||
length_subtraction_check="enabled"
|
||||
; Checks for methods or properties whose names conflict with built-in propertie
|
||||
; s
|
||||
builtin_property_names_check="enabled"
|
||||
; Checks for confusing code in inline asm statements
|
||||
asm_style_check="enabled"
|
||||
; Checks for confusing logical operator precedence
|
||||
logical_precedence_check="enabled"
|
||||
; Checks for undocumented public declarations
|
||||
undocumented_declaration_check="enabled"
|
||||
; Checks for poor placement of function attributes
|
||||
function_attribute_check="enabled"
|
||||
; Checks for use of the comma operator
|
||||
comma_expression_check="enabled"
|
||||
; Checks for local imports that are too broad. Only accurate when checking code
|
||||
; used with D versions older than 2.071.0
|
||||
local_import_check="disabled"
|
||||
; Checks for variables that could be declared immutable
|
||||
could_be_immutable_check="enabled"
|
||||
; Checks for redundant expressions in if statements
|
||||
redundant_if_check="enabled"
|
||||
; Checks for redundant parenthesis
|
||||
redundant_parens_check="enabled"
|
||||
; Checks for mismatched argument and parameter names
|
||||
mismatched_args_check="enabled"
|
||||
; Checks for labels with the same name as variables
|
||||
label_var_same_name_check="enabled"
|
||||
; Checks for lines longer than `max_line_length` characters
|
||||
long_line_check="enabled"
|
||||
; The maximum line length used in `long_line_check`.
|
||||
max_line_length="120"
|
||||
; Checks for assignment to auto-ref function parameters
|
||||
auto_ref_assignment_check="enabled"
|
||||
; Checks for incorrect infinite range definitions
|
||||
incorrect_infinite_range_check="enabled"
|
||||
; Checks for asserts that are always true
|
||||
useless_assert_check="enabled"
|
||||
; Check for uses of the old-style alias syntax
|
||||
alias_syntax_check="enabled"
|
||||
; Checks for else if that should be else static if
|
||||
static_if_else_check="enabled"
|
||||
; Check for unclear lambda syntax
|
||||
lambda_return_check="enabled"
|
||||
; Check for auto function without return statement
|
||||
auto_function_check="enabled"
|
||||
; Check for sortedness of imports
|
||||
imports_sortedness="disabled"
|
||||
; Check for explicitly annotated unittests
|
||||
explicitly_annotated_unittests="disabled"
|
||||
; Check for properly documented public functions (Returns, Params)
|
||||
properly_documented_public_functions="disabled"
|
||||
; Check for useless usage of the final attribute
|
||||
final_attribute_check="enabled"
|
||||
; Check for virtual calls in the class constructors
|
||||
vcall_in_ctor="enabled"
|
||||
; Check for useless user defined initializers
|
||||
useless_initializer="disabled"
|
||||
; Check allman brace style
|
||||
allman_braces_check="disabled"
|
||||
; Check for redundant attributes
|
||||
redundant_attributes_check="enabled"
|
||||
; Check public declarations without a documented unittest
|
||||
has_public_example="disabled"
|
||||
; Check for asserts without an explanatory message
|
||||
assert_without_msg="disabled"
|
||||
; Check indent of if constraints
|
||||
if_constraints_indent="disabled"
|
||||
; Check for @trusted applied to a bigger scope than a single function
|
||||
trust_too_much="enabled"
|
||||
; Check for redundant storage classes on variable declarations
|
||||
redundant_storage_classes="enabled"
|
||||
; Check for unused function return values
|
||||
unused_result="enabled"
|
||||
; Enable cyclomatic complexity check
|
||||
cyclomatic_complexity="disabled"
|
||||
; Maximum cyclomatic complexity after which to issue warnings
|
||||
max_cyclomatic_complexity="50"
|
||||
; Check for function bodies on disabled functions
|
||||
body_on_disabled_func_check="enabled"
|
||||
; ModuleFilters for selectively enabling (+std) and disabling (-std.internal) individual checks
|
||||
[analysis.config.ModuleFilters]
|
||||
23
dub.json
Normal file
23
dub.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "extism",
|
||||
"description": "The Universal Plug-in System. Extend anything with WebAssembly (wasm).",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
"Chance Snow <git@chancesnow.me>",
|
||||
"Extism contributors"
|
||||
],
|
||||
"copyright": "Copyright © 2023, Extism contributors",
|
||||
"toolchainRequirements": {
|
||||
"frontend": ">=2.102"
|
||||
},
|
||||
"targetPath": "target",
|
||||
"sourceFiles": ["d/extism.d"],
|
||||
"importPaths": ["d"],
|
||||
"systemDependencies": "extism >= 0.4.0",
|
||||
"targetType": "sourceLibrary",
|
||||
"subPackages": [
|
||||
"d/examples/hello"
|
||||
],
|
||||
"dflags": ["-P-I$EXTISM_PACKAGE_DIR"],
|
||||
"libs": ["extism"]
|
||||
}
|
||||
@@ -29,6 +29,7 @@
|
||||
(extism-manifest (= :version))
|
||||
(ppx_inline_test (>= v0.15.0))
|
||||
(cmdliner (>= 1.1.1))
|
||||
(uuidm (>= 0.9.0))
|
||||
)
|
||||
(tags
|
||||
(topics wasm plugin)))
|
||||
|
||||
@@ -5,7 +5,7 @@ prepare:
|
||||
mix compile
|
||||
|
||||
test: prepare
|
||||
mix test
|
||||
mix test -v
|
||||
|
||||
clean:
|
||||
mix clean
|
||||
|
||||
@@ -1,73 +1,3 @@
|
||||
# Extism
|
||||
# Elixir Host SDK
|
||||
|
||||
Extism Host SDK for Elixir and Erlang
|
||||
|
||||
## Docs
|
||||
|
||||
Read the [docs on hexdocs.pm](https://hexdocs.pm/extism/).
|
||||
|
||||
## Installation
|
||||
|
||||
You can find this package on [hex.pm](https://hex.pm/packages/extism).
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:extism, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Example
|
||||
|
||||
```elixir
|
||||
# Create a context for which plugins can be allocated and cleaned
|
||||
ctx = Extism.Context.new()
|
||||
|
||||
# point to some wasm code, this is the count_vowels example that ships with extism
|
||||
manifest = %{ wasm: [ %{ path: "/Users/ben/code/extism/wasm/code.wasm" } ]}
|
||||
{:ok, plugin} = Extism.Context.new_plugin(ctx, manifest, false)
|
||||
# {:ok,
|
||||
# %Extism.Plugin{
|
||||
# resource: 0,
|
||||
# reference: #Reference<0.520418104.1263009793.80956>
|
||||
# }}
|
||||
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "this is a test")
|
||||
# {:ok, "{\"count\": 4}"}
|
||||
{:ok, result} = JSON.decode(output)
|
||||
# {:ok, %{"count" => 4}}
|
||||
|
||||
# free up the context and any plugins we allocated
|
||||
Extism.Context.free(ctx)
|
||||
```
|
||||
|
||||
### Modules
|
||||
|
||||
The two primary modules you should learn are:
|
||||
|
||||
* [Extism.Context](Extism.Context.html)
|
||||
* [Extism.Plugin](Extism.Plugin.html)
|
||||
|
||||
#### Context
|
||||
|
||||
The [Context](Extism.Context.html) can be thought of as a session. You need a context to interact with the Extism runtime. The context holds your plugins and when you free the context, it frees your plugins. It's important to free up your context and plugins when you are done with them.
|
||||
|
||||
```elixir
|
||||
ctx = Extism.Context.new()
|
||||
# frees all the plugins
|
||||
Extism.Context.reset(ctx)
|
||||
# frees the context and all its plugins
|
||||
Extism.Context.free(ctx)
|
||||
```
|
||||
|
||||
#### Plugin
|
||||
|
||||
The [Plugin](Extism.Plugin.html) represents an instance of your WASM program from the given manifest.
|
||||
The key method to know here is [Extism.Plugin#call](Extism.Plugin.html#call/3) which takes a function name to invoke and some input data, and returns the results from the plugin.
|
||||
|
||||
```elixir
|
||||
{:ok, plugin} = Extism.Context.new_plugin(ctx, manifest, false)
|
||||
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "this is a test")
|
||||
```
|
||||
This contains the `0.x` version of the SDK. Development of this library has moved to [this repo](https://github.com/extism/elixir-sdk#readme).
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule Extism.CancelHandle do
|
||||
thread while running.
|
||||
"""
|
||||
defstruct [
|
||||
# The actual NIF Resource. PluginIndex and the context
|
||||
# The actual NIF Resource
|
||||
handle: nil
|
||||
]
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
defmodule Extism.Context do
|
||||
@moduledoc """
|
||||
A Context is needed to create plugins. The Context is where your plugins
|
||||
live. Freeing the context frees all of the plugins in its scope.
|
||||
"""
|
||||
|
||||
defstruct [
|
||||
# The actual NIF Resource. A pointer in this case
|
||||
ptr: nil
|
||||
]
|
||||
|
||||
def wrap_resource(ptr) do
|
||||
%__MODULE__{
|
||||
ptr: ptr
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a new context.
|
||||
"""
|
||||
def new() do
|
||||
ptr = Extism.Native.context_new()
|
||||
Extism.Context.wrap_resource(ptr)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Resets the context. This has the effect of freeing all the plugins created so far.
|
||||
"""
|
||||
def reset(ctx) do
|
||||
Extism.Native.context_reset(ctx.ptr)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Frees the context from memory and all of its plugins.
|
||||
"""
|
||||
def free(ctx) do
|
||||
Extism.Native.context_free(ctx.ptr)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Create a new plugin from a WASM module or manifest
|
||||
|
||||
## Examples:
|
||||
|
||||
iex> ctx = Extism.Context.new()
|
||||
iex> manifest = %{ wasm: [ %{ path: "/Users/ben/code/extism/wasm/code.wasm" } ]}
|
||||
iex> {:ok, plugin} = Extism.Context.new_plugin(ctx, manifest, false)
|
||||
|
||||
## Parameters
|
||||
|
||||
- ctx: The Context to manage this plugin
|
||||
- manifest: The String or Map of the WASM module or [manifest](https://extism.org/docs/concepts/manifest)
|
||||
- wasi: A bool you set to true if you want WASI support
|
||||
|
||||
"""
|
||||
def new_plugin(ctx, manifest, wasi \\ false) do
|
||||
{:ok, manifest_payload} = JSON.encode(manifest)
|
||||
|
||||
case Extism.Native.plugin_new_with_manifest(ctx.ptr, manifest_payload, wasi) do
|
||||
{:error, err} -> {:error, err}
|
||||
res -> {:ok, Extism.Plugin.wrap_resource(ctx, res)}
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -7,16 +7,12 @@ defmodule Extism.Native do
|
||||
otp_app: :extism,
|
||||
crate: :extism_nif
|
||||
|
||||
def context_new(), do: error()
|
||||
def context_reset(_ctx), do: error()
|
||||
def context_free(_ctx), do: error()
|
||||
def plugin_new_with_manifest(_ctx, _manifest, _wasi), do: error()
|
||||
def plugin_call(_ctx, _plugin_id, _name, _input), do: error()
|
||||
def plugin_update_manifest(_ctx, _plugin_id, _manifest, _wasi), do: error()
|
||||
def plugin_has_function(_ctx, _plugin_id, _function_name), do: error()
|
||||
def plugin_free(_ctx, _plugin_id), do: error()
|
||||
def plugin_new_with_manifest(_manifest, _wasi), do: error()
|
||||
def plugin_call(_plugin, _name, _input), do: error()
|
||||
def plugin_has_function(_plugin, _function_name), do: error()
|
||||
def plugin_free(_plugin), do: error()
|
||||
def set_log_file(_filename, _level), do: error()
|
||||
def plugin_cancel_handle(_ctx, _plugin_id), do: error()
|
||||
def plugin_cancel_handle(_plugin), do: error()
|
||||
def plugin_cancel(_handle), do: error()
|
||||
|
||||
defp error, do: :erlang.nif_error(:nif_not_loaded)
|
||||
|
||||
@@ -3,28 +3,25 @@ defmodule Extism.Plugin do
|
||||
A Plugin represents an instance of your WASM program from the given manifest.
|
||||
"""
|
||||
defstruct [
|
||||
# The actual NIF Resource. PluginIndex and the context
|
||||
plugin_id: nil,
|
||||
ctx: nil
|
||||
# The actual NIF Resource
|
||||
plugin: nil,
|
||||
]
|
||||
|
||||
def wrap_resource(ctx, plugin_id) do
|
||||
def wrap_resource(plugin) do
|
||||
%__MODULE__{
|
||||
ctx: ctx,
|
||||
plugin_id: plugin_id
|
||||
plugin: plugin
|
||||
}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a new plugin
|
||||
"""
|
||||
def new(manifest, wasi \\ false, context \\ nil) do
|
||||
ctx = context || Extism.Context.new()
|
||||
def new(manifest, wasi \\ false) do
|
||||
{:ok, manifest_payload} = JSON.encode(manifest)
|
||||
|
||||
case Extism.Native.plugin_new_with_manifest(ctx.ptr, manifest_payload, wasi) do
|
||||
case Extism.Native.plugin_new_with_manifest(manifest_payload, wasi) do
|
||||
{:error, err} -> {:error, err}
|
||||
res -> {:ok, Extism.Plugin.wrap_resource(ctx, res)}
|
||||
res -> {:ok, Extism.Plugin.wrap_resource(res)}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -49,49 +46,24 @@ defmodule Extism.Plugin do
|
||||
|
||||
"""
|
||||
def call(plugin, name, input) do
|
||||
case Extism.Native.plugin_call(plugin.ctx.ptr, plugin.plugin_id, name, input) do
|
||||
case Extism.Native.plugin_call(plugin.plugin, name, input) do
|
||||
{:error, err} -> {:error, err}
|
||||
res -> {:ok, res}
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates the manifest of the given plugin
|
||||
|
||||
## Parameters
|
||||
|
||||
- ctx: The Context to manage this plugin
|
||||
- manifest: The String or Map of the WASM module or [manifest](https://extism.org/docs/concepts/manifest)
|
||||
- wasi: A bool you set to true if you want WASI support
|
||||
|
||||
|
||||
"""
|
||||
def update(plugin, manifest, wasi) when is_map(manifest) do
|
||||
{:ok, manifest_payload} = JSON.encode(manifest)
|
||||
|
||||
case Extism.Native.plugin_update_manifest(
|
||||
plugin.ctx.ptr,
|
||||
plugin.plugin_id,
|
||||
manifest_payload,
|
||||
wasi
|
||||
) do
|
||||
{:error, err} -> {:error, err}
|
||||
_ -> :ok
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Frees the plugin
|
||||
"""
|
||||
def free(plugin) do
|
||||
Extism.Native.plugin_free(plugin.ctx.ptr, plugin.plugin_id)
|
||||
Extism.Native.plugin_free(plugin.plugin)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns true if the given plugin responds to the given function name
|
||||
"""
|
||||
def has_function(plugin, function_name) do
|
||||
Extism.Native.plugin_has_function(plugin.ctx.ptr, plugin.plugin_id, function_name)
|
||||
Extism.Native.plugin_has_function(plugin.plugin, function_name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -99,6 +71,6 @@ defimpl Inspect, for: Extim.Plugin do
|
||||
import Inspect.Algebra
|
||||
|
||||
def inspect(dict, opts) do
|
||||
concat(["#Extism.Plugin<", to_doc(dict.plugin_id, opts), ">"])
|
||||
concat(["#Extism.Plugin<", to_doc(dict.plugin, opts), ">"])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@ defmodule Extism.MixProject do
|
||||
def project do
|
||||
[
|
||||
app: :extism,
|
||||
version: "0.5.1",
|
||||
version: "0.5.0",
|
||||
elixir: "~> 1.12",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%{
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.33", "3c3fd9673bb5dcc9edc28dd90f50c87ce506d1f71b70e3de69aa8154bc695d44", [:mix], [], "hexpm", "2d526833729b59b9fdb85785078697c72ac5e5066350663e5be6a1182da61b8f"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.30.5", "aa6da96a5c23389d7dc7c381eba862710e108cee9cfdc629b7ec021313900e9e", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "88a1e115dcb91cefeef7e22df4a6ebbe4634fbf98b38adcbc25c9607d6d9d8e6"},
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.37", "2ad73550e27c8946648b06905a57e4d454e4d7229c2dafa72a0348c99d8be5f7", [:mix], [], "hexpm", "6b19783f2802f039806f375610faa22da130b8edc21209d0bff47918bb48360e"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.30.7", "dc7247091aec738ab781f71cbebfc07979d1040c98c7ee67dbde99b7829b743d", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "868ff1c7a44c462741853840d1e7ef19a07906e7467cb8da070c158ea6a42a51"},
|
||||
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
|
||||
"json": {:hex, :json, "1.4.1", "8648f04a9439765ad449bc56a3ff7d8b11dd44ff08ffcdefc4329f7c93843dfa", [:mix], [], "hexpm", "9abf218dbe4ea4fcb875e087d5f904ef263d012ee5ed21d46e9dbca63f053d16"},
|
||||
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
|
||||
|
||||
@@ -13,6 +13,6 @@ crate-type = ["cdylib"]
|
||||
[workspace]
|
||||
|
||||
[dependencies]
|
||||
rustler = "0.29.1"
|
||||
extism = "0.5.2"
|
||||
rustler = "0.28.0"
|
||||
extism = {path = "../../../runtime"} # "0.5.0"
|
||||
log = "0.4"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use extism::{Context, Plugin};
|
||||
use extism::Plugin;
|
||||
use rustler::{Atom, Env, ResourceArc, Term};
|
||||
use std::mem;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
use std::str::FromStr;
|
||||
@@ -14,11 +13,11 @@ mod atoms {
|
||||
}
|
||||
}
|
||||
|
||||
struct ExtismContext {
|
||||
ctx: RwLock<Context>,
|
||||
struct ExtismPlugin {
|
||||
plugin: RwLock<Option<Plugin>>,
|
||||
}
|
||||
unsafe impl Sync for ExtismContext {}
|
||||
unsafe impl Send for ExtismContext {}
|
||||
unsafe impl Sync for ExtismPlugin {}
|
||||
unsafe impl Send for ExtismPlugin {}
|
||||
|
||||
struct ExtismCancelHandle {
|
||||
handle: RwLock<extism::CancelHandle>,
|
||||
@@ -28,7 +27,7 @@ unsafe impl Sync for ExtismCancelHandle {}
|
||||
unsafe impl Send for ExtismCancelHandle {}
|
||||
|
||||
fn load(env: Env, _: Term) -> bool {
|
||||
rustler::resource!(ExtismContext, env);
|
||||
rustler::resource!(ExtismPlugin, env);
|
||||
rustler::resource!(ExtismCancelHandle, env);
|
||||
true
|
||||
}
|
||||
@@ -37,111 +36,73 @@ fn to_rustler_error(extism_error: extism::Error) -> rustler::Error {
|
||||
rustler::Error::Term(Box::new(extism_error.to_string()))
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn context_new() -> ResourceArc<ExtismContext> {
|
||||
ResourceArc::new(ExtismContext {
|
||||
ctx: RwLock::new(Context::new()),
|
||||
})
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn context_reset(ctx: ResourceArc<ExtismContext>) {
|
||||
let context = &mut ctx.ctx.write().unwrap();
|
||||
context.reset()
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn context_free(ctx: ResourceArc<ExtismContext>) {
|
||||
let context = ctx.ctx.read().unwrap();
|
||||
std::mem::drop(context)
|
||||
fn freed_error() -> rustler::Error {
|
||||
rustler::Error::Term(Box::new("Plugin has already been freed".to_string()))
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_new_with_manifest(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
manifest_payload: String,
|
||||
wasi: bool,
|
||||
) -> Result<i32, rustler::Error> {
|
||||
let context = ctx.ctx.write().unwrap();
|
||||
let result = match Plugin::new(&context, manifest_payload, [], wasi) {
|
||||
) -> Result<ResourceArc<ExtismPlugin>, rustler::Error> {
|
||||
let result = match Plugin::new(manifest_payload, [], wasi) {
|
||||
Err(e) => Err(to_rustler_error(e)),
|
||||
Ok(plugin) => {
|
||||
let plugin_id = plugin.as_i32();
|
||||
// this forget should be safe because the context will clean up
|
||||
// all it's plugins when it is dropped
|
||||
mem::forget(plugin);
|
||||
Ok(plugin_id)
|
||||
}
|
||||
Ok(plugin) => Ok(ResourceArc::new(ExtismPlugin {
|
||||
plugin: RwLock::new(Some(plugin)),
|
||||
})),
|
||||
};
|
||||
result
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_call(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
plugin_id: i32,
|
||||
plugin: ResourceArc<ExtismPlugin>,
|
||||
name: String,
|
||||
input: String,
|
||||
) -> Result<String, rustler::Error> {
|
||||
let context = &ctx.ctx.read().unwrap();
|
||||
let mut plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
let result = match plugin.call(name, input) {
|
||||
Err(e) => Err(to_rustler_error(e)),
|
||||
Ok(result) => match str::from_utf8(result) {
|
||||
Ok(output) => Ok(output.to_string()),
|
||||
Err(_e) => Err(rustler::Error::Term(Box::new(
|
||||
"Could not read output from plugin",
|
||||
))),
|
||||
},
|
||||
};
|
||||
// this forget should be safe because the context will clean up
|
||||
// all it's plugins when it is dropped
|
||||
mem::forget(plugin);
|
||||
result
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_update_manifest(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
plugin_id: i32,
|
||||
manifest_payload: String,
|
||||
wasi: bool,
|
||||
) -> Result<(), rustler::Error> {
|
||||
let context = &ctx.ctx.read().unwrap();
|
||||
let mut plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
let result = match plugin.update(manifest_payload, [], wasi) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => Err(to_rustler_error(e)),
|
||||
};
|
||||
// this forget should be safe because the context will clean up
|
||||
// all it's plugins when it is dropped
|
||||
mem::forget(plugin);
|
||||
result
|
||||
let mut plugin = plugin.plugin.write().unwrap();
|
||||
if let Some(plugin) = &mut *plugin {
|
||||
let result = match plugin.call(name, input) {
|
||||
Err(e) => Err(to_rustler_error(e)),
|
||||
Ok(result) => match str::from_utf8(result) {
|
||||
Ok(output) => Ok(output.to_string()),
|
||||
Err(_e) => Err(rustler::Error::Term(Box::new(
|
||||
"Could not read output from plugin",
|
||||
))),
|
||||
},
|
||||
};
|
||||
result
|
||||
} else {
|
||||
Err(freed_error())
|
||||
}
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_cancel_handle(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
plugin_id: i32,
|
||||
plugin: ResourceArc<ExtismPlugin>,
|
||||
) -> Result<ResourceArc<ExtismCancelHandle>, rustler::Error> {
|
||||
let context = &ctx.ctx.read().unwrap();
|
||||
let plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
let handle = plugin.cancel_handle();
|
||||
Ok(ResourceArc::new(ExtismCancelHandle {
|
||||
handle: RwLock::new(handle),
|
||||
}))
|
||||
let mut plugin = plugin.plugin.write().unwrap();
|
||||
if let Some(plugin) = &mut *plugin {
|
||||
let handle = plugin.cancel_handle();
|
||||
Ok(ResourceArc::new(ExtismCancelHandle {
|
||||
handle: RwLock::new(handle),
|
||||
}))
|
||||
} else {
|
||||
Err(freed_error())
|
||||
}
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_cancel(handle: ResourceArc<ExtismCancelHandle>) -> bool {
|
||||
handle.handle.read().unwrap().cancel()
|
||||
handle.handle.read().unwrap().cancel().is_ok()
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_free(ctx: ResourceArc<ExtismContext>, plugin_id: i32) -> Result<(), rustler::Error> {
|
||||
let context = &ctx.ctx.read().unwrap();
|
||||
let plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
std::mem::drop(plugin);
|
||||
fn plugin_free(plugin: ResourceArc<ExtismPlugin>) -> Result<(), rustler::Error> {
|
||||
let mut plugin = plugin.plugin.write().unwrap();
|
||||
if let Some(plugin) = plugin.take() {
|
||||
drop(plugin);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -153,42 +114,34 @@ fn set_log_file(filename: String, log_level: String) -> Result<Atom, rustler::Er
|
||||
"{} not a valid log level",
|
||||
log_level
|
||||
)))),
|
||||
Ok(level) => {
|
||||
if extism::set_log_file(path, Some(level)) {
|
||||
Ok(atoms::ok())
|
||||
} else {
|
||||
Err(rustler::Error::Term(Box::new(
|
||||
"Did not set log file, received false from the API.",
|
||||
)))
|
||||
}
|
||||
}
|
||||
Ok(level) => match extism::set_log_file(path, level) {
|
||||
Ok(()) => Ok(atoms::ok()),
|
||||
Err(e) => Err(rustler::Error::Term(Box::new(format!(
|
||||
"Did not set log file: {e:?}"
|
||||
)))),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[rustler::nif]
|
||||
fn plugin_has_function(
|
||||
ctx: ResourceArc<ExtismContext>,
|
||||
plugin_id: i32,
|
||||
plugin: ResourceArc<ExtismPlugin>,
|
||||
function_name: String,
|
||||
) -> Result<bool, rustler::Error> {
|
||||
let context = &ctx.ctx.read().unwrap();
|
||||
let plugin = unsafe { Plugin::from_id(plugin_id, context) };
|
||||
let has_function = plugin.has_function(function_name);
|
||||
// this forget should be safe because the context will clean up
|
||||
// all it's plugins when it is dropped
|
||||
mem::forget(plugin);
|
||||
Ok(has_function)
|
||||
let mut plugin = plugin.plugin.write().unwrap();
|
||||
if let Some(plugin) = &mut *plugin {
|
||||
let has_function = plugin.function_exists(function_name);
|
||||
Ok(has_function)
|
||||
} else {
|
||||
Err(freed_error())
|
||||
}
|
||||
}
|
||||
|
||||
rustler::init!(
|
||||
"Elixir.Extism.Native",
|
||||
[
|
||||
context_new,
|
||||
context_reset,
|
||||
context_free,
|
||||
plugin_new_with_manifest,
|
||||
plugin_call,
|
||||
plugin_update_manifest,
|
||||
plugin_has_function,
|
||||
plugin_cancel_handle,
|
||||
plugin_cancel,
|
||||
|
||||
@@ -2,33 +2,21 @@ defmodule ExtismTest do
|
||||
use ExUnit.Case
|
||||
doctest Extism
|
||||
|
||||
test "context create & reset" do
|
||||
ctx = Extism.Context.new()
|
||||
path = Path.join([__DIR__, "../../wasm/code.wasm"])
|
||||
manifest = %{wasm: [%{path: path}]}
|
||||
{:ok, plugin} = Extism.Context.new_plugin(ctx, manifest, false)
|
||||
Extism.Context.reset(ctx)
|
||||
# we should expect an error after resetting context
|
||||
{:error, _err} = Extism.Plugin.call(plugin, "count_vowels", "this is a test")
|
||||
end
|
||||
|
||||
defp new_plugin() do
|
||||
ctx = Extism.Context.new()
|
||||
path = Path.join([__DIR__, "../../wasm/code.wasm"])
|
||||
manifest = %{wasm: [%{path: path}]}
|
||||
{:ok, plugin} = Extism.Context.new_plugin(ctx, manifest, false)
|
||||
{ctx, plugin}
|
||||
{:ok, plugin} = Extism.Plugin.new(manifest, false)
|
||||
plugin
|
||||
end
|
||||
|
||||
test "counts vowels" do
|
||||
{ctx, plugin} = new_plugin()
|
||||
plugin = new_plugin()
|
||||
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "this is a test")
|
||||
assert JSON.decode(output) == {:ok, %{"count" => 4}}
|
||||
Extism.Context.free(ctx)
|
||||
end
|
||||
|
||||
test "can make multiple calls on a plugin" do
|
||||
{ctx, plugin} = new_plugin()
|
||||
plugin = new_plugin()
|
||||
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "this is a test")
|
||||
assert JSON.decode(output) == {:ok, %{"count" => 4}}
|
||||
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "this is a test again")
|
||||
@@ -37,37 +25,24 @@ defmodule ExtismTest do
|
||||
assert JSON.decode(output) == {:ok, %{"count" => 6}}
|
||||
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "🌎hello🌎world🌎")
|
||||
assert JSON.decode(output) == {:ok, %{"count" => 3}}
|
||||
Extism.Context.free(ctx)
|
||||
end
|
||||
|
||||
test "can free a plugin" do
|
||||
{ctx, plugin} = new_plugin()
|
||||
plugin = new_plugin()
|
||||
{:ok, output} = Extism.Plugin.call(plugin, "count_vowels", "this is a test")
|
||||
assert JSON.decode(output) == {:ok, %{"count" => 4}}
|
||||
Extism.Plugin.free(plugin)
|
||||
# Expect an error when calling a plugin that was freed
|
||||
{:error, _err} = Extism.Plugin.call(plugin, "count_vowels", "this is a test")
|
||||
Extism.Context.free(ctx)
|
||||
end
|
||||
|
||||
test "can update manifest" do
|
||||
{ctx, plugin} = new_plugin()
|
||||
path = Path.join([__DIR__, "../../wasm/code.wasm"])
|
||||
manifest = %{wasm: [%{path: path}]}
|
||||
assert Extism.Plugin.update(plugin, manifest, true) == :ok
|
||||
Extism.Context.free(ctx)
|
||||
end
|
||||
|
||||
test "errors on bad manifest" do
|
||||
ctx = Extism.Context.new()
|
||||
{:error, _msg} = Extism.Context.new_plugin(ctx, %{"wasm" => 123}, false)
|
||||
Extism.Context.free(ctx)
|
||||
{:error, _msg} = Extism.Plugin.new(%{"wasm" => 123}, false)
|
||||
end
|
||||
|
||||
test "errors on unknown function" do
|
||||
{ctx, plugin} = new_plugin()
|
||||
plugin = new_plugin()
|
||||
{:error, _msg} = Extism.Plugin.call(plugin, "unknown", "this is a test")
|
||||
Extism.Context.free(ctx)
|
||||
end
|
||||
|
||||
test "set_log_file" do
|
||||
@@ -75,9 +50,8 @@ defmodule ExtismTest do
|
||||
end
|
||||
|
||||
test "has_function" do
|
||||
{ctx, plugin} = new_plugin()
|
||||
plugin = new_plugin()
|
||||
assert Extism.Plugin.has_function(plugin, "count_vowels")
|
||||
assert !Extism.Plugin.has_function(plugin, "unknown")
|
||||
Extism.Context.free(ctx)
|
||||
end
|
||||
end
|
||||
|
||||
14
extism-maturin/Cargo.toml
Normal file
14
extism-maturin/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "extism-sys"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "extism_sys"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
extism = {path = "../runtime"}
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1"
|
||||
57
extism-maturin/build.rs
Normal file
57
extism-maturin/build.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
fn rewrite(line: &'_ str) -> Option<Cow<'_, str>> {
|
||||
let line = line.trim();
|
||||
|
||||
if line.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if line.starts_with('#') {
|
||||
return None;
|
||||
}
|
||||
|
||||
if cfg!(target_os = "macos") {
|
||||
if line.starts_with("typedef __builtin_va_list ") {
|
||||
return None
|
||||
}
|
||||
} else if cfg!(target_os = "windows") {
|
||||
if line.contains("__gnuc_va_list") ||
|
||||
line.starts_with("__pragma") ||
|
||||
line.contains("__attribute__") ||
|
||||
line.contains("uintptr_t") ||
|
||||
line.contains("intptr_t") ||
|
||||
line.contains("size_t") ||
|
||||
line.contains("ptrdiff_t") {
|
||||
return None
|
||||
}
|
||||
|
||||
return Some(Cow::Owned(line.replace("__attribute__((__cdecl__))", "")))
|
||||
};
|
||||
|
||||
Some(Cow::Borrowed(line))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=src/extism.c");
|
||||
println!("cargo:rerun-if-changed=../runtime/extism.h");
|
||||
std::fs::copy("../runtime/extism.h", "src/extism.h").unwrap();
|
||||
|
||||
let data = String::from_utf8(
|
||||
cc::Build::new()
|
||||
.file("src/extism.c")
|
||||
.warnings(false)
|
||||
.extra_warnings(false)
|
||||
.expand(),
|
||||
)
|
||||
.unwrap();
|
||||
let data: Vec<&str> = data.split('\n').collect();
|
||||
let data: String = data
|
||||
.into_iter()
|
||||
.filter_map(rewrite)
|
||||
.collect::<Vec<Cow<'_, str>>>()
|
||||
.join("\n\n");
|
||||
|
||||
|
||||
std::fs::write("../target/header.h", data).unwrap();
|
||||
}
|
||||
17
extism-maturin/pyproject.toml
Normal file
17
extism-maturin/pyproject.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[build-system]
|
||||
requires = ["maturin>=1.1,<2.0"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "extism-sys"
|
||||
version = "0.0.0+replaced-by-ci"
|
||||
requires-python = ">=3.7"
|
||||
classifiers = [
|
||||
"Programming Language :: Rust",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
]
|
||||
dependencies = ["cffi"]
|
||||
|
||||
[tool.maturin]
|
||||
bindings = "cffi"
|
||||
1
extism-maturin/src/extism.c
Normal file
1
extism-maturin/src/extism.c
Normal file
@@ -0,0 +1 @@
|
||||
#include "extism.h"
|
||||
1
extism-maturin/src/lib.rs
Normal file
1
extism-maturin/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub use extism::sdk::*;
|
||||
203
extism.go
203
extism.go
@@ -52,11 +52,6 @@ void extism_val_set_f64(ExtismValUnion* x, double f){
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Context is used to manage Plugins
|
||||
type Context struct {
|
||||
pointer *C.ExtismContext
|
||||
}
|
||||
|
||||
type ValType = C.ExtismValType
|
||||
|
||||
type Val = C.ExtismVal
|
||||
@@ -81,9 +76,11 @@ type Function struct {
|
||||
|
||||
// Free a function
|
||||
func (f *Function) Free() {
|
||||
C.extism_function_free(f.pointer)
|
||||
f.pointer = nil
|
||||
f.userData.Delete()
|
||||
if f.pointer != nil {
|
||||
C.extism_function_free(f.pointer)
|
||||
f.pointer = nil
|
||||
f.userData.Delete()
|
||||
}
|
||||
}
|
||||
|
||||
// NewFunction creates a new host function with the given name, input/outputs and optional user data, which can be an
|
||||
@@ -136,45 +133,32 @@ func GetCurrentPlugin(ptr unsafe.Pointer) CurrentPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *CurrentPlugin) Memory(offs uint) []byte {
|
||||
type MemoryHandle = uint
|
||||
|
||||
func (p *CurrentPlugin) Memory(offs MemoryHandle) []byte {
|
||||
length := C.extism_current_plugin_memory_length(p.pointer, C.uint64_t(offs))
|
||||
data := unsafe.Pointer(C.extism_current_plugin_memory(p.pointer))
|
||||
return unsafe.Slice((*byte)(unsafe.Add(data, offs)), C.int(length))
|
||||
}
|
||||
|
||||
// Alloc a new memory block of the given length, returning its offset
|
||||
func (p *CurrentPlugin) Alloc(n uint) uint {
|
||||
func (p *CurrentPlugin) Alloc(n uint) MemoryHandle {
|
||||
return uint(C.extism_current_plugin_memory_alloc(p.pointer, C.uint64_t(n)))
|
||||
}
|
||||
|
||||
// Free the memory block specified by the given offset
|
||||
func (p *CurrentPlugin) Free(offs uint) {
|
||||
func (p *CurrentPlugin) Free(offs MemoryHandle) {
|
||||
C.extism_current_plugin_memory_free(p.pointer, C.uint64_t(offs))
|
||||
}
|
||||
|
||||
// Length returns the number of bytes allocated at the specified offset
|
||||
func (p *CurrentPlugin) Length(offs uint) uint {
|
||||
return uint(C.extism_current_plugin_memory_length(p.pointer, C.uint64_t(offs)))
|
||||
}
|
||||
|
||||
// NewContext creates a new context, it should be freed using the `Free` method
|
||||
func NewContext() Context {
|
||||
p := C.extism_context_new()
|
||||
return Context{
|
||||
pointer: p,
|
||||
}
|
||||
}
|
||||
|
||||
// Free a context
|
||||
func (ctx *Context) Free() {
|
||||
C.extism_context_free(ctx.pointer)
|
||||
ctx.pointer = nil
|
||||
func (p *CurrentPlugin) Length(offs MemoryHandle) int {
|
||||
return int(C.extism_current_plugin_memory_length(p.pointer, C.uint64_t(offs)))
|
||||
}
|
||||
|
||||
// Plugin is used to call WASM functions
|
||||
type Plugin struct {
|
||||
ctx *Context
|
||||
id int32
|
||||
ptr *C.ExtismPlugin
|
||||
functions []Function
|
||||
}
|
||||
|
||||
@@ -234,175 +218,99 @@ func ExtismVersion() string {
|
||||
return C.GoString(C.extism_version())
|
||||
}
|
||||
|
||||
func register(ctx *Context, data []byte, functions []Function, wasi bool) (Plugin, error) {
|
||||
func register(data []byte, functions []Function, wasi bool) (Plugin, error) {
|
||||
ptr := makePointer(data)
|
||||
functionPointers := []*C.ExtismFunction{}
|
||||
for _, f := range functions {
|
||||
functionPointers = append(functionPointers, f.pointer)
|
||||
}
|
||||
plugin := C.int32_t(-1)
|
||||
|
||||
var plugin *C.ExtismPlugin
|
||||
errmsg := (*C.char)(nil)
|
||||
if len(functions) == 0 {
|
||||
plugin = C.extism_plugin_new(
|
||||
ctx.pointer,
|
||||
(*C.uchar)(ptr),
|
||||
C.uint64_t(len(data)),
|
||||
nil,
|
||||
0,
|
||||
C._Bool(wasi))
|
||||
C._Bool(wasi),
|
||||
&errmsg)
|
||||
} else {
|
||||
plugin = C.extism_plugin_new(
|
||||
ctx.pointer,
|
||||
(*C.uchar)(ptr),
|
||||
C.uint64_t(len(data)),
|
||||
&functionPointers[0],
|
||||
C.uint64_t(len(functions)),
|
||||
C._Bool(wasi),
|
||||
&errmsg,
|
||||
)
|
||||
}
|
||||
|
||||
if plugin < 0 {
|
||||
err := C.extism_error(ctx.pointer, C.int32_t(-1))
|
||||
msg := "Unknown"
|
||||
if err != nil {
|
||||
msg = C.GoString(err)
|
||||
}
|
||||
|
||||
return Plugin{id: -1}, errors.New(
|
||||
if plugin == nil {
|
||||
msg := C.GoString(errmsg)
|
||||
C.extism_plugin_new_error_free(errmsg)
|
||||
return Plugin{}, errors.New(
|
||||
fmt.Sprintf("Unable to load plugin: %s", msg),
|
||||
)
|
||||
}
|
||||
|
||||
return Plugin{id: int32(plugin), ctx: ctx, functions: functions}, nil
|
||||
return Plugin{ptr: plugin, functions: functions}, nil
|
||||
}
|
||||
|
||||
func update(ctx *Context, plugin int32, data []byte, functions []Function, wasi bool) error {
|
||||
ptr := makePointer(data)
|
||||
functionPointers := []*C.ExtismFunction{}
|
||||
for _, f := range functions {
|
||||
functionPointers = append(functionPointers, f.pointer)
|
||||
}
|
||||
|
||||
if len(functions) == 0 {
|
||||
b := bool(C.extism_plugin_update(
|
||||
ctx.pointer,
|
||||
C.int32_t(plugin),
|
||||
(*C.uchar)(ptr),
|
||||
C.uint64_t(len(data)),
|
||||
nil,
|
||||
0,
|
||||
C._Bool(wasi),
|
||||
))
|
||||
|
||||
if b {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
b := bool(C.extism_plugin_update(
|
||||
ctx.pointer,
|
||||
C.int32_t(plugin),
|
||||
(*C.uchar)(ptr),
|
||||
C.uint64_t(len(data)),
|
||||
&functionPointers[0],
|
||||
C.uint64_t(len(functions)),
|
||||
C._Bool(wasi),
|
||||
))
|
||||
|
||||
if b {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
err := C.extism_error(ctx.pointer, C.int32_t(-1))
|
||||
msg := "Unknown"
|
||||
if err != nil {
|
||||
msg = C.GoString(err)
|
||||
}
|
||||
|
||||
return errors.New(
|
||||
fmt.Sprintf("Unable to load plugin: %s", msg),
|
||||
)
|
||||
}
|
||||
|
||||
// NewPlugin creates a plugin in its own context
|
||||
// NewPlugin creates a plugin
|
||||
func NewPlugin(module io.Reader, functions []Function, wasi bool) (Plugin, error) {
|
||||
ctx := NewContext()
|
||||
return ctx.Plugin(module, functions, wasi)
|
||||
wasm, err := io.ReadAll(module)
|
||||
if err != nil {
|
||||
return Plugin{}, err
|
||||
}
|
||||
|
||||
return register(wasm, functions, wasi)
|
||||
}
|
||||
|
||||
// NewPlugin creates a plugin in its own context from a manifest
|
||||
// NewPlugin creates a plugin from a manifest
|
||||
func NewPluginFromManifest(manifest Manifest, functions []Function, wasi bool) (Plugin, error) {
|
||||
ctx := NewContext()
|
||||
return ctx.PluginFromManifest(manifest, functions, wasi)
|
||||
}
|
||||
|
||||
// PluginFromManifest creates a plugin from a `Manifest`
|
||||
func (ctx *Context) PluginFromManifest(manifest Manifest, functions []Function, wasi bool) (Plugin, error) {
|
||||
data, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
return Plugin{id: -1}, err
|
||||
return Plugin{}, err
|
||||
}
|
||||
|
||||
return register(ctx, data, functions, wasi)
|
||||
}
|
||||
|
||||
// Plugin creates a plugin from a WASM module
|
||||
func (ctx *Context) Plugin(module io.Reader, functions []Function, wasi bool) (Plugin, error) {
|
||||
wasm, err := io.ReadAll(module)
|
||||
if err != nil {
|
||||
return Plugin{id: -1}, err
|
||||
}
|
||||
|
||||
return register(ctx, wasm, functions, wasi)
|
||||
}
|
||||
|
||||
// Update a plugin with a new WASM module
|
||||
func (p *Plugin) Update(module io.Reader, functions []Function, wasi bool) error {
|
||||
wasm, err := io.ReadAll(module)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.functions = functions
|
||||
return update(p.ctx, p.id, wasm, functions, wasi)
|
||||
}
|
||||
|
||||
// Update a plugin with a new Manifest
|
||||
func (p *Plugin) UpdateManifest(manifest Manifest, functions []Function, wasi bool) error {
|
||||
data, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.functions = functions
|
||||
return update(p.ctx, p.id, data, functions, wasi)
|
||||
return register(data, functions, wasi)
|
||||
}
|
||||
|
||||
// Set configuration values
|
||||
func (plugin Plugin) SetConfig(data map[string][]byte) error {
|
||||
if plugin.ptr == nil {
|
||||
return errors.New("Cannot set config, Plugin already freed")
|
||||
}
|
||||
s, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ptr := makePointer(s)
|
||||
C.extism_plugin_config(plugin.ctx.pointer, C.int(plugin.id), (*C.uchar)(ptr), C.uint64_t(len(s)))
|
||||
C.extism_plugin_config(plugin.ptr, (*C.uchar)(ptr), C.uint64_t(len(s)))
|
||||
return nil
|
||||
}
|
||||
|
||||
// FunctionExists returns true when the named function is present in the plugin
|
||||
func (plugin Plugin) FunctionExists(functionName string) bool {
|
||||
if plugin.ptr == nil {
|
||||
return false
|
||||
}
|
||||
name := C.CString(functionName)
|
||||
b := C.extism_plugin_function_exists(plugin.ctx.pointer, C.int(plugin.id), name)
|
||||
b := C.extism_plugin_function_exists(plugin.ptr, name)
|
||||
C.free(unsafe.Pointer(name))
|
||||
return bool(b)
|
||||
}
|
||||
|
||||
// Call a function by name with the given input, returning the output
|
||||
func (plugin Plugin) Call(functionName string, input []byte) ([]byte, error) {
|
||||
if plugin.ptr == nil {
|
||||
return []byte{}, errors.New("Plugin has already been freed")
|
||||
}
|
||||
ptr := makePointer(input)
|
||||
name := C.CString(functionName)
|
||||
rc := C.extism_plugin_call(
|
||||
plugin.ctx.pointer,
|
||||
C.int32_t(plugin.id),
|
||||
plugin.ptr,
|
||||
name,
|
||||
(*C.uchar)(ptr),
|
||||
C.uint64_t(len(input)),
|
||||
@@ -410,7 +318,7 @@ func (plugin Plugin) Call(functionName string, input []byte) ([]byte, error) {
|
||||
C.free(unsafe.Pointer(name))
|
||||
|
||||
if rc != 0 {
|
||||
err := C.extism_error(plugin.ctx.pointer, C.int32_t(plugin.id))
|
||||
err := C.extism_plugin_error(plugin.ptr)
|
||||
msg := "<unset by plugin>"
|
||||
if err != nil {
|
||||
msg = C.GoString(err)
|
||||
@@ -421,10 +329,10 @@ func (plugin Plugin) Call(functionName string, input []byte) ([]byte, error) {
|
||||
)
|
||||
}
|
||||
|
||||
length := C.extism_plugin_output_length(plugin.ctx.pointer, C.int32_t(plugin.id))
|
||||
length := C.extism_plugin_output_length(plugin.ptr)
|
||||
|
||||
if length > 0 {
|
||||
x := C.extism_plugin_output_data(plugin.ctx.pointer, C.int32_t(plugin.id))
|
||||
x := C.extism_plugin_output_data(plugin.ptr)
|
||||
return unsafe.Slice((*byte)(x), C.int(length)), nil
|
||||
}
|
||||
|
||||
@@ -433,16 +341,11 @@ func (plugin Plugin) Call(functionName string, input []byte) ([]byte, error) {
|
||||
|
||||
// Free a plugin
|
||||
func (plugin *Plugin) Free() {
|
||||
if plugin.ctx.pointer == nil {
|
||||
if plugin.ptr == nil {
|
||||
return
|
||||
}
|
||||
C.extism_plugin_free(plugin.ctx.pointer, C.int32_t(plugin.id))
|
||||
plugin.id = -1
|
||||
}
|
||||
|
||||
// Reset removes all registered plugins in a Context
|
||||
func (ctx Context) Reset() {
|
||||
C.extism_context_reset(ctx.pointer)
|
||||
C.extism_plugin_free(plugin.ptr)
|
||||
plugin.ptr = nil
|
||||
}
|
||||
|
||||
// ValGetI64 returns an I64 from an ExtismVal, it accepts a pointer to a C.ExtismVal
|
||||
@@ -514,7 +417,7 @@ type CancelHandle struct {
|
||||
}
|
||||
|
||||
func (p *Plugin) CancelHandle() CancelHandle {
|
||||
pointer := C.extism_plugin_cancel_handle(p.ctx.pointer, C.int(p.id))
|
||||
pointer := C.extism_plugin_cancel_handle(p.ptr)
|
||||
return CancelHandle{pointer}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ depends: [
|
||||
"extism-manifest" {= version}
|
||||
"ppx_inline_test" {>= "v0.15.0"}
|
||||
"cmdliner" {>= "1.1.1"}
|
||||
"uuidm" {>= "0.9.0"}
|
||||
"odoc" {with-doc}
|
||||
]
|
||||
build: [
|
||||
|
||||
@@ -35,16 +35,8 @@ func expectVowelCount(plugin Plugin, input string, count int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCreateAndFreeContext(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
ctx.Free()
|
||||
}
|
||||
|
||||
func TestCallPlugin(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
defer ctx.Free()
|
||||
|
||||
plugin, err := ctx.PluginFromManifest(manifest(false), []Function{}, false)
|
||||
plugin, err := NewPluginFromManifest(manifest(false), []Function{}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -61,10 +53,7 @@ func TestCallPlugin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFreePlugin(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
defer ctx.Free()
|
||||
|
||||
plugin, err := ctx.PluginFromManifest(manifest(false), []Function{}, false)
|
||||
plugin, err := NewPluginFromManifest(manifest(false), []Function{}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -80,52 +69,8 @@ func TestFreePlugin(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextReset(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
defer ctx.Free()
|
||||
|
||||
plugin, err := ctx.PluginFromManifest(manifest(false), []Function{}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := expectVowelCount(plugin, "this is a test", 4); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// reset the context dropping all plugins
|
||||
ctx.Reset()
|
||||
|
||||
if err := expectVowelCount(plugin, "this is a test", 4); err == nil {
|
||||
t.Fatal("Expected an error after plugin was freed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanUpdateAManifest(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
defer ctx.Free()
|
||||
|
||||
plugin, err := ctx.PluginFromManifest(manifest(false), []Function{}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := expectVowelCount(plugin, "this is a test", 4); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
plugin.UpdateManifest(manifest(false), []Function{}, false)
|
||||
|
||||
// can still call the plugin
|
||||
if err := expectVowelCount(plugin, "this is a test", 4); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionExists(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
defer ctx.Free()
|
||||
|
||||
plugin, err := ctx.PluginFromManifest(manifest(false), []Function{}, false)
|
||||
plugin, err := NewPluginFromManifest(manifest(false), []Function{}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -139,10 +84,7 @@ func TestFunctionExists(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestErrorsOnUnknownFunction(t *testing.T) {
|
||||
ctx := NewContext()
|
||||
defer ctx.Free()
|
||||
|
||||
plugin, err := ctx.PluginFromManifest(manifest(false), []Function{}, false)
|
||||
plugin, err := NewPluginFromManifest(manifest(false), []Function{}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -162,10 +104,7 @@ func TestCancel(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
ctx := NewContext()
|
||||
defer ctx.Free()
|
||||
|
||||
plugin, err := ctx.PluginFromManifest(manifest, []Function{}, false)
|
||||
plugin, err := NewPluginFromManifest(manifest, []Function{}, false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
4
go/README.md
Normal file
4
go/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Go Host SDK
|
||||
|
||||
This contains the `0.x` version of the SDK. Development of this library has moved to [this repo](https://github.com/extism/go-sdk#readme).
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
module Main where
|
||||
|
||||
import Extism
|
||||
import Extism.CurrentPlugin
|
||||
import Extism.HostFunction
|
||||
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
|
||||
@@ -15,10 +11,11 @@ hello plugin params msg = do
|
||||
return [toI64 offs]
|
||||
|
||||
main = do
|
||||
setLogFile "stdout" Error
|
||||
setLogFile "stdout" LogError
|
||||
let m = manifest [wasmFile "../wasm/code-functions.wasm"]
|
||||
f <- hostFunction "hello_world" [I64] [I64] hello "Hello, again"
|
||||
plugin <- unwrap <$> createPluginFromManifest m [f] True
|
||||
plugin <- unwrap <$> pluginFromManifest m [f] True
|
||||
id <- pluginID plugin
|
||||
print id
|
||||
res <- unwrap <$> call plugin "count_vowels" (toByteString "this is a test")
|
||||
putStrLn (fromByteString res)
|
||||
free plugin
|
||||
|
||||
4
haskell/README.md
Normal file
4
haskell/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Haskell Host SDK
|
||||
|
||||
This contains the `0.x` version of the SDK. Development of this library has moved to [this repo](https://github.com/extism/haskell-sdk#readme).
|
||||
|
||||
@@ -11,7 +11,7 @@ category: Plugins, WebAssembly
|
||||
extra-doc-files: CHANGELOG.md
|
||||
|
||||
library
|
||||
exposed-modules: Extism Extism.CurrentPlugin
|
||||
exposed-modules: Extism Extism.HostFunction
|
||||
reexported-modules: Extism.Manifest
|
||||
hs-source-dirs: src
|
||||
other-modules: Extism.Bindings
|
||||
@@ -22,7 +22,8 @@ library
|
||||
base >= 4.16.1 && < 5,
|
||||
bytestring >= 0.11.3 && <= 0.12,
|
||||
json >= 0.10 && <= 0.11,
|
||||
extism-manifest >= 0.0.0 && < 0.4.0
|
||||
extism-manifest >= 0.0.0 && < 0.4.0,
|
||||
uuid >= 1.3 && < 2
|
||||
|
||||
test-suite extism-example
|
||||
type: exitcode-stdio-1.0
|
||||
|
||||
@@ -9,7 +9,7 @@ import Data.ByteString.Internal (c2w, w2c)
|
||||
import qualified Data.ByteString.Base64 as B64
|
||||
import qualified Data.ByteString.Char8 as BS (unpack)
|
||||
|
||||
data Nullable a = Null | NotNull a
|
||||
data Nullable a = Null | NotNull a deriving Eq
|
||||
|
||||
makeArray x = JSArray [showJSON a | a <- x]
|
||||
isNull JSNull = True
|
||||
@@ -49,7 +49,7 @@ instance JSON a => JSON (Nullable a) where
|
||||
readJSON x = readJSON x
|
||||
|
||||
|
||||
newtype Base64 = Base64 B.ByteString
|
||||
newtype Base64 = Base64 B.ByteString deriving (Eq, Show)
|
||||
|
||||
instance JSON Base64 where
|
||||
showJSON (Base64 bs) = showJSON (BS.unpack $ B64.encode bs)
|
||||
|
||||
@@ -9,6 +9,7 @@ newtype Memory = Memory
|
||||
{
|
||||
memoryMaxPages :: Nullable Int
|
||||
}
|
||||
deriving Eq
|
||||
|
||||
instance JSON Memory where
|
||||
showJSON (Memory max) =
|
||||
@@ -26,6 +27,7 @@ data HTTPRequest = HTTPRequest
|
||||
, headers :: Nullable [(String, String)]
|
||||
, method :: Nullable String
|
||||
}
|
||||
deriving Eq
|
||||
|
||||
makeKV x =
|
||||
object [(k, showJSON v) | (k, v) <- x]
|
||||
@@ -55,6 +57,7 @@ data WasmFile = WasmFile
|
||||
, fileName :: Nullable String
|
||||
, fileHash :: Nullable String
|
||||
}
|
||||
deriving Eq
|
||||
|
||||
instance JSON WasmFile where
|
||||
showJSON (WasmFile path name hash) =
|
||||
@@ -80,9 +83,7 @@ data WasmData = WasmData
|
||||
, dataName :: Nullable String
|
||||
, dataHash :: Nullable String
|
||||
}
|
||||
|
||||
|
||||
|
||||
deriving Eq
|
||||
|
||||
instance JSON WasmData where
|
||||
showJSON (WasmData bytes name hash) =
|
||||
@@ -110,6 +111,7 @@ data WasmURL = WasmURL
|
||||
, urlName :: Nullable String
|
||||
, urlHash :: Nullable String
|
||||
}
|
||||
deriving Eq
|
||||
|
||||
|
||||
instance JSON WasmURL where
|
||||
@@ -127,7 +129,7 @@ instance JSON WasmURL where
|
||||
Just req -> Ok (WasmURL req name hash)
|
||||
|
||||
-- | Specifies where to get WASM module data
|
||||
data Wasm = File WasmFile | Data WasmData | URL WasmURL
|
||||
data Wasm = File WasmFile | Data WasmData | URL WasmURL deriving Eq
|
||||
|
||||
instance JSON Wasm where
|
||||
showJSON x =
|
||||
|
||||
@@ -1,60 +1,69 @@
|
||||
module Extism (
|
||||
module Extism,
|
||||
module Extism.Manifest,
|
||||
ValType(..),
|
||||
Val(..)
|
||||
Function(..),
|
||||
Plugin(..),
|
||||
CancelHandle(..),
|
||||
LogLevel(..),
|
||||
Error(..),
|
||||
Result(..),
|
||||
toByteString,
|
||||
fromByteString,
|
||||
extismVersion,
|
||||
plugin,
|
||||
pluginFromManifest,
|
||||
isValid,
|
||||
setConfig,
|
||||
setLogFile,
|
||||
functionExists,
|
||||
call,
|
||||
cancelHandle,
|
||||
cancel,
|
||||
pluginID,
|
||||
unwrap
|
||||
) 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.Marshal.Alloc
|
||||
import Foreign.Storable
|
||||
import Foreign.StablePtr
|
||||
import Foreign.Concurrent
|
||||
import Foreign.Marshal.Utils (copyBytes, moveBytes)
|
||||
import Data.ByteString as B
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import Data.ByteString.Internal (c2w, w2c)
|
||||
import Data.ByteString.Unsafe (unsafeUseAsCString)
|
||||
import Data.Bifunctor (second)
|
||||
import Text.JSON (encode, toJSObject, showJSON)
|
||||
import qualified Text.JSON (encode, toJSObject, showJSON)
|
||||
import Extism.Manifest (Manifest, toString)
|
||||
import Extism.Bindings
|
||||
import qualified Data.UUID (UUID, fromByteString)
|
||||
|
||||
-- | Context for managing plugins
|
||||
newtype Context = Context (ForeignPtr ExtismContext)
|
||||
|
||||
-- | Host function
|
||||
data Function = Function (ForeignPtr ExtismFunction) (StablePtr ())
|
||||
-- | Host function, see 'Extism.HostFunction.hostFunction'
|
||||
data Function = Function (ForeignPtr ExtismFunction) (StablePtr ()) deriving Eq
|
||||
|
||||
-- | Plugins can be used to call WASM function
|
||||
data Plugin = Plugin Context Int32 [Function]
|
||||
newtype Plugin = Plugin (ForeignPtr ExtismPlugin) deriving Eq
|
||||
|
||||
-- | 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)
|
||||
data LogLevel = LogError | LogWarn | LogInfo | LogDebug | LogTrace deriving (Show, Eq)
|
||||
|
||||
-- | Extism error
|
||||
newtype Error = ExtismError String deriving Show
|
||||
newtype Error = ExtismError String deriving (Show, Eq)
|
||||
|
||||
-- | Result type
|
||||
type Result a = Either Error a
|
||||
|
||||
-- | Helper function to convert a 'String' to a 'ByteString'
|
||||
toByteString :: String -> ByteString
|
||||
toByteString x = B.pack (Prelude.map c2w x)
|
||||
toByteString :: String -> B.ByteString
|
||||
toByteString x = B.pack (map c2w x)
|
||||
|
||||
-- | Helper function to convert a 'ByteString' to a 'String'
|
||||
fromByteString :: ByteString -> String
|
||||
fromByteString bs = Prelude.map w2c $ B.unpack bs
|
||||
fromByteString :: B.ByteString -> String
|
||||
fromByteString bs = map w2c $ B.unpack bs
|
||||
|
||||
-- | Get the Extism version string
|
||||
extismVersion :: () -> IO String
|
||||
@@ -62,119 +71,55 @@ extismVersion () = do
|
||||
v <- extism_version
|
||||
peekCString v
|
||||
|
||||
-- | Remove all registered plugins in a 'Context'
|
||||
reset :: Context -> IO ()
|
||||
reset (Context ctx) =
|
||||
withForeignPtr ctx extism_context_reset
|
||||
|
||||
-- | Create a new 'Context'
|
||||
newContext :: IO Context
|
||||
newContext = do
|
||||
ptr <- extism_context_new
|
||||
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 -> [Function] -> Bool -> IO (Result Plugin)
|
||||
plugin c wasm functions useWasi =
|
||||
let nfunctions = fromIntegral (Prelude.length functions) in
|
||||
plugin :: B.ByteString -> [Function] -> Bool -> IO (Result Plugin)
|
||||
plugin wasm functions useWasi =
|
||||
let nfunctions = fromIntegral (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
|
||||
funcs <- mapM (\(Function ptr _) -> withForeignPtr ptr (\x -> do return x)) functions
|
||||
alloca (\e-> do
|
||||
let errmsg = (e :: Ptr CString)
|
||||
p <- unsafeUseAsCString wasm (\s ->
|
||||
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
|
||||
extism_plugin_new (castPtr s) length funcs nfunctions wasi errmsg ))
|
||||
if p == nullPtr then do
|
||||
err <- peek errmsg
|
||||
e <- peekCString err
|
||||
extism_plugin_new_error_free err
|
||||
return $ Left (ExtismError e)
|
||||
else
|
||||
return $ Right (Plugin c p functions))
|
||||
|
||||
-- | Create a 'Plugin' with its own 'Context'
|
||||
createPlugin :: B.ByteString -> [Function] -> Bool -> IO (Result Plugin)
|
||||
createPlugin c functions useWasi = do
|
||||
ctx <- newContext
|
||||
plugin ctx c functions useWasi
|
||||
else do
|
||||
ptr <- Foreign.Concurrent.newForeignPtr p (extism_plugin_free p)
|
||||
return $ Right (Plugin ptr))
|
||||
|
||||
-- | Create a 'Plugin' from a 'Manifest'
|
||||
pluginFromManifest :: Context -> Manifest -> [Function] -> Bool -> IO (Result Plugin)
|
||||
pluginFromManifest ctx manifest functions useWasi =
|
||||
pluginFromManifest :: Manifest -> [Function] -> Bool -> IO (Result Plugin)
|
||||
pluginFromManifest manifest functions useWasi =
|
||||
let wasm = toByteString $ toString manifest in
|
||||
plugin ctx wasm functions useWasi
|
||||
|
||||
-- | Create a 'Plugin' with its own 'Context' from a 'Manifest'
|
||||
createPluginFromManifest :: Manifest -> [Function] -> Bool -> IO (Result Plugin)
|
||||
createPluginFromManifest manifest functions useWasi = do
|
||||
ctx <- newContext
|
||||
pluginFromManifest ctx manifest functions useWasi
|
||||
|
||||
-- | Update a 'Plugin' with a new WASM module
|
||||
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
|
||||
funcs <- Prelude.mapM (\(Function ptr _ ) -> withForeignPtr ptr (\x -> do return x)) functions
|
||||
withForeignPtr ctx (\ctx' -> do
|
||||
b <- unsafeUseAsCString wasm (\s ->
|
||||
withArray funcs (\funcs ->
|
||||
extism_plugin_update ctx' id (castPtr s) length funcs nfunctions wasi))
|
||||
if b <= 0 then do
|
||||
err <- extism_error ctx' (-1)
|
||||
e <- peekCString err
|
||||
return $ Left (ExtismError e)
|
||||
else
|
||||
return (Right (Plugin (Context ctx) id functions)))
|
||||
|
||||
-- | Update a 'Plugin' with a new 'Manifest'
|
||||
updateManifest :: Plugin -> Manifest -> [Function] -> Bool -> IO (Result Plugin)
|
||||
updateManifest plugin manifest functions useWasi =
|
||||
let wasm = toByteString $ toString manifest in
|
||||
update plugin wasm functions useWasi
|
||||
plugin wasm functions useWasi
|
||||
|
||||
-- | Check if a 'Plugin' is valid
|
||||
isValid :: Plugin -> Bool
|
||||
isValid (Plugin _ p _) = p >= 0
|
||||
isValid :: Plugin -> IO Bool
|
||||
isValid (Plugin p) = withForeignPtr p (\x -> return (x /= nullPtr))
|
||||
|
||||
-- | Set configuration values for a plugin
|
||||
setConfig :: Plugin -> [(String, Maybe String)] -> IO Bool
|
||||
setConfig (Plugin (Context ctx) plugin _) x =
|
||||
if plugin < 0
|
||||
then return False
|
||||
else
|
||||
let obj = toJSObject [(k, showJSON v) | (k, v) <- x] in
|
||||
let bs = toByteString (encode obj) in
|
||||
let length = fromIntegral (B.length bs) in
|
||||
unsafeUseAsCString bs (\s -> do
|
||||
withForeignPtr ctx (\ctx -> do
|
||||
b <- extism_plugin_config ctx plugin (castPtr s) length
|
||||
return $ b /= 0))
|
||||
setConfig (Plugin plugin) x =
|
||||
let obj = Text.JSON.toJSObject [(k, Text.JSON.showJSON v) | (k, v) <- x] in
|
||||
let bs = toByteString (Text.JSON.encode obj) in
|
||||
let length = fromIntegral (B.length bs) in
|
||||
unsafeUseAsCString bs (\s -> do
|
||||
withForeignPtr plugin (\plugin-> do
|
||||
b <- extism_plugin_config plugin (castPtr s) length
|
||||
return $ b /= 0))
|
||||
|
||||
levelStr Error = "error"
|
||||
levelStr Debug = "debug"
|
||||
levelStr Warn = "warn"
|
||||
levelStr Trace = "trace"
|
||||
levelStr Info = "info"
|
||||
levelStr LogError = "error"
|
||||
levelStr LogDebug = "debug"
|
||||
levelStr LogWarn = "warn"
|
||||
levelStr LogTrace = "trace"
|
||||
levelStr LogInfo = "info"
|
||||
|
||||
-- | Set the log file and level, this is a global configuration
|
||||
setLogFile :: String -> LogLevel -> IO Bool
|
||||
@@ -187,43 +132,46 @@ setLogFile filename level =
|
||||
|
||||
-- | Check if a function exists in the given plugin
|
||||
functionExists :: Plugin -> String -> IO Bool
|
||||
functionExists (Plugin (Context ctx) plugin _) name = do
|
||||
withForeignPtr ctx (\ctx -> do
|
||||
b <- withCString name (extism_plugin_function_exists ctx plugin)
|
||||
functionExists (Plugin plugin) name = do
|
||||
withForeignPtr plugin (\plugin -> do
|
||||
b <- withCString name (extism_plugin_function_exists 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 plugin) name input =
|
||||
let length = fromIntegral (B.length input) in
|
||||
do
|
||||
withForeignPtr ctx (\ctx -> do
|
||||
withForeignPtr plugin (\plugin -> do
|
||||
rc <- withCString name (\name ->
|
||||
unsafeUseAsCString input (\input ->
|
||||
extism_plugin_call ctx plugin name (castPtr input) length))
|
||||
err <- extism_error ctx plugin
|
||||
extism_plugin_call plugin name (castPtr input) length))
|
||||
err <- extism_error plugin
|
||||
if err /= nullPtr
|
||||
then do e <- peekCString err
|
||||
return $ Left (ExtismError e)
|
||||
else if rc == 0
|
||||
then do
|
||||
length <- extism_plugin_output_length ctx plugin
|
||||
ptr <- extism_plugin_output_data ctx plugin
|
||||
buf <- packCStringLen (castPtr ptr, fromIntegral length)
|
||||
length <- extism_plugin_output_length plugin
|
||||
ptr <- extism_plugin_output_data plugin
|
||||
buf <- B.packCStringLen (castPtr ptr, fromIntegral length)
|
||||
return $ Right buf
|
||||
else return $ Left (ExtismError "Call failed"))
|
||||
|
||||
-- | 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 _) =
|
||||
withForeignPtr ctx (`extism_plugin_free` plugin)
|
||||
-- | Call a function with a string argument and return a string
|
||||
callString :: Plugin -> String -> String -> IO (Result String)
|
||||
callString p name input = do
|
||||
res <- call p name (toByteString input)
|
||||
case res of
|
||||
Left x -> return $ Left x
|
||||
Right x -> return $ Right (fromByteString x)
|
||||
|
||||
|
||||
-- | 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 (`extism_plugin_cancel_handle` plugin)
|
||||
cancelHandle (Plugin plugin) = do
|
||||
handle <- withForeignPtr plugin extism_plugin_cancel_handle
|
||||
return (CancelHandle handle)
|
||||
|
||||
-- | Cancel a running plugin using a 'CancelHandle'
|
||||
@@ -231,58 +179,16 @@ cancel :: CancelHandle -> IO Bool
|
||||
cancel (CancelHandle handle) =
|
||||
extism_plugin_cancel handle
|
||||
|
||||
pluginID :: Plugin -> IO Data.UUID.UUID
|
||||
pluginID (Plugin plugin) =
|
||||
withForeignPtr plugin (\plugin -> do
|
||||
ptr <- extism_plugin_id plugin
|
||||
buf <- B.packCStringLen (castPtr ptr, 16)
|
||||
case Data.UUID.fromByteString (BL.fromStrict buf) of
|
||||
Nothing -> error "Invalid Plugin ID"
|
||||
Just x -> return x)
|
||||
|
||||
-- | 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
|
||||
|
||||
unwrap (Right x) = x
|
||||
unwrap (Left (ExtismError msg)) = do
|
||||
error msg
|
||||
|
||||
@@ -13,7 +13,7 @@ import Foreign.StablePtr
|
||||
|
||||
type FreeCallback = Ptr () -> IO ()
|
||||
|
||||
newtype ExtismContext = ExtismContext () deriving Show
|
||||
newtype ExtismPlugin = ExtismPlugin () deriving Show
|
||||
newtype ExtismFunction = ExtismFunction () deriving Show
|
||||
newtype ExtismCancelHandle = ExtismCancelHandle () deriving Show
|
||||
newtype ExtismCurrentPlugin = ExtismCurrentPlugin () deriving Show
|
||||
@@ -79,21 +79,19 @@ instance Storable ValType where
|
||||
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
|
||||
foreign import ccall safe "extism.h extism_plugin_update" extism_plugin_update :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Word64 -> Ptr (Ptr ExtismFunction) -> Word64 -> CBool -> IO CBool
|
||||
foreign import ccall safe "extism.h extism_plugin_call" extism_plugin_call :: Ptr ExtismContext -> Int32 -> CString -> Ptr Word8 -> Word64 -> IO Int32
|
||||
foreign import ccall safe "extism.h extism_plugin_function_exists" extism_plugin_function_exists :: Ptr ExtismContext -> Int32 -> CString -> IO CBool
|
||||
foreign import ccall safe "extism.h extism_error" extism_error :: Ptr ExtismContext -> Int32 -> IO CString
|
||||
foreign import ccall safe "extism.h extism_plugin_output_length" extism_plugin_output_length :: Ptr ExtismContext -> Int32 -> IO Word64
|
||||
foreign import ccall safe "extism.h extism_plugin_output_data" extism_plugin_output_data :: Ptr ExtismContext -> Int32 -> IO (Ptr Word8)
|
||||
foreign import ccall safe "extism.h extism_plugin_new" extism_plugin_new :: Ptr Word8 -> Word64 -> Ptr (Ptr ExtismFunction) -> Word64 -> CBool -> Ptr CString -> IO (Ptr ExtismPlugin)
|
||||
foreign import ccall safe "extism.h extism_plugin_call" extism_plugin_call :: Ptr ExtismPlugin -> CString -> Ptr Word8 -> Word64 -> IO Int32
|
||||
foreign import ccall safe "extism.h extism_plugin_function_exists" extism_plugin_function_exists :: Ptr ExtismPlugin -> CString -> IO CBool
|
||||
foreign import ccall safe "extism.h extism_plugin_error" extism_error :: Ptr ExtismPlugin -> IO CString
|
||||
foreign import ccall safe "extism.h extism_plugin_output_length" extism_plugin_output_length :: Ptr ExtismPlugin -> IO Word64
|
||||
foreign import ccall safe "extism.h extism_plugin_output_data" extism_plugin_output_data :: Ptr ExtismPlugin -> IO (Ptr Word8)
|
||||
foreign import ccall safe "extism.h extism_log_file" extism_log_file :: CString -> CString -> IO CBool
|
||||
foreign import ccall safe "extism.h extism_plugin_config" extism_plugin_config :: Ptr ExtismContext -> Int32 -> Ptr Word8 -> Int64 -> IO CBool
|
||||
foreign import ccall safe "extism.h extism_plugin_free" extism_plugin_free :: Ptr ExtismContext -> Int32 -> IO ()
|
||||
foreign import ccall safe "extism.h extism_context_reset" extism_context_reset :: Ptr ExtismContext -> IO ()
|
||||
foreign import ccall safe "extism.h extism_plugin_config" extism_plugin_config :: Ptr ExtismPlugin -> Ptr Word8 -> Int64 -> IO CBool
|
||||
foreign import ccall safe "extism.h extism_plugin_free" extism_plugin_free :: Ptr ExtismPlugin -> IO ()
|
||||
foreign import ccall safe "extism.h extism_plugin_new_error_free" extism_plugin_new_error_free :: CString -> IO ()
|
||||
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_id" extism_plugin_id :: Ptr ExtismPlugin -> IO (Ptr Word8)
|
||||
foreign import ccall safe "extism.h extism_plugin_cancel_handle" extism_plugin_cancel_handle :: Ptr ExtismPlugin -> 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)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user