wasmer 0.3.0 → 0.4.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.3.0"
4
+ version = "0.4.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,6 @@ name = "wasmer"
15
15
  crate-type = ["dylib"]
16
16
 
17
17
  [dependencies]
18
- wasmer-runtime = "0.5.5"
19
- wasmer-runtime-core = "0.5.5"
20
- rutie = "0.6.0"
21
- lazy_static = "1.3.0"
18
+ wasmer-runtime = "0.14"
19
+ rutie = "0.7"
20
+ lazy_static = "1.4"
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019-present Wasmer, Inc. and its affiliates.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
- <a href="https://wasmer.io" target="_blank" rel="noopener noreferrer">
3
- <img width="400" src="https://raw.githubusercontent.com/wasmerio/wasmer/master/logo.png" alt="Wasmer logo">
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
4
  </a>
5
5
  </p>
6
6
 
@@ -76,7 +76,8 @@ $ ruby simple.rb
76
76
 
77
77
  ## The `Instance` class
78
78
 
79
- Instantiates a WebAssembly module represented by bytes, and calls exported functions on it:
79
+ Instantiates a WebAssembly module represented by bytes, and calls
80
+ exported functions on it:
80
81
 
81
82
  ```ruby
82
83
  require "wasmer"
@@ -93,10 +94,14 @@ result = instance.exports.sum 1, 2
93
94
  puts result # 3
94
95
  ```
95
96
 
97
+ ### Exported functions
98
+
96
99
  All exported functions are accessible on the `exports`
97
100
  getter. Arguments of these functions are automatically casted to
98
101
  WebAssembly values.
99
102
 
103
+ ### Exported memory
104
+
100
105
  The `memory` getter exposes the `Memory` class representing the memory
101
106
  of that particular instance, e.g.:
102
107
 
@@ -104,8 +109,30 @@ of that particular instance, e.g.:
104
109
  view = instance.memory.uint8_view
