wasmer 0.4.0 → 0.5.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.
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
- }