use super::*;
use crate::{
prefix_type::{PrefixRef, PrefixRefTrait},
sabi_types::Constructor,
};
#[repr(C)]
#[derive(StableAbi)]
pub struct LibHeader {
header:AbiHeader,
root_mod_consts:ErasedRootModuleConsts,
init_globals_with:InitGlobalsWith,
module:LateStaticRef<PrefixRef<ErasedPrefix>>,
constructor:Constructor<RootModuleResult>,
}
impl LibHeader {
pub const unsafe fn from_constructor<M>(
constructor:Constructor<RootModuleResult>,
root_mod_consts:RootModuleConsts<M>,
) -> Self {
Self {
header:AbiHeader::VALUE,
root_mod_consts:root_mod_consts.erased(),
init_globals_with: INIT_GLOBALS_WITH,
module:LateStaticRef::new(),
constructor,
}
}
pub fn from_module<T>(value:T)->Self
where
T: RootModule,
{
Self {
header:AbiHeader::VALUE,
root_mod_consts: T::CONSTANTS.erased(),
init_globals_with: INIT_GLOBALS_WITH,
module: {
let erased = unsafe{ value.to_prefix_ref().cast::<ErasedPrefix>() };
LateStaticRef::from_prefixref(PrefixRefTrait::PREFIX_FIELDS, erased)
},
constructor:GetAbortingConstructor::ABORTING_CONSTRUCTOR,
}
}
pub fn root_mod_consts(&self)->&ErasedRootModuleConsts{
&self.root_mod_consts
}
pub fn version_strings(&self)->VersionStrings{
self.root_mod_consts.version_strings()
}
pub 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>()?;
self.unchecked_layout()
.map_err(RootModuleError::into_library_error::<M>)
}
pub fn check_layout<M>(&self) -> Result<M, 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);
unsafe{
self.unchecked_layout()
.map_err(RootModuleError::into_library_error::<M>)
}
}
pub unsafe fn unchecked_layout<M>(&self)->Result<M, RootModuleError>
where
M: PrefixRefTrait,
{
self.module.try_init(|| (self.constructor.0)().into_result() )
.map_err(|mut err|{
err.reallocate();
err
})?
.cast::<M::PrefixFields>()
.piped(M::from_prefix_ref)
.piped(Ok)
}
}
struct GetAbortingConstructor<T>(T);
impl<T> GetAbortingConstructor<T>{
const ABORTING_CONSTRUCTOR:Constructor<T>=
Constructor(Self::aborting_constructor);
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(C)]
#[derive(Debug,StableAbi,Copy,Clone)]
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)
}
pub fn upgrade(&self)->Result< &LibHeader , LibraryError>{
if !self.is_valid() {
return Err(LibraryError::InvalidAbiHeader(*self))
}
let lib_header=unsafe{
transmute_reference::<AbiHeader,LibHeader>(self)
};
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)
}
}