From cfba72ce1b006896806259bb9f37b8eb94e95bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Matos?= Date: Sat, 28 Mar 2020 02:14:20 +0000 Subject: [PATCH] Initial C++ generator backend. --- src/Generator/Driver.cs | 5 +- src/Generator/Generators/C/CCodeGenerator.cs | 204 +++++- src/Generator/Generators/C/CppGenerator.cs | 48 ++ src/Generator/Generators/C/CppHeaders.cs | 609 +++++++++++++++++ src/Generator/Generators/C/CppMarshal.cs | 682 +++++++++++++++++++ src/Generator/Generators/C/CppSources.cs | 626 +++++++++++++++++ src/Generator/Options.cs | 2 + src/Generator/Types/TypeMap.cs | 25 + 8 files changed, 2188 insertions(+), 13 deletions(-) create mode 100644 src/Generator/Generators/C/CppGenerator.cs create mode 100644 src/Generator/Generators/C/CppHeaders.cs create mode 100644 src/Generator/Generators/C/CppMarshal.cs create mode 100644 src/Generator/Generators/C/CppSources.cs diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 5ebce8c5..57350271 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -13,6 +13,7 @@ using CppSharp.Passes; using CppSharp.Utils; using Microsoft.CSharp; using CppSharp.Types; +using CppSharp.Generators.Cpp; namespace CppSharp { @@ -35,13 +36,15 @@ namespace CppSharp { switch (kind) { + case GeneratorKind.CPlusPlus: + return new CppGenerator(Context); case GeneratorKind.CLI: return new CLIGenerator(Context); case GeneratorKind.CSharp: return new CSharpGenerator(Context); } - return null; + throw new NotImplementedException(); } void ValidateOptions() diff --git a/src/Generator/Generators/C/CCodeGenerator.cs b/src/Generator/Generators/C/CCodeGenerator.cs index bfd6dd5f..13f47e5f 100644 --- a/src/Generator/Generators/C/CCodeGenerator.cs +++ b/src/Generator/Generators/C/CCodeGenerator.cs @@ -32,9 +32,14 @@ namespace CppSharp.Generators.C : base(context, units) { VisitOptions.VisitPropertyAccessors = true; + typePrinter = new CppTypePrinter(); } - public override string FileExtension => "h"; + public abstract override string FileExtension { get; } + + public abstract override void Process(); + + public ISet Includes = new HashSet(); public virtual string QualifiedName(Declaration decl) { @@ -44,7 +49,20 @@ namespace CppSharp.Generators.C return decl.QualifiedName; } - protected CppTypePrinter typePrinter = new CppTypePrinter(); + public string QualifiedIdentifier(Declaration decl) + { + if (!string.IsNullOrEmpty(TranslationUnit.Module.OutputNamespace)) + { + if (string.IsNullOrEmpty(decl.QualifiedName)) + return $"{decl.TranslationUnit.Module.OutputNamespace}"; + + return $"{decl.TranslationUnit.Module.OutputNamespace}::{decl.QualifiedName}"; + } + + return decl.QualifiedName; + } + + protected CppTypePrinter typePrinter; public virtual CppTypePrinter CTypePrinter => typePrinter; public bool IsCLIGenerator => Context.Options.GeneratorKind == GeneratorKind.CLI; @@ -67,16 +85,6 @@ namespace CppSharp.Generators.C return decl.IsGenerated && !AlreadyVisited(decl); } - public virtual string GetMethodIdentifier(Method method) => method.Name; - - public override void GenerateMethodSpecifier(Method method, Class @class) - { - var retType = method.ReturnType.Visit(CTypePrinter); - Write($"{retType} {GetMethodIdentifier(method)}("); - Write(CTypePrinter.VisitParameters(method.Parameters)); - Write(")"); - } - public override bool VisitTypedefDecl(TypedefDecl typedef) { if (!VisitDeclaration(typedef)) @@ -276,9 +284,181 @@ namespace CppSharp.Generators.C public override bool VisitFieldDecl(Field field) { + CTypePrinter.PushContext(TypePrinterContextKind.Native); + var typeName = field.Type.Visit(CTypePrinter); + CTypePrinter.PopContext(); + + PushBlock(BlockKind.Field, field); + + WriteLine($"{typeName} {field.Name};"); + + PopBlock(NewLineKind.BeforeNextBlock); + + return true; + } + + public virtual string GetMethodIdentifier(Function function, + TypePrinterContextKind context = TypePrinterContextKind.Managed) + { + var method = function as Method; + if (method != null) + { + if (method.OperatorKind == CXXOperatorKind.Star) + { + CTypePrinter.PushContext(TypePrinterContextKind.Native); + var conversionType = method.ReturnType.Visit(CTypePrinter); + CTypePrinter.PopContext(); + } + + if (method.OperatorKind == CXXOperatorKind.Conversion || + method.OperatorKind == CXXOperatorKind.ExplicitConversion) + { + CTypePrinter.PushContext(context); + var conversionType = method.ConversionType.Visit(CTypePrinter); + CTypePrinter.PopContext(); + + return "operator " + conversionType; + } + + if (method.IsConstructor || method.IsDestructor) + { + var @class = (Class)method.Namespace; + return @class.Name; + } + } + + return (context == TypePrinterContextKind.Managed) ? + function.Name : function.OriginalName; + } + + public override void GenerateMethodSpecifier(Method method, Class @class) + { + var isHeaderFile = FileExtension == "h"; + if (isHeaderFile) + { + if (method.IsVirtual || method.IsOverride) + Write("virtual "); + + if (method.IsStatic) + Write("static "); + + if (method.IsExplicit) + Write("explicit "); + } + + if (method.IsConstructor || method.IsDestructor || + method.OperatorKind == CXXOperatorKind.Conversion || + method.OperatorKind == CXXOperatorKind.ExplicitConversion) + { + Write($"{GetMethodIdentifier(method)}("); + } + else + { + var returnType = method.ReturnType.Visit(CTypePrinter); + Write($"{returnType} {GetMethodIdentifier(method)}("); + } + + GenerateMethodParameters(method); + + Write(")"); + + if (method.IsOverride) + Write(" override"); + } + + public virtual void GenerateMethodParameters(Function function) + { + Write(CTypePrinter.VisitParameters(function.Parameters)); + } + + public override bool VisitMethodDecl(Method method) + { + PushBlock(BlockKind.Method, method); + + GenerateMethodSpecifier(method, method.Namespace as Class); + Write(";"); + + PopBlock(NewLineKind.BeforeNextBlock); + return true; } + public override bool VisitProperty(Property property) + { + if (!(property.HasGetter || property.HasSetter)) + return false; + + if (property.Field != null) + return false; + + if (property.HasGetter) + GeneratePropertyGetter(property.GetMethod); + + if (property.HasSetter) + GeneratePropertySetter(property.SetMethod); + + //if (Options.GenerateMSDeclspecProperties) + //GenerateMSDeclspecProperty(property); + + return true; + } + + public virtual void GeneratePropertyAccessorSpecifier(Method method) + { + GenerateMethodSpecifier(method, method.Namespace as Class); + } + + public virtual void GeneratePropertyGetter(Method method) + { + PushBlock(BlockKind.Method, method); + + GeneratePropertyAccessorSpecifier(method); + WriteLine(";"); + + PopBlock(NewLineKind.BeforeNextBlock); + } + + public virtual void GeneratePropertySetter(Method method) + { + PushBlock(BlockKind.Method, method); + + GeneratePropertyAccessorSpecifier(method); + WriteLine(";"); + + PopBlock(NewLineKind.BeforeNextBlock); + } + + private void GenerateMSDeclspecProperty(Property property) + { + PushBlock(BlockKind.Property, property); + + if (property.IsStatic) + Write("static "); + + if (property.IsIndexer) + { + //GenerateIndexer(property); + //throw new System.NotImplementedException(); + } + else + { + var blocks = new List(); + + if (property.HasGetter) + blocks.Add($"get = {property.GetMethod.Name}"); + + if (property.HasSetter) + blocks.Add($"put = {property.SetMethod.Name}"); + + var getterSetter = string.Join(",", blocks); + + var type = property.QualifiedType.Visit(CTypePrinter); + WriteLine($"__declspec(property({getterSetter})) {type} {property.Name};"); + } + + PopBlock(NewLineKind.BeforeNextBlock); + } + static readonly List CReservedKeywords = new List { // C99 6.4.1: Keywords. "auto", "break", "case", "char", "const", "continue", "default", diff --git a/src/Generator/Generators/C/CppGenerator.cs b/src/Generator/Generators/C/CppGenerator.cs new file mode 100644 index 00000000..3f7aac3b --- /dev/null +++ b/src/Generator/Generators/C/CppGenerator.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using CppSharp.AST; +using CppSharp.Generators.C; + +namespace CppSharp.Generators.Cpp +{ + /// + /// C/C++ generator responsible for driving the generation of source + /// and header files. + /// + public class CppGenerator : Generator + { + private readonly CppTypePrinter typePrinter; + + public CppGenerator(BindingContext context) : base(context) + { + typePrinter = new CppTypePrinter(); + } + + public override List Generate(IEnumerable units) + { + var outputs = new List(); + + var header = new CppHeaders(Context, units); + outputs.Add(header); + + var source = new CppSources(Context, units); + outputs.Add(source); + + return outputs; + } + + public override bool SetupPasses() => true; + + public static bool ShouldGenerateClassNativeField(Class @class) + { + if (@class.IsStatic) + return false; + + return @class.IsRefType && (!@class.HasBase || !@class.HasRefBase()); + } + + protected override string TypePrinterDelegate(Type type) + { + return type.Visit(typePrinter).ToString(); + } + } +} diff --git a/src/Generator/Generators/C/CppHeaders.cs b/src/Generator/Generators/C/CppHeaders.cs new file mode 100644 index 00000000..6299487e --- /dev/null +++ b/src/Generator/Generators/C/CppHeaders.cs @@ -0,0 +1,609 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using CppSharp.AST; +using CppSharp.AST.Extensions; +using CppSharp.Generators.C; +using CppSharp.Generators.CLI; + +namespace CppSharp.Generators.Cpp +{ + /// + /// Generates C/C++ header files. + /// + public class CppHeaders : CCodeGenerator + { + public CppHeaders(BindingContext context, IEnumerable units) + : base(context, units) + { + } + + public override string FileExtension => "h"; + + public override void Process() + { + GenerateFilePreamble(CommentKind.BCPL); + + PushBlock(BlockKind.Includes); + WriteLine("#pragma once"); + NewLine(); + + if (Options.OutputInteropIncludes) + WriteLine("#include \"CppSharp.h\""); + + // Generate #include forward references. + PushBlock(BlockKind.IncludesForwardReferences); + WriteLine("#include <{0}>", TranslationUnit.IncludePath); + GenerateIncludeForwardRefs(); + PopBlock(NewLineKind.BeforeNextBlock); + PopBlock(NewLineKind.Always); + + // Generate namespace for forward references. + PushBlock(BlockKind.ForwardReferences); + GenerateForwardRefs(); + PopBlock(NewLineKind.BeforeNextBlock); + + VisitNamespace(TranslationUnit); + + PushBlock(BlockKind.Footer); + PopBlock(); + } + + public void GenerateIncludeForwardRefs() + { + var typeReferenceCollector = new CLITypeReferenceCollector(Context.TypeMaps, + Context.Options); + typeReferenceCollector.Process(TranslationUnit, filterNamespaces: false); + + var includes = new SortedSet(StringComparer.InvariantCulture); + + foreach (var typeRef in typeReferenceCollector.TypeReferences) + { + if (typeRef.Include.TranslationUnit == TranslationUnit) + continue; + + if (typeRef.Include.File == TranslationUnit.FileName) + continue; + + var include = typeRef.Include; + var unit = include.TranslationUnit; + + if (unit != null && !unit.IsDeclared) + continue; + + if(!string.IsNullOrEmpty(include.File) && include.InHeader) + includes.Add(include.ToString()); + } + + foreach (var include in includes) + WriteLine(include); + } + + private Namespace FindCreateNamespace(Namespace @namespace, Declaration decl) + { + if (decl.Namespace is TranslationUnit) + return @namespace; + + var childNamespaces = decl.Namespace.GatherParentNamespaces(); + var currentNamespace = @namespace; + + foreach (var child in childNamespaces) + currentNamespace = currentNamespace.FindCreateNamespace(child.Name); + + return currentNamespace; + } + + public Namespace ConvertForwardReferencesToNamespaces( + IEnumerable typeReferences) + { + // Create a new tree of namespaces out of the type references found. + var rootNamespace = new TranslationUnit(); + rootNamespace.Module = TranslationUnit.Module; + + var sortedRefs = typeReferences.ToList(); + sortedRefs.Sort((ref1, ref2) => + string.CompareOrdinal(ref1.FowardReference, ref2.FowardReference)); + + var forwardRefs = new SortedSet(); + + foreach (var typeRef in sortedRefs) + { + if (string.IsNullOrWhiteSpace(typeRef.FowardReference)) + continue; + + var declaration = typeRef.Declaration; + + var isIncomplete = declaration.IsIncomplete && declaration.CompleteDeclaration == null; + if (!declaration.IsGenerated || isIncomplete) + continue; + + if (!(declaration.Namespace is Namespace)) + continue; + + if (!forwardRefs.Add(typeRef.FowardReference)) + continue; + + if (typeRef.Include.InHeader) + continue; + + var @namespace = FindCreateNamespace(rootNamespace, declaration); + @namespace.TypeReferences.Add(typeRef); + } + + return rootNamespace; + } + + public void GenerateForwardRefs() + { + var typeReferenceCollector = new CLITypeReferenceCollector(Context.TypeMaps, + Context.Options); + typeReferenceCollector.Process(TranslationUnit); + + var typeReferences = typeReferenceCollector.TypeReferences; + var @namespace = ConvertForwardReferencesToNamespaces(typeReferences); + + @namespace.Visit(this); + } + + public void GenerateDeclContext(DeclarationContext decl) + { + // Generate all the type references for the module. + foreach (var typeRef in decl.TypeReferences) + { + WriteLine(typeRef.FowardReference); + } + + // Generate all the enum declarations for the module. + foreach (var @enum in decl.Enums) + { + if (!@enum.IsGenerated || @enum.IsIncomplete) + continue; + + @enum.Visit(this); + } + + // Generate all the typedef declarations for the module. + GenerateTypedefs(decl); + + // Generate all the struct/class declarations for the module. + foreach (var @class in decl.Classes) + { + @class.Visit(this); + } + + if (decl.Functions.Any(f => f.IsGenerated)) + GenerateFunctions(decl); + + foreach (var childNamespace in decl.Namespaces) + childNamespace.Visit(this); + } + + public override bool VisitNamespace(Namespace @namespace) + { + var isTopLevel = @namespace is TranslationUnit; + var generateNamespace = !isTopLevel || + !string.IsNullOrEmpty(@namespace.TranslationUnit.Module.OutputNamespace); + + if (generateNamespace) + { + PushBlock(BlockKind.Namespace, @namespace); + WriteLine("namespace {0}", isTopLevel + ? @namespace.TranslationUnit.Module.OutputNamespace + : @namespace.Name); + WriteOpenBraceAndIndent(); + } + + GenerateDeclContext(@namespace); + + if (generateNamespace) + { + UnindentAndWriteCloseBrace(); + PopBlock(NewLineKind.BeforeNextBlock); + } + + return true; + } + + public virtual void GenerateTypedefs(DeclarationContext decl) + { + foreach (var typedef in decl.Typedefs) + { + if (!typedef.IsGenerated) + continue; + + typedef.Visit(this); + } + } + + public virtual void GenerateFunctions(DeclarationContext decl) + { + PushBlock(BlockKind.FunctionsClass); + + // Generate all the function declarations for the module. + foreach (var function in decl.Functions) + { + if (!function.IsGenerated) + continue; + + function.Visit(this); + } + + PopBlock(NewLineKind.BeforeNextBlock); + } + + public override bool VisitClassDecl(Class @class) + { + if (!@class.IsGenerated || @class.IsIncomplete || @class.IsDependent) + return false; + + //if (@class.IsOpaque) + // return false; + + PushBlock(BlockKind.Class, @class); + + GenerateDeclarationCommon(@class); + + GenerateClassSpecifier(@class); + + if (@class.IsOpaque) + { + WriteLine(";"); + return false; + } + + NewLine(); + WriteLine("{"); + WriteLine("public:"); + NewLine(); + + // Process the nested types. + Indent(); + GenerateDeclContext(@class); + Unindent(); + + var nativeType = $"::{@class.QualifiedOriginalName}*"; + + if (CppGenerator.ShouldGenerateClassNativeField(@class)) + GenerateClassNativeField(@class, nativeType); + + GenerateClassConstructors(@class, nativeType); + GenerateClassProperties(@class); + GenerateClassEvents(@class); + GenerateClassMethods(@class.Methods); + + if (Options.GenerateFunctionTemplates) + GenerateClassGenericMethods(@class); + + GenerateClassVariables(@class); + + if (CppGenerator.ShouldGenerateClassNativeField(@class)) + { + PushBlock(BlockKind.AccessSpecifier); + WriteLine("protected:"); + PopBlock(NewLineKind.IfNotEmpty); + + PushBlock(BlockKind.Fields); + WriteLineIndent($"bool {Helpers.OwnsNativeInstanceIdentifier};"); + PopBlock(); + } + + PushBlock(BlockKind.AccessSpecifier); + WriteLine("private:"); + var accBlock = PopBlock(NewLineKind.IfNotEmpty); + + PushBlock(BlockKind.Fields); + GenerateClassFields(@class); + var fieldsBlock = PopBlock(); + accBlock.CheckGenerate = () => !fieldsBlock.IsEmpty; + + WriteLine("};"); + + PopBlock(NewLineKind.BeforeNextBlock); + + return true; + } + + public void GenerateClassNativeField(Class @class, string nativeType) + { + var nativeInstanceField = new Field() + { + Name = Helpers.InstanceIdentifier, + QualifiedType = new QualifiedType(new PointerType(new QualifiedType(new TagType(@class)))), + Namespace = @class + }; + + Indent(); + nativeInstanceField.Visit(this); + Unindent(); + + /*var nativeInstanceProperty = new Property() + { + Name = Helpers.InstanceIdentifier, + QualifiedType = + }; + + nativeInstanceProperty.Visit(this);*/ + } + + public virtual void GenerateClassGenericMethods(Class @class) + { + } + + public void GenerateClassConstructors(Class @class, string nativeType) + { + if (@class.IsStatic) + return; + + Indent(); + + var classNativeName = @class.Visit(CTypePrinter); + + CTypePrinter.PushContext(TypePrinterContextKind.Native); + var classManagedName = @class.Visit(CTypePrinter); + CTypePrinter.PopContext(); + + WriteLine($"{@class.Name}({classManagedName}* native);"); + NewLine(); + + foreach (var ctor in @class.Constructors) + { + if (ASTUtils.CheckIgnoreMethod(ctor) || FunctionIgnored(ctor)) + continue; + + ctor.Visit(this); + } + + if (@class.IsRefType) + { + var destructor = @class.Destructors + .FirstOrDefault(d => d.Parameters.Count == 0 && d.Access == AccessSpecifier.Public); + + if (destructor != null) + { + GenerateClassDestructor(@class); + if (Options.GenerateFinalizers) + GenerateClassFinalizer(@class); + } + } + + Unindent(); + } + + public virtual void GenerateClassDestructor(Class @class) + { + PushBlock(BlockKind.Destructor); + WriteLine($"~{@class.Name}();"); + PopBlock(NewLineKind.BeforeNextBlock); + } + + public void GenerateClassFinalizer(Class @class) + { + PushBlock(BlockKind.Finalizer); + WriteLine($"!{@class.Name}();"); + PopBlock(NewLineKind.BeforeNextBlock); + } + + public void GenerateClassFields(Class @class) + { + // Handle the case of struct (value-type) inheritance by adding the base + // properties to the managed value subtypes. + if (@class.IsValueType) + { + foreach (var @base in @class.Bases.Where(b => b.IsClass && b.Class.IsDeclared)) + { + GenerateClassFields(@base.Class); + } + } + + Indent(); + // check for value types because some of the ignored fields may back properties; + // not the case for ref types because the NativePtr pattern is used there + foreach (var field in @class.Fields.Where(f => !ASTUtils.CheckIgnoreField(f))) + { + var property = @class.Properties.FirstOrDefault(p => p.Field == field); + if (property != null && !property.IsInRefTypeAndBackedByValueClassField()) + { + field.Visit(this); + } + } + Unindent(); + } + + public override bool VisitFieldDecl(Field field) + { + PushBlock(BlockKind.Field, field); + + GenerateDeclarationCommon(field); + + var @class = field.Namespace as Class; + WriteLine($"{field.Type} {field.Name};"); + + PopBlock(); + + return true; + } + + public void GenerateClassEvents(Class @class) + { + foreach (var @event in @class.Events) + { + if (!@event.IsGenerated) continue; + @event.Visit(this); + } + } + + public override bool VisitEvent(Event @event) + { + return true; + } + + public void GenerateClassMethods(List methods) + { + if (methods.Count == 0) + return; + + Indent(); + + var @class = (Class) methods[0].Namespace; + + if (@class.IsValueType) + foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore)) + GenerateClassMethods(@base.Class.Methods.Where(m => !m.IsOperator).ToList()); + + var staticMethods = new List(); + foreach (var method in methods) + { + if (ASTUtils.CheckIgnoreMethod(method) || FunctionIgnored(method)) + continue; + + if (method.IsConstructor) + continue; + + if (method.IsStatic) + { + staticMethods.Add(method); + continue; + } + + method.Visit(this); + } + + foreach(var method in staticMethods) + method.Visit(this); + + Unindent(); + } + + public void GenerateClassVariables(Class @class) + { + foreach (var variable in @class.Variables) + { + if (!variable.IsGenerated) continue; + variable.Visit(this); + } + } + + public override void GenerateClassSpecifier(Class @class) + { + Write(@class.IsValueType ? "struct " : "class "); + Write($"{@class.Name}"); + + if (@class.IsStatic) + Write(" abstract sealed"); + + if (!@class.IsStatic && @class.HasRefBase()) + Write($" : public {QualifiedIdentifier(@class.Bases[0].Class)}"); + } + + public void GenerateClassProperties(Class @class) + { + // Handle the case of struct (value-type) inheritance by adding the base + // properties to the managed value subtypes. + if (@class.IsValueType) + { + foreach (var @base in @class.Bases.Where(b => b.IsClass && b.Class.IsDeclared)) + { + GenerateClassProperties(@base.Class); + } + } + + Indent(); + foreach (var prop in @class.Properties.Where( + prop => !ASTUtils.CheckIgnoreProperty(prop) && !TypeIgnored(prop.Type))) + { + if (prop.IsInRefTypeAndBackedByValueClassField()) + { + prop.Field.Visit(this); + continue; + } + + prop.Visit(this); + } + Unindent(); + } + + public virtual void GenerateIndexer(Property property) + { + throw new System.NotImplementedException(); + } + + public override bool VisitProperty(Property property) + { + GenerateDeclarationCommon(property); + + return base.VisitProperty(property); + } + + public override bool VisitMethodDecl(Method method) + { + if (ASTUtils.CheckIgnoreMethod(method) || FunctionIgnored(method)) + return false; + + PushBlock(BlockKind.Method, method); + GenerateDeclarationCommon(method); + + GenerateMethodSpecifier(method, method.Namespace as Class); + WriteLine(";"); + + PopBlock(NewLineKind.BeforeNextBlock); + + return true; + } + + public override bool VisitTypedefNameDecl(TypedefNameDecl typedef) + { + if (!typedef.IsGenerated) + return false; + + var functionType = typedef.Type as FunctionType; + if (functionType != null || typedef.Type.IsPointerTo(out functionType)) + { + PushBlock(BlockKind.Typedef, typedef); + GenerateDeclarationCommon(typedef); + + var @delegate = string.Format(CTypePrinter.VisitDelegate(functionType), typedef.Name); + WriteLine($"{@delegate};"); + + PopBlock(NewLineKind.BeforeNextBlock); + + return true; + } + + return false; + } + + public override bool VisitFunctionDecl(Function function) + { + if (!function.IsGenerated || FunctionIgnored(function)) + return false; + + PushBlock(BlockKind.Function, function); + + GenerateDeclarationCommon(function); + + var retType = function.ReturnType.Visit(CTypePrinter); + Write($"{retType} {function.Name}("); + + GenerateMethodParameters(function); + WriteLine(");"); + + PopBlock(); + + return true; + } + + public static bool FunctionIgnored(Function function) + { + return TypeIgnored(function.ReturnType.Type) || + function.Parameters.Any(param => TypeIgnored(param.Type)); + } + + public static bool TypeIgnored(CppSharp.AST.Type type) + { + var desugared = type.Desugar(); + var finalType = (desugared.GetFinalPointee() ?? desugared).Desugar(); + Class @class; + return finalType.TryGetClass(out @class) && @class.IsIncomplete; + } + } +} diff --git a/src/Generator/Generators/C/CppMarshal.cs b/src/Generator/Generators/C/CppMarshal.cs new file mode 100644 index 00000000..80738f86 --- /dev/null +++ b/src/Generator/Generators/C/CppMarshal.cs @@ -0,0 +1,682 @@ +using System; +using CppSharp.AST; +using CppSharp.AST.Extensions; +using CppSharp.Generators.C; +using CppSharp.Generators.CLI; +using CppSharp.Types; +using Delegate = CppSharp.AST.Delegate; +using Type = CppSharp.AST.Type; + +namespace CppSharp.Generators.Cpp +{ + public class CppMarshalNativeToManagedPrinter : MarshalPrinter + { + public CppMarshalNativeToManagedPrinter(MarshalContext marshalContext) + : base(marshalContext) + { + } + + public string MemoryAllocOperator => + (Context.Context.Options.GeneratorKind == GeneratorKind.CLI) ? + "gcnew" : "new"; + + public override bool VisitType(Type type, TypeQualifiers quals) + { + TypeMap typeMap; + if (Context.Context.TypeMaps.FindTypeMap(type, out typeMap) && typeMap.DoesMarshalling) + { + typeMap.CppMarshalToManaged(Context); + return false; + } + + return true; + } + + public override bool VisitArrayType(ArrayType array, TypeQualifiers quals) + { + switch (array.SizeType) + { + case ArrayType.ArraySize.Constant: + case ArrayType.ArraySize.Incomplete: + case ArrayType.ArraySize.Variable: + Context.Return.Write("nullptr"); + break; + default: + throw new System.NotImplementedException(); + } + + return true; + } + + public override bool VisitFunctionType(FunctionType function, TypeQualifiers quals) + { + Context.Return.Write(Context.ReturnVarName); + return true; + } + + public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals) + { + if (!VisitType(pointer, quals)) + return false; + + var pointee = pointer.Pointee.Desugar(); + + PrimitiveType primitive; + var param = Context.Parameter; + if (param != null && (param.IsOut || param.IsInOut) && + pointee.IsPrimitiveType(out primitive)) + { + Context.Return.Write(Context.ReturnVarName); + return true; + } + + if (pointee.IsPrimitiveType(out primitive)) + { + var returnVarName = Context.ReturnVarName; + + if (pointer.GetFinalQualifiedPointee().Qualifiers.IsConst != + Context.ReturnType.Qualifiers.IsConst) + { + var nativeTypePrinter = new CppTypePrinter { PrintTypeQualifiers = false }; + var returnType = Context.ReturnType.Type.Desugar(); + var constlessPointer = new PointerType() + { + IsDependent = pointer.IsDependent, + Modifier = pointer.Modifier, + QualifiedPointee = new QualifiedType(returnType.GetPointee()) + }; + var nativeConstlessTypeName = constlessPointer.Visit(nativeTypePrinter, new TypeQualifiers()); + returnVarName = string.Format("const_cast<{0}>({1})", + nativeConstlessTypeName, Context.ReturnVarName); + } + + if (pointer.Pointee is TypedefType) + { + var desugaredPointer = new PointerType() + { + IsDependent = pointer.IsDependent, + Modifier = pointer.Modifier, + QualifiedPointee = new QualifiedType(pointee) + }; + var nativeTypePrinter = new CppTypePrinter(); + var nativeTypeName = desugaredPointer.Visit(nativeTypePrinter, quals); + Context.Return.Write("reinterpret_cast<{0}>({1})", nativeTypeName, + returnVarName); + } + else + Context.Return.Write(returnVarName); + + return true; + } + + TypeMap typeMap = null; + Context.Context.TypeMaps.FindTypeMap(pointee, out typeMap); + + Class @class; + if (pointee.TryGetClass(out @class) && typeMap == null) + { + var instance = (pointer.IsReference) ? "&" + Context.ReturnVarName + : Context.ReturnVarName; + WriteClassInstance(@class, instance); + return true; + } + + return pointer.QualifiedPointee.Visit(this); + } + + public override bool VisitMemberPointerType(MemberPointerType member, + TypeQualifiers quals) + { + return false; + } + + public override bool VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals) + { + return VisitPrimitiveType(builtin.Type); + } + + public bool VisitPrimitiveType(PrimitiveType primitive) + { + switch (primitive) + { + case PrimitiveType.Void: + return true; + case PrimitiveType.Bool: + case PrimitiveType.Char: + case PrimitiveType.Char16: + case PrimitiveType.WideChar: + case PrimitiveType.SChar: + case PrimitiveType.UChar: + case PrimitiveType.Short: + case PrimitiveType.UShort: + case PrimitiveType.Int: + case PrimitiveType.UInt: + case PrimitiveType.Long: + case PrimitiveType.ULong: + case PrimitiveType.LongLong: + case PrimitiveType.ULongLong: + case PrimitiveType.Float: + case PrimitiveType.Double: + case PrimitiveType.LongDouble: + case PrimitiveType.Null: + Context.Return.Write(Context.ReturnVarName); + return true; + } + + throw new NotSupportedException(); + } + + public override bool VisitTypedefType(TypedefType typedef, TypeQualifiers quals) + { + var decl = typedef.Declaration; + + TypeMap typeMap; + if (Context.Context.TypeMaps.FindTypeMap(decl.Type, out typeMap) && + typeMap.DoesMarshalling) + { + typeMap.Type = typedef; + typeMap.CppMarshalToManaged(Context); + return typeMap.IsValueType; + } + + FunctionType function; + if (decl.Type.IsPointerTo(out function)) + { + throw new System.NotImplementedException(); + } + + return decl.Type.Visit(this); + } + + public override bool VisitTemplateSpecializationType(TemplateSpecializationType template, + TypeQualifiers quals) + { + TypeMap typeMap; + if (Context.Context.TypeMaps.FindTypeMap(template, out typeMap) && typeMap.DoesMarshalling) + { + typeMap.Type = template; + typeMap.CppMarshalToManaged(Context); + return true; + } + + return template.Template.Visit(this); + } + + public override bool VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals) + { + throw new NotImplementedException(); + } + + public override bool VisitPrimitiveType(PrimitiveType type, TypeQualifiers quals) + { + throw new NotImplementedException(); + } + + public override bool VisitDeclaration(Declaration decl, TypeQualifiers quals) + { + throw new NotImplementedException(); + } + + public override bool VisitClassDecl(Class @class) + { + if (@class.CompleteDeclaration != null) + return VisitClassDecl(@class.CompleteDeclaration as Class); + + var instance = string.Empty; + + if (Context.Context.Options.GeneratorKind == GeneratorKind.CLI) + { + if (!Context.ReturnType.Type.IsPointer()) + instance += "&"; + } + + instance += Context.ReturnVarName; + var needsCopy = Context.MarshalKind != MarshalKind.NativeField; + + if (@class.IsRefType && needsCopy) + { + var name = Generator.GeneratedIdentifier(Context.ReturnVarName); + Context.Before.WriteLine($"auto {name} = {MemoryAllocOperator} ::{0}({1});", + @class.QualifiedOriginalName, Context.ReturnVarName); + instance = name; + } + + WriteClassInstance(@class, instance); + return true; + } + + public string QualifiedIdentifier(Declaration decl) + { + if (!string.IsNullOrEmpty(decl.TranslationUnit.Module.OutputNamespace)) + return $"{decl.TranslationUnit.Module.OutputNamespace}::{decl.QualifiedName}"; + + return decl.QualifiedName; + } + + public void WriteClassInstance(Class @class, string instance) + { + if (!Context.ReturnType.Type.IsPointer()) + { + Context.Return.Write($"{instance}"); + return; + } + + if (@class.IsRefType) + Context.Return.Write($"({instance} == nullptr) ? nullptr : {MemoryAllocOperator} "); + + Context.Return.Write($"{QualifiedIdentifier(@class)}("); + Context.Return.Write($"(::{@class.QualifiedOriginalName}*)"); + Context.Return.Write($"{instance})"); + } + + public override bool VisitFieldDecl(Field field) + { + return field.Type.Visit(this); + } + + public override bool VisitFunctionDecl(Function function) + { + throw new NotImplementedException(); + } + + public override bool VisitMethodDecl(Method method) + { + throw new NotImplementedException(); + } + + public override bool VisitParameterDecl(Parameter parameter) + { + Context.Parameter = parameter; + var ret = parameter.Type.Visit(this, parameter.QualifiedType.Qualifiers); + Context.Parameter = null; + + return ret; + } + + public override bool VisitTypedefDecl(TypedefDecl typedef) + { + throw new NotImplementedException(); + } + + public override bool VisitEnumDecl(Enumeration @enum) + { + var typePrinter = new CppTypePrinter(); + var typeName = typePrinter.VisitDeclaration(@enum); + Context.Return.Write($"({typeName}){Context.ReturnVarName}"); + + return true; + } + + public override bool VisitVariableDecl(Variable variable) + { + return variable.Type.Visit(this, variable.QualifiedType.Qualifiers); + } + + public override bool VisitClassTemplateDecl(ClassTemplate template) + { + return template.TemplatedClass.Visit(this); + } + + public override bool VisitFunctionTemplateDecl(FunctionTemplate template) + { + return template.TemplatedFunction.Visit(this); + } + } + + public class CppMarshalManagedToNativePrinter : MarshalPrinter + { + public readonly TextGenerator VarPrefix; + public readonly TextGenerator ArgumentPrefix; + + public CppMarshalManagedToNativePrinter(MarshalContext ctx) + : base(ctx) + { + VarPrefix = new TextGenerator(); + ArgumentPrefix = new TextGenerator(); + + Context.MarshalToNative = this; + } + + public override bool VisitType(Type type, TypeQualifiers quals) + { + TypeMap typeMap; + if (Context.Context.TypeMaps.FindTypeMap(type, out typeMap) && typeMap.DoesMarshalling) + { + typeMap.CppMarshalToNative(Context); + return false; + } + + return true; + } + + public override bool VisitTagType(TagType tag, TypeQualifiers quals) + { + if (!VisitType(tag, quals)) + return false; + + return tag.Declaration.Visit(this); + } + + public override bool VisitArrayType(ArrayType array, TypeQualifiers quals) + { + if (!VisitType(array, quals)) + return false; + + switch (array.SizeType) + { + default: + Context.Return.Write("nullptr"); + break; + } + + return true; + } + + public override bool VisitFunctionType(FunctionType function, TypeQualifiers quals) + { + var returnType = function.ReturnType; + return returnType.Visit(this); + } + + public bool VisitDelegateType(string type) + { + Context.Return.Write(Context.Parameter.Name); + return true; + } + + public override bool VisitPointerType(PointerType pointer, TypeQualifiers quals) + { + if (!VisitType(pointer, quals)) + return false; + + var pointee = pointer.Pointee.Desugar(); + + if (pointee is FunctionType) + { + var cppTypePrinter = new CppTypePrinter(); + var cppTypeName = pointer.Visit(cppTypePrinter, quals); + + return VisitDelegateType(cppTypeName); + } + + Enumeration @enum; + if (pointee.TryGetEnum(out @enum)) + { + var isRef = Context.Parameter.Usage == ParameterUsage.Out || + Context.Parameter.Usage == ParameterUsage.InOut; + + ArgumentPrefix.Write("&"); + Context.Return.Write($"(::{@enum.QualifiedOriginalName}){0}{Context.Parameter.Name}", + isRef ? string.Empty : "*"); + return true; + } + + Class @class; + if (pointee.TryGetClass(out @class) && @class.IsValueType) + { + if (Context.Function == null) + Context.Return.Write("&"); + return pointer.QualifiedPointee.Visit(this); + } + + var finalPointee = pointer.GetFinalPointee(); + if (finalPointee.IsPrimitiveType()) + { + var cppTypePrinter = new CppTypePrinter(); + var cppTypeName = pointer.Visit(cppTypePrinter, quals); + + Context.Return.Write($"({cppTypeName})"); + Context.Return.Write(Context.Parameter.Name); + return true; + } + + return pointer.QualifiedPointee.Visit(this); + } + + public override bool VisitMemberPointerType(MemberPointerType member, + TypeQualifiers quals) + { + return false; + } + + public override bool VisitBuiltinType(BuiltinType builtin, TypeQualifiers quals) + { + return VisitPrimitiveType(builtin.Type); + } + + public bool VisitPrimitiveType(PrimitiveType primitive) + { + switch (primitive) + { + case PrimitiveType.Void: + return true; + case PrimitiveType.Bool: + case PrimitiveType.Char: + case PrimitiveType.UChar: + case PrimitiveType.Short: + case PrimitiveType.UShort: + case PrimitiveType.Int: + case PrimitiveType.UInt: + case PrimitiveType.Long: + case PrimitiveType.ULong: + case PrimitiveType.LongLong: + case PrimitiveType.ULongLong: + case PrimitiveType.Float: + case PrimitiveType.Double: + Context.Return.Write(Context.Parameter.Name); + return true; + } + + return false; + } + + public override bool VisitTypedefType(TypedefType typedef, TypeQualifiers quals) + { + var decl = typedef.Declaration; + + TypeMap typeMap; + if (Context.Context.TypeMaps.FindTypeMap(decl.Type, out typeMap) && + typeMap.DoesMarshalling) + { + typeMap.CppMarshalToNative(Context); + return typeMap.IsValueType; + } + + FunctionType func; + if (decl.Type.IsPointerTo(out func)) + { + // Use the original typedef name if available, otherwise just use the function pointer type + string cppTypeName; + if (!decl.IsSynthetized) + cppTypeName = "::" + typedef.Declaration.QualifiedOriginalName; + else + { + var cppTypePrinter = new CppTypePrinter(); + cppTypeName = decl.Type.Visit(cppTypePrinter, quals); + } + + VisitDelegateType(cppTypeName); + return true; + } + + PrimitiveType primitive; + if (decl.Type.IsPrimitiveType(out primitive)) + { + Context.Return.Write($"(::{typedef.Declaration.QualifiedOriginalName})"); + } + + return decl.Type.Visit(this); + } + + public override bool VisitTemplateSpecializationType(TemplateSpecializationType template, + TypeQualifiers quals) + { + TypeMap typeMap; + if (Context.Context.TypeMaps.FindTypeMap(template, out typeMap) && typeMap.DoesMarshalling) + { + typeMap.Type = template; + typeMap.CppMarshalToNative(Context); + return true; + } + + return template.Template.Visit(this); + } + + public override bool VisitTemplateParameterType(TemplateParameterType param, TypeQualifiers quals) + { + Context.Return.Write(param.Parameter.Name); + return true; + } + + public override bool VisitPrimitiveType(PrimitiveType type, TypeQualifiers quals) + { + throw new NotImplementedException(); + } + + public override bool VisitDeclaration(Declaration decl, TypeQualifiers quals) + { + throw new NotImplementedException(); + } + + public override bool VisitClassDecl(Class @class) + { + if (@class.CompleteDeclaration != null) + return VisitClassDecl(@class.CompleteDeclaration as Class); + + if (@class.IsValueType) + { + MarshalValueClass(@class); + } + else + { + MarshalRefClass(@class); + } + + return true; + } + + private void MarshalRefClass(Class @class) + { + var type = Context.Parameter.Type.Desugar(); + TypeMap typeMap; + if (Context.Context.TypeMaps.FindTypeMap(type, out typeMap) && + typeMap.DoesMarshalling) + { + typeMap.CppMarshalToNative(Context); + return; + } + + if (!type.SkipPointerRefs().IsPointer()) + { + Context.Return.Write("*"); + + if (Context.Parameter.Type.IsReference()) + VarPrefix.Write("&"); + } + + var method = Context.Function as Method; + if (method != null + && method.Conversion == MethodConversionKind.FunctionToInstanceMethod + && Context.ParameterIndex == 0) + { + Context.Return.Write($"(::{@class.QualifiedOriginalName}*)"); + Context.Return.Write(Helpers.InstanceIdentifier); + return; + } + + var paramType = Context.Parameter.Type.Desugar(); + var deref = paramType.SkipPointerRefs().IsPointer() ? "->" : "."; + Context.Return.Write($"(::{@class.QualifiedOriginalName}*)"); + Context.Return.Write($"{Context.Parameter.Name}{deref}{Helpers.InstanceIdentifier}"); + } + + private void MarshalValueClass(Class @class) + { + throw new System.NotImplementedException(); + } + + public override bool VisitFieldDecl(Field field) + { + Context.Parameter = new Parameter + { + Name = Context.ArgName, + QualifiedType = field.QualifiedType + }; + + return field.Type.Visit(this); + } + + public override bool VisitProperty(Property property) + { + Context.Parameter = new Parameter + { + Name = Context.ArgName, + QualifiedType = property.QualifiedType + }; + + return base.VisitProperty(property); + } + + public override bool VisitFunctionDecl(Function function) + { + throw new NotImplementedException(); + } + + public override bool VisitMethodDecl(Method method) + { + throw new NotImplementedException(); + } + + public override bool VisitParameterDecl(Parameter parameter) + { + return parameter.Type.Visit(this); + } + + public override bool VisitTypedefDecl(TypedefDecl typedef) + { + throw new NotImplementedException(); + } + + public override bool VisitEnumDecl(Enumeration @enum) + { + Context.Return.Write("(::{0}){1}", @enum.QualifiedOriginalName, + Context.Parameter.Name); + return true; + } + + public override bool VisitVariableDecl(Variable variable) + { + throw new NotImplementedException(); + } + + public override bool VisitClassTemplateDecl(ClassTemplate template) + { + return template.TemplatedClass.Visit(this); + } + + public override bool VisitFunctionTemplateDecl(FunctionTemplate template) + { + return template.TemplatedFunction.Visit(this); + } + + public override bool VisitMacroDefinition(MacroDefinition macro) + { + throw new NotImplementedException(); + } + + public override bool VisitNamespace(Namespace @namespace) + { + throw new NotImplementedException(); + } + + public override bool VisitEvent(Event @event) + { + throw new NotImplementedException(); + } + + public bool VisitDelegate(Delegate @delegate) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Generator/Generators/C/CppSources.cs b/src/Generator/Generators/C/CppSources.cs new file mode 100644 index 00000000..d9802811 --- /dev/null +++ b/src/Generator/Generators/C/CppSources.cs @@ -0,0 +1,626 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using CppSharp.AST; +using CppSharp.AST.Extensions; +using CppSharp.Generators.C; +using CppSharp.Generators.CLI; + +namespace CppSharp.Generators.Cpp +{ + /// + /// Generates C/C++ source files. + /// + public class CppSources : CCodeGenerator + { + public CppSources(BindingContext context, IEnumerable units) + : base(context, units) + { + } + + public override string FileExtension { get { return "cpp"; } } + + public override void Process() + { + GenerateFilePreamble(CommentKind.BCPL); + + var file = Path.GetFileNameWithoutExtension(TranslationUnit.FileName) + .Replace('\\', '/'); + + if (Context.Options.GenerateName != null) + file = Context.Options.GenerateName(TranslationUnit); + + PushBlock(BlockKind.Includes); + WriteLine("#include \"{0}.h\"", file); + GenerateForwardReferenceHeaders(); + + NewLine(); + PopBlock(); + + VisitDeclContext(TranslationUnit); + + PushBlock(BlockKind.Footer); + PopBlock(); + } + + public void GenerateForwardReferenceHeaders() + { + PushBlock(BlockKind.IncludesForwardReferences); + + var typeReferenceCollector = new CLITypeReferenceCollector(Context.TypeMaps, Context.Options); + typeReferenceCollector.Process(TranslationUnit, filterNamespaces: false); + + var includes = new SortedSet(StringComparer.InvariantCulture); + + foreach (var typeRef in typeReferenceCollector.TypeReferences) + { + if (typeRef.Include.File == TranslationUnit.FileName) + continue; + + var include = typeRef.Include; + if(!string.IsNullOrEmpty(include.File) && !include.InHeader) + includes.Add(include.ToString()); + } + + foreach (var include in includes) + WriteLine(include); + + PopBlock(); + } + + public override bool VisitDeclContext(DeclarationContext context) + { + PushBlock(BlockKind.Namespace); + foreach (var @class in context.Classes) + { + if (!@class.IsGenerated || @class.IsDependent) + continue; + + if (@class.IsOpaque || @class.IsIncomplete) + continue; + + @class.Visit(this); + } + + // Generate all the function declarations for the module. + foreach (var function in context.Functions.Where(f => f.IsGenerated)) + { + function.Visit(this); + } + + if (Options.GenerateFunctionTemplates) + { + foreach (var template in context.Templates) + { + if (!template.IsGenerated) continue; + + var functionTemplate = template as FunctionTemplate; + if (functionTemplate == null) continue; + + if (!functionTemplate.IsGenerated) + continue; + + GenerateFunctionTemplate(functionTemplate); + } + } + + foreach(var childNamespace in context.Namespaces) + VisitDeclContext(childNamespace); + + PopBlock(); + + return true; + } + + public void GenerateClass(Class @class) + { + PushBlock(BlockKind.Class); + + VisitDeclContext(@class); + + GenerateClassConstructors(@class); + GenerateClassMethods(@class); + GenerateClassProperties(@class); + + foreach (var @event in @class.Events) + { + if (!@event.IsGenerated) + continue; + + @event.Visit(this); + } + + foreach (var variable in @class.Variables) + { + if (!variable.IsGenerated) + continue; + + if (variable.Access != AccessSpecifier.Public) + continue; + + variable.Visit(this); + } + + PopBlock(); + } + + public virtual void GenerateClassConstructors(Class @class) + { + if (@class.IsStatic) + return; + + // Output a default constructor that takes the native instance. + GenerateClassConstructor(@class); + + if (@class.IsRefType) + { + var destructor = @class.Destructors + .FirstOrDefault(d => d.Parameters.Count == 0 && d.Access == AccessSpecifier.Public); + + if (destructor != null) + { + GenerateClassDestructor(@class); + + if (Options.GenerateFinalizers) + GenerateClassFinalizer(@class); + } + } + } + + public virtual void GenerateClassMethods(Class @class) + { + foreach (var method in @class.Methods.Where(m => !m.IsOperator)) + { + if (ASTUtils.CheckIgnoreMethod(method) || CppHeaders.FunctionIgnored(method)) + continue; + + // Do not generate property getter/setter methods as they will be generated + // as part of properties generation. + var field = (method?.AssociatedDeclaration as Property)?.Field; + if (field != null) + continue; + + method.Visit(this); + } + } + + public virtual void GenerateClassProperties(Class @class) + { + foreach (var property in @class.Properties) + { + if (ASTUtils.CheckIgnoreProperty(property) || CppHeaders.TypeIgnored(property.Type)) + continue; + + property.Visit(this); + } + } + + public virtual void GenerateClassDestructor(Class @class) + { + PushBlock(BlockKind.Destructor); + + WriteLine($"{QualifiedIdentifier(@class)}::~{@class.Name}()"); + WriteOpenBraceAndIndent(); + UnindentAndWriteCloseBrace(); + + PopBlock(NewLineKind.BeforeNextBlock); + } + + public virtual void GenerateClassFinalizer(Class @class) + { + + } + + public virtual void GenerateFunctionTemplate(FunctionTemplate template) + { + } + + public override bool VisitProperty(Property property) + { + PushBlock(BlockKind.Property, property); + + if (property.HasGetter) + GeneratePropertyGetter(property.GetMethod); + + if (property.HasSetter) + GeneratePropertySetter(property.SetMethod); + + PopBlock(); + + return true; + } + + public override void GeneratePropertyGetter(Method method) + { + PushBlock(BlockKind.Method, method); + + var property = method.AssociatedDeclaration as Property; + GeneratePropertyAccessorSpecifier(method); + NewLine(); + + WriteOpenBraceAndIndent(); + + GenerateFunctionCall(method); + + UnindentAndWriteCloseBrace(); + NewLine(); + + PopBlock(NewLineKind.BeforeNextBlock); + } + + public override void GeneratePropertySetter(Method method) + { + PushBlock(BlockKind.Method, method); + + var property = method.AssociatedDeclaration as Property; + GeneratePropertyAccessorSpecifier(method); + NewLine(); + + WriteOpenBraceAndIndent(); + + GenerateFunctionCall(method); + + UnindentAndWriteCloseBrace(); + NewLine(); + + PopBlock(NewLineKind.BeforeNextBlock); + } + + public override bool VisitEvent(Event @event) + { + GenerateDeclarationCommon(@event); + + return true; + } + + public override bool VisitVariableDecl(Variable variable) + { + GenerateDeclarationCommon(variable); + + return true; + } + + public virtual string ClassCtorInstanceParamIdentifier => "instance"; + + public virtual void GenerateClassConstructor(Class @class) + { + Write($"{QualifiedIdentifier(@class)}::{@class.Name}("); + + var nativeType = $"::{@class.QualifiedOriginalName}*"; + WriteLine($"{nativeType} {ClassCtorInstanceParamIdentifier})"); + + WriteOpenBraceAndIndent(); + + UnindentAndWriteCloseBrace(); + NewLine(); + } + + private bool GenerateClassConstructorBase(Class @class, Method method = null) + { + var hasBase = @class.HasBase && @class.Bases[0].IsClass && @class.Bases[0].Class.IsGenerated; + if (!hasBase) + return false; + + if (!@class.IsValueType) + { + Indent(); + + var baseClass = @class.Bases[0].Class; + Write(": {0}(", QualifiedIdentifier(baseClass)); + + // We cast the value to the base clas type since otherwise there + // could be ambiguous call to overloaded constructors. + var cppTypePrinter = new CppTypePrinter(); + var nativeTypeName = baseClass.Visit(cppTypePrinter); + Write("({0}*)", nativeTypeName); + + WriteLine("{0})", method != null ? "nullptr" : ClassCtorInstanceParamIdentifier); + + Unindent(); + } + + return true; + } + + public override string GetMethodIdentifier(Function function, + TypePrinterContextKind context = TypePrinterContextKind.Managed) + { + var method = function as Method; + if (method != null) + { + var @class = method.Namespace as Class; + return $"{QualifiedIdentifier(@class)}::{base.GetMethodIdentifier(method, context)}"; + } + + return base.GetMethodIdentifier(function); + } + + public override bool VisitMethodDecl(Method method) + { + if (!method.IsGenerated || CppHeaders.FunctionIgnored(method)) + return false; + + PushBlock(BlockKind.Method, method); + + GenerateMethodSpecifier(method, method.Namespace as Class); + NewLine(); + + var @class = method.Namespace as Class; + if (method.IsConstructor) + GenerateClassConstructorBase(@class, method); + + WriteOpenBraceAndIndent(); + + PushBlock(BlockKind.MethodBody, method); + + if (method.IsConstructor && @class.IsRefType) + WriteLine($"{Helpers.OwnsNativeInstanceIdentifier} = true;"); + + if (method.IsProxy) + goto SkipImpl; + + if (@class.IsRefType) + { + if (method.IsConstructor && !@class.IsAbstract) + { + var @params = GenerateFunctionParamsMarshal(method.Parameters, method); + Write($"{Helpers.InstanceIdentifier} = new ::{method.Namespace.QualifiedOriginalName}("); + GenerateFunctionParams(@params); + WriteLine(");"); + } + else + { + GenerateFunctionCall(method); + } + } + + SkipImpl: + + PopBlock(); + + UnindentAndWriteCloseBrace(); + + PopBlock(NewLineKind.Always); + + return true; + } + + + public override bool VisitFunctionDecl(Function function) + { + if (!function.IsGenerated || CppHeaders.FunctionIgnored(function)) + return false; + + PushBlock(BlockKind.Function, function); + + GenerateDeclarationCommon(function); + + var returnType = function.ReturnType.Visit(CTypePrinter); + + var name = function.Visit(CTypePrinter); + Write($"{returnType} {name}("); + + for (var i = 0; i < function.Parameters.Count; ++i) + { + var param = function.Parameters[i]; + Write($"{CTypePrinter.VisitParameter(param)}"); + if (i < function.Parameters.Count - 1) + Write(", "); + } + + WriteLine(")"); + WriteOpenBraceAndIndent(); + + GenerateFunctionCall(function); + + UnindentAndWriteCloseBrace(); + + PopBlock(NewLineKind.BeforeNextBlock); + + return true; + } + + public void GenerateFunctionCall(Function function) + { + var @params = GenerateFunctionParamsMarshal(function.Parameters, function); + + var needsReturn = !function.ReturnType.Type.IsPrimitiveType(PrimitiveType.Void); + if (needsReturn) + { + CTypePrinter.PushContext(TypePrinterContextKind.Native); + var returnType = function.ReturnType.Visit(CTypePrinter); + CTypePrinter.PopContext(); + + Write($"{returnType} {Helpers.ReturnIdentifier} = "); + } + + var method = function as Method; + var field = (method?.AssociatedDeclaration as Property)?.Field; + var @class = function.Namespace as Class; + + if (field != null) + { + Write($"((::{@class.QualifiedOriginalName}*){Helpers.InstanceIdentifier})->"); + WriteLine ($"{field.OriginalName};"); + } + else + { + if (IsNativeFunctionOrStaticMethod(function)) + { + Write($"::{function.QualifiedOriginalName}("); + } + else + { + if (IsNativeMethod(function)) + Write($"((::{@class.QualifiedOriginalName}*){Helpers.InstanceIdentifier})->"); + + Write($"{base.GetMethodIdentifier(function, TypePrinterContextKind.Native)}("); + } + + GenerateFunctionParams(@params); + WriteLine(");"); + } + + foreach(var paramInfo in @params) + { + var param = paramInfo.Param; + if(param.Usage != ParameterUsage.Out && param.Usage != ParameterUsage.InOut) + continue; + + if (param.Type.IsPointer() && !param.Type.GetFinalPointee().IsPrimitiveType()) + param.QualifiedType = new QualifiedType(param.Type.GetFinalPointee()); + + var nativeVarName = paramInfo.Name; + + var ctx = new MarshalContext(Context, CurrentIndentation) + { + ArgName = nativeVarName, + ReturnVarName = nativeVarName, + ReturnType = param.QualifiedType + }; + + var marshal = new CppMarshalNativeToManagedPrinter(ctx); + param.Visit(marshal); + + if (!string.IsNullOrWhiteSpace(marshal.Context.Before)) + Write(marshal.Context.Before); + + WriteLine($"{param.Name} = {marshal.Context.Return};"); + } + + if (needsReturn) + { + var retTypeName = function.ReturnType.Visit(CTypePrinter).ToString(); + + var ctx = new MarshalContext(Context, CurrentIndentation) + { + ArgName = Helpers.ReturnIdentifier, + ReturnVarName = Helpers.ReturnIdentifier, + ReturnType = function.ReturnType + }; + + var marshal = new CppMarshalNativeToManagedPrinter(ctx); + function.ReturnType.Visit(marshal); + + if (!string.IsNullOrWhiteSpace(marshal.Context.Before)) + Write(marshal.Context.Before); + + WriteLine($"return {marshal.Context.Return};"); + } + } + + public static bool IsNativeMethod(Function function) + { + var method = function as Method; + if (method == null) + return false; + + return method.Conversion == MethodConversionKind.None; + } + + public bool IsNativeFunctionOrStaticMethod(Function function) + { + var method = function as Method; + if (method == null) + return true; + + if (!IsCLIGenerator && method.IsOperator) + return false; + + if (method.IsOperator && Operators.IsBuiltinOperator(method.OperatorKind)) + return true; + + return method.IsStatic || method.Conversion != MethodConversionKind.None; + } + + public struct ParamMarshal + { + public string Name; + public string Prefix; + + public Parameter Param; + } + + public List GenerateFunctionParamsMarshal(IEnumerable @params, + Function function = null) + { + var marshals = new List(); + + var paramIndex = 0; + foreach (var param in @params) + { + marshals.Add(GenerateFunctionParamMarshal(param, paramIndex, function)); + paramIndex++; + } + + return marshals; + } + + private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex, + Function function = null) + { + var paramMarshal = new ParamMarshal { Name = param.Name, Param = param }; + + if (param.Type is BuiltinType) + return paramMarshal; + + var argName = Generator.GeneratedIdentifier("arg") + paramIndex.ToString(CultureInfo.InvariantCulture); + + Parameter effectiveParam = param; + var isRef = param.IsOut || param.IsInOut; + var paramType = param.Type; + + var ctx = new MarshalContext(Context, CurrentIndentation) + { + Parameter = effectiveParam, + ParameterIndex = paramIndex, + ArgName = argName, + Function = function + }; + + var marshal = new CppMarshalManagedToNativePrinter(ctx); + effectiveParam.Visit(marshal); + + if (string.IsNullOrEmpty(marshal.Context.Return)) + throw new Exception($"Cannot marshal argument of function '{function.QualifiedOriginalName}'"); + + if (isRef) + { + var typePrinter = new CppTypePrinter(); + var type = paramType.Visit(typePrinter); + + if (param.IsInOut) + { + if (!string.IsNullOrWhiteSpace(marshal.Context.Before)) + Write(marshal.Context.Before); + + WriteLine($"{type} {argName} = {marshal.Context.Return};"); + } + else + WriteLine($"{type} {argName};"); + } + else + { + if (!string.IsNullOrWhiteSpace(marshal.Context.Before)) + Write(marshal.Context.Before); + + WriteLine($"auto {marshal.VarPrefix}{argName} = {marshal.Context.Return};"); + paramMarshal.Prefix = marshal.ArgumentPrefix; + } + + paramMarshal.Name = argName; + return paramMarshal; + } + + public void GenerateFunctionParams(List @params) + { + var names = @params.Select(param => + string.IsNullOrWhiteSpace(param.Prefix) ? param.Name : (param.Prefix + param.Name)) + .ToList(); + + Write(string.Join(", ", names)); + } + } +} diff --git a/src/Generator/Options.cs b/src/Generator/Options.cs index 40ca7b2a..17d45640 100644 --- a/src/Generator/Options.cs +++ b/src/Generator/Options.cs @@ -127,6 +127,8 @@ namespace CppSharp public bool IsCSharpGenerator => GeneratorKind == GeneratorKind.CSharp; + public bool IsCppGenerator => GeneratorKind == GeneratorKind.CPlusPlus; + public bool IsCLIGenerator => GeneratorKind == GeneratorKind.CLI; public readonly List DependentNameSpaces = new List(); diff --git a/src/Generator/Types/TypeMap.cs b/src/Generator/Types/TypeMap.cs index 681ca150..0b91f05f 100644 --- a/src/Generator/Types/TypeMap.cs +++ b/src/Generator/Types/TypeMap.cs @@ -3,6 +3,7 @@ using CppSharp.AST; using CppSharp.Generators; using CppSharp.Generators.AST; using CppSharp.Generators.CLI; +using CppSharp.Generators.Cpp; using CppSharp.Generators.CSharp; using Attribute = System.Attribute; using Type = CppSharp.AST.Type; @@ -99,6 +100,30 @@ namespace CppSharp.Types } #endregion + + #region C++ backend + + public virtual Type CppSignatureType(TypePrinterContext ctx) + { + return new CILType(typeof(object)); + } + + public virtual void CppTypeReference(CLITypeReference collector, ASTRecord record) + { + throw new NotImplementedException(); + } + + public virtual void CppMarshalToNative(MarshalContext ctx) + { + ctx.Return.Write(ctx.Parameter.Name); + } + + public virtual void CppMarshalToManaged(MarshalContext ctx) + { + ctx.Return.Write(ctx.ReturnVarName); + } + + #endregion } public interface ITypeMapDatabase