Refactor base types initialization to be done during API processing

This commit is contained in:
Wojtek Figat
2021-05-27 23:34:55 +02:00
parent 2a6f1df76e
commit b3d9ec687d
12 changed files with 185 additions and 210 deletions

View File

@@ -71,6 +71,13 @@ namespace Flax.Build.Bindings
}
}
public void EnsureInited(Builder.BuildData buildData)
{
if (IsInited)
return;
Init(buildData);
}
public virtual void Init(Builder.BuildData buildData)
{
IsInited = true;

View File

@@ -261,7 +261,7 @@ namespace Flax.Build.Bindings
{
if (typeInfo == null)
return null;
var result = FindApiTypeInfoInner(typeInfo, caller);
var result = FindApiTypeInfoInner(buildData, typeInfo, caller);
if (result != null)
return result;
if (buildData.TypeCache.TryGetValue(typeInfo, out result))
@@ -274,7 +274,7 @@ namespace Flax.Build.Bindings
// Find across all loaded modules for this build
foreach (var e in buildData.ModulesInfo)
{
result = FindApiTypeInfoInner(typeInfo, e.Value);
result = FindApiTypeInfoInner(buildData, typeInfo, e.Value);
if (result != null)
{
buildData.TypeCache.Add(typeInfo, result);
@@ -291,7 +291,7 @@ namespace Flax.Build.Bindings
{
if (result == null)
return null;
result = FindApiTypeInfoInner(new TypeInfo { Type = nesting[i], }, result);
result = FindApiTypeInfoInner(buildData, new TypeInfo { Type = nesting[i], }, result);
}
return result;
}
@@ -301,14 +301,17 @@ namespace Flax.Build.Bindings
return null;
}
private static ApiTypeInfo FindApiTypeInfoInner(TypeInfo typeInfo, ApiTypeInfo parent)
private static ApiTypeInfo FindApiTypeInfoInner(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent)
{
foreach (var child in parent.Children)
{
if (child.Name == typeInfo.Type)
{
child.EnsureInited(buildData);
return child;
}
var result = FindApiTypeInfoInner(typeInfo, child);
var result = FindApiTypeInfoInner(buildData, typeInfo, child);
if (result != null)
return result;
}

View File

@@ -14,7 +14,7 @@ namespace Flax.Build.Bindings
private static readonly HashSet<string> CSharpUsedNamespaces = new HashSet<string>();
private static readonly List<string> CSharpUsedNamespacesSorted = new List<string>();
private static readonly Dictionary<string, string> CSharpNativeToManagedBasicTypes = new Dictionary<string, string>()
internal static readonly Dictionary<string, string> CSharpNativeToManagedBasicTypes = new Dictionary<string, string>()
{
// Language types
{ "int8", "sbyte" },
@@ -32,7 +32,7 @@ namespace Flax.Build.Bindings
{ "double", "double" },
};
private static readonly Dictionary<string, string> CSharpNativeToManagedDefault = new Dictionary<string, string>()
internal static readonly Dictionary<string, string> CSharpNativeToManagedDefault = new Dictionary<string, string>()
{
// Engine types
{ "String", "string" },
@@ -527,7 +527,7 @@ namespace Flax.Build.Bindings
contents.Append("abstract ");
contents.Append("unsafe partial class ").Append(classInfo.Name);
if (classInfo.BaseType != null && !classInfo.IsBaseTypeHidden)
contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, classInfo.BaseType, classInfo));
contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = classInfo.BaseType.Name }, classInfo));
contents.AppendLine();
contents.Append(indent + "{");
indent += " ";
@@ -861,7 +861,7 @@ namespace Flax.Build.Bindings
contents.Append("private ");
contents.Append("unsafe partial struct ").Append(structureInfo.Name);
if (structureInfo.BaseType != null && structureInfo.IsPod)
contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, structureInfo.BaseType, structureInfo));
contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = structureInfo.BaseType.Name }, structureInfo));
contents.AppendLine();
contents.Append(indent + "{");
indent += " ";

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 = 7;
private const int CacheVersion = 8;
internal static void Write(BinaryWriter writer, string e)
{

View File

@@ -1095,7 +1095,7 @@ namespace Flax.Build.Bindings
var baseType = classInfo?.BaseType ?? structureInfo?.BaseType;
if (classInfo != null && classInfo.IsBaseTypeHidden)
baseType = null;
if (baseType != null && (baseType.Type == "PersistentScriptingObject" || baseType.Type == "ScriptingObject"))
if (baseType != null && (baseType.Name == "PersistentScriptingObject" || baseType.Name == "ScriptingObject"))
baseType = null;
CppAutoSerializeFields.Clear();
CppAutoSerializeProperties.Clear();
@@ -1105,7 +1105,7 @@ namespace Flax.Build.Bindings
contents.Append($"void {typeNameNative}::Serialize(SerializeStream& stream, const void* otherObj)").AppendLine();
contents.Append('{').AppendLine();
if (baseType != null)
contents.Append($" {baseType}::Serialize(stream, otherObj);").AppendLine();
contents.Append($" {baseType.FullNameNative}::Serialize(stream, otherObj);").AppendLine();
contents.Append($" SERIALIZE_GET_OTHER_OBJ({typeNameNative});").AppendLine();
if (classInfo != null)
@@ -1161,7 +1161,7 @@ namespace Flax.Build.Bindings
contents.Append($"void {typeNameNative}::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)").AppendLine();
contents.Append('{').AppendLine();
if (baseType != null)
contents.Append($" {baseType}::Deserialize(stream, modifier);").AppendLine();
contents.Append($" {baseType.FullNameNative}::Deserialize(stream, modifier);").AppendLine();
foreach (var fieldInfo in CppAutoSerializeFields)
{
@@ -1554,7 +1554,7 @@ namespace Flax.Build.Bindings
else
contents.Append($"(ScriptingType::SpawnHandler)&{classTypeNameNative}::Spawn, ");
if (classInfo.BaseType != null && useScripting)
contents.Append($"&{classInfo.BaseType}::TypeInitializer, ");
contents.Append($"&{classInfo.BaseType.FullNameNative}::TypeInitializer, ");
else
contents.Append("nullptr, ");
contents.Append(setupScriptVTable);
@@ -1566,7 +1566,7 @@ namespace Flax.Build.Bindings
else
contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor, ");
if (classInfo.BaseType != null)
contents.Append($"&{classInfo.BaseType}::TypeInitializer");
contents.Append($"&{classInfo.BaseType.FullNameNative}::TypeInitializer");
else
contents.Append("nullptr");
}

View File

@@ -416,38 +416,38 @@ namespace Flax.Build.Bindings
token = accessToken;
break;
}
var baseTypeInfo = new TypeInfo
var inheritType = new TypeInfo
{
Type = token.Value,
};
if (token.Value.Length > 2 && token.Value[0] == 'I' && char.IsUpper(token.Value[1]))
{
// Interface
if (desc.InterfaceNames == null)
desc.InterfaceNames = new List<TypeInfo>();
desc.InterfaceNames.Add(baseTypeInfo);
token = context.Tokenizer.NextToken();
continue;
}
if (desc.BaseType != null)
{
// Allow for multiple base classes, just the first one needs to be a valid base type
break;
throw new Exception($"Invalid '{desc.Name}' inheritance (only single base class is allowed for scripting types, excluding interfaces).");
}
desc.BaseType = baseTypeInfo;
if (desc.Inheritance == null)
desc.Inheritance = new List<TypeInfo>();
desc.Inheritance.Add(inheritType);
token = context.Tokenizer.NextToken();
if (token.Type == TokenType.LeftCurlyBrace)
{
break;
}
if (token.Type == TokenType.Colon)
{
token = context.Tokenizer.ExpectToken(TokenType.Colon);
token = context.Tokenizer.NextToken();
inheritType.Type = token.Value;
token = context.Tokenizer.NextToken();
continue;
}
if (token.Type == TokenType.DoubleColon)
{
token = context.Tokenizer.NextToken();
inheritType.Type += token.Value;
token = context.Tokenizer.NextToken();
continue;
}
if (token.Type == TokenType.LeftAngleBracket)
{
var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier);
token = context.Tokenizer.ExpectToken(TokenType.RightAngleBracket);
desc.BaseType.GenericArgs = new List<TypeInfo>
inheritType.GenericArgs = new List<TypeInfo>
{
new TypeInfo
{
@@ -456,9 +456,9 @@ namespace Flax.Build.Bindings
};
// TODO: find better way to resolve this (custom base type attribute?)
if (desc.BaseType.Type == "ShaderAssetTypeBase")
if (inheritType.Type == "ShaderAssetTypeBase")
{
desc.BaseType = desc.BaseType.GenericArgs[0];
desc.Inheritance[desc.Inheritance.Count - 1] = inheritType.GenericArgs[0];
}
token = context.Tokenizer.NextToken();

View File

@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Flax.Build.NativeCpp;
@@ -109,13 +108,6 @@ namespace Flax.Build.Bindings
if (LoadCache(ref moduleInfo, moduleOptions, headerFiles))
{
buildData.ModulesInfo[module] = moduleInfo;
// Initialize API
using (new ProfileEventScope("Init"))
{
moduleInfo.Init(buildData);
}
return moduleInfo;
}
}
@@ -164,12 +156,6 @@ namespace Flax.Build.Bindings
}
}
// Initialize API
using (new ProfileEventScope("Init"))
{
moduleInfo.Init(buildData);
}
return moduleInfo;
}
@@ -533,23 +519,10 @@ namespace Flax.Build.Bindings
if (moduleInfo.IsFromCache)
return;
// Process parsed API
using (new ProfileEventScope("Process"))
// Initialize parsed API
using (new ProfileEventScope("Init"))
{
foreach (var child in moduleInfo.Children)
{
try
{
foreach (var apiTypeInfo in child.Children)
ProcessAndValidate(buildData, apiTypeInfo);
}
catch (Exception)
{
if (child is FileInfo fileInfo)
Log.Error($"Failed to validate '{fileInfo.Name}' file to generate bindings.");
throw;
}
}
moduleInfo.Init(buildData);
}
// Generate bindings for scripting
@@ -583,122 +556,5 @@ namespace Flax.Build.Bindings
}
}
}
private static void ProcessAndValidate(BuildData buildData, ApiTypeInfo apiTypeInfo)
{
if (apiTypeInfo is ClassInfo classInfo)
ProcessAndValidate(buildData, classInfo);
else if (apiTypeInfo is StructureInfo structureInfo)
ProcessAndValidate(buildData, structureInfo);
foreach (var child in apiTypeInfo.Children)
ProcessAndValidate(buildData, child);
}
private static void ProcessAndValidate(BuildData buildData, ClassInfo classInfo)
{
if (classInfo.UniqueFunctionNames == null)
classInfo.UniqueFunctionNames = new HashSet<string>();
foreach (var fieldInfo in classInfo.Fields)
{
if (fieldInfo.Access == AccessLevel.Private)
continue;
fieldInfo.Getter = new FunctionInfo
{
Name = "Get" + fieldInfo.Name,
Comment = fieldInfo.Comment,
IsStatic = fieldInfo.IsStatic,
Access = fieldInfo.Access,
Attributes = fieldInfo.Attributes,
ReturnType = fieldInfo.Type,
Parameters = new List<FunctionInfo.ParameterInfo>(),
IsVirtual = false,
IsConst = true,
Glue = new FunctionInfo.GlueInfo()
};
ProcessAndValidate(classInfo, fieldInfo.Getter);
fieldInfo.Getter.Name = fieldInfo.Name;
if (!fieldInfo.IsReadOnly)
{
fieldInfo.Setter = new FunctionInfo
{
Name = "Set" + fieldInfo.Name,
Comment = fieldInfo.Comment,
IsStatic = fieldInfo.IsStatic,
Access = fieldInfo.Access,
Attributes = fieldInfo.Attributes,
ReturnType = new TypeInfo
{
Type = "void",
},
Parameters = new List<FunctionInfo.ParameterInfo>
{
new FunctionInfo.ParameterInfo
{
Name = "value",
Type = fieldInfo.Type,
},
},
IsVirtual = false,
IsConst = true,
Glue = new FunctionInfo.GlueInfo()
};
ProcessAndValidate(classInfo, fieldInfo.Setter);
fieldInfo.Setter.Name = fieldInfo.Name;
}
}
foreach (var propertyInfo in classInfo.Properties)
{
if (propertyInfo.Getter != null)
ProcessAndValidate(classInfo, propertyInfo.Getter);
if (propertyInfo.Setter != null)
ProcessAndValidate(classInfo, propertyInfo.Setter);
}
foreach (var functionInfo in classInfo.Functions)
ProcessAndValidate(classInfo, functionInfo);
}
private static void ProcessAndValidate(BuildData buildData, StructureInfo structureInfo)
{
foreach (var fieldInfo in structureInfo.Fields)
{
if (fieldInfo.Type.IsBitField)
throw new NotImplementedException($"TODO: support bit-fields in structure fields (found field {fieldInfo} in structure {structureInfo.Name})");
// Pointers are fine
if (fieldInfo.Type.IsPtr)
continue;
// In-build types
if (CSharpNativeToManagedBasicTypes.ContainsKey(fieldInfo.Type.Type))
continue;
if (CSharpNativeToManagedDefault.ContainsKey(fieldInfo.Type.Type))
continue;
// Find API type info for this field type
var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo);
if (apiType != null)
continue;
throw new Exception($"Unknown field type '{fieldInfo.Type} {fieldInfo.Name}' in structure '{structureInfo.Name}'.");
}
}
private static void ProcessAndValidate(ClassInfo classInfo, FunctionInfo functionInfo)
{
// Ensure that methods have unique names for bindings
if (classInfo.UniqueFunctionNames == null)
classInfo.UniqueFunctionNames = new HashSet<string>();
int idx = 1;
functionInfo.UniqueName = functionInfo.Name;
while (classInfo.UniqueFunctionNames.Contains(functionInfo.UniqueName))
functionInfo.UniqueName = functionInfo.Name + idx++;
classInfo.UniqueFunctionNames.Add(functionInfo.UniqueName);
}
}
}

