diff --git a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs
index 14e03e188..ffd5c5948 100644
--- a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs
+++ b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs
@@ -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
{
///
/// The native type information for bindings generator.
///
- public class ApiTypeInfo : IBindingsCache
+ public class ApiTypeInfo : IBindingsCache, ICloneable
{
public ApiTypeInfo Parent;
public List Children = new List();
@@ -136,5 +138,20 @@ namespace Flax.Build.Bindings
{
return Name;
}
+
+ ///
+ 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;
+ }
}
}
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
index b5763faea..5064895e1 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs
@@ -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;
}
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
index 203a932fa..f46e162e9 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs
@@ -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;
+ }
}
}
diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs
index a00efacfe..750cf3922 100644
--- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs
+++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs
@@ -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);
diff --git a/Source/Tools/Flax.Build/Bindings/TypedefInfo.cs b/Source/Tools/Flax.Build/Bindings/TypedefInfo.cs
new file mode 100644
index 000000000..dbfc4038b
--- /dev/null
+++ b/Source/Tools/Flax.Build/Bindings/TypedefInfo.cs
@@ -0,0 +1,139 @@
+// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
+
+using System;
+using System.IO;
+
+namespace Flax.Build.Bindings
+{
+ ///
+ /// The type definition for bindings generator.
+ ///
+ 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;
+ }
+ }
+}
diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj
index ad1e2a1fa..61fda8b97 100644
--- a/Source/Tools/Flax.Build/Flax.Build.csproj
+++ b/Source/Tools/Flax.Build/Flax.Build.csproj
@@ -84,6 +84,7 @@
+