#[cfg(not(any(julia_1_10, julia_1_11)))]
use std::ptr::null_mut;
use std::{any::TypeId, marker::PhantomData, ptr::NonNull};
use jl_sys::{
jl_base_module, jl_core_module, jl_get_global, jl_is_const, jl_main_module, jl_module_t,
jl_module_type, jl_set_global,
};
use jlrs_sys::{jlrs_module_name, jlrs_module_parent};
use super::{
Managed, Weak, erase_scope_lifetime,
value::{ValueData, ValueResult, ValueUnbound},
};
use crate::{
call::Call,
catch::{catch_exceptions, unwrap_exc},
convert::to_symbol::ToSymbol,
data::{
cache::{CacheMap, FxCache, new_fx_cache},
layout::nothing::Nothing,
managed::{private::ManagedPriv, symbol::Symbol, union_all::UnionAll, value::Value},
static_data::{StaticRef, get_top_item},
types::{construct_type::ConstructType, typecheck::Typecheck},
},
error::{AccessError, JlrsResult, TypeError},
gc_safe::GcSafeOnceLock,
impl_julia_typecheck, inline_static_ref,
memory::{
PTls,
gc::mark_queue_obj,
target::{Target, TargetException, TargetResult},
},
prelude::DataType,
private::Private,
};
pub(crate) static CACHE: FxCache<Box<[u8]>, (TypeId, ValueUnbound)> = new_fx_cache();
pub(crate) unsafe fn mark_global_cache(ptls: PTls, full: bool) {
unsafe {
if full || CACHE.is_dirty() {
CACHE.map(|(_, value)| {
mark_queue_obj(ptls, value.as_weak());
});
CACHE.clear_dirty();
}
}
}
#[derive(Copy, Clone, PartialEq)]
#[repr(transparent)]
pub struct Module<'scope>(NonNull<jl_module_t>, PhantomData<&'scope ()>);
impl<'scope> Module<'scope> {
#[inline]
pub fn name(self) -> Symbol<'scope> {
unsafe {
let sym = jlrs_module_name(self.unwrap(Private));
Symbol::wrap_non_null(NonNull::new_unchecked(sym), Private)
}
}
#[inline]
pub fn parent(self) -> Module<'scope> {
unsafe {
let parent = jlrs_module_parent(self.unwrap(Private));
Module(NonNull::new_unchecked(parent), PhantomData)
}
}
#[inline(never)]
pub unsafe fn typed_global_cached<'target, T, S, Tgt>(target: &Tgt, path: S) -> JlrsResult<T>
where
T: ConstructType + Managed<'target, 'static> + Typecheck,
S: AsRef<str>,
Tgt: Target<'target>,
{
unsafe {
let tid = T::type_id();
let path = path.as_ref();
if let Some(cached) = CACHE.get(path.as_bytes()) {
if cached.0 == tid {
return Ok(cached.1.cast_unchecked::<T>());
} else {
let ty = T::construct_type(target).as_value();
Err(TypeError::NotA {
value: cached.1.display_string_or("<Cannot display value>"),
field_type: ty.display_string_or("<Cannot display type>"),
})?
}
}
let mut parts = path.split('.');
let n_parts = parts.clone().count();
let module_name = parts.next().unwrap();
let top_item = get_top_item::<_, Value>(target, module_name)?;
let item = match n_parts {
1 => top_item.as_value().cast::<T>()?,
n => {
let mut module = top_item.cast::<Module>()?;
for _ in 1..n - 1 {
module = module
.submodule(&target, parts.next().unwrap())?
.as_managed();
}
module
.global(&target, parts.next().unwrap())?
.as_value()
.cast::<T>()?
}
};
CACHE.insert(
path.as_bytes().into(),
(tid, erase_scope_lifetime(item.as_value())),
);
Ok(item)
}
}
#[inline]
pub fn main<Tgt: Target<'scope>>(_: &Tgt) -> Self {
unsafe { Module::wrap_non_null(NonNull::new_unchecked(jl_main_module), Private) }
}
#[inline]
pub fn core<Tgt: Target<'scope>>(_: &Tgt) -> Self {
unsafe { Module::wrap_non_null(NonNull::new_unchecked(jl_core_module), Private) }
}
#[inline]
pub fn base<Tgt: Target<'scope>>(_: &Tgt) -> Self {
unsafe { Module::wrap_non_null(NonNull::new_unchecked(jl_base_module), Private) }
}
#[inline]
pub fn jlrs_core<Tgt: Target<'scope>>(target: &Tgt) -> Self {
static JLRS_CORE: StaticRef<Module> = StaticRef::new(
"Base.loaded_modules[Base.PkgId(Base.UUID(\"29be08bc-e5fd-4da2-bbc1-72011c6ea2c9\"), \"JlrsCore\")]",
);
unsafe { JLRS_CORE.get_or_eval(target) }
}
pub fn submodule<'target, N, Tgt>(
self,
target: Tgt,
name: N,
) -> JlrsResult<ModuleData<'target, Tgt>>
where
N: ToSymbol,
Tgt: Target<'target>,
{
match self.global(&target, name) {
Ok(v) => unsafe { Ok(v.as_value().cast::<Module>()?.root(target)) },
Err(e) => Err(e)?,
}
}
pub fn package_root_module<'target, N: ToSymbol, Tgt: Target<'target>>(
target: &Tgt,
name: N,
) -> Option<Module<'target>> {
static FUNC: GcSafeOnceLock<unsafe extern "C" fn(Symbol) -> Value> = GcSafeOnceLock::new();
unsafe {
let func = FUNC.get_or_init(|| {
let ptr = Module::jlrs_core(&target)
.global(&target, "root_module_c")
.unwrap()
.as_value()
.data_ptr()
.cast()
.as_ptr();
*ptr
});
let name = name.to_symbol(&target);
let module = func(name);
if module.is::<Nothing>() {
return None;
}
Some(module.cast_unchecked())
}
}
pub unsafe fn set_global<'target, N, Tgt>(
self,
target: Tgt,
name: N,
value: Value<'_, 'static>,
) -> TargetException<'target, 'static, (), Tgt>
where
N: ToSymbol,
Tgt: Target<'target>,
{
unsafe {
let symbol = name.to_symbol_priv(Private);
let callback = || {
jl_set_global(
self.unwrap(Private),
symbol.unwrap(Private),
value.unwrap(Private),
)
};
let res = catch_exceptions(callback, unwrap_exc);
target.exception_from_ptr(res, Private)
}
}
#[inline]
pub unsafe fn set_global_unchecked<N>(self, name: N, value: Value<'_, 'static>)
where
N: ToSymbol,
{
unsafe {
let symbol = name.to_symbol_priv(Private);
jl_set_global(
self.unwrap(Private),
symbol.unwrap(Private),
value.unwrap(Private),
);
}
}
pub fn set_const<'target, N, Tgt>(
self,
target: Tgt,
name: N,
value: Value<'_, 'static>,
) -> TargetException<'target, 'static, Value<'scope, 'static>, Tgt>
where
N: ToSymbol,
Tgt: Target<'target>,
{
unsafe {
let callback = || self.set_const_unchecked(name, value);
let res = match catch_exceptions(callback, unwrap_exc) {
Ok(_) => Ok(Value::wrap_non_null(
value.unwrap_non_null(Private),
Private,
)),
Err(e) => Err(e),
};
target.exception_from_ptr(res, Private)
}
}
#[inline]
pub unsafe fn set_const_unchecked<N>(
self,
name: N,
value: Value<'_, 'static>,
) -> Value<'scope, 'static>
where
N: ToSymbol,
{
unsafe {
let symbol = name.to_symbol_priv(Private);
#[cfg(any(julia_1_10, julia_1_11))]
jl_sys::bindings::jl_set_const(
self.unwrap(Private),
symbol.unwrap(Private),
value.unwrap(Private),
);
#[cfg(not(any(julia_1_10, julia_1_11)))]
jl_sys::jl_declare_constant_val(
null_mut(),
self.unwrap(Private),
symbol.unwrap(Private),
value.unwrap(Private),
);
Value::wrap_non_null(value.unwrap_non_null(Private), Private)
}
}
pub fn global<'target, N, Tgt>(
self,
target: Tgt,
name: N,
) -> JlrsResult<ValueData<'target, 'static, Tgt>>
where
N: ToSymbol,
Tgt: Target<'target>,
{
unsafe {
let name = name.to_symbol(&target);
let func = || {
let name = name.to_symbol(&target);
match self.global_unchecked(target, name) {
Some(x) => Ok(x),
None => Err(AccessError::GlobalNotFound {
name: name.as_str().unwrap_or("<Non-UTF8 symbol>").into(),
module: self.name().as_str().unwrap_or("<Non-UTF8 symbol>").into(),
})?,
}
};
let res = catch_exceptions(func, |_| {
AccessError::GlobalNotFound {
name: name.as_str().unwrap_or("<Non-UTF8 symbol>").into(),
module: self.name().as_str().unwrap_or("<Non-UTF8 symbol>").into(),
}
.into()
});
match res {
Ok(Ok(x)) => Ok(x),
Ok(Err(e)) => Err(e),
Err(e) => Err(e),
}
}
}
pub unsafe fn global_unchecked<'target, N, Tgt>(
self,
target: Tgt,
name: N,
) -> Option<ValueData<'target, 'static, Tgt>>
where
N: ToSymbol,
Tgt: Target<'target>,
{
unsafe {
let symbol = name.to_symbol(&target);
let value = jl_get_global(self.unwrap(Private), symbol.unwrap(Private));
let ptr = NonNull::new(value)?;
Some(Value::wrap_non_null(ptr, Private).root(target))
}
}
pub fn is_const<N>(self, name: N) -> bool
where
N: ToSymbol,
{
unsafe {
let symbol = name.to_symbol_priv(Private);
jl_is_const(self.unwrap(Private), symbol.unwrap(Private)) != 0
}
}
pub unsafe fn require<'target, Tgt, N>(
self,
target: Tgt,
module: N,
) -> ValueResult<'target, 'static, Tgt>
where
Tgt: Target<'target>,
N: ToSymbol,
{
unsafe {
Module::typed_global_cached::<Value, _, _>(&target, "Base.require")
.unwrap()
.call(
target,
[self.as_value(), module.to_symbol_priv(Private).as_value()],
)
}
}
}
impl_julia_typecheck!(Module<'target>, jl_module_type, 'target);
impl_debug!(Module<'_>);
impl<'scope> ManagedPriv<'scope, '_> for Module<'scope> {
type Wraps = jl_module_t;
type WithLifetimes<'target, 'da> = Module<'target>;
const NAME: &'static str = "Module";
#[inline]
unsafe fn wrap_non_null(inner: NonNull<Self::Wraps>, _: Private) -> Self {
Self(inner, PhantomData)
}
#[inline]
fn unwrap_non_null(self, _: Private) -> NonNull<Self::Wraps> {
self.0
}
}
impl_construct_type_managed!(Module, 1, jl_module_type);
pub type WeakModule<'scope> = Weak<'scope, 'static, Module<'scope>>;
pub type ModuleRet = WeakModule<'static>;
impl_valid_layout!(WeakModule, Module, jl_module_type);
use crate::memory::target::TargetType;
pub type ModuleData<'target, Tgt> = <Tgt as TargetType<'target>>::Data<'static, Module<'target>>;
pub type ModuleResult<'target, Tgt> = TargetResult<'target, 'static, Module<'target>, Tgt>;
impl_ccall_arg_managed!(Module, 1);
impl_into_typed!(Module);
pub struct JlrsCore;
impl JlrsCore {
#[inline]
pub fn module<'target, Tgt>(target: &Tgt) -> Module<'target>
where
Tgt: Target<'target>,
{
Module::jlrs_core(target)
}
#[inline]
pub fn borrow_error<'target, Tgt>(target: &Tgt) -> DataType<'target>
where
Tgt: Target<'target>,
{
inline_static_ref!(BORROW_ERROR, DataType, "JlrsCore.BorrowError", target)
}
#[inline]
pub fn jlrs_error<'target, Tgt>(target: &Tgt) -> DataType<'target>
where
Tgt: Target<'target>,
{
inline_static_ref!(JLRS_ERROR, DataType, "JlrsCore.JlrsError", target)
}
#[inline]
pub fn value_string<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
inline_static_ref!(VALUE_STRING, Value, "JlrsCore.valuestring", target)
}
#[inline]
pub fn error_string<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
inline_static_ref!(ERROR_STRING, Value, "JlrsCore.errorstring", target)
}
#[inline]
pub fn set_error_color<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
inline_static_ref!(SET_ERROR_COLOR, Value, "JlrsCore.set_error_color", target)
}
#[inline]
pub fn wait_main<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
inline_static_ref!(SET_POOL_SIZE, Value, "JlrsCore.Threads.wait_main", target)
}
#[inline]
pub fn notify_main<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
inline_static_ref!(SET_POOL_SIZE, Value, "JlrsCore.Threads.notify_main", target)
}
#[inline]
pub fn delegated_task<'target, Tgt>(target: &Tgt) -> DataType<'target>
where
Tgt: Target<'target>,
{
inline_static_ref!(DELEGATED_TASK, DataType, "JlrsCore.DelegatedTask", target)
}
#[inline]
pub fn background_task<'target, Tgt>(target: &Tgt) -> UnionAll<'target>
where
Tgt: Target<'target>,
{
inline_static_ref!(BACKGROUND_TASK, UnionAll, "JlrsCore.BackgroundTask", target)
}
#[cfg(feature = "async")]
#[inline]
pub(crate) fn async_call<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
inline_static_ref!(ASYNC_CALL, Value, "JlrsCore.Threads.asynccall", target)
}
#[cfg(feature = "async")]
#[inline]
pub(crate) fn interactive_call<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
inline_static_ref!(
INTERACTIVE_CALL,
Value,
"JlrsCore.Threads.interactivecall",
target
)
}
pub(crate) fn api_version<'target, Tgt>(target: &Tgt) -> isize
where
Tgt: Target<'target>,
{
inline_static_ref!(JLRS_API_VERSION, Value, "JlrsCore.JLRS_API_VERSION", target)
.unbox::<isize>()
.unwrap()
}
}
pub struct Main;
impl Main {
#[inline]
pub fn include<'target, Tgt>(target: &Tgt) -> Value<'target, 'static>
where
Tgt: Target<'target>,
{
inline_static_ref!(INCLUDE, Value, "Main.include", target)
}
}