C#: Static marshaling for bindings and source generators

Previously, we added source generators for invoking/accessing methods,
properties and fields in scripts. This freed us from the overhead of
reflection. However, the generated code still used our dynamic
marshaling functions, which do runtime type checking and box value
types.

This commit changes the bindings and source generators to include
'static' marshaling. Based on the types known at compile time, now
we generate the appropriate marshaling call for each type.
This commit is contained in:
Ignacio Roldán Etcheverry 2022-02-27 21:57:46 +01:00
parent 778007a358
commit e22dd3bc6a
14 changed files with 1009 additions and 356 deletions

View File

@ -2,6 +2,6 @@
<PropertyGroup>
<PackageFloatingVersion_Godot>4.0.*-*</PackageFloatingVersion_Godot>
<PackageVersion_Godot_NET_Sdk>4.0.0-dev7</PackageVersion_Godot_NET_Sdk>
<PackageVersion_Godot_SourceGenerators>4.0.0-dev7</PackageVersion_Godot_SourceGenerators>
<PackageVersion_Godot_SourceGenerators>4.0.0-dev8</PackageVersion_Godot_SourceGenerators>
</PropertyGroup>
</Project>

View File

@ -190,6 +190,7 @@ namespace Godot.SourceGenerators
if (method.IsGenericMethod)
continue;
var retSymbol = method.ReturnType;
var retType = method.ReturnsVoid ?
null :
MarshalUtils.ConvertManagedTypeToMarshalType(method.ReturnType, typeCache);
@ -212,7 +213,7 @@ namespace Godot.SourceGenerators
continue;
yield return new GodotMethodData(method, paramTypes, parameters
.Select(p => p.Type).ToImmutableArray(), retType);
.Select(p => p.Type).ToImmutableArray(), retType, retSymbol);
}
}

View File

