Bladeren bron

Merge pull request #49318 from reduz/native-extension

Add API for registering native extensions
Rémi Verschelde 4 jaren geleden
bovenliggende
commit
6e9a4c268f
5 gewijzigde bestanden met toevoegingen van 197 en 5 verwijderingen
  1. 60 2
      core/object/class_db.cpp
  2. 9 0
      core/object/class_db.h
  3. 47 0
      core/object/object.cpp
  4. 75 3
      core/object/object.h
  5. 6 0
      core/object/reference.cpp

+ 60 - 2
core/object/class_db.cpp

@@ -501,12 +501,27 @@ void ClassDB::add_compatibility_class(const StringName &p_class, const StringNam
 	compat_classes[p_class] = p_fallback;
 }
 
+thread_local bool initializing_with_extension = false;
+thread_local ObjectNativeExtension *initializing_extension = nullptr;
+thread_local void *initializing_extension_instance = nullptr;
+
+void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, void **r_extension_instance) {
+	if (initializing_with_extension) {
+		*r_extension = initializing_extension;
+		*r_extension_instance = initializing_extension_instance;
+		initializing_with_extension = false;
+	} else {
+		*r_extension = nullptr;
+		*r_extension_instance = nullptr;
+	}
+}
+
 Object *ClassDB::instance(const StringName &p_class) {
 	ClassInfo *ti;
 	{
 		OBJTYPE_RLOCK;
 		ti = classes.getptr(p_class);
-		if (!ti || ti->disabled || !ti->creation_func) {
+		if (!ti || ti->disabled || !ti->creation_func || (ti->native_extension && !ti->native_extension->create_instance)) {
 			if (compat_classes.has(p_class)) {
 				ti = classes.getptr(compat_classes[p_class]);
 			}
@@ -521,6 +536,11 @@ Object *ClassDB::instance(const StringName &p_class) {
 		return nullptr;
 	}
 #endif
+	if (ti->native_extension) {
+		initializing_with_extension = true;
+		initializing_extension = ti->native_extension;
+		initializing_extension_instance = ti->native_extension->create_instance(ti->native_extension->create_instance_userdata);
+	}
 	return ti->creation_func();
 }
 
@@ -534,7 +554,7 @@ bool ClassDB::can_instance(const StringName &p_class) {
 		return false;
 	}
 #endif
-	return (!ti->disabled && ti->creation_func != nullptr);
+	return (!ti->disabled && ti->creation_func != nullptr && !(ti->native_extension && !ti->native_extension->create_instance));
 }
 
 void ClassDB::_add_class2(const StringName &p_class, const StringName &p_inherits) {
@@ -1310,6 +1330,24 @@ bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inhe
 	return false;
 }
 
+void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method) {
+	ClassInfo *type = classes.getptr(p_class);
+	if (!type) {
+		ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'.");
+	}
+
+	if (type->method_map.has(p_method->get_name())) {
+		// overloading not supported
+		ERR_FAIL_MSG("Method already bound '" + p_class + "::" + p_method->get_name() + "'.");
+	}
+
+#ifdef DEBUG_METHODS_ENABLED
+	type->method_order.push_back(p_method->get_name());
+#endif
+
+	type->method_map[p_method->get_name()] = p_method;
+}
+
 #ifdef DEBUG_METHODS_ENABLED
 MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount) {
 	StringName mdname = method_name.name;
@@ -1545,6 +1583,26 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con
 	return var;
 }
 
