wasmer 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.
data/Cargo.toml CHANGED
@@ -1,7 +1,7 @@
1
1
  [package]
2
2
  publish = false
3
3
  name = "ruby-ext-wasm"
4
- version = "0.2.0"
4
+ version = "0.3.0"
5
5
  authors = ["Ivan Enderlin <ivan.enderlin@hoa-project.net>"]
6
6
  edition = "2018"
7
7
  description = "Ruby extension to run WebAssembly binaries"
@@ -15,7 +15,7 @@ name = "wasmer"
15
15
  crate-type = ["dylib"]
16
16
 
17
17
  [dependencies]
18
- wasmer-runtime = "0.4.0"
19
- wasmer-runtime-core = "0.4.0"
20
- rutie = "0.5.4"
18
+ wasmer-runtime = "0.5.5"
19
+ wasmer-runtime-core = "0.5.5"
20
+ rutie = "0.6.0"
21
21
  lazy_static = "1.3.0"
data/README.md CHANGED
@@ -10,7 +10,7 @@
10
10
  <a href="https://rubygems.org/gems/wasmer">
11
11
  <img src="https://img.shields.io/gem/v/wasmer.svg" alt="Gem"></a>
12
12
  <a href="https://rubygems.org/gems/wasmer">
13
- <img src="https://img.shields.io/gem/dt/wasmer.svg" alt="Gem"></a>
13
+ <img src="https://img.shields.io/gem/dt/wasmer.svg" alt="Number of gem downloads"></a>
14
14
  <a href="https://github.com/wasmerio/wasmer/blob/master/LICENSE">
15
15
  <img src="https://img.shields.io/github/license/wasmerio/wasmer.svg" alt="License"></a>
16
16
  </p>
@@ -61,7 +61,7 @@ Then, we can execute it in Ruby (!) with the `examples/simple.rb` file:
61
61
  require "wasmer"
62
62
 
63
63
  bytes = IO.read "simple.wasm", mode: "rb"
64
- instance = Instance.new bytes
64
+ instance = Wasmer::Instance.new bytes
65
65
  puts instance.exports.sum 1, 2
66
66
  ```
67
67
 
@@ -85,7 +85,7 @@ require "wasmer"
85
85
  wasm_bytes = IO.read "my_program.wasm", mode: "rb"
86
86
 
87
87
  # Instantiates the Wasm module.
88
- instance = Instance.new wasm_bytes
88
+ instance = Wasmer::Instance.new wasm_bytes
89
89
 
90
90
  # Call a function on it.
91
91
  result = instance.exports.sum 1, 2
@@ -109,7 +109,14 @@ See below for more information.
109
109
  ## The `Memory` class
110
110
 
111
111
  A WebAssembly instance has its own memory, represented by the `Memory`
112
- class. It is accessible by the `Instance.memory` getter.
112
+ class. It is accessible by the `Wasmer::Instance.memory` getter.
113
+
114
+ The `Memory.grow` methods allows to grow the memory by a number of
115
+ pages (of 65kb each).
116
+
117
+ ```ruby
118
+ instance.memory.grow 1
119
+ ```
113
120
 
114
121
  The `Memory` class offers methods to create views of the memory
115
122
  internal buffer, e.g. `uint8_view`, `int8_view`, `uint16_view`
@@ -159,7 +166,7 @@ require "wasmer"
159
166
  wasm_bytes = IO.read "my_program.wasm", mode: "rb"
160
167
 
161
168
  # Instantiates the Wasm module.
162
- instance = Instance.new wasm_bytes
169
+ instance = Wasmer::Instance.new wasm_bytes
163
170
 
164
171
  # Call a function that returns a pointer to a string for instance.
165
172
  pointer = instance.exports.return_string
@@ -168,18 +175,12 @@ pointer = instance.exports.return_string
168
175
  memory = instance.memory.uint8_view pointer
169
176
 
170
177
  # Read the string pointed by the pointer.
171
- nth = 0
172
- string = ""
173
-
174
- while true
175
- char = memory[nth]
176
178
 
177
- if 0 == char
178
- break
179
- end
179
+ string = ""
180
180
 
181
+ memory.each do |char|
182
+ break if char == 0
181
183
  string += char.chr
182
- nth += 1
183
184
  end
184
185
 
185
186
  puts string # Hello, World!
@@ -252,7 +253,7 @@ require "wasmer"
252
253
 
253
254
  wasm_bytes = IO.read "my_program.wasm", mode: "rb"
254
255
 
255
- if not Module.validate wasm_bytes
256
+ if not Wasmer::Module.validate wasm_bytes
256
257
  puts "The program seems corrupted."
257
258
  end
258
259
  ```
