Add support for generic types in Scripting API with Template flag

This commit is contained in:
Wojtek Figat
2022-05-23 19:43:34 +02:00
parent d39b6d7120
commit b70bce1746
9 changed files with 63 additions and 41 deletions

View File

@@ -28,6 +28,7 @@ namespace Flax.Build.Bindings
public virtual bool IsValueType => false;
public virtual bool IsScriptingObject => false;
public virtual bool IsPod => false;
public virtual bool SkipGeneration => IsInBuild;
public FileInfo File
{

View File

@@ -235,10 +235,10 @@ namespace Flax.Build.Bindings
}
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller);
@@ -284,21 +284,23 @@ namespace Flax.Build.Bindings
// Find API type info
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
var typeName = typeInfo.Type.Replace("::", ".");
if (apiType != null)
{
CSharpUsedNamespaces.Add(apiType.Namespace);
if (apiType.IsScriptingObject || apiType.IsInterface)
return typeInfo.Type.Replace("::", ".");
return typeName;
if (typeInfo.IsPtr && apiType.IsPod)
return typeInfo.Type.Replace("::", ".") + '*';
return typeName + '*';
if (apiType is LangType && CSharpNativeToManagedBasicTypes.TryGetValue(apiType.Name, out result))
return result;
}
// Pointer
if (typeInfo.IsPtr)
return "IntPtr";
return typeInfo.Type.Replace("::", ".");
return typeName;
}
private static string GenerateCSharpManagedToNativeType(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller)
@@ -316,10 +318,10 @@ namespace Flax.Build.Bindings
}
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
return "IntPtr";
@@ -374,10 +376,10 @@ namespace Flax.Build.Bindings
}
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
return "FlaxEngine.Object.GetUnmanagedPtr({0})";
@@ -1355,7 +1357,7 @@ namespace Flax.Build.Bindings
private static bool GenerateCSharpType(BuildData buildData, StringBuilder contents, string indent, object type)
{
if (type is ApiTypeInfo apiTypeInfo && apiTypeInfo.IsInBuild)
if (type is ApiTypeInfo apiTypeInfo && apiTypeInfo.SkipGeneration)
return false;
try

View File

@@ -19,7 +19,7 @@ namespace Flax.Build.Bindings
partial class BindingsGenerator
{
private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
private const int CacheVersion = 12;
private const int CacheVersion = 13;
internal static void Write(BinaryWriter writer, string e)
{

View File

@@ -131,10 +131,10 @@ namespace Flax.Build.Bindings
return value;
if (typeInfo.Type == "String")
return $"Variant(StringView({value}))";
if (typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "ScriptingObjectReference" ||
if (typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "SoftObjectReference")
return $"Variant({value}.Get())";
if (typeInfo.IsArray)
@@ -254,7 +254,7 @@ namespace Flax.Build.Bindings
CppReferencesFiles.Add(apiType.File);
if (apiType.IsStruct && !apiType.IsPod && !CppUsedNonPodTypes.Contains(apiType))
CppUsedNonPodTypes.Add(apiType);
if (!apiType.IsInBuild && !apiType.IsEnum)
if (!apiType.SkipGeneration && !apiType.IsEnum)
{
// Use declared type initializer
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MClass.h");
@@ -329,7 +329,7 @@ namespace Flax.Build.Bindings
CppReferencesFiles.Add(apiType.File);
if (apiType.IsStruct && !apiType.IsPod && !CppUsedNonPodTypes.Contains(apiType))
CppUsedNonPodTypes.Add(apiType);
if (!apiType.IsInBuild && !apiType.IsEnum)
if (!apiType.SkipGeneration && !apiType.IsEnum)
{
// Use declared type initializer
CppIncludeFiles.Add("Engine/Scripting/ManagedCLR/MClass.h");
@@ -406,10 +406,10 @@ namespace Flax.Build.Bindings
return "MUtils::ToManaged({0})";
default:
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
{
type = "MonoObject*";
@@ -578,10 +578,10 @@ namespace Flax.Build.Bindings
return "MUtils::ToNative({0})";
default:
// Object reference property
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
if ((typeInfo.Type == "ScriptingObjectReference" ||
typeInfo.Type == "AssetReference" ||
typeInfo.Type == "WeakAssetReference" ||
typeInfo.Type == "SoftAssetReference" ||
typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
{
// For non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code)
@@ -2156,7 +2156,7 @@ namespace Flax.Build.Bindings
private static void GenerateCppType(BuildData buildData, StringBuilder contents, ModuleInfo moduleInfo, object type)
{
if (type is ApiTypeInfo apiTypeInfo && apiTypeInfo.IsInBuild)
if (type is ApiTypeInfo apiTypeInfo && apiTypeInfo.SkipGeneration)
return;
try
@@ -2197,6 +2197,11 @@ namespace Flax.Build.Bindings
}
}
}
if (apiType is ClassStructInfo classStructInfo)
{
if (classStructInfo.IsTemplate)
throw new Exception($"Cannot use template type '{classStructInfo}' as non-POD type for cross-language bindings.");
}
if (!CppUsedNonPodTypesList.Contains(apiType))
CppUsedNonPodTypesList.Add(apiType);
}

View File

@@ -499,7 +499,7 @@ namespace Flax.Build.Bindings
// Read 'class' keyword
var token = context.Tokenizer.NextToken();
if (token.Value != "class")
throw new Exception($"Invalid API_CLASS usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
throw new Exception($"Invalid {ApiTokens.Class} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
// Read specifiers
while (true)
@@ -551,6 +551,9 @@ namespace Flax.Build.Bindings
case "private":
desc.Access = AccessLevel.Private;
break;
case "template":
desc.IsTemplate = true;
break;
case "inbuild":
desc.IsInBuild = true;
break;
@@ -592,7 +595,7 @@ namespace Flax.Build.Bindings
// Read 'class' keyword
var token = context.Tokenizer.NextToken();
if (token.Value != "class")
throw new Exception($"Invalid API_INTERFACE usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
throw new Exception($"Invalid {ApiTokens.Interface} usage (expected 'class' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
// Read specifiers
while (true)
@@ -629,6 +632,9 @@ namespace Flax.Build.Bindings
case "private":
desc.Access = AccessLevel.Private;
break;
case "template":
desc.IsTemplate = true;
break;
case "inbuild":
desc.IsInBuild = true;
break;
@@ -876,7 +882,7 @@ namespace Flax.Build.Bindings
// Read 'enum' or `enum class` keywords
var token = context.Tokenizer.NextToken();
if (token.Value != "enum")
throw new Exception($"Invalid API_ENUM usage at line {context.Tokenizer.CurrentLine} (expected 'enum' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
throw new Exception($"Invalid {ApiTokens.Enum} usage at line {context.Tokenizer.CurrentLine} (expected 'enum' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
token = context.Tokenizer.NextToken();
if (token.Value != "class")
context.Tokenizer.PreviousToken();
@@ -1046,7 +1052,7 @@ namespace Flax.Build.Bindings
// Read 'struct' keyword
var token = context.Tokenizer.NextToken();
if (token.Value != "struct")
throw new Exception($"Invalid API_STRUCT usage (expected 'struct' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
throw new Exception($"Invalid {ApiTokens.Struct} usage (expected 'struct' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
// Read name
desc.Name = desc.NativeName = ParseName(ref context);
@@ -1068,6 +1074,9 @@ namespace Flax.Build.Bindings
case "private":
desc.Access = AccessLevel.Private;
break;
case "template":
desc.IsTemplate = true;
break;
case "inbuild":
desc.IsInBuild = true;
break;
@@ -1232,7 +1241,7 @@ namespace Flax.Build.Bindings
if (desc.Type.Type == "Action")
desc.Type = new TypeInfo { Type = "Delegate", GenericArgs = new List<TypeInfo>() };
else if (desc.Type.Type != "Delegate")
throw new Exception($"Invalid API_EVENT type. Only Action and Delegate<> types are supported. '{desc.Type}' used on event at line {context.Tokenizer.CurrentLine}.");
throw new Exception($"Invalid {ApiTokens.Event} type. Only Action and Delegate<> types are supported. '{desc.Type}' used on event at line {context.Tokenizer.CurrentLine}.");
// Read name
desc.Name = ParseName(ref context);

View File

@@ -541,7 +541,7 @@ namespace Flax.Build.Bindings
private static bool UseBindings(object type)
{
var apiTypeInfo = type as ApiTypeInfo;
if (apiTypeInfo != null && apiTypeInfo.IsInBuild)
if (apiTypeInfo != null && apiTypeInfo.SkipGeneration)
return false;
if ((type is ModuleInfo || type is FileInfo) && apiTypeInfo != null)
{

View File

@@ -13,11 +13,14 @@ namespace Flax.Build.Bindings
{
public AccessLevel Access;
public AccessLevel BaseTypeInheritance;
public bool IsTemplate;
public ClassStructInfo BaseType;
public List<InterfaceInfo> Interfaces;
public List<TypeInfo> Inheritance; // Data from parsing, used to interfaces and base type construct in Init
public List<FunctionInfo> Functions = new List<FunctionInfo>();
public override bool SkipGeneration => IsInBuild || IsTemplate;
public override void Init(Builder.BuildData buildData)
{
base.Init(buildData);
@@ -51,6 +54,7 @@ namespace Flax.Build.Bindings
{
writer.Write((byte)Access);
writer.Write((byte)BaseTypeInheritance);
writer.Write(IsTemplate);
BindingsGenerator.Write(writer, BaseType);
BindingsGenerator.Write(writer, Inheritance);
BindingsGenerator.Write(writer, Functions);
@@ -62,6 +66,7 @@ namespace Flax.Build.Bindings
{
Access = (AccessLevel)reader.ReadByte();
BaseTypeInheritance = (AccessLevel)reader.ReadByte();
IsTemplate = reader.ReadBoolean();
BaseType = BindingsGenerator.Read(reader, BaseType);
Inheritance = BindingsGenerator.Read(reader, Inheritance);
Functions = BindingsGenerator.Read(reader, Functions);

View File

@@ -25,7 +25,7 @@ namespace Flax.Build.Bindings
{
base.Init(buildData);
if (ForceNoPod || (Interfaces != null && Interfaces.Count != 0))
if (ForceNoPod || (Interfaces != null && Interfaces.Count != 0) || IsTemplate)
{
_isPod = false;
return;

View File

@@ -20,7 +20,7 @@ namespace Flax.Build.Platforms
/// <inheritdoc />
public override bool HasSharedLibrarySupport => true;
/// <inheritdoc />
public override bool HasExecutableFileReferenceSupport => true;