summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRémi Verschelde <rverschelde@gmail.com>2024-09-23 16:13:49 +0200
committerRémi Verschelde <rverschelde@gmail.com>2024-09-23 16:13:49 +0200
commitea8d20d35b5171a3ddaebad4b5649ba8f67b51c9 (patch)
tree96b6e53bf3ec13622823dea184b765a6049be156
parent8c273eb14ceeecc08f1262e7fccc8cdf260958ae (diff)
parent307224927ced54943e1ce3da393c63b35160bd2e (diff)
downloadredot-engine-ea8d20d35b5171a3ddaebad4b5649ba8f67b51c9.tar.gz
Merge pull request #96955 from Delsin-Yu/generator-based-CreateManagedForGodotObjectBinding
[.NET] Replace Reflection-Based implementation with Generated one in `CreateManagedForGodotObjectBinding`
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs18
-rw-r--r--modules/mono/editor/bindings_generator.cpp127
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs76
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs11
4 files changed, 158 insertions, 74 deletions
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index e84b4e92c7..788b46ab9a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Reflection;
using GodotTools.Build;
using GodotTools.Ides;
using GodotTools.Ides.Rider;
@@ -701,6 +702,23 @@ namespace GodotTools
private static IntPtr InternalCreateInstance(IntPtr unmanagedCallbacks, int unmanagedCallbacksSize)
{
Internal.Initialize(unmanagedCallbacks, unmanagedCallbacksSize);
+
+ var populateConstructorMethod =
+ AppDomain.CurrentDomain
+ .GetAssemblies()
+ .First(x => x.GetName().Name == "GodotSharpEditor")
+ .GetType("Godot.EditorConstructors")?
+ .GetMethod("AddEditorConstructors",
+ BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
+
+ if (populateConstructorMethod == null)
+ {
+ throw new MissingMethodException("Godot.EditorConstructors",
+ "AddEditorConstructors");
+ }
+
+ populateConstructorMethod.Invoke(null, null);
+
return new GodotSharpEditor().NativeInstance;
}
}
diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp
index 8081c21ea0..a467aae2e9 100644
--- a/modules/mono/editor/bindings_generator.cpp
+++ b/modules/mono/editor/bindings_generator.cpp
@@ -77,6 +77,10 @@ StringBuilder &operator<<(StringBuilder &r_sb, const char *p_cstring) {
#define BINDINGS_GLOBAL_SCOPE_CLASS "GD"
#define BINDINGS_NATIVE_NAME_FIELD "NativeName"
+#define BINDINGS_CLASS_CONSTRUCTOR "Constructors"
+#define BINDINGS_CLASS_CONSTRUCTOR_EDITOR "EditorConstructors"
+#define BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY "BuiltInMethodConstructors"
+
#define CS_PARAM_MEMORYOWN "memoryOwn"
#define CS_PARAM_METHODBIND "method"
#define CS_PARAM_INSTANCE "ptr"
@@ -1737,6 +1741,69 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
+ // Generate source file for built-in type constructor dictionary.
+
+ {
+ StringBuilder cs_built_in_ctors_content;
+
+ cs_built_in_ctors_content.append("namespace " BINDINGS_NAMESPACE ";\n\n");
+ cs_built_in_ctors_content.append("using System;\n"
+ "using System.Collections.Generic;\n"
+ "\n");
+ cs_built_in_ctors_content.append("internal static class " BINDINGS_CLASS_CONSTRUCTOR "\n{");
+
+ cs_built_in_ctors_content.append(MEMBER_BEGIN "internal static readonly Dictionary<string, Func<IntPtr, GodotObject>> " BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY ";\n");
+
+ cs_built_in_ctors_content.append(MEMBER_BEGIN "public static GodotObject Invoke(string nativeTypeNameStr, IntPtr nativeObjectPtr)\n");
+ cs_built_in_ctors_content.append(INDENT1 OPEN_BLOCK);
+ cs_built_in_ctors_content.append(INDENT2 "if (!" BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY ".TryGetValue(nativeTypeNameStr, out var constructor))\n");
+ cs_built_in_ctors_content.append(INDENT3 "throw new InvalidOperationException(\"Wrapper class not found for type: \" + nativeTypeNameStr);\n");
+ cs_built_in_ctors_content.append(INDENT2 "return constructor(nativeObjectPtr);\n");
+ cs_built_in_ctors_content.append(INDENT1 CLOSE_BLOCK);
+
+ cs_built_in_ctors_content.append(MEMBER_BEGIN "static " BINDINGS_CLASS_CONSTRUCTOR "()\n");
+ cs_built_in_ctors_content.append(INDENT1 OPEN_BLOCK);
+ cs_built_in_ctors_content.append(INDENT2 BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY " = new();\n");
+
+ for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
+ const TypeInterface &itype = E.value;
+
+ if (itype.api_type != ClassDB::API_CORE || itype.is_singleton_instance) {
+ continue;
+ }
+
+ if (itype.is_deprecated) {
+ cs_built_in_ctors_content.append("#pragma warning disable CS0618\n");
+ }
+
+ cs_built_in_ctors_content.append(INDENT2 BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY ".Add(\"");
+ cs_built_in_ctors_content.append(itype.name);
+ cs_built_in_ctors_content.append("\", " CS_PARAM_INSTANCE " => new ");
+ cs_built_in_ctors_content.append(itype.proxy_name);
+ if (itype.is_singleton && !itype.is_compat_singleton) {
+ cs_built_in_ctors_content.append("Instance");
+ }
+ cs_built_in_ctors_content.append("(" CS_PARAM_INSTANCE "));\n");
+
+ if (itype.is_deprecated) {
+ cs_built_in_ctors_content.append("#pragma warning restore CS0618\n");
+ }
+ }
+
+ cs_built_in_ctors_content.append(INDENT1 CLOSE_BLOCK);
+
+ cs_built_in_ctors_content.append(CLOSE_BLOCK);
+
+ String constructors_file = path::join(base_gen_dir, BINDINGS_CLASS_CONSTRUCTOR ".cs");
+ Error err = _save_file(constructors_file, cs_built_in_ctors_content);
+
+ if (err != OK) {
+ return err;
+ }
+
+ compile_items.push_back(constructors_file);
+ }
+
// Generate native calls
StringBuilder cs_icalls_content;
@@ -1844,6 +1911,57 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir) {
compile_items.push_back(output_file);
}
+ // Generate source file for editor type constructor dictionary.
+
+ {
+ StringBuilder cs_built_in_ctors_content;
+
+ cs_built_in_ctors_content.append("namespace " BINDINGS_NAMESPACE ";\n\n");
+ cs_built_in_ctors_content.append("internal static class " BINDINGS_CLASS_CONSTRUCTOR_EDITOR "\n{");
+
+ cs_built_in_ctors_content.append(MEMBER_BEGIN "private static void AddEditorConstructors()\n");
+ cs_built_in_ctors_content.append(INDENT1 OPEN_BLOCK);
+ cs_built_in_ctors_content.append(INDENT2 "var builtInMethodConstructors = " BINDINGS_CLASS_CONSTRUCTOR "." BINDINGS_CLASS_CONSTRUCTOR_DICTIONARY ";\n");
+
+ for (const KeyValue<StringName, TypeInterface> &E : obj_types) {
+ const TypeInterface &itype = E.value;
+
+ if (itype.api_type != ClassDB::API_EDITOR || itype.is_singleton_instance) {
+ continue;
+ }
+
+ if (itype.is_deprecated) {
+ cs_built_in_ctors_content.append("#pragma warning disable CS0618\n");
+ }
+
+ cs_built_in_ctors_content.append(INDENT2 "builtInMethodConstructors.Add(\"");
+ cs_built_in_ctors_content.append(itype.name);
+ cs_built_in_ctors_content.append("\", " CS_PARAM_INSTANCE " => new ");
+ cs_built_in_ctors_content.append(itype.proxy_name);
+ if (itype.is_singleton && !itype.is_compat_singleton) {
+ cs_built_in_ctors_content.append("Instance");
+ }
+ cs_built_in_ctors_content.append("(" CS_PARAM_INSTANCE "));\n");
+
+ if (itype.is_deprecated) {
+ cs_built_in_ctors_content.append("#pragma warning restore CS0618\n");
+ }
+ }
+
+ cs_built_in_ctors_content.append(INDENT1 CLOSE_BLOCK);
+
+ cs_built_in_ctors_content.append(CLOSE_BLOCK);
+
+ String constructors_file = path::join(base_gen_dir, BINDINGS_CLASS_CONSTRUCTOR_EDITOR ".cs");
+ Error err = _save_file(constructors_file, cs_built_in_ctors_content);
+
+ if (err != OK) {
+ return err;
+ }
+
+ compile_items.push_back(constructors_file);
+ }
+
// Generate native calls
StringBuilder cs_icalls_content;
@@ -2210,6 +2328,15 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
<< CLOSE_BLOCK_L2 CLOSE_BLOCK_L1;
}
+ output << MEMBER_BEGIN "internal " << itype.proxy_name << "(IntPtr " CS_PARAM_INSTANCE ") : this("
+ << (itype.memory_own ? "true" : "false") << ")\n" OPEN_BLOCK_L1
+ << INDENT2 "NativePtr = " CS_PARAM_INSTANCE ";\n"
+ << INDENT2 "unsafe\n" INDENT2 OPEN_BLOCK
+ << INDENT3 "ConstructAndInitialize(null, "
+ << BINDINGS_NATIVE_NAME_FIELD ", CachedType, refCounted: "
+ << (itype.is_ref_counted ? "true" : "false") << ");\n"
+ << CLOSE_BLOCK_L2 CLOSE_BLOCK_L1;
+
// Add.. em.. trick constructor. Sort of.
output.append(MEMBER_BEGIN "internal ");
output.append(itype.proxy_name);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index 901700067d..1b3062c5db 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -93,27 +93,15 @@ namespace Godot.Bridge
internal static unsafe IntPtr CreateManagedForGodotObjectBinding(godot_string_name* nativeTypeName,
IntPtr godotObject)
{
- // TODO: Optimize with source generators and delegate pointers.
-
try
{
using var stringName = StringName.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_string_name_new_copy(CustomUnsafe.AsRef(nativeTypeName)));
string nativeTypeNameStr = stringName.ToString();
- Type nativeType = TypeGetProxyClass(nativeTypeNameStr) ?? throw new InvalidOperationException(
- "Wrapper class not found for type: " + nativeTypeNameStr);
- var obj = (GodotObject)FormatterServices.GetUninitializedObject(nativeType);
-
- var ctor = nativeType.GetConstructor(
- BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
- null, Type.EmptyTypes, null);
-
- obj.NativePtr = godotObject;
-
- _ = ctor!.Invoke(obj, null);
+ var instance = Constructors.Invoke(nativeTypeNameStr, godotObject);
- return GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(obj));
+ return GCHandle.ToIntPtr(CustomGCHandle.AllocStrong(instance));
}
catch (Exception e)
{
@@ -308,66 +296,6 @@ namespace Godot.Bridge
}
}
- private static Type? TypeGetProxyClass(string nativeTypeNameStr)
- {
- // Performance is not critical here as this will be replaced with a generated dictionary.
-
- if (nativeTypeNameStr[0] == '_')
- nativeTypeNameStr = nativeTypeNameStr.Substring(1);
-
- Type? wrapperType = typeof(GodotObject).Assembly.GetType("Godot." + nativeTypeNameStr);
-
- if (wrapperType == null)
- {
- wrapperType = GetTypeByGodotClassAttr(typeof(GodotObject).Assembly, nativeTypeNameStr);
- }
-
- if (wrapperType == null)
- {
- var editorAssembly = AppDomain.CurrentDomain.GetAssemblies()
- .FirstOrDefault(a => a.GetName().Name == "GodotSharpEditor");
-
- if (editorAssembly != null)
- {
- wrapperType = editorAssembly.GetType("Godot." + nativeTypeNameStr);
-
- if (wrapperType == null)
- {
- wrapperType = GetTypeByGodotClassAttr(editorAssembly, nativeTypeNameStr);
- }
- }
- }
-
- static Type? GetTypeByGodotClassAttr(Assembly assembly, string nativeTypeNameStr)
- {
- var types = assembly.GetTypes();
- foreach (var type in types)
- {
- var attr = type.GetCustomAttribute<GodotClassNameAttribute>();
- if (attr?.Name == nativeTypeNameStr)
- {
- return type;
- }
- }
- return null;
- }
-
- static bool IsStatic(Type type) => type.IsAbstract && type.IsSealed;
-
- if (wrapperType != null && IsStatic(wrapperType))
- {
- // A static class means this is a Godot singleton class. Try to get the Instance proxy type.
- wrapperType = TypeGetProxyClass($"{wrapperType.Name}Instance");
- if (wrapperType == null)
- {
- // Otherwise, fallback to GodotObject.
- return typeof(GodotObject);
- }
- }
-
- return wrapperType;
- }
-
// Called from GodotPlugins
// ReSharper disable once UnusedMember.Local
public static void LookupScriptsInAssembly(Assembly assembly)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
index c094eaed77..a429931399 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
@@ -29,6 +29,17 @@ namespace Godot
}
}
+ internal GodotObject(IntPtr nativePtr) : this(false)
+ {
+ // NativePtr must be non-zero before calling ConstructAndInitialize to avoid invoking the constructor NativeCtor.
+ // We don't want to invoke the constructor, because we already have a constructed instance in nativePtr.
+ NativePtr = nativePtr;
+ unsafe
+ {
+ ConstructAndInitialize(NativeCtor, NativeName, _cachedType, refCounted: false);
+ }
+ }
+
internal unsafe void ConstructAndInitialize(
delegate* unmanaged<godot_bool, IntPtr> nativeCtor,
StringName nativeName,