wasmer 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/documentation.yml +50 -0
  3. data/.github/workflows/test.yml +34 -61
  4. data/CHANGELOG.md +89 -29
  5. data/Cargo.lock +812 -380
  6. data/Cargo.toml +7 -20
  7. data/Gemfile +2 -3
  8. data/README.md +1 -0
  9. data/Rakefile +4 -3
  10. data/crates/rutie-derive-macros/Cargo.toml +19 -0
  11. data/crates/rutie-derive-macros/README.md +4 -0
  12. data/crates/rutie-derive-macros/src/class.rs +156 -0
  13. data/crates/rutie-derive-macros/src/function.rs +178 -0
  14. data/crates/rutie-derive-macros/src/lib.rs +27 -0
  15. data/crates/rutie-derive-macros/src/methods.rs +282 -0
  16. data/crates/rutie-derive/Cargo.toml +14 -0
  17. data/crates/rutie-derive/README.md +97 -0
  18. data/crates/rutie-derive/src/lib.rs +4 -0
  19. data/crates/rutie-derive/src/upcast.rs +47 -0
  20. data/crates/rutie-test/Cargo.toml +10 -0
  21. data/crates/rutie-test/src/lib.rs +38 -0
  22. data/crates/wasmer/Cargo.toml +27 -0
  23. data/crates/wasmer/README.md +228 -0
  24. data/crates/wasmer/src/doc.rs +1512 -0
  25. data/crates/wasmer/src/error.rs +55 -0
  26. data/crates/wasmer/src/exports.rs +107 -0
  27. data/crates/wasmer/src/externals/function.rs +159 -0
  28. data/crates/wasmer/src/externals/global.rs +62 -0
  29. data/crates/wasmer/src/externals/memory.rs +117 -0
  30. data/crates/wasmer/src/externals/mod.rs +9 -0
  31. data/crates/wasmer/src/externals/table.rs +41 -0
  32. data/crates/wasmer/src/import_object.rs +78 -0
  33. data/crates/wasmer/src/instance.rs +45 -0
  34. data/crates/wasmer/src/lib.rs +307 -0
  35. data/crates/wasmer/src/memory/mod.rs +1 -0
  36. data/crates/wasmer/src/memory/views.rs +112 -0
  37. data/crates/wasmer/src/module.rs +106 -0
  38. data/crates/wasmer/src/prelude.rs +3 -0
  39. data/crates/wasmer/src/store.rs +22 -0
  40. data/crates/wasmer/src/types.rs +390 -0
  41. data/crates/wasmer/src/values.rs +84 -0
  42. data/crates/wasmer/src/wasi.rs +226 -0
  43. data/crates/wasmer/src/wat.rs +20 -0
  44. data/justfile +7 -1
  45. data/lib/wasmer.rb +29 -3
  46. data/wasmer.gemspec +6 -10
  47. metadata +45 -47
  48. data/README.md +0 -332
  49. data/lib/wasmer/version.rb +0 -3
  50. data/src/error.rs +0 -16
  51. data/src/instance/exports.rs +0 -215
  52. data/src/instance/globals.rs +0 -234
  53. data/src/instance/mod.rs +0 -141
  54. data/src/lib.rs +0 -162
  55. data/src/memory/mod.rs +0 -158
  56. data/src/memory/view.rs +0 -145
  57. data/src/module.rs +0 -28
