diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index ec9c123f8d8..b908fe9df85 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -2344,9 +2344,7 @@ bool CSharpScript::can_instantiate() const { } StringName CSharpScript::get_instance_base_type() const { - StringName native_name; - GDMonoCache::managed_callbacks.ScriptManagerBridge_GetScriptNativeName(this, &native_name); - return native_name; + return type_info.native_base_name; } CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_argcount, Object *p_owner, bool p_is_ref_counted, Callable::CallError &r_error) { diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index ec7328be4a4..ff3c47a0977 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -68,6 +68,11 @@ public: */ String class_name; + /** + * Name of the native class this script derives from. + */ + StringName native_base_name; + /** * Path to the icon that will be used for this class by the editor. */ diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index fb1d32c0cb3..6d3724b11ed 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -1060,6 +1060,22 @@ namespace Godot.Collections private static Array FromVariantFunc(in godot_variant variant) => VariantUtils.ConvertToArray(variant); + private void SetTypedForUnderlyingArray() + { + Marshaling.GetTypedCollectionParameterInfo(out var elemVariantType, out var elemClassName, out var elemScriptRef); + + var self = (godot_array)NativeValue; + + using (elemScriptRef) + { + NativeFuncs.godotsharp_array_set_typed( + ref self, + (uint)elemVariantType, + elemClassName, + elemScriptRef); + } + } + static unsafe Array() { VariantUtils.GenericConversion>.ToVariantCb = &ToVariantFunc; @@ -1083,6 +1099,7 @@ namespace Godot.Collections public Array() { _underlyingArray = new Array(); + SetTypedForUnderlyingArray(); } /// @@ -1099,6 +1116,7 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(collection)); _underlyingArray = new Array(); + SetTypedForUnderlyingArray(); foreach (T element in collection) Add(element); @@ -1118,6 +1136,7 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(array)); _underlyingArray = new Array(); + SetTypedForUnderlyingArray(); foreach (T element in array) Add(element); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index 91d49854c73..93a3f52f7db 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -182,18 +182,7 @@ namespace Godot.Bridge return; } - var native = GodotObject.InternalGetClassNativeBase(scriptType); - - var field = native.GetField("NativeName", BindingFlags.DeclaredOnly | BindingFlags.Static | - BindingFlags.Public | BindingFlags.NonPublic); - - if (field == null) - { - *outRes = default; - return; - } - - var nativeName = (StringName?)field.GetValue(null); + var nativeName = GodotObject.InternalGetClassNativeBaseName(scriptType); if (nativeName == null) { @@ -658,10 +647,14 @@ namespace Godot.Bridge private static unsafe void GetScriptTypeInfo(Type scriptType, godot_csharp_type_info* outTypeInfo) { - Type native = GodotObject.InternalGetClassNativeBase(scriptType); - godot_string className = Marshaling.ConvertStringToNative(ReflectionUtils.ConstructTypeName(scriptType)); + StringName? nativeBase = GodotObject.InternalGetClassNativeBaseName(scriptType); + + godot_string_name nativeBaseName = nativeBase != null + ? NativeFuncs.godotsharp_string_name_new_copy((godot_string_name)nativeBase.NativeValue) + : default; + bool isTool = scriptType.IsDefined(typeof(ToolAttribute), inherit: false); // If the type is nested and the parent type is a tool script, @@ -686,6 +679,7 @@ namespace Godot.Bridge godot_string iconPath = Marshaling.ConvertStringToNative(iconAttr?.Path); outTypeInfo->ClassName = className; + outTypeInfo->NativeBaseName = nativeBaseName; outTypeInfo->IconPath = iconPath; outTypeInfo->IsTool = isTool.ToGodotBool(); outTypeInfo->IsGlobalClass = isGlobalClass.ToGodotBool(); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs index db6961fd12a..8c3dec1cbf7 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Dictionary.cs @@ -496,6 +496,27 @@ namespace Godot.Collections private static Dictionary FromVariantFunc(in godot_variant variant) => VariantUtils.ConvertToDictionary(variant); + private void SetTypedForUnderlyingDictionary() + { + Marshaling.GetTypedCollectionParameterInfo(out var keyVariantType, out var keyClassName, out var keyScriptRef); + Marshaling.GetTypedCollectionParameterInfo(out var valueVariantType, out var valueClassName, out var valueScriptRef); + + var self = (godot_dictionary)NativeValue; + + using (keyScriptRef) + using (valueScriptRef) + { + NativeFuncs.godotsharp_dictionary_set_typed( + ref self, + (uint)keyVariantType, + keyClassName, + keyScriptRef, + (uint)valueVariantType, + valueClassName, + valueScriptRef); + } + } + static unsafe Dictionary() { VariantUtils.GenericConversion>.ToVariantCb = &ToVariantFunc; @@ -519,6 +540,7 @@ namespace Godot.Collections public Dictionary() { _underlyingDict = new Dictionary(); + SetTypedForUnderlyingDictionary(); } /// @@ -535,6 +557,7 @@ namespace Godot.Collections throw new ArgumentNullException(nameof(dictionary)); _underlyingDict = new Dictionary(); + SetTypedForUnderlyingDictionary(); foreach (KeyValuePair entry in dictionary) Add(entry.Key, entry.Value); diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs index a429931399c..00eb64dd170 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Reflection; using System.Runtime.InteropServices; using Godot.Bridge; using Godot.NativeInterop; @@ -13,6 +15,8 @@ namespace Godot private bool _disposed; private static readonly Type _cachedType = typeof(GodotObject); + private static readonly Dictionary _nativeNames = new Dictionary(); + internal IntPtr NativePtr; private bool _memoryOwn; @@ -191,16 +195,56 @@ namespace Godot return new SignalAwaiter(source, signal, this); } + internal static bool IsNativeClass(Type t) + { + if (ReferenceEquals(t.Assembly, typeof(GodotObject).Assembly)) + { + return true; + } + + if (ReflectionUtils.IsEditorHintCached) + { + return t.Assembly.GetName().Name == "GodotSharpEditor"; + } + + return false; + } + internal static Type InternalGetClassNativeBase(Type t) { - var name = t.Assembly.GetName().Name; + while (!IsNativeClass(t)) + { + Debug.Assert(t.BaseType is not null, "Script types must derive from a native Godot type."); - if (name == "GodotSharp" || name == "GodotSharpEditor") - return t; + t = t.BaseType; + } - Debug.Assert(t.BaseType is not null, "Script types must derive from a native Godot type."); + return t; + } - return InternalGetClassNativeBase(t.BaseType); + internal static StringName? InternalGetClassNativeBaseName(Type t) + { + if (_nativeNames.TryGetValue(t, out var name)) + { + return name; + } + + var baseType = InternalGetClassNativeBase(t); + + if (_nativeNames.TryGetValue(baseType, out name)) + { + return name; + } + + var field = baseType.GetField("NativeName", + BindingFlags.DeclaredOnly | BindingFlags.Static | + BindingFlags.Public | BindingFlags.NonPublic); + + name = field?.GetValue(null) as StringName; + + _nativeNames[baseType] = name; + + return name; } // ReSharper disable once VirtualMemberNeverOverridden.Global diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs index 7e5c01d0f88..555aebcd831 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs @@ -109,6 +109,7 @@ namespace Godot.NativeInterop public ref struct godot_csharp_type_info { private godot_string _className; + private godot_string_name _nativeBaseName; private godot_string _iconPath; private godot_bool _isTool; private godot_bool _isGlobalClass; @@ -122,6 +123,12 @@ namespace Godot.NativeInterop set => _className = value; } + public godot_string_name NativeBaseName + { + readonly get => _nativeBaseName; + set => _nativeBaseName = value; + } + public godot_string IconPath { readonly get => _iconPath; diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index fc68b119322..54efaad3c84 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -199,6 +199,44 @@ namespace Godot.NativeInterop return Variant.Type.Nil; } + internal static void GetTypedCollectionParameterInfo( + out Variant.Type variantType, + out godot_string_name className, + out godot_ref script) + { + variantType = ConvertManagedTypeToVariantType(typeof(T), out _); + + if (variantType != Variant.Type.Object) + { + className = default; + script = default; + return; + } + + godot_ref scriptRef = default; + + if (!GodotObject.IsNativeClass(typeof(T))) + { + unsafe + { + Godot.Bridge.ScriptManagerBridge.GetOrLoadOrCreateScriptForType(typeof(T), &scriptRef); + } + + // Don't call GodotObject.InternalGetClassNativeBaseName here! + // godot_dictionary_set_typed and godot_array_set_typed will call CSharpScript::get_instance_base_type + // when a script is passed, because this is better for performance than using reflection to find the + // native base type. + className = default; + } + else + { + StringName? nativeBaseName = GodotObject.InternalGetClassNativeBaseName(typeof(T)); + className = nativeBaseName != null ? (godot_string_name)nativeBaseName.NativeValue : default; + } + + script = scriptRef; + } + // String public static unsafe godot_string ConvertStringToNative(string? p_mono_string) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs index 6a643833f6a..47998c8017c 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/NativeFuncs.cs @@ -402,6 +402,14 @@ namespace Godot.NativeInterop public static partial void godotsharp_array_make_read_only(ref godot_array p_self); + public static partial void godotsharp_array_set_typed( + ref godot_array p_self, + uint p_elem_type, + in godot_string_name p_elem_class_name, + in godot_ref p_elem_script); + + public static partial godot_bool godotsharp_array_is_typed(ref godot_array p_self); + public static partial void godotsharp_array_max(ref godot_array p_self, out godot_variant r_value); public static partial void godotsharp_array_min(ref godot_array p_self, out godot_variant r_value); @@ -463,6 +471,31 @@ namespace Godot.NativeInterop public static partial void godotsharp_dictionary_make_read_only(ref godot_dictionary p_self); + public static partial void godotsharp_dictionary_set_typed( + ref godot_dictionary p_self, + uint p_key_type, + in godot_string_name p_key_class_name, + in godot_ref p_key_script, + uint p_value_type, + in godot_string_name p_value_class_name, + in godot_ref p_value_script); + + public static partial godot_bool godotsharp_dictionary_is_typed_key(ref godot_dictionary p_self); + + public static partial godot_bool godotsharp_dictionary_is_typed_value(ref godot_dictionary p_self); + + public static partial uint godotsharp_dictionary_get_typed_key_builtin(ref godot_dictionary p_self); + + public static partial uint godotsharp_dictionary_get_typed_value_builtin(ref godot_dictionary p_self); + + public static partial void godotsharp_dictionary_get_typed_key_class_name(ref godot_dictionary p_self, out godot_string_name r_dest); + + public static partial void godotsharp_dictionary_get_typed_value_class_name(ref godot_dictionary p_self, out godot_string_name r_dest); + + public static partial void godotsharp_dictionary_get_typed_key_script(ref godot_dictionary p_self, out godot_variant r_dest); + + public static partial void godotsharp_dictionary_get_typed_value_script(ref godot_dictionary p_self, out godot_variant r_dest); + public static partial void godotsharp_dictionary_to_string(ref godot_dictionary p_self, out godot_string r_str); // StringExtensions diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs index 27989b5c813..e88155edb84 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs @@ -12,12 +12,12 @@ internal class ReflectionUtils { private static readonly HashSet? _tupleTypeSet; private static readonly Dictionary? _builtinTypeNameDictionary; - private static readonly bool _isEditorHintCached; + internal static readonly bool IsEditorHintCached; static ReflectionUtils() { - _isEditorHintCached = Engine.IsEditorHint(); - if (!_isEditorHintCached) + IsEditorHintCached = Engine.IsEditorHint(); + if (!IsEditorHintCached) { return; } @@ -66,7 +66,7 @@ internal class ReflectionUtils public static string ConstructTypeName(Type type) { - if (!_isEditorHintCached) + if (!IsEditorHintCached) { return type.Name; } diff --git a/modules/mono/glue/runtime_interop.cpp b/modules/mono/glue/runtime_interop.cpp index 73c10eba830..3a24eb5e7b3 100644 --- a/modules/mono/glue/runtime_interop.cpp +++ b/modules/mono/glue/runtime_interop.cpp @@ -1098,6 +1098,20 @@ void godotsharp_array_make_read_only(Array *p_self) { p_self->make_read_only(); } +void godotsharp_array_set_typed(Array *p_self, uint32_t p_elem_type, const StringName *p_elem_class_name, const Ref *p_elem_script) { + Variant elem_script_variant; + StringName elem_class_name = *p_elem_class_name; + if (p_elem_script && p_elem_script->is_valid()) { + elem_script_variant = Variant(p_elem_script->ptr()); + elem_class_name = p_elem_script->ptr()->get_instance_base_type(); + } + p_self->set_typed(p_elem_type, elem_class_name, p_elem_script->ptr()); +} + +bool godotsharp_array_is_typed(const Array *p_self) { + return p_self->is_typed(); +} + void godotsharp_array_max(const Array *p_self, Variant *r_value) { *r_value = p_self->max(); } @@ -1207,6 +1221,54 @@ void godotsharp_dictionary_make_read_only(Dictionary *p_self) { p_self->make_read_only(); } +void godotsharp_dictionary_set_typed(Dictionary *p_self, uint32_t p_key_type, const StringName *p_key_class_name, const Ref *p_key_script, uint32_t p_value_type, const StringName *p_value_class_name, const Ref *p_value_script) { + Variant key_script_variant; + StringName key_class_name = *p_key_class_name; + if (p_key_script && p_key_script->is_valid()) { + key_script_variant = Variant(p_key_script->ptr()); + key_class_name = p_key_script->ptr()->get_instance_base_type(); + } + Variant value_script_variant; + StringName value_class_name = *p_value_class_name; + if (p_value_script && p_value_script->is_valid()) { + value_script_variant = Variant(p_value_script->ptr()); + value_class_name = p_value_script->ptr()->get_instance_base_type(); + } + p_self->set_typed(p_key_type, key_class_name, p_key_script->ptr(), p_value_type, value_class_name, p_value_script->ptr()); +} + +bool godotsharp_dictionary_is_typed_key(const Dictionary *p_self) { + return p_self->is_typed_key(); +} + +bool godotsharp_dictionary_is_typed_value(const Dictionary *p_self) { + return p_self->is_typed_value(); +} + +uint32_t godotsharp_dictionary_get_typed_key_builtin(const Dictionary *p_self) { + return p_self->get_typed_key_builtin(); +} + +uint32_t godotsharp_dictionary_get_typed_value_builtin(const Dictionary *p_self) { + return p_self->get_typed_value_builtin(); +} + +void godotsharp_dictionary_get_typed_key_class_name(const Dictionary *p_self, StringName *r_dest) { + memnew_placement(r_dest, StringName(p_self->get_typed_key_class_name())); +} + +void godotsharp_dictionary_get_typed_value_class_name(const Dictionary *p_self, StringName *r_dest) { + memnew_placement(r_dest, StringName(p_self->get_typed_value_class_name())); +} + +void godotsharp_dictionary_get_typed_key_script(const Dictionary *p_self, Variant *r_dest) { + memnew_placement(r_dest, Variant(p_self->get_typed_key_script())); +} + +void godotsharp_dictionary_get_typed_value_script(const Dictionary *p_self, Variant *r_dest) { + memnew_placement(r_dest, Variant(p_self->get_typed_value_script())); +} + void godotsharp_dictionary_to_string(const Dictionary *p_self, String *r_str) { *r_str = Variant(*p_self).operator String(); } @@ -1585,6 +1647,8 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_array_insert, (void *)godotsharp_array_last_index_of, (void *)godotsharp_array_make_read_only, + (void *)godotsharp_array_set_typed, + (void *)godotsharp_array_is_typed, (void *)godotsharp_array_max, (void *)godotsharp_array_min, (void *)godotsharp_array_pick_random, @@ -1610,6 +1674,15 @@ static const void *unmanaged_callbacks[]{ (void *)godotsharp_dictionary_recursive_equal, (void *)godotsharp_dictionary_remove_key, (void *)godotsharp_dictionary_make_read_only, + (void *)godotsharp_dictionary_set_typed, + (void *)godotsharp_dictionary_is_typed_key, + (void *)godotsharp_dictionary_is_typed_value, + (void *)godotsharp_dictionary_get_typed_key_builtin, + (void *)godotsharp_dictionary_get_typed_value_builtin, + (void *)godotsharp_dictionary_get_typed_key_class_name, + (void *)godotsharp_dictionary_get_typed_value_class_name, + (void *)godotsharp_dictionary_get_typed_key_script, + (void *)godotsharp_dictionary_get_typed_value_script, (void *)godotsharp_dictionary_to_string, (void *)godotsharp_string_simplify_path, (void *)godotsharp_string_to_camel_case,