1 | /**************************************************************************/ |
2 | /* gdscript_utility_functions.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 "gdscript_utility_functions.h" |
32 | |
33 | #include "gdscript.h" |
34 | |
35 | #include "core/io/resource_loader.h" |
36 | #include "core/object/class_db.h" |
37 | #include "core/object/method_bind.h" |
38 | #include "core/object/object.h" |
39 | #include "core/templates/oa_hash_map.h" |
40 | #include "core/templates/vector.h" |
41 | #include "core/variant/typed_array.h" |
42 | |
43 | #ifdef DEBUG_ENABLED |
44 | |
45 | #define VALIDATE_ARG_COUNT(m_count) \ |
46 | if (p_arg_count < m_count) { \ |
47 | r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; \ |
48 | r_error.argument = m_count; \ |
49 | r_error.expected = m_count; \ |
50 | *r_ret = Variant(); \ |
51 | return; \ |
52 | } \ |
53 | if (p_arg_count > m_count) { \ |
54 | r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; \ |
55 | r_error.argument = m_count; \ |
56 | r_error.expected = m_count; \ |
57 | *r_ret = Variant(); \ |
58 | return; \ |
59 | } |
60 | |
61 | #define VALIDATE_ARG_INT(m_arg) \ |
62 | if (p_args[m_arg]->get_type() != Variant::INT) { \ |
63 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ |
64 | r_error.argument = m_arg; \ |
65 | r_error.expected = Variant::INT; \ |
66 | *r_ret = Variant(); \ |
67 | return; \ |
68 | } |
69 | |
70 | #define VALIDATE_ARG_NUM(m_arg) \ |
71 | if (!p_args[m_arg]->is_num()) { \ |
72 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; \ |
73 | r_error.argument = m_arg; \ |
74 | r_error.expected = Variant::FLOAT; \ |
75 | *r_ret = Variant(); \ |
76 | return; \ |
77 | } |
78 | |
79 | #else |
80 | |
81 | #define VALIDATE_ARG_COUNT(m_count) |
82 | #define VALIDATE_ARG_INT(m_arg) |
83 | #define VALIDATE_ARG_NUM(m_arg) |
84 | |
85 | #endif |
86 | |
87 | struct GDScriptUtilityFunctionsDefinitions { |
88 | #ifndef DISABLE_DEPRECATED |
89 | static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
90 | VALIDATE_ARG_COUNT(2); |
91 | VALIDATE_ARG_INT(1); |
92 | int type = *p_args[1]; |
93 | if (type < 0 || type >= Variant::VARIANT_MAX) { |
94 | *r_ret = RTR("Invalid type argument to convert(), use TYPE_* constants." ); |
95 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
96 | r_error.argument = 0; |
97 | r_error.expected = Variant::INT; |
98 | return; |
99 | |
100 | } else { |
101 | Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error); |
102 | } |
103 | } |
104 | #endif // DISABLE_DEPRECATED |
105 | |
106 | static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
107 | VALIDATE_ARG_COUNT(1); |
108 | *r_ret = ClassDB::class_exists(*p_args[0]); |
109 | } |
110 | |
111 | static inline void _char(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
112 | VALIDATE_ARG_COUNT(1); |
113 | VALIDATE_ARG_INT(0); |
114 | char32_t result[2] = { *p_args[0], 0 }; |
115 | *r_ret = String(result); |
116 | } |
117 | |
118 | static inline void range(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
119 | switch (p_arg_count) { |
120 | case 0: { |
121 | r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; |
122 | r_error.argument = 1; |
123 | r_error.expected = 1; |
124 | *r_ret = Variant(); |
125 | } break; |
126 | case 1: { |
127 | VALIDATE_ARG_NUM(0); |
128 | int count = *p_args[0]; |
129 | Array arr; |
130 | if (count <= 0) { |
131 | *r_ret = arr; |
132 | return; |
133 | } |
134 | Error err = arr.resize(count); |
135 | if (err != OK) { |
136 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; |
137 | *r_ret = Variant(); |
138 | return; |
139 | } |
140 | |
141 | for (int i = 0; i < count; i++) { |
142 | arr[i] = i; |
143 | } |
144 | |
145 | *r_ret = arr; |
146 | } break; |
147 | case 2: { |
148 | VALIDATE_ARG_NUM(0); |
149 | VALIDATE_ARG_NUM(1); |
150 | |
151 | int from = *p_args[0]; |
152 | int to = *p_args[1]; |
153 | |
154 | Array arr; |
155 | if (from >= to) { |
156 | *r_ret = arr; |
157 | return; |
158 | } |
159 | Error err = arr.resize(to - from); |
160 | if (err != OK) { |
161 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; |
162 | *r_ret = Variant(); |
163 | return; |
164 | } |
165 | for (int i = from; i < to; i++) { |
166 | arr[i - from] = i; |
167 | } |
168 | *r_ret = arr; |
169 | } break; |
170 | case 3: { |
171 | VALIDATE_ARG_NUM(0); |
172 | VALIDATE_ARG_NUM(1); |
173 | VALIDATE_ARG_NUM(2); |
174 | |
175 | int from = *p_args[0]; |
176 | int to = *p_args[1]; |
177 | int incr = *p_args[2]; |
178 | if (incr == 0) { |
179 | *r_ret = RTR("Step argument is zero!" ); |
180 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; |
181 | return; |
182 | } |
183 | |
184 | Array arr; |
185 | if (from >= to && incr > 0) { |
186 | *r_ret = arr; |
187 | return; |
188 | } |
189 | if (from <= to && incr < 0) { |
190 | *r_ret = arr; |
191 | return; |
192 | } |
193 | |
194 | // Calculate how many. |
195 | int count = 0; |
196 | if (incr > 0) { |
197 | count = ((to - from - 1) / incr) + 1; |
198 | } else { |
199 | count = ((from - to - 1) / -incr) + 1; |
200 | } |
201 | |
202 | Error err = arr.resize(count); |
203 | |
204 | if (err != OK) { |
205 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; |
206 | *r_ret = Variant(); |
207 | return; |
208 | } |
209 | |
210 | if (incr > 0) { |
211 | int idx = 0; |
212 | for (int i = from; i < to; i += incr) { |
213 | arr[idx++] = i; |
214 | } |
215 | } else { |
216 | int idx = 0; |
217 | for (int i = from; i > to; i += incr) { |
218 | arr[idx++] = i; |
219 | } |
220 | } |
221 | |
222 | *r_ret = arr; |
223 | } break; |
224 | default: { |
225 | r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; |
226 | r_error.argument = 3; |
227 | r_error.expected = 3; |
228 | *r_ret = Variant(); |
229 | |
230 | } break; |
231 | } |
232 | } |
233 | |
234 | static inline void load(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
235 | VALIDATE_ARG_COUNT(1); |
236 | if (p_args[0]->get_type() != Variant::STRING) { |
237 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
238 | r_error.argument = 0; |
239 | r_error.expected = Variant::STRING; |
240 | *r_ret = Variant(); |
241 | } else { |
242 | *r_ret = ResourceLoader::load(*p_args[0]); |
243 | } |
244 | } |
245 | |
246 | static inline void inst_to_dict(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
247 | VALIDATE_ARG_COUNT(1); |
248 | |
249 | if (p_args[0]->get_type() == Variant::NIL) { |
250 | *r_ret = Variant(); |
251 | } else if (p_args[0]->get_type() != Variant::OBJECT) { |
252 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
253 | r_error.argument = 0; |
254 | *r_ret = Variant(); |
255 | } else { |
256 | Object *obj = *p_args[0]; |
257 | if (!obj) { |
258 | *r_ret = Variant(); |
259 | |
260 | } else if (!obj->get_script_instance() || obj->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) { |
261 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
262 | r_error.argument = 0; |
263 | r_error.expected = Variant::DICTIONARY; |
264 | *r_ret = RTR("Not a script with an instance" ); |
265 | return; |
266 | } else { |
267 | GDScriptInstance *ins = static_cast<GDScriptInstance *>(obj->get_script_instance()); |
268 | Ref<GDScript> base = ins->get_script(); |
269 | if (base.is_null()) { |
270 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
271 | r_error.argument = 0; |
272 | r_error.expected = Variant::DICTIONARY; |
273 | *r_ret = RTR("Not based on a script" ); |
274 | return; |
275 | } |
276 | |
277 | GDScript *p = base.ptr(); |
278 | String path = p->get_script_path(); |
279 | Vector<StringName> sname; |
280 | |
281 | while (p->_owner) { |
282 | sname.push_back(p->local_name); |
283 | p = p->_owner; |
284 | } |
285 | sname.reverse(); |
286 | |
287 | if (!path.is_resource_file()) { |
288 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
289 | r_error.argument = 0; |
290 | r_error.expected = Variant::DICTIONARY; |
291 | *r_ret = Variant(); |
292 | |
293 | *r_ret = RTR("Not based on a resource file" ); |
294 | |
295 | return; |
296 | } |
297 | |
298 | NodePath cp(sname, Vector<StringName>(), false); |
299 | |
300 | Dictionary d; |
301 | d["@subpath" ] = cp; |
302 | d["@path" ] = path; |
303 | |
304 | for (const KeyValue<StringName, GDScript::MemberInfo> &E : base->member_indices) { |
305 | if (!d.has(E.key)) { |
306 | d[E.key] = ins->members[E.value.index]; |
307 | } |
308 | } |
309 | *r_ret = d; |
310 | } |
311 | } |
312 | } |
313 | |
314 | static inline void dict_to_inst(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
315 | VALIDATE_ARG_COUNT(1); |
316 | |
317 | if (p_args[0]->get_type() != Variant::DICTIONARY) { |
318 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
319 | r_error.argument = 0; |
320 | r_error.expected = Variant::DICTIONARY; |
321 | *r_ret = Variant(); |
322 | |
323 | return; |
324 | } |
325 | |
326 | Dictionary d = *p_args[0]; |
327 | |
328 | if (!d.has("@path" )) { |
329 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
330 | r_error.argument = 0; |
331 | r_error.expected = Variant::OBJECT; |
332 | *r_ret = RTR("Invalid instance dictionary format (missing @path)" ); |
333 | |
334 | return; |
335 | } |
336 | |
337 | Ref<Script> scr = ResourceLoader::load(d["@path" ]); |
338 | if (!scr.is_valid()) { |
339 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
340 | r_error.argument = 0; |
341 | r_error.expected = Variant::OBJECT; |
342 | *r_ret = RTR("Invalid instance dictionary format (can't load script at @path)" ); |
343 | return; |
344 | } |
345 | |
346 | Ref<GDScript> gdscr = scr; |
347 | |
348 | if (!gdscr.is_valid()) { |
349 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
350 | r_error.argument = 0; |
351 | r_error.expected = Variant::OBJECT; |
352 | *r_ret = Variant(); |
353 | *r_ret = RTR("Invalid instance dictionary format (invalid script at @path)" ); |
354 | return; |
355 | } |
356 | |
357 | NodePath sub; |
358 | if (d.has("@subpath" )) { |
359 | sub = d["@subpath" ]; |
360 | } |
361 | |
362 | for (int i = 0; i < sub.get_name_count(); i++) { |
363 | gdscr = gdscr->subclasses[sub.get_name(i)]; |
364 | if (!gdscr.is_valid()) { |
365 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
366 | r_error.argument = 0; |
367 | r_error.expected = Variant::OBJECT; |
368 | *r_ret = Variant(); |
369 | *r_ret = RTR("Invalid instance dictionary (invalid subclasses)" ); |
370 | return; |
371 | } |
372 | } |
373 | *r_ret = gdscr->_new(nullptr, -1 /*skip initializer*/, r_error); |
374 | |
375 | if (r_error.error != Callable::CallError::CALL_OK) { |
376 | *r_ret = Variant(); |
377 | return; |
378 | } |
379 | |
380 | GDScriptInstance *ins = static_cast<GDScriptInstance *>(static_cast<Object *>(*r_ret)->get_script_instance()); |
381 | Ref<GDScript> gd_ref = ins->get_script(); |
382 | |
383 | for (KeyValue<StringName, GDScript::MemberInfo> &E : gd_ref->member_indices) { |
384 | if (d.has(E.key)) { |
385 | ins->members.write[E.value.index] = d[E.key]; |
386 | } |
387 | } |
388 | } |
389 | |
390 | static inline void Color8(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
391 | if (p_arg_count < 3) { |
392 | r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; |
393 | r_error.argument = 3; |
394 | *r_ret = Variant(); |
395 | return; |
396 | } |
397 | if (p_arg_count > 4) { |
398 | r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; |
399 | r_error.argument = 4; |
400 | *r_ret = Variant(); |
401 | return; |
402 | } |
403 | |
404 | VALIDATE_ARG_INT(0); |
405 | VALIDATE_ARG_INT(1); |
406 | VALIDATE_ARG_INT(2); |
407 | |
408 | Color color((int64_t)*p_args[0] / 255.0f, (int64_t)*p_args[1] / 255.0f, (int64_t)*p_args[2] / 255.0f); |
409 | |
410 | if (p_arg_count == 4) { |
411 | VALIDATE_ARG_INT(3); |
412 | color.a = (int64_t)*p_args[3] / 255.0f; |
413 | } |
414 | |
415 | *r_ret = color; |
416 | } |
417 | |
418 | static inline void print_debug(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
419 | String s; |
420 | for (int i = 0; i < p_arg_count; i++) { |
421 | s += p_args[i]->operator String(); |
422 | } |
423 | |
424 | if (Thread::get_caller_id() == Thread::get_main_id()) { |
425 | ScriptLanguage *script = GDScriptLanguage::get_singleton(); |
426 | if (script->debug_get_stack_level_count() > 0) { |
427 | s += "\n At: " + script->debug_get_stack_level_source(0) + ":" + itos(script->debug_get_stack_level_line(0)) + ":" + script->debug_get_stack_level_function(0) + "()" ; |
428 | } |
429 | } else { |
430 | s += "\n At: Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id()); |
431 | } |
432 | |
433 | print_line(s); |
434 | *r_ret = Variant(); |
435 | } |
436 | |
437 | static inline void print_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
438 | VALIDATE_ARG_COUNT(0); |
439 | if (Thread::get_caller_id() != Thread::get_main_id()) { |
440 | print_line("Cannot retrieve debug info outside the main thread. Thread ID: " + itos(Thread::get_caller_id())); |
441 | return; |
442 | } |
443 | |
444 | ScriptLanguage *script = GDScriptLanguage::get_singleton(); |
445 | for (int i = 0; i < script->debug_get_stack_level_count(); i++) { |
446 | print_line("Frame " + itos(i) + " - " + script->debug_get_stack_level_source(i) + ":" + itos(script->debug_get_stack_level_line(i)) + " in function '" + script->debug_get_stack_level_function(i) + "'" ); |
447 | }; |
448 | *r_ret = Variant(); |
449 | } |
450 | |
451 | static inline void get_stack(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
452 | VALIDATE_ARG_COUNT(0); |
453 | if (Thread::get_caller_id() != Thread::get_main_id()) { |
454 | *r_ret = TypedArray<Dictionary>(); |
455 | return; |
456 | } |
457 | |
458 | ScriptLanguage *script = GDScriptLanguage::get_singleton(); |
459 | TypedArray<Dictionary> ret; |
460 | for (int i = 0; i < script->debug_get_stack_level_count(); i++) { |
461 | Dictionary frame; |
462 | frame["source" ] = script->debug_get_stack_level_source(i); |
463 | frame["function" ] = script->debug_get_stack_level_function(i); |
464 | frame["line" ] = script->debug_get_stack_level_line(i); |
465 | ret.push_back(frame); |
466 | }; |
467 | *r_ret = ret; |
468 | } |
469 | |
470 | static inline void len(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
471 | VALIDATE_ARG_COUNT(1); |
472 | switch (p_args[0]->get_type()) { |
473 | case Variant::STRING: { |
474 | String d = *p_args[0]; |
475 | *r_ret = d.length(); |
476 | } break; |
477 | case Variant::DICTIONARY: { |
478 | Dictionary d = *p_args[0]; |
479 | *r_ret = d.size(); |
480 | } break; |
481 | case Variant::ARRAY: { |
482 | Array d = *p_args[0]; |
483 | *r_ret = d.size(); |
484 | } break; |
485 | case Variant::PACKED_BYTE_ARRAY: { |
486 | Vector<uint8_t> d = *p_args[0]; |
487 | *r_ret = d.size(); |
488 | } break; |
489 | case Variant::PACKED_INT32_ARRAY: { |
490 | Vector<int32_t> d = *p_args[0]; |
491 | *r_ret = d.size(); |
492 | } break; |
493 | case Variant::PACKED_INT64_ARRAY: { |
494 | Vector<int64_t> d = *p_args[0]; |
495 | *r_ret = d.size(); |
496 | } break; |
497 | case Variant::PACKED_FLOAT32_ARRAY: { |
498 | Vector<float> d = *p_args[0]; |
499 | *r_ret = d.size(); |
500 | } break; |
501 | case Variant::PACKED_FLOAT64_ARRAY: { |
502 | Vector<double> d = *p_args[0]; |
503 | *r_ret = d.size(); |
504 | } break; |
505 | case Variant::PACKED_STRING_ARRAY: { |
506 | Vector<String> d = *p_args[0]; |
507 | *r_ret = d.size(); |
508 | } break; |
509 | case Variant::PACKED_VECTOR2_ARRAY: { |
510 | Vector<Vector2> d = *p_args[0]; |
511 | *r_ret = d.size(); |
512 | } break; |
513 | case Variant::PACKED_VECTOR3_ARRAY: { |
514 | Vector<Vector3> d = *p_args[0]; |
515 | *r_ret = d.size(); |
516 | } break; |
517 | case Variant::PACKED_COLOR_ARRAY: { |
518 | Vector<Color> d = *p_args[0]; |
519 | *r_ret = d.size(); |
520 | } break; |
521 | default: { |
522 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
523 | r_error.argument = 0; |
524 | r_error.expected = Variant::NIL; |
525 | *r_ret = vformat(RTR("Value of type '%s' can't provide a length." ), Variant::get_type_name(p_args[0]->get_type())); |
526 | } |
527 | } |
528 | } |
529 | |
530 | static inline void is_instance_of(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { |
531 | VALIDATE_ARG_COUNT(2); |
532 | |
533 | if (p_args[1]->get_type() == Variant::INT) { |
534 | int builtin_type = *p_args[1]; |
535 | if (builtin_type < 0 || builtin_type >= Variant::VARIANT_MAX) { |
536 | *r_ret = RTR("Invalid type argument for is_instance_of(), use TYPE_* constants for built-in types." ); |
537 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
538 | r_error.argument = 1; |
539 | r_error.expected = Variant::NIL; |
540 | return; |
541 | } |
542 | *r_ret = p_args[0]->get_type() == builtin_type; |
543 | return; |
544 | } |
545 | |
546 | bool was_type_freed = false; |
547 | Object *type_object = p_args[1]->get_validated_object_with_check(was_type_freed); |
548 | if (was_type_freed) { |
549 | *r_ret = RTR("Type argument is a previously freed instance." ); |
550 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
551 | r_error.argument = 1; |
552 | r_error.expected = Variant::NIL; |
553 | return; |
554 | } |
555 | if (!type_object) { |
556 | *r_ret = RTR("Invalid type argument for is_instance_of(), should be a TYPE_* constant, a class or a script." ); |
557 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
558 | r_error.argument = 1; |
559 | r_error.expected = Variant::NIL; |
560 | return; |
561 | } |
562 | |
563 | bool was_value_freed = false; |
564 | Object *value_object = p_args[0]->get_validated_object_with_check(was_value_freed); |
565 | if (was_value_freed) { |
566 | *r_ret = RTR("Value argument is a previously freed instance." ); |
567 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
568 | r_error.argument = 0; |
569 | r_error.expected = Variant::NIL; |
570 | return; |
571 | } |
572 | if (!value_object) { |
573 | *r_ret = false; |
574 | return; |
575 | } |
576 | |
577 | GDScriptNativeClass *native_type = Object::cast_to<GDScriptNativeClass>(type_object); |
578 | if (native_type) { |
579 | *r_ret = ClassDB::is_parent_class(value_object->get_class_name(), native_type->get_name()); |
580 | return; |
581 | } |
582 | |
583 | Script *script_type = Object::cast_to<Script>(type_object); |
584 | if (script_type) { |
585 | bool result = false; |
586 | if (value_object->get_script_instance()) { |
587 | Script *script_ptr = value_object->get_script_instance()->get_script().ptr(); |
588 | while (script_ptr) { |
589 | if (script_ptr == script_type) { |
590 | result = true; |
591 | break; |
592 | } |
593 | script_ptr = script_ptr->get_base_script().ptr(); |
594 | } |
595 | } |
596 | *r_ret = result; |
597 | return; |
598 | } |
599 | |
600 | *r_ret = RTR("Invalid type argument for is_instance_of(), should be a TYPE_* constant, a class or a script." ); |
601 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; |
602 | r_error.argument = 1; |
603 | r_error.expected = Variant::NIL; |
604 | } |
605 | }; |
606 | |
607 | struct GDScriptUtilityFunctionInfo { |
608 | GDScriptUtilityFunctions::FunctionPtr function = nullptr; |
609 | MethodInfo info; |
610 | bool is_constant = false; |
611 | }; |
612 | |
613 | static OAHashMap<StringName, GDScriptUtilityFunctionInfo> utility_function_table; |
614 | static List<StringName> utility_function_name_table; |
615 | |
616 | static void _register_function(const String &p_name, const MethodInfo &p_method_info, GDScriptUtilityFunctions::FunctionPtr p_function, bool p_is_const) { |
617 | StringName sname(p_name); |
618 | |
619 | ERR_FAIL_COND(utility_function_table.has(sname)); |
620 | |
621 | GDScriptUtilityFunctionInfo function; |
622 | function.function = p_function; |
623 | function.info = p_method_info; |
624 | function.is_constant = p_is_const; |
625 | |
626 | utility_function_table.insert(sname, function); |
627 | utility_function_name_table.push_back(sname); |
628 | } |
629 | |
630 | #define REGISTER_FUNC(m_func, m_is_const, m_return_type, ...) \ |
631 | { \ |
632 | String name(#m_func); \ |
633 | if (name.begins_with("_")) { \ |
634 | name = name.substr(1, name.length() - 1); \ |
635 | } \ |
636 | MethodInfo info = MethodInfo(name, __VA_ARGS__); \ |
637 | info.return_val.type = m_return_type; \ |
638 | _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ |
639 | } |
640 | |
641 | #define REGISTER_FUNC_NO_ARGS(m_func, m_is_const, m_return_type) \ |
642 | { \ |
643 | String name(#m_func); \ |
644 | if (name.begins_with("_")) { \ |
645 | name = name.substr(1, name.length() - 1); \ |
646 | } \ |
647 | MethodInfo info = MethodInfo(name); \ |
648 | info.return_val.type = m_return_type; \ |
649 | _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ |
650 | } |
651 | |
652 | #define REGISTER_VARARG_FUNC(m_func, m_is_const, m_return_type) \ |
653 | { \ |
654 | String name(#m_func); \ |
655 | if (name.begins_with("_")) { \ |
656 | name = name.substr(1, name.length() - 1); \ |
657 | } \ |
658 | MethodInfo info = MethodInfo(name); \ |
659 | info.return_val.type = m_return_type; \ |
660 | info.flags |= METHOD_FLAG_VARARG; \ |
661 | _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ |
662 | } |
663 | |
664 | #define REGISTER_VARIANT_FUNC(m_func, m_is_const, ...) \ |
665 | { \ |
666 | String name(#m_func); \ |
667 | if (name.begins_with("_")) { \ |
668 | name = name.substr(1, name.length() - 1); \ |
669 | } \ |
670 | MethodInfo info = MethodInfo(name, __VA_ARGS__); \ |
671 | info.return_val.type = Variant::NIL; \ |
672 | info.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; \ |
673 | _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ |
674 | } |
675 | |
676 | #define REGISTER_CLASS_FUNC(m_func, m_is_const, m_return_type, ...) \ |
677 | { \ |
678 | String name(#m_func); \ |
679 | if (name.begins_with("_")) { \ |
680 | name = name.substr(1, name.length() - 1); \ |
681 | } \ |
682 | MethodInfo info = MethodInfo(name, __VA_ARGS__); \ |
683 | info.return_val.type = Variant::OBJECT; \ |
684 | info.return_val.hint = PROPERTY_HINT_RESOURCE_TYPE; \ |
685 | info.return_val.class_name = m_return_type; \ |
686 | _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ |
687 | } |
688 | |
689 | #define REGISTER_FUNC_DEF(m_func, m_is_const, m_default, m_return_type, ...) \ |
690 | { \ |
691 | String name(#m_func); \ |
692 | if (name.begins_with("_")) { \ |
693 | name = name.substr(1, name.length() - 1); \ |
694 | } \ |
695 | MethodInfo info = MethodInfo(name, __VA_ARGS__); \ |
696 | info.return_val.type = m_return_type; \ |
697 | info.default_arguments.push_back(m_default); \ |
698 | _register_function(name, info, GDScriptUtilityFunctionsDefinitions::m_func, m_is_const); \ |
699 | } |
700 | |
701 | #define ARG(m_name, m_type) \ |
702 | PropertyInfo(m_type, m_name) |
703 | |
704 | #define VARARG(m_name) \ |
705 | PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT) |
706 | |
707 | void GDScriptUtilityFunctions::register_functions() { |
708 | #ifndef DISABLE_DEPRECATED |
709 | REGISTER_VARIANT_FUNC(convert, true, VARARG("what" ), ARG("type" , Variant::INT)); |
710 | #endif // DISABLE_DEPRECATED |
711 | REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type" , Variant::STRING_NAME)); |
712 | REGISTER_FUNC(_char, true, Variant::STRING, ARG("char" , Variant::INT)); |
713 | REGISTER_VARARG_FUNC(range, false, Variant::ARRAY); |
714 | REGISTER_CLASS_FUNC(load, false, "Resource" , ARG("path" , Variant::STRING)); |
715 | REGISTER_FUNC(inst_to_dict, false, Variant::DICTIONARY, ARG("instance" , Variant::OBJECT)); |
716 | REGISTER_FUNC(dict_to_inst, false, Variant::OBJECT, ARG("dictionary" , Variant::DICTIONARY)); |
717 | REGISTER_FUNC_DEF(Color8, true, 255, Variant::COLOR, ARG("r8" , Variant::INT), ARG("g8" , Variant::INT), ARG("b8" , Variant::INT), ARG("a8" , Variant::INT)); |
718 | REGISTER_VARARG_FUNC(print_debug, false, Variant::NIL); |
719 | REGISTER_FUNC_NO_ARGS(print_stack, false, Variant::NIL); |
720 | REGISTER_FUNC_NO_ARGS(get_stack, false, Variant::ARRAY); |
721 | REGISTER_FUNC(len, true, Variant::INT, VARARG("var" )); |
722 | REGISTER_FUNC(is_instance_of, true, Variant::BOOL, VARARG("value" ), VARARG("type" )); |
723 | } |
724 | |
725 | void GDScriptUtilityFunctions::unregister_functions() { |
726 | utility_function_name_table.clear(); |
727 | utility_function_table.clear(); |
728 | } |
729 | |
730 | GDScriptUtilityFunctions::FunctionPtr GDScriptUtilityFunctions::get_function(const StringName &p_function) { |
731 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
732 | ERR_FAIL_COND_V(!info, nullptr); |
733 | return info->function; |
734 | } |
735 | |
736 | bool GDScriptUtilityFunctions::has_function_return_value(const StringName &p_function) { |
737 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
738 | ERR_FAIL_COND_V(!info, false); |
739 | return info->info.return_val.type != Variant::NIL || bool(info->info.return_val.usage & PROPERTY_USAGE_NIL_IS_VARIANT); |
740 | } |
741 | |
742 | Variant::Type GDScriptUtilityFunctions::get_function_return_type(const StringName &p_function) { |
743 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
744 | ERR_FAIL_COND_V(!info, Variant::NIL); |
745 | return info->info.return_val.type; |
746 | } |
747 | |
748 | StringName GDScriptUtilityFunctions::get_function_return_class(const StringName &p_function) { |
749 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
750 | ERR_FAIL_COND_V(!info, StringName()); |
751 | return info->info.return_val.class_name; |
752 | } |
753 | |
754 | Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringName &p_function, int p_arg) { |
755 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
756 | ERR_FAIL_COND_V(!info, Variant::NIL); |
757 | ERR_FAIL_COND_V(p_arg >= info->info.arguments.size(), Variant::NIL); |
758 | return info->info.arguments[p_arg].type; |
759 | } |
760 | |
761 | int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) { |
762 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
763 | ERR_FAIL_COND_V(!info, 0); |
764 | return info->info.arguments.size(); |
765 | } |
766 | |
767 | bool GDScriptUtilityFunctions::is_function_vararg(const StringName &p_function) { |
768 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
769 | ERR_FAIL_COND_V(!info, false); |
770 | return (bool)(info->info.flags & METHOD_FLAG_VARARG); |
771 | } |
772 | |
773 | bool GDScriptUtilityFunctions::is_function_constant(const StringName &p_function) { |
774 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
775 | ERR_FAIL_COND_V(!info, false); |
776 | return info->is_constant; |
777 | } |
778 | |
779 | bool GDScriptUtilityFunctions::function_exists(const StringName &p_function) { |
780 | return utility_function_table.has(p_function); |
781 | } |
782 | |
783 | void GDScriptUtilityFunctions::get_function_list(List<StringName> *r_functions) { |
784 | for (const StringName &E : utility_function_name_table) { |
785 | r_functions->push_back(E); |
786 | } |
787 | } |
788 | |
789 | MethodInfo GDScriptUtilityFunctions::get_function_info(const StringName &p_function) { |
790 | GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function); |
791 | ERR_FAIL_COND_V(!info, MethodInfo()); |
792 | return info->info; |
793 | } |
794 | |