+void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) {
+	GLOBAL_LOCK_FUNCTION;
+
+	ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name));
+	ERR_FAIL_COND_MSG(classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name));
+
+	ClassInfo *parent = classes.getptr(p_extension->parent_class_name);
+
+	ClassInfo c;
+	c.api = p_extension->editor_class ? API_EDITOR_EXTENSION : API_EXTENSION;
+	c.native_extension = p_extension;
+	c.name = p_extension->class_name;
+	c.creation_func = parent->creation_func;
+	c.inherits = parent->name;
+	c.class_ptr = parent->class_ptr;
+	c.inherits_ptr = parent;
+
+	classes[p_extension->class_name] = c;
+}
+
 RWLock ClassDB::lock;
 
 void ClassDB::cleanup_defaults() {

+ 9 - 0
core/object/class_db.h

@@ -97,6 +97,8 @@ public:
 	enum APIType {
 		API_CORE,
 		API_EDITOR,
+		API_EXTENSION,
+		API_EDITOR_EXTENSION,
 		API_NONE
 	};
 
@@ -115,6 +117,8 @@ public:
 		ClassInfo *inherits_ptr = nullptr;
 		void *class_ptr = nullptr;
 
+		ObjectNativeExtension *native_extension = nullptr;
+
 		HashMap<StringName, MethodBind *> method_map;
 		HashMap<StringName, int> constant_map;
 		HashMap<StringName, List<StringName>> enum_map;
@@ -199,6 +203,8 @@ public:
 		//nothing
 	}
 
+	static void register_extension_class(ObjectNativeExtension *p_extension);
+
 	template <class T>
 	static Object *_create_ptr_func() {
 		return T::create();
@@ -226,6 +232,7 @@ public:
 	static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);
 	static bool can_instance(const StringName &p_class);
 	static Object *instance(const StringName &p_class);
+	static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, void **r_extension_instance);
 	static APIType get_api_type(const StringName &p_class);
 
 	static uint64_t get_api_hash(APIType p_api);
@@ -334,6 +341,8 @@ public:
 		return bind;
 	}
 
+	static void bind_method_custom(const StringName &p_class, MethodBind *p_method);
+
 	static void add_signal(StringName p_class, const MethodInfo &p_signal);
 	static bool has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance = false);
 	static bool get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal);

+ 47 - 0
core/object/object.cpp

@@ -385,6 +385,15 @@ void Object::set(const StringName &p_name, const Variant &p_value, bool *r_valid
 		}
 	}
 
