From e465187b4746a6f46a8060b826ad3024b7f83225 Mon Sep 17 00:00:00 2001 From: Karsten Bock Date: Thu, 15 Aug 2024 21:31:18 +0200 Subject: [PATCH] Only register actually overridden virtual methods --- src/Godot.Bindings/Bridge/ClassDB/ClassDB.cs | 17 +++++++++-------- .../NativeInterop/InteropUtils.cs | 6 ++++-- .../EngineClassesBindingsDataCollector.cs | 3 ++- .../BindingsGenerator/BindingsGenerator.cs | 4 ++++ .../MethodBodies/RegisterVirtualOverrides.cs | 8 +++++++- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/Godot.Bindings/Bridge/ClassDB/ClassDB.cs b/src/Godot.Bindings/Bridge/ClassDB/ClassDB.cs index 85490ef..bdc5eec 100644 --- a/src/Godot.Bindings/Bridge/ClassDB/ClassDB.cs +++ b/src/Godot.Bindings/Bridge/ClassDB/ClassDB.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Godot.NativeInterop; @@ -23,7 +24,7 @@ public static class ClassDB /// /// The type of the class. /// The configuration function. - public static void RegisterClass(Action configure) where T : GodotObject + public static void RegisterClass<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(Action configure) where T : GodotObject { RegisterClassCore(isVirtual: false, isAbstract: false, isExposed: true, isRuntime: false, configure); } @@ -35,7 +36,7 @@ public static void RegisterClass(Action configure /// /// The type of the class. /// The configuration function. - public static void RegisterRuntimeClass(Action configure) where T : GodotObject + public static void RegisterRuntimeClass<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(Action configure) where T : GodotObject { RegisterClassCore(isVirtual: false, isAbstract: false, isExposed: true, isRuntime: true, configure); } @@ -47,7 +48,7 @@ public static void RegisterRuntimeClass(Action co /// /// The type of the class. /// The configuration function. - public static void RegisterVirtualClass(Action configure) where T : GodotObject + public static void RegisterVirtualClass<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(Action configure) where T : GodotObject { RegisterClassCore(isVirtual: true, isAbstract: false, isExposed: true, isRuntime: false, configure); } @@ -59,7 +60,7 @@ public static void RegisterVirtualClass(Action co /// /// The type of the class. /// The configuration function. - public static void RegisterAbstractClass(Action configure) where T : GodotObject + public static void RegisterAbstractClass<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(Action configure) where T : GodotObject { RegisterClassCore(isVirtual: false, isAbstract: true, isExposed: true, isRuntime: false, configure); } @@ -70,12 +71,12 @@ public static void RegisterAbstractClass(Action c /// /// The type of the class. /// The configuration function. - public static void RegisterInternalClass(Action configure) where T : GodotObject + public static void RegisterInternalClass<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(Action configure) where T : GodotObject { RegisterClassCore(isVirtual: false, isAbstract: false, isExposed: false, isRuntime: false, configure); } - private unsafe static void RegisterClassCore(bool isVirtual, bool isAbstract, bool isExposed, bool isRuntime, Action configure) where T : GodotObject + private unsafe static void RegisterClassCore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] T>(bool isVirtual, bool isAbstract, bool isExposed, bool isRuntime, Action configure) where T : GodotObject { if (typeof(T).IsAbstract && !isAbstract) { @@ -152,7 +153,7 @@ private unsafe static void RegisterClassCore(bool isVirtual, bool isAbstract, if (InteropUtils.RegisterVirtualOverridesHelpers.TryGetValue(godotNativeName, out var registerVirtualOverrides)) { - registerVirtualOverrides(context); + registerVirtualOverrides(typeof(T), context); } } @@ -412,7 +413,7 @@ private unsafe static void Free_Native(void* userData, void* instance) if (!context.RegisteredVirtualMethodOverrides.TryGetValue(methodNameStr, out var virtualMethodInfo)) { - throw new InvalidOperationException($"Virtual method '{methodNameStr}' has not been registered in class '{context.ClassName}'."); + return null; } return (delegate* unmanaged[Cdecl])Marshal.GetFunctionPointerForDelegate(virtualMethodInfo.Invoker.CallVirtualWithPtrArgs); diff --git a/src/Godot.Bindings/NativeInterop/InteropUtils.cs b/src/Godot.Bindings/NativeInterop/InteropUtils.cs index e2c98c0..5103075 100644 --- a/src/Godot.Bindings/NativeInterop/InteropUtils.cs +++ b/src/Godot.Bindings/NativeInterop/InteropUtils.cs @@ -1,14 +1,16 @@ using System; using System.Collections.Frozen; +using System.Diagnostics.CodeAnalysis; using Godot.Bridge; namespace Godot.NativeInterop; -internal static partial class InteropUtils +public static partial class InteropUtils { internal static FrozenDictionary> CreateHelpers { get; private set; } - internal static FrozenDictionary> RegisterVirtualOverridesHelpers { get; private set; } + internal delegate void RegisterVirtualOverrideHelper([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type, ClassDBRegistrationContext context); + internal static FrozenDictionary RegisterVirtualOverridesHelpers { get; private set; } static InteropUtils() { diff --git a/src/Godot.BindingsGenerator/BindingsDataCollectors/EngineClassesBindingsDataCollector.cs b/src/Godot.BindingsGenerator/BindingsDataCollectors/EngineClassesBindingsDataCollector.cs index 6c142a8..b55d715 100644 --- a/src/Godot.BindingsGenerator/BindingsDataCollectors/EngineClassesBindingsDataCollector.cs +++ b/src/Godot.BindingsGenerator/BindingsDataCollectors/EngineClassesBindingsDataCollector.cs @@ -108,7 +108,7 @@ public override void Populate(BindingsData.CollectionContext context) Body = MethodBody.Create(writer => { writer.WriteLine($"var createHelpers = new global::System.Collections.Generic.Dictionary>(capacity: {context.Api.Classes.Length});"); - writer.WriteLine($"var registerVirtualOverridesHelpers = new global::System.Collections.Generic.Dictionary>(capacity: {context.Api.Classes.Length});"); + writer.WriteLine($"var registerVirtualOverridesHelpers = new global::System.Collections.Generic.Dictionary(capacity: {context.Api.Classes.Length});"); foreach (var engineClass in context.Api.Classes) { @@ -303,6 +303,7 @@ private void PopulateEngineClassMethods(BindingsData.CollectionContext context, IsNew = engineClass.Name != "Object", Parameters = { + new ParameterInfo("type", new TypeInfo("Type", "System")) { Attributes = ["[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)]"]}, new ParameterInfo("context", new TypeInfo("ClassDBRegistrationContext", "Godot.Bridge")), }, Body = new RegisterVirtualOverrides(type, virtualMethods), diff --git a/src/Godot.BindingsGenerator/BindingsGenerator/BindingsGenerator.cs b/src/Godot.BindingsGenerator/BindingsGenerator/BindingsGenerator.cs index d840d06..f09fcf3 100644 --- a/src/Godot.BindingsGenerator/BindingsGenerator/BindingsGenerator.cs +++ b/src/Godot.BindingsGenerator/BindingsGenerator/BindingsGenerator.cs @@ -51,6 +51,10 @@ private static void GenerateCore(GodotApi api, string outputDirectoryPath, Bindi using var streamWriter = new StreamWriter(stream); using var writer = new IndentedTextWriter(streamWriter); + writer.WriteLine("using System.Diagnostics.CodeAnalysis;"); + writer.WriteLine("using System.Reflection;"); + writer.WriteLine(); + writer.WriteLine($"namespace {type.Namespace};"); writer.WriteLine(); diff --git a/src/Godot.BindingsGenerator/MethodBodies/RegisterVirtualOverrides.cs b/src/Godot.BindingsGenerator/MethodBodies/RegisterVirtualOverrides.cs index 17aa805..3459995 100644 --- a/src/Godot.BindingsGenerator/MethodBodies/RegisterVirtualOverrides.cs +++ b/src/Godot.BindingsGenerator/MethodBodies/RegisterVirtualOverrides.cs @@ -23,10 +23,14 @@ public override void Write(MethodBase owner, IndentedTextWriter writer) { if (_type.BaseType is not null) { - writer.WriteLine($"{_type.BaseType.FullNameWithGlobal}.RegisterVirtualOverrides(context);"); + writer.WriteLine($"{_type.BaseType.FullNameWithGlobal}.RegisterVirtualOverrides(type, context);"); } foreach (var (method, engineMethod) in _virtualMethods) { + writer.WriteLine($"if (type.GetMethod(nameof(MethodName.{method.Name}), BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) != null)"); + writer.WriteLine('{'); + writer.Indent++; + writer.Write($"context.BindVirtualMethodOverride(MethodName.{method.Name}, "); writer.Write($"static ({_type.FullNameWithGlobal} __instance"); if (method.Parameters.Count > 0) @@ -84,6 +88,8 @@ public override void Write(MethodBase owner, IndentedTextWriter writer) writer.Indent--; writer.WriteLine("});"); + writer.Indent--; + writer.WriteLine('}'); } } }