Exemplo n.º 1
0
        private void WriteDocumentation([NotNull] SourceWriter sw, [NotNull] FunctionSignature function)
        {
            if (!_documentation.TryGetDocumentation(function, out var documentation))
            {
                Debug.WriteLine
                (
                    $"The function \"{function.Name}\" lacks documentation. Consider adding a documentation file for " +
                    $"the function."
                );

                WritePlaceholderDocumentation(sw, function);
                return;
            }

            sw.WriteLine("/// <summary>");
            var summaryLines = documentation.Purpose.TrimEnd().Split('\n');

            foreach (var summaryLine in summaryLines)
            {
                sw.WriteLine($"/// {summaryLine}");
            }

            sw.WriteLine("/// </summary>");

            for (int i = 0; i < function.Parameters.Count; ++i)
            {
                var parameter = function.Parameters[i];
                var parameterDocumentation = documentation.Parameters.FirstOrDefault(dp => dp.Name == parameter.Name);
                if (parameterDocumentation is null)
                {
                    sw.WriteLine($"/// <param name=\"{parameter.Name}\">This parameter lacks documentation.</param>");
                    continue;
                }

                // XML documentation doesn't require keyword escaping.
                if (parameter.Name.TrimStart('@') != parameterDocumentation.Name)
                {
                    Debug.WriteLine
                    (
                        $"Parameter {parameter.Name} in function {function.Name} has incorrect documentation name " +
                        $"{parameterDocumentation.Name}."
                    );
                }

                sw.WriteLine($"/// <param name=\"{parameterDocumentation.Name}\">");
                if (!(parameter.Count is null))
                {
                    if (parameter.Count.IsStatic)
                    {
                        sw.WriteLine($"/// This parameter contains {parameter.Count.Count} elements.");
                        sw.WriteLine("///");
                    }

                    if (parameter.Count.IsComputed)
                    {
                        var parameterList = parameter.Count.ComputedFrom.Select(cf => cf.Name).Humanize();
                        sw.WriteLine($"/// This parameter's element count is computed from {parameterList}.");
                        sw.WriteLine("///");
                    }

                    if (parameter.Count.IsReference)
                    {
                        // ReSharper disable once PossibleNullReferenceException
                        sw.WriteLine($"/// This parameter's element count is taken from {parameter.Count.ValueReference.Name}.");
                        sw.WriteLine("///");
                    }
                }

                var descriptionLines = parameterDocumentation.Description.TrimEnd().Split('\n');
                foreach (var descriptionLine in descriptionLines)
                {
                    sw.WriteLine($"/// {descriptionLine}");
                }

                sw.WriteLine("/// </param>");
            }

            foreach (var genericTypeParameter in function.GenericTypeParameters)
            {
                var referencingParameter = function.Parameters.First(f => f.Type.Name == genericTypeParameter.Name);
                sw.WriteLine
                (
                    $"/// <typeparam name=\"{genericTypeParameter.Name}\">The generic type of " +
                    $"{referencingParameter.Name}.</typeparam>"
                );
            }

            if (!function.ReturnType.Name.Equals(typeof(void).Name, StringComparison.OrdinalIgnoreCase))
            {
                sw.WriteLine("/// <returns>See online documentation.</returns>");
            }
        }
Exemplo n.º 2
0
        private void WriteCategoryWrapperFile
        (
            [NotNull] string extensionOutputDirectory,
            [NotNull] string extensionName,
            [NotNull] string category
        )
        {
            var translatedExtensionName = _identifierTranslator.Translate(extensionName);

            var nativeSignatures = _profile.NativeSignatures
                                   .Where(ns => ns.Extension == extensionName && ns.Categories.First() == category)
                                   .OrderBy(s => s.Name)
                                   .ThenBy(s => s.Parameters.Count)
                                   .ToList();

            var overloads = _profile.Overloads
                            .Where(o => o.Extension == extensionName && o.Categories.First() == category)
                            .ToList();

            var categorySignatures = nativeSignatures
                                     .Concat(overloads)
                                     .OrderBy(s => s.Name)
                                     .ThenBy(s => s.Parameters.Count)
                                     .ThenBy(s => s.ToString())
                                     .ToList();

            if (categorySignatures.Count == 0)
            {
                return;
            }

            var translatedCategoryName = _identifierTranslator.Translate(category);

            // Create the category file
            var tempWrapperOutputPath = Path.GetTempFileName();

            using (var outputFile = File.Open(tempWrapperOutputPath, FileMode.OpenOrCreate))
            {
                using (var sw = new SourceWriter(new StreamWriter(outputFile)))
                {
                    sw.WriteLine(EmbeddedResources.LicenseText);
                    sw.WriteLineNoTabs();

                    sw.WriteLine("using System;");
                    sw.WriteLine("using System.Runtime.InteropServices;");
                    sw.WriteLine("using System.Text;");
                    sw.WriteLineNoTabs();

                    sw.WriteLine($"namespace {_generatorSettings.Namespace}");
                    using (sw.BeginBlock())
                    {
                        SourceWriterBlock?outerBlock = null;
                        if (extensionName == "Core")
                        {
                            sw.WriteLine($"public sealed partial class {_generatorSettings.ClassName}");
                        }
                        else
                        {
                            sw.WriteLine($"public sealed partial class {_generatorSettings.ClassName}");

                            outerBlock = sw.BeginBlock();

                            sw.WriteLine("/// <summary>");
                            sw.WriteLine
                            (
                                $"/// Contains native bindings to functions in the category" +
                                $" \"{translatedCategoryName}\" in the extension " +
                                $"\"{translatedExtensionName}\"."
                            );
                            sw.WriteLine("/// </summary>");

                            // Identifiers can't begin with numbers
                            var safeExtensionName = char.IsDigit(extensionName[0])
                                ? $"{_generatorSettings.ConstantPrefix}{extensionName}"
                                : extensionName;

                            sw.WriteLine($"public static partial class {safeExtensionName}");
                        }

                        // Write the native signature methods
                        // Write the overload methods
                        using (sw.BeginBlock())
                        {
                            var last = categorySignatures[categorySignatures.Count - 1];
                            foreach (var signature in categorySignatures)
                            {
                                WriteMethod(sw, signature);

                                if (signature != last)
                                {
                                    sw.WriteLineNoTabs();
                                }
                            }

                            sw.WriteLineNoTabs();

                            sw.WriteLine(
                                "#pragma warning disable SA1300 // Element should begin with an upper-case letter");
                            sw.WriteLineNoTabs();

                            // Write the native signature externs
                            // These are required by the patcher.
                            foreach (var signature in nativeSignatures)
                            {
                                sw.WriteLine(
                                    $"[Slot({_entrypointSlots[signature.NativeEntrypoint].ToString()})]");
                                sw.WriteLine(
                                    "[DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]");
                                sw.WriteLine
                                (
                                    $"private static extern {GetNativeDeclarationString(signature)};"
                                );

                                if (signature != nativeSignatures.Last())
                                {
                                    sw.WriteLineNoTabs();
                                }
                            }
                        }

                        // We've got a nested class to close
                        outerBlock?.Dispose();
                    }
                }
            }

            var outputCategorySubPath = Path.Combine(extensionOutputDirectory, $"{translatedCategoryName}.cs");

            if (File.Exists(outputCategorySubPath))
            {
                File.Delete(outputCategorySubPath);
            }

            File.Move(tempWrapperOutputPath, outputCategorySubPath);
        }
