mirror of https://github.com/mono/CppSharp.git
c-sharpdotnetmonobindingsbridgecclangcpluspluscppsharpglueinteropparserparsingpinvokeswigsyntax-treevisitorsxamarinxamarin-bindings
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
412 lines
17 KiB
412 lines
17 KiB
using System.Collections.Generic; |
|
using System.Globalization; |
|
using System.Linq; |
|
using System.Text; |
|
using CppSharp.AST; |
|
using CppSharp.AST.Extensions; |
|
using CppSharp.Generators; |
|
using CppSharp.Generators.C; |
|
|
|
namespace CppSharp.Passes |
|
{ |
|
public class SymbolsCodeGenerator : CodeGenerator |
|
{ |
|
public override string FileExtension => "cpp"; |
|
|
|
public SymbolsCodeGenerator(BindingContext context, IEnumerable<TranslationUnit> units) |
|
: base(context, units) |
|
{ |
|
cppTypePrinter = new CppTypePrinter(Context) |
|
{ |
|
ScopeKind = TypePrintScopeKind.Qualified, |
|
ResolveTypedefs = true, |
|
ResolveTypeMaps = false |
|
}; |
|
|
|
cppTypePrinter.PushContext(TypePrinterContextKind.Native); |
|
} |
|
|
|
public override void Process() |
|
{ |
|
if (TranslationUnit.Module == Options.SystemModule) |
|
{ |
|
WriteLine("#define _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); |
|
WriteLine("#define _LIBCPP_HIDE_FROM_ABI"); |
|
NewLine(); |
|
WriteLine("#include <string>"); |
|
} |
|
else |
|
foreach (var header in TranslationUnit.Module.Headers) |
|
WriteLine($"#include <{header}>"); |
|
WriteLine("#include <new>"); |
|
NewLine(); |
|
} |
|
|
|
public override bool VisitMethodDecl(Method method) |
|
{ |
|
if (method.IsDestructor && |
|
(!((Class) method.Namespace).HasNonTrivialDestructor || |
|
method.Access == AccessSpecifier.Private)) |
|
return false; |
|
|
|
if (method.Namespace is ClassTemplateSpecialization && |
|
(method.TranslationUnit.IsSystemHeader || |
|
(!method.IsImplicit && !method.IsDefaulted && !method.IsPure && |
|
string.IsNullOrEmpty(method.Body)))) |
|
{ |
|
WriteLine($"template {GetExporting()}{method.Visit(cppTypePrinter)};"); |
|
return true; |
|
} |
|
|
|
Class @class = (Class) method.Namespace; |
|
bool needSubclass = (method.Access == AccessSpecifier.Protected || |
|
@class.IsAbstract) && (method.IsConstructor || method.IsDestructor); |
|
string wrapper = GetWrapper(method); |
|
int i = 0; |
|
foreach (var param in method.Parameters.Where( |
|
p => string.IsNullOrEmpty(p.OriginalName))) |
|
param.Name = "_" + i++; |
|
var @params = string.Join(", ", method.Parameters.Select(CastIfRVReference)); |
|
if (needSubclass) |
|
{ |
|
string className = @class.Visit(cppTypePrinter); |
|
Write($"class {wrapper}{method.Namespace.Name} : public {className} {{ public: "); |
|
Write(wrapper + method.Namespace.Name); |
|
Write($@"({string.Join(", ", method.Parameters.Select( |
|
p => cppTypePrinter.VisitParameter(p)))})"); |
|
Write($": {className}({@params}) {{}}; "); |
|
|
|
ImplementIfAbstract(@class); |
|
} |
|
if (method.IsConstructor) |
|
{ |
|
WrapConstructor(method, wrapper, @params); |
|
return true; |
|
} |
|
if (method.IsDestructor) |
|
{ |
|
WrapDestructor(method, wrapper); |
|
return true; |
|
} |
|
TakeFunctionAddress(method, wrapper); |
|
return true; |
|
} |
|
|
|
public override bool VisitFunctionDecl(Function function) |
|
{ |
|
TakeFunctionAddress(function, GetWrapper(function)); |
|
return true; |
|
} |
|
|
|
public override bool VisitVariableDecl(Variable variable) |
|
{ |
|
if (!(variable.Namespace is ClassTemplateSpecialization specialization) || |
|
specialization.SpecializationKind == TemplateSpecializationKind.ExplicitSpecialization) |
|
return false; |
|
|
|
WriteLine($"template {GetExporting()}{variable.Visit(cppTypePrinter)};"); |
|
return true; |
|
} |
|
|
|
private string GetExporting() |
|
{ |
|
return Context.ParserOptions.IsMicrosoftAbi ? |
|
"__declspec(dllexport) " : string.Empty; |
|
} |
|
|
|
private string GetWrapper(Function function) |
|
{ |
|
if (function is Method method && (method.IsConstructor || method.IsDestructor)) |
|
{ |
|
var nameBuilder = new StringBuilder(method.USR); |
|
for (int i = 0; i < nameBuilder.Length; i++) |
|
if (!char.IsLetterOrDigit(nameBuilder[i]) && |
|
nameBuilder[i] != '_') |
|
{ |
|
if (nameBuilder[i] == '&') |
|
nameBuilder.Insert(i + 1, '_'); |
|
nameBuilder[i] = '_'; |
|
} |
|
string @class = function.Namespace.Name; |
|
nameBuilder.Replace("c__S_", string.Empty).Replace( |
|
$"{@class}_F_", $"{@class}_").TrimUnderscores(); |
|
if (function.IsOperator) |
|
nameBuilder.Append(function.OperatorKind); |
|
return nameBuilder.ToString(); |
|
} |
|
return $"_{functionCount++}"; |
|
} |
|
|
|
private void WrapConstructor(Method method, string wrapper, string @params) |
|
{ |
|
if (Options.CheckSymbols) |
|
{ |
|
method.Mangled = wrapper; |
|
method.CallingConvention = CallingConvention.C; |
|
} |
|
|
|
int i = 0; |
|
foreach (var param in method.Parameters.Where( |
|
p => string.IsNullOrEmpty(p.OriginalName))) |
|
param.Name = "_" + i++; |
|
var signature = string.Join(", ", method.GatherInternalParams( |
|
Context.ParserOptions.IsItaniumLikeAbi).Select( |
|
p => cppTypePrinter.VisitParameter(p))); |
|
|
|
if (method.Access != AccessSpecifier.Protected) |
|
Write("extern \"C\" "); |
|
Write($"{GetExporting()}void {wrapper}({signature}) "); |
|
|
|
if (method.Access == AccessSpecifier.Protected || |
|
((Class) method.Namespace).IsAbstract) |
|
{ |
|
Write($@"{{ ::new ({Helpers.InstanceField}) { |
|
wrapper}{method.Namespace.Name}({@params}); }}"); |
|
WriteLine(method.Access == AccessSpecifier.Protected ? " };" : string.Empty); |
|
} |
|
else |
|
{ |
|
string @namespace = method.Namespace.Visit(cppTypePrinter); |
|
WriteLine($"{{ ::new ({Helpers.InstanceField}) {@namespace}({@params}); }}"); |
|
} |
|
|
|
foreach (var param in method.Parameters.Where(p => |
|
string.IsNullOrEmpty(p.OriginalName))) |
|
param.Name = param.OriginalName; |
|
} |
|
|
|
private string CastIfRVReference(Parameter p) |
|
{ |
|
var pointer = p.Type.Desugar() as PointerType; |
|
if (pointer == null || |
|
pointer.Modifier != PointerType.TypeModifier.RVReference) |
|
return p.Name; |
|
|
|
return $@"({pointer.Visit( |
|
cppTypePrinter, p.QualifiedType.Qualifiers)}) {p.Name}"; |
|
} |
|
|
|
private void WrapDestructor(Method method, string wrapper) |
|
{ |
|
if (Options.CheckSymbols) |
|
{ |
|
method.Mangled = wrapper; |
|
method.CallingConvention = CallingConvention.C; |
|
} |
|
|
|
bool needSubclass = method.Access == AccessSpecifier.Protected || |
|
((Class) method.Namespace).IsAbstract; |
|
string @namespace = method.Namespace.Visit(cppTypePrinter); |
|
if (!needSubclass) |
|
Write("extern \"C\" "); |
|
Write($"{GetExporting()}void {wrapper}"); |
|
if (needSubclass) |
|
Write("Protected"); |
|
|
|
string instance = Helpers.InstanceField; |
|
if (needSubclass) |
|
{ |
|
string @class = wrapper + method.Namespace.Name; |
|
WriteLine($"() {{ this->~{@class}(); }} }};"); |
|
Write($@"extern ""C"" {GetExporting()}void {wrapper}({ |
|
@class}* {instance}) {{ {instance}->{wrapper}Protected"); |
|
} |
|
else |
|
Write($@"({$"{@namespace}*{instance}"}) {{ { |
|
instance}->~{method.Namespace.Name}"); |
|
WriteLine("(); }"); |
|
} |
|
|
|
private void TakeFunctionAddress(Function function, string wrapper) |
|
{ |
|
string @namespace = function.OriginalNamespace.Visit(cppTypePrinter); |
|
if (function.Access == AccessSpecifier.Protected) |
|
{ |
|
Write($"class {wrapper}{function.Namespace.Name} : public {@namespace} {{ public: "); |
|
Write("static constexpr "); |
|
} |
|
TypePrinterResult returnType = function.OriginalReturnType.Visit(cppTypePrinter); |
|
string signature = GetSignature(function); |
|
|
|
string functionName = GetFunctionName(function, @namespace); |
|
if (function.FriendKind != FriendKind.None) |
|
WriteRedeclaration(function, returnType, signature, functionName); |
|
|
|
var method = function as Method; |
|
if (function.Namespace.Access == AccessSpecifier.Protected) |
|
Write($@"class {wrapper}{function.Namespace.Name} : public { |
|
function.Namespace.Namespace.Visit(cppTypePrinter)} {{ "); |
|
|
|
string variable = $@"({(method?.IsStatic == false ? |
|
(@namespace + "::") : string.Empty)}*{wrapper}){signature}"; |
|
returnType.NameSuffix.Append(' ').Append(variable); |
|
Write(returnType); |
|
|
|
if (function.Access == AccessSpecifier.Protected) |
|
{ |
|
Write($" = &{wrapper}{function.Namespace.Name}::{functionName};"); |
|
WriteLine(" };"); |
|
Write($"auto {wrapper}Protected = {wrapper}{function.Namespace.Name}::{wrapper};"); |
|
} |
|
else |
|
{ |
|
Write($" = &{functionName};"); |
|
} |
|
if (function.Namespace.Access == AccessSpecifier.Protected) |
|
Write(" };"); |
|
NewLine(); |
|
} |
|
|
|
private string GetSignature(Function function) |
|
{ |
|
var method = function as Method; |
|
|
|
var paramTypes = string.Join(", ", function.Parameters.Where( |
|
p => p.Kind == ParameterKind.Regular).Select( |
|
cppTypePrinter.VisitParameterDecl)); |
|
|
|
var variadicType = function.IsVariadic ? |
|
(function.Parameters.Any( |
|
p => p.Kind == ParameterKind.Regular) ? ", ..." : "...") : |
|
string.Empty; |
|
|
|
var @const = method?.IsConst == true ? " const" : string.Empty; |
|
|
|
var refQualifier = method == null || method.RefQualifier == RefQualifier.None ? |
|
string.Empty : (method.RefQualifier == RefQualifier.LValue ? " &" : " &&"); |
|
|
|
return $"({paramTypes}{variadicType}){@const}{refQualifier}"; |
|
} |
|
|
|
private string GetFunctionName(Function function, string @namespace) |
|
{ |
|
var nameBuilder = new StringBuilder(); |
|
if (function.Access != AccessSpecifier.Protected && |
|
!string.IsNullOrEmpty(@namespace)) |
|
nameBuilder.Append(@namespace).Append("::"); |
|
|
|
bool isConversionToSpecialization = |
|
(function.OperatorKind == CXXOperatorKind.Conversion || |
|
function.OperatorKind == CXXOperatorKind.ExplicitConversion) && |
|
function.OriginalReturnType.Type.Desugar( |
|
).TryGetDeclaration(out ClassTemplateSpecialization specialization); |
|
|
|
nameBuilder.Append(isConversionToSpecialization ? |
|
"operator " : function.OriginalName); |
|
|
|
if (function.SpecializationInfo != null) |
|
nameBuilder.Append('<').Append(string.Join(", ", |
|
GetTemplateArguments(function.SpecializationInfo.Arguments))).Append('>'); |
|
else if (isConversionToSpecialization) |
|
nameBuilder.Append(function.OriginalReturnType.Visit(cppTypePrinter)); |
|
|
|
return nameBuilder.ToString(); |
|
} |
|
|
|
private IEnumerable<string> GetTemplateArguments( |
|
IEnumerable<TemplateArgument> templateArguments) |
|
{ |
|
return templateArguments.Select( |
|
a => |
|
{ |
|
switch (a.Kind) |
|
{ |
|
case TemplateArgument.ArgumentKind.Type: |
|
return a.Type.Visit(cppTypePrinter).ToString(); |
|
case TemplateArgument.ArgumentKind.Declaration: |
|
return a.Declaration.Visit(cppTypePrinter).ToString(); |
|
case TemplateArgument.ArgumentKind.Integral: |
|
return a.Integral.ToString(CultureInfo.InvariantCulture); |
|
} |
|
throw new System.ArgumentOutOfRangeException( |
|
nameof(a.Kind), a.Kind, "Unsupported kind of template argument."); |
|
}); |
|
} |
|
|
|
private void WriteRedeclaration(Function function, string returnType, |
|
string paramTypes, string functionName) |
|
{ |
|
Stack<string> parentsOpen = GenerateNamespace(function); |
|
var functionType = (FunctionType) function.FunctionType.Type; |
|
Write($"{string.Concat(parentsOpen)}"); |
|
if (function.IsConstExpr) |
|
Write("constexpr "); |
|
Write(returnType); |
|
Write(" "); |
|
Write(parentsOpen.Count > 0 ? function.OriginalName : functionName); |
|
Write(paramTypes); |
|
if (functionType.ExceptionSpecType == ExceptionSpecType.BasicNoexcept) |
|
Write(" noexcept"); |
|
WriteLine($";{string.Concat(parentsOpen.Select(_ => " }"))}"); |
|
} |
|
|
|
private static Stack<string> GenerateNamespace(Function function) |
|
{ |
|
var declarationContextsInSignature = new List<DeclarationContext> { function.Namespace }; |
|
var typesInSignature = new List<Type> { function.OriginalReturnType.Type }; |
|
typesInSignature.AddRange(function.Parameters.Select(p => p.Type)); |
|
foreach (var type in typesInSignature) |
|
{ |
|
Declaration declaration; |
|
var finalType = (type.Desugar().GetFinalPointee() ?? type).Desugar(); |
|
if (finalType.TryGetDeclaration(out declaration)) |
|
declarationContextsInSignature.Add(declaration.Namespace); |
|
} |
|
var nestedNamespace = declarationContextsInSignature.Find(d => |
|
d.Namespace is Namespace && !(d.Namespace is TranslationUnit)); |
|
var parentsOpen = new Stack<string>(); |
|
if (nestedNamespace != null) |
|
{ |
|
var @namespace = nestedNamespace; |
|
while (!(@namespace is Namespace)) |
|
@namespace = @namespace.Namespace; |
|
while (!(@namespace is TranslationUnit)) |
|
{ |
|
parentsOpen.Push($"namespace {@namespace.OriginalName} {{ "); |
|
@namespace = @namespace.Namespace; |
|
} |
|
} |
|
return parentsOpen; |
|
} |
|
|
|
private void ImplementIfAbstract(Class @class) |
|
{ |
|
if (!@class.IsAbstract) |
|
return; |
|
|
|
Method abstractDtor = null; |
|
cppTypePrinter.MethodScopeKind = TypePrintScopeKind.Local; |
|
foreach (Method @abstract in @class.GetAbstractMethods()) |
|
{ |
|
if (@abstract.IsDestructor) |
|
{ |
|
abstractDtor = @abstract; |
|
continue; |
|
} |
|
Write(@abstract.Visit(cppTypePrinter) + " {"); |
|
Type returnType = @abstract.OriginalReturnType.Type.Desugar(); |
|
if (returnType.IsReference()) |
|
{ |
|
var pointee = returnType.GetPointee().Desugar().Visit(cppTypePrinter); |
|
Write($" static bool __value = false; return ({pointee}&) __value; "); |
|
} |
|
else if (!returnType.IsPrimitiveType(PrimitiveType.Void)) |
|
Write(" return {}; "); |
|
Write("} "); |
|
} |
|
cppTypePrinter.MethodScopeKind = TypePrintScopeKind.Qualified; |
|
|
|
WriteLine(" };"); |
|
if (abstractDtor != null && !implementedDtors.Contains(abstractDtor)) |
|
{ |
|
WriteLine($"{abstractDtor.Namespace.Name}::{abstractDtor.Name}() {{}}"); |
|
implementedDtors.Add(abstractDtor); |
|
} |
|
} |
|
|
|
private CppTypePrinter cppTypePrinter; |
|
|
|
private int functionCount; |
|
private HashSet<Method> implementedDtors = new HashSet<Method>(); |
|
} |
|
}
|
|
|