@ -6,18 +6,20 @@ namespace Godot.SourceGenerators
public struct GodotMethodData
{
public GodotMethodData(IMethodSymbol method, ImmutableArray<MarshalType> paramTypes,
ImmutableArray<ITypeSymbol> paramTypeSymbols, MarshalType? retType)
ImmutableArray<ITypeSymbol> paramTypeSymbols, MarshalType? retType, ITypeSymbol? retSymbol)
{
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 struct GodotPropertyData

View File

@ -46,14 +46,15 @@ namespace Godot.SourceGenerators
ByteArray,
Int32Array,
Int64Array,
SingleArray,
DoubleArray,
Float32Array,
Float64Array,
StringArray,
Vector2Array,
Vector3Array,
ColorArray,
GodotObjectOrDerivedArray,
SystemObjectArray,
SystemArrayOfSupportedType,
// Generics
GodotGenericDictionary,

View File

@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
namespace Godot.SourceGenerators
@ -11,7 +12,11 @@ namespace Godot.SourceGenerators
public INamedTypeSymbol GodotObjectType { get; }
public INamedTypeSymbol GodotGenericDictionary { get; }
public INamedTypeSymbol GodotGenericArray { get; }
// ReSharper disable once InconsistentNaming
public INamedTypeSymbol IDictionary { get; }
// ReSharper disable once InconsistentNaming
public INamedTypeSymbol ICollection { get; }
public INamedTypeSymbol GenericIDictionary { get; }
public INamedTypeSymbol SystemGenericDictionary { get; }
@ -74,14 +79,15 @@ namespace Godot.SourceGenerators
MarshalType.ByteArray => VariantType.PackedByteArray,
MarshalType.Int32Array => VariantType.PackedInt32Array,
MarshalType.Int64Array => VariantType.PackedInt64Array,
MarshalType.SingleArray => VariantType.PackedFloat32Array,
MarshalType.DoubleArray => VariantType.PackedFloat64Array,
MarshalType.Float32Array => VariantType.PackedFloat32Array,
MarshalType.Float64Array => VariantType.PackedFloat64Array,
MarshalType.StringArray => VariantType.PackedStringArray,
MarshalType.Vector2Array => VariantType.PackedVector2Array,
MarshalType.Vector3Array => VariantType.PackedVector3Array,
MarshalType.ColorArray => VariantType.PackedColorArray,
MarshalType.GodotObjectOrDerivedArray => VariantType.Array,
MarshalType.SystemObjectArray => VariantType.Array,
MarshalType.SystemArrayOfSupportedType => VariantType.Array,
MarshalType.GodotGenericDictionary => VariantType.Dictionary,
MarshalType.GodotGenericArray => VariantType.Array,
MarshalType.SystemGenericDictionary => VariantType.Dictionary,
@ -187,9 +193,9 @@ namespace Godot.SourceGenerators
case SpecialType.System_Int64:
return MarshalType.Int64Array;
case SpecialType.System_Single:
return MarshalType.SingleArray;
return MarshalType.Float32Array;
case SpecialType.System_Double:
return MarshalType.DoubleArray;
return MarshalType.Float64Array;
case SpecialType.System_String:
return MarshalType.StringArray;
case SpecialType.System_Object:
@ -209,12 +215,12 @@ namespace Godot.SourceGenerators
case { Name: "Vector3" }:
return MarshalType.Vector3Array;
case { Name: "Color" }:
return MarshalType.ColorArray;
return MarshalType.ColorArray;
}
}
if (ConvertManagedTypeToMarshalType(elementType, typeCache) != null)
return MarshalType.GodotArray;
return MarshalType.SystemArrayOfSupportedType;
return null;
}
@ -316,5 +322,292 @@ namespace Godot.SourceGenerators
return null;
}
private static StringBuilder Append(this StringBuilder source, string a, string b, string c)
=> source.Append(a).Append(b).Append(c);
private static StringBuilder Append(this StringBuilder source, string a, string b,
string c, string d)
=> source.Append(a).Append(b).Append(c).Append(d);
private static StringBuilder Append(this StringBuilder source, string a, string b,
string c, string d, string e)
=> source.Append(a).Append(b).Append(c).Append(d).Append(e);
private static StringBuilder Append(this StringBuilder source, string a, string b,
string c, string d, string e, string f)
=> source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f);
private static StringBuilder Append(this StringBuilder source, string a, string b,
string c, string d, string e, string f, string g)
=> source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g);
private static StringBuilder Append(this StringBuilder source, string a, string b,
string c, string d, string e, string f, string g, string h)
=> source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g).Append(h);
private const string Marshaling = "global::Godot.NativeInterop.Marshaling";
private const string VariantUtils = "global::Godot.NativeInterop.VariantUtils";
public static StringBuilder AppendVariantToManagedExpr(this StringBuilder source,
string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
{
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.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.FullQualifiedName(),
")", 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.FullQualifiedName(), ">(", inputExpr, ")"),
MarshalType.SystemObjectArray =>
source.Append(VariantUtils, ".ConvertToSystemArrayOfVariant(", inputExpr, ")"),
MarshalType.SystemArrayOfSupportedType =>
source.Append(VariantUtils, ".ConvertToSystemArrayOfSupportedType<",
((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedName(), ">(", inputExpr, ")"),
MarshalType.GodotGenericDictionary =>
source.Append(VariantUtils, ".ConvertToGenericDictionaryObject<",
((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ", ",
((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedName(), ">(", inputExpr, ")"),
MarshalType.GodotGenericArray =>
source.Append(VariantUtils, ".ConvertToGenericArrayObject<",
((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ">(", inputExpr, ")"),
MarshalType.SystemGenericDictionary =>
source.Append(VariantUtils, ".ConvertToSystemGenericDictionary<",
((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ", ",
((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedName(), ">(", inputExpr, ")"),
MarshalType.SystemGenericList =>
source.Append(VariantUtils, ".ConvertToSystemGenericList<",
((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ">(", inputExpr, ")"),
MarshalType.GenericIDictionary =>
source.Append(VariantUtils, ".ConvertToGenericDictionaryObject<",
((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ", ",
((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedName(), ">(", inputExpr, ")"),
MarshalType.GenericICollection or MarshalType.GenericIEnumerable =>
source.Append(VariantUtils, ".ConvertToGenericArrayObject<",
((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedName(), ">(", inputExpr, ")"),
MarshalType.SystemObject =>
source.Append(Marshaling, ".ConvertVariantToManagedObject(", inputExpr, ")"),
MarshalType.GodotObjectOrDerived =>
source.Append("(", typeSymbol.FullQualifiedName(),
")", 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, ")"),
MarshalType.IDictionary =>
source.Append(VariantUtils, ".ConvertToDictionaryObject(", inputExpr, ")"),
MarshalType.ICollection or MarshalType.IEnumerable =>
source.Append(VariantUtils, ".ConvertToArrayObject(", inputExpr, ")"),
_ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
"Received unexpected marshal type")
};
}
public static StringBuilder AppendManagedToVariantExpr(
this StringBuilder source, string inputExpr, 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.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.SystemObjectArray =>
source.Append(VariantUtils, ".CreateFromSystemArrayOfVariant(", inputExpr, ")"),
MarshalType.SystemArrayOfSupportedType =>
source.Append(VariantUtils, ".CreateFromSystemArrayOfSupportedType(", inputExpr, ")"),
MarshalType.GodotGenericDictionary =>
source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
MarshalType.GodotGenericArray =>
source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
MarshalType.SystemGenericDictionary =>
source.Append(VariantUtils, ".CreateFromSystemDictionary(", inputExpr, ")"),
MarshalType.SystemGenericList =>
source.Append(VariantUtils, ".CreateFromSystemICollection(", inputExpr, ")"),
MarshalType.GenericIDictionary =>
source.Append(VariantUtils, ".CreateFromSystemGenericIDictionary(", inputExpr, ")"),
MarshalType.GenericICollection =>
source.Append(VariantUtils, ".CreateFromSystemGenericICollection(", inputExpr, ")"),
MarshalType.GenericIEnumerable =>
source.Append(VariantUtils, ".CreateFromSystemGenericIEnumerable(", inputExpr, ")"),
MarshalType.SystemObject =>
source.Append(Marshaling, ".ConvertManagedObjectToVariant(", inputExpr, ")"),
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, ")"),
MarshalType.IDictionary =>
source.Append(VariantUtils, ".CreateFromSystemIDictionary(", inputExpr, ")"),
MarshalType.ICollection =>
source.Append(VariantUtils, ".CreateFromSystemICollection(", inputExpr, ")"),
MarshalType.IEnumerable =>
source.Append(VariantUtils, ".CreateFromSystemIEnumerable(", inputExpr, ")"),
_ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
"Received unexpected marshal type")
};
}
}
}

View File

@ -105,8 +105,6 @@ namespace Godot.SourceGenerators
var members = symbol.GetMembers();
// TODO: Static static marshaling (no reflection, no runtime type checks)
var methodSymbols = members
.Where(s => !s.IsStatic && s.Kind == SymbolKind.Method && !s.IsImplicitlyDeclared)
.Cast<IMethodSymbol>()
@ -182,7 +180,7 @@ namespace Godot.SourceGenerators
continue;
GeneratePropertySetter(property.PropertySymbol.Name,
property.PropertySymbol.Type.FullQualifiedName(), source, isFirstEntry);
property.PropertySymbol.Type, property.Type, source, isFirstEntry);
isFirstEntry = false;
}
@ -192,7 +190,7 @@ namespace Godot.SourceGenerators
continue;
GeneratePropertySetter(field.FieldSymbol.Name,
field.FieldSymbol.Type.FullQualifiedName(), source, isFirstEntry);
field.FieldSymbol.Type, field.Type, source, isFirstEntry);
isFirstEntry = false;
}
@ -209,13 +207,15 @@ namespace Godot.SourceGenerators
isFirstEntry = true;
foreach (var property in godotClassProperties)
{
GeneratePropertyGetter(property.PropertySymbol.Name, source, isFirstEntry);
GeneratePropertyGetter(property.PropertySymbol.Name,
property.Type, source, isFirstEntry);
isFirstEntry = false;
}
foreach (var field in godotClassFields)
{
GeneratePropertyGetter(field.FieldSymbol.Name, source, isFirstEntry);
GeneratePropertyGetter(field.FieldSymbol.Name,
field.Type, source, isFirstEntry);
isFirstEntry = false;
}
@ -278,7 +278,7 @@ namespace Godot.SourceGenerators
source.Append(") {\n");
if (method.RetType != null)
source.Append(" object retBoxed = ");
source.Append(" var callRet = ");
else
source.Append(" ");
@ -290,25 +290,19 @@ namespace Godot.SourceGenerators
if (i != 0)
source.Append(", ");
// TODO: static marshaling (no reflection, no runtime type checks)
string paramTypeQualifiedName = method.ParamTypeSymbols[i].FullQualifiedName();
source.Append("(");
source.Append(paramTypeQualifiedName);
source.Append(")Marshaling.ConvertVariantToManagedObjectOfType(args[");
source.Append(i);
source.Append("], typeof(");
source.Append(paramTypeQualifiedName);
source.Append("))");
source.AppendVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
method.ParamTypeSymbols[i], method.ParamTypes[i]);
}
source.Append(");\n");
if (method.RetType != null)
{
// TODO: static marshaling (no reflection, no runtime type checks)
source.Append(" ret = Marshaling.ConvertManagedObjectToVariant(retBoxed);\n");
source.Append(" ret = ");
source.AppendManagedToVariantExpr("callRet", method.RetType.Value);
source.Append(";\n");
source.Append(" return true;\n");
}
else
@ -322,56 +316,49 @@ namespace Godot.SourceGenerators
private static void GeneratePropertySetter(
string propertyMemberName,
string propertyTypeQualifiedName,
ITypeSymbol propertyTypeSymbol,
MarshalType propertyMarshalType,
StringBuilder source,
bool isFirstEntry
)
{
source.Append(" ");
if (!isFirstEntry)
source.Append("else ");
source.Append("if (name == GodotInternal.PropName_");
source.Append(propertyMemberName);
source.Append(") {\n");
source.Append(" ");
source.Append(propertyMemberName);
source.Append(" = ");
// TODO: static marshaling (no reflection, no runtime type checks)
source.Append("(");
source.Append(propertyTypeQualifiedName);
source.Append(")Marshaling.ConvertVariantToManagedObjectOfType(value, typeof(");
source.Append(propertyTypeQualifiedName);
source.Append("));\n");
source.Append(" return true;\n");
source.Append(" }\n");
source.Append("if (name == GodotInternal.PropName_")
.Append(propertyMemberName)
.Append(") {\n")
.Append(" ")
.Append(propertyMemberName)
.Append(" = ")
.AppendVariantToManagedExpr("value", propertyTypeSymbol, propertyMarshalType)
.Append(";\n")
.Append(" return true;\n")
.Append(" }\n");
}
private static void GeneratePropertyGetter(
string propertyMemberName,
MarshalType propertyMarshalType,
StringBuilder source,
bool isFirstEntry
)
{
source.Append(" ");
if (!isFirstEntry)
source.Append("else ");
source.Append("if (name == GodotInternal.PropName_");
source.Append(propertyMemberName);
source.Append(") {\n");
// TODO: static marshaling (no reflection, no runtime type checks)
source.Append(" value = Marshaling.ConvertManagedObjectToVariant(");
source.Append(propertyMemberName);
source.Append(");\n");
source.Append(" return true;\n");
source.Append(" }\n");
source.Append("if (name == GodotInternal.PropName_")
.Append(propertyMemberName)
.Append(") {\n")
.Append(" value = ")
.AppendManagedToVariantExpr(propertyMemberName, propertyMarshalType)
.Append(";\n")
.Append(" return true;\n")
.Append(" }\n");
}
private static void GenerateHasMethodEntry(

View File

@ -152,7 +152,7 @@ namespace Godot.SourceGenerators
{
source.Append("#pragma warning disable CS0109 // Disable warning about redundant 'new' keyword\n");
string dictionaryType = "System.Collections.Generic.List<Godot.Bridge.PropertyInfo>";
string dictionaryType = "System.Collections.Generic.List<global::Godot.Bridge.PropertyInfo>";
source.Append(" internal new static ")
.Append(dictionaryType)
@ -214,7 +214,7 @@ namespace Godot.SourceGenerators
private static void AppendPropertyInfo(StringBuilder source, PropertyInfo propertyInfo)
{
source.Append(" properties.Add(new Godot.Bridge.PropertyInfo(type: (Godot.Variant.Type)")
source.Append(" properties.Add(new(type: (Godot.Variant.Type)")
.Append((int)propertyInfo.Type)
.Append(", name: GodotInternal.PropName_")
.Append(propertyInfo.Name)

View File

@ -3,7 +3,7 @@
<ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
<TargetFramework>net5.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<LangVersion>8</LangVersion>
<LangVersion>9</LangVersion>
<!-- The Godot editor uses the Debug Godot API assemblies -->
<GodotApiConfiguration>Debug</GodotApiConfiguration>
<GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>

View File

@ -1653,7 +1653,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
<< INDENT3 "{\n";
if (imethod.return_type.cname != name_cache.type_void) {
output << INDENT4 "object retBoxed = ";
output << INDENT4 "var callRet = ";
} else {
output << INDENT4;
}
@ -1670,27 +1670,30 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
output << ", ";
}
// TODO: static marshaling (no reflection, no runtime type checks)
if (arg_type->cname == name_cache.type_Array_generic || arg_type->cname == name_cache.type_Dictionary_generic) {
String arg_cs_type = arg_type->cs_type + _get_generic_type_parameters(*arg_type, iarg.type.generic_type_parameters);
output << "new " << arg_cs_type << "((" << arg_type->cs_type << ")Marshaling.ConvertVariantToManagedObjectOfType(args["
<< itos(i) << "], typeof(" << arg_type->cs_type << ")))";
output << "new " << arg_cs_type << "(" << sformat(arg_type->cs_variant_to_managed,
"args[" + itos(i) + "]", arg_type->cs_type, arg_type->name) << ")";
} else {
output << "(" << arg_type->cs_type << ")Marshaling.ConvertVariantToManagedObjectOfType(args["
<< itos(i) << "], typeof(" << arg_type->cs_type << "))";
output << sformat(arg_type->cs_variant_to_managed,
"args[" + itos(i) + "]", arg_type->cs_type, arg_type->name);
}
}
output << ");\n";
if (imethod.return_type.cname != name_cache.type_void) {
// TODO: static marshaling (no reflection, no runtime type checks)
output << INDENT4 "ret = Marshaling.ConvertManagedObjectToVariant(retBoxed);\n";
output << INDENT4 "return true;\n";
const TypeInterface *return_type = _get_type_or_null(imethod.return_type);
ERR_FAIL_NULL_V(return_type, ERR_BUG); // Return type not found
output << INDENT4 "ret = "
<< sformat(return_type->cs_managed_to_variant, "callRet", return_type->cs_type, return_type->name)
<< ";\n"
<< INDENT4 "return true;\n";
} else {
output << INDENT4 "ret = default;\n";
output << INDENT4 "return true;\n";
output << INDENT4 "ret = default;\n"
<< INDENT4 "return true;\n";
}
output << INDENT3 "}\n";
@ -2321,7 +2324,7 @@ Error BindingsGenerator::_generate_cs_native_calls(const InternalCall &p_icall,
String c_in_vararg = arg_type->c_in_vararg;
if (arg_type->is_object_type) {
c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObject(%1);\n";
c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromGodotObjectPtr(%1);\n";
}
ERR_FAIL_COND_V_MSG(c_in_vararg.is_empty(), ERR_BUG,
@ -2746,6 +2749,9 @@ 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";
@ -3141,6 +3147,8 @@ 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);
}
@ -3361,14 +3369,16 @@ 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; \
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; \
itype.cs_variant_to_managed = "VariantUtils.ConvertTo%2(%0)"; \
itype.cs_managed_to_variant = "VariantUtils.CreateFrom%2(%0)"; \
builtin_types.insert(itype.cname, itype); \
}
INSERT_STRUCT_TYPE(Vector2)
@ -3399,13 +3409,15 @@ 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
{
// C interface for 'uint32_t' is the same as that of enums. Remember to apply
// any of the changes done here to 'TypeInterface::postsetup_enum_type' as well.
#define INSERT_INT_TYPE(m_name) \
#define INSERT_INT_TYPE(m_name, m_int_struct_name) \
{ \
itype = TypeInterface::create_value_type(String(m_name)); \
if (itype.name != "long" && itype.name != "ulong") { \
@ -3419,22 +3431,24 @@ 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); \
}
// The expected type for all integers in ptrcall is 'int64_t', so that's what we use for 'c_type'
INSERT_INT_TYPE("sbyte");
INSERT_INT_TYPE("short");
INSERT_INT_TYPE("int");
INSERT_INT_TYPE("long");
INSERT_INT_TYPE("byte");
INSERT_INT_TYPE("ushort");
INSERT_INT_TYPE("uint");
INSERT_INT_TYPE("ulong");
}
INSERT_INT_TYPE("sbyte", "Int8");
INSERT_INT_TYPE("short", "Int16");
INSERT_INT_TYPE("int", "Int32");
INSERT_INT_TYPE("long", "Int64");
INSERT_INT_TYPE("byte", "UInt8");
INSERT_INT_TYPE("ushort", "UInt16");
INSERT_INT_TYPE("uint", "UInt32");
INSERT_INT_TYPE("ulong", "UInt64");
#undef INSERT_INT_TYPE
}
// Floating point types
{
@ -3454,6 +3468,8 @@ 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
@ -3467,6 +3483,8 @@ 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);
}
@ -3483,7 +3501,9 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.cs_type;
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = true;
itype.c_in_vararg = "%5using godot_variant %1_in = VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue(" C_METHOD_MONOSTR_TO_GODOT "(%1));\n";
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
@ -3502,6 +3522,8 @@ 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
@ -3519,6 +3541,8 @@ 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
@ -3531,6 +3555,8 @@ 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
@ -3546,18 +3572,22 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.c_type_in = itype.cs_type;
itype.c_type_out = itype.cs_type;
itype.c_type_is_disposable_struct = true;
itype.cs_variant_to_managed = C_METHOD_MANAGED_FROM_VARIANT "(%0)";
itype.cs_managed_to_variant = C_METHOD_MANAGED_TO_VARIANT "(%0)";
builtin_types.insert(itype.cname, itype);
// Callable
itype = TypeInterface::create_value_type(String("Callable"));
itype.cs_in_expr = "ref %0";
itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(ref %1);\n";
itype.cs_in_expr = "%0";
itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_CALLABLE "(in %1);\n";
itype.c_out = "%5return " C_METHOD_MANAGED_FROM_CALLABLE "(in %1);\n";
itype.c_arg_in = "&%s_in";
itype.c_type = "godot_callable";
itype.c_type_in = "ref " + itype.cs_type;
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
@ -3566,14 +3596,16 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
itype.cname = itype.name;
itype.proxy_name = "SignalInfo";
itype.cs_type = itype.proxy_name;
itype.cs_in_expr = "ref %0";
itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(ref %1);\n";
itype.cs_in_expr = "%0";
itype.c_in = "%5using %0 %1_in = " C_METHOD_MANAGED_TO_SIGNAL "(in %1);\n";
itype.c_out = "%5return " C_METHOD_MANAGED_FROM_SIGNAL "(&%1);\n";
itype.c_arg_in = "&%s_in";
itype.c_type = "godot_signal";
itype.c_type_in = "ref " + itype.cs_type;
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)
@ -3587,6 +3619,7 @@ void BindingsGenerator::_populate_builtin_type_interfaces() {
// c_out and c_type_out are not applicable to VarArg.
itype.c_arg_in = "&%s_in";
itype.c_type_in = "object[]";
itype.cs_variant_to_managed = "VariantUtils.ConvertToSystemArray(%0)";
builtin_types.insert(itype.cname, itype);
#define INSERT_ARRAY_FULL(m_name, m_type, m_managed_type, m_proxy_t) \
@ -3603,6 +3636,8 @@ 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); \
}
@ -3638,6 +3673,8 @@ 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
@ -3662,6 +3699,8 @@ 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
@ -3734,6 +3773,8 @@ 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);
@ -3766,6 +3807,8 @@ 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);
}
}

