Преглед на файлове

Keep a weak reference to orphan subclasses to reuse on class reload

ChibiDenDen преди 5 години
родител
ревизия
86aa12e806
променени са 3 файла, в които са добавени 63 реда и са изтрити 5 реда
  1. 48 3
      modules/gdscript/gdscript.cpp
  2. 7 0
      modules/gdscript/gdscript.h
  3. 8 2
      modules/gdscript/gdscript_compiler.cpp

+ 48 - 3
modules/gdscript/gdscript.cpp

@@ -915,14 +915,43 @@ GDScript::GDScript() :
 #endif
 }
 
+void GDScript::_save_orphaned_subclasses() {
+	struct ClassRefWithName {
+		ObjectID id;
+		String fully_qualified_name;
+	};
+	Vector<ClassRefWithName> weak_subclasses;
+	// collect subclasses ObjectID and name
+	for (Map<StringName, Ref<GDScript> >::Element *E = subclasses.front(); E; E = E->next()) {
+		E->get()->_owner = NULL; //bye, you are no longer owned cause I died
+		ClassRefWithName subclass;
+		subclass.id = E->get()->get_instance_id();
+		subclass.fully_qualified_name = E->get()->fully_qualified_name;
+		weak_subclasses.push_back(subclass);
+	}
+
+	// clear subclasses to allow unused subclasses to be deleted
+	subclasses.clear();
+	// subclasses are also held by constants, clear those as well
+	constants.clear();
+
+	// keep orphan subclass only for subclasses that are still in use
+	for (int i = 0; i < weak_subclasses.size(); i++) {
+		ClassRefWithName subclass = weak_subclasses[i];
+		Object *obj = ObjectDB::get_instance(subclass.id);
+		if (!obj)
+			continue;
+		// subclass is not released
+		GDScriptLanguage::get_singleton()->add_orphan_subclass(subclass.fully_qualified_name, subclass.id);
+	}
+}
+
 GDScript::~GDScript() {
 	for (Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
 		memdelete(E->get());
 	}
 
-	for (Map<StringName, Ref<GDScript> >::Element *E = subclasses.front(); E; E = E->next()) {
-		E->get()->_owner = NULL; //bye, you are no longer owned cause I died
-	}
+	_save_orphaned_subclasses();
 
 #ifdef DEBUG_ENABLED
 	if (GDScriptLanguage::get_singleton()->lock) {
@@ -2176,6 +2205,22 @@ GDScriptLanguage::~GDScriptLanguage() {
 	singleton = NULL;
 }
 
+void GDScriptLanguage::add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass) {
+	orphan_subclasses[p_qualified_name] = p_subclass;
+}
+
+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)
+		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)
+		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) {

+ 7 - 0
modules/gdscript/gdscript.h

@@ -132,6 +132,8 @@ class GDScript : public Script {
 
 	bool _update_exports();
 
+	void _save_orphaned_subclasses();
+
 protected:
 	bool _get(const StringName &p_name, Variant &r_ret) const;
 	bool _set(const StringName &p_name, const Variant &p_value);
@@ -355,6 +357,8 @@ class GDScriptLanguage : public ScriptLanguage {
 	bool profiling;
 	uint64_t script_frame_time;
 
+	Map<String, ObjectID> orphan_subclasses;
+
 public:
 	int calls;
 
@@ -506,6 +510,9 @@ public:
 	virtual bool handles_global_class_type(const String &p_type) const;
 	virtual String get_global_class_name(const String &p_path, String *r_base_type = NULL, String *r_icon_path = NULL) const;
 
+	void add_orphan_subclass(const String &p_qualified_name, const ObjectID &p_subclass);
+	Ref<GDScript> get_orphan_subclass(const String &p_qualified_name);
+
 	GDScriptLanguage();
 	~GDScriptLanguage();
 };

+ 8 - 2
modules/gdscript/gdscript_compiler.cpp

@@ -2123,15 +2123,21 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
 		StringName name = p_class->subclasses[i]->name;
 
 		Ref<GDScript> subclass;
+		String fully_qualified_name = p_script->fully_qualified_name + "::" + name;
 
 		if (old_subclasses.has(name)) {
 			subclass = old_subclasses[name];
 		} else {
-			subclass.instance();
+			Ref<GDScript> orphan_subclass = GDScriptLanguage::get_singleton()->get_orphan_subclass(fully_qualified_name);
+			if (orphan_subclass.is_valid()) {
+				subclass = orphan_subclass;
+			} else {
+				subclass.instance();
+			}
 		}
 
 		subclass->_owner = p_script;
-		subclass->fully_qualified_name = p_script->fully_qualified_name + "::" + name;
+		subclass->fully_qualified_name = fully_qualified_name;
 		p_script->subclasses.insert(name, subclass);
 
 		_make_scripts(subclass.ptr(), p_class->subclasses[i], false);