wasmer 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/documentation.yml +50 -0
- data/.github/workflows/test.yml +34 -61
- data/CHANGELOG.md +89 -29
- data/Cargo.lock +812 -380
- data/Cargo.toml +7 -20
- data/Gemfile +2 -3
- data/README.md +1 -0
- data/Rakefile +4 -3
- data/crates/rutie-derive-macros/Cargo.toml +19 -0
- data/crates/rutie-derive-macros/README.md +4 -0
- data/crates/rutie-derive-macros/src/class.rs +156 -0
- data/crates/rutie-derive-macros/src/function.rs +178 -0
- data/crates/rutie-derive-macros/src/lib.rs +27 -0
- data/crates/rutie-derive-macros/src/methods.rs +282 -0
- data/crates/rutie-derive/Cargo.toml +14 -0
- data/crates/rutie-derive/README.md +97 -0
- data/crates/rutie-derive/src/lib.rs +4 -0
- data/crates/rutie-derive/src/upcast.rs +47 -0
- data/crates/rutie-test/Cargo.toml +10 -0
- data/crates/rutie-test/src/lib.rs +38 -0
- data/crates/wasmer/Cargo.toml +27 -0
- data/crates/wasmer/README.md +228 -0
- data/crates/wasmer/src/doc.rs +1512 -0
- data/crates/wasmer/src/error.rs +55 -0
- data/crates/wasmer/src/exports.rs +107 -0
- data/crates/wasmer/src/externals/function.rs +159 -0
- data/crates/wasmer/src/externals/global.rs +62 -0
- data/crates/wasmer/src/externals/memory.rs +117 -0
- data/crates/wasmer/src/externals/mod.rs +9 -0
- data/crates/wasmer/src/externals/table.rs +41 -0
- data/crates/wasmer/src/import_object.rs +78 -0
- data/crates/wasmer/src/instance.rs +45 -0
- data/crates/wasmer/src/lib.rs +307 -0
- data/crates/wasmer/src/memory/mod.rs +1 -0
- data/crates/wasmer/src/memory/views.rs +112 -0
- data/crates/wasmer/src/module.rs +106 -0
- data/crates/wasmer/src/prelude.rs +3 -0
- data/crates/wasmer/src/store.rs +22 -0
- data/crates/wasmer/src/types.rs +390 -0
- data/crates/wasmer/src/values.rs +84 -0
- data/crates/wasmer/src/wasi.rs +226 -0
- data/crates/wasmer/src/wat.rs +20 -0
- data/justfile +7 -1
- data/lib/wasmer.rb +29 -3
- data/wasmer.gemspec +6 -10
- metadata +45 -47
- data/README.md +0 -332
- data/lib/wasmer/version.rb +0 -3
- data/src/error.rs +0 -16
- data/src/instance/exports.rs +0 -215
- data/src/instance/globals.rs +0 -234
- data/src/instance/mod.rs +0 -141
- data/src/lib.rs +0 -162
- data/src/memory/mod.rs +0 -158
- data/src/memory/view.rs +0 -145
- data/src/module.rs +0 -28
@@ -0,0 +1,282 @@
|
|
1
|
+
use proc_macro2::{Group, Ident, Span, TokenStream, TokenTree};
|
2
|
+
use quote::{quote, ToTokens};
|
3
|
+
use syn::{punctuated::Punctuated, token::Colon2, *};
|
4
|
+
|
5
|
+
pub fn entry(
|
6
|
+
_attr: proc_macro::TokenStream,
|
7
|
+
input: proc_macro::TokenStream,
|
8
|
+
) -> proc_macro::TokenStream {
|
9
|
+
let impl_block = parse_macro_input!(input as ItemImpl);
|
10
|
+
|
11
|
+
let ty_name = match *impl_block.self_ty {
|
12
|
+
Type::Path(TypePath { qself: None, path }) if path.get_ident().is_some() => {
|
13
|
+
path.get_ident().unwrap().clone()
|
14
|
+
}
|
15
|
+
|
16
|
+
Type::Group(TypeGroup { elem, .. }) => match *elem {
|
17
|
+
Type::Path(TypePath { qself: None, path }) if path.get_ident().is_some() => {
|
18
|
+
path.get_ident().unwrap().clone()
|
19
|
+
}
|
20
|
+
_ => panic!("`impl` block must target a simple grouped identifier"),
|
21
|
+
},
|
22
|
+
|
23
|
+
_ => panic!("`impl` block must target a simple identifier, e.g. `impl T`"),
|
24
|
+
};
|
25
|
+
|
26
|
+
let ruby_rust_module_name = Ident::new(
|
27
|
+
&format!("ruby_{}", ty_name.to_string().to_lowercase()),
|
28
|
+
Span::call_site(),
|
29
|
+
);
|
30
|
+
let ruby_ty_name = Ident::new(&format!("Ruby{}", ty_name), ty_name.span());
|
31
|
+
|
32
|
+
let mut ruby_items = Vec::with_capacity(impl_block.items.len());
|
33
|
+
|
34
|
+
for item in impl_block.items {
|
35
|
+
match item {
|
36
|
+
ImplItem::Method(method) => {
|
37
|
+
if !method.attrs.is_empty() {
|
38
|
+
panic!("Methods in `impl` block do not support attributes for the moment");
|
39
|
+
}
|
40
|
+
|
41
|
+
let method_name = method.sig.ident.to_string();
|
42
|
+
|
43
|
+
if method.sig.constness.is_some() {
|
44
|
+
panic!("Method `{}` cannot be `const`", method_name);
|
45
|
+
}
|
46
|
+
|
47
|
+
if method.sig.asyncness.is_some() {
|
48
|
+
panic!("Method `{}` cannot be `async`", method_name);
|
49
|
+
}
|
50
|
+
|
51
|
+
if method.sig.unsafety.is_some() {
|
52
|
+
panic!("Method `{}` cannot be `unsafe`", method_name);
|
53
|
+
}
|
54
|
+
|
55
|
+
if method.sig.abi.is_some() {
|
56
|
+
panic!("Method `{}` cannot have a different ABI", method_name);
|
57
|
+
}
|
58
|
+
|
59
|
+
if !method.sig.generics.params.is_empty() {
|
60
|
+
panic!("Method `{}` cannot be generic", method_name);
|
61
|
+
}
|
62
|
+
|
63
|
+
if method.sig.variadic.is_some() {
|
64
|
+
panic!("Method `{}` cannot be variadic", method_name);
|
65
|
+
}
|
66
|
+
|
67
|
+
let ruby_method_name = &method.sig.ident;
|
68
|
+
let ruby_method_block = &method.block;
|
69
|
+
let ruby_method_visibility = &method.vis;
|
70
|
+
|
71
|
+
let mut need_self = false;
|
72
|
+
let mut need_mut_self = false;
|
73
|
+
|
74
|
+
let ruby_arguments_parsing = {
|
75
|
+
let (ruby_input_names, ruby_input_types): (
|
76
|
+
Vec<Ident>,
|
77
|
+
Vec<Punctuated<PathSegment, Colon2>>,
|
78
|
+
) = method
|
79
|
+
.sig
|
80
|
+
.inputs
|
81
|
+
.iter()
|
82
|
+
.filter_map(|input| match input {
|
83
|
+
FnArg::Receiver(Receiver { mutability, .. }) => {
|
84
|
+
need_self = true;
|
85
|
+
need_mut_self = mutability.is_some();
|
86
|
+
|
87
|
+
None
|
88
|
+
}
|
89
|
+
FnArg::Typed(PatType { pat, ty, .. }) => match (&**pat, &**ty) {
|
90
|
+
(
|
91
|
+
Pat::Ident(ident),
|
92
|
+
Type::Reference(TypeReference { elem, .. }),
|
93
|
+
) => match &**elem {
|
94
|
+
Type::Path(TypePath {
|
95
|
+
qself: None,
|
96
|
+
path: Path { segments: ty, .. },
|
97
|
+
}) => Some((ident.ident.clone(), ty.clone())),
|
98
|
+
_ => panic!(
|
99
|
+
"Typed input has an unsupported form (method `{}`)",
|
100
|
+
method_name
|
101
|
+
),
|
102
|
+
},
|
103
|
+
_ => panic!(
|
104
|
+
"Typed input has an unsupported form (method `{}`), it must be a reference type",
|
105
|
+
method_name
|
106
|
+
),
|
107
|
+
},
|
108
|
+
})
|
109
|
+
.unzip();
|
110
|
+
|
111
|
+
if ruby_input_names.is_empty() {
|
112
|
+
quote! {}
|
113
|
+
} else {
|
114
|
+
quote! {
|
115
|
+
let ( #( #ruby_input_names ),* ) =
|
116
|
+
{
|
117
|
+
let arguments = rutie::util::parse_arguments(argc, argv);
|
118
|
+
let mut argument_nth = 0;
|
119
|
+
|
120
|
+
(
|
121
|
+
#(
|
122
|
+
{
|
123
|
+
let argument = arguments
|
124
|
+
.get(argument_nth)
|
125
|
+
.ok_or_else(|| {
|
126
|
+
<rutie::AnyException as rutie::Exception>::new(
|
127
|
+
"ArgumentError",
|
128
|
+
Some(&format!(concat!("Argument #{} (`", stringify!( #ruby_input_types ), "`) of method `", stringify!( #ty_name ), ".", stringify!( #ruby_method_name ), "` is missing"), argument_nth)),
|
129
|
+
)
|
130
|
+
})
|
131
|
+
.and_then(|argument| {
|
132
|
+
<rutie::AnyObject as rutie::Object>
|
133
|
+
::try_convert_to::<< #ruby_input_types as rutie_derive::ClassInfo>::RubyClass>(argument)
|
134
|
+
})
|
135
|
+
.unwrap_or_else(|error| {
|
136
|
+
rutie::VM::raise_ex(error);
|
137
|
+
unreachable!()
|
138
|
+
});
|
139
|
+
|
140
|
+
argument_nth += 1;
|
141
|
+
|
142
|
+
argument
|
143
|
+
}
|
144
|
+
),*
|
145
|
+
)
|
146
|
+
};
|
147
|
+
|
148
|
+
let ( #( #ruby_input_names ),* ) =
|
149
|
+
(
|
150
|
+
#(
|
151
|
+
rutie_derive::UpcastRubyClass::<
|
152
|
+
<
|
153
|
+
< #ruby_input_types as rutie_derive::ClassInfo>::RubyClass as rutie_derive::ClassInfo
|
154
|
+
>::Class
|
155
|
+
>::upcast(&#ruby_input_names)
|
156
|
+
),*
|
157
|
+
);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
};
|
161
|
+
|
162
|
+
let ruby_input_receiver = if need_mut_self {
|
163
|
+
quote! { mut _ruby_self: #ruby_ty_name }
|
164
|
+
} else {
|
165
|
+
quote! { _ruby_self: #ruby_ty_name }
|
166
|
+
};
|
167
|
+
|
168
|
+
let ruby_output = match method.sig.output {
|
169
|
+
ReturnType::Type(_, ty) => match *ty {
|
170
|
+
Type::Path(TypePath {
|
171
|
+
qself: None,
|
172
|
+
path: Path {
|
173
|
+
leading_colon: None,
|
174
|
+
segments,
|
175
|
+
}
|
176
|
+
}) if segments.first().unwrap().ident.to_string() == "RubyResult" => {
|
177
|
+
match &segments.first().unwrap().arguments {
|
178
|
+
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
179
|
+
args,
|
180
|
+
..
|
181
|
+
}) => match args.first().unwrap() {
|
182
|
+
GenericArgument::Type(ty) => ty.clone(),
|
183
|
+
_ => panic!("Method has not well-formed rerturned type, expect `RubyResult<T>` where `T` is a type"),
|
184
|
+
},
|
185
|
+
_ => panic!("Method has not well-formed returned type, expect `RubyResult<T>`"),
|
186
|
+
}
|
187
|
+
}
|
188
|
+
_ => panic!("Method must wrap their output type inside `RubyResult<T>`"),
|
189
|
+
},
|
190
|
+
_ => panic!("Method must have an output of the form `RubyResult<T>`"),
|
191
|
+
};
|
192
|
+
|
193
|
+
let ruby_method_block = if need_self {
|
194
|
+
let slf_declaration = if need_mut_self {
|
195
|
+
quote! {
|
196
|
+
let _slf = _ruby_self.upcast_mut();
|
197
|
+
}
|
198
|
+
} else {
|
199
|
+
quote! {
|
200
|
+
let _slf = _ruby_self.upcast();
|
201
|
+
}
|
202
|
+
};
|
203
|
+
let block = rename_self(ruby_method_block.into_token_stream());
|
204
|
+
|
205
|
+
quote! {
|
206
|
+
#slf_declaration
|
207
|
+
|
208
|
+
#block
|
209
|
+
}
|
210
|
+
} else {
|
211
|
+
ruby_method_block.into_token_stream()
|
212
|
+
};
|
213
|
+
|
214
|
+
let ruby_block_visibility = if need_mut_self {
|
215
|
+
quote! { mut }
|
216
|
+
} else {
|
217
|
+
quote! {}
|
218
|
+
};
|
219
|
+
|
220
|
+
ruby_items.push(quote! {
|
221
|
+
#[allow(improper_ctypes_definitions)] // Not ideal but that's how Rutie works.
|
222
|
+
#ruby_method_visibility extern "C" fn #ruby_method_name(
|
223
|
+
argc: rutie::types::Argc,
|
224
|
+
argv: *const rutie::AnyObject,
|
225
|
+
#ruby_input_receiver,
|
226
|
+
) -> #ruby_output {
|
227
|
+
#ruby_arguments_parsing
|
228
|
+
|
229
|
+
let #ruby_block_visibility block = || -> Result<_, rutie::AnyException> {
|
230
|
+
#ruby_method_block
|
231
|
+
};
|
232
|
+
|
233
|
+
match block() {
|
234
|
+
Ok(x) => x,
|
235
|
+
Err(e) => {
|
236
|
+
rutie::VM::raise_ex(e);
|
237
|
+
unreachable!()
|
238
|
+
}
|
239
|
+
}
|
240
|
+
}
|
241
|
+
});
|
242
|
+
}
|
243
|
+
|
244
|
+
_ => panic!("`impl` block only supports methods for the moment"),
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
(quote! {
|
249
|
+
pub(crate) mod #ruby_rust_module_name {
|
250
|
+
use super::*;
|
251
|
+
|
252
|
+
#(#ruby_items)*
|
253
|
+
}
|
254
|
+
})
|
255
|
+
.into()
|
256
|
+
}
|
257
|
+
|
258
|
+
fn rename_self(stream: TokenStream) -> TokenStream {
|
259
|
+
let mut output_stream = TokenStream::new();
|
260
|
+
|
261
|
+
output_stream.extend(
|
262
|
+
stream
|
263
|
+
.into_iter()
|
264
|
+
.map(|token| match token {
|
265
|
+
TokenTree::Group(group) => {
|
266
|
+
TokenTree::Group(Group::new(group.delimiter(), rename_self(group.stream())))
|
267
|
+
}
|
268
|
+
TokenTree::Ident(ident) => {
|
269
|
+
if ident.to_string() == "self" {
|
270
|
+
TokenTree::Ident(Ident::new("_slf", ident.span()))
|
271
|
+
} else {
|
272
|
+
TokenTree::Ident(ident)
|
273
|
+
}
|
274
|
+
}
|
275
|
+
TokenTree::Punct(punct) => TokenTree::Punct(punct),
|
276
|
+
TokenTree::Literal(literal) => TokenTree::Literal(literal),
|
277
|
+
})
|
278
|
+
.collect::<Vec<TokenTree>>(),
|
279
|
+
);
|
280
|
+
|
281
|
+
output_stream
|
282
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
[package]
|
2
|
+
name = "rutie-derive"
|
3
|
+
version = "0.1.0"
|
4
|
+
authors = ["Wasmer Engineering Team <engineering@wasmer.io>"]
|
5
|
+
edition = "2018"
|
6
|
+
description = "Provide useful types to complete the `rutie-derive-macros` crate"
|
7
|
+
readme = "README.md"
|
8
|
+
repository = "https://github.com/wasmerio/wasmer-ruby"
|
9
|
+
keywords = ["ruby", "extension"]
|
10
|
+
publish = false
|
11
|
+
|
12
|
+
[dependencies]
|
13
|
+
rutie = "0.8"
|
14
|
+
rutie-derive-macros = { path = "../rutie-derive-macros", version = "0.1.0" }
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# `rutie-derive`
|
2
|
+
|
3
|
+
**Note**: This is not an official project from
|
4
|
+
[`rutie`](https://github.com/danielpclark/rutie).
|
5
|
+
|
6
|
+
We try to extend `rutie` with a new Domain Specific Language (DSL for
|
7
|
+
short) based on new procedural macros to make it easier to declare
|
8
|
+
Ruby modules, classes, methods and functions.
|
9
|
+
|
10
|
+
This crate is the front API that provides the `#[ruby*]` procedural
|
11
|
+
macros from the `rutie-derive-macros` crate, along with some extra
|
12
|
+
traits and implementations to make everything work magically.
|
13
|
+
|
14
|
+
## Ruby class
|
15
|
+
|
16
|
+
To declare a new Ruby class, use `#[rubyclass(module = "X::Y::Z")]`:
|
17
|
+
|
18
|
+
```rust
|
19
|
+
#[rubyclass(module = "Wasmer")]
|
20
|
+
pub struct Foo;
|
21
|
+
```
|
22
|
+
|
23
|
+
It will create a new type `Foo` in Rust, and a new class `Foo` in Ruby
|
24
|
+
inside the `Wasmer` module.
|
25
|
+
|
26
|
+
### Constructor
|
27
|
+
|
28
|
+
Every class has a Rust `ruby_new` associated method to create a new
|
29
|
+
instance of this Ruby type.
|
30
|
+
|
31
|
+
## Ruby methods
|
32
|
+
|
33
|
+
To declare methods attached to a class, use `#[rubymethods]`:
|
34
|
+
|
35
|
+
```rust
|
36
|
+
#[rubymethods]
|
37
|
+
impl Foo {
|
38
|
+
/// Constructor.
|
39
|
+
pub fn new() -> RubyResult<Foo> {
|
40
|
+
Ok(Foo::ruby_new(Foo))
|
41
|
+
}
|
42
|
+
|
43
|
+
// Method.
|
44
|
+
pub fn bar(&self, x: &Integer, y: &Integer) -> RubyResult<Integer> {
|
45
|
+
Ok(Integer::new(x.to_i64() + x.to_i64()))
|
46
|
+
}
|
47
|
+
}
|
48
|
+
```
|
49
|
+
|
50
|
+
Ruby methods are like Rust methods:
|
51
|
+
|
52
|
+
* Without a receiver (`&self`), it's a static method (`def self`),
|
53
|
+
* With a receiver (`&self` or `&mut self`), it's a method (`def`),
|
54
|
+
* All arguments are received by reference,
|
55
|
+
* The returned type is required to be a `RubyResult`. The error type
|
56
|
+
is `AnyException`.
|
57
|
+
|
58
|
+
### “Rust-defined” Ruby types
|
59
|
+
|
60
|
+
Arguments can be of kind “user-defined types”, let's see:
|
61
|
+
|
62
|
+
```rust
|
63
|
+
#[rubyclass(module = "Wasmer")]
|
64
|
+
pub struct Baz {
|
65
|
+
inner: i32,
|
66
|
+
};
|
67
|
+
|
68
|
+
#[rubyclass(module = "Wasmer")]
|
69
|
+
pub struct Foo;
|
70
|
+
|
71
|
+
#[rubymethods]
|
72
|
+
impl Foo {
|
73
|
+
pub fn bar(&self, baz: &Baz) -> RubyResult<…> {
|
74
|
+
let inner = &baz.inner;
|
75
|
+
|
76
|
+
…
|
77
|
+
}
|
78
|
+
}
|
79
|
+
```
|
80
|
+
|
81
|
+
## Ruby functions
|
82
|
+
|
83
|
+
Just like `#[rubymethods]`, `#[rubyfunction]` will create a Ruby
|
84
|
+
function:
|
85
|
+
|
86
|
+
```rust
|
87
|
+
#[rubyfunction]
|
88
|
+
fn hello(who: &RString) -> RubyResult<RString> {
|
89
|
+
Ok(RString::new_utf8(&format!("Hello, {}!", who.to_str())))
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
93
|
+
## License
|
94
|
+
|
95
|
+
Check the license of
|
96
|
+
[`wasmer-ruby`](https://github.com/wasmerio/wasmer-ruby/) as it is
|
97
|
+
part of this same project.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
pub trait ClassInfo {
|
2
|
+
type Class;
|
3
|
+
type RubyClass;
|
4
|
+
}
|
5
|
+
|
6
|
+
pub trait UpcastRubyClass<T> {
|
7
|
+
fn upcast(&self) -> &T;
|
8
|
+
|
9
|
+
fn upcast_mut(&mut self) -> &mut T;
|
10
|
+
}
|
11
|
+
|
12
|
+
macro_rules! id_impl {
|
13
|
+
($ty:ty) => {
|
14
|
+
impl ClassInfo for $ty {
|
15
|
+
type Class = Self;
|
16
|
+
type RubyClass = Self;
|
17
|
+
}
|
18
|
+
|
19
|
+
impl UpcastRubyClass<$ty> for $ty {
|
20
|
+
fn upcast(&self) -> &$ty {
|
21
|
+
self
|
22
|
+
}
|
23
|
+
|
24
|
+
fn upcast_mut(&mut self) -> &mut $ty {
|
25
|
+
self
|
26
|
+
}
|
27
|
+
}
|
28
|
+
};
|
29
|
+
|
30
|
+
( $( $ty:ty ),+ $(,)* ) => {
|
31
|
+
$( id_impl!( $ty ); )*
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
id_impl!(
|
36
|
+
rutie::AnyObject,
|
37
|
+
rutie::Array,
|
38
|
+
rutie::Boolean,
|
39
|
+
rutie::Fixnum,
|
40
|
+
rutie::Float,
|
41
|
+
rutie::Hash,
|
42
|
+
rutie::Integer,
|
43
|
+
rutie::NilClass,
|
44
|
+
rutie::Proc,
|
45
|
+
rutie::RString,
|
46
|
+
rutie::Symbol,
|
47
|
+
);
|