opengl source gen tool works
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="GlGetValueApiFilter"
|
||||
// Project: StgSharp
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using StgSharp.GenerateGL.Registry;
|
||||
|
||||
namespace StgSharp.GenerateGL.Generation
|
||||
{
|
||||
/// <summary>
|
||||
/// Replaces desktop state query pointer families with typed Span overloads.
|
||||
/// </summary>
|
||||
internal sealed class GlGetValueApiFilter : IGlApiFilter
|
||||
{
|
||||
|
||||
private static readonly Regex _pattern = new Regex(
|
||||
@"^glGet(?<type>Boolean|Double|Float|Integer64|Integer)(?<indexed>i_)?v$",
|
||||
RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||||
|
||||
public IReadOnlyList<ManagedMethodDefinition> Apply(
|
||||
GlCommandDefinition command,
|
||||
IReadOnlyList<ManagedMethodDefinition> methods,
|
||||
GlEquivalentTypeMapper typeMapper
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(command);
|
||||
ArgumentNullException.ThrowIfNull(methods);
|
||||
ArgumentNullException.ThrowIfNull(typeMapper);
|
||||
|
||||
Match match = _pattern.Match(command.Name);
|
||||
if (!match.Success)
|
||||
{
|
||||
return methods;
|
||||
}
|
||||
|
||||
bool isIndexed = match.Groups["indexed"].Success;
|
||||
int prefixCount = isIndexed ? 2 : 1;
|
||||
if (typeMapper.ReadReturnType(command) != "void" ||
|
||||
command.Parameters.Count != prefixCount + 1)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"State query '{command.Name}' does not match its recognized shape.");
|
||||
}
|
||||
|
||||
GlParameterDefinition output = command.Parameters[^1];
|
||||
string elementType = ReadElementType(match.Groups["type"].Value);
|
||||
string expectedLength = $"COMPSIZE({command.Parameters[0].Name})";
|
||||
if (output.DeclarationText.Contains("const", StringComparison.Ordinal) ||
|
||||
typeMapper.ReadParameterType(output) != $"{elementType}*" ||
|
||||
!string.Equals(output.Length, expectedLength, StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"State query '{command.Name}' has inconsistent output metadata.");
|
||||
}
|
||||
|
||||
List<ManagedParameterDefinition> parameters = new List<ManagedParameterDefinition>();
|
||||
List<ManagedArgument> arguments = new List<ManagedArgument>();
|
||||
for (int index = 0; index < prefixCount; index++)
|
||||
{
|
||||
GlParameterDefinition source = command.Parameters[index];
|
||||
parameters.Add(
|
||||
new ManagedParameterDefinition(
|
||||
typeMapper.ReadParameterType(source),
|
||||
source.Name,
|
||||
source));
|
||||
arguments.Add(new ParameterArgument(index));
|
||||
}
|
||||
|
||||
parameters.Add(
|
||||
new ManagedParameterDefinition(
|
||||
$"Span<{elementType}>",
|
||||
"values",
|
||||
output));
|
||||
return Array.AsReadOnly(
|
||||
new[]
|
||||
{
|
||||
new ManagedMethodDefinition(
|
||||
"GetValue",
|
||||
"void",
|
||||
Array.AsReadOnly(parameters.ToArray()),
|
||||
command,
|
||||
new QuerySpanInvocation(
|
||||
Array.AsReadOnly(arguments.ToArray()),
|
||||
parameters.Count - 1,
|
||||
elementType)),
|
||||
});
|
||||
}
|
||||
|
||||
private static string ReadElementType(
|
||||
string name
|
||||
)
|
||||
{
|
||||
return name switch
|
||||
{
|
||||
"Boolean" => "byte",
|
||||
"Double" => "double",
|
||||
"Float" => "float",
|
||||
"Integer" => "int",
|
||||
"Integer64" => "long",
|
||||
_ => throw new InvalidOperationException(
|
||||
$"Unsupported state query component type '{name}'."),
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,10 @@ namespace StgSharp.GenerateGL.Generation
|
||||
@"^glVertexAttrib(?<width>[1-4])(?<type>d|f|s)(?<vector>v?)$",
|
||||
RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||||
|
||||
private static readonly Regex _redundantVertexAttributeVectorPattern = new Regex(
|
||||
@"^glVertexAttrib(?:I[1-4](?:i|ui)|L[1-4]d|P[1-4]ui|4Nub)v$",
|
||||
RegexOptions.Compiled | RegexOptions.CultureInvariant);
|
||||
|
||||
private static readonly string[] _components = { "X", "Y", "Z", "W" };
|
||||
|
||||
public IReadOnlyList<ManagedMethodDefinition> Apply(
|
||||
@@ -46,6 +50,11 @@ namespace StgSharp.GenerateGL.Generation
|
||||
ArgumentNullException.ThrowIfNull(methods);
|
||||
ArgumentNullException.ThrowIfNull(typeMapper);
|
||||
|
||||
if (_redundantVertexAttributeVectorPattern.IsMatch(command.Name))
|
||||
{
|
||||
return Array.Empty<ManagedMethodDefinition>();
|
||||
}
|
||||
|
||||
Match matrixMatch = _matrixUniformPattern.Match(command.Name);
|
||||
if (matrixMatch.Success)
|
||||
{
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace StgSharp.GenerateGL.Generation
|
||||
IReadOnlyList<ManagedParameterDefinition> Parameters,
|
||||
GlCommandDefinition NativeCommand,
|
||||
ManagedInvocation Invocation,
|
||||
bool WrapHandleReturn = false
|
||||
bool WrapHandleReturn = false,
|
||||
string? DocumentationPage = null
|
||||
);
|
||||
|
||||
internal sealed record ManagedParameterDefinition(
|
||||
@@ -53,6 +54,12 @@ namespace StgSharp.GenerateGL.Generation
|
||||
bool IsSpan
|
||||
) : ManagedInvocation;
|
||||
|
||||
internal sealed record QuerySpanInvocation(
|
||||
IReadOnlyList<ManagedArgument> PrefixArguments,
|
||||
int SpanParameterIndex,
|
||||
string ElementType
|
||||
) : ManagedInvocation;
|
||||
|
||||
internal abstract record ManagedArgument;
|
||||
|
||||
internal sealed record ParameterArgument(
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using StgSharp.GenerateGL.Analysis;
|
||||
using StgSharp.GenerateGL.Naming;
|
||||
using StgSharp.GenerateGL.Registry;
|
||||
|
||||
namespace StgSharp.GenerateGL.Generation
|
||||
@@ -19,16 +21,21 @@ namespace StgSharp.GenerateGL.Generation
|
||||
new IGlApiFilter[]
|
||||
{
|
||||
new GlOverloadApiFilter(),
|
||||
new GlGetValueApiFilter(),
|
||||
new GlHandleApiFilter(),
|
||||
});
|
||||
private readonly GlEquivalentTypeMapper _typeMapper;
|
||||
private readonly GlManagedApiNamePolicy _namePolicy;
|
||||
|
||||
public ManagedApiPlanner(
|
||||
GlEquivalentTypeMapper typeMapper
|
||||
GlEquivalentTypeMapper typeMapper,
|
||||
GlFunctionFamilyAnalysis familyAnalysis
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(typeMapper);
|
||||
ArgumentNullException.ThrowIfNull(familyAnalysis);
|
||||
_typeMapper = typeMapper;
|
||||
_namePolicy = new GlManagedApiNamePolicy(familyAnalysis);
|
||||
}
|
||||
|
||||
public IReadOnlyList<ManagedMethodDefinition> Plan(
|
||||
@@ -50,9 +57,23 @@ namespace StgSharp.GenerateGL.Generation
|
||||
methods = filter.Apply(command, methods, _typeMapper);
|
||||
}
|
||||
|
||||
result.AddRange(methods);
|
||||
result.AddRange(
|
||||
methods.Select(
|
||||
method => method with
|
||||
{
|
||||
Name = _namePolicy.ReadMethodName(command, method.Name),
|
||||
DocumentationPage = _namePolicy.ReadDocumentationPage(command),
|
||||
Parameters = Array.AsReadOnly(
|
||||
method.Parameters.Select(
|
||||
parameter => parameter with
|
||||
{
|
||||
Name = CSharpIdentifierPolicy.NormalizeParameterName(
|
||||
parameter.Name),
|
||||
}).ToArray()),
|
||||
}));
|
||||
}
|
||||
|
||||
ResolveNameCollisions(result);
|
||||
return Array.AsReadOnly(result.ToArray());
|
||||
}
|
||||
|
||||
@@ -78,6 +99,55 @@ namespace StgSharp.GenerateGL.Generation
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResolveNameCollisions(
|
||||
List<ManagedMethodDefinition> methods
|
||||
)
|
||||
{
|
||||
ILookup<string, int> indicesBySignature = methods
|
||||
.Select(
|
||||
(method, index) => new
|
||||
{
|
||||
Index = index,
|
||||
Signature = ReadManagedSignature(method),
|
||||
})
|
||||
.ToLookup(value => value.Signature, value => value.Index, StringComparer.Ordinal);
|
||||
foreach (IGrouping<string, int> group in indicesBySignature)
|
||||
{
|
||||
int[] indices = group.ToArray();
|
||||
if (indices.Length < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] nativeNames = indices
|
||||
.Select(value => methods[value].NativeCommand.Name)
|
||||
.Distinct(StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
if (nativeNames.Length != indices.Length)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Managed overload '{group.Key}' is duplicated by one native command.");
|
||||
}
|
||||
|
||||
foreach (int index in indices)
|
||||
{
|
||||
ManagedMethodDefinition method = methods[index];
|
||||
methods[index] = method with
|
||||
{
|
||||
Name = GlManagedApiNamePolicy.ReadNativeMethodName(
|
||||
method.NativeCommand),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadManagedSignature(
|
||||
ManagedMethodDefinition method
|
||||
)
|
||||
{
|
||||
return $"{method.Name}({string.Join(",", method.Parameters.Select(value => value.Type))})";
|
||||
}
|
||||
|
||||
private ManagedMethodDefinition CreateRawMethod(
|
||||
GlCommandDefinition command
|
||||
)
|
||||
|
||||
@@ -13,6 +13,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using StgSharp.GenerateGL.Analysis;
|
||||
using StgSharp.GenerateGL.Registry;
|
||||
|
||||
namespace StgSharp.GenerateGL.Generation
|
||||
@@ -23,9 +24,6 @@ namespace StgSharp.GenerateGL.Generation
|
||||
internal static class OpenGlGenerator
|
||||
{
|
||||
|
||||
private const string ApiName = "gl";
|
||||
private const string CoreProfile = "core";
|
||||
|
||||
private static readonly HashSet<string> _csharpKeywords = new HashSet<string>(
|
||||
new[]
|
||||
{
|
||||
@@ -44,25 +42,41 @@ namespace StgSharp.GenerateGL.Generation
|
||||
StringComparer.Ordinal);
|
||||
|
||||
public static GeneratedFileSet Generate(
|
||||
GlRegistryModel registry
|
||||
GlRegistryProjection projection
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(registry);
|
||||
ArgumentNullException.ThrowIfNull(projection);
|
||||
|
||||
IReadOnlyList<GlCommandGroup> commandGroups = ReadCoreCommandGroups(registry);
|
||||
IReadOnlyList<GlCommandGroup> commandGroups = ReadCoreCommandGroups(projection);
|
||||
IReadOnlyList<GlCommandDefinition> commands = Array.AsReadOnly(
|
||||
commandGroups.SelectMany(value => value.Commands).ToArray());
|
||||
GlEquivalentTypeMapper typeMapper = new GlEquivalentTypeMapper(registry);
|
||||
ManagedApiPlanner apiPlanner = new ManagedApiPlanner(typeMapper);
|
||||
GlEquivalentTypeMapper typeMapper = new GlEquivalentTypeMapper(projection.Source);
|
||||
GlFunctionFamilyAnalysis familyAnalysis = GlFunctionFamilyAnalyzer.Analyze(projection);
|
||||
ManagedApiPlanner apiPlanner = new ManagedApiPlanner(typeMapper, familyAnalysis);
|
||||
IReadOnlyList<ManagedMethodDefinition> managedMethods = apiPlanner.Plan(commands);
|
||||
Dictionary<string, IReadOnlyList<ManagedMethodDefinition>> methodsByCommand =
|
||||
managedMethods.GroupBy(value => value.NativeCommand.Name, StringComparer.Ordinal)
|
||||
.ToDictionary(
|
||||
value => value.Key,
|
||||
value => (IReadOnlyList<ManagedMethodDefinition>)Array.AsReadOnly(
|
||||
value.ToArray()),
|
||||
StringComparer.Ordinal);
|
||||
List<ManagedCommandGroup> managedGroups = commandGroups.Select(
|
||||
group => new ManagedCommandGroup(
|
||||
group,
|
||||
apiPlanner.Plan(group.Commands))).ToList();
|
||||
Array.AsReadOnly(
|
||||
group.Commands.SelectMany(
|
||||
command => methodsByCommand.TryGetValue(
|
||||
command.Name,
|
||||
out IReadOnlyList<ManagedMethodDefinition>? methods)
|
||||
? methods
|
||||
: Array.Empty<ManagedMethodDefinition>())
|
||||
.ToArray()))).ToList();
|
||||
ManagedApiPlanner.Validate(
|
||||
managedGroups.SelectMany(value => value.Methods));
|
||||
List<GeneratedFile> files = new List<GeneratedFile>
|
||||
{
|
||||
new GeneratedFile("glconst.cs", GenerateConstants(registry)),
|
||||
new GeneratedFile("glconst.cs", GenerateConstants(projection.Enums)),
|
||||
new GeneratedFile(
|
||||
"OpenglContext.g.cs",
|
||||
GenerateContext(commands, typeMapper)),
|
||||
@@ -84,67 +98,26 @@ namespace StgSharp.GenerateGL.Generation
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<GlCommandGroup> ReadCoreCommandGroups(
|
||||
GlRegistryModel registry
|
||||
GlRegistryProjection projection
|
||||
)
|
||||
{
|
||||
Dictionary<string, GlCommandDefinition> definitions = new Dictionary<string, GlCommandDefinition>(
|
||||
StringComparer.Ordinal);
|
||||
foreach (GlCommandDefinition command in registry.Commands)
|
||||
{
|
||||
if (!definitions.TryAdd(command.Name, command))
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Command '{command.Name}' has more than one definition.");
|
||||
}
|
||||
}
|
||||
|
||||
List<string> order = new List<string>();
|
||||
HashSet<string> known = new HashSet<string>(StringComparer.Ordinal);
|
||||
HashSet<string> active = new HashSet<string>(StringComparer.Ordinal);
|
||||
Dictionary<string, string> introducedVersions = new Dictionary<string, string>(
|
||||
StringComparer.Ordinal);
|
||||
foreach (GlFeatureDefinition feature in registry.Features.Where(
|
||||
value => string.Equals(value.Api, ApiName, StringComparison.Ordinal)))
|
||||
{
|
||||
foreach (GlRequirementBlock block in feature.Requirements.Where(AppliesToCoreGl))
|
||||
{
|
||||
foreach (GlRegistryReference reference in block.References.Where(
|
||||
value => value.Kind == GlRegistryReferenceKind.Command))
|
||||
{
|
||||
if (!definitions.ContainsKey(reference.Name))
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Feature '{feature.Name}' references undefined command " +
|
||||
$"'{reference.Name}'.");
|
||||
}
|
||||
|
||||
if (block.Operation == GlRequirementOperation.Require)
|
||||
{
|
||||
active.Add(reference.Name);
|
||||
if (known.Add(reference.Name))
|
||||
{
|
||||
order.Add(reference.Name);
|
||||
introducedVersions.Add(reference.Name, feature.Number);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
active.Remove(reference.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<GlCommandDefinition> foundation = new List<GlCommandDefinition>();
|
||||
Dictionary<string, List<GlCommandDefinition>> versionGroups = new Dictionary<string, List<GlCommandDefinition>>(
|
||||
StringComparer.Ordinal);
|
||||
List<string> versionOrder = new List<string>();
|
||||
foreach (string commandName in order.Where(active.Contains))
|
||||
foreach (GlCommandDefinition command in projection.Commands)
|
||||
{
|
||||
string version = introducedVersions[commandName];
|
||||
if (!projection.CommandIntroducedVersions.TryGetValue(
|
||||
command.Name,
|
||||
out string? version))
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
$"Command '{command.Name}' has no introduction version.");
|
||||
}
|
||||
|
||||
if (IsFoundationVersion(version))
|
||||
{
|
||||
foundation.Add(definitions[commandName]);
|
||||
foundation.Add(command);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -157,7 +130,7 @@ namespace StgSharp.GenerateGL.Generation
|
||||
versionOrder.Add(version);
|
||||
}
|
||||
|
||||
commands.Add(definitions[commandName]);
|
||||
commands.Add(command);
|
||||
}
|
||||
|
||||
List<GlCommandGroup> groups = new List<GlCommandGroup>
|
||||
@@ -203,23 +176,8 @@ namespace StgSharp.GenerateGL.Generation
|
||||
return major < 3 || major == 3 && minor <= 2;
|
||||
}
|
||||
|
||||
private static bool AppliesToCoreGl(
|
||||
GlRequirementBlock block
|
||||
)
|
||||
{
|
||||
bool matchesApi = block.Api is null || string.Equals(
|
||||
block.Api,
|
||||
ApiName,
|
||||
StringComparison.Ordinal);
|
||||
bool matchesProfile = block.Profile is null || string.Equals(
|
||||
block.Profile,
|
||||
CoreProfile,
|
||||
StringComparison.Ordinal);
|
||||
return matchesApi && matchesProfile;
|
||||
}
|
||||
|
||||
private static string GenerateConstants(
|
||||
GlRegistryModel registry
|
||||
IReadOnlyList<GlEnumDefinition> enums
|
||||
)
|
||||
{
|
||||
StringBuilder builder = new StringBuilder(512 * 1024);
|
||||
@@ -231,8 +189,7 @@ namespace StgSharp.GenerateGL.Generation
|
||||
|
||||
Dictionary<string, GlEnumDefinition> emitted = new Dictionary<string, GlEnumDefinition>(
|
||||
StringComparer.Ordinal);
|
||||
foreach (GlEnumDefinition value in registry.EnumBlocks.SelectMany(
|
||||
block => block.Enums).Where(IsDesktopEnum))
|
||||
foreach (GlEnumDefinition value in enums)
|
||||
{
|
||||
if (value.Value is null)
|
||||
{
|
||||
@@ -267,16 +224,6 @@ namespace StgSharp.GenerateGL.Generation
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static bool IsDesktopEnum(
|
||||
GlEnumDefinition value
|
||||
)
|
||||
{
|
||||
return value.Api is null || string.Equals(
|
||||
value.Api,
|
||||
ApiName,
|
||||
StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static ConstantExpression ReadConstantExpression(
|
||||
GlEnumDefinition value
|
||||
)
|
||||
@@ -430,6 +377,7 @@ namespace StgSharp.GenerateGL.Generation
|
||||
|
||||
foreach (ManagedMethodDefinition method in methods)
|
||||
{
|
||||
AppendMethodDocumentation(builder, method);
|
||||
builder.Append("\n [MethodImpl(MethodImplOptions.AggressiveInlining)]\n");
|
||||
builder.Append(" public ");
|
||||
builder.Append(method.ReturnType);
|
||||
@@ -460,6 +408,41 @@ namespace StgSharp.GenerateGL.Generation
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static void AppendMethodDocumentation(
|
||||
StringBuilder builder,
|
||||
ManagedMethodDefinition method
|
||||
)
|
||||
{
|
||||
string declaration = ReadNativeDeclaration(method.NativeCommand);
|
||||
string page = method.DocumentationPage ?? method.NativeCommand.Name;
|
||||
builder.Append("\n /// <summary>Forwards to the native C API <c>");
|
||||
builder.Append(EscapeXml(declaration));
|
||||
builder.Append("</c>.</summary>\n");
|
||||
builder.Append(" /// <seealso href=\"https://docs.gl/gl4/");
|
||||
builder.Append(page);
|
||||
builder.Append("\" />");
|
||||
}
|
||||
|
||||
private static string ReadNativeDeclaration(
|
||||
GlCommandDefinition command
|
||||
)
|
||||
{
|
||||
return $"{command.Return.DeclarationText}(" +
|
||||
string.Join(", ", command.Parameters.Select(value => value.DeclarationText)) +
|
||||
");";
|
||||
}
|
||||
|
||||
private static string EscapeXml(
|
||||
string value
|
||||
)
|
||||
{
|
||||
return value.Replace("&", "&", StringComparison.Ordinal)
|
||||
.Replace("<", "<", StringComparison.Ordinal)
|
||||
.Replace(">", ">", StringComparison.Ordinal)
|
||||
.Replace("\"", """, StringComparison.Ordinal)
|
||||
.Replace("'", "'", StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
private static void AppendInvocation(
|
||||
StringBuilder builder,
|
||||
ManagedMethodDefinition method
|
||||
@@ -479,12 +462,42 @@ namespace StgSharp.GenerateGL.Generation
|
||||
case Matrix4Invocation matrix:
|
||||
AppendMatrix4Invocation(builder, method, matrix);
|
||||
break;
|
||||
case QuerySpanInvocation query:
|
||||
AppendQuerySpanInvocation(builder, method, query);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException(
|
||||
$"Unsupported managed invocation '{method.Invocation.GetType().Name}'.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendQuerySpanInvocation(
|
||||
StringBuilder builder,
|
||||
ManagedMethodDefinition method,
|
||||
QuerySpanInvocation invocation
|
||||
)
|
||||
{
|
||||
ManagedParameterDefinition span = method.Parameters[invocation.SpanParameterIndex];
|
||||
string spanName = EscapeIdentifier(span.Name);
|
||||
builder.Append(" fixed (");
|
||||
builder.Append(invocation.ElementType);
|
||||
builder.Append("* __value = ");
|
||||
builder.Append(spanName);
|
||||
builder.Append(")\n");
|
||||
builder.Append(" {\n");
|
||||
builder.Append(" __context->");
|
||||
builder.Append(method.NativeCommand.Name);
|
||||
builder.Append('(');
|
||||
AppendArguments(builder, method, invocation.PrefixArguments);
|
||||
if (invocation.PrefixArguments.Count != 0)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
|
||||
builder.Append("__value);\n");
|
||||
builder.Append(" }\n");
|
||||
}
|
||||
|
||||
private static void AppendDirectInvocation(
|
||||
StringBuilder builder,
|
||||
ManagedMethodDefinition method,
|
||||
|
||||
Reference in New Issue
Block a user