extism 1.0.0.pre.rc.2 → 1.0.0.pre.rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/Gemfile +16 -16
- data/Gemfile.lock +2 -1
- data/README.md +60 -97
- data/Rakefile +12 -12
- data/lib/extism/current_plugin.rb +7 -7
- data/lib/extism/host_environment.rb +20 -5
- data/lib/extism/manifest.rb +68 -0
- data/lib/extism/plugin.rb +18 -8
- data/lib/extism/version.rb +1 -1
- data/lib/extism/wasm.rb +16 -1
- data/lib/extism.rb +59 -38
- data/wasm/count_vowels.wasm +0 -0
- data/wasm/count_vowels_kvstore.wasm +0 -0
- data/wasm/reflect.wasm +0 -0
- data/wasm/store_credit.wasm +0 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ff531578509d2c71f3db9d20e01fdbbba922e342f76dbb84aafa69ce94db3350
|
4
|
+
data.tar.gz: 163c31c8cddeec68fd774af387effcbd13e3a8e9ea40ab26445ab94fa6db75a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5b97afbbeddff3e52a0627832fa92b860ef921cee1b82d0f1a4753b2b9a47ae89810e2f2c8b061f94aff9fa639283a7886d06026e3c98a588693f86369cbdb1
|
7
|
+
data.tar.gz: 5399c2a2d9f0183bad275c212f7009b73ef9234ddc2cc4a90433972a3decb36926c967fd3935f20e58f2cb18966c9e76546e967db4882ecc01936eb0593092ab
|
data/.yardopts
CHANGED
data/Gemfile
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source 'https://rubygems.org'
|
4
|
-
|
5
|
-
# Specify your gem's dependencies in extism.gemspec
|
6
|
-
gemspec
|
7
|
-
|
8
|
-
gem 'ffi', '~> 1.15.5'
|
9
|
-
gem 'rake', '~> 13.0'
|
10
|
-
|
11
|
-
group :development do
|
12
|
-
gem 'debug'
|
13
|
-
gem 'minitest', '~> 5.20.0'
|
14
|
-
gem 'rufo', '~> 0.13.0'
|
15
|
-
gem 'yard', '~> 0.9.28'
|
16
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in extism.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem 'ffi', '~> 1.15.5'
|
9
|
+
gem 'rake', '~> 13.0'
|
10
|
+
|
11
|
+
group :development do
|
12
|
+
gem 'debug'
|
13
|
+
gem 'minitest', '~> 5.20.0'
|
14
|
+
gem 'rufo', '~> 0.13.0'
|
15
|
+
gem 'yard', '~> 0.9.28'
|
16
|
+
end
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# Extism Ruby Host SDK
|
2
2
|
|
3
|
-
This repo
|
3
|
+
This repo contains the ruby gem for integrating with the [Extism](https://extism.org/) runtime. Install this library into your host ruby application to run Extism plug-ins.
|
4
4
|
|
5
|
-
> **Note**: This repo is 1.0 alpha version of the Ruby SDK and
|
5
|
+
> **Note**: This repo is 1.0 alpha version of the Ruby SDK and we may push breaking changes in between versions until we hit 1.0.0 in December, 2023. However, it is ready to use and you should use this if you're building a new integration. We'd love any feedback on it.
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
9
|
-
### Install the Extism Runtime
|
9
|
+
### Install the Extism Runtime Dependency
|
10
10
|
|
11
|
-
|
11
|
+
For this library, you first need to install the Extism Runtime. You can [download the shared object directly from a release](https://github.com/extism/extism/releases) or use the [Extism CLI](https://github.com/extism/cli) to install it:
|
12
12
|
|
13
13
|
```bash
|
14
14
|
sudo extism lib install latest
|
@@ -18,14 +18,14 @@ sudo extism lib install latest
|
|
18
18
|
#=> Copying extism.h to /usr/local/include/extism.h
|
19
19
|
```
|
20
20
|
|
21
|
-
> **Note**: This library has breaking changes and targets 1.0 of the runtime. For the time being, install the runtime from our nightly development builds on git: `sudo extism lib install --version git
|
21
|
+
> **Note**: This library has breaking changes and targets 1.0 of the runtime. For the time being, install the runtime from our nightly development builds on git: `sudo extism lib install --version git`.
|
22
22
|
|
23
|
-
### Install the
|
23
|
+
### Install the Gem
|
24
24
|
|
25
25
|
Add this library to your [Gemfile](https://bundler.io/):
|
26
26
|
|
27
27
|
```ruby
|
28
|
-
gem 'extism', '1.0.0.pre.rc.
|
28
|
+
gem 'extism', '1.0.0.pre.rc.3'
|
29
29
|
```
|
30
30
|
|
31
31
|
Or if installing on the system level:
|
@@ -36,41 +36,37 @@ gem install extism --pre
|
|
36
36
|
|
37
37
|
## Getting Started
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
First you should require `"extism"`:
|
39
|
+
This guide should walk you through some of the concepts in Extism and this ruby library.
|
42
40
|
|
43
|
-
|
44
|
-
require "extism"
|
45
|
-
```
|
41
|
+
> *Note*: You should be able to follow this guide by copy pasting the code into `irb`.
|
46
42
|
|
47
43
|
### Creating A Plug-in
|
48
44
|
|
49
|
-
The primary concept in Extism is the plug-in. You can think of a plug-in as a code module stored in a `.wasm` file.
|
45
|
+
The primary concept in Extism is the [plug-in](https://extism.org/docs/concepts/plug-in). You can think of a plug-in as a code module stored in a `.wasm` file.
|
50
46
|
|
51
|
-
|
47
|
+
Since you may not have an Extism plug-in on hand to test, let's load a demo plug-in from the web:
|
52
48
|
|
53
49
|
```ruby
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
50
|
+
# First require the library
|
51
|
+
require "extism"
|
52
|
+
|
53
|
+
url = "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm"
|
54
|
+
manifest = Extism::Manifest.from_url url
|
59
55
|
plugin = Extism::Plugin.new(manifest)
|
60
56
|
```
|
61
57
|
|
62
|
-
> **Note**:
|
58
|
+
> **Note**: See [the Manifest docs](https://extism.github.io/ruby-sdk/Extism/Manifest.html) as it has a rich schema and a lot of options.
|
63
59
|
|
64
60
|
### Calling A Plug-in's Exports
|
65
61
|
|
66
|
-
This plug-in was written in Rust and it does one thing, it counts vowels in a string. As such it exposes one "export" function: `count_vowels`. We can call exports using [Extism::Plugin#call](https://extism.github.io/ruby-sdk/Extism/Plugin.html#call-instance_method):
|
62
|
+
This plug-in was written in Rust and it does one thing, it counts vowels in a string. As such, it exposes one "export" function: `count_vowels`. We can call exports using [Extism::Plugin#call](https://extism.github.io/ruby-sdk/Extism/Plugin.html#call-instance_method):
|
67
63
|
|
68
64
|
```ruby
|
69
65
|
plugin.call("count_vowels", "Hello, World!")
|
70
66
|
# => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
|
71
67
|
```
|
72
68
|
|
73
|
-
All exports have a simple interface of
|
69
|
+
All exports have a simple interface of bytes-in and bytes-out. This plug-in happens to take a string and return a JSON encoded string with a report of results.
|
74
70
|
|
75
71
|
### Plug-in State
|
76
72
|
|
@@ -101,106 +97,73 @@ plugin.call("count_vowels", "Yellow, World!")
|
|
101
97
|
|
102
98
|
### Host Functions
|
103
99
|
|
104
|
-
|
105
|
-
|
106
|
-
> *Note*: Host functions can be a complicated topic. Please review this [concept doc](https://extism.org/docs/concepts/host-functions) if you are unsure how they work.
|
107
|
-
|
108
|
-
### Host Functions Example
|
100
|
+
Let's extend our count-vowels example a little bit: Instead of storing the `total` in an ephemeral plug-in var, let's store it in a persistent key-value store!
|
109
101
|
|
110
|
-
|
111
|
-
When a [charge.succeeded](https://stripe.com/docs/api/events/types#event_types-charge.succeeded) event occurs, we will call the `on_charge_succeeded` function on our merchant's plug-in and let them decide what to do with it. Here our merchant has some very specific requirements, if the account has spent more than $100, their currency is USD, and they have no credits on their account, it will add $10 credit to their account and then send them an email.
|
102
|
+
Wasm can't use our KV store on it's own. This is where [Host Functions](https://extism.org/docs/concepts/host-functions) come in.
|
112
103
|
|
113
|
-
|
104
|
+
[Host functions](https://extism.org/docs/concepts/host-functions) allow us to grant new capabilities to our plug-ins from our application. They are simply some ruby methods you write which can be passed down and invoked from any language inside the plug-in.
|
114
105
|
|
115
|
-
|
106
|
+
Let's load the manifest like usual but load up this `count_vowels_kvstore` plug-in:
|
116
107
|
|
117
108
|
```ruby
|
118
|
-
|
119
|
-
|
120
|
-
{ url: "https://github.com/extism/plugins/releases/latest/download/store_credit.wasm" }
|
121
|
-
]
|
122
|
-
}
|
109
|
+
url = "https://github.com/extism/plugins/releases/latest/download/count_vowels_kvstore.wasm"
|
110
|
+
manifest = Extism::Manifest.from_url(url)
|
123
111
|
```
|
124
112
|
|
125
|
-
|
113
|
+
> *Note*: The source code for this is [here](https://github.com/extism/plugins/blob/main/count_vowels_kvstore/src/lib.rs) and is written in rust, but it could be written in any of our PDK languages.
|
126
114
|
|
127
|
-
|
115
|
+
Unlike our previous plug-in, this plug-in expects you to provide host functions that satisfy our its import interface for a KV store.
|
128
116
|
|
129
|
-
|
117
|
+
In the ruby sdk, we have a concept for this called a [Host Environment](https://extism.github.io/ruby-sdk/Extism/HostEnvironment.html). An environment is an instance of a class that implements any host functions your plug-in needs.
|
130
118
|
|
131
|
-
|
132
|
-
# reality be in a database or something
|
133
|
-
CUSTOMER = {
|
134
|
-
full_name: 'John Smith',
|
135
|
-
customer_id: 'abcd1234',
|
136
|
-
total_spend: {
|
137
|
-
currency: 'USD',
|
138
|
-
amount_in_cents: 20_000
|
139
|
-
},
|
140
|
-
credit: {
|
141
|
-
currency: 'USD',
|
142
|
-
amount_in_cents: 0
|
143
|
-
}
|
144
|
-
}
|
145
|
-
|
146
|
-
class MyEnvironment
|
147
|
-
include Extism::HostEnvironment
|
119
|
+
We want to expose two functions to our plugin, `kv_write(key: String, value: Bytes)` which writes a bytes value to a key and `kv_read(key: String) -> Bytes` which reads the bytes at the given `key`.
|
148
120
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
register_import :send_email, [Extism::ValType::I64, Extism::ValType::I64], []
|
121
|
+
```ruby
|
122
|
+
# pretend this is Redis or something :)
|
123
|
+
KV_STORE = {}
|
153
124
|
|
154
|
-
|
155
|
-
|
156
|
-
customer_id = plugin.input_as_string(inputs.first)
|
157
|
-
# it takes an object `amount` { amount_in_cents: int, currency: string } as the second parameter
|
158
|
-
amount = plugin.input_as_json(inputs[1])
|
125
|
+
class KvEnvironment
|
126
|
+
include Extism::HostEnvironment
|
159
127
|
|
160
|
-
|
161
|
-
|
162
|
-
|
128
|
+
# We need to describe the wasm function signature of each host function
|
129
|
+
# to register them to this environment
|
130
|
+
register_import :kv_read, [Extism::ValType::PTR], [Extism::ValType::PTR]
|
131
|
+
register_import :kv_write, [Extism::ValType::PTR, Extism::ValType::PTR], []
|
163
132
|
|
164
|
-
|
165
|
-
plugin.
|
133
|
+
def kv_read(plugin, inputs, outputs, _user_data)
|
134
|
+
key = plugin.input_as_string(inputs.first)
|
135
|
+
val = KV_STORE[key] || [0].pack('V') # get 4 LE bytes for 0 default
|
136
|
+
puts "Read from key=#{key}"
|
137
|
+
plugin.output_string(outputs.first, val)
|
166
138
|
end
|
167
139
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
# we'll just print it but you could imagine we'd put something
|
175
|
-
# in a database or call an internal api to send this email
|
176
|
-
puts "Sending email #{email} to customer #{customer_id}"
|
177
|
-
|
178
|
-
# it doesn't return anything
|
140
|
+
def kv_write(plugin, inputs, _outputs, _user_data)
|
141
|
+
key = plugin.input_as_string(inputs.first)
|
142
|
+
val = plugin.input_as_string(inputs[1])
|
143
|
+
puts "Writing value=#{val.unpack1('V')} from key=#{key}"
|
144
|
+
KV_STORE[key] = val
|
179
145
|
end
|
180
146
|
end
|
181
147
|
```
|
182
148
|
|
183
|
-
|
149
|
+
> *Note*: In order to write host functions you should get familiar with the methods on the [Extism::CurrentPlugin](https://extism.github.io/ruby-sdk/Extism/CurrentPlugin.html) class. The `plugin` parameter is an instance of this class.
|
150
|
+
|
151
|
+
Now we just need to create a new host environment and pass it in when loading the plug-in. Here our environment initializer takes no arguments, but you could imagine putting some customer specific instance variables in there:
|
184
152
|
|
185
153
|
```ruby
|
186
|
-
env =
|
154
|
+
env = KvEnvironment.new
|
187
155
|
plugin = Extism::Plugin.new(manifest, environment: env)
|
188
156
|
```
|
189
157
|
|
190
158
|
Now we can invoke the event:
|
191
159
|
|
192
160
|
```ruby
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
}
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
```
|
203
|
-
Adding Credit {"amount_in_cents"=>1000, "currency"=>"USD"} for customer abcd1234
|
204
|
-
Sending email {"subject"=>"A gift for you John Smith", "body"=>"You have received $10 in store credi
|
205
|
-
t!"} to customer abcd1234
|
161
|
+
plugin.call("count_vowels", "Hello, World!")
|
162
|
+
# => Read from key=count-vowels"
|
163
|
+
# => Writing value=3 from key=count-vowels"
|
164
|
+
# => {"count": 3, "total": 3, "vowels": "aeiouAEIOU"}
|
165
|
+
plugin.call("count_vowels", "Hello, World!")
|
166
|
+
# => Read from key=count-vowels"
|
167
|
+
# => Writing value=6 from key=count-vowels"
|
168
|
+
# => {"count": 3, "total": 6, "vowels": "aeiouAEIOU"}
|
206
169
|
```
|
data/Rakefile
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bundler/gem_tasks"
|
4
|
-
require "rake/testtask"
|
5
|
-
|
6
|
-
Rake::TestTask.new(:test) do |t|
|
7
|
-
t.libs << "test"
|
8
|
-
t.libs << "lib"
|
9
|
-
t.test_files = FileList["test/**/test_*.rb"]
|
10
|
-
end
|
11
|
-
|
12
|
-
task default: :test
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/test_*.rb"]
|
10
|
+
end
|
11
|
+
|
12
|
+
task default: :test
|
@@ -16,15 +16,15 @@ module Extism
|
|
16
16
|
|
17
17
|
# Allocates a memory block in the plugin
|
18
18
|
#
|
19
|
-
# @example
|
19
|
+
# @example Allocate 1kB
|
20
20
|
# mem = current_plugin.alloc(1_024)
|
21
|
-
#
|
21
|
+
# current_plugin.free(mem)
|
22
22
|
#
|
23
|
-
# @param
|
23
|
+
# @param num_bytes [Integer] The amount in bytes to allocate
|
24
24
|
# @return [Extism::Memory] The reference to the freshly allocated memory
|
25
|
-
def alloc(
|
26
|
-
offset = LibExtism.extism_current_plugin_memory_alloc(@ptr,
|
27
|
-
Memory.new(offset,
|
25
|
+
def alloc(num_bytes)
|
26
|
+
offset = LibExtism.extism_current_plugin_memory_alloc(@ptr, num_bytes)
|
27
|
+
Memory.new(offset, num_bytes)
|
28
28
|
end
|
29
29
|
|
30
30
|
# Frees the memory block
|
@@ -142,7 +142,7 @@ module Extism
|
|
142
142
|
private
|
143
143
|
|
144
144
|
# Returns a raw pointer (absolute to the host) to the given memory block
|
145
|
-
# Be careful with this. it's not exposed for a reason.
|
145
|
+
# **Danger**: Be careful with this. it's not exposed for a reason.
|
146
146
|
# This is a pointer in host memory so it could read outside of the plugin
|
147
147
|
# if manipulated
|
148
148
|
def memory_ptr(mem)
|
@@ -21,6 +21,12 @@ module Extism
|
|
21
21
|
base.class_variable_set(:@@import_funcs, [])
|
22
22
|
end
|
23
23
|
|
24
|
+
# Creates the host functions to pass to the plug-in on intialization.
|
25
|
+
# Used internally by the Plugin initializer
|
26
|
+
#
|
27
|
+
# @see Extism::Plugin::new
|
28
|
+
#
|
29
|
+
# @return [Array<Extism::Function>]
|
24
30
|
def host_functions
|
25
31
|
import_funcs = self.class.class_variable_get(:@@import_funcs)
|
26
32
|
import_funcs.map do |f|
|
@@ -33,12 +39,21 @@ module Extism
|
|
33
39
|
)
|
34
40
|
end
|
35
41
|
end
|
36
|
-
end
|
37
42
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
43
|
+
module ClassMethods
|
44
|
+
# Register an import by name. You must know the wasm signature
|
45
|
+
# of the function to do this.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# register_import :my_func, [Extism::ValType::I64], [Extism::ValType::F64]
|
49
|
+
#
|
50
|
+
# @param [Symbol | String] func_name The name of the wasm import function. Assumes `env` namespace.
|
51
|
+
# @param [Array<Extism::ValType>] parameters The Wasm types of the parameters that the import takes
|
52
|
+
# @param [Array<Extism::ValType>] returns The Wasm types of the returns that the import returns. Will usually be just be one of these.
|
53
|
+
def register_import(func_name, parameters, returns)
|
54
|
+
import_funcs = class_variable_get(:@@import_funcs)
|
55
|
+
import_funcs << [func_name, parameters, returns]
|
56
|
+
end
|
42
57
|
end
|
43
58
|
end
|
44
59
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Extism
|
2
|
+
# The manifest represents a recipe to build a plug-in.
|
3
|
+
# It generally consists of a path to one wasm module
|
4
|
+
# but could contain more. It also helps you define some
|
5
|
+
# options and restrictions on the runtime behavior of the plug-in.
|
6
|
+
# See https://extism.org/docs/concepts/manifest for more info.
|
7
|
+
class Manifest
|
8
|
+
attr_reader :manifest_data
|
9
|
+
|
10
|
+
# Create a manifest of a single wasm from url.
|
11
|
+
# Look at {Manifest#initialize} for an interface with more control
|
12
|
+
#
|
13
|
+
# @see Manifest::initialize
|
14
|
+
# @param [String] url The url to the wasm module
|
15
|
+
# @param [String | nil] hash An optional sha256 integrity hash. Defaults to nil
|
16
|
+
# @param [String | nil] name An optional name. Defaults to nil
|
17
|
+
# @return [Extism::Manifest]
|
18
|
+
def self.from_url(url, hash: nil, name: nil)
|
19
|
+
wasm = { url: url }
|
20
|
+
wasm[:hash] = hash unless hash.nil?
|
21
|
+
wasm[:name] = name unless hash.nil?
|
22
|
+
|
23
|
+
Manifest.new({ wasm: [wasm] })
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create a manifest of a single wasm from file path.
|
27
|
+
# Look at {Manifest#initialize} for an interface with more control
|
28
|
+
#
|
29
|
+
# @see Manifest::initialize
|
30
|
+
# @param [String] path The path to the wasm module on disk
|
31
|
+
# @param [String | nil] hash An optional sha256 integrity hash. Defaults to nil
|
32
|
+
# @param [String | nil] name An optional name. Defaults to nil
|
33
|
+
# @return [Extism::Manifest]
|
34
|
+
def self.from_path(path, hash: nil, name: nil)
|
35
|
+
wasm = { path: path }
|
36
|
+
wasm[:hash] = hash unless hash.nil?
|
37
|
+
wasm[:name] = name unless hash.nil?
|
38
|
+
|
39
|
+
Manifest.new({ wasm: [wasm] })
|
40
|
+
end
|
41
|
+
|
42
|
+
# Create a manifest of a single wasm module with raw binary data.
|
43
|
+
# Look at {Manifest#initialize} for an interface with more control
|
44
|
+
# Consider using a file path instead of the raw wasm binary in memory.
|
45
|
+
# The performance is often better letting the runtime load the binary itself.
|
46
|
+
#
|
47
|
+
# @see Manifest::initialize
|
48
|
+
# @param [String] data The binary data of the wasm module
|
49
|
+
# @param [String | nil] hash An optional sha256 integrity hash. Defaults to nil
|
50
|
+
# @param [String | nil] name An optional name. Defaults to nil
|
51
|
+
# @return [Extism::Manifest]
|
52
|
+
def self.from_bytes(data, hash: nil, name: nil)
|
53
|
+
wasm = { data: data }
|
54
|
+
wasm[:hash] = hash unless hash.nil?
|
55
|
+
wasm[:name] = name unless hash.nil?
|
56
|
+
|
57
|
+
Manifest.new({ wasm: [wasm] })
|
58
|
+
end
|
59
|
+
|
60
|
+
# Initialize a manifest
|
61
|
+
# See https://extism.org/docs/concepts/manifest for schema
|
62
|
+
#
|
63
|
+
# @param [Hash] data The Hash data that conforms the Manifest schema
|
64
|
+
def initialize(data)
|
65
|
+
@manifest_data = data
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/extism/plugin.rb
CHANGED
@@ -4,19 +4,29 @@ module Extism
|
|
4
4
|
class Plugin
|
5
5
|
# Intialize a plugin
|
6
6
|
#
|
7
|
-
# @example
|
8
|
-
# manifest =
|
9
|
-
# wasm: [
|
10
|
-
# { url: "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm" }
|
11
|
-
# ]
|
12
|
-
# }
|
7
|
+
# @example Initialize a plugin from a url
|
8
|
+
# manifest = Extism::Manifest.from_url "https://github.com/extism/plugins/releases/latest/download/count_vowels.wasm"
|
13
9
|
# plugin = Extism::Plugin.new(manifest)
|
14
10
|
#
|
15
|
-
# @
|
11
|
+
# @example Pass a config object to configure the plug-in
|
12
|
+
# plugin = Extism::Plugin.new(manifest, config: { hello: "world" })
|
13
|
+
#
|
14
|
+
# @example Initalize a plug-in that needs WASI
|
15
|
+
# plugin = Extism::Plugin.new(manifest, wasi: true)
|
16
|
+
#
|
17
|
+
# @param wasm [Hash, String, Manifest] The manifest as a Hash or WASM binary as a String. See https://extism.org/docs/concepts/manifest/.
|
16
18
|
# @param wasi [Boolean] Enable WASI support
|
17
19
|
# @param config [Hash] The plugin config
|
18
20
|
def initialize(wasm, environment: nil, functions: [], wasi: false, config: nil)
|
19
|
-
wasm =
|
21
|
+
wasm = case wasm
|
22
|
+
when Hash
|
23
|
+
JSON.generate(wasm)
|
24
|
+
when Manifest
|
25
|
+
JSON.generate(wasm.manifest_data)
|
26
|
+
else
|
27
|
+
wasm
|
28
|
+
end
|
29
|
+
|
20
30
|
code = FFI::MemoryPointer.new(:char, wasm.bytesize)
|
21
31
|
errmsg = FFI::MemoryPointer.new(:pointer)
|
22
32
|
code.put_bytes(0, wasm)
|
data/lib/extism/version.rb
CHANGED
data/lib/extism/wasm.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
module Extism
|
2
|
+
# Extism specific values for Wasm types. Useful when you need to describe
|
3
|
+
# something in pure wasm like host function signatures.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# register_import :hostfunc, [Extism::ValType::I32, Extism::ValType::F64], [Extism::ValType::I64]
|
2
7
|
module ValType
|
3
8
|
I32 = 0
|
4
9
|
I64 = 1
|
10
|
+
PTR = 1
|
5
11
|
F32 = 2
|
6
12
|
F64 = 3
|
7
13
|
V128 = 4
|
@@ -9,6 +15,7 @@ module Extism
|
|
9
15
|
EXTERN_REF = 6
|
10
16
|
end
|
11
17
|
|
18
|
+
# A raw Wasm value. Contains the type and the data
|
12
19
|
class Val
|
13
20
|
def initialize(ptr)
|
14
21
|
@c_val = LibExtism::ExtismVal.new(ptr)
|
@@ -20,6 +27,8 @@ module Extism
|
|
20
27
|
:i32
|
21
28
|
when :I64
|
22
29
|
:i64
|
30
|
+
when :PTR
|
31
|
+
:i64
|
23
32
|
when :F32
|
24
33
|
:f32
|
25
34
|
when :F64
|
@@ -50,7 +59,10 @@ module Extism
|
|
50
59
|
end
|
51
60
|
end
|
52
61
|
|
53
|
-
# Represents a host function
|
62
|
+
# Represents a host function. This is mostly for internal use and you should
|
63
|
+
# try to use HostEnvironment instead
|
64
|
+
#
|
65
|
+
# @see Extism::HostEnvironment
|
54
66
|
class Function
|
55
67
|
# Create a new host function
|
56
68
|
#
|
@@ -81,6 +93,9 @@ module Extism
|
|
81
93
|
returns = LibExtism.from_int_array(@returns)
|
82
94
|
@_pointer = LibExtism.extism_function_new(@name, args, @params.length, returns, @returns.length, c_func, free,
|
83
95
|
nil)
|
96
|
+
$FUNCTIONS[object_id] = { function: @_pointer}
|
97
|
+
ObjectSpace.define_finalizer(self, $FREE_FUNCTION)
|
98
|
+
return @_pointer
|
84
99
|
end
|
85
100
|
|
86
101
|
def c_func
|
data/lib/extism.rb
CHANGED
@@ -1,38 +1,59 @@
|
|
1
|
-
require 'ffi'
|
2
|
-
require 'json'
|
3
|
-
require_relative './extism/
|
4
|
-
require_relative './extism/
|
5
|
-
require_relative './extism/
|
6
|
-
require_relative './extism/
|
7
|
-
require_relative './extism/
|
8
|
-
require_relative './extism/
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
#
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#
|
23
|
-
# @param
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
$
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
1
|
+
require 'ffi'
|
2
|
+
require 'json'
|
3
|
+
require_relative './extism/manifest'
|
4
|
+
require_relative './extism/version'
|
5
|
+
require_relative './extism/plugin'
|
6
|
+
require_relative './extism/current_plugin'
|
7
|
+
require_relative './extism/libextism'
|
8
|
+
require_relative './extism/wasm'
|
9
|
+
require_relative './extism/host_environment'
|
10
|
+
|
11
|
+
module Extism
|
12
|
+
class Error < StandardError
|
13
|
+
end
|
14
|
+
|
15
|
+
# Return the version of Extism
|
16
|
+
#
|
17
|
+
# @return [String] The version string of the Extism runtime
|
18
|
+
def self.extism_version
|
19
|
+
LibExtism.extism_version
|
20
|
+
end
|
21
|
+
|
22
|
+
# Set log file and level, this is a global configuration
|
23
|
+
# @param name [String] The path to the logfile
|
24
|
+
# @param level [String] The log level. One of {"debug", "error", "info", "trace" }
|
25
|
+
def self.set_log_file(name, level = nil)
|
26
|
+
LibExtism.extism_log_file(name, level)
|
27
|
+
end
|
28
|
+
|
29
|
+
$PLUGINS = {}
|
30
|
+
$FREE_PLUGIN = proc { |ptr|
|
31
|
+
x = $PLUGINS[ptr]
|
32
|
+
unless x.nil?
|
33
|
+
LibExtism.extism_plugin_free(x[:plugin])
|
34
|
+
$PLUGINS.delete(ptr)
|
35
|
+
end
|
36
|
+
}
|
37
|
+
|
38
|
+
|
39
|
+
$FUNCTIONS = {}
|
40
|
+
$FREE_FUNCTION = proc { |ptr|
|
41
|
+
x = $FUNCTIONS[ptr]
|
42
|
+
unless x.nil?
|
43
|
+
LibExtism.extism_function_free(x[:function])
|
44
|
+
$FUNCTIONS.delete(ptr)
|
45
|
+
end
|
46
|
+
}
|
47
|
+
|
48
|
+
# Represents a "block" of memory in Extism.
|
49
|
+
# This memory is in the communication buffer b/w the
|
50
|
+
# guest in the host and technically lives in host memory.
|
51
|
+
class Memory
|
52
|
+
attr_reader :offset, :len
|
53
|
+
|
54
|
+
def initialize(offset, len)
|
55
|
+
@offset = offset
|
56
|
+
@len = len
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/wasm/count_vowels.wasm
CHANGED
Binary file
|
Binary file
|
data/wasm/reflect.wasm
CHANGED
Binary file
|
data/wasm/store_credit.wasm
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: extism
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.pre.rc.
|
4
|
+
version: 1.0.0.pre.rc.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- zach
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -42,11 +42,13 @@ files:
|
|
42
42
|
- lib/extism/current_plugin.rb
|
43
43
|
- lib/extism/host_environment.rb
|
44
44
|
- lib/extism/libextism.rb
|
45
|
+
- lib/extism/manifest.rb
|
45
46
|
- lib/extism/plugin.rb
|
46
47
|
- lib/extism/version.rb
|
47
48
|
- lib/extism/wasm.rb
|
48
49
|
- sig/extism.rbs
|
49
50
|
- wasm/count_vowels.wasm
|
51
|
+
- wasm/count_vowels_kvstore.wasm
|
50
52
|
- wasm/reflect.wasm
|
51
53
|
- wasm/store_credit.wasm
|
52
54
|
homepage: https://github.com/extism/extism
|