C#: Array, Dictionary and marshaling refactoring

- Array and Dictionary now store `Variant` instead of `System.Object`.
- Removed generic Array and Dictionary.
  They cause too much issues, heavily relying on reflection and
  very limited by the lack of a generic specialization.
- Removed support for non-Godot collections.
  Support for them also relied heavily on reflection for marshaling.
  Support for them will likely be re-introduced in the future, but
  it will have to rely on source generators instead of reflection.
- Reduced our use of reflection.
  The remaining usages will be moved to source generators soon.
  The only usage that I'm not sure yet how to replace is dynamic
  invocation of delegates.
This commit is contained in:
Ignacio Roldán Etcheverry 2022-07-28 17:41:50 +02:00
parent 344f5028d4
commit 3123be2384
30 changed files with 766 additions and 1723 deletions

View file

@ -80,28 +80,9 @@ namespace Godot.SourceGenerators.Sample
[Export] private Vector3[] field_Vector3Array = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right };
[Export] private Color[] field_ColorArray = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige };
[Export] private Godot.Object[] field_GodotObjectOrDerivedArray = { null };
// Generics
[Export] private Godot.Collections.Dictionary<string, string> field_GodotGenericDictionary =
new Godot.Collections.Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } };
[Export] private Godot.Collections.Array<string> field_GodotGenericArray =
new Godot.Collections.Array<string> { "elem1", "elem2", "elem3" };
[Export] private System.Collections.Generic.Dictionary<string, string> field_SystemGenericDictionary =
new System.Collections.Generic.Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } };
[Export] private System.Collections.Generic.List<string> field_SystemGenericList =
new System.Collections.Generic.List<string> { "elem1", "elem2", "elem3" };
[Export] private System.Collections.Generic.IDictionary<string, string> field_GenericIDictionary =
new System.Collections.Generic.Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } };
[Export] private System.Collections.Generic.ICollection<string> field_GenericICollection =
new System.Collections.Generic.List<string> { "elem1", "elem2", "elem3" };
[Export] private System.Collections.Generic.IEnumerable<string> field_GenericIEnumerable =
new System.Collections.Generic.List<string> { "elem1", "elem2", "elem3" };
[Export] private StringName[] field_StringNameArray = { "foo", "bar" };
[Export] private NodePath[] field_NodePathArray = { "foo", "bar" };
[Export] private RID[] field_RIDArray = { default, default, default };
// Variant
[Export] private Variant field_Variant = "foo";
@ -118,15 +99,5 @@ namespace Godot.SourceGenerators.Sample
[Export] private Godot.Collections.Array field_GodotArray =
new() { "foo", 10, Vector2.Up, Colors.Chocolate };
[Export] private System.Collections.IDictionary field_IDictionary =
new System.Collections.Generic.Dictionary<object, object>
{ { "foo", 10 }, { Vector2.Up, Colors.Chocolate } };
[Export] private System.Collections.ICollection field_ICollection =
new System.Collections.Generic.List<object> { "foo", 10, Vector2.Up, Colors.Chocolate };
[Export] private System.Collections.IEnumerable field_IEnumerable =
new System.Collections.Generic.List<object> { "foo", 10, Vector2.Up, Colors.Chocolate };
}
}

View file

@ -80,28 +80,9 @@ namespace Godot.SourceGenerators.Sample
[Export] private Vector3[] property_Vector3Array { get; set; } = { Vector3.Up, Vector3.Down, Vector3.Left, Vector3.Right };
[Export] private Color[] property_ColorArray { get; set; } = { Colors.Aqua, Colors.Aquamarine, Colors.Azure, Colors.Beige };
[Export] private Godot.Object[] property_GodotObjectOrDerivedArray { get; set; } = { null };
// Generics
[Export] private Godot.Collections.Dictionary<string, string> property_GodotGenericDictionary { get; set; } =
new Godot.Collections.Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } };
[Export] private Godot.Collections.Array<string> property_GodotGenericArray { get; set; } =
new Godot.Collections.Array<string> { "elem1", "elem2", "elem3" };
[Export] private System.Collections.Generic.Dictionary<string, string> property_SystemGenericDictionary { get; set; } =
new System.Collections.Generic.Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } };
[Export] private System.Collections.Generic.List<string> property_SystemGenericList { get; set; } =
new System.Collections.Generic.List<string> { "elem1", "elem2", "elem3" };
[Export] private System.Collections.Generic.IDictionary<string, string> property_GenericIDictionary { get; set; } =
new System.Collections.Generic.Dictionary<string, string> { { "key1", "value1" }, { "key2", "value2" } };
[Export] private System.Collections.Generic.ICollection<string> property_GenericICollection { get; set; } =
new System.Collections.Generic.List<string> { "elem1", "elem2", "elem3" };
[Export] private System.Collections.Generic.IEnumerable<string> property_GenericIEnumerable { get; set; } =
new System.Collections.Generic.List<string> { "elem1", "elem2", "elem3" };
[Export] private StringName[] field_StringNameArray { get; set; } = { "foo", "bar" };
[Export] private NodePath[] field_NodePathArray { get; set; } = { "foo", "bar" };
[Export] private RID[] field_RIDArray { get; set; } = { default, default, default };
// Variant
[Export] private Variant property_Variant { get; set; } = "foo";
@ -118,15 +99,5 @@ namespace Godot.SourceGenerators.Sample
[Export] private Godot.Collections.Array property_GodotArray { get; set; } =
new() { "foo", 10, Vector2.Up, Colors.Chocolate };
[Export] private System.Collections.IDictionary property_IDictionary { get; set; } =
new System.Collections.Generic.Dictionary<object, object>
{ { "foo", 10 }, { Vector2.Up, Colors.Chocolate } };
[Export] private System.Collections.ICollection property_ICollection { get; set; } =
new System.Collections.Generic.List<object> { "foo", 10, Vector2.Up, Colors.Chocolate };
[Export] private System.Collections.IEnumerable property_IEnumerable { get; set; } =
new System.Collections.Generic.List<object> { "foo", 10, Vector2.Up, Colors.Chocolate };
}
}

View file

@ -53,16 +53,9 @@ namespace Godot.SourceGenerators
Vector3Array,
ColorArray,
GodotObjectOrDerivedArray,
SystemArrayOfSupportedType,
// Generics
GodotGenericDictionary,
GodotGenericArray,
SystemGenericDictionary,
SystemGenericList,
GenericIDictionary,
GenericICollection,
GenericIEnumerable,
SystemArrayOfStringName,
SystemArrayOfNodePath,
SystemArrayOfRID,
// Variant
Variant,
@ -74,8 +67,5 @@ namespace Godot.SourceGenerators
RID,
GodotDictionary,
GodotArray,
IDictionary,
ICollection,
IEnumerable,
}
}

View file

@ -10,17 +10,6 @@ namespace Godot.SourceGenerators
public class TypeCache
{
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; }
public INamedTypeSymbol SystemGenericList { get; }
public TypeCache(GeneratorExecutionContext context)
{
@ -31,13 +20,6 @@ namespace Godot.SourceGenerators
}
GodotObjectType = GetTypeByMetadataNameOrThrow("Godot.Object");
GodotGenericDictionary = GetTypeByMetadataNameOrThrow("Godot.Collections.Dictionary`2");
GodotGenericArray = GetTypeByMetadataNameOrThrow("Godot.Collections.Array`1");
IDictionary = GetTypeByMetadataNameOrThrow("System.Collections.IDictionary");
ICollection = GetTypeByMetadataNameOrThrow("System.Collections.ICollection");
GenericIDictionary = GetTypeByMetadataNameOrThrow("System.Collections.Generic.IDictionary`2");
SystemGenericDictionary = GetTypeByMetadataNameOrThrow("System.Collections.Generic.Dictionary`2");
SystemGenericList = GetTypeByMetadataNameOrThrow("System.Collections.Generic.List`1");
}
}
@ -86,14 +68,9 @@ namespace Godot.SourceGenerators
MarshalType.Vector3Array => VariantType.PackedVector3Array,
MarshalType.ColorArray => VariantType.PackedColorArray,
MarshalType.GodotObjectOrDerivedArray => VariantType.Array,
MarshalType.SystemArrayOfSupportedType => VariantType.Array,
MarshalType.GodotGenericDictionary => VariantType.Dictionary,
MarshalType.GodotGenericArray => VariantType.Array,
MarshalType.SystemGenericDictionary => VariantType.Dictionary,
MarshalType.SystemGenericList => VariantType.Array,
MarshalType.GenericIDictionary => VariantType.Dictionary,
MarshalType.GenericICollection => VariantType.Array,
MarshalType.GenericIEnumerable => VariantType.Array,
MarshalType.SystemArrayOfStringName => VariantType.Array,
MarshalType.SystemArrayOfNodePath => VariantType.Array,
MarshalType.SystemArrayOfRID => VariantType.Array,
MarshalType.Variant => VariantType.Nil,
MarshalType.GodotObjectOrDerived => VariantType.Object,
MarshalType.StringName => VariantType.StringName,
@ -101,9 +78,6 @@ namespace Godot.SourceGenerators
MarshalType.RID => VariantType.Rid,
MarshalType.GodotDictionary => VariantType.Dictionary,
MarshalType.GodotArray => VariantType.Array,
MarshalType.IDictionary => VariantType.Dictionary,
MarshalType.ICollection => VariantType.Array,
MarshalType.IEnumerable => VariantType.Array,
_ => null
};
@ -212,54 +186,22 @@ namespace Godot.SourceGenerators
return MarshalType.Vector3Array;
case { Name: "Color" }:
return MarshalType.ColorArray;
case { Name: "StringName" }:
return MarshalType.SystemArrayOfStringName;
case { Name: "NodePath" }:
return MarshalType.SystemArrayOfNodePath;
case { Name: "RID" }:
return MarshalType.SystemArrayOfRID;
}
}
if (ConvertManagedTypeToMarshalType(elementType, typeCache) != null)
return MarshalType.SystemArrayOfSupportedType;
return null;
}
else if (type is INamedTypeSymbol { IsGenericType: true } genericType)
{
var genericTypeDef = genericType.ConstructedFrom;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GodotGenericDictionary))
return MarshalType.GodotGenericDictionary;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GodotGenericArray))
return MarshalType.GodotGenericArray;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.SystemGenericDictionary))
return MarshalType.SystemGenericDictionary;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.SystemGenericList))
return MarshalType.SystemGenericList;
if (SymbolEqualityComparer.Default.Equals(genericTypeDef, typeCache.GenericIDictionary))
return MarshalType.GenericIDictionary;
return genericTypeDef.SpecialType switch
{
SpecialType.System_Collections_Generic_ICollection_T => MarshalType.GenericICollection,
SpecialType.System_Collections_Generic_IEnumerable_T => MarshalType.GenericIEnumerable,
_ => null
};
}
else
{
if (type.SimpleDerivesFrom(typeCache.GodotObjectType))
return MarshalType.GodotObjectOrDerived;
if (SymbolEqualityComparer.Default.Equals(type, typeCache.IDictionary))
return MarshalType.IDictionary;
if (SymbolEqualityComparer.Default.Equals(type, typeCache.ICollection))
return MarshalType.ICollection;
if (specialType == SpecialType.System_Collections_IEnumerable)
return MarshalType.IEnumerable;
if (type.ContainingAssembly.Name == "GodotSharp")
{
switch (type.ContainingNamespace.Name)
@ -341,13 +283,9 @@ namespace Godot.SourceGenerators
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 VariantUtils = "global::Godot.NativeInterop.VariantUtils";
public static StringBuilder AppendVariantToManagedExpr(this StringBuilder source,
public static StringBuilder AppendNativeVariantToManagedExpr(this StringBuilder source,
string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
{
return marshalType switch
@ -438,30 +376,12 @@ namespace Godot.SourceGenerators
MarshalType.GodotObjectOrDerivedArray =>
source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<",
((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedName(), ">(", 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.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 =>
@ -477,16 +397,12 @@ namespace Godot.SourceGenerators
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(
public static StringBuilder AppendManagedToNativeVariantExpr(
this StringBuilder source, string inputExpr, MarshalType marshalType)
{
return marshalType switch
@ -575,22 +491,12 @@ namespace Godot.SourceGenerators
source.Append(VariantUtils, ".CreateFromPackedColorArray(", inputExpr, ")"),
MarshalType.GodotObjectOrDerivedArray =>
source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", 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.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 =>
@ -605,15 +511,140 @@ namespace Godot.SourceGenerators
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")
};
}
public static StringBuilder AppendVariantToManagedExpr(this StringBuilder source,
string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
{
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.FullQualifiedName(), ")", 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.FullQualifiedName(), ">()"),
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.FullQualifiedName(), ")", 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()"),
_ => throw new ArgumentOutOfRangeException(nameof(marshalType), marshalType,
"Received unexpected marshal type")
};
}
public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source,
string inputExpr, MarshalType marshalType)
{
switch (marshalType)
{
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:
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");
}
}
}
}

View file

@ -381,7 +381,7 @@ namespace Godot.SourceGenerators
if (i != 0)
source.Append(", ");
source.AppendVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
source.AppendNativeVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
method.ParamTypeSymbols[i], method.ParamTypes[i]);
}
@ -391,7 +391,7 @@ namespace Godot.SourceGenerators
{
source.Append(" ret = ");
source.AppendManagedToVariantExpr("callRet", method.RetType.Value);
source.AppendManagedToNativeVariantExpr("callRet", method.RetType.Value);
source.Append(";\n");
source.Append(" return true;\n");

View file

@ -293,7 +293,7 @@ namespace Godot.SourceGenerators
.Append(" ")
.Append(propertyMemberName)
.Append(" = ")
.AppendVariantToManagedExpr("value", propertyTypeSymbol, propertyMarshalType)
.AppendNativeVariantToManagedExpr("value", propertyTypeSymbol, propertyMarshalType)
.Append(";\n")
.Append(" return true;\n")
.Append(" }\n");
@ -315,7 +315,7 @@ namespace Godot.SourceGenerators
.Append(propertyMemberName)
.Append(") {\n")
.Append(" value = ")
.AppendManagedToVariantExpr(propertyMemberName, propertyMarshalType)
.AppendManagedToNativeVariantExpr(propertyMemberName, propertyMarshalType)
.Append(";\n")
.Append(" return true;\n")
.Append(" }\n");

View file

@ -160,8 +160,8 @@ namespace Godot.SourceGenerators
source.Append(" info.AddProperty(GodotInternal.PropName_")
.Append(propertyName)
.Append(", this.")
.Append(propertyName)
.Append(", ")
.AppendManagedToVariantExpr(string.Concat("this.", propertyName), property.Type)
.Append(");\n");
}
@ -173,8 +173,8 @@ namespace Godot.SourceGenerators
source.Append(" info.AddProperty(GodotInternal.PropName_")
.Append(fieldName)
.Append(", this.")
.Append(fieldName)
.Append(", ")
.AppendManagedToVariantExpr(string.Concat("this.", fieldName), field.Type)
.Append(");\n");
}
@ -202,19 +202,17 @@ namespace Godot.SourceGenerators
foreach (var property in godotClassProperties)
{
string propertyName = property.PropertySymbol.Name;
string propertyTypeQualifiedName = property.PropertySymbol.Type.FullQualifiedName();
source.Append(" if (info.TryGetProperty<")
.Append(propertyTypeQualifiedName)
.Append(">(GodotInternal.PropName_")
source.Append(" if (info.TryGetProperty(GodotInternal.PropName_")
.Append(propertyName)
.Append(", out var _value_")
.Append(propertyName)
.Append("))\n")
.Append(" this.")
.Append(propertyName)
.Append(" = _value_")
.Append(propertyName)
.Append(" = ")
.AppendVariantToManagedExpr(string.Concat("_value_", propertyName),
property.PropertySymbol.Type, property.Type)
.Append(";\n");
}
@ -223,19 +221,17 @@ namespace Godot.SourceGenerators
foreach (var field in godotClassFields)
{
string fieldName = field.FieldSymbol.Name;
string fieldTypeQualifiedName = field.FieldSymbol.Type.FullQualifiedName();
source.Append(" if (info.TryGetProperty<")
.Append(fieldTypeQualifiedName)
.Append(">(GodotInternal.PropName_")
source.Append(" if (info.TryGetProperty(GodotInternal.PropName_")
.Append(fieldName)
.Append(", out var _value_")
.Append(fieldName)
.Append("))\n")
.Append(" this.")
.Append(fieldName)
.Append(" = _value_")
.Append(fieldName)
.Append(" = ")
.AppendVariantToManagedExpr(string.Concat("_value_", fieldName),
field.FieldSymbol.Type, field.Type)
.Append(";\n");
}

