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 3 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
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
Next Next commit
Added Structs to core.
nlupugla committed Nov 12, 2024
commit d25fe05b1e5ddd4e57c90aa7562b699dfb2f3553
2 changes: 2 additions & 0 deletions SConstruct
Original file line number Diff line number Diff line change
@@ -858,6 +858,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"])
77 changes: 77 additions & 0 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
@@ -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 {
@@ -1462,6 +1463,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);
@@ -1474,6 +1484,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);
@@ -1485,6 +1501,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);
}
@@ -1545,6 +1567,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(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;
@@ -1629,6 +1657,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);
}
@@ -1670,9 +1739,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);
@@ -1685,6 +1757,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", &ClassDB::class_call_static, MethodInfo("class_call_static", PropertyInfo(Variant::STRING_NAME, "class"), PropertyInfo(Variant::STRING_NAME, "method")));

@@ -1700,6 +1773,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);
8 changes: 8 additions & 0 deletions core/core_bind.h
Original file line number Diff line number Diff line change
@@ -471,9 +471,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;
@@ -486,6 +489,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(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;
@@ -501,6 +505,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
73 changes: 73 additions & 0 deletions core/object/class_db.cpp
Original file line number Diff line number Diff line change
@@ -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);
@@ -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);
@@ -1335,6 +1338,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;

@@ -1447,6 +1511,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();
10 changes: 10 additions & 0 deletions core/object/class_db.h
Original file line number Diff line number Diff line change
@@ -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;
@@ -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);
@@ -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);
@@ -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) {
Loading