Skip to content

[3.0] OpenGL Codegen #2020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add SAL object model & Khronos length metadata parsing
  • Loading branch information
Perksey committed May 3, 2024
commit 4376388fbc3d28bcc07481726fa5bf6c0686973b
3 changes: 3 additions & 0 deletions sources/SilkTouch/Mods/Common/ModCSharpSyntaxRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ or SyntaxKind.WhitespaceTrivia
/// <inheritdoc />
public MethodDeclarationSyntax? Original { get; set; }

/// <inheritdoc />
public IFunctionTransformer[]? Transformers { get; set; }

/// <summary>
/// Adds a namespace import to the resultant syntax tree.
/// </summary>
Expand Down
43 changes: 43 additions & 0 deletions sources/SilkTouch/Mods/Metadata/IApiMetadataProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;

namespace Silk.NET.SilkTouch.Mods.Metadata;

/// <summary>
/// Provides typed metadata for API symbols.
/// </summary>
public interface IApiMetadataProvider
{
/// <summary>
/// Gets metadata of the given type stored for the function with the given native name.
/// </summary>
/// <param name="jobKey">The job key.</param>
/// <param name="nativeName">The native name.</param>
/// <param name="metadata">The metadata.</param>
/// <typeparam name="T">The type of the metadata.</typeparam>
/// <returns>Whether metadata exists for this symbol. If true, <paramref name="metadata"/> is not null.</returns>
bool TryGetFunctionMetadata<T>(
string? jobKey,
string nativeName,
[NotNullWhen(true)] out T? metadata
) => (metadata = default) is not null;

/// <summary>
/// Gets metadata of the given type stored for the parameter with the given name on the function with the given
/// native name.
/// </summary>
/// <param name="jobKey">The job key.</param>
/// <param name="nativeName">The native name.</param>
/// <param name="parameterName">The parameter name.</param>
/// <param name="metadata">The metadata.</param>
/// <typeparam name="T">The type of the metadata.</typeparam>
/// <returns>Whether metadata exists for this symbol. If true, <paramref name="metadata"/> is not null.</returns>
bool TryGetParameterMetadata<T>(
string? jobKey,
string nativeName,
string parameterName,
[NotNullWhen(true)] out T? metadata
) => (metadata = default) is not null;
}
27 changes: 27 additions & 0 deletions sources/SilkTouch/Mods/Metadata/LogicalAnnotation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Silk.NET.SilkTouch.Mods.Metadata;

/// <summary>
/// Represents an annotation that only applies in specific circumstances.
/// </summary>
/// <param name="Requirement">The circumstances in which the annotation applies.</param>
/// <param name="RequirementExpression">An expression that determines when the annotation applies.</param>
/// <param name="AppliesAtExpression">An expression that gets the address at which the annotation applies.</param>
/// <param name="AppliesForLengthExpression">
/// An expression that determines the offset from the <paramref name="AppliesAtExpression"/> at which the requirement
/// stops applying.
/// </param>
/// <param name="Value">The value of the annotation.</param>
/// <typeparam name="T">The type of the value.</typeparam>
/// <seealso href="https://learn.microsoft.com/en-us/cpp/code-quality/specifying-when-and-where-an-annotation-applies">
/// Microsoft SAL: Specifying When and Where an Annotation Applies
/// </seealso>
public readonly record struct LogicalAnnotation<T>(
LogicalRequirement Requirement,
string? RequirementExpression,
string? AppliesAtExpression,
string? AppliesForLengthExpression,
T Value
);
31 changes: 31 additions & 0 deletions sources/SilkTouch/Mods/Metadata/LogicalRequirement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Silk.NET.SilkTouch.Mods.Metadata;

/// <summary>
/// The circumstances in which a <see cref="LogicalAnnotation{T}"/> applies. The <c>default</c> is
/// <see cref="Success"/>.
/// </summary>
public enum LogicalRequirement
{
/// <summary>
/// The annotation only applies if the function call is successful.
/// </summary>
Success = 0,

/// <summary>
/// The annotation only applies if the function call failed.
/// </summary>
Failure,

/// <summary>
/// The annotation always applies.
/// </summary>
Always,

/// <summary>
/// An expression is used to determine when the annotation applies.
/// </summary>
Expression
}
136 changes: 136 additions & 0 deletions sources/SilkTouch/Mods/Metadata/MetadataUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Silk.NET.SilkTouch.Mods.Metadata;

