diff options
| author | Raul Santos <raulsntos@gmail.com> | 2023-07-14 20:22:04 +0200 |
|---|---|---|
| committer | Raul Santos <raulsntos@gmail.com> | 2023-08-06 19:02:53 +0200 |
| commit | 23f7f24e8ab3673dd9967801bf4e4f4c82c54b8b (patch) | |
| tree | 1d72041208628fc55f800cab9d46b53ceebf617c /modules/mono | |
| parent | 6b713b1682c453add138555092e3fa0a7ee4261c (diff) | |
| download | redot-engine-23f7f24e8ab3673dd9967801bf4e4f4c82c54b8b.tar.gz | |
C#: Add hard-coded singletons to avoid breaking compat
Co-authored-by: Ignacio Etcheverry <ignalfonsore@gmail.com>
Diffstat (limited to 'modules/mono')
| -rw-r--r-- | modules/mono/editor/bindings_generator.cpp | 56 | ||||
| -rw-r--r-- | modules/mono/editor/bindings_generator.h | 10 |
2 files changed, 46 insertions, 20 deletions
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 43b4962641..9621bf5f10 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -1515,39 +1515,44 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str "' for class '" + itype.name + "'."); } - if (itype.is_singleton) { - // Add the type name and the singleton pointer as static fields - StringName instance_name = itype.name + CS_SINGLETON_INSTANCE_SUFFIX; - String instance_type_name = obj_types.has(instance_name) - ? obj_types[instance_name].proxy_name - : "GodotObject"; + // Add native name static field and cached type. + + if (is_derived_type && !itype.is_singleton) { + output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n"; + } + + output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); + output.append(itype.name); + output.append("\";\n"); + + if (itype.is_singleton || itype.is_compat_singleton) { + // Add the Singleton static property. + + String instance_type_name; + + if (itype.is_singleton) { + StringName instance_name = itype.name + CS_SINGLETON_INSTANCE_SUFFIX; + instance_type_name = obj_types.has(instance_name) + ? obj_types[instance_name].proxy_name + : "GodotObject"; + } else { + instance_type_name = itype.proxy_name; + } output.append(MEMBER_BEGIN "private static " + instance_type_name + " singleton;\n"); output << MEMBER_BEGIN "public static " + instance_type_name + " " CS_PROPERTY_SINGLETON " =>\n" << INDENT2 "singleton \?\?= (" + instance_type_name + ")" << C_METHOD_ENGINE_GET_SINGLETON "(\"" << itype.name << "\");\n"; + } - output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); - output.append(itype.name); - output.append("\";\n"); - } else { + if (!itype.is_singleton) { // IMPORTANT: We also generate the static fields for GodotObject instead of declaring // them manually in the `GodotObject.base.cs` partial class declaration, because they're // required by other static fields in this generated partial class declaration. // Static fields are initialized in order of declaration, but when they're in different // partial class declarations then it becomes harder to tell (Rider warns about this). - // Add native name static field - - if (is_derived_type) { - output << MEMBER_BEGIN "private static readonly System.Type CachedType = typeof(" << itype.proxy_name << ");\n"; - } - - output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \""); - output.append(itype.name); - output.append("\";\n"); - if (itype.is_instantiable) { // Add native constructor static field @@ -2964,6 +2969,11 @@ bool BindingsGenerator::_populate_object_type_interfaces() { itype.is_ref_counted = ClassDB::is_parent_class(type_cname, name_cache.type_RefCounted); itype.memory_own = itype.is_ref_counted; + if (itype.is_singleton && compat_singletons.has(itype.cname)) { + itype.is_singleton = false; + itype.is_compat_singleton = true; + } + itype.c_out = "%5return "; itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED; itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n"; @@ -4003,6 +4013,10 @@ void BindingsGenerator::_initialize_blacklisted_methods() { blacklisted_methods["Object"].push_back("_init"); // never called in C# (TODO: implement it) } +void BindingsGenerator::_initialize_compat_singletons() { + // No compat singletons yet. +} + void BindingsGenerator::_log(const char *p_format, ...) { if (log_print_enabled) { va_list list; @@ -4022,6 +4036,8 @@ void BindingsGenerator::_initialize() { _initialize_blacklisted_methods(); + _initialize_compat_singletons(); + bool obj_type_ok = _populate_object_type_interfaces(); ERR_FAIL_COND_MSG(!obj_type_ok, "Failed to generate object type interfaces"); diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index 80b0ed0b7d..d4c7a59e74 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -231,6 +231,14 @@ class BindingsGenerator { bool is_ref_counted = false; /** + * Class is a singleton, but can't be declared as a static class as that would + * break backwards compatibility. As such, instead of going with a static class, + * we use the actual singleton pattern (private constructor with instance property), + * which doesn't break compatibility. + */ + bool is_compat_singleton = false; + + /** * Determines whether the native return value of this type must be disposed * by the generated internal call (think of `godot_string`, whose destructor * must be called). Some structs that are disposable may still disable this @@ -615,8 +623,10 @@ class BindingsGenerator { HashMap<const MethodInterface *, const InternalCall *> method_icalls_map; HashMap<StringName, List<StringName>> blacklisted_methods; + HashSet<StringName> compat_singletons; void _initialize_blacklisted_methods(); + void _initialize_compat_singletons(); struct NameCache { StringName type_void = StaticCString::create("void"); |
