Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support fetching imports from a Module #372

Merged
merged 14 commits into from
Sep 30, 2024
19 changes: 17 additions & 2 deletions ext/src/ruby_api/convert.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
use crate::{define_rb_intern, err, error, helpers::SymbolEnum};
use lazy_static::lazy_static;
use magnus::{prelude::*, Error, IntoValue, RArray, Ruby, Symbol, TryConvert, TypedData, Value};
use magnus::{
prelude::*, try_convert, Error, IntoValue, RArray, Ruby, Symbol, TryConvert, TypedData, Value,
};
use wasmtime::{ExternRef, RefType, Val, ValType};

use super::{func::Func, global::Global, memory::Memory, store::StoreContextValue, table::Table};
use super::{
func::{Func, FuncType},
global::{Global, GlobalType},
memory::{Memory, MemoryType},
store::StoreContextValue,
table::{Table, TableType},
};

define_rb_intern!(
I32 => "i32",
Expand Down Expand Up @@ -206,3 +214,10 @@ where
{
fn wrap_wasmtime_type(&self, store: StoreContextValue<'a>) -> Result<T, Error>;
}

pub trait WrapWasmtimeExternType<T>
where
T: TypedData,
{
fn wrap_wasmtime_type(&self) -> Result<T, Error>;
}
114 changes: 112 additions & 2 deletions ext/src/ruby_api/externals.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,98 @@
use super::{
convert::WrapWasmtimeType, func::Func, global::Global, memory::Memory, root,
store::StoreContextValue, table::Table,
convert::{WrapWasmtimeExternType, WrapWasmtimeType},
func::{Func, FuncType},
global::{Global, GlobalType},
memory::{Memory, MemoryType},
root,
store::StoreContextValue,
table::{Table, TableType},
};
use crate::{conversion_err, not_implemented};
use magnus::{
class, gc::Marker, method, prelude::*, rb_sys::AsRawValue, typed_data::Obj, DataTypeFunctions,
Error, Module, RClass, Ruby, TypedData, Value,
};

#[derive(TypedData)]
#[magnus(
class = "Wasmtime::ExternType",
size,
mark,
free_immediately,
unsafe_generics
)]
pub enum ExternType {
Func(Obj<FuncType>),
Global(Obj<GlobalType>),
Memory(Obj<MemoryType>),
Table(Obj<TableType>),
}

impl DataTypeFunctions for ExternType {
fn mark(&self, marker: &Marker) {
match self {
ExternType::Func(f) => marker.mark(*f),
ExternType::Global(g) => marker.mark(*g),
ExternType::Memory(m) => marker.mark(*m),
ExternType::Table(t) => marker.mark(*t),
}
}
}
unsafe impl Send for ExternType {}

impl ExternType {
/// @yard
/// Returns the exported function's FuncType or raises a `{ConversionError}` when the export is not a
/// function.
/// @return [FuncType] The exported function's type.
pub fn to_func_type(ruby: &Ruby, rb_self: Obj<Self>) -> Result<Value, Error> {
match *rb_self {
ExternType::Func(f) => Ok(f.as_value()),
_ => conversion_err!(Self::inner_class(rb_self), Func::class(ruby)),
}
}

/// @yard
/// Returns the exported global's GlobalType or raises a `{ConversionError}` when the export is not a global.
/// @return [GlobalType] The exported global's type.
pub fn to_global_type(ruby: &Ruby, rb_self: Obj<Self>) -> Result<Value, Error> {
match *rb_self {
ExternType::Global(g) => Ok(g.as_value()),
_ => conversion_err!(Self::inner_class(rb_self), Global::class(ruby)),
}
}

/// @yard
/// Returns the exported memory's MemoryType or raises a `{ConversionError}` when the export is not a
/// memory.
/// @return [MemoryType] The exported memory's type.
pub fn to_memory_type(ruby: &Ruby, rb_self: Obj<Self>) -> Result<Value, Error> {
match *rb_self {
ExternType::Memory(m) => Ok(m.as_value()),
_ => conversion_err!(Self::inner_class(rb_self), Memory::class(ruby)),
}
}

/// @yard
/// Returns the exported table's TableType or raises a `{ConversionError}` when the export is not a table.
/// @return [TableType] The exported table's type.
pub fn to_table_type(ruby: &Ruby, rb_self: Obj<Self>) -> Result<Value, Error> {
match *rb_self {
ExternType::Table(t) => Ok(t.as_value()),
_ => conversion_err!(Self::inner_class(rb_self), Table::class(ruby)),
}
}

fn inner_class(rb_self: Obj<Self>) -> RClass {
match *rb_self {
ExternType::Func(f) => f.class(),
ExternType::Global(g) => g.class(),
ExternType::Memory(m) => m.class(),
ExternType::Table(t) => t.class(),
}
}
}

