diff options
Diffstat (limited to 'modules/gdscript/gdscript.cpp')
-rw-r--r-- | modules/gdscript/gdscript.cpp | 1179 |
1 files changed, 611 insertions, 568 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 06ab9e226d..502e294275 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,24 +32,26 @@ #include <stdint.h> +#include "core/config/engine.h" +#include "core/config/project_settings.h" +#include "core/core_constants.h" #include "core/core_string_names.h" -#include "core/engine.h" -#include "core/global_constants.h" #include "core/io/file_access_encrypted.h" #include "core/os/file_access.h" #include "core/os/os.h" -#include "core/project_settings.h" +#include "gdscript_analyzer.h" +#include "gdscript_cache.h" #include "gdscript_compiler.h" +#include "gdscript_parser.h" +#include "gdscript_warning.h" /////////////////////////// GDScriptNativeClass::GDScriptNativeClass(const StringName &p_name) { - name = p_name; } bool GDScriptNativeClass::_get(const StringName &p_name, Variant &r_ret) const { - bool ok; int v = ClassDB::get_integer_constant(name, p_name, &ok); @@ -62,12 +64,10 @@ bool GDScriptNativeClass::_get(const StringName &p_name, Variant &r_ret) const { } void GDScriptNativeClass::_bind_methods() { - ClassDB::bind_method(D_METHOD("new"), &GDScriptNativeClass::_new); } Variant GDScriptNativeClass::_new() { - Object *o = instance(); ERR_FAIL_COND_V_MSG(!o, Variant(), "Class type: '" + String(name) + "' is not instantiable."); @@ -80,12 +80,21 @@ Variant GDScriptNativeClass::_new() { } Object *GDScriptNativeClass::instance() { - return ClassDB::instance(name); } -GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error) { +void GDScript::_super_implicit_constructor(GDScript *p_script, GDScriptInstance *p_instance, Callable::CallError &r_error) { + GDScript *base = p_script->_base; + if (base != nullptr) { + _super_implicit_constructor(base, p_instance, r_error); + if (r_error.error != Callable::CallError::CALL_OK) { + return; + } + } + p_script->implicit_initializer->call(p_instance, nullptr, 0, r_error); +} +GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_isref, Callable::CallError &r_error) { /* STEP 1, CREATE */ GDScriptInstance *instance = memnew(GDScriptInstance); @@ -107,10 +116,8 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco MutexLock lock(GDScriptLanguage::singleton->lock); instances.insert(instance->owner); } - if (p_argcount < 0) { - return instance; - } - initializer->call(instance, p_args, p_argcount, r_error); + + _super_implicit_constructor(this, instance, r_error); if (r_error.error != Callable::CallError::CALL_OK) { instance->script = Ref<GDScript>(); instance->owner->set_script_instance(nullptr); @@ -120,12 +127,27 @@ GDScriptInstance *GDScript::_create_instance(const Variant **p_args, int p_argco } ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance."); } + + if (p_argcount < 0) { + return instance; + } + if (initializer != nullptr) { + initializer->call(instance, p_args, p_argcount, r_error); + if (r_error.error != Callable::CallError::CALL_OK) { + instance->script = Ref<GDScript>(); + instance->owner->set_script_instance(nullptr); + { + MutexLock lock(GDScriptLanguage::singleton->lock); + instances.erase(p_owner); + } + ERR_FAIL_V_MSG(nullptr, "Error constructing a GDScriptInstance."); + } + } //@TODO make thread safe return instance; } Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - /* STEP 1, CREATE */ if (!valid) { @@ -171,7 +193,6 @@ Variant GDScript::_new(const Variant **p_args, int p_argcount, Callable::CallErr } bool GDScript::can_instance() const { - #ifdef TOOLS_ENABLED return valid && (tool || ScriptServer::is_scripting_enabled()); #else @@ -180,7 +201,6 @@ bool GDScript::can_instance() const { } Ref<Script> GDScript::get_base_script() const { - if (_base) { return Ref<GDScript>(_base); } else { @@ -189,16 +209,16 @@ Ref<Script> GDScript::get_base_script() const { } StringName GDScript::get_instance_base_type() const { - - if (native.is_valid()) + if (native.is_valid()) { return native->get_name(); - if (base.is_valid() && base->is_valid()) + } + if (base.is_valid() && base->is_valid()) { return base->get_instance_base_type(); + } return StringName(); } struct _GDScriptMemberSort { - int index; StringName name; _FORCE_INLINE_ bool operator<(const _GDScriptMemberSort &p_member) const { return index < p_member.index; } @@ -207,13 +227,11 @@ struct _GDScriptMemberSort { #ifdef TOOLS_ENABLED void GDScript::_placeholder_erased(PlaceHolderScriptInstance *p_placeholder) { - placeholders.erase(p_placeholder); } #endif -void GDScript::get_script_method_list(List<MethodInfo> *p_list) const { - +void GDScript::_get_script_method_list(List<MethodInfo> *r_list, bool p_include_base) const { const GDScript *current = this; while (current) { for (const Map<StringName, GDScriptFunction *>::Element *E = current->member_functions.front(); E; E = E->next()) { @@ -221,27 +239,35 @@ void GDScript::get_script_method_list(List<MethodInfo> *p_list) const { MethodInfo mi; mi.name = E->key(); for (int i = 0; i < func->get_argument_count(); i++) { - mi.arguments.push_back(func->get_argument_type(i)); + PropertyInfo arginfo = func->get_argument_type(i); +#ifdef TOOLS_ENABLED + arginfo.name = func->get_argument_name(i); +#endif + mi.arguments.push_back(arginfo); } mi.return_val = func->get_return_type(); - p_list->push_back(mi); + r_list->push_back(mi); + } + if (!p_include_base) { + return; } current = current->_base; } } -void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const { +void GDScript::get_script_method_list(List<MethodInfo> *r_list) const { + _get_script_method_list(r_list, true); +} +void GDScript::_get_script_property_list(List<PropertyInfo> *r_list, bool p_include_base) const { const GDScript *sptr = this; List<PropertyInfo> props; while (sptr) { - Vector<_GDScriptMemberSort> msort; for (Map<StringName, PropertyInfo>::Element *E = sptr->member_info.front(); E; E = E->next()) { - _GDScriptMemberSort ms; ERR_CONTINUE(!sptr->member_indices.has(E->key())); ms.index = sptr->member_indices[E->key()].index; @@ -252,28 +278,33 @@ void GDScript::get_script_property_list(List<PropertyInfo> *p_list) const { msort.sort(); msort.invert(); for (int i = 0; i < msort.size(); i++) { - props.push_front(sptr->member_info[msort[i].name]); } + if (!p_include_base) { + break; + } sptr = sptr->_base; } for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - p_list->push_back(E->get()); + r_list->push_back(E->get()); } } -bool GDScript::has_method(const StringName &p_method) const { +void GDScript::get_script_property_list(List<PropertyInfo> *r_list) const { + _get_script_property_list(r_list, true); +} +bool GDScript::has_method(const StringName &p_method) const { return member_functions.has(p_method); } MethodInfo GDScript::get_method_info(const StringName &p_method) const { - const Map<StringName, GDScriptFunction *>::Element *E = member_functions.find(p_method); - if (!E) + if (!E) { return MethodInfo(); + } GDScriptFunction *func = E->get(); MethodInfo mi; @@ -287,7 +318,6 @@ MethodInfo GDScript::get_method_info(const StringName &p_method) const { } bool GDScript::get_property_default_value(const StringName &p_property, Variant &r_value) const { - #ifdef TOOLS_ENABLED const Map<StringName, Variant>::Element *E = member_default_values_cache.find(p_property); @@ -304,14 +334,13 @@ bool GDScript::get_property_default_value(const StringName &p_property, Variant } ScriptInstance *GDScript::instance_create(Object *p_this) { - GDScript *top = this; - while (top->_base) + while (top->_base) { top = top->_base; + } if (top->native.is_valid()) { if (!ClassDB::is_parent_class(p_this->get_class_name(), top->native->get_name())) { - if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), 1, "Script inherits from native type '" + String(top->native->get_name()) + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'"); } @@ -335,24 +364,23 @@ PlaceHolderScriptInstance *GDScript::placeholder_instance_create(Object *p_this) } bool GDScript::instance_has(const Object *p_this) const { - MutexLock lock(GDScriptLanguage::singleton->lock); return instances.has((Object *)p_this); } bool GDScript::has_source_code() const { - return source != ""; } -String GDScript::get_source_code() const { +String GDScript::get_source_code() const { return source; } -void GDScript::set_source_code(const String &p_code) { - if (source == p_code) +void GDScript::set_source_code(const String &p_code) { + if (source == p_code) { return; + } source = p_code; #ifdef TOOLS_ENABLED source_changed_cache = true; @@ -361,7 +389,6 @@ void GDScript::set_source_code(const String &p_code) { #ifdef TOOLS_ENABLED void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<PropertyInfo> &propnames) { - if (base_cache.is_valid()) { base_cache->_update_exports_values(values, propnames); } @@ -374,12 +401,194 @@ void GDScript::_update_exports_values(Map<StringName, Variant> &values, List<Pro propnames.push_back(E->get()); } } -#endif -bool GDScript::_update_exports() { +void GDScript::_add_doc(const DocData::ClassDoc &p_inner_class) { + if (_owner) { + _owner->_add_doc(p_inner_class); + } else { + for (int i = 0; i < docs.size(); i++) { + if (docs[i].name == p_inner_class.name) { + docs.remove(i); + break; + } + } + docs.append(p_inner_class); + } +} + +void GDScript::_clear_doc() { + docs.clear(); + doc = DocData::ClassDoc(); +} + +void GDScript::_update_doc() { + _clear_doc(); + + doc.script_path = "\"" + get_path().get_slice("://", 1) + "\""; + if (!name.is_empty()) { + doc.name = name; + } else { + doc.name = doc.script_path; + } + + if (_owner) { + doc.name = _owner->doc.name + "." + doc.name; + doc.script_path = doc.script_path + "." + doc.name; + } + + doc.is_script_doc = true; + + if (base.is_valid() && base->is_valid()) { + if (base->doc.name != String()) { + doc.inherits = base->doc.name; + } else { + doc.inherits = base->get_instance_base_type(); + } + } else if (native.is_valid()) { + doc.inherits = native->get_name(); + } + + doc.brief_description = doc_brief_description; + doc.description = doc_description; + doc.tutorials = doc_tutorials; + + for (Map<String, DocData::EnumDoc>::Element *E = doc_enums.front(); E; E = E->next()) { + if (E->value().description != "") { + doc.enums[E->key()] = E->value().description; + } + } + + List<MethodInfo> methods; + _get_script_method_list(&methods, false); + for (int i = 0; i < methods.size(); i++) { + // Ignore internal methods. + if (methods[i].name[0] == '@') { + continue; + } + + DocData::MethodDoc method_doc; + const String &class_name = methods[i].name; + if (member_functions.has(class_name)) { + GDScriptFunction *fn = member_functions[class_name]; + + // Change class name if return type is script reference. + GDScriptDataType return_type = fn->get_return_type(); + if (return_type.kind == GDScriptDataType::GDSCRIPT) { + methods[i].return_val.class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(return_type.script_type)); + } + + // Change class name if argumetn is script reference. + for (int j = 0; j < fn->get_argument_count(); j++) { + GDScriptDataType arg_type = fn->get_argument_type(j); + if (arg_type.kind == GDScriptDataType::GDSCRIPT) { + methods[i].arguments[j].class_name = _get_gdscript_reference_class_name(Object::cast_to<GDScript>(arg_type.script_type)); + } + } + } + if (doc_functions.has(methods[i].name)) { + DocData::method_doc_from_methodinfo(method_doc, methods[i], doc_functions[methods[i].name]); + } else { + DocData::method_doc_from_methodinfo(method_doc, methods[i], String()); + } + doc.methods.push_back(method_doc); + } + + List<PropertyInfo> props; + _get_script_property_list(&props, false); + for (int i = 0; i < props.size(); i++) { + ScriptMemberInfo scr_member_info; + scr_member_info.propinfo = props[i]; + scr_member_info.propinfo.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + if (member_indices.has(props[i].name)) { + const MemberInfo &mi = member_indices[props[i].name]; + scr_member_info.setter = mi.setter; + scr_member_info.getter = mi.getter; + if (mi.data_type.kind == GDScriptDataType::GDSCRIPT) { + scr_member_info.propinfo.class_name = _get_gdscript_reference_class_name( + Object::cast_to<GDScript>(mi.data_type.script_type)); + } + } + if (member_default_values.has(props[i].name)) { + scr_member_info.has_default_value = true; + scr_member_info.default_value = member_default_values[props[i].name]; + } + if (doc_variables.has(props[i].name)) { + scr_member_info.doc_string = doc_variables[props[i].name]; + } + + DocData::PropertyDoc prop_doc; + DocData::property_doc_from_scriptmemberinfo(prop_doc, scr_member_info); + doc.properties.push_back(prop_doc); + } + + List<MethodInfo> signals; + _get_script_signal_list(&signals, false); + for (int i = 0; i < signals.size(); i++) { + DocData::MethodDoc signal_doc; + if (doc_signals.has(signals[i].name)) { + DocData::signal_doc_from_methodinfo(signal_doc, signals[i], signals[i].name); + } else { + DocData::signal_doc_from_methodinfo(signal_doc, signals[i], String()); + } + doc.signals.push_back(signal_doc); + } + + for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { + if (subclasses.has(E->key())) { + continue; + } + + // Enums. + bool is_enum = false; + if (E->value().get_type() == Variant::DICTIONARY) { + if (doc_enums.has(E->key())) { + is_enum = true; + for (int i = 0; i < doc_enums[E->key()].values.size(); i++) { + doc_enums[E->key()].values.write[i].enumeration = E->key(); + doc.constants.push_back(doc_enums[E->key()].values[i]); + } + } + } + if (!is_enum && doc_enums.has("@unnamed_enums")) { + for (int i = 0; i < doc_enums["@unnamed_enums"].values.size(); i++) { + if (E->key() == doc_enums["@unnamed_enums"].values[i].name) { + is_enum = true; + DocData::ConstantDoc constant_doc; + constant_doc.enumeration = "@unnamed_enums"; + DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_enums["@unnamed_enums"].values[i].description); + doc.constants.push_back(constant_doc); + break; + } + } + } + if (!is_enum) { + DocData::ConstantDoc constant_doc; + String doc_description; + if (doc_constants.has(E->key())) { + doc_description = doc_constants[E->key()]; + } + DocData::constant_doc_from_variant(constant_doc, E->key(), E->value(), doc_description); + doc.constants.push_back(constant_doc); + } + } + + for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) { + E->get()->_update_doc(); + } + + _add_doc(doc); +} +#endif +bool GDScript::_update_exports(bool *r_err, bool p_recursive_call) { #ifdef TOOLS_ENABLED + static Vector<GDScript *> base_caches; + if (!p_recursive_call) { + base_caches.clear(); + } + base_caches.append(this); + bool changed = false; if (source_changed_cache) { @@ -388,21 +597,20 @@ bool GDScript::_update_exports() { String basedir = path; - if (basedir == "") + if (basedir == "") { basedir = get_path(); + } - if (basedir != "") + if (basedir != "") { basedir = basedir.get_base_dir(); + } GDScriptParser parser; - Error err = parser.parse(source, basedir, true, path); + GDScriptAnalyzer analyzer(&parser); + Error err = parser.parse(source, path, false); - if (err == OK) { - - const GDScriptParser::Node *root = parser.get_parse_tree(); - ERR_FAIL_COND_V(root->type != GDScriptParser::Node::TYPE_CLASS, false); - - const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(root); + if (err == OK && analyzer.analyze() == OK) { + const GDScriptParser::ClassNode *c = parser.get_tree(); if (base_cache.is_valid()) { base_cache->inheriters_cache.erase(get_instance_id()); @@ -411,32 +619,29 @@ bool GDScript::_update_exports() { if (c->extends_used) { String path = ""; - if (String(c->extends_file) != "" && String(c->extends_file) != get_path()) { - path = c->extends_file; + if (String(c->extends_path) != "" && String(c->extends_path) != get_path()) { + path = c->extends_path; if (path.is_rel_path()) { - String base = get_path(); if (base == "" || base.is_rel_path()) { - ERR_PRINT(("Could not resolve relative path for parent class: " + path).utf8().get_data()); } else { path = base.get_base_dir().plus_file(path); } } - } else if (c->extends_class.size() != 0) { - String base = c->extends_class[0]; + } else if (c->extends.size() != 0) { + const StringName &base = c->extends[0]; - if (ScriptServer::is_global_class(base)) + if (ScriptServer::is_global_class(base)) { path = ScriptServer::get_global_class_path(base); + } } if (path != "") { if (path != get_path()) { - Ref<GDScript> bf = ResourceLoader::load(path); if (bf.is_valid()) { - base_cache = bf; bf->inheriters_cache.insert(get_instance_id()); } @@ -448,19 +653,36 @@ bool GDScript::_update_exports() { members_cache.clear(); member_default_values_cache.clear(); + _signals.clear(); - for (int i = 0; i < c->variables.size(); i++) { - if (c->variables[i]._export.type == Variant::NIL) - continue; - - members_cache.push_back(c->variables[i]._export); - member_default_values_cache[c->variables[i].identifier] = c->variables[i].default_value; - } + for (int i = 0; i < c->members.size(); i++) { + const GDScriptParser::ClassNode::Member &member = c->members[i]; - _signals.clear(); + switch (member.type) { + case GDScriptParser::ClassNode::Member::VARIABLE: { + if (!member.variable->exported) { + continue; + } - for (int i = 0; i < c->_signals.size(); i++) { - _signals[c->_signals[i].name] = c->_signals[i].arguments; + members_cache.push_back(member.variable->export_info); + Variant default_value; + if (member.variable->initializer && member.variable->initializer->is_constant) { + default_value = member.variable->initializer->reduced_value; + } + member_default_values_cache[member.variable->identifier->name] = default_value; + } break; + case GDScriptParser::ClassNode::Member::SIGNAL: { + // TODO: Cache this in parser to avoid loops like this. + Vector<StringName> parameters_names; + parameters_names.resize(member.signal->parameters.size()); + for (int j = 0; j < member.signal->parameters.size(); j++) { + parameters_names.write[j] = member.signal->parameters[j]->identifier->name; + } + _signals[member.signal->identifier->name] = parameters_names; + } break; + default: + break; // Nothing. + } } } else { placeholder_fallback_enabled = true; @@ -473,7 +695,24 @@ bool GDScript::_update_exports() { placeholder_fallback_enabled = false; if (base_cache.is_valid() && base_cache->is_valid()) { - if (base_cache->_update_exports()) { + for (int i = 0; i < base_caches.size(); i++) { + if (base_caches[i] == base_cache.ptr()) { + if (r_err) { + *r_err = true; + } + valid = false; // to show error in the editor + base_cache->valid = false; + base_cache->inheriters_cache.clear(); // to prevent future stackoverflows + base_cache.unref(); + base.unref(); + _base = nullptr; + ERR_FAIL_V_MSG(false, "Cyclic inheritance in script class."); + } + } + if (base_cache->_update_exports(r_err, true)) { + if (r_err && *r_err) { + return false; + } changed = true; } } @@ -498,18 +737,22 @@ bool GDScript::_update_exports() { } void GDScript::update_exports() { - #ifdef TOOLS_ENABLED - _update_exports(); + bool cyclic_error = false; + _update_exports(&cyclic_error); + if (cyclic_error) { + return; + } Set<ObjectID> copy = inheriters_cache; //might get modified for (Set<ObjectID>::Element *E = copy.front(); E; E = E->next()) { Object *id = ObjectDB::get_instance(E->get()); GDScript *s = Object::cast_to<GDScript>(id); - if (!s) + if (!s) { continue; + } s->update_exports(); } @@ -517,16 +760,13 @@ void GDScript::update_exports() { } void GDScript::_set_subclass_path(Ref<GDScript> &p_sc, const String &p_path) { - p_sc->path = p_path; for (Map<StringName, Ref<GDScript>>::Element *E = p_sc->subclasses.front(); E; E = E->next()) { - _set_subclass_path(E->get(), p_path); } } Error GDScript::reload(bool p_keep_state) { - bool has_instances; { MutexLock lock(GDScriptLanguage::singleton->lock); @@ -538,40 +778,75 @@ Error GDScript::reload(bool p_keep_state) { String basedir = path; - if (basedir == "") + if (basedir == "") { basedir = get_path(); + } - if (basedir != "") + if (basedir != "") { basedir = basedir.get_base_dir(); + } if (source.find("%BASE%") != -1) { //loading a template, don't parse return OK; } + { + String source_path = path; + if (source_path.is_empty()) { + source_path = get_path(); + } + if (!source_path.is_empty()) { + MutexLock lock(GDScriptCache::singleton->lock); + if (!GDScriptCache::singleton->shallow_gdscript_cache.has(source_path)) { + GDScriptCache::singleton->shallow_gdscript_cache[source_path] = this; + } + } + } + valid = false; GDScriptParser parser; - Error err = parser.parse(source, basedir, false, path); + Error err = parser.parse(source, path, false); + if (err) { + if (EngineDebugger::is_active()) { + GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); + } + // TODO: Show all error messages. + _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(), ERR_HANDLER_SCRIPT); + ERR_FAIL_V(ERR_PARSE_ERROR); + } + + GDScriptAnalyzer analyzer(&parser); + err = analyzer.analyze(); + if (err) { if (EngineDebugger::is_active()) { - GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_error_line(), "Parser Error: " + parser.get_error()); + GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), parser.get_errors().front()->get().line, "Parser Error: " + parser.get_errors().front()->get().message); + } + + const List<GDScriptParser::ParserError>::Element *e = parser.get_errors().front(); + while (e != nullptr) { + _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(), ERR_HANDLER_SCRIPT); + e = e->next(); } - _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_error_line(), ("Parse Error: " + parser.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT); ERR_FAIL_V(ERR_PARSE_ERROR); } - bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool_script(); + bool can_run = ScriptServer::is_scripting_enabled() || parser.is_tool(); GDScriptCompiler compiler; err = compiler.compile(&parser, this, p_keep_state); - if (err) { +#ifdef TOOLS_ENABLED + _update_doc(); +#endif + if (err) { if (can_run) { if (EngineDebugger::is_active()) { GDScriptLanguage::get_singleton()->debug_break_parse(get_path(), compiler.get_error_line(), "Parser Error: " + compiler.get_error()); } - _err_print_error("GDScript::reload", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT); + _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(), ERR_HANDLER_SCRIPT); ERR_FAIL_V(ERR_COMPILATION_FAILED); } else { return err; @@ -582,7 +857,7 @@ Error GDScript::reload(bool p_keep_state) { const GDScriptWarning &warning = E->get(); if (EngineDebugger::is_active()) { Vector<ScriptLanguage::StackInfo> si; - EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si); + EngineDebugger::get_script_debugger()->send_error("", get_path(), warning.start_line, warning.get_name(), warning.get_message(), ERR_HANDLER_WARNING, si); } } #endif @@ -590,7 +865,6 @@ Error GDScript::reload(bool p_keep_state) { valid = true; for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) { - _set_subclass_path(E->get(), path); } @@ -600,12 +874,10 @@ Error GDScript::reload(bool p_keep_state) { } ScriptLanguage *GDScript::get_language() const { - return GDScriptLanguage::get_singleton(); } void GDScript::get_constants(Map<StringName, Variant> *p_constants) { - if (p_constants) { for (Map<StringName, Variant>::Element *E = constants.front(); E; E = E->next()) { (*p_constants)[E->key()] = E->value(); @@ -635,12 +907,16 @@ uint16_t GDScript::get_rpc_method_id(const StringName &p_method) const { } StringName GDScript::get_rpc_method(const uint16_t p_rpc_method_id) const { - if (p_rpc_method_id >= rpc_functions.size()) return StringName(); + if (p_rpc_method_id >= rpc_functions.size()) { + return StringName(); + } return rpc_functions[p_rpc_method_id].name; } MultiplayerAPI::RPCMode GDScript::get_rpc_mode_by_id(const uint16_t p_rpc_method_id) const { - if (p_rpc_method_id >= rpc_functions.size()) return MultiplayerAPI::RPC_MODE_DISABLED; + if (p_rpc_method_id >= rpc_functions.size()) { + return MultiplayerAPI::RPC_MODE_DISABLED; + } return rpc_functions[p_rpc_method_id].mode; } @@ -662,12 +938,16 @@ uint16_t GDScript::get_rset_property_id(const StringName &p_variable) const { } StringName GDScript::get_rset_property(const uint16_t p_rset_member_id) const { - if (p_rset_member_id >= rpc_variables.size()) return StringName(); + if (p_rset_member_id >= rpc_variables.size()) { + return StringName(); + } return rpc_variables[p_rset_member_id].name; } MultiplayerAPI::RPCMode GDScript::get_rset_mode_by_id(const uint16_t p_rset_member_id) const { - if (p_rset_member_id >= rpc_variables.size()) return MultiplayerAPI::RPC_MODE_DISABLED; + if (p_rset_member_id >= rpc_variables.size()) { + return MultiplayerAPI::RPC_MODE_DISABLED; + } return rpc_variables[p_rset_member_id].mode; } @@ -676,13 +956,10 @@ MultiplayerAPI::RPCMode GDScript::get_rset_mode(const StringName &p_variable) co } Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { - GDScript *top = this; while (top) { - Map<StringName, GDScriptFunction *>::Element *E = top->member_functions.find(p_method); if (E) { - ERR_FAIL_COND_V_MSG(!E->get()->is_static(), Variant(), "Can't call non-static function '" + String(p_method) + "' in script."); return E->get()->call(nullptr, p_args, p_argcount, r_error); @@ -696,16 +973,12 @@ Variant GDScript::call(const StringName &p_method, const Variant **p_args, int p } bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { - { - const GDScript *top = this; while (top) { - { const Map<StringName, Variant>::Element *E = top->constants.find(p_name); if (E) { - r_ret = E->get(); return true; } @@ -714,7 +987,6 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { { const Map<StringName, Ref<GDScript>>::Element *E = subclasses.find(p_name); if (E) { - r_ret = E->get(); return true; } @@ -723,7 +995,6 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { } if (p_name == GDScriptLanguage::get_singleton()->strings._script_source) { - r_ret = get_source_code(); return true; } @@ -731,120 +1002,42 @@ bool GDScript::_get(const StringName &p_name, Variant &r_ret) const { return false; } -bool GDScript::_set(const StringName &p_name, const Variant &p_value) { +bool GDScript::_set(const StringName &p_name, const Variant &p_value) { if (p_name == GDScriptLanguage::get_singleton()->strings._script_source) { - set_source_code(p_value); reload(); - } else + } else { return false; + } return true; } void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const { - p_properties->push_back(PropertyInfo(Variant::STRING, "script/source", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL)); } void GDScript::_bind_methods() { - ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "new", &GDScript::_new, MethodInfo("new")); ClassDB::bind_method(D_METHOD("get_as_byte_code"), &GDScript::get_as_byte_code); } Vector<uint8_t> GDScript::get_as_byte_code() const { - - GDScriptTokenizerBuffer tokenizer; - return tokenizer.parse_code_string(source); + return Vector<uint8_t>(); }; +// TODO: Fully remove this. There's not this kind of "bytecode" anymore. Error GDScript::load_byte_code(const String &p_path) { - - Vector<uint8_t> bytecode; - - if (p_path.ends_with("gde")) { - - FileAccess *fa = FileAccess::open(p_path, FileAccess::READ); - ERR_FAIL_COND_V(!fa, ERR_CANT_OPEN); - - FileAccessEncrypted *fae = memnew(FileAccessEncrypted); - ERR_FAIL_COND_V(!fae, ERR_CANT_OPEN); - - Vector<uint8_t> key; - key.resize(32); - for (int i = 0; i < key.size(); i++) { - key.write[i] = script_encryption_key[i]; - } - - Error err = fae->open_and_parse(fa, key, FileAccessEncrypted::MODE_READ); - - if (err) { - fa->close(); - memdelete(fa); - memdelete(fae); - - ERR_FAIL_COND_V(err, err); - } - - bytecode.resize(fae->get_len()); - fae->get_buffer(bytecode.ptrw(), bytecode.size()); - fae->close(); - memdelete(fae); - - } else { - - bytecode = FileAccess::get_file_as_array(p_path); - } - - ERR_FAIL_COND_V(bytecode.size() == 0, ERR_PARSE_ERROR); - path = p_path; - - String basedir = path; - - if (basedir == "") - basedir = get_path(); - - if (basedir != "") - basedir = basedir.get_base_dir(); - - valid = false; - GDScriptParser parser; - Error err = parser.parse_bytecode(bytecode, basedir, get_path()); - if (err) { - _err_print_error("GDScript::load_byte_code", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), parser.get_error_line(), ("Parse Error: " + parser.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT); - ERR_FAIL_V(ERR_PARSE_ERROR); - } - - GDScriptCompiler compiler; - err = compiler.compile(&parser, this); - - if (err) { - _err_print_error("GDScript::load_byte_code", path.empty() ? "built-in" : (const char *)path.utf8().get_data(), compiler.get_error_line(), ("Compile Error: " + compiler.get_error()).utf8().get_data(), ERR_HANDLER_SCRIPT); - ERR_FAIL_V(ERR_COMPILATION_FAILED); - } - - valid = true; - - for (Map<StringName, Ref<GDScript>>::Element *E = subclasses.front(); E; E = E->next()) { - - _set_subclass_path(E->get(), path); - } - - _init_rpc_methods_properties(); - - return OK; + return ERR_COMPILATION_FAILED; } Error GDScript::load_source_code(const String &p_path) { - Vector<uint8_t> sourcef; Error err; FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err); if (err) { - ERR_FAIL_COND_V(err, err); } @@ -859,7 +1052,6 @@ Error GDScript::load_source_code(const String &p_path) { String s; if (s.parse_utf8((const char *)w)) { - 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."); } @@ -872,23 +1064,20 @@ Error GDScript::load_source_code(const String &p_path) { } const Map<StringName, GDScriptFunction *> &GDScript::debug_get_member_functions() const { - return member_functions; } StringName GDScript::debug_get_member_by_index(int p_idx) const { - for (const Map<StringName, MemberInfo>::Element *E = member_indices.front(); E; E = E->next()) { - - if (E->get().index == p_idx) + if (E->get().index == p_idx) { return E->key(); + } } return "<error>"; } Ref<GDScript> GDScript::get_base() const { - return base; } @@ -911,8 +1100,9 @@ bool GDScript::inherits_script(const Ref<Script> &p_script) const { } bool GDScript::has_script_signal(const StringName &p_signal) const { - if (_signals.has(p_signal)) + if (_signals.has(p_signal)) { return true; + } if (base.is_valid()) { return base->has_script_signal(p_signal); } @@ -923,10 +1113,9 @@ bool GDScript::has_script_signal(const StringName &p_signal) const { #endif return false; } -void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { +void GDScript::_get_script_signal_list(List<MethodInfo> *r_list, bool p_include_base) const { for (const Map<StringName, Vector<StringName>>::Element *E = _signals.front(); E; E = E->next()) { - MethodInfo mi; mi.name = E->key(); for (int i = 0; i < E->get().size(); i++) { @@ -934,23 +1123,45 @@ void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { arg.name = E->get()[i]; mi.arguments.push_back(arg); } - r_signals->push_back(mi); + r_list->push_back(mi); + } + + if (!p_include_base) { + return; } if (base.is_valid()) { - base->get_script_signal_list(r_signals); + base->get_script_signal_list(r_list); } #ifdef TOOLS_ENABLED else if (base_cache.is_valid()) { - base_cache->get_script_signal_list(r_signals); + base_cache->get_script_signal_list(r_list); } #endif } +void GDScript::get_script_signal_list(List<MethodInfo> *r_signals) const { + _get_script_signal_list(r_signals, true); +} + +String GDScript::_get_gdscript_reference_class_name(const GDScript *p_gdscript) { + ERR_FAIL_NULL_V(p_gdscript, String()); + + String class_name; + while (p_gdscript) { + if (class_name == "") { + class_name = p_gdscript->get_script_class_name(); + } else { + class_name = p_gdscript->get_script_class_name() + "." + class_name; + } + p_gdscript = p_gdscript->_owner; + } + return class_name; +} + GDScript::GDScript() : script_list(this) { - valid = false; subclass_count = 0; initializer = nullptr; @@ -995,8 +1206,9 @@ void GDScript::_save_orphaned_subclasses() { for (int i = 0; i < weak_subclasses.size(); i++) { ClassRefWithName subclass = weak_subclasses[i]; Object *obj = ObjectDB::get_instance(subclass.id); - if (!obj) + if (!obj) { continue; + } // subclass is not released GDScriptLanguage::get_singleton()->add_orphan_subclass(subclass.fully_qualified_name, subclass.id); } @@ -1037,13 +1249,15 @@ void GDScript::_init_rpc_methods_properties() { } } - if (cscript != this) + if (cscript != this) { sub_E = sub_E->next(); + } - if (sub_E) + if (sub_E) { cscript = sub_E->get().ptr(); - else + } else { cscript = nullptr; + } } // Sort so we are 100% that they are always the same. @@ -1052,12 +1266,34 @@ void GDScript::_init_rpc_methods_properties() { } GDScript::~GDScript() { + { + MutexLock lock(GDScriptLanguage::get_singleton()->lock); + + while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) { + // Order matters since clearing the stack may already cause + // the GDSCriptFunctionState to be destroyed and thus removed from the list. + pending_func_states.remove(E); + E->self()->_clear_stack(); + } + } + for (Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) { memdelete(E->get()); } + if (GDScriptCache::singleton) { // Cache may have been already destroyed at engine shutdown. + GDScriptCache::remove_script(get_path()); + } + _save_orphaned_subclasses(); +#ifdef TOOLS_ENABLED + // Clearing inner class doc, script doc only cleared when the script source deleted. + if (_owner) { + _clear_doc(); + } +#endif + #ifdef DEBUG_ENABLED { MutexLock lock(GDScriptLanguage::get_singleton()->lock); @@ -1072,7 +1308,6 @@ GDScript::~GDScript() { ////////////////////////////// bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { - //member { const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name); @@ -1090,7 +1325,8 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { // Try conversion Callable::CallError ce; const Variant *value = &p_value; - Variant converted = Variant::construct(member->data_type.builtin_type, &value, 1, ce); + Variant converted; + Variant::construct(member->data_type.builtin_type, converted, &value, 1, ce); if (ce.error == Callable::CallError::CALL_OK) { members.write[member->index] = converted; return true; @@ -1107,17 +1343,16 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { GDScript *sptr = script.ptr(); while (sptr) { - Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._set); if (E) { - Variant name = p_name; const Variant *args[2] = { &name, &p_value }; Callable::CallError err; Variant ret = E->get()->call(this, (const Variant **)args, 2, err); - if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) + if (err.error == Callable::CallError::CALL_OK && ret.get_type() == Variant::BOOL && ret.operator bool()) { return true; + } } sptr = sptr->_base; } @@ -1126,10 +1361,8 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) { } bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { - const GDScript *sptr = script.ptr(); while (sptr) { - { const Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.find(p_name); if (E) { @@ -1146,7 +1379,6 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { } { - const GDScript *sl = sptr; while (sl) { const Map<StringName, Variant>::Element *E = sl->constants.find(p_name); @@ -1159,9 +1391,34 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { } { + // Signals. + const GDScript *sl = sptr; + while (sl) { + const Map<StringName, Vector<StringName>>::Element *E = sl->_signals.find(p_name); + if (E) { + r_ret = Signal(this->owner, E->key()); + return true; //index found + } + sl = sl->_base; + } + } + + { + // Methods. + const GDScript *sl = sptr; + while (sl) { + const Map<StringName, GDScriptFunction *>::Element *E = sl->member_functions.find(p_name); + if (E) { + r_ret = Callable(this->owner, E->key()); + return true; //index found + } + sl = sl->_base; + } + } + + { const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get); if (E) { - Variant name = p_name; const Variant *args[1] = { &name }; @@ -1180,20 +1437,20 @@ bool GDScriptInstance::get(const StringName &p_name, Variant &r_ret) const { } Variant::Type GDScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const { - const GDScript *sptr = script.ptr(); while (sptr) { - if (sptr->member_info.has(p_name)) { - if (r_is_valid) + if (r_is_valid) { *r_is_valid = true; + } return sptr->member_info[p_name].type; } sptr = sptr->_base; } - if (r_is_valid) + if (r_is_valid) { *r_is_valid = false; + } return Variant::NIL; } @@ -1204,19 +1461,15 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const List<PropertyInfo> props; while (sptr) { - const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(GDScriptLanguage::get_singleton()->strings._get_property_list); if (E) { - Callable::CallError err; Variant ret = const_cast<GDScriptFunction *>(E->get())->call(const_cast<GDScriptInstance *>(this), nullptr, 0, err); if (err.error == Callable::CallError::CALL_OK) { - ERR_FAIL_COND_MSG(ret.get_type() != Variant::ARRAY, "Wrong type for _get_property_list, must be an array of dictionaries."); Array arr = ret; for (int i = 0; i < arr.size(); i++) { - Dictionary d = arr[i]; ERR_CONTINUE(!d.has("name")); ERR_CONTINUE(!d.has("type")); @@ -1225,12 +1478,15 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const ERR_CONTINUE(pinfo.type < 0 || pinfo.type >= Variant::VARIANT_MAX); pinfo.name = d["name"]; ERR_CONTINUE(pinfo.name == ""); - if (d.has("hint")) + if (d.has("hint")) { pinfo.hint = PropertyHint(d["hint"].operator int()); - if (d.has("hint_string")) + } + if (d.has("hint_string")) { pinfo.hint_string = d["hint_string"]; - if (d.has("usage")) + } + if (d.has("usage")) { pinfo.usage = d["usage"]; + } props.push_back(pinfo); } @@ -1241,7 +1497,6 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const Vector<_GDScriptMemberSort> msort; for (Map<StringName, PropertyInfo>::Element *F = sptr->member_info.front(); F; F = F->next()) { - _GDScriptMemberSort ms; ERR_CONTINUE(!sptr->member_indices.has(F->key())); ms.index = sptr->member_indices[F->key()].index; @@ -1252,7 +1507,6 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const msort.sort(); msort.invert(); for (int i = 0; i < msort.size(); i++) { - props.push_front(sptr->member_info[msort[i].name]); } @@ -1260,23 +1514,20 @@ void GDScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const } for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - p_properties->push_back(E->get()); } } void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const { - const GDScript *sptr = script.ptr(); while (sptr) { - for (Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.front(); E; E = E->next()) { - MethodInfo mi; mi.name = E->key(); mi.flags |= METHOD_FLAG_FROM_SCRIPT; - for (int i = 0; i < E->get()->get_argument_count(); i++) + for (int i = 0; i < E->get()->get_argument_count(); i++) { mi.arguments.push_back(PropertyInfo(Variant::NIL, "arg" + itos(i))); + } p_list->push_back(mi); } sptr = sptr->_base; @@ -1284,19 +1535,19 @@ void GDScriptInstance::get_method_list(List<MethodInfo> *p_list) const { } bool GDScriptInstance::has_method(const StringName &p_method) const { - const GDScript *sptr = script.ptr(); while (sptr) { const Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method); - if (E) + if (E) { return true; + } sptr = sptr->_base; } return false; } -Variant GDScriptInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { +Variant GDScriptInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { GDScript *sptr = script.ptr(); while (sptr) { Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method); @@ -1309,42 +1560,7 @@ Variant GDScriptInstance::call(const StringName &p_method, const Variant **p_arg return Variant(); } -void GDScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) { - - GDScript *sptr = script.ptr(); - Callable::CallError ce; - - while (sptr) { - Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method); - if (E) { - E->get()->call(this, p_args, p_argcount, ce); - } - sptr = sptr->_base; - } -} - -void GDScriptInstance::_ml_call_reversed(GDScript *sptr, const StringName &p_method, const Variant **p_args, int p_argcount) { - - if (sptr->_base) - _ml_call_reversed(sptr->_base, p_method, p_args, p_argcount); - - Callable::CallError ce; - - Map<StringName, GDScriptFunction *>::Element *E = sptr->member_functions.find(p_method); - if (E) { - E->get()->call(this, p_args, p_argcount, ce); - } -} - -void GDScriptInstance::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) { - - if (script.ptr()) { - _ml_call_reversed(script.ptr(), p_method, p_args, p_argcount); - } -} - void GDScriptInstance::notification(int p_notification) { - //notification is not virtual, it gets called at ALL levels just like in C. Variant value = p_notification; const Variant *args[1] = { &value }; @@ -1369,27 +1585,28 @@ String GDScriptInstance::to_string(bool *r_valid) { Variant ret = call(CoreStringNames::get_singleton()->_to_string, nullptr, 0, ce); if (ce.error == Callable::CallError::CALL_OK) { if (ret.get_type() != Variant::STRING) { - if (r_valid) + if (r_valid) { *r_valid = false; + } ERR_FAIL_V_MSG(String(), "Wrong type for " + CoreStringNames::get_singleton()->_to_string + ", must be a String."); } - if (r_valid) + if (r_valid) { *r_valid = true; + } return ret.operator String(); } } - if (r_valid) + if (r_valid) { *r_valid = false; + } return String(); } Ref<Script> GDScriptInstance::get_script() const { - return script; } ScriptLanguage *GDScriptInstance::get_language() { - return GDScriptLanguage::get_singleton(); } @@ -1434,7 +1651,6 @@ MultiplayerAPI::RPCMode GDScriptInstance::get_rset_mode(const StringName &p_vari } void GDScriptInstance::reload_members() { - #ifdef DEBUG_ENABLED members.resize(script->member_indices.size()); //resize @@ -1444,7 +1660,6 @@ void GDScriptInstance::reload_members() { //pass the values to the new indices for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) { - if (member_indices_cache.has(E->key())) { Variant value = members[member_indices_cache[E->key()]]; new_members.write[E->get().index] = value; @@ -1457,7 +1672,6 @@ void GDScriptInstance::reload_members() { //pass the values to the new indices member_indices_cache.clear(); for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) { - member_indices_cache[E->key()] = E->get().index; } @@ -1470,9 +1684,16 @@ GDScriptInstance::GDScriptInstance() { } GDScriptInstance::~GDScriptInstance() { - if (script.is_valid() && owner) { - MutexLock lock(GDScriptLanguage::singleton->lock); + MutexLock lock(GDScriptLanguage::get_singleton()->lock); + while (SelfList<GDScriptFunctionState> *E = pending_func_states.first()) { + // Order matters since clearing the stack may already cause + // the GDSCriptFunctionState to be destroyed and thus removed from the list. + pending_func_states.remove(E); + E->self()->_clear_stack(); + } + + if (script.is_valid() && owner) { script->instances.erase(owner); } } @@ -1482,14 +1703,12 @@ GDScriptInstance::~GDScriptInstance() { GDScriptLanguage *GDScriptLanguage::singleton = nullptr; String GDScriptLanguage::get_name() const { - return "GDScript"; } /* LANGUAGE FUNCTIONS */ void GDScriptLanguage::_add_global(const StringName &p_name, const Variant &p_value) { - if (globals.has(p_name)) { //overwrite existing global_array.write[globals[p_name]] = p_value; @@ -1501,7 +1720,6 @@ void GDScriptLanguage::_add_global(const StringName &p_name, const Variant &p_va } void GDScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) { - _add_global(p_variable, p_value); } @@ -1515,12 +1733,10 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) { } void GDScriptLanguage::init() { - //populate global constants - int gcc = GlobalConstants::get_global_constant_count(); + int gcc = CoreConstants::get_global_constant_count(); for (int i = 0; i < gcc; i++) { - - _add_global(StaticCString::create(GlobalConstants::get_global_constant_name(i)), GlobalConstants::get_global_constant_value(i)); + _add_global(StaticCString::create(CoreConstants::get_global_constant_name(i)), CoreConstants::get_global_constant_value(i)); } _add_global(StaticCString::create("PI"), Math_PI); @@ -1533,14 +1749,15 @@ void GDScriptLanguage::init() { List<StringName> class_list; ClassDB::get_class_list(&class_list); for (List<StringName>::Element *E = class_list.front(); E; E = E->next()) { - StringName n = E->get(); String s = String(n); - if (s.begins_with("_")) + if (s.begins_with("_")) { n = s.substr(1, s.length()); + } - if (globals.has(n)) + if (globals.has(n)) { continue; + } Ref<GDScriptNativeClass> nc = memnew(GDScriptNativeClass(E->get())); _add_global(n, nc); } @@ -1550,29 +1767,27 @@ void GDScriptLanguage::init() { List<Engine::Singleton> singletons; Engine::get_singleton()->get_singletons(&singletons); for (List<Engine::Singleton>::Element *E = singletons.front(); E; E = E->next()) { - _add_global(E->get().name, E->get().ptr); } } String GDScriptLanguage::get_type() const { - return "GDScript"; } -String GDScriptLanguage::get_extension() const { +String GDScriptLanguage::get_extension() const { return "gd"; } -Error GDScriptLanguage::execute_file(const String &p_path) { +Error GDScriptLanguage::execute_file(const String &p_path) { // ?? return OK; } + void GDScriptLanguage::finish() { } void GDScriptLanguage::profiling_start() { - #ifdef DEBUG_ENABLED MutexLock lock(this->lock); @@ -1595,7 +1810,6 @@ void GDScriptLanguage::profiling_start() { } void GDScriptLanguage::profiling_stop() { - #ifdef DEBUG_ENABLED MutexLock lock(this->lock); @@ -1604,7 +1818,6 @@ void GDScriptLanguage::profiling_stop() { } int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) { - int current = 0; #ifdef DEBUG_ENABLED @@ -1612,8 +1825,9 @@ int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, SelfList<GDScriptFunction> *elem = function_list.first(); while (elem) { - if (current >= p_info_max) + if (current >= p_info_max) { break; + } p_info_arr[current].call_count = elem->self()->profile.call_count; p_info_arr[current].self_time = elem->self()->profile.self_time; p_info_arr[current].total_time = elem->self()->profile.total_time; @@ -1627,7 +1841,6 @@ int GDScriptLanguage::profiling_get_accumulated_data(ProfilingInfo *p_info_arr, } int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) { - int current = 0; #ifdef DEBUG_ENABLED @@ -1635,8 +1848,9 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_ SelfList<GDScriptFunction> *elem = function_list.first(); while (elem) { - if (current >= p_info_max) + if (current >= p_info_max) { break; + } if (elem->self()->profile.last_frame_call_count > 0) { p_info_arr[current].call_count = elem->self()->profile.last_frame_call_count; p_info_arr[current].self_time = elem->self()->profile.last_frame_self_time; @@ -1652,12 +1866,11 @@ int GDScriptLanguage::profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_ } struct GDScriptDepSort { - //must support sorting so inheritance works properly (parent must be reloaded first) bool operator()(const Ref<GDScript> &A, const Ref<GDScript> &B) const { - - if (A == B) + if (A == B) { return false; //shouldn't happen but.. + } const GDScript *I = B->get_base().ptr(); while (I) { if (I == A.ptr()) { @@ -1673,7 +1886,6 @@ struct GDScriptDepSort { }; void GDScriptLanguage::reload_all_scripts() { - #ifdef DEBUG_ENABLED print_verbose("GDScript: Reloading all scripts"); List<Ref<GDScript>> scripts; @@ -1695,7 +1907,6 @@ void GDScriptLanguage::reload_all_scripts() { scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order for (List<Ref<GDScript>>::Element *E = scripts.front(); E; E = E->next()) { - print_verbose("GDScript: Reloading: " + E->get()->get_path()); E->get()->load_source_code(E->get()->get_path()); E->get()->reload(true); @@ -1704,7 +1915,6 @@ void GDScriptLanguage::reload_all_scripts() { } void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) { - #ifdef DEBUG_ENABLED List<Ref<GDScript>> scripts; @@ -1714,7 +1924,6 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so SelfList<GDScript> *elem = script_list.first(); while (elem) { if (elem->self()->get_path().is_resource_file()) { - scripts.push_back(Ref<GDScript>(elem->self())); //cast to gdscript to avoid being erased by accident } elem = elem->next(); @@ -1730,16 +1939,15 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so scripts.sort_custom<GDScriptDepSort>(); //update in inheritance dependency order for (List<Ref<GDScript>>::Element *E = scripts.front(); E; E = E->next()) { - bool reload = E->get() == p_script || to_reload.has(E->get()->get_base()); - if (!reload) + if (!reload) { continue; + } to_reload.insert(E->get(), Map<ObjectID, List<Pair<StringName, Variant>>>()); if (!p_soft_reload) { - //save state and remove script from instances Map<ObjectID, List<Pair<StringName, Variant>>> &map = to_reload[E->get()]; @@ -1748,7 +1956,6 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so //save instance info List<Pair<StringName, Variant>> state; if (obj->get_script_instance()) { - obj->get_script_instance()->get_property_state(state); map[obj->get_instance_id()] = state; obj->set_script(Variant()); @@ -1763,7 +1970,6 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so //save instance info if (obj->get_script_instance()) { - map.insert(obj->get_instance_id(), List<Pair<StringName, Variant>>()); List<Pair<StringName, Variant>> &state = map[obj->get_instance_id()]; obj->get_script_instance()->get_property_state(state); @@ -1783,18 +1989,17 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so } for (Map<Ref<GDScript>, Map<ObjectID, List<Pair<StringName, Variant>>>>::Element *E = to_reload.front(); E; E = E->next()) { - Ref<GDScript> scr = E->key(); scr->reload(p_soft_reload); //restore state if saved for (Map<ObjectID, List<Pair<StringName, Variant>>>::Element *F = E->get().front(); F; F = F->next()) { - List<Pair<StringName, Variant>> &saved_state = F->get(); Object *obj = ObjectDB::get_instance(F->key()); - if (!obj) + if (!obj) { continue; + } if (!p_soft_reload) { //clear it just in case (may be a pending reload state) @@ -1833,7 +2038,6 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so } void GDScriptLanguage::frame() { - calls = 0; #ifdef DEBUG_ENABLED @@ -1857,7 +2061,7 @@ void GDScriptLanguage::frame() { /* EDITOR FUNCTIONS */ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { - + // TODO: Add annotations here? static const char *_reserved_words[] = { // operators "and", @@ -1880,6 +2084,7 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { // functions "as", "assert", + "await", "breakpoint", "class", "class_name", @@ -1887,15 +2092,13 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "is", "func", "preload", - "setget", "signal", - "tool", + "super", + "trait", "yield", // var "const", "enum", - "export", - "onready", "static", "var", // control flow @@ -1909,35 +2112,29 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "return", "match", "while", - "remote", - "master", - "puppet", - "remotesync", - "mastersync", - "puppetsync", nullptr }; const char **w = _reserved_words; while (*w) { - p_words->push_back(*w); w++; } - for (int i = 0; i < GDScriptFunctions::FUNC_MAX; i++) { - p_words->push_back(GDScriptFunctions::get_func_name(GDScriptFunctions::Function(i))); + List<StringName> functions; + GDScriptUtilityFunctions::get_function_list(&functions); + + for (const List<StringName>::Element *E = functions.front(); E; E = E->next()) { + p_words->push_back(String(E->get())); } } bool GDScriptLanguage::handles_global_class_type(const String &p_type) const { - return p_type == "GDScript"; } String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_base_type, String *r_icon_path) const { - Vector<uint8_t> sourcef; Error err; FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err); @@ -1948,60 +2145,61 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b String source = f->get_as_utf8_string(); GDScriptParser parser; - parser.parse(source, p_path.get_base_dir(), true, p_path, false, nullptr, true); - - if (parser.get_parse_tree() && parser.get_parse_tree()->type == GDScriptParser::Node::TYPE_CLASS) { + err = parser.parse(source, p_path, false); - const GDScriptParser::ClassNode *c = static_cast<const GDScriptParser::ClassNode *>(parser.get_parse_tree()); + // TODO: Simplify this code by using the analyzer to get full inheritance. + if (err == OK) { + const GDScriptParser::ClassNode *c = parser.get_tree(); if (r_icon_path) { - if (c->icon_path.empty() || c->icon_path.is_abs_path()) + if (c->icon_path.is_empty() || c->icon_path.is_abs_path()) { *r_icon_path = c->icon_path; - else if (c->icon_path.is_rel_path()) + } else if (c->icon_path.is_rel_path()) { *r_icon_path = p_path.get_base_dir().plus_file(c->icon_path).simplify_path(); + } } if (r_base_type) { - const GDScriptParser::ClassNode *subclass = c; String path = p_path; GDScriptParser subparser; while (subclass) { if (subclass->extends_used) { - if (subclass->extends_file) { - if (subclass->extends_class.size() == 0) { - get_global_class_name(subclass->extends_file, r_base_type); + if (!subclass->extends_path.is_empty()) { + if (subclass->extends.size() == 0) { + get_global_class_name(subclass->extends_path, r_base_type); subclass = nullptr; break; } else { - Vector<StringName> extend_classes = subclass->extends_class; + Vector<StringName> extend_classes = subclass->extends; - FileAccessRef subfile = FileAccess::open(subclass->extends_file, FileAccess::READ); + FileAccessRef subfile = FileAccess::open(subclass->extends_path, FileAccess::READ); if (!subfile) { break; } String subsource = subfile->get_as_utf8_string(); - if (subsource.empty()) { + if (subsource.is_empty()) { break; } - String subpath = subclass->extends_file; + String subpath = subclass->extends_path; if (subpath.is_rel_path()) { subpath = path.get_base_dir().plus_file(subpath).simplify_path(); } - if (OK != subparser.parse(subsource, subpath.get_base_dir(), true, subpath, false, nullptr, true)) { + if (OK != subparser.parse(subsource, subpath, false)) { break; } path = subpath; - if (!subparser.get_parse_tree() || subparser.get_parse_tree()->type != GDScriptParser::Node::TYPE_CLASS) { - break; - } - subclass = static_cast<const GDScriptParser::ClassNode *>(subparser.get_parse_tree()); + subclass = subparser.get_tree(); while (extend_classes.size() > 0) { bool found = false; - for (int i = 0; i < subclass->subclasses.size(); i++) { - const GDScriptParser::ClassNode *inner_class = subclass->subclasses[i]; - if (inner_class->name == extend_classes[0]) { + for (int i = 0; i < subclass->members.size(); i++) { + if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) { + continue; + } + + const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class; + if (inner_class->identifier->name == extend_classes[0]) { extend_classes.remove(0); found = true; subclass = inner_class; @@ -2014,8 +2212,8 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b } } } - } else if (subclass->extends_class.size() == 1) { - *r_base_type = subclass->extends_class[0]; + } else if (subclass->extends.size() == 1) { + *r_base_type = subclass->extends[0]; subclass = nullptr; } else { break; @@ -2026,183 +2224,13 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b } } } - return c->name; + return c->identifier != nullptr ? String(c->identifier->name) : String(); } return String(); } -#ifdef DEBUG_ENABLED -String GDScriptWarning::get_message() const { - -#define CHECK_SYMBOLS(m_amount) ERR_FAIL_COND_V(symbols.size() < m_amount, String()); - - switch (code) { - case UNASSIGNED_VARIABLE_OP_ASSIGN: { - CHECK_SYMBOLS(1); - return "Using assignment with operation but the variable '" + symbols[0] + "' was not previously assigned a value."; - } break; - case UNASSIGNED_VARIABLE: { - CHECK_SYMBOLS(1); - return "The variable '" + symbols[0] + "' was used but never assigned a value."; - } break; - case UNUSED_VARIABLE: { - CHECK_SYMBOLS(1); - return "The local variable '" + symbols[0] + "' is declared but never used in the block. If this is intended, prefix it with an underscore: '_" + symbols[0] + "'"; - } break; - case SHADOWED_VARIABLE: { - CHECK_SYMBOLS(2); - return "The local variable '" + symbols[0] + "' is shadowing an already-defined variable at line " + symbols[1] + "."; - } break; - case UNUSED_CLASS_VARIABLE: { - CHECK_SYMBOLS(1); - return "The class variable '" + symbols[0] + "' is declared but never used in the script."; - } break; - case UNUSED_ARGUMENT: { - CHECK_SYMBOLS(2); - return "The argument '" + symbols[1] + "' is never used in the function '" + symbols[0] + "'. If this is intended, prefix it with an underscore: '_" + symbols[1] + "'"; - } break; - case UNREACHABLE_CODE: { - CHECK_SYMBOLS(1); - return "Unreachable code (statement after return) in function '" + symbols[0] + "()'."; - } break; - case STANDALONE_EXPRESSION: { - return "Standalone expression (the line has no effect)."; - } break; - case VOID_ASSIGNMENT: { - CHECK_SYMBOLS(1); - return "Assignment operation, but the function '" + symbols[0] + "()' returns void."; - } break; - case NARROWING_CONVERSION: { - return "Narrowing conversion (float is converted to int and loses precision)."; - } break; - case FUNCTION_MAY_YIELD: { - CHECK_SYMBOLS(1); - return "Assigned variable is typed but the function '" + symbols[0] + "()' may yield and return a GDScriptFunctionState instead."; - } break; - case VARIABLE_CONFLICTS_FUNCTION: { - CHECK_SYMBOLS(1); - return "Variable declaration of '" + symbols[0] + "' conflicts with a function of the same name."; - } break; - case FUNCTION_CONFLICTS_VARIABLE: { - CHECK_SYMBOLS(1); - return "Function declaration of '" + symbols[0] + "()' conflicts with a variable of the same name."; - } break; - case FUNCTION_CONFLICTS_CONSTANT: { - CHECK_SYMBOLS(1); - return "Function declaration of '" + symbols[0] + "()' conflicts with a constant of the same name."; - } break; - case INCOMPATIBLE_TERNARY: { - return "Values of the ternary conditional are not mutually compatible."; - } break; - case UNUSED_SIGNAL: { - CHECK_SYMBOLS(1); - return "The signal '" + symbols[0] + "' is declared but never emitted."; - } break; - case RETURN_VALUE_DISCARDED: { - CHECK_SYMBOLS(1); - return "The function '" + symbols[0] + "()' returns a value, but this value is never used."; - } break; - case PROPERTY_USED_AS_FUNCTION: { - CHECK_SYMBOLS(2); - return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a property with the same name. Did you mean to access it?"; - } break; - case CONSTANT_USED_AS_FUNCTION: { - CHECK_SYMBOLS(2); - return "The method '" + symbols[0] + "()' was not found in base '" + symbols[1] + "' but there's a constant with the same name. Did you mean to access it?"; - } break; - case FUNCTION_USED_AS_PROPERTY: { - CHECK_SYMBOLS(2); - return "The property '" + symbols[0] + "' was not found in base '" + symbols[1] + "' but there's a method with the same name. Did you mean to call it?"; - } break; - case INTEGER_DIVISION: { - return "Integer division, decimal part will be discarded."; - } break; - case UNSAFE_PROPERTY_ACCESS: { - CHECK_SYMBOLS(2); - return "The property '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; - } break; - case UNSAFE_METHOD_ACCESS: { - CHECK_SYMBOLS(2); - return "The method '" + symbols[0] + "' is not present on the inferred type '" + symbols[1] + "' (but may be present on a subtype)."; - } break; - case UNSAFE_CAST: { - CHECK_SYMBOLS(1); - return "The value is cast to '" + symbols[0] + "' but has an unknown type."; - } break; - case UNSAFE_CALL_ARGUMENT: { - CHECK_SYMBOLS(4); - return "The argument '" + symbols[0] + "' of the function '" + symbols[1] + "' requires a the subtype '" + symbols[2] + "' but the supertype '" + symbols[3] + "' was provided"; - } break; - case DEPRECATED_KEYWORD: { - CHECK_SYMBOLS(2); - return "The '" + symbols[0] + "' keyword is deprecated and will be removed in a future release, please replace its uses by '" + symbols[1] + "'."; - } break; - case STANDALONE_TERNARY: { - return "Standalone ternary conditional operator: the return value is being discarded."; - } - case WARNING_MAX: break; // Can't happen, but silences warning - } - ERR_FAIL_V_MSG(String(), "Invalid GDScript warning code: " + get_name_from_code(code) + "."); - -#undef CHECK_SYMBOLS -} - -String GDScriptWarning::get_name() const { - return get_name_from_code(code); -} - -String GDScriptWarning::get_name_from_code(Code p_code) { - ERR_FAIL_COND_V(p_code < 0 || p_code >= WARNING_MAX, String()); - - static const char *names[] = { - "UNASSIGNED_VARIABLE", - "UNASSIGNED_VARIABLE_OP_ASSIGN", - "UNUSED_VARIABLE", - "SHADOWED_VARIABLE", - "UNUSED_CLASS_VARIABLE", - "UNUSED_ARGUMENT", - "UNREACHABLE_CODE", - "STANDALONE_EXPRESSION", - "VOID_ASSIGNMENT", - "NARROWING_CONVERSION", - "FUNCTION_MAY_YIELD", - "VARIABLE_CONFLICTS_FUNCTION", - "FUNCTION_CONFLICTS_VARIABLE", - "FUNCTION_CONFLICTS_CONSTANT", - "INCOMPATIBLE_TERNARY", - "UNUSED_SIGNAL", - "RETURN_VALUE_DISCARDED", - "PROPERTY_USED_AS_FUNCTION", - "CONSTANT_USED_AS_FUNCTION", - "FUNCTION_USED_AS_PROPERTY", - "INTEGER_DIVISION", - "UNSAFE_PROPERTY_ACCESS", - "UNSAFE_METHOD_ACCESS", - "UNSAFE_CAST", - "UNSAFE_CALL_ARGUMENT", - "DEPRECATED_KEYWORD", - "STANDALONE_TERNARY", - nullptr - }; - - return names[(int)p_code]; -} - -GDScriptWarning::Code GDScriptWarning::get_code_from_name(const String &p_name) { - for (int i = 0; i < WARNING_MAX; i++) { - if (get_name_from_code((Code)i) == p_name) { - return (Code)i; - } - } - - ERR_FAIL_V_MSG(WARNING_MAX, "Invalid GDScript warning name: " + p_name); -} - -#endif // DEBUG_ENABLED - GDScriptLanguage::GDScriptLanguage() { - calls = 0; ERR_FAIL_COND(singleton); singleton = this; @@ -2240,18 +2268,43 @@ GDScriptLanguage::GDScriptLanguage() { GLOBAL_DEF("debug/gdscript/completion/autocomplete_setters_and_getters", false); for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) { String warning = GDScriptWarning::get_name_from_code((GDScriptWarning::Code)i).to_lower(); - bool default_enabled = !warning.begins_with("unsafe_") && i != GDScriptWarning::UNUSED_CLASS_VARIABLE; + bool default_enabled = !warning.begins_with("unsafe_"); GLOBAL_DEF("debug/gdscript/warnings/" + warning, default_enabled); } #endif // DEBUG_ENABLED } GDScriptLanguage::~GDScriptLanguage() { - if (_call_stack) { memdelete_arr(_call_stack); } - singleton = nullptr; + + // Clear dependencies between scripts, to ensure cyclic references are broken (to avoid leaks at exit). + SelfList<GDScript> *s = script_list.first(); + while (s) { + GDScript *script = s->self(); + // This ensures the current script is not released before we can check what's the next one + // in the list (we can't get the next upfront because we don't know if the reference breaking + // will cause it -or any other after it, for that matter- to be released so the next one + // is not the same as before). + script->reference(); + + for (Map<StringName, GDScriptFunction *>::Element *E = script->member_functions.front(); E; E = E->next()) { + GDScriptFunction *func = E->get(); + for (int i = 0; i < func->argument_types.size(); i++) { + func->argument_types.write[i].script_type_ref = Ref<Script>(); + } + func->return_type.script_type_ref = Ref<Script>(); + } + for (Map<StringName, GDScript::MemberInfo>::Element *E = script->member_indices.front(); E; E = E->next()) { + E->get().data_type.script_type_ref = Ref<Script>(); + } + + s = s->next(); + script->unreference(); + } + + singleton = NULL; } void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass) { @@ -2260,81 +2313,73 @@ void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const Ref<GDScript> GDScriptLanguage::get_orphan_subclass(const String &p_qualified_name) { Map<String, ObjectID>::Element *orphan_subclass_element = orphan_subclasses.find(p_qualified_name); - if (!orphan_subclass_element) + if (!orphan_subclass_element) { return Ref<GDScript>(); + } ObjectID orphan_subclass = orphan_subclass_element->get(); Object *obj = ObjectDB::get_instance(orphan_subclass); orphan_subclasses.erase(orphan_subclass_element); - if (!obj) + if (!obj) { return Ref<GDScript>(); + } return Ref<GDScript>(Object::cast_to<GDScript>(obj)); } /*************** RESOURCE ***************/ RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, bool p_no_cache) { - - if (r_error) + if (r_error) { *r_error = ERR_FILE_CANT_OPEN; + } - GDScript *script = memnew(GDScript); - - Ref<GDScript> scriptres(script); - - if (p_path.ends_with(".gde") || p_path.ends_with(".gdc")) { - - script->set_script_path(p_original_path); // script needs this. - script->set_path(p_original_path); - Error err = script->load_byte_code(p_path); - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load byte code from file '" + p_path + "'."); - - } else { - Error err = script->load_source_code(p_path); - ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load source code from file '" + p_path + "'."); + Error err; + Ref<GDScript> script = GDScriptCache::get_full_script(p_path, err); - script->set_script_path(p_original_path); // script needs this. - script->set_path(p_original_path); + // TODO: Reintroduce binary and encrypted scripts. - script->reload(); + if (script.is_null()) { + // Don't fail loading because of parsing error. + script.instance(); } - if (r_error) + + if (r_error) { *r_error = OK; + } - return scriptres; + return script; } void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const { - p_extensions->push_back("gd"); - p_extensions->push_back("gdc"); - p_extensions->push_back("gde"); + // TODO: Reintroduce binary and encrypted scripts. + // p_extensions->push_back("gdc"); + // p_extensions->push_back("gde"); } bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const { - return (p_type == "Script" || p_type == "GDScript"); } String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const { - String el = p_path.get_extension().to_lower(); - if (el == "gd" || el == "gdc" || el == "gde") + // TODO: Reintroduce binary and encrypted scripts. + if (el == "gd" /*|| el == "gdc" || el == "gde"*/) { return "GDScript"; + } return ""; } void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - FileAccessRef file = FileAccess::open(p_path, FileAccess::READ); ERR_FAIL_COND_MSG(!file, "Cannot open file '" + p_path + "'."); String source = file->get_as_utf8_string(); - if (source.empty()) { + if (source.is_empty()) { return; } GDScriptParser parser; - if (OK != parser.parse(source, p_path.get_base_dir(), true, p_path, false, nullptr, true)) { + if (OK != parser.parse(source, p_path, false)) { return; } @@ -2344,7 +2389,6 @@ void ResourceFormatLoaderGDScript::get_dependencies(const String &p_path, List<S } Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { - Ref<GDScript> sqscr = p_resource; ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER); @@ -2371,12 +2415,11 @@ Error ResourceFormatSaverGDScript::save(const String &p_path, const RES &p_resou } void ResourceFormatSaverGDScript::get_recognized_extensions(const RES &p_resource, List<String> *p_extensions) const { - if (Object::cast_to<GDScript>(*p_resource)) { p_extensions->push_back("gd"); } } -bool ResourceFormatSaverGDScript::recognize(const RES &p_resource) const { +bool ResourceFormatSaverGDScript::recognize(const RES &p_resource) const { return Object::cast_to<GDScript>(*p_resource) != nullptr; } |