Example #1
0
        private static void EmitOverload(
            CSharpCodeWriter writer,
            OverloadDefinition overload,
            Dictionary <string, string> defaultValues,
            string selfName)
        {
            if (overload.Parameters.Where(tr => tr.Name.EndsWith("_begin") || tr.Name.EndsWith("_end"))
                .Any(tr => !defaultValues.ContainsKey(tr.Name)))
            {
                return;
            }

            Debug.Assert(!overload.IsMemberFunction || selfName != null);

            string nativeRet     = GetTypeString(overload.ReturnType, false);
            bool   isWrappedType = GetWrappedType(nativeRet, out string safeRet);

            if (!isWrappedType)
            {
                safeRet = GetSafeType(overload.ReturnType);
            }

            List <string> invocationArgs = new List <string>();

            MarshalledParameter[] marshalledParameters = new MarshalledParameter[overload.Parameters.Length];
            List <string>         preCallLines         = new List <string>();
            List <string>         postCallLines        = new List <string>();
            List <string>         byRefParams          = new List <string>();

            for (int i = 0; i < overload.Parameters.Length; i++)
            {
                if (i == 0 && selfName != null)
                {
                    continue;
                }

                TypeReference tr = overload.Parameters[i];
                if (tr.Name == "...")
                {
                    continue;
                }

                string correctedIdentifier = CorrectIdentifier(tr.Name);
                string nativeTypeName      = GetTypeString(tr.Type, tr.IsFunctionPointer);

                if (tr.Type == "char*")
                {
                    string textToEncode = correctedIdentifier;
                    bool   hasDefault   = false;
                    if (defaultValues.TryGetValue(tr.Name, out string defaultStrVal))
                    {
                        hasDefault = true;
                        if (!CorrectDefaultValue(defaultStrVal, tr, out string correctedDefault))
                        {
                            correctedDefault = defaultStrVal;
                        }

                        textToEncode = correctedDefault;
                    }

                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("string", false, nativeArgName, hasDefault);

                    if (textToEncode == "null")
                    {
                        preCallLines.Add($"byte* {nativeArgName} = null;");
                    }
                    else
                    {
                        preCallLines.Add($"int {correctedIdentifier}_byteCount = Encoding.UTF8.GetByteCount({textToEncode});");
                        preCallLines.Add($"byte* {nativeArgName} = stackalloc byte[{correctedIdentifier}_byteCount + 1];");
                        preCallLines.Add($"fixed (char* {correctedIdentifier}_ptr = {textToEncode})");
                        preCallLines.Add("{");
                        preCallLines.Add($"    int {nativeArgName}_offset = Encoding.UTF8.GetBytes({correctedIdentifier}_ptr, {textToEncode}.Length, {nativeArgName}, {correctedIdentifier}_byteCount);");
                        preCallLines.Add($"    {nativeArgName}[{nativeArgName}_offset] = 0;");
                        preCallLines.Add("}");
                    }
                }
                else if (tr.Type == "char* []")
                {
                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("string[]", false, nativeArgName, false);

                    preCallLines.Add($"int* {correctedIdentifier}_byteCounts = stackalloc int[{correctedIdentifier}.Length];");

                    preCallLines.Add($"int {correctedIdentifier}_byteCount = 0;");
                    preCallLines.Add($"for (int i = 0; i < {correctedIdentifier}.Length; i++)");
                    preCallLines.Add("{");
                    preCallLines.Add($"    string s = {correctedIdentifier}[i];");
                    preCallLines.Add($"    {correctedIdentifier}_byteCounts[i] = Encoding.UTF8.GetByteCount(s);");
                    preCallLines.Add($"    {correctedIdentifier}_byteCount += {correctedIdentifier}_byteCounts[i] + 1;");
                    preCallLines.Add("}");

                    preCallLines.Add($"byte* {nativeArgName}_data = stackalloc byte[{correctedIdentifier}_byteCount];");

                    preCallLines.Add("int offset = 0;");
                    preCallLines.Add($"for (int i = 0; i < {correctedIdentifier}.Length; i++)");
                    preCallLines.Add("{");
                    preCallLines.Add($"    string s = {correctedIdentifier}[i];");
                    preCallLines.Add($"    fixed (char* sPtr = s)");
                    preCallLines.Add("    {");
                    preCallLines.Add($"        offset += Encoding.UTF8.GetBytes(sPtr, s.Length, {nativeArgName}_data + offset, {correctedIdentifier}_byteCounts[i]);");
                    preCallLines.Add($"        offset += 1;");
                    preCallLines.Add($"        {nativeArgName}_data[offset] = 0;");
                    preCallLines.Add("    }");
                    preCallLines.Add("}");

                    preCallLines.Add($"byte** {nativeArgName} = stackalloc byte*[{correctedIdentifier}.Length];");
                    preCallLines.Add("offset = 0;");
                    preCallLines.Add($"for (int i = 0; i < {correctedIdentifier}.Length; i++)");
                    preCallLines.Add("{");
                    preCallLines.Add($"    {nativeArgName}[i] = &{nativeArgName}_data[offset];");
                    preCallLines.Add($"    offset += {correctedIdentifier}_byteCounts[i] + 1;");
                    preCallLines.Add("}");
                }
                else if (defaultValues.TryGetValue(tr.Name, out string defaultVal))
                {
                    if (!CorrectDefaultValue(defaultVal, tr, out string correctedDefault))
                    {
                        correctedDefault = defaultVal;
                    }
                    marshalledParameters[i] = new MarshalledParameter(nativeTypeName, false, correctedIdentifier, true);
                    preCallLines.Add($"{nativeTypeName} {correctedIdentifier} = {correctedDefault};");
                }
                else if (tr.Type == "bool")
                {
                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("bool", false, nativeArgName, false);
                    preCallLines.Add($"byte {nativeArgName} = {tr.Name} ? (byte)1 : (byte)0;");
                }
                else if (tr.Type == "bool*")
                {
                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("ref bool", false, nativeArgName, false);
                    preCallLines.Add($"byte {nativeArgName}_val = {correctedIdentifier} ? (byte)1 : (byte)0;");
                    preCallLines.Add($"byte* {nativeArgName} = &{nativeArgName}_val;");
                    postCallLines.Add($"{correctedIdentifier} = {nativeArgName}_val != 0;");
                }
                else if (tr.Type == "void*")
                {
                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("IntPtr", false, nativeArgName, false);
                    preCallLines.Add($"void* {nativeArgName} = {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)))
                {
                    marshalledParameters[i] = new MarshalledParameter(wrappedParamType, false, "native_" + tr.Name, false);
                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter(wrappedParamType, false, nativeArgName, false);
                    preCallLines.Add($"{tr.Type} {nativeArgName} = {correctedIdentifier}.NativePtr;");
                }
                else if ((tr.Type.EndsWith("*") || tr.Type.Contains("[") || tr.Type.EndsWith("&")) && tr.Type != "void*" && tr.Type != "ImGuiContext*")
                {
                    string nonPtrType;
                    if (tr.Type.Contains("["))
                    {
                        string wellKnown = s_wellKnownTypes[tr.Type];
                        nonPtrType = GetTypeString(wellKnown.Substring(0, wellKnown.Length - 1), false);
                    }
                    else
                    {
                        nonPtrType = GetTypeString(tr.Type.Substring(0, tr.Type.Length - 1), false);
                    }
                    string nativeArgName = "native_" + tr.Name;
                    bool   isOutParam    = tr.Name.Contains("out_");
                    string direction     = isOutParam ? "out" : "ref";
                    marshalledParameters[i]           = new MarshalledParameter($"{direction} {nonPtrType}", true, nativeArgName, false);
                    marshalledParameters[i].PinTarget = CorrectIdentifier(tr.Name);
                }
                else
                {
                    marshalledParameters[i] = new MarshalledParameter(nativeTypeName, false, correctedIdentifier, false);
                }

                if (!marshalledParameters[i].HasDefaultValue)
                {
                    invocationArgs.Add($"{marshalledParameters[i].MarshalledType} {correctedIdentifier}");
                }
            }

            string invocationList = string.Join(", ", invocationArgs);
            string friendlyName   = overload.FriendlyName;

            string staticPortion = selfName == null ? "static " : string.Empty;

            writer.PushBlock($"public {staticPortion}{safeRet} {friendlyName}({invocationList})");
            foreach (string line in preCallLines)
            {
                writer.WriteLine(line);
            }

            List <string> nativeInvocationArgs = new List <string>();

            if (selfName != null)
            {
                nativeInvocationArgs.Add(selfName);
            }

            for (int i = 0; i < marshalledParameters.Length; i++)
            {
                TypeReference       tr = overload.Parameters[i];
                MarshalledParameter mp = marshalledParameters[i];
                if (mp == null)
                {
                    continue;
                }
                if (mp.IsPinned)
                {
                    string nativePinType = GetTypeString(tr.Type, false);
                    writer.PushBlock($"fixed ({nativePinType} native_{tr.Name} = &{mp.PinTarget})");
                }

                nativeInvocationArgs.Add(mp.VarName);
            }

            string nativeInvocationStr = string.Join(", ", nativeInvocationArgs);
            string ret = safeRet == "void" ? string.Empty : $"{nativeRet} ret = ";

            string targetName = overload.ExportedName;

            if (targetName.Contains("nonUDT"))
            {
                targetName = targetName.Substring(0, targetName.IndexOf("_nonUDT"));
            }

            writer.WriteLine($"{ret}ImGuiNative.{targetName}({nativeInvocationStr});");

            foreach (string line in postCallLines)
            {
                writer.WriteLine(line);
            }

            if (safeRet != "void")
            {
                if (safeRet == "bool")
                {
                    writer.WriteLine("return ret != 0;");
                }
                else if (overload.ReturnType == "char*")
                {
                    writer.WriteLine("return Util.StringFromPtr(ret);");
                }
                else if (overload.ReturnType == "void*")
                {
                    writer.WriteLine("return (IntPtr)ret;");
                }
                else
                {
                    string retVal = isWrappedType ? $"new {safeRet}(ret)" : "ret";
                    writer.WriteLine($"return {retVal};");
                }
            }

            for (int i = 0; i < marshalledParameters.Length; i++)
            {
                MarshalledParameter mp = marshalledParameters[i];
                if (mp == null)
                {
                    continue;
                }
                if (mp.IsPinned)
                {
                    writer.PopBlock();
                }
            }

            writer.PopBlock();
        }
Example #2
0
        static void Main(string[] args)
        {
            string outputPath;

            if (args.Length > 0)
            {
                outputPath = args[0];
            }
            else
            {
                outputPath = 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);
                }

            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 =>
                {
                    return(new TypeReference(
                               v["name"].ToString(),
                               v["type"].ToString(),
                               v["template_type"]?.ToString(),
                               enums));
                }).ToArray();
                return(new TypeDefinition(name, fields));
            }).ToArray();

            FunctionDefinition[] functions = functionsJson.Children().Select(jt =>
            {
                JProperty jp = (JProperty)jt;
                string name  = jp.Name;
                if (name.Contains("GetMousePos"))
                {
                }

                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();

                    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 <TypeReference> parameters = new List <TypeReference>();
                    if (selfTypeName != null)
                    {
                        parameters.Add(new TypeReference("self", selfTypeName + "*", enums));
                    }

                    foreach (JToken p in val["argsT"])
                    {
                        string pType = p["type"].ToString();
                        string pName = p["name"].ToString();
                        parameters.Add(new TypeReference(pName, pType, enums));
                    }

                    Dictionary <string, string> defaultValues = new Dictionary <string, string>();
                    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();

                    return(new OverloadDefinition(
                               exportedName,
                               friendlyName,
                               parameters.ToArray(),
                               defaultValues,
                               returnType,
                               structName,
                               comment,
                               enums));
                }).Where(od => od != null).ToArray();

                return(new FunctionDefinition(name, overloads));
            }).ToArray();

            foreach (EnumDefinition ed in enums)
            {
                using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, ed.FriendlyName + ".gen.cs")))
                {
                    writer.PushBlock("namespace ImGuiNET");
                    if (ed.FriendlyName.Contains("Flags"))
                    {
                        writer.WriteLine("[System.Flags]");
                    }
                    writer.PushBlock($"public enum {ed.FriendlyName}");
                    foreach (EnumMember member in ed.Members)
                    {
                        string sanitizedName  = ed.SanitizeNames(member.Name);
                        string sanitizedValue = ed.SanitizeNames(member.Value);
                        writer.WriteLine($"{sanitizedName} = {sanitizedValue},");
                    }
                    writer.PopBlock();
                    writer.PopBlock();
                }
            }

            foreach (TypeDefinition td in types)
            {
                if (s_customDefinedTypes.Contains(td.Name))
                {
                    continue;
                }

                using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, td.Name + ".gen.cs")))
                {
                    writer.Using("System");
                    writer.Using("System.Numerics");
                    writer.Using("System.Runtime.CompilerServices");
                    writer.Using("System.Text");
                    writer.WriteLine(string.Empty);
                    writer.PushBlock("namespace ImGuiNET");

                    writer.PushBlock($"public unsafe partial struct {td.Name}");
                    foreach (TypeReference field in td.Fields)
                    {
                        string typeStr = GetTypeString(field.Type, field.IsFunctionPointer);

                        if (field.ArraySize != 0)
                        {
                            if (s_legalFixedTypes.Contains(typeStr))
                            {
                                writer.WriteLine($"public fixed {typeStr} {field.Name}[{field.ArraySize}];");
                            }
                            else
                            {
                                for (int i = 0; i < field.ArraySize; i++)
                                {
                                    writer.WriteLine($"public {typeStr} {field.Name}_{i};");
                                }
                            }
                        }
                        else
                        {
                            writer.WriteLine($"public {typeStr} {field.Name};");
                        }
                    }
                    writer.PopBlock();

                    string ptrTypeName = td.Name + "Ptr";
                    writer.PushBlock($"public unsafe partial struct {ptrTypeName}");
                    writer.WriteLine($"public {td.Name}* NativePtr {{ get; }}");
                    writer.WriteLine($"public {ptrTypeName}({td.Name}* nativePtr) => NativePtr = nativePtr;");
                    writer.WriteLine($"public {ptrTypeName}(IntPtr nativePtr) => NativePtr = ({td.Name}*)nativePtr;");
                    writer.WriteLine($"public static implicit operator {ptrTypeName}({td.Name}* nativePtr) => new {ptrTypeName}(nativePtr);");
                    writer.WriteLine($"public static implicit operator {td.Name}* ({ptrTypeName} wrappedPtr) => wrappedPtr.NativePtr;");
                    writer.WriteLine($"public static implicit operator {ptrTypeName}(IntPtr nativePtr) => new {ptrTypeName}(nativePtr);");

                    foreach (TypeReference field in td.Fields)
                    {
                        string typeStr = GetTypeString(field.Type, field.IsFunctionPointer);
                        string rawType = typeStr;

                        if (s_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";
                            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))
                            {
                                vectorElementType = wellKnown;
                            }

                            if (GetWrappedType(vectorElementType + "*", out string wrappedElementType))
                            {
                                writer.WriteLine($"public ImPtrVector<{wrappedElementType}> {field.Name} => new ImPtrVector<{wrappedElementType}>(NativePtr->{field.Name}, Unsafe.SizeOf<{vectorElementType}>());");
                            }
                            else
                            {
                                if (GetWrappedType(vectorElementType, out wrappedElementType))
                                {
                                    vectorElementType = wrappedElementType;
                                }
                                writer.WriteLine($"public ImVector<{vectorElementType}> {field.Name} => new ImVector<{vectorElementType}>(NativePtr->{field.Name});");
                            }
                        }
                        else
                        {
                            if (typeStr.Contains("*") && !typeStr.Contains("ImVector"))
                            {
                                if (GetWrappedType(typeStr, out string wrappedTypeName))
                                {
                                    writer.WriteLine($"public {wrappedTypeName} {field.Name} => new {wrappedTypeName}(NativePtr->{field.Name});");
                                }
                                else if (typeStr == "byte*" && IsStringFieldName(field.Name))
                                {
                                    writer.WriteLine($"public NullTerminatedString {field.Name} => new NullTerminatedString(NativePtr->{field.Name});");
                                }
                                else
                                {
                                    writer.WriteLine($"public IntPtr {field.Name} {{ get => (IntPtr)NativePtr->{field.Name}; set => NativePtr->{field.Name} = ({typeStr})value; }}");
                                }
                            }
                            else
                            {
                                writer.WriteLine($"public ref {typeStr} {field.Name} => ref Unsafe.AsRef<{typeStr}>(&NativePtr->{field.Name});");
                            }
                        }
                    }

                    foreach (FunctionDefinition fd in functions)
                    {
                        foreach (OverloadDefinition overload in fd.Overloads)
                        {
                            if (overload.StructName != td.Name)
                            {
                                continue;
                            }

                            if (overload.FriendlyName == overload.StructName)
                            {
                                continue;
                            }

                            string exportedName = overload.ExportedName;
                            if (exportedName.StartsWith("ig"))
                            {
                                exportedName = exportedName.Substring(2, exportedName.Length - 2);
                            }
                            if (exportedName.Contains("~"))
                            {
                                continue;
                            }
                            if (overload.Parameters.Any(tr => tr.Type.Contains('(')))
                            {
                                continue;
                            }                                                                       // TODO: Parse function pointer parameters.

                            bool hasVaList = false;
                            for (int i = 0; i < overload.Parameters.Length; i++)
                            {
                                TypeReference p         = overload.Parameters[i];
                                string        paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                                if (p.Name == "...")
                                {
                                    continue;
                                }

                                if (paramType == "va_list")
                                {
                                    hasVaList = true;
                                    break;
                                }
                            }
                            if (hasVaList)
                            {
                                continue;
                            }

                            KeyValuePair <string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending(
                                kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();

                            for (int i = overload.DefaultValues.Count; i >= 0; i--)
                            {
                                Dictionary <string, string> defaults = new Dictionary <string, string>();
                                for (int j = 0; j < i; j++)
                                {
                                    defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
                                }
                                EmitOverload(writer, overload, defaults, "NativePtr");
                            }
                        }
                    }
                    writer.PopBlock();

                    writer.PopBlock();
                }
            }

            using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, "ImGuiNative.gen.cs")))
            {
                writer.Using("System");
                writer.Using("System.Numerics");
                writer.Using("System.Runtime.InteropServices");
                writer.WriteLine(string.Empty);
                writer.PushBlock("namespace ImGuiNET");
                writer.PushBlock("public static unsafe partial class ImGuiNative");
                foreach (FunctionDefinition fd in functions)
                {
                    foreach (OverloadDefinition overload in fd.Overloads)
                    {
                        string exportedName = overload.ExportedName;
                        if (exportedName.Contains("~"))
                        {
                            continue;
                        }
                        if (overload.Parameters.Any(tr => tr.Type.Contains('(')))
                        {
                            continue;
                        }                                                                       // TODO: Parse function pointer parameters.

                        string ret = GetTypeString(overload.ReturnType, false);

                        bool          hasVaList  = false;
                        List <string> paramParts = new List <string>();
                        for (int i = 0; i < overload.Parameters.Length; i++)
                        {
                            TypeReference p         = overload.Parameters[i];
                            string        paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                            if (p.ArraySize != 0)
                            {
                                paramType = paramType + "*";
                            }

                            if (p.Name == "...")
                            {
                                continue;
                            }

                            paramParts.Add($"{paramType} {CorrectIdentifier(p.Name)}");

                            if (paramType == "va_list")
                            {
                                hasVaList = true;
                                break;
                            }
                        }

                        if (hasVaList)
                        {
                            continue;
                        }

                        string parameters = string.Join(", ", paramParts);

                        bool   isUdtVariant = exportedName.Contains("nonUDT");
                        string methodName   = isUdtVariant
                            ? exportedName.Substring(0, exportedName.IndexOf("_nonUDT"))
                            : exportedName;

                        if (isUdtVariant)
                        {
                            writer.WriteLine($"[DllImport(\"cimgui\", CallingConvention = CallingConvention.Cdecl, EntryPoint = \"{exportedName}\")]");
                        }
                        else
                        {
                            writer.WriteLine("[DllImport(\"cimgui\", CallingConvention = CallingConvention.Cdecl)]");
                        }
                        writer.WriteLine($"public static extern {ret} {methodName}({parameters});");
                    }
                }
                writer.PopBlock();
                writer.PopBlock();
            }

            using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, "ImGui.gen.cs")))
            {
                writer.Using("System");
                writer.Using("System.Numerics");
                writer.Using("System.Runtime.InteropServices");
                writer.Using("System.Text");
                writer.WriteLine(string.Empty);
                writer.PushBlock("namespace ImGuiNET");
                writer.PushBlock("public static unsafe partial class ImGui");
                foreach (FunctionDefinition fd in functions)
                {
                    foreach (OverloadDefinition overload in fd.Overloads)
                    {
                        string exportedName = overload.ExportedName;
                        if (exportedName.StartsWith("ig"))
                        {
                            exportedName = exportedName.Substring(2, exportedName.Length - 2);
                        }
                        if (exportedName.Contains("~"))
                        {
                            continue;
                        }
                        if (overload.Parameters.Any(tr => tr.Type.Contains('(')))
                        {
                            continue;
                        }                                                                       // TODO: Parse function pointer parameters.

                        bool hasVaList = false;
                        for (int i = 0; i < overload.Parameters.Length; i++)
                        {
                            TypeReference p         = overload.Parameters[i];
                            string        paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                            if (p.Name == "...")
                            {
                                continue;
                            }

                            if (paramType == "va_list")
                            {
                                hasVaList = true;
                                break;
                            }
                        }
                        if (hasVaList)
                        {
                            continue;
                        }

                        KeyValuePair <string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending(
                            kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();

                        for (int i = overload.DefaultValues.Count; i >= 0; i--)
                        {
                            if (overload.IsMemberFunction)
                            {
                                continue;
                            }
                            Dictionary <string, string> defaults = new Dictionary <string, string>();
                            for (int j = 0; j < i; j++)
                            {
                                defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
                            }
                            EmitOverload(writer, overload, defaults, null);
                        }
                    }
                }
                writer.PopBlock();
                writer.PopBlock();
            }
        }
