Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a struct-like type that can be exposed to scripting. #82198

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,8 @@ if env.msvc and not methods.using_clang(env): # MSVC
]
)

env.Append(CCFLAGS=["/permissive-"])

if env["werror"]:
env.Append(CCFLAGS=["/WX"])
env.Append(LINKFLAGS=["/WX"])
Expand Down
77 changes: 77 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "core/math/geometry_3d.h"
#include "core/os/keyboard.h"
#include "core/os/thread_safe.h"
#include "core/variant/struct.h"
#include "core/variant/typed_array.h"

namespace core_bind {
Expand Down Expand Up @@ -1442,6 +1443,15 @@ Dictionary ClassDB::class_get_signal(const StringName &p_class, const StringName
}
}

//Struct<MethodInfo> ClassDB::class_get_signal_as_struct(const StringName &p_class, const StringName &p_signal) const {
// MethodInfo signal;
// if (::ClassDB::get_signal(p_class, p_signal, &signal)) {
// return Struct<MethodInfo>(signal);
// } else {
// return Struct<MethodInfo>();
// }
//}

TypedArray<Dictionary> ClassDB::class_get_signal_list(const StringName &p_class, bool p_no_inheritance) const {
List<MethodInfo> signals;
::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance);
Expand All @@ -1454,6 +1464,12 @@ TypedArray<Dictionary> ClassDB::class_get_signal_list(const StringName &p_class,
return ret;
}

//TypedArray<Struct<MethodInfo>> ClassDB::class_get_signal_list_as_structs(const StringName &p_class, bool p_no_inheritance) const {
// List<MethodInfo> signals;
// ::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance);
// return TypedArray<Struct<MethodInfo>>(&signals);
//}

TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_class, bool p_no_inheritance) const {
List<PropertyInfo> plist;
::ClassDB::get_property_list(p_class, &plist, p_no_inheritance);
Expand All @@ -1465,6 +1481,12 @@ TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_clas
return ret;
}

//TypedArray<Struct<PropertyInfo>> ClassDB::class_get_property_list_as_structs(const StringName &p_class, bool p_no_inheritance) const {
// List<PropertyInfo> plist;
// ::ClassDB::get_property_list(p_class, &plist, p_no_inheritance);
// return TypedArray<Struct<PropertyInfo>>(&plist);
//}

StringName ClassDB::class_get_property_getter(const StringName &p_class, const StringName &p_property) {
return ::ClassDB::get_property_getter(p_class, p_property);
}
Expand Down Expand Up @@ -1525,6 +1547,12 @@ TypedArray<Dictionary> ClassDB::class_get_method_list(const StringName &p_class,
return ret;
}

//TypedArray<Struct<MethodInfo>> ClassDB::class_get_method_list_as_structs(const StringName &p_class, bool p_no_inheritance) const {
// List<MethodInfo> methods;
// ::ClassDB::get_method_list(p_class, &methods, p_no_inheritance);
// return TypedArray<Struct<MethodInfo>>(&methods);
//}

Variant ClassDB::class_call_static_method(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) {
if (p_argcount < 2) {
r_call_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
Expand Down Expand Up @@ -1609,6 +1637,47 @@ bool ClassDB::is_class_enum_bitfield(const StringName &p_class, const StringName
return ::ClassDB::is_enum_bitfield(p_class, p_enum, p_no_inheritance);
}

bool ClassDB::class_has_struct(const StringName &p_class, const StringName &p_struct, bool p_no_inheritance) const {
return ::ClassDB::get_struct_info(p_class, p_struct, p_no_inheritance) != nullptr;
}

TypedArray<Dictionary> ClassDB::class_get_struct_list(const StringName &p_class, bool p_no_inheritance) const {
List<StructInfo> structs;
TypedArray<Dictionary> ret;
::ClassDB::get_struct_list(p_class, &structs, p_no_inheritance);
for (const StructInfo &struct_info : structs) {
Dictionary struct_dict;
for (int i = 0; i < struct_info.count; i++) {
Dictionary member_dict;
member_dict[SNAME("name")] = struct_info.names[i];
member_dict[SNAME("type")] = struct_info.types[i];
member_dict[SNAME("class_name")] = struct_info.class_names[i];
member_dict[SNAME("default_value")] = struct_info.default_values[i];
struct_dict[struct_info.name] = member_dict;
}
ret.push_back(struct_dict);
}
return ret;
}

TypedArray<Dictionary> ClassDB::class_get_struct_members(const StringName &p_class, const StringName &p_struct) const {
// TODO: this should return an array of structs if possible without circular reference
TypedArray<Dictionary> ret;
const StructInfo *struct_info = ::ClassDB::get_struct_info(p_class, p_struct);
if (!struct_info) {
return ret; // TODO: should this be an error?
}
for (int i = 0; i < struct_info->count; i++) {
Dictionary dict;
dict[SNAME("name")] = struct_info->names[i];
dict[SNAME("type")] = struct_info->types[i];
dict[SNAME("class_name")] = struct_info->class_names[i];
dict[SNAME("default_value")] = struct_info->default_values[i];
ret.push_back(dict);
}
return ret;
}

bool ClassDB::is_class_enabled(const StringName &p_class) const {
return ::ClassDB::is_class_enabled(p_class);
}
Expand Down Expand Up @@ -1650,9 +1719,12 @@ void ClassDB::_bind_methods() {

::ClassDB::bind_method(D_METHOD("class_has_signal", "class", "signal"), &ClassDB::class_has_signal);
::ClassDB::bind_method(D_METHOD("class_get_signal", "class", "signal"), &ClassDB::class_get_signal);
//::ClassDB::bind_method(D_METHOD("class_get_signal_as_struct", "class", "signal"), &ClassDB::class_get_signal_as_struct);
::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false));
//::ClassDB::bind_method(D_METHOD("class_get_signal_list_as_structs", "class", "no_inheritance"), &ClassDB::class_get_signal_list_as_structs, DEFVAL(false));

::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false));
//::ClassDB::bind_method(D_METHOD("class_get_property_list_as_structs", "class", "no_inheritance"), &ClassDB::class_get_property_list_as_structs, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("class_get_property_getter", "class", "property"), &ClassDB::class_get_property_getter);
::ClassDB::bind_method(D_METHOD("class_get_property_setter", "class", "property"), &ClassDB::class_get_property_setter);
::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property);
Expand All @@ -1665,6 +1737,7 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("class_get_method_argument_count", "class", "method", "no_inheritance"), &ClassDB::class_get_method_argument_count, DEFVAL(false));