data/README.md DELETED
@@ -1,332 +0,0 @@
1
- <p align="center">
2
- <a href="https://wasmer.io" target="_blank" rel="noopener">
3
- <img width="300" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/assets/logo.png" alt="Wasmer logo">
4
- </a>
5
- </p>
6
-
7
- <p align="center">
8
- <a href="https://spectrum.chat/wasmer">
9
- <img src="https://withspectrum.github.io/badge/badge.svg" alt="Join the Wasmer Community"></a>
10
- <a href="https://rubygems.org/gems/wasmer">
11
- <img src="https://img.shields.io/gem/v/wasmer.svg" alt="Gem"></a>
12
- <a href="https://rubygems.org/gems/wasmer">
13
- <img src="https://img.shields.io/gem/dt/wasmer.svg" alt="Number of gem downloads"></a>
14
- <a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
15
- <img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"></a>
16
- </p>
17
-
18
- Wasmer is a Ruby library for executing WebAssembly binaries:
19
-
20
- * **Easy to use**: The `wasmer` API mimics the standard WebAssembly API,
21
- * **Fast**: `wasmer` executes the WebAssembly modules as fast as possible,
22
- * **Safe**: All calls to WebAssembly will be fast, but more
23
- importantly, completely safe and sandboxed.
24
-
25
- # Install
26
-
27
- To install the `wasmer` Ruby gem, just run this command in your shell:
28
-
29
- ```sh
30
- $ gem install wasmer
31
- ```
32
-
33
- **Note**: [Rust][rust] is required to install the Ruby library (Cargo
34
- —the build tool for Rust— is used to compile the extension). See [how
35
- to install Rust][install-rust].
36
-
37
- [View the `wasmer` gem on RubyGems][wasmer-gem].
38
-
39
- [rust]: https://www.rust-lang.org/
40
- [install-rust]: https://www.rust-lang.org/tools/install
41
- [wasmer-gem]: https://rubygems.org/gems/wasmer
42
-
43
- # Example
44
-
45
- There is a toy program in `examples/simple.rs`, written in Rust (or
46
- any other language that compiles to Wasm):
47
-
48
- ```rust
49
- #[no_mangle]
50
- pub extern fn sum(x: i32, y: i32) -> i32 {
51
- x + y
52
- }
53
- ```
54
-
55
- Once this program compiled to WebAssembly, we end up with a
56
- `examples/simple.wasm` binary file.
57
-
58
- Then, we can execute it in Ruby (!) with the `examples/simple.rb` file:
59
-
60
- ```rb
61
- require "wasmer"
62
-
63
- bytes = IO.read "simple.wasm", mode: "rb"
64
- instance = Wasmer::Instance.new bytes
65
- puts instance.exports.sum 1, 2
66
- ```
67
-
68
- And then, finally, enjoy by running:
69
-
70
- ```sh
71
- $ ruby simple.rb
72
- 3
73
- ```
74
-
75
- # API documentation
76
-
77
- ## The `Instance` class
78
-
79
- Instantiates a WebAssembly module represented by bytes, and calls
80
- exported functions on it:
81
-
82
- ```ruby
83
- require "wasmer"
84
-
85
- # Get the Wasm module as bytes.
86
- wasm_bytes = IO.read "my_program.wasm", mode: "rb"
87
-
88
- # Instantiates the Wasm module.
89
- instance = Wasmer::Instance.new wasm_bytes
90
-
91
- # Call a function on it.
92
- result = instance.exports.sum 1, 2
93
-
94
- puts result # 3
95
- ```
96
-
97
- ### Exported functions
98
-
99
- All exported functions are accessible on the `exports`
100
- getter. Arguments of these functions are automatically casted to
101
- WebAssembly values.
102
-
103
- ### Exported memory
104
-
105
- The `memory` getter exposes the `Memory` class representing the memory
106
- of that particular instance, e.g.:
107
-
108
- ```ruby
109
- view = instance.memory.uint8_view
110
- ```
111
-
112
- `Instance.memory` throws an exception if no memory is exported.
113
-
114
- See below for more information.
115
-
116
- ### Exported globals
117
-
118
- The `globals` getter exposes the `ExportedGlobal` class represented an
119
- exported global variable, e.g.:
120
-
121
- ```ruby
122
- # Get the `x` global.
123
- x = instance.globals.x
124
-
125
- # Check whether the global is mutable.
126
- assert x.mutable
127
- assert_equal x.value, 7
128
-
129
- # Update its value.
130
- x.value = 42
131
-
132
- # Tada!
133
- assert_equal x.value, 42
134
- ```
135
-
136
- ## The `Memory` class
137
-
138
- A WebAssembly instance has its own memory, represented by the `Memory`
139
- class. It is accessible by the `Wasmer::Instance.memory` getter.
140
-
141
- The `Memory.grow` methods allows to grow the memory by a number of
142
- pages (of 65kb each).
143
-
144
- ```ruby
145
- instance.memory.grow 1
146
- ```
147
-
148
- The `Memory` class offers methods to create views of the memory
149
- internal buffer, e.g. `uint8_view`, `int8_view`, `uint16_view`
150
- etc. All these methods accept one optional argument: `offset`, to
151
- subset the memory buffer at a particular offset. These methods return
152
- respectively a `*Array` object, i.e. `uint8_view` returns a
153
- `Uint8Array` object etc.
154
-
155
- ```ruby
156
- offset = 7
157
- view = instance.memory.uint8_view offset
158
-
159
- puts view[0]
160
- ```
161
-
162
- ### The `*Array` classes
163
-
164
- These classes represent views over a memory buffer of an instance.
165
-
166
- | Class | View buffer as a sequence of… | Bytes per element |
167
- |-|-|-|
168
- | `Int8Array` | `int8` | 1 |
169
- | `Uint8Array` | `uint8` | 1 |
170
- | `Int16Array` | `int16` | 2 |
171
- | `Uint16Array` | `uint16` | 2 |
172
- | `Int32Array` | `int32` | 4 |
173
- | `Uint32Array` | `uint32` | 4 |
174
-
175
- All these classes share the same implementation. Taking the example of
176
- `Uint8Array`, the class looks like this:
177
-
178
- ```ruby
179
- class Uint8Array
180
- def bytes_per_element
181
- def length
182
- def [](index)
183
- def []=(index, value)
184
- end
185
- ```
186
-
187
- Let's see it in action:
188
-
189
- ```ruby
190
- require "wasmer"
191
-
192
- # Get the Wasm module as bytes.
193
- wasm_bytes = IO.read "my_program.wasm", mode: "rb"
194
-
195
- # Instantiates the Wasm module.
196
- instance = Wasmer::Instance.new wasm_bytes
197
-
198
- # Call a function that returns a pointer to a string for instance.
199
- pointer = instance.exports.return_string
200
-
201
- # Get the memory view, with the offset set to `pointer` (default is 0).
202
- memory = instance.memory.uint8_view pointer
203
-
204
- # Read the string pointed by the pointer.
205
-
206
- string = ""
207
-
208
- memory.each do |char|
209
- break if char == 0
210
- string += char.chr
211
- end
212
-
213
- puts string # Hello, World!
214
- ```
215
-
216
- Notice that `*Array` treat bytes in little-endian, as required by the
217
- WebAssembly specification, [Chapter Structure, Section Instructions,
218
- Sub-Section Memory
219
- Instructions](https://webassembly.github.io/spec/core/syntax/instructions.html#memory-instructions):
220
-
221
- > All values are read and written in [little
222
- > endian](https://en.wikipedia.org/wiki/Endianness#Little-endian) byte
223
- > order.
224
-
225
- Each view shares the same memory buffer internally. Let's have some fun:
226
-
227
- ```ruby
228
- int8 = instance.memory.int8_view
229
- int16 = instance.memory.int16_view
230
- int32 = instance.memory.int32_view
231
-
232
- b₁
233
- ┌┬┬┬┬┬┬┐
234
- int8[0] = 0b00000001
235
- b₂
236
- ┌┬┬┬┬┬┬┐
237
- int8[1] = 0b00000100
238
- b₃
239
- ┌┬┬┬┬┬┬┐
240
- int8[2] = 0b00010000
241
- b₄
242
- ┌┬┬┬┬┬┬┐
243
- int8[3] = 0b01000000
244
-
245
- // No surprise with the following assertions.
246
- b₁
247
- ┌┬┬┬┬┬┬┐
248
- assert_equal 0b00000001, int8[0]
249
- b₂
250
- ┌┬┬┬┬┬┬┐
251
- assert_equal 0b00000100, int8[1]
252
- b₃
253
- ┌┬┬┬┬┬┬┐
254
- assert_equal 0b00010000, int8[2]
255
- b₄
256
- ┌┬┬┬┬┬┬┐
257
- assert_equal 0b01000000, int8[3]
258
-
259
- // The `int16` view reads 2 bytes.
260
- b₂ b₁
261
- ┌┬┬┬┬┬┬┐ ┌┬┬┬┬┬┬┐
262
- assert_equal 0b00000100_00000001, int16[0]
263
- b₄ b₃
264
- ┌┬┬┬┬┬┬┐ ┌┬┬┬┬┬┬┐
265
- assert_equal 0b01000000_00010000, int16[1]
266
-
267
- // The `int32` view reads 4 bytes.
268
- b₄ b₃ b₂ b₁
269
- ┌┬┬┬┬┬┬┐ ┌┬┬┬┬┬┬┐ ┌┬┬┬┬┬┬┐ ┌┬┬┬┬┬┬┐
270
- assert_equal 0b01000000_00010000_00000100_00000001, int32[0]
271
- ```
272
-
273
- ## The `Module` class
274
-
275
- The `Module` class contains one static method `validate`, that checks
276
- whether the given bytes represent valid WebAssembly bytes:
277
-
278
- ```ruby
279
- require "wasmer"
280
-
281
- wasm_bytes = IO.read "my_program.wasm", mode: "rb"
282
-
283
- if not Wasmer::Module.validate wasm_bytes
284
- puts "The program seems corrupted."
285
- end
286
- ```
287
-
288
- This function returns a boolean.
289
-
290
- # Install and Testing
291
-
292
- To compile the entire project, run the following commands:
293
-
294
- ```sh
295
- $ just build
296
- $ just test
297
- $ ruby examples/simple.rb
298
- ```
299
-
300
- (Yes, you need [`just`][just]).
301
-
302
- [just]: https://github.com/casey/just/
303
-
304
- # What is WebAssembly?
305
-
306
- Quoting [the WebAssembly site][wasm]:
307
-
308
- > WebAssembly (abbreviated Wasm) is a binary instruction format for a
309
- > stack-based virtual machine. Wasm is designed as a portable target
310
- > for compilation of high-level languages like C/C++/Rust, enabling
311
- > deployment on the web for client and server applications.
312
-
313
- About speed:
314
-
315
- > WebAssembly aims to execute at native speed by taking advantage of
316
- > [common hardware
317
- > capabilities](https://webassembly.org/docs/portability/#assumptions-for-efficient-execution)
318
- > available on a wide range of platforms.
319
-
320
- About safety:
321
-
322
- > WebAssembly describes a memory-safe, sandboxed [execution
323
- > environment](https://webassembly.org/docs/semantics/#linear-memory) […].
324
-
325
- [wasm]: https://webassembly.org/
326
-
327
- # License
328
-
329
- The entire project is under the MIT License. Please read [the
330
- `LICENSE` file][license].
331
-
332
- [license]: https://github.com/wasmerio/wasmer/blob/master/LICENSE
@@ -1,3 +0,0 @@
1
- module Wasmer
2
- VERSION = "0.4.0"
3
- end
data/src/error.rs DELETED
@@ -1,16 +0,0 @@
1
- //! Functions to handle error or exception correctly.
2
-
3
- use rutie::{AnyException, VM};
4
-
5
- pub fn unwrap_or_raise<Output, Function>(f: Function) -> Output
6
- where
7
- Function: FnOnce() -> Result<Output, AnyException>,
8
- {
9
- match f() {
10
- Ok(x) => x,
11
- Err(e) => {
12
- VM::raise_ex(e);
13
- unreachable!()
14
- }
15
- }
16
- }
@@ -1,215 +0,0 @@
1
- //! The `ExportedFunctions` WebAssembly class.
2
-
3
- use crate::error::unwrap_or_raise;
4
- use lazy_static::lazy_static;
5
- use rutie::{
6
- class, methods,
7
- rubysys::{class, value::ValueType},
8
- types::{Argc, Value},
9
- util::str_to_cstring,
10
- wrappable_struct, AnyException, AnyObject, Array, Boolean, Exception, Fixnum, Float, NilClass,
11
- Object, Symbol,
12
- };
13
- use std::rc::Rc;
14
- use wasmer_runtime::{self as runtime, types::Type};
15
-
16
- /// The `ExportedFunctions` Ruby class.
17
- pub struct ExportedFunctions {
18
- /// The WebAssembly runtime.
19
- instance: Rc<runtime::Instance>,
20
- }
21
-
22
- impl ExportedFunctions {
23
- /// Create a new instance of the `ExportedFunctions` Ruby class.
24
- pub fn new(instance: Rc<runtime::Instance>) -> Self {
25
- Self { instance }
26
- }
27
-
28
- /// Check that an exported function exists.
29
- pub fn respond_to_missing(&self, method_name: &str) -> bool {
30
- self.instance.dyn_func(method_name).is_ok()
31
- }
32
-
33
- /// Call an exported function on the given WebAssembly instance.
34
- pub fn method_missing(
35
- &self,
36
- method_name: &str,
37
- arguments: Array,
38
- ) -> Result<AnyObject, AnyException> {
39
- let function = self.instance.dyn_func(method_name).map_err(|_| {
40
- AnyException::new(
41
- "RuntimeError",
42
- Some(&format!("Function `{}` does not exist.", method_name)),
43
- )
44
- })?;
45
- let signature = function.signature();
46
- let parameters = signature.params();
47
- let number_of_parameters = parameters.len() as isize;
48
- let number_of_arguments = arguments.length() as isize;
49
- let diff: isize = number_of_parameters - number_of_arguments;
50
-
51
- if diff > 0 {
52
- return Err(AnyException::new(
53
- "ArgumentError",
54
- Some(&format!(
55
- "Missing {} argument(s) when calling `{}`: Expect {} argument(s), given {}.",
56
- diff, method_name, number_of_parameters, number_of_arguments
57
- )),
58
- ));
59
- } else if diff < 0 {
60
- return Err(AnyException::new(
61
- "ArgumentError",
62
- Some(&format!(
63
- "Given {} extra argument(s) when calling `{}`: Expect {} argument(s), given {}.",
64
- diff.abs(), method_name, number_of_parameters, number_of_arguments
65
- )),
66
- ));
67
- }
68
-
69
- let mut function_arguments =
70
- Vec::<runtime::Value>::with_capacity(number_of_parameters as usize);
71
-
72
- for (nth, (parameter, argument)) in parameters.iter().zip(arguments.into_iter()).enumerate()
73
- {
74
- let value = match (parameter, argument.ty()) {
75
- (Type::I32, ValueType::Fixnum) => runtime::Value::I32(
76
- argument
77
- .try_convert_to::<Fixnum>()
78
- .map_err(|_| {
79
- AnyException::new(
80
- "TypeError",
81
- Some(&format!(
82
- "Cannot convert argument #{} to a WebAssembly i32 value.",
83
- nth + 1
84
- )),
85
- )
86
- })?
87
- .to_i32(),
88
- ),
89
- (Type::I64, ValueType::Fixnum) => runtime::Value::I64(
90
- argument
91
- .try_convert_to::<Fixnum>()
92
- .map_err(|_| {
93
- AnyException::new(
94
- "TypeError",
95
- Some(&format!(
96
- "Cannot convert argument #{} to a WebAssembly i64 value.",
97
- nth + 1
98
- )),
99
- )
100
- })?
101
- .to_i64(),
102
- ),
103
- (Type::F32, ValueType::Float) => runtime::Value::F32(
104
- argument
105
- .try_convert_to::<Float>()
106
- .map_err(|_| {
107
- AnyException::new(
108
- "TypeError",
109
- Some(&format!(
110
- "Cannot convert argument #{} to a WebAssembly f32 value.",
111
- nth + 1
112
- )),
113
- )
114
- })?
115
- .to_f64() as f32,
116
- ),
117
- (Type::F64, ValueType::Float) => runtime::Value::F64(
118
- argument
119
- .try_convert_to::<Float>()
120
- .map_err(|_| {
121
- AnyException::new(
122
- "TypeError",
123
- Some(&format!(
124
- "Cannot convert argument #{} to a WebAssembly f64 value.",
125
- nth + 1
126
- )),
127
- )
128
- })?
129
- .to_f64(),
130
- ),
131
- (_, ty) => {
132
- return Err(AnyException::new(
133
- "ArgumentError",
134
- Some(&format!(
135
- "Cannot convert argument #{} to a WebAssembly value. Only integers and floats are supported. Given `{:?}`.",
136
- nth + 1,
137
- ty
138
- ))));
139
- }
140
- };
141
-
142
- function_arguments.push(value);
143
- }
144
-
145
- let results = function
146
- .call(function_arguments.as_slice())
147
- .map_err(|e| AnyException::new("RuntimeError", Some(&format!("{}", e))))?;
148
-
149
- if !results.is_empty() {
150
- Ok(match results[0] {
151
- runtime::Value::I32(result) => Fixnum::new(result as i64).into(),
152
- runtime::Value::I64(result) => Fixnum::new(result).into(),
153
- runtime::Value::F32(result) => Float::new(result as f64).into(),
154
- runtime::Value::F64(result) => Float::new(result).into(),
155
- runtime::Value::V128(_result) => {
156
- return Err(AnyException::new(
157
- "RuntimeError",
158
- Some("Type `V128` isn't supported yet."),
159
- ))
160
- }
161
- })
162
- } else {
163
- Ok(NilClass::new().into())
164
- }
165
- }
166
- }
167
-
168
- wrappable_struct!(
169
- ExportedFunctions,
170
- ExportedFunctionsWrapper,
171
- EXPORTED_FUNCTIONS_WRAPPER
172
- );
173
-
174
- class!(RubyExportedFunctions);
175
-
176
- #[rustfmt::skip]
177
- methods!(
178
- RubyExportedFunctions,
179
- itself,
180
-
181
- // Glue code to call the `ExportedFunctions.respond_to` method.
182
- fn ruby_exported_functions_method_exists(symbol: Symbol, _include_private: Boolean) -> Boolean {
183
- unwrap_or_raise(|| {
184
- let symbol = symbol?;
185
- let exported_functions = itself.get_data(&*EXPORTED_FUNCTIONS_WRAPPER);
186
-
187
- Ok(Boolean::new(exported_functions.respond_to_missing(symbol.to_str())))
188
- })
189
- }
190
- );
191
-
192
- /// Glue code to call the `ExportedFunctions.method_missing` method.
193
- pub extern "C" fn ruby_exported_functions_method_missing(
194
- argc: Argc,
195
- argv: *const AnyObject,
196
- itself: RubyExportedFunctions,
197
- ) -> AnyObject {
198
- unwrap_or_raise(|| {
199
- let arguments = Value::from(0);
200
-
201
- unsafe {
202
- let argv_pointer = argv as *const Value;
203
-
204
- class::rb_scan_args(argc, argv_pointer, str_to_cstring("*").as_ptr(), &arguments)
205
- };
206
-
207
- let mut arguments = Array::from(arguments);
208
- let method_name = unsafe { arguments.shift().to::<Symbol>() };
209
- let method_name = method_name.to_str();
210
-
211
- itself
212
- .get_data(&*EXPORTED_FUNCTIONS_WRAPPER)
213
- .method_missing(method_name, arguments)
214
- })
215
- }