]> git.proxmox.com Git - perlmod.git/commitdiff
add 'raw_return' function attribute
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Tue, 24 Nov 2020 08:36:50 +0000 (09:36 +0100)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Tue, 24 Nov 2020 08:37:59 +0000 (09:37 +0100)
A function declared with a raw_return attribute like this:

    #[export(raw_return)]
    fn foo() -> Result<Value, Error>;

will not perform serialization on the 'Value', but return
the "raw" value to perl. This allows returning blessed
values.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
perlmod-macro/src/attribs.rs
perlmod-macro/src/function.rs
perlmod-macro/src/module.rs
perlmod-test/src/bless.rs [new file with mode: 0644]
perlmod-test/src/lib.rs
perlmod/src/value.rs

index 03fb5410c3dfb07fe92e2864134cfe772f92b153..34b55e0de27fe474d474c0c34b21f5528e40d182 100644 (file)
@@ -66,6 +66,7 @@ impl ModuleAttrs {
 
 pub struct FunctionAttrs {
     pub xs_name: Option<Ident>,
+    pub raw_return: bool,
 }
 
 impl TryFrom<AttributeArgs> for FunctionAttrs {
@@ -73,6 +74,7 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
 
     fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> {
         let mut xs_name = None;
+        let mut raw_return = false;
 
         for arg in args {
             match arg {
@@ -87,10 +89,20 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
                         bail!(path => "unknown argument");
                     }
                 }
+                syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
+                    if path.is_ident("raw_return") {
+                        raw_return = true;
+                    } else {
+                        bail!(path => "unknown attribute");
+                    }
+                }
                 _ => bail!(Span::call_site(), "unexpected attribute argument"),
             }
         }
 
-        Ok(Self { xs_name })
+        Ok(Self {
+            xs_name,
+            raw_return,
+        })
     }
 }
index 899a48c86d56dd62c602992b63de4d1c9c827cf4..1007854b9d9bb08bf831bfe34f7ffa970868a5a5 100644 (file)
@@ -1,6 +1,6 @@
 use anyhow::Error;
 
-use proc_macro2::{Ident, TokenStream, Span};
+use proc_macro2::{Ident, Span, TokenStream};
 
 use quote::quote;
 
@@ -17,13 +17,6 @@ pub fn handle_function(
     func: syn::ItemFn,
     mangled_package_name: Option<&str>,
 ) -> Result<XSub, Error> {
-    //let vis = core::mem::replace(&mut func.vis, syn::Visibility::Inherited);
-    //if let syn::Visibility::Public(_) = vis {
-    //    // ok
-    //} else {
-    //    bail!(func.sig.fn_token => "only public functions can be exported as xsubs");
-    //}
-
     let sig = &func.sig;
     if !sig.generics.params.is_empty() {
         bail!(&sig.generics => "generic functions cannot be exported as xsubs");
@@ -34,12 +27,10 @@ pub fn handle_function(
     }
 
     let name = &sig.ident;
-    let xs_name = attr
-        .xs_name
-        .unwrap_or_else(|| match mangled_package_name {
-            None => Ident::new(&format!("xs_{}", name), name.span()),
-            Some(prefix) => Ident::new(&format!("xs_{}_{}", prefix, name), name.span()),
-        });
+    let xs_name = attr.xs_name.unwrap_or_else(|| match mangled_package_name {
+        None => Ident::new(&format!("xs_{}", name), name.span()),
+        Some(prefix) => Ident::new(&format!("xs_{}_{}", prefix, name), name.span()),
+    });
     let impl_xs_name = Ident::new(&format!("impl_xs_{}", name), name.span());
 
     let mut extract_arguments = TokenStream::new();
@@ -102,10 +93,29 @@ pub fn handle_function(
     }
 
     let too_many_args_error = syn::LitStr::new(
-        &format!("too many parameters for function '{}', (expected {})", name, sig.inputs.len()),
+        &format!(
+            "too many parameters for function '{}', (expected {})",
+            name,
+            sig.inputs.len()
+        ),
         Span::call_site(),
     );
 
+    let handle_return = if attr.raw_return {
+        quote! {
+            Ok(result.into_mortal().into_raw())
+        }
+    } else {
+        quote! {
+            match ::perlmod::to_value(&result) {
+                Ok(value) => Ok(value.into_mortal().into_raw()),
+                Err(err) => Err(::perlmod::Value::new_string(&err.to_string())
+                    .into_mortal()
+                    .into_raw()),
+            }
+        }
+    };
+
     let tokens = quote! {
         #func
 
@@ -151,12 +161,7 @@ pub fn handle_function(
                 }
             };
 
-            match ::perlmod::to_value(&result) {
-                Ok(value) => Ok(value.into_mortal().into_raw()),
-                Err(err) => Err(::perlmod::Value::new_string(&err.to_string())
-                    .into_mortal()
-                    .into_raw()),
-            }
+            #handle_return
         }
     };
 
