mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-31 13:41:03 +00:00 
			
		
		
		
	Merge pull request #35102 from ChibiDenDen/reuse_orphaned_subclass
#34161: Keep a weak reference to orphan subclasses to reuse on class reload
This commit is contained in:
		
						commit
						cd7b51b943
					
				
					 3 changed files with 63 additions and 5 deletions
				
			
		|  | @ -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) { | ||||
|  |  | |||
|  | @ -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(); | ||||
| }; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Rémi Verschelde
						Rémi Verschelde