From f536011ce5868dee66377a748d1295d4dd50bc00 Mon Sep 17 00:00:00 2001 From: Jarl Gullberg Date: Tue, 10 Jul 2018 17:22:38 +0200 Subject: [PATCH] Annotate nullability in Bind. --- FodyWeavers.xml | 4 + OpenTK.sln | 1 + build/targets/jetbrains.props | 17 ++ .../Bind.ExternalAnnotations.xml | 2 + src/Generator.Bind/BindingsWriter.cs | 41 ++- src/Generator.Bind/DocProcessor.cs | 42 +-- src/Generator.Bind/EnumProcessor.cs | 241 ++++++++++-------- .../Extensions/TypeDefinitionExtensions.cs | 3 +- src/Generator.Bind/FuncProcessor.cs | 213 ++++++++++------ src/Generator.Bind/Generator.Bind.csproj | 1 + .../Generators/GeneratorBase.cs | 11 +- src/Generator.Bind/Generators/IGenerator.cs | 13 + src/Generator.Bind/ISpecificationReader.cs | 25 +- .../Structures/ConstantDefinition.cs | 5 +- .../Structures/DelegateCollection.cs | 7 +- .../Structures/DelegateDefinition.cs | 5 +- .../Structures/EnumDefinition.cs | 3 + .../Structures/FunctionCollection.cs | 7 +- .../Structures/FunctionDefinition.cs | 12 +- src/Generator.Bind/Structures/IDeclarable.cs | 5 +- .../Structures/ParameterCollection.cs | 10 +- .../Structures/ParameterDefinition.cs | 13 +- .../Structures/TypeDefinition.cs | 21 +- src/Generator.Bind/Utilities.cs | 13 +- src/Generator.Bind/Writers/SourceWriter.cs | 7 +- .../Writers/SourceWriterBlock.cs | 7 +- .../Writers/SourceWriterIndentation.cs | 4 +- src/Generator.Bind/XmlSpecificationReader.cs | 241 ++++++++++-------- 28 files changed, 607 insertions(+), 367 deletions(-) create mode 100644 FodyWeavers.xml create mode 100644 build/targets/jetbrains.props create mode 100644 src/Generator.Bind/Bind.ExternalAnnotations.xml diff --git a/FodyWeavers.xml b/FodyWeavers.xml new file mode 100644 index 0000000000..e940453232 --- /dev/null +++ b/FodyWeavers.xml @@ -0,0 +1,4 @@ + + + + diff --git a/OpenTK.sln b/OpenTK.sln index 39d1c14db3..117d280ce1 100644 --- a/OpenTK.sln +++ b/OpenTK.sln @@ -21,6 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{2E399A9C build/targets/netfx-mono.props = build/targets/netfx-mono.props build/targets/stylecop.props = build/targets/stylecop.props build/targets/nuget-common.props = build/targets/nuget-common.props + build/targets/jetbrains.props = build/targets/jetbrains.props EndProjectSection EndProject diff --git a/build/targets/jetbrains.props b/build/targets/jetbrains.props new file mode 100644 index 0000000000..8fc146ea09 --- /dev/null +++ b/build/targets/jetbrains.props @@ -0,0 +1,17 @@ + + + $(DefineConstants);JETBRAINS_ANNOTATIONS + + + + + false + CopyIfNewer + + + + + + + + diff --git a/src/Generator.Bind/Bind.ExternalAnnotations.xml b/src/Generator.Bind/Bind.ExternalAnnotations.xml new file mode 100644 index 0000000000..069a4dce7b --- /dev/null +++ b/src/Generator.Bind/Bind.ExternalAnnotations.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/Generator.Bind/BindingsWriter.cs b/src/Generator.Bind/BindingsWriter.cs index fdad6f90cc..95c1b271f8 100644 --- a/src/Generator.Bind/BindingsWriter.cs +++ b/src/Generator.Bind/BindingsWriter.cs @@ -35,6 +35,7 @@ using Bind.Structures; using Bind.Writers; using Humanizer; +using JetBrains.Annotations; namespace Bind { @@ -49,13 +50,18 @@ internal sealed class BindingsWriter /// Writes the bindings for the given generator to file. /// /// The generator. - public void WriteBindings(IGenerator generator) + public void WriteBindings([NotNull] IGenerator generator) { Generator = generator; WriteBindings(generator.Delegates, generator.Wrappers, generator.Enums); } - private void WriteBindings(DelegateCollection delegates, FunctionCollection wrappers, EnumCollection enums) + private void WriteBindings + ( + [NotNull] DelegateCollection delegates, + [NotNull] FunctionCollection wrappers, + [NotNull] EnumCollection enums + ) { var baseOutputPath = Program.Arguments.OutputPath; @@ -73,7 +79,7 @@ private void WriteBindings(DelegateCollection delegates, FunctionCollection wrap WriteWrappers(wrappers, delegates); } - private void WriteWrappers(FunctionCollection wrappers, DelegateCollection delegates) + private void WriteWrappers([NotNull] FunctionCollection wrappers, [NotNull] DelegateCollection delegates) { Trace.WriteLine($"Writing wrappers to:\t{Generator.Namespace}.{Generator.ClassName}"); @@ -85,7 +91,7 @@ private void WriteWrappers(FunctionCollection wrappers, DelegateCollection deleg Directory.CreateDirectory(wrappersOutputDirectory); } - // Create the interop setup neccesary + // Create the interop setup necessary var tempInteropFilePath = Path.GetTempFileName(); using (var outputFile = File.Open(tempInteropFilePath, FileMode.OpenOrCreate)) { @@ -271,7 +277,7 @@ private void WriteWrappers(FunctionCollection wrappers, DelegateCollection deleg } } - private void WriteMethod(SourceWriter sw, FunctionDefinition f) + private void WriteMethod([NotNull] SourceWriter sw, [NotNull] FunctionDefinition f) { WriteDocumentation(sw, f); @@ -301,7 +307,7 @@ private void WriteMethod(SourceWriter sw, FunctionDefinition f) } } - private void WriteDocumentation(SourceWriter sw, FunctionDefinition f) + private void WriteDocumentation([NotNull] SourceWriter sw, [NotNull] FunctionDefinition f) { var docs = f.DocumentationDefinition; @@ -427,7 +433,11 @@ private void WriteDocumentation(SourceWriter sw, FunctionDefinition f) } } - private void WriteConstants(SourceWriter sw, IEnumerable constants) + private void WriteConstants + ( + [NotNull] SourceWriter sw, + [NotNull] IEnumerable constants + ) { // Make sure everything is sorted. This will avoid random changes between // consecutive runs of the program. @@ -488,7 +498,7 @@ private void WriteConstants(SourceWriter sw, IEnumerable con } } - private void WriteEnums(EnumCollection enums, FunctionCollection wrappers) + private void WriteEnums([NotNull] EnumCollection enums, [NotNull] FunctionCollection wrappers) { // Build a dictionary of which functions use which enums var enumCounts = new Dictionary>(); @@ -588,7 +598,12 @@ private void WriteEnums(EnumCollection enums, FunctionCollection wrappers) /// A dictionary of the functions in which the enum is used. /// The enum definition. /// The usage string. - private string GetEnumUsageString(IReadOnlyDictionary> enumCounts, EnumDefinition @enum) + [NotNull] + private string GetEnumUsageString + ( + [NotNull] IReadOnlyDictionary> enumCounts, + [NotNull] EnumDefinition @enum + ) { var functions = enumCounts[@enum] .Select(w => @@ -614,7 +629,7 @@ private string GetEnumUsageString(IReadOnlyDictionary class. /// /// The API generator. - public DocProcessor(IGenerator generator) + public DocProcessor([NotNull] IGenerator generator) { Generator = generator ?? throw new ArgumentNullException(); @@ -57,6 +58,11 @@ public DocProcessor(IGenerator generator) foreach (var file in docFiles) { var name = Path.GetFileName(file); + if (name is null) + { + continue; + } + if (!_documentationFiles.ContainsKey(name)) { _documentationFiles.Add(name, file); @@ -64,6 +70,7 @@ public DocProcessor(IGenerator generator) } } + [NotNull] private IGenerator Generator { get; } /// @@ -72,7 +79,8 @@ public DocProcessor(IGenerator generator) /// The function. /// The enumeration processor. /// A documentation definition. - public DocumentationDefinition Process(FunctionDefinition f, EnumProcessor processor) + [NotNull] + public DocumentationDefinition Process([NotNull] FunctionDefinition f, [NotNull] EnumProcessor processor) { if (_documentationCache.ContainsKey(f.WrappedDelegateDefinition.Name)) { @@ -111,7 +119,12 @@ public DocumentationDefinition Process(FunctionDefinition f, EnumProcessor proce // found in the comments in the docs. // Todo: Some simple MathML tags do not include comments, find a solution. // Todo: Some files include more than 1 function - find a way to map these extra functions. - private DocumentationDefinition ProcessFile(string file, EnumProcessor processor) + [NotNull] + private DocumentationDefinition ProcessFile + ( + [NotNull, PathReference] string file, + [NotNull] EnumProcessor processor + ) { if (_lastFile == file) { @@ -158,22 +171,14 @@ private DocumentationDefinition ProcessFile(string file, EnumProcessor processor m = RemoveMathml.Match(text); } - XDocument doc = null; - try - { - doc = XDocument.Parse(text); - _cached = ToInlineDocs(doc, processor); - return _cached; - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - Console.WriteLine(doc.ToString()); - return null; - } + var doc = XDocument.Parse(text); + _cached = ToInlineDocs(doc, processor); + + return _cached; } - private DocumentationDefinition ToInlineDocs(XDocument doc, EnumProcessor enumProcessor) + [NotNull] + private DocumentationDefinition ToInlineDocs([NotNull] XNode doc, [NotNull] EnumProcessor enumProcessor) { if (doc == null || enumProcessor == null) { @@ -215,7 +220,8 @@ private DocumentationDefinition ToInlineDocs(XDocument doc, EnumProcessor enumPr return inline; } - private static string Cleanup(string text) + [NotNull] + private static string Cleanup([NotNull] string text) { return string.Join(" ", text diff --git a/src/Generator.Bind/EnumProcessor.cs b/src/Generator.Bind/EnumProcessor.cs index 068f07c4d4..b4d3a7200d 100644 --- a/src/Generator.Bind/EnumProcessor.cs +++ b/src/Generator.Bind/EnumProcessor.cs @@ -31,6 +31,7 @@ using System.Xml.XPath; using Bind.Generators; using Bind.Structures; +using JetBrains.Annotations; namespace Bind { @@ -46,7 +47,7 @@ internal class EnumProcessor /// /// The API generator. /// The override file paths. - public EnumProcessor(IGenerator generator, IEnumerable overrides) + public EnumProcessor([NotNull] IGenerator generator, [NotNull] IEnumerable overrides) { Generator = generator ?? throw new ArgumentNullException(nameof(generator)); _overrides = overrides ?? throw new ArgumentNullException(nameof(overrides)); @@ -60,7 +61,8 @@ public EnumProcessor(IGenerator generator, IEnumerable overrides) /// The base enum collection to use. /// The name of the API. /// A collection of enums. - public EnumCollection Process(EnumCollection enums, string apiname) + [NotNull] + public EnumCollection Process([NotNull] EnumCollection enums, [NotNull] string apiname) { foreach (var file in _overrides) { @@ -81,7 +83,8 @@ public EnumCollection Process(EnumCollection enums, string apiname) /// The enumeration name. /// The node path. /// Thrown if is null. - public static string GetOverridesNodePath(string apiname, string enumeration) + [NotNull] + public static string GetOverridesNodePath([CanBeNull] string apiname, [NotNull] string enumeration) { if (enumeration == null) { @@ -100,7 +103,8 @@ public static string GetOverridesNodePath(string apiname, string enumeration) return path.ToString(); } - private EnumCollection ProcessNames(EnumCollection enums, XPathNavigator nav, string apiname) + [NotNull] + private EnumCollection ProcessNames([NotNull] EnumCollection enums, [NotNull] XPathNavigator nav, [NotNull] string apiname) { var processedEnums = new EnumCollection(); foreach (var e in enums.Values) @@ -118,22 +122,21 @@ private EnumCollection ProcessNames(EnumCollection enums, XPathNavigator nav, st return processedEnums; } - private static string ReplaceName(XPathNavigator nav, string apiname, string name) + [NotNull] + private static string ReplaceName([NotNull] XPathNavigator nav, [NotNull] string apiname, [NotNull] string name) { var enumOverride = nav.SelectSingleNode(GetOverridesNodePath(apiname, name)); - if (enumOverride != null) + var nameOverride = enumOverride?.SelectSingleNode("name"); + + if (nameOverride != null) { - var nameOverride = enumOverride.SelectSingleNode("name"); - if (nameOverride != null) - { - name = nameOverride.Value; - } + name = nameOverride.Value; } return name; } - private static bool IsAlreadyProcessed(string name) + private static bool IsAlreadyProcessed([NotNull] string name) { var extension = Utilities.GetExtension(name, true); var unprocessed = false; @@ -149,7 +152,8 @@ private static bool IsAlreadyProcessed(string name) /// /// The original name. /// The translated name. - public string TranslateEnumName(string name) + [NotNull] + public string TranslateEnumName([NotNull] string name) { if (string.IsNullOrEmpty(name)) { @@ -161,86 +165,89 @@ public string TranslateEnumName(string name) return name; } - if (!IsAlreadyProcessed(name)) + if (IsAlreadyProcessed(name)) { - if (char.IsDigit(name[0])) - { - name = $"{Generator.ConstantPrefix}{name}"; - } + return name; + } - var translator = new StringBuilder(name); + if (char.IsDigit(name[0])) + { + name = $"{Generator.ConstantPrefix}{name}"; + } + + var translator = new StringBuilder(name); - // Split on IHV names and acronyms, to ensure that characters appearing after these name are uppercase. - var match = Utilities.Acronyms.Match(name); - var offset = 0; // Everytime we insert a match, we must increase offset to compensate. - while (match.Success) + // Split on IHV names and acronyms, to ensure that characters appearing after these name are uppercase. + var match = Utilities.Acronyms.Match(name); + var offset = 0; // Every time we insert a match, we must increase offset to compensate. + while (match.Success) + { + var insertPos = match.Index + match.Length + offset++; + translator.Insert(insertPos, "_"); + match = match.NextMatch(); + } + + name = translator.ToString(); + translator.Remove(0, translator.Length); + + // Process according to these rules: + // 1. if current char is '_', '-' remove it and make next char uppercase + // 2. if current char is or '0-9' keep it and make next char uppercase. + // 3. if current char is uppercase make next char lowercase. + // 4. if current char is lowercase, respect next char case. + var isAfterUnderscoreOrNumber = true; + var isPreviousUppercase = false; + foreach (var c in name) + { + char charToAdd; + if (c == '_' || c == '-') { - var insertPos = match.Index + match.Length + offset++; - translator.Insert(insertPos, "_"); - match = match.NextMatch(); + isAfterUnderscoreOrNumber = true; + continue; // skip this character } - name = translator.ToString(); - translator.Remove(0, translator.Length); - - // Process according to these rules: - // 1. if current char is '_', '-' remove it and make next char uppercase - // 2. if current char is or '0-9' keep it and make next char uppercase. - // 3. if current char is uppercase make next char lowercase. - // 4. if current char is lowercase, respect next char case. - var isAfterUnderscoreOrNumber = true; - var isPreviousUppercase = false; - foreach (var c in name) + if (char.IsDigit(c)) { - char charToAdd; - if (c == '_' || c == '-') - { - isAfterUnderscoreOrNumber = true; - continue; // skip this character - } - - if (char.IsDigit(c)) - { - isAfterUnderscoreOrNumber = true; - } + isAfterUnderscoreOrNumber = true; + } - if (isAfterUnderscoreOrNumber) - { - charToAdd = char.ToUpper(c); - } - else if (isPreviousUppercase) - { - charToAdd = char.ToLower(c); - } - else - { - charToAdd = c; - } + if (isAfterUnderscoreOrNumber) + { + charToAdd = char.ToUpper(c); + } + else if (isPreviousUppercase) + { + charToAdd = char.ToLower(c); + } + else + { + charToAdd = c; + } - translator.Append(charToAdd); + translator.Append(charToAdd); - isPreviousUppercase = char.IsUpper(c); - isAfterUnderscoreOrNumber = false; - } + isPreviousUppercase = char.IsUpper(c); + isAfterUnderscoreOrNumber = false; + } - // First letter should always be uppercase in order - // to conform to .Net style guidelines. - translator[0] = char.ToUpper(translator[0]); + // First letter should always be uppercase in order + // to conform to .Net style guidelines. + translator[0] = char.ToUpper(translator[0]); - // Replace a number of words that do not play well - // with the previous process (i.e. they have two - // consecutive uppercase letters). - translator.Replace("Pname", "PName"); - translator.Replace("AttribIp", "AttribIP"); - translator.Replace("SRgb", "Srgb"); + // Replace a number of words that do not play well + // with the previous process (i.e. they have two + // consecutive uppercase letters). + translator.Replace("Pname", "PName"); + translator.Replace("AttribIp", "AttribIP"); + translator.Replace("SRgb", "Srgb"); - name = translator.ToString(); - } + name = translator.ToString(); return name; } - private EnumCollection ProcessConstants(EnumCollection enums, XPathNavigator nav, string apiname) + [NotNull] + private EnumCollection ProcessConstants([NotNull] EnumCollection enums, [NotNull] XPathNavigator nav, [NotNull] string apiname) { foreach (var e in enums.Values) { @@ -259,6 +266,11 @@ private EnumCollection ProcessConstants(EnumCollection enums, XPathNavigator nav e.ConstantCollection = processedConstants; var enumOverride = nav.SelectSingleNode(GetOverridesNodePath(apiname, e.Name)); + if (enumOverride is null) + { + continue; + } + foreach (var c in e.ConstantCollection.Values) { ReplaceConstant(enumOverride, c); @@ -274,29 +286,29 @@ private EnumCollection ProcessConstants(EnumCollection enums, XPathNavigator nav return enums; } - private static void ReplaceConstant(XPathNavigator enumOverride, ConstantDefinition c) + private static void ReplaceConstant([NotNull] XPathNavigator enumOverride, [NotNull] ConstantDefinition c) { - if (enumOverride != null) + var constantOverride = enumOverride.SelectSingleNode($"token[@name='{c.OriginalName}']") ?? + enumOverride.SelectSingleNode($"token[@name={c.Name}]"); + + if (constantOverride == null) + { + return; + } + + foreach (XPathNavigator node in constantOverride.SelectChildren(XPathNodeType.Element)) { - var constantOverride = enumOverride.SelectSingleNode($"token[@name='{c.OriginalName}']") ?? - enumOverride.SelectSingleNode($"token[@name={c.Name}]"); - if (constantOverride != null) + switch (node.Name) { - foreach (XPathNavigator node in constantOverride.SelectChildren(XPathNodeType.Element)) - { - switch (node.Name) - { - case "name": - c.Name = (string)node.TypedValue; - break; - case "value": - c.Value = (string)node.TypedValue; - break; - case "reference": - c.Reference = (string)node.TypedValue; - break; - } - } + case "name": + c.Name = (string)node.TypedValue; + break; + case "value": + c.Value = (string)node.TypedValue; + break; + case "reference": + c.Reference = (string)node.TypedValue; + break; } } } @@ -307,7 +319,8 @@ private static void ReplaceConstant(XPathNavigator enumOverride, ConstantDefinit /// The original string. /// Whether or not the string is a value. /// The translated name. - public string TranslateConstantName(string s, bool isValue) + [NotNull] + public string TranslateConstantName([NotNull] string s, bool isValue) { if (string.IsNullOrEmpty(s)) { @@ -330,7 +343,7 @@ public string TranslateConstantName(string s, bool isValue) var nextCharUppercase = true; var isAfterDigit = false; - if (!isValue && char.IsDigit(s[0])) + if (char.IsDigit(s[0])) { s = Generator.ConstantPrefix + s; } @@ -372,7 +385,8 @@ public string TranslateConstantName(string s, bool isValue) return translator.ToString(); } - private string TranslateConstantValue(string value) + [NotNull] + private string TranslateConstantValue([NotNull] string value) { // Remove decorations to get a pure number (e.g. 0x80u -> 0x80). if (value.ToLower().StartsWith("0x")) @@ -402,28 +416,32 @@ private string TranslateConstantValue(string value) // (e.g. FOG_COORD_ARRAY_TYPE = GL_FOG_COORDINATE_ARRAY_TYPE) // In this case try searching all enums for the correct constant to alias (stupid opengl specs). // This turns every bare alias into a normal alias that is processed afterwards. - private static void ResolveBareAlias(ConstantDefinition c, EnumCollection enums) + private static void ResolveBareAlias([NotNull] ConstantDefinition c, [NotNull] EnumCollection enums) { // Constants are considered bare aliases when they don't have a reference and // their values are non-numeric. - if (string.IsNullOrEmpty(c.Reference) && !char.IsDigit(c.Value[0])) + if (!string.IsNullOrEmpty(c.Reference) || char.IsDigit(c.Value[0])) { - // Skip generic GLenum, as this doesn't help resolve references. - foreach (var e in enums.Values.Where(e => e.Name != "GLenum")) + return; + } + + // Skip generic GLenum, as this doesn't help resolve references. + foreach (var e in enums.Values.Where(e => e.Name != "GLenum")) + { + if (!e.ConstantCollection.ContainsKey(c.Value)) { - if (e.ConstantCollection.ContainsKey(c.Value)) - { - c.Reference = e.Name; - break; - } + continue; } + + c.Reference = e.Name; + break; } } // Resolve 'use' tokens by searching and replacing the correct // value from the enum collection. // Tokens that can't be resolved are removed. - private static void ResolveAliases(EnumDefinition e, EnumCollection enums) + private static void ResolveAliases([NotNull] EnumDefinition e, [NotNull] EnumCollection enums) { // Note that we have the removal must be a separate step, since // we cannot modify a collection while iterating with foreach. @@ -437,10 +455,9 @@ private static void ResolveAliases(EnumDefinition e, EnumCollection enums) } } - private static bool IsValue(string test) + private static bool IsValue([NotNull] string test) { // Check if the result is a number. - long number; bool isNumber; if (test.ToLower().StartsWith("0x")) { @@ -452,7 +469,7 @@ private static bool IsValue(string test) test, NumberStyles.Number, CultureInfo.InvariantCulture, - out number); + out _); } return isNumber; diff --git a/src/Generator.Bind/Extensions/TypeDefinitionExtensions.cs b/src/Generator.Bind/Extensions/TypeDefinitionExtensions.cs index 701436c172..c3cf9a704f 100644 --- a/src/Generator.Bind/Extensions/TypeDefinitionExtensions.cs +++ b/src/Generator.Bind/Extensions/TypeDefinitionExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Bind.Structures; +using JetBrains.Annotations; namespace Bind.Extensions { @@ -30,7 +31,7 @@ public static class TypeDefinitionExtensions /// /// The type definition. /// The qualified type name or alias. - public static string GetQualifiedTypeOrAlias(this TypeDefinition @this) + public static string GetQualifiedTypeOrAlias([NotNull] this TypeDefinition @this) { if (Aliases.ContainsKey(@this.QualifiedTypeName)) { diff --git a/src/Generator.Bind/FuncProcessor.cs b/src/Generator.Bind/FuncProcessor.cs index 0802f37d41..bcd603fc81 100644 --- a/src/Generator.Bind/FuncProcessor.cs +++ b/src/Generator.Bind/FuncProcessor.cs @@ -32,6 +32,7 @@ using System.Xml.XPath; using Bind.Generators; using Bind.Structures; +using JetBrains.Annotations; namespace Bind { @@ -60,7 +61,7 @@ internal class FuncProcessor /// /// The API generator. /// The override files. - public FuncProcessor(IGenerator generator, IEnumerable overrides) + public FuncProcessor([NotNull] IGenerator generator, [NotNull] IEnumerable overrides) { Generator = generator ?? throw new ArgumentNullException(nameof(generator)); _overrides = overrides ?? throw new ArgumentNullException(nameof(overrides)); @@ -78,14 +79,15 @@ public FuncProcessor(IGenerator generator, IEnumerable overrides) /// The name of the API to produce a function collection for. /// The version of the API to produce a function collection for. /// A collection of functions. + [NotNull] public FunctionCollection Process ( - EnumProcessor enumProcessor, - DocProcessor docProcessor, - DelegateCollection delegates, - EnumCollection enums, - string apiname, - string apiversion + [NotNull] EnumProcessor enumProcessor, + [NotNull] DocProcessor docProcessor, + [NotNull] DelegateCollection delegates, + [NotNull] EnumCollection enums, + [NotNull] string apiname, + [NotNull] string apiversion ) { foreach (var file in _overrides) @@ -97,16 +99,22 @@ string apiversion { // 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); + var funcOverride = GetFuncOverride(nav, d, apiname, apiversion); + if (funcOverride is null) + { + continue; + } + TranslateExtension(d); - TranslateReturnType(d, replace, nav, enumProcessor, enums, apiname); - TranslateParameters(d, replace, nav, enumProcessor, enums, apiname); - TranslateAttributes(d, replace); + TranslateReturnType(d, funcOverride, nav, enumProcessor, enums, apiname); + TranslateParameters(d, funcOverride, nav, enumProcessor, enums, apiname); + TranslateAttributes(d, funcOverride); } } @@ -115,13 +123,18 @@ string apiversion var overloadList = new List(); foreach (var d in delegates.Values.Select(v => v.First())) { - var overloadElements = GetFuncOverload(nav, d, apiname, apiversion); - foreach (XPathNavigator overloadElement in overloadElements) + var funcOverloads = GetFuncOverload(nav, d, apiname, apiversion); + if (funcOverloads is null) + { + continue; + } + + foreach (XPathNavigator funcOverload in funcOverloads) { var overload = new DelegateDefinition(d); - TranslateReturnType(overload, overloadElement, nav, enumProcessor, enums, apiname); - TranslateParameters(overload, overloadElement, nav, enumProcessor, enums, apiname); - TranslateAttributes(overload, overloadElement); + TranslateReturnType(overload, funcOverload, nav, enumProcessor, enums, apiname); + TranslateParameters(overload, funcOverload, nav, enumProcessor, enums, apiname); + TranslateAttributes(overload, funcOverload); overloadList.Add(overload); } } @@ -151,7 +164,7 @@ string apiversion return wrappers; } - private void GenerateDocumentation(FunctionCollection wrappers, EnumProcessor enumProcessor, DocProcessor docProcessor) + private void GenerateDocumentation([NotNull] FunctionCollection wrappers, EnumProcessor enumProcessor, DocProcessor docProcessor) { foreach (var list in wrappers) { @@ -162,7 +175,7 @@ private void GenerateDocumentation(FunctionCollection wrappers, EnumProcessor en } } - private void GenerateAddressTable(DelegateCollection delegates) + private void GenerateAddressTable([NotNull] DelegateCollection delegates) { // We allocate one slot per entry point. Rules: // - All extensions get a slot @@ -189,7 +202,7 @@ private void GenerateAddressTable(DelegateCollection delegates) // 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. - private static void RemoveOverloadedDelegates(DelegateCollection delegates, FunctionCollection wrappers) + private static void RemoveOverloadedDelegates(DelegateCollection delegates, [NotNull] FunctionCollection wrappers) { foreach (var w in wrappers.Values.SelectMany(w => w)) { @@ -198,11 +211,20 @@ private static void RemoveOverloadedDelegates(DelegateCollection delegates, Func } } - private static string GetPath(string apipath, string apiname, string apiversion, string function, string extension) + [NotNull] + private static string GetPath + ( + [NotNull] string apipath, + [CanBeNull] string apiname, + [CanBeNull] string apiversion, + [CanBeNull] string function, + [CanBeNull] string extension + ) { var path = new StringBuilder(); path.Append("/signatures/"); path.Append(apipath); + if (!string.IsNullOrEmpty(apiname) && !string.IsNullOrEmpty(apiversion)) { path.Append($"[contains(concat('|', @name, '|'), '|{apiname}|') and " + @@ -238,17 +260,39 @@ private static string GetPath(string apipath, string apiname, string apiversion, return path.ToString(); } - private static string GetOverloadsPath(string apiname, string apiversion, string function, string extension) + [NotNull] + private static string GetOverloadsPath + ( + [CanBeNull] string apiname, + [CanBeNull] string apiversion, + [CanBeNull] string function, + [CanBeNull] string extension + ) { return GetPath("overload", apiname, apiversion, function, extension); } - private static string GetOverridesPath(string apiname, string apiversion, string function, string extension) + [NotNull] + private static string GetOverridesPath + ( + [CanBeNull] string apiname, + [CanBeNull] string apiversion, + [CanBeNull] string function, + [CanBeNull] string extension + ) { return GetPath("replace", apiname, apiversion, function, extension); } - private void TranslateType(TypeDefinition typeDefinition, XPathNavigator overrides, EnumProcessor enumProcessor, EnumCollection enums, string category, string apiname) + private void TranslateType + ( + [NotNull] TypeDefinition typeDefinition, + [NotNull] XPathNavigator overrides, + [NotNull] EnumProcessor enumProcessor, + [NotNull] EnumCollection enums, + [NotNull] string category, + [NotNull] string apiname + ) { category = enumProcessor.TranslateEnumName(category); @@ -352,7 +396,8 @@ private void TranslateType(TypeDefinition typeDefinition, XPathNavigator overrid } } - private static string TranslateExtension(string extension) + [NotNull] + private static string TranslateExtension([NotNull] string extension) { extension = extension.ToUpper(); if (extension.Length > 2) @@ -363,12 +408,13 @@ private static string TranslateExtension(string extension) return extension; } - private void TranslateExtension(DelegateDefinition d) + private void TranslateExtension([NotNull] DelegateDefinition d) { d.ExtensionName = TranslateExtension(d.ExtensionName); } - private static string GetTrimmedExtension(string name, string extension) + [NotNull] + private static string GetTrimmedExtension([NotNull] string name, [NotNull] string extension) { // Extensions are always uppercase var index = name.LastIndexOf(extension.ToUpper(), StringComparison.Ordinal); @@ -381,7 +427,8 @@ private static string GetTrimmedExtension(string name, string extension) } // Trims unecessary suffices from the specified OpenGL function name. - private static string GetTrimmedName(DelegateDefinition d) + [NotNull] + private static string GetTrimmedName([NotNull] DelegateDefinition d) { var name = d.Name; var extension = d.ExtensionName; @@ -420,7 +467,14 @@ private static string GetTrimmedName(DelegateDefinition d) return trimmedName; } - private static XPathNodeIterator GetFuncOverload(XPathNavigator nav, DelegateDefinition d, string apiname, string apiversion) + [CanBeNull] + private static XPathNodeIterator GetFuncOverload + ( + [NotNull] XPathNavigator nav, + [NotNull] DelegateDefinition d, + [NotNull] string apiname, + [NotNull] string apiversion + ) { // Try a few different extension variations that appear in the overrides xml file string[] extensions = { d.ExtensionName, TranslateExtension(d.ExtensionName), d.ExtensionName.ToUpper() }; @@ -452,7 +506,14 @@ private static XPathNodeIterator GetFuncOverload(XPathNavigator nav, DelegateDef return functionOverload; } - private static XPathNavigator GetFuncOverride(XPathNavigator nav, DelegateDefinition d, string apiname, string apiversion) + [CanBeNull] + private static XPathNavigator GetFuncOverride + ( + [NotNull] XPathNavigator nav, + [NotNull] DelegateDefinition d, + [NotNull] string apiname, + [NotNull] string apiversion + ) { // Try a few different extension variations that appear in the overrides xml file string[] extensions = { d.ExtensionName, TranslateExtension(d.ExtensionName), d.ExtensionName.ToUpper() }; @@ -476,18 +537,17 @@ private static XPathNavigator GetFuncOverride(XPathNavigator nav, DelegateDefini return functionOverride; } - private void TrimName(FunctionDefinition f) + private void TrimName([NotNull] FunctionDefinition f) { f.TrimmedName = GetTrimmedName(f); } - private static void ApplyParameterReplacement(DelegateDefinition d, XPathNavigator functionOverride) + private static void ApplyParameterReplacement + ( + [NotNull] DelegateDefinition d, + [NotNull] XPathNavigator functionOverride + ) { - if (functionOverride == null) - { - return; - } - for (var i = 0; i < d.Parameters.Count; i++) { var paramOverride = functionOverride.SelectSingleNode( @@ -520,9 +580,12 @@ private static void ApplyParameterReplacement(DelegateDefinition d, XPathNavigat } } - private static void ApplyReturnTypeReplacement(DelegateDefinition d, XPathNavigator functionOverride) + private static void ApplyReturnTypeReplacement + ( + [NotNull] DelegateDefinition d, [NotNull] XPathNavigator functionOverride + ) { - var returnOverride = functionOverride?.SelectSingleNode("returns"); + var returnOverride = functionOverride.SelectSingleNode("returns"); if (returnOverride != null) { d.ReturnTypeDefinition.TypeName = returnOverride.Value; @@ -540,12 +603,12 @@ private static void ApplyReturnTypeReplacement(DelegateDefinition d, XPathNaviga // Return types must always be CLS-compliant, because .Net does not support overloading on return types. private void TranslateReturnType ( - DelegateDefinition d, - XPathNavigator functionOverride, - XPathNavigator nav, - EnumProcessor enumProcessor, - EnumCollection enums, - string apiname + [NotNull] DelegateDefinition d, + [NotNull] XPathNavigator functionOverride, + [NotNull] XPathNavigator nav, + [NotNull] EnumProcessor enumProcessor, + [NotNull] EnumCollection enums, + [NotNull] string apiname ) { ApplyReturnTypeReplacement(d, functionOverride); @@ -585,12 +648,12 @@ string apiname private void TranslateParameters ( - DelegateDefinition d, - XPathNavigator functionOverride, - XPathNavigator nav, - EnumProcessor enumProcessor, - EnumCollection enums, - string apiname + [NotNull] DelegateDefinition d, + [NotNull] XPathNavigator functionOverride, + [NotNull] XPathNavigator nav, + [NotNull] EnumProcessor enumProcessor, + [NotNull] EnumCollection enums, + [NotNull] string apiname ) { ApplyParameterReplacement(d, functionOverride); @@ -607,12 +670,12 @@ string apiname private void TranslateParameter ( - ParameterDefinition p, - XPathNavigator overrides, - EnumProcessor enumProcessor, - EnumCollection enums, - string category, - string apiname + [NotNull] ParameterDefinition p, + [NotNull] XPathNavigator overrides, + [NotNull] EnumProcessor enumProcessor, + [NotNull] EnumCollection enums, + [NotNull] string category, + [NotNull] string apiname ) { var type = p.ParameterType; @@ -684,13 +747,8 @@ string apiname } } - private void TranslateAttributes(DelegateDefinition d, XPathNavigator functionOverride) + private void TranslateAttributes([NotNull] DelegateDefinition d, [NotNull] XPathNavigator functionOverride) { - if (functionOverride == null) - { - return; - } - var versionOverride = functionOverride.SelectSingleNode("version"); if (versionOverride != null) { @@ -716,7 +774,8 @@ private void TranslateAttributes(DelegateDefinition d, XPathNavigator functionOv } } - private FunctionCollection CreateWrappers(DelegateCollection delegates) + [NotNull] + private FunctionCollection CreateWrappers([NotNull] DelegateCollection delegates) { var wrappers = new FunctionCollection(); foreach (var d in delegates.Values.SelectMany(v => v)) @@ -727,7 +786,8 @@ private FunctionCollection CreateWrappers(DelegateCollection delegates) return wrappers; } - private IEnumerable CreateNormalWrappers(DelegateDefinition d) + [NotNull, ItemNotNull] + private IEnumerable CreateNormalWrappers([NotNull] DelegateDefinition d) { var f = new FunctionDefinition(d); TrimName(f); @@ -739,7 +799,8 @@ private IEnumerable CreateNormalWrappers(DelegateDefinition } } - private IEnumerable CreateConvenienceOverloads(FunctionCollection wrappers) + [NotNull, ItemNotNull] + private IEnumerable CreateConvenienceOverloads([NotNull] FunctionCollection wrappers) { var convenienceWrappers = new List(); foreach (var d in wrappers.Values.SelectMany(w => w)) @@ -799,7 +860,8 @@ private IEnumerable CreateConvenienceOverloads(FunctionColle return convenienceWrappers; } - private static FunctionDefinition CreateSizeParameterConvenienceWrapper(FunctionDefinition d) + [NotNull] + private static FunctionDefinition CreateSizeParameterConvenienceWrapper([NotNull] FunctionDefinition d) { var sizeConvenienceOverload = new FunctionDefinition(d); @@ -815,7 +877,8 @@ private static FunctionDefinition CreateSizeParameterConvenienceWrapper(Function return sizeConvenienceOverload; } - private static FunctionDefinition CreateReturnTypeConvenienceWrapper(FunctionDefinition d) + [NotNull] + private static FunctionDefinition CreateReturnTypeConvenienceWrapper([NotNull] FunctionDefinition d) { var f = new FunctionDefinition(d); f.ReturnTypeDefinition = new TypeDefinition(f.Parameters.Last().ParameterType) @@ -842,7 +905,8 @@ private static FunctionDefinition CreateReturnTypeConvenienceWrapper(FunctionDef return f; } - private static FunctionDefinition CreateArrayReturnTypeConvenienceWrapper(FunctionDefinition d) + [NotNull] + private static FunctionDefinition CreateArrayReturnTypeConvenienceWrapper([NotNull] FunctionDefinition d) { var function = new FunctionDefinition(d); var arrayParameter = function.Parameters.Last(); @@ -861,7 +925,13 @@ private static FunctionDefinition CreateArrayReturnTypeConvenienceWrapper(Functi return function; } - private List GetOrAddWrapperTypeGroup(IDictionary> dictionary, WrapperTypes key, FunctionDefinition raw) + [NotNull, ItemNotNull] + private List GetOrAddWrapperTypeGroup + ( + [NotNull] IDictionary> dictionary, + WrapperTypes key, + [CanBeNull] FunctionDefinition raw + ) { if (!dictionary.ContainsKey(key)) { @@ -875,7 +945,8 @@ private List GetOrAddWrapperTypeGroup(IDictionary WrapParameters(FunctionDefinition func) + [NotNull, ItemNotNull] + private IEnumerable WrapParameters([NotNull] FunctionDefinition func) { if (func.Parameters.Count == 0) { @@ -1068,7 +1139,7 @@ private IEnumerable WrapParameters(FunctionDefinition func) } } - private static void WrapReturnType(FunctionDefinition func) + private static void WrapReturnType([NotNull] FunctionDefinition func) { if ((func.ReturnTypeDefinition.WrapperType & WrapperTypes.StringReturnType) != 0) { diff --git a/src/Generator.Bind/Generator.Bind.csproj b/src/Generator.Bind/Generator.Bind.csproj index 7d473fb840..608248cc02 100644 --- a/src/Generator.Bind/Generator.Bind.csproj +++ b/src/Generator.Bind/Generator.Bind.csproj @@ -20,4 +20,5 @@ + diff --git a/src/Generator.Bind/Generators/GeneratorBase.cs b/src/Generator.Bind/Generators/GeneratorBase.cs index 6c38bb2218..0907a1936e 100644 --- a/src/Generator.Bind/Generators/GeneratorBase.cs +++ b/src/Generator.Bind/Generators/GeneratorBase.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using Bind.Structures; +using JetBrains.Annotations; namespace Bind.Generators { @@ -42,32 +43,38 @@ internal abstract class GeneratorBase : IGenerator /// /// Gets the path to the file that contains the language typemap. /// + [NotNull] protected virtual string LanguageTypemap => "csharp.tm"; /// /// Gets the path to the file that contains the API typemap. /// + [NotNull] protected virtual string APITypemap => Path.Combine("GL2", "gl.tm"); /// /// Gets the path to the file that contains the API specification. /// + [NotNull] protected virtual string SpecificationFile => Path.Combine("GL2", "signatures.xml"); /// /// Gets the path to the file that contains the API enum specification. /// + [NotNull] protected virtual string EnumSpecificationFile => Path.Combine("GL2", "signatures.xml"); /// /// Gets the name of the function that loads all entrypoints. /// + [NotNull] protected const string LoadAllFuncName = "LoadAll"; /// /// Gets the name that corresponds to the "profile" attribute in the OpenGL registry. We use this to distinguish /// between different profiles (e.g. "gl", "glcore", "gles1", "gles2"). /// + [NotNull] protected virtual string ProfileName => "gl"; /// @@ -75,11 +82,13 @@ internal abstract class GeneratorBase : IGenerator /// distinguish between OpenGL ES 2.0 and 3.0, which share the same profile "gles2". If empty, then all elements /// of a profile will be parsed, and their version number will be ignored. /// + [NotNull] protected virtual string Version => string.Empty; /// /// Gets the specification reader associated with this generator. /// + [NotNull] protected ISpecificationReader SpecificationReader { get; } /// @@ -100,7 +109,7 @@ internal abstract class GeneratorBase : IGenerator /// /// Initializes a new instance of the class. /// - public GeneratorBase() + protected GeneratorBase() { OverrideFiles = new List(); diff --git a/src/Generator.Bind/Generators/IGenerator.cs b/src/Generator.Bind/Generators/IGenerator.cs index 599d20346a..5d682a8c05 100644 --- a/src/Generator.Bind/Generators/IGenerator.cs +++ b/src/Generator.Bind/Generators/IGenerator.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Bind.Structures; +using JetBrains.Annotations; namespace Bind.Generators { @@ -15,61 +16,73 @@ internal interface IGenerator /// /// Gets a short-name identifier for the API (such as GL, GL4, ES10, etc). /// + [NotNull] string APIIdentifier { get; } /// /// Gets the name of the subfolder where the generated files should be placed. /// + [NotNull] string Namespace { get; } /// /// Gets the namespace that the output classes and enums should live in. /// + [NotNull] string OutputSubfolder { get; } /// /// Gets the name of the output class. /// + [NotNull] string ClassName { get; } /// /// Gets the prefix of the functions in the API. /// + [NotNull] string FunctionPrefix { get; } /// /// Gets the prefix of the constants defined in the API. /// + [NotNull] string ConstantPrefix { get; } /// /// Gets the path to the directory that contains the API documentation. /// + [NotNull] string SpecificationDocumentationPath { get; } /// /// Gets the delegates that were loaded from the API specification. /// + [NotNull] DelegateCollection Delegates { get; } /// /// Gets the enums that were generated from the API specification. /// + [NotNull] EnumCollection Enums { get; } /// /// Gets the function wrappers that were generated from the API specification. /// + [NotNull] FunctionCollection Wrappers { get; } /// /// Gets the API typemap. /// + [NotNull] IDictionary APITypes { get; } /// /// Gets the language typemap. /// + [NotNull] IDictionary LanguageTypes { get; } /// diff --git a/src/Generator.Bind/ISpecificationReader.cs b/src/Generator.Bind/ISpecificationReader.cs index d091f832ea..2ae4888b92 100644 --- a/src/Generator.Bind/ISpecificationReader.cs +++ b/src/Generator.Bind/ISpecificationReader.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Bind.Structures; +using JetBrains.Annotations; namespace Bind { @@ -20,7 +21,14 @@ internal interface ISpecificationReader /// The name of the API. /// The versions of the API to read. /// A delegate collection. - DelegateCollection ReadDelegates(string specFile, IEnumerable overrideFiles, string apiname, string apiversions); + [NotNull] + DelegateCollection ReadDelegates + ( + [NotNull, PathReference] string specFile, + [NotNull] IEnumerable overrideFiles, + [NotNull] string apiname, + [NotNull] string apiversions + ); /// /// Reads the enum specification into the given enum collection. @@ -30,20 +38,29 @@ internal interface ISpecificationReader /// The name of the API. /// The versions of the API to read. /// An enumeration collection. - EnumCollection ReadEnums(string specFile, IEnumerable overrideFiles, string apiname, string apiversions); + [NotNull] + EnumCollection ReadEnums + ( + [NotNull, PathReference] string specFile, + [NotNull] IEnumerable overrideFiles, + [NotNull] string apiname, + [NotNull] string apiversions + ); /// /// Reads the typemap for the input API into a dictionary. /// /// The typemap file. /// A dictionary of the mapped types. - Dictionary ReadAPITypeMap(string file); + [NotNull] + Dictionary ReadAPITypeMap([NotNull, PathReference] string file); /// /// Reads the typemap for the output language into a dictionary. /// /// The typemap file. /// A dictionary of the mapped types. - Dictionary ReadLanguageTypeMap(string file); + [NotNull] + Dictionary ReadLanguageTypeMap([NotNull, PathReference] string file); } } diff --git a/src/Generator.Bind/Structures/ConstantDefinition.cs b/src/Generator.Bind/Structures/ConstantDefinition.cs index f7093d4f2a..ef390bb957 100644 --- a/src/Generator.Bind/Structures/ConstantDefinition.cs +++ b/src/Generator.Bind/Structures/ConstantDefinition.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Globalization; using System.Linq; +using JetBrains.Annotations; namespace Bind.Structures { @@ -98,7 +99,7 @@ out var number /// The Constant to translate. /// The list of enums to check. /// True if the reference was found; false otherwise. - public static bool TranslateConstantWithReference(ConstantDefinition c, EnumCollection enums) + public static bool TranslateConstantWithReference([NotNull] ConstantDefinition c, [NotNull] EnumCollection enums) { if (c == null) { @@ -155,7 +156,7 @@ public override string ToString() } /// - public int CompareTo(ConstantDefinition other) + public int CompareTo([NotNull] ConstantDefinition other) { var ret = string.Compare(Value, other.Value, StringComparison.Ordinal); if (ret == 0) diff --git a/src/Generator.Bind/Structures/DelegateCollection.cs b/src/Generator.Bind/Structures/DelegateCollection.cs index 17e6a4123c..8777458ed3 100644 --- a/src/Generator.Bind/Structures/DelegateCollection.cs +++ b/src/Generator.Bind/Structures/DelegateCollection.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; namespace Bind.Structures { @@ -16,7 +17,7 @@ internal class DelegateCollection : IDictionary /// already in the collection, or adding it to an existing one if there is. /// /// The delegate definition. - public void Add(DelegateDefinition d) + public void Add([NotNull] DelegateDefinition d) { if (!ContainsKey(d.Name)) { @@ -54,7 +55,7 @@ public void Add(DelegateDefinition d) /// one if there is. /// /// The set of delegate definitions. - public void AddRange(DelegateCollection delegates) + public void AddRange([NotNull] DelegateCollection delegates) { foreach (var d in delegates.Values.SelectMany(v => v)) { @@ -63,7 +64,7 @@ public void AddRange(DelegateCollection delegates) } /// - public void Add(string key, List value) + public void Add(string key, [NotNull] List value) { _delegates.Add(key, value.ToList()); } diff --git a/src/Generator.Bind/Structures/DelegateDefinition.cs b/src/Generator.Bind/Structures/DelegateDefinition.cs index e6a9658bba..0f4c83e5ce 100644 --- a/src/Generator.Bind/Structures/DelegateDefinition.cs +++ b/src/Generator.Bind/Structures/DelegateDefinition.cs @@ -4,6 +4,7 @@ using System; using System.Text; +using JetBrains.Annotations; namespace Bind.Structures { @@ -26,7 +27,7 @@ public DelegateDefinition() /// This constructor performs a deep copy of the given delegate definition. /// /// The definition to copy. - public DelegateDefinition(DelegateDefinition d) + public DelegateDefinition([NotNull] DelegateDefinition d) { Category = d.Category; ExtensionName = d.ExtensionName; @@ -156,7 +157,7 @@ public override string ToString() } /// - public int CompareTo(DelegateDefinition other) + public int CompareTo([NotNull] DelegateDefinition other) { var ret = string.Compare(Name, other.Name, StringComparison.Ordinal); diff --git a/src/Generator.Bind/Structures/EnumDefinition.cs b/src/Generator.Bind/Structures/EnumDefinition.cs index fe8a1632e5..0edb84453f 100644 --- a/src/Generator.Bind/Structures/EnumDefinition.cs +++ b/src/Generator.Bind/Structures/EnumDefinition.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using JetBrains.Annotations; namespace Bind.Structures { @@ -27,6 +28,7 @@ public bool IsFlagCollection /// /// Gets or sets the name of the enum. /// + [NotNull] public string Name { get => _name ?? string.Empty; @@ -36,6 +38,7 @@ public string Name /// /// Gets or sets the type of the enum. Typically 'int', but can be 'long'. /// + [NotNull] public string Type { get => string.IsNullOrEmpty(_type) ? "int" : _type; diff --git a/src/Generator.Bind/Structures/FunctionCollection.cs b/src/Generator.Bind/Structures/FunctionCollection.cs index 733ae65ac3..b105253099 100644 --- a/src/Generator.Bind/Structures/FunctionCollection.cs +++ b/src/Generator.Bind/Structures/FunctionCollection.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using JetBrains.Annotations; namespace Bind.Structures { @@ -11,7 +12,7 @@ internal class FunctionCollection : SortedDictionary /// The functions. - public void AddRange(IEnumerable functions) + public void AddRange([NotNull] IEnumerable functions) { foreach (var f in functions) { @@ -40,7 +41,7 @@ public void AddRange(IEnumerable functions) /// Adds the function to the collection, if a function with the same name and parameters doesn't already exist. /// /// The Function to add. - private void AddChecked(FunctionDefinition f) + private void AddChecked([NotNull] FunctionDefinition f) { if (ContainsKey(f.ExtensionName)) { diff --git a/src/Generator.Bind/Structures/FunctionDefinition.cs b/src/Generator.Bind/Structures/FunctionDefinition.cs index 026f6a15be..9211a8f293 100644 --- a/src/Generator.Bind/Structures/FunctionDefinition.cs +++ b/src/Generator.Bind/Structures/FunctionDefinition.cs @@ -3,6 +3,7 @@ */ using System; +using JetBrains.Annotations; namespace Bind.Structures { @@ -15,7 +16,7 @@ internal class FunctionDefinition : DelegateDefinition, IEquatable class. /// /// The delegate definition to wrap. - public FunctionDefinition(DelegateDefinition d) + public FunctionDefinition([NotNull] DelegateDefinition d) : base(d) { TrimmedName = Name = d.Name; @@ -27,7 +28,7 @@ public FunctionDefinition(DelegateDefinition d) /// This constructor performs a deep copy of the given definition. /// /// The definition to copy. - public FunctionDefinition(FunctionDefinition f) + public FunctionDefinition([NotNull] FunctionDefinition f) : this(f.WrappedDelegateDefinition) { Parameters = new ParameterCollection(f.Parameters); @@ -61,6 +62,11 @@ public override string ToString() /// public bool Equals(FunctionDefinition other) { + if (other is null) + { + return false; + } + var result = !string.IsNullOrEmpty(TrimmedName) && !string.IsNullOrEmpty(other.TrimmedName) && TrimmedName.Equals(other.TrimmedName) && @@ -69,7 +75,7 @@ public bool Equals(FunctionDefinition other) } /// - public int CompareTo(FunctionDefinition other) + public int CompareTo([NotNull] FunctionDefinition other) { var ret = string.Compare(Name, other.Name, StringComparison.Ordinal); if (ret == 0) diff --git a/src/Generator.Bind/Structures/IDeclarable.cs b/src/Generator.Bind/Structures/IDeclarable.cs index 4b8e193ed5..6ef54f44cb 100644 --- a/src/Generator.Bind/Structures/IDeclarable.cs +++ b/src/Generator.Bind/Structures/IDeclarable.cs @@ -1,4 +1,6 @@ -namespace Bind.Structures +using JetBrains.Annotations; + +namespace Bind.Structures { /// /// Represents a type that can declare itself in source code. @@ -9,6 +11,7 @@ public interface IDeclarable /// Converts the object into a valid declaration string. /// /// The declaration string. + [NotNull] string GetDeclarationString(); } } diff --git a/src/Generator.Bind/Structures/ParameterCollection.cs b/src/Generator.Bind/Structures/ParameterCollection.cs index dea475397b..61283fbf08 100644 --- a/src/Generator.Bind/Structures/ParameterCollection.cs +++ b/src/Generator.Bind/Structures/ParameterCollection.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Text; +using JetBrains.Annotations; namespace Bind.Structures { @@ -23,7 +24,7 @@ public ParameterCollection() /// Initializes a new instance of the class. /// /// The existing to copy from. - public ParameterCollection(ParameterCollection pc) + public ParameterCollection([NotNull] ParameterCollection pc) { foreach (var p in pc) { @@ -151,7 +152,7 @@ public ParameterDefinition this[int index] } /// - public int CompareTo(ParameterCollection other) + public int CompareTo([NotNull] ParameterCollection other) { if (Count < other.Count) { @@ -177,6 +178,11 @@ public int CompareTo(ParameterCollection other) /// public bool Equals(ParameterCollection other) { + if (other is null) + { + return false; + } + if (Count != other.Count) { return false; diff --git a/src/Generator.Bind/Structures/ParameterDefinition.cs b/src/Generator.Bind/Structures/ParameterDefinition.cs index e2241b36ab..ed35d50985 100644 --- a/src/Generator.Bind/Structures/ParameterDefinition.cs +++ b/src/Generator.Bind/Structures/ParameterDefinition.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; +using JetBrains.Annotations; namespace Bind.Structures { @@ -27,13 +28,8 @@ public ParameterDefinition() /// Creates a new parameter from the parameter passed (deep copy). /// /// The parameter to copy from. - public ParameterDefinition(ParameterDefinition p) + public ParameterDefinition([NotNull] ParameterDefinition p) { - if (p == null) - { - return; - } - Name = p.Name; UnmanagedType = p.UnmanagedType; Generic = p.Generic; @@ -127,7 +123,7 @@ public FlowDirection Flow /// /// The raw direction to parse. /// A flow direction, based on the input. - public static FlowDirection GetFlowDirection(string direction) + public static FlowDirection GetFlowDirection([NotNull] string direction) { return direction == "out" ? FlowDirection.Out : direction == "in" ? FlowDirection.In : FlowDirection.Undefined; } @@ -136,6 +132,7 @@ public static FlowDirection GetFlowDirection(string direction) /// Gets a string that would declare this object in C# source code. /// /// The declaration string. + [NotNull] public string GetDeclarationString() { var sb = new StringBuilder(); @@ -204,7 +201,7 @@ public string GetDeclarationString() } /// - public int CompareTo(ParameterDefinition other) + public int CompareTo([NotNull] ParameterDefinition other) { var result = ParameterType.CompareTo(other.ParameterType); diff --git a/src/Generator.Bind/Structures/TypeDefinition.cs b/src/Generator.Bind/Structures/TypeDefinition.cs index c5bbc3c180..b14f0c8bef 100644 --- a/src/Generator.Bind/Structures/TypeDefinition.cs +++ b/src/Generator.Bind/Structures/TypeDefinition.cs @@ -4,6 +4,7 @@ using System; using Bind.Extensions; +using JetBrains.Annotations; namespace Bind.Structures { @@ -25,13 +26,8 @@ public TypeDefinition() /// This constructor performs a deep copy of the given definition. /// /// The definition to copy. - public TypeDefinition(TypeDefinition t) + public TypeDefinition([NotNull] TypeDefinition t) { - if (t == null) - { - return; - } - QualifiedTypeName = t.QualifiedTypeName ?? t.TypeName; // Covers current type and qualifier WrapperType = t.WrapperType; ArrayDimensions = t.ArrayDimensions; @@ -175,35 +171,44 @@ public virtual string GetDeclarationString() } /// - public int CompareTo(TypeDefinition other) + public int CompareTo([NotNull] TypeDefinition other) { // Make sure that Pointer parameters are sorted last to avoid bug [#1098]. // The rest of the comparisons help maintain a stable order (useful for source control). // Note that CompareTo is stricter than Equals and that there is code in // DelegateCollection.Add that depends on this fact. - var result = TypeName.CompareTo(other.TypeName); + var result = string.Compare(TypeName, other.TypeName, StringComparison.Ordinal); if (result == 0) { result = IndirectionLevel.CompareTo(other.IndirectionLevel); // Must come after array/ref, see issue [#1098] } + if (result == 0) { result = IsReference.CompareTo(other.IsReference); } + if (result == 0) { result = ArrayDimensions.CompareTo(other.ArrayDimensions); } + if (result == 0) { result = ElementCount.CompareTo(other.ElementCount); } + return result; } /// public bool Equals(TypeDefinition other) { + if (other is null) + { + return false; + } + var result = TypeName.Equals(other.TypeName) && IndirectionLevel.Equals(other.IndirectionLevel) && diff --git a/src/Generator.Bind/Utilities.cs b/src/Generator.Bind/Utilities.cs index 1ba8014859..6370a01134 100644 --- a/src/Generator.Bind/Utilities.cs +++ b/src/Generator.Bind/Utilities.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text.RegularExpressions; using Bind.Structures; +using JetBrains.Annotations; namespace Bind { @@ -38,7 +39,7 @@ internal static class Utilities /// Merges the given extensions into the current set of extensions. /// /// The extensions to merge in. - public static void AddExtensions(IEnumerable extensions) + public static void AddExtensions([NotNull] IReadOnlyCollection extensions) { // Merge the new extensions with the current list of extensions var extensionCount = _extensionNames.Count; @@ -77,6 +78,7 @@ public static void AddExtensions(IEnumerable extensions) /// /// Gets a list of keywords in the C# language. /// + [NotNull] public static List CSharpKeywords => new List { "abstract", @@ -163,7 +165,7 @@ public static void AddExtensions(IEnumerable extensions) /// /// The enum collection to merge into. /// The enums to merge. - internal static void Merge(EnumCollection currentEnums, EnumCollection newEnums) + internal static void Merge(EnumCollection currentEnums, [NotNull] EnumCollection newEnums) { foreach (var e in newEnums) { @@ -177,7 +179,7 @@ internal static void Merge(EnumCollection currentEnums, EnumCollection newEnums) /// /// The enums. /// The new enum. - internal static void Merge(EnumCollection currentEnums, EnumDefinition newEnum) + internal static void Merge([NotNull] EnumCollection currentEnums, [NotNull] EnumDefinition newEnum) { if (!currentEnums.ContainsKey(newEnum.Name)) { @@ -200,7 +202,7 @@ internal static void Merge(EnumCollection currentEnums, EnumDefinition newEnum) /// /// The enumeration definiton. /// The constant definition. - internal static void Merge(EnumDefinition enumDefinition, ConstantDefinition constantDefinition) + internal static void Merge([NotNull] EnumDefinition enumDefinition, [NotNull] ConstantDefinition constantDefinition) { if (!enumDefinition.ConstantCollection.ContainsKey(constantDefinition.Name)) { @@ -227,7 +229,8 @@ internal static void Merge(EnumDefinition enumDefinition, ConstantDefinition con /// case. /// /// The extension name. - internal static string GetExtension(string name, bool returnUnmodified) + [NotNull] + internal static string GetExtension([NotNull] string name, bool returnUnmodified) { var match = Extensions.Match(name); if (!match.Success) diff --git a/src/Generator.Bind/Writers/SourceWriter.cs b/src/Generator.Bind/Writers/SourceWriter.cs index 6697036e0a..e7821425e2 100644 --- a/src/Generator.Bind/Writers/SourceWriter.cs +++ b/src/Generator.Bind/Writers/SourceWriter.cs @@ -1,5 +1,6 @@ using System.CodeDom.Compiler; using System.IO; +using JetBrains.Annotations; namespace Bind.Writers { @@ -12,7 +13,7 @@ public class SourceWriter : IndentedTextWriter /// Initializes a new instance of the class. /// /// The to wrap. - public SourceWriter(TextWriter writer) + public SourceWriter([NotNull] TextWriter writer) : base(writer) { } @@ -22,7 +23,7 @@ public SourceWriter(TextWriter writer) /// /// The to wrap. /// The string of characters to use as a tabulation sequence. - public SourceWriter(TextWriter writer, string tabString) + public SourceWriter([NotNull] TextWriter writer, [NotNull] string tabString) : base(writer, tabString) { } @@ -40,6 +41,7 @@ public void WriteLineNoTabs() /// writer once the writing has been finished. /// /// An indentation handle. + [NotNull] public SourceWriterIndentation BeginIndent() { return new SourceWriterIndentation(this); @@ -51,6 +53,7 @@ public SourceWriterIndentation BeginIndent() /// /// Whether or not the closing brace should be terminated with a semicolon. /// An indentation handle. + [NotNull] public SourceWriterBlock BeginBlock(bool withSemicolon = false) { return new SourceWriterBlock(this, withSemicolon); diff --git a/src/Generator.Bind/Writers/SourceWriterBlock.cs b/src/Generator.Bind/Writers/SourceWriterBlock.cs index fcbf8803d5..8cc551d02a 100644 --- a/src/Generator.Bind/Writers/SourceWriterBlock.cs +++ b/src/Generator.Bind/Writers/SourceWriterBlock.cs @@ -1,4 +1,5 @@ using System; +using JetBrains.Annotations; namespace Bind.Writers { @@ -9,8 +10,12 @@ namespace Bind.Writers /// public class SourceWriterBlock : IDisposable { + [NotNull] private readonly SourceWriter _sourceWriter; + private readonly bool _withSemicolon; + + [NotNull] private readonly SourceWriterIndentation _indentation; /// @@ -18,7 +23,7 @@ public class SourceWriterBlock : IDisposable /// /// The source writer to manage the block of. /// Whether or not the closing brace should be terminated with a semicolon. - public SourceWriterBlock(SourceWriter sourceWriter, bool withSemicolon) + public SourceWriterBlock([NotNull] SourceWriter sourceWriter, bool withSemicolon) { _sourceWriter = sourceWriter; _withSemicolon = withSemicolon; diff --git a/src/Generator.Bind/Writers/SourceWriterIndentation.cs b/src/Generator.Bind/Writers/SourceWriterIndentation.cs index 4b8f035cba..5a2c8ccc08 100644 --- a/src/Generator.Bind/Writers/SourceWriterIndentation.cs +++ b/src/Generator.Bind/Writers/SourceWriterIndentation.cs @@ -1,4 +1,5 @@ using System; +using JetBrains.Annotations; namespace Bind.Writers { @@ -8,13 +9,14 @@ namespace Bind.Writers /// public class SourceWriterIndentation : IDisposable { + [NotNull] private readonly SourceWriter _sourceWriter; /// /// Initializes a new instance of the class. /// /// The source writer to manage the indentation of. - public SourceWriterIndentation(SourceWriter sourceWriter) + public SourceWriterIndentation([NotNull] SourceWriter sourceWriter) { _sourceWriter = sourceWriter; diff --git a/src/Generator.Bind/XmlSpecificationReader.cs b/src/Generator.Bind/XmlSpecificationReader.cs index 72a14d882d..18c0d4128a 100644 --- a/src/Generator.Bind/XmlSpecificationReader.cs +++ b/src/Generator.Bind/XmlSpecificationReader.cs @@ -29,6 +29,7 @@ using System.Linq; using System.Xml.XPath; using Bind.Structures; +using JetBrains.Annotations; namespace Bind { @@ -38,7 +39,13 @@ namespace Bind internal class XmlSpecificationReader : ISpecificationReader { /// - public DelegateCollection ReadDelegates(string specFile, IEnumerable overrideFiles, string apiname, string apiversions) + public DelegateCollection ReadDelegates + ( + string specFile, + IEnumerable overrideFiles, + string apiname, + string apiversions + ) { var delegates = new DelegateCollection(); @@ -80,7 +87,13 @@ public DelegateCollection ReadDelegates(string specFile, IEnumerable ove } /// - public EnumCollection ReadEnums(string specFile, IEnumerable overrideFiles, string apiname, string apiversions) + public EnumCollection ReadEnums + ( + string specFile, + IEnumerable overrideFiles, + string apiname, + string apiversions + ) { var enums = new EnumCollection(); @@ -217,7 +230,13 @@ public Dictionary ReadLanguageTypeMap(string file) } } - private static void GetSignaturePaths(string apiname, string apiversion, out string xpathAdd, out string xpathDelete) + private static void GetSignaturePaths + ( + [CanBeNull] string apiname, + [CanBeNull] string apiversion, + [NotNull] out string xpathAdd, + [NotNull] out string xpathDelete + ) { xpathAdd = "/signatures/add"; xpathDelete = "/signatures/delete"; @@ -237,11 +256,14 @@ private static void GetSignaturePaths(string apiname, string apiversion, out str } } - private string GetSpecVersion(XPathDocument specs) + [NotNull] + private string GetSpecVersion([NotNull] IXPathNavigable specs) { - var version = - specs.CreateNavigator().SelectSingleNode("/signatures") - .GetAttribute("version", string.Empty); + var version = specs + .CreateNavigator() + ?.SelectSingleNode("/signatures") + ?.GetAttribute("version", string.Empty); + if (string.IsNullOrEmpty(version)) { version = "1"; @@ -249,7 +271,8 @@ private string GetSpecVersion(XPathDocument specs) return version; } - private DelegateCollection ReadDelegates(XPathNavigator specs, string apiversion) + [NotNull] + private DelegateCollection ReadDelegates([NotNull] XPathNavigator specs, [CanBeNull] string apiversion) { var delegates = new DelegateCollection(); var extensions = new List(); @@ -319,134 +342,138 @@ private DelegateCollection ReadDelegates(XPathNavigator specs, string apiversion return delegates; } - private EnumCollection ReadEnums(XPathNavigator nav) + [NotNull] + private EnumCollection ReadEnums([CanBeNull] XPathNavigator nav) { var enums = new EnumCollection(); - if (nav != null) + if (nav == null) { - var reuseList = new List>(); + return enums; + } + + var reuseList = new List>(); - // First pass: collect all available tokens and enums - foreach (XPathNavigator node in nav.SelectChildren("enum", string.Empty)) + // First pass: collect all available tokens and enums + foreach (XPathNavigator node in nav.SelectChildren("enum", string.Empty)) + { + var e = new EnumDefinition { - var e = new EnumDefinition() - { - Name = node.GetAttribute("name", string.Empty).Trim(), - Type = node.GetAttribute("type", string.Empty).Trim() - }; + Name = node.GetAttribute("name", string.Empty).Trim(), + Type = node.GetAttribute("type", string.Empty).Trim(), + Obsolete = node.GetAttribute("obsolete", string.Empty).Trim() + }; - e.Obsolete = node.GetAttribute("obsolete", string.Empty).Trim(); + if (string.IsNullOrEmpty(e.Name)) + { + throw new InvalidOperationException($"Empty name for enum element {node}"); + } + + // It seems that all flag collections contain "Mask" in their names. + // This looks like a heuristic, but it holds 100% in practice + // (checked all enums to make sure). + e.IsFlagCollection = e.Name.ToLower().Contains("mask"); - if (string.IsNullOrEmpty(e.Name)) + foreach (XPathNavigator param in node.SelectChildren(XPathNodeType.Element)) + { + ConstantDefinition c = null; + switch (param.Name) { - throw new InvalidOperationException($"Empty name for enum element {node}"); + case "token": + c = new ConstantDefinition + { + Name = param.GetAttribute("name", string.Empty).Trim(), + Value = param.GetAttribute("value", string.Empty).Trim() + }; + break; + + case "use": + c = new ConstantDefinition + { + Name = param.GetAttribute("token", string.Empty).Trim(), + Reference = param.GetAttribute("enum", string.Empty).Trim(), + Value = param.GetAttribute("token", string.Empty).Trim(), + }; + break; + + case "reuse": + var reuseEnum = param.GetAttribute("enum", string.Empty).Trim(); + reuseList.Add(new KeyValuePair(e, reuseEnum)); + break; + + default: + throw new NotSupportedException(); } - // It seems that all flag collections contain "Mask" in their names. - // This looks like a heuristic, but it holds 100% in practice - // (checked all enums to make sure). - e.IsFlagCollection = e.Name.ToLower().Contains("mask"); + if (c == null) + { + continue; + } - foreach (XPathNavigator param in node.SelectChildren(XPathNodeType.Element)) + try { - ConstantDefinition c = null; - switch (param.Name) + if (!e.ConstantCollection.ContainsKey(c.Name)) { - case "token": - c = new ConstantDefinition - { - Name = param.GetAttribute("name", string.Empty).Trim(), - Value = param.GetAttribute("value", string.Empty).Trim() - }; - break; - - case "use": - c = new ConstantDefinition - { - Name = param.GetAttribute("token", string.Empty).Trim(), - Reference = param.GetAttribute("enum", string.Empty).Trim(), - Value = param.GetAttribute("token", string.Empty).Trim(), - }; - break; - - case "reuse": - var reuseEnum = param.GetAttribute("enum", string.Empty).Trim(); - reuseList.Add(new KeyValuePair(e, reuseEnum)); - break; - - default: - throw new NotSupportedException(); + e.ConstantCollection.Add(c.Name, c); } - - if (c != null) + else if (e.ConstantCollection[c.Name].Value != c.Value) { - try + var existing = e.ConstantCollection[c.Name]; + if (existing.Reference != null && c.Reference == null) + { + e.ConstantCollection[c.Name] = c; + } + else if (existing.Reference == null && c.Reference != null) { - if (!e.ConstantCollection.ContainsKey(c.Name)) - { - e.ConstantCollection.Add(c.Name, c); - } - else if (e.ConstantCollection[c.Name].Value != c.Value) - { - var existing = e.ConstantCollection[c.Name]; - if (existing.Reference != null && c.Reference == null) - { - e.ConstantCollection[c.Name] = c; - } - else if (existing.Reference == null && c.Reference != null) - { - // Keep existing - } - else - { - Console.WriteLine - ( - $"[Warning] Conflicting token {e.Name}.{c.Name} with value {e.ConstantCollection[c.Name].Value} != {c.Value}" - ); - } - } + // Keep existing } - catch (ArgumentException ex) + else { - Console.WriteLine("[Warning] Failed to add constant {0} to enum {1}: {2}", c.Name, e.Name, ex.Message); + Console.WriteLine + ( + $"[Warning] Conflicting token {e.Name}.{c.Name} with value {e.ConstantCollection[c.Name].Value} != {c.Value}" + ); } } } - - Utilities.Merge(enums, e); + catch (ArgumentException ex) + { + Console.WriteLine("[Warning] Failed to add constant {0} to enum {1}: {2}", c.Name, e.Name, ex.Message); + } } - // Second pass: resolve "reuse" directives -restart: - foreach (var pair in reuseList) - { - var e = pair.Key; - var reuse = pair.Value; - var count = e.ConstantCollection.Count; + Utilities.Merge(enums, e); + } - if (enums.ContainsKey(reuse)) - { - var reuseEnum = enums[reuse]; - foreach (var token in reuseEnum.ConstantCollection.Values) - { - Utilities.Merge(e, token); - } - } - else - { - Console.WriteLine("[Warning] Reuse token not found: {0}", reuse); - } + // Second pass: resolve "reuse" directives + restart: + foreach (var pair in reuseList) + { + var e = pair.Key; + var reuse = pair.Value; + var count = e.ConstantCollection.Count; - if (count != e.ConstantCollection.Count) + if (enums.ContainsKey(reuse)) + { + var reuseEnum = enums[reuse]; + foreach (var token in reuseEnum.ConstantCollection.Values) { - // Restart resolution of reuse directives whenever - // we modify an enum. This is the simplest (brute) way - // to resolve chains of reuse directives: - // e.g. enum A reuses B which reuses C - goto restart; + Utilities.Merge(e, token); } } + else + { + Console.WriteLine("[Warning] Reuse token not found: {0}", reuse); + } + + if (count != e.ConstantCollection.Count) + { + // Restart resolution of reuse directives whenever + // we modify an enum. This is the simplest (brute) way + // to resolve chains of reuse directives: + // e.g. enum A reuses B which reuses C + goto restart; + } } return enums;