wasmtime 0.1.0.alpha.1

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