Skip to content

Commit 627a016

Browse files
committed
Generalize function invocation at runtime using macros
1 parent 70e1c6a commit 627a016

File tree

6 files changed

+173
-106
lines changed

6 files changed

+173
-106
lines changed

crates/mun_abi/src/autogen.rs

+3-52
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ use crate::prelude::*;
22

33
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
44

5-
use std::ffi::CStr;
6-
use std::mem;
5+
use std::ffi::{c_void, CStr};
76
use std::slice;
87

98
impl TypeInfo {
@@ -43,56 +42,8 @@ impl FunctionInfo {
4342
unsafe { self.return_type.as_ref() }
4443
}
4544

46-
/// Tries to downcast the `fn_ptr` to the specified function type.
47-
///
48-
/// Returns an error message upon failure.
49-
pub fn downcast_fn2<A: Reflection, B: Reflection, Output: Reflection>(
50-
&self,
51-
) -> Result<fn(A, B) -> Output, String> {
52-
let num_args = 2;
53-
54-
let arg_types = self.arg_types();
55-
if arg_types.len() != num_args {
56-
return Err(format!(
57-
"Invalid number of arguments. Expected: {}. Found: {}.",
58-
arg_types.len(),
59-
num_args
60-
));
61-
}
62-
63-
if arg_types[0].guid != A::type_guid() {
64-
return Err(format!(
65-
"Invalid argument type for 'a'. Expected: {}. Found: {}.",
66-
arg_types[0].name(),
67-
A::type_name()
68-
));
69-
}
70-
71-
if arg_types[1].guid != B::type_guid() {
72-
return Err(format!(
73-
"Invalid argument type for 'b'. Expected: {}. Found: {}.",
74-
arg_types[1].name(),
75-
B::type_name()
76-
));
77-
}
78-
79-
if let Some(return_type) = self.return_type() {
80-
if return_type.guid != Output::type_guid() {
81-
return Err(format!(
82-
"Invalid return type. Expected: {}. Found: {}",
83-
return_type.name(),
84-
Output::type_name(),
85-
));
86-
}
87-
} else if <()>::type_guid() != Output::type_guid() {
88-
return Err(format!(
89-
"Invalid return type. Expected: {}. Found: {}",
90-
<()>::type_name(),
91-
Output::type_name(),
92-
));
93-
}
94-
95-
Ok(unsafe { mem::transmute(self.fn_ptr) })
45+
pub unsafe fn fn_ptr(&self) -> *const c_void {
46+
self.fn_ptr
9647
}
9748
}
9849

crates/mun_abi/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod autogen;
2+
mod macros;
23
mod reflection;
34

45
pub use autogen::*;

crates/mun_abi/src/macros.rs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#[doc(hidden)]
2+
#[macro_export(local_inner_macros)]
3+
macro_rules! count_args {
4+
() => { 0 };
5+
($name:ident) => { 1 };
6+
($first:ident, $($rest:ident),*) => {
7+
1 + count_args!($($rest),*)
8+
}
9+
}
10+
11+
/// Tries to downcast the `fn_ptr` of `FunctionInfo` to the specified function type.
12+
///
13+
/// Returns an error message upon failure.
14+
#[macro_export]
15+
macro_rules! downcast_fn {
16+
($FunctionInfo:expr, fn($($T:ident),*) -> $Output:ident) => {{
17+
let num_args = $crate::count_args!($($T),*);
18+
19+
let arg_types = $FunctionInfo.arg_types();
20+
if arg_types.len() != num_args {
21+
return Err(format!(
22+
"Invalid number of arguments. Expected: {}. Found: {}.",
23+
arg_types.len(),
24+
num_args
25+
));
26+
}
27+
28+
let mut idx = 0;
29+
$(
30+
if arg_types[idx].guid != $T::type_guid() {
31+
return Err(format!(
32+
"Invalid argument type at index {}. Expected: {}. Found: {}.",
33+
idx,
34+
arg_types[idx].name(),
35+
$T::type_name()
36+
));
37+
}
38+
idx += 1;
39+
)*
40+
41+
if let Some(return_type) = $FunctionInfo.return_type() {
42+
if return_type.guid != Output::type_guid() {
43+
return Err(format!(
44+
"Invalid return type. Expected: {}. Found: {}",
45+
return_type.name(),
46+
Output::type_name(),
47+
));
48+
}
49+
} else if <()>::type_guid() != Output::type_guid() {
50+
return Err(format!(
51+
"Invalid return type. Expected: {}. Found: {}",
52+
<()>::type_name(),
53+
Output::type_name(),
54+
));
55+
}
56+
57+
Ok(unsafe { core::mem::transmute($FunctionInfo.fn_ptr()) })
58+
}}
59+
}

crates/mun_runtime/src/assembly.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,9 @@ pub struct Assembly {
1919
impl Assembly {
2020
/// Loads an assembly for the library at `library_path` and its dependencies.
2121
pub fn load(library_path: &Path) -> Result<Self> {
22-
let library_name = library_path.file_name().ok_or(io::Error::new(
23-
io::ErrorKind::InvalidInput,
24-
"Incorrect library path.",
25-
))?;
22+
let library_name = library_path.file_name().ok_or_else(|| {
23+
io::Error::new(io::ErrorKind::InvalidInput, "Incorrect library path.")
24+
})?;
2625

2726
let tmp_dir = env::current_dir()?.join(LIB_DIR);
2827
if !tmp_dir.exists() {

crates/mun_runtime/src/lib.rs

+23-50
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ extern crate libloading;
55
mod assembly;
66
mod error;
77
mod library;
8+
#[macro_use]
9+
mod macros;
810

911
pub use crate::assembly::Assembly;
1012
pub use crate::error::*;
@@ -77,56 +79,11 @@ impl MunRuntime {
7779
self.assemblies.remove(library_path);
7880
}
7981

80-
/// Invokes the method `method_name` with arguments `args`, in the library compiled based on
81-
/// the manifest at `manifest_path`.
82-
///
83-
/// If an error occurs when invoking the method, an error message is logged. The runtime
84-
/// continues looping until the cause of the error has been resolved.
85-
pub fn invoke2<A: Reflection, B: Reflection, Output: Reflection>(
86-
&mut self,
87-
function_name: &str,
88-
a: A,
89-
b: B,
90-
) -> Output {
91-
// Initialize `updated` to `true` to guarantee the method is run at least once
92-
let mut updated = true;
93-
loop {
94-
if updated {
95-
let function = self
96-
.function_table
97-
.get(function_name)
98-
.ok_or(format!("Failed to obtain function '{}'", function_name))
99-
.and_then(FunctionInfo::downcast_fn2::<A, B, Output>);
100-
101-
match function {
102-
Ok(function) => return function(a, b),
103-
Err(ref e) => {
104-
eprintln!("{}", e);
105-
updated = false;
106-
}
107-
}
108-
} else {
109-
updated = self.update();
110-
}
111-
}
82+
/// Retrieves the function information corresponding to `function_name`, if available.
83+
pub fn get_function_info(&self, function_name: &str) -> Option<&FunctionInfo> {
84+
self.function_table.get(function_name)
11285
}
11386

114-
/// Retrieves the module corresponding to the manifest at `manifest_path`.
115-
// pub fn get_module(&self, manifest_path: &Path) -> Result<&Module> {
116-
// let manifest_path = manifest_path.canonicalize()?;
117-
118-
// self.assemblies.get(&manifest_path).ok_or(
119-
// io::Error::new(
120-
// io::ErrorKind::NotFound,
121-
// format!(
122-
// "The module at path '{}' cannot be found.",
123-
// manifest_path.to_string_lossy()
124-
// ),
125-
// )
126-
// .into(),
127-
// )
128-
// }
129-
13087
/// Updates the state of the runtime. This includes checking for file changes, and consequent
13188
/// recompilation.
13289
///
@@ -172,9 +129,25 @@ impl MunRuntime {
172129
}
173130
}
174131

132+
invoke_fn_impl! {
133+
fn invoke_fn0();
134+
fn invoke_fn1(a: A);
135+
fn invoke_fn2(a: A, b: B);
136+
fn invoke_fn3(a: A, b: B, c: C);
137+
fn invoke_fn4(a: A, b: B, c: C, d: D);
138+
fn invoke_fn5(a: A, b: B, c: C, d: D, e: E);
139+
fn invoke_fn6(a: A, b: B, c: C, d: D, e: E, f: F);
140+
fn invoke_fn7(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
141+
fn invoke_fn8(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
142+
fn invoke_fn9(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
143+
fn invoke_fn10(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
144+
fn invoke_fn11(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
145+
fn invoke_fn12(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);
146+
}
147+
175148
#[cfg(test)]
176149
mod tests {
177-
use super::MunRuntime;
150+
use super::{invoke_fn, MunRuntime};
178151
use std::path::PathBuf;
179152
use std::time::Duration;
180153

@@ -199,7 +172,7 @@ mod tests {
199172
let a: f64 = 4.0;
200173
let b: f64 = 2.0;
201174

202-
let result: f64 = runtime.invoke2("add", a, b);
175+
let result: f64 = invoke_fn!(runtime, "add", a, b);
203176

204177
assert_eq!(result, a + b);
205178
}

crates/mun_runtime/src/macros.rs

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
macro_rules! invoke_fn_impl {
2+
($(
3+
fn $FnName:ident($($Arg:tt: $T:ident),*);
4+
)+) => {
5+
$(
6+
impl MunRuntime {
7+
/// Invokes the method `method_name` with arguments `args`, in the library compiled based on
8+
/// the manifest at `manifest_path`.
9+
///
10+
/// If an error occurs when invoking the method, an error message is logged. The runtime
11+
/// continues looping until the cause of the error has been resolved.
12+
pub fn $FnName<$($T: Reflection,)* Output: Reflection>(
13+
&mut self,
14+
function_name: &str,
15+
$($Arg: $T,)*
16+
) -> Output {
17+
// Initialize `updated` to `true` to guarantee the method is run at least once
18+
let mut updated = true;
19+
loop {
20+
if updated {
21+
let function: core::result::Result<fn($($T),*) -> Output, String> = self
22+
.get_function_info(function_name)
23+
.ok_or(format!("Failed to obtain function '{}'", function_name))
24+
.and_then(|function| mun_abi::downcast_fn!(function, fn($($T),*) -> Output));
25+
26+
match function {
27+
Ok(function) => return function($($Arg),*),
28+
Err(ref e) => {
29+
eprintln!("{}", e);
30+
updated = false;
31+
}
32+
}
33+
} else {
34+
updated = self.update();
35+
}
36+
}
37+
}
38+
}
39+
)+
40+
}
41+
}
42+
43+
#[macro_export]
44+
macro_rules! invoke_fn {
45+
($Runtime:expr, $FnName:expr) => {
46+
$Runtime.invoke_fn0($FnName)
47+
};
48+
($Runtime:expr, $FnName:expr, $A:expr) => {
49+
$Runtime.invoke_fn1($FnName, $A)
50+
};
51+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr) => {
52+
$Runtime.invoke_fn2($FnName, $A, $B)
53+
};
54+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr) => {
55+
$Runtime.invoke_fn3($FnName, $A, $B, $C)
56+
};
57+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr) => {
58+
$Runtime.invoke_fn4($FnName, $A, $B, $C, $D)
59+
};
60+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr) => {
61+
$Runtime.invoke_fn5($FnName, $A, $B, $C, $D, $E)
62+
};
63+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr) => {
64+
$Runtime.invoke_fn6($FnName, $A, $B, $C, $D, $E, $F)
65+
};
66+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr) => {
67+
$Runtime.invoke_fn7($FnName, $A, $B, $C, $D, $E, $F, $G)
68+
};
69+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr, $H:expr) => {
70+
$Runtime.invoke_fn8($FnName, $A, $B, $C, $D, $E, $F, $G, $H)
71+
};
72+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr, $H:expr, $I:expr) => {
73+
$Runtime.invoke_fn9($FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I)
74+
};
75+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr, $H:expr, $I:expr, $J:expr) => {
76+
$Runtime.invoke_fn10($FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J)
77+
};
78+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr, $H:expr, $I:expr, $J:expr, $K:expr) => {
79+
$Runtime.invoke_fn11($FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K)
80+
};
81+
($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr, $H:expr, $I:expr, $J:expr, $K:expr, $L:expr) => {
82+
$Runtime.invoke_fn12($FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L)
83+
};
84+
}

0 commit comments

Comments
 (0)