View file

@ -397,7 +397,7 @@ namespace Godot.SourceGenerators
if (i != 0)
source.Append(", ");
source.AppendVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
source.AppendNativeVariantToManagedExpr(string.Concat("args[", i.ToString(), "]"),
invokeMethodData.ParamTypeSymbols[i], invokeMethodData.ParamTypes[i]);
}

View file

@ -20,7 +20,7 @@ namespace GodotTools.Build
public bool OnlyClean { get; private set; }
// TODO Use List once we have proper serialization
public Array<string> CustomProperties { get; private set; } = new Array<string>();
public Godot.Collections.Array CustomProperties { get; private set; } = new();
public string LogsDirPath =>
Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");

View file

@ -1,7 +1,6 @@
using Godot;
using System;
using System.Diagnostics.CodeAnalysis;
using Godot.Collections;
using GodotTools.Internals;
using File = GodotTools.Utils.File;
using Path = System.IO.Path;
@ -59,7 +58,7 @@ namespace GodotTools.Build
}
// TODO Use List once we have proper serialization.
private Array<BuildIssue> _issues = new Array<BuildIssue>();
private Godot.Collections.Array _issues = new();
private ItemList _issuesList;
private PopupMenu _issuesListContextMenu;
private TextEdit _buildLog;
@ -129,12 +128,12 @@ namespace GodotTools.Build
if (issueIndex < 0 || issueIndex >= _issues.Count)
throw new IndexOutOfRangeException("Issue index out of range");
BuildIssue issue = _issues[issueIndex];
var issue = (BuildIssue)_issues[issueIndex];
if (string.IsNullOrEmpty(issue.ProjectFile) && string.IsNullOrEmpty(issue.File))
return;
string projectDir = issue.ProjectFile.Length > 0 ?
string projectDir = !string.IsNullOrEmpty(issue.ProjectFile) ?
issue.ProjectFile.GetBaseDir() :
_buildInfo.Solution.GetBaseDir();
@ -163,7 +162,7 @@ namespace GodotTools.Build
{
for (int i = 0; i < _issues.Count; i++)
{
BuildIssue issue = _issues[i];
var issue = (BuildIssue)_issues[i];
if (!(issue.Warning ? WarningsVisible : ErrorsVisible))
continue;

View file

@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GodotTools.BuildLogger;
@ -156,9 +157,9 @@ namespace GodotTools.Build
AddLoggerArgument(buildInfo, arguments);
// Custom properties
foreach (string customProperty in buildInfo.CustomProperties)
foreach (var customProperty in buildInfo.CustomProperties)
{
arguments.Add("-p:" + customProperty);
arguments.Add("-p:" + (string)customProperty);
}
}
@ -198,9 +199,9 @@ namespace GodotTools.Build
AddLoggerArgument(buildInfo, arguments);
// Custom properties
foreach (string customProperty in buildInfo.CustomProperties)
foreach (var customProperty in buildInfo.CustomProperties)
{
arguments.Add("-p:" + customProperty);
arguments.Add("-p:" + (string)customProperty);
}
// Publish output directory

View file

@ -481,9 +481,9 @@ namespace GodotTools
_editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
["type"] = Variant.Type.Int,
["type"] = (int)Variant.Type.Int,
["name"] = "mono/editor/external_editor",
["hint"] = PropertyHint.Enum,
["hint"] = (int)PropertyHint.Enum,
["hint_string"] = settingsHintStr
});

View file

@ -30,9 +30,9 @@ namespace GodotTools.Ides.Rider
Globals.EditorDef(EditorPathSettingName, "Optional");
editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
{
["type"] = Variant.Type.String,
["type"] = (int)Variant.Type.String,
["name"] = EditorPathSettingName,
["hint"] = PropertyHint.File,
["hint"] = (int)PropertyHint.File,
["hint_string"] = ""
});
}

View file

