// (c) 2012-2020 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using BuildData = Flax.Build.Builder.BuildData; namespace Flax.Build.Bindings { partial class BindingsGenerator { private static readonly HashSet CSharpUsedNamespaces = new HashSet(); private static readonly List CSharpUsedNamespacesSorted = new List(); internal static readonly Dictionary CSharpNativeToManagedBasicTypes = new Dictionary() { // Language types { "int8", "sbyte" }, { "int16", "short" }, { "int32", "int" }, { "int64", "long" }, { "uint8", "byte" }, { "byte", "byte" }, { "uint16", "ushort" }, { "uint32", "uint" }, { "uint64", "ulong" }, { "char", "sbyte" }, { "Char", "char" }, { "float", "float" }, { "double", "double" }, }; internal static readonly Dictionary CSharpNativeToManagedDefault = new Dictionary() { // Engine types { "String", "string" }, { "StringView", "string" }, { "StringAnsi", "string" }, { "StringAnsiView", "string" }, { "ScriptingObject", "FlaxEngine.Object" }, { "PersistentScriptingObject", "FlaxEngine.Object" }, { "ManagedScriptingObject", "FlaxEngine.Object" }, { "MClass", "System.Type" }, { "Variant", "object" }, { "VariantType", "System.Type" }, // Mono types { "MonoObject", "object" }, { "MonoReflectionType", "System.Type" }, { "MonoType", "IntPtr" }, { "MonoArray", "Array" }, }; private static string GenerateCSharpDefaultValueNativeToManaged(BuildData buildData, string value, ApiTypeInfo caller, bool attribute = false) { if (string.IsNullOrEmpty(value)) return null; // Special case for Engine TEXT macro if (value.StartsWith("TEXT(\"") && value.EndsWith("\")")) return value.Substring(5, value.Length - 6); // In-built constants switch (value) { case "nullptr": case "NULL": case "String::Empty": case "StringView::Empty": return "null"; case "MAX_int8": return "sbyte.MaxValue"; case "MAX_uint8": return "byte.MaxValue"; case "MAX_int16": return "short.MaxValue"; case "MAX_uint16": return "ushort.MaxValue"; case "MAX_int32": return "int.MaxValue"; case "MAX_uint32": return "uint.MaxValue"; case "MAX_int64": return "long.MaxValue"; case "MAX_uint64": return "ulong.MaxValue"; case "MAX_float": return "float.MaxValue"; case "MAX_double": return "double.MaxValue"; case "true": case "false": return value; } // Numbers if (float.TryParse(value, out _) || (value[value.Length - 1] == 'f' && float.TryParse(value.Substring(0, value.Length - 1), out _))) return value; value = value.Replace("::", "."); var dot = value.LastIndexOf('.'); ApiTypeInfo apiType = null; if (dot != -1) { var type = new TypeInfo { Type = value.Substring(0, dot) }; apiType = FindApiTypeInfo(buildData, type, caller); } if (attribute) { // Value constructors (eg. Vector2(1, 2)) if (value.Contains('(') && value.Contains(')')) { // Support for in-built types if (value.StartsWith("Vector2(")) return $"typeof(Vector2), \"{value.Substring(8, value.Length - 9).Replace("f", "")}\""; if (value.StartsWith("Vector3(")) return $"typeof(Vector3), \"{value.Substring(8, value.Length - 9).Replace("f", "")}\""; if (value.StartsWith("Vector4(")) return $"typeof(Vector4), \"{value.Substring(8, value.Length - 9).Replace("f", "")}\""; return null; } // Constants (eg. Vector2::Zero) if (value.Contains('.')) { // Support for in-built constants switch (value) { case "Vector2.Zero": return "typeof(Vector2), \"0,0\""; case "Vector2.One": return "typeof(Vector2), \"1,1\""; case "Vector3.Zero": return "typeof(Vector3), \"0,0,0\""; case "Vector3.One": return "typeof(Vector3), \"1,1,1\""; case "Vector4.Zero": return "typeof(Vector4), \"0,0,0,0\""; case "Vector4.One": return "typeof(Vector4), \"1,1,1,1\""; case "Quaternion.Identity": return "typeof(Quaternion), \"0,0,0,1\""; } // Enums if (apiType != null && apiType.IsEnum) return value; return null; } } // Skip constants unsupported in C# if (apiType != null && apiType.IsStruct) return null; // Special case for value constructors if (value.Contains('(') && value.Contains(')')) return "new " + value; return value; } private static string GenerateCSharpNativeToManaged(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller) { string result; if (typeInfo?.Type == null) throw new ArgumentNullException(); // Use dynamic array as wrapper container for fixed-size native arrays if (typeInfo.IsArray) { typeInfo.IsArray = false; result = GenerateCSharpNativeToManaged(buildData, typeInfo, caller); typeInfo.IsArray = true; return result + "[]"; } // Special case for bit-fields if (typeInfo.IsBitField) { if (typeInfo.BitSize == 1) return "bool"; throw new NotImplementedException("TODO: support bit-fields with more than 1 bit."); } // In-build basic if (CSharpNativeToManagedBasicTypes.TryGetValue(typeInfo.Type, out result)) { if (typeInfo.IsPtr) return result + '*'; return result; } // In-build default if (CSharpNativeToManagedDefault.TryGetValue(typeInfo.Type, out result)) { return result; } // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return typeInfo.GenericArgs[0].Type.Replace("::", "."); // Array or Span if ((typeInfo.Type == "Array" || typeInfo.Type == "Span") && typeInfo.GenericArgs != null) return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller) + "[]"; // Dictionary if (typeInfo.Type == "Dictionary" && typeInfo.GenericArgs != null) return string.Format("System.Collections.Generic.Dictionary<{0}, {1}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller), GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[1], caller)); // HashSet if (typeInfo.Type == "HashSet" && typeInfo.GenericArgs != null) return string.Format("System.Collections.Generic.HashSet<{0}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller)); // BitArray if (typeInfo.Type == "BitArray" && typeInfo.GenericArgs != null) return "bool[]"; // BytesContainer if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null) return "byte[]"; // Function if (typeInfo.Type == "Function" && typeInfo.GenericArgs != null) { if (typeInfo.GenericArgs.Count == 0) throw new Exception("Missing function return type."); if (typeInfo.GenericArgs.Count > 1) { var args = string.Empty; args += GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[1], caller); for (int i = 2; i < typeInfo.GenericArgs.Count; i++) args += ", " + GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[i], caller); if (typeInfo.GenericArgs[0].Type == "void") return string.Format("Action<{0}>", args); return string.Format("Func<{0}, {1}>", args, GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller)); } if (typeInfo.GenericArgs[0].Type == "void") return "Action"; return string.Format("Func<{0}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller)); } // Find API type info var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) { CSharpUsedNamespaces.Add(apiType.Namespace); if (apiType.IsScriptingObject) return typeInfo.Type.Replace("::", "."); if (typeInfo.IsPtr && apiType.IsPod) return typeInfo.Type.Replace("::", ".") + '*'; } // Pointer if (typeInfo.IsPtr) return "IntPtr"; return typeInfo.Type.Replace("::", "."); } private static string GenerateCSharpManagedToNativeType(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller) { // Fixed-size array if (typeInfo.IsArray) return GenerateCSharpNativeToManaged(buildData, typeInfo, caller); // Find API type info var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) { if (apiType.IsScriptingObject) return "IntPtr"; } // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "IntPtr"; // Function if (typeInfo.Type == "Function" && typeInfo.GenericArgs != null) return "IntPtr"; return GenerateCSharpNativeToManaged(buildData, typeInfo, caller); } private static string GenerateCSharpManagedToNativeConverter(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller) { // Use dynamic array as wrapper container for fixed-size native arrays if (typeInfo.IsArray) return string.Empty; // Special case for bit-fields if (typeInfo.IsBitField) { if (typeInfo.BitSize == 1) return string.Empty; throw new NotImplementedException("TODO: support bit-fields with more than 1 bit."); } switch (typeInfo.Type) { case "String": case "StringView": case "StringAnsi": case "StringAnsiView": // String return string.Empty; case "ScriptingObject": case "PersistentScriptingObject": case "ManagedScriptingObject": // object return "FlaxEngine.Object.GetUnmanagedPtr"; case "Function": // delegate return "Marshal.GetFunctionPointerForDelegate"; default: var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) { // Scripting Object if (apiType.IsScriptingObject) return "FlaxEngine.Object.GetUnmanagedPtr"; } // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "FlaxEngine.Object.GetUnmanagedPtr"; // Default return string.Empty; } } private static void GenerateCSharpWrapperFunction(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo caller, FunctionInfo functionInfo) { string returnValueType; if (UsePassByReference(buildData, functionInfo.ReturnType, caller)) { returnValueType = "void"; } else { returnValueType = GenerateCSharpNativeToManaged(buildData, functionInfo.ReturnType, caller); } contents.AppendLine().Append(indent).Append("[MethodImpl(MethodImplOptions.InternalCall)]"); contents.AppendLine().Append(indent).Append("internal static extern "); contents.Append(returnValueType).Append(" Internal_").Append(functionInfo.UniqueName).Append('('); var separator = false; if (!functionInfo.IsStatic) { contents.Append("IntPtr obj"); separator = true; } foreach (var parameterInfo in functionInfo.Parameters) { if (separator) contents.Append(", "); separator = true; var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller); if (parameterInfo.IsOut) contents.Append("out "); else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) contents.Append("ref "); contents.Append(nativeType); contents.Append(' '); contents.Append(parameterInfo.Name); } foreach (var parameterInfo in functionInfo.Glue.CustomParameters) { if (separator) contents.Append(", "); separator = true; var nativeType = GenerateCSharpManagedToNativeType(buildData, parameterInfo.Type, caller); if (parameterInfo.IsOut) contents.Append("out "); else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) contents.Append("ref "); contents.Append(nativeType); contents.Append(' '); contents.Append(parameterInfo.Name); } contents.Append(')'); contents.Append(';'); contents.AppendLine(); } private static void GenerateCSharpWrapperFunctionCall(BuildData buildData, StringBuilder contents, ApiTypeInfo caller, FunctionInfo functionInfo, bool isSetter = false) { if (functionInfo.Glue.UseReferenceForResult) { } else if (!functionInfo.ReturnType.IsVoid) { contents.Append("return "); } contents.Append("Internal_").Append(functionInfo.UniqueName).Append('('); var separator = false; if (!functionInfo.IsStatic) { contents.Append("__unmanagedPtr"); separator = true; } for (var i = 0; i < functionInfo.Parameters.Count; i++) { var parameterInfo = functionInfo.Parameters[i]; if (separator) contents.Append(", "); separator = true; var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller); if (string.IsNullOrWhiteSpace(convertFunc)) { if (parameterInfo.IsOut) contents.Append("out "); else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) contents.Append("ref "); // Pass value contents.Append(isSetter ? "value" : parameterInfo.Name); } else { if (parameterInfo.IsOut) throw new Exception($"Cannot use Out meta on parameter {parameterInfo} in function {functionInfo.Name} in {caller}."); if (parameterInfo.IsRef) throw new Exception($"Cannot use Ref meta on parameter {parameterInfo} in function {functionInfo.Name} in {caller}."); // Convert value contents.Append(convertFunc); contents.Append('('); contents.Append(isSetter ? "value" : parameterInfo.Name); contents.Append(')'); } } foreach (var parameterInfo in functionInfo.Glue.CustomParameters) { if (separator) contents.Append(", "); separator = true; var convertFunc = GenerateCSharpManagedToNativeConverter(buildData, parameterInfo.Type, caller); if (string.IsNullOrWhiteSpace(convertFunc)) { if (parameterInfo.IsOut) contents.Append("out "); else if (parameterInfo.IsRef || UsePassByReference(buildData, parameterInfo.Type, caller)) contents.Append("ref "); // Pass value contents.Append(parameterInfo.DefaultValue); } else { // Convert value contents.Append(convertFunc); contents.Append('('); contents.Append(parameterInfo.DefaultValue); contents.Append(')'); } } contents.Append(");"); if (functionInfo.Glue.UseReferenceForResult) { contents.Append(" return resultAsRef;"); } } private static void GenerateCSharpComment(StringBuilder contents, string indent, string[] comment) { foreach (var c in comment) { contents.Append(indent).Append(c.Replace("::", ".")).AppendLine(); } } private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, string attributes, string[] comment, bool canUseTooltip, bool useUnmanaged, string defaultValue = null) { var writeTooltip = true; var writeDefaultValue = true; if (!string.IsNullOrEmpty(attributes)) { // Write attributes contents.Append(indent).Append('[').Append(attributes).Append(']').AppendLine(); writeTooltip = !attributes.Contains("Tooltip(") && !attributes.Contains("HideInEditor"); writeDefaultValue = !attributes.Contains("DefaultValue("); } if (useUnmanaged) { // Write attribute for C++ calling code contents.Append(indent).AppendLine("[Unmanaged]"); } if (canUseTooltip && comment != null && writeTooltip && buildData.Configuration != TargetConfiguration.Release) { // Write documentation comment as tooltip if (comment.Length >= 3 && comment[0] == "/// " && comment[1].StartsWith("/// ") && comment[comment.Length - 1] == "/// ") { var tooltip = comment[1].Substring(4); if (tooltip.StartsWith("Gets the ")) tooltip = "The " + tooltip.Substring(9); for (int i = 3; i < comment.Length; i++) { if (comment[i - 1].StartsWith("/// ")) tooltip += " " + comment[i - 1].Substring(4); } tooltip = tooltip.Replace("\"", "\"\""); contents.Append(indent).Append("[Tooltip(@\"").Append(tooltip).Append("\")]").AppendLine(); } } if (writeDefaultValue) { // Write default value attribute defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, defaultValue, apiTypeInfo, true); if (defaultValue != null) contents.Append(indent).Append("[DefaultValue(").Append(defaultValue).Append(")]").AppendLine(); } } private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, bool useUnmanaged, string defaultValue = null) { GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo, apiTypeInfo.Attributes, apiTypeInfo.Comment, true, useUnmanaged, defaultValue); } private static void GenerateCSharpAttributes(BuildData buildData, StringBuilder contents, string indent, ApiTypeInfo apiTypeInfo, MemberInfo memberInfo, bool useUnmanaged, string defaultValue = null) { GenerateCSharpAttributes(buildData, contents, indent, apiTypeInfo, memberInfo.Attributes, memberInfo.Comment, true, useUnmanaged, defaultValue); } private static void GenerateCSharpClass(BuildData buildData, StringBuilder contents, string indent, ClassInfo classInfo) { var useUnmanaged = classInfo.IsStatic || classInfo.IsScriptingObject; contents.AppendLine(); // Namespace begin if (!string.IsNullOrEmpty(classInfo.Namespace)) { contents.AppendFormat("namespace "); contents.AppendLine(classInfo.Namespace); contents.AppendLine("{"); indent += " "; } // Class docs GenerateCSharpComment(contents, indent, classInfo.Comment); // Class begin GenerateCSharpAttributes(buildData, contents, indent, classInfo, useUnmanaged); contents.Append(indent); if (classInfo.Access == AccessLevel.Public) contents.Append("public "); else if (classInfo.Access == AccessLevel.Protected) contents.Append("protected "); else if (classInfo.Access == AccessLevel.Private) contents.Append("private "); if (classInfo.IsStatic) contents.Append("static "); else if (classInfo.IsSealed) contents.Append("sealed "); else if (classInfo.IsAbstract) contents.Append("abstract "); contents.Append("unsafe partial class ").Append(classInfo.Name); if (classInfo.BaseType != null && !classInfo.IsBaseTypeHidden) contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = classInfo.BaseType.Name }, classInfo)); contents.AppendLine(); contents.Append(indent + "{"); indent += " "; // Constructor for managed objects if (!classInfo.IsStatic && !classInfo.NoConstructor) { contents.AppendLine(); contents.Append(indent).AppendLine("/// "); contents.Append(indent).Append("/// Initializes a new instance of the ."); contents.Append(indent).AppendLine("/// "); contents.Append(indent).Append(classInfo.IsAbstract ? "protected " : "public ").Append(classInfo.Name).AppendLine("() : base()"); contents.Append(indent + "{").AppendLine(); contents.Append(indent + "}").AppendLine(); } // Events foreach (var eventInfo in classInfo.Events) { if (!useUnmanaged) throw new NotImplementedException("TODO: support events inside non-static and non-scripting API class types."); var paramsCount = eventInfo.Type.GenericArgs?.Count ?? 0; contents.AppendLine(); var useCustomDelegateSignature = false; for (var i = 0; i < paramsCount; i++) { var paramType = eventInfo.Type.GenericArgs[i]; var result = GenerateCSharpNativeToManaged(buildData, paramType, classInfo); if ((paramType.IsRef && !paramType.IsConst && paramType.IsPod(buildData, classInfo)) || result[result.Length - 1] == '*') useCustomDelegateSignature = true; CppParamsWrappersCache[i] = result; } string eventSignature; if (useCustomDelegateSignature) { contents.Append(indent).Append($"/// The delegate for event {eventInfo.Name}.").AppendLine(); contents.Append(indent).Append("public delegate void ").Append(eventInfo.Name).Append("Delegate("); for (var i = 0; i < paramsCount; i++) { var paramType = eventInfo.Type.GenericArgs[i]; if (i != 0) contents.Append(", "); if (paramType.IsRef && !paramType.IsConst && paramType.IsPod(buildData, classInfo)) contents.Append("ref "); contents.Append(CppParamsWrappersCache[i]).Append(" arg").Append(i); } contents.Append(");").AppendLine().AppendLine(); eventSignature = "event " + eventInfo.Name + "Delegate"; } else { eventSignature = "event Action"; if (paramsCount != 0) { eventSignature += '<'; for (var i = 0; i < paramsCount; i++) { if (i != 0) eventSignature += ", "; CppParamsWrappersCache[i] = GenerateCSharpNativeToManaged(buildData, eventInfo.Type.GenericArgs[i], classInfo); eventSignature += CppParamsWrappersCache[i]; } eventSignature += '>'; } } foreach (var comment in eventInfo.Comment) { if (comment.Contains("/// ")) continue; contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine(); } GenerateCSharpAttributes(buildData, contents, indent, classInfo, eventInfo, useUnmanaged); contents.Append(indent); if (eventInfo.Access == AccessLevel.Public) contents.Append("public "); else if (eventInfo.Access == AccessLevel.Protected) contents.Append("protected "); else if (eventInfo.Access == AccessLevel.Private) contents.Append("private "); if (eventInfo.IsStatic) contents.Append("static "); contents.Append(eventSignature); contents.Append(' ').AppendLine(eventInfo.Name); contents.Append(indent).Append('{').AppendLine(); indent += " "; var eventInstance = eventInfo.IsStatic ? string.Empty : "__unmanagedPtr, "; contents.Append(indent).Append($"add {{ Internal_{eventInfo.Name} += value; if (Internal_{eventInfo.Name}_Count++ == 0) Internal_{eventInfo.Name}_Bind({eventInstance}true); }}").AppendLine(); contents.Append(indent).Append($"remove {{ Internal_{eventInfo.Name} -= value; if (--Internal_{eventInfo.Name}_Count == 0) Internal_{eventInfo.Name}_Bind({eventInstance}false); }}").AppendLine(); indent = indent.Substring(0, indent.Length - 4); contents.Append(indent).Append('}').AppendLine(); contents.AppendLine(); contents.Append(indent).Append("private "); if (eventInfo.IsStatic) contents.Append("static "); contents.Append($"int Internal_{eventInfo.Name}_Count;"); contents.AppendLine(); contents.Append("#pragma warning disable 67").AppendLine(); contents.Append(indent).Append("private "); if (eventInfo.IsStatic) contents.Append("static "); contents.Append($"{eventSignature} Internal_{eventInfo.Name};"); contents.AppendLine(); contents.AppendLine(); contents.Append(indent).Append("internal "); if (eventInfo.IsStatic) contents.Append("static "); contents.Append($"void Internal_{eventInfo.Name}_Invoke("); for (var i = 0; i < paramsCount; i++) { var paramType = eventInfo.Type.GenericArgs[i]; if (i != 0) contents.Append(", "); contents.Append(CppParamsWrappersCache[i]); if (paramType.IsRef && !paramType.IsConst && paramType.IsPod(buildData, classInfo)) contents.Append("*"); contents.Append(" arg").Append(i); } contents.Append(')').AppendLine(); contents.Append(indent).Append('{').AppendLine(); contents.Append(indent).Append(" Internal_").Append(eventInfo.Name); contents.Append('('); for (var i = 0; i < paramsCount; i++) { var paramType = eventInfo.Type.GenericArgs[i]; if (i != 0) contents.Append(", "); if (paramType.IsRef && !paramType.IsConst && paramType.IsPod(buildData, classInfo)) contents.Append("ref *"); contents.Append("arg").Append(i); } contents.Append(");").AppendLine(); contents.Append(indent).Append('}').AppendLine(); contents.AppendLine(); contents.Append(indent).Append("[MethodImpl(MethodImplOptions.InternalCall)]").AppendLine(); contents.Append(indent).Append($"internal static extern void Internal_{eventInfo.Name}_Bind("); if (!eventInfo.IsStatic) contents.Append("IntPtr obj, "); contents.Append("bool bind);"); contents.AppendLine(); } // Fields foreach (var fieldInfo in classInfo.Fields) { if (fieldInfo.Getter == null) continue; contents.AppendLine(); foreach (var comment in fieldInfo.Comment) { if (comment.Contains("/// ")) continue; contents.Append(indent).Append(comment.Replace("::", ".")).AppendLine(); } GenerateCSharpAttributes(buildData, contents, indent, classInfo, fieldInfo, useUnmanaged, fieldInfo.DefaultValue); contents.Append(indent); if (fieldInfo.Access == AccessLevel.Public) contents.Append("public "); else if (fieldInfo.Access == AccessLevel.Protected) contents.Append("protected "); else if (fieldInfo.Access == AccessLevel.Private) contents.Append("private "); if (fieldInfo.IsStatic) contents.Append("static "); var returnValueType = GenerateCSharpNativeToManaged(buildData, fieldInfo.Type, classInfo); contents.Append(returnValueType).Append(' ').Append(fieldInfo.Name); if (!useUnmanaged) { var defaultValue = GenerateCSharpDefaultValueNativeToManaged(buildData, fieldInfo.DefaultValue, classInfo); if (!string.IsNullOrEmpty(defaultValue)) contents.Append(" = ").Append(defaultValue); contents.AppendLine(";"); continue; } contents.AppendLine().AppendLine(indent + "{"); indent += " "; contents.Append(indent).Append("get { "); GenerateCSharpWrapperFunctionCall(buildData, contents, classInfo, fieldInfo.Getter); contents.Append(" }").AppendLine(); if (!fieldInfo.IsReadOnly) { contents.Append(indent).Append("set { "); GenerateCSharpWrapperFunctionCall(buildData, contents, classInfo, fieldInfo.Setter, useUnmanaged); contents.Append(" }").AppendLine(); } indent = indent.Substring(0, indent.Length - 4); contents.AppendLine(indent + "}"); GenerateCSharpWrapperFunction(buildData, contents, indent, classInfo, fieldInfo.Getter); if (!fieldInfo.IsReadOnly) GenerateCSharpWrapperFunction(buildData, contents, indent, classInfo, fieldInfo.Setter); } // Properties foreach (var propertyInfo in classInfo.Properties) { if (!useUnmanaged) throw new NotImplementedException("TODO: support properties inside non-static and non-scripting API class types."); contents.AppendLine(); foreach (var comment in propertyInfo.Comment) { if (comment.Contains("/// ") || comment.Contains(" binaryModule) { // Skip generating C# bindings code for native-only modules if (binaryModule.All(x => !x.BuildCSharp)) return; var contents = new StringBuilder(); var binaryModuleName = binaryModule.Key; var project = Builder.GetModuleProject(binaryModule.First(), buildData); // Generate binary module ID GuidInterop idInterop; idInterop.A = Utilities.GetHashCode(binaryModuleName); idInterop.B = Utilities.GetHashCode(project.Name); idInterop.C = Utilities.GetHashCode(project.Company); idInterop.D = Utilities.GetHashCode(project.Copyright); var id = *(Guid*)&idInterop; // Generate C# binary module information var dstFilePath = Path.Combine(project.ProjectFolderPath, "Source", binaryModuleName + ".Gen.cs"); contents.AppendLine("// This code was auto-generated. Do not modify it."); contents.AppendLine(); contents.AppendLine("using System.Reflection;"); contents.AppendLine("using System.Runtime.InteropServices;"); contents.AppendLine(); contents.AppendLine($"[assembly: AssemblyTitle(\"{binaryModuleName}\")]"); contents.AppendLine("[assembly: AssemblyDescription(\"\")]"); contents.AppendLine("[assembly: AssemblyConfiguration(\"\")]"); contents.AppendLine($"[assembly: AssemblyCompany(\"{project.Company}\")]"); contents.AppendLine("[assembly: AssemblyProduct(\"FlaxEngine\")]"); contents.AppendLine($"[assembly: AssemblyCopyright(\"{project.Copyright}\")]"); contents.AppendLine("[assembly: AssemblyTrademark(\"\")]"); contents.AppendLine("[assembly: AssemblyCulture(\"\")]"); contents.AppendLine("[assembly: ComVisible(false)]"); contents.AppendLine($"[assembly: Guid(\"{id:d}\")]"); contents.AppendLine($"[assembly: AssemblyVersion(\"{project.Version}\")]"); contents.AppendLine($"[assembly: AssemblyFileVersion(\"{project.Version}\")]"); Utilities.WriteFileIfChanged(dstFilePath, contents.ToString()); } } }