Merge pull request #230 from davidcole1340/function-arg-pass-by-ref

Support function args being passed by reference
This commit is contained in:
Daniil Gentili 2023-10-10 16:13:31 +02:00 committed by GitHub
commit 4dd7b8ded5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 3 deletions

View File

@ -26,6 +26,7 @@ pub struct Arg {
pub ty: String, pub ty: String,
pub nullable: bool, pub nullable: bool,
pub default: Option<String>, pub default: Option<String>,
pub as_ref: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -249,12 +250,19 @@ pub fn get_return_type(output_type: &ReturnType) -> Result<Option<(String, bool)
} }
impl Arg { impl Arg {
pub fn new(name: String, ty: String, nullable: bool, default: Option<String>) -> Self { pub fn new(
name: String,
ty: String,
nullable: bool,
default: Option<String>,
as_ref: bool,
) -> Self {
Self { Self {
name, name,
ty, ty,
nullable, nullable,
default, default,
as_ref,
} }
} }
@ -268,6 +276,7 @@ impl Arg {
match ty { match ty {
Type::Path(TypePath { path, .. }) => { Type::Path(TypePath { path, .. }) => {
let mut path = path.clone(); let mut path = path.clone();
let mut pass_by_ref = false;
path.drop_lifetimes(); path.drop_lifetimes();
let seg = path.segments.last()?; let seg = path.segments.last()?;
@ -283,9 +292,45 @@ impl Arg {
None None
} }
}); });
// For for types that are `Option<&mut T>` to turn them into `Option<&T>`,
// marking the Arg as as "passed by reference".
let option = Some(seg)
.filter(|seg| seg.ident == "Option")
.and_then(|seg| {
if let PathArguments::AngleBracketed(args) = &seg.arguments {
args.args
.iter()
.find(|arg| matches!(arg, GenericArgument::Type(_)))
.map(|ga| {
let new_ga = match ga {
GenericArgument::Type(ty) => {
let _rtype = match ty {
Type::Reference(r) => {
let mut new_ref = r.clone();
new_ref.mutability = None;
pass_by_ref = true;
Type::Reference(new_ref)
}
_ => ty.clone(),
};
GenericArgument::Type(_rtype)
}
_ => ga.clone(),
};
new_ga.to_token_stream().to_string()
})
} else {
None
}
});
let stringified = match result { let stringified = match result {
Some(result) if is_return => result, Some(result) if is_return => result,
_ => path.to_token_stream().to_string(), _ => match option {
Some(result) => result,
None => path.to_token_stream().to_string(),
},
}; };
Some(Arg::new( Some(Arg::new(
@ -293,6 +338,7 @@ impl Arg {
stringified, stringified,
seg.ident == "Option" || default.is_some(), seg.ident == "Option" || default.is_some(),
default, default,
pass_by_ref,
)) ))
} }
Type::Reference(ref_) => { Type::Reference(ref_) => {
@ -302,6 +348,7 @@ impl Arg {
ref_.to_token_stream().to_string(), ref_.to_token_stream().to_string(),
false, false,
default, default,
ref_.mutability.is_some(),
)) ))
} }
_ => None, _ => None,
@ -361,6 +408,7 @@ impl Arg {
let ty = self.get_type_ident(); let ty = self.get_type_ident();
let null = self.nullable.then(|| quote! { .allow_null() }); let null = self.nullable.then(|| quote! { .allow_null() });
let passed_by_ref = self.as_ref.then(|| quote! { .as_ref() });
let default = self.default.as_ref().map(|val| { let default = self.default.as_ref().map(|val| {
quote! { quote! {
.default(#val) .default(#val)
@ -368,7 +416,7 @@ impl Arg {
}); });
quote! { quote! {
::ext_php_rs::args::Arg::new(#name, #ty) #null #default ::ext_php_rs::args::Arg::new(#name, #ty) #null #passed_by_ref #default
} }
} }
} }

View File

@ -45,3 +45,17 @@ pub fn test_bool(input: bool) -> String {
var_dump(test_bool(true)); // string(4) "Yes!" var_dump(test_bool(true)); // string(4) "Yes!"
var_dump(test_bool(false)); // string(3) "No!" var_dump(test_bool(false)); // string(3) "No!"
``` ```
## Rust example, taking by reference
```rust,no_run
# #![cfg_attr(windows, feature(abi_vectorcall))]
# extern crate ext_php_rs;
# use ext_php_rs::prelude::*;
# use ext_php_rs::types;
#[php_function]
pub fn test_bool(input: &mut types::Zval) {
input.reference_mut().unwrap().set_bool(false);
}
# fn main() {}
```