@ -1664,8 +1664,7 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
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 << "(" << sformat(arg_type->cs_variant_to_managed,
"args[" + itos(i) + "]", arg_type->cs_type, arg_type->name) << ")";
output << "new " << arg_cs_type << "(" << sformat(arg_type->cs_variant_to_managed, "args[" + itos(i) + "]", arg_type->cs_type, arg_type->name) << ")";
} else {
output << sformat(arg_type->cs_variant_to_managed,
"args[" + itos(i) + "]", arg_type->cs_type, arg_type->name);
@ -2892,8 +2891,13 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
String() + "Return type is reference but hint is not '" _STR(PROPERTY_HINT_RESOURCE_TYPE) "'." +
" Are you returning a reference type by pointer? Method: '" + itype.name + "." + imethod.name + "'.");
} else if (return_info.type == Variant::ARRAY && return_info.hint == PROPERTY_HINT_ARRAY_TYPE) {
// TODO: Enable once generic Array is re-implemented
#if 0
imethod.return_type.cname = Variant::get_type_name(return_info.type) + "_@generic";
imethod.return_type.generic_type_parameters.push_back(TypeReference(return_info.hint_string));
#else
imethod.return_type.cname = Variant::get_type_name(return_info.type);
#endif
} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
imethod.return_type.cname = return_info.hint_string;
} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
@ -2924,8 +2928,13 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
} else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
// TODO: Enable once generic Array is re-implemented
#if 0
iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic";
iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
#else
iarg.type.cname = Variant::get_type_name(arginfo.type);
#endif
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {
@ -3032,8 +3041,13 @@ bool BindingsGenerator::_populate_object_type_interfaces() {
} else if (arginfo.class_name != StringName()) {
iarg.type.cname = arginfo.class_name;
} else if (arginfo.type == Variant::ARRAY && arginfo.hint == PROPERTY_HINT_ARRAY_TYPE) {
// TODO: Enable once generic Array is re-implemented
#if 0
iarg.type.cname = Variant::get_type_name(arginfo.type) + "_@generic";
iarg.type.generic_type_parameters.push_back(TypeReference(arginfo.hint_string));
#else
iarg.type.cname = Variant::get_type_name(arginfo.type);
#endif
} else if (arginfo.hint == PROPERTY_HINT_RESOURCE_TYPE) {
iarg.type.cname = arginfo.hint_string;
} else if (arginfo.type == Variant::NIL) {

View file

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using Godot.NativeInterop;
@ -14,8 +13,8 @@ namespace Godot.Collections
/// such as <see cref="System.Array"/> or <see cref="List{T}"/>.
/// </summary>
public sealed class Array :
IList<object>,
IReadOnlyList<object>,
IList<Variant>,
IReadOnlyList<Variant>,
ICollection,
IDisposable
{
@ -37,22 +36,90 @@ namespace Godot.Collections
/// </summary>
/// <param name="collection">The collection of elements to construct from.</param>
/// <returns>A new Godot Array.</returns>
public Array(IEnumerable collection) : this()
public Array(IEnumerable<Variant> collection) : this()
{
if (collection == null)
throw new ArgumentNullException(nameof(collection));
foreach (object element in collection)
foreach (Variant element in collection)
Add(element);
}
// TODO: This must be removed. Lots of silent mistakes as it takes pretty much anything.
/// <summary>
/// Constructs a new <see cref="Array"/> from the given objects.
/// </summary>
/// <param name="array">The objects to put in the new array.</param>
/// <returns>A new Godot Array.</returns>
public Array(params object[] array) : this()
public Array(Variant[] array) : this()
{
if (array == null)
throw new ArgumentNullException(nameof(array));
NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
int length = array.Length;
Resize(length);
for (int i = 0; i < length; i++)
this[i] = array[i];
}
public Array(Span<StringName> array) : this()
{
if (array == null)
throw new ArgumentNullException(nameof(array));
NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
int length = array.Length;
Resize(length);
for (int i = 0; i < length; i++)
this[i] = array[i];
}
public Array(Span<NodePath> array) : this()
{
if (array == null)
throw new ArgumentNullException(nameof(array));
NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
int length = array.Length;
Resize(length);
for (int i = 0; i < length; i++)
this[i] = array[i];
}
public Array(Span<RID> array) : this()
{
if (array == null)
throw new ArgumentNullException(nameof(array));
NativeValue = (godot_array.movable)NativeFuncs.godotsharp_array_new();
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
int length = array.Length;
Resize(length);
for (int i = 0; i < length; i++)
this[i] = array[i];
}
// We must use ReadOnlySpan instead of Span here as this can accept implicit conversions
// from derived types (e.g.: Node[]). Implicit conversion from Derived[] to Base[] are
// fine as long as the array is not mutated. However, Span does this type checking at
// instantiation, so it's not possible to use it even when not mutating anything.
// ReSharper disable once RedundantNameQualifier
public Array(ReadOnlySpan<Godot.Object> array) : this()
{
if (array == null)
throw new ArgumentNullException(nameof(array));
@ -170,15 +237,15 @@ namespace Godot.Collections
}
/// <summary>
/// Returns the object at the given <paramref name="index"/>.
/// Returns the item at the given <paramref name="index"/>.
/// </summary>
/// <value>The object at the given <paramref name="index"/>.</value>
public unsafe object this[int index]
/// <value>The <see cref="Variant"/> item at the given <paramref name="index"/>.</value>
public unsafe Variant this[int index]
{
get
{
GetVariantBorrowElementAt(index, out godot_variant borrowElem);
return Marshaling.ConvertVariantToManagedObject(borrowElem);
return Variant.CreateCopyingBorrowed(borrowElem);
}
set
{
@ -186,29 +253,30 @@ namespace Godot.Collections
throw new ArgumentOutOfRangeException(nameof(index));
var self = (godot_array)NativeValue;
godot_variant* ptrw = NativeFuncs.godotsharp_array_ptrw(ref self);
ptrw[index] = Marshaling.ConvertManagedObjectToVariant(value);
godot_variant* itemPtr = &ptrw[index];
(*itemPtr).Dispose();
*itemPtr = value.CopyNativeVariant();
}
}
/// <summary>
/// Adds an object to the end of this <see cref="Array"/>.
/// Adds an item to the end of this <see cref="Array"/>.
/// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
/// </summary>
/// <param name="item">The object to add.</param>
/// <returns>The new size after adding the object.</returns>
public void Add(object item)
/// <param name="item">The <see cref="Variant"/> item to add.</param>
public void Add(Variant item)
{
using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item);
godot_variant variantValue = (godot_variant)item.NativeVar;
var self = (godot_array)NativeValue;
_ = NativeFuncs.godotsharp_array_add(ref self, variantValue);
}
/// <summary>
/// Checks if this <see cref="Array"/> contains the given object.
/// Checks if this <see cref="Array"/> contains the given item.
/// </summary>
/// <param name="item">The item to look for.</param>
/// <returns>Whether or not this array contains the given object.</returns>
public bool Contains(object item) => IndexOf(item) != -1;
/// <param name="item">The <see cref="Variant"/> item to look for.</param>
/// <returns>Whether or not this array contains the given item.</returns>
public bool Contains(Variant item) => IndexOf(item) != -1;
/// <summary>
/// Erases all items from this <see cref="Array"/>.
@ -216,32 +284,32 @@ namespace Godot.Collections
public void Clear() => Resize(0);
/// <summary>
/// Searches this <see cref="Array"/> for an object
/// Searches this <see cref="Array"/> for an item
/// and returns its index or -1 if not found.
/// </summary>
/// <param name="item">The object to search for.</param>
/// <returns>The index of the object, or -1 if not found.</returns>
public int IndexOf(object item)
/// <param name="item">The <see cref="Variant"/> item to search for.</param>
/// <returns>The index of the item, or -1 if not found.</returns>
public int IndexOf(Variant item)
{
using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item);
godot_variant variantValue = (godot_variant)item.NativeVar;
var self = (godot_array)NativeValue;
return NativeFuncs.godotsharp_array_index_of(ref self, variantValue);
}
/// <summary>
/// Inserts a new object at a given position in the array.
/// Inserts a new item at a given position in the array.
/// The position must be a valid position of an existing item,
/// or the position at the end of the array.
/// Existing items will be moved to the right.
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="item">The object to insert.</param>
public void Insert(int index, object item)
/// <param name="item">The <see cref="Variant"/> item to insert.</param>
public void Insert(int index, Variant item)
{
if (index < 0 || index > Count)
throw new ArgumentOutOfRangeException(nameof(index));
using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item);
godot_variant variantValue = (godot_variant)item.NativeVar;
var self = (godot_array)NativeValue;
NativeFuncs.godotsharp_array_insert(ref self, index, variantValue);
}
@ -251,7 +319,7 @@ namespace Godot.Collections
/// from this <see cref="Array"/>.
/// </summary>
/// <param name="item">The value to remove.</param>
public bool Remove(object item)
public bool Remove(Variant item)
{
int index = IndexOf(item);
if (index >= 0)
@ -285,19 +353,19 @@ namespace Godot.Collections
/// <returns>The number of elements.</returns>
public int Count => NativeValue.DangerousSelfRef.Size;
public bool IsSynchronized => false;
bool ICollection.IsSynchronized => false;
public object SyncRoot => false;
object ICollection.SyncRoot => false;
public bool IsReadOnly => false;
bool ICollection<Variant>.IsReadOnly => false;
/// <summary>
/// Copies the elements of this <see cref="Array"/> to the given
/// untyped C# array, starting at the given index.
/// <see cref="Variant"/> C# array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
/// <param name="arrayIndex">The index to start at.</param>
public void CopyTo(object[] array, int arrayIndex)
public void CopyTo(Variant[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
@ -320,8 +388,7 @@ namespace Godot.Collections
{
for (int i = 0; i < count; i++)
{
object obj = Marshaling.ConvertVariantToManagedObject(NativeValue.DangerousSelfRef.Elements[i]);
array[arrayIndex] = obj;
array[arrayIndex] = Variant.CreateCopyingBorrowed(NativeValue.DangerousSelfRef.Elements[i]);
arrayIndex++;
}
}
@ -364,37 +431,13 @@ namespace Godot.Collections
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>
/// Gets an enumerator for this <see cref="Array"/>.
/// </summary>
/// <returns>An enumerator.</returns>
public IEnumerator<object> GetEnumerator()
public IEnumerator<Variant> GetEnumerator()
{
int count = Count;
@ -436,289 +479,4 @@ namespace Godot.Collections
elem = NativeValue.DangerousSelfRef.Elements[index];
}
}
internal interface IGenericGodotArray
{
Array UnderlyingArray { get; }
Type TypeOfElements { get; }
}
// TODO: Now we should be able to avoid boxing
/// <summary>
/// Typed wrapper around Godot's Array class, an array of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine. Otherwise prefer .NET collections
/// such as arrays or <see cref="List{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the array.</typeparam>
[SuppressMessage("ReSharper", "RedundantExtendsListEntry")]
[SuppressMessage("Naming", "CA1710", MessageId = "Identifiers should have correct suffix")]
public sealed class Array<T> : IList<T>, ICollection<T>, IEnumerable<T>, IGenericGodotArray
{
private readonly Array _underlyingArray;
internal ref godot_array.movable NativeValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _underlyingArray.NativeValue;
}
// ReSharper disable StaticMemberInGenericType
// Warning is about unique static fields being created for each generic type combination:
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
// In our case this is exactly what we want.
private static readonly Type TypeOfElements = typeof(T);
// ReSharper restore StaticMemberInGenericType
Array IGenericGodotArray.UnderlyingArray => _underlyingArray;
Type IGenericGodotArray.TypeOfElements => TypeOfElements;
/// <summary>
/// Constructs a new empty <see cref="Array{T}"/>.
/// </summary>
public Array()
{
_underlyingArray = new Array();
}
/// <summary>
/// Constructs a new <see cref="Array{T}"/> from the given collection's elements.
/// </summary>
/// <param name="collection">The collection of elements to construct from.</param>
/// <returns>A new Godot Array.</returns>
public Array(IEnumerable<T> collection)
{
if (collection == null)
throw new ArgumentNullException(nameof(collection));
_underlyingArray = new Array(collection);
}
/// <summary>
/// Constructs a new <see cref="Array{T}"/> from the given items.
/// </summary>
/// <param name="array">The items to put in the new array.</param>
/// <returns>A new Godot Array.</returns>
public Array(params T[] array) : this()
{
if (array == null)
throw new ArgumentNullException(nameof(array));
_underlyingArray = new Array(array);
}
/// <summary>
/// Constructs a typed <see cref="Array{T}"/> from an untyped <see cref="Array"/>.
/// </summary>
/// <param name="array">The untyped array to construct from.</param>
public Array(Array array)
{
_underlyingArray = array;
}
// Explicit name to make it very clear
internal static Array<T> CreateTakingOwnershipOfDisposableValue(godot_array nativeValueToOwn)
=> new Array<T>(Array.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Array{T}"/> to an untyped <see cref="Array"/>.
/// </summary>
/// <param name="from">The typed array to convert.</param>
public static explicit operator Array(Array<T> from)
{
return from?._underlyingArray;
}
/// <summary>
/// Duplicates this <see cref="Array{T}"/>.
/// </summary>
/// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
/// <returns>A new Godot Array.</returns>
public Array<T> Duplicate(bool deep = false)
{
return new Array<T>(_underlyingArray.Duplicate(deep));
}
/// <summary>
/// Resizes this <see cref="Array{T}"/> to the given size.
/// </summary>
/// <param name="newSize">The new size of the array.</param>
/// <returns><see cref="Error.Ok"/> if successful, or an error code.</returns>
public Error Resize(int newSize)
{
return _underlyingArray.Resize(newSize);
}
/// <summary>
/// Shuffles the contents of this <see cref="Array{T}"/> into a random order.
/// </summary>
public void Shuffle()
{
_underlyingArray.Shuffle();
}
/// <summary>
/// Concatenates these two <see cref="Array{T}"/>s.
/// </summary>
/// <param name="left">The first array.</param>
/// <param name="right">The second array.</param>
/// <returns>A new Godot Array with the contents of both arrays.</returns>
public static Array<T> operator +(Array<T> left, Array<T> right)
{
if (left == null)
{
if (right == null)
return new Array<T>();
return right.Duplicate(deep: false);
}
if (right == null)
return left.Duplicate(deep: false);
return new Array<T>(left._underlyingArray + right._underlyingArray);
}
// IList<T>
/// <summary>
/// Returns the value at the given <paramref name="index"/>.
/// </summary>
/// <value>The value at the given <paramref name="index"/>.</value>
public T this[int index]
{
get => (T)_underlyingArray.GetAtAsType(index, TypeOfElements);
set => _underlyingArray[index] = value;
}
/// <summary>
/// Searches this <see cref="Array{T}"/> for an item
/// and returns its index or -1 if not found.
/// </summary>
/// <param name="item">The item to search for.</param>
/// <returns>The index of the item, or -1 if not found.</returns>
public int IndexOf(T item)
{
return _underlyingArray.IndexOf(item);
}
/// <summary>
/// Inserts a new item at a given position in the <see cref="Array{T}"/>.
/// The position must be a valid position of an existing item,
/// or the position at the end of the array.
/// Existing items will be moved to the right.
/// </summary>
/// <param name="index">The index to insert at.</param>
/// <param name="item">The item to insert.</param>
public void Insert(int index, T item)
{
_underlyingArray.Insert(index, item);
}
/// <summary>
/// Removes an element from this <see cref="Array{T}"/> by index.
/// </summary>
/// <param name="index">The index of the element to remove.</param>
public void RemoveAt(int index)
{
_underlyingArray.RemoveAt(index);
}
// ICollection<T>
/// <summary>
/// Returns the number of elements in this <see cref="Array{T}"/>.
/// This is also known as the size or length of the array.
/// </summary>
/// <returns>The number of elements.</returns>
public int Count => _underlyingArray.Count;
bool ICollection<T>.IsReadOnly => false;
/// <summary>
/// Adds an item to the end of this <see cref="Array{T}"/>.
/// This is the same as <c>append</c> or <c>push_back</c> in GDScript.
/// </summary>
/// <param name="item">The item to add.</param>
/// <returns>The new size after adding the item.</returns>
public void Add(T item)
{
_underlyingArray.Add(item);
}
/// <summary>
/// Erases all items from this <see cref="Array{T}"/>.
/// </summary>
public void Clear()
{
_underlyingArray.Clear();
}
/// <summary>
/// Checks if this <see cref="Array{T}"/> contains the given item.
/// </summary>
/// <param name="item">The item to look for.</param>
/// <returns>Whether or not this array contains the given item.</returns>
public bool Contains(T item)
{
return _underlyingArray.Contains(item);
}
/// <summary>
/// Copies the elements of this <see cref="Array{T}"/> to the given
/// C# array, starting at the given index.
/// </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) =>
_underlyingArray.CopyToGeneric(array, arrayIndex, TypeOfElements);
/// <summary>
/// Removes the first occurrence of the specified value
/// from this <see cref="Array{T}"/>.
/// </summary>
/// <param name="item">The value to remove.</param>
/// <returns>A <see langword="bool"/> indicating success or failure.</returns>
public bool Remove(T item)
{
int index = IndexOf(item);
if (index >= 0)
{
RemoveAt(index);
return true;
}
return false;
}
// IEnumerable<T>
/// <summary>
/// Gets an enumerator for this <see cref="Array{T}"/>.
/// </summary>
/// <returns>An enumerator.</returns>
public IEnumerator<T> GetEnumerator()
{
int count = _underlyingArray.Count;
for (int i = 0; i < count; i++)
{
yield return this[i];
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Array{T}"/> to a string.
/// </summary>
/// <returns>A string representation of this array.</returns>
public override string ToString() => _underlyingArray.ToString();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Array<T> from) => Variant.From(from);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Array<T>(Variant from) => from.AsGodotGenericArray<T>();
}
}

View file

@ -206,11 +206,8 @@ namespace Godot.Bridge
// Save instance state
var info = new GodotSerializationInfo(
Collections.Dictionary<StringName, object>.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_dictionary_new_copy(*propertiesState)),
Collections.Dictionary<StringName, Collections.Array>.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_dictionary_new_copy(*signalEventsState)));
using var info = GodotSerializationInfo.CreateCopyingBorrowed(
*propertiesState, *signalEventsState);
godotObject.SaveGodotObjectData(info);
}
@ -236,11 +233,8 @@ namespace Godot.Bridge
// Restore instance state
var info = new GodotSerializationInfo(
Collections.Dictionary<StringName, object>.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_dictionary_new_copy(*propertiesState)),
Collections.Dictionary<StringName, Collections.Array>.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_dictionary_new_copy(*signalEventsState)));
using var info = GodotSerializationInfo.CreateCopyingBorrowed(
*propertiesState, *signalEventsState);
godotObject.RestoreGodotObjectData(info);