/// <summary>
/// Utilities for dealing with metadata.
/// </summary>
public static class MetadataUtils
{
/// <summary>
/// Gets the number of indirection levels in the given C representation of a pointer type.
/// </summary>
/// <param name="cType">The C type or parameter string.</param>
/// <returns>The indirection levels.</returns>
public static int GetIndirectionLevels(this ReadOnlySpan<char> cType) =>
cType.Count('*') + (cType.Contains('[') ? 1 : 0);

/// <summary>
/// Gets the mutability and type-specified length (if applicable) for each indirection level (and its element type)
/// </summary>
/// <param name="cType">The C type or parameter string.</param>
/// <param name="mutability">The mutabilities for each indirection level.</param>
/// <param name="outerCount">
/// If the type had an array specifier, this specifies the count of the outermost indirection level. Will be zero
/// if this is not the case.
/// </param>
/// <remarks>
/// The outermost element type will appear first in the mutability span.
/// </remarks>
public static void GetTypeDetails(
this ReadOnlySpan<char> cType,
Span<bool> mutability,
out int outerCount
)
{
outerCount = 0;
var i = 0;
while (cType.IndexOfAny('*', '[') is not -1 and var idx)
{
if (cType[idx] == '[')
{
outerCount = 1;
do
{
var num = cType[(idx + 1)..];
if (cType[(idx + 1)..].IndexOf(']') is not -1 and var j)
{
idx++;
num = num[..j];
}

idx += num.Length;
outerCount *= num.Length == 0 ? 0 : int.Parse(num);
} while (cType[idx] == '[');
idx = cType.LastIndexOf(']');
}

var prevSpan = cType[..idx];
mutability[^++i] =
!prevSpan.StartsWith("const ")
&& !prevSpan.Contains(" const ", StringComparison.Ordinal)
&& !prevSpan.EndsWith(" const");
cType = cType[(idx + 1)..];
}

mutability[^++i] =
!cType.StartsWith("const ")
&& !cType.Contains(" const ", StringComparison.Ordinal)
&& !cType.EndsWith(" const");
}

/// <summary>
/// Creates symbol constraints based on the given length and mutability data. No other properties are set.
/// </summary>
/// <param name="lengths">
/// The lengths for each pointer indirection level. The last element is the innermost element type.
/// </param>
/// <param name="mutability">
/// The mutability for each pointer indirection level. The last element is the innermost element type.
/// </param>
/// <param name="compSize">
/// Whether the <paramref name="lengths"/> parameter is actually a list of names from which the size is computed,
/// rather than being a length for each indirection level.
/// </param>
/// <param name="optional">Whether the parameter is optional.</param>
/// <param name="outerCount">The static count of the outermost pointer dimension.</param>
/// <returns>The symbol constraints.</returns>
public static SymbolConstraints CreateBasicSymbolConstraints(
ReadOnlySpan<string> lengths,
ReadOnlySpan<bool> mutability,
bool compSize,
bool optional,
int outerCount
) =>
new(
Usage:
[
new LogicalAnnotation<UsageConstraints>(
LogicalRequirement.Always,
null,
null,
null,
new UsageConstraints(
IsOptional: optional,
StaticCount: outerCount != 0
|| (lengths.Length > 0 && int.TryParse(lengths[0], out outerCount))
? outerCount
: 0,
CountExpression: outerCount == 0
&& lengths.Length > 0
&& lengths[0] != "null-terminated"
? lengths[0]
: null,
IsNullTerminated: outerCount == 0
&& lengths.Length > 0
&& lengths[0] == "null-terminated"
)
)
],
IsMutable: mutability[0],
ComputedFromNames: compSize ? [.. lengths] : [],
ElementTypeConstraints: mutability.Length > 1
? CreateBasicSymbolConstraints(
!compSize && lengths.Length > 1 ? lengths[1..] : [],
mutability[1..],
false,
false,
0
)
: null
);
}
73 changes: 73 additions & 0 deletions sources/SilkTouch/Mods/Metadata/SymbolConstraints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Linq;

namespace Silk.NET.SilkTouch.Mods.Metadata;

