Tools and libraries to glue C/C++ APIs to high-level languages
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

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)
{}
}
}