View file

@ -1,35 +1,43 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Godot.NativeInterop;
namespace Godot.Bridge;
public class GodotSerializationInfo
public class GodotSerializationInfo : IDisposable
{
private readonly Collections.Dictionary<StringName, object> _properties = new();
private readonly Collections.Dictionary<StringName, Collections.Array> _signalEvents = new();
private readonly Collections.Dictionary _properties;
private readonly Collections.Dictionary _signalEvents;
internal GodotSerializationInfo()
public void Dispose()
{
_properties?.Dispose();
_signalEvents?.Dispose();
GC.SuppressFinalize(this);
}
internal GodotSerializationInfo(
Collections.Dictionary<StringName, object> properties,
Collections.Dictionary<StringName, Collections.Array> signalEvents
)
private GodotSerializationInfo(in godot_dictionary properties, in godot_dictionary signalEvents)
{
_properties = properties;
_signalEvents = signalEvents;
_properties = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(properties);
_signalEvents = Collections.Dictionary.CreateTakingOwnershipOfDisposableValue(signalEvents);
}
public void AddProperty(StringName name, object value)
internal static GodotSerializationInfo CreateCopyingBorrowed(
in godot_dictionary properties, in godot_dictionary signalEvents)
{
return new(NativeFuncs.godotsharp_dictionary_new_copy(properties),
NativeFuncs.godotsharp_dictionary_new_copy(signalEvents));
}
public void AddProperty(StringName name, Variant value)
{
_properties[name] = value;
}
public bool TryGetProperty<T>(StringName name, [MaybeNullWhen(false)] out T value)
public bool TryGetProperty(StringName name, out Variant value)
{
return _properties.TryGetValueAsType(name, out value);
return _properties.TryGetValue(name, out value);
}
public void AddSignalEventDelegate(StringName name, Delegate eventDelegate)
@ -49,9 +57,9 @@ public class GodotSerializationInfo
public bool TryGetSignalEventDelegate<T>(StringName name, [MaybeNullWhen(false)] out T value)
where T : Delegate
{
if (_signalEvents.TryGetValue(name, out Collections.Array serializedData))
if (_signalEvents.TryGetValue(name, out Variant serializedData))
{
if (DelegateUtils.TryDeserializeDelegate(serializedData, out var eventDelegate))
if (DelegateUtils.TryDeserializeDelegate(serializedData.AsGodotArray(), out var eventDelegate))
{
value = eventDelegate as T;

View file

@ -11,10 +11,10 @@ public struct MethodInfo
public MethodFlags Flags { get; init; }
public int Id { get; init; } = 0;
public List<PropertyInfo>? Arguments { get; init; }
public List<object>? DefaultArguments { get; init; }
public List<Variant>? DefaultArguments { get; init; }
public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags, List<PropertyInfo>? arguments,
List<object>? defaultArguments)
public MethodInfo(StringName name, PropertyInfo returnVal, MethodFlags flags,
List<PropertyInfo>? arguments, List<Variant>? defaultArguments)
{
Name = name;
ReturnVal = returnVal;

View file

@ -608,8 +608,8 @@ namespace Godot.Bridge
methodParams.Add(new Collections.Dictionary()
{
{ "name", param.Name },
{ "type", param.Type },
{ "usage", param.Usage }
{ "type", (int)param.Type },
{ "usage", (int)param.Usage }
});
}
}
@ -628,7 +628,7 @@ namespace Godot.Bridge
// RPC functions
Collections.Dictionary<string, Collections.Dictionary> rpcFunctions = new();
Collections.Dictionary rpcFunctions = new();
top = scriptType;
@ -665,7 +665,7 @@ namespace Godot.Bridge
}
*outRpcFunctionsDest = NativeFuncs.godotsharp_dictionary_new_copy(
(godot_dictionary)((Collections.Dictionary)rpcFunctions).NativeValue);
(godot_dictionary)(rpcFunctions).NativeValue);
// Event signals
@ -696,8 +696,8 @@ namespace Godot.Bridge
signalParams.Add(new Collections.Dictionary()
{
{ "name", param.Name },
{ "type", param.Type },
{ "usage", param.Usage }
{ "type", (int)param.Type },
{ "usage", (int)param.Usage }
});
}
}

View file