Exemplo n.º 3
0
        private void WriteWrapperClassConstructorFile([NotNull] string wrappersOutputDirectory)
        {
            var tempInteropFilePath = Path.GetTempFileName();

            using (var outputFile = File.Open(tempInteropFilePath, FileMode.OpenOrCreate))
            {
                using (var sw = new SourceWriter(new StreamWriter(outputFile)))
                {
                    sw.WriteLine(EmbeddedResources.LicenseText);
                    sw.WriteLineNoTabs();

                    sw.WriteLine("using System;");
                    sw.WriteLine("using System.Runtime.InteropServices;");
                    sw.WriteLine("using System.Text;");
                    sw.WriteLineNoTabs();

                    sw.WriteLine($"namespace {_generatorSettings.Namespace}");
                    using (sw.BeginBlock())
                    {
                        sw.WriteLine($"public sealed partial class {_generatorSettings.ClassName}");
                        using (sw.BeginBlock())
                        {
                            // Write constructor
                            sw.WriteLine($"static {_generatorSettings.ClassName}()");
                            using (sw.BeginBlock())
                            {
                                var nativeEntrypointsWithPrefix = _profile.NativeSignatures
                                                                  .Select
                                                                  (
                                    ns => $"{_generatorSettings.FunctionPrefix}{ns.Name}"
                                                                  )
                                                                  .ToList();

                                // Write entry point names.
                                // Instead of strings, which are costly to construct,
                                // we use a 1d array of ASCII bytes. Names are laid out
                                // sequentially, with a nul-terminator between them.
                                sw.WriteLine("EntryPointNames = new byte[]");
                                using (sw.BeginBlock(true))
                                {
                                    foreach (var nativeEntrypoint in nativeEntrypointsWithPrefix)
                                    {
                                        var byteString = string.Join
                                                         (
                                            ", ",
                                            Encoding.ASCII.GetBytes(nativeEntrypoint).Select(b => b.ToString()).ToArray()
                                                         );

                                        sw.WriteLine($"{byteString}, 0,");
                                    }
                                }

                                sw.WriteLineNoTabs();

                                // Write entry point name offsets.
                                // This is an array of offsets into the EntryPointNames[] array above.
                                sw.WriteLine("EntryPointNameOffsets = new int[]");
                                using (sw.BeginBlock(true))
                                {
                                    var offset = 0;
                                    foreach (var nativeEntrypoint in nativeEntrypointsWithPrefix)
                                    {
                                        sw.WriteLine($"{offset.ToString()},");
                                        offset += nativeEntrypoint.Length + 1;
                                    }
                                }

                                sw.WriteLineNoTabs();

                                sw.WriteLine("EntryPoints = new IntPtr[EntryPointNameOffsets.Length];");
                            }
                        }
                    }
                }
            }

            var outputFilePath = Path.Combine(wrappersOutputDirectory, $"{_generatorSettings.ClassName}.cs");

            if (File.Exists(outputFilePath))
            {
                File.Delete(outputFilePath);
            }

            File.Move(tempInteropFilePath, outputFilePath);
        }
Exemplo n.º 4
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SourceWriterIndentation"/> class.
        /// </summary>
        /// <param name="sourceWriter">The source writer to manage the indentation of.</param>
        public SourceWriterIndentation([NotNull] SourceWriter sourceWriter)
        {
            _sourceWriter = sourceWriter;

            _sourceWriter.Indent++;
        }