use std::{
marker::PhantomData,
ptr::{NonNull, null_mut},
sync::atomic::{AtomicPtr, Ordering},
};
use jl_sys::{jl_module_t, jl_sym_t, jl_symbol_n, jl_value_t};
use super::{
managed::private::ManagedPriv,
types::{construct_type::ConstructType, typecheck::Typecheck},
};
use crate::{
data::{
cache::{CacheMap, FnvCache, new_fnv_cache},
managed::{Managed, module::Module, value::ValueUnbound},
},
error::{JlrsError, JlrsResult},
gc_safe::GcSafeOnceLock,
memory::{PTls, gc::mark_queue_obj, target::Target},
prelude::{Symbol, Value},
private::Private,
};
pub(crate) static CACHE: FnvCache<usize, ValueUnbound> = new_fnv_cache();
pub(crate) unsafe fn mark_static_data_cache(ptls: PTls, full: bool) {
unsafe {
if full || CACHE.is_dirty() {
CACHE.map(|value| {
mark_queue_obj(ptls, value.as_weak());
});
CACHE.clear_dirty();
}
}
}
pub(crate) static LOADING_PACKAGE: AtomicPtr<jl_module_t> = AtomicPtr::new(null_mut());
#[doc(hidden)]
pub unsafe fn set_loading_package(module: Option<Module>) {
match module {
Some(module) => LOADING_PACKAGE.store(module.as_weak().ptr().as_ptr(), Ordering::Relaxed),
None => LOADING_PACKAGE.store(null_mut(), Ordering::Relaxed),
}
}
pub(crate) fn get_top_item<'target, Tgt, T>(target: &Tgt, item_name: &str) -> JlrsResult<T>
where
T: Managed<'static, 'static> + Typecheck,
Tgt: Target<'target>,
{
let main = Module::main(target);
unsafe {
match item_name {
"Main" => {
if let Ok(main) = main.leak().as_value().cast::<T>() {
return Ok(main);
} else {
Err(JlrsError::exception(format!(
"Item Main has the wrong type, expected {}",
T::NAME
)))?
}
}
item => {
let ptr = LOADING_PACKAGE.load(Ordering::Relaxed);
if !ptr.is_null() {
let m = Module::wrap_non_null(NonNull::new_unchecked(ptr), Private);
if m.name().as_bytes() == item.as_bytes() {
if let Ok(module) = m.leak().as_value().cast::<T>() {
return Ok(module);
}
}
}
let module = Module::package_root_module(target, item);
if let Some(module) = module {
if let Ok(module) = module.leak().as_value().cast::<T>() {
return Ok(module);
}
}
let global = main.global(target, item);
match global {
Ok(global) => {
if let Ok(global) = global.leak().as_value().cast::<T>() {
return Ok(global);
} else {
Err(JlrsError::exception(format!(
"Item {item} has the wrong type, expected {}",
T::NAME
)))?
}
}
Err(_) => Err(JlrsError::exception(format!("Item {item} is unavailable")))?,
}
}
}
}
}
struct StaticDataInner<T>(ValueUnbound, PhantomData<T>);
unsafe impl<T> Send for StaticDataInner<T> {}
unsafe impl<T> Sync for StaticDataInner<T> {}
pub struct StaticGlobal<T> {
global: GcSafeOnceLock<StaticDataInner<T>>,
path: &'static str,
}
impl<T> StaticGlobal<T>
where
T: Managed<'static, 'static> + Typecheck,
{
#[inline]
pub const fn new(path: &'static str) -> StaticGlobal<T> {
StaticGlobal {
global: GcSafeOnceLock::new(),
path,
}
}
#[inline]
pub fn get_or_init<'target, Tgt>(&self, target: &Tgt) -> T
where
Tgt: Target<'target>,
{
unsafe {
if let Some(global) = self.global.get() {
return global.0.cast_unchecked::<T>();
} else {
self.init(target)
}
}
}
#[inline(never)]
#[cold]
unsafe fn init<'target, Tgt>(&self, target: &Tgt) -> T
where
Tgt: Target<'target>,
{
let global = self.global.get_or_init(|| unsafe {
let split_path = self.path.split('.').collect::<Vec<_>>();
let n_parts = split_path.len();
let global = if n_parts == 1 {
get_top_item(target, split_path[0]).unwrap()
} else {
let mut module = get_top_item::<_, Module>(target, split_path[0]).unwrap();
for i in 1..n_parts - 1 {
module = module
.submodule(target, split_path[i])
.unwrap()
.as_managed();
}
module
.global(target, split_path[n_parts - 1])
.unwrap()
.leak()
.as_value()
};
let key = global.as_weak().ptr().as_ptr().addr();
CACHE.insert(key, global);
return StaticDataInner(global, PhantomData);
});
global.0.cast::<T>().unwrap()
}
}
impl StaticGlobal<ValueUnbound> {
pub const fn new_value(path: &'static str) -> StaticGlobal<ValueUnbound> {
StaticGlobal {
global: GcSafeOnceLock::new(),
path,
}
}
}
pub struct StaticSymbolRef {
sym: AtomicPtr<jl_sym_t>,
sym_s: &'static str,
}
impl StaticSymbolRef {
#[inline]
pub const fn new(sym_s: &'static str) -> StaticSymbolRef {
StaticSymbolRef {
sym: AtomicPtr::new(null_mut()),
sym_s,
}
}
#[inline]
pub fn get_or_init<'target, Tgt>(&self, target: &Tgt) -> Symbol<'target>
where
Tgt: Target<'target>,
{
let ptr = self.sym.load(Ordering::Relaxed);
if ptr.is_null() {
self.init(target)
} else {
unsafe { Symbol::wrap_non_null(NonNull::new_unchecked(ptr), Private) }
}
}
#[cold]
#[inline(never)]
fn init<'target, Tgt>(&self, _: &Tgt) -> Symbol<'target>
where
Tgt: Target<'target>,
{
unsafe {
let bytes = self.sym_s.as_bytes();
let n_bytes = bytes.len();
let bytes_ptr = bytes.as_ptr().cast();
let sym = jl_symbol_n(bytes_ptr, n_bytes);
self.sym.store(sym, Ordering::Relaxed);
Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private)
}
}
}
pub struct StaticRef<T: Managed<'static, 'static>> {
global: AtomicPtr<T::Wraps>,
path: &'static str,
}
impl<T> StaticRef<T>
where
T: Managed<'static, 'static> + Typecheck,
{
#[inline]
pub const fn new(path: &'static str) -> StaticRef<T> {
StaticRef {
global: AtomicPtr::new(null_mut()),
path,
}
}
#[inline]
pub fn get_or_init<'target, Tgt>(&self, target: &Tgt) -> T
where
Tgt: Target<'target>,
{
let ptr = self.global.load(Ordering::Relaxed);
if ptr.is_null() {
self.init(target)
} else {
unsafe { T::wrap_non_null(NonNull::new_unchecked(ptr), Private) }
}
}
#[cold]
#[inline(never)]
fn init<'target, Tgt>(&self, target: &Tgt) -> T
where
Tgt: Target<'target>,
{
unsafe {
let split_path = self.path.split('.').collect::<Vec<_>>();
let n_parts = split_path.len();
if n_parts == 1 {
return get_top_item(target, split_path[0]).unwrap();
}
let mut module = get_top_item::<_, Module>(target, split_path[0]).unwrap();
for i in 1..n_parts - 1 {
module = module
.submodule(target, split_path[i])
.unwrap()
.as_managed();
}
let global = module
.global(target, split_path[n_parts - 1])
.unwrap()
.leak()
.as_value();
let key = global.as_weak().ptr().as_ptr().addr();
CACHE.insert(key, global);
let ptr = global.cast::<T>().unwrap().unwrap(Private);
self.global.store(ptr, Ordering::Relaxed);
T::wrap_non_null(NonNull::new_unchecked(ptr), Private)
}
}
#[inline]
pub(crate) unsafe fn get_or_eval<'target, Tgt>(&self, target: &Tgt) -> T
where
Tgt: Target<'target>,
{
unsafe {
let ptr = self.global.load(Ordering::Relaxed);
if ptr.is_null() {
self.eval(target)
} else {
T::wrap_non_null(NonNull::new_unchecked(ptr), Private)
}
}
}
#[cold]
#[inline(never)]
unsafe fn eval<'target, Tgt>(&self, target: &Tgt) -> T
where
Tgt: Target<'target>,
{
unsafe {
let global = Value::eval_string(target, self.path)
.unwrap()
.leak()
.as_value();
let key = global.as_weak().ptr().as_ptr().addr();
CACHE.insert(key, global);
let ptr = global.cast::<T>().unwrap().unwrap(Private);
self.global.store(ptr, Ordering::Relaxed);
T::wrap_non_null(NonNull::new_unchecked(ptr), Private)
}
}
}
pub struct StaticConstructibleType<T: ConstructType> {
global: AtomicPtr<jl_value_t>,
_marker: PhantomData<T>,
}
impl<T> StaticConstructibleType<T>
where
T: ConstructType,
{
#[inline]
pub const fn new() -> StaticConstructibleType<T> {
StaticConstructibleType {
global: AtomicPtr::new(null_mut()),
_marker: PhantomData,
}
}
#[inline]
pub fn get_or_init<'target, Tgt>(&self, target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
let ptr = self.global.load(Ordering::Relaxed);
if ptr.is_null() {
self.init(target)
} else {
unsafe { Value::wrap_non_null(NonNull::new_unchecked(ptr), Private) }
}
}
#[cold]
#[inline(never)]
fn init<'target, Tgt>(&self, target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
unsafe {
let global = T::construct_type(target).as_value().leak().as_value();
let key = global.as_weak().ptr().as_ptr().addr();
CACHE.insert(key, global);
let ptr = global.unwrap(Private);
self.global.store(ptr, Ordering::Relaxed);
global
}
}
}
#[macro_export]
macro_rules! define_static_global {
($(#[$meta:meta])* $vis:vis $ty:ident, $type:ty, $path:expr_2021) => {
$(#[$meta])*
$vis static $name: $crate::data::static_data::StaticGlobal<$type> =
$crate::data::static_data::StaticGlobal::new($path);
};
($(#[$meta:meta])* $vis:vis $name:ident, $path:expr_2021) => {
$(#[$meta])*
$vis static $name: $crate::data::static_data::StaticGlobal<
$crate::data::managed::value::ValueUnbound,
> = $crate::data::static_data::StaticGlobal::new_value($path);
};
}
#[macro_export]
macro_rules! define_static_ref {
($(#[$meta:meta])* $vis:vis $name:ident, $type:ty, $path:expr_2021) => {
$(#[$meta])*
$vis static $name: $crate::data::static_data::StaticRef<$type> =
$crate::data::static_data::StaticRef::new($path);
};
}
#[macro_export]
macro_rules! define_static_symbol_ref {
($(#[$meta:meta])+ $vis:vis $name:ident, $sym:expr_2021) => {
$(#[$meta])+
$vis static $name: $crate::data::static_data::StaticSymbolRef =
$crate::data::static_data::StaticSymbolRef::new($sym);
};
}
#[macro_export]
macro_rules! static_global {
($name:ident, $target:expr_2021) => {{ $name.get_or_init(&$target) }};
}
#[macro_export]
macro_rules! static_ref {
($name:ident, $target:expr_2021) => {{ $name.get_or_init(&$target) }};
}
#[macro_export]
macro_rules! static_symbol_ref {
($name:ident, $target:expr_2021) => {{ $name.get_or_init(&$target) }};
}
pub use define_static_global;
pub use define_static_ref;
pub use define_static_symbol_ref;
pub use static_global;
pub use static_ref;
pub use static_symbol_ref;
#[macro_export]
macro_rules! inline_static_global {
($(#[$meta:meta])* $name:ident, $type:ty, $path:expr_2021, $target:expr_2021) => {{
$crate::data::static_data::define_static_global!($(#[$meta])* $name, $type, $path);
$crate::data::static_data::static_global!($name, $target)
}};
($(#[$meta:meta])* $name:ident, $path:expr_2021, $target:expr_2021) => {{
$crate::data::static_data::define_static_global!($(#[$meta])* $name, $path);
$crate::data::static_data::static_global!($name, $target)
}};
}
#[macro_export]
macro_rules! inline_static_ref {
($(#[$meta:meta])* $name:ident, $type:ty, $path:expr_2021, $target:expr_2021) => {{
$crate::data::static_data::define_static_ref!($(#[$meta])* $name, $type, $path);
$crate::data::static_data::static_ref!($name, $target)
}};
}
#[macro_export]
macro_rules! inline_static_symbol_ref {
($(#[$meta:meta])* $name:ident, $sym:expr_2021, $target:expr_2021) => {{
$crate::data::static_data::define_static_symbol_ref!($(#[$meta])* $name, $sym);
$crate::data::static_data::static_symbol_ref!($name, $target)
}};
}
pub use inline_static_global;
pub use inline_static_ref;
pub use inline_static_symbol_ref;