@@ -298,7 +299,7 @@ About safety:
298
299
 
299
300
  # License
300
301
 
301
- The entire project is under the BSD-3-Clause license. Please read [the
302
+ The entire project is under the MIT License. Please read [the
302
303
  `LICENSE` file][license].
303
304
 
304
305
  [license]: https://github.com/wasmerio/wasmer/blob/master/LICENSE
@@ -0,0 +1,4 @@
1
+ status = [ "ci/circleci: build-and-test" ]
2
+ required_approvals = 0
3
+ timeout_sec = 900
4
+ delete_merged_branches = true
@@ -1,3 +1,3 @@
1
1
  module Wasmer
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1,16 @@
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,14 +1,17 @@
1
1
  //! The `Instance` WebAssembly class.
2
2
 
3
- use crate::memory::{Memory, RubyMemory, MEMORY_WRAPPER};
3
+ use crate::{
4
+ error::unwrap_or_raise,
5
+ memory::{Memory, RubyMemory, MEMORY_WRAPPER},
6
+ };
4
7
  use lazy_static::lazy_static;
5
8
  use rutie::{
6
9
  class, methods,
7
10
  rubysys::{class, value::ValueType},
8
11
  types::{Argc, Value},
9
12
  util::str_to_cstring,
10
- wrappable_struct, AnyException, AnyObject, Array, Class, Exception, Fixnum, Float, Object,
11
- RString, Symbol, VM,
13
+ wrappable_struct, AnyException, AnyObject, Array, Boolean, Exception, Fixnum, Float, Module,
14
+ NilClass, Object, RString, Symbol,
12
15
  };
13
16
  use std::{mem, rc::Rc};
14
17
  use wasmer_runtime::{self as runtime, imports, Export};
@@ -26,18 +29,23 @@ impl ExportedFunctions {
26
29
  Self { instance }
27
30
  }
28
31
 
32
+ /// Check that an exported function exists.
33
+ pub fn respond_to_missing(&self, method_name: &str) -> bool {
34
+ self.instance.dyn_func(method_name).is_ok()
35
+ }
36
+
29
37
  /// Call an exported function on the given WebAssembly instance.
30
- pub fn method_missing(&self, method_name: &str, arguments: Array) -> AnyObject {
31
- let function = self
32
- .instance
33
- .dyn_func(method_name)
34
- .map_err(|_| {
35
- VM::raise_ex(AnyException::new(
36
- "RuntimeError",
37
- Some(&format!("Function `{}` does not exist.", method_name)),
38
- ))
39
- })
40
- .unwrap();
38
+ pub fn method_missing(
39
+ &self,
40
+ method_name: &str,
41
+ arguments: Array,
42
+ ) -> Result<AnyObject, AnyException> {
43
+ let function = self.instance.dyn_func(method_name).map_err(|_| {
44
+ AnyException::new(
45
+ "RuntimeError",
46
+ Some(&format!("Function `{}` does not exist.", method_name)),
47
+ )
48
+ })?;
41
49
  let signature = function.signature();
42
50
  let parameters = signature.params();
43
51
  let number_of_parameters = parameters.len() as isize;
@@ -45,23 +53,21 @@ impl ExportedFunctions {
45
53
  let diff: isize = number_of_parameters - number_of_arguments;
46
54
 
47
55
  if diff > 0 {
48
- VM::raise_ex(AnyException::new(
56
+ return Err(AnyException::new(
49
57
  "ArgumentError",
50
58
  Some(&format!(
51
59
  "Missing {} argument(s) when calling `{}`: Expect {} argument(s), given {}.",
52
60
  diff, method_name, number_of_parameters, number_of_arguments
53
61
  )),
54
62
  ));
55
- unreachable!();
56
63
  } else if diff < 0 {
57
- VM::raise_ex(AnyException::new(
64
+ return Err(AnyException::new(
58
65
  "ArgumentError",
59
66
  Some(&format!(
60
67
  "Given {} extra argument(s) when calling `{}`: Expect {} argument(s), given {}.",
61
68
  diff.abs(), method_name, number_of_parameters, number_of_arguments
62
69
  )),
63
70
  ));
64
- unreachable!();
65
71
  }
66
72
 
67
73
  let mut function_arguments =
@@ -74,71 +80,66 @@ impl ExportedFunctions {
74
80
  argument
75
81
  .try_convert_to::<Fixnum>()
76
82
  .map_err(|_| {
77
- VM::raise_ex(AnyException::new(
83
+ AnyException::new(
78
84
  "TypeError",
79
85
  Some(&format!(
80
86
  "Cannot convert argument #{} to a WebAssembly i32 value.",
81
87
  nth + 1
82
88
  )),
83
- ))
84
- })
85
- .unwrap()
89
+ )
90
+ })?
86
91
  .to_i32(),
87
92
  ),
88
93
  (Type::I64, ValueType::Fixnum) => runtime::Value::I64(
89
94
  argument
90
95
  .try_convert_to::<Fixnum>()
91
96
  .map_err(|_| {
92
- VM::raise_ex(AnyException::new(
97
+ AnyException::new(
93
98
  "TypeError",
94
99
  Some(&format!(
95
100
  "Cannot convert argument #{} to a WebAssembly i64 value.",
96
101
  nth + 1
97
102
  )),
98
- ))
99
- })
100
- .unwrap()
103
+ )
104
+ })?
101
105
  .to_i64(),
102
106
  ),
103
107
  (Type::F32, ValueType::Float) => runtime::Value::F32(
104
108
  argument
105
109
  .try_convert_to::<Float>()
106
110
  .map_err(|_| {
107
- VM::raise_ex(AnyException::new(
111
+ AnyException::new(
108
112
  "TypeError",
109
113
  Some(&format!(
110
114
  "Cannot convert argument #{} to a WebAssembly f32 value.",
111
115
  nth + 1
112
116
  )),
113
- ))
114
- })
115
- .unwrap()
117
+ )
118
+ })?
116
119
  .to_f64() as f32,
117
120
  ),
118
121
  (Type::F64, ValueType::Float) => runtime::Value::F64(
119
122
  argument
120
123
  .try_convert_to::<Float>()
121
124
  .map_err(|_| {
122
- VM::raise_ex(AnyException::new(
125
+ AnyException::new(
123
126
  "TypeError",
124
127
  Some(&format!(
125
128
  "Cannot convert argument #{} to a WebAssembly f64 value.",
126
129
  nth + 1
127
130
  )),
128
- ))
129
- })
130
- .unwrap()
131
+ )
132
+ })?
131
133
  .to_f64(),
132
134
  ),
