Add new API_TYPEDEF metadata for Scriptign API types instantiation (with Alias option)

This commit is contained in:
Wojtek Figat
2022-05-23 19:50:37 +02:00
parent b70bce1746
commit 85fd540b97
6 changed files with 230 additions and 10 deletions

View File

@@ -1,14 +1,16 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Flax.Build.Bindings
{
/// <summary>
/// The native type information for bindings generator.
/// </summary>
public class ApiTypeInfo : IBindingsCache
public class ApiTypeInfo : IBindingsCache, ICloneable
{
public ApiTypeInfo Parent;
public List<ApiTypeInfo> Children = new List<ApiTypeInfo>();
@@ -136,5 +138,20 @@ namespace Flax.Build.Bindings
{
return Name;
}
/// <inheritdoc />
public object Clone()
{
var clone = (ApiTypeInfo)Activator.CreateInstance(GetType());
using (var stream = new MemoryStream(1024))
{
using (var writer = new BinaryWriter(stream, Encoding.UTF8, true))
Write(writer);
stream.Position = 0;
using (var reader = new BinaryReader(stream))
clone.Read(reader);
}
return clone;
}
}
}

View File

@@ -20,6 +20,7 @@ namespace Flax.Build.Bindings
public const string Field = "API_FIELD";
public const string Event = "API_EVENT";
public const string Param = "API_PARAM";
public const string Typedef = "API_TYPEDEF";
public const string InjectCppCode = "API_INJECT_CPP_CODE";
public const string AutoSerialization = "API_AUTO_SERIALIZATION";
@@ -29,6 +30,7 @@ namespace Flax.Build.Bindings
Class,
Struct,
Interface,
Typedef,
};
}
@@ -306,6 +308,8 @@ namespace Flax.Build.Bindings
if (child.Name == typeInfo.Type)
{
child.EnsureInited(buildData);
if (child is TypedefInfo typedef)
return typedef.Typedef;
return child;
}

View File

@@ -311,7 +311,7 @@ namespace Flax.Build.Bindings
currentParam.Attributes = tag.Value;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on parameter at line {context.Tokenizer.CurrentLine}.");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {"function parameter"} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -567,7 +567,7 @@ namespace Flax.Build.Bindings
desc.Namespace = tag.Value;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on class {desc.Name} at line {context.Tokenizer.CurrentLine}");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -648,7 +648,7 @@ namespace Flax.Build.Bindings
desc.Namespace = tag.Value;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on interface {desc.Name} at line {context.Tokenizer.CurrentLine}");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -775,7 +775,7 @@ namespace Flax.Build.Bindings
desc.IsHidden = true;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on function {desc.Name}");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -965,7 +965,7 @@ namespace Flax.Build.Bindings
entry.Attributes = tag.Value;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on enum {desc.Name} entry at line {context.Tokenizer.CurrentLine}");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name + " enum entry"} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -1029,7 +1029,7 @@ namespace Flax.Build.Bindings
desc.Namespace = tag.Value;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on enum {desc.Name} at line {context.Tokenizer.CurrentLine}");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -1093,7 +1093,7 @@ namespace Flax.Build.Bindings
desc.Namespace = tag.Value;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on struct {desc.Name} at line {context.Tokenizer.CurrentLine}");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -1211,7 +1211,7 @@ namespace Flax.Build.Bindings
desc.NoArray = true;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on field {desc.Name} at line {context.Tokenizer.CurrentLine}");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -1273,7 +1273,7 @@ namespace Flax.Build.Bindings
desc.IsHidden = true;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on event {desc.Name} at line {context.Tokenizer.CurrentLine}");
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}");
break;
}
}
@@ -1291,5 +1291,59 @@ namespace Flax.Build.Bindings
context.Tokenizer.ExpectToken(TokenType.RightParent);
return desc;
}
private static TypedefInfo ParseTypedef(ref ParsingContext context)
{
var desc = new TypedefInfo
{
};
// Read the documentation comment
desc.Comment = ParseComment(ref context);
// Read parameters from the tag
var tagParams = ParseTagParameters(ref context);
// Read 'typedef' keyword
var token = context.Tokenizer.NextToken();
if (token.Value != "typedef")
throw new Exception($"Invalid {ApiTokens.Typedef} usage (expected 'typedef' keyword but got '{token.Value} {context.Tokenizer.NextToken().Value}').");
// Read type definition
desc.Type = ParseType(ref context);
// Read name
desc.Name = ParseName(ref context);
// Read ';'
context.Tokenizer.ExpectToken(TokenType.SemiColon);
// Process tag parameters
foreach (var tag in tagParams)
{
switch (tag.Tag.ToLower())
{
case "alias":
desc.IsAlias = true;
break;
case "inbuild":
desc.IsInBuild = true;
break;
case "attributes":
desc.Attributes = tag.Value;
break;
case "name":
desc.Name = tag.Value;
break;
case "namespace":
desc.Namespace = tag.Value;
break;
default:
Log.Warning($"Unknown or not supported tag parameter {tag} used on {desc.Name} at line {context.Tokenizer.CurrentLine}");
break;
}
}
return desc;
}
}
}

