Skip to content

Commit

Permalink
Define Instance, Module, etc. inside Wasmer module
Browse files Browse the repository at this point in the history
  • Loading branch information
irxground committed May 7, 2019
1 parent 944058b commit 4766efb
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 71 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Then, we can execute it in Ruby (!) with the `examples/simple.rb` file:
require "wasmer"

bytes = IO.read "simple.wasm", mode: "rb"
instance = Instance.new bytes
instance = Wasmer::Instance.new bytes
puts instance.exports.sum 1, 2
```

Expand All @@ -85,7 +85,7 @@ require "wasmer"
wasm_bytes = IO.read "my_program.wasm", mode: "rb"

# Instantiates the Wasm module.
instance = Instance.new wasm_bytes
instance = Wasmer::Instance.new wasm_bytes

# Call a function on it.
result = instance.exports.sum 1, 2
Expand All @@ -109,7 +109,7 @@ See below for more information.
## The `Memory` class

A WebAssembly instance has its own memory, represented by the `Memory`
class. It is accessible by the `Instance.memory` getter.
class. It is accessible by the `Wasmer::Instance.memory` getter.

The `Memory` class offers methods to create views of the memory
internal buffer, e.g. `uint8_view`, `int8_view`, `uint16_view`
Expand Down Expand Up @@ -159,7 +159,7 @@ require "wasmer"
wasm_bytes = IO.read "my_program.wasm", mode: "rb"

# Instantiates the Wasm module.
instance = Instance.new wasm_bytes
instance = Wasmer::Instance.new wasm_bytes

# Call a function that returns a pointer to a string for instance.
pointer = instance.exports.return_string
Expand Down
2 changes: 1 addition & 1 deletion examples/memory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

file = File.expand_path "memory.wasm", File.dirname(__FILE__)
bytes = IO.read file, mode: "rb"
instance = Instance.new bytes
instance = Wasmer::Instance.new bytes
pointer = instance.exports.return_hello

memory = instance.memory.uint8_view pointer
Expand Down
2 changes: 1 addition & 1 deletion examples/simple.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

file = File.expand_path "simple.wasm", File.dirname(__FILE__)
bytes = IO.read file, mode: "rb"
instance = Instance.new bytes
instance = Wasmer::Instance.new bytes
puts instance.exports.sum 1, 2
10 changes: 6 additions & 4 deletions src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rutie::{
rubysys::{class, value::ValueType},
types::{Argc, Value},
util::str_to_cstring,
wrappable_struct, AnyException, AnyObject, Array, Class, Exception, Fixnum, Float, Object,
wrappable_struct, AnyException, AnyObject, Array, Module, Exception, Fixnum, Float, Object,
RString, Symbol, VM,
};
use std::{mem, rc::Rc};
Expand Down Expand Up @@ -249,17 +249,19 @@ methods!(
.ok_or_else(|| VM::raise_ex(AnyException::new("RuntimeError", Some("The WebAssembly module has no exported memory."))))
.unwrap();

let wasmer_module = Module::from_existing("Wasmer");

let mut ruby_instance: AnyObject =
Class::from_existing("Instance").wrap_data(instance, &*INSTANCE_WRAPPER);
wasmer_module.get_nested_class("Instance").wrap_data(instance, &*INSTANCE_WRAPPER);

let ruby_exported_functions: RubyExportedFunctions =
Class::from_existing("ExportedFunctions")
wasmer_module.get_nested_class("ExportedFunctions")
.wrap_data(exported_functions, &*EXPORTED_FUNCTIONS_WRAPPER);

ruby_instance.instance_variable_set("@exports", ruby_exported_functions);

let ruby_memory: RubyMemory =
Class::from_existing("Memory").wrap_data(memory, &*MEMORY_WRAPPER);
wasmer_module.get_nested_class("Memory").wrap_data(memory, &*MEMORY_WRAPPER);

ruby_instance.instance_variable_set("@memory", ruby_memory);

Expand Down
14 changes: 8 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![deny(warnings)]

use rutie::{Class, Object};
use rutie::{Class, Module, Object};

pub mod instance;
pub mod memory;
Expand All @@ -9,10 +9,12 @@ pub mod module;
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn Init_wasmer() {
let mut wasmer_module = Module::from_existing("Wasmer");

let instance_data_class = Class::from_existing("Object");

// Declare the `Instance` Ruby class.
Class::new("Instance", Some(&instance_data_class)).define(|itself| {
wasmer_module.define_nested_class("Instance", Some(&instance_data_class)).define(|itself| {
// Declare the `self.new` method.
itself.def_self("new", instance::ruby_instance_new);

Expand All @@ -26,7 +28,7 @@ pub extern "C" fn Init_wasmer() {
let exported_functions_data_class = Class::from_existing("Object");

// Declare the `ExportedFunctions` Ruby class.
Class::new("ExportedFunctions", Some(&exported_functions_data_class)).define(|itself| {
wasmer_module.define_nested_class("ExportedFunctions", Some(&exported_functions_data_class)).define(|itself| {
// Declare the `method_missing` method.
itself.def(
"method_missing",
Expand All @@ -37,15 +39,15 @@ pub extern "C" fn Init_wasmer() {
let module_data_class = Class::from_existing("Object");

// Declare the `Module` Ruby class.
Class::new("Module", Some(&module_data_class)).define(|itself| {
wasmer_module.define_nested_class("Module", Some(&module_data_class)).define(|itself| {
// Declare the `self.validate` method.
itself.def_self("validate", module::ruby_module_validate);
});

let memory_data_class = Class::from_existing("Object");

// Declare the `Memory` Ruby class.
Class::new("Memory", Some(&memory_data_class)).define(|itself| {
wasmer_module.define_nested_class("Memory", Some(&memory_data_class)).define(|itself| {
// Declare the `view` method.
itself.def("uint8_view", memory::ruby_memory_uint8array);

Expand All @@ -70,7 +72,7 @@ pub extern "C" fn Init_wasmer() {
let uint8array_data_class = Class::from_existing("Object");

// Declare the `MemoryView` Ruby class.
Class::new(stringify!($class_name), Some(&uint8array_data_class)).define(|itself| {
wasmer_module.define_nested_class(stringify!($class_name), Some(&uint8array_data_class)).define(|itself| {
// Declare the `bytes_per_element` getter method.
itself.def(
"bytes_per_element",
Expand Down
20 changes: 13 additions & 7 deletions src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::memory::view::{
uint8array::{RubyMemoryView as RubyUint8Array, MEMORY_VIEW_WRAPPER as UINT8ARRAY_WRAPPER},
};
use lazy_static::lazy_static;
use rutie::{class, methods, wrappable_struct, Class, Integer, Object};
use rutie::{class, methods, wrappable_struct, Module, Integer, Object};
use std::rc::Rc;
use wasmer_runtime as runtime;

Expand Down Expand Up @@ -65,7 +65,8 @@ methods!(
.unwrap_or(0);
let memory_view = itself.get_data(&*MEMORY_WRAPPER).uint8_view(offset);

Class::from_existing("Uint8Array").wrap_data(memory_view, &*UINT8ARRAY_WRAPPER)
let wasmer_module = Module::from_existing("Wasmer");
wasmer_module.get_nested_class("Uint8Array").wrap_data(memory_view, &*UINT8ARRAY_WRAPPER)
}

// Glue code to call the `Memory.int8_view` method.
Expand All @@ -75,7 +76,8 @@ methods!(
.unwrap_or(0);
let memory_view = itself.get_data(&*MEMORY_WRAPPER).int8_view(offset);

Class::from_existing("Int8Array").wrap_data(memory_view, &*INT8ARRAY_WRAPPER)
let wasmer_module = Module::from_existing("Wasmer");
wasmer_module.get_nested_class("Int8Array").wrap_data(memory_view, &*INT8ARRAY_WRAPPER)
}

// Glue code to call the `Memory.uint16_view` method.
Expand All @@ -85,7 +87,8 @@ methods!(
.unwrap_or(0);
let memory_view = itself.get_data(&*MEMORY_WRAPPER).uint16_view(offset);

Class::from_existing("Uint16Array").wrap_data(memory_view, &*UINT16ARRAY_WRAPPER)
let wasmer_module = Module::from_existing("Wasmer");
wasmer_module.get_nested_class("Uint16Array").wrap_data(memory_view, &*UINT16ARRAY_WRAPPER)
}

// Glue code to call the `Memory.int16_view` method.
Expand All @@ -95,7 +98,8 @@ methods!(
.unwrap_or(0);
let memory_view = itself.get_data(&*MEMORY_WRAPPER).int16_view(offset);

Class::from_existing("Int16Array").wrap_data(memory_view, &*INT16ARRAY_WRAPPER)
let wasmer_module = Module::from_existing("Wasmer");
wasmer_module.get_nested_class("Int16Array").wrap_data(memory_view, &*INT16ARRAY_WRAPPER)
}

// Glue code to call the `Memory.uint32_view` method.
Expand All @@ -105,7 +109,8 @@ methods!(
.unwrap_or(0);
let memory_view = itself.get_data(&*MEMORY_WRAPPER).uint32_view(offset);

Class::from_existing("Uint32Array").wrap_data(memory_view, &*UINT32ARRAY_WRAPPER)
let wasmer_module = Module::from_existing("Wasmer");
wasmer_module.get_nested_class("Uint32Array").wrap_data(memory_view, &*UINT32ARRAY_WRAPPER)
}

// Glue code to call the `Memory.int32_view` method.
Expand All @@ -115,6 +120,7 @@ methods!(
.unwrap_or(0);
let memory_view = itself.get_data(&*MEMORY_WRAPPER).int32_view(offset);

Class::from_existing("Int32Array").wrap_data(memory_view, &*INT32ARRAY_WRAPPER)
let wasmer_module = Module::from_existing("Wasmer");
wasmer_module.get_nested_class("Int32Array").wrap_data(memory_view, &*INT32ARRAY_WRAPPER)
}
);
38 changes: 19 additions & 19 deletions tests/instance_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,100 +10,100 @@ def invalid_bytes
end

def test_can_construct
assert Instance.new self.bytes
assert Wasmer::Instance.new self.bytes
end

def test_constructor_needs_bytes
error = assert_raises(ArgumentError) {
Instance.new 123
Wasmer::Instance.new 123
}
assert_equal "WebAssembly module must be represented by Ruby bytes only.", error.message
end

def test_module_must_have_an_exported_memory
error = assert_raises(RuntimeError) {
bytes = IO.read File.expand_path("no_memory.wasm", File.dirname(__FILE__)), mode: "rb"
Instance.new bytes
Wasmer::Instance.new bytes
}
assert_equal "The WebAssembly module has no exported memory.", error.message
end

def test_invalid_module
error = assert_raises(RuntimeError) {
Instance.new self.invalid_bytes
Wasmer::Instance.new self.invalid_bytes
}
assert_equal "Failed to instantiate the module:\n compile error: Validation error \"Invalid type\"", error.message
end

def test_basic_sum
assert_equal 3, Instance.new(self.bytes).exports.sum(1, 2)
assert_equal 3, Wasmer::Instance.new(self.bytes).exports.sum(1, 2)
end

def test_call_unknown_function
error = assert_raises(RuntimeError) {
Instance.new(self.bytes).exports.foo
Wasmer::Instance.new(self.bytes).exports.foo
}
assert_equal "Function `foo` does not exist.", error.message
end

def test_call_missing_argument
error = assert_raises(ArgumentError) {
Instance.new(self.bytes).exports.sum 1
Wasmer::Instance.new(self.bytes).exports.sum 1
}
assert_equal "Missing 1 argument(s) when calling `sum`: Expect 2 argument(s), given 1.", error.message
end

def test_call_extra_argument
error = assert_raises(ArgumentError) {
Instance.new(self.bytes).exports.sum 1, 2, 3
Wasmer::Instance.new(self.bytes).exports.sum 1, 2, 3
}
assert_equal "Given 1 extra argument(s) when calling `sum`: Expect 2 argument(s), given 3.", error.message
end

def test_call_cannot_convert_argument
error = assert_raises(ArgumentError) {
Instance.new(self.bytes).exports.sum 1, "2"
Wasmer::Instance.new(self.bytes).exports.sum 1, "2"
}
assert_equal "Cannot convert argument #2 to a WebAssembly value. Only integers and floats are supported. Given `RString`.", error.message
end

def test_call_arity_0
assert_equal 42, Instance.new(self.bytes).exports.arity_0
assert_equal 42, Wasmer::Instance.new(self.bytes).exports.arity_0
end

def test_call_i32_i32
assert_equal 7, Instance.new(self.bytes).exports.i32_i32(7)
assert_equal 7, Wasmer::Instance.new(self.bytes).exports.i32_i32(7)
end

def test_call_i64_i64
assert_equal 7, Instance.new(self.bytes).exports.i64_i64(7)
assert_equal 7, Wasmer::Instance.new(self.bytes).exports.i64_i64(7)
end

def test_call_f32_f32
assert_equal 7.0, Instance.new(self.bytes).exports.f32_f32(7.0)
assert_equal 7.0, Wasmer::Instance.new(self.bytes).exports.f32_f32(7.0)
end

def test_call_f64_f64
assert_equal 7.0, Instance.new(self.bytes).exports.f64_f64(7.0)
assert_equal 7.0, Wasmer::Instance.new(self.bytes).exports.f64_f64(7.0)
end

def test_call_i32_i64_f32_f64_f64
assert_equal 1 + 2 + 3.4 + 5.6, Instance.new(self.bytes).exports.i32_i64_f32_f64_f64(1, 2, 3.4, 5.6).round(6)
assert_equal 1 + 2 + 3.4 + 5.6, Wasmer::Instance.new(self.bytes).exports.i32_i64_f32_f64_f64(1, 2, 3.4, 5.6).round(6)
end

def test_call_bool_casted_to_i32
assert_equal 1, Instance.new(self.bytes).exports.bool_casted_to_i32
assert_equal 1, Wasmer::Instance.new(self.bytes).exports.bool_casted_to_i32
end

def test_call_string
assert_equal 1048576, Instance.new(self.bytes).exports.string
assert_equal 1048576, Wasmer::Instance.new(self.bytes).exports.string
end

def test_exports
assert_instance_of ExportedFunctions, Instance.new(self.bytes).exports
assert_instance_of Wasmer::ExportedFunctions, Wasmer::Instance.new(self.bytes).exports
end

def test_memory
assert_instance_of Memory, Instance.new(self.bytes).memory
assert_instance_of Wasmer::Memory, Wasmer::Instance.new(self.bytes).memory
end
end
Loading

0 comments on commit 4766efb

Please sign in to comment.