diff options
18 files changed, 479 insertions, 971 deletions
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs index 7008fb638f..d67e57edc2 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs @@ -268,8 +268,9 @@ namespace Godot.SourceGenerators if (parameters.Length > paramTypes.Length) return null; // Ignore incompatible method - return new GodotMethodData(method, paramTypes, parameters - .Select(p => p.Type).ToImmutableArray(), retType, retSymbol); + return new GodotMethodData(method, paramTypes, + parameters.Select(p => p.Type).ToImmutableArray(), + retType != null ? (retType.Value, retSymbol) : null); } public static IEnumerable<GodotMethodData> WhereHasGodotCompatibleSignature( @@ -330,10 +331,10 @@ namespace Godot.SourceGenerators public static string Path(this Location location) => location.SourceTree?.GetLineSpan(location.SourceSpan).Path - ?? location.GetLineSpan().Path; + ?? location.GetLineSpan().Path; public static int StartLine(this Location location) => location.SourceTree?.GetLineSpan(location.SourceSpan).StartLinePosition.Line - ?? location.GetLineSpan().StartLinePosition.Line; + ?? location.GetLineSpan().StartLinePosition.Line; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs index abd8079922..0760ea11bb 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GodotMemberData.cs @@ -6,20 +6,18 @@ namespace Godot.SourceGenerators public readonly struct GodotMethodData { public GodotMethodData(IMethodSymbol method, ImmutableArray<MarshalType> paramTypes, - ImmutableArray<ITypeSymbol> paramTypeSymbols, MarshalType? retType, ITypeSymbol? retSymbol) + ImmutableArray<ITypeSymbol> paramTypeSymbols, (MarshalType MarshalType, ITypeSymbol TypeSymbol)? retType) { Method = method; ParamTypes = paramTypes; ParamTypeSymbols = paramTypeSymbols; RetType = retType; - RetSymbol = retSymbol; } public IMethodSymbol Method { get; } public ImmutableArray<MarshalType> ParamTypes { get; } public ImmutableArray<ITypeSymbol> ParamTypeSymbols { get; } - public MarshalType? RetType { get; } - public ITypeSymbol? RetSymbol { get; } + public (MarshalType MarshalType, ITypeSymbol TypeSymbol)? RetType { get; } } public readonly struct GodotSignalDelegateData diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs index 4fdd40f638..5b3f677f87 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs @@ -304,240 +304,33 @@ namespace Godot.SourceGenerators { return marshalType switch { - MarshalType.Boolean => - source.Append(VariantUtils, ".ConvertToBool(", inputExpr, ")"), - MarshalType.Char => - source.Append("(char)", VariantUtils, ".ConvertToUInt16(", inputExpr, ")"), - MarshalType.SByte => - source.Append(VariantUtils, ".ConvertToInt8(", inputExpr, ")"), - MarshalType.Int16 => - source.Append(VariantUtils, ".ConvertToInt16(", inputExpr, ")"), - MarshalType.Int32 => - source.Append(VariantUtils, ".ConvertToInt32(", inputExpr, ")"), - MarshalType.Int64 => - source.Append(VariantUtils, ".ConvertToInt64(", inputExpr, ")"), - MarshalType.Byte => - source.Append(VariantUtils, ".ConvertToUInt8(", inputExpr, ")"), - MarshalType.UInt16 => - source.Append(VariantUtils, ".ConvertToUInt16(", inputExpr, ")"), - MarshalType.UInt32 => - source.Append(VariantUtils, ".ConvertToUInt32(", inputExpr, ")"), - MarshalType.UInt64 => - source.Append(VariantUtils, ".ConvertToUInt64(", inputExpr, ")"), - MarshalType.Single => - source.Append(VariantUtils, ".ConvertToFloat32(", inputExpr, ")"), - MarshalType.Double => - source.Append(VariantUtils, ".ConvertToFloat64(", inputExpr, ")"), - MarshalType.String => - source.Append(VariantUtils, ".ConvertToStringObject(", inputExpr, ")"), - MarshalType.Vector2 => - source.Append(VariantUtils, ".ConvertToVector2(", inputExpr, ")"), - MarshalType.Vector2i => - source.Append(VariantUtils, ".ConvertToVector2i(", inputExpr, ")"), - MarshalType.Rect2 => - source.Append(VariantUtils, ".ConvertToRect2(", inputExpr, ")"), - MarshalType.Rect2i => - source.Append(VariantUtils, ".ConvertToRect2i(", inputExpr, ")"), - MarshalType.Transform2D => - source.Append(VariantUtils, ".ConvertToTransform2D(", inputExpr, ")"), - MarshalType.Vector3 => - source.Append(VariantUtils, ".ConvertToVector3(", inputExpr, ")"), - MarshalType.Vector3i => - source.Append(VariantUtils, ".ConvertToVector3i(", inputExpr, ")"), - MarshalType.Basis => - source.Append(VariantUtils, ".ConvertToBasis(", inputExpr, ")"), - MarshalType.Quaternion => - source.Append(VariantUtils, ".ConvertToQuaternion(", inputExpr, ")"), - MarshalType.Transform3D => - source.Append(VariantUtils, ".ConvertToTransform3D(", inputExpr, ")"), - MarshalType.Vector4 => - source.Append(VariantUtils, ".ConvertToVector4(", inputExpr, ")"), - MarshalType.Vector4i => - source.Append(VariantUtils, ".ConvertToVector4i(", inputExpr, ")"), - MarshalType.Projection => - source.Append(VariantUtils, ".ConvertToProjection(", inputExpr, ")"), - MarshalType.AABB => - source.Append(VariantUtils, ".ConvertToAABB(", inputExpr, ")"), - MarshalType.Color => - source.Append(VariantUtils, ".ConvertToColor(", inputExpr, ")"), - MarshalType.Plane => - source.Append(VariantUtils, ".ConvertToPlane(", inputExpr, ")"), - MarshalType.Callable => - source.Append(VariantUtils, ".ConvertToCallableManaged(", inputExpr, ")"), - MarshalType.SignalInfo => - source.Append(VariantUtils, ".ConvertToSignalInfo(", inputExpr, ")"), - MarshalType.Enum => - source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), - ")", VariantUtils, ".ConvertToInt32(", inputExpr, ")"), - MarshalType.ByteArray => - source.Append(VariantUtils, ".ConvertAsPackedByteArrayToSystemArray(", inputExpr, ")"), - MarshalType.Int32Array => - source.Append(VariantUtils, ".ConvertAsPackedInt32ArrayToSystemArray(", inputExpr, ")"), - MarshalType.Int64Array => - source.Append(VariantUtils, ".ConvertAsPackedInt64ArrayToSystemArray(", inputExpr, ")"), - MarshalType.Float32Array => - source.Append(VariantUtils, ".ConvertAsPackedFloat32ArrayToSystemArray(", inputExpr, ")"), - MarshalType.Float64Array => - source.Append(VariantUtils, ".ConvertAsPackedFloat64ArrayToSystemArray(", inputExpr, ")"), - MarshalType.StringArray => - source.Append(VariantUtils, ".ConvertAsPackedStringArrayToSystemArray(", inputExpr, ")"), - MarshalType.Vector2Array => - source.Append(VariantUtils, ".ConvertAsPackedVector2ArrayToSystemArray(", inputExpr, ")"), - MarshalType.Vector3Array => - source.Append(VariantUtils, ".ConvertAsPackedVector3ArrayToSystemArray(", inputExpr, ")"), - MarshalType.ColorArray => - source.Append(VariantUtils, ".ConvertAsPackedColorArrayToSystemArray(", inputExpr, ")"), - MarshalType.GodotObjectOrDerivedArray => - source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<", - ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), - MarshalType.SystemArrayOfStringName => - source.Append(VariantUtils, ".ConvertToSystemArrayOfStringName(", inputExpr, ")"), - MarshalType.SystemArrayOfNodePath => - source.Append(VariantUtils, ".ConvertToSystemArrayOfNodePath(", inputExpr, ")"), - MarshalType.SystemArrayOfRID => - source.Append(VariantUtils, ".ConvertToSystemArrayOfRID(", inputExpr, ")"), - MarshalType.Variant => - source.Append("global::Godot.Variant.CreateCopyingBorrowed(", inputExpr, ")"), - MarshalType.GodotObjectOrDerived => - source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), - ")", VariantUtils, ".ConvertToGodotObject(", inputExpr, ")"), - MarshalType.StringName => - source.Append(VariantUtils, ".ConvertToStringNameObject(", inputExpr, ")"), - MarshalType.NodePath => - source.Append(VariantUtils, ".ConvertToNodePathObject(", inputExpr, ")"), - MarshalType.RID => - source.Append(VariantUtils, ".ConvertToRID(", inputExpr, ")"), - MarshalType.GodotDictionary => - source.Append(VariantUtils, ".ConvertToDictionaryObject(", inputExpr, ")"), - MarshalType.GodotArray => - source.Append(VariantUtils, ".ConvertToArrayObject(", inputExpr, ")"), + // For generic Godot collections, VariantUtils.ConvertTo<T> is slower, so we need this special case MarshalType.GodotGenericDictionary => source.Append(VariantUtils, ".ConvertToDictionaryObject<", ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ", - ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), + ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(", + inputExpr, ")"), MarshalType.GodotGenericArray => source.Append(VariantUtils, ".ConvertToArrayObject<", - ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), - _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, - "Received unexpected marshal type") + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(", + inputExpr, ")"), + _ => source.Append(VariantUtils, ".ConvertTo<", + typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), }; } - public static StringBuilder AppendManagedToNativeVariantExpr( - this StringBuilder source, string inputExpr, MarshalType marshalType) + public static StringBuilder AppendManagedToNativeVariantExpr(this StringBuilder source, + string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType) { return marshalType switch { - MarshalType.Boolean => - source.Append(VariantUtils, ".CreateFromBool(", inputExpr, ")"), - MarshalType.Char => - source.Append(VariantUtils, ".CreateFromInt((ushort)", inputExpr, ")"), - MarshalType.SByte => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Int16 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Int32 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Int64 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Byte => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.UInt16 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.UInt32 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.UInt64 => - source.Append(VariantUtils, ".CreateFromInt(", inputExpr, ")"), - MarshalType.Single => - source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"), - MarshalType.Double => - source.Append(VariantUtils, ".CreateFromFloat(", inputExpr, ")"), - MarshalType.String => - source.Append(VariantUtils, ".CreateFromString(", inputExpr, ")"), - MarshalType.Vector2 => - source.Append(VariantUtils, ".CreateFromVector2(", inputExpr, ")"), - MarshalType.Vector2i => - source.Append(VariantUtils, ".CreateFromVector2i(", inputExpr, ")"), - MarshalType.Rect2 => - source.Append(VariantUtils, ".CreateFromRect2(", inputExpr, ")"), - MarshalType.Rect2i => - source.Append(VariantUtils, ".CreateFromRect2i(", inputExpr, ")"), - MarshalType.Transform2D => - source.Append(VariantUtils, ".CreateFromTransform2D(", inputExpr, ")"), - MarshalType.Vector3 => - source.Append(VariantUtils, ".CreateFromVector3(", inputExpr, ")"), - MarshalType.Vector3i => - source.Append(VariantUtils, ".CreateFromVector3i(", inputExpr, ")"), - MarshalType.Basis => - source.Append(VariantUtils, ".CreateFromBasis(", inputExpr, ")"), - MarshalType.Quaternion => - source.Append(VariantUtils, ".CreateFromQuaternion(", inputExpr, ")"), - MarshalType.Transform3D => - source.Append(VariantUtils, ".CreateFromTransform3D(", inputExpr, ")"), - MarshalType.Vector4 => - source.Append(VariantUtils, ".CreateFromVector4(", inputExpr, ")"), - MarshalType.Vector4i => - source.Append(VariantUtils, ".CreateFromVector4i(", inputExpr, ")"), - MarshalType.Projection => - source.Append(VariantUtils, ".CreateFromProjection(", inputExpr, ")"), - MarshalType.AABB => - source.Append(VariantUtils, ".CreateFromAABB(", inputExpr, ")"), - MarshalType.Color => - source.Append(VariantUtils, ".CreateFromColor(", inputExpr, ")"), - MarshalType.Plane => - source.Append(VariantUtils, ".CreateFromPlane(", inputExpr, ")"), - MarshalType.Callable => - source.Append(VariantUtils, ".CreateFromCallable(", inputExpr, ")"), - MarshalType.SignalInfo => - source.Append(VariantUtils, ".CreateFromSignalInfo(", inputExpr, ")"), - MarshalType.Enum => - source.Append(VariantUtils, ".CreateFromInt((int)", inputExpr, ")"), - MarshalType.ByteArray => - source.Append(VariantUtils, ".CreateFromPackedByteArray(", inputExpr, ")"), - MarshalType.Int32Array => - source.Append(VariantUtils, ".CreateFromPackedInt32Array(", inputExpr, ")"), - MarshalType.Int64Array => - source.Append(VariantUtils, ".CreateFromPackedInt64Array(", inputExpr, ")"), - MarshalType.Float32Array => - source.Append(VariantUtils, ".CreateFromPackedFloat32Array(", inputExpr, ")"), - MarshalType.Float64Array => - source.Append(VariantUtils, ".CreateFromPackedFloat64Array(", inputExpr, ")"), - MarshalType.StringArray => - source.Append(VariantUtils, ".CreateFromPackedStringArray(", inputExpr, ")"), - MarshalType.Vector2Array => - source.Append(VariantUtils, ".CreateFromPackedVector2Array(", inputExpr, ")"), - MarshalType.Vector3Array => - source.Append(VariantUtils, ".CreateFromPackedVector3Array(", inputExpr, ")"), - MarshalType.ColorArray => - source.Append(VariantUtils, ".CreateFromPackedColorArray(", inputExpr, ")"), - MarshalType.GodotObjectOrDerivedArray => - source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"), - MarshalType.SystemArrayOfStringName => - source.Append(VariantUtils, ".CreateFromSystemArrayOfStringName(", inputExpr, ")"), - MarshalType.SystemArrayOfNodePath => - source.Append(VariantUtils, ".CreateFromSystemArrayOfNodePath(", inputExpr, ")"), - MarshalType.SystemArrayOfRID => - source.Append(VariantUtils, ".CreateFromSystemArrayOfRID(", inputExpr, ")"), - MarshalType.Variant => - source.Append(inputExpr, ".CopyNativeVariant()"), - MarshalType.GodotObjectOrDerived => - source.Append(VariantUtils, ".CreateFromGodotObject(", inputExpr, ")"), - MarshalType.StringName => - source.Append(VariantUtils, ".CreateFromStringName(", inputExpr, ")"), - MarshalType.NodePath => - source.Append(VariantUtils, ".CreateFromNodePath(", inputExpr, ")"), - MarshalType.RID => - source.Append(VariantUtils, ".CreateFromRID(", inputExpr, ")"), - MarshalType.GodotDictionary => - source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"), - MarshalType.GodotArray => - source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"), + // For generic Godot collections, VariantUtils.CreateFrom<T> is slower, so we need this special case MarshalType.GodotGenericDictionary => source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"), MarshalType.GodotGenericArray => source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"), - _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, - "Received unexpected marshal type") + _ => source.Append(VariantUtils, ".CreateFrom<", + typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"), }; } @@ -546,137 +339,30 @@ namespace Godot.SourceGenerators { return marshalType switch { - MarshalType.Boolean => source.Append(inputExpr, ".AsBool()"), - MarshalType.Char => source.Append(inputExpr, ".AsChar()"), - MarshalType.SByte => source.Append(inputExpr, ".AsSByte()"), - MarshalType.Int16 => source.Append(inputExpr, ".AsInt16()"), - MarshalType.Int32 => source.Append(inputExpr, ".AsInt32()"), - MarshalType.Int64 => source.Append(inputExpr, ".AsInt64()"), - MarshalType.Byte => source.Append(inputExpr, ".AsByte()"), - MarshalType.UInt16 => source.Append(inputExpr, ".AsUInt16()"), - MarshalType.UInt32 => source.Append(inputExpr, ".AsUInt32()"), - MarshalType.UInt64 => source.Append(inputExpr, ".AsUInt64()"), - MarshalType.Single => source.Append(inputExpr, ".AsSingle()"), - MarshalType.Double => source.Append(inputExpr, ".AsDouble()"), - MarshalType.String => source.Append(inputExpr, ".AsString()"), - MarshalType.Vector2 => source.Append(inputExpr, ".AsVector2()"), - MarshalType.Vector2i => source.Append(inputExpr, ".AsVector2i()"), - MarshalType.Rect2 => source.Append(inputExpr, ".AsRect2()"), - MarshalType.Rect2i => source.Append(inputExpr, ".AsRect2i()"), - MarshalType.Transform2D => source.Append(inputExpr, ".AsTransform2D()"), - MarshalType.Vector3 => source.Append(inputExpr, ".AsVector3()"), - MarshalType.Vector3i => source.Append(inputExpr, ".AsVector3i()"), - MarshalType.Basis => source.Append(inputExpr, ".AsBasis()"), - MarshalType.Quaternion => source.Append(inputExpr, ".AsQuaternion()"), - MarshalType.Transform3D => source.Append(inputExpr, ".AsTransform3D()"), - MarshalType.Vector4 => source.Append(inputExpr, ".AsVector4()"), - MarshalType.Vector4i => source.Append(inputExpr, ".AsVector4i()"), - MarshalType.Projection => source.Append(inputExpr, ".AsProjection()"), - MarshalType.AABB => source.Append(inputExpr, ".AsAABB()"), - MarshalType.Color => source.Append(inputExpr, ".AsColor()"), - MarshalType.Plane => source.Append(inputExpr, ".AsPlane()"), - MarshalType.Callable => source.Append(inputExpr, ".AsCallable()"), - MarshalType.SignalInfo => source.Append(inputExpr, ".AsSignalInfo()"), - MarshalType.Enum => - source.Append("(", typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsInt64()"), - MarshalType.ByteArray => source.Append(inputExpr, ".AsByteArray()"), - MarshalType.Int32Array => source.Append(inputExpr, ".AsInt32Array()"), - MarshalType.Int64Array => source.Append(inputExpr, ".AsInt64Array()"), - MarshalType.Float32Array => source.Append(inputExpr, ".AsFloat32Array()"), - MarshalType.Float64Array => source.Append(inputExpr, ".AsFloat64Array()"), - MarshalType.StringArray => source.Append(inputExpr, ".AsStringArray()"), - MarshalType.Vector2Array => source.Append(inputExpr, ".AsVector2Array()"), - MarshalType.Vector3Array => source.Append(inputExpr, ".AsVector3Array()"), - MarshalType.ColorArray => source.Append(inputExpr, ".AsColorArray()"), - MarshalType.GodotObjectOrDerivedArray => source.Append(inputExpr, ".AsGodotObjectArray<", - ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">()"), - MarshalType.SystemArrayOfStringName => source.Append(inputExpr, ".AsSystemArrayOfStringName()"), - MarshalType.SystemArrayOfNodePath => source.Append(inputExpr, ".AsSystemArrayOfNodePath()"), - MarshalType.SystemArrayOfRID => source.Append(inputExpr, ".AsSystemArrayOfRID()"), - MarshalType.Variant => source.Append(inputExpr), - MarshalType.GodotObjectOrDerived => source.Append("(", - typeSymbol.FullQualifiedNameIncludeGlobal(), ")", inputExpr, ".AsGodotObject()"), - MarshalType.StringName => source.Append(inputExpr, ".AsStringName()"), - MarshalType.NodePath => source.Append(inputExpr, ".AsNodePath()"), - MarshalType.RID => source.Append(inputExpr, ".AsRID()"), - MarshalType.GodotDictionary => source.Append(inputExpr, ".AsGodotDictionary()"), - MarshalType.GodotArray => source.Append(inputExpr, ".AsGodotArray()"), - MarshalType.GodotGenericDictionary => source.Append(inputExpr, ".AsGodotDictionary<", - ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ", - ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"), - MarshalType.GodotGenericArray => source.Append(inputExpr, ".AsGodotArray<", - ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"), - _ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, - "Received unexpected marshal type") + // For generic Godot collections, Variant.As<T> is slower, so we need this special case + MarshalType.GodotGenericDictionary => + source.Append(inputExpr, ".AsGodotDictionary<", + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ", + ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"), + MarshalType.GodotGenericArray => + source.Append(inputExpr, ".AsGodotArray<", + ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"), + _ => source.Append(inputExpr, ".As<", + typeSymbol.FullQualifiedNameIncludeGlobal(), ">()") }; } public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source, - string inputExpr, MarshalType marshalType) + string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType) { - switch (marshalType) + return marshalType switch { - case MarshalType.Boolean: - case MarshalType.Char: - case MarshalType.SByte: - case MarshalType.Int16: - case MarshalType.Int32: - case MarshalType.Int64: - case MarshalType.Byte: - case MarshalType.UInt16: - case MarshalType.UInt32: - case MarshalType.UInt64: - case MarshalType.Single: - case MarshalType.Double: - case MarshalType.String: - case MarshalType.Vector2: - case MarshalType.Vector2i: - case MarshalType.Rect2: - case MarshalType.Rect2i: - case MarshalType.Transform2D: - case MarshalType.Vector3: - case MarshalType.Vector3i: - case MarshalType.Basis: - case MarshalType.Quaternion: - case MarshalType.Transform3D: - case MarshalType.Vector4: - case MarshalType.Vector4i: - case MarshalType.Projection: - case MarshalType.AABB: - case MarshalType.Color: - case MarshalType.Plane: - case MarshalType.Callable: - case MarshalType.SignalInfo: - case MarshalType.ByteArray: - case MarshalType.Int32Array: - case MarshalType.Int64Array: - case MarshalType.Float32Array: - case MarshalType.Float64Array: - case MarshalType.StringArray: - case MarshalType.Vector2Array: - case MarshalType.Vector3Array: - case MarshalType.ColorArray: - case MarshalType.GodotObjectOrDerivedArray: - case MarshalType.SystemArrayOfStringName: - case MarshalType.SystemArrayOfNodePath: - case MarshalType.SystemArrayOfRID: - case MarshalType.GodotObjectOrDerived: - case MarshalType.StringName: - case MarshalType.NodePath: - case MarshalType.RID: - case MarshalType.GodotDictionary: - case MarshalType.GodotArray: - case MarshalType.GodotGenericDictionary: - case MarshalType.GodotGenericArray: - return source.Append("Variant.CreateFrom(", inputExpr, ")"); - case MarshalType.Enum: - return source.Append("Variant.CreateFrom((long)", inputExpr, ")"); - case MarshalType.Variant: - return source.Append(inputExpr); - default: - throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType, - "Received unexpected marshal type"); - } + // For generic Godot collections, Variant.From<T> is slower, so we need this special case + MarshalType.GodotGenericDictionary or MarshalType.GodotGenericArray => + source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"), + _ => source.Append("global::Godot.Variant.From<", + typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")") + }; } } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs index 2f51018293..f79909589e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs @@ -135,7 +135,8 @@ namespace Godot.SourceGenerators source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - source.Append($" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n"); + source.Append( + $" public new class MethodName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.MethodName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup @@ -297,7 +298,7 @@ namespace Godot.SourceGenerators if (method.RetType != null) { - returnVal = DeterminePropertyInfo(method.RetType.Value, name: string.Empty); + returnVal = DeterminePropertyInfo(method.RetType.Value.MarshalType, name: string.Empty); } else { @@ -391,7 +392,8 @@ namespace Godot.SourceGenerators { source.Append(" ret = "); - source.AppendManagedToNativeVariantExpr("callRet", method.RetType.Value); + source.AppendManagedToNativeVariantExpr("callRet", + method.RetType.Value.TypeSymbol, method.RetType.Value.MarshalType); source.Append(";\n"); source.Append(" return true;\n"); diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index 252f162b0c..6c4206a575 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -124,7 +124,8 @@ namespace Godot.SourceGenerators source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - source.Append($" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n"); + source.Append( + $" public new class PropertyName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.PropertyName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup @@ -199,14 +200,14 @@ namespace Godot.SourceGenerators foreach (var property in godotClassProperties) { GeneratePropertyGetter(property.PropertySymbol.Name, - property.Type, source, isFirstEntry); + property.PropertySymbol.Type, property.Type, source, isFirstEntry); isFirstEntry = false; } foreach (var field in godotClassFields) { GeneratePropertyGetter(field.FieldSymbol.Name, - field.Type, source, isFirstEntry); + field.FieldSymbol.Type, field.Type, source, isFirstEntry); isFirstEntry = false; } @@ -303,6 +304,7 @@ namespace Godot.SourceGenerators private static void GeneratePropertyGetter( string propertyMemberName, + ITypeSymbol propertyTypeSymbol, MarshalType propertyMarshalType, StringBuilder source, bool isFirstEntry @@ -317,7 +319,8 @@ namespace Godot.SourceGenerators .Append(propertyMemberName) .Append(") {\n") .Append(" value = ") - .AppendManagedToNativeVariantExpr("this." + propertyMemberName, propertyMarshalType) + .AppendManagedToNativeVariantExpr("this." + propertyMemberName, + propertyTypeSymbol, propertyMarshalType) .Append(";\n") .Append(" return true;\n") .Append(" }\n"); @@ -376,7 +379,8 @@ namespace Godot.SourceGenerators if (propertyUsage != PropertyUsageFlags.Category && attr.ConstructorArguments.Length > 1) hintString = attr.ConstructorArguments[1].Value?.ToString(); - yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString, propertyUsage.Value, true); + yield return new PropertyInfo(VariantType.Nil, name, PropertyHint.None, hintString, + propertyUsage.Value, true); } } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index 63339b3487..aa9dd9583e 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -174,7 +174,8 @@ namespace Godot.SourceGenerators } else { - var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors.Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault(); + var propertyGet = propertyDeclarationSyntax.AccessorList?.Accessors + .Where(a => a.Keyword.IsKind(SyntaxKind.GetKeyword)).FirstOrDefault(); if (propertyGet != null) { if (propertyGet.ExpressionBody != null) @@ -200,7 +201,8 @@ namespace Godot.SourceGenerators { var returns = propertyGet.DescendantNodes().OfType<ReturnStatementSyntax>(); if (returns.Count() == 1) - {// Generate only single return + { + // Generate only single return var returnStatementSyntax = returns.Single(); if (returnStatementSyntax.Expression is IdentifierNameSyntax identifierNameSyntax) { @@ -277,7 +279,8 @@ namespace Godot.SourceGenerators { source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - string dictionaryType = "System.Collections.Generic.Dictionary<Godot.StringName, object>"; + string dictionaryType = + "global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>"; source.Append("#if TOOLS\n"); source.Append(" internal new static "); @@ -304,7 +307,8 @@ namespace Godot.SourceGenerators source.Append(" values.Add(PropertyName."); source.Append(exportedMember.Name); source.Append(", "); - source.Append(defaultValueLocalName); + source.AppendManagedToVariantExpr(defaultValueLocalName, + exportedMember.TypeSymbol, exportedMember.Type); source.Append(");\n"); } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs index ed877cbd17..821f3af75f 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs @@ -162,7 +162,8 @@ namespace Godot.SourceGenerators source.Append(" info.AddProperty(PropertyName.") .Append(propertyName) .Append(", ") - .AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type) + .AppendManagedToVariantExpr(string.Concat("this.", propertyName), + property.PropertySymbol.Type, property.Type) .Append(");\n"); } @@ -175,7 +176,8 @@ namespace Godot.SourceGenerators source.Append(" info.AddProperty(PropertyName.") .Append(fieldName) .Append(", ") - .AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type) + .AppendManagedToVariantExpr(string.Concat("this.", fieldName), + field.FieldSymbol.Type, field.Type) .Append(");\n"); } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs index 119cc9d4f0..ba6c10aa31 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs @@ -176,7 +176,8 @@ namespace Godot.SourceGenerators source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n"); - source.Append($" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n"); + source.Append( + $" public new class SignalName : {symbol.BaseType.FullQualifiedNameIncludeGlobal()}.SignalName {{\n"); // Generate cached StringNames for methods and properties, for fast lookup @@ -236,7 +237,8 @@ namespace Godot.SourceGenerators .Append(signalName) .Append(";\n"); - source.Append($" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n"); + source.Append( + $" /// <inheritdoc cref=\"{signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()}\"/>\n"); source.Append(" public event ") .Append(signalDelegate.DelegateSymbol.FullQualifiedNameIncludeGlobal()) @@ -351,7 +353,7 @@ namespace Godot.SourceGenerators if (invokeMethodData.RetType != null) { - returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value, name: string.Empty); + returnVal = DeterminePropertyInfo(invokeMethodData.RetType.Value.MarshalType, name: string.Empty); } else { diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs index 89364d1c02..54969920cb 100644 --- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs +++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs @@ -455,7 +455,7 @@ namespace GodotTools _menuPopup.IdPressed += _MenuOptionPressed; // External editor settings - EditorDef("mono/editor/external_editor", ExternalEditorId.None); + EditorDef("mono/editor/external_editor", Variant.From(ExternalEditorId.None)); string settingsHintStr = "Disabled"; diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs index acb7cc3ab0..45ae7eb86b 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Globals.cs @@ -1,3 +1,4 @@ +using Godot; using Godot.NativeInterop; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -8,30 +9,31 @@ namespace GodotTools.Internals { public static float EditorScale => Internal.godot_icall_Globals_EditorScale(); - public static unsafe object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) + // ReSharper disable once UnusedMethodReturnValue.Global + public static Variant GlobalDef(string setting, Variant defaultValue, bool restartIfChanged = false) { using godot_string settingIn = Marshaling.ConvertStringToNative(setting); - using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue); - Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); - using (result) - return Marshaling.ConvertVariantToManagedObject(result); + using godot_variant defaultValueIn = defaultValue.CopyNativeVariant(); + Internal.godot_icall_Globals_GlobalDef(settingIn, defaultValueIn, restartIfChanged, + out godot_variant result); + return Variant.CreateTakingOwnershipOfDisposableValue(result); } - public static unsafe object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) + // ReSharper disable once UnusedMethodReturnValue.Global + public static Variant EditorDef(string setting, Variant defaultValue, bool restartIfChanged = false) { using godot_string settingIn = Marshaling.ConvertStringToNative(setting); - using godot_variant defaultValueIn = Marshaling.ConvertManagedObjectToVariant(defaultValue); - Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, out godot_variant result); - using (result) - return Marshaling.ConvertVariantToManagedObject(result); + using godot_variant defaultValueIn = defaultValue.CopyNativeVariant(); + Internal.godot_icall_Globals_EditorDef(settingIn, defaultValueIn, restartIfChanged, + out godot_variant result); + return Variant.CreateTakingOwnershipOfDisposableValue(result); } - public static object EditorShortcut(string setting) + public static Variant EditorShortcut(string setting) { using godot_string settingIn = Marshaling.ConvertStringToNative(setting); Internal.godot_icall_Globals_EditorShortcut(settingIn, out godot_variant result); - using (result) - return Marshaling.ConvertVariantToManagedObject(result); + return Variant.CreateTakingOwnershipOfDisposableValue(result); } [SuppressMessage("ReSharper", "InconsistentNaming")] diff --git a/modules/mono/editor/bindings_generator.cpp b/modules/mono/editor/bindings_generator.cpp index 9185506776..9f0bc3fbe3 100644 --- a/modules/mono/editor/bindings_generator.cpp +++ b/modules/mono/editor/bindings_generator.cpp @@ -2837,9 +2837,6 @@ 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.cs_variant_to_managed = "(%1)VariantUtils.ConvertToGodotObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromGodotObject(%0)"; - itype.c_out = "%5return "; itype.c_out += C_METHOD_UNMANAGED_GET_MANAGED; itype.c_out += itype.is_ref_counted ? "(%1.Reference);\n" : "(%1);\n"; @@ -3218,8 +3215,6 @@ bool BindingsGenerator::_populate_object_type_interfaces() { enum_itype.cname = StringName(enum_itype.name); enum_itype.proxy_name = itype.proxy_name + "." + enum_proxy_name; TypeInterface::postsetup_enum_type(enum_itype); - enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)"; - enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)"; enum_types.insert(enum_itype.cname, enum_itype); } @@ -3448,16 +3443,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_type_in = #m_type "*"; \ - itype.c_type_out = itype.cs_type; \ - itype.cs_in_expr = "&%0"; \ - itype.cs_in_expr_is_unsafe = true; \ - itype.cs_variant_to_managed = "VariantUtils.ConvertTo%2(%0)"; \ - itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \ - 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_expr = "&%0"; \ + itype.cs_in_expr_is_unsafe = true; \ + builtin_types.insert(itype.cname, itype); \ } INSERT_STRUCT_TYPE(Vector2) @@ -3488,8 +3481,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { 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"; - itype.cs_variant_to_managed = "VariantUtils.ConvertToBool(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromBool(%0)"; builtin_types.insert(itype.cname, itype); // Integer types @@ -3510,8 +3501,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { 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"; \ - itype.cs_variant_to_managed = "VariantUtils.ConvertTo" m_int_struct_name "(%0)"; \ - itype.cs_managed_to_variant = "VariantUtils.CreateFromInt(%0)"; \ builtin_types.insert(itype.cname, itype); \ } @@ -3547,8 +3536,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { 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"; - itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat32(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)"; builtin_types.insert(itype.cname, itype); // double @@ -3562,8 +3549,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { 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"; - itype.cs_variant_to_managed = "VariantUtils.ConvertToFloat64(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromFloat(%0)"; builtin_types.insert(itype.cname, itype); } @@ -3581,8 +3566,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = true; itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromString(%1);\n"; - itype.cs_variant_to_managed = "VariantUtils.ConvertToStringObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromString(%0)"; builtin_types.insert(itype.cname, itype); // StringName @@ -3601,8 +3584,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringName(%1);\n"; itype.c_type_is_disposable_struct = false; // [c_out] takes ownership itype.c_ret_needs_default_initialization = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToStringNameObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromStringName(%0)"; builtin_types.insert(itype.cname, itype); // NodePath @@ -3620,8 +3601,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { 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; - itype.cs_variant_to_managed = "VariantUtils.ConvertToNodePathObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromNodePath(%0)"; builtin_types.insert(itype.cname, itype); // RID @@ -3634,8 +3613,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type = itype.cs_type; itype.c_type_in = itype.c_type; itype.c_type_out = itype.c_type; - itype.cs_variant_to_managed = "VariantUtils.ConvertToRID(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromRID(%0)"; builtin_types.insert(itype.cname, itype); // Variant @@ -3652,8 +3629,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { 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; - itype.cs_variant_to_managed = "Variant.CreateCopyingBorrowed(%0)"; - itype.cs_managed_to_variant = "%0.CopyNativeVariant()"; builtin_types.insert(itype.cname, itype); // Callable @@ -3666,8 +3641,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = "in " + itype.cs_type; itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToCallableManaged(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromCallable(%0)"; builtin_types.insert(itype.cname, itype); // Signal @@ -3684,8 +3657,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = "in " + itype.cs_type; itype.c_type_out = itype.cs_type; itype.c_type_is_disposable_struct = true; - itype.cs_variant_to_managed = "VariantUtils.ConvertToSignalInfo(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromSignalInfo(%0)"; builtin_types.insert(itype.cname, itype); // VarArg (fictitious type to represent variable arguments) @@ -3715,8 +3686,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.c_type_in = itype.proxy_name; \ itype.c_type_out = itype.proxy_name; \ itype.c_type_is_disposable_struct = true; \ - itype.cs_variant_to_managed = "VariantUtils.ConvertAs%2ToSystemArray(%0)"; \ - itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \ builtin_types.insert(itype.name, itype); \ } @@ -3752,8 +3721,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { 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; - itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)"; builtin_types.insert(itype.cname, itype); // Array_@generic @@ -3761,6 +3728,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Array_@generic"; itype.cname = itype.name; itype.cs_out = "%5return new %2(%0(%1));"; + // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case + itype.cs_variant_to_managed = "VariantUtils.ConvertToArrayObject(%0)"; + itype.cs_managed_to_variant = "VariantUtils.CreateFromArray(%0)"; builtin_types.insert(itype.cname, itype); // Dictionary @@ -3778,8 +3748,6 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { 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; - itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)"; - itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)"; builtin_types.insert(itype.cname, itype); // Dictionary_@generic @@ -3787,6 +3755,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() { itype.name = "Dictionary_@generic"; itype.cname = itype.name; itype.cs_out = "%5return new %2(%0(%1));"; + // For generic Godot collections, Variant.From<T>/As<T> is slower, so we need this special case + itype.cs_variant_to_managed = "VariantUtils.ConvertToDictionaryObject(%0)"; + itype.cs_managed_to_variant = "VariantUtils.CreateFromDictionary(%0)"; builtin_types.insert(itype.cname, itype); // void (fictitious type to represent the return type of methods that do not return anything) @@ -3852,8 +3823,6 @@ void BindingsGenerator::_populate_global_constants() { enum_itype.cname = ienum.cname; enum_itype.proxy_name = enum_itype.name; TypeInterface::postsetup_enum_type(enum_itype); - enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)"; - enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)"; enum_types.insert(enum_itype.cname, enum_itype); int prefix_length = _determine_enum_prefix(ienum); @@ -3886,8 +3855,6 @@ void BindingsGenerator::_populate_global_constants() { enum_itype.cname = enum_cname; enum_itype.proxy_name = enum_itype.name; TypeInterface::postsetup_enum_type(enum_itype); - enum_itype.cs_variant_to_managed = "(%1)VariantUtils.ConvertToInt32(%0)"; - enum_itype.cs_managed_to_variant = "VariantUtils.CreateFromInt((int)%0)"; enum_types.insert(enum_itype.cname, enum_itype); } } diff --git a/modules/mono/editor/bindings_generator.h b/modules/mono/editor/bindings_generator.h index a479c44368..6d172f4fb8 100644 --- a/modules/mono/editor/bindings_generator.h +++ b/modules/mono/editor/bindings_generator.h @@ -209,7 +209,7 @@ class BindingsGenerator { String name; StringName cname; - int type_parameter_count; + int type_parameter_count = 0; /** * Identifier name of the base class. @@ -514,7 +514,12 @@ class BindingsGenerator { static void postsetup_enum_type(TypeInterface &r_enum_itype); - TypeInterface() {} + TypeInterface() { + static String default_cs_variant_to_managed = "VariantUtils.ConvertTo<%1>(%0)"; + static String default_cs_managed_to_variant = "VariantUtils.CreateFrom<%1>(%0)"; + cs_variant_to_managed = default_cs_variant_to_managed; + cs_managed_to_variant = default_cs_managed_to_variant; + } }; struct InternalCall { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs index 1b9f63c22d..130776499b 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Array.cs @@ -418,8 +418,8 @@ namespace Godot.Collections { for (int i = 0; i < count; i++) { - object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]); - array.SetValue(obj, index); + object boxedVariant = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]); + array.SetValue(boxedVariant, index); index++; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs index d6fad391b6..e6a8054ae2 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs @@ -159,7 +159,7 @@ namespace Godot.Bridge for (int i = 0; i < paramCount; i++) { - invokeParams[i] = Marshaling.ConvertVariantToManagedObjectOfType( + invokeParams[i] = DelegateUtils.RuntimeTypeConversionHelper.ConvertToObjectOfType( *args[i], parameters[i].ParameterType); } @@ -832,7 +832,8 @@ namespace Godot.Bridge } else { - interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_info)))!; + interopProperties = ((godotsharp_property_info*)NativeMemory.Alloc( + (nuint)length, (nuint)sizeof(godotsharp_property_info)))!; } try @@ -858,8 +859,8 @@ namespace Godot.Bridge addPropInfoFunc(scriptPtr, ¤tClassName, interopProperties, length); - // We're borrowing the StringName's without making an owning copy, so the - // managed collection needs to be kept alive until `addPropInfoFunc` returns. + // We're borrowing the native value of the StringName entries. + // The dictionary needs to be kept alive until `addPropInfoFunc` returns. GC.KeepAlive(properties); } finally @@ -884,12 +885,7 @@ namespace Godot.Bridge { // Careful with padding... public godot_string_name Name; // Not owned - public godot_variant Value; - - public void Dispose() - { - Value.Dispose(); - } + public godot_variant Value; // Not owned } [UnmanagedCallersOnly] @@ -928,10 +924,35 @@ namespace Godot.Bridge if (getGodotPropertyDefaultValuesMethod == null) return; - var defaultValues = (Dictionary<StringName, object>?) - getGodotPropertyDefaultValuesMethod.Invoke(null, null); + var defaultValuesObj = getGodotPropertyDefaultValuesMethod.Invoke(null, null); + + if (defaultValuesObj == null) + return; + + Dictionary<StringName, Variant> defaultValues; + + if (defaultValuesObj is Dictionary<StringName, object> defaultValuesLegacy) + { + // We have to support this for some time, otherwise this could cause data loss for projects + // built with previous releases. Ideally, we should remove this before Godot 4.0 stable. + + if (defaultValuesLegacy.Count <= 0) + return; - if (defaultValues == null || defaultValues.Count <= 0) + defaultValues = new(); + + foreach (var pair in defaultValuesLegacy) + { + defaultValues[pair.Key] = Variant.CreateTakingOwnershipOfDisposableValue( + DelegateUtils.RuntimeTypeConversionHelper.ConvertToVariant(pair.Value)); + } + } + else + { + defaultValues = (Dictionary<StringName, Variant>)defaultValuesObj; + } + + if (defaultValues.Count <= 0) return; int length = defaultValues.Count; @@ -952,7 +973,8 @@ namespace Godot.Bridge } else { - interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc((nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!; + interopDefaultValues = ((godotsharp_property_def_val_pair*)NativeMemory.Alloc( + (nuint)length, (nuint)sizeof(godotsharp_property_def_val_pair)))!; } try @@ -963,7 +985,7 @@ namespace Godot.Bridge godotsharp_property_def_val_pair interopProperty = new() { Name = (godot_string_name)defaultValuePair.Key.NativeValue, // Not owned - Value = Marshaling.ConvertManagedObjectToVariant(defaultValuePair.Value) + Value = (godot_variant)defaultValuePair.Value.NativeVar // Not owned }; interopDefaultValues[i] = interopProperty; @@ -973,15 +995,12 @@ namespace Godot.Bridge addDefValFunc(scriptPtr, interopDefaultValues, length); - // We're borrowing the StringName's without making an owning copy, so the - // managed collection needs to be kept alive until `addDefValFunc` returns. + // We're borrowing the native value of the StringName and Variant entries. + // The dictionary needs to be kept alive until `addDefValFunc` returns. GC.KeepAlive(defaultValues); } finally { - for (int i = 0; i < length; i++) - interopDefaultValues[i].Dispose(); - if (!useStack) NativeMemory.Free(interopDefaultValues); } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs index d19e0c08f2..a3cfecfaa6 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs @@ -186,7 +186,7 @@ namespace Godot writer.Write(field.Name); var fieldValue = field.GetValue(target); - using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue); + using var fieldValueVariant = RuntimeTypeConversionHelper.ConvertToVariant(fieldValue); byte[] valueBuffer = VarToBytes(fieldValueVariant); writer.Write(valueBuffer.Length); writer.Write(valueBuffer); @@ -443,7 +443,14 @@ namespace Godot FieldInfo? fieldInfo = targetType.GetField(name, BindingFlags.Instance | BindingFlags.Public); - fieldInfo?.SetValue(recreatedTarget, GD.BytesToVar(valueBuffer)); + + if (fieldInfo != null) + { + var variantValue = GD.BytesToVar(valueBuffer); + object? managedValue = RuntimeTypeConversionHelper.ConvertToObjectOfType( + (godot_variant)variantValue.NativeVar, fieldInfo.FieldType); + fieldInfo.SetValue(recreatedTarget, managedValue); + } } @delegate = Delegate.CreateDelegate(delegateType, recreatedTarget, methodInfo, @@ -537,5 +544,269 @@ namespace Godot return type; } + + internal static class RuntimeTypeConversionHelper + { + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + public static godot_variant ConvertToVariant(object? obj) + { + if (obj == null) + return default; + + switch (obj) + { + case bool @bool: + return VariantUtils.CreateFrom(@bool); + case char @char: + return VariantUtils.CreateFrom(@char); + case sbyte int8: + return VariantUtils.CreateFrom(int8); + case short int16: + return VariantUtils.CreateFrom(int16); + case int int32: + return VariantUtils.CreateFrom(int32); + case long int64: + return VariantUtils.CreateFrom(int64); + case byte uint8: + return VariantUtils.CreateFrom(uint8); + case ushort uint16: + return VariantUtils.CreateFrom(uint16); + case uint uint32: + return VariantUtils.CreateFrom(uint32); + case ulong uint64: + return VariantUtils.CreateFrom(uint64); + case float @float: + return VariantUtils.CreateFrom(@float); + case double @double: + return VariantUtils.CreateFrom(@double); + case Vector2 vector2: + return VariantUtils.CreateFrom(vector2); + case Vector2i vector2I: + return VariantUtils.CreateFrom(vector2I); + case Rect2 rect2: + return VariantUtils.CreateFrom(rect2); + case Rect2i rect2I: + return VariantUtils.CreateFrom(rect2I); + case Transform2D transform2D: + return VariantUtils.CreateFrom(transform2D); + case Vector3 vector3: + return VariantUtils.CreateFrom(vector3); + case Vector3i vector3I: + return VariantUtils.CreateFrom(vector3I); + case Vector4 vector4: + return VariantUtils.CreateFrom(vector4); + case Vector4i vector4I: + return VariantUtils.CreateFrom(vector4I); + case Basis basis: + return VariantUtils.CreateFrom(basis); + case Quaternion quaternion: + return VariantUtils.CreateFrom(quaternion); + case Transform3D transform3d: + return VariantUtils.CreateFrom(transform3d); + case Projection projection: + return VariantUtils.CreateFrom(projection); + case AABB aabb: + return VariantUtils.CreateFrom(aabb); + case Color color: + return VariantUtils.CreateFrom(color); + case Plane plane: + return VariantUtils.CreateFrom(plane); + case Callable callable: + return VariantUtils.CreateFrom(callable); + case SignalInfo signalInfo: + return VariantUtils.CreateFrom(signalInfo); + case string @string: + return VariantUtils.CreateFrom(@string); + case byte[] byteArray: + return VariantUtils.CreateFrom(byteArray); + case int[] int32Array: + return VariantUtils.CreateFrom(int32Array); + case long[] int64Array: + return VariantUtils.CreateFrom(int64Array); + case float[] floatArray: + return VariantUtils.CreateFrom(floatArray); + case double[] doubleArray: + return VariantUtils.CreateFrom(doubleArray); + case string[] stringArray: + return VariantUtils.CreateFrom(stringArray); + case Vector2[] vector2Array: + return VariantUtils.CreateFrom(vector2Array); + case Vector3[] vector3Array: + return VariantUtils.CreateFrom(vector3Array); + case Color[] colorArray: + return VariantUtils.CreateFrom(colorArray); + case StringName[] stringNameArray: + return VariantUtils.CreateFrom(stringNameArray); + case NodePath[] nodePathArray: + return VariantUtils.CreateFrom(nodePathArray); + case RID[] ridArray: + return VariantUtils.CreateFrom(ridArray); + case Godot.Object[] godotObjectArray: + return VariantUtils.CreateFrom(godotObjectArray); + case StringName stringName: + return VariantUtils.CreateFrom(stringName); + case NodePath nodePath: + return VariantUtils.CreateFrom(nodePath); + case RID rid: + return VariantUtils.CreateFrom(rid); + case Collections.Dictionary godotDictionary: + return VariantUtils.CreateFrom(godotDictionary); + case Collections.Array godotArray: + return VariantUtils.CreateFrom(godotArray); + case Variant variant: + return VariantUtils.CreateFrom(variant); + case Godot.Object godotObject: + return VariantUtils.CreateFrom(godotObject); + case Enum @enum: + return VariantUtils.CreateFrom(Convert.ToInt64(@enum)); + case Collections.IGenericGodotDictionary godotDictionary: + return VariantUtils.CreateFrom(godotDictionary.UnderlyingDictionary); + case Collections.IGenericGodotArray godotArray: + return VariantUtils.CreateFrom(godotArray.UnderlyingArray); + } + + GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + + obj.GetType().FullName + "."); + return new godot_variant(); + } + + private delegate object? ConvertToSystemObjectFunc(in godot_variant managed); + + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + // ReSharper disable once RedundantNameQualifier + private static readonly System.Collections.Generic.Dictionary<Type, ConvertToSystemObjectFunc> + ToSystemObjectFuncByType = new() + { + [typeof(bool)] = (in godot_variant variant) => VariantUtils.ConvertTo<bool>(variant), + [typeof(char)] = (in godot_variant variant) => VariantUtils.ConvertTo<char>(variant), + [typeof(sbyte)] = (in godot_variant variant) => VariantUtils.ConvertTo<sbyte>(variant), + [typeof(short)] = (in godot_variant variant) => VariantUtils.ConvertTo<short>(variant), + [typeof(int)] = (in godot_variant variant) => VariantUtils.ConvertTo<int>(variant), + [typeof(long)] = (in godot_variant variant) => VariantUtils.ConvertTo<long>(variant), + [typeof(byte)] = (in godot_variant variant) => VariantUtils.ConvertTo<byte>(variant), + [typeof(ushort)] = (in godot_variant variant) => VariantUtils.ConvertTo<ushort>(variant), + [typeof(uint)] = (in godot_variant variant) => VariantUtils.ConvertTo<uint>(variant), + [typeof(ulong)] = (in godot_variant variant) => VariantUtils.ConvertTo<ulong>(variant), + [typeof(float)] = (in godot_variant variant) => VariantUtils.ConvertTo<float>(variant), + [typeof(double)] = (in godot_variant variant) => VariantUtils.ConvertTo<double>(variant), + [typeof(Vector2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2>(variant), + [typeof(Vector2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2i>(variant), + [typeof(Rect2)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2>(variant), + [typeof(Rect2i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Rect2i>(variant), + [typeof(Transform2D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform2D>(variant), + [typeof(Vector3)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3>(variant), + [typeof(Vector3i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3i>(variant), + [typeof(Basis)] = (in godot_variant variant) => VariantUtils.ConvertTo<Basis>(variant), + [typeof(Quaternion)] = (in godot_variant variant) => VariantUtils.ConvertTo<Quaternion>(variant), + [typeof(Transform3D)] = (in godot_variant variant) => VariantUtils.ConvertTo<Transform3D>(variant), + [typeof(Vector4)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4>(variant), + [typeof(Vector4i)] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector4i>(variant), + [typeof(AABB)] = (in godot_variant variant) => VariantUtils.ConvertTo<AABB>(variant), + [typeof(Color)] = (in godot_variant variant) => VariantUtils.ConvertTo<Color>(variant), + [typeof(Plane)] = (in godot_variant variant) => VariantUtils.ConvertTo<Plane>(variant), + [typeof(Callable)] = (in godot_variant variant) => VariantUtils.ConvertTo<Callable>(variant), + [typeof(SignalInfo)] = (in godot_variant variant) => VariantUtils.ConvertTo<SignalInfo>(variant), + [typeof(string)] = (in godot_variant variant) => VariantUtils.ConvertTo<string>(variant), + [typeof(byte[])] = (in godot_variant variant) => VariantUtils.ConvertTo<byte[]>(variant), + [typeof(int[])] = (in godot_variant variant) => VariantUtils.ConvertTo<int[]>(variant), + [typeof(long[])] = (in godot_variant variant) => VariantUtils.ConvertTo<long[]>(variant), + [typeof(float[])] = (in godot_variant variant) => VariantUtils.ConvertTo<float[]>(variant), + [typeof(double[])] = (in godot_variant variant) => VariantUtils.ConvertTo<double[]>(variant), + [typeof(string[])] = (in godot_variant variant) => VariantUtils.ConvertTo<string[]>(variant), + [typeof(Vector2[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector2[]>(variant), + [typeof(Vector3[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Vector3[]>(variant), + [typeof(Color[])] = (in godot_variant variant) => VariantUtils.ConvertTo<Color[]>(variant), + [typeof(StringName[])] = + (in godot_variant variant) => VariantUtils.ConvertTo<StringName[]>(variant), + [typeof(NodePath[])] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath[]>(variant), + [typeof(RID[])] = (in godot_variant variant) => VariantUtils.ConvertTo<RID[]>(variant), + [typeof(StringName)] = (in godot_variant variant) => VariantUtils.ConvertTo<StringName>(variant), + [typeof(NodePath)] = (in godot_variant variant) => VariantUtils.ConvertTo<NodePath>(variant), + [typeof(RID)] = (in godot_variant variant) => VariantUtils.ConvertTo<RID>(variant), + [typeof(Godot.Collections.Dictionary)] = (in godot_variant variant) => + VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant), + [typeof(Godot.Collections.Array)] = + (in godot_variant variant) => VariantUtils.ConvertTo<Godot.Collections.Array>(variant), + [typeof(Variant)] = (in godot_variant variant) => VariantUtils.ConvertTo<Variant>(variant), + }; + + [SuppressMessage("ReSharper", "RedundantNameQualifier")] + public static object? ConvertToObjectOfType(in godot_variant variant, Type type) + { + if (ToSystemObjectFuncByType.TryGetValue(type, out var func)) + return func(variant); + + if (typeof(Godot.Object).IsAssignableFrom(type)) + return Convert.ChangeType(VariantUtils.ConvertTo<Godot.Object>(variant), type); + + if (type.IsEnum) + { + var enumUnderlyingType = type.GetEnumUnderlyingType(); + + switch (Type.GetTypeCode(enumUnderlyingType)) + { + case TypeCode.SByte: + return Enum.ToObject(type, VariantUtils.ConvertToInt8(variant)); + case TypeCode.Int16: + return Enum.ToObject(type, VariantUtils.ConvertToInt16(variant)); + case TypeCode.Int32: + return Enum.ToObject(type, VariantUtils.ConvertToInt32(variant)); + case TypeCode.Int64: + return Enum.ToObject(type, VariantUtils.ConvertToInt64(variant)); + case TypeCode.Byte: + return Enum.ToObject(type, VariantUtils.ConvertToUInt8(variant)); + case TypeCode.UInt16: + return Enum.ToObject(type, VariantUtils.ConvertToUInt16(variant)); + case TypeCode.UInt32: + return Enum.ToObject(type, VariantUtils.ConvertToUInt32(variant)); + case TypeCode.UInt64: + return Enum.ToObject(type, VariantUtils.ConvertToUInt64(variant)); + default: + { + GD.PushError( + "Attempted to convert Variant to enum value of unsupported underlying type. Name: " + + type.FullName + " : " + enumUnderlyingType.FullName + "."); + return null; + } + } + } + + if (type.IsGenericType) + { + var genericTypeDef = type.GetGenericTypeDefinition(); + + if (genericTypeDef == typeof(Godot.Collections.Dictionary<,>)) + { + var ctor = type.GetConstructor(BindingFlags.Default, + new[] { typeof(Godot.Collections.Dictionary) }); + + if (ctor == null) + throw new InvalidOperationException("Dictionary constructor not found"); + + return ctor.Invoke(new object?[] + { + VariantUtils.ConvertTo<Godot.Collections.Dictionary>(variant) + }); + } + + if (genericTypeDef == typeof(Godot.Collections.Array<>)) + { + var ctor = type.GetConstructor(BindingFlags.Default, + new[] { typeof(Godot.Collections.Array) }); + + if (ctor == null) + throw new InvalidOperationException("Array constructor not found"); + + return ctor.Invoke(new object?[] + { + VariantUtils.ConvertTo<Godot.Collections.Array>(variant) + }); + } + } + + GD.PushError($"Attempted to convert Variant to unsupported type. Name: {type.FullName}."); + return null; + } + } } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs index d2a80d0e92..6176093bc1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/Marshaling.cs @@ -1,5 +1,7 @@ using System; using System.Runtime.InteropServices; +using Godot.Collections; +using Array = System.Array; // ReSharper disable InconsistentNaming @@ -148,6 +150,15 @@ namespace Godot.NativeInterop { if (typeof(Godot.Object).IsAssignableFrom(type)) return Variant.Type.Object; + + // We use `IsAssignableFrom` with our helper interfaces to detect generic Godot collections + // because `GetGenericTypeDefinition` is not supported in NativeAOT reflection-free mode. + + if (typeof(IGenericGodotDictionary).IsAssignableFrom(type)) + return Variant.Type.Dictionary; + + if (typeof(IGenericGodotArray).IsAssignableFrom(type)) + return Variant.Type.Array; } else if (type == typeof(Variant)) { @@ -183,512 +194,6 @@ namespace Godot.NativeInterop return Variant.Type.Nil; } - /* TODO: Reflection and type checking each time is slow. This will be replaced with source generators. */ - public static godot_variant ConvertManagedObjectToVariant(object? p_obj) - { - if (p_obj == null) - return new godot_variant(); - - switch (p_obj) - { - case bool @bool: - return VariantUtils.CreateFromBool(@bool); - case char @char: - return VariantUtils.CreateFromInt(@char); - case sbyte @int8: - return VariantUtils.CreateFromInt(@int8); - case short @int16: - return VariantUtils.CreateFromInt(@int16); - case int @int32: - return VariantUtils.CreateFromInt(@int32); - case long @int64: - return VariantUtils.CreateFromInt(@int64); - case byte @uint8: - return VariantUtils.CreateFromInt(@uint8); - case ushort @uint16: - return VariantUtils.CreateFromInt(@uint16); - case uint @uint32: - return VariantUtils.CreateFromInt(@uint32); - case ulong @uint64: - return VariantUtils.CreateFromInt(@uint64); - case float @float: - return VariantUtils.CreateFromFloat(@float); - case double @double: - return VariantUtils.CreateFromFloat(@double); - case Vector2 @vector2: - return VariantUtils.CreateFromVector2(@vector2); - case Vector2i @vector2i: - return VariantUtils.CreateFromVector2i(@vector2i); - case Rect2 @rect2: - return VariantUtils.CreateFromRect2(@rect2); - case Rect2i @rect2i: - return VariantUtils.CreateFromRect2i(@rect2i); - case Transform2D @transform2D: - return VariantUtils.CreateFromTransform2D(@transform2D); - case Vector3 @vector3: - return VariantUtils.CreateFromVector3(@vector3); - case Vector3i @vector3i: - return VariantUtils.CreateFromVector3i(@vector3i); - case Vector4 @vector4: - return VariantUtils.CreateFromVector4(@vector4); - case Vector4i @vector4i: - return VariantUtils.CreateFromVector4i(@vector4i); - case Basis @basis: - return VariantUtils.CreateFromBasis(@basis); - case Quaternion @quaternion: - return VariantUtils.CreateFromQuaternion(@quaternion); - case Transform3D @transform3d: - return VariantUtils.CreateFromTransform3D(@transform3d); - case Projection @projection: - return VariantUtils.CreateFromProjection(@projection); - case AABB @aabb: - return VariantUtils.CreateFromAABB(@aabb); - case Color @color: - return VariantUtils.CreateFromColor(@color); - case Plane @plane: - return VariantUtils.CreateFromPlane(@plane); - case Callable @callable: - return VariantUtils.CreateFromCallable(@callable); - case SignalInfo @signalInfo: - return VariantUtils.CreateFromSignalInfo(@signalInfo); - case Enum @enum: - return VariantUtils.CreateFromInt(Convert.ToInt64(@enum)); - case string @string: - return VariantUtils.CreateFromString(@string); - case byte[] byteArray: - return VariantUtils.CreateFromPackedByteArray(byteArray); - case int[] int32Array: - return VariantUtils.CreateFromPackedInt32Array(int32Array); - case long[] int64Array: - return VariantUtils.CreateFromPackedInt64Array(int64Array); - case float[] floatArray: - return VariantUtils.CreateFromPackedFloat32Array(floatArray); - case double[] doubleArray: - return VariantUtils.CreateFromPackedFloat64Array(doubleArray); - case string[] stringArray: - return VariantUtils.CreateFromPackedStringArray(stringArray); - case Vector2[] vector2Array: - return VariantUtils.CreateFromPackedVector2Array(vector2Array); - case Vector3[] vector3Array: - return VariantUtils.CreateFromPackedVector3Array(vector3Array); - case Color[] colorArray: - return VariantUtils.CreateFromPackedColorArray(colorArray); - case StringName[] stringNameArray: - return VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray); - case NodePath[] nodePathArray: - return VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray); - case RID[] ridArray: - return VariantUtils.CreateFromSystemArrayOfRID(ridArray); - case Godot.Object[] godotObjectArray: - return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray); - case Godot.Object godotObject: - return VariantUtils.CreateFromGodotObject(godotObject); - case StringName stringName: - return VariantUtils.CreateFromStringName(stringName); - case NodePath nodePath: - return VariantUtils.CreateFromNodePath(nodePath); - case RID rid: - return VariantUtils.CreateFromRID(rid); - case Collections.Dictionary godotDictionary: - return VariantUtils.CreateFromDictionary(godotDictionary); - case Collections.Array godotArray: - return VariantUtils.CreateFromArray(godotArray); - case Collections.IGenericGodotDictionary godotDictionary: - return VariantUtils.CreateFromDictionary(godotDictionary.UnderlyingDictionary); - case Collections.IGenericGodotArray godotArray: - return VariantUtils.CreateFromArray(godotArray.UnderlyingArray); - case Variant variant: - return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar); - } - - GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" + - p_obj.GetType().FullName + "."); - return new godot_variant(); - } - - public static object? ConvertVariantToManagedObjectOfType(in godot_variant p_var, Type type) - { - // This function is only needed to set the value of properties. Fields have their own implementation, set_value_from_variant. - switch (Type.GetTypeCode(type)) - { - case TypeCode.Boolean: - return VariantUtils.ConvertToBool(p_var); - case TypeCode.Char: - return VariantUtils.ConvertToChar(p_var); - case TypeCode.SByte: - return VariantUtils.ConvertToInt8(p_var); - case TypeCode.Int16: - return VariantUtils.ConvertToInt16(p_var); - case TypeCode.Int32: - return VariantUtils.ConvertToInt32(p_var); - case TypeCode.Int64: - return VariantUtils.ConvertToInt64(p_var); - case TypeCode.Byte: - return VariantUtils.ConvertToUInt8(p_var); - case TypeCode.UInt16: - return VariantUtils.ConvertToUInt16(p_var); - case TypeCode.UInt32: - return VariantUtils.ConvertToUInt32(p_var); - case TypeCode.UInt64: - return VariantUtils.ConvertToUInt64(p_var); - case TypeCode.Single: - return VariantUtils.ConvertToFloat32(p_var); - case TypeCode.Double: - return VariantUtils.ConvertToFloat64(p_var); - case TypeCode.String: - return VariantUtils.ConvertToStringObject(p_var); - default: - { - if (type == typeof(Vector2)) - return VariantUtils.ConvertToVector2(p_var); - - if (type == typeof(Vector2i)) - return VariantUtils.ConvertToVector2i(p_var); - - if (type == typeof(Rect2)) - return VariantUtils.ConvertToRect2(p_var); - - if (type == typeof(Rect2i)) - return VariantUtils.ConvertToRect2i(p_var); - - if (type == typeof(Transform2D)) - return VariantUtils.ConvertToTransform2D(p_var); - - if (type == typeof(Vector3)) - return VariantUtils.ConvertToVector3(p_var); - - if (type == typeof(Vector3i)) - return VariantUtils.ConvertToVector3i(p_var); - - if (type == typeof(Vector4)) - return VariantUtils.ConvertToVector4(p_var); - - if (type == typeof(Vector4i)) - return VariantUtils.ConvertToVector4i(p_var); - - if (type == typeof(Basis)) - return VariantUtils.ConvertToBasis(p_var); - - if (type == typeof(Quaternion)) - return VariantUtils.ConvertToQuaternion(p_var); - - if (type == typeof(Transform3D)) - return VariantUtils.ConvertToTransform3D(p_var); - - if (type == typeof(Projection)) - return VariantUtils.ConvertToProjection(p_var); - - if (type == typeof(AABB)) - return VariantUtils.ConvertToAABB(p_var); - - if (type == typeof(Color)) - return VariantUtils.ConvertToColor(p_var); - - if (type == typeof(Plane)) - return VariantUtils.ConvertToPlane(p_var); - - if (type == typeof(Callable)) - return VariantUtils.ConvertToCallableManaged(p_var); - - if (type == typeof(SignalInfo)) - return VariantUtils.ConvertToSignalInfo(p_var); - - if (type.IsEnum) - { - var enumUnderlyingType = type.GetEnumUnderlyingType(); - switch (Type.GetTypeCode(enumUnderlyingType)) - { - case TypeCode.SByte: - return VariantUtils.ConvertToInt8(p_var); - case TypeCode.Int16: - return VariantUtils.ConvertToInt16(p_var); - case TypeCode.Int32: - return VariantUtils.ConvertToInt32(p_var); - case TypeCode.Int64: - return VariantUtils.ConvertToInt64(p_var); - case TypeCode.Byte: - return VariantUtils.ConvertToUInt8(p_var); - case TypeCode.UInt16: - return VariantUtils.ConvertToUInt16(p_var); - case TypeCode.UInt32: - return VariantUtils.ConvertToUInt32(p_var); - case TypeCode.UInt64: - return VariantUtils.ConvertToUInt64(p_var); - default: - { - GD.PushError( - "Attempted to convert Variant to enum value of unsupported underlying type. Name: " + - type.FullName + " : " + enumUnderlyingType.FullName + "."); - return null; - } - } - } - - if (type.IsArray || type.IsSZArray) - { - return ConvertVariantToSystemArrayOfType(p_var, type); - } - else if (type.IsGenericType) - { - if (typeof(Godot.Object).IsAssignableFrom(type)) - { - var godotObject = VariantUtils.ConvertToGodotObject(p_var); - - if (!type.IsInstanceOfType(godotObject)) - { - GD.PushError("Invalid cast when marshaling Godot.Object type." + - $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`."); - return null; - } - - return godotObject; - } - - return null; - } - else if (type == typeof(Variant)) - { - return Variant.CreateCopyingBorrowed(p_var); - } - - if (ConvertVariantToManagedObjectOfClass(p_var, type, out object? res)) - return res; - - break; - } - } - - GD.PushError("Attempted to convert Variant to unsupported type. Name: " + - type.FullName + "."); - return null; - } - - private static object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type) - { - if (type == typeof(byte[])) - return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var); - - if (type == typeof(int[])) - return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var); - - if (type == typeof(long[])) - return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var); - - if (type == typeof(float[])) - return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var); - - if (type == typeof(double[])) - return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var); - - if (type == typeof(string[])) - return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var); - - if (type == typeof(Vector2[])) - return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var); - - if (type == typeof(Vector3[])) - return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var); - - if (type == typeof(Color[])) - return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var); - - if (type == typeof(StringName[])) - return VariantUtils.ConvertToSystemArrayOfStringName(p_var); - - if (type == typeof(NodePath[])) - return VariantUtils.ConvertToSystemArrayOfNodePath(p_var); - - if (type == typeof(RID[])) - return VariantUtils.ConvertToSystemArrayOfRID(p_var); - - if (typeof(Godot.Object[]).IsAssignableFrom(type)) - return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type); - - GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " + - type.GetElementType()!.FullName + "."); - return null; - } - - private static bool ConvertVariantToManagedObjectOfClass(in godot_variant p_var, Type type, - out object? res) - { - if (typeof(Godot.Object).IsAssignableFrom(type)) - { - if (p_var.Type == Variant.Type.Nil) - { - res = null; - return true; - } - - if (p_var.Type != Variant.Type.Object) - { - GD.PushError("Invalid cast when marshaling Godot.Object type." + - $" Variant type is `{p_var.Type}`; expected `{p_var.Object}`."); - res = null; - return true; - } - - var godotObjectPtr = VariantUtils.ConvertToGodotObjectPtr(p_var); - - if (godotObjectPtr == IntPtr.Zero) - { - res = null; - return true; - } - - var godotObject = InteropUtils.UnmanagedGetManaged(godotObjectPtr); - - if (!type.IsInstanceOfType(godotObject)) - { - GD.PushError("Invalid cast when marshaling Godot.Object type." + - $" `{godotObject.GetType()}` is not assignable to `{type.FullName}`."); - res = null; - return false; - } - - res = godotObject; - return true; - } - - if (typeof(StringName) == type) - { - res = VariantUtils.ConvertToStringNameObject(p_var); - return true; - } - - if (typeof(NodePath) == type) - { - res = VariantUtils.ConvertToNodePathObject(p_var); - return true; - } - - if (typeof(RID) == type) - { - res = VariantUtils.ConvertToRID(p_var); - return true; - } - - if (typeof(Collections.Dictionary) == type) - { - res = VariantUtils.ConvertToDictionaryObject(p_var); - return true; - } - - if (typeof(Collections.Array) == type) - { - res = VariantUtils.ConvertToArrayObject(p_var); - return true; - } - - res = null; - return false; - } - - public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var) - { - switch (p_var.Type) - { - case Variant.Type.Bool: - return p_var.Bool.ToBool(); - case Variant.Type.Int: - return p_var.Int; - case Variant.Type.Float: - { -#if REAL_T_IS_DOUBLE - return p_var.Float; -#else - return (float)p_var.Float; -#endif - } - case Variant.Type.String: - return ConvertStringToManaged(p_var.String); - case Variant.Type.Vector2: - return p_var.Vector2; - case Variant.Type.Vector2i: - return p_var.Vector2i; - case Variant.Type.Rect2: - return p_var.Rect2; - case Variant.Type.Rect2i: - return p_var.Rect2i; - case Variant.Type.Vector3: - return p_var.Vector3; - case Variant.Type.Vector3i: - return p_var.Vector3i; - case Variant.Type.Transform2d: - return *p_var.Transform2D; - case Variant.Type.Vector4: - return p_var.Vector4; - case Variant.Type.Vector4i: - return p_var.Vector4i; - case Variant.Type.Plane: - return p_var.Plane; - case Variant.Type.Quaternion: - return p_var.Quaternion; - case Variant.Type.Aabb: - return *p_var.AABB; - case Variant.Type.Basis: - return *p_var.Basis; - case Variant.Type.Transform3d: - return *p_var.Transform3D; - case Variant.Type.Projection: - return *p_var.Projection; - case Variant.Type.Color: - return p_var.Color; - case Variant.Type.StringName: - { - // The Variant owns the value, so we need to make a copy - return StringName.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName)); - } - case Variant.Type.NodePath: - { - // The Variant owns the value, so we need to make a copy - return NodePath.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath)); - } - case Variant.Type.Rid: - return p_var.RID; - case Variant.Type.Object: - return InteropUtils.UnmanagedGetManaged(p_var.Object); - case Variant.Type.Callable: - return ConvertCallableToManaged(p_var.Callable); - case Variant.Type.Signal: - return ConvertSignalToManaged(p_var.Signal); - case Variant.Type.Dictionary: - { - // The Variant owns the value, so we need to make a copy - return Collections.Dictionary.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary)); - } - case Variant.Type.Array: - { - // The Variant owns the value, so we need to make a copy - return Collections.Array.CreateTakingOwnershipOfDisposableValue( - NativeFuncs.godotsharp_array_new_copy(p_var.Array)); - } - case Variant.Type.PackedByteArray: - return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var); - case Variant.Type.PackedInt32Array: - return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var); - case Variant.Type.PackedInt64Array: - return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var); - case Variant.Type.PackedFloat32Array: - return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var); - case Variant.Type.PackedFloat64Array: - return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var); - case Variant.Type.PackedStringArray: - return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var); - case Variant.Type.PackedVector2Array: - return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var); - case Variant.Type.PackedVector3Array: - return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var); - case Variant.Type.PackedColorArray: - return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var); - default: - return null; - } - } - // String public static unsafe godot_string ConvertStringToNative(string? p_mono_string) diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs index 694da6db77..80ef2a1ea1 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.generic.cs @@ -4,6 +4,8 @@ using System.Runtime.CompilerServices; namespace Godot.NativeInterop; +#nullable enable + public partial class VariantUtils { private static Exception UnsupportedType<T>() => new InvalidOperationException( diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs index 237a4da364..32d9598554 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Variant.cs @@ -109,16 +109,54 @@ public partial struct Variant : IDisposable public override string ToString() => AsString(); - public object? Obj - { - get + public object? Obj => + _obj ??= NativeVar.DangerousSelfRef.Type switch { - if (_obj == null) - _obj = Marshaling.ConvertVariantToManagedObject((godot_variant)NativeVar); - - return _obj; - } - } + Type.Bool => AsBool(), + Type.Int => AsInt64(), +#if REAL_T_IS_DOUBLE + Type.Float => AsDouble(), +#else + Type.Float => AsSingle(), +#endif + Type.String => AsString(), + Type.Vector2 => AsVector2(), + Type.Vector2i => AsVector2i(), + Type.Rect2 => AsRect2(), + Type.Rect2i => AsRect2i(), + Type.Vector3 => AsVector3(), + Type.Vector3i => AsVector3i(), + Type.Transform2d => AsTransform2D(), + Type.Vector4 => AsVector4(), + Type.Vector4i => AsVector4i(), + Type.Plane => AsPlane(), + Type.Quaternion => AsQuaternion(), + Type.Aabb => AsAABB(), + Type.Basis => AsBasis(), + Type.Transform3d => AsTransform3D(), + Type.Projection => AsProjection(), + Type.Color => AsColor(), + Type.StringName => AsStringName(), + Type.NodePath => AsNodePath(), + Type.Rid => AsRID(), + Type.Object => AsGodotObject(), + Type.Callable => AsCallable(), + Type.Signal => AsSignalInfo(), + Type.Dictionary => AsGodotDictionary(), + Type.Array => AsGodotArray(), + Type.PackedByteArray => AsByteArray(), + Type.PackedInt32Array => AsInt32Array(), + Type.PackedInt64Array => AsInt64Array(), + Type.PackedFloat32Array => AsFloat32Array(), + Type.PackedFloat64Array => AsFloat64Array(), + Type.PackedStringArray => AsStringArray(), + Type.PackedVector2Array => AsVector2Array(), + Type.PackedVector3Array => AsVector3Array(), + Type.PackedColorArray => AsColorArray(), + Type.Nil => null, + Type.Max or _ => + throw new InvalidOperationException($"Invalid Variant type: {NativeVar.DangerousSelfRef.Type}"), + }; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Variant From<[MustBeVariant] T>(in T from) => |