use crate::export::user_data::UserData;
use crate::export::{
class_registry, emplace, ClassBuilder, NativeClass, NativeClassMethods, StaticallyNamed,
};
use crate::object::{GodotObject, RawObject, TRef};
use crate::private::get_api;
use std::borrow::Cow;
use std::ffi::CString;
use std::ptr;
use super::InitLevel;
#[derive(Copy, Clone)]
pub struct InitHandle {
handle: *mut libc::c_void,
init_level: InitLevel,
}
#[allow(deprecated)] impl InitHandle {
#[doc(hidden)]
#[inline]
pub unsafe fn new(handle: *mut libc::c_void, init_level: InitLevel) -> Self {
InitHandle { handle, init_level }
}
#[inline]
pub fn add_class<C>(self)
where
C: NativeClassMethods + StaticallyNamed,
{
self.add_class_with::<C>(|_| {})
}
#[inline]
pub fn add_class_with<C>(self, f: impl FnOnce(&ClassBuilder<C>))
where
C: NativeClassMethods + StaticallyNamed,
{
self.add_maybe_tool_class_as_with::<C>(Cow::Borrowed(C::CLASS_NAME), false, |builder| {
C::nativeclass_register_monomorphized(builder);
f(builder);
})
}
#[inline]
pub fn add_tool_class<C>(self)
where
C: NativeClassMethods + StaticallyNamed,
{
self.add_tool_class_with::<C>(|_| {})
}
#[inline]
pub fn add_tool_class_with<C>(self, f: impl FnOnce(&ClassBuilder<C>))
where
C: NativeClassMethods + StaticallyNamed,
{
self.add_maybe_tool_class_as_with::<C>(Cow::Borrowed(C::CLASS_NAME), true, |builder| {
C::nativeclass_register_monomorphized(builder);
f(builder);
})
}
#[inline]
pub fn add_class_as<C>(self, name: String)
where
C: NativeClassMethods,
{
self.add_class_as_with::<C>(name, |_| {})
}
#[inline]
pub fn add_class_as_with<C>(self, name: String, f: impl FnOnce(&ClassBuilder<C>))
where
C: NativeClassMethods,
{
self.add_maybe_tool_class_as_with::<C>(Cow::Owned(name), false, f)
}
#[inline]
pub fn add_tool_class_as<C>(self, name: String)
where
C: NativeClassMethods,
{
self.add_tool_class_as_with::<C>(name, |_| {})
}
#[inline]
pub fn add_tool_class_as_with<C>(self, name: String, f: impl FnOnce(&ClassBuilder<C>))
where
C: NativeClassMethods,
{
self.add_maybe_tool_class_as_with::<C>(Cow::Owned(name), true, f)
}
#[inline]
fn add_maybe_tool_class_as_with<C>(
self,
name: Cow<'static, str>,
is_tool: bool,
f: impl FnOnce(&ClassBuilder<C>),
) where
C: NativeClassMethods,
{
let c_class_name = CString::new(&*name).unwrap();
match class_registry::register_class_as::<C>(name, self.init_level) {
Ok(true) => {}
Ok(false) => return,
Err(e) => {
godot_error!("gdnative-core: ignoring new registration: {e}");
return;
}
};
unsafe {
let base_name = CString::new(C::Base::class_name()).unwrap();
let create = {
unsafe extern "C" fn constructor<C: NativeClass>(
this: *mut sys::godot_object,
_method_data: *mut libc::c_void,
) -> *mut libc::c_void {
use std::panic::{self, AssertUnwindSafe};
let this = match ptr::NonNull::new(this) {
Some(this) => this,
None => {
godot_error!(
"gdnative-core: error constructing {}: owner pointer is null",
class_registry::class_name_or_default::<C>(),
);
return ptr::null_mut();
}
};
let owner = match RawObject::<C::Base>::try_from_sys_ref(this) {
Some(owner) => owner,
None => {
godot_error!(
"gdnative-core: error constructing {}: incompatible owner type, expecting {}",
class_registry::class_name_or_default::<C>(),
C::Base::class_name(),
);
return ptr::null_mut();
}
};
let val = match panic::catch_unwind(AssertUnwindSafe(|| {
emplace::take().unwrap_or_else(|| {
C::nativeclass_init(TRef::new(C::Base::cast_ref(owner)))
})
})) {
Ok(val) => val,
Err(e) => {
godot_error!(
"gdnative-core: error constructing {}: constructor panicked",
class_registry::class_name_or_default::<C>(),
);
crate::private::print_panic_error(e);
return ptr::null_mut();
}
};
let wrapper = C::UserData::new(val);
C::UserData::into_user_data(wrapper) as *mut _
}
sys::godot_instance_create_func {
create_func: Some(constructor::<C>),
method_data: ptr::null_mut(),
free_func: None,
}
};
let destroy = {
unsafe extern "C" fn destructor<C: NativeClass>(
_this: *mut sys::godot_object,
_method_data: *mut libc::c_void,
user_data: *mut libc::c_void,
) {
if user_data.is_null() {
godot_error!(
"gdnative-core: user data pointer for {} is null (did the constructor fail?)",
class_registry::class_name_or_default::<C>(),
);
return;
}
let wrapper = C::UserData::consume_user_data_unchecked(user_data);
drop(wrapper)
}
sys::godot_instance_destroy_func {
destroy_func: Some(destructor::<C>),
method_data: ptr::null_mut(),
free_func: None,
}
};
if is_tool {
(get_api().godot_nativescript_register_tool_class)(
self.handle as *mut _,
c_class_name.as_ptr() as *const _,
base_name.as_ptr() as *const _,
create,
destroy,
);
} else {
(get_api().godot_nativescript_register_class)(
self.handle as *mut _,
c_class_name.as_ptr() as *const _,
base_name.as_ptr() as *const _,
create,
destroy,
);
}
(get_api().godot_nativescript_set_type_tag)(
self.handle as *mut _,
c_class_name.as_ptr() as *const _,
crate::export::type_tag::create::<C>(),
);
let builder = ClassBuilder::new(self.handle, c_class_name);
C::nativeclass_register_properties(&builder);
C::nativeclass_register(&builder);
f(&builder);
}
}
}