/// @yard
/// @rename Wasmtime::Extern
/// An external item to a WebAssembly module, or a list of what can possibly be exported from a Wasm module.
Expand Down Expand Up @@ -127,7 +212,32 @@ impl<'a> WrapWasmtimeType<'a, Extern<'a>> for wasmtime::Extern {
}
}

impl WrapWasmtimeExternType<ExternType> for wasmtime::ExternType {
fn wrap_wasmtime_type(&self) -> Result<ExternType, Error> {
match self {
wasmtime::ExternType::Func(ft) => Ok(ExternType::Func(Obj::wrap(
FuncType::from_inner(ft.clone()),
))),
wasmtime::ExternType::Global(gt) => Ok(ExternType::Global(Obj::wrap(
GlobalType::from_inner(gt.clone()),
))),
wasmtime::ExternType::Memory(mt) => Ok(ExternType::Memory(Obj::wrap(
MemoryType::from_inner(mt.clone()),
))),
wasmtime::ExternType::Table(tt) => Ok(ExternType::Table(Obj::wrap(
TableType::from_inner(tt.clone()),
))),
}
}
}

pub fn init() -> Result<(), Error> {
let extern_type = root().define_class("ExternType", class::object())?;
extern_type.define_method("to_func_type", method!(ExternType::to_func_type, 0))?;
extern_type.define_method("to_global_type", method!(ExternType::to_global_type, 0))?;
extern_type.define_method("to_memory_type", method!(ExternType::to_memory_type, 0))?;
extern_type.define_method("to_table_type", method!(ExternType::to_table_type, 0))?;

let class = root().define_class("Extern", class::object())?;

class.define_method("to_func", method!(Extern::to_func, 0))?;
Expand Down
57 changes: 57 additions & 0 deletions ext/src/ruby_api/func.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::{
convert::{ToRubyValue, ToSym, ToValTypeVec, ToWasmVal},
engine,
errors::result_error,
params::Params,
root,
Expand All @@ -13,6 +14,58 @@ use magnus::{
};
use wasmtime::{Caller as CallerImpl, Func as FuncImpl, Val};

/// @yard
/// @rename Wasmtime::FuncType
/// Represents a WebAssembly Function Type
/// @see https://docs.rs/wasmtime/latest/wasmtime/struct.FuncType.html Wasmtime's Rust doc
#[derive(TypedData)]
#[magnus(
class = "Wasmtime::FuncType",
size,
mark,
free_immediately,
unsafe_generics
)]
pub struct FuncType {
inner: wasmtime::FuncType,
}

impl DataTypeFunctions for FuncType {}

impl FuncType {
pub fn from_inner(inner: wasmtime::FuncType) -> Self {
Self { inner }
}

/// @yard
/// @return [Array<Symbol>] The function's parameter types.
pub fn params(&self) -> Result<RArray, Error> {
let len = self.inner.params().len();
let mut params = self.inner.params();
params.try_fold(RArray::with_capacity(len), |array, p| {
array.push(p.to_sym()?)?;
Ok(array)
})
}

/// @yard
/// @return [Array<Symbol>] The function's result types.
pub fn results(&self) -> Result<RArray, Error> {
let len = self.inner.results().len();
let mut results = self.inner.results();
results.try_fold(RArray::with_capacity(len), |array, r| {
array.push(r.to_sym()?)?;
Ok(array)
})
}
}

impl From<&FuncType> for wasmtime::ExternType {
fn from(func: &FuncType) -> Self {
Self::Func(func.inner.clone())
}
}

/// @yard
/// @rename Wasmtime::Func
/// Represents a WebAssembly Function
Expand Down Expand Up @@ -287,6 +340,10 @@ pub fn make_func_closure(
}

pub fn init() -> Result<(), Error> {
let func_type = root().define_class("FuncType", class::object())?;
func_type.define_method("params", method!(FuncType::params, 0))?;
func_type.define_method("results", method!(FuncType::results, 0))?;

let func = root().define_class("Func", class::object())?;
func.define_singleton_method("new", function!(Func::new, -1))?;
func.define_method("call", method!(Func::call, -1))?;
Expand Down
59 changes: 56 additions & 3 deletions ext/src/ruby_api/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,55 @@ use magnus::{
class, function, gc::Marker, method, prelude::*, typed_data::Obj, DataTypeFunctions, Error,
Object, Symbol, TypedData, Value,
};
use wasmtime::{Extern, Global as GlobalImpl, GlobalType, Mutability};
use wasmtime::{Extern, Global as GlobalImpl, Mutability};

#[derive(TypedData)]
#[magnus(
class = "Wasmtime::GlobalType",
free_immediately,
mark,
unsafe_generics
)]
pub struct GlobalType {
inner: wasmtime::GlobalType,
}