/// <summary>
/// Represents the usage constraints for a function (and its return type) or a parameter. This data structure is
/// generally modelled after
/// <see href="https://learn.microsoft.com/en-us/cpp/code-quality/annotating-function-parameters-and-return-values">
/// Microsoft's Source Annotation Language
/// </see>.
/// </summary>
/// <param name="Usage">The usage constraints for each logical requirement set.</param>
/// <param name="IsMutable">
/// Whether the type is mutable in C terms. That is, <c>const int*</c> will have <see cref="IsMutable"/> as <c>true</c>
/// but its <see cref="ElementTypeConstraints"/> will have <see cref="IsMutable"/> as <c>false</c>.
/// </param>
/// <param name="ComputedFromNames">
/// A list of symbol (parameters usually) names from which the symbol's element count is arbitrarily computed.
/// </param>
/// <param name="SuccessExpression">An expression determining whether the function's execution was successful.</param>
/// <param name="HasComSemantics">Whether the parameter has COM semantics.</param>
/// <param name="ElementTypeConstraints">The constraints applied to the element type.</param>
/// <param name="PostExecutionConstraints">The constraints that apply after the function's execution.</param>
public record SymbolConstraints(
IReadOnlyList<LogicalAnnotation<UsageConstraints>> Usage,
bool IsMutable = false,
IReadOnlyList<string>? ComputedFromNames = null,
string? SuccessExpression = null,
bool HasComSemantics = false,
SymbolConstraints? ElementTypeConstraints = null,
SymbolConstraints? PostExecutionConstraints = null
)
{
private UsageConstraints? _commonUsage;

/// <summary>
/// Gets the common usage constraints i.e. the usage constraints that always apply for the caller.
/// </summary>
public UsageConstraints? CommonUsage =>
_commonUsage ??= Usage
.Select(x => (LogicalAnnotation<UsageConstraints>?)x)
.FirstOrDefault(x =>
x!.Value.Requirement == LogicalRequirement.Always
&& x.Value.AppliesForLengthExpression is null
&& x.Value.AppliesAtExpression is null
)
?.Value;

/// <summary>
/// Gets a value indicating whether the count is computed from other parameters.
/// </summary>
public bool IsComputed => ComputedFromNames?.Count > 0;

/// <summary>
/// Gets a value indicating whether the count is computed from a C# expression.
/// </summary>
public bool IsExpression => CommonUsage?.CountExpression is not null;

/// <summary>
/// Gets a value indicating whether the count is a static count.
/// </summary>
public bool IsStatic => !(IsComputed || CommonUsage?.CountExpression is not null);

/// <summary>
/// Gets a value indicating whether this count represents a count which is likely more than one.
/// </summary>
public bool IsMultiple =>
(IsStatic && CommonUsage?.StaticCount > 1) || CommonUsage?.CountExpression is not null;
}
62 changes: 62 additions & 0 deletions sources/SilkTouch/Mods/Metadata/UsageConstraints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Silk.NET.SilkTouch.Mods.Metadata;

/// <summary>
/// Represents the <see cref="SymbolConstraints"/> that are conditionally applied through a
/// <see cref="LogicalAnnotation{T}"/>.
/// </summary>
/// <param name="IsIn">
/// Whether this is an input parameter where, in the case of a pointer, the pointee is read but never written to.
/// This also implies that <see cref="SymbolConstraints.ElementTypeConstraints"/> has
/// <see cref="SymbolConstraints.IsMutable"/> set to <c>false</c>.
/// </param>
/// <param name="IsOut">
/// Whether this is an output parameter where, in the case of a pointer, the pointee is written to but never read.
/// This also implies that <see cref="SymbolConstraints.ElementTypeConstraints"/> has
/// <see cref="SymbolConstraints.IsMutable"/> set to <c>true</c>.
/// This should imply that the initial value of the pointee should be <c>default</c>.
/// </param>
/// <param name="IsOptional">Whether the parameter can be set to its <c>default</c> value.</param>
/// <param name="IsNullTerminated">Whether the buffer used by this parameter or return type is null-terminated.</param>
/// <param name="MinValueExpression">
/// An expression representing the minimum acceptable value within the range of the acceptable values.
/// </param>
/// <param name="MaxValueExpression">
/// An expression representing the maximum (exclusive) acceptable value within the range of acceptable values.
/// </param>
/// <param name="StaticCount">
/// A constant representing the number of elements to be read from the pointer.
/// Supersedes <see cref="CountExpression"/>.
/// </param>
/// <param name="CountExpression">
/// An expression evaluating to the number of elements to be read from the pointer.
/// </param>
/// <param name="IsCountBytes">Whether the count represented by either <see cref="StaticCount"/> or
/// <see cref="CountExpression"/> is in terms of bytes rather than the element type's size.</param>
/// <param name="RaisesSehException">Whether the symbol raises a Structured Exception Handling (SEH) exception.</param>
/// <param name="MustUseResult">Whether the result must not be discarded.</param>
/// <param name="FunctionClass">An arbitrary string determining the class/category into which a function falls.</param>
/// <param name="SatisfiesExpression">The given expression must evaluate to true.</param>
public readonly record struct UsageConstraints(
bool IsIn = false,
bool IsOut = false,
bool IsOptional = false,
bool IsNullTerminated = false,
int StaticCount = 0,
string? CountExpression = null,
bool IsCountBytes = false,
string? MinValueExpression = null,
string? MaxValueExpression = null,
VariableConstraint RaisesSehException = VariableConstraint.False,
bool MustUseResult = false,
string? FunctionClass = null,
string? SatisfiesExpression = null
)
{
/// <summary>
/// Whether the parameter is neither an input or an output (impossible). Bidirectional parameters have both set.
/// </summary>
public bool IsIndeterminateFlow => !IsIn && !IsOut;
}
Loading