From 94ffbb2645cb169540e3c77900d0b9d455a118b9 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Fri, 3 Sep 2021 03:29:51 +0200 Subject: [PATCH] refactor: simplified invoke syntax (#347) --- .../ch01-getting-started/listing04.rs | 6 +- .../listings/ch02-basic-concepts/listing02.rs | 4 +- .../listings/ch02-basic-concepts/listing03.rs | 4 +- book/listings/ch03-structs/listing11.rs | 8 +- book/listings/ch03-structs/listing12.rs | 4 +- book/listings/ch03-structs/listing14.rs | 6 +- crates/mun/src/ops/start.rs | 17 +- crates/mun/tests/integration.rs | 4 +- crates/mun_runtime/Cargo.toml | 1 + crates/mun_runtime/examples/buoyancy.rs | 8 +- crates/mun_runtime/examples/fibonacci.rs | 11 +- crates/mun_runtime/src/adt.rs | 1 - crates/mun_runtime/src/lib.rs | 228 +++++++++++++-- crates/mun_runtime/src/macros.rs | 261 ------------------ crates/mun_runtime/tests/marshalling.rs | 78 +++--- crates/mun_runtime/tests/memory.rs | 61 ++-- crates/mun_runtime/tests/runtime.rs | 18 ++ crates/mun_runtime/tests/util.rs | 7 +- crates/mun_skeptic/src/runtime.rs | 7 +- 19 files changed, 351 insertions(+), 383 deletions(-) delete mode 100644 crates/mun_runtime/src/macros.rs diff --git a/book/listings/ch01-getting-started/listing04.rs b/book/listings/ch01-getting-started/listing04.rs index 25de3a6bf..489ae575e 100644 --- a/book/listings/ch01-getting-started/listing04.rs +++ b/book/listings/ch01-getting-started/listing04.rs @@ -1,4 +1,4 @@ -use mun_runtime::{invoke_fn, RuntimeBuilder}; +use mun_runtime::RuntimeBuilder; use std::{cell::RefCell, env, rc::Rc}; fn main() { @@ -11,8 +11,8 @@ fn main() { loop { { let runtime_ref = runtime.borrow(); - let arg: i64 = invoke_fn!(runtime_ref, "arg").unwrap(); - let result: i64 = invoke_fn!(runtime_ref, "fibonacci", arg).unwrap(); + let arg: i64 = runtime_ref.invoke("arg", ()).unwrap(); + let result: i64 = runtime_ref.invoke("fibonacci", (arg,)).unwrap(); println!("fibonacci({}) = {}", arg, result); } runtime.borrow_mut().update(); diff --git a/book/listings/ch02-basic-concepts/listing02.rs b/book/listings/ch02-basic-concepts/listing02.rs index ebbe79a21..d72ff69cc 100644 --- a/book/listings/ch02-basic-concepts/listing02.rs +++ b/book/listings/ch02-basic-concepts/listing02.rs @@ -1,4 +1,4 @@ -use mun_runtime::{invoke_fn, RuntimeBuilder}; +use mun_runtime::RuntimeBuilder; use std::{cell::RefCell, rc::Rc}; fn main() { @@ -7,6 +7,6 @@ fn main() { .expect("Failed to spawn Runtime"); let runtime_ref = runtime.borrow(); - let result: bool = invoke_fn!(runtime_ref, "random_bool").unwrap(); + let result: bool = runtime_ref.invoke("random_bool", ()).unwrap(); println!("random bool: {}", result); } diff --git a/book/listings/ch02-basic-concepts/listing03.rs b/book/listings/ch02-basic-concepts/listing03.rs index 7e019fccf..c1a8967a7 100644 --- a/book/listings/ch02-basic-concepts/listing03.rs +++ b/book/listings/ch02-basic-concepts/listing03.rs @@ -1,4 +1,4 @@ -use mun_runtime::{invoke_fn, RuntimeBuilder}; +use mun_runtime::RuntimeBuilder; use std::{cell::RefCell, rc::Rc}; extern "C" fn random() -> i64 { @@ -14,6 +14,6 @@ fn main() { .expect("Failed to spawn Runtime"); let runtime_ref = runtime.borrow(); - let result: bool = invoke_fn!(runtime_ref, "random_bool").unwrap(); + let result: bool = runtime_ref.invoke("random_bool", ()).unwrap(); println!("random_bool: {}", result); } diff --git a/book/listings/ch03-structs/listing11.rs b/book/listings/ch03-structs/listing11.rs index 4ae0d2fb2..874d69507 100644 --- a/book/listings/ch03-structs/listing11.rs +++ b/book/listings/ch03-structs/listing11.rs @@ -1,5 +1,5 @@ # extern crate mun_runtime; -use mun_runtime::{invoke_fn, RuntimeBuilder, StructRef}; +use mun_runtime::{RuntimeBuilder, StructRef}; use std::{cell::RefCell, env, rc::Rc}; fn main() { @@ -10,7 +10,7 @@ fn main() { .expect("Failed to spawn Runtime"); let runtime_ref = runtime.borrow(); - let a: StructRef = invoke_fn!(runtime_ref, "vector2_new", -1.0f32, 1.0f32).unwrap(); - let b: StructRef = invoke_fn!(runtime_ref, "vector2_new", 1.0f32, -1.0f32).unwrap(); - let added: StructRef = invoke_fn!(runtime_ref, "vector2_add", a, b).unwrap(); + let a: StructRef = runtime_ref.invoke("vector2_new", (-1.0f32, 1.0f32)).unwrap(); + let b: StructRef = runtime_ref.invoke("vector2_new", (1.0f32, -1.0f32)).unwrap(); + let added: StructRef = runtime_ref.invoke("vector2_add", (a, b)).unwrap(); } diff --git a/book/listings/ch03-structs/listing12.rs b/book/listings/ch03-structs/listing12.rs index c2ba16972..d88e0f512 100644 --- a/book/listings/ch03-structs/listing12.rs +++ b/book/listings/ch03-structs/listing12.rs @@ -1,5 +1,5 @@ # extern crate mun_runtime; -# use mun_runtime::{invoke_fn, RuntimeBuilder, StructRef}; +# use mun_runtime::{RuntimeBuilder, StructRef}; # use std::{cell::RefCell, env, rc::Rc}; # # fn main() { @@ -11,7 +11,7 @@ # .expect("Failed to spawn Runtime"); # let runtime_ref = runtime.borrow(); - let mut xy: StructRef = invoke_fn!(runtime_ref, "vector2_new", -1.0f32, 1.0f32).unwrap(); + let mut xy: StructRef = runtime_ref.invoke("vector2_new", (-1.0f32, 1.0f32)).unwrap(); let x: f32 = xy.get("x").unwrap(); xy.set("x", x * x).unwrap(); let y = xy.replace("y", -1.0f32).unwrap(); diff --git a/book/listings/ch03-structs/listing14.rs b/book/listings/ch03-structs/listing14.rs index f36a6623a..cc83a763e 100644 --- a/book/listings/ch03-structs/listing14.rs +++ b/book/listings/ch03-structs/listing14.rs @@ -1,5 +1,5 @@ # extern crate mun_runtime; -use mun_runtime::{invoke_fn, RuntimeBuilder, StructRef}; +use mun_runtime::{RuntimeBuilder, StructRef}; use std::{env, time}; extern "C" fn log_f32(value: f32) { @@ -16,7 +16,7 @@ fn main() { let ctx = { let runtime_ref = runtime.borrow(); - let ctx: StructRef = invoke_fn!(runtime_ref, "new_sim").unwrap(); + let ctx: StructRef = runtime_ref.invoke("new_sim", ()).unwrap(); ctx.root(runtime.clone()) }; @@ -35,7 +35,7 @@ fn main() { { let runtime_ref = runtime.borrow(); - let _: () = invoke_fn!(runtime_ref, "sim_update", unsafe { ctx.as_ref(&runtime_ref) }, elapsed_secs).unwrap(); + let _: () = runtime_ref.invoke("sim_update", (unsafe { ctx.as_ref(&runtime_ref) }, elapsed_secs)).unwrap(); } previous = now; diff --git a/crates/mun/src/ops/start.rs b/crates/mun/src/ops/start.rs index f2ef28d73..27f86d198 100644 --- a/crates/mun/src/ops/start.rs +++ b/crates/mun/src/ops/start.rs @@ -2,7 +2,7 @@ use std::{cell::RefCell, rc::Rc}; use anyhow::anyhow; use clap::ArgMatches; -use mun_runtime::{invoke_fn, ReturnTypeReflection, Runtime, RuntimeBuilder}; +use mun_runtime::{ReturnTypeReflection, Runtime, RuntimeBuilder}; use crate::ExitStatus; @@ -24,15 +24,21 @@ pub fn start(matches: &ArgMatches) -> Result { if let Some(ret_type) = fn_definition.prototype.signature.return_type() { let type_guid = &ret_type.guid; if *type_guid == bool::type_guid() { - let result: bool = invoke_fn!(borrowed, entry_point).map_err(|e| anyhow!("{}", e))?; + let result: bool = borrowed + .invoke(entry_point, ()) + .map_err(|e| anyhow!("{}", e))?; println!("{}", result) } else if *type_guid == f64::type_guid() { - let result: f64 = invoke_fn!(borrowed, entry_point).map_err(|e| anyhow!("{}", e))?; + let result: f64 = borrowed + .invoke(entry_point, ()) + .map_err(|e| anyhow!("{}", e))?; println!("{}", result) } else if *type_guid == i64::type_guid() { - let result: i64 = invoke_fn!(borrowed, entry_point).map_err(|e| anyhow!("{}", e))?; + let result: i64 = borrowed + .invoke(entry_point, ()) + .map_err(|e| anyhow!("{}", e))?; println!("{}", result) } else { @@ -44,7 +50,8 @@ pub fn start(matches: &ArgMatches) -> Result { Ok(ExitStatus::Success) } else { #[allow(clippy::unit_arg)] - invoke_fn!(borrowed, entry_point) + borrowed + .invoke(entry_point, ()) .map(|_: ()| ExitStatus::Success) .map_err(|e| anyhow!("{}", e)) } diff --git a/crates/mun/tests/integration.rs b/crates/mun/tests/integration.rs index 958421b89..a68138c15 100644 --- a/crates/mun/tests/integration.rs +++ b/crates/mun/tests/integration.rs @@ -1,5 +1,5 @@ use mun::run_with_args; -use mun_runtime::{invoke_fn, RuntimeBuilder}; +use mun_runtime::RuntimeBuilder; use std::ffi::OsString; use std::path::Path; @@ -75,6 +75,6 @@ fn build_and_run(project: &Path) { let runtime = RuntimeBuilder::new(&library_path).spawn().unwrap(); let runtime_ref = runtime.borrow(); - let result: f64 = invoke_fn!(runtime_ref, "main").unwrap(); + let result: f64 = runtime_ref.invoke("main", ()).unwrap(); assert_eq!(result, 3.14159); } diff --git a/crates/mun_runtime/Cargo.toml b/crates/mun_runtime/Cargo.toml index ba99036ec..f894e9a87 100644 --- a/crates/mun_runtime/Cargo.toml +++ b/crates/mun_runtime/Cargo.toml @@ -24,6 +24,7 @@ notify = "4.0.12" once_cell = "1.4.0" parking_lot = "0.11.1" rustc-hash = "1.1" +seq-macro = "0.2.2" [dev-dependencies] compiler = { version="=0.3.0", path="../mun_compiler", package = "mun_compiler" } diff --git a/crates/mun_runtime/examples/buoyancy.rs b/crates/mun_runtime/examples/buoyancy.rs index d7af8d35e..67980a929 100644 --- a/crates/mun_runtime/examples/buoyancy.rs +++ b/crates/mun_runtime/examples/buoyancy.rs @@ -1,4 +1,4 @@ -use mun_runtime::{invoke_fn, RuntimeBuilder, StructRef}; +use mun_runtime::{RuntimeBuilder, StructRef}; use std::{env, time}; extern "C" fn log_f32(value: f32) { @@ -18,7 +18,7 @@ fn main() { .expect("Failed to spawn Runtime"); let runtime_ref = runtime.borrow(); - let ctx: StructRef = invoke_fn!(runtime_ref, "new_sim").unwrap(); + let ctx: StructRef = runtime_ref.invoke("new_sim", ()).unwrap(); let mut previous = time::Instant::now(); const FRAME_TIME: time::Duration = time::Duration::from_millis(40); @@ -34,7 +34,9 @@ fn main() { }; let runtime_ref = runtime.borrow(); - let _: () = invoke_fn!(runtime_ref, "sim_update", ctx.clone(), elapsed_secs).unwrap(); + let _: () = runtime_ref + .invoke("sim_update", (ctx.clone(), elapsed_secs)) + .unwrap(); previous = now; runtime.borrow_mut().update(); diff --git a/crates/mun_runtime/examples/fibonacci.rs b/crates/mun_runtime/examples/fibonacci.rs index 8f2dcc5b6..03a0d30f7 100644 --- a/crates/mun_runtime/examples/fibonacci.rs +++ b/crates/mun_runtime/examples/fibonacci.rs @@ -1,4 +1,4 @@ -use mun_runtime::{invoke_fn, RuntimeBuilder}; +use mun_runtime::RuntimeBuilder; use std::env; // How to run? @@ -16,9 +16,12 @@ fn main() { let mut runtime_ref = runtime.borrow_mut(); loop { - let n: i64 = invoke_fn!(runtime_ref, "nth").unwrap_or_else(|e| e.wait(&mut runtime_ref)); - let result: i64 = - invoke_fn!(runtime_ref, "fibonacci", n).unwrap_or_else(|e| e.wait(&mut runtime_ref)); + let n: i64 = runtime_ref + .invoke("nth", ()) + .unwrap_or_else(|e| e.wait(&mut runtime_ref)); + let result: i64 = runtime_ref + .invoke("fibonacci", (n,)) + .unwrap_or_else(|e| e.wait(&mut runtime_ref)); println!("fibonacci({}) = {}", n, result); runtime_ref.update(); } diff --git a/crates/mun_runtime/src/adt.rs b/crates/mun_runtime/src/adt.rs index fc893e677..a6cbd562c 100644 --- a/crates/mun_runtime/src/adt.rs +++ b/crates/mun_runtime/src/adt.rs @@ -216,7 +216,6 @@ impl<'s> Marshal<'s> for StructRef<'s> { fn marshal_from<'r>(value: Self::MunType, runtime: &'r Runtime) -> Self where - Self: 's, 'r: 's, { StructRef::new(value, runtime) diff --git a/crates/mun_runtime/src/lib.rs b/crates/mun_runtime/src/lib.rs index a44e624c1..1dab0e389 100644 --- a/crates/mun_runtime/src/lib.rs +++ b/crates/mun_runtime/src/lib.rs @@ -6,8 +6,6 @@ mod assembly; #[macro_use] -mod macros; -#[macro_use] mod garbage_collector; mod adt; mod marshal; @@ -41,7 +39,10 @@ pub use crate::{ marshal::Marshal, reflection::{ArgumentReflection, ReturnTypeReflection}, }; +use abi::FunctionSignature; pub use abi::IntoFunctionDefinition; +use std::ffi::c_void; +use std::fmt::{Debug, Display, Formatter}; /// Options for the construction of a [`Runtime`]. pub struct RuntimeOptions { @@ -436,21 +437,210 @@ impl Runtime { } } -invoke_fn_impl! { - fn invoke_fn0() -> InvokeErr0; - fn invoke_fn1(arg1: A) -> InvokeErr1; - fn invoke_fn2(arg1: A, arg2: B) -> InvokeErr2; - fn invoke_fn3(arg1: A, arg2: B, arg3: C) -> InvokeErr3; - fn invoke_fn4(arg1: A, arg2: B, arg3: C, arg4: D) -> InvokeErr4; - fn invoke_fn5(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E) -> InvokeErr5; - fn invoke_fn6(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F) -> InvokeErr6; - fn invoke_fn7(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G) -> InvokeErr7; - fn invoke_fn8(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G, arg8: H) -> InvokeErr8; - fn invoke_fn9(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G, arg8: H, arg9: I) -> InvokeErr9; - fn invoke_fn10(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G, arg8: H, arg9: I, arg10: J) -> InvokeErr10; - fn invoke_fn11(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G, arg8: H, arg9: I, arg10: J, arg11: K) -> InvokeErr11; - fn invoke_fn12(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G, arg8: H, arg9: I, arg10: J, arg11: K, arg12: L) -> InvokeErr12; - fn invoke_fn13(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G, arg8: H, arg9: I, arg10: J, arg11: K, arg12: L, arg13: M) -> InvokeErr13; - fn invoke_fn14(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G, arg8: H, arg9: I, arg10: J, arg11: K, arg12: L, arg13: M, arg14: N) -> InvokeErr14; - fn invoke_fn15(arg1: A, arg2: B, arg3: C, arg4: D, arg5: E, arg6: F, arg7: G, arg8: H, arg9: I, arg10: J, arg11: K, arg12: L, arg13: M, arg14: N, arg15: O) -> InvokeErr15; +/// An error that might occur when calling a mun function from Rust. +pub struct InvokeErr, T> { + msg: String, + function_name: Name, + arguments: T, +} + +impl, T> Debug for InvokeErr { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.msg) + } +} + +impl, T> Display for InvokeErr { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", &self.msg) + } +} + +impl, T: InvokeArgs> InvokeErr { + /// Retries a function invocation once, resulting in a potentially successful + /// invocation. + // FIXME: `unwrap_or_else` does not compile for `StructRef`, due to + // https://doc.rust-lang.org/nomicon/lifetime-mismatch.html#improperly-reduced-borrows + pub fn retry<'r, 'o, Output>(self, runtime: &'r mut Runtime) -> Result + where + Output: 'o + ReturnTypeReflection + Marshal<'o>, + 'r: 'o, + { + // Safety: The output of `retry_impl` is guaranteed to only contain a shared + // reference. + unsafe { self.retry_impl(runtime) } + } + + /// Retries the function invocation until it succeeds, resulting in an output. + // FIXME: `unwrap_or_else` does not compile for `StructRef`, due to + // https://doc.rust-lang.org/nomicon/lifetime-mismatch.html#improperly-reduced-borrows + pub fn wait<'r, 'o, Output>(mut self, runtime: &'r mut Runtime) -> Output + where + Output: 'o + ReturnTypeReflection + Marshal<'o>, + 'r: 'o, + { + // Safety: The output of `retry_impl` is guaranteed to only contain a shared + // reference. + let runtime = &*runtime; + + loop { + self = match unsafe { self.retry_impl(runtime) } { + Ok(output) => return output, + Err(e) => e, + }; + } + } + + /// Inner implementation that retries a function invocation once, resulting in a + /// potentially successful invocation. This is a workaround for: + /// https://doc.rust-lang.org/nomicon/lifetime-mismatch.html + /// + /// # Safety + /// + /// When calling this function, you have to guarantee that `runtime` is mutably + /// borrowed. The `Output` value can only contain a shared borrow of `runtime`. + unsafe fn retry_impl<'r, 'o, Output>(self, runtime: &'r Runtime) -> Result + where + Output: 'o + ReturnTypeReflection + Marshal<'o>, + 'r: 'o, + { + #[allow(clippy::cast_ref_to_mut)] + let runtime = &mut *(runtime as *const Runtime as *mut Runtime); + + eprintln!("{}", self.msg); + while !runtime.update() { + // Wait until there has been an update that might fix the error + } + + runtime.invoke(self.function_name, self.arguments) + } +} + +/// A trait that handles calling a certain function with a set of arguments. This trait is +/// implemented for tuples up to and including 20 elements. +pub trait InvokeArgs { + /// Determines whether the specified function can be called with these arguments + fn can_invoke<'runtime>( + &self, + runtime: &'runtime Runtime, + signature: &FunctionSignature, + ) -> Result<(), String>; + + /// Calls the specified function with these function arguments + /// + /// # Safety + /// + /// The `fn_ptr` is cast and invoked which might result in undefined behavior. + unsafe fn invoke(self, fn_ptr: *const c_void) -> ReturnType; +} + +// Implement `InvokeTraits` for tuples up to and including 20 elements +seq_macro::seq!(N in 0..=20 {#( +seq_macro::seq!(I in 0..N { + impl<'arg, #(T#I: ArgumentReflection + Marshal<'arg>,)*> InvokeArgs for (#(T#I,)*) { + #[allow(unused_variables)] + fn can_invoke<'runtime>(&self, runtime: &'runtime Runtime, signature: &FunctionSignature) -> Result<(), String> { + let arg_types = signature.arg_types(); + + // Ensure the number of arguments match + #[allow(clippy::len_zero)] + if N != arg_types.len() { + return Err(format!("invalid return type. Expected: {}. Found: {}", N, arg_types.len())) + } + + #( + crate::reflection::equals_argument_type(runtime, arg_types[I], &self.I) + .map_err(|(expected, found)| { + format!( + "invalid argument type at index {}. Expected: {}. Found: {}.", + I, + expected, + found, + ) + })?; + )* + + Ok(()) + } + + unsafe fn invoke(self, fn_ptr: *const c_void) -> ReturnType { + #[allow(clippy::type_complexity)] + let function: fn(#(T#I::MunType,)*) -> ReturnType = core::mem::transmute(fn_ptr); + function(#(self.I.marshal_into(),)*) + } + } +}); +)*}); + +impl Runtime { + /// Invokes the Mun function called `function_name` with the specified `arguments`. + pub fn invoke< + 'runtime, + 'ret, + ReturnType: ReturnTypeReflection + Marshal<'ret> + 'ret, + ArgTypes: InvokeArgs, + Name: AsRef, + >( + &'runtime self, + function_name: Name, + arguments: ArgTypes, + ) -> Result> + where + 'runtime: 'ret, + { + // Get the function information from the runtime + let function_name_str = function_name.as_ref(); + let function_info = match self + .get_function_definition(function_name_str) + .ok_or_else(|| format!("failed to obtain function '{}'", function_name_str)) + { + Ok(function_info) => function_info, + Err(msg) => { + return Err(InvokeErr { + msg, + function_name, + arguments, + }) + } + }; + + // Validate the arguments + match arguments.can_invoke(self, &function_info.prototype.signature) { + Ok(_) => {} + Err(msg) => { + return Err(InvokeErr { + msg, + function_name, + arguments, + }) + } + }; + + // Validate the return type + match if let Some(return_type) = function_info.prototype.signature.return_type() { + crate::reflection::equals_return_type::(return_type) + } else if <() as ReturnTypeReflection>::type_guid() != ReturnType::type_guid() { + Err(( + <() as ReturnTypeReflection>::type_name(), + ReturnType::type_name(), + )) + } else { + Ok(()) + } { + Ok(_) => {} + Err((expected, found)) => { + return Err(InvokeErr { + msg: format!( + "invalid return type. Expected: {}. Found: {}", + expected, found, + ), + function_name, + arguments, + }) + } + } + + let result: ReturnType::MunType = unsafe { arguments.invoke(function_info.fn_ptr) }; + Ok(Marshal::marshal_from(result, self)) + } } diff --git a/crates/mun_runtime/src/macros.rs b/crates/mun_runtime/src/macros.rs deleted file mode 100644 index d2c55cc0a..000000000 --- a/crates/mun_runtime/src/macros.rs +++ /dev/null @@ -1,261 +0,0 @@ -#[doc(hidden)] -#[macro_export(local_inner_macros)] -macro_rules! count_args { - () => { 0 }; - ($name:ident) => { 1 }; - ($first:ident, $($rest:ident),*) => { - 1 + count_args!($($rest),*) - } -} - -macro_rules! invoke_fn_impl { - ($( - fn $FnName:ident($($Arg:tt: $T:ident),*) -> $ErrName:ident; - )+) => { - $( - /// An invocation error that contains the function name, a mutable reference to the - /// runtime, passed arguments, and the output type. This allows the caller to retry - /// the function invocation using the `Retriable` trait. - pub struct $ErrName<'i, 's, $($T: ArgumentReflection + Marshal<'i>,)*> { - msg: String, - function_name: &'s str, - $($Arg: $T,)* - input: core::marker::PhantomData<&'i ()>, - } - - impl<'i, 's, $($T: ArgumentReflection + Marshal<'i>,)*> core::fmt::Debug for $ErrName<'i, 's, $($T,)*> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", &self.msg) - } - } - - impl<'i, 's, $($T: ArgumentReflection + Marshal<'i>,)*> core::fmt::Display for $ErrName<'i, 's, $($T,)*> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{}", &self.msg) - } - } - - impl<'i, 's, $($T: ArgumentReflection + Marshal<'i>,)*> std::error::Error for $ErrName<'i, 's, $($T,)*> { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - None - } - } - - impl<'i, 's, $($T: ArgumentReflection + Marshal<'i>,)*> $ErrName<'i, 's, $($T,)*> { - /// Constructs a new invocation error. - #[allow(clippy::too_many_arguments)] - pub fn new(err_msg: String, function_name: &'s str, $($Arg: $T),*) -> Self { - Self { - msg: err_msg, - function_name, - $($Arg,)* - input: core::marker::PhantomData, - } - } - - /// Retries a function invocation once, resulting in a potentially successful - /// invocation. - // FIXME: `unwrap_or_else` does not compile for `StructRef`, due to - // https://doc.rust-lang.org/nomicon/lifetime-mismatch.html#improperly-reduced-borrows - pub fn retry<'r, 'o, Output>(self, runtime: &'r mut Runtime) -> Result - where - Output: 'o + ReturnTypeReflection + Marshal<'o>, - 'r: 'o, - { - // Safety: The output of `retry_impl` is guaranteed to only contain a shared - // reference. - unsafe { self.retry_impl(runtime) } - } - - /// Retries the function invocation until it succeeds, resulting in an output. - // FIXME: `unwrap_or_else` does not compile for `StructRef`, due to - // https://doc.rust-lang.org/nomicon/lifetime-mismatch.html#improperly-reduced-borrows - pub fn wait<'r, 'o, Output>(mut self, runtime: &'r mut Runtime) -> Output - where - Output: 'o + ReturnTypeReflection + Marshal<'o>, - 'r: 'o, - { - // Safety: The output of `retry_impl` is guaranteed to only contain a shared - // reference. - let runtime = &*runtime; - - loop { - self = match unsafe { self.retry_impl(runtime) } { - Ok(output) => return output, - Err(e) => e, - }; - } - } - - /// Inner implementation that retries a function invocation once, resulting in a - /// potentially successful invocation. This is a workaround for: - /// https://doc.rust-lang.org/nomicon/lifetime-mismatch.html - /// - /// # Safety - /// - /// When calling this function, you have to guarantee that `runtime` is mutably - /// borrowed. The `Output` value can only contain a shared borrow of `runtime`. - unsafe fn retry_impl<'r, 'o, Output>(self, runtime: &'r Runtime) -> Result - where - Output: 'o + ReturnTypeReflection + Marshal<'o>, - 'r: 'o, - { - #[allow(clippy::cast_ref_to_mut)] - let runtime = &mut *(runtime as *const Runtime as *mut Runtime); - - eprintln!("{}", self.msg); - while !runtime.update() { - // Wait until there has been an update that might fix the error - } - $crate::Runtime::$FnName(runtime, self.function_name, $(self.$Arg,)*) - } - } - - impl Runtime { - /// Invokes the method `method_name` with arguments `args`, in the library compiled - /// based on the manifest at `manifest_path`. - /// - /// If an error occurs when invoking the method, an error message is logged. The - /// runtime continues looping until the cause of the error has been resolved. - #[allow(clippy::too_many_arguments, unused_assignments)] - pub fn $FnName<'i, 'o, 'r, 's, $($T: ArgumentReflection + Marshal<'i>,)* Output: 'o + ReturnTypeReflection + Marshal<'o>>( - runtime: &'r Runtime, - function_name: &'s str, - $($Arg: $T,)* - ) -> core::result::Result> - where - 'r: 'o, - { - match runtime - .get_function_definition(function_name) - .ok_or_else(|| format!("Failed to obtain function '{}'", function_name)) - .and_then(|function_info| { - // Validate function signature - let num_args = $crate::count_args!($($T),*); - - let arg_types = function_info.prototype.signature.arg_types(); - if arg_types.len() != num_args { - return Err(format!( - "Invalid number of arguments. Expected: {}. Found: {}.", - arg_types.len(), - num_args, - )); - } - - #[allow(unused_mut, unused_variables)] - let mut idx = 0; - $( - crate::reflection::equals_argument_type(runtime, &arg_types[idx], &$Arg) - .map_err(|(expected, found)| { - format!( - "Invalid argument type at index {}. Expected: {}. Found: {}.", - idx, - expected, - found, - ) - })?; - idx += 1; - )* - - if let Some(return_type) = function_info.prototype.signature.return_type() { - crate::reflection::equals_return_type::(return_type) - } else if <() as ReturnTypeReflection>::type_guid() != Output::type_guid() { - Err((<() as ReturnTypeReflection>::type_name(), Output::type_name())) - } else { - Ok(()) - }.map_err(|(expected, found)| { - format!( - "Invalid return type. Expected: {}. Found: {}", - expected, - found, - ) - })?; - - Ok(function_info) - }) { - Ok(function_info) => { - let function: fn($($T::MunType),*) -> Output::MunType = unsafe { - core::mem::transmute(function_info.fn_ptr) - }; - let result = function($($Arg.marshal_into()),*); - - // Marshall the result - return Ok(Marshal::marshal_from(result, runtime)) - } - Err(e) => Err($ErrName::new(e, function_name, $($Arg),*)) - } - } - } - )+ - } -} - -/// Invokes a runtime function and returns a [`Result`] that contains either the output value or -/// an error that can be used to retry the function invocation. -/// -/// The first argument `invoke_fn` receives is a `Ref` and the second argument is a -/// function string. This must be a `&str`. -/// -/// Additional parameters passed to `invoke_fn` are the arguments of the function in the order -/// given. -#[macro_export] -macro_rules! invoke_fn { - ($Runtime:expr, $FnName:expr) => { - $crate::Runtime::invoke_fn0(&$Runtime, $FnName) - }; - ($Runtime:expr, $FnName:expr, $A:expr) => { - $crate::Runtime::invoke_fn1(&$Runtime, $FnName, $A) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr) => { - $crate::Runtime::invoke_fn2(&$Runtime, $FnName, $A, $B) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr) => { - $crate::Runtime::invoke_fn3(&$Runtime, $FnName, $A, $B, $C) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr) => { - $crate::Runtime::invoke_fn4(&$Runtime, $FnName, $A, $B, $C, $D) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr) => { - $crate::Runtime::invoke_fn5(&$Runtime, $FnName, $A, $B, $C, $D, $E) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr) => { - $crate::Runtime::invoke_fn6(&$Runtime, $FnName, $A, $B, $C, $D, $E, $F) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr) => { - $crate::Runtime::invoke_fn7(&$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr, $H:expr) => { - $crate::Runtime::invoke_fn8(&$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G, $H) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr, $H:expr, $I:expr) => { - $crate::Runtime::invoke_fn9(&$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I) - }; - ($Runtime:expr, $FnName:expr, $A:expr, $B:expr, $C:expr, $D:expr, $E:expr, $F:expr, $G:expr, $H:expr, $I:expr, $J:expr) => { - $crate::Runtime::invoke_fn10(&$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J) - }; - ($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) => { - $crate::Runtime::invoke_fn11( - &$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, - ) - }; - ($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) => { - $crate::Runtime::invoke_fn12( - &$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, - ) - }; - ($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, $M:expr) => { - $crate::Runtime::invoke_fn13( - &$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, - ) - }; - ($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, $M:expr, $N:expr) => { - $crate::Runtime::invoke_fn14( - &$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, $N, - ) - }; - ($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, $M:expr, $N:expr, $O:expr) => { - $crate::Runtime::invoke_fn15( - &$Runtime, $FnName, $A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, $N, $O, - ) - }; -} diff --git a/crates/mun_runtime/tests/marshalling.rs b/crates/mun_runtime/tests/marshalling.rs index 5bf8b6096..f2a78f61a 100644 --- a/crates/mun_runtime/tests/marshalling.rs +++ b/crates/mun_runtime/tests/marshalling.rs @@ -1,4 +1,4 @@ -use mun_runtime::{invoke_fn, ArgumentReflection, Marshal, ReturnTypeReflection, StructRef}; +use mun_runtime::{ArgumentReflection, Marshal, ReturnTypeReflection, StructRef}; use mun_test::CompileAndRunTestDriver; @@ -317,7 +317,7 @@ fn fields() { ) .expect("Failed to build test driver"); - assert_invoke_eq!(bool, true, driver, "main", 48); + assert_invoke_eq!(bool, true, driver, "main", 48i32); } #[test] @@ -335,7 +335,7 @@ fn field_crash() { ) .expect("Failed to build test driver"); - assert_invoke_eq!(i32, 15, driver, "main", 10); + assert_invoke_eq!(i32, 15, driver, "main", 10i32); } #[test] @@ -396,11 +396,15 @@ fn marshal_struct() { let bool_data = TestData(true, false); // Verify that struct marshalling works for fundamental types - let mut foo: StructRef = invoke_fn!(runtime_ref, "foo_new", int_data.0, bool_data.0).unwrap(); + let mut foo: StructRef = runtime_ref + .invoke("foo_new", (int_data.0, bool_data.0)) + .unwrap(); test_field(&mut foo, &int_data, "a"); test_field(&mut foo, &bool_data, "b"); - let mut bar: StructRef = invoke_fn!(runtime_ref, "bar_new", int_data.0, bool_data.0).unwrap(); + let mut bar: StructRef = runtime_ref + .invoke("bar_new", (int_data.0, bool_data.0)) + .unwrap(); test_field(&mut bar, &int_data, "0"); test_field(&mut bar, &bool_data, "1"); @@ -431,26 +435,39 @@ fn marshal_struct() { } // Verify that struct marshalling works for struct types - let mut baz: StructRef = invoke_fn!(runtime_ref, "baz_new", foo).unwrap(); - let c1: StructRef = invoke_fn!(runtime_ref, "foo_new", int_data.0, bool_data.0).unwrap(); - let c2: StructRef = invoke_fn!(runtime_ref, "foo_new", int_data.1, bool_data.1).unwrap(); + let mut baz: StructRef = runtime_ref.invoke("baz_new", (foo,)).unwrap(); + let c1: StructRef = runtime_ref + .invoke("foo_new", (int_data.0, bool_data.0)) + .unwrap(); + let c2: StructRef = runtime_ref + .invoke("foo_new", (int_data.1, bool_data.1)) + .unwrap(); test_struct(&mut baz, c1, c2); - let mut qux: StructRef = invoke_fn!(runtime_ref, "qux_new", bar).unwrap(); - let c1: StructRef = invoke_fn!(runtime_ref, "bar_new", int_data.0, bool_data.0).unwrap(); - let c2: StructRef = invoke_fn!(runtime_ref, "bar_new", int_data.1, bool_data.1).unwrap(); + let mut qux: StructRef = runtime_ref.invoke("qux_new", (bar,)).unwrap(); + let c1: StructRef = runtime_ref + .invoke("bar_new", (int_data.0, bool_data.0)) + .unwrap(); + let c2: StructRef = runtime_ref + .invoke("bar_new", (int_data.1, bool_data.1)) + .unwrap(); test_struct(&mut qux, c1, c2); // Verify the dispatch table works when a marshallable wrapper function exists alongside the // original function. - let mut baz2: StructRef = - invoke_fn!(runtime_ref, "baz_new_transitive", int_data.0, bool_data.0).unwrap(); + let mut baz2: StructRef = runtime_ref + .invoke("baz_new_transitive", (int_data.0, bool_data.0)) + .unwrap(); // TODO: Find an ergonomic solution for this: // .unwrap_or_else(|e| e.wait(&mut runtime_ref)); let runtime_ref = runtime.borrow(); - let c1: StructRef = invoke_fn!(runtime_ref, "foo_new", int_data.0, bool_data.0).unwrap(); - let c2: StructRef = invoke_fn!(runtime_ref, "foo_new", int_data.1, bool_data.1).unwrap(); + let c1: StructRef = runtime_ref + .invoke("foo_new", (int_data.0, bool_data.0)) + .unwrap(); + let c2: StructRef = runtime_ref + .invoke("foo_new", (int_data.1, bool_data.1)) + .unwrap(); test_struct(&mut baz2, c1, c2); fn test_shallow_copy< @@ -524,11 +541,11 @@ fn marshal_struct() { assert!(bar_err.is_err()); // Specify invalid return type - let bar_err: Result = invoke_fn!(runtime_ref, "baz_new", foo); + let bar_err: Result = runtime_ref.invoke("baz_new", (foo,)); assert!(bar_err.is_err()); // Pass invalid struct type - let bar_err: Result = invoke_fn!(runtime_ref, "baz_new", bar); + let bar_err: Result = runtime_ref.invoke("baz_new", (bar,)); assert!(bar_err.is_err()); } @@ -656,23 +673,14 @@ fn test_primitive_types() { let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); - let mut foo: StructRef = invoke_fn!( - runtime_ref, - "new_primitives", - 1u8, - 2u16, - 3u32, - 4u64, - 5u128, - 6i8, - 7i16, - 8i32, - 9i64, - 10i128, - 11.0f32, - 12.0f64 - ) - .unwrap(); + let mut foo: StructRef = runtime_ref + .invoke( + "new_primitives", + ( + 1u8, 2u16, 3u32, 4u64, 5u128, 6i8, 7i16, 8i32, 9i64, 10i128, 11.0f32, 12.0f64, + ), + ) + .unwrap(); test_field(&mut foo, (1u8, 100u8), "a"); test_field(&mut foo, (2u16, 101u16), "b"); @@ -706,7 +714,7 @@ fn can_add_external_without_return() { let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); - let _: () = invoke_fn!(runtime_ref, "main").unwrap(); + let _: () = runtime_ref.invoke("main", ()).unwrap(); } #[test] diff --git a/crates/mun_runtime/tests/memory.rs b/crates/mun_runtime/tests/memory.rs index faecf1e72..8241e22c3 100644 --- a/crates/mun_runtime/tests/memory.rs +++ b/crates/mun_runtime/tests/memory.rs @@ -1,4 +1,4 @@ -use mun_runtime::{invoke_fn, StructRef}; +use mun_runtime::StructRef; use mun_test::CompileAndRunTestDriver; #[macro_use] @@ -33,7 +33,7 @@ fn gc_trace() { let runtime = driver.runtime(); let runtime_ref = runtime.borrow(); - let value: StructRef = invoke_fn!(runtime_ref, "new_foo").unwrap(); + let value: StructRef = runtime_ref.invoke("new_foo", ()).unwrap(); let value = value.root(driver.runtime()); assert_eq!(runtime_ref.gc_collect(), false); @@ -67,7 +67,7 @@ fn map_struct_insert_field1() { let b = 5i64; let c = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", b, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (b, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -108,7 +108,7 @@ fn map_struct_insert_field2() { let a = 5i64; let c = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -149,7 +149,7 @@ fn map_struct_insert_field3() { let a = 5i64; let b = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -192,7 +192,7 @@ fn map_struct_remove_field1() { let a = 1.0f64; let b = 3.0f64; let c = 5i64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -231,7 +231,7 @@ fn map_struct_remove_field2() { let a = 1.0f64; let b = 5i64; let c = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -270,7 +270,7 @@ fn map_struct_remove_field3() { let a = 5i64; let b = 1.0f64; let c = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -313,7 +313,7 @@ fn map_struct_cast_fields1() { let c = 3u32; let d = -4i64; let e = 3.14f32; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c, d, e).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c, d, e)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -356,7 +356,7 @@ fn map_struct_cast_fields2() { let runtime_ref = runtime.borrow(); let a = -2i16; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a,)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -396,7 +396,7 @@ fn map_struct_swap_fields1() { let a = 1.0f64; let b = 3i64; let c = 5.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -441,7 +441,7 @@ fn map_struct_swap_fields2() { let b = 3i64; let c = 5.0f64; let d = 7i64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c, d).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c, d)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -486,7 +486,7 @@ fn map_struct_rename_field1() { let a = 5i64; let b = 1.0f64; let c = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -529,7 +529,7 @@ fn map_struct_rename_field2() { let a = 5i64; let b = 1.0f64; let c = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -574,7 +574,7 @@ fn map_struct_all() { let b = 1.0f64; let c = 3.0f64; let d = -1i32; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c, d).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c, d)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -620,7 +620,7 @@ fn delete_used_struct() { let a = 5i64; let b = 1.0f64; let c = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, b, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, b, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( @@ -685,24 +685,19 @@ fn nested_structs() { let a = -3.14f32; let b = 6.18f32; - let gc_struct: StructRef = invoke_fn!(runtime_ref, "new_gc_struct", a, b).unwrap(); - let value_struct: StructRef = invoke_fn!(runtime_ref, "new_value_struct", a, b).unwrap(); + let gc_struct: StructRef = runtime_ref.invoke("new_gc_struct", (a, b)).unwrap(); + let value_struct: StructRef = runtime_ref.invoke("new_value_struct", (a, b)).unwrap(); - let gc_wrapper: StructRef = invoke_fn!( - runtime_ref, - "new_gc_wrapper", - gc_struct.clone(), - value_struct.clone() - ) - .unwrap(); + let gc_wrapper: StructRef = runtime_ref + .invoke("new_gc_wrapper", (gc_struct.clone(), value_struct.clone())) + .unwrap(); - let value_wrapper: StructRef = invoke_fn!( - runtime_ref, - "new_value_wrapper", - gc_struct.clone(), - value_struct.clone() - ) - .unwrap(); + let value_wrapper: StructRef = runtime_ref + .invoke( + "new_value_wrapper", + (gc_struct.clone(), value_struct.clone()), + ) + .unwrap(); let gc_wrapper = gc_wrapper.root(driver.runtime()); let value_wrapper = value_wrapper.root(driver.runtime()); @@ -903,7 +898,7 @@ fn insert_struct() { let a = 5i64; let c = 3.0f64; - let foo: StructRef = invoke_fn!(runtime_ref, "foo_new", a, c).unwrap(); + let foo: StructRef = runtime_ref.invoke("foo_new", (a, c)).unwrap(); let foo = foo.root(driver.runtime()); driver.update( diff --git a/crates/mun_runtime/tests/runtime.rs b/crates/mun_runtime/tests/runtime.rs index 853264682..57b3a39ca 100644 --- a/crates/mun_runtime/tests/runtime.rs +++ b/crates/mun_runtime/tests/runtime.rs @@ -4,6 +4,24 @@ use std::io; #[macro_use] mod util; +#[test] +fn invoke() { + let driver = CompileAndRunTestDriver::new( + r#" + pub fn sum(a: i32, b: i32) -> i32 { a + b } + "#, + |builder| builder, + ) + .expect("Failed to build test driver"); + + let result: i32 = driver + .runtime() + .borrow() + .invoke("sum", (123i32, 456i32)) + .unwrap(); + assert_eq!(123 + 456, result); +} + #[test] fn multiple_modules() { let driver = CompileAndRunTestDriver::from_fixture( diff --git a/crates/mun_runtime/tests/util.rs b/crates/mun_runtime/tests/util.rs index ed69fc164..f5d720df8 100644 --- a/crates/mun_runtime/tests/util.rs +++ b/crates/mun_runtime/tests/util.rs @@ -1,16 +1,19 @@ #![allow(unused_macros)] macro_rules! assert_invoke_eq { - ($ExpectedType:ty, $ExpectedResult:expr, $Driver:expr, $($Arg:tt)+) => { + ($ExpectedType:ty, $ExpectedResult:expr, $Driver:expr, $Name:expr, $($Arg:expr),*) => { { let runtime = $Driver.runtime(); let runtime_ref = runtime.borrow(); - let result: $ExpectedType = mun_runtime::invoke_fn!(runtime_ref, $($Arg)*).unwrap(); + let result: $ExpectedType = runtime_ref.invoke($Name, ( $($Arg,)*) ).unwrap(); assert_eq!( result, $ExpectedResult, "{} == {:?}", stringify!(mun_runtime::invoke_fn!(runtime_ref, $($Arg)*).unwrap()), $ExpectedResult ); } + }; + ($ExpectedType:ty, $ExpectedResult:expr, $Driver:expr, $Name:expr) => { + assert_invoke_eq!($ExpectedType, $ExpectedResult, $Driver, $Name, ) } } diff --git a/crates/mun_skeptic/src/runtime.rs b/crates/mun_skeptic/src/runtime.rs index 8bc4374fd..8c99531f2 100644 --- a/crates/mun_skeptic/src/runtime.rs +++ b/crates/mun_skeptic/src/runtime.rs @@ -1,7 +1,7 @@ //! Code to perform tests on Mun code. use mun_compiler::{Config, DisplayColor, PathOrInline, RelativePathBuf}; -use mun_runtime::{invoke_fn, RuntimeBuilder}; +use mun_runtime::RuntimeBuilder; /// The type of test to create #[derive(Copy, Clone)] @@ -82,5 +82,8 @@ pub fn run_test(code: &str, mode: TestMode) { } // Call the main function - let _: () = invoke_fn!(runtime.borrow_mut(), "main").expect("error calling main function"); + let _: () = runtime + .borrow_mut() + .invoke("main", ()) + .expect("error calling main function"); }