summaryrefslogtreecommitdiff
path: root/modules/mono/editor
diff options
context:
space:
mode:
authorIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2021-05-03 15:21:06 +0200
committerIgnacio Roldán Etcheverry <ignalfonsore@gmail.com>2022-08-22 03:35:59 +0200
commit124fbf95f8ef065215e9fcc937a370dbef3196e1 (patch)
treef2b05a195cd8a9d5a4bc8e527930873349d99a4b /modules/mono/editor
parent0c5f254956f0115e363ce08045dd178dc30b54f8 (diff)
C#: Move marshaling logic and generated glue to C#
We will be progressively moving most code to C#. The plan is to only use Mono's embedding APIs to set things at launch. This will make it much easier to later support CoreCLR too which doesn't have rich embedding APIs. Additionally the code in C# is more maintainable and makes it easier to implement new features, e.g.: runtime codegen which we could use to avoid using reflection for marshaling everytime a field, property or method is accessed. SOME NOTES ON INTEROP We make the same assumptions as GDNative about the size of the Godot structures we use. We take it a bit further by also assuming the layout of fields in some cases, which is riskier but let's us squeeze out some performance by avoiding unnecessary managed to native calls. Code that deals with native structs is less safe than before as there's no RAII and copy constructors in C#. It's like using the GDNative C API directly. One has to take special care to free values they own. Perhaps we could use roslyn analyzers to check this, but I don't know any that uses attributes to determine what's owned or borrowed. As to why we maily use pointers for native structs instead of ref/out: - AFAIK (and confirmed with a benchmark) ref/out are pinned during P/Invoke calls and that has a cost. - Native struct fields can't be ref/out in the first place. - A `using` local can't be passed as ref/out, only `in`. Calling a method or property on an `in` value makes a silent copy, so we want to avoid `in`. REGARDING THE BUILD SYSTEM There's no longer a `mono_glue=yes/no` SCons options. We no longer need to build with `mono_glue=no`, generate the glue and then build again with `mono_glue=yes`. We build only once and generate the glue (which is in C# now). However, SCons no longer builds the C# projects for us. Instead one must run `build_assemblies.py`, e.g.: ```sh %godot_src_root%/modules/mono/build_scripts/build_assemblies.py \ --godot-output-dir=%godot_src_root%/bin \ --godot-target=release_debug` ``` We could turn this into a custom build target, but I don't know how to do that with SCons (it's possible with Meson). OTHER NOTES Most of the moved code doesn't follow the C# naming convention and still has the word Mono in the names despite no longer dealing with Mono's embedding APIs. This is just temporary while transitioning, to make it easier to understand what was moved where.
Diffstat (limited to 'modules/mono/editor')
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs2
-rw-r--r--modules/mono/editor/bindings_generator.cpp1429
-rw-r--r--modules/mono/editor/bindings_generator.h182
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp1
4 files changed, 709 insertions, 905 deletions
diff --git a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
index dd05f28af0..43b1cf0f64 100644
--- a/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
@@ -38,7 +38,7 @@ namespace GodotTools
_watchTimer = new Timer
{
OneShot = false,
- WaitTime = (float)EditorDef("mono/assembly_watch_interval_sec", 0.5)
+ WaitTime = 0.5f
};
_watchTimer.Timeout += TimerTimeout;
AddChild(_watchTimer);
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 2e628cb576..64fe82664d 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -41,12 +41,20 @@
#include "core/string/ucaps.h"
#include "main/main.h"
-#include "../glue/cs_glue_version.gen.h"
#include "../godotsharp_defs.h"
-#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/path_utils.h"
#include "../utils/string_utils.h"
+StringBuilder &operator<<(StringBuilder &r_sb, const String &p_string) {
+ r_sb.append(p_string);
+ return r_sb;
+}
+
+StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
+ r_sb.append(p_cstring);
+ return r_sb;
+}
+
#define CS_INDENT " " // 4 whitespaces
#define INDENT1 CS_INDENT
@@ -60,50 +68,74 @@
#define OPEN_BLOCK "{\n"
#define CLOSE_BLOCK "}\n"
-#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK INDENT3
-#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK INDENT4
+#define OPEN_BLOCK_L1 INDENT1 OPEN_BLOCK
+#define OPEN_BLOCK_L2 INDENT2 OPEN_BLOCK
+#define OPEN_BLOCK_L3 INDENT3 OPEN_BLOCK
#define CLOSE_BLOCK_L2 INDENT2 CLOSE_BLOCK
#define CLOSE_BLOCK_L3 INDENT3 CLOSE_BLOCK
+#define CLOSE_BLOCK_L4 INDENT4 CLOSE_BLOCK
-#define CS_FIELD_MEMORYOWN "memoryOwn"
+#define CS_PARAM_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
#define CS_PARAM_INSTANCE "ptr"
-#define CS_SMETHOD_GETINSTANCE "GetPtr"
+#define CS_STATIC_METHOD_GETINSTANCE "GetPtr"
#define CS_METHOD_CALL "Call"
+#define CS_PROPERTY_SINGLETON "Singleton"
+
+#define CS_STATIC_FIELD_NATIVE_CTOR "NativeCtor"
+#define CS_STATIC_FIELD_METHOD_BIND_PREFIX "MethodBind"
+#define CS_STATIC_FIELD_SIGNAL_NAME_PREFIX "SignalName_"
-#define GLUE_HEADER_FILE "glue_header.h"
#define ICALL_PREFIX "godot_icall_"
-#define SINGLETON_ICALL_SUFFIX "_get_singleton"
-#define ICALL_GET_METHODBIND "__ClassDB_get_method"
+#define ICALL_CLASSDB_GET_METHOD "ClassDB_get_method"
+#define ICALL_CLASSDB_GET_CONSTRUCTOR "ClassDB_get_constructor"
#define C_LOCAL_RET "ret"
#define C_LOCAL_VARARG_RET "vararg_ret"
#define C_LOCAL_PTRCALL_ARGS "call_args"
-#define C_MACRO_OBJECT_CONSTRUCT "GODOTSHARP_INSTANCE_OBJECT"
-
-#define C_NS_MONOUTILS "GDMonoUtils"
-#define C_NS_MONOINTERNALS "GDMonoInternals"
-#define C_METHOD_TIE_MANAGED_TO_UNMANAGED C_NS_MONOINTERNALS "::tie_managed_to_unmanaged"
-#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS "::unmanaged_get_managed"
-
-#define C_NS_MONOMARSHAL "GDMonoMarshal"
-#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL "::mono_object_to_variant"
-#define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL "::variant_to_mono_object"
-#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL "::mono_string_to_godot"
-#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL "::mono_string_from_godot"
-#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL "::mono_array_to_" #m_type
-#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "::" #m_type "_to_mono_array"
-#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL "::managed_to_callable"
-#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL "::callable_to_managed"
-#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL "::signal_info_to_callable"
-#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL "::callable_to_signal_info"
-
-#define BINDINGS_GENERATOR_VERSION UINT32_C(13)
+
+#define C_CLASS_NATIVE_FUNCS "NativeFuncs"
+#define C_NS_MONOUTILS "InteropUtils"
+#define C_METHOD_TIE_MANAGED_TO_UNMANAGED "NativeInterop." C_NS_MONOUTILS ".TieManagedToUnmanaged"
+#define C_METHOD_UNMANAGED_GET_MANAGED C_NS_MONOUTILS ".UnmanagedGetManaged"
+#define C_METHOD_ENGINE_GET_SINGLETON "NativeInterop." C_NS_MONOUTILS ".EngineGetSingleton"
+
+#define C_NS_MONOMARSHAL "Marshaling"
+#define C_METHOD_MANAGED_TO_VARIANT C_NS_MONOMARSHAL ".mono_object_to_variant"
+#define C_METHOD_MANAGED_FROM_VARIANT C_NS_MONOMARSHAL ".variant_to_mono_object"
+#define C_METHOD_MONOSTR_TO_GODOT C_NS_MONOMARSHAL ".mono_string_to_godot"
+#define C_METHOD_MONOSTR_FROM_GODOT C_NS_MONOMARSHAL ".mono_string_from_godot"
+#define C_METHOD_MONOARRAY_TO(m_type) C_NS_MONOMARSHAL ".mono_array_to_" #m_type
+#define C_METHOD_MONOARRAY_FROM(m_type) C_NS_MONOMARSHAL "." #m_type "_to_mono_array"
+#define C_METHOD_MANAGED_TO_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToNative"
+#define C_METHOD_MANAGED_FROM_CALLABLE C_NS_MONOMARSHAL ".ConvertCallableToManaged"
+#define C_METHOD_MANAGED_TO_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToNative"
+#define C_METHOD_MANAGED_FROM_SIGNAL C_NS_MONOMARSHAL ".ConvertSignalToManaged"
// Types that will be ignored by the generator and won't be available in C#.
const Vector<String> ignored_types = { "PhysicsServer3DExtension" };
-const char *BindingsGenerator::TypeInterface::DEFAULT_VARARG_C_IN("\t%0 %1_in = %1;\n");
+typedef String string;
+
+void BindingsGenerator::TypeInterface::postsetup_enum_type(BindingsGenerator::TypeInterface &r_enum_itype) {
+ // C interface for enums is the same as that of 'uint32_t'. Remember to apply
+ // any of the changes done here to the 'uint32_t' type interface as well.
+
+ r_enum_itype.cs_type = r_enum_itype.proxy_name;
+ r_enum_itype.cs_in = "(int)%s";
+ r_enum_itype.cs_out = "%5return (%2)%0(%1);";
+
+ {
+ // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
+ r_enum_itype.c_in = "%5%0 %1_in = %1;\n";
+ r_enum_itype.c_out = "%5return (%0)%1;\n";
+ r_enum_itype.c_type = "long";
+ r_enum_itype.c_arg_in = "&%s_in";
+ }
+ r_enum_itype.c_type_in = "int";
+ r_enum_itype.c_type_out = r_enum_itype.c_type_in;
+ r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
+}
static String fix_doc_description(const String &p_bbcode) {
// This seems to be the correct way to do this. It's the same EditorHelp does.
@@ -794,47 +826,29 @@ void BindingsGenerator::_apply_prefix_to_enum_constants(BindingsGenerator::EnumI
}
}
-void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
+Error BindingsGenerator::_populate_method_icalls_table(const TypeInterface &p_itype) {
for (const MethodInterface &imethod : p_itype.methods) {
if (imethod.is_virtual) {
continue;
}
- const TypeInterface *return_type = _get_type_or_placeholder(imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_null(imethod.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
- String im_sig = "IntPtr " CS_PARAM_METHODBIND;
- String im_unique_sig = imethod.return_type.cname.operator String() + ",IntPtr";
+ String im_unique_sig = get_ret_unique_sig(return_type) + ",CallMethodBind";
if (!imethod.is_static) {
- im_sig += ", IntPtr " CS_PARAM_INSTANCE;
- im_unique_sig += ",IntPtr";
+ im_unique_sig += ",CallInstance";
}
// Get arguments information
int i = 0;
for (const ArgumentInterface &iarg : imethod.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
-
- im_sig += ", ";
- im_sig += arg_type->im_type_in;
- im_sig += " arg";
- im_sig += itos(i + 1);
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
im_unique_sig += ",";
- im_unique_sig += get_unique_sig(*arg_type);
-
- i++;
- }
-
- String im_type_out = return_type->im_type_out;
-
- if (return_type->ret_as_byref_arg) {
- // Doesn't affect the unique signature
- im_type_out = "void";
-
- im_sig += ", ";
- im_sig += return_type->im_type_out;
- im_sig += " argRet";
+ im_unique_sig += get_arg_unique_sig(*arg_type);
i++;
}
@@ -845,7 +859,15 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
icall_method += "_";
icall_method += itos(method_icalls.size());
- InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_type_out, im_sig, im_unique_sig);
+ InternalCall im_icall = InternalCall(p_itype.api_type, icall_method, im_unique_sig);
+
+ im_icall.is_vararg = imethod.is_vararg;
+ im_icall.is_static = imethod.is_static;
+ im_icall.return_type = imethod.return_type;
+
+ for (const List<ArgumentInterface>::Element *F = imethod.arguments.front(); F; F = F->next()) {
+ im_icall.argument_types.push_back(F->get().type);
+ }
List<InternalCall>::Element *match = method_icalls.find(im_icall);
@@ -859,6 +881,8 @@ void BindingsGenerator::_generate_method_icalls(const TypeInterface &p_itype) {
method_icalls_map.insert(&imethod, &added->get());
}
}
+
+ return OK;
}
void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
@@ -874,7 +898,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array check.</param>\n"); \
p_output.append(INDENT2 "/// <returns>Whether or not the array is empty.</returns>\n"); \
p_output.append(INDENT2 "public static bool IsEmpty(this " #m_type "[] instance)\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
+ p_output.append(OPEN_BLOCK_L2); \
p_output.append(INDENT3 "return instance == null || instance.Length == 0;\n"); \
p_output.append(INDENT2 CLOSE_BLOCK);
@@ -886,7 +910,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
p_output.append(INDENT2 "/// <param name=\"delimiter\">The delimiter to use between items.</param>\n"); \
p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \
p_output.append(INDENT2 "public static string Join(this " #m_type "[] instance, string delimiter = \", \")\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
+ p_output.append(OPEN_BLOCK_L2); \
p_output.append(INDENT3 "return String.Join(delimiter, instance);\n"); \
p_output.append(INDENT2 CLOSE_BLOCK);
@@ -897,7 +921,7 @@ void BindingsGenerator::_generate_array_extensions(StringBuilder &p_output) {
p_output.append(INDENT2 "/// <param name=\"instance\">The " #m_type " array to convert.</param>\n"); \
p_output.append(INDENT2 "/// <returns>A single string with all items.</returns>\n"); \
p_output.append(INDENT2 "public static string Stringify(this " #m_type "[] instance)\n"); \
- p_output.append(INDENT2 OPEN_BLOCK); \
+ p_output.append(OPEN_BLOCK_L2); \
p_output.append(INDENT3 "return \"[\" + instance.Join() + \"]\";\n"); \
p_output.append(INDENT2 CLOSE_BLOCK);
@@ -989,7 +1013,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("\n" INDENT1 "public static partial class ");
p_output.append(enum_class_name);
- p_output.append("\n" INDENT1 OPEN_BLOCK);
+ p_output.append("\n" OPEN_BLOCK_L1);
}
if (ienum.is_flags) {
@@ -999,7 +1023,7 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
p_output.append("\n" INDENT1 "public enum ");
p_output.append(enum_proxy_name);
p_output.append(" : long");
- p_output.append("\n" INDENT1 OPEN_BLOCK);
+ p_output.append("\n" OPEN_BLOCK_L1);
const ConstantInterface &last = ienum.constants.back()->get();
for (const ConstantInterface &iconstant : ienum.constants) {
@@ -1106,41 +1130,39 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
- // Generate sources from compressed files
+ // Generate native calls
StringBuilder cs_icalls_content;
cs_icalls_content.append("using System;\n"
- "using System.Runtime.CompilerServices;\n"
+ "using System.Diagnostics.CodeAnalysis;\n"
+ "using System.Runtime.InteropServices;\n"
+ "using Godot.NativeInterop;\n"
"\n");
cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
+ cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
+ cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
+ cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
+ cs_icalls_content.append("#if NET\n");
+ cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ cs_icalls_content.append("#endif\n");
cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS "\n" INDENT1 "{");
cs_icalls_content.append(MEMBER_BEGIN "internal static ulong godot_api_hash = ");
- cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + ";\n");
- cs_icalls_content.append(MEMBER_BEGIN "internal static uint bindings_version = ");
- cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
- cs_icalls_content.append(MEMBER_BEGIN "internal static uint cs_glue_version = ");
- cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
+ cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_CORE)) + ";\n");
-#define ADD_INTERNAL_CALL(m_icall) \
- if (!m_icall.editor_only) { \
- cs_icalls_content.append(MEMBER_BEGIN "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.append(INDENT2 "internal static extern "); \
- cs_icalls_content.append(m_icall.im_type_out + " "); \
- cs_icalls_content.append(m_icall.name + "("); \
- cs_icalls_content.append(m_icall.im_sig + ");\n"); \
- }
+ cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n");
- for (const InternalCall &internal_call : core_custom_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
- for (const InternalCall &internal_call : method_icalls) {
- ADD_INTERNAL_CALL(internal_call);
+ for (const InternalCall &icall : method_icalls) {
+ if (icall.editor_only) {
+ continue;
+ }
+ Error err = _generate_cs_native_calls(icall, cs_icalls_content);
+ if (err != OK) {
+ return err;
+ }
}
-#undef ADD_INTERNAL_CALL
-
cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
String internal_methods_file = path::join(base_gen_dir, BINDINGS_CLASS_NATIVECALLS ".cs");
@@ -1152,6 +1174,8 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(internal_methods_file);
+ // Generate GeneratedIncludes.props
+
StringBuilder includes_props_content;
includes_props_content.append("<Project>\n"
" <ItemGroup>\n");
@@ -1215,39 +1239,40 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
+ // Generate native calls
+
StringBuilder cs_icalls_content;
cs_icalls_content.append("using System;\n"
- "using System.Runtime.CompilerServices;\n"
+ "using System.Diagnostics.CodeAnalysis;\n"
+ "using System.Runtime.InteropServices;\n"
+ "using Godot.NativeInterop;\n"
"\n");
cs_icalls_content.append("namespace " BINDINGS_NAMESPACE "\n" OPEN_BLOCK);
- cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" INDENT1 OPEN_BLOCK);
+ cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"InconsistentNaming\")]\n");
+ cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantUnsafeContext\")]\n");
+ cs_icalls_content.append(INDENT1 "[SuppressMessage(\"ReSharper\", \"RedundantNameQualifier\")]\n");
+ cs_icalls_content.append("#if NET\n");
+ cs_icalls_content.append(INDENT1 "[System.Runtime.CompilerServices.SkipLocalsInit]\n");
+ cs_icalls_content.append("#endif\n");
+ cs_icalls_content.append(INDENT1 "internal static class " BINDINGS_CLASS_NATIVECALLS_EDITOR "\n" OPEN_BLOCK_L1);
cs_icalls_content.append(INDENT2 "internal static ulong godot_api_hash = ");
- cs_icalls_content.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + ";\n");
- cs_icalls_content.append(INDENT2 "internal static uint bindings_version = ");
- cs_icalls_content.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + ";\n");
- cs_icalls_content.append(INDENT2 "internal static uint cs_glue_version = ");
- cs_icalls_content.append(String::num_uint64(CS_GLUE_VERSION) + ";\n");
- cs_icalls_content.append("\n");
+ cs_icalls_content.append(String::num_uint64(ClassDB::get_api_hash(ClassDB::API_EDITOR)) + ";\n");
-#define ADD_INTERNAL_CALL(m_icall) \
- if (m_icall.editor_only) { \
- cs_icalls_content.append(INDENT2 "[MethodImpl(MethodImplOptions.InternalCall)]\n"); \
- cs_icalls_content.append(INDENT2 "internal static extern "); \
- cs_icalls_content.append(m_icall.im_type_out + " "); \
- cs_icalls_content.append(m_icall.name + "("); \
- cs_icalls_content.append(m_icall.im_sig + ");\n"); \
- }
+ cs_icalls_content.append(MEMBER_BEGIN "private const int VarArgsSpanThreshold = 10;\n");
- for (const InternalCall &internal_call : editor_custom_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
- for (const InternalCall &internal_call : method_icalls) {
- ADD_INTERNAL_CALL(internal_call);
- }
+ cs_icalls_content.append("\n");
-#undef ADD_INTERNAL_CALL
+ for (const InternalCall &icall : method_icalls) {
+ if (!icall.editor_only) {
+ continue;
+ }
+ Error err = _generate_cs_native_calls(icall, cs_icalls_content);
+ if (err != OK) {
+ return err;
+ }
+ }
cs_icalls_content.append(INDENT1 CLOSE_BLOCK CLOSE_BLOCK);
@@ -1260,6 +1285,8 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
compile_items.push_back(internal_methods_file);
+ // Generate GeneratedIncludes.props
+
StringBuilder includes_props_content;
includes_props_content.append("<Project>\n"
" <ItemGroup>\n");
@@ -1343,12 +1370,8 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
CRASH_COND(itype.is_singleton);
}
- List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
-
_log("Generating %s.cs...\n", itype.proxy_name.utf8().get_data());
- String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
-
StringBuilder output;
output.append("using System;\n"); // IntPtr
@@ -1491,53 +1514,96 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
// Add the type name and the singleton pointer as static fields
output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n");
- output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3
- "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5
- "singleton = Engine.GetSingleton(typeof(");
- output.append(itype.proxy_name);
- output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
- output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output << MEMBER_BEGIN "public static Godot.Object " CS_PROPERTY_SINGLETON "\n" INDENT2 "{\n"
+ << INDENT3 "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n"
+ << INDENT5 "singleton = " C_METHOD_ENGINE_GET_SINGLETON "(typeof("
+ << itype.proxy_name
+ << ").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n";
+
+ output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
+ } else {
+ // IMPORTANT: We also generate the static fields for Godot.Object instead of declaring
+ // them manually in the `Object.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).
- output.append(INDENT2 "internal static IntPtr " BINDINGS_PTR_FIELD " = ");
- output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
- output.append("." ICALL_PREFIX);
- output.append(itype.name);
- output.append(SINGLETON_ICALL_SUFFIX "();\n");
- } else if (is_derived_type) {
- // Add member fields
+ // Add native name static field
- output.append(MEMBER_BEGIN "private static StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
+ output.append(MEMBER_BEGIN "private static readonly StringName " BINDINGS_NATIVE_NAME_FIELD " = \"");
output.append(itype.name);
output.append("\";\n");
- // Add default constructor
if (itype.is_instantiable) {
- output.append(MEMBER_BEGIN "public ");
- output.append(itype.proxy_name);
- output.append("() : this(");
- output.append(itype.memory_own ? "true" : "false");
-
- // The default constructor may also be called by the engine when instancing existing native objects
- // The engine will initialize the pointer field of the managed side before calling the constructor
- // This is why we only allocate a new native object from the constructor if the pointer field is not set
- output.append(")\n" OPEN_BLOCK_L2 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" INDENT4 BINDINGS_PTR_FIELD " = ");
- output.append(itype.api_type == ClassDB::API_EDITOR ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS);
- output.append("." + ctor_method);
- output.append("(this);\n" INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2);
- } else {
- // Hide the constructor
+ // Add native constructor static field
+
+ String get_constructor_method = ICALL_CLASSDB_GET_CONSTRUCTOR;
+
+ if (itype.is_singleton) {
+ // Singletons are static classes. They don't derive Godot.Object,
+ // so we need to specify the type to call the static method.
+ get_constructor_method = "Object." + get_constructor_method;
+ }
+
+ output << MEMBER_BEGIN << "[DebuggerBrowsable(DebuggerBrowsableState.Never)]\n"
+
+ << "#if NET\n"
+
+ << INDENT2 "private static unsafe readonly delegate* unmanaged<IntPtr> "
+ << CS_STATIC_FIELD_NATIVE_CTOR " = " << get_constructor_method
+ << "(" BINDINGS_NATIVE_NAME_FIELD ");\n"
+
+ << "#else\n"
+
+ // Get rid of this one once we switch to .NET 5/6
+ << INDENT2 "private static readonly IntPtr " CS_STATIC_FIELD_NATIVE_CTOR
+ << " = " << get_constructor_method << "(" BINDINGS_NATIVE_NAME_FIELD ");\n"
+
+ << "#endif\n";
+ }
+
+ if (is_derived_type) {
+ // Add default constructor
+ if (itype.is_instantiable) {
+ output << MEMBER_BEGIN "public " << itype.proxy_name << "() : this("
+ << (itype.memory_own ? "true" : "false") << ")\n" OPEN_BLOCK_L2;
+
+ // The default constructor may also be called by the engine when instancing existing native objects
+ // The engine will initialize the pointer field of the managed side before calling the constructor
+ // This is why we only allocate a new native object from the constructor if the pointer field is not set
+ output << INDENT3 "if (" BINDINGS_PTR_FIELD " == IntPtr.Zero)\n" OPEN_BLOCK_L3
+
+ << "#if NET\n"
+
+ << INDENT4 "unsafe\n" INDENT4 OPEN_BLOCK
+ << INDENT5 BINDINGS_PTR_FIELD " = " CS_STATIC_FIELD_NATIVE_CTOR "();\n"
+ << CLOSE_BLOCK_L4
+
+ << "#else\n"
+
+ // Get rid of this one once we switch to .NET 5/6
+ << INDENT4 BINDINGS_PTR_FIELD " = _gd__invoke_class_constructor(" CS_STATIC_FIELD_NATIVE_CTOR ");\n"
+
+ << "#endif\n"
+
+ << INDENT4 C_METHOD_TIE_MANAGED_TO_UNMANAGED "(this, " BINDINGS_PTR_FIELD ");\n"
+ << CLOSE_BLOCK_L3
+ << INDENT3 "_InitializeGodotScriptInstanceInternals();\n" CLOSE_BLOCK_L2;
+ } else {
+ // Hide the constructor
+ output.append(MEMBER_BEGIN "internal ");
+ output.append(itype.proxy_name);
+ output.append("() {}\n");
+ }
+
+ // Add.. em.. trick constructor. Sort of.
output.append(MEMBER_BEGIN "internal ");
output.append(itype.proxy_name);
- output.append("() {}\n");
+ output.append("(bool " CS_PARAM_MEMORYOWN ") : base(" CS_PARAM_MEMORYOWN ") {}\n");
}
-
- // Add.. em.. trick constructor. Sort of.
- output.append(MEMBER_BEGIN "internal ");
- output.append(itype.proxy_name);
- output.append("(bool " CS_FIELD_MEMORYOWN ") : base(" CS_FIELD_MEMORYOWN ") {}\n");
}
int method_bind_count = 0;
@@ -1553,22 +1619,6 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
"Failed to generate signal '" + isignal.name + "' for class '" + itype.name + "'.");
}
- if (itype.is_singleton) {
- InternalCall singleton_icall = InternalCall(itype.api_type, ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX, "IntPtr");
-
- if (!find_icall_by_name(singleton_icall.name, custom_icalls)) {
- custom_icalls.push_back(singleton_icall);
- }
- }
-
- if (is_derived_type && itype.is_instantiable) {
- InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
-
- if (!find_icall_by_name(ctor_icall.name, custom_icalls)) {
- custom_icalls.push_back(ctor_icall);
- }
- }
-
output.append(INDENT1 CLOSE_BLOCK /* class */
CLOSE_BLOCK /* namespace */);
@@ -1669,7 +1719,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
p_output.append(prop_cs_type);
p_output.append(" ");
p_output.append(p_iprop.proxy_name);
- p_output.append("\n" INDENT2 OPEN_BLOCK);
+ p_output.append("\n" OPEN_BLOCK_L2);
if (getter) {
p_output.append(INDENT3 "get\n"
@@ -1677,7 +1727,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
"#pragma warning disable CS0618 // Disable warning about obsolete method\n"
- OPEN_BLOCK_L3);
+ OPEN_BLOCK_L3 INDENT4);
p_output.append("return ");
p_output.append(getter->proxy_name + "(");
@@ -1706,7 +1756,7 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
// TODO Remove this once we make accessor methods private/internal (they will no longer be marked as obsolete after that)
"#pragma warning disable CS0618 // Disable warning about obsolete method\n"
- OPEN_BLOCK_L3);
+ OPEN_BLOCK_L3 INDENT4);
p_output.append(setter->proxy_name + "(");
if (p_iprop.index != -1) {
@@ -1734,7 +1784,8 @@ Error BindingsGenerator::_generate_cs_property(const BindingsGenerator::TypeInte
}
Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output) {
- const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
+ const TypeInterface *return_type = _get_type_or_null(p_imethod.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
ERR_FAIL_COND_V_MSG(return_type->is_singleton, ERR_BUG,
"Method return type is a singleton: '" + p_itype.name + "." + p_imethod.name + "'.");
@@ -1745,10 +1796,11 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
"' from the editor API. Core API cannot have dependencies on the editor API.");
}
- String method_bind_field = "__method_bind_" + itos(p_method_bind_count);
+ String method_bind_field = CS_STATIC_FIELD_METHOD_BIND_PREFIX + itos(p_method_bind_count);
String arguments_sig;
- String cs_in_statements;
+ StringBuilder cs_in_statements;
+ bool cs_in_is_unsafe = false;
String icall_params = method_bind_field;
if (!p_imethod.is_static) {
@@ -1760,7 +1812,8 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Retrieve information from the arguments
const ArgumentInterface &first = p_imethod.arguments.front()->get();
for (const ArgumentInterface &iarg : p_imethod.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
"Argument type is a singleton: '" + iarg.name + "' of method '" + p_itype.name + "." + p_imethod.name + "'.");
@@ -1816,24 +1869,20 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String arg_in = iarg.name;
arg_in += "_in";
- cs_in_statements += arg_cs_type;
- cs_in_statements += " ";
- cs_in_statements += arg_in;
- cs_in_statements += " = ";
- cs_in_statements += iarg.name;
+ cs_in_statements << INDENT3 << arg_cs_type << " " << arg_in << " = " << iarg.name;
if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
- cs_in_statements += ".HasValue ? ";
+ cs_in_statements << ".HasValue ? ";
} else {
- cs_in_statements += " != null ? ";
+ cs_in_statements << " != null ? ";
}
- cs_in_statements += iarg.name;
+ cs_in_statements << iarg.name;
if (iarg.def_param_mode == ArgumentInterface::NULLABLE_VAL) {
- cs_in_statements += ".Value : ";
+ cs_in_statements << ".Value : ";
} else {
- cs_in_statements += " : ";
+ cs_in_statements << " : ";
}
String cs_type = arg_cs_type;
@@ -1843,8 +1892,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
String def_arg = sformat(iarg.default_argument, cs_type);
- cs_in_statements += def_arg;
- cs_in_statements += ";\n" INDENT3;
+ cs_in_statements << def_arg << ";\n";
icall_params += arg_type->cs_in.is_empty() ? arg_in : sformat(arg_type->cs_in, arg_in);
@@ -1857,16 +1905,25 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
} else {
icall_params += arg_type->cs_in.is_empty() ? iarg.name : sformat(arg_type->cs_in, iarg.name);
}
+
+ cs_in_is_unsafe |= arg_type->cs_in_is_unsafe;
}
// Generate method
{
if (!p_imethod.is_virtual && !p_imethod.requires_object_call) {
- p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr ");
- p_output.append(method_bind_field);
- p_output.append(" = Object." ICALL_GET_METHODBIND "(" BINDINGS_NATIVE_NAME_FIELD ", \"");
- p_output.append(p_imethod.name);
- p_output.append("\");\n");
+ p_output << MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static readonly IntPtr "
+ << method_bind_field << " = ";
+
+ if (p_itype.is_singleton) {
+ // Singletons are static classes. They don't derive Godot.Object,
+ // so we need to specify the type to call the static method.
+ p_output << "Object.";
+ }
+
+ p_output << ICALL_CLASSDB_GET_METHOD "(" BINDINGS_NATIVE_NAME_FIELD ", \""
+ << p_imethod.name
+ << "\");\n";
}
if (p_imethod.method_doc && p_imethod.method_doc->description.size()) {
@@ -1919,6 +1976,10 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
p_output.append("virtual ");
}
+ if (cs_in_is_unsafe) {
+ p_output.append("unsafe ");
+ }
+
String return_cs_type = return_type->cs_type + _get_generic_type_parameters(*return_type, p_imethod.return_type.generic_type_parameters);
p_output.append(return_cs_type + " ");
@@ -1929,11 +1990,9 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
// Godot virtual method must be overridden, therefore we return a default value by default.
if (return_type->cname == name_cache.type_void) {
- p_output.append("return;\n" CLOSE_BLOCK_L2);
+ p_output.append(CLOSE_BLOCK_L2);
} else {
- p_output.append("return default(");
- p_output.append(return_cs_type);
- p_output.append(");\n" CLOSE_BLOCK_L2);
+ p_output.append(INDENT3 "return default;\n" CLOSE_BLOCK_L2);
}
return OK; // Won't increment method bind count
@@ -1942,7 +2001,7 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
if (p_imethod.requires_object_call) {
// Fallback to Godot's object.Call(string, params)
- p_output.append(CS_METHOD_CALL "(\"");
+ p_output.append(INDENT3 CS_METHOD_CALL "(\"");
p_output.append(p_imethod.name);
p_output.append("\"");
@@ -1966,15 +2025,16 @@ Error BindingsGenerator::_generate_cs_method(const BindingsGenerator::TypeInterf
im_call += im_icall->name;
if (p_imethod.arguments.size()) {
- p_output.append(cs_in_statements);
+ p_output.append(cs_in_statements.as_string());
}
if (return_type->cname == name_cache.type_void) {
- p_output.append(im_call + "(" + icall_params + ");\n");
+ p_output << INDENT3 << im_call << "(" << icall_params << ");\n";
} else if (return_type->cs_out.is_empty()) {
- p_output.append("return " + im_call + "(" + icall_params + ");\n");
+ p_output << INDENT3 "return " << im_call << "(" << icall_params << ");\n";
} else {
- p_output.append(sformat(return_type->cs_out, im_call, icall_params, return_cs_type, return_type->im_type_out));
+ p_output.append(sformat(return_type->cs_out, im_call, icall_params,
+ return_cs_type, return_type->c_type_out, String(), INDENT3));
p_output.append("\n");
}
@@ -1992,7 +2052,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// Retrieve information from the arguments
const ArgumentInterface &first = p_isignal.arguments.front()->get();
for (const ArgumentInterface &iarg : p_isignal.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ const TypeInterface *arg_type = _get_type_or_null(iarg.type);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Argument type not found
ERR_FAIL_COND_V_MSG(arg_type->is_singleton, ERR_BUG,
"Argument type is a singleton: '" + iarg.name + "' of signal '" + p_itype.name + "." + p_isignal.name + "'.");
@@ -2058,7 +2119,8 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
// If so, we could store the pointer we get from `data_unique_pointer()` instead of allocating StringName here.
// Cached signal name (StringName)
- p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN "private static StringName __signal_name_");
+ p_output.append(MEMBER_BEGIN "[DebuggerBrowsable(DebuggerBrowsableState.Never)]" MEMBER_BEGIN
+ "private static readonly StringName " CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
p_output.append(p_isignal.name);
p_output.append(" = \"");
p_output.append(p_isignal.name);
@@ -2075,21 +2137,21 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
p_output.append(delegate_name);
p_output.append(" ");
p_output.append(p_isignal.proxy_name);
- p_output.append("\n" OPEN_BLOCK_L2);
+ p_output.append("\n" OPEN_BLOCK_L2 INDENT3);
if (p_itype.is_singleton) {
- p_output.append("add => Singleton.Connect(__signal_name_");
+ p_output.append("add => " CS_PROPERTY_SINGLETON ".Connect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
} else {
- p_output.append("add => Connect(__signal_name_");
+ p_output.append("add => Connect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
}
p_output.append(p_isignal.name);
p_output.append(", new Callable(value));\n");
if (p_itype.is_singleton) {
- p_output.append(INDENT3 "remove => Singleton.Disconnect(__signal_name_");
+ p_output.append(INDENT3 "remove => " CS_PROPERTY_SINGLETON ".Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
} else {
- p_output.append(INDENT3 "remove => Disconnect(__signal_name_");
+ p_output.append(INDENT3 "remove => Disconnect(" CS_STATIC_FIELD_SIGNAL_NAME_PREFIX);
}
p_output.append(p_isignal.name);
@@ -2100,388 +2162,218 @@ Error BindingsGenerator::_generate_cs_signal(const BindingsGenerator::TypeInterf
return OK;
}
-Error BindingsGenerator::generate_glue(const String &p_output_dir) {
- ERR_FAIL_COND_V(!initialized, ERR_UNCONFIGURED);
-
- bool dir_exists = DirAccess::exists(p_output_dir);
- ERR_FAIL_COND_V_MSG(!dir_exists, ERR_FILE_BAD_PATH, "The output directory does not exist.");
-
- StringBuilder output;
-
- output.append("/* THIS FILE IS GENERATED DO NOT EDIT */\n");
- output.append("#include \"" GLUE_HEADER_FILE "\"\n");
- output.append("\n#ifdef MONO_GLUE_ENABLED\n");
-
- generated_icall_funcs.clear();
-
- for (const KeyValue<StringName, TypeInterface> &type_elem : obj_types) {
- const TypeInterface &itype = type_elem.value;
-
- bool is_derived_type = itype.base_name != StringName();
+Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output) {
+ bool ret_void = p_icall.return_type.cname == name_cache.type_void;
- if (!is_derived_type) {
- // Some Object assertions
- CRASH_COND(itype.cname != name_cache.type_Object);
- CRASH_COND(!itype.is_instantiable);
- CRASH_COND(itype.api_type != ClassDB::API_CORE);
- CRASH_COND(itype.is_singleton);
- }
-
- List<InternalCall> &custom_icalls = itype.api_type == ClassDB::API_EDITOR ? editor_custom_icalls : core_custom_icalls;
-
- OS::get_singleton()->print("Generating %s...\n", itype.name.utf8().get_data());
-
- String ctor_method(ICALL_PREFIX + itype.proxy_name + "_Ctor"); // Used only for derived types
-
- for (const MethodInterface &imethod : itype.methods) {
- Error method_err = _generate_glue_method(itype, imethod, output);
- ERR_FAIL_COND_V_MSG(method_err != OK, method_err,
- "Failed to generate method '" + imethod.name + "' for class '" + itype.name + "'.");
- }
-
- if (itype.is_singleton) {
- String singleton_icall_name = ICALL_PREFIX + itype.name + SINGLETON_ICALL_SUFFIX;
- InternalCall singleton_icall = InternalCall(itype.api_type, singleton_icall_name, "IntPtr");
-
- if (!find_icall_by_name(singleton_icall.name, custom_icalls)) {
- custom_icalls.push_back(singleton_icall);
- }
+ const TypeInterface *return_type = _get_type_or_null(p_icall.return_type);
+ ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
- output.append("Object* ");
- output.append(singleton_icall_name);
- output.append("() " OPEN_BLOCK "\treturn Engine::get_singleton()->get_singleton_object(\"");
- output.append(itype.proxy_name);
- output.append("\");\n" CLOSE_BLOCK "\n");
- }
+ StringBuilder c_func_sig;
+ StringBuilder c_in_statements;
+ StringBuilder c_args_var_content;
- if (is_derived_type && itype.is_instantiable) {
- InternalCall ctor_icall = InternalCall(itype.api_type, ctor_method, "IntPtr", itype.proxy_name + " obj");
+ c_func_sig << "IntPtr " CS_PARAM_METHODBIND;
- if (!find_icall_by_name(ctor_icall.name, custom_icalls)) {
- custom_icalls.push_back(ctor_icall);
- }
-
- output.append("Object* ");
- output.append(ctor_method);
- output.append("(MonoObject* obj) " OPEN_BLOCK
- "\t" C_MACRO_OBJECT_CONSTRUCT "(instance, \"");
- output.append(itype.name);
- output.append("\");\n"
- "\t" C_METHOD_TIE_MANAGED_TO_UNMANAGED "(obj, instance);\n"
- "\treturn instance;\n" CLOSE_BLOCK "\n");
- }
+ if (!p_icall.is_static) {
+ c_func_sig += ", IntPtr " CS_PARAM_INSTANCE;
}
- output.append("namespace GodotSharpBindings\n" OPEN_BLOCK "\n");
-
- output.append("uint64_t get_core_api_hash() { return ");
- output.append(String::num_uint64(GDMono::get_singleton()->get_api_core_hash()) + "U; }\n");
-
- output.append("#ifdef TOOLS_ENABLED\n"
- "uint64_t get_editor_api_hash() { return ");
- output.append(String::num_uint64(GDMono::get_singleton()->get_api_editor_hash()) + "U; }\n");
- output.append("#endif // TOOLS_ENABLED\n");
-
- output.append("uint32_t get_bindings_version() { return ");
- output.append(String::num_uint64(BINDINGS_GENERATOR_VERSION) + "; }\n");
-
- output.append("uint32_t get_cs_glue_version() { return ");
- output.append(String::num_uint64(CS_GLUE_VERSION) + "; }\n");
-
- output.append("\nvoid register_generated_icalls() " OPEN_BLOCK);
- output.append("\tgodot_register_glue_header_icalls();\n");
-
-#define ADD_INTERNAL_CALL_REGISTRATION(m_icall) \
- { \
- output.append("\tGDMonoUtils::add_internal_call("); \
- output.append("\"" BINDINGS_NAMESPACE "."); \
- output.append(m_icall.editor_only ? BINDINGS_CLASS_NATIVECALLS_EDITOR : BINDINGS_CLASS_NATIVECALLS); \
- output.append("::"); \
- output.append(m_icall.name); \
- output.append("\", "); \
- output.append(m_icall.name); \
- output.append(");\n"); \
- }
-
- bool tools_sequence = false;
- for (const InternalCall &internal_call : core_custom_icalls) {
- if (tools_sequence) {
- if (!internal_call.editor_only) {
- tools_sequence = false;
- output.append("#endif\n");
- }
- } else {
- if (internal_call.editor_only) {
- output.append("#ifdef TOOLS_ENABLED\n");
- tools_sequence = true;
- }
- }
-
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
- }
-
- if (tools_sequence) {
- tools_sequence = false;
- output.append("#endif\n");
- }
-
- output.append("#ifdef TOOLS_ENABLED\n");
- for (const InternalCall &internal_call : editor_custom_icalls) {
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
- }
- output.append("#endif // TOOLS_ENABLED\n");
-
- for (const InternalCall &internal_call : method_icalls) {
- if (tools_sequence) {
- if (!internal_call.editor_only) {
- tools_sequence = false;
- output.append("#endif\n");
- }
- } else {
- if (internal_call.editor_only) {
- output.append("#ifdef TOOLS_ENABLED\n");
- tools_sequence = true;
- }
- }
-
- ADD_INTERNAL_CALL_REGISTRATION(internal_call);
- }
-
- if (tools_sequence) {
- tools_sequence = false;
- output.append("#endif\n");
- }
-
-#undef ADD_INTERNAL_CALL_REGISTRATION
-
- output.append(CLOSE_BLOCK "\n} // namespace GodotSharpBindings\n");
-
- output.append("\n#endif // MONO_GLUE_ENABLED\n");
-
- Error save_err = _save_file(path::join(p_output_dir, "mono_glue.gen.cpp"), output);
- if (save_err != OK) {
- return save_err;
- }
-
- OS::get_singleton()->print("Mono glue generated successfully\n");
-
- return OK;
-}
-
-uint32_t BindingsGenerator::get_version() {
- return BINDINGS_GENERATOR_VERSION;
-}
-
-Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
- Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE);
- ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'.");
-
- file->store_string(p_content.as_string());
-
- return OK;
-}
-
-Error BindingsGenerator::_generate_glue_method(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::MethodInterface &p_imethod, StringBuilder &p_output) {
- if (p_imethod.is_virtual) {
- return OK; // Ignore
- }
-
- bool ret_void = p_imethod.return_type.cname == name_cache.type_void;
-
- const TypeInterface *return_type = _get_type_or_placeholder(p_imethod.return_type);
-
- String argc_str = itos(p_imethod.arguments.size());
-
- String c_func_sig = "MethodBind* " CS_PARAM_METHODBIND;
- if (!p_imethod.is_static) {
- c_func_sig += ", " + p_itype.c_type_in + " " CS_PARAM_INSTANCE;
- }
- String c_in_statements;
- String c_args_var_content;
-
// Get arguments information
int i = 0;
- for (const ArgumentInterface &iarg : p_imethod.arguments) {
- const TypeInterface *arg_type = _get_type_or_placeholder(iarg.type);
+ for (const TypeReference &arg_type_ref : p_icall.argument_types) {
+ const TypeInterface *arg_type = _get_type_or_null(arg_type_ref);
+ ERR_FAIL_NULL_V(arg_type, ERR_BUG); // Return type not found
String c_param_name = "arg" + itos(i + 1);
- if (p_imethod.is_vararg) {
- if (i < p_imethod.arguments.size() - 1) {
- c_in_statements += sformat(arg_type->c_in.size() ? arg_type->c_in : TypeInterface::DEFAULT_VARARG_C_IN, "Variant", c_param_name);
- c_in_statements += "\t" C_LOCAL_PTRCALL_ARGS ".set(";
- c_in_statements += itos(i);
- c_in_statements += sformat(", &%s_in);\n", c_param_name);
+ if (p_icall.is_vararg) {
+ if (i < p_icall.get_arguments_count() - 1) {
+ string c_in_vararg = arg_type->c_in_vararg;
+
+ if (arg_type->is_object_type) {
+ c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObject(%1);\n";
+ }
+
+ ERR_FAIL_COND_V_MSG(c_in_vararg.is_empty(), ERR_BUG,
+ "VarArg support not implemented for parameter type: " + arg_type->name);
+
+ c_in_statements
+ << sformat(c_in_vararg, return_type->c_type, c_param_name,
+ String(), String(), String(), INDENT4)
+ << INDENT4 C_LOCAL_PTRCALL_ARGS "[" << itos(i)
+ << "] = new IntPtr(&" << c_param_name << "_in);\n";
}
} else {
if (i > 0) {
- c_args_var_content += ", ";
+ c_args_var_content << ", ";
}
if (arg_type->c_in.size()) {
- c_in_statements += sformat(arg_type->c_in, arg_type->c_type, c_param_name);
+ c_in_statements << sformat(arg_type->c_in, arg_type->c_type, c_param_name,
+ String(), String(), String(), INDENT3);
}
- c_args_var_content += sformat(arg_type->c_arg_in, c_param_name);
+ c_args_var_content << sformat(arg_type->c_arg_in, c_param_name);
}
- c_func_sig += ", ";
- c_func_sig += arg_type->c_type_in;
- c_func_sig += " ";
- c_func_sig += c_param_name;
+ c_func_sig << ", " << arg_type->c_type_in << " " << c_param_name;
i++;
}
- if (return_type->ret_as_byref_arg) {
- c_func_sig += ", ";
- c_func_sig += return_type->c_type_in;
- c_func_sig += " ";
- c_func_sig += "arg_ret";
-
- i++;
- }
+ String icall_method = p_icall.name;
- HashMap<const MethodInterface *, const InternalCall *>::ConstIterator match = method_icalls_map.find(&p_imethod);
- ERR_FAIL_NULL_V(match, ERR_BUG);
+ // Generate icall function
- const InternalCall *im_icall = match->value;
- String icall_method = im_icall->name;
+ r_output << MEMBER_BEGIN "internal static unsafe " << (ret_void ? "void" : return_type->c_type_out) << " "
+ << icall_method << "(" << c_func_sig.as_string() << ") " OPEN_BLOCK;
- if (!generated_icall_funcs.find(im_icall)) {
- generated_icall_funcs.push_back(im_icall);
+ if (!ret_void && (!p_icall.is_vararg || return_type->cname != name_cache.type_Variant)) {
+ String ptrcall_return_type;
+ String initialization;
- if (im_icall->editor_only) {
- p_output.append("#ifdef TOOLS_ENABLED\n");
+ if (return_type->is_object_type) {
+ ptrcall_return_type = return_type->is_ref_counted ? "godot_ref" : return_type->c_type;
+ initialization = " = default";
+ } else {
+ ptrcall_return_type = return_type->c_type;
}
- // Generate icall function
+ r_output << INDENT3;
- p_output.append((ret_void || return_type->ret_as_byref_arg) ? "void " : return_type->c_type_out + " ");
- p_output.append(icall_method);
- p_output.append("(");
- p_output.append(c_func_sig);
- p_output.append(") " OPEN_BLOCK);
+ if (return_type->is_ref_counted || return_type->c_type_is_disposable_struct) {
+ r_output << "using ";
- if (!ret_void) {
- String ptrcall_return_type;
- String initialization;
-
- if (p_imethod.is_vararg && return_type->cname != name_cache.type_Variant) {
- // VarArg methods always return Variant, but there are some cases in which MethodInfo provides
- // a specific return type. We trust this information is valid. We need a temporary local to keep
- // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
- // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
- // Alternatively, we could just return Variant, but that would result in a worse API.
- p_output.append("\tVariant " C_LOCAL_VARARG_RET ";\n");
+ if (initialization.is_empty()) {
+ initialization = " = default";
}
+ } else if (return_type->c_ret_needs_default_initialization) {
+ initialization = " = default";
+ }
- if (return_type->is_object_type) {
- ptrcall_return_type = return_type->is_ref_counted ? "Ref<RefCounted>" : return_type->c_type;
- initialization = return_type->is_ref_counted ? "" : " = nullptr";
- } else {
- ptrcall_return_type = return_type->c_type;
- }
+ r_output << ptrcall_return_type << " " C_LOCAL_RET << initialization << ";\n";
+ }
- p_output.append("\t" + ptrcall_return_type);
- p_output.append(" " C_LOCAL_RET);
- p_output.append(initialization + ";\n");
+ if (!p_icall.is_static) {
+ r_output << INDENT3 "if (" CS_PARAM_INSTANCE " == IntPtr.Zero)\n"
+ << INDENT4 "throw new ArgumentNullException(nameof(" CS_PARAM_INSTANCE "));\n";
+ }
- String fail_ret = return_type->c_type_out.ends_with("*") && !return_type->ret_as_byref_arg ? "nullptr" : return_type->c_type_out + "()";
+ String argc_str = itos(p_icall.get_arguments_count());
- if (!p_imethod.is_static) {
- if (return_type->ret_as_byref_arg) {
- p_output.append("\tif (" CS_PARAM_INSTANCE " == nullptr) { *arg_ret = ");
- p_output.append(fail_ret);
- p_output.append("; ERR_FAIL_MSG(\"Parameter ' " CS_PARAM_INSTANCE " ' is null.\"); }\n");
- } else {
- p_output.append("\tERR_FAIL_NULL_V(" CS_PARAM_INSTANCE ", ");
- p_output.append(fail_ret);
- p_output.append(");\n");
- }
- }
- } else {
- if (!p_imethod.is_static) {
- p_output.append("\tERR_FAIL_NULL(" CS_PARAM_INSTANCE ");\n");
- }
- }
+ auto generate_call_and_return_stmts = [&](const char *base_indent) {
+ if (p_icall.is_vararg) {
+ r_output << base_indent << "godot_variant_call_error vcall_error;\n";
- if (p_imethod.arguments.size()) {
- if (p_imethod.is_vararg) {
- String vararg_arg = "arg" + argc_str;
- String real_argc_str = itos(p_imethod.arguments.size() - 1); // Arguments count without vararg
-
- p_output.append("\tint vararg_length = mono_array_length(");
- p_output.append(vararg_arg);
- p_output.append(");\n\tint total_length = ");
- p_output.append(real_argc_str);
- p_output.append(" + vararg_length;\n"
- "\tArgumentsVector<Variant> varargs(vararg_length);\n"
- "\tArgumentsVector<const Variant *> " C_LOCAL_PTRCALL_ARGS "(total_length);\n");
- p_output.append(c_in_statements);
- p_output.append("\tfor (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
- "\t\tMonoObject* elem = mono_array_get(");
- p_output.append(vararg_arg);
- p_output.append(", MonoObject*, i);\n"
- "\t\tvarargs.set(i, GDMonoMarshal::mono_object_to_variant(elem));\n"
- "\t\t" C_LOCAL_PTRCALL_ARGS ".set(");
- p_output.append(real_argc_str);
- p_output.append(" + i, &varargs.get(i));\n\t" CLOSE_BLOCK);
- } else {
- p_output.append(c_in_statements);
- p_output.append("\tconst void* " C_LOCAL_PTRCALL_ARGS "[");
- p_output.append(argc_str + "] = { ");
- p_output.append(c_args_var_content + " };\n");
- }
- }
+ // MethodBind Call
+ r_output << base_indent;
- if (p_imethod.is_vararg) {
- p_output.append("\tCallable::CallError vcall_error;\n\t");
+ // VarArg methods always return Variant, but there are some cases in which MethodInfo provides
+ // a specific return type. We trust this information is valid. We need a temporary local to keep
+ // the Variant alive until the method returns. Otherwise, if the returned Variant holds a RefPtr,
+ // it could be deleted too early. This is the case with GDScript.new() which returns OBJECT.
+ // Alternatively, we could just return Variant, but that would result in a worse API.
if (!ret_void) {
- // See the comment on the C_LOCAL_VARARG_RET declaration
if (return_type->cname != name_cache.type_Variant) {
- p_output.append(C_LOCAL_VARARG_RET " = ");
+ r_output << "using godot_variant " << C_LOCAL_VARARG_RET " = ";
} else {
- p_output.append(C_LOCAL_RET " = ");
+ r_output << "using godot_variant " << C_LOCAL_RET " = ";
}
}
- p_output.append(CS_PARAM_METHODBIND "->call(");
- p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE);
- p_output.append(", ");
- p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ".ptr()" : "nullptr");
- p_output.append(", total_length, vcall_error);\n");
+ r_output << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_call("
+ << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
+ << ", " << (p_icall.get_arguments_count() ? "(godot_variant**)" C_LOCAL_PTRCALL_ARGS : "null")
+ << ", total_length, &vcall_error);\n";
if (!ret_void) {
- // See the comment on the C_LOCAL_VARARG_RET declaration
if (return_type->cname != name_cache.type_Variant) {
- p_output.append("\t" C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n");
+ if (return_type->cname == name_cache.enum_Error) {
+ r_output << base_indent << C_LOCAL_RET " = VariantUtils.ConvertToInt64(&" C_LOCAL_VARARG_RET ");\n";
+ } else {
+ // TODO: Use something similar to c_in_vararg (see usage above, with error if not implemented)
+ CRASH_NOW_MSG("Custom VarArg return type not implemented: " + return_type->name);
+ r_output << base_indent << C_LOCAL_RET " = " C_LOCAL_VARARG_RET ";\n";
+ }
}
}
} else {
- p_output.append("\t" CS_PARAM_METHODBIND "->ptrcall(");
- p_output.append(p_imethod.is_static ? "nullptr" : CS_PARAM_INSTANCE);
- p_output.append(", ");
- p_output.append(p_imethod.arguments.size() ? C_LOCAL_PTRCALL_ARGS ", " : "nullptr, ");
- p_output.append(!ret_void ? "&" C_LOCAL_RET ");\n" : "nullptr);\n");
+ // MethodBind PtrCall
+ r_output << base_indent << C_CLASS_NATIVE_FUNCS ".godotsharp_method_bind_ptrcall("
+ << CS_PARAM_METHODBIND ", " << (p_icall.is_static ? "IntPtr.Zero" : CS_PARAM_INSTANCE)
+ << ", " << (p_icall.get_arguments_count() ? C_LOCAL_PTRCALL_ARGS : "null")
+ << ", " << (!ret_void ? "&" C_LOCAL_RET ");\n" : "null);\n");
}
+ // Return statement
+
if (!ret_void) {
if (return_type->c_out.is_empty()) {
- p_output.append("\treturn " C_LOCAL_RET ";\n");
- } else if (return_type->ret_as_byref_arg) {
- p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name, "arg_ret"));
+ r_output << base_indent << "return " C_LOCAL_RET ";\n";
} else {
- p_output.append(sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET, return_type->name));
+ r_output << sformat(return_type->c_out, return_type->c_type_out, C_LOCAL_RET,
+ return_type->name, String(), String(), base_indent);
}
}
+ };
+
+ if (p_icall.get_arguments_count()) {
+ if (p_icall.is_vararg) {
+ String vararg_arg = "arg" + argc_str;
+ String real_argc_str = itos(p_icall.get_arguments_count() - 1); // Arguments count without vararg
+
+ p_icall.get_arguments_count();
+
+ r_output << INDENT3 "int vararg_length = " << vararg_arg << ".Length;\n"
+ << INDENT3 "int total_length = " << real_argc_str << " + vararg_length;\n";
+
+ r_output << INDENT3 "Span<godot_variant> varargs_span = vararg_length <= VarArgsSpanThreshold ?\n"
+ << INDENT4 "stackalloc godot_variant[VarArgsSpanThreshold].Cleared() :\n"
+ << INDENT4 "new godot_variant[vararg_length];\n";
+
+ r_output << INDENT3 "Span<IntPtr> " C_LOCAL_PTRCALL_ARGS "_span = total_length <= VarArgsSpanThreshold ?\n"
+ << INDENT4 "stackalloc IntPtr[VarArgsSpanThreshold] :\n"
+ << INDENT4 "new IntPtr[total_length];\n";
+
+ r_output << INDENT3 "using var variantSpanDisposer = new VariantSpanDisposer(varargs_span);\n";
+
+ r_output << INDENT3 "fixed (godot_variant* varargs = &MemoryMarshal.GetReference(varargs_span))\n"
+ << INDENT3 "fixed (IntPtr* " C_LOCAL_PTRCALL_ARGS " = "
+ "&MemoryMarshal.GetReference(" C_LOCAL_PTRCALL_ARGS "_span))\n"
+ << OPEN_BLOCK_L3;
+
+ r_output << c_in_statements.as_string();
- p_output.append(CLOSE_BLOCK "\n");
+ r_output << INDENT4 "for (int i = 0; i < vararg_length; i++) " OPEN_BLOCK
+ << INDENT5 "varargs[i] = " C_METHOD_MANAGED_TO_VARIANT "(" << vararg_arg << "[i]);\n"
+ << INDENT5 C_LOCAL_PTRCALL_ARGS "[" << real_argc_str << " + i] = new IntPtr(&varargs[i]);\n"
+ << CLOSE_BLOCK_L4;
- if (im_icall->editor_only) {
- p_output.append("#endif // TOOLS_ENABLED\n");
+ generate_call_and_return_stmts(INDENT4);
+
+ r_output << CLOSE_BLOCK_L3;
+ } else {
+ r_output << c_in_statements.as_string();
+
+ r_output << INDENT3 "void** " C_LOCAL_PTRCALL_ARGS " = stackalloc void*["
+ << argc_str << "] { " << c_args_var_content.as_string() << " };\n";
+
+ generate_call_and_return_stmts(INDENT3);
}
+ } else {
+ generate_call_and_return_stmts(INDENT3);
}
+ r_output << CLOSE_BLOCK_L2;
+
+ return OK;
+}
+
+Error BindingsGenerator::_save_file(const String &p_path, const StringBuilder &p_content) {
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE);
+ ERR_FAIL_COND_V_MSG(file.is_null(), ERR_FILE_CANT_WRITE, "Cannot open file: '" + p_path + "'.");
+
+ file->store_string(p_content.as_string());
+
return OK;
}
@@ -2514,27 +2406,6 @@ const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_null(con
return nullptr;
}
-const BindingsGenerator::TypeInterface *BindingsGenerator::_get_type_or_placeholder(const TypeReference &p_typeref) {
- const TypeInterface *found = _get_type_or_null(p_typeref);
-
- if (found) {
- return found;
- }
-
- ERR_PRINT(String() + "Type not found. Creating placeholder: '" + p_typeref.cname.operator String() + "'.");
-
- HashMap<StringName, TypeInterface>::ConstIterator match = placeholder_types.find(p_typeref.cname);
-
- if (match) {
- return &match->value;
- }
-
- TypeInterface placeholder;
- TypeInterface::create_placeholder_type(placeholder, p_typeref.cname);
-
- return &placeholder_types.insert(placeholder.cname, placeholder)->value;
-}
-
const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters) {
if (p_generic_type_parameters.is_empty()) {
return "";
@@ -2548,7 +2419,8 @@ const String BindingsGenerator::_get_generic_type_parameters(const TypeInterface
int i = 0;
String params = "<";
for (const TypeReference &param_type : p_generic_type_parameters) {
- const TypeInterface *param_itype = _get_type_or_placeholder(param_type);
+ const TypeInterface *param_itype = _get_type_or_null(param_type);
+ ERR_FAIL_NULL_V(param_itype, "");
ERR_FAIL_COND_V_MSG(param_itype->is_singleton, "",
"Generic type parameter is a singleton: '" + param_itype->name + "'.");
@@ -2666,8 +2538,6 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
case Variant::RECT2:
case Variant::VECTOR3:
case Variant::RID:
- case Variant::ARRAY:
- case Variant::DICTIONARY:
case Variant::PACKED_BYTE_ARRAY:
case Variant::PACKED_INT32_ARRAY:
case Variant::PACKED_INT64_ARRAY:
@@ -2680,6 +2550,10 @@ bool BindingsGenerator::_arg_default_value_is_assignable_to_type(const Variant &
case Variant::CALLABLE:
case Variant::SIGNAL:
return p_arg_type.name == Variant::get_type_name(p_val.get_type());
+ case Variant::ARRAY:
+ return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Array_generic;
+ case Variant::DICTIONARY:
+ return p_arg_type.name == Variant::get_type_name(p_val.get_type()) || p_arg_type.cname == name_cache.type_Dictionary_generic;
case Variant::OBJECT:
return p_arg_type.is_object_type;
case Variant::VECTOR2I:
@@ -2744,18 +2618,24 @@ 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;
- itype.c_out = "\treturn ";
+ itype.c_out = "%5return ";
itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED;
- itype.c_out += itype.is_ref_counted ? "(%1.ptr());\n" : "(%1);\n";
+ itype.c_out += itype.is_ref_counted ? "(%1._reference);\n" : "(%1);\n";
- itype.cs_in = itype.is_singleton ? BINDINGS_PTR_FIELD : "Object." CS_SMETHOD_GETINSTANCE "(%0)";
+ itype.cs_type = itype.proxy_name;
+
+ if (itype.is_singleton) {
+ itype.cs_in = "Object." CS_STATIC_METHOD_GETINSTANCE "(" CS_PROPERTY_SINGLETON ")";
+ } else {
+ itype.cs_in = "Object." CS_STATIC_METHOD_GETINSTANCE "(%0)";
+ }
- itype.c_type = "Object*";
+ itype.cs_out = "%5return (%2)%0(%1);";
+
+ itype.c_arg_in = "(void*)%s";
+ itype.c_type = "IntPtr";
itype.c_type_in = itype.c_type;
- itype.c_type_out = "MonoObject*";
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = "IntPtr";
- itype.im_type_out = itype.proxy_name;
+ itype.c_type_out = "Object";
// Populate properties
@@ -2887,7 +2767,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." +
" Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'.");
} else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) {
- imethod.return_type.cname = Variant::get_type_name(return_info.type);
+ imethod.return_type.cname = Variant::get_type_name(return_info.type) + "_@generic";
imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string));
} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
imethod.return_type.cname = return_info.hint_string;
@@ -2919,7 +2799,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
} else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
- iarg.type.cname = Variant::get_type_name(arginfo.type);
+ iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic";
iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
@@ -3027,7 +2907,7 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
} else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
- iarg.type.cname = Variant::get_type_name(arginfo.type);
+ iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic";
iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
@@ -3246,7 +3126,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
ERR_FAIL_COND_V_MSG(!p_val.is_zero(), false,
"Parameter of type '" + String(r_iarg.type.cname) + "' can only have null/zero as the default value.");
- r_iarg.default_argument = "null";
+ r_iarg.default_argument = "default";
break;
case Variant::ARRAY:
r_iarg.default_argument = "new %s { }";
@@ -3325,7 +3205,7 @@ bool BindingsGenerator::_arg_default_value_from_variant(const Variant &p_val, Ar
r_iarg.default_argument = "default";
break;
default:
- CRASH_NOW_MSG("Unexpected Variant type: " + itos(p_val.get_type()));
+ ERR_FAIL_V_MSG(false, "Unexpected Variant type: " + itos(p_val.get_type()));
break;
}
@@ -3341,20 +3221,14 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
TypeInterface itype;
-#define INSERT_STRUCT_TYPE(m_type) \
- { \
- itype = TypeInterface::create_value_type(String(#m_type)); \
- itype.c_in = "\t%0 %1_in = MARSHALLED_IN(" #m_type ", %1);\n"; \
- itype.c_out = "\t*%3 = MARSHALLED_OUT(" #m_type ", %1);\n"; \
- itype.c_arg_in = "&%s_in"; \
- itype.c_type_in = "GDMonoMarshal::M_" #m_type "*"; \
- itype.c_type_out = "GDMonoMarshal::M_" #m_type; \
- itype.cs_in = "ref %s"; \
- /* in cs_out, im_type_out (%3) includes the 'out ' part */ \
- itype.cs_out = "%0(%1, %3 argRet); return argRet;"; \
- itype.im_type_out = "out " + itype.cs_type; \
- itype.ret_as_byref_arg = true; \
- builtin_types.insert(itype.cname, itype); \
+#define INSERT_STRUCT_TYPE(m_type) \
+ { \
+ itype = TypeInterface::create_value_type(String(#m_type)); \
+ itype.c_type_in = #m_type "*"; \
+ itype.c_type_out = itype.cs_type; \
+ itype.cs_in = "&%s"; \
+ itype.cs_in_is_unsafe = true; \
+ builtin_types.insert(itype.cname, itype); \
}
INSERT_STRUCT_TYPE(Vector2)
@@ -3370,56 +3244,56 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
INSERT_STRUCT_TYPE(AABB)
INSERT_STRUCT_TYPE(Color)
INSERT_STRUCT_TYPE(Plane)
+ INSERT_STRUCT_TYPE(Vector4)
+ INSERT_STRUCT_TYPE(Vector4i)
+ INSERT_STRUCT_TYPE(Projection)
#undef INSERT_STRUCT_TYPE
// bool
itype = TypeInterface::create_value_type(String("bool"));
- {
- // MonoBoolean <---> bool
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
- itype.c_type = "bool";
- itype.c_type_in = "MonoBoolean";
- itype.c_type_out = itype.c_type_in;
- itype.c_arg_in = "&%s_in";
- }
- itype.im_type_in = itype.name;
- itype.im_type_out = itype.name;
+ itype.c_type = "godot_bool";
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.c_type;
+ itype.c_arg_in = "&%s";
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromBool(%1);\n";
builtin_types.insert(itype.cname, itype);
// Integer types
{
// C interface for 'uint32_t' is the same as that of enums. Remember to apply
// any of the changes done here to 'TypeInterface::postsetup_enum_type' as well.
-#define INSERT_INT_TYPE(m_name, m_c_type_in_out, m_c_type) \
- { \
- itype = TypeInterface::create_value_type(String(m_name)); \
- { \
- itype.c_in = "\t%0 %1_in = (%0)%1;\n"; \
- itype.c_out = "\treturn (%0)%1;\n"; \
- itype.c_type = #m_c_type; \
- itype.c_arg_in = "&%s_in"; \
- } \
- itype.c_type_in = #m_c_type_in_out; \
- itype.c_type_out = itype.c_type_in; \
- itype.im_type_in = itype.name; \
- itype.im_type_out = itype.name; \
- builtin_types.insert(itype.cname, itype); \
+#define INSERT_INT_TYPE(m_name) \
+ { \
+ itype = TypeInterface::create_value_type(String(m_name)); \
+ if (itype.name != "long" && itype.name != "ulong") { \
+ itype.c_in = "%5%0 %1_in = %1;\n"; \
+ itype.c_out = "%5return (%0)%1;\n"; \
+ itype.c_type = "long"; \
+ itype.c_arg_in = "&%s_in"; \
+ } else { \
+ itype.c_arg_in = "&%s"; \
+ } \
+ itype.c_type_in = itype.name; \
+ itype.c_type_out = itype.name; \
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromInt(%1);\n"; \
+ builtin_types.insert(itype.cname, itype); \
}
// The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
- INSERT_INT_TYPE("sbyte", int8_t, int64_t);
- INSERT_INT_TYPE("short", int16_t, int64_t);
- INSERT_INT_TYPE("int", int32_t, int64_t);
- INSERT_INT_TYPE("long", int64_t, int64_t);
- INSERT_INT_TYPE("byte", uint8_t, int64_t);
- INSERT_INT_TYPE("ushort", uint16_t, int64_t);
- INSERT_INT_TYPE("uint", uint32_t, int64_t);
- INSERT_INT_TYPE("ulong", uint64_t, int64_t);
+ INSERT_INT_TYPE("sbyte");
+ INSERT_INT_TYPE("short");
+ INSERT_INT_TYPE("int");
+ INSERT_INT_TYPE("long");
+ INSERT_INT_TYPE("byte");
+ INSERT_INT_TYPE("ushort");
+ INSERT_INT_TYPE("uint");
+ INSERT_INT_TYPE("ulong");
}
+#undef INSERT_INT_TYPE
+
// Floating point types
{
// float
@@ -3427,18 +3301,17 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "float";
itype.cname = itype.name;
itype.proxy_name = "float";
+ itype.cs_type = itype.proxy_name;
{
// The expected type for 'float' in ptrcall is 'double'
- itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- itype.c_out = "\treturn (%0)%1;\n";
+ itype.c_in = "%5%0 %1_in = %1;\n";
+ itype.c_out = "%5return (%0)%1;\n";
itype.c_type = "double";
- itype.c_type_in = "float";
- itype.c_type_out = "float";
itype.c_arg_in = "&%s_in";
}
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_type_in = itype.proxy_name;
+ itype.c_type_out = itype.proxy_name;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
builtin_types.insert(itype.cname, itype);
// double
@@ -3446,15 +3319,12 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "double";
itype.cname = itype.name;
itype.proxy_name = "double";
- {
- itype.c_type = "double";
- itype.c_type_in = "double";
- itype.c_type_out = "double";
- itype.c_arg_in = "&%s";
- }
itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_type = "double";
+ itype.c_arg_in = "&%s";
+ itype.c_type_in = itype.proxy_name;
+ itype.c_type_out = itype.proxy_name;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromFloat(%1);\n";
builtin_types.insert(itype.cname, itype);
}
@@ -3463,15 +3333,15 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "String";
itype.cname = itype.name;
itype.proxy_name = "string";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n";
- itype.c_out = "\treturn " C_METHOD_MONOSTR_FROM_GODOT "(%1);\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = itype.name;
- itype.c_type_in = "MonoString*";
- itype.c_type_out = "MonoString*";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOSTR_TO_GODOT "(%1);\n";
+ itype.c_out = "%5return " C_METHOD_MONOSTR_FROM_GODOT "(&%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_string";
+ itype.c_type_in = itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue(" C_METHOD_MONOSTR_TO_GODOT "(%1));\n";
builtin_types.insert(itype.cname, itype);
// StringName
@@ -3479,17 +3349,18 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "StringName";
itype.cname = itype.name;
itype.proxy_name = "StringName";
- itype.c_in = "\t%0 %1_in = %1 ? *%1 : StringName();\n";
- itype.c_out = "\treturn memnew(StringName(%1));\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = itype.name;
- itype.c_type_in = itype.c_type + "*";
- itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.cs_in = "StringName." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.cs_in = "ref %0.NativeValue";
+ // Cannot pass null StringName to ptrcall
+ itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_string_name_new_copy(%1);\n";
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_string_name";
+ itype.c_type_in = "ref " + itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(ref %1);\n";
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
builtin_types.insert(itype.cname, itype);
// NodePath
@@ -3497,15 +3368,17 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "NodePath";
itype.cname = itype.name;
itype.proxy_name = "NodePath";
- itype.c_out = "\treturn memnew(NodePath(%1));\n";
- itype.c_type = itype.name;
- itype.c_type_in = itype.c_type + "*";
- itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.cs_in = "NodePath." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.cs_in = "ref %0.NativeValue";
+ // Cannot pass null NodePath to ptrcall
+ itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_node_path_new_copy(%1);\n";
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_node_path";
+ itype.c_type_in = "ref " + itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
builtin_types.insert(itype.cname, itype);
// RID
@@ -3513,15 +3386,11 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "RID";
itype.cname = itype.name;
itype.proxy_name = "RID";
- itype.c_out = "\treturn memnew(RID(%1));\n";
- itype.c_type = itype.name;
- itype.c_type_in = itype.c_type + "*";
- itype.c_type_out = itype.c_type + "*";
itype.cs_type = itype.proxy_name;
- itype.cs_in = "RID." CS_SMETHOD_GETINSTANCE "(%0)";
- itype.cs_out = "return new %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.c_arg_in = "&%s";
+ itype.c_type = itype.cs_type;
+ itype.c_type_in = itype.c_type;
+ itype.c_type_out = itype.c_type;
builtin_types.insert(itype.cname, itype);
// Variant
@@ -3529,29 +3398,26 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Variant";
itype.cname = itype.name;
itype.proxy_name = "object";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n";
- itype.c_out = "\treturn " C_METHOD_MANAGED_FROM_VARIANT "(%1);\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = itype.name;
- itype.c_type_in = "MonoObject*";
- itype.c_type_out = "MonoObject*";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "object";
- itype.im_type_out = itype.proxy_name;
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_VARIANT "(%1);\n";
+ itype.c_out = "%5return " C_METHOD_MANAGED_FROM_VARIANT "(&%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_variant";
+ itype.c_type_in = itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
builtin_types.insert(itype.cname, itype);
// Callable
itype = TypeInterface::create_value_type(String("Callable"));
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(*%1);\n";
- itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_CALLABLE "(%1);\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type_in = "GDMonoMarshal::M_Callable*";
- itype.c_type_out = "GDMonoMarshal::M_Callable";
itype.cs_in = "ref %s";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
- itype.im_type_out = "out " + itype.cs_type;
- itype.ret_as_byref_arg = true;
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(ref %1);\n";
+ itype.c_out = "%5return " C_METHOD_MANAGED_FROM_CALLABLE "(&%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_callable";
+ itype.c_type_in = "ref " + itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
builtin_types.insert(itype.cname, itype);
// Signal
@@ -3559,19 +3425,15 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "Signal";
itype.cname = itype.name;
itype.proxy_name = "SignalInfo";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(*%1);\n";
- itype.c_out = "\t*%3 = " C_METHOD_MANAGED_FROM_SIGNAL "(%1);\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = itype.name;
- itype.c_type_in = "GDMonoMarshal::M_SignalInfo*";
- itype.c_type_out = "GDMonoMarshal::M_SignalInfo";
- itype.cs_in = "ref %s";
- /* in cs_out, im_type_out (%3) includes the 'out ' part */
- itype.cs_out = "%0(%1, %3 argRet); return argRet;";
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "ref " + itype.cs_type;
- itype.im_type_out = "out " + itype.cs_type;
- itype.ret_as_byref_arg = true;
+ itype.cs_in = "ref %s";
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(ref %1);\n";
+ itype.c_out = "%5return " C_METHOD_MANAGED_FROM_SIGNAL "(&%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_signal";
+ itype.c_type_in = "ref " + itype.cs_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = true;
builtin_types.insert(itype.cname, itype);
// VarArg (fictitious type to represent variable arguments)
@@ -3579,46 +3441,44 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "VarArg";
itype.cname = itype.name;
itype.proxy_name = "object[]";
- itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(Array) "(%1);\n";
- itype.c_arg_in = "&%s_in";
- itype.c_type = "Array";
- itype.c_type_in = "MonoArray*";
itype.cs_type = "params object[]";
- itype.im_type_in = "object[]";
+ // c_type, c_in and c_arg_in are hard-coded in the generator.
+ // c_out and c_type_out are not applicable to VarArg.
+ itype.c_arg_in = "&%s_in";
+ itype.c_type_in = "object[]";
builtin_types.insert(itype.cname, itype);
-#define INSERT_ARRAY_FULL(m_name, m_type, m_proxy_t) \
- { \
- itype = TypeInterface(); \
- itype.name = #m_name; \
- itype.cname = itype.name; \
- itype.proxy_name = #m_proxy_t "[]"; \
- itype.c_in = "\t%0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \
- itype.c_out = "\treturn " C_METHOD_MONOARRAY_FROM(m_type) "(%1);\n"; \
- itype.c_arg_in = "&%s_in"; \
- itype.c_type = #m_type; \
- itype.c_type_in = "MonoArray*"; \
- itype.c_type_out = "MonoArray*"; \
- itype.cs_type = itype.proxy_name; \
- itype.im_type_in = itype.proxy_name; \
- itype.im_type_out = itype.proxy_name; \
- builtin_types.insert(itype.name, itype); \
+#define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t) \
+ { \
+ itype = TypeInterface(); \
+ itype.name = #m_name; \
+ itype.cname = itype.name; \
+ itype.proxy_name = #m_proxy_t "[]"; \
+ itype.cs_type = itype.proxy_name; \
+ itype.c_in = "%5using %0 %1_in = " C_METHOD_MONOARRAY_TO(m_type) "(%1);\n"; \
+ itype.c_out = "%5return " C_METHOD_MONOARRAY_FROM(m_type) "(&%1);\n"; \
+ itype.c_arg_in = "&%s_in"; \
+ itype.c_type = #m_managed_type; \
+ itype.c_type_in = itype.proxy_name; \
+ itype.c_type_out = itype.proxy_name; \
+ itype.c_type_is_disposable_struct = true; \
+ builtin_types.insert(itype.name, itype); \
}
-#define INSERT_ARRAY(m_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_proxy_t)
+#define INSERT_ARRAY(m_type, m_managed_type, m_proxy_t) INSERT_ARRAY_FULL(m_type, m_type, m_managed_type, m_proxy_t)
- INSERT_ARRAY(PackedInt32Array, int);
- INSERT_ARRAY(PackedInt64Array, long);
- INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, byte);
+ INSERT_ARRAY(PackedInt32Array, godot_packed_int32_array, int);
+ INSERT_ARRAY(PackedInt64Array, godot_packed_int64_array, long);
+ INSERT_ARRAY_FULL(PackedByteArray, PackedByteArray, godot_packed_byte_array, byte);
- INSERT_ARRAY(PackedFloat32Array, float);
- INSERT_ARRAY(PackedFloat64Array, double);
+ INSERT_ARRAY(PackedFloat32Array, godot_packed_float32_array, float);
+ INSERT_ARRAY(PackedFloat64Array, godot_packed_float64_array, double);
- INSERT_ARRAY(PackedStringArray, string);
+ INSERT_ARRAY(PackedStringArray, godot_packed_string_array, string);
- INSERT_ARRAY(PackedColorArray, Color);
- INSERT_ARRAY(PackedVector2Array, Vector2);
- INSERT_ARRAY(PackedVector3Array, Vector3);
+ INSERT_ARRAY(PackedColorArray, godot_packed_color_array, Color);
+ INSERT_ARRAY(PackedVector2Array, godot_packed_vector2_array, Vector2);
+ INSERT_ARRAY(PackedVector3Array, godot_packed_vector3_array, Vector3);
#undef INSERT_ARRAY
@@ -3628,15 +3488,23 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.cname = itype.name;
itype.proxy_name = itype.name;
itype.type_parameter_count = 1;
- itype.c_out = "\treturn memnew(Array(%1));\n";
- itype.c_type = itype.name;
- itype.c_type_in = itype.c_type + "*";
- itype.c_type_out = itype.c_type + "*";
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
- itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
- itype.cs_out = "return new %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.cs_in = "ref %0.NativeValue";
+ itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_array_new_copy(%1);\n";
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_array";
+ itype.c_type_in = "ref " + itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
+ builtin_types.insert(itype.cname, itype);
+
+ // Array_@generic
+ // Re-use Array's itype
+ itype.name = "Array_@generic";
+ itype.cname = itype.name;
+ itype.cs_out = "%5return new %2(%0(%1));";
builtin_types.insert(itype.cname, itype);
// Dictionary
@@ -3645,15 +3513,23 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.cname = itype.name;
itype.proxy_name = itype.name;
itype.type_parameter_count = 2;
- itype.c_out = "\treturn memnew(Dictionary(%1));\n";
- itype.c_type = itype.name;
- itype.c_type_in = itype.c_type + "*";
- itype.c_type_out = itype.c_type + "*";
itype.cs_type = BINDINGS_NAMESPACE_COLLECTIONS "." + itype.proxy_name;
- itype.cs_in = "%0." CS_SMETHOD_GETINSTANCE "()";
- itype.cs_out = "return new %2(%0(%1));";
- itype.im_type_in = "IntPtr";
- itype.im_type_out = "IntPtr";
+ itype.cs_in = "ref %0.NativeValue";
+ itype.c_in = "%5using %0 %1_in = " C_CLASS_NATIVE_FUNCS ".godotsharp_dictionary_new_copy(%1);\n";
+ itype.c_out = "%5return %0.CreateTakingOwnershipOfDisposableValue(%1);\n";
+ itype.c_arg_in = "&%s_in";
+ itype.c_type = "godot_dictionary";
+ itype.c_type_in = "ref " + itype.c_type;
+ itype.c_type_out = itype.cs_type;
+ itype.c_type_is_disposable_struct = false; // [c_out] takes ownership
+ itype.c_ret_needs_default_initialization = true;
+ builtin_types.insert(itype.cname, itype);
+
+ // Dictionary_@generic
+ // Re-use Dictionary's itype
+ itype.name = "Dictionary_@generic";
+ itype.cname = itype.name;
+ itype.cs_out = "%5return new %2(%0(%1));";
builtin_types.insert(itype.cname, itype);
// void (fictitious type to represent the return type of methods that do not return anything)
@@ -3661,12 +3537,10 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.name = "void";
itype.cname = itype.name;
itype.proxy_name = itype.name;
- itype.c_type = itype.name;
+ itype.cs_type = itype.proxy_name;
+ itype.c_type = itype.proxy_name;
itype.c_type_in = itype.c_type;
itype.c_type_out = itype.c_type;
- itype.cs_type = itype.proxy_name;
- itype.im_type_in = itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
builtin_types.insert(itype.cname, itype);
}
@@ -3791,21 +3665,18 @@ void BindingsGenerator::_initialize() {
// Generate internal calls (after populating type interfaces and global constants)
- core_custom_icalls.clear();
- editor_custom_icalls.clear();
-
for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
- _generate_method_icalls(E.value);
+ const TypeInterface &itype = E.value;
+ Error err = _populate_method_icalls_table(itype);
+ ERR_FAIL_COND_MSG(err != OK, "Failed to generate icalls table for type: " + itype.name);
}
initialized = true;
}
static String generate_all_glue_option = "--generate-mono-glue";
-static String generate_cs_glue_option = "--generate-mono-cs-glue";
-static String generate_cpp_glue_option = "--generate-mono-cpp-glue";
-static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, String cpp_dir_path) {
+static void handle_cmdline_options(String glue_dir_path) {
BindingsGenerator bindings_generator;
bindings_generator.set_log_print_enabled(true);
@@ -3814,43 +3685,25 @@ static void handle_cmdline_options(String glue_dir_path, String cs_dir_path, Str
return;
}
- if (glue_dir_path.length()) {
- if (bindings_generator.generate_glue(glue_dir_path) != OK) {
- ERR_PRINT(generate_all_glue_option + ": Failed to generate the C++ glue.");
- }
-
- if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK) {
- ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API.");
- }
- }
+ CRASH_COND(glue_dir_path.is_empty());
- if (cs_dir_path.length()) {
- if (bindings_generator.generate_cs_api(cs_dir_path) != OK) {
- ERR_PRINT(generate_cs_glue_option + ": Failed to generate the C# API.");
- }
+ if (bindings_generator.generate_cs_api(glue_dir_path.plus_file(API_SOLUTION_NAME)) != OK) {
+ ERR_PRINT(generate_all_glue_option + ": Failed to generate the C# API.");
}
+}
- if (cpp_dir_path.length()) {
- if (bindings_generator.generate_glue(cpp_dir_path) != OK) {
- ERR_PRINT(generate_cpp_glue_option + ": Failed to generate the C++ glue.");
- }
- }
+static void cleanup_and_exit_godot() {
+ // Exit once done
+ Main::cleanup(true);
+ ::exit(0);
}
void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
- const int NUM_OPTIONS = 2;
-
String glue_dir_path;
- String cs_dir_path;
- String cpp_dir_path;
-
- int options_left = NUM_OPTIONS;
-
- bool exit_godot = false;
const List<String>::Element *elem = p_cmdline_args.front();
- while (elem && options_left) {
+ while (elem) {
if (elem->get() == generate_all_glue_option) {
const List<String>::Element *path_elem = elem->next();
@@ -3859,48 +3712,20 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
elem = elem->next();
} else {
ERR_PRINT(generate_all_glue_option + ": No output directory specified (expected path to '{GODOT_ROOT}/modules/mono/glue').");
- exit_godot = true;
- }
-
- --options_left;
- } else if (elem->get() == generate_cs_glue_option) {
- const List<String>::Element *path_elem = elem->next();
-
- if (path_elem) {
- cs_dir_path = path_elem->get();
- elem = elem->next();
- } else {
- ERR_PRINT(generate_cs_glue_option + ": No output directory specified.");
- exit_godot = true;
+ // Exit once done with invalid command line arguments
+ cleanup_and_exit_godot();
}
- --options_left;
- } else if (elem->get() == generate_cpp_glue_option) {
- const List<String>::Element *path_elem = elem->next();
-
- if (path_elem) {
- cpp_dir_path = path_elem->get();
- elem = elem->next();
- } else {
- ERR_PRINT(generate_cpp_glue_option + ": No output directory specified.");
- exit_godot = true;
- }
-
- --options_left;
+ break;
}
elem = elem->next();
}
- if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) {
- handle_cmdline_options(glue_dir_path, cs_dir_path, cpp_dir_path);
- exit_godot = true;
- }
-
- if (exit_godot) {
+ if (glue_dir_path.length()) {
+ handle_cmdline_options(glue_dir_path);
// Exit once done
- Main::cleanup(true);
- ::exit(0);
+ cleanup_and_exit_godot();
}
}
diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h
index ee170e4558..1e1e8be178 100644
--- a/modules/mono/editor/bindings_generator.h
+++ b/modules/mono/editor/bindings_generator.h
@@ -229,6 +229,23 @@ class BindingsGenerator {
bool is_ref_counted = 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
+ * flag if the ownership is transferred.
+ */
+ bool c_type_is_disposable_struct = false;
+
+ /**
+ * Determines whether the native return value of this type must be zero initialized
+ * before its address is passed to ptrcall. This is required for types whose destructor
+ * is called before being assigned the return value by `PtrToArg::encode`, e.g.:
+ * Array, Dictionary, String, StringName.
+ * It's not necessary to set this to `true` if [c_type_is_disposable_struct] is already `true`.
+ */
+ bool c_ret_needs_default_initialization = false;
+
+ /**
* Used only by Object-derived types.
* Determines if this type is not abstract (incomplete).
* e.g.: CanvasItem cannot be instantiated.
@@ -242,33 +259,36 @@ class BindingsGenerator {
*/
bool memory_own = false;
- /**
- * This must be set to true for any struct bigger than 32-bits. Those cannot be passed/returned by value
- * with internal calls, so we must use pointers instead. Returns must be replace with out parameters.
- * In this case, [c_out] and [cs_out] must have a different format, explained below.
- * The Mono IL interpreter icall trampolines don't support passing structs bigger than 32-bits by value (at least not on WASM).
- */
- bool ret_as_byref_arg = false;
-
// !! The comments of the following fields make reference to other fields via square brackets, e.g.: [field_name]
// !! When renaming those fields, make sure to rename their references in the comments
// --- C INTERFACE ---
- static const char *DEFAULT_VARARG_C_IN;
-
/**
* One or more statements that manipulate the parameter before being passed as argument of a ptrcall.
* If the statement adds a local that must be passed as the argument instead of the parameter,
* the name of that local must be specified with [c_arg_in].
- * For variadic methods, this field is required and, if empty, [DEFAULT_VARARG_C_IN] is used instead.
* Formatting elements:
* %0: [c_type] of the parameter
* %1: name of the parameter
+ * %2-4: reserved
+ * %5: indentation text
*/
String c_in;
/**
+ * One or more statements that manipulate the parameter before being passed as argument of a vararg call.
+ * If the statement adds a local that must be passed as the argument instead of the parameter,
+ * the name of that local must be specified with [c_arg_in].
+ * Formatting elements:
+ * %0: [c_type] of the parameter
+ * %1: name of the parameter
+ * %2-4: reserved
+ * %5: indentation text
+ */
+ String c_in_vararg;
+
+ /**
* Determines the expression that will be passed as argument to ptrcall.
* By default the value equals the name of the parameter,
* this varies for types that require special manipulation via [c_in].
@@ -291,7 +311,8 @@ class BindingsGenerator {
* %0: [c_type_out] of the return type
* %1: name of the variable to be returned
* %2: [name] of the return type
- * %3: name of the parameter that must be assigned the return value
+ * %3-4: reserved
+ * %5: indentation text
*/
String c_out;
@@ -330,6 +351,7 @@ class BindingsGenerator {
* %0 or %s: name of the parameter
*/
String cs_in;
+ bool cs_in_is_unsafe = false;
/**
* One or more statements that determine how a variable of this type is returned from a method.
@@ -338,7 +360,9 @@ class BindingsGenerator {
* %0: internal method name
* %1: internal method call arguments without surrounding parenthesis
* %2: [cs_type] of the return type
- * %3: [im_type_out] of the return type
+ * %3: [c_type_out] of the return type
+ * %4: reserved
+ * %5: indentation text
*/
String cs_out;
@@ -348,16 +372,6 @@ class BindingsGenerator {
*/
String cs_type;
- /**
- * Type used for parameters of internal call methods.
- */
- String im_type_in;
-
- /**
- * Type used for the return type of internal call methods.
- */
- String im_type_out;
-
const DocData::ClassDoc *class_doc = nullptr;
List<ConstantInterface> constants;
@@ -432,8 +446,8 @@ class BindingsGenerator {
itype.c_type = itype.name;
itype.cs_type = itype.proxy_name;
- itype.im_type_in = "ref " + itype.proxy_name;
- itype.im_type_out = itype.proxy_name;
+ itype.c_type_in = itype.proxy_name + "*";
+ itype.c_type_out = itype.proxy_name;
itype.class_doc = &EditorHelp::get_doc_data()->class_list[itype.proxy_name];
}
@@ -467,65 +481,27 @@ class BindingsGenerator {
return itype;
}
- static void create_placeholder_type(TypeInterface &r_itype, const StringName &p_cname) {
- r_itype.name = p_cname;
- r_itype.cname = p_cname;
- r_itype.proxy_name = r_itype.name;
-
- r_itype.c_type = r_itype.name;
- r_itype.c_type_in = "MonoObject*";
- r_itype.c_type_out = "MonoObject*";
- r_itype.cs_type = r_itype.proxy_name;
- r_itype.im_type_in = r_itype.proxy_name;
- r_itype.im_type_out = r_itype.proxy_name;
- }
-
- static void postsetup_enum_type(TypeInterface &r_enum_itype) {
- // C interface for enums is the same as that of 'uint32_t'. Remember to apply
- // any of the changes done here to the 'uint32_t' type interface as well.
-
- r_enum_itype.c_arg_in = "&%s_in";
- {
- // The expected types for parameters and return value in ptrcall are 'int64_t' or 'uint64_t'.
- r_enum_itype.c_in = "\t%0 %1_in = (%0)%1;\n";
- r_enum_itype.c_out = "\treturn (%0)%1;\n";
- r_enum_itype.c_type = "int64_t";
- }
- r_enum_itype.c_type_in = "int32_t";
- r_enum_itype.c_type_out = r_enum_itype.c_type_in;
-
- r_enum_itype.cs_type = r_enum_itype.proxy_name;
- r_enum_itype.cs_in = "(int)%s";
- r_enum_itype.cs_out = "return (%2)%0(%1);";
- r_enum_itype.im_type_in = "int";
- r_enum_itype.im_type_out = "int";
- r_enum_itype.class_doc = &EditorHelp::get_doc_data()->class_list[r_enum_itype.proxy_name];
- }
+ static void postsetup_enum_type(TypeInterface &r_enum_itype);
TypeInterface() {}
};
struct InternalCall {
String name;
- String im_type_out; // Return type for the C# method declaration. Also used as companion of [unique_siq]
- String im_sig; // Signature for the C# method declaration
String unique_sig; // Unique signature to avoid duplicates in containers
bool editor_only = false;
- InternalCall() {}
+ bool is_vararg = false;
+ bool is_static = false;
+ TypeReference return_type;
+ List<TypeReference> argument_types;
- InternalCall(const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
- name = p_name;
- im_type_out = p_im_type_out;
- im_sig = p_im_sig;
- unique_sig = p_unique_sig;
- editor_only = false;
- }
+ _FORCE_INLINE_ int get_arguments_count() const { return argument_types.size(); }
+
+ InternalCall() {}
- InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_im_type_out, const String &p_im_sig = String(), const String &p_unique_sig = String()) {
+ InternalCall(ClassDB::APIType api_type, const String &p_name, const String &p_unique_sig = String()) {
name = p_name;
- im_type_out = p_im_type_out;
- im_sig = p_im_sig;
unique_sig = p_unique_sig;
editor_only = api_type == ClassDB::API_EDITOR;
}
@@ -540,7 +516,6 @@ class BindingsGenerator {
HashMap<StringName, TypeInterface> obj_types;
- HashMap<StringName, TypeInterface> placeholder_types;
HashMap<StringName, TypeInterface> builtin_types;
HashMap<StringName, TypeInterface> enum_types;
@@ -548,13 +523,9 @@ class BindingsGenerator {
List<ConstantInterface> global_constants;
List<InternalCall> method_icalls;
+ /// Stores the unique internal calls from [method_icalls] that are assigned to each method.
HashMap<const MethodInterface *, const InternalCall *> method_icalls_map;
- List<const InternalCall *> generated_icall_funcs;
-
- List<InternalCall> core_custom_icalls;
- List<InternalCall> editor_custom_icalls;
-
HashMap<StringName, List<StringName>> blacklisted_methods;
void _initialize_blacklisted_methods();
@@ -571,6 +542,8 @@ class BindingsGenerator {
StringName type_String = StaticCString::create("String");
StringName type_StringName = StaticCString::create("StringName");
StringName type_NodePath = StaticCString::create("NodePath");
+ StringName type_Array_generic = StaticCString::create("Array_@generic");
+ StringName type_Dictionary_generic = StaticCString::create("Dictionary_@generic");
StringName type_at_GlobalScope = StaticCString::create("@GlobalScope");
StringName enum_Error = StaticCString::create("Error");
@@ -595,12 +568,14 @@ class BindingsGenerator {
StringName type_Vector4i = StaticCString::create("Vector4i");
// Object not included as it must be checked for all derived classes
- static constexpr int nullable_types_count = 17;
+ static constexpr int nullable_types_count = 18;
StringName nullable_types[nullable_types_count] = {
type_String,
type_StringName,
type_NodePath,
+ type_Array_generic,
+ type_Dictionary_generic,
StaticCString::create(_STR(Array)),
StaticCString::create(_STR(Dictionary)),
StaticCString::create(_STR(Callable)),
@@ -636,17 +611,6 @@ class BindingsGenerator {
NameCache name_cache;
- const List<InternalCall>::Element *find_icall_by_name(const String &p_name, const List<InternalCall> &p_list) {
- const List<InternalCall>::Element *it = p_list.front();
- while (it) {
- if (it->get().name == p_name) {
- return it;
- }
- it = it->next();
- }
- return nullptr;
- }
-
const ConstantInterface *find_constant_by_name(const String &p_name, const List<ConstantInterface> &p_constants) const {
for (const ConstantInterface &E : p_constants) {
if (E.name == p_name) {
@@ -657,18 +621,38 @@ class BindingsGenerator {
return nullptr;
}
- inline String get_unique_sig(const TypeInterface &p_type) {
- if (p_type.is_ref_counted) {
- return "Ref";
- } else if (p_type.is_object_type) {
+ inline String get_arg_unique_sig(const TypeInterface &p_type) {
+ // For parameters, we treat reference and non-reference derived types the same.
+ if (p_type.is_object_type) {
return "Obj";
} else if (p_type.is_enum) {
return "int";
+ } else if (p_type.cname == name_cache.type_Array_generic) {
+ return "Array";
+ } else if (p_type.cname == name_cache.type_Dictionary_generic) {
+ return "Dictionary";
}
return p_type.name;
}
+ inline String get_ret_unique_sig(const TypeInterface *p_type) {
+ // Reference derived return types are treated differently.
+ if (p_type->is_ref_counted) {
+ return "Ref";
+ } else if (p_type->is_object_type) {
+ return "Obj";
+ } else if (p_type->is_enum) {
+ return "int";
+ } else if (p_type->cname == name_cache.type_Array_generic) {
+ return "Array";
+ } else if (p_type->cname == name_cache.type_Dictionary_generic) {
+ return "Dictionary";
+ }
+
+ return p_type->name;
+ }
+
String bbcode_to_xml(const String &p_bbcode, const TypeInterface *p_itype);
void _append_xml_method(StringBuilder &p_xml_output, const TypeInterface *p_target_itype, const StringName &p_target_cname, const String &p_link_target, const Vector<String> &p_link_target_parts);
@@ -682,10 +666,9 @@ class BindingsGenerator {
int _determine_enum_prefix(const EnumInterface &p_ienum);
void _apply_prefix_to_enum_constants(EnumInterface &p_ienum, int p_prefix_length);
- void _generate_method_icalls(const TypeInterface &p_itype);
+ Error _populate_method_icalls_table(const TypeInterface &p_itype);
const TypeInterface *_get_type_or_null(const TypeReference &p_typeref);
- const TypeInterface *_get_type_or_placeholder(const TypeReference &p_typeref);
const String _get_generic_type_parameters(const TypeInterface &p_itype, const List<TypeReference> &p_generic_type_parameters);
@@ -706,11 +689,11 @@ class BindingsGenerator {
Error _generate_cs_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, int &p_method_bind_count, StringBuilder &p_output);
Error _generate_cs_signal(const BindingsGenerator::TypeInterface &p_itype, const BindingsGenerator::SignalInterface &p_isignal, StringBuilder &p_output);
+ Error _generate_cs_native_calls(const InternalCall &p_icall, StringBuilder &r_output);
+
void _generate_array_extensions(StringBuilder &p_output);
void _generate_global_constants(StringBuilder &p_output);
- Error _generate_glue_method(const TypeInterface &p_itype, const MethodInterface &p_imethod, StringBuilder &p_output);
-
Error _save_file(const String &p_path, const StringBuilder &p_content);
void _log(const char *p_format, ...) _PRINTF_FORMAT_ATTRIBUTE_2_3;
@@ -721,15 +704,12 @@ public:
Error generate_cs_core_project(const String &p_proj_dir);
Error generate_cs_editor_project(const String &p_proj_dir);
Error generate_cs_api(const String &p_output_dir);
- Error generate_glue(const String &p_output_dir);
_FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; }
_FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
_FORCE_INLINE_ bool is_initialized() { return initialized; }
- static uint32_t get_version();
-
static void handle_cmdline_args(const List<String> &p_cmdline_args);
BindingsGenerator() {
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index f830c7ffe1..102d27378f 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -46,7 +46,6 @@
#include "main/main.h"
#include "../csharp_script.h"
-#include "../glue/cs_glue_version.gen.h"
#include "../godotsharp_dirs.h"
#include "../mono_gd/gd_mono_marshal.h"
#include "../utils/macos_utils.h"