@ -85,7 +85,7 @@ namespace Godot
/// </summary>
/// <param name="args">Arguments that will be passed to the method call.</param>
/// <returns>The value returned by the method.</returns>
public unsafe object Call(params object[] args)
public unsafe Variant Call(params Variant[] args)
{
using godot_callable callable = Marshaling.ConvertCallableToNative(this);
@ -106,13 +106,13 @@ namespace Godot
{
for (int i = 0; i < argc; i++)
{
varargs[i] = Marshaling.ConvertManagedObjectToVariant(args[i]);
varargs[i] = (godot_variant)args[i].NativeVar;
argsPtr[i] = new IntPtr(&varargs[i]);
}
using godot_variant ret = NativeFuncs.godotsharp_callable_call(callable,
godot_variant ret = NativeFuncs.godotsharp_callable_call(callable,
(godot_variant**)argsPtr, argc, out _);
return Marshaling.ConvertVariantToManagedObject(ret);
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
}
@ -121,7 +121,7 @@ namespace Godot
/// Arguments can be passed and should match the method's signature.
/// </summary>
/// <param name="args">Arguments that will be passed to the method call.</param>
public unsafe void CallDeferred(params object[] args)
public unsafe void CallDeferred(params Variant[] args)
{
using godot_callable callable = Marshaling.ConvertCallableToNative(this);
@ -142,7 +142,7 @@ namespace Godot
{
for (int i = 0; i < argc; i++)
{
varargs[i] = Marshaling.ConvertManagedObjectToVariant(args[i]);
varargs[i] = (godot_variant)args[i].NativeVar;
argsPtr[i] = new IntPtr(&varargs[i]);
}

View file

@ -99,7 +99,7 @@ namespace Godot
if (TrySerializeSingleDelegate(@delegate, out byte[]? buffer))
{
serializedData.Add(buffer);
serializedData.Add((Span<byte>)buffer);
return true;
}
@ -181,8 +181,18 @@ namespace Godot
if (variantType == Variant.Type.Nil)
return false;
static byte[] Var2Bytes(in godot_variant var)
{
NativeFuncs.godotsharp_var2bytes(var, false.ToGodotBool(), out var varBytes);
using (varBytes)
return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
}
writer.Write(field.Name);
byte[] valueBuffer = GD.Var2Bytes(field.GetValue(target));
var fieldValue = field.GetValue(target);
using var fieldValueVariant = Marshaling.ConvertManagedObjectToVariant(fieldValue);
byte[] valueBuffer = Var2Bytes(fieldValueVariant);
writer.Write(valueBuffer.Length);
writer.Write(valueBuffer);
}
@ -320,9 +330,14 @@ namespace Godot
internal static bool TryDeserializeDelegate(Collections.Array serializedData,
[MaybeNullWhen(false)] out Delegate @delegate)
{
@delegate = null;
if (serializedData.Count == 1)
{
object elem = serializedData[0];
var elem = serializedData[0].Obj;
if (elem == null)
return false;
if (elem is Collections.Array multiCastData)
return TryDeserializeDelegate(multiCastData, out @delegate);
@ -330,12 +345,15 @@ namespace Godot
return TryDeserializeSingleDelegate((byte[])elem, out @delegate);
}
@delegate = null;
var delegates = new List<Delegate>(serializedData.Count);
foreach (object elem in serializedData)
foreach (Variant variantElem in serializedData)
{
var elem = variantElem.Obj;
if (elem == null)
continue;
if (elem is Collections.Array multiCastData)
{
if (TryDeserializeDelegate(multiCastData, out Delegate? oneDelegate))

View file

@ -2,10 +2,6 @@ using System;
using System.Collections.Generic;
using System.Collections;
using Godot.NativeInterop;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Godot.Collections
{
@ -15,9 +11,8 @@ namespace Godot.Collections
/// interfacing with the engine.
/// </summary>
public sealed class Dictionary :
IDictionary<object, object>,
IDictionary,
IReadOnlyDictionary<object, object>,
IDictionary<Variant, Variant>,
IReadOnlyDictionary<Variant, Variant>,
IDisposable
{
internal godot_dictionary.movable NativeValue;
@ -33,20 +28,6 @@ namespace Godot.Collections
_weakReferenceToSelf = DisposablesTracker.RegisterDisposable(this);
}
/// <summary>
/// Constructs a new <see cref="Dictionary"/> from the given dictionary's elements.
/// </summary>
/// <param name="dictionary">The dictionary to construct from.</param>
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary dictionary) : this()
{
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
foreach (DictionaryEntry entry in dictionary)
Add(entry.Key, entry.Value);
}
private Dictionary(godot_dictionary nativeValueToOwn)
{
NativeValue = (godot_dictionary.movable)(nativeValueToOwn.IsAllocated ?
@ -102,7 +83,7 @@ namespace Godot.Collections
/// <summary>
/// Gets the collection of keys in this <see cref="Dictionary"/>.
/// </summary>
public ICollection<object> Keys
public ICollection<Variant> Keys
{
get
{
@ -116,7 +97,7 @@ namespace Godot.Collections
/// <summary>
/// Gets the collection of elements in this <see cref="Dictionary"/>.
/// </summary>
public ICollection<object> Values
public ICollection<Variant> Values
{
get
{
@ -127,13 +108,9 @@ namespace Godot.Collections
}
}
IEnumerable<object> IReadOnlyDictionary<object, object>.Keys => Keys;
IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Keys => Keys;
IEnumerable<object> IReadOnlyDictionary<object, object>.Values => Values;
ICollection IDictionary.Keys => Keys.ToList();
ICollection IDictionary.Values => Values.ToList();
IEnumerable<Variant> IReadOnlyDictionary<Variant, Variant>.Values => Values;
private (Array keys, Array values, int count) GetKeyValuePairs()
{
@ -152,25 +129,20 @@ namespace Godot.Collections
return (keys, values, count);
}
bool IDictionary.IsFixedSize => false;
bool IDictionary.IsReadOnly => false;
/// <summary>
/// Returns the object at the given <paramref name="key"/>.
/// Returns the value at the given <paramref name="key"/>.
/// </summary>
/// <value>The object at the given <paramref name="key"/>.</value>
public object this[object key]
/// <value>The value at the given <paramref name="key"/>.</value>
public Variant this[Variant key]
{
get
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var self = (godot_dictionary)NativeValue;
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self, variantKey,
out godot_variant value).ToBool())
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
(godot_variant)key.NativeVar, out godot_variant value).ToBool())
{
using (value)
return Marshaling.ConvertVariantToManagedObject(value);
return Variant.CreateTakingOwnershipOfDisposableValue(value);
}
else
{
@ -179,33 +151,31 @@ namespace Godot.Collections
}
set
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value);
var self = (godot_dictionary)NativeValue;
NativeFuncs.godotsharp_dictionary_set_value(ref self, variantKey, variantValue);
NativeFuncs.godotsharp_dictionary_set_value(ref self,
(godot_variant)key.NativeVar, (godot_variant)value.NativeVar);
}
}
/// <summary>
/// Adds an object <paramref name="value"/> at key <paramref name="key"/>
/// Adds an value <paramref name="value"/> at key <paramref name="key"/>
/// to this <see cref="Dictionary"/>.
/// </summary>
/// <param name="key">The key at which to add the object.</param>
/// <param name="value">The object to add.</param>
public void Add(object key, object value)
/// <param name="key">The key at which to add the value.</param>
/// <param name="value">The value to add.</param>
public void Add(Variant key, Variant value)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var variantKey = (godot_variant)key.NativeVar;
var self = (godot_dictionary)NativeValue;
if (NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool())
throw new ArgumentException("An element with the same key already exists", nameof(key));
using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(value);
godot_variant variantValue = (godot_variant)value.NativeVar;
NativeFuncs.godotsharp_dictionary_add(ref self, variantKey, variantValue);
}
void ICollection<KeyValuePair<object, object>>.Add(KeyValuePair<object, object> item)
void ICollection<KeyValuePair<Variant, Variant>>.Add(KeyValuePair<Variant, Variant> item)
=> Add(item.Key, item.Value);
/// <summary>
@ -222,16 +192,15 @@ namespace Godot.Collections
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
public bool ContainsKey(object key)
public bool ContainsKey(Variant key)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var self = (godot_dictionary)NativeValue;
return NativeFuncs.godotsharp_dictionary_contains_key(ref self, variantKey).ToBool();
return NativeFuncs.godotsharp_dictionary_contains_key(ref self, (godot_variant)key.NativeVar).ToBool();
}
public bool Contains(KeyValuePair<object, object> item)
public bool Contains(KeyValuePair<Variant, Variant> item)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key);
godot_variant variantKey = (godot_variant)item.Key.NativeVar;
var self = (godot_dictionary)NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
@ -241,30 +210,24 @@ namespace Godot.Collections
if (!found)
return false;
using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value);
godot_variant variantValue = (godot_variant)item.Value.NativeVar;
return NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool();
}
}
bool IDictionary.Contains(object key)
{
throw new NotImplementedException();
}
/// <summary>
/// Removes an element from this <see cref="Dictionary"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
public bool Remove(object key)
public bool Remove(Variant key)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var self = (godot_dictionary)NativeValue;
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, (godot_variant)key.NativeVar).ToBool();
}
public bool Remove(KeyValuePair<object, object> item)
public bool Remove(KeyValuePair<Variant, Variant> item)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(item.Key);
godot_variant variantKey = (godot_variant)item.Key.NativeVar;
var self = (godot_dictionary)NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
@ -274,7 +237,7 @@ namespace Godot.Collections
if (!found)
return false;
using godot_variant variantValue = Marshaling.ConvertManagedObjectToVariant(item.Value);
godot_variant variantValue = (godot_variant)item.Value.NativeVar;
if (NativeFuncs.godotsharp_variant_equals(variantValue, retValue).ToBool())
{
return NativeFuncs.godotsharp_dictionary_remove_key(
@ -285,17 +248,6 @@ namespace Godot.Collections
}
}
void IDictionary.Remove(object key)
{
_ = Remove(key);
}
// ICollection
object ICollection.SyncRoot => this;
bool ICollection.IsSynchronized => false;
/// <summary>
/// Returns the number of elements in this <see cref="Dictionary"/>.
/// This is also known as the size or length of the dictionary.
@ -310,19 +262,15 @@ namespace Godot.Collections
}
}
public bool IsReadOnly => false;
bool ICollection<KeyValuePair<Variant, Variant>>.IsReadOnly => false;
public bool TryGetValue(object key, out object value)
public bool TryGetValue(Variant key, out Variant value)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var self = (godot_dictionary)NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
(godot_variant)key.NativeVar, out godot_variant retValue).ToBool();
using (retValue)
{
value = found ? Marshaling.ConvertVariantToManagedObject(retValue) : default;
}
value = found ? Variant.CreateTakingOwnershipOfDisposableValue(retValue) : default;
return found;
}
@ -333,7 +281,7 @@ namespace Godot.Collections
/// </summary>
/// <param name="array">The array to copy to.</param>
/// <param name="arrayIndex">The index to start at.</param>
public void CopyTo(KeyValuePair<object, object>[] array, int arrayIndex)
public void CopyTo(KeyValuePair<Variant, Variant>[] array, int arrayIndex)
{
if (array == null)
throw new ArgumentNullException(nameof(array), "Value cannot be null.");
@ -355,35 +303,13 @@ namespace Godot.Collections
}
}
void ICollection.CopyTo(System.Array 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.");
var (keys, values, count) = GetKeyValuePairs();
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.SetValue(new DictionaryEntry(keys[i], values[i]), arrayIndex);
arrayIndex++;
}
}
// IEnumerable
/// <summary>
/// Gets an enumerator for this <see cref="Dictionary"/>.
/// </summary>
/// <returns>An enumerator.</returns>
public IEnumerator<KeyValuePair<object, object>> GetEnumerator()
public IEnumerator<KeyValuePair<Variant, Variant>> GetEnumerator()
{
for (int i = 0; i < Count; i++)
{
@ -393,84 +319,14 @@ namespace Godot.Collections
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
IDictionaryEnumerator IDictionary.GetEnumerator() => new DictionaryEnumerator(this);
private class DictionaryEnumerator : IDictionaryEnumerator
{
private readonly Dictionary _dictionary;
private readonly int _count;
private int _index = -1;
private bool _dirty = true;
private DictionaryEntry _entry;
public DictionaryEnumerator(Dictionary dictionary)
{
_dictionary = dictionary;
_count = dictionary.Count;
}
public object Current => Entry;
public DictionaryEntry Entry
{
get
{
if (_dirty)
{
UpdateEntry();
}
return _entry;
}
}
private void UpdateEntry()
{
_dirty = false;
var self = (godot_dictionary)_dictionary.NativeValue;
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, _index,
out godot_variant key,
out godot_variant value);
using (key)
using (value)
{
// FIXME: DictionaryEntry keys cannot be null, but Godot dictionaries allow null keys
_entry = new DictionaryEntry(Marshaling.ConvertVariantToManagedObject(key)!,
Marshaling.ConvertVariantToManagedObject(value));
}
}
public object Key => Entry.Key;
public object Value => Entry.Value;
public bool MoveNext()
{
_index++;
_dirty = true;
return _index < _count;
}
public void Reset()
{
_index = -1;
_dirty = true;
}
}
private KeyValuePair<object, object> GetKeyValuePair(int index)
private KeyValuePair<Variant, Variant> GetKeyValuePair(int index)
{
var self = (godot_dictionary)NativeValue;
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
out godot_variant key,
out godot_variant value);
using (key)
using (value)
{
return new KeyValuePair<object, object>(Marshaling.ConvertVariantToManagedObject(key),
Marshaling.ConvertVariantToManagedObject(value));
}
return new KeyValuePair<Variant, Variant>(Variant.CreateTakingOwnershipOfDisposableValue(key),
Variant.CreateTakingOwnershipOfDisposableValue(value));
}
/// <summary>
@ -485,332 +341,4 @@ namespace Godot.Collections
return Marshaling.ConvertStringToManaged(str);
}
}
internal interface IGenericGodotDictionary
{
Dictionary UnderlyingDictionary { get; }
Type TypeOfKeys { get; }
Type TypeOfValues { get; }
}
// TODO: Now we should be able to avoid boxing
/// <summary>
/// Typed wrapper around Godot's Dictionary class, a dictionary of Variant
/// typed elements allocated in the engine in C++. Useful when
/// interfacing with the engine. Otherwise prefer .NET collections
/// such as <see cref="System.Collections.Generic.Dictionary{TKey, TValue}"/>.
/// </summary>
/// <typeparam name="TKey">The type of the dictionary's keys.</typeparam>
/// <typeparam name="TValue">The type of the dictionary's values.</typeparam>
public class Dictionary<TKey, TValue> :
IDictionary<TKey, TValue>, IGenericGodotDictionary
{
private readonly Dictionary _underlyingDict;
internal ref godot_dictionary.movable NativeValue
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref _underlyingDict.NativeValue;
}
// ReSharper disable StaticMemberInGenericType
// Warning is about unique static fields being created for each generic type combination:
// https://www.jetbrains.com/help/resharper/StaticMemberInGenericType.html
// In our case this is exactly what we want.
private static readonly Type TypeOfKeys = typeof(TKey);
private static readonly Type TypeOfValues = typeof(TValue);
// ReSharper restore StaticMemberInGenericType
Dictionary IGenericGodotDictionary.UnderlyingDictionary => _underlyingDict;
Type IGenericGodotDictionary.TypeOfKeys => TypeOfKeys;
Type IGenericGodotDictionary.TypeOfValues => TypeOfValues;
/// <summary>
/// Constructs a new empty <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
public Dictionary()
{
_underlyingDict = new Dictionary();
}
/// <summary>
/// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements.
/// </summary>
/// <param name="dictionary">The dictionary to construct from.</param>
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(IDictionary<TKey, TValue> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException(nameof(dictionary));
_underlyingDict = new Dictionary();
foreach (KeyValuePair<TKey, TValue> entry in dictionary)
Add(entry.Key, entry.Value);
}
/// <summary>
/// Constructs a new <see cref="Dictionary{TKey, TValue}"/> from the given dictionary's elements.
/// </summary>
/// <param name="dictionary">The dictionary to construct from.</param>
/// <returns>A new Godot Dictionary.</returns>
public Dictionary(Dictionary dictionary)
{
_underlyingDict = dictionary;
}
// Explicit name to make it very clear
internal static Dictionary<TKey, TValue> CreateTakingOwnershipOfDisposableValue(
godot_dictionary nativeValueToOwn)
=> new Dictionary<TKey, TValue>(Dictionary.CreateTakingOwnershipOfDisposableValue(nativeValueToOwn));
/// <summary>
/// Converts this typed <see cref="Dictionary{TKey, TValue}"/> to an untyped <see cref="Dictionary"/>.
/// </summary>
/// <param name="from">The typed dictionary to convert.</param>
public static explicit operator Dictionary(Dictionary<TKey, TValue> from)
{
return from?._underlyingDict;
}
/// <summary>
/// Duplicates this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
/// <param name="deep">If <see langword="true"/>, performs a deep copy.</param>
/// <returns>A new Godot Dictionary.</returns>
public Dictionary<TKey, TValue> Duplicate(bool deep = false)
{
return new Dictionary<TKey, TValue>(_underlyingDict.Duplicate(deep));
}
// IDictionary<TKey, TValue>
/// <summary>
/// Returns the value at the given <paramref name="key"/>.
/// </summary>
/// <value>The value at the given <paramref name="key"/>.</value>
public TValue this[TKey key]
{
get
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
if (NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant value).ToBool())
{
using (value)
return (TValue)Marshaling.ConvertVariantToManagedObjectOfType(value, TypeOfValues);
}
else
{
throw new KeyNotFoundException();
}
}
set => _underlyingDict[key] = value;
}
/// <summary>
/// Gets the collection of keys in this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
public ICollection<TKey> Keys
{
get
{
godot_array keyArray;
var self = (godot_dictionary)_underlyingDict.NativeValue;
NativeFuncs.godotsharp_dictionary_keys(ref self, out keyArray);
return Array<TKey>.CreateTakingOwnershipOfDisposableValue(keyArray);
}
}
/// <summary>
/// Gets the collection of elements in this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
public ICollection<TValue> Values
{
get
{
godot_array valuesArray;
var self = (godot_dictionary)_underlyingDict.NativeValue;
NativeFuncs.godotsharp_dictionary_values(ref self, out valuesArray);
return Array<TValue>.CreateTakingOwnershipOfDisposableValue(valuesArray);
}
}
private KeyValuePair<TKey, TValue> GetKeyValuePair(int index)
{
var self = (godot_dictionary)_underlyingDict.NativeValue;
NativeFuncs.godotsharp_dictionary_key_value_pair_at(ref self, index,
out godot_variant key,
out godot_variant value);
using (key)
using (value)
{
return new KeyValuePair<TKey, TValue>(
(TKey)Marshaling.ConvertVariantToManagedObjectOfType(key, TypeOfKeys),
(TValue)Marshaling.ConvertVariantToManagedObjectOfType(value, TypeOfValues));
}
}
/// <summary>
/// Adds an object <paramref name="value"/> at key <paramref name="key"/>
/// to this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
/// <param name="key">The key at which to add the object.</param>
/// <param name="value">The object to add.</param>
public void Add(TKey key, TValue value)
{
_underlyingDict.Add(key, value);
}
/// <summary>
/// Checks if this <see cref="Dictionary{TKey, TValue}"/> contains the given key.
/// </summary>
/// <param name="key">The key to look for.</param>
/// <returns>Whether or not this dictionary contains the given key.</returns>
public bool ContainsKey(TKey key)
{
return _underlyingDict.ContainsKey(key);
}
/// <summary>
/// Removes an element from this <see cref="Dictionary{TKey, TValue}"/> by key.
/// </summary>
/// <param name="key">The key of the element to remove.</param>
public bool Remove(TKey key)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
return NativeFuncs.godotsharp_dictionary_remove_key(ref self, variantKey).ToBool();
}
/// <summary>
/// Gets the object at the given <paramref name="key"/>.
/// </summary>
/// <param name="key">The key of the element to get.</param>
/// <param name="value">The value at the given <paramref name="key"/>.</param>
/// <returns>If an object was found for the given <paramref name="key"/>.</returns>
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
using (retValue)
{
value = found ?
(TValue)Marshaling.ConvertVariantToManagedObjectOfType(retValue, TypeOfValues) :
default;
}
return found;
}
// TODO: This is temporary. It's needed for the serialization generator. It won't be needed once we replace System.Object with a Variant type.
internal bool TryGetValueAsType<TValueCustom>(TKey key, [MaybeNullWhen(false)] out TValueCustom value)
{
using godot_variant variantKey = Marshaling.ConvertManagedObjectToVariant(key);
var self = (godot_dictionary)_underlyingDict.NativeValue;
bool found = NativeFuncs.godotsharp_dictionary_try_get_value(ref self,
variantKey, out godot_variant retValue).ToBool();
using (retValue)
{
value = found ?
(TValueCustom)Marshaling.ConvertVariantToManagedObjectOfType(retValue, typeof(TValueCustom)) :
default;
}
return found;
}
// ICollection<KeyValuePair<TKey, TValue>>
/// <summary>
/// Returns the number of elements in this <see cref="Dictionary{TKey, TValue}"/>.
/// This is also known as the size or length of the dictionary.
/// </summary>
/// <returns>The number of elements.</returns>
public int Count => _underlyingDict.Count;
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
_underlyingDict.Add(item.Key, item.Value);
}
/// <summary>
/// Erases all the items from this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
public void Clear()
{
_underlyingDict.Clear();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
=> _underlyingDict.Contains(new(item.Key, item.Value));
/// <summary>
/// Copies the elements of this <see cref="Dictionary{TKey, TValue}"/> to the given
/// untyped C# array, starting at the given index.
/// </summary>
/// <param name="array">The array to copy to.</param>
/// <param name="arrayIndex">The index to start at.</param>
public void CopyTo(KeyValuePair<TKey, TValue>[] 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 = 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] = GetKeyValuePair(i);
arrayIndex++;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
=> _underlyingDict.Remove(new(item.Key, item.Value));
// IEnumerable<KeyValuePair<TKey, TValue>>
/// <summary>
/// Gets an enumerator for this <see cref="Dictionary{TKey, TValue}"/>.
/// </summary>
/// <returns>An enumerator.</returns>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
for (int i = 0; i < Count; i++)
{
yield return GetKeyValuePair(i);
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
/// <summary>
/// Converts this <see cref="Dictionary{TKey, TValue}"/> to a string.
/// </summary>
/// <returns>A string representation of this dictionary.</returns>
public override string ToString() => _underlyingDict.ToString();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Dictionary<TKey, TValue> from) => Variant.From(from);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Dictionary<TKey, TValue>(Variant from) => from.AsGodotGenericDictionary<TKey, TValue>();
}
}