Example #3
0
        private static void EmitOverload(
            CSharpCodeWriter writer,
            OverloadDefinition overload,
            Dictionary <string, string> defaultValues,
            string selfName,
            string classPrefix,
            int primitiveOverloadIndex       = -1,
            string primitiveOverloadEnumName = null)
        {
            if (overload.Parameters.Where(tr => tr.Name.EndsWith("_begin") || tr.Name.EndsWith("_end"))
                .Any(tr => !defaultValues.ContainsKey(tr.Name)))
            {
                return;
            }

            Debug.Assert(!overload.IsMemberFunction || selfName != null);

            string nativeRet     = GetTypeString(overload.ReturnType, false);
            bool   isWrappedType = GetWrappedType(nativeRet, out string safeRet);

            if (!isWrappedType)
            {
                safeRet = GetSafeType(overload.ReturnType);
            }

            List <string> invocationArgs = new List <string>();

            MarshalledParameter[] marshalledParameters = new MarshalledParameter[overload.Parameters.Length];
            List <string>         preCallLines         = new List <string>();
            List <string>         postCallLines        = new List <string>();
            List <string>         byRefParams          = new List <string>();
            int    selfIndex   = -1;
            int    pOutIndex   = -1;
            string overrideRet = null;

            for (int i = 0; i < overload.Parameters.Length; i++)
            {
                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;
                    bool   hasDefault   = false;
                    if (defaultValues.TryGetValue(tr.Name, out string defaultStrVal))
                    {
                        hasDefault = true;
                        if (!CorrectDefaultValue(defaultStrVal, tr, out string correctedDefault))
                        {
                            correctedDefault = defaultStrVal;
                        }

                        textToEncode = correctedDefault;
                    }

                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("string", false, nativeArgName, hasDefault);

                    if (textToEncode == "null")
                    {
                        preCallLines.Add($"byte* {nativeArgName} = null;");
                    }
                    else
                    {
                        preCallLines.Add($"byte* {nativeArgName};");
                        preCallLines.Add($"int {correctedIdentifier}_byteCount = 0;");
                        if (!hasDefault)
                        {
                            preCallLines.Add($"if ({textToEncode} != null)");
                            preCallLines.Add("{");
                        }
                        preCallLines.Add($"    {correctedIdentifier}_byteCount = Encoding.UTF8.GetByteCount({textToEncode});");
                        preCallLines.Add($"    if ({correctedIdentifier}_byteCount > Util.StackAllocationSizeLimit)");
                        preCallLines.Add($"    {{");
                        preCallLines.Add($"        {nativeArgName} = Util.Allocate({correctedIdentifier}_byteCount + 1);");
                        preCallLines.Add($"    }}");
                        preCallLines.Add($"    else");
                        preCallLines.Add($"    {{");
                        preCallLines.Add($"        byte* {nativeArgName}_stackBytes = stackalloc byte[{correctedIdentifier}_byteCount + 1];");
                        preCallLines.Add($"        {nativeArgName} = {nativeArgName}_stackBytes;");
                        preCallLines.Add($"    }}");
                        preCallLines.Add($"    int {nativeArgName}_offset = Util.GetUtf8({textToEncode}, {nativeArgName}, {correctedIdentifier}_byteCount);");
                        preCallLines.Add($"    {nativeArgName}[{nativeArgName}_offset] = 0;");

                        if (!hasDefault)
                        {
                            preCallLines.Add("}");
                            preCallLines.Add($"else {{ {nativeArgName} = null; }}");
                        }

                        postCallLines.Add($"if ({correctedIdentifier}_byteCount > Util.StackAllocationSizeLimit)");
                        postCallLines.Add($"{{");
                        postCallLines.Add($"    Util.Free({nativeArgName});");
                        postCallLines.Add($"}}");
                    }
                }
                else if (defaultValues.TryGetValue(tr.Name, out string defaultVal))
                {
                    if (!CorrectDefaultValue(defaultVal, tr, out string correctedDefault))
                    {
                        correctedDefault = defaultVal;
                    }
                    marshalledParameters[i] = new MarshalledParameter(nativeTypeName, false, correctedIdentifier, true);
                    preCallLines.Add($"{nativeTypeName} {correctedIdentifier} = {correctedDefault};");
                }
                else if (tr.Type == "char* []")
                {
                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("string[]", false, nativeArgName, false);

                    preCallLines.Add($"int* {correctedIdentifier}_byteCounts = stackalloc int[{correctedIdentifier}.Length];");

                    preCallLines.Add($"int {correctedIdentifier}_byteCount = 0;");
                    preCallLines.Add($"for (int i = 0; i < {correctedIdentifier}.Length; i++)");
                    preCallLines.Add("{");
                    preCallLines.Add($"    string s = {correctedIdentifier}[i];");
                    preCallLines.Add($"    {correctedIdentifier}_byteCounts[i] = Encoding.UTF8.GetByteCount(s);");
                    preCallLines.Add($"    {correctedIdentifier}_byteCount += {correctedIdentifier}_byteCounts[i] + 1;");
                    preCallLines.Add("}");

                    preCallLines.Add($"byte* {nativeArgName}_data = stackalloc byte[{correctedIdentifier}_byteCount];");

                    preCallLines.Add("int offset = 0;");
                    preCallLines.Add($"for (int i = 0; i < {correctedIdentifier}.Length; i++)");
                    preCallLines.Add("{");
                    preCallLines.Add($"    string s = {correctedIdentifier}[i];");
                    preCallLines.Add($"    fixed (char* sPtr = s)");
                    preCallLines.Add("    {");
                    preCallLines.Add($"        offset += Encoding.UTF8.GetBytes(sPtr, s.Length, {nativeArgName}_data + offset, {correctedIdentifier}_byteCounts[i]);");
                    preCallLines.Add($"        {nativeArgName}_data[offset] = 0;");
                    preCallLines.Add($"        offset += 1;");
                    preCallLines.Add("    }");
                    preCallLines.Add("}");

                    preCallLines.Add($"byte** {nativeArgName} = stackalloc byte*[{correctedIdentifier}.Length];");
                    preCallLines.Add("offset = 0;");
                    preCallLines.Add($"for (int i = 0; i < {correctedIdentifier}.Length; i++)");
                    preCallLines.Add("{");
                    preCallLines.Add($"    {nativeArgName}[i] = &{nativeArgName}_data[offset];");
                    preCallLines.Add($"    offset += {correctedIdentifier}_byteCounts[i] + 1;");
                    preCallLines.Add("}");
                }
                else if (tr.Type == "bool")
                {
                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("bool", false, nativeArgName, false);
                    preCallLines.Add($"byte {nativeArgName} = {tr.Name} ? (byte)1 : (byte)0;");
                }
                else if (tr.Type == "bool*")
                {
                    string nativeArgName = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("ref bool", false, nativeArgName, false);
                    preCallLines.Add($"byte {nativeArgName}_val = {correctedIdentifier} ? (byte)1 : (byte)0;");
                    preCallLines.Add($"byte* {nativeArgName} = &{nativeArgName}_val;");
                    postCallLines.Add($"{correctedIdentifier} = {nativeArgName}_val != 0;");
                }
                else if (tr.Type == "void*" || tr.Type == "ImWchar*")
                {
                    string nativePtrTypeName = tr.Type == "void*" ? "void*" : "ushort*";
                    string nativeArgName     = "native_" + tr.Name;
                    marshalledParameters[i] = new MarshalledParameter("IntPtr", false, nativeArgName, false);
                    preCallLines.Add($"{nativePtrTypeName} {nativeArgName} = ({nativePtrTypeName}){correctedIdentifier}.ToPointer();");
                }
                else if (GetWrappedType(tr.Type, out string wrappedParamType) &&
                         !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;
                    marshalledParameters[i] = new MarshalledParameter(wrappedParamType, false, nativeArgName, false);
                    preCallLines.Add($"{tr.Type} {nativeArgName} = {correctedIdentifier}.NativePtr;");
                }
                else if ((tr.Type.EndsWith("*") || tr.Type.Contains("[") || tr.Type.EndsWith("&")) && tr.Type != "void*" && tr.Type != "ImGuiContext*" && tr.Type != "ImPlotContext*" && tr.Type != "EditorContext*")
                {
                    string nonPtrType;
                    if (tr.Type.Contains("["))
                    {
                        string wellKnown = TypeInfo.WellKnownTypes[tr.Type];
                        nonPtrType = GetTypeString(wellKnown.Substring(0, wellKnown.Length - 1), false);
                    }
                    else
                    {
                        nonPtrType = GetTypeString(tr.Type.Substring(0, tr.Type.Length - 1), false);
                    }
                    string nativeArgName = "native_" + tr.Name;
                    bool   isOutParam    = tr.Name.Contains("out_") || tr.Name == "out";
                    string direction     = isOutParam ? "out" : "ref";
                    marshalledParameters[i]           = new MarshalledParameter($"{direction} {nonPtrType}", true, nativeArgName, false);
                    marshalledParameters[i].PinTarget = CorrectIdentifier(tr.Name);
                }
                else
                {
                    marshalledParameters[i] = new MarshalledParameter(nativeTypeName, false, correctedIdentifier, false);
                }

                if (!marshalledParameters[i].HasDefaultValue)
                {
                    invocationArgs.Add($"{marshalledParameters[i].MarshalledType} {correctedIdentifier}");
                }
            }

            string invocationList = string.Join(", ", invocationArgs);
            string friendlyName   = overload.FriendlyName;

            // If we have a primitive overload, we want to notify callers that they should be using the enum-based signatures instead
            if (primitiveOverloadIndex != -1)
            {
                writer.WriteLine("[Obsolete(\"Use method with non-primitive (enum) arguments instead.\")]");
            }

            string staticPortion = selfName == null ? "static " : string.Empty;

            writer.PushBlock($"public {staticPortion}{overrideRet ?? safeRet} {friendlyName}({invocationList})");
            foreach (string line in preCallLines)
            {
                writer.WriteLine(line);
            }

            List <string> nativeInvocationArgs = new List <string>();

            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)
                {
                    string nativePinType = GetTypeString(tr.Type, false);
                    writer.PushBlock($"fixed ({nativePinType} native_{tr.Name} = &{mp.PinTarget})");
                }

                var argString = mp.VarName;
                if (i == primitiveOverloadIndex)
                {
                    argString = $"({primitiveOverloadEnumName}){mp.VarName}";
                }
                nativeInvocationArgs.Add(argString);
            }

            string nativeInvocationStr = string.Join(", ", nativeInvocationArgs);
            string ret = safeRet == "void" ? string.Empty : $"{nativeRet} ret = ";

            string targetName = overload.ExportedName;

            if (targetName.Contains("nonUDT"))
            {
                targetName = targetName.Substring(0, targetName.IndexOf("_nonUDT"));
            }

            writer.WriteLine($"{ret}{classPrefix}Native.{targetName}({nativeInvocationStr});");

            foreach (string line in postCallLines)
            {
                writer.WriteLine(line);
            }

            if (safeRet != "void")
            {
                if (safeRet == "bool")
                {
                    writer.WriteLine("return ret != 0;");
                }
                else if (overload.ReturnType == "char*")
                {
                    writer.WriteLine("return Util.StringFromPtr(ret);");
                }
                else if (overload.ReturnType == "ImWchar*")
                {
                    writer.WriteLine("return (IntPtr)ret;");
                }
                else if (overload.ReturnType == "void*")
                {
                    writer.WriteLine("return (IntPtr)ret;");
                }
                else
                {
                    string retVal = isWrappedType ? $"new {safeRet}(ret)" : "ret";
                    writer.WriteLine($"return {retVal};");
                }
            }

            if (overrideRet != null)
            {
                writer.WriteLine("return __retval;");
            }

            for (int i = 0; i < marshalledParameters.Length; i++)
            {
                MarshalledParameter mp = marshalledParameters[i];
                if (mp == null)
                {
                    continue;
                }
                if (mp.IsPinned)
                {
                    writer.PopBlock();
                }
            }

            writer.PopBlock();
        }