View File

@ -385,6 +385,22 @@ class BindingsGenerator {
*/
String cs_type;
/**
* Formatting elements:
* %0: input expression of type `in godot_variant`
* %1: [cs_type] of this type
* %2: [name] of this type
*/
String cs_variant_to_managed;
/**
* Formatting elements:
* %0: input expression
* %1: [cs_type] of this type
* %2: [name] of this type
*/
String cs_managed_to_variant;
const DocData::ClassDoc *class_doc = nullptr;
List<ConstantInterface> constants;

View File

@ -320,6 +320,37 @@ namespace Godot.Collections
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal object GetAtAsType(int index, Type type)
{
GetVariantBorrowElementAt(index, out godot_variant borrowElem);
return Marshaling.ConvertVariantToManagedObjectOfType(borrowElem, type);
}
internal void CopyToGeneric<T>(T[] array, int arrayIndex, Type type = null)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex),
"Number was less than the array's lower bound in the first dimension.");
var typeOfElements = type ?? typeof(T);
int count = Count;
if (array.Length < (arrayIndex + count))
throw new ArgumentException(
"Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
array[arrayIndex] = (T)GetAtAsType(i, typeOfElements);
arrayIndex++;
}
}
// IEnumerable
/// <summary>
@ -517,11 +548,7 @@ namespace Godot.Collections
/// <value>The value at the given <paramref name="index"/>.</value>
public T this[int index]
{
get
{
_underlyingArray.GetVariantBorrowElementAt(index, out godot_variant borrowElem);
return (T)Marshaling.ConvertVariantToManagedObjectOfType(borrowElem, TypeOfElements);
}
get => (T)_underlyingArray.GetAtAsType(index, TypeOfElements);
set => _underlyingArray[index] = value;
}
@ -604,27 +631,8 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The C# array to copy to.</param>
/// <param name="arrayIndex">The index to start at.</param>
public void CopyTo(T[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
if (arrayIndex < 0)
throw new ArgumentOutOfRangeException(nameof(arrayIndex),
"Number was less than the array's lower bound in the first dimension.");
int count = _underlyingArray.Count;
if (array.Length < (arrayIndex + count))
throw new ArgumentException(
"Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
for (int i = 0; i < count; i++)
{
array[arrayIndex] = this[i];
arrayIndex++;
}
}
public void CopyTo(T[] array, int arrayIndex) =>
_underlyingArray.CopyToGeneric<T>(array, arrayIndex, TypeOfElements);
/// <summary>
/// Removes the first occurrence of the specified value

View File

@ -373,7 +373,7 @@ namespace Godot.Collections
/// </summary>
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
public sealed class Dictionary<TKey, TValue> :
public class Dictionary<TKey, TValue> :
IDictionary<TKey, TValue>, IGenericGodotDictionary
{
private readonly Dictionary _underlyingDict;

View File

@ -11,6 +11,8 @@ using System.Runtime.InteropServices;
#nullable enable
// TODO: Consider removing support for IEnumerable
namespace Godot.NativeInterop
{
public static class Marshaling
@ -280,121 +282,74 @@ namespace Godot.NativeInterop
case Plane @plane:
return VariantUtils.CreateFromPlane(@plane);
case Callable @callable:
return VariantUtils.CreateFromCallableTakingOwnershipOfDisposableValue(
ConvertCallableToNative(ref @callable));
return VariantUtils.CreateFromCallable(@callable);
case SignalInfo @signalInfo:
return VariantUtils.CreateFromSignalTakingOwnershipOfDisposableValue(
ConvertSignalToNative(ref @signalInfo));
return VariantUtils.CreateFromSignalInfo(@signalInfo);
case Enum @enum:
return VariantUtils.CreateFromInt(Convert.ToInt64(@enum));
case string @string:
{
return VariantUtils.CreateFromStringTakingOwnershipOfDisposableValue(
ConvertStringToNative(@string));
}
return VariantUtils.CreateFromString(@string);
case byte[] byteArray:
{
using godot_packed_byte_array array = ConvertSystemArrayToNativePackedByteArray(byteArray);
return VariantUtils.CreateFromPackedByteArray(array);
}
return VariantUtils.CreateFromPackedByteArray(byteArray);
case Int32[] int32Array:
{
using godot_packed_int32_array array = ConvertSystemArrayToNativePackedInt32Array(int32Array);
return VariantUtils.CreateFromPackedInt32Array(array);
}
return VariantUtils.CreateFromPackedInt32Array(int32Array);
case Int64[] int64Array:
{
using godot_packed_int64_array array = ConvertSystemArrayToNativePackedInt64Array(int64Array);
return VariantUtils.CreateFromPackedInt64Array(array);
}
return VariantUtils.CreateFromPackedInt64Array(int64Array);
case float[] floatArray:
{
using godot_packed_float32_array array = ConvertSystemArrayToNativePackedFloat32Array(floatArray);
return VariantUtils.CreateFromPackedFloat32Array(array);
}
return VariantUtils.CreateFromPackedFloat32Array(floatArray);
case double[] doubleArray:
{
using godot_packed_float64_array array = ConvertSystemArrayToNativePackedFloat64Array(doubleArray);
return VariantUtils.CreateFromPackedFloat64Array(array);
}
return VariantUtils.CreateFromPackedFloat64Array(doubleArray);
case string[] stringArray:
{
using godot_packed_string_array array = ConvertSystemArrayToNativePackedStringArray(stringArray);
return VariantUtils.CreateFromPackedStringArray(array);
}
return VariantUtils.CreateFromPackedStringArray(stringArray);
case Vector2[] vector2Array:
{
using godot_packed_vector2_array array = ConvertSystemArrayToNativePackedVector2Array(vector2Array);
return VariantUtils.CreateFromPackedVector2Array(array);
}
return VariantUtils.CreateFromPackedVector2Array(vector2Array);
case Vector3[] vector3Array:
{
using godot_packed_vector3_array array = ConvertSystemArrayToNativePackedVector3Array(vector3Array);
return VariantUtils.CreateFromPackedVector3Array(array);
}
return VariantUtils.CreateFromPackedVector3Array(vector3Array);
case Color[] colorArray:
{
using godot_packed_color_array array = ConvertSystemArrayToNativePackedColorArray(colorArray);
return VariantUtils.CreateFromPackedColorArray(array);
}
return VariantUtils.CreateFromPackedColorArray(colorArray);
case StringName[] stringNameArray:
{
using godot_array array = ConvertSystemArrayToNativeGodotArray(stringNameArray);
return VariantUtils.CreateFromArray(array);
}
return VariantUtils.CreateFromSystemArrayOfSupportedType(stringNameArray);
case NodePath[] nodePathArray:
{
using godot_array array = ConvertSystemArrayToNativeGodotArray(nodePathArray);
return VariantUtils.CreateFromArray(array);
}
return VariantUtils.CreateFromSystemArrayOfSupportedType(nodePathArray);
case RID[] ridArray:
{
using godot_array array = ConvertSystemArrayToNativeGodotArray(ridArray);
return VariantUtils.CreateFromArray(array);
}
return VariantUtils.CreateFromSystemArrayOfSupportedType(ridArray);
case Godot.Object[] godotObjectArray:
{
using godot_array array = ConvertSystemArrayToNativeGodotArray(godotObjectArray);
return VariantUtils.CreateFromArray(array);
}
return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray);
case object[] objectArray: // Last one to avoid catching others like string[] and Godot.Object[]
{
// The pattern match for `object[]` catches arrays on any reference type,
// so we need to check the actual type to make sure it's truly `object[]`.
if (objectArray.GetType() == typeof(object[]))
{
using godot_array array = ConvertSystemArrayToNativeGodotArray(objectArray);
return VariantUtils.CreateFromArray(array);
}
return VariantUtils.CreateFromSystemArrayOfVariant(objectArray);
GD.PushError("Attempted to convert a managed array of unmarshallable element type to Variant.");
return new godot_variant();
}
case Godot.Object godotObject:
return VariantUtils.CreateFromGodotObject(godotObject.NativeInstance);
return VariantUtils.CreateFromGodotObject(godotObject);
case StringName stringName:
return VariantUtils.CreateFromStringName(stringName.NativeValue.DangerousSelfRef);
return VariantUtils.CreateFromStringName(stringName);
case NodePath nodePath:
return VariantUtils.CreateFromNodePath((godot_node_path)nodePath.NativeValue);
return VariantUtils.CreateFromNodePath(nodePath);
case RID rid:
return VariantUtils.CreateFromRID(rid);
case Collections.Dictionary godotDictionary:
return VariantUtils.CreateFromDictionary((godot_dictionary)godotDictionary.NativeValue);
return VariantUtils.CreateFromDictionary(godotDictionary);
case Collections.Array godotArray:
return VariantUtils.CreateFromArray((godot_array)godotArray.NativeValue);
return VariantUtils.CreateFromArray(godotArray);
case Collections.IGenericGodotDictionary genericGodotDictionary:
{
var godotDict = genericGodotDictionary.UnderlyingDictionary;
if (godotDict == null)
return new godot_variant();
return VariantUtils.CreateFromDictionary((godot_dictionary)godotDict.NativeValue);
return VariantUtils.CreateFromDictionary(godotDict);
}
case Collections.IGenericGodotArray genericGodotArray:
{
var godotArray = genericGodotArray.UnderlyingArray;
if (godotArray == null)
return new godot_variant();
return VariantUtils.CreateFromArray((godot_array)godotArray.NativeValue);
return VariantUtils.CreateFromArray(godotArray);
}
default:
{
@ -412,13 +367,13 @@ namespace Godot.NativeInterop
foreach (KeyValuePair<object, object> entry in (IDictionary)p_obj)
godotDict.Add(entry.Key, entry.Value);
return VariantUtils.CreateFromDictionary((godot_dictionary)godotDict.NativeValue);
return VariantUtils.CreateFromDictionary(godotDict);
}
if (genericTypeDefinition == typeof(System.Collections.Generic.List<>))
{
// TODO: Validate element type is compatible with Variant
using var nativeGodotArray = ConvertIListToNativeGodotArray((IList)p_obj);
using var nativeGodotArray = ConvertICollectionToNativeGodotArray((ICollection)p_obj);
return VariantUtils.CreateFromArray(nativeGodotArray);
}
}
@ -432,25 +387,6 @@ namespace Godot.NativeInterop
return new godot_variant();
}
private static string? ConvertVariantToManagedString(in godot_variant p_var)
{
switch (p_var.Type)
{
case Variant.Type.Nil:
return null; // Otherwise, Variant -> String would return the string "Null"
case Variant.Type.String:
{
// We avoid the internal call if the stored type is the same we want.
return ConvertStringToManaged(p_var.String);
}
default:
{
using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var);
return ConvertStringToManaged(godotString);
}
}
}
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.
@ -481,7 +417,7 @@ namespace Godot.NativeInterop
case TypeCode.Double:
return VariantUtils.ConvertToFloat64(p_var);
case TypeCode.String:
return ConvertVariantToManagedString(p_var);
return VariantUtils.ConvertToStringObject(p_var);
default:
{
if (type == typeof(Vector2))
@ -533,16 +469,10 @@ namespace Godot.NativeInterop
return VariantUtils.ConvertToPlane(p_var);
if (type == typeof(Callable))
{
using godot_callable callable = NativeFuncs.godotsharp_variant_as_callable(p_var);
return ConvertCallableToManaged(in callable);
}
return VariantUtils.ConvertToCallableManaged(p_var);
if (type == typeof(SignalInfo))
{
using godot_signal signal = NativeFuncs.godotsharp_variant_as_signal(p_var);
return ConvertSignalToManaged(in signal);
}
return VariantUtils.ConvertToSignalInfo(p_var);
if (type.IsEnum)
{
@ -596,88 +526,46 @@ namespace Godot.NativeInterop
private static object? ConvertVariantToSystemArrayOfType(in godot_variant p_var, Type type)
{
if (type == typeof(byte[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var);
return ConvertNativePackedByteArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
if (type == typeof(Int32[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var);
return ConvertNativePackedInt32ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
if (type == typeof(Int64[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var);
return ConvertNativePackedInt64ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
if (type == typeof(float[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var);
return ConvertNativePackedFloat32ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
if (type == typeof(double[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var);
return ConvertNativePackedFloat64ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
if (type == typeof(string[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var);
return ConvertNativePackedStringArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
if (type == typeof(Vector2[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var);
return ConvertNativePackedVector2ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
if (type == typeof(Vector3[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var);
return ConvertNativePackedVector3ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
if (type == typeof(Color[]))
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);
return ConvertNativePackedColorArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
if (type == typeof(StringName[]))
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type);
}
return VariantUtils.ConvertToSystemArrayOfSupportedType<StringName>(p_var);
if (type == typeof(NodePath[]))
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type);
}
return VariantUtils.ConvertToSystemArrayOfSupportedType<NodePath>(p_var);
if (type == typeof(RID[]))
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type);
}
return VariantUtils.ConvertToSystemArrayOfSupportedType<RID>(p_var);
if (typeof(Godot.Object[]).IsAssignableFrom(type))
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return ConvertNativeGodotArrayToSystemArrayOfType(godotArray, type);
}
return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type);
if (type == typeof(object[]))
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return ConvertNativeGodotArrayToSystemArray(godotArray);
}
return VariantUtils.ConvertToSystemArrayOfVariant(p_var);
GD.PushError("Attempted to convert Variant to array of unsupported element type. Name: " +
type.GetElementType()!.FullName + ".");
@ -689,21 +577,29 @@ namespace Godot.NativeInterop
{
if (typeof(Godot.Object).IsAssignableFrom(type))
{
res = InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var));
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}`.");
res = null;
return false;
}
res = godotObject;
return true;
}
if (typeof(StringName) == type)
{
res = StringName.CreateTakingOwnershipOfDisposableValue(
VariantUtils.ConvertToStringName(p_var));
res = VariantUtils.ConvertToStringNameObject(p_var);
return true;
}
if (typeof(NodePath) == type)
{
res = NodePath.CreateTakingOwnershipOfDisposableValue(
VariantUtils.ConvertToNodePath(p_var));
res = VariantUtils.ConvertToNodePathObject(p_var);
return true;
}
@ -715,8 +611,7 @@ namespace Godot.NativeInterop
if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type)
{
res = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(
VariantUtils.ConvertToDictionary(p_var));
res = VariantUtils.ConvertToDictionaryObject(p_var);
return true;
}
@ -724,8 +619,7 @@ namespace Godot.NativeInterop
typeof(System.Collections.ICollection) == type ||
typeof(System.Collections.IEnumerable) == type)
{
res = Collections.Array.CreateTakingOwnershipOfDisposableValue(
VariantUtils.ConvertToArray(p_var));
res = VariantUtils.ConvertToArrayObject(p_var);
return true;
}
@ -737,8 +631,7 @@ namespace Godot.NativeInterop
{
static object ConvertVariantToGenericGodotCollectionsDictionary(in godot_variant p_var, Type fullType)
{
var underlyingDict = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(
VariantUtils.ConvertToDictionary(p_var));
var underlyingDict = VariantUtils.ConvertToDictionaryObject(p_var);
return Activator.CreateInstance(fullType,
BindingFlags.Public | BindingFlags.Instance, null,
args: new object[] { underlyingDict }, null)!;
@ -746,8 +639,7 @@ namespace Godot.NativeInterop
static object ConvertVariantToGenericGodotCollectionsArray(in godot_variant p_var, Type fullType)
{
var underlyingArray = Collections.Array.CreateTakingOwnershipOfDisposableValue(
VariantUtils.ConvertToArray(p_var));
var underlyingArray = VariantUtils.ConvertToArrayObject(p_var);
return Activator.CreateInstance(fullType,
BindingFlags.Public | BindingFlags.Instance, null,
args: new object[] { underlyingArray }, null)!;
@ -763,8 +655,7 @@ namespace Godot.NativeInterop
if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>))
{
using var godotDictionary = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(
VariantUtils.ConvertToDictionary(p_var));
using var godotDictionary = VariantUtils.ConvertToDictionaryObject(p_var);
var dictionary = (System.Collections.IDictionary)Activator.CreateInstance(type,
BindingFlags.Public | BindingFlags.Instance, null,
@ -781,8 +672,7 @@ namespace Godot.NativeInterop
if (genericTypeDefinition == typeof(System.Collections.Generic.List<>))
{
using var godotArray = Collections.Array.CreateTakingOwnershipOfDisposableValue(
VariantUtils.ConvertToArray(p_var));
using var godotArray = VariantUtils.ConvertToArrayObject(p_var);
var list = (System.Collections.IList)Activator.CreateInstance(type,
BindingFlags.Public | BindingFlags.Instance, null,
@ -818,7 +708,18 @@ namespace Godot.NativeInterop
}
if (typeof(Godot.Object).IsAssignableFrom(type))
return InteropUtils.UnmanagedGetManaged(VariantUtils.ConvertToGodotObject(p_var));
{
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;
}
@ -906,50 +807,23 @@ namespace Godot.NativeInterop
NativeFuncs.godotsharp_array_new_copy(p_var.Array));
}
case Variant.Type.PackedByteArray:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var);
return ConvertNativePackedByteArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
case Variant.Type.PackedInt32Array:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var);
return ConvertNativePackedInt32ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
case Variant.Type.PackedInt64Array:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var);
return ConvertNativePackedInt64ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
case Variant.Type.PackedFloat32Array:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var);
return ConvertNativePackedFloat32ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedFloat32ArrayToSystemArray(p_var);
case Variant.Type.PackedFloat64Array:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var);
return ConvertNativePackedFloat64ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedFloat64ArrayToSystemArray(p_var);
case Variant.Type.PackedStringArray:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var);
return ConvertNativePackedStringArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedStringArrayToSystemArray(p_var);
case Variant.Type.PackedVector2Array:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var);
return ConvertNativePackedVector2ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedVector2ArrayToSystemArray(p_var);
case Variant.Type.PackedVector3Array:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var);
return ConvertNativePackedVector3ArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedVector3ArrayToSystemArray(p_var);
case Variant.Type.PackedColorArray:
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);
return ConvertNativePackedColorArrayToSystemArray(packedArray);
}
return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
default:
return null;
}
@ -986,10 +860,7 @@ namespace Godot.NativeInterop
// Callable
public static godot_callable ConvertCallableToNative(ref Callable p_managed_callable)
=> ConvertCallableToNative(p_managed_callable);
public static godot_callable ConvertCallableToNative(Callable p_managed_callable)
public static godot_callable ConvertCallableToNative(in Callable p_managed_callable)
{
if (p_managed_callable.Delegate != null)
{
@ -1041,7 +912,7 @@ namespace Godot.NativeInterop
// SignalInfo
public static godot_signal ConvertSignalToNative(ref SignalInfo p_managed_signal)
public static godot_signal ConvertSignalToNative(in SignalInfo p_managed_signal)
{
ulong ownerId = p_managed_signal.Owner.GetInstanceId();
godot_string_name name;
@ -1082,17 +953,46 @@ namespace Godot.NativeInterop
return ret;
}
private static object ConvertNativeGodotArrayToSystemArrayOfType(in godot_array p_array, Type type)
internal static T[] ConvertNativeGodotArrayToSystemArrayOfType<T>(in godot_array p_array)
{
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_array_new_copy(p_array));
int length = array.Count;
object ret = Activator.CreateInstance(type, length)!;
var ret = new T[length];
// ConvertVariantToManagedObject handled by Collections.Array
// ConvertVariantToManagedObjectOfType is not needed because target element types are Godot.Object (or derived)
array.CopyTo((object[])ret, 0);
// ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric<T>
array.CopyToGeneric(ret, 0);
return ret;
}
internal static T[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(in godot_array p_array)
where T : Godot.Object
{
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_array_new_copy(p_array));
int length = array.Count;
var ret = new T[length];
// ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric<T>
array.CopyToGeneric(ret, 0);
return ret;
}
internal static Godot.Object[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(in godot_array p_array,
Type type)
{
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_array_new_copy(p_array));
int length = array.Count;
var ret = (Godot.Object[])Activator.CreateInstance(type, length)!;
// ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric<T>
array.CopyToGeneric(ret, 0, type.GetElementType());
return ret;
}
@ -1131,7 +1031,7 @@ namespace Godot.NativeInterop
return NativeFuncs.godotsharp_array_new_copy(src);
}
public static godot_array ConvertIListToNativeGodotArray(IList p_array)
public static godot_array ConvertICollectionToNativeGodotArray(ICollection p_array)
{
int length = p_array.Count;
@ -1141,13 +1041,62 @@ namespace Godot.NativeInterop
using var array = new Collections.Array();
array.Resize(length);
for (int i = 0; i < length; i++)
array[i] = p_array[i];
int i = 0;
foreach (var elem in p_array)
{
array[i] = elem;
i++;
}
var src = (godot_array)array.NativeValue;
return NativeFuncs.godotsharp_array_new_copy(src);
}
public static godot_array ConvertGenericICollectionToNativeGodotArray<T>(ICollection<T> p_array)
{
int length = p_array.Count;
if (length == 0)
return NativeFuncs.godotsharp_array_new();
var array = new Collections.Array<T>();
using var underlyingArray = (Collections.Array)array;
array.Resize(length);
int i = 0;
foreach (var elem in p_array)
{
array[i] = elem;
i++;
}
var src = (godot_array)underlyingArray.NativeValue;
return NativeFuncs.godotsharp_array_new_copy(src);
}
public static godot_array ConvertIEnumerableToNativeGodotArray(IEnumerable p_array)
{
using var array = new Collections.Array();
foreach (var elem in p_array)
array.Add(elem);
var src = (godot_array)array.NativeValue;
return NativeFuncs.godotsharp_array_new_copy(src);
}
public static godot_array ConvertGenericIEnumerableToNativeGodotArray<T>(IEnumerable<T> p_array)
{
var array = new Collections.Array<T>();
using var underlyingArray = (Collections.Array)array;
foreach (var elem in p_array)
array.Add(elem);
var src = (godot_array)underlyingArray.NativeValue;
return NativeFuncs.godotsharp_array_new_copy(src);
}
// PackedByteArray
public static unsafe byte[] ConvertNativePackedByteArrayToSystemArray(in godot_packed_byte_array p_array)

View File

@ -1,7 +1,11 @@
using System;
using System.Runtime.CompilerServices;
using Godot.Collections;
// ReSharper disable InconsistentNaming
#nullable enable
namespace Godot.NativeInterop
{
public static class VariantUtils
@ -94,14 +98,28 @@ namespace Godot.NativeInterop
public static godot_variant CreateFromCallableTakingOwnershipOfDisposableValue(godot_callable from)
=> new() { Type = Variant.Type.Callable, Callable = from };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromCallable(Callable from)
=> CreateFromCallableTakingOwnershipOfDisposableValue(
Marshaling.ConvertCallableToNative(from));
// Explicit name to make it very clear
public static godot_variant CreateFromSignalTakingOwnershipOfDisposableValue(godot_signal from)
=> new() { Type = Variant.Type.Signal, Signal = from };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromSignalInfo(SignalInfo from)
=> CreateFromSignalTakingOwnershipOfDisposableValue(
Marshaling.ConvertSignalToNative(from));
// Explicit name to make it very clear
public static godot_variant CreateFromStringTakingOwnershipOfDisposableValue(godot_string from)
=> new() { Type = Variant.Type.String, String = from };
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromString(string? from)
=> CreateFromStringTakingOwnershipOfDisposableValue(Marshaling.ConvertStringToNative(from));
public static godot_variant CreateFromPackedByteArray(in godot_packed_byte_array from)
{
NativeFuncs.godotsharp_variant_new_packed_byte_array(out godot_variant ret, from);
@ -156,31 +174,192 @@ namespace Godot.NativeInterop
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedByteArray(Span<byte> from)
=> CreateFromPackedByteArray(Marshaling.ConvertSystemArrayToNativePackedByteArray(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedInt32Array(Span<int> from)
=> CreateFromPackedInt32Array(Marshaling.ConvertSystemArrayToNativePackedInt32Array(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedInt64Array(Span<long> from)
=> CreateFromPackedInt64Array(Marshaling.ConvertSystemArrayToNativePackedInt64Array(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedFloat32Array(Span<float> from)
=> CreateFromPackedFloat32Array(Marshaling.ConvertSystemArrayToNativePackedFloat32Array(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedFloat64Array(Span<double> from)
=> CreateFromPackedFloat64Array(Marshaling.ConvertSystemArrayToNativePackedFloat64Array(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedStringArray(Span<string> from)
=> CreateFromPackedStringArray(Marshaling.ConvertSystemArrayToNativePackedStringArray(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedVector2Array(Span<Vector2> from)
=> CreateFromPackedVector2Array(Marshaling.ConvertSystemArrayToNativePackedVector2Array(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedVector3Array(Span<Vector3> from)
=> CreateFromPackedVector3Array(Marshaling.ConvertSystemArrayToNativePackedVector3Array(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromPackedColorArray(Span<Color> from)
=> CreateFromPackedColorArray(Marshaling.ConvertSystemArrayToNativePackedColorArray(from));
public static godot_variant CreateFromSystemArrayOfSupportedType<T>(T[]? from)
{
if (from == null)
return default; // Nil
using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from);
return CreateFromArray(array);
}
// ReSharper disable once RedundantNameQualifier
public static godot_variant CreateFromSystemArrayOfGodotObject(Godot.Object[]? from)
{
if (from == null)
return default; // Nil
using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from);
return CreateFromArray(array);
}
public static godot_variant CreateFromSystemArrayOfVariant(object[]? from)
{
if (from == null)
return default; // Nil
using godot_array array = Marshaling.ConvertSystemArrayToNativeGodotArray(from);
return CreateFromArray(array);
}
public static godot_variant CreateFromArray(godot_array from)
{
NativeFuncs.godotsharp_variant_new_array(out godot_variant ret, from);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromArray(Collections.Array? from)
=> from != null ? CreateFromArray((godot_array)from.NativeValue) : default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once RedundantNameQualifier
public static godot_variant CreateFromArray<T>(Collections.Array<T>? from)
=> from != null ? CreateFromArray((godot_array)((Collections.Array)from).NativeValue) : default;
public static godot_variant CreateFromSystemICollection(System.Collections.ICollection? from)
{
if (from == null)
return default; // Nil
using var nativeGodotArray = Marshaling.ConvertICollectionToNativeGodotArray(from);
return CreateFromArray(nativeGodotArray);
}
public static godot_variant CreateFromSystemGenericICollection<T>(
System.Collections.Generic.ICollection<T>? from)
{
if (from == null)
return default; // Nil
using var nativeGodotArray = Marshaling.ConvertGenericICollectionToNativeGodotArray(from);
return CreateFromArray(nativeGodotArray);
}
public static godot_variant CreateFromSystemIEnumerable(System.Collections.IEnumerable? from)
{
if (from == null)
return default; // Nil
using var nativeGodotArray = Marshaling.ConvertIEnumerableToNativeGodotArray(from);
return CreateFromArray(nativeGodotArray);
}
public static godot_variant CreateFromSystemGenericIEnumerable<T>(
System.Collections.Generic.IEnumerable<T>? from)
{
if (from == null)
return default; // Nil
using var nativeGodotArray = Marshaling.ConvertGenericIEnumerableToNativeGodotArray(from);
return CreateFromArray(nativeGodotArray);
}
public static godot_variant CreateFromDictionary(godot_dictionary from)
{
NativeFuncs.godotsharp_variant_new_dictionary(out godot_variant ret, from);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromDictionary(Dictionary? from)
=> from != null ? CreateFromDictionary((godot_dictionary)from.NativeValue) : default;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromDictionary<TKey, TValue>(Dictionary<TKey, TValue>? from)
=> from != null ? CreateFromDictionary((godot_dictionary)((Dictionary)from).NativeValue) : default;
public static godot_variant CreateFromSystemDictionary<TKey, TValue>(
System.Collections.Generic.Dictionary<TKey, TValue>? from) where TKey : notnull
{
if (from == null)
return default; // Nil
var godotDict = new Dictionary();
foreach (var entry in from)
godotDict.Add(entry.Key, entry.Value);
return CreateFromDictionary(godotDict);
}
public static godot_variant CreateFromSystemIDictionary(System.Collections.IDictionary? from)
{
if (from == null)
return default; // Nil
var godotDict = new Dictionary();
foreach (var entry in from)
godotDict.Add(entry, entry);
return CreateFromDictionary(godotDict);
}
public static godot_variant CreateFromSystemGenericIDictionary<TKey, TValue>(
System.Collections.Generic.IDictionary<TKey, TValue>? from)
{
if (from == null)
return default; // Nil
var godotDict = new Dictionary<TKey, TValue>();
foreach (var entry in from)
godotDict.Add(entry.Key, entry.Value);
return CreateFromDictionary(godotDict);
}
public static godot_variant CreateFromStringName(godot_string_name from)
{
NativeFuncs.godotsharp_variant_new_string_name(out godot_variant ret, from);
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromStringName(StringName? from)
=> from != null ? CreateFromStringName((godot_string_name)from.NativeValue) : default;
public static godot_variant CreateFromNodePath(godot_node_path from)
{
NativeFuncs.godotsharp_variant_new_node_path(out godot_variant ret, from);
return ret;
}
public static godot_variant CreateFromGodotObject(IntPtr from)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_variant CreateFromNodePath(NodePath? from)
=> from != null ? CreateFromNodePath((godot_node_path)from.NativeValue) : default;
public static godot_variant CreateFromGodotObjectPtr(IntPtr from)
{
if (from == IntPtr.Zero)
return new godot_variant();
@ -188,6 +367,11 @@ namespace Godot.NativeInterop
return ret;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once RedundantNameQualifier
public static godot_variant CreateFromGodotObject(Godot.Object? from)
=> from != null ? CreateFromGodotObjectPtr(Object.GetPtr(from)) : default;
// We avoid the internal call if the stored type is the same we want.
public static bool ConvertToBool(in godot_variant p_var)
@ -328,32 +512,201 @@ namespace Godot.NativeInterop
p_var.Plane :
NativeFuncs.godotsharp_variant_as_plane(p_var);
public static IntPtr ConvertToGodotObject(in godot_variant p_var)
=> p_var.Type == Variant.Type.Object ? p_var.Object : IntPtr.Zero;
public static RID ConvertToRID(in godot_variant p_var)
=> p_var.Type == Variant.Type.Rid ?
p_var.RID :
NativeFuncs.godotsharp_variant_as_rid(p_var);
public static IntPtr ConvertToGodotObjectPtr(in godot_variant p_var)
=> p_var.Type == Variant.Type.Object ? p_var.Object : IntPtr.Zero;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
// ReSharper disable once RedundantNameQualifier
public static Godot.Object ConvertToGodotObject(in godot_variant p_var)
=> InteropUtils.UnmanagedGetManaged(ConvertToGodotObjectPtr(p_var));
public static string ConvertToStringObject(in godot_variant p_var)
{
switch (p_var.Type)
{
case Variant.Type.Nil:
return ""; // Otherwise, Variant -> String would return the string "Null"
case Variant.Type.String:
{
// We avoid the internal call if the stored type is the same we want.
return Marshaling.ConvertStringToManaged(p_var.String);
}
default:
{
using godot_string godotString = NativeFuncs.godotsharp_variant_as_string(p_var);
return Marshaling.ConvertStringToManaged(godotString);
}
}
}
public static godot_string_name ConvertToStringName(in godot_variant p_var)
=> p_var.Type == Variant.Type.StringName ?
NativeFuncs.godotsharp_string_name_new_copy(p_var.StringName) :
NativeFuncs.godotsharp_variant_as_string_name(p_var);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static StringName ConvertToStringNameObject(in godot_variant p_var)
=> StringName.CreateTakingOwnershipOfDisposableValue(ConvertToStringName(p_var));
public static godot_node_path ConvertToNodePath(in godot_variant p_var)
=> p_var.Type == Variant.Type.NodePath ?
NativeFuncs.godotsharp_node_path_new_copy(p_var.NodePath) :
NativeFuncs.godotsharp_variant_as_node_path(p_var);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static NodePath ConvertToNodePathObject(in godot_variant p_var)
=> NodePath.CreateTakingOwnershipOfDisposableValue(ConvertToNodePath(p_var));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_callable ConvertToCallable(in godot_variant p_var)
=> NativeFuncs.godotsharp_variant_as_callable(p_var);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Callable ConvertToCallableManaged(in godot_variant p_var)
=> Marshaling.ConvertCallableToManaged(ConvertToCallable(p_var));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_signal ConvertToSignal(in godot_variant p_var)
=> NativeFuncs.godotsharp_variant_as_signal(p_var);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static SignalInfo ConvertToSignalInfo(in godot_variant p_var)
=> Marshaling.ConvertSignalToManaged(ConvertToSignal(p_var));
public static godot_array ConvertToArray(in godot_variant p_var)
=> p_var.Type == Variant.Type.Array ?
NativeFuncs.godotsharp_array_new_copy(p_var.Array) :
NativeFuncs.godotsharp_variant_as_array(p_var);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Collections.Array ConvertToArrayObject(in godot_variant p_var)
=> Collections.Array.CreateTakingOwnershipOfDisposableValue(ConvertToArray(p_var));
public static godot_dictionary ConvertToDictionary(in godot_variant p_var)
=> p_var.Type == Variant.Type.Dictionary ?
NativeFuncs.godotsharp_dictionary_new_copy(p_var.Dictionary) :
NativeFuncs.godotsharp_variant_as_dictionary(p_var);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Dictionary ConvertToDictionaryObject(in godot_variant p_var)
=> Dictionary.CreateTakingOwnershipOfDisposableValue(ConvertToDictionary(p_var));
public static byte[] ConvertAsPackedByteArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_byte_array(p_var);
return Marshaling.ConvertNativePackedByteArrayToSystemArray(packedArray);
}
public static int[] ConvertAsPackedInt32ArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int32_array(p_var);
return Marshaling.ConvertNativePackedInt32ArrayToSystemArray(packedArray);
}
public static long[] ConvertAsPackedInt64ArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_int64_array(p_var);
return Marshaling.ConvertNativePackedInt64ArrayToSystemArray(packedArray);
}
public static float[] ConvertAsPackedFloat32ArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float32_array(p_var);
return Marshaling.ConvertNativePackedFloat32ArrayToSystemArray(packedArray);
}
public static double[] ConvertAsPackedFloat64ArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_float64_array(p_var);
return Marshaling.ConvertNativePackedFloat64ArrayToSystemArray(packedArray);
}
public static string[] ConvertAsPackedStringArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_string_array(p_var);
return Marshaling.ConvertNativePackedStringArrayToSystemArray(packedArray);
}
public static Vector2[] ConvertAsPackedVector2ArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector2_array(p_var);
return Marshaling.ConvertNativePackedVector2ArrayToSystemArray(packedArray);
}
public static Vector3[] ConvertAsPackedVector3ArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_vector3_array(p_var);
return Marshaling.ConvertNativePackedVector3ArrayToSystemArray(packedArray);
}
public static Color[] ConvertAsPackedColorArrayToSystemArray(in godot_variant p_var)
{
using var packedArray = NativeFuncs.godotsharp_variant_as_packed_color_array(p_var);
return Marshaling.ConvertNativePackedColorArrayToSystemArray(packedArray);
}
public static T[] ConvertToSystemArrayOfSupportedType<T>(in godot_variant p_var)
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfType<T>(godotArray);
}
public static T[] ConvertToSystemArrayOfGodotObject<T>(in godot_variant p_var)
// ReSharper disable once RedundantNameQualifier
where T : Godot.Object
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType<T>(godotArray);
}
// ReSharper disable once RedundantNameQualifier
public static Godot.Object[] ConvertToSystemArrayOfGodotObject(in godot_variant p_var, Type type)
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(godotArray, type);
}
public static object[] ConvertToSystemArrayOfVariant(in godot_variant p_var)
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Marshaling.ConvertNativeGodotArrayToSystemArray(godotArray);
}
public static Array<T> ConvertToGenericArrayObject<T>(in godot_variant p_var) =>
new(ConvertToArrayObject(p_var));
public static Dictionary<TKey, TValue> ConvertToGenericDictionaryObject<TKey, TValue>(in godot_variant p_var) =>
new(ConvertToDictionaryObject(p_var));
public static System.Collections.Generic.List<T> ConvertToSystemGenericList<T>(in godot_variant p_var)
{
var godotArray = ConvertToArrayObject(p_var);
var res = new System.Collections.Generic.List<T>(godotArray.Count);
foreach (object elem in godotArray)
res.Add((T)elem);
return res;
}
public static System.Collections.Generic.Dictionary<TKey, TValue>
ConvertToSystemGenericDictionary<TKey, TValue>(in godot_variant p_var)
where TKey : notnull
{
var godotDictionary = ConvertToDictionaryObject(p_var);
var res = new System.Collections.Generic.Dictionary<TKey, TValue>(godotDictionary.Count);
foreach (System.Collections.Generic.KeyValuePair<TKey, TValue> pair in godotDictionary)
res.Add(pair.Key, pair.Value);
return res;
}
}
}