::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false));
//::ClassDB::bind_method(D_METHOD("class_get_method_list_as_structs", "class", "no_inheritance"), &ClassDB::class_get_method_list_as_structs, DEFVAL(false));

::ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "class_call_static_method", &ClassDB::class_call_static_method, MethodInfo("class_call_static_method", PropertyInfo(Variant::STRING_NAME, "class"), PropertyInfo(Variant::STRING_NAME, "method")));

Expand All @@ -1680,6 +1753,10 @@ void ClassDB::_bind_methods() {

::ClassDB::bind_method(D_METHOD("is_class_enum_bitfield", "class", "enum", "no_inheritance"), &ClassDB::is_class_enum_bitfield, DEFVAL(false));

// ::ClassDB::bind_method(D_METHOD("class_has_struct", "class", "struct", "no_inheritance"), &ClassDB::class_has_struct, DEFVAL(false));
// ::ClassDB::bind_method(D_METHOD("class_get_struct_list", "class", "no_inheritance"), &ClassDB::class_get_struct_list, DEFVAL(false));
// ::ClassDB::bind_method(D_METHOD("class_get_struct_members", "class", "struct"), &ClassDB::class_get_struct_members);

::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled);

BIND_ENUM_CONSTANT(API_CORE);
Expand Down
8 changes: 8 additions & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,9 +468,12 @@ class ClassDB : public Object {
APIType class_get_api_type(const StringName &p_class) const;
bool class_has_signal(const StringName &p_class, const StringName &p_signal) const;
Dictionary class_get_signal(const StringName &p_class, const StringName &p_signal) const;
//Struct<MethodInfo> class_get_signal_as_struct(const StringName &p_class, const StringName &p_signal) const;
TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const;
//TypedArray<Struct<MethodInfo>> class_get_signal_list_as_structs(const StringName &p_class, bool p_no_inheritance = false) const;

TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const;
//TypedArray<Struct<PropertyInfo>> class_get_property_list_as_structs(const StringName &p_class, bool p_no_inheritance = false) const;
StringName class_get_property_getter(const StringName &p_class, const StringName &p_property);
StringName class_get_property_setter(const StringName &p_class, const StringName &p_property);
Variant class_get_property(Object *p_object, const StringName &p_property) const;
Expand All @@ -483,6 +486,7 @@ class ClassDB : public Object {
int class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;

TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const;
//TypedArray<Struct<MethodInfo>> class_get_method_list_as_structs(const StringName &p_class, bool p_no_inheritance = false) const;
Variant class_call_static_method(const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error);

PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
Expand All @@ -498,6 +502,10 @@ class ClassDB : public Object {

bool is_class_enabled(const StringName &p_class) const;

bool class_has_struct(const StringName &p_class, const StringName &p_struct, bool p_no_inheritance = false) const;
TypedArray<Dictionary> class_get_struct_list(const StringName &p_class, bool p_no_inheritance = false) const;
TypedArray<Dictionary> class_get_struct_members(const StringName &p_class, const StringName &p_struct) const;

#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#endif
Expand Down
73 changes: 73 additions & 0 deletions core/object/class_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
#include "core/os/mutex.h"
#include "core/variant/struct.h"
#include "core/version.h"

#define OBJTYPE_RLOCK RWLockRead _rw_lockr_(lock);
Expand Down Expand Up @@ -505,6 +506,8 @@ uint32_t ClassDB::get_api_hash(APIType p_api) {
hash = hash_murmur3_one_64(F.hint_string.hash(), hash);
hash = hash_murmur3_one_64(F.usage, hash);
}

// TODO: do I need to incorporate the structs into the hash?
}

hash = hash_fmix32(hash);
Expand Down Expand Up @@ -1317,6 +1320,67 @@ bool ClassDB::is_enum_bitfield(const StringName &p_class, const StringName &p_na
return false;
}

void ClassDB::bind_struct(const StringName &p_class_name, const StructInfo &p_struct_info) {
OBJTYPE_WLOCK;

ClassInfo *type = classes.getptr(p_class_name);

ERR_FAIL_NULL(type);

if (type->struct_map.has(p_struct_info.name)) {
ERR_FAIL();
}

String struct_name = p_struct_info.name;
if (struct_name.contains(".")) {
struct_name = struct_name.get_slicec('.', 1);
}

type->struct_map.insert(struct_name, p_struct_info);
}

void ClassDB::get_struct_list(const StringName &p_class, List<StructInfo> *r_structs, bool p_no_inheritance) {
OBJTYPE_RLOCK;

ClassInfo *type = classes.getptr(p_class);

while (type) {
for (const KeyValue<StringName, StructInfo> &E : type->struct_map) {
r_structs->push_back(E.value);
}

if (p_no_inheritance) {
break;
}

type = type->inherits_ptr;
}
}

const StructInfo *ClassDB::get_struct_info(const StringName &p_class, const StringName &p_name, bool p_no_inheritance) {
OBJTYPE_RLOCK;

ClassInfo *type = classes.getptr(p_class);
while (type) {
if (const StructInfo *info = type->struct_map.getptr(p_name)) {
return info;
}
if (p_no_inheritance) {
return nullptr;
}
type = type->inherits_ptr;
}
return nullptr;
}

const StructInfo *ClassDB::get_struct_info(const String &p_qualified_name, bool p_no_inheritance) {
Vector<String> names = String(p_qualified_name).split("."); // TODO: what about cases other than size == 2?
if (names.size() == 2) {
return ClassDB::get_struct_info(names[0], names[1]);
}
return nullptr;
}

void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) {
OBJTYPE_WLOCK;

Expand Down Expand Up @@ -1429,6 +1493,15 @@ void ClassDB::add_property_array(const StringName &p_class, const StringName &p_
type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix));
}

