Files
FlaxEngine/Source/Tools/Flax.Build/Bindings/ClassInfo.cs
2025-12-16 00:15:29 +02:00

222 lines
8.2 KiB
C#

// Copyright (c) Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Flax.Build.Bindings
{
/// <summary>
/// The native class information for bindings generator.
/// </summary>
public class ClassInfo : VirtualClassInfo
{
private static readonly HashSet<string> InBuildScriptingObjectTypes = new HashSet<string>
{
"ScriptingObject",
"ManagedScriptingObject",
"PersistentScriptingObject",
"ScriptingObjectReference",
"AssetReference",
"BinaryAsset",
"SceneObject",
"Asset",
"Script",
"Actor",
};
public bool IsBaseTypeHidden;
public bool IsStatic;
public bool IsSealed;
public bool IsAbstract;
public bool IsAutoSerialization;
public bool NoSpawn;
public bool NoConstructor;
public List<PropertyInfo> Properties = new List<PropertyInfo>();
public List<FieldInfo> Fields = new List<FieldInfo>();
public List<EventInfo> Events = new List<EventInfo>();
private bool _isScriptingObject;
private int _scriptVTableSize = -1;
private int _scriptVTableOffset;
public override bool IsClass => true;
public override bool IsScriptingObject => _isScriptingObject;
public override void Init(Builder.BuildData buildData)
{
base.Init(buildData);
// Internal base types are usually hidden from bindings (used in core-only internally)
IsBaseTypeHidden = BaseTypeInheritance == AccessLevel.Private || BaseType == null;
// Cache if it it Scripting Object type
if (InBuildScriptingObjectTypes.Contains(Name))
_isScriptingObject = true;
else if (BaseType == null)
_isScriptingObject = false;
else if (InBuildScriptingObjectTypes.Contains(BaseType.Name))
_isScriptingObject = true;
else
_isScriptingObject = BaseType != null && BaseType.IsScriptingObject;
if (UniqueFunctionNames == null)
UniqueFunctionNames = new HashSet<string>();
if (BaseType is ClassInfo baseClass)
{
if (baseClass.IsStatic)
throw new Exception(string.Format("Class {0} inherits from the class {1} that is {2}.", FullNameNative, baseClass.FullNameNative, "static"));
if (baseClass.IsSealed)
throw new Exception(string.Format("Class {0} inherits from the class {1} that is {2}.", FullNameNative, baseClass.FullNameNative, "sealed"));
}
foreach (var fieldInfo in Fields)
{
if (fieldInfo.IsHidden)
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(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,
IsConst = true,
},
},
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);
}
}
public override void Write(BinaryWriter writer)
{
// TODO: convert into flags
writer.Write(IsStatic);
writer.Write(IsSealed);
writer.Write(IsAbstract);
writer.Write(IsAutoSerialization);
writer.Write(NoSpawn);
writer.Write(NoConstructor);
BindingsGenerator.Write(writer, Properties);
BindingsGenerator.Write(writer, Fields);
BindingsGenerator.Write(writer, Events);
base.Write(writer);
}
public override void Read(BinaryReader reader)
{
// TODO: convert into flags
IsStatic = reader.ReadBoolean();
IsSealed = reader.ReadBoolean();
IsAbstract = reader.ReadBoolean();
IsAutoSerialization = reader.ReadBoolean();
NoSpawn = reader.ReadBoolean();
NoConstructor = reader.ReadBoolean();
Properties = BindingsGenerator.Read(reader, Properties);
Fields = BindingsGenerator.Read(reader, Fields);
Events = BindingsGenerator.Read(reader, Events);
base.Read(reader);
}
public override int GetScriptVTableSize(out int offset)
{
if (_scriptVTableSize == -1)
{
if (BaseType is ClassInfo baseApiTypeInfo)
{
_scriptVTableOffset = baseApiTypeInfo.GetScriptVTableSize(out _);
}
if (Interfaces != null)
{
foreach (var interfaceInfo in Interfaces)
{
if (interfaceInfo.Access != AccessLevel.Public)
continue;
_scriptVTableOffset += interfaceInfo.GetScriptVTableSize(out _);
}
}
_scriptVTableSize = _scriptVTableOffset + Functions.Count(x => x.IsVirtual);
if (IsSealed)
{
// Skip vtables for sealed classes
_scriptVTableSize = _scriptVTableOffset = 0;
}
}
offset = _scriptVTableOffset;
return _scriptVTableSize;
}
public override int GetScriptVTableOffset(VirtualClassInfo classInfo)
{
if (classInfo == BaseType)
return 0;
if (Interfaces != null)
{
var offset = BaseType is ClassInfo baseApiTypeInfo ? baseApiTypeInfo.GetScriptVTableSize(out _) : 0;
foreach (var interfaceInfo in Interfaces)
{
if (interfaceInfo.Access != AccessLevel.Public)
continue;
if (interfaceInfo == classInfo)
return offset;
offset += interfaceInfo.GetScriptVTableSize(out _);
}
}
throw new Exception($"Cannot get Script VTable offset for {classInfo} that is not part of {this}");
}
public override string ToString()
{
return "class " + Name;
}
}
}