wasmtime 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|