wasmtime 0.1.0.alpha.1

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.
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Wasmtime
4
+ VERSION = '0.1.0.alpha.1'
5
+ end
@@ -0,0 +1,200 @@
1
+ use lazy_static::lazy_static;
2
+ use rutie as r;
3
+ use rutie::rubysys;
4
+ use rutie::{
5
+ class, methods, wrappable_struct, AnyObject, Array, Float, Hash, Integer, Module, NilClass,
6
+ Object, RString, Symbol,
7
+ };
8
+ use std::mem;
9
+ use wasmtime as w;
10
+
11
+ use crate::vm::*;
12
+
13
+ pub struct Func {
14
+ func: w::Func,
15
+ }
16
+
17
+ impl Func {
18
+ pub fn new(func: w::Func) -> Self {
19
+ Func { func }
20
+ }
21
+
22
+ pub fn call(&mut self, args: &[w::Val]) -> Vec<w::Val> {
23
+ self.func.call(args).expect("failed to call func").to_vec()
24
+ }
25
+
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
+ fn parse_param_types(&self) -> Vec<RubyType> {
33
+ self.func
34
+ .ty()
35
+ .params()
36
+ .iter()
37
+ .map(|val_type| val_type_to_ruby_type(val_type))
38
+ .collect()
39
+ }
40
+
41
+ fn parse_result_type(&self) -> RubyType {
42
+ match self.func.ty().results().len() {
43
+ 0 => RubyType::NilClass,
44
+ 1 => val_type_to_ruby_type(self.func.ty().results().first().unwrap()),
45
+ _ => raise("StandardError", "multiple return values are not supported"),
46
+ }
47
+ }
48
+ }
49
+
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
+ fn translate_incoming(args: Array, param_types: &[RubyType]) -> Vec<w::Val> {
79
+ if args.length() != param_types.len() {
80
+ raise(
81
+ "ArgumentError",
82
+ &format!(
83
+ "wrong number of arguments (given {}, expected {})",
84
+ args.length(),
85
+ param_types.len()
86
+ ),
87
+ )
88
+ }
89
+ args.into_iter()
90
+ .zip(param_types)
91
+ .map(|(arg, param_type)| match param_type {
92
+ RubyType::Integer32 => w::Val::I32(
93
+ arg.try_convert_to::<Integer>()
94
+ .expect("failed to convert integer")
95
+ .to_i32(),
96
+ ),
97
+ RubyType::Integer64 => w::Val::I64(
98
+ arg.try_convert_to::<Integer>()
99
+ .expect("failed to convert integer")
100
+ .to_i64(),
101
+ ),
102
+ RubyType::Float32 => w::Val::F32(
103
+ (arg.try_convert_to::<Float>()
104
+ .expect("failed to convert float")
105
+ .to_f64() as f32)
106
+ .to_bits(),
107
+ ),
108
+ RubyType::Float64 => w::Val::F64(
109
+ arg.try_convert_to::<Float>()
110
+ .expect("failed to convert float")
111
+ .to_f64()
112
+ .to_bits(),
113
+ ),
114
+ RubyType::NilClass | RubyType::Unsupported => raise(
115
+ "StandardError",
116
+ &format!("unsupported arg type: {:?}", param_type),
117
+ ),
118
+ })
119
+ .collect()
120
+ }
121
+
122
+ fn translate_outgoing(native_results: Vec<w::Val>) -> AnyObject {
123
+ let results: Vec<AnyObject> = native_results
124
+ .into_iter()
125
+ .map(|r| match r {
126
+ w::Val::I32(v) => Integer::new(v.into()).into(),
127
+ w::Val::I64(v) => Integer::new(v).into(),
128
+ w::Val::F32(v) => Float::new(f32::from_bits(v).into()).into(),
129
+ w::Val::F64(v) => Float::new(f64::from_bits(v)).into(),
130
+ _ => raise("StandardError", &format!("unsupported value: {:?}", r)),
131
+ })
132
+ .collect();
133
+
134
+ match results.len() {
135
+ 0 => NilClass::new().into(),
136
+ 1 => results.first().unwrap().into(),
137
+ _ => raise("StandardError", "multiple return values are not supported"),
138
+ }
139
+ }
140
+
141
+ wrappable_struct!(Func, FuncWrapper, FUNC_WRAPPER);
142
+ class!(RubyFunc);
143
+
144
+ #[rustfmt::skip]
145
+ methods!(
146
+ RubyFunc,
147
+ itself,
148
+
149
+ fn ruby_func_signature() -> Hash {
150
+ let func = itself.get_data(&*FUNC_WRAPPER);
151
+
152
+ let mut param_types = Array::new();
153
+ for param_type in func.parse_param_types().iter() {
154
+ param_types.push(RString::new_utf8(&format!("{:?}", param_type)));
155
+ }
156
+
157
+ let result_type: AnyObject = func.parse_result_type().into();
158
+
159
+ let mut signature = Hash::new();
160
+ signature.store(Symbol::new("params"), param_types);
161
+ signature.store(Symbol::new("result"), result_type);
162
+
163
+ signature
164
+ }
165
+ );
166
+
167
+ pub extern "C" fn ruby_func_call(
168
+ argc: r::types::Argc,
169
+ argv: *const AnyObject,
170
+ mut itself: AnyObject,
171
+ ) -> AnyObject {
172
+ // TODO: Remove this section when rutie `methods!` macro has support for variadic functions
173
+ // https://github.com/danielpclark/rutie/blob/1c951b59e00944d305ca425267c54115c8c1bb86/README.md#variadic-functions--splat-operator
174
+ let args_raw = r::types::Value::from(0);
175
+ unsafe {
176
+ let p_argv: *const r::types::Value = mem::transmute(argv);
177
+ rubysys::class::rb_scan_args(
178
+ argc,
179
+ p_argv,
180
+ r::util::str_to_cstring("*").as_ptr(),
181
+ &args_raw,
182
+ )
183
+ };
184
+ let args = Array::from(args_raw);
185
+ // ---
186
+ let func = itself.get_data_mut(&*FUNC_WRAPPER);
187
+
188
+ let args_native = translate_incoming(args, &func.parse_param_types());
189
+ let results_native = func.call(&args_native[..]);
190
+ translate_outgoing(results_native)
191
+ }
192
+
193
+ pub fn ruby_init() {
194
+ Module::from_existing("Wasmtime").define(|module| {
195
+ module.define_nested_class("Func", None).define(|class| {
196
+ class.def("signature", ruby_func_signature);
197
+ class.def("call", ruby_func_call);
198
+ });
199
+ });
200
+ }
@@ -0,0 +1,94 @@
1
+ use lazy_static::lazy_static;
2
+ use rutie::{class, methods, wrappable_struct, Hash, Module, Object, RString, Symbol};
3
+ use std::collections::HashMap;
4
+ use std::fs;
5
+ use wasmtime as w;
6
+
7
+ use crate::func::Func;
8
+ use crate::memory::Memory;
9
+
10
+ pub struct Instance {
11
+ instance: w::Instance,
12
+ }
13
+
14
+ impl Instance {
15
+ pub fn new(path: String) -> Self {
16
+ let wasm = fs::read(path).expect("failed to read wasm file");
17
+
18
+ let config = w::Config::new();
19
+ // config.wasm_interface_types(true);
20
+
21
+ let engine = w::Engine::new(&config);
22
+ let store = w::Store::new(&engine);
23
+ let module = w::Module::new(&store, &wasm).expect("failed to create module");
24
+ let imports: Vec<w::Extern> = Vec::new();
25
+ let instance = w::Instance::new(&module, &imports).expect("failed to create instance");
26
+
27
+ Instance { instance }
28
+ }
29
+
30
+ fn exports(&self) -> (HashMap<String, Func>, HashMap<String, Memory>) {
31
+ let mut funcs = HashMap::new();
32
+ let mut memories = HashMap::new();
33
+
34
+ for export in self.instance.exports() {
35
+ match export.ty() {
36
+ w::ExternType::Func(_) => {
37
+ let name = export.name().to_string();
38
+ let func = Func::new(export.into_func().expect("failed to create func"));
39
+ funcs.insert(name, func);
40
+ }
41
+ w::ExternType::Memory(_) => {
42
+ let memory = Memory::new();
43
+ memories.insert(export.name().to_string(), memory);
44
+ }
45
+ _ => {}
46
+ }
47
+ }
48
+
49
+ (funcs, memories)
50
+ }
51
+
52
+ pub fn funcs(&self) -> HashMap<String, Func> {
53
+ let (functions, _) = self.exports();
54
+ functions
55
+ }
56
+
57
+ pub fn into_ruby(self) -> RubyInstance {
58
+ Module::from_existing("Wasmtime")
59
+ .get_nested_class("Instance")
60
+ .wrap_data(self, &*INSTANCE_WRAPPER)
61
+ }
62
+ }
63
+
64
+ wrappable_struct!(Instance, InstanceWrapper, INSTANCE_WRAPPER);
65
+ class!(RubyInstance);
66
+
67
+ #[rustfmt::skip]
68
+ methods!(
69
+ RubyInstance,
70
+ itself,
71
+
72
+ fn ruby_instance_new(path: RString) -> RubyInstance {
73
+ Instance::new(path.expect("failed read path").to_string()).into_ruby()
74
+ }
75
+
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());
80
+ }
81
+ funcs
82
+ }
83
+ );
84
+
85
+ pub fn ruby_init() {
86
+ Module::from_existing("Wasmtime").define(|module| {
87
+ module
88
+ .define_nested_class("Instance", None)
89
+ .define(|class| {
90
+ class.def_self("new", ruby_instance_new);
91
+ class.def("funcs", ruby_instance_funcs);
92
+ });
93
+ });
94
+ }
@@ -0,0 +1,19 @@
1
+ mod func;
2
+ mod instance;
3
+ mod memory;
4
+ mod vm;
5
+
6
+ #[allow(non_snake_case)]
7
+ #[no_mangle]
8
+ pub extern "C" fn Init_native() {
9
+ std::panic::set_hook(Box::new(|panic_info| {
10
+ if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
11
+ vm::raise("StandardError", s)
12
+ } else {
13
+ vm::raise("StandardError", &format!("{:?}", panic_info))
14
+ }
15
+ }));
16
+
17
+ instance::ruby_init();
18
+ func::ruby_init();
19
+ }
@@ -0,0 +1,7 @@
1
+ pub struct Memory {}
2
+
3
+ impl Memory {
4
+ pub fn new() -> Self {
5
+ Memory {}
6
+ }
7
+ }
@@ -0,0 +1,6 @@
1
+ use rutie::{Class, VM};
2
+
3
+ pub fn raise(exception: &str, message: &str) -> ! {
4
+ VM::raise(Class::from_existing(exception), message);
5
+ loop {}
6
+ }
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: wasmtime
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0.alpha.1
5
+ platform: ruby
6
+ authors:
7
+ - David Cristofaro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-07 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ - david@dtcristo.com
16
+ executables: []
17
+ extensions:
18
+ - ext/wasmtime/extconf.rb
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".cargo/config"
22
+ - Cargo.lock
23
+ - Cargo.toml
24
+ - LICENSE
25
+ - README.md
26
+ - ext/wasmtime/Makefile
27
+ - ext/wasmtime/Rakefile
28
+ - ext/wasmtime/extconf.rb
29
+ - lib/tasks/compile.rake
30
+ - lib/wasmtime.rb
31
+ - lib/wasmtime/refinements.rb
32
+ - lib/wasmtime/require.rb
33
+ - lib/wasmtime/version.rb
34
+ - src/func.rs
35
+ - src/instance.rs
36
+ - src/lib.rs
37
+ - src/memory.rs
38
+ - src/vm.rs
39
+ homepage: https://github.com/dtcristo/wasmtime-ruby
40
+ licenses:
41
+ - Apache-2.0 WITH LLVM-exception
42
+ metadata:
43
+ homepage_uri: https://github.com/dtcristo/wasmtime-ruby
44
+ source_code_uri: https://github.com/dtcristo/wasmtime-ruby
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">"
57
+ - !ruby/object:Gem::Version
58
+ version: 1.3.1
59
+ requirements: []
60
+ rubygems_version: 3.1.2
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Wasmtime WebAssembly runtime
64
+ test_files: []