Example #4
0
        static void Main(string[] args)
        {
            string outputPath;

            if (args.Length > 0)
            {
                outputPath = args[0];
            }
            else
            {
                outputPath = AppContext.BaseDirectory;
            }

            string libraryName;

            if (args.Length > 1)
            {
                libraryName = args[1];
            }
            else
            {
                libraryName = "cimgui";
            }

            string projectNamespace = libraryName switch
            {
                "cimgui" => "ImGuiNET",
                "cimplot" => "ImPlotNET",
                "cimnodes" => "imnodesNET",
                "cimguizmo" => "ImGuizmoNET",
                _ => throw new NotImplementedException($"Library \"{libraryName}\" is not supported.")
            };

            bool referencesImGui = libraryName switch
            {
                "cimgui" => false,
                "cimplot" => true,
                "cimnodes" => true,
                "cimguizmo" => true,
                _ => throw new NotImplementedException($"Library \"{libraryName}\" is not supported.")
            };

            string classPrefix = libraryName switch
            {
                "cimgui" => "ImGui",
                "cimplot" => "ImPlot",
                "cimnodes" => "imnodes",
                "cimguizmo" => "ImGuizmo",
                _ => throw new NotImplementedException($"Library \"{libraryName}\" is not supported.")
            };

            string dllName = libraryName switch
            {
                "cimgui" => "cimgui",
                "cimplot" => "cimplot",
                "cimnodes" => "cimnodes",
                "cimguizmo" => "cimguizmo",
                _ => throw new NotImplementedException()
            };

            string definitionsPath = Path.Combine(AppContext.BaseDirectory, "definitions", libraryName);
            var    defs            = new ImguiDefinitions();

            defs.LoadFrom(definitionsPath);

            Console.WriteLine($"Outputting generated code files to {outputPath}.");

            foreach (EnumDefinition ed in defs.Enums)
            {
                using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, ed.FriendlyName + ".gen.cs")))
                {
                    writer.PushBlock($"namespace {projectNamespace}");
                    if (ed.FriendlyName.Contains("Flags"))
                    {
                        writer.WriteLine("[System.Flags]");
                    }
                    writer.PushBlock($"public enum {ed.FriendlyName}");
                    foreach (EnumMember member in ed.Members)
                    {
                        string sanitizedName  = ed.SanitizeNames(member.Name);
                        string sanitizedValue = ed.SanitizeNames(member.Value);
                        writer.WriteLine($"{sanitizedName} = {sanitizedValue},");
                    }
                    writer.PopBlock();
                    writer.PopBlock();
                }
            }

            foreach (TypeDefinition td in defs.Types)
            {
                if (TypeInfo.CustomDefinedTypes.Contains(td.Name))
                {
                    continue;
                }

                using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, td.Name + ".gen.cs")))
                {
                    writer.Using("System");
                    writer.Using("System.Numerics");
                    writer.Using("System.Runtime.CompilerServices");
                    writer.Using("System.Text");
                    if (referencesImGui)
                    {
                        writer.Using("ImGuiNET");
                    }
                    writer.WriteLine(string.Empty);
                    writer.PushBlock($"namespace {projectNamespace}");

                    writer.PushBlock($"public unsafe partial struct {td.Name}");
                    foreach (TypeReference field in td.Fields)
                    {
                        string typeStr = GetTypeString(field.Type, field.IsFunctionPointer);

                        if (field.ArraySize != 0)
                        {
                            if (TypeInfo.LegalFixedTypes.Contains(typeStr))
                            {
                                writer.WriteLine($"public fixed {typeStr} {field.Name}[{field.ArraySize}];");
                            }
                            else
                            {
                                for (int i = 0; i < field.ArraySize; i++)
                                {
                                    writer.WriteLine($"public {typeStr} {field.Name}_{i};");
                                }
                            }
                        }
                        else
                        {
                            writer.WriteLine($"public {typeStr} {field.Name};");
                        }
                    }
                    writer.PopBlock();

                    string ptrTypeName = td.Name + "Ptr";
                    writer.PushBlock($"public unsafe partial struct {ptrTypeName}");
                    writer.WriteLine($"public {td.Name}* NativePtr {{ get; }}");
                    writer.WriteLine($"public {ptrTypeName}({td.Name}* nativePtr) => NativePtr = nativePtr;");
                    writer.WriteLine($"public {ptrTypeName}(IntPtr nativePtr) => NativePtr = ({td.Name}*)nativePtr;");
                    writer.WriteLine($"public static implicit operator {ptrTypeName}({td.Name}* nativePtr) => new {ptrTypeName}(nativePtr);");
                    writer.WriteLine($"public static implicit operator {td.Name}* ({ptrTypeName} wrappedPtr) => wrappedPtr.NativePtr;");
                    writer.WriteLine($"public static implicit operator {ptrTypeName}(IntPtr nativePtr) => new {ptrTypeName}(nativePtr);");

                    foreach (TypeReference field in td.Fields)
                    {
                        string typeStr = GetTypeString(field.Type, field.IsFunctionPointer);
                        string rawType = typeStr;

                        if (TypeInfo.WellKnownFieldReplacements.TryGetValue(field.Type, out string wellKnownFieldType))
                        {
                            typeStr = wellKnownFieldType;
                        }

                        if (field.ArraySize != 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 (TypeInfo.WellKnownTypes.TryGetValue(vectorElementType, out string wellKnown))
                            {
                                vectorElementType = wellKnown;
                            }

                            if (GetWrappedType(vectorElementType + "*", out string wrappedElementType))
                            {
                                writer.WriteLine($"public ImPtrVector<{wrappedElementType}> {field.Name} => new ImPtrVector<{wrappedElementType}>(NativePtr->{field.Name}, Unsafe.SizeOf<{vectorElementType}>());");
                            }
                            else
                            {
                                if (GetWrappedType(vectorElementType, out wrappedElementType))
                                {
                                    vectorElementType = wrappedElementType;
                                }
                                writer.WriteLine($"public ImVector<{vectorElementType}> {field.Name} => new ImVector<{vectorElementType}>(NativePtr->{field.Name});");
                            }
                        }
                        else
                        {
                            if (typeStr.Contains("*") && !typeStr.Contains("ImVector"))
                            {
                                if (GetWrappedType(typeStr, out string wrappedTypeName))
                                {
                                    writer.WriteLine($"public {wrappedTypeName} {field.Name} => new {wrappedTypeName}(NativePtr->{field.Name});");
                                }
                                else if (typeStr == "byte*" && IsStringFieldName(field.Name))
                                {
                                    writer.WriteLine($"public NullTerminatedString {field.Name} => new NullTerminatedString(NativePtr->{field.Name});");
                                }
                                else
                                {
                                    writer.WriteLine($"public IntPtr {field.Name} {{ get => (IntPtr)NativePtr->{field.Name}; set => NativePtr->{field.Name} = ({typeStr})value; }}");
                                }
                            }
                            else
                            {
                                writer.WriteLine($"public ref {typeStr} {field.Name} => ref Unsafe.AsRef<{typeStr}>(&NativePtr->{field.Name});");
                            }
                        }
                    }

                    foreach (FunctionDefinition fd in defs.Functions)
                    {
                        foreach (OverloadDefinition overload in fd.Overloads)
                        {
                            if (overload.StructName != td.Name)
                            {
                                continue;
                            }

                            if (overload.IsConstructor)
                            {
                                // TODO: Emit a static function on the type that invokes the native constructor.
                                // Also, add a "Dispose" function or similar.
                                continue;
                            }

                            string exportedName = overload.ExportedName;
                            if (exportedName.StartsWith("ig"))
                            {
                                exportedName = exportedName.Substring(2, exportedName.Length - 2);
                            }
                            if (exportedName.Contains("~"))
                            {
                                continue;
                            }
                            if (overload.Parameters.Any(tr => tr.Type.Contains('(')))
                            {
                                continue;
                            }                                                                       // TODO: Parse function pointer parameters.

                            bool hasVaList = false;
                            for (int i = 0; i < overload.Parameters.Length; i++)
                            {
                                TypeReference p         = overload.Parameters[i];
                                string        paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                                if (p.Name == "...")
                                {
                                    continue;
                                }

                                if (paramType == "va_list")
                                {
                                    hasVaList = true;
                                    break;
                                }
                            }
                            if (hasVaList)
                            {
                                continue;
                            }

                            KeyValuePair <string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending(
                                kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();

                            for (int i = overload.DefaultValues.Count; i >= 0; i--)
                            {
                                Dictionary <string, string> defaults = new Dictionary <string, string>();
                                for (int j = 0; j < i; j++)
                                {
                                    defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
                                }
                                EmitOverload(writer, overload, defaults, "NativePtr", classPrefix);

                                for (int j = 0; j < overload.Parameters.Length; j++)
                                {
                                    // We only want to replace enums that are not a default value for this overload
                                    if (overload.Parameters[j].IsEnum &&
                                        !defaults.TryGetValue(overload.Parameters[j].Name, out var unused))
                                    {
                                        var primitiveOverload =
                                            new OverloadDefinition(
                                                overload.ExportedName, overload.FriendlyName, (TypeReference[])overload.Parameters.Clone(), overload.DefaultValues,
                                                overload.ReturnType, overload.StructName, overload.Comment, overload.IsConstructor, overload.IsDestructor);
                                        var oldParam = primitiveOverload.Parameters[j];
                                        var newParam = new TypeReference(oldParam.Name, "int", oldParam.ArraySize, new EnumDefinition[] {});
                                        primitiveOverload.Parameters[j] = newParam;
                                        EmitOverload(writer, primitiveOverload, defaults, "NativePtr", classPrefix, j, oldParam.Type);
                                    }
                                }
                            }
                        }
                    }
                    writer.PopBlock();

                    writer.PopBlock();
                }
            }

            using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, $"{classPrefix}Native.gen.cs")))
            {
                writer.Using("System");
                writer.Using("System.Numerics");
                writer.Using("System.Runtime.InteropServices");
                if (referencesImGui)
                {
                    writer.Using("ImGuiNET");
                }
                writer.WriteLine(string.Empty);
                writer.PushBlock($"namespace {projectNamespace}");
                writer.PushBlock($"public static unsafe partial class {classPrefix}Native");
                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);

                        bool          hasVaList  = false;
                        List <string> paramParts = new List <string>();
                        for (int i = 0; i < overload.Parameters.Length; i++)
                        {
                            TypeReference p         = overload.Parameters[i];
                            string        paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                            if (p.ArraySize != 0)
                            {
                                paramType = paramType + "*";
                            }

                            if (p.Name == "...")
                            {
                                continue;
                            }

                            paramParts.Add($"{paramType} {CorrectIdentifier(p.Name)}");

                            if (paramType == "va_list")
                            {
                                hasVaList = true;
                                break;
                            }
                        }

                        if (hasVaList)
                        {
                            continue;
                        }

                        string parameters = string.Join(", ", paramParts);

                        bool   isUdtVariant = exportedName.Contains("nonUDT");
                        string methodName   = isUdtVariant
                            ? exportedName.Substring(0, exportedName.IndexOf("_nonUDT"))
                            : exportedName;

                        if (isUdtVariant)
                        {
                            writer.WriteLine($"[DllImport(\"{dllName}\", CallingConvention = CallingConvention.Cdecl, EntryPoint = \"{exportedName}\")]");
                        }
                        else
                        {
                            writer.WriteLine($"[DllImport(\"{dllName}\", CallingConvention = CallingConvention.Cdecl)]");
                        }
                        writer.WriteLine($"public static extern {ret} {methodName}({parameters});");
                    }
                }
                writer.PopBlock();
                writer.PopBlock();
            }

            using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, $"{classPrefix}.gen.cs")))
            {
                writer.Using("System");
                writer.Using("System.Numerics");
                writer.Using("System.Runtime.InteropServices");
                writer.Using("System.Text");
                if (referencesImGui)
                {
                    writer.Using("ImGuiNET");
                }
                writer.WriteLine(string.Empty);
                writer.PushBlock($"namespace {projectNamespace}");
                writer.PushBlock($"public static unsafe partial class {classPrefix}");
                foreach (FunctionDefinition fd in defs.Functions)
                {
                    if (TypeInfo.SkippedFunctions.Contains(fd.Name))
                    {
                        continue;
                    }

                    foreach (OverloadDefinition overload in fd.Overloads)
                    {
                        string exportedName = overload.ExportedName;
                        if (exportedName.StartsWith("ig"))
                        {
                            exportedName = exportedName.Substring(2, exportedName.Length - 2);
                        }
                        if (exportedName.Contains("~"))
                        {
                            continue;
                        }
                        if (overload.Parameters.Any(tr => tr.Type.Contains('(')))
                        {
                            continue;
                        }                                                                       // TODO: Parse function pointer parameters.

                        bool hasVaList = false;
                        for (int i = 0; i < overload.Parameters.Length; i++)
                        {
                            TypeReference p         = overload.Parameters[i];
                            string        paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                            if (p.Name == "...")
                            {
                                continue;
                            }

                            if (paramType == "va_list")
                            {
                                hasVaList = true;
                                break;
                            }
                        }
                        if (hasVaList)
                        {
                            continue;
                        }

                        KeyValuePair <string, string>[] orderedDefaults = overload.DefaultValues.OrderByDescending(
                            kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();

                        for (int i = overload.DefaultValues.Count; i >= 0; i--)
                        {
                            if (overload.IsMemberFunction)
                            {
                                continue;
                            }
                            Dictionary <string, string> defaults = new Dictionary <string, string>();
                            for (int j = 0; j < i; j++)
                            {
                                defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
                            }
                            EmitOverload(writer, overload, defaults, null, classPrefix);

                            for (int j = 0; j < overload.Parameters.Length; j++)
                            {
                                // We only want to replace enums that are not a default value for this overload
                                if (overload.Parameters[j].IsEnum &&
                                    !defaults.TryGetValue(overload.Parameters[j].Name, out var unused))
                                {
                                    var primitiveOverload =
                                        new OverloadDefinition(
                                            overload.ExportedName, overload.FriendlyName, (TypeReference[])overload.Parameters.Clone(), overload.DefaultValues,
                                            overload.ReturnType, overload.StructName, overload.Comment, overload.IsConstructor, overload.IsDestructor);
                                    var oldParam = primitiveOverload.Parameters[j];
                                    var newParam = new TypeReference(oldParam.Name, "int", oldParam.ArraySize, new EnumDefinition[] {});
                                    primitiveOverload.Parameters[j] = newParam;
                                    EmitOverload(writer, primitiveOverload, defaults, null, classPrefix, j, oldParam.Type);
                                }
                            }
                        }
                    }
                }
                writer.PopBlock();
                writer.PopBlock();
            }

            foreach (var method in defs.Variants)
            {
                foreach (var variant in method.Value.Parameters)
                {
                    if (!variant.Used)
                    {
                        Console.WriteLine($"Error: Variants targetting parameter {variant.Name} with type {variant.OriginalType} could not be applied to method {method.Key}.");
                    }
                }
            }
        }
