virtualx-engine/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/MarshalUtils.cs
Ignacio Roldán Etcheverry 4d710bf659 C#: Add initial implementation of source generator for script members
This replaces the way we invoke methods and set/get properties.
This first iteration rids us of runtime type checking in those
cases, as it's now done at compile time.
Later it will also stop needing the use of reflection. After that,
we will only depend on reflection for generic Godot Array and
Dictionary. We're stuck with reflection in generic collections
for now as C# doesn't support generic/template specialization.

This is only the initial implementation. Further iterations are
coming, specially once we switch to the native extension system
which completely changes the way members are accessed/invoked.
For example, with the native extension system we will likely need
to create `UnmanagedCallersOnly` invoke wrapper methods and return
function pointers to the engine.

Other kind of members, like event signals will be receiving the
same treatment in the future.
2022-08-22 03:36:51 +02:00

224 lines
11 KiB
C#

using System;
using Microsoft.CodeAnalysis;
namespace Godot.SourceGenerators
{
public static class MarshalUtils
{
public class TypeCache
{
public INamedTypeSymbol GodotObjectType { get; }
public INamedTypeSymbol GodotGenericDictionary { get; }
public INamedTypeSymbol GodotGenericArray { get; }
public INamedTypeSymbol IDictionary { get; }
public INamedTypeSymbol ICollection { get; }
public INamedTypeSymbol GenericIDictionary { get; }
public INamedTypeSymbol SystemGenericDictionary { get; }
public INamedTypeSymbol SystemGenericList { get; }
public TypeCache(GeneratorExecutionContext context)
{
INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName)
{
return context.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ??
throw new InvalidOperationException("Type not found: " + fullyQualifiedMetadataName);
}
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");
}
}
public static MarshalType? ConvertManagedTypeToVariantType(ITypeSymbol type, TypeCache typeCache)
{
var specialType = type.SpecialType;
switch (specialType)
{
case SpecialType.System_Boolean:
return MarshalType.Boolean;
case SpecialType.System_Char:
return MarshalType.Char;
case SpecialType.System_SByte:
return MarshalType.SByte;
case SpecialType.System_Int16:
return MarshalType.Int16;
case SpecialType.System_Int32:
return MarshalType.Int32;
case SpecialType.System_Int64:
return MarshalType.Int64;
case SpecialType.System_Byte:
return MarshalType.Byte;
case SpecialType.System_UInt16:
return MarshalType.UInt16;
case SpecialType.System_UInt32:
return MarshalType.UInt32;
case SpecialType.System_UInt64:
return MarshalType.UInt64;
case SpecialType.System_Single:
return MarshalType.Single;
case SpecialType.System_Double:
return MarshalType.Double;
case SpecialType.System_String:
return MarshalType.String;
case SpecialType.System_Object:
return MarshalType.SystemObject;
case SpecialType.System_ValueType:
{
if (type.ContainingAssembly.Name == "GodotSharp" &&
type.ContainingNamespace.Name == "Godot")
{
return type switch
{
{ Name: "Vector2" } => MarshalType.Vector2,
{ Name: "Vector2i" } => MarshalType.Vector2i,
{ Name: "Rect2" } => MarshalType.Rect2,
{ Name: "Rect2i" } => MarshalType.Rect2i,
{ Name: "Transform2D" } => MarshalType.Transform2D,
{ Name: "Vector3" } => MarshalType.Vector3,
{ Name: "Vector3i" } => MarshalType.Vector3i,
{ Name: "Basis" } => MarshalType.Basis,
{ Name: "Quaternion" } => MarshalType.Quaternion,
{ Name: "Transform3D" } => MarshalType.Transform3D,
{ Name: "AABB" } => MarshalType.AABB,
{ Name: "Color" } => MarshalType.Color,
{ Name: "Plane" } => MarshalType.Plane,
{ Name: "RID" } => MarshalType.RID,
{ Name: "Callable" } => MarshalType.Callable,
{ Name: "SignalInfo" } => MarshalType.SignalInfo,
{ TypeKind: TypeKind.Enum } => MarshalType.Enum,
_ => null
};
}
return null;
}
default:
{
if (type.TypeKind == TypeKind.Array)
{
var arrayType = (IArrayTypeSymbol)type;
var elementType = arrayType.ElementType;
switch (elementType.SpecialType)
{
case SpecialType.System_Byte:
return MarshalType.ByteArray;
case SpecialType.System_Int32:
return MarshalType.Int32Array;
case SpecialType.System_Int64:
return MarshalType.Int64Array;
case SpecialType.System_Single:
return MarshalType.SingleArray;
case SpecialType.System_Double:
return MarshalType.DoubleArray;
case SpecialType.System_String:
return MarshalType.StringArray;
case SpecialType.System_Object:
return MarshalType.SystemObjectArray;
}
if (elementType.SimpleDerivesFrom(typeCache.GodotObjectType))
return MarshalType.GodotObjectOrDerivedArray;
if (type.ContainingAssembly.Name == "GodotSharp" &&
type.ContainingNamespace.Name == "Godot")
{
return elementType switch
{
{ Name: "Vector2" } => MarshalType.Vector2Array,
{ Name: "Vector3" } => MarshalType.Vector3Array,
{ Name: "Color" } => MarshalType.ColorArray,
_ => 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)
{
case "Godot":
return type switch
{
{ Name: "StringName" } => MarshalType.StringName,
{ Name: "NodePath" } => MarshalType.NodePath,
_ => null
};
case "Godot.Collections" when !(type is INamedTypeSymbol { IsGenericType: true }):
return type switch
{
{ Name: "Dictionary" } => MarshalType.GodotDictionary,
{ Name: "Array" } => MarshalType.GodotArray,
_ => null
};
}
}
}
break;
}
}
return null;
}
private static bool SimpleDerivesFrom(this ITypeSymbol? type, ITypeSymbol candidateBaseType)
{
while (type != null)
{
if (SymbolEqualityComparer.Default.Equals(type, candidateBaseType))
return true;
type = type.BaseType;
}
return false;
}
}
}