#region License // // The Open Toolkit Library License // // Copyright (c) 2006 - 2013 Stefanos Apostolopoulos for the Open Toolkit library. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights to // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of // the Software, and to permit persons to whom the Software is furnished to do // so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // #endregion using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Xml.XPath; using Bind.Structures; using Delegate = Bind.Structures.Delegate; namespace Bind { using Enum = Bind.Structures.Enum; using Type = Bind.Structures.Type; class FuncProcessor { static readonly Regex Endings = new Regex( @"([fd]v?|u?[isb](64)?v?|v|i_v|fi)$", RegexOptions.Compiled); static readonly Regex EndingsNotToTrim = new Regex( "(sh|ib|[tdrey]s|[eE]n[vd]|bled" + "|Attrib|Access|Boolean|Coord|Depth|Feedbacks|Finish|Flag" + "|Groups|IDs|Indexed|Instanced|Pixels|Queries|Status|Tess|Through" + "|Uniforms|Varyings|Weight|Width)$", RegexOptions.Compiled); static readonly Regex EndingsAddV = new Regex("^0", RegexOptions.Compiled); readonly IEnumerable Overrides; IBind Generator { get; set; } Settings Settings { get { return Generator.Settings; } } public FuncProcessor(IBind generator, IEnumerable overrides) { if (generator == null) throw new ArgumentNullException("generator"); if (overrides == null) throw new ArgumentNullException("overrides"); Generator = generator; Overrides = overrides; } public FunctionCollection Process(EnumProcessor enum_processor, DocProcessor doc_processor, DelegateCollection delegates, EnumCollection enums, string apiname, string apiversion) { foreach (var file in Overrides) { Console.WriteLine("Processing funcs in {0}.", file); var nav = new XPathDocument(file).CreateNavigator(); foreach (var version in apiversion.Split('|')) { // Translate each delegate: // 1st using the elements in overrides.xml // 2nd using the hardcoded rules in FuncProcessor (e.g. char* -> string) foreach (var signatures in delegates.Values) { foreach (var d in signatures) { var replace = GetFuncOverride(nav, d, apiname, apiversion); TranslateExtension(d); TranslateReturnType(d, replace, nav, enum_processor, enums, apiname, version); TranslateParameters(d, replace, nav, enum_processor, enums, apiname, version); TranslateAttributes(d, replace, nav, apiname, version); } } // Create overloads for backwards compatibility, // by resolving elements var overload_list = new List(); foreach (var d in delegates.Values.Select(v => v.First())) { var overload_elements = GetFuncOverload(nav, d, apiname, apiversion); foreach (XPathNavigator overload_element in overload_elements) { var overload = new Delegate(d); TranslateReturnType(overload, overload_element, nav, enum_processor, enums, apiname, version); TranslateParameters(overload, overload_element, nav, enum_processor, enums, apiname, version); TranslateAttributes(overload, overload_element, nav, apiname, version); overload_list.Add(overload); } } foreach (var overload in overload_list) { delegates.Add(overload); } } } Console.WriteLine("Generating wrappers."); var wrappers = CreateWrappers(delegates, enums); Console.WriteLine("Generating convenience overloads."); wrappers.AddRange(CreateConvenienceOverloads(wrappers)); Console.WriteLine("Generating CLS compliant overloads."); wrappers = CreateCLSCompliantWrappers(wrappers, enums); Console.WriteLine("Removing non-CLS compliant duplicates."); wrappers = MarkCLSCompliance(wrappers); Console.WriteLine("Removing overloaded delegates."); RemoveOverloadedDelegates(delegates, wrappers); Console.WriteLine("Generating address table."); GenerateAddressTable(delegates); Console.WriteLine("Generating documentation."); GenerateDocumentation(wrappers, enum_processor, doc_processor); return wrappers; } #region Private Members void GenerateDocumentation(FunctionCollection wrappers, EnumProcessor enum_processor, DocProcessor doc_processor) { foreach (var list in wrappers) { foreach (var f in list.Value) { f.Documentation = doc_processor.Process(f, enum_processor); } } } void GenerateAddressTable(DelegateCollection delegates) { // We allocate one slot per entry point. Rules: // - All extensions get a slot // - Core functions get a slot, unless UseDllImports is enabled // - On Windows, core functions with version > 1.1 must be treated as extensions. // This is controlled via the UseWindowsCompatibleGL setting. // Entry points without a slot are assigned the magic slot index -1. // Generator.Rewrite detects this and generates a static DllImport call // instead of a calli instruction for these functions. int slot = -1; foreach (var list in delegates.Values) { var func = list.First(); if (func.RequiresSlot(Settings)) { slot++; foreach (var d in list) { d.Slot = slot; } } else { // Core function routed through DllImport - no slot generated foreach (var d in list) { d.Slot = -1; } } } } // When we have a list of overloaded delegates, make sure that // all generated wrappers use the first (original) delegate, not // the overloaded ones. This allows us to reduce the amount // of delegates we need to generate (1 per entry point instead // of 1 per overload), which improves loading times. static void RemoveOverloadedDelegates(DelegateCollection delegates, FunctionCollection wrappers) { foreach (var w in wrappers.Values.SelectMany(w => w)) { var d = delegates[w.Name].First(); w.WrappedDelegate = d; } } static string GetPath(string apipath, string apiname, string apiversion, string function, string extension) { var path = new StringBuilder(); path.Append("/signatures/"); path.Append(apipath); if (!String.IsNullOrEmpty(apiname) && !String.IsNullOrEmpty(apiversion)) { path.Append(String.Format( "[contains(concat('|', @name, '|'), '|{0}|') and " + "(contains(concat('|', @version, '|'), '|{1}|') or not(boolean(@version)))]", apiname, apiversion)); } else if (!String.IsNullOrEmpty(apiname)) { path.Append(String.Format("[contains(concat('|', @name, '|'), '|{0}|')]", apiname)); } else if (!String.IsNullOrEmpty(apiversion)) { path.Append(String.Format("[contains(concat('|', @version, '|'), '|{0}|') or not(boolean(@version))]", apiversion)); } if (function != null) { if (extension != null) { // match an override that has this specific extension // *or* one that has no extension at all (equivalent // to "match all possible extensions") path.Append(String.Format( "/function[contains(concat('|', @name, '|'), '|{0}|') and " + "(contains(concat('|', @extension, '|'), '|{1}|') or not(boolean(@extension)))]", function, extension)); } else { path.Append(String.Format( "/function[contains(concat('|', @name, '|'), '|{0}|')]", function)); } } return path.ToString(); } static string GetOverloadsPath(string apiname, string apiversion, string function, string extension) { return GetPath("overload", apiname, apiversion, function, extension); } static string GetOverridesPath(string apiname, string apiversion, string function, string extension) { return GetPath("replace", apiname, apiversion, function, extension); } void TranslateType(Bind.Structures.Type type, XPathNavigator function_override, XPathNavigator overrides, EnumProcessor enum_processor, EnumCollection enums, string category, string apiname) { Bind.Structures.Enum @enum; string s; category = enum_processor.TranslateEnumName(category); // Try to find out if it is an enum. If the type exists in the normal GLEnums list, use this. // Special case for Boolean which is there simply because C89 does not support bool types. // We don't really need that in C# bool normal = enums.TryGetValue(type.CurrentType, out @enum) || enums.TryGetValue(enum_processor.TranslateEnumName(type.CurrentType), out @enum); // Translate enum types type.IsEnum = false; if (normal && @enum.Name != "GLenum" && @enum.Name != "Boolean") { type.IsEnum = true; if ((Settings.Compatibility & Settings.Legacy.ConstIntEnums) != Settings.Legacy.None) { type.QualifiedType = "int"; } else { // Some functions and enums have the same names. // Make sure we reference the enums rather than the functions. if (normal) { type.QualifiedType = String.Format("{0}.{1}", Settings.EnumsOutput, @enum.Name); } } } else if (Generator.GLTypes.TryGetValue(type.CurrentType, out s)) { // Check if the parameter is a generic GLenum. If it is, search for a better match, // otherwise fallback to Settings.CompleteEnumName (named 'All' by default). if (s.Contains("GLenum") /*&& !String.IsNullOrEmpty(category)*/) { type.IsEnum = true; if ((Settings.Compatibility & Settings.Legacy.ConstIntEnums) != Settings.Legacy.None) { type.QualifiedType = "int"; } else { // Better match: enum.Name == function.Category (e.g. GL_VERSION_1_1 etc) // Note: for backwards compatibility we use "category" only for the gl api. // glcore, gles1 and gles2 use the All enum instead. if (apiname == "gl" && enums.ContainsKey(category)) { type.QualifiedType = String.Format("{0}{1}{2}", Settings.EnumsOutput, Settings.NamespaceSeparator, enum_processor.TranslateEnumName(category)); } else { type.QualifiedType = String.Format("{0}{1}{2}", Settings.EnumsOutput, Settings.NamespaceSeparator, Settings.CompleteEnumName); } } } else { type.QualifiedType = s; } } if ((type.Array == 0 && type.Pointer == 0 && !type.Reference) && (type.QualifiedType.ToLower().Contains("buffersize") || type.QualifiedType.ToLower().Contains("sizeiptr") || type.QualifiedType.Contains("size_t"))) { type.WrapperType |= WrapperTypes.SizeParameter; } type.CurrentType = Generator.CSTypes.ContainsKey(type.CurrentType) ? Generator.CSTypes[type.CurrentType] : type.CurrentType; // Make sure that enum parameters follow enum overrides, i.e. // if enum ErrorCodes is overriden to ErrorCode, then parameters // of type ErrorCodes should also be overriden to ErrorCode. XPathNavigator enum_override = overrides.SelectSingleNode( EnumProcessor.GetOverridesPath(apiname, type.CurrentType)); if (enum_override != null) { // For consistency - many overrides use string instead of String. if (enum_override.Value == "string") type.QualifiedType = "String"; else if (enum_override.Value == "StringBuilder") type.QualifiedType = "StringBuilder"; else type.CurrentType = enum_override.Value; } if (type.CurrentType == "IntPtr" && String.IsNullOrEmpty(type.PreviousType)) type.Pointer = 0; if (type.Pointer >= 3) { System.Diagnostics.Trace.WriteLine(String.Format( "[Error] Type '{0}' has a high pointer level. Bindings will be incorrect.", type)); } if (!type.IsEnum) { // Remove qualifier if type is not an enum // Resolves issues when replacing / overriding // an enum parameter with a non-enum type type.QualifiedType = type.CurrentType; } } static string TranslateExtension(string extension) { extension = extension.ToUpper(); if (extension.Length > 2) { extension = extension[0] + extension.Substring(1).ToLower(); } return extension; } void TranslateExtension(Delegate d) { d.Extension = TranslateExtension(d.Extension); } static string GetTrimmedExtension(string name, string extension) { // Extensions are always uppercase int index = name.LastIndexOf(extension.ToUpper()); if (index >= 0) { name = name.Remove(index); } return name; } // Trims unecessary suffices from the specified OpenGL function name. static string GetTrimmedName(Delegate d) { string name = d.Name; string extension = d.Extension; string trimmed_name = GetTrimmedExtension(name, extension); // Note: some endings should not be trimmed, for example: 'b' from Attrib. // Check the endingsNotToTrim regex for details. Match m = EndingsNotToTrim.Match(trimmed_name); if ((m.Index + m.Length) != trimmed_name.Length) { m = Endings.Match(trimmed_name); if (m.Length > 0 && m.Index + m.Length == trimmed_name.Length) { // Only trim endings, not internal matches. if (m.Value[m.Length - 1] == 'v' && EndingsAddV.IsMatch(name) && !name.StartsWith("Get") && !name.StartsWith("MatrixIndex")) { // Only trim ending 'v' when there is a number trimmed_name = trimmed_name.Substring(0, m.Index) + "v"; } else { if (!trimmed_name.EndsWith("xedv")) { trimmed_name = trimmed_name.Substring(0, m.Index); } else { trimmed_name = trimmed_name.Substring(0, m.Index + 1); } } } } return trimmed_name; } static XPathNodeIterator GetFuncOverload(XPathNavigator nav, Delegate d, string apiname, string apiversion) { // Try a few different extension variations that appear in the overrides xml file string[] extensions = { d.Extension, TranslateExtension(d.Extension), d.Extension.ToUpper() }; string trimmed_name = GetTrimmedName(d); XPathNodeIterator function_overload = null; foreach (var ext in extensions) { string extensionless_name = GetTrimmedExtension(d.Name, ext); function_overload = nav.Select(GetOverloadsPath(apiname, apiversion, d.Name, ext)); if (function_overload.Count != 0) break; function_overload = nav.Select(GetOverloadsPath(apiname, apiversion, extensionless_name, ext)); if (function_overload.Count != 0) break; function_overload = nav.Select(GetOverloadsPath(apiname, apiversion, trimmed_name, ext)); if (function_overload.Count != 0) break; } return function_overload; } static XPathNavigator GetFuncOverride(XPathNavigator nav, Delegate d, string apiname, string apiversion) { // Try a few different extension variations that appear in the overrides xml file string[] extensions = { d.Extension, TranslateExtension(d.Extension), d.Extension.ToUpper() }; string trimmed_name = GetTrimmedName(d); XPathNavigator function_override = null; foreach (var ext in extensions) { string extensionless_name = GetTrimmedExtension(d.Name, ext); function_override = nav.SelectSingleNode(GetOverridesPath(apiname, apiversion, d.Name, ext)) ?? nav.SelectSingleNode(GetOverridesPath(apiname, apiversion, extensionless_name, ext)) ?? nav.SelectSingleNode(GetOverridesPath(apiname, apiversion, trimmed_name, ext)); if (function_override != null) { break; } } return function_override; } void TrimName(Function f) { f.TrimmedName = GetTrimmedName(f); } static void ApplyParameterReplacement(Delegate d, XPathNavigator function_override) { if (function_override != null) { for (int i = 0; i < d.Parameters.Count; i++) { XPathNavigator param_override = function_override.SelectSingleNode(String.Format( "param[@name='{0}' or @index='{1}']", d.Parameters[i].RawName, i)); if (param_override != null) { foreach (XPathNavigator node in param_override.SelectChildren(XPathNodeType.Element)) { switch (node.Name) { case "type": d.Parameters[i].CurrentType = (string)node.TypedValue; break; case "name": d.Parameters[i].Name = (string)node.TypedValue; break; case "flow": d.Parameters[i].Flow = Parameter.GetFlowDirection((string)node.TypedValue); break; case "count": int count; if (Int32.TryParse(node.Value, out count)) d.Parameters[i].ElementCount = count; break; } } } } } } static void ApplyReturnTypeReplacement(Delegate d, XPathNavigator function_override) { if (function_override != null) { XPathNavigator return_override = function_override.SelectSingleNode("returns"); if (return_override != null) { d.ReturnType.CurrentType = return_override.Value; } } } // Translates the opengl return type to the equivalent C# type. // // First, we use the official typemap (gl.tm) to get the correct type. // Then we override this, when it is: // 1) A string (we have to use Marshal.PtrToStringAnsi, to avoid heap corruption) // 2) An array (translates to IntPtr) // 3) A generic object or void* (translates to IntPtr) // 4) A GLenum (translates to int on Legacy.Tao or GL.Enums.GLenum otherwise). // Return types must always be CLS-compliant, because .Net does not support overloading on return types. void TranslateReturnType(Delegate d, XPathNavigator function_override, XPathNavigator nav, EnumProcessor enum_processor, EnumCollection enums, string apiname, string apiversion) { ApplyReturnTypeReplacement(d, function_override); TranslateType(d.ReturnType, function_override, nav, enum_processor, enums, d.Category, apiname); if (d.ReturnType.CurrentType.ToLower() == "void" && d.ReturnType.Pointer != 0) { d.ReturnType.QualifiedType = "IntPtr"; d.ReturnType.Pointer--; d.ReturnType.WrapperType |= WrapperTypes.GenericReturnType; } if (d.ReturnType.CurrentType.ToLower() == "string") { d.ReturnType.QualifiedType = "IntPtr"; d.ReturnType.WrapperType |= WrapperTypes.StringReturnType; } if (d.ReturnType.CurrentType.ToLower() == "object") { d.ReturnType.QualifiedType = "IntPtr"; d.ReturnType.WrapperType |= WrapperTypes.GenericReturnType; } if (d.ReturnType.CurrentType.Contains("GLenum")) { if ((Settings.Compatibility & Settings.Legacy.ConstIntEnums) == Settings.Legacy.None) d.ReturnType.QualifiedType = String.Format("{0}{1}{2}", Settings.EnumsOutput, Settings.NamespaceSeparator, Settings.CompleteEnumName); else d.ReturnType.QualifiedType = "int"; } if (d.ReturnType.CurrentType.ToLower().Contains("bool")) { d.ReturnType.QualifiedType = "byte"; d.ReturnType.WrapperType |= WrapperTypes.BoolParameter; } d.ReturnType.CurrentType = GetCLSCompliantType(d.ReturnType); } Delegate GetCLSCompliantDelegate(Delegate d) { Delegate f = new Delegate(d); for (int i = 0; i < f.Parameters.Count; i++) { f.Parameters[i].CurrentType = GetCLSCompliantType(f.Parameters[i]); } f.ReturnType.CurrentType = GetCLSCompliantType(f.ReturnType); return f; } void TranslateParameters(Delegate d, XPathNavigator function_override, XPathNavigator nav, EnumProcessor enum_processor, EnumCollection enums, string apiname, string apiversion) { ApplyParameterReplacement(d, function_override); for (int i = 0; i < d.Parameters.Count; i++) { TranslateParameter(d.Parameters[i], function_override, nav, enum_processor, enums, d.Category, apiname); if (d.Parameters[i].CurrentType == "UInt16" && d.Name.Contains("LineStipple")) d.Parameters[i].WrapperType |= WrapperTypes.UncheckedParameter; } } void TranslateParameter(Parameter p, XPathNavigator function_override, XPathNavigator overrides, EnumProcessor enum_processor, EnumCollection enums, string category, string apiname) { TranslateType(p, function_override, overrides, enum_processor, enums, category, apiname); // Translate char* -> string. This simplifies the rest of the logic below if (p.CurrentType.ToLower().Contains("char") && p.Pointer > 0) { p.CurrentType = "string"; p.Pointer--; } // Find out the necessary wrapper types. if (p.CurrentType.ToLower() == "string" && p.Pointer == 0) { // char* -> IntPtr // Due to a bug in the Mono runtime, we need // to marshal [out] string parameters ourselves. // StringBuilder crashes at runtime. // For symmetry, and to avoid potential runtime bugs, // we will also marshal [in] string types manually. p.QualifiedType = "IntPtr"; p.WrapperType |= WrapperTypes.StringParameter; } if (p.CurrentType.ToLower() == "string" && p.Pointer >= 1) { // string* -> [In] String[] // [Out] StringBuilder[] parameter is not currently supported // Higher indirection levels are also not supported if (p.Flow == FlowDirection.Out) { throw new NotSupportedException("[Out] String* parameters are not currently supported."); } if (p.Pointer >= 2) { throw new NotSupportedException("String arrays with arity >= 2 are not currently supported."); } p.QualifiedType = "IntPtr"; p.Pointer = 0; p.Array = 0; p.WrapperType |= WrapperTypes.StringArrayParameter; } if (p.Pointer > 0 && p.WrapperType == 0) { if (p.QualifiedType.ToLower().StartsWith("void")) { p.QualifiedType = "IntPtr"; p.Pointer = 0; // Generic parameters cannot have pointers p.WrapperType |= WrapperTypes.GenericParameter; p.WrapperType |= WrapperTypes.ArrayParameter; p.WrapperType |= WrapperTypes.ReferenceParameter; } else { p.WrapperType |= WrapperTypes.ArrayParameter; p.WrapperType |= WrapperTypes.ReferenceParameter; p.WrapperType |= WrapperTypes.PointerParameter; } } if (Utilities.Keywords(Settings.Language).Contains(p.Name)) p.Name = Settings.KeywordEscapeCharacter + p.Name; // This causes problems with bool arrays //if (CurrentType.ToLower().Contains("bool")) // WrapperType = WrapperTypes.BoolParameter; } void TranslateAttributes(Delegate d, XPathNavigator function_override, XPathNavigator nav, string apiname, string apiversion) { if (function_override != null) { var version_override = function_override.SelectSingleNode("version"); if (version_override != null) { d.Version = version_override.Value; } var profile_override = function_override.SelectSingleNode("profile"); if (profile_override != null) { Debug.Print("Profile override not yet implemented"); } var name_override = function_override.SelectSingleNode("name"); if (name_override != null) { d.Name = name_override.Value; } var obsolete = function_override.GetAttribute("obsolete", String.Empty); if (!String.IsNullOrEmpty(obsolete)) { d.Obsolete = obsolete; } } } FunctionCollection CreateWrappers(DelegateCollection delegates, EnumCollection enums) { var wrappers = new FunctionCollection(); foreach (var d in delegates.Values.SelectMany(v => v)) { wrappers.AddRange(CreateNormalWrappers(d, enums)); } if ((Settings.Compatibility & Settings.Legacy.KeepUntypedEnums) != 0) { // Generate an "All" overload for every function that takes strongly-typed enums var overloads = new List(); foreach (var list in wrappers.Values) { overloads.AddRange(list.Where(f => f.Parameters.Any(p => p.IsEnum)).Select(f => { var fnew = new Function(f); fnew.Obsolete = "Use strongly-typed overload instead"; // Note that we can only overload parameters, not the return type foreach (var p in fnew.Parameters) { if (p.IsEnum) { p.CurrentType = Settings.CompleteEnumName; } } return fnew; })); } wrappers.AddRange(overloads); } return wrappers; } FunctionCollection CreateCLSCompliantWrappers(FunctionCollection functions, EnumCollection enums) { // If the function is not CLS-compliant (e.g. it contains unsigned parameters) // we need to create a CLS-Compliant overload. However, we should only do this // iff the opengl function does not contain unsigned/signed overloads itself // to avoid redefinitions. var wrappers = new FunctionCollection(); foreach (var list in functions.Values) { foreach (var f in list) { wrappers.AddChecked(f); if (!f.CLSCompliant) { // The return type must always be cls-compliant, // since we cannot overload on return types alone. f.ReturnType.CurrentType = GetCLSCompliantType(f.ReturnType); // Create a cls-compliant wrapper for the parameters Function cls = new Function(f); bool modified = false; for (int i = 0; i < f.Parameters.Count; i++) { cls.Parameters[i].CurrentType = GetCLSCompliantType(cls.Parameters[i]); if (cls.Parameters[i].CurrentType != f.Parameters[i].CurrentType) modified = true; } // Only add a cls-compliant overload if we have // changed a parameter. if (modified) { wrappers.AddChecked(cls); } } } } return wrappers; } static FunctionCollection MarkCLSCompliance(FunctionCollection collection) { //foreach (var w in // (from list in collection // from w1 in list.Value // from w2 in list.Value // where // w1.TrimmedName == w2.TrimmedName && // w1.Parameters.Count == w2.Parameters.Count && // ParametersDifferOnlyInReference(w1.Parameters, w2.Parameters) // select !w1.Parameters.HasReferenceParameters ? w1 : w2)) // { // results.Add(w); // } foreach (List wrappers in collection.Values) { var must_remove = new List(); for (int i = 0; i < wrappers.Count; i++) { for (int j = i + 1; j < wrappers.Count; j++) { if (wrappers[i].TrimmedName == wrappers[j].TrimmedName && wrappers[i].Parameters.Count == wrappers[j].Parameters.Count) { bool function_i_is_problematic = false; bool function_j_is_problematic = false; int k; for (k = 0; k < wrappers[i].Parameters.Count; k++) { if (wrappers[i].Parameters[k].CurrentType != wrappers[j].Parameters[k].CurrentType) break; if (wrappers[i].Parameters[k].DiffersOnlyOnReference(wrappers[j].Parameters[k])) if (wrappers[i].Parameters[k].Reference) function_i_is_problematic = true; else function_j_is_problematic = true; } if (k == wrappers[i].Parameters.Count) { if (function_i_is_problematic) must_remove.Add(i); if (function_j_is_problematic) must_remove.Add(j); } } } } int count = 0; must_remove.Sort(); foreach (var i in must_remove) { // Careful: whenever we remove a function, the total count // is reduced. We must account for that, or we will remove // the wrong function! wrappers.RemoveAt(i - count); count++; } } return collection; } string GetCLSCompliantType(Type type) { if (!type.CLSCompliant) { if (type.Pointer != 0 && Settings.Compatibility == Settings.Legacy.Tao) return "IntPtr"; switch (type.CurrentType) { case "UInt16": case "ushort": return "Int16"; case "UInt32": case "uint": return "Int32"; case "UInt64": case "ulong": return "Int64"; case "SByte": case "sbyte": return "Byte"; case "UIntPtr": return "IntPtr"; } } return type.CurrentType; } IEnumerable CreateNormalWrappers(Delegate d, EnumCollection enums) { Function f = new Function(d); TrimName(f); WrapReturnType(f); foreach (var wrapper in WrapParameters(f, enums)) { yield return wrapper; } } IEnumerable CreateConvenienceOverloads(FunctionCollection wrappers) { var convenience_wrappers = new List(); foreach (var d in wrappers.Values.SelectMany(w => w)) { if (d.Parameters.Count > 0 && d.Parameters.Count <= 2) { var p = d.Parameters.Last(); var r = d.ReturnType; var name = d.Name; bool is_candidate = true; is_candidate &= name.StartsWith("Get") || name.StartsWith("Gen") || name.StartsWith("Delete") || name.StartsWith("New"); is_candidate &= p.Pointer > 0; // if there is a specific count set, such as "4", then this function // returns a vector of specific dimensions and it would be wrong // to generate an overload that returns a value of different size. is_candidate &= p.ElementCount == 0 || p.ElementCount == 1; is_candidate &= r.CurrentType == "void" && r.Pointer == 0; Function f = null; if (is_candidate && p.Flow == FlowDirection.Out) { // Match Gen*|Get*|New*([Out] int[] names) methods f = CreateReturnTypeConvenienceWrapper(d); } else if (is_candidate && p.Flow != FlowDirection.Out) { // Match *Delete(int count, int[] names) methods if (d.Parameters.Count == 2) { f = CreateArrayReturnTypeConvenienceWrapper(d); } } if (f != null) { // If we have a convenience overload, we should turn its name from plural into singular f.TrimmedName = f.TrimmedName.Replace("Queries", "Query").TrimEnd('s'); convenience_wrappers.Add(f); } } // Check for IntPtr parameters that correspond to size_t (e.g. GLsizei) // and add Int32 overloads for convenience. { Function f = null; int i = 0; foreach (var p in d.Parameters) { if ((p.WrapperType & WrapperTypes.SizeParameter) != 0) { f = f ?? new Function(d); f.Parameters[i].QualifiedType = "Int32"; } i++; } if (f != null) { convenience_wrappers.Add(f); } } } return convenience_wrappers; } static Function CreateReturnTypeConvenienceWrapper(Function d) { var f = new Function(d); f.ReturnType = new Type(f.Parameters.Last()); f.ReturnType.Pointer = 0; f.Parameters.RemoveAt(f.Parameters.Count - 1); f.ReturnType.WrapperType |= WrapperTypes.ConvenienceReturnType; if (f.Parameters.Count > 0) { var p_size = f.Parameters.Last(); if (p_size.CurrentType.ToLower().StartsWith("int") && p_size.Pointer == 0) { f.Parameters.RemoveAt(f.Parameters.Count - 1); f.ReturnType.WrapperType |= WrapperTypes.ConvenienceArrayReturnType; } } return f; } static Function CreateArrayReturnTypeConvenienceWrapper(Function d) { var f = new Function(d); var p_array = f.Parameters.Last(); var p_size = f.Parameters[f.Parameters.Count - 2]; f.Parameters.RemoveAt(f.Parameters.Count - 2); p_array.WrapperType |= WrapperTypes.ConvenienceArrayType; // Since this is a 1-element overload, we don't need // array or reference wrappers. p_array.WrapperType &= ~( WrapperTypes.ReferenceParameter | WrapperTypes.ArrayParameter); p_array.Array = p_array.Pointer = 0; p_array.Reference = false; return f; } List GetWrapper(IDictionary> dictionary, WrapperTypes key, Function raw) { if (!dictionary.ContainsKey(key)) { dictionary.Add(key, new List()); if (raw != null) { dictionary[key].Add(new Function(raw)); } } return dictionary[key]; } public IEnumerable WrapParameters(Function func, EnumCollection enums) { if (func.Parameters.Count == 0) { // Functions without parameters do not need // parameter wrappers yield return func; yield break; } var wrappers = new Dictionary>(); for (int i = 0; i < func.Parameters.Count; i++) { var parameter = func.Parameters[i]; // Handle all non-generic parameters first. // Generics are handled in a second pass. if ((parameter.WrapperType & WrapperTypes.GenericParameter) == 0) { if ((parameter.WrapperType & WrapperTypes.ArrayParameter) != 0) { foreach (var wrapper in GetWrapper(wrappers, WrapperTypes.ArrayParameter, func)) { var p = wrapper.Parameters[i]; if (p.ElementCount == 1) { p.Reference = true; } else { p.Array++; } p.Pointer--; } } if ((parameter.WrapperType & WrapperTypes.ReferenceParameter) != 0) { foreach (var wrapper in GetWrapper(wrappers, WrapperTypes.ReferenceParameter, func)) { var p = wrapper.Parameters[i]; p.Reference = true; p.Pointer--; } } if ((parameter.WrapperType & WrapperTypes.PointerParameter) != 0) { foreach (var wrapper in GetWrapper(wrappers, WrapperTypes.PointerParameter, func)) { var p = wrapper.Parameters[i]; if (Settings.IsEnabled(Settings.Legacy.NoPublicUnsafeFunctions)) { p.QualifiedType = "IntPtr"; p.Pointer = 0; } } } if (parameter.WrapperType == 0 || (parameter.WrapperType & WrapperTypes.ConvenienceArrayType) != 0 || (parameter.WrapperType & WrapperTypes.ConvenienceReturnType) != 0 || (parameter.WrapperType & WrapperTypes.ConvenienceArrayReturnType) != 0) { // We don't need to do anything, just add this function directly // to the list of wrappers. GetWrapper(wrappers, parameter.WrapperType, func); } } } // Handle generic parameters if (wrappers.Count == 0) { // Some functions, such as VDPAUInit, only have generic parameters // This means no wrapper has been generated by any of the previous // transformations. Since the generic translation below operates on // existing wrappers, add one here to get the process started. wrappers.Add(WrapperTypes.None, new List { new Function(func) }); } var list = new List(); foreach (var wrapper in wrappers.Values.SelectMany(v => v)) { // Add generic 'ref T' wrapper Function generic_wrapper = null; for (int i = 0; i < wrapper.Parameters.Count; i++) { var parameter = wrapper.Parameters[i]; if ((parameter.WrapperType & WrapperTypes.GenericParameter) != 0) { generic_wrapper = generic_wrapper ?? new Function(wrapper); var p = generic_wrapper.Parameters[i]; p.Reference = true; p.Pointer = 0; p.Array = 0; p.Generic = true; p.QualifiedType = "T" + i.ToString(); p.Flow = FlowDirection.Undefined; } } if (generic_wrapper != null) { list.Add(generic_wrapper); } // Add the following wrappers: // 'IntPtr', 'T[]', 'T[,]' and 'T[,,]' for (int arity = 0; arity < 4; arity++) { generic_wrapper = null; for (int i = 0; i < wrapper.Parameters.Count; i++) { var parameter = wrapper.Parameters[i]; if ((parameter.WrapperType & WrapperTypes.GenericParameter) != 0) { generic_wrapper = generic_wrapper ?? new Function(wrapper); if (arity > 0) { // Overloading on array arity is not CLS-compliant generic_wrapper.CLSCompliant = false; } var p = generic_wrapper.Parameters[i]; p.Reference = false; p.Pointer = 0; p.Array = arity; if (arity == 0) { p.QualifiedType = "IntPtr"; } else { p.Generic = true; p.QualifiedType = "T" + i.ToString(); p.Flow = FlowDirection.Undefined; } } } if (generic_wrapper != null) { list.Add(generic_wrapper); } } } GetWrapper(wrappers, WrapperTypes.GenericParameter, null) .AddRange(list); // Handle string parameters foreach (var wrapper in wrappers.Values.SelectMany(v => v)) { for (int i = 0; i < wrapper.Parameters.Count; i++) { var p = wrapper.Parameters[i]; if ((p.WrapperType & WrapperTypes.StringParameter) != 0) { if (p.Flow == FlowDirection.Out) { p.QualifiedType = "StringBuilder"; } else { p.QualifiedType = "String"; } } if ((p.WrapperType & WrapperTypes.StringArrayParameter) != 0) { if (p.Flow == FlowDirection.Out) { throw new NotImplementedException(); } else { p.QualifiedType = "String"; p.Pointer = 0; p.Array = 1; } } } } // Return all generated wrappers foreach (var w in wrappers.Values.SelectMany(v => v).OrderBy(v => v)) { yield return w; } } static void WrapReturnType(Function func) { if ((func.ReturnType.WrapperType & WrapperTypes.StringReturnType) != 0) { func.ReturnType.QualifiedType = "String"; } if ((func.ReturnType.WrapperType & WrapperTypes.GenericReturnType) != 0) { // Nothing else we can do, using generics will break the runtime func.ReturnType.QualifiedType = "IntPtr"; } if ((func.ReturnType.WrapperType & WrapperTypes.BoolParameter) != 0) { func.ReturnType.QualifiedType = "bool"; } } #endregion } }