Browse Source

Generate C++ for constructors of abstract types

Signed-off-by: Dimitar Dobrev <dpldobrev@protonmail.com>
pull/1507/head
Dimitar Dobrev 5 years ago
parent
commit
0fd4307b81
  1. 14
      src/AST/ClassExtensions.cs
  2. 69
      src/Generator/Generators/C/CppTypePrinter.cs
  3. 17
      src/Generator/Passes/GenerateAbstractImplementationsPass.cs
  4. 21
      src/Generator/Passes/GenerateSymbolsPass.cs
  5. 146
      src/Generator/Passes/SymbolsCodeGenerator.cs

14
src/AST/ClassExtensions.cs

@ -30,6 +30,20 @@ namespace CppSharp.AST @@ -30,6 +30,20 @@ namespace CppSharp.AST
}
}
public static List<Method> GetAbstractMethods(this Class @class)
{
var abstractMethods = @class.Methods.Where(m => m.IsPure).ToList();
var abstractOverrides = abstractMethods.Where(a => a.IsOverride).ToArray();
foreach (var baseAbstractMethods in @class.Bases.Select(b => GetAbstractMethods(b.Class)))
{
for (var i = baseAbstractMethods.Count - 1; i >= 0; i--)
if (abstractOverrides.Any(a => a.CanOverride(baseAbstractMethods[i])))
baseAbstractMethods.RemoveAt(i);
abstractMethods.AddRange(baseAbstractMethods);
}
return abstractMethods;
}
public static Class GetNonIgnoredRootBase(this Class @class)
{
while (true)

69
src/Generator/Generators/C/CppTypePrinter.cs

@ -21,6 +21,7 @@ namespace CppSharp.Generators.C @@ -21,6 +21,7 @@ namespace CppSharp.Generators.C
public bool PrintTypeQualifiers { get; set; }
public bool PrintTypeModifiers { get; set; }
public bool PrintVariableArrayAsPointers { get; set; }
public TypePrintScopeKind MethodScopeKind = TypePrintScopeKind.Qualified;
public CppTypePrinter(BindingContext context) : base(TypePrinterContextKind.Native)
{
@ -522,11 +523,13 @@ namespace CppSharp.Generators.C @@ -522,11 +523,13 @@ namespace CppSharp.Generators.C
var functionType = method.FunctionType.Type.Desugar() as FunctionType;
if (functionType == null)
return string.Empty;
var returnType = method.IsConstructor || method.IsDestructor ||
var @return = method.IsConstructor || method.IsDestructor ||
method.OperatorKind == CXXOperatorKind.Conversion ||
method.OperatorKind == CXXOperatorKind.ExplicitConversion ?
string.Empty : $"{method.OriginalReturnType.Visit(this)} ";
var @class = method.Namespace.Visit(this);
new TypePrinterResult() : method.OriginalReturnType.Visit(this);
string @class = GetQualifier(method);
var @params = string.Join(", ", method.Parameters.Select(p => p.Visit(this)));
var @const = (method.IsConst ? " const" : string.Empty);
var name = method.OperatorKind == CXXOperatorKind.Conversion ||
@ -534,24 +537,13 @@ namespace CppSharp.Generators.C @@ -534,24 +537,13 @@ namespace CppSharp.Generators.C
$"operator {method.OriginalReturnType.Visit(this)}" :
method.OriginalName;
string exceptionType;
switch (functionType.ExceptionSpecType)
{
case ExceptionSpecType.BasicNoexcept:
exceptionType = " noexcept";
break;
case ExceptionSpecType.NoexceptFalse:
exceptionType = " noexcept(false)";
break;
case ExceptionSpecType.NoexceptTrue:
exceptionType = " noexcept(true)";
break;
// TODO: research and handle the remaining cases
default:
exceptionType = string.Empty;
break;
}
return $"{returnType}{@class}::{name}({@params}){@const}{exceptionType}";
string exceptionType = GetExceptionType(functionType);
if (!string.IsNullOrEmpty(@return.Type))
@return.NamePrefix.Append(' ');
@return.NamePrefix.Append(@class).Append(name).Append('(').Append(@params).Append(')');
@return.NameSuffix.Append(@const).Append(exceptionType);
return @return.ToString();
}
public override TypePrinterResult VisitParameterDecl(Parameter parameter)
@ -588,10 +580,8 @@ namespace CppSharp.Generators.C @@ -588,10 +580,8 @@ namespace CppSharp.Generators.C
return VisitDeclaration(item);
}
public override TypePrinterResult VisitVariableDecl(Variable variable)
{
return VisitDeclaration(variable);
}
public override TypePrinterResult VisitVariableDecl(Variable variable) =>
$"{variable.Type.Visit(this)} {VisitDeclaration(variable)}";
public override TypePrinterResult VisitClassTemplateDecl(ClassTemplate template)
{
@ -698,5 +688,34 @@ namespace CppSharp.Generators.C @@ -698,5 +688,34 @@ namespace CppSharp.Generators.C
return string.Empty;
return string.Join(" ", stringQuals) + (appendSpace ? " " : string.Empty);
}
private string GetQualifier(Method method)
{
switch (MethodScopeKind)
{
case TypePrintScopeKind.Qualified:
return $"{method.Namespace.Visit(this)}::";
case TypePrintScopeKind.GlobalQualified:
return $"::{method.Namespace.Visit(this)}::";
default:
return string.Empty;
}
}
private static string GetExceptionType(FunctionType functionType)
{
switch (functionType.ExceptionSpecType)
{
case ExceptionSpecType.BasicNoexcept:
return " noexcept";
case ExceptionSpecType.NoexceptFalse:
return " noexcept(false)";
case ExceptionSpecType.NoexceptTrue:
return " noexcept(true)";
// TODO: research and handle the remaining cases
default:
return string.Empty;
}
}
}
}

