1 | /**************************************************************************/ |
2 | /* gdscript.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.h" |
32 | |
33 | #include "gdscript_analyzer.h" |
34 | #include "gdscript_cache.h" |
35 | #include "gdscript_compiler.h" |
36 | #include "gdscript_parser.h" |
37 | #include "gdscript_rpc_callable.h" |
38 | #include "gdscript_warning.h" |
39 | |
40 | #ifdef TOOLS_ENABLED |
41 | #include "editor/gdscript_docgen.h" |
42 | #endif |
43 | |
44 | #ifdef TESTS_ENABLED |
45 | #include "tests/gdscript_test_runner.h" |
46 | #endif |
47 | |
48 | #include "core/config/engine.h" |
49 | #include "core/config/project_settings.h" |
50 | #include "core/core_constants.h" |
51 | #include "core/core_string_names.h" |
52 | #include "core/io/file_access.h" |
53 | #include "core/io/file_access_encrypted.h" |
54 | #include "core/os/os.h" |
55 | |
56 | #ifdef TOOLS_ENABLED |
57 | #include "editor/editor_paths.h" |
58 | #endif |
59 | |
60 | #include <stdint.h> |
61 | |
62 | /////////////////////////// |
63 | |
64 | GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) { |
65 | name = p_name; |
66 | } |
67 | |
68 | bool GDScriptNativeClass::_get(const StringName &p_name, Variant &r_ret) const { |
69 | bool ok; |
70 | int64_t v = ClassDB::get_integer_constant(name, p_name, &ok); |
71 | |
72 | if (ok) { |
73 | r_ret = v; |
74 | return true; |
75 | } else { |
76 | return false; |
77 | } |
78 | } |
79 | |
80 | void GDScriptNativeClass::_bind_methods() { |
81 | ClassDB::bind_method(D_METHOD("new" ), &GDScriptNativeClass::_new); |
82 | } |
83 | |
84 | Variant GDScriptNativeClass::_new() { |
85 | Object *o = instantiate(); |
86 | ERR_FAIL_COND_V_MSG(!o, Variant(), "Class type: '" + String(name) + "' is not instantiable." ); |
87 | |
88 | RefCounted *rc = Object::cast_to<RefCounted>(o); |
89 | if (rc) { |
90 | return Ref<RefCounted>(rc); |
91 | } else { |
92 | return o; |
93 | } |
94 | } |
95 | |
96 | Object *GDScriptNativeClass::instantiate() { |
97 | return ClassDB::instantiate(name); |
98 | } |
99 | |
100 | Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { |
101 | if (p_method == SNAME("new" )) { |
102 | // Constructor. |
103 | return Object::callp(p_method, p_args, p_argcount, r_error); |
104 | } |
105 | MethodBind *method = ClassDB::get_method(name, p_method); |
106 | if (method && method->is_static()) { |
107 | // Native static method. |
108 | return method->call(nullptr, p_args, p_argcount, r_error); |
109 | } |
110 | |
111 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; |
112 | return Variant(); |
113 | } |
114 | |
115 | GDScriptFunction *GDScript::_super_constructor(GDScript *p_script) { |
116 | if (p_script->initializer) { |
117 | return p_script->initializer; |
118 | } else { |
119 | GDScript *base_src = p_script->_base; |
120 | if (base_src != nullptr) { |
121 | return _super_constructor(base_src); |
122 | } else { |
123 | return nullptr; |
124 | } |
125 | } |
126 | } |
127 | |
128 | void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error) { |
129 | GDScript *base_src = p_script->_base; |
130 | if (base_src != nullptr) { |
131 | _super_implicit_constructor(base_src, p_instance, r_error); |
132 | if (r_error.error != Callable::CallError::CALL_OK) { |
133 | return; |
134 | } |
135 | } |
136 | ERR_FAIL_NULL(p_script->implicit_initializer); |
137 | p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error); |
138 | } |
139 | |
140 | GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) { |
141 | /* STEP 1, CREATE */ |
142 | |
143 | GDScriptInstance *instance = memnew(GDScriptInstance); |
144 | instance->base_ref_counted = p_is_ref_counted; |
145 | instance->members.resize(member_indices.size()); |
146 | instance->script = Ref<GDScript>(this); |
147 | instance->owner = p_owner; |
148 | instance->owner_id = p_owner->get_instance_id(); |
149 | #ifdef DEBUG_ENABLED |
150 | //needed for hot reloading |
151 | for (const KeyValue<StringName, MemberInfo> &E : member_indices) { |
152 | instance->member_indices_cache[E.key] = E.value.index; |
153 | } |
154 | #endif |
155 | instance->owner->set_script_instance(instance); |
156 | |
157 | /* STEP 2, INITIALIZE AND CONSTRUCT */ |
158 | { |
159 | MutexLock lock(GDScriptLanguage::singleton->mutex); |
160 | instances.insert(instance->owner); |
161 | } |
162 | |
163 | _super_implicit_constructor(this, instance, r_error); |
164 | if (r_error.error != Callable::CallError::CALL_OK) { |
165 | instance->script = Ref<GDScript>(); |
166 | instance->owner->set_script_instance(nullptr); |
167 | { |
168 | MutexLock lock(GDScriptLanguage::singleton->mutex); |
169 | instances.erase(p_owner); |
170 | } |
171 | ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance." ); |
172 | } |
173 | |
174 | if (p_argcount < 0) { |
175 | return instance; |
176 | } |
177 | |
178 | initializer = _super_constructor(this); |
179 | if (initializer != nullptr) { |
180 | initializer->call(instance, p_args, p_argcount, r_error); |
181 | if (r_error.error != Callable::CallError::CALL_OK) { |
182 | instance->script = Ref<GDScript>(); |
183 | instance->owner->set_script_instance(nullptr); |
184 | { |
185 | MutexLock lock(GDScriptLanguage::singleton->mutex); |
186 | instances.erase(p_owner); |
187 | } |
188 | ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance." ); |
189 | } |
190 | } |
191 | //@TODO make thread safe |
192 | return instance; |
193 | } |
194 | |
195 | Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { |
196 | /* STEP 1, CREATE */ |
197 | |
198 | if (!valid) { |
199 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; |
200 | return Variant(); |
201 | } |
202 | |
203 | r_error.error = Callable::CallError::CALL_OK; |
204 | Ref<RefCounted> ref; |
205 | Object *owner = nullptr; |
206 | |
207 | GDScript *_baseptr = this; |
208 | while (_baseptr->_base) { |
209 | _baseptr = _baseptr->_base; |
210 | } |
211 | |
212 | ERR_FAIL_COND_V(_baseptr->native.is_null(), Variant()); |
213 | if (_baseptr->native.ptr()) { |
214 | owner = _baseptr->native->instantiate(); |
215 | } else { |
216 | owner = memnew(RefCounted); //by default, no base means use reference |
217 | } |
218 | ERR_FAIL_COND_V_MSG(!owner, Variant(), "Can't inherit from a virtual class." ); |
219 | |
220 | RefCounted *r = Object::cast_to<RefCounted>(owner); |
221 | if (r) { |
222 | ref = Ref<RefCounted>(r); |
223 | } |
224 | |
225 | GDScriptInstance *instance = _create_instance(p_args, p_argcount, owner, r != nullptr, r_error); |
226 | if (!instance) { |
227 | if (ref.is_null()) { |
228 | memdelete(owner); //no owner, sorry |
229 | } |
230 | return Variant(); |
231 | } |
232 | |
233 | if (ref.is_valid()) { |
234 | return ref; |
235 | } else { |
236 | return owner; |
237 | } |
238 | } |
239 | |
240 | bool GDScript::can_instantiate() const { |
241 | #ifdef TOOLS_ENABLED |
242 | return valid && (tool || ScriptServer::is_scripting_enabled()); |
243 | #else |
244 | return valid; |
245 | #endif |
246 | } |
247 | |
248 | Ref<Script> GDScript::get_base_script() const { |
249 | if (_base) { |
250 | return Ref<GDScript>(_base); |
251 | } else { |
252 | return Ref<Script>(); |
253 | } |
254 | } |
255 | |
256 | StringName GDScript::get_global_name() const { |
257 | return global_name; |
258 | } |
259 | |
260 | StringName GDScript::get_instance_base_type() const { |
261 | if (native.is_valid()) { |
262 | return native->get_name(); |
263 | } |
264 | if (base.is_valid() && base->is_valid()) { |
265 | return base->get_instance_base_type(); |
266 | } |
267 | return StringName(); |
268 | } |
269 | |
270 | struct _GDScriptMemberSort { |
271 | int index = 0; |
272 | StringName name; |
273 | _FORCE_INLINE_ bool operator<(const _GDScriptMemberSort &p_member) const { return index < p_member.index; } |
274 | }; |
275 | |
276 | #ifdef TOOLS_ENABLED |
277 | |
278 | void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { |
279 | placeholders.erase(p_placeholder); |
280 | } |
281 | #endif |
282 | |
283 | void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const { |
284 | const GDScript *current = this; |
285 | while (current) { |
286 | for (const KeyValue<StringName, GDScriptFunction *> &E : current->member_functions) { |
287 | r_list->push_back(E.value->get_method_info()); |
288 | } |
289 | |
290 | if (!p_include_base) { |
291 | return; |
292 | } |
293 | |
294 | current = current->_base; |
295 | } |
296 | } |
297 | |
298 | void GDScript::get_script_method_list(List<MethodInfo> *r_list) const { |
299 | _get_script_method_list(r_list, true); |
300 | } |
301 | |
302 | void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const { |
303 | const GDScript *sptr = this; |
304 | List<PropertyInfo> props; |
305 | |
306 | while (sptr) { |
307 | Vector<_GDScriptMemberSort> msort; |
308 | for (const KeyValue<StringName, MemberInfo> &E : sptr->member_indices) { |
309 | _GDScriptMemberSort ms; |
310 | ms.index = E.value.index; |
311 | ms.name = E.key; |
312 | msort.push_back(ms); |
313 | } |
314 | |
315 | msort.sort(); |
316 | msort.reverse(); |
317 | for (int i = 0; i < msort.size(); i++) { |
318 | props.push_front(sptr->member_indices[msort[i].name].property_info); |
319 | } |
320 | |
321 | #ifdef TOOLS_ENABLED |
322 | r_list->push_back(sptr->get_class_category()); |
323 | #endif // TOOLS_ENABLED |
324 | |
325 | for (const PropertyInfo &E : props) { |
326 | r_list->push_back(E); |
327 | } |
328 | |
329 | if (!p_include_base) { |
330 | break; |
331 | } |
332 | |
333 | props.clear(); |
334 | sptr = sptr->_base; |
335 | } |
336 | } |
337 | |
338 | void GDScript::get_script_property_list(List<PropertyInfo> *r_list) const { |
339 | _get_script_property_list(r_list, true); |
340 | } |
341 | |
342 | bool GDScript::has_method(const StringName &p_method) const { |
343 | return member_functions.has(p_method); |
344 | } |
345 | |
346 | MethodInfo GDScript::get_method_info(const StringName &p_method) const { |
347 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method); |
348 | if (!E) { |
349 | return MethodInfo(); |
350 | } |
351 | |
352 | return E->value->get_method_info(); |
353 | } |
354 | |
355 | bool GDScript::get_property_default_value(const StringName &p_property, Variant &r_value) const { |
356 | #ifdef TOOLS_ENABLED |
357 | |
358 | HashMap<StringName, Variant>::ConstIterator E = member_default_values_cache.find(p_property); |
359 | if (E) { |
360 | r_value = E->value; |
361 | return true; |
362 | } |
363 | |
364 | if (base_cache.is_valid()) { |
365 | return base_cache->get_property_default_value(p_property, r_value); |
366 | } |
367 | #endif |
368 | return false; |
369 | } |
370 | |
371 | ScriptInstance *GDScript::instance_create(Object *p_this) { |
372 | GDScript *top = this; |
373 | while (top->_base) { |
374 | top = top->_base; |
375 | } |
376 | |
377 | if (top->native.is_valid()) { |
378 | if (!ClassDB::is_parent_class(p_this->get_class_name(), top->native->get_name())) { |
379 | if (EngineDebugger::is_active()) { |
380 | GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be assigned to an object of type: '" + p_this->get_class() + "'" ); |
381 | } |
382 | ERR_FAIL_V_MSG(nullptr, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be assigned to an object of type '" + p_this->get_class() + "'" + "." ); |
383 | } |
384 | } |
385 | |
386 | Callable::CallError unchecked_error; |
387 | return _create_instance(nullptr, 0, p_this, Object::cast_to<RefCounted>(p_this) != nullptr, unchecked_error); |
388 | } |
389 | |
390 | PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this) { |
391 | #ifdef TOOLS_ENABLED |
392 | PlaceHolderScriptInstance *si = memnew(PlaceHolderScriptInstance(GDScriptLanguage::get_singleton(), Ref<Script>(this), p_this)); |
393 | placeholders.insert(si); |
394 | _update_exports(nullptr, false, si); |
395 | return si; |
396 | #else |
397 | return nullptr; |
398 | #endif |
399 | } |
400 | |
401 | bool GDScript::instance_has(const Object *p_this) const { |
402 | MutexLock lock(GDScriptLanguage::singleton->mutex); |
403 | |
404 | return instances.has((Object *)p_this); |
405 | } |
406 | |
407 | bool GDScript::has_source_code() const { |
408 | return !source.is_empty(); |
409 | } |
410 | |
411 | String GDScript::get_source_code() const { |
412 | return source; |
413 | } |
414 | |
415 | void GDScript::set_source_code(const String &p_code) { |
416 | if (source == p_code) { |
417 | return; |
418 | } |
419 | source = p_code; |
420 | #ifdef TOOLS_ENABLED |
421 | source_changed_cache = true; |
422 | #endif |
423 | } |
424 | |
425 | #ifdef TOOLS_ENABLED |
426 | void GDScript::_update_exports_values(HashMap<StringName, Variant> &values, List<PropertyInfo> &propnames) { |
427 | for (const KeyValue<StringName, Variant> &E : member_default_values_cache) { |
428 | values[E.key] = E.value; |
429 | } |
430 | |
431 | for (const PropertyInfo &E : members_cache) { |
432 | propnames.push_back(E); |
433 | } |
434 | |
435 | if (base_cache.is_valid()) { |
436 | base_cache->_update_exports_values(values, propnames); |
437 | } |
438 | } |
439 | |
440 | void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) { |
441 | if (_owner) { // Only the top-level class stores doc info |
442 | _owner->_add_doc(p_inner_class); |
443 | } else { // Remove old docs, add new |
444 | for (int i = 0; i < docs.size(); i++) { |
445 | if (docs[i].name == p_inner_class.name) { |
446 | docs.remove_at(i); |
447 | break; |
448 | } |
449 | } |
450 | docs.append(p_inner_class); |
451 | } |
452 | } |
453 | |
454 | void GDScript::_clear_doc() { |
455 | docs.clear(); |
456 | doc = DocData::ClassDoc(); |
457 | } |
458 | |
459 | String GDScript::get_class_icon_path() const { |
460 | return simplified_icon_path; |
461 | } |
462 | #endif |
463 | |
464 | bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderScriptInstance *p_instance_to_update) { |
465 | #ifdef TOOLS_ENABLED |
466 | |
467 | static Vector<GDScript *> base_caches; |
468 | if (!p_recursive_call) { |
469 | base_caches.clear(); |
470 | } |
471 | base_caches.append(this); |
472 | |
473 | bool changed = false; |
474 | |
475 | if (source_changed_cache) { |
476 | source_changed_cache = false; |
477 | changed = true; |
478 | |
479 | String basedir = path; |
480 | |
481 | if (basedir.is_empty()) { |
482 | basedir = get_path(); |
483 | } |
484 | |
485 | if (!basedir.is_empty()) { |
486 | basedir = basedir.get_base_dir(); |
487 | } |
488 | |
489 | GDScriptParser parser; |
490 | GDScriptAnalyzer analyzer(&parser); |
491 | Error err = parser.parse(source, path, false); |
492 | |
493 | if (err == OK && analyzer.analyze() == OK) { |
494 | const GDScriptParser::ClassNode *c = parser.get_tree(); |
495 | |
496 | if (base_cache.is_valid()) { |
497 | base_cache->inheriters_cache.erase(get_instance_id()); |
498 | base_cache = Ref<GDScript>(); |
499 | } |
500 | |
501 | GDScriptParser::DataType base_type = parser.get_tree()->base_type; |
502 | if (base_type.kind == GDScriptParser::DataType::CLASS) { |
503 | Ref<GDScript> bf = GDScriptCache::get_full_script(base_type.script_path, err, path); |
504 | if (err == OK) { |
505 | bf = Ref<GDScript>(bf->find_class(base_type.class_type->fqcn)); |
506 | if (bf.is_valid()) { |
507 | base_cache = bf; |
508 | bf->inheriters_cache.insert(get_instance_id()); |
509 | } |
510 | } |
511 | } |
512 | |
513 | members_cache.clear(); |
514 | member_default_values_cache.clear(); |
515 | _signals.clear(); |
516 | |
517 | members_cache.push_back(get_class_category()); |
518 | |
519 | for (int i = 0; i < c->members.size(); i++) { |
520 | const GDScriptParser::ClassNode::Member &member = c->members[i]; |
521 | |
522 | switch (member.type) { |
523 | case GDScriptParser::ClassNode::Member::VARIABLE: { |
524 | if (!member.variable->exported) { |
525 | continue; |
526 | } |
527 | |
528 | members_cache.push_back(member.variable->export_info); |
529 | Variant default_value = analyzer.make_variable_default_value(member.variable); |
530 | member_default_values_cache[member.variable->identifier->name] = default_value; |
531 | } break; |
532 | case GDScriptParser::ClassNode::Member::SIGNAL: { |
533 | _signals[member.signal->identifier->name] = member.signal->method_info; |
534 | } break; |
535 | case GDScriptParser::ClassNode::Member::GROUP: { |
536 | members_cache.push_back(member.annotation->export_info); |
537 | } break; |
538 | default: |
539 | break; // Nothing. |
540 | } |
541 | } |
542 | } else { |
543 | placeholder_fallback_enabled = true; |
544 | return false; |
545 | } |
546 | } else if (placeholder_fallback_enabled) { |
547 | return false; |
548 | } |
549 | |
550 | placeholder_fallback_enabled = false; |
551 | |
552 | if (base_cache.is_valid() && base_cache->is_valid()) { |
553 | for (int i = 0; i < base_caches.size(); i++) { |
554 | if (base_caches[i] == base_cache.ptr()) { |
555 | if (r_err) { |
556 | *r_err = true; |
557 | } |
558 | valid = false; // to show error in the editor |
559 | base_cache->valid = false; |
560 | base_cache->inheriters_cache.clear(); // to prevent future stackoverflows |
561 | base_cache.unref(); |
562 | base.unref(); |
563 | _base = nullptr; |
564 | ERR_FAIL_V_MSG(false, "Cyclic inheritance in script class." ); |
565 | } |
566 | } |
567 | if (base_cache->_update_exports(r_err, true)) { |
568 | if (r_err && *r_err) { |
569 | return false; |
570 | } |
571 | changed = true; |
572 | } |
573 | } |
574 | |
575 | if ((changed || p_instance_to_update) && placeholders.size()) { //hm :( |
576 | |
577 | // update placeholders if any |
578 | HashMap<StringName, Variant> values; |
579 | List<PropertyInfo> propnames; |
580 | _update_exports_values(values, propnames); |
581 | |
582 | if (changed) { |
583 | for (PlaceHolderScriptInstance *E : placeholders) { |
584 | E->update(propnames, values); |
585 | } |
586 | } else { |
587 | p_instance_to_update->update(propnames, values); |
588 | } |
589 | } |
590 | |
591 | return changed; |
592 | |
593 | #else |
594 | return false; |
595 | #endif |
596 | } |
597 | |
598 | void GDScript::update_exports() { |
599 | #ifdef TOOLS_ENABLED |
600 | |
601 | bool cyclic_error = false; |
602 | _update_exports(&cyclic_error); |
603 | if (cyclic_error) { |
604 | return; |
605 | } |
606 | |
607 | HashSet<ObjectID> copy = inheriters_cache; //might get modified |
608 | |
609 | for (const ObjectID &E : copy) { |
610 | Object *id = ObjectDB::get_instance(E); |
611 | GDScript *s = Object::cast_to<GDScript>(id); |
612 | if (!s) { |
613 | continue; |
614 | } |
615 | s->update_exports(); |
616 | } |
617 | |
618 | #endif |
619 | } |
620 | |
621 | String GDScript::_get_debug_path() const { |
622 | if (is_built_in() && !get_name().is_empty()) { |
623 | return vformat("%s(%s)" , get_name(), get_script_path()); |
624 | } else { |
625 | return get_script_path(); |
626 | } |
627 | } |
628 | |
629 | Error GDScript::_static_init() { |
630 | if (static_initializer) { |
631 | Callable::CallError call_err; |
632 | static_initializer->call(nullptr, nullptr, 0, call_err); |
633 | if (call_err.error != Callable::CallError::CALL_OK) { |
634 | return ERR_CANT_CREATE; |
635 | } |
636 | } |
637 | Error err = OK; |
638 | for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) { |
639 | err = inner.value->_static_init(); |
640 | if (err) { |
641 | break; |
642 | } |
643 | } |
644 | return err; |
645 | } |
646 | |
647 | #ifdef TOOLS_ENABLED |
648 | |
649 | void GDScript::_save_old_static_data() { |
650 | old_static_variables_indices = static_variables_indices; |
651 | old_static_variables = static_variables; |
652 | for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) { |
653 | inner.value->_save_old_static_data(); |
654 | } |
655 | } |
656 | |
657 | void GDScript::_restore_old_static_data() { |
658 | for (KeyValue<StringName, MemberInfo> &E : old_static_variables_indices) { |
659 | if (static_variables_indices.has(E.key)) { |
660 | static_variables.write[static_variables_indices[E.key].index] = old_static_variables[E.value.index]; |
661 | } |
662 | } |
663 | old_static_variables_indices.clear(); |
664 | old_static_variables.clear(); |
665 | for (KeyValue<StringName, Ref<GDScript>> &inner : subclasses) { |
666 | inner.value->_restore_old_static_data(); |
667 | } |
668 | } |
669 | |
670 | #endif |
671 | |
672 | Error GDScript::reload(bool p_keep_state) { |
673 | if (reloading) { |
674 | return OK; |
675 | } |
676 | reloading = true; |
677 | |
678 | bool has_instances; |
679 | { |
680 | MutexLock lock(GDScriptLanguage::singleton->mutex); |
681 | |
682 | has_instances = instances.size(); |
683 | } |
684 | |
685 | ERR_FAIL_COND_V(!p_keep_state && has_instances, ERR_ALREADY_IN_USE); |
686 | |
687 | String basedir = path; |
688 | |
689 | if (basedir.is_empty()) { |
690 | basedir = get_path(); |
691 | } |
692 | |
693 | if (!basedir.is_empty()) { |
694 | basedir = basedir.get_base_dir(); |
695 | } |
696 | |
697 | // Loading a template, don't parse. |
698 | #ifdef TOOLS_ENABLED |
699 | if (EditorPaths::get_singleton() && basedir.begins_with(EditorPaths::get_singleton()->get_project_script_templates_dir())) { |
700 | reloading = false; |
701 | return OK; |
702 | } |
703 | #endif |
704 | |
705 | { |
706 | String source_path = path; |
707 | if (source_path.is_empty()) { |
708 | source_path = get_path(); |
709 | } |
710 | Ref<GDScript> cached_script = GDScriptCache::get_cached_script(source_path); |
711 | if (!source_path.is_empty() && cached_script.is_null()) { |
712 | MutexLock lock(GDScriptCache::singleton->mutex); |
713 | GDScriptCache::singleton->shallow_gdscript_cache[source_path] = Ref<GDScript>(this); |
714 | } |
715 | } |
716 | |
717 | bool can_run = ScriptServer::is_scripting_enabled() || is_tool(); |
718 | |
719 | #ifdef TOOLS_ENABLED |
720 | if (p_keep_state && can_run && is_valid()) { |
721 | _save_old_static_data(); |
722 | } |
723 | #endif |
724 | |
725 | valid = false; |
726 | GDScriptParser parser; |
727 | Error err = parser.parse(source, path, false); |
728 | if (err) { |
729 | if (EngineDebugger::is_active()) { |
730 | GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); |
731 | } |
732 | // TODO: Show all error messages. |
733 | _err_print_error("GDScript::reload" , path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_errors().front()->get().line, ("Parse Error: " + parser.get_errors().front()->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); |
734 | reloading = false; |
735 | return ERR_PARSE_ERROR; |
736 | } |
737 | |
738 | GDScriptAnalyzer analyzer(&parser); |
739 | err = analyzer.analyze(); |
740 | |
741 | if (err) { |
742 | if (EngineDebugger::is_active()) { |
743 | GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); |
744 | } |
745 | |
746 | const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front(); |
747 | while (e != nullptr) { |
748 | _err_print_error("GDScript::reload" , path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), e->get().line, ("Parse Error: " + e->get().message).utf8().get_data(), false, ERR_HANDLER_SCRIPT); |
749 | e = e->next(); |
750 | } |
751 | reloading = false; |
752 | return ERR_PARSE_ERROR; |
753 | } |
754 | |
755 | can_run = ScriptServer::is_scripting_enabled() || parser.is_tool(); |
756 | |
757 | GDScriptCompiler compiler; |
758 | err = compiler.compile(&parser, this, p_keep_state); |
759 | |
760 | if (err) { |
761 | _err_print_error("GDScript::reload" , path.is_empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), false, ERR_HANDLER_SCRIPT); |
762 | if (can_run) { |
763 | if (EngineDebugger::is_active()) { |
764 | GDScriptLanguage::get_singleton()->debug_break_parse(_get_debug_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error()); |
765 | } |
766 | reloading = false; |
767 | return ERR_COMPILATION_FAILED; |
768 | } else { |
769 | reloading = false; |
770 | return err; |
771 | } |
772 | } |
773 | |
774 | #ifdef TOOLS_ENABLED |
775 | // Done after compilation because it needs the GDScript object's inner class GDScript objects, |
776 | // which are made by calling make_scripts() within compiler.compile() above. |
777 | GDScriptDocGen::generate_docs(this, parser.get_tree()); |
778 | #endif |
779 | |
780 | #ifdef DEBUG_ENABLED |
781 | for (const GDScriptWarning &warning : parser.get_warnings()) { |
782 | if (EngineDebugger::is_active()) { |
783 | Vector<ScriptLanguage::StackInfo> si; |
784 | EngineDebugger::get_script_debugger()->send_error("" , get_script_path(), warning.start_line, warning.get_name(), warning.get_message(), false, ERR_HANDLER_WARNING, si); |
785 | } |
786 | } |
787 | #endif |
788 | |
789 | if (can_run) { |
790 | err = _static_init(); |
791 | if (err) { |
792 | return err; |
793 | } |
794 | } |
795 | |
796 | #ifdef TOOLS_ENABLED |
797 | if (can_run && p_keep_state) { |
798 | _restore_old_static_data(); |
799 | } |
800 | #endif |
801 | |
802 | reloading = false; |
803 | return OK; |
804 | } |
805 | |
806 | ScriptLanguage *GDScript::get_language() const { |
807 | return GDScriptLanguage::get_singleton(); |
808 | } |
809 | |
810 | void GDScript::get_constants(HashMap<StringName, Variant> *p_constants) { |
811 | if (p_constants) { |
812 | for (const KeyValue<StringName, Variant> &E : constants) { |
813 | (*p_constants)[E.key] = E.value; |
814 | } |
815 | } |
816 | } |
817 | |
818 | void GDScript::get_members(HashSet<StringName> *p_members) { |
819 | if (p_members) { |
820 | for (const StringName &E : members) { |
821 | p_members->insert(E); |
822 | } |
823 | } |
824 | } |
825 | |
826 | const Variant GDScript::get_rpc_config() const { |
827 | return rpc_config; |
828 | } |
829 | |
830 | void GDScript::unload_static() const { |
831 | GDScriptCache::remove_script(fully_qualified_name); |
832 | } |
833 | |
834 | Variant GDScript::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { |
835 | GDScript *top = this; |
836 | while (top) { |
837 | HashMap<StringName, GDScriptFunction *>::Iterator E = top->member_functions.find(p_method); |
838 | if (E) { |
839 | ERR_FAIL_COND_V_MSG(!E->value->is_static(), Variant(), "Can't call non-static function '" + String(p_method) + "' in script." ); |
840 | |
841 | return E->value->call(nullptr, p_args, p_argcount, r_error); |
842 | } |
843 | top = top->_base; |
844 | } |
845 | |
846 | //none found, regular |
847 | |
848 | return Script::callp(p_method, p_args, p_argcount, r_error); |
849 | } |
850 | |
851 | bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { |
852 | if (p_name == GDScriptLanguage::get_singleton()->strings._script_source) { |
853 | r_ret = get_source_code(); |
854 | return true; |
855 | } |
856 | |
857 | const GDScript *top = this; |
858 | while (top) { |
859 | { |
860 | HashMap<StringName, Variant>::ConstIterator E = top->constants.find(p_name); |
861 | if (E) { |
862 | r_ret = E->value; |
863 | return true; |
864 | } |
865 | } |
866 | |
867 | { |
868 | HashMap<StringName, MemberInfo>::ConstIterator E = top->static_variables_indices.find(p_name); |
869 | if (E) { |
870 | if (E->value.getter) { |
871 | Callable::CallError ce; |
872 | r_ret = const_cast<GDScript *>(this)->callp(E->value.getter, nullptr, 0, ce); |
873 | return true; |
874 | } |
875 | r_ret = top->static_variables[E->value.index]; |
876 | return true; |
877 | } |
878 | } |
879 | |
880 | { |
881 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = top->member_functions.find(p_name); |
882 | if (E && E->value->is_static()) { |
883 | if (top->rpc_config.has(p_name)) { |
884 | r_ret = Callable(memnew(GDScriptRPCCallable(const_cast<GDScript *>(top), E->key))); |
885 | } else { |
886 | r_ret = Callable(const_cast<GDScript *>(top), E->key); |
887 | } |
888 | return true; |
889 | } |
890 | } |
891 | |
892 | { |
893 | HashMap<StringName, Ref<GDScript>>::ConstIterator E = top->subclasses.find(p_name); |
894 | if (E) { |
895 | r_ret = E->value; |
896 | return true; |
897 | } |
898 | } |
899 | |
900 | top = top->_base; |
901 | } |
902 | |
903 | return false; |
904 | } |
905 | |
906 | bool GDScript::_set(const StringName &p_name, const Variant &p_value) { |
907 | if (p_name == GDScriptLanguage::get_singleton()->strings._script_source) { |
908 | set_source_code(p_value); |
909 | reload(); |
910 | return true; |
911 | } |
912 | |
913 | GDScript *top = this; |
914 | while (top) { |
915 | HashMap<StringName, MemberInfo>::ConstIterator E = top->static_variables_indices.find(p_name); |
916 | if (E) { |
917 | const MemberInfo *member = &E->value; |
918 | Variant value = p_value; |
919 | if (member->data_type.has_type && !member->data_type.is_type(value)) { |
920 | const Variant *args = &p_value; |
921 | Callable::CallError err; |
922 | Variant::construct(member->data_type.builtin_type, value, &args, 1, err); |
923 | if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) { |
924 | return false; |
925 | } |
926 | } |
927 | if (member->setter) { |
928 | const Variant *args = &value; |
929 | Callable::CallError err; |
930 | callp(member->setter, &args, 1, err); |
931 | return err.error == Callable::CallError::CALL_OK; |
932 | } else { |
933 | top->static_variables.write[member->index] = value; |
934 | return true; |
935 | } |
936 | } |
937 | |
938 | top = top->_base; |
939 | } |
940 | |
941 | return false; |
942 | } |
943 | |
944 | void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const { |
945 | p_properties->push_back(PropertyInfo(Variant::STRING, "script/source" , PROPERTY_HINT_NONE, "" , PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL)); |
946 | |
947 | List<const GDScript *> classes; |
948 | const GDScript *top = this; |
949 | while (top) { |
950 | classes.push_back(top); |
951 | top = top->_base; |
952 | } |
953 | |
954 | for (const List<const GDScript *>::Element *E = classes.back(); E; E = E->prev()) { |
955 | Vector<_GDScriptMemberSort> msort; |
956 | for (const KeyValue<StringName, MemberInfo> &F : E->get()->static_variables_indices) { |
957 | _GDScriptMemberSort ms; |
958 | ms.index = F.value.index; |
959 | ms.name = F.key; |
960 | msort.push_back(ms); |
961 | } |
962 | msort.sort(); |
963 | |
964 | for (int i = 0; i < msort.size(); i++) { |
965 | p_properties->push_back(E->get()->static_variables_indices[msort[i].name].property_info); |
966 | } |
967 | } |
968 | } |
969 | |
970 | void GDScript::_bind_methods() { |
971 | ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new" , &GDScript::_new, MethodInfo("new" )); |
972 | } |
973 | |
974 | void GDScript::set_path(const String &p_path, bool p_take_over) { |
975 | if (is_root_script()) { |
976 | Script::set_path(p_path, p_take_over); |
977 | } |
978 | |
979 | String old_path = path; |
980 | path = p_path; |
981 | GDScriptCache::move_script(old_path, p_path); |
982 | |
983 | for (KeyValue<StringName, Ref<GDScript>> &kv : subclasses) { |
984 | kv.value->set_path(p_path, p_take_over); |
985 | } |
986 | } |
987 | |
988 | String GDScript::get_script_path() const { |
989 | return path; |
990 | } |
991 | |
992 | Error GDScript::load_source_code(const String &p_path) { |
993 | if (p_path.is_empty() || p_path.begins_with("gdscript://" ) || ResourceLoader::get_resource_type(p_path.get_slice("::" , 0)) == "PackedScene" ) { |
994 | return OK; |
995 | } |
996 | |
997 | Vector<uint8_t> sourcef; |
998 | Error err; |
999 | Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); |
1000 | if (err) { |
1001 | const char *err_name; |
1002 | if (err < 0 || err >= ERR_MAX) { |
1003 | err_name = "(invalid error code)" ; |
1004 | } else { |
1005 | err_name = error_names[err]; |
1006 | } |
1007 | ERR_FAIL_COND_V_MSG(err, err, "Attempt to open script '" + p_path + "' resulted in error '" + err_name + "'." ); |
1008 | } |
1009 | |
1010 | uint64_t len = f->get_length(); |
1011 | sourcef.resize(len + 1); |
1012 | uint8_t *w = sourcef.ptrw(); |
1013 | uint64_t r = f->get_buffer(w, len); |
1014 | ERR_FAIL_COND_V(r != len, ERR_CANT_OPEN); |
1015 | w[len] = 0; |
1016 | |
1017 | String s; |
1018 | if (s.parse_utf8((const char *)w) != OK) { |
1019 | ERR_FAIL_V_MSG(ERR_INVALID_DATA, "Script '" + p_path + "' contains invalid unicode (UTF-8), so it was not loaded. Please ensure that scripts are saved in valid UTF-8 unicode." ); |
1020 | } |
1021 | |
1022 | source = s; |
1023 | path = p_path; |
1024 | #ifdef TOOLS_ENABLED |
1025 | source_changed_cache = true; |
1026 | set_edited(false); |
1027 | set_last_modified_time(FileAccess::get_modified_time(path)); |
1028 | #endif // TOOLS_ENABLED |
1029 | return OK; |
1030 | } |
1031 | |
1032 | const HashMap<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const { |
1033 | return member_functions; |
1034 | } |
1035 | |
1036 | StringName GDScript::debug_get_member_by_index(int p_idx) const { |
1037 | for (const KeyValue<StringName, MemberInfo> &E : member_indices) { |
1038 | if (E.value.index == p_idx) { |
1039 | return E.key; |
1040 | } |
1041 | } |
1042 | |
1043 | return "<error>" ; |
1044 | } |
1045 | |
1046 | StringName GDScript::debug_get_static_var_by_index(int p_idx) const { |
1047 | for (const KeyValue<StringName, MemberInfo> &E : static_variables_indices) { |
1048 | if (E.value.index == p_idx) { |
1049 | return E.key; |
1050 | } |
1051 | } |
1052 | |
1053 | return "<error>" ; |
1054 | } |
1055 | |
1056 | Ref<GDScript> GDScript::get_base() const { |
1057 | return base; |
1058 | } |
1059 | |
1060 | bool GDScript::inherits_script(const Ref<Script> &p_script) const { |
1061 | Ref<GDScript> gd = p_script; |
1062 | if (gd.is_null()) { |
1063 | return false; |
1064 | } |
1065 | |
1066 | const GDScript *s = this; |
1067 | |
1068 | while (s) { |
1069 | if (s == p_script.ptr()) { |
1070 | return true; |
1071 | } |
1072 | s = s->_base; |
1073 | } |
1074 | |
1075 | return false; |
1076 | } |
1077 | |
1078 | GDScript *GDScript::find_class(const String &p_qualified_name) { |
1079 | String first = p_qualified_name.get_slice("::" , 0); |
1080 | |
1081 | Vector<String> class_names; |
1082 | GDScript *result = nullptr; |
1083 | // Empty initial name means start here. |
1084 | if (first.is_empty() || first == global_name) { |
1085 | class_names = p_qualified_name.split("::" ); |
1086 | result = this; |
1087 | } else if (p_qualified_name.begins_with(get_root_script()->path)) { |
1088 | // Script path could have a class path separator("::") in it. |
1089 | class_names = p_qualified_name.trim_prefix(get_root_script()->path).split("::" ); |
1090 | result = get_root_script(); |
1091 | } else if (HashMap<StringName, Ref<GDScript>>::Iterator E = subclasses.find(first)) { |
1092 | class_names = p_qualified_name.split("::" ); |
1093 | result = E->value.ptr(); |
1094 | } else if (_owner != nullptr) { |
1095 | // Check parent scope. |
1096 | return _owner->find_class(p_qualified_name); |
1097 | } |
1098 | |
1099 | // Starts at index 1 because index 0 was handled above. |
1100 | for (int i = 1; result != nullptr && i < class_names.size(); i++) { |
1101 | String current_name = class_names[i]; |
1102 | if (HashMap<StringName, Ref<GDScript>>::Iterator E = result->subclasses.find(current_name)) { |
1103 | result = E->value.ptr(); |
1104 | } else { |
1105 | // Couldn't find inner class. |
1106 | return nullptr; |
1107 | } |
1108 | } |
1109 | |
1110 | return result; |
1111 | } |
1112 | |
1113 | bool GDScript::has_class(const GDScript *p_script) { |
1114 | String fqn = p_script->fully_qualified_name; |
1115 | if (fully_qualified_name.is_empty() && fqn.get_slice("::" , 0).is_empty()) { |
1116 | return p_script == this; |
1117 | } else if (fqn.begins_with(fully_qualified_name)) { |
1118 | return p_script == find_class(fqn.trim_prefix(fully_qualified_name)); |
1119 | } |
1120 | return false; |
1121 | } |
1122 | |
1123 | GDScript *GDScript::get_root_script() { |
1124 | GDScript *result = this; |
1125 | while (result->_owner) { |
1126 | result = result->_owner; |
1127 | } |
1128 | return result; |
1129 | } |
1130 | |
1131 | RBSet<GDScript *> GDScript::get_dependencies() { |
1132 | RBSet<GDScript *> dependencies; |
1133 | |
1134 | _get_dependencies(dependencies, this); |
1135 | dependencies.erase(this); |
1136 | |
1137 | return dependencies; |
1138 | } |
1139 | |
1140 | RBSet<GDScript *> GDScript::get_inverted_dependencies() { |
1141 | RBSet<GDScript *> inverted_dependencies; |
1142 | |
1143 | List<GDScript *> scripts; |
1144 | { |
1145 | MutexLock lock(GDScriptLanguage::singleton->mutex); |
1146 | |
1147 | SelfList<GDScript> *elem = GDScriptLanguage::singleton->script_list.first(); |
1148 | while (elem) { |
1149 | scripts.push_back(elem->self()); |
1150 | elem = elem->next(); |
1151 | } |
1152 | } |
1153 | |
1154 | for (GDScript *scr : scripts) { |
1155 | if (scr == nullptr || scr == this || scr->destructing) { |
1156 | continue; |
1157 | } |
1158 | |
1159 | RBSet<GDScript *> scr_dependencies = scr->get_dependencies(); |
1160 | if (scr_dependencies.has(this)) { |
1161 | inverted_dependencies.insert(scr); |
1162 | } |
1163 | } |
1164 | |
1165 | return inverted_dependencies; |
1166 | } |
1167 | |
1168 | RBSet<GDScript *> GDScript::get_must_clear_dependencies() { |
1169 | RBSet<GDScript *> dependencies = get_dependencies(); |
1170 | RBSet<GDScript *> must_clear_dependencies; |
1171 | HashMap<GDScript *, RBSet<GDScript *>> inverted_dependencies; |
1172 | |
1173 | for (GDScript *E : dependencies) { |
1174 | inverted_dependencies.insert(E, E->get_inverted_dependencies()); |
1175 | } |
1176 | |
1177 | RBSet<GDScript *> cant_clear; |
1178 | for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) { |
1179 | for (GDScript *F : E.value) { |
1180 | if (!dependencies.has(F)) { |
1181 | cant_clear.insert(E.key); |
1182 | for (GDScript *G : E.key->get_dependencies()) { |
1183 | cant_clear.insert(G); |
1184 | } |
1185 | break; |
1186 | } |
1187 | } |
1188 | } |
1189 | |
1190 | for (KeyValue<GDScript *, RBSet<GDScript *>> &E : inverted_dependencies) { |
1191 | if (cant_clear.has(E.key) || ScriptServer::is_global_class(E.key->get_fully_qualified_name())) { |
1192 | continue; |
1193 | } |
1194 | must_clear_dependencies.insert(E.key); |
1195 | } |
1196 | |
1197 | cant_clear.clear(); |
1198 | dependencies.clear(); |
1199 | inverted_dependencies.clear(); |
1200 | return must_clear_dependencies; |
1201 | } |
1202 | |
1203 | bool GDScript::has_script_signal(const StringName &p_signal) const { |
1204 | if (_signals.has(p_signal)) { |
1205 | return true; |
1206 | } |
1207 | if (base.is_valid()) { |
1208 | return base->has_script_signal(p_signal); |
1209 | } |
1210 | #ifdef TOOLS_ENABLED |
1211 | else if (base_cache.is_valid()) { |
1212 | return base_cache->has_script_signal(p_signal); |
1213 | } |
1214 | #endif |
1215 | return false; |
1216 | } |
1217 | |
1218 | void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const { |
1219 | for (const KeyValue<StringName, MethodInfo> &E : _signals) { |
1220 | r_list->push_back(E.value); |
1221 | } |
1222 | |
1223 | if (!p_include_base) { |
1224 | return; |
1225 | } |
1226 | |
1227 | if (base.is_valid()) { |
1228 | base->get_script_signal_list(r_list); |
1229 | } |
1230 | #ifdef TOOLS_ENABLED |
1231 | else if (base_cache.is_valid()) { |
1232 | base_cache->get_script_signal_list(r_list); |
1233 | } |
1234 | #endif |
1235 | } |
1236 | |
1237 | void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { |
1238 | _get_script_signal_list(r_signals, true); |
1239 | } |
1240 | |
1241 | GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) { |
1242 | Object *obj = p_variant; |
1243 | if (obj == nullptr || obj->get_instance_id().is_null()) { |
1244 | return nullptr; |
1245 | } |
1246 | return Object::cast_to<GDScript>(obj); |
1247 | } |
1248 | |
1249 | void GDScript::_get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except) { |
1250 | if (p_dependencies.has(this)) { |
1251 | return; |
1252 | } |
1253 | p_dependencies.insert(this); |
1254 | |
1255 | for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) { |
1256 | if (E.value == nullptr) { |
1257 | continue; |
1258 | } |
1259 | for (const Variant &V : E.value->constants) { |
1260 | GDScript *scr = _get_gdscript_from_variant(V); |
1261 | if (scr != nullptr && scr != p_except) { |
1262 | scr->_get_dependencies(p_dependencies, p_except); |
1263 | } |
1264 | } |
1265 | } |
1266 | |
1267 | if (implicit_initializer) { |
1268 | for (const Variant &V : implicit_initializer->constants) { |
1269 | GDScript *scr = _get_gdscript_from_variant(V); |
1270 | if (scr != nullptr && scr != p_except) { |
1271 | scr->_get_dependencies(p_dependencies, p_except); |
1272 | } |
1273 | } |
1274 | } |
1275 | |
1276 | if (implicit_ready) { |
1277 | for (const Variant &V : implicit_ready->constants) { |
1278 | GDScript *scr = _get_gdscript_from_variant(V); |
1279 | if (scr != nullptr && scr != p_except) { |
1280 | scr->_get_dependencies(p_dependencies, p_except); |
1281 | } |
1282 | } |
1283 | } |
1284 | |
1285 | for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) { |
1286 | if (E.value != p_except) { |
1287 | E.value->_get_dependencies(p_dependencies, p_except); |
1288 | } |
1289 | } |
1290 | |
1291 | for (const KeyValue<StringName, Variant> &E : constants) { |
1292 | GDScript *scr = _get_gdscript_from_variant(E.value); |
1293 | if (scr != nullptr && scr != p_except) { |
1294 | scr->_get_dependencies(p_dependencies, p_except); |
1295 | } |
1296 | } |
1297 | } |
1298 | |
1299 | GDScript::GDScript() : |
1300 | script_list(this) { |
1301 | { |
1302 | MutexLock lock(GDScriptLanguage::get_singleton()->mutex); |
1303 | |
1304 | GDScriptLanguage::get_singleton()->script_list.add(&script_list); |
1305 | } |
1306 | |
1307 | path = vformat("gdscript://%d.gd" , get_instance_id()); |
1308 | } |
1309 | |
1310 | void GDScript::_save_orphaned_subclasses(ClearData *p_clear_data) { |
1311 | struct ClassRefWithName { |
1312 | ObjectID id; |
1313 | String fully_qualified_name; |
1314 | }; |
1315 | Vector<ClassRefWithName> weak_subclasses; |
1316 | // collect subclasses ObjectID and name |
1317 | for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) { |
1318 | E.value->_owner = nullptr; //bye, you are no longer owned cause I died |
1319 | ClassRefWithName subclass; |
1320 | subclass.id = E.value->get_instance_id(); |
1321 | subclass.fully_qualified_name = E.value->fully_qualified_name; |
1322 | weak_subclasses.push_back(subclass); |
1323 | } |
1324 | |
1325 | // clear subclasses to allow unused subclasses to be deleted |
1326 | for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) { |
1327 | p_clear_data->scripts.insert(E.value); |
1328 | } |
1329 | subclasses.clear(); |
1330 | // subclasses are also held by constants, clear those as well |
1331 | for (KeyValue<StringName, Variant> &E : constants) { |
1332 | GDScript *gdscr = _get_gdscript_from_variant(E.value); |
1333 | if (gdscr != nullptr) { |
1334 | p_clear_data->scripts.insert(gdscr); |
1335 | } |
1336 | } |
1337 | constants.clear(); |
1338 | |
1339 | // keep orphan subclass only for subclasses that are still in use |
1340 | for (int i = 0; i < weak_subclasses.size(); i++) { |
1341 | ClassRefWithName subclass = weak_subclasses[i]; |
1342 | Object *obj = ObjectDB::get_instance(subclass.id); |
1343 | if (!obj) { |
1344 | continue; |
1345 | } |
1346 | // subclass is not released |
1347 | GDScriptLanguage::get_singleton()->add_orphan_subclass(subclass.fully_qualified_name, subclass.id); |
1348 | } |
1349 | } |
1350 | |
1351 | #ifdef DEBUG_ENABLED |
1352 | String GDScript::debug_get_script_name(const Ref<Script> &p_script) { |
1353 | if (p_script.is_valid()) { |
1354 | Ref<GDScript> gdscript = p_script; |
1355 | if (gdscript.is_valid()) { |
1356 | if (gdscript->get_local_name() != StringName()) { |
1357 | return gdscript->get_local_name(); |
1358 | } |
1359 | return gdscript->get_fully_qualified_name().get_file(); |
1360 | } |
1361 | |
1362 | if (p_script->get_global_name() != StringName()) { |
1363 | return p_script->get_global_name(); |
1364 | } else if (!p_script->get_path().is_empty()) { |
1365 | return p_script->get_path().get_file(); |
1366 | } else if (!p_script->get_name().is_empty()) { |
1367 | return p_script->get_name(); // Resource name. |
1368 | } |
1369 | } |
1370 | |
1371 | return "<unknown script>" ; |
1372 | } |
1373 | #endif |
1374 | |
1375 | void GDScript::clear(ClearData *p_clear_data) { |
1376 | if (clearing) { |
1377 | return; |
1378 | } |
1379 | clearing = true; |
1380 | |
1381 | ClearData data; |
1382 | ClearData *clear_data = p_clear_data; |
1383 | bool is_root = false; |
1384 | |
1385 | // If `clear_data` is `nullptr`, it means that it's the root. |
1386 | // The root is in charge to clear functions and scripts of itself and its dependencies |
1387 | if (clear_data == nullptr) { |
1388 | clear_data = &data; |
1389 | is_root = true; |
1390 | } |
1391 | |
1392 | RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies(); |
1393 | for (GDScript *E : must_clear_dependencies) { |
1394 | clear_data->scripts.insert(E); |
1395 | E->clear(clear_data); |
1396 | } |
1397 | |
1398 | for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) { |
1399 | clear_data->functions.insert(E.value); |
1400 | } |
1401 | member_functions.clear(); |
1402 | |
1403 | for (KeyValue<StringName, MemberInfo> &E : member_indices) { |
1404 | clear_data->scripts.insert(E.value.data_type.script_type_ref); |
1405 | E.value.data_type.script_type_ref = Ref<Script>(); |
1406 | } |
1407 | |
1408 | for (KeyValue<StringName, MemberInfo> &E : static_variables_indices) { |
1409 | clear_data->scripts.insert(E.value.data_type.script_type_ref); |
1410 | E.value.data_type.script_type_ref = Ref<Script>(); |
1411 | } |
1412 | static_variables.clear(); |
1413 | static_variables_indices.clear(); |
1414 | |
1415 | if (implicit_initializer) { |
1416 | clear_data->functions.insert(implicit_initializer); |
1417 | implicit_initializer = nullptr; |
1418 | } |
1419 | |
1420 | if (implicit_ready) { |
1421 | clear_data->functions.insert(implicit_ready); |
1422 | implicit_ready = nullptr; |
1423 | } |
1424 | |
1425 | if (static_initializer) { |
1426 | clear_data->functions.insert(static_initializer); |
1427 | static_initializer = nullptr; |
1428 | } |
1429 | |
1430 | _save_orphaned_subclasses(clear_data); |
1431 | |
1432 | #ifdef TOOLS_ENABLED |
1433 | // Clearing inner class doc, script doc only cleared when the script source deleted. |
1434 | if (_owner) { |
1435 | _clear_doc(); |
1436 | } |
1437 | #endif |
1438 | |
1439 | // If it's not the root, skip clearing the data |
1440 | if (is_root) { |
1441 | // All dependencies have been accounted for |
1442 | for (GDScriptFunction *E : clear_data->functions) { |
1443 | memdelete(E); |
1444 | } |
1445 | for (Ref<Script> &E : clear_data->scripts) { |
1446 | Ref<GDScript> gdscr = E; |
1447 | if (gdscr.is_valid()) { |
1448 | GDScriptCache::remove_script(gdscr->get_path()); |
1449 | } |
1450 | } |
1451 | clear_data->clear(); |
1452 | } |
1453 | } |
1454 | |
1455 | GDScript::~GDScript() { |
1456 | if (destructing) { |
1457 | return; |
1458 | } |
1459 | destructing = true; |
1460 | |
1461 | clear(); |
1462 | |
1463 | { |
1464 | MutexLock lock(GDScriptLanguage::get_singleton()->mutex); |
1465 | |
1466 | while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) { |
1467 | // Order matters since clearing the stack may already cause |
1468 | // the GDScriptFunctionState to be destroyed and thus removed from the list. |
1469 | pending_func_states.remove(E); |
1470 | GDScriptFunctionState *state = E->self(); |
1471 | ObjectID state_id = state->get_instance_id(); |
1472 | state->_clear_connections(); |
1473 | if (ObjectDB::get_instance(state_id)) { |
1474 | state->_clear_stack(); |
1475 | } |
1476 | } |
1477 | } |
1478 | |
1479 | { |
1480 | MutexLock lock(GDScriptLanguage::get_singleton()->mutex); |
1481 | |
1482 | script_list.remove_from_list(); |
1483 | } |
1484 | } |
1485 | |
1486 | ////////////////////////////// |
1487 | // INSTANCE // |
1488 | ////////////////////////////// |
1489 | |
1490 | bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { |
1491 | { |
1492 | HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name); |
1493 | if (E) { |
1494 | const GDScript::MemberInfo *member = &E->value; |
1495 | Variant value = p_value; |
1496 | if (member->data_type.has_type && !member->data_type.is_type(value)) { |
1497 | const Variant *args = &p_value; |
1498 | Callable::CallError err; |
1499 | Variant::construct(member->data_type.builtin_type, value, &args, 1, err); |
1500 | if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) { |
1501 | return false; |
1502 | } |
1503 | } |
1504 | if (member->setter) { |
1505 | const Variant *args = &value; |
1506 | Callable::CallError err; |
1507 | callp(member->setter, &args, 1, err); |
1508 | return err.error == Callable::CallError::CALL_OK; |
1509 | } else { |
1510 | members.write[member->index] = value; |
1511 | return true; |
1512 | } |
1513 | } |
1514 | } |
1515 | |
1516 | GDScript *sptr = script.ptr(); |
1517 | while (sptr) { |
1518 | { |
1519 | HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = sptr->static_variables_indices.find(p_name); |
1520 | if (E) { |
1521 | const GDScript::MemberInfo *member = &E->value; |
1522 | Variant value = p_value; |
1523 | if (member->data_type.has_type && !member->data_type.is_type(value)) { |
1524 | const Variant *args = &p_value; |
1525 | Callable::CallError err; |
1526 | Variant::construct(member->data_type.builtin_type, value, &args, 1, err); |
1527 | if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) { |
1528 | return false; |
1529 | } |
1530 | } |
1531 | if (member->setter) { |
1532 | const Variant *args = &value; |
1533 | Callable::CallError err; |
1534 | callp(member->setter, &args, 1, err); |
1535 | return err.error == Callable::CallError::CALL_OK; |
1536 | } else { |
1537 | sptr->static_variables.write[member->index] = value; |
1538 | return true; |
1539 | } |
1540 | } |
1541 | } |
1542 | |
1543 | { |
1544 | HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set); |
1545 | if (E) { |
1546 | Variant name = p_name; |
1547 | const Variant *args[2] = { &name, &p_value }; |
1548 | |
1549 | Callable::CallError err; |
1550 | Variant ret = E->value->call(this, (const Variant **)args, 2, err); |
1551 | if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) { |
1552 | return true; |
1553 | } |
1554 | } |
1555 | } |
1556 | |
1557 | sptr = sptr->_base; |
1558 | } |
1559 | |
1560 | return false; |
1561 | } |
1562 | |
1563 | bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { |
1564 | { |
1565 | HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = script->member_indices.find(p_name); |
1566 | if (E) { |
1567 | if (E->value.getter) { |
1568 | Callable::CallError err; |
1569 | r_ret = const_cast<GDScriptInstance *>(this)->callp(E->value.getter, nullptr, 0, err); |
1570 | if (err.error == Callable::CallError::CALL_OK) { |
1571 | return true; |
1572 | } |
1573 | } |
1574 | r_ret = members[E->value.index]; |
1575 | return true; |
1576 | } |
1577 | } |
1578 | |
1579 | const GDScript *sptr = script.ptr(); |
1580 | while (sptr) { |
1581 | { |
1582 | HashMap<StringName, Variant>::ConstIterator E = sptr->constants.find(p_name); |
1583 | if (E) { |
1584 | r_ret = E->value; |
1585 | return true; |
1586 | } |
1587 | } |
1588 | |
1589 | { |
1590 | HashMap<StringName, GDScript::MemberInfo>::ConstIterator E = sptr->static_variables_indices.find(p_name); |
1591 | if (E) { |
1592 | if (E->value.getter) { |
1593 | Callable::CallError ce; |
1594 | r_ret = const_cast<GDScript *>(sptr)->callp(E->value.getter, nullptr, 0, ce); |
1595 | return true; |
1596 | } |
1597 | r_ret = sptr->static_variables[E->value.index]; |
1598 | return true; |
1599 | } |
1600 | } |
1601 | |
1602 | { |
1603 | HashMap<StringName, MethodInfo>::ConstIterator E = sptr->_signals.find(p_name); |
1604 | if (E) { |
1605 | r_ret = Signal(this->owner, E->key); |
1606 | return true; |
1607 | } |
1608 | } |
1609 | |
1610 | { |
1611 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_name); |
1612 | if (E) { |
1613 | if (sptr->rpc_config.has(p_name)) { |
1614 | r_ret = Callable(memnew(GDScriptRPCCallable(this->owner, E->key))); |
1615 | } else { |
1616 | r_ret = Callable(this->owner, E->key); |
1617 | } |
1618 | return true; |
1619 | } |
1620 | } |
1621 | |
1622 | { |
1623 | HashMap<StringName, Ref<GDScript>>::ConstIterator E = sptr->subclasses.find(p_name); |
1624 | if (E) { |
1625 | r_ret = E->value; |
1626 | return true; |
1627 | } |
1628 | } |
1629 | |
1630 | { |
1631 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get); |
1632 | if (E) { |
1633 | Variant name = p_name; |
1634 | const Variant *args[1] = { &name }; |
1635 | |
1636 | Callable::CallError err; |
1637 | Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), (const Variant **)args, 1, err); |
1638 | if (err.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) { |
1639 | r_ret = ret; |
1640 | return true; |
1641 | } |
1642 | } |
1643 | } |
1644 | sptr = sptr->_base; |
1645 | } |
1646 | |
1647 | return false; |
1648 | } |
1649 | |
1650 | Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { |
1651 | const GDScript *sptr = script.ptr(); |
1652 | while (sptr) { |
1653 | if (sptr->member_indices.has(p_name)) { |
1654 | if (r_is_valid) { |
1655 | *r_is_valid = true; |
1656 | } |
1657 | return sptr->member_indices[p_name].property_info.type; |
1658 | } |
1659 | sptr = sptr->_base; |
1660 | } |
1661 | |
1662 | if (r_is_valid) { |
1663 | *r_is_valid = false; |
1664 | } |
1665 | return Variant::NIL; |
1666 | } |
1667 | |
1668 | void GDScriptInstance::validate_property(PropertyInfo &p_property) const { |
1669 | Variant property = (Dictionary)p_property; |
1670 | const Variant *args[1] = { &property }; |
1671 | |
1672 | const GDScript *sptr = script.ptr(); |
1673 | while (sptr) { |
1674 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._validate_property); |
1675 | if (E) { |
1676 | Callable::CallError err; |
1677 | Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err); |
1678 | if (err.error == Callable::CallError::CALL_OK) { |
1679 | p_property = PropertyInfo::from_dict(property); |
1680 | return; |
1681 | } |
1682 | } |
1683 | sptr = sptr->_base; |
1684 | } |
1685 | } |
1686 | |
1687 | void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { |
1688 | // exported members, not done yet! |
1689 | |
1690 | const GDScript *sptr = script.ptr(); |
1691 | List<PropertyInfo> props; |
1692 | |
1693 | while (sptr) { |
1694 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list); |
1695 | if (E) { |
1696 | Callable::CallError err; |
1697 | Variant ret = const_cast<GDScriptFunction *>(E->value)->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err); |
1698 | if (err.error == Callable::CallError::CALL_OK) { |
1699 | ERR_FAIL_COND_MSG(ret.get_type() != Variant::ARRAY, "Wrong type for _get_property_list, must be an array of dictionaries." ); |
1700 | |
1701 | Array arr = ret; |
1702 | for (int i = 0; i < arr.size(); i++) { |
1703 | Dictionary d = arr[i]; |
1704 | ERR_CONTINUE(!d.has("name" )); |
1705 | ERR_CONTINUE(!d.has("type" )); |
1706 | |
1707 | PropertyInfo pinfo; |
1708 | pinfo.name = d["name" ]; |
1709 | pinfo.type = Variant::Type(d["type" ].operator int()); |
1710 | if (d.has("hint" )) { |
1711 | pinfo.hint = PropertyHint(d["hint" ].operator int()); |
1712 | } |
1713 | if (d.has("hint_string" )) { |
1714 | pinfo.hint_string = d["hint_string" ]; |
1715 | } |
1716 | if (d.has("usage" )) { |
1717 | pinfo.usage = d["usage" ]; |
1718 | } |
1719 | if (d.has("class_name" )) { |
1720 | pinfo.class_name = d["class_name" ]; |
1721 | } |
1722 | |
1723 | ERR_CONTINUE(pinfo.name.is_empty() && (pinfo.usage & PROPERTY_USAGE_STORAGE)); |
1724 | ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX); |
1725 | |
1726 | props.push_back(pinfo); |
1727 | } |
1728 | } |
1729 | } |
1730 | |
1731 | //instance a fake script for editing the values |
1732 | |
1733 | Vector<_GDScriptMemberSort> msort; |
1734 | for (const KeyValue<StringName, GDScript::MemberInfo> &F : sptr->member_indices) { |
1735 | _GDScriptMemberSort ms; |
1736 | ms.index = F.value.index; |
1737 | ms.name = F.key; |
1738 | msort.push_back(ms); |
1739 | } |
1740 | |
1741 | msort.sort(); |
1742 | msort.reverse(); |
1743 | for (int i = 0; i < msort.size(); i++) { |
1744 | props.push_front(sptr->member_indices[msort[i].name].property_info); |
1745 | } |
1746 | |
1747 | #ifdef TOOLS_ENABLED |
1748 | p_properties->push_back(sptr->get_class_category()); |
1749 | #endif // TOOLS_ENABLED |
1750 | |
1751 | for (PropertyInfo &prop : props) { |
1752 | validate_property(prop); |
1753 | p_properties->push_back(prop); |
1754 | } |
1755 | |
1756 | props.clear(); |
1757 | |
1758 | sptr = sptr->_base; |
1759 | } |
1760 | } |
1761 | |
1762 | bool GDScriptInstance::property_can_revert(const StringName &p_name) const { |
1763 | Variant name = p_name; |
1764 | const Variant *args[1] = { &name }; |
1765 | |
1766 | const GDScript *sptr = script.ptr(); |
1767 | while (sptr) { |
1768 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._property_can_revert); |
1769 | if (E) { |
1770 | Callable::CallError err; |
1771 | Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err); |
1772 | if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) { |
1773 | return true; |
1774 | } |
1775 | } |
1776 | sptr = sptr->_base; |
1777 | } |
1778 | |
1779 | return false; |
1780 | } |
1781 | |
1782 | bool GDScriptInstance::property_get_revert(const StringName &p_name, Variant &r_ret) const { |
1783 | Variant name = p_name; |
1784 | const Variant *args[1] = { &name }; |
1785 | |
1786 | const GDScript *sptr = script.ptr(); |
1787 | while (sptr) { |
1788 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._property_get_revert); |
1789 | if (E) { |
1790 | Callable::CallError err; |
1791 | Variant ret = E->value->call(const_cast<GDScriptInstance *>(this), args, 1, err); |
1792 | if (err.error == Callable::CallError::CALL_OK && ret.get_type() != Variant::NIL) { |
1793 | r_ret = ret; |
1794 | return true; |
1795 | } |
1796 | } |
1797 | sptr = sptr->_base; |
1798 | } |
1799 | |
1800 | return false; |
1801 | } |
1802 | |
1803 | void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const { |
1804 | const GDScript *sptr = script.ptr(); |
1805 | while (sptr) { |
1806 | for (const KeyValue<StringName, GDScriptFunction *> &E : sptr->member_functions) { |
1807 | p_list->push_back(E.value->get_method_info()); |
1808 | } |
1809 | sptr = sptr->_base; |
1810 | } |
1811 | } |
1812 | |
1813 | bool GDScriptInstance::has_method(const StringName &p_method) const { |
1814 | const GDScript *sptr = script.ptr(); |
1815 | while (sptr) { |
1816 | HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_method); |
1817 | if (E) { |
1818 | return true; |
1819 | } |
1820 | sptr = sptr->_base; |
1821 | } |
1822 | |
1823 | return false; |
1824 | } |
1825 | |
1826 | Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { |
1827 | GDScript *sptr = script.ptr(); |
1828 | if (unlikely(p_method == SNAME("_ready" ))) { |
1829 | // Call implicit ready first, including for the super classes. |
1830 | while (sptr) { |
1831 | if (sptr->implicit_ready) { |
1832 | sptr->implicit_ready->call(this, nullptr, 0, r_error); |
1833 | } |
1834 | sptr = sptr->_base; |
1835 | } |
1836 | |
1837 | // Reset this back for the regular call. |
1838 | sptr = script.ptr(); |
1839 | } |
1840 | while (sptr) { |
1841 | HashMap<StringName, GDScriptFunction *>::Iterator E = sptr->member_functions.find(p_method); |
1842 | if (E) { |
1843 | return E->value->call(this, p_args, p_argcount, r_error); |
1844 | } |
1845 | sptr = sptr->_base; |
1846 | } |
1847 | r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; |
1848 | return Variant(); |
1849 | } |
1850 | |
1851 | void GDScriptInstance::notification(int p_notification, bool p_reversed) { |
1852 | //notification is not virtual, it gets called at ALL levels just like in C. |
1853 | Variant value = p_notification; |
1854 | const Variant *args[1] = { &value }; |
1855 | |
1856 | List<GDScript *> pl; |
1857 | GDScript *sptr = script.ptr(); |
1858 | while (sptr) { |
1859 | if (p_reversed) { |
1860 | pl.push_back(sptr); |
1861 | } else { |
1862 | pl.push_front(sptr); |
1863 | } |
1864 | sptr = sptr->_base; |
1865 | } |
1866 | for (GDScript *sc : pl) { |
1867 | HashMap<StringName, GDScriptFunction *>::Iterator E = sc->member_functions.find(GDScriptLanguage::get_singleton()->strings._notification); |
1868 | if (E) { |
1869 | Callable::CallError err; |
1870 | E->value->call(this, args, 1, err); |
1871 | if (err.error != Callable::CallError::CALL_OK) { |
1872 | //print error about notification call |
1873 | } |
1874 | } |
1875 | } |
1876 | } |
1877 | |
1878 | String GDScriptInstance::to_string(bool *r_valid) { |
1879 | if (has_method(CoreStringNames::get_singleton()->_to_string)) { |
1880 | Callable::CallError ce; |
1881 | Variant ret = callp(CoreStringNames::get_singleton()->_to_string, nullptr, 0, ce); |
1882 | if (ce.error == Callable::CallError::CALL_OK) { |
1883 | if (ret.get_type() != Variant::STRING) { |
1884 | if (r_valid) { |
1885 | *r_valid = false; |
1886 | } |
1887 | ERR_FAIL_V_MSG(String(), "Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String." ); |
1888 | } |
1889 | if (r_valid) { |
1890 | *r_valid = true; |
1891 | } |
1892 | return ret.operator String(); |
1893 | } |
1894 | } |
1895 | if (r_valid) { |
1896 | *r_valid = false; |
1897 | } |
1898 | return String(); |
1899 | } |
1900 | |
1901 | Ref<Script> GDScriptInstance::get_script() const { |
1902 | return script; |
1903 | } |
1904 | |
1905 | ScriptLanguage *GDScriptInstance::get_language() { |
1906 | return GDScriptLanguage::get_singleton(); |
1907 | } |
1908 | |
1909 | const Variant GDScriptInstance::get_rpc_config() const { |
1910 | return script->get_rpc_config(); |
1911 | } |
1912 | |
1913 | void GDScriptInstance::reload_members() { |
1914 | #ifdef DEBUG_ENABLED |
1915 | |
1916 | Vector<Variant> new_members; |
1917 | new_members.resize(script->member_indices.size()); |
1918 | |
1919 | //pass the values to the new indices |
1920 | for (KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) { |
1921 | if (member_indices_cache.has(E.key)) { |
1922 | Variant value = members[member_indices_cache[E.key]]; |
1923 | new_members.write[E.value.index] = value; |
1924 | } |
1925 | } |
1926 | |
1927 | members.resize(new_members.size()); //resize |
1928 | |
1929 | //apply |
1930 | members = new_members; |
1931 | |
1932 | //pass the values to the new indices |
1933 | member_indices_cache.clear(); |
1934 | for (const KeyValue<StringName, GDScript::MemberInfo> &E : script->member_indices) { |
1935 | member_indices_cache[E.key] = E.value.index; |
1936 | } |
1937 | |
1938 | #endif |
1939 | } |
1940 | |
1941 | GDScriptInstance::GDScriptInstance() { |
1942 | owner = nullptr; |
1943 | base_ref_counted = false; |
1944 | } |
1945 | |
1946 | GDScriptInstance::~GDScriptInstance() { |
1947 | MutexLock lock(GDScriptLanguage::get_singleton()->mutex); |
1948 | |
1949 | while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) { |
1950 | // Order matters since clearing the stack may already cause |
1951 | // the GDSCriptFunctionState to be destroyed and thus removed from the list. |
1952 | pending_func_states.remove(E); |
1953 | GDScriptFunctionState *state = E->self(); |
1954 | ObjectID state_id = state->get_instance_id(); |
1955 | state->_clear_connections(); |
1956 | if (ObjectDB::get_instance(state_id)) { |
1957 | state->_clear_stack(); |
1958 | } |
1959 | } |
1960 | |
1961 | if (script.is_valid() && owner) { |
1962 | script->instances.erase(owner); |
1963 | } |
1964 | } |
1965 | |
1966 | /************* SCRIPT LANGUAGE **************/ |
1967 | |
1968 | GDScriptLanguage *GDScriptLanguage::singleton = nullptr; |
1969 | |
1970 | String GDScriptLanguage::get_name() const { |
1971 | return "GDScript" ; |
1972 | } |
1973 | |
1974 | /* LANGUAGE FUNCTIONS */ |
1975 | |
1976 | void GDScriptLanguage::_add_global(const StringName &p_name, const Variant &p_value) { |
1977 | if (globals.has(p_name)) { |
1978 | //overwrite existing |
1979 | global_array.write[globals[p_name]] = p_value; |
1980 | return; |
1981 | } |
1982 | globals[p_name] = global_array.size(); |
1983 | global_array.push_back(p_value); |
1984 | _global_array = global_array.ptrw(); |
1985 | } |
1986 | |
1987 | void GDScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) { |
1988 | _add_global(p_variable, p_value); |
1989 | } |
1990 | |
1991 | void GDScriptLanguage::add_named_global_constant(const StringName &p_name, const Variant &p_value) { |
1992 | named_globals[p_name] = p_value; |
1993 | } |
1994 | |
1995 | Variant GDScriptLanguage::get_any_global_constant(const StringName &p_name) { |
1996 | if (named_globals.has(p_name)) { |
1997 | return named_globals[p_name]; |
1998 | } |
1999 | if (globals.has(p_name)) { |
2000 | return _global_array[globals[p_name]]; |
2001 | } |
2002 | ERR_FAIL_V_MSG(Variant(), vformat("Could not find any global constant with name: %s." , p_name)); |
2003 | } |
2004 | |
2005 | void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) { |
2006 | ERR_FAIL_COND(!named_globals.has(p_name)); |
2007 | named_globals.erase(p_name); |
2008 | } |
2009 | |
2010 | void GDScriptLanguage::init() { |
2011 | //populate global constants |
2012 | int gcc = CoreConstants::get_global_constant_count(); |
2013 | for (int i = 0; i < gcc; i++) { |
2014 | _add_global(StaticCString::create(CoreConstants::get_global_constant_name(i)), CoreConstants::get_global_constant_value(i)); |
2015 | } |
2016 | |
2017 | _add_global(StaticCString::create("PI" ), Math_PI); |
2018 | _add_global(StaticCString::create("TAU" ), Math_TAU); |
2019 | _add_global(StaticCString::create("INF" ), INFINITY); |
2020 | _add_global(StaticCString::create("NAN" ), NAN); |
2021 | |
2022 | //populate native classes |
2023 | |
2024 | List<StringName> class_list; |
2025 | ClassDB::get_class_list(&class_list); |
2026 | for (const StringName &n : class_list) { |
2027 | if (globals.has(n)) { |
2028 | continue; |
2029 | } |
2030 | Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(n)); |
2031 | _add_global(n, nc); |
2032 | } |
2033 | |
2034 | //populate singletons |
2035 | |
2036 | List<Engine::Singleton> singletons; |
2037 | Engine::get_singleton()->get_singletons(&singletons); |
2038 | for (const Engine::Singleton &E : singletons) { |
2039 | _add_global(E.name, E.ptr); |
2040 | } |
2041 | |
2042 | #ifdef TESTS_ENABLED |
2043 | GDScriptTests::GDScriptTestRunner::handle_cmdline(); |
2044 | #endif |
2045 | } |
2046 | |
2047 | String GDScriptLanguage::get_type() const { |
2048 | return "GDScript" ; |
2049 | } |
2050 | |
2051 | String GDScriptLanguage::get_extension() const { |
2052 | return "gd" ; |
2053 | } |
2054 | |
2055 | void GDScriptLanguage::finish() { |
2056 | _call_stack.free(); |
2057 | |
2058 | // Clear the cache before parsing the script_list |
2059 | GDScriptCache::clear(); |
2060 | |
2061 | // Clear dependencies between scripts, to ensure cyclic references are broken |
2062 | // (to avoid leaks at exit). |
2063 | SelfList<GDScript> *s = script_list.first(); |
2064 | while (s) { |
2065 | // This ensures the current script is not released before we can check |
2066 | // what's the next one in the list (we can't get the next upfront because we |
2067 | // don't know if the reference breaking will cause it -or any other after |
2068 | // it, for that matter- to be released so the next one is not the same as |
2069 | // before). |
2070 | Ref<GDScript> scr = s->self(); |
2071 | if (scr.is_valid()) { |
2072 | for (KeyValue<StringName, GDScriptFunction *> &E : scr->member_functions) { |
2073 | GDScriptFunction *func = E.value; |
2074 | for (int i = 0; i < func->argument_types.size(); i++) { |
2075 | func->argument_types.write[i].script_type_ref = Ref<Script>(); |
2076 | } |
2077 | func->return_type.script_type_ref = Ref<Script>(); |
2078 | } |
2079 | for (KeyValue<StringName, GDScript::MemberInfo> &E : scr->member_indices) { |
2080 | E.value.data_type.script_type_ref = Ref<Script>(); |
2081 | } |
2082 | |
2083 | // Clear backup for scripts that could slip out of the cyclic reference |
2084 | // check |
2085 | scr->clear(); |
2086 | } |
2087 | s = s->next(); |
2088 | } |
2089 | script_list.clear(); |
2090 | function_list.clear(); |
2091 | } |
2092 | |
2093 | void GDScriptLanguage::profiling_start() { |
2094 | #ifdef DEBUG_ENABLED |
2095 | MutexLock lock(this->mutex); |
2096 | |
2097 | SelfList<GDScriptFunction> *elem = function_list.first(); |
2098 | while (elem) { |
2099 | elem->self()->profile.call_count.set(0); |
2100 | elem->self()->profile.self_time.set(0); |
2101 | elem->self()->profile.total_time.set(0); |
2102 | elem->self()->profile.frame_call_count.set(0); |
2103 | elem->self()->profile.frame_self_time.set(0); |
2104 | elem->self()->profile.frame_total_time.set(0); |
2105 | elem->self()->profile.last_frame_call_count = 0; |
2106 | elem->self()->profile.last_frame_self_time = 0; |
2107 | elem->self()->profile.last_frame_total_time = 0; |
2108 | elem = elem->next(); |
2109 | } |
2110 | |
2111 | profiling = true; |
2112 | #endif |
2113 | } |
2114 | |
2115 | void GDScriptLanguage::profiling_stop() { |
2116 | #ifdef DEBUG_ENABLED |
2117 | MutexLock lock(this->mutex); |
2118 | |
2119 | profiling = false; |
2120 | #endif |
2121 | } |
2122 | |
2123 | int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) { |
2124 | int current = 0; |
2125 | #ifdef DEBUG_ENABLED |
2126 | |
2127 | MutexLock lock(this->mutex); |
2128 | |
2129 | SelfList<GDScriptFunction> *elem = function_list.first(); |
2130 | while (elem) { |
2131 | if (current >= p_info_max) { |
2132 | break; |
2133 | } |
2134 | p_info_arr[current].call_count = elem->self()->profile.call_count.get(); |
2135 | p_info_arr[current].self_time = elem->self()->profile.self_time.get(); |
2136 | p_info_arr[current].total_time = elem->self()->profile.total_time.get(); |
2137 | p_info_arr[current].signature = elem->self()->profile.signature; |
2138 | elem = elem->next(); |
2139 | current++; |
2140 | } |
2141 | #endif |
2142 | |
2143 | return current; |
2144 | } |
2145 | |
2146 | int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) { |
2147 | int current = 0; |
2148 | |
2149 | #ifdef DEBUG_ENABLED |
2150 | MutexLock lock(this->mutex); |
2151 | |
2152 | SelfList<GDScriptFunction> *elem = function_list.first(); |
2153 | while (elem) { |
2154 | if (current >= p_info_max) { |
2155 | break; |
2156 | } |
2157 | if (elem->self()->profile.last_frame_call_count > 0) { |
2158 | p_info_arr[current].call_count = elem->self()->profile.last_frame_call_count; |
2159 | p_info_arr[current].self_time = elem->self()->profile.last_frame_self_time; |
2160 | p_info_arr[current].total_time = elem->self()->profile.last_frame_total_time; |
2161 | p_info_arr[current].signature = elem->self()->profile.signature; |
2162 | current++; |
2163 | } |
2164 | elem = elem->next(); |
2165 | } |
2166 | #endif |
2167 | |
2168 | return current; |
2169 | } |
2170 | |
2171 | struct GDScriptDepSort { |
2172 | //must support sorting so inheritance works properly (parent must be reloaded first) |
2173 | bool operator()(const Ref<GDScript> &A, const Ref<GDScript> &B) const { |
2174 | if (A == B) { |
2175 | return false; //shouldn't happen but.. |
2176 | } |
2177 | const GDScript *I = B->get_base().ptr(); |
2178 | while (I) { |
2179 | if (I == A.ptr()) { |
2180 | // A is a base of B |
2181 | return true; |
2182 | } |
2183 | |
2184 | I = I->get_base().ptr(); |
2185 | } |
2186 | |
2187 | return false; //not a base |
2188 | } |
2189 | }; |
2190 | |
2191 | void GDScriptLanguage::reload_all_scripts() { |
2192 | #ifdef DEBUG_ENABLED |
2193 | print_verbose("GDScript: Reloading all scripts" ); |
2194 | List<Ref<GDScript>> scripts; |
2195 | { |
2196 | MutexLock lock(this->mutex); |
2197 | |
2198 | SelfList<GDScript> *elem = script_list.first(); |
2199 | while (elem) { |
2200 | // Scripts will reload all subclasses, so only reload root scripts. |
2201 | if (elem->self()->is_root_script() && elem->self()->get_path().is_resource_file()) { |
2202 | print_verbose("GDScript: Found: " + elem->self()->get_path()); |
2203 | scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident |
2204 | } |
2205 | elem = elem->next(); |
2206 | } |
2207 | } |
2208 | |
2209 | //as scripts are going to be reloaded, must proceed without locking here |
2210 | |
2211 | scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order |
2212 | |
2213 | for (Ref<GDScript> &scr : scripts) { |
2214 | print_verbose("GDScript: Reloading: " + scr->get_path()); |
2215 | scr->load_source_code(scr->get_path()); |
2216 | scr->reload(true); |
2217 | } |
2218 | #endif |
2219 | } |
2220 | |
2221 | void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) { |
2222 | #ifdef DEBUG_ENABLED |
2223 | |
2224 | List<Ref<GDScript>> scripts; |
2225 | { |
2226 | MutexLock lock(this->mutex); |
2227 | |
2228 | SelfList<GDScript> *elem = script_list.first(); |
2229 | while (elem) { |
2230 | // Scripts will reload all subclasses, so only reload root scripts. |
2231 | if (elem->self()->is_root_script() && elem->self()->get_path().is_resource_file()) { |
2232 | scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident |
2233 | } |
2234 | elem = elem->next(); |
2235 | } |
2236 | } |
2237 | |
2238 | //when someone asks you why dynamically typed languages are easier to write.... |
2239 | |
2240 | HashMap<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> to_reload; |
2241 | |
2242 | //as scripts are going to be reloaded, must proceed without locking here |
2243 | |
2244 | scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order |
2245 | |
2246 | for (Ref<GDScript> &scr : scripts) { |
2247 | bool reload = scr == p_script || to_reload.has(scr->get_base()); |
2248 | |
2249 | if (!reload) { |
2250 | continue; |
2251 | } |
2252 | |
2253 | to_reload.insert(scr, HashMap<ObjectID, List<Pair<StringName, Variant>>>()); |
2254 | |
2255 | if (!p_soft_reload) { |
2256 | //save state and remove script from instances |
2257 | HashMap<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[scr]; |
2258 | |
2259 | while (scr->instances.front()) { |
2260 | Object *obj = scr->instances.front()->get(); |
2261 | //save instance info |
2262 | List<Pair<StringName, Variant>> state; |
2263 | if (obj->get_script_instance()) { |
2264 | obj->get_script_instance()->get_property_state(state); |
2265 | map[obj->get_instance_id()] = state; |
2266 | obj->set_script(Variant()); |
2267 | } |
2268 | } |
2269 | |
2270 | //same thing for placeholders |
2271 | #ifdef TOOLS_ENABLED |
2272 | |
2273 | while (scr->placeholders.size()) { |
2274 | Object *obj = (*scr->placeholders.begin())->get_owner(); |
2275 | |
2276 | //save instance info |
2277 | if (obj->get_script_instance()) { |
2278 | map.insert(obj->get_instance_id(), List<Pair<StringName, Variant>>()); |
2279 | List<Pair<StringName, Variant>> &state = map[obj->get_instance_id()]; |
2280 | obj->get_script_instance()->get_property_state(state); |
2281 | obj->set_script(Variant()); |
2282 | } else { |
2283 | // no instance found. Let's remove it so we don't loop forever |
2284 | scr->placeholders.erase(*scr->placeholders.begin()); |
2285 | } |
2286 | } |
2287 | |
2288 | #endif |
2289 | |
2290 | for (const KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : scr->pending_reload_state) { |
2291 | map[F.key] = F.value; //pending to reload, use this one instead |
2292 | } |
2293 | } |
2294 | } |
2295 | |
2296 | for (KeyValue<Ref<GDScript>, HashMap<ObjectID, List<Pair<StringName, Variant>>>> &E : to_reload) { |
2297 | Ref<GDScript> scr = E.key; |
2298 | scr->reload(p_soft_reload); |
2299 | |
2300 | //restore state if saved |
2301 | for (KeyValue<ObjectID, List<Pair<StringName, Variant>>> &F : E.value) { |
2302 | List<Pair<StringName, Variant>> &saved_state = F.value; |
2303 | |
2304 | Object *obj = ObjectDB::get_instance(F.key); |
2305 | if (!obj) { |
2306 | continue; |
2307 | } |
2308 | |
2309 | if (!p_soft_reload) { |
2310 | //clear it just in case (may be a pending reload state) |
2311 | obj->set_script(Variant()); |
2312 | } |
2313 | obj->set_script(scr); |
2314 | |
2315 | ScriptInstance *script_inst = obj->get_script_instance(); |
2316 | |
2317 | if (!script_inst) { |
2318 | //failed, save reload state for next time if not saved |
2319 | if (!scr->pending_reload_state.has(obj->get_instance_id())) { |
2320 | scr->pending_reload_state[obj->get_instance_id()] = saved_state; |
2321 | } |
2322 | continue; |
2323 | } |
2324 | |
2325 | if (script_inst->is_placeholder() && scr->is_placeholder_fallback_enabled()) { |
2326 | PlaceHolderScriptInstance *placeholder = static_cast<PlaceHolderScriptInstance *>(script_inst); |
2327 | for (List<Pair<StringName, Variant>>::Element *G = saved_state.front(); G; G = G->next()) { |
2328 | placeholder->property_set_fallback(G->get().first, G->get().second); |
2329 | } |
2330 | } else { |
2331 | for (List<Pair<StringName, Variant>>::Element *G = saved_state.front(); G; G = G->next()) { |
2332 | script_inst->set(G->get().first, G->get().second); |
2333 | } |
2334 | } |
2335 | |
2336 | scr->pending_reload_state.erase(obj->get_instance_id()); //as it reloaded, remove pending state |
2337 | } |
2338 | |
2339 | //if instance states were saved, set them! |
2340 | } |
2341 | |
2342 | #endif |
2343 | } |
2344 | |
2345 | void GDScriptLanguage::frame() { |
2346 | calls = 0; |
2347 | |
2348 | #ifdef DEBUG_ENABLED |
2349 | if (profiling) { |
2350 | MutexLock lock(this->mutex); |
2351 | |
2352 | SelfList<GDScriptFunction> *elem = function_list.first(); |
2353 | while (elem) { |
2354 | elem->self()->profile.last_frame_call_count = elem->self()->profile.frame_call_count.get(); |
2355 | elem->self()->profile.last_frame_self_time = elem->self()->profile.frame_self_time.get(); |
2356 | elem->self()->profile.last_frame_total_time = elem->self()->profile.frame_total_time.get(); |
2357 | elem->self()->profile.frame_call_count.set(0); |
2358 | elem->self()->profile.frame_self_time.set(0); |
2359 | elem->self()->profile.frame_total_time.set(0); |
2360 | elem = elem->next(); |
2361 | } |
2362 | } |
2363 | |
2364 | #endif |
2365 | } |
2366 | |
2367 | /* EDITOR FUNCTIONS */ |
2368 | void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { |
2369 | // TODO: Add annotations here? |
2370 | static const char *_reserved_words[] = { |
2371 | // operators |
2372 | "and" , |
2373 | "in" , |
2374 | "not" , |
2375 | "or" , |
2376 | // types and values |
2377 | "false" , |
2378 | "float" , |
2379 | "int" , |
2380 | "bool" , |
2381 | "null" , |
2382 | "PI" , |
2383 | "TAU" , |
2384 | "INF" , |
2385 | "NAN" , |
2386 | "self" , |
2387 | "true" , |
2388 | "void" , |
2389 | // functions |
2390 | "as" , |
2391 | "assert" , |
2392 | "await" , |
2393 | "breakpoint" , |
2394 | "class" , |
2395 | "class_name" , |
2396 | "extends" , |
2397 | "is" , |
2398 | "func" , |
2399 | "preload" , |
2400 | "signal" , |
2401 | "super" , |
2402 | // var |
2403 | "const" , |
2404 | "enum" , |
2405 | "static" , |
2406 | "var" , |
2407 | // control flow |
2408 | "break" , |
2409 | "continue" , |
2410 | "if" , |
2411 | "elif" , |
2412 | "else" , |
2413 | "for" , |
2414 | "pass" , |
2415 | "return" , |
2416 | "match" , |
2417 | "while" , |
2418 | // These keywords are not implemented currently, but reserved for (potential) future use. |
2419 | // We highlight them as keywords to make errors easier to understand. |
2420 | "trait" , |
2421 | "namespace" , |
2422 | "yield" , |
2423 | nullptr |
2424 | }; |
2425 | |
2426 | const char **w = _reserved_words; |
2427 | |
2428 | while (*w) { |
2429 | p_words->push_back(*w); |
2430 | w++; |
2431 | } |
2432 | |
2433 | List<StringName> functions; |
2434 | GDScriptUtilityFunctions::get_function_list(&functions); |
2435 | |
2436 | for (const StringName &E : functions) { |
2437 | p_words->push_back(String(E)); |
2438 | } |
2439 | } |
2440 | |
2441 | bool GDScriptLanguage::is_control_flow_keyword(String p_keyword) const { |
2442 | return p_keyword == "break" || |
2443 | p_keyword == "continue" || |
2444 | p_keyword == "elif" || |
2445 | p_keyword == "else" || |
2446 | p_keyword == "if" || |
2447 | p_keyword == "for" || |
2448 | p_keyword == "match" || |
2449 | p_keyword == "pass" || |
2450 | p_keyword == "return" || |
2451 | p_keyword == "while" ; |
2452 | } |
2453 | |
2454 | bool GDScriptLanguage::handles_global_class_type(const String &p_type) const { |
2455 | return p_type == "GDScript" ; |
2456 | } |
2457 | |
2458 | String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { |
2459 | Error err; |
2460 | Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err); |
2461 | if (err) { |
2462 | return String(); |
2463 | } |
2464 | |
2465 | String source = f->get_as_utf8_string(); |
2466 | |
2467 | GDScriptParser parser; |
2468 | err = parser.parse(source, p_path, false); |
2469 | |
2470 | const GDScriptParser::ClassNode *c = parser.get_tree(); |
2471 | if (!c) { |
2472 | return String(); // No class parsed. |
2473 | } |
2474 | |
2475 | /* **WARNING** |
2476 | * |
2477 | * This function is written with the goal to be *extremely* error tolerant, as such |
2478 | * it should meet the following requirements: |
2479 | * |
2480 | * - It must not rely on the analyzer (in fact, the analyzer must not be used here), |
2481 | * because at the time global classes are parsed, the dependencies may not be present |
2482 | * yet, hence the function will fail (which is unintended). |
2483 | * - It must not fail even if the parsing fails, because even if the file is broken, |
2484 | * it should attempt its best to retrieve the inheritance metadata. |
2485 | * |
2486 | * Before changing this function, please ask the current maintainer of EditorFileSystem. |
2487 | */ |
2488 | |
2489 | if (r_base_type) { |
2490 | const GDScriptParser::ClassNode *subclass = c; |
2491 | String path = p_path; |
2492 | GDScriptParser subparser; |
2493 | while (subclass) { |
2494 | if (subclass->extends_used) { |
2495 | if (!subclass->extends_path.is_empty()) { |
2496 | if (subclass->extends.size() == 0) { |
2497 | get_global_class_name(subclass->extends_path, r_base_type); |
2498 | subclass = nullptr; |
2499 | break; |
2500 | } else { |
2501 | Vector<GDScriptParser::IdentifierNode *> extend_classes = subclass->extends; |
2502 | |
2503 | Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); |
2504 | if (subfile.is_null()) { |
2505 | break; |
2506 | } |
2507 | String subsource = subfile->get_as_utf8_string(); |
2508 | |
2509 | if (subsource.is_empty()) { |
2510 | break; |
2511 | } |
2512 | String subpath = subclass->extends_path; |
2513 | if (subpath.is_relative_path()) { |
2514 | subpath = path.get_base_dir().path_join(subpath).simplify_path(); |
2515 | } |
2516 | |
2517 | if (OK != subparser.parse(subsource, subpath, false)) { |
2518 | break; |
2519 | } |
2520 | path = subpath; |
2521 | subclass = subparser.get_tree(); |
2522 | |
2523 | while (extend_classes.size() > 0) { |
2524 | bool found = false; |
2525 | for (int i = 0; i < subclass->members.size(); i++) { |
2526 | if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) { |
2527 | continue; |
2528 | } |
2529 | |
2530 | const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class; |
2531 | if (inner_class->identifier->name == extend_classes[0]->name) { |
2532 | extend_classes.remove_at(0); |
2533 | found = true; |
2534 | subclass = inner_class; |
2535 | break; |
2536 | } |
2537 | } |
2538 | if (!found) { |
2539 | subclass = nullptr; |
2540 | break; |
2541 | } |
2542 | } |
2543 | } |
2544 | } else if (subclass->extends.size() == 1) { |
2545 | *r_base_type = subclass->extends[0]->name; |
2546 | subclass = nullptr; |
2547 | } else { |
2548 | break; |
2549 | } |
2550 | } else { |
2551 | *r_base_type = "RefCounted" ; |
2552 | subclass = nullptr; |
2553 | } |
2554 | } |
2555 | } |
2556 | if (r_icon_path) { |
2557 | *r_icon_path = c->simplified_icon_path; |
2558 | } |
2559 | return c->identifier != nullptr ? String(c->identifier->name) : String(); |
2560 | } |
2561 | |
2562 | thread_local GDScriptLanguage::CallStack GDScriptLanguage::_call_stack; |
2563 | |
2564 | GDScriptLanguage::GDScriptLanguage() { |
2565 | calls = 0; |
2566 | ERR_FAIL_COND(singleton); |
2567 | singleton = this; |
2568 | strings._init = StaticCString::create("_init" ); |
2569 | strings._static_init = StaticCString::create("_static_init" ); |
2570 | strings._notification = StaticCString::create("_notification" ); |
2571 | strings._set = StaticCString::create("_set" ); |
2572 | strings._get = StaticCString::create("_get" ); |
2573 | strings._get_property_list = StaticCString::create("_get_property_list" ); |
2574 | strings._validate_property = StaticCString::create("_validate_property" ); |
2575 | strings._property_can_revert = StaticCString::create("_property_can_revert" ); |
2576 | strings._property_get_revert = StaticCString::create("_property_get_revert" ); |
2577 | strings._script_source = StaticCString::create("script/source" ); |
2578 | _debug_parse_err_line = -1; |
2579 | _debug_parse_err_file = "" ; |
2580 | |
2581 | profiling = false; |
2582 | script_frame_time = 0; |
2583 | |
2584 | int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack" , PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1" ), 1024); |
2585 | |
2586 | if (EngineDebugger::is_active()) { |
2587 | //debugging enabled! |
2588 | |
2589 | _debug_max_call_stack = dmcs; |
2590 | } else { |
2591 | _debug_max_call_stack = 0; |
2592 | } |
2593 | |
2594 | #ifdef DEBUG_ENABLED |
2595 | GLOBAL_DEF("debug/gdscript/warnings/enable" , true); |
2596 | GLOBAL_DEF("debug/gdscript/warnings/exclude_addons" , true); |
2597 | for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { |
2598 | GDScriptWarning::Code code = (GDScriptWarning::Code)i; |
2599 | Variant default_enabled = GDScriptWarning::get_default_value(code); |
2600 | String path = GDScriptWarning::get_settings_path_from_code(code); |
2601 | GLOBAL_DEF(GDScriptWarning::get_property_info(code), default_enabled); |
2602 | } |
2603 | #endif // DEBUG_ENABLED |
2604 | } |
2605 | |
2606 | GDScriptLanguage::~GDScriptLanguage() { |
2607 | singleton = nullptr; |
2608 | } |
2609 | |
2610 | void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass) { |
2611 | orphan_subclasses[p_qualified_name] = p_subclass; |
2612 | } |
2613 | |
2614 | Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_name) { |
2615 | HashMap<String, ObjectID>::Iterator orphan_subclass_element = orphan_subclasses.find(p_qualified_name); |
2616 | if (!orphan_subclass_element) { |
2617 | return Ref<GDScript>(); |
2618 | } |
2619 | ObjectID orphan_subclass = orphan_subclass_element->value; |
2620 | Object *obj = ObjectDB::get_instance(orphan_subclass); |
2621 | orphan_subclasses.remove(orphan_subclass_element); |
2622 | if (!obj) { |
2623 | return Ref<GDScript>(); |
2624 | } |
2625 | return Ref<GDScript>(Object::cast_to<GDScript>(obj)); |
2626 | } |
2627 | |
2628 | Ref<GDScript> GDScriptLanguage::get_script_by_fully_qualified_name(const String &p_name) { |
2629 | { |
2630 | MutexLock lock(mutex); |
2631 | |
2632 | SelfList<GDScript> *elem = script_list.first(); |
2633 | while (elem) { |
2634 | GDScript *scr = elem->self(); |
2635 | if (scr->fully_qualified_name == p_name) { |
2636 | return scr; |
2637 | } |
2638 | elem = elem->next(); |
2639 | } |
2640 | } |
2641 | |
2642 | Ref<GDScript> scr; |
2643 | scr.instantiate(); |
2644 | scr->fully_qualified_name = p_name; |
2645 | return scr; |
2646 | } |
2647 | |
2648 | /*************** RESOURCE ***************/ |
2649 | |
2650 | Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { |
2651 | Error err; |
2652 | Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "" , p_cache_mode == CACHE_MODE_IGNORE); |
2653 | |
2654 | if (err && scr.is_valid()) { |
2655 | // If !scr.is_valid(), the error was likely from scr->load_source_code(), which already generates an error. |
2656 | ERR_PRINT_ED(vformat(R"(Failed to load script "%s" with error "%s".)" , p_path, error_names[err])); |
2657 | } |
2658 | |
2659 | if (r_error) { |
2660 | // Don't fail loading because of parsing error. |
2661 | *r_error = scr.is_valid() ? OK : err; |
2662 | } |
2663 | |
2664 | return scr; |
2665 | } |
2666 | |
2667 | void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const { |
2668 | p_extensions->push_back("gd" ); |
2669 | } |
2670 | |
2671 | bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const { |
2672 | return (p_type == "Script" || p_type == "GDScript" ); |
2673 | } |
2674 | |
2675 | String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const { |
2676 | String el = p_path.get_extension().to_lower(); |
2677 | if (el == "gd" ) { |
2678 | return "GDScript" ; |
2679 | } |
2680 | return "" ; |
2681 | } |
2682 | |
2683 | void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { |
2684 | Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ); |
2685 | ERR_FAIL_COND_MSG(file.is_null(), "Cannot open file '" + p_path + "'." ); |
2686 | |
2687 | String source = file->get_as_utf8_string(); |
2688 | if (source.is_empty()) { |
2689 | return; |
2690 | } |
2691 | |
2692 | GDScriptParser parser; |
2693 | if (OK != parser.parse(source, p_path, false)) { |
2694 | return; |
2695 | } |
2696 | |
2697 | for (const String &E : parser.get_dependencies()) { |
2698 | p_dependencies->push_back(E); |
2699 | } |
2700 | } |
2701 | |
2702 | Error ResourceFormatSaverGDScript::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { |
2703 | Ref<GDScript> sqscr = p_resource; |
2704 | ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); |
2705 | |
2706 | String source = sqscr->get_source_code(); |
2707 | |
2708 | { |
2709 | Error err; |
2710 | Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err); |
2711 | |
2712 | ERR_FAIL_COND_V_MSG(err, err, "Cannot save GDScript file '" + p_path + "'." ); |
2713 | |
2714 | file->store_string(source); |
2715 | if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) { |
2716 | return ERR_CANT_CREATE; |
2717 | } |
2718 | } |
2719 | |
2720 | if (ScriptServer::is_reload_scripts_on_save_enabled()) { |
2721 | GDScriptLanguage::get_singleton()->reload_tool_script(p_resource, true); |
2722 | } |
2723 | |
2724 | return OK; |
2725 | } |
2726 | |
2727 | void ResourceFormatSaverGDScript::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const { |
2728 | if (Object::cast_to<GDScript>(*p_resource)) { |
2729 | p_extensions->push_back("gd" ); |
2730 | } |
2731 | } |
2732 | |
2733 | bool ResourceFormatSaverGDScript::recognize(const Ref<Resource> &p_resource) const { |
2734 | return Object::cast_to<GDScript>(*p_resource) != nullptr; |
2735 | } |
2736 | |