Add API for registering native extensions

* First step for GDNative to behave more like modules
* Only Object and ClassDB, the rest needs to happen on the GDNative side.
This commit is contained in:
reduz 2021-06-04 14:33:48 -03:00
parent 766c6dbb24
commit 98a81fe8aa
5 changed files with 197 additions and 5 deletions

View file

@ -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() {

View file

@ -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);

View file

@ -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) {

View file

@ -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 {

View file

@ -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]) {