1use crate::export::user_data::UserData;
2use crate::export::{
3 class_registry, emplace, ClassBuilder, NativeClass, NativeClassMethods, StaticallyNamed,
4};
5use crate::object::{GodotObject, RawObject, TRef};
6use crate::private::get_api;
7use std::borrow::Cow;
8use std::ffi::CString;
9use std::ptr;
10
11use super::InitLevel;
12
13#[derive(Copy, Clone)]
18pub struct InitHandle {
19 handle: *mut libc::c_void,
20 init_level: InitLevel,
21}
22
23#[allow(deprecated)] impl InitHandle {
25 #[doc(hidden)]
26 #[inline]
27 pub unsafe fn new(handle: *mut libc::c_void, init_level: InitLevel) -> Self {
28 InitHandle { handle, init_level }
29 }
30
31 #[inline]
33 pub fn add_class<C>(self)
34 where
35 C: NativeClassMethods + StaticallyNamed,
36 {
37 self.add_class_with::<C>(|_| {})
38 }
39
40 #[inline]
42 pub fn add_class_with<C>(self, f: impl FnOnce(&ClassBuilder<C>))
43 where
44 C: NativeClassMethods + StaticallyNamed,
45 {
46 self.add_maybe_tool_class_as_with::<C>(Cow::Borrowed(C::CLASS_NAME), false, |builder| {
47 C::nativeclass_register_monomorphized(builder);
48 f(builder);
49 })
50 }
51
52 #[inline]
54 pub fn add_tool_class<C>(self)
55 where
56 C: NativeClassMethods + StaticallyNamed,
57 {
58 self.add_tool_class_with::<C>(|_| {})
59 }
60
61 #[inline]
63 pub fn add_tool_class_with<C>(self, f: impl FnOnce(&ClassBuilder<C>))
64 where
65 C: NativeClassMethods + StaticallyNamed,
66 {
67 self.add_maybe_tool_class_as_with::<C>(Cow::Borrowed(C::CLASS_NAME), true, |builder| {
68 C::nativeclass_register_monomorphized(builder);
69 f(builder);
70 })
71 }
72
73 #[inline]
78 pub fn add_class_as<C>(self, name: String)
79 where
80 C: NativeClassMethods,
81 {
82 self.add_class_as_with::<C>(name, |_| {})
83 }
84
85 #[inline]
90 pub fn add_class_as_with<C>(self, name: String, f: impl FnOnce(&ClassBuilder<C>))
91 where
92 C: NativeClassMethods,
93 {
94 self.add_maybe_tool_class_as_with::<C>(Cow::Owned(name), false, f)
95 }
96
97 #[inline]
102 pub fn add_tool_class_as<C>(self, name: String)
103 where
104 C: NativeClassMethods,
105 {
106 self.add_tool_class_as_with::<C>(name, |_| {})
107 }
108
109 #[inline]
114 pub fn add_tool_class_as_with<C>(self, name: String, f: impl FnOnce(&ClassBuilder<C>))
115 where
116 C: NativeClassMethods,
117 {
118 self.add_maybe_tool_class_as_with::<C>(Cow::Owned(name), true, f)
119 }
120
121 #[inline]
122 fn add_maybe_tool_class_as_with<C>(
123 self,
124 name: Cow<'static, str>,
125 is_tool: bool,
126 f: impl FnOnce(&ClassBuilder<C>),
127 ) where
128 C: NativeClassMethods,
129 {
130 let c_class_name = CString::new(&*name).unwrap();
131
132 match class_registry::register_class_as::<C>(name, self.init_level) {
133 Ok(true) => {}
134 Ok(false) => return,
135 Err(e) => {
136 godot_error!("gdnative-core: ignoring new registration: {e}");
137 return;
138 }
139 };
140
141 unsafe {
142 let base_name = CString::new(C::Base::class_name()).unwrap();
143
144 let create = {
145 unsafe extern "C" fn constructor<C: NativeClass>(
146 this: *mut sys::godot_object,
147 _method_data: *mut libc::c_void,
148 ) -> *mut libc::c_void {
149 use std::panic::{self, AssertUnwindSafe};
150
151 let this = match ptr::NonNull::new(this) {
152 Some(this) => this,
153 None => {
154 godot_error!(
155 "gdnative-core: error constructing {}: owner pointer is null",
156 class_registry::class_name_or_default::<C>(),
157 );
158
159 return ptr::null_mut();
160 }
161 };
162
163 let owner = match RawObject::<C::Base>::try_from_sys_ref(this) {
164 Some(owner) => owner,
165 None => {
166 godot_error!(
167 "gdnative-core: error constructing {}: incompatible owner type, expecting {}",
168 class_registry::class_name_or_default::<C>(),
169 C::Base::class_name(),
170 );
171 return ptr::null_mut();
172 }
173 };
174
175 let val = match panic::catch_unwind(AssertUnwindSafe(|| {
176 emplace::take().unwrap_or_else(|| {
177 C::nativeclass_init(TRef::new(C::Base::cast_ref(owner)))
178 })
179 })) {
180 Ok(val) => val,
181 Err(e) => {
182 godot_error!(
183 "gdnative-core: error constructing {}: constructor panicked",
184 class_registry::class_name_or_default::<C>(),
185 );
186 crate::private::print_panic_error(e);
187 return ptr::null_mut();
188 }
189 };
190
191 let wrapper = C::UserData::new(val);
192 C::UserData::into_user_data(wrapper) as *mut _
193 }
194
195 sys::godot_instance_create_func {
196 create_func: Some(constructor::<C>),
197 method_data: ptr::null_mut(),
198 free_func: None,
199 }
200 };
201
202 let destroy = {
203 unsafe extern "C" fn destructor<C: NativeClass>(
204 _this: *mut sys::godot_object,
205 _method_data: *mut libc::c_void,
206 user_data: *mut libc::c_void,
207 ) {
208 if user_data.is_null() {
209 godot_error!(
210 "gdnative-core: user data pointer for {} is null (did the constructor fail?)",
211 class_registry::class_name_or_default::<C>(),
212 );
213 return;
214 }
215
216 let wrapper = C::UserData::consume_user_data_unchecked(user_data);
217 drop(wrapper)
218 }
219
220 sys::godot_instance_destroy_func {
221 destroy_func: Some(destructor::<C>),
222 method_data: ptr::null_mut(),
223 free_func: None,
224 }
225 };
226
227 if is_tool {
228 (get_api().godot_nativescript_register_tool_class)(
229 self.handle as *mut _,
230 c_class_name.as_ptr() as *const _,
231 base_name.as_ptr() as *const _,
232 create,
233 destroy,
234 );
235 } else {
236 (get_api().godot_nativescript_register_class)(
237 self.handle as *mut _,
238 c_class_name.as_ptr() as *const _,
239 base_name.as_ptr() as *const _,
240 create,
241 destroy,
242 );
243 }
244
245 (get_api().godot_nativescript_set_type_tag)(
246 self.handle as *mut _,
247 c_class_name.as_ptr() as *const _,
248 crate::export::type_tag::create::<C>(),
249 );
250
251 let builder = ClassBuilder::new(self.handle, c_class_name);
252
253 C::nativeclass_register_properties(&builder);
254
255 C::nativeclass_register(&builder);
257
258 f(&builder);
259 }
260 }
261}