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