impl DataTypeFunctions for GlobalType {
fn mark(&self, _marker: &Marker) {}
}

impl GlobalType {
pub fn from_inner(inner: wasmtime::GlobalType) -> Self {
Self { inner }
}

/// @yard
/// @def const?
/// @return [Boolean]
pub fn is_const(&self) -> bool {
self.inner.mutability() == Mutability::Const
}

/// @yard
/// @def var?
/// @return [Boolean]
pub fn is_var(&self) -> bool {
self.inner.mutability() == Mutability::Var
}

/// @yard
/// @def type
/// @return [Symbol] The Wasm type of the global‘s content.
pub fn type_(&self) -> Result<Symbol, Error> {
self.inner.content().to_sym()
}
}

impl From<&GlobalType> for wasmtime::ExternType {
fn from(global_type: &GlobalType) -> Self {
Self::Global(global_type.inner.clone())
}
}

/// @yard
/// @rename Wasmtime::Global
Expand Down Expand Up @@ -58,7 +106,7 @@ impl<'a> Global<'a> {
let wasm_default = default.to_wasm_val(&store.into(), wasm_type.clone())?;
let inner = GlobalImpl::new(
store.context_mut(),
GlobalType::new(wasm_type, mutability),
wasmtime::GlobalType::new(wasm_type, mutability),
wasm_default,
)
.map_err(|e| error!("{}", e))?;
Expand Down Expand Up @@ -124,7 +172,7 @@ impl<'a> Global<'a> {
})
}

fn ty(&self) -> Result<GlobalType, Error> {
fn ty(&self) -> Result<wasmtime::GlobalType, Error> {
Ok(self.inner.ty(self.store.context()?))
}

Expand All @@ -151,6 +199,11 @@ impl From<&Global<'_>> for Extern {
}

pub fn init() -> Result<(), Error> {
let type_class = root().define_class("GlobalType", class::object())?;
type_class.define_method("const?", method!(GlobalType::is_const, 0))?;
type_class.define_method("var?", method!(GlobalType::is_var, 0))?;
type_class.define_method("type", method!(GlobalType::type_, 0))?;

let class = root().define_class("Global", class::object())?;
class.define_singleton_method("var", function!(Global::var, 3))?;
class.define_singleton_method("const", function!(Global::const_, 3))?;
Expand Down
43 changes: 43 additions & 0 deletions ext/src/ruby_api/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,45 @@ define_rb_intern!(
MAX_SIZE => "max_size",
);

#[derive(TypedData)]
#[magnus(
class = "Wasmtime::MemoryType",
free_immediately,
mark,
unsafe_generics
)]
pub struct MemoryType {
inner: wasmtime::MemoryType,
}

impl DataTypeFunctions for MemoryType {
fn mark(&self, _marker: &Marker) {}
}

impl MemoryType {
pub fn from_inner(inner: wasmtime::MemoryType) -> Self {
Self { inner }
}

/// @yard
/// @return [Integer] The minimum number of memory pages.
pub fn min_size(&self) -> u64 {
self.inner.minimum()
}

/// @yard
/// @return [Integer, nil] The maximum number of memory pages.
pub fn max_size(&self) -> Option<u64> {
self.inner.maximum()
}
}

impl From<&MemoryType> for wasmtime::ExternType {
fn from(memory_type: &MemoryType) -> Self {
Self::Memory(memory_type.inner.clone())
}
}

/// @yard
/// @rename Wasmtime::Memory
/// Represents a WebAssembly memory.
Expand Down Expand Up @@ -224,6 +263,10 @@ impl From<&Memory<'_>> for Extern {
}

pub fn init(ruby: &Ruby) -> Result<(), Error> {
let type_class = root().define_class("MemoryType", class::object())?;
type_class.define_method("min_size", method!(MemoryType::min_size, 0))?;
type_class.define_method("max_size", method!(MemoryType::max_size, 0))?;

let class = root().define_class("Memory", class::object())?;
class.define_singleton_method("new", function!(Memory::new, -1))?;
class.define_method("min_size", method!(Memory::min_size, 0))?;
Expand Down
Loading