1 | /**************************************************************************/ |
2 | /* class_db.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "class_db.h" |
32 | |
33 | #include "core/config/engine.h" |
34 | #include "core/object/script_language.h" |
35 | #include "core/os/mutex.h" |
36 | #include "core/version.h" |
37 | |
38 | #define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock); |
39 | #define OBJTYPE_WLOCK RWLockWrite _rw_lockw_(lock); |
40 | |
41 | #ifdef DEBUG_METHODS_ENABLED |
42 | |
43 | MethodDefinition D_METHODP(const char *p_name, const char *const **p_args, uint32_t p_argcount) { |
44 | MethodDefinition md; |
45 | md.name = StaticCString::create(p_name); |
46 | md.args.resize(p_argcount); |
47 | for (uint32_t i = 0; i < p_argcount; i++) { |
48 | md.args.write[i] = StaticCString::create(*p_args[i]); |
49 | } |
50 | return md; |
51 | } |
52 | |
53 | #endif |
54 | |
55 | ClassDB::APIType ClassDB::current_api = API_CORE; |
56 | HashMap<ClassDB::APIType, uint32_t> ClassDB::api_hashes_cache; |
57 | |
58 | void ClassDB::set_current_api(APIType p_api) { |
59 | DEV_ASSERT(!api_hashes_cache.has(p_api)); // This API type may not be suitable for caching of hash if it can change later. |
60 | current_api = p_api; |
61 | } |
62 | |
63 | ClassDB::APIType ClassDB::get_current_api() { |
64 | return current_api; |
65 | } |
66 | |
67 | HashMap<StringName, ClassDB::ClassInfo> ClassDB::classes; |
68 | HashMap<StringName, StringName> ClassDB::resource_base_extensions; |
69 | HashMap<StringName, StringName> ClassDB::compat_classes; |
70 | |
71 | bool ClassDB::_is_parent_class(const StringName &p_class, const StringName &p_inherits) { |
72 | if (!classes.has(p_class)) { |
73 | return false; |
74 | } |
75 | |
76 | StringName inherits = p_class; |
77 | while (inherits.operator String().length()) { |
78 | if (inherits == p_inherits) { |
79 | return true; |
80 | } |
81 | inherits = _get_parent_class(inherits); |
82 | } |
83 | |
84 | return false; |
85 | } |
86 | |
87 | bool ClassDB::is_parent_class(const StringName &p_class, const StringName &p_inherits) { |
88 | OBJTYPE_RLOCK; |
89 | |
90 | return _is_parent_class(p_class, p_inherits); |
91 | } |
92 | |
93 | void ClassDB::get_class_list(List<StringName> *p_classes) { |
94 | OBJTYPE_RLOCK; |
95 | |
96 | for (const KeyValue<StringName, ClassInfo> &E : classes) { |
97 | p_classes->push_back(E.key); |
98 | } |
99 | |
100 | p_classes->sort(); |
101 | } |
102 | |
103 | void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { |
104 | OBJTYPE_RLOCK; |
105 | |
106 | for (const KeyValue<StringName, ClassInfo> &E : classes) { |
107 | if (E.key != p_class && _is_parent_class(E.key, p_class)) { |
108 | p_classes->push_back(E.key); |
109 | } |
110 | } |
111 | } |
112 | |
113 | void ClassDB::get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { |
114 | OBJTYPE_RLOCK; |
115 | |
116 | for (const KeyValue<StringName, ClassInfo> &E : classes) { |
117 | if (E.key != p_class && _get_parent_class(E.key) == p_class) { |
118 | p_classes->push_back(E.key); |
119 | } |
120 | } |
121 | } |
122 | |
123 | StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) { |
124 | OBJTYPE_RLOCK; |
125 | |
126 | ClassInfo *ti = classes.getptr(p_class); |
127 | if (!ti) { |
128 | return StringName(); |
129 | } |
130 | return ti->inherits; |
131 | } |
132 | |
133 | StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) { |
134 | if (classes.has(p_class)) { |
135 | return p_class; |
136 | } |
137 | |
138 | if (compat_classes.has(p_class)) { |
139 | return compat_classes[p_class]; |
140 | } |
141 | |
142 | return p_class; |
143 | } |
144 | |
145 | StringName ClassDB::_get_parent_class(const StringName &p_class) { |
146 | ClassInfo *ti = classes.getptr(p_class); |
147 | ERR_FAIL_NULL_V_MSG(ti, StringName(), "Cannot get class '" + String(p_class) + "'." ); |
148 | return ti->inherits; |
149 | } |
150 | |
151 | StringName ClassDB::get_parent_class(const StringName &p_class) { |
152 | OBJTYPE_RLOCK; |
153 | |
154 | return _get_parent_class(p_class); |
155 | } |
156 | |
157 | ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { |
158 | OBJTYPE_RLOCK; |
159 | |
160 | ClassInfo *ti = classes.getptr(p_class); |
161 | |
162 | ERR_FAIL_NULL_V_MSG(ti, API_NONE, "Cannot get class '" + String(p_class) + "'." ); |
163 | return ti->api; |
164 | } |
165 | |
166 | uint32_t ClassDB::get_api_hash(APIType p_api) { |
167 | OBJTYPE_RLOCK; |
168 | #ifdef DEBUG_METHODS_ENABLED |
169 | |
170 | if (api_hashes_cache.has(p_api)) { |
171 | return api_hashes_cache[p_api]; |
172 | } |
173 | |
174 | uint64_t hash = hash_murmur3_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG)); |
175 | |
176 | List<StringName> class_list; |
177 | ClassDB::get_class_list(&class_list); |
178 | // Must be alphabetically sorted for hash to compute. |
179 | class_list.sort_custom<StringName::AlphCompare>(); |
180 | |
181 | for (const StringName &E : class_list) { |
182 | ClassInfo *t = classes.getptr(E); |
183 | ERR_FAIL_NULL_V_MSG(t, 0, "Cannot get class '" + String(E) + "'." ); |
184 | if (t->api != p_api || !t->exposed) { |
185 | continue; |
186 | } |
187 | hash = hash_murmur3_one_64(t->name.hash(), hash); |
188 | hash = hash_murmur3_one_64(t->inherits.hash(), hash); |
189 | |
190 | { //methods |
191 | |
192 | List<StringName> snames; |
193 | |
194 | for (const KeyValue<StringName, MethodBind *> &F : t->method_map) { |
195 | String name = F.key.operator String(); |
196 | |
197 | ERR_CONTINUE(name.is_empty()); |
198 | |
199 | if (name[0] == '_') { |
200 | continue; // Ignore non-virtual methods that start with an underscore |
201 | } |
202 | |
203 | snames.push_back(F.key); |
204 | } |
205 | |
206 | snames.sort_custom<StringName::AlphCompare>(); |
207 | |
208 | for (const StringName &F : snames) { |
209 | MethodBind *mb = t->method_map[F]; |
210 | hash = hash_murmur3_one_64(mb->get_name().hash(), hash); |
211 | hash = hash_murmur3_one_64(mb->get_argument_count(), hash); |
212 | hash = hash_murmur3_one_64(mb->get_argument_type(-1), hash); //return |
213 | |
214 | for (int i = 0; i < mb->get_argument_count(); i++) { |
215 | const PropertyInfo info = mb->get_argument_info(i); |
216 | hash = hash_murmur3_one_64(info.type, hash); |
217 | hash = hash_murmur3_one_64(info.name.hash(), hash); |
218 | hash = hash_murmur3_one_64(info.hint, hash); |
219 | hash = hash_murmur3_one_64(info.hint_string.hash(), hash); |
220 | } |
221 | |
222 | hash = hash_murmur3_one_64(mb->get_default_argument_count(), hash); |
223 | |
224 | for (int i = 0; i < mb->get_default_argument_count(); i++) { |
225 | //hash should not change, i hope for tis |
226 | Variant da = mb->get_default_argument(i); |
227 | hash = hash_murmur3_one_64(da.hash(), hash); |
228 | } |
229 | |
230 | hash = hash_murmur3_one_64(mb->get_hint_flags(), hash); |
231 | } |
232 | } |
233 | |
234 | { //constants |
235 | |
236 | List<StringName> snames; |
237 | |
238 | for (const KeyValue<StringName, int64_t> &F : t->constant_map) { |
239 | snames.push_back(F.key); |
240 | } |
241 | |
242 | snames.sort_custom<StringName::AlphCompare>(); |
243 | |
244 | for (const StringName &F : snames) { |
245 | hash = hash_murmur3_one_64(F.hash(), hash); |
246 | hash = hash_murmur3_one_64(t->constant_map[F], hash); |
247 | } |
248 | } |
249 | |
250 | { //signals |
251 | |
252 | List<StringName> snames; |
253 | |
254 | for (const KeyValue<StringName, MethodInfo> &F : t->signal_map) { |
255 | snames.push_back(F.key); |
256 | } |
257 | |
258 | snames.sort_custom<StringName::AlphCompare>(); |
259 | |
260 | for (const StringName &F : snames) { |
261 | MethodInfo &mi = t->signal_map[F]; |
262 | hash = hash_murmur3_one_64(F.hash(), hash); |
263 | for (int i = 0; i < mi.arguments.size(); i++) { |
264 | hash = hash_murmur3_one_64(mi.arguments[i].type, hash); |
265 | } |
266 | } |
267 | } |
268 | |
269 | { //properties |
270 | |
271 | List<StringName> snames; |
272 | |
273 | for (const KeyValue<StringName, PropertySetGet> &F : t->property_setget) { |
274 | snames.push_back(F.key); |
275 | } |
276 | |
277 | snames.sort_custom<StringName::AlphCompare>(); |
278 | |
279 | for (const StringName &F : snames) { |
280 | PropertySetGet *psg = t->property_setget.getptr(F); |
281 | ERR_FAIL_NULL_V(psg, 0); |
282 | |
283 | hash = hash_murmur3_one_64(F.hash(), hash); |
284 | hash = hash_murmur3_one_64(psg->setter.hash(), hash); |
285 | hash = hash_murmur3_one_64(psg->getter.hash(), hash); |
286 | } |
287 | } |
288 | |
289 | //property list |
290 | for (const PropertyInfo &F : t->property_list) { |
291 | hash = hash_murmur3_one_64(F.name.hash(), hash); |
292 | hash = hash_murmur3_one_64(F.type, hash); |
293 | hash = hash_murmur3_one_64(F.hint, hash); |
294 | hash = hash_murmur3_one_64(F.hint_string.hash(), hash); |
295 | hash = hash_murmur3_one_64(F.usage, hash); |
296 | } |
297 | } |
298 | |
299 | hash = hash_fmix32(hash); |
300 | |
301 | // Extension API changes at runtime; let's just not cache them by now. |
302 | if (p_api != API_EXTENSION && p_api != API_EDITOR_EXTENSION) { |
303 | api_hashes_cache[p_api] = hash; |
304 | } |
305 | |
306 | return hash; |
307 | #else |
308 | return 0; |
309 | #endif |
310 | } |
311 | |
312 | bool ClassDB::class_exists(const StringName &p_class) { |
313 | OBJTYPE_RLOCK; |
314 | return classes.has(p_class); |
315 | } |
316 | |
317 | void ClassDB::add_compatibility_class(const StringName &p_class, const StringName &p_fallback) { |
318 | OBJTYPE_WLOCK; |
319 | compat_classes[p_class] = p_fallback; |
320 | } |
321 | |
322 | StringName ClassDB::get_compatibility_class(const StringName &p_class) { |
323 | if (compat_classes.has(p_class)) { |
324 | return compat_classes[p_class]; |
325 | } |
326 | return StringName(); |
327 | } |
328 | |
329 | Object *ClassDB::instantiate(const StringName &p_class) { |
330 | ClassInfo *ti; |
331 | { |
332 | OBJTYPE_RLOCK; |
333 | ti = classes.getptr(p_class); |
334 | if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) { |
335 | if (compat_classes.has(p_class)) { |
336 | ti = classes.getptr(compat_classes[p_class]); |
337 | } |
338 | } |
339 | ERR_FAIL_NULL_V_MSG(ti, nullptr, "Cannot get class '" + String(p_class) + "'." ); |
340 | ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled." ); |
341 | ERR_FAIL_NULL_V_MSG(ti->creation_func, nullptr, "Class '" + String(p_class) + "' or its base class cannot be instantiated." ); |
342 | } |
343 | #ifdef TOOLS_ENABLED |
344 | if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { |
345 | ERR_PRINT("Class '" + String(p_class) + "' can only be instantiated by editor." ); |
346 | return nullptr; |
347 | } |
348 | #endif |
349 | if (ti->gdextension && ti->gdextension->create_instance) { |
350 | return (Object *)ti->gdextension->create_instance(ti->gdextension->class_userdata); |
351 | } else { |
352 | return ti->creation_func(); |
353 | } |
354 | } |
355 | |
356 | void ClassDB::set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance) { |
357 | ERR_FAIL_NULL(p_object); |
358 | ClassInfo *ti; |
359 | { |
360 | OBJTYPE_RLOCK; |
361 | ti = classes.getptr(p_class); |
362 | if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) { |
363 | if (compat_classes.has(p_class)) { |
364 | ti = classes.getptr(compat_classes[p_class]); |
365 | } |
366 | } |
367 | ERR_FAIL_NULL_MSG(ti, "Cannot get class '" + String(p_class) + "'." ); |
368 | ERR_FAIL_COND_MSG(ti->disabled, "Class '" + String(p_class) + "' is disabled." ); |
369 | ERR_FAIL_NULL_MSG(ti->gdextension, "Class '" + String(p_class) + "' has no native extension." ); |
370 | } |
371 | |
372 | p_object->_extension = ti->gdextension; |
373 | p_object->_extension_instance = p_instance; |
374 | } |
375 | |
376 | bool ClassDB::can_instantiate(const StringName &p_class) { |
377 | OBJTYPE_RLOCK; |
378 | |
379 | ClassInfo *ti = classes.getptr(p_class); |
380 | ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'." ); |
381 | #ifdef TOOLS_ENABLED |
382 | if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { |
383 | return false; |
384 | } |
385 | #endif |
386 | return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance)); |
387 | } |
388 | |
389 | bool ClassDB::is_virtual(const StringName &p_class) { |
390 | OBJTYPE_RLOCK; |
391 | |
392 | ClassInfo *ti = classes.getptr(p_class); |
393 | if (!ti) { |
394 | if (!ScriptServer::is_global_class(p_class)) { |
395 | ERR_FAIL_V_MSG(false, "Cannot get class '" + String(p_class) + "'." ); |
396 | } |
397 | return false; |
398 | } |
399 | #ifdef TOOLS_ENABLED |
400 | if (ti->api == API_EDITOR && !Engine::get_singleton()->is_editor_hint()) { |
401 | return false; |
402 | } |
403 | #endif |
404 | return (!ti->disabled && ti->creation_func != nullptr && !(ti->gdextension && !ti->gdextension->create_instance) && ti->is_virtual); |
405 | } |
406 | |
407 | void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) { |
408 | OBJTYPE_WLOCK; |
409 | |
410 | const StringName &name = p_class; |
411 | |
412 | ERR_FAIL_COND_MSG(classes.has(name), "Class '" + String(p_class) + "' already exists." ); |
413 | |
414 | classes[name] = ClassInfo(); |
415 | ClassInfo &ti = classes[name]; |
416 | ti.name = name; |
417 | ti.inherits = p_inherits; |
418 | ti.api = current_api; |
419 | |
420 | if (ti.inherits) { |
421 | ERR_FAIL_COND(!classes.has(ti.inherits)); //it MUST be registered. |
422 | ti.inherits_ptr = &classes[ti.inherits]; |
423 | |
424 | } else { |
425 | ti.inherits_ptr = nullptr; |
426 | } |
427 | } |
428 | |
429 | static MethodInfo info_from_bind(MethodBind *p_method) { |
430 | MethodInfo minfo; |
431 | minfo.name = p_method->get_name(); |
432 | minfo.id = p_method->get_method_id(); |
433 | |
434 | for (int i = 0; i < p_method->get_argument_count(); i++) { |
435 | minfo.arguments.push_back(p_method->get_argument_info(i)); |
436 | } |
437 | |
438 | minfo.return_val = p_method->get_return_info(); |
439 | minfo.flags = p_method->get_hint_flags(); |
440 | |
441 | for (int i = 0; i < p_method->get_argument_count(); i++) { |
442 | if (p_method->has_default_argument(i)) { |
443 | minfo.default_arguments.push_back(p_method->get_default_argument(i)); |
444 | } |
445 | } |
446 | |
447 | return minfo; |
448 | } |
449 | |
450 | void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) { |
451 | OBJTYPE_RLOCK; |
452 | |
453 | ClassInfo *type = classes.getptr(p_class); |
454 | |
455 | while (type) { |
456 | if (type->disabled) { |
457 | if (p_no_inheritance) { |
458 | break; |
459 | } |
460 | |
461 | type = type->inherits_ptr; |
462 | continue; |
463 | } |
464 | |
465 | #ifdef DEBUG_METHODS_ENABLED |
466 | |
467 | for (const MethodInfo &E : type->virtual_methods) { |
468 | p_methods->push_back(E); |
469 | } |
470 | |
471 | for (const StringName &E : type->method_order) { |
472 | if (p_exclude_from_properties && type->methods_in_properties.has(E)) { |
473 | continue; |
474 | } |
475 | |
476 | MethodBind *method = type->method_map.get(E); |
477 | MethodInfo minfo = info_from_bind(method); |
478 | |
479 | p_methods->push_back(minfo); |
480 | } |
481 | |
482 | #else |
483 | |
484 | for (KeyValue<StringName, MethodBind *> &E : type->method_map) { |
485 | MethodBind *m = E.value; |
486 | MethodInfo minfo = info_from_bind(m); |
487 | p_methods->push_back(minfo); |
488 | } |
489 | |
490 | #endif |
491 | |
492 | if (p_no_inheritance) { |
493 | break; |
494 | } |
495 | |
496 | type = type->inherits_ptr; |
497 | } |
498 | } |
499 | |
500 | bool ClassDB::get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) { |
501 | OBJTYPE_RLOCK; |
502 | |
503 | ClassInfo *type = classes.getptr(p_class); |
504 | |
505 | while (type) { |
506 | if (type->disabled) { |
507 | if (p_no_inheritance) { |
508 | break; |
509 | } |
510 | |
511 | type = type->inherits_ptr; |
512 | continue; |
513 | } |
514 | |
515 | #ifdef DEBUG_METHODS_ENABLED |
516 | MethodBind **method = type->method_map.getptr(p_method); |
517 | if (method && *method) { |
518 | if (r_info != nullptr) { |
519 | MethodInfo minfo = info_from_bind(*method); |
520 | *r_info = minfo; |
521 | } |
522 | return true; |
523 | } else if (type->virtual_methods_map.has(p_method)) { |
524 | if (r_info) { |
525 | *r_info = type->virtual_methods_map[p_method]; |
526 | } |
527 | return true; |
528 | } |
529 | #else |
530 | if (type->method_map.has(p_method)) { |
531 | if (r_info) { |
532 | MethodBind *m = type->method_map[p_method]; |
533 | MethodInfo minfo = info_from_bind(m); |
534 | *r_info = minfo; |
535 | } |
536 | return true; |
537 | } |
538 | #endif |
539 | |
540 | if (p_no_inheritance) { |
541 | break; |
542 | } |
543 | |
544 | type = type->inherits_ptr; |
545 | } |
546 | |
547 | return false; |
548 | } |
549 | |
550 | MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_name) { |
551 | OBJTYPE_RLOCK; |
552 | |
553 | ClassInfo *type = classes.getptr(p_class); |
554 | |
555 | while (type) { |
556 | MethodBind **method = type->method_map.getptr(p_name); |
557 | if (method && *method) { |
558 | return *method; |
559 | } |
560 | type = type->inherits_ptr; |
561 | } |
562 | return nullptr; |
563 | } |
564 | |
565 | Vector<uint32_t> ClassDB::get_method_compatibility_hashes(const StringName &p_class, const StringName &p_name) { |
566 | OBJTYPE_RLOCK; |
567 | |
568 | ClassInfo *type = classes.getptr(p_class); |
569 | |
570 | while (type) { |
571 | if (type->method_map_compatibility.has(p_name)) { |
572 | LocalVector<MethodBind *> *c = type->method_map_compatibility.getptr(p_name); |
573 | Vector<uint32_t> ret; |
574 | for (uint32_t i = 0; i < c->size(); i++) { |
575 | ret.push_back((*c)[i]->get_hash()); |
576 | } |
577 | return ret; |
578 | } |
579 | type = type->inherits_ptr; |
580 | } |
581 | return Vector<uint32_t>(); |
582 | } |
583 | |
584 | MethodBind *ClassDB::get_method_with_compatibility(const StringName &p_class, const StringName &p_name, uint64_t p_hash, bool *r_method_exists, bool *r_is_deprecated) { |
585 | OBJTYPE_RLOCK; |
586 | |
587 | ClassInfo *type = classes.getptr(p_class); |
588 | |
589 | while (type) { |
590 | MethodBind **method = type->method_map.getptr(p_name); |
591 | if (method && *method) { |
592 | if (r_method_exists) { |
593 | *r_method_exists = true; |
594 | } |
595 | if ((*method)->get_hash() == p_hash) { |
596 | return *method; |
597 | } |
598 | } |
599 | |
600 | LocalVector<MethodBind *> *compat = type->method_map_compatibility.getptr(p_name); |
601 | if (compat) { |
602 | if (r_method_exists) { |
603 | *r_method_exists = true; |
604 | } |
605 | for (uint32_t i = 0; i < compat->size(); i++) { |
606 | if ((*compat)[i]->get_hash() == p_hash) { |
607 | if (r_is_deprecated) { |
608 | *r_is_deprecated = true; |
609 | } |
610 | return (*compat)[i]; |
611 | } |
612 | } |
613 | } |
614 | type = type->inherits_ptr; |
615 | } |
616 | return nullptr; |
617 | } |
618 | |
619 | void ClassDB::bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield) { |
620 | OBJTYPE_WLOCK; |
621 | |
622 | ClassInfo *type = classes.getptr(p_class); |
623 | |
624 | ERR_FAIL_NULL(type); |
625 | |
626 | if (type->constant_map.has(p_name)) { |
627 | ERR_FAIL(); |
628 | } |
629 | |
630 | type->constant_map[p_name] = p_constant; |
631 | |
632 | String enum_name = p_enum; |
633 | if (!enum_name.is_empty()) { |
634 | if (enum_name.contains("." )) { |
635 | enum_name = enum_name.get_slicec('.', 1); |
636 | } |
637 | |
638 | ClassInfo::EnumInfo *constants_list = type->enum_map.getptr(enum_name); |
639 | |
640 | if (constants_list) { |
641 | constants_list->constants.push_back(p_name); |
642 | constants_list->is_bitfield = p_is_bitfield; |
643 | } else { |
644 | ClassInfo::EnumInfo new_list; |
645 | new_list.is_bitfield = p_is_bitfield; |
646 | new_list.constants.push_back(p_name); |
647 | type->enum_map[enum_name] = new_list; |
648 | } |
649 | } |
650 | |
651 | #ifdef DEBUG_METHODS_ENABLED |
652 | type->constant_order.push_back(p_name); |
653 | #endif |
654 | } |
655 | |
656 | void ClassDB::get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance) { |
657 | OBJTYPE_RLOCK; |
658 | |
659 | ClassInfo *type = classes.getptr(p_class); |
660 | |
661 | while (type) { |
662 | #ifdef DEBUG_METHODS_ENABLED |
663 | for (const StringName &E : type->constant_order) { |
664 | p_constants->push_back(E); |
665 | } |
666 | #else |
667 | |
668 | for (const KeyValue<StringName, int64_t> &E : type->constant_map) { |
669 | p_constants->push_back(E.key); |
670 | } |
671 | |
672 | #endif |
673 | if (p_no_inheritance) { |
674 | break; |
675 | } |
676 | |
677 | type = type->inherits_ptr; |
678 | } |
679 | } |
680 | |
681 | int64_t ClassDB::get_integer_constant(const StringName &p_class, const StringName &p_name, bool *p_success) { |
682 | OBJTYPE_RLOCK; |
683 | |
684 | ClassInfo *type = classes.getptr(p_class); |
685 | |
686 | while (type) { |
687 | int64_t *constant = type->constant_map.getptr(p_name); |
688 | if (constant) { |
689 | if (p_success) { |
690 | *p_success = true; |
691 | } |
692 | return *constant; |
693 | } |
694 | |
695 | type = type->inherits_ptr; |
696 | } |
697 | |
698 | if (p_success) { |
699 | *p_success = false; |
700 | } |
701 | |
702 | return 0; |
703 | } |
704 | |
705 | bool ClassDB::has_integer_constant(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { |
706 | OBJTYPE_RLOCK; |
707 | |
708 | ClassInfo *type = classes.getptr(p_class); |
709 | |
710 | while (type) { |
711 | if (type->constant_map.has(p_name)) { |
712 | return true; |
713 | } |
714 | if (p_no_inheritance) { |
715 | return false; |
716 | } |
717 | |
718 | type = type->inherits_ptr; |
719 | } |
720 | |
721 | return false; |
722 | } |
723 | |
724 | StringName ClassDB::get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { |
725 | OBJTYPE_RLOCK; |
726 | |
727 | ClassInfo *type = classes.getptr(p_class); |
728 | |
729 | while (type) { |
730 | for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) { |
731 | List<StringName> &constants_list = E.value.constants; |
732 | const List<StringName>::Element *found = constants_list.find(p_name); |
733 | if (found) { |
734 | return E.key; |
735 | } |
736 | } |
737 | |
738 | if (p_no_inheritance) { |
739 | break; |
740 | } |
741 | |
742 | type = type->inherits_ptr; |
743 | } |
744 | |
745 | return StringName(); |
746 | } |
747 | |
748 | void ClassDB::get_enum_list(const StringName &p_class, List<StringName> *p_enums, bool p_no_inheritance) { |
749 | OBJTYPE_RLOCK; |
750 | |
751 | ClassInfo *type = classes.getptr(p_class); |
752 | |
753 | while (type) { |
754 | for (KeyValue<StringName, ClassInfo::EnumInfo> &E : type->enum_map) { |
755 | p_enums->push_back(E.key); |
756 | } |
757 | |
758 | if (p_no_inheritance) { |
759 | break; |
760 | } |
761 | |
762 | type = type->inherits_ptr; |
763 | } |
764 | } |
765 | |
766 | void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_enum, List<StringName> *p_constants, bool p_no_inheritance) { |
767 | OBJTYPE_RLOCK; |
768 | |
769 | ClassInfo *type = classes.getptr(p_class); |
770 | |
771 | while (type) { |
772 | const ClassInfo::EnumInfo *constants = type->enum_map.getptr(p_enum); |
773 | |
774 | if (constants) { |
775 | for (const List<StringName>::Element *E = constants->constants.front(); E; E = E->next()) { |
776 | p_constants->push_back(E->get()); |
777 | } |
778 | } |
779 | |
780 | if (p_no_inheritance) { |
781 | break; |
782 | } |
783 | |
784 | type = type->inherits_ptr; |
785 | } |
786 | } |
787 | |
788 | void ClassDB::set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values) { |
789 | OBJTYPE_RLOCK; |
790 | #ifdef DEBUG_METHODS_ENABLED |
791 | ClassInfo *type = classes.getptr(p_class); |
792 | |
793 | ERR_FAIL_NULL(type); |
794 | |
795 | type->method_error_values[p_method] = p_values; |
796 | #endif |
797 | } |
798 | |
799 | Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class, const StringName &p_method) { |
800 | #ifdef DEBUG_METHODS_ENABLED |
801 | ClassInfo *type = classes.getptr(p_class); |
802 | |
803 | ERR_FAIL_NULL_V(type, Vector<Error>()); |
804 | |
805 | if (!type->method_error_values.has(p_method)) { |
806 | return Vector<Error>(); |
807 | } |
808 | return type->method_error_values[p_method]; |
809 | #else |
810 | return Vector<Error>(); |
811 | #endif |
812 | } |
813 | |
814 | bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { |
815 | OBJTYPE_RLOCK; |
816 | |
817 | ClassInfo *type = classes.getptr(p_class); |
818 | |
819 | while (type) { |
820 | if (type->enum_map.has(p_name)) { |
821 | return true; |
822 | } |
823 | if (p_no_inheritance) { |
824 | return false; |
825 | } |
826 | |
827 | type = type->inherits_ptr; |
828 | } |
829 | |
830 | return false; |
831 | } |
832 | |
833 | bool ClassDB::is_enum_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) { |
834 | OBJTYPE_RLOCK; |
835 | |
836 | ClassInfo *type = classes.getptr(p_class); |
837 | |
838 | while (type) { |
839 | if (type->enum_map.has(p_name) && type->enum_map[p_name].is_bitfield) { |
840 | return true; |
841 | } |
842 | if (p_no_inheritance) { |
843 | return false; |
844 | } |
845 | |
846 | type = type->inherits_ptr; |
847 | } |
848 | |
849 | return false; |
850 | } |
851 | |
852 | void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) { |
853 | OBJTYPE_WLOCK; |
854 | |
855 | ClassInfo *type = classes.getptr(p_class); |
856 | ERR_FAIL_NULL(type); |
857 | |
858 | StringName sname = p_signal.name; |
859 | |
860 | #ifdef DEBUG_METHODS_ENABLED |
861 | ClassInfo *check = type; |
862 | while (check) { |
863 | ERR_FAIL_COND_MSG(check->signal_map.has(sname), "Class '" + String(p_class) + "' already has signal '" + String(sname) + "'." ); |
864 | check = check->inherits_ptr; |
865 | } |
866 | #endif |
867 | |
868 | type->signal_map[sname] = p_signal; |
869 | } |
870 | |
871 | void ClassDB::get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) { |
872 | OBJTYPE_RLOCK; |
873 | |
874 | ClassInfo *type = classes.getptr(p_class); |
875 | ERR_FAIL_NULL(type); |
876 | |
877 | ClassInfo *check = type; |
878 | |
879 | while (check) { |
880 | for (KeyValue<StringName, MethodInfo> &E : check->signal_map) { |
881 | p_signals->push_back(E.value); |
882 | } |
883 | |
884 | if (p_no_inheritance) { |
885 | return; |
886 | } |
887 | |
888 | check = check->inherits_ptr; |
889 | } |
890 | } |
891 | |
892 | bool ClassDB::has_signal(const StringName &p_class, const StringName &p_signal, bool p_no_inheritance) { |
893 | OBJTYPE_RLOCK; |
894 | ClassInfo *type = classes.getptr(p_class); |
895 | ClassInfo *check = type; |
896 | while (check) { |
897 | if (check->signal_map.has(p_signal)) { |
898 | return true; |
899 | } |
900 | if (p_no_inheritance) { |
901 | return false; |
902 | } |
903 | check = check->inherits_ptr; |
904 | } |
905 | |
906 | return false; |
907 | } |
908 | |
909 | bool ClassDB::get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal) { |
910 | OBJTYPE_RLOCK; |
911 | ClassInfo *type = classes.getptr(p_class); |
912 | ClassInfo *check = type; |
913 | while (check) { |
914 | if (check->signal_map.has(p_signal)) { |
915 | if (r_signal) { |
916 | *r_signal = check->signal_map[p_signal]; |
917 | } |
918 | return true; |
919 | } |
920 | check = check->inherits_ptr; |
921 | } |
922 | |
923 | return false; |
924 | } |
925 | |
926 | void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) { |
927 | OBJTYPE_WLOCK; |
928 | ClassInfo *type = classes.getptr(p_class); |
929 | ERR_FAIL_NULL(type); |
930 | |
931 | String prefix = p_prefix; |
932 | if (p_indent_depth > 0) { |
933 | prefix = vformat("%s,%d" , p_prefix, p_indent_depth); |
934 | } |
935 | |
936 | type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, prefix, PROPERTY_USAGE_GROUP)); |
937 | } |
938 | |
939 | void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix, int p_indent_depth) { |
940 | OBJTYPE_WLOCK; |
941 | ClassInfo *type = classes.getptr(p_class); |
942 | ERR_FAIL_NULL(type); |
943 | |
944 | String prefix = p_prefix; |
945 | if (p_indent_depth > 0) { |
946 | prefix = vformat("%s,%d" , p_prefix, p_indent_depth); |
947 | } |
948 | |
949 | type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, prefix, PROPERTY_USAGE_SUBGROUP)); |
950 | } |
951 | |
952 | void ClassDB::add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage) { |
953 | add_property(p_class, PropertyInfo(Variant::INT, p_count_property, PROPERTY_HINT_NONE, "" , p_count_usage | PROPERTY_USAGE_ARRAY, vformat("%s,%s" , p_label, p_array_element_prefix)), p_count_setter, p_count_getter); |
954 | } |
955 | |
956 | void ClassDB::add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix) { |
957 | OBJTYPE_WLOCK; |
958 | ClassInfo *type = classes.getptr(p_class); |
959 | ERR_FAIL_NULL(type); |
960 | |
961 | type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix)); |
962 | } |
963 | |
964 | // NOTE: For implementation simplicity reasons, this method doesn't allow setters to have optional arguments at the end. |
965 | void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) { |
966 | lock.read_lock(); |
967 | ClassInfo *type = classes.getptr(p_class); |
968 | lock.read_unlock(); |
969 | |
970 | ERR_FAIL_NULL(type); |
971 | |
972 | MethodBind *mb_set = nullptr; |
973 | if (p_setter) { |
974 | mb_set = get_method(p_class, p_setter); |
975 | #ifdef DEBUG_METHODS_ENABLED |
976 | |
977 | ERR_FAIL_NULL_MSG(mb_set, "Invalid setter '" + p_class + "::" + p_setter + "' for property '" + p_pinfo.name + "'." ); |
978 | |
979 | int exp_args = 1 + (p_index >= 0 ? 1 : 0); |
980 | ERR_FAIL_COND_MSG(mb_set->get_argument_count() != exp_args, "Invalid function for setter '" + p_class + "::" + p_setter + " for property '" + p_pinfo.name + "'." ); |
981 | #endif |
982 | } |
983 | |
984 | MethodBind *mb_get = nullptr; |
985 | if (p_getter) { |
986 | mb_get = get_method(p_class, p_getter); |
987 | #ifdef DEBUG_METHODS_ENABLED |
988 | |
989 | ERR_FAIL_NULL_MSG(mb_get, "Invalid getter '" + p_class + "::" + p_getter + "' for property '" + p_pinfo.name + "'." ); |
990 | |
991 | int exp_args = 0 + (p_index >= 0 ? 1 : 0); |
992 | ERR_FAIL_COND_MSG(mb_get->get_argument_count() != exp_args, "Invalid function for getter '" + p_class + "::" + p_getter + "' for property: '" + p_pinfo.name + "'." ); |
993 | #endif |
994 | } |
995 | |
996 | #ifdef DEBUG_METHODS_ENABLED |
997 | ERR_FAIL_COND_MSG(type->property_setget.has(p_pinfo.name), "Object '" + p_class + "' already has property '" + p_pinfo.name + "'." ); |
998 | #endif |
999 | |
1000 | OBJTYPE_WLOCK |
1001 | |
1002 | type->property_list.push_back(p_pinfo); |
1003 | type->property_map[p_pinfo.name] = p_pinfo; |
1004 | #ifdef DEBUG_METHODS_ENABLED |
1005 | if (mb_get) { |
1006 | type->methods_in_properties.insert(p_getter); |
1007 | } |
1008 | if (mb_set) { |
1009 | type->methods_in_properties.insert(p_setter); |
1010 | } |
1011 | #endif |
1012 | PropertySetGet psg; |
1013 | psg.setter = p_setter; |
1014 | psg.getter = p_getter; |
1015 | psg._setptr = mb_set; |
1016 | psg._getptr = mb_get; |
1017 | psg.index = p_index; |
1018 | psg.type = p_pinfo.type; |
1019 | |
1020 | type->property_setget[p_pinfo.name] = psg; |
1021 | } |
1022 | |
1023 | void ClassDB::set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default) { |
1024 | if (!default_values.has(p_class)) { |
1025 | default_values[p_class] = HashMap<StringName, Variant>(); |
1026 | } |
1027 | default_values[p_class][p_name] = p_default; |
1028 | } |
1029 | |
1030 | void ClassDB::add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property) { |
1031 | #ifdef TOOLS_ENABLED |
1032 | OBJTYPE_WLOCK; |
1033 | ClassInfo *type = classes.getptr(p_class); |
1034 | ERR_FAIL_NULL(type); |
1035 | |
1036 | ERR_FAIL_COND(!type->property_map.has(p_property)); |
1037 | ERR_FAIL_COND(!type->property_map.has(p_linked_property)); |
1038 | |
1039 | if (!type->linked_properties.has(p_property)) { |
1040 | type->linked_properties.insert(p_property, List<StringName>()); |
1041 | } |
1042 | type->linked_properties[p_property].push_back(p_linked_property); |
1043 | |
1044 | #endif |
1045 | } |
1046 | |
1047 | void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) { |
1048 | OBJTYPE_RLOCK; |
1049 | |
1050 | ClassInfo *type = classes.getptr(p_class); |
1051 | ClassInfo *check = type; |
1052 | while (check) { |
1053 | for (const PropertyInfo &pi : check->property_list) { |
1054 | if (p_validator) { |
1055 | // Making a copy as we may modify it. |
1056 | PropertyInfo pi_mut = pi; |
1057 | p_validator->validate_property(pi_mut); |
1058 | p_list->push_back(pi_mut); |
1059 | } else { |
1060 | p_list->push_back(pi); |
1061 | } |
1062 | } |
1063 | |
1064 | if (p_no_inheritance) { |
1065 | return; |
1066 | } |
1067 | check = check->inherits_ptr; |
1068 | } |
1069 | } |
1070 | |
1071 | void ClassDB::get_linked_properties_info(const StringName &p_class, const StringName &p_property, List<StringName> *r_properties, bool p_no_inheritance) { |
1072 | #ifdef TOOLS_ENABLED |
1073 | ClassInfo *check = classes.getptr(p_class); |
1074 | while (check) { |
1075 | if (!check->linked_properties.has(p_property)) { |
1076 | return; |
1077 | } |
1078 | for (const StringName &E : check->linked_properties[p_property]) { |
1079 | r_properties->push_back(E); |
1080 | } |
1081 | |
1082 | if (p_no_inheritance) { |
1083 | break; |
1084 | } |
1085 | check = check->inherits_ptr; |
1086 | } |
1087 | #endif |
1088 | } |
1089 | |
1090 | bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) { |
1091 | OBJTYPE_RLOCK; |
1092 | |
1093 | ClassInfo *check = classes.getptr(p_class); |
1094 | while (check) { |
1095 | if (check->property_map.has(p_property)) { |
1096 | PropertyInfo pinfo = check->property_map[p_property]; |
1097 | if (p_validator) { |
1098 | p_validator->validate_property(pinfo); |
1099 | } |
1100 | if (r_info) { |
1101 | *r_info = pinfo; |
1102 | } |
1103 | return true; |
1104 | } |
1105 | if (p_no_inheritance) { |
1106 | break; |
1107 | } |
1108 | check = check->inherits_ptr; |
1109 | } |
1110 | |
1111 | return false; |
1112 | } |
1113 | |
1114 | bool ClassDB::set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid) { |
1115 | ERR_FAIL_NULL_V(p_object, false); |
1116 | |
1117 | ClassInfo *type = classes.getptr(p_object->get_class_name()); |
1118 | ClassInfo *check = type; |
1119 | while (check) { |
1120 | const PropertySetGet *psg = check->property_setget.getptr(p_property); |
1121 | if (psg) { |
1122 | if (!psg->setter) { |
1123 | if (r_valid) { |
1124 | *r_valid = false; |
1125 | } |
1126 | return true; //return true but do nothing |
1127 | } |
1128 | |
1129 | Callable::CallError ce; |
1130 | |
1131 | if (psg->index >= 0) { |
1132 | Variant index = psg->index; |
1133 | const Variant *arg[2] = { &index, &p_value }; |
1134 | //p_object->call(psg->setter,arg,2,ce); |
1135 | if (psg->_setptr) { |
1136 | psg->_setptr->call(p_object, arg, 2, ce); |
1137 | } else { |
1138 | p_object->callp(psg->setter, arg, 2, ce); |
1139 | } |
1140 | |
1141 | } else { |
1142 | const Variant *arg[1] = { &p_value }; |
1143 | if (psg->_setptr) { |
1144 | psg->_setptr->call(p_object, arg, 1, ce); |
1145 | } else { |
1146 | p_object->callp(psg->setter, arg, 1, ce); |
1147 | } |
1148 | } |
1149 | |
1150 | if (r_valid) { |
1151 | *r_valid = ce.error == Callable::CallError::CALL_OK; |
1152 | } |
1153 | |
1154 | return true; |
1155 | } |
1156 | |
1157 | check = check->inherits_ptr; |
1158 | } |
1159 | |
1160 | return false; |
1161 | } |
1162 | |
1163 | bool ClassDB::get_property(Object *p_object, const StringName &p_property, Variant &r_value) { |
1164 | ERR_FAIL_NULL_V(p_object, false); |
1165 | |
1166 | ClassInfo *type = classes.getptr(p_object->get_class_name()); |
1167 | ClassInfo *check = type; |
1168 | while (check) { |
1169 | const PropertySetGet *psg = check->property_setget.getptr(p_property); |
1170 | if (psg) { |
1171 | if (!psg->getter) { |
1172 | return true; //return true but do nothing |
1173 | } |
1174 | |
1175 | if (psg->index >= 0) { |
1176 | Variant index = psg->index; |
1177 | const Variant *arg[1] = { &index }; |
1178 | Callable::CallError ce; |
1179 | r_value = p_object->callp(psg->getter, arg, 1, ce); |
1180 | |
1181 | } else { |
1182 | Callable::CallError ce; |
1183 | if (psg->_getptr) { |
1184 | r_value = psg->_getptr->call(p_object, nullptr, 0, ce); |
1185 | } else { |
1186 | r_value = p_object->callp(psg->getter, nullptr, 0, ce); |
1187 | } |
1188 | } |
1189 | return true; |
1190 | } |
1191 | |
1192 | const int64_t *c = check->constant_map.getptr(p_property); //constants count |
1193 | if (c) { |
1194 | r_value = *c; |
1195 | return true; |
1196 | } |
1197 | |
1198 | if (check->method_map.has(p_property)) { //methods count |
1199 | r_value = Callable(p_object, p_property); |
1200 | return true; |
1201 | } |
1202 | |
1203 | if (check->signal_map.has(p_property)) { //signals count |
1204 | r_value = Signal(p_object, p_property); |
1205 | return true; |
1206 | } |
1207 | |
1208 | check = check->inherits_ptr; |
1209 | } |
1210 | |
1211 | return false; |
1212 | } |
1213 | |
1214 | int ClassDB::get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid) { |
1215 | ClassInfo *type = classes.getptr(p_class); |
1216 | ClassInfo *check = type; |
1217 | while (check) { |
1218 | const PropertySetGet *psg = check->property_setget.getptr(p_property); |
1219 | if (psg) { |
1220 | if (r_is_valid) { |
1221 | *r_is_valid = true; |
1222 | } |
1223 | |
1224 | return psg->index; |
1225 | } |
1226 | |
1227 | check = check->inherits_ptr; |
1228 | } |
1229 | if (r_is_valid) { |
1230 | *r_is_valid = false; |
1231 | } |
1232 | |
1233 | return -1; |
1234 | } |
1235 | |
1236 | Variant::Type ClassDB::get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid) { |
1237 | ClassInfo *type = classes.getptr(p_class); |
1238 | ClassInfo *check = type; |
1239 | while (check) { |
1240 | const PropertySetGet *psg = check->property_setget.getptr(p_property); |
1241 | if (psg) { |
1242 | if (r_is_valid) { |
1243 | *r_is_valid = true; |
1244 | } |
1245 | |
1246 | return psg->type; |
1247 | } |
1248 | |
1249 | check = check->inherits_ptr; |
1250 | } |
1251 | if (r_is_valid) { |
1252 | *r_is_valid = false; |
1253 | } |
1254 | |
1255 | return Variant::NIL; |
1256 | } |
1257 | |
1258 | StringName ClassDB::get_property_setter(const StringName &p_class, const StringName &p_property) { |
1259 | ClassInfo *type = classes.getptr(p_class); |
1260 | ClassInfo *check = type; |
1261 | while (check) { |
1262 | const PropertySetGet *psg = check->property_setget.getptr(p_property); |
1263 | if (psg) { |
1264 | return psg->setter; |
1265 | } |
1266 | |
1267 | check = check->inherits_ptr; |
1268 | } |
1269 | |
1270 | return StringName(); |
1271 | } |
1272 | |
1273 | StringName ClassDB::get_property_getter(const StringName &p_class, const StringName &p_property) { |
1274 | ClassInfo *type = classes.getptr(p_class); |
1275 | ClassInfo *check = type; |
1276 | while (check) { |
1277 | const PropertySetGet *psg = check->property_setget.getptr(p_property); |
1278 | if (psg) { |
1279 | return psg->getter; |
1280 | } |
1281 | |
1282 | check = check->inherits_ptr; |
1283 | } |
1284 | |
1285 | return StringName(); |
1286 | } |
1287 | |
1288 | bool ClassDB::has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance) { |
1289 | ClassInfo *type = classes.getptr(p_class); |
1290 | ClassInfo *check = type; |
1291 | while (check) { |
1292 | if (check->property_setget.has(p_property)) { |
1293 | return true; |
1294 | } |
1295 | |
1296 | if (p_no_inheritance) { |
1297 | break; |
1298 | } |
1299 | check = check->inherits_ptr; |
1300 | } |
1301 | |
1302 | return false; |
1303 | } |
1304 | |
1305 | void ClassDB::set_method_flags(const StringName &p_class, const StringName &p_method, int p_flags) { |
1306 | OBJTYPE_WLOCK; |
1307 | ClassInfo *type = classes.getptr(p_class); |
1308 | ClassInfo *check = type; |
1309 | ERR_FAIL_NULL(check); |
1310 | ERR_FAIL_COND(!check->method_map.has(p_method)); |
1311 | check->method_map[p_method]->set_hint_flags(p_flags); |
1312 | } |
1313 | |
1314 | bool ClassDB::has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) { |
1315 | ClassInfo *type = classes.getptr(p_class); |
1316 | ClassInfo *check = type; |
1317 | while (check) { |
1318 | if (check->method_map.has(p_method)) { |
1319 | return true; |
1320 | } |
1321 | if (p_no_inheritance) { |
1322 | return false; |
1323 | } |
1324 | check = check->inherits_ptr; |
1325 | } |
1326 | |
1327 | return false; |
1328 | } |
1329 | |
1330 | void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method) { |
1331 | _bind_method_custom(p_class, p_method, false); |
1332 | } |
1333 | void ClassDB::bind_compatibility_method_custom(const StringName &p_class, MethodBind *p_method) { |
1334 | _bind_method_custom(p_class, p_method, true); |
1335 | } |
1336 | |
1337 | void ClassDB::_bind_compatibility(ClassInfo *type, MethodBind *p_method) { |
1338 | if (!type->method_map_compatibility.has(p_method->get_name())) { |
1339 | type->method_map_compatibility.insert(p_method->get_name(), LocalVector<MethodBind *>()); |
1340 | } |
1341 | type->method_map_compatibility[p_method->get_name()].push_back(p_method); |
1342 | } |
1343 | |
1344 | void ClassDB::_bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility) { |
1345 | ClassInfo *type = classes.getptr(p_class); |
1346 | if (!type) { |
1347 | ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'." ); |
1348 | } |
1349 | |
1350 | if (p_compatibility) { |
1351 | _bind_compatibility(type, p_method); |
1352 | return; |
1353 | } |
1354 | |
1355 | if (type->method_map.has(p_method->get_name())) { |
1356 | // overloading not supported |
1357 | ERR_FAIL_MSG("Method already bound '" + p_class + "::" + p_method->get_name() + "'." ); |
1358 | } |
1359 | |
1360 | #ifdef DEBUG_METHODS_ENABLED |
1361 | type->method_order.push_back(p_method->get_name()); |
1362 | #endif |
1363 | |
1364 | type->method_map[p_method->get_name()] = p_method; |
1365 | } |
1366 | |
1367 | MethodBind *ClassDB::_bind_vararg_method(MethodBind *p_bind, const StringName &p_name, const Vector<Variant> &p_default_args, bool p_compatibility) { |
1368 | MethodBind *bind = p_bind; |
1369 | bind->set_name(p_name); |
1370 | bind->set_default_arguments(p_default_args); |
1371 | |
1372 | String instance_type = bind->get_instance_class(); |
1373 | |
1374 | ClassInfo *type = classes.getptr(instance_type); |
1375 | if (!type) { |
1376 | memdelete(bind); |
1377 | ERR_FAIL_NULL_V(type, nullptr); |
1378 | } |
1379 | |
1380 | if (p_compatibility) { |
1381 | _bind_compatibility(type, bind); |
1382 | return bind; |
1383 | } |
1384 | |
1385 | if (type->method_map.has(p_name)) { |
1386 | memdelete(bind); |
1387 | // Overloading not supported |
1388 | ERR_FAIL_V_MSG(nullptr, "Method already bound: " + instance_type + "::" + p_name + "." ); |
1389 | } |
1390 | type->method_map[p_name] = bind; |
1391 | #ifdef DEBUG_METHODS_ENABLED |
1392 | // FIXME: <reduz> set_return_type is no longer in MethodBind, so I guess it should be moved to vararg method bind |
1393 | //bind->set_return_type("Variant"); |
1394 | type->method_order.push_back(p_name); |
1395 | #endif |
1396 | |
1397 | return bind; |
1398 | } |
1399 | |
1400 | #ifdef DEBUG_METHODS_ENABLED |
1401 | MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_compatibility, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) { |
1402 | StringName mdname = method_name.name; |
1403 | #else |
1404 | MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_compatibility, const char *method_name, const Variant **p_defs, int p_defcount) { |
1405 | StringName mdname = StaticCString::create(method_name); |
1406 | #endif |
1407 | |
1408 | OBJTYPE_WLOCK; |
1409 | ERR_FAIL_NULL_V(p_bind, nullptr); |
1410 | p_bind->set_name(mdname); |
1411 | |
1412 | String instance_type = p_bind->get_instance_class(); |
1413 | |
1414 | #ifdef DEBUG_ENABLED |
1415 | |
1416 | ERR_FAIL_COND_V_MSG(!p_compatibility && has_method(instance_type, mdname), nullptr, "Class " + String(instance_type) + " already has a method " + String(mdname) + "." ); |
1417 | #endif |
1418 | |
1419 | ClassInfo *type = classes.getptr(instance_type); |
1420 | if (!type) { |
1421 | memdelete(p_bind); |
1422 | ERR_FAIL_V_MSG(nullptr, "Couldn't bind method '" + mdname + "' for instance '" + instance_type + "'." ); |
1423 | } |
1424 | |
1425 | if (!p_compatibility && type->method_map.has(mdname)) { |
1426 | memdelete(p_bind); |
1427 | // overloading not supported |
1428 | ERR_FAIL_V_MSG(nullptr, "Method already bound '" + instance_type + "::" + mdname + "'." ); |
1429 | } |
1430 | |
1431 | #ifdef DEBUG_METHODS_ENABLED |
1432 | |
1433 | if (method_name.args.size() > p_bind->get_argument_count()) { |
1434 | memdelete(p_bind); |
1435 | ERR_FAIL_V_MSG(nullptr, "Method definition provides more arguments than the method actually has '" + instance_type + "::" + mdname + "'." ); |
1436 | } |
1437 | |
1438 | p_bind->set_argument_names(method_name.args); |
1439 | |
1440 | if (!p_compatibility) { |
1441 | type->method_order.push_back(mdname); |
1442 | } |
1443 | #endif |
1444 | |
1445 | if (p_compatibility) { |
1446 | _bind_compatibility(type, p_bind); |
1447 | } else { |
1448 | type->method_map[mdname] = p_bind; |
1449 | } |
1450 | |
1451 | Vector<Variant> defvals; |
1452 | |
1453 | defvals.resize(p_defcount); |
1454 | for (int i = 0; i < p_defcount; i++) { |
1455 | defvals.write[i] = *p_defs[i]; |
1456 | } |
1457 | |
1458 | p_bind->set_default_arguments(defvals); |
1459 | p_bind->set_hint_flags(p_flags); |
1460 | return p_bind; |
1461 | } |
1462 | |
1463 | void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual, const Vector<String> &p_arg_names, bool p_object_core) { |
1464 | ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'." ); |
1465 | |
1466 | OBJTYPE_WLOCK; |
1467 | |
1468 | #ifdef DEBUG_METHODS_ENABLED |
1469 | MethodInfo mi = p_method; |
1470 | if (p_virtual) { |
1471 | mi.flags |= METHOD_FLAG_VIRTUAL; |
1472 | } |
1473 | if (p_object_core) { |
1474 | mi.flags |= METHOD_FLAG_OBJECT_CORE; |
1475 | } |
1476 | |
1477 | if (!p_object_core) { |
1478 | if (p_arg_names.size() != mi.arguments.size()) { |
1479 | WARN_PRINT("Mismatch argument name count for virtual method: " + String(p_class) + "::" + p_method.name); |
1480 | } else { |
1481 | for (int i = 0; i < p_arg_names.size(); i++) { |
1482 | mi.arguments[i].name = p_arg_names[i]; |
1483 | } |
1484 | } |
1485 | } |
1486 | |
1487 | if (classes[p_class].virtual_methods_map.has(p_method.name)) { |
1488 | // overloading not supported |
1489 | ERR_FAIL_MSG("Virtual method already bound '" + String(p_class) + "::" + p_method.name + "'." ); |
1490 | } |
1491 | classes[p_class].virtual_methods.push_back(mi); |
1492 | classes[p_class].virtual_methods_map[p_method.name] = mi; |
1493 | |
1494 | #endif |
1495 | } |
1496 | |
1497 | void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance) { |
1498 | ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'." ); |
1499 | |
1500 | #ifdef DEBUG_METHODS_ENABLED |
1501 | |
1502 | ClassInfo *type = classes.getptr(p_class); |
1503 | ClassInfo *check = type; |
1504 | while (check) { |
1505 | for (const MethodInfo &E : check->virtual_methods) { |
1506 | p_methods->push_back(E); |
1507 | } |
1508 | |
1509 | if (p_no_inheritance) { |
1510 | return; |
1511 | } |
1512 | check = check->inherits_ptr; |
1513 | } |
1514 | |
1515 | #endif |
1516 | } |
1517 | |
1518 | void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) { |
1519 | OBJTYPE_WLOCK; |
1520 | |
1521 | ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'." ); |
1522 | classes[p_class].disabled = !p_enable; |
1523 | } |
1524 | |
1525 | bool ClassDB::is_class_enabled(const StringName &p_class) { |
1526 | OBJTYPE_RLOCK; |
1527 | |
1528 | ClassInfo *ti = classes.getptr(p_class); |
1529 | if (!ti || !ti->creation_func) { |
1530 | if (compat_classes.has(p_class)) { |
1531 | ti = classes.getptr(compat_classes[p_class]); |
1532 | } |
1533 | } |
1534 | |
1535 | ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'." ); |
1536 | return !ti->disabled; |
1537 | } |
1538 | |
1539 | bool ClassDB::is_class_exposed(const StringName &p_class) { |
1540 | OBJTYPE_RLOCK; |
1541 | |
1542 | ClassInfo *ti = classes.getptr(p_class); |
1543 | ERR_FAIL_NULL_V_MSG(ti, false, "Cannot get class '" + String(p_class) + "'." ); |
1544 | return ti->exposed; |
1545 | } |
1546 | |
1547 | void ClassDB::add_resource_base_extension(const StringName &p_extension, const StringName &p_class) { |
1548 | if (resource_base_extensions.has(p_extension)) { |
1549 | return; |
1550 | } |
1551 | |
1552 | resource_base_extensions[p_extension] = p_class; |
1553 | } |
1554 | |
1555 | void ClassDB::get_resource_base_extensions(List<String> *p_extensions) { |
1556 | for (const KeyValue<StringName, StringName> &E : resource_base_extensions) { |
1557 | p_extensions->push_back(E.key); |
1558 | } |
1559 | } |
1560 | |
1561 | bool ClassDB::is_resource_extension(const StringName &p_extension) { |
1562 | return resource_base_extensions.has(p_extension); |
1563 | } |
1564 | |
1565 | void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) { |
1566 | for (const KeyValue<StringName, StringName> &E : resource_base_extensions) { |
1567 | if (is_parent_class(p_class, E.value) || is_parent_class(E.value, p_class)) { |
1568 | p_extensions->push_back(E.key); |
1569 | } |
1570 | } |
1571 | } |
1572 | |
1573 | HashMap<StringName, HashMap<StringName, Variant>> ClassDB::default_values; |
1574 | HashSet<StringName> ClassDB::default_values_cached; |
1575 | |
1576 | Variant ClassDB::class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid) { |
1577 | if (!default_values_cached.has(p_class)) { |
1578 | if (!default_values.has(p_class)) { |
1579 | default_values[p_class] = HashMap<StringName, Variant>(); |
1580 | } |
1581 | |
1582 | Object *c = nullptr; |
1583 | bool cleanup_c = false; |
1584 | |
1585 | if (Engine::get_singleton()->has_singleton(p_class)) { |
1586 | c = Engine::get_singleton()->get_singleton_object(p_class); |
1587 | cleanup_c = false; |
1588 | } else if (ClassDB::can_instantiate(p_class) && !ClassDB::is_virtual(p_class)) { // Keep this condition in sync with doc_tools.cpp get_documentation_default_value. |
1589 | c = ClassDB::instantiate(p_class); |
1590 | cleanup_c = true; |
1591 | } |
1592 | |
1593 | if (c) { |
1594 | List<PropertyInfo> plist; |
1595 | c->get_property_list(&plist); |
1596 | for (const PropertyInfo &E : plist) { |
1597 | if (E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) { |
1598 | if (!default_values[p_class].has(E.name)) { |
1599 | Variant v = c->get(E.name); |
1600 | default_values[p_class][E.name] = v; |
1601 | } |
1602 | } |
1603 | } |
1604 | |
1605 | if (cleanup_c) { |
1606 | memdelete(c); |
1607 | } |
1608 | } |
1609 | |
1610 | default_values_cached.insert(p_class); |
1611 | } |
1612 | |
1613 | if (!default_values.has(p_class)) { |
1614 | if (r_valid != nullptr) { |
1615 | *r_valid = false; |
1616 | } |
1617 | return Variant(); |
1618 | } |
1619 | |
1620 | if (!default_values[p_class].has(p_property)) { |
1621 | if (r_valid != nullptr) { |
1622 | *r_valid = false; |
1623 | } |
1624 | return Variant(); |
1625 | } |
1626 | |
1627 | if (r_valid != nullptr) { |
1628 | *r_valid = true; |
1629 | } |
1630 | |
1631 | Variant var = default_values[p_class][p_property]; |
1632 | |
1633 | #ifdef DEBUG_ENABLED |
1634 | // Some properties may have an instantiated Object as default value, |
1635 | // (like Path2D's `curve` used to have), but that's not a good practice. |
1636 | // Instead, those properties should use PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT |
1637 | // to be auto-instantiated when created in the editor with the following method: |
1638 | // EditorNode::get_editor_data().instantiate_object_properties(obj); |
1639 | if (var.get_type() == Variant::OBJECT) { |
1640 | Object *obj = var.get_validated_object(); |
1641 | if (obj) { |
1642 | WARN_PRINT(vformat("Instantiated %s used as default value for %s's \"%s\" property." , obj->get_class(), p_class, p_property)); |
1643 | } |
1644 | } |
1645 | #endif |
1646 | |
1647 | return var; |
1648 | } |
1649 | |
1650 | void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { |
1651 | GLOBAL_LOCK_FUNCTION; |
1652 | |
1653 | ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name)); |
1654 | ERR_FAIL_COND_MSG(!classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name)); |
1655 | |
1656 | ClassInfo *parent = classes.getptr(p_extension->parent_class_name); |
1657 | |
1658 | ClassInfo c; |
1659 | c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION; |
1660 | c.gdextension = p_extension; |
1661 | c.name = p_extension->class_name; |
1662 | c.is_virtual = p_extension->is_virtual; |
1663 | if (!p_extension->is_abstract) { |
1664 | // Find the closest ancestor which is either non-abstract or native (or both). |
1665 | ClassInfo *concrete_ancestor = parent; |
1666 | while (concrete_ancestor->creation_func == nullptr && |
1667 | concrete_ancestor->inherits_ptr != nullptr && |
1668 | concrete_ancestor->gdextension != nullptr) { |
1669 | concrete_ancestor = concrete_ancestor->inherits_ptr; |
1670 | } |
1671 | ERR_FAIL_NULL_MSG(concrete_ancestor->creation_func, "Extension class " + String(p_extension->class_name) + " cannot extend native abstract class " + String(concrete_ancestor->name)); |
1672 | c.creation_func = concrete_ancestor->creation_func; |
1673 | } |
1674 | c.inherits = parent->name; |
1675 | c.class_ptr = parent->class_ptr; |
1676 | c.inherits_ptr = parent; |
1677 | c.exposed = p_extension->is_exposed; |
1678 | if (c.exposed) { |
1679 | // The parent classes should be exposed if it has an exposed child class. |
1680 | while (parent && !parent->exposed) { |
1681 | parent->exposed = true; |
1682 | parent = classes.getptr(parent->name); |
1683 | } |
1684 | } |
1685 | |
1686 | classes[p_extension->class_name] = c; |
1687 | } |
1688 | |
1689 | void ClassDB::unregister_extension_class(const StringName &p_class) { |
1690 | ClassInfo *c = classes.getptr(p_class); |
1691 | ERR_FAIL_NULL_MSG(c, "Class '" + String(p_class) + "' does not exist." ); |
1692 | for (KeyValue<StringName, MethodBind *> &F : c->method_map) { |
1693 | memdelete(F.value); |
1694 | } |
1695 | classes.erase(p_class); |
1696 | } |
1697 | |
1698 | HashMap<StringName, ClassDB::NativeStruct> ClassDB::native_structs; |
1699 | void ClassDB::register_native_struct(const StringName &p_name, const String &p_code, uint64_t p_current_size) { |
1700 | NativeStruct ns; |
1701 | ns.ccode = p_code; |
1702 | ns.struct_size = p_current_size; |
1703 | native_structs[p_name] = ns; |
1704 | } |
1705 | |
1706 | void ClassDB::get_native_struct_list(List<StringName> *r_names) { |
1707 | for (const KeyValue<StringName, NativeStruct> &E : native_structs) { |
1708 | r_names->push_back(E.key); |
1709 | } |
1710 | } |
1711 | |
1712 | String ClassDB::get_native_struct_code(const StringName &p_name) { |
1713 | ERR_FAIL_COND_V(!native_structs.has(p_name), String()); |
1714 | return native_structs[p_name].ccode; |
1715 | } |
1716 | |
1717 | uint64_t ClassDB::get_native_struct_size(const StringName &p_name) { |
1718 | ERR_FAIL_COND_V(!native_structs.has(p_name), 0); |
1719 | return native_structs[p_name].struct_size; |
1720 | } |
1721 | |
1722 | RWLock ClassDB::lock; |
1723 | |
1724 | void ClassDB::cleanup_defaults() { |
1725 | default_values.clear(); |
1726 | default_values_cached.clear(); |
1727 | } |
1728 | |
1729 | void ClassDB::cleanup() { |
1730 | //OBJTYPE_LOCK; hah not here |
1731 | |
1732 | for (KeyValue<StringName, ClassInfo> &E : classes) { |
1733 | ClassInfo &ti = E.value; |
1734 | |
1735 | for (KeyValue<StringName, MethodBind *> &F : ti.method_map) { |
1736 | memdelete(F.value); |
1737 | } |
1738 | for (KeyValue<StringName, LocalVector<MethodBind *>> &F : ti.method_map_compatibility) { |
1739 | for (uint32_t i = 0; i < F.value.size(); i++) { |
1740 | memdelete(F.value[i]); |
1741 | } |
1742 | } |
1743 | } |
1744 | |
1745 | classes.clear(); |
1746 | resource_base_extensions.clear(); |
1747 | compat_classes.clear(); |
1748 | native_structs.clear(); |
1749 | } |
1750 | |
1751 | // |
1752 | |