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.
633 lines
20 KiB
633 lines
20 KiB
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 |
|
{ |
|
/// <summary> |
|
/// Generates C/C++ source files. |
|
/// </summary> |
|
public class CppSources : CCodeGenerator |
|
{ |
|
public CppSources(BindingContext context, IEnumerable<TranslationUnit> 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(); |
|
|
|
VisitNamespace(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<string>(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 override bool VisitClassDecl(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(); |
|
|
|
return true; |
|
} |
|
|
|
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})"); |
|
GenerateClassConstructorBase(@class); |
|
|
|
WriteOpenBraceAndIndent(); |
|
|
|
WriteLine($"{Helpers.InstanceIdentifier} = {ClassCtorInstanceParamIdentifier};"); |
|
|
|
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($": {QualifiedIdentifier(baseClass)}("); |
|
|
|
// We cast the value to the base class type since otherwise there |
|
// could be ambiguous call to overloaded constructors. |
|
CTypePrinter.PushContext(TypePrinterContextKind.Native); |
|
var nativeTypeName = baseClass.Visit(CTypePrinter); |
|
CTypePrinter.PopContext(); |
|
Write($"({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) |
|
{ |
|
if (!@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 @class = function.Namespace as Class; |
|
|
|
var field = (method?.AssociatedDeclaration as Property)?.Field; |
|
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<ParamMarshal> GenerateFunctionParamsMarshal(IEnumerable<Parameter> @params, |
|
Function function = null) |
|
{ |
|
var marshals = new List<ParamMarshal>(); |
|
|
|
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 type = paramType.Visit(CTypePrinter); |
|
|
|
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<ParamMarshal> @params) |
|
{ |
|
var names = @params.Select(param => |
|
string.IsNullOrWhiteSpace(param.Prefix) ? param.Name : (param.Prefix + param.Name)) |
|
.ToList(); |
|
|
|
Write(string.Join(", ", names)); |
|
} |
|
} |
|
}
|
|
|