write!(w, "ret").unwrap();
} else if !to_c && self_segs_iter.is_some() && self_segs_iter.unwrap().next().is_none() {
// If we're returning "Self" (and not "Self::X"), just do it manually
- write!(w, "{} {{ inner: Box::into_raw(Box::new(ret)), is_owned: true }}", this_type).unwrap();
+ write!(w, "{} {{ inner: ObjOps::heap_alloc(ret), is_owned: true }}", this_type).unwrap();
} else if to_c {
let new_var = types.write_from_c_conversion_new_var(w, &format_ident!("ret"), rtype, generics);
if new_var {
writeln!(w, "}}\n").unwrap();
writeln!(w, "impl Drop for {} {{\n\tfn drop(&mut self) {{", struct_name).unwrap();
writeln!(w, "\t\tif self.is_owned && !<*mut native{}>::is_null(self.inner) {{", ident).unwrap();
- writeln!(w, "\t\t\tlet _ = unsafe {{ Box::from_raw(self.inner) }};\n\t\t}}\n\t}}\n}}").unwrap();
+ writeln!(w, "\t\t\tlet _ = unsafe {{ Box::from_raw(ObjOps::untweak_ptr(self.inner)) }};\n\t\t}}\n\t}}\n}}").unwrap();
writeln!(w, "/// Frees any resources used by the {}, if is_owned is set and inner is non-NULL.", struct_name).unwrap();
writeln!(w, "#[no_mangle]\npub extern \"C\" fn {}_free(this_obj: {}) {{ }}", struct_name, struct_name).unwrap();
writeln!(w, "#[allow(unused)]").unwrap();
writeln!(w, "extern \"C\" fn {}_free_void(this_ptr: *mut c_void) {{", struct_name).unwrap();
writeln!(w, "\tunsafe {{ let _ = Box::from_raw(this_ptr as *mut native{}); }}\n}}", struct_name).unwrap();
writeln!(w, "#[allow(unused)]").unwrap();
- writeln!(w, "/// When moving out of the pointer, we have to ensure we aren't a reference, this makes that easy").unwrap();
writeln!(w, "impl {} {{", struct_name).unwrap();
+ writeln!(w, "\tpub(crate) fn get_native_ref(&self) -> &'static native{} {{", struct_name).unwrap();
+ writeln!(w, "\t\tunsafe {{ &*ObjOps::untweak_ptr(self.inner) }}").unwrap();
+ writeln!(w, "\t}}").unwrap();
+ writeln!(w, "\tpub(crate) fn get_native_mut_ref(&self) -> &'static mut native{} {{", struct_name).unwrap();
+ writeln!(w, "\t\tunsafe {{ &mut *ObjOps::untweak_ptr(self.inner) }}").unwrap();
+ writeln!(w, "\t}}").unwrap();
+ writeln!(w, "\t/// When moving out of the pointer, we have to ensure we aren't a reference, this makes that easy").unwrap();
writeln!(w, "\tpub(crate) fn take_inner(mut self) -> *mut native{} {{", struct_name).unwrap();
writeln!(w, "\t\tassert!(self.is_owned);").unwrap();
- writeln!(w, "\t\tlet ret = self.inner;").unwrap();
+ writeln!(w, "\t\tlet ret = ObjOps::untweak_ptr(self.inner);").unwrap();
writeln!(w, "\t\tself.inner = std::ptr::null_mut();").unwrap();
writeln!(w, "\t\tret").unwrap();
writeln!(w, "\t}}\n}}").unwrap();
writeln_arg_docs(w, &field.attrs, "", types, Some(&gen_types), vec![].drain(..), Some(&ref_type));
write!(w, "#[no_mangle]\npub extern \"C\" fn {}_get_{}(this_ptr: &{}) -> ", struct_name, ident, struct_name).unwrap();
types.write_c_type(w, &ref_type, Some(&gen_types), true);
- write!(w, " {{\n\tlet mut inner_val = &mut unsafe {{ &mut *this_ptr.inner }}.{};\n\t", ident).unwrap();
+ write!(w, " {{\n\tlet mut inner_val = &mut this_ptr.get_native_mut_ref().{};\n\t", ident).unwrap();
let local_var = types.write_to_c_conversion_new_var(w, &format_ident!("inner_val"), &ref_type, Some(&gen_types), true);
if local_var { write!(w, "\n\t").unwrap(); }
types.write_to_c_conversion_inline_prefix(w, &ref_type, Some(&gen_types), true);
write!(w, ") {{\n\t").unwrap();
let local_var = types.write_from_c_conversion_new_var(w, &format_ident!("val"), &field.ty, Some(&gen_types));
if local_var { write!(w, "\n\t").unwrap(); }
- write!(w, "unsafe {{ &mut *this_ptr.inner }}.{} = ", ident).unwrap();
+ write!(w, "unsafe {{ &mut *ObjOps::untweak_ptr(this_ptr.inner) }}.{} = ", ident).unwrap();
types.write_from_c_conversion_prefix(w, &field.ty, Some(&gen_types));
write!(w, "val").unwrap();
types.write_from_c_conversion_suffix(w, &field.ty, Some(&gen_types));
write!(w, "\n\t").unwrap();
}
}
- writeln!(w, "{} {{ inner: Box::into_raw(Box::new(native{} {{", struct_name, s.ident).unwrap();
+ writeln!(w, "{} {{ inner: ObjOps::heap_alloc(native{} {{", struct_name, s.ident).unwrap();
for field in fields.named.iter() {
write!(w, "\t\t{}: ", field.ident.as_ref().unwrap()).unwrap();
types.write_from_c_conversion_prefix(w, &field.ty, Some(&gen_types));
types.write_from_c_conversion_suffix(w, &field.ty, Some(&gen_types));
writeln!(w, ",").unwrap();
}
- writeln!(w, "\t}})), is_owned: true }}\n}}").unwrap();
+ writeln!(w, "\t}}), is_owned: true }}\n}}").unwrap();
}
}
}
// type-conversion logic without actually knowing the concrete native type.
writeln!(w, "impl From<native{}> for crate::{} {{", ident, full_trait_path).unwrap();
writeln!(w, "\tfn from(obj: native{}) -> Self {{", ident).unwrap();
- writeln!(w, "\t\tlet mut rust_obj = {} {{ inner: Box::into_raw(Box::new(obj)), is_owned: true }};", ident).unwrap();
+ writeln!(w, "\t\tlet mut rust_obj = {} {{ inner: ObjOps::heap_alloc(obj), is_owned: true }};", ident).unwrap();
writeln!(w, "\t\tlet mut ret = {}_as_{}(&rust_obj);", ident, trait_obj.ident).unwrap();
writeln!(w, "\t\t// We want to free rust_obj when ret gets drop()'d, not rust_obj, so wipe rust_obj's pointer and set ret's free() fn").unwrap();
writeln!(w, "\t\trust_obj.inner = std::ptr::null_mut();").unwrap();
writeln!(w, "/// This copies the `inner` pointer in this_arg and thus the returned {} must be freed before this_arg is", trait_obj.ident).unwrap();
write!(w, "#[no_mangle]\npub extern \"C\" fn {}_as_{}(this_arg: &{}) -> crate::{} {{\n", ident, trait_obj.ident, ident, full_trait_path).unwrap();
writeln!(w, "\tcrate::{} {{", full_trait_path).unwrap();
- writeln!(w, "\t\tthis_arg: unsafe {{ (*this_arg).inner as *mut c_void }},").unwrap();
+ writeln!(w, "\t\tthis_arg: unsafe {{ ObjOps::untweak_ptr((*this_arg).inner) as *mut c_void }},").unwrap();
writeln!(w, "\t\tfree: None,").unwrap();
macro_rules! write_meth {
(s, t) => {
if let Some(supertrait_obj) = types.crate_types.traits.get(s) {
writeln!(w, "\t\t{}: crate::{} {{", t, s).unwrap();
- writeln!(w, "\t\t\tthis_arg: unsafe {{ (*this_arg).inner as *mut c_void }},").unwrap();
+ writeln!(w, "\t\t\tthis_arg: unsafe {{ ObjOps::untweak_ptr((*this_arg).inner) as *mut c_void }},").unwrap();
writeln!(w, "\t\t\tfree: None,").unwrap();
for item in supertrait_obj.items.iter() {
match item {
} else if path_matches_nongeneric(&trait_path.1, &["Default"]) {
writeln!(w, "/// Creates a \"default\" {}. See struct and individual field documentaiton for details on which values are used.", ident).unwrap();
write!(w, "#[must_use]\n#[no_mangle]\npub extern \"C\" fn {}_default() -> {} {{\n", ident, ident).unwrap();
- write!(w, "\t{} {{ inner: Box::into_raw(Box::new(Default::default())), is_owned: true }}\n", ident).unwrap();
+ write!(w, "\t{} {{ inner: ObjOps::heap_alloc(Default::default()), is_owned: true }}\n", ident).unwrap();
write!(w, "}}\n").unwrap();
} else if path_matches_nongeneric(&trait_path.1, &["core", "cmp", "PartialEq"]) {
} else if path_matches_nongeneric(&trait_path.1, &["core", "cmp", "Eq"]) {
writeln!(w, "\tfn clone(&self) -> Self {{").unwrap();
writeln!(w, "\t\tSelf {{").unwrap();
writeln!(w, "\t\t\tinner: if <*mut native{}>::is_null(self.inner) {{ std::ptr::null_mut() }} else {{", ident).unwrap();
- writeln!(w, "\t\t\t\tBox::into_raw(Box::new(unsafe {{ &*self.inner }}.clone())) }},").unwrap();
+ writeln!(w, "\t\t\t\tObjOps::heap_alloc(unsafe {{ &*ObjOps::untweak_ptr(self.inner) }}.clone()) }},").unwrap();
writeln!(w, "\t\t\tis_owned: true,").unwrap();
writeln!(w, "\t\t}}\n\t}}\n}}").unwrap();
writeln!(w, "#[allow(unused)]").unwrap();
if takes_owned_self {
write!(w, "(*unsafe {{ Box::from_raw(this_arg.take_inner()) }}).{}(", m.sig.ident).unwrap();
} else if takes_mut_self {
- write!(w, "unsafe {{ &mut (*(this_arg.inner as *mut native{})) }}.{}(", ident, m.sig.ident).unwrap();
+ write!(w, "unsafe {{ &mut (*ObjOps::untweak_ptr(this_arg.inner as *mut native{})) }}.{}(", ident, m.sig.ident).unwrap();
} else {
- write!(w, "unsafe {{ &*this_arg.inner }}.{}(", m.sig.ident).unwrap();
+ write!(w, "unsafe {{ &*ObjOps::untweak_ptr(this_arg.inner) }}.{}(", m.sig.ident).unwrap();
}
},
_ => unimplemented!(),
let is_inner_ref = if let Some(syn::Type::Reference(_)) = single_contained { true } else { false };
if is_ref {
return Some(("if ", vec![
- (".is_none() { std::ptr::null() } else { ".to_owned(),
+ (".is_none() { std::ptr::null() } else { ObjOps::nonnull_ptr_to_inner(".to_owned(),
format!("({}{}.unwrap())", var_access, if is_inner_ref { "" } else { ".as_ref()" }))
- ], " }", ContainerPrefixLocation::OutsideConv));
+ ], ") }", ContainerPrefixLocation::OutsideConv));
} else {
return Some(("if ", vec![
(".is_none() { std::ptr::null_mut() } else { ".to_owned(), format!("({}.unwrap())", var_access))
DeclType::MirroredEnum => write!(w, "crate::{}::native_into(", decl_path).unwrap(),
DeclType::EnumIgnored|DeclType::StructImported if is_ref && ptr_for_ref && from_ptr =>
write!(w, "crate::{} {{ inner: unsafe {{ (", decl_path).unwrap(),
- DeclType::EnumIgnored|DeclType::StructImported if is_ref && ptr_for_ref =>
- write!(w, "crate::{} {{ inner: unsafe {{ ( (&(*", decl_path).unwrap(),
- DeclType::EnumIgnored|DeclType::StructImported if is_ref =>
- write!(w, "&crate::{} {{ inner: unsafe {{ (", decl_path).unwrap(),
+ DeclType::EnumIgnored|DeclType::StructImported if is_ref => {
+ if !ptr_for_ref { write!(w, "&").unwrap(); }
+ write!(w, "crate::{} {{ inner: unsafe {{ ObjOps::nonnull_ptr_to_inner((", decl_path).unwrap()
+ },
DeclType::EnumIgnored|DeclType::StructImported if !is_ref && from_ptr =>
write!(w, "crate::{} {{ inner: ", decl_path).unwrap(),
DeclType::EnumIgnored|DeclType::StructImported if !is_ref =>
- write!(w, "crate::{} {{ inner: Box::into_raw(Box::new(", decl_path).unwrap(),
+ write!(w, "crate::{} {{ inner: ObjOps::heap_alloc(", decl_path).unwrap(),
DeclType::Trait(_) if is_ref => write!(w, "").unwrap(),
DeclType::Trait(_) if !is_ref => {},
_ => panic!("{:?}", decl_path),
DeclType::MirroredEnum => write!(w, ")").unwrap(),
DeclType::EnumIgnored|DeclType::StructImported if is_ref && ptr_for_ref && from_ptr =>
write!(w, " as *const _) as *mut _ }}, is_owned: false }}").unwrap(),
- DeclType::EnumIgnored|DeclType::StructImported if is_ref && ptr_for_ref =>
- write!(w, ") as *const _) as *mut _) }}, is_owned: false }}").unwrap(),
DeclType::EnumIgnored|DeclType::StructImported if is_ref =>
- write!(w, " as *const _) as *mut _ }}, is_owned: false }}").unwrap(),
+ write!(w, " as *const _) as *mut _) }}, is_owned: false }}").unwrap(),
DeclType::EnumIgnored|DeclType::StructImported if !is_ref && from_ptr =>
write!(w, ", is_owned: true }}").unwrap(),
- DeclType::EnumIgnored|DeclType::StructImported if !is_ref => write!(w, ")), is_owned: true }}").unwrap(),
+ DeclType::EnumIgnored|DeclType::StructImported if !is_ref => write!(w, "), is_owned: true }}").unwrap(),
DeclType::Trait(_) if is_ref => {},
DeclType::Trait(_) => {
// This is used when we're converting a concrete Rust type into a C trait
self.write_to_c_conversion_inline_suffix_inner(w, t, generics, false, ptr_for_ref, false);
}
- fn write_from_c_conversion_prefix_inner<W: std::io::Write>(&self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, is_ref: bool, ptr_for_ref: bool) {
+ fn write_from_c_conversion_prefix_inner<W: std::io::Write>(&self, w: &mut W, t: &syn::Type, generics: Option<&GenericTypes>, is_ref: bool, _ptr_for_ref: bool) {
self.write_conversion_inline_intern(w, t, generics, is_ref, false, false, "() /*", true, |_, _| "&local_".to_owned(),
|a, b, _c| self.from_c_conversion_prefix_from_path(a, b),
- |w, decl_type, _full_path, is_ref, is_mut| match decl_type {
- DeclType::StructImported if is_ref && ptr_for_ref => write!(w, "unsafe {{ &*(*").unwrap(),
- DeclType::StructImported if is_mut && is_ref => write!(w, "unsafe {{ &mut *").unwrap(),
- DeclType::StructImported if is_ref => write!(w, "unsafe {{ &*").unwrap(),
+ |w, decl_type, _full_path, is_ref, _is_mut| match decl_type {
+ DeclType::StructImported if is_ref => write!(w, "").unwrap(),
DeclType::StructImported if !is_ref => write!(w, "*unsafe {{ Box::from_raw(").unwrap(),
DeclType::MirroredEnum if is_ref => write!(w, "&").unwrap(),
DeclType::MirroredEnum => {},
(true, Some(_)) => unreachable!(),
},
|a, b, _c| self.from_c_conversion_suffix_from_path(a, b),
- |w, decl_type, _full_path, is_ref, _is_mut| match decl_type {
- DeclType::StructImported if is_ref && ptr_for_ref => write!(w, ").inner }}").unwrap(),
- DeclType::StructImported if is_ref => write!(w, ".inner }}").unwrap(),
+ |w, decl_type, _full_path, is_ref, is_mut| match decl_type {
+ DeclType::StructImported if is_ref && ptr_for_ref => write!(w, "XXX unimplemented").unwrap(),
+ DeclType::StructImported if is_mut && is_ref => write!(w, ".get_native_mut_ref()").unwrap(),
+ DeclType::StructImported if is_ref => write!(w, ".get_native_ref()").unwrap(),
DeclType::StructImported if !is_ref => write!(w, ".take_inner()) }}").unwrap(),
DeclType::MirroredEnum if is_ref => write!(w, ".to_native()").unwrap(),
DeclType::MirroredEnum => write!(w, ".into_native()").unwrap(),
} else { None }
},
|w, decl_type, _full_path, is_ref, _is_mut| match decl_type {
- DeclType::StructImported if !is_ref => write!(w, "unsafe {{ &*").unwrap(),
+ DeclType::StructImported if !is_ref => write!(w, "").unwrap(),
_ => unimplemented!(),
});
}
},
|a, b, _c| self.from_c_conversion_suffix_from_path(a, b),
|w, decl_type, _full_path, is_ref, _is_mut| match decl_type {
- DeclType::StructImported if !is_ref => write!(w, ".inner }}").unwrap(),
+ DeclType::StructImported if !is_ref => write!(w, ".get_native_ref()").unwrap(),
_ => unimplemented!(),
});
}
if prefix_location == ContainerPrefixLocation::PerConv {
var_prefix(w, conv_ty, generics, is_ref && ty_has_inner, ptr_for_ref, false);
} else if !is_ref && !needs_ref_map && to_c && only_contained_has_inner {
- write!(w, "Box::into_raw(Box::new(").unwrap();
+ write!(w, "ObjOps::heap_alloc(").unwrap();
}
write!(w, "{}{}", if contains_slice { "local_" } else { "" }, if new_var { new_var_name } else { var_access }).unwrap();
if prefix_location == ContainerPrefixLocation::PerConv {
var_suffix(w, conv_ty, generics, is_ref && ty_has_inner, ptr_for_ref, false);
} else if !is_ref && !needs_ref_map && to_c && only_contained_has_inner {
- write!(w, "))").unwrap();
+ write!(w, ")").unwrap();
}
write!(w, " }}").unwrap();
}
ret
}
}
+
+
+pub(crate) mod ObjOps {
+ #[inline]
+ #[must_use = "returns new dangling pointer"]
+ pub(crate) fn heap_alloc<T>(obj: T) -> *mut T {
+ let ptr = Box::into_raw(Box::new(obj));
+ nonnull_ptr_to_inner(ptr)
+ }
+ #[inline]
+ pub(crate) fn nonnull_ptr_to_inner<T>(ptr: *const T) -> *mut T {
+ if core::mem::size_of::<T>() == 0 {
+ // We map `None::<T>` as `T { inner: null, .. }` which works great for all
+ // non-Zero-Sized-Types `T`.
+ // For ZSTs, we need to differentiate between null implying `None` and null implying
+ // `Some` with no allocation.
+ // Thus, for ZSTs, we add one (usually) page here, which should always be aligned.
+ // Note that this relies on undefined behavior! A pointer to NULL may be valid, but a
+ // pointer to NULL + 4096 is almost certainly not. That said, Rust's existing use of
+ // `(*mut T)1` for the pointer we're adding to is also not defined, so we should be
+ // fine.
+ // Note that we add 4095 here as at least the Java client assumes that the low bit on
+ // any heap pointer is 0, which is generally provided by malloc, but which is not true
+ // for ZSTs "allocated" by `Box::new`.
+ debug_assert_eq!(ptr as usize, 1);
+ unsafe { (ptr as *mut T).cast::<u8>().add(4096 - 1).cast::<T>() }
+ } else {
+ // In order to get better test coverage, also increment non-ZST pointers with
+ // --cfg=test_mod_pointers, which is set in genbindings.sh for debug builds.
+ #[cfg(test_mod_pointers)]
+ unsafe { (ptr as *mut T).cast::<u8>().add(4096).cast::<T>() }
+ #[cfg(not(test_mod_pointers))]
+ unsafe { ptr as *mut T }
+ }
+ }
+ #[inline]
+ /// Invert nonnull_ptr_to_inner
+ pub(crate) fn untweak_ptr<T>(ptr: *mut T) -> *mut T {
+ if core::mem::size_of::<T>() == 0 {
+ unsafe { ptr.cast::<u8>().sub(4096 - 1).cast::<T>() }
+ } else {
+ #[cfg(test_mod_pointers)]
+ unsafe { ptr.cast::<u8>().sub(4096).cast::<T>() }
+ #[cfg(not(test_mod_pointers))]
+ ptr
+ }
+ }
+}