summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs30
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs179
2 files changed, 181 insertions, 28 deletions
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
index eef26cdd4e..01d9103221 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs
@@ -725,15 +725,7 @@ namespace Godot.Bridge
{
Type native = GodotObject.InternalGetClassNativeBase(scriptType);
- string typeName = scriptType.Name;
- if (scriptType.IsGenericType)
- {
- var sb = new StringBuilder();
- AppendTypeName(sb, scriptType);
- typeName = sb.ToString();
- }
-
- godot_string className = Marshaling.ConvertStringToNative(typeName);
+ godot_string className = Marshaling.ConvertStringToNative(ReflectionUtils.ConstructTypeName(scriptType));
bool isTool = scriptType.IsDefined(typeof(ToolAttribute), inherit: false);
@@ -766,24 +758,6 @@ namespace Godot.Bridge
outTypeInfo->IsGenericTypeDefinition = scriptType.IsGenericTypeDefinition.ToGodotBool();
outTypeInfo->IsConstructedGenericType = scriptType.IsConstructedGenericType.ToGodotBool();
- static void AppendTypeName(StringBuilder sb, Type type)
- {
- sb.Append(type.Name);
- if (type.IsGenericType)
- {
- sb.Append('<');
- for (int i = 0; i < type.GenericTypeArguments.Length; i++)
- {
- Type typeArg = type.GenericTypeArguments[i];
- AppendTypeName(sb, typeArg);
- if (i != type.GenericTypeArguments.Length - 1)
- {
- sb.Append(", ");
- }
- }
- sb.Append('>');
- }
- }
}
[UnmanagedCallersOnly]
@@ -1097,7 +1071,7 @@ namespace Godot.Bridge
interopProperties[i] = interopProperty;
}
- using godot_string currentClassName = Marshaling.ConvertStringToNative(type.Name);
+ using godot_string currentClassName = Marshaling.ConvertStringToNative(ReflectionUtils.ConstructTypeName(type));
addPropInfoFunc(scriptPtr, &currentClassName, interopProperties, length);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
index ee605f8d8f..27989b5c81 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/ReflectionUtils.cs
@@ -1,5 +1,8 @@
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
+using System.Text;
#nullable enable
@@ -7,10 +10,186 @@ namespace Godot;
internal class ReflectionUtils
{
+ private static readonly HashSet<Type>? _tupleTypeSet;
+ private static readonly Dictionary<Type, string>? _builtinTypeNameDictionary;
+ private static readonly bool _isEditorHintCached;
+
+ static ReflectionUtils()
+ {
+ _isEditorHintCached = Engine.IsEditorHint();
+ if (!_isEditorHintCached)
+ {
+ return;
+ }
+
+ _tupleTypeSet = new HashSet<Type>
+ {
+ // ValueTuple with only one element should be treated as normal generic type.
+ //typeof(ValueTuple<>),
+ typeof(ValueTuple<,>),
+ typeof(ValueTuple<,,>),
+ typeof(ValueTuple<,,,>),
+ typeof(ValueTuple<,,,,>),
+ typeof(ValueTuple<,,,,,>),
+ typeof(ValueTuple<,,,,,,>),
+ typeof(ValueTuple<,,,,,,,>),
+ };
+
+ _builtinTypeNameDictionary ??= new Dictionary<Type, string>
+ {
+ { typeof(sbyte), "sbyte" },
+ { typeof(byte), "byte" },
+ { typeof(short), "short" },
+ { typeof(ushort), "ushort" },
+ { typeof(int), "int" },
+ { typeof(uint), "uint" },
+ { typeof(long), "long" },
+ { typeof(ulong), "ulong" },
+ { typeof(nint), "nint" },
+ { typeof(nuint), "nuint" },
+ { typeof(float), "float" },
+ { typeof(double), "double" },
+ { typeof(decimal), "decimal" },
+ { typeof(bool), "bool" },
+ { typeof(char), "char" },
+ { typeof(string), "string" },
+ { typeof(object), "object" },
+ };
+ }
+
public static Type? FindTypeInLoadedAssemblies(string assemblyName, string typeFullName)
{
return AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.GetName().Name == assemblyName)?
.GetType(typeFullName);
}
+
+ public static string ConstructTypeName(Type type)
+ {
+ if (!_isEditorHintCached)
+ {
+ return type.Name;
+ }
+
+ if (type is { IsArray: false, IsGenericType: false })
+ {
+ return GetSimpleTypeName(type);
+ }
+
+ var typeNameBuilder = new StringBuilder();
+ AppendType(typeNameBuilder, type);
+ return typeNameBuilder.ToString();
+
+ static void AppendType(StringBuilder sb, Type type)
+ {
+ if (type.IsArray)
+ {
+ AppendArray(sb, type);
+ }
+ else if (type.IsGenericType)
+ {
+ AppendGeneric(sb, type);
+ }
+ else
+ {
+ sb.Append(GetSimpleTypeName(type));
+ }
+ }
+
+ static void AppendArray(StringBuilder sb, Type type)
+ {
+ // Append inner most non-array element.
+ var elementType = type.GetElementType()!;
+ while (elementType.IsArray)
+ {
+ elementType = elementType.GetElementType()!;
+ }
+
+ AppendType(sb, elementType);
+ // Append brackets.
+ AppendArrayBrackets(sb, type);
+
+ static void AppendArrayBrackets(StringBuilder sb, Type type)
+ {
+ while (type != null && type.IsArray)
+ {
+ int rank = type.GetArrayRank();
+ sb.Append('[');
+ sb.Append(',', rank - 1);
+ sb.Append(']');
+ type = type.GetElementType();
+ }
+ }
+ }
+
+ static void AppendGeneric(StringBuilder sb, Type type)
+ {
+ var genericArgs = type.GenericTypeArguments;
+ var genericDefinition = type.GetGenericTypeDefinition();
+
+ // Nullable<T>
+ if (genericDefinition == typeof(Nullable<>))
+ {
+ AppendType(sb, genericArgs[0]);
+ sb.Append('?');
+ return;
+ }
+
+ // ValueTuple
+ Debug.Assert(_tupleTypeSet != null);
+ if (_tupleTypeSet.Contains(genericDefinition))
+ {
+ sb.Append('(');
+ while (true)
+ {
+ // We assume that ValueTuple has 1~8 elements.
+ // And the 8th element (TRest) is always another ValueTuple.
+
+ // This is a hard coded tuple element length check.
+ if (genericArgs.Length != 8)
+ {
+ AppendParamTypes(sb, genericArgs);
+ break;
+ }
+ else
+ {
+ AppendParamTypes(sb, genericArgs.AsSpan(0, 7));
+ sb.Append(", ");
+
+ // TRest should be a ValueTuple!
+ var nextTuple = genericArgs[7];
+
+ genericArgs = nextTuple.GenericTypeArguments;
+ }
+ }
+ sb.Append(')');
+ return;
+ }
+
+ // Normal generic
+ var typeName = type.Name.AsSpan();
+ sb.Append(typeName[..typeName.LastIndexOf('`')]);
+ sb.Append('<');
+ AppendParamTypes(sb, genericArgs);
+ sb.Append('>');
+
+ static void AppendParamTypes(StringBuilder sb, ReadOnlySpan<Type> genericArgs)
+ {
+ int n = genericArgs.Length - 1;
+ for (int i = 0; i < n; i += 1)
+ {
+ AppendType(sb, genericArgs[i]);
+ sb.Append(", ");
+ }
+
+ AppendType(sb, genericArgs[n]);
+ }
+ }
+
+ static string GetSimpleTypeName(Type type)
+ {
+ Debug.Assert(_builtinTypeNameDictionary != null);
+ return _builtinTypeNameDictionary.TryGetValue(type, out string? name) ? name : type.Name;
+ }
+ }
}