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.
3149 lines
125 KiB
3149 lines
125 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Globalization; |
|
using System.IO; |
|
using System.Linq; |
|
using System.Text; |
|
using System.Text.RegularExpressions; |
|
using System.Web.Util; |
|
using CppSharp.AST; |
|
using CppSharp.AST.Extensions; |
|
using CppSharp.Passes; |
|
using CppSharp.Types; |
|
using CppSharp.Utils; |
|
using Microsoft.CodeAnalysis.CSharp.Syntax; |
|
using Attribute = CppSharp.AST.Attribute; |
|
using Type = CppSharp.AST.Type; |
|
using Microsoft.CodeAnalysis; |
|
using Microsoft.CodeAnalysis.Formatting; |
|
using Microsoft.CodeAnalysis.CSharp; |
|
|
|
namespace CppSharp.Generators.CSharp |
|
{ |
|
public static class Helpers |
|
{ |
|
// from https://github.com/mono/mono/blob/master/mcs/class/System/Microsoft.CSharp/CSharpCodeGenerator.cs |
|
private static readonly string[] Keywords = |
|
{ |
|
"abstract", "event", "new", "struct", "as", "explicit", "null", "switch", |
|
"base", "extern", "this", "false", "operator", "throw", "break", "finally", |
|
"out", "true", "fixed", "override", "try", "case", "params", "typeof", |
|
"catch", "for", "private", "foreach", "protected", "checked", "goto", |
|
"public", "unchecked", "class", "if", "readonly", "unsafe", "const", |
|
"implicit", "ref", "continue", "in", "return", "using", "virtual", "default", |
|
"interface", "sealed", "volatile", "delegate", "internal", "do", "is", |
|
"sizeof", "while", "lock", "stackalloc", "else", "static", "enum", |
|
"namespace", "object", "bool", "byte", "float", "uint", "char", "ulong", |
|
"ushort", "decimal", "int", "sbyte", "short", "double", "long", "string", |
|
"void", "partial", "yield", "where" |
|
}; |
|
|
|
public static string SafeIdentifier(string id) |
|
{ |
|
if (id.All(char.IsLetterOrDigit)) |
|
return Keywords.Contains(id) ? "@" + id : id; |
|
return new string(id.Select(c => char.IsLetterOrDigit(c) ? c : '_').ToArray()); |
|
} |
|
|
|
public static readonly string InstanceField = Generator.GeneratedIdentifier("instance"); |
|
public static readonly string InstanceIdentifier = Generator.GeneratedIdentifier("Instance"); |
|
public static readonly string PointerAdjustmentIdentifier = Generator.GeneratedIdentifier("PointerAdjustment"); |
|
public static readonly string ReturnIdentifier = Generator.GeneratedIdentifier("ret"); |
|
public static readonly string DummyIdentifier = Generator.GeneratedIdentifier("dummy"); |
|
public static readonly string TargetIdentifier = Generator.GeneratedIdentifier("target"); |
|
public static readonly string SlotIdentifier = Generator.GeneratedIdentifier("slot"); |
|
|
|
public static readonly string OwnsNativeInstanceIdentifier = Generator.GeneratedIdentifier("ownsNativeInstance"); |
|
|
|
public static readonly string CreateInstanceIdentifier = Generator.GeneratedIdentifier("CreateInstance"); |
|
|
|
public static SyntaxKind GetAccess(AccessSpecifier accessSpecifier) |
|
{ |
|
switch (accessSpecifier) |
|
{ |
|
case AccessSpecifier.Private: |
|
case AccessSpecifier.Internal: |
|
return SyntaxKind.InternalKeyword; |
|
case AccessSpecifier.Protected: |
|
return SyntaxKind.ProtectedKeyword; |
|
default: |
|
return SyntaxKind.PublicKeyword; |
|
} |
|
} |
|
} |
|
|
|
public class CSharpBlockKind |
|
{ |
|
private const int FIRST = BlockKind.LAST + 1000; |
|
public const int Usings = FIRST + 1; |
|
public const int Namespace = FIRST + 2; |
|
public const int Enum = FIRST + 3; |
|
public const int Typedef = FIRST + 4; |
|
public const int Class = FIRST + 5; |
|
public const int InternalsClass = FIRST + 6; |
|
public const int InternalsClassMethod = FIRST + 7; |
|
public const int InternalsClassField = FIRST + 15; |
|
public const int Functions = FIRST + 8; |
|
public const int Function = FIRST + 9; |
|
public const int Method = FIRST + 10; |
|
public const int Event = FIRST + 11; |
|
public const int Variable = FIRST + 12; |
|
public const int Property = FIRST + 13; |
|
public const int Field = FIRST + 14; |
|
public const int VTableDelegate = FIRST + 16; |
|
public const int Region = FIRST + 17; |
|
public const int Interface = FIRST + 18; |
|
public const int Finalizer = FIRST + 19; |
|
} |
|
|
|
public class CSharpTextTemplate : Template |
|
{ |
|
public CSharpTypePrinter TypePrinter { get; private set; } |
|
public CSharpExpressionPrinter ExpressionPrinter { get; private set; } |
|
|
|
public override string FileExtension |
|
{ |
|
get { return "cs"; } |
|
} |
|
|
|
public CSharpTextTemplate(Driver driver, IEnumerable<TranslationUnit> units, |
|
CSharpTypePrinter typePrinter, CSharpExpressionPrinter expressionPrinter) |
|
: base(driver, units) |
|
{ |
|
TypePrinter = typePrinter; |
|
ExpressionPrinter = expressionPrinter; |
|
} |
|
|
|
#region Identifiers |
|
|
|
public static string GeneratedIdentifier(string id) |
|
{ |
|
return Generator.GeneratedIdentifier(id); |
|
} |
|
|
|
#endregion |
|
|
|
public override void Process() |
|
{ |
|
compilationUnit = SyntaxFactory.CompilationUnit(); |
|
|
|
compilationUnit = compilationUnit.AddUsings( |
|
SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("System")), |
|
SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("System.Runtime.InteropServices")), |
|
SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName("System.Security"))); |
|
|
|
foreach (var customUsingStatement in Options.DependentNameSpaces) |
|
{ |
|
compilationUnit = compilationUnit.AddUsings( |
|
SyntaxFactory.UsingDirective(SyntaxFactory.IdentifierName(customUsingStatement))); |
|
} |
|
|
|
var members = TranslationUnits.SelectMany(unit => GenerateDeclContext(unit)).ToArray(); |
|
|
|
if (Options.GenerateLibraryNamespace) |
|
{ |
|
var root = SyntaxFactory.NamespaceDeclaration( |
|
SyntaxFactory.IdentifierName(Driver.Options.OutputNamespace)); |
|
root = AddMembers(root, members); |
|
compilationUnit = AddMembers(compilationUnit, new [] { root }); |
|
} |
|
else |
|
{ |
|
compilationUnit = AddMembers(compilationUnit, members); |
|
} |
|
|
|
GenerateHeader(); |
|
} |
|
|
|
public override void Generate(TextWriter textWriter) |
|
{ |
|
using (var workspace = new AdhocWorkspace()) |
|
{ |
|
var formattedNode = Formatter.Format(compilationUnit, workspace); |
|
formattedNode.WriteTo(textWriter); |
|
} |
|
} |
|
|
|
private void GenerateHeader() |
|
{ |
|
compilationUnit = compilationUnit.WithLeadingTrivia( |
|
SyntaxFactory.Comment("//----------------------------------------------------------------------------"), |
|
SyntaxFactory.CarriageReturnLineFeed, |
|
SyntaxFactory.Comment("// This is autogenerated code by CppSharp."), |
|
SyntaxFactory.CarriageReturnLineFeed, |
|
SyntaxFactory.Comment("// Do not edit this file or all your changes will be lost after re-generation."), |
|
SyntaxFactory.CarriageReturnLineFeed, |
|
SyntaxFactory.Comment("//----------------------------------------------------------------------------"), |
|
SyntaxFactory.CarriageReturnLineFeed); |
|
} |
|
|
|
private MemberDeclarationSyntax[] GenerateDeclContext(DeclarationContext context) |
|
{ |
|
var isNamespace = context is Namespace; |
|
var isTranslationUnit = context is TranslationUnit; |
|
|
|
var members = new List<MemberDeclarationSyntax>(); |
|
|
|
members.AddRange( |
|
from @enum in context.Enums |
|
where @enum.IsGenerated && !@enum.IsIncomplete |
|
select GenerateEnum(@enum)); |
|
|
|
members.AddRange(context.Typedefs.Select(t => GenerateTypedef(t)).Where(t => t != null)); |
|
|
|
foreach (var @class in context.Classes.Where(c => !c.IsIncomplete)) |
|
{ |
|
if (@class.IsInterface) |
|
members.Add(GenerateInterface(@class)); |
|
else |
|
members.Add(GenerateClass(@class)); |
|
} |
|
|
|
//if (context.HasFunctions) |
|
//{ |
|
// PushBlock(CSharpBlockKind.Functions); |
|
// WriteLine("public unsafe partial class {0}", |
|
// context.TranslationUnit.FileNameWithoutExtension); |
|
// WriteStartBraceIndent(); |
|
|
|
// PushBlock(CSharpBlockKind.InternalsClass); |
|
// GenerateClassInternalHead(); |
|
// WriteStartBraceIndent(); |
|
|
|
// // Generate all the internal function declarations. |
|
// foreach (var function in context.Functions) |
|
// { |
|
// if ((!function.IsGenerated && !function.IsInternal) || function.IsSynthetized) continue; |
|
|
|
// GenerateInternalFunction(function); |
|
// } |
|
|
|
// WriteCloseBraceIndent(); |
|
// PopBlock(NewLineKind.BeforeNextBlock); |
|
|
|
// foreach (var function in context.Functions) |
|
// { |
|
// if (!function.IsGenerated) continue; |
|
|
|
// GenerateFunction(function); |
|
// } |
|
|
|
// WriteCloseBraceIndent(); |
|
// PopBlock(NewLineKind.BeforeNextBlock); |
|
//} |
|
|
|
//foreach (var @event in context.Events) |
|
//{ |
|
// if (!@event.IsGenerated) continue; |
|
|
|
// GenerateEvent(@event); |
|
//} |
|
|
|
members.AddRange(context.Namespaces.SelectMany(n => GenerateDeclContext(n))); |
|
|
|
if (isNamespace && !isTranslationUnit) |
|
{ |
|
var @namespace = SyntaxFactory.NamespaceDeclaration( |
|
SyntaxFactory.IdentifierName(context.Name)); |
|
return new [] { AddMembers(@namespace, members.ToArray()) }; |
|
} |
|
|
|
return members.ToArray(); |
|
} |
|
|
|
public void GenerateDeclarationCommon(Declaration decl) |
|
{ |
|
if (decl.Comment != null) |
|
{ |
|
GenerateComment(decl.Comment); |
|
GenerateDebug(decl); |
|
} |
|
foreach (Attribute attribute in decl.Attributes) |
|
WriteLine("[{0}({1})]", attribute.Type.FullName, attribute.Value); |
|
} |
|
|
|
public void GenerateDebug(Declaration decl) |
|
{ |
|
if (Options.OutputDebug && !string.IsNullOrWhiteSpace(decl.DebugText)) |
|
WriteLine("// DEBUG: " + decl.DebugText); |
|
} |
|
|
|
public void GenerateComment(RawComment comment) |
|
{ |
|
if (comment.FullComment != null) |
|
{ |
|
PushBlock(BlockKind.BlockComment); |
|
WriteLine(comment.FullComment.CommentToString()); |
|
PopBlock(); |
|
} |
|
else |
|
{ |
|
if (string.IsNullOrWhiteSpace(comment.BriefText)) |
|
return; |
|
|
|
PushBlock(BlockKind.BlockComment); |
|
WriteLine("<summary>"); |
|
foreach (string line in HtmlEncoder.HtmlEncode(comment.BriefText).Split( |
|
Environment.NewLine.ToCharArray())) |
|
WriteLine("<para>{0}</para>", line); |
|
WriteLine("</summary>"); |
|
PopBlock(); |
|
} |
|
} |
|
|
|
public void GenerateInlineSummary(RawComment comment) |
|
{ |
|
if (comment == null) return; |
|
|
|
if (string.IsNullOrWhiteSpace(comment.BriefText)) |
|
return; |
|
|
|
PushBlock(BlockKind.InlineComment); |
|
if (comment.BriefText.Contains("\n")) |
|
{ |
|
WriteLine("{0} <summary>", Options.CommentPrefix); |
|
foreach (string line in HtmlEncoder.HtmlEncode(comment.BriefText).Split( |
|
Environment.NewLine.ToCharArray())) |
|
WriteLine("{0} <para>{1}</para>", Options.CommentPrefix, line); |
|
WriteLine("{0} </summary>", Options.CommentPrefix); |
|
} |
|
else |
|
{ |
|
WriteLine("{0} <summary>{1}</summary>", Options.CommentPrefix, comment.BriefText); |
|
} |
|
PopBlock(); |
|
} |
|
|
|
#region Classes |
|
|
|
public MemberDeclarationSyntax GenerateClass(Class @class) |
|
{ |
|
//GenerateDeclarationCommon(@class); |
|
|
|
MemberDeclarationSyntax type = GenerateClassProlog(@class); |
|
if (@class.IsOpaque) |
|
return type; |
|
|
|
System.Type typeMap = null; |
|
if (Driver.TypeDatabase.TypeMaps.ContainsKey(@class.Name)) |
|
{ |
|
typeMap = Driver.TypeDatabase.TypeMaps[@class.Name]; |
|
// disable the type map for the mapped class itself so that operator params are not mapped |
|
Driver.TypeDatabase.TypeMaps.Remove(@class.Name); |
|
} |
|
|
|
if (!@class.IsAbstractImpl) |
|
type = AddMembers(type, GenerateClassInternals(@class)); |
|
type = AddMembers(type, GenerateDeclContext(@class)); |
|
|
|
if (@class.IsDependent || !@class.IsGenerated) |
|
goto exit; |
|
|
|
if (ShouldGenerateClassNativeField(@class)) |
|
{ |
|
if (@class.IsValueType) |
|
{ |
|
type = AddMembers(type, GenerateStructNativeField(@class)); |
|
} |
|
else |
|
{ |
|
type = AddMembers(type, GenerateClassNativeField(@class)); |
|
} |
|
} |
|
|
|
//if (Options.GenerateClassMarshals) |
|
//{ |
|
// GenerateClassMarshals(@class); |
|
//} |
|
|
|
if (!@class.IsStatic) |
|
type = AddMembers(type, GenerateClassConstructors(@class)); |
|
|
|
//GenerateClassMethods(@class.Methods); |
|
//GenerateClassVariables(@class); |
|
//GenerateClassProperties(@class); |
|
|
|
//if (@class.IsDynamic) |
|
// GenerateVTable(@class); |
|
exit: |
|
|
|
if (typeMap != null) |
|
Driver.TypeDatabase.TypeMaps.Add(@class.Name, typeMap); |
|
|
|
return type; |
|
} |
|
|
|
private MemberDeclarationSyntax[] GenerateStructNativeField(Class @class) |
|
{ |
|
var members = new List<MemberDeclarationSyntax>(); |
|
|
|
var internalType = string.Format("{0}.Internal", @class.Name); |
|
|
|
members.Add(SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration( |
|
SyntaxFactory.ParseTypeName(internalType)) |
|
.AddVariables(SyntaxFactory.VariableDeclarator(Helpers.InstanceField))) |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword))); |
|
|
|
members.Add(SyntaxFactory.PropertyDeclaration( |
|
SyntaxFactory.ParseTypeName(internalType), Helpers.InstanceIdentifier) |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) |
|
.AddAccessorListAccessors(SyntaxFactory.AccessorDeclaration( |
|
SyntaxKind.GetAccessorDeclaration, |
|
SyntaxFactory.Block(SyntaxFactory.ReturnStatement( |
|
SyntaxFactory.IdentifierName(Helpers.InstanceField)))))); |
|
|
|
return members.ToArray(); |
|
} |
|
|
|
private MemberDeclarationSyntax[] GenerateClassNativeField(Class @class) |
|
{ |
|
var members = new List<MemberDeclarationSyntax>(); |
|
|
|
members.Add(SyntaxFactory.PropertyDeclaration( |
|
SyntaxFactory.ParseTypeName("global::System.IntPtr"), Helpers.InstanceIdentifier) |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) |
|
.AddAccessorListAccessors(SyntaxFactory.AccessorDeclaration( |
|
SyntaxKind.GetAccessorDeclaration).WithSemicolonToken( |
|
SyntaxFactory.Token(SyntaxKind.SemicolonToken)), |
|
SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration) |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.ProtectedKeyword)) |
|
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)))); |
|
|
|
members.Add(SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration( |
|
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.IntKeyword))) |
|
.AddVariables(SyntaxFactory.VariableDeclarator( |
|
Helpers.PointerAdjustmentIdentifier))) |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.ProtectedKeyword))); |
|
|
|
// use interfaces if any - derived types with a secondary base this class must be compatible with the map |
|
var name = (@class.Namespace.Classes.Find(c => c.OriginalClass == @class) ?? @class).Name; |
|
var dictionaryType = string.Format( |
|
"System.Collections.Concurrent.ConcurrentDictionary<IntPtr, {0}>", name); |
|
members.Add(SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration( |
|
SyntaxFactory.ParseTypeName(dictionaryType)) |
|
.AddVariables(SyntaxFactory.VariableDeclarator("NativeToManagedMap") |
|
.WithInitializer( |
|
SyntaxFactory.EqualsValueClause(SyntaxFactory.ObjectCreationExpression( |
|
SyntaxFactory.ParseTypeName(dictionaryType), |
|
SyntaxFactory.ArgumentList(), null))))) |
|
.AddModifiers( |
|
SyntaxFactory.Token(SyntaxKind.PublicKeyword), |
|
SyntaxFactory.Token(SyntaxKind.StaticKeyword), |
|
SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword))); |
|
|
|
members.Add(SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration( |
|
SyntaxFactory.ArrayType( |
|
SyntaxFactory.PointerType( |
|
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword))) |
|
.WithAsteriskToken(SyntaxFactory.Token(SyntaxKind.AsteriskToken))) |
|
.AddRankSpecifiers(SyntaxFactory.ArrayRankSpecifier())) |
|
.AddVariables(SyntaxFactory.VariableDeclarator( |
|
SyntaxFactory.Identifier("__OriginalVTables")))) |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.ProtectedKeyword))); |
|
|
|
return members.ToArray(); |
|
} |
|
|
|
private void GenerateClassMarshals(Class @class) |
|
{ |
|
WriteLine("int CppSharp.Runtime.ICppMarshal.NativeDataSize"); |
|
WriteStartBraceIndent(); |
|
WriteLine("get {{ return {0}; }}", @class.Layout.DataSize); |
|
WriteCloseBraceIndent(); |
|
NewLine(); |
|
|
|
WriteLine("void CppSharp.Runtime.ICppMarshal.MarshalManagedToNative(global::System.IntPtr instance)"); |
|
WriteStartBraceIndent(); |
|
WriteCloseBraceIndent(); |
|
NewLine(); |
|
|
|
WriteLine("void CppSharp.Runtime.ICppMarshal.MarshalNativeToManaged(global::System.IntPtr instance)"); |
|
WriteStartBraceIndent(); |
|
WriteCloseBraceIndent(); |
|
NewLine(); |
|
} |
|
|
|
private TypeDeclarationSyntax GenerateInterface(Class @class) |
|
{ |
|
//GenerateDeclarationCommon(@class); |
|
|
|
return GenerateClassProlog(@class); |
|
|
|
//foreach (var method in @class.Methods.Where(m => |
|
// !ASTUtils.CheckIgnoreMethod(m, Options) && m.Access == AccessSpecifier.Public)) |
|
//{ |
|
// PushBlock(CSharpBlockKind.Method); |
|
// GenerateDeclarationCommon(method); |
|
|
|
// var functionName = GetMethodIdentifier(method); |
|
|
|
// Write("{0} {1}(", method.OriginalReturnType, functionName); |
|
|
|
// Write(FormatMethodParameters(method.Parameters)); |
|
|
|
// WriteLine(");"); |
|
|
|
// PopBlock(NewLineKind.BeforeNextBlock); |
|
//} |
|
//foreach (var prop in @class.Properties.Where(p => p.IsGenerated && p.Access == AccessSpecifier.Public)) |
|
//{ |
|
// PushBlock(CSharpBlockKind.Property); |
|
// var type = prop.Type; |
|
// if (prop.Parameters.Count > 0 && prop.Type.IsPointerToPrimitiveType()) |
|
// type = ((PointerType) prop.Type).Pointee; |
|
// GenerateDeclarationCommon(prop); |
|
// Write("{0} {1} {{ ", type, GetPropertyName(prop)); |
|
// if (prop.HasGetter) |
|
// Write("get; "); |
|
// if (prop.HasSetter) |
|
// Write("set; "); |
|
|
|
// WriteLine("}"); |
|
// PopBlock(NewLineKind.BeforeNextBlock); |
|
//} |
|
} |
|
|
|
public MemberDeclarationSyntax GenerateClassInternals(Class @class) |
|
{ |
|
var @struct = SyntaxFactory.StructDeclaration("Internal").AddModifiers( |
|
SyntaxFactory.Token(SyntaxKind.PublicKeyword)) |
|
.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes( |
|
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("StructLayout"), |
|
SyntaxFactory.AttributeArgumentList().AddArguments( |
|
SyntaxFactory.AttributeArgument( |
|
SyntaxFactory.IdentifierName("LayoutKind.Explicit")), |
|
SyntaxFactory.AttributeArgument( |
|
SyntaxFactory.IdentifierName("Size = " + @class.Layout.Size)))))); |
|
|
|
if (@class != null && @class.NeedsBase && !@class.BaseClass.IsInterface) |
|
@struct = @struct.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword)); |
|
|
|
@struct = @struct.AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword)); |
|
|
|
TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
|
|
@struct = AddMembers(@struct, |
|
GenerateClassFields(@class, @class, GenerateClassInternalsField, true)); |
|
if (@class.IsGenerated) |
|
{ |
|
if (@class.IsDynamic) |
|
@struct = AddMembers(@struct, GenerateVTablePointers(@class)); |
|
|
|
var functions = GatherClassInternalFunctions(@class); |
|
|
|
@struct = AddMembers(@struct, |
|
functions.Where(f => !f.IsPure).Select(f => GenerateInternalFunction(f)).ToArray()); |
|
} |
|
|
|
TypePrinter.PopContext(); |
|
|
|
return @struct; |
|
} |
|
|
|
private IEnumerable<Function> GatherClassInternalFunctions(Class @class, |
|
bool includeCtors = true) |
|
{ |
|
var functions = new List<Function>(); |
|
if (@class.IsValueType) |
|
foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore)) |
|
functions.AddRange(GatherClassInternalFunctions(@base.Class, false)); |
|
|
|
Action<Method> tryAddOverload = method => |
|
{ |
|
if (method.IsSynthetized && |
|
method.SynthKind != FunctionSynthKind.AdjustedMethod) |
|
return; |
|
|
|
if (method.IsProxy || |
|
(method.IsVirtual && !method.IsOperator && |
|
// virtual destructors in abstract classes may lack a pointer in the v-table |
|
// so they have to be called by symbol and therefore not ignored |
|
!(method.IsDestructor && @class.IsAbstract))) |
|
return; |
|
|
|
functions.Add(method); |
|
}; |
|
|
|
if (includeCtors) |
|
{ |
|
foreach (var ctor in @class.Constructors) |
|
{ |
|
if (@class.IsStatic || ctor.IsMoveConstructor) |
|
continue; |
|
|
|
if (!ctor.IsGenerated) |
|
continue; |
|
|
|
if (ctor.IsDefaultConstructor && !@class.HasNonTrivialDefaultConstructor) |
|
continue; |
|
|
|
tryAddOverload(ctor); |
|
} |
|
} |
|
|
|
if (@class.HasNonTrivialDestructor && !@class.IsStatic) |
|
foreach (var dtor in @class.Destructors) |
|
tryAddOverload(dtor); |
|
|
|
foreach (var method in @class.Methods) |
|
{ |
|
if (ASTUtils.CheckIgnoreMethod(method, Options)) |
|
continue; |
|
|
|
if (method.IsConstructor) |
|
continue; |
|
|
|
tryAddOverload(method); |
|
} |
|
|
|
foreach (var prop in @class.Properties) |
|
{ |
|
if (prop.GetMethod != null) |
|
tryAddOverload(prop.GetMethod); |
|
|
|
if (prop.SetMethod != null && prop.SetMethod != prop.GetMethod) |
|
tryAddOverload(prop.SetMethod); |
|
} |
|
|
|
return functions; |
|
} |
|
|
|
private IEnumerable<string> GatherInternalParams(Function function, out CSharpTypePrinterResult retType) |
|
{ |
|
TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
|
|
var retParam = new Parameter { QualifiedType = function.ReturnType }; |
|
retType = retParam.CSharpType(TypePrinter); |
|
|
|
var @params = function.GatherInternalParams(Driver.Options.IsItaniumLikeAbi).Select(p => |
|
string.Format("{0} {1}", p.CSharpType(TypePrinter), p.Name)).ToList(); |
|
|
|
TypePrinter.PopContext(); |
|
|
|
return @params; |
|
} |
|
|
|
public static bool ShouldGenerateClassNativeField(Class @class) |
|
{ |
|
if (@class.IsStatic) |
|
return false; |
|
return @class.IsValueType || !@class.HasBase || !@class.HasRefBase(); |
|
} |
|
|
|
public TypeDeclarationSyntax GenerateClassProlog(Class @class) |
|
{ |
|
TypeDeclarationSyntax type; |
|
if (@class.IsInterface) |
|
type = SyntaxFactory.InterfaceDeclaration(@class.Name); |
|
else if (@class.IsValueType) |
|
type = SyntaxFactory.StructDeclaration(@class.Name); |
|
else |
|
type = SyntaxFactory.ClassDeclaration(@class.Name); |
|
|
|
type = AddModifier(type, @class.IsInternal ? |
|
SyntaxKind.InternalKeyword : Helpers.GetAccess(@class.Access)); |
|
if (@class.Access == AccessSpecifier.Protected) |
|
type = AddModifier(type, SyntaxKind.InternalKeyword); |
|
type = AddModifier(type, SyntaxKind.UnsafeKeyword); |
|
|
|
if (@class.IsAbstract) |
|
type = AddModifier(type, SyntaxKind.AbstractKeyword); |
|
|
|
if (@class.IsStatic) |
|
type = AddModifier(type, SyntaxKind.StaticKeyword); |
|
|
|
// This token needs to directly precede the "class" token. |
|
if (Options.GeneratePartialClasses) |
|
type = AddModifier(type, SyntaxKind.PartialKeyword); |
|
|
|
var bases = new List<string>(); |
|
|
|
if (@class.NeedsBase) |
|
{ |
|
bases.AddRange( |
|
from @base in @class.Bases |
|
where @base.IsClass |
|
select @base.Class.Visit(TypePrinter).Type); |
|
} |
|
|
|
if (!@class.IsStatic) |
|
foreach (var @base in bases) |
|
type = AddBase(type, @base); |
|
|
|
if (@class.IsGenerated) |
|
{ |
|
if (@class.IsRefType) |
|
type = AddBase(type,"IDisposable"); |
|
|
|
if (Options.GenerateClassMarshals) |
|
type = AddBase(type, "CppSharp.Runtime.ICppMarshal"); |
|
} |
|
|
|
return type; |
|
} |
|
|
|
public MemberDeclarationSyntax[] GenerateClassFields(Class owner, Class @class, |
|
Func<Class, Field, MemberDeclarationSyntax[]> function, bool nativeFields = false) |
|
{ |
|
var members = new List<MemberDeclarationSyntax>(); |
|
foreach (var @base in @class.Bases.Where(b => b.Class != null)) |
|
{ |
|
TypeMap typeMap; |
|
if ((!Driver.TypeDatabase.FindTypeMap(@base.Type, out typeMap) && !@base.Class.IsDeclared) || |
|
@base.Class.OriginalClass == @class) |
|
continue; |
|
|
|
members.AddRange(GenerateClassFields(owner, @base.Class, function, nativeFields)); |
|
} |
|
|
|
foreach (var field in @class.Fields) |
|
{ |
|
if (ASTUtils.CheckIgnoreField(field, nativeFields)) continue; |
|
members.AddRange(function(owner, field)); |
|
} |
|
|
|
return members.ToArray(); |
|
} |
|
|
|
private MemberDeclarationSyntax[] GenerateClassInternalsField(Class owner, Field field) |
|
{ |
|
// we do not support dependent fields yet, see https://github.com/mono/CppSharp/issues/197 |
|
Class @class; |
|
field.Type.TryGetClass(out @class); |
|
if ((field.Type.IsDependent && !field.Type.IsPointer() && |
|
!(@class != null && @class.IsUnion)) || (@class != null && @class.TranslationUnit.IsSystemHeader)) |
|
return new MemberDeclarationSyntax[0]; |
|
|
|
var members = new List<MemberDeclarationSyntax>(); |
|
|
|
var safeIdentifier = Helpers.SafeIdentifier(field.InternalName); |
|
if(safeIdentifier.All(c => c.Equals('_'))) |
|
{ |
|
safeIdentifier = Helpers.SafeIdentifier(field.Name); |
|
} |
|
|
|
TypePrinter.PushMarshalKind(CSharpMarshalKind.NativeField); |
|
var fieldTypePrinted = field.QualifiedType.CSharpType(TypePrinter); |
|
TypePrinter.PopMarshalKind(); |
|
|
|
var fieldName = safeIdentifier; |
|
if (!string.IsNullOrWhiteSpace(fieldTypePrinted.NameSuffix)) |
|
fieldName += fieldTypePrinted.NameSuffix; |
|
|
|
var offset = (int) field.OffsetInBytes + |
|
owner.ComputeNonVirtualBaseClassOffsetTo((Class) field.Namespace); |
|
var access = @class != null && !@class.IsGenerated ? |
|
SyntaxKind.InternalKeyword : SyntaxKind.PublicKeyword; |
|
|
|
var variable = SyntaxFactory.VariableDeclarator(fieldName); |
|
if (field.Expression != null) |
|
{ |
|
var fieldValuePrinted = field.Expression.CSharpValue(ExpressionPrinter).Value; |
|
variable = variable.WithInitializer( |
|
SyntaxFactory.EqualsValueClause(SyntaxFactory.IdentifierName(fieldValuePrinted))); |
|
} |
|
|
|
var fieldDeclaration = (SyntaxFactory.FieldDeclaration( |
|
SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName(fieldTypePrinted.Type)) |
|
.AddVariables(variable)) |
|
.AddModifiers(SyntaxFactory.Token(access)) |
|
.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes( |
|
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("FieldOffset")) |
|
.AddArgumentListArguments(SyntaxFactory.AttributeArgument( |
|
SyntaxFactory.LiteralExpression( |
|
SyntaxKind.NumericLiteralExpression, |
|
SyntaxFactory.Literal(offset))))))); |
|
|
|
// Workaround a bug in Mono when handling fixed arrays in P/Invoke declarations. |
|
// https://bugzilla.xamarin.com/show_bug.cgi?id=33571 |
|
var arrayType = field.Type.Desugar() as ArrayType; |
|
if (arrayType != null && arrayType.SizeType == ArrayType.ArraySize.Constant) |
|
{ |
|
fieldDeclaration = fieldDeclaration.AddModifiers( |
|
SyntaxFactory.Token(SyntaxKind.FixedKeyword)); |
|
for (var i = 1; i < arrayType.Size; ++i) |
|
{ |
|
var dummy = new Field |
|
{ |
|
Name = string.Format("{0}_{1}_{2}", Helpers.DummyIdentifier, |
|
safeIdentifier, i), |
|
QualifiedType = new QualifiedType(arrayType.Type), |
|
Offset = (uint) (field.Offset + i * arrayType.ElementSize), |
|
Namespace = owner |
|
}; |
|
|
|
members.AddRange(GenerateClassInternalsField(owner, dummy)); |
|
} |
|
} |
|
|
|
members.Insert(0, fieldDeclaration); |
|
return members.ToArray(); |
|
} |
|
|
|
private void GenerateClassField(Field field, bool @public = false) |
|
{ |
|
PushBlock(CSharpBlockKind.Field); |
|
|
|
GenerateDeclarationCommon(field); |
|
|
|
WriteLine("{0} {1} {2};", @public ? "public" : "private", |
|
field.Type, field.Name); |
|
|
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
#endregion |
|
|
|
private Tuple<string, string> GetDeclarationLibrarySymbol(IMangledDecl decl) |
|
{ |
|
var library = Options.SharedLibraryName; |
|
|
|
if (!Options.CheckSymbols) |
|
goto Out; |
|
|
|
NativeLibrary nativeLib; |
|
if (!Driver.Symbols.FindLibraryBySymbol(decl.Mangled, out nativeLib)) |
|
goto Out; |
|
|
|
library = Path.GetFileNameWithoutExtension(nativeLib.FileName); |
|
|
|
Out: |
|
return Tuple.Create(library, decl.Mangled); |
|
} |
|
|
|
private void GeneratePropertySetter<T>(T decl, |
|
Class @class, bool isAbstract = false, Property property = null) |
|
where T : Declaration, ITypedDecl |
|
{ |
|
if (!(decl is Function || decl is Field) ) |
|
{ |
|
return; |
|
} |
|
|
|
PushBlock(CSharpBlockKind.Method); |
|
Write("set"); |
|
|
|
var param = new Parameter |
|
{ |
|
Name = "value", |
|
QualifiedType = decl.QualifiedType |
|
}; |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
Parameter = param, |
|
ArgName = param.Name, |
|
}; |
|
|
|
if (decl is Function) |
|
{ |
|
var function = decl as Function; |
|
if (isAbstract) |
|
{ |
|
Write(";"); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
return; |
|
} |
|
|
|
NewLine(); |
|
WriteStartBraceIndent(); |
|
if (function.Parameters.Count == 0) |
|
throw new NotSupportedException("Expected at least one parameter in setter"); |
|
|
|
param.QualifiedType = function.Parameters[0].QualifiedType; |
|
|
|
var method = function as Method; |
|
if (function.SynthKind == FunctionSynthKind.AbstractImplCall) |
|
GenerateVirtualPropertyCall(method, @class.BaseClass, property, |
|
new List<Parameter> { param }); |
|
else if (method != null && method.IsVirtual) |
|
GenerateVirtualPropertyCall(method, @class, property, new List<Parameter> { param }); |
|
else |
|
{ |
|
if (method != null && method.OperatorKind == CXXOperatorKind.Subscript) |
|
{ |
|
if (method.OperatorKind == CXXOperatorKind.Subscript) |
|
{ |
|
GenerateIndexerSetter(method); |
|
} |
|
else |
|
{ |
|
GenerateInternalFunctionCall(function, new List<Parameter> { param }); |
|
} |
|
} |
|
else |
|
{ |
|
GenerateInternalFunctionCall(function, new List<Parameter> { param }); |
|
} |
|
} |
|
WriteCloseBraceIndent(); |
|
} |
|
else |
|
{ |
|
var field = decl as Field; |
|
if (WrapSetterArrayOfPointers(decl.Name, field.Type)) |
|
return; |
|
|
|
NewLine(); |
|
WriteStartBraceIndent(); |
|
|
|
ctx.Kind = CSharpMarshalKind.NativeField; |
|
var marshal = new CSharpMarshalManagedToNativePrinter(ctx); |
|
|
|
var arrayType = field.Type as ArrayType; |
|
|
|
if (arrayType != null && @class.IsValueType) |
|
{ |
|
ctx.ReturnVarName = HandleValueArray(arrayType, field); |
|
} |
|
else |
|
{ |
|
ctx.ReturnVarName = string.Format("{0}{1}{2}", |
|
@class.IsValueType |
|
? Helpers.InstanceField |
|
: string.Format("((Internal*) {0})", Helpers.InstanceIdentifier), |
|
@class.IsValueType ? "." : "->", |
|
Helpers.SafeIdentifier(field.InternalName)); |
|
} |
|
param.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
if (marshal.Context.Return.StringBuilder.Length > 0) |
|
{ |
|
WriteLine("{0} = {1};", ctx.ReturnVarName, marshal.Context.Return); |
|
} |
|
|
|
if (arrayType != null && @class.IsValueType) |
|
WriteCloseBraceIndent(); |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
private string HandleValueArray(ArrayType arrayType, Field field) |
|
{ |
|
var originalType = new PointerType(new QualifiedType(arrayType.Type)); |
|
var arrPtrIden = Generator.GeneratedIdentifier("arrPtr"); |
|
var arrPtr = arrPtrIden; |
|
var finalElementType = (arrayType.Type.GetFinalPointee() ?? arrayType.Type); |
|
var isChar = finalElementType.IsPrimitiveType(PrimitiveType.Char); |
|
|
|
string type; |
|
if (Driver.Options.MarshalCharAsManagedChar && isChar) |
|
{ |
|
var typePrinter = new CSharpTypePrinter(Driver); |
|
typePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
type = originalType.Visit(typePrinter).Type; |
|
arrPtrIden = Generator.GeneratedIdentifier(arrPtrIden); |
|
} |
|
else |
|
{ |
|
type = originalType.ToString(); |
|
} |
|
|
|
WriteLine(string.Format("fixed ({0} {1} = {2}.{3})", |
|
type, arrPtrIden, Helpers.InstanceField, |
|
Helpers.SafeIdentifier(field.InternalName))); |
|
WriteStartBraceIndent(); |
|
if (Driver.Options.MarshalCharAsManagedChar && isChar) |
|
WriteLine("var {0} = ({1}) {2};", arrPtr, originalType, arrPtrIden); |
|
return arrPtr; |
|
} |
|
|
|
private bool WrapSetterArrayOfPointers(string name, Type fieldType) |
|
{ |
|
var arrayType = fieldType as ArrayType; |
|
if (arrayType == null || !arrayType.Type.IsPointerToPrimitiveType()) |
|
return false; |
|
|
|
NewLine(); |
|
WriteStartBraceIndent(); |
|
WriteLine("{0} = value;", name); |
|
WriteLine("if (!{0}{1})", name, "Initialised"); |
|
WriteStartBraceIndent(); |
|
WriteLine("{0}{1} = true;", name, "Initialised"); |
|
WriteCloseBraceIndent(); |
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
|
|
return true; |
|
} |
|
|
|
private void GenerateIndexerSetter(Function function) |
|
{ |
|
Type type; |
|
function.Type.IsPointerTo(out type); |
|
PrimitiveType primitiveType; |
|
var internalFunction = GetFunctionNativeIdentifier(function); |
|
if (type.IsPrimitiveType(out primitiveType)) |
|
{ |
|
WriteLine("*Internal.{0}({1}, {2}) = value;", internalFunction, |
|
GetInstanceParam(function), function.Parameters[0].Name); |
|
} |
|
else |
|
{ |
|
var typeString = type.ToString(); |
|
Class @class; |
|
var isValueType = (type.GetFinalPointee() ?? type).TryGetClass(out @class) && |
|
@class.IsValueType; |
|
var paramMarshal = GenerateFunctionParamMarshal(function.Parameters[0], 0, function); |
|
WriteLine("*({0}.Internal*) Internal.{1}({2}, {3}) = {4}value.{5};", |
|
typeString, internalFunction, GetInstanceParam(function), |
|
paramMarshal.Context == null ? paramMarshal.Name : paramMarshal.Context.Return, |
|
isValueType ? string.Empty : string.Format("*({0}.Internal*) ", typeString), |
|
Helpers.InstanceIdentifier); |
|
} |
|
} |
|
|
|
private void GeneratePropertyGetter<T>(QualifiedType returnType, T decl, |
|
Class @class, bool isAbstract = false, Property property = null) |
|
where T : Declaration, ITypedDecl |
|
{ |
|
PushBlock(CSharpBlockKind.Method); |
|
Write("get"); |
|
|
|
if (decl is Function) |
|
{ |
|
var function = decl as Function; |
|
if (isAbstract) |
|
{ |
|
Write(";"); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
return; |
|
} |
|
|
|
NewLine(); |
|
WriteStartBraceIndent(); |
|
|
|
var method = function as Method; |
|
if (function.SynthKind == FunctionSynthKind.AbstractImplCall) |
|
GenerateVirtualPropertyCall(method, @class.BaseClass, property); |
|
else if (method != null && method.IsVirtual) |
|
GenerateVirtualPropertyCall(method, @class, property); |
|
else |
|
GenerateInternalFunctionCall(function, function.Parameters, returnType.Type); |
|
} |
|
else if (decl is Field) |
|
{ |
|
var field = decl as Field; |
|
if (WrapGetterArrayOfPointers(decl.Name, field.Type)) |
|
return; |
|
|
|
NewLine(); |
|
WriteStartBraceIndent(); |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
Kind = CSharpMarshalKind.NativeField, |
|
ArgName = decl.Name, |
|
ReturnVarName = string.Format("{0}{1}{2}", |
|
@class.IsValueType |
|
? Helpers.InstanceField |
|
: string.Format("((Internal*) {0})", Helpers.InstanceIdentifier), |
|
@class.IsValueType ? "." : "->", |
|
Helpers.SafeIdentifier(field.InternalName)), |
|
ReturnType = decl.QualifiedType |
|
}; |
|
|
|
var arrayType = field.Type as ArrayType; |
|
|
|
if (arrayType != null && @class.IsValueType) |
|
ctx.ReturnVarName = HandleValueArray(arrayType, field); |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
decl.CSharpMarshalToManaged(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("return {0};", marshal.Context.Return); |
|
|
|
if (arrayType != null && @class.IsValueType) |
|
WriteCloseBraceIndent(); |
|
} |
|
else if (decl is Variable) |
|
{ |
|
NewLine(); |
|
WriteStartBraceIndent(); |
|
var @var = decl as Variable; |
|
var libSymbol = GetDeclarationLibrarySymbol(@var); |
|
|
|
TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
|
|
var location = string.Format("CppSharp.SymbolResolver.ResolveSymbol(\"{0}\", \"{1}\")", |
|
libSymbol.Item1, libSymbol.Item2); |
|
|
|
var arrayType = decl.Type as ArrayType; |
|
var isRefTypeArray = arrayType != null && @class != null && @class.IsRefType; |
|
if (isRefTypeArray) |
|
WriteLine("var {0} = {1}{2};", Generator.GeneratedIdentifier("ptr"), |
|
arrayType.Type.IsPrimitiveType(PrimitiveType.Char) && |
|
arrayType.QualifiedType.Qualifiers.IsConst |
|
? string.Empty : "(byte*)", |
|
location); |
|
else |
|
WriteLine("var {0} = ({1}*){2};", Generator.GeneratedIdentifier("ptr"), |
|
@var.Type, location); |
|
|
|
TypePrinter.PopContext(); |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ArgName = decl.Name, |
|
ReturnVarName = (isRefTypeArray ? string.Empty : "*") + Generator.GeneratedIdentifier("ptr"), |
|
ReturnType = new QualifiedType(var.Type) |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
decl.CSharpMarshalToManaged(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("return {0};", marshal.Context.Return); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
private bool WrapGetterArrayOfPointers(string name, Type fieldType) |
|
{ |
|
var arrayType = fieldType as ArrayType; |
|
if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType()) |
|
{ |
|
NewLine(); |
|
WriteStartBraceIndent(); |
|
WriteLine("if (!{0}{1})", name, "Initialised"); |
|
WriteStartBraceIndent(); |
|
WriteLine("{0} = null;", name); |
|
WriteLine("{0}{1} = true;", name, "Initialised"); |
|
WriteCloseBraceIndent(); |
|
WriteLine("return {0};", name); |
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
public void GenerateClassMethods(IList<Method> methods) |
|
{ |
|
if (methods.Count == 0) |
|
return; |
|
|
|
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<Method>(); |
|
foreach (var method in methods) |
|
{ |
|
if (ASTUtils.CheckIgnoreMethod(method, Options)) |
|
continue; |
|
|
|
if (method.IsConstructor) |
|
continue; |
|
|
|
if (method.IsStatic) |
|
{ |
|
staticMethods.Add(method); |
|
continue; |
|
} |
|
|
|
GenerateMethod(method, @class); |
|
} |
|
|
|
foreach (var method in staticMethods) |
|
{ |
|
GenerateMethod(method, @class); |
|
} |
|
} |
|
|
|
public void GenerateClassVariables(Class @class) |
|
{ |
|
if (@class.IsValueType) |
|
foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore)) |
|
GenerateClassVariables(@base.Class); |
|
|
|
foreach (var variable in @class.Variables) |
|
{ |
|
if (!variable.IsGenerated) continue; |
|
|
|
if (variable.Access != AccessSpecifier.Public) |
|
continue; |
|
|
|
var type = variable.Type; |
|
|
|
GenerateVariable(@class, type, variable); |
|
} |
|
} |
|
|
|
private void GenerateClassProperties(Class @class) |
|
{ |
|
if (@class.IsValueType) |
|
foreach (var @base in @class.Bases.Where(b => b.IsClass && !b.Class.Ignore && b.Class.IsDeclared)) |
|
GenerateClassProperties(@base.Class); |
|
|
|
GenerateProperties(@class); |
|
} |
|
|
|
private void GenerateProperties(Class @class) |
|
{ |
|
foreach (var prop in @class.Properties.Where(p => p.IsGenerated)) |
|
{ |
|
if (prop.IsInRefTypeAndBackedByValueClassField()) |
|
{ |
|
GenerateClassField(prop.Field, true); |
|
continue; |
|
} |
|
|
|
PushBlock(CSharpBlockKind.Property); |
|
|
|
ArrayType arrayType = prop.Type as ArrayType; |
|
if (arrayType != null && arrayType.Type.IsPointerToPrimitiveType() && prop.Field != null) |
|
{ |
|
GenerateClassField(prop.Field); |
|
WriteLine("private bool {0};", |
|
GeneratedIdentifier(string.Format("{0}Initialised", prop.Field.InternalName))); |
|
} |
|
|
|
GenerateDeclarationCommon(prop); |
|
if (prop.ExplicitInterfaceImpl == null) |
|
{ |
|
//Write(Helpers.GetAccess(GetValidPropertyAccess(prop))); |
|
|
|
if (prop.IsStatic) |
|
Write("static "); |
|
|
|
// check if overriding a property from a secondary base |
|
Property rootBaseProperty; |
|
var isOverride = prop.IsOverride && |
|
(rootBaseProperty = @class.GetBaseProperty(prop, true, true)) != null && |
|
(rootBaseProperty.IsVirtual || rootBaseProperty.IsPure); |
|
|
|
if (isOverride) |
|
Write("override "); |
|
else if (prop.IsPure) |
|
Write("abstract "); |
|
|
|
if (prop.IsVirtual && !isOverride && !prop.IsPure) |
|
Write("virtual "); |
|
|
|
WriteLine("{0} {1}", prop.Type, GetPropertyName(prop)); |
|
} |
|
else |
|
{ |
|
WriteLine("{0} {1}.{2}", prop.Type, prop.ExplicitInterfaceImpl.Name, |
|
GetPropertyName(prop)); |
|
} |
|
WriteStartBraceIndent(); |
|
|
|
if (prop.Field != null) |
|
{ |
|
if (prop.HasGetter) |
|
GeneratePropertyGetter(prop.QualifiedType, prop.Field, @class); |
|
|
|
if (prop.HasSetter) |
|
GeneratePropertySetter(prop.Field, @class); |
|
} |
|
else |
|
{ |
|
if (prop.HasGetter) |
|
GeneratePropertyGetter(prop.QualifiedType, prop.GetMethod, @class, prop.IsPure, prop); |
|
|
|
if (prop.HasSetter) |
|
GeneratePropertySetter(prop.SetMethod, @class, prop.IsPure, prop); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
} |
|
|
|
private string GetPropertyName(Property prop) |
|
{ |
|
return prop.Parameters.Count == 0 ? prop.Name |
|
: string.Format("this[{0}]", FormatMethodParameters(prop.Parameters)); |
|
} |
|
|
|
private void GenerateVariable(Class @class, Type type, Variable variable) |
|
{ |
|
PushBlock(CSharpBlockKind.Variable); |
|
|
|
GenerateDeclarationCommon(variable); |
|
WriteLine("public static {0} {1}", type, variable.Name); |
|
WriteStartBraceIndent(); |
|
|
|
GeneratePropertyGetter(variable.QualifiedType, variable, @class); |
|
|
|
if (!variable.QualifiedType.Qualifiers.IsConst) |
|
GeneratePropertySetter(variable, @class); |
|
|
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
#region Virtual Tables |
|
|
|
public List<VTableComponent> GetUniqueVTableMethodEntries(Class @class) |
|
{ |
|
var uniqueEntries = new OrderedSet<VTableComponent>(); |
|
var vTableMethodEntries = VTables.GatherVTableMethodEntries(@class); |
|
foreach (var entry in vTableMethodEntries.Where(e => !e.IsIgnored() && !e.Method.IsOperator)) |
|
uniqueEntries.Add(entry); |
|
|
|
return uniqueEntries.ToList(); |
|
} |
|
|
|
public void GenerateVTable(Class @class) |
|
{ |
|
var wrappedEntries = GetUniqueVTableMethodEntries(@class); |
|
if (wrappedEntries.Count == 0) |
|
return; |
|
|
|
PushBlock(CSharpBlockKind.Region); |
|
WriteLine("#region Virtual table interop"); |
|
NewLine(); |
|
|
|
// Generate a delegate type for each method. |
|
foreach (var method in wrappedEntries.Select(e => e.Method)) |
|
{ |
|
GenerateVTableMethodDelegates(@class, method); |
|
} |
|
|
|
WriteLine("private static void*[] __ManagedVTables;"); |
|
if (wrappedEntries.Any(e => e.Method.IsDestructor)) |
|
WriteLine("private static void*[] __ManagedVTablesDtorOnly;"); |
|
WriteLine("private static void*[] _Thunks;"); |
|
NewLine(); |
|
|
|
GenerateVTableClassSetup(@class, wrappedEntries); |
|
|
|
WriteLine("#endregion"); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
private void GenerateVTableClassSetup(Class @class, IList<VTableComponent> wrappedEntries) |
|
{ |
|
const string destructorOnly = "destructorOnly"; |
|
WriteLine("private void SetupVTables(bool {0} = false)", destructorOnly); |
|
WriteStartBraceIndent(); |
|
|
|
WriteLine("if (__OriginalVTables != null)"); |
|
WriteLineIndent("return;"); |
|
|
|
WriteLine("var native = (Internal*) {0}.ToPointer();", Helpers.InstanceIdentifier); |
|
NewLine(); |
|
|
|
SaveOriginalVTablePointers(@class.Layout.VFTables); |
|
|
|
NewLine(); |
|
|
|
var hasVirtualDtor = wrappedEntries.Any(e => e.Method.IsDestructor); |
|
if (!hasVirtualDtor) |
|
{ |
|
WriteLine("if ({0})", destructorOnly); |
|
WriteLineIndent("return;"); |
|
} |
|
|
|
// Get the _Thunks |
|
WriteLine("if (_Thunks == null)"); |
|
WriteStartBraceIndent(); |
|
WriteLine("_Thunks = new void*[{0}];", wrappedEntries.Count); |
|
|
|
var uniqueEntries = new HashSet<VTableComponent>(); |
|
|
|
for (int i = 0; i < wrappedEntries.Count; i++) |
|
{ |
|
var entry = wrappedEntries[i]; |
|
var method = entry.Method; |
|
var name = GetVTableMethodDelegateName(method); |
|
var instance = name + "Instance"; |
|
if (uniqueEntries.Add(entry)) |
|
WriteLine("{0} += {1}Hook;", instance, name); |
|
WriteLine("_Thunks[{0}] = Marshal.GetFunctionPointerForDelegate({1}).ToPointer();", |
|
i, instance); |
|
} |
|
WriteCloseBraceIndent(); |
|
|
|
NewLine(); |
|
|
|
if (hasVirtualDtor) |
|
{ |
|
WriteLine("if ({0})", destructorOnly); |
|
WriteStartBraceIndent(); |
|
WriteLine("if (__ManagedVTablesDtorOnly == null)"); |
|
WriteStartBraceIndent(); |
|
|
|
AllocateNewVTables(@class, wrappedEntries, true); |
|
|
|
WriteCloseBraceIndent(); |
|
WriteLine("else"); |
|
WriteStartBraceIndent(); |
|
} |
|
WriteLine("if (__ManagedVTables == null)"); |
|
WriteStartBraceIndent(); |
|
|
|
AllocateNewVTables(@class, wrappedEntries, false); |
|
|
|
if (hasVirtualDtor) |
|
WriteCloseBraceIndent(); |
|
|
|
WriteCloseBraceIndent(); |
|
NewLine(); |
|
} |
|
|
|
private void AllocateNewVTables(Class @class, IList<VTableComponent> wrappedEntries, |
|
bool destructorOnly) |
|
{ |
|
if (Options.IsMicrosoftAbi) |
|
AllocateNewVTablesMS(@class, wrappedEntries, destructorOnly); |
|
else |
|
AllocateNewVTablesItanium(@class, wrappedEntries, destructorOnly); |
|
} |
|
|
|
private void SaveOriginalVTablePointers(IEnumerable<VFTableInfo> vfTables) |
|
{ |
|
if (Driver.Options.IsMicrosoftAbi) |
|
WriteLine("__OriginalVTables = new void*[] {{ {0} }};", |
|
string.Join(", ", vfTables.Select((v, i) => string.Format("native->vfptr{0}.ToPointer()", i)))); |
|
else |
|
WriteLine("__OriginalVTables = new void*[] { native->vfptr0.ToPointer() };"); |
|
} |
|
|
|
private void AllocateNewVTablesMS(Class @class, IList<VTableComponent> wrappedEntries, |
|
bool destructorOnly) |
|
{ |
|
var managedVTables = destructorOnly ? "__ManagedVTablesDtorOnly" : "__ManagedVTables"; |
|
WriteLine("{0} = new void*[{1}];", managedVTables, @class.Layout.VFTables.Count); |
|
|
|
for (int tableIndex = 0; tableIndex < @class.Layout.VFTables.Count; tableIndex++) |
|
{ |
|
var vfptr = @class.Layout.VFTables[tableIndex]; |
|
var size = vfptr.Layout.Components.Count; |
|
WriteLine("var vfptr{0} = Marshal.AllocHGlobal({1} * {2});", |
|
tableIndex, size, Driver.TargetInfo.PointerWidth / 8); |
|
WriteLine("{0}[{1}] = vfptr{1}.ToPointer();", managedVTables, tableIndex); |
|
|
|
AllocateNewVTableEntries(vfptr.Layout.Components, wrappedEntries, tableIndex, |
|
destructorOnly); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
NewLine(); |
|
|
|
for (var i = 0; i < @class.Layout.VFTables.Count; i++) |
|
WriteLine("native->vfptr{0} = new IntPtr({1}[{0}]);", i, managedVTables); |
|
} |
|
|
|
private void AllocateNewVTablesItanium(Class @class, IList<VTableComponent> wrappedEntries, |
|
bool destructorOnly) |
|
{ |
|
var managedVTables = destructorOnly ? "__ManagedVTablesDtorOnly" : "__ManagedVTables"; |
|
WriteLine("{0} = new void*[1];", managedVTables); |
|
|
|
var size = @class.Layout.Layout.Components.Count; |
|
var pointerSize = Driver.TargetInfo.PointerWidth / 8; |
|
WriteLine("var vtptr = Marshal.AllocHGlobal({0} * {1});", size, pointerSize); |
|
|
|
WriteLine("var vfptr0 = vtptr + {0} * {1};", VTables.ItaniumOffsetToTopAndRTTI, pointerSize); |
|
WriteLine("{0}[0] = vfptr0.ToPointer();", managedVTables); |
|
|
|
AllocateNewVTableEntries(@class.Layout.Layout.Components, |
|
wrappedEntries, 0, destructorOnly); |
|
|
|
WriteCloseBraceIndent(); |
|
NewLine(); |
|
|
|
WriteLine("native->vfptr0 = new IntPtr({0}[0]);", managedVTables); |
|
} |
|
|
|
private void AllocateNewVTableEntries(IList<VTableComponent> entries, |
|
IList<VTableComponent> wrappedEntries, int tableIndex, bool destructorOnly) |
|
{ |
|
var pointerSize = Driver.TargetInfo.PointerWidth / 8; |
|
for (var i = 0; i < entries.Count; i++) |
|
{ |
|
var entry = entries[i]; |
|
var offset = pointerSize |
|
* (i - (Options.IsMicrosoftAbi ? 0 : VTables.ItaniumOffsetToTopAndRTTI)); |
|
|
|
var nativeVftableEntry = string.Format("*(void**)(native->vfptr{0} + {1})", tableIndex, offset); |
|
var managedVftableEntry = string.Format("*(void**)(vfptr{0} + {1})", tableIndex, offset); |
|
|
|
if ((entry.Kind == VTableComponentKind.FunctionPointer || |
|
entry.Kind == VTableComponentKind.DeletingDtorPointer) && |
|
!entry.IsIgnored() && |
|
(!destructorOnly || entry.Method.IsDestructor || |
|
Driver.Options.ExplicitlyPatchedVirtualFunctions.Contains(entry.Method.OriginalName))) |
|
WriteLine("{0} = _Thunks[{1}];", managedVftableEntry, wrappedEntries.IndexOf(entry)); |
|
else |
|
WriteLine("{0} = {1};", managedVftableEntry, nativeVftableEntry); |
|
} |
|
} |
|
|
|
private void GenerateVTableClassSetupCall(Class @class, bool destructorOnly = false) |
|
{ |
|
if (@class.IsDynamic && GetUniqueVTableMethodEntries(@class).Count > 0) |
|
{ |
|
if (destructorOnly) |
|
{ |
|
WriteLine("SetupVTables(true);"); |
|
return; |
|
} |
|
var typeFullName = TypePrinter.VisitClassDecl(@class); |
|
if (!string.IsNullOrEmpty(Driver.Options.OutputNamespace)) |
|
typeFullName = string.Format("{0}.{1}", Driver.Options.OutputNamespace, typeFullName); |
|
WriteLine("SetupVTables(GetType().FullName == \"{0}\");", typeFullName); |
|
} |
|
} |
|
|
|
private void GenerateVTableManagedCall(Method method) |
|
{ |
|
if (method.IsDestructor) |
|
{ |
|
WriteLine("{0}.Dispose(false);", Helpers.TargetIdentifier); |
|
return; |
|
} |
|
|
|
var marshals = new List<string>(); |
|
for (int i = 0; i < method.Parameters.Count; i++) |
|
{ |
|
var param = method.Parameters[i]; |
|
if (!param.IsGenerated && param.GenerationKind != GenerationKind.Link) |
|
continue; |
|
|
|
if (param.Kind == ParameterKind.IndirectReturnType) |
|
continue; |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ReturnType = param.QualifiedType, |
|
ReturnVarName = param.Name, |
|
ParameterIndex = i |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx) { MarshalsParameter = true }; |
|
param.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
marshals.Add(marshal.Context.Return); |
|
} |
|
|
|
var hasReturn = !method.OriginalReturnType.Type.IsPrimitiveType(PrimitiveType.Void); |
|
|
|
if (hasReturn) |
|
Write("var {0} = ", Helpers.ReturnIdentifier); |
|
|
|
if (method.IsGenerated || method.GenerationKind == GenerationKind.Link) |
|
{ |
|
WriteLine("{0}.{1}({2});", Helpers.TargetIdentifier, |
|
method.Name, string.Join(", ", marshals)); |
|
} |
|
else |
|
{ |
|
InvokeProperty(method, marshals); |
|
} |
|
|
|
if (hasReturn) |
|
{ |
|
var param = new Parameter |
|
{ |
|
Name = Helpers.ReturnIdentifier, |
|
QualifiedType = method.OriginalReturnType |
|
}; |
|
|
|
// Marshal the managed result to native |
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ArgName = Helpers.ReturnIdentifier, |
|
Parameter = param, |
|
Function = method |
|
}; |
|
|
|
var marshal = new CSharpMarshalManagedToNativePrinter(ctx); |
|
method.OriginalReturnType.Visit(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
if (method.HasIndirectReturnTypeParameter) |
|
{ |
|
var retParam = method.Parameters.First(p => p.Kind == ParameterKind.IndirectReturnType); |
|
TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
WriteLine("*({0}*) {1} = {2};", |
|
method.OriginalReturnType.Visit(TypePrinter), retParam.Name, marshal.Context.Return); |
|
TypePrinter.PopContext(); |
|
} |
|
else |
|
{ |
|
WriteLine("return {0};", marshal.Context.Return); |
|
} |
|
} |
|
} |
|
|
|
private void InvokeProperty(Declaration method, IEnumerable<string> marshals) |
|
{ |
|
var property = ((Class) method.Namespace).Properties.FirstOrDefault( |
|
p => p.GetMethod == method); |
|
if (property == null) |
|
{ |
|
property = ((Class) method.Namespace).Properties.First( |
|
p => p.SetMethod == method); |
|
WriteLine("{0}.{1} = {2};", Helpers.TargetIdentifier, property.Name, |
|
string.Join(", ", marshals)); |
|
} |
|
else |
|
{ |
|
WriteLine("{0}.{1};", Helpers.TargetIdentifier, property.Name); |
|
} |
|
} |
|
|
|
private void GenerateVTableMethodDelegates(Class @class, Method method) |
|
{ |
|
PushBlock(CSharpBlockKind.VTableDelegate); |
|
|
|
// This works around a parser bug, see https://github.com/mono/CppSharp/issues/202 |
|
if (method.Signature != null) |
|
{ |
|
var cleanSig = method.Signature.ReplaceLineBreaks(""); |
|
cleanSig = Regex.Replace(cleanSig, @"\s+", " "); |
|
|
|
WriteLine("// {0}", cleanSig); |
|
} |
|
|
|
CSharpTypePrinterResult retType; |
|
var @params = GatherInternalParams(method, out retType); |
|
|
|
var vTableMethodDelegateName = GetVTableMethodDelegateName(method); |
|
|
|
WriteLine("private static {0} {1}Instance;", DelegatesPass.Delegates[method].Visit(TypePrinter), |
|
vTableMethodDelegateName); |
|
NewLine(); |
|
|
|
WriteLine("private static {0} {1}Hook({2})", retType, vTableMethodDelegateName, |
|
string.Join(", ", @params)); |
|
WriteStartBraceIndent(); |
|
|
|
WriteLine("if (!NativeToManagedMap.ContainsKey(instance))"); |
|
WriteLineIndent("throw new global::System.Exception(\"No managed instance was found\");"); |
|
NewLine(); |
|
|
|
WriteLine("var {0} = ({1}) NativeToManagedMap[instance];", Helpers.TargetIdentifier, @class.Name); |
|
WriteLine("if ({0}.{1})", Helpers.TargetIdentifier, Helpers.OwnsNativeInstanceIdentifier); |
|
WriteLineIndent("{0}.SetupVTables();", Helpers.TargetIdentifier); |
|
GenerateVTableManagedCall(method); |
|
|
|
WriteCloseBraceIndent(); |
|
|
|
PopBlock(NewLineKind.Always); |
|
} |
|
|
|
public string GetVTableMethodDelegateName(Function function) |
|
{ |
|
var nativeId = GetFunctionNativeIdentifier(function); |
|
|
|
// Trim '@' (if any) because '@' is valid only as the first symbol. |
|
nativeId = nativeId.Trim('@'); |
|
|
|
return string.Format("_{0}Delegate", nativeId); |
|
} |
|
|
|
public MemberDeclarationSyntax[] GenerateVTablePointers(Class @class) |
|
{ |
|
var members = new List<MemberDeclarationSyntax>(); |
|
|
|
if (Options.IsMicrosoftAbi) |
|
{ |
|
for (int i = 0; i < @class.Layout.VFTables.Count; i++) |
|
{ |
|
var vFPtrFullOffset = (int) @class.Layout.VFTables[i].VFPtrFullOffset; |
|
members.Add(VTablePointer(i, vFPtrFullOffset)); |
|
} |
|
} |
|
else |
|
{ |
|
members.Add(VTablePointer(0, 0)); |
|
} |
|
|
|
return members.ToArray(); |
|
} |
|
|
|
private FieldDeclarationSyntax VTablePointer(int i, int offset) |
|
{ |
|
return SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration( |
|
SyntaxFactory.ParseTypeName("global::System.IntPtr")).AddVariables( |
|
SyntaxFactory.VariableDeclarator("vfptr" + i))) |
|
.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes( |
|
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("FieldOffset")) |
|
.AddArgumentListArguments(SyntaxFactory.AttributeArgument( |
|
SyntaxFactory.LiteralExpression( |
|
SyntaxKind.NumericLiteralExpression, |
|
SyntaxFactory.Literal(offset)))))) |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)); |
|
} |
|
|
|
#endregion |
|
|
|
#region Events |
|
|
|
private void GenerateEvent(Event @event) |
|
{ |
|
PushBlock(CSharpBlockKind.Event, @event); |
|
TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
var args = TypePrinter.VisitParameters(@event.Parameters, hasNames: true); |
|
TypePrinter.PopContext(); |
|
|
|
var delegateInstance = Generator.GeneratedIdentifier(@event.OriginalName); |
|
var delegateName = delegateInstance + "Delegate"; |
|
var delegateRaise = delegateInstance + "RaiseInstance"; |
|
|
|
WriteLine("[UnmanagedFunctionPointerAttribute(global::System.Runtime.InteropServices.CallingConvention.Cdecl)]"); |
|
WriteLine("delegate void {0}({1});", delegateName, args); |
|
WriteLine("{0} {1};", delegateName, delegateRaise); |
|
NewLine(); |
|
|
|
WriteLine("{0} {1};", @event.Type, delegateInstance); |
|
WriteLine("public event {0} {1}", @event.Type, @event.Name); |
|
WriteStartBraceIndent(); |
|
|
|
GenerateEventAdd(@event, delegateRaise, delegateName, delegateInstance); |
|
NewLine(); |
|
|
|
GenerateEventRemove(@event, delegateInstance); |
|
|
|
WriteCloseBraceIndent(); |
|
NewLine(); |
|
|
|
GenerateEventRaiseWrapper(@event, delegateInstance); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
private void GenerateEventAdd(Event @event, string delegateRaise, string delegateName, string delegateInstance) |
|
{ |
|
WriteLine("add"); |
|
WriteStartBraceIndent(); |
|
|
|
WriteLine("if ({0} == null)", delegateRaise); |
|
WriteStartBraceIndent(); |
|
|
|
WriteLine("{0} = new {1}(_{2}Raise);", delegateRaise, delegateName, @event.Name); |
|
|
|
WriteLine("var {0} = Marshal.GetFunctionPointerForDelegate({1}).ToPointer();", |
|
Generator.GeneratedIdentifier("ptr"), delegateInstance); |
|
|
|
// Call type map here. |
|
|
|
//WriteLine("((::{0}*)NativePtr)->{1}.Connect(_fptr);", @class.QualifiedOriginalName, |
|
// @event.OriginalName); |
|
|
|
WriteCloseBraceIndent(); |
|
|
|
WriteLine("{0} = ({1})System.Delegate.Combine({0}, value);", |
|
delegateInstance, @event.Type); |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
private void GenerateEventRemove(ITypedDecl @event, string delegateInstance) |
|
{ |
|
WriteLine("remove"); |
|
WriteStartBraceIndent(); |
|
|
|
WriteLine("{0} = ({1})System.Delegate.Remove({0}, value);", |
|
delegateInstance, @event.Type); |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
private void GenerateEventRaiseWrapper(Event @event, string delegateInstance) |
|
{ |
|
TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
var args = TypePrinter.VisitParameters(@event.Parameters, hasNames: true); |
|
TypePrinter.PopContext(); |
|
|
|
WriteLine("void _{0}Raise({1})", @event.Name, args); |
|
WriteStartBraceIndent(); |
|
|
|
var returns = new List<string>(); |
|
foreach (var param in @event.Parameters) |
|
{ |
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ReturnVarName = param.Name, |
|
ReturnType = param.QualifiedType |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
param.Visit(marshal); |
|
|
|
returns.Add(marshal.Context.Return); |
|
} |
|
|
|
WriteLine("if ({0} != null)", delegateInstance); |
|
WriteStartBraceIndent(); |
|
WriteLine("{0}({1});", delegateInstance, string.Join(", ", returns)); |
|
WriteCloseBraceIndent(); |
|
|
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
#endregion |
|
|
|
#region Constructors |
|
|
|
public MemberDeclarationSyntax[] GenerateClassConstructors(Class @class) |
|
{ |
|
var members = new List<MemberDeclarationSyntax>(); |
|
|
|
// Output a default constructor that takes the native pointer. |
|
members.AddRange(GenerateNativeConstructor(@class)); |
|
|
|
//foreach (var ctor in @class.Constructors) |
|
//{ |
|
// if (ASTUtils.CheckIgnoreMethod(ctor, Options)) |
|
// continue; |
|
|
|
// GenerateMethod(ctor, @class); |
|
//} |
|
|
|
if (@class.IsRefType) |
|
{ |
|
if (Options.GenerateFinalizers) |
|
members.Add(GenerateClassFinalizer(@class)); |
|
|
|
// ensure any virtual dtor in the chain is called |
|
var dtor = @class.Destructors.FirstOrDefault(d => d.Access != AccessSpecifier.Private && d.IsVirtual); |
|
var baseDtor = @class.BaseClass == null ? null : |
|
@class.BaseClass.Destructors.FirstOrDefault(d => !d.IsVirtual); |
|
if (ShouldGenerateClassNativeField(@class) || (dtor != null && baseDtor != null) || |
|
// virtual destructors in abstract classes may lack a pointer in the v-table |
|
// so they have to be called by symbol; thus we need an explicit Dispose override |
|
@class.IsAbstract) |
|
members.AddRange(GenerateDisposeMethods(@class)); |
|
} |
|
|
|
return members.ToArray(); |
|
} |
|
|
|
private MemberDeclarationSyntax GenerateClassFinalizer(INamedDecl @class) |
|
{ |
|
return SyntaxFactory.DestructorDeclaration(@class.Name).AddBodyStatements( |
|
SyntaxFactory.ParseStatement("Dispose(false)")); |
|
} |
|
|
|
private IEnumerable<MemberDeclarationSyntax> GenerateDisposeMethods(Class @class) |
|
{ |
|
var members = new List<MemberDeclarationSyntax>(); |
|
|
|
var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; |
|
|
|
// Generate the IDispose Dispose() method. |
|
if (!hasBaseClass) |
|
{ |
|
var dispose = SyntaxFactory.MethodDeclaration( |
|
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "Dispose") |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword)) |
|
.AddBodyStatements(SyntaxFactory.ParseStatement("Dispose(disposing: true);")); |
|
|
|
if (Options.GenerateFinalizers) |
|
dispose = dispose.AddBodyStatements( |
|
SyntaxFactory.ParseStatement("GC.SuppressFinalize(this);")); |
|
members.Add(dispose); |
|
} |
|
|
|
// Generate Dispose(bool) method |
|
var disposeBool = SyntaxFactory.MethodDeclaration( |
|
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "Dispose") |
|
.AddModifiers(SyntaxFactory.Token( |
|
@class.IsValueType ? SyntaxKind.PrivateKeyword : SyntaxKind.ProtectedKeyword)) |
|
.AddParameterListParameters( |
|
SyntaxFactory.Parameter(SyntaxFactory.Identifier("disposing")).WithType( |
|
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword)))) |
|
.AddBodyStatements(); |
|
|
|
if (@class.IsRefType) |
|
disposeBool = disposeBool.AddModifiers(SyntaxFactory.Token( |
|
hasBaseClass ? SyntaxKind.OverrideKeyword : SyntaxKind.VirtualKeyword)); |
|
|
|
members.Add(disposeBool); |
|
|
|
//WriteLine("if (!{0} && disposing)", Helpers.OwnsNativeInstanceIdentifier); |
|
//WriteLineIndent("throw new global::System.InvalidOperationException" + |
|
// "(\"Managed instances owned by native code cannot be disposed of.\");"); |
|
|
|
//if (@class.IsRefType) |
|
//{ |
|
// var @base = @class.GetNonIgnoredRootBase(); |
|
|
|
// // Use interfaces if any - derived types with a this class as a seconary base, must be compatible with the map |
|
// var @interface = @base.Namespace.Classes.Find(c => c.OriginalClass == @base); |
|
|
|
// // The local var must be of the exact type in the object map because of TryRemove |
|
// WriteLine("{0} {1};", |
|
// (@interface ?? (@base.IsAbstractImpl ? @base.BaseClass : @base)).Visit(TypePrinter), |
|
// Helpers.DummyIdentifier); |
|
// WriteLine("NativeToManagedMap.TryRemove({0}, out {1});", |
|
// Helpers.InstanceIdentifier, Helpers.DummyIdentifier); |
|
// if (@class.IsDynamic && GetUniqueVTableMethodEntries(@class).Count != 0) |
|
// { |
|
// if (Options.IsMicrosoftAbi) |
|
// for (var i = 0; i < @class.Layout.VFTables.Count; i++) |
|
// WriteLine("((Internal*) {0})->vfptr{1} = new global::System.IntPtr(__OriginalVTables[{1}]);", |
|
// Helpers.InstanceIdentifier, i); |
|
// else |
|
// WriteLine("((Internal*) {0})->vfptr0 = new global::System.IntPtr(__OriginalVTables[0]);", |
|
// Helpers.InstanceIdentifier); |
|
// } |
|
//} |
|
|
|
//var dtor = @class.Destructors.FirstOrDefault(); |
|
//if (dtor != null && dtor.Access != AccessSpecifier.Private && |
|
// @class.HasNonTrivialDestructor && !dtor.IsPure) |
|
//{ |
|
// NativeLibrary library; |
|
// if (!Options.CheckSymbols || |
|
// Driver.Symbols.FindLibraryBySymbol(dtor.Mangled, out library)) |
|
// { |
|
// if (dtor.IsVirtual) |
|
// { |
|
// GenerateVirtualFunctionCall(dtor, @class, true); |
|
// if (@class.IsAbstract) |
|
// { |
|
// WriteCloseBraceIndent(); |
|
// WriteLine("else"); |
|
// PushIndent(); |
|
// GenerateInternalFunctionCall(dtor); |
|
// PopIndent(); |
|
// } |
|
// } |
|
// else |
|
// GenerateInternalFunctionCall(dtor); |
|
// } |
|
//} |
|
|
|
//WriteLine("if ({0})", Helpers.OwnsNativeInstanceIdentifier); |
|
//WriteLineIndent("Marshal.FreeHGlobal({0});", Helpers.InstanceIdentifier); |
|
|
|
return members; |
|
} |
|
|
|
private MemberDeclarationSyntax[] GenerateNativeConstructor(Class @class) |
|
{ |
|
var members = new List<MemberDeclarationSyntax>(); |
|
|
|
var shouldGenerateClassNativeField = ShouldGenerateClassNativeField(@class); |
|
if (@class.IsRefType && shouldGenerateClassNativeField) |
|
{ |
|
members.Add(SyntaxFactory.FieldDeclaration(SyntaxFactory.VariableDeclaration( |
|
SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.BoolKeyword))) |
|
.AddVariables( |
|
SyntaxFactory.VariableDeclarator(Helpers.OwnsNativeInstanceIdentifier))) |
|
.AddModifiers(SyntaxFactory.Token(SyntaxKind.ProtectedKeyword))); |
|
} |
|
|
|
return members.ToArray(); |
|
|
|
var className = @class.IsAbstractImpl ? @class.BaseClass.Name : @class.Name; |
|
|
|
var ctorCall = string.Format("{0}{1}", @class.Name, @class.IsAbstract ? "Internal" : ""); |
|
if (!@class.IsAbstractImpl) |
|
{ |
|
PushBlock(CSharpBlockKind.Method); |
|
WriteLine("public static {0}{1} {2}(global::System.IntPtr native{3}{4})", |
|
@class.NeedsBase && !@class.BaseClass.IsInterface ? "new " : string.Empty, |
|
@class.Name, Helpers.CreateInstanceIdentifier, |
|
@class.IsRefType ? ", bool ownsNativeInstance = false" : string.Empty, |
|
", bool skipVTables = false"); |
|
WriteStartBraceIndent(); |
|
WriteLine("return new {0}(({1}.Internal*) native, skipVTables){2};", ctorCall, className, |
|
@class.IsRefType |
|
? string.Format(" {{ {0} = ownsNativeInstance }}", Helpers.OwnsNativeInstanceIdentifier) |
|
: string.Empty); |
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
GenerateNativeConstructorByValue(@class, className, ctorCall); |
|
|
|
PushBlock(CSharpBlockKind.Method); |
|
WriteLine("{0} {1}({2}.Internal* native, bool skipVTables = false){3}", |
|
@class.IsAbstractImpl ? "internal" : (@class.IsRefType ? "protected" : "private"), |
|
@class.Name, className, @class.IsValueType ? " : this()" : string.Empty); |
|
|
|
var hasBaseClass = @class.HasBaseClass && @class.BaseClass.IsRefType; |
|
if (hasBaseClass) |
|
WriteLineIndent(": base(({0}.Internal*) null)", @class.BaseClass.Visit(TypePrinter)); |
|
|
|
WriteStartBraceIndent(); |
|
|
|
if (@class.IsRefType) |
|
{ |
|
if (@class.HasBaseClass) |
|
WriteLine("{0} = {1};", Helpers.PointerAdjustmentIdentifier, |
|
@class.ComputeNonVirtualBaseClassOffsetTo(@class.BaseClass)); |
|
if (!@class.IsAbstractImpl) |
|
{ |
|
WriteLine("if (native == null)"); |
|
WriteLineIndent("return;"); |
|
} |
|
|
|
WriteLine("{0} = new global::System.IntPtr(native);", Helpers.InstanceIdentifier); |
|
var dtor = @class.Destructors.FirstOrDefault(); |
|
var hasVTables = @class.IsDynamic && GetUniqueVTableMethodEntries(@class).Count > 0; |
|
var setupVTables = !@class.IsAbstractImpl && hasVTables && dtor != null && dtor.IsVirtual; |
|
if (setupVTables) |
|
{ |
|
WriteLine("if (skipVTables)"); |
|
PushIndent(); |
|
} |
|
|
|
if (@class.IsAbstractImpl || hasVTables) |
|
SaveOriginalVTablePointers(@class.Layout.VFTables); |
|
|
|
if (setupVTables) |
|
{ |
|
PopIndent(); |
|
WriteLine("else"); |
|
PushIndent(); |
|
GenerateVTableClassSetupCall(@class, destructorOnly: true); |
|
PopIndent(); |
|
} |
|
} |
|
else |
|
{ |
|
WriteLine("{0} = *native;", Helpers.InstanceField); |
|
} |
|
|
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
private void GenerateNativeConstructorByValue(Class @class, string className, string ctorCall) |
|
{ |
|
if (!@class.IsAbstractImpl) |
|
{ |
|
PushBlock(CSharpBlockKind.Method); |
|
WriteLine("public static {0} {1}({0}.Internal native, bool skipVTables = false)", |
|
className, Helpers.CreateInstanceIdentifier); |
|
WriteStartBraceIndent(); |
|
WriteLine("return new {0}(native, skipVTables);", ctorCall); |
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
if (@class.IsRefType && !@class.IsAbstract) |
|
{ |
|
PushBlock(CSharpBlockKind.Method); |
|
WriteLine("private static {0}.Internal* __CopyValue({0}.Internal native)", className); |
|
WriteStartBraceIndent(); |
|
var copyCtorMethod = @class.Methods.FirstOrDefault(method => |
|
method.IsCopyConstructor); |
|
if (@class.HasNonTrivialCopyConstructor && copyCtorMethod != null && copyCtorMethod.IsGenerated) |
|
{ |
|
// Allocate memory for a new native object and call the ctor. |
|
WriteLine("var ret = Marshal.AllocHGlobal({0});", @class.Layout.Size); |
|
WriteLine("{0}.Internal.{1}(ret, new global::System.IntPtr(&native));", |
|
@class.Visit(TypePrinter), GetFunctionNativeIdentifier(copyCtorMethod)); |
|
WriteLine("return ({0}.Internal*) ret;", className); |
|
} |
|
else |
|
{ |
|
WriteLine("var ret = ({0}.Internal*) Marshal.AllocHGlobal({1});", |
|
className, @class.Layout.Size); |
|
WriteLine("*ret = native;", className); |
|
WriteLine("return ret;"); |
|
} |
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
if (!@class.IsAbstract) |
|
{ |
|
PushBlock(CSharpBlockKind.Method); |
|
WriteLine("{0} {1}({2}.Internal native, bool skipVTables = false)", |
|
@class.IsAbstractImpl ? "internal" : "private", @class.Name, className); |
|
WriteLineIndent(@class.IsRefType ? ": this(__CopyValue(native), skipVTables)" : ": this()"); |
|
WriteStartBraceIndent(); |
|
if (@class.IsRefType) |
|
{ |
|
WriteLine("{0} = true;", Helpers.OwnsNativeInstanceIdentifier); |
|
WriteLine("NativeToManagedMap[{0}] = this;", Helpers.InstanceIdentifier); |
|
} |
|
else |
|
{ |
|
WriteLine("{0} = native;", Helpers.InstanceField); |
|
} |
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
} |
|
|
|
private void GenerateClassConstructorBase(Class @class, Method method) |
|
{ |
|
var hasBase = @class.HasBaseClass; |
|
|
|
if (hasBase && !@class.IsValueType) |
|
{ |
|
PushIndent(); |
|
Write(": this("); |
|
|
|
Write(method != null ? "(Internal*) null" : "native"); |
|
|
|
WriteLine(")"); |
|
PopIndent(); |
|
} |
|
|
|
if (@class.IsValueType) |
|
WriteLineIndent(": this()"); |
|
} |
|
|
|
#endregion |
|
|
|
#region Methods / Functions |
|
|
|
public void GenerateFunction(Function function) |
|
{ |
|
PushBlock(CSharpBlockKind.Function); |
|
GenerateDeclarationCommon(function); |
|
|
|
var functionName = GetFunctionIdentifier(function); |
|
Write("public static {0} {1}(", function.OriginalReturnType, functionName); |
|
Write(FormatMethodParameters(function.Parameters)); |
|
WriteLine(")"); |
|
WriteStartBraceIndent(); |
|
|
|
if (function.SynthKind == FunctionSynthKind.DefaultValueOverload) |
|
GenerateOverloadCall(function); |
|
else |
|
GenerateInternalFunctionCall(function); |
|
|
|
WriteCloseBraceIndent(); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
public void GenerateMethod(Method method, Class @class) |
|
{ |
|
PushBlock(CSharpBlockKind.Method, method); |
|
GenerateDeclarationCommon(method); |
|
|
|
if (method.ExplicitInterfaceImpl == null) |
|
{ |
|
//Write(Helpers.GetAccess(GetValidMethodAccess(method))); |
|
} |
|
|
|
// check if overriding a function from a secondary base |
|
Method rootBaseMethod; |
|
var isOverride = method.IsOverride && |
|
(rootBaseMethod = @class.GetBaseMethod(method, true)) != null && |
|
(rootBaseMethod.IsVirtual || rootBaseMethod.IsPure); |
|
|
|
if (method.IsVirtual && !isOverride && !method.IsOperator && !method.IsPure) |
|
Write("virtual "); |
|
|
|
var isBuiltinOperator = method.IsOperator && |
|
Operators.IsBuiltinOperator(method.OperatorKind); |
|
|
|
if (method.IsStatic || isBuiltinOperator) |
|
Write("static "); |
|
|
|
if (isOverride) |
|
{ |
|
if (method.Access == AccessSpecifier.Private) |
|
Write("sealed "); |
|
Write("override "); |
|
} |
|
|
|
if (method.IsPure) |
|
Write("abstract "); |
|
|
|
var functionName = GetMethodIdentifier(method); |
|
|
|
if (method.IsConstructor || method.IsDestructor) |
|
Write("{0}(", functionName); |
|
else if (method.ExplicitInterfaceImpl != null) |
|
Write("{0} {1}.{2}(", method.OriginalReturnType, |
|
method.ExplicitInterfaceImpl.Name, functionName); |
|
else if (method.OperatorKind == CXXOperatorKind.Conversion || |
|
method.OperatorKind == CXXOperatorKind.ExplicitConversion) |
|
Write("{0} {1}(", functionName, method.OriginalReturnType); |
|
else |
|
Write("{0} {1}(", method.OriginalReturnType, functionName); |
|
|
|
|
|
Write(FormatMethodParameters(method.Parameters)); |
|
|
|
Write(")"); |
|
|
|
if (method.SynthKind == FunctionSynthKind.DefaultValueOverload && method.IsConstructor && !method.IsPure) |
|
{ |
|
Write(" : this({0})", |
|
string.Join(", ", |
|
method.Parameters.Where( |
|
p => p.Kind == ParameterKind.Regular).Select( |
|
p => p.Ignore ? ExpressionPrinter.VisitExpression(p.DefaultArgument).Value : p.Name))); |
|
} |
|
|
|
if (method.IsPure) |
|
{ |
|
Write(";"); |
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
return; |
|
} |
|
NewLine(); |
|
|
|
if (method.Kind == CXXMethodKind.Constructor && |
|
method.SynthKind != FunctionSynthKind.DefaultValueOverload) |
|
GenerateClassConstructorBase(@class, method); |
|
|
|
WriteStartBraceIndent(); |
|
|
|
if (method.IsProxy) |
|
goto SkipImpl; |
|
|
|
if (method.SynthKind == FunctionSynthKind.DefaultValueOverload) |
|
{ |
|
if (!method.IsConstructor) |
|
{ |
|
GenerateOverloadCall(method); |
|
} |
|
goto SkipImpl; |
|
} |
|
|
|
if (@class.IsRefType) |
|
{ |
|
if (method.IsConstructor) |
|
{ |
|
GenerateClassConstructor(method, @class); |
|
} |
|
else if (method.IsOperator) |
|
{ |
|
GenerateOperator(method); |
|
} |
|
else if (method.SynthKind == FunctionSynthKind.AbstractImplCall) |
|
{ |
|
GenerateVirtualFunctionCall(method, @class.BaseClass); |
|
} |
|
else if (method.IsVirtual) |
|
{ |
|
GenerateVirtualFunctionCall(method, @class); |
|
} |
|
else |
|
{ |
|
GenerateInternalFunctionCall(method); |
|
} |
|
} |
|
else if (@class.IsValueType) |
|
{ |
|
if (method.IsConstructor) |
|
{ |
|
GenerateInternalFunctionCall(method); |
|
} |
|
else if (method.IsOperator) |
|
{ |
|
GenerateOperator(method); |
|
} |
|
else |
|
{ |
|
GenerateInternalFunctionCall(method); |
|
} |
|
} |
|
|
|
SkipImpl: |
|
|
|
WriteCloseBraceIndent(); |
|
|
|
if (method.OperatorKind == CXXOperatorKind.EqualEqual) |
|
{ |
|
GenerateEqualsAndGetHashCode(method, @class); |
|
} |
|
|
|
PopBlock(NewLineKind.BeforeNextBlock); |
|
} |
|
|
|
private string OverloadParamNameWithDefValue(Parameter p, ref int index) |
|
{ |
|
return p.Type.IsPointerToPrimitiveType() && p.Usage == ParameterUsage.InOut && p.HasDefaultValue |
|
? "ref param" + index++ |
|
: ExpressionPrinter.VisitExpression(p.DefaultArgument).Value; |
|
} |
|
|
|
private void GenerateOverloadCall(Function function) |
|
{ |
|
for (int i = 0, j = 0; i < function.Parameters.Count; i++) |
|
{ |
|
var parameter = function.Parameters[i]; |
|
PrimitiveType primitiveType; |
|
if (parameter.Kind == ParameterKind.Regular && parameter.Ignore && |
|
parameter.Type.IsPointerToPrimitiveType(out primitiveType) && |
|
parameter.Usage == ParameterUsage.InOut && parameter.HasDefaultValue) |
|
{ |
|
var pointeeType = ((PointerType) parameter.Type).Pointee.ToString(); |
|
WriteLine("{0} param{1} = {2};", pointeeType, j++, |
|
primitiveType == PrimitiveType.Bool ? "false" : "0"); |
|
} |
|
} |
|
|
|
GenerateManagedCall(function); |
|
} |
|
|
|
private void GenerateManagedCall(Function function, bool prependBase = false) |
|
{ |
|
var type = function.OriginalReturnType.Type; |
|
var index = 0; |
|
WriteLine("{0}{1}{2}({3});", |
|
type.IsPrimitiveType(PrimitiveType.Void) ? string.Empty : "return ", |
|
prependBase ? "base." : string.Empty, |
|
function.Name, |
|
string.Join(", ", |
|
function.Parameters.Where( |
|
p => p.Kind != ParameterKind.IndirectReturnType).Select( |
|
p => p.Ignore ? OverloadParamNameWithDefValue(p, ref index) : |
|
(p.Usage == ParameterUsage.InOut ? "ref " : string.Empty) + p.Name))); |
|
} |
|
|
|
private void GenerateEqualsAndGetHashCode(Function method, Class @class) |
|
{ |
|
Class leftHandSide; |
|
Class rightHandSide; |
|
if (method.Parameters[0].Type.SkipPointerRefs().TryGetClass(out leftHandSide) && |
|
leftHandSide.OriginalPtr == @class.OriginalPtr && |
|
method.Parameters[1].Type.SkipPointerRefs().TryGetClass(out rightHandSide) && |
|
rightHandSide.OriginalPtr == @class.OriginalPtr) |
|
{ |
|
NewLine(); |
|
WriteLine("public override bool Equals(object obj)"); |
|
WriteStartBraceIndent(); |
|
if (@class.IsRefType) |
|
{ |
|
WriteLine("return this == obj as {0};", @class.Name); |
|
} |
|
else |
|
{ |
|
WriteLine("if (!(obj is {0})) return false;", @class.Name); |
|
WriteLine("return this == ({0}) obj;", @class.Name); |
|
} |
|
WriteCloseBraceIndent(); |
|
|
|
NewLine(); |
|
|
|
WriteLine("public override int GetHashCode()"); |
|
WriteStartBraceIndent(); |
|
if (@class.IsRefType) |
|
{ |
|
WriteLine("if ({0} == global::System.IntPtr.Zero)", Helpers.InstanceIdentifier); |
|
WriteLineIndent("return global::System.IntPtr.Zero.GetHashCode();"); |
|
WriteLine("return (*(Internal*) {0}).GetHashCode();", Helpers.InstanceIdentifier); |
|
} |
|
else |
|
{ |
|
WriteLine("return {0}.GetHashCode();", Helpers.InstanceIdentifier); |
|
} |
|
WriteCloseBraceIndent(); |
|
} |
|
} |
|
|
|
private void CheckArgumentRange(Function method) |
|
{ |
|
if (Driver.Options.MarshalCharAsManagedChar) |
|
{ |
|
foreach (var param in method.Parameters.Where( |
|
p => p.Type.IsPrimitiveType(PrimitiveType.Char))) |
|
{ |
|
WriteLine("if ({0} < char.MinValue || {0} > sbyte.MaxValue)", param.Name); |
|
WriteLineIndent( |
|
"throw new global::System.ArgumentException(\"{0} must be in the range {1} - {2}.\");", |
|
param.Name, (int) char.MinValue, sbyte.MaxValue); |
|
} |
|
} |
|
} |
|
|
|
private static AccessSpecifier GetValidMethodAccess(Method method) |
|
{ |
|
return method.IsOverride |
|
? ((Class) method.Namespace).GetBaseMethod(method).Access |
|
: method.Access; |
|
} |
|
|
|
private static AccessSpecifier GetValidPropertyAccess(Property property) |
|
{ |
|
if (property.Access == AccessSpecifier.Public) |
|
return AccessSpecifier.Public; |
|
return property.IsOverride |
|
? ((Class) property.Namespace).GetBaseProperty(property).Access |
|
: property.Access; |
|
} |
|
|
|
private void GenerateVirtualPropertyCall(Method method, Class @class, |
|
Property property, List<Parameter> parameters = null) |
|
{ |
|
if (property.IsOverride && !property.IsPure && |
|
method.SynthKind != FunctionSynthKind.AbstractImplCall && |
|
@class.HasNonAbstractBasePropertyInPrimaryBase(property)) |
|
{ |
|
WriteLine(parameters == null ? |
|
"return base.{0};" : "base.{0} = value;", property.Name); |
|
} |
|
else |
|
{ |
|
string delegateId; |
|
GetVirtualCallDelegate(method, @class, out delegateId); |
|
GenerateFunctionCall(delegateId, parameters ?? method.Parameters, method); |
|
} |
|
} |
|
|
|
private void GenerateVirtualFunctionCall(Method method, Class @class, |
|
bool forceVirtualCall = false) |
|
{ |
|
if (!forceVirtualCall && method.IsOverride && !method.IsPure && |
|
method.SynthKind != FunctionSynthKind.AbstractImplCall && |
|
@class.HasNonAbstractBaseMethodInPrimaryBase(method)) |
|
{ |
|
GenerateManagedCall(method, true); |
|
} |
|
else |
|
{ |
|
string delegateId; |
|
GetVirtualCallDelegate(method, @class, out delegateId); |
|
GenerateFunctionCall(delegateId, method.Parameters, method); |
|
} |
|
} |
|
|
|
public void GetVirtualCallDelegate(Method method, Class @class, |
|
out string delegateId) |
|
{ |
|
var i = VTables.GetVTableIndex(method.OriginalFunction ?? method, @class); |
|
WriteLine("var {0} = *(void**) ((IntPtr) __OriginalVTables[0] + {1} * {2});", |
|
Helpers.SlotIdentifier, i, Driver.TargetInfo.PointerWidth / 8); |
|
if (method.IsDestructor && @class.IsAbstract) |
|
{ |
|
WriteLine("if ({0} != null)", Helpers.SlotIdentifier); |
|
WriteStartBraceIndent(); |
|
} |
|
|
|
var @delegate = GetVTableMethodDelegateName(method.OriginalFunction ?? method); |
|
delegateId = Generator.GeneratedIdentifier(@delegate); |
|
|
|
WriteLine("var {0} = ({1}) Marshal.GetDelegateForFunctionPointer(new IntPtr({2}), typeof({1}));", |
|
delegateId, DelegatesPass.Delegates[method].Visit(TypePrinter), Helpers.SlotIdentifier); |
|
} |
|
|
|
private void GenerateOperator(Method method) |
|
{ |
|
if (method.SynthKind == FunctionSynthKind.ComplementOperator) |
|
{ |
|
if (method.Kind == CXXMethodKind.Conversion) |
|
{ |
|
// To avoid ambiguity when having the multiple inheritance pass enabled |
|
var paramType = method.Parameters[0].Type.SkipPointerRefs().Desugar(); |
|
Class paramClass; |
|
Class @interface = null; |
|
if (paramType.TryGetClass(out paramClass)) |
|
@interface = paramClass.Namespace.Classes.Find(c => c.OriginalClass == paramClass); |
|
|
|
var paramName = string.Format("{0}{1}", |
|
method.Parameters[0].Type.IsPrimitiveTypeConvertibleToRef() ? |
|
"ref *" : string.Empty, |
|
method.Parameters[0].Name); |
|
if (@interface != null) |
|
WriteLine("return new {0}(({2}) {1});", |
|
method.ConversionType, paramName, @interface.Name); |
|
else |
|
WriteLine("return new {0}({1});", method.ConversionType, paramName); |
|
} |
|
else |
|
{ |
|
var @operator = Operators.GetOperatorOverloadPair(method.OperatorKind); |
|
|
|
WriteLine("return !({0} {1} {2});", method.Parameters[0].Name, |
|
@operator, method.Parameters[1].Name); |
|
} |
|
return; |
|
} |
|
|
|
if (method.OperatorKind == CXXOperatorKind.EqualEqual || |
|
method.OperatorKind == CXXOperatorKind.ExclaimEqual) |
|
{ |
|
WriteLine("bool {0}Null = ReferenceEquals({0}, null);", |
|
method.Parameters[0].Name); |
|
WriteLine("bool {0}Null = ReferenceEquals({0}, null);", |
|
method.Parameters[1].Name); |
|
WriteLine("if ({0}Null || {1}Null)", |
|
method.Parameters[0].Name, method.Parameters[1].Name); |
|
WriteLineIndent("return {0}{1}Null && {2}Null{3};", |
|
method.OperatorKind == CXXOperatorKind.EqualEqual ? string.Empty : "!(", |
|
method.Parameters[0].Name, method.Parameters[1].Name, |
|
method.OperatorKind == CXXOperatorKind.EqualEqual ? string.Empty : ")"); |
|
} |
|
|
|
GenerateInternalFunctionCall(method); |
|
} |
|
|
|
private void GenerateClassConstructor(Method method, Class @class) |
|
{ |
|
WriteLine("{0} = Marshal.AllocHGlobal({1});", Helpers.InstanceIdentifier, |
|
@class.Layout.Size); |
|
WriteLine("{0} = true;", Helpers.OwnsNativeInstanceIdentifier); |
|
WriteLine("NativeToManagedMap[{0}] = this;", Helpers.InstanceIdentifier); |
|
|
|
if (method.IsCopyConstructor) |
|
{ |
|
if (@class.HasNonTrivialCopyConstructor) |
|
GenerateInternalFunctionCall(method); |
|
else |
|
WriteLine("*(({0}.Internal*) {2}) = *(({0}.Internal*) {1}.{2});", |
|
@class.Name, method.Parameters[0].Name, Helpers.InstanceIdentifier); |
|
} |
|
else |
|
{ |
|
if (!method.IsDefaultConstructor || @class.HasNonTrivialDefaultConstructor) |
|
GenerateInternalFunctionCall(method); |
|
} |
|
|
|
GenerateVTableClassSetupCall(@class); |
|
} |
|
|
|
public void GenerateInternalFunctionCall(Function function, |
|
List<Parameter> parameters = null, Type returnType = null) |
|
{ |
|
if (parameters == null) |
|
parameters = function.Parameters; |
|
|
|
CheckArgumentRange(function); |
|
var functionName = string.Format("Internal.{0}", |
|
GetFunctionNativeIdentifier(function.OriginalFunction ?? function)); |
|
GenerateFunctionCall(functionName, parameters, function, returnType); |
|
} |
|
|
|
public void GenerateFunctionCall(string functionName, List<Parameter> parameters, |
|
Function function, Type returnType = null) |
|
{ |
|
if (function.IsPure) |
|
{ |
|
WriteLine("throw new System.NotImplementedException();"); |
|
return; |
|
} |
|
|
|
var retType = function.OriginalReturnType; |
|
if (returnType == null) |
|
returnType = retType.Type; |
|
|
|
var method = function as Method; |
|
var hasThisReturnStructor = method != null && (method.IsConstructor || method.IsDestructor); |
|
var needsReturn = !retType.Type.IsPrimitiveType(PrimitiveType.Void) && !hasThisReturnStructor; |
|
|
|
var isValueType = false; |
|
var needsInstance = false; |
|
|
|
Parameter operatorParam = null; |
|
if (method != null) |
|
{ |
|
var @class = (Class) method.Namespace; |
|
isValueType = @class.IsValueType; |
|
|
|
operatorParam = method.Parameters.FirstOrDefault( |
|
p => p.Kind == ParameterKind.OperatorParameter); |
|
needsInstance = !method.IsStatic || operatorParam != null; |
|
} |
|
|
|
var @params = GenerateFunctionParamsMarshal(parameters, function); |
|
|
|
var originalFunction = function.OriginalFunction ?? function; |
|
|
|
if (originalFunction.HasIndirectReturnTypeParameter) |
|
{ |
|
var indirectRetType = originalFunction.Parameters.First( |
|
parameter => parameter.Kind == ParameterKind.IndirectReturnType); |
|
|
|
Class retClass; |
|
indirectRetType.Type.Desugar().TryGetClass(out retClass); |
|
|
|
TypeMap typeMap; |
|
string construct = null; |
|
if (Driver.TypeDatabase.FindTypeMap(retClass, out typeMap)) |
|
construct = typeMap.CSharpConstruct(); |
|
|
|
if (construct == null) |
|
{ |
|
WriteLine("var {0} = new {1}.Internal();", Helpers.ReturnIdentifier, |
|
(retClass.OriginalClass ?? retClass).Visit(TypePrinter)); |
|
} |
|
else |
|
{ |
|
if (string.IsNullOrWhiteSpace(construct)) |
|
WriteLine("{0} {1};", |
|
typeMap.CSharpSignature(new CSharpTypePrinterContext |
|
{ |
|
Type = indirectRetType.Type.Desugar() |
|
}), |
|
Helpers.ReturnIdentifier); |
|
else |
|
WriteLine("var {0} = {1};", construct); |
|
} |
|
} |
|
|
|
var names = new List<string>(); |
|
foreach (var param in @params) |
|
{ |
|
if (param.Param == operatorParam && needsInstance) |
|
continue; |
|
|
|
var name = new StringBuilder(); |
|
if (param.Context != null |
|
&& !string.IsNullOrWhiteSpace(param.Context.ArgumentPrefix)) |
|
name.Append(param.Context.ArgumentPrefix); |
|
|
|
name.Append(param.Name); |
|
names.Add(name.ToString()); |
|
} |
|
|
|
var needsFixedThis = needsInstance && isValueType; |
|
|
|
if (originalFunction.HasIndirectReturnTypeParameter) |
|
{ |
|
var name = string.Format("new IntPtr(&{0})", Helpers.ReturnIdentifier); |
|
names.Insert(0, name); |
|
} |
|
|
|
if (needsInstance) |
|
{ |
|
var instanceIndex = GetInstanceParamIndex(method); |
|
|
|
if (needsFixedThis) |
|
{ |
|
names.Insert(instanceIndex, "new global::System.IntPtr(__instancePtr)"); |
|
} |
|
else |
|
{ |
|
names.Insert(instanceIndex, |
|
operatorParam != null ? @params[0].Name : GetInstanceParam(function)); |
|
} |
|
} |
|
|
|
if (needsFixedThis) |
|
{ |
|
if (operatorParam == null) |
|
{ |
|
WriteLine("fixed (Internal* __instancePtr = &{0})", Helpers.InstanceField); |
|
WriteStartBraceIndent(); |
|
} |
|
else |
|
{ |
|
WriteLine("var __instancePtr = &{0}.{1};", operatorParam.Name, Helpers.InstanceField); |
|
} |
|
} |
|
|
|
if (needsReturn && !originalFunction.HasIndirectReturnTypeParameter) |
|
Write("var {0} = ", Helpers.ReturnIdentifier); |
|
|
|
WriteLine("{0}({1});", functionName, string.Join(", ", names)); |
|
|
|
var cleanups = new List<TextGenerator>(); |
|
GenerateFunctionCallOutParams(@params, cleanups); |
|
|
|
cleanups.AddRange( |
|
from param in @params |
|
select param.Context |
|
into context |
|
where context != null && !string.IsNullOrWhiteSpace(context.Cleanup) |
|
select context.Cleanup); |
|
|
|
foreach (var cleanup in cleanups) |
|
{ |
|
Write(cleanup); |
|
} |
|
|
|
if (needsReturn) |
|
{ |
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
ArgName = Helpers.ReturnIdentifier, |
|
ReturnVarName = Helpers.ReturnIdentifier, |
|
ReturnType = retType, |
|
Parameter = operatorParam |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
retType.CSharpMarshalToManaged(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
// Special case for indexer - needs to dereference if the internal |
|
// function is a pointer type and the property is not. |
|
if (retType.Type.IsAddress() && |
|
retType.Type.GetPointee().Equals(returnType) && |
|
returnType.IsPrimitiveType()) |
|
WriteLine("return *{0};", marshal.Context.Return); |
|
else |
|
WriteLine("return {0};", marshal.Context.Return); |
|
} |
|
|
|
if (needsFixedThis && operatorParam == null) |
|
WriteCloseBraceIndent(); |
|
|
|
var numFixedBlocks = @params.Count(param => param.HasFixedBlock); |
|
for(var i = 0; i < numFixedBlocks; ++i) |
|
WriteCloseBraceIndent(); |
|
} |
|
|
|
private static string GetInstanceParam(Function function) |
|
{ |
|
var from = (Class) function.Namespace; |
|
var to = function.OriginalFunction == null ? @from.BaseClass : |
|
(Class) function.OriginalFunction.Namespace; |
|
|
|
var baseOffset = 0; |
|
if (to != null) |
|
{ |
|
to = to.OriginalClass ?? to; |
|
baseOffset = @from.ComputeNonVirtualBaseClassOffsetTo(to); |
|
} |
|
var isPrimaryBase = from.BaseClass == to; |
|
if (isPrimaryBase) |
|
{ |
|
return string.Format("({0} + {1}{2})", |
|
Helpers.InstanceIdentifier, |
|
Helpers.PointerAdjustmentIdentifier, |
|
baseOffset == 0 ? string.Empty : (" - " + baseOffset)); |
|
} |
|
return string.Format("({0}{1})", |
|
Helpers.InstanceIdentifier, |
|
baseOffset == 0 ? string.Empty : " + " + baseOffset); |
|
} |
|
|
|
private int GetInstanceParamIndex(Function method) |
|
{ |
|
if (Options.IsMicrosoftAbi) |
|
return 0; |
|
|
|
var indirectReturnType = method.Parameters.FirstOrDefault( |
|
parameter => parameter.Kind == ParameterKind.IndirectReturnType); |
|
var indirectReturnTypeIndex = method.Parameters.IndexOf(indirectReturnType); |
|
|
|
return indirectReturnTypeIndex >= 0 ? ++indirectReturnTypeIndex : 0; |
|
} |
|
|
|
private void GenerateFunctionCallOutParams(IEnumerable<ParamMarshal> @params, |
|
ICollection<TextGenerator> cleanups) |
|
{ |
|
foreach (var paramInfo in @params) |
|
{ |
|
var param = paramInfo.Param; |
|
if (!(param.IsOut || param.IsInOut)) continue; |
|
if (param.Type.IsPrimitiveTypeConvertibleToRef()) |
|
continue; |
|
|
|
var nativeVarName = paramInfo.Name; |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
Parameter = param, |
|
ArgName = nativeVarName, |
|
ReturnVarName = nativeVarName, |
|
ReturnType = param.QualifiedType |
|
}; |
|
|
|
var marshal = new CSharpMarshalNativeToManagedPrinter(ctx); |
|
param.CSharpMarshalToManaged(marshal); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
WriteLine("{0} = {1};", param.Name, marshal.Context.Return); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.Cleanup)) |
|
cleanups.Add(marshal.Context.Cleanup); |
|
} |
|
} |
|
|
|
public struct ParamMarshal |
|
{ |
|
public string Name; |
|
public Parameter Param; |
|
public CSharpMarshalContext Context; |
|
public bool HasFixedBlock; |
|
} |
|
|
|
public List<ParamMarshal> GenerateFunctionParamsMarshal(IEnumerable<Parameter> @params, |
|
Function function = null) |
|
{ |
|
var marshals = new List<ParamMarshal>(); |
|
|
|
var paramIndex = 0; |
|
foreach (var param in @params) |
|
{ |
|
if (param.Kind == ParameterKind.IndirectReturnType) |
|
continue; |
|
|
|
marshals.Add(GenerateFunctionParamMarshal(param, paramIndex, function)); |
|
paramIndex++; |
|
} |
|
|
|
return marshals; |
|
} |
|
|
|
private ParamMarshal GenerateFunctionParamMarshal(Parameter param, int paramIndex, |
|
Function function = null) |
|
{ |
|
PrimitiveType primitive; |
|
// Do not delete instance in MS ABI. |
|
var name = param.Kind == ParameterKind.ImplicitDestructorParameter ? "0" : param.Name; |
|
if (param.Type.IsPrimitiveType(out primitive) && primitive != PrimitiveType.Char) |
|
{ |
|
return new ParamMarshal { Name = name, Param = param }; |
|
} |
|
|
|
var argName = "arg" + paramIndex.ToString(CultureInfo.InvariantCulture); |
|
var paramMarshal = new ParamMarshal { Name = argName, Param = param }; |
|
|
|
if (param.IsOut || param.IsInOut) |
|
{ |
|
var paramType = param.Type; |
|
|
|
Class @class; |
|
if ((paramType.GetFinalPointee() ?? paramType).Desugar().TryGetClass(out @class)) |
|
{ |
|
var qualifiedIdentifier = (@class.OriginalClass ?? @class).Visit(TypePrinter); |
|
WriteLine("{0} = new {1}();", name, qualifiedIdentifier); |
|
} |
|
} |
|
|
|
var ctx = new CSharpMarshalContext(Driver) |
|
{ |
|
Parameter = param, |
|
ParameterIndex = paramIndex, |
|
ArgName = argName, |
|
Function = function |
|
}; |
|
|
|
paramMarshal.Context = ctx; |
|
var marshal = new CSharpMarshalManagedToNativePrinter(ctx); |
|
param.CSharpMarshalToNative(marshal); |
|
paramMarshal.HasFixedBlock = ctx.HasFixedBlock; |
|
|
|
if (string.IsNullOrEmpty(marshal.Context.Return)) |
|
throw new Exception("Cannot marshal argument of function"); |
|
|
|
if (!string.IsNullOrWhiteSpace(marshal.Context.SupportBefore)) |
|
Write(marshal.Context.SupportBefore); |
|
|
|
if (paramMarshal.HasFixedBlock) |
|
PushIndent(); |
|
|
|
WriteLine("var {0} = {1};", argName, marshal.Context.Return); |
|
|
|
return paramMarshal; |
|
} |
|
|
|
static string GetParameterUsage(ParameterUsage usage) |
|
{ |
|
switch (usage) |
|
{ |
|
case ParameterUsage.Out: |
|
return "out "; |
|
case ParameterUsage.InOut: |
|
return "ref "; |
|
default: |
|
return string.Empty; |
|
} |
|
} |
|
|
|
private string FormatMethodParameters(IEnumerable<Parameter> @params) |
|
{ |
|
return string.Join(", ", |
|
from param in @params |
|
where param.Kind != ParameterKind.IndirectReturnType && !param.Ignore |
|
let typeName = param.CSharpType(TypePrinter) |
|
select string.Format("{0}{1} {2}", GetParameterUsage(param.Usage), |
|
typeName, param.Name + |
|
(param.DefaultArgument == null || !Options.GenerateDefaultValuesForArguments ? |
|
string.Empty : " = " + ExpressionPrinter.VisitExpression(param.DefaultArgument)))); |
|
} |
|
|
|
#endregion |
|
|
|
public MemberDeclarationSyntax GenerateTypedef(TypedefDecl typedef) |
|
{ |
|
if (!typedef.IsGenerated) |
|
return null; |
|
|
|
//GenerateDeclarationCommon(typedef); |
|
|
|
FunctionType functionType; |
|
TagType tag; |
|
|
|
if (typedef.Type.IsPointerToPrimitiveType(PrimitiveType.Void) |
|
|| typedef.Type.IsPointerTo(out tag)) |
|
{ |
|
return SyntaxFactory.ClassDeclaration(typedef.Name).AddModifiers( |
|
SyntaxFactory.Token(SyntaxKind.PublicKeyword)); |
|
} |
|
if (!typedef.Type.IsPointerTo(out functionType)) |
|
{ |
|
return null; |
|
} |
|
|
|
return GenerateDelegate(typedef, functionType); |
|
} |
|
|
|
private MemberDeclarationSyntax GenerateDelegate(TypedefDecl typedef, FunctionType functionType) |
|
{ |
|
var attributedType = typedef.Type.GetPointee() as AttributedType; |
|
var callingConvention = attributedType == null |
|
? functionType.CallingConvention |
|
: ((FunctionType) attributedType.Equivalent.Type).CallingConvention; |
|
|
|
var attributes = new List<AttributeSyntax>(2) |
|
{ |
|
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("SuppressUnmanagedCodeSecurity")) |
|
}; |
|
var interopCallConv = callingConvention.ToInteropCallConv(); |
|
if (interopCallConv != System.Runtime.InteropServices.CallingConvention.Winapi) |
|
attributes.Add( |
|
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("UnmanagedFunctionPointerAttribute"), |
|
SyntaxFactory.AttributeArgumentList().AddArguments( |
|
SyntaxFactory.AttributeArgument( |
|
SyntaxFactory.IdentifierName( |
|
"global::System.Runtime.InteropServices.CallingConvention." + |
|
interopCallConv))))); |
|
|
|
TypePrinter.PushContext(CSharpTypePrinterContextKind.Native); |
|
var @delegate = SyntaxFactory.DelegateDeclaration( |
|
SyntaxFactory.ParseTypeName(functionType.ReturnType.Visit(TypePrinter).Type), |
|
typedef.Name).AddAttributeLists( |
|
SyntaxFactory.AttributeList().AddAttributes(attributes.ToArray())) |
|
.AddModifiers( |
|
SyntaxFactory.Token(SyntaxKind.PublicKeyword), SyntaxFactory.Token(SyntaxKind.UnsafeKeyword)) |
|
.AddParameterListParameters( |
|
(from parameter in functionType.Parameters |
|
select SyntaxFactory.Parameter( |
|
SyntaxFactory.Identifier(parameter.Name)).WithType( |
|
SyntaxFactory.ParseTypeName(parameter.Type.Visit(TypePrinter).Type))).ToArray()); |
|
TypePrinter.PopContext(); |
|
|
|
return @delegate; |
|
} |
|
|
|
public EnumDeclarationSyntax GenerateEnum(Enumeration @enum) |
|
{ |
|
var @enumDeclaration = SyntaxFactory.EnumDeclaration(@enum.Name).AddModifiers( |
|
SyntaxFactory.Token(Helpers.GetAccess(@enum.Access))); |
|
if (@enum.IsFlags) |
|
{ |
|
enumDeclaration = enumDeclaration.AddAttributeLists( |
|
SyntaxFactory.AttributeList().AddAttributes( |
|
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("Flags")))); |
|
} |
|
|
|
foreach (var item in @enum.Items) |
|
{ |
|
var enumMember = SyntaxFactory.EnumMemberDeclaration(item.Name); |
|
if (item.ExplicitValue) |
|
{ |
|
var value = @enum.GetItemValueAsString(item); |
|
enumMember = enumMember.WithEqualsValue( |
|
SyntaxFactory.EqualsValueClause(SyntaxFactory.IdentifierName(value))); |
|
} |
|
enumDeclaration = enumDeclaration.AddMembers(enumMember); |
|
} |
|
|
|
return enumDeclaration; |
|
|
|
//GenerateDeclarationCommon(@enum); |
|
} |
|
|
|
public static string GetMethodIdentifier(Method method) |
|
{ |
|
if (method.IsConstructor || method.IsDestructor) |
|
return method.Namespace.Name; |
|
|
|
return GetFunctionIdentifier(method); |
|
} |
|
|
|
public static string GetFunctionIdentifier(Function function) |
|
{ |
|
if (function.IsOperator) |
|
return Operators.GetOperatorIdentifier(function.OperatorKind); |
|
|
|
return function.Name; |
|
} |
|
|
|
public static string GetFunctionNativeIdentifier(Function function) |
|
{ |
|
var functionName = function.Name; |
|
|
|
var method = function as Method; |
|
if (method != null) |
|
{ |
|
if (method.IsConstructor && !method.IsCopyConstructor) |
|
functionName = "ctor"; |
|
else if (method.IsCopyConstructor) |
|
functionName = "cctor"; |
|
else if (method.IsDestructor) |
|
functionName = "dtor"; |
|
else |
|
functionName = GetMethodIdentifier(method); |
|
} |
|
|
|
var identifier = functionName; |
|
|
|
if (function.IsOperator) |
|
identifier = "Operator" + function.OperatorKind; |
|
|
|
var overloads = function.Namespace.GetOverloads(function) |
|
.ToList(); |
|
var index = overloads.IndexOf(function); |
|
|
|
if (index >= 0) |
|
identifier += "_" + index.ToString(CultureInfo.InvariantCulture); |
|
else if (function.Index.HasValue) |
|
identifier += "_" + function.Index.Value; |
|
|
|
return identifier; |
|
} |
|
|
|
public MemberDeclarationSyntax GenerateInternalFunction(Function function) |
|
{ |
|
if (function.OriginalFunction != null) |
|
function = function.OriginalFunction; |
|
|
|
CSharpTypePrinterResult retType; |
|
var @params = GatherInternalParams(function, out retType); |
|
|
|
string libName = Options.SharedLibraryName; |
|
|
|
if (Options.CheckSymbols) |
|
{ |
|
NativeLibrary library; |
|
Driver.Symbols.FindLibraryBySymbol(function.Mangled, out library); |
|
|
|
if (library != null) |
|
libName = Path.GetFileNameWithoutExtension(library.FileName); |
|
} |
|
if (Options.StripLibPrefix && libName != null && libName.Length > 3 && libName.StartsWith("lib")) |
|
{ |
|
libName = libName.Substring(3); |
|
} |
|
if (libName == null) |
|
libName = Options.SharedLibraryName; |
|
|
|
if (Options.GenerateInternalImports) |
|
libName = "__Internal"; |
|
|
|
var callConv = function.CallingConvention.ToInteropCallConv(); |
|
|
|
return SyntaxFactory.MethodDeclaration(SyntaxFactory.ParseTypeName(retType.Type), |
|
GetFunctionNativeIdentifier(function)) |
|
.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes( |
|
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("SuppressUnmanagedCodeSecurity")))) |
|
.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes( |
|
SyntaxFactory.Attribute(SyntaxFactory.IdentifierName("DllImport")) |
|
.AddArgumentListArguments( |
|
SyntaxFactory.AttributeArgument(SyntaxFactory.LiteralExpression( |
|
SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(libName))), |
|
SyntaxFactory.AttributeArgument(SyntaxFactory.IdentifierName( |
|
"CallingConvention = global::System.Runtime.InteropServices.CallingConvention." + callConv)), |
|
SyntaxFactory.AttributeArgument( |
|
SyntaxFactory.IdentifierName(string.Format("EntryPoint=\"{0}\"", function.Mangled)))))) |
|
.AddModifiers( |
|
SyntaxFactory.Token(SyntaxKind.InternalKeyword), |
|
SyntaxFactory.Token(SyntaxKind.StaticKeyword), |
|
SyntaxFactory.Token(SyntaxKind.ExternKeyword)) |
|
.AddParameterListParameters( |
|
(@params.Select(p => SyntaxFactory.Parameter(SyntaxFactory.Identifier(p))).ToArray())) |
|
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken)); |
|
|
|
if (function.ReturnType.Type.IsPrimitiveType(PrimitiveType.Bool)) |
|
WriteLine("[return: MarshalAsAttribute(UnmanagedType.I1)]"); |
|
} |
|
|
|
private T AddMembers<T>(T parent, params MemberDeclarationSyntax[] members) where T : CSharpSyntaxNode |
|
{ |
|
var compilationUnitSyntax = parent as CompilationUnitSyntax; |
|
if (compilationUnitSyntax != null) |
|
{ |
|
return compilationUnitSyntax.AddMembers(members) as T; |
|
} |
|
|
|
var @namespace = parent as NamespaceDeclarationSyntax; |
|
if (@namespace != null) |
|
{ |
|
return @namespace.AddMembers(members) as T; |
|
} |
|
|
|
var @class = parent as ClassDeclarationSyntax; |
|
if (@class != null) |
|
{ |
|
return @class.AddMembers(members) as T; |
|
} |
|
|
|
var @struct = parent as StructDeclarationSyntax; |
|
if (@struct != null) |
|
{ |
|
return @struct.AddMembers(members) as T; |
|
} |
|
|
|
var @interface = parent as InterfaceDeclarationSyntax; |
|
if (@interface != null) |
|
{ |
|
return @interface.AddMembers(members) as T; |
|
} |
|
|
|
return parent; |
|
} |
|
|
|
private T AddModifier<T>(T parent, SyntaxKind modifier) where T : CSharpSyntaxNode |
|
{ |
|
var @class = parent as ClassDeclarationSyntax; |
|
if (@class != null) |
|
return @class.AddModifiers(SyntaxFactory.Token(modifier)) as T; |
|
|
|
var @struct = parent as StructDeclarationSyntax; |
|
if (@struct != null) |
|
return @struct.AddModifiers(SyntaxFactory.Token(modifier)) as T; |
|
|
|
var @interface = parent as InterfaceDeclarationSyntax; |
|
if (@interface != null) |
|
return @interface.AddModifiers(SyntaxFactory.Token(modifier)) as T; |
|
|
|
return parent; |
|
} |
|
|
|
private T AddBase<T>(T parent, string @base) where T : CSharpSyntaxNode |
|
{ |
|
var @class = parent as ClassDeclarationSyntax; |
|
if (@class != null) |
|
return @class.AddBaseListTypes( |
|
SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(@base))) as T; |
|
|
|
var @struct = parent as StructDeclarationSyntax; |
|
if (@struct != null) |
|
return @struct.AddBaseListTypes( |
|
SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(@base))) as T; |
|
|
|
var @interface = parent as InterfaceDeclarationSyntax; |
|
if (@interface != null) |
|
return @interface.AddBaseListTypes( |
|
SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(@base))) as T; |
|
|
|
return parent; |
|
} |
|
|
|
private CompilationUnitSyntax compilationUnit; |
|
} |
|
|
|
internal class SymbolNotFoundException : Exception |
|
{ |
|
public SymbolNotFoundException(string msg) : base(msg) |
|
{} |
|
} |
|
} |