View file

@ -1,66 +0,0 @@
using System.Reflection;
using Godot.Collections;
using Godot.NativeInterop;
namespace Godot
{
public partial class SceneTree
{
/// <summary>
/// Returns a list of all nodes assigned to the given <paramref name="group"/>.
/// </summary>
/// <typeparam name="T">The type to cast to. Should be a descendant of <see cref="Node"/>.</typeparam>
public Array<T> GetNodesInGroup<T>(StringName group) where T : class
{
var array = GetNodesInGroup(group);
if (array.Count == 0)
return new Array<T>(array);
var typeOfT = typeof(T);
bool nativeBase = InternalIsClassNativeBase(typeOfT);
if (nativeBase)
{
// Native type
var field = typeOfT.GetField("NativeName",
BindingFlags.DeclaredOnly | BindingFlags.Static |
BindingFlags.Public | BindingFlags.NonPublic);
var nativeName = (StringName)field!.GetValue(null);
var nativeNameSelf = (godot_string_name)nativeName!.NativeValue;
var inputSelf = (godot_array)array.NativeValue;
NativeFuncs.godotsharp_array_filter_godot_objects_by_native(nativeNameSelf, inputSelf,
out godot_array filteredArray);
return Array<T>.CreateTakingOwnershipOfDisposableValue(filteredArray);
}
else
{
// Custom derived type
var inputSelf = (godot_array)array.NativeValue;
NativeFuncs.godotsharp_array_filter_godot_objects_by_non_native(inputSelf,
out godot_array filteredArray);
var filteredArrayWrapped = Array.CreateTakingOwnershipOfDisposableValue(filteredArray);
// Re-use first array as its size is the same or greater than the filtered one
var resWrapped = new Array<T>(array);
int j = 0;
for (int i = 0; i < filteredArrayWrapped.Count; i++)
{
if (filteredArrayWrapped[i] is T t)
{
resWrapped[j] = t;
j++;
}
}
// Remove trailing elements, since this was re-used
resWrapped.Resize(j);
return resWrapped;
}
}
}
}

View file

