wasmtime 0.2.0 → 0.3.0
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/Cargo.lock +941 -627
- data/Cargo.toml +2 -14
- data/LICENSE +23 -21
- data/README.md +47 -118
- data/ext/Cargo.toml +17 -0
- data/ext/extconf.rb +6 -0
- data/ext/src/helpers/mod.rs +3 -0
- data/ext/src/helpers/wrapped_struct.rs +84 -0
- data/ext/src/lib.rs +9 -0
- data/ext/src/ruby_api/config.rs +68 -0
- data/ext/src/ruby_api/convert.rs +92 -0
- data/ext/src/ruby_api/engine.rs +66 -0
- data/ext/src/ruby_api/errors.rs +56 -0
- data/ext/src/ruby_api/externals.rs +113 -0
- data/ext/src/ruby_api/func.rs +388 -0
- data/ext/src/ruby_api/func_type.rs +139 -0
- data/ext/src/ruby_api/instance.rs +174 -0
- data/ext/src/ruby_api/linker.rs +296 -0
- data/ext/src/ruby_api/macros.rs +12 -0
- data/ext/src/ruby_api/memory.rs +142 -0
- data/ext/src/ruby_api/memory_type.rs +56 -0
- data/ext/src/ruby_api/mod.rs +48 -0
- data/ext/src/ruby_api/module.rs +107 -0
- data/ext/src/ruby_api/params.rs +42 -0
- data/ext/src/ruby_api/static_id.rs +57 -0
- data/ext/src/ruby_api/store.rs +182 -0
- data/ext/src/ruby_api/trap.rs +135 -0
- data/lib/wasmtime/version.rb +1 -1
- data/lib/wasmtime.rb +29 -4
- metadata +54 -30
- data/.cargo/config +0 -4
- data/CHANGELOG.md +0 -27
- data/ext/wasmtime/Makefile +0 -5
- data/ext/wasmtime/Rakefile +0 -3
- data/ext/wasmtime/extconf.rb +0 -5
- data/lib/tasks/compile.rake +0 -27
- data/lib/wasmtime/refinements.rb +0 -20
- data/lib/wasmtime/require.rb +0 -72
- data/src/export.rs +0 -19
- data/src/func.rs +0 -175
- data/src/instance.rs +0 -93
- data/src/lib.rs +0 -22
- data/src/memory.rs +0 -48
- data/src/ruby_type.rs +0 -32
- data/src/vm.rs +0 -6
@@ -0,0 +1,66 @@
|
|
1
|
+
use super::{config::Config, root};
|
2
|
+
use crate::error;
|
3
|
+
use magnus::{function, method, scan_args, Error, Module, Object, RString, Value};
|
4
|
+
use wasmtime::Engine as EngineImpl;
|
5
|
+
|
6
|
+
/// @yard
|
7
|
+
/// Represents a Wasmtime execution engine.
|
8
|
+
/// @see https://docs.rs/wasmtime/latest/wasmtime/struct.Engine.html Wasmtime's Rust doc
|
9
|
+
#[derive(Clone)]
|
10
|
+
#[magnus::wrap(class = "Wasmtime::Engine")]
|
11
|
+
pub struct Engine {
|
12
|
+
inner: EngineImpl,
|
13
|
+
}
|
14
|
+
|
15
|
+
impl Engine {
|
16
|
+
/// @yard
|
17
|
+
/// @def new(config)
|
18
|
+
/// @param config [Configuration]
|
19
|
+
pub fn new(args: &[Value]) -> Result<Self, Error> {
|
20
|
+
let args = scan_args::scan_args::<(), (Option<Value>,), (), (), (), ()>(args)?;
|
21
|
+
let (config,) = args.optional;
|
22
|
+
let config = config.and_then(|v| if v.is_nil() { None } else { Some(v) });
|
23
|
+
let inner = match config {
|
24
|
+
Some(config) => EngineImpl::new(&config.try_convert::<&Config>()?.get())
|
25
|
+
.map_err(|e| error!("{}", e))?,
|
26
|
+
None => EngineImpl::default(),
|
27
|
+
};
|
28
|
+
|
29
|
+
Ok(Self { inner })
|
30
|
+
}
|
31
|
+
|
32
|
+
pub fn get(&self) -> &EngineImpl {
|
33
|
+
&self.inner
|
34
|
+
}
|
35
|
+
|
36
|
+
pub fn is_equal(&self, other: &Engine) -> bool {
|
37
|
+
EngineImpl::same(self.get(), other.get())
|
38
|
+
}
|
39
|
+
|
40
|
+
/// @yard
|
41
|
+
/// AoT compile a WebAssembly text or WebAssembly binary module for later use.
|
42
|
+
///
|
43
|
+
/// The compiled module can be instantiated using {Module.deserialize}.
|
44
|
+
///
|
45
|
+
/// @def precompile_module(wat_or_wasm)
|
46
|
+
/// @param wat_or_wasm [String] The String of WAT or Wasm.
|
47
|
+
/// @return [String] Binary String of the compiled module.
|
48
|
+
/// @see Module.deserialize
|
49
|
+
pub fn precompile_module(&self, wat_or_wasm: RString) -> Result<RString, Error> {
|
50
|
+
self.inner
|
51
|
+
.precompile_module(unsafe { wat_or_wasm.as_slice() })
|
52
|
+
.map(|bytes| RString::from_slice(&bytes))
|
53
|
+
.map_err(|e| error!("{}", e.to_string()))
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
pub fn init() -> Result<(), Error> {
|
58
|
+
let class = root().define_class("Engine", Default::default())?;
|
59
|
+
|
60
|
+
class.define_singleton_method("new", function!(Engine::new, -1))?;
|
61
|
+
|
62
|
+
class.define_method("==", method!(Engine::is_equal, 1))?;
|
63
|
+
class.define_method("precompile_module", method!(Engine::precompile_module, 1))?;
|
64
|
+
|
65
|
+
Ok(())
|
66
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
use crate::ruby_api::root;
|
2
|
+
use magnus::rb_sys::FromRawValue;
|
3
|
+
use magnus::{exception::standard_error, memoize, ExceptionClass, Module};
|
4
|
+
use magnus::{Error, Value};
|
5
|
+
|
6
|
+
/// Base error class for all Wasmtime errors.
|
7
|
+
pub fn base_error() -> ExceptionClass {
|
8
|
+
*memoize!(ExceptionClass: root().define_error("Error", standard_error()).unwrap())
|
9
|
+
}
|
10
|
+
|
11
|
+
/// Ruby's `NotImplementedError` class.
|
12
|
+
pub fn not_implemented_error() -> ExceptionClass {
|
13
|
+
*memoize!(ExceptionClass: {
|
14
|
+
Value::from_raw(rb_sys::rb_eNotImpError).try_convert().unwrap()
|
15
|
+
})
|
16
|
+
}
|
17
|
+
|
18
|
+
/// The `Wasmtime::ConversionError` class.
|
19
|
+
pub fn conversion_error() -> ExceptionClass {
|
20
|
+
*memoize!(ExceptionClass: root().define_error("ConversionError", base_error()).unwrap())
|
21
|
+
}
|
22
|
+
|
23
|
+
#[macro_export]
|
24
|
+
macro_rules! err {
|
25
|
+
($($arg:expr),*) => {
|
26
|
+
Result::Err($crate::error!($($arg),*))
|
27
|
+
};
|
28
|
+
}
|
29
|
+
|
30
|
+
#[macro_export]
|
31
|
+
macro_rules! error {
|
32
|
+
($($arg:expr),*) => {
|
33
|
+
Error::new($crate::ruby_api::errors::base_error(), format!($($arg),*))
|
34
|
+
};
|
35
|
+
}
|
36
|
+
|
37
|
+
#[macro_export]
|
38
|
+
macro_rules! not_implemented {
|
39
|
+
($($arg:expr),*) => {
|
40
|
+
Err(Error::new($crate::ruby_api::errors::not_implemented_error(), format!($($arg),*)))
|
41
|
+
};
|
42
|
+
}
|
43
|
+
|
44
|
+
#[macro_export]
|
45
|
+
macro_rules! conversion_err {
|
46
|
+
($($arg:expr),*) => {
|
47
|
+
Err(Error::new($crate::ruby_api::errors::conversion_error(), format!("cannot convert {} to {}", $($arg),*)))
|
48
|
+
};
|
49
|
+
}
|
50
|
+
|
51
|
+
pub fn init() -> Result<(), Error> {
|
52
|
+
let _ = base_error();
|
53
|
+
let _ = conversion_error();
|
54
|
+
|
55
|
+
Ok(())
|
56
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
use super::{
|
2
|
+
convert::WrapWasmtimeType, func::Func, memory::Memory, root, store::StoreContextValue,
|
3
|
+
};
|
4
|
+
use crate::{conversion_err, helpers::WrappedStruct, not_implemented};
|
5
|
+
use magnus::{
|
6
|
+
memoize, method, r_typed_data::DataTypeBuilder, rb_sys::AsRawValue, DataTypeFunctions, Error,
|
7
|
+
Module, RClass, TypedData, Value,
|
8
|
+
};
|
9
|
+
|
10
|
+
/// @yard
|
11
|
+
/// @rename Wasmtime::Extern
|
12
|
+
/// An external item to a WebAssembly module, or a list of what can possibly be exported from a Wasm module.
|
13
|
+
/// @see https://docs.rs/wasmtime/latest/wasmtime/enum.Extern.html Wasmtime's Rust doc
|
14
|
+
pub enum Extern<'a> {
|
15
|
+
Func(WrappedStruct<Func<'a>>),
|
16
|
+
Memory(WrappedStruct<Memory<'a>>),
|
17
|
+
}
|
18
|
+
|
19
|
+
unsafe impl TypedData for Extern<'_> {
|
20
|
+
fn class() -> magnus::RClass {
|
21
|
+
*memoize!(RClass: root().define_class("Extern", Default::default()).unwrap())
|
22
|
+
}
|
23
|
+
|
24
|
+
fn data_type() -> &'static magnus::DataType {
|
25
|
+
memoize!(magnus::DataType: {
|
26
|
+
let mut builder = DataTypeBuilder::<Extern<'_>>::new("Wasmtime::Extern");
|
27
|
+
builder.size();
|
28
|
+
builder.mark();
|
29
|
+
builder.free_immediately();
|
30
|
+
builder.build()
|
31
|
+
})
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
impl DataTypeFunctions for Extern<'_> {
|
36
|
+
fn mark(&self) {
|
37
|
+
match self {
|
38
|
+
Extern::Func(f) => f.mark(),
|
39
|
+
Extern::Memory(m) => m.mark(),
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
unsafe impl Send for Extern<'_> {}
|
44
|
+
|
45
|
+
impl Extern<'_> {
|
46
|
+
/// @yard
|
47
|
+
/// Returns the exported function or raises a `{ConversionError}` when the export is not a
|
48
|
+
/// function.
|
49
|
+
/// @return [Func] The exported function.
|
50
|
+
pub fn to_func(rb_self: WrappedStruct<Self>) -> Result<Value, Error> {
|
51
|
+
match rb_self.get()? {
|
52
|
+
Extern::Func(f) => Ok(f.to_value()),
|
53
|
+
_ => conversion_err!(Self::inner_class(rb_self)?, Func::class()),
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
/// @yard
|
58
|
+
/// Returns the exported memory or raises a `{ConversionError}` when the export is not a
|
59
|
+
/// memory.
|
60
|
+
/// @return [Memory] The exported memory.
|
61
|
+
pub fn to_memory(rb_self: WrappedStruct<Self>) -> Result<Value, Error> {
|
62
|
+
match rb_self.get()? {
|
63
|
+
Extern::Memory(f) => Ok(f.to_value()),
|
64
|
+
_ => conversion_err!(Self::inner_class(rb_self)?, Memory::class()),
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
pub fn inspect(rb_self: WrappedStruct<Self>) -> Result<String, Error> {
|
69
|
+
let rs_self = rb_self.get()?;
|
70
|
+
|
71
|
+
let inner_string: String = match rs_self {
|
72
|
+
Extern::Func(f) => f.inspect(),
|
73
|
+
Extern::Memory(m) => m.inspect(),
|
74
|
+
};
|
75
|
+
|
76
|
+
Ok(format!(
|
77
|
+
"#<Wasmtime::Extern:0x{:016x} @value={}>",
|
78
|
+
rb_self.to_value().as_raw(),
|
79
|
+
inner_string
|
80
|
+
))
|
81
|
+
}
|
82
|
+
|
83
|
+
fn inner_class(rb_self: WrappedStruct<Self>) -> Result<RClass, Error> {
|
84
|
+
match rb_self.get()? {
|
85
|
+
Extern::Func(f) => Ok(f.to_value().class()),
|
86
|
+
Extern::Memory(m) => Ok(m.to_value().class()),
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
impl<'a> WrapWasmtimeType<'a, Extern<'a>> for wasmtime::Extern {
|
92
|
+
fn wrap_wasmtime_type(&self, store: StoreContextValue<'a>) -> Result<Extern<'a>, Error> {
|
93
|
+
match self {
|
94
|
+
wasmtime::Extern::Func(func) => Ok(Extern::Func(Func::from_inner(store, *func).into())),
|
95
|
+
wasmtime::Extern::Memory(mem) => {
|
96
|
+
Ok(Extern::Memory(Memory::from_inner(store, *mem).into()))
|
97
|
+
}
|
98
|
+
wasmtime::Extern::Global(_) => not_implemented!("global not yet supported"),
|
99
|
+
wasmtime::Extern::Table(_) => not_implemented!("table not yet supported"),
|
100
|
+
wasmtime::Extern::SharedMemory(_) => not_implemented!("shared memory not supported"),
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
pub fn init() -> Result<(), Error> {
|
106
|
+
let class = root().define_class("Extern", Default::default())?;
|
107
|
+
|
108
|
+
class.define_method("to_func", method!(Extern::to_func, 0))?;
|
109
|
+
class.define_method("to_memory", method!(Extern::to_memory, 0))?;
|
110
|
+
class.define_method("inspect", method!(Extern::inspect, 0))?;
|
111
|
+
|
112
|
+
Ok(())
|
113
|
+
}
|
@@ -0,0 +1,388 @@
|
|
1
|
+
use super::{
|
2
|
+
convert::{ToRubyValue, ToWasmVal, WrapWasmtimeType},
|
3
|
+
externals::Extern,
|
4
|
+
func_type::FuncType,
|
5
|
+
params::Params,
|
6
|
+
root,
|
7
|
+
store::{Store, StoreContextValue, StoreData},
|
8
|
+
};
|
9
|
+
use crate::{error, helpers::WrappedStruct};
|
10
|
+
use magnus::{
|
11
|
+
block::Proc, function, memoize, method, r_typed_data::DataTypeBuilder, scan_args::scan_args,
|
12
|
+
value::BoxValue, DataTypeFunctions, Error, Exception, Module as _, Object, RArray, RClass,
|
13
|
+
RString, TryConvert, TypedData, Value, QNIL,
|
14
|
+
};
|
15
|
+
use std::cell::UnsafeCell;
|
16
|
+
use wasmtime::{
|
17
|
+
AsContext, AsContextMut, Caller as CallerImpl, Func as FuncImpl, StoreContext, StoreContextMut,
|
18
|
+
Val,
|
19
|
+
};
|
20
|
+
|
21
|
+
/// @yard
|
22
|
+
/// @rename Wasmtime::Func
|
23
|
+
/// Represents a WebAssembly Function
|
24
|
+
/// @see https://docs.rs/wasmtime/latest/wasmtime/struct.Func.html Wasmtime's Rust doc
|
25
|
+
#[derive(Debug)]
|
26
|
+
pub struct Func<'a> {
|
27
|
+
store: StoreContextValue<'a>,
|
28
|
+
inner: FuncImpl,
|
29
|
+
}
|
30
|
+
|
31
|
+
unsafe impl<'a> TypedData for Func<'a> {
|
32
|
+
fn class() -> magnus::RClass {
|
33
|
+
*memoize!(RClass: root().define_class("Func", Default::default()).unwrap())
|
34
|
+
}
|
35
|
+
|
36
|
+
fn data_type() -> &'static magnus::DataType {
|
37
|
+
memoize!(magnus::DataType: {
|
38
|
+
let mut builder = DataTypeBuilder::<Func<'_>>::new("Wasmtime::Func");
|
39
|
+
builder.size();
|
40
|
+
builder.mark();
|
41
|
+
builder.free_immediately();
|
42
|
+
builder.build()
|
43
|
+
})
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
impl DataTypeFunctions for Func<'_> {
|
48
|
+
fn mark(&self) {
|
49
|
+
self.store.mark()
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
// Wraps a Proc to satisfy wasmtime::Func's Send+Sync requirements. This is safe
|
54
|
+
// to do as long as (1) we hold the GVL when whe execute the proc and (2) we do
|
55
|
+
// not have multiple threads running at once (e.g. with Wasm thread proposal).
|
56
|
+
#[repr(transparent)]
|
57
|
+
struct ShareableProc(Proc);
|
58
|
+
unsafe impl Send for ShareableProc {}
|
59
|
+
unsafe impl Sync for ShareableProc {}
|
60
|
+
|
61
|
+
unsafe impl Send for Func<'_> {}
|
62
|
+
|
63
|
+
impl<'a> Func<'a> {
|
64
|
+
/// @yard
|
65
|
+
/// @def new(store, type, callable, &block)
|
66
|
+
/// @param store [Store]
|
67
|
+
/// @param type [FuncType]
|
68
|
+
/// @param block [Block] The funcs's implementation
|
69
|
+
///
|
70
|
+
/// @yield [caller, *args] The function's body
|
71
|
+
/// @yieldparam caller [Caller] Caller which can be used to interact with the {Store}.
|
72
|
+
/// @yieldparam *args [Object] Splat of Ruby objects matching the {FuncType}’s params arity.
|
73
|
+
/// @yieldreturn [nil, Object, Array<Object>] The return type depends on {FuncType}’s results arity:
|
74
|
+
/// * 0 => +nil+
|
75
|
+
/// * 1 => +Object+
|
76
|
+
/// * > 1 => +Array<Object>+
|
77
|
+
///
|
78
|
+
/// @return [Func]
|
79
|
+
///
|
80
|
+
/// @example Function that increments an i32:
|
81
|
+
/// store = Wasmtime::Store.new(Wasmtime::Engine.new)
|
82
|
+
/// type = FuncType.new([:i32], [:i32])
|
83
|
+
/// Wasmtime::Func.new(store, type) do |_caller, arg1|
|
84
|
+
/// arg1.succ
|
85
|
+
/// end
|
86
|
+
pub fn new(args: &[Value]) -> Result<Self, Error> {
|
87
|
+
let args = scan_args::<(Value, &FuncType), (), (), (), (), Proc>(args)?;
|
88
|
+
let (s, functype) = args.required;
|
89
|
+
let callable = args.block;
|
90
|
+
|
91
|
+
let wrapped_store: WrappedStruct<Store> = s.try_convert()?;
|
92
|
+
let store = wrapped_store.get()?;
|
93
|
+
|
94
|
+
store.retain(callable.into());
|
95
|
+
let context = store.context_mut();
|
96
|
+
let ty = functype.get();
|
97
|
+
|
98
|
+
let inner = wasmtime::Func::new(context, ty.clone(), make_func_closure(ty, callable));
|
99
|
+
|
100
|
+
Ok(Self {
|
101
|
+
store: wrapped_store.into(),
|
102
|
+
inner,
|
103
|
+
})
|
104
|
+
}
|
105
|
+
|
106
|
+
pub fn from_inner(store: StoreContextValue<'a>, inner: FuncImpl) -> Self {
|
107
|
+
Self { store, inner }
|
108
|
+
}
|
109
|
+
|
110
|
+
pub fn get(&self) -> FuncImpl {
|
111
|
+
// Makes a copy (wasmtime::Func implements Copy)
|
112
|
+
self.inner
|
113
|
+
}
|
114
|
+
|
115
|
+
/// @yard
|
116
|
+
/// Calls a Wasm function.
|
117
|
+
///
|
118
|
+
/// @def call(*args)
|
119
|
+
/// @param args [Object]
|
120
|
+
/// The arguments to send to the Wasm function. Raises if the arguments do
|
121
|
+
/// not conform to the Wasm function's parameters.
|
122
|
+
///
|
123
|
+
/// @return [nil, Object, Array<Object>] The return type depends on {FuncType}’s results arity:
|
124
|
+
/// * 0 => +nil+
|
125
|
+
/// * 1 => +Object+
|
126
|
+
/// * > 1 => +Array<Object>+
|
127
|
+
pub fn call(&self, args: &[Value]) -> Result<Value, Error> {
|
128
|
+
Self::invoke(&self.store, &self.inner, args).map_err(|e| e.into())
|
129
|
+
}
|
130
|
+
|
131
|
+
pub fn inner(&self) -> &FuncImpl {
|
132
|
+
&self.inner
|
133
|
+
}
|
134
|
+
|
135
|
+
pub fn invoke(
|
136
|
+
store: &StoreContextValue,
|
137
|
+
func: &wasmtime::Func,
|
138
|
+
args: &[Value],
|
139
|
+
) -> Result<Value, InvokeError> {
|
140
|
+
let func_ty = func.ty(store.context_mut()?);
|
141
|
+
let param_types = func_ty.params().collect::<Vec<_>>();
|
142
|
+
let params = Params::new(args, param_types)?.to_vec()?;
|
143
|
+
let mut results = vec![Val::null(); func_ty.results().len()];
|
144
|
+
|
145
|
+
func.call(store.context_mut()?, ¶ms, &mut results)
|
146
|
+
.map_err(|e| store.handle_wasm_error(e))?;
|
147
|
+
|
148
|
+
match results.as_slice() {
|
149
|
+
[] => Ok(QNIL.into()),
|
150
|
+
[result] => result.to_ruby_value(store).map_err(|e| e.into()),
|
151
|
+
_ => {
|
152
|
+
let array = RArray::with_capacity(results.len());
|
153
|
+
for result in results {
|
154
|
+
array.push(result.to_ruby_value(store)?)?;
|
155
|
+
}
|
156
|
+
Ok(array.into())
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
impl From<&Func<'_>> for wasmtime::Extern {
|
163
|
+
fn from(func: &Func) -> Self {
|
164
|
+
Self::Func(func.get())
|
165
|
+
}
|
166
|
+
}
|
167
|
+
|
168
|
+
pub fn make_func_closure(
|
169
|
+
ty: &wasmtime::FuncType,
|
170
|
+
callable: Proc,
|
171
|
+
) -> impl Fn(CallerImpl<'_, StoreData>, &[Val], &mut [Val]) -> anyhow::Result<()> + Send + Sync + 'static
|
172
|
+
{
|
173
|
+
let ty = ty.to_owned();
|
174
|
+
let callable = ShareableProc(callable);
|
175
|
+
|
176
|
+
move |caller_impl: CallerImpl<'_, StoreData>, params: &[Val], results: &mut [Val]| {
|
177
|
+
let wrapped_caller: WrappedStruct<Caller> = Caller::new(caller_impl).into();
|
178
|
+
let caller = wrapped_caller.get().unwrap();
|
179
|
+
let store_context = StoreContextValue::from(wrapped_caller);
|
180
|
+
|
181
|
+
let rparams = RArray::with_capacity(params.len() + 1);
|
182
|
+
rparams.push(Value::from(wrapped_caller)).unwrap();
|
183
|
+
|
184
|
+
for (i, param) in params.iter().enumerate() {
|
185
|
+
let rparam = param
|
186
|
+
.to_ruby_value(&store_context)
|
187
|
+
.map_err(|e| anyhow::anyhow!(format!("invalid argument at index {}: {}", i, e)))?;
|
188
|
+
rparams.push(rparam).unwrap();
|
189
|
+
}
|
190
|
+
|
191
|
+
let callable = callable.0;
|
192
|
+
|
193
|
+
let result = callable
|
194
|
+
.call(unsafe { rparams.as_slice() })
|
195
|
+
.map_err(|e| {
|
196
|
+
if let Error::Exception(exception) = e {
|
197
|
+
caller.hold_exception(exception);
|
198
|
+
}
|
199
|
+
e
|
200
|
+
})
|
201
|
+
.and_then(|proc_result| {
|
202
|
+
match results.len() {
|
203
|
+
0 => Ok(()), // Ignore return value
|
204
|
+
n => {
|
205
|
+
// For len=1, accept both `val` and `[val]`
|
206
|
+
let proc_result = RArray::try_convert(proc_result)?;
|
207
|
+
if proc_result.len() != n {
|
208
|
+
return Result::Err(error!(
|
209
|
+
"wrong number of results (given {}, expected {})",
|
210
|
+
proc_result.len(),
|
211
|
+
n
|
212
|
+
));
|
213
|
+
}
|
214
|
+
for ((rb_val, wasm_val), ty) in unsafe { proc_result.as_slice() }
|
215
|
+
.iter()
|
216
|
+
.zip(results.iter_mut())
|
217
|
+
.zip(ty.results())
|
218
|
+
{
|
219
|
+
*wasm_val = rb_val.to_wasm_val(&ty)?;
|
220
|
+
}
|
221
|
+
Ok(())
|
222
|
+
}
|
223
|
+
}
|
224
|
+
})
|
225
|
+
.map_err(|e| {
|
226
|
+
anyhow::anyhow!(format!(
|
227
|
+
"Error when calling Func {}\n Error: {}",
|
228
|
+
callable.inspect(),
|
229
|
+
e
|
230
|
+
))
|
231
|
+
});
|
232
|
+
|
233
|
+
// Drop the wasmtime::Caller so it does not outlive the Func call, if e.g. the user
|
234
|
+
// assigned the Ruby Wasmtime::Caller instance to a global.
|
235
|
+
caller.expire();
|
236
|
+
|
237
|
+
result
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
pub enum InvokeError {
|
242
|
+
BoxedException(BoxValue<Exception>),
|
243
|
+
Error(Error),
|
244
|
+
}
|
245
|
+
|
246
|
+
impl From<InvokeError> for magnus::Error {
|
247
|
+
fn from(e: InvokeError) -> Self {
|
248
|
+
match e {
|
249
|
+
InvokeError::Error(e) => e,
|
250
|
+
InvokeError::BoxedException(e) => Error::from(e.to_owned()),
|
251
|
+
}
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
impl From<magnus::Error> for InvokeError {
|
256
|
+
fn from(e: magnus::Error) -> Self {
|
257
|
+
InvokeError::Error(e)
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
impl From<BoxValue<Exception>> for InvokeError {
|
262
|
+
fn from(e: BoxValue<Exception>) -> Self {
|
263
|
+
InvokeError::BoxedException(e)
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
/// A handle to a [`wasmtime::Caller`] that's only valid during a Func execution.
|
268
|
+
/// [`UnsafeCell`] wraps the wasmtime::Caller because the Value's lifetime can't
|
269
|
+
/// be tied to the Caller: the Value is handed back to Ruby and we can't control
|
270
|
+
/// whether the user keeps a handle to it or not.
|
271
|
+
#[derive(Debug)]
|
272
|
+
pub struct CallerHandle<'a> {
|
273
|
+
caller: UnsafeCell<Option<CallerImpl<'a, StoreData>>>,
|
274
|
+
}
|
275
|
+
|
276
|
+
impl<'a> CallerHandle<'a> {
|
277
|
+
pub fn new(caller: CallerImpl<'a, StoreData>) -> Self {
|
278
|
+
Self {
|
279
|
+
caller: UnsafeCell::new(Some(caller)),
|
280
|
+
}
|
281
|
+
}
|
282
|
+
|
283
|
+
pub fn get_mut(&self) -> Result<&mut CallerImpl<'a, StoreData>, Error> {
|
284
|
+
unsafe { &mut *self.caller.get() }
|
285
|
+
.as_mut()
|
286
|
+
.ok_or_else(|| error!("Caller outlived its Func execution"))
|
287
|
+
}
|
288
|
+
|
289
|
+
pub fn get(&self) -> Result<&CallerImpl<'a, StoreData>, Error> {
|
290
|
+
unsafe { (*self.caller.get()).as_ref() }
|
291
|
+
.ok_or_else(|| error!("Caller outlived its Func execution"))
|
292
|
+
}
|
293
|
+
|
294
|
+
pub fn expire(&self) {
|
295
|
+
unsafe { *self.caller.get() = None }
|
296
|
+
}
|
297
|
+
}
|
298
|
+
|
299
|
+
/// @yard
|
300
|
+
/// @rename Wasmtime::Caller
|
301
|
+
/// Represents the Caller's context within a Func execution. An instance of
|
302
|
+
/// Caller is sent as the first parameter to Func's implementation (the
|
303
|
+
/// block argument in {Func.new}).
|
304
|
+
/// @see https://docs.rs/wasmtime/latest/wasmtime/struct.Caller.html Wasmtime's Rust doc
|
305
|
+
#[derive(Debug)]
|
306
|
+
pub struct Caller<'a> {
|
307
|
+
handle: CallerHandle<'a>,
|
308
|
+
}
|
309
|
+
|
310
|
+
impl<'a> Caller<'a> {
|
311
|
+
pub fn new(caller: CallerImpl<'a, StoreData>) -> Self {
|
312
|
+
Self {
|
313
|
+
handle: CallerHandle::new(caller),
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
/// @yard
|
318
|
+
/// Returns the store's data. Akin to {Store#data}.
|
319
|
+
/// @return [Object] The store's data (the object passed to {Store.new}).
|
320
|
+
pub fn store_data(&self) -> Result<Value, Error> {
|
321
|
+
self.context().map(|ctx| ctx.data().user_data())
|
322
|
+
}
|
323
|
+
|
324
|
+
/// @yard
|
325
|
+
/// @def export(name)
|
326
|
+
/// @see Instance#export
|
327
|
+
pub fn export(
|
328
|
+
rb_self: WrappedStruct<Caller<'a>>,
|
329
|
+
name: RString,
|
330
|
+
) -> Result<Option<Extern<'a>>, Error> {
|
331
|
+
let caller = rb_self.try_convert::<&Self>()?;
|
332
|
+
let inner = caller.handle.get_mut()?;
|
333
|
+
|
334
|
+
if let Some(export) = inner.get_export(unsafe { name.as_str() }?) {
|
335
|
+
export.wrap_wasmtime_type(rb_self.into()).map(Some)
|
336
|
+
} else {
|
337
|
+
Ok(None)
|
338
|
+
}
|
339
|
+
}
|
340
|
+
|
341
|
+
pub fn context(&self) -> Result<StoreContext<StoreData>, Error> {
|
342
|
+
self.handle.get().map(|c| c.as_context())
|
343
|
+
}
|
344
|
+
|
345
|
+
pub fn context_mut(&self) -> Result<StoreContextMut<StoreData>, Error> {
|
346
|
+
self.handle.get_mut().map(|c| c.as_context_mut())
|
347
|
+
}
|
348
|
+
|
349
|
+
pub fn expire(&self) {
|
350
|
+
self.handle.expire();
|
351
|
+
}
|
352
|
+
|
353
|
+
fn hold_exception(&self, exception: Exception) {
|
354
|
+
self.context_mut()
|
355
|
+
.unwrap()
|
356
|
+
.data_mut()
|
357
|
+
.exception()
|
358
|
+
.hold(exception);
|
359
|
+
}
|
360
|
+
}
|
361
|
+
|
362
|
+
unsafe impl<'a> TypedData for Caller<'a> {
|
363
|
+
fn class() -> magnus::RClass {
|
364
|
+
*memoize!(RClass: root().define_class("Caller", Default::default()).unwrap())
|
365
|
+
}
|
366
|
+
|
367
|
+
fn data_type() -> &'static magnus::DataType {
|
368
|
+
memoize!(magnus::DataType: {
|
369
|
+
let mut builder = DataTypeBuilder::<Caller<'_>>::new("Wasmtime::Caller");
|
370
|
+
builder.free_immediately();
|
371
|
+
builder.build()
|
372
|
+
})
|
373
|
+
}
|
374
|
+
}
|
375
|
+
impl DataTypeFunctions for Caller<'_> {}
|
376
|
+
unsafe impl Send for Caller<'_> {}
|
377
|
+
|
378
|
+
pub fn init() -> Result<(), Error> {
|
379
|
+
let func = root().define_class("Func", Default::default())?;
|
380
|
+
func.define_singleton_method("new", function!(Func::new, -1))?;
|
381
|
+
func.define_method("call", method!(Func::call, -1))?;
|
382
|
+
|
383
|
+
let caller = root().define_class("Caller", Default::default())?;
|
384
|
+
caller.define_method("store_data", method!(Caller::store_data, 0))?;
|
385
|
+
caller.define_method("export", method!(Caller::export, 1))?;
|
386
|
+
|
387
|
+
Ok(())
|
388
|
+
}
|