SharpGen/Generator/VtblGenerator.cs (133 lines of code) (raw):
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using SharpGen.Config;
using SharpGen.Model;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace SharpGen.Generator
{
internal sealed class VtblGenerator : CodeGeneratorBase, ICodeGenerator<CsInterface, MemberDeclarationSyntax>
{
private static readonly SyntaxList<AttributeListSyntax> DebuggerTypeProxyAttribute = SingletonList(
AttributeList(
SingletonSeparatedList(
Attribute(
ParseName("System.Diagnostics.DebuggerTypeProxy"),
AttributeArgumentList(
SingletonSeparatedList(
AttributeArgument(TypeOfExpression(IdentifierName("CppObjectVtblDebugView")))
)
)
)
)
)
);
public MemberDeclarationSyntax GenerateCode(CsInterface csElement)
{
var vtblClassName = csElement.VtblName.Split('.').Last();
// Default: at least protected to enable inheritance.
var vtblVisibility = csElement.VtblVisibility ?? Visibility.ProtectedInternal;
StatementSyntax VtblMethodSelector(CsMethod method)
{
StatementSyntax MethodBuilder(PlatformDetectionType platform)
{
var arguments = new[]
{
Argument(
ObjectCreationExpression(IdentifierName(GetMethodDelegateName(method, platform)))
.WithArgumentList(
ArgumentList(
SingletonSeparatedList(
Argument(
IdentifierName(
$"{method.Name}{GeneratorHelpers.GetPlatformSpecificSuffix(platform)}"
)
)
)
)
)
),
Argument(
LiteralExpression(
SyntaxKind.NumericLiteralExpression,
Literal((platform & PlatformDetectionType.Windows) != 0
? method.WindowsOffset
: method.Offset)
)
)
};
return ExpressionStatement(
InvocationExpression(IdentifierName("AddMethod"))
.WithArgumentList(ArgumentList(SeparatedList(arguments)))
);
}
return GeneratorHelpers.GetPlatformSpecificStatements(GlobalNamespace, Generators.Config,
method.InteropSignatures.Keys, MethodBuilder);
}
List<MemberDeclarationSyntax> members = new()
{
ConstructorDeclaration(Identifier(vtblClassName))
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)))
.WithParameterList(
ParameterList(
SingletonSeparatedList(
Parameter(Identifier("numberOfCallbackMethods"))
.WithType(PredefinedType(Token(SyntaxKind.IntKeyword)))
)
)
)
.WithInitializer(
ConstructorInitializer(
SyntaxKind.BaseConstructorInitializer,
ArgumentList(
SingletonSeparatedList(
Argument(
BinaryExpression(
SyntaxKind.AddExpression,
IdentifierName("numberOfCallbackMethods"),
LiteralExpression(
SyntaxKind.NumericLiteralExpression,
Literal(csElement.MethodList.Count)
)
)
)
)
)
)
)
.WithBody(
Block(
csElement.Methods
.OrderBy(method => method.Offset)
.Select(VtblMethodSelector)
)
)
};
members.AddRange(csElement.Methods.SelectMany(method => Generators.ShadowCallable.GenerateCode(method)));
return ClassDeclaration(vtblClassName)
.WithModifiers(
ModelUtilities.VisibilityToTokenList(vtblVisibility, SyntaxKind.UnsafeKeyword,
SyntaxKind.PartialKeyword)
)
.WithAttributeLists(DebuggerTypeProxyAttribute)
.WithBaseList(
BaseList(
SingletonSeparatedList<BaseTypeSyntax>(
SimpleBaseType(
csElement.Base != null
? IdentifierName(csElement.Base.VtblName)
: GlobalNamespace.GetTypeNameSyntax(WellKnownName.CppObjectVtbl)
)
)
)
)
.WithMembers(List(members));
}
internal static string GetMethodDelegateName(CsCallable csElement, PlatformDetectionType platform) =>
csElement.Name + "Delegate" + GeneratorHelpers.GetPlatformSpecificSuffix(platform);
public VtblGenerator(Ioc ioc) : base(ioc)
{
}
}
}