+	if (_extension && _extension->set) {
+		if (_extension->set(_extension_instance, &p_name, &p_value)) {
+			if (r_valid) {
+				*r_valid = true;
+			}
+			return;
+		}
+	}
+
 	//try built-in setgetter
 	{
 		if (ClassDB::set_property(this, p_name, p_value, r_valid)) {
@@ -451,6 +460,15 @@ Variant Object::get(const StringName &p_name, bool *r_valid) const {
 		}
 	}
 
+	if (_extension && _extension->get) {
+		if (_extension->get(_extension_instance, &p_name, &ret)) {
+			if (r_valid) {
+				*r_valid = true;
+			}
+			return ret;
+		}
+	}
+
 	//try built-in setgetter
 	{
 		if (ClassDB::get_property(const_cast<Object *>(this), p_name, ret)) {
@@ -596,6 +614,17 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons
 
 	_get_property_listv(p_list, p_reversed);
 
+	if (_extension && _extension->get_property_list) {
+		uint32_t pcount;
+		const ObjectNativeExtension::PInfo *pinfo = _extension->get_property_list(_extension_instance, &pcount);
+		for (uint32_t i = 0; i < pcount; i++) {
+			p_list->push_back(PropertyInfo(Variant::Type(pinfo[i].type), pinfo[i].class_name, PropertyHint(pinfo[i].hint), pinfo[i].hint_string, pinfo[i].usage, pinfo[i].class_name));
+		}
+		if (_extension->free_property_list) {
+			_extension->free_property_list(_extension_instance, pinfo);
+		}
+	}
+
 	if (!is_class("Script")) { // can still be set, but this is for user-friendliness
 		p_list->push_back(PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_DEFAULT));
 	}
@@ -761,6 +790,7 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
 
 	Variant ret;
 	OBJ_DEBUG_LOCK
+
 	if (script_instance) {
 		ret = script_instance->call(p_method, p_args, p_argcount, r_error);
 		//force jumptable
@@ -778,6 +808,8 @@ Variant Object::call(const StringName &p_method, const Variant **p_args, int p_a
 		}
 	}
 
+	//extension does not need this, because all methods are registered in MethodBind
+
 	MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
 
 	if (method) {
@@ -795,6 +827,10 @@ void Object::notification(int p_notification, bool p_reversed) {
 	if (script_instance) {
 		script_instance->notification(p_notification);
 	}
+
+	if (_extension && _extension->notification) {
+		_extension->notification(_extension_instance, p_notification);
+	}
 }
 
 String Object::to_string() {
@@ -805,6 +841,9 @@ String Object::to_string() {
 			return ret;
 		}
 	}
+	if (_extension && _extension->to_string) {
+		return _extension->to_string(_extension_instance);
+	}
 	return "[" + get_class() + ":" + itos(get_instance_id()) + "]";
 }
 
@@ -1751,6 +1790,8 @@ void Object::_construct_object(bool p_reference) {
 	_instance_id = ObjectDB::add_instance(this);
 	memset(_script_instance_bindings, 0, sizeof(void *) * MAX_SCRIPT_INSTANCE_BINDINGS);
 
+	ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance);
+
 #ifdef DEBUG_ENABLED
 	_lock_index.init(1);
 #endif
@@ -1770,6 +1811,12 @@ Object::~Object() {
 	}
 	script_instance = nullptr;
 
+	if (_extension && _extension->free_instance) {
+		_extension->free_instance(_extension->create_instance_userdata, _extension_instance);
+		_extension = nullptr;
+		_extension_instance = nullptr;
+	}
+
 	const StringName *S = nullptr;
 
 	if (_emitting) {

+ 75 - 3
core/object/object.h

@@ -238,6 +238,50 @@ struct MethodInfo {
 ////else
 //return nullptr;
 
+// API used to extend in GDNative and other C compatible compiled languages
+class MethodBind;
+
+struct ObjectNativeExtension {
+	ObjectNativeExtension *parent = nullptr;
+	StringName parent_class_name;
+	StringName class_name;
+	bool editor_class = false;
+	bool (*set)(void *instance, const void *name, const void *value) = nullptr;
+	bool (*get)(void *instance, const void *name, void *ret_variant) = nullptr;
+	struct PInfo {
+		uint32_t type;
+		const char *name;
+		const char *class_name;
+		uint32_t hint;
+		const char *hint_string;
+		uint32_t usage;
+	};
+	const PInfo *(*get_property_list)(void *instance, uint32_t *count) = nullptr;
+	void (*free_property_list)(void *instance, const PInfo *) = nullptr;
+
+	//call is not used, as all methods registered in ClassDB
+
+	void (*notification)(void *instance, int32_t what) = nullptr;
+	const char *(*to_string)(void *instance) = nullptr;
+
+	void (*reference)(void *instance) = nullptr;
+	void (*unreference)(void *instance) = nullptr;
+
+	_FORCE_INLINE_ bool is_class(const String &p_class) const {
+		const ObjectNativeExtension *e = this;
+		while (e) {
+			if (p_class == e->class_name.operator String()) {
+				return true;
+			}
+			e = e->parent;
+		}
+		return false;
+	}
+	void *create_instance_userdata = nullptr;
+	void *(*create_instance)(void *create_instance_userdata) = nullptr;
+	void (*free_instance)(void *create_instance_userdata, void *instance) = nullptr;
+};
+
 /*
    the following is an incomprehensible blob of hacks and workarounds to compensate for many of the fallencies in C++. As a plus, this macro pretty much alone defines the object model.
 */
@@ -262,9 +306,15 @@ private:
                                                                                                                                                  \
 public:                                                                                                                                          \
 	virtual String get_class() const override {                                                                                                  \
+		if (_get_extension()) {                                                                                                                  \
+			return _get_extension()->class_name.operator String();                                                                               \
+		}                                                                                                                                        \
 		return String(#m_class);                                                                                                                 \
 	}                                                                                                                                            \
 	virtual const StringName *_get_class_namev() const override {                                                                                \
+		if (_get_extension()) {                                                                                                                  \
+			return &_get_extension()->class_name;                                                                                                \
+		}                                                                                                                                        \
 		if (!_class_name) {                                                                                                                      \
 			_class_name = get_class_static();                                                                                                    \
 		}                                                                                                                                        \
@@ -297,7 +347,12 @@ public:
 	static String inherits_static() {                                                                                                            \
 		return String(#m_inherits);                                                                                                              \
 	}                                                                                                                                            \
-	virtual bool is_class(const String &p_class) const override { return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class); }       \
+	virtual bool is_class(const String &p_class) const override {                                                                                \
+		if (_get_extension() && _get_extension()->is_class(p_class)) {                                                                           \
+			return true;                                                                                                                         \
+		}                                                                                                                                        \
+		return (p_class == (#m_class)) ? true : m_inherits::is_class(p_class);                                                                   \
+	}                                                                                                                                            \
 	virtual bool is_class_ptr(void *p_ptr) const override { return (p_ptr == get_class_ptr_static()) ? true : m_inherits::is_class_ptr(p_ptr); } \
                                                                                                                                                  \
 	static void get_valid_parents_static(List<String> *p_parents) {                                                                              \
@@ -440,6 +495,9 @@ private:
 	friend bool predelete_handler(Object *);
 	friend void postinitialize_handler(Object *);
 
+	ObjectNativeExtension *_extension = nullptr;
+	void *_extension_instance = nullptr;
+
 	struct SignalData {
 		struct Slot {
 			int reference_count = 0;
@@ -495,6 +553,8 @@ private:
 	Object(bool p_reference);
 
 protected:
+	_ALWAYS_INLINE_ const ObjectNativeExtension *_get_extension() const { return _extension; }
+	_ALWAYS_INLINE_ void *_get_extension_instance() const { return _extension_instance; }
 	virtual void _initialize_classv() { initialize_class(); }
 	virtual bool _setv(const StringName &p_name, const Variant &p_property) { return false; };
 	virtual bool _getv(const StringName &p_name, Variant &r_property) const { return false; };
@@ -610,13 +670,25 @@ public:
 	static String get_parent_class_static() { return String(); }
 	static String get_category_static() { return String(); }
 
-	virtual String get_class() const { return "Object"; }
+	virtual String get_class() const {
+		if (_extension)
+			return _extension->class_name.operator String();
+		return "Object";
+	}
 	virtual String get_save_class() const { return get_class(); } //class stored when saving
 
-	virtual bool is_class(const String &p_class) const { return (p_class == "Object"); }
+	virtual bool is_class(const String &p_class) const {
+		if (_extension && _extension->is_class(p_class)) {
+			return true;
+		}
+		return (p_class == "Object");
+	}
 	virtual bool is_class_ptr(void *p_ptr) const { return get_class_ptr_static() == p_ptr; }
 
 	_FORCE_INLINE_ const StringName &get_class_name() const {
+		if (_extension) {
+			return _extension->class_name;
+		}
 		if (!_class_ptr) {
 			return *_get_class_namev();
 		} else {

+ 6 - 0
core/object/reference.cpp

@@ -62,6 +62,9 @@ bool Reference::reference() {
 		if (get_script_instance()) {
 			get_script_instance()->refcount_incremented();
 		}
+		if (_get_extension() && _get_extension()->reference) {
+			_get_extension()->reference(_get_extension_instance());
+		}
 		if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) {
 			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
 				if (_script_instance_bindings[i]) {
@@ -83,6 +86,9 @@ bool Reference::unreference() {
 			bool script_ret = get_script_instance()->refcount_decremented();
 			die = die && script_ret;
 		}
+		if (_get_extension() && _get_extension()->unreference) {
+			_get_extension()->unreference(_get_extension_instance());
+		}
 		if (instance_binding_count.get() > 0 && !ScriptServer::are_languages_finished()) {
 			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
 				if (_script_instance_bindings[i]) {