MarshalUtils.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. using System;
  2. using System.Linq;
  3. using System.Text;
  4. using Microsoft.CodeAnalysis;
  5. namespace Godot.SourceGenerators
  6. {
  7. internal static class MarshalUtils
  8. {
  9. public class TypeCache
  10. {
  11. public INamedTypeSymbol GodotObjectType { get; }
  12. public TypeCache(Compilation compilation)
  13. {
  14. INamedTypeSymbol GetTypeByMetadataNameOrThrow(string fullyQualifiedMetadataName)
  15. {
  16. return compilation.GetTypeByMetadataName(fullyQualifiedMetadataName) ??
  17. throw new InvalidOperationException($"Type not found: '{fullyQualifiedMetadataName}'.");
  18. }
  19. GodotObjectType = GetTypeByMetadataNameOrThrow(GodotClasses.GodotObject);
  20. }
  21. }
  22. public static VariantType? ConvertMarshalTypeToVariantType(MarshalType marshalType)
  23. => marshalType switch
  24. {
  25. MarshalType.Boolean => VariantType.Bool,
  26. MarshalType.Char => VariantType.Int,
  27. MarshalType.SByte => VariantType.Int,
  28. MarshalType.Int16 => VariantType.Int,
  29. MarshalType.Int32 => VariantType.Int,
  30. MarshalType.Int64 => VariantType.Int,
  31. MarshalType.Byte => VariantType.Int,
  32. MarshalType.UInt16 => VariantType.Int,
  33. MarshalType.UInt32 => VariantType.Int,
  34. MarshalType.UInt64 => VariantType.Int,
  35. MarshalType.Single => VariantType.Float,
  36. MarshalType.Double => VariantType.Float,
  37. MarshalType.String => VariantType.String,
  38. MarshalType.Vector2 => VariantType.Vector2,
  39. MarshalType.Vector2I => VariantType.Vector2I,
  40. MarshalType.Rect2 => VariantType.Rect2,
  41. MarshalType.Rect2I => VariantType.Rect2I,
  42. MarshalType.Transform2D => VariantType.Transform2D,
  43. MarshalType.Vector3 => VariantType.Vector3,
  44. MarshalType.Vector3I => VariantType.Vector3I,
  45. MarshalType.Basis => VariantType.Basis,
  46. MarshalType.Quaternion => VariantType.Quaternion,
  47. MarshalType.Transform3D => VariantType.Transform3D,
  48. MarshalType.Vector4 => VariantType.Vector4,
  49. MarshalType.Vector4I => VariantType.Vector4I,
  50. MarshalType.Projection => VariantType.Projection,
  51. MarshalType.Aabb => VariantType.Aabb,
  52. MarshalType.Color => VariantType.Color,
  53. MarshalType.Plane => VariantType.Plane,
  54. MarshalType.Callable => VariantType.Callable,
  55. MarshalType.Signal => VariantType.Signal,
  56. MarshalType.Enum => VariantType.Int,
  57. MarshalType.ByteArray => VariantType.PackedByteArray,
  58. MarshalType.Int32Array => VariantType.PackedInt32Array,
  59. MarshalType.Int64Array => VariantType.PackedInt64Array,
  60. MarshalType.Float32Array => VariantType.PackedFloat32Array,
  61. MarshalType.Float64Array => VariantType.PackedFloat64Array,
  62. MarshalType.StringArray => VariantType.PackedStringArray,
  63. MarshalType.Vector2Array => VariantType.PackedVector2Array,
  64. MarshalType.Vector3Array => VariantType.PackedVector3Array,
  65. MarshalType.Vector4Array => VariantType.PackedVector4Array,
  66. MarshalType.ColorArray => VariantType.PackedColorArray,
  67. MarshalType.GodotObjectOrDerivedArray => VariantType.Array,
  68. MarshalType.SystemArrayOfStringName => VariantType.Array,
  69. MarshalType.SystemArrayOfNodePath => VariantType.Array,
  70. MarshalType.SystemArrayOfRid => VariantType.Array,
  71. MarshalType.Variant => VariantType.Nil,
  72. MarshalType.GodotObjectOrDerived => VariantType.Object,
  73. MarshalType.StringName => VariantType.StringName,
  74. MarshalType.NodePath => VariantType.NodePath,
  75. MarshalType.Rid => VariantType.Rid,
  76. MarshalType.GodotDictionary => VariantType.Dictionary,
  77. MarshalType.GodotArray => VariantType.Array,
  78. MarshalType.GodotGenericDictionary => VariantType.Dictionary,
  79. MarshalType.GodotGenericArray => VariantType.Array,
  80. _ => null
  81. };
  82. public static MarshalType? ConvertManagedTypeToMarshalType(ITypeSymbol type, TypeCache typeCache)
  83. {
  84. var specialType = type.SpecialType;
  85. switch (specialType)
  86. {
  87. case SpecialType.System_Boolean:
  88. return MarshalType.Boolean;
  89. case SpecialType.System_Char:
  90. return MarshalType.Char;
  91. case SpecialType.System_SByte:
  92. return MarshalType.SByte;
  93. case SpecialType.System_Int16:
  94. return MarshalType.Int16;
  95. case SpecialType.System_Int32:
  96. return MarshalType.Int32;
  97. case SpecialType.System_Int64:
  98. return MarshalType.Int64;
  99. case SpecialType.System_Byte:
  100. return MarshalType.Byte;
  101. case SpecialType.System_UInt16:
  102. return MarshalType.UInt16;
  103. case SpecialType.System_UInt32:
  104. return MarshalType.UInt32;
  105. case SpecialType.System_UInt64:
  106. return MarshalType.UInt64;
  107. case SpecialType.System_Single:
  108. return MarshalType.Single;
  109. case SpecialType.System_Double:
  110. return MarshalType.Double;
  111. case SpecialType.System_String:
  112. return MarshalType.String;
  113. default:
  114. {
  115. var typeKind = type.TypeKind;
  116. if (typeKind == TypeKind.Enum)
  117. return MarshalType.Enum;
  118. if (typeKind == TypeKind.Struct)
  119. {
  120. if (type.ContainingAssembly?.Name == "GodotSharp" &&
  121. type.ContainingNamespace?.Name == "Godot")
  122. {
  123. return type switch
  124. {
  125. { Name: "Vector2" } => MarshalType.Vector2,
  126. { Name: "Vector2I" } => MarshalType.Vector2I,
  127. { Name: "Rect2" } => MarshalType.Rect2,
  128. { Name: "Rect2I" } => MarshalType.Rect2I,
  129. { Name: "Transform2D" } => MarshalType.Transform2D,
  130. { Name: "Vector3" } => MarshalType.Vector3,
  131. { Name: "Vector3I" } => MarshalType.Vector3I,
  132. { Name: "Basis" } => MarshalType.Basis,
  133. { Name: "Quaternion" } => MarshalType.Quaternion,
  134. { Name: "Transform3D" } => MarshalType.Transform3D,
  135. { Name: "Vector4" } => MarshalType.Vector4,
  136. { Name: "Vector4I" } => MarshalType.Vector4I,
  137. { Name: "Projection" } => MarshalType.Projection,
  138. { Name: "Aabb" } => MarshalType.Aabb,
  139. { Name: "Color" } => MarshalType.Color,
  140. { Name: "Plane" } => MarshalType.Plane,
  141. { Name: "Rid" } => MarshalType.Rid,
  142. { Name: "Callable" } => MarshalType.Callable,
  143. { Name: "Signal" } => MarshalType.Signal,
  144. { Name: "Variant" } => MarshalType.Variant,
  145. _ => null
  146. };
  147. }
  148. }
  149. else if (typeKind == TypeKind.Array)
  150. {
  151. var arrayType = (IArrayTypeSymbol)type;
  152. if (arrayType.Rank != 1)
  153. return null;
  154. var elementType = arrayType.ElementType;
  155. switch (elementType.SpecialType)
  156. {
  157. case SpecialType.System_Byte:
  158. return MarshalType.ByteArray;
  159. case SpecialType.System_Int32:
  160. return MarshalType.Int32Array;
  161. case SpecialType.System_Int64:
  162. return MarshalType.Int64Array;
  163. case SpecialType.System_Single:
  164. return MarshalType.Float32Array;
  165. case SpecialType.System_Double:
  166. return MarshalType.Float64Array;
  167. case SpecialType.System_String:
  168. return MarshalType.StringArray;
  169. }
  170. if (elementType.SimpleDerivesFrom(typeCache.GodotObjectType))
  171. return MarshalType.GodotObjectOrDerivedArray;
  172. if (elementType.ContainingAssembly?.Name == "GodotSharp" &&
  173. elementType.ContainingNamespace?.Name == "Godot")
  174. {
  175. switch (elementType)
  176. {
  177. case { Name: "Vector2" }:
  178. return MarshalType.Vector2Array;
  179. case { Name: "Vector3" }:
  180. return MarshalType.Vector3Array;
  181. case { Name: "Vector4" }:
  182. return MarshalType.Vector4Array;
  183. case { Name: "Color" }:
  184. return MarshalType.ColorArray;
  185. case { Name: "StringName" }:
  186. return MarshalType.SystemArrayOfStringName;
  187. case { Name: "NodePath" }:
  188. return MarshalType.SystemArrayOfNodePath;
  189. case { Name: "Rid" }:
  190. return MarshalType.SystemArrayOfRid;
  191. }
  192. }
  193. return null;
  194. }
  195. else
  196. {
  197. if (type.SimpleDerivesFrom(typeCache.GodotObjectType))
  198. return MarshalType.GodotObjectOrDerived;
  199. if (type.ContainingAssembly?.Name == "GodotSharp")
  200. {
  201. switch (type.ContainingNamespace?.Name)
  202. {
  203. case "Godot":
  204. return type switch
  205. {
  206. { Name: "StringName" } => MarshalType.StringName,
  207. { Name: "NodePath" } => MarshalType.NodePath,
  208. _ => null
  209. };
  210. case "Collections"
  211. when type.ContainingNamespace?.FullQualifiedNameOmitGlobal() == "Godot.Collections":
  212. return type switch
  213. {
  214. { Name: "Dictionary" } =>
  215. type is INamedTypeSymbol { IsGenericType: false } ?
  216. MarshalType.GodotDictionary :
  217. MarshalType.GodotGenericDictionary,
  218. { Name: "Array" } =>
  219. type is INamedTypeSymbol { IsGenericType: false } ?
  220. MarshalType.GodotArray :
  221. MarshalType.GodotGenericArray,
  222. _ => null
  223. };
  224. }
  225. }
  226. }
  227. break;
  228. }
  229. }
  230. return null;
  231. }
  232. private static bool SimpleDerivesFrom(this ITypeSymbol? type, ITypeSymbol candidateBaseType)
  233. {
  234. while (type != null)
  235. {
  236. if (SymbolEqualityComparer.Default.Equals(type, candidateBaseType))
  237. return true;
  238. type = type.BaseType;
  239. }
  240. return false;
  241. }
  242. public static ITypeSymbol? GetArrayElementType(ITypeSymbol typeSymbol)
  243. {
  244. if (typeSymbol.TypeKind == TypeKind.Array)
  245. {
  246. var arrayType = (IArrayTypeSymbol)typeSymbol;
  247. return arrayType.ElementType;
  248. }
  249. if (typeSymbol is INamedTypeSymbol { IsGenericType: true } genericType)
  250. return genericType.TypeArguments.FirstOrDefault();
  251. return null;
  252. }
  253. private static StringBuilder Append(this StringBuilder source, string a, string b)
  254. => source.Append(a).Append(b);
  255. private static StringBuilder Append(this StringBuilder source, string a, string b, string c)
  256. => source.Append(a).Append(b).Append(c);
  257. private static StringBuilder Append(this StringBuilder source, string a, string b,
  258. string c, string d)
  259. => source.Append(a).Append(b).Append(c).Append(d);
  260. private static StringBuilder Append(this StringBuilder source, string a, string b,
  261. string c, string d, string e)
  262. => source.Append(a).Append(b).Append(c).Append(d).Append(e);
  263. private static StringBuilder Append(this StringBuilder source, string a, string b,
  264. string c, string d, string e, string f)
  265. => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f);
  266. private static StringBuilder Append(this StringBuilder source, string a, string b,
  267. string c, string d, string e, string f, string g)
  268. => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g);
  269. private static StringBuilder Append(this StringBuilder source, string a, string b,
  270. string c, string d, string e, string f, string g, string h)
  271. => source.Append(a).Append(b).Append(c).Append(d).Append(e).Append(f).Append(g).Append(h);
  272. private const string VariantUtils = "global::Godot.NativeInterop.VariantUtils";
  273. public static StringBuilder AppendNativeVariantToManagedExpr(this StringBuilder source,
  274. string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
  275. {
  276. return marshalType switch
  277. {
  278. // We need a special case for GodotObjectOrDerived[], because it's not supported by VariantUtils.ConvertTo<T>
  279. MarshalType.GodotObjectOrDerivedArray =>
  280. source.Append(VariantUtils, ".ConvertToSystemArrayOfGodotObject<",
  281. ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">(",
  282. inputExpr, ")"),
  283. // We need a special case for generic Godot collections and GodotObjectOrDerived[], because VariantUtils.ConvertTo<T> is slower
  284. MarshalType.GodotGenericDictionary =>
  285. source.Append(VariantUtils, ".ConvertToDictionary<",
  286. ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
  287. ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">(",
  288. inputExpr, ")"),
  289. MarshalType.GodotGenericArray =>
  290. source.Append(VariantUtils, ".ConvertToArray<",
  291. ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">(",
  292. inputExpr, ")"),
  293. _ => source.Append(VariantUtils, ".ConvertTo<",
  294. typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
  295. };
  296. }
  297. public static StringBuilder AppendManagedToNativeVariantExpr(this StringBuilder source,
  298. string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
  299. {
  300. return marshalType switch
  301. {
  302. // We need a special case for GodotObjectOrDerived[], because it's not supported by VariantUtils.CreateFrom<T>
  303. MarshalType.GodotObjectOrDerivedArray =>
  304. source.Append(VariantUtils, ".CreateFromSystemArrayOfGodotObject(", inputExpr, ")"),
  305. // We need a special case for generic Godot collections and GodotObjectOrDerived[], because VariantUtils.CreateFrom<T> is slower
  306. MarshalType.GodotGenericDictionary =>
  307. source.Append(VariantUtils, ".CreateFromDictionary(", inputExpr, ")"),
  308. MarshalType.GodotGenericArray =>
  309. source.Append(VariantUtils, ".CreateFromArray(", inputExpr, ")"),
  310. _ => source.Append(VariantUtils, ".CreateFrom<",
  311. typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")"),
  312. };
  313. }
  314. public static StringBuilder AppendVariantToManagedExpr(this StringBuilder source,
  315. string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
  316. {
  317. return marshalType switch
  318. {
  319. // We need a special case for GodotObjectOrDerived[], because it's not supported by Variant.As<T>
  320. MarshalType.GodotObjectOrDerivedArray =>
  321. source.Append(inputExpr, ".AsGodotObjectArray<",
  322. ((IArrayTypeSymbol)typeSymbol).ElementType.FullQualifiedNameIncludeGlobal(), ">()"),
  323. // We need a special case for generic Godot collections and GodotObjectOrDerived[], because Variant.As<T> is slower
  324. MarshalType.GodotGenericDictionary =>
  325. source.Append(inputExpr, ".AsGodotDictionary<",
  326. ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ", ",
  327. ((INamedTypeSymbol)typeSymbol).TypeArguments[1].FullQualifiedNameIncludeGlobal(), ">()"),
  328. MarshalType.GodotGenericArray =>
  329. source.Append(inputExpr, ".AsGodotArray<",
  330. ((INamedTypeSymbol)typeSymbol).TypeArguments[0].FullQualifiedNameIncludeGlobal(), ">()"),
  331. _ => source.Append(inputExpr, ".As<",
  332. typeSymbol.FullQualifiedNameIncludeGlobal(), ">()")
  333. };
  334. }
  335. public static StringBuilder AppendManagedToVariantExpr(this StringBuilder source,
  336. string inputExpr, ITypeSymbol typeSymbol, MarshalType marshalType)
  337. {
  338. return marshalType switch
  339. {
  340. // We need a special case for GodotObjectOrDerived[], because it's not supported by Variant.From<T>
  341. MarshalType.GodotObjectOrDerivedArray =>
  342. source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"),
  343. // We need a special case for generic Godot collections, because Variant.From<T> is slower
  344. MarshalType.GodotGenericDictionary or MarshalType.GodotGenericArray =>
  345. source.Append("global::Godot.Variant.CreateFrom(", inputExpr, ")"),
  346. _ => source.Append("global::Godot.Variant.From<",
  347. typeSymbol.FullQualifiedNameIncludeGlobal(), ">(", inputExpr, ")")
  348. };
  349. }
  350. }
  351. }