Example #5
0
        static void Main(string[] args)
        {
            string outputPath;

            if (args.Length > 0)
            {
                outputPath = args[0];
            }
            else
            {
                outputPath = AppContext.BaseDirectory;
            }

            var def = new SpirVDefinition();

            def.LoadFrom(AppContext.BaseDirectory);

            Console.WriteLine($"SpirV specification version: {def.VersionString}");
            Console.WriteLine($"Outputting generated code files to {outputPath}.");

            using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, "Grammar.gen.cs")))
            {
                writer.PushBlock("namespace SpirVNET");
                writer.PushBlock($"public static class Grammar");
                writer.WriteLine($"public static uint MagicNumber => {def.MagicNumber};");
                writer.WriteLine($"public static uint MajorVersion => {def.MajorVersion};");
                writer.WriteLine($"public static uint MinorVersion => {def.MinorVersion};");
                writer.WriteLine($"public static uint Revision => {def.Revision};");
                writer.PopBlock();
                writer.PopBlock();
            }

            using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, "Instructions.gen.cs")))
            {
                writer.WriteLine("using System.IO;");
                writer.PushBlock("namespace SpirVNET.Instructions");

                writer.PushBlock("public enum OpCode");
                foreach (InstructionDefinition id in def.Instructions)
                {
                    writer.WriteLine($"{id.Name} = {id.OpCode},");
                }
                writer.PopBlock();

                writer.PushBlock("public static class InstructionCreator");
                writer.PushBlock("public static IInstruction CreateInstruction(OpCode opcode)");
                writer.PushBlock("switch(opcode)");
                HashSet <int> handledCases = new HashSet <int>();
                foreach (InstructionDefinition id in def.Instructions)
                {
                    // Skip aliases
                    if (handledCases.Contains(id.OpCode))
                    {
                        continue;
                    }

                    writer.WriteLine($"case OpCode.{id.Name}: return new {id.Name}();");
                    handledCases.Add(id.OpCode);
                }
                writer.WriteLine("default: throw new InvalidDataException($\"Opcode ({opcode}) does not exist!\");");
                writer.PopBlock();
                writer.PopBlock();
                writer.PopBlock();

                // Base interface
                writer.PushBlock("public interface IInstruction");
                writer.WriteLine("OpCode Opcode { get; }");
                writer.PopBlock();

                foreach (InstructionDefinition id in def.Instructions)
                {
                    writer.PushBlock($"public class {id.Name} : IInstruction");
                    writer.WriteLine($"public OpCode Opcode => OpCode.{id.Name};");
                    writer.PopBlock();
                }
                writer.PopBlock();
            }

            using (CSharpCodeWriter writer = new CSharpCodeWriter(Path.Combine(outputPath, "OperandKinds.gen.cs")))
            {
                writer.WriteLine("using System;");
                writer.PushBlock("namespace SpirVNET");

                foreach (OperandKindDefinition okd in def.OperandKinds)
                {
                    switch (okd.Category)
                    {
                    case OperandCategory.BitEnum:
                        writer.WriteLine("[Flags]");
                        writer.PushBlock($"enum {okd.Kind}");
                        break;

                    case OperandCategory.ValueEnum:
                        writer.PushBlock($"enum {okd.Kind}");
                        break;
                    }

                    switch (okd.Category)
                    {
                    case OperandCategory.BitEnum:
                    case OperandCategory.ValueEnum:
                        foreach (var enumerant in okd.Enumerables)
                        {
                            var name = Sanitize(enumerant.Name);
                            writer.WriteLine($"{name} = {enumerant.Value},");
                        }

                        writer.PopBlock();
                        break;
                    }
                }

                writer.PopBlock();
            }
        }
