wasmer 0.3.0 → 0.4.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.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
+ );