105
110
  ```
106
111
 
112
+ `Instance.memory` throws an exception if no memory is exported.
113
+
107
114
  See below for more information.
108
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
+
109
136
  ## The `Memory` class
110
137
 
111
138
  A WebAssembly instance has its own memory, represented by the `Memory`
data/bors.toml CHANGED
@@ -1,4 +1,6 @@
1
- status = [ "ci/circleci: build-and-test" ]
1
+ status = [
2
+ "Test (ubuntu-latest)",
3
+ ]
2
4
  required_approvals = 0
3
5
  timeout_sec = 900
4
6
  delete_merged_branches = true
@@ -1,3 +1,3 @@
1
1
  module Wasmer
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,21 +1,17 @@
1
- //! The `Instance` WebAssembly class.
1
+ //! The `ExportedFunctions` WebAssembly class.
2
2
 
3
- use crate::{
4
- error::unwrap_or_raise,
5
- memory::{Memory, RubyMemory, MEMORY_WRAPPER},
6
- };
3
+ use crate::error::unwrap_or_raise;
7
4
  use lazy_static::lazy_static;
8
5
  use rutie::{
9
6
  class, methods,
10
7
  rubysys::{class, value::ValueType},
11
8
  types::{Argc, Value},
12
9
  util::str_to_cstring,
13
- wrappable_struct, AnyException, AnyObject, Array, Boolean, Exception, Fixnum, Float, Module,
14
- NilClass, Object, RString, Symbol,
10
+ wrappable_struct, AnyException, AnyObject, Array, Boolean, Exception, Fixnum, Float, NilClass,
11
+ Object, Symbol,
15
12
  };
16
- use std::{mem, rc::Rc};
17
- use wasmer_runtime::{self as runtime, imports, Export};
18
- use wasmer_runtime_core::types::Type;
13
+ use std::rc::Rc;
14
+ use wasmer_runtime::{self as runtime, types::Type};
19
15
 
20
16
  /// The `ExportedFunctions` Ruby class.
21
17
  pub struct ExportedFunctions {
@@ -150,12 +146,18 @@ impl ExportedFunctions {
150
146
  .call(function_arguments.as_slice())
151
147
  .map_err(|e| AnyException::new("RuntimeError", Some(&format!("{}", e))))?;
152
148
 
153
- if results.len() > 0 {
149
+ if !results.is_empty() {
154
150
  Ok(match results[0] {
155
151
  runtime::Value::I32(result) => Fixnum::new(result as i64).into(),
156
152
  runtime::Value::I64(result) => Fixnum::new(result).into(),
157
153
  runtime::Value::F32(result) => Float::new(result as f64).into(),
158
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
+ }
159
161
  })
160
162
  } else {
161
163
  Ok(NilClass::new().into())
@@ -180,9 +182,9 @@ methods!(
180
182
  fn ruby_exported_functions_method_exists(symbol: Symbol, _include_private: Boolean) -> Boolean {
181
183
  unwrap_or_raise(|| {
182
184
  let symbol = symbol?;
183
- let instance = itself.get_data(&*EXPORTED_FUNCTIONS_WRAPPER);
185
+ let exported_functions = itself.get_data(&*EXPORTED_FUNCTIONS_WRAPPER);
184
186
 
185
- Ok(Boolean::new(instance.respond_to_missing(symbol.to_str())))
187
+ Ok(Boolean::new(exported_functions.respond_to_missing(symbol.to_str())))
186
188
  })
187
189
  }
188
190
  );
@@ -197,7 +199,7 @@ pub extern "C" fn ruby_exported_functions_method_missing(
197
199
  let arguments = Value::from(0);
198
200
 
199
201
  unsafe {
200
- let argv_pointer: *const Value = mem::transmute(argv);
202
+ let argv_pointer = argv as *const Value;
201
203
 
202
204
  class::rb_scan_args(argc, argv_pointer, str_to_cstring("*").as_ptr(), &arguments)
203
205
  };
@@ -211,101 +213,3 @@ pub extern "C" fn ruby_exported_functions_method_missing(
211
213
  .method_missing(method_name, arguments)
212
214
  })
213
215
  }
214
-
215
- /// The `Instance` Ruby class.
216
- pub struct Instance {
217
- /// The WebAssembly instance.
218
- instance: Rc<runtime::Instance>,
219
- }
220
-
221
- impl Instance {
222
- /// Create a new instance of the `Instance` Ruby class.
223
- /// The constructor receives bytes from a string.
224
- pub fn new(bytes: &[u8]) -> Result<Self, AnyException> {
225
- let import_object = imports! {};
226
-
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
- })
235
- }
236
- }
237
-
238
- wrappable_struct!(Instance, InstanceWrapper, INSTANCE_WRAPPER);
239
-
240
- class!(RubyInstance);
241
-
242
- #[rustfmt::skip]
243
- methods!(
244
- RubyInstance,
245
- _itself,
246
-
247
- // Glue code to call the `Instance.new` method.
248
- fn ruby_instance_new(bytes: RString) -> AnyObject {
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,
268
- })
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");
277
-
278
- let mut ruby_instance: AnyObject = wasmer_module
279
- .get_nested_class("Instance")
280
- .wrap_data(instance, &*INSTANCE_WRAPPER);
281
-
282
- let ruby_exported_functions: RubyExportedFunctions = wasmer_module
283
- .get_nested_class("ExportedFunctions")
284
- .wrap_data(exported_functions, &*EXPORTED_FUNCTIONS_WRAPPER);
285
-
286
- ruby_instance.instance_variable_set("@exports", ruby_exported_functions);
287
-
288
- let ruby_memory: RubyMemory = wasmer_module
289
- .get_nested_class("Memory")
290
- .wrap_data(memory, &*MEMORY_WRAPPER);
291
-
292
- ruby_instance.instance_variable_set("@memory", ruby_memory);
293
-
294
- Ok(ruby_instance)
295
- })
296
- }
297
-
298
- // Glue code to call the `Instance.exports` getter method.
299
- fn ruby_instance_exported_functions() -> RubyExportedFunctions {
300
- unsafe {
301
- _itself
302
- .instance_variable_get("@exports")
303
- .to::<RubyExportedFunctions>()
304
- }
305
- }
306
-
307
- // Glue code to call the `Instance.memory` getter method.
308
- fn ruby_instance_memory() -> RubyMemory {
309
- unsafe { _itself.instance_variable_get("@memory").to::<RubyMemory>() }
310
- }
311
- );
@@ -0,0 +1,234 @@
1
+ //! The `ExportedGlobals` 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,
8
+ types::{Argc, Value},
9
+ util::str_to_cstring,
10
+ wrappable_struct, AnyException, AnyObject, Array, Boolean, Exception, Fixnum, Float, Module,
11
+ NilClass, Object, Symbol,
12
+ };
13
+ use std::rc::Rc;
14
+ use wasmer_runtime::{
15
+ self as runtime, types::Type as WasmType, Export, Global, Value as WasmValue,
16
+ };
17
+
18
+ /// The `ExportedGlobals` Ruby class.
19
+ pub struct ExportedGlobals {
20
+ /// The WebAssembly runtime.
21
+ instance: Rc<runtime::Instance>,
22
+ }
23
+
24
+ impl ExportedGlobals {
25
+ /// Create a new instance of the `ExportedGlobals` Ruby class.
26
+ pub fn new(instance: Rc<runtime::Instance>) -> Self {
27
+ Self { instance }
28
+ }
29
+
30
+ /// Check that an exported function exists.
31
+ pub fn respond_to_missing(&self, method_name: &str) -> bool {
32
+ self.instance
33
+ .exports()
34
+ .any(|(export_name, export)| match export {
35
+ Export::Global(_) if export_name == method_name => true,
36
+ _ => false,
37
+ })
38
+ }
39
+
40
+ /// Call an exported function on the given WebAssembly instance.
41
+ pub fn method_missing(
42
+ &self,
43
+ method_name: &str,
44
+ _arguments: Array,
45
+ ) -> Result<ExportedGlobal, AnyException> {
46
+ let global = self
47
+ .instance
48
+ .exports()
49
+ .find_map(|(export_name, export)| match export {
50
+ Export::Global(global) if export_name == method_name => Some(global),
51
+ _ => None,
52
+ })
53
+ .ok_or_else(|| {
54
+ AnyException::new(
55
+ "RuntimeError",
56
+ Some(&format!("Global `{}` does not exist.", method_name)),
57
+ )
58
+ })?;
59
+
60
+ Ok(ExportedGlobal {
61
+ global_name: method_name.to_string(),
62
+ global: Rc::new(global),
63
+ })
64
+ }
65
+ }
66
+
67
+ wrappable_struct!(
68
+ ExportedGlobals,
69
+ ExportedGlobalsWrapper,
70
+ EXPORTED_GLOBALS_WRAPPER
71
+ );
72
+
73
+ class!(RubyExportedGlobals);
74
+
75
+ #[rustfmt::skip]
76
+ methods!(
77
+ RubyExportedGlobals,
78
+ itself,
79
+
80
+ // Glue code to call the `Exportedglobals.respond_to` method.
81
+ fn ruby_exported_globals_method_exists(symbol: Symbol, _include_private: Boolean) -> Boolean {
82
+ unwrap_or_raise(|| {
83
+ let symbol = symbol?;
84
+ let exported_globals = itself.get_data(&*EXPORTED_GLOBALS_WRAPPER);
85
+
86
+ Ok(Boolean::new(exported_globals.respond_to_missing(symbol.to_str())))
87
+ })
88
+ }
89
+ );
90
+
91
+ /// Glue code to call the `ExportedGlobals.method_missing` method.
92
+ pub extern "C" fn ruby_exported_globals_method_missing(
93
+ argc: Argc,
94
+ argv: *const AnyObject,
95
+ itself: RubyExportedGlobals,
96
+ ) -> RubyExportedGlobal {
97
+ unwrap_or_raise(|| {
98
+ let arguments = Value::from(0);
99
+
100
+ unsafe {
101
+ let argv_pointer = argv as *const Value;
102
+
103
+ class::rb_scan_args(argc, argv_pointer, str_to_cstring("*").as_ptr(), &arguments)
104
+ };
105
+
106
+ let mut arguments = Array::from(arguments);
107
+ let method_name = unsafe { arguments.shift().to::<Symbol>() };
108
+ let method_name = method_name.to_str();
109
+
110
+ Ok(Module::from_existing("Wasmer")
111
+ .get_nested_class("ExportedGlobal")
112
+ .wrap_data(
113
+ itself
114
+ .get_data(&*EXPORTED_GLOBALS_WRAPPER)
115
+ .method_missing(method_name, arguments)?,
116
+ &*EXPORTED_GLOBAL_WRAPPER,
117
+ ))
118
+ })
119
+ }
120
+
121
+ #[allow(unused)]
122
+ /// The `ExportedGlobal` Ruby class.
123
+ pub struct ExportedGlobal {
124
+ /// The exported global name from the WebAssembly instance.
125
+ global_name: String,
126
+
127
+ /// The exported global from the WebAssembly instance.
128
+ global: Rc<Global>,
129
+ }
130
+
131
+ impl ExportedGlobal {
132
+ pub fn get_value(&self) -> WasmValue {
133
+ self.global.get()
134
+ }
135
+
136
+ pub fn set_value(&self, value: WasmValue) {
137
+ self.global.set(value);
138
+ }
139
+
140
+ pub fn mutable(&self) -> bool {
141
+ self.global.descriptor().mutable
142
+ }
143
+
144
+ fn ty(&self) -> WasmType {
145
+ self.global.descriptor().ty
146
+ }
147
+ }
148
+
149
+ wrappable_struct!(
150
+ ExportedGlobal,
151
+ ExportedGlobalWrapper,
152
+ EXPORTED_GLOBAL_WRAPPER
153
+ );
154
+
155
+ class!(RubyExportedGlobal);
156
+
157
+ #[rustfmt::skip]
158
+ methods!(
159
+ RubyExportedGlobal,
160
+ itself,
161
+
162
+ // Glue code to call the `ExportedGlobal.get_value` method.
163
+ fn ruby_exported_global_get_value() -> AnyObject {
164
+ unwrap_or_raise(|| {
165
+ let exported_global = itself.get_data(&*EXPORTED_GLOBAL_WRAPPER);
166
+
167
+ Ok(match exported_global.get_value() {
168
+ WasmValue::I32(result) => Fixnum::new(result as i64).to_any_object(),
169
+ WasmValue::I64(result) => Fixnum::new(result).to_any_object(),
170
+ WasmValue::F32(result) => Float::new(result as f64).to_any_object(),
171
+ WasmValue::F64(result) => Float::new(result).to_any_object(),
172
+ WasmValue::V128(_result) => {
173
+ return Err(AnyException::new(
174
+ "RuntimeError",
175
+ Some("Type `V128` isn't supported yet."),
176
+ ))
177
+ }
178
+ })
179
+ })
180
+ }
181
+
182
+ // Glue code to call the `ExportedGlobal.set_value` method.
183
+ fn ruby_exported_global_set_value(value: AnyObject) -> NilClass {
184
+ unwrap_or_raise(|| {
185
+ let exported_global = itself.get_data(&*EXPORTED_GLOBAL_WRAPPER);
186
+
187
+ if !exported_global.mutable() {
188
+ return Err(AnyException::new(
189
+ "RuntimeError",
190
+ Some(&format!(
191
+ "The global variable `{}` is not mutable, cannot set a new value.",
192
+ exported_global.global_name
193
+ )),
194
+ ));
195
+ }
196
+
197
+ let value = value?;
198
+
199
+ let wasm_type = exported_global.ty();
200
+ let wasm_value = match wasm_type {
201
+ WasmType::I32 if value.is_fixnum() => {
202
+ WasmValue::I32(unsafe { value.to::<Fixnum>().to_i32() })
203
+ }
204
+ WasmType::I64 if value.is_fixnum() => {
205
+ WasmValue::I64(unsafe { value.to::<Fixnum>().to_i64() })
206
+ }
207
+ WasmType::F32 if value.is_flonum() => {
208
+ WasmValue::F32(unsafe { value.to::<Float>().to_f64() as f32 })
209
+ }
210
+ WasmType::F64 if value.is_flonum() => {
211
+ WasmValue::F64(unsafe { value.to::<Float>().to_f64() })
212
+ }
213
+ _ => {
214
+ return Err(AnyException::new(
215
+ "TypeError",
216
+ Some(&format!(
217
+ "Failed to set `{:?}` to the global `{}` (with type `{}`).",
218
+ value, exported_global.global_name, wasm_type
219
+ )),
220
+ ))
221
+ }
222
+ };
223
+
224
+ exported_global.set_value(wasm_value);
225
+
226
+ Ok(NilClass::new())
227
+ })
228
+ }
229
+
230
+ // Glue code to call the `ExportedGlobal.mutable` getter.
231
+ fn ruby_exported_global_mutable() -> Boolean {
232
+ Boolean::new(itself.get_data(&*EXPORTED_GLOBAL_WRAPPER).mutable())
233
+ }
234
+ );