use super::*;
use crate::{
prefix_type::{PrefixRef, PrefixRefTrait},
sabi_types::RRef,
};
#[repr(C)]
#[derive(StableAbi)]
pub struct LibHeader {
header: AbiHeader,
root_mod_consts: RootModuleConsts,
init_globals_with: InitGlobalsWith,
module: LateStaticRef<PrefixRef<ErasedPrefix>>,
constructor: extern "C" fn() -> RootModuleResult,
}
impl LibHeader {
pub const unsafe fn from_constructor<M>(
constructor: extern "C" fn() -> RootModuleResult,
check_layout: CheckTypeLayout,
) -> Self
where
M: RootModule,
{
Self {
header: AbiHeader::VALUE,
root_mod_consts: match check_layout {
CheckTypeLayout::Yes => M::CONSTANTS,
CheckTypeLayout::No => M::CONSTANTS_NO_ABI_INFO,
},
init_globals_with: INIT_GLOBALS_WITH,
module: LateStaticRef::new(),
constructor,
}
}
pub fn from_module<M>(value: M) -> Self
where
M: RootModule,
{
Self {
header: AbiHeader::VALUE,
root_mod_consts: M::CONSTANTS,
init_globals_with: INIT_GLOBALS_WITH,
module: {
let erased = unsafe { value.to_prefix_ref().cast::<ErasedPrefix>() };
LateStaticRef::from_prefixref(erased)
},
constructor: GetAbortingConstructor::aborting_constructor,
}
}
pub const fn root_mod_consts(&self) -> &RootModuleConsts {
&self.root_mod_consts
}
pub const fn version_strings(&self) -> VersionStrings {
self.root_mod_consts.version_strings()
}
pub const fn layout(&self) -> Option<&'static TypeLayout> {
self.root_mod_consts.layout().into_option()
}
pub(super) fn initialize_library_globals(&self, globals: &'static Globals) {
(self.init_globals_with.0)(globals);
}
fn check_version<M>(&self) -> Result<(), LibraryError>
where
M: RootModule,
{
let expected_version = M::VERSION_STRINGS.piped(VersionNumber::new)?;
let actual_version = self.version_strings().piped(VersionNumber::new)?;
if expected_version.major != actual_version.major
|| (expected_version.major == 0) && expected_version.minor > actual_version.minor
{
return Err(LibraryError::IncompatibleVersionNumber {
library_name: M::NAME,
expected_version,
actual_version,
});
}
Ok(())
}
pub fn init_root_module<M>(&self) -> Result<M, LibraryError>
where
M: RootModule,
{
self.check_version::<M>()?;
self.check_layout::<M>()
}
pub unsafe fn init_root_module_with_unchecked_layout<M>(&self) -> Result<M, LibraryError>
where
M: RootModule,
{
self.check_version::<M>()?;
unsafe { self.unchecked_layout() }.map_err(RootModuleError::into_library_error::<M>)
}
pub fn ensure_layout<M>(&self) -> Result<(), LibraryError>
where
M: RootModule,
{
if let IsLayoutChecked::Yes(root_mod_layout) = self.root_mod_consts.layout() {
(globals::initialized_globals().layout_checking)(<M>::LAYOUT, root_mod_layout)
.into_result()
.map_err(|e| {
let formatted = e.to_formatted_error();
LibraryError::AbiInstability(formatted)
})?;
}
atomic::compiler_fence(atomic::Ordering::SeqCst);
Ok(())
}
pub fn check_layout<M>(&self) -> Result<M, LibraryError>
where
M: RootModule,
{
self.ensure_layout::<M>()?;
unsafe {
self.unchecked_layout()
.map_err(RootModuleError::into_library_error::<M>)
}
}
pub unsafe fn unchecked_layout<M>(&self) -> Result<M, RootModuleError>
where
M: PrefixRefTrait,
{
let reff = self
.module
.try_init(|| (self.constructor)().into_result())
.map_err(|mut err| {
err.reallocate();
err
})?;
unsafe { Ok(M::from_prefix_ref(reff.cast::<M::PrefixFields>())) }
}
}
struct GetAbortingConstructor<T>(T);
impl<T> GetAbortingConstructor<T> {
extern "C" fn aborting_constructor() -> T {
extern_fn_panic_handling! {
panic!(
"BUG:\n\
This function \
(abi_stable::library::lib_header::GetAbortingConstructor::aborting_constructor) \
must only be used \
as a dummy functions when initializing `LibHeader` \
within `LibHeader::from_module`."
);
}
}
}
#[repr(C)]
#[derive(StableAbi, Copy, Clone)]
struct InitGlobalsWith(pub extern "C" fn(&'static Globals));
const INIT_GLOBALS_WITH: InitGlobalsWith = InitGlobalsWith(crate::globals::initialize_globals_with);
#[repr(transparent)]
#[derive(Debug, StableAbi, Copy, Clone)]
pub struct AbiHeaderRef(pub(super) RRef<'static, AbiHeader>);
impl std::ops::Deref for AbiHeaderRef {
type Target = AbiHeader;
fn deref(&self) -> &AbiHeader {
self.0.get()
}
}
#[repr(C)]
#[derive(Debug, StableAbi, Copy, Clone)]
#[allow(clippy::manual_non_exhaustive)]
pub struct AbiHeader {
pub magic_string: [u8; 32],
pub abi_major: u32,
pub abi_minor: u32,
_priv: (),
}
impl AbiHeader {
pub const VALUE: AbiHeader = {
mod value {
use super::*;
abi_stable_derive::construct_abi_header! {}
}
value::ABI_HEADER
};
}
impl AbiHeader {
pub fn is_compatible(&self, other: &Self) -> bool {
self.magic_string == other.magic_string
&& self.abi_major == other.abi_major
&& (self.abi_major != 0 || self.abi_minor == other.abi_minor)
}
pub fn is_valid(&self) -> bool {
self.is_compatible(&AbiHeader::VALUE)
}
}
impl AbiHeaderRef {
pub fn upgrade(self) -> Result<&'static LibHeader, LibraryError> {
if !self.is_valid() {
return Err(LibraryError::InvalidAbiHeader(*self));
}
let lib_header: &'static LibHeader = unsafe { self.0.transmute_into_ref() };
let c_abi_testing_fns = lib_header.root_mod_consts().c_abi_testing_fns();
crate::library::c_abi_testing::run_tests(c_abi_testing_fns)?;
let globals = globals::initialized_globals();
lib_header.initialize_library_globals(globals);
Ok(lib_header)
}
}