View File

@@ -296,6 +296,11 @@ namespace Flax.Build.Bindings
else
throw new Exception($"Not supported location for event {eventInfo.Name} at line {tokenizer.CurrentLine}. Place it in the class to use API bindings for it.");
}
else if (string.Equals(token.Value, ApiTokens.Typedef, StringComparison.Ordinal))
{
var typeInfo = ParseTypedef(ref context);
fileInfo.AddChild(typeInfo);
}
else if (string.Equals(token.Value, ApiTokens.InjectCppCode, StringComparison.Ordinal))
{
var injectCppCodeInfo = ParseInjectCppCode(ref context);

View File

@@ -0,0 +1,139 @@
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
using System;
using System.IO;
namespace Flax.Build.Bindings
{
/// <summary>
/// The type definition for bindings generator.
/// </summary>
public class TypedefInfo : ApiTypeInfo
{
public bool IsAlias;
public TypeInfo Type;
public ApiTypeInfo Typedef;
public override void Init(Builder.BuildData buildData)
{
base.Init(buildData);
// Remove previous typedef (if any)
if (Typedef == null)
{
foreach (var e in Parent.Children)
{
if (e != this && e.Name == Name)
{
Typedef = e;
break;
}
}
}
if (Typedef != null)
{
Typedef.Parent = null;
Parent.Children.Remove(Typedef);
Typedef = null;
}
// Find typedef type
var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, Type, Parent);
if (apiTypeInfo == null)
throw new Exception(string.Format("Unknown type '{0}' for typedef '{1}'.", Type, Name));
apiTypeInfo.EnsureInited(buildData);
// Alias type without introducing any new type
if (IsAlias || apiTypeInfo is LangType)
{
if (apiTypeInfo is ClassStructInfo typedefClassStruct && typedefClassStruct.IsTemplate)
throw new Exception(string.Format("Cannot use typedef '{0}' for type '{1}' that is a template.", Name, Type));
Typedef = apiTypeInfo;
return;
}
try
{
// Duplicate type
var typedef = (ApiTypeInfo)apiTypeInfo.Clone();
typedef.NativeName = NativeName ?? Name;
typedef.Name = Name;
typedef.Namespace = Namespace;
if (!string.IsNullOrEmpty(Attributes))
{
if (string.IsNullOrEmpty(typedef.Attributes))
typedef.Attributes = Attributes;
else
typedef.Attributes += ',' + Attributes;
}
if (Comment != null && Comment.Length != 0)
typedef.Comment = Comment;
typedef.IsInBuild |= IsInBuild;
typedef.IsDeprecated |= IsDeprecated;
if (typedef is ClassStructInfo typedefClassStruct && typedefClassStruct.IsTemplate)
{
// Inflate template type
typedefClassStruct.IsTemplate = false;
if (typedefClassStruct is ClassInfo typedefClass)
{
foreach (var fieldInfo in typedefClass.Fields)
InflateType(buildData, typedefClassStruct, ref fieldInfo.Type);
}
else if (typedefClassStruct is StructureInfo typedefStruct)
{
foreach (var fieldInfo in typedefStruct.Fields)
InflateType(buildData, typedefStruct, ref fieldInfo.Type);
}
}
// Add to the hierarchy
typedef.Parent = Parent;
Parent.Children.Add(typedef);
typedef.EnsureInited(buildData);
Typedef = typedef;
}
catch (Exception)
{
Log.Error($"Failed to typedef '{Type}' as '{Name}'.");
throw;
}
}
private void InflateType(Builder.BuildData buildData, ClassStructInfo typedef, ref TypeInfo typeInfo)
{
if (BindingsGenerator.CSharpNativeToManagedBasicTypes.ContainsKey(typeInfo.Type))
return;
if (BindingsGenerator.CSharpNativeToManagedDefault.ContainsKey(typeInfo.Type))
return;
// Find API type info for this field type
var apiType = BindingsGenerator.FindApiTypeInfo(buildData, typeInfo, typedef);
if (apiType == null)
{
// TODO: implement more advanced template types inflating with tokenization of the generic definition
typeInfo = Type.GenericArgs[0];
}
}
public override void Write(BinaryWriter writer)
{
writer.Write(IsAlias);
BindingsGenerator.Write(writer, Type);
base.Write(writer);
}
public override void Read(BinaryReader reader)
{
IsAlias = reader.ReadBoolean();
Type = BindingsGenerator.Read(reader, Type);
base.Read(reader);
}
public override string ToString()
{
return "typedef " + Type + " " + Name;
}
}
}

View File

@@ -84,6 +84,7 @@
<Compile Include="Bindings\ModuleInfo.cs" />
<Compile Include="Bindings\PropertyInfo.cs" />
<Compile Include="Bindings\StructureInfo.cs" />
<Compile Include="Bindings\TypedefInfo.cs" />
<Compile Include="Bindings\TypeInfo.cs" />
<Compile Include="Build\Assembler.cs" />
<Compile Include="Build\Builder.cs" />