From 85fd540b97403d155d390dccf14f334bd7aa2fbb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 23 May 2022 19:50:37 +0200 Subject: [PATCH] Add new `API_TYPEDEF` metadata for Scriptign API types instantiation (with `Alias` option) --- .../Tools/Flax.Build/Bindings/ApiTypeInfo.cs | 19 ++- .../Bindings/BindingsGenerator.Api.cs | 4 + .../Bindings/BindingsGenerator.Parsing.cs | 72 +++++++-- .../Flax.Build/Bindings/BindingsGenerator.cs | 5 + .../Tools/Flax.Build/Bindings/TypedefInfo.cs | 139 ++++++++++++++++++ Source/Tools/Flax.Build/Flax.Build.csproj | 1 + 6 files changed, 230 insertions(+), 10 deletions(-) create mode 100644 Source/Tools/Flax.Build/Bindings/TypedefInfo.cs 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 @@ +