summaryrefslogtreecommitdiff
path: root/modules/gdscript/gdscript.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/gdscript/gdscript.cpp')
-rw-r--r--modules/gdscript/gdscript.cpp326
1 files changed, 162 insertions, 164 deletions
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 2b0b8a9065..fe79f37454 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -98,7 +98,7 @@ Variant GDScriptNativeClass::callp(const StringName &p_method, const Variant **p
return Object::callp(p_method, p_args, p_argcount, r_error);
}
MethodBind *method = ClassDB::get_method(name, p_method);
- if (method) {
+ if (method && method->is_static()) {
// Native static method.
return method->call(nullptr, p_args, p_argcount, r_error);
}
@@ -248,6 +248,10 @@ Ref<Script> GDScript::get_base_script() const {
}
}
+StringName GDScript::get_global_name() const {
+ return name;
+}
+
StringName GDScript::get_instance_base_type() const {
if (native.is_valid()) {
return native->get_name();
@@ -702,11 +706,7 @@ bool GDScript::_update_exports(bool *r_err, bool p_recursive_call, PlaceHolderSc
}
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;
- GDScriptCompiler::convert_to_initializer_type(default_value, member.variable);
- }
+ Variant default_value = analyzer.make_variable_default_value(member.variable);
member_default_values_cache[member.variable->identifier->name] = default_value;
} break;
case GDScriptParser::ClassNode::Member::SIGNAL: {
@@ -1007,17 +1007,6 @@ void GDScript::_get_property_list(List<PropertyInfo> *p_properties) const {
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 {
- 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) {
- return ERR_COMPILATION_FAILED;
}
void GDScript::set_path(const String &p_path, bool p_take_over) {
@@ -1037,7 +1026,7 @@ String GDScript::get_script_path() const {
}
Error GDScript::load_source_code(const String &p_path) {
- if (p_path.is_empty() || ResourceLoader::get_resource_type(p_path.get_slice("::", 0)) == "PackedScene") {
+ if (p_path.is_empty() || p_path.begins_with("gdscript://") || ResourceLoader::get_resource_type(p_path.get_slice("::", 0)) == "PackedScene") {
return OK;
}
@@ -1307,7 +1296,7 @@ GDScript *GDScript::_get_gdscript_from_variant(const Variant &p_variant) {
}
void GDScript::_get_dependencies(RBSet<GDScript *> &p_dependencies, const GDScript *p_except) {
- if (skip_dependencies || p_dependencies.has(this)) {
+ if (p_dependencies.has(this)) {
return;
}
p_dependencies.insert(this);
@@ -1363,9 +1352,11 @@ GDScript::GDScript() :
GDScriptLanguage::get_singleton()->script_list.add(&script_list);
}
+
+ path = vformat("gdscript://%d.gd", get_instance_id());
}
-void GDScript::_save_orphaned_subclasses() {
+void GDScript::_save_orphaned_subclasses(GDScript::ClearData *p_clear_data) {
struct ClassRefWithName {
ObjectID id;
String fully_qualified_name;
@@ -1381,8 +1372,17 @@ void GDScript::_save_orphaned_subclasses() {
}
// clear subclasses to allow unused subclasses to be deleted
+ for (KeyValue<StringName, Ref<GDScript>> &E : subclasses) {
+ p_clear_data->scripts.insert(E.value);
+ }
subclasses.clear();
// subclasses are also held by constants, clear those as well
+ for (KeyValue<StringName, Variant> &E : constants) {
+ GDScript *gdscr = _get_gdscript_from_variant(E.value);
+ if (gdscr != nullptr) {
+ p_clear_data->scripts.insert(gdscr);
+ }
+ }
constants.clear();
// keep orphan subclass only for subclasses that are still in use
@@ -1413,60 +1413,50 @@ void GDScript::_init_rpc_methods_properties() {
}
}
-void GDScript::clear() {
+void GDScript::clear(GDScript::ClearData *p_clear_data) {
if (clearing) {
return;
}
clearing = true;
- RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
- HashMap<GDScript *, ObjectID> must_clear_dependencies_objectids;
+ GDScript::ClearData data;
+ GDScript::ClearData *clear_data = p_clear_data;
+ bool is_root = false;
- // Log the objectids before clearing, as a cascade of clear could
- // remove instances that are still in the clear loop
- for (GDScript *E : must_clear_dependencies) {
- must_clear_dependencies_objectids.insert(E, E->get_instance_id());
+ // If `clear_data` is `nullptr`, it means that it's the root.
+ // The root is in charge to clear functions and scripts of itself and its dependencies
+ if (clear_data == nullptr) {
+ clear_data = &data;
+ is_root = true;
}
+ RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies();
for (GDScript *E : must_clear_dependencies) {
- Object *obj = ObjectDB::get_instance(must_clear_dependencies_objectids[E]);
- if (obj == nullptr) {
- continue;
- }
-
- E->skip_dependencies = true;
- E->clear();
- E->skip_dependencies = false;
- GDScriptCache::remove_script(E->get_path());
+ clear_data->scripts.insert(E);
+ E->clear(clear_data);
}
- RBSet<StringName> member_function_names;
for (const KeyValue<StringName, GDScriptFunction *> &E : member_functions) {
- member_function_names.insert(E.key);
+ clear_data->functions.insert(E.value);
}
- for (const StringName &E : member_function_names) {
- if (member_functions.has(E)) {
- memdelete(member_functions[E]);
- }
- }
- member_function_names.clear();
member_functions.clear();
for (KeyValue<StringName, GDScript::MemberInfo> &E : member_indices) {
+ clear_data->scripts.insert(E.value.data_type.script_type_ref);
E.value.data_type.script_type_ref = Ref<Script>();
}
if (implicit_initializer) {
- memdelete(implicit_initializer);
+ clear_data->functions.insert(implicit_initializer);
+ implicit_initializer = nullptr;
}
- implicit_initializer = nullptr;
if (implicit_ready) {
- memdelete(implicit_ready);
+ clear_data->functions.insert(implicit_ready);
+ implicit_ready = nullptr;
}
- implicit_ready = nullptr;
- _save_orphaned_subclasses();
+ _save_orphaned_subclasses(clear_data);
#ifdef TOOLS_ENABLED
// Clearing inner class doc, script doc only cleared when the script source deleted.
@@ -1474,7 +1464,21 @@ void GDScript::clear() {
_clear_doc();
}
#endif
- clearing = false;
+
+ // If it's not the root, skip clearing the data
+ if (is_root) {
+ // All dependencies have been accounted for
+ for (GDScriptFunction *E : clear_data->functions) {
+ memdelete(E);
+ }
+ for (Ref<Script> &E : clear_data->scripts) {
+ Ref<GDScript> gdscr = E;
+ if (gdscr.is_valid()) {
+ GDScriptCache::remove_script(gdscr->get_path());
+ }
+ }
+ clear_data->clear();
+ }
}
GDScript::~GDScript() {
@@ -1492,7 +1496,12 @@ GDScript::~GDScript() {
// 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();
+ GDScriptFunctionState *state = E->self();
+ ObjectID state_id = state->get_instance_id();
+ state->_clear_connections();
+ if (ObjectDB::get_instance(state_id)) {
+ state->_clear_stack();
+ }
}
}
@@ -1517,41 +1526,24 @@ bool GDScriptInstance::set(const StringName &p_name, const Variant &p_value) {
HashMap<StringName, GDScript::MemberInfo>::Iterator E = script->member_indices.find(p_name);
if (E) {
const GDScript::MemberInfo *member = &E->value;
- if (member->setter) {
- const Variant *val = &p_value;
+ Variant value = p_value;
+ if (member->data_type.has_type && !member->data_type.is_type(value)) {
+ const Variant *args = &p_value;
Callable::CallError err;
- callp(member->setter, &val, 1, err);
- if (err.error == Callable::CallError::CALL_OK) {
- return true; //function exists, call was successful
- } else {
+ Variant::construct(member->data_type.builtin_type, value, &args, 1, err);
+ if (err.error != Callable::CallError::CALL_OK || !member->data_type.is_type(value)) {
return false;
}
+ }
+ if (member->setter) {
+ const Variant *args = &value;
+ Callable::CallError err;
+ callp(member->setter, &args, 1, err);
+ return err.error == Callable::CallError::CALL_OK;
} else {
- if (member->data_type.has_type) {
- if (member->data_type.builtin_type == Variant::ARRAY && member->data_type.has_container_element_type()) {
- // Typed array.
- if (p_value.get_type() == Variant::ARRAY) {
- return VariantInternal::get_array(&members.write[member->index])->typed_assign(p_value);
- } else {
- return false;
- }
- } else if (!member->data_type.is_type(p_value)) {
- // Try conversion
- Callable::CallError ce;
- const Variant *value = &p_value;
- 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;
- } else {
- return false;
- }
- }
- }
- members.write[member->index] = p_value;
+ members.write[member->index] = value;
+ return true;
}
- return true;
}
}
@@ -1933,7 +1925,12 @@ GDScriptInstance::~GDScriptInstance() {
// 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();
+ GDScriptFunctionState *state = E->self();
+ ObjectID state_id = state->get_instance_id();
+ state->_clear_connections();
+ if (ObjectDB::get_instance(state_id)) {
+ state->_clear_stack();
+ }
}
if (script.is_valid() && owner) {
@@ -2440,7 +2437,6 @@ bool GDScriptLanguage::handles_global_class_type(const String &p_type) const {
}
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;
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
if (err) {
@@ -2452,87 +2448,100 @@ String GDScriptLanguage::get_global_class_name(const String &p_path, String *r_b
GDScriptParser parser;
err = parser.parse(source, p_path, false);
- // 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.is_empty() || c->icon_path.is_absolute_path()) {
- *r_icon_path = c->icon_path;
- } else if (c->icon_path.is_relative_path()) {
- *r_icon_path = p_path.get_base_dir().path_join(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_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;
+ const GDScriptParser::ClassNode *c = parser.get_tree();
+ if (!c) {
+ return String(); // No class parsed.
+ }
+
+ /* **WARNING**
+ *
+ * This function is written with the goal to be *extremely* error tolerant, as such
+ * it should meet the following requirements:
+ *
+ * - It must not rely on the analyzer (in fact, the analyzer must not be used here),
+ * because at the time global classes are parsed, the dependencies may not be present
+ * yet, hence the function will fail (which is unintended).
+ * - It must not fail even if the parsing fails, because even if the file is broken,
+ * it should attempt its best to retrieve the inheritance metadata.
+ *
+ * Before changing this function, please ask the current maintainer of EditorFileSystem.
+ */
+
+ if (r_icon_path) {
+ if (c->icon_path.is_empty() || c->icon_path.is_absolute_path()) {
+ *r_icon_path = c->icon_path.simplify_path();
+ } else if (c->icon_path.is_relative_path()) {
+ *r_icon_path = p_path.get_base_dir().path_join(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_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;
- Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ);
- if (subfile.is_null()) {
- break;
- }
- String subsource = subfile->get_as_utf8_string();
+ Ref<FileAccess> subfile = FileAccess::open(subclass->extends_path, FileAccess::READ);
+ if (subfile.is_null()) {
+ break;
+ }
+ String subsource = subfile->get_as_utf8_string();
- if (subsource.is_empty()) {
- break;
- }
- String subpath = subclass->extends_path;
- if (subpath.is_relative_path()) {
- subpath = path.get_base_dir().path_join(subpath).simplify_path();
- }
+ if (subsource.is_empty()) {
+ break;
+ }
+ String subpath = subclass->extends_path;
+ if (subpath.is_relative_path()) {
+ subpath = path.get_base_dir().path_join(subpath).simplify_path();
+ }
- if (OK != subparser.parse(subsource, subpath, false)) {
- break;
- }
- path = subpath;
- subclass = subparser.get_tree();
-
- while (extend_classes.size() > 0) {
- bool found = false;
- 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_at(0);
- found = true;
- subclass = inner_class;
- break;
- }
+ if (OK != subparser.parse(subsource, subpath, false)) {
+ break;
+ }
+ path = subpath;
+ subclass = subparser.get_tree();
+
+ while (extend_classes.size() > 0) {
+ bool found = false;
+ for (int i = 0; i < subclass->members.size(); i++) {
+ if (subclass->members[i].type != GDScriptParser::ClassNode::Member::CLASS) {
+ continue;
}
- if (!found) {
- subclass = nullptr;
+
+ const GDScriptParser::ClassNode *inner_class = subclass->members[i].m_class;
+ if (inner_class->identifier->name == extend_classes[0]) {
+ extend_classes.remove_at(0);
+ found = true;
+ subclass = inner_class;
break;
}
}
+ if (!found) {
+ subclass = nullptr;
+ break;
+ }
}
- } else if (subclass->extends.size() == 1) {
- *r_base_type = subclass->extends[0];
- subclass = nullptr;
- } else {
- break;
}
- } else {
- *r_base_type = "RefCounted";
+ } else if (subclass->extends.size() == 1) {
+ *r_base_type = subclass->extends[0];
subclass = nullptr;
+ } else {
+ break;
}
+ } else {
+ *r_base_type = "RefCounted";
+ subclass = nullptr;
}
}
- return c->identifier != nullptr ? String(c->identifier->name) : String();
}
-
- return String();
+ return c->identifier != nullptr ? String(c->identifier->name) : String();
}
GDScriptLanguage::GDScriptLanguage() {
@@ -2554,8 +2563,7 @@ GDScriptLanguage::GDScriptLanguage() {
script_frame_time = 0;
_debug_call_stack_pos = 0;
- int dmcs = GLOBAL_DEF("debug/settings/gdscript/max_call_stack", 1024);
- ProjectSettings::get_singleton()->set_custom_property_info("debug/settings/gdscript/max_call_stack", PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater")); //minimum is 1024
+ int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "1024,4096,1,or_greater"), 1024);
if (EngineDebugger::is_active()) {
//debugging enabled!
@@ -2570,16 +2578,12 @@ GDScriptLanguage::GDScriptLanguage() {
#ifdef DEBUG_ENABLED
GLOBAL_DEF("debug/gdscript/warnings/enable", true);
- GLOBAL_DEF("debug/gdscript/warnings/treat_warnings_as_errors", false);
GLOBAL_DEF("debug/gdscript/warnings/exclude_addons", true);
for (int i = 0; i < (int)GDScriptWarning::WARNING_MAX; i++) {
GDScriptWarning::Code code = (GDScriptWarning::Code)i;
Variant default_enabled = GDScriptWarning::get_default_value(code);
String path = GDScriptWarning::get_settings_path_from_code(code);
- GLOBAL_DEF(path, default_enabled);
-
- PropertyInfo property_info = GDScriptWarning::get_property_info(code);
- ProjectSettings::get_singleton()->set_custom_property_info(path, property_info);
+ GLOBAL_DEF(GDScriptWarning::get_property_info(code), default_enabled);
}
#endif // DEBUG_ENABLED
}
@@ -2636,8 +2640,6 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str
Error err;
Ref<GDScript> scr = GDScriptCache::get_full_script(p_path, err, "", p_cache_mode == CACHE_MODE_IGNORE);
- // TODO: Reintroduce binary and encrypted scripts.
-
if (scr.is_null()) {
// Don't fail loading because of parsing error.
scr.instantiate();
@@ -2652,9 +2654,6 @@ Ref<Resource> ResourceFormatLoaderGDScript::load(const String &p_path, const Str
void ResourceFormatLoaderGDScript::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("gd");
- // 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 {
@@ -2663,8 +2662,7 @@ bool ResourceFormatLoaderGDScript::handles_type(const String &p_type) const {
String ResourceFormatLoaderGDScript::get_resource_type(const String &p_path) const {
String el = p_path.get_extension().to_lower();
- // TODO: Reintroduce binary and encrypted scripts.
- if (el == "gd" /*|| el == "gdc" || el == "gde"*/) {
+ if (el == "gd") {
return "GDScript";
}
return "";