From dbfd3df810cdfee0a09095f18f93a3e0b4b593f3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 4 Sep 2020 11:57:40 +0800 Subject: [PATCH] Support multiple names in rhai_fn. --- codegen/src/function.rs | 44 ++++++++++++----------- codegen/src/rhai_module.rs | 74 +++++++++++++++++++++----------------- tests/plugins.rs | 4 ++- 3 files changed, 68 insertions(+), 54 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 3470e691..aa4001f5 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -19,7 +19,7 @@ use crate::attrs::{ExportInfo, ExportScope, ExportedParams}; #[derive(Debug, Default)] pub(crate) struct ExportedFnParams { - pub name: Option, + pub name: Option>, pub return_raw: bool, pub skip: bool, pub span: Option, @@ -55,11 +55,12 @@ impl ExportedParams for ExportedFnParams { Default::default() } - fn from_info( - info: crate::attrs::ExportInfo, - ) -> syn::Result { - let ExportInfo { item_span: span, items: attrs } = info; - let mut name = None; + fn from_info(info: crate::attrs::ExportInfo) -> syn::Result { + let ExportInfo { + item_span: span, + items: attrs, + } = info; + let mut name = Vec::new(); let mut return_raw = false; let mut skip = false; for attr in attrs { @@ -73,15 +74,15 @@ impl ExportedParams for ExportedFnParams { "Rhai function names may not contain dot", )); } - name = Some(s.value()) + name.push(s.value()) } - ("get", Some(s)) => name = Some(make_getter(&s.value())), - ("set", Some(s)) => name = Some(make_setter(&s.value())), + ("get", Some(s)) => name.push(make_getter(&s.value())), + ("set", Some(s)) => name.push(make_setter(&s.value())), ("get", None) | ("set", None) | ("name", None) => { return Err(syn::Error::new(key.span(), "requires value")) } - ("index_get", None) => name = Some(FN_IDX_GET.to_string()), - ("index_set", None) => name = Some(FN_IDX_SET.to_string()), + ("index_get", None) => name.push(FN_IDX_GET.to_string()), + ("index_set", None) => name.push(FN_IDX_SET.to_string()), ("return_raw", None) => return_raw = true, ("index_get", Some(s)) | ("index_set", Some(s)) | ("return_raw", Some(s)) => { return Err(syn::Error::new(s.span(), "extraneous value")) @@ -98,7 +99,7 @@ impl ExportedParams for ExportedFnParams { } Ok(ExportedFnParams { - name, + name: if name.is_empty() { None } else { Some(name) }, return_raw, skip, span: Some(span), @@ -260,7 +261,7 @@ impl ExportedFn { pub(crate) fn exported_name<'n>(&'n self) -> Cow<'n, str> { if let Some(ref name) = self.params.name { - Cow::Borrowed(name.as_str()) + Cow::Borrowed(name.last().unwrap().as_str()) } else { Cow::Owned(self.signature.ident.to_string()) } @@ -346,7 +347,9 @@ impl ExportedFn { }) .collect(); - let return_span = self.return_type().map(|r| r.span()) + let return_span = self + .return_type() + .map(|r| r.span()) .unwrap_or_else(|| proc_macro2::Span::call_site()); if !self.params.return_raw { quote_spanned! { return_span=> @@ -393,11 +396,10 @@ impl ExportedFn { pub fn generate_impl(&self, on_type_name: &str) -> proc_macro2::TokenStream { let sig_name = self.name().clone(); - let name = self - .params - .name - .clone() - .unwrap_or_else(|| self.name().to_string()); + let name = self.params.name.as_ref().map_or_else( + || self.name().to_string(), + |names| names.last().unwrap().clone(), + ); let arg_count = self.arg_count(); let is_method_call = self.mutable_receiver(); @@ -518,7 +520,9 @@ impl ExportedFn { // Handle "raw returns", aka cases where the result is a dynamic or an error. // // This allows skipping the Dynamic::from wrap. - let return_span = self.return_type().map(|r| r.span()) + let return_span = self + .return_type() + .map(|r| r.span()) .unwrap_or_else(|| proc_macro2::Span::call_site()); let return_expr = if !self.params.return_raw { quote_spanned! { return_span=> diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 40485adb..57ec78a8 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -67,12 +67,12 @@ pub(crate) fn generate_body( &format!("{}_token", function.name().to_string()), function.name().span(), ); - let reg_name = function + let reg_names = function .params() .name .clone() - .unwrap_or_else(|| function.name().to_string()); - let fn_literal = syn::LitStr::new(®_name, proc_macro2::Span::call_site()); + .unwrap_or_else(|| vec![function.name().to_string()]); + let fn_input_types: Vec = function .arg_list() .map(|fnarg| match fnarg { @@ -110,13 +110,17 @@ pub(crate) fn generate_body( }) .collect(); - set_fn_stmts.push( - syn::parse2::(quote! { - m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*], - CallableFunction::from_plugin(#fn_token_name())); - }) - .unwrap(), - ); + for reg_name in reg_names { + let fn_literal = syn::LitStr::new(®_name, proc_macro2::Span::call_site()); + + set_fn_stmts.push( + syn::parse2::(quote! { + m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*], + CallableFunction::from_plugin(#fn_token_name())); + }) + .unwrap(), + ); + } gen_fn_tokens.push(quote! { #[allow(non_camel_case_types)] @@ -155,29 +159,33 @@ pub(crate) fn check_rename_collisions(fns: &Vec) -> Result<(), syn:: let mut renames = HashMap::::new(); let mut names = HashMap::::new(); for itemfn in fns.iter() { - if let Some(ref name) = itemfn.params().name { - let current_span = itemfn.params().span.as_ref().unwrap(); - let key = itemfn.arg_list().fold(name.clone(), |mut argstr, fnarg| { - let type_string: String = match fnarg { - syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"), - syn::FnArg::Typed(syn::PatType { ref ty, .. }) => { - ty.as_ref().to_token_stream().to_string() - } - }; - argstr.push('.'); - argstr.push_str(&type_string); - argstr - }); - if let Some(other_span) = renames.insert(key, *current_span) { - let mut err = syn::Error::new( - *current_span, - format!("duplicate Rhai signature for '{}'", &name), - ); - err.combine(syn::Error::new( - other_span, - format!("duplicated function renamed '{}'", &name), - )); - return Err(err); + if let Some(ref names) = itemfn.params().name { + for name in names { + let current_span = itemfn.params().span.as_ref().unwrap(); + let key = itemfn.arg_list().fold(name.clone(), |mut argstr, fnarg| { + let type_string: String = match fnarg { + syn::FnArg::Receiver(_) => { + unimplemented!("receiver rhai_fns not implemented") + } + syn::FnArg::Typed(syn::PatType { ref ty, .. }) => { + ty.as_ref().to_token_stream().to_string() + } + }; + argstr.push('.'); + argstr.push_str(&type_string); + argstr + }); + if let Some(other_span) = renames.insert(key, *current_span) { + let mut err = syn::Error::new( + *current_span, + format!("duplicate Rhai signature for '{}'", &name), + ); + err.combine(syn::Error::new( + other_span, + format!("duplicated function renamed '{}'", &name), + )); + return Err(err); + } } } else { let ident = itemfn.name(); diff --git a/tests/plugins.rs b/tests/plugins.rs index 5310c79e..cac8718f 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -21,7 +21,7 @@ mod test { } } - #[rhai_fn(name = "test")] + #[rhai_fn(name = "test", name = "hi")] #[inline(always)] pub fn len(array: &mut Array, mul: INT) -> INT { (array.len() as INT) * mul @@ -74,6 +74,8 @@ fn test_plugins_package() -> Result<(), Box> { #[cfg(not(feature = "no_object"))] assert_eq!(engine.eval::("let a = [1, 2, 3]; a.foo")?, 1); + assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); + assert_eq!(engine.eval::("let a = [1, 2, 3]; hi(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); assert_eq!(engine.eval::("2 + 2")?, 5); assert_eq!(