wasmtime 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 278e090fef9ae4f35850cc892273ddbe94b2b838b0fc619ca835a17505129957
4
- data.tar.gz: fbafd3a60ace1ec5f4806ac94d86e73264feb16e5b4f781c09ad9927c070758e
3
+ metadata.gz: 51b9d23eb473612cd8d79f307021bdcf6ccbce2c1e522996af1bf3c1ebb85c1a
4
+ data.tar.gz: cde5dc2a81f5cc73a4e8abd70268d1e9322bad3093cf7ce8f1d3d13fa44aafda
5
5
  SHA512:
6
- metadata.gz: '016450946bf0d7ee92ffbc44bdc1be90a83602a14a209e3c24e7cc438883845b0b19aa173288169beb5248a080205c8b1d9abe46b373868b72e5caed3e5831bb'
7
- data.tar.gz: 8dda113d2501e84a1222dd5ccbc1683cc84490d30628f98f489c681db4e2712d0aa24d65261ae0f223377602d74293472e56166d6aae55d9253bebc78ce1d17d
6
+ metadata.gz: e5a78a30dd600e7c9618c2c0612b5d657d3002e027e826375522455762756b56f4606b433b94070a0227ceb2319321752744d6c86ce613bda672bc3e067c5423
7
+ data.tar.gz: 301fbc2e124b4653b957cee6e260408a5cefed5743da6bc7327f7a7d153f29f2ba63ddf331cb93da0fbb16f593cdd7f4b555dfae6b242e7c97bc2067b50ffa88
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] - 2020-05-19
11
+
12
+ - Exported functions now live on `Wasmtime::Instance#exports` instead of `#funcs`.
13
+ - Exports hash is now keyed by strings instead of symbols.
14
+ - Added support for `Wasmtime::Memory` exports with `#data_size`, `#size` and `#grow` methods.
15
+
10
16
  ## [0.1.0] - 2020-05-07
11
17
 
12
18
  ### Added
@@ -16,5 +22,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16
22
  - Support for 32 and 64-bit integers and floats in arguments and as return values.
17
23
  - Require patch for defining a Ruby module with functions for each export.
18
24
 
19
- [unreleased]: https://github.com/dtcristo/wasmtime-ruby/compare/v0.1.0...HEAD
25
+ [unreleased]: https://github.com/dtcristo/wasmtime-ruby/compare/v0.2.0...HEAD
26
+ [0.2.0]: https://github.com/dtcristo/wasmtime-ruby/releases/tag/v0.2.0
20
27
  [0.1.0]: https://github.com/dtcristo/wasmtime-ruby/releases/tag/v0.1.0
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <h1>wasmtime-ruby</h1>
3
3
  <p>
4
4
  <strong>
5
- <a href="https://github.com/bytecodealliance/wasmtime">Wasmtime</a> WebAssembly runtime in Ruby
5
+ Ruby bindings for <a href="https://github.com/bytecodealliance/wasmtime">Wasmtime</a>, a WebAssembly runtime
6
6
  </strong>
7
7
  </p>
8
8
  <p>
@@ -17,13 +17,10 @@
17
17
 
18
18
  ## Introduction
19
19
 