Example #6
0
        private static void Main(string[] args)
        {
            string outputPath;

            if (args.Length > 0)
            {
                outputPath = args[0];
            }
            else
            {
                outputPath = Path.Combine(AppContext.BaseDirectory, @"Generated\");
                if (!Directory.Exists(outputPath))
                {
                    Directory.CreateDirectory(outputPath);
                }
            }

            var defs = new ImguiDefinitions();

            defs.LoadFrom(AppContext.BaseDirectory);

            Console.WriteLine($"Outputting generated code files to {outputPath}.");

            foreach (var ed in defs.Enums)
            {
                using (var writer = new CSharpCodeWriter(Path.Combine(outputPath, ed.FriendlyName + ".gen.cs")))
                {
                    writer.PushBlock("namespace ImGuiNET");
                    if (ed.FriendlyName.Contains("Flags"))
                    {
                        writer.WriteLine("[System.Flags]");
                    }
                    writer.PushBlock($"public enum {ed.FriendlyName}");
                    foreach (var member in ed.Members)
                    {
                        var sanitizedName  = ed.SanitizeNames(member.Name);
                        var sanitizedValue = ed.SanitizeNames(member.Value);
                        writer.WriteLine($"{sanitizedName} = {sanitizedValue},");
                    }
                    writer.PopBlock();
                    writer.PopBlock();
                }
            }

            foreach (var td in defs.Types)
            {
                if (TypeInfo.CustomDefinedTypes.Contains(td.Name))
                {
                    continue;
                }

                using (var writer = new CSharpCodeWriter(Path.Combine(outputPath, td.Name + ".gen.cs")))
                {
                    writer.Using("System");
                    writer.Using("System.Numerics");
                    writer.Using("System.Runtime.CompilerServices");
                    writer.Using("System.Text");
                    writer.WriteLine(string.Empty);
                    writer.PushBlock("namespace ImGuiNET");

                    writer.PushBlock($"public unsafe partial struct {td.Name}");
                    foreach (var field in td.Fields)
                    {
                        var typeStr = GetTypeString(field.Type, field.IsFunctionPointer);

                        if (field.ArraySize != 0)
                        {
                            if (TypeInfo.LegalFixedTypes.Contains(typeStr))
                            {
                                writer.WriteLine($"public fixed {typeStr} {field.Name}[{field.ArraySize}];");
                            }
                            else
                            {
                                for (var i = 0; i < field.ArraySize; i++)
                                {
                                    writer.WriteLine($"public {typeStr} {field.Name}_{i};");
                                }
                            }
                        }
                        else
                        {
                            writer.WriteLine($"public {typeStr} {field.Name};");
                        }
                    }
                    writer.PopBlock();

                    var ptrTypeName = td.Name + "Ptr";
                    writer.PushBlock($"public unsafe partial struct {ptrTypeName}");
                    writer.WriteLine($"public {td.Name}* NativePtr {{ get; }}");
                    writer.WriteLine($"public {ptrTypeName}({td.Name}* nativePtr) => NativePtr = nativePtr;");
                    writer.WriteLine($"public {ptrTypeName}(IntPtr nativePtr) => NativePtr = ({td.Name}*)nativePtr;");
                    writer.WriteLine($"public static implicit operator {ptrTypeName}({td.Name}* nativePtr) => new {ptrTypeName}(nativePtr);");
                    writer.WriteLine($"public static implicit operator {td.Name}* ({ptrTypeName} wrappedPtr) => wrappedPtr.NativePtr;");
                    writer.WriteLine($"public static implicit operator {ptrTypeName}(IntPtr nativePtr) => new {ptrTypeName}(nativePtr);");

                    foreach (var field in td.Fields)
                    {
                        var typeStr = GetTypeString(field.Type, field.IsFunctionPointer);
                        var rawType = typeStr;

                        if (TypeInfo.WellKnownFieldReplacements.TryGetValue(field.Type, out var wellKnownFieldType))
                        {
                            typeStr = wellKnownFieldType;
                        }

                        if (field.ArraySize != 0)
                        {
                            var 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"))
                        {
                            var vectorElementType = GetTypeString(field.TemplateType, false);

                            if (TypeInfo.WellKnownTypes.TryGetValue(vectorElementType, out var wellKnown))
                            {
                                vectorElementType = wellKnown;
                            }

                            if (GetWrappedType(vectorElementType + "*", out var wrappedElementType))
                            {
                                writer.WriteLine($"public ImPtrVector<{wrappedElementType}> {field.Name} => new ImPtrVector<{wrappedElementType}>(NativePtr->{field.Name}, Unsafe.SizeOf<{vectorElementType}>());");
                            }
                            else
                            {
                                if (GetWrappedType(vectorElementType, out wrappedElementType))
                                {
                                    vectorElementType = wrappedElementType;
                                }
                                writer.WriteLine($"public ImVector<{vectorElementType}> {field.Name} => new ImVector<{vectorElementType}>(NativePtr->{field.Name});");
                            }
                        }
                        else
                        {
                            if (typeStr.Contains("*") && !typeStr.Contains("ImVector"))
                            {
                                if (GetWrappedType(typeStr, out var wrappedTypeName))
                                {
                                    writer.WriteLine($"public {wrappedTypeName} {field.Name} => new {wrappedTypeName}(NativePtr->{field.Name});");
                                }
                                else if (typeStr == "byte*" && IsStringFieldName(field.Name))
                                {
                                    writer.WriteLine($"public NullTerminatedString {field.Name} => new NullTerminatedString(NativePtr->{field.Name});");
                                }
                                else
                                {
                                    writer.WriteLine($"public IntPtr {field.Name} {{ get => (IntPtr)NativePtr->{field.Name}; set => NativePtr->{field.Name} = ({typeStr})value; }}");
                                }
                            }
                            else
                            {
                                writer.WriteLine($"public ref {typeStr} {field.Name} => ref Unsafe.AsRef<{typeStr}>(&NativePtr->{field.Name});");
                            }
                        }
                    }

                    foreach (var fd in defs.Functions)
                    {
                        foreach (var overload in fd.Overloads)
                        {
                            if (overload.StructName != td.Name)
                            {
                                continue;
                            }

                            if (overload.IsConstructor)
                            {
                                // TODO: Emit a static function on the type that invokes the native constructor.
                                // Also, add a "Dispose" function or similar.
                                continue;
                            }

                            var exportedName = overload.ExportedName;
                            if (exportedName.StartsWith("ig"))
                            {
                                exportedName = exportedName.Substring(2, exportedName.Length - 2);
                            }
                            if (exportedName.Contains("~"))
                            {
                                continue;
                            }
                            if (overload.Parameters.Any(tr => tr.Type.Contains('(')))
                            {
                                continue;
                            }                                                                                                   // TODO: Parse function pointer parameters.

                            var hasVaList = false;
                            for (var i = 0; i < overload.Parameters.Length; i++)
                            {
                                var p         = overload.Parameters[i];
                                var paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                                if (p.Name == "...")
                                {
                                    continue;
                                }

                                if (paramType == "va_list")
                                {
                                    hasVaList = true;
                                    break;
                                }
                            }
                            if (hasVaList)
                            {
                                continue;
                            }

                            var orderedDefaults = overload.DefaultValues.OrderByDescending(
                                kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();

                            for (var i = overload.DefaultValues.Count; i >= 0; i--)
                            {
                                var defaults = new Dictionary <string, string>();
                                for (var j = 0; j < i; j++)
                                {
                                    defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
                                }
                                EmitOverload(writer, overload, defaults, "NativePtr");
                            }
                        }
                    }
                    writer.PopBlock();

                    writer.PopBlock();
                }
            }

            using (var writer = new CSharpCodeWriter(Path.Combine(outputPath, "ImGuiNative.gen.cs")))
            {
                writer.Using("System");
                writer.Using("System.Numerics");
                writer.Using("System.Runtime.InteropServices");
                writer.WriteLine(string.Empty);
                writer.PushBlock("namespace ImGuiNET");
                writer.PushBlock("public static unsafe partial class ImGuiNative");
                foreach (var fd in defs.Functions)
                {
                    foreach (var overload in fd.Overloads)
                    {
                        var 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.

                        var ret = GetTypeString(overload.ReturnType, false);

                        var hasVaList  = false;
                        var paramParts = new List <string>();
                        for (var i = 0; i < overload.Parameters.Length; i++)
                        {
                            var p         = overload.Parameters[i];
                            var paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                            if (p.ArraySize != 0)
                            {
                                paramType = paramType + "*";
                            }

                            if (p.Name == "...")
                            {
                                continue;
                            }

                            paramParts.Add($"{paramType} {CorrectIdentifier(p.Name)}");

                            if (paramType == "va_list")
                            {
                                hasVaList = true;
                                break;
                            }
                        }

                        if (hasVaList)
                        {
                            continue;
                        }

                        var parameters = string.Join(", ", paramParts);

                        var isUdtVariant = exportedName.Contains("nonUDT");
                        var methodName   = isUdtVariant
                                                        ? exportedName.Substring(0, exportedName.IndexOf("_nonUDT"))
                                                        : exportedName;

                        writer.WriteLine("[SuppressGCTransition]");

                        if (isUdtVariant)
                        {
                            writer.WriteLine($"[DllImport(\"cimgui\", CallingConvention = CallingConvention.Cdecl, EntryPoint = \"{exportedName}\")]");
                        }
                        else
                        {
                            writer.WriteLine("[DllImport(\"cimgui\", CallingConvention = CallingConvention.Cdecl)]");
                        }
                        writer.WriteLine($"public static extern {ret} {methodName}({parameters});");
                    }
                }
                writer.PopBlock();
                writer.PopBlock();
            }

            using (var writer = new CSharpCodeWriter(Path.Combine(outputPath, "ImGui.gen.cs")))
            {
                writer.Using("System");
                writer.Using("System.Numerics");
                writer.Using("System.Runtime.InteropServices");
                writer.Using("System.Text");
                writer.WriteLine(string.Empty);
                writer.PushBlock("namespace ImGuiNET");
                writer.PushBlock("public static unsafe partial class ImGui");
                foreach (var fd in defs.Functions)
                {
                    if (TypeInfo.SkippedFunctions.Contains(fd.Name))
                    {
                        continue;
                    }

                    foreach (var overload in fd.Overloads)
                    {
                        var exportedName = overload.ExportedName;
                        if (exportedName.StartsWith("ig"))
                        {
                            exportedName = exportedName.Substring(2, exportedName.Length - 2);
                        }
                        if (exportedName.Contains("~"))
                        {
                            continue;
                        }
                        if (overload.Parameters.Any(tr => tr.Type.Contains('(')))
                        {
                            continue;
                        }                                                                                               // TODO: Parse function pointer parameters.

                        var hasVaList = false;
                        for (var i = 0; i < overload.Parameters.Length; i++)
                        {
                            var p         = overload.Parameters[i];
                            var paramType = GetTypeString(p.Type, p.IsFunctionPointer);
                            if (p.Name == "...")
                            {
                                continue;
                            }

                            if (paramType == "va_list")
                            {
                                hasVaList = true;
                                break;
                            }
                        }
                        if (hasVaList)
                        {
                            continue;
                        }

                        var orderedDefaults = overload.DefaultValues.OrderByDescending(
                            kvp => GetIndex(overload.Parameters, kvp.Key)).ToArray();

                        for (var i = overload.DefaultValues.Count; i >= 0; i--)
                        {
                            if (overload.IsMemberFunction)
                            {
                                continue;
                            }
                            var defaults = new Dictionary <string, string>();
                            for (var j = 0; j < i; j++)
                            {
                                defaults.Add(orderedDefaults[j].Key, orderedDefaults[j].Value);
                            }
                            EmitOverload(writer, overload, defaults, null);
                        }
                    }
                }
                writer.PopBlock();
                writer.PopBlock();
            }

            foreach (var method in defs.Variants)
            {
                foreach (var variant in method.Value.Parameters)
                {
                    if (!variant.Used)
                    {
                        Console.WriteLine($"Error: Variants targetting parameter {variant.Name} with type {variant.OriginalType} could not be applied to method {method.Key}.");
                    }
                }
            }
        }