133
135
  (_, ty) => {
134
- VM::raise_ex(AnyException::new(
136
+ return Err(AnyException::new(
135
137
  "ArgumentError",
136
138
  Some(&format!(
137
139
  "Cannot convert argument #{} to a WebAssembly value. Only integers and floats are supported. Given `{:?}`.",
138
140
  nth + 1,
139
141
  ty
140
142
  ))));
141
- unreachable!()
142
143
  }
143
144
  };
144
145
 
@@ -147,14 +148,17 @@ impl ExportedFunctions {
147
148
 
148
149
  let results = function
149
150
  .call(function_arguments.as_slice())
150
- .map_err(|e| VM::raise_ex(AnyException::new("RuntimeError", Some(&format!("{}", e)))))
151
- .unwrap();
152
-
153
- match results[0] {
154
- runtime::Value::I32(result) => Fixnum::new(result as i64).into(),
155
- runtime::Value::I64(result) => Fixnum::new(result).into(),
156
- runtime::Value::F32(result) => Float::new(result as f64).into(),
157
- runtime::Value::F64(result) => Float::new(result).into(),
151
+ .map_err(|e| AnyException::new("RuntimeError", Some(&format!("{}", e))))?;
152
+
153
+ if results.len() > 0 {
154
+ Ok(match results[0] {
155
+ runtime::Value::I32(result) => Fixnum::new(result as i64).into(),
156
+ runtime::Value::I64(result) => Fixnum::new(result).into(),
157
+ runtime::Value::F32(result) => Float::new(result as f64).into(),
158
+ runtime::Value::F64(result) => Float::new(result).into(),
159
+ })
160
+ } else {
161
+ Ok(NilClass::new().into())
158
162
  }
159
163
  }
160
164
  }
@@ -167,27 +171,45 @@ wrappable_struct!(
167
171
 
168
172
  class!(RubyExportedFunctions);
169
173
 
174
+ #[rustfmt::skip]
175
+ methods!(
176
+ RubyExportedFunctions,
177
+ itself,
178
+
179
+ // Glue code to call the `ExportedFunctions.respond_to` method.
180
+ fn ruby_exported_functions_method_exists(symbol: Symbol, _include_private: Boolean) -> Boolean {
181
+ unwrap_or_raise(|| {
182
+ let symbol = symbol?;
183
+ let instance = itself.get_data(&*EXPORTED_FUNCTIONS_WRAPPER);
184
+
185
+ Ok(Boolean::new(instance.respond_to_missing(symbol.to_str())))
186
+ })
187
+ }
188
+ );
189
+
170
190
  /// Glue code to call the `ExportedFunctions.method_missing` method.
171
191
  pub extern "C" fn ruby_exported_functions_method_missing(
172
192
  argc: Argc,
173
193
  argv: *const AnyObject,
174
194
  itself: RubyExportedFunctions,
175
195
  ) -> AnyObject {
176
- let arguments = Value::from(0);
196
+ unwrap_or_raise(|| {
197
+ let arguments = Value::from(0);
177
198
 
178
- unsafe {
179
- let argv_pointer: *const Value = mem::transmute(argv);
199
+ unsafe {
200
+ let argv_pointer: *const Value = mem::transmute(argv);
180
201
 
181
- class::rb_scan_args(argc, argv_pointer, str_to_cstring("*").as_ptr(), &arguments)
182
- };
202
+ class::rb_scan_args(argc, argv_pointer, str_to_cstring("*").as_ptr(), &arguments)
203
+ };
183
204
 
184
- let mut arguments = Array::from(arguments);
185
- let method_name = unsafe { arguments.shift().to::<Symbol>() };
186
- let method_name = method_name.to_str();
205
+ let mut arguments = Array::from(arguments);
206
+ let method_name = unsafe { arguments.shift().to::<Symbol>() };
207
+ let method_name = method_name.to_str();
187
208
 
188
- itself
189
- .get_data(&*EXPORTED_FUNCTIONS_WRAPPER)
190
- .method_missing(method_name, arguments)
209
+ itself
210
+ .get_data(&*EXPORTED_FUNCTIONS_WRAPPER)
211
+ .method_missing(method_name, arguments)
212
+ })
191
213
  }
192
214
 
193
215
  /// The `Instance` Ruby class.
@@ -199,20 +221,17 @@ pub struct Instance {
199
221
  impl Instance {
200
222
  /// Create a new instance of the `Instance` Ruby class.
201
223
  /// The constructor receives bytes from a string.
202
- pub fn new(bytes: &[u8]) -> Self {
224
+ pub fn new(bytes: &[u8]) -> Result<Self, AnyException> {
203
225
  let import_object = imports! {};
204
- let instance = Rc::new(
205
- runtime::instantiate(bytes, &import_object)
206
- .map_err(|e| {
207
- VM::raise_ex(AnyException::new(
208
- "RuntimeError",
209
- Some(&format!("Failed to instantiate the module:\n {}", e)),
210
- ))
211
- })
212
- .unwrap(),
213
- );
214
226
 
215
- Self { instance }
227
+ Ok(Self {
228
+ instance: Rc::new(runtime::instantiate(bytes, &import_object).map_err(|e| {
229
+ AnyException::new(
230
+ "RuntimeError",
231
+ Some(&format!("Failed to instantiate the module:\n {}", e)),
232
+ )
233
+ })?),
234
+ })
216
235
  }
217
236
  }
218
237
 
@@ -224,46 +243,56 @@ class!(RubyInstance);
224
243
  methods!(
225
244
  RubyInstance,
226
245
  _itself,
246
+
227
247
  // Glue code to call the `Instance.new` method.
228
248
  fn ruby_instance_new(bytes: RString) -> AnyObject {
229
- let instance = Instance::new(
230
- bytes
231
- .map_err(|_| {
232
- VM::raise_ex(AnyException::new(
233
- "ArgumentError",
234
- Some("WebAssembly module must be represented by Ruby bytes only."),
235
- ))
249
+ unwrap_or_raise(|| {
250
+ let instance = Instance::new(
251
+ bytes
252
+ .map_err(|_| {
253
+ AnyException::new(
254
+ "ArgumentError",
255
+ Some("WebAssembly module must be represented by Ruby bytes only."),
256
+ )
257
+ })?
258
+ .to_bytes_unchecked(),
259
+ )?;
260
+ let exported_functions = ExportedFunctions::new(instance.instance.clone());
261
+
262
+ let memory = instance
263
+ .instance
264
+ .exports()
265
+ .find_map(|(_, export)| match export {
266
+ Export::Memory(memory) => Some(Memory::new(Rc::new(memory))),
267
+ _ => None,
236
268
  })
237
- .unwrap()
238
- .to_bytes_unchecked(),
239
- );
240
- let exported_functions = ExportedFunctions::new(instance.instance.clone());
241
-
242
- let memory = instance
243
- .instance
244
- .exports()
245
- .find_map(|(_, export)| match export {
246
- Export::Memory(memory) => Some(Memory::new(Rc::new(memory))),
247
- _ => None,
248
- })
249
- .ok_or_else(|| VM::raise_ex(AnyException::new("RuntimeError", Some("The WebAssembly module has no exported memory."))))
250
- .unwrap();
269
+ .ok_or_else(|| {
270
+ AnyException::new(
271
+ "RuntimeError",
272
+ Some("The WebAssembly module has no exported memory."),
273
+ )
274
+ })?;
275
+
276
+ let wasmer_module = Module::from_existing("Wasmer");
251
277
 
252
- let mut ruby_instance: AnyObject =
253
- Class::from_existing("Instance").wrap_data(instance, &*INSTANCE_WRAPPER);
278
+ let mut ruby_instance: AnyObject = wasmer_module
279
+ .get_nested_class("Instance")
280
+ .wrap_data(instance, &*INSTANCE_WRAPPER);
254
281
 
255
- let ruby_exported_functions: RubyExportedFunctions =
256
- Class::from_existing("ExportedFunctions")
282
+ let ruby_exported_functions: RubyExportedFunctions = wasmer_module
283
+ .get_nested_class("ExportedFunctions")
257
284
  .wrap_data(exported_functions, &*EXPORTED_FUNCTIONS_WRAPPER);
258
285
 
259
- ruby_instance.instance_variable_set("@exports", ruby_exported_functions);
286
+ ruby_instance.instance_variable_set("@exports", ruby_exported_functions);
260
287
 
261
- let ruby_memory: RubyMemory =
262
- Class::from_existing("Memory").wrap_data(memory, &*MEMORY_WRAPPER);
288
+ let ruby_memory: RubyMemory = wasmer_module
289
+ .get_nested_class("Memory")
290
+ .wrap_data(memory, &*MEMORY_WRAPPER);
263
291
 
264
- ruby_instance.instance_variable_set("@memory", ruby_memory);
292
+ ruby_instance.instance_variable_set("@memory", ruby_memory);
265
293
 
266
- ruby_instance
294
+ Ok(ruby_instance)
295
+ })
267
296
  }
268
297
 
269
298
  // Glue code to call the `Instance.exports` getter method.