mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-04 07:31:16 +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
 | 
					#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() {
 | 
					GDScript::~GDScript() {
 | 
				
			||||||
	for (Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
 | 
						for (Map<StringName, GDScriptFunction *>::Element *E = member_functions.front(); E; E = E->next()) {
 | 
				
			||||||
		memdelete(E->get());
 | 
							memdelete(E->get());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (Map<StringName, Ref<GDScript> >::Element *E = subclasses.front(); E; E = E->next()) {
 | 
						_save_orphaned_subclasses();
 | 
				
			||||||
		E->get()->_owner = NULL; //bye, you are no longer owned cause I died
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef DEBUG_ENABLED
 | 
					#ifdef DEBUG_ENABLED
 | 
				
			||||||
	if (GDScriptLanguage::get_singleton()->lock) {
 | 
						if (GDScriptLanguage::get_singleton()->lock) {
 | 
				
			||||||
| 
						 | 
					@ -2176,6 +2205,22 @@ GDScriptLanguage::~GDScriptLanguage() {
 | 
				
			||||||
	singleton = NULL;
 | 
						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 ***************/
 | 
					/*************** RESOURCE ***************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RES ResourceFormatLoaderGDScript::load(const String &p_path, const String &p_original_path, Error *r_error) {
 | 
					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();
 | 
						bool _update_exports();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void _save_orphaned_subclasses();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
	bool _get(const StringName &p_name, Variant &r_ret) const;
 | 
						bool _get(const StringName &p_name, Variant &r_ret) const;
 | 
				
			||||||
	bool _set(const StringName &p_name, const Variant &p_value);
 | 
						bool _set(const StringName &p_name, const Variant &p_value);
 | 
				
			||||||
| 
						 | 
					@ -355,6 +357,8 @@ class GDScriptLanguage : public ScriptLanguage {
 | 
				
			||||||
	bool profiling;
 | 
						bool profiling;
 | 
				
			||||||
	uint64_t script_frame_time;
 | 
						uint64_t script_frame_time;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Map<String, ObjectID> orphan_subclasses;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	int calls;
 | 
						int calls;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -506,6 +510,9 @@ public:
 | 
				
			||||||
	virtual bool handles_global_class_type(const String &p_type) const;
 | 
						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;
 | 
						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();
 | 
				
			||||||
	~GDScriptLanguage();
 | 
						~GDScriptLanguage();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2123,15 +2123,21 @@ void GDScriptCompiler::_make_scripts(GDScript *p_script, const GDScriptParser::C
 | 
				
			||||||
		StringName name = p_class->subclasses[i]->name;
 | 
							StringName name = p_class->subclasses[i]->name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Ref<GDScript> subclass;
 | 
							Ref<GDScript> subclass;
 | 
				
			||||||
 | 
							String fully_qualified_name = p_script->fully_qualified_name + "::" + name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (old_subclasses.has(name)) {
 | 
							if (old_subclasses.has(name)) {
 | 
				
			||||||
			subclass = old_subclasses[name];
 | 
								subclass = old_subclasses[name];
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								Ref<GDScript> orphan_subclass = GDScriptLanguage::get_singleton()->get_orphan_subclass(fully_qualified_name);
 | 
				
			||||||
 | 
								if (orphan_subclass.is_valid()) {
 | 
				
			||||||
 | 
									subclass = orphan_subclass;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				subclass.instance();
 | 
									subclass.instance();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		subclass->_owner = p_script;
 | 
							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);
 | 
							p_script->subclasses.insert(name, subclass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_make_scripts(subclass.ptr(), p_class->subclasses[i], false);
 | 
							_make_scripts(subclass.ptr(), p_class->subclasses[i], false);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue