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:
parent
766c6dbb24
commit
98a81fe8aa
5 changed files with 197 additions and 5 deletions
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]) {
|
||||
|
|
Loading…
Reference in a new issue