@ -21,12 +21,11 @@ namespace Godot
/// <param name="bytes">Byte array that will be decoded to a <c>Variant</c>.</param>
/// <param name="allowObjects">If objects should be decoded.</param>
/// <returns>The decoded <c>Variant</c>.</returns>
public static object Bytes2Var(byte[] bytes, bool allowObjects = false)
public static Variant Bytes2Var(Span<byte> bytes, bool allowObjects = false)
{
using var varBytes = Marshaling.ConvertSystemArrayToNativePackedByteArray(bytes);
NativeFuncs.godotsharp_bytes2var(varBytes, allowObjects.ToGodotBool(), out godot_variant ret);
using (ret)
return Marshaling.ConvertVariantToManagedObject(ret);
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
@ -44,12 +43,10 @@ namespace Godot
/// </code>
/// </example>
/// <returns>The <c>Variant</c> converted to the given <paramref name="type"/>.</returns>
public static object Convert(object what, Variant.Type type)
public static Variant Convert(Variant what, Variant.Type type)
{
using var whatVariant = Marshaling.ConvertManagedObjectToVariant(what);
NativeFuncs.godotsharp_convert(whatVariant, (int)type, out godot_variant ret);
using (ret)
return Marshaling.ConvertVariantToManagedObject(ret);
NativeFuncs.godotsharp_convert((godot_variant)what.NativeVar, (int)type, out godot_variant ret);
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
@ -83,10 +80,9 @@ namespace Godot
/// </example>
/// <param name="var">Variable that will be hashed.</param>
/// <returns>Hash of the variable passed.</returns>
public static int Hash(object var)
public static int Hash(Variant var)
{
using var variant = Marshaling.ConvertManagedObjectToVariant(var);
return NativeFuncs.godotsharp_hash(variant);
return NativeFuncs.godotsharp_hash((godot_variant)var.NativeVar);
}
/// <summary>
@ -515,16 +511,16 @@ namespace Godot
/// </summary>
/// <param name="what">Arguments that will converted to string.</param>
/// <returns>The string formed by the given arguments.</returns>
public static string Str(params object[] what)
public static string Str(params Variant[] what)
{
using var whatGodotArray = Marshaling.ConvertSystemArrayToNativeGodotArray(what);
NativeFuncs.godotsharp_str(whatGodotArray, out godot_string ret);
using var whatGodot = new Godot.Collections.Array(what);
NativeFuncs.godotsharp_str((godot_array)whatGodot.NativeValue, out godot_string ret);
using (ret)
return Marshaling.ConvertStringToManaged(ret);
}
/// <summary>
/// Converts a formatted string that was returned by <see cref="Var2Str(object)"/> to the original value.
/// Converts a formatted string that was returned by <see cref="Var2Str(Variant)"/> to the original value.
/// </summary>
/// <example>
/// <code>
@ -535,27 +531,25 @@ namespace Godot
/// </example>
/// <param name="str">String that will be converted to Variant.</param>
/// <returns>The decoded <c>Variant</c>.</returns>
public static object Str2Var(string str)
public static Variant Str2Var(string str)
{
using var godotStr = Marshaling.ConvertStringToNative(str);
NativeFuncs.godotsharp_str2var(godotStr, out godot_variant ret);
using (ret)
return Marshaling.ConvertVariantToManagedObject(ret);
return Variant.CreateTakingOwnershipOfDisposableValue(ret);
}
/// <summary>
/// Encodes a <c>Variant</c> value to a byte array.
/// If <paramref name="fullObjects"/> is <see langword="true"/> encoding objects is allowed
/// (and can potentially include code).
/// Deserialization can be done with <see cref="Bytes2Var(byte[], bool)"/>.
/// Deserialization can be done with <see cref="Bytes2Var(Span{byte}, bool)"/>.
/// </summary>
/// <param name="var">Variant that will be encoded.</param>
/// <param name="fullObjects">If objects should be serialized.</param>
/// <returns>The <c>Variant</c> encoded as an array of bytes.</returns>
public static byte[] Var2Bytes(object var, bool fullObjects = false)
public static byte[] Var2Bytes(Variant var, bool fullObjects = false)
{
using var variant = Marshaling.ConvertManagedObjectToVariant(var);
NativeFuncs.godotsharp_var2bytes(variant, fullObjects.ToGodotBool(), out var varBytes);
NativeFuncs.godotsharp_var2bytes((godot_variant)var.NativeVar, fullObjects.ToGodotBool(), out var varBytes);
using (varBytes)
return Marshaling.ConvertNativePackedByteArrayToSystemArray(varBytes);
}
@ -577,10 +571,9 @@ namespace Godot
/// </example>
/// <param name="var">Variant that will be converted to string.</param>
/// <returns>The <c>Variant</c> encoded as a string.</returns>
public static string Var2Str(object var)
public static string Var2Str(Variant var)
{
using var variant = Marshaling.ConvertManagedObjectToVariant(var);
NativeFuncs.godotsharp_var2str(variant, out godot_string ret);
NativeFuncs.godotsharp_var2str((godot_variant)var.NativeVar, out godot_string ret);
using (ret)
return Marshaling.ConvertStringToManaged(ret);
}

View file

@ -1,7 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming
@ -11,8 +8,6 @@ using System.Runtime.InteropServices;
#nullable enable
// TODO: Consider removing support for IEnumerable
namespace Godot.NativeInterop
{
public static class Marshaling
@ -113,10 +108,10 @@ namespace Godot.NativeInterop
if (type == typeof(byte[]))
return Variant.Type.PackedByteArray;
if (type == typeof(Int32[]))
if (type == typeof(int[]))
return Variant.Type.PackedInt32Array;
if (type == typeof(Int64[]))
if (type == typeof(long[]))
return Variant.Type.PackedInt64Array;
if (type == typeof(float[]))
@ -151,27 +146,6 @@ namespace Godot.NativeInterop
}
else if (type.IsGenericType)
{
var genericTypeDefinition = type.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(Collections.Dictionary<,>))
return Variant.Type.Dictionary;
if (genericTypeDefinition == typeof(Collections.Array<>))
return Variant.Type.Array;
if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>))
return Variant.Type.Dictionary;
if (genericTypeDefinition == typeof(System.Collections.Generic.List<>))
return Variant.Type.Array;
if (genericTypeDefinition == typeof(IDictionary<,>))
return Variant.Type.Dictionary;
if (genericTypeDefinition == typeof(ICollection<>) ||
genericTypeDefinition == typeof(IEnumerable<>))
return Variant.Type.Array;
if (typeof(Godot.Object).IsAssignableFrom(type))
return Variant.Type.Object;
}
@ -194,15 +168,11 @@ namespace Godot.NativeInterop
if (typeof(RID) == type)
return Variant.Type.Rid;
if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type)
if (typeof(Collections.Dictionary) == type)
return Variant.Type.Dictionary;
if (typeof(Collections.Array) == type ||
typeof(System.Collections.ICollection) == type ||
typeof(System.Collections.IEnumerable) == type)
{
if (typeof(Collections.Array) == type)
return Variant.Type.Array;
}
}
break;
@ -214,7 +184,6 @@ namespace Godot.NativeInterop
}
/* 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)
@ -228,19 +197,19 @@ namespace Godot.NativeInterop
return VariantUtils.CreateFromInt(@char);
case sbyte @int8:
return VariantUtils.CreateFromInt(@int8);
case Int16 @int16:
case short @int16:
return VariantUtils.CreateFromInt(@int16);
case Int32 @int32:
case int @int32:
return VariantUtils.CreateFromInt(@int32);
case Int64 @int64:
case long @int64:
return VariantUtils.CreateFromInt(@int64);
case byte @uint8:
return VariantUtils.CreateFromInt(@uint8);
case UInt16 @uint16:
case ushort @uint16:
return VariantUtils.CreateFromInt(@uint16);
case UInt32 @uint32:
case uint @uint32:
return VariantUtils.CreateFromInt(@uint32);
case UInt64 @uint64:
case ulong @uint64:
return VariantUtils.CreateFromInt(@uint64);
case float @float:
return VariantUtils.CreateFromFloat(@float);
@ -288,9 +257,9 @@ namespace Godot.NativeInterop
return VariantUtils.CreateFromString(@string);
case byte[] byteArray:
return VariantUtils.CreateFromPackedByteArray(byteArray);
case Int32[] int32Array:
case int[] int32Array:
return VariantUtils.CreateFromPackedInt32Array(int32Array);
case Int64[] int64Array:
case long[] int64Array:
return VariantUtils.CreateFromPackedInt64Array(int64Array);
case float[] floatArray:
return VariantUtils.CreateFromPackedFloat32Array(floatArray);
@ -305,11 +274,11 @@ namespace Godot.NativeInterop
case Color[] colorArray:
return VariantUtils.CreateFromPackedColorArray(colorArray);
case StringName[] stringNameArray:
return VariantUtils.CreateFromSystemArrayOfSupportedType(stringNameArray);
return VariantUtils.CreateFromSystemArrayOfStringName(stringNameArray);
case NodePath[] nodePathArray:
return VariantUtils.CreateFromSystemArrayOfSupportedType(nodePathArray);
return VariantUtils.CreateFromSystemArrayOfNodePath(nodePathArray);
case RID[] ridArray:
return VariantUtils.CreateFromSystemArrayOfSupportedType(ridArray);
return VariantUtils.CreateFromSystemArrayOfRID(ridArray);
case Godot.Object[] godotObjectArray:
return VariantUtils.CreateFromSystemArrayOfGodotObject(godotObjectArray);
case Godot.Object godotObject:
@ -326,49 +295,6 @@ namespace Godot.NativeInterop
return VariantUtils.CreateFromArray(godotArray);
case Variant variant:
return NativeFuncs.godotsharp_variant_new_copy((godot_variant)variant.NativeVar);
case Collections.IGenericGodotDictionary genericGodotDictionary:
{
var godotDict = genericGodotDictionary.UnderlyingDictionary;
if (godotDict == null)
return new godot_variant();
return VariantUtils.CreateFromDictionary(godotDict);
}
case Collections.IGenericGodotArray genericGodotArray:
{
var godotArray = genericGodotArray.UnderlyingArray;
if (godotArray == null)
return new godot_variant();
return VariantUtils.CreateFromArray(godotArray);
}
default:
{
var type = p_obj.GetType();
if (type.IsGenericType)
{
var genericTypeDefinition = type.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>))
{
// TODO: Validate key and value types are compatible with Variant
var godotDict = new Collections.Dictionary();
foreach (KeyValuePair<object, object> entry in (IDictionary)p_obj)
godotDict.Add(entry.Key, entry.Value);
return VariantUtils.CreateFromDictionary(godotDict);
}
if (genericTypeDefinition == typeof(System.Collections.Generic.List<>))
{
// TODO: Validate element type is compatible with Variant
using var nativeGodotArray = ConvertICollectionToNativeGodotArray((ICollection)p_obj);
return VariantUtils.CreateFromArray(nativeGodotArray);
}
}
break;
}
}
GD.PushError("Attempted to convert an unmarshallable managed type to Variant. Name: '" +
@ -495,11 +421,31 @@ namespace Godot.NativeInterop
}
if (type.IsArray || type.IsSZArray)
{
return ConvertVariantToSystemArrayOfType(p_var, type);
}
else if (type.IsGenericType)
return ConvertVariantToManagedObjectOfGenericType(p_var, type);
{
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;
@ -518,10 +464,10 @@ namespace Godot.NativeInterop
if (type == typeof(byte[]))
return VariantUtils.ConvertAsPackedByteArrayToSystemArray(p_var);
if (type == typeof(Int32[]))
if (type == typeof(int[]))
return VariantUtils.ConvertAsPackedInt32ArrayToSystemArray(p_var);
if (type == typeof(Int64[]))
if (type == typeof(long[]))
return VariantUtils.ConvertAsPackedInt64ArrayToSystemArray(p_var);
if (type == typeof(float[]))
@ -543,13 +489,13 @@ namespace Godot.NativeInterop
return VariantUtils.ConvertAsPackedColorArrayToSystemArray(p_var);
if (type == typeof(StringName[]))
return VariantUtils.ConvertToSystemArrayOfSupportedType<StringName>(p_var);
return VariantUtils.ConvertToSystemArrayOfStringName(p_var);
if (type == typeof(NodePath[]))
return VariantUtils.ConvertToSystemArrayOfSupportedType<NodePath>(p_var);
return VariantUtils.ConvertToSystemArrayOfNodePath(p_var);
if (type == typeof(RID[]))
return VariantUtils.ConvertToSystemArrayOfSupportedType<RID>(p_var);
return VariantUtils.ConvertToSystemArrayOfRID(p_var);
if (typeof(Godot.Object[]).IsAssignableFrom(type))
return VariantUtils.ConvertToSystemArrayOfGodotObject(p_var, type);
@ -618,15 +564,13 @@ namespace Godot.NativeInterop
return true;
}
if (typeof(Collections.Dictionary) == type || typeof(System.Collections.IDictionary) == type)
if (typeof(Collections.Dictionary) == type)
{
res = VariantUtils.ConvertToDictionaryObject(p_var);
return true;
}
if (typeof(Collections.Array) == type ||
typeof(System.Collections.ICollection) == type ||
typeof(System.Collections.IEnumerable) == type)
if (typeof(Collections.Array) == type)
{
res = VariantUtils.ConvertToArrayObject(p_var);
return true;
@ -636,103 +580,6 @@ namespace Godot.NativeInterop
return false;
}
private static object? ConvertVariantToManagedObjectOfGenericType(in godot_variant p_var, Type type)
{
static object ConvertVariantToGenericGodotCollectionsDictionary(in godot_variant p_var, Type fullType)
{
var underlyingDict = VariantUtils.ConvertToDictionaryObject(p_var);
return Activator.CreateInstance(fullType,
BindingFlags.Public | BindingFlags.Instance, null,
args: new object[] { underlyingDict }, null)!;
}
static object ConvertVariantToGenericGodotCollectionsArray(in godot_variant p_var, Type fullType)
{
var underlyingArray = VariantUtils.ConvertToArrayObject(p_var);
return Activator.CreateInstance(fullType,
BindingFlags.Public | BindingFlags.Instance, null,
args: new object[] { underlyingArray }, null)!;
}
var genericTypeDefinition = type.GetGenericTypeDefinition();
if (genericTypeDefinition == typeof(Collections.Dictionary<,>))
return ConvertVariantToGenericGodotCollectionsDictionary(p_var, type);
if (genericTypeDefinition == typeof(Collections.Array<>))
return ConvertVariantToGenericGodotCollectionsArray(p_var, type);
if (genericTypeDefinition == typeof(System.Collections.Generic.Dictionary<,>))
{
using var godotDictionary = VariantUtils.ConvertToDictionaryObject(p_var);
var dictionary = (System.Collections.IDictionary)Activator.CreateInstance(type,
BindingFlags.Public | BindingFlags.Instance, null,
args: new object[]
{
/* capacity: */ godotDictionary.Count
}, null)!;
foreach (KeyValuePair<object, object> pair in godotDictionary)
dictionary.Add(pair.Key, pair.Value);
return dictionary;
}
if (genericTypeDefinition == typeof(System.Collections.Generic.List<>))
{
using var godotArray = VariantUtils.ConvertToArrayObject(p_var);
var list = (System.Collections.IList)Activator.CreateInstance(type,
BindingFlags.Public | BindingFlags.Instance, null,
args: new object[]
{
/* capacity: */ godotArray.Count
}, null)!;
foreach (object elem in godotArray)
list.Add(elem);
return list;
}
if (genericTypeDefinition == typeof(IDictionary<,>))
{
var genericArgs = type.GetGenericArguments();
var keyType = genericArgs[0];
var valueType = genericArgs[1];
var genericGodotDictionaryType = typeof(Collections.Dictionary<,>)
.MakeGenericType(keyType, valueType);
return ConvertVariantToGenericGodotCollectionsDictionary(p_var, genericGodotDictionaryType);
}
if (genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IEnumerable<>))
{
var elementType = type.GetGenericArguments()[0];
var genericGodotArrayType = typeof(Collections.Array<>)
.MakeGenericType(elementType);
return ConvertVariantToGenericGodotCollectionsArray(p_var, genericGodotArrayType);
}
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;
}
public static unsafe object? ConvertVariantToManagedObject(in godot_variant p_var)
{
switch (p_var.Type)
@ -949,20 +796,6 @@ namespace Godot.NativeInterop
// Array
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;
var ret = new T[length];
// 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
{
@ -972,12 +805,13 @@ namespace Godot.NativeInterop
int length = array.Count;
var ret = new T[length];
// ConvertVariantToManagedObjectOfType handled by Collections.Array.CopyToGeneric<T>
array.CopyToGeneric(ret, 0);
for (int i = 0; i < length; i++)
ret[i] = (T)array[i].AsGodotObject();
return ret;
}
// TODO: This needs reflection. Look for an alternative.
internal static Godot.Object[] ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(in godot_array p_array,
Type type)
{
@ -987,93 +821,52 @@ namespace Godot.NativeInterop
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());
for (int i = 0; i < length; i++)
ret[i] = array[i].AsGodotObject();
return ret;
}
public static godot_array ConvertSystemArrayToNativeGodotArray<T>(T[] p_array)
internal static StringName[] ConvertNativeGodotArrayToSystemArrayOfStringName(in godot_array p_array)
{
int length = p_array.Length;
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_array_new_copy(p_array));
if (length == 0)
return NativeFuncs.godotsharp_array_new();
using var array = new Collections.Array();
array.Resize(length);
int length = array.Count;
var ret = new StringName[length];
for (int i = 0; i < length; i++)
array[i] = p_array[i];
ret[i] = array[i].AsStringName();
var src = (godot_array)array.NativeValue;
return NativeFuncs.godotsharp_array_new_copy(src);
return ret;
}
public static godot_array ConvertICollectionToNativeGodotArray(ICollection p_array)
internal static NodePath[] ConvertNativeGodotArrayToSystemArrayOfNodePath(in godot_array p_array)
{
int length = p_array.Count;
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_array_new_copy(p_array));
if (length == 0)
return NativeFuncs.godotsharp_array_new();
int length = array.Count;
var ret = new NodePath[length];
using var array = new Collections.Array();
array.Resize(length);
for (int i = 0; i < length; i++)
ret[i] = array[i].AsNodePath();
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);
return ret;
}
public static godot_array ConvertGenericICollectionToNativeGodotArray<T>(ICollection<T> p_array)
internal static RID[] ConvertNativeGodotArrayToSystemArrayOfRID(in godot_array p_array)
{
int length = p_array.Count;
var array = Collections.Array.CreateTakingOwnershipOfDisposableValue(
NativeFuncs.godotsharp_array_new_copy(p_array));
if (length == 0)
return NativeFuncs.godotsharp_array_new();
int length = array.Count;
var ret = new RID[length];
var array = new Collections.Array<T>();
using var underlyingArray = (Collections.Array)array;
array.Resize(length);
for (int i = 0; i < length; i++)
ret[i] = array[i].AsRID();
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);
return ret;
}
// PackedByteArray