index 3ad37d03846dea3092ac4d3d9620bfc36271012f..bb4bb902dd202388b91aee5c254b685dd3cdfd57 100644 (file)
@@ -6,7 +6,6 @@ use anyhow::Error;
 use proc_macro2::TokenStream;
 
 use quote::quote;
-use syn::parse::Parser;
 use syn::punctuated::Punctuated;
 use syn::{AttributeArgs, Token};
 
diff --git a/perlmod-test/src/bless.rs b/perlmod-test/src/bless.rs
new file mode 100644 (file)
index 0000000..cbdd5f4
--- /dev/null
@@ -0,0 +1,15 @@
+#[perlmod::package(name = "RSPM::Bless", lib = "perlmod_test")]
+mod export {
+    use anyhow::Error;
+
+    use perlmod::Value;
+
+    #[export(raw_return)]
+    fn new() -> Result<Value, Error> {
+        let hash = Value::from(perlmod::hash::Hash::new());
+        let hash = Value::new_ref(&hash);
+        let hash = hash.bless("RSPM::Bless")?;
+        Ok(hash)
+        //Ok(this.bless("RSPM::Bless")?)
+    }
+}
index 637c209714d29ba7a49abff370fccafec93e4bbb..a10c2e894e9dd62b08c161008af71d4ce818ee02 100644 (file)
@@ -9,3 +9,6 @@ mod pkginline;
 
 /// This is possible on stable rust with some 1.3x already.
 mod pkgstable;
+
+/// A test for blessed values.
+mod bless;
index ea26ce55c14de988efbe339bbc7a91d917a36072..53880e25dd05dd292347287a6c9adb161b7d84e9 100644 (file)
@@ -59,26 +59,15 @@ impl Value {
         Value::Reference(unsafe { Scalar::from_raw_move(ffi::RSPL_newRV_inc(value.sv())) })
     }
 
-    /// Bless a value into a package. This turns the value into a reference and forwards to
-    /// [`Value::bless_ref`].
-    pub fn bless_value(&self, package: &str) -> Result<Value, Error> {
-        self.clone_ref().bless_ref(package)
-    }
-
     /// Bless a reference into a package. The `Value` must be a reference.
-    pub fn bless_ref(&self, package: &str) -> Result<Value, Error> {
-        let value = match self {
-            Value::Reference(v) => v,
-            _ => Error::fail("trying to bless a non-reference")?,
-        };
-
+    pub fn bless(&self, package: &str) -> Result<Value, Error> {
         let pkgsv = Scalar::new_string(package);
         let stash = unsafe { ffi::RSPL_gv_stashsv(pkgsv.sv(), 0) };
         if stash.is_null() {
             return Err(Error(format!("failed to find package {:?}", package)));
         }
 
-        let value = unsafe { ffi::RSPL_sv_bless(value.sv(), stash) };
+        let value = unsafe { ffi::RSPL_sv_bless(self.sv(), stash) };
         if value.is_null() {
             return Err(Error(format!(
                 "failed to bless value into package {:?}",
@@ -86,7 +75,7 @@ impl Value {
             )));
         }
 
-        Ok(Value::Reference(unsafe { Scalar::from_raw_move(value) }))
+        Ok(Value::Reference(unsafe { Scalar::from_raw_ref(value) }))
     }
 
     /// Take over a raw `SV` value, assuming that we then own a reference to it.