View File

@@ -57,22 +57,87 @@ namespace Flax.Build.Bindings
_isScriptingObject = true;
else if (BaseType == null)
_isScriptingObject = false;
else if (InBuildScriptingObjectTypes.Contains(BaseType.Type))
else if (InBuildScriptingObjectTypes.Contains(BaseType.Name))
_isScriptingObject = true;
else
_isScriptingObject = BaseType != null && BaseType.IsScriptingObject;
if (UniqueFunctionNames == null)
UniqueFunctionNames = new HashSet<string>();
foreach (var fieldInfo in Fields)
{
var baseApiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, BaseType, this);
if (baseApiTypeInfo != null)
if (fieldInfo.Access == AccessLevel.Private)
continue;
fieldInfo.Getter = new FunctionInfo
{
if (!baseApiTypeInfo.IsInited)
baseApiTypeInfo.Init(buildData);
_isScriptingObject = baseApiTypeInfo.IsScriptingObject;
}
else
Name = "Get" + fieldInfo.Name,
Comment = fieldInfo.Comment,
IsStatic = fieldInfo.IsStatic,
Access = fieldInfo.Access,
Attributes = fieldInfo.Attributes,
ReturnType = fieldInfo.Type,
Parameters = new List<FunctionInfo.ParameterInfo>(),
IsVirtual = false,
IsConst = true,
Glue = new FunctionInfo.GlueInfo()
};
ProcessAndValidate(fieldInfo.Getter);
fieldInfo.Getter.Name = fieldInfo.Name;
if (!fieldInfo.IsReadOnly)
{
_isScriptingObject = false;
fieldInfo.Setter = new FunctionInfo
{
Name = "Set" + fieldInfo.Name,
Comment = fieldInfo.Comment,
IsStatic = fieldInfo.IsStatic,
Access = fieldInfo.Access,
Attributes = fieldInfo.Attributes,
ReturnType = new TypeInfo
{
Type = "void",
},
Parameters = new List<FunctionInfo.ParameterInfo>
{
new FunctionInfo.ParameterInfo
{
Name = "value",
Type = fieldInfo.Type,
},
},
IsVirtual = false,
IsConst = true,
Glue = new FunctionInfo.GlueInfo()
};
ProcessAndValidate(fieldInfo.Setter);
fieldInfo.Setter.Name = fieldInfo.Name;
}
}
foreach (var propertyInfo in Properties)
{
if (propertyInfo.Getter != null)
ProcessAndValidate(propertyInfo.Getter);
if (propertyInfo.Setter != null)
ProcessAndValidate(propertyInfo.Setter);
}
foreach (var functionInfo in Functions)
ProcessAndValidate(functionInfo);
}
private void ProcessAndValidate(FunctionInfo functionInfo)
{
// Ensure that methods have unique names for bindings
if (UniqueFunctionNames == null)
UniqueFunctionNames = new HashSet<string>();
int idx = 1;
functionInfo.UniqueName = functionInfo.Name;
while (UniqueFunctionNames.Contains(functionInfo.UniqueName))
functionInfo.UniqueName = functionInfo.Name + idx++;
UniqueFunctionNames.Add(functionInfo.UniqueName);
}
public override void Write(BinaryWriter writer)
@@ -113,7 +178,7 @@ namespace Flax.Build.Bindings
{
if (_scriptVTableSize == -1)
{
if (BindingsGenerator.FindApiTypeInfo(buildData, BaseType, this) is ClassInfo baseApiTypeInfo)
if (BaseType is ClassInfo baseApiTypeInfo)
{
_scriptVTableOffset = baseApiTypeInfo.GetScriptVTableSize(buildData, out _);
}

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
@@ -12,29 +13,37 @@ namespace Flax.Build.Bindings
{
public AccessLevel Access;
public AccessLevel BaseTypeInheritance;
public TypeInfo BaseType;
public List<InterfaceInfo> Interfaces; // Optional
public List<TypeInfo> InterfaceNames; // Optional
public ClassStructInfo BaseType;
public List<InterfaceInfo> Interfaces;
public List<TypeInfo> Inheritance; // Data from parsing, used to interfaces and base type construct in Init
public override void Init(Builder.BuildData buildData)
{
base.Init(buildData);
if (Interfaces == null && InterfaceNames != null && InterfaceNames.Count != 0)
if (BaseType == null && Interfaces == null && Inheritance != null)
{
Interfaces = new List<InterfaceInfo>();
for (var i = 0; i < InterfaceNames.Count; i++)
// Extract base class and interfaces from inheritance info
for (int i = 0; i < Inheritance.Count; i++)
{
var interfaceName = InterfaceNames[i];
var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, interfaceName, this);
var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, Inheritance[i], Parent);
if (apiTypeInfo is InterfaceInfo interfaceInfo)
{
if (Interfaces == null)
Interfaces = new List<InterfaceInfo>();
Interfaces.Add(interfaceInfo);
}
else if (apiTypeInfo is ClassStructInfo otherInfo)
{
if (otherInfo == this)
throw new Exception($"Type '{Name}' inherits from itself.");
if (BaseType != null)
throw new Exception($"Invalid '{Name}' inheritance (only single base class is allowed for scripting types, excluding interfaces).");
BaseType = otherInfo;
}
}
if (Interfaces.Count == 0)
Interfaces = null;
}
BaseType?.EnsureInited(buildData);
}
public override void Write(BinaryWriter writer)
@@ -42,7 +51,7 @@ namespace Flax.Build.Bindings
writer.Write((byte)Access);
writer.Write((byte)BaseTypeInheritance);
BindingsGenerator.Write(writer, BaseType);
BindingsGenerator.Write(writer, InterfaceNames);
BindingsGenerator.Write(writer, Inheritance);
base.Write(writer);
}
@@ -52,7 +61,7 @@ namespace Flax.Build.Bindings
Access = (AccessLevel)reader.ReadByte();
BaseTypeInheritance = (AccessLevel)reader.ReadByte();
BaseType = BindingsGenerator.Read(reader, BaseType);
InterfaceNames = BindingsGenerator.Read(reader, InterfaceNames);
Inheritance = BindingsGenerator.Read(reader, Inheritance);
base.Read(reader);
}