View file

@ -210,21 +210,22 @@ namespace Godot.NativeInterop
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);
}
public static godot_variant CreateFromSystemArrayOfStringName(Span<StringName> from)
=> CreateFromArray(new Collections.Array(from));
public static godot_variant CreateFromSystemArrayOfNodePath(Span<NodePath> from)
=> CreateFromArray(new Collections.Array(from));
public static godot_variant CreateFromSystemArrayOfRID(Span<RID> from)
=> CreateFromArray(new Collections.Array(from));
// 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);
using var fromGodot = new Collections.Array(from);
return CreateFromArray((godot_array)fromGodot.NativeValue);
}
public static godot_variant CreateFromArray(godot_array from)
@ -237,45 +238,6 @@ namespace Godot.NativeInterop
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);
@ -286,51 +248,6 @@ namespace Godot.NativeInterop
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);
@ -381,17 +298,17 @@ namespace Godot.NativeInterop
p_var.Int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static Int16 ConvertToInt16(in godot_variant p_var)
=> (Int16)(p_var.Type == Variant.Type.Int ?
public static short ConvertToInt16(in godot_variant p_var)
=> (short)(p_var.Type == Variant.Type.Int ?
p_var.Int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static Int32 ConvertToInt32(in godot_variant p_var)
=> (Int32)(p_var.Type == Variant.Type.Int ?
public static int ConvertToInt32(in godot_variant p_var)
=> (int)(p_var.Type == Variant.Type.Int ?
p_var.Int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static Int64 ConvertToInt64(in godot_variant p_var)
public static long ConvertToInt64(in godot_variant p_var)
=> p_var.Type == Variant.Type.Int ? p_var.Int : NativeFuncs.godotsharp_variant_as_int(p_var);
public static byte ConvertToUInt8(in godot_variant p_var)
@ -399,18 +316,18 @@ namespace Godot.NativeInterop
p_var.Int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static UInt16 ConvertToUInt16(in godot_variant p_var)
=> (UInt16)(p_var.Type == Variant.Type.Int ?
public static ushort ConvertToUInt16(in godot_variant p_var)
=> (ushort)(p_var.Type == Variant.Type.Int ?
p_var.Int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static UInt32 ConvertToUInt32(in godot_variant p_var)
=> (UInt32)(p_var.Type == Variant.Type.Int ?
public static uint ConvertToUInt32(in godot_variant p_var)
=> (uint)(p_var.Type == Variant.Type.Int ?
p_var.Int :
NativeFuncs.godotsharp_variant_as_int(p_var));
public static UInt64 ConvertToUInt64(in godot_variant p_var)
=> (UInt64)(p_var.Type == Variant.Type.Int ?
public static ulong ConvertToUInt64(in godot_variant p_var)
=> (ulong)(p_var.Type == Variant.Type.Int ?
p_var.Int :
NativeFuncs.godotsharp_variant_as_int(p_var));
@ -642,10 +559,22 @@ namespace Godot.NativeInterop
return Marshaling.ConvertNativePackedColorArrayToSystemArray(packedArray);
}
public static T[] ConvertToSystemArrayOfSupportedType<T>(in godot_variant p_var)
public static StringName[] ConvertToSystemArrayOfStringName(in godot_variant p_var)
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfType<T>(godotArray);
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfStringName(godotArray);
}
public static NodePath[] ConvertToSystemArrayOfNodePath(in godot_variant p_var)
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfNodePath(godotArray);
}
public static RID[] ConvertToSystemArrayOfRID(in godot_variant p_var)
{
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfRID(godotArray);
}
public static T[] ConvertToSystemArrayOfGodotObject<T>(in godot_variant p_var)
@ -662,37 +591,5 @@ namespace Godot.NativeInterop
using var godotArray = NativeFuncs.godotsharp_variant_as_array(p_var);
return Marshaling.ConvertNativeGodotArrayToSystemArrayOfGodotObjectType(godotArray, type);
}
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<object, object> pair in godotDictionary)
res.Add((TKey)pair.Key, (TValue)pair.Value);
return res;
}
}
}

View file

@ -187,18 +187,6 @@ namespace Godot
return null;
}
internal static bool InternalIsClassNativeBase(Type t)
{
// Check whether the type is declared in the GodotSharp or GodotSharpEditor assemblies
var typeAssembly = t.Assembly;
if (typeAssembly == CachedType.Assembly)
return true;
var typeAssemblyName = t.Assembly.GetName();
return typeAssemblyName.Name == "GodotSharpEditor";
}
// ReSharper disable once VirtualMemberNeverOverridden.Global
protected internal virtual bool SetGodotClassPropertyValue(in godot_string_name name, in godot_variant value)
{

View file

@ -4,10 +4,10 @@ using Godot.NativeInterop;
namespace Godot
{
public class SignalAwaiter : IAwaiter<object[]>, IAwaitable<object[]>
public class SignalAwaiter : IAwaiter<Variant[]>, IAwaitable<Variant[]>
{
private bool _completed;
private object[] _result;
private Variant[] _result;
private Action _continuation;
public SignalAwaiter(Object source, StringName signal, Object target)
@ -26,9 +26,9 @@ namespace Godot
_continuation = continuation;
}
public object[] GetResult() => _result;
public Variant[] GetResult() => _result;
public IAwaiter<object[]> GetAwaiter() => this;
public IAwaiter<Variant[]> GetAwaiter() => this;
[UnmanagedCallersOnly]
internal static unsafe void SignalCallback(IntPtr awaiterGCHandlePtr, godot_variant** args, int argCount,
@ -48,10 +48,10 @@ namespace Godot
awaiter._completed = true;
object[] signalArgs = new object[argCount];
Variant[] signalArgs = new Variant[argCount];
for (int i = 0; i < argCount; i++)
signalArgs[i] = Marshaling.ConvertVariantToManagedObject(*args[i]);
signalArgs[i] = Variant.CreateCopyingBorrowed(*args[i]);
awaiter._result = signalArgs;

View file

@ -78,7 +78,6 @@
<Compile Include="Core\Extensions\ObjectExtensions.cs" />
<Compile Include="Core\Extensions\PackedSceneExtensions.cs" />
<Compile Include="Core\Extensions\ResourceLoaderExtensions.cs" />
<Compile Include="Core\Extensions\SceneTreeExtensions.cs" />
<Compile Include="Core\GD.cs" />
<Compile Include="Core\GodotSynchronizationContext.cs" />
<Compile Include="Core\GodotTaskScheduler.cs" />

View file

@ -105,6 +105,8 @@ public partial struct Variant : IDisposable
// TODO: Consider renaming Variant.Type to VariantType and this property to Type. VariantType would also avoid ambiguity with System.Type.
public Type VariantType => NativeVar.DangerousSelfRef.Type;
public override string ToString() => AsString();
public object? Obj
{
get
@ -116,8 +118,6 @@ public partial struct Variant : IDisposable
}
}
// TODO: Consider implicit operators
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool AsBool() =>
VariantUtils.ConvertToBool((godot_variant)NativeVar);
@ -210,6 +210,18 @@ public partial struct Variant : IDisposable
public Transform3D AsTransform3D() =>
VariantUtils.ConvertToTransform3D((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 AsVector4() =>
VariantUtils.ConvertToVector4((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4i AsVector4i() =>
VariantUtils.ConvertToVector4i((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Projection AsProjection() =>
VariantUtils.ConvertToProjection((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public AABB AsAABB() =>
VariantUtils.ConvertToAABB((godot_variant)NativeVar);
@ -272,25 +284,16 @@ public partial struct Variant : IDisposable
VariantUtils.ConvertToSystemArrayOfGodotObject<T>((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] AsSystemArrayOfSupportedType<T>() =>
VariantUtils.ConvertToSystemArrayOfSupportedType<T>((godot_variant)NativeVar);
public StringName[] AsSystemArrayOfStringName() =>
VariantUtils.ConvertToSystemArrayOfStringName((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Collections.Dictionary<TKey, TValue> AsGodotGenericDictionary<TKey, TValue>() =>
VariantUtils.ConvertToGenericDictionaryObject<TKey, TValue>((godot_variant)NativeVar);
public NodePath[] AsSystemArrayOfNodePath() =>
VariantUtils.ConvertToSystemArrayOfNodePath((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Collections.Array<T> AsGodotGenericArray<T>() =>
VariantUtils.ConvertToGenericArrayObject<T>((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public System.Collections.Generic.Dictionary<TKey, TValue> AsSystemGenericDictionary<TKey, TValue>()
where TKey : notnull =>
VariantUtils.ConvertToSystemGenericDictionary<TKey, TValue>((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public System.Collections.Generic.List<T> AsSystemGenericList<T>() =>
VariantUtils.ConvertToSystemGenericList<T>((godot_variant)NativeVar);
public RID[] AsSystemArrayOfRID() =>
VariantUtils.ConvertToSystemArrayOfRID((godot_variant)NativeVar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Godot.Object AsGodotObject() =>
@ -387,6 +390,15 @@ public partial struct Variant : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Transform3D(Variant from) => from.AsTransform3D();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector4(Variant from) => from.AsVector4();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Vector4i(Variant from) => from.AsVector4i();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Projection(Variant from) => from.AsProjection();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator AABB(Variant from) => from.AsAABB();
@ -429,6 +441,15 @@ public partial struct Variant : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Color[](Variant from) => from.AsColorArray();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator StringName[](Variant from) => from.AsSystemArrayOfStringName();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator NodePath[](Variant from) => from.AsSystemArrayOfNodePath();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator RID[](Variant from) => from.AsSystemArrayOfRID();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Godot.Object(Variant from) => from.AsGodotObject();
@ -447,6 +468,161 @@ public partial struct Variant : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator Collections.Array(Variant from) => from.AsGodotArray();
// While we provide implicit conversion operators, normal methods are still needed for
// casts that are not done implicitly (e.g.: raw array to Span, enum to integer, etc).
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(bool from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(char from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(sbyte from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(short from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(int from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(long from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(byte from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(ushort from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(uint from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(ulong from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(float from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(double from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(string from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Vector2 from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Vector2i from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Rect2 from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Rect2i from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Transform2D from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Vector3 from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Vector3i from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Basis from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Quaternion from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Transform3D from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Vector4 from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Vector4i from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Projection from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(AABB from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Color from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Plane from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Callable from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(SignalInfo from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<byte> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<int> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<long> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<float> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<double> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<string> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<Vector2> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<Vector3> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<Color> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Godot.Object[] from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<StringName> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<NodePath> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Span<RID> from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Godot.Object from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(StringName from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(NodePath from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(RID from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Collections.Dictionary from) => from;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant CreateFrom(Collections.Array from) => from;
// Implicit conversion operators
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(bool from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromBool(from));
@ -539,6 +715,18 @@ public partial struct Variant : IDisposable
public static implicit operator Variant(Transform3D from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromTransform3D(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Vector4 from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Vector4i from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromVector4i(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Projection from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromProjection(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(AABB from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromAABB(from));
@ -596,36 +784,20 @@ public partial struct Variant : IDisposable
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromPackedColorArray(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Godot.Object[]? from) =>
public static implicit operator Variant(Godot.Object[] from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfGodotObject(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<TKey, TValue>(Collections.Dictionary<TKey, TValue> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromDictionary(from));
public static implicit operator Variant(Span<StringName> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfStringName(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<T>(Collections.Array<T> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
public static implicit operator Variant(Span<NodePath> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfNodePath(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<TKey, TValue>(System.Collections.Generic.Dictionary<TKey, TValue> from)
where TKey : notnull => CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemDictionary(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<T>(System.Collections.Generic.List<T> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemICollection(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<TKey, TValue>(System.Collections.Generic.IDictionary<TKey, TValue> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericIDictionary(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<T>(System.Collections.Generic.ICollection<T> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericICollection(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From<T>(System.Collections.Generic.IEnumerable<T> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemGenericIEnumerable(from));
public static implicit operator Variant(Span<RID> from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemArrayOfRID(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Godot.Object from) =>
@ -650,16 +822,4 @@ public partial struct Variant : IDisposable
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Variant(Collections.Array from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromArray(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From(System.Collections.IDictionary from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemIDictionary(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From(System.Collections.ICollection from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemICollection(from));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Variant From(System.Collections.IEnumerable from) =>
CreateTakingOwnershipOfDisposableValue(VariantUtils.CreateFromSystemIEnumerable(from));
}