17
src/Generator/Passes/GenerateAbstractImplementationsPass.cs

@ -123,7 +123,7 @@ namespace CppSharp.Passes @@ -123,7 +123,7 @@ namespace CppSharp.Passes
private static IEnumerable<Method> GetRelevantAbstractMethods(Class @class)
{
var abstractMethods = GetAbstractMethods(@class);
var abstractMethods = @class.GetAbstractMethods();
var overriddenMethods = GetOverriddenMethods(@class);
for (var i = abstractMethods.Count - 1; i >= 0; i--)
@ -158,21 +158,6 @@ namespace CppSharp.Passes @@ -158,21 +158,6 @@ namespace CppSharp.Passes
return abstractMethods;
}
private static List<Method> GetAbstractMethods(Class @class)
{
var abstractMethods = @class.Methods.Where(m => m.IsPure).ToList();
var abstractOverrides = abstractMethods.Where(a => a.IsOverride).ToList();
foreach (var baseAbstractMethods in @class.Bases.Select(b => GetAbstractMethods(b.Class)))
{
for (var i = baseAbstractMethods.Count - 1; i >= 0; i--)
if (abstractOverrides.Any(a => a.CanOverride(baseAbstractMethods[i])))
baseAbstractMethods.RemoveAt(i);
abstractMethods.AddRange(baseAbstractMethods);
}
return abstractMethods;
}
private static List<Method> GetOverriddenMethods(Class @class)
{
var overriddenMethods = @class.Methods.Where(m => m.IsOverride && !m.IsPure).ToList();

21
src/Generator/Passes/GenerateSymbolsPass.cs

@ -22,7 +22,6 @@ namespace CppSharp.Passes @@ -22,7 +22,6 @@ namespace CppSharp.Passes
VisitOptions.VisitNamespaceEvents = false;
VisitOptions.VisitNamespaceTemplates = false;
VisitOptions.VisitNamespaceTypedefs = false;
VisitOptions.VisitNamespaceVariables = false;
VisitOptions.VisitTemplateArguments = false;
}
@ -81,8 +80,6 @@ namespace CppSharp.Passes @@ -81,8 +80,6 @@ namespace CppSharp.Passes
if (!base.VisitFunctionDecl(function))
return false;
var module = function.TranslationUnit.Module;
if (function.IsGenerated)
{
ASTUtils.CheckTypeForSpecialization(function.OriginalReturnType.Type,
@ -95,8 +92,19 @@ namespace CppSharp.Passes @@ -95,8 +92,19 @@ namespace CppSharp.Passes
if (!NeedsSymbol(function))
return false;
var symbolsCodeGenerator = GetSymbolsCodeGenerator(module);
return function.Visit(symbolsCodeGenerator);
Module module = function.TranslationUnit.Module;
return function.Visit(GetSymbolsCodeGenerator(module));
}
public override bool VisitVariableDecl(Variable variable)
{
if (!base.VisitVariableDecl(variable) ||
!(variable.Namespace is ClassTemplateSpecialization specialization) ||
specialization.SpecializationKind == TemplateSpecializationKind.ExplicitSpecialization)
return false;
Module module = variable.TranslationUnit.Module;
return variable.Visit(GetSymbolsCodeGenerator(module));
}
public class SymbolsCodeEventArgs : EventArgs
@ -131,8 +139,7 @@ namespace CppSharp.Passes @@ -131,8 +139,7 @@ namespace CppSharp.Passes
(!string.IsNullOrEmpty(function.Body) ||
isInImplicitSpecialization || function.IsImplicit) &&
// we don't need symbols for virtual functions anyway
(method == null || (!method.IsVirtual && !method.IsSynthetized &&
(!method.IsConstructor || !((Class) method.Namespace).IsAbstract))) &&
(method == null || (!method.IsVirtual && !method.IsSynthetized)) &&
// we cannot handle nested anonymous types
(!(function.Namespace is Class) || !string.IsNullOrEmpty(function.Namespace.OriginalName)) &&
!Context.Symbols.FindLibraryBySymbol(function.Mangled, out _);

146
src/Generator/Passes/SymbolsCodeGenerator.cs

@ -51,29 +51,60 @@ namespace CppSharp.Passes @@ -51,29 +51,60 @@ namespace CppSharp.Passes
if (method.Namespace is ClassTemplateSpecialization &&
(method.TranslationUnit.IsSystemHeader ||
((method.IsConstructor || method.IsDestructor) &&
!method.IsImplicit && !method.IsDefaulted && !method.IsPure &&
(!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;
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);
WrapConstructor(method, wrapper, @params);
return true;
}
if (method.IsDestructor)
{
WrapDestructor(method);
WrapDestructor(method, wrapper);
return true;
}
return this.VisitFunctionDecl(method);
TakeFunctionAddress(method, wrapper);
return true;
}
public override bool VisitFunctionDecl(Function function)
{
TakeFunctionAddress(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;
}
@ -106,14 +137,8 @@ namespace CppSharp.Passes @@ -106,14 +137,8 @@ namespace CppSharp.Passes
return $"_{functionCount++}";
}
private static string GetDerivedType(string @namespace, string wrapper)
private void WrapConstructor(Method method, string wrapper, string @params)
{
return $"class {wrapper}{@namespace} : public {@namespace} {{ public: ";
}
private void WrapConstructor(Method method)
{
string wrapper = GetWrapper(method);
if (Options.CheckSymbols)
{
method.Mangled = wrapper;
@ -124,31 +149,27 @@ namespace CppSharp.Passes @@ -124,31 +149,27 @@ namespace CppSharp.Passes
foreach (var param in method.Parameters.Where(
p => string.IsNullOrEmpty(p.OriginalName)))
param.Name = "_" + i++;
var @params = string.Join(", ", method.Parameters.Select(CastIfRVReference));
var signature = string.Join(", ", method.GatherInternalParams(
Context.ParserOptions.IsItaniumLikeAbi).Select(
p => cppTypePrinter.VisitParameter(p)));
string @namespace = method.Namespace.Visit(cppTypePrinter);
if (method.Access == AccessSpecifier.Protected)
Class @class = (Class) method.Namespace;
bool needSubclass = method.Access == AccessSpecifier.Protected || @class.IsAbstract;
if (needSubclass)
{
Write(GetDerivedType(@namespace, wrapper));
Write(wrapper + @namespace);
Write($@"({string.Join(", ", method.Parameters.Select(
p => cppTypePrinter.VisitParameter(p)))})");
WriteLine($": {@namespace}({@params}) {{}} }};");
Write($"extern \"C\" {{ void {wrapper}({signature}) ");
WriteLine($"{{ ::new ({Helpers.InstanceField}) {wrapper}{@namespace}({@params}); }} }}");
Write($"extern \"C\" void {wrapper}({signature}) ");
WriteLine($"{{ ::new ({Helpers.InstanceField}) {wrapper}{method.Namespace.Name}({@params}); }}");
}
else
{
Write("extern \"C\" ");
if (method.Namespace.Access == AccessSpecifier.Protected)
Write($@"{{ class {wrapper}{method.Namespace.Namespace.Name} : public {
method.Namespace.Namespace.Visit(cppTypePrinter)} ");
Write($"{{ void {wrapper}({signature}) ");
Write($"{{ ::new ({Helpers.InstanceField}) {@namespace}({@params}); }} }}");
if (method.Namespace.Access == AccessSpecifier.Protected)
if (needSubclass)
Write($@"class {wrapper}{method.Namespace.Namespace.Name} : public {
method.Namespace.Namespace.Visit(cppTypePrinter)} {{ ");
Write($"{GetExporting()}void {wrapper}({signature}) ");
Write($"{{ ::new ({Helpers.InstanceField}) {@namespace}({@params}); }}");
if (needSubclass)
Write("; }");
NewLine();
}
@ -169,45 +190,36 @@ namespace CppSharp.Passes @@ -169,45 +190,36 @@ namespace CppSharp.Passes
cppTypePrinter, p.QualifiedType.Qualifiers)}) {p.Name}";
}
private void WrapDestructor(Method method)
private void WrapDestructor(Method method, string wrapper)
{
string wrapper = GetWrapper(method);
if (Options.CheckSymbols)
{
method.Mangled = wrapper;
method.CallingConvention = CallingConvention.C;
}
bool isProtected = method.Access == AccessSpecifier.Protected;
bool needSubclass = method.Access == AccessSpecifier.Protected ||
((Class) method.Namespace).IsAbstract;
string @namespace = method.Namespace.Visit(cppTypePrinter);
if (isProtected)
Write($"class {wrapper} : public {@namespace} {{ public: ");
else
Write("extern \"C\" { ");
if (method.Namespace.Access == AccessSpecifier.Protected)
Write($@"class {wrapper}{method.Namespace.Namespace.Name} : public {
method.Namespace.Namespace.Visit(cppTypePrinter)} {{ ");
Write($"void {wrapper}");
if (isProtected)
Write("extern \"C\" ");
Write($"{GetExporting()}void {wrapper}");
if (needSubclass)
Write("Protected");
string instance = Helpers.InstanceField;
string @class = isProtected ? wrapper : @namespace;
Write($"({@class}* {instance}) {{ {instance}->~{method.Namespace.Name}(); }} }};");
if (isProtected)
string @class = needSubclass ? wrapper : @namespace;
Write($"({@class}* {instance}) {{ {instance}->~{method.Namespace.Name}(); }};");
if (needSubclass)
{
NewLine();
Write($@"extern ""C"" {{ void {wrapper}({wrapper}* {instance}) {{ {
instance}->{wrapper}Protected({instance}); }} }}");
Write($@"extern ""C"" {GetExporting()}void {wrapper}({wrapper}* {instance}) {{ {
instance}->{wrapper}Protected({instance}); }}");
}
if (method.Namespace.Access == AccessSpecifier.Protected)
Write("; }");
NewLine();
}
private void TakeFunctionAddress(Function function)
private void TakeFunctionAddress(Function function, string wrapper)
{
string wrapper = GetWrapper(function);
string @namespace = function.OriginalNamespace.Visit(cppTypePrinter);
if (function.Access == AccessSpecifier.Protected)
{
@ -358,8 +370,44 @@ namespace CppSharp.Passes @@ -358,8 +370,44 @@ namespace CppSharp.Passes
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>();
}
}

Loading…
Cancel
Save