// TODO: This probably isn't right, I just copied the function above.
void ClassDB::add_property_struct(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix) {
OBJTYPE_WLOCK; // TODO: I'm not sure what this does but it's in the one above so I figure I need it
ClassInfo *type = classes.getptr(p_class);
ERR_FAIL_NULL(type);

type->property_list.push_back(PropertyInfo(Variant::NIL, p_path, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, p_array_element_prefix));
}

// NOTE: For implementation simplicity reasons, this method doesn't allow setters to have optional arguments at the end.
void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) {
lock.read_lock();
Expand Down
10 changes: 10 additions & 0 deletions core/object/class_db.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class ClassDB {
};

HashMap<StringName, EnumInfo> enum_map;
HashMap<StringName, StructInfo> struct_map;
HashMap<StringName, MethodInfo> signal_map;
List<PropertyInfo> property_list;
HashMap<StringName, PropertyInfo> property_map;
Expand Down Expand Up @@ -426,6 +427,7 @@ class ClassDB {
static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = "", int p_indent_depth = 0);
static void add_property_array_count(const StringName &p_class, const String &p_label, const StringName &p_count_property, const StringName &p_count_setter, const StringName &p_count_getter, const String &p_array_element_prefix, uint32_t p_count_usage = PROPERTY_USAGE_DEFAULT);
static void add_property_array(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix);
static void add_property_struct(const StringName &p_class, const StringName &p_path, const String &p_array_element_prefix);
static void add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1);
static void set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default);
static void add_linked_property(const StringName &p_class, const String &p_property, const String &p_linked_property);
Expand Down Expand Up @@ -467,6 +469,11 @@ class ClassDB {
static bool has_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static bool is_enum_bitfield(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);

static void bind_struct(const StringName &p_class, const StructInfo &p_struct_info);
static void get_struct_list(const StringName &p_class, List<StructInfo> *r_structs, bool p_no_inheritance = false);
static const StructInfo *get_struct_info(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false);
static const StructInfo *get_struct_info(const String &p_qualified_name, bool p_no_inheritance = false);

static void set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values);
static Vector<Error> get_method_error_return_values(const StringName &p_class, const StringName &p_method);
static Variant class_get_default_property_value(const StringName &p_class, const StringName &p_property, bool *r_valid = nullptr);
Expand Down Expand Up @@ -506,6 +513,9 @@ class ClassDB {
#define BIND_CONSTANT(m_constant) \
::ClassDB::bind_integer_constant(get_class_static(), StringName(), #m_constant, m_constant);

#define BIND_STRUCT(m_struct) \
::ClassDB::bind_struct(get_class_static(), m_struct::Layout::get_struct_info());

#ifdef DEBUG_METHODS_ENABLED

_FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr) {
Expand Down
Loading
Loading