From 4ec0c37abc101af6f6ada921c9a190913810fe02 Mon Sep 17 00:00:00 2001 From: CallumDev Date: Mon, 24 Aug 2020 22:41:10 +0930 Subject: [PATCH] Upgrade CodeGenerator to handle new cimgui json files --- src/CodeGenerator/CodeGenerator.csproj | 4 +- src/CodeGenerator/ImguiDefinitions.cs | 506 +++++++++++++++++++ src/CodeGenerator/Program.cs | 646 +++---------------------- src/CodeGenerator/TypeInfo.cs | 105 ++++ 4 files changed, 676 insertions(+), 585 deletions(-) create mode 100644 src/CodeGenerator/ImguiDefinitions.cs create mode 100644 src/CodeGenerator/TypeInfo.cs diff --git a/src/CodeGenerator/CodeGenerator.csproj b/src/CodeGenerator/CodeGenerator.csproj index 5668f6a..b86b10b 100644 --- a/src/CodeGenerator/CodeGenerator.csproj +++ b/src/CodeGenerator/CodeGenerator.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + netcoreapp3.1 @@ -12,6 +12,8 @@ + + diff --git a/src/CodeGenerator/ImguiDefinitions.cs b/src/CodeGenerator/ImguiDefinitions.cs new file mode 100644 index 0000000..ac5409f --- /dev/null +++ b/src/CodeGenerator/ImguiDefinitions.cs @@ -0,0 +1,506 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.CodeAnalysis.CSharp.Scripting; +using System.IO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace CodeGenerator +{ + class ImguiDefinitions + { + public EnumDefinition[] Enums; + public TypeDefinition[] Types; + public FunctionDefinition[] Functions; + public Dictionary Variants; + public void LoadFrom(string directory) + { + + JObject typesJson; + using (StreamReader fs = File.OpenText(Path.Combine(directory, "structs_and_enums.json"))) + using (JsonTextReader jr = new JsonTextReader(fs)) + { + typesJson = JObject.Load(jr); + } + + JObject functionsJson; + using (StreamReader fs = File.OpenText(Path.Combine(directory, "definitions.json"))) + using (JsonTextReader jr = new JsonTextReader(fs)) + { + functionsJson = JObject.Load(jr); + } + + JObject variantsJson = null; + if (File.Exists(Path.Combine(directory, "variants.json"))) + { + using (StreamReader fs = File.OpenText(Path.Combine(AppContext.BaseDirectory, "variants.json"))) + using (JsonTextReader jr = new JsonTextReader(fs)) + { + variantsJson = JObject.Load(jr); + } + } + + Variants = new Dictionary(); + foreach (var jt in variantsJson.Children()) + { + JProperty jp = (JProperty)jt; + ParameterVariant[] methodVariants = jp.Values().Select(jv => + { + return new ParameterVariant(jv["name"].ToString(), jv["type"].ToString(), jv["variants"].Select(s => s.ToString()).ToArray()); + }).ToArray(); + Variants.Add(jp.Name, new MethodVariant(jp.Name, methodVariants)); + } + + var typeLocations = typesJson["locations"]; + + Enums = typesJson["enums"].Select(jt => + { + JProperty jp = (JProperty)jt; + string name = jp.Name; + if (typeLocations[jp.Name].Value() == "internal") { + return null; + } + EnumMember[] elements = jp.Values().Select(v => + { + return new EnumMember(v["name"].ToString(), v["value"].ToString()); + }).ToArray(); + return new EnumDefinition(name, elements); + }).Where(x => x != null).ToArray(); + + Types = typesJson["structs"].Select(jt => + { + JProperty jp = (JProperty)jt; + string name = jp.Name; + if (typeLocations[jp.Name].Value() == "internal") { + return null; + } + TypeReference[] fields = jp.Values().Select(v => + { + if (v["type"].ToString().Contains("static")) { return null; } + + return new TypeReference( + v["name"].ToString(), + v["type"].ToString(), + v["template_type"]?.ToString(), + Enums); + }).Where(tr => tr != null).ToArray(); + return new TypeDefinition(name, fields); + }).Where(x => x != null).ToArray(); + + Functions = functionsJson.Children().Select(jt => + { + JProperty jp = (JProperty)jt; + string name = jp.Name; + bool hasNonUdtVariants = jp.Values().Any(val => val["ov_cimguiname"]?.ToString().EndsWith("nonUDT") ?? false); + OverloadDefinition[] overloads = jp.Values().Select(val => + { + string ov_cimguiname = val["ov_cimguiname"]?.ToString(); + string cimguiname = val["cimguiname"].ToString(); + string friendlyName = val["funcname"]?.ToString(); + if (cimguiname.EndsWith("_destroy")) + { + friendlyName = "Destroy"; + } + //skip internal functions + var typename = val["stname"]?.ToString(); + if (!string.IsNullOrEmpty(typename)) + { + if (!Types.Any(x => x.Name == val["stname"]?.ToString())) { + return null; + } + } + if (friendlyName == null) { return null; } + if (val["location"]?.ToString() == "internal") return null; + + string exportedName = ov_cimguiname; + if (exportedName == null) + { + exportedName = cimguiname; + } + + if (hasNonUdtVariants && !exportedName.EndsWith("nonUDT2")) + { + return null; + } + + string selfTypeName = null; + int underscoreIndex = exportedName.IndexOf('_'); + if (underscoreIndex > 0 && !exportedName.StartsWith("ig")) // Hack to exclude some weirdly-named non-instance functions. + { + selfTypeName = exportedName.Substring(0, underscoreIndex); + } + + List parameters = new List(); + + // find any variants that can be applied to the parameters of this method based on the method name + MethodVariant methodVariants = null; + Variants.TryGetValue(jp.Name, out methodVariants); + + foreach (JToken p in val["argsT"]) + { + string pType = p["type"].ToString(); + string pName = p["name"].ToString(); + + // if there are possible variants for this method then try to match them based on the parameter name and expected type + ParameterVariant matchingVariant = methodVariants?.Parameters.Where(pv => pv.Name == pName && pv.OriginalType == pType).FirstOrDefault() ?? null; + if (matchingVariant != null) matchingVariant.Used = true; + + parameters.Add(new TypeReference(pName, pType, Enums, matchingVariant?.VariantTypes)); + } + + Dictionary defaultValues = new Dictionary(); + foreach (JToken dv in val["defaults"]) + { + JProperty dvProp = (JProperty)dv; + defaultValues.Add(dvProp.Name, dvProp.Value.ToString()); + } + string returnType = val["ret"]?.ToString() ?? "void"; + string comment = null; + + string structName = val["stname"].ToString(); + bool isConstructor = val.Value("constructor"); + bool isDestructor = val.Value("destructor"); + if (isConstructor) + { + returnType = structName + "*"; + } + + return new OverloadDefinition( + exportedName, + friendlyName, + parameters.ToArray(), + defaultValues, + returnType, + structName, + comment, + isConstructor, + isDestructor); + }).Where(od => od != null).ToArray(); + if(overloads.Length == 0) return null; + return new FunctionDefinition(name, overloads, Enums); + }).Where(x => x != null).OrderBy(fd => fd.Name).ToArray(); + } + } + + class MethodVariant + { + public string Name { get; } + + public ParameterVariant[] Parameters { get; } + + public MethodVariant(string name, ParameterVariant[] parameters) + { + Name = name; + Parameters = parameters; + } + } + + class ParameterVariant + { + public string Name { get; } + + public string OriginalType { get; } + + public string[] VariantTypes { get; } + + public bool Used { get; set; } + + public ParameterVariant(string name, string originalType, string[] variantTypes) + { + Name = name; + OriginalType = originalType; + VariantTypes = variantTypes; + Used = false; + } + } + + class EnumDefinition + { + private readonly Dictionary _sanitizedNames; + + public string Name { get; } + public string FriendlyName { get; } + public EnumMember[] Members { get; } + + public EnumDefinition(string name, EnumMember[] elements) + { + Name = name; + if (Name.EndsWith('_')) + { + FriendlyName = Name.Substring(0, Name.Length - 1); + } + else + { + FriendlyName = Name; + } + Members = elements; + + _sanitizedNames = new Dictionary(); + foreach (EnumMember el in elements) + { + _sanitizedNames.Add(el.Name, SanitizeMemberName(el.Name)); + } + } + + public string SanitizeNames(string text) + { + foreach (KeyValuePair kvp in _sanitizedNames) + { + text = text.Replace(kvp.Key, kvp.Value); + } + + return text; + } + + private string SanitizeMemberName(string memberName) + { + string ret = memberName; + if (memberName.StartsWith(Name)) + { + ret = memberName.Substring(Name.Length); + } + + if (ret.EndsWith('_')) + { + ret = ret.Substring(0, ret.Length - 1); + } + + return ret; + } + } + + class EnumMember + { + public EnumMember(string name, string value) + { + Name = name; + Value = value; + } + + public string Name { get; } + public string Value { get; } + } + + class TypeDefinition + { + public string Name { get; } + public TypeReference[] Fields { get; } + + public TypeDefinition(string name, TypeReference[] fields) + { + Name = name; + Fields = fields; + } + } + + class TypeReference + { + public string Name { get; } + public string Type { get; } + public string TemplateType { get; } + public int ArraySize { get; } + public bool IsFunctionPointer { get; } + public string[] TypeVariants { get; } + public bool IsEnum { get; } + + public TypeReference(string name, string type, EnumDefinition[] enums) + : this(name, type, null, enums, null) { } + + public TypeReference(string name, string type, EnumDefinition[] enums, string[] typeVariants) + : this(name, type, null, enums, typeVariants) { } + + public TypeReference(string name, string type, string templateType, EnumDefinition[] enums) + : this(name, type, templateType, enums, null) { } + + public TypeReference(string name, string type, string templateType, EnumDefinition[] enums, string[] typeVariants) + { + Name = name; + Type = type.Replace("const", string.Empty).Trim(); + + + if (Type.StartsWith("ImVector_")) + { + if (Type.EndsWith("*")) + { + Type = "ImVector*"; + } + else + { + Type = "ImVector"; + } + } + + if (Type.StartsWith("ImChunkStream_")) + { + if (Type.EndsWith("*")) + { + Type = "ImChunkStream*"; + } + else + { + Type = "ImChunkStream"; + } + } + + TemplateType = templateType; + int startBracket = name.IndexOf('['); + if (startBracket != -1) + { + int endBracket = name.IndexOf(']'); + string sizePart = name.Substring(startBracket + 1, endBracket - startBracket - 1); + ArraySize = ParseSizeString(sizePart, enums); + Name = Name.Substring(0, startBracket); + } + + IsFunctionPointer = Type.IndexOf('(') != -1; + + TypeVariants = typeVariants; + + IsEnum = enums.Any(t => t.Name == type || t.FriendlyName == type); + } + + private int ParseSizeString(string sizePart, EnumDefinition[] enums) + { + if (sizePart.Contains("(") || sizePart.Contains("+") || sizePart.Contains("/") || + sizePart.Contains("*")) + { + return Task.WaitAny(CSharpScript.EvaluateAsync($"(int)({sizePart})")); + } + if (!int.TryParse(sizePart, out int ret)) + { + foreach (EnumDefinition ed in enums) + { + if (sizePart.StartsWith(ed.Name)) + { + foreach (EnumMember member in ed.Members) + { + if (member.Name == sizePart) + { + return int.Parse(member.Value); + } + } + } + } + + ret = -1; + } + + return ret; + } + + public TypeReference WithVariant(int variantIndex, EnumDefinition[] enums) + { + if (variantIndex == 0) return this; + else return new TypeReference(Name, TypeVariants[variantIndex - 1], TemplateType, enums); + } + } + + class FunctionDefinition + { + public string Name { get; } + public OverloadDefinition[] Overloads { get; } + + public FunctionDefinition(string name, OverloadDefinition[] overloads, EnumDefinition[] enums) + { + Name = name; + Overloads = ExpandOverloadVariants(overloads, enums); + } + + private OverloadDefinition[] ExpandOverloadVariants(OverloadDefinition[] overloads, EnumDefinition[] enums) + { + List newDefinitions = new List(); + + foreach (OverloadDefinition overload in overloads) + { + bool hasVariants = false; + int[] variantCounts = new int[overload.Parameters.Length]; + + for (int i = 0; i < overload.Parameters.Length; i++) + { + if (overload.Parameters[i].TypeVariants != null) + { + hasVariants = true; + variantCounts[i] = overload.Parameters[i].TypeVariants.Length + 1; + } + else + { + variantCounts[i] = 1; + } + } + + if (hasVariants) + { + int totalVariants = variantCounts[0]; + for (int i = 1; i < variantCounts.Length; i++) totalVariants *= variantCounts[i]; + + for (int i = 0; i < totalVariants; i++) + { + TypeReference[] parameters = new TypeReference[overload.Parameters.Length]; + int div = 1; + + for (int j = 0; j < parameters.Length; j++) + { + int k = (i / div) % variantCounts[j]; + + parameters[j] = overload.Parameters[j].WithVariant(k, enums); + + if (j > 0) div *= variantCounts[j]; + } + + newDefinitions.Add(overload.WithParameters(parameters)); + } + } + else + { + newDefinitions.Add(overload); + } + } + + return newDefinitions.ToArray(); + } + } + + class OverloadDefinition + { + public string ExportedName { get; } + public string FriendlyName { get; } + public TypeReference[] Parameters { get; } + public Dictionary DefaultValues { get; } + public string ReturnType { get; } + public string StructName { get; } + public bool IsMemberFunction { get; } + public string Comment { get; } + public bool IsConstructor { get; } + public bool IsDestructor { get; } + + public OverloadDefinition( + string exportedName, + string friendlyName, + TypeReference[] parameters, + Dictionary defaultValues, + string returnType, + string structName, + string comment, + bool isConstructor, + bool isDestructor) + { + ExportedName = exportedName; + FriendlyName = friendlyName; + Parameters = parameters; + DefaultValues = defaultValues; + ReturnType = returnType.Replace("const", string.Empty).Replace("inline", string.Empty).Trim(); + StructName = structName; + IsMemberFunction = !string.IsNullOrEmpty(structName); + Comment = comment; + IsConstructor = isConstructor; + IsDestructor = isDestructor; + } + + public OverloadDefinition WithParameters(TypeReference[] parameters) + { + return new OverloadDefinition(ExportedName, FriendlyName, parameters, DefaultValues, ReturnType, StructName, Comment, IsConstructor, IsDestructor); + } + } +} \ No newline at end of file diff --git a/src/CodeGenerator/Program.cs b/src/CodeGenerator/Program.cs index f3f8886..3ccd814 100644 --- a/src/CodeGenerator/Program.cs +++ b/src/CodeGenerator/Program.cs @@ -7,102 +7,13 @@ using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.Scripting; namespace CodeGenerator { internal static class Program { - private static readonly Dictionary s_wellKnownTypes = new Dictionary() - { - { "bool", "byte" }, - { "unsigned char", "byte" }, - { "char", "byte" }, - { "ImWchar", "ushort" }, - { "unsigned short", "ushort" }, - { "unsigned int", "uint" }, - { "ImVec2", "Vector2" }, - { "ImVec2_Simple", "Vector2" }, - { "ImVec3", "Vector3" }, - { "ImVec4", "Vector4" }, - { "ImVec4_Simple", "Vector4" }, - { "ImColor_Simple", "ImColor" }, - { "ImTextureID", "IntPtr" }, - { "ImGuiID", "uint" }, - { "ImDrawIdx", "ushort" }, - { "ImDrawListSharedData", "IntPtr" }, - { "ImDrawListSharedData*", "IntPtr" }, - { "ImU32", "uint" }, - { "ImDrawCallback", "IntPtr" }, - { "size_t", "uint" }, - { "ImGuiContext*", "IntPtr" }, - { "float[2]", "Vector2*" }, - { "float[3]", "Vector3*" }, - { "float[4]", "Vector4*" }, - { "int[2]", "int*" }, - { "int[3]", "int*" }, - { "int[4]", "int*" }, - { "float&", "float*" }, - { "ImVec2[2]", "Vector2*" }, - { "char* []", "byte**" }, - }; - - private static readonly Dictionary s_wellKnownFieldReplacements = new Dictionary() - { - { "bool", "bool" }, // Force bool to remain as bool in type-safe wrappers. - }; - - private static readonly HashSet s_customDefinedTypes = new HashSet() - { - "ImVector", - "ImVec2", - "ImVec4", - "ImGuiStoragePair", - }; - - private static readonly Dictionary s_wellKnownDefaultValues = new Dictionary() - { - { "((void *)0)", "null" }, - { "((void*)0)", "null" }, - { "ImVec2(0,0)", "new Vector2()" }, - { "ImVec2(-1,0)", "new Vector2(-1, 0)" }, - { "ImVec2(1,0)", "new Vector2(1, 0)" }, - { "ImVec2(1,1)", "new Vector2(1, 1)" }, - { "ImVec2(0,1)", "new Vector2(0, 1)" }, - { "ImVec4(0,0,0,0)", "new Vector4()" }, - { "ImVec4(1,1,1,1)", "new Vector4(1, 1, 1, 1)" }, - { "ImDrawCornerFlags_All", "ImDrawCornerFlags.All" }, - { "FLT_MAX", "float.MaxValue" }, - { "(((ImU32)(255)<<24)|((ImU32)(255)<<16)|((ImU32)(255)<<8)|((ImU32)(255)<<0))", "0xFFFFFFFF" } - }; - - private static readonly Dictionary s_identifierReplacements = new Dictionary() - { - { "in", "@in" }, - { "out", "@out" }, - { "ref", "@ref" }, - }; - - private static readonly HashSet s_legalFixedTypes = new HashSet() - { - "byte", - "sbyte", - "char", - "ushort", - "short", - "uint", - "int", - "ulong", - "long", - "float", - "double", - }; - - private static readonly HashSet s_skippedFunctions = new HashSet() - { - "igInputText", - "igInputTextMultiline" - }; - static void Main(string[] args) { string outputPath; @@ -114,157 +25,13 @@ namespace CodeGenerator { outputPath = AppContext.BaseDirectory; } + + var defs = new ImguiDefinitions(); + defs.LoadFrom(AppContext.BaseDirectory); + Console.WriteLine($"Outputting generated code files to {outputPath}."); - JObject typesJson; - using (StreamReader fs = File.OpenText(Path.Combine(AppContext.BaseDirectory, "structs_and_enums.json"))) - using (JsonTextReader jr = new JsonTextReader(fs)) - { - typesJson = JObject.Load(jr); - } - - JObject functionsJson; - using (StreamReader fs = File.OpenText(Path.Combine(AppContext.BaseDirectory, "definitions.json"))) - using (JsonTextReader jr = new JsonTextReader(fs)) - { - functionsJson = JObject.Load(jr); - } - - JObject variantsJson = null; - if (File.Exists(Path.Combine(AppContext.BaseDirectory, "variants.json"))) - { - using (StreamReader fs = File.OpenText(Path.Combine(AppContext.BaseDirectory, "variants.json"))) - using (JsonTextReader jr = new JsonTextReader(fs)) - { - variantsJson = JObject.Load(jr); - } - } - - Dictionary variants = new Dictionary(); - foreach (var jt in variantsJson.Children()) - { - JProperty jp = (JProperty)jt; - ParameterVariant[] methodVariants = jp.Values().Select(jv => - { - return new ParameterVariant(jv["name"].ToString(), jv["type"].ToString(), jv["variants"].Select(s => s.ToString()).ToArray()); - }).ToArray(); - variants.Add(jp.Name, new MethodVariant(jp.Name, methodVariants)); - } - - EnumDefinition[] enums = typesJson["enums"].Select(jt => - { - JProperty jp = (JProperty)jt; - string name = jp.Name; - EnumMember[] elements = jp.Values().Select(v => - { - return new EnumMember(v["name"].ToString(), v["value"].ToString()); - }).ToArray(); - return new EnumDefinition(name, elements); - }).ToArray(); - - TypeDefinition[] types = typesJson["structs"].Select(jt => - { - JProperty jp = (JProperty)jt; - string name = jp.Name; - TypeReference[] fields = jp.Values().Select(v => - { - if (v["type"].ToString().Contains("static")) { return null; } - - return new TypeReference( - v["name"].ToString(), - v["type"].ToString(), - v["template_type"]?.ToString(), - enums); - }).Where(tr => tr != null).ToArray(); - return new TypeDefinition(name, fields); - }).ToArray(); - - FunctionDefinition[] functions = functionsJson.Children().Select(jt => - { - JProperty jp = (JProperty)jt; - string name = jp.Name; - bool hasNonUdtVariants = jp.Values().Any(val => val["ov_cimguiname"]?.ToString().EndsWith("nonUDT") ?? false); - - OverloadDefinition[] overloads = jp.Values().Select(val => - { - string ov_cimguiname = val["ov_cimguiname"]?.ToString(); - string cimguiname = val["cimguiname"].ToString(); - string friendlyName = val["funcname"]?.ToString(); - if (cimguiname.EndsWith("_destroy")) - { - friendlyName = "Destroy"; - } - if (friendlyName == null) { return null; } - - string exportedName = ov_cimguiname; - if (exportedName == null) - { - exportedName = cimguiname; - } - - if (hasNonUdtVariants && !exportedName.EndsWith("nonUDT2")) - { - return null; - } - - string selfTypeName = null; - int underscoreIndex = exportedName.IndexOf('_'); - if (underscoreIndex > 0 && !exportedName.StartsWith("ig")) // Hack to exclude some weirdly-named non-instance functions. - { - selfTypeName = exportedName.Substring(0, underscoreIndex); - } - - List parameters = new List(); - - // find any variants that can be applied to the parameters of this method based on the method name - MethodVariant methodVariants = null; - variants.TryGetValue(jp.Name, out methodVariants); - - foreach (JToken p in val["argsT"]) - { - string pType = p["type"].ToString(); - string pName = p["name"].ToString(); - - // if there are possible variants for this method then try to match them based on the parameter name and expected type - ParameterVariant matchingVariant = methodVariants?.Parameters.Where(pv => pv.Name == pName && pv.OriginalType == pType).FirstOrDefault() ?? null; - if (matchingVariant != null) matchingVariant.Used = true; - - parameters.Add(new TypeReference(pName, pType, enums, matchingVariant?.VariantTypes)); - } - - Dictionary defaultValues = new Dictionary(); - foreach (JToken dv in val["defaults"]) - { - JProperty dvProp = (JProperty)dv; - defaultValues.Add(dvProp.Name, dvProp.Value.ToString()); - } - string returnType = val["ret"]?.ToString() ?? "void"; - string comment = null; - - string structName = val["stname"].ToString(); - bool isConstructor = val.Value("constructor"); - bool isDestructor = val.Value("destructor"); - if (isConstructor) - { - returnType = structName + "*"; - } - - return new OverloadDefinition( - exportedName, - friendlyName, - parameters.ToArray(), - defaultValues, - returnType, - structName, - comment, - isConstructor, - isDestructor); - }).Where(od => od != null).ToArray(); - - return new FunctionDefinition(name, overloads, enums); - }).OrderBy(fd => fd.Name).ToArray(); - - foreach (EnumDefinition ed in enums) + foreach (EnumDefinition ed in defs.Enums) { using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, ed.FriendlyName + ".gen.cs"))) { @@ -285,9 +52,9 @@ namespace CodeGenerator } } - foreach (TypeDefinition td in types) + foreach (TypeDefinition td in defs.Types) { - if (s_customDefinedTypes.Contains(td.Name)) { continue; } + if (TypeInfo.CustomDefinedTypes.Contains(td.Name)) { continue; } using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, td.Name + ".gen.cs"))) { @@ -305,7 +72,7 @@ namespace CodeGenerator if (field.ArraySize != 0) { - if (s_legalFixedTypes.Contains(typeStr)) + if (TypeInfo.LegalFixedTypes.Contains(typeStr)) { writer.WriteLine($"public fixed {typeStr} {field.Name}[{field.ArraySize}];"); } @@ -338,21 +105,21 @@ namespace CodeGenerator string typeStr = GetTypeString(field.Type, field.IsFunctionPointer); string rawType = typeStr; - if (s_wellKnownFieldReplacements.TryGetValue(field.Type, out string wellKnownFieldType)) + if (TypeInfo.WellKnownFieldReplacements.TryGetValue(field.Type, out string wellKnownFieldType)) { typeStr = wellKnownFieldType; } if (field.ArraySize != 0) { - string addrTarget = s_legalFixedTypes.Contains(rawType) ? $"NativePtr->{field.Name}" : $"&NativePtr->{field.Name}_0"; + string addrTarget = TypeInfo.LegalFixedTypes.Contains(rawType) ? $"NativePtr->{field.Name}" : $"&NativePtr->{field.Name}_0"; writer.WriteLine($"public RangeAccessor<{typeStr}> {field.Name} => new RangeAccessor<{typeStr}>({addrTarget}, {field.ArraySize});"); } else if (typeStr.Contains("ImVector")) { string vectorElementType = GetTypeString(field.TemplateType, false); - if (s_wellKnownTypes.TryGetValue(vectorElementType, out string wellKnown)) + if (TypeInfo.WellKnownTypes.TryGetValue(vectorElementType, out string wellKnown)) { vectorElementType = wellKnown; } @@ -394,7 +161,7 @@ namespace CodeGenerator } } - foreach (FunctionDefinition fd in functions) + foreach (FunctionDefinition fd in defs.Functions) { foreach (OverloadDefinition overload in fd.Overloads) { @@ -461,13 +228,15 @@ namespace CodeGenerator writer.WriteLine(string.Empty); writer.PushBlock("namespace ImGuiNET"); writer.PushBlock("public static unsafe partial class ImGuiNative"); - foreach (FunctionDefinition fd in functions) + foreach (FunctionDefinition fd in defs.Functions) { foreach (OverloadDefinition overload in fd.Overloads) { string exportedName = overload.ExportedName; if (exportedName.Contains("~")) { continue; } if (exportedName.Contains("ImVector_")) { continue; } + if (exportedName.Contains("ImChunkStream_")) { continue; } + if (overload.Parameters.Any(tr => tr.Type.Contains('('))) { continue; } // TODO: Parse function pointer parameters. string ret = GetTypeString(overload.ReturnType, false); @@ -528,9 +297,9 @@ namespace CodeGenerator writer.WriteLine(string.Empty); writer.PushBlock("namespace ImGuiNET"); writer.PushBlock("public static unsafe partial class ImGui"); - foreach (FunctionDefinition fd in functions) + foreach (FunctionDefinition fd in defs.Functions) { - if (s_skippedFunctions.Contains(fd.Name)) { continue; } + if (TypeInfo.SkippedFunctions.Contains(fd.Name)) { continue; } foreach (OverloadDefinition overload in fd.Overloads) { @@ -576,7 +345,7 @@ namespace CodeGenerator writer.PopBlock(); } - foreach (var method in variants) + foreach (var method in defs.Variants) { foreach (var variant in method.Value.Parameters) { @@ -635,17 +404,28 @@ namespace CodeGenerator List preCallLines = new List(); List postCallLines = new List(); List byRefParams = new List(); - + int selfIndex = -1; + int pOutIndex = -1; + string overrideRet = null; for (int i = 0; i < overload.Parameters.Length; i++) { - if (i == 0 && selfName != null) { continue; } - TypeReference tr = overload.Parameters[i]; + if (tr.Name == "self") + { + selfIndex = i; + continue; + } if (tr.Name == "...") { continue; } string correctedIdentifier = CorrectIdentifier(tr.Name); string nativeTypeName = GetTypeString(tr.Type, tr.IsFunctionPointer); - + if (correctedIdentifier == "pOut" && overload.ReturnType == "void") + { + pOutIndex = i; + overrideRet = nativeTypeName.TrimEnd('*'); + preCallLines.Add($"{overrideRet} __retval;"); + continue; + } if (tr.Type == "char*") { string textToEncode = correctedIdentifier; @@ -770,8 +550,8 @@ namespace CodeGenerator preCallLines.Add($"{nativePtrTypeName} {nativeArgName} = ({nativePtrTypeName}){correctedIdentifier}.ToPointer();"); } else if (GetWrappedType(tr.Type, out string wrappedParamType) - && !s_wellKnownTypes.ContainsKey(tr.Type) - && !s_wellKnownTypes.ContainsKey(tr.Type.Substring(0, tr.Type.Length - 1))) + && !TypeInfo.WellKnownTypes.ContainsKey(tr.Type) + && !TypeInfo.WellKnownTypes.ContainsKey(tr.Type.Substring(0, tr.Type.Length - 1))) { marshalledParameters[i] = new MarshalledParameter(wrappedParamType, false, "native_" + tr.Name, false); string nativeArgName = "native_" + tr.Name; @@ -783,7 +563,7 @@ namespace CodeGenerator string nonPtrType; if (tr.Type.Contains("[")) { - string wellKnown = s_wellKnownTypes[tr.Type]; + string wellKnown = TypeInfo.WellKnownTypes[tr.Type]; nonPtrType = GetTypeString(wellKnown.Substring(0, wellKnown.Length - 1), false); } else @@ -811,7 +591,7 @@ namespace CodeGenerator string friendlyName = overload.FriendlyName; string staticPortion = selfName == null ? "static " : string.Empty; - writer.PushBlock($"public {staticPortion}{safeRet} {friendlyName}({invocationList})"); + writer.PushBlock($"public {staticPortion}{overrideRet ?? safeRet} {friendlyName}({invocationList})"); foreach (string line in preCallLines) { writer.WriteLine(line); @@ -819,14 +599,22 @@ namespace CodeGenerator List nativeInvocationArgs = new List(); - if (selfName != null) - { - nativeInvocationArgs.Add(selfName); - } - for (int i = 0; i < marshalledParameters.Length; i++) { TypeReference tr = overload.Parameters[i]; + if (selfIndex == i) + { + //Some overloads seem to be generated with IntPtr as self + //instead of the proper pointer type. TODO: investigate + string tstr = GetTypeString(tr.Type, false); + nativeInvocationArgs.Add($"({tstr})({selfName})"); + continue; + } + if (pOutIndex == i) + { + nativeInvocationArgs.Add("&__retval"); + continue; + } MarshalledParameter mp = marshalledParameters[i]; if (mp == null) { continue; } if (mp.IsPinned) @@ -879,6 +667,9 @@ namespace CodeGenerator } } + if (overrideRet != null) + writer.WriteLine("return __retval;"); + for (int i = 0; i < marshalledParameters.Length; i++) { MarshalledParameter mp = marshalledParameters[i]; @@ -927,7 +718,7 @@ namespace CodeGenerator } string nonPtrType = nativeType.Substring(0, nativeType.Length - pointerLevel); - if (s_wellKnownTypes.ContainsKey(nonPtrType)) + if (TypeInfo.WellKnownTypes.ContainsKey(nonPtrType)) { wrappedType = null; return false; @@ -952,7 +743,7 @@ namespace CodeGenerator return true; } - if (s_wellKnownDefaultValues.TryGetValue(defaultVal, out correctedDefault)) { return true; } + if (TypeInfo.WellKnownDefaultValues.TryGetValue(defaultVal, out correctedDefault)) { return true; } if (tr.Type == "bool") { @@ -978,13 +769,13 @@ namespace CodeGenerator if (typeName.EndsWith("**")) { pointerLevel = 2; } else if (typeName.EndsWith("*")) { pointerLevel = 1; } - if (!s_wellKnownTypes.TryGetValue(typeName, out string typeStr)) + if (!TypeInfo.WellKnownTypes.TryGetValue(typeName, out string typeStr)) { - if (s_wellKnownTypes.TryGetValue(typeName.Substring(0, typeName.Length - pointerLevel), out typeStr)) + if (TypeInfo.WellKnownTypes.TryGetValue(typeName.Substring(0, typeName.Length - pointerLevel), out typeStr)) { typeStr = typeStr + new string('*', pointerLevel); } - else if (!s_wellKnownTypes.TryGetValue(typeName, out typeStr)) + else if (!TypeInfo.WellKnownTypes.TryGetValue(typeName, out typeStr)) { typeStr = typeName; if (isFunctionPointer) { typeStr = "IntPtr"; } @@ -996,7 +787,7 @@ namespace CodeGenerator private static string CorrectIdentifier(string identifier) { - if (s_identifierReplacements.TryGetValue(identifier, out string replacement)) + if (TypeInfo.IdentifierReplacements.TryGetValue(identifier, out string replacement)) { return replacement; } @@ -1007,319 +798,6 @@ namespace CodeGenerator } } - class MethodVariant - { - public string Name { get; } - - public ParameterVariant[] Parameters { get; } - - public MethodVariant(string name, ParameterVariant[] parameters) - { - Name = name; - Parameters = parameters; - } - } - - class ParameterVariant - { - public string Name { get; } - - public string OriginalType { get; } - - public string[] VariantTypes { get; } - - public bool Used { get; set; } - - public ParameterVariant(string name, string originalType, string[] variantTypes) - { - Name = name; - OriginalType = originalType; - VariantTypes = variantTypes; - Used = false; - } - } - - class EnumDefinition - { - private readonly Dictionary _sanitizedNames; - - public string Name { get; } - public string FriendlyName { get; } - public EnumMember[] Members { get; } - - public EnumDefinition(string name, EnumMember[] elements) - { - Name = name; - if (Name.EndsWith('_')) - { - FriendlyName = Name.Substring(0, Name.Length - 1); - } - else - { - FriendlyName = Name; - } - Members = elements; - - _sanitizedNames = new Dictionary(); - foreach (EnumMember el in elements) - { - _sanitizedNames.Add(el.Name, SanitizeMemberName(el.Name)); - } - } - - public string SanitizeNames(string text) - { - foreach (KeyValuePair kvp in _sanitizedNames) - { - text = text.Replace(kvp.Key, kvp.Value); - } - - return text; - } - - private string SanitizeMemberName(string memberName) - { - string ret = memberName; - if (memberName.StartsWith(Name)) - { - ret = memberName.Substring(Name.Length); - } - - if (ret.EndsWith('_')) - { - ret = ret.Substring(0, ret.Length - 1); - } - - return ret; - } - } - - class EnumMember - { - public EnumMember(string name, string value) - { - Name = name; - Value = value; - } - - public string Name { get; } - public string Value { get; } - } - - class TypeDefinition - { - public string Name { get; } - public TypeReference[] Fields { get; } - - public TypeDefinition(string name, TypeReference[] fields) - { - Name = name; - Fields = fields; - } - } - - class TypeReference - { - public string Name { get; } - public string Type { get; } - public string TemplateType { get; } - public int ArraySize { get; } - public bool IsFunctionPointer { get; } - public string[] TypeVariants { get; } - public bool IsEnum { get; } - - public TypeReference(string name, string type, EnumDefinition[] enums) - : this(name, type, null, enums, null) { } - - public TypeReference(string name, string type, EnumDefinition[] enums, string[] typeVariants) - : this(name, type, null, enums, typeVariants) { } - - public TypeReference(string name, string type, string templateType, EnumDefinition[] enums) - : this(name, type, templateType, enums, null) { } - - public TypeReference(string name, string type, string templateType, EnumDefinition[] enums, string[] typeVariants) - { - Name = name; - Type = type.Replace("const", string.Empty).Trim(); - - - if (Type.StartsWith("ImVector_")) - { - if (Type.EndsWith("*")) - { - Type = "ImVector*"; - } - else - { - Type = "ImVector"; - } - } - - TemplateType = templateType; - int startBracket = name.IndexOf('['); - if (startBracket != -1) - { - int endBracket = name.IndexOf(']'); - string sizePart = name.Substring(startBracket + 1, endBracket - startBracket - 1); - ArraySize = ParseSizeString(sizePart, enums); - Name = Name.Substring(0, startBracket); - } - - IsFunctionPointer = Type.IndexOf('(') != -1; - - TypeVariants = typeVariants; - - IsEnum = enums.Any(t => t.Name == type || t.FriendlyName == type); - } - - private int ParseSizeString(string sizePart, EnumDefinition[] enums) - { - int plusStart = sizePart.IndexOf('+'); - if (plusStart != -1) - { - string first = sizePart.Substring(0, plusStart); - string second = sizePart.Substring(plusStart, sizePart.Length - plusStart); - int firstVal = int.Parse(first); - int secondVal = int.Parse(second); - return firstVal + secondVal; - } - - if (!int.TryParse(sizePart, out int ret)) - { - foreach (EnumDefinition ed in enums) - { - if (sizePart.StartsWith(ed.Name)) - { - foreach (EnumMember member in ed.Members) - { - if (member.Name == sizePart) - { - return int.Parse(member.Value); - } - } - } - } - - ret = -1; - } - - return ret; - } - - public TypeReference WithVariant(int variantIndex, EnumDefinition[] enums) - { - if (variantIndex == 0) return this; - else return new TypeReference(Name, TypeVariants[variantIndex - 1], TemplateType, enums); - } - } - - class FunctionDefinition - { - public string Name { get; } - public OverloadDefinition[] Overloads { get; } - - public FunctionDefinition(string name, OverloadDefinition[] overloads, EnumDefinition[] enums) - { - Name = name; - Overloads = ExpandOverloadVariants(overloads, enums); - } - - private OverloadDefinition[] ExpandOverloadVariants(OverloadDefinition[] overloads, EnumDefinition[] enums) - { - List newDefinitions = new List(); - - foreach (OverloadDefinition overload in overloads) - { - bool hasVariants = false; - int[] variantCounts = new int[overload.Parameters.Length]; - - for (int i = 0; i < overload.Parameters.Length; i++) - { - if (overload.Parameters[i].TypeVariants != null) - { - hasVariants = true; - variantCounts[i] = overload.Parameters[i].TypeVariants.Length + 1; - } - else - { - variantCounts[i] = 1; - } - } - - if (hasVariants) - { - int totalVariants = variantCounts[0]; - for (int i = 1; i < variantCounts.Length; i++) totalVariants *= variantCounts[i]; - - for (int i = 0; i < totalVariants; i++) - { - TypeReference[] parameters = new TypeReference[overload.Parameters.Length]; - int div = 1; - - for (int j = 0; j < parameters.Length; j++) - { - int k = (i / div) % variantCounts[j]; - - parameters[j] = overload.Parameters[j].WithVariant(k, enums); - - if (j > 0) div *= variantCounts[j]; - } - - newDefinitions.Add(overload.WithParameters(parameters)); - } - } - else - { - newDefinitions.Add(overload); - } - } - - return newDefinitions.ToArray(); - } - } - - class OverloadDefinition - { - public string ExportedName { get; } - public string FriendlyName { get; } - public TypeReference[] Parameters { get; } - public Dictionary DefaultValues { get; } - public string ReturnType { get; } - public string StructName { get; } - public bool IsMemberFunction { get; } - public string Comment { get; } - public bool IsConstructor { get; } - public bool IsDestructor { get; } - - public OverloadDefinition( - string exportedName, - string friendlyName, - TypeReference[] parameters, - Dictionary defaultValues, - string returnType, - string structName, - string comment, - bool isConstructor, - bool isDestructor) - { - ExportedName = exportedName; - FriendlyName = friendlyName; - Parameters = parameters; - DefaultValues = defaultValues; - ReturnType = returnType.Replace("const", string.Empty).Replace("inline", string.Empty).Trim(); - StructName = structName; - IsMemberFunction = !string.IsNullOrEmpty(structName); - Comment = comment; - IsConstructor = isConstructor; - IsDestructor = isDestructor; - } - - public OverloadDefinition WithParameters(TypeReference[] parameters) - { - return new OverloadDefinition(ExportedName, FriendlyName, parameters, DefaultValues, ReturnType, StructName, Comment, IsConstructor, IsDestructor); - } - } - class MarshalledParameter { public MarshalledParameter(string marshalledType, bool isPinned, string varName, bool hasDefaultValue) @@ -1336,4 +814,4 @@ namespace CodeGenerator public bool HasDefaultValue { get; } public string PinTarget { get; internal set; } } -} +} \ No newline at end of file diff --git a/src/CodeGenerator/TypeInfo.cs b/src/CodeGenerator/TypeInfo.cs new file mode 100644 index 0000000..b4f94b4 --- /dev/null +++ b/src/CodeGenerator/TypeInfo.cs @@ -0,0 +1,105 @@ +using System.Collections.Generic; + +namespace CodeGenerator +{ + public class TypeInfo + { + public static readonly Dictionary WellKnownTypes = new Dictionary() + { + { "bool", "byte" }, + { "unsigned char", "byte" }, + { "signed char", "sbyte" }, + { "char", "byte" }, + { "ImWchar", "ushort" }, + { "ImFileHandle", "IntPtr" }, + { "ImU8", "byte" }, + { "ImS8", "sbyte" }, + { "ImU64", "ulong" }, + { "unsigned short", "ushort" }, + { "unsigned int", "uint" }, + { "ImVec2", "Vector2" }, + { "ImVec2_Simple", "Vector2" }, + { "ImVec3", "Vector3" }, + { "ImVec4", "Vector4" }, + { "ImWchar16", "ushort" }, //char is not blittable + { "ImVec4_Simple", "Vector4" }, + { "ImColor_Simple", "ImColor" }, + { "ImTextureID", "IntPtr" }, + { "ImGuiID", "uint" }, + { "ImDrawIdx", "ushort" }, + { "ImDrawListSharedData", "IntPtr" }, + { "ImDrawListSharedData*", "IntPtr" }, + { "ImU32", "uint" }, + { "ImDrawCallback", "IntPtr" }, + { "size_t", "uint" }, + { "ImGuiContext*", "IntPtr" }, + { "float[2]", "Vector2*" }, + { "float[3]", "Vector3*" }, + { "float[4]", "Vector4*" }, + { "int[2]", "int*" }, + { "int[3]", "int*" }, + { "int[4]", "int*" }, + { "float&", "float*" }, + { "ImVec2[2]", "Vector2*" }, + { "char* []", "byte**" }, + { "unsigned char[256]", "byte*"}, + }; + + public static readonly Dictionary WellKnownFieldReplacements = new Dictionary() + { + { "bool", "bool" }, // Force bool to remain as bool in type-safe wrappers. + }; + + public static readonly HashSet CustomDefinedTypes = new HashSet() + { + "ImVector", + "ImVec2", + "ImVec4", + "ImGuiStoragePair", + }; + + public static readonly Dictionary WellKnownDefaultValues = new Dictionary() + { + { "((void *)0)", "null" }, + { "((void*)0)", "null" }, + { "ImVec2(0,0)", "new Vector2()" }, + { "ImVec2(-1,0)", "new Vector2(-1, 0)" }, + { "ImVec2(1,0)", "new Vector2(1, 0)" }, + { "ImVec2(1,1)", "new Vector2(1, 1)" }, + { "ImVec2(0,1)", "new Vector2(0, 1)" }, + { "ImVec4(0,0,0,0)", "new Vector4()" }, + { "ImVec4(1,1,1,1)", "new Vector4(1, 1, 1, 1)" }, + { "ImDrawCornerFlags_All", "ImDrawCornerFlags.All" }, + { "FLT_MAX", "float.MaxValue" }, + { "(((ImU32)(255)<<24)|((ImU32)(255)<<16)|((ImU32)(255)<<8)|((ImU32)(255)<<0))", "0xFFFFFFFF" } + }; + + public static readonly Dictionary IdentifierReplacements = new Dictionary() + { + { "in", "@in" }, + { "out", "@out" }, + { "ref", "@ref" }, + }; + + public static readonly HashSet LegalFixedTypes = new HashSet() + { + "byte", + "sbyte", + "char", + "ushort", + "short", + "uint", + "int", + "ulong", + "long", + "float", + "double", + }; + + public static readonly HashSet SkippedFunctions = new HashSet() + { + "igInputText", + "igInputTextMultiline" + }; + } +} \ No newline at end of file