20
- This gem allows you to use the [Wasmtime](https://wasmtime.dev/) WebAssembly
21
- runtime from within your Ruby project.
22
-
23
- Why would you want that? [WebAssembly](https://webassembly.org/) (or WASM) is a
24
- technology that allows you to write programs that run at near-native speeds in a
25
- safe sandboxed environment. Wasmtime is a runtime for WASM programs. This gem
26
- embedds Wasmtime in a native extension so you can now run WASM from Ruby.
20
+ This project provides Ruby bindings for [Wasmtime](https://github.com/bytecodealliance/wasmtime),
21
+ a [WebAssembly](https://webassembly.org/) runtime. This allows you to execute
22
+ WASM modules from within Ruby executing at near-native speeds in a safe
23
+ sandboxed environment.
27
24
 
28
25
  This is pretty experimental and not production ready right now. There are quite
29
26
  a few things that aren't built yet, see [TODO](#todo) section below.
@@ -42,7 +39,7 @@ Install the `wasmtime` gem. Pre-compiled binaries are available for
42
39
  gem install wasmtime
43
40
  ```
44
41
 
45
- WASM has two formats `*.wasm` (binary) and `*.wat` (human-readable text). Both
42
+ WASM has two formats `.wasm` (binary) and `.wat` (human-readable text). Both
46
43
  formats are supported. With the following `fibonacci.wat` file in your current
47
44
  directory.
48
45
 
@@ -65,7 +62,7 @@ directory.
65
62
  ```
66
63
 
67
64
  In a ruby file, `require 'wasmtime/require'` to activate the Wasmtime require
68
- patch, allowing you to require any `*.wasm` or `*.wat` module as if it were a
65
+ patch, allowing you to require any `.wasm` or `.wat` module as if it were a
69
66
  Ruby file. Doing so will internally create a `Wasmtime::Instance` and define a
70
67
  Ruby module with functions for each export.
71
68
 
@@ -86,7 +83,7 @@ use this approach too.
86
83
  require 'wasmtime'
87
84
 
88
85
  instance = Wasmtime::Instance.new('fibonacci.wat')
89
- puts instance.funcs[:fib].call(11) #=> 89
86
+ puts instance.exports['fib'].call(11) #=> 89
90
87
  ```
91
88
 
92
89
  ## Benchmarks
@@ -60,8 +60,11 @@ module Wasmtime
60
60
  end
61
61
  mod = Object.const_set(module_name.camelize, Module.new)
62
62
  instance = Wasmtime::Instance.new(absolute_path)
63
- instance.funcs.each do |name, func|
64
- mod.define_singleton_method(name) { |*args| func.call(*args) }
63
+ instance.exports.each do |name, export|
64
+ case export
65
+ when Wasmtime::Func
66
+ mod.define_singleton_method(name) { |*args| export.call(*args) }
67
+ end
65
68
  end
66
69
  $LOADED_FEATURES << absolute_path
67
70
  true
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wasmtime
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -0,0 +1,19 @@
1
+ use rutie::{AnyObject, Object};
2
+ use wasmtime as w;
3
+
4
+ use crate::func::{Func, RubyFunc};
5
+ use crate::memory::RubyMemory;
6
+
7
+ pub enum Export {
8
+ Func(Func),
9
+ Memory(w::Memory),
10
+ }
11
+
12
+ impl From<Export> for AnyObject {
13
+ fn from(export: Export) -> Self {
14
+ match export {
15
+ Export::Func(func) => RubyFunc::from(func).value().into(),
16
+ Export::Memory(memory) => RubyMemory::from(memory).value().into(),
17
+ }
18
+ }
19
+ }
@@ -8,7 +8,8 @@ use rutie::{
8
8
  use std::mem;
9
9
  use wasmtime as w;
10
10
 
11
- use crate::vm::*;
11
+ use crate::ruby_type::RubyType;
12
+ use crate::vm::raise;
12
13
 
13
14
  pub struct Func {
14
15
  func: w::Func,
@@ -23,58 +24,24 @@ impl Func {
23
24
  self.func.call(args).expect("failed to call func").to_vec()
24
25
  }
25
26
 
26
- pub fn into_ruby(self) -> RubyFunc {
27
- Module::from_existing("Wasmtime")
28
- .get_nested_class("Func")
29
- .wrap_data(self, &*FUNC_WRAPPER)
30
- }
31
-
32
27
  fn parse_param_types(&self) -> Vec<RubyType> {
33
28
  self.func
34
29
  .ty()
35
30
  .params()
36
31
  .iter()
37
- .map(|val_type| val_type_to_ruby_type(val_type))
32
+ .map(|val_type| val_type.into())
38
33
  .collect()
39
34
  }
40
35
 
41
36
  fn parse_result_type(&self) -> RubyType {
42
37
  match self.func.ty().results().len() {
43
38
  0 => RubyType::NilClass,
44
- 1 => val_type_to_ruby_type(self.func.ty().results().first().unwrap()),
39
+ 1 => self.func.ty().results().first().unwrap().into(),
45
40
  _ => raise("StandardError", "multiple return values are not supported"),
46
41
  }
47
42
  }
48
43
  }
49
44
 
50
- fn val_type_to_ruby_type(val_type: &w::ValType) -> RubyType {
51
- match val_type {
52
- w::ValType::I32 => RubyType::Integer32,
53
- w::ValType::I64 => RubyType::Integer64,
54
- w::ValType::F32 => RubyType::Float32,
55
- w::ValType::F64 => RubyType::Float64,
56
- _ => RubyType::Unsupported,
57
- }
58
- }
59
-
60
- #[derive(Debug, Copy, Clone)]
61
- enum RubyType {
62
- Integer32,
63
- Integer64,
64
- Float32,
65
- Float64,
66
- // String,
67
- // Boolean,
68
- NilClass,
69
- Unsupported,
70
- }
71
-
72
- impl Into<AnyObject> for RubyType {
73
- fn into(self) -> AnyObject {
74
- RString::new_utf8(&format!("{:?}", self)).into()
75
- }
76
- }
77
-
78
45
  fn translate_incoming(args: Array, param_types: &[RubyType]) -> Vec<w::Val> {
79
46
  if args.length() != param_types.len() {
80
47
  raise(
@@ -141,6 +108,14 @@ fn translate_outgoing(native_results: Vec<w::Val>) -> AnyObject {
141
108
  wrappable_struct!(Func, FuncWrapper, FUNC_WRAPPER);
142
109
  class!(RubyFunc);
143
110
 
111
+ impl From<Func> for RubyFunc {
112
+ fn from(func: Func) -> Self {
113
+ Module::from_existing("Wasmtime")
114
+ .get_nested_class("Func")
115
+ .wrap_data(func, &*FUNC_WRAPPER)
116
+ }
117
+ }
118
+
144
119
  #[rustfmt::skip]
145
120
  methods!(
146
121
  RubyFunc,
@@ -1,11 +1,11 @@
1
1
  use lazy_static::lazy_static;
2
- use rutie::{class, methods, wrappable_struct, Hash, Module, Object, RString, Symbol};
2
+ use rutie::{class, methods, wrappable_struct, AnyObject, Hash, Module, Object, RString};
3
3
  use std::collections::HashMap;
4
4
  use std::fs;
5
5
  use wasmtime as w;
6
6
 
7
+ use crate::export::Export;
7
8
  use crate::func::Func;
8
- use crate::memory::Memory;
9
9
 
10
10
  pub struct Instance {
11
11
  instance: w::Instance,
@@ -27,58 +27,57 @@ impl Instance {
27
27
  Instance { instance }
28
28
  }
29
29
 
30
- fn exports(&self) -> (HashMap<String, Func>, HashMap<String, Memory>) {
31
- let mut funcs = HashMap::new();
32
- let mut memories = HashMap::new();
30
+ fn exports(&self) -> HashMap<String, Export> {
31
+ let mut exports = HashMap::new();
33
32
 
34
33
  for export in self.instance.exports() {
35
34
  match export.ty() {
36
35
  w::ExternType::Func(_) => {
37
36
  let name = export.name().to_string();
38
- let func = Func::new(export.into_func().expect("failed to create func"));
39
- funcs.insert(name, func);
37
+ let func = Func::new(export.into_func().unwrap());
38
+ exports.insert(name, Export::Func(func));
40
39
  }
41
40
  w::ExternType::Memory(_) => {
42
- let memory = Memory::new();
43
- memories.insert(export.name().to_string(), memory);
41
+ let name = export.name().to_string();
42
+ let memory = export.into_memory().unwrap();
43
+ exports.insert(name, Export::Memory(memory));
44
44
  }
45
45
  _ => {}
46
46
  }
47
47
  }
48
48
 
49
- (funcs, memories)
49
+ exports
50
50
  }
51
+ }
51
52
 
52
- pub fn funcs(&self) -> HashMap<String, Func> {
53
- let (functions, _) = self.exports();
54
- functions
55
- }
53
+ wrappable_struct!(Instance, InstanceWrapper, INSTANCE_WRAPPER);
54
+ class!(RubyInstance);
56
55
 
57
- pub fn into_ruby(self) -> RubyInstance {
56
+ impl From<Instance> for RubyInstance {
57
+ fn from(instance: Instance) -> Self {
58
58
  Module::from_existing("Wasmtime")
59
59
  .get_nested_class("Instance")
60
- .wrap_data(self, &*INSTANCE_WRAPPER)
60
+ .wrap_data(instance, &*INSTANCE_WRAPPER)
61
61
  }
62
62
  }
63
63
 
64
- wrappable_struct!(Instance, InstanceWrapper, INSTANCE_WRAPPER);
65
- class!(RubyInstance);
66
-
67
64
  #[rustfmt::skip]
68
65
  methods!(
69
66
  RubyInstance,
70
67
  itself,
71
68
 
72
69
  fn ruby_instance_new(path: RString) -> RubyInstance {
73
- Instance::new(path.expect("failed read path").to_string()).into_ruby()
70
+ Instance::new(path.expect("failed read path").to_string()).into()
74
71
  }
75
72
 
76
- fn ruby_instance_funcs() -> Hash {
77
- let mut funcs = Hash::new();
78
- for (export_name, func) in itself.get_data(&*INSTANCE_WRAPPER).funcs().into_iter() {
79
- funcs.store(Symbol::new(&export_name), func.into_ruby());
73
+ fn ruby_instance_exports() -> Hash {
74
+ let mut exports = Hash::new();
75
+
76
+ for (export_name, export) in itself.get_data(&*INSTANCE_WRAPPER).exports().into_iter() {
77
+ exports.store::<RString, AnyObject>(RString::new_utf8(&export_name), export.into());
80
78
  }
81
- funcs
79
+
80
+ exports
82
81
  }
83
82
  );
84
83
 
@@ -88,7 +87,7 @@ pub fn ruby_init() {
88
87
  .define_nested_class("Instance", None)
89
88
  .define(|class| {
90
89
  class.def_self("new", ruby_instance_new);
91
- class.def("funcs", ruby_instance_funcs);
90
+ class.def("exports", ruby_instance_exports);
92
91
  });
93
92
  });
94
93
  }
data/src/lib.rs CHANGED
@@ -1,6 +1,8 @@
1
+ mod export;
1
2
  mod func;
2
3
  mod instance;
3
4
  mod memory;
5
+ mod ruby_type;
4
6
  mod vm;
5
7
 
6
8
  #[allow(non_snake_case)]
@@ -16,4 +18,5 @@ pub extern "C" fn Init_native() {
16
18
 
17
19
  instance::ruby_init();
18
20
  func::ruby_init();
21
+ memory::ruby_init();
19
22
  }
@@ -1,7 +1,48 @@
1
- pub struct Memory {}
1
+ use lazy_static::lazy_static;
2
+ use rutie::{class, methods, wrappable_struct, AnyObject, Integer, Module, NilClass, Object};
3
+ use wasmtime as w;
2
4
 
3
- impl Memory {
4
- pub fn new() -> Self {
5
- Memory {}
5
+ wrappable_struct!(w::Memory, MemoryWrapper, MEMORY_WRAPPER);
6
+ class!(RubyMemory);
7
+
8
+ impl From<w::Memory> for RubyMemory {
9
+ fn from(memory: w::Memory) -> Self {
10
+ Module::from_existing("Wasmtime")
11
+ .get_nested_class("Memory")
12
+ .wrap_data(memory, &*MEMORY_WRAPPER)
13
+ }
14
+ }
15
+
16
+ #[rustfmt::skip]
17
+ methods!(
18
+ RubyMemory,
19
+ itself,
20
+
21
+ fn ruby_instance_data_size() -> Integer {
22
+ Integer::from(itself.get_data(&*MEMORY_WRAPPER).data_size() as u32)
6
23
  }
24
+
25
+ fn ruby_instance_size() -> Integer {
26
+ Integer::from(itself.get_data(&*MEMORY_WRAPPER).size())
27
+ }
28
+
29
+ fn ruby_instance_grow(delta: Integer) -> AnyObject {
30
+ let memory = itself.get_data(&*MEMORY_WRAPPER);
31
+ let delta = delta.unwrap().to_i32();
32
+ if delta < 0 { return NilClass::new().into() }
33
+ match memory.grow(delta as u32) {
34
+ Ok(original) => Integer::from(original).into(),
35
+ Err(_) => NilClass::new().into()
36
+ }
37
+ }
38
+ );
39
+
40
+ pub fn ruby_init() {
41
+ Module::from_existing("Wasmtime").define(|module| {
42
+ module.define_nested_class("Memory", None).define(|class| {
43
+ class.def("data_size", ruby_instance_data_size);
44
+ class.def("size", ruby_instance_size);
45
+ class.def("grow", ruby_instance_grow);
46
+ });
47
+ });
7
48
  }
@@ -0,0 +1,32 @@
1
+ use rutie::{AnyObject, RString};
2
+ use wasmtime as w;
3
+
4
+ #[derive(Debug, Copy, Clone)]
5
+ pub enum RubyType {
6
+ Integer32,
7
+ Integer64,
8
+ Float32,
9
+ Float64,
10
+ // String,
11
+ // Boolean,
12
+ NilClass,
13
+ Unsupported,
14
+ }
15
+
16
+ impl From<RubyType> for AnyObject {
17
+ fn from(ruby_type: RubyType) -> AnyObject {
18
+ RString::new_utf8(&format!("{:?}", ruby_type)).into()
19
+ }
20
+ }
21
+
22
+ impl From<&w::ValType> for RubyType {
23
+ fn from(val_type: &w::ValType) -> Self {
24
+ match val_type {
25
+ w::ValType::I32 => RubyType::Integer32,
26
+ w::ValType::I64 => RubyType::Integer64,
27
+ w::ValType::F32 => RubyType::Float32,
28
+ w::ValType::F64 => RubyType::Float64,
29
+ _ => RubyType::Unsupported,
30
+ }
31
+ }
32
+ }
metadata CHANGED
@@ -1,17 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wasmtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cristofaro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-07 00:00:00.000000000 Z
11
+ date: 2020-05-19 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: This gem allows you to use the Wasmtime WebAssembly runtime from within
14
- your Ruby project.
13
+ description:
15
14
  email:
16
15
  - david@dtcristo.com
17
16
  executables: []
@@ -33,10 +32,12 @@ files:
33
32
  - lib/wasmtime/refinements.rb
34
33
  - lib/wasmtime/require.rb
35
34
  - lib/wasmtime/version.rb
35
+ - src/export.rs
36
36
  - src/func.rs
37
37
  - src/instance.rs
38
38
  - src/lib.rs
39
39
  - src/memory.rs
40
+ - src/ruby_type.rs
40
41
  - src/vm.rs
41
42
  homepage: https://github.com/dtcristo/wasmtime-ruby
42
43
  licenses:
@@ -62,5 +63,5 @@ requirements: []
62
63
  rubygems_version: 3.1.2
63
64
  signing_key:
64
65
  specification_version: 4
65
- summary: Wasmtime WebAssembly runtime
66
+ summary: Ruby bindings for Wasmtime, a WebAssembly runtime
66
67
  test_files: []