wasmer 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.