From ee6d82241d2f7143af771768cc687a11a847d097 Mon Sep 17 00:00:00 2001 From: Yota Toyama Date: Tue, 21 Jan 2025 23:48:49 +0900 Subject: [PATCH] Use `any-fn` crate (#2031) --- Cargo.lock | 7 ++ native/Cargo.toml | 3 +- native/src/dynamic.rs | 11 +-- native/src/dynamic/error.rs | 17 ++-- native/src/dynamic/function.rs | 152 --------------------------------- 5 files changed, 25 insertions(+), 165 deletions(-) delete mode 100644 native/src/dynamic/function.rs diff --git a/Cargo.lock b/Cargo.lock index 2ab58f286..7d9558625 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -81,6 +81,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "any-fn" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cda56fb5cafae83fa51209e5121c394e1a1e054b2818806d44d2609420305a" + [[package]] name = "autocfg" version = "1.4.0" @@ -1397,6 +1403,7 @@ dependencies = [ name = "stak-native" version = "0.1.6" dependencies = [ + "any-fn", "heapless", "stak-vm", ] diff --git a/native/Cargo.toml b/native/Cargo.toml index 19a9ac17d..5c763fbe2 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -9,9 +9,10 @@ readme.workspace = true repository.workspace = true [features] -alloc = [] +alloc = ["dep:any-fn"] [dependencies] +any-fn = { version = "0.4.1", optional = true } heapless = { version = "0.8.0", default-features = false } stak-vm = { version = "0.7.21", path = "../vm" } diff --git a/native/src/dynamic.rs b/native/src/dynamic.rs index 361290b3b..22afad330 100644 --- a/native/src/dynamic.rs +++ b/native/src/dynamic.rs @@ -1,13 +1,10 @@ //! Native functions dynamically defined. mod error; -mod function; -pub use self::{ - error::DynamicError, - function::{DynamicFunction, IntoDynamicFunction}, -}; +pub use self::error::DynamicError; use alloc::boxed::Box; +use any_fn::AnyFn; use core::{any::Any, cell::RefCell}; use heapless::Vec; use stak_vm::{Error, Memory, Number, PrimitiveSet, Type}; @@ -16,13 +13,13 @@ const MAXIMUM_ARGUMENT_COUNT: usize = 16; /// A dynamic primitive set equipped with native functions in Rust. pub struct DynamicPrimitiveSet<'a, const N: usize> { - functions: &'a mut [DynamicFunction<'a>], + functions: &'a mut [AnyFn<'a>], objects: [Option>>; N], } impl<'a, const N: usize> DynamicPrimitiveSet<'a, N> { /// Creates a primitive set. - pub fn new(functions: &'a mut [DynamicFunction<'a>]) -> Self { + pub fn new(functions: &'a mut [AnyFn<'a>]) -> Self { Self { functions, // TODO Garbage-collect foreign objects. diff --git a/native/src/dynamic/error.rs b/native/src/dynamic/error.rs index 50957ff6d..68b189cba 100644 --- a/native/src/dynamic/error.rs +++ b/native/src/dynamic/error.rs @@ -1,31 +1,38 @@ +use any_fn::AnyFnError; use core::{ - error, + error::Error, fmt::{self, Display, Formatter}, }; /// An error. #[derive(Debug)] pub enum DynamicError { - /// A object downcast error. - Downcast, + /// An `AnyFn` error. + AnyFn(AnyFnError), /// A object index error. ObjectIndex, /// A virtual machine error. Vm(stak_vm::Error), } +impl From for DynamicError { + fn from(error: AnyFnError) -> Self { + Self::AnyFn(error) + } +} + impl From for DynamicError { fn from(error: stak_vm::Error) -> Self { Self::Vm(error) } } -impl error::Error for DynamicError {} +impl Error for DynamicError {} impl Display for DynamicError { fn fmt(&self, formatter: &mut Formatter) -> fmt::Result { match self { - Self::Downcast => write!(formatter, "cannot downcast object"), + Self::AnyFn(error) => write!(formatter, "{error}"), Self::ObjectIndex => write!(formatter, "invalid object index"), Self::Vm(error) => write!(formatter, "{error}"), } diff --git a/native/src/dynamic/function.rs b/native/src/dynamic/function.rs deleted file mode 100644 index eb9d5b9fd..000000000 --- a/native/src/dynamic/function.rs +++ /dev/null @@ -1,152 +0,0 @@ -use super::error::DynamicError; -use alloc::boxed::Box; -use core::{any::Any, cell::RefCell, marker::PhantomData, mem::size_of}; - -type AnyCell<'a> = &'a RefCell>; -type BoxedFunction<'a> = Box Result, DynamicError> + 'a>; - -/// A dynamic function. -pub struct DynamicFunction<'a> { - arity: usize, - function: BoxedFunction<'a>, -} - -impl<'a> DynamicFunction<'a> { - /// Creates a dynamic function. - pub fn new(arity: usize, function: BoxedFunction<'a>) -> Self { - Self { arity, function } - } - - /// Returns an arity of unboxed arguments. - pub const fn arity(&self) -> usize { - self.arity - } - - /// Calls a function. - pub fn call(&mut self, arguments: &[AnyCell]) -> Result, DynamicError> { - (self.function)(arguments) - } -} - -/// A native function dynamically defined. -pub trait IntoDynamicFunction<'a, T, S> { - /// Converts itself into a dynamic function. - fn into_dynamic(self) -> DynamicFunction<'a>; -} - -struct RefMut { - _data: PhantomData, -} - -macro_rules! impl_function { - ([$($type:ident),*], [$($ref:ident),*]) => { - impl<'a, T1: FnMut($($type,)* $(&mut $ref,)*) -> T2 + 'a, T2: Any, $($type: Any + Clone,)* $($ref: Any,)*> IntoDynamicFunction<'a, ($($type,)* $(RefMut<$ref>,)*), T2> for T1 { - #[allow(non_snake_case)] - fn into_dynamic(mut self) -> DynamicFunction<'a> { - #[allow(unused, unused_mut)] - DynamicFunction::new( - (&[$(size_of::<$type>(),)* $(size_of::<$ref>(),)*] as &[usize]).len(), - Box::new(move |arguments: &[AnyCell]| { - let mut iter = 0..; - - Ok(Box::new(self( - $( - arguments[iter.next().unwrap_or_default()] - .borrow() - .downcast_ref::<$type>() - .ok_or(DynamicError::Downcast)? - .clone(), - )* - $( - arguments[iter.next().unwrap_or_default()] - .borrow_mut() - .downcast_mut::<$ref>() - .ok_or(DynamicError::Downcast)?, - )* - ))) - }), - ) - } - } - }; -} - -macro_rules! impl_ref_functions { - ([$($type:ident),*], [$first_ref:ident, $($ref:ident),*]) => { - impl_function!([$($type),*], [$first_ref, $($ref),*]); - impl_ref_functions!([$($type),*], [$($ref),*]); - }; - ([$($type:ident),*], [$ref:ident]) => { - impl_function!([$($type),*], [$ref]); - impl_function!([$($type),*], []); - } -} - -macro_rules! impl_functions { - ([$first_type:ident, $($type:ident),*], [$($ref:ident),*]) => { - impl_ref_functions!([$first_type, $($type),*], [$($ref),*]); - impl_functions!([$($type),*], [$($ref),*]); - }; - ([$type:ident], [$($ref:ident),*]) => { - impl_ref_functions!([$type], [$($ref),*]); - impl_ref_functions!([], [$($ref),*]); - } -} - -impl_functions!( - [A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z], - [い, ろ, は, に, お, へ, と, ち, り, ぬ, る, を] -); - -#[cfg(test)] -mod tests { - use super::*; - use alloc::{format, string::String}; - use core::cell::RefCell; - - #[derive(Clone, Debug)] - struct Foo {} - - const fn foo(x: usize, y: usize) -> usize { - x + y - } - - fn bar(name: String, value: Option) -> String { - format!("{name}: {value:?}") - } - - fn baz(x: usize, y: &mut usize) { - *y = x; - } - - fn wrap(x: T) -> RefCell> { - RefCell::new(Box::new(x)) - } - - #[test] - fn create_dynamic_function() { - foo.into_dynamic(); - bar.into_dynamic(); - } - - #[test] - fn call_dynamic_function() { - assert_eq!( - *foo.into_dynamic() - .call(&[&wrap(1usize), &wrap(2usize)]) - .unwrap() - .downcast::() - .unwrap(), - 3 - ); - } - - #[test] - fn call_dynamic_function_with_mutable_reference() { - let x: RefCell> = RefCell::new(Box::new(0usize)); - - baz.into_dynamic().call(&[&wrap(42usize), &x]).unwrap(); - - assert_eq!(*x.borrow().downcast_ref::().unwrap(), 42); - } -}