View File

@@ -17,6 +17,19 @@ namespace Flax.Build.Bindings
base.AddChild(apiTypeInfo);
}
public override void Init(Builder.BuildData buildData)
{
try
{
base.Init(buildData);
}
catch (Exception)
{
Log.Error($"Failed to init '{Name}' file scripting API.");
throw;
}
}
public int CompareTo(FileInfo other)
{
return Name.CompareTo(other.Name);

View File

@@ -26,14 +26,14 @@ namespace Flax.Build.Bindings
{
base.Init(buildData);
if (ForceNoPod || (InterfaceNames != null && InterfaceNames.Count != 0))
if (ForceNoPod || (Interfaces != null && Interfaces.Count != 0))
{
_isPod = false;
return;
}
// Structure is POD (plain old data) only if all of it's fields are (and has no base type ro base type is also POD)
_isPod = BaseType == null || (BindingsGenerator.FindApiTypeInfo(buildData, BaseType, Parent)?.IsPod ?? false);
_isPod = BaseType == null || (BaseType?.IsPod ?? false);
for (int i = 0; _isPod && i < Fields.Count; i++)
{
var field = Fields[i];
@@ -42,6 +42,29 @@ namespace Flax.Build.Bindings
_isPod = false;
}
}
foreach (var fieldInfo in Fields)
{
if (fieldInfo.Type.IsBitField)
throw new NotImplementedException($"TODO: support bit-fields in structure fields (found field {fieldInfo} in structure {Name})");
// Pointers are fine
if (fieldInfo.Type.IsPtr)
continue;
// In-build types
if (BindingsGenerator.CSharpNativeToManagedBasicTypes.ContainsKey(fieldInfo.Type.Type))
continue;
if (BindingsGenerator.CSharpNativeToManagedDefault.ContainsKey(fieldInfo.Type.Type))
continue;
// Find API type info for this field type
var apiType = BindingsGenerator.FindApiTypeInfo(buildData, fieldInfo.Type, this);
if (apiType != null)
continue;
throw new Exception($"Unknown field type '{fieldInfo.Type} {fieldInfo.Name}' in structure '{Name}'.");
}
}
public override void Write(BinaryWriter writer)

View File

@@ -36,8 +36,7 @@ namespace Flax.Build.Bindings
var apiType = BindingsGenerator.FindApiTypeInfo(buildData, this, caller);
if (apiType != null)
{
if (!apiType.IsInited)
apiType.Init(buildData);
apiType.EnsureInited(buildData);
return apiType.IsPod;
}